Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
5667279cf1 | |||
2941bb2d5d | |||
a0044ad0ac | |||
fc5c0a1d6e | |||
b28719b7a5 | |||
87a3b6e409 | |||
dd62a10ed5 | |||
225d079d48 | |||
7ea43fdc7d | |||
67ad912a71 | |||
5602a456a7 | |||
28e5aa4e69 | |||
e55f3b6e74 | |||
f17afd932e | |||
7a6cc8caef | |||
94ead3751b | |||
ab600e014a | |||
60d02d88b5 | |||
05594441b8 | |||
9e461a9036 | |||
ce6bee19af | |||
0354c6e634 | |||
ac9efccd94 | |||
4df0fc5d5c | |||
4c180fedbd | |||
8b73ac77f9 | |||
cbb444f1bc | |||
b2762a3b6c | |||
512420f5a2 | |||
e94a625072 | |||
7bbe81ad1d | |||
335fad3f0d |
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.vscode
|
||||||
|
*.DS_Store
|
@ -34,7 +34,7 @@ If you have any questions or problems, check out [our forum](https://forum.airgr
|
|||||||
- [Sensirion Gas Index Algorithm](https://github.com/Sensirion/arduino-gas-index-algorithm)
|
- [Sensirion Gas Index Algorithm](https://github.com/Sensirion/arduino-gas-index-algorithm)
|
||||||
- [Sensirion Core](https://github.com/Sensirion/arduino-core/)
|
- [Sensirion Core](https://github.com/Sensirion/arduino-core/)
|
||||||
- [Sensirion I2C SGP41](https://github.com/Sensirion/arduino-i2c-sgp41)
|
- [Sensirion I2C SGP41](https://github.com/Sensirion/arduino-i2c-sgp41)
|
||||||
- [Sensirion I2C SHT4x](https://github.com/Sensirion/arduino-i2c-sht4x)
|
- [Sensirion I2C SHT](https://github.com/Sensirion/arduino-sht)
|
||||||
- [PMS](https://github.com/fu-hsi/pms)
|
- [PMS](https://github.com/fu-hsi/pms)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
BIN
examples/.DS_Store
vendored
@ -1,7 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
This is the code for the AirGradient DIY BASIC Air Quality Monitor with an D1 ESP8266 Microcontroller.
|
This is the code for the AirGradient DIY BASIC Air Quality Monitor with an D1
|
||||||
|
ESP8266 Microcontroller.
|
||||||
|
|
||||||
It is an air quality monitor for PM2.5, CO2, Temperature and Humidity with a small display and can send data over Wifi.
|
It is an air quality monitor for PM2.5, CO2, Temperature and Humidity with a
|
||||||
|
small display and can send data over Wifi.
|
||||||
|
|
||||||
Open source air quality monitors and kits are available:
|
Open source air quality monitors and kits are available:
|
||||||
Indoor Monitor: https://www.airgradient.com/indoor/
|
Indoor Monitor: https://www.airgradient.com/indoor/
|
||||||
@ -15,11 +17,13 @@ Following libraries need to be installed:
|
|||||||
"Arduino_JSON" by Arduino version 0.2.0
|
"Arduino_JSON" by Arduino version 0.2.0
|
||||||
"U8g2" by oliver version 2.34.22
|
"U8g2" by oliver version 2.34.22
|
||||||
|
|
||||||
Please make sure you have esp8266 board manager installed. Tested with version 3.1.2.
|
Please make sure you have esp8266 board manager installed. Tested with
|
||||||
|
version 3.1.2.
|
||||||
|
|
||||||
Set board to "LOLIN(WEMOS) D1 R2 & mini"
|
Set board to "LOLIN(WEMOS) D1 R2 & mini"
|
||||||
|
|
||||||
Configuration parameters, e.g. Celsius / Fahrenheit or PM unit (US AQI vs ug/m3) can be set through the AirGradient dashboard.
|
Configuration parameters, e.g. Celsius / Fahrenheit or PM unit (US AQI vs ug/m3)
|
||||||
|
can be set through the AirGradient dashboard.
|
||||||
|
|
||||||
If you have any questions please visit our forum at
|
If you have any questions please visit our forum at
|
||||||
https://forum.airgradient.com/
|
https://forum.airgradient.com/
|
||||||
@ -32,156 +36,425 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#include <Arduino_JSON.h>
|
#include <Arduino_JSON.h>
|
||||||
#include <ESP8266HTTPClient.h>
|
#include <ESP8266HTTPClient.h>
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <U8g2lib.h>
|
|
||||||
#include <WiFiClient.h>
|
#include <WiFiClient.h>
|
||||||
#include <WiFiManager.h>
|
#include <WiFiManager.h>
|
||||||
|
|
||||||
typedef struct {
|
#define WIFI_CONNECT_COUNTDOWN_MAX 180 /** sec */
|
||||||
bool inF; /** Temperature unit */
|
#define WIFI_CONNECT_RETRY_MS 10000 /** ms */
|
||||||
bool inUSAQI; /** PMS standard */
|
#define LED_BAR_COUNT_INIT_VALUE (-1) /** */
|
||||||
uint8_t ledBarMode; /** @ref UseLedBar*/
|
#define LED_BAR_ANIMATION_PERIOD 100 /** ms */
|
||||||
char model[16]; /** Model string value, Just define, don't know how much
|
#define DISP_UPDATE_INTERVAL 5000 /** ms */
|
||||||
memory usage */
|
#define SERVER_CONFIG_UPDATE_INTERVAL 30000 /** ms */
|
||||||
char mqttBroker[128]; /** Mqtt broker link */
|
#define SERVER_SYNC_INTERVAL 60000 /** ms */
|
||||||
uint32_t _check; /** Checksum configuration data */
|
#define SENSOR_CO2_CALIB_COUNTDOWN_MAX 5 /** sec */
|
||||||
} ServerConfig_t;
|
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
||||||
static ServerConfig_t serverConfig;
|
#define SENSOR_CO2_UPDATE_INTERVAL 5000 /** ms */
|
||||||
|
#define SENSOR_PM_UPDATE_INTERVAL 5000 /** ms */
|
||||||
|
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 5000 /** ms */
|
||||||
|
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
|
||||||
|
#define WIFI_HOTSPOT_PASSWORD_DEFAULT \
|
||||||
|
"cleanair" /** default WiFi AP password \
|
||||||
|
*/
|
||||||
|
|
||||||
AirGradient ag = AirGradient(BOARD_DIY_BASIC_KIT);
|
/**
|
||||||
|
* @brief Use use LED bar state
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
UseLedBarOff, /** Don't use LED bar */
|
||||||
|
UseLedBarPM, /** Use LED bar for PMS */
|
||||||
|
UseLedBarCO2, /** Use LED bar for CO2 */
|
||||||
|
} UseLedBar;
|
||||||
|
|
||||||
// CONFIGURATION START
|
/**
|
||||||
|
* @brief Schedule handle with timing period
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class AgSchedule {
|
||||||
|
public:
|
||||||
|
AgSchedule(int period, void (*handler)(void))
|
||||||
|
: period(period), handler(handler) {}
|
||||||
|
void run(void) {
|
||||||
|
uint32_t ms = (uint32_t)(millis() - count);
|
||||||
|
if (ms >= period) {
|
||||||
|
/** Call handler */
|
||||||
|
handler();
|
||||||
|
|
||||||
// set to the endpoint you would like to use
|
Serial.printf("[AgSchedule] handle 0x%08x, period: %d(ms)\r\n",
|
||||||
String APIROOT = "http://hw.airgradient.com/";
|
(unsigned int)handler, period);
|
||||||
|
|
||||||
String wifiApPass = "cleanair";
|
/** Update period time */
|
||||||
|
count = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set to true if you want to connect to wifi. You have 60 seconds to connect.
|
private:
|
||||||
// Then it will go into an offline mode.
|
void (*handler)(void);
|
||||||
boolean connectWIFI = true;
|
int period;
|
||||||
|
int count;
|
||||||
|
};
|
||||||
|
|
||||||
// CONFIGURATION END
|
/**
|
||||||
|
* @brief AirGradient server configuration and sync data
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class AgServer {
|
||||||
|
public:
|
||||||
|
void begin(void) {
|
||||||
|
inF = false;
|
||||||
|
inUSAQI = false;
|
||||||
|
configFailed = false;
|
||||||
|
serverFailed = false;
|
||||||
|
memset(models, 0, sizeof(models));
|
||||||
|
memset(mqttBroker, 0, sizeof(mqttBroker));
|
||||||
|
}
|
||||||
|
|
||||||
unsigned long currentMillis = 0;
|
/**
|
||||||
|
* @brief Get server configuration
|
||||||
|
*
|
||||||
|
* @param id Device ID
|
||||||
|
* @return true Success
|
||||||
|
* @return false Failure
|
||||||
|
*/
|
||||||
|
bool pollServerConfig(String id) {
|
||||||
|
String uri =
|
||||||
|
"http://hw.airgradient.com/sensors/airgradient:" + id + "/one/config";
|
||||||
|
|
||||||
const int oledInterval = 5000;
|
/** Init http client */
|
||||||
unsigned long previousOled = 0;
|
WiFiClient wifiClient;
|
||||||
bool co2CalibrationRequest = false;
|
HTTPClient client;
|
||||||
uint32_t serverConfigLoadTime = 0;
|
if (client.begin(wifiClient, uri) == false) {
|
||||||
String HOSTPOT = "";
|
configFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const int sendToServerInterval = 60000;
|
/** Get */
|
||||||
const int pollServerConfigInterval = 30000;
|
int retCode = client.GET();
|
||||||
const int co2CalibCountdown = 5; /** Seconds */
|
if (retCode != 200) {
|
||||||
unsigned long previoussendToServer = 0;
|
client.end();
|
||||||
|
configFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const int co2Interval = 5000;
|
/** clear failed */
|
||||||
unsigned long previousCo2 = 0;
|
configFailed = false;
|
||||||
int Co2 = 0;
|
|
||||||
|
|
||||||
const int pm25Interval = 5000;
|
/** Get response string */
|
||||||
unsigned long previousPm25 = 0;
|
String respContent = client.getString();
|
||||||
int pm25 = 0;
|
client.end();
|
||||||
|
Serial.println("Get server config: " + respContent);
|
||||||
|
|
||||||
const int tempHumInterval = 2500;
|
/** Parse JSON */
|
||||||
unsigned long previousTempHum = 0;
|
JSONVar root = JSON.parse(respContent);
|
||||||
float temp = 0;
|
if (JSON.typeof(root) == "undefined") {
|
||||||
int hum = 0;
|
/** JSON invalid */
|
||||||
long val;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void failedHandler(String msg);
|
/** Get "country" */
|
||||||
void boardInit(void);
|
if (JSON.typeof_(root["country"]) == "string") {
|
||||||
void getServerConfig(void);
|
String country = root["country"];
|
||||||
void co2Calibration(void);
|
if (country == "US") {
|
||||||
|
inF = true;
|
||||||
|
} else {
|
||||||
|
inF = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get "pmStandard" */
|
||||||
|
if (JSON.typeof_(root["pmStandard"]) == "string") {
|
||||||
|
String standard = root["pmStandard"];
|
||||||
|
if (standard == "ugm3") {
|
||||||
|
inUSAQI = false;
|
||||||
|
} else {
|
||||||
|
inUSAQI = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get "co2CalibrationRequested" */
|
||||||
|
if (JSON.typeof_(root["co2CalibrationRequested"]) == "boolean") {
|
||||||
|
co2Calib = root["co2CalibrationRequested"];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get "ledBarMode" */
|
||||||
|
if (JSON.typeof_(root["ledBarMode"]) == "string") {
|
||||||
|
String mode = root["ledBarMode"];
|
||||||
|
if (mode == "co2") {
|
||||||
|
ledBarMode = UseLedBarCO2;
|
||||||
|
} else if (mode == "pm") {
|
||||||
|
ledBarMode = UseLedBarPM;
|
||||||
|
} else if (mode == "off") {
|
||||||
|
ledBarMode = UseLedBarOff;
|
||||||
|
} else {
|
||||||
|
ledBarMode = UseLedBarOff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get model */
|
||||||
|
if (JSON.typeof_(root["model"]) == "string") {
|
||||||
|
String model = root["model"];
|
||||||
|
if (model.length()) {
|
||||||
|
int len =
|
||||||
|
model.length() < sizeof(models) ? model.length() : sizeof(models);
|
||||||
|
memset(models, 0, sizeof(models));
|
||||||
|
memcpy(models, model.c_str(), len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get "mqttBrokerUrl" */
|
||||||
|
if (JSON.typeof_(root["mqttBrokerUrl"]) == "string") {
|
||||||
|
String mqtt = root["mqttBrokerUrl"];
|
||||||
|
if (mqtt.length()) {
|
||||||
|
int len = mqtt.length() < sizeof(mqttBroker) ? mqtt.length()
|
||||||
|
: sizeof(mqttBroker);
|
||||||
|
memset(mqttBroker, 0, sizeof(mqttBroker));
|
||||||
|
memcpy(mqttBroker, mqtt.c_str(), len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get 'abcDays' */
|
||||||
|
if (JSON.typeof_(root["abcDays"]) == "number") {
|
||||||
|
co2AbcCalib = root["abcDays"];
|
||||||
|
} else {
|
||||||
|
co2AbcCalib = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Show configuration */
|
||||||
|
showServerConfig();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool postToServer(String id, String payload) {
|
||||||
|
/**
|
||||||
|
* @brief Only post data if WiFi is connected
|
||||||
|
*/
|
||||||
|
if (WiFi.isConnected() == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("Post payload: %s\r\n", payload.c_str());
|
||||||
|
|
||||||
|
String uri =
|
||||||
|
"http://hw.airgradient.com/sensors/airgradient:" + id + "/measures";
|
||||||
|
|
||||||
|
WiFiClient wifiClient;
|
||||||
|
HTTPClient client;
|
||||||
|
if (client.begin(wifiClient, uri.c_str()) == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
client.addHeader("content-type", "application/json");
|
||||||
|
int retCode = client.POST(payload);
|
||||||
|
client.end();
|
||||||
|
|
||||||
|
if ((retCode == 200) || (retCode == 429)) {
|
||||||
|
serverFailed = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
serverFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get temperature configuration unit
|
||||||
|
*
|
||||||
|
* @return true F unit
|
||||||
|
* @return false C Unit
|
||||||
|
*/
|
||||||
|
bool isTemperatureUnitF(void) { return inF; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get PMS standard unit
|
||||||
|
*
|
||||||
|
* @return true USAQI
|
||||||
|
* @return false ugm3
|
||||||
|
*/
|
||||||
|
bool isPMSinUSAQI(void) { return inUSAQI; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get status of get server coniguration is failed
|
||||||
|
*
|
||||||
|
* @return true Failed
|
||||||
|
* @return false Success
|
||||||
|
*/
|
||||||
|
bool isConfigFailed(void) { return configFailed; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get status of post server configuration is failed
|
||||||
|
*
|
||||||
|
* @return true Failed
|
||||||
|
* @return false Success
|
||||||
|
*/
|
||||||
|
bool isServerFailed(void) { return serverFailed; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get request calibration CO2
|
||||||
|
*
|
||||||
|
* @return true Requested. If result = true, it's clear after function call
|
||||||
|
* @return false Not-requested
|
||||||
|
*/
|
||||||
|
bool isCo2Calib(void) {
|
||||||
|
bool ret = co2Calib;
|
||||||
|
if (ret) {
|
||||||
|
co2Calib = false;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the Co2 auto calib period
|
||||||
|
*
|
||||||
|
* @return int days, -1 if invalid.
|
||||||
|
*/
|
||||||
|
int getCo2Abccalib(void) { return co2AbcCalib; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get device configuration model name
|
||||||
|
*
|
||||||
|
* @return String Model name, empty string if server failed
|
||||||
|
*/
|
||||||
|
String getModelName(void) { return String(models); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get mqttBroker url
|
||||||
|
*
|
||||||
|
* @return String Broker url, empty if server failed
|
||||||
|
*/
|
||||||
|
String getMqttBroker(void) { return String(mqttBroker); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Show server configuration parameter
|
||||||
|
*/
|
||||||
|
void showServerConfig(void) {
|
||||||
|
Serial.println("Server configuration: ");
|
||||||
|
Serial.printf(" inF: %s\r\n", inF ? "true" : "false");
|
||||||
|
Serial.printf(" inUSAQI: %s\r\n", inUSAQI ? "true" : "false");
|
||||||
|
Serial.printf(" useRGBLedBar: %d\r\n", (int)ledBarMode);
|
||||||
|
Serial.printf(" Model: %s\r\n", models);
|
||||||
|
Serial.printf(" Mqtt Broker: %s\r\n", mqttBroker);
|
||||||
|
Serial.printf(" S8 calib period: %d\r\n", co2AbcCalib);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get server config led bar mode
|
||||||
|
*
|
||||||
|
* @return UseLedBar
|
||||||
|
*/
|
||||||
|
UseLedBar getLedBarMode(void) { return ledBarMode; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool inF; /** Temperature unit, true: F, false: C */
|
||||||
|
bool inUSAQI; /** PMS unit, true: USAQI, false: ugm3 */
|
||||||
|
bool configFailed; /** Flag indicate get server configuration failed */
|
||||||
|
bool serverFailed; /** Flag indicate post data to server failed */
|
||||||
|
bool co2Calib; /** Is co2Ppmcalibration requset */
|
||||||
|
int co2AbcCalib = -1; /** update auto calibration number of day */
|
||||||
|
UseLedBar ledBarMode = UseLedBarCO2; /** */
|
||||||
|
char models[20]; /** */
|
||||||
|
char mqttBroker[256]; /** */
|
||||||
|
};
|
||||||
|
AgServer agServer;
|
||||||
|
|
||||||
|
/** Create airgradient instance for 'DIY_BASIC' board */
|
||||||
|
AirGradient ag = AirGradient(DIY_BASIC);
|
||||||
|
|
||||||
|
static int co2Ppm = -1;
|
||||||
|
static int pm25 = -1;
|
||||||
|
static float temp = -1;
|
||||||
|
static int hum = -1;
|
||||||
|
static long val;
|
||||||
|
static String wifiSSID = "";
|
||||||
|
static bool wifiHasConfig = false; /** */
|
||||||
|
|
||||||
|
static void boardInit(void);
|
||||||
|
static void failedHandler(String msg);
|
||||||
|
static void co2Calibration(void);
|
||||||
|
static void serverConfigPoll(void);
|
||||||
|
static void co2Poll(void);
|
||||||
|
static void pmPoll(void);
|
||||||
|
static void tempHumPoll(void);
|
||||||
|
static void sendDataToServer(void);
|
||||||
|
static void dispHandler(void);
|
||||||
|
static String getDevId(void);
|
||||||
|
static void updateWiFiConnect(void);
|
||||||
|
|
||||||
|
AgSchedule configSchedule(SERVER_CONFIG_UPDATE_INTERVAL, serverConfigPoll);
|
||||||
|
AgSchedule serverSchedule(SERVER_SYNC_INTERVAL, sendDataToServer);
|
||||||
|
AgSchedule dispSchedule(DISP_UPDATE_INTERVAL, dispHandler);
|
||||||
|
AgSchedule co2Schedule(SENSOR_CO2_UPDATE_INTERVAL, co2Poll);
|
||||||
|
AgSchedule pmsSchedule(SENSOR_PM_UPDATE_INTERVAL, pmPoll);
|
||||||
|
AgSchedule tempHumSchedule(SENSOR_TEMP_HUM_UPDATE_INTERVAL, tempHumPoll);
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
|
||||||
/** Init I2C */
|
/** Init I2C */
|
||||||
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
|
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
/** Board init */
|
/** Board init */
|
||||||
boardInit();
|
boardInit();
|
||||||
|
|
||||||
|
/** Init AirGradient server */
|
||||||
|
agServer.begin();
|
||||||
|
|
||||||
/** Show boot display */
|
/** Show boot display */
|
||||||
displayShowText("Basic v4", "Lib:" + ag.getVersion(), "");
|
displayShowText("DIY basic", "Lib:" + ag.getVersion(), "");
|
||||||
delay(2000);
|
delay(2000);
|
||||||
|
|
||||||
if (connectWIFI) {
|
/** WiFi connect */
|
||||||
connectToWifi();
|
connectToWifi();
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
wifiHasConfig = true;
|
||||||
|
sendPing();
|
||||||
|
|
||||||
|
agServer.pollServerConfig(getDevId());
|
||||||
|
if (agServer.isCo2Calib()) {
|
||||||
|
co2Calibration();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Show display */
|
/** Show serial number display */
|
||||||
displayShowText("Warm Up", "Serial#", String(ESP.getChipId(), HEX));
|
ag.display.clear();
|
||||||
delay(10000);
|
ag.display.setCursor(1, 1);
|
||||||
|
ag.display.setText("Warm Up");
|
||||||
|
ag.display.setCursor(1, 15);
|
||||||
|
ag.display.setText("Serial#");
|
||||||
|
ag.display.setCursor(1, 29);
|
||||||
|
String id = getNormalizedMac();
|
||||||
|
Serial.println("Device id: " + id);
|
||||||
|
String id1 = id.substring(0, 9);
|
||||||
|
String id2 = id.substring(9, 12);
|
||||||
|
ag.display.setText("\'" + id1);
|
||||||
|
ag.display.setCursor(1, 40);
|
||||||
|
ag.display.setText(id2 + "\'");
|
||||||
|
ag.display.show();
|
||||||
|
|
||||||
getServerConfig();
|
delay(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
currentMillis = millis();
|
configSchedule.run();
|
||||||
updateOLED();
|
serverSchedule.run();
|
||||||
updateCo2();
|
dispSchedule.run();
|
||||||
updatePm25();
|
co2Schedule.run();
|
||||||
updateTempHum();
|
pmsSchedule.run();
|
||||||
sendToServer();
|
tempHumSchedule.run();
|
||||||
getServerConfig();
|
|
||||||
|
updateWiFiConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateCo2() {
|
static void sendPing() {
|
||||||
if (currentMillis - previousCo2 >= co2Interval) {
|
JSONVar root;
|
||||||
previousCo2 += co2Interval;
|
root["wifi"] = WiFi.RSSI();
|
||||||
Co2 = ag.s8.getCo2();
|
root["boot"] = 0;
|
||||||
Serial.println(String(Co2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updatePm25() {
|
// delay(1500);
|
||||||
if (currentMillis - previousPm25 >= pm25Interval) {
|
if (agServer.postToServer(getDevId(), JSON.stringify(root))) {
|
||||||
previousPm25 += pm25Interval;
|
// Ping Server succses
|
||||||
if (ag.pms5003.readData()) {
|
|
||||||
pm25 = ag.pms5003.getPm25Ae();
|
|
||||||
Serial.printf("PM25: %d\r\n", pm25);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateTempHum() {
|
|
||||||
if (currentMillis - previousTempHum >= tempHumInterval) {
|
|
||||||
previousTempHum += tempHumInterval;
|
|
||||||
|
|
||||||
/** Get temperature and humidity */
|
|
||||||
temp = ag.sht.getTemperature();
|
|
||||||
hum = ag.sht.getRelativeHumidity();
|
|
||||||
|
|
||||||
/** Print debug message */
|
|
||||||
Serial.printf("SHT Humidity: %d%, Temperature: %0.2f\r\n", hum, temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateOLED() {
|
|
||||||
if (currentMillis - previousOled >= oledInterval) {
|
|
||||||
previousOled += oledInterval;
|
|
||||||
|
|
||||||
String ln1;
|
|
||||||
String ln2;
|
|
||||||
String ln3;
|
|
||||||
|
|
||||||
if (serverConfig.inUSAQI) {
|
|
||||||
ln1 = "AQI:" + String(ag.pms5003.convertPm25ToUsAqi(pm25));
|
|
||||||
} else {
|
} else {
|
||||||
ln1 = "PM :" + String(pm25) + " ug";
|
// Ping server failed
|
||||||
}
|
|
||||||
ln2 = "CO2:" + String(Co2);
|
|
||||||
|
|
||||||
if (serverConfig.inF) {
|
|
||||||
ln3 =
|
|
||||||
String((temp * 9 / 5) + 32).substring(0, 4) + " " + String(hum) + "%";
|
|
||||||
} else {
|
|
||||||
ln3 = String(temp).substring(0, 4) + " " + String(hum) + "%";
|
|
||||||
}
|
|
||||||
displayShowText(ln1, ln2, ln3);
|
|
||||||
}
|
}
|
||||||
|
// delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void displayShowText(String ln1, String ln2, String ln3) {
|
void displayShowText(String ln1, String ln2, String ln3) {
|
||||||
@ -198,58 +471,24 @@ void displayShowText(String ln1, String ln2, String ln3) {
|
|||||||
ag.display.show();
|
ag.display.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendToServer() {
|
|
||||||
if (currentMillis - previoussendToServer >= sendToServerInterval) {
|
|
||||||
previoussendToServer += sendToServerInterval;
|
|
||||||
|
|
||||||
String payload = "{\"wifi\":" + String(WiFi.RSSI()) +
|
|
||||||
(Co2 < 0 ? "" : ", \"rco2\":" + String(Co2)) +
|
|
||||||
(pm25 < 0 ? "" : ", \"pm02\":" + String(pm25)) +
|
|
||||||
", \"atmp\":" + String(temp) +
|
|
||||||
(hum < 0 ? "" : ", \"rhum\":" + String(hum)) + "}";
|
|
||||||
|
|
||||||
if (WiFi.status() == WL_CONNECTED) {
|
|
||||||
Serial.println(payload);
|
|
||||||
String POSTURL = APIROOT +
|
|
||||||
"sensors/airgradient:" + String(ESP.getChipId(), HEX) +
|
|
||||||
"/measures";
|
|
||||||
Serial.println(POSTURL);
|
|
||||||
WiFiClient client;
|
|
||||||
HTTPClient http;
|
|
||||||
http.begin(client, POSTURL);
|
|
||||||
http.addHeader("content-type", "application/json");
|
|
||||||
int httpCode = http.POST(payload);
|
|
||||||
String response = http.getString();
|
|
||||||
Serial.println(httpCode);
|
|
||||||
Serial.println(response);
|
|
||||||
http.end();
|
|
||||||
} else {
|
|
||||||
Serial.println("WiFi Disconnected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wifi Manager
|
// Wifi Manager
|
||||||
void connectToWifi() {
|
void connectToWifi() {
|
||||||
WiFiManager wifiManager;
|
WiFiManager wifiManager;
|
||||||
// WiFi.disconnect(); //to delete previous saved hotspot
|
wifiSSID = "AG-" + String(ESP.getChipId(), HEX);
|
||||||
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
|
|
||||||
// displayShowText("Connect", "AG-", String(ESP.getChipId(), HEX));
|
|
||||||
delay(2000);
|
|
||||||
// wifiManager.setTimeout(90);
|
|
||||||
wifiManager.setConfigPortalBlocking(false);
|
wifiManager.setConfigPortalBlocking(false);
|
||||||
wifiManager.setConfigPortalTimeout(180);
|
wifiManager.setConfigPortalTimeout(WIFI_CONNECT_COUNTDOWN_MAX);
|
||||||
wifiManager.autoConnect(HOTSPOT.c_str(), wifiApPass.c_str());
|
wifiManager.autoConnect(wifiSSID.c_str(), WIFI_HOTSPOT_PASSWORD_DEFAULT);
|
||||||
|
|
||||||
uint32_t lastTime = millis();
|
uint32_t lastTime = millis();
|
||||||
int count = 179;
|
int count = WIFI_CONNECT_COUNTDOWN_MAX;
|
||||||
displayShowText("180 sec", "SSID:",HOTSPOT);
|
displayShowText(String(WIFI_CONNECT_COUNTDOWN_MAX) + " sec",
|
||||||
|
"SSID:", wifiSSID);
|
||||||
while (wifiManager.getConfigPortalActive()) {
|
while (wifiManager.getConfigPortalActive()) {
|
||||||
wifiManager.process();
|
wifiManager.process();
|
||||||
uint32_t ms = (uint32_t)(millis() - lastTime);
|
uint32_t ms = (uint32_t)(millis() - lastTime);
|
||||||
if (ms >= 1000) {
|
if (ms >= 1000) {
|
||||||
lastTime = millis();
|
lastTime = millis();
|
||||||
displayShowText(String(count) + " sec", "SSID:",HOTSPOT);
|
displayShowText(String(count) + " sec", "SSID:", wifiSSID);
|
||||||
count--;
|
count--;
|
||||||
|
|
||||||
// Timeout
|
// Timeout
|
||||||
@ -261,18 +500,11 @@ void connectToWifi() {
|
|||||||
if (!WiFi.isConnected()) {
|
if (!WiFi.isConnected()) {
|
||||||
displayShowText("Booting", "offline", "mode");
|
displayShowText("Booting", "offline", "mode");
|
||||||
Serial.println("failed to connect and hit timeout");
|
Serial.println("failed to connect and hit timeout");
|
||||||
delay(6000);
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void failedHandler(String msg) {
|
static void boardInit(void) {
|
||||||
while (true) {
|
|
||||||
Serial.println(msg);
|
|
||||||
delay(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void boardInit(void) {
|
|
||||||
/** Init SHT sensor */
|
/** Init SHT sensor */
|
||||||
if (ag.sht.begin(Wire) == false) {
|
if (ag.sht.begin(Wire) == false) {
|
||||||
failedHandler("SHT init failed");
|
failedHandler("SHT init failed");
|
||||||
@ -293,152 +525,18 @@ void boardInit(void) {
|
|||||||
ag.display.setTextColor(1);
|
ag.display.setTextColor(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showConfig(void) {
|
static void failedHandler(String msg) {
|
||||||
Serial.println("Server configuration: ");
|
while (true) {
|
||||||
Serial.printf(" inF: %s\r\n", serverConfig.inF ? "true" : "false");
|
Serial.println(msg);
|
||||||
Serial.printf(" inUSAQI: %s\r\n",
|
delay(1000);
|
||||||
serverConfig.inUSAQI ? "true" : "false");
|
|
||||||
Serial.printf("useRGBLedBar: %d\r\n", (int)serverConfig.ledBarMode);
|
|
||||||
Serial.printf(" Model: %.*s\r\n", sizeof(serverConfig.model),
|
|
||||||
serverConfig.model);
|
|
||||||
Serial.printf(" Mqtt Broker: %.*s\r\n", sizeof(serverConfig.mqttBroker),
|
|
||||||
serverConfig.mqttBroker);
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateServerConfigLoadTime(void) {
|
|
||||||
serverConfigLoadTime = millis();
|
|
||||||
if (serverConfigLoadTime == 0) {
|
|
||||||
serverConfigLoadTime = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void getServerConfig(void) {
|
static void co2Calibration(void) {
|
||||||
/** Only trigger load configuration again after pollServerConfigInterval sec
|
|
||||||
*/
|
|
||||||
if (serverConfigLoadTime) {
|
|
||||||
uint32_t ms = (uint32_t)(millis() - serverConfigLoadTime);
|
|
||||||
if (ms < pollServerConfigInterval) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateServerConfigLoadTime();
|
|
||||||
|
|
||||||
Serial.println("Trigger load server configuration");
|
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
|
||||||
Serial.println(
|
|
||||||
"Ignore get server configuration because WIFI not connected");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// WiFiClient wifiClient;
|
|
||||||
HTTPClient httpClient;
|
|
||||||
|
|
||||||
String getUrl = "http://hw.airgradient.com/sensors/airgradient:" +
|
|
||||||
String(ESP.getChipId(), HEX) + "/one/config";
|
|
||||||
Serial.println("HttpClient get: " + getUrl);
|
|
||||||
WiFiClient client;
|
|
||||||
if (httpClient.begin(client, getUrl) == false) {
|
|
||||||
Serial.println("HttpClient init failed");
|
|
||||||
updateServerConfigLoadTime();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int respCode = httpClient.GET();
|
|
||||||
|
|
||||||
/** get failure */
|
|
||||||
if (respCode != 200) {
|
|
||||||
Serial.printf("HttpClient get failed: %d\r\n", respCode);
|
|
||||||
updateServerConfigLoadTime();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String respContent = httpClient.getString();
|
|
||||||
Serial.println("Server config: " + respContent);
|
|
||||||
|
|
||||||
/** Parse JSON */
|
|
||||||
JSONVar root = JSON.parse(respContent);
|
|
||||||
if (JSON.typeof_(root) == "undefined") {
|
|
||||||
Serial.println("Server configura JSON invalid");
|
|
||||||
updateServerConfigLoadTime();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get "country" */
|
|
||||||
bool inF = serverConfig.inF;
|
|
||||||
if (JSON.typeof_(root["country"]) == "string") {
|
|
||||||
String country = root["country"];
|
|
||||||
if (country == "US") {
|
|
||||||
inF = true;
|
|
||||||
} else {
|
|
||||||
inF = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get "pmStandard" */
|
|
||||||
bool inUSAQI = serverConfig.inUSAQI;
|
|
||||||
if (JSON.typeof_(root["pmStandard"]) == "string") {
|
|
||||||
String standard = root["pmStandard"];
|
|
||||||
if (standard == "ugm3") {
|
|
||||||
inUSAQI = false;
|
|
||||||
} else {
|
|
||||||
inUSAQI = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get CO2 "co2CalibrationRequested" */
|
|
||||||
co2CalibrationRequest = false;
|
|
||||||
if (JSON.typeof_(root["co2CalibrationRequested"]) == "boolean") {
|
|
||||||
co2CalibrationRequest = root["co2CalibrationRequested"];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** get "model" */
|
|
||||||
String model = "";
|
|
||||||
if (JSON.typeof_(root["model"]) == "string") {
|
|
||||||
String _model = root["model"];
|
|
||||||
model = _model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** get "mqttBrokerUrl" */
|
|
||||||
String mqtt = "";
|
|
||||||
if (JSON.typeof_(root["mqttBrokerUrl"]) == "string") {
|
|
||||||
String _mqtt = root["mqttBrokerUrl"];
|
|
||||||
mqtt = _mqtt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inF != serverConfig.inF) {
|
|
||||||
serverConfig.inF = inF;
|
|
||||||
}
|
|
||||||
if (inUSAQI != serverConfig.inUSAQI) {
|
|
||||||
serverConfig.inUSAQI = inUSAQI;
|
|
||||||
}
|
|
||||||
if (model.length()) {
|
|
||||||
if (model != String(serverConfig.model)) {
|
|
||||||
memset(serverConfig.model, 0, sizeof(serverConfig.model));
|
|
||||||
memcpy(serverConfig.model, model.c_str(), model.length());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mqtt.length()) {
|
|
||||||
if (mqtt != String(serverConfig.mqttBroker)) {
|
|
||||||
memset(serverConfig.mqttBroker, 0, sizeof(serverConfig.mqttBroker));
|
|
||||||
memcpy(serverConfig.mqttBroker, mqtt.c_str(), mqtt.length());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Show server configuration */
|
|
||||||
showConfig();
|
|
||||||
|
|
||||||
/** Calibration */
|
|
||||||
if (co2CalibrationRequest) {
|
|
||||||
co2Calibration();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void co2Calibration(void) {
|
|
||||||
/** Count down for co2CalibCountdown secs */
|
/** Count down for co2CalibCountdown secs */
|
||||||
for (int i = 0; i < co2CalibCountdown; i++) {
|
for (int i = 0; i < SENSOR_CO2_CALIB_COUNTDOWN_MAX; i++) {
|
||||||
displayShowText("CO2 calib", "after",
|
displayShowText("CO2 calib", "after",
|
||||||
String(co2CalibCountdown - i) + " sec");
|
String(SENSOR_CO2_CALIB_COUNTDOWN_MAX - i) + " sec");
|
||||||
delay(1000);
|
delay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,9 +550,118 @@ void co2Calibration(void) {
|
|||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
displayShowText("Finish", "after", String(count) + " sec");
|
displayShowText("Finish", "after", String(count) + " sec");
|
||||||
delay(2000);
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
||||||
} else {
|
} else {
|
||||||
displayShowText("Calib", "failure!!!", "");
|
displayShowText("Calib", "failure!!!", "");
|
||||||
delay(2000);
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void serverConfigPoll(void) {
|
||||||
|
if (agServer.pollServerConfig(getDevId())) {
|
||||||
|
if (agServer.isCo2Calib()) {
|
||||||
|
co2Calibration();
|
||||||
|
}
|
||||||
|
if (agServer.getCo2Abccalib() > 0) {
|
||||||
|
if (ag.s8.setAutoCalib(agServer.getCo2Abccalib() * 24) == false) {
|
||||||
|
Serial.println("Set S8 auto calib failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void co2Poll() {
|
||||||
|
co2Ppm = ag.s8.getCo2();
|
||||||
|
Serial.printf("CO2 index: %d\r\n", co2Ppm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmPoll() {
|
||||||
|
if (ag.pms5003.readData()) {
|
||||||
|
pm25 = ag.pms5003.getPm25Ae();
|
||||||
|
Serial.printf("PMS2.5: %d\r\n", pm25);
|
||||||
|
} else {
|
||||||
|
pm25 = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tempHumPoll() {
|
||||||
|
if (ag.sht.measure()) {
|
||||||
|
temp = ag.sht.getTemperature();
|
||||||
|
hum = ag.sht.getRelativeHumidity();
|
||||||
|
Serial.printf("Temperature: %0.2f\r\n", temp);
|
||||||
|
Serial.printf(" Humidity: %d\r\n", hum);
|
||||||
|
} else {
|
||||||
|
Serial.println("Meaure SHT failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sendDataToServer() {
|
||||||
|
JSONVar root;
|
||||||
|
root["wifi"] = WiFi.RSSI();
|
||||||
|
if (co2Ppm >= 0) {
|
||||||
|
root["rco2"] = co2Ppm;
|
||||||
|
}
|
||||||
|
if (pm25 >= 0) {
|
||||||
|
root["pm02"] = pm25;
|
||||||
|
}
|
||||||
|
if (temp >= 0) {
|
||||||
|
root["atmp"] = temp;
|
||||||
|
}
|
||||||
|
if (hum >= 0) {
|
||||||
|
root["rhum"] = hum;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (agServer.postToServer(getDevId(), JSON.stringify(root)) == false) {
|
||||||
|
Serial.println("Post to server failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dispHandler() {
|
||||||
|
String ln1 = "";
|
||||||
|
String ln2 = "";
|
||||||
|
String ln3 = "";
|
||||||
|
|
||||||
|
if (agServer.isPMSinUSAQI()) {
|
||||||
|
ln1 = "AQI:" + String(ag.pms5003.convertPm25ToUsAqi(pm25));
|
||||||
|
} else {
|
||||||
|
ln1 = "PM :" + String(pm25) + " ug";
|
||||||
|
}
|
||||||
|
ln2 = "CO2:" + String(co2Ppm);
|
||||||
|
|
||||||
|
if (agServer.isTemperatureUnitF()) {
|
||||||
|
ln3 = String((temp * 9 / 5) + 32).substring(0, 4) + " " + String(hum) + "%";
|
||||||
|
} else {
|
||||||
|
ln3 = String(temp).substring(0, 4) + " " + String(hum) + "%";
|
||||||
|
}
|
||||||
|
displayShowText(ln1, ln2, ln3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getDevId(void) { return getNormalizedMac(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief WiFi reconnect handler
|
||||||
|
*/
|
||||||
|
static void updateWiFiConnect(void) {
|
||||||
|
static uint32_t lastRetry;
|
||||||
|
if (wifiHasConfig == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (WiFi.isConnected()) {
|
||||||
|
lastRetry = millis();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t ms = (uint32_t)(millis() - lastRetry);
|
||||||
|
if (ms >= WIFI_CONNECT_RETRY_MS) {
|
||||||
|
lastRetry = millis();
|
||||||
|
WiFi.reconnect();
|
||||||
|
|
||||||
|
Serial.printf("Re-Connect WiFi\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getNormalizedMac() {
|
||||||
|
String mac = WiFi.macAddress();
|
||||||
|
mac.replace(":", "");
|
||||||
|
mac.toLowerCase();
|
||||||
|
return mac;
|
||||||
|
}
|
||||||
|
@ -1,687 +0,0 @@
|
|||||||
/*
|
|
||||||
This is the code for the AirGradient Open Air open-source hardware outdoor Air Quality Monitor with an ESP32-C3 Microcontroller.
|
|
||||||
|
|
||||||
It is an air quality monitor for PM2.5, CO2, TVOCs, NOx, Temperature and Humidity and can send data over Wifi.
|
|
||||||
|
|
||||||
Open source air quality monitors and kits are available:
|
|
||||||
Indoor Monitor: https://www.airgradient.com/indoor/
|
|
||||||
Outdoor Monitor: https://www.airgradient.com/outdoor/
|
|
||||||
|
|
||||||
Build Instructions: https://www.airgradient.com/documentation/open-air-pst-kit-1-3/
|
|
||||||
|
|
||||||
The codes needs the following libraries installed:
|
|
||||||
“WifiManager by tzapu, tablatronix” tested with version 2.0.16-rc.2
|
|
||||||
"Arduino_JSON" by Arduino Version 0.2.0
|
|
||||||
|
|
||||||
Please make sure you have esp32 board manager installed. Tested with version 2.0.11.
|
|
||||||
|
|
||||||
Important flashing settings:
|
|
||||||
- Set board to "ESP32C3 Dev Module"
|
|
||||||
- Enable "USB CDC On Boot"
|
|
||||||
- Flash frequency "80Mhz"
|
|
||||||
- Flash mode "QIO"
|
|
||||||
- Flash size "4MB"
|
|
||||||
- Partition scheme "Default 4MB with spiffs (1.2MB APP/1,5MB SPIFFS)"
|
|
||||||
- JTAG adapter "Disabled"
|
|
||||||
|
|
||||||
If you have any questions please visit our forum at
|
|
||||||
https://forum.airgradient.com/
|
|
||||||
|
|
||||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <AirGradient.h>
|
|
||||||
#include <Arduino_JSON.h>
|
|
||||||
#include <HTTPClient.h>
|
|
||||||
#include <HardwareSerial.h>
|
|
||||||
#include <WiFiManager.h>
|
|
||||||
#include <Wire.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @brief Application state machine state
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
enum {
|
|
||||||
APP_SM_WIFI_MANAGER_MODE, /** In WiFi Manger Mode */
|
|
||||||
APP_SM_WIFI_MAMAGER_PORTAL_ACTIVE, /** WiFi Manager has connected to mobile
|
|
||||||
phone */
|
|
||||||
APP_SM_WIFI_MANAGER_STA_CONNECTING, /** After SSID and PW entered and OK
|
|
||||||
clicked, connection to WiFI network is
|
|
||||||
attempted*/
|
|
||||||
APP_SM_WIFI_MANAGER_STA_CONNECTED, /** Connecting to WiFi worked */
|
|
||||||
APP_SM_WIFI_OK_SERVER_CONNECTING, /** Once connected to WiFi an attempt to
|
|
||||||
reach the server is performed */
|
|
||||||
APP_SM_WIFI_OK_SERVER_CONNNECTED, /** Server is reachable, all fine */
|
|
||||||
/** Exceptions during WIFi Setup */
|
|
||||||
APP_SM_WIFI_MANAGER_CONNECT_FAILED, /** Cannot connect to WiFi (e.g. wrong
|
|
||||||
password, WPA Enterprise etc.) */
|
|
||||||
APP_SM_WIFI_OK_SERVER_CONNECT_FAILED, /** Connected to WiFi but server not
|
|
||||||
reachable, e.g. firewall block/
|
|
||||||
whitelisting needed etc. */
|
|
||||||
APP_SM_WIFI_OK_SERVER_OK_SENSOR_CONFIG_FAILED, /** Server reachable but sensor
|
|
||||||
not configured correctly*/
|
|
||||||
|
|
||||||
/** During Normal Operation */
|
|
||||||
APP_SM_WIFI_LOST, /** Connection to WiFi network failed credentials incorrect
|
|
||||||
encryption not supported etc. */
|
|
||||||
APP_SM_SERVER_LOST, /** Connected to WiFi network but the server cannot be
|
|
||||||
reached through the internet, e.g. blocked by firewall
|
|
||||||
*/
|
|
||||||
APP_SM_SENSOR_CONFIG_FAILED, /** Server is reachable but there is some
|
|
||||||
configuration issue to be fixed on the server
|
|
||||||
side */
|
|
||||||
APP_SM_NORMAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define DEBUG true
|
|
||||||
|
|
||||||
#define WIFI_CONNECT_COUNTDOWN_MAX 180 /** sec */
|
|
||||||
#define WIFI_HOTSPOT_PASSWORD_DEFAULT "cleanair"
|
|
||||||
|
|
||||||
AirGradient ag(BOARD_OUTDOOR_MONITOR_V1_3);
|
|
||||||
|
|
||||||
// time in seconds needed for NOx conditioning
|
|
||||||
uint16_t conditioning_s = 10;
|
|
||||||
|
|
||||||
String APIROOT = "http://hw.airgradient.com/";
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
bool inF; /** Temperature unit */
|
|
||||||
bool inUSAQI; /** PMS standard */
|
|
||||||
uint8_t ledBarMode; /** @ref UseLedBar*/
|
|
||||||
char model[16]; /** Model string value, Just define, don't know how much
|
|
||||||
memory usage */
|
|
||||||
char mqttBroker[128]; /** Mqtt broker link */
|
|
||||||
uint32_t _check; /** Checksum configuration data */
|
|
||||||
} ServerConfig_t;
|
|
||||||
static ServerConfig_t serverConfig;
|
|
||||||
|
|
||||||
// set to true if you want to connect to wifi. You have 60 seconds to connect.
|
|
||||||
// Then it will go into an offline mode.
|
|
||||||
boolean connectWIFI = true;
|
|
||||||
|
|
||||||
static int ledSmState = APP_SM_NORMAL;
|
|
||||||
static bool serverFailed = false;
|
|
||||||
static bool configFailed = false;
|
|
||||||
static bool wifiHasConfig = false;
|
|
||||||
|
|
||||||
int loopCount = 0;
|
|
||||||
|
|
||||||
WiFiManager wifiManager; /** wifi manager instance */
|
|
||||||
|
|
||||||
unsigned long currentMillis = 0;
|
|
||||||
|
|
||||||
const int oledInterval = 5000;
|
|
||||||
unsigned long previousOled = 0;
|
|
||||||
|
|
||||||
const int sendToServerInterval = 60000;
|
|
||||||
const int pollServerConfigInterval = 30000;
|
|
||||||
const int co2CalibCountdown = 5; /** Seconds */
|
|
||||||
unsigned long previoussendToServer = 0;
|
|
||||||
|
|
||||||
const int tvocInterval = 1000;
|
|
||||||
unsigned long previousTVOC = 0;
|
|
||||||
int TVOC = -1;
|
|
||||||
int NOX = -1;
|
|
||||||
|
|
||||||
const int co2Interval = 5000;
|
|
||||||
unsigned long previousCo2 = 0;
|
|
||||||
int Co2 = 0;
|
|
||||||
|
|
||||||
const int pmInterval = 5000;
|
|
||||||
unsigned long previousPm = 0;
|
|
||||||
int pm25 = -1;
|
|
||||||
int pm01 = -1;
|
|
||||||
int pm10 = -1;
|
|
||||||
int pm03PCount = -1;
|
|
||||||
float temp;
|
|
||||||
int hum;
|
|
||||||
|
|
||||||
bool co2CalibrationRequest = false;
|
|
||||||
uint32_t serverConfigLoadTime = 0;
|
|
||||||
String HOTSPOT = "";
|
|
||||||
|
|
||||||
// const int tempHumInterval = 2500;
|
|
||||||
// unsigned long previousTempHum = 0;
|
|
||||||
|
|
||||||
void boardInit(void);
|
|
||||||
void failedHandler(String msg);
|
|
||||||
void getServerConfig(void);
|
|
||||||
void co2Calibration(void);
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
if (DEBUG) {
|
|
||||||
Serial.begin(115200);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Board init */
|
|
||||||
boardInit();
|
|
||||||
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
countdown(3);
|
|
||||||
|
|
||||||
if (connectWIFI) {
|
|
||||||
connectToWifi();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WiFi.status() == WL_CONNECTED) {
|
|
||||||
sendPing();
|
|
||||||
Serial.println(F("WiFi connected!"));
|
|
||||||
Serial.println("IP address: ");
|
|
||||||
Serial.println(WiFi.localIP());
|
|
||||||
}
|
|
||||||
|
|
||||||
getServerConfig();
|
|
||||||
if (configFailed) {
|
|
||||||
ledSmHandler(APP_SM_SENSOR_CONFIG_FAILED);
|
|
||||||
delay(5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
ledSmHandler(APP_SM_NORMAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
currentMillis = millis();
|
|
||||||
updateTVOC();
|
|
||||||
updateCo2();
|
|
||||||
updatePm();
|
|
||||||
sendToServer();
|
|
||||||
getServerConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateTVOC() {
|
|
||||||
delay(1000);
|
|
||||||
|
|
||||||
if (currentMillis - previousTVOC >= tvocInterval) {
|
|
||||||
previousTVOC += tvocInterval;
|
|
||||||
TVOC = ag.sgp41.getTvocIndex();
|
|
||||||
NOX = ag.sgp41.getNoxIndex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateCo2() {
|
|
||||||
if (currentMillis - previousCo2 >= co2Interval) {
|
|
||||||
previousCo2 += co2Interval;
|
|
||||||
Co2 = ag.s8.getCo2();
|
|
||||||
Serial.printf("CO2: %d\r\n", Co2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updatePm() {
|
|
||||||
if (currentMillis - previousPm >= pmInterval) {
|
|
||||||
previousPm += pmInterval;
|
|
||||||
if (ag.pms5003t_1.readData()) {
|
|
||||||
pm01 = ag.pms5003t_1.getPm01Ae();
|
|
||||||
pm25 = ag.pms5003t_1.getPm25Ae();
|
|
||||||
pm10 = ag.pms5003t_1.getPm10Ae();
|
|
||||||
pm03PCount = ag.pms5003t_1.getPm03ParticleCount();
|
|
||||||
temp = ag.pms5003t_1.getTemperature();
|
|
||||||
hum = ag.pms5003t_1.getRelativeHumidity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendPing() {
|
|
||||||
String payload =
|
|
||||||
"{\"wifi\":" + String(WiFi.RSSI()) + ", \"boot\":" + loopCount + "}";
|
|
||||||
if (postToServer(payload)) {
|
|
||||||
ledSmHandler(APP_SM_WIFI_OK_SERVER_CONNNECTED);
|
|
||||||
} else {
|
|
||||||
ledSmHandler(APP_SM_WIFI_OK_SERVER_CONNECT_FAILED);
|
|
||||||
}
|
|
||||||
delay(5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool postToServer(String &payload) {
|
|
||||||
String POSTURL = APIROOT +
|
|
||||||
"sensors/airgradient:" + String(getNormalizedMac()) +
|
|
||||||
"/measures";
|
|
||||||
WiFiClient client;
|
|
||||||
HTTPClient http;
|
|
||||||
|
|
||||||
ag.statusLed.setOn();
|
|
||||||
|
|
||||||
http.begin(client, POSTURL);
|
|
||||||
http.addHeader("content-type", "application/json");
|
|
||||||
int httpCode = http.POST(payload);
|
|
||||||
Serial.printf("Post to %s, %d\r\n", POSTURL.c_str(), httpCode);
|
|
||||||
http.end();
|
|
||||||
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
|
|
||||||
return (httpCode == 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendToServer() {
|
|
||||||
if (currentMillis - previoussendToServer >= sendToServerInterval) {
|
|
||||||
previoussendToServer += sendToServerInterval;
|
|
||||||
String payload =
|
|
||||||
"{\"wifi\":" + String(WiFi.RSSI()) +
|
|
||||||
(Co2 < 0 ? "" : ", \"rco2\":" + String(Co2)) +
|
|
||||||
(pm01 < 0 ? "" : ", \"pm01\":" + String(pm01)) +
|
|
||||||
(pm25 < 0 ? "" : ", \"pm02\":" + String(pm25)) +
|
|
||||||
(pm10 < 0 ? "" : ", \"pm10\":" + String(pm10)) +
|
|
||||||
(pm03PCount < 0 ? "" : ", \"pm003_count\":" + String(pm03PCount)) +
|
|
||||||
(TVOC < 0 ? "" : ", \"tvoc_index\":" + String(TVOC)) +
|
|
||||||
(NOX < 0 ? "" : ", \"nox_index\":" + String(NOX)) +
|
|
||||||
", \"atmp\":" + String(temp) +
|
|
||||||
(hum < 0 ? "" : ", \"rhum\":" + String(hum)) +
|
|
||||||
", \"boot\":" + loopCount + "}";
|
|
||||||
|
|
||||||
if (WiFi.status() == WL_CONNECTED) {
|
|
||||||
postToServer(payload);
|
|
||||||
resetWatchdog();
|
|
||||||
loopCount++;
|
|
||||||
} else {
|
|
||||||
Serial.println("WiFi Disconnected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void countdown(int from) {
|
|
||||||
debug("\n");
|
|
||||||
while (from > 0) {
|
|
||||||
debug(String(from--));
|
|
||||||
debug(" ");
|
|
||||||
delay(1000);
|
|
||||||
}
|
|
||||||
debug("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void resetWatchdog() {
|
|
||||||
Serial.println("Watchdog reset");
|
|
||||||
ag.watchdog.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wifiMangerClientConnected(void) {
|
|
||||||
return WiFi.softAPgetStationNum() ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wifi Manager
|
|
||||||
void connectToWifi() {
|
|
||||||
HOTSPOT = "airgradient-" + String(getNormalizedMac());
|
|
||||||
|
|
||||||
wifiManager.setConfigPortalBlocking(false);
|
|
||||||
wifiManager.setTimeout(WIFI_CONNECT_COUNTDOWN_MAX);
|
|
||||||
|
|
||||||
wifiManager.setAPCallback([](WiFiManager *obj) {
|
|
||||||
/** This callback if wifi connnected failed and try to start configuration
|
|
||||||
* portal */
|
|
||||||
ledSmState = APP_SM_WIFI_MANAGER_MODE;
|
|
||||||
});
|
|
||||||
wifiManager.setSaveConfigCallback([]() {
|
|
||||||
/** Wifi connected save the configuration */
|
|
||||||
ledSmHandler(APP_SM_WIFI_MANAGER_STA_CONNECTED);
|
|
||||||
});
|
|
||||||
wifiManager.setSaveParamsCallback([]() {
|
|
||||||
/** Wifi set connect: ssid, password */
|
|
||||||
ledSmHandler(APP_SM_WIFI_MANAGER_STA_CONNECTING);
|
|
||||||
});
|
|
||||||
wifiManager.autoConnect(HOTSPOT.c_str(), WIFI_HOTSPOT_PASSWORD_DEFAULT);
|
|
||||||
|
|
||||||
xTaskCreate(
|
|
||||||
[](void *obj) {
|
|
||||||
while (wifiManager.getConfigPortalActive()) {
|
|
||||||
wifiManager.process();
|
|
||||||
}
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
},
|
|
||||||
"wifi_cfg", 4096, NULL, 10, NULL);
|
|
||||||
|
|
||||||
uint32_t stimer = millis();
|
|
||||||
bool clientConnectChanged = false;
|
|
||||||
while (wifiManager.getConfigPortalActive()) {
|
|
||||||
if (WiFi.isConnected() == false) {
|
|
||||||
if (ledSmState == APP_SM_WIFI_MANAGER_MODE) {
|
|
||||||
uint32_t ms = (uint32_t)(millis() - stimer);
|
|
||||||
if (ms >= 100) {
|
|
||||||
stimer = millis();
|
|
||||||
ledSmHandler(ledSmState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Check for client connect to change led color */
|
|
||||||
bool clientConnected = wifiMangerClientConnected();
|
|
||||||
if (clientConnected != clientConnectChanged) {
|
|
||||||
clientConnectChanged = clientConnected;
|
|
||||||
if (clientConnectChanged) {
|
|
||||||
ledSmHandler(APP_SM_WIFI_MAMAGER_PORTAL_ACTIVE);
|
|
||||||
} else {
|
|
||||||
ledSmHandler(APP_SM_WIFI_MANAGER_MODE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Show display wifi connect result failed */
|
|
||||||
if (WiFi.isConnected() == false) {
|
|
||||||
ledSmHandler(APP_SM_WIFI_MANAGER_CONNECT_FAILED);
|
|
||||||
} else {
|
|
||||||
wifiHasConfig = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void debug(String msg) {
|
|
||||||
if (DEBUG)
|
|
||||||
Serial.print(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void debug(int msg) {
|
|
||||||
if (DEBUG)
|
|
||||||
Serial.print(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugln(String msg) {
|
|
||||||
if (DEBUG)
|
|
||||||
Serial.println(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugln(int msg) {
|
|
||||||
if (DEBUG)
|
|
||||||
Serial.println(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getNormalizedMac() {
|
|
||||||
String mac = WiFi.macAddress();
|
|
||||||
mac.replace(":", "");
|
|
||||||
mac.toLowerCase();
|
|
||||||
return mac;
|
|
||||||
}
|
|
||||||
|
|
||||||
void boardInit(void) {
|
|
||||||
if (Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin()) == false) {
|
|
||||||
failedHandler("Init I2C failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
ag.watchdog.begin();
|
|
||||||
|
|
||||||
ag.button.begin();
|
|
||||||
|
|
||||||
ag.statusLed.begin();
|
|
||||||
|
|
||||||
if (ag.pms5003t_1.begin(Serial0) == false) {
|
|
||||||
failedHandler("Init PMS5003T failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ag.s8.begin(Serial1) == false) {
|
|
||||||
failedHandler("Init SenseAirS8 failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ag.sgp41.begin(Wire) == false) {
|
|
||||||
failedHandler("Init SGP41 failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void failedHandler(String msg) {
|
|
||||||
while (true) {
|
|
||||||
Serial.println(msg);
|
|
||||||
vTaskDelay(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateServerConfigLoadTime(void) {
|
|
||||||
serverConfigLoadTime = millis();
|
|
||||||
if (serverConfigLoadTime == 0) {
|
|
||||||
serverConfigLoadTime = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void showConfig(void) {
|
|
||||||
Serial.println("Server configuration: ");
|
|
||||||
Serial.printf(" inF: %s\r\n", serverConfig.inF ? "true" : "false");
|
|
||||||
Serial.printf(" inUSAQI: %s\r\n",
|
|
||||||
serverConfig.inUSAQI ? "true" : "false");
|
|
||||||
Serial.printf("useRGBLedBar: %d\r\n", (int)serverConfig.ledBarMode);
|
|
||||||
Serial.printf(" Model: %.*s\r\n", sizeof(serverConfig.model),
|
|
||||||
serverConfig.model);
|
|
||||||
Serial.printf(" Mqtt Broker: %.*s\r\n", sizeof(serverConfig.mqttBroker),
|
|
||||||
serverConfig.mqttBroker);
|
|
||||||
}
|
|
||||||
|
|
||||||
void getServerConfig(void) {
|
|
||||||
/** Only trigger load configuration again after pollServerConfigInterval sec
|
|
||||||
*/
|
|
||||||
if (serverConfigLoadTime) {
|
|
||||||
uint32_t ms = (uint32_t)(millis() - serverConfigLoadTime);
|
|
||||||
if (ms < pollServerConfigInterval) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateServerConfigLoadTime();
|
|
||||||
|
|
||||||
Serial.println("Trigger load server configuration");
|
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
|
||||||
Serial.println(
|
|
||||||
"Ignore get server configuration because WIFI not connected");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// WiFiClient wifiClient;
|
|
||||||
HTTPClient httpClient;
|
|
||||||
|
|
||||||
String getUrl = "http://hw.airgradient.com/sensors/airgradient:" +
|
|
||||||
String(getNormalizedMac()) + "/one/config";
|
|
||||||
Serial.println("HttpClient get: " + getUrl);
|
|
||||||
if (httpClient.begin(getUrl) == false) {
|
|
||||||
Serial.println("HttpClient init failed");
|
|
||||||
updateServerConfigLoadTime();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int respCode = httpClient.GET();
|
|
||||||
|
|
||||||
/** get failure */
|
|
||||||
if (respCode != 200) {
|
|
||||||
Serial.printf("HttpClient get failed: %d\r\n", respCode);
|
|
||||||
updateServerConfigLoadTime();
|
|
||||||
httpClient.end();
|
|
||||||
configFailed = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String respContent = httpClient.getString();
|
|
||||||
Serial.println("Server config: " + respContent);
|
|
||||||
httpClient.end();
|
|
||||||
|
|
||||||
/** Parse JSON */
|
|
||||||
JSONVar root = JSON.parse(respContent);
|
|
||||||
if (JSON.typeof_(root) == "undefined") {
|
|
||||||
Serial.println("Server configura JSON invalid");
|
|
||||||
updateServerConfigLoadTime();
|
|
||||||
configFailed = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
configFailed = false;
|
|
||||||
|
|
||||||
/** Get "country" */
|
|
||||||
bool inF = serverConfig.inF;
|
|
||||||
if (JSON.typeof_(root["country"]) == "string") {
|
|
||||||
String country = root["country"];
|
|
||||||
if (country == "US") {
|
|
||||||
inF = true;
|
|
||||||
} else {
|
|
||||||
inF = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get "pmStandard" */
|
|
||||||
bool inUSAQI = serverConfig.inUSAQI;
|
|
||||||
if (JSON.typeof_(root["pmStandard"]) == "string") {
|
|
||||||
String standard = root["pmStandard"];
|
|
||||||
if (standard == "ugm3") {
|
|
||||||
inUSAQI = false;
|
|
||||||
} else {
|
|
||||||
inUSAQI = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get CO2 "co2CalibrationRequested" */
|
|
||||||
co2CalibrationRequest = false;
|
|
||||||
if (JSON.typeof_(root["co2CalibrationRequested"]) == "boolean") {
|
|
||||||
co2CalibrationRequest = root["co2CalibrationRequested"];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** get "model" */
|
|
||||||
String model = "";
|
|
||||||
if (JSON.typeof_(root["model"]) == "string") {
|
|
||||||
String _model = root["model"];
|
|
||||||
model = _model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** get "mqttBrokerUrl" */
|
|
||||||
String mqtt = "";
|
|
||||||
if (JSON.typeof_(root["mqttBrokerUrl"]) == "string") {
|
|
||||||
String _mqtt = root["mqttBrokerUrl"];
|
|
||||||
mqtt = _mqtt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inF != serverConfig.inF) {
|
|
||||||
serverConfig.inF = inF;
|
|
||||||
}
|
|
||||||
if (inUSAQI != serverConfig.inUSAQI) {
|
|
||||||
serverConfig.inUSAQI = inUSAQI;
|
|
||||||
}
|
|
||||||
if (model.length()) {
|
|
||||||
if (model != String(serverConfig.model)) {
|
|
||||||
memset(serverConfig.model, 0, sizeof(serverConfig.model));
|
|
||||||
memcpy(serverConfig.model, model.c_str(), model.length());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mqtt.length()) {
|
|
||||||
if (mqtt != String(serverConfig.mqttBroker)) {
|
|
||||||
memset(serverConfig.mqttBroker, 0, sizeof(serverConfig.mqttBroker));
|
|
||||||
memcpy(serverConfig.mqttBroker, mqtt.c_str(), mqtt.length());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Show server configuration */
|
|
||||||
showConfig();
|
|
||||||
|
|
||||||
/** Calibration */
|
|
||||||
if (co2CalibrationRequest) {
|
|
||||||
co2Calibration();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void co2Calibration(void) {
|
|
||||||
/** Count down for co2CalibCountdown secs */
|
|
||||||
for (int i = 0; i < co2CalibCountdown; i++) {
|
|
||||||
Serial.printf("Start CO2 calib after %d sec\r\n", co2CalibCountdown - i);
|
|
||||||
delay(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ag.s8.setBaselineCalibration()) {
|
|
||||||
Serial.println("Calibration success");
|
|
||||||
delay(1000);
|
|
||||||
Serial.println("Wait for calib finish...");
|
|
||||||
int count = 0;
|
|
||||||
while (ag.s8.isBaseLineCalibrationDone() == false) {
|
|
||||||
delay(1000);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
Serial.printf("Calib finish after %d sec\r\n", count);
|
|
||||||
delay(2000);
|
|
||||||
} else {
|
|
||||||
Serial.println("Calibration failure!!!");
|
|
||||||
delay(2000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ledSmHandler(int sm) {
|
|
||||||
if (sm > APP_SM_NORMAL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ledSmState = sm;
|
|
||||||
switch (sm) {
|
|
||||||
case APP_SM_WIFI_MANAGER_MODE: {
|
|
||||||
ag.statusLed.setToggle();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APP_SM_WIFI_MAMAGER_PORTAL_ACTIVE: {
|
|
||||||
ag.statusLed.setOn();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APP_SM_WIFI_MANAGER_STA_CONNECTING: {
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APP_SM_WIFI_MANAGER_STA_CONNECTED: {
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APP_SM_WIFI_OK_SERVER_CONNECTING: {
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APP_SM_WIFI_OK_SERVER_CONNNECTED: {
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
ag.statusLed.setOn();
|
|
||||||
delay(50);
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
delay(950);
|
|
||||||
ag.statusLed.setOn();
|
|
||||||
delay(50);
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APP_SM_WIFI_MANAGER_CONNECT_FAILED: {
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
for (int i = 0; i < 3 * 2; i++) {
|
|
||||||
ag.statusLed.setToggle();
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
delay(2000);
|
|
||||||
}
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APP_SM_WIFI_OK_SERVER_CONNECT_FAILED: {
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
for (int i = 0; i < 4 * 2; i++) {
|
|
||||||
ag.statusLed.setToggle();
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
delay(2000);
|
|
||||||
}
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APP_SM_WIFI_OK_SERVER_OK_SENSOR_CONFIG_FAILED: {
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
for (int i = 0; i < 5 * 2; i++) {
|
|
||||||
ag.statusLed.setToggle();
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
delay(2000);
|
|
||||||
}
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APP_SM_WIFI_LOST: {
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APP_SM_SERVER_LOST: {
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APP_SM_SENSOR_CONFIG_FAILED: {
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APP_SM_NORMAL: {
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
974
examples/Open_Air_O/Open_Air_O.ino
Normal file
@ -0,0 +1,974 @@
|
|||||||
|
/*
|
||||||
|
This is the code for the AirGradient Open Air open-source hardware outdoor Air
|
||||||
|
Quality Monitor with an ESP32-C3 Microcontroller.
|
||||||
|
|
||||||
|
It is an air quality monitor for PM2.5, CO2, TVOCs, NOx, Temperature and
|
||||||
|
Humidity and can send data over Wifi.
|
||||||
|
|
||||||
|
Open source air quality monitors and kits are available:
|
||||||
|
Indoor Monitor: https://www.airgradient.com/indoor/
|
||||||
|
Outdoor Monitor: https://www.airgradient.com/outdoor/
|
||||||
|
|
||||||
|
Build Instructions:
|
||||||
|
https://www.airgradient.com/documentation/open-air-pst-kit-1-3/
|
||||||
|
|
||||||
|
The codes needs the following libraries installed:
|
||||||
|
“WifiManager by tzapu, tablatronix” tested with version 2.0.16-rc.2
|
||||||
|
"Arduino_JSON" by Arduino Version 0.2.0
|
||||||
|
|
||||||
|
Please make sure you have esp32 board manager installed. Tested with
|
||||||
|
version 2.0.11.
|
||||||
|
|
||||||
|
Important flashing settings:
|
||||||
|
- Set board to "ESP32C3 Dev Module"
|
||||||
|
- Enable "USB CDC On Boot"
|
||||||
|
- Flash frequency "80Mhz"
|
||||||
|
- Flash mode "QIO"
|
||||||
|
- Flash size "4MB"
|
||||||
|
- Partition scheme "Default 4MB with spiffs (1.2MB APP/1,5MB SPIFFS)"
|
||||||
|
- JTAG adapter "Disabled"
|
||||||
|
|
||||||
|
If you have any questions please visit our forum at
|
||||||
|
https://forum.airgradient.com/
|
||||||
|
|
||||||
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AirGradient.h>
|
||||||
|
#include <Arduino_JSON.h>
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
#include <WiFiManager.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @brief Application state machine state
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
APP_SM_WIFI_MANAGER_MODE, /** In WiFi Manger Mode */
|
||||||
|
APP_SM_WIFI_MAMAGER_PORTAL_ACTIVE, /** WiFi Manager has connected to mobile
|
||||||
|
phone */
|
||||||
|
APP_SM_WIFI_MANAGER_STA_CONNECTING, /** After SSID and PW entered and OK
|
||||||
|
clicked, connection to WiFI network is
|
||||||
|
attempted*/
|
||||||
|
APP_SM_WIFI_MANAGER_STA_CONNECTED, /** Connecting to WiFi worked */
|
||||||
|
APP_SM_WIFI_OK_SERVER_CONNECTING, /** Once connected to WiFi an attempt to
|
||||||
|
reach the server is performed */
|
||||||
|
APP_SM_WIFI_OK_SERVER_CONNNECTED, /** Server is reachable, all fine */
|
||||||
|
/** Exceptions during WIFi Setup */
|
||||||
|
APP_SM_WIFI_MANAGER_CONNECT_FAILED, /** Cannot connect to WiFi (e.g. wrong
|
||||||
|
password, WPA Enterprise etc.) */
|
||||||
|
APP_SM_WIFI_OK_SERVER_CONNECT_FAILED, /** Connected to WiFi but server not
|
||||||
|
reachable, e.g. firewall block/
|
||||||
|
whitelisting needed etc. */
|
||||||
|
APP_SM_WIFI_OK_SERVER_OK_SENSOR_CONFIG_FAILED, /** Server reachable but sensor
|
||||||
|
not configured correctly*/
|
||||||
|
|
||||||
|
/** During Normal Operation */
|
||||||
|
APP_SM_WIFI_LOST, /** Connection to WiFi network failed credentials incorrect
|
||||||
|
encryption not supported etc. */
|
||||||
|
APP_SM_SERVER_LOST, /** Connected to WiFi network but the server cannot be
|
||||||
|
reached through the internet, e.g. blocked by firewall
|
||||||
|
*/
|
||||||
|
APP_SM_SENSOR_CONFIG_FAILED, /** Server is reachable but there is some
|
||||||
|
configuration issue to be fixed on the server
|
||||||
|
side */
|
||||||
|
APP_SM_NORMAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define LED_FAST_BLINK_DELAY 250 /** ms */
|
||||||
|
#define LED_SLOW_BLINK_DELAY 1000 /** ms */
|
||||||
|
#define WIFI_CONNECT_COUNTDOWN_MAX 180 /** sec */
|
||||||
|
#define WIFI_CONNECT_RETRY_MS 10000 /** ms */
|
||||||
|
#define LED_BAR_COUNT_INIT_VALUE (-1) /** */
|
||||||
|
#define LED_BAR_ANIMATION_PERIOD 100 /** ms */
|
||||||
|
#define DISP_UPDATE_INTERVAL 5000 /** ms */
|
||||||
|
#define SERVER_CONFIG_UPDATE_INTERVAL 30000 /** ms */
|
||||||
|
#define SERVER_SYNC_INTERVAL 60000 /** ms */
|
||||||
|
#define SENSOR_CO2_CALIB_COUNTDOWN_MAX 5 /** sec */
|
||||||
|
#define SENSOR_TVOC_UPDATE_INTERVAL 1000 /** ms */
|
||||||
|
#define SENSOR_CO2_UPDATE_INTERVAL 5000 /** ms */
|
||||||
|
#define SENSOR_PM_UPDATE_INTERVAL 5000 /** ms */
|
||||||
|
#define SENSOR_TEMP_HUM_UPDATE_INTERVAL 5000 /** ms */
|
||||||
|
#define DISPLAY_DELAY_SHOW_CONTENT_MS 2000 /** ms */
|
||||||
|
#define WIFI_HOTSPOT_PASSWORD_DEFAULT \
|
||||||
|
"cleanair" /** default WiFi AP password \
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Use use LED bar state
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
UseLedBarOff, /** Don't use LED bar */
|
||||||
|
UseLedBarPM, /** Use LED bar for PMS */
|
||||||
|
UseLedBarCO2, /** Use LED bar for CO2 */
|
||||||
|
} UseLedBar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Schedule handle with timing period
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class AgSchedule {
|
||||||
|
public:
|
||||||
|
AgSchedule(int period, void (*handler)(void))
|
||||||
|
: period(period), handler(handler) {}
|
||||||
|
void run(void) {
|
||||||
|
uint32_t ms = (uint32_t)(millis() - count);
|
||||||
|
if (ms >= period) {
|
||||||
|
/** Call handler */
|
||||||
|
handler();
|
||||||
|
|
||||||
|
Serial.printf("[AgSchedule] handle 0x%08x, period: %d(ms)\r\n",
|
||||||
|
(unsigned int)handler, period);
|
||||||
|
|
||||||
|
/** Update period time */
|
||||||
|
count = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void setPeriod(int period) { this->period = period; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void (*handler)(void);
|
||||||
|
int period;
|
||||||
|
int count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief AirGradient server configuration and sync data
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class AgServer {
|
||||||
|
public:
|
||||||
|
void begin(void) {
|
||||||
|
inF = false;
|
||||||
|
inUSAQI = false;
|
||||||
|
configFailed = false;
|
||||||
|
serverFailed = false;
|
||||||
|
memset(models, 0, sizeof(models));
|
||||||
|
memset(mqttBroker, 0, sizeof(mqttBroker));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get server configuration
|
||||||
|
*
|
||||||
|
* @param id Device ID
|
||||||
|
* @return true Success
|
||||||
|
* @return false Failure
|
||||||
|
*/
|
||||||
|
bool pollServerConfig(String id) {
|
||||||
|
String uri =
|
||||||
|
"http://hw.airgradient.com/sensors/airgradient:" + id + "/one/config";
|
||||||
|
|
||||||
|
/** Init http client */
|
||||||
|
HTTPClient client;
|
||||||
|
if (client.begin(uri) == false) {
|
||||||
|
configFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get */
|
||||||
|
int retCode = client.GET();
|
||||||
|
if (retCode != 200) {
|
||||||
|
client.end();
|
||||||
|
configFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** clear failed */
|
||||||
|
configFailed = false;
|
||||||
|
|
||||||
|
/** Get response string */
|
||||||
|
String respContent = client.getString();
|
||||||
|
client.end();
|
||||||
|
Serial.println("Get server config: " + respContent);
|
||||||
|
|
||||||
|
/** Parse JSON */
|
||||||
|
JSONVar root = JSON.parse(respContent);
|
||||||
|
if (JSON.typeof(root) == "undefined") {
|
||||||
|
/** JSON invalid */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get "country" */
|
||||||
|
if (JSON.typeof_(root["country"]) == "string") {
|
||||||
|
String country = root["country"];
|
||||||
|
if (country == "US") {
|
||||||
|
inF = true;
|
||||||
|
} else {
|
||||||
|
inF = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get "pmStandard" */
|
||||||
|
if (JSON.typeof_(root["pmStandard"]) == "string") {
|
||||||
|
String standard = root["pmStandard"];
|
||||||
|
if (standard == "ugm3") {
|
||||||
|
inUSAQI = false;
|
||||||
|
} else {
|
||||||
|
inUSAQI = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get "co2CalibrationRequested" */
|
||||||
|
if (JSON.typeof_(root["co2CalibrationRequested"]) == "boolean") {
|
||||||
|
co2Calib = root["co2CalibrationRequested"];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get "ledBarMode" */
|
||||||
|
if (JSON.typeof_(root["ledBarMode"]) == "string") {
|
||||||
|
String mode = root["ledBarMode"];
|
||||||
|
if (mode == "co2") {
|
||||||
|
ledBarMode = UseLedBarCO2;
|
||||||
|
} else if (mode == "pm") {
|
||||||
|
ledBarMode = UseLedBarPM;
|
||||||
|
} else if (mode == "off") {
|
||||||
|
ledBarMode = UseLedBarOff;
|
||||||
|
} else {
|
||||||
|
ledBarMode = UseLedBarOff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get model */
|
||||||
|
if (JSON.typeof_(root["model"]) == "string") {
|
||||||
|
String model = root["model"];
|
||||||
|
if (model.length()) {
|
||||||
|
int len =
|
||||||
|
model.length() < sizeof(models) ? model.length() : sizeof(models);
|
||||||
|
memset(models, 0, sizeof(models));
|
||||||
|
memcpy(models, model.c_str(), len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get "mqttBrokerUrl" */
|
||||||
|
if (JSON.typeof_(root["mqttBrokerUrl"]) == "string") {
|
||||||
|
String mqtt = root["mqttBrokerUrl"];
|
||||||
|
if (mqtt.length()) {
|
||||||
|
int len = mqtt.length() < sizeof(mqttBroker) ? mqtt.length()
|
||||||
|
: sizeof(mqttBroker);
|
||||||
|
memset(mqttBroker, 0, sizeof(mqttBroker));
|
||||||
|
memcpy(mqttBroker, mqtt.c_str(), len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get 'abcDays' */
|
||||||
|
if (JSON.typeof_(root["abcDays"]) == "number") {
|
||||||
|
co2AbcCalib = root["abcDays"];
|
||||||
|
} else {
|
||||||
|
co2AbcCalib = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Show configuration */
|
||||||
|
showServerConfig();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool postToServer(String id, String payload) {
|
||||||
|
/**
|
||||||
|
* @brief Only post data if WiFi is connected
|
||||||
|
*/
|
||||||
|
if (WiFi.isConnected() == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("Post payload: %s\r\n", payload.c_str());
|
||||||
|
|
||||||
|
String uri =
|
||||||
|
"http://hw.airgradient.com/sensors/airgradient:" + id + "/measures";
|
||||||
|
|
||||||
|
WiFiClient wifiClient;
|
||||||
|
HTTPClient client;
|
||||||
|
if (client.begin(wifiClient, uri.c_str()) == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
client.addHeader("content-type", "application/json");
|
||||||
|
int retCode = client.POST(payload);
|
||||||
|
client.end();
|
||||||
|
|
||||||
|
if ((retCode == 200) || (retCode == 429)) {
|
||||||
|
serverFailed = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
serverFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get temperature configuration unit
|
||||||
|
*
|
||||||
|
* @return true F unit
|
||||||
|
* @return false C Unit
|
||||||
|
*/
|
||||||
|
bool isTemperatureUnitF(void) { return inF; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get PMS standard unit
|
||||||
|
*
|
||||||
|
* @return true USAQI
|
||||||
|
* @return false ugm3
|
||||||
|
*/
|
||||||
|
bool isPMSinUSAQI(void) { return inUSAQI; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get status of get server coniguration is failed
|
||||||
|
*
|
||||||
|
* @return true Failed
|
||||||
|
* @return false Success
|
||||||
|
*/
|
||||||
|
bool isConfigFailed(void) { return configFailed; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get status of post server configuration is failed
|
||||||
|
*
|
||||||
|
* @return true Failed
|
||||||
|
* @return false Success
|
||||||
|
*/
|
||||||
|
bool isServerFailed(void) { return serverFailed; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get request calibration CO2
|
||||||
|
*
|
||||||
|
* @return true Requested. If result = true, it's clear after function call
|
||||||
|
* @return false Not-requested
|
||||||
|
*/
|
||||||
|
bool isCo2Calib(void) {
|
||||||
|
bool ret = co2Calib;
|
||||||
|
if (ret) {
|
||||||
|
co2Calib = false;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the Co2 auto calib period
|
||||||
|
*
|
||||||
|
* @return int days, -1 if invalid.
|
||||||
|
*/
|
||||||
|
int getCo2Abccalib(void) { return co2AbcCalib; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get device configuration model name
|
||||||
|
*
|
||||||
|
* @return String Model name, empty string if server failed
|
||||||
|
*/
|
||||||
|
String getModelName(void) { return String(models); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get mqttBroker url
|
||||||
|
*
|
||||||
|
* @return String Broker url, empty if server failed
|
||||||
|
*/
|
||||||
|
String getMqttBroker(void) { return String(mqttBroker); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Show server configuration parameter
|
||||||
|
*/
|
||||||
|
void showServerConfig(void) {
|
||||||
|
Serial.println("Server configuration: ");
|
||||||
|
Serial.printf(" inF: %s\r\n", inF ? "true" : "false");
|
||||||
|
Serial.printf(" inUSAQI: %s\r\n", inUSAQI ? "true" : "false");
|
||||||
|
Serial.printf(" useRGBLedBar: %d\r\n", (int)ledBarMode);
|
||||||
|
Serial.printf(" Model: %s\r\n", models);
|
||||||
|
Serial.printf(" Mqtt Broker: %s\r\n", mqttBroker);
|
||||||
|
Serial.printf(" S8 calib period: %d\r\n", co2AbcCalib);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get server config led bar mode
|
||||||
|
*
|
||||||
|
* @return UseLedBar
|
||||||
|
*/
|
||||||
|
UseLedBar getLedBarMode(void) { return ledBarMode; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool inF; /** Temperature unit, true: F, false: C */
|
||||||
|
bool inUSAQI; /** PMS unit, true: USAQI, false: ugm3 */
|
||||||
|
bool configFailed; /** Flag indicate get server configuration failed */
|
||||||
|
bool serverFailed; /** Flag indicate post data to server failed */
|
||||||
|
bool co2Calib; /** Is co2Ppmcalibration requset */
|
||||||
|
int co2AbcCalib = -1; /** update auto calibration number of day */
|
||||||
|
UseLedBar ledBarMode = UseLedBarCO2; /** */
|
||||||
|
char models[20]; /** */
|
||||||
|
char mqttBroker[256]; /** */
|
||||||
|
};
|
||||||
|
AgServer agServer;
|
||||||
|
|
||||||
|
/** Create airgradient instance for 'OPEN_AIR_OUTDOOR' board */
|
||||||
|
AirGradient ag(OPEN_AIR_OUTDOOR);
|
||||||
|
|
||||||
|
static int ledSmState = APP_SM_NORMAL;
|
||||||
|
|
||||||
|
int loopCount = 0;
|
||||||
|
|
||||||
|
WiFiManager wifiManager; /** wifi manager instance */
|
||||||
|
static bool wifiHasConfig = false;
|
||||||
|
static String wifiSSID = "";
|
||||||
|
|
||||||
|
int tvocIndex = -1;
|
||||||
|
int noxIndex = -1;
|
||||||
|
int co2Ppm = 0;
|
||||||
|
|
||||||
|
int pm25_1 = -1;
|
||||||
|
int pm01_1 = -1;
|
||||||
|
int pm10_1 = -1;
|
||||||
|
int pm03PCount_1 = -1;
|
||||||
|
float temp_1;
|
||||||
|
int hum_1;
|
||||||
|
|
||||||
|
int pm25_2 = -1;
|
||||||
|
int pm01_2 = -1;
|
||||||
|
int pm10_2 = -1;
|
||||||
|
int pm03PCount_2 = -1;
|
||||||
|
float temp_2;
|
||||||
|
int hum_2;
|
||||||
|
|
||||||
|
int pm1Value01;
|
||||||
|
int pm1Value25;
|
||||||
|
int pm1Value10;
|
||||||
|
int pm1PCount;
|
||||||
|
int pm1temp;
|
||||||
|
int pm1hum;
|
||||||
|
int pm2Value01;
|
||||||
|
int pm2Value25;
|
||||||
|
int pm2Value10;
|
||||||
|
int pm2PCount;
|
||||||
|
int pm2temp;
|
||||||
|
int pm2hum;
|
||||||
|
int countPosition;
|
||||||
|
const int targetCount = 20;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
FW_MODE_PST, /** PMS5003T, S8 and SGP41 */
|
||||||
|
FW_MODE_PPT, /** PMS5003T_1, PMS5003T_2, SGP41 */
|
||||||
|
FW_MODE_PP /** PMS5003T_1, PMS5003T_2 */
|
||||||
|
};
|
||||||
|
int fw_mode = FW_MODE_PST;
|
||||||
|
|
||||||
|
void boardInit(void);
|
||||||
|
void failedHandler(String msg);
|
||||||
|
void co2Calibration(void);
|
||||||
|
static String getDevId(void);
|
||||||
|
static void updateWiFiConnect(void);
|
||||||
|
static void tvocPoll(void);
|
||||||
|
static void pmPoll(void);
|
||||||
|
static void sendDataToServer(void);
|
||||||
|
static void co2Poll(void);
|
||||||
|
static void serverConfigPoll(void);
|
||||||
|
static const char *getFwMode(int mode);
|
||||||
|
|
||||||
|
AgSchedule configSchedule(SERVER_CONFIG_UPDATE_INTERVAL, serverConfigPoll);
|
||||||
|
AgSchedule serverSchedule(SERVER_SYNC_INTERVAL, sendDataToServer);
|
||||||
|
AgSchedule co2Schedule(SENSOR_CO2_UPDATE_INTERVAL, co2Poll);
|
||||||
|
AgSchedule pmsSchedule(SENSOR_PM_UPDATE_INTERVAL, pmPoll);
|
||||||
|
AgSchedule tvocSchedule(SENSOR_TVOC_UPDATE_INTERVAL, tvocPoll);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
/** Board init */
|
||||||
|
boardInit();
|
||||||
|
|
||||||
|
/** Server init */
|
||||||
|
agServer.begin();
|
||||||
|
|
||||||
|
/** WiFi connect */
|
||||||
|
connectToWifi();
|
||||||
|
|
||||||
|
if (WiFi.isConnected()) {
|
||||||
|
wifiHasConfig = true;
|
||||||
|
sendPing();
|
||||||
|
|
||||||
|
agServer.pollServerConfig(getDevId());
|
||||||
|
if (agServer.isConfigFailed()) {
|
||||||
|
ledSmHandler(APP_SM_WIFI_OK_SERVER_OK_SENSOR_CONFIG_FAILED);
|
||||||
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ledSmHandler(APP_SM_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
configSchedule.run();
|
||||||
|
serverSchedule.run();
|
||||||
|
if (fw_mode == FW_MODE_PST) {
|
||||||
|
co2Schedule.run();
|
||||||
|
}
|
||||||
|
pmsSchedule.run();
|
||||||
|
if (fw_mode == FW_MODE_PST || fw_mode == FW_MODE_PPT) {
|
||||||
|
tvocSchedule.run();
|
||||||
|
}
|
||||||
|
updateWiFiConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendPing() {
|
||||||
|
JSONVar root;
|
||||||
|
root["wifi"] = WiFi.RSSI();
|
||||||
|
root["boot"] = loopCount;
|
||||||
|
if (agServer.postToServer(getDevId(), JSON.stringify(root))) {
|
||||||
|
ledSmHandler(APP_SM_WIFI_OK_SERVER_CONNNECTED);
|
||||||
|
} else {
|
||||||
|
ledSmHandler(APP_SM_WIFI_OK_SERVER_CONNECT_FAILED);
|
||||||
|
}
|
||||||
|
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sendDataToServer(void) {
|
||||||
|
JSONVar root;
|
||||||
|
root["wifi"] = WiFi.RSSI();
|
||||||
|
root["boot"] = loopCount;
|
||||||
|
if (fw_mode == FW_MODE_PST) {
|
||||||
|
if (co2Ppm >= 0) {
|
||||||
|
root["rco2"] = co2Ppm;
|
||||||
|
}
|
||||||
|
if (pm01_1 >= 0) {
|
||||||
|
root["pm01"] = pm01_1;
|
||||||
|
}
|
||||||
|
if (pm25_1 >= 0) {
|
||||||
|
root["pm02"] = pm25_1;
|
||||||
|
}
|
||||||
|
if (pm10_1 >= 0) {
|
||||||
|
root["pm10"] = pm10_1;
|
||||||
|
}
|
||||||
|
if (pm03PCount_1 >= 0) {
|
||||||
|
root["pm003_count"] = pm03PCount_1;
|
||||||
|
}
|
||||||
|
if (tvocIndex >= 0) {
|
||||||
|
root["tvoc_index"] = tvocIndex;
|
||||||
|
}
|
||||||
|
if (noxIndex >= 0) {
|
||||||
|
root["noxIndex"] = noxIndex;
|
||||||
|
}
|
||||||
|
if (temp_1 >= 0) {
|
||||||
|
root["atmp"] = temp_1;
|
||||||
|
}
|
||||||
|
if (hum_1 >= 0) {
|
||||||
|
root["rhum"] = hum_1;
|
||||||
|
}
|
||||||
|
} else if (fw_mode == FW_MODE_PPT) {
|
||||||
|
if (tvocIndex > 0) {
|
||||||
|
root["tvoc_index"] = loopCount;
|
||||||
|
}
|
||||||
|
if (noxIndex > 0) {
|
||||||
|
root["nox_index"] = loopCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fw_mode == FW_MODE_PP || FW_MODE_PPT) {
|
||||||
|
root["pm01"] = (int)((pm01_1 + pm01_2) / 2);
|
||||||
|
root["pm02"] = (int)((pm25_1 + pm25_2) / 2);
|
||||||
|
root["pm003_count"] = (int)((pm03PCount_1 + pm03PCount_2) / 2);
|
||||||
|
root["atmp"] = (int)((temp_1 + temp_2) / 2);
|
||||||
|
root["rhum"] = (int)((hum_1 + hum_2) / 2);
|
||||||
|
root["channels"]["1"]["pm01"] = pm01_1;
|
||||||
|
root["channels"]["1"]["pm02"] = pm25_1;
|
||||||
|
root["channels"]["1"]["pm10"] = pm10_1;
|
||||||
|
root["channels"]["1"]["pm003_count"] = pm03PCount_1;
|
||||||
|
root["channels"]["1"]["atmp"] = temp_1;
|
||||||
|
root["channels"]["1"]["rhum"] = hum_1;
|
||||||
|
root["channels"]["2"]["pm01"] = pm01_2;
|
||||||
|
root["channels"]["2"]["pm02"] = pm25_2;
|
||||||
|
root["channels"]["2"]["pm10"] = pm10_2;
|
||||||
|
root["channels"]["2"]["pm003_count"] = pm03PCount_2;
|
||||||
|
root["channels"]["2"]["atmp"] = temp_2;
|
||||||
|
root["channels"]["2"]["rhum"] = hum_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send data to sensor */
|
||||||
|
if (agServer.postToServer(getDevId(), JSON.stringify(root))) {
|
||||||
|
resetWatchdog();
|
||||||
|
}
|
||||||
|
loopCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetWatchdog() {
|
||||||
|
Serial.println("Watchdog reset");
|
||||||
|
ag.watchdog.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wifiMangerClientConnected(void) {
|
||||||
|
return WiFi.softAPgetStationNum() ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wifi Manager
|
||||||
|
void connectToWifi() {
|
||||||
|
wifiSSID = "airgradient-" + String(getNormalizedMac());
|
||||||
|
|
||||||
|
wifiManager.setConfigPortalBlocking(false);
|
||||||
|
wifiManager.setTimeout(WIFI_CONNECT_COUNTDOWN_MAX);
|
||||||
|
|
||||||
|
wifiManager.setAPCallback([](WiFiManager *obj) {
|
||||||
|
/** This callback if wifi connnected failed and try to start configuration
|
||||||
|
* portal */
|
||||||
|
ledSmState = APP_SM_WIFI_MANAGER_MODE;
|
||||||
|
});
|
||||||
|
wifiManager.setSaveConfigCallback([]() {
|
||||||
|
/** Wifi connected save the configuration */
|
||||||
|
ledSmHandler(APP_SM_WIFI_MANAGER_STA_CONNECTED);
|
||||||
|
});
|
||||||
|
wifiManager.setSaveParamsCallback([]() {
|
||||||
|
/** Wifi set connect: ssid, password */
|
||||||
|
ledSmHandler(APP_SM_WIFI_MANAGER_STA_CONNECTING);
|
||||||
|
});
|
||||||
|
wifiManager.autoConnect(wifiSSID.c_str(), WIFI_HOTSPOT_PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
xTaskCreate(
|
||||||
|
[](void *obj) {
|
||||||
|
while (wifiManager.getConfigPortalActive()) {
|
||||||
|
wifiManager.process();
|
||||||
|
}
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
},
|
||||||
|
"wifi_cfg", 4096, NULL, 10, NULL);
|
||||||
|
|
||||||
|
uint32_t stimer = millis();
|
||||||
|
bool clientConnectChanged = false;
|
||||||
|
while (wifiManager.getConfigPortalActive()) {
|
||||||
|
if (WiFi.isConnected() == false) {
|
||||||
|
if (ledSmState == APP_SM_WIFI_MANAGER_MODE) {
|
||||||
|
uint32_t ms = (uint32_t)(millis() - stimer);
|
||||||
|
if (ms >= 100) {
|
||||||
|
stimer = millis();
|
||||||
|
ledSmHandler(ledSmState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check for client connect to change led color */
|
||||||
|
bool clientConnected = wifiMangerClientConnected();
|
||||||
|
if (clientConnected != clientConnectChanged) {
|
||||||
|
clientConnectChanged = clientConnected;
|
||||||
|
if (clientConnectChanged) {
|
||||||
|
ledSmHandler(APP_SM_WIFI_MAMAGER_PORTAL_ACTIVE);
|
||||||
|
} else {
|
||||||
|
ledSmHandler(APP_SM_WIFI_MANAGER_MODE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Show display wifi connect result failed */
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
delay(2000);
|
||||||
|
if (WiFi.isConnected() == false) {
|
||||||
|
ledSmHandler(APP_SM_WIFI_MANAGER_CONNECT_FAILED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getNormalizedMac() {
|
||||||
|
String mac = WiFi.macAddress();
|
||||||
|
mac.replace(":", "");
|
||||||
|
mac.toLowerCase();
|
||||||
|
return mac;
|
||||||
|
}
|
||||||
|
|
||||||
|
void boardInit(void) {
|
||||||
|
if (Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin()) == false) {
|
||||||
|
failedHandler("Init I2C failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
ag.watchdog.begin();
|
||||||
|
|
||||||
|
ag.button.begin();
|
||||||
|
|
||||||
|
ag.statusLed.begin();
|
||||||
|
|
||||||
|
/** detect sensor: PMS5003, PMS5003T, SGP41 and S8 */
|
||||||
|
if (ag.s8.begin(Serial1) == false) {
|
||||||
|
Serial.println("S8 not detect run mode 'PPT'");
|
||||||
|
fw_mode = FW_MODE_PPT;
|
||||||
|
|
||||||
|
/** De-initialize Serial1 */
|
||||||
|
Serial1.end();
|
||||||
|
}
|
||||||
|
if (ag.sgp41.begin(Wire) == false) {
|
||||||
|
if (fw_mode == FW_MODE_PST) {
|
||||||
|
failedHandler("Init SGP41 failed");
|
||||||
|
} else {
|
||||||
|
Serial.println("SGP41 not detect run mode 'PP'");
|
||||||
|
fw_mode = FW_MODE_PP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ag.pms5003t_1.begin(Serial0) == false) {
|
||||||
|
failedHandler("Init PMS5003T_1 failed");
|
||||||
|
}
|
||||||
|
if (fw_mode != FW_MODE_PST) {
|
||||||
|
if (ag.pms5003t_2.begin(Serial1) == false) {
|
||||||
|
failedHandler("Init PMS5003T_2 failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fw_mode != FW_MODE_PST) {
|
||||||
|
pmsSchedule.setPeriod(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("Firmware node: %s\r\n", getFwMode(fw_mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void failedHandler(String msg) {
|
||||||
|
while (true) {
|
||||||
|
Serial.println(msg);
|
||||||
|
vTaskDelay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void co2Calibration(void) {
|
||||||
|
/** Count down for co2CalibCountdown secs */
|
||||||
|
for (int i = 0; i < SENSOR_CO2_CALIB_COUNTDOWN_MAX; i++) {
|
||||||
|
Serial.printf("Start CO2 calib after %d sec\r\n",
|
||||||
|
SENSOR_CO2_CALIB_COUNTDOWN_MAX - i);
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ag.s8.setBaselineCalibration()) {
|
||||||
|
Serial.println("Calibration success");
|
||||||
|
delay(1000);
|
||||||
|
Serial.println("Wait for calib finish...");
|
||||||
|
int count = 0;
|
||||||
|
while (ag.s8.isBaseLineCalibrationDone() == false) {
|
||||||
|
delay(1000);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
Serial.printf("Calib finish after %d sec\r\n", count);
|
||||||
|
delay(2000);
|
||||||
|
} else {
|
||||||
|
Serial.println("Calibration failure!!!");
|
||||||
|
delay(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief WiFi reconnect handler
|
||||||
|
*/
|
||||||
|
static void updateWiFiConnect(void) {
|
||||||
|
static uint32_t lastRetry;
|
||||||
|
if (wifiHasConfig == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (WiFi.isConnected()) {
|
||||||
|
lastRetry = millis();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t ms = (uint32_t)(millis() - lastRetry);
|
||||||
|
if (ms >= WIFI_CONNECT_RETRY_MS) {
|
||||||
|
lastRetry = millis();
|
||||||
|
WiFi.reconnect();
|
||||||
|
|
||||||
|
Serial.printf("Re-Connect WiFi\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update tvocIndexindex
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void tvocPoll(void) {
|
||||||
|
tvocIndex = ag.sgp41.getTvocIndex();
|
||||||
|
noxIndex = ag.sgp41.getNoxIndex();
|
||||||
|
|
||||||
|
Serial.printf("tvocIndexindex: %d\r\n", tvocIndex);
|
||||||
|
Serial.printf(" NOx index: %d\r\n", noxIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update PMS data
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void pmPoll(void) {
|
||||||
|
if (fw_mode == FW_MODE_PST) {
|
||||||
|
if (ag.pms5003t_1.readData()) {
|
||||||
|
pm01_1 = ag.pms5003t_1.getPm01Ae();
|
||||||
|
pm25_1 = ag.pms5003t_1.getPm25Ae();
|
||||||
|
pm25_1 = ag.pms5003t_1.getPm10Ae();
|
||||||
|
pm03PCount_1 = ag.pms5003t_1.getPm03ParticleCount();
|
||||||
|
temp_1 = ag.pms5003t_1.getTemperature();
|
||||||
|
hum_1 = ag.pms5003t_1.getRelativeHumidity();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ag.pms5003t_1.readData() && ag.pms5003t_2.readData()) {
|
||||||
|
pm1Value01 = pm1Value01 + ag.pms5003t_1.getPm01Ae();
|
||||||
|
pm1Value25 = pm1Value25 + ag.pms5003t_1.getPm25Ae();
|
||||||
|
pm1Value10 = pm1Value10 + ag.pms5003t_1.getPm10Ae();
|
||||||
|
pm1PCount = pm1PCount + ag.pms5003t_1.getPm03ParticleCount();
|
||||||
|
pm1temp = pm1temp + ag.pms5003t_1.getTemperature();
|
||||||
|
pm1hum = pm1hum + ag.pms5003t_1.getRelativeHumidity();
|
||||||
|
pm2Value01 = pm2Value01 + ag.pms5003t_2.getPm01Ae();
|
||||||
|
pm2Value25 = pm2Value25 + ag.pms5003t_2.getPm25Ae();
|
||||||
|
pm2Value10 = pm2Value10 + ag.pms5003t_2.getPm10Ae();
|
||||||
|
pm2PCount = pm2PCount + ag.pms5003t_2.getPm03ParticleCount();
|
||||||
|
pm2temp = pm2temp + ag.pms5003t_2.getTemperature();
|
||||||
|
pm2hum = pm2hum + ag.pms5003t_2.getRelativeHumidity();
|
||||||
|
countPosition++;
|
||||||
|
if (countPosition == targetCount) {
|
||||||
|
pm01_1 = pm1Value01 / targetCount;
|
||||||
|
pm25_1 = pm1Value25 / targetCount;
|
||||||
|
pm10_1 = pm1Value10 / targetCount;
|
||||||
|
pm03PCount_1 = pm1PCount / targetCount;
|
||||||
|
temp_1 = pm1temp / targetCount;
|
||||||
|
hum_1 = pm1hum / targetCount;
|
||||||
|
pm01_2 = pm2Value01 / targetCount;
|
||||||
|
pm25_2 = pm2Value25 / targetCount;
|
||||||
|
pm10_2 = pm2Value10 / targetCount;
|
||||||
|
pm03PCount_2 = pm2PCount / targetCount;
|
||||||
|
temp_2 = pm2temp / targetCount;
|
||||||
|
hum_2 = pm2hum / targetCount;
|
||||||
|
|
||||||
|
countPosition = 0;
|
||||||
|
|
||||||
|
pm1Value01 = 0;
|
||||||
|
pm1Value25 = 0;
|
||||||
|
pm1Value10 = 0;
|
||||||
|
pm1PCount = 0;
|
||||||
|
pm1temp = 0;
|
||||||
|
pm1hum = 0;
|
||||||
|
pm2Value01 = 0;
|
||||||
|
pm2Value25 = 0;
|
||||||
|
pm2Value10 = 0;
|
||||||
|
pm2PCount = 0;
|
||||||
|
pm2temp = 0;
|
||||||
|
pm2hum = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void co2Poll(void) {
|
||||||
|
co2Ppm = ag.s8.getCo2();
|
||||||
|
Serial.printf("CO2 index: %d\r\n", co2Ppm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serverConfigPoll(void) {
|
||||||
|
if (agServer.pollServerConfig(getDevId())) {
|
||||||
|
/** Only support CO2 S8 sensor on FW_MODE_PST */
|
||||||
|
if (fw_mode == FW_MODE_PST) {
|
||||||
|
if (agServer.isCo2Calib()) {
|
||||||
|
co2Calibration();
|
||||||
|
}
|
||||||
|
if (agServer.getCo2Abccalib() > 0) {
|
||||||
|
if (ag.s8.setAutoCalib(agServer.getCo2Abccalib() * 24) == false) {
|
||||||
|
Serial.println("Set S8 auto calib failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getDevId(void) { return getNormalizedMac(); }
|
||||||
|
|
||||||
|
void ledBlinkDelay(uint32_t tdelay) {
|
||||||
|
ag.statusLed.setOn();
|
||||||
|
delay(tdelay);
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
delay(tdelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ledSmHandler(int sm) {
|
||||||
|
if (sm > APP_SM_NORMAL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ledSmState = sm;
|
||||||
|
switch (sm) {
|
||||||
|
case APP_SM_WIFI_MANAGER_MODE: {
|
||||||
|
ag.statusLed.setToggle();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APP_SM_WIFI_MAMAGER_PORTAL_ACTIVE: {
|
||||||
|
ag.statusLed.setOn();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APP_SM_WIFI_MANAGER_STA_CONNECTING: {
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APP_SM_WIFI_MANAGER_STA_CONNECTED: {
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APP_SM_WIFI_OK_SERVER_CONNECTING: {
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APP_SM_WIFI_OK_SERVER_CONNNECTED: {
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
|
||||||
|
/** two time slow blink, then off */
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
ledBlinkDelay(LED_SLOW_BLINK_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APP_SM_WIFI_MANAGER_CONNECT_FAILED: {
|
||||||
|
/** Three time fast blink then 2 sec pause. Repeat 3 times */
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
ledBlinkDelay(LED_FAST_BLINK_DELAY);
|
||||||
|
}
|
||||||
|
delay(2000);
|
||||||
|
}
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APP_SM_WIFI_OK_SERVER_CONNECT_FAILED: {
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
ledBlinkDelay(LED_FAST_BLINK_DELAY);
|
||||||
|
}
|
||||||
|
delay(2000);
|
||||||
|
}
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APP_SM_WIFI_OK_SERVER_OK_SENSOR_CONFIG_FAILED: {
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
ledBlinkDelay(LED_FAST_BLINK_DELAY);
|
||||||
|
}
|
||||||
|
delay(2000);
|
||||||
|
}
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APP_SM_WIFI_LOST: {
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APP_SM_SERVER_LOST: {
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APP_SM_SENSOR_CONFIG_FAILED: {
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APP_SM_NORMAL: {
|
||||||
|
ag.statusLed.setOff();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *getFwMode(int mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case FW_MODE_PST:
|
||||||
|
return "FW_MODE_PST";
|
||||||
|
case FW_MODE_PPT:
|
||||||
|
return "FW_MODE_PPT";
|
||||||
|
case FW_MODE_PP:
|
||||||
|
return "FW_MODE_PP";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "FW_MODE_UNKNOW";
|
||||||
|
}
|
@ -2,41 +2,47 @@
|
|||||||
This is sample code for the AirGradient library with a minimal implementation to read CO2 values from the SenseAir S8 sensor.
|
This is sample code for the AirGradient library with a minimal implementation to read CO2 values from the SenseAir S8 sensor.
|
||||||
|
|
||||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AirGradient.h>
|
#include <AirGradient.h>
|
||||||
|
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
AirGradient ag = AirGradient(BOARD_DIY_PRO_INDOOR_V4_2);
|
AirGradient ag = AirGradient(DIY_BASIC);
|
||||||
// AirGradient ag = AirGradient(BOARD_DIY_BASIC_KIT);
|
|
||||||
#else
|
#else
|
||||||
// AirGradient ag = AirGradient(BOARD_ONE_INDOOR_MONITOR_V9_0);
|
/** Create airgradient instance for 'OPEN_AIR_OUTDOOR' board */
|
||||||
AirGradient ag = AirGradient(BOARD_OUTDOOR_MONITOR_V1_3);
|
AirGradient ag = AirGradient(OPEN_AIR_OUTDOOR);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void failedHandler(String msg);
|
void failedHandler(String msg);
|
||||||
|
|
||||||
void setup() {
|
void setup()
|
||||||
|
{
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
|
||||||
/** Init CO2 sensor */
|
/** Init CO2 sensor */
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
if (ag.s8.begin(&Serial) == false) {
|
if (ag.s8.begin(&Serial) == false)
|
||||||
|
{
|
||||||
#else
|
#else
|
||||||
if (ag.s8.begin(Serial1) == false) {
|
if (ag.s8.begin(Serial1) == false)
|
||||||
|
{
|
||||||
#endif
|
#endif
|
||||||
failedHandler("SenseAir S8 init failed");
|
failedHandler("SenseAir S8 init failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop()
|
||||||
int CO2 = ag.s8.getCo2();
|
{
|
||||||
Serial.printf("CO2: %d\r\n", CO2);
|
int co2Ppm = ag.s8.getCo2();
|
||||||
|
Serial.printf("CO2: %d\r\n", co2Ppm);
|
||||||
delay(5000);
|
delay(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void failedHandler(String msg) {
|
void failedHandler(String msg)
|
||||||
while (true) {
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
Serial.println(msg);
|
Serial.println(msg);
|
||||||
delay(1000);
|
delay(1000);
|
||||||
}
|
}
|
||||||
|
@ -1,369 +0,0 @@
|
|||||||
#include <AirGradient.h>
|
|
||||||
#include <HardwareSerial.h>
|
|
||||||
#include <Wire.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AirGradient use ESP32C3 has default Serial0 use for PMS5003, to print log
|
|
||||||
* should use esp-hal-log instead.
|
|
||||||
*/
|
|
||||||
#include <esp32-hal-log.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Define test board
|
|
||||||
*/
|
|
||||||
#define TEST_BOARD_OUTDOOR_MONITOR_V1_3 0
|
|
||||||
#define TEST_BOARD_ONE_INDOOR_MONITOR_V9_0 1
|
|
||||||
/**
|
|
||||||
* @brief Define test sensor
|
|
||||||
*/
|
|
||||||
#define TEST_SENSOR_SenseAirS8 0
|
|
||||||
#define TEST_SENSOR_SHT4x 0
|
|
||||||
#define TEST_SENSOR_SGP4x 0
|
|
||||||
#define TEST_SWITCH 0
|
|
||||||
#define TEST_OLED 0
|
|
||||||
#if TEST_BOARD_OUTDOOR_MONITOR_V1_3
|
|
||||||
#define TEST_STATUS_LED 0
|
|
||||||
#define TEST_PMS5003T 1
|
|
||||||
#endif
|
|
||||||
#define TEST_WATCHDOG 1
|
|
||||||
|
|
||||||
#if TEST_BOARD_ONE_INDOOR_MONITOR_V9_0
|
|
||||||
#define TEST_LED_BAR 1
|
|
||||||
#define TEST_SENSOR_PMS5003 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_BOARD_OUTDOOR_MONITOR_V1_3
|
|
||||||
AirGradient ag(BOARD_OUTDOOR_MONITOR_V1_3);
|
|
||||||
#elif TEST_BOARD_ONE_INDOOR_MONITOR_V9_0
|
|
||||||
AirGradient ag(BOARD_ONE_INDOOR_MONITOR_V9_0);
|
|
||||||
#else
|
|
||||||
#error "Must enable board test
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
|
|
||||||
/** Print All AirGradient board define */
|
|
||||||
printBoardDef(NULL);
|
|
||||||
|
|
||||||
#if TEST_SENSOR_SenseAirS8
|
|
||||||
/** Cause Serial is use default for PMS, CO2S8 should be use Serial 1
|
|
||||||
* Serial 1 will be init by SenseAirS8 don't need to init any more on user
|
|
||||||
* code
|
|
||||||
*/
|
|
||||||
if (ag.s8.begin(Serial1)) {
|
|
||||||
log_i("CO2S8 sensor init success");
|
|
||||||
} else {
|
|
||||||
log_i("CO2S8 sensor init failure");
|
|
||||||
}
|
|
||||||
|
|
||||||
log_i("Start baseline calib");
|
|
||||||
if (ag.s8.setBaselineCalibration()) {
|
|
||||||
log_i("Calib success");
|
|
||||||
} else {
|
|
||||||
log_e("Calib failure");
|
|
||||||
}
|
|
||||||
delay(5000); // Wait for calib done
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_PMS5003
|
|
||||||
if (ag.pms5003.begin(Serial0)) {
|
|
||||||
log_i("PMS5003 sensor init success");
|
|
||||||
} else {
|
|
||||||
log_i("PMS5003 sensor init failure");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_PMS5003T
|
|
||||||
/**
|
|
||||||
* @brief PMS5003T_1 alway connect to Serial (esp32c3 RXD0, RXD0)
|
|
||||||
*/
|
|
||||||
if (ag.pms5003t_1.begin(Serial)) {
|
|
||||||
log_i("PMS5003T_1 sensor init success");
|
|
||||||
} else {
|
|
||||||
log_i("PMS5003T_1 sensor init failure");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Only test without senseair s8 because it's share the UART bus
|
|
||||||
#if TEST_SENSOR_SenseAirS8 == 0
|
|
||||||
if (ag.pms5003t_2.begin(Serial1)) {
|
|
||||||
log_i("PMS5003T_2 sensor init success");
|
|
||||||
} else {
|
|
||||||
log_i("PMS5003T_2 sensor init failure");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_SHT4x || TEST_SENSOR_SGP4x || TEST_OLED
|
|
||||||
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_SHT4x
|
|
||||||
|
|
||||||
if (ag.sht.begin(Wire)) {
|
|
||||||
log_i("SHT init success");
|
|
||||||
} else {
|
|
||||||
log_i("SHT init failed");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_SGP4x
|
|
||||||
if (ag.sgp41.begin(Wire)) {
|
|
||||||
log_i("SGP init success");
|
|
||||||
} else {
|
|
||||||
log_e("SGP init failure");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_LED
|
|
||||||
led.begin();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SWITCH
|
|
||||||
ag.button.begin();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_OLED
|
|
||||||
ag.display.begin(Wire);
|
|
||||||
ag.display.setTextSize(1);
|
|
||||||
ag.display.setCursor(0, 0);
|
|
||||||
ag.display.setTextColor(1);
|
|
||||||
ag.display.setText("180s to connect to wifi hostpost AC-xxxxx");
|
|
||||||
ag.display.show();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_STATUS_LED
|
|
||||||
ag.statusLed.begin();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_LED_BAR
|
|
||||||
ag.ledBar.begin();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_WATCHDOG
|
|
||||||
ag.watchdog.begin();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
uint32_t ms;
|
|
||||||
#if TEST_SENSOR_SenseAirS8
|
|
||||||
static uint32_t lastTime = 0;
|
|
||||||
|
|
||||||
/** Wait for sensor ready */
|
|
||||||
ms = (uint32_t)(millis() - lastTime);
|
|
||||||
if (ms >= 1000) {
|
|
||||||
lastTime = millis();
|
|
||||||
log_i("CO2: %d (PPM)", ag.s8.getCo2());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_PMS5003
|
|
||||||
static uint32_t pmsTime = 0;
|
|
||||||
ms = (uint32_t)(millis() - pmsTime);
|
|
||||||
|
|
||||||
if (ms >= 1000) {
|
|
||||||
pmsTime = millis();
|
|
||||||
if (ag.pms5003.readData()) {
|
|
||||||
log_i("Passive mode PM 1.0 (ug/m3): %d", ag.pms5003.getPm10Ae());
|
|
||||||
log_i("Passive mode PM 2.5 (ug/m3): %d", ag.pms5003.getPm25Ae());
|
|
||||||
log_i("Passive mode PM 10.0 (ug/m3): %d", ag.pms5003.getPm10Ae());
|
|
||||||
} else {
|
|
||||||
log_i("PMS sensor read failure");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_PMS5003T
|
|
||||||
static uint32_t pmsTime = 0;
|
|
||||||
ms = (uint32_t)(millis() - pmsTime);
|
|
||||||
|
|
||||||
if (ms >= 1000) {
|
|
||||||
pmsTime = millis();
|
|
||||||
if (ag.pms5003t_1.readData()) {
|
|
||||||
log_i("PMS5003_1 PM 1.0 (ug/m3): %d", ag.pms5003t_1.getPm10Ae());
|
|
||||||
log_i("PMS5003_1 PM 2.5 (ug/m3): %d", ag.pms5003t_1.getPm25Ae());
|
|
||||||
log_i("PMS5003_1 PM 10.0 (ug/m3): %d", ag.pms5003t_1.getPm10Ae());
|
|
||||||
log_i("PMS5003_1 PM 3.0 (ug/m3): %d",
|
|
||||||
ag.pms5003t_1.getPm03ParticleCount());
|
|
||||||
log_i("Temperature : %02f °C",
|
|
||||||
ag.pms5003t_1.getTemperature());
|
|
||||||
log_i("Humidity : %02f %%",
|
|
||||||
ag.pms5003t_1.getRelativeHumidity());
|
|
||||||
} else {
|
|
||||||
log_i("PMS5003_1 sensor read failure");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ag.pms5003t_2.readData()) {
|
|
||||||
log_i("PMS5003_2 PM 1.0 (ug/m3): %d", ag.pms5003t_2.getPm10Ae());
|
|
||||||
log_i("PMS5003_2 PM 2.5 (ug/m3): %d", ag.pms5003t_2.getPm25Ae());
|
|
||||||
log_i("PMS5003_2 PM 10.0 (ug/m3): %d", ag.pms5003t_2.getPm10Ae());
|
|
||||||
log_i("PMS5003_2 PM 3.0 (ug/m3): %d",
|
|
||||||
ag.pms5003t_2.getPm03ParticleCount());
|
|
||||||
// log_i("Temperature : %02f °C",
|
|
||||||
// ag.pms5003t_1.getTemperature());
|
|
||||||
// log_i("Humidity : %02f %%",
|
|
||||||
// ag.pms5003t_1.getRelativeHumidity());
|
|
||||||
} else {
|
|
||||||
log_i("PMS5003_2 sensor read failure");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_SHT4x
|
|
||||||
/**
|
|
||||||
* @brief Get SHT sensor data each 1sec
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static uint32_t shtTime = 0;
|
|
||||||
ms = (uint32_t)(millis() - shtTime);
|
|
||||||
if (ms >= 1000) {
|
|
||||||
shtTime = millis();
|
|
||||||
log_i("Get sht temperature: %0.2f (degree celsius)",
|
|
||||||
ag.sht.getTemperature());
|
|
||||||
log_i("Get sht temperature: %0.2f (%%)", ag.sht.getRelativeHumidity());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_SGP4x
|
|
||||||
static uint32_t sgpTime;
|
|
||||||
ms = (uint32_t)(millis() - sgpTime);
|
|
||||||
if (ms >= 1000) {
|
|
||||||
sgpTime = millis();
|
|
||||||
uint16_t rawVOC;
|
|
||||||
log_i("Get TVOC: %d", ag.sgp41.getTvocIndex());
|
|
||||||
log_i("Get NOx: %d", ag.sgp41.getNoxIndex());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_LED
|
|
||||||
static uint32_t ledTime;
|
|
||||||
#if TEST_BOARD_OUTDOOR_MONITOR_V1_3
|
|
||||||
// ms = (uint32_t)(millis() - ledTime);
|
|
||||||
// if(ms >= 500)
|
|
||||||
// {
|
|
||||||
// ledTime = millis();
|
|
||||||
// led.ledToggle();
|
|
||||||
// }
|
|
||||||
#elif TEST_BOARD_ONE_INDOOR_MONITOR_V9_0
|
|
||||||
|
|
||||||
static int ledIndex;
|
|
||||||
static int ledIndexOld;
|
|
||||||
ms = (uint32_t)(millis() - ledTime);
|
|
||||||
if (ms >= 50) {
|
|
||||||
ledTime = millis();
|
|
||||||
if (ledIndex == ledIndexOld) {
|
|
||||||
led.ledOff();
|
|
||||||
} else {
|
|
||||||
// Turn last LED off
|
|
||||||
led.ledSetColor(0, 0, 0, ledIndexOld);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn new led ON
|
|
||||||
led.ledSetColor(255, 0, 0, ledIndex);
|
|
||||||
ledIndexOld = ledIndex;
|
|
||||||
ledIndex++;
|
|
||||||
if (ledIndex >= led.getNumberOfLed()) {
|
|
||||||
ledIndex = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SWITCH
|
|
||||||
static PushButton::State stateOld = PushButton::State::BUTTON_RELEASED;
|
|
||||||
PushButton::State state = ag.button.getState();
|
|
||||||
if (state != stateOld) {
|
|
||||||
stateOld = state;
|
|
||||||
log_i("Button state changed: %s", ag.button.toString(state).c_str());
|
|
||||||
if (state == PushButton::State::BUTTON_PRESSED) {
|
|
||||||
ag.statusLed.setOn();
|
|
||||||
} else {
|
|
||||||
ag.statusLed.setOff();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_LED_BAR
|
|
||||||
static uint32_t ledTime;
|
|
||||||
static uint8_t ledNum = 0;
|
|
||||||
static uint8_t ledIndex = 0;
|
|
||||||
static uint8_t ledStep = 0;
|
|
||||||
static bool ledOn = false;
|
|
||||||
|
|
||||||
if (ledNum == 0) {
|
|
||||||
ledNum = ag.ledBar.getNumberOfLed();
|
|
||||||
log_i("Get number of led: %d", ledNum);
|
|
||||||
if (ledNum) {
|
|
||||||
ag.ledBar.setBrighness(0xff);
|
|
||||||
for (int i = 0; i < ledNum; i++) {
|
|
||||||
// ag.ledBar.setColor(0xff, 0xff, 0xff, i);
|
|
||||||
// ag.ledBar.setColor(204, 136, 153, i);
|
|
||||||
// ag.ledBar.setColor(204, 0, 0, i);
|
|
||||||
// ag.ledBar.setColor(204, 100, 153, i);
|
|
||||||
ag.ledBar.setColor(0, 136, 255, i);
|
|
||||||
}
|
|
||||||
ag.ledBar.show();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ms = (uint32_t)(millis() - ledTime);
|
|
||||||
if (ms >= 500) {
|
|
||||||
ledTime = millis();
|
|
||||||
switch (ledStep) {
|
|
||||||
case 0: {
|
|
||||||
ag.ledBar.setColor(255, 0, 0, ledIndex);
|
|
||||||
ledIndex++;
|
|
||||||
if (ledIndex >= ledNum) {
|
|
||||||
ag.ledBar.setColor(0, 0, 0);
|
|
||||||
ledIndex = 0;
|
|
||||||
ledStep = 1;
|
|
||||||
}
|
|
||||||
ag.ledBar.show();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
ledIndex++;
|
|
||||||
if (ledIndex >= ledNum) {
|
|
||||||
ag.ledBar.setColor(255, 0, 0);
|
|
||||||
ag.ledBar.show();
|
|
||||||
ledIndex = ledNum - 1;
|
|
||||||
ledStep = 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2: {
|
|
||||||
if (ledOn) {
|
|
||||||
ag.ledBar.setColor(255, 0, 0);
|
|
||||||
} else {
|
|
||||||
ag.ledBar.setColor(0, 0, 0);
|
|
||||||
}
|
|
||||||
ledOn = !ledOn;
|
|
||||||
ag.ledBar.show();
|
|
||||||
|
|
||||||
ledIndex--;
|
|
||||||
if (ledIndex == 0) {
|
|
||||||
ag.ledBar.setColor(0, 0, 0);
|
|
||||||
ag.ledBar.show();
|
|
||||||
ledStep = 0;
|
|
||||||
ledIndex = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_WATCHDOG
|
|
||||||
static uint32_t wdgTime;
|
|
||||||
ms = (uint32_t)(millis() - wdgTime);
|
|
||||||
if (ms >= (1000 * 60)) {
|
|
||||||
wdgTime = millis();
|
|
||||||
/** Reset watchdog reach 1 minutes */
|
|
||||||
ag.watchdog.reset();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
@ -1,164 +0,0 @@
|
|||||||
#include <AirGradient.h>
|
|
||||||
#include <Wire.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Define test board
|
|
||||||
*/
|
|
||||||
#define TEST_BOARD_DIY_BASIC_KIT 0
|
|
||||||
#define TEST_BOARD_DIY_PRO_INDOOR_V4_2 1
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Define test sensor
|
|
||||||
*/
|
|
||||||
#define TEST_SENSOR_SenseAirS8 0
|
|
||||||
#define TEST_SENSOR_PMS5003 0
|
|
||||||
#define TEST_SENSOR_SHT4x 0
|
|
||||||
#define TEST_SENSOR_SGP4x 1
|
|
||||||
#define TEST_SWITCH 0
|
|
||||||
#define TEST_OLED 0
|
|
||||||
|
|
||||||
#if TEST_BOARD_DIY_BASIC_KIT
|
|
||||||
AirGradient ag(BOARD_DIY_BASIC_KIT);
|
|
||||||
#elif TEST_BOARD_DIY_PRO_INDOOR_V4_2
|
|
||||||
AirGradient ag(BOARD_DIY_PRO_INDOOR_V4_2);
|
|
||||||
#else
|
|
||||||
#error "Board test not defined"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
|
|
||||||
/** Print All AirGradient board define */
|
|
||||||
printBoardDef(&Serial);
|
|
||||||
|
|
||||||
#if TEST_SENSOR_SenseAirS8
|
|
||||||
if (ag.s8.begin(&Serial) == true) {
|
|
||||||
Serial.println("CO2S8 sensor init success");
|
|
||||||
} else {
|
|
||||||
Serial.println("CO2S8 sensor init failure");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ag.s8.setBaselineCalibration()) {
|
|
||||||
Serial.println("Manual calib success");
|
|
||||||
} else {
|
|
||||||
Serial.println("Manual calib failure");
|
|
||||||
}
|
|
||||||
delay(5000);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_PMS5003
|
|
||||||
if (ag.pms5003.begin(&Serial) == true) {
|
|
||||||
Serial.println("PMS5003 sensor init success");
|
|
||||||
} else {
|
|
||||||
Serial.println("PMS5003 sensor init failure");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_SHT4x || TEST_SENSOR_SGP4x || TEST_OLED
|
|
||||||
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_SHT4x
|
|
||||||
if (ag.sht.begin(Wire, Serial)) {
|
|
||||||
Serial.println("SHT init success");
|
|
||||||
} else {
|
|
||||||
Serial.println("SHT init failed");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_SGP4x
|
|
||||||
if (ag.sgp41.begin(Wire, Serial)) {
|
|
||||||
Serial.println("SGP init succses");
|
|
||||||
} else {
|
|
||||||
Serial.println("SGP init failure");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SWITCH
|
|
||||||
ag.button.begin(Serial);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_OLED
|
|
||||||
ag.display.begin(Wire, Serial);
|
|
||||||
ag.display.setTextSize(1);
|
|
||||||
ag.display.setCursor(0, 0);
|
|
||||||
ag.display.setTextColor(1);
|
|
||||||
ag.display.setText("Hello");
|
|
||||||
ag.display.show();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
uint32_t ms;
|
|
||||||
#if TEST_SENSOR_SenseAirS8
|
|
||||||
static uint32_t lastTime = 0;
|
|
||||||
|
|
||||||
/** Wait for sensor ready */
|
|
||||||
// if(co2s8.isReady())
|
|
||||||
// {
|
|
||||||
// Get sensor data each 1sec
|
|
||||||
ms = (uint32_t)(millis() - lastTime);
|
|
||||||
if (ms >= 1000) {
|
|
||||||
lastTime = millis();
|
|
||||||
Serial.printf("CO2: %d (PMM)\r\n", ag.s8.getCo2());
|
|
||||||
}
|
|
||||||
// }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_PMS5003
|
|
||||||
static uint32_t pmsTime = 0;
|
|
||||||
ms = (uint32_t)(millis() - pmsTime);
|
|
||||||
if (ms >= 1000) {
|
|
||||||
pmsTime = millis();
|
|
||||||
if (ag.pms5003.readData()) {
|
|
||||||
Serial.printf("Passive mode PM 1.0 (ug/m3): %d\r\n",
|
|
||||||
ag.pms5003.getPm01Ae());
|
|
||||||
Serial.printf("Passive mode PM 2.5 (ug/m3): %d\r\n",
|
|
||||||
ag.pms5003.getPm25Ae());
|
|
||||||
Serial.printf("Passive mode PM 10.5 (ug/m3): %d\r\n",
|
|
||||||
ag.pms5003.getPm10Ae());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_SHT4x
|
|
||||||
/**
|
|
||||||
* @brief Get SHT sensor data each 1sec
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static uint32_t shtTime = 0;
|
|
||||||
ms = (uint32_t)(millis() - shtTime);
|
|
||||||
if (ms >= 1000) {
|
|
||||||
shtTime = millis();
|
|
||||||
float temperature, humidity;
|
|
||||||
Serial.printf("SHT Temperature: %f, Humidity: %f\r\n",
|
|
||||||
ag.sht.getTemperature(), ag.sht.getRelativeHumidity());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SENSOR_SGP4x
|
|
||||||
static uint32_t sgpTime;
|
|
||||||
ms = (uint32_t)(millis() - sgpTime);
|
|
||||||
|
|
||||||
/***
|
|
||||||
* Must call this task on loop and avoid delay on loop over 1000 ms
|
|
||||||
*/
|
|
||||||
ag.sgp41.handle();
|
|
||||||
|
|
||||||
if (ms >= 1000) {
|
|
||||||
sgpTime = millis();
|
|
||||||
Serial.printf("SGP TVOC: %d, NOx: %d\r\n", ag.sgp41.getTvocIndex(),
|
|
||||||
ag.sgp41.getNoxIndex());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TEST_SWITCH
|
|
||||||
static PushButton::State stateOld = PushButton::State::BUTTON_RELEASED;
|
|
||||||
PushButton::State state = ag.button.getState();
|
|
||||||
if (state != stateOld) {
|
|
||||||
stateOld = state;
|
|
||||||
Serial.printf("Button state changed: %s\r\n",
|
|
||||||
ag.button.toString(state).c_str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This is sample code for the AirGradient library with a minimal implementation to read PM values from the Plantower sensor.
|
This is sample code for the AirGradient library with a minimal implementation
|
||||||
|
to read PM values from the Plantower sensor.
|
||||||
|
|
||||||
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
||||||
*/
|
*/
|
||||||
@ -7,11 +8,10 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
|
|||||||
#include <AirGradient.h>
|
#include <AirGradient.h>
|
||||||
|
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
AirGradient ag = AirGradient(BOARD_DIY_PRO_INDOOR_V4_2);
|
AirGradient ag = AirGradient(DIY_BASIC);
|
||||||
// AirGradient ag = AirGradient(BOARD_DIY_BASIC_KIT);
|
|
||||||
#else
|
#else
|
||||||
// AirGradient ag = AirGradient(BOARD_ONE_INDOOR_MONITOR_V9_0);
|
// AirGradient ag = AirGradient(ONE_INDOOR);
|
||||||
AirGradient ag = AirGradient(BOARD_OUTDOOR_MONITOR_V1_3);
|
AirGradient ag = AirGradient(OPEN_AIR_OUTDOOR);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void failedHandler(String msg);
|
void failedHandler(String msg);
|
||||||
@ -19,16 +19,16 @@ void failedHandler(String msg);
|
|||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
if(ag.pms5003.begin(&Serial) == false) {
|
if (ag.pms5003.begin(&Serial) == false) {
|
||||||
failedHandler("Init PMS5003 failed");
|
failedHandler("Init PMS5003 failed");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (ag.getBoardType() == BOARD_OUTDOOR_MONITOR_V1_3) {
|
if (ag.getBoardType() == OPEN_AIR_OUTDOOR) {
|
||||||
if(ag.pms5003t_1.begin(Serial0) == false) {
|
if (ag.pms5003t_1.begin(Serial0) == false) {
|
||||||
failedHandler("Init PMS5003T failed");
|
failedHandler("Init PMS5003T failed");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(ag.pms5003.begin(Serial0) == false) {
|
if (ag.pms5003.begin(Serial0) == false) {
|
||||||
failedHandler("Init PMS5003T failed");
|
failedHandler("Init PMS5003T failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,25 +37,37 @@ void setup() {
|
|||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
int PM2;
|
int PM2;
|
||||||
|
bool readResul = false;
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
|
if (ag.pms5003.readData()) {
|
||||||
PM2 = ag.pms5003.getPm25Ae();
|
PM2 = ag.pms5003.getPm25Ae();
|
||||||
Serial.printf("PM2.5 in ug/m3: %d\r\n", PM2);
|
Serial.printf("PM2.5 in ug/m3: %d\r\n", PM2);
|
||||||
Serial.printf("PM2.5 in US AQI: %d\r\n", ag.pms5003.convertPm25ToUsAqi(PM2));
|
Serial.printf("PM2.5 in US AQI: %d\r\n",
|
||||||
|
ag.pms5003.convertPm25ToUsAqi(PM2));
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
if (ag.getBoardType() == BOARD_OUTDOOR_MONITOR_V1_3) {
|
if (ag.getBoardType() == OPEN_AIR_OUTDOOR) {
|
||||||
|
if (ag.pms5003t_1.readData()) {
|
||||||
PM2 = ag.pms5003t_1.getPm25Ae();
|
PM2 = ag.pms5003t_1.getPm25Ae();
|
||||||
|
readResul = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (ag.pms5003.readData()) {
|
||||||
PM2 = ag.pms5003.getPm25Ae();
|
PM2 = ag.pms5003.getPm25Ae();
|
||||||
|
readResul = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (readResul) {
|
||||||
Serial.printf("PM2.5 in ug/m3: %d\r\n", PM2);
|
Serial.printf("PM2.5 in ug/m3: %d\r\n", PM2);
|
||||||
if (ag.getBoardType() == BOARD_OUTDOOR_MONITOR_V1_3) {
|
if (ag.getBoardType() == OPEN_AIR_OUTDOOR) {
|
||||||
Serial.printf("PM2.5 in US AQI: %d\r\n",
|
Serial.printf("PM2.5 in US AQI: %d\r\n",
|
||||||
ag.pms5003t_1.convertPm25ToUsAqi(PM2));
|
ag.pms5003t_1.convertPm25ToUsAqi(PM2));
|
||||||
} else {
|
} else {
|
||||||
Serial.printf("PM2.5 in US AQI: %d\r\n",
|
Serial.printf("PM2.5 in US AQI: %d\r\n",
|
||||||
ag.pms5003.convertPm25ToUsAqi(PM2));
|
ag.pms5003.convertPm25ToUsAqi(PM2));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
delay(5000);
|
delay(5000);
|
||||||
|
39
examples/TestSht/TestSht.ino
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include <AirGradient.h>
|
||||||
|
|
||||||
|
#if defined(ESP8266)
|
||||||
|
AirGradient ag(DIY_BASIC);
|
||||||
|
#else
|
||||||
|
AirGradient ag(ONE_INDOOR);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void failedHandler(String msg);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("Hello");
|
||||||
|
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
if (ag.sht.begin(Wire) == false) {
|
||||||
|
failedHandler("SHT init failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (ag.sht.measure()) {
|
||||||
|
float hum = ag.sht.getRelativeHumidity();
|
||||||
|
float temp = ag.sht.getTemperature();
|
||||||
|
Serial.printf("Get temperature: %f\r\n", temp);
|
||||||
|
Serial.printf(" Get humidity: %f\r\n", hum);
|
||||||
|
} else {
|
||||||
|
Serial.println("Measure failed");
|
||||||
|
}
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void failedHandler(String msg) {
|
||||||
|
while (true) {
|
||||||
|
Serial.println(msg);
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
name=AirGradient Air Quality Sensor
|
name=AirGradient Air Quality Sensor
|
||||||
version=3.0.0
|
version=3.0.3
|
||||||
author=AirGradient <support@airgradient.com>
|
author=AirGradient <support@airgradient.com>
|
||||||
maintainer=AirGradient <support@airgradient.com>
|
maintainer=AirGradient <support@airgradient.com>
|
||||||
sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display.
|
sentence=ESP32-C3 / ESP8266 library for air quality monitor measuring PM, CO2, Temperature, TVOC and Humidity with OLED display.
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
#include "AirGradient.h"
|
#include "AirGradient.h"
|
||||||
|
|
||||||
#define AG_LIB_VER "3.0.0"
|
#define AG_LIB_VER "3.0.3"
|
||||||
|
|
||||||
AirGradient::AirGradient(BoardType type)
|
AirGradient::AirGradient(BoardType type)
|
||||||
: pms5003(type), pms5003t_1(type), pms5003t_2(type), s8(type), sht(type), sgp41(type),
|
: pms5003(type), pms5003t_1(type), pms5003t_2(type), s8(type), sgp41(type),
|
||||||
display(type), boardType(type), button(type), statusLed(type),
|
display(type), boardType(type), button(type), statusLed(type),
|
||||||
ledBar(type), watchdog(type) {}
|
ledBar(type), watchdog(type), sht(type) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get pin number for I2C SDA
|
* @brief Get pin number for I2C SDA
|
||||||
|
@ -1,18 +1,22 @@
|
|||||||
#ifndef _AIR_GRADIENT_H_
|
#ifndef _AIR_GRADIENT_H_
|
||||||
#define _AIR_GRADIENT_H_
|
#define _AIR_GRADIENT_H_
|
||||||
|
|
||||||
#include "bsp/BoardDef.h"
|
|
||||||
#include "bsp/LedBar.h"
|
|
||||||
#include "bsp/PushButton.h"
|
|
||||||
#include "bsp/StatusLed.h"
|
|
||||||
#include "bsp/HardwareWatchdog.h"
|
|
||||||
#include "co2/s8.h"
|
|
||||||
#include "display/oled.h"
|
#include "display/oled.h"
|
||||||
#include "pm/pms5003.h"
|
#include "main/BoardDef.h"
|
||||||
#include "pm/pms5003t.h"
|
#include "main/HardwareWatchdog.h"
|
||||||
#include "sgp/sgp41.h"
|
#include "main/LedBar.h"
|
||||||
#include "sht/sht4x.h"
|
#include "main/PushButton.h"
|
||||||
|
#include "main/StatusLed.h"
|
||||||
|
#include "pms/pms5003.h"
|
||||||
|
#include "pms/pms5003t.h"
|
||||||
|
#include "s8/s8.h"
|
||||||
|
#include "sgp41/sgp41.h"
|
||||||
|
#include "sht/sht.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Class with define all the sensor has supported by Airgradient. Each
|
||||||
|
* sensor usage must be init before use.
|
||||||
|
*/
|
||||||
class AirGradient {
|
class AirGradient {
|
||||||
public:
|
public:
|
||||||
AirGradient(BoardType type);
|
AirGradient(BoardType type);
|
||||||
@ -21,7 +25,15 @@ public:
|
|||||||
* @brief Plantower PMS5003 sensor
|
* @brief Plantower PMS5003 sensor
|
||||||
*/
|
*/
|
||||||
PMS5003 pms5003;
|
PMS5003 pms5003;
|
||||||
|
/**
|
||||||
|
* @brief Plantower PMS5003T sensor: connect to PM1 connector on
|
||||||
|
* OPEN_AIR_OUTDOOR.
|
||||||
|
*/
|
||||||
PMS5003T pms5003t_1;
|
PMS5003T pms5003t_1;
|
||||||
|
/**
|
||||||
|
* @brief Plantower PMS5003T sensor: connect to PM2 connector on
|
||||||
|
* OPEN_AIR_OUTDOOR.
|
||||||
|
*/
|
||||||
PMS5003T pms5003t_2;
|
PMS5003T pms5003t_2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,18 +42,19 @@ public:
|
|||||||
S8 s8;
|
S8 s8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Temperature and humidity sensor
|
* @brief Temperature and humidity sensor supported SHT3x and SHT4x
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
Sht sht;
|
Sht sht;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief TVOC and NOx sensor
|
* @brief SGP41 TVOC and NOx sensor
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Sgp41 sgp41;
|
Sgp41 sgp41;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Display
|
* @brief OLED Display
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Display display;
|
Display display;
|
||||||
@ -55,18 +68,43 @@ public:
|
|||||||
* @brief LED
|
* @brief LED
|
||||||
*/
|
*/
|
||||||
StatusLed statusLed;
|
StatusLed statusLed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief RGB LED array
|
||||||
|
*
|
||||||
|
*/
|
||||||
LedBar ledBar;
|
LedBar ledBar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Hardware watchdog
|
* @brief External hardware watchdog
|
||||||
*/
|
*/
|
||||||
HardwareWatchdog watchdog;
|
HardwareWatchdog watchdog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get I2C SDA pin has of board supported
|
||||||
|
*
|
||||||
|
* @return int Pin number if -1 invalid
|
||||||
|
*/
|
||||||
int getI2cSdaPin(void);
|
int getI2cSdaPin(void);
|
||||||
|
/**
|
||||||
|
* @brief Get I2C SCL pin has of board supported
|
||||||
|
*
|
||||||
|
* @return int Pin number if -1 invalid
|
||||||
|
*/
|
||||||
int getI2cSclPin(void);
|
int getI2cSclPin(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the Board Type
|
||||||
|
*
|
||||||
|
* @return BoardType @ref BoardType
|
||||||
|
*/
|
||||||
BoardType getBoardType(void);
|
BoardType getBoardType(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the library version string
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
String getVersion(void);
|
String getVersion(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include "../library/Adafruit_SSD1306_Wemos_OLED/Adafruit_SSD1306.h"
|
#include "../library/Adafruit_SSD1306_Wemos_OLED/Adafruit_SSD1306.h"
|
||||||
|
|
||||||
#define disp(func) \
|
#define disp(func) \
|
||||||
if (this->_boardType == BOARD_DIY_BASIC_KIT) { \
|
if (this->_boardType == DIY_BASIC) { \
|
||||||
((Adafruit_SSD1306 *)(this->oled))->func; \
|
((Adafruit_SSD1306 *)(this->oled))->func; \
|
||||||
} else { \
|
} else { \
|
||||||
((Adafruit_SH110X *)(this->oled))->func; \
|
((Adafruit_SH110X *)(this->oled))->func; \
|
||||||
@ -19,7 +19,18 @@ void Display::begin(TwoWire &wire, Stream &debugStream) {
|
|||||||
|
|
||||||
Display::Display(BoardType type) : _boardType(type) {}
|
Display::Display(BoardType type) : _boardType(type) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize display, should be call this function before call of ther,
|
||||||
|
* if not it's always return failure.
|
||||||
|
*
|
||||||
|
* @param wire TwoWire instance, Must be initialized
|
||||||
|
*/
|
||||||
void Display::begin(TwoWire &wire) {
|
void Display::begin(TwoWire &wire) {
|
||||||
|
if (_isBegin) {
|
||||||
|
AgLog("Initialized, call end() then try again");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this->_bsp = getBoardDef(this->_boardType);
|
this->_bsp = getBoardDef(this->_boardType);
|
||||||
if ((this->_bsp == nullptr) || (this->_bsp->I2C.supported == false) ||
|
if ((this->_bsp == nullptr) || (this->_bsp->I2C.supported == false) ||
|
||||||
(this->_bsp->OLED.supported == false)) {
|
(this->_bsp->OLED.supported == false)) {
|
||||||
@ -28,67 +39,106 @@ void Display::begin(TwoWire &wire) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Init OLED */
|
/** Init OLED */
|
||||||
if (this->_boardType == BOARD_DIY_BASIC_KIT) {
|
if (this->_boardType == DIY_BASIC) {
|
||||||
AgLog("Init Adafruit_SSD1306");
|
AgLog("Init Adafruit_SSD1306");
|
||||||
Adafruit_SSD1306 *_oled = new Adafruit_SSD1306();
|
Adafruit_SSD1306 *_oled = new Adafruit_SSD1306();
|
||||||
_oled->begin(wire, SSD1306_SWITCHCAPVCC, this->_bsp->OLED.addr);
|
_oled->begin(wire, SSD1306_SWITCHCAPVCC, this->_bsp->OLED.addr);
|
||||||
this->oled = _oled;
|
this->oled = _oled;
|
||||||
} else {
|
} else {
|
||||||
AgLog("Init Adafruit_SH1106G");
|
AgLog("Init Adafruit_SH1106G");
|
||||||
Adafruit_SH1106G *_oled = new Adafruit_SH1106G(this->_bsp->OLED.width, this->_bsp->OLED.height, &wire);
|
Adafruit_SH1106G *_oled = new Adafruit_SH1106G(
|
||||||
|
this->_bsp->OLED.width, this->_bsp->OLED.height, &wire);
|
||||||
_oled->begin(this->_bsp->OLED.addr, false);
|
_oled->begin(this->_bsp->OLED.addr, false);
|
||||||
this->oled = _oled;
|
this->oled = _oled;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->_isInit = true;
|
this->_isBegin = true;
|
||||||
disp(clearDisplay());
|
disp(clearDisplay());
|
||||||
AgLog("Init");
|
AgLog("Initialize");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear display buffer
|
||||||
|
*
|
||||||
|
*/
|
||||||
void Display::clear(void) {
|
void Display::clear(void) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(clearDisplay());
|
disp(clearDisplay());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Invert display color
|
||||||
|
*
|
||||||
|
* @param i 0: black, other is white
|
||||||
|
*/
|
||||||
void Display::invertDisplay(uint8_t i) {
|
void Display::invertDisplay(uint8_t i) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(invertDisplay(i));
|
disp(invertDisplay(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send display frame buffer to OLED
|
||||||
|
*
|
||||||
|
*/
|
||||||
void Display::show() {
|
void Display::show() {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(display());
|
disp(display());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set display contract
|
||||||
|
*
|
||||||
|
* @param value Contract (0;255);
|
||||||
|
*/
|
||||||
void Display::setContrast(uint8_t value) {
|
void Display::setContrast(uint8_t value) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(setContrast(value));
|
disp(setContrast(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draw pixel into display frame buffer, call show to draw to
|
||||||
|
* display(OLED)
|
||||||
|
*
|
||||||
|
* @param x X Position
|
||||||
|
* @param y Y Position
|
||||||
|
* @param color Color (0: black, other white)
|
||||||
|
*/
|
||||||
void Display::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
void Display::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(drawPixel(x, y, color));
|
disp(drawPixel(x, y, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set text size, it's scale default font instead of point to multiple
|
||||||
|
* font has define for special size
|
||||||
|
*
|
||||||
|
* @param size Size of text (default = 1)
|
||||||
|
*/
|
||||||
void Display::setTextSize(int size) {
|
void Display::setTextSize(int size) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(setTextSize(size));
|
disp(setTextSize(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move draw cursor into new position
|
||||||
|
*
|
||||||
|
* @param x X Position
|
||||||
|
* @param y Y Position
|
||||||
|
*/
|
||||||
void Display::setCursor(int16_t x, int16_t y) {
|
void Display::setCursor(int16_t x, int16_t y) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(setCursor(x, y));
|
disp(setCursor(x, y));
|
||||||
@ -100,7 +150,7 @@ void Display::setCursor(int16_t x, int16_t y) {
|
|||||||
* @param color 0:black, 1: While
|
* @param color 0:black, 1: While
|
||||||
*/
|
*/
|
||||||
void Display::setTextColor(uint16_t color) {
|
void Display::setTextColor(uint16_t color) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(setTextColor(color));
|
disp(setTextColor(color));
|
||||||
@ -113,66 +163,120 @@ void Display::setTextColor(uint16_t color) {
|
|||||||
* @param backGroundColor Text background color
|
* @param backGroundColor Text background color
|
||||||
*/
|
*/
|
||||||
void Display::setTextColor(uint16_t foreGroundColor, uint16_t backGroundColor) {
|
void Display::setTextColor(uint16_t foreGroundColor, uint16_t backGroundColor) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(setTextColor(foreGroundColor, backGroundColor));
|
disp(setTextColor(foreGroundColor, backGroundColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draw text to display framebuffer, call show() to draw to display
|
||||||
|
* (OLED)
|
||||||
|
*
|
||||||
|
* @param text String
|
||||||
|
*/
|
||||||
void Display::setText(String text) {
|
void Display::setText(String text) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(print(text));
|
disp(print(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draw bitmap into display framebuffer, call show() to draw to display
|
||||||
|
* (OLED)
|
||||||
|
*
|
||||||
|
* @param x X Position
|
||||||
|
* @param y Y Position
|
||||||
|
* @param bitmap Bitmap buffer
|
||||||
|
* @param w Bitmap width
|
||||||
|
* @param h Bitmap hight
|
||||||
|
* @param color Bitmap color
|
||||||
|
*/
|
||||||
void Display::drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
|
void Display::drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
|
||||||
int16_t w, int16_t h, uint16_t color) {
|
int16_t w, int16_t h, uint16_t color) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(drawBitmap(x, y, bitmap, w, h, color));
|
disp(drawBitmap(x, y, bitmap, w, h, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set text to display framebuffer, call show() to draw into to display
|
||||||
|
* (OLED)
|
||||||
|
*
|
||||||
|
* @param text Character buffer
|
||||||
|
*/
|
||||||
void Display::setText(const char text[]) {
|
void Display::setText(const char text[]) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(print(text));
|
disp(print(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draw line to display framebuffer, call show() to draw to
|
||||||
|
* display(OLED)
|
||||||
|
*
|
||||||
|
* @param x0 Start X position
|
||||||
|
* @param y0 Start Y position
|
||||||
|
* @param x1 End X Position
|
||||||
|
* @param y1 End Y Position
|
||||||
|
* @param color Color (0: black, otherwise white)
|
||||||
|
*/
|
||||||
void Display::drawLine(int x0, int y0, int x1, int y1, uint16_t color) {
|
void Display::drawLine(int x0, int y0, int x1, int y1, uint16_t color) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(drawLine(x0, y0, x1, y1, color));
|
disp(drawLine(x0, y0, x1, y1, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draw circle to display framebuffer,
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param r
|
||||||
|
* @param color
|
||||||
|
*/
|
||||||
void Display::drawCircle(int x, int y, int r, uint16_t color) {
|
void Display::drawCircle(int x, int y, int r, uint16_t color) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(drawCircle(x, y, r, color));
|
disp(drawCircle(x, y, r, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Display::drawRect(int x0, int y0, int x1, int y1, uint16_t color) {
|
void Display::drawRect(int x0, int y0, int x1, int y1, uint16_t color) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(drawRect(x0, y0, x1, y1, color));
|
disp(drawRect(x0, y0, x1, y1, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Display::checkInit(void) {
|
bool Display::isBegin(void) {
|
||||||
if (this->_isInit) {
|
if (this->_isBegin) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
AgLog("OLED is not init");
|
AgLog("Display not-initialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Display::setRotation(uint8_t r) {
|
void Display::setRotation(uint8_t r) {
|
||||||
if (checkInit() == false) {
|
if (isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
disp(setRotation(r));
|
disp(setRotation(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Display::end(void) {
|
||||||
|
if (this->_isBegin == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_isBegin = false;
|
||||||
|
if (this->_boardType == DIY_BASIC) {
|
||||||
|
delete ((Adafruit_SSD1306 *)(this->oled));
|
||||||
|
} else {
|
||||||
|
delete ((Adafruit_SH110X *)(this->oled));
|
||||||
|
}
|
||||||
|
AgLog("De-initialize");
|
||||||
|
}
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
#ifndef _AIR_GRADIENT_OLED_H_
|
#ifndef _AIR_GRADIENT_OLED_H_
|
||||||
#define _AIR_GRADIENT_OLED_H_
|
#define _AIR_GRADIENT_OLED_H_
|
||||||
|
|
||||||
#include "../bsp/BoardDef.h"
|
#include "../main/BoardDef.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The class define how to handle the OLED display on Airgradient has
|
||||||
|
* attached or support OLED display like: ONE-V9, Basic-V4
|
||||||
|
*/
|
||||||
class Display {
|
class Display {
|
||||||
public:
|
public:
|
||||||
const uint16_t COLOR_WHILTE = 1;
|
const uint16_t COLOR_WHILTE = 1;
|
||||||
@ -15,10 +19,11 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
Display(BoardType type);
|
Display(BoardType type);
|
||||||
void begin(TwoWire &wire);
|
void begin(TwoWire &wire);
|
||||||
|
void end(void);
|
||||||
|
|
||||||
void clear(void); // .clear
|
void clear(void);
|
||||||
void invertDisplay(uint8_t i);
|
void invertDisplay(uint8_t i);
|
||||||
void show(); // .show()
|
void show();
|
||||||
|
|
||||||
void setContrast(uint8_t value);
|
void setContrast(uint8_t value);
|
||||||
void drawPixel(int16_t x, int16_t y, uint16_t color);
|
void drawPixel(int16_t x, int16_t y, uint16_t color);
|
||||||
@ -39,14 +44,14 @@ private:
|
|||||||
BoardType _boardType;
|
BoardType _boardType;
|
||||||
const BoardDef *_bsp = nullptr;
|
const BoardDef *_bsp = nullptr;
|
||||||
void *oled;
|
void *oled;
|
||||||
bool _isInit = false;
|
bool _isBegin = false;
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
const char *TAG = "oled";
|
const char *TAG = "oled";
|
||||||
Stream *_debugStream = nullptr;
|
Stream *_debugStream = nullptr;
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
bool checkInit(void);
|
bool isBegin(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /** _AIR_GRADIENT_OLED_H_ */
|
#endif /** _AIR_GRADIENT_OLED_H_ */
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
Language: Cpp
|
|
||||||
BasedOnStyle: LLVM
|
|
||||||
IndentWidth: 4
|
|
||||||
AlignAfterOpenBracket: Align
|
|
||||||
AllowShortBlocksOnASingleLine: false
|
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
|
||||||
AllowShortFunctionsOnASingleLine: false
|
|
||||||
IndentCaseLabels: true
|
|
||||||
SpacesBeforeTrailingComments: 2
|
|
||||||
PointerAlignment: Left
|
|
||||||
AlignEscapedNewlines: Left
|
|
||||||
ForEachMacros: ['TEST_GROUP', 'TEST']
|
|
||||||
...
|
|
70
src/library/SensirionSHT4x/.gitignore
vendored
@ -1,70 +0,0 @@
|
|||||||
app/bin/
|
|
||||||
app/pde.jar
|
|
||||||
build/macosx/work/
|
|
||||||
arduino-core/bin/
|
|
||||||
arduino-core/arduino-core.jar
|
|
||||||
hardware/arduino/bootloaders/caterina_LUFA/Descriptors.o
|
|
||||||
hardware/arduino/bootloaders/caterina_LUFA/Descriptors.lst
|
|
||||||
hardware/arduino/bootloaders/caterina_LUFA/Caterina.sym
|
|
||||||
hardware/arduino/bootloaders/caterina_LUFA/Caterina.o
|
|
||||||
hardware/arduino/bootloaders/caterina_LUFA/Caterina.map
|
|
||||||
hardware/arduino/bootloaders/caterina_LUFA/Caterina.lst
|
|
||||||
hardware/arduino/bootloaders/caterina_LUFA/Caterina.lss
|
|
||||||
hardware/arduino/bootloaders/caterina_LUFA/Caterina.elf
|
|
||||||
hardware/arduino/bootloaders/caterina_LUFA/Caterina.eep
|
|
||||||
hardware/arduino/bootloaders/caterina_LUFA/.dep/
|
|
||||||
build/*.zip
|
|
||||||
build/*.tar.bz2
|
|
||||||
build/windows/work/
|
|
||||||
build/windows/*.zip
|
|
||||||
build/windows/*.tgz
|
|
||||||
build/windows/*.tar.bz2
|
|
||||||
build/windows/libastylej*
|
|
||||||
build/windows/liblistSerials*
|
|
||||||
build/windows/arduino-*.zip
|
|
||||||
build/windows/dist/*.tar.gz
|
|
||||||
build/windows/dist/*.tar.bz2
|
|
||||||
build/windows/launch4j-*.tgz
|
|
||||||
build/windows/launch4j-*.zip
|
|
||||||
build/windows/launcher/launch4j
|
|
||||||
build/windows/WinAVR-*.zip
|
|
||||||
build/macosx/arduino-*.zip
|
|
||||||
build/macosx/dist/*.tar.gz
|
|
||||||
build/macosx/dist/*.tar.bz2
|
|
||||||
build/macosx/*.tar.bz2
|
|
||||||
build/macosx/libastylej*
|
|
||||||
build/macosx/appbundler*.jar
|
|
||||||
build/macosx/appbundler*.zip
|
|
||||||
build/macosx/appbundler
|
|
||||||
build/macosx/appbundler-1.0ea-arduino?
|
|
||||||
build/macosx/appbundler-1.0ea-arduino*.zip
|
|
||||||
build/macosx/appbundler-1.0ea-upstream*.zip
|
|
||||||
build/linux/work/
|
|
||||||
build/linux/dist/*.tar.gz
|
|
||||||
build/linux/dist/*.tar.bz2
|
|
||||||
build/linux/*.tgz
|
|
||||||
build/linux/*.tar.xz
|
|
||||||
build/linux/*.tar.bz2
|
|
||||||
build/linux/*.zip
|
|
||||||
build/linux/libastylej*
|
|
||||||
build/linux/liblistSerials*
|
|
||||||
build/shared/arduino-examples*
|
|
||||||
build/shared/reference*.zip
|
|
||||||
build/shared/Edison*.zip
|
|
||||||
build/shared/Galileo*.zip
|
|
||||||
build/shared/WiFi101-Updater-ArduinoIDE-Plugin*.zip
|
|
||||||
test-bin
|
|
||||||
*.iml
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
.directory
|
|
||||||
hardware/arduino/avr/libraries/Bridge/examples/XivelyClient/passwords.h
|
|
||||||
avr-toolchain-*.zip
|
|
||||||
/app/nbproject/private/
|
|
||||||
/arduino-core/nbproject/private/
|
|
||||||
/app/build/
|
|
||||||
/arduino-core/build/
|
|
||||||
|
|
||||||
manifest.mf
|
|
||||||
nbbuild.xml
|
|
||||||
nbproject
|
|
@ -1,103 +0,0 @@
|
|||||||
stages:
|
|
||||||
- validate
|
|
||||||
- test
|
|
||||||
|
|
||||||
variables:
|
|
||||||
YQ_URL: https://github.com/mikefarah/yq/releases/download/v4.33.3/yq_linux_amd64
|
|
||||||
|
|
||||||
compile_test:
|
|
||||||
stage: test
|
|
||||||
image:
|
|
||||||
name: registry.gitlab.sensirion.lokal/sensirion/docker/docker-arduino:0.4.0
|
|
||||||
tags: [docker, linux]
|
|
||||||
before_script:
|
|
||||||
- rm -rf ../sensirion-core-arduino-library
|
|
||||||
script:
|
|
||||||
- git clone --depth 1 --branch 0.5.2 https://github.com/Sensirion/arduino-core.git ../sensirion-core-arduino-library
|
|
||||||
- arduino-cli compile --libraries=".." --warnings all --fqbn arduino:samd:mkrzero ./examples/exampleUsage/exampleUsage.ino
|
|
||||||
- arduino-cli compile --libraries=".." --warnings all --fqbn arduino:avr:mega ./examples/exampleUsage/exampleUsage.ino
|
|
||||||
- arduino-cli compile --libraries=".." --warnings all --fqbn arduino:avr:nano ./examples/exampleUsage/exampleUsage.ino
|
|
||||||
- arduino-cli compile --libraries=".." --warnings all --fqbn arduino:avr:uno ./examples/exampleUsage/exampleUsage.ino
|
|
||||||
- arduino-cli compile --libraries=".." --warnings all --fqbn esp32:esp32:esp32 ./examples/exampleUsage/exampleUsage.ino
|
|
||||||
- arduino-cli compile --libraries=".." --warnings all --fqbn esp8266:esp8266:generic ./examples/exampleUsage/exampleUsage.ino
|
|
||||||
|
|
||||||
arduino_lint:
|
|
||||||
stage: validate
|
|
||||||
image:
|
|
||||||
name: registry.gitlab.sensirion.lokal/mso-sw/drivers/docker-driver-generator:0.2.0
|
|
||||||
tags: [linux, docker]
|
|
||||||
script:
|
|
||||||
- mkdir ~/arlint
|
|
||||||
- PATH=~/arlint:$PATH
|
|
||||||
- curl -fsSL https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/install.sh | BINDIR=~/arlint sh
|
|
||||||
- arduino-lint --library-manager false
|
|
||||||
|
|
||||||
syntax_check:
|
|
||||||
stage: validate
|
|
||||||
image:
|
|
||||||
name: registry.gitlab.sensirion.lokal/mso-sw/drivers/docker-driver-generator:0.2.0
|
|
||||||
tags: [linux, docker]
|
|
||||||
script:
|
|
||||||
- find . -type f -iregex ".*\.\(c\|h\|cpp\|ino\)" -exec clang-format-6.0 -i -style=file {} \; && git diff --exit-code
|
|
||||||
|
|
||||||
|
|
||||||
cppcheck:
|
|
||||||
stage: validate
|
|
||||||
image:
|
|
||||||
name: registry.gitlab.sensirion.lokal/mso-sw/drivers/docker-driver-generator:0.2.0
|
|
||||||
tags: [linux, docker]
|
|
||||||
script:
|
|
||||||
- cppcheck --std=c++11 --language=c++ --error-exitcode=1 --enable=warning,style,performance,portability --suppress=unreadVariable src/*
|
|
||||||
|
|
||||||
TODO_check:
|
|
||||||
stage: validate
|
|
||||||
image:
|
|
||||||
name: registry.gitlab.sensirion.lokal/mso-sw/drivers/docker-driver-generator:0.2.0
|
|
||||||
tags: [linux, docker]
|
|
||||||
script:
|
|
||||||
- '! grep -rnw --exclude=.gitlab-ci.yml --exclude-dir=.git . -e "TODO"'
|
|
||||||
|
|
||||||
metadata_check:
|
|
||||||
stage: validate
|
|
||||||
image:
|
|
||||||
name: registry.gitlab.sensirion.lokal/mso-sw/drivers/docker-driver-generator:0.2.0
|
|
||||||
tags: [linux, docker]
|
|
||||||
before_script:
|
|
||||||
- apt-get -qq update && apt-get -qq install -y wget
|
|
||||||
- if ! [ -d downloads/ ]; then mkdir downloads; fi
|
|
||||||
- if ! [ -e downloads/yq ]; then wget --no-verbose $YQ_URL -O downloads/yq; fi
|
|
||||||
- cp downloads/yq /usr/local/bin/yq && chmod +x /usr/local/bin/yq
|
|
||||||
script:
|
|
||||||
# check if metadata.yml exists
|
|
||||||
- >
|
|
||||||
if ! [ -f "metadata.yml" ]; then
|
|
||||||
echo "metadata.yml file not found"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# check that dg_status is 'released'
|
|
||||||
- export DG_STATUS=$(yq ".dg_status.[]" ./metadata.yml)
|
|
||||||
- >
|
|
||||||
if [ $DG_STATUS != "released" ]; then
|
|
||||||
echo "dg_status in metadata.yml has to be 'released', not '$DG_STATUS'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# check that last_generated is not older than timestamp of last non-merge commit (+ 3 minutes)
|
|
||||||
- export IS_MANUALLY_MODIFIED=$(yq ".is_manually_modified" ./metadata.yml)
|
|
||||||
- >
|
|
||||||
if [ $IS_MANUALLY_MODIFIED = false ]; then
|
|
||||||
export LAST_GENERATED_TS=$(yq ".last_generated" ./metadata.yml)
|
|
||||||
export LAST_GENERATED_TS_EPOCH=$(date -d "$LAST_GENERATED_TS" +%s)
|
|
||||||
export LAST_NON_MERGE_COMMIT_TS=$(git log --format=%ad --date=iso-strict --no-merges -1)
|
|
||||||
export COMMIT_TS_EPOCH=$(date -d "$LAST_NON_MERGE_COMMIT_TS" +%s)
|
|
||||||
if [ $(($LAST_GENERATED_TS_EPOCH + 180)) -lt $COMMIT_TS_EPOCH ]; then
|
|
||||||
echo "'last_generated' timestamp in metadata.yml is older than commit timestamp ($LAST_GENERATED_TS vs $LAST_NON_MERGE_COMMIT_TS)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# check that 'is_manually_modified' is set to true if commit is not from driver generator
|
|
||||||
- export LAST_NON_MERGE_COMMIT_AUTHOR=$(git log --format=%an --no-merges -1)
|
|
||||||
- >
|
|
||||||
if ! [ "$LAST_NON_MERGE_COMMIT_AUTHOR" = "Driver Generator 2" ] && [ "$IS_MANUALLY_MODIFIED" = false ]; then
|
|
||||||
echo "Last commit is not from Driver Generator. Please update 'is_manually_modified' in metadata.yml"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
@ -1,19 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
Defined I2C addresses for different sensor models.
|
|
||||||
You must now pass the I2C address to use in the begin() method of SensirionI2cSht4x
|
|
||||||
|
|
||||||
Improved README
|
|
||||||
|
|
||||||
## [0.1.0] - 2021-07-27
|
|
||||||
|
|
||||||
Initial release
|
|
||||||
|
|
||||||
[0.1.0]: https://github.com/Sensirion/arduino-i2c-sht4x/releases/tag/0.1.0
|
|
@ -1,206 +0,0 @@
|
|||||||
# Sensirion I²C SHT4X Arduino Library
|
|
||||||
|
|
||||||
This is the Sensirion SHT4X library for Arduino allowing you to
|
|
||||||
communicate with a sensor of the SHT4X family over I²C.
|
|
||||||
|
|
||||||
<img src="images/SHT4x.png" width="300px">
|
|
||||||
|
|
||||||
Click [here](https://sensirion.com/products/catalog/SEK-SHT40/) to learn more about the Sensirion SHT4X sensor family.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Supported sensor types
|
|
||||||
|
|
||||||
| Sensor name | I²C Addresses |
|
|
||||||
| ------------- | -------------- |
|
|
||||||
|[SHT40](https://sensirion.com/products/catalog/SHT40/)| **0x44**, 0x45|
|
|
||||||
|[SHT41](https://sensirion.com/products/catalog/SHT41/)| **0x44**, 0x45|
|
|
||||||
|[SHT45](https://sensirion.com/products/catalog/SHT45/)| **0x44**, 0x45|
|
|
||||||
|
|
||||||
The following instructions and examples use a *SHT40*.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Installation of the library
|
|
||||||
|
|
||||||
This library can be installed using the Arduino Library manager:
|
|
||||||
Start the [Arduino IDE](http://www.arduino.cc/en/main/software) and open
|
|
||||||
the Library Manager via
|
|
||||||
|
|
||||||
`Sketch` ➔ `Include Library` ➔ `Manage Libraries...`
|
|
||||||
|
|
||||||
Search for the `Sensirion I2C SHT4X` library in the `Filter
|
|
||||||
your search...` field and install it by clicking the `install` button.
|
|
||||||
|
|
||||||
If you cannot find it in the library manager, download the latest release as .zip file
|
|
||||||
and add it to your [Arduino IDE](http://www.arduino.cc/en/main/software) via
|
|
||||||
|
|
||||||
`Sketch` ➔ `Include Library` ➔ `Add .ZIP Library...`
|
|
||||||
|
|
||||||
Don't forget to **install the dependencies** listed below the same way via library
|
|
||||||
manager or `Add .ZIP Library`
|
|
||||||
|
|
||||||
#### Dependencies
|
|
||||||
* [Sensirion Core](https://github.com/Sensirion/arduino-core)
|
|
||||||
|
|
||||||
## Sensor wiring
|
|
||||||
|
|
||||||
Use the following pin description to connect your SHT4X to the standard I²C bus of your Arduino board:
|
|
||||||
|
|
||||||
<img src="images/SHT40_pinout.png" width="300px">
|
|
||||||
|
|
||||||
| *Pin* | *Cable Color* | *Name* | *Description* | *Comments* |
|
|
||||||
|-------|---------------|:------:|----------------|------------|
|
|
||||||
| 1 | green | SDA | I2C: Serial data input / output |
|
|
||||||
| 2 | black | GND | Ground |
|
|
||||||
| 3 | yellow | SCL | I2C: Serial clock input |
|
|
||||||
| 4 | red | VDD | Supply Voltage | 1.1V to 3.6V
|
|
||||||
|
|
||||||
The recommended voltage is 3.3V.
|
|
||||||
|
|
||||||
Please refer to the datasheet for proper circuit setup. There are 3rd party boards for easy connection of the SHT4x sensor to your Arduino Board.
|
|
||||||
|
|
||||||
### Board specific wiring
|
|
||||||
You will find pinout schematics for recommended board models below:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary>Arduino Uno</summary>
|
|
||||||
<p>
|
|
||||||
|
|
||||||
| *SHT4X* | *SHT4X Pin* | *Cable Color* | *Board Pin* |
|
|
||||||
| :---: | --- | --- | --- |
|
|
||||||
| SDA | 1 | green | D18/SDA |
|
|
||||||
| GND | 2 | black | GND |
|
|
||||||
| SCL | 3 | yellow | D19/SCL |
|
|
||||||
| VDD | 4 | red | 3.3V |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="images/Arduino-Uno-Rev3-i2c-pinout-3.3V.png" width="600px">
|
|
||||||
</p>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary>Arduino Nano</summary>
|
|
||||||
<p>
|
|
||||||
|
|
||||||
| *SHT4X* | *SHT4X Pin* | *Cable Color* | *Board Pin* |
|
|
||||||
| :---: | --- | --- | --- |
|
|
||||||
| SDA | 1 | green | A4 |
|
|
||||||
| GND | 2 | black | GND |
|
|
||||||
| SCL | 3 | yellow | A5 |
|
|
||||||
| VDD | 4 | red | 3.3V |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="images/Arduino-Nano-i2c-pinout-3.3V.png" width="600px">
|
|
||||||
</p>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary>Arduino Micro</summary>
|
|
||||||
<p>
|
|
||||||
|
|
||||||
| *SHT4X* | *SHT4X Pin* | *Cable Color* | *Board Pin* |
|
|
||||||
| :---: | --- | --- | --- |
|
|
||||||
| SDA | 1 | green | D2/SDA |
|
|
||||||
| GND | 2 | black | GND |
|
|
||||||
| SCL | 3 | yellow | ~D3/SCL |
|
|
||||||
| VDD | 4 | red | 3.3V |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="images/Arduino-Micro-i2c-pinout-3.3V.png" width="600px">
|
|
||||||
</p>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary>Arduino Mega 2560</summary>
|
|
||||||
<p>
|
|
||||||
|
|
||||||
| *SHT4X* | *SHT4X Pin* | *Cable Color* | *Board Pin* |
|
|
||||||
| :---: | --- | --- | --- |
|
|
||||||
| SDA | 1 | green | D20/SDA |
|
|
||||||
| GND | 2 | black | GND |
|
|
||||||
| SCL | 3 | yellow | D21/SCL |
|
|
||||||
| VDD | 4 | red | 3.3V |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="images/Arduino-Mega-2560-Rev3-i2c-pinout-3.3V.png" width="600px">
|
|
||||||
</p>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary>ESP32 DevKitC</summary>
|
|
||||||
<p>
|
|
||||||
|
|
||||||
| *SHT4X* | *SHT4X Pin* | *Cable Color* | *Board Pin* |
|
|
||||||
| :---: | --- | --- | --- |
|
|
||||||
| SDA | 1 | green | GPIO 21 |
|
|
||||||
| GND | 2 | black | GND |
|
|
||||||
| SCL | 3 | yellow | GPIO 22 |
|
|
||||||
| VDD | 4 | red | 3V3 |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<img src="images/esp32-devkitc-i2c-pinout-3.3V.png" width="600px">
|
|
||||||
</p>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
1. Install the libraries and dependencies according to [Installation of the library](#installation-of-the-library)
|
|
||||||
|
|
||||||
2. Connect the SHT4X sensor to your Arduino as explained in [Sensor wiring](#sensor-wiring)
|
|
||||||
|
|
||||||
3. Open the `exampleUsage` sample project within the Arduino IDE:
|
|
||||||
|
|
||||||
`File` ➔ `Examples` ➔ `Sensirion I2C SHT4X` ➔ `exampleUsage`
|
|
||||||
|
|
||||||
|
|
||||||
The provided example is working with a SHT40, I²C address 0x44.
|
|
||||||
In order to use the code with another product or I²C address you need to change it in the code of `exampleUsage`.
|
|
||||||
You find the list with pre-defined addresses in `src/SensirionI2CSht4x.h`.
|
|
||||||
|
|
||||||
|
|
||||||
5. Click the `Upload` button in the Arduino IDE or `Sketch` ➔ `Upload`
|
|
||||||
|
|
||||||
4. When the upload process has finished, open the `Serial Monitor` or `Serial
|
|
||||||
Plotter` via the `Tools` menu to observe the measurement values. Note that
|
|
||||||
the `Baud Rate` in the used tool has to be set to `115200 baud`.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
**Contributions are welcome!**
|
|
||||||
|
|
||||||
We develop and test this driver using our company internal tools (version
|
|
||||||
control, continuous integration, code review etc.) and automatically
|
|
||||||
synchronize the master branch with GitHub. But this doesn't mean that we don't
|
|
||||||
respond to issues or don't accept pull requests on GitHub. In fact, you're very
|
|
||||||
welcome to open issues or create pull requests :)
|
|
||||||
|
|
||||||
This Sensirion library uses
|
|
||||||
[`clang-format`](https://releases.llvm.org/download.html) to standardize the
|
|
||||||
formatting of all our `.cpp` and `.h` files. Make sure your contributions are
|
|
||||||
formatted accordingly:
|
|
||||||
|
|
||||||
The `-i` flag will apply the format changes to the files listed.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
clang-format -i src/*.cpp src/*.h
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that differences from this formatting will result in a failed build until
|
|
||||||
they are fixed.
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
See [LICENSE](LICENSE).
|
|
@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
* THIS FILE IS AUTOMATICALLY GENERATED
|
|
||||||
*
|
|
||||||
* Generator: sensirion-driver-generator 0.32.0
|
|
||||||
* Product: sht4x
|
|
||||||
* Model-Version: 2.0.0
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Sensirion AG
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
|
||||||
* list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of Sensirion AG nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <SensirionI2CSht4x.h>
|
|
||||||
#include <Wire.h>
|
|
||||||
|
|
||||||
SensirionI2CSht4x sensor;
|
|
||||||
|
|
||||||
static char errorMessage[64];
|
|
||||||
static int16_t error;
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
|
|
||||||
Serial.begin(115200);
|
|
||||||
while (!Serial) {
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
Wire.begin();
|
|
||||||
sensor.begin(Wire, SHT40_I2C_ADDR_44);
|
|
||||||
|
|
||||||
sensor.softReset();
|
|
||||||
delay(10);
|
|
||||||
uint32_t serialNumber = 0;
|
|
||||||
error = sensor.serialNumber(serialNumber);
|
|
||||||
if (error != NO_ERROR) {
|
|
||||||
Serial.print("Error trying to execute serialNumber(): ");
|
|
||||||
errorToString(error, errorMessage, sizeof errorMessage);
|
|
||||||
Serial.println(errorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Serial.print("serialNumber: ");
|
|
||||||
Serial.print(serialNumber);
|
|
||||||
Serial.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
|
|
||||||
float aTemperature = 0.0;
|
|
||||||
float aHumidity = 0.0;
|
|
||||||
delay(20);
|
|
||||||
error = sensor.measureLowestPrecision(aTemperature, aHumidity);
|
|
||||||
if (error != NO_ERROR) {
|
|
||||||
Serial.print("Error trying to execute measureLowestPrecision(): ");
|
|
||||||
errorToString(error, errorMessage, sizeof errorMessage);
|
|
||||||
Serial.println(errorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Serial.print("aTemperature: ");
|
|
||||||
Serial.print(aTemperature);
|
|
||||||
Serial.print("\t");
|
|
||||||
Serial.print("aHumidity: ");
|
|
||||||
Serial.print(aHumidity);
|
|
||||||
Serial.println();
|
|
||||||
}
|
|
Before Width: | Height: | Size: 405 KiB |
Before Width: | Height: | Size: 322 KiB |
Before Width: | Height: | Size: 296 KiB |
Before Width: | Height: | Size: 315 KiB |
Before Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 524 KiB |
Before Width: | Height: | Size: 238 KiB |
@ -1,46 +0,0 @@
|
|||||||
#######################################
|
|
||||||
# Syntax Coloring Map
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
#######################################
|
|
||||||
# Datatypes (KEYWORD1)
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
SensirionI2CSht4x KEYWORD1
|
|
||||||
|
|
||||||
#######################################
|
|
||||||
# Methods and Functions (KEYWORD2)
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
measureHighPrecision KEYWORD2
|
|
||||||
measureMediumPrecision KEYWORD2
|
|
||||||
measureLowestPrecision KEYWORD2
|
|
||||||
activateHighestHeaterPowerLong KEYWORD2
|
|
||||||
activateHighestHeaterPowerShort KEYWORD2
|
|
||||||
activateMediumHeaterPowerLong KEYWORD2
|
|
||||||
activateMediumHeaterPowerShort KEYWORD2
|
|
||||||
activateLowestHeaterPowerLong KEYWORD2
|
|
||||||
activateLowestHeaterPowerShort KEYWORD2
|
|
||||||
measureHighPrecisionTicks KEYWORD2
|
|
||||||
measureMediumPrecisionTicks KEYWORD2
|
|
||||||
measureLowestPrecisionTicks KEYWORD2
|
|
||||||
activateHighestHeaterPowerLongTicks KEYWORD2
|
|
||||||
activateHighestHeaterPowerShortTicks KEYWORD2
|
|
||||||
activateMediumHeaterPowerLongTicks KEYWORD2
|
|
||||||
activateMediumHeaterPowerShortTicks KEYWORD2
|
|
||||||
activateLowestHeaterPowerLongTicks KEYWORD2
|
|
||||||
activateLowestHeaterPowerShortTicks KEYWORD2
|
|
||||||
serialNumber KEYWORD2
|
|
||||||
softReset KEYWORD2
|
|
||||||
signalTemperature KEYWORD2
|
|
||||||
signalHumidity KEYWORD2
|
|
||||||
|
|
||||||
#######################################
|
|
||||||
# Instances (KEYWORD2)
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
sensor KEYWORD2
|
|
||||||
|
|
||||||
#######################################
|
|
||||||
# Constants (LITERAL1)
|
|
||||||
#######################################
|
|
@ -1,11 +0,0 @@
|
|||||||
name=Sensirion I2C SHT4x
|
|
||||||
version=1.0.0
|
|
||||||
author=Sensirion
|
|
||||||
maintainer=Sensirion
|
|
||||||
sentence=Library for the SHT4X sensor family by Sensirion
|
|
||||||
paragraph=Enables you to use the SHT4X sensor family via I2C.
|
|
||||||
url=https://github.com/Sensirion/arduino-i2c-sht4x
|
|
||||||
category=Sensors
|
|
||||||
architectures=*
|
|
||||||
depends=Sensirion Core
|
|
||||||
includes=SensirionI2CSht4x.h
|
|
@ -1,7 +0,0 @@
|
|||||||
generator_version: 0.32.0
|
|
||||||
model_version: 2.0.0
|
|
||||||
dg_status:
|
|
||||||
- released
|
|
||||||
is_manually_modified: true
|
|
||||||
first_generated: '2021-07-06 15:05'
|
|
||||||
last_generated: '2023-10-18 07:36'
|
|
@ -1,436 +0,0 @@
|
|||||||
/*
|
|
||||||
* THIS FILE IS AUTOMATICALLY GENERATED
|
|
||||||
*
|
|
||||||
* Generator: sensirion-driver-generator 0.32.0
|
|
||||||
* Product: sht4x
|
|
||||||
* Model-Version: 2.0.0
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Sensirion AG
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
|
||||||
* list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of Sensirion AG nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "SensirionI2CSht4x.h"
|
|
||||||
#include "../../SensirionCore/src/SensirionCore.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
|
|
||||||
static uint8_t communication_buffer[6] = {0};
|
|
||||||
|
|
||||||
SensirionI2CSht4x::SensirionI2CSht4x() {
|
|
||||||
}
|
|
||||||
|
|
||||||
float SensirionI2CSht4x::signalTemperature(uint16_t temperatureTicks) {
|
|
||||||
float temperature = 0.0;
|
|
||||||
temperature = (float)(temperatureTicks);
|
|
||||||
temperature = (float)((temperature * 175.0) / 65535.0) - 45.0;
|
|
||||||
return temperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
float SensirionI2CSht4x::signalHumidity(uint16_t humidityTicks) {
|
|
||||||
float humidity = 0.0;
|
|
||||||
humidity = (float)(humidityTicks);
|
|
||||||
humidity = (float)((humidity * 125.0) / 65535.0) - 6.0;
|
|
||||||
return humidity;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::measureHighPrecision(float& aTemperature,
|
|
||||||
float& aHumidity) {
|
|
||||||
uint16_t tempTicks = 0;
|
|
||||||
uint16_t humiTicks = 0;
|
|
||||||
int16_t localError = 0;
|
|
||||||
localError = measureHighPrecisionTicks(tempTicks, humiTicks);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
aTemperature = signalTemperature(tempTicks);
|
|
||||||
aHumidity = signalHumidity(humiTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::measureMediumPrecision(float& aTemperature,
|
|
||||||
float& aHumidity) {
|
|
||||||
uint16_t tempTicks = 0;
|
|
||||||
uint16_t humiTicks = 0;
|
|
||||||
int16_t localError = 0;
|
|
||||||
localError = measureMediumPrecisionTicks(tempTicks, humiTicks);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
aTemperature = signalTemperature(tempTicks);
|
|
||||||
aHumidity = signalHumidity(humiTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::measureLowestPrecision(float& aTemperature,
|
|
||||||
float& aHumidity) {
|
|
||||||
uint16_t tempTicks = 0;
|
|
||||||
uint16_t humiTicks = 0;
|
|
||||||
int16_t localError = 0;
|
|
||||||
localError = measureLowestPrecisionTicks(tempTicks, humiTicks);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
aTemperature = signalTemperature(tempTicks);
|
|
||||||
aHumidity = signalHumidity(humiTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::activateHighestHeaterPowerLong(float& aTemperature,
|
|
||||||
float& aHumidity) {
|
|
||||||
uint16_t tempTicks = 0;
|
|
||||||
uint16_t humiTicks = 0;
|
|
||||||
int16_t localError = 0;
|
|
||||||
localError = activateHighestHeaterPowerLongTicks(tempTicks, humiTicks);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
aTemperature = signalTemperature(tempTicks);
|
|
||||||
aHumidity = signalHumidity(humiTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::activateHighestHeaterPowerShort(float& aTemperature,
|
|
||||||
float& aHumidity) {
|
|
||||||
uint16_t tempTicks = 0;
|
|
||||||
uint16_t humiTicks = 0;
|
|
||||||
int16_t localError = 0;
|
|
||||||
localError = activateHighestHeaterPowerShortTicks(tempTicks, humiTicks);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
aTemperature = signalTemperature(tempTicks);
|
|
||||||
aHumidity = signalHumidity(humiTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::activateMediumHeaterPowerLong(float& aTemperature,
|
|
||||||
float& aHumidity) {
|
|
||||||
uint16_t tempTicks = 0;
|
|
||||||
uint16_t humiTicks = 0;
|
|
||||||
int16_t localError = 0;
|
|
||||||
localError = activateMediumHeaterPowerLongTicks(tempTicks, humiTicks);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
aTemperature = signalTemperature(tempTicks);
|
|
||||||
aHumidity = signalHumidity(humiTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::activateMediumHeaterPowerShort(float& aTemperature,
|
|
||||||
float& aHumidity) {
|
|
||||||
uint16_t tempTicks = 0;
|
|
||||||
uint16_t humiTicks = 0;
|
|
||||||
int16_t localError = 0;
|
|
||||||
localError = activateMediumHeaterPowerShortTicks(tempTicks, humiTicks);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
aTemperature = signalTemperature(tempTicks);
|
|
||||||
aHumidity = signalHumidity(humiTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::activateLowestHeaterPowerLong(float& aTemperature,
|
|
||||||
float& aHumidity) {
|
|
||||||
uint16_t tempTicks = 0;
|
|
||||||
uint16_t humiTicks = 0;
|
|
||||||
int16_t localError = 0;
|
|
||||||
localError = activateLowestHeaterPowerLongTicks(tempTicks, humiTicks);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
aTemperature = signalTemperature(tempTicks);
|
|
||||||
aHumidity = signalHumidity(humiTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::activateLowestHeaterPowerShort(float& aTemperature,
|
|
||||||
float& aHumidity) {
|
|
||||||
uint16_t tempTicks = 0;
|
|
||||||
uint16_t humiTicks = 0;
|
|
||||||
int16_t localError = 0;
|
|
||||||
localError = activateLowestHeaterPowerShortTicks(tempTicks, humiTicks);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
aTemperature = signalTemperature(tempTicks);
|
|
||||||
aHumidity = signalHumidity(humiTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::measureHighPrecisionTicks(uint16_t& temperatureTicks,
|
|
||||||
uint16_t& humidityTicks) {
|
|
||||||
int16_t localError = NO_ERROR;
|
|
||||||
uint8_t* buffer_ptr = communication_buffer;
|
|
||||||
SensirionI2CTxFrame txFrame =
|
|
||||||
SensirionI2CTxFrame::createWithUInt8Command(0xfd, buffer_ptr, 6);
|
|
||||||
localError =
|
|
||||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
delay(10);
|
|
||||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
|
|
||||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
|
|
||||||
rxFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
localError |= rxFrame.getUInt16(temperatureTicks);
|
|
||||||
localError |= rxFrame.getUInt16(humidityTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t
|
|
||||||
SensirionI2CSht4x::measureMediumPrecisionTicks(uint16_t& temperatureTicks,
|
|
||||||
uint16_t& humidityTicks) {
|
|
||||||
int16_t localError = NO_ERROR;
|
|
||||||
uint8_t* buffer_ptr = communication_buffer;
|
|
||||||
SensirionI2CTxFrame txFrame =
|
|
||||||
SensirionI2CTxFrame::createWithUInt8Command(0xf6, buffer_ptr, 6);
|
|
||||||
localError =
|
|
||||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
delay(5);
|
|
||||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
|
|
||||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
|
|
||||||
rxFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
localError |= rxFrame.getUInt16(temperatureTicks);
|
|
||||||
localError |= rxFrame.getUInt16(humidityTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t
|
|
||||||
SensirionI2CSht4x::measureLowestPrecisionTicks(uint16_t& temperatureTicks,
|
|
||||||
uint16_t& humidityTicks) {
|
|
||||||
int16_t localError = NO_ERROR;
|
|
||||||
uint8_t* buffer_ptr = communication_buffer;
|
|
||||||
SensirionI2CTxFrame txFrame =
|
|
||||||
SensirionI2CTxFrame::createWithUInt8Command(0xe0, buffer_ptr, 6);
|
|
||||||
localError =
|
|
||||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
delay(2);
|
|
||||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
|
|
||||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
|
|
||||||
rxFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
localError |= rxFrame.getUInt16(temperatureTicks);
|
|
||||||
localError |= rxFrame.getUInt16(humidityTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::activateHighestHeaterPowerLongTicks(
|
|
||||||
uint16_t& temperatureTicks, uint16_t& humidityTicks) {
|
|
||||||
int16_t localError = NO_ERROR;
|
|
||||||
uint8_t* buffer_ptr = communication_buffer;
|
|
||||||
SensirionI2CTxFrame txFrame =
|
|
||||||
SensirionI2CTxFrame::createWithUInt8Command(0x39, buffer_ptr, 6);
|
|
||||||
localError =
|
|
||||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
delay(1100);
|
|
||||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
|
|
||||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
|
|
||||||
rxFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
localError |= rxFrame.getUInt16(temperatureTicks);
|
|
||||||
localError |= rxFrame.getUInt16(humidityTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::activateHighestHeaterPowerShortTicks(
|
|
||||||
uint16_t& temperatureTicks, uint16_t& humidityTicks) {
|
|
||||||
int16_t localError = NO_ERROR;
|
|
||||||
uint8_t* buffer_ptr = communication_buffer;
|
|
||||||
SensirionI2CTxFrame txFrame =
|
|
||||||
SensirionI2CTxFrame::createWithUInt8Command(0x32, buffer_ptr, 6);
|
|
||||||
localError =
|
|
||||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
delay(110);
|
|
||||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
|
|
||||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
|
|
||||||
rxFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
localError |= rxFrame.getUInt16(temperatureTicks);
|
|
||||||
localError |= rxFrame.getUInt16(humidityTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::activateMediumHeaterPowerLongTicks(
|
|
||||||
uint16_t& temperatureTicks, uint16_t& humidityTicks) {
|
|
||||||
int16_t localError = NO_ERROR;
|
|
||||||
uint8_t* buffer_ptr = communication_buffer;
|
|
||||||
SensirionI2CTxFrame txFrame =
|
|
||||||
SensirionI2CTxFrame::createWithUInt8Command(0x2f, buffer_ptr, 6);
|
|
||||||
localError =
|
|
||||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
delay(1100);
|
|
||||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
|
|
||||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
|
|
||||||
rxFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
localError |= rxFrame.getUInt16(temperatureTicks);
|
|
||||||
localError |= rxFrame.getUInt16(humidityTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::activateMediumHeaterPowerShortTicks(
|
|
||||||
uint16_t& temperatureTicks, uint16_t& humidityTicks) {
|
|
||||||
int16_t localError = NO_ERROR;
|
|
||||||
uint8_t* buffer_ptr = communication_buffer;
|
|
||||||
SensirionI2CTxFrame txFrame =
|
|
||||||
SensirionI2CTxFrame::createWithUInt8Command(0x24, buffer_ptr, 6);
|
|
||||||
localError =
|
|
||||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
delay(110);
|
|
||||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
|
|
||||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
|
|
||||||
rxFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
localError |= rxFrame.getUInt16(temperatureTicks);
|
|
||||||
localError |= rxFrame.getUInt16(humidityTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::activateLowestHeaterPowerLongTicks(
|
|
||||||
uint16_t& temperatureTicks, uint16_t& humidityTicks) {
|
|
||||||
int16_t localError = NO_ERROR;
|
|
||||||
uint8_t* buffer_ptr = communication_buffer;
|
|
||||||
SensirionI2CTxFrame txFrame =
|
|
||||||
SensirionI2CTxFrame::createWithUInt8Command(0x1e, buffer_ptr, 6);
|
|
||||||
localError =
|
|
||||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
delay(1100);
|
|
||||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
|
|
||||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
|
|
||||||
rxFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
localError |= rxFrame.getUInt16(temperatureTicks);
|
|
||||||
localError |= rxFrame.getUInt16(humidityTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::activateLowestHeaterPowerShortTicks(
|
|
||||||
uint16_t& temperatureTicks, uint16_t& humidityTicks) {
|
|
||||||
int16_t localError = NO_ERROR;
|
|
||||||
uint8_t* buffer_ptr = communication_buffer;
|
|
||||||
SensirionI2CTxFrame txFrame =
|
|
||||||
SensirionI2CTxFrame::createWithUInt8Command(0x15, buffer_ptr, 6);
|
|
||||||
localError =
|
|
||||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
delay(110);
|
|
||||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
|
|
||||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
|
|
||||||
rxFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
localError |= rxFrame.getUInt16(temperatureTicks);
|
|
||||||
localError |= rxFrame.getUInt16(humidityTicks);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::serialNumber(uint32_t& serialNumber) {
|
|
||||||
int16_t localError = NO_ERROR;
|
|
||||||
uint8_t* buffer_ptr = communication_buffer;
|
|
||||||
SensirionI2CTxFrame txFrame =
|
|
||||||
SensirionI2CTxFrame::createWithUInt8Command(0x89, buffer_ptr, 6);
|
|
||||||
localError =
|
|
||||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
delay(10);
|
|
||||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 6);
|
|
||||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 6,
|
|
||||||
rxFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
localError |= rxFrame.getUInt32(serialNumber);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SensirionI2CSht4x::softReset() {
|
|
||||||
int16_t localError = NO_ERROR;
|
|
||||||
uint8_t* buffer_ptr = communication_buffer;
|
|
||||||
SensirionI2CTxFrame txFrame =
|
|
||||||
SensirionI2CTxFrame::createWithUInt8Command(0x94, buffer_ptr, 2);
|
|
||||||
localError =
|
|
||||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
|
||||||
if (localError != NO_ERROR) {
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
delay(10);
|
|
||||||
return localError;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SensirionI2CSht4x::begin(TwoWire& i2cBus, uint8_t i2cAddress) {
|
|
||||||
_i2cBus = &i2cBus;
|
|
||||||
_i2cAddress = i2cAddress;
|
|
||||||
}
|
|
@ -1,424 +0,0 @@
|
|||||||
/*
|
|
||||||
* THIS FILE IS AUTOMATICALLY GENERATED
|
|
||||||
*
|
|
||||||
* Generator: sensirion-driver-generator 0.32.0
|
|
||||||
* Product: sht4x
|
|
||||||
* Model-Version: 2.0.0
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Sensirion AG
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
|
||||||
* list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of Sensirion AG nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SENSIRIONI2CSHT4X_H
|
|
||||||
#define SENSIRIONI2CSHT4X_H
|
|
||||||
|
|
||||||
#include <Wire.h>
|
|
||||||
|
|
||||||
#define NO_ERROR 0
|
|
||||||
#define SHT40_I2C_ADDR_44 0x44
|
|
||||||
#define SHT40_I2C_ADDR_45 0x45
|
|
||||||
#define SHT41_I2C_ADDR_44 0x44
|
|
||||||
#define SHT41_I2C_ADDR_45 0x45
|
|
||||||
#define SHT45_I2C_ADDR_44 0x44
|
|
||||||
#define SHT45_I2C_ADDR_45 0x45
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
MEASURE_HIGH_PRECISION_TICKS_CMD_ID = 0xfd,
|
|
||||||
MEASURE_MEDIUM_PRECISION_TICKS_CMD_ID = 0xf6,
|
|
||||||
MEASURE_LOWEST_PRECISION_TICKS_CMD_ID = 0xe0,
|
|
||||||
ACTIVATE_HIGHEST_HEATER_POWER_LONG_TICKS_CMD_ID = 0x39,
|
|
||||||
ACTIVATE_HIGHEST_HEATER_POWER_SHORT_TICKS_CMD_ID = 0x32,
|
|
||||||
ACTIVATE_MEDIUM_HEATER_POWER_LONG_TICKS_CMD_ID = 0x2f,
|
|
||||||
ACTIVATE_MEDIUM_HEATER_POWER_SHORT_TICKS_CMD_ID = 0x24,
|
|
||||||
ACTIVATE_LOWEST_HEATER_POWER_LONG_TICKS_CMD_ID = 0x1e,
|
|
||||||
ACTIVATE_LOWEST_HEATER_POWER_SHORT_TICKS_CMD_ID = 0x15,
|
|
||||||
SERIAL_NUMBER_CMD_ID = 0x89,
|
|
||||||
SOFT_RESET_CMD_ID = 0x94,
|
|
||||||
} CmdId;
|
|
||||||
|
|
||||||
class SensirionI2CSht4x {
|
|
||||||
public:
|
|
||||||
SensirionI2CSht4x();
|
|
||||||
/**
|
|
||||||
* @brief Initializes the SHT4x class.
|
|
||||||
*
|
|
||||||
* @param i2cBus Arduino stream object to be used for communication.
|
|
||||||
*/
|
|
||||||
void begin(TwoWire& i2cBus, uint8_t i2cAddress);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Run measurement with high precision
|
|
||||||
*
|
|
||||||
* SHT4x command for a single shot measurement with high repeatability.
|
|
||||||
*
|
|
||||||
* @param[out] aTemperature Converted from ticks to degrees celsius by (175
|
|
||||||
* * ticks_value / 65535) - 45
|
|
||||||
* @param[out] aHumidity Converted from ticks to percent relative humdity by
|
|
||||||
* (125 * ticks_value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t measureHighPrecision(float& aTemperature, float& aHumidity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Run measurement with medium precision
|
|
||||||
*
|
|
||||||
* SHT4x command for a single shot measurement with medium repeatability.
|
|
||||||
*
|
|
||||||
* @param[out] aTemperature Converted from ticks to degrees celsius by (175
|
|
||||||
* * ticks_value / 65535) - 45
|
|
||||||
* @param[out] aHumidity Converted from ticks to percent relative humdity by
|
|
||||||
* (125 * ticks_value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t measureMediumPrecision(float& aTemperature, float& aHumidity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Run measurement with low precision
|
|
||||||
*
|
|
||||||
* SHT4x command for a single shot measurement with low repeatability.
|
|
||||||
*
|
|
||||||
* @param[out] aTemperature Converted from ticks to degrees celsius by (175
|
|
||||||
* * ticks_value / 65535) - 45
|
|
||||||
* @param[out] aHumidity Converted from ticks to percent relative humdity by
|
|
||||||
* (125 * ticks_value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t measureLowestPrecision(float& aTemperature, float& aHumidity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Run measurement with high precision and the highest power heater
|
|
||||||
* for 1s
|
|
||||||
*
|
|
||||||
* SHT4x command to activate highest heater power and perform a single shot
|
|
||||||
* high precision measurement for 1s.
|
|
||||||
*
|
|
||||||
* @param[out] aTemperature Converted from ticks to degrees celsius by (175
|
|
||||||
* * ticks_value / 65535) - 45
|
|
||||||
* @param[out] aHumidity Converted from ticks to percent relative humdity by
|
|
||||||
* (125 * ticks_value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t activateHighestHeaterPowerLong(float& aTemperature,
|
|
||||||
float& aHumidity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Run measurement with high precision and the highest power heater
|
|
||||||
* for 0.1s
|
|
||||||
*
|
|
||||||
* SHT4x command to activate highest heater power and perform a single shot
|
|
||||||
* high precision measurement for 0.1s.
|
|
||||||
*
|
|
||||||
* @param[out] aTemperature Converted from ticks to degrees celsius by (175
|
|
||||||
* * ticks_value / 65535) - 45
|
|
||||||
* @param[out] aHumidity Converted from ticks to percent relative humdity by
|
|
||||||
* (125 * ticks_value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t activateHighestHeaterPowerShort(float& aTemperature,
|
|
||||||
float& aHumidity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Run measurement with high precision and the medium power heater
|
|
||||||
* for 1s
|
|
||||||
*
|
|
||||||
* SHT4x command to activate medium heater power and perform a single shot
|
|
||||||
* high precision measurement for 1s.
|
|
||||||
*
|
|
||||||
* @param[out] aTemperature Converted from ticks to degrees celsius by (175
|
|
||||||
* * ticks_value / 65535) - 45
|
|
||||||
* @param[out] aHumidity Converted from ticks to percent relative humdity by
|
|
||||||
* (125 * ticks_value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t activateMediumHeaterPowerLong(float& aTemperature,
|
|
||||||
float& aHumidity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Run measurement with high precision and the medium power heater
|
|
||||||
* for 0.1s
|
|
||||||
*
|
|
||||||
* SHT4x command to activate medium heater power and perform a single shot
|
|
||||||
* high precision measurement for 0.1s.
|
|
||||||
*
|
|
||||||
* @param[out] aTemperature Converted from ticks to degrees celsius by (175
|
|
||||||
* * ticks_value / 65535) - 45
|
|
||||||
* @param[out] aHumidity Converted from ticks to percent relative humdity by
|
|
||||||
* (125 * ticks_value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t activateMediumHeaterPowerShort(float& aTemperature,
|
|
||||||
float& aHumidity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Run measurement with high precision and the lowest power heater
|
|
||||||
* for 1s
|
|
||||||
*
|
|
||||||
* SHT4x command to activate lowest heater power and perform a single shot
|
|
||||||
* high precision measurement for 1s.
|
|
||||||
*
|
|
||||||
* @param[out] aTemperature Converted from ticks to degrees celsius by (175
|
|
||||||
* * ticks_value / 65535) - 45
|
|
||||||
* @param[out] aHumidity Converted from ticks to percent relative humdity by
|
|
||||||
* (125 * ticks_value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t activateLowestHeaterPowerLong(float& aTemperature,
|
|
||||||
float& aHumidity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Run measurement with high precision and the lowest power heater
|
|
||||||
* for 0.1s
|
|
||||||
*
|
|
||||||
* SHT4x command to activate lowest heater power and perform a single shot
|
|
||||||
* high precision measurement for 0.1s.
|
|
||||||
*
|
|
||||||
* @param[out] aTemperature Converted from ticks to degrees celsius by (175
|
|
||||||
* * ticks_value / 65535) - 45
|
|
||||||
* @param[out] aHumidity Converted from ticks to percent relative humdity by
|
|
||||||
* (125 * ticks_value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t activateLowestHeaterPowerShort(float& aTemperature,
|
|
||||||
float& aHumidity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief measureHighPrecisionTicks
|
|
||||||
*
|
|
||||||
* SHT4x command for a single shot measurement with high repeatability.
|
|
||||||
*
|
|
||||||
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
|
|
||||||
* celsius by (175 * value / 65535) - 45
|
|
||||||
* @param[out] humidityTicks Humidity ticks. Convert to degrees celsius by
|
|
||||||
* (125
|
|
||||||
* * value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t measureHighPrecisionTicks(uint16_t& temperatureTicks,
|
|
||||||
uint16_t& humidityTicks);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief measureMediumPrecisionTicks
|
|
||||||
*
|
|
||||||
* SHT4x command for a single shot measurement with medium repeatability.
|
|
||||||
*
|
|
||||||
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
|
|
||||||
* celsius by (175 * value / 65535) - 45
|
|
||||||
* @param[out] humidityTicks Humidity ticks. Convert to degrees celsius by
|
|
||||||
* (125
|
|
||||||
* * value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t measureMediumPrecisionTicks(uint16_t& temperatureTicks,
|
|
||||||
uint16_t& humidityTicks);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief measureLowestPrecisionTicks
|
|
||||||
*
|
|
||||||
* SHT4x command for a single shot measurement with lowest repeatability.
|
|
||||||
*
|
|
||||||
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
|
|
||||||
* celsius by (175 * value / 65535) - 45
|
|
||||||
* @param[out] humidityTicks Humidity ticks. Convert to degrees celsius by
|
|
||||||
* (125
|
|
||||||
* * value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t measureLowestPrecisionTicks(uint16_t& temperatureTicks,
|
|
||||||
uint16_t& humidityTicks);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief activateHighestHeaterPowerLongTicks
|
|
||||||
*
|
|
||||||
* SHT4x command to activate highest heater power and perform a single shot
|
|
||||||
* high precision measurement for 1s.
|
|
||||||
*
|
|
||||||
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
|
|
||||||
* celsius by (175 * value / 65535) - 45
|
|
||||||
* @param[out] humidityTicks Humidity ticks. Convert to degrees celsius by
|
|
||||||
* (125
|
|
||||||
* * value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t activateHighestHeaterPowerLongTicks(uint16_t& temperatureTicks,
|
|
||||||
uint16_t& humidityTicks);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief activateHighestHeaterPowerShortTicks
|
|
||||||
*
|
|
||||||
* SHT4x command to activate highest heater power and perform a single shot
|
|
||||||
* high precision measurement for 0.1s.
|
|
||||||
*
|
|
||||||
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
|
|
||||||
* celsius by (175 * value / 65535) - 45
|
|
||||||
* @param[out] humidityTicks Humidity ticks. Convert to degrees celsius by
|
|
||||||
* (125
|
|
||||||
* * value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t activateHighestHeaterPowerShortTicks(uint16_t& temperatureTicks,
|
|
||||||
uint16_t& humidityTicks);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief activateMediumHeaterPowerLongTicks
|
|
||||||
*
|
|
||||||
* SHT4x command to activate medium heater power and perform a single shot
|
|
||||||
* high precision measurement for 1s.
|
|
||||||
*
|
|
||||||
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
|
|
||||||
* celsius by (175 * value / 65535) - 45
|
|
||||||
* @param[out] humidityTicks Humidity ticks. Convert to degrees celsius by
|
|
||||||
* (125
|
|
||||||
* * value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t activateMediumHeaterPowerLongTicks(uint16_t& temperatureTicks,
|
|
||||||
uint16_t& humidityTicks);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief activateMediumHeaterPowerShortTicks
|
|
||||||
*
|
|
||||||
* SHT4x command to activate medium heater power and perform a single shot
|
|
||||||
* high precision measurement for 0.1s.
|
|
||||||
*
|
|
||||||
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
|
|
||||||
* celsius by (175 * value / 65535) - 45
|
|
||||||
* @param[out] humidityTicks Humidity ticks. Convert to degrees celsius by
|
|
||||||
* (125
|
|
||||||
* * value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t activateMediumHeaterPowerShortTicks(uint16_t& temperatureTicks,
|
|
||||||
uint16_t& humidityTicks);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief activateLowestHeaterPowerLongTicks
|
|
||||||
*
|
|
||||||
* SHT4x command to activate lowest heater power and perform a single shot
|
|
||||||
* high precision measurement for 1s.
|
|
||||||
*
|
|
||||||
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
|
|
||||||
* celsius by (175 * value / 65535) - 45
|
|
||||||
* @param[out] humidityTicks Humidity ticks. Convert to degrees celsius by
|
|
||||||
* (125
|
|
||||||
* * value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t activateLowestHeaterPowerLongTicks(uint16_t& temperatureTicks,
|
|
||||||
uint16_t& humidityTicks);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief activateLowestHeaterPowerShortTicks
|
|
||||||
*
|
|
||||||
* SHT4x command to activate lowest heater power and perform a single shot
|
|
||||||
* high precision measurement for 0.1s.
|
|
||||||
*
|
|
||||||
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
|
|
||||||
* celsius by (175 * value / 65535) - 45
|
|
||||||
* @param[out] humidityTicks Humidity ticks. Convert to degrees celsius by
|
|
||||||
* (125
|
|
||||||
* * value / 65535) - 6
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t activateLowestHeaterPowerShortTicks(uint16_t& temperatureTicks,
|
|
||||||
uint16_t& humidityTicks);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief serialNumber
|
|
||||||
*
|
|
||||||
* Read out the serial number
|
|
||||||
*
|
|
||||||
* @param[out] serialNumber Unique serial number
|
|
||||||
*
|
|
||||||
* @note Each sensor has a unique serial number that is assigned by
|
|
||||||
* Sensirion during production.It is stored in the one-time-programmable
|
|
||||||
* memory and cannot be manipulated after production.
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t serialNumber(uint32_t& serialNumber);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief softReset
|
|
||||||
*
|
|
||||||
* Perform a soft reset.
|
|
||||||
*
|
|
||||||
* @note A reset of the sensor can be achieved in three ways: • Soft reset:
|
|
||||||
* use this function • I2C general call: all devices on the I2C bus are
|
|
||||||
* reset by sending the command 0x06 to the I2C address 0x00. • Power down
|
|
||||||
* (incl.pulling SCL and SDA low)
|
|
||||||
*
|
|
||||||
* @return error_code 0 on success, an error code otherwise.
|
|
||||||
*/
|
|
||||||
int16_t softReset();
|
|
||||||
|
|
||||||
private:
|
|
||||||
TwoWire* _i2cBus = nullptr;
|
|
||||||
uint8_t _i2cAddress = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief signalTemperature
|
|
||||||
*
|
|
||||||
* @param[in] temperatureTicks
|
|
||||||
*
|
|
||||||
* @return Converted from ticks to degrees celsius by (175 * ticks_value /
|
|
||||||
* 65535) - 45
|
|
||||||
*/
|
|
||||||
float signalTemperature(uint16_t temperatureTicks);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief signalHumidity
|
|
||||||
*
|
|
||||||
* @param[in] humidityTicks
|
|
||||||
*
|
|
||||||
* @return Converted from ticks to percent relative humdity by (125 *
|
|
||||||
* ticks_value / 65535) - 6
|
|
||||||
*/
|
|
||||||
float signalHumidity(uint16_t humidityTicks);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SENSIRIONI2CSHT4X_H
|
|
@ -1,19 +1,17 @@
|
|||||||
BSD 3-Clause License
|
Copyright (c) 2018, Sensirion AG
|
||||||
|
|
||||||
Copyright (c) 2023, Sensirion AG
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
list of conditions and the following disclaimer.
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
this list of conditions and the following disclaimer in the documentation
|
this list of conditions and the following disclaimer in the documentation
|
||||||
and/or other materials provided with the distribution.
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
3. Neither the name of the copyright holder nor the names of its
|
* Neither the name of Sensirion AG nor the names of its
|
||||||
contributors may be used to endorse or promote products derived from
|
contributors may be used to endorse or promote products derived from
|
||||||
this software without specific prior written permission.
|
this software without specific prior written permission.
|
||||||
|
|
72
src/library/arduino-sht/README.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# arduino-sht
|
||||||
|
Repository for Sensirion humidity and temperature sensor support on Arduino
|
||||||
|
|
||||||
|
## Supported sensors:
|
||||||
|
- SHTC1
|
||||||
|
- SHTC3
|
||||||
|
- SHTW1
|
||||||
|
- SHTW2
|
||||||
|
- SHT2x (SHT20, SHT21, SHT25)
|
||||||
|
- SHT3x-DIS (I2C)
|
||||||
|
- SHT3x-ARP (ratiometric analog voltage output)
|
||||||
|
- SHT85
|
||||||
|
- SHT4x
|
||||||
|
|
||||||
|
For <code><a href="https://github.com/Sensirion/arduino-i2c-sht3x">sht3x</a></code> and <code><a href="https://github.com/Sensirion/arduino-i2c-sht4x">sht4x</a></code> there are specific drivers available in separate repositories.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
The recommended way to install ```arduino-sht``` is through the Library
|
||||||
|
Manager of the Arduino IDE. To access it, go to the ```Tools``` menu and
|
||||||
|
select ```Manage Libraries...```, and search for the library name there.
|
||||||
|
|
||||||
|
If you prefer to install it manually, you can download either via git or from
|
||||||
|
the releases page and place it in your Arduino/libraries directory. After
|
||||||
|
restarting the Arduino IDE, you will see the new SHTSensor menu items under
|
||||||
|
libraries and examples.
|
||||||
|
|
||||||
|
## Integrating it into your sketch
|
||||||
|
|
||||||
|
Assuming you installed the library as described above, the following steps are
|
||||||
|
necessary:
|
||||||
|
|
||||||
|
1. Import the Wire library like this: From the menu bar, select Sketch > Import
|
||||||
|
Library > Wire
|
||||||
|
1. Import the arduino-sht library: From the menu bar, select Sketch >
|
||||||
|
Import Library > arduino-sht
|
||||||
|
1. Create an instance of the `SHTSensor` class (`SHTSensor sht;`)
|
||||||
|
2. In `setup()`, make sure to init the Wire library with `Wire.begin()`
|
||||||
|
3. Also in `setup()`, call `sht.init()`
|
||||||
|
5. If you want to use the serial console, remember to initialize the Serial
|
||||||
|
library with `Serial.begin(9600)`
|
||||||
|
1. Call `sht.readSample()` in the `loop()` function, which reads a temperature
|
||||||
|
and humidity sample from the sensor
|
||||||
|
2. Use `sht.getHumidity()` and `sht.getTemperature()` to get the values from
|
||||||
|
the last sample
|
||||||
|
|
||||||
|
*Important:* `getHumidity()` and `getTemperature()` do *not* read a new sample
|
||||||
|
from the sensor, but return the values read last. To read a new sample, make
|
||||||
|
sure to call `readSample()`
|
||||||
|
|
||||||
|
### Using an custom or alternative I2C port/Wire instance
|
||||||
|
|
||||||
|
Some Arduino boards have multiple predefined I2C ports; generally, the second port will be called `Wire1`.
|
||||||
|
|
||||||
|
The `arduino-sht` library allows to use an alternative interface; to do so, pass the port you want to use as an argument to `sht.init()`, like this:
|
||||||
|
```
|
||||||
|
if (sht.init(Wire1)) {
|
||||||
|
Serial.print("init(): success\n");
|
||||||
|
} else {
|
||||||
|
Serial.print("init(): failed\n");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example projects
|
||||||
|
|
||||||
|
See example project
|
||||||
|
[sht-autodetect](examples/sht-autodetect/sht-autodetect.ino)
|
||||||
|
|
||||||
|
### Usage with multiple SHT31 sensors
|
||||||
|
|
||||||
|
See example project
|
||||||
|
[multiple-sht-sensors](examples/multiple-sht-sensors/multiple-sht-sensors.ino)
|
442
src/library/arduino-sht/SHTSensor.cpp
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Sensirion AG <andreas.brauchli@sensirion.com>
|
||||||
|
* Copyright (c) 2015-2016, Johannes Winkelmann <jw@smts.ch>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the Sensirion AG nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "SHTSensor.h"
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// class SHTSensorDriver
|
||||||
|
//
|
||||||
|
|
||||||
|
SHTSensorDriver::~SHTSensorDriver()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SHTSensorDriver::readSample()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// class SHTI2cSensor
|
||||||
|
//
|
||||||
|
|
||||||
|
const uint8_t SHTI2cSensor::EXPECTED_DATA_SIZE = 6;
|
||||||
|
|
||||||
|
bool SHTI2cSensor::readFromI2c(TwoWire & wire,
|
||||||
|
uint8_t i2cAddress,
|
||||||
|
const uint8_t *i2cCommand,
|
||||||
|
uint8_t commandLength, uint8_t *data,
|
||||||
|
uint8_t dataLength,
|
||||||
|
uint8_t duration)
|
||||||
|
{
|
||||||
|
wire.beginTransmission(i2cAddress);
|
||||||
|
for (int i = 0; i < commandLength; ++i) {
|
||||||
|
if (wire.write(i2cCommand[i]) != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wire.endTransmission() != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(duration);
|
||||||
|
|
||||||
|
wire.requestFrom(i2cAddress, dataLength);
|
||||||
|
|
||||||
|
// check if the same number of bytes are received that are requested.
|
||||||
|
if (wire.available() != dataLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < dataLength; ++i) {
|
||||||
|
data[i] = wire.read();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SHTI2cSensor::crc8(const uint8_t *data, uint8_t len, uint8_t crcInit)
|
||||||
|
{
|
||||||
|
// adapted from SHT21 sample code from
|
||||||
|
// http://www.sensirion.com/en/products/humidity-temperature/download-center/
|
||||||
|
|
||||||
|
uint8_t crc = crcInit;
|
||||||
|
uint8_t byteCtr;
|
||||||
|
for (byteCtr = 0; byteCtr < len; ++byteCtr) {
|
||||||
|
crc ^= data[byteCtr];
|
||||||
|
for (uint8_t bit = 8; bit > 0; --bit) {
|
||||||
|
if (crc & 0x80) {
|
||||||
|
crc = (crc << 1) ^ 0x31;
|
||||||
|
} else {
|
||||||
|
crc = (crc << 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SHTI2cSensor::readSample()
|
||||||
|
{
|
||||||
|
uint8_t data[EXPECTED_DATA_SIZE];
|
||||||
|
uint8_t cmd[mCmd_Size];
|
||||||
|
|
||||||
|
cmd[0] = mI2cCommand >> 8;
|
||||||
|
//is omitted for SHT4x Sensors
|
||||||
|
cmd[1] = mI2cCommand & 0xff;
|
||||||
|
|
||||||
|
if (!readFromI2c(mWire, mI2cAddress, cmd, mCmd_Size, data,
|
||||||
|
EXPECTED_DATA_SIZE, mDuration)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Important: assuming each 2 byte of data is followed by 1 byte of CRC
|
||||||
|
|
||||||
|
// check CRC for both RH and T
|
||||||
|
if (crc8(&data[0], 2) != data[2] || crc8(&data[3], 2) != data[5]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to Temperature/Humidity
|
||||||
|
uint16_t val;
|
||||||
|
val = (data[0] << 8) + data[1];
|
||||||
|
mTemperature = mA + mB * (val / mC);
|
||||||
|
|
||||||
|
val = (data[3] << 8) + data[4];
|
||||||
|
mHumidity = mX + mY * (val / mZ);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// class SHTC1Sensor
|
||||||
|
//
|
||||||
|
|
||||||
|
class SHTC1Sensor : public SHTI2cSensor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SHTC1Sensor(TwoWire & wire)
|
||||||
|
// clock stretching disabled, high precision, T first
|
||||||
|
: SHTI2cSensor(0x70, 0x7866, 15, -45, 175, 65535, 0, 100, 65535, 2, wire)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// class SHT2xSensor (SHT20, SHT21, SHT25)
|
||||||
|
//
|
||||||
|
|
||||||
|
class SHT2xSensor : public SHTI2cSensor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SHT2xSensor(TwoWire &wire)
|
||||||
|
// clock stretching disabled
|
||||||
|
: SHTI2cSensor(0x40, // i2cAddress
|
||||||
|
0xF3F5, // i2cCommand Hi: T, Lo: RH
|
||||||
|
85, // duration
|
||||||
|
-46.85, // a (sht_t_poly1)
|
||||||
|
175.72, // b (sht_t_poly2)
|
||||||
|
65536.0, // c (sht_t_poly3)
|
||||||
|
-6.0, // x (sht_h_poly1)
|
||||||
|
125.0, // y (sht_h_poly2)
|
||||||
|
65536.0, // z (sht_h_poly3)
|
||||||
|
1, // cmd_Size
|
||||||
|
wire)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readSample() override
|
||||||
|
{
|
||||||
|
uint8_t data[EXPECTED_DATA_SIZE];
|
||||||
|
uint8_t cmd[mCmd_Size];
|
||||||
|
|
||||||
|
// SHT2x sends T and RH in two separate commands (different to other sensors)
|
||||||
|
// so we have to spit the command into two bytes and
|
||||||
|
// have to read from I2C two times with EXPECTED_DATA_SIZE / 2
|
||||||
|
|
||||||
|
// Upper byte is T for SHT2x Sensors
|
||||||
|
cmd[0] = mI2cCommand >> 8;
|
||||||
|
// Lower byte is RH for SHT2x Sensors
|
||||||
|
cmd[1] = mI2cCommand & 0xff;
|
||||||
|
|
||||||
|
// read T from SHT2x Sensor
|
||||||
|
if (!readFromI2c(mWire, mI2cAddress, cmd, mCmd_Size, data,
|
||||||
|
EXPECTED_DATA_SIZE / 2, mDuration)) {
|
||||||
|
DEBUG_SHT("SHT2x readFromI2c(T) false\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// read RH from SHT2x Sensor
|
||||||
|
if (!readFromI2c(mWire, mI2cAddress, &cmd[1], mCmd_Size, &data[3],
|
||||||
|
EXPECTED_DATA_SIZE / 2, mDuration)) {
|
||||||
|
DEBUG_SHT("SHT2x readFromI2c(RH) false\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Important: assuming each 2 byte of data is followed by 1 byte of CRC
|
||||||
|
|
||||||
|
// check CRC for both RH and T with a crc init value of 0
|
||||||
|
if (crc8(&data[0], 2, 0) != data[2] || crc8(&data[3], 2, 0) != data[5]) {
|
||||||
|
DEBUG_SHT("SHT2x crc8 false\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check status bits [1..0] (see datasheet)
|
||||||
|
// bit 0: not used, bit 1: measurement type (0: temperature, 1 humidity)
|
||||||
|
if (((data[1] & 0x02) != 0x00) || ((data[4] & 0x02) != 0x02)) {
|
||||||
|
DEBUG_SHT("SHT2x status bits false\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to Temperature/Humidity
|
||||||
|
uint16_t val;
|
||||||
|
val = (data[0] << 8) + (data[1] & ~0x03); // get value and clear status bits [1..0]
|
||||||
|
mTemperature = mA + mB * (val / mC);
|
||||||
|
|
||||||
|
val = (data[3] << 8) + (data[4] & ~0x03); // get value and clear status bits [1..0]
|
||||||
|
mHumidity = mX + mY * (val / mZ);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// class SHT3xSensor
|
||||||
|
//
|
||||||
|
|
||||||
|
class SHT3xSensor : public SHTI2cSensor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static const uint16_t SHT3X_ACCURACY_HIGH = 0x2400;
|
||||||
|
static const uint16_t SHT3X_ACCURACY_MEDIUM = 0x240b;
|
||||||
|
static const uint16_t SHT3X_ACCURACY_LOW = 0x2416;
|
||||||
|
|
||||||
|
static const uint8_t SHT3X_ACCURACY_HIGH_DURATION = 15;
|
||||||
|
static const uint8_t SHT3X_ACCURACY_MEDIUM_DURATION = 6;
|
||||||
|
static const uint8_t SHT3X_ACCURACY_LOW_DURATION = 4;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const uint8_t SHT3X_I2C_ADDRESS_44 = 0x44;
|
||||||
|
static const uint8_t SHT3X_I2C_ADDRESS_45 = 0x45;
|
||||||
|
|
||||||
|
SHT3xSensor(TwoWire & wire, uint8_t i2cAddress = SHT3X_I2C_ADDRESS_44)
|
||||||
|
: SHTI2cSensor(i2cAddress, SHT3X_ACCURACY_HIGH,
|
||||||
|
SHT3X_ACCURACY_HIGH_DURATION,
|
||||||
|
-45, 175, 65535, 0, 100, 65535, 2, wire)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool setAccuracy(SHTSensor::SHTAccuracy newAccuracy)
|
||||||
|
{
|
||||||
|
switch (newAccuracy) {
|
||||||
|
case SHTSensor::SHT_ACCURACY_HIGH:
|
||||||
|
mI2cCommand = SHT3X_ACCURACY_HIGH;
|
||||||
|
mDuration = SHT3X_ACCURACY_HIGH_DURATION;
|
||||||
|
break;
|
||||||
|
case SHTSensor::SHT_ACCURACY_MEDIUM:
|
||||||
|
mI2cCommand = SHT3X_ACCURACY_MEDIUM;
|
||||||
|
mDuration = SHT3X_ACCURACY_MEDIUM_DURATION;
|
||||||
|
break;
|
||||||
|
case SHTSensor::SHT_ACCURACY_LOW:
|
||||||
|
mI2cCommand = SHT3X_ACCURACY_LOW;
|
||||||
|
mDuration = SHT3X_ACCURACY_LOW_DURATION;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// class SHT4xSensor
|
||||||
|
//
|
||||||
|
|
||||||
|
class SHT4xSensor : public SHTI2cSensor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static const uint16_t SHT4X_ACCURACY_HIGH = 0xFD00;
|
||||||
|
static const uint16_t SHT4X_ACCURACY_MEDIUM = 0xF600;
|
||||||
|
static const uint16_t SHT4X_ACCURACY_LOW = 0xE000;
|
||||||
|
|
||||||
|
static const uint8_t SHT4X_ACCURACY_HIGH_DURATION = 10;
|
||||||
|
static const uint8_t SHT4X_ACCURACY_MEDIUM_DURATION = 4;
|
||||||
|
static const uint8_t SHT4X_ACCURACY_LOW_DURATION = 2;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const uint8_t SHT4X_I2C_ADDRESS_44 = 0x44;
|
||||||
|
static const uint8_t SHT4X_I2C_ADDRESS_45 = 0x45;
|
||||||
|
|
||||||
|
SHT4xSensor(TwoWire & wire, uint8_t i2cAddress = SHT4X_I2C_ADDRESS_44)
|
||||||
|
: SHTI2cSensor(i2cAddress, SHT4X_ACCURACY_HIGH,
|
||||||
|
SHT4X_ACCURACY_HIGH_DURATION,
|
||||||
|
-45, 175, 65535, -6, 125, 65535, 1, wire)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool setAccuracy(SHTSensor::SHTAccuracy newAccuracy)
|
||||||
|
{
|
||||||
|
switch (newAccuracy) {
|
||||||
|
case SHTSensor::SHT_ACCURACY_HIGH:
|
||||||
|
mI2cCommand = SHT4X_ACCURACY_HIGH;
|
||||||
|
mDuration = SHT4X_ACCURACY_HIGH_DURATION;
|
||||||
|
break;
|
||||||
|
case SHTSensor::SHT_ACCURACY_MEDIUM:
|
||||||
|
mI2cCommand = SHT4X_ACCURACY_MEDIUM;
|
||||||
|
mDuration = SHT4X_ACCURACY_MEDIUM_DURATION;
|
||||||
|
break;
|
||||||
|
case SHTSensor::SHT_ACCURACY_LOW:
|
||||||
|
mI2cCommand = SHT4X_ACCURACY_LOW;
|
||||||
|
mDuration = SHT4X_ACCURACY_LOW_DURATION;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// class SHT3xAnalogSensor
|
||||||
|
//
|
||||||
|
|
||||||
|
float SHT3xAnalogSensor::readHumidity()
|
||||||
|
{
|
||||||
|
float max_adc = (float)((1 << mReadResolutionBits) - 1);
|
||||||
|
return -12.5f + 125 * (analogRead(mHumidityAdcPin) / max_adc);
|
||||||
|
}
|
||||||
|
|
||||||
|
float SHT3xAnalogSensor::readTemperature()
|
||||||
|
{
|
||||||
|
float max_adc = (float)((1 << mReadResolutionBits) - 1);
|
||||||
|
return -66.875f + 218.75f * (analogRead(mTemperatureAdcPin) / max_adc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// class SHTSensor
|
||||||
|
//
|
||||||
|
|
||||||
|
const SHTSensor::SHTSensorType SHTSensor::AUTO_DETECT_SENSORS[] = {
|
||||||
|
SHT4X, // IMPORTANT: SHT4x needs to be probed before the SHT3x, since they
|
||||||
|
// share their I2C address, and probing for an SHT3x can cause the
|
||||||
|
// first reading of and SHT4x to be off.
|
||||||
|
// see https://github.com/Sensirion/arduino-sht/issues/27
|
||||||
|
|
||||||
|
SHT2X,
|
||||||
|
SHT3X,
|
||||||
|
SHT3X_ALT,
|
||||||
|
SHTC1
|
||||||
|
};
|
||||||
|
const float SHTSensor::TEMPERATURE_INVALID = NAN;
|
||||||
|
const float SHTSensor::HUMIDITY_INVALID = NAN;
|
||||||
|
|
||||||
|
bool SHTSensor::init(TwoWire & wire)
|
||||||
|
{
|
||||||
|
if (mSensor != NULL) {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(mSensorType) {
|
||||||
|
case SHT2X:
|
||||||
|
mSensor = new SHT2xSensor(wire);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHT3X:
|
||||||
|
case SHT85:
|
||||||
|
mSensor = new SHT3xSensor(wire);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHT3X_ALT:
|
||||||
|
mSensor = new SHT3xSensor(wire, SHT3xSensor::SHT3X_I2C_ADDRESS_45);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHTW1:
|
||||||
|
case SHTW2:
|
||||||
|
case SHTC1:
|
||||||
|
case SHTC3:
|
||||||
|
mSensor = new SHTC1Sensor(wire);
|
||||||
|
break;
|
||||||
|
case SHT4X:
|
||||||
|
mSensor = new SHT4xSensor(wire);
|
||||||
|
break;
|
||||||
|
case AUTO_DETECT:
|
||||||
|
{
|
||||||
|
bool detected = false;
|
||||||
|
for (unsigned int i = 0;
|
||||||
|
i < sizeof(AUTO_DETECT_SENSORS) / sizeof(AUTO_DETECT_SENSORS[0]);
|
||||||
|
++i) {
|
||||||
|
mSensorType = AUTO_DETECT_SENSORS[i];
|
||||||
|
delay(40); // TODO: this was necessary to make SHT4x autodetect work; revisit to find root cause
|
||||||
|
if (init(wire)) {
|
||||||
|
detected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!detected) {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to finish the initialization, attempt to read to make sure the communication works
|
||||||
|
// Note: readSample() will check for a NULL mSensor in case auto detect failed
|
||||||
|
return readSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SHTSensor::readSample()
|
||||||
|
{
|
||||||
|
if (!mSensor || !mSensor->readSample())
|
||||||
|
return false;
|
||||||
|
mTemperature = mSensor->mTemperature;
|
||||||
|
mHumidity = mSensor->mHumidity;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SHTSensor::setAccuracy(SHTAccuracy newAccuracy)
|
||||||
|
{
|
||||||
|
if (!mSensor)
|
||||||
|
return false;
|
||||||
|
return mSensor->setAccuracy(newAccuracy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHTSensor::cleanup()
|
||||||
|
{
|
||||||
|
if (mSensor) {
|
||||||
|
delete mSensor;
|
||||||
|
mSensor = NULL;
|
||||||
|
}
|
||||||
|
}
|
309
src/library/arduino-sht/SHTSensor.h
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Sensirion AG <andreas.brauchli@sensirion.com>
|
||||||
|
* Copyright (c) 2015-2016, Johannes Winkelmann <jw@smts.ch>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the Sensirion AG nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHTSENSOR_H
|
||||||
|
#define SHTSENSOR_H
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
//#define DEBUG_SHT_SENSOR
|
||||||
|
|
||||||
|
#ifdef DEBUG_SHT_SENSOR
|
||||||
|
#ifdef DEBUG_ESP_PORT
|
||||||
|
#define DEBUG_SHT(f) do { DEBUG_ESP_PORT.print(PSTR(f)); } while (0)
|
||||||
|
#else
|
||||||
|
#define DEBUG_SHT(f) do { Serial.print(PSTR(f)); } while (0)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define DEBUG_SHT(x...) do { (void)0; } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
class SHTSensorDriver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Official interface for Sensirion SHT Sensors
|
||||||
|
*/
|
||||||
|
class SHTSensor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Enum of the supported Digital Sensirion SHT Sensors.
|
||||||
|
* For analog sensors, see SHT3xAnalogSensor.
|
||||||
|
* Using the special AUTO_DETECT sensor causes all i2c sensors to be
|
||||||
|
* probed. The first matching sensor will then be used.
|
||||||
|
*/
|
||||||
|
enum SHTSensorType {
|
||||||
|
/** Automatically detect the sensor type (only i2c sensors listed above) */
|
||||||
|
AUTO_DETECT,
|
||||||
|
// i2c Sensors:
|
||||||
|
/** SHT3x-DIS with ADDR (sensor pin 2) connected to VSS (default) */
|
||||||
|
SHT3X,
|
||||||
|
SHT85,
|
||||||
|
/** SHT3x-DIS with ADDR (sensor pin 2) connected to VDD */
|
||||||
|
SHT3X_ALT,
|
||||||
|
SHTC1,
|
||||||
|
SHTC3,
|
||||||
|
SHTW1,
|
||||||
|
SHTW2,
|
||||||
|
SHT4X,
|
||||||
|
SHT2X
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accuracy setting of measurement.
|
||||||
|
* Not all sensors support changing the sampling accuracy.
|
||||||
|
*/
|
||||||
|
enum SHTAccuracy {
|
||||||
|
/** Highest repeatability at the cost of slower measurement */
|
||||||
|
SHT_ACCURACY_HIGH,
|
||||||
|
/** Balanced repeatability and speed of measurement */
|
||||||
|
SHT_ACCURACY_MEDIUM,
|
||||||
|
/** Fastest measurement but lowest repeatability */
|
||||||
|
SHT_ACCURACY_LOW
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Value reported by getHumidity() when the sensor is not initialized */
|
||||||
|
static const float HUMIDITY_INVALID;
|
||||||
|
/** Value reported by getTemperature() when the sensor is not initialized */
|
||||||
|
static const float TEMPERATURE_INVALID;
|
||||||
|
/**
|
||||||
|
* Auto-detectable sensor types.
|
||||||
|
* Note that the SHTC3, SHTW1 and SHTW2 share exactly the same driver as the SHTC1
|
||||||
|
* and are thus not listed individually.
|
||||||
|
*/
|
||||||
|
static const SHTSensorType AUTO_DETECT_SENSORS[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a new SHTSensor
|
||||||
|
* By default, the i2c bus is queried for known SHT Sensors. To address
|
||||||
|
* a specific sensor, set the `sensorType'.
|
||||||
|
*/
|
||||||
|
SHTSensor(SHTSensorType sensorType = AUTO_DETECT)
|
||||||
|
: mSensorType(sensorType),
|
||||||
|
mSensor(NULL),
|
||||||
|
mTemperature(SHTSensor::TEMPERATURE_INVALID),
|
||||||
|
mHumidity(SHTSensor::HUMIDITY_INVALID)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~SHTSensor() {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the sensor driver, and probe for the sensor on the bus
|
||||||
|
*
|
||||||
|
* If SHTSensor() was created with an empty constructor or with 'sensorType'
|
||||||
|
* AUTO_DETECT, init() will also try to automatically detect a sensor.
|
||||||
|
* Auto detection will stop as soon as the first sensor was found; if you have
|
||||||
|
* multiple sensor types on the bus, use the 'sensorType' argument of the
|
||||||
|
* constructor to control which sensor type will be instantiated.
|
||||||
|
*
|
||||||
|
* To read out the sensor use readSample(), followed by getTemperature() and
|
||||||
|
* getHumidity() to retrieve the values from the sample
|
||||||
|
*
|
||||||
|
* Returns true if communication with a sensor on the bus was successful, false otherwise
|
||||||
|
*/
|
||||||
|
bool init(TwoWire & wire = Wire);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read new values from the sensor
|
||||||
|
* After the call, use getTemperature() and getHumidity() to retrieve the
|
||||||
|
* values
|
||||||
|
* Returns true if the sample was read and the values are cached
|
||||||
|
*/
|
||||||
|
bool readSample();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the relative humidity in percent read from the last sample
|
||||||
|
* Use readSample() to trigger a new sensor reading
|
||||||
|
*/
|
||||||
|
float getHumidity() const {
|
||||||
|
return mHumidity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the temperature in Celsius read from the last sample
|
||||||
|
* Use readSample() to trigger a new sensor reading
|
||||||
|
*/
|
||||||
|
float getTemperature() const {
|
||||||
|
return mTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the sensor accurancy, if supported by the sensor
|
||||||
|
* Returns true if the accuracy was changed
|
||||||
|
*/
|
||||||
|
bool setAccuracy(SHTAccuracy newAccuracy);
|
||||||
|
|
||||||
|
SHTSensorType mSensorType;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
|
||||||
|
SHTSensorDriver *mSensor;
|
||||||
|
float mTemperature;
|
||||||
|
float mHumidity;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** Abstract class for a digital SHT Sensor driver */
|
||||||
|
class SHTSensorDriver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~SHTSensorDriver() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the sensor accuracy.
|
||||||
|
* Returns false if the sensor does not support changing the accuracy
|
||||||
|
*/
|
||||||
|
virtual bool setAccuracy(SHTSensor::SHTAccuracy /* newAccuracy */) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true if the next sample was read and the values are cached */
|
||||||
|
virtual bool readSample();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the relative humidity in percent read from the last sample
|
||||||
|
* Use readSample() to trigger a new sensor reading
|
||||||
|
*/
|
||||||
|
float getHumidity() const {
|
||||||
|
return mHumidity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the humidity in percent read from the last sample
|
||||||
|
* Use readSample() to trigger a new sensor reading
|
||||||
|
*/
|
||||||
|
float getTemperature() const {
|
||||||
|
return mTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
float mTemperature;
|
||||||
|
float mHumidity;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Base class for i2c SHT Sensor drivers */
|
||||||
|
class SHTI2cSensor : public SHTSensorDriver {
|
||||||
|
public:
|
||||||
|
/** Size of i2c commands to send */
|
||||||
|
|
||||||
|
|
||||||
|
/** Size of i2c replies to expect */
|
||||||
|
static const uint8_t EXPECTED_DATA_SIZE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for i2c SHT Sensors
|
||||||
|
* Takes the `i2cAddress' to read, the `i2cCommand' issues when sampling
|
||||||
|
* the sensor and the values `a', `b', `c' to convert the fixed-point
|
||||||
|
* temperature value received by the sensor to a floating point value using
|
||||||
|
* the formula: temperature = a + b * (rawTemperature / c)
|
||||||
|
* and the values `x' and `y' to convert the fixed-point humidity value
|
||||||
|
* received by the sensor to a floating point value using the formula:
|
||||||
|
* humidity = x + y * (rawHumidity / z)
|
||||||
|
* duration is the duration in milliseconds of one measurement
|
||||||
|
*/
|
||||||
|
SHTI2cSensor(uint8_t i2cAddress, uint16_t i2cCommand, uint8_t duration,
|
||||||
|
float a, float b, float c,
|
||||||
|
float x, float y, float z, uint8_t cmd_Size,
|
||||||
|
TwoWire & wire = Wire)
|
||||||
|
: mI2cAddress(i2cAddress), mI2cCommand(i2cCommand), mDuration(duration),
|
||||||
|
mA(a), mB(b), mC(c), mX(x), mY(y), mZ(z), mCmd_Size(cmd_Size),
|
||||||
|
mWire(wire)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~SHTI2cSensor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool readSample();
|
||||||
|
|
||||||
|
uint8_t mI2cAddress;
|
||||||
|
uint16_t mI2cCommand;
|
||||||
|
uint8_t mDuration;
|
||||||
|
float mA;
|
||||||
|
float mB;
|
||||||
|
float mC;
|
||||||
|
float mX;
|
||||||
|
float mY;
|
||||||
|
float mZ;
|
||||||
|
uint8_t mCmd_Size;
|
||||||
|
TwoWire & mWire;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crcInit = 0xff);
|
||||||
|
static bool readFromI2c(TwoWire & wire,
|
||||||
|
uint8_t i2cAddress,
|
||||||
|
const uint8_t *i2cCommand,
|
||||||
|
uint8_t commandLength, uint8_t *data,
|
||||||
|
uint8_t dataLength, uint8_t duration);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SHT3xAnalogSensor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a new Sensirion SHT3x Analog sensor driver instance.
|
||||||
|
* The required paramters are `humidityPin` and `temperaturePin`
|
||||||
|
* An optional `readResolutionBits' can be set since the Arduino/Genuino Zero
|
||||||
|
* support 12bit precision analog readings. By default, 10 bit precision is
|
||||||
|
* used.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* SHT3xAnalogSensor sht3xAnalog(HUMIDITY_PIN, TEMPERATURE_PIN);
|
||||||
|
* float humidity = sht.readHumidity();
|
||||||
|
* float temperature = sht.readTemperature();
|
||||||
|
*/
|
||||||
|
SHT3xAnalogSensor(uint8_t humidityPin, uint8_t temperaturePin,
|
||||||
|
uint8_t readResolutionBits = 10)
|
||||||
|
: mHumidityAdcPin(humidityPin), mTemperatureAdcPin(temperaturePin),
|
||||||
|
mReadResolutionBits(readResolutionBits)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~SHT3xAnalogSensor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float readHumidity();
|
||||||
|
float readTemperature();
|
||||||
|
|
||||||
|
uint8_t mHumidityAdcPin;
|
||||||
|
uint8_t mTemperatureAdcPin;
|
||||||
|
uint8_t mReadResolutionBits;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* SHTSENSOR_H */
|
6
src/library/arduino-sht/arduino-sht.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef ARDUINO_SHT_H
|
||||||
|
#define ARDUINO_SHT_H
|
||||||
|
|
||||||
|
#include "SHTSensor.h"
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,62 @@
|
|||||||
|
#include <Wire.h>
|
||||||
|
#include "SHTSensor.h"
|
||||||
|
|
||||||
|
// Note that all i2c devices sharing one bus must have distinct addresses. Thus
|
||||||
|
// this example only works with sensors that have distinct i2c addresses (e.g.
|
||||||
|
// SHT3x and SHT3x_alt, or SHT3x and SHTC3).
|
||||||
|
// Make sure not to use auto-detection as it will only pick up one sensor.
|
||||||
|
|
||||||
|
// Sensor with normal i2c address
|
||||||
|
// Sensor 1 with address pin pulled to GND
|
||||||
|
SHTSensor sht1(SHTSensor::SHT3X);
|
||||||
|
|
||||||
|
// Sensor with alternative i2c address
|
||||||
|
// Sensor 2 with address pin pulled to Vdd
|
||||||
|
SHTSensor sht2(SHTSensor::SHT3X_ALT);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// put your setup code here, to run once:
|
||||||
|
Wire.begin();
|
||||||
|
Serial.begin(9600);
|
||||||
|
delay(1000); // let serial console settle
|
||||||
|
|
||||||
|
// init on a specific sensor type (i.e. without auto detecting),
|
||||||
|
// does not check if the sensor is responding and will thus always succeed.
|
||||||
|
|
||||||
|
// initialize sensor with normal i2c-address
|
||||||
|
sht1.init();
|
||||||
|
|
||||||
|
// initialize sensor with alternative i2c-address
|
||||||
|
sht2.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// put your main code here, to run repeatedly:
|
||||||
|
// read from first sensor
|
||||||
|
if (sht1.readSample()) {
|
||||||
|
Serial.print("SHT1 :\n");
|
||||||
|
Serial.print(" RH: ");
|
||||||
|
Serial.print(sht1.getHumidity(), 2);
|
||||||
|
Serial.print("\n");
|
||||||
|
Serial.print(" T: ");
|
||||||
|
Serial.print(sht1.getTemperature(), 2);
|
||||||
|
Serial.print("\n");
|
||||||
|
} else {
|
||||||
|
Serial.print("Sensor 1: Error in readSample()\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// read from second sensor
|
||||||
|
if (sht2.readSample()) {
|
||||||
|
Serial.print("SHT2:\n");
|
||||||
|
Serial.print(" RH: ");
|
||||||
|
Serial.print(sht2.getHumidity(), 2);
|
||||||
|
Serial.print("\n");
|
||||||
|
Serial.print(" T: ");
|
||||||
|
Serial.print(sht2.getTemperature(), 2);
|
||||||
|
Serial.print("\n");
|
||||||
|
} else {
|
||||||
|
Serial.print("Sensor 2: Error in readSample()\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
#include "SHTSensor.h"
|
||||||
|
|
||||||
|
SHTSensor sht;
|
||||||
|
// To use a specific sensor instead of probing the bus use this command:
|
||||||
|
// SHTSensor sht(SHTSensor::SHT3X);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// put your setup code here, to run once:
|
||||||
|
|
||||||
|
Wire.begin();
|
||||||
|
Serial.begin(9600);
|
||||||
|
delay(1000); // let serial console settle
|
||||||
|
|
||||||
|
if (sht.init()) {
|
||||||
|
Serial.print("init(): success\n");
|
||||||
|
} else {
|
||||||
|
Serial.print("init(): failed\n");
|
||||||
|
}
|
||||||
|
sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM); // only supported by SHT3x
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// put your main code here, to run repeatedly:
|
||||||
|
|
||||||
|
if (sht.readSample()) {
|
||||||
|
Serial.print("SHT:\n");
|
||||||
|
Serial.print(" RH: ");
|
||||||
|
Serial.print(sht.getHumidity(), 2);
|
||||||
|
Serial.print("\n");
|
||||||
|
Serial.print(" T: ");
|
||||||
|
Serial.print(sht.getTemperature(), 2);
|
||||||
|
Serial.print("\n");
|
||||||
|
} else {
|
||||||
|
Serial.print("Error in readSample()\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
}
|
27
src/library/arduino-sht/examples/sht3xanalog/sht3xanalog.ino
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
#include "SHTSensor.h"
|
||||||
|
|
||||||
|
SHT3xAnalogSensor sht3xAnalog(A0, A1);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// put your setup code here, to run once:
|
||||||
|
|
||||||
|
Wire.begin();
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
delay(1000); // let serial console settle
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
Serial.print("SHT3x Analog:\n");
|
||||||
|
Serial.print(" RH: ");
|
||||||
|
Serial.print(sht3xAnalog.readHumidity(), 2);
|
||||||
|
Serial.print("\n");
|
||||||
|
Serial.print(" T: ");
|
||||||
|
Serial.print(sht3xAnalog.readTemperature(), 2);
|
||||||
|
Serial.print("\n");
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
}
|
29
src/library/arduino-sht/keywords.txt
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#######################################
|
||||||
|
# Syntax Coloring Map For arduino-sht
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Datatypes (KEYWORD1)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
SHTSensorType KEYWORD1
|
||||||
|
SHTAccuracy KEYWORD1
|
||||||
|
SHTSensor KEYWORD1
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Methods and Functions (KEYWORD2)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
init KEYWORD2
|
||||||
|
readSample KEYWORD2
|
||||||
|
getHumidity KEYWORD2
|
||||||
|
getTemperature KEYWORD2
|
||||||
|
setAccuracy KEYWORD2
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Instances (KEYWORD2)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Constants (LITERAL1)
|
||||||
|
#######################################
|
9
src/library/arduino-sht/library.properties
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
name=arduino-sht
|
||||||
|
version=1.2.3
|
||||||
|
author=Johannes Winkelmann, Andreas Brauchli
|
||||||
|
maintainer=Johannes Winkelmann <jwi@sensirion.com>
|
||||||
|
sentence=Support for Sensirion's humidity and temperature sensors.
|
||||||
|
paragraph=Supported sensors: SHTC1, SHTC3, SHTW1, SHTW2, SHT3x-DIS (I2C), SHT2x, SHT85, SHT3x-ARP, SHT4x
|
||||||
|
category=Sensors
|
||||||
|
url=https://developer.sensirion.com
|
||||||
|
architectures=*
|
@ -3,9 +3,9 @@
|
|||||||
#include "esp32-hal-log.h"
|
#include "esp32-hal-log.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const BoardDef bsps[BOARD_DEF_MAX] = {
|
const BoardDef bsps[_BOARD_MAX] = {
|
||||||
/** BOARD_DIY_BASIC_KIT */
|
/** DIY_BASIC */
|
||||||
[BOARD_DIY_BASIC_KIT] =
|
[DIY_BASIC] =
|
||||||
{
|
{
|
||||||
.SenseAirS8 =
|
.SenseAirS8 =
|
||||||
{
|
{
|
||||||
@ -17,7 +17,7 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
|
|||||||
.supported = false,
|
.supported = false,
|
||||||
#endif
|
#endif
|
||||||
},
|
},
|
||||||
.PMS5003 =
|
.Pms5003 =
|
||||||
{
|
{
|
||||||
.uart_tx_pin = 14,
|
.uart_tx_pin = 14,
|
||||||
.uart_rx_pin = 12,
|
.uart_rx_pin = 12,
|
||||||
@ -70,10 +70,10 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
|
|||||||
.resetPin = -1,
|
.resetPin = -1,
|
||||||
.supported = false,
|
.supported = false,
|
||||||
},
|
},
|
||||||
.name = "BOARD_DIY_BASIC_KIT",
|
.name = "DIY_BASIC",
|
||||||
},
|
},
|
||||||
/** BOARD_DIY_PRO_INDOOR_V4_2 */
|
/** DIY_PRO_INDOOR_V4_2 */
|
||||||
[BOARD_DIY_PRO_INDOOR_V4_2] =
|
[DIY_PRO_INDOOR_V4_2] =
|
||||||
{
|
{
|
||||||
.SenseAirS8 =
|
.SenseAirS8 =
|
||||||
{
|
{
|
||||||
@ -85,7 +85,7 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
|
|||||||
.supported = false,
|
.supported = false,
|
||||||
#endif
|
#endif
|
||||||
},
|
},
|
||||||
.PMS5003 =
|
.Pms5003 =
|
||||||
{
|
{
|
||||||
.uart_tx_pin = 14,
|
.uart_tx_pin = 14,
|
||||||
.uart_rx_pin = 12,
|
.uart_rx_pin = 12,
|
||||||
@ -144,10 +144,10 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
|
|||||||
.resetPin = -1,
|
.resetPin = -1,
|
||||||
.supported = false,
|
.supported = false,
|
||||||
},
|
},
|
||||||
.name = "BOARD_DIY_PRO_INDOOR_V4_2",
|
.name = "DIY_PRO_INDOOR_V4_2",
|
||||||
},
|
},
|
||||||
/** BOARD_ONE_INDOOR_MONITOR_V9_0 */
|
/** ONE_INDOOR */
|
||||||
[BOARD_ONE_INDOOR_MONITOR_V9_0] =
|
[ONE_INDOOR] =
|
||||||
{
|
{
|
||||||
.SenseAirS8 =
|
.SenseAirS8 =
|
||||||
{
|
{
|
||||||
@ -160,7 +160,7 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
|
|||||||
#endif
|
#endif
|
||||||
},
|
},
|
||||||
/** Use UART0 don't use define pin number */
|
/** Use UART0 don't use define pin number */
|
||||||
.PMS5003 =
|
.Pms5003 =
|
||||||
{
|
{
|
||||||
.uart_tx_pin = -1,
|
.uart_tx_pin = -1,
|
||||||
.uart_rx_pin = -1,
|
.uart_rx_pin = -1,
|
||||||
@ -232,10 +232,10 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
|
|||||||
.supported = true,
|
.supported = true,
|
||||||
#endif
|
#endif
|
||||||
},
|
},
|
||||||
.name = "BOARD_ONE_INDOOR_MONITOR_V9_0",
|
.name = "ONE_INDOOR",
|
||||||
},
|
},
|
||||||
/** BOARD_OUTDOOR_MONITOR_V1_3 */
|
/** OPEN_AIR_OUTDOOR */
|
||||||
[BOARD_OUTDOOR_MONITOR_V1_3] = {
|
[OPEN_AIR_OUTDOOR] = {
|
||||||
.SenseAirS8 =
|
.SenseAirS8 =
|
||||||
{
|
{
|
||||||
.uart_tx_pin = 1,
|
.uart_tx_pin = 1,
|
||||||
@ -247,7 +247,7 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
|
|||||||
#endif
|
#endif
|
||||||
},
|
},
|
||||||
/** Use UART0 don't use define pin number */
|
/** Use UART0 don't use define pin number */
|
||||||
.PMS5003 =
|
.Pms5003 =
|
||||||
{
|
{
|
||||||
.uart_tx_pin = -1,
|
.uart_tx_pin = -1,
|
||||||
.uart_rx_pin = -1,
|
.uart_rx_pin = -1,
|
||||||
@ -319,7 +319,7 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
|
|||||||
.supported = true,
|
.supported = true,
|
||||||
#endif
|
#endif
|
||||||
},
|
},
|
||||||
.name = "BOARD_OUTDOOR_MONITOR_V1_3",
|
.name = "OPEN_AIR_OUTDOOR",
|
||||||
}};
|
}};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -329,7 +329,7 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
|
|||||||
* @return const BoardDef*
|
* @return const BoardDef*
|
||||||
*/
|
*/
|
||||||
const BoardDef *getBoardDef(BoardType def) {
|
const BoardDef *getBoardDef(BoardType def) {
|
||||||
if (def >= BOARD_DEF_MAX) {
|
if (def >= _BOARD_MAX) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return &bsps[def];
|
return &bsps[def];
|
||||||
@ -356,7 +356,7 @@ void printBoardDef(Stream *_debug) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (int i = 0; i < BOARD_DEF_MAX; i++) {
|
for (int i = 0; i < _BOARD_MAX; i++) {
|
||||||
bspPrintf("Board name: %s", bsps[i].name);
|
bspPrintf("Board name: %s", bsps[i].name);
|
||||||
bspPrintf("\tSensor CO2 S8:");
|
bspPrintf("\tSensor CO2 S8:");
|
||||||
bspPrintf("\t\tSupported: %d", bsps[i].SenseAirS8.supported);
|
bspPrintf("\t\tSupported: %d", bsps[i].SenseAirS8.supported);
|
||||||
@ -366,10 +366,10 @@ void printBoardDef(Stream *_debug) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bspPrintf("\tSensor PMS5003:");
|
bspPrintf("\tSensor PMS5003:");
|
||||||
bspPrintf("\t\tSupported: %d", bsps[i].PMS5003.supported);
|
bspPrintf("\t\tSupported: %d", bsps[i].Pms5003.supported);
|
||||||
if (bsps[i].PMS5003.supported) {
|
if (bsps[i].Pms5003.supported) {
|
||||||
bspPrintf("\t\tUART Tx: %d", bsps[i].PMS5003.uart_tx_pin);
|
bspPrintf("\t\tUART Tx: %d", bsps[i].Pms5003.uart_tx_pin);
|
||||||
bspPrintf("\t\tUART Rx: %d", bsps[i].PMS5003.uart_rx_pin);
|
bspPrintf("\t\tUART Rx: %d", bsps[i].Pms5003.uart_rx_pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
bspPrintf("\tI2C");
|
bspPrintf("\tI2C");
|
@ -13,14 +13,21 @@
|
|||||||
#define AgLog(c, ...) log_i(c, ##__VA_ARGS__)
|
#define AgLog(c, ...) log_i(c, ##__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Define Airgradient supported board type
|
||||||
|
*/
|
||||||
enum BoardType {
|
enum BoardType {
|
||||||
BOARD_DIY_BASIC_KIT = 0x00,
|
DIY_BASIC = 0x00,
|
||||||
BOARD_DIY_PRO_INDOOR_V4_2 = 0x01,
|
DIY_PRO_INDOOR_V4_2 = 0x01,
|
||||||
BOARD_ONE_INDOOR_MONITOR_V9_0 = 0x02,
|
ONE_INDOOR = 0x02,
|
||||||
BOARD_OUTDOOR_MONITOR_V1_3 = 0x03,
|
OPEN_AIR_OUTDOOR = 0x03,
|
||||||
BOARD_DEF_MAX
|
_BOARD_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Board definitions
|
||||||
|
*
|
||||||
|
*/
|
||||||
struct BoardDef {
|
struct BoardDef {
|
||||||
/** Board Support CO2 SenseS8 */
|
/** Board Support CO2 SenseS8 */
|
||||||
struct {
|
struct {
|
||||||
@ -34,7 +41,7 @@ struct BoardDef {
|
|||||||
const int uart_tx_pin; /** UART tx pin */
|
const int uart_tx_pin; /** UART tx pin */
|
||||||
const int uart_rx_pin; /** UART rx pin */
|
const int uart_rx_pin; /** UART rx pin */
|
||||||
const bool supported; /** Is BSP supported for this sensor */
|
const bool supported; /** Is BSP supported for this sensor */
|
||||||
} PMS5003;
|
} Pms5003;
|
||||||
|
|
||||||
/** I2C Bus */
|
/** I2C Bus */
|
||||||
struct {
|
struct {
|
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
#include "BoardDef.h"
|
#include "BoardDef.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The class define how to control external watchdog on ONE-V9 and
|
||||||
|
* Outdoor
|
||||||
|
*/
|
||||||
class HardwareWatchdog {
|
class HardwareWatchdog {
|
||||||
public:
|
public:
|
||||||
HardwareWatchdog(BoardType type);
|
HardwareWatchdog(BoardType type);
|
@ -18,7 +18,7 @@ LedBar::LedBar(BoardType type) : _boardType(type) {}
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void LedBar::begin(void) {
|
void LedBar::begin(void) {
|
||||||
if (this->_isInit) {
|
if (this->_isBegin) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,9 +36,9 @@ void LedBar::begin(void) {
|
|||||||
pixel()->begin();
|
pixel()->begin();
|
||||||
pixel()->clear();
|
pixel()->clear();
|
||||||
|
|
||||||
this->_isInit = true;
|
this->_isBegin = true;
|
||||||
|
|
||||||
AgLog("Init");
|
AgLog("Initialize");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,7 +48,7 @@ void LedBar::begin(void) {
|
|||||||
* @param red Color Red (0 - 255)
|
* @param red Color Red (0 - 255)
|
||||||
* @param green Color Green (0 - 255)
|
* @param green Color Green (0 - 255)
|
||||||
* @param blue Color Blue (0 - 255)
|
* @param blue Color Blue (0 - 255)
|
||||||
* @param ledNum Index of LED from 0 to getNumberOfLed() - 1
|
* @param ledNum Index of LED from 0 to getNumberOfLeds() - 1
|
||||||
*/
|
*/
|
||||||
void LedBar::setColor(uint8_t red, uint8_t green, uint8_t blue, int ledNum) {
|
void LedBar::setColor(uint8_t red, uint8_t green, uint8_t blue, int ledNum) {
|
||||||
if (this->ledNumInvalid(ledNum)) {
|
if (this->ledNumInvalid(ledNum)) {
|
||||||
@ -64,7 +64,7 @@ void LedBar::setColor(uint8_t red, uint8_t green, uint8_t blue, int ledNum) {
|
|||||||
* @param brightness Brightness (0 - 255)
|
* @param brightness Brightness (0 - 255)
|
||||||
*/
|
*/
|
||||||
void LedBar::setBrighness(uint8_t brightness) {
|
void LedBar::setBrighness(uint8_t brightness) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pixel()->setBrightness(brightness);
|
pixel()->setBrightness(brightness);
|
||||||
@ -75,16 +75,16 @@ void LedBar::setBrighness(uint8_t brightness) {
|
|||||||
*
|
*
|
||||||
* @return int Number of LED
|
* @return int Number of LED
|
||||||
*/
|
*/
|
||||||
int LedBar::getNumberOfLed(void) {
|
int LedBar::getNumberOfLeds(void) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this->_bsp->LED.rgbNum;
|
return this->_bsp->LED.rgbNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LedBar::checkInit(void) {
|
bool LedBar::isBegin(void) {
|
||||||
if (this->_isInit) {
|
if (this->_isBegin) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
AgLog("LED is not initialized");
|
AgLog("LED is not initialized");
|
||||||
@ -92,7 +92,7 @@ bool LedBar::checkInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool LedBar::ledNumInvalid(int ledNum) {
|
bool LedBar::ledNumInvalid(int ledNum) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
#include "BoardDef.h"
|
#include "BoardDef.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The class define how to handle the RGB LED bar
|
||||||
|
*
|
||||||
|
*/
|
||||||
class LedBar {
|
class LedBar {
|
||||||
public:
|
public:
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
@ -16,13 +20,13 @@ public:
|
|||||||
void setColor(uint8_t red, uint8_t green, uint8_t blue, int ledNum);
|
void setColor(uint8_t red, uint8_t green, uint8_t blue, int ledNum);
|
||||||
void setColor(uint8_t red, uint8_t green, uint8_t blue);
|
void setColor(uint8_t red, uint8_t green, uint8_t blue);
|
||||||
void setBrighness(uint8_t brightness);
|
void setBrighness(uint8_t brightness);
|
||||||
int getNumberOfLed(void);
|
int getNumberOfLeds(void);
|
||||||
void show(void);
|
void show(void);
|
||||||
void clear(void);
|
void clear(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const BoardDef *_bsp;
|
const BoardDef *_bsp;
|
||||||
bool _isInit = false;
|
bool _isBegin = false;
|
||||||
uint8_t _ledState = 0;
|
uint8_t _ledState = 0;
|
||||||
BoardType _boardType;
|
BoardType _boardType;
|
||||||
void *pixels = nullptr;
|
void *pixels = nullptr;
|
||||||
@ -31,7 +35,7 @@ private:
|
|||||||
const char *TAG = "LED";
|
const char *TAG = "LED";
|
||||||
#else
|
#else
|
||||||
#endif
|
#endif
|
||||||
bool checkInit(void);
|
bool isBegin(void);
|
||||||
bool ledNumInvalid(int ledNum);
|
bool ledNumInvalid(int ledNum);
|
||||||
};
|
};
|
||||||
|
|
@ -15,7 +15,8 @@ void PushButton::begin(Stream &debugStream) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void PushButton::begin(void) {
|
void PushButton::begin(void) {
|
||||||
if (this->_isInit) {
|
if (this->_isBegin) {
|
||||||
|
AgLog("Initialized, call end() then try again");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,14 +26,14 @@ void PushButton::begin(void) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->_boardType == BOARD_DIY_PRO_INDOOR_V4_2) {
|
if (this->_boardType == DIY_PRO_INDOOR_V4_2) {
|
||||||
pinMode(this->_bsp->SW.pin, INPUT_PULLUP);
|
pinMode(this->_bsp->SW.pin, INPUT_PULLUP);
|
||||||
} else {
|
} else {
|
||||||
pinMode(this->_bsp->SW.pin, INPUT);
|
pinMode(this->_bsp->SW.pin, INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->_isInit = true;
|
this->_isBegin = true;
|
||||||
AgLog("Init");
|
AgLog("Initialize");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,7 +42,7 @@ void PushButton::begin(void) {
|
|||||||
* @return PushButton::State
|
* @return PushButton::State
|
||||||
*/
|
*/
|
||||||
PushButton::State PushButton::getState(void) {
|
PushButton::State PushButton::getState(void) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return State::BUTTON_RELEASED;
|
return State::BUTTON_RELEASED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,8 +65,8 @@ String PushButton::toString(PushButton::State state) {
|
|||||||
return "Released";
|
return "Released";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PushButton::checkInit(void) {
|
bool PushButton::isBegin(void) {
|
||||||
if (this->_isInit) {
|
if (this->_isBegin) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
AgLog("Switch not initialized");
|
AgLog("Switch not initialized");
|
@ -4,6 +4,10 @@
|
|||||||
#include "BoardDef.h"
|
#include "BoardDef.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The class define how to handle the Push button
|
||||||
|
*
|
||||||
|
*/
|
||||||
class PushButton {
|
class PushButton {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@ -26,7 +30,7 @@ private:
|
|||||||
/** Board type */
|
/** Board type */
|
||||||
BoardType _boardType;
|
BoardType _boardType;
|
||||||
/** Is inititalize flag */
|
/** Is inititalize flag */
|
||||||
bool _isInit = false;
|
bool _isBegin = false;
|
||||||
|
|
||||||
/** Special variable for ESP8266 */
|
/** Special variable for ESP8266 */
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
@ -37,7 +41,7 @@ private:
|
|||||||
|
|
||||||
/** Method */
|
/** Method */
|
||||||
|
|
||||||
bool checkInit(void);
|
bool isBegin(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /** _AIR_GRADIENT_SW_H_ */
|
#endif /** _AIR_GRADIENT_SW_H_ */
|
@ -15,6 +15,10 @@ void StatusLed::begin(Stream &debugStream) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void StatusLed::begin(void) {
|
void StatusLed::begin(void) {
|
||||||
|
if (this->_isBegin) {
|
||||||
|
AgLog("Initialized, call end() then try again");
|
||||||
|
return;
|
||||||
|
}
|
||||||
bsp = getBoardDef(this->boardType);
|
bsp = getBoardDef(this->boardType);
|
||||||
if ((bsp == nullptr) || (bsp->LED.supported == false)) {
|
if ((bsp == nullptr) || (bsp->LED.supported == false)) {
|
||||||
AgLog("Board not support StatusLed");
|
AgLog("Board not support StatusLed");
|
||||||
@ -25,9 +29,9 @@ void StatusLed::begin(void) {
|
|||||||
digitalWrite(bsp->LED.pin, !bsp->LED.onState);
|
digitalWrite(bsp->LED.pin, !bsp->LED.onState);
|
||||||
|
|
||||||
this->state = LED_OFF;
|
this->state = LED_OFF;
|
||||||
this->isInit = true;
|
this->_isBegin = true;
|
||||||
|
|
||||||
AgLog("Init");
|
AgLog("Initialize");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,7 +39,7 @@ void StatusLed::begin(void) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void StatusLed::setOn(void) {
|
void StatusLed::setOn(void) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
digitalWrite(bsp->LED.pin, bsp->LED.onState);
|
digitalWrite(bsp->LED.pin, bsp->LED.onState);
|
||||||
@ -48,7 +52,7 @@ void StatusLed::setOn(void) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void StatusLed::setOff(void) {
|
void StatusLed::setOff(void) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
digitalWrite(bsp->LED.pin, !bsp->LED.onState);
|
digitalWrite(bsp->LED.pin, !bsp->LED.onState);
|
||||||
@ -88,11 +92,24 @@ String StatusLed::toString(StatusLed::State state) {
|
|||||||
return "Off";
|
return "Off";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StatusLed::checkInit(void) {
|
bool StatusLed::isBegin(void) {
|
||||||
if (this->isInit == false) {
|
if (this->_isBegin == false) {
|
||||||
AgLog("No-Initialized");
|
AgLog("Not-Initialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StatusLed::end(void) {
|
||||||
|
if (_isBegin == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ESP8266)
|
||||||
|
_debugStream = nullptr;
|
||||||
|
#endif
|
||||||
|
setOff();
|
||||||
|
_isBegin = false;
|
||||||
|
AgLog("De-initialize");
|
||||||
|
}
|
@ -4,6 +4,10 @@
|
|||||||
#include "BoardDef.h"
|
#include "BoardDef.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The class define how to handle the LED
|
||||||
|
*
|
||||||
|
*/
|
||||||
class StatusLed {
|
class StatusLed {
|
||||||
public:
|
public:
|
||||||
enum State {
|
enum State {
|
||||||
@ -17,6 +21,7 @@ public:
|
|||||||
#else
|
#else
|
||||||
#endif
|
#endif
|
||||||
void begin(void);
|
void begin(void);
|
||||||
|
void end(void);
|
||||||
void setOn(void);
|
void setOn(void);
|
||||||
void setOff(void);
|
void setOff(void);
|
||||||
void setToggle(void);
|
void setToggle(void);
|
||||||
@ -26,7 +31,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
const BoardDef *bsp = nullptr;
|
const BoardDef *bsp = nullptr;
|
||||||
BoardType boardType;
|
BoardType boardType;
|
||||||
bool isInit = false;
|
bool _isBegin = false;
|
||||||
State state;
|
State state;
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
Stream *_debugStream;
|
Stream *_debugStream;
|
||||||
@ -34,7 +39,7 @@ private:
|
|||||||
#else
|
#else
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool checkInit(void);
|
bool isBegin(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /** _STATUS_LED_H_ */
|
#endif /** _STATUS_LED_H_ */
|
@ -1,7 +1,5 @@
|
|||||||
#include "PMS.h"
|
#include "PMS.h"
|
||||||
|
|
||||||
// PMS::PMS(Stream &stream) { this->_stream = &stream; }
|
|
||||||
|
|
||||||
bool PMS::begin(Stream *stream) {
|
bool PMS::begin(Stream *stream) {
|
||||||
_stream = stream;
|
_stream = stream;
|
||||||
|
|
@ -3,6 +3,11 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Class define how to handle plantower PMS sensor it's upport for
|
||||||
|
* PMS5003 and PMS5003T series. The data @ref AMB_TMP and @ref AMB_HUM only
|
||||||
|
* valid on PMS5003T
|
||||||
|
*/
|
||||||
class PMS {
|
class PMS {
|
||||||
public:
|
public:
|
||||||
static const uint16_t SINGLE_RESPONSE_TIME = 1000;
|
static const uint16_t SINGLE_RESPONSE_TIME = 1000;
|
||||||
@ -38,7 +43,7 @@ public:
|
|||||||
uint16_t AMB_HUM;
|
uint16_t AMB_HUM;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool begin(Stream* stream);
|
bool begin(Stream *stream);
|
||||||
void sleep();
|
void sleep();
|
||||||
void wakeUp();
|
void wakeUp();
|
||||||
void activeMode();
|
void activeMode();
|
@ -43,32 +43,27 @@ PMS5003::PMS5003(BoardType def) : _boardDef(def) {}
|
|||||||
* @return false Failure
|
* @return false Failure
|
||||||
*/
|
*/
|
||||||
bool PMS5003::begin(void) {
|
bool PMS5003::begin(void) {
|
||||||
if (this->_isInit) {
|
if (this->_isBegin) {
|
||||||
|
AgLog("Initialized, call end() then try again");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ESP32)
|
#if defined(ESP32)
|
||||||
// if (this->_serial != &Serial) {
|
|
||||||
// AgLog("Hardware serial must be Serial(0)");
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->bsp = getBoardDef(this->_boardDef);
|
this->bsp = getBoardDef(this->_boardDef);
|
||||||
if (bsp == NULL) {
|
if (bsp == NULL) {
|
||||||
AgLog("Board [%d] not supported", this->_boardDef);
|
AgLog("Board [%d] not supported", this->_boardDef);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bsp->PMS5003.supported == false) {
|
if (bsp->Pms5003.supported == false) {
|
||||||
AgLog("Board [%d] PMS50035003 not supported", this->_boardDef);
|
AgLog("Board [%d] PMS50035003 not supported", this->_boardDef);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
bsp->PMS5003.uart_tx_pin;
|
bsp->Pms5003.uart_tx_pin;
|
||||||
SoftwareSerial *uart =
|
SoftwareSerial *uart = new SoftwareSerial(bsp->Pms5003.uart_tx_pin, bsp->Pms5003.uart_rx_pin);
|
||||||
new SoftwareSerial(bsp->PMS5003.uart_tx_pin, bsp->PMS5003.uart_rx_pin);
|
|
||||||
uart->begin(9600);
|
uart->begin(9600);
|
||||||
if (pms.begin(uart) == false) {
|
if (pms.begin(uart) == false) {
|
||||||
AgLog("PMS failed");
|
AgLog("PMS failed");
|
||||||
@ -82,7 +77,7 @@ bool PMS5003::begin(void) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->_isInit = true;
|
this->_isBegin = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +114,7 @@ int PMS5003::pm25ToAQI(int pm02) {
|
|||||||
* @return false Failure
|
* @return false Failure
|
||||||
*/
|
*/
|
||||||
bool PMS5003::readData(void) {
|
bool PMS5003::readData(void) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,10 +163,26 @@ int PMS5003::convertPm25ToUsAqi(int pm25) { return this->pm25ToAQI(pm25); }
|
|||||||
* @return true Initialized
|
* @return true Initialized
|
||||||
* @return false No-initialized
|
* @return false No-initialized
|
||||||
*/
|
*/
|
||||||
bool PMS5003::checkInit(void) {
|
bool PMS5003::isBegin(void) {
|
||||||
if (this->_isInit == false) {
|
if (this->_isBegin == false) {
|
||||||
AgLog("No initialized");
|
AgLog("Not-initialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief De-initialize sensor
|
||||||
|
*/
|
||||||
|
void PMS5003::end(void) {
|
||||||
|
if (_isBegin == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_isBegin = false;
|
||||||
|
#if defined(ESP8266)
|
||||||
|
_debugStream = NULL;
|
||||||
|
#else
|
||||||
|
delete _serial;
|
||||||
|
#endif
|
||||||
|
AgLog("De-initialize");
|
||||||
|
}
|
@ -1,10 +1,13 @@
|
|||||||
#ifndef _AIR_GRADIENT_PMS5003_H_
|
#ifndef _AIR_GRADIENT_PMS5003_H_
|
||||||
#define _AIR_GRADIENT_PMS5003_H_
|
#define _AIR_GRADIENT_PMS5003_H_
|
||||||
|
|
||||||
#include "../bsp/BoardDef.h"
|
#include "../main/BoardDef.h"
|
||||||
#include "Stream.h"
|
#include "Stream.h"
|
||||||
#include "PMS.h"
|
#include "PMS.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The class define how to handle PMS5003 sensor bas on @ref PMS class
|
||||||
|
*/
|
||||||
class PMS5003 {
|
class PMS5003 {
|
||||||
public:
|
public:
|
||||||
PMS5003(BoardType def);
|
PMS5003(BoardType def);
|
||||||
@ -13,6 +16,7 @@ public:
|
|||||||
#else
|
#else
|
||||||
bool begin(HardwareSerial &serial);
|
bool begin(HardwareSerial &serial);
|
||||||
#endif
|
#endif
|
||||||
|
void end(void);
|
||||||
|
|
||||||
bool readData(void);
|
bool readData(void);
|
||||||
int getPm01Ae(void);
|
int getPm01Ae(void);
|
||||||
@ -22,7 +26,7 @@ public:
|
|||||||
int convertPm25ToUsAqi(int pm25);
|
int convertPm25ToUsAqi(int pm25);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _isInit = false;
|
bool _isBegin = false;
|
||||||
BoardType _boardDef;
|
BoardType _boardDef;
|
||||||
PMS pms;
|
PMS pms;
|
||||||
const BoardDef *bsp;
|
const BoardDef *bsp;
|
||||||
@ -36,7 +40,7 @@ private:
|
|||||||
PMS::DATA pmsData;
|
PMS::DATA pmsData;
|
||||||
|
|
||||||
bool begin(void);
|
bool begin(void);
|
||||||
bool checkInit(void);
|
bool isBegin(void);
|
||||||
int pm25ToAQI(int pm02);
|
int pm25ToAQI(int pm02);
|
||||||
};
|
};
|
||||||
#endif /** _AIR_GRADIENT_PMS5003_H_ */
|
#endif /** _AIR_GRADIENT_PMS5003_H_ */
|
@ -43,7 +43,7 @@ PMS5003T::PMS5003T(BoardType def) : _boardDef(def) {}
|
|||||||
* @return false Failure
|
* @return false Failure
|
||||||
*/
|
*/
|
||||||
bool PMS5003T::begin(void) {
|
bool PMS5003T::begin(void) {
|
||||||
if (this->_isInit) {
|
if (this->_isBegin) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,15 +60,15 @@ bool PMS5003T::begin(void) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bsp->PMS5003.supported == false) {
|
if (bsp->Pms5003.supported == false) {
|
||||||
AgLog("Board [%d] PMS5003 not supported", this->_boardDef);
|
AgLog("Board [%d] PMS5003 not supported", this->_boardDef);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
bsp->PMS5003.uart_tx_pin;
|
bsp->Pms5003.uart_tx_pin;
|
||||||
SoftwareSerial *uart =
|
SoftwareSerial *uart =
|
||||||
new SoftwareSerial(bsp->PMS5003.uart_tx_pin, bsp->PMS5003.uart_rx_pin);
|
new SoftwareSerial(bsp->Pms5003.uart_tx_pin, bsp->Pms5003.uart_rx_pin);
|
||||||
uart->begin(9600);
|
uart->begin(9600);
|
||||||
if (pms.begin(uart) == false) {
|
if (pms.begin(uart) == false) {
|
||||||
AgLog("PMS failed");
|
AgLog("PMS failed");
|
||||||
@ -82,8 +82,8 @@ bool PMS5003T::begin(void) {
|
|||||||
if (this->_serial == &Serial) {
|
if (this->_serial == &Serial) {
|
||||||
#endif
|
#endif
|
||||||
AgLog("Init Serial");
|
AgLog("Init Serial");
|
||||||
this->_serial->begin(9600, SERIAL_8N1, bsp->PMS5003.uart_rx_pin,
|
this->_serial->begin(9600, SERIAL_8N1, bsp->Pms5003.uart_rx_pin,
|
||||||
bsp->PMS5003.uart_tx_pin);
|
bsp->Pms5003.uart_tx_pin);
|
||||||
} else {
|
} else {
|
||||||
if (bsp->SenseAirS8.supported == false) {
|
if (bsp->SenseAirS8.supported == false) {
|
||||||
AgLog("Board [%d] PMS5003T_2 not supported", this->_boardDef);
|
AgLog("Board [%d] PMS5003T_2 not supported", this->_boardDef);
|
||||||
@ -101,7 +101,7 @@ bool PMS5003T::begin(void) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->_isInit = true;
|
this->_isBegin = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ int PMS5003T::pm25ToAQI(int pm02) {
|
|||||||
* @return false Failure
|
* @return false Failure
|
||||||
*/
|
*/
|
||||||
bool PMS5003T::readData(void) {
|
bool PMS5003T::readData(void) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,9 +207,9 @@ float PMS5003T::getRelativeHumidity(void) {
|
|||||||
* @return true Initialized
|
* @return true Initialized
|
||||||
* @return false No-initialized
|
* @return false No-initialized
|
||||||
*/
|
*/
|
||||||
bool PMS5003T::checkInit(void) {
|
bool PMS5003T::isBegin(void) {
|
||||||
if (this->_isInit == false) {
|
if (this->_isBegin == false) {
|
||||||
AgLog("No initialized");
|
AgLog("Not-initialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -221,3 +221,16 @@ float PMS5003T::correctionTemperature(float inTemp) {
|
|||||||
}
|
}
|
||||||
return inTemp * 1.181f - 5.113f;
|
return inTemp * 1.181f - 5.113f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PMS5003T::end(void) {
|
||||||
|
if (_isBegin == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_isBegin = false;
|
||||||
|
#if defined(ESP8266)
|
||||||
|
_debugStream = NULL;
|
||||||
|
#else
|
||||||
|
delete _serial;
|
||||||
|
#endif
|
||||||
|
AgLog("De-initialize");
|
||||||
|
}
|
@ -2,10 +2,13 @@
|
|||||||
#define _PMS5003T_H_
|
#define _PMS5003T_H_
|
||||||
|
|
||||||
#include <HardwareSerial.h>
|
#include <HardwareSerial.h>
|
||||||
#include "../bsp/BoardDef.h"
|
#include "../main/BoardDef.h"
|
||||||
#include "PMS.h"
|
#include "PMS.h"
|
||||||
#include "Stream.h"
|
#include "Stream.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The class define how to handle PMS5003T sensor bas on @ref PMS class
|
||||||
|
*/
|
||||||
class PMS5003T {
|
class PMS5003T {
|
||||||
public:
|
public:
|
||||||
PMS5003T(BoardType def);
|
PMS5003T(BoardType def);
|
||||||
@ -14,6 +17,7 @@ public:
|
|||||||
#else
|
#else
|
||||||
bool begin(HardwareSerial &serial);
|
bool begin(HardwareSerial &serial);
|
||||||
#endif
|
#endif
|
||||||
|
void end(void);
|
||||||
|
|
||||||
bool readData(void);
|
bool readData(void);
|
||||||
int getPm01Ae(void);
|
int getPm01Ae(void);
|
||||||
@ -25,7 +29,7 @@ public:
|
|||||||
float getRelativeHumidity(void);
|
float getRelativeHumidity(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _isInit = false;
|
bool _isBegin = false;
|
||||||
bool _isSleep = false;
|
bool _isSleep = false;
|
||||||
|
|
||||||
BoardType _boardDef;
|
BoardType _boardDef;
|
||||||
@ -41,7 +45,7 @@ private:
|
|||||||
int pm25ToAQI(int pm02);
|
int pm25ToAQI(int pm02);
|
||||||
PMS pms;
|
PMS pms;
|
||||||
PMS::DATA pmsData;
|
PMS::DATA pmsData;
|
||||||
bool checkInit(void);
|
bool isBegin(void);
|
||||||
float correctionTemperature(float inTemp);
|
float correctionTemperature(float inTemp);
|
||||||
};
|
};
|
||||||
|
|
@ -6,7 +6,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a new Sense Air S 8:: Sense Air S 8 object
|
* @brief Construct a new Sense Air S8:: Sense Air S8 object
|
||||||
*
|
*
|
||||||
* @param def
|
* @param def
|
||||||
*/
|
*/
|
||||||
@ -18,7 +18,7 @@ S8::S8(BoardType def) : _boardDef(def) {}
|
|||||||
* @return true = success, otherwise is failure
|
* @return true = success, otherwise is failure
|
||||||
*/
|
*/
|
||||||
bool S8::begin(void) {
|
bool S8::begin(void) {
|
||||||
if (this->_isInit) {
|
if (this->_isBegin) {
|
||||||
AgLog("Initialized, Call end() then try again");
|
AgLog("Initialized, Call end() then try again");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -27,8 +27,8 @@ bool S8::begin(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Init sensor has print debug log, if class create without serial debug
|
* @brief Init S8 sensor, this methos should be call before other, if not it's
|
||||||
* before it's override last define
|
* always return the failure status
|
||||||
*
|
*
|
||||||
* @param _debugStream Serial print debug log, NULL if don't use
|
* @param _debugStream Serial print debug log, NULL if don't use
|
||||||
* @return true = success, otherwise is failure
|
* @return true = success, otherwise is failure
|
||||||
@ -56,13 +56,12 @@ bool S8::begin(HardwareSerial &serial) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void S8::end(void) {
|
void S8::end(void) {
|
||||||
if (this->_isInit == false) {
|
if (this->_isBegin == false) {
|
||||||
AgLog("Senor is not initialized");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deinit
|
// Deinit
|
||||||
AgLog("De-Inititlized");
|
AgLog("De-Inititlize");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,7 +70,7 @@ void S8::end(void) {
|
|||||||
* @param firmver String buffer, len = 10 char
|
* @param firmver String buffer, len = 10 char
|
||||||
*/
|
*/
|
||||||
void S8::getFirmwareVersion(char firmver[]) {
|
void S8::getFirmwareVersion(char firmver[]) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +102,7 @@ void S8::getFirmwareVersion(char firmver[]) {
|
|||||||
* @return int32_t Return ID
|
* @return int32_t Return ID
|
||||||
*/
|
*/
|
||||||
int32_t S8::getSensorTypeId(void) {
|
int32_t S8::getSensorTypeId(void) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +148,7 @@ int32_t S8::getSensorTypeId(void) {
|
|||||||
* @return int32_t ID
|
* @return int32_t ID
|
||||||
*/
|
*/
|
||||||
int32_t S8::getSensorId(void) {
|
int32_t S8::getSensorId(void) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +194,7 @@ int32_t S8::getSensorId(void) {
|
|||||||
* @return int16_t
|
* @return int16_t
|
||||||
*/
|
*/
|
||||||
int16_t S8::getMemoryMapVersion(void) {
|
int16_t S8::getMemoryMapVersion(void) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,7 +224,7 @@ int16_t S8::getMemoryMapVersion(void) {
|
|||||||
* @return int16_t (PPM), -1 if invalid.
|
* @return int16_t (PPM), -1 if invalid.
|
||||||
*/
|
*/
|
||||||
int16_t S8::getCo2(void) {
|
int16_t S8::getCo2(void) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,7 +285,7 @@ bool S8::isBaseLineCalibrationDone(void) {
|
|||||||
* @return int16_t PWM
|
* @return int16_t PWM
|
||||||
*/
|
*/
|
||||||
int16_t S8::getOutputPWM(void) {
|
int16_t S8::getOutputPWM(void) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +317,7 @@ int16_t S8::getOutputPWM(void) {
|
|||||||
* @return int16_t Hour
|
* @return int16_t Hour
|
||||||
*/
|
*/
|
||||||
int16_t S8::getCalibPeriodABC(void) {
|
int16_t S8::getCalibPeriodABC(void) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,7 +349,7 @@ int16_t S8::getCalibPeriodABC(void) {
|
|||||||
* @return false Failure
|
* @return false Failure
|
||||||
*/
|
*/
|
||||||
bool S8::setCalibPeriodABC(int16_t period) {
|
bool S8::setCalibPeriodABC(int16_t period) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +390,7 @@ bool S8::setCalibPeriodABC(int16_t period) {
|
|||||||
* @return false Failure
|
* @return false Failure
|
||||||
*/
|
*/
|
||||||
bool S8::manualCalib(void) {
|
bool S8::manualCalib(void) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,7 +415,7 @@ bool S8::manualCalib(void) {
|
|||||||
* @return int16_t Flags
|
* @return int16_t Flags
|
||||||
*/
|
*/
|
||||||
int16_t S8::getAcknowledgement(void) {
|
int16_t S8::getAcknowledgement(void) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,7 +445,7 @@ int16_t S8::getAcknowledgement(void) {
|
|||||||
* @return false Failure
|
* @return false Failure
|
||||||
*/
|
*/
|
||||||
bool S8::clearAcknowledgement(void) {
|
bool S8::clearAcknowledgement(void) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,7 +479,7 @@ bool S8::clearAcknowledgement(void) {
|
|||||||
* @return int16_t Alarm status
|
* @return int16_t Alarm status
|
||||||
*/
|
*/
|
||||||
int16_t S8::getAlarmStatus(void) {
|
int16_t S8::getAlarmStatus(void) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,7 +508,7 @@ int16_t S8::getAlarmStatus(void) {
|
|||||||
* @return S8::Status Sensor status
|
* @return S8::Status Sensor status
|
||||||
*/
|
*/
|
||||||
S8::Status S8::getStatus(void) {
|
S8::Status S8::getStatus(void) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return (Status)0;
|
return (Status)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -538,7 +537,7 @@ S8::Status S8::getStatus(void) {
|
|||||||
* @return int16_t Output status
|
* @return int16_t Output status
|
||||||
*/
|
*/
|
||||||
int16_t S8::getOutputStatus(void) {
|
int16_t S8::getOutputStatus(void) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,7 +568,7 @@ int16_t S8::getOutputStatus(void) {
|
|||||||
* @return false Failure
|
* @return false Failure
|
||||||
*/
|
*/
|
||||||
bool S8::sendSpecialCommand(CalibrationSpecialComamnd command) {
|
bool S8::sendSpecialCommand(CalibrationSpecialComamnd command) {
|
||||||
if (this->isInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,16 +655,16 @@ bool S8::init(int txPin, int rxPin, uint32_t baud) {
|
|||||||
|
|
||||||
/** Check communication by get firmware version */
|
/** Check communication by get firmware version */
|
||||||
char fwVers[11];
|
char fwVers[11];
|
||||||
this->_isInit = true;
|
this->_isBegin = true;
|
||||||
this->getFirmwareVersion(fwVers);
|
this->getFirmwareVersion(fwVers);
|
||||||
if (strlen(fwVers) == 0) {
|
if (strlen(fwVers) == 0) {
|
||||||
this->_isInit = false;
|
this->_isBegin = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
AgLog("Firmware version: %s", fwVers);
|
AgLog("Firmware version: %s", fwVers);
|
||||||
|
|
||||||
AgLog("Sensor successfully initialized. Heating up for 10s");
|
AgLog("Sensor successfully initialized. Heating up for 10s");
|
||||||
this->_isInit = true;
|
this->_isBegin = true;
|
||||||
this->_lastInitTime = millis();
|
this->_lastInitTime = millis();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -676,11 +675,11 @@ bool S8::init(int txPin, int rxPin, uint32_t baud) {
|
|||||||
* @return true Initialized
|
* @return true Initialized
|
||||||
* @return false No-Initialized
|
* @return false No-Initialized
|
||||||
*/
|
*/
|
||||||
bool S8::isInit(void) {
|
bool S8::isBegin(void) {
|
||||||
if (this->_isInit) {
|
if (this->_isBegin) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
AgLog("Sensor no-initialized");
|
AgLog("Sensor not-initialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -794,3 +793,23 @@ void S8::sendCommand(uint8_t func, uint16_t reg, uint16_t value) {
|
|||||||
uartWriteBytes(8);
|
uartWriteBytes(8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set Auto calib baseline period
|
||||||
|
*
|
||||||
|
* @param hours Number of hour will trigger auto calib: [0, 4800], 0: disable
|
||||||
|
* @return true Success
|
||||||
|
* @return false Failure
|
||||||
|
*/
|
||||||
|
bool S8::setAutoCalib(int hours) {
|
||||||
|
if (isBegin() == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int curCalib = getCalibPeriodABC();
|
||||||
|
if (curCalib == hours) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return setCalibPeriodABC(hours);
|
||||||
|
}
|
@ -1,9 +1,12 @@
|
|||||||
#ifndef _S8_H_
|
#ifndef _S8_H_
|
||||||
#define _S8_H_
|
#define _S8_H_
|
||||||
|
|
||||||
#include "../bsp/BoardDef.h"
|
#include "../main/BoardDef.h"
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The class define how to handle the senseair S8 sensor (CO2 sensor)
|
||||||
|
*/
|
||||||
class S8 {
|
class S8 {
|
||||||
public:
|
public:
|
||||||
const int S8_BAUDRATE =
|
const int S8_BAUDRATE =
|
||||||
@ -75,6 +78,7 @@ public:
|
|||||||
int16_t getCo2(void);
|
int16_t getCo2(void);
|
||||||
bool setBaselineCalibration(void);
|
bool setBaselineCalibration(void);
|
||||||
bool isBaseLineCalibrationDone(void);
|
bool isBaseLineCalibrationDone(void);
|
||||||
|
bool setAutoCalib(int hours);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Variables */
|
/** Variables */
|
||||||
@ -86,7 +90,7 @@ private:
|
|||||||
#if defined(ESP32)
|
#if defined(ESP32)
|
||||||
HardwareSerial *_serial;
|
HardwareSerial *_serial;
|
||||||
#endif
|
#endif
|
||||||
bool _isInit = false;
|
bool _isBegin = false;
|
||||||
uint32_t _lastInitTime;
|
uint32_t _lastInitTime;
|
||||||
bool isCalib = false;
|
bool isCalib = false;
|
||||||
|
|
||||||
@ -95,7 +99,7 @@ private:
|
|||||||
bool init(const BoardDef *bsp);
|
bool init(const BoardDef *bsp);
|
||||||
bool init(int txPin, int rxPin);
|
bool init(int txPin, int rxPin);
|
||||||
bool init(int txPin, int rxPin, uint32_t baud);
|
bool init(int txPin, int rxPin, uint32_t baud);
|
||||||
bool isInit(void);
|
bool isBegin(void);
|
||||||
|
|
||||||
void uartWriteBytes(uint8_t size); // Send bytes to sensor
|
void uartWriteBytes(uint8_t size); // Send bytes to sensor
|
||||||
uint8_t
|
uint8_t
|
@ -7,6 +7,11 @@
|
|||||||
#define vocAlgorithm() ((VOCGasIndexAlgorithm *)(this->_vocAlgorithm))
|
#define vocAlgorithm() ((VOCGasIndexAlgorithm *)(this->_vocAlgorithm))
|
||||||
#define noxAlgorithm() ((NOxGasIndexAlgorithm *)(this->_noxAlgorithm))
|
#define noxAlgorithm() ((NOxGasIndexAlgorithm *)(this->_noxAlgorithm))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Sgp 4 1:: Sgp 4 1 object
|
||||||
|
*
|
||||||
|
* @param type Board type @ref BoardType
|
||||||
|
*/
|
||||||
Sgp41::Sgp41(BoardType type) : _boardType(type) {}
|
Sgp41::Sgp41(BoardType type) : _boardType(type) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,9 +23,13 @@ Sgp41::Sgp41(BoardType type) : _boardType(type) {}
|
|||||||
* @return false Failure
|
* @return false Failure
|
||||||
*/
|
*/
|
||||||
bool Sgp41::begin(TwoWire &wire) {
|
bool Sgp41::begin(TwoWire &wire) {
|
||||||
if (this->_isInit) {
|
/** Ignore next step if initialized */
|
||||||
|
if (this->_isBegin) {
|
||||||
|
AgLog("Initialized, call end() then try again");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check that board has supported this sensor */
|
||||||
if (this->boardSupported() == false) {
|
if (this->boardSupported() == false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -55,8 +64,8 @@ bool Sgp41::begin(TwoWire &wire) {
|
|||||||
conditioningCount = 0;
|
conditioningCount = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->_isInit = true;
|
this->_isBegin = true;
|
||||||
AgLog("Init");
|
AgLog("Initialize");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,6 +98,10 @@ void Sgp41::handle(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
/**
|
||||||
|
* @brief Handle the sensor conditioning and run time udpate value, This method
|
||||||
|
* must not call, it's called on private task
|
||||||
|
*/
|
||||||
void Sgp41::_handle(void) {
|
void Sgp41::_handle(void) {
|
||||||
/** NOx conditionning */
|
/** NOx conditionning */
|
||||||
uint16_t err;
|
uint16_t err;
|
||||||
@ -115,18 +128,26 @@ void Sgp41::_handle(void) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief De-Initialize sensor
|
||||||
|
*/
|
||||||
void Sgp41::end(void) {
|
void Sgp41::end(void) {
|
||||||
if (this->_isInit == false) {
|
if (this->_isBegin == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
vTaskDelete(pollTask);
|
vTaskDelete(pollTask);
|
||||||
|
#else
|
||||||
|
_debugStream = nullptr;
|
||||||
#endif
|
#endif
|
||||||
this->_isInit = false;
|
bsp = NULL;
|
||||||
|
this->_isBegin = false;
|
||||||
delete sgpSensor();
|
delete sgpSensor();
|
||||||
|
delete vocAlgorithm();
|
||||||
|
delete noxAlgorithm();
|
||||||
|
|
||||||
AgLog("De-Init");
|
AgLog("De-initialize");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,6 +174,12 @@ int Sgp41::getNoxIndex(void) {
|
|||||||
return nox;
|
return nox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check that board has supported sensor
|
||||||
|
*
|
||||||
|
* @return true Supported
|
||||||
|
* @return false Not-supported
|
||||||
|
*/
|
||||||
bool Sgp41::boardSupported(void) {
|
bool Sgp41::boardSupported(void) {
|
||||||
if (this->bsp == nullptr) {
|
if (this->bsp == nullptr) {
|
||||||
this->bsp = getBoardDef(this->_boardType);
|
this->bsp = getBoardDef(this->_boardType);
|
||||||
@ -165,25 +192,19 @@ bool Sgp41::boardSupported(void) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Sgp41::sdaPin(void) {
|
/**
|
||||||
if (this->bsp) {
|
* @brief Get raw signal
|
||||||
return this->bsp->I2C.sda_pin;
|
*
|
||||||
}
|
* @param raw_voc Raw VOC output
|
||||||
AgLog("sdaPin(): board not supported I2C");
|
* @param row_nox Raw NOx output
|
||||||
return -1;
|
* @param defaultRh
|
||||||
}
|
* @param defaultT
|
||||||
|
* @return true Success
|
||||||
int Sgp41::sclPin(void) {
|
* @return false Failure
|
||||||
if (this->bsp) {
|
*/
|
||||||
return this->bsp->I2C.scl_pin;
|
|
||||||
}
|
|
||||||
AgLog("sdlPin(): board not supported I2C");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sgp41::getRawSignal(uint16_t &raw_voc, uint16_t &row_nox,
|
bool Sgp41::getRawSignal(uint16_t &raw_voc, uint16_t &row_nox,
|
||||||
uint16_t defaultRh, uint16_t defaultT) {
|
uint16_t defaultRh, uint16_t defaultT) {
|
||||||
if (this->checkInit() == false) {
|
if (this->isBegin() == false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,43 +216,25 @@ bool Sgp41::getRawSignal(uint16_t &raw_voc, uint16_t &row_nox,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This command turns the hotplate off and stops the measurement.
|
* @brief Check that sensor is initialized
|
||||||
* Subsequently, the sensor enters the idle mode.
|
*
|
||||||
|
* @return true Initialized
|
||||||
|
* @return false Not-initialized
|
||||||
|
*/
|
||||||
|
bool Sgp41::isBegin(void) {
|
||||||
|
if (this->_isBegin) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
AgLog("Sensor not-initialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle nox conditioning process
|
||||||
*
|
*
|
||||||
* @return true Success
|
* @return true Success
|
||||||
* @return false Failure
|
* @return false Failure
|
||||||
*/
|
*/
|
||||||
bool Sgp41::turnHeaterOff(void) {
|
|
||||||
if (this->checkInit() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sgpSensor()->turnHeaterOff() == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sgp41::getSerialNumber(uint16_t serialNumbers[],
|
|
||||||
uint8_t serialNumberSize) {
|
|
||||||
if (this->checkInit() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sgpSensor()->getSerialNumber(serialNumbers) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sgp41::checkInit(void) {
|
|
||||||
if (this->_isInit) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
AgLog("Sensor no-initialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sgp41::_noxConditioning(void) {
|
bool Sgp41::_noxConditioning(void) {
|
||||||
uint16_t err;
|
uint16_t err;
|
||||||
uint16_t srawVoc;
|
uint16_t srawVoc;
|
@ -1,10 +1,15 @@
|
|||||||
#ifndef _AIR_GRADIENT_SGP4X_H_
|
#ifndef _AIR_GRADIENT_SGP4X_H_
|
||||||
#define _AIR_GRADIENT_SGP4X_H_
|
#define _AIR_GRADIENT_SGP4X_H_
|
||||||
|
|
||||||
#include "../bsp/BoardDef.h"
|
#include "../main/BoardDef.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The class define how to handle Sensirion sensor SGP41 (VOC and NOx
|
||||||
|
* sensor)
|
||||||
|
*
|
||||||
|
*/
|
||||||
class Sgp41 {
|
class Sgp41 {
|
||||||
public:
|
public:
|
||||||
Sgp41(BoardType type);
|
Sgp41(BoardType type);
|
||||||
@ -22,7 +27,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool onConditioning = true;
|
bool onConditioning = true;
|
||||||
bool ready = false;
|
bool ready = false;
|
||||||
bool _isInit = false;
|
bool _isBegin = false;
|
||||||
void *_sensor;
|
void *_sensor;
|
||||||
void *_vocAlgorithm;
|
void *_vocAlgorithm;
|
||||||
void *_noxAlgorithm;
|
void *_noxAlgorithm;
|
||||||
@ -40,14 +45,10 @@ private:
|
|||||||
#else
|
#else
|
||||||
TaskHandle_t pollTask;
|
TaskHandle_t pollTask;
|
||||||
#endif
|
#endif
|
||||||
bool checkInit(void);
|
bool isBegin(void);
|
||||||
bool boardSupported(void);
|
bool boardSupported(void);
|
||||||
int sdaPin(void);
|
|
||||||
int sclPin(void);
|
|
||||||
bool getRawSignal(uint16_t &raw_voc, uint16_t &raw_nox,
|
bool getRawSignal(uint16_t &raw_voc, uint16_t &raw_nox,
|
||||||
uint16_t defaultRh = 0x8000, uint16_t defaultT = 0x6666);
|
uint16_t defaultRh = 0x8000, uint16_t defaultT = 0x6666);
|
||||||
bool turnHeaterOff(void);
|
|
||||||
bool getSerialNumber(uint16_t serialNumbers[], uint8_t serialNumberSize);
|
|
||||||
bool _noxConditioning(void);
|
bool _noxConditioning(void);
|
||||||
};
|
};
|
||||||
|
|
149
src/sht/sht.cpp
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
#include "sht.h"
|
||||||
|
|
||||||
|
#include "../library/arduino-sht/SHTSensor.h"
|
||||||
|
|
||||||
|
/** Cast _sensor to SHTSensor */
|
||||||
|
#define shtSensor() ((SHTSensor *)(this->_sensor))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check that is sensor initialized
|
||||||
|
*
|
||||||
|
* @return true Initialized
|
||||||
|
* @return false Not-initialized
|
||||||
|
*/
|
||||||
|
bool Sht::isBegin(void) {
|
||||||
|
if (this->_isBegin) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
AgLog("Sensor not-initialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check board is support I2C to work with this sensor
|
||||||
|
*
|
||||||
|
* @return true Supported
|
||||||
|
* @return false Not supported
|
||||||
|
*/
|
||||||
|
bool Sht::boardSupported(void) {
|
||||||
|
if (this->_bsp == NULL) {
|
||||||
|
this->_bsp = getBoardDef(this->_boardType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((this->_bsp == NULL) || (this->_bsp->I2C.supported == false)) {
|
||||||
|
AgLog("Board not supported");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Sht:: Sht object
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
*/
|
||||||
|
Sht::Sht(BoardType type) : _boardType(type) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destroy the Sht:: Sht object
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Sht::~Sht() {}
|
||||||
|
|
||||||
|
#if defined(ESP8266)
|
||||||
|
/**
|
||||||
|
* @brief Init sensor, Ifthis funciton not call the other funtion call will
|
||||||
|
* always return false or value invalid
|
||||||
|
*
|
||||||
|
* @param wire wire TwoWire instance, Must be initialized
|
||||||
|
* @param debugStream Point to debug Serial to print debug log
|
||||||
|
* @return true Sucecss
|
||||||
|
* @return false Failure
|
||||||
|
*/
|
||||||
|
bool Sht::begin(TwoWire &wire, Stream &debugStream) {
|
||||||
|
_debugStream = &debugStream;
|
||||||
|
return begin(wire);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* @brief Initialize sensor, should be init sensor before use other API, if not
|
||||||
|
* return result always failed
|
||||||
|
*
|
||||||
|
* @param wire TwoWire instance, Must be initialized
|
||||||
|
* @return true Success
|
||||||
|
* @return false Failure
|
||||||
|
*/
|
||||||
|
bool Sht::begin(TwoWire &wire) {
|
||||||
|
if (_isBegin) {
|
||||||
|
AgLog("Initialized, call end() then try again");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boardSupported() == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create new sensor */
|
||||||
|
_sensor = new SHTSensor();
|
||||||
|
if (_sensor == nullptr) {
|
||||||
|
AgLog("Create SHTSensor failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Initialize sensor */
|
||||||
|
if (shtSensor()->init(wire) == false) {
|
||||||
|
AgLog("Initialize SHTSensor failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only supported by SHT3x
|
||||||
|
if (shtSensor()->setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM) == false) {
|
||||||
|
AgLog("Configure sensor failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AgLog("Initialize");
|
||||||
|
_isBegin = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief De-initialize sht sensor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void Sht::end(void) {
|
||||||
|
if (_isBegin == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete shtSensor();
|
||||||
|
_isBegin = false;
|
||||||
|
#if defined(ESP8266)
|
||||||
|
_debugStream = nullptr;
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
_isBegin = false;
|
||||||
|
AgLog("De-Initialize");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get temprature degree celcius
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
float Sht::getTemperature(void) { return shtSensor()->getTemperature(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get humidity
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
float Sht::getRelativeHumidity(void) { return shtSensor()->getHumidity(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Measure temperature and humidity
|
||||||
|
*
|
||||||
|
* @return true Success
|
||||||
|
* @return false Failure
|
||||||
|
*/
|
||||||
|
bool Sht::measure(void) { return shtSensor()->readSample(); }
|
42
src/sht/sht.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#ifndef _SHT_H_
|
||||||
|
#define _SHT_H_
|
||||||
|
|
||||||
|
#include "../main/BoardDef.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class with define how to handlet sensirion sensor sht4x and
|
||||||
|
* sht3x(Temperature and humidity sensor)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class Sht {
|
||||||
|
private:
|
||||||
|
BoardType _boardType;
|
||||||
|
bool _isBegin = false;
|
||||||
|
void *_sensor;
|
||||||
|
const BoardDef *_bsp = NULL;
|
||||||
|
#if defined(ESP8266)
|
||||||
|
Stream *_debugStream = nullptr;
|
||||||
|
const char *TAG = "SHT";
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
bool isBegin(void);
|
||||||
|
bool boardSupported(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Sht(BoardType type);
|
||||||
|
~Sht();
|
||||||
|
|
||||||
|
#if defined(ESP8266)
|
||||||
|
bool begin(TwoWire &wire, Stream &debugStream);
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
bool begin(TwoWire &wire);
|
||||||
|
void end(void);
|
||||||
|
bool measure(void);
|
||||||
|
float getTemperature(void);
|
||||||
|
float getRelativeHumidity(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /** _SHT_H_ */
|
@ -1,210 +0,0 @@
|
|||||||
#include "sht4x.h"
|
|
||||||
#include "../library/SensirionSHT4x/src/SensirionI2CSht4x.h"
|
|
||||||
|
|
||||||
#define shtSensor() ((SensirionI2CSht4x *)(this->_sensor))
|
|
||||||
|
|
||||||
#if defined(ESP8266)
|
|
||||||
bool Sht::begin(TwoWire &wire, Stream &debugStream) {
|
|
||||||
this->_debugStream = &debugStream;
|
|
||||||
return this->begin(wire);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Sht::Sht(BoardType type) : _boardType(type) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Init sensor, Ifthis funciton not call the other funtion call will
|
|
||||||
* always return false or value invalid
|
|
||||||
*
|
|
||||||
* @param wire TwoWire instance, Must be initialized
|
|
||||||
* @return true Success
|
|
||||||
* @return false Failure
|
|
||||||
*/
|
|
||||||
bool Sht::begin(TwoWire &wire) {
|
|
||||||
if (this->_isInit) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->boardSupported() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this->_sensor = new SensirionI2CSht4x();
|
|
||||||
shtSensor()->begin(wire, SHT40_I2C_ADDR_44);
|
|
||||||
if (shtSensor()->softReset() != 0) {
|
|
||||||
AgLog("Reset sensor fail, look like sensor is not on I2C bus");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
delay(10);
|
|
||||||
|
|
||||||
this->_isInit = true;
|
|
||||||
AgLog("Init");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sht::end(void) {
|
|
||||||
if (this->_isInit == false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->_isInit = false;
|
|
||||||
delete shtSensor();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get temperature degrees celsius
|
|
||||||
*
|
|
||||||
* @return float value <= 256.0f is invalid, That mean sensor has issue or
|
|
||||||
* communication to sensor not worked as well.
|
|
||||||
*/
|
|
||||||
float Sht::getTemperature(void) {
|
|
||||||
float temperature;
|
|
||||||
float humidity;
|
|
||||||
if (this->measureMediumPrecision(temperature, humidity)) {
|
|
||||||
return temperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -256.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get humidity
|
|
||||||
*
|
|
||||||
* @return float Percent(0 - 100), value < 0 is invalid.
|
|
||||||
*/
|
|
||||||
float Sht::getRelativeHumidity(void) {
|
|
||||||
float temperature;
|
|
||||||
float humidity;
|
|
||||||
if (this->measureMediumPrecision(temperature, humidity)) {
|
|
||||||
return humidity;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sht::boardSupported(void) {
|
|
||||||
if (this->_bsp == NULL) {
|
|
||||||
this->_bsp = getBoardDef(this->_boardType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((this->_bsp == NULL) || (this->_bsp->I2C.supported == false)) {
|
|
||||||
AgLog("Board not supported");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Sht::sdaPin(void) {
|
|
||||||
if (this->_bsp) {
|
|
||||||
return this->_bsp->I2C.sda_pin;
|
|
||||||
}
|
|
||||||
AgLog("sdaPin(): board not supported I2C");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Sht::sclPin(void) {
|
|
||||||
if (this->_bsp) {
|
|
||||||
return this->_bsp->I2C.scl_pin;
|
|
||||||
}
|
|
||||||
AgLog("sdlPin(): board not supported I2C");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sht::checkInit(void) {
|
|
||||||
if (this->_isInit) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
AgLog("Sensor no-initialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sht::measureHighPrecision(float &temperature, float &humidity) {
|
|
||||||
if (this->checkInit() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shtSensor()->measureHighPrecision(temperature, humidity) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sht::measureMediumPrecision(float &temperature, float &humidity) {
|
|
||||||
if (this->checkInit() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shtSensor()->measureMediumPrecision(temperature, humidity) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sht::measureLowestPrecision(float &temperature, float &humidity) {
|
|
||||||
if (this->checkInit() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shtSensor()->measureLowestPrecision(temperature, humidity) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sht::activateHighestHeaterPowerShort(float &temperature, float &humidity) {
|
|
||||||
if (this->checkInit() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shtSensor()->activateHighestHeaterPowerShort(temperature, humidity) ==
|
|
||||||
0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sht::activateMediumHeaterPowerLong(float &temperature, float &humidity) {
|
|
||||||
if (this->checkInit() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shtSensor()->activateMediumHeaterPowerLong(temperature, humidity) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sht::activateLowestHeaterPowerLong(float &temperature, float &humidity) {
|
|
||||||
if (this->checkInit() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shtSensor()->activateLowestHeaterPowerLong(temperature, humidity) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sht::getSerialNumber(uint32_t &serialNumber) {
|
|
||||||
if (this->checkInit() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shtSensor()->serialNumber(serialNumber) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sht::softReset(void) {
|
|
||||||
if (this->checkInit() == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shtSensor()->softReset() == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
#ifndef _AIR_GRADIENT_SHT_H_
|
|
||||||
#define _AIR_GRADIENT_SHT_H_
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <Wire.h>
|
|
||||||
|
|
||||||
#include "../bsp/BoardDef.h"
|
|
||||||
|
|
||||||
class Sht {
|
|
||||||
public:
|
|
||||||
#if defined(ESP8266)
|
|
||||||
bool begin(TwoWire &wire, Stream &debugStream);
|
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
Sht(BoardType type);
|
|
||||||
bool begin(TwoWire &wire);
|
|
||||||
void end(void);
|
|
||||||
|
|
||||||
float getTemperature(void);
|
|
||||||
float getRelativeHumidity(void);
|
|
||||||
|
|
||||||
private:
|
|
||||||
BoardType _boardType;
|
|
||||||
bool _isInit = false;
|
|
||||||
void *_sensor;
|
|
||||||
const BoardDef *_bsp = NULL;
|
|
||||||
#if defined(ESP8266)
|
|
||||||
Stream *_debugStream = nullptr;
|
|
||||||
const char *TAG = "SHT4x";
|
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
bool checkInit(void);
|
|
||||||
bool boardSupported(void);
|
|
||||||
int sdaPin(void);
|
|
||||||
int sclPin(void);
|
|
||||||
|
|
||||||
bool measureHighPrecision(float &temperature, float &humidity);
|
|
||||||
bool measureMediumPrecision(float &temperature, float &humidity);
|
|
||||||
bool measureLowestPrecision(float &temperature, float &humidity);
|
|
||||||
|
|
||||||
bool activateHighestHeaterPowerShort(float &temperature, float &humidity);
|
|
||||||
bool activateMediumHeaterPowerLong(float &temperature, float &humidity);
|
|
||||||
bool activateLowestHeaterPowerLong(float &temperature, float &humidity);
|
|
||||||
|
|
||||||
bool getSerialNumber(uint32_t &serialNumber);
|
|
||||||
bool softReset(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /** _AIR_GRADIENT_SHT_H_ */
|
|