Last active
July 28, 2018 03:23
-
-
Save chris-pws/a249a2181dff57c1c4900667b47b093a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SPI.h> | |
#include "RF24.h" | |
//#include <SoftwareSerial.h> | |
#include <avr/sleep.h> | |
// Set packet data structure | |
struct data_t { | |
byte stn_id; | |
byte msg_type; | |
unsigned short msg_value; | |
unsigned long ts; | |
}; | |
data_t message; | |
void radioInit(uint8_t dir); | |
bool sendMsg(struct data_t msg, char retry = -1); | |
void setWdt(); | |
void clearWdt(); | |
uint16_t readVcc(uint8_t sample); | |
#define CHANNEL 68 | |
#define MSG_RETRY 5 | |
#define VCC_SAMPLE 60 | |
const uint8_t doorPin = 8; | |
const uint16_t vccInterval = 1000; | |
volatile long counter = 0; | |
const bool dir = 0; // 0 - Write, 1 - Read | |
bool doorState = 0; | |
bool swState = 0; | |
byte addresses[][6] = {"1Node","2Node"}; // Radio pipe addresses for the 2 nodes to communicate. | |
RF24 radio(10,9); | |
void setup() { | |
if (!dir) { | |
OSCCAL = 0x7A; // Calibration offset for internal oscillator | |
clearWdt(); // Clear watchdog timer | |
ADCSRA &= ~(1<<ADEN); // Disable ADC | |
} | |
delay(250); | |
/* | |
Serial.begin(9600); | |
delay(4000); | |
Serial.println(F("nrf_node")); | |
radioInit(dir); | |
delay(5000); | |
*/ | |
message.stn_id = 1; | |
if (!dir) { | |
setWdt(); // Set watchdog in preparation for sleepytime | |
radio.powerDown(); // Turn off the radio to save power | |
} | |
} | |
void loop() { | |
// transmitter code | |
if (!dir) { | |
radio.stopListening(); | |
//sleep_bod_disable(); | |
sleep_mode(); | |
clearWdt(); | |
/* | |
* Run readVcc every interval | |
*/ | |
if (counter >= vccInterval) { | |
counter = 0; | |
message.msg_type = 2; | |
message.msg_value = readVcc(VCC_SAMPLE); | |
message.ts = millis(); | |
radio.powerUp(); | |
/* | |
Serial.print(message.msg_value); | |
Serial.println(F(" VCC ")); | |
*/ | |
sendMsg(message, MSG_RETRY); | |
radio.powerDown(); | |
} | |
swState = digitalRead(doorPin); // read the state of the switch | |
if (doorState != swState) { // if switch differs with last state set | |
if (swState == 0) message.msg_value = 7; // closed | |
else message.msg_value = 15; // open | |
message.msg_type = 1; | |
message.ts = millis(); // set timestamp as unique packet identifier | |
/* | |
Serial.print(F("Xmit ")); // Use a simple byte counter as payload | |
Serial.print(message.msg_value); | |
Serial.print(F(" ts: ")); | |
Serial.println(message.ts); | |
*/ | |
radio.powerUp(); | |
if (sendMsg(message, MSG_RETRY)) { | |
//Serial.println(F("OK")); | |
doorState = swState; // state has successfully been communicated | |
} else { | |
//Serial.println(F("FAIL")); | |
doorState = swState; // setting new state removes failed message from queue | |
} | |
radio.powerDown(); | |
} | |
setWdt(); | |
} | |
// receiver code | |
if (dir==1){ | |
byte pipeNo; // Declare variables for the pipe | |
radio.powerUp(); | |
while( radio.available(&pipeNo)){ // Read all available payloads | |
radio.read( &message, sizeof(data_t) ); | |
Serial.print(F("Recv: ")); | |
Serial.print(message.msg_value); | |
Serial.print(F(" ts: ")); | |
Serial.print(message.ts); | |
unsigned long tsAck = message.ts; // directly with an ack payload. | |
// we'll send the ack back when we have recorded the message | |
radio.writeAckPayload(pipeNo, &tsAck, sizeof(tsAck) ); // This can be commented out to send empty payloads. | |
Serial.print(F(" Xmit: ")); | |
Serial.println(tsAck); | |
//Serial.print(F(" ")); | |
//Serial.print(radio.whatHappened()); | |
} | |
} | |
} | |
void radioInit(uint8_t dir=0) { | |
radio.begin(); | |
radio.setChannel(CHANNEL); | |
radio.enableAckPayload(); // Allow optional ack payloads | |
radio.enableDynamicPayloads(); // Ack payloads are dynamic payloads | |
radio.setDataRate(RF24_250KBPS); | |
radio.setPALevel(RF24_PA_MIN); | |
if (dir==0) { | |
radio.openWritingPipe(addresses[0]); | |
radio.openReadingPipe(1,addresses[1]); | |
} else { | |
radio.openWritingPipe(addresses[1]); | |
radio.openReadingPipe(1,addresses[0]); | |
} | |
/* | |
Serial.print(F("Init ")); | |
if (radio.isPVariant()) { | |
Serial.println(F("OK")); | |
} else { | |
Serial.println(F("Fail")); | |
} | |
*/ | |
radio.startListening(); // Start listening | |
} | |
bool sendMsg(struct data_t msg, int retry) { | |
unsigned long tsAck; // Initialize a variable for the incoming response | |
if (retry <= 0) return false; // Baseline condition | |
if ( radio.write(&msg, sizeof(data_t)) ){ // Send the counter variable to the other radio | |
if(!radio.available()){ // If nothing in the buffer, we got an ack but it is blank | |
retry--; // Incorrect ts ack received, subtract a retry | |
} else { | |
while(radio.available() ){ // If an ack with payload was received | |
radio.read( &tsAck, sizeof(tsAck) ); | |
if (tsAck != msg.ts) { | |
retry--; // Incorrect ts ack received, subtract a retry | |
break; | |
} | |
return true; // Return true on successful acknowledgement | |
} | |
} | |
} else { | |
retry--; // Couldn't send the packet, so subtract a retry | |
} | |
delay(50); | |
sendMsg(msg, retry); // Run recursive until baseline met | |
} | |
void setWdt() { | |
cli(); // disable global interrupts | |
// Clear WDRF in MCUSR | |
MCUSR &= ~(1<<WDRF); | |
// Prep the watchdog register | |
WDTCSR |= (1<<WDCE) | (1<<WDE); | |
// Set interrupt mode with a timeout of 1 sec | |
WDTCSR = (1<<WDIE) | (1<<WDP1) | (1<<WDP2); | |
//WDTCR = ((1<<WDIE)); | |
SMCR = (1<<SM1); // Enable power down sleep mode | |
sei(); // Enable global interrupts | |
//pinMode(doorPin, OUTPUT); | |
//digitalWrite(doorPin, LOW); | |
return; | |
} | |
void clearWdt() { | |
cli(); | |
// Clear WDRF in MCUSR | |
MCUSR &= ~(1<<WDRF); | |
// Write logical one to WDCE and WDE | |
// Keep old prescaler setting to prevent unintentional time-out | |
WDTCSR |= (1<<WDCE) | (1<<WDE); | |
// Turn off WDT | |
WDTCSR = 0x00; | |
sei(); | |
return; | |
} | |
uint16_t readVcc(uint8_t sample) { | |
// Use ADC and 1.1V internal bandgap voltage reference to determine V_cc | |
ADMUX = 0b01001110; // Set to AV_cc, no ADLAR, MUX 1110 = single end V_bg | |
ADCSRA |= (1<<ADEN); | |
delay(250); // Wait for the dust to settle | |
uint16_t sum = 0; | |
for (uint8_t i=0;i<sample;i++) { | |
// Read ADC i times and avg the result | |
ADCSRA |= (1<<ADSC); // start ADC conversion | |
while( ADCSRA & (1<<ADSC) ) ; // Wait for 1st conversion to be ready... | |
uint8_t low = ADCL; // read low bit | |
uint8_t high = ADCH; // read high bit | |
uint16_t result = (high<<8) | low; // with our powers combined | |
sum += result; | |
} | |
ADCSRA &= ~(1<<ADEN); // disable ADC | |
return sum ? (1100L * 1023) / (sum/sample) : -1; // return the oversampled voltage | |
} | |
ISR(WDT_vect) { | |
// Keep track of sleep time | |
counter++; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment