ESP32 | Cloud Firestore - Ep 3. Status Icons on Display (The End)

This commit is contained in:
Eric
2021-08-18 15:12:46 -07:00
parent 82829a8dd3
commit 0379f585f5
10 changed files with 480 additions and 0 deletions

View File

@@ -0,0 +1,248 @@
/////////////////////////////////////////////////////////////////
/*
ESP32 | Cloud Firestore - Ep 3. Status Icons on Display
Video Tutorial: https://youtu.be/LR_FgObfuCw
Created by Eric N. (ThatProject)
*/
/////////////////////////////////////////////////////////////////
#include "DHTesp.h" // Click here to get the library: http://librarymanager/All#DHTesp
#include <Ticker.h>
#include "Display.h"
#include "Network.h"
#ifndef ESP32
#pragma message(THIS EXAMPLE IS FOR ESP32 ONLY!)
#error Select ESP32 board.
#endif
/**************************************************************/
/* Example how to read DHT sensors from an ESP32 using multi- */
/* tasking. */
/* This example depends on the Ticker library to wake up */
/* the task every 20 seconds */
/**************************************************************/
Display *display;
Network *network;
DHTesp dht;
void tempTask(void *pvParameters);
bool getTemperature();
void triggerGetTemp();
/** Task handle for the light value read task */
TaskHandle_t tempTaskHandle = NULL;
/** Ticker for temperature reading */
Ticker tempTicker;
/** Comfort profile */
ComfortState cf;
/** Flag if task should run */
bool tasksEnabled = false;
/** Pin number for DHT11 data pin */
int dhtPin = 21;
/**
* initTemp
* Setup DHT library
* Setup task and timer for repeated measurement
* @return bool
* true if task and timer are started
* false if task or timer couldn't be started
*/
bool initTemp() {
byte resultValue = 0;
// Initialize temperature sensor
dht.setup(dhtPin, DHTesp::DHT11);
Serial.println("DHT initiated");
// Start task to get temperature
xTaskCreatePinnedToCore(
tempTask, /* Function to implement the task */
"tempTask ", /* Name of the task */
10000, /* Stack size in words */
NULL, /* Task input parameter */
5, /* Priority of the task */
&tempTaskHandle, /* Task handle. */
1); /* Core where the task should run */
if (tempTaskHandle == NULL) {
Serial.println("Failed to start task for temperature update");
return false;
} else {
// Start update of environment data every 20 seconds
tempTicker.attach(20, triggerGetTemp);
}
return true;
}
/**
* triggerGetTemp
* Sets flag dhtUpdated to true for handling in loop()
* called by Ticker getTempTimer
*/
void triggerGetTemp() {
if (tempTaskHandle != NULL) {
xTaskResumeFromISR(tempTaskHandle);
}
}
/**
* Task to reads temperature from DHT11 sensor
* @param pvParameters
* pointer to task parameters
*/
void tempTask(void *pvParameters) {
Serial.println("tempTask loop started");
while (1) // tempTask loop
{
if (tasksEnabled) {
// Get temperature values
getTemperature();
}
// Got sleep again
vTaskSuspend(NULL);
}
}
/**
* getTemperature
* Reads temperature from DHT11 sensor
* @return bool
* true if temperature could be aquired
* false if aquisition failed
*/
bool getTemperature() {
// Reading temperature for humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (it's a very slow sensor)
TempAndHumidity newValues = dht.getTempAndHumidity();
// Check if any reads failed and exit early (to try again).
if (dht.getStatus() != 0) {
Serial.println("DHT11 error status: " + String(dht.getStatusString()));
return false;
}
float heatIndex = dht.computeHeatIndex(newValues.temperature, newValues.humidity);
float dewPoint = dht.computeDewPoint(newValues.temperature, newValues.humidity);
float cr = dht.getComfortRatio(cf, newValues.temperature, newValues.humidity);
String comfortStatus;
switch(cf) {
case Comfort_OK:
comfortStatus = "OK";
break;
case Comfort_TooHot:
comfortStatus = "TooHot";
break;
case Comfort_TooCold:
comfortStatus = "TooCold";
break;
case Comfort_TooDry:
comfortStatus = "TooDry";
break;
case Comfort_TooHumid:
comfortStatus = "TooHumid";
break;
case Comfort_HotAndHumid:
comfortStatus = "Hot&Humid";
break;
case Comfort_HotAndDry:
comfortStatus = "Hot&Dry";
break;
case Comfort_ColdAndHumid:
comfortStatus = "Cold&Humid";
break;
case Comfort_ColdAndDry:
comfortStatus = "Cold&Dry";
break;
default:
comfortStatus = "Unknown";
break;
};
Serial.println(" T:" + String(newValues.temperature) + " H:" + String(newValues.humidity) + " I:" + String(heatIndex) + " D:" + String(dewPoint) + " " + comfortStatus);
display->tempUpdates("Temp " + String(newValues.temperature, 1) +"'C",
"Humidity " + String(newValues.humidity,0) + "%",
comfortStatus);
network->firestoreDataUpdate(newValues.temperature, newValues.humidity);
return true;
}
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println("DHT ESP32 example with tasks");
initDisplay();
initNetwork();
initTemp();
// Signal end of setup() to tasks
tasksEnabled = true;
}
void loop() {
if (!tasksEnabled) {
// Wait 2 seconds to let system settle down
delay(2000);
// Enable task that will read values from the DHT sensor
tasksEnabled = true;
if (tempTaskHandle != NULL) {
vTaskResume(tempTaskHandle);
}
}
yield();
}
void initDisplay(){
display = new Display();
display->initTFT();
display->showWiFiIcon(false);
display->showFirebaseIcon(false);
display->centerMsg("System Init...");
}
void initNetwork(){
void (*ptr)(Network_State_t) = &networkEvent;
network = new Network(ptr);
network->initWiFi();
}
void networkEvent(Network_State_t event){
switch(event){
case NETWORK_CONNECTED:
display->showWiFiIcon(true);
break;
case NETWORK_DISCONNECTED:
display->showWiFiIcon(false);
break;
case FIREBASE_CONNECTED:
display->showFirebaseIcon(true);
break;
case FIREBASE_DISCONNECTED:
display->showFirebaseIcon(false);
break;
default: break;
}
}

