Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ricardomb-tech/surqo/llms.txt

Use this file to discover all available pages before exploring further.

This page covers everything you need to wire up a Surqo sensor node, configure the firmware constants, and flash the ESP32. The firmware uses WiFiManager for zero-config WiFi setup and derives the node’s identity from its MAC address — no hardcoded credentials or farm IDs are stored in the firmware itself.

GPIO Pinout

Connect each sensor to the following ESP32 pins. All sensor VCC lines are wired to GPIO25 rather than the 3.3V rail directly, so the firmware can cut power to every sensor before entering deep sleep.
ESP32 GPIOSensorNotes
GPIO 4DHT22 DATAAir temperature + humidity
GPIO 5DS18B20 DATASoil temperature — requires 10 kΩ pull-up to 3.3V
GPIO 25Sensor VCC (all sensors)Set HIGH on wake, LOW before deep sleep to save energy
GPIO 32Capacitive soil AOUTADC1_CH4
GPIO 34ML8511 UV OUTADC1_CH6 — input only
GPIO 35Battery voltage dividerADC1_CH7 — input only
GPIO 34, 35, 36, and 39 are input-only on the ESP32. They have no output driver and no internal pull resistors. Never route an output signal to these pins.

First-Boot WiFi Setup (WiFiManager)

The Surqo firmware stores no WiFi credentials. On first power-on — or whenever it cannot connect to a previously saved network — it automatically opens a captive portal access point so the farmer can configure WiFi from any phone.
1

Node opens the setup AP

The ESP32 broadcasts a WiFi network named Surqo-Setup-{CLAIM_CODE} (e.g. Surqo-Setup-A3F2C1). The AP password is defined in secrets.h as AP_PASSWORD.
2

Connect your phone

Join the Surqo-Setup-XXXXXX network from your phone’s WiFi settings. A captive portal page opens automatically (or navigate to 192.168.4.1 manually).
3

Enter your WiFi credentials

Select your home or farm WiFi network from the list and enter the password. Tap Save.
4

Node connects and resumes

The ESP32 connects to your WiFi, saves the credentials to flash, and continues the measurement cycle. Future boots connect automatically without opening the portal.
The portal times out after 180 seconds (PORTAL_TIMEOUT_S). If no credentials are entered in time, the node logs the failure to Serial and enters deep sleep, retrying on the next scheduled wake.

Claim Code and Device Pairing

Every Surqo node derives a unique claim code from the last 6 hex digits of its MAC address. This code is the only identifier you need to link a physical node to a farm in the web app.
  • Derivation example: MAC a4:cf:12:a3:f2:c1g_mac = "a4cf12a3f2c1"claim_code = "A3F2C1"
  • The claim code is printed to Serial at 115200 baud on every boot, inside the startup banner.
  • The node announces itself to the backend via POST /api/v1/devices/register (sends MAC + firmware version) on each boot after WiFi connects.
To pair a node with a farm:
  1. Open the Surqo web app → DevicesPair Device
  2. Enter the 6-character claim code shown in the Serial monitor
  3. Select the farm to associate with this node
  4. All subsequent MQTT readings are routed to that farm automatically
No FARM_ID is stored in the firmware. The backend resolves the farm association from the MAC address after pairing. You can re-pair a node to a different farm at any time from the web app.

config.h Constants

The config.h file defines every tunable parameter for the node. It is safe to version-control — all secrets (MQTT credentials and the portal password) live in secrets.h, which is excluded from git.
config.h
// MQTT credentials — defined in secrets.h (not version-controlled)
#define MQTT_HOST         "4fd28440312d4127bb4fc40a08d21e18.s1.eu.hivemq.cloud"
#define MQTT_PORT         8883

// Backend API
#define API_BASE_URL      "https://surqo-api.fly.dev"
#define FIRMWARE_VERSION  "2.0.0"

// GPIO pins
#define DHT_PIN           4
#define DS18B20_PIN       5
#define SOIL_MOISTURE_PIN 32
#define UV_SENSOR_PIN     34
#define BATTERY_PIN       35
#define SENSOR_POWER_PIN  25

// Capacitive sensor calibration (calibrate for YOUR soil)
#define SOIL_DRY_ADC      3200  // ADC value in completely dry soil
#define SOIL_WET_ADC      1200  // ADC value in saturated soil

// Timing
#define SLEEP_MINUTES     1     // Deep sleep between readings — 1 for USB/demo; use 15-30 in field
#define SENSOR_WARMUP_MS  2000  // DHT22 stabilization time
#define WIFI_TIMEOUT_MS   30000
#define PORTAL_TIMEOUT_S  180

Create secrets.h

Before compiling, copy secrets.example.h to secrets.h and fill in your real credentials:
firmware/surqo_node/secrets.h
// Copy from secrets.example.h — never commit this file
#define MQTT_USERNAME  "your-hivemq-username"
#define MQTT_PASSWORD  "your-hivemq-password"
#define AP_PASSWORD    "surqo123"  // Portal WiFi password
secrets.h is listed in .gitignore. Never commit MQTT credentials to version control. Treat them as you would any API key or database password.

Build and Upload (PlatformIO)

cd firmware
pio run -t upload        # Compile + upload to ESP32
pio device monitor       # Serial monitor at 115200 baud
Set SLEEP_MINUTES 1 in config.h during development so the node wakes every minute. Change it to 15 or 30 before deploying on battery in the field.

Operation Cycle

On every wake triggered by the RTC deep sleep timer, the node executes the following sequence and then returns to sleep:
1

Wake from RTC timer

The ESP32 boots from deep sleep. setup() runs — loop() is never called.
2

Power sensors

GPIO25 is set HIGH, supplying 3.3V to all sensor VCC lines. A 2-second warmup delay (SENSOR_WARMUP_MS) allows the DHT22 to stabilize.
3

Read all sensors

The firmware reads in this order (before WiFi is enabled, because the radio can interfere with DHT22 timing):
  • DHT22 — up to 5 attempts, average of first 3 valid readings
  • DS18B20 — 12-bit conversion (~750 ms)
  • Capacitive soil — 10 ADC samples averaged (ADC_SAMPLES = 10)
  • ML8511 UV — 10 ADC samples averaged
  • Battery voltage — 10 ADC samples averaged, multiplied by voltage divider factor 2
4

Connect WiFi

WiFiManager connects using stored credentials (or opens the captive portal if no credentials are saved).
5

Register device

A best-effort HTTPS POST to POST /api/v1/devices/register announces the MAC address and firmware version to the backend.
6

Publish via MQTT TLS

The node connects to HiveMQ Cloud on port 8883 (TLS) and publishes a JSON payload to topic surqo/devices/{mac}/sensors.
7

HTTP fallback

If MQTT fails (bad credentials, broker unreachable, TLS error), the firmware falls back to POST /api/v1/devices/{mac}/reading over HTTPS.
8

Power down and sleep

GPIO25 is set LOW (cutting sensor power), WiFi is disconnected, and the ESP32 enters deep sleep at ~10 µA for SLEEP_MINUTES minutes.

Soil Sensor Calibration

The capacitive sensor returns raw ADC values that vary by soil composition. Always calibrate for your specific field:
  1. Dry reading: Hold the sensor in completely dry air or dry soil for 10 minutes. Read the ADC value from the Serial monitor and set SOIL_DRY_ADC in config.h.
  2. Wet reading: Submerge the sensor up to the white line in a glass of water for 10 minutes. Read the ADC value and set SOIL_WET_ADC in config.h.
The firmware maps the raw ADC range [SOIL_DRY_ADC … SOIL_WET_ADC] linearly to [0% … 100%] and clamps the result to that range.

Build docs developers (and LLMs) love