Browse Source

arduino-esp32 original code April 2022

master
TeZ 2 years ago
parent
commit
32dcfd3684
  1. 134
      Arduino-ESP/Pylon-MQTTeZ-Broker/Pylon-MQTTeZ-Broker.ino
  2. 818
      Arduino-ESP/PylontechMonitoring/PylontechMonitoring.ino
  3. 52
      Arduino-ESP/PylontechMonitoring/README.md
  4. BIN
      Arduino-ESP/PylontechMonitoring/Schematics.png
  5. 113
      Arduino-ESP/PylontechMonitoring/libraries/Misc/circular_buffer.h
  6. 96
      Arduino-ESP/PylontechMonitoring/libraries/Misc/circular_log.h
  7. 81
      Arduino-ESP/PylontechMonitoring/libraries/NtpTime/ntp_time.h
  8. 2
      Arduino-ESP/PylontechMonitoring/libraries/SimpleTimer/README
  9. 250
      Arduino-ESP/PylontechMonitoring/libraries/SimpleTimer/SimpleTimer.cpp
  10. 126
      Arduino-ESP/PylontechMonitoring/libraries/SimpleTimer/SimpleTimer.h
  11. 97
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/DateStrings.cpp
  12. 131
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/Readme.txt
  13. 321
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/Time.cpp
  14. 1
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/Time.h
  15. 144
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/TimeLib.h
  16. 78
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/Processing/SyncArduinoClock/SyncArduinoClock.pde
  17. 9
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/Processing/SyncArduinoClock/readme.txt
  18. 71
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeArduinoDue/TimeArduinoDue.ino
  19. 87
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeGPS/TimeGPS.ino
  20. 135
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeNTP/TimeNTP.ino
  21. 156
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeNTP_ESP8266WiFi/TimeNTP_ESP8266WiFi.ino
  22. 55
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeRTC/TimeRTC.ino
  23. 107
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeRTCLog/TimeRTCLog.ino
  24. 80
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeRTCSet/TimeRTCSet.ino
  25. 81
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeSerial/TimeSerial.ino
  26. 108
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeSerialDateStrings/TimeSerialDateStrings.ino
  27. 78
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeTeensy3/TimeTeensy3.ino
  28. 34
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/keywords.txt
  29. 22
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/library.json
  30. 10
      Arduino-ESP/PylontechMonitoring/libraries/Time-master/library.properties
  31. 103
      Arduino-ESP/Pylontez-MQTT/CACert.ino
  32. 898
      Arduino-ESP/Pylontez-MQTT/Pylontez-MQTT.ino

134
Arduino-ESP/Pylon-MQTTeZ-Broker/Pylon-MQTTeZ-Broker.ino

@ -0,0 +1,134 @@
// MQTT server + client for ESP32
#include"sMQTTBroker.h"
#include <WiFi.h>
#include <PubSubClient.h>
#include <WiFiClientSecure.h>
#include <MQTT.h>
const char* ssid = "NOS-3B26"; // Setubal
const char* password = "RMKSX2GL"; // Setubal
//const char* ssid = "MEO-AA9030"; // Andre
//const char* password = "81070ce635"; // Andre
// Set your Static IP address
IPAddress local_IP(192, 168, 1, 123);
// Set your Gateway IP address
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 0, 0);
IPAddress primaryDNS(8, 8, 8, 8); // optional
IPAddress secondaryDNS(8, 8, 4, 4); // optional
sMQTTBroker broker;
WiFiClient net;
//WiFiClientSecure net;
MQTTClient mqtt_client(1024);
//WiFiClient espClient;
//PubSubClient client(espClient);
//long lastMsg = 0;
//char msg[50];
//int value = 0;
unsigned long lastMillis = 0;
//////////////////////////////////
void WiFiconnect(){
int nn = 0;
while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
delay(500);
if(nn<9){
Serial.print(".");
nn++;
}else{
nn=0;
Serial.println(".");
}
}
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
Serial.println("STA Failed to configure");
}
Serial.println("Connection established!");
Serial.print("IP address:\t");
Serial.println(WiFi.localIP());
// Serial.print("\nconnecting...");
//// while (!mqtt_client.connect("bsidebotham", "", "")) {
// while (!mqtt_client.connect("tezmqlientx2323", "", "")) {
// Serial.print(".");
// delay(1000);
// }
//
// Serial.println("\nconnected!");
//
// mqtt_client.subscribe("/hello");
//
// // MQTT brokers usually use port 8883 for secure connections.
//// client.begin("broker.shiftr.io", 8883, net);
// mqtt_client.begin("192.168.1.123", 1883, net);
// mqtt_client.onMessage(messageReceived);
}
//////////////////////////////
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
//////////////////////////////
void setup()
{
Serial.begin(115200);
// const char* ssid = "MEO-AA9030"; // The SSID (name) of the Wi-Fi network you want to connect to
// const char* password = "81070ce635"; // The password of the Wi-Fi network
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
const unsigned short mqttPort=1883;
broker.init(mqttPort);
// all done
WiFiconnect();
}
//////////////////////////////
void loop()
{
broker.update();
// mqtt_client.loop();
delay(10); // <- fixes some issues with WiFi stability
// publish a message roughly every second.
// if (millis() - lastMillis > 1000) {
// if (!mqtt_client.connected()) {
// Serial.print("lastError: ");
// Serial.println(mqtt_client.lastError());
// WiFiconnect();
// }
// lastMillis = millis();
// int rnum = random(1,23);
// mqtt_client.publish("/hello", "ciccio" + String(rnum));
// }
//
}

818
Arduino-ESP/PylontechMonitoring/PylontechMonitoring.ino

