Node-red a data z ADC

Project owner: Cyberian
Interested:
Related:
License: Uveďte původ-Zachovejte licenci CC BY-SA

Vzhledem k netušené náročnosti to trochu potrvá, dokud to nebude dostatečně “kyber”. Nicméně tvrdě na tom dělám ;-) Edit: Už je to dostatečně “kyber”. Celý projekt je hotový a odladěný.

Mým cílem je měřit pravidelně napětí baterie, data vizualizovat a mít je na dosah v případě potřeby, bez nutnosti chodit k zařízení. Dále zaslání emailu v případě, kdy se baterie dostane pod 11 V. Hotové zařízení se namontuje na pojízdnou platformu, na které sedí terapeut s dítětem, případně jen dítě, když je šikovnější a je tam jedno tlačítko, které po stisku platformu rozjede a tato sleduje černou izolační pásku nalepenou na podlaze. Předlohou k řešení se stala architektura internetu věcí, kterou využíváme v SensoricNetu, který je nejrozsáhlejším projektem Labky a ve kterém stále pokračujeme. Data z ESP8266 budeme posílat skrz Wifi připojení protokolem MQTT. Podrobnosti o protokolu, viz poslední odstavec s referencemi. Tímto protokolem se připojíme k MQTT brokerovi, který schromažďuje data ze zařízení publikovaná pod určitým tématem (topic). Jiná zařízení nebo aplikace naopak mohou data z konkrétního tématu přijímat. Podle obrázku vidíte, že odesílateli se říká publisher a tomu kdo přijímá subscriber. Samozřejmě můžete být obojím.

Někteří z Vás si položí otázku, jak takovou strukturu zařízení uřídit, zvlášť pokud takových zařízení máme desítky či stovky. Chce to pro naši IoT síť nějakého orchestrátora, dirigenta. Tím je právě nástroj, který se jmenuje Node-red. Node-red je visuální programovací nástroj, který propojuje hardware, API a služby.

Čeká nás čeká a nemine: Instalace Mosquitto brokera a klienta, instalace Node-redu a kněmu několika modulů. Rožšíření kódu programu a práce v Node-red editoru.

  • ip66 waterproof case
  • LVDO stabilizátor na 3,3V - LF33CV
  • elektrolytický kapacitor 2.2uF, 200V
  • trimr 20K
  • jumper
  • svorkovnice
  • pájitelné pole
  • menší chladič

Volba pevného stabilizátoru nebyla úplně nejštastnější. Později jsem zjistil, že je lepší step down měnič. Stabilizátor se při tomto rozdílu napětí zahřívá cca na 50°C. Z toho a i znaučného důvodu jsem zvolil jako řešení deep sleep mód, i když mi to v ten moment přišlo jako černá magie :). V momentu kdy ESP8266 spí se během chvilky stabilizátor ochladí. I tak je tam pro jistotu chladič, kdyby se běh programu zasekl a wifi byla stále aktivní. Chladič teplo razantně snížil…

Jak to funguje?

Deep sleep mód vypíná celé CPU až na čítač, který při dosažení určitě hodnoty pošle log 0 z pinu D0, který by měl být připojen na pin RST. Pokud to takto zapojíte, počítejte s tím, že při flashování je nutné to rozpojit. Proto tam mám jumper.

Kód

Samotné zavolání deep sleepu je jednoduché. Je potřeba nastavit pin D0/wake na WAKEUP_PULLUP.

pinMode(D0, WAKEUP_PULLUP);

Pozor u node mcu je built in LED, ať se vám to pak nekříží a nespadnete do věčné instantní restartovací smyčky. Po nastavení pinu už jen zavoláte v kódu spánek tam, kde to potřebujete

ESP.deepSleep(120000000, WAKE_NO_RFCAL);

Prodleva je v tomto případě 120 s.

Problémy

Problémy, které deep sleep mód přináší jsou následující.

  • Při flashování je nutno rozpojit D0 a RST.
  • Druhým je připojení k wifi. Standardně je ESP schopné se znovupřipojit již za cca 110 ms, avšak dociluje toho tím, že si ukládá do flash paměti SSID, kanál na kterém bylo naposledy připojeno, IP adresu a bránu. Při každém deep sleepu by se tyto hodnoty zapsali znovu do flash paměti a vzhledem k tomu, že flash pamět vydrží kolem 1000 přepisů (pokud se nemýlím), tak tento stav nechcete. Proto je v kódu toto chování zakázáno:
