Software

Introduction


As seen in the firmware section, it is possible to control RoboVero with a simple serial terminal application. However, for anything but the simplest of tasks, this can quickly become unwieldy. The solution is a language interface that allows RoboVero functions to be called from within a program. One such API is the RoboVero Python Client Library. Python has a comprehensive standard library and online documentation making it an accessible and powerful language for robotics applications.

Source


Overo COM Host

Depending on your distribution and image you might need to install python and some modules. On Angstrom,

$ opkg update
$ opkg install python
$ opkg install python-modules
$ opkg install python-pyserial

You also need to download the Python Client Library. Because git doesn't exist on Angstrom, you will need to maintain it yourself. You can mount the SD Card to copy the PCL over, or use cURL:

$ opkg install curl
$ curl -L -k https://github.com/robovero/python/tarball/master | tar zx

Note the RoboVero HubCommander Interface. For the Overo to see the RoboVero, you must disconnect the USB mini-B. SSH to your Overo to send commands.

Linux

The Python Client Library is available via github. If you don't already use git you will need to install it.

$ sudo apt-get install git

Now you can get the most up to date version of the library.

$ cd ~/robovero
$ git clone git://github.com/robovero/python.git

The repository will be stored in ~/robovero/python. Before moving on, let's make sure everything is up to date.

$ sudo apt-get update
$ sudo apt-get install python python-serial

Mac

If you are using Mac, install git from here. Now you can get the most up to date version of the library.

$ cd ~/robovero
$ git clone git://github.com/robovero/python.git

Python should already be installed on your Mac. To get pyserial, download the archive from http://pypi.python.org/pypi/pyserial. Unpack the archive, enter the directory in Terminal and run

$ python setup.py install

Windows

NOTE: The installation instructions for Windows are in progress. Visit the Question and Answer section if you need help installing on Windows. You may also need to update your firmware to get it to work.


If you are using Windows, follow the instructions here to obtain git. Run the following command in your RoboVero folder in Git Bash.

$ git clone git://github.com/robovero/python.git

Python also needs to be installed on your Windows computer. Get Python 2.7 from http://python.org/download/. After that, to get pyserial, download the archive from http://pypi.python.org/pypi/pyserial. Unpack the archive. Set your Environment Variables by following instructions http://docs.python.org/using/windows.html#excursus-setting-environment-variables. Enter the directory in Command Prompt and run

$ python setup.py install

Examples



The easiest way to get started with the Python Client Library is to try some of the included examples. Make sure that your RoboVero is powered, USB is connected, and the device has been reset (see pitfalls).

$ cd ~/robovero/python

Analog-to-Digital Converter

This examples measures a voltage on AD0_0.

$ python adc.py
1748
756
883
749
795
^C
keyboard interrupt: how rude!

Inertial Measurement

This example displays x, y, and z readings for the on-board accelerometer, compass, and gyro.

$ python IMU.py
a [x, y, z]:  [-880, -640, -16704]
c [x, y, z]:  [-257, -257, -257]
g [x, y, z]:  [-257, -257, -257]

a [x, y, z]:  [-832, -608, -16672]
c [x, y, z]:  [-257, -257, -257]
g [x, y, z]:  [-257, -257, -257]

^CTraceback (most recent call last):
  File "IMU.py", line 130, in <module>
    time.sleep(1)
KeyboardInterrupt

Servo

This example requires an RC servo connected to PWM1.

$ python servo.py
New angle: 10 
New angle: 100
enter an angle between 0 and 90 degrees
New angle: 20
New angle: 0
New angle: ^CTraceback (most recent call last):
  File "servo.py", line 65, in <module>
    match_value = getServoAngle()
  File "servo.py", line 22, in getServoAngle
    user_angle = raw_input("New angle: ")
KeyboardInterrupt


Update your Python Client Library to access the latest examples.

$ git pull

Usage


For the most part, you can call Python Client Library functions the same way as C Peripheral Driver functions - python will even forgive you if your function call is followed by a semi-colon. Some key differences are outlined below using the peripheral pin select as an example.

Includes

#include "lpc17xx_pinsel.h"           /* C */
import robovero.lpc17xx_pinsel        # Python


Additionally, unless you want to append each function call with the name of the module containing it, you need to import functions and types individually by name.

from robovero.lpc17xx_pinsel import PINSEL_CFG_Type, PINSEL_ConfigPin

Enums

C enums are represented by classes in Python. The enumerators are class variables. Here is the declaration of a function that accepts an enumerated type as its sole argument.

void PINSEL_ConfigTraceFunc (FunctionalState NewState);

And here is how it is used in python and C.

PINSEL_ConfigTraceFunc(ENABLE);                   /* C */
from robovero.lpc_types import FunctionalState    # Python
PINSEL_ConfigTraceFunc(FunctionalState.ENABLE);   

Structs

Structs in C are treated as classes in Python. The main differences are declaration and referencing.

Declaration

PINSEL_CFG_Type PinCfg;         /* C */
PinCfg = PINSEL_CFG_Type()      # Python

Referencing

PINSEL_ConfigPin(&PinCfg);      /* C */
PINSEL_ConfigPin(PinCfg.ptr)    # Python

Arrays

You can use lists, tuples, strings, etc. to store data in memory on the machine where python is running. In certain cases you need to store an array of data in RoboVero memory. One such case is to use the function UART_SEND which expects a pointer to an array of data. Here is one of many ways this can be achieved in C.

#include "string.h"
#include "lpc17xx_uart.h"
#include "lpc_types.h"
#include "LPC17xx.h"
#include "extras.h"

char msg[] = "RoboVero smash!";

roboveroConfig();
UART_Send(LPC_UART1, msg, strlen(msg), BLOCKING);

The robovero.extras module contains a class called Array. The initialization functions takes the number of elements, width of each element, and initialization values (optional) as arguments. Values can be a string, list, or single value to copy to RoboVero memory. In case a single value is provided, it is used for each element in the array.

from robovero.extras import Array, roboveroConfig
from robovero.LPC17xx import LPC_UART1
from robovero.lpc17xx_uart import UART_Send
from robovero.lpc_types import TRANSFER_BLOCK_Type

_msg = "RoboVero smash!"
msg = Array(len(_msg), 1, _msg)

roboveroConfig()
UART_Send(LPC_UART1, msg.ptr, msg.length, TRANSFER_BLOCK_Type.BLOCKING)

There is a documentation site for robovero python library at Robovero Python.                                                                                         

Advanced Workflow


Python can be used for developing your robotics applications quickly and interactively. Follow these steps if you find yourself limited by USB latency.

These four functions are often called sequentially to enable PWM in counter mode on a given channel.

PWM_ChannelCmd(LPC_PWM1, 1, FunctionalState.ENABLE)
PWM_ResetCounter(LPC_PWM1)
PWM_CounterCmd(LPC_PWM1, FunctionalState.ENABLE)
PWM_Cmd(LPC_PWM1, FunctionalState.ENABLE)

Step 1: Create a python function

def pwmCounterState(PWMChannel, NewState):
	PWM_ChannelCmd(LPC_PWM1, PWMChannel, NewState)
	PWM_ResetCounter(LPC_PWM1)
	PWM_CounterCmd(LPC_PWM1, NewState)
	PWM_Cmd(LPC_PWM1, NewState)
Now replace the four function calls with one call to pwmCounterState.
pwmCounterState(1, FunctionalState.ENABLE)
At this point we haven't done any optimization but the function can be tested quickly with various combinations of arguments to ensure that it behaves as expected.

Step 2: Port your function to C

Once you're satisfied with your new function, convert it to C and add it to extras.c

Remember to include any headers that you reference.

#include "lpc17xx_pwm.h"
Now comes the tricky part. For your function to be called over USB it must have accept a string representing the arguments and return an int. To get an idea of how this is done, it's best to look at the wrappers for the functions you are using. The following function definition can be found in src/wrap/_lpc17xx_pwm.c
int _PWM_ChannelCmd(uint8_t * args)
{
	uint8_t * arg_ptr;
	LPC_PWM_TypeDef* PWMx;
	uint8_t PWMChannel;
	FunctionalState NewState;

	if ((arg_ptr = (uint8_t *) strtok(NULL, " ")) == NULL) return 1;
	PWMx = (LPC_PWM_TypeDef*) strtoul((char *) arg_ptr, NULL, 16);
	if ((arg_ptr = (uint8_t *) strtok(NULL, " ")) == NULL) return 1;
	PWMChannel = (uint8_t) strtoul((char *) arg_ptr, NULL, 16);
	if ((arg_ptr = (uint8_t *) strtok(NULL, " ")) == NULL) return 1;
	NewState = (FunctionalState) strtoul((char *) arg_ptr, NULL, 16);

	PWM_ChannelCmd(PWMx, PWMChannel, NewState);
	return 0;
}

