|
|
|
@ -18,42 +18,30 @@
|
|
|
|
|
#include <Adafruit_NeoPixel.h>
|
|
|
|
|
#include <Wire.h>
|
|
|
|
|
#include <Adafruit_Sensor.h>
|
|
|
|
|
#ifdef BME680
|
|
|
|
|
#include "Adafruit_BME680.h"
|
|
|
|
|
#else
|
|
|
|
|
#ifdef BMP280
|
|
|
|
|
#include "Adafruit_BMP280.h"
|
|
|
|
|
#else
|
|
|
|
|
#error "Decition, which BMx??? is used missing"
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
|
* DEFINES
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
|
|
#define GPIO_WS2812 D4 /**< GPIO2 */
|
|
|
|
|
#define SENSOR_PM1006_RX D2 /**< GPIO4 */
|
|
|
|
|
#define SENSOR_PM1006_RX D2 /**< GPIO4 */
|
|
|
|
|
#define SENSOR_PM1006_TX -1 /**< Unused */
|
|
|
|
|
#define WITTY_RGB_R D8 /**< GPIO15 */
|
|
|
|
|
#define WITTY_RGB_G D6 /**< GPIO12 Used as 3.3V Power supply for the I2C Sensor */
|
|
|
|
|
#define WITTY_RGB_G D6 /**< GPIO12 */
|
|
|
|
|
#define WITTY_RGB_B D7 /**< GPIO13 */
|
|
|
|
|
#define PM1006_BIT_RATE 9600
|
|
|
|
|
#define PM1006_MQTT_UPDATE 30000 /**< Check the sensor every 30 seconds; New measurement is done every 20seconds by the PM1006 sensor */
|
|
|
|
|
#define PM1006_MQTT_UPDATE 5000 /**< Check the sensor every 10 seconds; New measurement is done every 20seconds by the sensor */
|
|
|
|
|
#define PIXEL_COUNT 3
|
|
|
|
|
#define GPIO_BUTTON SENSOR_PM1006_RX /**< Button and software serial share one pin on Witty board */
|
|
|
|
|
#define SENSOR_I2C_SCK D5 /**< GPIO14 - I2C clock pin */
|
|
|
|
|
#define SENSOR_I2C_SDI D1 /**< GPIO5 - I2C data pin */
|
|
|
|
|
#define SENSOR_I2C_SCK D1 /**< GPIO14 - I2C clock pin */
|
|
|
|
|
#define SENSOR_I2C_SDI D5 /**< GPIO5 - I2C data pin */
|
|
|
|
|
|
|
|
|
|
#define PM1006_BIT_RATE 9600
|
|
|
|
|
#define PM1006_MQTT_UPDATE 5000 /**< Check the sensor every 10 seconds; New measurement is done every 20seconds by the sensor */
|
|
|
|
|
|
|
|
|
|
#define SEALEVELPRESSURE_HPA (1013.25)
|
|
|
|
|
|
|
|
|
|
#define BUTTON_MAX_CYCLE 10000U /**< Action: Reset configuration */
|
|
|
|
|
#define BUTTON_MIN_ACTION_CYCLE 55U /**< Minimum cycle to react on the button (e.g. 5 second) */
|
|
|
|
|
#define BUTTON_CHECK_INTERVALL 100U /**< Check every 100 ms the button state */
|
|
|
|
|
|
|
|
|
|
#define MIN_MEASURED_CYCLES 2
|
|
|
|
|
#define PM_MAX 1001 /**< According datasheet https://en.gassensor.com.cn/ParticulateMatterSensor/info_itemid_105.html 1000 is the maximum */
|
|
|
|
|
|
|
|
|
|
#define LOG_TOPIC "log\0"
|
|
|
|
|
#define MQTT_LEVEL_ERROR 1
|
|
|
|
|
#define MQTT_LEVEL_WARNING 10
|
|
|
|
@ -74,8 +62,6 @@
|
|
|
|
|
strcat(topic, "/"); \
|
|
|
|
|
strcat(topic, test);
|
|
|
|
|
|
|
|
|
|
#define PERCENT2FACTOR(b, a) ((b * a.get()) / 100)
|
|
|
|
|
|
|
|
|
|
#define NUMBER_TYPE "Number"
|
|
|
|
|
#define NODE_PARTICLE "particle"
|
|
|
|
|
#define NODE_TEMPERATUR "temp"
|
|
|
|
@ -84,8 +70,6 @@
|
|
|
|
|
#define NODE_GAS "gas"
|
|
|
|
|
#define NODE_HUMIDITY "humidity"
|
|
|
|
|
#define NODE_AMBIENT "ambient"
|
|
|
|
|
#define NODE_BUTTON "button"
|
|
|
|
|
#define SERIAL_RCEVBUF_MAX 80 /**< Maximum 80 characters can be received from the PM1006 sensor */
|
|
|
|
|
/******************************************************************************
|
|
|
|
|
* TYPE DEFS
|
|
|
|
|
******************************************************************************/
|
|
|
|
@ -102,62 +86,36 @@ void log(int level, String message, int code);
|
|
|
|
|
|
|
|
|
|
bool mConfigured = false;
|
|
|
|
|
bool mConnected = false;
|
|
|
|
|
bool mOTAactive = false; /**< Stop sleeping, if OTA is running */
|
|
|
|
|
bool mFailedI2Cinitialization = false;
|
|
|
|
|
long mLastButtonAction = 0;
|
|
|
|
|
|
|
|
|
|
/******************************* Sensor data **************************/
|
|
|
|
|
HomieNode particle(NODE_PARTICLE, "particle", "number"); /**< Measuret in micro gram per quibik meter air volume */
|
|
|
|
|
HomieNode temperaturNode(NODE_TEMPERATUR, "Room Temperature", "number");
|
|
|
|
|
HomieNode temperatureNode(NODE_TEMPERATUR, "Room Temperature", "number");
|
|
|
|
|
HomieNode pressureNode(NODE_PRESSURE, "Pressure", "number");
|
|
|
|
|
HomieNode altitudeNode(NODE_ALTITUDE, "Altitude", "number");
|
|
|
|
|
#ifdef BME680
|
|
|
|
|
HomieNode gasNode(NODE_GAS, "Gas", "number");
|
|
|
|
|
HomieNode humidityNode(NODE_HUMIDITY, "Humidity", "number");
|
|
|
|
|
#endif
|
|
|
|
|
HomieNode buttonNode(NODE_BUTTON, "Button", "number");
|
|
|
|
|
|
|
|
|
|
/****************************** Output control ***********************/
|
|
|
|
|
HomieNode ledStripNode /* to rule them all */("led", "RGB led", "color");
|
|
|
|
|
|
|
|
|
|
/************************** Settings ******************************/
|
|
|
|
|
HomieSetting<bool> i2cEnable("i2c",
|
|
|
|
|
#ifdef BME680
|
|
|
|
|
"BME680 sensor present"
|
|
|
|
|
#else
|
|
|
|
|
#ifdef BMP280
|
|
|
|
|
"BMP280 sensor present"
|
|
|
|
|
#else
|
|
|
|
|
"No I2C sensor specified in the project"
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
);
|
|
|
|
|
HomieSetting<bool> rgbTemp("rgbTemp", "Show temperature via red (>20 °C) and blue (< 20°C)");
|
|
|
|
|
HomieSetting<long> rgbDim("rgbDim", "Factor (1 to 200%) of the status LEDs");
|
|
|
|
|
HomieSetting<long> deepsleep("deepsleep", "Amount of seconds to sleep (default 0 - always online, maximum 4294 - 71 minutes)");
|
|
|
|
|
HomieSetting<bool> i2cEnable("i2c", "BME280 sensor present");
|
|
|
|
|
HomieSetting<bool> rgbTemp("rgbTemp", "Show temperatur via red (>20 °C) and blue (< 20°C)");
|
|
|
|
|
|
|
|
|
|
static SoftwareSerial pmSerial(SENSOR_PM1006_RX, SENSOR_PM1006_TX);
|
|
|
|
|
#ifdef BME680
|
|
|
|
|
Adafruit_BME680 bmx(&Wire); // connected via I2C
|
|
|
|
|
#else
|
|
|
|
|
#ifdef BMP280
|
|
|
|
|
Adafruit_BMP280 bmx; // connected via I2C
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
Adafruit_BME680 bme(&Wire); // connected via I2C
|
|
|
|
|
|
|
|
|
|
Adafruit_NeoPixel strip(PIXEL_COUNT, GPIO_WS2812, NEO_GRB + NEO_KHZ800);
|
|
|
|
|
|
|
|
|
|
// Variablen
|
|
|
|
|
uint8_t serialRxBuf[SERIAL_RCEVBUF_MAX];
|
|
|
|
|
uint8_t serialRxBuf[80];
|
|
|
|
|
uint8_t rxBufIdx = 0;
|
|
|
|
|
int mParticle_pM25 = 0;
|
|
|
|
|
int spm25 = 0;
|
|
|
|
|
int last = 0;
|
|
|
|
|
unsigned int mButtonPressed = 0;
|
|
|
|
|
bool mSomethingReceived = false;
|
|
|
|
|
|
|
|
|
|
uint32_t mMeasureIndex = 0;
|
|
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
|
* LOCAL FUNCTIONS
|
|
|
|
|
*****************************************************************************/
|
|
|
|
@ -196,12 +154,7 @@ int getSensorData() {
|
|
|
|
|
// Header und Prüfsumme checken
|
|
|
|
|
if (serialRxBuf[0] == 0x16 && serialRxBuf[1] == 0x11 && serialRxBuf[2] == 0x0B /* && checksum == 0 */)
|
|
|
|
|
{
|
|
|
|
|
int pmValue = (serialRxBuf[5] << 8 | serialRxBuf[6]);
|
|
|
|
|
if (pmValue > PM_MAX) {
|
|
|
|
|
return (-1);
|
|
|
|
|
} else {
|
|
|
|
|
return pmValue;
|
|
|
|
|
}
|
|
|
|
|
return (serialRxBuf[5] << 8 | serialRxBuf[6]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -217,84 +170,53 @@ void onHomieEvent(const HomieEvent &event)
|
|
|
|
|
{
|
|
|
|
|
switch (event.type)
|
|
|
|
|
{
|
|
|
|
|
case HomieEventType::READY_TO_SLEEP:
|
|
|
|
|
if (mOTAactive) {
|
|
|
|
|
Homie.getLogger() << "Skip sleeping, as OTA was started" << endl;
|
|
|
|
|
return;
|
|
|
|
|
} else if (deepsleep.get() > 0) {
|
|
|
|
|
long sleepInSeconds = deepsleep.get();
|
|
|
|
|
Homie.doDeepSleep(sleepInSeconds * 1000000, RF_NO_CAL);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case HomieEventType::MQTT_READY:
|
|
|
|
|
mConnected=true;
|
|
|
|
|
digitalWrite(WITTY_RGB_R, LOW);
|
|
|
|
|
if (!i2cEnable.get()) { /** keep green LED activated to power I2C sensor */
|
|
|
|
|
digitalWrite(WITTY_RGB_G, LOW);
|
|
|
|
|
log(MQTT_LEVEL_INFO, F("I2C powersupply deactivated"), MQTT_LOG_I2CINIT);
|
|
|
|
|
}
|
|
|
|
|
digitalWrite(WITTY_RGB_B, LOW);
|
|
|
|
|
/* Update LED only, if not sleeping */
|
|
|
|
|
if (deepsleep.get() <= 0) {
|
|
|
|
|
strip.fill(strip.Color(0,0,PERCENT2FACTOR(127, rgbDim)));
|
|
|
|
|
strip.show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
strip.fill(strip.Color(0,0,128));
|
|
|
|
|
strip.show();
|
|
|
|
|
if (mFailedI2Cinitialization) {
|
|
|
|
|
log(MQTT_LEVEL_DEBUG,
|
|
|
|
|
#ifdef BME680
|
|
|
|
|
"Could not find a valid BME680 sensor, check wiring or try a different address!"
|
|
|
|
|
#else
|
|
|
|
|
#ifdef BMP280
|
|
|
|
|
"Could not find a valid BMP280 sensor, check wiring or try a different address!"
|
|
|
|
|
#else
|
|
|
|
|
"no I2C sensor defined"
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
, MQTT_LOG_I2CINIT);
|
|
|
|
|
log(MQTT_LEVEL_DEBUG, F("Could not find a valid BME680 sensor, check wiring or "
|
|
|
|
|
"try a different address!"), MQTT_LOG_I2CINIT);
|
|
|
|
|
} else {
|
|
|
|
|
log(MQTT_LEVEL_INFO, F("BME680 sensor found"), MQTT_LOG_I2CINIT);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case HomieEventType::READY_TO_SLEEP:
|
|
|
|
|
break;
|
|
|
|
|
case HomieEventType::OTA_STARTED:
|
|
|
|
|
mOTAactive = true;
|
|
|
|
|
break;
|
|
|
|
|
case HomieEventType::OTA_SUCCESSFUL:
|
|
|
|
|
ESP.restart();
|
|
|
|
|
break;
|
|
|
|
|
case HomieEventType::WIFI_CONNECTED:
|
|
|
|
|
digitalWrite(WITTY_RGB_B, HIGH);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void bmpPublishValues() {
|
|
|
|
|
#ifdef BME680
|
|
|
|
|
// Tell BME680 to begin measurement.
|
|
|
|
|
unsigned long endTime = bmx.beginReading();
|
|
|
|
|
unsigned long endTime = bme.beginReading();
|
|
|
|
|
if (endTime == 0) {
|
|
|
|
|
log(MQTT_LEVEL_ERROR, "BMX not accessible", MQTT_LOG_I2READ);
|
|
|
|
|
log(MQTT_LEVEL_ERROR, F("BME680 not accessable"), MQTT_LOG_I2READ);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
// Publish the values
|
|
|
|
|
temperaturNode.setProperty(NODE_TEMPERATUR).send(String(bmx.readTemperature()));
|
|
|
|
|
pressureNode.setProperty(NODE_PRESSURE).send(String(bmx.readPressure() / 100.0F));
|
|
|
|
|
altitudeNode.setProperty(NODE_ALTITUDE).send(String(bmx.readAltitude(SEALEVELPRESSURE_HPA)));
|
|
|
|
|
#ifdef BME680
|
|
|
|
|
gasNode.setProperty(NODE_GAS).send(String((bmx.gas_resistance / 1000.0)));
|
|
|
|
|
humidityNode.setProperty(NODE_HUMIDITY).send(String(bmx.humidity));
|
|
|
|
|
#endif
|
|
|
|
|
log(MQTT_LEVEL_DEBUG, String("Temp" + String(bmx.readTemperature()) + "\tPressure:" +
|
|
|
|
|
String(bmx.readPressure() / 100.0F) + "\t Altitude:"+
|
|
|
|
|
String(bmx.readAltitude(SEALEVELPRESSURE_HPA))), MQTT_LOG_I2READ);
|
|
|
|
|
temperatureNode.setProperty(NODE_TEMPERATUR).send(String(bme.readTemperature()));
|
|
|
|
|
pressureNode.setProperty(NODE_PRESSURE).send(String(bme.readPressure() / 100.0F));
|
|
|
|
|
altitudeNode.setProperty(NODE_ALTITUDE).send(String(bme.readAltitude(SEALEVELPRESSURE_HPA)));
|
|
|
|
|
gasNode.setProperty(NODE_GAS).send(String((bme.gas_resistance / 1000.0)));
|
|
|
|
|
|
|
|
|
|
humidityNode.setProperty(NODE_HUMIDITY).send(String(bme.humidity));
|
|
|
|
|
|
|
|
|
|
if ( (rgbTemp.get()) && (!mSomethingReceived) ) {
|
|
|
|
|
if (bmx.readTemperature() < TEMPBORDER) {
|
|
|
|
|
strip.setPixelColor(0, strip.Color(0,0,PERCENT2FACTOR(127, rgbDim)));
|
|
|
|
|
if (bme.readTemperature() < TEMPBORDER) {
|
|
|
|
|
strip.setPixelColor(0, strip.Color(0,0,255));
|
|
|
|
|
} else {
|
|
|
|
|
strip.setPixelColor(0, strip.Color(PERCENT2FACTOR(127, rgbDim),0,0));
|
|
|
|
|
strip.setPixelColor(0, strip.Color(255,0,0));
|
|
|
|
|
}
|
|
|
|
|
strip.show();
|
|
|
|
|
}
|
|
|
|
@ -315,16 +237,16 @@ void loopHandler()
|
|
|
|
|
{
|
|
|
|
|
static long lastRead = 0;
|
|
|
|
|
if ((millis() - lastRead) > PM1006_MQTT_UPDATE) {
|
|
|
|
|
mParticle_pM25 = getSensorData();
|
|
|
|
|
if (mParticle_pM25 >= 0) {
|
|
|
|
|
particle.setProperty(NODE_PARTICLE).send(String(mParticle_pM25));
|
|
|
|
|
int pM25 = getSensorData();
|
|
|
|
|
if (pM25 >= 0) {
|
|
|
|
|
particle.setProperty(NODE_PARTICLE).send(String(pM25));
|
|
|
|
|
if (!mSomethingReceived) {
|
|
|
|
|
if (mParticle_pM25 < 35) {
|
|
|
|
|
strip.fill(strip.Color(0, PERCENT2FACTOR(127, rgbDim), 0)); /* green */
|
|
|
|
|
} else if (mParticle_pM25 < 85) {
|
|
|
|
|
strip.fill(strip.Color(PERCENT2FACTOR(127, rgbDim), PERCENT2FACTOR(64, rgbDim), 0)); /* orange */
|
|
|
|
|
if (pM25 < 35) {
|
|
|
|
|
strip.fill(strip.Color(0, 255, 0)); /* green */
|
|
|
|
|
} else if (pM25 < 85) {
|
|
|
|
|
strip.fill(strip.Color(255, 127, 0)); /* orange */
|
|
|
|
|
} else {
|
|
|
|
|
strip.fill(strip.Color(PERCENT2FACTOR(127, rgbDim), 0, 0)); /* red */
|
|
|
|
|
strip.fill(strip.Color(255, 0, 0)); /* red */
|
|
|
|
|
}
|
|
|
|
|
strip.show();
|
|
|
|
|
}
|
|
|
|
@ -334,28 +256,9 @@ void loopHandler()
|
|
|
|
|
if (i2cEnable.get() && (!mFailedI2Cinitialization)) {
|
|
|
|
|
bmpPublishValues();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mMeasureIndex++;
|
|
|
|
|
|
|
|
|
|
/* Clean cycles buttons */
|
|
|
|
|
if (mButtonPressed <= BUTTON_MIN_ACTION_CYCLE) {
|
|
|
|
|
buttonNode.setProperty(NODE_BUTTON).send("0");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lastRead = millis();
|
|
|
|
|
|
|
|
|
|
/* If nothing needs to be done, sleep and the time is ready for sleeping */
|
|
|
|
|
if (mMeasureIndex > MIN_MEASURED_CYCLES && (deepsleep.get() > 0) ) {
|
|
|
|
|
Homie.prepareToSleep();
|
|
|
|
|
delay(100);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if the user sees something via the LEDs, inform MQTT, too */
|
|
|
|
|
if (mButtonPressed > BUTTON_MIN_ACTION_CYCLE) {
|
|
|
|
|
buttonNode.setProperty(NODE_BUTTON).send(String(mButtonPressed));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Feed the dog -> ESP stay alive
|
|
|
|
|
ESP.wdtFeed();
|
|
|
|
|
}
|
|
|
|
@ -365,30 +268,23 @@ void loopHandler()
|
|
|
|
|
bool ledHandler(const HomieRange& range, const String& value) {
|
|
|
|
|
if (range.isRange) return false; // only one switch is present
|
|
|
|
|
|
|
|
|
|
Homie.getLogger() << "Received: " << (value) << endl;
|
|
|
|
|
if (value.equals("250,250,250")) {
|
|
|
|
|
mSomethingReceived = false; // enable animation again
|
|
|
|
|
ledStripNode.setProperty(NODE_AMBIENT).send(value);
|
|
|
|
|
mSomethingReceived = true; // Stop animation
|
|
|
|
|
|
|
|
|
|
int sep1 = value.indexOf(',');
|
|
|
|
|
int sep2 = value.indexOf(',', sep1 + 1);
|
|
|
|
|
if ((sep1 > 0) && (sep2 > 0)) {
|
|
|
|
|
int red = value.substring(0,sep1).toInt(); /* OpenHAB hue (0-360°) */
|
|
|
|
|
int green = value.substring(sep1 + 1, sep2).toInt(); /* OpenHAB saturation (0-100%) */
|
|
|
|
|
int blue = value.substring(sep2 + 1, value.length()).toInt(); /* brightness (0-100%) */
|
|
|
|
|
|
|
|
|
|
uint8_t r = (red * 255) / 250;
|
|
|
|
|
uint8_t g = (green *255) / 250;
|
|
|
|
|
uint8_t b = (blue *255) / 250;
|
|
|
|
|
uint32_t c = strip.Color(r,g,b);
|
|
|
|
|
strip.fill(c);
|
|
|
|
|
strip.show();
|
|
|
|
|
ledStripNode.setProperty(NODE_AMBIENT).send(String(r) + "," + String(g) + "," + String(b));
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
mSomethingReceived = true; // Stop animation
|
|
|
|
|
|
|
|
|
|
int sep1 = value.indexOf(',');
|
|
|
|
|
int sep2 = value.indexOf(',', sep1 + 1);
|
|
|
|
|
if ((sep1 > 0) && (sep2 > 0)) {
|
|
|
|
|
int red = value.substring(0,sep1).toInt();
|
|
|
|
|
int green = value.substring(sep1 + 1, sep2).toInt();
|
|
|
|
|
int blue = value.substring(sep2 + 1, value.length()).toInt();
|
|
|
|
|
|
|
|
|
|
uint8_t r = (red * 255) / 250;
|
|
|
|
|
uint8_t g = (green *255) / 250;
|
|
|
|
|
uint8_t b = (blue *255) / 250;
|
|
|
|
|
uint32_t c = strip.Color(r,g,b);
|
|
|
|
|
strip.fill(c);
|
|
|
|
|
strip.show();
|
|
|
|
|
ledStripNode.setProperty(NODE_AMBIENT).send(value);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
@ -400,7 +296,7 @@ bool ledHandler(const HomieRange& range, const String& value) {
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void setup()
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
SPIFFS.begin();
|
|
|
|
|
Serial.begin(115200);
|
|
|
|
|
Serial.setTimeout(2000);
|
|
|
|
@ -416,19 +312,13 @@ void setup()
|
|
|
|
|
Homie.onEvent(onHomieEvent);
|
|
|
|
|
i2cEnable.setDefaultValue(false);
|
|
|
|
|
rgbTemp.setDefaultValue(false);
|
|
|
|
|
rgbDim.setDefaultValue(100).setValidator([] (long candidate) {
|
|
|
|
|
return (candidate > 1) && (candidate <= 200);
|
|
|
|
|
});
|
|
|
|
|
deepsleep.setDefaultValue(0).setValidator([] (long candidate) {
|
|
|
|
|
return ((candidate >= 0) && (candidate < 4294)); /* between 0 (deactivated) and 71 minutes */
|
|
|
|
|
});
|
|
|
|
|
memset(serialRxBuf, 0, SERIAL_RCEVBUF_MAX);
|
|
|
|
|
memset(serialRxBuf, 0, 80);
|
|
|
|
|
|
|
|
|
|
pmSerial.begin(PM1006_BIT_RATE);
|
|
|
|
|
Homie.setup();
|
|
|
|
|
|
|
|
|
|
particle.advertise(NODE_PARTICLE).setName("Particle").setDatatype(NUMBER_TYPE).setUnit("micro gram per quibik");
|
|
|
|
|
temperaturNode.advertise(NODE_TEMPERATUR).setName("Degrees")
|
|
|
|
|
temperatureNode.advertise(NODE_TEMPERATUR).setName("Degrees")
|
|
|
|
|
.setDatatype("float")
|
|
|
|
|
.setUnit("ºC");
|
|
|
|
|
pressureNode.advertise(NODE_PRESSURE).setName("Pressure")
|
|
|
|
@ -437,70 +327,47 @@ void setup()
|
|
|
|
|
altitudeNode.advertise(NODE_ALTITUDE).setName("Altitude")
|
|
|
|
|
.setDatatype("float")
|
|
|
|
|
.setUnit("m");
|
|
|
|
|
#ifdef BME680
|
|
|
|
|
gasNode.advertise(NODE_GAS).setName("Gas")
|
|
|
|
|
.setDatatype("float")
|
|
|
|
|
.setUnit(" KOhms");
|
|
|
|
|
humidityNode.advertise(NODE_HUMIDITY).setName("Humidity")
|
|
|
|
|
.setDatatype("float")
|
|
|
|
|
.setUnit("%");
|
|
|
|
|
#endif
|
|
|
|
|
ledStripNode.advertise(NODE_AMBIENT).setName("All Leds")
|
|
|
|
|
.setDatatype("color").setFormat("rgb")
|
|
|
|
|
.settable(ledHandler);
|
|
|
|
|
buttonNode.advertise(NODE_BUTTON).setName("Button pressed")
|
|
|
|
|
.setDatatype("integer");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
strip.begin();
|
|
|
|
|
/* activate I2C for BOSCH sensor */
|
|
|
|
|
Wire.begin(SENSOR_I2C_SDI, SENSOR_I2C_SCK);
|
|
|
|
|
|
|
|
|
|
mConfigured = Homie.isConfigured();
|
|
|
|
|
digitalWrite(WITTY_RGB_G, HIGH);
|
|
|
|
|
if (mConfigured)
|
|
|
|
|
{
|
|
|
|
|
if (i2cEnable.get()) {
|
|
|
|
|
#ifdef BME680
|
|
|
|
|
printf("Wait 1 second...\r\n");
|
|
|
|
|
delay(1000);
|
|
|
|
|
#endif
|
|
|
|
|
/* activate I2C for BOSCH sensor */
|
|
|
|
|
Wire.begin(SENSOR_I2C_SDI, SENSOR_I2C_SCK);
|
|
|
|
|
printf("Wait 50 milliseconds...\r\n");
|
|
|
|
|
delay(50);
|
|
|
|
|
/* Extracted from library's example */
|
|
|
|
|
mFailedI2Cinitialization = !bmx.begin();
|
|
|
|
|
if (!mFailedI2Cinitialization) {
|
|
|
|
|
strip.fill(strip.Color(0,PERCENT2FACTOR(64, rgbDim),0));
|
|
|
|
|
strip.show();
|
|
|
|
|
#ifdef BME680
|
|
|
|
|
bmx.setTemperatureOversampling(BME680_OS_8X);
|
|
|
|
|
bmx.setHumidityOversampling(BME680_OS_2X);
|
|
|
|
|
bmx.setPressureOversampling(BME680_OS_4X);
|
|
|
|
|
bmx.setIIRFilterSize(BME680_FILTER_SIZE_3);
|
|
|
|
|
bmx.setGasHeater(320, 150); // 320*C for 150 ms
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef BMP280
|
|
|
|
|
/* Default settings from datasheet. */
|
|
|
|
|
bmx.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
|
|
|
|
|
Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
|
|
|
|
|
Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
|
|
|
|
|
Adafruit_BMP280::FILTER_X16, /* Filtering. */
|
|
|
|
|
Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
|
|
|
|
|
#endif
|
|
|
|
|
printf("Sensor found on I2C bus\r\n");
|
|
|
|
|
} else {
|
|
|
|
|
printf("Failed to initialize I2C bus\r\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Nothing when sleeping */
|
|
|
|
|
if (deepsleep.get() <= 0) {
|
|
|
|
|
strip.fill(strip.Color(0,0,0));
|
|
|
|
|
for (int i=0;i < (PIXEL_COUNT / 2); i++) {
|
|
|
|
|
strip.setPixelColor(0, strip.Color(0,0,128 * rgbDim.get()));
|
|
|
|
|
}
|
|
|
|
|
strip.fill(strip.Color(0,128,0));
|
|
|
|
|
strip.show();
|
|
|
|
|
/* Extracted from library's example */
|
|
|
|
|
mFailedI2Cinitialization = !bme.begin();
|
|
|
|
|
if (!mFailedI2Cinitialization) {
|
|
|
|
|
bme.setTemperatureOversampling(BME680_OS_8X);
|
|
|
|
|
bme.setHumidityOversampling(BME680_OS_2X);
|
|
|
|
|
bme.setPressureOversampling(BME680_OS_4X);
|
|
|
|
|
bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
|
|
|
|
|
bme.setGasHeater(320, 150); // 320*C for 150 ms
|
|
|
|
|
} else {
|
|
|
|
|
printf("Faild to initialize I2C bus\r\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
strip.fill(strip.Color(0,0,0));
|
|
|
|
|
for (int i=0;i < (PIXEL_COUNT / 2); i++) {
|
|
|
|
|
strip.setPixelColor(0, strip.Color(0,0,128));
|
|
|
|
|
}
|
|
|
|
|
strip.show();
|
|
|
|
|
digitalWrite(WITTY_RGB_B, HIGH);
|
|
|
|
|
} else {
|
|
|
|
|
digitalWrite(WITTY_RGB_R, HIGH);
|
|
|
|
|
strip.fill(strip.Color(128,0,0));
|
|
|
|
|
for (int i=0;i < (PIXEL_COUNT / 2); i++) {
|
|
|
|
|
strip.setPixelColor(0, strip.Color(0,0,128));
|
|
|
|
@ -514,36 +381,19 @@ void loop()
|
|
|
|
|
Homie.loop();
|
|
|
|
|
/* use the pin, receiving the soft serial additionally as button */
|
|
|
|
|
if (digitalRead(GPIO_BUTTON) == LOW) {
|
|
|
|
|
if ((millis() - mLastButtonAction) > BUTTON_CHECK_INTERVALL) {
|
|
|
|
|
mButtonPressed++;
|
|
|
|
|
}
|
|
|
|
|
if (mButtonPressed > BUTTON_MIN_ACTION_CYCLE) {
|
|
|
|
|
digitalWrite(WITTY_RGB_R, HIGH);
|
|
|
|
|
digitalWrite(WITTY_RGB_B, LOW);
|
|
|
|
|
strip.fill(strip.Color(0,0,0));
|
|
|
|
|
strip.setPixelColor(0, strip.Color((mButtonPressed % 100),0,0));
|
|
|
|
|
strip.setPixelColor(1, strip.Color((mButtonPressed / 100),0,0));
|
|
|
|
|
strip.setPixelColor(2, strip.Color((mButtonPressed / 100),0,0));
|
|
|
|
|
strip.show();
|
|
|
|
|
}
|
|
|
|
|
mButtonPressed++;
|
|
|
|
|
} else {
|
|
|
|
|
mButtonPressed=0U;
|
|
|
|
|
digitalWrite(WITTY_RGB_R, LOW);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mButtonPressed > BUTTON_MAX_CYCLE) {
|
|
|
|
|
if (mButtonPressed > 10000U) {
|
|
|
|
|
mButtonPressed=0U;
|
|
|
|
|
if (SPIFFS.exists("/homie/config.json")) {
|
|
|
|
|
strip.fill(strip.Color(0,PERCENT2FACTOR(127, rgbDim),0));
|
|
|
|
|
strip.show();
|
|
|
|
|
printf("Resetting config\r\n");
|
|
|
|
|
SPIFFS.remove("/homie/config.json");
|
|
|
|
|
SPIFFS.end();
|
|
|
|
|
delay(50);
|
|
|
|
|
Homie.reboot();
|
|
|
|
|
} else {
|
|
|
|
|
printf("No config present\r\n");
|
|
|
|
|
strip.fill(strip.Color(0,0,128));
|
|
|
|
|
strip.show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -564,5 +414,4 @@ void log(int level, String message, int statusCode)
|
|
|
|
|
Homie.getMqttClient().publish(logTopic, 2, false, buffer.c_str());
|
|
|
|
|
delete logTopic;
|
|
|
|
|
}
|
|
|
|
|
Homie.getLogger() << (level) << "@" << (statusCode) << " " << (message) << endl;
|
|
|
|
|
}
|
|
|
|
|