WiFi.persistent( false );
  • Vše však něco stojí. Mám sice pevně danou IP, masku, bránu a režim stanice, ale daní za toto je potřeba znovu proskenovat dostupné kanály. Pokud máte v síti jen jednu wifi a zařízení se nepohybuje, pak s tím problém není :) a však v mém případě pojízdná plošina jezdí po chodbách. Připojení k wifi síti tedy trvá zpravidla 2870 ms.
  • Dále je problém na některých deskách s probuzením Wifi po deepsleep modu a proto je v kódu force příkaz, který si to preventivně vynucuje.
  • Další problém je, že nemusíte stihnout odeslat data, což se v mém případě stalo. Čekal jsem, že se to bude dít jen u instantního deep sleep módu, ale děje se to i u toho klasického. Hledal jsem řešení asi týden, nakonec to vyřešil kód těsně před sleepem, který data čte, dokud to jde.
  • Procesor se po tomto druhu spánku neprobouzí, ale restartuje. Přijdete tedy o hodnoty všech proměnných, pokud si je neuložíte někam externě do EEPROM, nebo interně, ale opět počítejte s tím, že opotřebováváte EEPROM. Do budoucna se chystám využit RTC hodiny, které mají 32K paměti :)

Začneme instalací Mosquitto brokera1. Jako systém jsem si zvolil Debian 9, na který jsem zvyklý. Příkazy jsou tedy uzpůsobeny pro něj:

Provedeme preventivní update a upgrade správce balíčků, následnou instalaci Mosquitto a Mosquiito klienta pro možnost posílat nebo odebírat zprávy z MQTT brokera:

apt-get update
apt-get upgrade
apt-get install mosquitto-dev mosquitto-clients

Před vlastní instalací node-red, je nutno nainstalovat nodejs. Zvolil jsem aktuální dostupnou verzi nodejs a jsem připraven čelit případným bugům:

- toto jsem bohužel zakřikl a verze z https://deb.nodesource.com/setup_12.x se ukázala jako příliš experimentální a selhala při kompilaci

info o bugu, viz https://github.com/nodejs/nan/issues/849

Přejdeme na verzi 11 než komunita verzi 12 opraví.Taktéž nainstalujeme node-red dashboard

apt get-install g++
curl -sL https://deb.nodesource.com/setup_11.x | bash -
apt-get install -y nodejs
npm install -g --unsafe-perm node-red
npm install node-red-dashboard

Nastavíme si Node-Red tak, aby běžel aut. po startu jako služba.

sudo wget -O /etc/systemd/system/Node-Red.service https://raw.githubusercontent.com/ktndesai/Node-RED/master/Node-Red.service

Je potřebea zkontrolovat skript pro službu, zda ukazuje správně na nodejs a node-red

Skript /etc/systemd/system/Node-Red.service obsahuje následující dva řádky, kde jsem si upravil správně cesty podle mé skutečnosti

ExecStart=/usr/bin/node $NODE_OPTIONS /usr/lib/node_modules/node-red/red.js $NODE_RED_OPTIONS
WorkingDirectory=/root/.node-red/

Po restartu by jste měli nalézt přes příkaz

systemctl status Node-Red

svoji službu aut. spuštěnou

Nyní máme připravený node-red s dashboardem. Do node-red editoru se lze dostat přes http://localhost:1880. Na dashboard proklikem z editoru přes tab dashboard a následně ikonou šipky na úhlopříčku nebo přes http://localhost:1880/ui

Měli by jsme si node-red editor zabezpečit.

instalace bcryptu modulu a modulu pro generování hashů hesel:

npm install -g yarn
yarn add bcrypt
yarn add node-red-admin

Při instalaci bcryptu došlo taktéž k bugu, proto jsem zvolil další pomocný modul pro instalace yarn.

Hash pro vlastní libovolné heslo získame pomocí modulu node-red-admin:

node-red-admin hash-pw

Nastavení přístupových účtů pro node-red lze provést odkomentováním následující sekce v /root/.npm/node-red/settings.js a vložením vygenerovaného hashe:

adminAuth: {
    type: "credentials",
    users: [
        {
            username: "admin",
            password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.",
            permissions: "*"
        },
        {
            username: "george",
            password: "$2b$08$wuAqPiKJlVN27eF5qJp.RuQYuy6ZYONW7a/UWYxDTtwKFCdB8F19y",
            permissions: "read"
        }
    ]
}

Následovat budou drobné úpravy obvodu, vynechání displeje, připájení na pájitelné pole a přimontování do krabice. Pak se vrhneme na návrh v node-redu.

Původní kód najdete v předchozím projektu: Měření napětí pomocí ADC

Úpravy kódu:

  • přidání kódu pro zasílání zpráv s napětím pomocí mqtt protokolu na mqtt brokera
  • brokerovi odešleme pro účeli statistiky místní sítě i dobu připojení na Wifi AP a dobu spojení s brokerem
  • odebrání kódu pro OLED displej
  • deep sleep mód

Odesílání dat do MQTT brokera se děje prostřednictvím knihovny PubSubClient, pomocí funkce PubSubClient::publish(const char* topic, const char* payload). Podrobnosti o dalších funkcích se lze dočíst na webu autora Nick O'Leary

// ADC_MODE(ADC_VCC);
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <PubSubClient.h>
#undef  MQTT_MAX_PACKET_SIZE // un-define max packet size
#define MQTT_MAX_PACKET_SIZE 500  // fix for MQTT client dropping messages over 128B

//const char* ssid = "........";
//const char* password = "........";
const char* mqtt_server = "192.168.16.3";

WiFiClient espClient;
PubSubClient mqttClient(espClient);
char msg[10];
char pingTime[10];
unsigned long wifiTime;
unsigned long pubTime; 
bool pingTimeout=false;
//unsigned long pingTime2;


void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i=0;i<length;i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

void reconnect() {
  // Loop until we're reconnected
  unsigned long start = micros();
  while (!mqttClient.connected()) {
    Serial.println(F("Attempting MQTT connection..."));
    // Create a random client ID
    String clientId = "ESP8266Client-AkkaMonitor";
    //clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (mqttClient.connect(clientId.c_str())) {
      Serial.println(F("MQTT connected"));
      // Once connected, publish an announcement...
      mqttClient.publish("AkkaMonitor/reconnect", "reconnect");
      
      // ... and resubscribe
      mqttClient.subscribe("AkkaMonitor/inTopic");
    } else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
    start = micros()-start;
    pubTime = start/1000;
  Serial.print("MQTT reconnect trval: ");
  Serial.println(pubTime);
}
 


void setup_wifi(){
  unsigned long start = micros();
  IPAddress ip( 192, 192, 192, 192 );
  IPAddress gateway( 192, 192, 192, 1 );
  IPAddress subnet( 255, 255, 255, 0 );
  WiFi.forceSleepWake();
  delay( 1 );
  WiFi.persistent( false ); // vypiname ukladani nastaveni spojeni do flash; zdůvodu opotřebení
  WiFi.mode( WIFI_STA ); // rezim stanice (klient)
  WiFi.config( ip, gateway, subnet );
  WiFi.begin("NejakeSSID", "NejakeHeslo"); //SSID, heslo
  Serial.println();
  Serial.print(F("Pripojeni trvalo (ms): "));
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(20);
    //Serial.print(WiFi.status());
  }
  start = micros()-start;
  wifiTime = start/1000;
  Serial.print(wifiTime);
  Serial.println();
  //Serial.print("Connected, IP address: ");
  //Serial.println(WiFi.localIP());
  WiFi.printDiag(Serial); 
}


void setup() 
{ 
  //pinMode(BUILTIN_LED, OUTPUT);     // pro MQTT PubSub: Initialize the BUILTIN_LED pin as an output
  //digitalWrite(BUILTIN_LED, LOW);   // builtin led potlačena, jelikož je na ní wake up signál z deep sleepu
  pinMode(D0, WAKEUP_PULLUP); // nastavujeme D0 na wakeup, jumper musi byt spojen, aby se signál dostal na RST
  Serial.begin(115200);
  Wire.begin();
  setup_wifi();
  

  //MQTT
  mqttClient.setServer(mqtt_server, 1883);
  mqttClient.setCallback(callback);
  
   
}