View File

@@ -0,0 +1,66 @@
#include "Display.h"
static Display* instance = NULL;
Display::Display(){
instance = this;
tft = new TFT_eSPI();
}
Display::~Display(){
delete tft;
}
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t * bitmap){
if (y >= instance->tft->height()) return 0;
instance->tft->pushImage(x, y, w, h, bitmap);
return 1;
}
void Display::initTFT(){
tft->init();
tft->fillScreen(TFT_BLACK);
tft->setRotation(1);
tft->setTextColor(TFT_WHITE, TFT_BLACK);
tft->setTextDatum(MC_DATUM);
tft->setFreeFont(&Orbitron_Light_24);
TJpgDec.setJpgScale(1);
TJpgDec.setSwapBytes(true);
TJpgDec.setCallback(tft_output);
if(!SPIFFS.begin()){
while(1) yield();
}
}
void Display::centerMsg(String text){
tft->drawString(text, tft->width()/2, 60);
}
void Display::tempUpdates(String temp, String hum, String status){
tft->setTextPadding(tft->width());
tft->drawString(temp, tft->width()/2, 40);
tft->drawString(hum, tft->width()/2, 70);
tft->drawString(status, tft->width()/2, 110);
}
void Display::showWiFiIcon(bool isOn){
tft->fillRect(tft->width() -30, 0, 30, 30, TFT_BLACK);
TJpgDec.drawFsJpg(tft->width() -30, 0, isOn ? "/icon_wifi_on.jpg" : "/icon_wifi_off.jpg");
}
void Display::showFirebaseIcon(bool isOn){
tft->fillRect(tft->width() -60, 0, 30, 30, TFT_BLACK);
TJpgDec.drawFsJpg(tft->width() -60, 0, isOn ? "/icon_firebase_on.jpg" : "/icon_firebase_off.jpg");
}

View File

@@ -0,0 +1,25 @@
#ifndef Display_H_
#define Display_H_
#include <TFT_eSPI.h>
#include <TJpg_Decoder.h>
#include "SPIFFS.h"
class Display {
private:
TFT_eSPI* tft;
friend bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t * bitmap);
public:
Display();
~Display();
void initTFT();
void centerMsg(String text);
void tempUpdates(String temp, String hum, String status);
void showWiFiIcon(bool isOn);
void showFirebaseIcon(bool isOn);
};
#endif

View File

@@ -0,0 +1,102 @@
#include "Network.h"
#include "addons/TokenHelper.h"
#define WIFI_SSID "<YOUR_WIFI_SSID>"
#define WIFI_PASSWORD "<YOUR_WIFI_PASSWORD>"
#define API_KEY "<YOUR_WEB_API_KEY>"
#define FIREBASE_PROJECT_ID "<YOUR_PROJECT_ID>"
#define USER_EMAIL "<YOUR_USER_EMAIL>"
#define USER_PASSWORD "<YOUR_USER_PASSWORD>"
static Network *instance = NULL;
Network::Network(){}
Network::Network(FuncPtrInt f){
instance = this;
callBackEvent = f;
}
void WiFiEventConnected(WiFiEvent_t event, WiFiEventInfo_t info){
Serial.println("WIFI CONNECTED! BUT WAIT FOR THE LOCAL IP ADDR");
}
void WiFiEventGotIP(WiFiEvent_t event, WiFiEventInfo_t info){
Serial.print("LOCAL IP ADDRESS: ");
Serial.println(WiFi.localIP());
instance->callBackEvent(NETWORK_CONNECTED);
instance->firebaseInit();
}
void WiFiEventDisconnected(WiFiEvent_t event, WiFiEventInfo_t info){
Serial.println("WIFI DISCONNECTED!");
instance->callBackEvent(NETWORK_DISCONNECTED);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
void FirestoreTokenStatusCallback(TokenInfo info){
Serial.printf("Token Info: type = %s, status = %s\n", getTokenType(info).c_str(), getTokenStatus(info).c_str());
if(info.status == token_status_ready){
instance->callBackEvent(FIREBASE_CONNECTED);
}else{
instance->callBackEvent(FIREBASE_DISCONNECTED);
}
}
void Network::initWiFi(){
WiFi.disconnect();
WiFi.onEvent(WiFiEventConnected, SYSTEM_EVENT_STA_CONNECTED);
WiFi.onEvent(WiFiEventGotIP, SYSTEM_EVENT_STA_GOT_IP);
WiFi.onEvent(WiFiEventDisconnected, SYSTEM_EVENT_STA_DISCONNECTED);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
void Network::firebaseInit(){
config.api_key = API_KEY;
auth.user.email = USER_EMAIL;
auth.user.password = USER_PASSWORD;
config.token_status_callback = FirestoreTokenStatusCallback;
Firebase.begin(&config, &auth);
}
void Network::firestoreDataUpdate(double temp, double humi){
if(WiFi.status() == WL_CONNECTED && Firebase.ready()){
String documentPath = "House/Room_1";
FirebaseJson content;
content.set("fields/temperature/doubleValue", String(temp).c_str());
content.set("fields/humidity/doubleValue", String(humi).c_str());
if(Firebase.Firestore.patchDocument(&fbdo, FIREBASE_PROJECT_ID, "", documentPath.c_str(), content.raw(), "temperature,humidity")){
Serial.printf("ok\n%s\n\n", fbdo.payload().c_str());
return;
}else{
Serial.println(fbdo.errorReason());
}
if(Firebase.Firestore.createDocument(&fbdo, FIREBASE_PROJECT_ID, "", documentPath.c_str(), content.raw())){
Serial.printf("ok\n%s\n\n", fbdo.payload().c_str());
return;
}else{
Serial.println(fbdo.errorReason());
}
}
}

View File

@@ -0,0 +1,37 @@
#ifndef Network_H_
#define Network_H_
#include <WiFi.h>
#include <Firebase_ESP_Client.h>
typedef enum {
NETWORK_CONNECTED,
NETWORK_DISCONNECTED,
FIREBASE_CONNECTED,
FIREBASE_DISCONNECTED
} Network_State_t;
class Network{
private:
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
typedef void (*FuncPtrInt)(Network_State_t);
void firebaseInit();
friend void WiFiEventConnected(WiFiEvent_t event, WiFiEventInfo_t info);
friend void WiFiEventGotIP(WiFiEvent_t event, WiFiEventInfo_t info);
friend void WiFiEventDisconnected(WiFiEvent_t event, WiFiEventInfo_t info);
friend void FirestoreTokenStatusCallback(TokenInfo info);
public:
FuncPtrInt callBackEvent;
Network();
Network(FuncPtrInt f);
void initWiFi();
void firestoreDataUpdate(double temp, double humi);
};
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -10,6 +10,8 @@ Thank you.
* [Youtube ESP32 Project](https://www.youtube.com/playlist?list=PLnq7JUnBumAyhSBBp95MsQ5-chBAYheZw)
* [ESP32 | Cloud Firestore - Ep 3. Status Icons on Display (The End)][[Video]](https://youtu.be/LR_FgObfuCw)[[Source Code]](https://github.com/0015/ThatProject/tree/master/FIREBASE/Cloud_Firestore_Application/3_Display_Done)
* [ESP32 | Cloud Firestore - Ep 2. Firebase Client For ESP32][[Video]](https://youtu.be/KiF9uGFkA_o)[[Source Code]](https://github.com/0015/ThatProject/tree/master/FIREBASE/Cloud_Firestore_Application/2_Firebase_Client)
* [ESP32 | Cloud Firestore - Ep 1. Firebase Setup][[Video]](https://youtu.be/KV0D8nrsBLg)[[Source Code]](https://github.com/0015/ThatProject/tree/master/FIREBASE/Cloud_Firestore_Application/1_Network_Class)