Community project

Remote Weather Monitoring Station

ESP32
Photo of Remote Weather Monitoring Station
Generated with AI

Dmitriy Artoymov

Published September 28, 2026

This remote weather monitoring station captures comprehensive environmental data from a field location and transmits it via cellular network. Built around an ESP32 microcontroller, the station measures temperature at seven locations using DS18B20 probes, reads humidity and air temperature from a DHT21 sensor, monitors barometric pressure with a BMP280, detects rainfall, measures soil moisture across 16 channels, and tracks battery voltage—then queues and sends all measurements over GPRS using a SIM900 module.

This guide provides a complete wiring diagram, full parts list with resistor values for power conditioning and level shifting, and step-by-step assembly instructions covering power supply protection, sensor wiring, the multiplexed soil-moisture circuit, and battery monitoring. The included Arduino firmware handles sensor reading, local data queuing during connectivity gaps, and reliable transmission to a remote server, with the station entering deep sleep between measurement cycles to conserve power.

Wiring diagram

Wiring diagram for Remote Weather Monitoring Station

Gather all the parts

QtyComponent
1

DHT21 (AM2301) temperature and humidity sensor

The small digital sensor that measures outside air temperature and humidity.

7

DS18B20

Digital temperature sensor using OneWire protocol

1

4.7k Pull Up Resistor

4.7 kΩ

4.7kΩ pull-up resistor used in 1-Wire (OneWire) communication buses. Essential passive component that pulls the single data line to 3.3V or 5V when idle. Works with 1-Wire protocol devices (DS18B20, DS18S20, iButton, etc.) to ensure proper bus communication at standard 16.3 kbit/s data rates. Supports both 3.3V and 5V systems.

1

BMP280 I²C barometric-pressure breakout

A small sensor board that measures air pressure and temperature through two shared signal wires.

1

74HC4067 16-channel analog multiplexer module

The small selector board that lets one ESP32 input read seven separate soil-moisture sensors.

1

Rain sensor module with analog output

The exposed sensing pad and module that report how much rainwater is on the pad.

1

10 kΩ fixed resistor

10 kΩ

A resistor that completes the light sensor’s voltage divider.

1

SIM900 GSM/GPRS module

The cellular modem that sends measurements and retrieves updates over the mobile network.

1

10 kΩ resistor for SIM900 receive divider

10 kΩ

The upper resistor that safely reduces the ESP32 transmit signal for the SIM900 receive pin.

1

50 kΩ resistor for SIM900 receive divider

50 kΩ

The lower resistor that completes the SIM900 receive signal voltage divider.

1

99.6 kΩ battery-divider resistor

99.6 kΩ

The upper resistor that scales battery voltage down for the ESP32 measurement pin.

1

50 kΩ battery-divider resistor

50 kΩ

The lower resistor that completes the safe battery-voltage measuring divider.

1

470 µF low-ESR electrolytic capacitor, 6.3 V or higher

470 µF

A capacitor placed beside the cellular modem to supply its short high-current radio bursts.

1

18650 Li-ion Cell

18650 lithium-ion cell, nominal 3.7 V, ~2500 mAh. Common for higher-capacity portable / battery-bank style projects; needs a holder and protection / charger circuit.

1

TP4056 Li-Ion/LiPo charger module with protection

TP4056 single-cell Li-Ion/LiPo linear charger module, 5V USB input, 1A charge current (programmable). Common variants ship with DW01 protection. Pair with battery_lipo_storage for the cell.

1

3.3v Buck Boost

TI TPS63030/TPS63031 high-efficiency single-inductor buck-boost converter family with 1A switches. Used to hold a regulated rail when battery voltage crosses above and below the target output.

1

3.3 V high-side load-switch module

A small switch module that turns the weather-sensor power rail on only while measurements are taken.

1

FC-28 compatible 3.3 V analog soil-moisture sensor

A probe-and-board sensor that reports how wet the soil is at the 0 cm depth.

1

FC-28 compatible 3.3 V analog soil-moisture sensor

A probe-and-board sensor that reports how wet the soil is at the 15 cm depth.

1

FC-28 compatible 3.3 V analog soil-moisture sensor

A probe-and-board sensor that reports how wet the soil is at the 30 cm depth.

1

FC-28 compatible 3.3 V analog soil-moisture sensor

A probe-and-board sensor that reports how wet the soil is at the 45 cm depth.

1

FC-28 compatible 3.3 V analog soil-moisture sensor

A probe-and-board sensor that reports how wet the soil is at the 60 cm depth.

1

FC-28 compatible 3.3 V analog soil-moisture sensor

A probe-and-board sensor that reports how wet the soil is at the 75 cm depth.

1

FC-28 compatible 3.3 V analog soil-moisture sensor

A probe-and-board sensor that reports how wet the soil is at the 90 cm depth.

1

GL5528 photoresistor

A light-sensitive resistor that lets the station estimate daylight level.

Assemble it in 7 steps

1. Build the protected battery supply

Connect the 18650 cell positive terminal to charger_tp4056 B+ and its negative terminal to B-. Connect charger_tp4056 OUT+ to esp_regulator VIN and OUT- to esp_regulator GND. Set and verify the regulator output is 3.3 V before connecting it to the ESP32 3V3 and GND pins.

  • Charge through the TP4056 USB input; use only a protected single 18650 cell.
  • Use short, thicker wires for the battery and regulator connections.
  • Never connect the 18650 cell directly to the ESP32 3V3 pin — an overcharged cell can damage the board.
  • Do not reverse the battery leads; reversed battery wiring can damage the charger and cause the cell to overheat.

2. Give the GSM module its own strong supply wires

Connect sim900_1 VCC to charger_tp4056 OUT+ and sim900_1 GND to charger_tp4056 OUT-. Put sim_bulk_capacitor directly across the same two SIM900 power points: its + leg goes to VCC and its marked - leg goes to GND. Connect SIM900 TX to ESP32 GPIO16. Make the receive voltage divider: ESP32 GPIO17 to sim_rx_series End 1, sim_rx_series End 2 to SIM900 RX, and sim_rx_shunt End 1 also to SIM900 RX with its End 2 to GND.

  • Place the 470 µF capacitor physically close to the SIM900 VCC and GND pins.
  • The two resistors reduce the ESP32’s 3.3 V transmit signal for the SIM900 receive pin.
  • Do not power the SIM900 from the ESP32 3V3 pin; its short radio bursts can pull far more current than the board supply can safely provide.
  • Make sure the capacitor stripe marking is on the GND side — reversed electrolytic capacitors can fail violently.

3. Add the switched sensor supply

Connect sensor_power_switch VIN to the ESP32 3V3 pin and GND to ESP32 GND. Connect its EN pin to GPIO14. Its VOUT pin is the switched 3.3 V supply used by every sensor in the following steps.

  • Keep all sensor ground wires connected to the ESP32 GND rail even though their positive supply is switched off during sleep.
  • Make sure VIN and VOUT are not swapped — swapped connections can leave the sensors permanently off or damage a module.

4. Wire the air, pressure, light, and rain sensors

Connect dht21_1 VCC to switched VOUT (power), GND to GND (ground), and DATA to GPIO4 (signal). Connect bmp280_1 VCC to switched VOUT (power), GND to GND (ground), SCL to GPIO22 (clock), SDA to GPIO21 (data), CSB to GND (selects the two-wire I²C mode), and SDO to GND (selects address 0x76). For the light divider, connect ldr_1 Terminal 1 to switched VOUT; join its Terminal 2 with ldr_fixed_resistor End 1 and GPIO34; connect ldr_fixed_resistor End 2 to GND. Connect rain_sensor VCC to switched VOUT (power), GND to GND (ground), and AO to GPIO36 (signal).

  • All sensor modules must run at 3.3 V so their output voltage stays safe for the ESP32.
  • Keep the BMP280 dry inside the enclosure but give it small air vents so it can measure air pressure.
  • Do not connect a 5 V rain or soil module analog output to an ESP32 pin; voltage above 3.3 V can damage it.
  • Make sure VCC and GND are not swapped on the BMP280 — swapped power can damage the sensor.

5. Wire the seven temperature probes

Connect every DS18B20 probe in parallel: all VCC wires to switched VOUT, all GND wires to GND, and all DATA wires together on GPIO27. Connect onewire_pullup between switched VOUT and the same GPIO27 data junction; this is the 4.7 kΩ pull-up resistor.

  • Label each probe by depth before burying it: 0, 15, 30, 45, 60, 75, and 90 cm.
  • Waterproof probes are needed for direct soil placement.
  • Do not omit the 4.7 kΩ resistor; without it the shared temperature wire will give unreliable or missing readings.

6. Wire the soil-moisture multiplexer

