Omówienie sytuacji aktualnej
Tak wygląda log, który będę omawiał
channel: 2, temperature: 20.90 *C, humidity: 62 %, Num of measures: 3 lb: 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, sb: 4,-4,-4, 4, 4, 4,-4, 4, 4,-4, 4, 4,-4,-4,-4, 4,-4,-4,-4,-4, 4, 4,-4, 4,-4,-3,-3, 3,-3,-3, 3, 3, 3, 3, 3,-3,-3, b: 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, pre 0-0: (49049, 1944, 540), pre 1-1: (49051, 1948, 544), pre 2-2: (49055, 3833, 539), pre 3-3: (49060, 3910, 514), pre 4-4: (49064, 3849, 547), pre 5-5: (49069, 3608, 780), pre 6-6: (49073, 3817, 547), pre 7-7: (49076, 1944, 544), pre 8-8: (49078, 1937, 531), pre 9-9: (49087, 8644, 548), bit 0: (49091, 3812, 544), (49226, 3807, 553), (49361, 3823, 545), (49497, 3806, 558), (0, 0, 0), (0, 0, 0), bit 1: (49094, 1925, 523), (49229, 1911, 533), (49364, 1941, 527), (49500, 1908, 560), (0, 0, 0), (0, 0, 0), bit 2: (49096, 1932, 532), (49231, 1916, 544), (49367, 1915, 533), (49502, 1888, 560), (0, 0, 0), (0, 0, 0), bit 3: (49101, 3851, 537), (49235, 3848, 532), (49372, 3848, 548), (49507, 3830, 550), (0, 0, 0), (0, 0, 0), bit 4: (49105, 3862, 522), (49241, 3860, 520), (49376, 3851, 529), (49511, 3841, 547), (0, 0, 0), (0, 0, 0), bit 5: (49110, 3836, 536), (49245, 3848, 540), (49380, 3843, 537), (49515, 3827, 545), (0, 0, 0), (0, 0, 0), bit 6: (49112, 1944, 532), (49247, 1927, 549), (49382, 1953, 527), (49518, 1955, 533), (0, 0, 0), (0, 0, 0), bit 7: (49117, 3839, 545), (49252, 3845, 531), (49387, 3845, 539), (49522, 3857, 515), (0, 0, 0), (0, 0, 0), bit 8: (49121, 3843, 533), (49256, 3856, 516), (49391, 3835, 545), (49526, 3857, 535), (0, 0, 0), (0, 0, 0), bit 9: (49123, 1964, 528), (49258, 1971, 533), (49393, 1955, 541), (49529, 1940, 560), (0, 0, 0), (0, 0, 0), bit 10: (49128, 3856, 524), (49263, 3831, 545), (49398, 3848, 520), (49533, 3833, 531), (0, 0, 0), (0, 0, 0), bit 11: (49132, 3857, 535), (49267, 3867, 533), (49402, 3856, 552), (49538, 3861, 523), (0, 0, 0), (0, 0, 0), bit 12: (49134, 1955, 533), (49269, 1936, 540), (49404, 1937, 547), (49541, 1957, 543), (0, 0, 0), (0, 0, 0), bit 13: (49137, 1953, 527), (49272, 1942, 522), (49408, 1956, 524), (49543, 1957, 519), (0, 0, 0), (0, 0, 0), bit 14: (49139, 1948, 532), (49274, 1934, 558), (49410, 1950, 526), (49545, 1952, 524), (0, 0, 0), (0, 0, 0), bit 15: (49144, 3853, 527), (49280, 3832, 544), (49415, 3837, 547), (49550, 3858, 526), (0, 0, 0), (0, 0, 0), bit 16: (49146, 1944, 528), (49282, 1940, 528), (49417, 1944, 520), (49552, 1959, 529), (0, 0, 0), (0, 0, 0), bit 17: (49148, 1933, 535), (49284, 1959, 533), (49419, 1948, 560), (49554, 1953, 535), (0, 0, 0), (0, 0, 0), bit 18: (49152, 1973, 515), (49287, 1954, 518), (49422, 1946, 518), (49557, 1935, 533), (0, 0, 0), (0, 0, 0), bit 19: (49154, 1964, 552), (49289, 1956, 548), (49424, 1968, 560), (49559, 1965, 531), (0, 0, 0), (0, 0, 0), bit 20: (49159, 3849, 527), (49294, 3855, 529), (49429, 3819, 529), (49564, 3850, 522), (0, 0, 0), (0, 0, 0), bit 21: (49163, 3854, 522), (49298, 3842, 526), (49433, 3899, 529), (49568, 3862, 510), (0, 0, 0), (0, 0, 0), bit 22: (49165, 1943, 545), (49300, 1941, 535), (49435, 1941, 543), (49570, 1951, 533), (0, 0, 0), (0, 0, 0), bit 23: (49170, 3849, 535), (49305, 3839, 545), (49440, 3848, 528), (49575, 3838, 554), (0, 0, 0), (0, 0, 0), bit 24: (49172, 1940, 524), (49307, 1997, 519), (49442, 1954, 522), (49577, 1943, 545), (0, 0, 0), (0, 0, 0), bit 25: (49174, 1959, 533), (49309, 1945, 547), (49444, 1945, 531), (49580, 1292, 560), (0, 0, 0), (0, 0, 0), bit 26: (49177, 1958, 526), (49312, 1947, 533), (49447, 1943, 533), (0, 0, 0), (0, 0, 0), (0, 0, 0), bit 27: (49181, 3865, 527), (49316, 3861, 543), (49452, 3857, 535), (0, 0, 0), (0, 0, 0), (0, 0, 0), bit 28: (49184, 1965, 523), (49319, 1953, 523), (49455, 1953, 531), (0, 0, 0), (0, 0, 0), (0, 0, 0), bit 29: (49186, 1936, 560), (49321, 1936, 560), (49457, 1960, 532), (0, 0, 0), (0, 0, 0), (0, 0, 0), bit 30: (49190, 3817, 547), (49326, 3828, 536), (49461, 3833, 543), (0, 0, 0), (0, 0, 0), (0, 0, 0), bit 31: (49196, 3848, 532), (49331, 3844, 540), (49466, 3857, 523), (0, 0, 0), (0, 0, 0), (0, 0, 0), bit 32: (49200, 3853, 527), (49335, 3857, 515), (49470, 3852, 528), (0, 0, 0), (0, 0, 0), (0, 0, 0), bit 33: (49204, 3849, 535), (49339, 3832, 560), (49474, 3837, 547), (0, 0, 0), (0, 0, 0), (0, 0, 0), bit 34: (49208, 3833, 543), (49343, 3831, 537), (49478, 3844, 560), (0, 0, 0), (0, 0, 0), (0, 0, 0), bit 35: (49211, 1928, 548), (49346, 1949, 527), (49481, 1916, 548), (0, 0, 0), (0, 0, 0), (0, 0, 0), bit 36: (49213, 1929, 539), (49348, 1939, 529), (49483, 1919, 537), (0, 0, 0), (0, 0, 0), (0, 0, 0), ---------------------------------
W pierwszej linii znajdują się informacje o odczycie, czyli:
- kanał na którym nastąpił odczyt
- odczytana temperatura
- odczytana wilgotność
- liczba pełnych udanych odczytów wszystkich bitów - jak pisałem wcześniej sonda nadaje 6 razy ten sam zestaw bitów w trakcie pojedynczej transmisji
- pierwszy wiersz (lb:) zawiera informację o tym ile razy bit został prawidłowo odczytany
- kolejny wiersz (sb:) zawiera informację o tym jakie informacje zostały odczytane - kiedy odczytam zero zmniejszam tę wartość o 1, a kiedy odczytam 1 zwiększam ją - w idealnym przypadku ta wartość jest równa tej z pierwszego wiersza lub ujemnej wartości z pierwszego wiersza
- ostatni wiersz (b:) zawiera interpretację odczytu czyli 0 lub 1
Kolejnych 10 wierszy, tych zaczynających się od "pre", zawiera informacje o czasach 10 pomiarów niskich i wysokich stanów zanim rozpoczął się właściwy pomiar. Liczby w nawiasach oznaczają:
- moment w którym zmiana została zmierzona, jest to technicznie millis() % 65536 - obcinam wartość millis() tylko do 16 bitów bo ze względu na małą pamięć nie mam gdzie przechowywać danych, ledwo się mieszczą
- czas w jakim stan był niski
- czas w jakim stan był wysoki
Na obrazie poniżej widać jak wygląda odczyt początku transmisji. Stan wysoki na lewo od zielonego znacznika "1" to pierwszy stan wysoki nadany przez sondę. Między znacznikami "1" a "2" sonda nadawała stan niski, jednak odbiornik wychwycił kilka stanów wysokich. Są to zakłócenia z którymi mój algorytm sobie nie poradził i uznał całość za szumy.
Widać też zakłócenia w dalszej części i dopiero potem (od mniej więcej 87ms) czyste dane.
Kolejnych 37 wierszy zawiera informacje o odczytach kolejnych bitów w kolejnych 6 transmisjach. Liczby w nawiasach mają takie samo znaczenie jak w wierszach "pre".
Tu widać, że podczas odczytu 4 serii bitów nagle nastąpiła przerwa podczas odczytu bitu nr 25. Czas stanu wysokiego jest ok, jednak czas stanu niskiego jest zbyt krótki, wynosi tylko 1292μs. Zobaczmy co się stało:
Pomiędzy znacznikami "1" i "2" powinien być stan niski, jednak widać, że odbiornik wychwycił jakieś zakłócenia jedno o długości 103μs, a drugie o długości 1028μs. Aktualny algorytm zignorował to pierwsze, jednak tego drugiego już nie i uznał odczyt za błędny i przerwał działanie.
Co zamierzam zrobić aby algorytm lepiej działał:
- nie przerywać całkowicie działania po pierwszym błędnym odczycie, ale czekać na kolejną transmisję (czyli przerwę o czasie 8640μs) i zaprzestać czekania dopiero kiedy zbyt długo ona nie nastąpiła.
- jeszcze bardziej uniewrażliwić algorytm na takie pojedyncze zakłócenia.
Nie przerywanie działania
Pisząc program, który po pierwszym błędzie nie przerywa działania tylko czeka na kolejną porcję danych praktycznie główną pętlę napisałem od nowa. Oto ten program.//#define DEBUG_ADHOC // ustawienie tej definicji powoduje, że program // wypisuje na rs232 różne informacje diagnostyczne #define DEBUG9 // ustawienie tej definicji powoduje, że program // po dokonaniu pomiaru ustawia na chwilę stan wysoki // na jednym z wyjść, podłączony tam analizator // stanów logicznych zaczyna wtedy pomiar // ponieważ posiada on bufor na jakiś czas przed // rozpoczęciem pomiwaru więc można przeanalizować // co pojawiło się na wyjściu z odbiornika RDF #define DEBUG_SALAE1 // wyjśącie odbiornika RDF #define RF_DATA_PIN 11 // wyjście z diodą świecącą #define LED_PIN 13 // czasy stanu wyokiego i przerw #define UP_TIME 560 #define SEPARATOR_TIME 8640 #define ZERO_TIME 1900 #define ONE_TIME 3800 #define MODE_WAITING_FOR_SEPARATOR 1 #define MODE_READING_BITS 2 #define MODE_STOP_READING 3 // maksymalny czas transmisji w ms // obliczony przy założeniu, że są same jedynki wysylane // (czyli długie stany niskie) #define MAX_TRANSMISION_TIME ((6L * (37L * (UP_TIME + ONE_TIME) + UP_TIME + SEPARATOR_TIME) + UP_TIME) / 1000L) // makra sprawdzające czy czas mieści się w buforze #define CHK_UT(CT,T) (((CT) >= ((T) - 50)) && ((CT) <= ((T) + 50))) #define CHK_FT(CT,T) (((CT) >= ((T) - 250)) && ((CT) <= ((T) + 250))) // liczba bitów jednej wiadomości i liczba // paczek, które są nadawane #define NUM_OF_BITS 37 #define NUM_OF_SERIES 6 // specjalna wartość oznaczająca, że odczyt był błędny #define BAD_VALUE 0b1000000000000000 // bufor na bity int8_t bits [NUM_OF_BITS]; // za każdym z 6 pomiarów na określonej pozycji mogę zmierzyć // 0 lub 1 (na skutek błędów transmisji) // jeżeli zmierzę 1 to zwiększam tę wartość, // a jeśli 0 to zmniejszam int8_t bit_adds [NUM_OF_BITS]; // za każdym poprawnym pomiarem zwiększam tę wartość o 1 uint8_t bit_measures [NUM_OF_BITS]; uint32_t last_report_time = 0; #ifdef DEBUG9 uint16_t arr_bit_times [NUM_OF_SERIES][NUM_OF_BITS][3]; #define PRE_DEBUG_BUF_SIZE 10 uint16_t arr_pre_buf_times [PRE_DEBUG_BUF_SIZE][3]; int pre_buf_counter; #endif #ifdef DEBUG_SALAE1 #define DEBUG_SALAE1_PIN 6 #endif uint32_t getvalue(int bitStart, int bitNum) { uint32_t ret = 0; for (int i = 0; i < bitNum; i++) { if (bits [bitStart + i] > 1) return BAD_VALUE; ret *= 2; ret += bits [bitStart + i]; } return ret; } void setup() { Serial.begin(115200); pinMode(RF_DATA_PIN, INPUT); pinMode(LED_PIN, OUTPUT); #ifdef DEBUG_SALAE1 pinMode(DEBUG_SALAE1_PIN, OUTPUT); #endif Serial.print(F("Startujemy - max transmision time = ")); Serial.println(MAX_TRANSMISION_TIME); } void loop() { // czyszczę wszystkie zmienne na początku for (int i = 0; i < NUM_OF_BITS; i++) { bit_adds [i] = 0; bit_measures [i] = 0; #ifdef DEBUG9 for (int j = 0; j < NUM_OF_SERIES; j++) { arr_bit_times [j][i][0] = 0; arr_bit_times [j][i][1] = 0; arr_bit_times [j][i][2] = 0; } #endif } #ifdef DEBUG9 for (int i = 0; i < PRE_DEBUG_BUF_SIZE; i++) { arr_pre_buf_times [i][0] = 0; arr_pre_buf_times [i][1] = 0; arr_pre_buf_times [i][2] = 0; } pre_buf_counter = 0; #endif int8_t mode = MODE_WAITING_FOR_SEPARATOR; // czas stanu wysokiego w microsekundach uint32_t highTime; // czas aktualnego pomiaru w microsekundach uint32_t time; // czas stanu niskiego i wysokiego razem uint32_t tickTime; // czas ostatniego pełnego pomiaru w microsecundach uint32_t last_pulse_time = micros(); // aktualna seria danych int8_t measure_number = -1; // czas rozpoczęcia pełnego pomiaru // używany to sprawdzania czy timeout nie upłynął uint32_t full_measure_start_time; // licznik odczytywanych bitów int8_t bit_number; do { while (true) { highTime = pulseIn(RF_DATA_PIN, HIGH); time = micros(); #ifdef DEBUG9 uint32_t lowTime = time - last_pulse_time - highTime; // do prebufora tylko przed właściwymi pomiarami if (measure_number < 0) { arr_pre_buf_times [pre_buf_counter][0] = lowTime; arr_pre_buf_times [pre_buf_counter][1] = highTime; arr_pre_buf_times [pre_buf_counter][2] = millis(); pre_buf_counter = (pre_buf_counter + 1) % PRE_DEBUG_BUF_SIZE; } #endif // sprawdzenie czy timeout if ((measure_number >= 0) && ((millis () - full_measure_start_time) > MAX_TRANSMISION_TIME)) { mode = MODE_STOP_READING; break; } // poprawny, wystarczająco długi stan wysoki if (CHK_UT(highTime, UP_TIME)) break; } tickTime = time - last_pulse_time; #ifdef DEBUG9 if ((measure_number >= 0) && (bit_number < NUM_OF_BITS)) { arr_bit_times [measure_number][bit_number][0] = tickTime - highTime; arr_bit_times [measure_number][bit_number][1] = highTime; arr_bit_times [measure_number][bit_number][2] = millis(); } #endif // ta zmienna określa czy "skonsumować" ten odczyt boolean bConsumeThisTick = false; // sprawdzamy czy udało się odczytać prawidłowy czas switch (mode) { case MODE_READING_BITS: if (CHK_FT(tickTime, UP_TIME + ZERO_TIME)) { bit_adds [bit_number]--; bit_measures [bit_number]++; bit_number++; bConsumeThisTick = true; } else if (CHK_FT(tickTime, UP_TIME + ONE_TIME)) { bit_adds [bit_number]++; bit_measures [bit_number]++; bit_number++; bConsumeThisTick = true; } if (bit_number >= NUM_OF_BITS) { mode = MODE_WAITING_FOR_SEPARATOR; } case MODE_WAITING_FOR_SEPARATOR: if (CHK_FT(tickTime, UP_TIME + SEPARATOR_TIME)) { measure_number++; if (measure_number == 0) { full_measure_start_time = millis(); } bit_number = 0; if (measure_number >= NUM_OF_SERIES) { mode = MODE_STOP_READING; } else { mode = MODE_READING_BITS; } bConsumeThisTick = true; } } if ((mode != MODE_STOP_READING) && !bConsumeThisTick) { mode = MODE_WAITING_FOR_SEPARATOR; bConsumeThisTick = true; } if (bConsumeThisTick) { last_pulse_time = time; } } while (mode != MODE_STOP_READING); // jeżeli choć jeden pomiar się udał wypisuję raport if (measure_number > 0) { #ifdef DEBUG_SALAE1 digitalWrite(DEBUG_SALAE1_PIN, HIGH); delay(10); digitalWrite(DEBUG_SALAE1_PIN, LOW); #endif for (int i = 0; i < NUM_OF_BITS; i++) { if (bit_adds[i] > (bit_measures[i] / 2)) { bits[i] = 1; } else if (bit_adds[i] < (-bit_measures[i] / 2)) { bits[i] = 0; } else { bits[i] = 2; } } uint32_t nChannel = getvalue (14, 2); uint32_t nTemp = getvalue (16, 12); uint32_t nHumidity = getvalue (28, 8); Serial.print(F("channel: ")); if (nChannel != BAD_VALUE) Serial.print(nChannel + 1); else Serial.print(F("???")); Serial.print (F(", temperature: ")); if (nTemp != BAD_VALUE) Serial.print(nTemp / 10.0); else Serial.print(F("???")); Serial.print (F(" *C, humidity: ")); if (nHumidity != BAD_VALUE) Serial.print(nHumidity); else Serial.print(F("???")); Serial.print(F(" %, Num of measures: ")); Serial.println(measure_number); //wypisuję liczbę pomiarów dla bitów Serial.print(F("lb: ")); for (int i = 0; i < NUM_OF_BITS; i++) { Serial.print(F(" ")); Serial.print(bit_measures[i]); Serial.print(F(",")); } Serial.println(); Serial.print(F("sb: ")); for (int i = 0; i < NUM_OF_BITS; i++) { if (bit_adds[i] >= 0) { Serial.print(F(" ")); } Serial.print(bit_adds[i]); Serial.print(F(",")); } Serial.println(); Serial.print(F("b: ")); for (int i = 0; i < NUM_OF_BITS; i++) { Serial.print(F(" ")); if (bits[i] > 1) Serial.print(F("?")); else Serial.print(bits[i]); Serial.print(F(",")); } Serial.println(); #ifdef DEBUG9 for (int i = 0; i < PRE_DEBUG_BUF_SIZE; i++) { Serial.print(F("pre ")); Serial.print(i); Serial.print(F("-")); Serial.print(pre_buf_counter); Serial.print(F(": ")); Serial.print(F("(")); Serial.print(arr_pre_buf_times [pre_buf_counter][2]); Serial.print(F(", ")); Serial.print(arr_pre_buf_times [pre_buf_counter][0]); Serial.print(F(", ")); Serial.print(arr_pre_buf_times [pre_buf_counter][1]); Serial.print(F("), ")); Serial.println(); pre_buf_counter = (pre_buf_counter + 1) % PRE_DEBUG_BUF_SIZE; } #endif #ifdef DEBUG9 for (int i = 0; i < NUM_OF_BITS; i++) { Serial.print(F("bit ")); Serial.print(i); Serial.print(F(": ")); if (bits[i] > 1) Serial.print(F("?")); else Serial.print(bits[i]); Serial.print(F(":")); for (int j = 0; j < NUM_OF_SERIES; j++) { if ((bits[i] == 0) && !CHK_FT(arr_bit_times [j][i][0] + arr_bit_times [j][i][1], UP_TIME + ZERO_TIME)) Serial.print(F("#")); else if ((bits[i] == 1) && !CHK_FT(arr_bit_times [j][i][0] + arr_bit_times [j][i][1], UP_TIME + ONE_TIME)) Serial.print(F("#")); else Serial.print(F(" ")); Serial.print(F("(")); Serial.print(arr_bit_times [j][i][2]); Serial.print(F(", ")); Serial.print(arr_bit_times [j][i][0]); Serial.print(F(", ")); Serial.print(arr_bit_times [j][i][1]); Serial.print(F("),")); } Serial.println(); } #endif Serial.println(F("---------------------------------")); } }
Ale zawiłości... dobrze, że z moją stacją pogodą zakupioną na https://www.oleole.pl/stacje-pogody,_Meteo.bhtml nie mam aż tyle problemów. Wyświetlacz jest bardzo czytelny i wszystkie potrzebne parametry można sprawdzić. I są w pełni wiarygodne. I na zewnątrz i wewnątrz można taką stację trzymać.
OdpowiedzUsuń