proszę oto uproszczony kod z wszystkimi zależnościami i logiką
/*╔══════════════════════════════════════════════════════════════╗║ WODOMIERZ — ESP32-C3 Super Mini ║║ Apator JS2.5-GI-02 ║║ Czujnik B (GPIO1) — liczenie impulsów ║║ Czujniki A (GPIO0) + C (GPIO3) — ambient (dynamiczny próg) ║╚══════════════════════════════════════════════════════════════╝
Zasada działania:
Tarcza wodomierza podzielona na pół: czarna / lustrzana
Dioda IR nadawcza w centrum tarczy (GPIO10, aktywna LOW)
Trzy fotorezystory na szczytach trójkąta wpisanego w tarczę
Kanał B (GPIO1) liczy impulsy — zbocze opadające = 1 impuls
Kanały A (GPIO0) i C (GPIO3) mierzą światło otoczenia
Próg dynamiczny = (A + C) / 2 + offset
Dzięki temu zmieniające się oświetlenie (słońce przez okno)nie wpływa na wykrywanie impulsów
Kalibracja:
Nalej dokładnie 1L wody i sprawdź ile impulsów zliczył Serial
Wpisz: LITERS_PER_PULSE = 1.0 / liczba_impulsow*/
<Arduino.h> <esp_timer.h>
// ════════════════════════════════════════════════════════════════// PINY// ════════════════════════════════════════════════════════════════static const int PIN_PHOTO_A = 0; // fotorezystor A — ambientstatic const int PIN_PHOTO_B = 1; // fotorezystor B — tarcza (główny)static const int PIN_PHOTO_C = 3; // fotorezystor C — ambientstatic const int PIN_IR_LED = 10; // dioda IR nadawcza (ON = LOW)
// ════════════════════════════════════════════════════════════════// KALIBRACJA// ════════════════════════════════════════════════════════════════static const float LITERS_PER_PULSE = 0.001f; // L/impuls — ustaw empiryczniestatic const int DYNAMIC_OFFSET = 200; // próg = ambient + offsetstatic const uint32_t MIN_PULSE_INTERVAL_US = 5000; // anty-drgania [µs]
// ════════════════════════════════════════════════════════════════// ZMIENNE// ════════════════════════════════════════════════════════════════static volatile int adc_a = 0, adc_b = 0, adc_c = 0;static volatile int dynamic_threshold = 500;static volatile uint32_t pulse_total = 0;static volatile uint32_t last_pulse_us = 0;static volatile uint8_t b_state_prev = 0;static float total_liters = 0.0f;
static esp_timer_handle_t adc_timer_handle = NULL;
// ════════════════════════════════════════════════════════════════// TIMER ADC — odpytuje czujniki co 5ms (200 Hz)// ════════════════════════════════════════════════════════════════static void IRAM_ATTR adc_poll_cb(void* arg) {int va = analogRead(PIN_PHOTO_A);int vb = analogRead(PIN_PHOTO_B);int vc = analogRead(PIN_PHOTO_C);
adc_a = va; adc_b = vb; adc_c = vc;
// Dynamiczny próg — kompensacja światła otoczeniaint ambient = (va + vc) / 2;dynamic_threshold = ambient + DYNAMIC_OFFSET;
// Zbocze opadające na B = impuls (tarcza przeszła lustro → czarny)uint8_t b_cur = (vb >= dynamic_threshold) ? 1 : 0;uint8_t b_prev = b_state_prev;b_state_prev = b_cur;
if (b_prev == 1 && b_cur == 0) {uint32_t now_us = (uint32_t)esp_timer_get_time();if ((now_us - last_pulse_us) >= MIN_PULSE_INTERVAL_US) {last_pulse_us = now_us;pulse_total++;}}}
// ════════════════════════════════════════════════════════════════// SETUP// ════════════════════════════════════════════════════════════════void setup() {Serial.begin(115200);delay(200);
// Dioda IR nadawcza — włączpinMode(PIN_IR_LED, OUTPUT);digitalWrite(PIN_IR_LED, LOW); // ON = LOW
// Wejścia ADCanalogReadResolution(12);pinMode(PIN_PHOTO_A, INPUT);pinMode(PIN_PHOTO_B, INPUT);pinMode(PIN_PHOTO_C, INPUT);
// Timer ADC co 5msesp_timer_create_args_t args = {};args.callback = adc_poll_cb;args.name = “adc_poll”;esp_timer_create(&args, &adc_timer_handle);esp_timer_start_periodic(adc_timer_handle, 5000); // 5000µs = 5ms
Serial.println(“=== WODOMIERZ START ===”);Serial.printf(“L/impuls: %.6f | offset: %d\n”, LITERS_PER_PULSE, DYNAMIC_OFFSET);}
// ════════════════════════════════════════════════════════════════// LOOP// ════════════════════════════════════════════════════════════════static uint32_t last_pulse_reported = 0;static uint32_t last_print_ms = 0;
void loop() {// Sprawdź czy przybyły nowe impulsyuint32_t current_total = pulse_total; // odczyt volatileif (current_total != last_pulse_reported) {uint32_t new_pulses = current_total - last_pulse_reported;last_pulse_reported = current_total;total_liters += (float)new_pulses * LITERS_PER_PULSE;
Serial.printf("Impuls! total_impulsow=%lu | %.3f L | ADC: A=%d B=%d C=%d | prog=%d\n",
current_total, total_liters,
(int)adc_a, (int)adc_b, (int)adc_c,
(int)dynamic_threshold);
}
// Co 2 sekundy wypisz stan nawet bez impulsuif (millis() - last_print_ms > 2000) {last_print_ms = millis();Serial.printf(“Stan: %.3f L | impulsy=%lu | A=%d B=%d C=%d | prog=%d\n”,total_liters, current_total,(int)adc_a, (int)adc_b, (int)adc_c,(int)dynamic_threshold);}
delay(10);}
________________________________________________________
Poprzedni kod (3 czujniki, kod Graya):
Wszystkie 3 kanały (A, B, C) montowane na tarczy.
Kierunek obrotu wykrywany przez tabelę Graya 3-bitową.
Stały próg ADC ustawiany ręcznie przez WWW.
Problem: zmieniające się oświetlenie (słońce) zawyżało progi i blokował zliczanie.
Nowy kod (1 czujnik + ambient):
Kanał B — jedyny liczący impulsy (zbocze opadające lustro→czarny).
Kanały A i C — mierzą tylko światło otoczenia a raczej starają się to robić.
Próg dynamiczny = (A + C) / 2 + offset — automatycznie podąża za zmianami oświetlenia.
Brak kodu Graya — prościej, i tak myślę że mniej podatne na błędy.
Brak detekcji kierunku.
Auto-kalibracja przez WWW — program sam wyznacza offset obserwując MIN/MAX kanału B gdy leci woda.
Dlaczego tak:
Okazało się że kanały A i C praktycznie nie reagowały na tarczę (a_tog=5M, c_tog=15k vs b_tog=85M w logach diagnostycznych) — tylko B był użyteczny. Zamiast marnować A i C na słaby odczyt tarczy, lepiej użyć ich do kompensacji światła otoczenia.
Poza tym ponieważ od samego początku zawsze kanał B wykazywał większą aktywność, tak myślę jeszcze tego nie potwierdziłem może to nie są trzy identyczne elementy optyczne?
i ta jedna jest lekko oddalona od środka układu czyli diody nadawczej białej.