Так как пока других вариантов использования прибывшего контроллера LaunchPad MSP-EXP430G2 от Texas Instruments не придумалось — я решил, для развлечения, портировать для него rosserial — протокол точка-точка для общения с ROS.
Скопируем ros_lib из Arduino IDE в energia\hardware\msp430\libraries\
у меня под рукой оказалась только старая версия для ROS electric
первым делом поправим
ArduinoHardware.h
#ifndef ROS_ARDUINO_HARDWARE_H_ #define ROS_ARDUINO_HARDWARE_H_ #include <Arduino.h> // Energia #include <HardwareSerial.h> class ArduinoHardware { public: ArduinoHardware(HardwareSerial* io , long baud= 9600){ iostream = io; baud_ = baud; } ArduinoHardware() { iostream = &Serial; baud_ = 9600; }
Да-да, Arduino-вские 57600 придётся заменить на LaunchPad-кие 9600 :/
Серъёзные данные не прокачаешь, но и МК у нас так себе — так что для простеньких сообщений пойдёт 😉
попробуем собрать идущий в комплекте пример HelloWorld
/* * rosserial Publisher Example * Prints "hello world!" */ #include <ros.h> #include <std_msgs/String.h> ros::NodeHandle nh; std_msgs::String str_msg; ros::Publisher chatter("chatter", &str_msg); char hello[13] = "hello world!"; void setup() { nh.initNode(); nh.advertise(chatter); } void loop() { str_msg.data = hello; chatter.publish( &str_msg ); nh.spinOnce(); delay(1000); }
запускаем компиляцию и смотрим на появляющиеся ошибки 🙂
не находится стандартная функция realloc:
energia\hardware\msp430\libraries\ros_lib/ros/../rosserial_msgs/RequestParam.h:152:87: error: 'realloc' was not declared in this scope
а потом не находится round:
time.h:57:106: error: 'round' was not declared in this scope
то что у msp430 нету realloc() — вполне логично — памяти то кот наплакал.
Даже в дуиновком классе String вместо realloc() используется malloc() (в методе String::changeBuffer()).
Реализуем недостающие методы самостоятельно %)
подключим в ros/msg.h и ros/time.h
#include "ros2energia.h"
содержание ros2energia.h:
/* * Copyright (c) 2012, RoboCraft * All rights reserved. */ #ifndef ROS_2_ENERGIA_H_ #define ROS_2_ENERGIA_H_ #include <Energia.h> // Energia #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) void *realloc( void *ptr, size_t size ); #endif
содержание ros2energia.сpp:
/* * Copyright (c) 2012, RoboCraft * All rights reserved. */ #include "ros2energia.h" void *realloc( void *ptr, size_t size ) { if(ptr) { if(size){ free(ptr); ptr = malloc(size); } else{ //size==0 free(ptr); ptr = NULL; } } else { //ptr==0 if(size) return malloc(size); } return ptr; }
Теперь сборка проходит без ошибок, но, к сожалению, прошивка не влезает в память 🙁
energia/hardware/tools/msp430/bin/../lib/gcc/msp430/4.6.3/../../../../msp430/bin/ld.exe: HelloWorld.cpp.elf section `.bss' will not fit in region `ram' energia/hardware/tools/msp430/bin/../lib/gcc/msp430/4.6.3/../../../../msp430/bin/ld.exe: region `ram' overflowed by 844 bytes collect2: ld returned 1 exit status
попробуем поправить ros.h, добавив:
#elif defined(__MSP430G2553) typedef NodeHandle_NodeHandle;
— тем самым мы уменьшаем число подписчиков, писателей и размеры буферов приёма-передачи сообщений
/* Node Handle */ templateclass NodeHandle_ { protected: Hardware hardware_; NodeOutput no_;
та-да!
Собралось!!!
Binary sketch size: 5 180 bytes (of a 16 384 byte maximum)
попробуем прошить и посмотрим, что будет писаться в порт 🙂
ничего 🙁
разбираемся дальше 🙂
добираемся до ros/node_output.h
видим буфер для выдачи сообщений и функцию отправки, которая возвращает число переданных байт.
для отладки добавим в скетч светодиодной индикации
/* * rosserial Publisher Example with blink * Prints "hello world!" */ #include <ros.h> #include <std_msgs/String.h> ros::NodeHandle nh; std_msgs::String str_msg; ros::Publisher chatter("chatter", &str_msg); char hello[13] = "hello world!"; int res=0; void setup() { pinMode(RED_LED, OUTPUT); pinMode(GREEN_LED, OUTPUT); nh.initNode(); nh.advertise(chatter); } void loop() { res=0; digitalWrite(GREEN_LED, HIGH); str_msg.data = hello; res = chatter.publish( &str_msg ); nh.spinOnce(); if(res>0) digitalWrite(RED_LED, HIGH); delay(500); digitalWrite(GREEN_LED, LOW); digitalWrite(RED_LED, LOW); delay(500); }
т.о. зелёный светодиод будет мигать, а красный должен мигать только при успешной передаче данных в порт.
гм… красный не загорается — значит с записью действительно проблемы :/
ага — в начале метода NodeOutput::publish есть проверка булевого флага configured_, который выставляется методом setConfigured()
class NodeOutput : public NodeOutput_{ // ... void setConfigured(bool b){ configured_ =b; } bool configured(){return configured_;}; virtual int publish(int id, Msg * msg){ if(!configured_) return 0; /* serialize message */ int l = msg->serialize(message_out+6); /* setup the header */ message_out[0] = 0xff; message_out[1] = 0xff; message_out[2] = (unsigned char) id&255; message_out[3] = (unsigned char) id>>8; message_out[4] = (unsigned char) l&255; message_out[5] = ((unsigned char) l>>8); /* calculate checksum */ int chk = 0; for(int i =2; iwrite(message_out, l); return l; }
в class NodeHandle_ , этот метод вызывается дважды:
virtual void spinOnce(){ /* restart if timed out */ unsigned long c_time = hardware_.time(); if( (c_time - last_sync_receive_time) > (SYNC_SECONDS*2200) ){ no_.setConfigured(false); } ... void negotiateTopics() { no_.setConfigured(true);
т.о. сообщения будут печататься только при общении с ROS-овским приложением.
Это можно проверить, заменив в обработке тайм-аута
no_.setConfigured(false);
на
no_.setConfigured(true);
пересобираем, прошиваем, открываем терминал COM-порта и чуть-чуть подождём 🙂
через несколько секунд красный светодиод замигает, а в терминале увидим иероглифы, обрамляющие наше сообщение hello world!
Вернём всё как было и посмотим питоновский код клиента —
rosserial\rosserial_python\src\rosserial_python\SerialClient.py
def requestTopics(self): """ Determine topics to subscribe/publish. """ self.port.flushInput() # request topic sync self.port.write("\xff\xff\x00\x00\x00\x00\xff")
быстренько набросаем программу, пишущую в порт этот запрос и считывающую ответ:
в формате Gtest:
TEST(Serial, WriteCOM) { Serial serial; serial.open("COM7", 9600); if(!serial.connected()){ FAIL()<<"cant open serial port!"; } char buf[1024]; int res = 0; while(1){ char wbuf[] = {0xff, 0xff, 0x00, 0x00, 0x00, 0x00 ,0xff}; serial.write(wbuf, sizeof(wbuf)); if( serial.waitInput(1000) > 0){ if( (res = serial.available()) > 0 ){ if( res = serial.read(buf, res) ){ printf("[i] read data(%d): \n", res); for(int i=0; i0 && (i+1)%16 == 0) { printf("\t"); for(int j=i-15; j<=i; j++){ printf("%c", buf[j]); } printf("\n"); } } printf("\n"); res = 0; } } } Sleep(1000); } }
хм... получаются сообщения вида:
[i] read data(85): FF FF 66 00 10 00 00 00 00 00 68 65 6C 6C 6F 20 f hello 77 6F 72 6C 64 21 0C FF FF 0A 00 08 00 00 00 00 world! 00 00 00 00 00 ED FF FF 00 00 28 00 66 07 00 00 ? ( f 00 00 63 68 61 74 74 65 72 0F 00 00 00 73 74 64 chatter std 5F 6D 73 67 73 2F 53 74 72 69 6E 04 00 00 00 00 _msgs/Strin 20 20 20 20 49
что-то название типа String полностью не пролазит 🙁
разберём пакеты:
пакет hello world!:
FF FF 66 00 10 00 00 00 00 00 68 65 6C 6C 6F 20 77 6F 72 6C 64 21 0C
FF FF - два ff-а заголовка
00 66 - 102 - id Publisher-а chatter
присваивается в NodeHandle_::advertise()
p.id_ = i+100+MAX_SUBSCRIBERS; // (0+100+MAX_SUBSCRIBERS), где MAX_SUBSCRIBERS=2
00 10 = 16 - длина сериализованного сообщения
00 00 00 00 68 65 6C 6C 6F 20 77 6F 72 6C 64 21 - сериализованное сообщения типа std_msgs::String
String::serialize: virtual int serialize(unsigned char *outbuffer) { int offset = 0; long * length_data = (long *)(outbuffer + offset); *length_data = strlen( (const char*) this->data); offset += 4; memcpy(outbuffer + offset, this->data, *length_data); offset += *length_data; return offset; }
strlen() не отработал? О_о
0C - контрольная сумма, высчитывается в NodeOutput::publish()
/* calculate checksum */ int chk = 0; for(int i =2; iпакет с меткой времени:
FF FF 0A 00 08 00 00 00 00 00 00 00 00 00 ED00 0A = 10 - rosserial_msgs::TopicInfo::ID_TIME
enum { ID_PUBLISHER = 0 }; enum { ID_SUBSCRIBER = 1 }; enum { ID_SERVICE_SERVER = 2 }; enum { ID_SERVICE_CLIENT = 3 }; enum { ID_PARAMETER_REQUEST = 4 }; enum { ID_LOG = 5 }; enum { ID_TIME = 10 };00 08 - 8 - длина
00 00 00 00 00 00 00 00 - вместо данных пустой пакет :/
ED - контрольная суммапакет с типом отправления:
FF FF 00 00 28 00 66 07 00 00 00 00 63 68 61 74 74 65 72 0F 00 00 00 73 74 64 5F 6D 73 67 73 2F 53 74 72 69 6E 04 00 00 00 00 20 20 20 20 49похоже, результат метода NodeHandle_::negotiateTopics()
void negotiateTopics() { no_.setConfigured(true); rosserial_msgs::TopicInfo ti; int i; for(i = 0; i < MAX_PUBLISHERS; i++) { if(publishers[i] != 0) // non-empty slot { ti.topic_id = publishers[i]->id_; ti.topic_name = (char *) publishers[i]->topic_; ti.message_type = (char *) publishers[i]->msg_->getType(); no_.publish( TOPIC_PUBLISHERS, &ti ); } } for(i = 0; i < MAX_SUBSCRIBERS; i++) { if(receivers[i] != 0) // non-empty slot { ti.topic_id = receivers[i]->id_; ti.topic_name = (char *) receivers[i]->topic_; ti.message_type = (char *) receivers[i]->getMsgType(); no_.publish( TOPIC_SUBSCRIBERS, &ti ); } } }00 00 = 0 - TOPIC_PUBLISHERS
из ros\rosserial_ids.h
#define TOPIC_NEGOTIATION 0 #define TOPIC_PUBLISHERS 0 #define TOPIC_SUBSCRIBERS 1 #define TOPIC_SERVICES 200 28 = 40 - длина сообщения
66 07 00 00 00 00 63 68 61 74 74 65 72 0F 00 00 00 73 74 64 5F 6D 73 67 73 2F 53 74 72 69 6E 04 00 00 00 00 20 20 20 20 - сообщение
rosserial_msgs::TopicInfo
enum { ID_PUBLISHER = 0 }; enum { ID_SUBSCRIBER = 1 }; enum { ID_SERVICE_SERVER = 2 }; enum { ID_SERVICE_CLIENT = 3 }; enum { ID_PARAMETER_REQUEST = 4 }; enum { ID_LOG = 5 }; enum { ID_TIME = 10 }; virtual int serialize(unsigned char *outbuffer) { int offset = 0; union { unsigned int real; unsigned int base; } u_topic_id; u_topic_id.real = this->topic_id; *(outbuffer + offset + 0) = (u_topic_id.base >> (8 * 0)) & 0xFF; *(outbuffer + offset + 1) = (u_topic_id.base >> (8 * 1)) & 0xFF; offset += sizeof(this->topic_id); long * length_topic_name = (long *)(outbuffer + offset); *length_topic_name = strlen( (const char*) this->topic_name); offset += 4; memcpy(outbuffer + offset, this->topic_name, *length_topic_name); offset += *length_topic_name; long * length_message_type = (long *)(outbuffer + offset); *length_message_type = strlen( (const char*) this->message_type); offset += 4; memcpy(outbuffer + offset, this->message_type, *length_message_type); offset += *length_message_type; long * length_md5_checksum = (long *)(outbuffer + offset); *length_md5_checksum = strlen( (const char*) this->md5_checksum); offset += 4; memcpy(outbuffer + offset, this->md5_checksum, *length_md5_checksum); offset += *length_md5_checksum; return offset; }49 - контрольная сумма
Сравним выдачу от msp430g2553 с данными на аналогичный запрос от Arduino Mega.
msp430g2553[i] read data(85): FF FF 66 00 10 00 00 00 00 00 68 65 6C 6C 6F 20 f hello 77 6F 72 6C 64 21 0C FF FF 0A 00 08 00 00 00 00 world! 00 00 00 00 00 ED FF FF 00 00 28 00 66 07 00 00 ? ( f 00 00 63 68 61 74 74 65 72 0F 00 00 00 73 74 64 chatter std 5F 6D 73 67 73 2F 53 74 72 69 6E 04 00 00 00 00 _msgs/Strin 20 20 20 20 49для сравнения - пример HelloWorld на Arduino Mega:
typedef NodeHandle_
NodeHandle; [i] read data(81): FF FF 66 00 10 00 0C 00 00 00 68 65 6C 6C 6F 20 ¦ f > + hello 77 6F 72 6C 64 21 00 FF FF 0A 00 08 00 00 00 00 world! 00 00 00 00 00 ED FF FF 00 00 24 00 66 00 07 00 э $ f 00 00 63 68 61 74 74 65 72 0F 00 00 00 73 74 64 chatter0 std 5F 6D 73 67 73 2F 53 74 72 69 6E 67 00 00 00 00 d_msgs/String 6Atypedef NodeHandle_
NodeHandle; [i] read data(81): FF FF 7D 00 10 00 0C 00 00 00 68 65 6C 6C 6F 20 ¦ } > + hello 77 6F 72 6C 64 21 E9 FF FF 0A 00 08 00 00 00 00 world!щ 00 00 00 00 00 ED FF FF 00 00 24 00 7D 00 07 00 э $ } 00 00 63 68 61 74 74 65 72 0F 00 00 00 73 74 64 chatter0 std 5F 6D 73 67 73 2F 53 74 72 69 6E 67 00 00 00 00 d_msgs/String 53похоже, существует сдвиг записи значения длины строки на 1 байт, а так же определение длины строки поля md5_checksum
добавим в NodeHandle_::negotiateTopics()
строчкуti.md5_checksum = "";(присваивание 0 - оказалось плохой идеей 😉
в результате, получили хотя бы совпадение длины:
[i] read data(81): FF FF 66 00 10 00 00 00 00 00 68 65 6C 6C 6F 20 f hello 77 6F 72 6C 64 21 0C FF FF 0A 00 08 00 00 00 00 world! 00 00 00 00 00 ED FF FF 00 00 24 00 66 07 00 00 ? $ f 00 00 63 68 61 74 74 65 72 0F 00 00 00 73 74 64 chatter std 5F 6D 73 67 73 2F 53 74 72 69 6E 00 00 00 00 00 _msgs/Strin D1при добавлении в TopicInfo::serialize() смещения на 1:
long * length_topic_name = (long *)(outbuffer + offset+1); *length_topic_name = strlen( (const char*) this->topic_name); offset += 4; memcpy(outbuffer + offset, this->topic_name, *length_topic_name); offset += *length_topic_name; long * length_message_type = (long *)(outbuffer + offset+1); *length_message_type = strlen( (const char*) this->message_type); offset += 4; memcpy(outbuffer + offset, this->message_type, *length_message_type); offset += *length_message_type; long * length_md5_checksum = (long *)(outbuffer + offset+1);[i] read data(81): FF FF 66 00 10 00 00 00 00 07 68 65 6C 6C 6F 20 f hello 77 6F 72 6C 64 21 05 FF FF 0A 00 08 00 00 00 00 world! 00 00 00 00 00 ED FF FF 00 00 24 00 66 00 00 07 ? $ f 00 00 63 68 61 74 74 65 72 0F 00 00 00 73 74 64 chatter std 5F 6D 73 67 73 2F 53 74 72 69 6E 67 00 00 00 00 _msgs/String 6AУ кого ещё есть какие идеи? 🙂
Уменьшить буферы?
Уменьшение буферов даже до 64 ничего не принесло:typedef NodeHandle_NodeHandle; Справка по формату протокола rosserial
формат пакета протокола rosserial:
1st Byte - Sync Flag (Value: 0xff) 2nd Byte - Sync Flag (Value: 0xff) 3rd Byte - Topic ID - Low Byte 4th Byte - Topic ID - High Byte 5th Byte - Message Length (N) - Low Byte 6th Byte - Message Length (N) - High Byte N Bytes - Serialized Message Data Byte N+7 - ChecksumЗапрос для получения информации о топиках:
0xff 0xff 0x00 0x00 0x00 0x00 0xffПоследовательность ответных пакетов (тип сообщения - rosserial_msgs/TopicInfo), каждый из которых содержит информацию о соответствующем топике - набор сериализованных данных:
uint16 topic_id string topic_name string message_type string md5sum int32 buffer_sizeПроверка размеров переменных в msp430g2553
char hello[13] = "hello world!"; void setup() { // P1_0 - red LED // P1_6 - green LED pinMode(P1_0, OUTPUT); pinMode(P1_6, OUTPUT); Serial.begin(9600); Serial.print("char\t"); Serial.println(sizeof(char)); Serial.print("int\t"); Serial.println(sizeof(int)); Serial.print("long\t"); Serial.println(sizeof(long)); Serial.print("size_t\t"); Serial.println(sizeof(size_t)); Serial.print("float\t"); Serial.println(sizeof(float)); Serial.print("strlen\t"); Serial.println(strlen(hello)); }в терминале получаем:
char 1 int 2 long 4 size_t 2 float 4 strlen 12Ссылки
http://ros.org/wiki/rosserial/Overview/ProtocolПо теме
LaunchPad MSP-EXP430G2 от Texas Instruments - альтернатива Arduino?
Rosserial - связь двух Arduino через ROS
0 комментариев на «“Попытки запуска rosserial на LaunchPad MSP-EXP430G2”»
> Да-да, Arduino-вские 57600 придётся заменить на LaunchPad-кие 9600 :/
Если переключить «UTCTL0 |= SSEL1;», то можно и 230400 бод ставить…
> Серъёзные данные не прокачаешь, но и МК у нас так себе
Ну, в сравнении с Ардуиной разницу в производительности едва ли можно заметить. А вот разница в энергопотреблении впечатляет! (10:1 в пользу MSP430)