Licznik impulsów (impulse counter) - Arduino IDE

Awatar użytkownika
klew
Posty: 8184
Rejestracja: czw cze 27, 2019 12:16 pm
Lokalizacja: Wrocław

bigthomas pisze: pn lis 30, 2020 1:27 pm @klew, udało Ci się coś pogrzebać dla liczniku impulsów w ESP32?
Szkoda mi trochę urządzenia bo ma dużo pinów do wykorzystania... a w planach miałem jeszcze kilka rzeczy tam podpiąć ;)
Jeszcze się do tego nie zabrałem.
Natomiast do listy niedziałających rzeczy dołączył Button ;)
Widzimy się na Supla Offline Party vol. 2 :!:
bigthomas
Posty: 234
Rejestracja: pn sie 12, 2019 3:35 pm

klew pisze: pn lis 30, 2020 1:32 pm
Jeszcze się do tego nie zabrałem.
Natomiast do listy niedziałających rzeczy dołączył Button ;)
To chyba jednak rozbiję na dwa urządzenia mój projekt :) bo widzę że z ESP32 nie szybko będzie rozwiązany problem... a wręcz będzie ich przybywać ;)
elmaya
Posty: 1482
Rejestracja: śr cze 27, 2018 5:48 pm
Lokalizacja: El Saucejo - Sevilla

if you use this "timer.cpp" the ESP32 will count the pulses. ;)

timer.rar
(1.27 KiB) Pobrany 111 razy

paste to: Documents\Arduino\libraries\SuplaDevice\src\supla
or edit as below:

Kod: Zaznacz cały

