Compare commits

...

59 Commits
3.0.0 ... 3.0.4

Author SHA1 Message Date
1a52c2d9f8 Merge pull request #54 from airgradienthq/feature/bugfix-and-example-update
Feature/bugfix and example update
2024-02-21 20:19:54 +07:00
6afcf6d4c3 Udpate version: 3.0.4 2024-02-20 21:06:09 +07:00
af139331b1 Show message when sensor module not found on display 2024-02-20 21:05:13 +07:00
e79a798b88 Update show invalid value into display 2024-02-20 21:05:04 +07:00
14fb790e2a PMS5003 add failed count to 3 before show invalid value to display 2024-02-20 20:36:06 +07:00
2aab02940d add local webserver mDNS airgradient_<devId>.local 2024-02-18 15:20:31 +07:00
da07067661 Save configuration on device persistently 2024-02-18 15:01:30 +07:00
b2091114b3 add serialno to local server data GET response 2024-02-18 12:50:46 +07:00
e09128572c fix: FW stops if some sensor not found 2024-02-18 12:43:37 +07:00
e16966d092 Add webserver to get measure data on example Open_Air 2024-02-18 11:06:06 +07:00
26a8b065bc fix: wifi not connect if LED test with button request 2024-02-18 10:59:01 +07:00
589b98d97e Better server configure for abcDays debug message 2024-02-18 10:49:06 +07:00
cb4d9372f8 Add device webserver to get measure data at <IPAddress>/measures/current 2024-02-18 10:35:20 +07:00
6cb7fa8a1b Fix: capitalize folder and file 2024-02-17 17:34:01 +07:00
6cdbb8a0a3 Fix capitalize folder and file name ignored 2024-02-17 17:28:51 +07:00
781fb51c6f Add mqtt client 2024-02-17 17:19:29 +07:00
17646f3067 Update typo, #49 2024-02-17 14:05:17 +07:00
7a4b665bb5 [Update] optimize display not show on power up or after flash firmware 2024-02-17 13:56:07 +07:00
571b36d05f Optimize Serial nr show and display do not show content after power up or flash firmware 2024-02-17 13:50:22 +07:00
23513cf88c Add parameter tvoc_raw for SGP41 2024-02-17 13:36:32 +07:00
b475c5c1ec capitalize folder names and file names Same like class file names 2024-02-17 13:17:45 +07:00
ee9f26ee04 Update multiple typos, #50 2024-02-17 13:02:24 +07:00
8c94cea764 round real value with 2 decimal on server sync data json 2024-02-17 12:47:51 +07:00
7c63af5ba9 Add Serial Nr into log 2024-02-17 12:11:44 +07:00
7c1eae83e4 Add logging for abcDays 2024-02-17 12:04:11 +07:00
e48ff0e41c Fix model PST send data to cloud with channel 2024-02-17 10:45:56 +07:00
c9e3a2a9b4 Rename example Open_Air_O to Open_Air 2024-02-17 10:38:10 +07:00
5667279cf1 Merge pull request #53 from airgradienthq/feature/update-sht-for-all-examples
Feature/update sht for all examples
2024-02-16 21:57:52 +07:00
2941bb2d5d next version 3.0.3 2024-02-16 21:56:43 +07:00
a0044ad0ac Update example to use sht support for sht3x and sht4x 2024-02-16 13:39:33 +07:00
fc5c0a1d6e Merge branch 'hotfix/sht30' into feature/update-sht-for-all-examples 2024-02-16 13:29:03 +07:00
b28719b7a5 Improved LED test on startup. Set for version 3.0.2 2024-02-16 12:11:17 +07:00
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
7a6cc8caef use "arduino-sht" library for sht3x and sht4x 2024-02-10 21:14:27 +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
05594441b8 Update sht3x 2024-02-07 09:18:02 +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
276 changed files with 5290 additions and 4205 deletions

2
.gitignore vendored Normal file
View File

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

View File

