Skocz do zawartości

Błąd przy kopiowaniu struktury


_LM_

Pomocna odpowiedź

Cześć. Piszę program obsługujący czujniki, gdzie dane są odbierane za pomocą funkcji espnow a następnie wysyłane ze stacji bazowej na serwer RemoteMe. Działa to tak że po odebraniu danych z czujnika, stacja bazowa przełącza się w tryb WIFISTA, łączy z serwerem RemoteMe i odsyła dane. Przełączanie trybów pracy realizuję w taki sposób że po odebraniu danych z czujnika, zapisuję je do pamięci RTC a następnie resetuję ESP aby po resecie znów mógł działać jako stacja. Ogólnie to działa elegancko jednak pewna rzecz nie daje mi spokoju: jest struktura do której zapisuję lub czytam z niej

typedef struct {
  char name[SIZENAMETAB]; // nazwa czujnika
  float temp;             // temperatura
  float press;            // cisnienie
  int humm;               // wilgotnosc
  uint16_t vbat;          // U zas czujnika
  uint16_t timeToRestart; // czas do nast wysylki danych
  bool okno;              // okno 0 -> zamkniete, 1->otwrte
} readNow_t;

typedef struct {
  readNow_t data;
  uint8_t stat; // status co ma sie dziac po resecie
} sysMem_t;

sysMem_t g_structData;

Oraz kluczowe miejsce gdzie następuje błąd:

void OnDataRecv(unsigned char *mac, unsigned char *incomingData,
                unsigned char len) { // dane z czujnikow kopiowanie do pamieci rtc i restart 

g_structData.stat = 1; // przy nastepnym resecie uruchom jako stacja
memcpy(&g_structData.data, incomingData, sizeof(readNow_t)); // kopiowanie do struktury

system_rtc_mem_write(RTCSTARTADRESS, &g_structData, sizeof(sysMem_t)); // kopiowanie do pamieci rtc
 
 // digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // test

 Serial.println("Nowe dane");
 ESP.restart();
}

błąd polega na tym że jakby następowało przeciążenie stosu? nie bardzo rozumiem te komunikaty wygląda to tak:

--------------- CUT HERE FOR EXCEPTION DECODER ---------------

Exception (28):
epc1=0x4020757f epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000

>>>stack>>>

ctx: cont
sp: 3ffffdf0 end: 3fffffc0 offset: 0190
3fffff80:  00000000 00000000 00000001 4010017c
3fffff90:  3fffdad0 00000000 3ffee618 4010019d
3fffffa0:  3fffdad0 00000000 3ffee618 402057d4
3fffffb0:  feefeffe feefeffe 3ffe84ec 40100cdd
<<<stack<<<

--------------- CUT HERE FOR EXCEPTION DECODER ---------------

 ets Jan  8 2013,rst cause:2, boot mode:(3,7)

load 0x4010f000, len 3584, room 16
tail 0
chksum 0xb0
csum 0xb0
v2843a5ac
~ld

Ten system działa, jednak to mi się nie bardzo podoba, czuję że gdzieś robię błąd podczas kopiowania do pamięci RTC. 

Edytowano przez _LM_
Link do komentarza
Share on other sites

5 godzin temu, _LM_ napisał:

memcpy(&g_structData.data, incomingData, sizeof(readNow_t)); // kopiowanie do struktury

Generalnie zastanawia mnie powyższe. sizeof zwraca liczbę bajtów, natomiast jako parametr dajesz wskaźnik na strukturę, wg mnie powinieneś ten wskaźnik jawnie rzutować na (uint8_t *). Dodatkowo to nie wiem czy nie dochodzi tutaj do braku wyrównania danych struktury w pamięci. Rozmiar SIZENAMETAB nie jest tutaj jawnie podany i zakładam, że architektura w przykładzie jest 32 bit (dział ESP), a sam literał nie jest podzielny przez 4 i brakuje wyrównania. Przy operacji na wskaźnikach dla struktur niewyrównanych może dochodzić do runtime error. 

Należałoby zatem dodać atrybut __attribute__ ((packed) dla struktury  zastosować padding czyli tak skonstruować strukturę (dane wyrównujące) żeby rozmiar struktury był podzielny przez 4 lub poprzestawiać pola struktury, by pierwsze pole było miało 4 bajty.

Być może to nie rozwiąże Twojego problemu, bo podałeś dość ograniczone dane, ale powyższe zasady i tak należy stosować.

  • Lubię! 2
Link do komentarza
Share on other sites

(edytowany)

SIZENAMETAB jest zdefiniowane jako 4 znaki char. Usunąłem ze struktury zmienne bool żeby mieć pewność co do rozmiarów typów danych teraz wygląda to tak: 

typedef struct {
  char name[SIZENAMETAB]; // nazwa czujnika   4
  float temp;             // temperatura      4
  float press;            // cisnienie        4
  int humm;               // wilgotnosc       4
  uint16_t vbat;          // U zas czujnika   2
  uint8_t okno; // okno 0 -> zamkniete, 1->otwrte 1
} readNow_t;     // struktura zajmuje 19 bajtow 

A całościowo(ta do której wskaźnik pobiera funkcja memcpy()

typedef struct {
  readNow_t data;
  uint8_t stat; // status co ma sie dziac po resecie
} sysMem_t;

sysMem_t g_structData;

Jednak po gdy chcę poznać rozmiar tej struktury 

Serial.println(sizeof(sysMem_t)); // 24

wskazuje na rozmiar 24 bajtów podczas gdy liczę go "z ręki" wychodzi 20 bajtów. Tak czy owak 24 czy 20 jest podzielne przez 4(minimalny rozmiar strony w system_rtc_mem). Problem występuje nadal zastanawiam się czy może jednak w innym miejscu mam błąd. Nie wiem czy wrzucać tu całość bo kod jest dość rozgrzebany w tej chwili

Edytowano przez _LM_
Link do komentarza
Share on other sites

No i właśnie dochodzi tutaj padding. Struktura readNow_t data zostaje wyrównana do 20 bajtów, a dana uint8_t stat do 4 i struktura  g_structData ma 24 bajty.

typedef struct {
  char name[SIZENAMETAB]; // nazwa czujnika   4
  float temp;             // temperatura      4
  float press;            // cisnienie        4
  int humm;               // wilgotnosc       4
  uint16_t vbat;          // U zas czujnika   2
  uint8_t okno;           //                  1
  uint8_t padding[1]      //                  1
} readNow_t;              // struktura zajmuje 20 bajtow 

typedef struct {
  readNow_t data;         // 20
  uint8_t stat;           // 1
  uint8_t padding[3]      // 3
} sysMem_t;
                          // 24

 

Edytowano przez Zealota
  • Pomogłeś! 1
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

(edytowany)

Aa to o to chodzi z tym paddingiem 🙂 Co prawda nadal błąd występuje, wyeliminowałem inne powody. Spróbuję jeszcze rzutować na uint8_t i później kopiować do rtc_mem bajt po bajcie. Ok chyba znalazłem. Błąd nie występuje kiedy zakomentuję zapis w tym miejscu.

void OnDataRecv(unsigned char *mac, unsigned char *incomingData,
                unsigned char len) { // dane z czujnikow kopiowanie do pamieci rtc i restart 

//g_structData.stat = 1; Zapis do tej zmiennej generuje błąd

memcpy(&g_structData.data, incomingData, sizeof(readNow_t)); // kopiowanie do struktury
system_rtc_mem_write(RTCSTARTADRESS, &g_structData, sizeof(sysMem_t));
 Serial.println("Nowe dane");
 ESP.restart();
}

Może trzeba g_structData zdefiniować jako volatile? Funkcja OnDataRecv jest callbackiem więc ciężko uznać co się w tle odbywa. Co mogę jeszcze zrobić?

Uff zadziałało... Problem rozwiązało zadeklarowanie zmiennej lokalnej typu sysMem_t w funkcji OnDataRecv funkcja teraz wygląda tak: 

void OnDataRecv( unsigned char *mac, unsigned char *incomingData, 
unsigned char len) { // dane z czujnikow kopiowanie do pamieci rtc i restart
   
   

  sysMem_t x;
  memcpy(&x.data, incomingData, sizeof(readNow_t));
  x.stat = 1;
  system_rtc_mem_write(RTCSTARTADRESS, &x, sizeof(sysMem_t));
  Serial.println("Nowe dane");
  ESP.restart();
}

Nie mam pojęcia dlaczego dlaczego zapis do globalnej nie działał. Z jednej strony fajnie by było znać przyczynę takiego zachowania a z drugiej - jest to nauczka i przypomnienie żeby nie nadużywać właśnie zmiennych globalnych. Wielkie dzięki @Zealota z pomoc. W prawdzie już dosyć długo programuję w C ale ten motyw z paddingiem nie był mi znany jak do tej pory( lub gdzieś przeoczyłem). No mogę tworzyć kolejne problemy 😄 Pozdrawiam!

Edytowano przez _LM_
Link do komentarza
Share on other sites

2 godziny temu, _LM_ napisał:

Problem rozwiązało zadeklarowanie zmiennej lokalnej typu sysMem_t

Mam przeczucie, że jednak tylko "zapudrowałeś" problem. Tworzysz lokalną strukturę i operujesz na niej, a co ze strukturą globalną? Nic w niej nie zmieniłeś, przynajmniej w tej funkcji, zatem nie jest potrzebna w ogóle - tak by wyglądało 🙂

Z innych ciekawostek używasz memcpy do kopiowania struktur, ale przecież struktury można łatwo kopiować jedna do drugiej poprzez przyrównanie, a resztą zajmie się kompilator, na pewno dobrze i nie trzeba nic wyliczać poprzez sizeof.

sysMem_t x;
x = incomingData;

Dodatkowo spróbuj zadziałać poprzez wskaźnik:

sysMem_t * ptr = &g_structData;
ptr->stat = 1;

Strukturę tę najlepiej też ustawić jako static, w ten sposób będzie zainicjowana zerami, można też wypełnić tę strukturę podczas deklaracji i inicjalizacji, jeśli używasz jej w innych jednostkach kompilacji to oczywiście należałoby docierać do niej poprzez funkcje i wskaźniki.

Link do komentarza
Share on other sites

(edytowany)
48 minut temu, Zealota napisał:

Mam przeczucie, że jednak tylko "zapudrowałeś" problem. Tworzysz lokalną strukturę i operujesz na niej, a co ze strukturą globalną? Nic w niej nie zmieniłeś, przynajmniej w tej funkcji, zatem nie jest potrzebna w ogóle - tak by wyglądało 🙂

W sumie do tego dążę, jak narazie do globalnej (z rtc_mem)kopiuję dane podczas restartu stacji kiedy ma ona pracować jako WIFISTA, wtedy następuje wysyłka do RemoteMe i znów reset oraz oczekiwanie na dane z czujników .

48 minut temu, Zealota napisał:

Z innych ciekawostek używasz memcpy do kopiowania struktur, ale przecież struktury można łatwo kopiować jedna do drugiej poprzez przyrównanie, a resztą zajmie się kompilator, na pewno dobrze i nie trzeba nic wyliczać poprzez sizeof.

Przy kopiowaniu tak, ale do funkcji dostaję wskaźnik do danych a nie strukturę.

void OnDataRecv( unsigned char *mac, unsigned char *incomingData, 
unsigned char len)

 Mógłbym też skorzystać z zmiennej len. W tej chwili obsługiwany jest tylko jeden czujnik więc i obsługa danych uproszczona. W ogóle wziąłem się za temat bo chcę mieć kilka esp-ków zasilanych bateryjnie i stąd takie kombinacje, wcześniej czujnik bezpośrednio łączył się do serwera remoteme jednak nie byłem zadowolony z czasów dostępu. Niekiedy do jednej sekundy, nawet, gdy miałem IP przypisane na stałe. Stąd też moje zainteresowanie skierowałem w stronę - można tak to chyba nazwać - "protokołu" espnow. W tej chwili czas na wysyłkę spadł do 150ms co jest dobrym wynikiem zwłaszcza że większość czasu czujnik śpi z prądem na poziome 15µA

48 minut temu, Zealota napisał:

Strukturę tę najlepiej też ustawić jako static, w ten sposób będzie zainicjowana zerami, można też wypełnić tę strukturę podczas deklaracji i inicjalizacji, jeśli używasz jej w innych jednostkach kompilacji to oczywiście należałoby docierać do niej poprzez funkcje i wskaźniki.

Tak to prawda, u mnie ta struktura pośrednicząca jest od razu kopiowana do rtc_mem, program jest w tej chwili dosyć prosty.Przy większej ich liczbie możliwe że będzie trzeba jakoś inaczej to ogarnąć, choć pomysły już na to mam. Teraz zajmuję się drugą stroną czyli nadawcą danych z czujnika. 

51 minut temu, Zealota napisał:

Dodatkowo spróbuj zadziałać poprzez wskaźnik:

A sprawdzę

Edytowano przez _LM_
Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.