@ -0,0 +1,818 @@
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>
#include <ESP8266WebServer.h>
#include <SimpleTimer.h>
#include <TimeLib.h> //https://github.com/PaulStoffregen/Time
#include <ntp_time.h>
#include <circular_log.h>
//IMPORTANT: Specify your WIFI settings:
//#define WIFI_SSID "NOS-3B26"
//#define WIFI_PASS "RMKSX2GL"
#define WIFI_SSID "MEO-AA9030"
#define WIFI_PASS "81070ce635"
//IMPORTANT: Uncomment this line if you want to enable MQTT (and fill correct MQTT_ values below):
//#define ENABLE_MQTT
#ifdef ENABLE_MQTT
//NOTE 1: if you want to change what is pushed via MQTT - edit function: pushBatteryDataToMqtt.
//NOTE 2: MQTT_TOPIC_ROOT is where battery will push MQTT topics. For example "soc" will be pushed to: "home/grid_battery/soc"
#define MQTT_SERVER "192.168.1.123"
#define MQTT_PORT 1883
#define MQTT_USER ""
#define MQTT_PASSWORD ""
#define MQTT_TOPIC_ROOT "home/grid_battery/" //this is where mqtt data will be pushed
#define MQTT_PUSH_FREQ_SEC 2 //maximum mqtt update frequency in seconds
#include <PubSubClient.h>
WiFiClient espClient;
PubSubClient mqttClient(espClient);
#endif //ENABLE_MQTT
char g_szRecvBuff[7000];
IPAddress thisip;
ESP8266WebServer server(80);
SimpleTimer timer;
circular_log<7000> g_log;
bool ntpTimeReceived = false;
int g_baudRate = 0;
void Log(const char* msg)
{
g_log.Log(msg);
}
/////////////////////////////////
void goWiFi(){
// connect to WiFi
WiFi.mode(WIFI_STA);
WiFi.persistent(false); //our credentials are hardcoded, so we don't need ESP saving those each boot (will save on flash wear)
WiFi.hostname("PylonBattery");
Serial.println();
Serial.print("connecting to ");
Serial.println(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("");
for(int i=1; i<=3; i++){
LedBlink();
}
}
//////////////////////////////////////////////////
void LedBlink(){
digitalWrite(LED_BUILTIN, LOW);
delay(150);
digitalWrite(LED_BUILTIN, HIGH); // high = off
delay(150);
}
//////////////////////
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);//high is off
// connect to WiFi
goWiFi();
// // original wifi code
// WiFi.mode(WIFI_STA);
// WiFi.persistent(false); //our credentialss are hardcoded, so we don't need ESP saving those each boot (will save on flash wear)
// WiFi.hostname("PylonBattery");
// WiFi.begin(WIFI_SSID, WIFI_PASS);
//
// for(int ix=0; ix<10; ix++)
// {
// if(WiFi.status() == WL_CONNECTED)
// {
// break;
// }
//
// delay(1000);
// }
// Serial.println("");
// Serial.println("WiFi connected");
// Serial.println("IP address: ");
// thisip = WiFi.localIP();
// Serial.println( thisip );
ArduinoOTA.setHostname("AndrePylon");
ArduinoOTA.begin();
server.on("/", handleRoot);
server.on("/log", handleLog);
server.on("/req", handleReq);
server.on("/jsonOut", handleJsonOut);
server.on("/reboot", [](){
ESP.restart();
});
server.begin();
syncTime();
#ifdef ENABLE_MQTT
mqttClient.setServer(MQTT_SERVER, MQTT_PORT);
#endif
Log("Boot event");
}
void handleLog()
{
server.send(200, "text/html", g_log.c_str());
}
void switchBaud(int newRate)
{
if(g_baudRate == newRate)
{
return;
}
if(g_baudRate != 0)
{
Serial.flush();
delay(20);
Serial.end();
delay(20);
}
char szMsg[50];
snprintf(szMsg, sizeof(szMsg)-1, "New baud: %d", newRate);
Log(szMsg);
Serial.begin(newRate);
g_baudRate = newRate;
delay(20);
}
void waitForSerial()
{
for(int ix=0; ix<150;ix++)
{
if(Serial.available()) break;
delay(10);
}
}
int readFromSerial()
{
memset(g_szRecvBuff, 0, sizeof(g_szRecvBuff));
int recvBuffLen = 0;
bool foundTerminator = true;
waitForSerial();
while(Serial.available())
{
char szResponse[256] = "";
const int readNow = Serial.readBytesUntil('>', szResponse, sizeof(szResponse)-1); //all commands terminate with "$$\r\n\rpylon>" (no new line at the end)
if(readNow > 0 &&
szResponse[0] != '\0')
{
if(readNow + recvBuffLen + 1 >= (int)(sizeof(g_szRecvBuff)))
{
Log("WARNING: Read too much data on the console!");
break;
}
strcat(g_szRecvBuff, szResponse);
recvBuffLen += readNow;
if(strstr(g_szRecvBuff, "$$\r\n\rpylon"))
{
strcat(g_szRecvBuff, ">"); //readBytesUntil will skip this, so re-add
foundTerminator = true;
break; //found end of the string
}
if(strstr(g_szRecvBuff, "Press [Enter] to be continued,other key to exit"))
{
//we need to send new line character so battery continues the output
Serial.write("\r");
}
waitForSerial();
}
}
if(recvBuffLen > 0 )
{
if(foundTerminator == false)
{
Log("Failed to find pylon> terminator");
}
}
return recvBuffLen;
}
bool readFromSerialAndSendResponse()
{
const int recvBuffLen = readFromSerial();
if(recvBuffLen > 0)
{
server.sendContent(g_szRecvBuff);
return true;
}
return false;
}
bool sendCommandAndReadSerialResponse(const char* pszCommand)
{
switchBaud(115200);
if(pszCommand[0] != '\0')
{
Serial.write(pszCommand);
}
Serial.write("\n");
const int recvBuffLen = readFromSerial();
if(recvBuffLen > 0)
{
return true;
}
//wake up console and try again:
wakeUpConsole();
if(pszCommand[0] != '\0')
{
Serial.write(pszCommand);
}
Serial.write("\n");
return readFromSerial() > 0;
}
void handleReq()
{
bool respOK;
if(server.hasArg("code") == false)
{
respOK = sendCommandAndReadSerialResponse("");
}
else
{
respOK = sendCommandAndReadSerialResponse(server.arg("code").c_str());
}
if(respOK)
{
server.send(200, "text/plain", g_szRecvBuff);
}
else
{
server.send(500, "text/plain", "????");
}
}
void handleJsonOut()
{
if(sendCommandAndReadSerialResponse("pwr") == false)
{
server.send(500, "text/plain", "Failed to get response to 'pwr' command");
return;
}
parsePwrResponse(g_szRecvBuff);
prepareJsonOutput(g_szRecvBuff, sizeof(g_szRecvBuff));
server.send(200, "application/json", g_szRecvBuff);
}
void handleRoot() {
unsigned long days = 0, hours = 0, minutes = 0;
unsigned long val = os_getCurrentTimeSec();
days = val / (3600*24);
val -= days * (3600*24);
hours = val / 3600;
val -= hours * 3600;
minutes = val / 60;
val -= minutes*60;
static char szTmp[2500] = "";
snprintf(szTmp, sizeof(szTmp)-1, "<html><b>Garage Battery</b><br>Time GMT: %d/%02d/%02d %02d:%02d:%02d (%s)<br>Uptime: %02d:%02d:%02d.%02d<br><br>free heap: %u<br>Wifi RSSI: %d<BR>Wifi SSID: %s",
year(), month(), day(), hour(), minute(), second(), "GMT",
(int)days, (int)hours, (int)minutes, (int)val,
ESP.getFreeHeap(), WiFi.RSSI(), WiFi.SSID().c_str());
strncat(szTmp, "<BR><a href='/log'>Runtime log</a><HR>", sizeof(szTmp)-1);
strncat(szTmp, "<form action='/req' method='get'>Command:<input type='text' name='code'/><input type='submit'></form><a href='/req?code=pwr'>Power</a> | <a href='/req?code=help'>Help</a> | <a href='/req?code=log'>Event Log</a> | <a href='/req?code=time'>Time</a>", sizeof(szTmp)-1);
strncat(szTmp, "</html>", sizeof(szTmp)-1);
server.send(200, "text/html", szTmp);
}
unsigned long os_getCurrentTimeSec()
{
static unsigned int wrapCnt = 0;
static unsigned long lastVal = 0;
unsigned long currentVal = millis();
if(currentVal < lastVal)
{
wrapCnt++;
}
lastVal = currentVal;
unsigned long seconds = currentVal/1000;
//millis will wrap each 50 days, as we are interested only in seconds, let's keep the wrap counter
return (wrapCnt*4294967) + seconds;
}
void syncTime()
{
//get time from NTP
time_t currentTimeGMT = getNtpTime();
if(currentTimeGMT)
{
ntpTimeReceived = true;
setTime(currentTimeGMT);
}
else
{
timer.setTimeout(5000, syncTime); //try again in 5 seconds
}
}
void wakeUpConsole()
{
switchBaud(1200);
//byte wakeUpBuff[] = {0x7E, 0x32, 0x30, 0x30, 0x31, 0x34, 0x36, 0x38, 0x32, 0x43, 0x30, 0x30, 0x34, 0x38, 0x35, 0x32, 0x30, 0x46, 0x43, 0x43, 0x33, 0x0D};
//Serial.write(wakeUpBuff, sizeof(wakeUpBuff));
Serial.write("~20014682C0048520FCC3\r");
delay(1000);
byte newLineBuff[] = {0x0E, 0x0A};
switchBaud(115200);
for(int ix=0; ix<10; ix++)
{
Serial.write(newLineBuff, sizeof(newLineBuff));
delay(1000);
if(Serial.available())
{
while(Serial.available())
{
Serial.read();
}
break;
}
}
}
#define MAX_PYLON_BATTERIES 8
struct pylonBattery
{
bool isPresent;
long soc; //Coulomb in %
long voltage; //in mW
long current; //in mA, negative value is discharge
long tempr; //temp of case or BMS?
long cellTempLow;
long cellTempHigh;
long cellVoltLow;
long cellVoltHigh;
char baseState[9]; //Charge | Dischg | Idle
char voltageState[9]; //Normal
char currentState[9]; //Normal
char tempState[9]; //Normal
char time[20]; //2019-06-08 04:00:29
char b_v_st[9]; //Normal (battery voltage?)
char b_t_st[9]; //Normal (battery temperature?)
bool isCharging() const { return strcmp(baseState, "Charge") == 0; }
bool isDischarging() const { return strcmp(baseState, "Dischg") == 0; }
bool isIdle() const { return strcmp(baseState, "Idle") == 0; }
bool isBalancing() const { return strcmp(baseState, "Balance") == 0; }
bool isNormal() const
{
if(isCharging() == false &&
isDischarging() == false &&
isIdle() == false &&
isBalancing() == false)
{
return false; //base state looks wrong!
}
return strcmp(voltageState, "Normal") == 0 &&
strcmp(currentState, "Normal") == 0 &&
strcmp(tempState, "Normal") == 0 &&
strcmp(b_v_st, "Normal") == 0 &&
strcmp(b_t_st, "Normal") == 0 ;
}
};
struct batteryStack
{
int batteryCount;
int soc; //in %, if charging: average SOC, otherwise: lowest SOC
int temp; //in mC, if highest temp is > 15C, this will show the highest temp, otherwise the lowest
long currentDC; //mAh current going in or out of the battery
long avgVoltage; //in mV
char baseState[9]; //Charge | Dischg | Idle | Balance | Alarm!
pylonBattery batts[MAX_PYLON_BATTERIES];
bool isNormal() const
{
for(int ix=0; ix<MAX_PYLON_BATTERIES; ix++)
{
if(batts[ix].isPresent &&
batts[ix].isNormal() == false)
{
return false;
}
}
return true;
}
//in wH
long getPowerDC() const
{
return (long)(((double)currentDC/1000.0)*((double)avgVoltage/1000.0));
}
//wH estimated current on AC side (taking into account Sofar ME3000SP losses)
long getEstPowerAc() const
{
double powerDC = (double)getPowerDC();
if(powerDC == 0)
{
return 0;
}
else if(powerDC < 0)
{
//we are discharging, on AC side we will see less power due to losses
if(powerDC < -1000)
{
return (long)(powerDC*0.94);
}
else if(powerDC < -600)
{
return (long)(powerDC*0.90);
}
else
{
return (long)(powerDC*0.87);
}
}
else
{
//we are charging, on AC side we will have more power due to losses
if(powerDC > 1000)
{
return (long)(powerDC*1.06);
}
else if(powerDC > 600)
{
return (long)(powerDC*1.1);
}
else
{
return (long)(powerDC*1.13);
}
}
}
};
batteryStack g_stack;
long extractInt(const char* pStr, int pos)
{
return atol(pStr+pos);
}
void extractStr(const char* pStr, int pos, char* strOut, int strOutSize)
{
strOut[strOutSize-1] = '\0';
strncpy(strOut, pStr+pos, strOutSize-1);
strOutSize--;
//trim right
while(strOutSize > 0)
{
if(isspace(strOut[strOutSize-1]))
{
strOut[strOutSize-1] = '\0';
}
else
{
break;
}
strOutSize--;
}
}
/* Output has mixed \r and \r\n
pwr
@
Power Volt Curr Tempr Tlow Thigh Vlow Vhigh Base.St Volt.St Curr.St Temp.St Coulomb Time B.V.St B.T.St
1 49735 -1440 22000 19000 19000 3315 3317 Dischg Normal Normal Normal 93% 2019-06-08 04:00:30 Normal Normal
....
8 - - - - - - - Absent - - - - - - -
Command completed successfully
$$
pylon
*/
bool parsePwrResponse(const char* pStr)
{
if(strstr(pStr, "Command completed successfully") == NULL)
{
return false;
}
int chargeCnt = 0;
int dischargeCnt = 0;
int idleCnt = 0;
int alarmCnt = 0;
int socAvg = 0;
int socLow = 0;
int tempHigh = 0;
int tempLow = 0;
memset(&g_stack, 0, sizeof(g_stack));
for(int ix=0; ix<MAX_PYLON_BATTERIES; ix++)
{
char szToFind[32] = "";
snprintf(szToFind, sizeof(szToFind)-1, "\r\r\n%d ", ix+1);
const char* pLineStart = strstr(pStr, szToFind);
if(pLineStart == NULL)
{
return false;
}
pLineStart += 3; //move past \r\r\n
extractStr(pLineStart, 55, g_stack.batts[ix].baseState, sizeof(g_stack.batts[ix].baseState));
if(strcmp(g_stack.batts[ix].baseState, "Absent") == 0)
{
g_stack.batts[ix].isPresent = false;
}
else
{
g_stack.batts[ix].isPresent = true;
extractStr(pLineStart, 64, g_stack.batts[ix].voltageState, sizeof(g_stack.batts[ix].voltageState));
extractStr(pLineStart, 73, g_stack.batts[ix].currentState, sizeof(g_stack.batts[ix].currentState));
extractStr(pLineStart, 82, g_stack.batts[ix].tempState, sizeof(g_stack.batts[ix].tempState));
extractStr(pLineStart, 100, g_stack.batts[ix].time, sizeof(g_stack.batts[ix].time));
extractStr(pLineStart, 121, g_stack.batts[ix].b_v_st, sizeof(g_stack.batts[ix].b_v_st));
extractStr(pLineStart, 130, g_stack.batts[ix].b_t_st, sizeof(g_stack.batts[ix].b_t_st));
g_stack.batts[ix].voltage = extractInt(pLineStart, 6);
g_stack.batts[ix].current = extractInt(pLineStart, 13);
g_stack.batts[ix].tempr = extractInt(pLineStart, 20);
g_stack.batts[ix].cellTempLow = extractInt(pLineStart, 27);
g_stack.batts[ix].cellTempHigh = extractInt(pLineStart, 34);
g_stack.batts[ix].cellVoltLow = extractInt(pLineStart, 41);
g_stack.batts[ix].cellVoltHigh = extractInt(pLineStart, 48);
g_stack.batts[ix].soc = extractInt(pLineStart, 91);
//////////////////////////////// Post-process ////////////////////////
g_stack.batteryCount++;
g_stack.currentDC += g_stack.batts[ix].current;
g_stack.avgVoltage += g_stack.batts[ix].voltage;
socAvg += g_stack.batts[ix].soc;
if(g_stack.batts[ix].isNormal() == false){ alarmCnt++; }
else if(g_stack.batts[ix].isCharging()){chargeCnt++;}
else if(g_stack.batts[ix].isDischarging()){dischargeCnt++;}
else if(g_stack.batts[ix].isIdle()){idleCnt++;}
else{ alarmCnt++; } //should not really happen!
if(g_stack.batteryCount == 1)
{
socLow = g_stack.batts[ix].soc;
tempLow = g_stack.batts[ix].cellTempLow;
tempHigh = g_stack.batts[ix].cellTempHigh;
}
else
{
if(socLow > g_stack.batts[ix].soc){socLow = g_stack.batts[ix].soc;}
if(tempHigh < g_stack.batts[ix].cellTempHigh){tempHigh = g_stack.batts[ix].cellTempHigh;}
if(tempLow > g_stack.batts[ix].cellTempLow){tempLow = g_stack.batts[ix].cellTempLow;}
}
}
}
//now update stack state:
g_stack.avgVoltage /= g_stack.batteryCount;
g_stack.soc = socLow;
if(tempHigh > 15000) //15C
{
g_stack.temp = tempHigh; //in the summer we highlight the warmest cell
}
else
{
g_stack.temp = tempLow; //in the winter we focus on coldest cell
}
if(alarmCnt > 0)
{
strcpy(g_stack.baseState, "Alarm!");
}
else if(chargeCnt == g_stack.batteryCount)
{
strcpy(g_stack.baseState, "Charge");
g_stack.soc = (int)(socAvg / g_stack.batteryCount);
}
else if(dischargeCnt == g_stack.batteryCount)
{
strcpy(g_stack.baseState, "Dischg");
}
else if(idleCnt == g_stack.batteryCount)
{
strcpy(g_stack.baseState, "Idle");
}
else
{
strcpy(g_stack.baseState, "Balance");
}
return true;
}
void prepareJsonOutput(char* pBuff, int buffSize)
{
memset(pBuff, 0, buffSize);
snprintf(pBuff, buffSize-1, "{\"soc\": %d, \"temp\": %d, \"currentDC\": %ld, \"avgVoltage\": %ld, \"baseState\": \"%s\", \"batteryCount\": %d, \"powerDC\": %ld, \"estPowerAC\": %ld, \"isNormal\": %s}", g_stack.soc,
g_stack.temp,
g_stack.currentDC,
g_stack.avgVoltage,
g_stack.baseState,
g_stack.batteryCount,
g_stack.getPowerDC(),
g_stack.getEstPowerAc(),
g_stack.isNormal() ? "true" : "false");
}
void loop() {
#ifdef ENABLE_MQTT
mqttLoop();
#endif
ArduinoOTA.handle();
server.handleClient();
timer.run();
//if there are bytes availbe on serial here - it's unexpected
//when we send a command to battery, we read whole response
//if we get anything here anyways - we will log it
int bytesAv = Serial.available();
if(bytesAv > 0)
{
if(bytesAv > 63)
{
bytesAv = 63;
}
char buff[64+4] = "RCV:";
if(Serial.readBytes(buff+4, bytesAv) > 0)
{
digitalWrite(LED_BUILTIN, LOW);
delay(5);
digitalWrite(LED_BUILTIN, HIGH);//high is off
Log(buff);
}
}
}
#ifdef ENABLE_MQTT
#define ABS_DIFF(a, b) (a > b ? a-b : b-a)
void mqtt_publish_f(const char* topic, float newValue, float oldValue, float minDiff, bool force)
{
char szTmp[16] = "";
snprintf(szTmp, 15, "%.2f", newValue);
if(force || ABS_DIFF(newValue, oldValue) > minDiff)
{
mqttClient.publish(topic, szTmp, false);
}
}
void mqtt_publish_i(const char* topic, int newValue, int oldValue, int minDiff, bool force)
{
char szTmp[16] = "";
snprintf(szTmp, 15, "%d", newValue);
if(force || ABS_DIFF(newValue, oldValue) > minDiff)
{
mqttClient.publish(topic, szTmp, false);
}
}
void mqtt_publish_s(const char* topic, const char* newValue, const char* oldValue, bool force)
{
if(force || strcmp(newValue, oldValue) != 0)
{
mqttClient.publish(topic, newValue, false);
}
}
void pushBatteryDataToMqtt(const batteryStack& lastSentData, bool forceUpdate /* if true - we will send all data regardless if it's the same */)
{
mqtt_publish_f(MQTT_TOPIC_ROOT "soc", g_stack.soc, lastSentData.soc, 0, forceUpdate);
mqtt_publish_f(MQTT_TOPIC_ROOT "temp", (float)g_stack.temp/1000.0, (float)lastSentData.temp/1000.0, 0, forceUpdate);
mqtt_publish_i(MQTT_TOPIC_ROOT "estPowerAC", g_stack.getEstPowerAc(), lastSentData.getEstPowerAc(), 10, forceUpdate);
mqtt_publish_i(MQTT_TOPIC_ROOT "battery_count",g_stack.batteryCount, lastSentData.batteryCount, 0, forceUpdate);
mqtt_publish_s(MQTT_TOPIC_ROOT "base_state", g_stack.baseState, lastSentData.baseState , forceUpdate);
mqtt_publish_i(MQTT_TOPIC_ROOT "is_normal", g_stack.isNormal() ? 1:0, lastSentData.isNormal() ? 1:0, 0, forceUpdate);
}
void mqttLoop()
{
//if we have problems with connecting to mqtt server, we will attempt to re-estabish connection each 1minute (not more than that)
static unsigned long g_lastConnectionAttempt = 0;
//first: let's make sure we are connected to mqtt
const char* topicLastWill = MQTT_TOPIC_ROOT "availability";
if (!mqttClient.connected() && (g_lastConnectionAttempt == 0 || os_getCurrentTimeSec() - g_lastConnectionAttempt > 60)) {
if(mqttClient.connect("GarageBattery", MQTT_USER, MQTT_PASSWORD, topicLastWill, 1, true, "offline"))
{
Log("Connected to MQTT server: " MQTT_SERVER);
mqttClient.publish(topicLastWill, "online", true);
}
else
{
Log("Failed to connect to MQTT server.");
}
g_lastConnectionAttempt = os_getCurrentTimeSec();
}
//next: read data from battery and send via MQTT (but only once per MQTT_PUSH_FREQ_SEC seconds)
static unsigned long g_lastDataSent = 0;
if(mqttClient.connected() &&
os_getCurrentTimeSec() - g_lastDataSent > MQTT_PUSH_FREQ_SEC &&
sendCommandAndReadSerialResponse("pwr") == true)
{
static batteryStack lastSentData; //this is the last state we sent to MQTT, used to prevent sending the same data over and over again
static unsigned int callCnt = 0;
parsePwrResponse(g_szRecvBuff);
bool forceUpdate = (callCnt % 20 == 0); //push all the data every 20th call
pushBatteryDataToMqtt(lastSentData, forceUpdate);
callCnt++;
g_lastDataSent = os_getCurrentTimeSec();
memcpy(&lastSentData, &g_stack, sizeof(batteryStack));
}
mqttClient.loop();
}
#endif //ENABLE_MQTT

52
Arduino-ESP/PylontechMonitoring/README.md

