Zápisník experimentátora
Hierarchy: Node.js
V dnešnom článku prepojíme dve úžasné technológie. Node-RED a jeho toky nám budú slúžiť ako server a ESP8266 budeme používať ako HTTP klienta, ktorý bude na server odosielať nameranú teplotu. V tomto článku využijeme vedomosti z dvoch predchádzajúcich článkov. Arduino nahradíme za ESP8266 a meranú teplotu nebudeme prenášať cez sériový port, ale pomocou protokolu HTTP. V Node-RED budeme používať HTTP server, na ktorý bude ESP8266 odosielať nameranú teplotu. Poďme na to.

V týchto článkoch nájdete informácie, ktoré môžu byť pre vás užitočné. V článkoch som popisoval Node-RED, ESP8266 a meranie teploty pomocou senzora DS18B20.
Budeme potrebovať tieto súčiastky:
Súčiastky sú zapojené podľa tejto schémy. Na pine D6 musí byť zapojený pull-up rezistor. Senzor teploty je zapojený normálne na GND a 3,3 V. Ak máte originálne senzory teploty, môžete ho zapojiť aj v parazitickom napájaní. Môžete použiť ľubovoľné množstvo senzorov. Program v ESP8266 aj v Node-RED je na to pripravený.

Node-RED je tokovo orientované prostredie, v ktorom vytvárate toky dát medzi jednotlivými uzlami. Takéto prostredie sa hodí na spracovanie údajov, ktoré vznikajú pri meraní teploty. Na jednom mieste, alebo aj na viacerých miestach sa odmerá teplota a tá sa následne niekde musí spracovať. Node-RED na to poskytuje ideálne prostredie, pretože v ňom môžete ľahko určiť, odkiaľ sa dáta berú a kam sa majú následne odoslať. To všetko robíte v grafickom prostredí, kde si pridávate na plochu uzly, ktoré prepájate pomocou prepojovacích čiar. Čiary predstavujú samotný tok dát z jedného uzla do druhého.
Node-RED nájdete na Internete na stránke https://nodered.org/. Na stránke nájdete popis, ako vývojové prostredie nainštalovať, ale niektoré podrobnosti tam dostatočne nezdôrazňujú. Dôležitá informácia je to, že musíte mať oprávnenia administrátora. Tie získate na Linuxe pomocou príkazu sudo a vo Windows tak, že spustíte konzolu v režime administrátora. Podrobnosti o inštalácii nájdete v článku, v ktorom popisujem proces inštalácie.

V článku o ladení HTTP endpointu som opísal, ako sa dá odladiť vstupný bod v toku s názvom HTTP ESP8266 Request. Opísal som formát vstupných údajov a čo sa s údajmi ďalej robí. Základnou myšlienkou boto to, že ESP8266 meria teplotu pomocou ľubovoľného množstva senzorov DS18B20. Každé jedno meranie môže mať preto 0-N hodnôt. Preto majú vstupné údaje formát poľa objektov s dvomi parametrami.
[
{
"id": "0011223344556677",
"value": 25
},
{
"id": "0011223344556688",
"value": 26
}
]
V článku som opísal, ako sa tieto údaje validujú a ako sa transformujú z poľa údajov na tok jednotlivých údajov. To má za úlohu uzol splitArray. Ten má jeden vstupný bod a tri výstupné body. Vstupom je pole nameraných teplot a výstupom môžu byť tri rôzne body. Uzol validuje vstup a podľa toho sa rozhodne, či namerané teploty akceptuje alebo odmietne.
Takto vyzerá zdrojový kód uzla splitArray v javascripte. Jednotlivé použité príkazy sú prispôsobené toku, ktorý Node-RED poskytuje. To znamená, že názvy niektorých premenných sú pevne určené. Je to napríklad premenná msg, ktorú Node-RED používa ako vstupnú premennú do vašej funkcie. Prvý príkaz overuje, či je msg.payload pole. Ak nie je, je odoslaná prvá odpoveď s chybou. Pretože odpoveď môžeme nasmerovať do niektorého z troch výstupov, musíme ju zapísať ako pole odpovedí. V tomto prípade odpoveď zasielame do prvého výstupu, preto je formát správy v tvare poľa [msg, null, null].
Následne môžeme skontrolovať jednotlivé prvky poľa. To robíme pomocou overenia existencie vyžadovaných parametrov a pomocou regulérneho výrazu, ktorým overíme, že je obsah parametra id dlhý 16 znakov a predstavuje ID senzora teploty DS18B20. Ak je všetko v poriadku, vytvoríme novú správu, ktorú nasmerujeme do výstupu číslo 3.
V tejto chvíli ešte nebola odoslaná odpoveď do ESP8266, ktoré na ňu v tej chvíli stále čaká. Tá sa odošle až po spracovaní všetkých prvkov poľa a v odpovedi odošleme počet spracovaných záznamov pomocou výstupu 2. V tej chvíli má ESP8266 možnosť spracovať odpoveď zo servera.
if(Array.isArray(msg.payload)===false) {
msg.payload = { error: 'Bad request'};
return [msg, null, null];
}
const regex = /[0-9A-Fa-f]{16}/g;
for(let element in msg.payload) {
let e = msg.payload[element];
if(e.id===undefined || e.value===undefined) {
msg.payload = { error: 'Bad element parameters'};
return [msg, null, null];
}
if(!e.id.match(regex)) {
msg.payload = { error: 'Bad element id'};
return [msg, null, null];
}
node.send([null, null, {"payload": e}]);
}
msg.payload = { ok: 'Handled: '+msg.payload.length};
return [null, msg, null];
Teraz nasleduje zdrojový kód programu pre ESP8266. Aby tento program nebol len jednoduchou variáciu na meranie teploty, naprogramoval som v ňom niekoľko ukážok rôznych techník, ktoré môžu byť pre vás užitočné.
RedConfig a obsahuje interval, ako často sa má merať teplota a URL servera, kam sa teplota odosiela. Samotná premenná, ktorá obsahuje konfiguráciu sa volá cfg. Implicitné hodnoty ľahko vyplníme použitím konštruktora, čo je iba metóda v štruktúre s rovnakým názvom.loadConfig.loadConfig sa nachádza aj zdrojový kód, ktorý pomocou tejto knižnice rozparsuje konfiguráciu v JSON súbore. Autor knižnice odviedol dobrú prácu a parsovanie pomocou knižnice je smiešne jednoduché. Pri konverzii niektorých hodnôt na String je potrebné kompilátou trošku pomôcť a pretypovať hodnotu parametra na const char*. Ostatné konverzie knižnica zvládne sama. Zdrojový kód je pre verziu 6. V starších verziách knižnice vyzerá zdrojový kód mierne odlišne.sendData. Opäť je na to treba iba niekoľko riadkov zdrojového kódu. A to napriek tomu, že vytvárame pole objektov. Pre každý senzor vytvoríme jeden objekt. Všimnite si, že na vytvorenie parametra id použijeme funkciu sprintf. Takéto vytvorenie reťazca znakov vo svete Arduina vidíte zriedkavo. Väčšinou sa programátori uchyľujú k šialeným konštrukciam namiesto toho, aby poriadne využili funkcie, ktoré sú na to určené.
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <FS.h>
#include "arduino_secret.h"
#include <OneWire.h>
#include <DallasTemperature.h>
#include <ArduinoJson.h>
#define ONE_WIRE_BUS D6
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
struct RedConfig {
long timeout;
unsigned long previous;
String server;
RedConfig()
: timeout(30000),
previous(0),
server("http://192.168.0.150:1880/sendtemperature")
{}
};
RedConfig cfg;
void loadConfig() {
File f = SPIFFS.open("/config.json", "r");
if (!f) {
Serial.println("File '/config.json' open failed.");
return;
}
StaticJsonDocument<256> doc;
deserializeJson(doc, f);
cfg.server = (const char*)doc["server"];
cfg.timeout = doc["timeout"];
f.close();
Serial.printf("New config server: %s\n", cfg.server.c_str());
Serial.printf("New config timeout: %d\n", cfg.timeout);
}
void sendData() {
if (WiFi.status() == WL_CONNECTED) {
int numberOfDevices = sensors.getDeviceCount();
if (numberOfDevices > 0) {
sensors.requestTemperatures();
StaticJsonDocument<1024> array;
for (int i = 0; i < numberOfDevices; i++) {
DeviceAddress tda;
sensors.getAddress(tda, i);
char id[17];
sprintf(id, "%02X%02X%02X%02X%02X%02X%02X%02X",
tda[0], tda[1], tda[2], tda[3],
tda[4], tda[5], tda[6], tda[7]);
float t = sensors.getTempCByIndex(0);
JsonObject nested = array.createNestedObject();
nested["id"] = id;
nested["value"] = t;
}
serializeJson(array, Serial);
Serial.println("");
HTTPClient http;
bool connected = http.begin(cfg.server);
if (connected) {
http.addHeader("Content-Type", "application/json");
String output;
serializeJson(array, output);
int httpCode = http.POST(output);
Serial.print("http.POST=");
Serial.print(httpCode);
if (httpCode < 0) {
Serial.print(" ");
Serial.print(http.errorToString(httpCode));
}
Serial.println("");
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_BAD_REQUEST) {
String payload = http.getString();
Serial.println(payload);
}
}
else
Serial.println("http.begin error");
}
}
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
sensors.begin();
SPIFFS.begin();
loadConfig();
}
void loop() {
unsigned long current = millis();
if (current - cfg.previous >= cfg.timeout) {
cfg.previous = current;
digitalWrite(LED_BUILTIN, LOW);
sendData();
digitalWrite(LED_BUILTIN, HIGH);
}
}
Súbor config.json je nutné nahrať do SPIFFS pomocou príkazu menu Tools/ESP8266 Sketch Data Upload.
{
"server": "http://192.168.0.150:1880/sendtemperature",
"timeout": 20000
}
Zdrojový kód sa nachádza na serveri GitHub.
07.09.2019