Hello,
Here is my Node.js/JavaScript regarding ESP8266/DHT22 temperature logging. This script polls via HTTP the temperature sensors every n minutes and then updates Domoticz. This script is written is a way you don't have to add additional lines of code for each sensor. It is able to manage real physical sensors and virtual ones (used here to compute Degrees Days).
If a sensor fails, it logs the error (like an NAN) at Domoticz Log. If serious error, it set to ON a notification (email/sms) virtual switch within Domoticz (I've chosen for myself a smoked detector type with its reset pushbutton).
You will find also the Arduino sketch I use at ESP8266 side to read its DHT22 sensor and answer to the HTTP requests. Both polling the sensors and Domoticz updating are done using JSON API. The ESP8266 Arduino sketch returns HTTP JSON string message like :
{
"SensorID":"$$YOUR_SENSOR_ID$$",
"Temperature_Celsius":" 20.4",
"Humidity_Percentage":" 80.3",
"HeatIndex_Celsius":" 20.6",
"Sensor_Elapse_Running_Time":"67:00:49"
}
With these two scripts, I'm able to implement rather good and cheap (less than 10 Euros per sensor) temperature sensors in my house.
It can also be used as an example how you can build using JavaScript and Arduino IDE your own end-to-end HTTP communication between ESP8266 and Domoticz. Because the Arduino sketch is Domoticz agnostic (i.e. it doesn't call directly any Domoticz JSON API), I think it's a better architecture for the long term (No need to reflash the ESP8266 in the future if your Domoticz IDX numbers change for example).
Last, I don't try to compete against a Domoticz LUA script in term of number of lines. It was rather for me a question to:
- continue to use Node.js (and its efficient asynchronous functions) I've already used for another feature,
- build a "LEGO architecture".
PS: Items to update in the scripts are tagged $$, for examples :
- Domoticz IDX number corresponding to a sensor tagged [$$DOMOTICZ_PARAMETER],
- "$$YOUR_WIFI_PASSWORD$$" in the Arduino sketch,
- ...
************** Node.js (JavaScript) at Domoticz server side >>>>>>>>>>>>
Code: Select all
// ***** Script to poll ESP8266/DHT22 sensors every n minutes and then update Domoticz *****
var httpDomoticz = require('http');
var JSON_API = {
host: 'localhost', //[$$DOMOTICZ_PARAMETER]
port: 8084, //[$$DOMOTICZ_PARAMETER]
path: '/'
};
const passwordProtectedDevice = '$$YOUR_PASSWORD$$'; //[$$DOMOTICZ_PARAMETER]
const idxHeatingSelector = '17'; //[$$DOMOTICZ_PARAMETER]
const idxTempFailureFlag = '43'; //[$$DOMOTICZ_PARAMETER]
var DomoticzJsonTalk = function( JsonUrl, callBack, objectToCompute ) { // Function to talk to DomoticZ. Do Callback if any
var savedURL=JSON.stringify(JsonUrl);
// console.log("\n** DomoticZ URL request=" + savedURL );
httpDomoticz.get(JsonUrl, function(resp){
resp.on('data', function(ReturnData){
// console.log("\nDomoticZ answer=" + ReturnData);
if( callBack ) callBack( null, JSON.parse(ReturnData), objectToCompute );
});
}).on("error", function(e){
// console.log("\n** Error - " + e.message + "\nCan't reach DomoticZ with URL:" + savedURL );
if( callBack ) callBack( e, null, objectToCompute );
});
}; // function DomoticzJsonTalk( JsonUrl )
var RaisefailureFlag = function( error, data ) {
if( error ) return;
if( data.result[0].Status === "Off" ) {
JSON_API.path = '/json.htm?type=command¶m=switchlight&idx=' + idxTempFailureFlag + '&switchcmd=On&passcode=' + passwordProtectedDevice;
JSON_API.path = JSON_API.path.replace(/ /g,"");
DomoticzJsonTalk( JSON_API );
} // if( GetAlarmIDX.result[0].Status ===
}; // var RaisefailureFlag = fu
var ESP8266DHT22 = require('http');
var ExternalSensor = {
Device: "DHT22",
ID: 'OUTDOOR_ESP8266_DHT22',
SensorUnavailable : 1,
NbReadError : 0,
host: '192.168.1.101',
port: 80,
path: '/',
lastTemp: -150,
lastHumidity: -50,
lastHumStat: '-50',
lastHeatIndex: -150,
idxTempHum: 8, //[$$DOMOTICZ_PARAMETER]
idxHeatIndex: 15 //[$$DOMOTICZ_PARAMETER]
};
var InternalSensor = {
Device: "DHT22",
ID: 'INDOOR_ESP8266_DHT22',
SensorUnavailable : 1,
NbReadError : 0,
host: '192.168.1.102',
port: 80,
path: '/',
lastTemp: -150,
lastHumidity: -50,
lastHumStat: '-50',
lastHeatIndex: -150,
idxTempHum: 9, //[$$DOMOTICZ_PARAMETER]
idxHeatIndex: 14 //[$$DOMOTICZ_PARAMETER]
};
var DegreesDaysSensor = { // Virtual device !
Device: "DEGREESDAYS",
ID: 'DEGREESDAYS',
SensorUnavailable : 1,
lastTemp: -150,
lastHumidity: -50,
lastHumStat: '-50',
idxTempHum: 25, //[$$DOMOTICZ_PARAMETER]
idxHeatIndex: -1, // -1 means no HeatIndex for this sensor
ComputeDegreesDays: function( error, data, DegreesDaysValue ) {
// Compute Degrees-days with BaseTemp=18°C
// TEMP=Degrés-jour = (18°C - ExternalTemp)
// HUM=Degrés-jour_Chauffage_Effectif% = 100 * ( (InternalTemp -1°C (apport occupant) - ExternalTemp) / (18°C - ExternalTemp) )
// Heating level 0/10/20/30 for OFF/HorsGel/Eco/Confort. If OFF or HorsGel, we assume Heating is Power Off (i.e Degrés-jour_Chauffage_Effectif%=0)
DegreesDaysValue.lastTemp=0; DegreesDaysValue.lastHumidity=100; DegreesDaysValue.lastHumStat='0';
DegreesDaysValue.SensorUnavailable = ExternalSensor.SensorUnavailable;
if ( !DegreesDaysValue.SensorUnavailable && ExternalSensor.lastTemp < 18 ) { DegreesDaysValue.lastTemp=(18-ExternalSensor.lastTemp); DegreesDaysValue.lastTemp=DegreesDaysValue.lastTemp.toFixed(1); }
if( error ) return;
if ( data.result[0].Level === 0 || data.result[0].Level === 10 ) DegreesDaysValue.lastHumidity=0;
else if ( !DegreesDaysValue.SensorUnavailable && ExternalSensor.lastTemp < 18 && !InternalSensor.SensorUnavailable ) {
DegreesDaysValue.lastHumidity=100*(InternalSensor.lastTemp-1-ExternalSensor.lastTemp)/(18-ExternalSensor.lastTemp); DegreesDaysValue.lastHumidity = DegreesDaysValue.lastHumidity.toFixed(1);
}
if (DegreesDaysValue.lastHumidity <= 50) DegreesDaysValue.lastHumStat='2'; else if (DegreesDaysValue.lastHumidity > 100) DegreesDaysValue.lastHumStat='1';
} // var ComputeDegreesDays
};
// ** Temp sensors list.
var MyTempSensors = [ExternalSensor, InternalSensor, DegreesDaysSensor];
const PollingTimer = 5; // Poll the sensors every n minutes
const SensorTimeOut = 2 * (60/PollingTimer); // If a Sensor is not available for n hours, raise TEMP SENSOR ALARM in DomoticZ
var lastDaySeen = new Date().getDate();
// ***** INIT
console.log("*** " + new Date() + " - Domoticz iot_ESP8266/DHT22 started ***\n");
// ***** LOOP à la Arduino mode...
setInterval(function(){ // get Temp/Humidity from sensors every n minutes
// *** Print each day, the total number of DHT22 reading errors
var Today = new Date().getDate();
if( Today !== lastDaySeen ) {
console.log("\n*** " + new Date() + " Indoor/Outdoor DHT22 NAN Reading Error=", InternalSensor.NbReadError, "/", ExternalSensor.NbReadError);
InternalSensor.NbReadError = ExternalSensor.NbReadError = 0;
lastDaySeen=Today;
}
// *** READ SENSORS LOOP ***
MyTempSensors.forEach(function(value) {
var ERROR_MESSAGE = "";
if( value.Device === "DEGREESDAYS" ) {
JSON_API.path = '/json.htm?type=devices&rid=' + idxHeatingSelector;
DomoticzJsonTalk( JSON_API, value.ComputeDegreesDays, value );
} else // if( value.Device === "DEGREESDAYS" )
ESP8266DHT22.get(value, function(resp){
resp.on('data', function(JSONSensor) {
// console.log("Sensor=" + JSONSensor);
const CurrentSensorValue = JSON.parse(JSONSensor);
if( CurrentSensorValue.Temperature_Celsius === "DHT22 ERROR" ) { // ESP8266 available but a DHT22 reading error occured
value.SensorUnavailable++; value.NbReadError++;
ERROR_MESSAGE="Error - " + value.ID + " has returned a wrong value (NAN)";
ERROR_MESSAGE=ERROR_MESSAGE.replace(/ /g,"%20");
JSON_API.path = '/json.htm?type=command¶m=addlogmessage&message=' + ERROR_MESSAGE;
DomoticzJsonTalk( JSON_API );
} else {
value.lastTemp=CurrentSensorValue.Temperature_Celsius;
value.lastHumidity=CurrentSensorValue.Humidity_Percentage;
value.lastHeatIndex=CurrentSensorValue.HeatIndex_Celsius;
value.lastHumStat='0';
if (value.lastHumidity <= 30 ) value.lastHumStat='2'
else if (value.lastHumidity >= 70 ) value.lastHumStat='3'
else if (value.lastHumidity >= 45 && value.lastHumidity <= 65 ) value.lastHumStat='1';
value.SensorUnavailable = 0;
} // // if( CurrentSensorValue.Temperature_Celsius == "DHT22 ERROR" )
}); //resp.on('data', function(JSONSensor
}).on("error", function(e){
if( value.SensorUnavailable===1) { // Log this error in DomoticZ only the first time in a row
ERROR_MESSAGE="Error - " + value.ID + " " + e.message;
ERROR_MESSAGE=ERROR_MESSAGE.replace(/ /g,"%20");
JSON_API.path = '/json.htm?type=command¶m=addlogmessage&message=' + ERROR_MESSAGE;
DomoticzJsonTalk( JSON_API );
} // if( value.SensorUnavailable===1
value.SensorUnavailable++; value.NbReadError++;
}); // ESP8266DHT22.get(value, function(resp){
}); // MyTempSensors.forEach(f
// *** DOMOTICZ UPDATE LOOP ***
MyTempSensors.forEach(function(value) {
// Log temperature and humidity
if( !value.SensorUnavailable ) {
JSON_API.path = '/json.htm?type=command¶m=udevice&idx='+ value.idxTempHum +'&nvalue=0&svalue=' + value.lastTemp + ';' + value.lastHumidity + ';' + value.lastHumStat;
JSON_API.path = JSON_API.path.replace(/ /g,"");
DomoticzJsonTalk( JSON_API );
// Log HeatIndex temperature
if( value.idxHeatIndex !== -1 ) {
JSON_API.path = '/json.htm?type=command¶m=udevice&idx='+ value.idxHeatIndex +'&nvalue=0&svalue=' + value.lastHeatIndex;
JSON_API.path = JSON_API.path.replace(/ /g,"");
DomoticzJsonTalk( JSON_API );
} // if( value.idxHeatIndex !== -1 ) {
} // if( !value.SensorUnavailable ) {
}); // MyTempSensors.forEach(f
// *** SENSORS HEALTH CHECK LOOP. If we have a sensor failure for n hours, raise TEMP SENSOR FAILURE in DomoticZ ***
MyTempSensors.forEach(function(value) {
if( value.SensorUnavailable >= SensorTimeOut ) {
JSON_API.path = '/json.htm?type=devices&rid=' + idxTempFailureFlag;
DomoticzJsonTalk( JSON_API, RaisefailureFlag );
} // if( value.SensorUnavaila
}); // TempSensors.forEach(f
}, PollingTimer*60000); // setInterval(function(){ get Temp/Humidity from sensors every n mn
************** Arduino sketch at ESP8266 side >>>>>>>>>>>>
Code: Select all
// ***** Script to read DHTxx sensor and send the measure as HTTP JSON message *****
#include "DHT.h"
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
const char *ssid = "$$YOUR_SSID$$";
const char *password = "$$YOUR_WIFI_PASSWORD$$";
IPAddress ip(192,168,1,101);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);
ESP8266WebServer server ( 80 );
#define SID "$$YOUR_SENSOR_ID$$"
#define JSONstart "{"
#define JSONend "}"
#define DHTPIN 5 // what digital pin we're connected to
//#define DHTTYPE DHT11 // DHT 11
#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
//#define DHTTYPE DHT21 // DHT 21 (AM2301)
DHT dht(DHTPIN, DHTTYPE);
void handleRoot() {
char temp[520];
char str_t[10];
char str_h[10];
char str_hic[10];
int sec = millis() / 1000;
int min = sec / 60;
int hr = min / 60;
// Wait a few seconds between measurements.
delay(2000);
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float h = dht.readHumidity();
// Read temperature as Celsius (the default)
float t = dht.readTemperature();
/* Check if any reads failed and exit early (to try again) */
if ( isnan(h) || isnan(t) ) {
/* Serial.println("Failed to read from DHT sensor!"); */
snprintf ( temp, 520,
"%s\n\
\"SensorID\":\"%s\",\n\
\"Temperature_Celsius\":\"%s\",\n\
\"Humidity_Percentage\":\"%s\",\n\
\"HeatIndex_Celsius\":\"%s\",\n\
\"Sensor_Elapse_Running_Time\":\"%02d:%02d:%02d\"\n\
%s",
JSONstart, SID, "DHT22 ERROR", "DHT22 ERROR", "DHT22 ERROR", hr, min % 60, sec % 60, JSONend
);
} // if ( isnan(h) || isnan(t) ) {
else {
/* Compute heat index in Celsius (isFahreheit = false) */
float hic = dht.computeHeatIndex(t, h, false);
dtostrf(t, 5, 1, str_t);
dtostrf(h, 5, 1, str_h);
dtostrf(hic, 5, 1, str_hic);
snprintf ( temp, 520,
"%s\n\
\"SensorID\":\"%s\",\n\
\"Temperature_Celsius\":\"%s\",\n\
\"Humidity_Percentage\":\"%s\",\n\
\"HeatIndex_Celsius\":\"%s\",\n\
\"Sensor_Elapse_Running_Time\":\"%02d:%02d:%02d\"\n\
%s",
JSONstart, SID, str_t, str_h, str_hic, hr, min % 60, sec % 60, JSONend
);
} /* else if if (isnan(h) || isnan(t) || isnan(f)) */
server.send ( 200, "application/json", temp );
} /* void handleRoot() { */
void handleNotFound() {
String message = "URL Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for ( uint8_t i = 0; i < server.args(); i++ ) {
message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
}
server.send ( 404, "text/plain", message );
} /* handleNotFound() { */
void setup ( void ) {
dht.begin();
Serial.begin ( 57600 );
WiFi.begin ( ssid, password );
WiFi.config(ip, gateway, subnet);
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() );
if ( MDNS.begin ( "esp8266" ) ) {
Serial.println ( "MDNS responder started" );
}
server.on ( "/", handleRoot );
server.on ( "/inline", []() {
server.send ( 200, "text/plain", "$$YOUR_SENSOR_ID$$.Temp/RESTAPI Reserved Use Mode" );
} );
server.onNotFound ( handleNotFound );
server.begin();
Serial.println ( "HTTP server started" );
}
void loop ( void ) {
server.handleClient();
}

- My_Switches.JPG (355.25 KiB) Viewed 8645 times