Which is fairly similar to our new function.

int _pwmCounterState(uint8_t * args)
{
	uint8_t * arg_ptr;
	uint8_t PWMChannel;
	FunctionalState NewState;

	if ((arg_ptr = (uint8_t *) strtok(NULL, " ")) == NULL) return 1;
	PWMChannel = (uint8_t) strtoul((char *) arg_ptr, NULL, 16);
	if ((arg_ptr = (uint8_t *) strtok(NULL, " ")) == NULL) return 1;
	NewState = (FunctionalState) strtoul((char *) arg_ptr, NULL, 16);

	PWM_ChannelCmd(LPC_PWM1, PWMChannel, NewState);
	PWM_ResetCounter(LPC_PWM1);
	PWM_CounterCmd(LPC_PWM1, NewState);
	PWM_Cmd(LPC_PWM1, NewState);

	return 0;
}

Let's take a closer look. First, prepend the function name with an underscore as a reminder that the function is intended to be called by the USB interface only. The arguments and return type are mandatory.

int _pwmCounterState(uint8_t * args)

Arguments are passed to the function in a string separated by spaces. This first variable declared below is used as a pointer to the individual arguments. The other variables are used to store the arguments.

uint8_t * arg_ptr;
uint8_t PWMChannel;
FunctionalState NewState;

Now the arguments are separated, converted from string to unsigned long, and then typecast. If an argument is missing the function returns 1.

if ((arg_ptr = (uint8_t *) strtok(NULL, " ")) == NULL) return 1;
PWMChannel = (uint8_t) strtoul((char *) arg_ptr, NULL, 16);
if ((arg_ptr = (uint8_t *) strtok(NULL, " ")) == NULL) return 1;
NewState = (FunctionalState) strtoul((char *) arg_ptr, NULL, 16);

And finally, call the functions

PWM_ChannelCmd(LPC_PWM1, PWMChannel, NewState);
PWM_ResetCounter(LPC_PWM1);
PWM_CounterCmd(LPC_PWM1, NewState);
PWM_Cmd(LPC_PWM1, NewState);

return 0;

The return value is used to indicate the status of the operation. If your function needs to return data, it needs to be sent via USB. The following snippet is from _PWM_GetCaptureValue which wraps the library function PWM_GetCaptureValue and returns an unsigned int.

sprintf((char *) str, "%x\r\n", (unsigned int) PWM_GetCaptureValue(PWMx, CaptureChannel));
writeUSBOutString(str);
return 0;

Step 3: Append the driver function table

Add this entry to the driver function table in src/table.c. The first parameter is the command that causes your function to be invoked. The second parameter is the pointer to your function.

{(uint8_t *) "pwmCounterState", _pwmCounterState},
Now add the prototype to extras.h
int pwmCounterState(uint8_t * args);

Step 4: Replace the python function

Add this new function to extras.py

def pwmCounterState(PWMChannel, NewState):
  return robocaller("pwmCounterState", "void", PWMChannel, NewState)
Now remove the old function declaration from your original source file and replace it with an import.
+ from robovero.extras import pwmCounterState

- def pwmCounterState(PWMChannel, NewState):
-   PWM_ChannelCmd(LPC_PWM1, PWMChannel, NewState)
-   PWM_ResetCounter(LPC_PWM1)
-   PWM_CounterCmd(LPC_PWM1, NewState)
-   PWM_Cmd(LPC_PWM1, NewState)

That's it! We've just reduced four USB function calls to one.

Tips

extras.c and extras.py contain more examples of this process. 


If you absolutely must use interrupts, there is no need to ditch the Python Client Library for a pure C firmware implementation. Simply write and test your interrupt service routine - you might be able to do so in python before porting to C - and then add it to the vector table in startup.c. Now, you can enable the interrupt through your python application and expect it to be handled with minimal latency.


Pitfalls


Here are some hangups to watch out for when developing with RoboVero.


> There is currently no mechanism to synchronize firmware and software versions. If an example isn't working for you, it is possible that it was written for a more recent revision than that which shipped with your RoboVero. Update your firmware to the most recent version.


> When you first connect to RoboVero it asks you to press enter to begin. This is used to detect the line termination that your host machine is using - whether it be an Overo COM, desktop PC, etc. It is possible that PCL sends different line terminators than your favorite serial application (Kermit, Minicom, etc.). If such is the case, you will have to perform a hard reset when switching from Kermit to your program or vice-versa.


 
Designed by Business wordpress themes and Joomla templates.