Guide no. 15
From the number the converter returns to degrees, volts and percent moisture. With a soil sensor calibrated on two points and an analog temperature sensor, both read with the ADS1115.
By Ada Intelisano · informaticait.it technical team
- Every conversion takes two steps: from the number to volts (depends on the converter) and from volts to the quantity (depends on the sensor).
- If the datasheet gives a formula, use it: the TMP36 gives 0.5 V at 0 °C and 10 mV per degree.
- If there is no formula, you calibrate on two points: read the soil sensor in the air (0 %) and in water (100 %), then draw the straight line.
- The conversion does not make the sensor more accurate: 16 bits on a ±3 °C TMP36 are still ±3 °C.
Step 1: from the number to volts
The converter returns an integer. To get volts you multiply it by the value of one step, that is the full scale divided by the number of levels:
| Converter | One step is worth | Volts = |
|---|---|---|
| Arduino UNO, 5 V reference | 5 ÷ 1024 ≈ 4.9 mV | reading × 5 ÷ 1024 |
| ESP32 (Arduino) | the chip works it out with its calibration | analogReadMilliVolts(pin) ÷ 1000 |
ADS1115, GAIN_ONE (±4.096 V) | 4.096 ÷ 32768 = 0.125 mV | ads.computeVolts(reading) |
Step 2: from volts to the quantity
With the datasheet formula. The Analog Devices TMP36 gives a voltage proportional to temperature: 500 mV at 0 °C and 10 mV for every degree, so T = (V − 0.5) × 100. At 25 °C the output is 750 mV. It runs on 2.7 to 5.5 V and draws less than 50 µA.
With two-point calibration. The DFRobot SEN0193 capacitive sensor gives a voltage that drops as moisture rises, but every unit is a bit different and there is no formula. DFRobot explains how to do it: read the sensor in the air (dry soil, 0 %) and in a glass of water without going past the line (100 %). Between the two points you draw a straight line: moisture = (Vair − V) ÷ (Vair − Vwater) × 100.
Complete example with the ADS1115
Channel A0 reads 13520 → 13520 × 0.125 mV = 1.690 V → moisture = (2.54 − 1.69) ÷ (2.54 − 1.27) × 100 = 67 %.
Channel A1 (TMP36) reads 5960 → 5960 × 0.125 mV = 0.745 V → T = (0.745 − 0.5) × 100 = 24.5 °C.
The capacitive soil sensor
DFRobot SEN0193: 98 × 23 mm, 3.3 to 5.5 V supply (it has its own regulator), 3-pin connector A, +, −. The components on the upper part are not waterproof: push it into the soil only up to the white line. DFRobot warns that the reading also changes with depth and with how compact the soil is: the calibration holds for the way you install it.
Wiring with the ESP32 and the ADS1115
Everything at 3.3 V: the ADS1115, the soil sensor and the TMP36 take their power from the ESP32's 3V3. The soil sensor goes to input A0, the middle output of the TMP36 to A1. With the Arduino UNO you use 5 V and the same sketch.
The sketch
// From bits to real values with the ADS1115: // A0 = capacitive soil moisture sensor (DFRobot SEN0193): two-point calibration, air and water // A1 = TMP36 analog temperature sensor: 10 mV per degree, 500 mV at 0 degrees // Same sketch for Arduino UNO and ESP32. Library: Adafruit ADS1X15. #include <Adafruit_ADS1X15.h> Adafruit_ADS1115 ads; // Calibration: write YOUR two readings in volts here (see the guide, step 2). // These are the values from the DFRobot example (520 and 260 on Arduino UNO, that is 2.54 V and 1.27 V): // every sensor is a little different, so measure yours. const float V_AIR = 2.54; // dry sensor, in the air const float V_WATER = 1.27; // sensor in a glass of water, not past the line void setup() { Serial.begin(115200); if (!ads.begin(0x48)) { Serial.println("ADS1115 not found"); while (true) delay(1000); } ads.setGain(GAIN_ONE); // +/-4.096 V full scale, 1 bit = 0.125 mV } void loop() { // 1) bits -> volts float vSoil = ads.computeVolts(ads.readADC_SingleEnded(0)); float vTmp36 = ads.computeVolts(ads.readADC_SingleEnded(1)); // 2) volts -> real value // moisture: straight line through the two calibration points (more water = lower voltage) float moisture = (V_AIR - vSoil) / (V_AIR - V_WATER) * 100.0; moisture = constrain(moisture, 0.0, 100.0); // temperature: formula from the TMP36 datasheet float tempC = (vTmp36 - 0.5) * 100.0; Serial.print("Soil: "); Serial.print(vSoil, 3); Serial.print(" V = "); Serial.print(moisture, 0); Serial.print(" % TMP36: "); Serial.print(vTmp36, 3); Serial.print(" V = "); Serial.print(tempC, 1); Serial.println(" degrees C"); delay(1000); }
Calibrate first: leave the sensor in the air and note the voltage printed, then put it in a glass of water up to the line and note the second one. Write the two values in V_AIR and V_WATER and upload the sketch again.
Other sensors, other formulas
| Sensor | From volts to quantity | Good to know |
|---|---|---|
| TMP36 | T = (V − 0.5) × 100 | 2.7–5.5 V; in the TO-92 package (grade G) the maximum error is ±3 °C at 25 °C |
| LM35 | T = V × 100 (0 V at 0 °C) | needs at least 4 V of supply: with the ESP32 at 3.3 V it is outside its specifications |
| NTC 10 kΩ, B 3950 | resistance from the divider, then the Beta equation: 1/T = 1/T0 + ln(R/R0)/B (T in kelvin, T0 = 298.15 K) | away from 25–50 °C the Beta formula drifts from the manufacturer's table (calculated on the Adafruit 4890 table: about 0.5 °C at 0 °C, over 2 °C at −40 °C); “3950” thermistors from different brands do not have the same curve |
| Voltage divider | Vin = V × (R1 + R2) ÷ R2 | for voltages higher than the converter's supply, for example a battery |
Common mistakes
| What happens | Why | Fix |
|---|---|---|
| Moisture above 100 % or below 0 % | the reading falls outside the two calibration points | clamp the result (constrain) or calibrate again |
| The soil sensor fails after a few weeks | pushed in past the line: the electronics at the top are not waterproof | respect the line; DFRobot suggests heat-shrink tubing |
| Different readings when you move the sensor | depth and soil compactness change the output | calibrate the sensor in the position where it will stay |
| LM35 reading wrong with the ESP32 | powered at 3.3 V, below the 4 V minimum | use the TMP36, which works from 2.7 V |
| Temperature “accurate” to a hundredth but wrong | the ADC resolution is not the sensor's accuracy | compare with the ±°C in the datasheet |
| NTC reading a few tenths too high | self-heating: the divider current warms it up | power the divider only during the reading |
Useful products
Other guides
- Resistor colour code and tolerances
- N-channel and P-channel MOSFETs
- Bipolar transistor (BJT) or MOSFET?
- Relays: how to drive them and how much current they draw
- THT and SMD/SMT components
- XIAO board pinouts
- LEDs: forward voltage and series resistor
- The multimeter: measuring voltage and current
- Wemos D1 mini: first steps
- MOSFET module with Arduino and ESP32
- Relay module with Arduino and ESP32
- 3D printing filaments: PLA, PETG and fibre-filled versions
- Analog and digital: the differences
- ADS1115: 16-bit converter with Arduino and ESP32
- DS3231 RTC clock with Arduino and ESP32
- Linear or switching regulator: which one to choose
- Step-down, step-up and buck-boost: choosing the right module
- Voltage divider: reading voltages higher than 5 V or 3.3 V
- Pull-up, pull-down and bounce: wiring a button properly
- 3.3 V and 5 V logic levels: getting an ESP32 and an Arduino to talk
- The I²C bus: addresses, pull-ups and the scanner
- Lithium batteries: voltages, charging and protection
- ESP32 power consumption and deep sleep: how long the battery lasts
- Temperature sensors compared
- ESP32: which pins to use and which to avoid
- Getting started with the ESP32 in Arduino IDE 2 and PlatformIO
- SSD1306 OLED display and 16×2 I²C LCD with Arduino and ESP32
- PIR motion sensor and HC-SR04 ultrasonic sensor
- Date and time from the Internet with the ESP32: NTP, Italian time zone and daylight saving time
- MicroSD data logger: saving measurements to CSV
- Solar-powered ESP32: panel, charger and the numbers for Italy
- Web server on the ESP32: control a relay from your phone
- Your first LED: wire it up and make it blink
- The button: let Arduino decide
- The potentiometer: turn a knob and read its value
- Soft light: adjusting the brightness of an LED
- The buzzer: playing notes and melodies
- The traffic light: three LEDs in sequence
- The thermometer: reading the temperature with the TMP36
- The servo motor: moving an arm with Arduino
- The night light: the photoresistor and your first decision
- The relay: switching on a light bulb with Arduino
- The LCD display: making Arduino write
- The electronic dice: your first complete project
- The RGB LED: every color from three lights
- The joystick: two-axis control with a shield
- The LED ring: twelve colors from a single pin
- The DC motor: driving it with a MOSFET
- Soil moisture: the plant that asks for water
- The stepper motor: turn exactly as far as you want
- The parking sensor: faster and faster beeps
- The 7-segment display: big, bright numbers
- Automatic watering: the plant that waters itself
- The keypad: a lock with a secret code
- Networked weather station: values and chart on your phone
- A Telegram bot on the ESP32: your home from your phone
- MQTT with the ESP32: the language of connected devices
- Updating the ESP32 over Wi-Fi (OTA)
- ESP-NOW: two ESP32 boards talking without a router
- WebSocket with the ESP32: the LED that follows the slider
- Readings on a Google sheet, with deep sleep
- WiFiManager: choose the Wi-Fi from your phone
- Bluetooth LE with the ESP32: the phone without a network
- The camera in your browser with the ESP32-S3
- A portable Bluetooth speaker you build yourself
- Bluetooth desktop speakers, 2 × 20 W
- Stereo MP3 player with two buttons
- The shelf that plays music: a Visaton exciter
- How to solder well
- How much does it draw? Current and power with the INA219
- Load-cell scale with the HX711
- CO2 traffic light: when to open the windows
- RFID card door opener
- GPS: track logger on microSD
- LoRa: how far does it go? A range test with two boards
- Battery weather sign with an e-paper display
- Web radio with an ESP32 and an I2S amplifier
- Getting started with the Raspberry Pi Pico 2 in MicroPython
- Your first amplifier: the LM386 and a speaker
- Talking Arduino: the DFR0760 speech synthesis module
- The spectrum of your music on an LED matrix
- Drawing with 64 LEDs: the 8×8 matrix with the MAX7219
- A real voltage: the 12-bit MCP4725 DAC
- The heartbeat on Arduino: an ECG with the AD8232