Mavlink
Back to Flexbot main page
Contents |
Introduction
The MAVLink protocol have been implemented in the Flexbot project to secure a standardized communication protocol with as little overhead as possible. This allows for not only transferring data over the serial USB interface between the Teensy and Intel NUC, but also for wireless transfer of all data from the NUC to client(s) and vice versa.
For more information of the MAVLink protocol itself see http://qgroundcontrol.org/mavlink/start.
This page will give information on how MAVLink is utilized in this project as well as how to update the main flexbot.xml message container and generate the C-headers + Python files needed.
Prerequisites
Make sure to have downloaded the newest version of the Flexbot repository and head into code/MAVLink/ and make sure flexbot.xml, Bridge/ and Generator/ is present.
It is further needed to install "python future" to run the MAVgenerate python application.
$ pip install future
Adding messages to Flexbot
MAV is used between the main processor (the base_node_ros node) and the 5 Teensy controllers.
file structure
code +-- base_node_ros // ros node (accessed from catkin workspace) | +-- include // directory for generated MAV library | +-- flexbot_2 // generated directory +-- MAVLink // MAV link definition directory | +-- flexbot_2.xml // definition of MAV link | +-- Generator // MAV library generator (python code) | +-- mavgenerate.py // python code for MAV library generation +-- teensy_2 // code for teensy | +-- src // source code | +-- Makefile // normal makefile (not using arduino IDE) | +-- teensy_2.hex // generated code for teensy | +-- teensy3 // link to teensyduino installation: arduino-1.x.y/hardware/teensy/avr/cores/teensy3 | +-- libteensy // link to teensyduin0 installation: arduino-1.x.y/hardware/teensy/avr/libraries | +-- tools // link to teensyduino installation: arduino-1.x.y/hardware/tools +-- 99-flexbot-legs.rules // definition of leg interface - should be linked from /udev/rules.d/
UDEV device names
The 99-flexbot-legs.rules holds rules that makes a teensy device unique, i.e. makes a symbolic link that is unique regardless of connection order.
make a link in /etc/udev/rules.d/ to this file, e.g.
cd /etc/udev/rules.d sudo ln -s /home/local/flexbot/code/99-fklexbot-legs.rules
to find the serial number for a device, connect just one device and
udevadm info -q all -n /dev/ttyACM0
find the ID_SERIAL_SHORT=xxxxx and insert that number into ATTRS{serial}=="xxxxx" to make udev read the changes you probably need:
sudo udevadm control --reload-rules
The rules in 99-fklexbot-legs.rules for the 5 Teensy processors look something like:
SUBSYSTEM=="tty", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="0483", ATTRS{serial}=="4004150", SYMLINK+="flexbot_base" SUBSYSTEM=="tty", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="0483", ATTRS{serial}=="4145230", SYMLINK+="flexbot_leg1" SUBSYSTEM=="tty", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="0483", ATTRS{serial}=="4000001", SYMLINK+="flexbot_leg2" SUBSYSTEM=="tty", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="0483", ATTRS{serial}=="4000002", SYMLINK+="flexbot_leg3" SUBSYSTEM=="tty", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="0483", ATTRS{serial}=="4000003", SYMLINK+="flexbot_leg4"
Message definition
All messages/signals to/from Flexbot are defined in flexbot_2.xml. A message is defined by an ID, name and description. All signals related to this message is defined by a type (uint8_t, float, etc), name and signal description.
E.g. to add a new message to Flexbot:
<message id="16" name="TestSignal"> <description>Test signal for this example</description> <field type="float" name="value1">Floating point test value.</field> <field type="uint8_t" name="publish">Sets whether or not Teensy should publish this message.</field> </message>
An important note which goes for all messages is that it must contain the publish signal. This signal allows clients to subscribe to different messages and will save bandwidth as the Teensy only will publish messages that has subscribers.
By updating flexbot_2.xml nothing magical will happen. In order to integrate the new version of the message definitions, C-headers must be generated and the source code on both Teensy and the GUI must be altered to support the new message definitions.
Generating C-headers & Python file
In order to generate the header files used by the Teensy and GUI, a MAVLink generator program is used. This translates the message definition file (flexbot_2.xml) into a bunch of files related to the chosen language. In order to run the generator, go to /code/MAVLink/Generator and type
cd code/MAVLink/Generator python mavgenerate.py
This program should only depend on Python itself future and Tkinter. See https://github.com/mavlink/mavlink for references.
In the GUI, choose XML file
~/flexbot/code/MAVLink/flexbot_2.xml
and set output path to
~/flexbot/code/base_node_ros/include
Set the protocol to version 1.0 and choose the desired language (C).
By pressing generate, the program should generate a set of files if C has been chosen and a single file for Python.
The generated MAV C files is generated into
~/flexbot/code/base_node_ros/include
Here it will make a subdirectory called flexbot_2 with the main file to include "mavlink.h"
Old code
The old code generated by Jesper and Servet need this:
The files generated for C will need to be put into two locations. First copy all the generated files into
/code/Teensy/src/mavlink
and replace with the ones already existing. Repeat the process for the GUI - files are located at
/code/GUI/flexbot_visualizer/mavlink
Python
The Python code is used by the Jesper/Servet code only:
For Python, the generator produces an error. This is corrected by changing line 13 from
13: from ...generator.mavcrc import x25crc
to
13: from mavcrc import x25crc
Now move the newly generated file to /code/MAVLink/Bridge and replace it with the one already existing.
Update Teensy and bridge and visualizer source code to support new messages
NB! most of this is old code, this is replaced with a ROS interface (base_node_ros).
This is considered the most important part. This ensures that the newly introduced messages are supported by software in both the Teensy source code and in the Flexbot Visualizer. There is a bit manual labour here, but with the given MAVLink protocol there is no way around it.
Updating Teensy source code
A few steps needs to be taken here to ensure that the new message is supported. First locate and open
/code/Teensy/src/command.cpp
Starting from (about) line 320 is a function called
void MAVLink_msg_handler(mavlink_message_t _msg)
This is where the incomming MAVLink messages will be processed. In order to support new messages there needs to be a section that processes each message. Follow the syntax in the code to append to the function.
E.g. for a message with ID = 3 the processing will look similar to this
else if(ID == 3) { // DCCmd mavlink_dcwheelcmd_t msg; mavlink_msg_dcwheelcmd_decode(&_msg,&msg); dc_wheel.set(msg.dutycycle,msg.direction); dc_wheel.publish = msg.publish; }
I.e. a struct available from the MAVLink generated files needs to be initialised. This struct needs to be the one that matches the message. Next the message is decoded and put into the struct. From the struct all signals will be available as showed in the example and existing source code.
The general idea is that each message is associated with a class (or signal) that has an attribute called publish. This will become useful when printing data over the USB serial interface. This is seen by looking at
void printSensorStatus()
in command.cpp. This function call all classes' print_data()-function which could look similar to this
void Linear_Actuator::print_data() { if (publish) { uint8_t buf[2041]; mavlink_message_t msg; mavlink_msg_act1sts_pack(0,0,&msg,0,current, publish); uint16_t len = mavlink_msg_to_send_buffer(buf,&msg); usb_serial_write(buf,len); } }
where the data is printed only if publish is set.
This is basically what needs to be done in the Teensy source code. It is important to make sure that each received signal is decoded and processed.
Updating Flexbot Visualizer source code
Two files needs to updated here. First locate and open
/code/GUI/flexbot_visualizer/receiver.cpp
and go to the function
QList<int> Receiver::process_MAVLink_msg(mavlink_message_t _msg)
As with the Teensy source code, the GUI also needs to process all incoming MAVLink messages. An example to this could be
else if(ID == 3) { // DCCmd mavlink_dcwheelcmd_t msg; mavlink_msg_dcwheelcmd_decode(&_msg,&msg); flexbot->update(flexbot->MavIDtoID(ID),msg.speed); IDarray.push_back(flexbot->MavIDtoID(ID)); flexbot->update(flexbot->MavIDtoID(ID)+1,msg.publish); IDarray.push_back(flexbot->MavIDtoID(ID)+1); }
Again the message is decoded, and the signal values are used to update the local values held by the GUI. Follow the examples already existing in the source code.
Next locate and open
/code/GUI/flexbot_visualizer/sender.cpp
and go to the function
void Sender::process_mavlink_message(int ID)
Here each value for a given signal is retrieved from the GUI and packed as a MAVLink struct. An example of this is
else if(ID == 3) { // DCCmd double speed = flexbot->getValue("DCWheelCmd","speed"); int dc = flexbot->getValue("DCWheelCmd","dutycycle"); int dir = flexbot->getValue("DCWheelCmd","direction"); int publish = flexbot->getValue("DCWheelCmd","publish"); mavlink_msg_dcwheelcmd_pack(0,0,&msg,speed,dc,dir,publish); }
Follow the examples already existing in the source code.
Closing remarks
This is all that needs to be done to add new signals/messages to Flexbot.
Some manual labour goes into hardcoding these functions as they cannot exist dynamically. It is very important that this is completed every single time a message is added. Otherwise the MAVLink interface, GUI and general communication simply cannot work. So please make sure this is complied to at all times!
Questions about this can be referred to Jesper Christensen: jesper@haahrchristensen.dk