niedziela, 10 czerwca 2018

ESP8266 i pomiar długości impusu z BLYNkiem w tle

LED w liczniku energii miga radośnie. I jak na razie bezproduktywnie. Pomysłowy sensor chińskiej produkcji bez problemu odbiera wszystkie błyski licznikowego LEDa ale D1 MINI nic z tym sensownego nie potrafi zrobić. Na razie. A temat wydaje się banalnie prosty i brzmi: jak w miarę dokładnie pomierzyć długość impulsu w zakresie 300ms - 36 s podawany na dowolny pin mikrokontrolera. Czemu w takim zakresie? Odpowiada to mierzonemu przez licznik energii zakresowi mocy. W tym przypadku jest to 10 kW - do 100 W dla 1000 impulsów LED/kWh. 10 kW to maksymalna moc wynikająca z zabezpieczeń. A 100 W to praktycznie  minimum poboru energii patrząc na ilość domowych urządzeń włączonych na stałe. Jak pomierzyć odcinek czasu pomiędzy dwoma błyśnięciami LEDa - to temat  dzisiejszego wpisu.
Pomiar czasu to niemalże podstawowa procedura mikrokontrolera w systemach automatyki. Za pomocą Arduino IDE można to zrobić na kilka sposobów

  • prosto - pulseIn  - funkcja Arduini IDE 
  • mój ulubiony sposób - pomiar czasu w poolingu
  • profesjonalnie - pomiar czasu z wykorzystaniem przerwań

Ad1 Funkcja Arduini IDE - pulseIn()

To podstawowa funkcja Arduino IDE pomiaru czasu wysokiego lub niskiego stanu na określonym pinie mikrokontrolera.  Jest banalnie prosta w użyciu

int els_pin = 4;
void elssetup() {
  pinMode(els_pin, INPUT);
}
void elsakcja() {
  long duration;
  duration = pulseIn(els_pin, LOW);
  Serial.println(duration);
}

Ponoć działa też na ESP8266. Nie sprawdziłem bo funkcja pulseIn  ma jedną zasadniczą wadę. Wstrzymuje pracę programu przez cały czas pomiaru długości  impulsu. Dla zakładanych maksymalnie 40 sekund długości impulsu funkcja rozwali totalnie działanie całego systemu. pulseIn() - do kosza


Ad 2 Pomiar czasu w poolingu

Jak już wielokrotnie deklarowałem pooling (cykliczne odpytywanie) to mój ulubiony sposób obsługi różnych procedur. Przede wszystkim dlatego, że używa go podstawowa aplikacja zarządzająca moimi  systemami IoT - BLYNK. I ulubiona biblioteka zegarowa timers.h także. To w zasadzie wyczerpuje znamiona uzasadnienia ale jest jeszcze jedno bardzo istotne - pooling jak do tej pory nigdy mnie nie zawiódł nawet w bardzo skomplikowanych, jak na mnie, projektach. Tak naprawdę to bardzo prosta i klarowna procedura dająca się łatwo konfigurować i testować.

Jak w moim programie działa pooling do odczytu długości impulsu? Banalnie prosto
  1. czytam w kółko port czujnika LED licznika energii
  2. jeśli stan portu uległ zmianie odejmuję od aktualnego czasu poprzednio zapamiętany i dostaję wynik długości impulsu
  3. zapamiętuję aktualny czas i wracam do punktu 1
Kod jest jeszcze prostszy


void elsakcja() {
  unsigned  long duration;
  if (stan_els == digitalRead(els_pin)); else {
    stan_els = digitalRead(els_pin);
    unsigned long   temp_time_els = millis();      // zapamiętanie wartości czasu
    duration = temp_time_els - time_els;
    time_els = temp_time_els;
    Serial.println(duration);
  }
}

A efekt?





Program w poolingu obsługuje pomiar długości impulsu, BLYNKa i bibliotekę Timers.h. Pomimo tego pomiar  daje dokładność na poziomie 0,01%. dla najkrótszych czasów impuslsów (największych mocy) Całkiem nieźle. Pomiary są stabilne i powtarzalne a cały program pracuje bez zacięć i przerw.
Co prawda program nie jest zbyt rozbudowany toteż nie wiadomo jaki wpływ na pomiar długości impulsu będzie miał ostateczny kształt programu ale jak na razie trzeba uznać pomiar w poolingu za absolutnie zadowalający.


Ad2 Pomiar czasu z wykorzystaniem przerwań

To podstawowy i zalecany przez wszystkie podręczniki programowania sposób pomiaru czasu. Zmiana stanu (z 0 > 1 lub odwrotnie) przerywa działanie programu głównego uruchamiając podprogram obsługi przerwania.



W podprogramie rejestrowany jest aktualny czas i porównywany z czasem z poprzedniego przerwania. Różnica obu czasów daje długość mierzonego impulsu. Następnie program powraca do wykonywania pętli głównej kodu.

Gotowy kod do licznika energii i to działający z BLYNKiem już ktoś przygotował i znajduje się tu>>>>>
Najważniejsza część programu to obsługa przerwania uruchamiana poleceniem

attachInterrupt(els_pin, onPulse, FALLING);

Przerwanie ustawione jest na porcie els_port, na zboczu opadającym z 1 > 0 i wywołuje podprogram onPulse

void onPulse() // The interrupt routine
{

  lastTime = pulseTime;
  pulseTime = micros();

  pulseCount++;

  power = (3600000000.0 / (pulseTime - lastTime))/ppwh; //Calculate power
 
  elapsedkWh = (1.0*pulseCount/(ppwh*1000)); // convert wh to kwh

  Serial.print(power,4);  Serial.print(" ");  Serial.println(elapsedkWh,3);
}

Wszystko wygląda łatwo i przejrzyście ale ...
Programy z przerwaniami to jeden z najtrudniejszych (jak dla mnie) elementów programowania. Przerwania stosuje się do obsługi przypadkowych zdarzeń. Nie znany jest więc czas wystąpienia ani miejsce w kodzie gdzie program zostanie na chwilę przerwany. Jeśli cokolwiek pójdzie nie tak ( z powodu naszego błędu, błędu kompilacji czy siły wyższej) znalezienie przyczyny bez specjalnych sprzętowych narzędzi typu debugger graniczy z cudem. Np. wystarczy nie dodać słynnego słówka volatile do zmiennej obsługiwanej w przerwaniu i nasz program może " nie zauważyć skutków działania przerwania.
Przerwania to potężne narzędzie programowania mikrokontrolerów i  niezbędne np. w systemach wielowątkowych ale należy je używać jak strzelbę - z pełną świadomością ewentualnych skutków.

Akurat program obsługi czujnika LED licznika energii nie powinien sprawić kłopotów ale dla przezorności warto zmienić deklaracje zmiennych w tym programie na

volatile long pulseCount = 0;   //Used to measure power.
volatile unsigned long pulseTime,lastTime;
volatile double power, elapsedkWh; //power and energy

Za to skutek działania programu pomiaru czasu impulsu opartego na przerwaniach jest wręcz rewelacyjny




Walka pomiędzy poolingiem a przerwaniem wydaje się nierozstrzygnięta. Obie więc metody będą brane pod uwagę w trakcie dalszych prac nad systemem monitoringu licznika energii.

LEDa licznika energii zasymulowałem LEDem na płytce sterowanym z D1MINI. Żółty suwak zmienia częstotliwość błysków. Długość Świecenia LEDa jest stała - ok 10 ms. Czas przerwy jest zmieniany suwakiem od 1 do 400 x 100ms to znaczy od 100 ms do 40 sek co odpowiada mocy od 32 kW do 100 W (około).

Który ze sposobów pomiaru czasu pozostanie na placu boju czas i ewentualny ciąg dalszy pokaże ...

Poprzednie wpisy z tego tematu
http://100-x-arduino.blogspot.com/2018/06/czujnik-led-licznika-energii.html


Przydatne linki
http://www.instructables.com/id/Esp8266-12-blynk-wireless-electric-power-meter/
https://learn.openenergymonitor.org/electricity-monitoring/pulse-counting/interrupt-based-pulse-counter
https://edu.pjwstk.edu.pl/wyklady/swb/scb/SWBwyklad10i.pdf
http://www.avdweb.nl/arduino/libraries/frequency-period-counter.html
143

2 komentarze: