# 16 — SCADA gateway (ESP32 reads the PLC, sends readings every second)
[English](README.md) | [Tiếng Việt](README_vi.md)
An ESP32 sits on the plant's control network. Every second it reads the PLC/SCADA
over **Modbus TCP**, sends a snapshot to the cloud (`telemetry`, PROTOCOL-v1 §3c), and
raises an alert when a tag crosses a threshold.
```
PLC / SCADA ──Modbus TCP (read-only)──► ESP32 ──outbound WebSocket──► cloud ──► dashboard
```
**Read-only.** No cloud command writes to the PLC or drives. The only command the
gateway accepts is `config_updated`, which reloads the tag map.
## Network
The ESP32 must reach **both** the PLC and the Internet. Control networks often have no
Internet. Either use a WiFi network that can see the PLC and route out, or put
Ethernet (W5500/LAN8720) on the PLC network with WiFi/4G for the uplink. The second
option is *not in this example yet*. The gateway only dials out and opens no inbound
port into the plant.
## Tag map
There is a compiled-in default in `main/scada_tags.c` with sample addresses; replace
them with your PLC's real addresses. Better: send a `scada` key in the cloud's
operational config. It is cached in NVS, so you can change tags without reflashing:
```json
{"scada":{"host":"192.168.10.5","port":502,"interval_ms":1000,
"tags":[["MLM5.hz",1,3,100,"u16",0.01],["CM2.temp1",1,3,200,"s16",0.1,80]]}}
```
Columns are `name, unit id, function (1–4), address, type (u16 s16 u32 s32 f32 bit),
scale, [high, low, word_swap]`. One frame holds about 100 tags (~3.5 KB).
## Alerts
- `<tag>_high` / `<tag>_low` use hysteresis: an alert clears only once the value is 2%
(minimum 0.5) back inside the limit.
- `scada_source_down` is critical and fires when no tag could be read in a cycle.
- Alerts raised while offline are kept and sent on reconnect.
## Build
```bash
idf.py set-target esp32s3 && idf.py flash monitor
```
Try it without a cloud: run `go run ./tools/mock-cloud`, then open
`http://localhost:8080/telemetry`.
Tested with host tests (Modbus TCP against a real-socket fake server, alarm
hysteresis), the mock-cloud telemetry test, and `idf.py build` on ESP-IDF 5.5.4.
**Not yet run on a board against a real PLC.**
To create a project from this example, run:
idf.py create-project-from-example "nguyenduchoai/innoedge=0.3.0:16-scada-gateway"