niedziela, 14 stycznia 2018

Widget TABLE - proste bazy danych w BLYNKu

BLYNK to prosty (by nie powiedzieć prostacki) system do sterowania mikroprocesorami z poziomu telefonicznej aplikacji. I dokładnie pod tym kątem tworzone są kolejne widgety, z których nasze aplikacje możemy sobie poskładać. Elementy te to niemalże wprost odpowiedniki rzeczywistych podzespołów i układów elektronicznych. Co jednak gdy chcemy użyć BLYNKa  do działań bardziej zaawansowanych np. obróbki większej ilości danych. Tu już trzeba poczekać na sponsora. Kiedyś był prosty GRAPH ale w jego miejsce wskoczył dużo bardziej zaawansowany SuperChart. Drugim widgetem wykonanym na zamówienie i udostępnionym do użytkowania dla wszystkich blynkersów jest  widget TABLE.

Widgeta wykresów na razie nie będę omawiał - nie ma (jak na razie) miejsca dla niego w projekcie routera. Widget TABLET został wyczerpująco opisany na portalu BLYNK.pl i nie ma sensu powtarzać tych informacji. Ważna w tym miejscu jest jego możliwość wyświetlenia bardzo dużej ilości dowolnych typów zmiennych. W standardowej konfiguracji to 100 pozycji (rekordów) ale można to jeszcze powiększyć w lokalnym serwerze ustawieniem parametru table.rows.pool.size. TABLE jest zasadniczo dwuwymiarową tablicą ale używając danych typu String możemy tam wyświetlić i kilkanaście pól rekordu.
W poprzednim poście założyłem konieczność stworzenia trzech tablic struktur. Dwie dla kodów (adresów) urządzeń pracujących jako nadajniki i odbiorniki zawierają dwuelementowe struktury <kod> <impuls>. Trzecia tablica - funkcji - jest złożoną strukturą wielu informacji <indeks adr we> <indeks adr wy> <funkcja> <czas>. To są tymczasowe pola rekordu. Ostateczny kształt tablicy funkcji będzie pewnikiem zmieniany jeszcze nie raz bo to podstawa działania routera 433.

Czas na próbę wyświetlenia tych trzech tablic w trzech widgetach TABLE. Taka mini prezentacja możliwości.
Na początek fragment kodu - deklaracje struktur i tablic

struct stadres {
  long kod;   
  int impuls;
};

#define zal 1  // wartosci pola funkcja
#define przel 2
#define wlsek 3
#define wlmin 4
#define wlh 5

struct stfunkcja {
  byte kodwe;
  byte kodwy;  //indeks kodu wyjściowego
  byte funkcja;
  byte czas; // czas lub 2 indeks kodu wyjściowego
  byte stan; //zmienna pomocnicza
};

const int rozm_tab_nad = 5; //rozmiar tablicy kodów nadajników
const int rozm_tab_odb = 5; //rozmiar tablicy kodów odbiorników
const int rozm_tab_fun = 5; //rozmiar tablicy kodów funkcji
stadres tablica_adres_nad[rozm_tab_nad]; //deklaracja tablicy kodów nadajników
stadres tablica_adres_odb[rozm_tab_odb]; //deklaracja tablicy kodów odb
stfunkcja tablica_adres_fun[rozm_tab_fun]; //deklaracja tablicy kodów funkcji


Teraz pozostaje nakarmić tablice danymi. Pierwszy rekord tablicy zawiera zerowe dane

void setup()
{
  Blynk.virtualWrite(V20, "clr");
  Blynk.virtualWrite(V30, "clr");
  Blynk.virtualWrite(V40, "clr");

  tablica_adres_nad[0] = {0 , 0};
  tablica_adres_nad[1] = {5526804 , 320};
  tablica_adres_nad[2] = {5526801 , 320};
  tablica_adres_nad[3] = {5526609 , 320};
  tablica_adres_nad[4] = {5526612 , 320};

  for (int i = 0; i < (rozm_tab_nad + 1); i++) {
    Blynk.virtualWrite(V20, "add", i, tablica_adres_nad[i].kod, tablica_adres_nad[i].impuls);
  }
  tablica_adres_odb[0] = {0 , 0};
  tablica_adres_odb[1] = {5522769 , 320};
  tablica_adres_odb[2] = {5522772 , 320};
  tablica_adres_odb[3] = {5525841 , 320};
  tablica_adres_odb[4] = {5525844 , 320};

  for (int i = 0; i < (rozm_tab_odb + 1); i++ ) {
    Blynk.virtualWrite(V30, "add", i, tablica_adres_odb[i].kod, tablica_adres_odb[i].impuls);
  }
  tablica_adres_fun[0] = {0, 0, 0, 0, 0};
  tablica_adres_fun[1] = {1, 1, 1, 0, 0};
  tablica_adres_fun[2] = {2, 1, 2, 0, 0};
  tablica_adres_fun[3] = {3, 1, 3, 0, 0};
  tablica_adres_fun[4] = {4, 1, 4, 0, 0};
  
  for (int i = 0; i < (rozm_tab_fun + 1); i++ ) {
    String s = String (String (tablica_adres_fun[i].kodwe) + " " + String (tablica_adres_fun[i].funkcja) + " " + String (tablica_adres_fun[i].kodwy) + " " + String (tablica_adres_fun[i].czas));
    Blynk.virtualWrite(V40, "add", i, s , tablica_adres_fun[i].stan);
  }
}

Po wyczyszczeniu pamięci widgetu TABLE (na serwerze BLYNK) wprowadzam wartości do kolejnych pól rekordów zmiennych będących tablicą struktur. Następnie dane te są wysyłane do widgetu sekwencyjnie a polecenie "add" tworzy kolejne wiersze w TABLE wpisując tam wysyłane dane. Tablice adresów są dwuwymiarowe więc pole <kod> zapisuję do zmiennej <name> widgetu, a pole <impuls> do <value>. Trochę bardziej złożona jest sytuacja dla rekordu tablicy funkcji tablica_adres_fun[] Składa się z pięciu pól i żeby je wszystkie wyświetlić trzeba trochę pomajstrować przy tworzeniu jednego zbiorczego łańcucha tekstowego. Finalnie Strting s zostanie wyświetlony w zmiennej <name>  zaś piąte dodatkowe pole <stan> w zmiennej <value>.

A tak to jest widoczne w aplikacji BLYNK Wiersze oczywiście można skrolować.


























Co oznaczają liczby w tablicach adresów łatwo jest zidentyfikować. Gorzej z tablicą funkcji.
Zaps <2> <2> <1> <0> jest dosyć tajemniczy a oznacza że kod nadawany przez urządzenie z trzeciej pozycji tabeli kodów wej (numeracja tablic jest od 0) o kodzie <5526801> ma spowodować wygenerowanie przez router kodu z trzeciej pozycji tabeli  kodów wyj <5522772>. Jak raz kody pochodzą urządzeń tego samego producenta więc długość impulsu jest w obu przypadkach identyczna <320>. Kolejna cyfra <1> oznacza nr funkcji realizowanej przez router  <zał> czyli prostą zamianę jednego kodu na drugi. Zmienna czas ma w tym przypadku ma oczywiście wartość <0> (czwarta cyfra). Zmienna <status> jest przyszłościowa.
W wersji finalnej trzeba będzie jakoś opisać te liczby. Ruter musi dać się zrozumieć i obsłużyć bez konieczności studiowania opasłej instrukcji obsługi.

Nie mogę sobie darować sprawdzenia w działaniu pomysłu operacji na tablicach. Przygotowałem testowy program który po odebraniu kodu z nadajnika ma uruchomić właściwą funkcję "routingu" kodu wejściowego na kod wyjściowy zgodną z tabelą funkcji.

byte szukaj_indeks_we(long kod) {
  byte     indekswy = 0;
  for (int i = 1; i < (rozm_tab_nad + 1); i++) if (kod == tablica_adres_nad[i].kod) indekswy = i;
  return indekswy;
}
void powtorz_send() {
  if (powtorz) {
    sendkod(powtorzkod, powtorzimpuls);
    Serial.println("powtarzam send  " + String(powtorz));
    powtorz--;
  }
}
void send_indeks(byte ind) {
  powtorzkod = tablica_adres_odb[ind].kod;
  powtorzimpuls = tablica_adres_odb[ind].impuls;
  powtorz = 2;
  sendkod(tablica_adres_odb[ind].kod, tablica_adres_odb[ind].impuls);
  Serial.println ("index wy  " + String (tablica_adres_fun[ind].kodwy) + "  kod wy  " + String (tablica_adres_odb[ind].kod));
}
void sendkod(unsigned long code, int impuls) {
  mySwitch.setPulseLength(impuls);
  mySwitch.setProtocol(1);
  mySwitch.setRepeatTransmit(4);
  mySwitch.send(code, 24);
}
void gogo(long kod) {

  byte indeks_kod_we = szukaj_indeks_we(kod);
  Serial.println ("kod we " + String (kod) + "  index we  " + String (indeks_kod_we));

  if (indeks_kod_we != 0) {

    for (int i = 1; i < (rozm_tab_fun); i++ )
    {
      if (indeks_kod_we == tablica_adres_fun[i].kodwe) {

        if (tablica_adres_fun[i].funkcja == zal) {
          send_indeks(tablica_adres_fun[i].kodwy);
        }
      }
    }
  }
}

Po odebraniu kodu przez bibliotekę <RCSwitch.h> wywoływana jest procedura gogo() z kodem jako parametrem wejściowym. W procedurze następuje próba odszukania indeksu tabeli dla kodu nadanego przez urządzenie procedurą szukaj_indeks_we(kod); . Jeśli indeks > 0 tzn kod wejściowy został odnaleziony i zmienna indeks wskazuje nr pozycji kodu wejściowego. Numer ten jest odszukiwany w tablicy funkcji
  (indeks_kod_we == tablica_adres_fun[i].kodwe)
Znalezienie indeksu tabeli funkcji oznacza, że istnieje funkcja routowania dla danego adresu wejściowego. Trzeba sprawdzić typ funkcji ( tu na razie zaimplementowano jedynie funkcję <zał> ) i następnie pobieramy z tabeli funkcji indeks kodu wyjściowego. W procedurze void send_indeks(byte ind) 
przekształcamy go na rzeczywisty kod i impuls wybranego urządzenia odbiorczego. Procedura wysłania kodu po wstępnych ustawieniach parametrów transmisji inicjowana jest w procedurze
 void sendkod(unsigned long code, int impuls) 
zaś samą transmisję przejmuje znowu biblioteka  <RCSwitch.h>.
Cholera jak to mądrze brzmi - a jest tak niesamowicie proste.

I to tak naprawdę cała filozofia pracy routera 433 MHz no może nie licząc paru istotnych dodatków.

A jak to wygląda w praktyce?


Na szaro zaznaczono odebrane kody i wygenerowane na podstawie tablicy funkcji kody wyjściowe. Na niebiesko są kody, których brak w tablicy wejściowej (indeks = 0)
"Powtarzam send 1/2" to komunikat potwierdzający ponowną wysyłkę kodu wyjściowego po 1 i 2 sek od odebraniu kodu wejściowego. To przesuniecie czasowe jest niezbędne by oba kody wej i wyj nie nakładały się na siebie w tym samym momencie.

Czyli co ? Działa ?

Automat skończony do dekodowania kodów 
Zapisywanie kodów do EEPROMu  
typ struktura i zapis tablicy struktur do  EEPROMu  
baza danych i operacje na tablicy struktur  
tabela funkcji i dowiązanie jej do adresów wejściowych i wyjściowych  

zamiana kodu wejściowego na kod wyjściowy zgodnie z tablicą funkcji - czyli ROUTOWANIE adresów urządzeń 433 MHz  !!!! i o to chodziło 


Brak komentarzy:

Publikowanie komentarza