Sensor Selection
This chapter answers two questions:
- Which current sensor to choose for a given load.
- How to tell the rbAmp module about your choice so that the factory calibration for that combination loads automatically.
The physical connection of the sensor (clamp orientation, L/N polarity check) is covered in 04_hardware.md. This chapter is about model selection and API calls.
Sensor class
rbAmp modules work with CT clamps of the SCT-013 family. The
sensor class is determined by the module's hardware revision and is
fixed at the factory — the user reports their choice via
rbamp_set_sensor_class() before selecting a specific CT clamp
model.
Choosing the SCT-013 model
The SCT-013 family has five models, differing in the maximum primary current:
code |
Model | Current range | Typical use |
|---|---|---|---|
| 1 | SCT-013-005 | 0…5 A | Small loads — lamps, low-power electronics, a single switch |
| 2 | SCT-013-010 | 0…10 A | A single medium-power appliance — refrigerator, washing machine, air conditioner up to 2 kW |
| 3 | SCT-013-030 | 0…30 A | A medium household's mains feed — up to ~7 kW |
| 4 | SCT-013-050 | 0…50 A | A large feed — electric heating, EV charger, a house with peak loads |
| 5 | SCT-013-100 | 0…100 A | A main feed for a house or small office — up to ~23 kW |
How to pick the right model
The basic rule:
- Determine the maximum current that can flow in the circuit (the largest load + 30 % headroom).
- Choose the model whose range this value fits into.
- Don't add more than 5× headroom. An SCT-013-100 clamp on a circuit with a maximum current of 5 A will work, but it will give low resolution and a large error at typical values.
Headroom
An SCT-013 clamp operates without saturation within its rated range. Brief peaks (compressor inrush, inductive load) may exceed the rating by 5–7× — this is normal, the clamp physically withstands it, but the measurement becomes nonlinear above the rating.
If your load has a peak current above the clamp's rating, choose the next size up. For example, for a washing machine with a 12 A inrush current (but a 2–3 A rating while running), SCT-013-030 is better than SCT-013-005.
How to tell the module about your choice
Two calls in the correct order are mandatory — first
rbamp_set_sensor_class(), then rbamp_set_ct_model(). If you call
rbamp_set_ct_model() before rbamp_set_sensor_class() on v1.2+
firmware, the component returns ESP_ERR_INVALID_STATE and the
preset is NOT loaded.
#include "driver/i2c_master.h"
#include "rbamp.h"
void setup_sensor(rbamp_handle_t dev) {
/* Step 1: choose the sensor family.
* MANDATORY before rbamp_set_ct_model(). */
esp_err_t err = rbamp_set_sensor_class(dev, RBAMP_SENSOR_SCT013);
if (err != ESP_OK) {
ESP_LOGE("app", "set_sensor_class: %s", rbamp_err_to_str(err));
return;
}
/* Step 2: choose the model within the family.
* 1 = SCT-013-005, 2 = SCT-013-010, 3 = SCT-013-030,
* 4 = SCT-013-050, 5 = SCT-013-100. */
err = rbamp_set_ct_model(dev, 3); /* e.g., SCT-013-030 */
if (err != ESP_OK) {
/* Possible causes:
* ESP_ERR_INVALID_STATE — rbamp_set_sensor_class() was not called
* before rbamp_set_ct_model() (on v1.2+ firmware)
* ESP_ERR_INVALID_ARG — code outside the 1..5 range
* ESP_FAIL — communication error (NACK after retries)
*/
ESP_LOGE("app", "set_ct_model: %s", rbamp_err_to_str(err));
return;
}
}The component deliberately does NOT call
rbamp_set_sensor_class()on your behalf. If this step is skipped,rbamp_set_ct_model()returnsESP_ERR_INVALID_STATEwithout writing to flash. This is done so that the behavior is predictable and explicit — no "magic" in the public API.
After these two calls:
- The module saves both values to flash — the configuration survives a reset, a power cycle, and a firmware re-flash.
- From the factory preset table, the calibration coefficients for this specific combination (sensor class + model) are loaded. You don't need to touch any manual calibration registers.
- The next call to
rbamp_read_current(dev, 0, &i)already returns a value in amperes with the correct scaling.
The total time for both calls is about 1.4 seconds (two flash-write operations × ~700 ms each, limited by the flash page erase time). It's done once at first installation; the configuration is saved to flash and is not repeated.
If you already chose the sensor on the first run and are simply rebooting the ESP32 — you don't need to repeat the
rbamp_set_sensor_class()andrbamp_set_ct_model()calls, the module remembers the previous choice. But it's not harmful either — calling again with the same value simply rewrites the same byte.
Verifying the setup
A simple sanity check after rbamp_set_ct_model():
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
static const char *TAG = "rbamp_check";
void sanity_check(rbamp_handle_t dev) {
ESP_LOGI(TAG, "Ready. Connect a purely resistive load "
"(e.g., an incandescent lamp).");
ESP_LOGI(TAG, "Expecting a stable PF ~= 1.0 and a positive P.");
while (1) {
float u = NAN, i = NAN, p = NAN, pf = NAN;
rbamp_read_voltage(dev, 0, &u);
rbamp_read_current(dev, 0, &i);
rbamp_read_power(dev, 0, &p);
rbamp_read_power_factor(dev, 0, &pf);
ESP_LOGI(TAG, "U=%.1f V I=%.2f A P=%.1f W PF=%.2f",
(double)u, (double)i, (double)p, (double)pf);
vTaskDelay(pdMS_TO_TICKS(2000));
}
}On a purely resistive load (incandescent lamp, electric kettle, heating element), expect:
U≈ 220–240 V (for 230 V grids)I≈ corresponds to the load's power (P / U)P> 0 and stablePF≈ 1.0 (definitely positive)
If something doesn't add up, see 10_troubleshooting.md.
Modules with multiple current channels
On the UI2, UI3, I2, I3 modules, each current channel has an
independent SCT-013 model selection. You can connect, for
example, an SCT-013-005 on channel 0 (a single outlet), an
SCT-013-030 on channel 1 (the electric stove line), and an
SCT-013-100 on channel 2 (the main feed).
The API for per-channel selection is rbamp_set_ct_model_ch(dev, channel, code):
rbamp_set_sensor_class(dev, RBAMP_SENSOR_SCT013); /* once for all */
/* IMPORTANT: assign channels from highest to lowest (descending order).
* First channel 2, then 1, then 0. See below for why. */
rbamp_set_ct_model_ch(dev, 2, 5); /* channel 2: SCT-013-100 */
rbamp_set_ct_model_ch(dev, 1, 3); /* channel 1: SCT-013-030 */
rbamp_set_ct_model_ch(dev, 0, 1); /* channel 0: SCT-013-005 */⚠ Order matters. Each
rbamp_set_ct_model_ch(dev, channel, code)call also applies the samecodeto channel 0 as a side effect — this is the legacy compatibility path with v1.1 firmware (the single-argrbamp_set_ct_model(dev, code)always wrote to channel 0 directly). If you assign channels in forward order(0,1) → (1,3) → (2,5), the last call overwrites ch0 with the value 5, and the final state will bech0=5, ch1=3, ch2=5— incorrect.The correct order is from the highest channel to the lowest: the last call, with
channel=0, fixes the final ch0 value.
If all channels get the same model, the order doesn't matter (the side effect is idempotent):
rbamp_set_sensor_class(dev, RBAMP_SENSOR_SCT013);
rbamp_set_ct_model_ch(dev, 0, 1);
rbamp_set_ct_model_ch(dev, 1, 1); /* ch0 is overwritten with the same value */
rbamp_set_ct_model_ch(dev, 2, 1);The single-parameter rbamp_set_ct_model(dev, code) remains for
backward compatibility with UI1 modules (it applies to channel 0).
On multi-channel modules it is equivalent to
rbamp_set_ct_model_ch(dev, 0, code).
On v1.0/v1.1 firmware (REG_VERSION < 0x03) the per-channel opcodes
CMD_SET_CT_MODEL_CHndo not exist —rbamp_set_ct_model_ch(dev, channel, code)returnsESP_ERR_NOT_SUPPORTEDwith no write. Use the single-parameterrbamp_set_ct_model(dev, code), which writes to channel 0 via the legacy path.
Advanced setup: two clamps of different ratings on one wire
⚙ An additional pattern, not a basic one. This section describes an optional technique for improving resolution at low currents. For most installations, a single clamp matched to the load range is sufficient. Use dual-CT only if you have a specific accuracy requirement at currents < 1 A.
When this applies
- Multi-channel modules UI2 / UI3 on v1.2+ firmware.
- The same wire needs to be measured both for small loads (≤ 1 A) and for peak events (≥ 5 A) with equal quality.
- A typical example: an apartment feed where during the day there's 50–100 W of standby, and in the evening — a kettle or electric stove starting up at 3+ kW.
The idea
Two SCT-013 clamps of different ratings are installed on the same wire:
- Channel 0 — a small clamp (e.g., SCT-013-005, 5 A): it sees small currents with better resolution and a lower noise floor.
- Channel 1 — a large clamp (e.g., SCT-013-030 or higher): it handles currents above the small clamp's overload point without saturation.
The master itself chooses which channel to use depending on the current value — while the small clamp is in its linear range, its reading is more accurate; when exceeded, it switches to the large one.
Configuration (descending order — mandatory)
First the sensor class once, then the models from the highest
channel to the lowest — otherwise the legacy REG_CT_MODEL side
effect will overwrite channel 0 with the value of the last call (see
the warning in the previous section, "Modules with multiple current
channels").
rbamp_set_sensor_class(dev, RBAMP_SENSOR_SCT013); /* once */
/* First channel 1 (the large clamp), then channel 0 (the small one) —
* so that the final ch0 value is correct. */
rbamp_set_ct_model_ch(dev, 1, 3); /* ch1 = SCT-013-030 (0..30 A) */
rbamp_set_ct_model_ch(dev, 0, 1); /* ch0 = SCT-013-005 (0..5 A) */Final state: ch0 = SCT-013-005, ch1 = SCT-013-030. ✓
Aggregation logic on the master side
The simplest pattern is to switch on a threshold:
float read_combined_current(rbamp_handle_t dev) {
float i_low = NAN;
float i_high = NAN;
esp_err_t err_low = rbamp_read_current(dev, 0, &i_low); /* small */
esp_err_t err_high = rbamp_read_current(dev, 1, &i_high); /* large */
/* While the small clamp is far from saturation, it gives better
* accuracy at low currents. We switch to the large one as we
* approach the overload point.
*
* The 4.5 A threshold for the SCT-013-005 is PROVISIONAL; the exact
* value will be determined by bench validation (see below on IP-010).
* The behavior near the threshold is a matter of measurement, not
* estimation. */
if (err_low == ESP_OK && i_low < 4.5f) {
return i_low;
}
return (err_high == ESP_OK) ? i_high : NAN;
}📷 An installation diagram is forthcoming. Two clamps on one wire are physically possible on most household-gauge cables, but they need a bit of room in the panel. A detailed diagram — including the arrow orientation of both clamps and the permissible distances between them — will appear here as it is prepared.
⚙ Bench validation. The exact figures for the dual-CT pattern (behavior near the threshold, temperature drift, divergence of the two clamps in the overlapping range) are established by the IP-010 measurement program (the one following IP-001). Until it is complete, treat dual-CT as a pilot pattern; for critical applications, a single clamp matched to the upper load range is preferable.
Approaches to improving sensitivity at low currents
If your load has a large dynamic range (for example, 1 W standby for a router vs. a 2000 W immersion heater on the same outlet), a single clamp sized for the upper limit loses the lower currents in the noise.
Three strategies in increasing order of complexity:
- Size the CT for the maximum, not "with headroom". The most common mistake is to put an SCT-013-100 (100 A) on a household outlet with a typical draw of 0.5–10 A. The signal sits in the lower 1–10 % of the ADC range — where the noise becomes comparable to the signal. For a household scenario (16 A outlet), SCT-013-030 is optimal; for connecting a single device (≤ 5 A) — SCT-013-005.
- Dual-CT topology (requires a UI2/UI3 SKU): a small clamp for the lower range + a large one for the upper, with the master choosing based on a threshold. See the "Dual-CT topology" section above — the pattern is a pilot, the numbers refined by the IP-010 program.
- Bench calibration of the noise floor (factory-side): IP-001
characterizes the noise floor on a test bench; the results are
baked into the firmware's calibration array. On the user side,
nothing needs to be done beyond
setSensorClass()+setCTModel(). Until the program is complete, specific low-current accuracy figures are not published.
What's next
- 04 · Hardware connection — physically connecting the clamp, arrow orientation, L/N polarity
- 05 · Quickstart — a full first-light project
- 06 · Examples — working scenarios for different loads
- 10 · Troubleshooting — what to do if the readings are odd (negative PF, unstable I, etc.)
← Tier Support | Contents | Hardware Setup →
Source & issues: rb-amp/rbamp-esp-idf · this page in the repo: docs/03_sensor_selection.md