segunda-feira, 29 de agosto de 2022

WISOL LOM204 COMO INTERFACE PARA COMUNICAÇÃO MASTER/SLAVE (P2P) - MQTT, MODBUS e ENTRADAS ANALÓGICAS COM LM358

   

O objetivo geral deste BLOG é demonstrar como é possível programar o módulo WISOL LOM204A02 via VISUINO e assim utilizá-lo como OPENCPU.

O objetivo específico neste projeto é programar o LOM204A02 com VISUINO, para permitir que seu NINA W106  (Master) possa efetuar um comunicação bidirecional (Topologia P2P) com vários NINA W106 (Slaves).  Foi  utilizado o recurso do VISUINO: Custom Code, o qual permite a inclusão de código Arduino,  baseado nos exemplos Arduino da Library LMIC.

Neste exemplo foi acrescentada a opção para publicar os dados coletados dos Slaves, via MQTT e  assim mostrar a eficiência do Protocolo LoRa.

p2p

Uma comunicação peer-to-peer utilizando a tecnologia LoRa permite uma comunicação direta e de longa distância entre 2/n dispositivos (Polling). Isso é particularmente útil em ambientes restritos onde a infraestrutura global LoraWAN não é necessária ou simplesmente não está disponível.

Protocolo de Comunicação
Comunicação Bidirecional

Basicamente o Master enviará um pacote o LOM204A02 e este fará um broadcasting para todos os Slaves.

Este pacote será um JSON que conterá um Id do Slave com o qual ele deseja se comunicar:

{"Id":0,"ALARM":0}, onde X é Id do Slave, que no caso será 0,1,2,3...em Alarm você já pode enviar também dado para o Slave do Id selecionado processar.

O Slave que corresponde à este Id, poderá enviar uma resposta através de um pacote JSON também:

{"ID":0,"A0":26,"A1":0,"A2":0,"A3":1023,"D0":0,"D1":0,"D2":0,"D3":0,"C":54761,"T":26,"U":59,"P":102028

Também via Broadcasting, o pacote enviado pelo Slave chegará ao Master, mas também para os outros Slaves o qual deverá ser descartado observando seu Id.


Para o NINA W106 (Master ou Slave) fazer um broadcasting, dever enviar (Txd) via Serial o pacote até o RX5 do LOM204.

Para o NINA W106 (Master ou Slave) receber (Rxd) um broadcasting, deve ser conectando a Serial no TX1 do LOM204.

NINA W106 MASTER e SLAVE
LOM204 atuando como Broadcasting
VISUINO
O Visuino é o mais recente software inovador da Mitov Software. Um ambiente de programação visual que permite programar suas placas Arduino.
Os componentes encontrados no software Visuino representam seus componentes de hardware e você poderá criar e projetar facilmente seus programas usando arrastar e soltar. Nenhum equipamento ou hardware é necessário para executar o software no modo de design. Depois de ter concluído o design, você pode conectar o upload da placa Arduino e executá-lo.

Para aquelas pessoas que não são fortes em escrever código, em seguida, projetar, compilar e criar programas Arduino nunca foi tão fácil! Por que perder tempo criando código quando já se faz todo o trabalho duro para você? Você tem sua placa Arduino e um ótimo design de hardware, veja-a rodando em minutos, não em horas!

ARDUINO

O que é Arduino? Se você sabe pouco sobre Arduino, por favor dê uma olhada abaixo:

Você conhece Arduino. Instale o IDE primeiro:
https://www.arduino.cc/en/Main/Software

LOM204 e Arduino

LOM204 é baseado em STM32L071.  No Arduino STM32 existe este core.

Como instalar Arduino STM32?
adicionar em Arquivo Preferências URLs adicionais

http://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json

BSP (utilizar 2.2.0)

Em seguida, instale o BSP para STM32. Se a instalação estiver concluída como segue:

LIB LoraWAN para Arduino - LMIC (Catena)


Adicione ao Arduino IDE:

Ok,  a LIB está instalada para ser acessada pelo Visuino. 

Defina a Região
lmic_project_config.h

// project-specific definitions //#define CFG_eu868 1 //#define CFG_us915 1 #define CFG_au915 1 //#define CFG_as923 1 // #define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP /* for as923-JP */ //#define CFG_kr920 1 //#define CFG_in866 1 #define CFG_sx1276_radio 1 //#define LMIC_USE_INTERRUPTS







No PACKAGE do STM32, alguns arquivos tiveram que ser alterados para ter acesso aos GPIOS do LOM204.

Em
C:\Users\Usuario\AppData\Local\Arduino15\packages\STMicroelectronics\hardware\stm32\2.2.0\variants\STM32L0xx

Descompacte

Em 

C:\Users\Usuario\AppData\Local\Arduino15\packages

Tem-se então em variants, uma placa LOM204



O projeto do Visuino (Componente Custom), foi baseado no exemplo P2p Raw.


/******************************************************************************* * Copyright (c) 2015 Matthijs Kooijman * Copyright (c) 2018 Terry Moore, MCCI Corporation * * Permission is hereby granted, free of charge, to anyone * obtaining a copy of this document and accompanying files, * to do whatever they want with them without any restriction, * including, but not limited to, copying, modification and redistribution. * NO WARRANTY OF ANY KIND IS PROVIDED. * * This example transmits data on hardcoded channel and receives data * when not transmitting. Running this sketch on two nodes should allow * them to communicate. *******************************************************************************/ #include <lmic.h> #include <hal/hal.h> #include <SPI.h> // we formerly would check this configuration; but now there is a flag, // in the LMIC, LMIC.noRXIQinversion; // if we set that during init, we get the same effect. If // DISABLE_INVERT_IQ_ON_RX is defined, it means that LMIC.noRXIQinversion is // treated as always set. // // #if !defined(DISABLE_INVERT_IQ_ON_RX) // #error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \ // lmic_project_config.h in arduino-lmic/project_config to set it. // #endif // How often to send a packet. Note that this sketch bypasses the normal // LMIC duty cycle limiting, so when you change anything in this sketch // (payload length, frequency, spreading factor), be sure to check if // this interval should not also be increased. // See this spreadsheet for an easy airtime and duty cycle calculator: // https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc #define TX_INTERVAL 2000 // Pin mapping const lmic_pinmap lmic_pins = { .nss = 6, .rxtx = LMIC_UNUSED_PIN, .rst = 5, .dio = {2, 3, 4}, }; // These callbacks are only used in over-the-air activation, so they are // left empty here (we cannot leave them out completely unless // DISABLE_JOIN is set in arduino-lmoc/project_config/lmic_project_config.h, // otherwise the linker will complain). void os_getArtEui (u1_t* buf) { } void os_getDevEui (u1_t* buf) { } void os_getDevKey (u1_t* buf) { } void onEvent (ev_t ev) { } osjob_t txjob; osjob_t timeoutjob; static void tx_func (osjob_t* job); // Transmit the given string and call the given function afterwards void tx(const char *str, osjobcb_t func) { os_radio(RADIO_RST); // Stop RX first delay(1); // Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet LMIC.dataLen = 0; while (*str) LMIC.frame[LMIC.dataLen++] = *str++; LMIC.osjob.func = func; os_radio(RADIO_TX); Serial.println("TX"); } // Enable rx mode and call func when a packet is received void rx(osjobcb_t func) { LMIC.osjob.func = func; LMIC.rxtime = os_getTime(); // RX _now_ // Enable "continuous" RX (e.g. without a timeout, still stops after // receiving a packet) os_radio(RADIO_RXON); Serial.println("RX"); } static void rxtimeout_func(osjob_t *job) { digitalWrite(LED_BUILTIN, LOW); // off } static void rx_func (osjob_t* job) { // Blink once to confirm reception and then keep the led on digitalWrite(LED_BUILTIN, LOW); // off delay(10); digitalWrite(LED_BUILTIN, HIGH); // on // Timeout RX (i.e. update led status) after 3 periods without RX os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func); // Reschedule TX so that it should not collide with the other side's // next TX os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL/2), tx_func); Serial.print("Got "); Serial.print(LMIC.dataLen); Serial.println(" bytes"); Serial.write(LMIC.frame, LMIC.dataLen); Serial.println(); // Restart RX rx(rx_func); } static void txdone_func (osjob_t* job) { rx(rx_func); } // log text to USART and toggle LED static void tx_func (osjob_t* job) { // say hello tx("Hello, world!", txdone_func); // reschedule job every TX_INTERVAL (plus a bit of random to prevent // systematic collisions), unless packets are received, then rx_func // will reschedule at half this time. os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func); } // application entry point void setup() { Serial.begin(115200); Serial.println("Starting"); #ifdef VCC_ENABLE // For Pinoccio Scout boards pinMode(VCC_ENABLE, OUTPUT); digitalWrite(VCC_ENABLE, HIGH); delay(1000); #endif pinMode(LED_BUILTIN, OUTPUT); // initialize runtime env os_init(); #if defined(CFG_eu868) // Use a frequency in the g3 which allows 10% duty cycling. LMIC.freq = 869525000; // Use a medium spread factor. This can be increased up to SF12 for // better range, but then, the interval should be (significantly) // raised to comply with duty cycle limits as well. LMIC.datarate = DR_SF9; // Maximum TX power LMIC.txpow = 27; #elif defined(CFG_us915) // make it easier for test, by pull the parameters up to the top of the // block. Ideally, we'd use the serial port to drive this; or have // a voting protocol where one side is elected the controller and // guides the responder through all the channels, powers, ramps // the transmit power from min to max, and measures the RSSI and SNR. // Even more amazing would be a scheme where the controller could // handle multiple nodes; in that case we'd have a way to do // production test and qualification. However, using an RWC5020A // is a much better use of development time. // set fDownlink true to use a downlink channel; false // to use an uplink channel. Generally speaking, uplink // is more interesting, because you can prove that gateways // *should* be able to hear you. const static bool fDownlink = false; // the downlink channel to be used. const static uint8_t kDownlinkChannel = 3; // the uplink channel to be used. const static uint8_t kUplinkChannel = 8 + 3; // this is automatically set to the proper bandwidth in kHz, // based on the selected channel. uint32_t uBandwidth; if (! fDownlink) { if (kUplinkChannel < 64) { LMIC.freq = US915_125kHz_UPFBASE + kUplinkChannel * US915_125kHz_UPFSTEP; uBandwidth = 125; } else { LMIC.freq = US915_500kHz_UPFBASE + (kUplinkChannel - 64) * US915_500kHz_UPFSTEP; uBandwidth = 500; } } else { // downlink channel LMIC.freq = US915_500kHz_DNFBASE + kDownlinkChannel * US915_500kHz_DNFSTEP; uBandwidth = 500; } // Use a suitable spreading factor if (uBandwidth < 500) LMIC.datarate = US915_DR_SF7; // DR4 else LMIC.datarate = US915_DR_SF12CR; // DR8 // default tx power for US: 21 dBm LMIC.txpow = 21; #elif defined(CFG_au915) // make it easier for test, by pull the parameters up to the top of the // block. Ideally, we'd use the serial port to drive this; or have // a voting protocol where one side is elected the controller and // guides the responder through all the channels, powers, ramps // the transmit power from min to max, and measures the RSSI and SNR. // Even more amazing would be a scheme where the controller could // handle multiple nodes; in that case we'd have a way to do // production test and qualification. However, using an RWC5020A // is a much better use of development time. // set fDownlink true to use a downlink channel; false // to use an uplink channel. Generally speaking, uplink // is more interesting, because you can prove that gateways // *should* be able to hear you. const static bool fDownlink = false; // the downlink channel to be used. const static uint8_t kDownlinkChannel = 3; // the uplink channel to be used. const static uint8_t kUplinkChannel = 8 + 3; // this is automatically set to the proper bandwidth in kHz, // based on the selected channel. uint32_t uBandwidth; if (! fDownlink) { if (kUplinkChannel < 64) { LMIC.freq = AU915_125kHz_UPFBASE + kUplinkChannel * AU915_125kHz_UPFSTEP; uBandwidth = 125; } else { LMIC.freq = AU915_500kHz_UPFBASE + (kUplinkChannel - 64) * AU915_500kHz_UPFSTEP; uBandwidth = 500; } } else { // downlink channel LMIC.freq = AU915_500kHz_DNFBASE + kDownlinkChannel * AU915_500kHz_DNFSTEP; uBandwidth = 500; } // Use a suitable spreading factor if (uBandwidth < 500) LMIC.datarate = AU915_DR_SF7; // DR4 else LMIC.datarate = AU915_DR_SF12CR; // DR8 // default tx power for AU: 30 dBm LMIC.txpow = 30; #elif defined(CFG_as923) // make it easier for test, by pull the parameters up to the top of the // block. Ideally, we'd use the serial port to drive this; or have // a voting protocol where one side is elected the controller and // guides the responder through all the channels, powers, ramps // the transmit power from min to max, and measures the RSSI and SNR. // Even more amazing would be a scheme where the controller could // handle multiple nodes; in that case we'd have a way to do // production test and qualification. However, using an RWC5020A // is a much better use of development time. const static uint8_t kChannel = 0; uint32_t uBandwidth; LMIC.freq = AS923_F1 + kChannel * 200000; uBandwidth = 125; // Use a suitable spreading factor if (uBandwidth == 125) LMIC.datarate = AS923_DR_SF7; // DR7 else LMIC.datarate = AS923_DR_SF7B; // DR8 // default tx power for AS: 21 dBm LMIC.txpow = 16; if (LMIC_COUNTRY_CODE == LMIC_COUNTRY_CODE_JP) { LMIC.lbt_ticks = us2osticks(AS923JP_LBT_US); LMIC.lbt_dbmax = AS923JP_LBT_DB_MAX; } #elif defined(CFG_kr920) // make it easier for test, by pull the parameters up to the top of the // block. Ideally, we'd use the serial port to drive this; or have // a voting protocol where one side is elected the controller and // guides the responder through all the channels, powers, ramps // the transmit power from min to max, and measures the RSSI and SNR. // Even more amazing would be a scheme where the controller could // handle multiple nodes; in that case we'd have a way to do // production test and qualification. However, using an RWC5020A // is a much better use of development time. const static uint8_t kChannel = 0; uint32_t uBandwidth; LMIC.freq = KR920_F1 + kChannel * 200000; uBandwidth = 125; LMIC.datarate = KR920_DR_SF7; // DR7 // default tx power for KR: 14 dBm LMIC.txpow = KR920_TX_EIRP_MAX_DBM; if (LMIC.freq < KR920_F14DBM) LMIC.txpow = KR920_TX_EIRP_MAX_DBM_LOW; LMIC.lbt_ticks = us2osticks(KR920_LBT_US); LMIC.lbt_dbmax = KR920_LBT_DB_MAX; #elif defined(CFG_in866) // make it easier for test, by pull the parameters up to the top of the // block. Ideally, we'd use the serial port to drive this; or have // a voting protocol where one side is elected the controller and // guides the responder through all the channels, powers, ramps // the transmit power from min to max, and measures the RSSI and SNR. // Even more amazing would be a scheme where the controller could // handle multiple nodes; in that case we'd have a way to do // production test and qualification. However, using an RWC5020A // is a much better use of development time. const static uint8_t kChannel = 0; uint32_t uBandwidth; LMIC.freq = IN866_F1 + kChannel * 200000; uBandwidth = 125; LMIC.datarate = IN866_DR_SF7; // DR7 // default tx power for IN: 30 dBm LMIC.txpow = IN866_TX_EIRP_MAX_DBM; #else # error Unsupported LMIC regional configuration. #endif // disable RX IQ inversion LMIC.noRXIQinversion = true; // This sets CR 4/5, BW125 (except for EU/AS923 DR_SF7B, which uses BW250) LMIC.rps = updr2rps(LMIC.datarate); Serial.print("Frequency: "); Serial.print(LMIC.freq / 1000000); Serial.print("."); Serial.print((LMIC.freq / 100000) % 10); Serial.print("MHz"); Serial.print(" LMIC.datarate: "); Serial.print(LMIC.datarate); Serial.print(" LMIC.txpow: "); Serial.println(LMIC.txpow); // This sets CR 4/5, BW125 (except for DR_SF7B, which uses BW250) LMIC.rps = updr2rps(LMIC.datarate); // disable RX IQ inversion LMIC.noRXIQinversion = true; Serial.println("Started"); Serial.flush(); // setup initial job os_setCallback(&txjob, tx_func); } void loop() { // execute scheduled jobs and events os_runloop_once(); }

Gravador para utilizar com Visuino

Instale o STM32 Cube Programmer, o Arduino (STM32 PACKAGE) irá reconhecê-lo e então utilizá-lo para programar o LOM204. O Kit LOM204 possui um gravador ST-LINK embarcado.

Caso não tenhas um Kit LOM204 ou seja, apenas um módulo, utilize o um ST-LINK V2

O autor utilizou o ST-LINK.


Baixe o VISUINO PRO e instale


Foi Criado, não oficialmente , um VCOMP (Virtual Component) para o LOM204,  o qual pode ser baixado aqui.


Copie para a PASTA

C:\Users\Usuario\Documents\Arduino\libraries\Mitov\Visuino


Abra o projeto!


Atenção, o VISUINO instalará o SDK "STM32" em 

C:\Users\Usuario\AppData\Local\Arduino15\packages

Podes apagar, deixe o baixado pela IDE do Arduino, para que faça referência ao SDK oficial do STM32.

C:\Users\Usuario\AppData\Local\Arduino15\packages\STMicroelectronics

LMIC LoRaWAN  Stack

O programa Raw foi modificado e incluído no Custom Code do Visuino, veja como ficou o código fonte completo gerado pelo Visuino.

//---------------------------------------------- // // Sketch Generated by Visuino // www.visuino.com // //------------------ Source -------------------- // // lora_p2p_modbus_device.visuino // //---------------------------------------------- #define VISUINO_STMDUINO #include <OpenWire.h> #include <Mitov.h> #include <Mitov_StandardSerial.h> #include <lmic.h> #include <hal/hal.h> #include <SPI.h> HardwareSerial Serial1(PB4, PB3); #include <Mitov_CustomCode.h> #include <Mitov_Converters.h> // we formerly would check this configuration; but now there is a flag, // in the LMIC, LMIC.noRXIQinversion; // if we set that during init, we get the same effect. If // DISABLE_INVERT_IQ_ON_RX is defined, it means that LMIC.noRXIQinversion is // treated as always set. // // #if !defined(DISABLE_INVERT_IQ_ON_RX) // #error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \ // lmic_project_config.h in arduino-lmic/project_config to set it. // #endif // How often to send a packet. Note that this sketch bypasses the normal // LMIC duty cycle limiting, so when you change anything in this sketch // (payload length, frequency, spreading factor), be sure to check if // this interval should not also be increased. // See this spreadsheet for an easy airtime and duty cycle calculator: // https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc #define TX_INTERVAL 300 String PAYLOAD_String; char PAYLOAD_char[512] = {0}; char Text2_[512]; // Pin mapping const lmic_pinmap lmic_pins = { .nss = RADIO_NSS, .rxtx = RADIO_ANT_SWITCH_HF, .rst = RADIO_RESET, .dio = {RADIO_DIO_0, RADIO_DIO_1, RADIO_DIO_2}, }; // These callbacks are only used in over-the-air activation, so they are // left empty here (we cannot leave them out completely unless // DISABLE_JOIN is set in arduino-lmoc/project_config/lmic_project_config.h, // otherwise the linker will complain). void os_getArtEui (u1_t* buf) { } void os_getDevEui (u1_t* buf) { } void os_getDevKey (u1_t* buf) { } void onEvent (ev_t ev) { } osjob_t txjob; osjob_t timeoutjob; static void tx_func (osjob_t* job); // Transmit the given string and call the given function afterwards void tx(char *str, osjobcb_t func) { os_radio(RADIO_RST); // Stop RX first delay(1); // Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet LMIC.dataLen = 0; while (*str) LMIC.frame[LMIC.dataLen++] = *str++; LMIC.osjob.func = func; os_radio(RADIO_TX); //text2("TX"); //Serial.println("TX"); } // Enable rx mode and call func when a packet is received void rx(osjobcb_t func) { LMIC.osjob.func = func; LMIC.rxtime = os_getTime(); // RX _now_ // Enable "continuous" RX (e.g. without a timeout, still stops after // receiving a packet) os_radio(RADIO_RXON); //text2("RX"); //Serial.println("RX"); } static void rxtimeout_func(osjob_t *job) { digitalWrite(LED_BUILTIN, LOW); // off } static void rx_func (osjob_t* job) { // Blink once to confirm reception and then keep the led on digitalWrite(LED_BUILTIN, LOW); // off delay(10); digitalWrite(LED_BUILTIN, HIGH); // on // Timeout RX (i.e. update led status) after 3 periods without RX os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func); // Reschedule TX so that it should not collide with the other side's // next TX //os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL/2), tx_func); ////sprintf(Text2_,"Got %u bytes",LMIC.dataLen); ////text2((String) Text2_); //Serial.print("Got "); //Serial.print((String) LMIC.dataLen); //Serial.println(" bytes"); sprintf(Text2_,"%s",LMIC.frame); //bug ? Text2_[LMIC.dataLen]=0; text2((String) Text2_); //Serial.write(LMIC.frame, LMIC.dataLen); //Serial.println(); // Restart RX rx(rx_func); } static void txdone_func (osjob_t* job) { rx(rx_func); } // log text to USART and toggle LED static void tx_func (osjob_t* job) { // Transmit if Payload_char not NULL (transmitted by Visuino component) if(!(PAYLOAD_char[0] == 0)) { tx(PAYLOAD_char, txdone_func); PAYLOAD_char[0]=0; PAYLOAD_String=""; // Blink once to confirm transmit digitalWrite(LED_BUILTIN, LOW); // off delay(10); digitalWrite(LED_BUILTIN, HIGH); // on delay(10); digitalWrite(LED_BUILTIN, LOW); // off delay(10); digitalWrite(LED_BUILTIN, HIGH); // on delay(10); digitalWrite(LED_BUILTIN, LOW); // off delay(10); digitalWrite(LED_BUILTIN, HIGH); // on delay(10); digitalWrite(LED_BUILTIN, LOW); // off delay(10); digitalWrite(LED_BUILTIN, HIGH); // on delay(10); digitalWrite(LED_BUILTIN, LOW); // off delay(10); digitalWrite(LED_BUILTIN, HIGH); // on delay(10); digitalWrite(LED_BUILTIN, LOW); // off delay(10); digitalWrite(LED_BUILTIN, HIGH); // on delay(10); digitalWrite(LED_BUILTIN, LOW); // off delay(10); digitalWrite(LED_BUILTIN, HIGH); // on delay(10); digitalWrite(LED_BUILTIN, LOW); // off delay(10); digitalWrite(LED_BUILTIN, HIGH); // on } // reschedule job every TX_INTERVAL (plus a bit of random to prevent // systematic collisions), unless packets are received, then rx_func // will reschedule at half this time. os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func); } // Shared Component Member Variables namespace ComponentVariables { class { public: uint32_t Value1 : 10; } BitFields; class Variable1 { public: inline static uint32_t GetValue() { return BitFields.Value1; } inline static void SetValue( uint32_t AValue ) { BitFields.Value1 = AValue; } }; } // ComponentVariables // Pin Call Declarations namespace PinCalls { class PinCallerReceive0 { public: void Notify( void *_Data ); }; class PinCallerReceive1 { public: void Notify( void *_Data ); }; class PinCallerReceive2 { public: void Notify( void *_Data ); }; } // PinCalls // Call Chains namespace CallChains { class IsEnding1 { public: inline static uint32_t Count() { return 0; } static void Call( bool & AResult ); }; } // CallChains // Arduino Board Declarations namespace BoardDeclarations { namespace Types { typedef Mitov::SerialSTM32Port< SERIAL_TYPE, // 0_T_TYPE Serial, // 1_C_OBJECT Mitov::ConstantProperty<4, uint32_t, 0 >, // AfterSendingDelay Mitov::ConstantProperty<7, uint32_t, 8 >, // DataBits Mitov::ConstantProperty<2, bool, true >, // Enabled Mitov::ConstantProperty<14, uint32_t, 0 >, // FEndTime Mitov::ConstantProperty<12, bool, false >, // FSending Mitov::GenericPin_NoImplementation<5 >, // OutputPin Mitov::ConstantProperty<9, Mitov::TArduinoStandardSerialParity, Mitov::spNone >, // Parity Mitov::ConstantProperty<10, uint32_t, 10 >, // RXPin Mitov::DigitalPin_NoImplementation<3 >, // SendingOutputPin Mitov::ConstantProperty<6, uint32_t, 115200 >, // Speed Mitov::ConstantProperty<8, uint32_t, 1 >, // StopBits Mitov::ConstantProperty<11, uint32_t, 9 > // TXPin > SerialPort0; } // Types namespace Instances { Types::SerialPort0 SerialPort0; } // Instances namespace Types { typedef Mitov::ArduinoSerialInput_String<BoardDeclarations::Types::SerialPort0, BoardDeclarations::Instances::SerialPort0, char *> SerialPort0_Input_IOWStringStream_1; } // Types namespace Instances { Types::SerialPort0_Input_IOWStringStream_1 SerialPort0_Input_IOWStringStream_1; } // Instances namespace Types { typedef Mitov::SerialPort< SERIAL_TYPE, // 0_T_TYPE Serial1, // 1_C_OBJECT Mitov::ConstantProperty<4, uint32_t, 0 >, // AfterSendingDelay Mitov::ConstantProperty<7, uint32_t, 8 >, // DataBits Mitov::ConstantProperty<2, bool, true >, // Enabled Mitov::ConstantProperty<12, uint32_t, 0 >, // FEndTime Mitov::ConstantProperty<10, bool, false >, // FSending Mitov::GenericPin_EmbeddedPinImplementation<5, ::PinCalls::PinCallerReceive0 >, // OutputPin Mitov::ConstantProperty<9, Mitov::TArduinoStandardSerialParity, Mitov::spNone >, // Parity Mitov::DigitalPin_NoImplementation<3 >, // SendingOutputPin Mitov::ConstantProperty<6, uint32_t, 115200 >, // Speed Mitov::ConstantProperty<8, uint32_t, 1 > // StopBits > SerialPort1; } // Types namespace Instances { Types::SerialPort1 SerialPort1; } // Instances } // BoardDeclarations // Custom Code Declarations namespace CustomCode { class TCustomCodeLoraWAN_Stack1 { public: // Outputs Mitov::CustomOutput< Mitov::TextPin_EmbeddedPinImplementation<2, ::PinCalls::PinCallerReceive1 >, // OutputPin Mitov::String // TYPE > Text2; public: // Inputs inline void __Inputs_o_0_o_Receive( Mitov::String AValue ) { AValue.toCharArray(PAYLOAD_char,512); } public: void SystemInit(); public: void SystemStart(); public: void SystemLoopUpdateHardware(); }; class TArduinoCustomCodeInputTextElement1 { public: void InputPin_o_Receive( void *_Data ); }; } // CustomCode // Declarations namespace Declarations { namespace Types { typedef CustomCode::TCustomCodeLoraWAN_Stack1 CustomCodeLoraWAN_Stack; } // Types namespace Instances { Types::CustomCodeLoraWAN_Stack CustomCodeLoraWAN_Stack; } // Instances namespace Types { typedef CustomCode::TArduinoCustomCodeInputTextElement1 TArduinoCustomCodeInputTextElement1; } // Types namespace Instances { Types::TArduinoCustomCodeInputTextElement1 TArduinoCustomCodeInputTextElement1; } // Instances Mitov::CustomOutput< Mitov::TextPin_EmbeddedPinImplementation<2, ::PinCalls::PinCallerReceive1 >, // OutputPin Mitov::String // TYPE > &TArduinoCustomCodeOutputTextElement1 = Declarations::Instances::CustomCodeLoraWAN_Stack.Text2; namespace Types { typedef Mitov::CharToText< Mitov::EmbeddedCallChain<CallChains::IsEnding1 >, // Elements_IsEnding Mitov::ConstantProperty<4, bool, true >, // Enabled Mitov::ConstantProperty<14, bool, true >, // EndOnNewLine Mitov::TypedVariable<9, uint32_t, ::ComponentVariables::Variable1 >, // FIndex 512, // MaxLength Mitov::TextPin_EmbeddedPinImplementation<3, ::PinCalls::PinCallerReceive2 >, // OutputPin Mitov::ConstantProperty<12, bool, false >, // Truncate Mitov::ConstantProperty<13, bool, false > // UpdateOnEachChar > CharToText1; } // Types namespace Instances { Types::CharToText1 CharToText1; } // Instances } // Declarations // Custom Code Implementations namespace CustomCode { void TCustomCodeLoraWAN_Stack1::SystemInit() { pinMode(RADIO_TCXO_VCC_PIN,OUTPUT); digitalWrite(RADIO_TCXO_VCC_PIN,HIGH); pinMode(RADIO_ANT_SWITCH_PIN_RX,OUTPUT); digitalWrite(RADIO_ANT_SWITCH_PIN_RX,HIGH); pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, LOW); } void TCustomCodeLoraWAN_Stack1::SystemStart() { //LOM204 pinMode(RADIO_TCXO_VCC_PIN,OUTPUT); digitalWrite(RADIO_TCXO_VCC_PIN,HIGH); pinMode(RADIO_ANT_SWITCH_PIN_RX,OUTPUT); digitalWrite(RADIO_ANT_SWITCH_PIN_RX,HIGH); pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, LOW); // LMIC init // initialize runtime env os_init(); // make it easier for test, by pull the parameters up to the top of the // block. Ideally, we'd use the serial port to drive this; or have // a voting protocol where one side is elected the controller and // guides the responder through all the channels, powers, ramps // the transmit power from min to max, and measures the RSSI and SNR. // Even more amazing would be a scheme where the controller could // handle multiple nodes; in that case we'd have a way to do // production test and qualification. However, using an RWC5020A // is a much better use of development time. // set fDownlink true to use a downlink channel; false // to use an uplink channel. Generally speaking, uplink // is more interesting, because you can prove that gateways // *should* be able to hear you. const static bool fDownlink = false; // the downlink channel to be used. const static uint8_t kDownlinkChannel = 3; // the uplink channel to be used. const static uint8_t kUplinkChannel = 8 + 3; // this is automatically set to the proper bandwidth in kHz, // based on the selected channel. uint32_t uBandwidth; if (! fDownlink) { if (kUplinkChannel < 64) { LMIC.freq = AU915_125kHz_UPFBASE + kUplinkChannel * AU915_125kHz_UPFSTEP; uBandwidth = 125; } else { LMIC.freq = AU915_500kHz_UPFBASE + (kUplinkChannel - 64) * AU915_500kHz_UPFSTEP; uBandwidth = 500; } } else { // downlink channel LMIC.freq = AU915_500kHz_DNFBASE + kDownlinkChannel * AU915_500kHz_DNFSTEP; uBandwidth = 500; } // Use a suitable spreading factor if (uBandwidth < 500) LMIC.datarate = AU915_DR_SF10; // DR4 else LMIC.datarate = AU915_DR_SF12CR; // DR8 // default tx power for AU: 30 dBm LMIC.txpow = 20; // disable RX IQ inversion LMIC.noRXIQinversion = true; // This sets CR 4/5, BW125 (except for EU/AS923 DR_SF7B, which uses BW250) LMIC.rps = updr2rps(LMIC.datarate); sprintf(Text2_,"Frequency: %u.%uMHz LMIC.datarate: %u LMIC.txpow: %u",LMIC.freq / 1000000,(LMIC.freq / 100000) % 10,LMIC.datarate,LMIC.txpow); text2(Text2_); // This sets CR 4/5, BW125 (except for DR_SF7B, which uses BW250) LMIC.rps = updr2rps(LMIC.datarate); // disable RX IQ inversion LMIC.noRXIQinversion = true; // setup initial job //event receive - unidirecional rx(rx_func); os_setCallback(&txjob, tx_func); } void TCustomCodeLoraWAN_Stack1::SystemLoopUpdateHardware() { os_runloop_once(); } void TArduinoCustomCodeInputTextElement1::InputPin_o_Receive( void *_Data ) { Declarations::Instances::CustomCodeLoraWAN_Stack.__Inputs_o_0_o_Receive( Mitov::String( ( char *)_Data ) ); } } // CustomCode // Type Converters namespace TypeConverters { Mitov::Convert_BinaryBlockToChar Converter0; } // TypeConverters // Call Chains namespace CallChains { void IsEnding1::Call( bool & AResult ) { } } // CallChains // Pin Call Declarations namespace PinCalls { void PinCallerConverterReceive1( void *_Data ); } // PinCalls // Pin Call Implementations namespace PinCalls { void PinCallerReceive0::Notify( void *_Data ) { TypeConverters::Converter0.Convert( _Data, PinCallerConverterReceive1 ); } void PinCallerConverterReceive1( void *_Data ) { Declarations::Instances::CharToText1.InputPin_o_Receive( _Data ); } void PinCallerReceive1::Notify( void *_Data ) { BoardDeclarations::Instances::SerialPort0_Input_IOWStringStream_1.InputPin_o_Receive( _Data ); } void PinCallerReceive2::Notify( void *_Data ) { Declarations::Instances::TArduinoCustomCodeInputTextElement1.InputPin_o_Receive( _Data ); } } // PinCalls namespace ComponentsHardware { void SystemUpdateHardware() { Declarations::Instances::CustomCodeLoraWAN_Stack.SystemLoopUpdateHardware(); } } // ComponentsHardware void text2(String Txt) { Declarations::Instances::CustomCodeLoraWAN_Stack.Text2.Send(Txt); } //The setup function is called once at startup of the sketch void setup() { BoardDeclarations::Instances::SerialPort0.SystemInit(); BoardDeclarations::Instances::SerialPort1.SystemInit(); Declarations::Instances::CustomCodeLoraWAN_Stack.SystemInit(); Declarations::Instances::CustomCodeLoraWAN_Stack.SystemStart(); } // The loop function is called in an endless loop void loop() { BoardDeclarations::Instances::SerialPort1.SystemLoopBeginOutput(); ComponentsHardware::SystemUpdateHardware(); }
Certifique-se que LOM204 e programador estejam selecionados, bem como demais parâmetros e então compile.


Transfira o programa  para os dois LOM204

Pronto, agora você tem dois LOM204 para comunicação P2p.

Você agora pode desenvolver uma aplicação no NINA W106 (Master) que envie dados para os demais NINA W106 (Slaves), conforme descrito no início.

Para NINA W106 (Master ou Slave) fazer um broadcasting, dever enviar (Txd) via Serial o pacote até o RX5 do LOM204.

Para o NINA W106 (Master ou Slave) receber (Rxd) um broadcasting, deve ser conectando a Serial no TX1 do LOM204.

A velocidade comunicação com a UART é de 115200,N,8,1

Não envie dados antes do intervalo de 300mS.

Master


Slave


Colisões

Não deveria haver, pois apenas um Slave irá responder por vez.

Vídeo da execução da comunicação Master e Slave

Parte I


Parte II

P2P 


Payloads


Slave recebendo requisição e devolvendo dados para Master


Frequência LoRa (Default do exemplo Raw LMIC)


Streaming para 4 Slaves







Testando P2P com 3 NINA W106 (1 Master e 2 Slaves)

LED (PC_0)

Enquanto aceso tiver 1 Blink - recepção de dados (tanto do Master como Slave)
Enquanto aceso tiver 2 Blinks rápidos - transmissão de dados
Apagado - ocioso

STM32Programmer

Uma boa opção se tiveres problemas com drivers é instalar.

MQTT

Para os dispositivos de Internet das Coisas (IoT), a conexão com a Internet é um requisito. A conexão com a Internet permite que os dispositivos trabalhem entre si e com serviços de backend. O protocolo de rede subjacente da Internet é o TCP/IP. Desenvolvido com base na pilha TCP/IP, o MQTT (Message Queue Telemetry Transport) tornou-se o padrão para comunicações de IoT.




MODBUS

Modbus é um Protocolo de comunicação de dados utilizado em sistemas de automação industrial. Criado originalmente no final da década de 1970, mais especificamente em 1979, pela fabricante de equipamentos Modicon. É um dos mais antigos e até hoje mais utilizados[2] protocolos em redes de Controladores lógicos programáveis (PLC) para aquisição de sinais(0 ou 1) de instrumentos e comandar atuadores. A Schneider Electric (atual controladora da Modicon) transferiu os direitos do protocolo para a Modbus Organization em 2004 e a utilização é livre de taxas de licenciamento.Por esta razão, e também por se adequar facilmente a diversos meios físicos, é utilizado em milhares de equipamentos existentes e é uma das soluções de rede mais baratas a serem utilizadas em Automação Industrial.

Originalmente implementado como um protocolo de nível de aplicação destinado a transferir dados por uma camada serial, o Modbus foi ampliado para incluir implementações em comunicações seriais, TCP/IP e UDP (user datagram protocol). 

O Modbus é um protocolo de requisição-resposta que utiliza um relacionamento mestre-escravo. Em um relacionamento mestre-escravo, a comunicação sempre ocorre em pares — um dispositivo deve iniciar a requisição e então aguardar por uma resposta — e o dispositivo iniciador (o mestre) é responsável por iniciar cada interação. Tipicamente, o mestre é uma interface homem-máquina (IHM) ou sistema SCADA (Supervisory Control and Data Acquisition) e o escravo é um sensor, controlador lógico programável (CLP) ou controlador programável para automação (CPA). O conteúdo dessas requisições e respostas e as camadas de rede pelas quais essas mensagens são enviadas são definidos pelas diferentes camadas do protocolo.

Acesso aos dados no Modbus e o modelo de dados do Modbus

Os dados que podem ser acessados pelo Modbus são armazenados, de forma geral, em um dos quatro bancos de dados, ou faixas de endereço: coils, entradas discretas, registradores holding e registradores de entrada. Como ocorre com muitas partes da especificação, esses nomes podem variar, dependendo da indústria ou aplicação. Por exemplo, os registradores holding podem ser denominados registradores de saída, e os coils podem ser referidos como saídas digitais ou discretas. Esses bancos de dados definem o tipo e os direitos de acesso dos dados contidos. Os dispositivos escravo têm acesso direto a esses dados, que são hospedados localmente nos dispositivos. Os dados que podem ser acessados pelo Modbus são de forma geral um subconjunto da memória principal do dispositivo. Por outro lado, os mestres Modbus precisam solicitar acesso a esses dados, utilizando diversos códigos de função

Endereçamento do modelo de dados

A especificação define que cada bloco contém um espaço de endereçamento de até 65.536 (216) elementos. Com a definição da PDU, o Modbus define o endereço de cada elemento de dados na faixa de 0 a 65.535. Entretanto, cada elemento de dados é numerado de 1 a n, onde n tem o valor máximo de 65.536. Assim, o coil 1 está no endereço 0 do bloco de coils, enquanto que o registrador holding 54 está no endereço 53 da seção de memória reservada pelo escravo para os registradores holding.

Não é obrigatório implementar as faixas completas permitidas pela especificação no dispositivo. Por exemplo, pode ser escolhido para um dado dispositivo não implementar coils, entradas discretas ou registradores de entrada e, em vez disso, utilizar registradores holding de 150 a 175 e 200 a 225. Isso é perfeitamente aceitável; nesse caso, tentativas de acesso inválidas seriam tratadas por exceções.

Faixas de endereçamento de dados

Embora a especificação defina que diferentes tipos de dados devem existir em blocos diferentes e atribua uma faixa de endereços local para cada tipo, isso não implica que haverá necessariamente um esquema de endereçamento intuitivo ou facilmente compreensível para a documentação da memória que pode ser acessada pelo Modbus para um determinado dispositivo. Para simplificar a discussão sobre as posições dos blocos de memória, foi introduzido um esquema de numeração que inclui prefixos ao endereço dos dados em questão.

Por exemplo, em vez de se referir a um item como registrador holding 14 no endereço 13, o manual do dispositivo pode se referir a um item de dados no endereço 4.014, 40.014 ou 400.014. Em todos esses casos, o primeiro número especificado é 4, que representa os registradores holding; os demais números especificam um endereço. A diferença entre 4XXX, 4XXXX e 4XXXXX depende do espaço de endereços utilizado pelo dispositivo. Se todos os 65.536 registradores estiverem em uso, utilizaremos a notação 4XXXXX, pois ela permite o uso da faixa de 400.001 a 465.536. Se apenas alguns registradores forem usados, uma prática comum é usar a faixa de 4.001 a 4.999.

Foi utilizado Modbus RTU Serial.


Master
Slave


Vídeos 















Fontes:


Dúvidas

suporte@smartcore.com.br


Sobre a SMARTCORE

A SmartCore fornece módulos para comunicação wireless, biometria, conectividade, rastreamento e automação.
Nosso portfólio inclui modem 2G/3G/4G/NB-IoT/Cat.M, satelital, módulos WiFi, Bluetooth, GNSS / GPS, Sigfox, LoRa, leitor de cartão, leitor QR code, mecanismo de impressão, mini-board PC, antena, pigtail, LCD, bateria, repetidor GPS e sensores.

Mais detalhes em www.smartcore.com.br

Nenhum comentário:

Postar um comentário