Szukaj na tym blogu

niedziela, 26 maja 2013

Odczyt bezprzewodowej sondy temperatury i wilgotności

Krótkie wprowadzenie

Dawno mnie nie było, ale poważnym tematem się zająłem ;) i dlatego tyle to trwało.
Jak widać na blogu od jakiegoś czasu zajmuję się monitorowaniem warunków (temperatura, wilgotność) w mieszkaniu i na zewnątrz.
Jestem w posiadaniu stacji pogodowej (mniej więcej takiej http://www.biowin.pl/bioterm/elektroniczne-stacje-pogody/bezprzewodowy-barom--term--higrom-wew--zew-/bezprzew-stacja-pog-barometr-pogod-zeg-kal-rcc) z bezprzewodową sondą temperatury i wilgotności i postanowiłem nauczyć się odczytywać jej wskazania i przesyłać je do komputera.




Zapytanie do producenta

Pierwsze co postanowiłem zrobić to zapytać producenta o ten protokół. Początek był obiecujący. Miły pan wprawdzie nie wiedział nic więcej ponad to, że sonda nadaje na częstotliwości 433 MHz, ale obiecał się dowiedzieć. Skończyło się jednak na "Tak, pytałem u źródła, niestety, nie da się tak zrobić, aby czujka sczytywała do innego urządzenia niż nasza stacja bazowa."
Oczywiście nie uwierzyłem :)

Pytanie na majsterkowo.pl

Postanowiłem poszukać pomocy gdzie indziej, a mianowicie na portalu http://majsterkowo.pl.Zadałem pytanie w wątku http://majsterkowo.pl/forum/arduino-i-bezprzewodowy-czujnik-temperatury-i-wilgotnosci-t816.html licząc na jakąś pomoc. Jednak znowu chciano mnie zniechęcić bo odpowiedź jaką dostałem brzmiała "W opisie jest napisane z jaką stacją to współpracuje. Do pracy z Arduino tego raczej nie zmusisz, producent na pewno zadbał o odpowiednie kodowanie sygnału."
Oczywiście nie uwierzyłem :)

Kupno odbiornika RF 433

W międzyczasie zakupiłem odbiornik i nadajnik RF 433 MHz.

Analiza sygnału

Kupno analizatora

Buszując gdzieś po sieci natrafiłem na pojęcie "analizatora stanów logicznych". Pomyślałem, że właśnie tego potrzebuję. W sieci znalazłem przebogatą ofertę tego typu urządzeń od takich za wiele tysięcy złotych (np http://www.conrad.pl/websale7/?Ctx={ver%2f7%2fver}{st%2f3ec%2fst}{cmd%2f0%2fcmd}{m%2fwebsale%2fm}{s%2fconrad%2fs}{l%2f01-aa%2fl}{p1%2fd2f6e56f669660582c6750d86eaf57b8%2fp1}{md5%2f5ffac58ff15c6a24fcf953c797b8c9b3%2fmd5}&act=product&prod_index=123271&cat_index=SHOP_AREA_17630_1114018&otp1=tablink2) do bardzo prostych podłączanych bezpośrednio do portu LPT (drukarkowego) w komputerze.
Ostatecznie zdecydowałem się na urządzenie o takie:
Ma 8 kanałów, częstotliwość do 24 MHz i ... kosztuje tylko 57 PLN :)

Odczyt sygnałów

Aby odczytać nadawane przez sondę sygnały postanowiłem podłączyć analizator bezpośrednio do nadajnika RF wewnątrz sondy. Po otwarciu sondy wnętrze przedstawia się następująco


Następnie rozpocząłem pomiar. Ustawiłem częstotliwość próbkowania na 1 MHz, wyłaczyłęm wszystkie czujniki poza pierwszym. Włączyłem dla niego opcję "Require rising edge transition" i nacisnąłem "Start".
Po jakimś czasie (sonda nadaje co ok 50-56 sekund - więcej o tym później) na ekranie ujrzałem taki obrazek:

