Iot house exhaust air systems controller

First of all I’m going to explain what a house extractor is used for. Because our houses are very well isolated (here in The Netherlands at least) we need some air flow to get rid off the polluted air in house and get some fresh oxygen in. The polluted air consists meanly cooking smell, co2, water vapor from showering and exhaling.
To accomplish this we have an central extractor (mechanische ventilator for our dutch readers).

Normally the extractor is always running on low speed and with the help of a switch in the kitchen you can put it in speed 2 or 3. There’s no way to switch it completely off.
To let my Joshua domotica Iot (internet of things) get control of the extractor I designed this hardware interface. With this interface and the help of Mqtt and Node-red you can control the extractor and still use the original switch in the kitchen to control it.
In the design I used 3 solid state relays so it’s also possible to switch the extractor completely off. With the help of node-red the extractor will be switched off when nobody is at home and the outside temperature is <18°C and the humidity in the bathroom is below 85%. Why? When nobody is at home the central heating system is also switched to a lower temperature with the help of node-red and Toon thermostat. To prevent that the house was cooling down (fast) due the extractor is switched off because this was sucking off the warmth.

The Iot hardware interface was placed near the central heating system and solar collector it was also interesting to add some temperature sensors to measure different parameters and see what’s going on and eventually use this data in Node-red. 5 DS18B20 where added to measure these values.
The rest of the schematic is not that special the part regarding the mains is copied from my Iot light switch with mains input. Added a nice 1.3″ i2c o-led display for some feedback.
As you can see in the schematic and pcb there was also the plan to add 433Mhz receiver and transmitter to control the extractor with “klik aan klik uit”, however I don’t think this option is necessary anymore and leave it for what it is.

This was my first project that I use an ESP32 in the form of an Esp32-wroom32. The software development was all done in the Arduino ide and Sloeber ide.
What a lot of work was this.  Because the Esp32 Arduino core and a lot of used library’s are still buggy I had to do a lot of debugging, scrolling to Github issues, open new issues and finding new library’s. The Esp32 was giving me a panic handler ones in a couple of days, connecting the serial port to a computer to log everything helped me to solve this. Then after updating the Arduino esp32 core to version 1.0.0 there where again new problems. Most of them where related to the dual core’s that where not working probably together with some library’s. Now it looks like it all working stable.

Below a screenshot from Node-red where all the data and functions for the extractor are handled. At the top the sensor data is stored in a SQlite and Influx database. In the middle the Node-red dashboard is build and data query from the SQlite database and shown in charts.
Below that we have the control over the extractor speed with left and right connection to the Mqtt broker.
And in the bottom flow the status of the extractor will be stored in the SQlite database.

Here you can also see what amazing charts you can make with Grafana and Influx database.

Schematic
Finished pcb front
Finished pcb back
Ventilator
Finished mounted
Finished temperature sensors
Grafana charts
Nodered flow
PlayPause
previous arrow
next arrow
 
Schematic
Finished pcb front
Finished pcb back
Ventilator
Finished mounted
Finished temperature sensors
Grafana charts
Nodered flow
previous arrow
next arrow
PZEM-016 Energy Monitor

I bought some (2pieces to start) PZEM-016 energy measure modules from Ali express. You can get them for around $8, not that expensive at all if you take in consideration what this device all can do.
The idea was to measure in my fuse box all the groups (6) separately and push the data to my Mqtt broker over Wifi with the help of an Esp32 and Arduino.

As always the idea was born quick, but to get it all working together was a bit harder, partial because of faulty (not sure about that) manual, other bugs and even something stupid as a bad power supply. But, never give up and it’s now working fine.

The PZEM-014/016 can measure Ac voltage V, current A, active power W, frequency Hz, power factor pf and used energy Wh. You can read all these values from the PZEM with the help of a Rs-485 Half duplex bus. The protocol they used is the well known modbus protocol.

Note: The Rs485 side is safe, but the rest of the PZEM-014/016 has deadly mains voltage. Please be careful.

The schematic is not that special, if you worked before with Rs485 you will recognize the parts.
As Rs485 line driver I used the SN65HVD75 from Texas Instruments, why? Because that’s the one I had on stock. You can use every Rs485 driver for it, however keep in mind that is should work with Vcc of 3.3V provided in the case by the ESP32 Devkit V1.
The data in and out of the Rs4854 driver are connect to the 3e (uart2) hardware port of the Esp32. The data direction (RE_Not/DE) selectors are connected to 2 gpio of the Esp32. You can connected those pins also together to one gpio.

And now something about the RS485 bus.

It’s important that you do this correct to get it all reliable. A good Rs485 has at the begin and the end of the line a termination resistor of 120Ω. So it doesn’t matter if you have 2 or 20 devices on the bus, you always need 2 termination resistors. Why, to get load on the line and prevent reflection of the signal. Google for it.
You see in the schematic and the picture the termination resistor on the Rs485 driver in the breadboard. In the PZEM is a build in termination resistor that can’t be easy disabled, you need to solder it out.
I have to test if a setup with 6 PZEM will work if you leave all the resistors, will come back to that if all my ordered PZEM are arrived and tested.

For the wiring of the Rs485 bus you need to use twisted cable, this will prevent interfering of the Rs485 signal by external sources.  Read this if you want to know more theory.
And then we have the ground wire for the network. it’s not necessary but my experience is that the network preforms not that good with long wires if you don’t use the ground wire. To prevent unwanted ground loop currents I have added a 100Ω resistor.

That was the hardware, let continue with the software. First we have to edit some files.
If you are going to use uart1 or 2 on the esp32 you have to comment out some lines in esp32-hal-uart.c ;

//uart->dev->conf0.txfifo_rst = 1;
//uart->dev->conf0.txfifo_rst = 0;
//uart->dev->conf0.rxfifo_rst = 1;
//uart->dev->conf0.rxfifo_rst = 0;

If you want to use an easy way to switch between PZEM slaves you can edit the ModbusMaster files.
In ModbusMaster.h you add at blanco line 78;

void slaveid(uint8_t);

In ModbusMaster.cpp you add at blanco line 75;

void ModbusMaster::slaveid(uint8_t slave)
  {
    _u8MBSlave = slave;
  }

If you are going to use the ModMaster library attached to this post, there’s no need to change this, it’s already done.

The Arduino code is commented and should be clear enough to get you going.
In the code there’s a function called changeAddress , with this function you can change the slave address of the PZEM. Preferable you do this the best when only one PZEM is connected to the network. If you do something wrong or forgot the old slave address you can use the broadcast address 0xF8 to reset the address to a known number changeAddress(0xF8, 0x01). When using the broadcast address you must have only one slave connected.
The other function resetEnergy will set the energy counter (Wh) back to zero.

One think that’s a bit strange and is (I think) as misprint in the manual and that’s the order of the crc bytes.
The manual talks about Crc-highbyte first and then Crc-lowbyte. However this doesn’t work and with the help of sniffing the data between the PZEM014-Master software provided by Peacefair and the PZEM I found out that the crc order must be Crc-lowbyte first and then the Crc-highbyte. You can calculate very easy modbus crc here online.

Tested it all with an Esp32, but it should also work with an Esp8266 if you use the software uart and probably it works also with the other Arduino boards.

Schematic
Overview test setup
Detail Esp32 connections
Detail Pzm-015 connection
PlayPause
previous arrow
next arrow
 
Schematic
Overview test setup
Detail Esp32 connections
Detail Pzm-015 connection
previous arrow
next arrow

 

Note 27/7/2020: If the communication fails and you are using the PZEM-016 for the first time you must set the slave address first. You can do this with the tool provided by manufacturer or with the help of de Arduino code below by enable the line changeAddress and change it to  changeAddress(0xF8, 0x01)

 

 

Download the Arduino Sketch and the modified ModbusMaster library.

5091 Downloads