Connect soil_mux VCC to switched VOUT, GND to GND, and EN to GND. Connect S0 to GPIO25, S1 to GPIO26, S2 to GPIO32, S3 to GPIO33, and SIG to GPIO39. For each soil sensor, connect VCC to switched VOUT and GND to GND. Connect their AO wires in order to Y0, Y1, Y2, Y3, Y4, Y5, and Y6 on the multiplexer.

  • Use the same depth labels as the matching DS18B20 probes so the dashboard values remain meaningful.
  • Capacitive soil probes last much longer in wet ground than exposed-metal FC-28 style probes.
  • Do not power soil-sensor modules from 5 V; their analog output may rise above the ESP32-safe 3.3 V limit.

7. Add the battery measurement divider and inspect the build

Connect battery_divider_top End 1 to charger_tp4056 OUT+. Join battery_divider_top End 2, battery_divider_bottom End 1, and ESP32 GPIO35. Connect battery_divider_bottom End 2 to GND. Recheck that every module has a ground return, then connect the regulator’s 3.3 V output to ESP32 3V3 and its ground to ESP32 GND.

  • The resistor divider makes the battery voltage safe for GPIO35.
  • Keep the ESP32 USB port available; it is used for the first firmware deployment.
  • Do not connect the charger OUT+ point directly to GPIO35 — the divider is required to keep battery voltage away from the ESP32 input.

Review all connections

1. Connections between "battery_18650" and "ESP32"

Functionbattery_18650ESP32
power+V → TP4056 Li-Ion/LiPo charger module with protection B+EXT
groundGND → TP4056 Li-Ion/LiPo charger module with protection B-EXT

2. Connections between "charger_tp4056" and "ESP32"

Functioncharger_tp4056ESP32
powerIN+ → 5 V USB charging inputEXT
groundIN- → USB charging-input groundEXT
powerOUT+ → 3.3v Buck Boost VINEXT
groundOUT- → 3.3v Buck Boost GNDEXT

3. Connections between "esp_regulator" and "ESP32"

Functionesp_regulatorESP32
powerVOUT3V3

4. Connections between "sensor_power_switch" and "ESP32"

Functionsensor_power_switchESP32
powerVIN3V3
groundGNDGND
digitalENGPIO 14

5. Connections between "dht21_1" and "ESP32"

Functiondht21_1ESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
groundGNDGND
dataDATAGPIO 4

6. Connections between "ds18b20_soil_array" and "ESP32"

Functionds18b20_soil_arrayESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
groundGNDGND
dataDATA → 4.7k Pull Up Resistor DATA_LINEEXT

7. Connections between "onewire_pullup" and "ESP32"

Functiononewire_pullupESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
dataDATA_LINEGPIO 27
groundGNDGND

8. Connections between "bmp280_1" and "ESP32"

Functionbmp280_1ESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
groundGNDGND
i2cSCLGPIO 22
i2cSDAGPIO 21
groundCSBGND
groundSDOGND

9. Connections between "soil_mux" and "ESP32"

Functionsoil_muxESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
groundGNDGND
digitalS0GPIO 25
digitalS1GPIO 26
digitalS2GPIO 32
digitalS3GPIO 33
groundENGND
analogSIGGPIO 39

10. Connections between "rain_sensor" and "ESP32"

Functionrain_sensorESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
groundGNDGND
analogAOGPIO 36

11. Connections between "ldr_1" and "ESP32"

Functionldr_1ESP32
powerTerminal 1 → 3.3 V high-side load-switch module VOUTEXT
analogTerminal 2 → 10 kΩ fixed resistor End 1EXT

12. Connections between "ldr_fixed_resistor" and "ESP32"

Functionldr_fixed_resistorESP32
analogEnd 1GPIO 34
groundEnd 2GND

13. Connections between "soil_sensor_0" and "ESP32"

Functionsoil_sensor_0ESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
groundGNDGND
analogAO → 74HC4067 16-channel analog multiplexer module Y0EXT

14. Connections between "soil_sensor_15" and "ESP32"

Functionsoil_sensor_15ESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
groundGNDGND
analogAO → 74HC4067 16-channel analog multiplexer module Y1EXT

15. Connections between "soil_sensor_30" and "ESP32"

Functionsoil_sensor_30ESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
groundGNDGND
analogAO → 74HC4067 16-channel analog multiplexer module Y2EXT

16. Connections between "soil_sensor_45" and "ESP32"

