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.
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.
Adquirimos então os seguintes componentes
-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)
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 *)cManufacturerData, strManufacturerData.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* 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();
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
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
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:
https://www.u-blox.com/en/docs/UBX-17056481
https://developer.ibm.com/br/articles/iot-mqtt-why-good-for-iot/
https://developer.ibm.com/br/articles/iot-mqtt-why-good-for-iot/
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