/*
  An Arduino Sketch for reading data from a PZEM-014 or PZEM-016, tested with ESP32 DEVKit 1, Arduino 1.8.5
  EvertDekker.com 2018

  If you want to use slaveid function to change the slaveid on the fly, you need to modify the ModbusMaster library (Or get the copy from my website)
  In ModbusMaster.h add at line 78
    void slaveid(uint8_t);
  In ModbusMaster.cpp add at line 75
    void ModbusMaster::slaveid(uint8_t slave)
     {
      _u8MBSlave = slave;
     }
*/
/* If you are using other then uart0 on the ESP32, Comment out in esp32-hal-uart.c the follwing line:
  //uart-&gt;dev-&gt;conf0.txfifo_rst = 1;
  //uart-&gt;dev-&gt;conf0.txfifo_rst = 0;
  //uart-&gt;dev-&gt;conf0.rxfifo_rst = 1;
  //uart-&gt;dev-&gt;conf0.rxfifo_rst = 0;
  Source: https://github.com/4-20ma/ModbusMaster/issues/93
*/

#include &lt;ModbusMaster.h&gt;

HardwareSerial Pzemserial(2);

#define RXD2 16 //Gpio pins Serial2
#define TXD2 17

#define MAX485_DE      19  // We're using a MAX485-compatible RS485 Transceiver. The Data Enable and Receiver Enable pins are hooked up as follows:
#define MAX485_RE_NEG  18

ModbusMaster node;
static uint8_t pzemSlaveAddr = 0x01;

void setup() {
  Pzemserial.begin(9600, SERIAL_8N1, RXD2, TXD2);  // Note the format for setting a serial port is as follows: Serial2.begin(baud-rate, protocol, RX pin, TX pin);
  Serial.begin(9600);
  node.begin(pzemSlaveAddr, Pzemserial);  //Start the Modbusmaster

  pinMode(MAX485_RE_NEG, OUTPUT);  // Setting up the RS485 transceivers
  pinMode(MAX485_DE, OUTPUT);
  digitalWrite(MAX485_RE_NEG, 0);  // Init in receive mode
  digitalWrite(MAX485_DE, 0);

  node.preTransmission(preTransmission);  // Callbacks allow us to configure the RS485 transceiver correctly
  node.postTransmission(postTransmission);

  //changeAddress(0x01, 0x02);
  /* By Uncomment the function in the above line you can change the slave address from one of the nodes, only need to be done ones. Preverable do this only with 1 slave in the network.
     changeAddress(OldAddress, Newaddress)
     If you f*ck it up or don't know the new address anymore, you can use the broadcast address 0XF8 as OldAddress to change the slave address. Use this with one slave ONLY in the network.
     Note: First run of a new PZEM-016 you have to set the slave address first with: changeAddress(0xF8, 0x01)<br /><br /> */

  //resetEnergy(0x01);
  /* By Uncomment the function in the above line you can reset the energy counter (Wh) back to zero from one of the slaves.
  */

  delay(1000);
}

/*
  RegAddr Description                 Resolution
  0x0000  Voltage value               1LSB correspond to 0.1V
  0x0001  Current value low 16 bits   1LSB correspond to 0.001A
  0x0002  Current value high 16 bits
  0x0003  Power value low 16 bits     1LSB correspond to 0.1W
  0x0004  Power value high 16 bits
  0x0005  Energy value low 16 bits    1LSB correspond to 1Wh
  0x0006  Energy value high 16 bits
  0x0007  Frequency value             1LSB correspond to 0.1Hz
  0x0008  Power factor value          1LSB correspond to 0.01
  0x0009  Alarm status  0xFFFF is alarm,0x0000is not alarm
*/

void loop() {
  uint8_t result;

  for (pzemSlaveAddr = 1; pzemSlaveAddr &lt; 3; pzemSlaveAddr++) {  // Loop all the Pzem sensors
    node.slaveid(pzemSlaveAddr);          //Switch to another slave address. NOTE: You can only use this function is you have modified the ModbusMaster library (Or get the copy from my website)
    Serial.print("Pzem Slave ");
    Serial.print(pzemSlaveAddr);
    Serial.print(": ");
   
    result = node.readInputRegisters(0x0000, 9); //read the 9 registers of the PZEM-014 / 016
    if (result == node.ku8MBSuccess)
    {
      uint32_t tempdouble = 0x00000000;

      float voltage = node.getResponseBuffer(0x0000) / 10.0;  //get the 16bit value for the voltage, divide it by 10 and cast in the float variable 

      tempdouble =  (node.getResponseBuffer(0x0002) << 16) + node.getResponseBuffer(0x0001);  // Get the 2 16bits registers and combine them to an unsigned 32bit
      float current = tempdouble / 1000.00;   // Divide the unsigned 32bit by 1000 and put in the current float variable 

      tempdouble =  (node.getResponseBuffer(0x0004) << 16) + node.getResponseBuffer(0x0003);
      float power = tempdouble / 10.0;

      tempdouble =  (node.getResponseBuffer(0x0006) << 16) + node.getResponseBuffer(0x0005);
      float energy = tempdouble;

      float hz = node.getResponseBuffer(0x0007) / 10.0;
      float pf = node.getResponseBuffer(0x0008) / 100.00;

      Serial.print(voltage, 1);  // Print Voltage with 1 decimal
      Serial.print("V   ");

      Serial.print(hz, 1);
      Serial.print("Hz   ");

      Serial.print(current, 3);
      Serial.print("A   ");

      Serial.print(power, 1);
      Serial.print("W  ");

      Serial.print(pf, 2);
      Serial.print("pf   ");

      Serial.print(energy, 0);
      Serial.print("Wh  ");
      Serial.println();
    } else
    {
      Serial.println("Failed to read modbus");
    }
    delay(1000);
  }

}

void preTransmission()  // Put RS485 Transceiver in transmit mode
{
  digitalWrite(MAX485_RE_NEG, 1);
  digitalWrite(MAX485_DE, 1);
  delay(1);
}

void postTransmission()  // Put RS485 Transceiver back in receive mode (default mode)
{
  delay(3);
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
}

void resetEnergy(uint8_t slaveAddr)    //Reset the slave's energy counter
{
  uint16_t u16CRC = 0xFFFF;
  static uint8_t resetCommand = 0x42;
  u16CRC = crc16_update(u16CRC, slaveAddr);
  u16CRC = crc16_update(u16CRC, resetCommand);
  Serial.println("Resetting Energy");
  preTransmission();
  Pzemserial.write(slaveAddr);
  Pzemserial.write(resetCommand);
  Pzemserial.write(lowByte(u16CRC));
  Pzemserial.write(highByte(u16CRC));
  delay(10);
  postTransmission();
  delay(100);
  while (Pzemserial.available()) {         // Prints the response from the Pzem, do something with it if you like
    Serial.print(char(Pzemserial.read()), HEX);
    Serial.print(" ");
  }
}

void changeAddress(uint8_t OldslaveAddr, uint8_t NewslaveAddr)  //Change the slave address of a node
{
  static uint8_t SlaveParameter = 0x06;
  static uint16_t registerAddress = 0x0002; // Register address to be changed
  uint16_t u16CRC = 0xFFFF;
  u16CRC = crc16_update(u16CRC, OldslaveAddr);  // Calculate the crc16 over the 6bytes to be send
  u16CRC = crc16_update(u16CRC, SlaveParameter);
  u16CRC = crc16_update(u16CRC, highByte(registerAddress));
  u16CRC = crc16_update(u16CRC, lowByte(registerAddress));
  u16CRC = crc16_update(u16CRC, highByte(NewslaveAddr));
  u16CRC = crc16_update(u16CRC, lowByte(NewslaveAddr));

  Serial.println("Change Slave Address");
  preTransmission();
  Pzemserial.write(OldslaveAddr);
  Pzemserial.write(SlaveParameter);
  Pzemserial.write(highByte(registerAddress));
  Pzemserial.write(lowByte(registerAddress));
  Pzemserial.write(highByte(NewslaveAddr));
  Pzemserial.write(lowByte(NewslaveAddr));
  Pzemserial.write(lowByte(u16CRC));
  Pzemserial.write(highByte(u16CRC));
  delay(10);
  postTransmission();
  delay(100);
  while (Pzemserial.available()) {   // Prints the response from the Pzem, do something with it if you like
    Serial.print(char(Pzemserial.read()), HEX);
    Serial.print(" ");
  }
}


Iot Blinds Controller

This is a renewed and updated version of my blinds controller. With this controller we can lower and raise the horizontal Luxaflex blinds. This controller was one of the first devices in my Joshua Domotica and the controller for one of the bedrooms needed to be replaced.

Just like my previous Esp8266 and Esp32 domotica projects it will connect through Mosquitto with Node-red. With the help of Node-red we can control the blinds and do almost everything we want with it.

The hart of the design is the Esp8266 and a DRV8800 full bridge motor driver. With the h-bridge we can control the direction that the 24V motor of the blinds will turn and therefore lower or raise the blinds .
With the help of the Enable pin and pwm (pulse wide modulation) it’s possible to let motor run on lower speed to tilt the blinds for example half way the window.
Because the motor requires 24V to operate and the Esp8266 only 3.3V we needed a buck convertor to step down the 24V with a minimum of loss to 3.3V. For this buck convertor I simply bought a ready made one from Ali express.

To suppress the inrush current for the motor some electrolytic capacitors were added to prevent the Esp8266 for resetting due to a dip in the power supply. Note: In the schematic C4 has a value of 1000µF, but this should be just like C1, 220µF. Btw the value of R8 is also incorrect, see below. I really should update my schematics sooner and better.

Because the blinds doesn’t have end switches or other feedback, the only way to determine where they are is the time when the motor is running and in what direction, it’s mostly done by the software. To determine if the motor runs there’s a current sense resistor added to the DRV8800. Over this resistor is an 1.8V zenerdiode mounted to prevent to high voltage on the analog input of the Esp8266.
The 1Ω resistor on the photo is replaced with a 0.5Ω 1210 resistor because the DRV8800 was not working with 1Ω. According to the datasheet this will exceed the maximum sense voltage.
You read those things in the datasheet after you find out it’s not working correct. Due to the smaller resistor the voltage drop is also smaller and it’s not possible anymore to use the full AD range of 1V of the Esp8266. We can still detect if the motor is running or not, but in a next design there should be an op-amp added to amplify the signal.

The pcb was designed very compact. I bought a small box 53x35x23mm and made the pcb to fit exactly in this box. The pcb is mounted on the back of the switch to make it one and save some space for mounting holes. The pcb is 1mm thick to save also some space in the height. Very nice design if I may say so.

 

Schematic
Bare pcb
Finished pcb back
Finished pcb front
Finished pcb front 2
Pcb in enclosure
Finished enclosure front with iluminated button
PlayPause
previous arrow
next arrow
 
Schematic
Bare pcb
Finished pcb back
Finished pcb front
Finished pcb front 2
Pcb in enclosure
Finished enclosure front with iluminated button
previous arrow
next arrow

 

Demo Blinds Controller

 

 

If want to make your own, or use some parts from the design, here are the Altium 16 design files.

1431 Downloads

Iot Co2 Sensor

For measuring the air quality in the living room I was looking for a CO2 sensor to build my own CO2 Iot node. Looking around on the internet I found the MH-Z19 sensor, not that expensive and according to other users this sensor is accurate.

