⚡ ESP32-S3 · OPC-UA · BLE · Industrial SCADA

FlowSense Pro
Industrial Flow & SCADA Gateway

A multi-sensor ESP32-S3 SCADA gateway that monitors flow rates, pressure, and chemical dosing for water treatment plants and food-grade production lines — with OPC-UA integration, BLE commissioning, and TLS-secured cloud reporting.

⏱ Duration: 1–2 weeks
💰 Budget: from $390
📦 Platform: ESP32-S3
🔧 Framework: ESP-IDF v5.x
📡 Protocols: OPC-UA · BLE · MQTT
# Key Outcomes
±0.5%
Flow Accuracy
<10ms
Alarm Response
32
Nodes per Hub
5yr
Battery Life
TLS 1.3
Security
OTA
Remote Update
# Problem & Solution

The Problem

  • Water treatment plant had 4–20 mA flow sensors with no digital readout
  • SCADA system required OPC-UA — existing Arduino solutions didn't support it
  • Field technicians couldn't commission devices without laptop + USB cable
  • Cloud-sent firmware updates required physically visiting each remote station
  • E-ink displays needed to show live flow rates without power drain

The Solution

  • ESP32-S3 with 16-bit ADS1115 ADC reads 4–20 mA loop at ±0.5% accuracy
  • open62541 OPC-UA client stack compiled for ESP-IDF — direct SCADA integration
  • Custom BLE GATT profile — iOS/Android app configures Wi-Fi, calibration, thresholds
  • ESP-IDF OTA with automatic rollback on consecutive boot failures
  • 2.9" Waveshare E-Paper SPI display — updates every 5s, zero standby draw
# Firmware Architecture (Sample)
flow_sensor.c — 4-20mA current loop ADC sampling
/* Reads 4-20mA industrial current loop via ADS1115 (16-bit I²C ADC)
   Maps 4mA → 0 L/min, 20mA → MAX_FLOW_LPM with linearisation     */

#define ADS1115_ADDR    0x48
#define SHUNT_OHMS     100.0f   /* 100Ω precision shunt resistor */
#define CURRENT_4MA    4.0f
#define CURRENT_20MA   20.0f
#define MAX_FLOW_LPM  500.0f

static i2c_master_dev_handle_t ads_dev;

esp_err_t flow_sensor_init(void) {
    i2c_master_bus_config_t bus_cfg = {
        .i2c_port     = I2C_NUM_0,
        .sda_io_num   = GPIO_SDA,
        .scl_io_num   = GPIO_SCL,
        .clk_source   = I2C_CLK_SRC_DEFAULT,
        .glitch_ignore_cnt = 7,
        .flags.enable_internal_pullup = true,
    };
    return i2c_new_master_bus(&bus_cfg, &i2c_bus);
}

float flow_read_lpm(void) {
    /* Trigger single-shot conversion on AIN0 (PGA ±2.048V) */
    uint8_t cfg[] = {0x01, 0xC1, 0x83};  /* OS=1, AIN0-GND, FS=2.048V */
    i2c_master_transmit(ads_dev, cfg, sizeof(cfg), 100);
    vTaskDelay(pdMS_TO_TICKS(9));       /* Wait for 860 SPS conversion */

    uint8_t reg = 0x00;
    uint8_t raw[2];
    i2c_master_transmit_receive(ads_dev, ®, 1, raw, 2, 100);
    int16_t adc_val = (raw[0] << 8) | raw[1];

    /* Convert raw → voltage → current → flow */
    float voltage = adc_val * (2.048f / 32768.0f);
    float current_mA = (voltage / SHUNT_OHMS) * 1000.0f;
    float flow_lpm = ((current_mA - CURRENT_4MA) /
                       (CURRENT_20MA - CURRENT_4MA)) * MAX_FLOW_LPM;

    return (flow_lpm < 0) ? 0 : flow_lpm;
}
ble_commissioning.c — Custom GATT profile for field configuration
/* Custom GATT service UUID: 0xAB01 — Wi-Fi + calibration commissioning */

static const esp_gatts_attr_db_t flowsense_gatt_db[] = {
    [IDX_SVC] = {
        {ESP_GATT_AUTO_RSP},
        {ESP_UUID_LEN_16, (uint8_t*)&primary_service_uuid, ESP_GATT_PERM_READ,
         sizeof(uint16_t), sizeof(flowsense_svc_uuid), (uint8_t*)&flowsense_svc_uuid}
    },
    /* Characteristic: Wi-Fi SSID (write, UTF-8, max 64 bytes) */
    [IDX_CHAR_SSID_VAL] = {
        {ESP_GATT_AUTO_RSP},
        {ESP_UUID_LEN_16, (uint8_t*)&char_wifi_ssid_uuid,
         ESP_GATT_PERM_WRITE, 64, 0, NULL}
    },
    /* Characteristic: Flow calibration factor (float32, notify + write) */
    [IDX_CHAR_CAL_VAL] = {
        {ESP_GATT_AUTO_RSP},
        {ESP_UUID_LEN_16, (uint8_t*)&char_cal_uuid,
         ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, sizeof(float),
         sizeof(cal_factor), (uint8_t*)&cal_factor}
    },
};

void gatts_event_handler(esp_gatts_cb_event_t event, ...) {
    if (event == ESP_GATTS_WRITE_EVT && param->write.handle == ssid_handle) {
        memcpy(wifi_config.sta.ssid, param->write.value, param->write.len);
        ble_respond_ok(param);
        wifi_reconnect();   /* apply new SSID immediately */
    }
}
# Full Tech Stack

Hardware

ESP32-S3-WROOM-1 ADS1115 16-bit ADC 100Ω Precision Shunt Pulse Flow Meter (NPN) 2.9" E-Paper (Waveshare) 4-Ch MOSFET Relay Custom 4-layer PCB (KiCad) DIN Rail Enclosure IP54

Firmware & Software

ESP-IDF v5.2 FreeRTOS open62541 OPC-UA BLE GATT Server MQTT/TLS (ESP-MQTT) AWS IoT Core OTA (esp_https_ota) Node-RED + SCADA
# Project Timeline
Week 1
PCB Design & Sensor Bringup
4-layer KiCad PCB, ADS1115 I²C driver, pulse counter ISR, E-paper SPI driver verification
Week 2
OPC-UA & BLE Stack
Porting open62541 to ESP-IDF build system, custom GATT commissioning service, iOS/Android BLE app (Flutter)
Week 3
MQTT/TLS & OTA Pipeline
AWS IoT mutual-auth TLS certificates, HTTPS OTA with rollback, Node-RED SCADA dashboard integration
Week 4
Site Testing & Compliance Docs
FAT at water treatment plant, EMC pre-compliance checks, IEC 61508 SIL-1 evidence pack, handover training

Need a SCADA Gateway for Your Plant?

I'll design a flow monitoring system tailored to your process — from 4-20mA sensors to your existing SCADA platform.