SurtrTech
Published © GPL3+

Measure AC voltage with ZMPT101B and ESP8266 12E

Any AC voltage up to 250VAC 50/60Hz, then send values to Adafruit IO MQTT, and a local App Inventor app...

BeginnerFull instructions provided2 hours37,265
Measure AC voltage with ZMPT101B and ESP8266 12E

Things used in this project

Hardware components

NodeMCU ESP8266 Breakout Board
NodeMCU ESP8266 Breakout Board
×1
ZMPT101B 250 VAC Voltage sensor
×1

Software apps and online services

Arduino IDE
Arduino IDE
Adafruit IO
MIT App Inventor 2
MIT App Inventor 2

Story

Read more

Schematics

Wiring I used

You can measure what you want

Code

Code snippet #1

Plain text
void setup() {
Serial.begin(115200);
pinMode(A0,INPUT);
}
void loop() {
Serial.println(analogRead(A0));
//delay(100);
}

ESP8266_ZMPT101B_Base.ino

Arduino
The base code, can be adapted to other projects
/* This code works with ESP8266 12E or Arduino and ZMPT101B AC voltage sensor up to 250 VAC 50/60Hz
 * It permits the measure of True RMS value of any AC signal, not only sinewave
 * The code uses the Sigma "Standard deviation" method and displays the value every "printPeriod"
 * check www.SurtrTech.com for more details
 */

#include <Filters.h>                            //Library to use

#define ZMPT101B A0                            //Analog input

float testFrequency = 50;                     // test signal frequency (Hz)
float windowLength = 100/testFrequency;       // how long to average the signal, for statistist, changing this can have drastic effect
                                              // Test as you need

int RawValue = 0;     
float Volts_TRMS;     // estimated actual voltage in Volts

float intercept = 0;  // to be adjusted based on calibration testin
float slope = 1;      

/* How to get the intercept and slope? First keep them like above, intercept=0 and slope=1, 
 * also below keep displaying Calibrated and non calibrated values to help you in this process.
 * Put the AC input as 0 Volts, upload the code and check the serial monitor, normally you should have 0
 * if you see another value and it is stable then the intercept will be the opposite of that value
 * Example you upload first time and then you see a stable 1.65V so the intercept will be -1.65
 * To set the slope now you need to put the voltage at something higher than 0, and measure that using your reference TRMS multimeter
 * upload the new code with the new intercept and check the value displayed as calibrated values
 * Slope = (Measured values by multimeter)/(Measured values by the code)
 * Place your new slope and reupload the code, if you have problems with calibration try to adjust them both
 * or add a new line to calibrate further
 * Slope and intercept have nothing to do with the TRMS calculation, it's just an adjustement of the line of solutions
 */


unsigned long printPeriod = 1000; //Measuring frequency, every 1s, can be changed
unsigned long previousMillis = 0;

RunningStatistics inputStats; //This class collects the value so we can apply some functions

void setup() {
  Serial.begin(115200);    // start the serial port
  Serial.println("Serial started");
  inputStats.setWindowSecs( windowLength );
}

void loop() {
                 
     ReadVoltage();  //The only function I'm running, be careful when using with this kind of boards
                     //Do not use very long delays, or endless loops inside the loop
    
       
}

float ReadVoltage(){
    RawValue = analogRead(ZMPT101B);  // read the analog in value:
    inputStats.input(RawValue);       // log to Stats function
        
    if((unsigned long)(millis() - previousMillis) >= printPeriod) { //We calculate and display every 1s
      previousMillis = millis();   // update time
      
      Volts_TRMS = inputStats.sigma()* slope + intercept;
//      Volts_TRMS = Volts_TRMS*0.979;              //Further calibration if needed
      
      Serial.print("Non Calibrated: ");
      Serial.print("\t");
      Serial.print(inputStats.sigma()); 
      Serial.print("\t");
      Serial.print("Calibrated: ");
      Serial.print("\t");
      Serial.println(Volts_TRMS);
    
  }
}

ESP8266_ZMPT101B_AdafruitIO.ino

Arduino
To use with Adafruit IO MQTT
/* This code works with ESP8266 12E and ZMPT101B AC voltage sensor
 * It can measure the TRMS of Any voltage up to 250 VAC 50/60Hz and send the values to Adafruit MQTT
 * Refer to www.SurtrTech.com for more details 
 */

#include <ESP8266WiFi.h>                         //Libraries needed
#include <Adafruit_MQTT.h>
#include <Adafruit_MQTT_Client.h>
#include <Filters.h>

#define WLAN_SSID       "xxxxxxxx"             //Your WiFi SSID and Passcode
#define WLAN_PASS       "xxxxxxxx"

#define AIO_SERVER      "io.adafruit.com"
#define AIO_SERVERPORT  1883                  

#define AIO_USERNAME    "xxxxxxxxxx"               //Your Adafruit IO username and Key
#define AIO_KEY         "xxxxxxxxxxxxxxxxxxxxxxxx"

#define ZMPT101B A0                     //ZMPT101B analog pin
 

int Current_Time=0, Previous_Time=0, Period=10000;  //We send a value every 10s, the maximum is 30 value per minute

float testFrequency = 50;                     // test signal frequency (Hz)
float windowLength = 100/testFrequency;      // how long to average the signal, for statistist

int RawValue = 0;

float intercept = 0; // to be adjusted based on calibration testing
float slope = 1;    // to be adjusted based on calibration testing
float Volts_TRMS;   // estimated actual voltage in Volts

//For more details about the intercept and slope check the Base code at SurtrTech

WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
Adafruit_MQTT_Publish Test = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Test");        //My feed name is "Test" so pay attention to yours, remplace it at the name and "/feeds/Test"
RunningStatistics inputStats;

void setup() {
  
  Serial.begin(115200);
  delay(10);
  inputStats.setWindowSecs( windowLength );
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);

  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("WiFi connected");
  Serial.println("IP address: "); 
  Serial.println(WiFi.localIP());

}

void loop() {
  
  Volts_TRMS=ReadVoltage(); //We read the voltage, normally the function returns the Volts_TRMS value,you can do it directly in the Test.publish
                            //But this is the syntax that worked for me, return a value to it self :D
  MQTT_connect();           //Keep the MQTT connected
  Current_Time=millis();

  if(Current_Time - Previous_Time >= Period){  //Every period we send the value to the service provider
  Serial.print(F("\nSending Value "));         //The value is shown on the Serial monitor as well
  Serial.print(Volts_TRMS);
  Serial.print("...");
  if (! Test.publish(Volts_TRMS)) {
    Serial.println(F("Failed"));
  } else {
    Serial.println(F("OK!"));
  }
  Previous_Time = Current_Time;
  
  }

}

void MQTT_connect() {                  //Auto reconnect to MQTT, otherwise it will trigger a watchdog reset
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
       Serial.println(mqtt.connectErrorString(ret));
       Serial.println("Retrying MQTT connection in 5 seconds...");
       mqtt.disconnect();
       delay(5000);  // wait 5 seconds
       retries--;
       if (retries == 0) {
         // basically die and wait for WDT to reset me
         while (1);
       }
  }
  Serial.println("MQTT Connected!");
}

float ReadVoltage(){
  
      RawValue = analogRead(ZMPT101B);  // read the analog in value:
      inputStats.input(RawValue);  // log to Stats function
     
      Volts_TRMS = inputStats.sigma()* slope + intercept;
     // Volts_TRMS = Volts_TRMS*0.979;

      return Volts_TRMS;
        
}

ESP8266_ZMPT101B_LocalApp.ino

Arduino
To use with a local Android app
/* This code works with ESP8266 12E and ZMPT101B AC voltage sensor
 * It can measure the TRMS of Any voltage up to 250 VAC 50/60Hz and send the values to Android App
 * The ESP8266 starts a local server and sends data whenever something is connected to it
 * Refer to www.SurtrTech.com for more details 
 */

#include <ESP8266WiFi.h>
#include <Filters.h>

#define ZMPT101B A0
#define WLAN_SSID       "xxxxxxxxxxx"          //Your WiFI SSID and Passcode
#define WLAN_PASS       "xxxxxxxxxxx"

float testFrequency = 50;                     // test signal frequency (Hz)
float windowLength = 100/testFrequency;      // how long to average the signal, for statistist

int RawValue = 0;

float intercept = 0;  // to be adjusted based on calibration testing
float slope = 1;      // to be adjusted based on calibration testing
float Volts_TRMS;     // estimated actual voltage in Volts

//Check the Base Code for ZMPT101B on SurtrTech for more details about the intercept, slope and windowLength


unsigned long printPeriod = 1000;
unsigned long previousMillis = 0;

RunningStatistics inputStats;
WiFiServer server(80);

void setup() {
  
  Serial.begin(115200);    
  Serial.println("Serial started");
  inputStats.setWindowSecs( windowLength );
  WiFi.disconnect();
  delay(3000);
  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while ((!(WiFi.status() == WL_CONNECTED))){
    delay(300);

  }
  Serial.println("Connected");
  Serial.println((WiFi.localIP().toString())); //Display the local IP Address, this will be needed when trying to connect
  server.begin();
  pinMode(LED_BUILTIN, OUTPUT);               //I added some blinking of the internal LED, so I can know when the ESP is connected
  digitalWrite(LED_BUILTIN, LOW);             //And also when someone tries to get the value (it helps debugging)
  delay(1000);
  digitalWrite(LED_BUILTIN, HIGH);
}

void loop() {
                 
     ReadVoltage();
    
     WiFiClient client = server.available();
    if (client) {                           //IF someone is trying to connect
      Serial.println("New Client");         //Shows new client on the serial monitor to show that someone is connecting + blinking
      digitalWrite(LED_BUILTIN, LOW);
      delay(300);
      digitalWrite(LED_BUILTIN, HIGH);
      client.println("HTTP/1.1 200 OK");
      client.println("Content-type: text/html");
      client.println("");
//      client.println("<!DOCTYPE HTML>");  //Uncomment these lines if you want to send as HTML, it depends on the app and what it will receive
//      client.println("<html>");            
      client.println(Volts_TRMS);
//      client.println("</html>");
      client.println();
      digitalWrite(LED_BUILTIN, LOW);     //LED blinking to show that data is sent
      delay(300);
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);
      delay(300);
      digitalWrite(LED_BUILTIN, HIGH);
      }

    
   
    
}

float ReadVoltage(){          //Measure and calibrate the values to be sent
    
    RawValue = analogRead(ZMPT101B);  // read the analog in value:
    inputStats.input(RawValue);  // log to Stats function
             
    Volts_TRMS = inputStats.sigma()* slope + intercept;

    return Volts_TRMS;

}

Filters Library

Credits

SurtrTech

SurtrTech

9 projects • 207 followers
YT Channel bit.ly/35Ai76l, run by Automation and Electrical Engineer, Electronics amateur, no IT background so you may see wreckage in codes

Comments