Hacking the Fluke
The Fluke sits between software running the Myro API and a robot. In fact, you can talk to a robot (such as the Scribbler) directly. Some of the following byte codes can be used without the Fluke, as they are Scribbler commands. If using the Fluke, it will intercept any byte-code commands that it knows about, and will return appropriate responses. However, if the Fluke does not know the byte-code, it will pass it on to the Scribbler, and relay its response---thereby acting as a wireless serial cable. The Fluke handles camera commands, obstacle commands, and brightness commands. The Scribbler handles everything else (sounds, movement, IR, battery, etc). Commands such as DATA are handled by both: a DATA requested is made by the Myro API software, received by the Fluke, passed to the Scribbler, responded by the Scribbler to the Fluke, to which the Fluke adds some data and passes it back to the host Myro API software.
The Fluke is a small electronic board that contains wireless Bluetooth, camera, IR (infrared) sensors, LEDs (light emitting diodes) and an ARM microprocessor. By itself (with a battery), it is a complete (although immobile) robot that can receive simple commands and send back IR readings, camera images, and turn on its LED lights. Connect it to a robot and you can control the robot wirelessly, and add vision and sensors to it. More on the prototyping of the fluke can be found here.
This page is designed to get you started in exploring the advanced capabilities of the Fluke.
You can purchase a Fluke at Georgia Robotics.
The Fluke has easy to use software built into Myro, using Python, for the Scribbler robot from Parallax. This page describes how to write your own software, and use it for robots other than the Scribbler.
The Byte Codes
The Fluke (and underlying robots) are controlled through sending byte codes, or messages. The interface is used in the following files:
The byte-codes are:
#SCRIBBLER-SPECIFIC SOFT_RESET=33 GET_ALL=65 GET_ALL_BINARY=66 GET_LIGHT_LEFT=67 GET_LIGHT_CENTER=68 GET_LIGHT_RIGHT=69 GET_LIGHT_ALL=70 GET_IR_LEFT=71 GET_IR_RIGHT=72 GET_IR_ALL=73 GET_LINE_LEFT=74 GET_LINE_RIGHT=75 GET_LINE_ALL=76 GET_STATE=77 GET_NAME1=78 GET_NAME2=64 GET_STALL=79 GET_INFO=80 GET_DATA=81 GET_PASS1=50 GET_PASS2=51 #FLUKE-SPECIFIC GET_RLE=82 # a segmented and run-length encoded image GET_IMAGE=83 # the entire 256 x 192 image in YUYV format GET_WINDOW=84 # the windowed image (followed by which window) GET_DONGLE_L_IR=85 # number of returned pulses when left emitter is turned on GET_DONGLE_C_IR=86 # number of returned pulses when center emitter is turned on GET_DONGLE_R_IR=87 # number of returned pulses when right emitter is turned on GET_WINDOW_LIGHT=88 # average intensity in the user defined region GET_BATTERY=89 # battery voltage GET_SERIAL_MEM=90 # with the address returns the value in serial memory GET_SCRIB_PROGRAM=91 # with offset, returns the scribbler program buffer GET_CAM_PARAM=92 # with address, returns the camera parameter at that address GET_BLOB=95 #SCRIBBLER-SPECIFIC SET_PASS1=55 SET_PASS2=56 SET_SINGLE_DATA=96 SET_DATA=97 SET_ECHO_MODE=98 SET_LED_LEFT_ON=99 SET_LED_LEFT_OFF=100 SET_LED_CENTER_ON=101 SET_LED_CENTER_OFF=102 SET_LED_RIGHT_ON=103 SET_LED_RIGHT_OFF=104 SET_LED_ALL_ON=105 SET_LED_ALL_OFF=106 SET_LED_ALL=107 SET_MOTORS_OFF=108 SET_MOTORS=109 SET_NAME1=110 SET_NAME2=119 # set name2 byte SET_LOUD=111 SET_QUIET=112 SET_SPEAKER=113 SET_SPEAKER_2=114 #FLUKE-SPECIFIC SET_DONGLE_LED_ON=116 # turn binary dongle led on SET_DONGLE_LED_OFF=117 # turn binary dongle led off SET_RLE=118 # set rle parameters SET_DONGLE_IR=120 # set dongle IR power SET_SERIAL_MEM=121 # set serial memory byte SET_SCRIB_PROGRAM=122 # set scribbler program memory byte SET_START_PROGRAM=123 # initiate scribbler programming process SET_RESET_SCRIBBLER=124 # hard reset scribbler SET_SERIAL_ERASE=125 # erase serial memory SET_DIMMER_LED=126 # set dimmer led SET_WINDOW=127 # set user defined window SET_FORWARDNESS=128 # set direction of scribbler SET_WHITE_BALANCE=129 # turn on white balance on camera SET_NO_WHITE_BALANCE=130 # diable white balance on camera (default) SET_CAM_PARAM=131 # with address and value, sets the camera parameter at that address GET_JPEG_GRAY_HEADER=135 GET_JPEG_GRAY_SCAN=136 GET_JPEG_COLOR_HEADER=137 GET_JPEG_COLOR_SCAN=138
To send a message to the Scribbler, you need to put the byte into 9-byte packet, however if the command is for the Fluke, the packet will be variable length.
For example, to get the battery voltage from the Fluke, you would send the number 89 as a byte. You can test this in any language or system, including something like Hyperterm. Here is this example in Python:
>>> import serial >>> ser = serial.Serial("COM1", 57600) >>> ser.write(chr(89)) >>> bytes = ser.inWaiting() >>> battery_level = ser.read(bytes)
You will need to interpret the results. For example, see the function getBattery in the Python Interface.
Or to set the IR power to full-power:
>>> import serial >>> ser = serial.Serial("COM1", 57600) >>> ser.write(chr(120)) >>> ser.write(chr(255))
More detail on using the Fluke on other robots can be found below.
Building the Fluke Firwmare
Firmware is another name for the software running on an "embedded system", like the Fluke. The firmware is usually written and compiled on one computer, and then transfered to the Fluke.
When you compile a program on one computer for use on another, that is called "cross-compiling" and requires a special environment and tools (often called a "toolchain"). The toolchain can be installed on Fedora9 with:
yum install arm-gp2x-linux-binutils arm-gp2x-linux-gcc
NOTE: the current arm-elf cross-compiler version 4.1.2 seems to have a bug:
/usr/lib/gcc/arm-gp2x-linux/4.1.2//libgcc.a(_dvmd_lnx.o): In function `__div0': ../../gcc-4.1.2/gcc/config/arm/lib1funcs.asm:1000: undefined reference to `raise'
The arm-elf cross-compiler version 4.1.1 seems to work fine, but you must install from source code. As root:
wget http://www.gnuarm.com/binutils-2.17.tar.bz2 bzip2 -d binutils-2.17.tar.bz2 tar -xf binutils-2.17.tar cd binutils-2.17 ./configure --target=arm-elf --prefix=/usr/local/arm-elf/ --enable-interwork --enable-multilib \ --enable-languages="c,c++" --disable-werror make all make install export PATH=$PATH:/usr/local/arm-elf/bin cd .. wget http://www.gnuarm.com/newlib-1.14.0.tar.gz tar xfz newlib-1.14.0.tar.gz wget http://www.gnuarm.com/gcc-4.1.1.tar.bz2 bzip2 -d gcc-4.1.1.tar.bz2 tar -xf gcc-4.1.1.tar cd gcc-4.1.1 ./configure --target=arm-elf --prefix=/usr/local/arm-elf/ --enable-interwork --enable-multilib \ --enable-languages="c,c++" --with-newlib --with-headers=../newlib-1.14.0/newlib/libc/include make all-gcc make install-gcc cd .. ln -s /usr/local/arm-elf/bin/arm-elf-gcc /usr/local/arm-elf/bin/arm-elf-cc cd newlib-1.14.0 ./configure --target=arm-elf --prefix=/usr/local/arm-elf/ --enable-interwork --enable-multilib # edit Makefile and change MAKEINFO to be path to makeinfo (ie, /usr/bin/makeinfo) make make install
All the fluke firmware can be found in the myro SVN repository. You can get it with:
svn export http://svn.cs.brynmawr.edu/Myro/trunk/Fluke
The IPRE Fluke uses the LPC2106 ARM microcontroller from Phillips. We use the GNU ARM toolchain for development.
After you have installed arm gcc and downloaded fluke firmware, you'll probably have to change the Makefile to set the LIBGCC variable to point to the correct location of where libgcc.a is located. For example:
Next, you can set the PATH and compile the code:
export PATH=/usr/local/arm/bin make
Then you download the firmware over bluetooth using:
make dl2 # will run: # python update-firmware.py main.hex /dev/tty.scribbler5844 fast
Or download the firmware using a null modem serial cable. Use the IPRE version of the lpc21isp program to upload new code using the serial link (see below for binary and more info). When upgrading the firmware using the null modem cable a separate power cable will be necessary. Either using a 9 volt battery or a wall adapter (6-9 volts).
make dl # will run: # lpc21isp2 -control main.hex /dev/tty.usbserial 19200 20000)
To run plug into the scribbler or use external power connector and:
python >>> from myro import * >>> init()
Hacking the Fluke2
The fluke2 login is:
username root password password
I am working on making some better documentation for fluke2 hacking. The fluke2 is designed to make it easy for people to login to the native linux system. There is about 1GB of linux programs, tools and utilities pre-installed on the fluke2 memory card.
Currently you have to login using a usb/serial converter in the micro usb connector - as you have already figured out. I have experimentally been able to ssh/telnet in over bluetooth using the bluetooth PAN profile which sets up TCP/IP over bluetooth networking. It worked great once I got it running but it was a little tricky to get it started. I am working on a good way to automatically start this up so its easy to ssh into the fluke2.
You can absolutely also just pop out the flash memory card and mount it in a desktop linux computer and make any changes you would like. Its just a standard ext2 filesystem. There are 4 partitions on the card. The first one is the root ext2 filesystem. The second one is a spare auxiliary 1GB ext2 filesystem that is unused by anything and available for user data. The 3rd partition is used by the IPRE fluke2srv server to emulate the fluke1 serial EEPROM and store non-filesystem data like the bluetooth link keys. The fourth partition has the bootloader.
Once the root filesystem is running in the fluke2 it gets a little more complicated. The fluke2 mounts the root filesytem as an overlay filesystem with a large but read-only ext2 filesystem (/mnt/readonly) at the base and a small 5 meg ramdisk (/mnt/ramdisk) sitting on top of that. Any changes go to the ramdisk. The underlying ext2 filesystem has to be read-only to allow for just killing power to the fluke2 without requiring a shutdown to prevent filesystem damage. So any changes you make to the fluke2 filesystem while you are logged in are volatile and will be erased on the next reboot.
If you need to make permanent changes to the fluke2 filesystem you can use an external card reader or you can use the commands below.
Command to mount readwrite. Edit files under /mnt/readonly directory:
mount -o remount,rw /dev/mmcblk0p1 /mnt/readonly
Command to remount readonly. Must either shutdown or execute this command before killing power to avoid filesystem corruption:
mount -o remount,ro /dev/mmcblk0p1 /mnt/readonly
The fluke2 runs a very simple set of two startup bash scripts. The first script that is executed on startup is /etc/rc.d/rc.S and the second script executed is /etc/rc.d/rc.M. If you edit the rc.M script and add your own executable at the end then this executable will always be run on startup.
If you mess up your flash card you can download the full raw image here. You can dd this image to the card to reset it to the factory default status: http://www.betterbots.com/download/fluke2_image-9-10-12.disk.dd.7z
The fluke2 has a native gcc C compiler already installed. You can login to the fluke and natively compile C programs and they execute fine. If you want to cross compile on your PC and then transfer the executables I use the codesourcery lite free toolchain: http://go.mentor.com/253xm
They also have a version available that runs under windows I think. For transferring the executable to the fluke you can either pop out the flash card or use a z-modem transfer when you are logged in through your serial terminal emulator. There is a zmodem server executable already on the fluke2 installed at /home/fluke2/tmp/lrz
The fluke2 also has a python interpreter already installed. I briefly tested it to make sure it works but I didn't try anything fancy with it. Myro is not locally installed on the fluke2 so you'd need to install that before locally running a python program that used myro.
It should definitely be possible to locally run a myro python script that drove the robot around but you'd have to modify myro so it was able to connect to the fluke2srv executable over the bluetooth equivalent of localhost. I'm not sure how to do that.
Alternatively all of the fluke2 hardware has linux device drivers so its pretty easy to write a C program that talks to the scribbler or any of the other hardware. Take a look at the fluke2srv source to see how. The source is installed on the flash card in /home/fluke2/server.
The fluke2 scribbler serial port device is /dev/ttyfiq. Its not a standard serial port because it is emulated by software. You can't use linux serial tools like minicom to talk to it because the driver is missing a lot of serial port functionality. However you can just open the device like a file and read/write to it. You can also execute: echo hello > /dev/ttyfiq
To control the scribbler you would just have to echo the scribbler command codes to it.
There are no fluke2 C libraries. All of the code is in the fluke2srv executable.
The fluke2 root filesystem is mostly full but the second partition has a totally empty 1GB ext2 filesystem that is only used for the upgrade("fluke") command. You can mount this partition and write anything you want to it. Make sure you umount or shutdown cleanly though before killing power because the fluke2 is very sensitive to filesystem corruption if something is mounted read/write and you just kill the power.
You are getting the incorrect 3% free from the df command because of the funky RAM overlay filesystem. The RAM overlay allows for only 5 megs of changes to the root filesystem that are erased on the next reboot. See my previous long email for more details.
Really the only external interface are the 4 servo ports. Each servo port has a digital IO line from the processor. There is a standard GPIOLIB linux command line interface. If you google for linux GPIOLIB you can find out how it works. Basically you can run the commands below:
echo 1 > /sys/class/gpio/export echo out > /sys/class/gpio/gpio1/direction echo 1 > /sys/class/gpio/gpio1/value
I think the 4 gpio servo lines should be numbered 1 through 4 in gpiolib. I can double check later which number lines up with which pin.
I am also working on enabling actual servos to be controlled from the servo ports using bluetooth myro commands.
Here are a couple of notes on how to control one of the LEDs on the Fluke2 via the embedded Python:
1. Build a shared object file:
# cd /home/fluke2/server # make # gcc main.o fluke2.o fluke2cmd.o camera.o blue tooth.o gpio.o misc.o scribbler.o -shared -o fluke2cmd.so -lbluetooth -ljpeg
2. Use ctypes to load it:
# python >>> from ctypes import * >>> cdll.LoadLibrary("/home/fluke2/server/fluke2cmd.so") <CDLL '/home/fluke2/server/fluke2cmd.so', handle b80c0 at 4048bc10> >>> libc = CDLL("/home/fluke2/server/fluke2cmd.so")
3. Do something:
>>> libc.gpio_set_value(14, 1) # LED on >>> libc.gpio_set_value(14, 0) # LED off
So, it looks like it would be fairly easy to wrap the Fluke functionality for Python and an embedded Myro.py. And using the /dev/scribbler serial port, I guess we could do those as well.
- windows binary: http://myro.roboteducation.org/packages/lpc21isp-ipre-win.zip
- linux binary: Media:Lpc21isp.bin
- source: http://www.roboteducation.org/files/lpc21isp-ipre.c
Building an External Power Connector
The Fluke is normally powered over the serial port of the scribbler (pin 8), but it can also be powered by an external power source (e.g. a 9V battery). The external power plugs into the 2 pin white external conector housing near the bottom of the board (below the 2 5-pin connectors). The bottom pin is ground. Part list:
- 2 pos connector housing - digikey: 455-1486-ND manufacturer: PAP-02V-S
- connector crimp terminal - digikey: 455-1325-1-ND manufacturer: SPHD-001T-P0.5
- wall transformer - digikey: T978-P7P-ND manufacturer: EPS060100-P7P
The Fluke was designed to be connected to a Scribbler or PC serial port so its using PC style RS232 voltages (+/- 12V) rather than TTY (5V). The RTS/CTS pins 6 and 8 are inverted and used for programming not synchronization (since there is no RTS/CTS on the scribbler we decided to do this).
A nice USB/serial adapter (works great on mac, some others don't work so well):
A female-female RS232 null modem adapter:
By default the Fluke talks to the underlying robot at 38400 baud, 8N1, but this can be changed in the firmware, or via a dynamic command. You can talk to the Fluke over Bluetooth at up to 460800 baud.
Pin 1: Reset (inverted) Pin 2: Receive Pin 3: Transmit Pin 4: Scribbler Reset Pin 5: Ground Pin 6: Reset (inverted) Pin 7: Not Connected Pin 8: Boot Mode (inverted) and Power Pin 9: Not Connected
There are six Fluke commands related to controlling the underlying device/robot:
- put the fluke in complete passthrough mode
- send N bytes through the Fluke to the robot
- get N bytes back from the robot
- get bytes back from robot until ending byte
- turn on passthrough so the underlying robot can send data back over the Bluetooth
- turn off passthrough so the underlying robot no longer sends data back over the Bluetooth
You can get the latest firmware (main.hex) from SVN at http://svn.cs.brynmawr.edu/Myro/trunk/Fluke/firmware/.
The codes (from ipre-bytecodes.h) are:
#define SET_PASSTHROUGH 134 #define SET_PASS_N_BYTES 139 #define GET_PASS_N_BYTES 140 #define GET_PASS_BYTES_UNTIL 141 #define SET_PASSTHROUGH_ON 143 #define SET_PASSTHROUGH_OFF 144
Loading Firmware onto Fluke
To load via Bluetooth using Myro:
- Save main.hex in directory
- Start python and upload:
- $ python
- >>> from myro import *
- >>> upgrade_fluke("main.hex")
- give Bluetooth device name (eg, /dev/rfcomm2, /dev/usbserial0, com5, etc)
or via a cable, from the terminal/console:
$ PORT=/dev/ttyS0 make download
or from any operating system:
Windows: lpc21isp -control main.hex COM1 19200 20000 Unix: ./lpc21isp -control main.hex /dev/ttyS0 19200 20000
On Unix, replace ttyS0 with your serial port's device name.
To test, using Myro Python library (needs Myro 2.8.3 or higher):
$ python >>> from myro.robots.fluke import Fluke >>> robot = Fluke("/dev/rfcomm2")
You should see a fluke version number echoed to the screen. If you don't then turn the fluke off and then back on, and try making the connection again (last line). Once you see a version number, you are ready to continue.
If you use Myro's Fluke interface, the serial port is robot.ser. All of the following should work talking directory to the serial port. The Fluke interface has all of the built-in functions for talking to the Fluke directly (such as processing the images from the camera).
If you are not using Myro, you can just open a serial connection to the fluke and send it the bytecode for returning the version number. Here, using pyserial in Python:
>>> import serial >>> ser = serial.Serial("COM1", 57600) >>> ser.write(chr(142)) # get fluke version >>> ser.inWaiting() # responds with number of bytes ready to read >>> ser.readline()
NOTE: On Windows, for COM ports greater than 9, you must use a format that looks like: r"\\.\COM11" (which is COM11)
You can ser.close() and ser.open() to close and reopen the port.
To send N bytes to the underlying robot (in this example, a Scribbler):
>>> import serial >>> ser = serial.Serial("COM1", 57600) >>> ser.write(chr(143)) # turn on passthrough from the robot >>> ser.write("".join(map(chr, [139, N, N1, ...]))) # send N bytes (N1, N2, ..., NN) >>> ser.inWaiting() 20 >>> ser.read(20) >>> ser.write(chr(144)) # turn off passthrough from the robot
When talking to the Scribbler, this will automatically give you 20 bytes back: 9 for the packet echo, and 11 for all sensors values.
If you want to read a particular number of bytes back:
>>> ser.write("".join(map(chr, [140, N]))) # read N bytes >>> ser.inWaiting() # should echo back N >>> ser.read(N) # read the bytes
Or until a certain character:
>>> ser.write(chr(141) + "\n"]) # read until newline >>> ser.readline()
To change the baud rate between the Fluke and the 9-pin UART (not the PC to Bluetooth connection) then there is a command for that:
#define SET_UART0 132
This takes a magic code (1), and three parameters, given in order by the following codes.
First, the baud rate:
#define NB1200 0 #define NB2400 1 #define NB4800 2 #define NB9600 3 #define NB19200 4 #define NB38400 5 #define NB57600 6 #define NB115200 7 #define NB230400 8 #define NB460800 9 #define NB921600 10 #define NB1843200 11 #define NB3686400 12
Second, the bit settings:
#define NUART_8N1 0 #define NUART_7N1 1 #define NUART_8N2 2 #define NUART_7N2 3 #define NUART_8E1 4 #define NUART_7E1 5 #define NUART_8E2 6 #define NUART_7E2 7 #define NUART_8O1 8 #define NUART_7O1 9 #define NUART_8O2 10 #define NUART_7O2 11
and last the FIFO setting:
#define NUART_FIFO_OFF 0 #define NUART_FIFO_1 1 #define NUART_FIFO_4 2 #define NUART_FIFO_8 3 #define NUART_FIFO_14 4
So, to set the Fluke-to-robot baud rate to 1200 baud, 8N1, with FIFO off, send this:
>>> ser.write( chr(132) + chr(1) + chr(0) + chr(0) + chr(0))
To set the Fluke-to-robot baud rate to 57600 baud, 7N2, with FIFO 8, send this:
>>> ser.write( chr(132) + chr(1) + chr(6) + chr(3) + chr(3))
The Fluke as the head of a Robonova:
The Fluke on the Roomba:
The Fluke on the Tevbot: