TotalVario Arduino Sketch with updated comments section

  

30 January 2023 update of post: 25 Jan 23

 
TotalVario Data Sentences: A NMEA(like) sentence with the following parameters:

$PTVSOAR

  • MNA Manufacturer name, ASCII 127, no [$*,]
  • MMO Model, ASCII 127, no [$*,]
  • MSN Manufacturers device serial number, ASCII 127, no [$*,]
  • OAT Outside air temperature in deg C
  • OAH Outside air humidity
  • PRS Static pressure in hPa
  • PIT Pitot differential pressure in Pa
  • VOL Battery voltage in V
  • PCT Battery percent
  • VAR Vario in m/s
  • TEV TE Vario in m/s
  • EXT Reserved, control button/event identifer etc, ASCII 127, no [$*,]

Examples:
$PTVSOAR,OAT,21.45,OAH,42.42,PRS,1013.25,PIT,88.5,PCT,34.5
$PTVSOAR,MNA,FlyingSilicon,MMO,FS1,MSN,A9999-,OAT,21.45,OAH,42.42,PRS,1013.25,PIT,88.5,VOL,3.7,PCT,50.0,VAR,-1.3,TEV,1.2,EXT,XYZ

Updated comments section of sketch:

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

      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 interrogate 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 <AHTxx.h>      // Asair AHTx library

// ---------CONSTANTS---------------

// Function Timing
const int IntervalSDP = 199;   // number of mS before IAS function repeats and also sends all BLE data
const int IntervalPOL = 500;   // number of mS before Poll HardwareBLESerial function repeats
const int IntervalAHT = 4800;  // number of mS before Temperature Humidity function repeats
const int IntervalHUM = 9700;  // number of mS before Humidity function prints RH%
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
AHTxx aht20(AHTXX_ADDRESS_X38, AHT2x_SENSOR);  //sensor address, sensor type

//------------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 Variables
float sdpPres;  // Variable for the differential 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 ahtValue;  // To store T/Rh result temporarily
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 previousAHTMillis = 0;
long previousHUMMillis = 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);
  //delay(200);  // Let serial console settle

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

  // Initialise Asair AHT2x Temp|Humidity Sensor
  while (aht20.begin() != true) {
    delay(200);
  }

  // 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 IASsends to buildMSG for BLEprinting
  readAHT();                 // This function reads ambient Temperature and sends to buildMSG for BLEprinting
  readHUM();                 // This function prints ambient Humidity and sends to buildMSG for BLEprinting
  readBTV();                 // This function reads Battery Voltage and sends to buildMSG for BLEprinting
  flashLED();                // This function flashes the LED
}


//================ 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 snsor, 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();
    }
    sdpPres = sdpPres - 0.02;
    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
  }
}

// readAHT+++++++++++
void readAHT()  // read Temp & Hum and print Temp (only temperature)
{
  if (currentMillis >= previousAHTMillis + IntervalAHT)  // run 'readTH' function only once time is up
  {
    previousAHTMillis = currentMillis;   // save the time when change was made
    ahtValue = aht20.readTemperature();  //read 6-bytes via I2C, takes 80 milliseconds
    ambTemp = ahtValue;
  }
}

// readHUM+++++++++++
void readHUM()  // print Humidity
{
  if (currentMillis >= previousHUMMillis + IntervalHUM)  // run 'readTH' function only once time is up
  {
    previousHUMMillis = currentMillis;  // save the time when change was made
    ahtValue = aht20.readHumidity();    //read another 6-bytes via I2C, takes 80 milliseconds
    ambHumi = ahtValue;
  }
}

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

// 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 (TotalVario $PTVSOAR checksum is optional)
  msg += F("$PTVSOAR");
  msg += F(",");
  msg += F("PIT");
  msg += F(",");
  msg += String(sdpPres, 2);
  msg += F(",");
  msg += F("OAT");
  msg += F(",");
  msg += String(ambTemp, 2);
  msg += F(",");
  msg += F("OAH");
  msg += F(",");
  msg += String(ambHumi, 2);
  msg += F(",");
  msg += F("PCT");
  msg += F(",");
  msg += String(batPrct, 1);

  // 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 = "";
}