ESP32 and Arduino

ESP32 SCT-013 Current Sensor Energy Monitor over MQTT

Clamp an SCT-013 current transformer on a mains cable, measure RMS current with the ESP32 ADC, estimate power and energy, and publish the results to broker.connectmq.com for energy dashboards.

SCT-013 current clamp around a cable connected to an ESP32 energy monitor, illustrating the esp32 sct-013 current sensor project
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

An ESP32 SCT-013 current sensor build is the cheapest non-invasive way to see how much power a circuit uses, because the clamp goes around a cable without cutting anything. In this tutorial you will build the bias circuit, compute RMS current with the ESP32 ADC, estimate power and kWh, and publish the results over MQTT to broker.connectmq.com for an energy dashboard.

Safety First

The SCT-013 itself is a split-core clamp that never touches live conductors, which is what makes this project approachable. The cabinet it goes into is a different matter. Opening a consumer unit or distribution board exposes lethal voltages. If you are not qualified, clamp only on a cable that is already accessible, such as a single appliance lead split into its conductors in a purpose-made adapter, or ask an electrician to install the clamp. Never open the clamp on an SCT-013-000 while current flows without a burden resistor fitted, because an unloaded current transformer can develop a dangerous voltage on its leads.

Choosing the Right SCT-013 Variant

Model Output Burden needed Range Conversion
SCT-013-000 Current, 50 mA at 100 A Yes, external Up to 100 A Depends on your burden resistor
SCT-013-030 Voltage, 1 V RMS at 30 A No, built in Up to 30 A 30 A per volt
SCT-013-020 Voltage, 1 V RMS at 20 A No, built in Up to 20 A 20 A per volt
SCT-013-005 Voltage, 1 V RMS at 5 A No, built in Up to 5 A 5 A per volt

For a first build, choose a voltage-output version sized to the circuit. Higher resolution at low currents comes from picking the smallest range that covers your maximum load: a 5 A clamp on a fridge circuit gives far better readings than a 100 A clamp.

If you use the SCT-013-000, add a burden resistor across its output. A 22 Ohm burden keeps the peak voltage under about 1.6 V at 100 A RMS, which fits the bias circuit below, and gives a conversion of 2000 / 22, roughly 90.9 A per volt.

The Bias Circuit

The clamp produces an AC signal that swings positive and negative around zero. The ESP32 ADC can only read 0 to roughly 3.1 V, so the signal must ride on a DC midpoint.

  1. Connect two equal resistors (10 kOhm each) in series between 3V3 and GND. Their junction sits at about 1.65 V.
  2. Connect a 10 uF electrolytic capacitor from the junction to GND to stabilise the midpoint.
  3. Connect one lead of the clamp (the 3.5 mm plug tip) to the junction.
  4. Connect the other lead (the sleeve) to the ADC input, GPIO 34.

With a 1 V RMS output, the signal peaks at about 1.41 V, so the ADC sees roughly 0.24 V to 3.06 V. That stays inside the range of the default attenuation. Use a pin on ADC1 (GPIO 32 to 39 on a classic ESP32); ADC2 pins cannot be read while Wi-Fi is active.

Measuring RMS Current

The code samples the ADC as fast as it can for 200 ms, which covers 10 full cycles at 50 Hz and 12 at 60 Hz. It computes the mean (the DC midpoint, measured rather than assumed) and the standard deviation around it, which is exactly the RMS value of the AC component. Multiplying by the clamp's conversion factor gives amps.

Measuring the midpoint on every pass removes errors from resistor tolerance and supply drift, and analogReadMilliVolts() applies the factory ADC calibration, which helps with the ESP32 ADC's known non-linearity.

The ESP32 SCT-013 Current Sensor Sketch

#include <WiFi.h>
#include <PubSubClient.h>
#include <Preferences.h>
#include <math.h>

const char* WIFI_SSID     = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST     = "broker.connectmq.com";
const uint16_t MQTT_PORT  = 1883;
const char* PREFIX        = "connectmq/demo/sct3m8w";   // use your own random id

const uint8_t CT_PIN          = 34;      // ADC1 pin
const float   AMPS_PER_VOLT   = 30.0f;   // SCT-013-030; 90.9 for -000 with 22 Ohm burden
const float   MAINS_VOLTAGE   = 230.0f;  // set 120.0 in North America
const float   CALIBRATION     = 1.00f;   // adjust after comparing with a clamp meter
const float   NOISE_FLOOR_A   = 0.08f;   // readings below this are reported as 0
const uint32_t WINDOW_MS      = 200;
const uint32_t PUBLISH_MS     = 10000;
const uint32_t SAVE_MS        = 600000;  // persist energy every 10 min

WiFiClient   net;
PubSubClient mqtt(net);
Preferences  prefs;
String clientId, tPower, tStatus;

double energyKwh = 0;
uint32_t lastSample = 0, lastPublish = 0, lastSave = 0;
float lastIrms = 0, sumIrms = 0;
uint32_t samplesInPeriod = 0;

float readIrms() {
  uint32_t n = 0;
  double sum = 0, sumSq = 0;
  uint32_t start = millis();
  while (millis() - start < WINDOW_MS) {
    double mv = analogReadMilliVolts(CT_PIN);
    sum += mv;
    sumSq += mv * mv;
    n++;
  }
  double mean = sum / n;
  double variance = sumSq / n - mean * mean;
  if (variance < 0) variance = 0;
  double vrms = sqrt(variance) / 1000.0;          // volts RMS of the AC part
  float irms = vrms * AMPS_PER_VOLT * CALIBRATION;
  return irms < NOISE_FLOOR_A ? 0.0f : irms;
}

void connectWifi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) delay(300);
  Serial.printf("Wi-Fi OK, IP %s\n", WiFi.localIP().toString().c_str());
}

void connectMqtt() {
  while (!mqtt.connected()) {
    if (mqtt.connect(clientId.c_str(), tStatus.c_str(), 1, true, "offline")) {
      mqtt.publish(tStatus.c_str(), "online", true);
      Serial.println("MQTT connected");
    } else {
      Serial.printf("MQTT failed, state %d\n", mqtt.state());
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  analogReadResolution(12);
  prefs.begin("energy", false);
  energyKwh = prefs.getDouble("kwh", 0.0);

  clientId = "esp32-sct013-" + String((uint32_t)ESP.getEfuseMac(), HEX);
  tPower   = String(PREFIX) + "/power";
  tStatus  = String(PREFIX) + "/status";

  connectWifi();
  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  mqtt.setBufferSize(512);
  connectMqtt();
  lastSample = lastPublish = lastSave = millis();
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) connectWifi();
  if (!mqtt.connected()) connectMqtt();
  mqtt.loop();

  // Sample about once per second and integrate energy
  uint32_t now = millis();
  if (now - lastSample >= 1000) {
    float irms = readIrms();
    float dtHours = (millis() - lastSample) / 3600000.0f;
    lastSample = millis();
    energyKwh += (irms * MAINS_VOLTAGE) * dtHours / 1000.0;
    lastIrms = irms;
    sumIrms += irms;
    samplesInPeriod++;
    mqtt.loop();
  }

  if (now - lastPublish >= PUBLISH_MS && samplesInPeriod > 0) {
    lastPublish = now;
    float avgIrms = sumIrms / samplesInPeriod;
    sumIrms = 0;
    samplesInPeriod = 0;

    char payload[200];
    snprintf(payload, sizeof(payload),
             "{\"irms\":%.2f,\"irmsAvg\":%.2f,\"apparentPowerVA\":%.0f,"
             "\"energyKwh\":%.4f,\"voltageAssumed\":%.0f}",
             lastIrms, avgIrms, avgIrms * MAINS_VOLTAGE, energyKwh, MAINS_VOLTAGE);
    mqtt.publish(tPower.c_str(), payload);
    Serial.println(payload);
  }

  if (now - lastSave >= SAVE_MS) {
    lastSave = now;
    prefs.putDouble("kwh", energyKwh);   // limited writes protect flash
  }
}

Each measurement window blocks the loop for 200 ms, which is harmless for MQTT keep-alives, because the sketch calls mqtt.loop() right after sampling. If you add more clamps, read them one after another in the same pass, or move sampling into its own FreeRTOS task as shown in the ESP32 FreeRTOS MQTT guide, so the network code never waits behind a long series of windows.

Testing and Calibrating

Subscribe to the topic:

mosquitto_sub -h broker.connectmq.com -p 1883 -i sct-view-3m8w -t "connectmq/demo/sct3m8w/#" -v

With nothing running, irms should read 0. Switch on a known resistive load, such as a kettle or a heater, and compare irms with a clamp meter on the same conductor. Adjust CALIBRATION by the ratio between the two readings, for example 1.04 if the ESP32 reads 4 percent low. Calibrate near the middle of the clamp's range for the best overall accuracy.

The public area of the broker is readable by anyone, and power consumption patterns can reveal when a home is occupied, so use it for bench tests only. Its limits are listed on the free public MQTT broker page.

What the Numbers Mean

The sketch publishes apparent power in volt-amperes, calculated from measured current and an assumed mains voltage. For resistive loads like kettles and heaters, apparent power is close to real power in watts. For motors, compressors, power supplies and LED drivers, the power factor is below 1, so the real power is lower than the VA figure. Mains voltage also varies through the day.

Real power needs a voltage measurement too, typically from an isolated AC-AC transformer feeding a second ADC pin, plus sample-by-sample multiplication. That is a worthwhile next step, but the current-only version is already enough to see which appliance runs when, spot standby loads and compare days.

Accuracy Checklist

  1. Clamp around one conductor only. Around a whole cable, live and neutral currents cancel and you read near zero.
  2. Pick the smallest clamp range that covers your maximum current.
  3. Keep the analog wires short and away from Wi-Fi antennas and switching supplies.
  4. Measure the midpoint every window instead of assuming 1.65 V.
  5. Apply a noise floor so an idle circuit reads 0, not a few hundred milliamps.
  6. Calibrate against a trusted meter with a resistive load.

Building an Energy Dashboard

On the receiving side, a subscriber turns messages into charts. Telegraf's MQTT consumer can parse the JSON and write it to InfluxDB for Grafana, Node-RED can calculate daily totals and costs, and Home Assistant can treat energyKwh as an energy sensor with an MQTT sensor definition. All of them connect to the broker as normal MQTT clients. If you monitor several circuits or temperatures at once, combine this node with the DS18B20 multi-probe project, or read existing meters with the ESP32 Modbus RS485 gateway.

For production, create a broker at app.connectmq.com, switch to WiFiClientSecure on port 8883 with the ISRG Root X1 certificate, and connect with YOUR_CLIENT_ID, YOUR_USERNAME and YOUR_PASSWORD. The ConnectMQ portal's threshold alert rules can then warn you when current exceeds a limit, such as a circuit approaching its breaker rating.

Get Started with ConnectMQ

Energy data is most valuable when it is collected continuously and kept private. ConnectMQ is a managed MQTT broker with TLS, MQTT 3.1.1 and 5.0, a live topic explorer and alerting, and the free Serverless plan needs no credit card. Point this sketch at your broker and see the ESP32 IoT tutorial hub for more measurement projects.

Frequently Asked Questions

Can the ESP32 SCT-013 current sensor measure real power in watts?

With current alone it estimates apparent power in VA. Real power needs a voltage measurement as well, so the code can account for power factor.

Why does my reading show current with nothing switched on?

That is ADC noise and pickup. Apply a noise floor, keep wires short, and check that the midpoint capacitor is fitted.

Which ESP32 pins can I use for the SCT-013?

Use ADC1 pins (GPIO 32 to 39 on a classic ESP32). ADC2 pins do not work while Wi-Fi is active.

Do I need a burden resistor?

Only for the current-output SCT-013-000. The voltage-output versions such as the SCT-013-030 have a built-in burden.

Related articles

ESP-IDF MQTT Example: Build an esp-mqtt Client from Scratch

Build an ESP-IDF project from an empty folder with the esp-mqtt component: Wi-Fi station setup, the MQTT event handler, publish and subscribe, MQTT 5 properties and TLS with the certificate bundle.

8 min read

Using esp-mqtt MQTT 5 Features on ESP32 with ESP-IDF

A hands-on ESP-IDF project that enables MQTT 5.0 in esp-mqtt and uses user properties, message expiry, topic aliases, session expiry and response topics against a real MQTT 5 broker.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.