@ -0,0 +1,52 @@
# Pylontech Battery Monitoring via WiFi
This project allows you to control and monitor Pylontech US2000B and US2000C batteries via console port over WiFi.
It it's a great starting point to integrate battery with your home automation.
**I ACCEPT NO RESPONSIBILTY FOR ANY DAMAGE CAUSED, PROCEED AT YOUR OWN RISK**
# Features:
* Low cost (around 20$ in total).
* Adds WiFi capability to your Pylontech US2000B/C battery.
* Device exposes web interface that allows to:
* send console commands and read response over WiFi (no PC needed)
* battery information can be retrevied also in JSON format for easy parsing
* MQTT support:
* device pushes basic battery data like SOC, temperature, state, etc to selected MQTT server
* Easy to modify code using Arduino IDE and flash new firmware over WiFi (no need to disconnect from the battery).
See the project in action on [Youtube](https://youtu.be/7VyQjKU3MsU):</br>
<a href="http://www.youtube.com/watch?feature=player_embedded&v=7VyQjKU3MsU" target="_blank"><img src="http://img.youtube.com/vi/7VyQjKU3MsU/0.jpg" alt="See the project in action on YouTube" width="240" height="180" border="10" /></a>
# Parts needed and schematics:
* [Wemos D1 mini microcontroller](https://www.amazon.co.uk/Makerfire-NodeMcu-Development-ESP8266-Compatible/dp/B071S8MWTY/).
* [SparkFun MAX3232 Transceiver](https://www.sparkfun.com/products/11189).
* US2000B: Cable with RJ10 connector (some RJ10 cables have only two wires, make sure to buy one that has all four wires present).
* US2000C: Cable with RJ45 connector (see below for more details).
* Capacitors C1: 10uF, C2: 0.1uF (this is not strictly required, but recommended as Wemos D1 can have large current spikes).
![Schematics](Schemetics.png)
# US2000C notes:
This battery uses RJ45 cable instead of RJ10. Schematics is the same only plug differs:
* RJ45 Pin 3 (white-green) = R1IN
* RJ45 Pin 6 (green) = T1OUT
* RJ45 Pin 8 (brown) = GND
![image](https://user-images.githubusercontent.com/19826327/146428324-29e3f9bf-6cc3-415c-9d60-fa5ee3d65613.png)
# How to get going:
* Get Wemos D1 mini
* Install arduino IDE and ESP8266 libraries as [described here](https://averagemaker.com/2018/03/wemos-d1-mini-setup.html)
* Open [PylontechMonitoring.ino](PylontechMonitoring.ino) in arduino IDE
* Make sure to copy content of [libraries subdirectory](libraries) to [libraries of your Arduino IDE](https://forum.arduino.cc/index.php?topic=88380.0).
* Specify your WiFi login and password at the top of the file (line 13-14)
* If you want MQTT support, uncomment line 17 and fill details in lines 21-24
* Upload project to your device
* Connect Wemos D1 mini to the MAX3232 transreceiver
* Connect transreceiver to RJ10/RJ45 as descibed in the schematics (all three lines need to be connected)
* Connect RJ10/RJ45 to the serial port of the Pylontech US2000 battery. If you have multiple batteries - connect to the master one.
* Connect Wemos D1 to the power via USB
* Find what IP address was assigned to your Wemos by your router and open it in the web-browser
* You should be able now to connunicate with the battery via WiFi

BIN
Arduino-ESP/PylontechMonitoring/Schematics.png

After

Width: 1200  |  Height: 921  |  Size: 504 KiB

113
Arduino-ESP/PylontechMonitoring/libraries/Misc/circular_buffer.h

@ -0,0 +1,113 @@
#ifndef circ_buffer_h
#define circ_buffer_h
#include <stdlib.h>
/// <summary>
/// This class allows a fixed size circular buffer.
/// When push_back is called, oldest data is overwritten.
/// Does not use any dynamic allocators.
/// </summary>
template <class ItemType, int elementCnt> class circular_buffer
{
private:
ItemType m_arr[elementCnt];
int m_writePos;
int m_size;
void advanceWritePos()
{
if(m_size < elementCnt)
{
m_size++;
}
m_writePos++;
if(m_writePos >= elementCnt)
{
m_writePos = 0;
}
}
circular_buffer(const circular_buffer<ItemType, elementCnt>& rhs);
public:
circular_buffer()
{
clear();
}
void operator=(const circular_buffer<ItemType, elementCnt>& rhs)
{
memcpy(m_arr, rhs.m_arr, sizeof(m_arr));
m_size = rhs.m_size;
m_writePos = rhs.m_writePos;
}
void push_back(const ItemType& item)
{
m_arr[m_writePos] = item;
advanceWritePos();
}
void clear()
{
memset(m_arr,0,sizeof(m_arr));
m_size = m_writePos = 0;
}
void sort()
{
if (size() < 2)
return;
bool swapped;
do
{
swapped = false;
for(int ix=0; ix<size()-1; ix++)
{
if(at(ix) > at(ix+1))
{
ItemType tmp = at(ix);
at(ix) = at(ix+1);
at(ix+1) = tmp;
swapped = true;
}
}
}while(swapped);
}
int size() const { return m_size; }
bool isFull() const { return size() == elementCnt; }
ItemType& operator[](int pos) {return at(pos);}
ItemType& at(int pos)
{
if(m_size < elementCnt)
{
return m_arr[pos];
}
int readPos = m_writePos + pos;
if(readPos >= elementCnt)
{
readPos -= elementCnt;
}
return m_arr[readPos];
}
#if _DEBUG_ENABLED
void print()
{
printf("---\n");
for(int i=0; i<size(); i++)
{
printf("%d = %d\n", i, at(i));
}
}
#endif
};
#endif //circ_buffer_h

96
Arduino-ESP/PylontechMonitoring/libraries/Misc/circular_log.h

@ -0,0 +1,96 @@
#ifndef circ_log_h
#define circ_log_h
#include <TimeLib.h> //https://github.com/PaulStoffregen/Time
#include <stdlib.h>
template <int elementCnt> class circular_log
{
private:
char m_log[elementCnt];
bool removeLastFromLog()
{
char* nextLine = strstr(m_log+1, "<BR>");
if(nextLine == NULL)
{
return false;
}
int newLineLen = strlen(nextLine);
memmove(m_log, nextLine, newLineLen);
m_log[newLineLen] = '\0';
return true;
}
public:
circular_log()
{
memset(m_log, 0, sizeof(m_log));
}
const char* c_str() const { return m_log; }
int freeSpace() const { return elementCnt - strlen(m_log) - 1; }
void LogXml(const char* msg)
{
char szNew[256] = "";
snprintf(szNew, sizeof(szNew)-1, "<BR>%02d - %02d:%02d | ", day(), hour(), minute());
int ix = strlen(szNew);
while(*msg != '\0' && ix < 250)
{
if(*msg == '<')
{
szNew[ix++] = '&';
szNew[ix++] = 'l';
szNew[ix++] = 't';
szNew[ix++] = ';';
}
else if(*msg == '>')
{
szNew[ix++] = '&';
szNew[ix++] = 'g';
szNew[ix++] = 't';
szNew[ix++] = ';';
}
else
{
szNew[ix++] = *msg;
}
msg++;
}
const int newLen = strlen(szNew);
while(freeSpace() < newLen)
{
if(removeLastFromLog() == false)
{
return;
}
}
strcat(m_log, szNew);
}
void Log(const char* msg)
{
char szNew[256] = "";
snprintf(szNew, sizeof(szNew)-1, "<BR>%02d - %02d:%02d | %s", day(), hour(), minute(), msg);
const int newLen = strlen(szNew);
while(freeSpace() < newLen)
{
if(removeLastFromLog() == false)
{
return;
}
}
strcat(m_log, szNew);
}
};
#endif //circular_log_h

81
Arduino-ESP/PylontechMonitoring/libraries/NtpTime/ntp_time.h

@ -0,0 +1,81 @@
#ifndef ntp_time_h
#define ntp_time_h
#include <WiFiUdp.h>
// NTP Servers:
static const char ntpServerName[] = "0.uk.pool.ntp.org";
const int timeZone = 0;
unsigned int localPort = 8888; // local port to listen for UDP packets
/*-------- 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
WiFiUDP udpNtp;
// 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:
udpNtp.beginPacket(address, 123); //NTP requests are to port 123
udpNtp.write(packetBuffer, NTP_PACKET_SIZE);
udpNtp.endPacket();
}
time_t getNtpTime()
{
if(WiFi.status() != WL_CONNECTED)
{
return 0;
}
static bool udpStarted = false;
if(udpStarted == false)
{
udpStarted = true;
udpNtp.begin(localPort);
}
IPAddress ntpServerIP; // NTP server's ip address
while (udpNtp.parsePacket() > 0) ; // discard any previously received packets
// get a random server from the pool
WiFi.hostByName(ntpServerName, ntpServerIP);
sendNTPpacket(ntpServerIP);
delay(100);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = udpNtp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
udpNtp.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;
}
delay(10);
}
return 0; // return 0 if unable to get the time
}
#endif //ntp_time_h

2
Arduino-ESP/PylontechMonitoring/libraries/SimpleTimer/README

@ -0,0 +1,2 @@
Visit this page for more information:
http://playground.arduino.cc/Code/SimpleTimer

250
Arduino-ESP/PylontechMonitoring/libraries/SimpleTimer/SimpleTimer.cpp

@ -0,0 +1,250 @@
/*
* SimpleTimer.cpp
*
* SimpleTimer - A timer library for Arduino.
* Author: mromani@ottotecnica.com
* Copyright (c) 2010 OTTOTECNICA Italy
*
* This library is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software
* Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser
* General Public License along with this library; if not,
* write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "SimpleTimer.h"
// Select time function:
//static inline unsigned long elapsed() { return micros(); }
static inline unsigned long elapsed() { return millis(); }
SimpleTimer::SimpleTimer()
: numTimers (-1)
{
}
void SimpleTimer::init() {
unsigned long current_millis = elapsed();
for (int i = 0; i < MAX_TIMERS; i++) {
enabled[i] = false;
callbacks[i] = 0; // if the callback pointer is zero, the slot is free, i.e. doesn't "contain" any timer
prev_millis[i] = current_millis;
numRuns[i] = 0;
}
numTimers = 0;
}
void SimpleTimer::run() {
int i;
unsigned long current_millis;
// get current time
current_millis = elapsed();
for (i = 0; i < MAX_TIMERS; i++) {
toBeCalled[i] = DEFCALL_DONTRUN;
// no callback == no timer, i.e. jump over empty slots
if (callbacks[i]) {
// is it time to process this timer ?
// see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592
if (current_millis - prev_millis[i] >= delays[i]) {
// update time
//prev_millis[i] = current_millis;
prev_millis[i] += delays[i];
// check if the timer callback has to be executed
if (enabled[i]) {
// "run forever" timers must always be executed
if (maxNumRuns[i] == RUN_FOREVER) {
toBeCalled[i] = DEFCALL_RUNONLY;
}
// other timers get executed the specified number of times
else if (numRuns[i] < maxNumRuns[i]) {
toBeCalled[i] = DEFCALL_RUNONLY;
numRuns[i]++;
// after the last run, delete the timer
if (numRuns[i] >= maxNumRuns[i]) {
toBeCalled[i] = DEFCALL_RUNANDDEL;
}
}
}
}
}
}
for (i = 0; i < MAX_TIMERS; i++) {
switch(toBeCalled[i]) {
case DEFCALL_DONTRUN:
break;
case DEFCALL_RUNONLY:
(*callbacks[i])();
break;
case DEFCALL_RUNANDDEL:
(*callbacks[i])();
deleteTimer(i);
break;
}
}
}
// find the first available slot
// return -1 if none found
int SimpleTimer::findFirstFreeSlot() {
int i;
// all slots are used
if (numTimers >= MAX_TIMERS) {
return -1;
}
// return the first slot with no callback (i.e. free)
for (i = 0; i < MAX_TIMERS; i++) {
if (callbacks[i] == 0) {
return i;
}
}
// no free slots found
return -1;
}
int SimpleTimer::setTimer(long d, timer_callback f, int n) {
int freeTimer;
if (numTimers < 0) {
init();
}
freeTimer = findFirstFreeSlot();
if (freeTimer < 0) {
return -1;
}
if (f == NULL) {
return -1;
}
delays[freeTimer] = d;
callbacks[freeTimer] = f;
maxNumRuns[freeTimer] = n;
enabled[freeTimer] = true;
prev_millis[freeTimer] = elapsed();
numTimers++;
return freeTimer;
}
int SimpleTimer::setInterval(long d, timer_callback f) {
return setTimer(d, f, RUN_FOREVER);
}
int SimpleTimer::setTimeout(long d, timer_callback f) {
return setTimer(d, f, RUN_ONCE);
}
void SimpleTimer::deleteTimer(int timerId) {
if (timerId >= MAX_TIMERS) {
return;
}
// nothing to delete if no timers are in use
if (numTimers == 0) {
return;
}
// don't decrease the number of timers if the
// specified slot is already empty
if (callbacks[timerId] != NULL) {
callbacks[timerId] = 0;
enabled[timerId] = false;
toBeCalled[timerId] = DEFCALL_DONTRUN;
delays[timerId] = 0;
numRuns[timerId] = 0;
// update number of timers
numTimers--;
}
}
// function contributed by code@rowansimms.com
void SimpleTimer::restartTimer(int numTimer) {
if (numTimer >= MAX_TIMERS) {
return;
}
prev_millis[numTimer] = elapsed();
}
boolean SimpleTimer::isEnabled(int numTimer) {
if (numTimer >= MAX_TIMERS) {
return false;
}
return enabled[numTimer];
}
void SimpleTimer::enable(int numTimer) {
if (numTimer >= MAX_TIMERS) {
return;
}
enabled[numTimer] = true;
}
void SimpleTimer::disable(int numTimer) {
if (numTimer >= MAX_TIMERS) {
return;
}
enabled[numTimer] = false;
}
void SimpleTimer::toggle(int numTimer) {
if (numTimer >= MAX_TIMERS) {
return;
}
enabled[numTimer] = !enabled[numTimer];
}
int SimpleTimer::getNumTimers() {
return numTimers;
}

126
Arduino-ESP/PylontechMonitoring/libraries/SimpleTimer/SimpleTimer.h

@ -0,0 +1,126 @@
/*
* SimpleTimer.h
*
* SimpleTimer - A timer library for Arduino.
* Author: mromani@ottotecnica.com
* Copyright (c) 2010 OTTOTECNICA Italy
*
* This library is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software
* Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser
* General Public License along with this library; if not,
* write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef SIMPLETIMER_H
#define SIMPLETIMER_H
#if defined(ARDUINO) && ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
typedef void (*timer_callback)(void);
class SimpleTimer {
public:
// maximum number of timers
const static int MAX_TIMERS = 10;
// setTimer() constants
const static int RUN_FOREVER = 0;
const static int RUN_ONCE = 1;
// constructor
SimpleTimer();
void init();
// this function must be called inside loop()
void run();
// call function f every d milliseconds
int setInterval(long d, timer_callback f);
// call function f once after d milliseconds
int setTimeout(long d, timer_callback f);
// call function f every d milliseconds for n times
int setTimer(long d, timer_callback f, int n);
// destroy the specified timer
void deleteTimer(int numTimer);
// restart the specified timer
void restartTimer(int numTimer);
// returns true if the specified timer is enabled
boolean isEnabled(int numTimer);
// enables the specified timer
void enable(int numTimer);
// disables the specified timer
void disable(int numTimer);
// enables the specified timer if it's currently disabled,
// and vice-versa
void toggle(int numTimer);
// returns the number of used timers
int getNumTimers();
// returns the number of available timers
int getNumAvailableTimers() { return MAX_TIMERS - numTimers; };
private:
// deferred call constants
const static int DEFCALL_DONTRUN = 0; // don't call the callback function
const static int DEFCALL_RUNONLY = 1; // call the callback function but don't delete the timer
const static int DEFCALL_RUNANDDEL = 2; // call the callback function and delete the timer
// find the first available slot
int findFirstFreeSlot();
// value returned by the millis() function
// in the previous run() call
unsigned long prev_millis[MAX_TIMERS];
// pointers to the callback functions
timer_callback callbacks[MAX_TIMERS];
// delay values
long delays[MAX_TIMERS];
// number of runs to be executed for each timer
int maxNumRuns[MAX_TIMERS];
// number of executed runs for each timer
int numRuns[MAX_TIMERS];
// which timers are enabled
boolean enabled[MAX_TIMERS];
// deferred function call (sort of) - N.B.: this array is only used in run()
int toBeCalled[MAX_TIMERS];
// actual number of timers in use
int numTimers;
};
#endif

97
Arduino-ESP/PylontechMonitoring/libraries/Time-master/DateStrings.cpp

@ -0,0 +1,97 @@
/* DateStrings.cpp
* Definitions for date strings for use with the Time library
*
* Updated for Arduino 1.5.7 18 July 2014
*
* No memory is consumed in the sketch if your code does not call any of the string methods
* You can change the text of the strings, make sure the short strings are each exactly 3 characters
* the long strings can be any length up to the constant dt_MAX_STRING_LEN defined in TimeLib.h
*
*/
#if defined(__AVR__)
#include <avr/pgmspace.h>
#else
// for compatiblity with Arduino Due and Teensy 3.0 and maybe others?
#define PROGMEM
#define PGM_P const char *
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#define pgm_read_word(addr) (*(const unsigned char **)(addr))
#define strcpy_P(dest, src) strcpy((dest), (src))
#endif
#include <string.h> // for strcpy_P or strcpy
#include "TimeLib.h"
// the short strings for each day or month must be exactly dt_SHORT_STR_LEN
#define dt_SHORT_STR_LEN 3 // the length of short strings
static char buffer[dt_MAX_STRING_LEN+1]; // must be big enough for longest string and the terminating null
const char monthStr0[] PROGMEM = "";
const char monthStr1[] PROGMEM = "January";
const char monthStr2[] PROGMEM = "February";
const char monthStr3[] PROGMEM = "March";
const char monthStr4[] PROGMEM = "April";
const char monthStr5[] PROGMEM = "May";
const char monthStr6[] PROGMEM = "June";
const char monthStr7[] PROGMEM = "July";
const char monthStr8[] PROGMEM = "August";
const char monthStr9[] PROGMEM = "September";
const char monthStr10[] PROGMEM = "October";
const char monthStr11[] PROGMEM = "November";
const char monthStr12[] PROGMEM = "December";
const PROGMEM char * const PROGMEM monthNames_P[] =
{
monthStr0,monthStr1,monthStr2,monthStr3,monthStr4,monthStr5,monthStr6,
monthStr7,monthStr8,monthStr9,monthStr10,monthStr11,monthStr12
};
const char monthShortNames_P[] PROGMEM = "ErrJanFebMarAprMayJunJulAugSepOctNovDec";
const char dayStr0[] PROGMEM = "Err";
const char dayStr1[] PROGMEM = "Sunday";
const char dayStr2[] PROGMEM = "Monday";
const char dayStr3[] PROGMEM = "Tuesday";
const char dayStr4[] PROGMEM = "Wednesday";
const char dayStr5[] PROGMEM = "Thursday";
const char dayStr6[] PROGMEM = "Friday";
const char dayStr7[] PROGMEM = "Saturday";
const PROGMEM char * const PROGMEM dayNames_P[] =
{
dayStr0,dayStr1,dayStr2,dayStr3,dayStr4,dayStr5,dayStr6,dayStr7
};
const char dayShortNames_P[] PROGMEM = "ErrSunMonTueWedThuFriSat";
/* functions to return date strings */
char* monthStr(uint8_t month)
{
strcpy_P(buffer, (PGM_P)pgm_read_word(&(monthNames_P[month])));
return buffer;
}
char* monthShortStr(uint8_t month)
{
for (int i=0; i < dt_SHORT_STR_LEN; i++)
buffer[i] = pgm_read_byte(&(monthShortNames_P[i+ (month*dt_SHORT_STR_LEN)]));
buffer[dt_SHORT_STR_LEN] = 0;
return buffer;
}
char* dayStr(uint8_t day)
{
strcpy_P(buffer, (PGM_P)pgm_read_word(&(dayNames_P[day])));
return buffer;
}
char* dayShortStr(uint8_t day)
{
uint8_t index = day*dt_SHORT_STR_LEN;
for (int i=0; i < dt_SHORT_STR_LEN; i++)
buffer[i] = pgm_read_byte(&(dayShortNames_P[index + i]));
buffer[dt_SHORT_STR_LEN] = 0;
return buffer;
}

131
Arduino-ESP/PylontechMonitoring/libraries/Time-master/Readme.txt

@ -0,0 +1,131 @@
Readme file for Arduino Time Library
Time is a library that provides timekeeping functionality for Arduino.
The code is derived from the Playground DateTime library but is updated
to provide an API that is more flexable and easier to use.
A primary goal was to enable date and time functionality that can be used with
a variety of external time sources with minimum differences required in sketch logic.
Example sketches illustrate how similar sketch code can be used with: a Real Time Clock,
internet NTP time service, GPS time data, and Serial time messages from a computer
for time synchronization.
The functions available in the library include:
hour(); // the hour now (0-23)
minute(); // the minute now (0-59)
second(); // the second now (0-59)
day(); // the day now (1-31)
weekday(); // day of the week, Sunday is day 0
month(); // the month now (1-12)
year(); // the full four digit year: (2009, 2010 etc)
there are also functions to return the hour in 12 hour format
hourFormat12(); // the hour now in 12 hour format
isAM(); // returns true if time now is AM
isPM(); // returns true if time now is PM
now(); // returns the current time as seconds since Jan 1 1970
The time and date functions can take an optional parameter for the time. This prevents
errors if the time rolls over between elements. For example, if a new minute begins
between getting the minute and second, the values will be inconsistent. Using the
following functions eliminates this probglem
time_t t = now(); // store the current time in time variable t
hour(t); // returns the hour for the given time t
minute(t); // returns the minute for the given time t
second(t); // returns the second for the given time t
day(t); // the day for the given time t
weekday(t); // day of the week for the given time t
month(t); // the month for the given time t
year(t); // the year for the given time t
Functions for managing the timer services are:
setTime(t); // set the system time to the give time t
setTime(hr,min,sec,day,mnth,yr); // alternative to above, yr is 2 or 4 digit yr (2010 or 10 sets year to 2010)
adjustTime(adjustment); // adjust system time by adding the adjustment value
timeStatus(); // indicates if time has been set and recently synchronized
// returns one of the following enumerations:
timeNotSet // the time has never been set, the clock started at Jan 1 1970
timeNeedsSync // the time had been set but a sync attempt did not succeed
timeSet // the time is set and is synced
Time and Date values are not valid if the status is timeNotSet. Otherwise values can be used but
the returned time may have drifted if the status is timeNeedsSync.
setSyncProvider(getTimeFunction); // set the external time provider
setSyncInterval(interval); // set the number of seconds between re-sync
There are many convenience macros in the time.h file for time constants and conversion of time units.
To use the library, copy the download to the Library directory.
The Time directory contains the Time library and some example sketches
illustrating how the library can be used with various time sources:
- TimeSerial.pde shows Arduino as a clock without external hardware.
It is synchronized by time messages sent over the serial port.
A companion Processing sketch will automatically provide these messages
if it is running and connected to the Arduino serial port.
- TimeSerialDateStrings.pde adds day and month name strings to the sketch above
Short (3 character) and long strings are available to print the days of
the week and names of the months.
- TimeRTC uses a DS1307 real time clock to provide time synchronization.
A basic RTC library named DS1307RTC is included in the download.
To run this sketch the DS1307RTC library must be installed.
- TimeRTCSet is similar to the above and adds the ability to set the Real Time Clock
- TimeRTCLog demonstrates how to calculate the difference between times.
It is a vary simple logger application that monitors events on digtial pins
and prints (to the serial port) the time of an event and the time period since the previous event.
- TimeNTP uses the Arduino Ethernet shield to access time using the internet NTP time service.
The NTP protocol uses UDP and the UdpBytewise library is required, see:
http://bitbucket.org/bjoern/arduino_osc/src/14667490521f/libraries/Ethernet/
- TimeGPS gets time from a GPS
This requires the TinyGPS library from Mikal Hart:
http://arduiniana.org/libraries/TinyGPS
Differences between this code and the playground DateTime library
although the Time library is based on the DateTime codebase, the API has changed.
Changes in the Time library API:
- time elements are functions returning int (they are variables in DateTime)
- Years start from 1970
- days of the week and months start from 1 (they start from 0 in DateTime)
- DateStrings do not require a seperate library
- time elements can be accessed non-atomically (in DateTime they are always atomic)
- function added to automatically sync time with extrnal source
- localTime and maketime parameters changed, localTime renamed to breakTime
Technical notes:
Internal system time is based on the standard Unix time_t.
The value is the number of seconds since Jan 1 1970.
System time begins at zero when the sketch starts.
The internal time can be automatically synchronized at regular intervals to an external time source.
This is enabled by calling the setSyncProvider(provider) function - the provider argument is
the address of a function that returns the current time as a time_t.
See the sketches in the examples directory for usage.
The default interval for re-syncing the time is 5 minutes but can be changed by calling the
setSyncInterval( interval) method to set the number of seconds between re-sync attempts.
The Time library defines a structure for holding time elements that is a compact version of the C tm structure.
All the members of the Arduino tm structure are bytes and the year is offset from 1970.
Convenience macros provide conversion to and from the Arduino format.
Low level functions to convert between system time and individual time elements are provided:
breakTime( time, &tm); // break time_t into elements stored in tm struct
makeTime( &tm); // return time_t from elements stored in tm struct
The DS1307RTC library included in the download provides an example of how a time provider
can use the low level functions to interface with the Time library.

321
Arduino-ESP/PylontechMonitoring/libraries/Time-master/Time.cpp

@ -0,0 +1,321 @@
/*
time.c - low level time and date functions
Copyright (c) Michael Margolis 2009-2014
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1.0 6 Jan 2010 - initial release
1.1 12 Feb 2010 - fixed leap year calculation error
1.2 1 Nov 2010 - fixed setTime bug (thanks to Korman for this)
1.3 24 Mar 2012 - many edits by Paul Stoffregen: fixed timeStatus() to update
status, updated examples for Arduino 1.0, fixed ARM
compatibility issues, added TimeArduinoDue and TimeTeensy3
examples, add error checking and messages to RTC examples,
add examples to DS1307RTC library.
1.4 5 Sep 2014 - compatibility with Arduino 1.5.7
*/
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
#include "TimeLib.h"
static tmElements_t tm; // a cache of time elements
static time_t cacheTime; // the time the cache was updated
static uint32_t syncInterval = 300; // time sync will be attempted after this many seconds
void refreshCache(time_t t) {
if (t != cacheTime) {
breakTime(t, tm);
cacheTime = t;
}
}
int hour() { // the hour now
return hour(now());
}
int hour(time_t t) { // the hour for the given time
refreshCache(t);
return tm.Hour;
}
int hourFormat12() { // the hour now in 12 hour format
return hourFormat12(now());
}
int hourFormat12(time_t t) { // the hour for the given time in 12 hour format
refreshCache(t);
if( tm.Hour == 0 )
return 12; // 12 midnight
else if( tm.Hour > 12)
return tm.Hour - 12 ;
else
return tm.Hour ;
}
uint8_t isAM() { // returns true if time now is AM
return !isPM(now());
}
uint8_t isAM(time_t t) { // returns true if given time is AM
return !isPM(t);
}
uint8_t isPM() { // returns true if PM
return isPM(now());
}
uint8_t isPM(time_t t) { // returns true if PM
return (hour(t) >= 12);
}
int minute() {
return minute(now());
}
int minute(time_t t) { // the minute for the given time
refreshCache(t);
return tm.Minute;
}
int second() {
return second(now());
}
int second(time_t t) { // the second for the given time
refreshCache(t);
return tm.Second;
}
int day(){
return(day(now()));
}
int day(time_t t) { // the day for the given time (0-6)
refreshCache(t);
return tm.Day;
}
int weekday() { // Sunday is day 1
return weekday(now());
}
int weekday(time_t t) {
refreshCache(t);
return tm.Wday;
}
int month(){
return month(now());
}
int month(time_t t) { // the month for the given time
refreshCache(t);
return tm.Month;
}
int year() { // as in Processing, the full four digit year: (2009, 2010 etc)
return year(now());
}
int year(time_t t) { // the year for the given time
refreshCache(t);
return tmYearToCalendar(tm.Year);
}
/*============================================================================*/
/* functions to convert to and from system time */
/* These are for interfacing with time serivces and are not normally needed in a sketch */
// leap year calulator expects year argument as years offset from 1970
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0
void breakTime(time_t timeInput, tmElements_t &tm){
// break the given time_t into time components
// this is a more compact version of the C library localtime function
// note that year is offset from 1970 !!!
uint8_t year;
uint8_t month, monthLength;
uint32_t time;
unsigned long days;
time = (uint32_t)timeInput;
tm.Second = time % 60;
time /= 60; // now it is minutes
tm.Minute = time % 60;
time /= 60; // now it is hours
tm.Hour = time % 24;
time /= 24; // now it is days
tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1
year = 0;
days = 0;
while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
year++;
}
tm.Year = year; // year is offset from 1970
days -= LEAP_YEAR(year) ? 366 : 365;
time -= days; // now it is days in this year, starting at 0
days=0;
month=0;
monthLength=0;
for (month=0; month<12; month++) {
if (month==1) { // february
if (LEAP_YEAR(year)) {
monthLength=29;
} else {
monthLength=28;
}
} else {
monthLength = monthDays[month];
}
if (time >= monthLength) {
time -= monthLength;
} else {
break;
}
}
tm.Month = month + 1; // jan is month 1
tm.Day = time + 1; // day of month
}
time_t makeTime(tmElements_t &tm){
// assemble time elements into time_t
// note year argument is offset from 1970 (see macros in time.h to convert to other formats)
// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9
int i;
uint32_t seconds;
// seconds from 1970 till 1 jan 00:00:00 of the given year
seconds= tm.Year*(SECS_PER_DAY * 365);
for (i = 0; i < tm.Year; i++) {
if (LEAP_YEAR(i)) {
seconds += SECS_PER_DAY; // add extra days for leap years
}
}
// add days for this year, months start from 1
for (i = 1; i < tm.Month; i++) {
if ( (i == 2) && LEAP_YEAR(tm.Year)) {
seconds += SECS_PER_DAY * 29;
} else {
seconds += SECS_PER_DAY * monthDays[i-1]; //monthDay array starts from 0
}
}
seconds+= (tm.Day-1) * SECS_PER_DAY;
seconds+= tm.Hour * SECS_PER_HOUR;
seconds+= tm.Minute * SECS_PER_MIN;
seconds+= tm.Second;
return (time_t)seconds;
}
/*=====================================================*/
/* Low level system time functions */
static uint32_t sysTime = 0;
static uint32_t prevMillis = 0;
static uint32_t nextSyncTime = 0;
static timeStatus_t Status = timeNotSet;
getExternalTime getTimePtr; // pointer to external sync function
//setExternalTime setTimePtr; // not used in this version
#ifdef TIME_DRIFT_INFO // define this to get drift data
time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync
#endif
time_t now() {
// calculate number of seconds passed since last call to now()
while (millis() - prevMillis >= 1000) {
// millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference
sysTime++;
prevMillis += 1000;
#ifdef TIME_DRIFT_INFO
sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift
#endif
}
if (nextSyncTime <= sysTime) {
if (getTimePtr != 0) {
time_t t = getTimePtr();
if (t != 0) {
setTime(t);
} else {
nextSyncTime = sysTime + syncInterval;
Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync;
}
}
}
return (time_t)sysTime;
}
void setTime(time_t t) {
#ifdef TIME_DRIFT_INFO
if(sysUnsyncedTime == 0)
sysUnsyncedTime = t; // store the time of the first call to set a valid Time
#endif
sysTime = (uint32_t)t;
nextSyncTime = (uint32_t)t + syncInterval;
Status = timeSet;
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
}
void setTime(int hr,int min,int sec,int dy, int mnth, int yr){
// year can be given as full four digit year or two digts (2010 or 10 for 2010);
//it is converted to years since 1970
if( yr > 99)
yr = yr - 1970;
else
yr += 30;
tm.Year = yr;
tm.Month = mnth;
tm.Day = dy;
tm.Hour = hr;
tm.Minute = min;
tm.Second = sec;
setTime(makeTime(tm));
}
void adjustTime(long adjustment) {
sysTime += adjustment;
}
// indicates if time has been set and recently synchronized
timeStatus_t timeStatus() {
now(); // required to actually update the status
return Status;
}
void setSyncProvider( getExternalTime getTimeFunction){
getTimePtr = getTimeFunction;
nextSyncTime = sysTime;
now(); // this will sync the clock
}
void setSyncInterval(time_t interval){ // set the number of seconds between re-sync
syncInterval = (uint32_t)interval;
nextSyncTime = sysTime + syncInterval;
}

1
Arduino-ESP/PylontechMonitoring/libraries/Time-master/Time.h

@ -0,0 +1 @@
#include "TimeLib.h"

144
Arduino-ESP/PylontechMonitoring/libraries/Time-master/TimeLib.h

@ -0,0 +1,144 @@
/*
time.h - low level time and date functions
*/
/*
July 3 2011 - fixed elapsedSecsThisWeek macro (thanks Vincent Valdy for this)
- fixed daysToTime_t macro (thanks maniacbug)
*/
#ifndef _Time_h
#ifdef __cplusplus
#define _Time_h
#include <inttypes.h>
#ifndef __AVR__
#include <sys/types.h> // for __time_t_defined, but avr libc lacks sys/types.h
#endif
#if !defined(__time_t_defined) // avoid conflict with newlib or other posix libc
typedef unsigned long time_t;
#endif
// This ugly hack allows us to define C++ overloaded functions, when included
// from within an extern "C", as newlib's sys/stat.h does. Actually it is
// intended to include "time.h" from the C library (on ARM, but AVR does not
// have that file at all). On Mac and Windows, the compiler will find this
// "Time.h" instead of the C library "time.h", so we may cause other weird
// and unpredictable effects by conflicting with the C library header "time.h",
// but at least this hack lets us define C++ functions as intended. Hopefully
// nothing too terrible will result from overriding the C library header?!
extern "C++" {
typedef enum {timeNotSet, timeNeedsSync, timeSet
} timeStatus_t ;
typedef enum {
dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday
} timeDayOfWeek_t;
typedef enum {
tmSecond, tmMinute, tmHour, tmWday, tmDay,tmMonth, tmYear, tmNbrFields
} tmByteFields;
typedef struct {
uint8_t Second;
uint8_t Minute;
uint8_t Hour;
uint8_t Wday; // day of week, sunday is day 1
uint8_t Day;
uint8_t Month;
uint8_t Year; // offset from 1970;
} tmElements_t, TimeElements, *tmElementsPtr_t;
//convenience macros to convert to and from tm years
#define tmYearToCalendar(Y) ((Y) + 1970) // full four digit year
#define CalendarYrToTm(Y) ((Y) - 1970)
#define tmYearToY2k(Y) ((Y) - 30) // offset is from 2000
#define y2kYearToTm(Y) ((Y) + 30)
typedef time_t(*getExternalTime)();
//typedef void (*setExternalTime)(const time_t); // not used in this version
/*==============================================================================*/
/* Useful Constants */
#define SECS_PER_MIN ((time_t)(60UL))
#define SECS_PER_HOUR ((time_t)(3600UL))
#define SECS_PER_DAY ((time_t)(SECS_PER_HOUR * 24UL))
#define DAYS_PER_WEEK ((time_t)(7UL))
#define SECS_PER_WEEK ((time_t)(SECS_PER_DAY * DAYS_PER_WEEK))
#define SECS_PER_YEAR ((time_t)(SECS_PER_WEEK * 52UL))
#define SECS_YR_2000 ((time_t)(946684800UL)) // the time at the start of y2k
/* Useful Macros for getting elapsed time */
#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN)
#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN)
#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR)
#define dayOfWeek(_time_) ((( _time_ / SECS_PER_DAY + 4) % DAYS_PER_WEEK)+1) // 1 = Sunday
#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY) // this is number of days since Jan 1 1970
#define elapsedSecsToday(_time_) (_time_ % SECS_PER_DAY) // the number of seconds since last midnight
// The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971
// Always set the correct time before settting alarms
#define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY) // time at the start of the given day
#define nextMidnight(_time_) ( previousMidnight(_time_) + SECS_PER_DAY ) // time at the end of the given day
#define elapsedSecsThisWeek(_time_) (elapsedSecsToday(_time_) + ((dayOfWeek(_time_)-1) * SECS_PER_DAY) ) // note that week starts on day 1
#define previousSunday(_time_) (_time_ - elapsedSecsThisWeek(_time_)) // time at the start of the week for the given time
#define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK) // time at the end of the week for the given time
/* Useful Macros for converting elapsed time to a time_t */
#define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN)
#define hoursToTime_t ((H)) ( (H) * SECS_PER_HOUR)
#define daysToTime_t ((D)) ( (D) * SECS_PER_DAY) // fixed on Jul 22 2011
#define weeksToTime_t ((W)) ( (W) * SECS_PER_WEEK)
/*============================================================================*/
/* time and date functions */
int hour(); // the hour now
int hour(time_t t); // the hour for the given time
int hourFormat12(); // the hour now in 12 hour format
int hourFormat12(time_t t); // the hour for the given time in 12 hour format
uint8_t isAM(); // returns true if time now is AM
uint8_t isAM(time_t t); // returns true the given time is AM
uint8_t isPM(); // returns true if time now is PM
uint8_t isPM(time_t t); // returns true the given time is PM
int minute(); // the minute now
int minute(time_t t); // the minute for the given time
int second(); // the second now
int second(time_t t); // the second for the given time
int day(); // the day now
int day(time_t t); // the day for the given time
int weekday(); // the weekday now (Sunday is day 1)
int weekday(time_t t); // the weekday for the given time
int month(); // the month now (Jan is month 1)
int month(time_t t); // the month for the given time
int year(); // the full four digit year: (2009, 2010 etc)
int year(time_t t); // the year for the given time
time_t now(); // return the current time as seconds since Jan 1 1970
void setTime(time_t t);
void setTime(int hr,int min,int sec,int day, int month, int yr);
void adjustTime(long adjustment);
/* date strings */
#define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null)
char* monthStr(uint8_t month);
char* dayStr(uint8_t day);
char* monthShortStr(uint8_t month);
char* dayShortStr(uint8_t day);
/* time sync functions */
timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized
void setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider
void setSyncInterval(time_t interval); // set the number of seconds between re-sync
/* low level functions to convert to and from system time */
void breakTime(time_t time, tmElements_t &tm); // break time_t into elements
time_t makeTime(tmElements_t &tm); // convert time elements into time_t
} // extern "C++"
#endif // __cplusplus
#endif /* _Time_h */

78
Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/Processing/SyncArduinoClock/SyncArduinoClock.pde

@ -0,0 +1,78 @@
/**
* SyncArduinoClock.
*
* portIndex must be set to the port connected to the Arduino
*
* The current time is sent in response to request message from Arduino
* or by clicking the display window
*
* The time message is 11 ASCII text characters; a header (the letter 'T')
* followed by the ten digit system time (unix time)
*/
import processing.serial.*;
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;
public static final short portIndex = 0; // select the com port, 0 is the first port
public static final String TIME_HEADER = "T"; //header for arduino serial time message
public static final char TIME_REQUEST = 7; // ASCII bell character
public static final char LF = 10; // ASCII linefeed
public static final char CR = 13; // ASCII linefeed
Serial myPort; // Create object from Serial class
void setup() {
size(200, 200);
println(Serial.list());
println(" Connecting to -> " + Serial.list()[portIndex]);
myPort = new Serial(this,Serial.list()[portIndex], 9600);
println(getTimeNow());
}
void draw()
{
textSize(20);
textAlign(CENTER);
fill(0);
text("Click to send\nTime Sync", 0, 75, 200, 175);
if ( myPort.available() > 0) { // If data is available,
char val = char(myPort.read()); // read it and store it in val
if(val == TIME_REQUEST){
long t = getTimeNow();
sendTimeMessage(TIME_HEADER, t);
}
else
{
if(val == LF)
; //igonore
else if(val == CR)
println();
else
print(val); // echo everying but time request
}
}
}
void mousePressed() {
sendTimeMessage( TIME_HEADER, getTimeNow());
}
void sendTimeMessage(String header, long time) {
String timeStr = String.valueOf(time);
myPort.write(header); // send header and time to arduino
myPort.write(timeStr);
myPort.write('\n');
}
long getTimeNow(){
// java time is in ms, we want secs
Date d = new Date();
Calendar cal = new GregorianCalendar();
long current = d.getTime()/1000;
long timezone = cal.get(cal.ZONE_OFFSET)/1000;
long daylight = cal.get(cal.DST_OFFSET)/1000;
return current + timezone + daylight;
}

9
Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/Processing/SyncArduinoClock/readme.txt

@ -0,0 +1,9 @@
SyncArduinoClock is a Processing sketch that responds to Arduino requests for
time synchronization messages.
The portIndex must be set the Serial port connected to Arduino.
Download TimeSerial.pde onto Arduino and you should see the time
message displayed when you run SyncArduinoClock in Processing.
The Arduino time is set from the time on your computer through the
Processing sketch.

71
Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeArduinoDue/TimeArduinoDue.ino

@ -0,0 +1,71 @@
/*
* TimeRTC.pde
* example code illustrating Time library with Real Time Clock.
*
* This example requires Markus Lange's Arduino Due RTC Library
* https://github.com/MarkusLange/Arduino-Due-RTC-Library
*/
#include <TimeLib.h>
#include <rtc_clock.h>
// Select the Slowclock source
//RTC_clock rtc_clock(RC);
RTC_clock rtc_clock(XTAL);
void setup() {
Serial.begin(9600);
rtc_clock.init();
if (rtc_clock.date_already_set() == 0) {
// Unfortunately, the Arduino Due hardware does not seem to
// be designed to maintain the RTC clock state when the
// board resets. Markus described it thusly: "Uhh the Due
// does reset with the NRSTB pin. This resets the full chip
// with all backup regions including RTC, RTT and SC. Only
// if the reset is done with the NRST pin will these regions
// stay with their old values."
rtc_clock.set_time(__TIME__);
rtc_clock.set_date(__DATE__);
// However, this might work on other unofficial SAM3X boards
// with different reset circuitry than Arduino Due?
}
setSyncProvider(getArduinoDueTime);
if(timeStatus()!= timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
}
time_t getArduinoDueTime()
{
return rtc_clock.unixtime();
}
void loop()
{
digitalClockDisplay();
delay(1000);
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}

87
Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeGPS/TimeGPS.ino

@ -0,0 +1,87 @@
/*
* TimeGPS.pde
* example code illustrating time synced from a GPS
*
*/
#include <TimeLib.h>
#include <TinyGPS.h> // http://arduiniana.org/libraries/TinyGPS/
#include <SoftwareSerial.h>
// TinyGPS and SoftwareSerial libraries are the work of Mikal Hart
SoftwareSerial SerialGPS = SoftwareSerial(10, 11); // receive on pin 10
TinyGPS gps;
// To use a hardware serial port, which is far more efficient than
// SoftwareSerial, uncomment this line and remove SoftwareSerial
//#define SerialGPS Serial1
// Offset hours from gps time (UTC)
const int offset = 1; // Central European Time
//const int offset = -5; // Eastern Standard Time (USA)
//const int offset = -4; // Eastern Daylight Time (USA)
//const int offset = -8; // Pacific Standard Time (USA)
//const int offset = -7; // Pacific Daylight Time (USA)
// Ideally, it should be possible to learn the time zone
// based on the GPS position data. However, that would
// require a complex library, probably incorporating some
// sort of database using Eric Muller's time zone shape
// maps, at http://efele.net/maps/tz/
time_t prevDisplay = 0; // when the digital clock was displayed
void setup()
{
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
SerialGPS.begin(4800);
Serial.println("Waiting for GPS time ... ");
}
void loop()
{
while (SerialGPS.available()) {
if (gps.encode(SerialGPS.read())) { // process gps messages
// when TinyGPS reports new data...
unsigned long age;
int Year;
byte Month, Day, Hour, Minute, Second;
gps.crack_datetime(&Year, &Month, &Day, &Hour, &Minute, &Second, NULL, &age);
if (age < 500) {
// set the Time to the latest GPS reading
setTime(Hour, Minute, Second, Day, Month, Year);
adjustTime(offset * SECS_PER_HOUR);
}
}
}
if (timeStatus()!= timeNotSet) {
if (now() != prevDisplay) { //update the display only if the time has changed
prevDisplay = now();
digitalClockDisplay();
}
}
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits) {
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}

135
Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeNTP/TimeNTP.ino

@ -0,0 +1,135 @@
/*
* Time_NTP.pde
* Example showing time sync to NTP time source
*
* This sketch uses the Ethernet library
*/
#include <TimeLib.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <SPI.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// NTP Servers:
IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
// IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov
// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov
const int timeZone = 1; // Central European 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)
EthernetUDP Udp;
unsigned int localPort = 8888; // local port to listen for UDP packets
void setup()
{
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
delay(250);
Serial.println("TimeNTP Example");
if (Ethernet.begin(mac) == 0) {
// no point in carrying on, so do nothing forevermore:
while (1) {
Serial.println("Failed to configure Ethernet using DHCP");
delay(10000);
}
}
Serial.print("IP number assigned by DHCP is ");
Serial.println(Ethernet.localIP());
Udp.begin(localPort);
Serial.println("waiting for sync");
setSyncProvider(getNtpTime);
}
time_t prevDisplay = 0; // when the digital clock was displayed
void loop()
{
if (timeStatus() != timeNotSet) {
if (now() != prevDisplay) { //update the display only if time has changed
prevDisplay = now();
digitalClockDisplay();
}
}
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits){
// utility for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.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()
{
while (Udp.parsePacket() > 0) ; // discard any previously received packets
Serial.println("Transmit NTP Request");
sendNTPpacket(timeServer);
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 :-(");
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();
}

156
Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeNTP_ESP8266WiFi/TimeNTP_ESP8266WiFi.ino

@ -0,0 +1,156 @@
/*
* TimeNTP_ESP8266WiFi.ino
* Example showing time sync to NTP time source
*
* This sketch uses the ESP8266WiFi library
*/
#include <TimeLib.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
const char ssid[] = "*************"; // your network SSID (name)
const char pass[] = "********"; // your network password
// 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 = 1; // Central European 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
time_t getNtpTime();
void digitalClockDisplay();
void printDigits(int digits);
void sendNTPpacket(IPAddress &address);
void setup()
{
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
delay(250);
Serial.println("TimeNTP Example");
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.print("IP number assigned by DHCP is ");
Serial.println(WiFi.localIP());
Serial.println("Starting UDP");
Udp.begin(localPort);
Serial.print("Local port: ");
Serial.println(Udp.localPort());
Serial.println("waiting for sync");
setSyncProvider(getNtpTime);
setSyncInterval(300);
}
time_t prevDisplay = 0; // when the digital clock was displayed
void loop()
{
if (timeStatus() != timeNotSet) {
if (now() != prevDisplay) { //update the display only if time has changed
prevDisplay = now();
digitalClockDisplay();
}
}
}
void digitalClockDisplay()
{
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(".");
Serial.print(month());
Serial.print(".");
Serial.print(year());
Serial.println();
}
void printDigits(int digits)
{
// utility for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if (digits < 10)
Serial.print('0');
Serial.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
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 :-(");
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();
}

55
Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeRTC/TimeRTC.ino

@ -0,0 +1,55 @@
/*
* TimeRTC.pde
* example code illustrating Time library with Real Time Clock.
*
*/
#include <TimeLib.h>
#include <Wire.h>
#include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t
void setup() {
Serial.begin(9600);
while (!Serial) ; // wait until Arduino Serial Monitor opens
setSyncProvider(RTC.get); // the function to get the time from the RTC
if(timeStatus()!= timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
}
void loop()
{
if (timeStatus() == timeSet) {
digitalClockDisplay();
} else {
Serial.println("The time has not been set. Please run the Time");
Serial.println("TimeRTCSet example, or DS1307RTC SetTime example.");
Serial.println();
delay(4000);
}
delay(1000);
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}

107
Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeRTCLog/TimeRTCLog.ino

@ -0,0 +1,107 @@
/*
* TimeRTCLogger.pde
* example code illustrating adding and subtracting Time.
*
* this sketch logs pin state change events
* the time of the event and time since the previous event is calculated and sent to the serial port.
*/
#include <TimeLib.h>
#include <Wire.h>
#include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t
const int nbrInputPins = 6; // monitor 6 digital pins
const int inputPins[nbrInputPins] = {2,3,4,5,6,7}; // pins to monitor
boolean state[nbrInputPins] ; // the state of the monitored pins
time_t prevEventTime[nbrInputPins] ; // the time of the previous event
void setup() {
Serial.begin(9600);
setSyncProvider(RTC.get); // the function to sync the time from the RTC
for(int i=0; i < nbrInputPins; i++){
pinMode( inputPins[i], INPUT);
// uncomment these lines if pull-up resistors are wanted
// pinMode( inputPins[i], INPUT_PULLUP);
// state[i] = HIGH;
}
}
void loop()
{
for(int i=0; i < nbrInputPins; i++)
{
boolean val = digitalRead(inputPins[i]);
if(val != state[i])
{
time_t duration = 0; // the time since the previous event
state[i] = val;
time_t timeNow = now();
if(prevEventTime[i] > 0)
// if this was not the first state change, calculate the time from the previous change
duration = duration = timeNow - prevEventTime[i];
logEvent(inputPins[i], val, timeNow, duration ); // log the event
prevEventTime[i] = timeNow; // store the time for this event
}
}
}
void logEvent( int pin, boolean state, time_t timeNow, time_t duration)
{
Serial.print("Pin ");
Serial.print(pin);
if( state == HIGH)
Serial.print(" went High at ");
else
Serial.print(" went Low at ");
showTime(timeNow);
if(duration > 0){
// only display duration if greater than 0
Serial.print(", Duration was ");
showDuration(duration);
}
Serial.println();
}
void showTime(time_t t){
// display the given time
Serial.print(hour(t));
printDigits(minute(t));
printDigits(second(t));
Serial.print(" ");
Serial.print(day(t));
Serial.print(" ");
Serial.print(month(t));
Serial.print(" ");
Serial.print(year(t));
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
void showDuration(time_t duration){
// prints the duration in days, hours, minutes and seconds
if(duration >= SECS_PER_DAY){
Serial.print(duration / SECS_PER_DAY);
Serial.print(" day(s) ");
duration = duration % SECS_PER_DAY;
}
if(duration >= SECS_PER_HOUR){
Serial.print(duration / SECS_PER_HOUR);
Serial.print(" hour(s) ");
duration = duration % SECS_PER_HOUR;
}
if(duration >= SECS_PER_MIN){
Serial.print(duration / SECS_PER_MIN);
Serial.print(" minute(s) ");
duration = duration % SECS_PER_MIN;
}
Serial.print(duration);
Serial.print(" second(s) ");
}

80
Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeRTCSet/TimeRTCSet.ino

@ -0,0 +1,80 @@
/*
* TimeRTCSet.pde
* example code illustrating Time library with Real Time Clock.
*
* RTC clock is set in response to serial port time message
* A Processing example sketch to set the time is included in the download
* On Linux, you can use "date +T%s > /dev/ttyACM0" (UTC time zone)
*/
#include <TimeLib.h>
#include <Wire.h>
#include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t
void setup() {
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
setSyncProvider(RTC.get); // the function to get the time from the RTC
if (timeStatus() != timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
}
void loop()
{
if (Serial.available()) {
time_t t = processSyncMessage();
if (t != 0) {
RTC.set(t); // set the RTC and the system time to the received value
setTime(t);
}
}
digitalClockDisplay();
delay(1000);
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
/* code to process time sync messages from the serial port */
#define TIME_HEADER "T" // Header tag for serial time sync message
unsigned long processSyncMessage() {
unsigned long pctime = 0L;
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
if(Serial.find(TIME_HEADER)) {
pctime = Serial.parseInt();
return pctime;
if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
pctime = 0L; // return 0 to indicate that the time is not valid
}
}
return pctime;
}

81
Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeSerial/TimeSerial.ino

@ -0,0 +1,81 @@
/*
* TimeSerial.pde
* example code illustrating Time library set through serial port messages.
*
* Messages consist of the letter T followed by ten digit time (as seconds since Jan 1 1970)
* you can send the text on the next line using Serial Monitor to set the clock to noon Jan 1 2013
T1357041600
*
* A Processing example sketch to automatically send the messages is included in the download
* On Linux, you can use "date +T%s\n > /dev/ttyACM0" (UTC time zone)
*/
#include <TimeLib.h>
#define TIME_HEADER "T" // Header tag for serial time sync message
#define TIME_REQUEST 7 // ASCII bell character requests a time sync message
void setup() {
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
pinMode(13, OUTPUT);
setSyncProvider( requestSync); //set function to call when sync required
Serial.println("Waiting for sync message");
}
void loop(){
if (Serial.available()) {
processSyncMessage();
}
if (timeStatus()!= timeNotSet) {
digitalClockDisplay();
}
if (timeStatus() == timeSet) {
digitalWrite(13, HIGH); // LED on if synced
} else {
digitalWrite(13, LOW); // LED off if needs refresh
}
delay(1000);
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
void processSyncMessage() {
unsigned long pctime;
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
if(Serial.find(TIME_HEADER)) {
pctime = Serial.parseInt();
if( pctime >= DEFAULT_TIME) { // check the integer is a valid time (greater than Jan 1 2013)
setTime(pctime); // Sync Arduino clock to the time received on the serial port
}
}
}
time_t requestSync()
{
Serial.write(TIME_REQUEST);
return 0; // the time will be sent later in response to serial mesg
}

108
Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeSerialDateStrings/TimeSerialDateStrings.ino

@ -0,0 +1,108 @@
/*
* TimeSerialDateStrings.pde
* example code illustrating Time library date strings
*
* This sketch adds date string functionality to TimeSerial sketch
* Also shows how to handle different messages
*
* A message starting with a time header sets the time
* A Processing example sketch to automatically send the messages is inclided in the download
* On Linux, you can use "date +T%s\n > /dev/ttyACM0" (UTC time zone)
*
* A message starting with a format header sets the date format
* send: Fs\n for short date format
* send: Fl\n for long date format
*/
#include <TimeLib.h>
// single character message tags
#define TIME_HEADER 'T' // Header tag for serial time sync message
#define FORMAT_HEADER 'F' // Header tag indicating a date format message
#define FORMAT_SHORT 's' // short month and day strings
#define FORMAT_LONG 'l' // (lower case l) long month and day strings
#define TIME_REQUEST 7 // ASCII bell character requests a time sync message
static boolean isLongFormat = true;
void setup() {
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
setSyncProvider( requestSync); //set function to call when sync required
Serial.println("Waiting for sync message");
}
void loop(){
if (Serial.available() > 1) { // wait for at least two characters
char c = Serial.read();
if( c == TIME_HEADER) {
processSyncMessage();
}
else if( c== FORMAT_HEADER) {
processFormatMessage();
}
}
if (timeStatus()!= timeNotSet) {
digitalClockDisplay();
}
delay(1000);
}
void digitalClockDisplay() {
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
if(isLongFormat)
Serial.print(dayStr(weekday()));
else
Serial.print(dayShortStr(weekday()));
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
if(isLongFormat)
Serial.print(monthStr(month()));
else
Serial.print(monthShortStr(month()));
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits) {
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
void processFormatMessage() {
char c = Serial.read();
if( c == FORMAT_LONG){
isLongFormat = true;
Serial.println(F("Setting long format"));
}
else if( c == FORMAT_SHORT) {
isLongFormat = false;
Serial.println(F("Setting short format"));
}
}
void processSyncMessage() {
unsigned long pctime;
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 - paul, perhaps we define in time.h?
pctime = Serial.parseInt();
if( pctime >= DEFAULT_TIME) { // check the integer is a valid time (greater than Jan 1 2013)
setTime(pctime); // Sync Arduino clock to the time received on the serial port
}
}
time_t requestSync() {
Serial.write(TIME_REQUEST);
return 0; // the time will be sent later in response to serial mesg
}

78
Arduino-ESP/PylontechMonitoring/libraries/Time-master/examples/TimeTeensy3/TimeTeensy3.ino

@ -0,0 +1,78 @@
/*
* TimeRTC.pde
* example code illustrating Time library with Real Time Clock.
*
*/
#include <TimeLib.h>
void setup() {
// set the Time library to use Teensy 3.0's RTC to keep time
setSyncProvider(getTeensy3Time);
Serial.begin(115200);
while (!Serial); // Wait for Arduino Serial Monitor to open
delay(100);
if (timeStatus()!= timeSet) {
Serial.println("Unable to sync with the RTC");
} else {
Serial.println("RTC has set the system time");
}
}
void loop() {
if (Serial.available()) {
time_t t = processSyncMessage();
if (t != 0) {
Teensy3Clock.set(t); // set the RTC
setTime(t);
}
}
digitalClockDisplay();
delay(1000);
}
void digitalClockDisplay() {
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
time_t getTeensy3Time()
{
return Teensy3Clock.get();
}
/* code to process time sync messages from the serial port */
#define TIME_HEADER "T" // Header tag for serial time sync message
unsigned long processSyncMessage() {
unsigned long pctime = 0L;
const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
if(Serial.find(TIME_HEADER)) {
pctime = Serial.parseInt();
return pctime;
if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
pctime = 0L; // return 0 to indicate that the time is not valid
}
}
return pctime;
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}

34
Arduino-ESP/PylontechMonitoring/libraries/Time-master/keywords.txt

@ -0,0 +1,34 @@
#######################################
# Syntax Coloring Map For Time
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
time_t KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
now KEYWORD2
second KEYWORD2
minute KEYWORD2
hour KEYWORD2
day KEYWORD2
month KEYWORD2
year KEYWORD2
isAM KEYWORD2
isPM KEYWORD2
weekday KEYWORD2
setTime KEYWORD2
adjustTime KEYWORD2
setSyncProvider KEYWORD2
setSyncInterval KEYWORD2
timeStatus KEYWORD2
TimeLib KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
#######################################
# Constants (LITERAL1)
#######################################

22
Arduino-ESP/PylontechMonitoring/libraries/Time-master/library.json

@ -0,0 +1,22 @@
{
"name": "Time",
"frameworks": "Arduino",
"keywords": "Time, date, hour, minute, second, day, week, month, year, RTC",
"description": "Time keeping library",
"url": "http://playground.arduino.cc/Code/Time",
"authors":
[
{
"name": "Michael Margolis"
},
{
"name": "Paul Stoffregen"
}
],
"repository":
{
"type": "git",
"url": "https://github.com/PaulStoffregen/Time"
}
}

10
Arduino-ESP/PylontechMonitoring/libraries/Time-master/library.properties

@ -0,0 +1,10 @@
name=Time
version=1.5
author=Michael Margolis
maintainer=Paul Stoffregen
sentence=Timekeeping functionality for Arduino
paragraph=Date and Time functions, with provisions to synchronize to external time sources like GPS and NTP (Internet). This library is often used together with TimeAlarms and DS1307RTC.
category=Timing
url=http://playground.arduino.cc/code/time
architectures=*

103
Arduino-ESP/Pylontez-MQTT/CACert.ino

@ -0,0 +1,103 @@
// rootca1.cer
const unsigned char caCert[] PROGMEM = {
0x30, 0x82, 0x04, 0x92, 0x30, 0x82, 0x03, 0x7a, 0xa0, 0x03, 0x02, 0x01,
0x02, 0x02, 0x13, 0x06, 0x7f, 0x94, 0x4a, 0x2a, 0x27, 0xcd, 0xf3, 0xfa,
0xc2, 0xae, 0x2b, 0x01, 0xf9, 0x08, 0xee, 0xb9, 0xc4, 0xc6, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
0x00, 0x30, 0x81, 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55,
0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31,
0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63,
0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x25, 0x30, 0x23,
0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66,
0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c,
0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x32, 0x53, 0x74,
0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x53, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x73, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65,
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75,
0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32,
0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x35, 0x32, 0x35, 0x31, 0x32,
0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x37, 0x31, 0x32, 0x33,
0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x39, 0x31, 0x0b,
0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x06, 0x41, 0x6d,
0x61, 0x7a, 0x6f, 0x6e, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04,
0x03, 0x13, 0x10, 0x41, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x20, 0x52, 0x6f,
0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31, 0x30, 0x82, 0x01, 0x22, 0x30,
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
0x82, 0x01, 0x01, 0x00, 0xb2, 0x78, 0x80, 0x71, 0xca, 0x78, 0xd5, 0xe3,
0x71, 0xaf, 0x47, 0x80, 0x50, 0x74, 0x7d, 0x6e, 0xd8, 0xd7, 0x88, 0x76,
0xf4, 0x99, 0x68, 0xf7, 0x58, 0x21, 0x60, 0xf9, 0x74, 0x84, 0x01, 0x2f,
0xac, 0x02, 0x2d, 0x86, 0xd3, 0xa0, 0x43, 0x7a, 0x4e, 0xb2, 0xa4, 0xd0,
0x36, 0xba, 0x01, 0xbe, 0x8d, 0xdb, 0x48, 0xc8, 0x07, 0x17, 0x36, 0x4c,
0xf4, 0xee, 0x88, 0x23, 0xc7, 0x3e, 0xeb, 0x37, 0xf5, 0xb5, 0x19, 0xf8,
0x49, 0x68, 0xb0, 0xde, 0xd7, 0xb9, 0x76, 0x38, 0x1d, 0x61, 0x9e, 0xa4,
0xfe, 0x82, 0x36, 0xa5, 0xe5, 0x4a, 0x56, 0xe4, 0x45, 0xe1, 0xf9, 0xfd,
0xb4, 0x16, 0xfa, 0x74, 0xda, 0x9c, 0x9b, 0x35, 0x39, 0x2f, 0xfa, 0xb0,
0x20, 0x50, 0x06, 0x6c, 0x7a, 0xd0, 0x80, 0xb2, 0xa6, 0xf9, 0xaf, 0xec,
0x47, 0x19, 0x8f, 0x50, 0x38, 0x07, 0xdc, 0xa2, 0x87, 0x39, 0x58, 0xf8,
0xba, 0xd5, 0xa9, 0xf9, 0x48, 0x67, 0x30, 0x96, 0xee, 0x94, 0x78, 0x5e,
0x6f, 0x89, 0xa3, 0x51, 0xc0, 0x30, 0x86, 0x66, 0xa1, 0x45, 0x66, 0xba,
0x54, 0xeb, 0xa3, 0xc3, 0x91, 0xf9, 0x48, 0xdc, 0xff, 0xd1, 0xe8, 0x30,
0x2d, 0x7d, 0x2d, 0x74, 0x70, 0x35, 0xd7, 0x88, 0x24, 0xf7, 0x9e, 0xc4,
0x59, 0x6e, 0xbb, 0x73, 0x87, 0x17, 0xf2, 0x32, 0x46, 0x28, 0xb8, 0x43,
0xfa, 0xb7, 0x1d, 0xaa, 0xca, 0xb4, 0xf2, 0x9f, 0x24, 0x0e, 0x2d, 0x4b,
0xf7, 0x71, 0x5c, 0x5e, 0x69, 0xff, 0xea, 0x95, 0x02, 0xcb, 0x38, 0x8a,
0xae, 0x50, 0x38, 0x6f, 0xdb, 0xfb, 0x2d, 0x62, 0x1b, 0xc5, 0xc7, 0x1e,
0x54, 0xe1, 0x77, 0xe0, 0x67, 0xc8, 0x0f, 0x9c, 0x87, 0x23, 0xd6, 0x3f,
0x40, 0x20, 0x7f, 0x20, 0x80, 0xc4, 0x80, 0x4c, 0x3e, 0x3b, 0x24, 0x26,
0x8e, 0x04, 0xae, 0x6c, 0x9a, 0xc8, 0xaa, 0x0d, 0x02, 0x03, 0x01, 0x00,
0x01, 0xa3, 0x82, 0x01, 0x31, 0x30, 0x82, 0x01, 0x2d, 0x30, 0x0f, 0x06,
0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01,
0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
0x0e, 0x04, 0x16, 0x04, 0x14, 0x84, 0x18, 0xcc, 0x85, 0x34, 0xec, 0xbc,
0x0c, 0x94, 0x94, 0x2e, 0x08, 0x59, 0x9c, 0xc7, 0xb2, 0x10, 0x4e, 0x0a,
0x08, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
0x80, 0x14, 0x9c, 0x5f, 0x00, 0xdf, 0xaa, 0x01, 0xd7, 0x30, 0x2b, 0x38,
0x88, 0xa2, 0xb8, 0x6d, 0x4a, 0x9c, 0xf2, 0x11, 0x91, 0x83, 0x30, 0x78,
0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x6c,
0x30, 0x6a, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
0x30, 0x01, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f,
0x63, 0x73, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, 0x2e, 0x61,
0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
0x6f, 0x6d, 0x30, 0x38, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
0x30, 0x02, 0x86, 0x2c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
0x72, 0x74, 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, 0x2e, 0x61, 0x6d,
0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, 0x2e, 0x63, 0x65, 0x72,
0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x36, 0x30, 0x34, 0x30,
0x32, 0xa0, 0x30, 0xa0, 0x2e, 0x86, 0x2c, 0x68, 0x74, 0x74, 0x70, 0x3a,
0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32,
0x2e, 0x61, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, 0x2e,
0x63, 0x72, 0x6c, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a,
0x30, 0x08, 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x62, 0x37, 0x42, 0x5c, 0xbc, 0x10,
0xb5, 0x3e, 0x8b, 0x2c, 0xe9, 0x0c, 0x9b, 0x6c, 0x45, 0xe2, 0x07, 0x00,
0x7a, 0xf9, 0xc5, 0x58, 0x0b, 0xb9, 0x08, 0x8c, 0x3e, 0xed, 0xb3, 0x25,
0x3c, 0xb5, 0x6f, 0x50, 0xe4, 0xcd, 0x35, 0x6a, 0xa7, 0x93, 0x34, 0x96,
0x32, 0x21, 0xa9, 0x48, 0x44, 0xab, 0x9c, 0xed, 0x3d, 0xb4, 0xaa, 0x73,
0x6d, 0xe4, 0x7f, 0x16, 0x80, 0x89, 0x6c, 0xcf, 0x28, 0x03, 0x18, 0x83,
0x47, 0x79, 0xa3, 0x10, 0x7e, 0x30, 0x5b, 0xac, 0x3b, 0xb0, 0x60, 0xe0,
0x77, 0xd4, 0x08, 0xa6, 0xe1, 0x1d, 0x7c, 0x5e, 0xc0, 0xbb, 0xf9, 0x9a,
0x7b, 0x22, 0x9d, 0xa7, 0x00, 0x09, 0x7e, 0xac, 0x46, 0x17, 0x83, 0xdc,
0x9c, 0x26, 0x57, 0x99, 0x30, 0x39, 0x62, 0x96, 0x8f, 0xed, 0xda, 0xde,
0xaa, 0xc5, 0xcc, 0x1b, 0x3e, 0xca, 0x43, 0x68, 0x6c, 0x57, 0x16, 0xbc,
0xd5, 0x0e, 0x20, 0x2e, 0xfe, 0xff, 0xc2, 0x6a, 0x5d, 0x2e, 0xa0, 0x4a,
0x6d, 0x14, 0x58, 0x87, 0x94, 0xe6, 0x39, 0x31, 0x5f, 0x7c, 0x73, 0xcb,
0x90, 0x88, 0x6a, 0x84, 0x11, 0x96, 0x27, 0xa6, 0xed, 0xd9, 0x81, 0x46,
0xa6, 0x7e, 0xa3, 0x72, 0x00, 0x0a, 0x52, 0x3e, 0x83, 0x88, 0x07, 0x63,
0x77, 0x89, 0x69, 0x17, 0x0f, 0x39, 0x85, 0xd2, 0xab, 0x08, 0x45, 0x4d,
0xd0, 0x51, 0x3a, 0xfd, 0x5d, 0x5d, 0x37, 0x64, 0x4c, 0x7e, 0x30, 0xb2,
0x55, 0x24, 0x42, 0x9d, 0x36, 0xb0, 0x5d, 0x9c, 0x17, 0x81, 0x61, 0xf1,
0xca, 0xf9, 0x10, 0x02, 0x24, 0xab, 0xeb, 0x0d, 0x74, 0x91, 0x8d, 0x7b,
0x45, 0x29, 0x50, 0x39, 0x88, 0xb2, 0xa6, 0x89, 0x35, 0x25, 0x1e, 0x14,
0x6a, 0x47, 0x23, 0x31, 0x2f, 0x5c, 0x9a, 0xfa, 0xad, 0x9a, 0x0e, 0x62,
0x51, 0xa4, 0x2a, 0xa9, 0xc4, 0xf9, 0x34, 0x9d, 0x21, 0x18};
const unsigned int caCertLen = 1174;

898
Arduino-ESP/Pylontez-MQTT/Pylontez-MQTT.ino

@ -0,0 +1,898 @@
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>
#include <ESP8266WebServer.h>
#include <SimpleTimer.h>
#include <TimeLib.h> //https://github.com/PaulStoffregen/Time
#include <ntp_time.h>
#include <circular_log.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
extern const unsigned char caCert[] PROGMEM;
extern const unsigned int caCertLen;
//IMPORTANT: Specify your WIFI settings:
//#define WIFI_SSID "NOS-3B26" // setubal
//#define WIFI_PASS "RMKSX2GL" // Setubal
#define WIFI_SSID "MEO-AA9030"// Andre
#define WIFI_PASS "81070ce635" // andre
//IMPORTANT: Uncomment this line if you want to enable MQTT (and fill correct MQTT_ values below):
//#define ENABLE_MQTT
#ifdef ENABLE_MQTT
//NOTE 1: if you want to change what is pushed via MQTT - edit function: pushBatteryDataToMqtt.
//NOTE 2: MQTT_TOPIC_ROOT is where battery will push MQTT topics. For example "soc" will be pushed to: "home/grid_battery/soc"
//#define MQTT_SERVER "cc42fcb4f1eb492fa3c2e07a9e617830.s2.eu.hivemq.cloud" //"192.168.1.123"
#define MQTT_SERVER "192.168.1.123"
#define MQTT_PORT 1883 // 8883
#define MQTT_USER "tezmqtt"
#define MQTT_PASSWORD "mqtTez_123"
#define MQTT_TOPIC_ROOT "home/grid_battery/" //this is where mqtt data will be pushed
#define MQTT_PUSH_FREQ_SEC 2 //maximum mqtt update frequency in seconds
#endif ENABLE_MQTT
WiFiClient espClient;
//WiFiClientSecure espClient;
PubSubClient mqttClient(espClient);
IPAddress thisip;
ESP8266WebServer server(80);
SimpleTimer timer;
char g_szRecvBuff[7000];
circular_log<7000> g_log;
bool ntpTimeReceived = false;
int g_baudRate = 0;
void Log(const char* msg)
{
g_log.Log(msg);
}
int LEDPIN = 2; // The on-board Wemos D1 mini LED
//////////////////////////////////////
void setup() {
Serial.begin(115200);
Serial.println("---");
Serial.println("Serial started");
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW); // LOW = on
delay(2000);
digitalWrite(LED_BUILTIN, HIGH); // HIGH = off
// connect to WiFi
WiFi.mode(WIFI_STA);
WiFi.persistent(false); //our credentials are hardcoded, so we don't need ESP saving those each boot (will save on flash wear)
WiFi.hostname("PylonBattery");
Serial.println();
Serial.print("connecting to ");
Serial.println(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("");
for(int i=1; i<=3; i++){
LedBlink();
}
ArduinoOTA.setHostname("AndrePylon");
ArduinoOTA.begin();
server.on("/", handleRoot);
server.on("/log", handleLog);
server.on("/req", handleReq);
server.on("/jsonOut", handleJsonOut);
server.on("/reboot", [](){
ESP.restart();
});
server.begin();
Serial.println("web server started");
Serial.println("");
delay(1000);
syncTime();
Serial.println("SNTP time synced");
Serial.println("");
for(int i=1; i<=3; i++){
LedBlink();
}
/* COMMENTED BY TEZ */
/*
// https://links2004.github.io/Arduino/d2/d2f/class_wi_fi_client_secure.html
// Load root certificate in DER format into WiFiClientSecure object
// bool res = espClient.setCACert_P(caCert, caCertLen);
bool res = espClient.setCertificate(caCert, caCertLen);
if (!res) {
Serial.println("Failed to load root CA certificate!");
while (true) {
yield();
}
}
Serial.println("local load root CA certificate OK!");
// VERIFY SSL FINGERPRINT
if (!espClient.verify( "49 7E 82 A3 DB B4 19 1E 73 E5 19 A6 D3 C6 C5 31 DE D9 DE 97 BA B1 D8 19 80 A3 88 96 70 8D 94 3D", "websocketclient.hivemq.cloud") ) {
Serial.println( "Fingerprint certificate NOT verified sorry!" );
// mqttClient.disconnect();
// return false;
}else{
Serial.println( "Fingerprint verified OK!");
}
*/
// END COMMENTED BY TEZ
#ifdef ENABLE_MQTT
mqttClient.setServer(MQTT_SERVER, MQTT_PORT);
#endif
Log("Boot event");
}
//////////////////////////////////////////////////
void LedBlink(){
digitalWrite(LED_BUILTIN, LOW);
delay(150);
digitalWrite(LED_BUILTIN, HIGH); // high = off
delay(150);
}
//////////////////////////////////////////////////
void handleLog()
{
server.send(200, "text/html", g_log.c_str());
}
//////////////////////////////////////////////////
void switchBaud(int newRate)
{
if(g_baudRate == newRate)
{
return;
}
if(g_baudRate != 0)
{
Serial.flush();
delay(20);
Serial.end();
delay(20);
}
char szMsg[50];
snprintf(szMsg, sizeof(szMsg)-1, "New baud: %d", newRate);
Log(szMsg);
Serial.begin(newRate);
g_baudRate = newRate;
delay(20);
}
//////////////////////////////////////////////////
void waitForSerial()
{
for(int ix=0; ix<150;ix++)
{
if(Serial.available()) break;
delay(10);
}
}
//////////////////////////////////////////////////
int readFromSerial()
{
memset(g_szRecvBuff, 0, sizeof(g_szRecvBuff));
int recvBuffLen = 0;
bool foundTerminator = true;
waitForSerial();
while(Serial.available())
{
char szResponse[256] = "";
const int readNow = Serial.readBytesUntil('>', szResponse, sizeof(szResponse)-1); //all commands terminate with "$$\r\n\rpylon>" (no new line at the end)
if(readNow > 0 &&
szResponse[0] != '\0')
{
if(readNow + recvBuffLen + 1 >= (int)(sizeof(g_szRecvBuff)))
{
Log("WARNING: Read too much data on the console!");
break;
}
strcat(g_szRecvBuff, szResponse);
recvBuffLen += readNow;
if(strstr(g_szRecvBuff, "$$\r\n\rpylon"))
{
strcat(g_szRecvBuff, ">"); //readBytesUntil will skip this, so re-add
foundTerminator = true;
break; //found end of the string
}
if(strstr(g_szRecvBuff, "Press [Enter] to be continued,other key to exit"))
{
//we need to send new line character so battery continues the output
Serial.write("\r");
}
waitForSerial();
}
}
if(recvBuffLen > 0 )
{
if(foundTerminator == false)
{
Log("Failed to find pylon> terminator");
}
}
return recvBuffLen;
}
//////////////////////////////////////////////////
bool readFromSerialAndSendResponse()
{
const int recvBuffLen = readFromSerial();
if(recvBuffLen > 0)
{
server.sendContent(g_szRecvBuff);
return true;
}
return false;
}
//////////////////////////////////////////////////
bool sendCommandAndReadSerialResponse(const char* pszCommand)
{
switchBaud(115200);
if(pszCommand[0] != '\0')
{
Serial.write(pszCommand);
}
Serial.write("\n");
const int recvBuffLen = readFromSerial();
if(recvBuffLen > 0)
{
return true;
}
//wake up console and try again:
wakeUpConsole();
if(pszCommand[0] != '\0')
{
Serial.write(pszCommand);
}
Serial.write("\n");
return readFromSerial() > 0;
}
//////////////////////////////////////////////////
void handleReq()
{
bool respOK;
if(server.hasArg("code") == false)
{
respOK = sendCommandAndReadSerialResponse("");
}
else
{
respOK = sendCommandAndReadSerialResponse(server.arg("code").c_str());
}
if(respOK)
{
server.send(200, "text/plain", g_szRecvBuff);
}
else
{
server.send(500, "text/plain", "????");
}
}
//////////////////////////////////////////////////
void handleJsonOut()
{
if(sendCommandAndReadSerialResponse("pwr") == false)
{
server.send(500, "text/plain", "Failed to get response to 'pwr' command");
return;
}
parsePwrResponse(g_szRecvBuff);
prepareJsonOutput(g_szRecvBuff, sizeof(g_szRecvBuff));
server.send(200, "application/json", g_szRecvBuff);
}
//////////////////////////////////////////////////
void handleRoot() {
unsigned long days = 0, hours = 0, minutes = 0;
unsigned long val = os_getCurrentTimeSec();
days = val / (3600*24);
val -= days * (3600*24);
hours = val / 3600;
val -= hours * 3600;
minutes = val / 60;
val -= minutes*60;
static char szTmp[2500] = "";
snprintf(szTmp, sizeof(szTmp)-1, "<html><b>Garage Battery</b><br>Time GMT: %d/%02d/%02d %02d:%02d:%02d (%s)<br>Uptime: %02d:%02d:%02d.%02d<br><br>free heap: %u<br>Wifi RSSI: %d<BR>Wifi SSID: %s",
year(), month(), day(), hour(), minute(), second(), "GMT",
(int)days, (int)hours, (int)minutes, (int)val,
ESP.getFreeHeap(), WiFi.RSSI(), WiFi.SSID().c_str());
strncat(szTmp, "<BR><a href='/log'>Runtime log</a><HR>", sizeof(szTmp)-1);
strncat(szTmp, "<form action='/req' method='get'>Command:<input type='text' name='code'/><input type='submit'></form><a href='/req?code=pwr'>Power</a> | <a href='/req?code=help'>Help</a> | <a href='/req?code=log'>Event Log</a> | <a href='/req?code=time'>Time</a>", sizeof(szTmp)-1);
strncat(szTmp, "</html>", sizeof(szTmp)-1);
server.send(200, "text/html", szTmp);
}
unsigned long os_getCurrentTimeSec()
{
static unsigned int wrapCnt = 0;
static unsigned long lastVal = 0;
unsigned long currentVal = millis();
if(currentVal < lastVal)
{
wrapCnt++;
}
lastVal = currentVal;
unsigned long seconds = currentVal/1000;
//millis will wrap each 50 days, as we are interested only in seconds, let's keep the wrap counter
return (wrapCnt*4294967) + seconds;
}
//////////////////////////////////////////////////
void syncTime()
{
// configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
//get time from NTP
time_t currentTimeGMT = getNtpTime();
if(currentTimeGMT)
{
ntpTimeReceived = true;
setTime(currentTimeGMT);
}
else
{
timer.setTimeout(3000, syncTime); //try again in 5 seconds
}
struct tm timeinfo;
gmtime_r(&currentTimeGMT, &timeinfo);
Serial.print("Current time: ");
Serial.print(asctime(&timeinfo));
Serial.println("------------------");
// Synchronize time using SNTP. This is necessary to verify that
// the TLS certificates offered by the server are currently valid.
// Serial.print("Setting time using SNTP");
// configTime(8 * 3600, 0, "pool.ntp.org", "time.nist.gov");
// time_t now = time(nullptr);
// while (now < 8 * 3600 * 2) {
// delay(500);
// Serial.print(".");
// now = time(nullptr);
// }
//
// setTime(now);
//
// Serial.println("");
// struct tm timeinfo;
// gmtime_r(&now, &timeinfo);
// Serial.print("Current time: ");
// Serial.print(asctime(&timeinfo));
}
//////////////////////////////////////////////////
void wakeUpConsole()
{
switchBaud(1200);
//byte wakeUpBuff[] = {0x7E, 0x32, 0x30, 0x30, 0x31, 0x34, 0x36, 0x38, 0x32, 0x43, 0x30, 0x30, 0x34, 0x38, 0x35, 0x32, 0x30, 0x46, 0x43, 0x43, 0x33, 0x0D};
//Serial.write(wakeUpBuff, sizeof(wakeUpBuff));
Serial.write("~20014682C0048520FCC3\r");
delay(1000);
byte newLineBuff[] = {0x0E, 0x0A};
switchBaud(115200);
for(int ix=0; ix<10; ix++)
{
Serial.write(newLineBuff, sizeof(newLineBuff));
delay(1000);
if(Serial.available())
{
while(Serial.available())
{
Serial.read();
}
break;
}
}
}
#define MAX_PYLON_BATTERIES 2
struct pylonBattery
{
bool isPresent;
long soc; //Coulomb in %
long voltage; //in mW
long current; //in mA, negative value is discharge
long tempr; //temp of case or BMS?
long cellTempLow;
long cellTempHigh;
long cellVoltLow;
long cellVoltHigh;
char baseState[9]; //Charge | Dischg | Idle
char voltageState[9]; //Normal
char currentState[9]; //Normal
char tempState[9]; //Normal
char time[20]; //2019-06-08 04:00:29
char b_v_st[9]; //Normal (battery voltage?)
char b_t_st[9]; //Normal (battery temperature?)
bool isCharging() const { return strcmp(baseState, "Charge") == 0; }
bool isDischarging() const { return strcmp(baseState, "Dischg") == 0; }
bool isIdle() const { return strcmp(baseState, "Idle") == 0; }
bool isBalancing() const { return strcmp(baseState, "Balance") == 0; }
bool isNormal() const
{
if(isCharging() == false &&
isDischarging() == false &&
isIdle() == false &&
isBalancing() == false)
{
return false; //base state looks wrong!
}
return strcmp(voltageState, "Normal") == 0 &&
strcmp(currentState, "Normal") == 0 &&
strcmp(tempState, "Normal") == 0 &&
strcmp(b_v_st, "Normal") == 0 &&
strcmp(b_t_st, "Normal") == 0 ;
}
};
struct batteryStack
{
int batteryCount;
int soc; //in %, if charging: average SOC, otherwise: lowest SOC
int temp; //in mC, if highest temp is > 15C, this will show the highest temp, otherwise the lowest
long currentDC; //mAh current going in or out of the battery
long avgVoltage; //in mV
char baseState[9]; //Charge | Dischg | Idle | Balance | Alarm!
pylonBattery batts[MAX_PYLON_BATTERIES];
bool isNormal() const
{
for(int ix=0; ix<MAX_PYLON_BATTERIES; ix++)
{
if(batts[ix].isPresent &&
batts[ix].isNormal() == false)
{
return false;
}
}
return true;
}
//in wH
long getPowerDC() const
{
return (long)(((double)currentDC/1000.0)*((double)avgVoltage/1000.0));
}
//wH estimated current on AC side (taking into account Sofar ME3000SP losses)
long getEstPowerAc() const
{
double powerDC = (double)getPowerDC();
if(powerDC == 0)
{
return 0;
}
else if(powerDC < 0)
{
//we are discharging, on AC side we will see less power due to losses
if(powerDC < -1000)
{
return (long)(powerDC*0.94);
}
else if(powerDC < -600)
{
return (long)(powerDC*0.90);
}
else
{
return (long)(powerDC*0.87);
}
}
else
{
//we are charging, on AC side we will have more power due to losses
if(powerDC > 1000)
{
return (long)(powerDC*1.06);
}
else if(powerDC > 600)
{
return (long)(powerDC*1.1);
}
else
{
return (long)(powerDC*1.13);
}
}
}
};
batteryStack g_stack;
//////////////////////////////////////////////////
long extractInt(const char* pStr, int pos)
{
return atol(pStr+pos);
}
//////////////////////////////////////////////////
void extractStr(const char* pStr, int pos, char* strOut, int strOutSize)
{
strOut[strOutSize-1] = '\0';
strncpy(strOut, pStr+pos, strOutSize-1);
strOutSize--;
//trim right
while(strOutSize > 0)
{
if(isspace(strOut[strOutSize-1]))
{
strOut[strOutSize-1] = '\0';
}
else
{
break;
}
strOutSize--;
}
}
/* Output has mixed \r and \r\n
pwr
@
Power Volt Curr Tempr Tlow Thigh Vlow Vhigh Base.St Volt.St Curr.St Temp.St Coulomb Time B.V.St B.T.St
1 49735 -1440 22000 19000 19000 3315 3317 Dischg Normal Normal Normal 93% 2019-06-08 04:00:30 Normal Normal
....
8 - - - - - - - Absent - - - - - - -
Command completed successfully
$$
pylon
*/
//////////////////////////////////////////////////
bool parsePwrResponse(const char* pStr)
{
if(strstr(pStr, "Command completed successfully") == NULL)
{
return false;
}
int chargeCnt = 0;
int dischargeCnt = 0;
int idleCnt = 0;
int alarmCnt = 0;
int socAvg = 0;
int socLow = 0;
int tempHigh = 0;
int tempLow = 0;
memset(&g_stack, 0, sizeof(g_stack));
for(int ix=0; ix<MAX_PYLON_BATTERIES; ix++)
{
char szToFind[32] = "";
snprintf(szToFind, sizeof(szToFind)-1, "\r\r\n%d ", ix+1);
const char* pLineStart = strstr(pStr, szToFind);
if(pLineStart == NULL)
{
return false;
}
pLineStart += 3; //move past \r\r\n
extractStr(pLineStart, 55, g_stack.batts[ix].baseState, sizeof(g_stack.batts[ix].baseState));
if(strcmp(g_stack.batts[ix].baseState, "Absent") == 0)
{
g_stack.batts[ix].isPresent = false;
}
else
{
g_stack.batts[ix].isPresent = true;
extractStr(pLineStart, 64, g_stack.batts[ix].voltageState, sizeof(g_stack.batts[ix].voltageState));
extractStr(pLineStart, 73, g_stack.batts[ix].currentState, sizeof(g_stack.batts[ix].currentState));
extractStr(pLineStart, 82, g_stack.batts[ix].tempState, sizeof(g_stack.batts[ix].tempState));
extractStr(pLineStart, 100, g_stack.batts[ix].time, sizeof(g_stack.batts[ix].time));
extractStr(pLineStart, 121, g_stack.batts[ix].b_v_st, sizeof(g_stack.batts[ix].b_v_st));
extractStr(pLineStart, 130, g_stack.batts[ix].b_t_st, sizeof(g_stack.batts[ix].b_t_st));
g_stack.batts[ix].voltage = extractInt(pLineStart, 6);
g_stack.batts[ix].current = extractInt(pLineStart, 13);
g_stack.batts[ix].tempr = extractInt(pLineStart, 20);
g_stack.batts[ix].cellTempLow = extractInt(pLineStart, 27);
g_stack.batts[ix].cellTempHigh = extractInt(pLineStart, 34);
g_stack.batts[ix].cellVoltLow = extractInt(pLineStart, 41);
g_stack.batts[ix].cellVoltHigh = extractInt(pLineStart, 48);
g_stack.batts[ix].soc = extractInt(pLineStart, 91);
//////////////////////////////// Post-process ////////////////////////
g_stack.batteryCount++;
g_stack.currentDC += g_stack.batts[ix].current;
g_stack.avgVoltage += g_stack.batts[ix].voltage;
socAvg += g_stack.batts[ix].soc;
if(g_stack.batts[ix].isNormal() == false){ alarmCnt++; }
else if(g_stack.batts[ix].isCharging()){chargeCnt++;}
else if(g_stack.batts[ix].isDischarging()){dischargeCnt++;}
else if(g_stack.batts[ix].isIdle()){idleCnt++;}
else{ alarmCnt++; } //should not really happen!
if(g_stack.batteryCount == 1)
{
socLow = g_stack.batts[ix].soc;
tempLow = g_stack.batts[ix].cellTempLow;
tempHigh = g_stack.batts[ix].cellTempHigh;
}
else
{
if(socLow > g_stack.batts[ix].soc){socLow = g_stack.batts[ix].soc;}
if(tempHigh < g_stack.batts[ix].cellTempHigh){tempHigh = g_stack.batts[ix].cellTempHigh;}
if(tempLow > g_stack.batts[ix].cellTempLow){tempLow = g_stack.batts[ix].cellTempLow;}
}
}
}
//now update stack state:
g_stack.avgVoltage /= g_stack.batteryCount;
g_stack.soc = socLow;
if(tempHigh > 15000) //15C
{
g_stack.temp = tempHigh; //in the summer we highlight the warmest cell
}
else
{
g_stack.temp = tempLow; //in the winter we focus on coldest cell
}
if(alarmCnt > 0)
{
strcpy(g_stack.baseState, "Alarm!");
}
else if(chargeCnt == g_stack.batteryCount)
{
strcpy(g_stack.baseState, "Charge");
g_stack.soc = (int)(socAvg / g_stack.batteryCount);
}
else if(dischargeCnt == g_stack.batteryCount)
{
strcpy(g_stack.baseState, "Dischg");
}
else if(idleCnt == g_stack.batteryCount)
{
strcpy(g_stack.baseState, "Idle");
}
else
{
strcpy(g_stack.baseState, "Balance");
}
return true;
}
//////////////////////////////////////////////////
void prepareJsonOutput(char* pBuff, int buffSize)
{
memset(pBuff, 0, buffSize);
snprintf(pBuff, buffSize-1, "{\"soc\": %d, \"temp\": %d, \"currentDC\": %ld, \"avgVoltage\": %ld, \"baseState\": \"%s\", \"batteryCount\": %d, \"powerDC\": %ld, \"estPowerAC\": %ld, \"isNormal\": %s}", g_stack.soc,
g_stack.temp, g_stack.isNormal() ? "true" : "false");
}
//////////////////////////////////////////////////
void loop() {
#ifdef ENABLE_MQTT
mqttLoop();
#endif
ArduinoOTA.handle();
server.handleClient();
timer.run();
//if there are bytes availbe on serial here - it's unexpected
//when we send a command to battery, we read whole response
//if we get anything here anyways - we will log it
int bytesAv = Serial.available();
if(bytesAv > 0)
{
if(bytesAv > 63)
{
bytesAv = 63;
}
char buff[64+4] = "RCV:";
if(Serial.readBytes(buff+4, bytesAv) > 0)
{
digitalWrite(LED_BUILTIN, LOW);
delay(5);
digitalWrite(LED_BUILTIN, HIGH);//high is off
Log(buff);
}
}
}
//////////////////////////////////////////////////
#ifdef ENABLE_MQTT
#define ABS_DIFF(a, b) (a > b ? a-b : b-a)
//////////////////////////////////////////////////
void mqtt_publish_f(const char* topic, float newValue, float oldValue, float minDiff, bool force)
{
char szTmp[16] = "";
snprintf(szTmp, 15, "%.2f", newValue);
if(force || ABS_DIFF(newValue, oldValue) > minDiff)
{
mqttClient.publish(topic, szTmp, false);
}
}
//////////////////////////////////////////////////
void mqtt_publish_i(const char* topic, int newValue, int oldValue, int minDiff, bool force)
{
char szTmp[16] = "";
snprintf(szTmp, 15, "%d", newValue);
if(force || ABS_DIFF(newValue, oldValue) > minDiff)
{
mqttClient.publish(topic, szTmp, false);
}
}
//////////////////////////////////////////////////
void mqtt_publish_s(const char* topic, const char* newValue, const char* oldValue, bool force)
{
if(force || strcmp(newValue, oldValue) != 0)
{
mqttClient.publish(topic, newValue, false);
}
}
//////////////////////////////////////////////////
void pushBatteryDataToMqtt(const batteryStack& lastSentData, bool forceUpdate /* if true - we will send all data regardless if it's the same */)
{
Serial.println("entered publish");
mqtt_publish_f(MQTT_TOPIC_ROOT "soc", g_stack.soc, lastSentData.soc, 0, forceUpdate);
mqtt_publish_f(MQTT_TOPIC_ROOT "temp", (float)g_stack.temp/1000.0, (float)lastSentData.temp/1000.0, 0, forceUpdate);
mqtt_publish_i(MQTT_TOPIC_ROOT "estPowerAC", g_stack.getEstPowerAc(), lastSentData.getEstPowerAc(), 10, forceUpdate);
mqtt_publish_i(MQTT_TOPIC_ROOT "battery_count",g_stack.batteryCount, lastSentData.batteryCount, 0, forceUpdate);
mqtt_publish_s(MQTT_TOPIC_ROOT "base_state", g_stack.baseState, lastSentData.baseState , forceUpdate);
mqtt_publish_i(MQTT_TOPIC_ROOT "is_normal", g_stack.isNormal() ? 1:0, lastSentData.isNormal() ? 1:0, 0, forceUpdate);
Serial.println("finished publish");
}
//////////////////////////////////////////////////
void mqttLoop()
{
//if we have problems with connecting to mqtt server, we will attempt to re-estabish connection each 1minute (not more than that)
static unsigned long g_lastConnectionAttempt = 0;
//first: let's make sure we are connected to mqtt
const char* topicLastWill = MQTT_TOPIC_ROOT "availability";
if (!mqttClient.connected() && (g_lastConnectionAttempt == 0 || os_getCurrentTimeSec() - g_lastConnectionAttempt > 60)) {
if(mqttClient.connect("GarageBattery", MQTT_USER, MQTT_PASSWORD, topicLastWill, 1, true, "offline"))
{
Log("Connected to MQTT server: " MQTT_SERVER);
Serial.println("Connected to MQTT server!!!");
mqttClient.publish(topicLastWill, "online", true);
// test by TeZ
mqttClient.publish("home/grid_battery/tez", "cicciobello", true);
mqttClient.publish("testbytez", "232323", true);
}
else
{
Log("Failed to connect to MQTT server.");
Serial.println("Failed to connect to MQTT server.");
}
g_lastConnectionAttempt = os_getCurrentTimeSec();
}
//next: read data from battery and send via MQTT (but only once per MQTT_PUSH_FREQ_SEC seconds)
static unsigned long g_lastDataSent = 0;
if(mqttClient.connected() &&
os_getCurrentTimeSec() - g_lastDataSent > MQTT_PUSH_FREQ_SEC &&
sendCommandAndReadSerialResponse("pwr") == true)
{
Serial.println("Connected and sending to MQTT server!!!");
static batteryStack lastSentData; //this is the last state we sent to MQTT, used to prevent sending the same data over and over again
static unsigned int callCnt = 0;
parsePwrResponse(g_szRecvBuff);
bool forceUpdate = (callCnt % 20 == 0); //push all the data every 20th call
pushBatteryDataToMqtt(lastSentData, forceUpdate);
callCnt++;
g_lastDataSent = os_getCurrentTimeSec();
memcpy(&lastSentData, &g_stack, sizeof(batteryStack));
}
mqttClient.loop();
}
#endif //ENABLE_MQTT
Loading…
Cancel
Save