A new project was born. The MH-Z19 was combined with a BME280 and TSL2561 so we can measure lux, temperature, humidity, air pressure and CO2 with one Iot node in the living room and send everything over Mqtt to Node-red for further handling and Sqlite database storage.
The biggest part of the design is a copy of my Iot Bme280 sensor module, this Iot sensor runs 40 days on one 18650 li-ion cell. However the MH-Z19 requires 5V and uses a lot more current so I thought it would be nice to power it from a power bank.
The esp8266 requires 3.3V so the 5V from the powerbank is stepped down with a PAM2305AAB330, very interesting stepping regulator that only requires 2 capacitors and 1 inductor. Little bit hard to get here in Europe, but luckily Arrow had “no shipping charge” days and the parts where quick and cheap here.
The MH-Z19 requires serial communication with the esp8266, the last one has power enough to do this with a software uart. Example for the Arduino IDE to get data out of the MH-Z19 was also quickly found. With all of this together the data was quickly streamed to the Mqtt broker.
The pcb was designed so that it will fit inside a Hammond 1591 case and the pcb is used like a sort of front panel this way. The MH-Z19 is milled out the pcb so you can mount it on the back.

 

Schematic
Pcb bare front
Pcb bare back
Pcb finished front
Pcb finished front detail
Pcb finished back
PlayPause
previous arrow
next arrow
 
Schematic
Pcb bare front
Pcb bare back
Pcb finished front
Pcb finished front detail
Pcb finished back
previous arrow
next arrow

 

If want to make your own, or use some parts from the design, here are the Altium 16 design files.

1628 Downloads

Iot light switch with mains input

IOT light switch with input for switch with mains.

The idea is the same as the famous Sonoff wifi switch but this one has also an input for a 230V~ switch so you can still use your existing light switches.
Most of the light in my house is replaced with Sonoff touch switch , however this is not possible everywhere. For example on the stair case we have what’s called a switching circuit (wisselschakelaar for the dutch readers) and due to the wiring it was not possible to replace them both with a Sonoff touch switch.

To detect the present of the mains on the input I started with a simple schematic based around the H11AA1 which you can find a lot on the net as mains or zero cross detector for Arduino. This worked flawless, see the photo of my experiment. With the CTR (current transfer ratio) of 20% it was necessary to have a current of at least 3.3mA through the led of the opto-coupler. With the used current limiter resistors of 2x33K this will dissipate 0.71W.
Not that much energy to “waste”, however depending on the state of the switching circuit and the Mqtt state this “wasting” can occur 24 hours a day, this can be done better.
Searching for an opto-coupler with a higher CTR should do the trick. The SFH6286-3 has a CTR of 100~320%, a led current of 1mA would be enough with this CTR ratio.  2x110K Resistor will limit the current to 1mA and will dissipate 0.22W.
That’s 0.5W less then before, that will save 4.36kWh per year maximum.

Note: All calculation done in this post where done for 230V~ 50Hz.

The rest of the circuit is not that special. The Esp-01 is programmed with an external programmer and then equiped with a OTA bootloader so programming is only needed to be done once (theoretically). To have enough room for the OTA bootloader and the sketch I have updated the memory from the esp-01 from 1MB to 4MB. That’s easy done with a hot air gun to replace the memory chip. 4Mb spi flash chips can be bought very cheap on ebay.
The solid state relay will consume 6mA at 3.3V, this is broad within the max pin GPIO current of the ESP8266 so no transistor driver is needed.

Because the Esp-01 doesn’t have that much IO pins available the RX and TX can also made be available as IO, to disconnect them from the uart use:

pinMode(1, FUNCTION_3); //GPIO 1 (TX) swap the pin to a GPIO.
pinMode(3, FUNCTION_3); //GPIO 3 (RX) swap the pin to a GPIO.

You can still use the TX/RX pin to program the ESP-01, but using them in your sketch is not possible anymore. To still have the possibility to debug I use a remote debug library to debug over telnet.

Schematic
Bare pcb
Finished pcb
Test setup
Experiment with the optocoupler
Current trought the optocoupler
Scoop 1
Scoop 2
Pcb build in the lamp
PlayPause
previous arrow
next arrow
 
Schematic
Bare pcb
Finished pcb
Test setup
Experiment with the optocoupler
Current trought the optocoupler
Scoop 1
Scoop 2
Pcb build in the lamp
previous arrow
next arrow

 

If you want to make your own, or use parts of the design here are  the Altium Designer 16 files:

1530 Downloads