Low Power nRF24 Node
/*
ATtiny24/44/84 Pin map with CE_PIN 8 and CSN_PIN 7
Schematic provided and successfully tested by Carmine Pastore (https://github.com/Carminepz)
+-\/-+
nRF24L01 VCC, pin2 --- VCC 1|o |14 GND --- nRF24L01 GND, pin1
PB0 2| |13 AREF
PB1 3| |12 PA1
PB3 4| |11 PA2 --- nRF24L01 CE, pin3
PB2 5| |10 PA3 --- nRF24L01 CSN, pin4
PA7 6| |9 PA4 --- nRF24L01 SCK, pin5
nRF24L01 MOSI, pin7 --- PA6 7| |8 PA5 --- nRF24L01 MISO, pin6
+----+
*/
// CE and CSN are configurable, specified values for ATtiny85 as connected above
#define CE_PIN 8
#define CSN_PIN 7
#include "RF24.h"
#include <avr/sleep.h>
#include <avr/wdt.h>
RF24 radio(CE_PIN, CSN_PIN);
byte address[] = "KK123";
// attinycore pin ordering
const int in1 = 2;
const int in2 = 3;
const int led = 10;
const int idleTimeout = 1000;
const int deviceId = 1;
typedef struct {
int motor;
int target;
int mode;
} CommandPacket;
CommandPacket message;
CommandPacket messageStaging;
unsigned long lastMessage;
void setup() {
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(led, OUTPUT);
// Setup and configure rf radio
radio.begin(); // Start up the radio
radio.setPALevel(RF24_PA_LOW);
radio.setRetries(15, 0); // Max delay between retries & number of retries
radio.openReadingPipe(1, address);
radio.setAutoAck(false); // Ensure autoACK is enabled
radio.startListening(); // Start listening
// blink for 550ms to indicate startup
for (int i = 0; i < 5; i++) {
digitalWrite(led, HIGH);
delay(10);
digitalWrite(led, LOW);
delay(100);
}
ADCSRA &= ~(1 << ADEN); //Disable ADC, saves ~230uA
//Power down various bits of hardware to lower power usage
set_sleep_mode(SLEEP_MODE_PWR_DOWN); //Power down everything, wake up from WDT
}
void loop(void) {
if (radio.available()) {
while (radio.available()) {
radio.read( &messageStaging, sizeof(messageStaging) );
if (messageStaging.target == deviceId) {
message = messageStaging;
lastMessage = millis();
wdt_disable();
}
}
}
bool isIdle = (millis() - lastMessage) > idleTimeout;
if (isIdle) {
message.mode = 0;
message.motor = 0;
}
if (message.mode == 0) {
digitalWrite(led, LOW);
} else if (message.mode == 1) {
digitalWrite(led, HIGH);
} else {
if ((millis() / message.mode) % 2 == 0) {
digitalWrite(led, LOW);
} else {
digitalWrite(led, HIGH);
}
}
if (message.motor == 0) {
digitalWrite(in1, LOW);
digitalWrite(in2, LOW);
} else if (message.motor > 0) {
digitalWrite(in1, LOW);
analogWrite(in2, message.motor);
} else if (message.motor < 0) {
digitalWrite(in2, LOW);
analogWrite(in1, -message.motor);
}
if (isIdle) {
radio.stopListening();
radio.powerDown();
setup_watchdog(WDTO_2S); //Setup watchdog to go off after 1sec
sleep_mode(); //Go to sleep! Wake up 1sec later and check water
radio.powerUp();
radio.startListening();
delay(50); // some time to pick up messages
}
}
//This runs each time the watch dog wakes us up from sleep
ISR(WDT_vect) {
//Don't do anything. This is just here so that we wake up.
}
//Sets the watchdog timer to wake us up, but not reset
//0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms
//6=1sec, 7=2sec, 8=4sec, 9=8sec
//From: http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
void setup_watchdog(int timerPrescaler) {
if (timerPrescaler > 9 ) timerPrescaler = 9; //Limit incoming amount to legal settings
byte bb = timerPrescaler & 7;
if (timerPrescaler > 7) bb |= (1 << 5); //Set the special 5th bit if necessary
//This order of commands is important and cannot be combined
MCUSR &= ~(1 << WDRF); //Clear the watch dog reset
WDTCSR |= (1 << WDCE) | (1 << WDE); //Set WD_change enable, set WD enable
WDTCSR = bb; //Set new watchdog timeout value
WDTCSR |= _BV(WDIE); //Set the interrupt enable, this will keep unit from resetting after each int
}
include define