598 lines
22 KiB
C++
598 lines
22 KiB
C++
/*
|
|
Metriful_sensor.cpp
|
|
|
|
This file defines functions which are used in the code examples.
|
|
|
|
Copyright 2020 Metriful Ltd.
|
|
Licensed under the MIT License - for further details see LICENSE.txt
|
|
|
|
For code examples, datasheet and user guide, visit
|
|
https://github.com/metriful/sensor
|
|
*/
|
|
|
|
#include "Metriful_sensor.h"
|
|
#include "host_pin_definitions.h"
|
|
|
|
// The Arduino Wire library has a limited internal buffer size:
|
|
#define ARDUINO_WIRE_BUFFER_LIMIT_BYTES 32
|
|
|
|
void SensorHardwareSetup(uint8_t i2c_7bit_address) {
|
|
|
|
pinMode(LED_BUILTIN, OUTPUT);
|
|
|
|
#ifdef ESP8266
|
|
// Must specify the I2C pins
|
|
Wire.begin(SDA_PIN, SCL_PIN);
|
|
digitalWrite(LED_BUILTIN, HIGH);
|
|
#else
|
|
// Default I2C pins are used
|
|
Wire.begin();
|
|
digitalWrite(LED_BUILTIN, LOW);
|
|
#endif
|
|
|
|
Wire.setClock(I2C_CLK_FREQ_HZ);
|
|
|
|
// READY, light interrupt and sound interrupt lines are digital inputs.
|
|
pinMode(READY_PIN, INPUT);
|
|
pinMode(L_INT_PIN, INPUT);
|
|
pinMode(S_INT_PIN, INPUT);
|
|
|
|
// Set up interrupt monitoring of the READY signal, triggering on a falling edge
|
|
// event (high-to-low voltage change) indicating READY assertion. The
|
|
// function ready_ISR() will be called when this happens.
|
|
attachInterrupt(digitalPinToInterrupt(READY_PIN), ready_ISR, FALLING);
|
|
|
|
// Start the serial port.
|
|
// Full settings are: 8 data bits, no parity, one stop bit
|
|
Serial.begin(SERIAL_BAUD_RATE);
|
|
|
|
// Wait for the MS430 to finish power-on initialization:
|
|
while (digitalRead(READY_PIN) == HIGH) {
|
|
yield();
|
|
}
|
|
|
|
// Reset to clear any previous state:
|
|
TransmitI2C(i2c_7bit_address, RESET_CMD, 0, 0);
|
|
delay(5);
|
|
|
|
// Wait for reset completion and entry to standby mode
|
|
while (digitalRead(READY_PIN) == HIGH) {
|
|
yield();
|
|
}
|
|
}
|
|
|
|
volatile bool ready_assertion_event = false;
|
|
|
|
// This function is automatically called after a falling edge (assertion) of READY.
|
|
// The flag variable is set true - it must be set false again in the main program.
|
|
void ISR_ATTRIBUTE ready_ISR(void) {
|
|
ready_assertion_event = true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
// Functions to convert data from integer representation to floating-point representation.
|
|
// Floats are easy to use for writing programs but require greater memory and processing
|
|
// power resources, so may not always be appropriate.
|
|
|
|
void convertAirDataF(const AirData_t * airData_in, AirData_F_t * airDataF_out) {
|
|
// Decode the signed value for T (in Celsius)
|
|
airDataF_out->T_C = convertEncodedTemperatureToFloat(airData_in->T_C_int_with_sign,
|
|
airData_in->T_C_fr_1dp);
|
|
airDataF_out->P_Pa = airData_in->P_Pa;
|
|
airDataF_out->H_pc = ((float) airData_in->H_pc_int) + (((float) airData_in->H_pc_fr_1dp)/10.0);
|
|
airDataF_out->G_Ohm = airData_in->G_ohm;
|
|
}
|
|
|
|
void convertAirQualityDataF(const AirQualityData_t * airQualityData_in,
|
|
AirQualityData_F_t * airQualityDataF_out) {
|
|
airQualityDataF_out->AQI = ((float) airQualityData_in->AQI_int) +
|
|
(((float) airQualityData_in->AQI_fr_1dp)/10.0);
|
|
airQualityDataF_out->CO2e = ((float) airQualityData_in->CO2e_int) +
|
|
(((float) airQualityData_in->CO2e_fr_1dp)/10.0);
|
|
airQualityDataF_out->bVOC = ((float) airQualityData_in->bVOC_int) +
|
|
(((float) airQualityData_in->bVOC_fr_2dp)/100.0);
|
|
airQualityDataF_out->AQI_accuracy = airQualityData_in->AQI_accuracy;
|
|
}
|
|
|
|
void convertLightDataF(const LightData_t * lightData_in, LightData_F_t * lightDataF_out) {
|
|
lightDataF_out->illum_lux = ((float) lightData_in->illum_lux_int) +
|
|
(((float) lightData_in->illum_lux_fr_2dp)/100.0);
|
|
lightDataF_out->white = lightData_in->white;
|
|
}
|
|
|
|
void convertSoundDataF(const SoundData_t * soundData_in, SoundData_F_t * soundDataF_out) {
|
|
soundDataF_out->SPL_dBA = ((float) soundData_in->SPL_dBA_int) +
|
|
(((float) soundData_in->SPL_dBA_fr_1dp)/10.0);
|
|
for (uint16_t i=0; i<SOUND_FREQ_BANDS; i++) {
|
|
soundDataF_out->SPL_bands_dB[i] = ((float) soundData_in->SPL_bands_dB_int[i]) +
|
|
(((float) soundData_in->SPL_bands_dB_fr_1dp[i])/10.0);
|
|
}
|
|
soundDataF_out->peakAmp_mPa = ((float) soundData_in->peak_amp_mPa_int) +
|
|
(((float) soundData_in->peak_amp_mPa_fr_2dp)/100.0);
|
|
soundDataF_out->stable = (soundData_in->stable == 1);
|
|
}
|
|
|
|
void convertParticleDataF(const ParticleData_t * particleData_in, ParticleData_F_t * particleDataF_out) {
|
|
particleDataF_out->duty_cycle_pc = ((float) particleData_in->duty_cycle_pc_int) +
|
|
(((float) particleData_in->duty_cycle_pc_fr_2dp)/100.0);
|
|
particleDataF_out->concentration = ((float) particleData_in->concentration_int) +
|
|
(((float) particleData_in->concentration_fr_2dp)/100.0);
|
|
particleDataF_out->valid = (particleData_in->valid == 1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
// The following five functions print data (in floating-point
|
|
// representation) over the serial port as text
|
|
|
|
void printAirDataF(const AirData_F_t * airDataF) {
|
|
Serial.print("Temperature = ");
|
|
#ifdef USE_FAHRENHEIT
|
|
float temperature_F = convertCtoF(airDataF->T_C);
|
|
Serial.print(temperature_F,1);Serial.println(" " FAHRENHEIT_SYMBOL);
|
|
#else
|
|
Serial.print(airDataF->T_C,1);Serial.println(" " CELSIUS_SYMBOL);
|
|
#endif
|
|
Serial.print("Pressure = ");Serial.print(airDataF->P_Pa);Serial.println(" Pa");
|
|
Serial.print("Humidity = ");Serial.print(airDataF->H_pc,1);Serial.println(" %");
|
|
Serial.print("Gas Sensor Resistance = ");Serial.print(airDataF->G_Ohm);Serial.println(" " OHM_SYMBOL);
|
|
}
|
|
|
|
void printAirQualityDataF(const AirQualityData_F_t * airQualityDataF) {
|
|
if (airQualityDataF->AQI_accuracy > 0) {
|
|
Serial.print("Air Quality Index = ");Serial.print(airQualityDataF->AQI,1);
|
|
Serial.print(" (");
|
|
Serial.print(interpret_AQI_value((uint16_t) airQualityDataF->AQI));
|
|
Serial.println(")");
|
|
Serial.print("Estimated CO" SUBSCRIPT_2 " = ");Serial.print(airQualityDataF->CO2e,1);
|
|
Serial.println(" ppm");
|
|
Serial.print("Equivalent Breath VOC = ");Serial.print(airQualityDataF->bVOC,2);
|
|
Serial.println(" ppm");
|
|
}
|
|
Serial.print("Air Quality Accuracy: ");
|
|
Serial.println(interpret_AQI_accuracy(airQualityDataF->AQI_accuracy));
|
|
}
|
|
|
|
void printLightDataF(const LightData_F_t * lightDataF) {
|
|
Serial.print("Illuminance = ");Serial.print(lightDataF->illum_lux,2);Serial.println(" lux");
|
|
Serial.print("White Light Level = ");Serial.print(lightDataF->white);Serial.println();
|
|
}
|
|
|
|
void printSoundDataF(const SoundData_F_t * soundDataF) {
|
|
char strbuf[50] = {0};
|
|
Serial.print("A-weighted Sound Pressure Level = ");
|
|
Serial.print(soundDataF->SPL_dBA,1);Serial.println(" dBA");
|
|
for (uint16_t i=0; i<SOUND_FREQ_BANDS; i++) {
|
|
sprintf(strbuf,"Frequency Band %u (%u Hz) SPL = ", i+1, sound_band_mids_Hz[i]);
|
|
Serial.print(strbuf);
|
|
Serial.print(soundDataF->SPL_bands_dB[i],1);Serial.println(" dB");
|
|
}
|
|
Serial.print("Peak Sound Amplitude = ");Serial.print(soundDataF->peakAmp_mPa,2);Serial.println(" mPa");
|
|
}
|
|
|
|
void printParticleDataF(const ParticleData_F_t * particleDataF, uint8_t particleSensor) {
|
|
Serial.print("Particle Duty Cycle = ");Serial.print(particleDataF->duty_cycle_pc,2);Serial.println(" %");
|
|
Serial.print("Particle Concentration = ");
|
|
Serial.print(particleDataF->concentration,2);
|
|
if (particleSensor == PARTICLE_SENSOR_PPD42) {
|
|
Serial.println(" ppL");
|
|
}
|
|
else if (particleSensor == PARTICLE_SENSOR_SDS011) {
|
|
Serial.println(" " SDS011_UNIT_SYMBOL);
|
|
}
|
|
else {
|
|
Serial.println(" (?)");
|
|
}
|
|
Serial.print("Particle data valid: ");
|
|
if (particleDataF->valid) {
|
|
Serial.println("Yes");
|
|
}
|
|
else {
|
|
Serial.println("No (Initializing)");
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
// The following five functions print data (in integer representation) over the serial port as text.
|
|
// printColumns determines the print format:
|
|
// choosing printColumns = false gives labeled values with measurement units
|
|
// choosing printColumns = true gives columns of numbers (convenient for spreadsheets).
|
|
|
|
void printAirData(const AirData_t * airData, bool printColumns) {
|
|
char strbuf[50] = {0};
|
|
|
|
uint8_t T_intPart = 0;
|
|
uint8_t T_fractionalPart = 0;
|
|
bool isPositive = true;
|
|
const char * T_unit = getTemperature(airData, &T_intPart, &T_fractionalPart, &isPositive);
|
|
|
|
if (printColumns) {
|
|
// Print: temperature, pressure/Pa, humidity/%, gas sensor resistance/ohm
|
|
sprintf(strbuf,"%s%u.%u %" PRIu32 " %u.%u %" PRIu32 " ",isPositive?"":"-", T_intPart, T_fractionalPart,
|
|
airData->P_Pa, airData->H_pc_int, airData->H_pc_fr_1dp, airData->G_ohm);
|
|
Serial.print(strbuf);
|
|
}
|
|
else {
|
|
sprintf(strbuf,"Temperature = %s%u.%u %s", isPositive?"":"-", T_intPart, T_fractionalPart, T_unit);
|
|
Serial.println(strbuf);
|
|
Serial.print("Pressure = ");Serial.print(airData->P_Pa);Serial.println(" Pa");
|
|
sprintf(strbuf,"Humidity = %u.%u %%",airData->H_pc_int,airData->H_pc_fr_1dp);
|
|
Serial.println(strbuf);
|
|
Serial.print("Gas Sensor Resistance = ");Serial.print(airData->G_ohm);Serial.println(" " OHM_SYMBOL);
|
|
}
|
|
}
|
|
|
|
void printAirQualityData(const AirQualityData_t * airQualityData, bool printColumns) {
|
|
char strbuf[50] = {0};
|
|
if (printColumns) {
|
|
// Print: Air Quality Index, Estimated CO2/ppm, Equivalent breath VOC/ppm, Accuracy
|
|
sprintf(strbuf,"%u.%u %u.%u %u.%02u %u ",airQualityData->AQI_int, airQualityData->AQI_fr_1dp,
|
|
airQualityData->CO2e_int, airQualityData->CO2e_fr_1dp,
|
|
airQualityData->bVOC_int, airQualityData->bVOC_fr_2dp, airQualityData->AQI_accuracy);
|
|
Serial.print(strbuf);
|
|
}
|
|
else {
|
|
if (airQualityData->AQI_accuracy > 0) {
|
|
sprintf(strbuf,"Air Quality Index = %u.%u (%s)",
|
|
airQualityData->AQI_int, airQualityData->AQI_fr_1dp, interpret_AQI_value(airQualityData->AQI_int));
|
|
Serial.println(strbuf);
|
|
sprintf(strbuf,"Estimated CO" SUBSCRIPT_2 " = %u.%u ppm",
|
|
airQualityData->CO2e_int, airQualityData->CO2e_fr_1dp);
|
|
Serial.println(strbuf);
|
|
sprintf(strbuf,"Equivalent Breath VOC = %u.%02u ppm",
|
|
airQualityData->bVOC_int, airQualityData->bVOC_fr_2dp);
|
|
Serial.println(strbuf);
|
|
}
|
|
Serial.print("Air Quality Accuracy: ");
|
|
Serial.println(interpret_AQI_accuracy(airQualityData->AQI_accuracy));
|
|
}
|
|
}
|
|
|
|
void printSoundData(const SoundData_t * soundData, bool printColumns) {
|
|
char strbuf[50] = {0};
|
|
if (printColumns) {
|
|
// Print: Sound pressure level/dBA, Sound pressure level for frequency bands 1 to 6 (six columns),
|
|
// Peak sound amplitude/mPa, stability
|
|
sprintf(strbuf,"%u.%u ", soundData->SPL_dBA_int, soundData->SPL_dBA_fr_1dp);
|
|
Serial.print(strbuf);
|
|
for (uint16_t i=0; i<SOUND_FREQ_BANDS; i++) {
|
|
sprintf(strbuf,"%u.%u ", soundData->SPL_bands_dB_int[i], soundData->SPL_bands_dB_fr_1dp[i]);
|
|
Serial.print(strbuf);
|
|
}
|
|
sprintf(strbuf,"%u.%02u %u ", soundData->peak_amp_mPa_int,
|
|
soundData->peak_amp_mPa_fr_2dp, soundData->stable);
|
|
Serial.print(strbuf);
|
|
}
|
|
else {
|
|
sprintf(strbuf,"A-weighted Sound Pressure Level = %u.%u dBA",
|
|
soundData->SPL_dBA_int, soundData->SPL_dBA_fr_1dp);
|
|
Serial.println(strbuf);
|
|
for (uint8_t i=0; i<SOUND_FREQ_BANDS; i++) {
|
|
sprintf(strbuf,"Frequency Band %u (%u Hz) SPL = %u.%u dB",
|
|
i+1, sound_band_mids_Hz[i], soundData->SPL_bands_dB_int[i], soundData->SPL_bands_dB_fr_1dp[i]);
|
|
Serial.println(strbuf);
|
|
}
|
|
sprintf(strbuf,"Peak Sound Amplitude = %u.%02u mPa",
|
|
soundData->peak_amp_mPa_int, soundData->peak_amp_mPa_fr_2dp);
|
|
Serial.println(strbuf);
|
|
}
|
|
}
|
|
|
|
void printLightData(const LightData_t * lightData, bool printColumns) {
|
|
char strbuf[50] = {0};
|
|
if (printColumns) {
|
|
// Print: illuminance/lux, white level
|
|
sprintf(strbuf,"%u.%02u %u ", lightData->illum_lux_int, lightData->illum_lux_fr_2dp, lightData->white);
|
|
Serial.print(strbuf);
|
|
}
|
|
else {
|
|
sprintf(strbuf,"Illuminance = %u.%02u lux", lightData->illum_lux_int, lightData->illum_lux_fr_2dp);
|
|
Serial.println(strbuf);
|
|
Serial.print("White Light Level = ");Serial.print(lightData->white);Serial.println();
|
|
}
|
|
}
|
|
|
|
void printParticleData(const ParticleData_t * particleData, bool printColumns, uint8_t particleSensor) {
|
|
char strbuf[50] = {0};
|
|
if (printColumns) {
|
|
// Print: duty cycle/%, concentration
|
|
sprintf(strbuf,"%u.%02u %u.%02u %u ", particleData->duty_cycle_pc_int,
|
|
particleData->duty_cycle_pc_fr_2dp, particleData->concentration_int,
|
|
particleData->concentration_fr_2dp, particleData->valid);
|
|
Serial.print(strbuf);
|
|
}
|
|
else {
|
|
sprintf(strbuf,"Particle Duty Cycle = %u.%02u %%",
|
|
particleData->duty_cycle_pc_int, particleData->duty_cycle_pc_fr_2dp);
|
|
Serial.println(strbuf);
|
|
sprintf(strbuf,"Particle Concentration = %u.%02u ",
|
|
particleData->concentration_int, particleData->concentration_fr_2dp);
|
|
Serial.print(strbuf);
|
|
if (particleSensor == PARTICLE_SENSOR_PPD42) {
|
|
Serial.println("ppL");
|
|
}
|
|
else if (particleSensor == PARTICLE_SENSOR_SDS011) {
|
|
Serial.println(SDS011_UNIT_SYMBOL);
|
|
}
|
|
else {
|
|
Serial.println("(?)");
|
|
}
|
|
Serial.print("Particle data valid: ");
|
|
if (particleData->valid == 0) {
|
|
Serial.println("No (Initializing)");
|
|
}
|
|
else {
|
|
Serial.println("Yes");
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
// Send data to the Metriful MS430 using the I2C-compatible two wire interface.
|
|
//
|
|
// Returns true on success, false on failure.
|
|
//
|
|
// dev_addr_7bit = the 7-bit I2C address of the MS430 board.
|
|
// commandRegister = the settings register code or command code to be used.
|
|
// data = array containing the data to be sent; its length must be at least "data_length" bytes.
|
|
// data_length = the number of bytes from the "data" array to be sent.
|
|
//
|
|
bool TransmitI2C(uint8_t dev_addr_7bit, uint8_t commandRegister, uint8_t data[], uint8_t data_length) {
|
|
|
|
if (data_length > ARDUINO_WIRE_BUFFER_LIMIT_BYTES) {
|
|
// The Arduino Wire library has a limited internal buffer size
|
|
return false;
|
|
}
|
|
|
|
Wire.beginTransmission(dev_addr_7bit);
|
|
uint8_t bytesWritten = Wire.write(commandRegister);
|
|
if (data_length > 0) {
|
|
bytesWritten += Wire.write(data, data_length);
|
|
}
|
|
if (bytesWritten != (data_length+1)) {
|
|
return false;
|
|
}
|
|
|
|
return (Wire.endTransmission(true) == 0);
|
|
}
|
|
|
|
// Read data from the Metriful MS430 using the I2C-compatible two wire interface.
|
|
//
|
|
// Returns true on success, false on failure.
|
|
//
|
|
// dev_addr_7bit = the 7-bit I2C address of the MS430 board.
|
|
// commandRegister = the settings register code or data location code to be used.
|
|
// data = array to store the received data; its length must be at least "data_length" bytes.
|
|
// data_length = the number of bytes to read.
|
|
//
|
|
bool ReceiveI2C(uint8_t dev_addr_7bit, uint8_t commandRegister, uint8_t data[], uint8_t data_length) {
|
|
|
|
if (data_length == 0) {
|
|
// Cannot do a zero byte read
|
|
return false;
|
|
}
|
|
|
|
if (data_length > ARDUINO_WIRE_BUFFER_LIMIT_BYTES) {
|
|
// The Arduino Wire library has a limited internal buffer size
|
|
return false;
|
|
}
|
|
|
|
Wire.beginTransmission(dev_addr_7bit);
|
|
Wire.write(commandRegister);
|
|
if (Wire.endTransmission(false) != 0) {
|
|
return false;
|
|
}
|
|
|
|
if (Wire.requestFrom(dev_addr_7bit, data_length, (uint8_t) 1) != data_length) {
|
|
// Did not receive the expected number of bytes
|
|
return false;
|
|
}
|
|
|
|
for (uint8_t i=0; i<data_length; i++) {
|
|
if (Wire.available() > 0) {
|
|
data[i] = Wire.read();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
// Provide a readable interpretation of the accuracy code for
|
|
// the air quality measurements (applies to all air quality data)
|
|
const char * interpret_AQI_accuracy(uint8_t AQI_accuracy_code) {
|
|
switch (AQI_accuracy_code) {
|
|
default:
|
|
case 0:
|
|
return "Not yet valid, self-calibration incomplete";
|
|
case 1:
|
|
return "Low accuracy, self-calibration ongoing";
|
|
case 2:
|
|
return "Medium accuracy, self-calibration ongoing";
|
|
case 3:
|
|
return "High accuracy";
|
|
}
|
|
}
|
|
|
|
// Provide a readable interpretation of the AQI (air quality index)
|
|
const char * interpret_AQI_value(uint16_t AQI) {
|
|
if (AQI < 50) {
|
|
return "Good";
|
|
}
|
|
else if (AQI < 100) {
|
|
return "Acceptable";
|
|
}
|
|
else if (AQI < 150) {
|
|
return "Substandard";
|
|
}
|
|
else if (AQI < 200) {
|
|
return "Poor";
|
|
}
|
|
else if (AQI < 300) {
|
|
return "Bad";
|
|
}
|
|
else {
|
|
return "Very bad";
|
|
}
|
|
}
|
|
|
|
// Set the threshold for triggering a sound interrupt.
|
|
//
|
|
// Returns true on success, false on failure.
|
|
//
|
|
// threshold_mPa = peak sound amplitude threshold in milliPascals, any 16-bit integer is allowed.
|
|
bool setSoundInterruptThreshold(uint8_t dev_addr_7bit, uint16_t threshold_mPa) {
|
|
uint8_t TXdata[SOUND_INTERRUPT_THRESHOLD_BYTES] = {0};
|
|
TXdata[0] = (uint8_t) (threshold_mPa & 0x00FF);
|
|
TXdata[1] = (uint8_t) (threshold_mPa >> 8);
|
|
return TransmitI2C(dev_addr_7bit, SOUND_INTERRUPT_THRESHOLD_REG, TXdata, SOUND_INTERRUPT_THRESHOLD_BYTES);
|
|
}
|
|
|
|
// Set the threshold for triggering a light interrupt.
|
|
//
|
|
// Returns true on success, false on failure.
|
|
//
|
|
// The threshold value in lux units can be fractional and is formed as:
|
|
// threshold = thres_lux_int + (thres_lux_fr_2dp/100)
|
|
//
|
|
// Threshold values exceeding MAX_LUX_VALUE will be limited to MAX_LUX_VALUE.
|
|
bool setLightInterruptThreshold(uint8_t dev_addr_7bit, uint16_t thres_lux_int, uint8_t thres_lux_fr_2dp) {
|
|
uint8_t TXdata[LIGHT_INTERRUPT_THRESHOLD_BYTES] = {0};
|
|
TXdata[0] = (uint8_t) (thres_lux_int & 0x00FF);
|
|
TXdata[1] = (uint8_t) (thres_lux_int >> 8);
|
|
TXdata[2] = thres_lux_fr_2dp;
|
|
return TransmitI2C(dev_addr_7bit, LIGHT_INTERRUPT_THRESHOLD_REG, TXdata, LIGHT_INTERRUPT_THRESHOLD_BYTES);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
// Convenience functions for reading data (integer representation)
|
|
//
|
|
// For each category of data (air, sound, etc.) a pointer to the data
|
|
// struct is passed to the ReceiveI2C() function. The received byte
|
|
// sequence fills the data struct in the correct order so that each
|
|
// field within the struct receives the value of an environmental data
|
|
// quantity (temperature, sound level, etc.)
|
|
|
|
SoundData_t getSoundData(uint8_t i2c_7bit_address) {
|
|
SoundData_t soundData = {0};
|
|
ReceiveI2C(i2c_7bit_address, SOUND_DATA_READ, (uint8_t *) &soundData, SOUND_DATA_BYTES);
|
|
return soundData;
|
|
}
|
|
|
|
AirData_t getAirData(uint8_t i2c_7bit_address) {
|
|
AirData_t airData = {0};
|
|
ReceiveI2C(i2c_7bit_address, AIR_DATA_READ, (uint8_t *) &airData, AIR_DATA_BYTES);
|
|
return airData;
|
|
}
|
|
|
|
LightData_t getLightData(uint8_t i2c_7bit_address) {
|
|
LightData_t lightData = {0};
|
|
ReceiveI2C(i2c_7bit_address, LIGHT_DATA_READ, (uint8_t *) &lightData, LIGHT_DATA_BYTES);
|
|
return lightData;
|
|
}
|
|
|
|
AirQualityData_t getAirQualityData(uint8_t i2c_7bit_address) {
|
|
AirQualityData_t airQualityData = {0};
|
|
ReceiveI2C(i2c_7bit_address, AIR_QUALITY_DATA_READ, (uint8_t *) &airQualityData, AIR_QUALITY_DATA_BYTES);
|
|
return airQualityData;
|
|
}
|
|
|
|
ParticleData_t getParticleData(uint8_t i2c_7bit_address) {
|
|
ParticleData_t particleData = {0};
|
|
ReceiveI2C(i2c_7bit_address, PARTICLE_DATA_READ, (uint8_t *) &particleData, PARTICLE_DATA_BYTES);
|
|
return particleData;
|
|
}
|
|
|
|
// Convenience functions for reading data (float representation)
|
|
|
|
SoundData_F_t getSoundDataF(uint8_t i2c_7bit_address) {
|
|
SoundData_F_t soundDataF = {0};
|
|
SoundData_t soundData = getSoundData(i2c_7bit_address);
|
|
convertSoundDataF(&soundData, &soundDataF);
|
|
return soundDataF;
|
|
}
|
|
|
|
AirData_F_t getAirDataF(uint8_t i2c_7bit_address) {
|
|
AirData_F_t airDataF = {0};
|
|
AirData_t airData = getAirData(i2c_7bit_address);
|
|
convertAirDataF(&airData, &airDataF);
|
|
return airDataF;
|
|
}
|
|
|
|
LightData_F_t getLightDataF(uint8_t i2c_7bit_address) {
|
|
LightData_F_t lightDataF = {0};
|
|
LightData_t lightData = getLightData(i2c_7bit_address);
|
|
convertLightDataF(&lightData, &lightDataF);
|
|
return lightDataF;
|
|
}
|
|
|
|
AirQualityData_F_t getAirQualityDataF(uint8_t i2c_7bit_address) {
|
|
AirQualityData_F_t airQualityDataF = {0};
|
|
AirQualityData_t airQualityData = getAirQualityData(i2c_7bit_address);
|
|
convertAirQualityDataF(&airQualityData, &airQualityDataF);
|
|
return airQualityDataF;
|
|
}
|
|
|
|
ParticleData_F_t getParticleDataF(uint8_t i2c_7bit_address) {
|
|
ParticleData_F_t particleDataF = {0};
|
|
ParticleData_t particleData = getParticleData(i2c_7bit_address);
|
|
convertParticleDataF(&particleData, &particleDataF);
|
|
return particleDataF;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
// Functions to convert Celsius temperature to Fahrenheit, in float
|
|
// and integer formats
|
|
|
|
float convertCtoF(float C) {
|
|
return ((C*1.8) + 32.0);
|
|
}
|
|
|
|
// Convert Celsius to Fahrenheit in sign, integer and fractional parts
|
|
void convertCtoF_int(float C, uint8_t * F_int, uint8_t * F_fr_1dp, bool * isPositive) {
|
|
float F = convertCtoF(C);
|
|
bool isNegative = (F < 0.0);
|
|
if (isNegative) {
|
|
F = -F;
|
|
}
|
|
F += 0.05;
|
|
F_int[0] = (uint8_t) F;
|
|
F -= (float) F_int[0];
|
|
F_fr_1dp[0] = (uint8_t) (F*10.0);
|
|
isPositive[0] = (!isNegative);
|
|
}
|
|
|
|
// Decode and convert the temperature as read from the MS430 (integer
|
|
// representation) into a float value
|
|
float convertEncodedTemperatureToFloat(uint8_t T_C_int_with_sign, uint8_t T_C_fr_1dp) {
|
|
float temperature_C = ((float) (T_C_int_with_sign & TEMPERATURE_VALUE_MASK)) +
|
|
(((float) T_C_fr_1dp)/10.0);
|
|
if ((T_C_int_with_sign & TEMPERATURE_SIGN_MASK) != 0) {
|
|
// the most-significant bit is set, indicating that the temperature is negative
|
|
temperature_C = -temperature_C;
|
|
}
|
|
return temperature_C;
|
|
}
|
|
|
|
// Obtain temperature, in chosen units (C or F), as sign, integer and fractional parts
|
|
const char * getTemperature(const AirData_t * pAirData, uint8_t * T_intPart,
|
|
uint8_t * T_fractionalPart, bool * isPositive) {
|
|
#ifdef USE_FAHRENHEIT
|
|
float temperature_C = convertEncodedTemperatureToFloat(pAirData->T_C_int_with_sign,
|
|
pAirData->T_C_fr_1dp);
|
|
convertCtoF_int(temperature_C, T_intPart, T_fractionalPart, isPositive);
|
|
return FAHRENHEIT_SYMBOL;
|
|
#else
|
|
isPositive[0] = ((pAirData->T_C_int_with_sign & TEMPERATURE_SIGN_MASK) == 0);
|
|
T_intPart[0] = pAirData->T_C_int_with_sign & TEMPERATURE_VALUE_MASK;
|
|
T_fractionalPart[0] = pAirData->T_C_fr_1dp;
|
|
return CELSIUS_SYMBOL;
|
|
#endif
|
|
}
|