Este tutorial tem como objetivo montar um ambiente em que se possa testar o protocolo MQTT-SN. Para isso utilizaremos o RSMB (Really Small Message Broker) como base para testes de conexão, publicações e inscrições.
Materiais Necessários:
1x Arduino Mega
1x XBEE Arduino Shield
1x Arduino XBee (Configurado como Coordenator em MeshBee)
1x XBEE Explorer
1x Arduino XBee (Configurado como Router em MeshBee)
Obs: Seguir o tutorial Rede Mesh Xbee (https://bytespassivos.blogspot.com.br/2018/03/rede-mesh-xbee.html) para configurar os XBEE.
Passos:
1) Instalar a lib mqttsn
2) Fazer upload do código da Arduino
3) Compilar e instalar o RSMB
4) Criar arquivo de configuração do broker
5) Fazer o setup do ambiente
6) Testar a comunicação com o broker mqtt
1) Instalar a lib mqttsn
1. Fazer o download da lib mqttsn (https://github.com/boriz/MQTT-SN-Arduino)
2. Instalar na pasta de bibliotecas da Arduino (No meu caso Arduino/libraries)
$ cd ~/Downloads $ git clone https://github.com/boriz/MQTT-SN-Arduino $ cp MQTT-SN-Arduino/Arduino/libraries/mqttsn/ ~/Arduino/libraries/
2) Fazer upload do código da Arduino
1. Fazer o upload do código abaixo para a arduino:
#include <stdint.h> #include <stdbool.h> #include <string.h> #include <mqttsn-messages.h> //////////////////////////////////////// MESHBEE.H DEFINES//////////////////////////////////////// #define API_DATA_LEN 20 #define API_PAY_LEN (API_DATA_LEN + 5) #define API_FRAME_LEN (API_DATA_LEN + 9) #define API_DATA_PACKET 0x02 #define API_START_DELIMITER 0x7E #define OPTION_CAST_MASK 0x40 //option unicast or broadcast MASK #define OPTION_ACK_MASK 0x80 // option ACK or not MASK //////////////////////////////////////// MAIN DEFINES //////////////////////////////////////// #define XBEE Serial3 #define API_DATA_LEN 20 #define API_PAY_LEN (API_DATA_LEN + 5) #define API_FRAME_LEN (API_DATA_LEN + 9) #define TOPIC_PUB "arduino/temp" #define TOPIC_SUB "arduino/temp" //////////////////////////////////////// MESHBEE.H //////////////////////////////////////// uint8_t FrameID = 0; // Create MeshBee frame (return frame lenght) int MB_FrameCreate(uint8_t* data, int data_len, uint16_t dest_addr, uint8_t* frame, int frame_max_len, bool broadcast) { // frame buffer is big enough? if ( frame_max_len < API_FRAME_LEN ) { return -1; } // data is too long? // TODO: Split in multiple packets? if (data_len > API_DATA_LEN) { return -2; } // Frame buffer is fine. Clear it memset (frame, 0, frame_max_len); // Header frame[0] = API_START_DELIMITER; // Delimiter frame[1] = API_PAY_LEN; // Length of the payload frame[2] = API_DATA_PACKET; // API ID // Payload uint8_t cs = 0; // CS=Sum of the payload cs += frame[3] = FrameID++; // frame id if (broadcast) { cs += frame[4] = OPTION_CAST_MASK; // option } cs += frame[5] = data_len; // data length // Data for (int i=0; i<data_len; i++) { cs += frame[6 + i] = data[i]; } cs += frame[6 + API_DATA_LEN] = (dest_addr >> 8) && 0xFF; // Unicast address (16 bits) cs += frame[7 + API_DATA_LEN] = (dest_addr >> 0) && 0xFF; // Unicast address (16 bits) frame[8 + API_DATA_LEN] = cs; return API_FRAME_LEN; } // Parse MeshBee frame (return data length) int MB_FrameParse(uint8_t* frame, int frame_len, uint8_t* data, int data_max_len, uint16_t* src_addr) { // got a full frame? if ( frame_len != API_FRAME_LEN ) { // TODO: May be a valid frame, keep reading? return -1; } // Delimeter? if (frame[0] != API_START_DELIMITER) // Delimiter { return -2; } // Payload length? if (frame[1] != API_PAY_LEN) // Length of the payload { return -3; } // Right API ID? if (frame[2] != API_DATA_PACKET) // API ID { return -4; } // Payload int len; uint8_t cs = 0; // CS=Sum of the payload cs += frame[3]; // frame id cs += frame[4]; // option // Enough space in the payload buffer? cs += len = frame[5]; if (len > data_max_len) { return -5; } // Clear output buffer memset (data, 0, data_max_len); // Copy data to the buffer for (int i=0; i<API_DATA_LEN; i++) { if (i<len) { cs += data[i] = frame[6 + i]; } else { // Rest of the payload data (for checksum) cs += frame[6 + i]; } } *src_addr = (frame[6 + API_DATA_LEN] << 8) | frame[7 + API_DATA_LEN]; // Unicast address (16 bits) cs += frame[6 + API_DATA_LEN]; cs += frame[7 + API_DATA_LEN]; // Verify checksum if (cs != frame[8 + API_DATA_LEN]) { return -6; } return len; } //////////////////////////////////////// MAIN //////////////////////////////////////// MQTTSN mqttsn; uint16_t u16TopicPubID; uint16_t u16TopicSubID; uint8_t u8Counter; uint8_t FrameBufferIn[API_FRAME_LEN]; uint8_t FrameBufferOut[API_FRAME_LEN]; void setup() { Serial.begin(9600); XBEE.begin(9600); ADMUX = 0xC8; // turn on internal reference, right-shift ADC buffer, ADC channel = internal temp sensor delay(100); // wait a sec for the analog reference to stabilize u8Counter = 1; Serial.println("Setup done"); } void loop() { // Check serila data CheckXBEE(); // ------- MQTT-SN publis logic ------- // Busy? if (mqttsn.wait_for_response()) { // Busy. exit return; } // Connected? if (!mqttsn.connected()) { // Not connected - connect mqttsn.connect(0, 10, "test1758"); // Flags=0, Duration=10 Serial.println("Connect sent"); return; } // Topic registered? uint8_t index; u16TopicPubID = mqttsn.find_topic_id(TOPIC_PUB, &index); if (u16TopicPubID == 0xffff) { // Topic is not registered yet mqttsn.register_topic(TOPIC_PUB); Serial.println("Reg top sent"); return; } // Topic is alredy registered, publish char str[50]; char temp[10]; float t = averageTemperature(); itoa(t, temp, 10); sprintf(str, "%s (%i)", temp, u8Counter); Serial.print("Top id:"); Serial.print(u16TopicPubID); Serial.print("; Val:"); Serial.println(str); mqttsn.publish(0, u16TopicPubID, str, strlen(str)); // Flags=0 u8Counter++; delay(2000); // ------- MQTT-SN subscribe logic ------- u16TopicSubID = mqttsn.find_topic_id(TOPIC_SUB, &index); if (u16TopicSubID == 0xffff) { // Topic is not registered yet mqttsn.subscribe_by_name(0, TOPIC_SUB); // Flags=0 Serial.println("Sub top sent"); return; } delay(2000); } // gwinfo message callback void MQTTSN_gwinfo_handler(const msg_gwinfo* msg) { // Got a gateway response // The frame is still in the buffer - parse it Serial.println("GW info hand"); } void MQTTSN_publish_handler(const msg_publish* msg) { Serial.println("Sub pub hand"); } // Callback funciton to send XBEE data void MQTTSN_serial_send(uint8_t* message_buffer, int length) { // Assuming that our gateway is at address 0 (coordinator) int len = MB_FrameCreate (message_buffer, length, 0x0000, FrameBufferOut, sizeof(FrameBufferOut), false); if (len > 0) { XBEE.write(FrameBufferOut, len); XBEE.flush(); } } // XBEE event interrupt void CheckXBEE() { if (XBEE.available() > 0) { // Wait till we got the whole packet or timeout long Start = millis(); int cnt =0; while (cnt < API_FRAME_LEN && (millis() - Start) < 100) { while (XBEE.available() > 0) { FrameBufferIn[cnt++] = (uint8_t)XBEE.read(); // Reset timeout counter Start = millis(); } } if (cnt == API_FRAME_LEN) { // Got the whole packet Serial.print("From ser:"); for (int i=0; i<cnt; i++) { Serial.print (FrameBufferIn[i], HEX); Serial.print (" "); } Serial.println(); uint8_t pay[API_DATA_LEN]; uint16_t src_addr; int pay_len = MB_FrameParse(FrameBufferIn, cnt, pay, sizeof(pay), &src_addr); if (pay_len > 0) { Serial.print("Ser payload:"); for (int i=0; i<pay_len; i++) { Serial.print (pay[i], HEX); Serial.print (" "); } Serial.println(); // Valid frame. Pare it mqttsn.parse_stream(pay, pay_len); } } else { // Timeout. Serial.println("Chec ser timeout"); } } } // =================== Measure temperature =================== int readTemperature() { /*ADCSRA |= _BV(ADSC); // start the conversion while (bit_is_set(ADCSRA, ADSC)); // ADSC is cleared when the conversion finishes return (ADCL | (ADCH << 8)) - 342; // combine bytes & correct for temp offset (approximate)} */ return 42; } float averageTemperature() { /*readTemperature(); // discard first sample (never hurts to be safe) float averageTemp; // create a float to hold running average for (int i = 1; i < 1000; i++) // start at 1 so we dont divide by 0 averageTemp += ((readTemperature() - averageTemp)/(float)i); // get next sample, calculate running average return averageTemp; // return average temperature reading*/ return readTemperature(); }
3) Compilar e instalar o RSMB
1. Fazer o download do RSMB (https://github.com/eclipse/mosquitto.rsmb)
2. Compilar o broker_mqtts da pasta mosquitto.rsmb/rsmb/src
3. Instalar o broker copiando o arquivo compilado broker_mqtts para o diretório /usr/bin/
$ cd ~/Downloads $ git clone https://github.com/eclipse/mosquitto.rsmb $ cd mosquitto.rsmb/rsmb/src/ $ make broker_mqtts
4) Criar arquivo de configuração do broker
1. Crie um arquivo broker.cfgcom as confugurações abaixo:
$ nano ~/broker.cfg Obs: Copie e cole o conteúdo do broker.cfg abaixo. ctrl+o: Salva o arquivo ctrl+x: Sai do editor.broker.cfg:
# will show you packets being sent and received trace_output protocol #listener 1883 INADDR_ANY mqtt //Listen for MQTT Connections "Normal Broker" listener 1883 INADDR_ANY mqtts //Listen for MQTT-SN Connections "Sensor Network Broker" connection MY_MQTTSN_GATEWAY protocol mqtt address 198.41.30.241:1883 # 198.41.30.241 is the ip of iot.eclipse.org #address 127.0.0.1:1887 #local mosquitto broker topic # out
5) Fazer o setup do ambiente
1. Abra o primeiro terminal (ctrl+alt+t)
2. Encerre qualquer server que possa estar usando a porta 1883. No meu caso, quase sempre é o Mosquitto que esta ocupando essa porta.
$ sudo killall mosquitto
3. Execute o broker_mqtts com o arquivo de configuração criado
5. Conecte o XBEE Explorer (com o XBEE Coordenator montado) ao PC (anote a porta serial ao qual está conectada)
5. Abra um segundo terminal
6. Execute o Bridge Serial-UDP (~/Downloads/MQTT-SN-Arduino/serial-mqtts/ser_redir.py)
Obs: Antes de executar é necessário fazer as seguintes modificações no código:
SerPort = "/dev/ttyUSB0" (Deve ser a porta ao qual o XBEE Explorer está conectado)
Baud = 9600 (Baudrate configurado do XBEE Explorer)
BrokerHost = "127.0.0.1"
BrokerPort = 1883
Obs: Isso vai fazer com que a Arduino inicie o processo de conexão com o Gateway e sucessivamente com o broker online (iot.eclipse.org)
6) Testar a comunicação com o broker mqtt
1. Abra um terceiro terminal$ cd ~/Downloads/mosquitto.rsmb/rsmb/src/ $ ./broker_mqtts ~/broker.cfg4. Conecte a Arduino Mega (com o XBEE Router montado) ao PC (anote a porta serial ao qual está conectada)
5. Conecte o XBEE Explorer (com o XBEE Coordenator montado) ao PC (anote a porta serial ao qual está conectada)
5. Abra um segundo terminal
6. Execute o Bridge Serial-UDP (~/Downloads/MQTT-SN-Arduino/serial-mqtts/ser_redir.py)
Obs: Antes de executar é necessário fazer as seguintes modificações no código:
SerPort = "/dev/ttyUSB0" (Deve ser a porta ao qual o XBEE Explorer está conectado)
Baud = 9600 (Baudrate configurado do XBEE Explorer)
BrokerHost = "127.0.0.1"
BrokerPort = 1883
$ cd ~/Downloads/MQTT-SN-Arduino/serial-mqtts/ $ python ser_redir.py7. Aperte o botão de reset no shield XBEE.
Obs: Isso vai fazer com que a Arduino inicie o processo de conexão com o Gateway e sucessivamente com o broker online (iot.eclipse.org)
6) Testar a comunicação com o broker mqtt
2. Lance um cliente que se inscreva no tópico "arduino/temp"
$ mosquitto_sub -h iot.eclipse.org -p 1883 -t arduino/temp3. Se esse cliente estiver recebendo as publicações deste tópico então a conexão com o broker MQTT e o Gateway MQTT-SN estão funcionando corretamente.
Fontes:
[Para postar o código no post]
http://hilite.me/
https://www.engineersgarage.com/Tutorials/IoT-Communication-MQTT-SN-RSMB-Mosquitto-Broker
https://www.engineersgarage.com/Tutorials/RSMB-Broker-for-MQTT-SN
Nenhum comentário:
Postar um comentário