/*
 Copyright (C) AC SOFTWARE SP. Z O.O.
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 This program 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 General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include <Arduino.h>
#include <SuplaDevice.h>

#include "timer.h"

#if defined(ARDUINO_ARCH_ESP32)
#include <Ticker.h>
#endif

namespace {
#if defined(ARDUINO_ARCH_ESP8266)
ETSTimer supla_esp_timer;
ETSTimer supla_esp_fastTimer;

void esp_timer_cb(void *timer_arg) {
  (void)(timer_arg);
  SuplaDevice.onTimer();
}

void esp_fastTimer_cb(void *timer_arg) {
  (void)(timer_arg);
  SuplaDevice.onFastTimer();
}
#elif defined(ARDUINO_ARCH_ESP32)
Ticker supla_esp_timer;
Ticker supla_esp_fastTimer;

void esp_timer_cb() {
  SuplaDevice.onTimer();
}

void esp_fastTimer_cb() {
  SuplaDevice.onFastTimer();
}
#else
ISR(TIMER1_COMPA_vect) {
  SuplaDevice.onTimer();
}
ISR(TIMER2_COMPA_vect) {
  SuplaDevice.onFastTimer();
}
#endif
};  // namespace

namespace Supla {
void initTimers() {
#if defined(ARDUINO_ARCH_ESP8266)

  os_timer_disarm(&supla_esp_timer);
  os_timer_setfn(&supla_esp_timer, (os_timer_func_t *)esp_timer_cb, NULL);
  os_timer_arm(&supla_esp_timer, 10, 1);

  os_timer_disarm(&supla_esp_fastTimer);
  os_timer_setfn(&supla_esp_fastTimer, (os_timer_func_t *)esp_fastTimer_cb, NULL);
  os_timer_arm(&supla_esp_fastTimer, 1, 1);

#elif defined(ARDUINO_ARCH_ESP32)
  supla_esp_timer.attach_ms(10, esp_timer_cb);
  supla_esp_fastTimer.attach_ms(1, esp_fastTimer_cb);
#else
  // Timer 1 for interrupt frequency 100 Hz (10 ms)
  TCCR1A = 0;  // set entire TCCR1A register to 0
  TCCR1B = 0;  // same for TCCR1B
  TCNT1 = 0;   // initialize counter value to 0
  // set compare match register for 1hz increments
  OCR1A = 155;  // (16*10^6) / (100*1024) - 1 (must be <65536) == 155.25
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS12 and CS10 bits for 1024 prescaler
  TCCR1B |= (1 << CS12) | (1 << CS10);
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  sei();  // enable interrupts

  // TIMER 2 for interrupt frequency 2000 Hz (0.5 ms)
  cli();       // stop interrupts
  TCCR2A = 0;  // set entire TCCR2A register to 0
  TCCR2B = 0;  // same for TCCR2B
  TCNT2 = 0;   // initialize counter value to 0
  // set compare match register for 2000 Hz increments
  OCR2A = 249;  // = 16000000 / (32 * 2000) - 1 (must be <256)
  // turn on CTC mode
  TCCR2B |= (1 << WGM21);
  // Set CS22, CS21 and CS20 bits for 32 prescaler
  TCCR2B |= (0 << CS22) | (1 << CS21) | (1 << CS20);
  // enable timer compare interrupt
  TIMSK2 |= (1 << OCIE2A);
  sei();  // allow interrupts
#endif
}

};  // namespace Supla
bigthomas
Posty: 234
Rejestracja: pn sie 12, 2019 3:35 pm

elmaya pisze: pn lis 30, 2020 9:29 pm
Yes, it works.
Thank you elmaya!
elmaya
Posty: 1482
Rejestracja: śr cze 27, 2018 5:48 pm
Lokalizacja: El Saucejo - Sevilla

@klew The ball is on your roof ;)
Awatar użytkownika
klew
Posty: 8184
Rejestracja: czw cze 27, 2019 12:16 pm
Lokalizacja: Wrocław

elmaya pisze: śr gru 02, 2020 11:24 am @klew The ball is on your roof ;)
Thanks. I didn't have time yet to check it. How does it work? It isn't done on interruptions, because those require part of application to be copied to RAM and it doesn't work for virtual methods in classes (that's where I stuck previously).
So if not interruptions, then how? Does it run on separate thread/core?
Widzimy się na Supla Offline Party vol. 2 :!:
elmaya
Posty: 1482
Rejestracja: śr cze 27, 2018 5:48 pm
Lokalizacja: El Saucejo - Sevilla

Ticker does not use interrupts but runs on core 0. "arduino uses core 1"

it works fine even when disconnected from the network "I have only noticed a small difference with the buttons, less response when disconnected but nothing dramatic(in combination with 2 impulse counters and 1 blind)"


Also consider completing "esp32_wifi.h" by adding:

Kod: Zaznacz cały

  void fillStateData(TDSC_ChannelState &channelState) {
    channelState.Fields |= SUPLA_CHANNELSTATE_FIELD_IPV4 |
                           SUPLA_CHANNELSTATE_FIELD_MAC |
                           SUPLA_CHANNELSTATE_FIELD_WIFIRSSI |
                           SUPLA_CHANNELSTATE_FIELD_WIFISIGNALSTRENGTH;
    channelState.IPv4 = WiFi.localIP();
    WiFi.macAddress(channelState.MAC);
    int rssi = WiFi.RSSI();
    channelState.WiFiRSSI = rssi;
    if (rssi > -50) {
      channelState.WiFiSignalStrength = 100;
    } else if (rssi <= -100) {
      channelState.WiFiSignalStrength = 0;
    } else {
      channelState.WiFiSignalStrength = 2 * (rssi + 100);
    }
  }
Awatar użytkownika
klew
Posty: 8184
Rejestracja: czw cze 27, 2019 12:16 pm
Lokalizacja: Wrocław

elmaya pisze: śr gru 02, 2020 12:33 pm Ticker does not use interrupts but runs on core 0. "arduino uses core 1"

it works fine even when disconnected from the network "I have only noticed a small difference with the buttons, less response when disconnected but nothing dramatic"


Also consider completing "esp32_wifi.h" by adding:

Kod: Zaznacz cały

  void fillStateData(TDSC_ChannelState &channelState) {
    channelState.Fields |= SUPLA_CHANNELSTATE_FIELD_IPV4 |
                           SUPLA_CHANNELSTATE_FIELD_MAC |
                           SUPLA_CHANNELSTATE_FIELD_WIFIRSSI |
                           SUPLA_CHANNELSTATE_FIELD_WIFISIGNALSTRENGTH;
    channelState.IPv4 = WiFi.localIP();
    WiFi.macAddress(channelState.MAC);
    int rssi = WiFi.RSSI();
    channelState.WiFiRSSI = rssi;
    if (rssi > -50) {
      channelState.WiFiSignalStrength = 100;
    } else if (rssi <= -100) {
      channelState.WiFiSignalStrength = 0;
    } else {
      channelState.WiFiSignalStrength = 2 * (rssi + 100);
    }
  }
Ok, so it works, but SuplaDevice code is not thread safe. I was thinking about moving it to another core and putting into some loop, but this ticker will actually do the same and code looks more consistent with esp8266/avr implementation.
I'll have still to make it thread safe.

I'll add this fillStateData implementation. It was waiting in my queue, but due to lack of working timers, I didn't have any motivation to work on those other topics, when main functionalities were not working at all :)
Widzimy się na Supla Offline Party vol. 2 :!:
Awatar użytkownika
Hrumque
Posty: 275
Rejestracja: pn cze 27, 2022 10:11 am
Lokalizacja: Opole

klew pisze: pt wrz 11, 2020 8:45 pm
// w setup:
auto *ic = new Supla::Sensor::ImpulseCounter(PIN_DO_IMPULSÓW, true, false, 50);
[/code]
1. Czy licznik impulsów używa przerwania (w sensie - czy każdy impuls/zbocze opadające zostanie zliczone? dałoby to możliwość zliczania szybkich impulsów, czy wręcz pomiar RPM itp, czy testowany jest co X ms stan pinu i tylko tyle?)
2. BARDZO by się przydała druga funkcja zliczania w obu kierunkach - drugi pin PIN_KIERUNEK_ZLICZANIA_IMPULSÓW - gdy występuje impuls który jest zliczany, to zliczenie na (+1) lub (-1) zależy od stanu tego drugiego pinu.
A jakby jeszcze był 3ci pin PIN_DO_RESETOWANIA_IMPULSÓW to już miodzio ;)
Dałoby to możliwości naprawdę wiele:
- podłączenie enkoderów A/B (koder diodowy do wejścia impulsu - obrót enkodera w dowolną stronę wywołuje zbocze opadające na wejściu impulsu, a zależnie od tego w którą stronę się kręciło - w momencie impulsu (i przerwania nim wygenerowanego) na drugim wejściu przy zboczu opadającym jest 0 lub 1
Obrazek

- monitorowanie pozycji i kierunku ruchu (pozycji) bramy, windy, wiadra w studni itp urządzeń (gdzie znamy tylko kierunek ruchu, oraz możemy zliczać impulsy np silnika napędowego, a krańcówka w pozycji zero - zerowałaby licznik impulsów, więc zawsze wiedzielibyśmy idealnie o pozycji aktualnej, a nie tak, że po X ruchach tam i spowrotem się przesunie o kilka impulsów i rozjedzie wszystko)

- zastosowanie prostych liczników energii (dających na wyjściu tylko impuls przy zliczeniu - bez wskazywania kierunku przepływu energii) z osobnym sygnałem np z falownika (czy produkujemy prąd, czy go konsumujemy - a tu wystarczy fotoelement przylepiony przy diodzie statusu na obudowie falownika, by łapać stan pracy)

- liczniki wody wpływającej i wypływającej np. z zbiornika deszczówki (sam licznik-impulsator daje takie same impulsy w obu kierukach przepływu, drugi sensor kierunku przepływu (wystarczy pływak z magnesem i kontaktron, w zależności od kierunku przepływu byłby przesuwany w lewo lub prawo) dałby pełny nadzór nad obiektem

itd. Pomysłów może być naprawdę wiele ;)
Awatar użytkownika
klew
Posty: 8184
Rejestracja: czw cze 27, 2019 12:16 pm
Lokalizacja: Wrocław

Odpisuję po czasie, bo gdzieś mi ten post umknął :)
Hrumque pisze: pn lip 04, 2022 7:42 am 1. Czy licznik impulsów używa przerwania (w sensie - czy każdy impuls/zbocze opadające zostanie zliczone? dałoby to możliwość zliczania szybkich impulsów, czy wręcz pomiar RPM itp, czy testowany jest co X ms stan pinu i tylko tyle?)
Aktualny licznik impulsów sprawdza stan wejścia cyklicznie i jeszcze dodatkowo używa "filtrów" wymagających, aby stan był utrzymywany przez jakiś czas (aby nie zliczać tzw. "drgań styków").
Nie ma technicznego problemu aby dodać inny typ licznika impulsów, który zlicza inne rzeczy. Natomiast problemem jest brak czasu z mojej strony na takie niszowe tematy :). Zawsze można samemu dopisać i podzelić się z innymi (poprzez przygotowanie kodu i zrobienie pull request do biblioteki)
Kanał licznika impulsów nie nadaje się do pomiaru RPM, bo on tylko "liczy impulsy". Tego typu pomiar RPM to coś co jest planowane np. do kanału prędkości wiatru (często tam oblicza się prędkość wiatru na podstawie częstotliwości impulsów). Są też na forum dostępne projekty, które to robią.
Hrumque pisze: pn lip 04, 2022 7:42 am 2. BARDZO by się przydała druga funkcja zliczania w obu kierunkach - drugi pin PIN_KIERUNEK_ZLICZANIA_IMPULSÓW - gdy występuje impuls który jest zliczany, to zliczenie na (+1) lub (-1) zależy od stanu tego drugiego pinu.
W Supli licznik impulsów przechowuje dane w zmiennych typu "unsigned integer" (na 64 bitach). Więc o ile doliczenie do 0 w dół jest możliwe, to liczb ujemnych się tutaj nie pokaże. Może lepiej byłoby robić osobny licznik na dane z drugiego pinu?
Hrumque pisze: pn lip 04, 2022 7:42 am A jakby jeszcze był 3ci pin PIN_DO_RESETOWANIA_IMPULSÓW to już miodzio ;)
Możesz to zrealizować poprzez ustawienie Button-u na tym 3-cim pinie i ustawić w nim akcję resetowania licznika impulsów.
Hrumque pisze: pn lip 04, 2022 7:42 am - monitorowanie pozycji i kierunku ruchu (pozycji) bramy, windy, wiadra w studni itp urządzeń (gdzie znamy tylko kierunek ruchu, oraz możemy zliczać impulsy np silnika napędowego, a krańcówka w pozycji zero - zerowałaby licznik impulsów, więc zawsze wiedzielibyśmy idealnie o pozycji aktualnej, a nie tak, że po X ruchach tam i spowrotem się przesunie o kilka impulsów i rozjedzie wszystko)
Brzmi jak fajny projekt DIY, ale nie ma zastosowania do Supli. Kanały bram pokazują tylko 3 stany: całkowicie otwarta, częściowo otwarta, zamknięta. Nic więcej w takim kanale się nie wyświetli.
Nawet jakby jakoś rozbudować reprezentację stanu bramy, to robienie tego w oparciu o licznik impulsów nie jest dobrym pomysłem. Dużo łatwiej po prostu zaimplementować nowy komponent, który reaguje na te wszystkie zbocza i ustawia odpowiedni stan bramy.
Hrumque pisze: pn lip 04, 2022 7:42 am - zastosowanie prostych liczników energii (dających na wyjściu tylko impuls przy zliczeniu - bez wskazywania kierunku przepływu energii) z osobnym sygnałem np z falownika (czy produkujemy prąd, czy go konsumujemy - a tu wystarczy fotoelement przylepiony przy diodzie statusu na obudowie falownika, by łapać stan pracy)
Falownik nie wie jaki jest bilans energii na liczniku, więc wystawienie przez niego sygnału nic Ci nie da. Tutaj jest po prostu potrzebny licznik dwukierunkowy.
Np. falownik "produkuje energię" 100 W i zapala diodę "produkcja". Masz włączony czajnik, który pobiera 2000 W. Przez licznik nadal pobierana jest energia o mocy 1900 W, mimo tego, że falownik produkuje ;).
Hrumque pisze: pn lip 04, 2022 7:42 am - liczniki wody wpływającej i wypływającej np. z zbiornika deszczówki (sam licznik-impulsator daje takie same impulsy w obu kierukach przepływu, drugi sensor kierunku przepływu (wystarczy pływak z magnesem i kontaktron, w zależności od kierunku przepływu byłby przesuwany w lewo lub prawo) dałby pełny nadzór nad obiektem
Tutaj lepiej zrobić sobie jakiś czujnik poziomu cieczy.
Widzimy się na Supla Offline Party vol. 2 :!:
ODPOWIEDZ

Wróć do „Arduino IDE”