Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
metriful
committed
Nov 16, 2020
1 parent
392e0dc
commit 00200c9
Showing
49 changed files
with
4,577 additions
and
1,029 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
/* | ||
Home_Assistant.ino | ||
Example code for sending environment data from the Metriful MS430 to | ||
an installation of Home Assistant on your local WiFi network. | ||
For more information, visit www.home-assistant.io | ||
This example is designed for the following WiFi enabled hosts: | ||
* Arduino Nano 33 IoT | ||
* Arduino MKR WiFi 1010 | ||
* ESP8266 boards (e.g. Wemos D1, NodeMCU) | ||
* ESP32 boards (e.g. DOIT DevKit v1) | ||
Data are sent at regular intervals over your WiFi network to Home | ||
Assistant and can be viewed on the dashboard or used to control | ||
home automation tasks. More setup information is provided in the | ||
Readme and User Guide. | ||
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 <WiFi_functions.h> | ||
|
||
////////////////////////////////////////////////////////// | ||
// USER-EDITABLE SETTINGS | ||
|
||
// How often to read and report the data (every 3, 100 or 300 seconds) | ||
uint8_t cycle_period = CYCLE_PERIOD_100_S; | ||
|
||
// The details of the WiFi network: | ||
char SSID[] = "PUT WIFI NETWORK NAME HERE IN QUOTES"; // network SSID (name) | ||
char password[] = "PUT WIFI PASSWORD HERE IN QUOTES"; // network password | ||
|
||
// Home Assistant settings | ||
|
||
// You must have already installed Home Assistant on a computer on your | ||
// network. Go to www.home-assistant.io for help on this. | ||
|
||
// Choose a unique name for this MS430 sensor board so you can identify it. | ||
// Variables in HA will have names like: SENSOR_NAME.temperature, etc. | ||
#define SENSOR_NAME "kitchen3" | ||
|
||
// Change this to the IP address of the computer running Home Assistant. | ||
// You can find this from the admin interface of your router. | ||
#define HOME_ASSISTANT_IP "192.168.43.144" | ||
|
||
// Security access token: the Readme and User Guide explain how to get this | ||
#define LONG_LIVED_ACCESS_TOKEN "PASTE YOUR TOKEN HERE WITHIN QUOTES" | ||
|
||
// END OF USER-EDITABLE SETTINGS | ||
////////////////////////////////////////////////////////// | ||
|
||
#if !defined(HAS_WIFI) | ||
#error ("This example program has been created for specific WiFi enabled hosts only.") | ||
#endif | ||
|
||
WiFiClient client; | ||
|
||
// Buffers for assembling http POST requests | ||
char postBuffer[450] = {0}; | ||
char fieldBuffer[70] = {0}; | ||
|
||
// Structs for data | ||
AirData_t airData = {0}; | ||
AirQualityData_t airQualityData = {0}; | ||
LightData_t lightData = {0}; | ||
ParticleData_t particleData = {0}; | ||
SoundData_t soundData = {0}; | ||
|
||
// Define 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} | ||
HA_Attributes_t pressure = {"Pressure","Pa","weather-cloudy",0}; | ||
HA_Attributes_t humidity = {"Humidity","%","water-percent",1}; | ||
HA_Attributes_t illuminance = {"Illuminance","lx","white-balance-sunny",2}; | ||
HA_Attributes_t soundLevel = {"Sound level","dBA","microphone",1}; | ||
HA_Attributes_t peakAmplitude = {"Sound peak","mPa","waveform",2}; | ||
HA_Attributes_t AQI = {"Air Quality Index"," ","thought-bubble-outline",1}; | ||
HA_Attributes_t AQ_assessment = {"Air quality assessment","","flower-tulip",0}; | ||
#if (PARTICLE_SENSOR == PARTICLE_SENSOR_PPD42) | ||
HA_Attributes_t particulates = {"Particle concentration","ppL","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() { | ||
// Initialize the host's pins, set up the serial port and reset: | ||
SensorHardwareSetup(I2C_ADDRESS); | ||
|
||
connectToWiFi(SSID, password); | ||
|
||
// Apply settings to the MS430 and enter cycle mode | ||
uint8_t particleSensorCode = PARTICLE_SENSOR; | ||
TransmitI2C(I2C_ADDRESS, PARTICLE_SENSOR_SELECT_REG, &particleSensorCode, 1); | ||
TransmitI2C(I2C_ADDRESS, CYCLE_TIME_PERIOD_REG, &cycle_period, 1); | ||
ready_assertion_event = false; | ||
TransmitI2C(I2C_ADDRESS, CYCLE_MODE_CMD, 0, 0); | ||
} | ||
|
||
|
||
void loop() { | ||
|
||
// Wait for the next new data release, indicated by a falling edge on READY | ||
while (!ready_assertion_event) { | ||
yield(); | ||
} | ||
ready_assertion_event = false; | ||
|
||
// Read data from the MS430 into the data structs. | ||
ReceiveI2C(I2C_ADDRESS, AIR_DATA_READ, (uint8_t *) &airData, AIR_DATA_BYTES); | ||
ReceiveI2C(I2C_ADDRESS, AIR_QUALITY_DATA_READ, (uint8_t *) &airQualityData, AIR_QUALITY_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, PARTICLE_DATA_READ, (uint8_t *) &particleData, PARTICLE_DATA_BYTES); | ||
|
||
// Check that WiFi is still connected | ||
uint8_t wifiStatus = WiFi.status(); | ||
if (wifiStatus != WL_CONNECTED) { | ||
// There is a problem with the WiFi connection: attempt to reconnect. | ||
Serial.print("Wifi status: "); | ||
Serial.println(interpret_WiFi_status(wifiStatus)); | ||
connectToWiFi(SSID, password); | ||
ready_assertion_event = false; | ||
} | ||
|
||
uint8_t T_intPart = 0; | ||
uint8_t T_fractionalPart = 0; | ||
bool isPositive = true; | ||
getTemperature(&airData, &T_intPart, &T_fractionalPart, &isPositive); | ||
|
||
// Send data to Home Assistant | ||
sendNumericData(&temperature, (uint32_t) T_intPart, T_fractionalPart, isPositive); | ||
sendNumericData(&pressure, (uint32_t) airData.P_Pa, 0, 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(&soundLevel, (uint32_t) soundData.SPL_dBA_int, soundData.SPL_dBA_fr_1dp, true); | ||
sendNumericData(&peakAmplitude, (uint32_t) soundData.peak_amp_mPa_int, | ||
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)); | ||
} | ||
|
||
// Send numeric data with specified sign, integer and fractional parts | ||
void sendNumericData(const HA_Attributes_t * attributes, uint32_t valueInteger, | ||
uint8_t valueDecimal, bool isPositive) { | ||
char valueText[20] = {0}; | ||
const char * sign = isPositive ? "" : "-"; | ||
switch (attributes->decimalPlaces) { | ||
case 0: | ||
default: | ||
sprintf(valueText,"%s%" PRIu32, sign, valueInteger); | ||
break; | ||
case 1: | ||
sprintf(valueText,"%s%" PRIu32 ".%u", sign, valueInteger, valueDecimal); | ||
break; | ||
case 2: | ||
sprintf(valueText,"%s%" PRIu32 ".%02u", sign, valueInteger, valueDecimal); | ||
break; | ||
} | ||
http_POST_Home_Assistant(attributes, valueText); | ||
} | ||
|
||
// Send a text string: must have quotation marks added | ||
void sendTextData(const HA_Attributes_t * attributes, const char * valueText) { | ||
char quotedText[20] = {0}; | ||
sprintf(quotedText,"\"%s\"", valueText); | ||
http_POST_Home_Assistant(attributes, quotedText); | ||
} | ||
|
||
// Send the data to Home Assistant as an HTTP POST request. | ||
void http_POST_Home_Assistant(const HA_Attributes_t * attributes, const char * valueText) { | ||
client.stop(); | ||
if (client.connect(HOME_ASSISTANT_IP, 8123)) { | ||
// Form the URL from the name but replace spaces with underscores | ||
strcpy(fieldBuffer,attributes->name); | ||
for (uint8_t i=0; i<strlen(fieldBuffer); i++) { | ||
if (fieldBuffer[i] == ' ') { | ||
fieldBuffer[i] = '_'; | ||
} | ||
} | ||
sprintf(postBuffer,"POST /api/states/" SENSOR_NAME ".%s HTTP/1.1", fieldBuffer); | ||
client.println(postBuffer); | ||
client.println("Host: " HOME_ASSISTANT_IP ":8123"); | ||
client.println("Content-Type: application/json"); | ||
client.println("Authorization: Bearer " LONG_LIVED_ACCESS_TOKEN); | ||
|
||
// Assemble the JSON content string: | ||
sprintf(postBuffer,"{\"state\":%s,\"attributes\":{\"unit_of_measurement\"" | ||
":\"%s\",\"friendly_name\":\"%s\",\"icon\":\"mdi:%s\"}}", | ||
valueText, attributes->unit, attributes->name, attributes->icon); | ||
|
||
sprintf(fieldBuffer,"Content-Length: %u", strlen(postBuffer)); | ||
client.println(fieldBuffer); | ||
client.println(); | ||
client.print(postBuffer); | ||
} | ||
else { | ||
Serial.println("Client connection failed."); | ||
} | ||
} |
Oops, something went wrong.