sexta-feira, 9 de abril de 2021

UTILIZANDO U-BLOX NINA W106 COM UM GATEWAY DE BLE (SCAN) PARA MQTT

 UTILIZANDO U-BLOX NINA W106 COM UM GATEWAY DE BLE (SCAN) PARA MQTT


O objetivo deste BLOG é mostrar um exemplo em ARDUINO (Platformio) que permita o U-BLOX NINA W106 fazer um SCAN de beacons da Efento e transmitir os dados coletados (temperatura) para um servidor MQTT da U-BLOX (mqtt.thingstream.io) via WIFI, pela porta 8883 (SSL). Os  dados são tratados por uma poderosa ferramenta da U-BLOX chamada FLOW e então são enviados para o ThingSpeak server para visualização gráfica.

Como funciona (Versão BETA)
  • Coloque a Whitelist dos MAC dos sensores (EFENTO);
  • Coloque as credenciais de acesso ao WIFI;
  • Coloque as credenciais de acesso ao ThingStream;
Uma vez  com estes dados, o programa fará um SCAN dos dados (advertisings) enviados via BLE próximos e filtrará pelos da EFENTO. Ao encontrá-los, enviará para o servidor ThingStream (quando mudarem seus conteúdos).

No servidor ThingStream, há a opção de fazer a extração dos dados enviados através de um programação Visual chamada Flow (Node), o qual tens acesso à programação JavaScript e então enviar para o destino final para futuras consultas do LOG.


Utilize a programação clássica com Sockets do Arduino! 

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.


Thigstream
Conheça ThingStream, servidor MQTT da U-BLOX
Implementa também SSL e SN-MQTT

ublox


EFENTO
Sensores wireless e plataforma IoT na nuvem para análise e armazenamento de dados. Nosso objetivo é fazer o monitoramento de qualquer medida física ou quimica o mais simples possivel ! Mais de 20 tipos de sensores. Mais de um milhão de dados recebidos e analisados diariamente Os sensores podem operar até 10 anos com bateria. Sensores wireless com conexão BLE ou celular do tipo NB-IoT e LTE Cat.M. Os gateways possuem conexão WiFi e Ethernet.



Thingspeak é uma plataforma de análise IoT (Internet of Things) que permite agregar, visualizar e analisar streams de dados, de uma forma muito simples. Uma das grandes vantagens da plataforma Thingspeak é que nos permite visualizar os dados enviados pelos nossos dispositivos, em tempo real, mas também a possibilidade de analisar os mesmo recorrendo ao poderoso Matlab.


MONTAGEM

Adquirimos então os seguintes componentes



Montado ficou assim



O esquema elétrico é este



Algumas características do Kit

-Botão de RESET;
-Botão de Modo BOOTLOADER (W106);
-Plugável no PROTOBOARD;
-Acesso às várias GPIOS;

Pequena 


1) Baixe e instale o Visual Studio Code


2) Execute o Visual Studio Code


3) Na opção EXTENSIONS (canto esquerdo), procure por PlatformIO e Instale. Aguarde!

4) Abra o projeto do BLE SCAN TO MQTT GATEWAY que se encontra em...

...e aguarde alguns minutos para instalação do SDK e  pacotes dependentes.


Altere config-example.h...

/*
WiFi config
*/

const char *WLAN_SSID[] = {"Andreia Oi Miguel 2.4G"};
const char *WLAN_PASS[] = {"xxxxxxxx"};
const int NUM_WLANS = 1; //number of Wifi Options

/*
MQTT settings ThingStream 
Topic to Store the Efento Data
*/

const char *MQTT_PREFIX_TOPIC = "esp32-sniffer/";
const char *MQTT_ANNOUNCE_TOPIC = "/status";
const char *MQTT_CONTROL_TOPIC = "/control";
const char *MQTT_BLE_TOPIC = "/ble";
const int  MQTT_PORT =   8883; //if you use SSL  //1883 no SSL
// GOT FROM ThingsStream!
const char *MQTT_SERVER = "mqtt.thingstream.io";
const char *MQTT_USER = "xxxxxxxxxxxxxxxxxxxxxxxxxx;
const char *MQTT_PASS = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const char *MQTT_CLIENT_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";



..com os dados obtidos do THINGSTREAM


Código final

/* * Hardware target: * U-BLOX NINA W106 * * IDE setup: * Board: ESP32 Dev Module * CPU frequency: 240MHz (WiFi/BT) * Flash frequency: 80MHz * Flash size: 4MB (16Mb) * * https://h2zero.github.io/esp-nimble-cpp/class_nim_b_l_e_device.html * */ /* SSL */ #include <WiFiClientSecure.h> /* NO SSL #include <WiFi.h> */ #include <WiFiUdp.h> #include <WiFiMulti.h> #include <PubSubClient.h> #include <Arduino.h> /* This lib resolved the HEAP MEMORY error during SSL MQTT ACCESS */ #include <NimBLEDevice.h> #include <NimBLEAdvertisedDevice.h> #include "NimBLEEddystoneURL.h" #include "NimBLEEddystoneTLM.h" #include "NimBLEBeacon.h" #define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8)) /* * configuration includes passwords/etc * include separately to not leak private information */ #include "config-example.h" /* * globals */ String topic; uint8_t mac[6]; String my_mac; /* blink-per-message housekeeping */ int nBlinks = 0; bool led_state = 1; bool in_blink = false; unsigned long last_blink = 0; /* Blocking Scan */ BLEScan* pBLEScan; bool is_scanning = false; bool cold_boot = true; // treat power-on differently than re-starting a scan //SSL WiFiClientSecure wifi; //wifiSecure /* No SSL WifiClient wifi; */ WiFiMulti wifiMulti; // use multiple wifi options PubSubClient mqtt(wifi); //wifiSecure void check_mqtt(); /* * Given a byte array of length (n), return the ASCII hex representation * and properly zero pad values less than 0x10. * String(0x08, HEX) will yield '8' instead of the expected '08' string */ String hexToStr(uint8_t* arr, int n) { String result; for (int i = 0; i < n; ++i) { if (arr[i] < 0x10) {result += '0';} result += String(arr[i], HEX); } return result; } /* WHITELIST Limited to QUEUE size (Numbers_Of_SensorEfento) Fill this format, Finish with "" */ String WHITELIST_EFENTO_MAC_LIST[] = {"xx:xx:xx:xx:xx:xx","28:2C:02:40:3D:9B","xx:xx:xx:xx:xx:xx",""}; //all upcase //global uint8_t Index_Queue = 0; /* See if Mac is in WHITELIST */ bool MAC_IN_THE_LIST(String MAC) { Index_Queue = 0; //Finish with "" while(!(WHITELIST_EFENTO_MAC_LIST[Index_Queue].compareTo("")==0)) { if(MAC.compareTo(WHITELIST_EFENTO_MAC_LIST[Index_Queue])==0) { return true; } Index_Queue++; } return false; } /* QUEUE FOR PUBLISH TO MQTT */ String QUEUE[Numbers_Of_SensorEfento]; int Efento_Change[Numbers_Of_SensorEfento]; /* Flag to publish or not on Mqtt False, not publish; True, Publish */ void Init_Efento_To_Publish_MQTT() { uint8_t i; for(i=0;i<Numbers_Of_SensorEfento;i++) Efento_Change[i] = false; } /* This function restore from QUEUE, the EfentoSensor contents and Publish to MQTT */ void Get_Manufacturer_Data_Queue() { uint8_t i; //Publish if mqtt connected if (mqtt.connected()) { for(i=0;i<Numbers_Of_SensorEfento;i++) { if(Efento_Change[i]==true) { //Mount Jason do Publish (you can filter if you want the EfentoSensor before to send to MQTT) String msg = "{"; msg.reserve(256); msg.concat("\"Gateway_MAC\":\""); msg.concat(String(WiFi.macAddress())); msg.concat("\","); msg.concat("\"EfentoSensor_MAC\":\""); msg.concat(WHITELIST_EFENTO_MAC_LIST[i]); msg.concat("\","); msg.concat("\"ManufacturerData\":\""); msg.concat(QUEUE[i]); msg.concat("\","); // trim the final comma to ensure valid JSON if (msg.endsWith(",")) { msg.remove(msg.lastIndexOf(",")); } msg.concat("}"); Serial.println("Publish json Data to MQTT"); Serial.println(msg); mqtt.beginPublish(topic.c_str(), msg.length(), false); mqtt.print(msg.c_str());; Serial.println("Done"); Efento_Change[i]=false; //Make the Led blinks nBlinks += 1; } } } } /* BLE SCAN CALLBACKS */ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks { /*** Only a reference to the advertised device is passed now void onResult(BLEAdvertisedDevice advertisedDevice) { **/ void onResult(BLEAdvertisedDevice *advertisedDevice) { String Address = advertisedDevice->getAddress().toString().c_str(); Address.toUpperCase(); //all upcase //This is MAC is on WHITELIST ? if(MAC_IN_THE_LIST(Address)) //Index_Queue will point ManufacturerData Queue { std::string strManufacturerData = advertisedDevice->getManufacturerData(); //You will access to ManufacturerData using index cManufacturerData[xx] uint8_t cManufacturerData[64]; strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0); //You will access to ManufacturerData using index cManufacturerData[xx] String ManufacturerData = hexToStr(cManufacturerData, strManufacturerData.length()); ManufacturerData.toUpperCase(); //all upcase //See if ManufacturerData CHANGED, then put in the Queue, ready to be publish on MQTT SOON (after BLE scan) if(ManufacturerData.compareTo(QUEUE[Index_Queue])==0) //no change {} else { //Signal that EFENTOSENSOR has published data to MQTT Efento_Change[Index_Queue]= true; QUEUE[Index_Queue] = ManufacturerData; Serial.print("Found EfentoSensor: "); Serial.print(Address); Serial.print(" "); Serial.println(ManufacturerData); } } } }; /* * Callback invoked when scanning has completed. */ static void scanCompleteCB(BLEScanResults scanResults) { //Use to BLOCKING SCAN is_scanning = false; } // scanCompleteCB /* * Start a scan for BLE advertisements * if reinit is true, then re-setup the scan configuration parameters */ void startBLEScan(bool reinit=false) { // forget about the devices seen in the last BLE_SCAN_TIME interval pBLEScan->start(BLE_SCAN_TIME, scanCompleteCB, false); is_scanning = true; } /* * Called whenever a payload is received from a subscribed MQTT topic */ void mqtt_receive_callback(char* topic, byte* payload, unsigned int length) { Serial.print("ThingStream MQTT-receive ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); /* Switch on the LED if an 1 was received as first character */ if ((char)payload[0] == '1') { led_state = 0; } else { led_state = 1; } /* this will effectively be a half-blink, forcing the LED to the requested state */ nBlinks += 1; } /* * Check WiFi connection, attempt to reconnect. * This blocks until a connection is (re)established. */ void check_wifi() { if (WiFi.status() != WL_CONNECTED) { Serial.print("WiFi connection lost, reconnecting"); while (wifiMulti.run() != WL_CONNECTED) { delay(500); Serial.print("."); } } } /* * Check the MQTT connection state and attempt to reconnect. * If we do reconnect, then subscribe to MQTT_CONTROL_TOPIC and * make an announcement to MQTT_ANNOUNCE_TOPIC with the WiFi SSID and * local IP address. */ void check_mqtt() { if (mqtt.connected()) { return; } // reconnect Serial.print("ThingStream MQTT reconnect..."); // Attempt to connect int connect_status = mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, (MQTT_PREFIX_TOPIC + my_mac + MQTT_ANNOUNCE_TOPIC).c_str(),2, false,""); if (connect_status) { Serial.println("connected"); Serial.println(); /* Subscribe if you want, to receive some CALL BACK mqtt.subscribe((MQTT_PREFIX_TOPIC + my_mac + MQTT_CONTROL_TOPIC).c_str()); //<======== */ } else { Serial.print("failed, rc="); Serial.println(mqtt.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } /* Start */ void setup() { Serial.begin(9600); Serial.println(); Serial.println("Efento BLE to MQTT Scanner"); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, led_state); /* * setup WiFi */ //WiFi.mode(WIFI_STA); for (int i=0; i<NUM_WLANS; i++) { wifiMulti.addAP(WLAN_SSID[i], WLAN_PASS[i]); } WiFi.macAddress(mac); my_mac = hexToStr(mac, 6); //To use to Topic, without ":" Serial.print("MAC: "); Serial.println(String(WiFi.macAddress())); while (wifiMulti.run() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("IP address: "); Serial.println(WiFi.localIP()); //if you use SSL wifi.setCACert(root_ca); /* * setup MQTT */ mqtt.setServer(MQTT_SERVER, MQTT_PORT); mqtt.setCallback(mqtt_receive_callback); topic = MQTT_PREFIX_TOPIC + my_mac + MQTT_BLE_TOPIC; Serial.println("********************"); Serial.println(topic); Serial.println("********************"); /* Set Flags to publish to No Publish State */ Init_Efento_To_Publish_MQTT(); Serial.println("Starting BLE scan."); BLEDevice::init(""); pBLEScan = BLEDevice::getScan(); //create new scan pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster pBLEScan->setInterval(100); pBLEScan->setWindow(99); // less or equal setInterval value } /* Loop */ void loop() { /* Check all connections, wifi, mqtt */ check_wifi(); check_mqtt(); /* Alive */ mqtt.loop(); // Handle blinking without using delay() unsigned long now = millis(); if (nBlinks > 0) { if (now - last_blink >= BLINK_MS) { last_blink = now; if (in_blink) { // then finish digitalWrite(LED_PIN, led_state); nBlinks--; in_blink = false; } else { digitalWrite(LED_PIN, !led_state); in_blink = true; } } } /* Setup BLE scanning and restart a scan only fire off a new scan if we are not already scanning! */ if (not is_scanning) { if (cold_boot) { startBLEScan(true); cold_boot = false; } else { //Publish Efento Values... Get_Manufacturer_Data_Queue(); //... and then Start a new BLE SCAN (blocking) startBLEScan(); } } delay(1); //Really Necessary ? }}



Compile o programa  e pressione o botão para gravar (reset/boot)

Como podem observar, o programa será transferido!

Abra a serial e veja o BLE SCAN encontrando beacon da EFENTO


Servidor MQTT ThingStream recebendo dados do Beacon EFENTO (Topic)

Certificado (SSL)

/*
Certificate
if you use SSL
*/
const char root_ca[] = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n" \
"ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" \
"b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\n" \
"MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n" \
"b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\n" \
"ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n" \
"9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\n" \
"IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\n" \
"VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n" \
"93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\n" \
"jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" \
"AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\n" \
"A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\n" \
"U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\n" \
"N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\n" \
"o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n" \
"5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\n" \
"rqXRfboQnoZsG4q5WTP468SQvvG5\n" \
"-----END CERTIFICATE-----\n";

Principais partes do Programa

String WHITELIST_EFENTO_MAC_LIST[] = {"xx:xx:xx:xx:xx:xx","28:2C:02:40:3D:9B","xx:xx:xx:xx:xx:xx",""}; //all upcase
Coloque aqui WHITELIST, ou seja, os MAC dos módulos da EFENTO, termine com ""

/*
Number of SensorEfento
*/
#define Numbers_Of_SensorEfento 32
Coloque aqui o número máximo de EFENTOS

 if(ManufacturerData.compareTo(QUEUE[Index_Queue])==0) //no change
Só será publicado se houve alteração nos valores a serem publicados

/*
This lib resolved the HEAP MEMORY error during SSL MQTT ACCESS
*/
#include <NimBLEDevice.h>
#include <NimBLEAdvertisedDevice.h>
#include "NimBLEEddystoneURL.h"
#include "NimBLEEddystoneTLM.h"
#include "NimBLEBeacon.h"
Nimble ocupa pouca memória se funciona muito bem como MQTT (SSL)

//SSL
WiFiClientSecure wifi; //wifiSecure

Conexão Segura, mas você pode mudar.

          msg.reserve(256);
      
  //Mount Json do Publish 
          String msg = "{";
          msg.reserve(256);
      
          msg.concat("\"MAC\":\"");
          msg.concat(String(WiFi.macAddress()));
          msg.concat("\",");

          msg.concat("\"TMP\":\"");
          msg.concat(String(random(20,40)));
          msg.concat("\",");

          // trim the final comma to ensure valid JSON
          if (msg.endsWith(",")) {
            msg.remove(msg.lastIndexOf(","));
          }
          msg.concat("}");
        
Onde é montado o payload para envio ao servidor MQTT da U-blox

  if(MAC_IN_THE_LIST(Address)) //Index_Queue will point ManufacturerData Queue
        {
          std::string strManufacturerData = advertisedDevice->getManufacturerData();

          //You will access to ManufacturerData using index cManufacturerData[xx]
          uint8_t cManufacturerData[64];
          strManufacturerData.copy((char *)cManufacturerDatastrManufacturerData.length(), 0);
Detecta se o MAC é do EFENTO (Whitelist)

/*
 * Called whenever a payload is received from a subscribed MQTT topic
 */
void mqtt_receive_callback(char* topicbyte* payloadunsigned int length) {
  Serial.print("ThingStream MQTT-receive [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0i < lengthi++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
Inscrição (Subscribe) para um CALLBACK

void check_wifi()
{
Verifica se tem WIFI

void check_mqtt()
{
Verifica se MQTT está no ar

 //if you use SSL
  wifi.setCACert(root_ca);

Poe certificado

void loop() {
/*
Check all connections, wifi, mqtt
*/
  check_wifi();
  check_mqtt();
/*
Alive
*/
  mqtt.loop();
Loop principal

SERVIDOR MQTT

Baseado no pacote (Payload) enviado pelo Beacon da EFENTO

Foi criado um Interpretador da temperatura no servidor via FLOW (JavaScript)


Além de filtrar a temperatura, esta é enviada ao Servidor THINGSPEAK via HTTP para se obter um histórico que pode ser compartilhado.

Basicamente o FLOW acima se inscreve em um Tópico, transforma os dados recebidos para String e então para formato Json do qual são extraídos os Dados. Via HTTP alguns destes dados são enviados para o servidor ThingSpeak.

Sobre a function do FLOW (a primeira)

msg.payload = msg.payload.Gateway_MAC;
return msg;

pega o campo Gateway_MAC do JSON criado e enviado

msg.concat("\"Gateway_MAC\":\"");
          msg.concat(String(WiFi.macAddress()));
          msg.concat("\",");

Sobre o http request no FLOW, veja como foi configurado


Veja publicação

ThingSpeak

Sobre o thingsteam subcribe no FLOW, é o campo que fez subscribe para receber os dados enviados pelo u-blox NINA W106, o qual fez publish.


BLE para MQTT (gateway) com NINA U-BLOX W106

Dados Capturados quando mudam de valor (SCAN)

Dúvidas:

suporte@smartcore.com.br

Referências:

https://thingstream.io/

Sobre a SMARTCORE

A SmartCore fornece módulos para comunicação wireless, biometria, conectividade, rastreamento e automação.
Nosso portifó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