Updated Sketch now includes DPS310 Atmospheric Pressure Sensor





 

/*********************************************************************
  FOR USE WITH TOTALVARIO

  Tools > Board > Arduino MBed OS Boards > Seeed XIAO nRF52840
   XIAO BLE Arduino device driver bug in v2.7.2 (and v2.9.0)
   Note: See - https://forum.seeedstudio.com/t/xiao-ble-sense-mbed-2-7-2-battery-charge-and-voltage-monitor-analogread-p0-31-does-not-work/266438

   Note: (SEE 30th JANUARY NOTE BELOW)
          HardwareBLESerial library needs to be modified (HardwareBLESerial.h file) so that the BLE characterstics used are HM-10 compatible (not Nordic nRF)

   Current draw with BLE paired and sensors enabled = 7.7mA (measured at: 2Hz)
   
      20 April 23:  Reduced Abs Pressure to Oversampling x32 and 16 Measurements per second.
                    This to comply with DSP310 datasheet "possible combinations ... Pressure Measurement Rate and Oversampling".

      16 April 23:  Use Abs Pressure to correct Diff Pressure (formula in Sensirion tech sheet)
                    Changed code to accomodate the addition of a Infineon DPS310 absolute pressure sensor.

      23 March 23:
                    Changed code to accomodate replacement of AHT temp/hum sensor to: SHT45.

      31 January 23:
                    Changed SDP31 read interval to 99mS (10.1Hz).Preliminary testing seems to show stability of device at this setting.

      30 January 23:
                    BWing kindly changed TotalVario's BLE configuration, Nordic BLE UUID characteristics are now accepted.
                    These are the default characteristics used by Uberi's Arduino library for Nordic Semiconductors' proprietary
                    UART/Serial Port Emulation over BLE protocol, using ArduinoBLE.
                    https://github.com/Uberi/Arduino-HardwareBLESerial

      27 January 23:
                    Changed Battery Voltage output from Battery Volts, to Battery SOC %. Battery SOC now showing on TotalVario main display.
                    All functions working (with HM-10 BLE characteristics in HardwareBLESerial.h file, of HardwareBLESerial library)

      23 January 23:
                    Second version sketch - Works. Had to make changes to 2.9.0 mbed analogread library as per above.
                    TV not showing battery Voltage on main screen.

      Very good BLE Android Logger app: Serial Bluetooth Terminal 1.4.
      Setup for BLE Devices:
        Home button (three horizontal lines) > "Devices" > Tab: "Bluetooth LE" > "SCAN" > Device should be found - Long press on device in list of devices (long press!)
          "Edit" > "Custom" selection, then press "Service UUID" ... App will interogate device for UUID's, repeat for Read & Write chars too >
Go back and short press device.
            You should now see the data streaming in now.

*********************************************************************/

//========================================

// ----------LIBRARIES--------------

#include <HardwareBLESerial.h>
#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;  // number of mS before IAS function repeats and also sends all BLE data
const int IntervalDPS = 113;   // number of mS before DPS function repeats and collects Atmospheric Pressure
const int IntervalPOL = 500;   // number of mS before Poll HardwareBLESerial function repeats
const int IntervalSHT = 4800;  // number of mS before Temperature Humidity function repeats
const int IntervalBTV = 9963;  // number of mS before Battery Voltage function repeats
const int IntervalLED = 3000;  // number of mS before the LED indicating USB plugged in is on
const int LightLED = 500;      // millisecs that the LED indicating USB plugged in is on

// -----------CLASSES---------------

// BLE Services
HardwareBLESerial &bleSerial = HardwareBLESerial::getInstance();

// Dual Pressure Sensor
SDP3XSensor sdp;

// Temperature & Humidity Sensor
SensirionI2CSht4x sht4x;

// Absolute Pressure Sensor
Adafruit_DPS310 dps;
//Adafruit_Sensor *dps_pressure = dps.getPressureSensor();

//------------VARIABLES-------------

// 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;

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

// Battery Voltage Variables
int adcRead = 0;       // Variable for reading the Voltage-Divide network on Xiao
float batVolt = 4.00;  // Battery Volts
float batPrct = 50.0;  // Battery SOC %

// Battery Charging Variable
#define charging D10  // Variable for detecting USB plugged in - so battery is charging

// Ambient Environment Variables
float ambTemp = 0;
float ambHumi = 0;

// Sentence Buffer (String)
String msg = "";  // The final constructed sentence is: msg

// Function Timing
unsigned long currentMillis = 0;  // Stores the value of millis() in each iteration of loop()
unsigned long previousSDPMillis = 0;
unsigned long previousPOLMillis = 0;
long previousSHTMillis = 0;
long previousDPSMillis = 0;
long previousBTVMillis = 0;
long previousLEDMillis = 0;
bool flashLEDstate = 0;  // Used to record whether the LED is on or off


//===================SETUP========================

void setup() {

  // Initialize the LED's as outputs and ensure they are off.
  pinMode(LEDR, OUTPUT);
  digitalWrite(LEDR, HIGH);  // Ensure the LED off
  pinMode(LEDG, OUTPUT);
  digitalWrite(LEDG, HIGH);  // Ensure the LED off
  pinMode(LEDB, OUTPUT);
  digitalWrite(LEDB, HIGH);  // Ensure the LED off

  // Setup Xiao Battery Monitoring
  pinMode(P0_31, INPUT);     // Battery Voltage monitoring pin
  pinMode(P0_14, OUTPUT);    // Enable Battery Voltage monitoring pin
  digitalWrite(P0_14, LOW);  // Enable

  // Setup Xiao Battery Monitoring ADC
  analogReference(AR_INTERNAL2V4);  //Vref=2.4V
  analogReadResolution(12);         //12bits

  // Setup Charge Monitoring
  pinMode(D10, INPUT);  //USB Voltage divided and fed to D10. Low =< 0.99V, High => 2.31V

  // Setup Xiao Battery Charging Rate
  pinMode(P0_13, OUTPUT);    // Charge Current setting pin
  digitalWrite(P0_13, LOW);  // Charge Current = 100mA (13 High = 50mA)

  // Setup Buzzer Output
  pinMode(D1, OUTPUT);  // D1 output to Buzzer

  // Set up HardwareBLESerial and set BLE Name
  bleSerial.beginAndSetupBLE("Airspeed_probe");

  // Initialise I2C
  Wire.begin();

  // Initialise Serial Monitor (only used for debugging)
  //  Serial.begin(115200);
  //  while (!Serial) {
  //    delay(1000);
  //  }

  // Initialise Sensirion SDP
  int ret = sdp.init();
  if (ret == 0) {
  } else {
    while (true) {
      delay(500);
    }
  }

  // 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 combinations
  dps.configureTemperature(DPS310_2HZ, DPS310_2SAMPLES);

  // 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
  adcRead = analogRead(P0_31);
  batVolt = ((510e3 + 1000e3) / 510e3) * 2.4 * adcRead / 4096;
  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);
}

//=================== LOOP ========================

void loop() {
  currentMillis = millis();  // Capture the latest value of millis()
  pollBLE();                 // This function must be called regularly to perform BLE updates
  readSDP();                 // This function reads SDP, sends to variable then calls buildMSG for BLEprinting
  readDPS();                 // This function reads DPS function reads Atmospheric Pressure and sends to variable
  readSHT();                 // This function reads ambient Temperature/Humidity and sends to variable
  readBTV();                 // This function reads Battery Voltage and sends to variable
  flashLED();                // This function flashes the LED if USB is plugged (Battery charging)
}


//================ POLL BLESERIAL FUNCTION =====================

// pollBLE+++++++++++
void pollBLE()  // Call function to poll BLE
{
  if (currentMillis >= previousPOLMillis + IntervalPOL)  // run 'pollBLE' function only once time is up
  {
    previousPOLMillis = currentMillis;  // save the time when change was made to bleSerial.poll();
    bleSerial.poll();                   // this must be called regularly to perform BLE updates
  }
}


//================ SENSOR FUNCTIONS =====================

// readSPD+++++++++++
void readSDP()  // read Differential Pressure from SDP sensor, append to a NEMA message, then print / write to serial / BLE
{
  if (currentMillis >= previousSDPMillis + IntervalSDP)  // run 'readIAS' function only once time is up
  {
    previousSDPMillis = currentMillis;  // save the time when change was made
    int ret = sdp.readSample();
    if (ret == 0) {
      sdpPres = sdp.getDifferentialPressure();
      //      Serial.println(sdpPres);  // Print Differential Pressure for debugging
    }
    //sdpPres = sdpPres - 0.02;
    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);
    //    Serial.print(F("Pressure = "));
    //    Serial.println(pressure_event.pressure);
    absPres = pressure_event.pressure;
    //    Serial.println(absPres);  // Print Static Pressure for debugging
  }
}

// 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);
    //    Serial.println(ambTemp);  // Print OAT for debugging
    //    Serial.println(ambHumi);  // Print Humidity RH 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
    adcRead = analogRead(P0_31);
    batVolt = ((510e3 + 1000e3) / 510e3) * 2.4 * adcRead / 4096;
    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;
    }
    //    Serial.println(batVolt);  // Print Battery Voltage for debugging
    //    Serial.println(batPrct);  // Print Battery Percentage for debugging
  }
}

// flashLED+++++++++++
void flashLED()  // flash the LED if USB is plugged in
{
  // If the USB power is connected go to next step to send BLE "Charging" message and flash LED's
  if (flashLEDstate == 0)  // if the LED is off, wait for the interval to expire before turning it on
  {
    if (currentMillis - previousLEDMillis >= IntervalLED)  // time is up, so turn LED on
    {
      // Test to see if USB power is connected (so if battery is charging)
      int value = digitalRead(charging);
      if (value > 0) {
        //bleSerial.println("Battery is charging");  // BLE Print
        //Serial.println("Battery is charging");     // Serial Print
        digitalWrite(LEDB, LOW);           // LED on
        flashLEDstate = 1;                 // Set the LED state to 1 or "on"
        previousLEDMillis += IntervalLED;  // save the time when change was made
      }
    }
  } else  // ie if LED is on. If on, we must wait for the duration to expire before turning it off
  {
    if (currentMillis - previousLEDMillis >= LightLED)  // time is up, so turn LED off
    {
      digitalWrite(LEDB, HIGH);       // LED off
      flashLEDstate = 0;              // Set the LED state to 0 or "off"
      previousLEDMillis += LightLED;  // save the time when change was made
    }
  }
}

//================ MESSAGE BUILD FUNCTIONS =====================

// $PTVSOAR,<type>,<value>,<type>,<value>,...*<checksum>
// Note: Checksum is optional

// buildMSG+++++++++++
void buildMSG() {
  // TotalVario Sentence without checksum (checksum is optional)
  msg += F("$PTVSOAR"); // Protocol Identifier
  msg += F(",");
  msg += F("MNA");
  msg += F(",");
  msg += F("FlyingSilicon"); // Device Manufacturer (free text modify as required)
  msg += F(",");
  msg += F("MMO");
  msg += F(",");
  msg += F("ProbeS4"); // Device Model (free text modify as required)
  msg += F(",");
  msg += F("MSN");
  msg += F(",");
  msg += F("2023Q2001"); // Device Serial Identifier (free text modify as required)
  msg += F(",");
  msg += F("PIT");
  msg += F(",");
  msg += String(sdpPres, 2); // Pitot Differential Pressure
  msg += F(",");
  msg += F("PRS");
  msg += F(",");
  msg += String(absPres, 2); // Atmospheric Pressure
  msg += F(",");
  msg += F("OAT");
  msg += F(",");
  msg += String(ambTemp, 2); // Air Temerature
  msg += F(",");
  msg += F("OAH");
  msg += F(",");
  msg += String(ambHumi, 2); // Air Relative Humidity
  msg += F(",");
  msg += F("PCT");
  msg += F(",");
  msg += String(batPrct, 1); // Battery State of Charge %

  //  Serial.println(msg);  // Print msg for debugging

  // Convert string to a character array and BLE Print
  int buff_len = msg.length() + 1;
  char buff_array[buff_len];
  msg.toCharArray(buff_array, buff_len);
  bleSerial.println(buff_array);  // Print the entire message string

  // Clear the msg buffer for next read
  msg = "";
}