Last active
November 25, 2019 06:21
-
-
Save Conplug/c7014cf9f4129de6e9c3250286563390 to your computer and use it in GitHub Desktop.
NTP Clock, DHT11, Light Sensor(BH1750), PMS5003T, PMS3003
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Copyright (c) 2019 Conplug (https://conplug.com.tw) | |
// Author: Hartman Hsieh | |
// | |
// Description : | |
// | |
// Building Configurations: | |
// Board : NodeMCU 1.0(ESP-12E Module) | |
// Flash Size : 4M(1M SPIFFS) | |
// | |
// Connections : | |
// "DFRobot Gravity I2C LCD1602" => "IIC0" ( "NMNR_EX V1.0" ) | |
// "Light Sensor BH1750" => "IIC1" ( "NMNR_EX V1.0" ) | |
// "Dust Sensor PMS5003T" => "JD8D7" ( "NMNR_EX V1.0" ) | |
// "DFRobot DHT11 Sensor" => "JD3" ( "NMNR_EX V1.0" ) | |
// "POT_BTN V1.0" => "JA0D4" ( "NMNR_EX V1.0" ) | |
// "Light Meter" => "IIC2" ( "NMNR_EX V1.0" ) | |
// | |
// Required Library : | |
// https://github.com/PaulStoffregen/Time | |
// https://github.com/bearwaterfall/DFRobot_LCD-master | |
// https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library | |
// https://github.com/Conplug/Conplug_UnifiedLcd | |
// https://github.com/beegee-tokyo/DHTesp | |
// https://github.com/Conplug/Conplug_PMS5003T | |
// https://github.com/claws/BH1750 | |
// | |
#define ENABLE_HTTPS_UPDATE 1 | |
#define ENABLE_PMS 1 | |
#define ENABLE_LIGHT_METER_BH1750 1 | |
#include <Wire.h> // IIC bus | |
#include <FS.h> // SPIFFS | |
const float FwVersion = 1.3; | |
// | |
// Wifi SSID and password | |
// | |
const char ssid[] = ""; // your network SSID (name) | |
const char pass[] = ""; // your network password | |
// | |
// DHT pin defines | |
// | |
#define DHTPIN D3 | |
// | |
// Button PIN | |
// | |
const int BUTTON_PIN = D4; | |
// | |
// Potentiometer PIN | |
// | |
const int POT_PIN = A0; | |
// | |
// The file would be saved in SPIFFS. | |
// | |
const String CFG_FILE_NAME = "ntpclock.cfg"; | |
// | |
// IIC LCD Module | |
// | |
#include "Conplug_UnifiedLcd.h" | |
Conplug_UnifiedLcd* Lcd = 0; | |
// | |
// Library for Time function | |
// | |
#include <TimeLib.h> | |
// | |
// ESP8266 Wifi library | |
// | |
#include <ESP8266WiFi.h> | |
#include <ESP8266HTTPClient.h> | |
#include <WiFiClientSecureBearSSL.h> // HTTPS | |
#include <WiFiUdp.h> | |
// NTP Servers: | |
static const char ntpServerName[] = "us.pool.ntp.org"; | |
//static const char ntpServerName[] = "time.nist.gov"; | |
//static const char ntpServerName[] = "time-a.timefreq.bldrdoc.gov"; | |
//static const char ntpServerName[] = "time-b.timefreq.bldrdoc.gov"; | |
//static const char ntpServerName[] = "time-c.timefreq.bldrdoc.gov"; | |
const int timeZone = 8; // Taipei Time | |
//const int timeZone = -5; // Eastern Standard Time (USA) | |
//const int timeZone = -4; // Eastern Daylight Time (USA) | |
//const int timeZone = -8; // Pacific Standard Time (USA) | |
//const int timeZone = -7; // Pacific Daylight Time (USA) | |
WiFiUDP Udp; | |
unsigned int localPort = 8888; // local port to listen for UDP packets | |
// | |
// Function definitions | |
// | |
time_t getNtpTime(); | |
void sendNTPpacket(IPAddress &address); | |
const char* WeekDays[] = {"Sun", "Mon","Tue","Wed","Thu","Fri","Sat"}; // Days Of The Week | |
#if ENABLE_HTTPS_UPDATE == 1 | |
#include <ESP8266httpUpdate.h> | |
#endif // #if ENABLE_HTTPS_UPDATE == 1 | |
// | |
// Include DHT header file. | |
// | |
#include "DHTesp.h" | |
DHTesp Dht; | |
#if ENABLE_LIGHT_METER_BH1750 == 1 | |
#include <BH1750.h> | |
BH1750* LightMeter; | |
#endif // #if ENABLE_LIGHT_METER_BH1750 == 1 | |
#include "SoftwareSerial.h" | |
SoftwareSerial SerialSensor(D7, D8); // RX:D7, TX:D8 | |
#if ENABLE_PMS == 1 | |
// | |
// PMS3003, PMS5003T | |
// | |
#include "Conplug_PMS5003T.h" | |
Conplug_PMS5003T Pms(&SerialSensor); | |
#endif // #if ENABLE_PMS == 1 | |
// | |
// Function definitions | |
// | |
void ICACHE_RAM_ATTR IntCallback(); | |
void DisplayDht11(); | |
void DisplayLight(); | |
void DisplayPms(); | |
// | |
// Global variable | |
// | |
unsigned long PreviousClick = 0; | |
unsigned long PreviousDisplay = 0; | |
unsigned long PreviousDhtReading = 0; | |
unsigned long PreviousLuxReading = 0; | |
unsigned long PreviousPmsReading = 0; | |
// | |
// Sensor values | |
// | |
float h = 0.0; | |
float t = 0.0; | |
float lux = 0.0; | |
uint8_t BuffPms[MAX_PMS_DATA_SIZE]; | |
PMS5003T_DATA* pd; | |
unsigned long PreviousGetFwVer = 0; | |
float FwVerOnServer = 0.0; | |
#define CFG_DATA_FLAG_ACTIVE 1 | |
const int MAX_CFGDATA_DATA_SIZE = 48; // String size is 48-1 | |
struct CFG_DATA { | |
char Type[6]; // Max size is 5 | |
char Data[MAX_CFGDATA_DATA_SIZE]; | |
uint8_t Flag; | |
}; | |
const int MAX_CFGDATA_COUNT = 16; | |
CFG_DATA CfgData[MAX_CFGDATA_COUNT]; | |
const String CFG_DATA_FLAG_DESC[3] = {"", "ACTIVE", "DEL"}; | |
const int GLOBAL_WIFI_SSID_LIST_COUNT = 5; | |
String GlobalWifiSsidList[GLOBAL_WIFI_SSID_LIST_COUNT] = {"","","","",""}; | |
#define SENSOR_INDEX_DHT11 0 | |
#define SENSOR_INDEX_LIGHT 1 | |
#define SENSOR_INDEX_PMS 2 | |
typedef void (*SensorDisplay)(); | |
SensorDisplay SensorDisplayArr[8] = { | |
DisplayDht11, | |
DisplayLight, | |
DisplayPms | |
}; | |
int SensorDetectedArr[8]; | |
int SensorDetectedCount = 0; | |
int PmsErrCount = 0; | |
void setup() | |
{ | |
Wire.begin(); // Join IIC bus | |
pinMode(POT_PIN, INPUT); | |
pinMode(BUTTON_PIN, INPUT); | |
Serial.begin(9600); | |
// | |
// Clear the array memory | |
// | |
memset(&(CfgData[0]), 0, sizeof(CFG_DATA) * MAX_CFGDATA_COUNT); | |
// | |
// Parsing CFG file in FFS and saving in array - CfgData. | |
// | |
ParsingCfg(); | |
// | |
// Initialize LCD module | |
// | |
Lcd = new Conplug_UnifiedLcd(16, 2); | |
Lcd->init(); | |
Lcd->setCursor(0, 0); | |
Lcd->print("conplug.com.tw "); | |
Lcd->setCursor(0, 1); | |
Lcd->print("FW:"); | |
Lcd->print(FwVersion, 1); | |
Lcd->print(" "); | |
// | |
// Add hardcoding SSID setting to array - GlobalWifiSsidList. | |
// | |
int wifi_ssid_index = 0; | |
if((strlen(ssid) > 0) && (strlen(pass) > 0)) { | |
GlobalWifiSsidList[wifi_ssid_index] = ssid; | |
GlobalWifiSsidList[wifi_ssid_index] += ","; | |
GlobalWifiSsidList[wifi_ssid_index] += pass; | |
wifi_ssid_index++; | |
} | |
// | |
// Add SSID settings that is saved in SPIFFS config file. | |
// | |
for(int i = 0; i < MAX_CFGDATA_COUNT; i++) { | |
if(wifi_ssid_index > GLOBAL_WIFI_SSID_LIST_COUNT) break; | |
// | |
// Skip the data that is not active. | |
// | |
if(CfgData[i].Flag != CFG_DATA_FLAG_ACTIVE) | |
continue; | |
String cfg_data_type = CfgData[i].Type; | |
if(cfg_data_type.equalsIgnoreCase("SSID")) { | |
GlobalWifiSsidList[wifi_ssid_index] = CfgData[i].Data; | |
wifi_ssid_index++; | |
} | |
} | |
// | |
// Connect to WIFI APs. | |
// | |
WifiConn(GlobalWifiSsidList, wifi_ssid_index); | |
// | |
// Display current firmware version. | |
// | |
Lcd->setCursor(0, 1); | |
Lcd->print("FW:"); | |
Lcd->print(FwVersion, 1); | |
Lcd->print(" "); | |
// | |
// Sensor initialize and detect | |
// Sensors must be initialized later. | |
// | |
SensorInit(); | |
#if ENABLE_HTTPS_UPDATE == 1 | |
if(digitalRead(BUTTON_PIN) && (analogRead(POT_PIN) >= 512)) { | |
delay(1500); | |
if(digitalRead(BUTTON_PIN) && (analogRead(POT_PIN) >= 512)) { | |
// | |
// Get the newest firmware version on server. | |
// | |
DisplayTypeFwVer(); | |
// | |
// Start flashing. | |
// | |
FlashFirmware(); | |
while(1){delay(60 * 60 * 1000);} | |
} | |
} | |
#endif // #if ENABLE_HTTPS_UPDATE == 1 | |
// | |
// NTP function | |
// | |
Serial.println("Starting UDP"); | |
Udp.begin(localPort); | |
Serial.print("Local port: "); | |
Serial.println(Udp.localPort()); | |
Serial.println("waiting for sync"); | |
setSyncProvider(getNtpTime); // set the external time provider | |
setSyncInterval(36000); // set the number of seconds between re-sync | |
//attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), IntCallback, RISING); | |
} | |
// | |
// Sensor initialize and detect | |
// | |
void SensorInit() { | |
// | |
// Clear the array memory | |
// | |
memset(&(SensorDetectedArr[0]), 0, sizeof(SensorDetectedArr)); | |
// | |
// DHT Sensor initialize and detect | |
// | |
//Dht.setup(DHTPIN, DHTesp::AUTO_DETECT); | |
Dht.setup(DHTPIN, DHTesp::DHT11); | |
//delay(200); | |
h = Dht.getHumidity(); | |
t = Dht.getTemperature(); | |
if (isnan(h) || isnan(t)) | |
SensorDetectedArr[SENSOR_INDEX_DHT11] = 0; | |
else | |
SensorDetectedArr[SENSOR_INDEX_DHT11] = 1; | |
// | |
// Light Sensor initialize and detect | |
// | |
#if ENABLE_LIGHT_METER_BH1750 == 1 | |
Serial.println("LightMeter (BH1750)..."); | |
LightMeter = new BH1750(); | |
//delay(200); | |
if(LightMeter->begin()) | |
SensorDetectedArr[SENSOR_INDEX_LIGHT] = 1; | |
else | |
SensorDetectedArr[SENSOR_INDEX_LIGHT] = 0; | |
#endif // #if ENABLE_LIGHT_METER_BH1750 == 1 | |
// | |
// PMS5003T initialize and detect | |
// | |
#if ENABLE_PMS == 1 | |
Serial.println("Init PMS5003T..."); | |
Pms.begin(); | |
if(Pms.readPms()) { | |
Serial.println("PMSX003 initialize successfully."); | |
SensorDetectedArr[SENSOR_INDEX_PMS] = 1; | |
} | |
else { | |
Serial.println("PMSX003 initialize unsuccessfully."); | |
SensorDetectedArr[SENSOR_INDEX_PMS] = 0; | |
} | |
#endif // #if ENABLE_PMS == 1 | |
// | |
// Calculate the detected sensor count. | |
// | |
for(int i = 0; i < (sizeof(SensorDetectedArr) / sizeof(int)); i++) { | |
if(SensorDetectedArr[i]) SensorDetectedCount++; | |
Serial.print("SensorDetectedArr["); | |
Serial.print(i); | |
Serial.print("]="); | |
Serial.println(SensorDetectedArr[i]); | |
} | |
} | |
void loop() | |
{ | |
int DisplayIndex = 0; | |
int PotValue = analogRead(POT_PIN); | |
// | |
// Detect PMS5003T or PMS3003 again. | |
// | |
if((millis() < (20 * 1000)) && (SensorDetectedArr[SENSOR_INDEX_PMS] == 0)) { | |
// | |
// Detect PMS again. | |
// | |
if(Pms.readPms()) | |
SensorDetectedArr[SENSOR_INDEX_PMS] = 1; | |
// | |
// Calculate the detected sensor count. | |
// | |
SensorDetectedCount = 0; | |
for(int i = 0; i < (sizeof(SensorDetectedArr) / sizeof(int)); i++) { | |
if(SensorDetectedArr[i]) SensorDetectedCount++; | |
Serial.print("SensorDetectedArr["); | |
Serial.print(i); | |
Serial.print("]="); | |
Serial.println(SensorDetectedArr[i]); | |
} | |
} | |
if(PotValue < ((1024 / (3 + SensorDetectedCount)) * (DisplayIndex + 1))) { | |
DisplayType1(); | |
} | |
DisplayIndex++; | |
if((PotValue >= ((1024 / (3 + SensorDetectedCount)) * DisplayIndex)) && (PotValue < ((1024 / (3 + SensorDetectedCount)) * (DisplayIndex + 1)))) { | |
DisplayType2(); | |
} | |
DisplayIndex++; | |
// | |
// Display detected sensor values. | |
// | |
for(int i = 0; i < (sizeof(SensorDetectedArr) / sizeof(int)); i++) { | |
if(SensorDetectedArr[i]) { | |
if((PotValue >= ((1024 / (3 + SensorDetectedCount)) * DisplayIndex)) && (PotValue < ((1024 / (3 + SensorDetectedCount)) * (DisplayIndex + 1)))) { | |
// | |
// Execute the sensor display function. | |
// | |
SensorDisplayArr[i](); | |
} | |
DisplayIndex++; | |
} | |
} | |
if(PotValue >= ((1024 / (3 + SensorDetectedCount)) * DisplayIndex)) { | |
DisplayTypeFwVer(); | |
} | |
DisplayIndex++; | |
// | |
// Read command from user. | |
// | |
if (Serial.available()) { | |
char line_buff[64] = {0}; | |
int ci = 0; | |
memset(&(line_buff[0]), 0, 64); // Clear the data. | |
while(Serial.available()) { | |
char c = Serial.read(); | |
if(c == ';') { // reach command ending char - ';' | |
line_buff[ci] = 0; | |
String str_temp = line_buff; | |
Serial.print("INPUT LENGTH = "); | |
Serial.println(ci); | |
ParsingCmd(&str_temp); | |
ci = 0; | |
break; | |
} | |
else if(c == '\r') {} // Skip char - CR('\r')(0x0d) | |
else if(c == '\n') {} // Skip char - LF('\n')(0x0a) | |
else { | |
line_buff[ci] = c; | |
Serial.print(line_buff[ci], HEX); | |
Serial.print(" "); | |
ci++; | |
} | |
delay(30); | |
} | |
} | |
} | |
void ICACHE_RAM_ATTR IntCallback(){ | |
unsigned long MillisTemp = millis(); | |
if ((MillisTemp - PreviousClick) > 500) { // Debouncing | |
Serial.println("Button Click"); | |
PreviousClick = MillisTemp; | |
} | |
} | |
// | |
// Display Date, Time, Humidity and Temperature | |
// | |
void DisplayType1() | |
{ | |
boolean pms_read_ok = false; | |
if ((millis() - PreviousDhtReading) > (Dht.getMinimumSamplingPeriod() * 2)) { | |
// | |
// Reading temperature or humidity takes about 250 milliseconds! | |
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) | |
// | |
h = Dht.getHumidity(); | |
// | |
// Read temperature as Celsius (the default) | |
// | |
t = Dht.getTemperature(); | |
// | |
// Check if any reads failed and exit early (to try again). | |
// | |
if (isnan(h) || isnan(t)) { | |
//Serial.println("Failed to read from DHT sensor!"); | |
} | |
else | |
SensorDetectedArr[SENSOR_INDEX_DHT11] = 1; | |
PreviousDhtReading = millis(); | |
} | |
// | |
// Print YEAR-MONTH-DAY | |
// | |
Lcd->setCursor(0, 0); | |
Lcd->print(year()); | |
Lcd->print("-"); | |
PrintLcdDigits(month()); | |
Lcd->print("-"); | |
PrintLcdDigits(day()); | |
Lcd->print(" "); | |
Lcd->setCursor(16 - 5, 0); // Align right | |
if(SensorDetectedArr[SENSOR_INDEX_DHT11]) { | |
Lcd->printf("%2.1f", t); | |
Lcd->print("C"); | |
} | |
else if(SensorDetectedArr[SENSOR_INDEX_PMS]) { | |
#if ENABLE_PMS == 1 | |
PMS5003T_DATA* pd = 0; | |
// | |
// Running readPms before running pm2_5, temp, humi and readDeviceType. | |
// | |
if(pd = Pms.readPms()) { | |
pms_read_ok = true; | |
if(Pms.pm2_5() < 16) | |
Lcd->printf("%5s", "GOOD"); | |
else if((Pms.pm2_5() >= 16) && (Pms.pm2_5() < 35)) | |
Lcd->printf("%5s", "FAIR"); | |
else | |
Lcd->printf("%5s", "POOR"); | |
} | |
else { | |
PmsErrCount++; | |
} | |
#endif // #if ENABLE_PMS == 1 | |
} | |
else { | |
Lcd->print(" "); | |
} | |
// | |
// Print HOUR:MIN:SEC WEEK | |
// | |
Lcd->setCursor(0, 1); | |
PrintLcdDigits(hour()); | |
Lcd->print(":"); | |
PrintLcdDigits(minute()); | |
Lcd->print(":"); | |
PrintLcdDigits(second()); | |
Lcd->print(" "); | |
Lcd->print(WeekDays[weekday() - 1]); | |
Lcd->print(" "); | |
Lcd->setCursor(16 - 3, 1); // Align right | |
if(SensorDetectedArr[SENSOR_INDEX_DHT11]) { | |
Lcd->printf("%2d", (int)h); | |
Lcd->print("%"); | |
} | |
else if(SensorDetectedArr[SENSOR_INDEX_PMS]) { | |
#if ENABLE_PMS == 1 | |
// | |
// Check the sign characters. | |
// | |
if(pms_read_ok && (Pms.readDeviceType() == Conplug_PMS5003T::PMS5003T)) { | |
Lcd->printf("%2d", (int)(Pms.temp())); | |
Lcd->print("C"); | |
} | |
#endif // #if ENABLE_PMS == 1 | |
} | |
else { | |
Lcd->print(" "); | |
} | |
} | |
// | |
// Display Date and Time | |
// | |
void DisplayType2() | |
{ | |
// | |
// Print YEAR-MONTH-DAY | |
// | |
Lcd->setCursor(0, 0); | |
Lcd->print(" "); | |
Lcd->print(year()); | |
Lcd->print("-"); | |
PrintLcdDigits(month()); | |
Lcd->print("-"); | |
PrintLcdDigits(day()); | |
Lcd->print(" "); | |
// | |
// Print HOUR:MIN:SEC WEEK | |
// | |
Lcd->setCursor(0, 1); | |
Lcd->print(" "); | |
PrintLcdDigits(hour()); | |
Lcd->print(":"); | |
PrintLcdDigits(minute()); | |
Lcd->print(":"); | |
PrintLcdDigits(second()); | |
Lcd->print(" "); | |
Lcd->print(WeekDays[weekday() - 1]); | |
Lcd->print(" "); | |
} | |
// | |
// Display Humidity and Temperature | |
// | |
void DisplayDht11() | |
{ | |
if ((millis() - PreviousDhtReading) > (Dht.getMinimumSamplingPeriod() * 2)) { | |
// | |
// Reading temperature or humidity takes about 250 milliseconds! | |
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) | |
// | |
h = Dht.getHumidity(); | |
// | |
// Read temperature as Celsius (the default) | |
// | |
t = Dht.getTemperature(); | |
// | |
// Check if any reads failed and exit early (to try again). | |
// | |
if (isnan(h) || isnan(t)) { | |
//Serial.println("Failed to read from DHT sensor!"); | |
} | |
PreviousDhtReading = millis(); | |
} | |
Lcd->setCursor(0, 0); | |
Lcd->print("Temp: "); | |
Lcd->print(t, 1); | |
Lcd->print("C"); | |
Lcd->setCursor(0, 1); | |
Lcd->print("Humidity: "); | |
Lcd->print(h, 0); | |
Lcd->print("%"); | |
} | |
// | |
// Display | |
// | |
void DisplayLight() | |
{ | |
#if ENABLE_LIGHT_METER_BH1750 == 1 | |
if ((millis() - PreviousLuxReading) > 500) { | |
lux = LightMeter->readLightLevel(); | |
PreviousLuxReading = millis(); | |
} | |
#endif // #if ENABLE_LIGHT_METER_BH1750 == 1 | |
// | |
// Print HOUR:MIN:SEC WEEK | |
// | |
Lcd->setCursor(0, 0); | |
Lcd->print(" "); | |
PrintLcdDigits(hour()); | |
Lcd->print(":"); | |
PrintLcdDigits(minute()); | |
Lcd->print(":"); | |
PrintLcdDigits(second()); | |
Lcd->print(" "); | |
Lcd->print(WeekDays[weekday() - 1]); | |
Lcd->print(" "); | |
Lcd->setCursor(0, 1); | |
Lcd->print("Light:"); | |
Lcd->printf("%7d LX", (int)lux); // Align right | |
} | |
// | |
// Display PMS5003T(PM2.5, Humidity and Temperature) | |
// | |
void DisplayPms() | |
{ | |
#if ENABLE_PMS == 1 | |
if((millis() - PreviousPmsReading) > 1500) { // 1.5 second | |
PMS5003T_DATA* pd = 0; | |
// | |
// Running readPms before running pm2_5, temp, humi and readDeviceType. | |
// | |
if(pd = Pms.readPms()) { | |
Lcd->setCursor(0, 0); | |
Lcd->print("PM2.5: "); | |
Lcd->print(Pms.pm2_5()); | |
Lcd->print("ug/m3 "); | |
if(Pms.readDeviceType() == Conplug_PMS5003T::PMS5003T) { | |
Lcd->setCursor(0, 1); | |
Lcd->print((float)(Pms.temp()), 1); | |
Lcd->print("C "); | |
Lcd->print((float)(Pms.humi()), 1); | |
Lcd->print("% "); | |
} | |
else { | |
Lcd->setCursor(0, 1); | |
Lcd->print(" "); | |
} | |
} | |
else { | |
Serial.println("PMS data format is wrong."); | |
Serial.println(Pms.LastErr); | |
PmsErrCount++; | |
} | |
//Lcd->setCursor(13, 1); | |
//Lcd->printf("%3d", PmsErrCount); | |
PreviousPmsReading = millis(); | |
} | |
#endif // #if ENABLE_PMS == 1 | |
} | |
// | |
// Display the newest firmware version on server. | |
// | |
void DisplayTypeFwVer() { | |
if(FwVerOnServer == 0.0) { | |
Lcd->setCursor(0, 0); | |
Lcd->print("Checking... "); | |
Lcd->setCursor(0, 1); | |
Lcd->print(" "); | |
if((PreviousGetFwVer == 0) || ((millis() - PreviousGetFwVer) > (60 * 60 * 1000))) { // 60 minutes | |
// | |
// Connect to server to get the newest firmware version. | |
// | |
std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure); | |
client->setInsecure(); | |
HTTPClient https; | |
if (https.begin(*client, "https://conplug.com.tw/iot/fw.php")) { // HTTPS | |
Serial.println("[HTTPS] GET..."); | |
int httpCode = https.GET(); | |
// httpCode will be negative on error | |
if (httpCode > 0) { | |
// HTTP header has been send and Server response header has been handled | |
Serial.printf("[HTTPS] GET... code: %d\n", httpCode); | |
// file found at server? | |
if (httpCode == HTTP_CODE_OK) { | |
String payload = https.getString(); | |
Serial.print("The newest firmware version on server is "); | |
Serial.println(payload); | |
const char* c = payload.c_str(); | |
if(isDigit(c[0]) && (c[1] == '.') && isDigit(c[2]) && (payload.length() == 3)) { // Check the result is a float. | |
FwVerOnServer = payload.toFloat(); | |
} | |
} | |
} | |
else { | |
Serial.printf("[HTTPS] GET... failed, error: %s\n\r", https.errorToString(httpCode).c_str()); | |
} | |
https.end(); | |
} | |
else { | |
Serial.printf("[HTTPS] Unable to connect\n\r"); | |
} | |
PreviousGetFwVer = millis(); | |
} | |
} | |
else { | |
Lcd->setCursor(0, 0); | |
Lcd->print("Newest Firmware "); | |
Lcd->setCursor(0, 1); | |
Lcd->print("On Server : "); | |
Lcd->print(FwVerOnServer, 1); | |
Lcd->print(" "); | |
} | |
} | |
int WifiConn(const String* WifiSsidList, const int Count) { | |
// | |
// Try to connect to WIFI APs in WifiSsidList. | |
// | |
for(int i = 0; i < Count; i++) { | |
// | |
// The string format should be: | |
// ssid_pass = "ssid,pass" | |
// | |
String ssid_pass = WifiSsidList[i]; | |
int pos = ssid_pass.indexOf(","); | |
if(pos == -1) continue; // format error, skip. | |
String tmp_ssid = ssid_pass.substring(0, pos); | |
tmp_ssid.trim(); | |
String tmp_pass = ssid_pass.substring(pos + 1); // +1 to skip char - ',' | |
tmp_pass.trim(); | |
Serial.print("Connecting to "); | |
Serial.print(tmp_ssid); | |
Serial.print(","); | |
Serial.println(tmp_pass); | |
WiFi.begin(tmp_ssid, tmp_pass); | |
Lcd->setCursor(0, 1); | |
Lcd->print(tmp_ssid); | |
Lcd->print(" "); | |
// | |
// Waiting for WIFI connection | |
// | |
unsigned long WifiConnectingTimeout = millis(); | |
while (WiFi.status() != WL_CONNECTED) { | |
// | |
// Waiting for 15 seconds | |
// | |
if((millis() - WifiConnectingTimeout) > 15000) { // 15 seconds | |
break; | |
} | |
delay(500); | |
Lcd->setCursor(14, 1); | |
Lcd->print((int)((millis() - WifiConnectingTimeout) / 1000)); | |
} | |
if(WiFi.status() == WL_CONNECTED) | |
break; | |
} | |
Serial.print("IP number assigned by DHCP is "); | |
Serial.println(WiFi.localIP()); | |
if(WiFi.status() == WL_CONNECTED) | |
return 0; // Success | |
else | |
return 2; // Failure | |
} | |
// | |
// CFG File Format: | |
// SSID=SSID,PASSWORD | |
// DVTP=1 | |
// | |
void ParsingCfg() { | |
String file_name = "/" + CFG_FILE_NAME; | |
SPIFFS.begin(); | |
// open the file for reading: | |
File cfg_file = SPIFFS.open(file_name, "r"); | |
char line_buff[64] = {0}; | |
int index = 0; | |
memset(&(line_buff[0]), 0, 64); // Clear the data. | |
if (cfg_file) { | |
Serial.println(file_name); | |
index = 0; | |
// read from the file until there's nothing else in it: | |
while (cfg_file.available()) { | |
char c = cfg_file.read(); | |
if (c == '\r') { | |
} | |
else if (c == '\n') { | |
line_buff[index] = 0; | |
ParsingCfgLine(line_buff); | |
//Serial.println(line_buff); | |
index = 0; | |
} | |
else { | |
line_buff[index] = c; | |
index++; | |
//Serial.write(line_buff[index]); | |
} | |
} | |
if (index != 0) { | |
line_buff[index] = 0; | |
ParsingCfgLine(line_buff); | |
//Serial.println(line_buff); | |
} | |
// close the file: | |
cfg_file.close(); | |
} | |
else { | |
// | |
// if the file didn't open, print an error: | |
// | |
Serial.print("error opening "); | |
Serial.println(file_name); | |
for(int i = 0; i < MAX_CFGDATA_COUNT; i++) { | |
if(CfgData[i].Type[0] == 0) { // empty | |
// | |
// Create a cfg file and add one line - "DVTP=2". | |
// | |
strcpy(CfgData[i].Type, "DVTP"); | |
strcpy(CfgData[i].Data, "2"); | |
CfgData[i].Flag = CFG_DATA_FLAG_ACTIVE; | |
SaveCfgFile(); | |
break; | |
} | |
} | |
} | |
} | |
void ParsingCfgLine(const char* Line) { | |
ParsingCfgLine(Line, -1); | |
} | |
// | |
// CFG Line Format: | |
// SSID=SSID,PASSWORD | |
// | |
// if(Index == -1) Add a new CFG data | |
// else Modify the data of CfgData[Index] | |
// | |
void ParsingCfgLine(const char* Line, int Index) { | |
String line_temp = Line; | |
int target_index = -1; | |
// | |
// Find out the target index in CfgData array. | |
// | |
if(Index == -1) { // Add a new CFG data | |
for(int i = 0; i < MAX_CFGDATA_COUNT; i++) { | |
if(CfgData[i].Type[0] == 0) { // Found an empty data. | |
target_index = i; | |
break; | |
} | |
} | |
} | |
else { // Modify one CFG data | |
if(Index < MAX_CFGDATA_COUNT) { // in range | |
if(CfgData[Index].Type[0] != 0) { // not empty | |
target_index = Index; | |
} | |
} | |
} | |
if(target_index != -1) { | |
int pos = line_temp.indexOf("="); | |
String str_temp = line_temp.substring(0, pos); | |
str_temp.trim(); | |
//Serial.print(str_temp); | |
//Serial.print(" "); | |
strncpy(CfgData[target_index].Type, str_temp.c_str(), 5); // max size is 5 | |
str_temp = line_temp.substring(pos + 1); // +1 is for skipping '=' | |
str_temp.trim(); | |
//Serial.println(str_temp); | |
strncpy(CfgData[target_index].Data, str_temp.c_str(), MAX_CFGDATA_DATA_SIZE - 1); // max string size is MAX_CFGDATA_DATA_SIZE - 1 | |
CfgData[target_index].Flag = CFG_DATA_FLAG_ACTIVE; | |
} | |
} | |
// | |
// Save array - CfgData to SPIFFS config file. | |
// | |
void SaveCfgFile () { | |
// | |
// The path in FFS | |
// | |
String file_name = "/" + CFG_FILE_NAME; | |
File cfg_file = SPIFFS.open(file_name, "w+"); | |
if (cfg_file) { | |
for(int i = 0; i < MAX_CFGDATA_COUNT; i++) { | |
// | |
// Skip the data that is not active. | |
// | |
if(CfgData[i].Flag != CFG_DATA_FLAG_ACTIVE) | |
continue; | |
cfg_file.print(CfgData[i].Type); | |
cfg_file.print("="); | |
cfg_file.println(CfgData[i].Data); | |
} | |
// | |
// close the file | |
// | |
cfg_file.close(); | |
} | |
else { | |
// | |
// if the file didn't open, print an error: | |
// | |
Serial.print("error opening "); | |
Serial.println(file_name); | |
// | |
// close the file: | |
// | |
cfg_file.close(); | |
} | |
} | |
// | |
// Parsing command that received from serial port. | |
// | |
void ParsingCmd(String* Line) { | |
Line->trim(); | |
String str_temp = ""; | |
int first_space_index = Line->indexOf(" "); | |
Serial.print("COMMAND : "); | |
Serial.println(*Line); | |
if(first_space_index == -1) { | |
if (Line->equalsIgnoreCase("ls")) { | |
Serial.println("List data in config file."); | |
for(int i = 0; i < MAX_CFGDATA_COUNT; i++) { | |
if(CfgData[i].Type[0] != 0) { | |
Serial.print("("); | |
Serial.print(i); | |
Serial.print(")\t"); | |
Serial.print(CfgData[i].Type); | |
Serial.print("\t"); | |
Serial.print(CfgData[i].Data); | |
Serial.print("\t"); | |
Serial.println(CFG_DATA_FLAG_DESC[CfgData[i].Flag]); | |
} | |
} | |
} | |
} | |
else { | |
// | |
// Command line format: | |
// add SSID=NAME,PASS | |
// | +----------CfgData array data | |
// +---------------CfgData array type | |
// | |
String str_cmd = Line->substring(0, first_space_index); | |
if (str_cmd.equalsIgnoreCase("add")) { | |
Serial.println("Add data to config file."); | |
int pos = indexOfNot(Line, ' ', first_space_index); | |
str_temp = Line->substring(pos); | |
str_temp.trim(); | |
ParsingCfgLine(str_temp.c_str()); // Add the cfg data. | |
SaveCfgFile(); // Save to FFS | |
} | |
// | |
// Command line format: | |
// del 2 | |
// +-----------------CfgData array index | |
// | |
else if (str_cmd.equalsIgnoreCase("del")) { | |
Serial.println("Delete data in config file."); | |
int pos = indexOfNot(Line, ' ', first_space_index); | |
str_temp = Line->substring(pos); | |
str_temp.trim(); | |
if(isValidNumber(str_temp)) { | |
int cfg_data_index = str_temp.toInt(); | |
if(cfg_data_index < MAX_CFGDATA_COUNT) { | |
Serial.print("DELETE ("); | |
Serial.print(cfg_data_index); | |
Serial.println(")"); | |
memset(&(CfgData[cfg_data_index]), 0, sizeof(CFG_DATA)); // Clear the data. | |
SaveCfgFile(); // Save to FFS | |
} | |
} | |
} | |
// | |
// Command line format: | |
// mod 2 SSID=NAME,PASS | |
// | | +----------CfgData array data | |
// | +---------------CfgData array type | |
// +-----------------CfgData array index | |
// | |
else if (str_cmd.equalsIgnoreCase("mod")) { | |
Serial.println("Modify data in config file."); | |
int pos = indexOfNot(Line, ' ', first_space_index); | |
int pos2 = Line->indexOf(" ", pos); | |
str_temp = Line->substring(pos, pos2); // Grab "CfgData array index" in command line | |
str_temp.trim(); | |
if(isValidNumber(str_temp)) { // Check the "CfgData array index" is a number | |
int cfg_data_index = str_temp.toInt(); // Transform it to int | |
if(cfg_data_index < MAX_CFGDATA_COUNT) { | |
for(int i = 0; i < MAX_CFGDATA_COUNT; i++) { | |
if((i == cfg_data_index) && (CfgData[i].Type[0] != 0)) { | |
pos = indexOfNot(Line, ' ', pos2); | |
str_temp = Line->substring(pos); // Grab "CfgData array type and data" in command line | |
Serial.print("MODIFY ("); | |
Serial.print(i); | |
Serial.print(") TO "); | |
Serial.println(str_temp); | |
ParsingCfgLine(str_temp.c_str(), i); // modify it | |
SaveCfgFile(); // Save to FFS | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
// | |
// Find the first char index that doesn't equal to val. | |
// | |
int indexOfNot(String* str, char val, int from) { | |
const char* c = str->c_str(); | |
int pos = from; | |
for(pos = from; pos < str->length(); pos++) { | |
if(c[pos] != val) break; | |
} | |
if(pos == str->length()) | |
return -1; | |
else | |
return pos; | |
} | |
boolean isValidNumber(String str) | |
{ | |
boolean isNum=false; | |
if(!(str.charAt(0) == '+' || str.charAt(0) == '-' || isDigit(str.charAt(0)))) return false; | |
for(byte i=1;i<str.length();i++) | |
{ | |
if(!(isDigit(str.charAt(i)) || str.charAt(i) == '.')) return false; | |
} | |
return true; | |
} | |
#if ENABLE_HTTPS_UPDATE == 1 | |
void FlashFirmware() { | |
String err_msg = ""; | |
if(FwVerOnServer != 0.0) { | |
std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure); | |
client->setInsecure(); // For HTTPS | |
Lcd->setCursor(0, 0); | |
Lcd->print("Flashing... "); | |
Lcd->setCursor(0, 1); | |
Lcd->print(" "); | |
// | |
// The line below is optional. It can be used to blink the LED on the board during flashing | |
// The LED will be on during download of one buffer of data from the network. The LED will | |
// be off during writing that buffer to flash | |
// On a good connection the LED should flash regularly. On a bad connection the LED will be | |
// on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second | |
// value is used to put the LED on. If the LED is on with HIGH, that value should be passed | |
// | |
ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW); | |
String url = "https://conplug.com.tw/iot/bin/ntpclock_"; | |
url += String(FwVerOnServer, 1); | |
url += ".bin"; | |
Serial.println("Download firmware for flashing."); | |
Serial.println(url); | |
t_httpUpdate_return ret = ESPhttpUpdate.update(*client, url); | |
switch (ret) { | |
case HTTP_UPDATE_FAILED: | |
err_msg = ESPhttpUpdate.getLastErrorString(); | |
Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), err_msg.c_str()); | |
Lcd->setCursor(0, 0); | |
Lcd->print("ERROR:"); | |
Lcd->print(err_msg.substring(0, 10)); | |
Lcd->print(" "); | |
Lcd->setCursor(0, 1); | |
Lcd->print(err_msg.substring(10)); | |
Lcd->print(" "); | |
break; | |
case HTTP_UPDATE_NO_UPDATES: | |
Serial.println("HTTP_UPDATE_NO_UPDATES"); | |
break; | |
case HTTP_UPDATE_OK: | |
Serial.println("HTTP_UPDATE_OK"); | |
break; | |
} | |
} | |
} | |
#endif // #if ENABLE_HTTPS_UPDATE == 1 | |
void PrintLcdDigits(int digits) | |
{ | |
if (digits < 10) | |
Lcd->print('0'); | |
Lcd->print(digits); | |
} | |
/*-------- NTP code ----------*/ | |
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message | |
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets | |
time_t getNtpTime() | |
{ | |
IPAddress ntpServerIP; // NTP server's ip address | |
for (int i = 0; i < 6; i++) { // Retry 6 times if "No NTP Response" | |
while (Udp.parsePacket() > 0) ; // discard any previously received packets | |
Serial.println("Transmit NTP Request"); | |
// get a random server from the pool | |
WiFi.hostByName(ntpServerName, ntpServerIP); | |
Serial.print(ntpServerName); | |
Serial.print(": "); | |
Serial.println(ntpServerIP); | |
sendNTPpacket(ntpServerIP); | |
uint32_t beginWait = millis(); | |
while (millis() - beginWait < 1500) { | |
int size = Udp.parsePacket(); | |
if (size >= NTP_PACKET_SIZE) { | |
Serial.println("Receive NTP Response"); | |
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer | |
unsigned long secsSince1900; | |
// convert four bytes starting at location 40 to a long integer | |
secsSince1900 = (unsigned long)packetBuffer[40] << 24; | |
secsSince1900 |= (unsigned long)packetBuffer[41] << 16; | |
secsSince1900 |= (unsigned long)packetBuffer[42] << 8; | |
secsSince1900 |= (unsigned long)packetBuffer[43]; | |
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR; | |
} | |
} | |
Serial.println("No NTP Response :-("); | |
delay(500); | |
} | |
return 0; // return 0 if unable to get the time | |
} | |
// send an NTP request to the time server at the given address | |
void sendNTPpacket(IPAddress &address) | |
{ | |
// set all bytes in the buffer to 0 | |
memset(packetBuffer, 0, NTP_PACKET_SIZE); | |
// Initialize values needed to form NTP request | |
// (see URL above for details on the packets) | |
packetBuffer[0] = 0b11100011; // LI, Version, Mode | |
packetBuffer[1] = 0; // Stratum, or type of clock | |
packetBuffer[2] = 6; // Polling Interval | |
packetBuffer[3] = 0xEC; // Peer Clock Precision | |
// 8 bytes of zero for Root Delay & Root Dispersion | |
packetBuffer[12] = 49; | |
packetBuffer[13] = 0x4E; | |
packetBuffer[14] = 49; | |
packetBuffer[15] = 52; | |
// all NTP fields have been given values, now | |
// you can send a packet requesting a timestamp: | |
Udp.beginPacket(address, 123); //NTP requests are to port 123 | |
Udp.write(packetBuffer, NTP_PACKET_SIZE); | |
Udp.endPacket(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment