środa, 3 stycznia 2018

Operacje na tablicy struktur - czyli mini baza adresów urządzeń dla routera 433MHz

Struktury upakowane w tablice znakomicie rozwiązują wiele problemów w konfiguracji bezprzewodowego routera 433MHz. Najważniejszym ich plusem jest stworzenie, niejako przy okazji innych ułatwień , czegoś na kształt bazy danych. Przy kilku urządzeniach dołączonych do routera nie ma to większego znaczenia. Ale gdy takich układów będzie kilkanaście lub nawet kilkadziesiąt odszukiwanie właściwego kodu (adresu) wymaga już działań systemowych. To zadanie wprost idealne dla obiektów magazynowych jakim są w informatyce bazy danych.


Stwórzmy więc mini bazę danych na potrzeby naszego routera 433 do zapamiętywania kodów urządzeń (adresów):

struct stadres {
  long kod;
  int impuls;
};
#define ilekod 3   //rozmiar tablicy kodów
stadres tablica_adres[ilekod]; //deklaracja tablicy kodów

Rekordami bazy danych są kolejne pozycje tabicy [0], [1]..... Tu rekordem jest struktura zawierająca dwie dane (dwa pola rekordu) - kod i impuls - specyficzne dla każdego urządzenia bezprzewodowego. Na najniższym (bajtowym) poziomie rekord składa się z 6 bajtów - 4 bajtowe pole rekordu dla kodu w formacie long i 2 dla impulsu w formacie int.



W poprzednim wpisie ćwiczyłem adresowanie poszczególnych pól rekordów a więc kodu i impulsu dla danego urządzenia. Przy użyciu tabeli struktur jest to banalnie proste. Jeśli chcemy odczytać  kod dla 11-stego urządzenia w tabeli piszemy po prostu

long l = tablica_adres[11].kod;

a kompilator sam wyliczy adres pierwszego bajtu zmiennej kod dla 11 rekordu tablicy i pobierze 4 kolejne bajty do zmiennej typu long. Coś pięknego. Zastanawiam się ile dłubania wymagałoby napisanie takiej procedury w asemblerze. C to potęga nawet w tak uproszczonej formie jaka jest dostępna w Arduino IDE. Zbyszek nie ma się co zastanawiać - wgrywaj program i do roboty!!!

Procedury zapisu i odczytu kodu w eepromie nieco się uprościły i fragment programu odpowiedzialny z odczyt kodu i zapisanie go do pamięci przybrał postać

struct stadres {
  long kod;
  int impuls;
};

#define pamkod 100    // początek tablicy kodów we flash
#define ilekod 3 //romiar tablicy kodów
stadres tablica_adres[ilekod]; //deklaracja tablicy kodów

int nrpozkod = 0; //nr pozycji do zapisu kodu pobierany z aplikacji BLYNK
int stan = 1; // nr stanu programu
int p_kod = 0; //stan przycisku aplikacji BLYNK - start programu i zapisu do pamięci 

long KOD = 0; // kod urządzenia do zapisu w pamięci
int IMPULS = 0; //impuls urządzenia do zapisu w pamięci

void savetab () { //zapisz tabelę kodow do flash
  int  liczadres = pamkod;
  EEPROM.begin(512);
  EEPROM.put(liczadres, tablica_adres);
  EEPROM.end(); //  EEPROM.commit();
}

void readtab() { // odczyt tabeli kodów z flash
  int  liczadres = pamkod;
  EEPROM.begin(512);
  EEPROM.get(liczadres, tablica_adres);
  EEPROM.end(); //  EEPROM.commit();
}

BLYNK_WRITE(V110) {
  nrpozkod   = param.asInt();  //nr pozycji zapisu kodów - widget menu
}
BLYNK_WRITE(V111) {
  p_kod  = param.asInt(); //stan klawisza "start zapisu kodu" 1 = start procedury
}
void zapisz_kod() {
  tablica_adres[nrpozkod].kod = KOD;
  tablica_adres[nrpozkod].impuls = IMPULS;
  Serial.print(" nr  " + String(nrpozkod)); Serial.print("  zapisuje kod  " + String(KOD));  Serial.println(" impuls " + String(IMPULS));
  savetab();
} //procedura wywołania zapisu tablicy kodów do pamięci nieulotnej

void do_zapisz_kod() { // resety i zapisz kod
  zapisz_kod();
  Blynk.virtualWrite(V112, KOD); //wyświetlanie zapisywanego kodu
  Blynk.virtualWrite(V111, 0); //przełcznik procedury wyłącz
  reset_all();
}
void reset_all() { // resety po zakończeniu procedury
  o_kod = 0; KOD = 0; p_kod = 0; o_impuls = 0;
}
void KOD_do_eeprom() { //graf czyli automat skończony procedury zapisu kodu 433MHz do pamięci
  switch (stan) {
    case 1: {
        if (p_kod != 0) stan = 2; else stan = 1;
        o_kod = 0; KOD = 0;
      } break;
    case 2: {
        if (p_kod == 0) stan = 1;
        if (o_kod != 0) stan = 3;
      } break;
    case 3: {
        KOD = o_kod;
        IMPULS = o_impuls;
        o_kod = 0;
        stan = 4;
      } break;
    case 4: {
        if (p_kod == 0) stan = 5;
        if (o_kod != 0) stan = 6;
      } break;
    case 5: {
        do_zapisz_kod();
        stan = 1;
      } break;
    case 6: {
        if (o_kod == KOD) stan = 5; else stan = 3;
      } break;
    default: break;
  }
}

Zapisuję i odczytuję wszystko za jednym zamachem to znaczy zapisywana jest cała tablica pomimo zmiany tylko jednego jej rekordu. Tablica to nie tylko wygodny sposób zapisu danych do pamięci nieulotnej. To przede wszystkim wygodne narzędzie do żonglowania dużą ilością różnych danych w  programie. A dzięki temu że tablica tworzona jest w RAMie procesora to znakomicie przyspiesza wszelkie operacje na zmiennych.  Na początku programu załaduję zmienną tablica_adres  zawartością przechowywaną w eepromie i to ta zmienna będzie bazą dla wszelkich dalszych operacji na adresach. Jeśli z jakiegokolwiek powodu  pola rekordu w tablicy tablica_adres  w czasie działania programu ulegną zmianie niezwłocznie nastąpi kolejny zapis do pamięci aktualizujący zawartość eepromu. Taki sposób znacząco zwiększa ilość operacji na pamięci nieulotnej co w przyszłości może być pewnym problemem (maksymalna gwarantowana liczba zapisów dla pamięci flash to ok 10tys. razy). Ale tym sposobem mam pewność iż dane zostaną zapamiętane w pamięci co jest ważne np. w przypadku niespodziewanego resetu mikrokontrolera.

Dla sprawdzenia działania procedury przygotowałem mały test

void setup()
.................
  tablica_adres[0] = {1000000, 100};  //wypełnienie tablicy wartością początkową adresów
  tablica_adres[1] = {111111, 111};
  tablica_adres[2] = {22222, 222};


  Serial.print(tablica_adres[0].kod);  Serial.println("  wartosc poczatkowa");
  Serial.print(tablica_adres[1].kod);  Serial.println("  wartosc poczatkowa");
  Serial.print(tablica_adres[2].kod);  Serial.println("  wartosc poczatkowa");
  readtab(); // odczytuje z eepromu
  Serial.print(tablica_adres[0].kod);  Serial.println("  z eeprom");
  Serial.print(tablica_adres[1].kod);  Serial.println("  z eeprom");
  Serial.print(tablica_adres[2].kod);  Serial.println("  z eeprom");
}

W procedurze setup() ustawiam wstępnie pola rekordu zmiennej tablica_adres a następnie zapisuje ją danymi pobranymi z eepromu. Oba stany - przed i po odczycie pamięci wyświetlam. Skutek działania po resecie mikrokontrolera jak niżej



CBDO
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  

W tym tempie router powstanie pewnie w następnej pięciolatce - no ale dzięki temu jest szansa, że ciąg dalszy jednak kiedyś nastąpi.


Wcześniejsze odcinki serialu
http://100-x-arduino.blogspot.com/2017/11/router-433-mhz-aczymy-rozne-systemy.html
http://100-x-arduino.blogspot.com/2017/11/router-433-mhz-nauka-czytania-i-pisania.html
http://100-x-arduino.blogspot.com/2017/12/router433-jak-programowac-schematy.html
http://100-x-arduino.blogspot.com/2017/12/esp8266-zapis-do-pamieci-eeprom-kodow.html
http://100-x-arduino.blogspot.com/2017/12/zapis-tablicy-struktur-do-eeproma-nauka.html

109

Brak komentarzy:

Publikowanie komentarza