Mr TotalVario - this should work for your hardware
/*********************************************************************
18 July Commented/deleted out the AHT sensor and uncommented to use the SHT4x sensor
Tested on the bench and all 3 sensors working. The buzzer also works.
Battery sensing not checked yet.
Not test-flown yet.
10 June Removed sensor intialisation conditional halts.
Sketch will run even if SDP3x or AHT45 are not ready
(DPS310 pauses to set parameters)
09 June Corrected Battery Voltage Read (was always reading 0V)
Removed unused libraries
Created Debug toggle for debugging Print.Serial. Set in Line#65
Sensors in use: SDP3x | AHT45
Sensors Commented out but ready: SHT45 | DPS310
07 June added Charging notification (requires TotalVario Beta 9-2)
06 June added BLE connection beeps
04 June 2023 - used Bernd's sprintf sketch
22 April 2023 - Working with XIAO nRF (ie: not XIAO mBed device).
INFO:
MCU Hardware - Seeed XIAO nRF52840 (not "Sense" model)
Using Arduino IDE 1.8.19
Using Arduino 'Board' -> "Seeed nRF52 Boards" -> "Seeed XIAO nRF52840"
*********************************************************************/
// ----------LIBRARIES--------------
#include <bluefruit.h> // for BLE - Adafruit BLE Library
#include <Wire.h> // for I2C comms
#include <sdpsensor.h> // Sensirion SDP3x library
#include <SensirionI2CSht4x.h> // Sensirion SHT4x library
#include <Adafruit_DPS310.h> // Adafruit DPS310 library
// ---------CONSTANTS---------------
// Function Timing
const int IntervalSDP = 99; // mS before IAS function repeats, also sends all BLE data (99)
const int IntervalDPS = 113; // mS before DPS function repeats Atmospheric Pressure read (113)
const int IntervalSHT = 1333; // mS before Temperature Humidity function repeats (1333)
const int IntervalBTV = 3333; // mS before Battery Voltage function repeats (3333)
// -----------CLASSES---------------
// BLE Services
BLEDfu bledfu; // OTA DFU service
BLEDis bledis; // device information
BLEUart bleuart; // uart over ble
// Dual Pressure Sensor
SDP3XSensor sdp;
// Temperature & Humidity Sensor
SensirionI2CSht4x sht4x;
// Absolute Pressure Sensor
Adafruit_DPS310 dps;
//------------VARIABLES-------------
// Debug setup for Serial Print
#define DEBUG false //set to true for debug output, false for no debug output
#define DEBUG_SERIAL \
if (DEBUG) Serial
// Startup Melody Notes & Variables
#define NOTE_C6 1047
#define NOTE_E6 1319
#define NOTE_G6 1568
#define REST 0
int tempo = 90;
int buzzer = 1;
int melody[] = {
NOTE_C6,
16,
NOTE_G6,
16,
NOTE_E6,
16,
NOTE_C6,
32,
NOTE_G6,
-16,
NOTE_E6,
8,
};
int notes = sizeof(melody) / sizeof(melody[0]) / 2;
int wholenote = (60000 * 4) / tempo;
int divider = 0, noteDuration = 0;
// Function Timing
unsigned long currentMillis = 0; // Stores the value of millis() for each loop()
unsigned long previousSDPMillis = 0;
long previousSHTMillis = 0;
long previousDPSMillis = 0;
long previousBTVMillis = 0;
// Sensirion Differential Pressure Variable
float sdpPres; // Variable for the differential pressure
// Infinion Atmospheric Pressure Variable
float absPres = 966.0; // Variable for the Atmospheric absolute pressure
// Ambient Environment Variables
float ambTemp = 0;
float ambHumi = 0;
float ahtValue; // To store T/Rh result temporarily
// Battery Variables
float batVolt = 4.00; // Battery Volts
float batPrct = 50.0; // Battery SOC %
bool battChrg = 0; // Battery Charging
//===================SETUP========================
void setup() {
// Debugging
#if DEBUG == true
Serial.begin(115200);
while (!Serial)
;
#endif //DEBUG == true
// Initialise I2C
Wire.begin();
// Setup Xiao Battery Monitoring
pinMode(VBAT_ENABLE, OUTPUT); // Battery Voltage divider ground leg as an Output
digitalWrite(VBAT_ENABLE, LOW); // Ground the Voltage divider ground leg to enable read
pinMode(23, INPUT); // Set monitoring leg as an Input
// Setup Xiao Battery Charging Rate
pinMode(22, OUTPUT); // Set the charger mA selection pin as an Output
digitalWrite(22, LOW); // Charge Current = 100mA (22 High = 50mA)
// Setup Buzzer Output
pinMode(D1, OUTPUT); // D1 output to Buzzer
// Initialise Sensirion SDP
int ret = sdp.init();
// if (ret == 0) {
// } else {
// while (true) {
// delay(500);
// }
// }
// Initialise Asair AHT2x Temp|Humidity Sensor
// while (aht20.begin() != true) {
// delay(200);
// }
// aht20.begin();
// Initialise Sensirion SHT
sht4x.begin(Wire);
// Initialise Infinion DPS sensor
if (!dps.begin_I2C()) { // Can pass in I2C address here
while (1) yield();
}
dps.configurePressure(DPS310_16HZ, DPS310_32SAMPLES); // See DPS310 datasheet for examples
dps.configureTemperature(DPS310_2HZ, DPS310_2SAMPLES); // See DPS310 datasheet for examples
// Startup Melody
{
for (int thisNote = 0; thisNote < notes * 2; thisNote = thisNote + 2) {
divider = melody[thisNote + 1];
if (divider > 0) {
// regular note, just proceed
noteDuration = (wholenote) / divider;
} else if (divider < 0) {
// dotted notes are represented with negative durations!!
noteDuration = (wholenote) / abs(divider);
noteDuration *= 1.5; // increases the duration in half for dotted notes
}
tone(buzzer, melody[thisNote], noteDuration * 0.9);
delay(noteDuration);
noTone(buzzer);
}
delay(200);
}
// Battery SoC Beeps
batVolt = analogRead(PIN_VBAT) * (0.003515625F) * (1510.0 / 510.0);
int n;
{
if (batVolt > 3.9) {
// fully charged ... four beeps
for (n = 1; n <= 4; n++) {
tone(buzzer, 880, 200);
delay(400);
}
} else if (batVolt > 3.8) {
// well charged ... three beeps
for (n = 1; n <= 3; n++) {
tone(buzzer, 880, 200);
delay(400);
}
} else if (batVolt > 3.7) {
// some charge ... two beeps
for (n = 1; n <= 2; n++) {
tone(buzzer, 880, 200);
delay(400);
}
} else {
// discharged ... one beep
tone(buzzer, 440, 300);
delay(400);
}
}
noTone(buzzer);
delay(100);
//BLE
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.begin();
Bluefruit.setTxPower(-12); // Set max power. Values can be: -40,-30,-20,-16,-12,-8,-4,0,4
//Bluefruit.setName(getMcuUniqueID());
// useful testing with multiple central connections or (eg): Bluefruit.setName("Name");
Bluefruit.setName("ProbeS4");
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
// To be consistent OTA DFU should be added first if it exists
bledfu.begin();
// Configure and Start Device Information Service
bledis.setManufacturer("Seeed");
bledis.setModel("XIAOnRF");
bledis.begin();
// Configure and Start BLE Uart Service
bleuart.begin();
// Set up and start advertising
startAdv();
}
void startAdv(void) {
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include bleuart 128-bit uuid
Bluefruit.Advertising.addService(bleuart);
// Secondary Scan Response packet (optional)
// Since there is no room for 'Name' in Advertising packet
Bluefruit.ScanResponse.addName();
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
}
//===================LOOP========================
void loop() {
currentMillis = millis(); // Capture the latest value of millis()
readSDP(); // Calls function to read SDP, sends to variable then calls buildMSG for BLEprinting
readDPS(); // Calls function to read DPS. Reads Atmospheric Pressure and sends to variable
readSHT(); // Calls function to read ambient Temperature/Humidity and sends to variable
readBTV(); // Calls function to read Battery Voltage and if Charging, sends to variable
}
//================BLE FUNCTIONS=====================
// Callback invoked when central connects
void connect_callback(uint16_t conn_handle) {
// Get the reference to current connection
BLEConnection* connection = Bluefruit.Connection(conn_handle);
char central_name[32] = { 0 };
connection->getPeerName(central_name, sizeof(central_name));
DEBUG_SERIAL.print("Connected to "); // For debugging
DEBUG_SERIAL.println(central_name); // For debugging
tone(buzzer, 440, 400);
delay(400);
tone(buzzer, 880, 200);
}
void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
(void)conn_handle;
(void)reason;
DEBUG_SERIAL.print("Disconnected, reason = 0x"); // For debugging
DEBUG_SERIAL.println(reason, HEX); // For debugging
tone(buzzer, 440, 600);
delay(400);
tone(buzzer, 330, 600);
}
//================ SENSOR FUNCTIONS =====================
// readSPD+++++++++++
void readSDP()
// read Differential Pressure from SDP sensor, append to a NEMA message, write to serial BLE
{
if (currentMillis >= previousSDPMillis + IntervalSDP) // run 'readIAS' function once time is up
{
previousSDPMillis = currentMillis; // save the time when change was made
int ret = sdp.readSample();
if (ret == 0) {
sdpPres = sdp.getDifferentialPressure();
DEBUG_SERIAL.println(""); // For debugging
DEBUG_SERIAL.println(sdpPres); // Print Differential Pressure for debugging
}
sdpPres = sdpPres + 0.02; // Offset if needed
sdpPres = sdpPres * (966 / absPres); // Correction of presssure differential wrt altitude/QNH
if (sdpPres < 0) { // If there is a negative pressure set Differential pressure to zero
sdpPres = 0;
}
buildMSG(); // Call function to build the TotalVario sentence, then transmit to BLE
}
}
// readDPS+++++++++++
void readDPS()
// read Atmospheric Pressure from DPS sensor
{
if (currentMillis >= previousDPSMillis + IntervalDPS) // run 'readDPS' function only once time is up
{
previousDPSMillis = currentMillis; // save the time when change was made
sensors_event_t temp_event, pressure_event;
while (!dps.temperatureAvailable() || !dps.pressureAvailable()) {
return; // wait until there's something to read
}
dps.getEvents(&temp_event, &pressure_event);
absPres = pressure_event.pressure;
}
}
// readSHT+++++++++++
void readSHT()
// read Temperature & Humidity
{
if (currentMillis >= previousSHTMillis + IntervalSHT) // run 'readTH' function only once time is up
{
previousSHTMillis = currentMillis; // save the time when change was made
sht4x.measureHighPrecision(ambTemp, ambHumi);
DEBUG_SERIAL.println(ambTemp); // For debugging
DEBUG_SERIAL.println(ambHumi); // For debugging
// ahtValue = aht20.readTemperature(); //read 6-bytes via I2C, takes 80 milliseconds
// ambTemp = ahtValue;
// DEBUG_SERIAL.println(ambTemp); // For debugging
// ahtValue = aht20.readHumidity(); //read another 6-bytes via I2C, takes 80 milliseconds
// ambHumi = ahtValue;
// DEBUG_SERIAL.println(ambHumi); // For debugging
}
}
// readBTV+++++++++++
void readBTV()
// read battery Volts
{
if (currentMillis >= previousBTVMillis + IntervalBTV) // run 'readBV' function only once time is up
{
previousBTVMillis = currentMillis; // save the time when change was made
batVolt = batVolt = analogRead(PIN_VBAT) * (0.003515625F) * (1510.0 / 510.0);
batPrct = -4370 + (2105 * batVolt) - (247.5144 * sq(batVolt));
// approximates battery SOC from Volts. Uses quadratic to approx.
if (batPrct < 0) // If battery Voltage is less than 0% set batPrct to 0%
{
batPrct = 0;
}
if (batPrct > 100) // If battery Voltage is more than 100% set batPrct to 100%
{
batPrct = 100;
}
battChrg = !digitalRead(23);
DEBUG_SERIAL.println(battChrg); // For debugging
DEBUG_SERIAL.println(batVolt); // For debugging
DEBUG_SERIAL.println(batPrct); // For debugging
}
}
//================ MESSAGE BUILD FUNCTIONS =====================
// $PTVSOAR,<type>,<value>,<type>,<value>,...*<checksum>
// Note: Checksum is optional
// buildMSG+++++++++++
void buildMSG() // Function to build sentence
{
// Non-data Half of $PTVSOAR Sentence:
String msg = F("$PTVSOAR,MNA,FlyingSilicon,MMO,ProbeS4,MSN,2023Q2001");
// SECOND Half of $PTVSOAR Sentence:
//String msg = F("$PTV"); // Short PTV sentence structure (if using).
//(Note: PTV does not use 'type' characters (eg: "PIT"). Rather placeholders are used.)
char buf[100]; // enough characters
sprintf(buf,
",PIT,%06.2f"
",PRS,%08.3f"
",OAT,%+03.0f"
",OAH,%03.0f"
",PCT,%03.0f"
",CHG,%d"
,
sdpPres // Pitot Differential Pressure 999.99
,
absPres // Atmospheric Pressure 9999.999
,
ambTemp // Air Temperature -99 .. +99
,
ambHumi // Air Relative Humidity 000 - 100
,
batPrct // Battery State of Charge % 000 - 100
,
battChrg > 0 ? 1 : 0); // Battery Charging Flag
msg += buf;
msg.concat(" \n"); // End of sentence notifier for TV
printMSG(msg); // Call the printMSG function to transmit to BLE then come back
}
// END OF BUILD MSG ---------------------------------------------------------
// printMSG+++++++++++
void printMSG(String msg) // Function to transmit to BLE
{
uint8_t buf[64]; // create a variable 'buf1' of size 64
msg.getBytes(buf, msg.length() + 1); // move msg1 characters into 'buf', for length of msg
bleuart.write(buf, msg.length() + 0); // write 'buf2' characters to BLE, for length of msg
msg = ""; // Clear the msg buffer for next read and print
}
// END OF BLE UART WRITE MSG FUNCTION ----------------------------------------