Andrew Linden
Published © GPL3+

IOT2020 heating controller with NPS support

Use the price of electricity from Nord Pool Spot exchange to decide when to turn on the heating system of a house

IntermediateFull instructions provided8 hours2,531
IOT2020 heating controller with NPS support

Things used in this project

Hardware components

SIMATIC IOT2020
Siemens SIMATIC IOT2020
×1
Wemos D1 Mini
Espressif Wemos D1 Mini
×1
SHT30 shield for D1
×1
5V 1-channel relay module
×1

Software apps and online services

Node-RED
Node-RED
jq
Mosquitto
Homie for ESP8266
Yocto Linux

Story

Read more

Schematics

Original user manual with schematics for the heating system

It is in Estonian language, but the schematics on pages 5 and 6 should be understandable.

IOT2020 connection diagram

Code

Node-red flows

JavaScript
Node-red flows to get NPS data and to control the heating system. To import copy the code and go to your Node-red and then klick: Menu -> Import -> Clipboard
[{"id":"d161f036.d6a668","type":"mraa-gpio-dout","z":"d5adb344.518238","name":"User LED","pin":"13","set":true,"level":"0","x":1017,"y":124,"wires":[]},{"id":"43d23804.b5279","type":"exec","z":"d5adb344.518238","command":"/usr/local/bin/getnps.sh","addpay":false,"append":"","useSpawn":"","timer":"10","name":"getnps.sh","x":376,"y":53.5,"wires":[["96bc646.a1bf718"],[],[]]},{"id":"2d002d9c.8b74da","type":"inject","z":"d5adb344.518238","name":"Get NPS data","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"56 00 * * *","once":false,"x":123,"y":47,"wires":[["43d23804.b5279"]]},{"id":"ecb5db20.705098","type":"debug","z":"d5adb344.518238","name":"debug","active":true,"console":"false","complete":"payload","x":874,"y":303,"wires":[]},{"id":"96bc646.a1bf718","type":"json","z":"d5adb344.518238","name":"","x":536,"y":58,"wires":[["8f327def.a6ab28"]]},{"id":"8f327def.a6ab28","type":"function","z":"d5adb344.518238","name":"Set global nps","func":"global.set(\"nps\",msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":709,"y":56,"wires":[["9997171b.80ca2"]]},{"id":"3f89bfa9.025cb","type":"inject","z":"d5adb344.518238","name":"5min timer","topic":"","payload":"","payloadType":"date","repeat":"300","crontab":"","once":false,"x":349,"y":275,"wires":[["b5b3ea1d.33a13"]]},{"id":"b5b3ea1d.33a13","type":"function","z":"d5adb344.518238","name":"Should we heat?","func":"var maxprice = 35;\nvar settemp = 23;\nvar mintemp = 21;\n\nvar nps = global.get(\"nps\");\nvar temp = global.get(\"temp\");\n\nvar price = nps[new Date().getHours()];\n\nvar tempdiff = settemp - temp;\nvar pricediff = maxprice - price;\n\nvar state = {};\nvar statemsg = {};\nvar debug = {};\n\nstate.topic = 'devices/heatingcontrol/status';\nstatemsg.topic = 'devices/heatingcontrol/statusmsg';\ndebug.topic = 'devices/heatingcontrol/debug';\n\ndebug.payload = { 'temp': temp, 'price': price, 'tempdiff': tempdiff, 'pricediff': pricediff };\n\nif (tempdiff <= 0) {\n    state.payload = 1;\n    statemsg.payload = \"OFF temp higher than settemp\";\n} else {\n    if ((pricediff < 0) && (temp > mintemp)) { \n        state.payload = 1;\n        statemsg.payload = \"OFF temp lower than settemp, but higher than mintemp, price higher than max price\";\n    } else { \n        state.payload = 0;\n        statemsg.payload = \"ON temp lower than settemp\";\n    }\n}\n\nreturn [state,statemsg,debug];\n","outputs":"3","noerr":0,"x":584,"y":237,"wires":[["ecb5db20.705098","35a9df70.384c78","3785f7e6.1a34c","6d29964.5ec5a68"],["3785f7e6.1a34c","ecb5db20.705098"],["3785f7e6.1a34c","ecb5db20.705098"]]},{"id":"9997171b.80ca2","type":"debug","z":"d5adb344.518238","name":"debug","active":true,"console":"false","complete":"payload","x":885,"y":56,"wires":[]},{"id":"78146c79.524014","type":"mqtt in","z":"d5adb344.518238","name":"Temperature","topic":"devices/homieDeviceId/temperature/degrees","qos":"2","broker":"1bd4165e.7fee02","x":118,"y":189,"wires":[["63cbe117.ae4b98"]]},{"id":"63cbe117.ae4b98","type":"function","z":"d5adb344.518238","name":"Set global temp","func":"global.set(\"temp\",msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":338,"y":190,"wires":[["b5b3ea1d.33a13"]]},{"id":"35a9df70.384c78","type":"mraa-gpio-dout","z":"d5adb344.518238","name":"Heater","pin":"6","set":true,"level":"1","x":872,"y":177,"wires":[]},{"id":"3785f7e6.1a34c","type":"mqtt out","z":"d5adb344.518238","name":"","topic":"","qos":"","retain":"","broker":"1bd4165e.7fee02","x":873,"y":236,"wires":[]},{"id":"4ba7ab31.b04e4c","type":"inject","z":"d5adb344.518238","name":"Get NPS data at startup","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"x":152,"y":95,"wires":[["43d23804.b5279"]]},{"id":"2a63978b.a2456","type":"mqtt in","z":"d5adb344.518238","name":"Test Temperature","topic":"devices/testtemp/temperature","qos":"2","broker":"1bd4165e.7fee02","x":123,"y":273,"wires":[[]]},{"id":"6d29964.5ec5a68","type":"function","z":"d5adb344.518238","name":"Invert","func":"msg.payload = 1-msg.payload;\nreturn msg;","outputs":1,"noerr":0,"x":874,"y":124,"wires":[["d161f036.d6a668"]]},{"id":"1bd4165e.7fee02","type":"mqtt-broker","z":"","broker":"192.168.1.10","port":"1883","clientid":"iot2020","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}]

Temperature sensor code

C/C++
// https://github.com/marvinroger/homie-esp8266
#include <Homie.h>

// https://github.com/wemos/WEMOS_SHT3x_Arduino_Library
#include <WEMOS_SHT3X.h>

SHT3X sht30(0x45);
float tempC = 0;
float humidity = 0;

HomieNode temperatureNode("temperature", "temperature");
HomieNode humidityNode("humidity", "humidity");

void setupHandler() {
  temperatureNode.setProperty("unit").send("c");
}

void loopHandler() {
}

void onHomieEvent(const HomieEvent& event) {
  switch(event.type) {
    case HomieEventType::MQTT_CONNECTED:
      Homie.getLogger() << "MQTT connected, sending sensor data..." << endl;
      Homie.getLogger() << "Temperature: " << tempC << " °C" << endl;
      Homie.getLogger() << "Humidity: " << humidity << " %" << endl;
      temperatureNode.setProperty("degrees").send(String(tempC));
      humidityNode.setProperty("percent").send(String(humidity));
      Homie.prepareToSleep();
      break; 
    case HomieEventType::READY_TO_SLEEP:
      Homie.getLogger() << "Ready to sleep" << endl;
      ESP.deepSleep(300 * 1000000);
      break;
  }
}

void setup() {
  // take measurement early, publish when mqtt connected
  sht30.get();
  tempC = sht30.cTemp;
  humidity = sht30.humidity;    

  Serial.begin(115200);
  delay(10);
  Serial << endl << endl;

  Homie_setFirmware("sht30node", "1.0.0");
  Homie.setSetupFunction(setupHandler).setLoopFunction(loopHandler);
  Homie.onEvent(onHomieEvent);

  temperatureNode.advertise("unit");
  temperatureNode.advertise("degrees");
  humidityNode.advertise("percent");

  Homie.disableLedFeedback();
  Homie.setup();
}

void loop() {
  Homie.loop();
}

Credits

Andrew Linden

Andrew Linden

1 project • 1 follower

Comments