317 lines
7.1 KiB
Arduino
317 lines
7.1 KiB
Arduino
|
///////////////////
|
||
|
// Includes //
|
||
|
///////////////////
|
||
|
|
||
|
#include <EEPROM.h>
|
||
|
#include <SPI.h>
|
||
|
#include <avr/sleep.h>
|
||
|
#include <avr/power.h>
|
||
|
#include <nRF24L01.h>
|
||
|
#include <RF24.h> // https://github.com/stanleyseow/RF24/tree/2a1a4e6e27056844a3bc419d65b8a2d4e0f1770e
|
||
|
#include "printf.h"
|
||
|
#include <EmonLib.h> // https://github.com/openenergymonitor/EmonLib
|
||
|
#include <AESLib.h> // https://github.com/DavyLandman/AESLib
|
||
|
|
||
|
///////////////////////
|
||
|
// CONFIGURATION //
|
||
|
///////////////////////
|
||
|
|
||
|
#define DEBUG 1
|
||
|
#define SERIAL_BAUDRATE 57600
|
||
|
|
||
|
// PreAmplifier level for the nRF
|
||
|
// Lower this to reduce power consumption. This will reduce range.
|
||
|
rf24_pa_dbm_e NRF_PA_LEVEL = RF24_PA_LOW;
|
||
|
|
||
|
// Radio pipe addresses for the 2 nodes to communicate.
|
||
|
uint64_t pipes[2] = { 0xE056D446D0LL, 0xF0F0F0F0D2LL };
|
||
|
|
||
|
// AES
|
||
|
uint8_t key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
|
||
|
|
||
|
// Calibration factor for the intensity
|
||
|
double ICAL = 85.0;
|
||
|
|
||
|
// =============================================================
|
||
|
|
||
|
// Speed for the nrf module
|
||
|
// RF24_250KBPS / RF24_1MBPS / RF24_2MBPS
|
||
|
// Reduce it to improve reliability
|
||
|
rf24_datarate_e NRF_SPEED = RF24_250KBPS;
|
||
|
|
||
|
// Channel for the nrf module
|
||
|
// 76 is default safe channel in RF24
|
||
|
int NRF_CHANNEL = 0x4c;
|
||
|
|
||
|
// Set this to 0 to enable voltage measurement.
|
||
|
// Else, set this to your mean voltage.
|
||
|
double VOLTAGE = 240.0;
|
||
|
|
||
|
// Number of samples over which the mean must be done for the current measurement
|
||
|
int NUMBER_SAMPLES_I = 1480;
|
||
|
|
||
|
// Pin for current measurement
|
||
|
const int CURRENT_PIN = A0;
|
||
|
|
||
|
// Pin for voltage measurement
|
||
|
const int VOLTAGE_PIN = 0;
|
||
|
|
||
|
// Pin for the green LED
|
||
|
const int GREEN_LED_PIN = 2;
|
||
|
|
||
|
// Pin for the red LED
|
||
|
const int RED_LED_PIN = 3;
|
||
|
|
||
|
// Energy Monitor object
|
||
|
EnergyMonitor emon1;
|
||
|
|
||
|
///////////////////////
|
||
|
// Declarations //
|
||
|
///////////////////////
|
||
|
|
||
|
#define TIMEOUT 250 // timeout in ms
|
||
|
|
||
|
// Struct to send RF data
|
||
|
typedef struct {
|
||
|
int power;
|
||
|
int voltage;
|
||
|
int battery;
|
||
|
unsigned long timer;
|
||
|
long padding4;
|
||
|
int padding2;
|
||
|
} PayloadTX;
|
||
|
|
||
|
// Next measurement to be sent
|
||
|
PayloadTX nrf = {0, 0, 0, 0, 0, 0};
|
||
|
|
||
|
////////////////////////////////
|
||
|
// Hardware configuration //
|
||
|
////////////////////////////////
|
||
|
|
||
|
// Set up nRF24L01 radio on SPI bus plus pins 9 & 10
|
||
|
RF24 radio(9,10);
|
||
|
|
||
|
//////////////////////////////
|
||
|
// Sleep configuration //
|
||
|
//////////////////////////////
|
||
|
|
||
|
typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, wdt_500ms, wdt_1s, wdt_2s, wdt_4s, wdt_8s } wdt_prescalar_e;
|
||
|
|
||
|
void setup_watchdog(uint8_t prescalar);
|
||
|
void do_sleep(void);
|
||
|
|
||
|
//////////////////////////
|
||
|
// Setup operation //
|
||
|
//////////////////////////
|
||
|
|
||
|
void setup(void)
|
||
|
{
|
||
|
pinMode(RED_LED_PIN, OUTPUT);
|
||
|
pinMode(GREEN_LED_PIN, OUTPUT);
|
||
|
digitalWrite(RED_LED_PIN, HIGH);
|
||
|
digitalWrite(GREEN_LED_PIN, HIGH);
|
||
|
|
||
|
|
||
|
Serial.begin(SERIAL_BAUDRATE);
|
||
|
Serial.println(F("/!\\ STARTING CitizenWatt Sensor"));
|
||
|
Serial.println(F("//////////////////////////////"));
|
||
|
Serial.println(F("// CitizenWatt sensor //"));
|
||
|
Serial.println(F("// citizenwatt.paris //"));
|
||
|
Serial.println(F("////////////////////////////// \n"));
|
||
|
|
||
|
if( DEBUG )
|
||
|
printf_begin();
|
||
|
|
||
|
//
|
||
|
// Prepare sleep parameters
|
||
|
//
|
||
|
|
||
|
setup_watchdog(wdt_8s); // /!\ 8s sleeping
|
||
|
|
||
|
//
|
||
|
// Setup and configure rf radio
|
||
|
//
|
||
|
|
||
|
// Initialize nRF
|
||
|
radio.begin();
|
||
|
radio.setChannel(NRF_CHANNEL);
|
||
|
|
||
|
// Max number of retries and max delay between them
|
||
|
radio.setRetries(15, 15);
|
||
|
|
||
|
// Reduce payload size to improve reliability
|
||
|
radio.setPayloadSize(16);
|
||
|
|
||
|
// Set the datarate
|
||
|
radio.setDataRate(NRF_SPEED);
|
||
|
|
||
|
// Use the largest CRC
|
||
|
radio.setCRCLength(RF24_CRC_16);
|
||
|
|
||
|
// Ensure auto ACK is enabled
|
||
|
//radio.setAutoAck(1);
|
||
|
|
||
|
// Use the adapted PA level
|
||
|
radio.setPALevel(NRF_PA_LEVEL);
|
||
|
|
||
|
// Open writing pipe
|
||
|
radio.openWritingPipe(pipes[0]);
|
||
|
radio.openReadingPipe(1, pipes[1]);
|
||
|
|
||
|
if(DEBUG) {
|
||
|
radio.printDetails();
|
||
|
}
|
||
|
|
||
|
// Init EnergyMonitor, with calibration factor for R1 = 22 Ohms
|
||
|
emon1.current(0, ICAL);
|
||
|
|
||
|
digitalWrite(RED_LED_PIN, LOW);
|
||
|
digitalWrite(GREEN_LED_PIN, LOW);
|
||
|
}
|
||
|
|
||
|
///////////////////////////////
|
||
|
// Loop part of the code //
|
||
|
///////////////////////////////
|
||
|
|
||
|
void loop(void)
|
||
|
{
|
||
|
//
|
||
|
// Read Current
|
||
|
//
|
||
|
digitalWrite(GREEN_LED_PIN, HIGH);
|
||
|
nrf.power = (int) (emon1.calcIrms(NUMBER_SAMPLES_I) * VOLTAGE);
|
||
|
digitalWrite(GREEN_LED_PIN, LOW);
|
||
|
|
||
|
// Set voltage
|
||
|
nrf.voltage = VOLTAGE;
|
||
|
|
||
|
// Read Vcc
|
||
|
nrf.battery = (int) emon1.readVcc();
|
||
|
|
||
|
// Adding random for AES
|
||
|
nrf.timer = millis();
|
||
|
|
||
|
if( DEBUG )
|
||
|
{
|
||
|
Serial.print("|");
|
||
|
Serial.print(nrf.power);
|
||
|
Serial.print("\t");
|
||
|
Serial.print("|");
|
||
|
Serial.print("\t");
|
||
|
Serial.print(nrf.voltage);
|
||
|
Serial.print("\t");
|
||
|
Serial.print("|");
|
||
|
Serial.print("\t");
|
||
|
Serial.print(nrf.battery);
|
||
|
Serial.print("\t");
|
||
|
Serial.println("|");
|
||
|
|
||
|
Serial.print(F("Clear :"));
|
||
|
PrintHex8((uint8_t*)&nrf, sizeof(PayloadTX));
|
||
|
Serial.println();
|
||
|
Serial.flush();
|
||
|
}
|
||
|
|
||
|
// AES ciphering
|
||
|
aes128_enc_single(key, &nrf);
|
||
|
|
||
|
if( DEBUG )
|
||
|
{
|
||
|
Serial.print(F("Cipher:"));
|
||
|
PrintHex8((uint8_t*)&nrf, sizeof(PayloadTX));
|
||
|
Serial.println();
|
||
|
Serial.flush();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Data sender
|
||
|
//
|
||
|
|
||
|
// First, stop listening so we can talk.
|
||
|
radio.stopListening();
|
||
|
|
||
|
radio.write(&nrf, sizeof(PayloadTX));
|
||
|
|
||
|
// Now, continue listening now the goal is to get some data back as ACK
|
||
|
radio.startListening();
|
||
|
|
||
|
// Wait here until we get a response, or timeout (250ms)
|
||
|
unsigned long started_waiting_at = millis();
|
||
|
bool timeout = false;
|
||
|
while ( ! radio.available() && ! timeout )
|
||
|
if (millis() - started_waiting_at > TIMEOUT )
|
||
|
timeout = true;
|
||
|
|
||
|
// Describe the results
|
||
|
if ( timeout && DEBUG )
|
||
|
Serial.println(F("[!] Failed to send packet : response timed out ..."));
|
||
|
|
||
|
// Stop listening
|
||
|
radio.stopListening();
|
||
|
|
||
|
//
|
||
|
// Shut down the system
|
||
|
//
|
||
|
|
||
|
// 100ms for the MCU to settle
|
||
|
delay(100);
|
||
|
|
||
|
// Power down the radio. Note that the radio will get powered back up
|
||
|
// on the next write() call.
|
||
|
// TODO : Fix this !
|
||
|
//radio.powerDown();
|
||
|
|
||
|
// Sleep the MCU. The watchdog timer will awaken in a short while, and
|
||
|
// continue execution here.
|
||
|
do_sleep();
|
||
|
|
||
|
// 100ms for the MCU to settle
|
||
|
delay(100);
|
||
|
|
||
|
}
|
||
|
|
||
|
//////////////////////////
|
||
|
// Sleep functions //
|
||
|
//////////////////////////
|
||
|
|
||
|
// 0=16ms, 1=32ms,2=64ms,3=125ms,4=250ms,5=500ms
|
||
|
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
|
||
|
|
||
|
void setup_watchdog(uint8_t prescalar)
|
||
|
{
|
||
|
prescalar = min(9,prescalar);
|
||
|
uint8_t wdtcsr = prescalar & 7;
|
||
|
if ( prescalar & 8 )
|
||
|
wdtcsr |= _BV(WDP3);
|
||
|
|
||
|
MCUSR &= ~_BV(WDRF);
|
||
|
WDTCSR = _BV(WDCE) | _BV(WDE);
|
||
|
WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE);
|
||
|
}
|
||
|
|
||
|
ISR(WDT_vect)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void do_sleep(void)
|
||
|
{
|
||
|
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
|
||
|
sleep_enable();
|
||
|
|
||
|
sleep_mode(); // System sleeps here
|
||
|
|
||
|
sleep_disable(); // System continues execution here when watchdog timed out
|
||
|
}
|
||
|
|
||
|
// Function for debug
|
||
|
void PrintHex8(uint8_t *data, uint8_t length) // prints 8-bit data in hex with leading zeroes
|
||
|
{
|
||
|
Serial.print("0x");
|
||
|
for (int i=0; i<length; i++) {
|
||
|
if (data[i]<0x10) {Serial.print("0");}
|
||
|
Serial.print(data[i],HEX);
|
||
|
Serial.print(" ");
|
||
|
}
|
||
|
}
|
||
|
|