@ -34,7 +34,7 @@ If you have any questions or problems, check out [our forum](https://forum.airgr
- [Sensirion Gas Index Algorithm](https://github.com/Sensirion/arduino-gas-index-algorithm)
- [Sensirion 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 SHT](https://github.com/Sensirion/arduino-sht)
- [PMS](https://github.com/fu-hsi/pms)
## License

BIN
examples/.DS_Store vendored

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

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

@ -1,369 +0,0 @@
#include <AirGradient.h>
#include <HardwareSerial.h>
#include <Wire.h>
/**
* AirGradient use ESP32C3 has default Serial0 use for PMS5003, to print log
* should use esp-hal-log instead.
*/
#include <esp32-hal-log.h>
/**
* @brief Define test board
*/
#define TEST_BOARD_OUTDOOR_MONITOR_V1_3 0
#define TEST_BOARD_ONE_INDOOR_MONITOR_V9_0 1
/**
* @brief Define test sensor
*/
#define TEST_SENSOR_SenseAirS8 0
#define TEST_SENSOR_SHT4x 0
#define TEST_SENSOR_SGP4x 0
#define TEST_SWITCH 0
#define TEST_OLED 0
#if TEST_BOARD_OUTDOOR_MONITOR_V1_3
#define TEST_STATUS_LED 0
#define TEST_PMS5003T 1
#endif
#define TEST_WATCHDOG 1
#if TEST_BOARD_ONE_INDOOR_MONITOR_V9_0
#define TEST_LED_BAR 1
#define TEST_SENSOR_PMS5003 0
#endif
#if TEST_BOARD_OUTDOOR_MONITOR_V1_3
AirGradient ag(BOARD_OUTDOOR_MONITOR_V1_3);
#elif TEST_BOARD_ONE_INDOOR_MONITOR_V9_0
AirGradient ag(BOARD_ONE_INDOOR_MONITOR_V9_0);
#else
#error "Must enable board test
#endif
void setup() {
/** Print All AirGradient board define */
printBoardDef(NULL);
#if TEST_SENSOR_SenseAirS8
/** Cause Serial is use default for PMS, CO2S8 should be use Serial 1
* Serial 1 will be init by SenseAirS8 don't need to init any more on user
* code
*/
if (ag.s8.begin(Serial1)) {
log_i("CO2S8 sensor init success");
} else {
log_i("CO2S8 sensor init failure");
}
log_i("Start baseline calib");
if (ag.s8.setBaselineCalibration()) {
log_i("Calib success");
} else {
log_e("Calib failure");
}
delay(5000); // Wait for calib done
#endif
#if TEST_SENSOR_PMS5003
if (ag.pms5003.begin(Serial0)) {
log_i("PMS5003 sensor init success");
} else {
log_i("PMS5003 sensor init failure");
}
#endif
#if TEST_PMS5003T
/**
* @brief PMS5003T_1 alway connect to Serial (esp32c3 RXD0, RXD0)
*/
if (ag.pms5003t_1.begin(Serial)) {
log_i("PMS5003T_1 sensor init success");
} else {
log_i("PMS5003T_1 sensor init failure");
}
// TODO Only test without senseair s8 because it's share the UART bus
#if TEST_SENSOR_SenseAirS8 == 0
if (ag.pms5003t_2.begin(Serial1)) {
log_i("PMS5003T_2 sensor init success");
} else {
log_i("PMS5003T_2 sensor init failure");
}
#endif
#endif
#if TEST_SENSOR_SHT4x || TEST_SENSOR_SGP4x || TEST_OLED
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
#endif
#if TEST_SENSOR_SHT4x
if (ag.sht.begin(Wire)) {
log_i("SHT init success");
} else {
log_i("SHT init failed");
}
#endif
#if TEST_SENSOR_SGP4x
if (ag.sgp41.begin(Wire)) {
log_i("SGP init success");
} else {
log_e("SGP init failure");
}
#endif
#if TEST_LED
led.begin();
#endif
#if TEST_SWITCH
ag.button.begin();
#endif
#if TEST_OLED
ag.display.begin(Wire);
ag.display.setTextSize(1);
ag.display.setCursor(0, 0);
ag.display.setTextColor(1);
ag.display.setText("180s to connect to wifi hostpost AC-xxxxx");
ag.display.show();
#endif
#if TEST_STATUS_LED
ag.statusLed.begin();
#endif
#if TEST_LED_BAR
ag.ledBar.begin();
#endif
#if TEST_WATCHDOG
ag.watchdog.begin();
#endif
}
void loop() {
uint32_t ms;
#if TEST_SENSOR_SenseAirS8
static uint32_t lastTime = 0;
/** Wait for sensor ready */
ms = (uint32_t)(millis() - lastTime);
if (ms >= 1000) {
lastTime = millis();
log_i("CO2: %d (PPM)", ag.s8.getCo2());
}
#endif
#if TEST_SENSOR_PMS5003
static uint32_t pmsTime = 0;
ms = (uint32_t)(millis() - pmsTime);
if (ms >= 1000) {
pmsTime = millis();
if (ag.pms5003.readData()) {
log_i("Passive mode PM 1.0 (ug/m3): %d", ag.pms5003.getPm10Ae());
log_i("Passive mode PM 2.5 (ug/m3): %d", ag.pms5003.getPm25Ae());
log_i("Passive mode PM 10.0 (ug/m3): %d", ag.pms5003.getPm10Ae());
} else {
log_i("PMS sensor read failure");
}
}
#endif
#if TEST_PMS5003T
static uint32_t pmsTime = 0;
ms = (uint32_t)(millis() - pmsTime);
if (ms >= 1000) {
pmsTime = millis();
if (ag.pms5003t_1.readData()) {
log_i("PMS5003_1 PM 1.0 (ug/m3): %d", ag.pms5003t_1.getPm10Ae());
log_i("PMS5003_1 PM 2.5 (ug/m3): %d", ag.pms5003t_1.getPm25Ae());
log_i("PMS5003_1 PM 10.0 (ug/m3): %d", ag.pms5003t_1.getPm10Ae());
log_i("PMS5003_1 PM 3.0 (ug/m3): %d",
ag.pms5003t_1.getPm03ParticleCount());
log_i("Temperature : %02f °C",
ag.pms5003t_1.getTemperature());
log_i("Humidity : %02f %%",
ag.pms5003t_1.getRelativeHumidity());
} else {
log_i("PMS5003_1 sensor read failure");
}
if (ag.pms5003t_2.readData()) {
log_i("PMS5003_2 PM 1.0 (ug/m3): %d", ag.pms5003t_2.getPm10Ae());
log_i("PMS5003_2 PM 2.5 (ug/m3): %d", ag.pms5003t_2.getPm25Ae());
log_i("PMS5003_2 PM 10.0 (ug/m3): %d", ag.pms5003t_2.getPm10Ae());
log_i("PMS5003_2 PM 3.0 (ug/m3): %d",
ag.pms5003t_2.getPm03ParticleCount());
// log_i("Temperature : %02f °C",
// ag.pms5003t_1.getTemperature());
// log_i("Humidity : %02f %%",
// ag.pms5003t_1.getRelativeHumidity());
} else {
log_i("PMS5003_2 sensor read failure");
}
}
#endif
#if TEST_SENSOR_SHT4x
/**
* @brief Get SHT sensor data each 1sec
*
*/
static uint32_t shtTime = 0;
ms = (uint32_t)(millis() - shtTime);
if (ms >= 1000) {
shtTime = millis();
log_i("Get sht temperature: %0.2f (degree celsius)",
ag.sht.getTemperature());
log_i("Get sht temperature: %0.2f (%%)", ag.sht.getRelativeHumidity());
}
#endif
#if TEST_SENSOR_SGP4x
static uint32_t sgpTime;
ms = (uint32_t)(millis() - sgpTime);
if (ms >= 1000) {
sgpTime = millis();
uint16_t rawVOC;
log_i("Get TVOC: %d", ag.sgp41.getTvocIndex());
log_i("Get NOx: %d", ag.sgp41.getNoxIndex());
}
#endif
#if TEST_LED
static uint32_t ledTime;
#if TEST_BOARD_OUTDOOR_MONITOR_V1_3
// ms = (uint32_t)(millis() - ledTime);
// if(ms >= 500)
// {
// ledTime = millis();
// led.ledToggle();
// }
#elif TEST_BOARD_ONE_INDOOR_MONITOR_V9_0
static int ledIndex;
static int ledIndexOld;
ms = (uint32_t)(millis() - ledTime);
if (ms >= 50) {
ledTime = millis();
if (ledIndex == ledIndexOld) {
led.ledOff();
} else {
// Turn last LED off
led.ledSetColor(0, 0, 0, ledIndexOld);
}
// Turn new led ON
led.ledSetColor(255, 0, 0, ledIndex);
ledIndexOld = ledIndex;
ledIndex++;
if (ledIndex >= led.getNumberOfLed()) {
ledIndex = 0;
}
}
#else
#endif
#endif
#if TEST_SWITCH
static PushButton::State stateOld = PushButton::State::BUTTON_RELEASED;
PushButton::State state = ag.button.getState();
if (state != stateOld) {
stateOld = state;
log_i("Button state changed: %s", ag.button.toString(state).c_str());
if (state == PushButton::State::BUTTON_PRESSED) {
ag.statusLed.setOn();
} else {
ag.statusLed.setOff();
}
}
#endif
#if TEST_LED_BAR
static uint32_t ledTime;
static uint8_t ledNum = 0;
static uint8_t ledIndex = 0;
static uint8_t ledStep = 0;
static bool ledOn = false;
if (ledNum == 0) {
ledNum = ag.ledBar.getNumberOfLed();
log_i("Get number of led: %d", ledNum);
if (ledNum) {
ag.ledBar.setBrighness(0xff);
for (int i = 0; i < ledNum; i++) {
// ag.ledBar.setColor(0xff, 0xff, 0xff, i);
// ag.ledBar.setColor(204, 136, 153, i);
// ag.ledBar.setColor(204, 0, 0, i);
// ag.ledBar.setColor(204, 100, 153, i);
ag.ledBar.setColor(0, 136, 255, i);
}
ag.ledBar.show();
}
} else {
ms = (uint32_t)(millis() - ledTime);
if (ms >= 500) {
ledTime = millis();
switch (ledStep) {
case 0: {
ag.ledBar.setColor(255, 0, 0, ledIndex);
ledIndex++;
if (ledIndex >= ledNum) {
ag.ledBar.setColor(0, 0, 0);
ledIndex = 0;
ledStep = 1;
}
ag.ledBar.show();
break;
}
case 1: {
ledIndex++;
if (ledIndex >= ledNum) {
ag.ledBar.setColor(255, 0, 0);
ag.ledBar.show();
ledIndex = ledNum - 1;
ledStep = 2;
}
break;
}
case 2: {
if (ledOn) {
ag.ledBar.setColor(255, 0, 0);
} else {
ag.ledBar.setColor(0, 0, 0);
}
ledOn = !ledOn;
ag.ledBar.show();
ledIndex--;
if (ledIndex == 0) {
ag.ledBar.setColor(0, 0, 0);
ag.ledBar.show();
ledStep = 0;
ledIndex = 0;
}
break;
}
default:
break;
}
}
}
#endif
#if TEST_WATCHDOG
static uint32_t wdgTime;
ms = (uint32_t)(millis() - wdgTime);
if (ms >= (1000 * 60)) {
wdgTime = millis();
/** Reset watchdog reach 1 minutes */
ag.watchdog.reset();
}
#endif
}

View File

@ -1,164 +0,0 @@
#include <AirGradient.h>
#include <Wire.h>
/**
* @brief Define test board
*/
#define TEST_BOARD_DIY_BASIC_KIT 0
#define TEST_BOARD_DIY_PRO_INDOOR_V4_2 1
/**
* @brief Define test sensor
*/
#define TEST_SENSOR_SenseAirS8 0
#define TEST_SENSOR_PMS5003 0
#define TEST_SENSOR_SHT4x 0
#define TEST_SENSOR_SGP4x 1
#define TEST_SWITCH 0
#define TEST_OLED 0
#if TEST_BOARD_DIY_BASIC_KIT
AirGradient ag(BOARD_DIY_BASIC_KIT);
#elif TEST_BOARD_DIY_PRO_INDOOR_V4_2
AirGradient ag(BOARD_DIY_PRO_INDOOR_V4_2);
#else
#error "Board test not defined"
#endif
void setup() {
Serial.begin(115200);
/** Print All AirGradient board define */
printBoardDef(&Serial);
#if TEST_SENSOR_SenseAirS8
if (ag.s8.begin(&Serial) == true) {
Serial.println("CO2S8 sensor init success");
} else {
Serial.println("CO2S8 sensor init failure");
}
if (ag.s8.setBaselineCalibration()) {
Serial.println("Manual calib success");
} else {
Serial.println("Manual calib failure");
}
delay(5000);
#endif
#if TEST_SENSOR_PMS5003
if (ag.pms5003.begin(&Serial) == true) {
Serial.println("PMS5003 sensor init success");
} else {
Serial.println("PMS5003 sensor init failure");
}
#endif
#if TEST_SENSOR_SHT4x || TEST_SENSOR_SGP4x || TEST_OLED
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
#endif
#if TEST_SENSOR_SHT4x
if (ag.sht.begin(Wire, Serial)) {
Serial.println("SHT init success");
} else {
Serial.println("SHT init failed");
}
#endif
#if TEST_SENSOR_SGP4x
if (ag.sgp41.begin(Wire, Serial)) {
Serial.println("SGP init succses");
} else {
Serial.println("SGP init failure");
}
#endif
#if TEST_SWITCH
ag.button.begin(Serial);
#endif
#if TEST_OLED
ag.display.begin(Wire, Serial);
ag.display.setTextSize(1);
ag.display.setCursor(0, 0);
ag.display.setTextColor(1);
ag.display.setText("Hello");
ag.display.show();
#endif
}
void loop() {
uint32_t ms;
#if TEST_SENSOR_SenseAirS8
static uint32_t lastTime = 0;
/** Wait for sensor ready */
// if(co2s8.isReady())
// {
// Get sensor data each 1sec
ms = (uint32_t)(millis() - lastTime);
if (ms >= 1000) {
lastTime = millis();
Serial.printf("CO2: %d (PMM)\r\n", ag.s8.getCo2());
}
// }
#endif
#if TEST_SENSOR_PMS5003
static uint32_t pmsTime = 0;
ms = (uint32_t)(millis() - pmsTime);
if (ms >= 1000) {
pmsTime = millis();
if (ag.pms5003.readData()) {
Serial.printf("Passive mode PM 1.0 (ug/m3): %d\r\n",
ag.pms5003.getPm01Ae());
Serial.printf("Passive mode PM 2.5 (ug/m3): %d\r\n",
ag.pms5003.getPm25Ae());
Serial.printf("Passive mode PM 10.5 (ug/m3): %d\r\n",
ag.pms5003.getPm10Ae());
}
}
#endif
#if TEST_SENSOR_SHT4x
/**
* @brief Get SHT sensor data each 1sec
*
*/
static uint32_t shtTime = 0;
ms = (uint32_t)(millis() - shtTime);
if (ms >= 1000) {
shtTime = millis();
float temperature, humidity;
Serial.printf("SHT Temperature: %f, Humidity: %f\r\n",
ag.sht.getTemperature(), ag.sht.getRelativeHumidity());
}
#endif
#if TEST_SENSOR_SGP4x
static uint32_t sgpTime;
ms = (uint32_t)(millis() - sgpTime);
/***
* Must call this task on loop and avoid delay on loop over 1000 ms
*/
ag.sgp41.handle();
if (ms >= 1000) {
sgpTime = millis();
Serial.printf("SGP TVOC: %d, NOx: %d\r\n", ag.sgp41.getTvocIndex(),
ag.sgp41.getNoxIndex());
}
#endif
#if TEST_SWITCH
static PushButton::State stateOld = PushButton::State::BUTTON_RELEASED;
PushButton::State state = ag.button.getState();
if (state != stateOld) {
stateOld = state;
Serial.printf("Button state changed: %s\r\n",
ag.button.toString(state).c_str());
}
#endif
}

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);
@ -23,7 +23,7 @@ void setup() {
failedHandler("Init PMS5003 failed");
}
#else
if (ag.getBoardType() == BOARD_OUTDOOR_MONITOR_V1_3) {
if (ag.getBoardType() == OPEN_AIR_OUTDOOR) {
if (ag.pms5003t_1.begin(Serial0) == false) {
failedHandler("Init PMS5003T failed");
}
@ -37,25 +37,37 @@ void setup() {
void loop() {
int PM2;
bool readResul = false;
#ifdef ESP8266
if (ag.pms5003.readData()) {
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));
Serial.printf("PM2.5 in US AQI: %d\r\n",
ag.pms5003.convertPm25ToUsAqi(PM2));
}
#else
if (ag.getBoardType() == BOARD_OUTDOOR_MONITOR_V1_3) {
if (ag.getBoardType() == OPEN_AIR_OUTDOOR) {
if (ag.pms5003t_1.readData()) {
PM2 = ag.pms5003t_1.getPm25Ae();
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() == BOARD_OUTDOOR_MONITOR_V1_3) {
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>
#if defined(ESP8266)
AirGradient ag(DIY_BASIC);
#else
AirGradient ag(ONE_INDOOR);
#endif
void failedHandler(String msg);
void setup() {
Serial.begin(115200);
Serial.println("Hello");
Wire.begin(ag.getI2cSdaPin(), ag.getI2cSclPin());
delay(1000);
if (ag.sht.begin(Wire) == false) {
failedHandler("SHT init failed");
}
}
void loop() {
if (ag.sht.measure()) {
float hum = ag.sht.getRelativeHumidity();
float temp = ag.sht.getTemperature();
Serial.printf("Get temperature: %f\r\n", temp);
Serial.printf(" Get humidity: %f\r\n", hum);
} else {
Serial.println("Measure failed");
}
delay(1000);
}
void failedHandler(String msg) {
while (true) {
Serial.println(msg);
delay(1000);
}
}

View File

@ -1,5 +1,5 @@
name=AirGradient Air Quality Sensor
version=3.0.0
version=3.0.4
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.4"
AirGradient::AirGradient(BoardType type)
: pms5003(type), pms5003t_1(type), pms5003t_2(type), s8(type), sht(type), sgp41(type),
: pms5003(type), pms5003t_1(type), pms5003t_2(type), s8(type), sgp41(type),
display(type), boardType(type), button(type), statusLed(type),
ledBar(type), watchdog(type) {}
ledBar(type), watchdog(type), sht(type) {}
/**
* @brief Get pin number for I2C SDA
@ -36,3 +36,7 @@ int AirGradient::getI2cSclPin(void) {
String AirGradient::getVersion(void) { return AG_LIB_VER; }
BoardType AirGradient::getBoardType(void) { return boardType; }
double AirGradient::round2(double value) {
return (int)(value * 100 + 0.5) / 100.0;
}

View File

@ -1,18 +1,22 @@
#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 "sht/sht4x.h"
#include "Display/Display.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/Sht.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 +25,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 +42,19 @@ public:
S8 s8;
/**
* @brief Temperature and humidity sensor
* @brief Temperature and humidity sensor supported SHT3x and SHT4x
*
*/
Sht sht;
/**
* @brief TVOC and NOx sensor
* @brief SGP41 TVOC and NOx sensor
*
*/
Sgp41 sgp41;
/**
* @brief Display
* @brief OLED Display
*
*/
Display display;
@ -55,20 +68,53 @@ 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);
/**
* @brief Round double value with for 2 decimal
*
* @param valuem Round value
* @return double
*/
double round2(double value);
private:
BoardType boardType;
};

282
src/Display/Display.cpp Normal file
View File

@ -0,0 +1,282 @@
#include "Display.h"
#include "../Libraries/Adafruit_SH110x/Adafruit_SH110X.h"
#include "../Libraries/Adafruit_SSD1306_Wemos_OLED/Adafruit_SSD1306.h"
#define disp(func) \
if (this->_boardType == DIY_BASIC) { \
((Adafruit_SSD1306 *)(this->oled))->func; \
} else { \
((Adafruit_SH110X *)(this->oled))->func; \
}
#if defined(ESP8266)
void Display::begin(TwoWire &wire, Stream &debugStream) {
this->_debugStream = &debugStream;
this->begin(wire);
}
#else
#endif
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)) {
AgLog("Init failed: board not supported");
return;
}
/** Init OLED */
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);
_oled->begin(this->_bsp->OLED.addr, false);
this->oled = _oled;
}
this->_isBegin = true;
disp(clearDisplay());
AgLog("Initialize");
}
/**
* @brief Clear display buffer
*
*/
void Display::clear(void) {
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->isBegin() == false) {
return;
}
disp(invertDisplay(i));
}
/**
* @brief Send display frame buffer to OLED
*
*/
void Display::show() {
if (this->isBegin() == false) {
return;
}
disp(display());
}
/**
* @brief Set display contract
*
* @param value Contract (0;255);
*/
void Display::setContrast(uint8_t value) {
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->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->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->isBegin() == false) {
return;
}
disp(setCursor(x, y));
}
/**
* @brief Set Text Color
*
* @param color 0:black, 1: While
*/
void Display::setTextColor(uint16_t color) {
if (this->isBegin() == false) {
return;
}
disp(setTextColor(color));
}
/**
* @brief Set text foreground color and background color
*
* @param foreGroundColor Text Color (foreground color)
* @param backGroundColor Text background color
*/
void Display::setTextColor(uint16_t foreGroundColor, uint16_t backGroundColor) {
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->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->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->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->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->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->isBegin() == false) {
return;
}
disp(drawRect(x0, y0, x1, y1, color));
}
bool Display::isBegin(void) {
if (this->_isBegin) {
return true;
}
AgLog("Display not-initialized");
return false;
}
void Display::setRotation(uint8_t r) {
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_ */

Some files were not shown because too many files have changed in this diff Show More