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 de acessar via Modbus ScadaBR os dados dos sensores Slaves.
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
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.
LOM204 atuando como Broadcasting
Testado no STARTER KIT LOM204 e também no Breakout LOM204
VISUINO
O Visuino é o mais recente software inovador da Mitov Software. Um ambiente de programação visual que permite programar suas placas Arduino.
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
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
Em seguida, instale o BSP para STM32. Se a instalação estiver concluída como segue:
LIB LoraWAN para Arduino - LMIC (Catena)
Baixe a biblioteca LoRaWAN de:
mcci-catena/arduino-lmic: LoraWAN-MAC-in-C library, adapted to run under the Arduino environment (github.com)
mcci-catena/arduino-lmic: LoraWAN-MAC-in-C library, adapted to run under the Arduino environment (github.com)
Adicione ao Arduino IDE:
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.
ScadaBR é um software livre, gratuito e de código-fonte aberto, para desenvolvimento de aplicações de Automação, Aquisição de Dados e Controle Supervisório.
Configuração no SCADABR
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