diff --git a/src/app/BoardConfig.cpp b/src/app/BoardConfig.cpp new file mode 100644 index 0000000..a78096a --- /dev/null +++ b/src/app/BoardConfig.cpp @@ -0,0 +1,17 @@ +#include "BoardConfig.h" + +BoardConfig::BoardConfig( const Pin ExternalInt, + const Pin LDOEnable, + const Pin BattVSensEnable, + const Pin NRFCe, + const Pin SwitchVSensEnable, + const Pin NRFCs, + const Pin MOSI, + const Pin MISO, + const Pin SCK, + const Pin BattAnalogVSens, + const Pin SwitchAnalogVSens +):ExternalInt(ExternalInt), LDOEnable(LDOEnable), BattVSensEnable(BattVSensEnable), NRFCe(NRFCe), SwitchVSensEnable(SwitchVSensEnable), NRFCs(NRFCs), MOSI(MOSI), MISO(MISO), SCK(SCK), BattAnalogVSens(BattAnalogVSens), SwitchAnalogVSens(SwitchAnalogVSens) +{ + +} diff --git a/src/app/BoardConfig.h b/src/app/BoardConfig.h new file mode 100644 index 0000000..104dfd8 --- /dev/null +++ b/src/app/BoardConfig.h @@ -0,0 +1,44 @@ +/** + * Author : Anatole SCHRAMM-HENRY + * Created the : 08/09/2022 + * This class encapsulates the various configuration values for the board like the Pin Mapping for instance. + */ +#ifndef BOARDCONFIG_H +#define BOARDCONFIG_H + +#include "definition.h" + +class BoardConfig +{ + public: + BoardConfig( + const Pin ExternalInt = D2_EXTERNAL_INT, + const Pin LDOEnable = D4_LDO_EN, + const Pin BattVSensEnable = D5_BAT_V_SENS_EN, + const Pin NRFCe = D6_NRF_CE, + const Pin SwitchVSensEnable = D7_SWITCH_V_SENS_EN, + const Pin NRFCs = D10_NRF_CS, + const Pin MOSI = D11_MOSI, + const Pin MISO = D12_MISO, + const Pin SCK = D13_SCK, + const Pin BattAnalogVSens = A0_BAT_V_SENS, + const Pin SwitchAnalogVSens = A1_SWITCH_V_SENS + ); + + const Pin ExternalInt; + const Pin LDOEnable; + const Pin BattVSensEnable; + const Pin NRFCe; + const Pin SwitchVSensEnable; + const Pin NRFCs; + const Pin MOSI; + const Pin MISO; + const Pin SCK; + const Pin BattAnalogVSens; + const Pin SwitchAnalogVSens; + + protected: + private: +}; + +#endif //BOARDCONFIG_H diff --git a/src/app/MBPeripherals.cpp b/src/app/MBPeripherals.cpp new file mode 100644 index 0000000..a9e2665 --- /dev/null +++ b/src/app/MBPeripherals.cpp @@ -0,0 +1,109 @@ +#include "MBPeripherals.h" + +void defaultExtIntIsr(void){} + +MBPeripherals::MBPeripherals(const BoardConfig &boardConfig, void (*extIntIsr)(void)): _boardConfig(boardConfig), _NRF(boardConfig.NRFCe, boardConfig.NRFCs) +{ + if(!extIntIsr) + _extIntIsr = &(defaultExtIntIsr); //We provide a default ISR +} + +uint8_t MBPeripherals::init() +{ + uint8_t toReturn(0); + //We initialize needed IOs : + pinMode(_boardConfig.ExternalInt, INPUT); + pinMode(_boardConfig.LDOEnable, OUTPUT); + _3V3PowerRail(OFF); + pinMode(_boardConfig.SwitchVSensEnable, INPUT_PULLUP); + pinMode(_boardConfig.BattVSensEnable, OUTPUT); + digitalWrite(_boardConfig.BattVSensEnable, LOW); + + //Unused pins are set as inputs with internal pullup enabled to reduce power consumption during sleep + pinMode(0,INPUT_PULLUP); + //pinMode(1,INPUT_PULLUP); TX pin for serial + pinMode(6,INPUT_PULLUP); + pinMode(7,INPUT_PULLUP); + pinMode(8,INPUT_PULLUP); + pinMode(9,INPUT_PULLUP); + pinMode(10,INPUT_PULLUP); + pinMode(11,INPUT_PULLUP); + pinMode(12,INPUT_PULLUP); + pinMode(A2,INPUT_PULLUP); + pinMode(A3,INPUT_PULLUP); + pinMode(A4,INPUT_PULLUP); + pinMode(A5,INPUT_PULLUP); + pinMode(A6,INPUT_PULLUP); + pinMode(A7,INPUT_PULLUP); + + //We check that every external devices are responding + _3V3PowerRail(ON); + toReturn |= _NRF.begin(); + _3V3PowerRail(OFF); + + attachInterrupt(digitalPinToInterrupt(_boardConfig.ExternalInt), _extIntIsr, FALLING); + + return toReturn; +} + +uint8_t MBPeripherals::initExternalPeripherals(void) +{ + uint8_t toReturn(0); + + toReturn |= _NRF.begin(); + + return toReturn; +} + +float MBPeripherals::batteryVoltage(void) +{ + //We close the voltage divider bridge and we do the measurement + digitalWrite(_boardConfig.BattVSensEnable, HIGH); + int rawBatteryValue = analogRead(_boardConfig.BattAnalogVSens); + digitalWrite(_boardConfig.BattVSensEnable, LOW); + + return float(rawBatteryValue) * ADC_QUANTUM * VOLTAGE_DIV_COEFF; +} + +void MBPeripherals::_3V3PowerRail(State state) +{ + digitalWrite(_boardConfig.LDOEnable, state); + if(state) //We let some time for the voltage to stabilize on the rail. + delay(10); +} + +const RF24 &MBPeripherals::getRadio(void){return _NRF;} + +void MBPeripherals::applyRadioConfig(uint8_t channel, uint8_t paLevel, bool enableLNA, rf24_datarate_e datarate) +{ + _NRF.setChannel(channel); + _NRF.setPALevel(paLevel, enableLNA); + _NRF.setDataRate(datarate); +} + +MAILBOX_EVENT_e MBPeripherals::readMailboxEvent(void) +{ + pinMode(_boardConfig.SwitchVSensEnable, OUTPUT); + digitalWrite(_boardConfig.SwitchVSensEnable, HIGH); + int analogValue = analogRead(_boardConfig.SwitchAnalogVSens); + digitalWrite(_boardConfig.SwitchVSensEnable, LOW); + pinMode(_boardConfig.SwitchVSensEnable, INPUT_PULLUP); + + if(analogValue < 526) + return MAILBOX_EVENT_e::MAILBOX_LETTER; + else if(analogValue >= 526 && analogValue < 711) + return MAILBOX_EVENT_e::MAILBOX_PACKAGE; + else if(analogValue >= 711 && analogValue < 1000) + return MAILBOX_EVENT_e::MAILBOX_COLLECTED; + else return MAILBOX_EVENT_e::MAILBOX_UNKNOWN; +} + +void MBPeripherals::maskExtInt(void) +{ + detachInterrupt(digitalPinToInterrupt(_boardConfig.ExternalInt)); +} + +void MBPeripherals::unmaskExtInt(void) +{ + attachInterrupt(digitalPinToInterrupt(_boardConfig.ExternalInt), _extIntIsr, FALLING); +} diff --git a/src/app/MBPeripherals.h b/src/app/MBPeripherals.h new file mode 100644 index 0000000..2e9b335 --- /dev/null +++ b/src/app/MBPeripherals.h @@ -0,0 +1,79 @@ +/** + * Author : Anatole SCHRAMM-HENRY + * Created the : 08/09/2022 + * This classe exposes all the methods necessary to init the MBPeripherals (Mail Box Peripherals) and + * to retrieve various data and measurements like battery voltage, Mailbox events etc... + */ +#ifndef MBPERIPHERALS_H +#define MBPERIPHERALS_H + +#include +#include + +#include "BoardConfig.h" + +void defaultExtIntIsr(void); + +class MBPeripherals +{ + public: + enum State {OFF, ON}; + + MBPeripherals(const BoardConfig &boardConfig, void (*extIntIsr)(void) = nullptr); + /* + * Returns 1 if all the external devices are working properly, or an other value if it is not the case. + */ + uint8_t init(void); + + /* + * After calling this methode , you need to execute initExternalPeripherals() to init the peripherals. + */ + void externalPeripherals(State state){_3V3PowerRail(state);} + + /* + * Used to init devices after each LDO powerup, external devices need to be turned one externalPeripherals(ON) before calling this function. + */ + uint8_t initExternalPeripherals(void); + + /* + * Starts a battery voltage measurement and returns a float value in Volts. + */ + float batteryVoltage(void); + + /* + * Before calling this method, externalPeripherals(ON) and initExternalPeripherals() must be called respectively. + * This methods applies the NRF radio configuration. + */ + void applyRadioConfig(uint8_t channel = RADIO_CHANNEL, uint8_t paLevel = RADIO_PA_LEVEL, bool enableLNA = ENABLE_LNA, rf24_datarate_e datarate = RADIO_DATARATE); + + /* + * Use this function just after the interrupt wake up retrieve the wake up event. + * Wake up event can be : MAILBOX_LETTER, MAILBOX_PACKAGE, MAILBOX_COLLECTED or MAILBOX_UNKNOWN. + */ + MAILBOX_EVENT_e readMailboxEvent(void); + + /* + * Disables wake up interrupts + */ + void maskExtInt(void); + + /* + * Enables wake up interrupts + */ + void unmaskExtInt(void); + + /* + * Gets the NRF radio object to access it's api. + */ + const RF24 &getRadio(void); + + protected: + private: + void _3V3PowerRail(State state); + const BoardConfig &_boardConfig; + const RF24 _NRF; + const void (*_extIntIsr)(void); + +}; + +#endif //MBPERIPHERALS_H diff --git a/src/app/app.ino b/src/app/app.ino new file mode 100644 index 0000000..fda8e44 --- /dev/null +++ b/src/app/app.ino @@ -0,0 +1,142 @@ +#include +#include +#include +#include "definition.h" +#include "BoardConfig.h" +#include "MBPeripherals.h" + +BoardConfig defaultBC; +MBPeripherals MBP(defaultBC); + +MailboxDataPacket payload; +uint8_t rCode(0); + +void setup() +{ + #if SERIAL_DEBUG_ENABLED + Serial.begin(SERIAL_BAUD_RATE); + Serial.println("Setup begin"); + #endif + + rCode = MBP.init(); + memset(&payload, 0, sizeof payload); + + #if SERIAL_DEBUG_ENABLED + Serial.print("Payload size : ");Serial.println(sizeof payload); + debugStruct(&payload); + #endif + payload.header = HEADER_e::CONNECTED_MAILBOX; + + #if SERIAL_DEBUG_ENABLED + Serial.print("WSP init returned : ");Serial.println(rCode); + Serial.println("Setup end"); + //Let time to the uart to spit out what's left + delay(100); + #endif + + LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); +} + +void loop() +{ + + MBP.maskExtInt(); + if((payload.mailbox_event = MBP.readMailboxEvent()) != MAILBOX_EVENT_e::MAILBOX_UNKNOWN) + { + payload.battery = MBP.batteryVoltage(); + + //We now send the event + MBP.externalPeripherals(MBPeripherals::State::ON); + rCode = MBP.initExternalPeripherals(); + + #if SERIAL_DEBUG_ENABLED + Serial.print("MBP Ext Peripheral : "); Serial.println(rCode); + debugStruct(&payload); + #endif + + if(MBP.getRadio().isChipConnected()) + { + MBP.applyRadioConfig(); + MBP.getRadio().setRetries(15, 15); + MBP.getRadio().openWritingPipe((const uint8_t *)RADIO_NODE_ADDRESS); + MBP.getRadio().stopListening(); //Very important, if not called, no ack will be received ! + + for(uint8_t i(0); i < MAX_SEND_ROUND_RETRIES; i++) + { + if(MBP.getRadio().write(&payload, sizeof payload)) + { + #if SERIAL_DEBUG_ENABLED + Serial.println("Payload sent !"); + delay(100); + #endif + break; + } + #if SERIAL_DEBUG_ENABLED + else + { + Serial.println("Failed to send payload !"); + delay(100); + } + #endif + } + } + #if SERIAL_DEBUG_ENABLED + else + { + Serial.println("NRF missing !"); + delay(100); + } + #endif + + MBP.externalPeripherals(MBPeripherals::State::OFF); + + //We wait for some time before allowing next trigger + //Done to prevent switch bounces + LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF); + } + #if SERIAL_DEBUG_ENABLED + else + { + Serial.println("Unknown event :O !"); + delay(100); + } + #endif + + MBP.unmaskExtInt(); + payload.id++; + //Sleep until next trigger happens + LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); +} + +void debugStruct(MailboxDataPacket *p) +{ + Serial.println("##############DATA##############"); + Serial.print("ID : "); + Serial.println(p->id); + + Serial.print("HEADER : "); + Serial.println(p->header); + + Serial.print("BATT : "); + Serial.print(p->battery); + Serial.println(" V"); + + Serial.print("EVENT : "); + switch(p->mailbox_event) + { + case MAILBOX_LETTER: + Serial.println("LETTER"); + break; + case MAILBOX_PACKAGE: + Serial.println("PACKAGE"); + break; + case MAILBOX_COLLECTED: + Serial.println("COLLECTED"); + break; + default: + Serial.println("UNKNOWN"); + break; + } + + delay(100); //Needed to prevent the micro to go to sleep to quickly and thus not sending the full serial output ! +} diff --git a/src/app/definition.h b/src/app/definition.h new file mode 100644 index 0000000..4ec2f6d --- /dev/null +++ b/src/app/definition.h @@ -0,0 +1,63 @@ +/** + * Author : Anatole SCHRAMM-HENRY + * Created the : 08/09/2022 + * This file contains all the config used by the other classes and sources files. + */ +#ifndef DEFINITION_H +#define DEFINITION_H + +#include +#include +#include "packet_format.h" + +//Serial debug config part +#define SERIAL_DEBUG_ENABLED 0 +#define SERIAL_BAUD_RATE 115200 + +//Battery config part +#define ADC_QUANTUM 0.0033017578125 //ADC_VREF / ADC_RESOLUTION -> 3.314 and 10 bits (1024) in my case +#define VOLTAGE_DIV_COEFF 1.321127673363577 //(R1 + R2)/R2 + +//NRF Radio config part +#define RADIO_CHANNEL 108 //0-125 +#define RADIO_NODE_ADDRESS "WEST1" //Weather Station 1 network because the mail box is connected on it +#define RADIO_PA_LEVEL RF24_PA_MAX //RF24_PA_MIN,RF24_PA_LOW,RF24_PA_HIGH,RF24_PA_MAX +#define ENABLE_LNA false //true or false +#define RADIO_DATARATE RF24_250KBPS //Slow datarate to maximize range +#define MAX_SEND_ROUND_RETRIES 5 //If no ACK is received, tries to resend a MAX_SEND_ROUND_RETRIES-1 more times + //Increase this parameter if the RF link is bad. + +//Pin config part +typedef enum +{ + D2_EXTERNAL_INT = 2, + D4_LDO_EN = 4, + D5_BAT_V_SENS_EN = 5, + D6_NRF_CE = 6, + D7_SWITCH_V_SENS_EN = 7, + D10_NRF_CS = 10, + D11_MOSI = 11, + D12_MISO = 12, + D13_SCK = 13, + A0_BAT_V_SENS = A0, + A1_SWITCH_V_SENS = A1, +} Pin; + +typedef enum +{ + MAILBOX_LETTER = 0, + MAILBOX_PACKAGE, + MAILBOX_COLLECTED, + MAILBOX_UNKNOWN, +} MAILBOX_EVENT_e; + +//Payload structure +typedef struct +{ + uint16_t id; + HEADER_e header : 6; + float battery; + MAILBOX_EVENT_e mailbox_event; +} __attribute__((__packed__)) MailboxDataPacket; + +#endif //DEFINITION_H diff --git a/src/app/packet_format.h b/src/app/packet_format.h new file mode 100644 index 0000000..a5e72a1 --- /dev/null +++ b/src/app/packet_format.h @@ -0,0 +1,6 @@ +#ifndef PACKET_FORMAT_H +#define PACKET_FORMAT_H + +enum HEADER_e {WEATHER_STATION = 0, CONNECTED_MAILBOX}; + +#endif //PACKET_FORMAT_H