Widać na nim, że odczytane zostały dane, że nadawanie trwało trochę ponad 0,8s.
Po powiększeniu początku danych zobaczyłem:
Widać, że sonda nadaje krótki "piki" (stan wysoki), a pomiędzy nimi przerwy.
Stan wysoki zawsze trwa ok 560μs, a przerwy pomiędzy nimi 8640, 3800 i 1900μs. Przerwa 8640 μs występuje na samym początku transmisji, na samym końcu oraz tylko kilka razy w środku. Przerwy 3800 i 1900 o wiele częściej i są pomieszane. Wydaje się, że 8640 to jakiś sygnał synchronizująco-rozdzielający, a te dwie pozostałe przerwy oznaczają bit 0 i 1.
Czas to sprawdzić.

Analiza w arkuszu kalkulacyjnym

Analizę danych postanowiłem przeprowadzić w LibreOffice Calc - to taka darmowy odpowiednik Excela.
Po wybraniu opcji "Options/Export Data" (Ctrl+E) pojawia się okienko
Po wybraniu opcji jak na obrazku na dysku mamy plik z następującymi danymi (początkowy fragment):
Time[s], czujnik
13.539262, 0
23.539262, 1
23.53983, 0
23.548476, 1
23.549044, 0
23.552861, 1
23.553429, 0
23.555343, 1
23.555911, 0
23.557816, 1
23.558384, 0
23.562232, 1
...
W pierwszej kolumnie jest czas mierzony w sekundach, w drugiej kolumnie jest wartość sygnału jaka się pojawiła na wejściu w tym czasie.
Zaimportowałem zatem te dane do arkusza i dodałem dwie kolejne kolumny: czas i stan.
W kolumnie czas wpisałem wszędzie formułę (dla komórki C3) =1000*(A4-A3). Dzięki temu otrzymałem czas w milisekundach trwania danego stanu.
Widać ponownie, że stan wysoki zawsze trwa ok 560μs, a stan niski 8640, 3800 i 1900μs. W czwartej kolumnie wpisuję regułę, które dla przerwy równej 3800μs wylicza, że to 1, a dla 1900μs wylicza, że to 0. Dla 8640μs wypisuje wartość "dystans". Oto przykładowe dane:

Time[s] SCIO czas analiza
0 0

11,35824 1 0,56
11,3588 0 8,64 dystans
11,36744 1 0,56
11,368 0 3,8 1
11,3718 1 0,56
11,37236 0 1,88 0
11,37424 1 0,56
11,3748 0 1,92 0
11,37672 1 0,56
11,37728 0 3,84 1

Taka analiza pozwoliła mi zauważyć, że podczas pojedynczej transmisji 6 razy przesyłanych jest 37 bitów. Za każdym razem takich samych.Serie 37 bitów rozdzielane są "dystansami".

Ok, spróbujmy zatem przeanalizować te bity. Najpierw wykorzystując autofiltr przeniosłem do drugiego arkusz taką paczkę 37 bitów do pierwszej kolumny. W kolejnych kolumnach zapisałem następujące formuły. Kolumny te miały następujące znaczenie.
Kol. formuła znaczenie
B =1-A2 odwrotny bit (nie wiem czy długa przerwa to bit czy krótka)
C =A2+2*(A3+2*(A4+2*(A5+2*(A6+2*(A7+2*(A8+2*A9)))))) wartość bajtu zaczynającego się od tego bitu przy założeniu, że: długa przerwa to 1, a krótka to 0, najpierw jest najmłodszy bit a potem coraz starsze.
D =B2+2*(B3+2*(B4+2*(B5+2*(B6+2*(B7+2*(B8+2*B9)))))) wartość bajtu zaczynającego się od tego bitu przy założeniu, że: długa przerwa to 0, a krótka to 1, najpierw jest najmłodszy bit a potem coraz starsze.
E =A9+2*(A8+2*(A7+2*(A6+2*(A5+2*(A4+2*(A3+2*A2)))))) wartość bajtu zaczynającego się od tego bitu przy założeniu, że: długa przerwa to 1, a krótka to 0, najpierw jest najstarszy bit a potem coraz młodsze.
F =B9+2*(B8+2*(B7+2*(B6+2*(B5+2*(B4+2*(B3+2*B2)))))) wartość bajtu zaczynającego się od tego bitu przy założeniu, że: długa przerwa to 0, a krótka to 1, najpierw jest najstarszy bit a potem coraz młodsze.

Dla następujących danych:
  • kanału nr 3
  • temperatury 24,1 stopnia celsjusza
  • wilgotności 43%
tabela przedstawiała się następująco:
A B C D E F
1 0 137 118 145 110
0 1 68 187 34 221
0 1 162 93 69 186
1 0 209 46 139 116
0 1 104 151 22 233
0 1 52 203 44 211
0 1 26 229 88 167
1 0 141 114 177 78
0 1 70 185 98 157
1 0 35 220 196 59
1 0 17 238 136 119
0 1 8 247 16 239
0 1 4 251 32 223
0 1 130 125 65 190
1 0 193 62 131 124
0 1 224 31 7 248
0 1 240 15 15 240
0 1 120 135 30 225
0 1 60 195 60 195
0 1 30 225 120 135
1 0 143 112 241 14
1 0 71 184 226 29
1 0 35 220 196 59
1 0 145 110 137 118
0 1 72 183 18 237
0 1 164 91 37 218
0 1 82 173 74 181
1 0 169 86 149 106
0 1 212 43 43 212
0 1 106 149 86 169
1 0



0 1



1 0



0 1



1 0



1 0



0 1




Na żółto zaznaczyłem te bajty gdzie widać pewne interesujące dane. w ostatniej żółtej komórce widać wilgotność, a w przedostatniej temperaturę pomnożoną przez 10. Pierwsze dwie zaznaczyłem na żółto bo to po prostu wcześniejsze dwa bajty. Że to ta kolumna to utwierdziłem się po kilku kolejnych pomiarach dla różnych wartości temperatury i wilgotności.
Powstają oczywiście pytania:
  • czy na wilgotność potrzeba aż 8 bitów?  skoro jej wartości są od 0-99 to wystarczy 7.
  • na ilu bitach i w jaki sposób kodowana jest temperatura, ten jeden bajt wystarczy na temperatury od 0.0 do 25.5 stopni Celsjusza, a ma wystarczyć na jakieś -50.0 do 70.0 czyli 1200 wartości więc co najmniej 11 (w praktycznie 12) bitów
Na te pytania postaram się odpowiedzieć później.
Po kolejnych pomiarach i próbach, których nie będę tu szczegółowo opisywał (było ich ok 10) wynik jest następujący:
licznik bity wartości komentarz
1 1 9 jakieś 4 bity
2 0

3 0

4 1

5 1 219 losowe id
6 1

7 0

8 1

9 1

10 0

11 1

12 1

13 0 0 jakieś 2 bity
14 0

15 0 1 kanał (od 0)
16 1

17 0 332 temperatura
18 0

19 0

20 1

21 0

22 1

23 0

24 0

25 1

26 1

27 0

28 0

29 0 25 wilgotność
30 0

31 0

32 1

33 1

34 0

35 0

36 1

37 0 0 jakiś bit

Czyli mamy po kolei:
  1. jakieś 4 bity - te bity ani razu nie zmieniły swojej wartości
  2. losowe id - wg dokumentacji po każdej zmianie baterii konieczne jest ponowne synchronizacja stacji bazowej z sondą ponieważ sonda zaczyna wysyłać nowe, losowe id - to prawdopodobnie te bity
  3. jakieś 2 bity - nigdy się nie zmieniły
  4. kanał (od 0) - w sondzie można wybrać kanał, jego wartość wysyłana jest właśnie na tych dwóch bitach
  5. temperatura - zakładam, że wysyłana jest na tych 12 bitach jako liczba całkowita będąca wartością temperatury pomnożoną przez 10. Podejrzewam, że najstarszy bit to bit znaku, ale nie robiłem jeszcze testów dla temperatur ujemnych
  6. wilgotność - jest przechowywana jako liczba całkowita na tych bitach. Możliwe, że nie na 8 od 29 bitu, a tylko na 7 od 30 bitu, a bit 29 jest do czegoś innego.
  7. jakiś bit - jego wartość nigdy się nie zmieniła.
Na razie wystarczy tych badań, podstawy już są, czas zbudować odbiornik, który będzie bezprzewodowo odczytywał te dane. Wtedy będę mógł go podłączyć do swojego systemu monitoringu temperatury i jednocześnie prowadzić dalsze badania.

Wysłanie samemu danych do odbiornika

Jako pierwszy test czy dobrze rozpracowałem format było zmontowanie układu do nadawania i próba nadania temperatury i wilgotności do stacji bazowej czyli udawanie sondy.
W tym celu zmontowałem następujący układ:

todo: wstawić schemat układu z atmega, nadajnikiem i wyświetlaczem lcd.

Napisałem następujący programik:

#include <LiquidCrystal.h>

#define RF_DATA_PIN 9
#define LED_PIN 10

#define UP_TIME 560
#define SEPARATOR_TIME 8640
#define ZERO_TIME 1900
#define ONE_TIME 3800

LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

int BITS [37] = 
{
  1,0,0,1,0,0,0,1,
  0,1,1,0,0,0,1,0,
  0,0,0,0,1,1,1,1,
  0,0,0,1,0,0,1,0,
  1,0,1,1,0
};

void setup ()
{
  pinMode(RF_DATA_PIN, OUTPUT);      
  pinMode(LED_PIN, OUTPUT);      

  lcd.begin(16, 2);
}


// nadaje wysoki poziom przez określony czas
void writeOne()
{
  digitalWrite(RF_DATA_PIN, HIGH);
  delayMicroseconds(UP_TIME);
  digitalWrite(RF_DATA_PIN, LOW);
}


// nadaje wysoki poziom, a potem czeka określony czas
// z niskim poziomem
void writeOneTick(unsigned int microsTime)
{
  writeOne();
  delayMicroseconds(microsTime);
}


void writeAllData ()
{
  // sonda tego nie nadaje, 
  // ale to dla odbiornika żeby zdążył się wygrzać 
  for (int i = 0; i < 30; i++)
    writeOneTick(UP_TIME);

  writeOneTick(SEPARATOR_TIME);
  
  // 6 razy nadajemy
  for (int i = 0; i < 6; i++)
  {
    // paczkę 37 bitów
    for (int j = 0; j < 37; j++)
    {
      if (BITS [j])
        writeOneTick(ONE_TIME);
      else
        writeOneTick(ZERO_TIME);
    }
  
    writeOneTick(SEPARATOR_TIME);
  }
  
  writeOne();
}


// za każdym razem nadaję inną wilgotność
// ta zmienna przechowuje jej wartość
int humidity = 50;



void loop ()
{
  // zapalam diodę aby widzieć kiedy trwa nadawanie
  digitalWrite(LED_PIN, HIGH);

  // na wyświetlaczu LCD wypisuję 
  // wartość wilgotności
  lcd.setCursor(0, 1);
  lcd.print(humidity);
  lcd.print("                  ");



  // ustawienie bitów w tablicy aby
  // aby wilgotność była wysłana
  int h = humidity;
  for (int i = 0; i < 8; i++)
  {
    BITS [35 - i] = h % 2;
    h = h / 2;
  }


  // wysłanie danych
  writeAllData();

  // zwiększenie wilgotności
  humidity = humidity == 99 ? 0 : humidity + 1;
  
  // zgaszenie diody
  digitalWrite(LED_PIN, LOW);
  
 
  // odczekanie odpowiedniego czasu do następnej
  // transmisji 
  // kanał 1 - 50 s w sumie
  // kanał 2 - 53 s w sumie
  // kanał 3 - 56 s w sumie
  delay(55300);
}





Po uruchomieniu całości efekt jest następujący:

todo: wstawić filmik z pokazującą się temperaturą i wilgotnością

Jedyny problem jaki miałem to odstępy pomiędzy nadawaniem wartości. Okazało się, że odstęp jest różny na różnych kanałach i tak:
  • na kanale 1 wynosi on 50s
  • na kanale 2 wynosi on 53s
  • na kanale 3 wynosi on 56s
Dokładnie co taki okres czasu stacja bazowa spodziewa się kolejnego nadawania i oczekuje na niego przez krótki okres czasu. Prawdopodobnie aby oszczędzać baterię.

Odczyt danych

Odczyt danych okazał się najtrudniejszą częścią, a to za sprawą dużej ilości błędów.
Po pierwsze kiedy sonda nie nadaje to odbiornik i tak wychwytuje "coś" z powietrza i  ciągle na jego wyjściu stan się zmienia. Kiedy sonda zaczyna nadawać to odbiornik chwilę potrzebuje żeby się "dostroić" do mocy sygnału i pierwsze bity są uszkodzone często.
Poza tym wychodzi, że czułość odbiornika jest niewielka i już przy odległości rzędu metra liczba niedokładnych odczytów mocno rośnie.

Pierwsze podejście

W tej chwili program do odczytu wygląda następująco:
// 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

// makra sprawdzające czy czas mieści się w buforze
#define CHK_T(CT,T) ((CT >= (T - 250)) && (CT <= (T + 250)))
#define CHK_T2(CT,T) ((CT >= (T - 800)) && (CT <= (T + 800)))


// 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];

// licznik pomiarów całej paczki
uint8_t measure_number;

uint32_t last_report_time = 0;
uint32_t last_pulse_time;


#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

}





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
  }
  measure_number = 0;
  
#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
  
  
  
  
  uint32_t highTime, time, lowTime;
  
  
  // czekam na prawidłowy pierwszy pulse high
  last_pulse_time = micros();
  while (true)
  {
    highTime = pulseIn(RF_DATA_PIN, HIGH);
    time = micros();
    lowTime = time - last_pulse_time - highTime;
    last_pulse_time = time;

#ifdef DEBUG9
    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

    if (CHK_T (highTime, UP_TIME) && CHK_T2 (lowTime, SEPARATOR_TIME))
    {
      break;
    }
  }
  
  int bit_number = 0;

  // wykonuję sześć pomiarów
  while (measure_number < NUM_OF_SERIES)
  {
    // odczytuję pulse
    highTime = pulseIn(RF_DATA_PIN, HIGH);
    
    // ignoruję takie krótkie pulsy tak jakby ich nie było
    if (highTime < 300)
    {
      continue;
    }

    time = micros();
    
    // zbyt długie traktuję jako krótki który się zlał z normalnym
    if (highTime > 750)
    {
      highTime = UP_TIME;
    }
    
    lowTime = time - last_pulse_time - highTime;
    last_pulse_time = time;


#ifdef DEBUG9
    if (bit_number < NUM_OF_BITS)
    {
      arr_bit_times [measure_number][bit_number][0] = lowTime;
      arr_bit_times [measure_number][bit_number][1] = highTime;
      arr_bit_times [measure_number][bit_number][2] = millis();
    }
#endif

    // sprawdzam czy pulse ma dobry czas
    if (!CHK_T (highTime, UP_TIME))
    {
      // i jeśli nie to koniec
      //Serial.println("1");
      break;
    }
   

    
    // sprawdzam czy to znacznik końca serii
    if (CHK_T2 (lowTime, SEPARATOR_TIME))
    {
      // sprawdzam czy została odpowiednia liczba bitów odczytana
      if (bit_number <= NUM_OF_BITS)
      {
        measure_number++;
        bit_number = 0;
      }
      else
      {
        // została odczytana zła liczba bitów
        Serial.print("2 - ");
        Serial.println(bit_number);
        break;
      }
    }
    
    // sprawdzam czy kolejny bit został odczytany
    else if (CHK_T2 (lowTime, ZERO_TIME))
    {
      if (bit_number >= NUM_OF_BITS)
      {
        break;
      }
      
      bit_adds [bit_number]--;
      bit_measures [bit_number]++;
      
      bit_number++;
    }
    else if (CHK_T2 (lowTime, ONE_TIME))
    {
      if (bit_number >= NUM_OF_BITS)
      {
        break;
      }
      
      bit_adds [bit_number]++;
      bit_measures [bit_number]++;
      
      bit_number++;
    }
    else
    {
      // to nieprawidłowy bit
      break;
    }
  }
  
  
  // jeżeli choć jeden pomiar się udał wypisuję raport

  if (measure_number > 0)
  {
    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);
    
#ifdef DEBUG_SALAE1
    if (nChannel == 1)
    {
      digitalWrite(DEBUG_SALAE1_PIN, HIGH);
      delay(10);
      digitalWrite(DEBUG_SALAE1_PIN, LOW);
    }
#endif

    Serial.print("channel: ");
    if (nChannel != BAD_VALUE)
      Serial.print(nChannel + 1);
    else
      Serial.print("???");
      
    Serial.print (", temperature: ");
    if (nTemp != BAD_VALUE)
      Serial.print(nTemp / 10.0);
    else
      Serial.print("???");

    Serial.print (" *C, humidity: ");
    if (nHumidity != BAD_VALUE)
      Serial.print(nHumidity);
    else
      Serial.print("???");

    Serial.print(" %, Num of measures: ");
    Serial.println(measure_number);
    
    //wypisuję liczbę pomiarów dla bitów
    Serial.print("lb: ");
    for (int i = 0; i < NUM_OF_BITS; i++)
    {
      Serial.print(" ");
      Serial.print(bit_measures[i]);
      Serial.print(",");
    }
    Serial.println();
    

    Serial.print("sb: ");
    for (int i = 0; i < NUM_OF_BITS; i++)
    {
      if (bit_adds[i] >= 0)
      {
        Serial.print(" ");
      }
      Serial.print(bit_adds[i]);
      Serial.print(",");
    }
    Serial.println();
    
    
    Serial.print("b:  ");
    for (int i = 0; i < NUM_OF_BITS; i++)
    {
      Serial.print(" ");
      if (bits[i] > 1)
        Serial.print("?");
      else
        Serial.print(bits[i]);
      Serial.print(",");
    }
    Serial.println();
 
 
 
 
 #ifdef DEBUG9
 
 
    for (int i = 0; i < PRE_DEBUG_BUF_SIZE; i++)
    {
      Serial.print("pre ");
      Serial.print(i);
      Serial.print("-");
      Serial.print(pre_buf_counter);
      Serial.print(": ");

      Serial.print("(");
      Serial.print(arr_pre_buf_times [pre_buf_counter][2]);
      Serial.print(", ");
      Serial.print(arr_pre_buf_times [pre_buf_counter][0]);
      Serial.print(", ");
      Serial.print(arr_pre_buf_times [pre_buf_counter][1]);
      Serial.print("), ");
      
      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("bit ");
      Serial.print(i);
      Serial.print(": ");
      
      for (int j = 0; j < NUM_OF_SERIES; j++)
      {
        Serial.print("(");
        Serial.print(arr_bit_times [j][i][2]);
        Serial.print(", ");
        Serial.print(arr_bit_times [j][i][0]);
        Serial.print(", ");
        Serial.print(arr_bit_times [j][i][1]);
        Serial.print("), ");
      }      
      
      Serial.println();
    }
#endif

 
    
    Serial.println("---------------------------------");
  }  
}

Wymaga on jeszcze paru poprawek więc nie będę się o nim rozpisywał, kiedy go poprawię to w kolejnym wpisie więcej napiszę.

Problemy z zasięgiem

Jak już pisałem kiedy żaden nadajnik nie nadaje odbiornik "łapał coś" i stan na wyjściu co chwila się zmieniał. Założyłem jednak, że kiedy nadajnik zacznie nadawać to odbiornik zacznie reagować prawidłowo.
Pierwsza wersja programu została napisana tak, że program czeka na stan wysoki o odpowiedniej długości, a następnie odczytuje dane spodziewając się prawidłowych czasów zarówno stanów wysokich jak i niskich. Po pierwszym błędzie program przerywał odczyt i próbował zanalizować odczytane dane, a mianowicie jeżeli udało się odczytać choć dwie serie danych to następowało porównywanie odczytanych serii.

Pierwsze próby robiłem nadając mój testowy sygnał z nadajnika, który zakupiłem razem z odbiornikiem. Sygnał nadawany był z niewielkiej odległości. Zauważyłem, że dane nie odczytują się do końca poprawnie. Często zamiast sześciu serii danych odczytywane było pięć serii.
Przyczyną okazało się to, że kiedy nadajnik zaczynał nadawać to odbiornik tak jakby potrzebował chwili aby się do niego dostroić.

Dużo gorzej było kiedy próbowałem odczytać sygnał z oryginalnej sondy, tu już przy jednometrowej odległości zaczynały się spore problemy. Często nie udawało się odczytać w ogóle danych lub najwyżej jedną, dwie serie.
Przyczyną jak się okazało było pojawianie się na wyjściu stanów wysokich mimo tego, że sonda nadawała stan niski.
Próbowałem poprawić czułość sondy przylutowując antenkę w postaci kawałka druta długości około 15 cm, ale niewiele to pomogło. Wywiązała się nawet dyskusja na forum majsterkowo.pl. Jak tylko znajdę moment to popracuję nad tą antenką.

Kolejnym sposobem było ignorowanie krótkich stanów wysokich, to poprawiło nieco jakość odbioru.

Oto przykład wadliwie odczytanych danych:


To co zamierzam zrobić to:
  • jeszcze bardziej uniewrażliwić oprogramowanie na sporadyczne błędy
  • nie przerywać pomiaru po pierwszym poważnym błędzie tylko czekać na kolejną serię danych
  • być może pewną inteligencję wprowadzić

Co dalej

W tej chwili muszę zrobić dwie rzeczy:
  • poprawić kod aby nie przerywał odczytu po pierwszym błędzie tylko czekał na kolejną porcję danych, po tej zmianie projekt będzie gotowy do opublikowania
  • poprawić czułość odbiornika, być może konieczny będzie zakup kolejnego

4 komentarze:

  1. SZACUN
    Podłączyłem odbiornik RX 433mhz z tym czujnikiem ze stacji meteo.
    Działało to jak kierunkowskaz (działa nie działa).
    Grzebałem w kodzie do bólu raz działało wszystko na 100%
    Raz tak jak chciało. zainwestowałem w trochę droższy odbiornik i nic.
    No to jazda nasłuch na 432,000-435,000 nadajnik siedzi w 433,850 a w okolicy cisza totalna.
    No to wio zamiast Serial print to na lcd i to samo. no to przez BT z tym samym skutkiem.
    Pomiary napięcia na usb nic filtry do zasilania nic petle i inne dziwne poradniki.
    Zrezygnowany osobne czujniki zamówiłem.
    PC stan uśpienia i 100% sukces zasilane przez baterie to samo.
    BANAN na twarzy i ide zajarać :)

    OdpowiedzUsuń
  2. Ten komentarz został usunięty przez autora.

    OdpowiedzUsuń
  3. A jakie stacje pogodowe polecacie do użytku domowego? Zależy mi na tym, aby była to stacja dobrej jakości, ponieważ wolę zainwestować raz, a porządnie. Myślę, że strona https://www.oleole.pl/stacje-pogody,_biowin.bhtml jak i opinie na niej zawarte bardzo mi pomogą.

    OdpowiedzUsuń
  4. Zobaczyłem ten artykuł i naprawdę podziwiam i gratuluję projektu. Złamanie zamkniętego kodowania to jest naprawdę trudna sprawa. Dobra robota :)

    OdpowiedzUsuń