Functionsoil_sensor_45ESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
groundGNDGND
analogAO → 74HC4067 16-channel analog multiplexer module Y3EXT

17. Connections between "soil_sensor_60" and "ESP32"

Functionsoil_sensor_60ESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
groundGNDGND
analogAO → 74HC4067 16-channel analog multiplexer module Y4EXT

18. Connections between "soil_sensor_75" and "ESP32"

Functionsoil_sensor_75ESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
groundGNDGND
analogAO → 74HC4067 16-channel analog multiplexer module Y5EXT

19. Connections between "soil_sensor_90" and "ESP32"

Functionsoil_sensor_90ESP32
powerVCC → 3.3 V high-side load-switch module VOUTEXT
groundGNDGND
analogAO → 74HC4067 16-channel analog multiplexer module Y6EXT

20. Connections between "sim900_1" and "ESP32"

Functionsim900_1ESP32
powerVCC → TP4056 Li-Ion/LiPo charger module with protection OUT+EXT
groundGND → TP4056 Li-Ion/LiPo charger module with protection OUT-EXT
uartTXGPIO 16
uartRX → 10 kΩ resistor for SIM900 receive divider End 2EXT

21. Connections between "sim_rx_series" and "ESP32"

Functionsim_rx_seriesESP32
dataEnd 1GPIO 17

22. Connections between "sim_rx_shunt" and "ESP32"

Functionsim_rx_shuntESP32
dataEnd 1 → SIM900 GSM/GPRS module RXEXT
groundEnd 2GND

23. Connections between "sim_bulk_capacitor" and "ESP32"

Functionsim_bulk_capacitorESP32
power+ → SIM900 GSM/GPRS module VCCEXT
ground-GND

24. Connections between "battery_divider_top" and "ESP32"

Functionbattery_divider_topESP32
analogEnd 1 → TP4056 Li-Ion/LiPo charger module with protection OUT+EXT
analogEnd 2 → 50 kΩ battery-divider resistor End 1EXT

25. Connections between "battery_divider_bottom" and "ESP32"

Functionbattery_divider_bottomESP32
analogEnd 1GPIO 35
groundEnd 2GND

Deploy the firmware

#include <Arduino.h>
#include <DHT.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_BMP280.h>
#include <LittleFS.h>
#include "esp_sleep.h"

// Field weather station: reads sensors, queues measurements locally, then posts by SIM900 GPRS.

// Forward declarations
void selectMuxChannel(uint8_t channel);
int averageAnalog(int pin, int samples);
float batteryVoltage();
int batteryPercent(float volts);
void clearModem();
bool waitFor(const char *text, uint32_t timeout);
bool at(const String &command, const char *expect, uint32_t timeout);
bool modemReady();
bool startGprs();
bool postMeasurement(const String &body);
bool beginQueue();
void queueMeasurement(const String &body);
bool firstQueued(String &line);
bool removeFirstQueued();
void sendQueue();

constexpr int STATION_ID = 22;
constexpr uint64_t SLEEP_US = 30ULL * 60ULL * 1000000ULL;
constexpr char APN[] = "izi.me";
constexpr char POST_URL[] = "http://37.151.253.210:2409/dashboard/index.php";
constexpr char QUEUE_FILE[] = "/post_queue.txt";

constexpr int SENSOR_POWER_PIN = 14;
constexpr int DHT_PIN = 4;
constexpr int ONE_WIRE_PIN = 27;
constexpr int BMP_SDA_PIN = 21;
constexpr int BMP_SCL_PIN = 22;
constexpr int PHOTO_PIN = 34;
constexpr int RAIN_PIN = 36;
constexpr int SOIL_MUX_PIN = 39;
constexpr int MUX_S0_PIN = 25;
constexpr int MUX_S1_PIN = 26;
constexpr int MUX_S2_PIN = 32;
constexpr int MUX_S3_PIN = 33;
constexpr int BATTERY_PIN = 35;
constexpr int SIM_RX_PIN = 16; // ESP32 RX receives SIM900 TX
constexpr int SIM_TX_PIN = 17; // ESP32 TX, through the resistor divider, drives SIM900 RX

constexpr float BATTERY_R_TOP_KOHM = 99.6f;
constexpr float BATTERY_R_BOTTOM_KOHM = 50.0f;
constexpr float ADC_REFERENCE = 3.3f;
constexpr float ADC_CORRECTION = 1.107f;
constexpr int SOIL_DRY = 4095;
constexpr int SOIL_WET = 600;
constexpr int RAIN_DRY = 4095;
constexpr int RAIN_WET = 0;

DHT dht(DHT_PIN, DHT21);
OneWire oneWire(ONE_WIRE_PIN);
DallasTemperature soilTemps(&oneWire);
Adafruit_BMP280 bmp;
HardwareSerial sim900(2);

DeviceAddress temperatureAddresses[7] = {
  {0x28,0x33,0x59,0x34,0x00,0x00,0x00,0x8A},
  {0x28,0x0D,0x23,0x34,0x00,0x00,0x00,0xA4},
  {0x28,0xBF,0x36,0x34,0x00,0x00,0x00,0x63},
  {0x28,0x8B,0x1F,0x36,0x00,0x00,0x00,0x5E},
  {0x28,0xA2,0x7A,0x38,0x00,0x00,0x00,0xA8},
  {0x28,0xE9,0x27,0x36,0x00,0x00,0x00,0x49},
  {0x28,0xF0,0x48,0x38,0x00,0x00,0x00,0xEF}
};

void selectMuxChannel(uint8_t channel) {
  digitalWrite(MUX_S0_PIN, channel & 1);
  digitalWrite(MUX_S1_PIN, (channel >> 1) & 1);
  digitalWrite(MUX_S2_PIN, (channel >> 2) & 1);
  digitalWrite(MUX_S3_PIN, (channel >> 3) & 1);
}

int averageAnalog(int pin, int samples = 8) {
  uint32_t total = 0;
  for (int i = 0; i < samples; ++i) { total += analogRead(pin); delay(5); }
  return total / samples;
}

float batteryVoltage() {
  float adc = averageAnalog(BATTERY_PIN, 20);
  return (adc / 4095.0f) * ADC_REFERENCE * ADC_CORRECTION *
         ((BATTERY_R_TOP_KOHM + BATTERY_R_BOTTOM_KOHM) / BATTERY_R_BOTTOM_KOHM);
}

int batteryPercent(float volts) {
  return constrain((int)((volts - 3.0f) * 100.0f / 1.2f), 0, 100);
}

void clearModem() { while (sim900.available()) sim900.read(); }

bool waitFor(const char *text, uint32_t timeout) {
  String reply;
  uint32_t started = millis();
  while (millis() - started < timeout) {
    while (sim900.available()) {
      char c = (char)sim900.read();
      Serial.write(c);
      reply += c;
      if (reply.indexOf(text) >= 0) return true;
      if (reply.indexOf("ERROR") >= 0) return false;
      if (reply.length() > 400) reply.remove(0, 200);
    }
    delay(1);
  }
  return false;
}

bool at(const String &command, const char *expect, uint32_t timeout) {
  clearModem();
  sim900.println(command);
  return waitFor(expect, timeout);
}

bool modemReady() {
  for (int i = 0; i < 3; ++i) if (at("AT", "OK", 2000)) return true;
  return false;
}

bool startGprs() {
  if (!at("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"", "OK", 5000)) return false;
  if (!at(String("AT+SAPBR=3,1,\"APN\",\"") + APN + "\"", "OK", 5000)) return false;
  if (!at("AT+SAPBR=1,1", "OK", 60000)) {
    if (!at("AT+SAPBR=2,1", "+SAPBR: 1,1", 5000)) return false;
  }
  return true;
}

bool postMeasurement(const String &body) {
  if (!modemReady() || !startGprs()) return false;
  at("AT+HTTPTERM", "OK", 2000);
  bool ok = at("AT+HTTPINIT", "OK", 5000) &&
            at("AT+HTTPPARA=\"CID\",1", "OK", 5000) &&
            at(String("AT+HTTPPARA=\"URL\",\"") + POST_URL + "\"", "OK", 10000) &&
            at("AT+HTTPPARA=\"CONTENT\",\"application/x-www-form-urlencoded\"", "OK", 5000);
  if (ok) {
    clearModem();
    sim900.print("AT+HTTPDATA="); sim900.print(body.length()); sim900.println(",10000");
    ok = waitFor("DOWNLOAD", 5000);
    if (ok) { sim900.print(body); ok = waitFor("OK", 10000); }
    if (ok) { clearModem(); sim900.println("AT+HTTPACTION=1"); ok = waitFor("+HTTPACTION: 1,200", 60000); }
  }
  at("AT+HTTPTERM", "OK", 5000);
  at("AT+SAPBR=0,1", "OK", 10000);
  return ok;
}

bool beginQueue() {
  if (!LittleFS.begin(true)) return false;
  if (!LittleFS.exists(QUEUE_FILE)) { File f = LittleFS.open(QUEUE_FILE, FILE_WRITE); if (!f) return false; f.close(); }
  return true;
}

void queueMeasurement(const String &body) {
  File f = LittleFS.open(QUEUE_FILE, FILE_APPEND);
  if (f) { f.println(body); f.close(); }
}

bool firstQueued(String &line) {
  File f = LittleFS.open(QUEUE_FILE, FILE_READ);
  if (!f || !f.available()) { if (f) f.close(); return false; }
  line = f.readStringUntil('\n'); f.close(); line.trim(); return line.length() > 0;
}

bool removeFirstQueued() {
  File input = LittleFS.open(QUEUE_FILE, FILE_READ);
  File output = LittleFS.open("/queue_tmp.txt", FILE_WRITE);
  if (!input || !output) return false;
  input.readStringUntil('\n');
  while (input.available()) output.println(input.readStringUntil('\n'));
  input.close(); output.close();
  LittleFS.remove(QUEUE_FILE);
  return LittleFS.rename("/queue_tmp.txt", QUEUE_FILE);
}

void sendQueue() {
  String body;
  while (firstQueued(body)) {
    if (!postMeasurement(body)) break;
    if (!removeFirstQueued()) break;
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(SENSOR_POWER_PIN, OUTPUT);
  digitalWrite(SENSOR_POWER_PIN, HIGH);
  pinMode(MUX_S0_PIN, OUTPUT); pinMode(MUX_S1_PIN, OUTPUT);
  pinMode(MUX_S2_PIN, OUTPUT); pinMode(MUX_S3_PIN, OUTPUT);
  analogReadResolution(12);
  analogSetAttenuation(ADC_11db);
  delay(1500); // sensors need time to receive stable 3.3 V

  dht.begin();
  soilTemps.begin();
  Wire.begin(BMP_SDA_PIN, BMP_SCL_PIN);
  bool bmpFound = bmp.begin(0x76, &Wire);

  float airHumidity = dht.readHumidity();
  float airTemp = dht.readTemperature();
  float pressureMmHg = NAN;
  if (bmpFound) { bmp.setSampling(Adafruit_BMP280::MODE_FORCED); bmp.takeForcedMeasurement(); pressureMmHg = bmp.readPressure() / 100.0f * 0.750062f; }

  float soilTemperature[7];
  soilTemps.requestTemperatures();
  delay(750);
  for (int i = 0; i < 7; ++i) soilTemperature[i] = soilTemps.getTempC(temperatureAddresses[i]);

  int soilHumidity[7];
  for (int i = 0; i < 7; ++i) {
    selectMuxChannel(i); delay(100);
    soilHumidity[i] = constrain(map(averageAnalog(SOIL_MUX_PIN, 5), SOIL_DRY, SOIL_WET, 0, 100), 0, 100);
  }
  int light = constrain(map(averageAnalog(PHOTO_PIN), 4095, 0, 0, 100), 0, 100);
  int rain = constrain(map(averageAnalog(RAIN_PIN), RAIN_DRY, RAIN_WET, 0, 100), 0, 100);
  float volts = batteryVoltage();

  String body = "station_id=" + String(STATION_ID);
  body += "&temperature_air=" + String(airTemp, 1);
  body += "&humidity_air=" + String(airHumidity, 1);
  body += "&pressure=" + String(pressureMmHg, 1);
  body += "&illumination=" + String(light);
  const int depth[] = {0,15,30,45,60,75,90};
  for (int i = 0; i < 7; ++i) {
    body += "&humidity_soil_" + String(depth[i]) + "=" + String(soilHumidity[i]);
    body += "&temperature_soil_" + String(depth[i]) + "=" + String(soilTemperature[i], 1);
  }
  body += "&precipitation=" + String(rain);
  body += "&battery=" + String(batteryPercent(volts));
  body += "&version_firmware=1.0.0";

  Serial.println(body);
  sim900.begin(9600, SERIAL_8N1, SIM_RX_PIN, SIM_TX_PIN);
  if (beginQueue()) { queueMeasurement(body); sendQueue(); }

  digitalWrite(SENSOR_POWER_PIN, LOW);
  esp_sleep_enable_timer_wakeup(SLEEP_US);
  delay(100);
  esp_deep_sleep_start();
}

void loop() {}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik