|
Altium DesignerAltium Designer
|
|
|
Microchip studio |
|
|
SolidWorksSolidWorks
|
Minimalistic Soil Moisture Sensor
Youtube video -----> https://youtu.be/oqFGDM0aIfc
https://www.youtube.com/@NickElectronics
Description:
If you often forget to water your plants, this is a must-have device for you—a soil moisture tracker based on a PCB capacitive sensor and an ATmega48 microcontroller. Powered by two AAA batteries connected in series, the device can operate for two years without needing a battery replacement.
Functionality:
The device remains in sleep mode most of the time and wakes up every 10 minutes to measure soil moisture. If the soil is dry, it blinks red and then goes back to sleep. If the soil is sufficiently moist, it simply returns to sleep without blinking to avoid disturbing the user. When the battery is low, the device also signals this by blinking twice if the soil is dry or three times if the soil is moist.
/*
* SoilSensor.c
*/
#define F_CPU 8000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <stdbool.h>
#include <util/delay.h>
void sleep(void);
void wakeup(void);
void WDT (void);
void PWM_ON (void);
void LED_ON (void);
void LED_OFF (void);
void ADC_ON (void);
uint8_t read_adc(void);
uint8_t vin = 0;
uint8_t sensor = 0;
uint8_t WDT_counter = 0; // each interrupt is 8 sec, so 600 sec is 75 interrupts
int main(void)
{
WDT(); //enable WDT
sleep(); // Go to sleep
while (1)
{
if(WDT_counter >= 1) // If it`s been 600 seconds, proceed with the routine
{
wakeup(); //Wake up from sleep, turn on all periphery
_delay_ms(500);
//Read batteries voltage. 1.1Vref internal source is used for the ADC
ADMUX = 0b11100110;
DDRD |= (1<<DDD1); // Connect voltage divider
vin = read_adc(); // 193 value for 3.3V on batteries. 117 value when battery is at 2.0V level (discharged).
DDRD &= ~(1<<DDD1); // Disconnect voltage divider
//Read moisture level. Vcc as a voltage reference is used. This allows to read kinda same values when battery discharges.
ADMUX = 0b01100111;
sensor = read_adc(); // Read moisture level from the filter
if(sensor >= 156) // Adjust to control moisture level. 203 is full dry. 110 is full wet
{
if(vin <= 117)// If battery is discharged blink twice 1 sec each time
{
LED_ON();
_delay_ms(1000); // Use delays, cause why not. I don`t run any other important stuff in parallel :D
LED_OFF();
_delay_ms(1000);
LED_ON();
_delay_ms(1000);
}
else // If battery is above 2.0V just blink once for 2 seconds
{
LED_ON();
_delay_ms(2000);
}
}
else // If soil is moist enough go to this else
{
if(vin <= 117) // Blink 3 times fast if battery is discharged
{
LED_ON();
_delay_ms(500);
LED_OFF();
_delay_ms(500);
LED_ON();
_delay_ms(500);
LED_OFF();
_delay_ms(500);
LED_ON();
_delay_ms(500);
}
else
{
LED_OFF(); // If battery is charged and soil is moist just keep LED OFF
}
}
sleep(); //Go to sleep after all routine is done
}
else // If it`s been less than 600 seconds, continue sleeping.
{
sleep_enable();
}
}
}
void PWM_ON (void)
{
// PWM
DDRD |= (1<<DDRD6);
PORTD &= ~(1<<PIND6);
TCCR0A |= (1 << COM0A0); // Set OC0A on Compare Match
TCCR0A &= ~(1 << COM0A1);
TCCR0A |= (1 << WGM01); // CTC mode, top OCRA
TCCR0B &= ~((1 << WGM02) | (1 << WGM00)) ;
OCR0A = 10;
TCCR0B |= (1 << CS00);
TCCR0B &= ~((1 << CS01) | (1 << CS02));
}
void PWM_OFF (void)
{
// PWM
DDRD &= ~(1<<DDRD6);
PORTD &= ~(1<<PIND6);
TCCR0A &= ~(1 << COM0A0); // Set OC0A on Compare Match
TCCR0A &= ~(1 << COM0A1);
TCCR0A |= (1 << WGM01); // CTC mode, top OCRA
TCCR0B &= ~((1 << WGM02) | (1 << WGM00)) ;
OCR0A = 0;
TCCR0B &= ~((1 << CS01) | (1 << CS02) | (1 << CS00));
}
void LED_ON (void)
{
DDRC |= (1<<DDRC2);
PORTC |= (1<<PINC2);
}
void LED_OFF (void)
{
DDRC &= ~(1<<DDRC2);
PORTC &= ~(1<<PINC2);
}
void ADC_ON (void)
{
DDRC &= ~(1<<DDRC1);
DDRE &= ~((1<<DDRE3) | (1<<DDRE2));
DIDR0=(0<<ADC5D) | (0<<ADC4D) | (0<<ADC3D) | (0<<ADC2D) | (0<<ADC1D) | (0<<ADC0D);
ADCSRA=(1<<ADEN) | (0<<ADSC) | (1<<ADATE) | (0<<ADIF) | (0<<ADIE) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
ADCSRB=(0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
}
void ADC_OFF (void)
{
DDRC &= ~(1<<DDRC1);
PORTC &= ~(1<<PINC1);
DDRE &= ~((1<<DDRE3) | (1<<DDRE2));
PORTE &= ~((1<<PINE3) | (1<<PINE2));
DIDR0=(0<<ADC5D) | (0<<ADC4D) | (0<<ADC3D) | (0<<ADC2D) | (0<<ADC1D) | (0<<ADC0D);
ADMUX = 0b00000000;
ADCSRA=(0<<ADEN) | (0<<ADSC) | (1<<ADATE) | (0<<ADIF) | (0<<ADIE) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
ADCSRB=(0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
}
void wakeup(void)
{
sleep_disable();
cli();
PWM_ON();
ADC_ON();
}
void sleep (void)
{
cli();
WDT_counter = 0;
PWM_OFF();
ADC_OFF();
LED_OFF();
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
sleep_enable();
sei(); //ensure interrupts enabled so we can wake up again
sleep_cpu();
}
void WDT (void)
{
cli();
// Enable WDT with 8s timeout
WDTCSR = (1 << WDCE) | (1 << WDE); // Enable WDT configuration
WDTCSR = (1 << WDP3) | (1 << WDP0); // Set prescaler for 8s timeout
// Enable WDT interrupt
WDTCSR |= (1 << WDIE); // Watchdog Interrupt Enable
sei(); // Enable interrupts
}
ISR(WDT_vect)
{
WDT_counter++;
}
uint8_t read_adc(void)
{
// Delay needed for the stabilization of the ADC input voltage
for(int delay = 0; delay < 100; delay++){}
// Start the AD conversion
ADCSRA|=(1<<ADSC);
// Wait for the AD conversion to complete
while ((ADCSRA & (1<<ADIF))==0);
ADCSRA|=(1<<ADIF);
return ADCH;//ADCH;
}
Minimalistic Soil Moisture Sensor
*PCBWay community is a sharing platform. We are not responsible for any design issues and parameter issues (board thickness, surface finish, etc.) you choose.
- Comments(0)
- Likes(5)
-
Nick Electronics
Oct 24,2025
-
Engineer
Sep 12,2025
-
Engineer
Aug 02,2025
-
Engineer
May 12,2025
-
_ Floppy Lab
Jan 20,2025
- 1 USER VOTES
- YOUR VOTE 0.00 0.00
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
-
10design
-
10usability
-
10creativity
-
10content
More by Nick Electronics
-
Tiny Volumetric Lamp
Wireless power transfer coils: https://aliexpi.com/WLjwDC motor: RF-300EA-1D390, or any motor with a...
-
Minimalistic Soil Moisture Sensor
Youtube video -----> https://youtu.be/oqFGDM0aIfchttps://www.youtube.com/@NickElectronicsDescript...
-
PCBWay 11th Anniversary Badge
Idea behind: The design of this badge is not only beautiful, but functional. There is a dragon const...
-
DIY LED Resin Sticker with Custom Electronics & 3D-Printed Case (3rd PCB)
DIY LED Resin Sticker with Custom Electronics & 3D-Printed Case (3rd PCB)Third board of the proj...
-
DIY LED Resin Sticker with Custom Electronics & 3D-Printed Case (2nd PCB)
Second board of the project DIY LED Resin Sticker with Custom Electronics & 3D-Printed Case (2nd...
-
DIY LED Resin Sticker with Custom Electronics & 3D-Printed Case
DIY LED Resin Sticker with Custom Electronics & 3D-Printed CaseYoutube video -----> https://y...
-
DIY LED Resin Sticker with Custom Electronics & 3D-Printed Case
DIY LED Resin Sticker with Custom Electronics & 3D-Printed CaseYoutube video -----> https://y...
-
-
AEL-2011 Power Supply Module
322 0 1 -
AEL-2011 50W Power Amplifier
296 0 1 -
-
-
Custom Mechanical Keyboard
565 0 0 -
Tester for Touch Screen Digitizer without using microcontroller
230 2 2 -
Audio reactive glow LED wristband/bracelet with NFC / RFID-Tags
236 0 1 -
-
-







