NRF24L01 ATTINY85 work together to measure and broadcast soil moisture sensor data
To make the experience fit your profile, pick a username and tell us what interests you.
We found and based on your interests.
This project was on hold for the whole winter for simple reason - no flowers during winter where I live. Now I'm trying to resurrect that and move forward as much as time allows.
So first of all, I've dropped Tiny85 and got Tiny84 for few reasons
- Although it is possible (proved before) to communicate with NRF24 chip using 3 or 4 pins, to optimize battery usage it is much better to have all 5 pins to work with.
- First approach to use of-the-shelf moisture sensor turns out to be power hungry solution. Therefore I dropped 555-chip based sensor and added built-in on the board. Tiny84 will generate waveform just as well.
- Idea of the whole setup is that each module will report to master node (on the photo above). When it is not available It would be nice to have 'offline' kind of way to get information update. Hence 3 LEDs onboard - red, yellowa and green. Their blinking should be a good clue of the actual sensor reading (when calibrated, clearly)
- First prototype of master node is prepared. Same NRF24 chip is there ("it is dead cheap" is the reason). But also WiFi connectivity added via mighty ESP8266. With possibility to switch to some ESP32 alternative in the future, It has 8 power MOSFET drivers to drive pumps, all individually adressable using onboard 74HC595 chip.
Having enough time I'd hope to make it work before winter comes :)
So idea of using this device in wild is to have it sleeping most of the time, then it should wake once in a while, do measuremrnt, send it out and go back to sleep. Therefore battery life will depend heavily on the power consumption in sleep mode, since wakup cycle can be as rare as once per few hours.
So for the first test i want to ensure that when PW_ON pin is low, all my peripherals are off and not drawing current. And here it is, not even a microamp when PW_ON is zero. That's a good start.
Next test I don't care for any of the periferials, they will be off in the sleep mode, now i need to make sure MCU draw as less current as possible.
Okay so here is the first attempt to use sleep mode
My fuses are
board_fuses.efuse = 0xFF board_fuses.hfuse = 0xDE board_fuses.lfuse = 0xF1
BOD set on 1.8V
main.h
#include <avr/io.h>
#define SLEEP_16ms 0
#define SLEEP_32ms 1
#define SLEEP_64ms 2
#define SLEEP_128ms 3
#define SLEEP_256ms 4
#define SLEEP_512ms 5
#define SLEEP_1024ms 6
#define SLEEP_2048ms 7
#define SLEEP_4096ms 8
#define SLEEP_8192ms 9
#define PIN_PWR_SW PINB3
#define PIN_PWR_SW_ _BV(PINB3)
void setup_watchdog(int mode);
void sleep(int mode);
main.c
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "main.h"
#include <Arduino.h>
void setup() {
DDRB = DDRB | PIN_PWR_SW_;
PORTB = 0x00;
}
void loop() {
PORTB |= PIN_PWR_SW_;
_delay_ms(1024);
PORTB &= ~PIN_PWR_SW_;
sleep(SLEEP_2048ms);
}
// =============== SLEEP MODE ===============
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
void sleep(int mode) {
setup_watchdog(mode);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode(); // System actually sleeps here
sleep_disable(); // System continues execution here when watchdog timed out
}
void setup_watchdog(int ii) {
uint8_t bb = ii & 7;
if (ii > 7)
bb |= (1 << 5);
bb |= (1 << WDCE);
int ww = bb;
MCUSR &= ~(1 << WDRF);
// start timed sequence
WDTCR |= (1 << WDCE) | (1 << WDE);
// set new watchdog timeout value
WDTCR = bb;
WDTCR |= _BV(WDIE);
}
This gives me two figures, (A) active mcu, soil sensor, nrf and (B) all off, mcu sleeping. Once again case (A) is non optimised, for now i case only for case (B).
(A) Active: 8mA,
(B) Sleep: 0,23mA
Good start.
Now by handbook i'll disable ADC during sleep mode:
void sleep(int mode) {
setup_watchdog(mode);
cbi(ADCSRA, ADEN); // switch Analog to Digitalconverter OFF
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode(); // System actually sleeps here
sleep_disable(); // System continues execution here when watchdog timed out
sbi(ADCSRA, ADEN); // switch Analog to Digitalconverter ON
}
(A) Active: 8mA,
(B) Sleep: 24uA
That's 10 times improvement compared to pervious setup.
Now there is a hint to disable BOD during sleep
I'll try to do it softwarewise
void sleep(int mode) {
setup_watchdog(mode);
cbi(ADCSRA, ADEN); // switch Analog to Digitalconverter OFF
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
cli(); // Disable Interrupts
sleep_bod_disable(); // Disable BOD
sei();
sleep_mode(); // System actually sleeps here
sleep_disable(); // System continues execution here when watchdog timed out
sbi(ADCSRA, ADEN); // switch Analog to Digitalconverter ON
}
Unfortunately makes no difference on current consumption, does Attiny85 supports it anyway?
So let's disable it completely
New fuses
board_fuses.efuse = 0xFF board_fuses.hfuse = 0xDF board_fuses.lfuse = 0xF1
And now i'm down to
(A) Active: 8mA,
(B) Sleep: 4,5uA
So another 5 times imprevement.
I tried also to set all pins to input mode, but this made no difference as well.
Now that's really the best I've managed so far. Datasheet says it should be under 1uA, and possibly as low as 0.2uA in my conditions, but i'm failing to get the same result. So if you know what I'm missing here, please help me out. Could it be the crappy multimeter...
Read more »Rev. A PCBs just arrived. As you would expect I've messed up pinout for nrf module. But still plan to do series of tests, and hopefully finalize fimware based on this design.
Next task would be a fight for power consumption, therefore i took some time to prepare tooling for that.
I've assmbled INA219 based power meter additionaly to standard multimeter tool. Later has better resolution for sleep mode currents (talking few uA here), first one i plan to use to capture consumption on longer runs.
I replaced 0.1 Ohm resistor with 1 Ohm to have 10 times better resolution, so instead of ±800uA it should be ±80uA. It is not quite enough for sleep mode capturing, but should give good enough overview on total power consumption on longer run.
Here is the firmware for it
#define SERIAL_SPEED 115200
#define DISPLAY_CONTRAST 120
#define DISPLAY_PIN_RST 3
#define DISPLAY_PIN_CE 4
#define DISPLAY_PIN_DC 5
#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include "main.h"
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
Adafruit_PCD8544 display = Adafruit_PCD8544(DISPLAY_PIN_DC, DISPLAY_PIN_CE, DISPLAY_PIN_RST);
#include <Adafruit_INA219.h>
Adafruit_INA219 ina219;
void setup()
{
Serial.begin(SERIAL_SPEED);
while (!Serial)
_delay_ms(1);
display.begin();
display.setContrast(DISPLAY_CONTRAST);
display.setTextSize(0);
display.setTextColor(BLACK);
if (!ina219.begin())
{
Serial.println("Failed to find INA219 chip");
while (1)
_delay_ms(10);
}
ina219.setCalibration_16V_400mA();
}
uint32_t last_measurement = 0;
void loop()
{
uint32_t started = millis();
update_power_display();
Serial.println();
// _delay_ms(100);
}
float total_mA_ms = 0.0;
void update_power_display() {
// Read voltage and current from INA219.
float shuntvoltage = ina219.getShuntVoltage_mV();
float busvoltage = ina219.getBusVoltage_V();
float current_mA = ina219.getCurrent_mA() / 10.;
if (current_mA < 0)
current_mA = 0;
// Compute load voltage, power, and milliamp-hours.
float loadvoltage = busvoltage + (shuntvoltage / 1000);
float power_mW = loadvoltage * current_mA;
display.clearDisplay();
display.setCursor(0, 0);
display.setTextColor(BLACK);
display.print(F("VCC, V= "));
display.setTextColor(WHITE, BLACK);
display.println(loadvoltage);
Serial.print(loadvoltage);
Serial.print('\t');
display.setTextColor(BLACK);
display.print(F("CUR,mA= "));
display.setTextColor(WHITE, BLACK);
display.println(current_mA);
Serial.print(current_mA);
Serial.print('\t');
display.setTextColor(BLACK);
display.print(F("PWR,mW= "));
display.setTextColor(WHITE, BLACK);
display.println(power_mW);
Serial.print(power_mW);
Serial.print('\t');
uint32_t passed_ms = millis() - last_measurement;
Serial.print(F("+"));
Serial.print(passed_ms);
Serial.print('\t');
last_measurement = millis();
uint32_t total_sec = last_measurement / 1000.0;
uint32_t sec = total_sec % 60;
uint32_t min = (total_sec - sec) / 60;
display.setTextColor(BLACK);
display.print(F("TIME = "));
display.setTextColor(WHITE, BLACK);
display.print(min);
display.setTextColor(BLACK);
display.print(F(":"));
display.setTextColor(WHITE, BLACK);
if (sec < 10) display.print(F("0"));
display.println(sec);
Serial.print(total_sec);
Serial.print('\t');
total_mA_ms += current_mA * passed_ms;
float total_mAs = total_mA_ms / 1000.; // 3600.0;
display.setTextColor(BLACK);
display.print(F("CP,mAs= "));
display.setTextColor(WHITE, BLACK);
display.println(total_mAs);
Serial.print(total_mAs);
Serial.print('\t');
// Serial.println();
display.display();
}
Started with library example I tried to reduce time between measurements to possible minimum, since total power use is calculated based on assumption that current doesn't change between measurements. Currently i have ~33 ms between probes, will see later on if this would be enough. Basically it should be at least one order of magnitude lower than wakup time of my MCU....
Read more »With previos step done i'm ready to assebmle final 3-pin setup
Using schematics taken from here, and recommendation from here i replaced resistor with 20K, and cap with 10nF.
No need to change library, it is already adjusted to work with 3-pin config, only need to initialize it with same pin numbers for CE and CSN, like this
#define NRF_CE 3
#define NRF_CSN 3
RF24 radio(NRF_CE, NRF_CSN);
Actual pin 3 will not be used by library and it is still available for Serial.
Pin 4 is connected to Analog soil moisture sensor and i'm ready to send out actual data.
Image is not much different from previous one, but in fact numbers are actual sensor readings.
Next step is to play around with energy saving and sleep modes. I still have couple more weeks before PCBs should arrive.
As mentioned before i started with simplest possible 5-pin connection between attiny85 and nrf24L01 and moving forward.
Today I have 4-pin configuration working, with nrf's CE pin connected to Vcc. What is more important, now when i have pin 3 of attiny85 for my disposal, i started Serial output there. So now transmission and Serial working at the same time and i'm able to debug anything!
I'm using SoftwareSerial library and often you'll see it is not working with other libs since tiny85 only have 2 timers and limited interrupts, but this time it works perfectly and i'm surprized to get away with it this easy.
On the background is my "reciever" that i've assembled yesterday. It helps me quickly check if transmission is working (oled is black otherwise). And treminal is connected to tiny85 now.
I'm adding schematics with a bit of explanation
Now to details.
Attiny85 5 pin configuration
Normally to connect NRF24L01 module you need 5 pins, 3 SPI + 2 control pins (CE,CSN). In that case module will work in it's perfect conditions, power consumption would be on minimum (since chip will be idling most of the time) and i have no more pins to use.
Attiny85 4 pin configuration
However plan is to connect external sensor, therefore i need to free up some space. Step one - CE pin will be tied to Vcc, and Vcc will be switched on and off when i need it via mosfet. This way i win no extra pins, but i have ability to switch on and of power to the soil moisture sensor and radio module at the same time using single pin.
Attiny85 3 pin configuration
Using this article and this library it is possible to win one more pin using multiplexing (in other words using single pin to simulate two, CSN and SPI_CLK in our case. However this setup is not that trivial to start and before going this way i'll need to start with 5 pin config and progress slowly to 3 pin.
Atmel328P receiver node
To test any configuration i need to have second node in constant monitoring mode. Here i'm not limited to attiny85 and I'd prefer to have something with Serial and even a oled screen. So here it is:
I slightly modified starter ping-pong example from RF24 library to add OLED printing and now i'm able to listen Attiny85 in 5-pin configuration from another breadboard.
To be continued...
Create an account to leave a comment. Already have an account? Log In.
Become a member to follow this project and never miss any updates
By using our website and services, you expressly agree to the placement of our performance, functionality, and advertising cookies. Learn More
I made something very similar to this. My solution was to use the sensor probe with the LMC555 cmos version of the 555, this can be powered directly using the output from an esp. I used a Wemos D1 Mini that is powered directly (not via the voltage regulator which is power hungry) with a 3.7V LiPo battery. This is also connected to a CN3791 solar charge controller and a small (70X70) solar panel so no concerns over the battery running out of power.. The ESP spends most of its time sleeping, on wakeup it connects to wifi, powers the moisture sensor and sends the reading via an http GET to the main controller. Then it goes back to sleep...