First commit of the connected mailbox app

This commit is contained in:
Th3maz1ng 2022-10-01 12:04:36 +02:00
parent 3254117798
commit e4a258ba3b
7 changed files with 460 additions and 0 deletions

17
src/app/BoardConfig.cpp Normal file
View File

@ -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)
{
}

44
src/app/BoardConfig.h Normal file
View File

@ -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

109
src/app/MBPeripherals.cpp Normal file
View File

@ -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);
}

79
src/app/MBPeripherals.h Normal file
View File

@ -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 <RF24.h>
#include <LowPower.h>
#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

142
src/app/app.ino Normal file
View File

@ -0,0 +1,142 @@
#include <Arduino.h>
#include <LowPower.h>
#include <SPI.h>
#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 !
}

63
src/app/definition.h Normal file
View File

@ -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 <Arduino.h>
#include <RF24.h>
#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

6
src/app/packet_format.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef PACKET_FORMAT_H
#define PACKET_FORMAT_H
enum HEADER_e {WEATHER_STATION = 0, CONNECTED_MAILBOX};
#endif //PACKET_FORMAT_H