void loop() 
{ 
  float napeti=0;
  mqttClient.loop(); //mqtt member, nutno volat každou iteraci smyčky 
  if (!mqttClient.connected()) {
    reconnect();
  }
  
  int adcread=(analogRead(0));
  napeti= (float)(map(adcread, 0, 1024, 0, 14490))/1000;
  Serial.print(F("ADC A0: "));Serial.print(adcread);Serial.print(F(" DEC, "));
  Serial.print(F("Napětí: "));Serial.print(napeti);Serial.println(F(" V"));
 
  //snprintf(str, sizeof(str), "%.2f", f);
  snprintf(msg, 6, "%.2f", napeti);
  
  Serial.print("napeti: "); Serial.println(napeti);
  Serial.print("wifiTimeMilis: "); Serial.println(wifiTime);
  Serial.print("pubTime: "); Serial.println(pubTime);
  
  mqttClient.publish("AkkaMonitor/napeti", msg); //MQTT posílá zprávu pod topicem AkkaMonitor/napeti
  mqttClient.publish("AkkaMonitor/wifiTimeMilis", ltoa(wifiTime, msg, 10)); //Doba, kterou trva pripojeni k Wifi AP
  mqttClient.publish("AkkaMonitor/pubTime", ltoa(pubTime, msg, 10)); // Doba, kterou trvá připojení k mqtt brokeru 

  
  //Read the reply from the server. Zajisti, že se data pres wifi stihnou deslat než dojde k deep sleepu
while (espClient.connected()) 
  if (espClient.available())
    espClient.read();
  //mqttClient.disconnect();
  yield();
  ESP.deepSleep(120000000, WAKE_NO_RFCAL); // 120 sec.   
  
  }

Takto by se vám měl po přihlášení, zobrazit editor node-redu sprázdným pracovním prostorem. Případně bez přihlášení, pokud jste zabezpečení nepotřebovali.

Nejprve bude potřeba vytvořit ve sloupci vpravo layout dashboardu. Vytvoříte si vlastní uzel, kde vyplníte Name a pod ním skupinu. Podle toho jak budete přidávat různé grafické prvky, tak bude pak možno na tomto místě měnit pořadí. V tabu Site si lze nastavit rozměry základní dlaždice widgetu. V mém případě jsem nastavil 64×64 pro 1×1.

Pro každou změnu v node-red editou, u které chcete, aby se projevila, je potřeba kliknout na tlačítko “Deploy”

Nyní k popisu uzlů, které jsem použil. Rozdělil jsem si to vizuálně na 3 svislé části.

První část

První jsou vstupy AkkaMonitor/napeti, wifiTimeMilis, pubTime. Tyto vstupy beru jako mqtt subscriber ze serveru, který je jako local MQTT broker. Do názvu jsem dal IP adresu serveru a standardní port 1883. Topic AkkaMonitor/napeti, který odpovídá posílanému topicu v kódu programu. Pozor na lomítka. Pokud napíšete /Akkamonitor/napeti, tak se jedná o jiný topic.

Druhá část

“msg.payload”, jde o prvek s názve debug, který ve svých vlastnostech vypisuje hodnoty ze subscribera. Dále je tu žlutý switch, který pouští hodnoty, až když je splněna podmínka. Chová se jako switch-case v C++ Nastavenu má podmínku pro napětí “pokud je hodnota 3-11” Na 10,5 V začíná pro baterii kritický stav, kdy se může začít vybíjet i bez odběru a může dojít k trvalé degradaci kapacity

Třetí část

Výstupy do grafických prvků. “VU” metry, grafy a odeslání informací na mail. V defaultním nastavení to pošle jako předmět emailu topic a jako obsah emailu hodnotu napětí.

Takto to vypadá funkční ve finále

Instalace byla poměrně úsměvná. Zjistil jsem že deska není dřevěná ale cca 4-5mm plast, do kterého závit nevyříznu, jelikož závit potřebuje ještě výběh pro vrchol vrtáku i závitníku :) Posloužila však hliníková pásovina, na které jsou upevněná kolečka. Kabely jsou připájeny přímo na piny baterie pro fastony. Monitoruje se jen jedna baterie, jelikož se nabíjí v sérii. Pro jistotu jsem měřil i to zda nedochází k napěťovým špičkám při spuštění serv, ale zjevně je to obsloužené a nedochází k nim.

A teď už jen pár fotek:

  • project/esp8266_adc_grafana.txt
  • Last modified: 2019/07/10 13:16
  • by cyberian