6.7.2. Orangutan Examples

Orangutan SVP fully assembled.

The Orangutan robot controllers feature user-programmable Atmel AVR microcontrollers interfaced with additional hardware useful for controlling robots. They are programmable in C or C++ and supported by the Pololu AVR library, which makes it easy to use the integrated hardware and AVR peripherals, such as the UART module. Unlike the Arduino, the hardware serial lines are completely available on the Orangutans, so software serial is not necessary when connecting to serial devices like the Simple Motor Controller.

In the following example programs, we use the OrangutanSerial functions from the Pololu AVR library to transmit bytes on pin PD1. In the advanced example, we use the OrangutanSerial functions to receive bytes on pin PD0, and we use the OrangutanLCD functions to report feedback obtained from the Simple Motor Controller. See the Pololu AVR library command reference for more information on these functions.

This code requires the Simple Motor Controller to have automatic baud rate detection enabled or to have a fixed baud rate set to 115200 bps. It must also be in Binary serial mode with the CRC Mode set to Disabled.

Simple Example

This example assumes the following connections exist between the Orangutan and the Simple Motor Controller:

  • Orangutan pin PD0 to Simple Motor Controller TX
  • Orangutan GND to Simple Motor Controller GND

Pin PD0 is the Orangutan’s hardware serial receive line and must be connected to the Simple Motor Controller as described above for this sample program to work. See Section 4.2 for more information on connecting a serial device to the Simple Motor Controller.

This program demonstrates how to initiate serial communication with the Simple Motor Controller and how to send commands to set the motor speed. For information about the serial commands used by this sample code, refer to Section 6.2.1. Note that the Simple Motor Controller must be powered when this Orangutan program starts running.

#include <pololu/orangutan.h>  

char command[3];

// These first two functions call the appropriate Pololu AVR library serial functions
// depending on which Orangutan you are using.  The Orangutan SVP and X2 have multiple
// serial ports, so the serial functions for these devices require an extra argument
// specifying which port to use.  You can simplify this program by just calling the
// library function appropriate for your Orangutan board.

void setBaudRate(unsigned long baud)
{
#if _SERIAL_PORTS > 1  // Orangutan X2 and SVP users
  serial_set_baud_rate(UART0, baud);
#else
  serial_set_baud_rate(baud);
#endif
}

void sendBlocking(char * buffer, unsigned char size)
{
#if _SERIAL_PORTS > 1  // Orangutan X2 and SVP users
  serial_send_blocking(UART0, buffer, size);
#else
  serial_send_blocking(buffer, size);
#endif
}
  
// required to allow motors to move  
// must be called when controller restarts and after any error  
void exitSafeStart()  
{  
  command[0] = 0x83;
  sendBlocking(command, 1);  
}  
  
// speed should be a number from -3200 to 3200  
void setMotorSpeed(int speed)  
{  
  if (speed < 0)  
  {  
    command[0] = 0x86;  // motor reverse command  
    speed = -speed;  // make speed positive  
  }  
  else  
  {  
    command[0] = 0x85;  // motor forward command  
  }
  command[1] = speed & 0x1F;
  command[2] = speed >> 5;
  sendBlocking(command, 3);  
}  
  
// initialization code called once when the program starts running
void setup()    
{
  // initialize hardware serial (UART0) with baud rate of 115.2 kbps
  setBaudRate(115200);

  // the Simple Motor Controller must be running for at least 1 ms
  // before we try to send serial data, so we delay here for 5 ms
  delay_ms(5); 
  
  // if the Simple Motor Controller has automatic baud detection
  // enabled, we first need to send it the byte 0xAA (170 in decimal)
  // so that it can learn the baud rate
  command[0] = 0xAA;
  sendBlocking(command, 1);  // send baud-indicator byte

  // next we need to send the Exit Safe Start command, which
  // clears the safe-start violation and lets the motor run
  exitSafeStart();  // clear the safe-start violation and let the motor run
}  

// program execution starts here
int main()
{
  setup();
  while (1)  // loop forever
  {
    setMotorSpeed(3200);
    delay_ms(1000);
    setMotorSpeed(-3200);
    delay_ms(1000);
  }
}
Advanced Example

This example assumes the following connections exist between the Orangutan and the Simple Motor Controller:

  • Orangutan pin PD0 to Simple Motor Controller TX
  • Orangutan pin PD1 to Simple Motor Controller RX
  • Orangutan pin PC0 to Simple Motor Controller RST
  • Orangutan pin PC1 to Simple Motor Controller ERR
  • Orangutan GND to Simple Motor Controller GND

Pins PD0 and PD1 are the Orangutan’s hardware serial receive and transmit lines, respectively, and must be connected to the Simple Motor Controller as described above for this sample program to work. There is nothing special about pins PC0 and PC1, however; you can connect any free digital pins to the Simple Motor Controller RST and ERR pins if you change the pin definitions at the top of the sample program accordingly. See Section 4.2 for more information on connecting a serial device to the Simple Motor Controller.

This program demonstrates how to initiate serial communication with the Simple Motor Controller and how to send commands to set the motor speed, read variables, and change the temporary motor limits. For information about the serial commands used by this sample code, refer to Section 6.2.1. It will be more interesting if you have input power and a motor connected to your Simple Motor Controller (see Section 4.1), but you can see some interesting things even without a motor connected by using the Status tab of the Simple Motor Control Center application to monitor the effect this program has on the controller’s variables (see Section 3.3).

#include <pololu/orangutan.h>  
#define resetPin IO_C0  // pin PC0 connects to SMC nRST
#define errPin IO_C1  // pin PC1 connects to SMC ERR
  
// some variable IDs  
#define ERROR_STATUS 0  
#define LIMIT_STATUS 3  
#define TARGET_SPEED 20
#define INPUT_VOLTAGE 23
#define TEMPERATURE 24
  
// some motor limit IDs  
#define FORWARD_ACCELERATION 5  
#define REVERSE_ACCELERATION 9  
#define DECELERATION 2

char command[4];

// These first three functions call the appropriate Pololu AVR library serial functions
// depending on which Orangutan you are using.  The Orangutan SVP and X2 have multiple
// serial ports, so the serial functions for these devices require an extra argument
// specifying which port to use.  You can simplify this program by just calling the
// library function appropriate for your Orangutan board.

void setBaudRate(unsigned long baud)
{
#if _SERIAL_PORTS > 1  // Orangutan X2 and SVP users
  serial_set_baud_rate(UART0, baud);
#else
  serial_set_baud_rate(baud);
#endif
}

void sendBlocking(char * buffer, unsigned char size)
{
#if _SERIAL_PORTS > 1  // Orangutan X2 and SVP users
  serial_send_blocking(UART0, buffer, size);
#else
  serial_send_blocking(buffer, size);
#endif
}

char receiveBlocking(char * buffer, unsigned char size, unsigned int timeout_ms)
{
#if _SERIAL_PORTS > 1  // Orangutan X2 and SVP users
  return serial_receive_blocking(UART0, buffer, size, timeout_ms);
#else
  return serial_receive_blocking(buffer, size, timeout_ms);
#endif
}
  
// required to allow motors to move  
// must be called when controller restarts and after any error  
void exitSafeStart()  
{  
  command[0] = 0x83;
  sendBlocking(command, 1);  
}  
  
// speed should be a number from -3200 to 3200  
void setMotorSpeed(int speed)  
{  
  if (speed < 0)  
  {  
    command[0] = 0x86;  // motor reverse command  
    speed = -speed;  // make speed positive  
  }  
  else  
  {  
    command[0] = 0x85;  // motor forward command  
  }
  command[1] = speed & 0x1F;
  command[2] = speed >> 5;
  sendBlocking(command, 3);  
}  
  
char setMotorLimit(unsigned char  limitID, unsigned int limitValue)  
{
  command[0] = 0xA2;
  command[1] = limitID;
  command[2] = limitValue & 0x7F;
  command[3] = limitValue >> 7;
  sendBlocking(command, 4);
  
  char response = -1;
  receiveBlocking(&response, 1, 500);
  return response;  
}  
  
// returns the specified variable as an unsigned integer.
// if the requested variable is signed, the value returned by this function
// should be typecast as an int.
unsigned int getVariable(unsigned char variableID)  
{
  command[0] = 0xA1;
  command[1] = variableID;
  sendBlocking(command, 2);

  unsigned int response;
  if (receiveBlocking((char *)&response, 2, 500))
    return 0;  // if we don't get a response in 500 ms, return 0
  return response;
}  
  
// initialization code called once when the program starts running
void setup()    
{
  setBaudRate(115200);
  
  // briefly reset SMC when Arduino starts up (optional)
  set_digital_output(resetPin, LOW);
  delay_ms(1);  // wait 1 ms  
  set_digital_input(resetPin, HIGH_IMPEDANCE);  // let SMC run again  

  // must wait at least 1 ms after reset before transmitting
  delay_ms(5);

  // this lets us read the state of the SMC ERR pin (optional)
  set_digital_input(errPin, HIGH_IMPEDANCE);
  
  command[0] = 0xAA;
  sendBlocking(command, 1);  // send baud-indicator byte
  setMotorLimit(FORWARD_ACCELERATION, 4);  
  setMotorLimit(REVERSE_ACCELERATION, 10);  
  setMotorLimit(DECELERATION, 20);
  // clear the safe-start violation and let the motor run
  exitSafeStart();  
}  

// main loop of the program; this executes over and over while the program runs
void loop()  
{
  static int speed = 3200;  // full-speed forward

  setMotorSpeed(speed);
  speed = -speed;  // switch motor direction

  clear();  // clear the LCD and move cursor to start of first row
  print("ts=");
  // signed variables must be cast to ints:
  print_long((int)getVariable(TARGET_SPEED));
  lcd_goto_xy(0, 1);  // move LCD cursor to start of second row

  if (is_digital_input_high(errPin))
  {
    // if an error is stopping the motor, print the error status variable
    // in hex and try to re-enable the motor
    print("Err=");
    print_hex(getVariable(ERROR_STATUS));
    // once all other errors have been fixed, this lets the motor run again
    exitSafeStart();
  }
  else
  {
    // print input voltage (in Volts) to the LCD
    print("VIN=");
    unsigned int vin = getVariable(INPUT_VOLTAGE);
    // print truncated whole number of Volts
    print_unsigned_long(vin/1000);  
    print_character('.');
    // print rounded tenths of a Volt
    print_unsigned_long(((vin%1000) + 50) / 100);
  }

  delay_ms(1000);
}

// program execution starts here
int main()
{
  setup();
  while (1)
  {
    loop();
  }
}