domingo, 26 de novembro de 2023

LSM110A - MEDINDO ESTADO DE UMA BOIA E ENVIANDO VIA LoRaWAN PARA CHIRPSTACK - ARDUINO

       

LSM110A - MEDINDO ESTADO DE UMA BOIA E ENVIANDO VIA LoRaWAN PARA CHIRPSTACK

LSM110A Starter KIT

Módulo

O LSM110A é um módulo de última geração que integra o STMicroelectronics STM32WL. É muito menos consumo atual para o dispositivo IoT para estender a vida útil da bateria. E, também suporta ambas as tecnologias – Sigfox e LoRa – com o próprio módulo LSM110A.

BREAKOUT para testes


Esquema Elétrico - últimas correções





PLACA MONTADA


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

O objetivo específico neste projeto é programar o LSM110A para permitir a leitura do estado de uma boia de nível (LIGADO/DESLIGADO) a cada 10s e via OTAA enviar para o servidor CHIRPSTACK via Gateway LoRaWAN da Dragino.

Será enviada a quantidade de vezes que a boia transbordou também.
Boia de Nível (LIGADO/DESLIGADO)

O que é ChirpStack


ChirpStack é um Servidor de Rede LoRaWAN de código aberto que pode ser usado para configurar redes LoRaWAN. O ChirpStack fornece uma interface web para o gerenciamento de gateways, dispositivos e inquilinos, bem como para configurar integrações de dados com os principais provedores de nuvem, bancos de dados e serviços comumente usados para lidar com dados de dispositivos. O ChirpStack fornece uma API baseada em gRPC que pode ser usada para integrar ou estender o ChirpStack.


REDE LoRaWAN - CHIRPSTACK

Os elementos da CHIRPSTACK são classificados como: 

• Endpoints (nós): Os dispositivos responsáveis pela camada de sensoriamento da rede, o endpoint LoRaWAN. Podem coletar informações através de sensores e também acionar dispositivos/máquinas via atuadores. São configurados através de uma das três classes distintas do protocolo LaRaWAN; 

• Gateways: Elementos responsáveis por concentrar e processar as informações enviadas pelos endpoints. Os gateways em geral estão conectados a internet, seja por WiFi/Ethernet ou 3G/4G em locais remotos. Mesmo que uma mesma rede LoRaWAN tenha diferentes objetivos, baseados em aplicações distintas, os gateways possuem o objetivo comum de fornecer a maior área de cobertura possível; 

• Aplicações: Conectar e interligar os diferentes dispositivos da rede CHIRPSTACK para o fornecimento de informações gerais sobre a coleta de dados dos dispositivos.


CONFIGURAÇÃO AUTENTICAÇÃO NA REDE LoRaWAN

OTAA

• Vantagem: a rede gera e envia as chaves de criptografia; isto torna mais seguro. Devido ao maior nível de segurança, o OTAA é o método mais utilizado em IoT / LoRaWAN. 

• AppEUI: Este é um identificador de aplicativo exclusivo usado para agrupar objetos. este
endereço, 64 bits, é usado para classificar os dispositivos periféricos por aplicação. Essa configuração pode seja ajustado. 

• DevEUI: Este identificador, configurado de fábrica, torna cada objeto único. Em princípio, esta
configuração não pode ser ajustada. 

• AppKey: esta é uma chave secreta compartilhada entre o dispositivo periférico e a rede. É usado para determinar as chaves da sessão. Essa configuração pode ser ajustada.

Concentre-se na OTAA 

O Servido de Rede é o componente de software encarregado de estabelecer uma conexão com os objetos e gerenciando o núcleo da rede. Durante a conexão OTAA, e supondo que o dispositivo esteja autorizado a conectar-se a rede, a rede troca chaves de criptografia específicas da sessão com o núcleo da rede. O Servidor de Rede então aloca informações específicas para a sessão e as envia para o aparelho periférico:

DevAddr: Endereço lógico (equivalente a um endereço IP) que será usado para todos comunicação subseqüente.

 NetSKey (chave de sessão de rede): chave de criptografia entre o objeto e o operador usado para transmissões e para validar a integridade das mensagens. 

•   AppSKey (chave de sessão do aplicativo): chave de criptografia entre o objeto e operador (através da aplicação) utilizado para as transmissões e para validar a integridade das mensagens



LSM110A e ARDUINO (RAKWIRELESS)

LSM110A é baseado em STM32WL55.  No Arduino RAKWIRELESS existe o core similar, o qual deve ser modificado para ser compatível com LSM110A

Baixar RAKWIRELESS Arduino
https://raw.githubusercontent.com/RAKWireless/RAKwireless-Arduino-BSP-Index/main/package_rakwireless.com_rui_index.json
Alterando configurações para ser compatível com o LSM110A

Altere radio_board_if.c para

int32_t RBI_ConfigRFSwitch(RBI_Switch_TypeDef Config) { switch (Config) { case RBI_SWITCH_OFF: { /* Turn off switch */ HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET); break; } case RBI_SWITCH_RX: { /*Turns On in Rx Mode the RF Switch */ HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET); break; } case RBI_SWITCH_RFO_LP: { /*Turns On in Tx Low Power the RF Switch */ HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_SET); break; } case RBI_SWITCH_RFO_HP: { /*Turns On in Tx High Power the RF Switch */ HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_SET); break; } default: break; } return 0; }



































Altere radio_board_if.h para

#define RF_SW_CTRL1_PIN GPIO_PIN_12 #define RF_SW_CTRL1_GPIO_PORT GPIOB #define RF_SW_CTRL1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define RF_SW_CTRL1_GPIO_CLK_DISABLE() __HAL_RCC_GPIOA_CLK_DISABLE() /* official version */ #define RF_SW_CTRL2_PIN GPIO_PIN_13 #define RF_SW_CTRL2_GPIO_PORT GPIOC #define RF_SW_CTRL2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define RF_SW_CTRL2_GPIO_CLK_DISABLE() __HAL_RCC_GPIOA_CLK_DISABLE()

Altere Cristal para uso de frequênciais (BAND) mais altas (board.c)

uint8_t BoardGetHardwareFreq(void)
{
    uint8_t hardwareFreq = 0;
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /*Configure GPIO pin : PB12 */
    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    hardwareFreq  = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12);
hardwareFreq = 1;

    HAL_GPIO_DeInit(GPIOB,GPIO_PIN_12);
    return hardwareFreq;
}

Altere Placa para RAK3272-SIP, compile



Grave com o STM32 Programmer o BOOTLOADER no LSM110A 

Assim, pode-se transferir o programa via Serial, através dos pinos PA2 e PA3 do LSM110A.



Ok, o ambiente de desenvolvimento está pronto. Configure 2 parâmetros, os quais deve ser obtidos do servidor CHIRPSTACK.

Use os comandos AT

AT+DEVEUI=9ca7653a4a8bXX15
AT+APPKEY=e6XX867aa4a0dXXf1a5166abff3be9b5

Os demais são HardCoded

Transferindo


Executando

Uma vez gravado o Software, o LSM110A fará o JOIN na LoRaWAN enviará o pacote toda vez que o detectar o transbordo da boia.

CHIRPSTACK


RECEBENDO PACOTES 

Mais de 22000 leituras


Código Fonte

