Compare commits

...

25 Commits
3.0.0 ... 3.0.1

Author SHA1 Message Date
87a3b6e409 Merge commit 'f17afd932ebef7d0d2e49c130e076dfc5462c086' 2024-02-15 20:07:42 +07:00
dd62a10ed5 Update arduino library version 3.0.1 2024-02-15 20:01:02 +07:00
225d079d48 Merge pull request #52 from airgradienthq/feature/led-test-with-button-on-power-up
Feature/led test with button on power up
2024-02-15 19:58:40 +07:00
7ea43fdc7d Update lib version string: 3.0.1 2024-02-15 19:56:46 +07:00
67ad912a71 update DISPLAY_DELAY_SHOW_CONTENT_MS from 3000 to 6000 2024-02-15 19:55:17 +07:00
5602a456a7 fix: wrong config key for PM Standard 2024-02-15 19:48:58 +07:00
28e5aa4e69 LED test update: support country TH and Show display Press now for LED test 2024-02-15 13:40:42 +07:00
e55f3b6e74 LED Test on Button 2024-02-15 11:21:04 +07:00
f17afd932e Auto detect 1PST, 1PPT and 1PP 2024-02-15 10:56:53 +07:00
94ead3751b Merge pull request #48 from airgradienthq/feature/Basic_V4-show-full-device-id-on-display
Feature/basic v4 show full device id on display
2024-02-07 21:01:22 +07:00
ab600e014a Update content and line space of display show serial number value 2024-02-07 21:00:13 +07:00
60d02d88b5 Update show device id on 4 line of display 2024-02-07 09:43:52 +07:00
9e461a9036 Merge pull request #47 from airgradienthq/hotfix/ledbar-test
updated `ledBarTestRequested`
2024-02-06 13:25:14 +07:00
ce6bee19af updated ledBarTestRequested 2024-02-06 13:24:10 +07:00
0354c6e634 Merge pull request #46 from airgradienthq/optimize-and-clean-code
Optimize firmware and bug fix
2024-02-06 10:56:39 +07:00
ac9efccd94 Fix: missing handle serverConfig ledBarTestRequested test 2024-02-06 10:52:01 +07:00
4df0fc5d5c Set S8 Automatic Baseline Period 2024-02-06 10:41:10 +07:00
4c180fedbd Support SHT3x 2024-02-06 09:38:37 +07:00
8b73ac77f9 Update TVOC missing call to polling data 2024-02-04 16:30:50 +07:00
cbb444f1bc Change WiFi connection screen to four lines 2024-02-04 16:20:37 +07:00
b2762a3b6c [Lib] Change LedBar function name getNumberOfLed to getNumberOfLeds 2024-02-04 15:48:06 +07:00
512420f5a2 Update: Explain difference between PMS5003 and PMS5003T 2024-02-04 15:22:06 +07:00
e94a625072 Remove .DS_Store 2024-02-04 15:19:39 +07:00
7bbe81ad1d Fix: TestPM (before PM Simple) only shows nulls 2024-02-04 15:10:46 +07:00
335fad3f0d Clean code and add comments 2024-02-04 15:04:38 +07:00
62 changed files with 5086 additions and 1935 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.vscode
*.DS_Store

View File

