updated screen + added OTA

This commit is contained in:
2021-07-27 08:26:12 +02:00
parent 11b146c2d1
commit dd511c8e18
14 changed files with 261 additions and 57 deletions

3
.gitignore vendored
View File

@@ -1 +1,4 @@
.pio/ .pio/
.vscode/c_cpp_properties.json
.gitignore
**/DS.store

View File

@@ -42,22 +42,32 @@ void initCO2sensor(void)
void handleCO2sensor(void) void handleCO2sensor(void)
{ {
uint32_t currentmillis = millis(); uint32_t currentmillis = millis();
if ((currentmillis - CO2_lastUpdate > (g_pms_report_period * 1000)) || (!CO2_lastUpdate) && (scd30.dataReady())) if ((currentmillis - CO2_lastUpdate > (g_pms_report_period * 1000)) || (!CO2_lastUpdate) )
{ {
if (!scd30.read()) if (!scd30.read())
{ {
Serial.println("SCD30: read error!"); Serial.println("SCD30: read error!");
return; return;
} }
SCD30_temperature.set(uint32_t(scd30.temperature)); if(!scd30.dataReady())
SCD30_Humidity.set(uint32_t(scd30.relative_humidity)); {
SCD30_CO2.set(uint32_t(scd30.CO2)); return;
}
else
{
SCD30_temperature.set(uint32_t(scd30.temperature));
SCD30_Humidity.set(uint32_t(scd30.relative_humidity));
SCD30_CO2.set(uint32_t(scd30.CO2));
SCD30_temperature.publish(); SCD30_temperature.publish();
SCD30_Humidity.publish(); SCD30_Humidity.publish();
SCD30_CO2.publish(); SCD30_CO2.publish();
CO2_lastUpdate = currentmillis;
}
CO2_lastUpdate = currentmillis;
} }
} }

View File

@@ -4,14 +4,15 @@ Adafruit_SGP30 sgp;
uint8_t VOC_samples = 3600 / g_pms_report_period; uint8_t VOC_samples = 3600 / g_pms_report_period;
#define VOC_MIN 0 #define VOC_MIN 0
#define VOC_MAX 10000 #define VOC_MAX 10000
#define VOC_RAW_MAX 30000
#define VOC_INTERVAL 120000 //ms #define VOC_INTERVAL 120000 //ms
uint32_t lastVOCtime = 0; uint32_t lastVOCtime = 0;
//sensors //sensors
AQSSensor SGP30_tvoc("TVOC", SGP30_TVOC, "ppb", device_name, VOC_samples, VOC_MIN, VOC_MAX); AQSSensor SGP30_tvoc("TVOC", SGP30_TVOC, "ppb", device_name, VOC_samples, VOC_MIN, VOC_MAX);
AQSSensor SGP30_eco2("eCO2", SGP30_eCO2, "ppm", device_name, VOC_samples, VOC_MIN, VOC_MAX); AQSSensor SGP30_eco2("eCO2", SGP30_eCO2, "ppm", device_name, VOC_samples, VOC_MIN, VOC_MAX);
AQSSensor SGP30_rawh2("Raw_H2", SGP30_rawH2, "#", device_name, VOC_samples, VOC_MIN, VOC_MAX); AQSSensor SGP30_rawh2("Raw_H2", SGP30_rawH2, "#", device_name, VOC_samples, VOC_MIN, VOC_RAW_MAX);
AQSSensor SGP30_rawethanol("Raw_Ethanol", SGP30_rawEthanol, "#", device_name, VOC_samples, VOC_MIN, VOC_MAX); AQSSensor SGP30_rawethanol("Raw_Ethanol", SGP30_rawEthanol, "#", device_name, VOC_samples, VOC_MIN, VOC_RAW_MAX);
void initVOCsensor(void) void initVOCsensor(void)
{ {
@@ -31,7 +32,7 @@ void initVOCsensor(void)
sgp.IAQmeasure(); sgp.IAQmeasure();
sgp.IAQmeasureRaw(); sgp.IAQmeasureRaw();
Serial.println("VOCSensor: Init OK"); Serial.println("VOCSensor: Init OK");
} }
} }
@@ -50,7 +51,7 @@ void handleVOCsensor(void)
SGP30_eco2.set(sgp.eCO2); SGP30_eco2.set(sgp.eCO2);
SGP30_tvoc.publish(); SGP30_tvoc.publish();
SGP30_eco2.publish(); SGP30_eco2.publish();
if (!sgp.IAQmeasureRaw()) if (!sgp.IAQmeasureRaw())
{ {

View File

@@ -2,6 +2,7 @@
#include "Arduino.h" #include "Arduino.h"
#include "config.h" #include "config.h"
#include "lcd.h"
void initButtons( void ); void initButtons( void );
void handleButtons( void ); void handleButtons( void );

View File

@@ -27,6 +27,7 @@ void handleButtons(void)
return; return;
} }
Serial.println("Button pressed"); Serial.println("Button pressed");
backlightRefresh();
// Increment display state // Increment display state
g_last_debounce_time = millis(); g_last_debounce_time = millis();
} }

View File

@@ -28,6 +28,11 @@
/* ----------------- Hardware-specific Config ---------------------- */ /* ----------------- Hardware-specific Config ---------------------- */
/* Mode button connection (momentary between this pin and GND) */ /* Mode button connection (momentary between this pin and GND) */
#define MODE_BUTTON_PIN 0 #define MODE_BUTTON_PIN 0
#define TFT_BL 4 // LED back-light control pin
#define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW)
#define TFT_BL_PWMCHANNEL 1
#define TFT_BL_FREQ 5000
#define TFT_BL_BITS 8
/* I2C */ /* I2C */
#define I2C_SDA_PIN 21 #define I2C_SDA_PIN 21

46
esp_ota.cpp Normal file
View File

@@ -0,0 +1,46 @@
#include "esp_ota.h"
void initEspOta(void)
{
Serial.println("ESPOTA: init");
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("ESPOTA: Start updating " + type);
ProgressbarVisible(true);
ledcWrite(TFT_BL_PWMCHANNEL, 64);
})
.onEnd([]() {
Serial.println("\nESPOTA: End");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("ESPOTA: Progress: %u%%\r", (progress / (total / 100)));
setOTAProgress((progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("ESPOTA: Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("ESPOTA: Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("ESPOTA: Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("ESPOTA: Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("ESPOTA: Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("ESPOTA: End Failed");
});
ArduinoOTA.begin();
Serial.print("ESPOTA: IP address: ");
Serial.println(WiFi.localIP());
Serial.println("ESPOTA: init OK");
}
void handleEspOta(void)
{
ArduinoOTA.handle();
}

8
esp_ota.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include "Arduino.h"
#include <ArduinoOTA.h>
#include "lcd.h"
void initEspOta(void);
void handleEspOta(void);

152
lcd.cpp
View File

@@ -38,6 +38,7 @@ TFT_eSprite needle = TFT_eSprite(&tft); // Sprite object for needle
TFT_eSprite spr = TFT_eSprite(&tft); // Sprite for meter reading TFT_eSprite spr = TFT_eSprite(&tft); // Sprite for meter reading
TFT_eSprite nameSpr = TFT_eSprite(&tft); TFT_eSprite nameSpr = TFT_eSprite(&tft);
TFT_eSprite unitSpr = TFT_eSprite(&tft); TFT_eSprite unitSpr = TFT_eSprite(&tft);
TFT_eSprite ProgressBar = TFT_eSprite(&tft);
// Jpeg image array attached to this sketch // Jpeg image array attached to this sketch
#include "dial.h" #include "dial.h"
@@ -49,6 +50,7 @@ uint16_t *tft_buffer;
bool buffer_loaded = false; bool buffer_loaded = false;
uint16_t spr_width = 0; uint16_t spr_width = 0;
uint16_t name_spr_width = 0; uint16_t name_spr_width = 0;
bool progessbarActive = false;
void createNeedle(void); void createNeedle(void);
void plotNeedle(int16_t angle, uint16_t ms_delay); void plotNeedle(int16_t angle, uint16_t ms_delay);
@@ -69,29 +71,58 @@ bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
return 1; return 1;
} }
// ======================================================================================= void setBacklight(uint8_t value)
// Setup
// =======================================================================================
void initLCD()
{ {
Serial.print("InitLCD:"); ledcWrite(TFT_BL_PWMCHANNEL, value);
// The byte order can be swapped (set true for TFT_eSPI) }
TJpgDec.setSwapBytes(true);
// The jpeg decoder must be given the exact name of the rendering function above void createProgressBar(void)
TJpgDec.setCallback(tft_output); {
ProgressBar.createSprite(220, 30);
ProgressBar.fillSprite(TFT_BLACK);
ProgressBar.drawRoundRect(0, 2, 220, 22, 5, TFT_BLUE);
ProgressBar.pushSprite(10, 270);
}
tft.begin(); void ProgressbarVisible(bool visible)
tft.setRotation(0); {
tft.fillScreen(TFT_BLACK); progessbarActive = visible;
if (!visible)
{
ProgressBar.fillSprite(TFT_BLACK);
ProgressBar.pushSprite(10, 270);
}
}
pinMode(TFT_BL, OUTPUT); void setOTAProgress(uint8_t value)
{
uint16_t progress = map(value, 0, 100, 0, 212);
ProgressBar.drawRoundRect(0, 2, 220, 22, 5, TFT_BLUE);
ProgressBar.fillRoundRect(4, 5, progress, 15, 3, TFT_BLUE);
if (progessbarActive)
{
ProgressBar.pushSprite(10, 270);
}
}
// Draw the dial void createNameSprite(void)
TJpgDec.drawJpg(0, 0, dial, sizeof(dial)); {
tft.drawCircle(DIAL_CENTRE_X, DIAL_CENTRE_Y, NEEDLE_RADIUS - NEEDLE_LENGTH, TFT_DARKGREY); nameSpr.setTextFont(2);
name_spr_width = nameSpr.textWidth("---Sensor Name---");
nameSpr.createSprite(name_spr_width, nameSpr.fontHeight() * 2 + 2);
uint16_t bg_color = tft.readPixel(120, 120); // Get colour from dial centre
// Load the font and create the Sprite for reporting the value nameSpr.fillSprite(bg_color);
nameSpr.setTextColor(TFT_WHITE, bg_color);
nameSpr.setTextDatum(MC_DATUM);
nameSpr.setTextPadding(name_spr_width);
nameSpr.drawString("Sensor Name", name_spr_width / 2, nameSpr.fontHeight() / 2);
nameSpr.drawString("Unit", name_spr_width / 2, nameSpr.fontHeight() / 2 * 3 + 2);
nameSpr.pushSprite(DIAL_CENTRE_X - name_spr_width / 2, DIAL_CENTRE_Y + 40, 2);
}
void createValueSprinte(void)
{
spr.loadFont(AA_FONT_LARGE); spr.loadFont(AA_FONT_LARGE);
spr_width = spr.textWidth("277"); spr_width = spr.textWidth("277");
spr.createSprite(spr_width, spr.fontHeight()); spr.createSprite(spr_width, spr.fontHeight());
@@ -102,25 +133,45 @@ void initLCD()
spr.setTextPadding(spr_width); spr.setTextPadding(spr_width);
spr.drawNumber(0, spr_width / 2, spr.fontHeight() / 2); spr.drawNumber(0, spr_width / 2, spr.fontHeight() / 2);
spr.pushSprite(DIAL_CENTRE_X - spr_width / 2, DIAL_CENTRE_Y - spr.fontHeight() / 2); spr.pushSprite(DIAL_CENTRE_X - spr_width / 2, DIAL_CENTRE_Y - spr.fontHeight() / 2);
}
// Plot the label text void createDail(void)
nameSpr.setTextFont(2); {
name_spr_width = nameSpr.textWidth("---Sensor Name---"); TJpgDec.setSwapBytes(true);
nameSpr.createSprite(name_spr_width, nameSpr.fontHeight() * 2 + 2);
nameSpr.fillSprite(bg_color); // The jpeg decoder must be given the exact name of the rendering function above
nameSpr.setTextColor(TFT_WHITE, bg_color); TJpgDec.setCallback(tft_output);
nameSpr.setTextDatum(MC_DATUM); // Draw the dial
nameSpr.setTextPadding(name_spr_width); TJpgDec.drawJpg(0, 0, dial, sizeof(dial));
nameSpr.drawString("Sensor Name", name_spr_width / 2, nameSpr.fontHeight() / 2); tft.drawCircle(DIAL_CENTRE_X, DIAL_CENTRE_Y, NEEDLE_RADIUS - NEEDLE_LENGTH, TFT_DARKGREY);
nameSpr.drawString("Unit", name_spr_width / 2, nameSpr.fontHeight() / 2 * 3 + 2); nameSpr.pushSprite(DIAL_CENTRE_X - name_spr_width / 2, DIAL_CENTRE_Y + 40, 2);
nameSpr.pushSprite(DIAL_CENTRE_X - name_spr_width / 2, DIAL_CENTRE_Y + 40, 2);
// Define where the needle pivot point is on the TFT before // Define where the needle pivot point is on the TFT before
// creating the needle so boundary calculation is correct // creating the needle so boundary calculation is correct
tft.setPivot(DIAL_CENTRE_X, DIAL_CENTRE_Y); tft.setPivot(DIAL_CENTRE_X, DIAL_CENTRE_Y);
}
// Create the needle Sprite // =======================================================================================
// Setup
// =======================================================================================
void initLCD()
{
Serial.print("InitLCD:");
// The byte order can be swapped (set true for TFT_eSPI)
tft.begin();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
// Create the Sprites
createProgressBar();
createDail();
createValueSprinte();
createNeedle(); createNeedle();
createNameSprite();
ledcSetup(TFT_BL_PWMCHANNEL, TFT_BL_FREQ, TFT_BL_BITS);
ledcAttachPin(TFT_BL, TFT_BL_PWMCHANNEL);
ledcWrite(TFT_BL_PWMCHANNEL, 64);
// Reset needle position to 0 // Reset needle position to 0
plotNeedle(0, 0, 0); plotNeedle(0, 0, 0);
@@ -150,6 +201,47 @@ uint32_t display_last_update = 0;
#define MINGUAGE 0 #define MINGUAGE 0
#define DISPLAY_ROTATE 15 //sec #define DISPLAY_ROTATE 15 //sec
uint32_t backlightTimeout = 0;
#define BACKLIGHTTIMEOUT 60000
#define BACKLIGHTONBRGT 129
#define BACKLIGHTSTEP 5
uint8_t backloghtBrightness = BACKLIGHTONBRGT;
void backlightRefresh(void)
{
backlightTimeout = millis();
}
void handleBacklight(void)
{
uint32_t timeNow = millis();
if (!backlightTimeout)
{
backlightRefresh();
}
if (timeNow - backlightTimeout > BACKLIGHTTIMEOUT)
{
if (backloghtBrightness > BACKLIGHTSTEP)
{
backloghtBrightness -= BACKLIGHTSTEP;
Serial.printf("LCD: backlight %d\n", backloghtBrightness);
}
else
{
backloghtBrightness = 0;
}
}
else
{
if (backloghtBrightness < BACKLIGHTONBRGT)
{
backloghtBrightness += BACKLIGHTSTEP;
Serial.printf("LCD: backlight %d", backloghtBrightness);
}
}
ledcWrite(TFT_BL_PWMCHANNEL, backloghtBrightness);
}
void handleLCD() void handleLCD()
{ {
static uint16_t angle; static uint16_t angle;
@@ -226,6 +318,8 @@ void handleLCD()
nameSpr.drawString(sensor->getName().c_str(), name_spr_width / 2, nameSpr.fontHeight() / 2); nameSpr.drawString(sensor->getName().c_str(), name_spr_width / 2, nameSpr.fontHeight() / 2);
nameSpr.drawString(sensor->getUnit().c_str(), name_spr_width / 2, nameSpr.fontHeight() / 2 * 3 + 2); nameSpr.drawString(sensor->getUnit().c_str(), name_spr_width / 2, nameSpr.fontHeight() / 2 * 3 + 2);
nameSpr.pushSprite(DIAL_CENTRE_X - name_spr_width / 2, DIAL_CENTRE_Y + 40, 2); nameSpr.pushSprite(DIAL_CENTRE_X - name_spr_width / 2, DIAL_CENTRE_Y + 40, 2);
handleBacklight();
} }
// ======================================================================================= // =======================================================================================

4
lcd.h
View File

@@ -7,3 +7,7 @@ void initLCD();
void handleLCD(); void handleLCD();
void plotNeedle(int16_t angle, uint16_t ms_delay, uint32_t value); void plotNeedle(int16_t angle, uint16_t ms_delay, uint32_t value);
void setOTAProgress(uint8_t value);
void ProgressbarVisible(bool visible);
void backlightRefresh(void);

View File

@@ -41,6 +41,7 @@
#include "CO2_sensor.h" #include "CO2_sensor.h"
#include "VOC_sensor.h" #include "VOC_sensor.h"
#include "button.h" #include "button.h"
#include "esp_ota.h"
/*--------------------------- Program ---------------------------------------*/ /*--------------------------- Program ---------------------------------------*/
/** /**
@@ -52,17 +53,33 @@ void setup()
Serial.println(); Serial.println();
Serial.print("Air Quality Sensor starting up, v"); Serial.print("Air Quality Sensor starting up, v");
Serial.println(VERSION); Serial.println(VERSION);
ProgressbarVisible(true);
digitalWrite(5, false);
initWifi();
initMQTT();
initSensor();
initParticles();
initCO2sensor();
initVOCsensor();
initLCD(); initLCD();
setOTAProgress(10);
initButtons(); initButtons();
setOTAProgress(20);
initWifi();
setOTAProgress(30);
initEspOta();
setOTAProgress(40);
initMQTT();
setOTAProgress(50);
initSensor();
setOTAProgress(60);
initParticles();
setOTAProgress(70);
initCO2sensor();
setOTAProgress(80);
initVOCsensor();
setOTAProgress(90);
ProgressbarVisible(false);
} }
/** /**
@@ -77,4 +94,5 @@ void loop()
handleSensor(); handleSensor();
handleLCD(); handleLCD();
handleMQTT(); handleMQTT();
handleEspOta();
} }

View File

@@ -12,7 +12,7 @@ char g_command_topic[50]; // MQTT topic for receiving commands
#define PMS_STATE_READY 2 // Warmed up, ready to give data #define PMS_STATE_READY 2 // Warmed up, ready to give data
#define PMSMAX 50 #define PMSMAX 50
#define PSMMIN 0 #define PSMMIN 0
#define PPDMAX 2000 #define PPDMAX 3000
uint8_t g_pms_state = PMS_STATE_WAKING_UP; uint8_t g_pms_state = PMS_STATE_WAKING_UP;
uint32_t g_pms_state_start = 0; // Timestamp when PMS state last changed uint32_t g_pms_state_start = 0; // Timestamp when PMS state last changed

View File

@@ -33,3 +33,5 @@ lib_deps =
${common.lib_deps_builtin} ${common.lib_deps_builtin}
${common.lib_deps} ${common.lib_deps}
adafruit/Adafruit SGP30 Sensor@^2.0.0 adafruit/Adafruit SGP30 Sensor@^2.0.0
upload_protocol = espota
upload_port = 192.168.2.236

View File

@@ -42,6 +42,7 @@ class AQSSensor
bool _publish = false; bool _publish = false;
const uint32_t _scaleMin; const uint32_t _scaleMin;
const uint32_t _scaleMax; const uint32_t _scaleMax;
bool _valid;
public: public:
AQSSensor(String name, sensor_e sensor, String unit, AQSSensor(String name, sensor_e sensor, String unit,
@@ -58,14 +59,23 @@ public:
sprintf(_topic, "Sensors/%s/%s", deviceID.c_str(), name.c_str()); sprintf(_topic, "Sensors/%s/%s", deviceID.c_str(), name.c_str());
sprintf(_topic_1h, "Sensors/%s/%s/1h/", deviceID.c_str(), name.c_str()); sprintf(_topic_1h, "Sensors/%s/%s/1h/", deviceID.c_str(), name.c_str());
sprintf(_topic_24h, "Sensors/%s/%s/24h/", deviceID.c_str(), name.c_str()); sprintf(_topic_24h, "Sensors/%s/%s/24h/", deviceID.c_str(), name.c_str());
_valid = false;
} }
void publish(void) void publish(void)
{ {
_publish = true; if (_valid)
{
_publish = true;
}
else
{
Serial.printf("Sensor %s : Not published (invalid)\n", _name.c_str());
}
} }
String getName(void) String
getName(void)
{ {
return _name; return _name;
} }
@@ -88,17 +98,18 @@ public:
virtual void set(uint32_t value) virtual void set(uint32_t value)
{ {
if((value > _scaleMin) && (value < _scaleMax)) if ((value > _scaleMin) && (value < _scaleMax))
{ {
_value = value; _value = value;
_average1h.add(_value); _average1h.add(_value);
_average24h.add(_value); _average24h.add(_value);
_valid = true;
} }
else else
{ {
Serial.printf("Sensor %s: value out of bounds (%d)\n", _name.c_str(), value); Serial.printf("Sensor %s set(%d): value out of bounds\n", _name.c_str(), _value);
_valid = false;
} }
} }
uint32_t value() { return _value; } uint32_t value() { return _value; }
@@ -113,7 +124,7 @@ public:
sensor_e getSensor(void) { return _sensor; } sensor_e getSensor(void) { return _sensor; }
String getUnit(void) { return _unit;} String getUnit(void) { return _unit; }
uint32_t getMin(void) { return _scaleMin; } uint32_t getMin(void) { return _scaleMin; }
uint32_t getMax(void) { return _scaleMax; } uint32_t getMax(void) { return _scaleMax; }