//https://docs.rakwireless.com/RUI3/Arduino-API/#settimeout //https://github.com/beegee-tokyo/RUI3-LowPower-Example/blob/main/RUI3-LowPower-Example.ino //AT+DEVEUI=9ca7xx3a4axx7515 //AT+APPKEY=e6338xxaa4xxd3af1a51xxabfxxbe9b5 //ATZ #define DEBUGGER_ON 0 #define LOW_POWER_DISABLE 0 #define OTAA_BAND RAK_REGION_AU915 #define OTAA_DEVEUI {0x00,0x80,0xe1,0x15,0x05,0x1f,0xd8,0x0a} #define OTAA_APPEUI {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} #define OTAA_APPKEY {0xa6, 0x14, 0xd5, 0x99 , 0x63 , 0xa0 , 0x86 , 0x1e , 0xdf , 0x27 , 0x02 , 0xae , 0x6f , 0x0e , 0x1a , 0xd6} /** Time interval to send packets in milliseconds */ uint32_t g_send_repeat_time = 10000; uint8_t QNT_LINKCHECK_FAIL = 0; uint8_t QNT_JOINED_FAIL = 0; uint16_t maskBuff = 0x0002; uint8_t buff[16]; #include "app.h" float BAT; float intpart; // A variable to store the integer part float floatpart; // A variable to store the float part bool LINKCHECK_ERROR = 0; #define ANALOG_BAT PB3 //ATC+SENDCOUNTER=0 //flash uint8_t flash_value[4] = {0}; bool wr_result = false; uint32_t data_to_save = 0; uint8_t flash_read[4] = {0}; uint32_t flash_data = 0; /** Packet is confirmed/unconfirmed (Set with AT commands) */ bool g_confirmed_mode = false; /** If confirmed packet, number or retries (Set with AT commands) */ uint8_t g_confirmed_retry = 0; /** Data rate (Set with AT commands) */ uint8_t g_data_rate = 3; /** Counter pulse */ uint32_t g_send_counter = 0; /** Flag if transmit is active, used by some sensors */ volatile bool tx_active = false; /** fPort to send packages */ uint8_t set_fPort = 2; /** Payload buffer */ uint8_t g_solution_data[64]; /** * @brief Callback after join request cycle * * @param status Join result */ void joinCallback(int32_t status) { // MYLOG("JOIN-CB", "Join result %d", status); if (status != 0) { MYLOG("JOIN-CB", "LoRaWan OTAA - join fail! \r\n"); // To be checked if this makes sense // api.lorawan.join(); // adicionado por Miguel } else { MYLOG("JOIN-CB", "LoRaWan OTAA - joined! \r\n"); } } /** * @brief LoRaWAN callback after packet was received * * @param data Structure with the received data */ void receiveCallback(SERVICE_LORA_RECEIVE_T *data) { MYLOG("RX-CB", "RX, port %d, DR %d, RSSI %d, SNR %d", data->Port, data->RxDatarate, data->Rssi, data->Snr); //number of digits on register if(data->BufferSize==7) { flash_data = 0; flash_data = flash_data + (data->Buffer[0]-'0')*1000000; flash_data = flash_data + (data->Buffer[1]-'0')*100000; flash_data = flash_data + (data->Buffer[2]-'0')*10000; flash_data = flash_data + (data->Buffer[3]-'0')*1000; flash_data = flash_data + (data->Buffer[4]-'0')*100; flash_data = flash_data + (data->Buffer[5]-'0')*10; flash_data = flash_data + (data->Buffer[6]-'0')*1; Serial.print("New Downlink Flash data: "); Serial.print(flash_data); Serial.println(); flash_value[0] = (uint8_t)(flash_data >> 0); flash_value[1] = (uint8_t)(flash_data >> 8); flash_value[2] = (uint8_t)(flash_data >> 16); flash_value[3] = (uint8_t)(flash_data >> 24); wr_result = api.system.flash.set(0, flash_value, 4); while (!wr_result) { // Retry wr_result = api.system.flash.set(0, flash_value, 4); delay(100); } //for (int i = 0; i < data->BufferSize; i++) //{ //Serial.printf("%02X", data->Buffer[i]); //} //Serial.print("\r\n"); } tx_active = false; } /** * @brief LoRaWAN callback after TX is finished * * @param status TX status */ void sendCallback(int32_t status) { MYLOG("TX-CB", "TX status %d", status); tx_active = false; } uint8_t Hall_Sensor = PB8; // pin to measure pulses void Hydrometer_Pulse() { sensor_handler(NULL); } /** * @brief Arduino setup, called once after reboot/power-up * */ void setup() { analogReadResolution(12); Serial.begin(115200); //Para entrada de comandos apos o reset delay(10000); // sensor hall pinMode(Hall_Sensor, INPUT); attachInterrupt(Hall_Sensor, Hydrometer_Pulse, CHANGE); // OTAA Device EUI MSB first uint8_t node_device_eui[8] = OTAA_DEVEUI; // OTAA Application EUI MSB first uint8_t node_app_eui[8] = OTAA_APPEUI; // OTAA Application Key MSB first uint8_t node_app_key[16] = OTAA_APPKEY; if(api.lorawan.appkey.get(buff, 16) == true) { Serial.print("LoRaWan AppKey = 0x"); for(int i = 0; i < 16; i++) { Serial.printf("%02X", buff[i]); } Serial.println(""); } else { Serial.println("LoRaWan AppKey get fail"); } ////// if (!api.lorawan.appkey.set(buff, 16)) { ////// Serial.printf("LoRaWan OTAA - set application key is incorrect! \r\n"); ////// return; ////// } if(api.lorawan.deui.get(buff, 8) == true) { Serial.print("LoRaWan AppEUI = 0x"); for(int i = 0; i < 8; i++) { Serial.printf("%02X", buff[i]); } Serial.println(""); } else { Serial.println("LoRaWan APPEUI get fail"); } ////// if (!api.lorawan.deui.set(buff, 8)) { ////// Serial.printf("LoRaWan OTAA - set device EUI is incorrect! \r\n"); ////// return; ////// } ////// if (!api.lorawan.appeui.set(node_app_eui, 8)) { ////// Serial.printf("LoRaWan OTAA - set application EUI is incorrect! \r\n"); ////// return; ////// } if (!api.lorawan.band.set(OTAA_BAND)) { Serial.printf("LoRaWan OTAA - set band is incorrect! \r\n"); return; } if (!api.lorawan.deviceClass.set(RAK_LORA_CLASS_A)) { Serial.printf("LoRaWan OTAA - set device class is incorrect! \r\n"); return; } if (!api.lorawan.njm.set(RAK_LORA_OTAA)) // Set the network join mode to OTAA { Serial.printf("LoRaWan OTAA - set network join mode is incorrect! \r\n"); return; } if (!api.lorawan.rety.set(1)) { Serial.printf("LoRaWan OTAA - set retry times is incorrect! \r\n"); return; } if (!api.lorawan.cfm.set(0)) { Serial.printf("LoRaWan OTAA - set confirm mode is incorrect! \r\n"); return; } if (!api.lorawan.adr.set(0)) { Serial.printf("LoRaWan OTAA - set adaptive data rate is incorrect! \r\n"); return; } Serial.printf("Set the data rate %s\r\n", api.lorawan.dr.set(2) ? "Success" : "Fail"); Serial.printf("Set channel mask %s\r\n", api.lorawan.mask.set(&maskBuff) ? "Success" : "Fail"); // Setup for LoRaWAN g_confirmed_mode = api.lorawan.cfm.get(); g_confirmed_retry = api.lorawan.rety.get(); g_data_rate = api.lorawan.dr.get(); // Setup the callbacks for joined and send finished api.lorawan.registerRecvCallback(receiveCallback); api.lorawan.registerSendCallback(sendCallback); api.lorawan.registerJoinCallback(joinCallback); Serial.printf("RUI3 %s\n", api.system.firmwareVersion.get().c_str()); api.lorawan.linkcheck.set(1); // Initialize module Wire.begin(); // Register the custom AT command to get device status if (!init_status_at()) { MYLOG("SETUP", "Add custom AT command STATUS fail"); } // Register the custom AT command to set the send Counter if (!init_counter_at()) { MYLOG("SETUP", "Add custom AT command Send Counter fail"); } // Get saved sending counter from flash get_at_setting(); // Create a timer. api.system.timer.create(RAK_TIMER_0, sensor_handler, RAK_TIMER_PERIODIC); if (g_send_repeat_time != 0) { // Start a timer. api.system.timer.start(RAK_TIMER_0, g_send_repeat_time, NULL); } if (g_confirmed_mode) { MYLOG("SETUP", "Confirmed enabled"); } else { MYLOG("SETUP", "Confirmed disabled"); } MYLOG("SETUP", "Retry = %d", g_confirmed_retry); MYLOG("SETUP", "DR = %d", g_data_rate); api.system.lpm.set(1); if (!api.lorawan.join()) // Join to Gateway { Serial.printf("LoRaWan OTAA - join fail! \r\n"); return; } } /** * @brief sensor_handler is a Interrupt function or user call */ void sensor_handler(void *) { MYLOG("UPLINK", "Start"); //FALLING if(digitalRead(Hall_Sensor)==LOW) { if (api.system.flash.get(0, flash_read, 4)) { flash_data |= flash_read[0] << 0; flash_data |= flash_read[1] << 8; flash_data |= flash_read[2] << 16; flash_data |= flash_read[3] << 24; } flash_data++; //Overflow Register if(flash_data==9999999) flash_data++; flash_value[0] = (uint8_t)(flash_data >> 0); flash_value[1] = (uint8_t)(flash_data >> 8); flash_value[2] = (uint8_t)(flash_data >> 16); flash_value[3] = (uint8_t)(flash_data >> 24); wr_result = api.system.flash.set(0, flash_value, 4); } // Create payload char value = 0x31; for (int idx = 0; idx < 7; idx++) { g_solution_data[idx] = value; value++; } // BAT = (analogRead(ANALOG_BAT) * 3.0) / 4096.0; //CR BAT = (analogRead(ANALOG_BAT) * 3.3) / 4096.0; Serial.println(BAT); intpart = floor (BAT); // Get the integer part by rounding down floatpart = fmod (BAT, intpart); // Get the float part by calculating the remainder g_solution_data[0] = flash_value[0]; g_solution_data[1] = flash_value[1]; g_solution_data[2] = flash_value[2]; g_solution_data[3] = flash_value[3]; g_solution_data[4] = intpart; g_solution_data[5] = floatpart*100.0; g_solution_data[6] = digitalRead(Hall_Sensor); // Send the packet send_packet(); } /** * @brief Send the data packet that was prepared in * Cayenne LPP format by the different sensor and location * aqcuision functions * */ void send_packet(void) { Serial.println(flash_data); if (!api.lorawan.njs.get()) { MYLOG("UPLINK", "Not joined, skip sending"); QNT_JOINED_FAIL++; if(QNT_JOINED_FAIL==3) { QNT_JOINED_FAIL=0; //api.system.reboot(); } return; } // Check if it is LoRaWAN MYLOG("UPLINK", "Sending packet..."); // Send the packet api.lorawan.linkcheck.set(1); Serial.printf("Set channel mask %s\r\n", api.lorawan.mask.set(&maskBuff) ? "Success" : "Fail"); if (api.lorawan.send(7, g_solution_data, set_fPort, g_confirmed_mode, g_confirmed_retry)) { MYLOG("UPLINK", "Packet enqueued, size 7"); tx_active = true; } //linkcheck fails 3 times if(LINKCHECK_ERROR==true) { LINKCHECK_ERROR=false; QNT_LINKCHECK_FAIL++; if(QNT_LINKCHECK_FAIL==3) { QNT_LINKCHECK_FAIL=0; //api.system.reboot(); } } } /** * @brief This example is complete Interrupt Sensor (GPIO) * driven. The loop() does nothing than * sleep. * */ void loop() { api.system.sleep.all(); }