@ -35,6 +35,7 @@ If you have any questions or problems, check out [our forum](https://forum.airgr
- [Sensirion Core](https://github.com/Sensirion/arduino-core/)
- [Sensirion I2C SGP41](https://github.com/Sensirion/arduino-i2c-sgp41)
- [Sensirion I2C SHT4x](https://github.com/Sensirion/arduino-i2c-sht4x)
- [Sensirion I2C SHT3x](https://github.com/Sensirion/arduino-i2c-sht3x)
- [PMS](https://github.com/fu-hsi/pms)
## License

BIN
examples/.DS_Store vendored

Binary file not shown.

View File

@ -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:
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
"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"
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
https://forum.airgradient.com/
@ -32,67 +36,353 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
#include <Arduino_JSON.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>
#include <U8g2lib.h>
#include <WiFiClient.h>
#include <WiFiManager.h>
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;
#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 6000 /** 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
String APIROOT = "http://hw.airgradient.com/";
Serial.printf("[AgSchedule] handle 0x%08x, period: %d(ms)\r\n",
(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.
// Then it will go into an offline mode.
boolean connectWIFI = true;
private:
void (*handler)(void);
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;
unsigned long previousOled = 0;
bool co2CalibrationRequest = false;
uint32_t serverConfigLoadTime = 0;
String HOSTPOT = "";
/** Init http client */
WiFiClient wifiClient;
HTTPClient client;
if (client.begin(wifiClient, uri) == false) {
configFailed = true;
return false;
}
const int sendToServerInterval = 60000;
const int pollServerConfigInterval = 30000;
const int co2CalibCountdown = 5; /** Seconds */
unsigned long previoussendToServer = 0;
/** Get */
int retCode = client.GET();
if (retCode != 200) {
client.end();
configFailed = true;
return false;
}
const int co2Interval = 5000;
unsigned long previousCo2 = 0;
int Co2 = 0;
/** clear failed */
configFailed = false;
const int pm25Interval = 5000;
unsigned long previousPm25 = 0;
int pm25 = 0;
/** Get response string */
String respContent = client.getString();
client.end();
Serial.println("Get server config: " + respContent);
const int tempHumInterval = 2500;
unsigned long previousTempHum = 0;
float temp = 0;
int hum = 0;
long val;
/** Parse JSON */
JSONVar root = JSON.parse(respContent);
if (JSON.typeof(root) == "undefined") {
/** JSON invalid */
return false;
}
void failedHandler(String msg);
void boardInit(void);
void getServerConfig(void);
void co2Calibration(void);
/** 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 '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() {
Serial.begin(115200);
@ -103,85 +393,67 @@ void setup() {
/** Board init */
boardInit();
/** Init AirGradient server */
agServer.begin();
/** Show boot display */
displayShowText("Basic v4", "Lib:" + ag.getVersion(), "");
displayShowText("DIY basic", "Lib:" + ag.getVersion(), "");
delay(2000);
if (connectWIFI) {
connectToWifi();
/** WiFi connect */
connectToWifi();
if (WiFi.status() == WL_CONNECTED) {
wifiHasConfig = true;
sendPing();
agServer.pollServerConfig(getDevId());
if (agServer.isCo2Calib()) {
co2Calibration();
}
}
/** Show display */
displayShowText("Warm Up", "Serial#", String(ESP.getChipId(), HEX));
delay(10000);
/** Show serial number display */
ag.display.clear();
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() {
currentMillis = millis();
updateOLED();
updateCo2();
updatePm25();
updateTempHum();
sendToServer();
getServerConfig();
configSchedule.run();
serverSchedule.run();
dispSchedule.run();
co2Schedule.run();
pmsSchedule.run();
tempHumSchedule.run();
updateWiFiConnect();
}
void updateCo2() {
if (currentMillis - previousCo2 >= co2Interval) {
previousCo2 += co2Interval;
Co2 = ag.s8.getCo2();
Serial.println(String(Co2));
}
}
void updatePm25() {
if (currentMillis - previousPm25 >= pm25Interval) {
previousPm25 += pm25Interval;
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 {
ln1 = "PM :" + String(pm25) + " ug";
}
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);
static void sendPing() {
JSONVar root;
root["wifi"] = WiFi.RSSI();
root["boot"] = 0;
// delay(1500);
if (agServer.postToServer(getDevId(), JSON.stringify(root))) {
// Ping Server succses
} else {
// Ping server failed
}
// delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
}
void displayShowText(String ln1, String ln2, String ln3) {
@ -198,58 +470,24 @@ void displayShowText(String ln1, String ln2, String ln3) {
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
void connectToWifi() {
WiFiManager wifiManager;
// WiFi.disconnect(); //to delete previous saved hotspot
String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
// displayShowText("Connect", "AG-", String(ESP.getChipId(), HEX));
delay(2000);
// wifiManager.setTimeout(90);
wifiSSID = "AG-" + String(ESP.getChipId(), HEX);
wifiManager.setConfigPortalBlocking(false);
wifiManager.setConfigPortalTimeout(180);
wifiManager.autoConnect(HOTSPOT.c_str(), wifiApPass.c_str());
wifiManager.setConfigPortalTimeout(WIFI_CONNECT_COUNTDOWN_MAX);
wifiManager.autoConnect(wifiSSID.c_str(), WIFI_HOTSPOT_PASSWORD_DEFAULT);
uint32_t lastTime = millis();
int count = 179;
displayShowText("180 sec", "SSID:",HOTSPOT);
int count = WIFI_CONNECT_COUNTDOWN_MAX;
displayShowText(String(WIFI_CONNECT_COUNTDOWN_MAX) + " sec",
"SSID:", wifiSSID);
while (wifiManager.getConfigPortalActive()) {
wifiManager.process();
uint32_t ms = (uint32_t)(millis() - lastTime);
if (ms >= 1000) {
lastTime = millis();
displayShowText(String(count) + " sec", "SSID:",HOTSPOT);
displayShowText(String(count) + " sec", "SSID:", wifiSSID);
count--;
// Timeout
@ -261,20 +499,13 @@ void connectToWifi() {
if (!WiFi.isConnected()) {
displayShowText("Booting", "offline", "mode");
Serial.println("failed to connect and hit timeout");
delay(6000);
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
}
}
void failedHandler(String msg) {
while (true) {
Serial.println(msg);
delay(1000);
}
}
void boardInit(void) {
static void boardInit(void) {
/** Init SHT sensor */
if (ag.sht.begin(Wire) == false) {
if (ag.sht4x.begin(Wire) == false) {
failedHandler("SHT init failed");
}
@ -293,152 +524,18 @@ void boardInit(void) {
ag.display.setTextColor(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 updateServerConfigLoadTime(void) {
serverConfigLoadTime = millis();
if (serverConfigLoadTime == 0) {
serverConfigLoadTime = 1;
static void failedHandler(String msg) {
while (true) {
Serial.println(msg);
delay(1000);
}
}
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(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) {
static void co2Calibration(void) {
/** 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",
String(co2CalibCountdown - i) + " sec");
String(SENSOR_CO2_CALIB_COUNTDOWN_MAX - i) + " sec");
delay(1000);
}
@ -452,9 +549,115 @@ void co2Calibration(void) {
count++;
}
displayShowText("Finish", "after", String(count) + " sec");
delay(2000);
delay(DISPLAY_DELAY_SHOW_CONTENT_MS);
} else {
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() {
temp = ag.sht4x.getTemperature();
hum = ag.sht4x.getRelativeHumidity();
Serial.printf("Temperature: %0.2f\r\n", temp);
Serial.printf(" Humidity: %d\r\n", hum);
}
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}

View 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 6000 /** 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";
}

View File

@ -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.
CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
*/
#include <AirGradient.h>
#ifdef ESP8266
AirGradient ag = AirGradient(BOARD_DIY_PRO_INDOOR_V4_2);
// AirGradient ag = AirGradient(BOARD_DIY_BASIC_KIT);
AirGradient ag = AirGradient(DIY_BASIC);
#else
// AirGradient ag = AirGradient(BOARD_ONE_INDOOR_MONITOR_V9_0);
AirGradient ag = AirGradient(BOARD_OUTDOOR_MONITOR_V1_3);
/** Create airgradient instance for 'OPEN_AIR_OUTDOOR' board */
AirGradient ag = AirGradient(OPEN_AIR_OUTDOOR);
#endif
void failedHandler(String msg);
void setup() {
void setup()
{
Serial.begin(115200);
/** Init CO2 sensor */
#ifdef ESP8266
if (ag.s8.begin(&Serial) == false) {
if (ag.s8.begin(&Serial) == false)
{
#else
if (ag.s8.begin(Serial1) == false) {
if (ag.s8.begin(Serial1) == false)
{
#endif
failedHandler("SenseAir S8 init failed");
}
}
void loop() {
int CO2 = ag.s8.getCo2();
Serial.printf("CO2: %d\r\n", CO2);
void loop()
{
int co2Ppm = ag.s8.getCo2();
Serial.printf("CO2: %d\r\n", co2Ppm);
delay(5000);
}
void failedHandler(String msg) {
while (true) {
void failedHandler(String msg)
{
while (true)
{
Serial.println(msg);
delay(1000);
}

View File

@ -11,8 +11,8 @@
/**
* @brief Define test board
*/
#define TEST_BOARD_OUTDOOR_MONITOR_V1_3 0
#define TEST_BOARD_ONE_INDOOR_MONITOR_V9_0 1
#define TEST_OPEN_AIR_OUTDOOR 0
#define TEST_ONE_INDOOR 1
/**
* @brief Define test sensor
*/
@ -21,21 +21,21 @@
#define TEST_SENSOR_SGP4x 0
#define TEST_SWITCH 0
#define TEST_OLED 0
#if TEST_BOARD_OUTDOOR_MONITOR_V1_3
#if TEST_OPEN_AIR_OUTDOOR
#define TEST_STATUS_LED 0
#define TEST_PMS5003T 1
#endif
#define TEST_WATCHDOG 1
#if TEST_BOARD_ONE_INDOOR_MONITOR_V9_0
#if TEST_ONE_INDOOR
#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);
#if TEST_OPEN_AIR_OUTDOOR
AirGradient ag(OPEN_AIR_OUTDOOR);
#elif TEST_ONE_INDOOR
AirGradient ag(ONE_INDOOR);
#else
#error "Must enable board test
#endif
@ -99,7 +99,7 @@ void setup() {
#if TEST_SENSOR_SHT4x
if (ag.sht.begin(Wire)) {
if (ag.sht4x.begin(Wire)) {
log_i("SHT init success");
} else {
log_i("SHT init failed");
@ -218,9 +218,9 @@ void loop() {
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());
log_i("Get sht4x temperature: %0.2f (degree celsius)",
ag.sht4x.getTemperature());
log_i("Get sht4x temperature: %0.2f (%%)", ag.sht4x.getRelativeHumidity());
}
#endif
@ -237,14 +237,14 @@ void loop() {
#if TEST_LED
static uint32_t ledTime;
#if TEST_BOARD_OUTDOOR_MONITOR_V1_3
#if TEST_OPEN_AIR_OUTDOOR
// ms = (uint32_t)(millis() - ledTime);
// if(ms >= 500)
// {
// ledTime = millis();
// led.ledToggle();
// }
#elif TEST_BOARD_ONE_INDOOR_MONITOR_V9_0
#elif TEST_ONE_INDOOR
static int ledIndex;
static int ledIndexOld;
@ -262,7 +262,7 @@ void loop() {
led.ledSetColor(255, 0, 0, ledIndex);
ledIndexOld = ledIndex;
ledIndex++;
if (ledIndex >= led.getNumberOfLed()) {
if (ledIndex >= led.getNumberOfLeds()) {
ledIndex = 0;
}
}
@ -293,7 +293,7 @@ void loop() {
static bool ledOn = false;
if (ledNum == 0) {
ledNum = ag.ledBar.getNumberOfLed();
ledNum = ag.ledBar.getNumberOfLeds();
log_i("Get number of led: %d", ledNum);
if (ledNum) {
ag.ledBar.setBrighness(0xff);

View File

@ -4,23 +4,23 @@
/**
* @brief Define test board
*/
#define TEST_BOARD_DIY_BASIC_KIT 0
#define TEST_BOARD_DIY_PRO_INDOOR_V4_2 1
#define TEST_DIY_BASIC 1
/**
* @brief Define test sensor
*/
#define TEST_SENSOR_SenseAirS8 0
#define TEST_SENSOR_SenseAirS8 1
// #define S8_BASELINE_CALIB
#define TEST_SENSOR_PMS5003 0
#define TEST_SENSOR_SHT4x 0
#define TEST_SENSOR_SGP4x 1
#define TEST_SENSOR_SGP4x 0
#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);
#if TEST_DIY_BASIC
AirGradient ag(DIY_BASIC);
#elif TEST_DIY_PRO_INDOOR_V4_2
AirGradient ag(DIY_PRO_INDOOR_V4_2);
#else
#error "Board test not defined"
#endif
@ -38,11 +38,19 @@ void setup() {
Serial.println("CO2S8 sensor init failure");
}
#ifdef S8_BASELINE_CALIB
if (ag.s8.setBaselineCalibration()) {
Serial.println("Manual calib success");
} else {
Serial.println("Manual calib failure");
}
#else
if (ag.s8.setAutoCalib(8)) {
Serial.println("Set auto calib success");
} else {
Serial.println("Set auto calib failure");
}
#endif
delay(5000);
#endif
@ -59,7 +67,7 @@ void setup() {
#endif
#if TEST_SENSOR_SHT4x
if (ag.sht.begin(Wire, Serial)) {
if (ag.sht4x.begin(Wire, Serial)) {
Serial.println("SHT init success");
} else {
Serial.println("SHT init failed");
@ -132,7 +140,7 @@ void loop() {
shtTime = millis();
float temperature, humidity;
Serial.printf("SHT Temperature: %f, Humidity: %f\r\n",
ag.sht.getTemperature(), ag.sht.getRelativeHumidity());
ag.sht4x.getTemperature(), ag.sht4x.getRelativeHumidity());
}
#endif
@ -142,7 +150,7 @@ void loop() {
/***
* Must call this task on loop and avoid delay on loop over 1000 ms
*/
*/
ag.sgp41.handle();
if (ms >= 1000) {

View File

@ -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
*/
@ -7,11 +8,10 @@ CC BY-SA 4.0 Attribution-ShareAlike 4.0 International License
#include <AirGradient.h>
#ifdef ESP8266
AirGradient ag = AirGradient(BOARD_DIY_PRO_INDOOR_V4_2);
// AirGradient ag = AirGradient(BOARD_DIY_BASIC_KIT);
AirGradient ag = AirGradient(DIY_BASIC);
#else
// AirGradient ag = AirGradient(BOARD_ONE_INDOOR_MONITOR_V9_0);
AirGradient ag = AirGradient(BOARD_OUTDOOR_MONITOR_V1_3);
// AirGradient ag = AirGradient(ONE_INDOOR);
AirGradient ag = AirGradient(OPEN_AIR_OUTDOOR);
#endif
void failedHandler(String msg);
@ -19,16 +19,16 @@ void failedHandler(String msg);
void setup() {
Serial.begin(115200);
#ifdef ESP8266
if(ag.pms5003.begin(&Serial) == false) {
if (ag.pms5003.begin(&Serial) == false) {
failedHandler("Init PMS5003 failed");
}
#else
if (ag.getBoardType() == BOARD_OUTDOOR_MONITOR_V1_3) {
if(ag.pms5003t_1.begin(Serial0) == false) {
if (ag.getBoardType() == OPEN_AIR_OUTDOOR) {
if (ag.pms5003t_1.begin(Serial0) == false) {
failedHandler("Init PMS5003T failed");
}
} else {
if(ag.pms5003.begin(Serial0) == false) {
if (ag.pms5003.begin(Serial0) == false) {
failedHandler("Init PMS5003T failed");
}
}
@ -37,25 +37,37 @@ void setup() {
void loop() {
int PM2;
bool readResul = false;
#ifdef ESP8266
PM2 = ag.pms5003.getPm25Ae();
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));
#else
if (ag.getBoardType() == BOARD_OUTDOOR_MONITOR_V1_3) {
PM2 = ag.pms5003t_1.getPm25Ae();
} else {
if (ag.pms5003.readData()) {
PM2 = ag.pms5003.getPm25Ae();
}
Serial.printf("PM2.5 in ug/m3: %d\r\n", PM2);
if (ag.getBoardType() == BOARD_OUTDOOR_MONITOR_V1_3) {
Serial.printf("PM2.5 in US AQI: %d\r\n",
ag.pms5003t_1.convertPm25ToUsAqi(PM2));
} else {
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));
}
#else
if (ag.getBoardType() == OPEN_AIR_OUTDOOR) {
if (ag.pms5003t_1.readData()) {
PM2 = ag.pms5003t_1.getPm25Ae();
readResul = true;
}
} else {
if (ag.pms5003.readData()) {
PM2 = ag.pms5003.getPm25Ae();
readResul = true;
}
}
if (readResul) {
Serial.printf("PM2.5 in ug/m3: %d\r\n", PM2);
if (ag.getBoardType() == OPEN_AIR_OUTDOOR) {
Serial.printf("PM2.5 in US AQI: %d\r\n",
ag.pms5003t_1.convertPm25ToUsAqi(PM2));
} else {
Serial.printf("PM2.5 in US AQI: %d\r\n",
ag.pms5003.convertPm25ToUsAqi(PM2));
}
}
#endif
delay(5000);

View File

@ -0,0 +1,39 @@
#include <AirGradient.h>
AirGradient ag(DIY_BASIC);
void failedHandler(String msg);
void setup() {
Serial.begin(115200);
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
if (ag.sht3x.begin(Wire, Serial) == false) {
failedHandler("SHT3x init failed");
}
}
void loop() {
float temp = ag.sht3x.getTemperature();
if (temp <= -256.0f) {
Serial.println("Get temperature failed");
} else {
Serial.printf("Get temperature: %f\r\n", temp);
}
float hum = ag.sht3x.getRelativeHumidity();
if (hum < 0) {
Serial.println("Get humidity failed");
} else {
Serial.printf("Get humidity: %f\r\n", hum);
}
delay(1000);
}
void failedHandler(String msg) {
while (true) {
Serial.println(msg);
delay(1000);
}
}

View File

@ -1,5 +1,5 @@
name=AirGradient Air Quality Sensor
version=3.0.0
version=3.0.1
author=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.

View File

@ -1,11 +1,11 @@
#include "AirGradient.h"
#define AG_LIB_VER "3.0.0"
#define AG_LIB_VER "3.0.1"
AirGradient::AirGradient(BoardType type)
: pms5003(type), pms5003t_1(type), pms5003t_2(type), s8(type), sht(type), sgp41(type),
display(type), boardType(type), button(type), statusLed(type),
ledBar(type), watchdog(type) {}
: pms5003(type), pms5003t_1(type), pms5003t_2(type), s8(type), sht4x(type),
sht3x(type), sgp41(type), display(type), boardType(type), button(type),
statusLed(type), ledBar(type), watchdog(type) {}
/**
* @brief Get pin number for I2C SDA

View File

@ -1,18 +1,23 @@
#ifndef _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 "pm/pms5003.h"
#include "pm/pms5003t.h"
#include "sgp/sgp41.h"
#include "main/BoardDef.h"
#include "main/HardwareWatchdog.h"
#include "main/LedBar.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/sht4x.h"
#include "sht/sht3x.h"
/**
* @brief Class with define all the sensor has supported by Airgradient. Each
* sensor usage must be init before use.
*/
class AirGradient {
public:
AirGradient(BoardType type);
@ -21,7 +26,15 @@ public:
* @brief Plantower PMS5003 sensor
*/
PMS5003 pms5003;
/**
* @brief Plantower PMS5003T sensor: connect to PM1 connector on
* OPEN_AIR_OUTDOOR.
*/
PMS5003T pms5003t_1;
/**
* @brief Plantower PMS5003T sensor: connect to PM2 connector on
* OPEN_AIR_OUTDOOR.
*/
PMS5003T pms5003t_2;
/**
@ -30,18 +43,24 @@ public:
S8 s8;
/**
* @brief Temperature and humidity sensor
* @brief SHT41 Temperature and humidity sensor
*/
Sht sht;
Sht4x sht4x;
/**
* @brief TVOC and NOx sensor
* @brief SHT3x Temperature and humidity sensor
*
*/
Sht3x sht3x;
/**
* @brief SGP41 TVOC and NOx sensor
*
*/
Sgp41 sgp41;
/**
* @brief Display
* @brief OLED Display
*
*/
Display display;
@ -55,18 +74,43 @@ public:
* @brief LED
*/
StatusLed statusLed;
/**
* @brief RGB LED array
*
*/
LedBar ledBar;
/**
* @brief Hardware watchdog
* @brief External hardware watchdog
*/
HardwareWatchdog watchdog;
/**
* @brief Get I2C SDA pin has of board supported
*
* @return int Pin number if -1 invalid
*/
int getI2cSdaPin(void);
/**
* @brief Get I2C SCL pin has of board supported
*
* @return int Pin number if -1 invalid
*/
int getI2cSclPin(void);
/**
* @brief Get the Board Type
*
* @return BoardType @ref BoardType
*/
BoardType getBoardType(void);
/**
* @brief Get the library version string
*
* @return String
*/
String getVersion(void);
private:

View File

@ -3,7 +3,7 @@
#include "../library/Adafruit_SSD1306_Wemos_OLED/Adafruit_SSD1306.h"
#define disp(func) \
if (this->_boardType == BOARD_DIY_BASIC_KIT) { \
if (this->_boardType == DIY_BASIC) { \
((Adafruit_SSD1306 *)(this->oled))->func; \
} else { \
((Adafruit_SH110X *)(this->oled))->func; \
@ -19,7 +19,18 @@ void Display::begin(TwoWire &wire, Stream &debugStream) {
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) {
if (_isBegin) {
AgLog("Initialized, call end() then try again");
return;
}
this->_bsp = getBoardDef(this->_boardType);
if ((this->_bsp == nullptr) || (this->_bsp->I2C.supported == false) ||
(this->_bsp->OLED.supported == false)) {
@ -28,67 +39,106 @@ void Display::begin(TwoWire &wire) {
}
/** Init OLED */
if (this->_boardType == BOARD_DIY_BASIC_KIT) {
if (this->_boardType == DIY_BASIC) {
AgLog("Init Adafruit_SSD1306");
Adafruit_SSD1306 *_oled = new Adafruit_SSD1306();
_oled->begin(wire, SSD1306_SWITCHCAPVCC, this->_bsp->OLED.addr);
this->oled = _oled;
} else {
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);
this->oled = _oled;
}
this->_isInit = true;
this->_isBegin = true;
disp(clearDisplay());
AgLog("Init");
AgLog("Initialize");
}
/**
* @brief Clear display buffer
*
*/
void Display::clear(void) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
disp(clearDisplay());
}
/**
* @brief Invert display color
*
* @param i 0: black, other is white
*/
void Display::invertDisplay(uint8_t i) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
disp(invertDisplay(i));
}
/**
* @brief Send display frame buffer to OLED
*
*/
void Display::show() {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
disp(display());
}
/**
* @brief Set display contract
*
* @param value Contract (0;255);
*/
void Display::setContrast(uint8_t value) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
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) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
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) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
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) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
disp(setCursor(x, y));
@ -100,7 +150,7 @@ void Display::setCursor(int16_t x, int16_t y) {
* @param color 0:black, 1: While
*/
void Display::setTextColor(uint16_t color) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
disp(setTextColor(color));
@ -113,66 +163,120 @@ void Display::setTextColor(uint16_t color) {
* @param backGroundColor Text background color
*/
void Display::setTextColor(uint16_t foreGroundColor, uint16_t backGroundColor) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
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) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
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[],
int16_t w, int16_t h, uint16_t color) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
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[]) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
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) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
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) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
disp(drawCircle(x, y, r, color));
}
void Display::drawRect(int x0, int y0, int x1, int y1, uint16_t color) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
disp(drawRect(x0, y0, x1, y1, color));
}
bool Display::checkInit(void) {
if (this->_isInit) {
bool Display::isBegin(void) {
if (this->_isBegin) {
return true;
}
AgLog("OLED is not init");
AgLog("Display not-initialized");
return false;
}
void Display::setRotation(uint8_t r) {
if (checkInit() == false) {
if (isBegin() == false) {
return;
}
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");
}

View File

@ -1,10 +1,14 @@
#ifndef _AIR_GRADIENT_OLED_H_
#define _AIR_GRADIENT_OLED_H_
#include "../bsp/BoardDef.h"
#include "../main/BoardDef.h"
#include <Arduino.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 {
public:
const uint16_t COLOR_WHILTE = 1;
@ -15,10 +19,11 @@ public:
#endif
Display(BoardType type);
void begin(TwoWire &wire);
void end(void);
void clear(void); // .clear
void clear(void);
void invertDisplay(uint8_t i);
void show(); // .show()
void show();
void setContrast(uint8_t value);
void drawPixel(int16_t x, int16_t y, uint16_t color);
@ -39,14 +44,14 @@ private:
BoardType _boardType;
const BoardDef *_bsp = nullptr;
void *oled;
bool _isInit = false;
bool _isBegin = false;
#if defined(ESP8266)
const char *TAG = "oled";
Stream *_debugStream = nullptr;
#else
#endif
bool checkInit(void);
bool isBegin(void);
};
#endif /** _AIR_GRADIENT_OLED_H_ */

View File

@ -0,0 +1,14 @@
---
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']
...

View File

@ -0,0 +1,110 @@
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/exampleUsageSingleShot/exampleUsageSingleShot.ino
- arduino-cli compile --libraries=".." --warnings all --fqbn arduino:avr:mega ./examples/exampleUsageSingleShot/exampleUsageSingleShot.ino
- arduino-cli compile --libraries=".." --warnings all --fqbn arduino:avr:nano ./examples/exampleUsageSingleShot/exampleUsageSingleShot.ino
- arduino-cli compile --libraries=".." --warnings all --fqbn arduino:avr:uno ./examples/exampleUsageSingleShot/exampleUsageSingleShot.ino
- arduino-cli compile --libraries=".." --warnings all --fqbn esp32:esp32:esp32 ./examples/exampleUsageSingleShot/exampleUsageSingleShot.ino
- arduino-cli compile --libraries=".." --warnings all --fqbn esp8266:esp8266:generic ./examples/exampleUsageSingleShot/exampleUsageSingleShot.ino
- 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

View File

@ -0,0 +1,13 @@
# 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]
## [1.0.0] - 2023-10-27
Initial release

View File

@ -0,0 +1,29 @@
BSD 3-Clause License
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:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder 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.

View File

@ -0,0 +1,217 @@
# Sensirion I²C SHT3X Arduino Library
This is the Sensirion SHT3X library for Arduino allowing you to
communicate with a sensor of the SHT3X family over I²C.
<img src="images/SHT3x.png" width="300px">
Click [here](https://sensirion.com/products/catalog/SHT30-DIS-B) to learn more about the Sensirion SHT3X sensor family.
Not all sensors of this driver family support all measurements.
In case a measurement is not supported by all sensors, the products that
support it are listed in the API description.
## Supported sensor types
| Sensor name | I²C Addresses |
| ------------- | -------------- |
|[SHT30A](https://sensirion.com/products/catalog/SHT30A-DIS-B)| **0x44**, 0x45|
|[SHT30](https://sensirion.com/products/catalog/SHT30-DIS-B)| **0x44**, 0x45|
|[SHT31A](https://sensirion.com/products/catalog/SHT31A-DIS-B)| **0x44**, 0x45|
|[SHT31](https://sensirion.com/products/catalog/SHT31-DIS-B)| **0x44**, 0x45|
|[SHT33](https://sensirion.com/products/catalog/SHT33-DIS)| **0x44**, 0x45|
|[SHT35A](https://sensirion.com/products/catalog/SHT35A-DIS-B)| **0x44**, 0x45|
|[SHT35](https://sensirion.com/products/catalog/SHT35-DIS-B)| **0x44**, 0x45|
|[SHT85](https://sensirion.com/sht85)| **0x44** |
The following instructions and examples use a *SHT30*.
## 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 SHT3X` 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 SHT3X to the standard I²C bus of your Arduino board:
<img src="images/SHT3x_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 | 2.15V to 5.5V
The recommended voltage is 3.3V.
### Board specific wiring
You will find pinout schematics for recommended board models below:
<details><summary>Arduino Uno</summary>
<p>
| *SHT3X* | *SHT3X 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>
| *SHT3X* | *SHT3X 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>
| *SHT3X* | *SHT3X 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>
| *SHT3X* | *SHT3X 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>
| *SHT3X* | *SHT3X 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 SHT3X 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 SHT3X``exampleUsage`
The provided example is working with a SHT30, 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/SensirionI2CSht3x.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).

View File

@ -0,0 +1,98 @@
/*
* THIS FILE IS AUTOMATICALLY GENERATED
*
* Generator: sensirion-driver-generator 0.33.0
* Product: sht3x
* Model-Version: 1.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 <SensirionI2cSht3x.h>
#include <Wire.h>
SensirionI2cSht3x sensor;
static char errorMessage[64];
static int16_t error;
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(100);
}
Wire.begin();
sensor.begin(Wire, SHT30_I2C_ADDR_44);
sensor.stopMeasurement();
delay(1);
sensor.softReset();
delay(100);
uint16_t aStatusRegister = 0u;
error = sensor.readStatusRegister(aStatusRegister);
if (error != NO_ERROR) {
Serial.print("Error trying to execute readStatusRegister(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
Serial.print("aStatusRegister: ");
Serial.print(aStatusRegister);
Serial.println();
error = sensor.startPeriodicMeasurement(REPEATABILITY_MEDIUM,
MPS_ONE_PER_SECOND);
if (error != NO_ERROR) {
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
}
void loop() {
float aTemperature = 0.0;
float aHumidity = 0.0;
error = sensor.blockingReadMeasurement(aTemperature, aHumidity);
if (error != NO_ERROR) {
Serial.print("Error trying to execute blockingReadMeasurement(): ");
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();
}

View File

@ -0,0 +1,91 @@
/*
* THIS FILE IS AUTOMATICALLY GENERATED
*
* Generator: sensirion-driver-generator 0.33.0
* Product: sht3x
* Model-Version: 1.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 <SensirionI2cSht3x.h>
#include <Wire.h>
SensirionI2cSht3x sensor;
static char errorMessage[64];
static int16_t error;
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(100);
}
Wire.begin();
sensor.begin(Wire, SHT30_I2C_ADDR_44);
sensor.stopMeasurement();
delay(1);
sensor.softReset();
delay(100);
uint16_t aStatusRegister = 0u;
error = sensor.readStatusRegister(aStatusRegister);
if (error != NO_ERROR) {
Serial.print("Error trying to execute readStatusRegister(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
Serial.print("aStatusRegister: ");
Serial.print(aStatusRegister);
Serial.println();
}
void loop() {
float aTemperature = 0.0;
float aHumidity = 0.0;
error = sensor.measureSingleShot(REPEATABILITY_MEDIUM, false, aTemperature,
aHumidity);
if (error != NO_ERROR) {
Serial.print("Error trying to execute measureSingleShot(): ");
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();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 KiB

View File

@ -0,0 +1,59 @@
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
SensirionI2cSht3x KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
measureSingleShot KEYWORD2
startPeriodicMeasurement KEYWORD2
blockingReadMeasurement KEYWORD2
readStatusRegister KEYWORD2
measureSingleShotHighRepeatability KEYWORD2
measureSingleShotHighRepeatabilityClockStretching KEYWORD2
measureSingleShotMediumRepeatability KEYWORD2
measureSingleShotMediumRepeatabilityClockStretching KEYWORD2
measureSingleShotLowRepeatability KEYWORD2
measureSingleShotLowRepeatabilityClockStretching KEYWORD2
startMeasurement05MpsHighRepeatability KEYWORD2
startMeasurement05MpsMediumRepeatability KEYWORD2
startMeasurement05MpsLowRepeatability KEYWORD2
startMeasurement1MpsHighRepeatability KEYWORD2
startMeasurement1MpsMediumRepeatability KEYWORD2
startMeasurement1MpsLowRepeatability KEYWORD2
startMeasurement2MpsHighRepeatability KEYWORD2
startMeasurement2MpsMediumRepeatability KEYWORD2
startMeasurement2MpsLowRepeatability KEYWORD2
startMeasurement4MpsHighRepeatability KEYWORD2
startMeasurement4MpsMediumRepeatability KEYWORD2
startMeasurement4MpsLowRepeatability KEYWORD2
startMeasurement10MpsHighRepeatability KEYWORD2
startMeasurement10MpsMediumRepeatability KEYWORD2
startMeasurement10MpsLowRepeatability KEYWORD2
startArtMeasurement KEYWORD2
readMeasurement KEYWORD2
stopMeasurement KEYWORD2
enableHeater KEYWORD2
disableHeater KEYWORD2
llreadStatusRegister KEYWORD2
clearStatusRegister KEYWORD2
softReset KEYWORD2
signalTemperature KEYWORD2
signalHumidity KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
sensor KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@ -0,0 +1,11 @@
name=SensirionI2cSht3x
version=1.0.0
author=Sensirion
maintainer=Sensirion
sentence=Library for the SHT3X sensor family by Sensirion
paragraph=Enables you to use the SHT3X sensor family via I2C.
url=https://github.com/Sensirion/arduino-i2c-sht3x
category=Sensors
architectures=*
depends=Sensirion Core
includes=SensirionI2cSht3x.h

View File

@ -0,0 +1,7 @@
generator_version: 0.33.0
model_version: 1.0.0
dg_status:
- released
is_manually_modified: true
first_generated: '2023-10-27 14:38'
last_generated: '2023-10-27 14:38'

View File

@ -0,0 +1,720 @@
/*
* THIS FILE IS AUTOMATICALLY GENERATED
*
* Generator: sensirion-driver-generator 0.33.0
* Product: sht3x
* Model-Version: 1.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 "SensirionI2cSht3x.h"
#include <Arduino.h>
static uint8_t communication_buffer[6] = {0};
SensirionI2cSht3x::SensirionI2cSht3x() {
}
float SensirionI2cSht3x::signalTemperature(uint16_t temperatureTicks) {
float temperature = 0.0;
temperature = (float)(temperatureTicks);
temperature = -45 + ((temperature * 175.0) / 65535.0);
return temperature;
}
float SensirionI2cSht3x::signalHumidity(uint16_t humidityTicks) {
float humidity = 0.0;
humidity = (float)(humidityTicks);
humidity = (100 * humidity) / 65535.0;
return humidity;
}
int16_t
SensirionI2cSht3x::measureSingleShot(Repeatability measurementRepeatability,
bool isClockStretching,
float& aTemperature, float& aHumidity) {
uint16_t rawTemp = 0;
uint16_t rawHumi = 0;
int16_t localError = 0;
if (isClockStretching) {
if (measurementRepeatability == REPEATABILITY_HIGH) {
localError = measureSingleShotHighRepeatabilityClockStretching(
rawTemp, rawHumi);
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_MEDIUM) {
localError = measureSingleShotMediumRepeatabilityClockStretching(
rawTemp, rawHumi);
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_LOW) {
localError = measureSingleShotLowRepeatabilityClockStretching(
rawTemp, rawHumi);
if (localError != NO_ERROR) {
return localError;
}
}
} else if (measurementRepeatability == REPEATABILITY_HIGH) {
localError = measureSingleShotHighRepeatability(rawTemp, rawHumi);
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_MEDIUM) {
localError = measureSingleShotMediumRepeatability(rawTemp, rawHumi);
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_LOW) {
localError = measureSingleShotLowRepeatability(rawTemp, rawHumi);
if (localError != NO_ERROR) {
return localError;
}
}
aTemperature = signalTemperature(rawTemp);
aHumidity = signalHumidity(rawHumi);
return localError;
}
int16_t SensirionI2cSht3x::startPeriodicMeasurement(
Repeatability measurementRepeatability, Mps messagesPerSecond) {
int16_t localError = 0;
if (messagesPerSecond == MPS_EVERY_TWO_SECONDS) {
if (measurementRepeatability == REPEATABILITY_HIGH) {
localError = startMeasurement05MpsHighRepeatability();
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_MEDIUM) {
localError = startMeasurement05MpsMediumRepeatability();
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_LOW) {
localError = startMeasurement05MpsLowRepeatability();
if (localError != NO_ERROR) {
return localError;
}
}
} else if (messagesPerSecond == MPS_ONE_PER_SECOND) {
if (measurementRepeatability == REPEATABILITY_HIGH) {
localError = startMeasurement1MpsHighRepeatability();
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_MEDIUM) {
localError = startMeasurement1MpsMediumRepeatability();
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_LOW) {
localError = startMeasurement1MpsLowRepeatability();
if (localError != NO_ERROR) {
return localError;
}
}
} else if (messagesPerSecond == MPS_TWO_PER_SECOND) {
if (measurementRepeatability == REPEATABILITY_HIGH) {
localError = startMeasurement2MpsHighRepeatability();
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_MEDIUM) {
localError = startMeasurement2MpsMediumRepeatability();
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_LOW) {
localError = startMeasurement2MpsLowRepeatability();
if (localError != NO_ERROR) {
return localError;
}
}
} else if (messagesPerSecond == MPS_FOUR_PER_SECOND) {
if (measurementRepeatability == REPEATABILITY_HIGH) {
localError = startMeasurement4MpsHighRepeatability();
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_MEDIUM) {
localError = startMeasurement4MpsMediumRepeatability();
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_LOW) {
localError = startMeasurement4MpsLowRepeatability();
if (localError != NO_ERROR) {
return localError;
}
}
} else if (messagesPerSecond == MPS_TEN_PER_SECOND) {
if (measurementRepeatability == REPEATABILITY_HIGH) {
localError = startMeasurement10MpsHighRepeatability();
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_MEDIUM) {
localError = startMeasurement10MpsMediumRepeatability();
if (localError != NO_ERROR) {
return localError;
}
} else if (measurementRepeatability == REPEATABILITY_LOW) {
localError = startMeasurement10MpsLowRepeatability();
if (localError != NO_ERROR) {
return localError;
}
}
}
return localError;
}
int16_t SensirionI2cSht3x::blockingReadMeasurement(float& aTemperature,
float& aHumidity) {
uint16_t status = 0u;
uint16_t dataReadyFlag = 0u;
uint16_t rawTemp = 0;
uint16_t rawHumi = 0;
int16_t localError = 0;
localError = llreadStatusRegister(status);
if (localError != NO_ERROR) {
return localError;
}
dataReadyFlag = (status >> 6) & 15;
while (dataReadyFlag == 0) {
delay(100);
localError = llreadStatusRegister(status);
if (localError != NO_ERROR) {
return localError;
}
dataReadyFlag = (status >> 6) & 15;
}
localError = readMeasurement(rawTemp, rawHumi);
if (localError != NO_ERROR) {
return localError;
}
aTemperature = signalTemperature(rawTemp);
aHumidity = signalHumidity(rawHumi);
return localError;
}
int16_t SensirionI2cSht3x::readStatusRegister(uint16_t& aStatusRegister) {
uint16_t status = 0u;
int16_t localError = 0;
localError = llreadStatusRegister(status);
if (localError != NO_ERROR) {
return localError;
}
aStatusRegister = static_cast<uint16_t>(status);
return localError;
}
int16_t SensirionI2cSht3x::measureSingleShotHighRepeatability(
uint16_t& temperatureTicks, uint16_t& humidityTicks) {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2400, buffer_ptr, 6);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(16);
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 SensirionI2cSht3x::measureSingleShotHighRepeatabilityClockStretching(
uint16_t& temperatureTicks, uint16_t& humidityTicks) {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2c06, buffer_ptr, 6);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(16);
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 SensirionI2cSht3x::measureSingleShotMediumRepeatability(
uint16_t& temperatureTicks, uint16_t& humidityTicks) {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x240b, buffer_ptr, 6);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(7);
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 SensirionI2cSht3x::measureSingleShotMediumRepeatabilityClockStretching(
uint16_t& temperatureTicks, uint16_t& humidityTicks) {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2c0d, buffer_ptr, 6);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(7);
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
SensirionI2cSht3x::measureSingleShotLowRepeatability(uint16_t& temperatureTicks,
uint16_t& humidityTicks) {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2416, 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 SensirionI2cSht3x::measureSingleShotLowRepeatabilityClockStretching(
uint16_t& temperatureTicks, uint16_t& humidityTicks) {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2c10, 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 SensirionI2cSht3x::startMeasurement05MpsHighRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2032, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(16);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement05MpsMediumRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2024, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(7);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement05MpsLowRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x202f, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(5);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement1MpsHighRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2130, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(16);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement1MpsMediumRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2126, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(7);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement1MpsLowRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x212d, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(5);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement2MpsHighRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2236, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(16);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement2MpsMediumRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2220, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(7);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement2MpsLowRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x222b, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(5);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement4MpsHighRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2334, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(16);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement4MpsMediumRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2322, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(7);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement4MpsLowRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2329, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(5);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement10MpsHighRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2737, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(16);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement10MpsMediumRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2721, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(7);
return localError;
}
int16_t SensirionI2cSht3x::startMeasurement10MpsLowRepeatability() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x273a, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(5);
return localError;
}
int16_t SensirionI2cSht3x::startArtMeasurement() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2b32, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
return localError;
}
int16_t SensirionI2cSht3x::readMeasurement(uint16_t& temperatureTicks,
uint16_t& humidityTicks) {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0xe000, buffer_ptr, 6);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
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 SensirionI2cSht3x::stopMeasurement() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x3093, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(1);
return localError;
}
int16_t SensirionI2cSht3x::enableHeater() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x306d, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(10);
return localError;
}
int16_t SensirionI2cSht3x::disableHeater() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x3066, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(10);
return localError;
}
int16_t SensirionI2cSht3x::llreadStatusRegister(uint16_t& statusRegister) {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0xf32d, buffer_ptr, 3);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(10);
SensirionI2CRxFrame rxFrame(buffer_ptr, 3);
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3,
rxFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
localError |= rxFrame.getUInt16(statusRegister);
return localError;
}
int16_t SensirionI2cSht3x::clearStatusRegister() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x3041, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(10);
return localError;
}
int16_t SensirionI2cSht3x::softReset() {
int16_t localError = NO_ERROR;
uint8_t* buffer_ptr = communication_buffer;
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x30a2, buffer_ptr, 2);
localError =
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
if (localError != NO_ERROR) {
return localError;
}
delay(2);
return localError;
}
void SensirionI2cSht3x::begin(TwoWire& i2cBus, uint8_t i2cAddress) {
_i2cBus = &i2cBus;
_i2cAddress = i2cAddress;
}

View File

@ -0,0 +1,548 @@
/*
* THIS FILE IS AUTOMATICALLY GENERATED
*
* Generator: sensirion-driver-generator 0.33.0
* Product: sht3x
* Model-Version: 1.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 SENSIRIONI2CSHT3X_H
#define SENSIRIONI2CSHT3X_H
#include "../../SensirionCore/src/SensirionCore.h"
#include <Wire.h>
#define NO_ERROR 0
#define SHT30A_I2C_ADDR_44 0x44
#define SHT30A_I2C_ADDR_45 0x45
#define SHT30_I2C_ADDR_44 0x44
#define SHT30_I2C_ADDR_45 0x45
#define SHT31A_I2C_ADDR_44 0x44
#define SHT31A_I2C_ADDR_45 0x45
#define SHT31_I2C_ADDR_44 0x44
#define SHT31_I2C_ADDR_45 0x45
#define SHT33_I2C_ADDR_44 0x44
#define SHT33_I2C_ADDR_45 0x45
#define SHT35A_I2C_ADDR_44 0x44
#define SHT35A_I2C_ADDR_45 0x45
#define SHT35_I2C_ADDR_44 0x44
#define SHT35_I2C_ADDR_45 0x45
typedef enum {
MEASURE_SINGLE_SHOT_HIGH_REPEATABILITY_CMD_ID = 0x2400,
MEASURE_SINGLE_SHOT_HIGH_REPEATABILITY_CLOCK_STRETCHING_CMD_ID = 0x2c06,
MEASURE_SINGLE_SHOT_MEDIUM_REPEATABILITY_CMD_ID = 0x240b,
MEASURE_SINGLE_SHOT_MEDIUM_REPEATABILITY_CLOCK_STRETCHING_CMD_ID = 0x2c0d,
MEASURE_SINGLE_SHOT_LOW_REPEATABILITY_CMD_ID = 0x2416,
MEASURE_SINGLE_SHOT_LOW_REPEATABILITY_CLOCK_STRETCHING_CMD_ID = 0x2c10,
START_MEASUREMENT_0_5_MPS_HIGH_REPEATABILITY_CMD_ID = 0x2032,
START_MEASUREMENT_0_5_MPS_MEDIUM_REPEATABILITY_CMD_ID = 0x2024,
START_MEASUREMENT_0_5_MPS_LOW_REPEATABILITY_CMD_ID = 0x202f,
START_MEASUREMENT_1_MPS_HIGH_REPEATABILITY_CMD_ID = 0x2130,
START_MEASUREMENT_1_MPS_MEDIUM_REPEATABILITY_CMD_ID = 0x2126,
START_MEASUREMENT_1_MPS_LOW_REPEATABILITY_CMD_ID = 0x212d,
START_MEASUREMENT_2_MPS_HIGH_REPEATABILITY_CMD_ID = 0x2236,
START_MEASUREMENT_2_MPS_MEDIUM_REPEATABILITY_CMD_ID = 0x2220,
START_MEASUREMENT_2_MPS_LOW_REPEATABILITY_CMD_ID = 0x222b,
START_MEASUREMENT_4_MPS_HIGH_REPEATABILITY_CMD_ID = 0x2334,
START_MEASUREMENT_4_MPS_MEDIUM_REPEATABILITY_CMD_ID = 0x2322,
START_MEASUREMENT_4_MPS_LOW_REPEATABILITY_CMD_ID = 0x2329,
START_MEASUREMENT_10_MPS_HIGH_REPEATABILITY_CMD_ID = 0x2737,
START_MEASUREMENT_10_MPS_MEDIUM_REPEATABILITY_CMD_ID = 0x2721,
START_MEASUREMENT_10_MPS_LOW_REPEATABILITY_CMD_ID = 0x273a,
START_ART_MEASUREMENT_CMD_ID = 0x2b32,
READ_MEASUREMENT_CMD_ID = 0xe000,
STOP_MEASUREMENT_CMD_ID = 0x3093,
ENABLE_HEATER_CMD_ID = 0x306d,
DISABLE_HEATER_CMD_ID = 0x3066,
READ_STATUS_REGISTER_CMD_ID = 0xf32d,
CLEAR_STATUS_REGISTER_CMD_ID = 0x3041,
SOFT_RESET_CMD_ID = 0x30a2,
} CmdId;
typedef enum {
REPEATABILITY_LOW = 0,
REPEATABILITY_MEDIUM = 1,
REPEATABILITY_HIGH = 2,
} Repeatability;
typedef enum {
MPS_EVERY_TWO_SECONDS = 0,
MPS_ONE_PER_SECOND = 1,
MPS_TWO_PER_SECOND = 2,
MPS_FOUR_PER_SECOND = 4,
MPS_TEN_PER_SECOND = 10,
} Mps;
class SensirionI2cSht3x {
public:
SensirionI2cSht3x();
/**
* @brief Initializes the SHT3x class.
*
* @param i2cBus Arduino stream object to be used for communication.
*/
void begin(TwoWire& i2cBus, uint8_t i2cAddress);
/**
* @brief Single shot measurement with the specified properties
*
* @param[in] measurementRepeatability The repeatability of the periodic
* measurement
* @param[in] isClockStretching Toggle clock stretching
* @param[out] aTemperature Converted from ticks to degrees celsius by -45 +
* (175 * value / 65535)
* @param[out] aHumidity Converted from ticks to relative humidity by 100 *
* value / 65535
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t measureSingleShot(Repeatability measurementRepeatability,
bool isClockStretching, float& aTemperature,
float& aHumidity);
/**
* @brief startPeriodicMeasurement
*
* Start the periodic measurement measurement mode.
*
* This is a convenience method that selects the correct measurement command
* based on the provided arguments.
*
* @param[in] measurementRepeatability The repeatability of the periodic
* measurement
* @param[in] messagesPerSecond The messages per second of the periodic
* measurement
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startPeriodicMeasurement(Repeatability measurementRepeatability,
Mps messagesPerSecond);
/**
* @brief blockingReadMeasurement
*
* This is a convenience method that combines polling the data ready flag
* and reading out the data. As the minimal measurement interval is 2s and
* we sleep for 100ms we iterate at most 200 times. Note that this is
* blocking the system for a considerable amount of time!
*
* @param[out] aTemperature Converted from ticks to degrees celsius by -45 +
* (175 * value / 65535)
* @param[out] aHumidity Converted from ticks to relative humidity by 100 *
* value / 65535
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t blockingReadMeasurement(float& aTemperature, float& aHumidity);
/**
* @brief Read the contents of the status register
*
* @param[out] aStatusRegister
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t readStatusRegister(uint16_t& aStatusRegister);
/**
* @brief measureSingleShotHighRepeatability
*
* Single shot measurement with high repeatability
*
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
* celsius by -45 + 175 * value / 65535
* @param[out] humidityTicks Humidity ticks. Convert to relative humidity by
* 100
* * value / 65535
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t measureSingleShotHighRepeatability(uint16_t& temperatureTicks,
uint16_t& humidityTicks);
/**
* @brief measureSingleShotHighRepeatabilityClockStretching
*
* Single shot measurement with high repeatability and clock stretching
* enabled
*
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
* celsius by -45 + 175 * value / 65535
* @param[out] humidityTicks Humidity ticks. Convert to relative humidity by
* 100
* * value / 65535
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t measureSingleShotHighRepeatabilityClockStretching(
uint16_t& temperatureTicks, uint16_t& humidityTicks);
/**
* @brief measureSingleShotMediumRepeatability
*
* Single shot measurement with medium repeatability
*
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
* celsius by -45 + 175 * value / 65535
* @param[out] humidityTicks Humidity ticks. Convert to relative humidity by
* 100
* * value / 65535
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t measureSingleShotMediumRepeatability(uint16_t& temperatureTicks,
uint16_t& humidityTicks);
/**
* @brief measureSingleShotMediumRepeatabilityClockStretching
*
* Single shot measurement with medium repeatability and clock stretching
* enabled
*
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
* celsius by -45 + 175 * value / 65535
* @param[out] humidityTicks Humidity ticks. Convert to relative humidity by
* 100
* * value / 65535
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t measureSingleShotMediumRepeatabilityClockStretching(
uint16_t& temperatureTicks, uint16_t& humidityTicks);
/**
* @brief measureSingleShotLowRepeatability
*
* Single shot measurement with low repeatability
*
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
* celsius by -45 + 175 * value / 65535
* @param[out] humidityTicks Humidity ticks. Convert to relative humidity by
* 100
* * value / 65535
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t measureSingleShotLowRepeatability(uint16_t& temperatureTicks,
uint16_t& humidityTicks);
/**
* @brief measureSingleShotLowRepeatabilityClockStretching
*
* Single shot measurement with low repeatability and clock stretching
* enabled
*
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
* celsius by -45 + 175 * value / 65535
* @param[out] humidityTicks Humidity ticks. Convert to relative humidity by
* 100
* * value / 65535
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t
measureSingleShotLowRepeatabilityClockStretching(uint16_t& temperatureTicks,
uint16_t& humidityTicks);
/**
* @brief startMeasurement05MpsHighRepeatability
*
* Start periodic measurement mode with 0.5 mps and high repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement05MpsHighRepeatability();
/**
* @brief startMeasurement05MpsMediumRepeatability
*
* Start periodic measurement mode with 0.5 mps and medium repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement05MpsMediumRepeatability();
/**
* @brief startMeasurement05MpsLowRepeatability
*
* Start periodic measurement mode with 0.5 mps and low repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement05MpsLowRepeatability();
/**
* @brief startMeasurement1MpsHighRepeatability
*
* Start periodic measurement mode with 1 mps and high repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement1MpsHighRepeatability();
/**
* @brief startMeasurement1MpsMediumRepeatability
*
* Start periodic measurement mode with 1 mps and medium repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement1MpsMediumRepeatability();
/**
* @brief startMeasurement1MpsLowRepeatability
*
* Start periodic measurement mode with 1 mps and low repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement1MpsLowRepeatability();
/**
* @brief startMeasurement2MpsHighRepeatability
*
* Start periodic measurement mode with 2 mps and high repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement2MpsHighRepeatability();
/**
* @brief startMeasurement2MpsMediumRepeatability
*
* Start periodic measurement mode with 2 mps and medium repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement2MpsMediumRepeatability();
/**
* @brief startMeasurement2MpsLowRepeatability
*
* Start periodic measurement mode with 2 mps and low repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement2MpsLowRepeatability();
/**
* @brief startMeasurement4MpsHighRepeatability
*
* Start periodic measurement mode with 4 mps and high repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement4MpsHighRepeatability();
/**
* @brief startMeasurement4MpsMediumRepeatability
*
* Start periodic measurement mode with 4 mps and medium repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement4MpsMediumRepeatability();
/**
* @brief startMeasurement4MpsLowRepeatability
*
* Start periodic measurement mode with 4 mps and low repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement4MpsLowRepeatability();
/**
* @brief startMeasurement10MpsHighRepeatability
*
* Start periodic measurement mode with 10 mps and high repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement10MpsHighRepeatability();
/**
* @brief startMeasurement10MpsMediumRepeatability
*
* Start periodic measurement mode with 10 mps and medium repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement10MpsMediumRepeatability();
/**
* @brief startMeasurement10MpsLowRepeatability
*
* Start periodic measurement mode with 10 mps and low repeatability.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startMeasurement10MpsLowRepeatability();
/**
* @brief startArtMeasurement
*
* Start ART (accelerated response time) measurement
*
* @note After issuing the ART command the sensor will start acquiring data
* with a frequency of 4Hz.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t startArtMeasurement();
/**
* @brief readMeasurement
*
* Read out data after a "start measurement" or "start art measurement"
* command has been issued.
*
* @param[out] temperatureTicks Temperature ticks. Convert to degrees
* celsius by -45 + 175 * value / 65535
* @param[out] humidityTicks Humidity ticks. Convert to relative humidity by
* 100
* * value / 65535
*
* @note After the read out command fetch data has been issued, the data
* memory is cleared, i.e. no measurement data is present.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t readMeasurement(uint16_t& temperatureTicks,
uint16_t& humidityTicks);
/**
* @brief stopMeasurement
*
* Stop the periodic measurement mode.
*
* @note Upon reception of this command the sensor will abort the ongoing
* measurement and enter the single shot mode.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t stopMeasurement();
/**
* @brief enableHeater
*
* Enable the heater
*
* @note The SHT3x is equipped with an internal heater, which is meant for
* plausibility checking only. The temperature increase achieved by the
* heater depends on various parameters and lies in the range of a few
* degrees centigrade.
*
* After a reset the heater is disabled (default condition).
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t enableHeater();
/**
* @brief disableHeater
*
* Disable the heater
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t disableHeater();
/**
* @brief llreadStatusRegister
*
* Read out the status register
*
* @param[out] statusRegister The contents of the status register
*
* @note The status register contains information on the operational status
* of the heater, the alert mode and on the execution status of the last
* command and the last write sequence.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t llreadStatusRegister(uint16_t& statusRegister);
/**
* @brief clearStatusRegister
*
* Clear (set to zero) all flags (Bit 4) in the status register.
*
* @return error_code 0 on success, an error code otherwise.
*/
int16_t clearStatusRegister();
/**
* @brief softReset
*
* Perform a soft reset.
*
* @note A system reset of the SHT3x can be achieved in multiple ways: -
* Soft reset: use this command - I2C general call: all devices on the I2C
* bus are reset by sending the command 0x06 to the I2C address 0x00 - Reset
* pin: send a pulse to the dedicated nReset pin. The nReset pin has to be
* pulled low for a minimum of 1 µs to generate a reset of the sensor. -
* Hard reset: Power down (incl. pulling SDA, SCL and ADDR low)
*
* During the reset procedure the sensor will not process commands.
*
* @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 -45 + (175 * value /
* 65535)
*/
float signalTemperature(uint16_t temperatureTicks);
/**
* @brief signalHumidity
*
* @param[in] humidityTicks
*
* @return Converted from ticks to relative humidity by 100 * value / 65535
*/
float signalHumidity(uint16_t humidityTicks);
};
#endif // SENSIRIONI2CSHT3X_H

View File

@ -3,9 +3,9 @@
#include "esp32-hal-log.h"
#endif
const BoardDef bsps[BOARD_DEF_MAX] = {
/** BOARD_DIY_BASIC_KIT */
[BOARD_DIY_BASIC_KIT] =
const BoardDef bsps[_BOARD_MAX] = {
/** DIY_BASIC */
[DIY_BASIC] =
{
.SenseAirS8 =
{
@ -17,7 +17,7 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
.supported = false,
#endif
},
.PMS5003 =
.Pms5003 =
{
.uart_tx_pin = 14,
.uart_rx_pin = 12,
@ -70,10 +70,10 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
.resetPin = -1,
.supported = false,
},
.name = "BOARD_DIY_BASIC_KIT",
.name = "DIY_BASIC",
},
/** BOARD_DIY_PRO_INDOOR_V4_2 */
[BOARD_DIY_PRO_INDOOR_V4_2] =
/** DIY_PRO_INDOOR_V4_2 */
[DIY_PRO_INDOOR_V4_2] =
{
.SenseAirS8 =
{
@ -85,7 +85,7 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
.supported = false,
#endif
},
.PMS5003 =
.Pms5003 =
{
.uart_tx_pin = 14,
.uart_rx_pin = 12,
@ -144,10 +144,10 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
.resetPin = -1,
.supported = false,
},
.name = "BOARD_DIY_PRO_INDOOR_V4_2",
.name = "DIY_PRO_INDOOR_V4_2",
},
/** BOARD_ONE_INDOOR_MONITOR_V9_0 */
[BOARD_ONE_INDOOR_MONITOR_V9_0] =
/** ONE_INDOOR */
[ONE_INDOOR] =
{
.SenseAirS8 =
{
@ -160,7 +160,7 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
#endif
},
/** Use UART0 don't use define pin number */
.PMS5003 =
.Pms5003 =
{
.uart_tx_pin = -1,
.uart_rx_pin = -1,
@ -232,10 +232,10 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
.supported = true,
#endif
},
.name = "BOARD_ONE_INDOOR_MONITOR_V9_0",
.name = "ONE_INDOOR",
},
/** BOARD_OUTDOOR_MONITOR_V1_3 */
[BOARD_OUTDOOR_MONITOR_V1_3] = {
/** OPEN_AIR_OUTDOOR */
[OPEN_AIR_OUTDOOR] = {
.SenseAirS8 =
{
.uart_tx_pin = 1,
@ -247,7 +247,7 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
#endif
},
/** Use UART0 don't use define pin number */
.PMS5003 =
.Pms5003 =
{
.uart_tx_pin = -1,
.uart_rx_pin = -1,
@ -319,7 +319,7 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
.supported = true,
#endif
},
.name = "BOARD_OUTDOOR_MONITOR_V1_3",
.name = "OPEN_AIR_OUTDOOR",
}};
/**
@ -329,7 +329,7 @@ const BoardDef bsps[BOARD_DEF_MAX] = {
* @return const BoardDef*
*/
const BoardDef *getBoardDef(BoardType def) {
if (def >= BOARD_DEF_MAX) {
if (def >= _BOARD_MAX) {
return NULL;
}
return &bsps[def];
@ -356,7 +356,7 @@ void printBoardDef(Stream *_debug) {
}
#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("\tSensor CO2 S8:");
bspPrintf("\t\tSupported: %d", bsps[i].SenseAirS8.supported);
@ -366,10 +366,10 @@ void printBoardDef(Stream *_debug) {
}
bspPrintf("\tSensor PMS5003:");
bspPrintf("\t\tSupported: %d", bsps[i].PMS5003.supported);
if (bsps[i].PMS5003.supported) {
bspPrintf("\t\tUART Tx: %d", bsps[i].PMS5003.uart_tx_pin);
bspPrintf("\t\tUART Rx: %d", bsps[i].PMS5003.uart_rx_pin);
bspPrintf("\t\tSupported: %d", bsps[i].Pms5003.supported);
if (bsps[i].Pms5003.supported) {
bspPrintf("\t\tUART Tx: %d", bsps[i].Pms5003.uart_tx_pin);
bspPrintf("\t\tUART Rx: %d", bsps[i].Pms5003.uart_rx_pin);
}
bspPrintf("\tI2C");

View File

@ -13,14 +13,21 @@
#define AgLog(c, ...) log_i(c, ##__VA_ARGS__)
#endif
/**
* @brief Define Airgradient supported board type
*/
enum BoardType {
BOARD_DIY_BASIC_KIT = 0x00,
BOARD_DIY_PRO_INDOOR_V4_2 = 0x01,
BOARD_ONE_INDOOR_MONITOR_V9_0 = 0x02,
BOARD_OUTDOOR_MONITOR_V1_3 = 0x03,
BOARD_DEF_MAX
DIY_BASIC = 0x00,
DIY_PRO_INDOOR_V4_2 = 0x01,
ONE_INDOOR = 0x02,
OPEN_AIR_OUTDOOR = 0x03,
_BOARD_MAX
};
/**
* @brief Board definitions
*
*/
struct BoardDef {
/** Board Support CO2 SenseS8 */
struct {
@ -34,7 +41,7 @@ struct BoardDef {
const int uart_tx_pin; /** UART tx pin */
const int uart_rx_pin; /** UART rx pin */
const bool supported; /** Is BSP supported for this sensor */
} PMS5003;
} Pms5003;
/** I2C Bus */
struct {

View File

@ -5,6 +5,10 @@
#include "BoardDef.h"
/**
* @brief The class define how to control external watchdog on ONE-V9 and
* Outdoor
*/
class HardwareWatchdog {
public:
HardwareWatchdog(BoardType type);

View File

@ -18,7 +18,7 @@ LedBar::LedBar(BoardType type) : _boardType(type) {}
*
*/
void LedBar::begin(void) {
if (this->_isInit) {
if (this->_isBegin) {
return;
}
@ -36,9 +36,9 @@ void LedBar::begin(void) {
pixel()->begin();
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 green Color Green (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) {
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)
*/
void LedBar::setBrighness(uint8_t brightness) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
pixel()->setBrightness(brightness);
@ -75,16 +75,16 @@ void LedBar::setBrighness(uint8_t brightness) {
*
* @return int Number of LED
*/
int LedBar::getNumberOfLed(void) {
if (this->checkInit() == false) {
int LedBar::getNumberOfLeds(void) {
if (this->isBegin() == false) {
return 0;
}
return this->_bsp->LED.rgbNum;
}
bool LedBar::checkInit(void) {
if (this->_isInit) {
bool LedBar::isBegin(void) {
if (this->_isBegin) {
return true;
}
AgLog("LED is not initialized");
@ -92,7 +92,7 @@ bool LedBar::checkInit(void) {
}
bool LedBar::ledNumInvalid(int ledNum) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return true;
}

View File

@ -5,6 +5,10 @@
#include "BoardDef.h"
/**
* @brief The class define how to handle the RGB LED bar
*
*/
class LedBar {
public:
#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);
void setBrighness(uint8_t brightness);
int getNumberOfLed(void);
int getNumberOfLeds(void);
void show(void);
void clear(void);
private:
const BoardDef *_bsp;
bool _isInit = false;
bool _isBegin = false;
uint8_t _ledState = 0;
BoardType _boardType;
void *pixels = nullptr;
@ -31,7 +35,7 @@ private:
const char *TAG = "LED";
#else
#endif
bool checkInit(void);
bool isBegin(void);
bool ledNumInvalid(int ledNum);
};

View File

@ -15,7 +15,8 @@ void PushButton::begin(Stream &debugStream) {
*
*/
void PushButton::begin(void) {
if (this->_isInit) {
if (this->_isBegin) {
AgLog("Initialized, call end() then try again");
return;
}
@ -25,14 +26,14 @@ void PushButton::begin(void) {
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);
} else {
pinMode(this->_bsp->SW.pin, INPUT);
}
this->_isInit = true;
AgLog("Init");
this->_isBegin = true;
AgLog("Initialize");
}
/**
@ -41,7 +42,7 @@ void PushButton::begin(void) {
* @return PushButton::State
*/
PushButton::State PushButton::getState(void) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return State::BUTTON_RELEASED;
}
@ -64,8 +65,8 @@ String PushButton::toString(PushButton::State state) {
return "Released";
}
bool PushButton::checkInit(void) {
if (this->_isInit) {
bool PushButton::isBegin(void) {
if (this->_isBegin) {
return true;
}
AgLog("Switch not initialized");

View File

@ -4,6 +4,10 @@
#include "BoardDef.h"
#include <Arduino.h>
/**
* @brief The class define how to handle the Push button
*
*/
class PushButton {
public:
/**
@ -26,7 +30,7 @@ private:
/** Board type */
BoardType _boardType;
/** Is inititalize flag */
bool _isInit = false;
bool _isBegin = false;
/** Special variable for ESP8266 */
#if defined(ESP8266)
@ -37,7 +41,7 @@ private:
/** Method */
bool checkInit(void);
bool isBegin(void);
};
#endif /** _AIR_GRADIENT_SW_H_ */

View File

@ -15,6 +15,10 @@ void StatusLed::begin(Stream &debugStream) {
*
*/
void StatusLed::begin(void) {
if (this->_isBegin) {
AgLog("Initialized, call end() then try again");
return;
}
bsp = getBoardDef(this->boardType);
if ((bsp == nullptr) || (bsp->LED.supported == false)) {
AgLog("Board not support StatusLed");
@ -25,9 +29,9 @@ void StatusLed::begin(void) {
digitalWrite(bsp->LED.pin, !bsp->LED.onState);
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) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
digitalWrite(bsp->LED.pin, bsp->LED.onState);
@ -48,7 +52,7 @@ void StatusLed::setOn(void) {
*
*/
void StatusLed::setOff(void) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return;
}
digitalWrite(bsp->LED.pin, !bsp->LED.onState);
@ -88,11 +92,24 @@ String StatusLed::toString(StatusLed::State state) {
return "Off";
}
bool StatusLed::checkInit(void) {
if (this->isInit == false) {
AgLog("No-Initialized");
bool StatusLed::isBegin(void) {
if (this->_isBegin == false) {
AgLog("Not-Initialized");
return false;
}
return true;
}
void StatusLed::end(void) {
if (_isBegin == false) {
return;
}
#if defined(ESP8266)
_debugStream = nullptr;
#endif
setOff();
_isBegin = false;
AgLog("De-initialize");
}

View File

@ -4,6 +4,10 @@
#include "BoardDef.h"
#include <Arduino.h>
/**
* @brief The class define how to handle the LED
*
*/
class StatusLed {
public:
enum State {
@ -17,6 +21,7 @@ public:
#else
#endif
void begin(void);
void end(void);
void setOn(void);
void setOff(void);
void setToggle(void);
@ -26,7 +31,7 @@ public:
private:
const BoardDef *bsp = nullptr;
BoardType boardType;
bool isInit = false;
bool _isBegin = false;
State state;
#if defined(ESP8266)
Stream *_debugStream;
@ -34,7 +39,7 @@ private:
#else
#endif
bool checkInit(void);
bool isBegin(void);
};
#endif /** _STATUS_LED_H_ */

View File

@ -1,7 +1,5 @@
#include "PMS.h"
// PMS::PMS(Stream &stream) { this->_stream = &stream; }
bool PMS::begin(Stream *stream) {
_stream = stream;

View File

@ -3,10 +3,15 @@
#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 {
public:
static const uint16_t SINGLE_RESPONSE_TIME = 1000;
static const uint16_t TOTAL_RESPONSE_TIME = 1000 * 10;
static const uint16_t TOTAL_RESPONSE_TIME = 1000 * 10;
static const uint16_t STEADY_RESPONSE_TIME = 1000 * 30;
// static const uint16_t BAUD_RATE = 9600;
@ -38,7 +43,7 @@ public:
uint16_t AMB_HUM;
};
bool begin(Stream* stream);
bool begin(Stream *stream);
void sleep();
void wakeUp();
void activeMode();

View File

@ -43,32 +43,27 @@ PMS5003::PMS5003(BoardType def) : _boardDef(def) {}
* @return false Failure
*/
bool PMS5003::begin(void) {
if (this->_isInit) {
if (this->_isBegin) {
AgLog("Initialized, call end() then try again");
return true;
}
#if defined(ESP32)
// if (this->_serial != &Serial) {
// AgLog("Hardware serial must be Serial(0)");
// return false;
// }
#endif
this->bsp = getBoardDef(this->_boardDef);
if (bsp == NULL) {
AgLog("Board [%d] not supported", this->_boardDef);
return false;
}
if (bsp->PMS5003.supported == false) {
if (bsp->Pms5003.supported == false) {
AgLog("Board [%d] PMS50035003 not supported", this->_boardDef);
return false;
}
#if defined(ESP8266)
bsp->PMS5003.uart_tx_pin;
SoftwareSerial *uart =
new SoftwareSerial(bsp->PMS5003.uart_tx_pin, bsp->PMS5003.uart_rx_pin);
bsp->Pms5003.uart_tx_pin;
SoftwareSerial *uart = new SoftwareSerial(bsp->Pms5003.uart_tx_pin, bsp->Pms5003.uart_rx_pin);
uart->begin(9600);
if (pms.begin(uart) == false) {
AgLog("PMS failed");
@ -82,7 +77,7 @@ bool PMS5003::begin(void) {
}
#endif
this->_isInit = true;
this->_isBegin = true;
return true;
}
@ -119,7 +114,7 @@ int PMS5003::pm25ToAQI(int pm02) {
* @return false Failure
*/
bool PMS5003::readData(void) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return false;
}
@ -168,10 +163,26 @@ int PMS5003::convertPm25ToUsAqi(int pm25) { return this->pm25ToAQI(pm25); }
* @return true Initialized
* @return false No-initialized
*/
bool PMS5003::checkInit(void) {
if (this->_isInit == false) {
AgLog("No initialized");
bool PMS5003::isBegin(void) {
if (this->_isBegin == false) {
AgLog("Not-initialized");
return false;
}
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");
}

View File

@ -1,10 +1,13 @@
#ifndef _AIR_GRADIENT_PMS5003_H_
#define _AIR_GRADIENT_PMS5003_H_
#include "../bsp/BoardDef.h"
#include "../main/BoardDef.h"
#include "Stream.h"
#include "PMS.h"
/**
* @brief The class define how to handle PMS5003 sensor bas on @ref PMS class
*/
class PMS5003 {
public:
PMS5003(BoardType def);
@ -13,6 +16,7 @@ public:
#else
bool begin(HardwareSerial &serial);
#endif
void end(void);
bool readData(void);
int getPm01Ae(void);
@ -22,7 +26,7 @@ public:
int convertPm25ToUsAqi(int pm25);
private:
bool _isInit = false;
bool _isBegin = false;
BoardType _boardDef;
PMS pms;
const BoardDef *bsp;
@ -36,7 +40,7 @@ private:
PMS::DATA pmsData;
bool begin(void);
bool checkInit(void);
bool isBegin(void);
int pm25ToAQI(int pm02);
};
#endif /** _AIR_GRADIENT_PMS5003_H_ */

View File

@ -43,7 +43,7 @@ PMS5003T::PMS5003T(BoardType def) : _boardDef(def) {}
* @return false Failure
*/
bool PMS5003T::begin(void) {
if (this->_isInit) {
if (this->_isBegin) {
return true;
}
@ -60,15 +60,15 @@ bool PMS5003T::begin(void) {
return false;
}
if (bsp->PMS5003.supported == false) {
if (bsp->Pms5003.supported == false) {
AgLog("Board [%d] PMS5003 not supported", this->_boardDef);
return false;
}
#if defined(ESP8266)
bsp->PMS5003.uart_tx_pin;
bsp->Pms5003.uart_tx_pin;
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);
if (pms.begin(uart) == false) {
AgLog("PMS failed");
@ -82,8 +82,8 @@ bool PMS5003T::begin(void) {
if (this->_serial == &Serial) {
#endif
AgLog("Init Serial");
this->_serial->begin(9600, SERIAL_8N1, bsp->PMS5003.uart_rx_pin,
bsp->PMS5003.uart_tx_pin);
this->_serial->begin(9600, SERIAL_8N1, bsp->Pms5003.uart_rx_pin,
bsp->Pms5003.uart_tx_pin);
} else {
if (bsp->SenseAirS8.supported == false) {
AgLog("Board [%d] PMS5003T_2 not supported", this->_boardDef);
@ -101,7 +101,7 @@ bool PMS5003T::begin(void) {
}
#endif
this->_isInit = true;
this->_isBegin = true;
return true;
}
@ -138,7 +138,7 @@ int PMS5003T::pm25ToAQI(int pm02) {
* @return false Failure
*/
bool PMS5003T::readData(void) {
if (this->checkInit() == false) {
if (this->isBegin() == false) {
return false;
}
@ -207,9 +207,9 @@ float PMS5003T::getRelativeHumidity(void) {
* @return true Initialized
* @return false No-initialized
*/
bool PMS5003T::checkInit(void) {
if (this->_isInit == false) {
AgLog("No initialized");
bool PMS5003T::isBegin(void) {
if (this->_isBegin == false) {
AgLog("Not-initialized");
return false;
}
return true;
@ -221,3 +221,16 @@ float PMS5003T::correctionTemperature(float inTemp) {
}
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");
}

View File

@ -2,10 +2,13 @@
#define _PMS5003T_H_
#include <HardwareSerial.h>
#include "../bsp/BoardDef.h"
#include "../main/BoardDef.h"
#include "PMS.h"
#include "Stream.h"
/**
* @brief The class define how to handle PMS5003T sensor bas on @ref PMS class
*/
class PMS5003T {
public:
PMS5003T(BoardType def);
@ -14,6 +17,7 @@ public:
#else
bool begin(HardwareSerial &serial);
#endif
void end(void);
bool readData(void);
int getPm01Ae(void);
@ -25,7 +29,7 @@ public:
float getRelativeHumidity(void);
private:
bool _isInit = false;
bool _isBegin = false;
bool _isSleep = false;
BoardType _boardDef;
@ -41,7 +45,7 @@ private:
int pm25ToAQI(int pm02);
PMS pms;
PMS::DATA pmsData;
bool checkInit(void);
bool isBegin(void);
float correctionTemperature(float inTemp);
};

View File

@ -6,7 +6,7 @@
#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
*/
@ -18,7 +18,7 @@ S8::S8(BoardType def) : _boardDef(def) {}
* @return true = success, otherwise is failure
*/
bool S8::begin(void) {
if (this->_isInit) {
if (this->_isBegin) {
AgLog("Initialized, Call end() then try again");
return true;
}
@ -27,8 +27,8 @@ bool S8::begin(void) {
}
/**
* @brief Init sensor has print debug log, if class create without serial debug
* before it's override last define
* @brief Init S8 sensor, this methos should be call before other, if not it's
* always return the failure status
*
* @param _debugStream Serial print debug log, NULL if don't use
* @return true = success, otherwise is failure
@ -56,13 +56,12 @@ bool S8::begin(HardwareSerial &serial) {
*
*/
void S8::end(void) {
if (this->_isInit == false) {
AgLog("Senor is not initialized");
if (this->_isBegin == false) {
return;
}
// Deinit
AgLog("De-Inititlized");
AgLog("De-Inititlize");
}
/**
@ -71,7 +70,7 @@ void S8::end(void) {
* @param firmver String buffer, len = 10 char
*/
void S8::getFirmwareVersion(char firmver[]) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return;
}
@ -103,7 +102,7 @@ void S8::getFirmwareVersion(char firmver[]) {
* @return int32_t Return ID
*/
int32_t S8::getSensorTypeId(void) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return -1;
}
@ -149,7 +148,7 @@ int32_t S8::getSensorTypeId(void) {
* @return int32_t ID
*/
int32_t S8::getSensorId(void) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return -1;
}
@ -195,7 +194,7 @@ int32_t S8::getSensorId(void) {
* @return int16_t
*/
int16_t S8::getMemoryMapVersion(void) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return -1;
}
@ -225,7 +224,7 @@ int16_t S8::getMemoryMapVersion(void) {
* @return int16_t (PPM), -1 if invalid.
*/
int16_t S8::getCo2(void) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return -1;
}
@ -266,7 +265,7 @@ bool S8::setBaselineCalibration(void) {
/**
* @brief Wait for background calibration done
*
*
* @return true Done
* @return false On calib
*/
@ -286,7 +285,7 @@ bool S8::isBaseLineCalibrationDone(void) {
* @return int16_t PWM
*/
int16_t S8::getOutputPWM(void) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return -1;
}
@ -318,7 +317,7 @@ int16_t S8::getOutputPWM(void) {
* @return int16_t Hour
*/
int16_t S8::getCalibPeriodABC(void) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return -1;
}
@ -350,7 +349,7 @@ int16_t S8::getCalibPeriodABC(void) {
* @return false Failure
*/
bool S8::setCalibPeriodABC(int16_t period) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return false;
}
@ -391,7 +390,7 @@ bool S8::setCalibPeriodABC(int16_t period) {
* @return false Failure
*/
bool S8::manualCalib(void) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return false;
}
@ -416,7 +415,7 @@ bool S8::manualCalib(void) {
* @return int16_t Flags
*/
int16_t S8::getAcknowledgement(void) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return -1;
}
@ -446,7 +445,7 @@ int16_t S8::getAcknowledgement(void) {
* @return false Failure
*/
bool S8::clearAcknowledgement(void) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return false;
}
@ -480,7 +479,7 @@ bool S8::clearAcknowledgement(void) {
* @return int16_t Alarm status
*/
int16_t S8::getAlarmStatus(void) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return -1;
}
@ -509,7 +508,7 @@ int16_t S8::getAlarmStatus(void) {
* @return S8::Status Sensor status
*/
S8::Status S8::getStatus(void) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return (Status)0;
}
@ -538,7 +537,7 @@ S8::Status S8::getStatus(void) {
* @return int16_t Output status
*/
int16_t S8::getOutputStatus(void) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return -1;
}
@ -569,7 +568,7 @@ int16_t S8::getOutputStatus(void) {
* @return false Failure
*/
bool S8::sendSpecialCommand(CalibrationSpecialComamnd command) {
if (this->isInit() == false) {
if (this->isBegin() == false) {
return false;
}
@ -656,16 +655,16 @@ bool S8::init(int txPin, int rxPin, uint32_t baud) {
/** Check communication by get firmware version */
char fwVers[11];
this->_isInit = true;
this->_isBegin = true;
this->getFirmwareVersion(fwVers);
if (strlen(fwVers) == 0) {
this->_isInit = false;
this->_isBegin = false;
return false;
}
AgLog("Firmware version: %s", fwVers);
AgLog("Sensor successfully initialized. Heating up for 10s");
this->_isInit = true;
this->_isBegin = true;
this->_lastInitTime = millis();
return true;
}
@ -676,11 +675,11 @@ bool S8::init(int txPin, int rxPin, uint32_t baud) {
* @return true Initialized
* @return false No-Initialized
*/
bool S8::isInit(void) {
if (this->_isInit) {
bool S8::isBegin(void) {
if (this->_isBegin) {
return true;
}
AgLog("Sensor no-initialized");
AgLog("Sensor not-initialized");
return false;
}
@ -794,3 +793,23 @@ void S8::sendCommand(uint8_t func, uint16_t reg, uint16_t value) {
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);
}

View File

@ -1,9 +1,12 @@
#ifndef _S8_H_
#define _S8_H_
#include "../bsp/BoardDef.h"
#include "../main/BoardDef.h"
#include "Arduino.h"
/**
* @brief The class define how to handle the senseair S8 sensor (CO2 sensor)
*/
class S8 {
public:
const int S8_BAUDRATE =
@ -75,6 +78,7 @@ public:
int16_t getCo2(void);
bool setBaselineCalibration(void);
bool isBaseLineCalibrationDone(void);
bool setAutoCalib(int hours);
private:
/** Variables */
@ -86,7 +90,7 @@ private:
#if defined(ESP32)
HardwareSerial *_serial;
#endif
bool _isInit = false;
bool _isBegin = false;
uint32_t _lastInitTime;
bool isCalib = false;
@ -95,7 +99,7 @@ private:
bool init(const BoardDef *bsp);
bool init(int txPin, int rxPin);
bool init(int txPin, int rxPin, uint32_t baud);
bool isInit(void);
bool isBegin(void);
void uartWriteBytes(uint8_t size); // Send bytes to sensor
uint8_t

View File

@ -7,6 +7,11 @@
#define vocAlgorithm() ((VOCGasIndexAlgorithm *)(this->_vocAlgorithm))
#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) {}
/**
@ -18,9 +23,13 @@ Sgp41::Sgp41(BoardType type) : _boardType(type) {}
* @return false Failure
*/
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;
}
/** Check that board has supported this sensor */
if (this->boardSupported() == false) {
return false;
}
@ -55,8 +64,8 @@ bool Sgp41::begin(TwoWire &wire) {
conditioningCount = 0;
#endif
this->_isInit = true;
AgLog("Init");
this->_isBegin = true;
AgLog("Initialize");
return true;
}
@ -88,7 +97,11 @@ 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) {
/** NOx conditionning */
uint16_t err;
@ -115,18 +128,26 @@ void Sgp41::_handle(void) {
}
#endif
/**
* @brief De-Initialize sensor
*/
void Sgp41::end(void) {
if (this->_isInit == false) {
if (this->_isBegin == false) {
return;
}
#ifdef ESP32
vTaskDelete(pollTask);
#else
_debugStream = nullptr;
#endif
this->_isInit = false;
bsp = NULL;
this->_isBegin = false;
delete sgpSensor();
delete vocAlgorithm();
delete noxAlgorithm();
AgLog("De-Init");
AgLog("De-initialize");
}
/**
@ -153,6 +174,12 @@ int Sgp41::getNoxIndex(void) {
return nox;
}
/**
* @brief Check that board has supported sensor
*
* @return true Supported
* @return false Not-supported
*/
bool Sgp41::boardSupported(void) {
if (this->bsp == nullptr) {
this->bsp = getBoardDef(this->_boardType);
@ -165,25 +192,19 @@ bool Sgp41::boardSupported(void) {
return true;
}
int Sgp41::sdaPin(void) {
if (this->bsp) {
return this->bsp->I2C.sda_pin;
}
AgLog("sdaPin(): board not supported I2C");
return -1;
}
int Sgp41::sclPin(void) {
if (this->bsp) {
return this->bsp->I2C.scl_pin;
}
AgLog("sdlPin(): board not supported I2C");
return -1;
}
/**
* @brief Get raw signal
*
* @param raw_voc Raw VOC output
* @param row_nox Raw NOx output
* @param defaultRh
* @param defaultT
* @return true Success
* @return false Failure
*/
bool Sgp41::getRawSignal(uint16_t &raw_voc, uint16_t &row_nox,
uint16_t defaultRh, uint16_t defaultT) {
if (this->checkInit() == false) {
if (this->isBegin() == 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.
* Subsequently, the sensor enters the idle mode.
* @brief Check that sensor is initialized
*
* @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 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) {
uint16_t err;
uint16_t srawVoc;

View File

@ -1,10 +1,15 @@
#ifndef _AIR_GRADIENT_SGP4X_H_
#define _AIR_GRADIENT_SGP4X_H_
#include "../bsp/BoardDef.h"
#include "../main/BoardDef.h"
#include <Arduino.h>
#include <Wire.h>
/**
* @brief The class define how to handle Sensirion sensor SGP41 (VOC and NOx
* sensor)
*
*/
class Sgp41 {
public:
Sgp41(BoardType type);
@ -12,7 +17,7 @@ public:
#if defined(ESP8266)
bool begin(TwoWire &wire, Stream &stream);
void handle(void);
#else
#else
void _handle(void);
#endif
void end(void);
@ -22,7 +27,7 @@ public:
private:
bool onConditioning = true;
bool ready = false;
bool _isInit = false;
bool _isBegin = false;
void *_sensor;
void *_vocAlgorithm;
void *_noxAlgorithm;
@ -40,14 +45,10 @@ private:
#else
TaskHandle_t pollTask;
#endif
bool checkInit(void);
bool isBegin(void);
bool boardSupported(void);
int sdaPin(void);
int sclPin(void);
bool getRawSignal(uint16_t &raw_voc, uint16_t &raw_nox,
uint16_t defaultRh = 0x8000, uint16_t defaultT = 0x6666);
bool turnHeaterOff(void);
bool getSerialNumber(uint16_t serialNumbers[], uint8_t serialNumberSize);
bool _noxConditioning(void);
};

163
src/sht/sht3x.cpp Normal file
View File

@ -0,0 +1,163 @@
#include "sht3x.h"
#include "../library/arduino-i2c-sht3x/src/SensirionI2cSht3x.h"
#define sht3x() ((SensirionI2cSht3x *)(this->_sensor))
/**
* @brief Check that sensor has initialized
*
* @return true Initialized
* @return false Not-initialized
*/
bool Sht3x::isBegin(void) {
if (_isBegin) {
return true;
}
AgLog("Sensor not-initialized");
return false;
}
/**
* @brief Check sensor has supported by board
*
* @return true Supported
* @return false Not-supported
*/
bool Sht3x::boardSupported(void) {
if (_bsp == NULL) {
_bsp = getBoardDef(_boarType);
}
if ((_bsp == NULL) || (_bsp->I2C.supported == false)) {
AgLog("Board not supported");
return false;
}
return true;
}
/**
* @brief Get temperature and humidity data
*
* @param temp Tempreature read out
* @param hum Humidity read out
* @return true Success
* @return false Failure
*/
bool Sht3x::measure(float &temp, float &hum) {
if (isBegin() == false) {
return false;
}
if (sht3x()->measureSingleShot(REPEATABILITY_MEDIUM, false, temp, hum) ==
NO_ERROR) {
return true;
}
return false;
}
/**
* @brief Construct a new Sht 3x:: Sht 3x object
*
* @param type
*/
Sht3x::Sht3x(BoardType type) : _boarType(type) {}
/**
* @brief Destroy the Sht 3x:: Sht 3x object
*
*/
Sht3x::~Sht3x() { end(); }
#ifdef ESP8266
/**
* @brief Initialized sensor
*
* @param wire TwoWire instance, must be initialized
* @param debugStream Point to debug Serial to print debug log
* @return true Success
* @return false Failure
*/
bool Sht3x::begin(TwoWire &wire, Stream &debugStream) {
_debugStream = &debugStream;
return begin(wire);
}
#else
#endif
/**
* @brief Init sensor, should init before use sensor, if not call other method
* always return invalid
*
* @param wire TwoWire instance, must be initialized
* @return true Success
* @return false Failure
*/
bool Sht3x::begin(TwoWire &wire) {
if (_isBegin) {
AgLog("Initialized, call end() then try again");
return true;
}
/** Check sensor has supported on board */
if (boardSupported() == false) {
return false;
}
/** Create sensor and init */
_sensor = new SensirionI2cSht3x();
sht3x()->begin(wire, SHT30_I2C_ADDR_44);
if (sht3x()->softReset() != NO_ERROR) {
AgLog("Reset sensor fail, look like sensor is not on I2C bus");
return false;
}
_isBegin = true;
AgLog("Initialize");
return true;
}
/**
* @brief De-initialize sensor
*
*/
void Sht3x::end(void) {
if (_isBegin == false) {
return;
}
_isBegin = false;
_bsp = NULL;
delete sht3x();
#ifdef ESP8266
_debugStream = nullptr;
#endif
AgLog("De-initialize");
}
/**
* @brief Get temperature degree celsius
*
* @return float value <= 256.0f is invalid, that mean sensor has issue or
* communication to sensor not worked as well
*/
float Sht3x::getTemperature(void) {
float temp;
float hum;
if (measure(temp, hum)) {
return temp;
}
return -256.0f;
}
/**
* @brief Get humidity
*
* @return float Percent(0 - 100), value < 0 is invalid.
*/
float Sht3x::getRelativeHumidity(void) {
float temp;
float hum;
if (measure(temp, hum)) {
return hum;
}
return -1.0f;
}

42
src/sht/sht3x.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef _AIR_GRADIENT_SHT3X_H_
#define _AIR_GRADIENT_SHT3X_H_
#include "../main/BoardDef.h"
#include <Arduino.h>
#include <Wire.h>
/**
* @brief The class with define how to handle the Sensirion sensor SHT3x
* (temperature and humidity sensor).
*/
class Sht3x {
private:
BoardType _boarType;
bool _isBegin = false;
const BoardDef *_bsp = NULL;
void *_sensor;
#ifdef ESP8266
Stream *_debugStream = nullptr;
const char *TAG = "SHT3x";
#else
#endif
bool isBegin(void);
bool boardSupported(void);
bool measure(float &temp, float &hum);
public:
Sht3x(BoardType type);
~Sht3x();
#ifdef ESP8266
bool begin(TwoWire &wire, Stream &debugStream);
#else
#endif
bool begin(TwoWire &wire);
void end(void);
float getTemperature(void);
float getRelativeHumidity(void);
};
#endif /** _AIR_GRADIENT_SHT3X_H_ */

View File

@ -1,18 +1,32 @@
#include "sht4x.h"
#include "../library/SensirionSHT4x/src/SensirionI2CSht4x.h"
/** Cast _sensor to SensirionI2CSht4x */
#define shtSensor() ((SensirionI2CSht4x *)(this->_sensor))
#if defined(ESP8266)
bool Sht::begin(TwoWire &wire, Stream &debugStream) {
/**
* @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 Sht4x::begin(TwoWire &wire, Stream &debugStream) {
this->_debugStream = &debugStream;
return this->begin(wire);
}
#else
#endif
Sht::Sht(BoardType type) : _boardType(type) {}
/**
* @brief Construct a new Sht4x:: Sht4x object
*
* @param type Board type @ref BoardType
*/
Sht4x::Sht4x(BoardType type) : _boardType(type) {}
/**
* @brief Init sensor, Ifthis funciton not call the other funtion call will
@ -22,14 +36,19 @@ Sht::Sht(BoardType type) : _boardType(type) {}
* @return true Success
* @return false Failure
*/
bool Sht::begin(TwoWire &wire) {
if (this->_isInit) {
bool Sht4x::begin(TwoWire &wire) {
/** Ignore next step if sensor has intiialized */
if (this->_isBegin) {
AgLog("Initialized, call end() then try again");
return true;
}
/** Check sensor has supported on board */
if (this->boardSupported() == false) {
return false;
}
/** Create new SensirionI2CSht4x and init */
this->_sensor = new SensirionI2CSht4x();
shtSensor()->begin(wire, SHT40_I2C_ADDR_44);
if (shtSensor()->softReset() != 0) {
@ -39,18 +58,27 @@ bool Sht::begin(TwoWire &wire) {
delay(10);
this->_isInit = true;
AgLog("Init");
this->_isBegin = true;
AgLog("Initialize");
return true;
}
void Sht::end(void) {
if (this->_isInit == false) {
/**
* @brief De-initialize SHT41 sensor
*
*/
void Sht4x::end(void) {
if (this->_isBegin == false) {
return;
}
this->_isInit = false;
this->_isBegin = false;
_bsp = NULL;
delete shtSensor();
#if defined(ESP8266)
_debugStream = nullptr;
#endif
AgLog("De-initialize");
}
/**
@ -59,7 +87,7 @@ void Sht::end(void) {
* @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 Sht4x::getTemperature(void) {
float temperature;
float humidity;
if (this->measureMediumPrecision(temperature, humidity)) {
@ -74,7 +102,7 @@ float Sht::getTemperature(void) {
*
* @return float Percent(0 - 100), value < 0 is invalid.
*/
float Sht::getRelativeHumidity(void) {
float Sht4x::getRelativeHumidity(void) {
float temperature;
float humidity;
if (this->measureMediumPrecision(temperature, humidity)) {
@ -84,7 +112,13 @@ float Sht::getRelativeHumidity(void) {
return -1.0f;
}
bool Sht::boardSupported(void) {
/**
* @brief Check sensor has supported by board
*
* @return true Supported
* @return false Not supported
*/
bool Sht4x::boardSupported(void) {
if (this->_bsp == NULL) {
this->_bsp = getBoardDef(this->_boardType);
}
@ -96,43 +130,30 @@ bool Sht::boardSupported(void) {
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) {
/**
* @brief Check that sensor has initialized
*
* @return true Initialized
* @return false Not-initialized
*/
bool Sht4x::isBegin(void) {
if (this->_isBegin) {
return true;
}
AgLog("Sensor no-initialized");
AgLog("Sensor not-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) {
/**
* @brief Ge SHT41 temperature and humidity value with medium meaure precision
*
* @param temperature Read out temperarure
* @param humidity Read humidity
* @return true Success
* @return false Failure
*/
bool Sht4x::measureMediumPrecision(float &temperature, float &humidity) {
if (this->isBegin() == false) {
return false;
}
@ -142,69 +163,3 @@ bool Sht::measureMediumPrecision(float &temperature, float &humidity) {
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;
}

View File

@ -4,24 +4,27 @@
#include <Arduino.h>
#include <Wire.h>
#include "../bsp/BoardDef.h"
#include "../main/BoardDef.h"
class Sht {
/**
* @brief The class with define how to handle the Sensirion sensor SHT41
* (temperature and humidity sensor).
*/
class Sht4x {
public:
#if defined(ESP8266)
bool begin(TwoWire &wire, Stream &debugStream);
#else
#endif
Sht(BoardType type);
Sht4x(BoardType type);
bool begin(TwoWire &wire);
void end(void);
float getTemperature(void);
float getRelativeHumidity(void);
private:
BoardType _boardType;
bool _isInit = false;
bool _isBegin = false; /** Flag indicate that sensor initialized or not */
void *_sensor;
const BoardDef *_bsp = NULL;
#if defined(ESP8266)
@ -29,21 +32,9 @@ private:
const char *TAG = "SHT4x";
#else
#endif
bool checkInit(void);
bool isBegin(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_ */