Devi considerare molti fattori quando cerchi di creare un dispositivo generico, data/ora ed alimentazione possono essere molto importanti e non così semplici da gestire.
Gestione data ed ora
Un problema che deve essere gestito è come recuperare l’orario e come gestirla.
Ci sono molte possibilità, alcuni usano l’ora UTC e la convertono nell’app WEB locale, alcuni memorizzano il timestamp iniziale e così via.
Nella mia centralina vorrei memorizzare direttamente l’ora locale iniziale e non aggiungo il dispositivi RTC.
Quindi ho intenzione di ottenere l’orario da un server NTP (Network Time Protocol) ed impostare il DST (Fuso Orario).
WiFiUDP ntpUDP;
// By default 'pool.ntp.org' is used with 60 seconds update interval and
// no offset
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 0*60*60, 60*60*1000);
[...]
timeClient.begin();
[...]
timeClient.update();
[...]
if (timeClient.forceUpdate()){
unsigned long epoch = timeClient.getEpochTime();
DEBUG_PRINTLN(epoch);
DEBUG_PRINTLN(F("NTP Time retrieved."));
adjustTime(epoch);
fixedTime = true;
}
Non implemento tutte le configurazioni dell’ora legale, quindi se hai bisogno di aggiungere il tuo fuso orario con la gestione dell’ora legale chiedimi e lo aggiungo sia al codice dell’esp8266 che alla parte WEB.
Puoi trovare tutte le informazioni sulla gestione del fuso orario di Arduino nel mio articolo “Protocollo NTP, fuso orario e ora legale (DST) con esp8266, esp32 o Arduino“.
Per gestire parzialmente l’ora aggiungo la gestione GTM ma se selezioni il DST sostituirà GTM.
time_t getLocalTime(void){
Timezone tz = getTimezoneData(codeDST);
return tz.toLocal(now());
}
Timezone getTimezoneData(const String code){
// DEBUG_PRINT("CODE DST RETRIEVED: ");
// DEBUG_PRINTLN(code);
if (code==F("AETZ")){
// Australia Eastern Time Zone (Sydney, Melbourne)
TimeChangeRule aEDT = {"AEDT", First, Sun, Oct, 2, 660}; // UTC + 11 hours
TimeChangeRule aEST = {"AEST", First, Sun, Apr, 3, 600}; // UTC + 10 hours
Timezone tzTmp = Timezone(aEDT, aEST);
return tzTmp;
}else if (code==F("CET")){
// DEBUG_PRINTLN(F("CET FIND"));
// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time
Timezone tzTmp = Timezone(CEST, CET);
return tzTmp;
}else if(code==F("MSK")){
// Moscow Standard Time (MSK, does not observe DST)
TimeChangeRule msk = {"MSK", Last, Sun, Mar, 1, 180};
Timezone tzTmp = Timezone(msk);
return tzTmp;
}else if(code==F("UK")){
// United Kingdom (London, Belfast)
TimeChangeRule BST = {"BST", Last, Sun, Mar, 1, 60}; // British Summer Time
TimeChangeRule GMT = {"GMT", Last, Sun, Oct, 2, 0}; // Standard Time
Timezone tzTmp = Timezone(BST, GMT);
return tzTmp;
}else if(code==F("USCTZ")){
// US Central Time Zone (Chicago, Houston)
TimeChangeRule usCDT = {"CDT", Second, Sun, Mar, 2, -300};
TimeChangeRule usCST = {"CST", First, Sun, Nov, 2, -360};
Timezone tzTmp = Timezone(usCDT, usCST);
return tzTmp;
}else if(code==F("USMTZ")){
// US Mountain Time Zone (Denver, Salt Lake City)
TimeChangeRule usMDT = {"MDT", Second, Sun, Mar, 2, -360};
TimeChangeRule usMST = {"MST", First, Sun, Nov, 2, -420};
Timezone tzTmp = Timezone(usMDT, usMST);
return tzTmp;
}else if(code==F("ARIZONA")){
// Arizona is US Mountain Time Zone but does not use DST
TimeChangeRule usMST = {"MST", First, Sun, Nov, 2, -420};
Timezone tzTmp = Timezone(usMST);
return tzTmp;
}else if(code==F("USPTZ")){
// US Pacific Time Zone (Las Vegas, Los Angeles)
TimeChangeRule usPDT = {"PDT", Second, Sun, Mar, 2, -420};
TimeChangeRule usPST = {"PST", First, Sun, Nov, 2, -480};
Timezone tzTmp = Timezone(usPDT, usPST);
return tzTmp;
}else if(code==F("UTC")){
// UTC
TimeChangeRule utcRule = {"UTC", Last, Sun, Mar, 1, 0}; // UTC
Timezone tzTmp = Timezone(utcRule);
return tzTmp;
}else{
// UTC
// DEBUG_PRINT("TIME_OFFSET ");
int to = timeOffset/60;
// DEBUG_PRINTLN(to);
TimeChangeRule utcRule = {"GTM", Last, Sun, Mar, 1, to}; // UTC
Timezone tzTmp = Timezone(utcRule);
return tzTmp;
}
}
Un altro passaggio che voglio gestire è prendere l’orario dall’Inverter.
L’inverter ha un RTC (Real Time Clock) interno, e ho implementato una chiamata per ottenere questa informazione, come puoi vedere nell’articolo Protocollo di comunicazione ABB Aurora PV tramite MAX485 / MAX3485
The code is quite simple:
Aurora inverter = Aurora(2, &Serial, 5);
[...]
inverter.begin();
[...]
Aurora::DataTimeDate timeDate = inverter.readTimeDate();
Timezone tz = getTimezoneData(codeDST);
tm tempTime = timeDate.getDateTime();
time_t currentUTCTime = tz.toUTC(mktime(&tempTime));
adjustTime(currentUTCTime);
Ulteriori informazioni su come recuperare i dati dall’inverter Aurora sull’articolo “Libreria Inverter ABB Aurora PV per Arduino, esp8266 and esp32“
Alimentazione
Ho creato questa centralina in sostituzione di quella vecchia. L’alimentazione della centralina precedente veniva presa direttamente dall’inverter, quindi quando non c’è il sole non c’è alimentazione, questo era giusto per la centralina precedente che utilizzava un protocollo MQTT per inviare i dati direttamente a un sito centrale, ma io intendo creare una centralina “autonoma” senza sito esterno o altro, quindi utilizzo la mia vecchia idea di un UPS creato con materiale semplice, lo potete vedere in questo articolo Power bank di emergenza fatto in casa , e riesce a funzionare per ore senza alimentazione.
Per ridurre le dimensioni utilizzo uno shield creato per WeMos D1, molto compatto:
You can find It here on AliExpress
L’ultima versione di questo shield con una saldatura inserisce direttamente il valore analogico della batteria nel pin A0 con un partitore di tensione, in modo da poter testare direttamente il valore di carica, ma è probabile che trovi la versione precedente e quindi devi usare partitore di tensione per mettere il valore analogico sul pin, maggiori informazioni in questo articolo “Partitore di tensione (voltage divider): calcolatore e applicazioni“.
Per la batteria utilizzo una resistenza da 10kΩ e 20kΩ.
float getBatteryVoltage(){
//************ Measuring Battery Voltage ***********
float sample1 = 0;
for (int i = 0; i < 100; i++) {
sample1 = sample1 + analogRead(A0); //read the voltage from the divider circuit
delay(2);
}
sample1 = sample1 / 100;
DEBUG_PRINT(F("AnalogRead..."));
DEBUG_PRINTLN(sample1);
float batVolt = (sample1 * 3.3 * (BAT_RES_VALUE_VCC + BAT_RES_VALUE_GND) / BAT_RES_VALUE_GND) / 1023;
return batVolt;
}
Ho anche rielaborato un design del portabatteria per standardizzare la soluzione.
Quindi uso dupont per dividere il valore e XH2.54 2p 2.54mm per collegarmi allo shield.
You can buy dupont box here AliExpress
You can buy XH2.54 pins box here AliExpress XH2.54 2.54mm AliExpress PH2.0 2.0mm
Ora puoi esporre il valore di carica e creare il grafico relativo.
Grazie
- Centralina Web per inverter ABB (ex Power One) Aurora (WIM): intro al progetto
- Centralina Web per inverter ABB Aurora (WIM): connessione Arduino all’RS-485
- Centralina Web per inverter ABB Aurora (WIM): dispositivi di archiviazione
- Centralina Web per inverter ABB Aurora (WIM): debug e notifica
- Centralina Web per inverter ABB Aurora (WIM): impostare l’ora e UPS
- Centralina Web per inverter ABB Aurora (WIM): configurazione WIFI e server REST
- Centralina Web per inverter ABB Aurora (WIM): WebSocket e Web Server
- Centralina Web per inverter ABB Aurora (WIM): cablaggio e PCB
- Centralina Web per inverter ABB Aurora (WIM): caricare il programma ed il front end
- Centralina Web per inverter ABB Aurora (WIM): scatola stampata in 3D e completamento
- Centralina Web per inverter ABB Aurora (WIM): riparare l’errore E013
GitHub repository con tutto il codice FE (transpilato) e BE