Compilação


Altere em seu Gateway  SUB-BAND para 2


Faça a gravação



Execução


UPLINK


Status do Transbordo ou não

DOWNLINK para inicializar contador de Transbordos

Exemplo (BASE64)
MDAwMDExMQ== ---> 0000111 transbordos

/** * @brief LoRaWAN callback after packet was received * * @param data Structure with the received data */ void receiveCallback(SERVICE_LORA_RECEIVE_T *data) { MYLOG("RX-CB", "RX, port %d, DR %d, RSSI %d, SNR %d", data->Port, data->RxDatarate, data->Rssi, data->Snr); //number of digits on register if(data->BufferSize==7) { flash_data = 0; flash_data = flash_data + (data->Buffer[0]-'0')*1000000; flash_data = flash_data + (data->Buffer[1]-'0')*100000; flash_data = flash_data + (data->Buffer[2]-'0')*10000; flash_data = flash_data + (data->Buffer[3]-'0')*1000; flash_data = flash_data + (data->Buffer[4]-'0')*100; flash_data = flash_data + (data->Buffer[5]-'0')*10; flash_data = flash_data + (data->Buffer[6]-'0')*1; Serial.print("New Downlink Flash data: "); Serial.print(flash_data); Serial.println(); flash_value[0] = (uint8_t)(flash_data >> 0); flash_value[1] = (uint8_t)(flash_data >> 8);

Detectado pelo Downlink



LOW POWER

Init

void loop() { api.system.sleep.all(); }

Dúvidas sobre o código fonte

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