Don't report unreliable data. Also translate Pa -> hPa, which is a more commonly used unit.
This commit is contained in:
parent
0767d1661f
commit
d67511fbb5
|
@ -15,17 +15,13 @@ monitor_speed = 9600
|
||||||
platform = atmelsam
|
platform = atmelsam
|
||||||
board = nano_33_iot
|
board = nano_33_iot
|
||||||
framework = arduino
|
framework = arduino
|
||||||
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
WiFiNINA
|
WiFiNINA
|
||||||
MQTT
|
MQTT
|
||||||
|
|
||||||
[env:d1_mini]
|
[env:d1_mini]
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
board = d1_mini
|
board = d1_mini
|
||||||
framework = arduino
|
framework = arduino
|
||||||
upload_protocol = esptool
|
upload_protocol = esptool
|
||||||
|
lib_deps = MQTT
|
||||||
lib_deps =
|
|
||||||
MQTT
|
|
||||||
|
|
||||||
|
|
121
src/main.cpp
121
src/main.cpp
|
@ -3,58 +3,41 @@
|
||||||
#include <WiFi_functions.h>
|
#include <WiFi_functions.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#if !defined(HAS_WIFI)
|
|
||||||
#error ("This example program has been created for specific WiFi enabled hosts only.")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
WiFiClient client;
|
WiFiClient client;
|
||||||
|
|
||||||
// Buffers for assembling http POST requests
|
// Buffers for assembling http POST requests.
|
||||||
char postBuffer[450] = {0};
|
char postBuffer[450] = {0};
|
||||||
char fieldBuffer[70] = {0};
|
char fieldBuffer[70] = {0};
|
||||||
|
|
||||||
// Structs for data
|
// Structs for holding measurement data.
|
||||||
AirData_t airData = {0};
|
AirData_t airData = {0};
|
||||||
AirQualityData_t airQualityData = {0};
|
AirQualityData_t airQualityData = {0};
|
||||||
LightData_t lightData = {0};
|
LightData_t lightData = {0};
|
||||||
ParticleData_t particleData = {0};
|
ParticleData_t particleData = {0};
|
||||||
SoundData_t soundData = {0};
|
SoundData_t soundData = {0};
|
||||||
|
|
||||||
// Define the display attributes of data sent to Home Assistant.
|
// Definition of the display attributes of data sent to Home Assistant.
|
||||||
// The chosen name, unit and icon will appear in on the overview
|
|
||||||
// dashboard in Home Assistant. The icons can be chosen from
|
|
||||||
// https://cdn.materialdesignicons.com/5.3.45/
|
|
||||||
// (remove the "mdi-" part from the icon name).
|
|
||||||
// The attribute fields are: {name, unit, icon, decimal places}
|
// The attribute fields are: {name, unit, icon, decimal places}
|
||||||
HA_Attributes_t pressure = {"Pressure","Pa","weather-cloudy",0};
|
HA_Attributes_t pressure = {"Pressure", "hPa", "weather-cloudy", 0};
|
||||||
HA_Attributes_t humidity = {"Humidity","%","water-percent",1};
|
HA_Attributes_t humidity = {"Humidity","%","water-percent",1};
|
||||||
|
HA_Attributes_t temperature = {"Temperature", CELSIUS_SYMBOL, "thermometer", 1};
|
||||||
HA_Attributes_t illuminance = {"Illuminance","lx","white-balance-sunny",2};
|
HA_Attributes_t illuminance = {"Illuminance","lx","white-balance-sunny",2};
|
||||||
HA_Attributes_t soundLevel = {"Sound level","dBA","microphone",1};
|
HA_Attributes_t soundLevel = {"Sound level","dBA","microphone",1};
|
||||||
HA_Attributes_t peakAmplitude = {"Sound peak","mPa","waveform",2};
|
HA_Attributes_t peakAmplitude = {"Sound peak","mPa","waveform",2};
|
||||||
HA_Attributes_t estimatedCO2 = {"Estimated CO2","ppm","chart-bubble",1};
|
HA_Attributes_t estimatedCO2 = {"Estimated CO2","ppm","chart-bubble",1};
|
||||||
HA_Attributes_t equivalentBreathVOC = {"Equivalent breath VOC","ppm","chart-bubble",2};
|
HA_Attributes_t equivalentBreathVOC = {"Equivalent breath VOC","ppm","chart-bubble",2};
|
||||||
HA_Attributes_t AQI = {"Air Quality Index"," ","thought-bubble-outline",1};
|
HA_Attributes_t AQ_index = {"Air Quality Index", " ", "thought-bubble-outline", 1};
|
||||||
HA_Attributes_t AQ_assessment = {"Air quality assessment","","flower-tulip",0};
|
HA_Attributes_t AQ_assessment = {"Air quality assessment","","flower-tulip",0};
|
||||||
HA_Attributes_t AQ_calibration = {"Air quality calibration","","flower-tulip",0};
|
HA_Attributes_t AQ_calibration = {"Air quality calibration","","flower-tulip",0};
|
||||||
#if (PARTICLE_SENSOR == PARTICLE_SENSOR_PPD42)
|
HA_Attributes_t particleConcentration = {"Particle concentration", "ppL", "chart-bubble", 0};
|
||||||
HA_Attributes_t particulates = {"Particle concentration","ppL","chart-bubble",0};
|
HA_Attributes_t particleCalibration = {"Particle calibration", "", "chart-bubble", 0};
|
||||||
#else
|
|
||||||
HA_Attributes_t particulates = {"Particle concentration",SDS011_UNIT_SYMBOL,"chart-bubble",2};
|
|
||||||
#endif
|
|
||||||
#ifdef USE_FAHRENHEIT
|
|
||||||
HA_Attributes_t temperature = {"Temperature",FAHRENHEIT_SYMBOL,"thermometer",1};
|
|
||||||
#else
|
|
||||||
HA_Attributes_t temperature = {"Temperature",CELSIUS_SYMBOL,"thermometer",1};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
void setup()
|
||||||
void setup() {
|
{
|
||||||
// Initialize the host's pins, set up the serial port and reset:
|
|
||||||
SensorHardwareSetup(I2C_ADDRESS);
|
SensorHardwareSetup(I2C_ADDRESS);
|
||||||
|
|
||||||
connectToWiFi(WIFI_SSID, WIFI_PASSWORD);
|
connectToWiFi(WIFI_SSID, WIFI_PASSWORD);
|
||||||
|
|
||||||
// Apply settings to the MS430 and enter cycle mode
|
// Apply settings to the Metriful MS430 board and enter cycle mode.
|
||||||
uint8_t particleSensorCode = PARTICLE_SENSOR;
|
uint8_t particleSensorCode = PARTICLE_SENSOR;
|
||||||
uint8_t cycle_period = CYCLE_PERIOD;
|
uint8_t cycle_period = CYCLE_PERIOD;
|
||||||
TransmitI2C(I2C_ADDRESS, PARTICLE_SENSOR_SELECT_REG, &particleSensorCode, 1);
|
TransmitI2C(I2C_ADDRESS, PARTICLE_SENSOR_SELECT_REG, &particleSensorCode, 1);
|
||||||
|
@ -63,12 +46,12 @@ void setup() {
|
||||||
TransmitI2C(I2C_ADDRESS, CYCLE_MODE_CMD, 0, 0);
|
TransmitI2C(I2C_ADDRESS, CYCLE_MODE_CMD, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the data to Home Assistant as an HTTP POST request.
|
// Send data to Home Assistant as an HTTP POST request.
|
||||||
void http_POST_Home_Assistant(const HA_Attributes_t * attributes, const char * valueText) {
|
void http_POST_Home_Assistant(const HA_Attributes_t * attributes, const char * valueText) {
|
||||||
client.stop();
|
client.stop();
|
||||||
if (client.connect(HOME_ASSISTANT_IP, 8123)) {
|
if (client.connect(HOME_ASSISTANT_IP, 8123)) {
|
||||||
// Form the URL from the name but replace spaces with underscores
|
// Form the URL from the name but replace spaces with underscores
|
||||||
strcpy(fieldBuffer,attributes->name);
|
strcpy(fieldBuffer, attributes->name);
|
||||||
for (uint8_t i=0; i<strlen(fieldBuffer); i++) {
|
for (uint8_t i=0; i<strlen(fieldBuffer); i++) {
|
||||||
if (fieldBuffer[i] == ' ') {
|
if (fieldBuffer[i] == ' ') {
|
||||||
fieldBuffer[i] = '_';
|
fieldBuffer[i] = '_';
|
||||||
|
@ -81,9 +64,10 @@ void http_POST_Home_Assistant(const HA_Attributes_t * attributes, const char * v
|
||||||
client.println("Authorization: Bearer " LONG_LIVED_ACCESS_TOKEN);
|
client.println("Authorization: Bearer " LONG_LIVED_ACCESS_TOKEN);
|
||||||
|
|
||||||
// Assemble the JSON content string:
|
// Assemble the JSON content string:
|
||||||
sprintf(postBuffer,"{\"state\":%s,\"attributes\":{\"unit_of_measurement\""
|
sprintf(postBuffer,
|
||||||
":\"%s\",\"friendly_name\":\"%s\",\"icon\":\"mdi:%s\"}}",
|
"{\"state\":%s,\"attributes\":{\"unit_of_measurement\""
|
||||||
valueText, attributes->unit, attributes->name, attributes->icon);
|
":\"%s\",\"friendly_name\":\"%s\",\"icon\":\"mdi:%s\"}}",
|
||||||
|
valueText, attributes->unit, attributes->name, attributes->icon);
|
||||||
|
|
||||||
sprintf(fieldBuffer,"Content-Length: %u", strlen(postBuffer));
|
sprintf(fieldBuffer,"Content-Length: %u", strlen(postBuffer));
|
||||||
client.println(fieldBuffer);
|
client.println(fieldBuffer);
|
||||||
|
@ -95,7 +79,7 @@ void http_POST_Home_Assistant(const HA_Attributes_t * attributes, const char * v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send numeric data with specified sign, integer and fractional parts
|
// Send numeric data with specified sign, integer and fractional parts.
|
||||||
void sendNumericData(const HA_Attributes_t * attributes, uint32_t valueInteger,
|
void sendNumericData(const HA_Attributes_t * attributes, uint32_t valueInteger,
|
||||||
uint8_t valueDecimal, bool isPositive) {
|
uint8_t valueDecimal, bool isPositive) {
|
||||||
char valueText[20] = {0};
|
char valueText[20] = {0};
|
||||||
|
@ -115,22 +99,24 @@ void sendNumericData(const HA_Attributes_t * attributes, uint32_t valueInteger,
|
||||||
http_POST_Home_Assistant(attributes, valueText);
|
http_POST_Home_Assistant(attributes, valueText);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a text string: must have quotation marks added
|
// Send a text string: must have quotation marks added.
|
||||||
void sendTextData(const HA_Attributes_t * attributes, const char * valueText) {
|
void sendTextData(const HA_Attributes_t * attributes, const char * valueText) {
|
||||||
char quotedText[20] = {0};
|
char quotedText[20] = {0};
|
||||||
sprintf(quotedText,"\"%s\"", valueText);
|
sprintf(quotedText,"\"%s\"", valueText);
|
||||||
http_POST_Home_Assistant(attributes, quotedText);
|
http_POST_Home_Assistant(attributes, quotedText);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void wait_for_new_readings()
|
||||||
|
{
|
||||||
// Wait for the next new data release, indicated by a falling edge on READY
|
Serial.println("Waiting for new data from sensors ...");
|
||||||
Serial.println("Waiting for new data ...");
|
|
||||||
while (!ready_assertion_event) {
|
while (!ready_assertion_event) {
|
||||||
yield();
|
yield();
|
||||||
}
|
}
|
||||||
ready_assertion_event = false;
|
ready_assertion_event = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_sensor_data_from_i2c()
|
||||||
|
{
|
||||||
// Read data from the MS430 into the data structs.
|
// Read data from the MS430 into the data structs.
|
||||||
Serial.println("Reading data from sensors");
|
Serial.println("Reading data from sensors");
|
||||||
ReceiveI2C(I2C_ADDRESS, AIR_DATA_READ, (uint8_t *) &airData, AIR_DATA_BYTES);
|
ReceiveI2C(I2C_ADDRESS, AIR_DATA_READ, (uint8_t *) &airData, AIR_DATA_BYTES);
|
||||||
|
@ -138,41 +124,64 @@ void loop() {
|
||||||
ReceiveI2C(I2C_ADDRESS, LIGHT_DATA_READ, (uint8_t *) &lightData, LIGHT_DATA_BYTES);
|
ReceiveI2C(I2C_ADDRESS, LIGHT_DATA_READ, (uint8_t *) &lightData, LIGHT_DATA_BYTES);
|
||||||
ReceiveI2C(I2C_ADDRESS, SOUND_DATA_READ, (uint8_t *) &soundData, SOUND_DATA_BYTES);
|
ReceiveI2C(I2C_ADDRESS, SOUND_DATA_READ, (uint8_t *) &soundData, SOUND_DATA_BYTES);
|
||||||
ReceiveI2C(I2C_ADDRESS, PARTICLE_DATA_READ, (uint8_t *) &particleData, PARTICLE_DATA_BYTES);
|
ReceiveI2C(I2C_ADDRESS, PARTICLE_DATA_READ, (uint8_t *) &particleData, PARTICLE_DATA_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
// Check that WiFi is still connected
|
void make_sure_wifi_is_connected() {
|
||||||
uint8_t wifiStatus = WiFi.status();
|
uint8_t wifiStatus = WiFi.status();
|
||||||
if (wifiStatus != WL_CONNECTED) {
|
if (wifiStatus != WL_CONNECTED) {
|
||||||
// There is a problem with the WiFi connection: attempt to reconnect.
|
Serial.print("Wifi diconnected! Status: ");
|
||||||
Serial.print("Wifi status: ");
|
|
||||||
Serial.println(interpret_WiFi_status(wifiStatus));
|
Serial.println(interpret_WiFi_status(wifiStatus));
|
||||||
connectToWiFi(WIFI_SSID, WIFI_PASSWORD);
|
connectToWiFi(WIFI_SSID, WIFI_PASSWORD);
|
||||||
ready_assertion_event = false;
|
ready_assertion_event = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_data_to_home_assistant() {
|
||||||
|
Serial.println("Send data to Home Assistant");
|
||||||
|
|
||||||
uint8_t T_intPart = 0;
|
uint8_t T_intPart = 0;
|
||||||
uint8_t T_fractionalPart = 0;
|
uint8_t T_fractionalPart = 0;
|
||||||
bool isPositive = true;
|
bool isPositive = true;
|
||||||
getTemperature(&airData, &T_intPart, &T_fractionalPart, &isPositive);
|
getTemperature(&airData, &T_intPart, &T_fractionalPart, &isPositive);
|
||||||
|
|
||||||
// Send data to Home Assistant
|
|
||||||
Serial.println("Send data to Home Assistant");
|
|
||||||
sendNumericData(&temperature, (uint32_t) T_intPart, T_fractionalPart, isPositive);
|
sendNumericData(&temperature, (uint32_t) T_intPart, T_fractionalPart, isPositive);
|
||||||
sendNumericData(&pressure, (uint32_t) airData.P_Pa, 0, true);
|
sendNumericData(&pressure, (uint32_t)airData.P_Pa / 100, 0, true);
|
||||||
sendNumericData(&humidity, (uint32_t) airData.H_pc_int, airData.H_pc_fr_1dp, true);
|
sendNumericData(&humidity, (uint32_t) airData.H_pc_int, airData.H_pc_fr_1dp, true);
|
||||||
sendNumericData(&illuminance, (uint32_t) lightData.illum_lux_int, lightData.illum_lux_fr_2dp, true);
|
sendNumericData(&illuminance, (uint32_t) lightData.illum_lux_int, lightData.illum_lux_fr_2dp, true);
|
||||||
sendNumericData(&soundLevel, (uint32_t) soundData.SPL_dBA_int, soundData.SPL_dBA_fr_1dp, true);
|
sendNumericData(&soundLevel, (uint32_t) soundData.SPL_dBA_int, soundData.SPL_dBA_fr_1dp, true);
|
||||||
sendNumericData(&peakAmplitude, (uint32_t) soundData.peak_amp_mPa_int,
|
sendNumericData(&peakAmplitude, (uint32_t) soundData.peak_amp_mPa_int, soundData.peak_amp_mPa_fr_2dp, true);
|
||||||
soundData.peak_amp_mPa_fr_2dp, true);
|
|
||||||
sendNumericData(&AQI, (uint32_t) airQualityData.AQI_int, airQualityData.AQI_fr_1dp, true);
|
|
||||||
if (PARTICLE_SENSOR != PARTICLE_SENSOR_OFF) {
|
|
||||||
sendNumericData(&particulates, (uint32_t) particleData.concentration_int,
|
|
||||||
particleData.concentration_fr_2dp, true);
|
|
||||||
}
|
|
||||||
sendTextData(&AQ_assessment, interpret_AQI_value(airQualityData.AQI_int));
|
|
||||||
sendNumericData(&estimatedCO2, (uint32_t) airQualityData.CO2e_int, airQualityData.CO2e_fr_1dp, true);
|
|
||||||
sendNumericData(&equivalentBreathVOC, (uint32_t) airQualityData.bVOC_int, airQualityData.bVOC_fr_2dp, true);
|
|
||||||
sendNumericData(&AQ_calibration, airQualityData.AQI_accuracy, 0, true);
|
|
||||||
|
|
||||||
|
// When the particle sensor is not connected, we get 65535 readings.
|
||||||
|
// We want to skip those. Also skip sending data while the sensor
|
||||||
|
// is warming up. This way, invalid data is not logged in
|
||||||
|
// Home Assistant.
|
||||||
|
sendNumericData(&particleCalibration, (uint32_t)particleData.valid, 0, true);
|
||||||
|
if (particleData.valid != 0 && particleData.concentration_int < 60000)
|
||||||
|
{
|
||||||
|
sendNumericData(&particleConcentration, (uint32_t)particleData.concentration_int, particleData.concentration_fr_2dp, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// While the Metriful board is still in its self-calibration
|
||||||
|
// phase, do not send air quality data to Home Assistant.
|
||||||
|
sendNumericData(&AQ_calibration, airQualityData.AQI_accuracy, 0, true);
|
||||||
|
if (airQualityData.AQI_accuracy > 0) {
|
||||||
|
sendNumericData(&estimatedCO2, (uint32_t) airQualityData.CO2e_int, airQualityData.CO2e_fr_1dp, true);
|
||||||
|
sendNumericData(&equivalentBreathVOC, (uint32_t) airQualityData.bVOC_int, airQualityData.bVOC_fr_2dp, true);
|
||||||
|
sendNumericData(&AQ_index, (uint32_t)airQualityData.AQI_int, airQualityData.AQI_fr_1dp, true);
|
||||||
|
sendTextData(&AQ_assessment, interpret_AQI_value(airQualityData.AQI_int));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_measurements_to_console() {
|
||||||
printAirData(&airData, false);
|
printAirData(&airData, false);
|
||||||
printParticleData(&particleData, false, PARTICLE_SENSOR);
|
printParticleData(&particleData, false, PARTICLE_SENSOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
wait_for_new_readings();
|
||||||
|
read_sensor_data_from_i2c();
|
||||||
|
make_sure_wifi_is_connected();
|
||||||
|
send_data_to_home_assistant();
|
||||||
|
log_measurements_to_console();
|
||||||
|
}
|
Loading…
Reference in New Issue