Brak możliwości zdefiniowania callback'ów

krycha88
Posty: 5187
Rejestracja: pt lis 16, 2018 7:25 am
Kontakt:

klew pisze: wt lut 04, 2020 9:36 am Takie klasy z własnymi sensorami można trzymać w pliku ino, albo dorzucić własne pliki .h w swoim projekcie. Odradzam wrzucanie własnych plików do lokalnej kopii biblioteki, bo przy aktualizacji może się to rozjechać (np. skasujemy sobie pliki).
Jeśli ktoś pisze obsługę sensora do jakiegoś konkretnego urządzenia, to może to wrzucić do pliku .h i dodać do biblioteki, a następnie zrobić pull request do https://github.com/SUPLA/arduino/tree/develop
Jeśli ktoś chce dodać obsługe jakiegoś sensora, to niech tylko dokładnie w komentarzach w pliku .h dopisze jaki HW jest tutaj obsługiwany i na jakim board to używał/testował. Proszę też dodawać informację jakie dodatkowe biblioteki są używane oraz skąd je pobrać.
Projekt używa też formatowania zdefiniowanego w clang-format (plik konfiguracyjny jest w repo) - także najlepiej przed stworzeniem pull request jest odpalić formatowanie na swoim kodzie.
Właśnie doszedłem do momentu, że potrzebuję własnej funkcji nie zmieniając biblioteki. Wymyśliłem taką klasę:

Kod: Zaznacz cały

#ifndef SuplaSensor_h
#define SuplaSensor_h

#include <supla/sensor/DS18B20.h>

class DS18B20 : public Supla::Sensor::DS18B20 {
  public:
    DS18B20(uint8_t pin, uint8_t *deviceAddress = nullptr)
      : Supla::Sensor::DS18B20(pin, deviceAddress) {};
      
    uint8_t  getPin() {
      return myBus->pin;
    }

};

#endif //SuplaSensor_h
W pliku *.ino dodaję:

Kod: Zaznacz cały

#include "SuplaSensor.h"
std::vector <DS18B20 *> sensor;
wszystko działa poprawnie aż do momentu gdy chcę dodać referecję #include "SuplaSensor.h" w innym pliku:

Kod: Zaznacz cały

#include "SuplaSensor.h"
extern std::vector <DS18B20 *> sensor;
dostaję wtedy błąd kompilacji:

Kod: Zaznacz cały

....
multiple definition of `Supla::Sensor::DS18B20::oneWireBus'; sketch\Primary_GUI_nexo.ino.cpp.o:(.bss._ZN5Supla6Sensor7DS18B2010oneWireBusE+0x0): first defined here
....
i pytanie jak zadeklarować tą zmienną aby było poprawnie :)
https://gui-generic-builder.supla.io/
Awatar użytkownika
klew
Posty: 8184
Rejestracja: czw cze 27, 2019 12:16 pm
Lokalizacja: Wrocław

krycha88 pisze: czw lut 27, 2020 9:05 pm dostaję wtedy błąd kompilacji:

Kod: Zaznacz cały

....
multiple definition of `Supla::Sensor::DS18B20::oneWireBus'; sketch\Primary_GUI_nexo.ino.cpp.o:(.bss._ZN5Supla6Sensor7DS18B2010oneWireBusE+0x0): first defined here
....
i pytanie jak zadeklarować tą zmienną aby było poprawnie :)
Pewnie masz z dwóch plików .ino/.cpp zrobionego include'a do klasy DS18B20.
Niestety Arduino IDE nie ma żadnego sensownego build systemu i nie znam sposobu jak to zrobić, aby zawsze było dobrze. W bibliotekach są kompilowane tylko pliki cpp z głównego katalogu biblioteki i zawsze wszystkie. Tworzy to pewne problemy: cały kod z wszystkich plików cpp musi się zawsze skompilować poprawnie, aby biblioteka ruszyła. Problem polega na tym, że różne sensory mają dependencje do zewnętrznych bibliotek (np. DS, PZEMy, DHT, ...) Gdyby więc wydzielić poprawnie definicje funkcji oraz inicjalizacje zmiennych statycznych, to aby używać SuplaDevice trzeba by zawsze mieć zainstalowane wszystkie biblioteki, od których SuplaDevice zależy - niezależnie od tego, czy używamy danego sensora czy nie.
W związku z tym, zdecydowałem się na umieszczanie kodu w plikach .h w podkatalogach. Dzięki temu tylko include'owane sensory będą się kompilować i tylko wtedy gdy zrobimy include'a, będzie wymagana zewnętrzna biblioteka.

Niestety jeśli Twój projekt zawiera więcej plików cpp/ino, które zrobią include'a do tego samego pliku h, to definicje w nich zawarte zostaną skompilowane dwa razy i zdezorientowany linker się rozjedzie ;).

Można to spróbować głębiej przemyśleć i jakoś rozwiązać. Na chwilę obecną proponuję dwie alternatywy:
1. Przeorganizuj swój kod, tak aby include nie był robiony dwa razy z dwóch różnych jednostek kompilacji
2. Pisz jeśli potrzebujesz jakieś dodatkowe funkcje na interfejsie do sensorów - mogę dodać je do oficjalnej biblioteki jeśli będą przynajmniej sprawiały wrażenie, że może się komuś jeszcze przydadzą ;)
Widzimy się na Supla Offline Party vol. 2 :!:
krycha88
Posty: 5187
Rejestracja: pt lis 16, 2018 7:25 am
Kontakt:

klew pisze: pt lut 28, 2020 12:10 am Pewnie masz z dwóch plików .ino/.cpp zrobionego include'a do klasy DS18B20.
W tym rzecz, że mam tylko jednego include'a do klasy DS18B20
https://github.com/krycha88/Primary_GUI ... laSensor.h
oraz odwołuje się SuplaSensor.h:
https://github.com/krycha88/Primary_GUI ... xo.ino#L22

problem zaczyna pojawiać się gdy dodam:
https://github.com/krycha88/Primary_GUI ... rver.h#L26

klew pisze: pt lut 28, 2020 12:10 am Można to spróbować głębiej przemyśleć i jakoś rozwiązać. Na chwilę obecną proponuję dwie alternatywy:
1. Przeorganizuj swój kod, tak aby include nie był robiony dwa razy z dwóch różnych jednostek kompilacji
Na razie nie mam pomysłu jak to ładnie zrobić :)
https://gui-generic-builder.supla.io/
Awatar użytkownika
klew
Posty: 8184
Rejestracja: czw cze 27, 2019 12:16 pm
Lokalizacja: Wrocław

Masz SuplaWebServer.cpp, które jest druga jednostką kompilacji i drugi raz zasysa klasę ds.
Widzimy się na Supla Offline Party vol. 2 :!:
krycha88
Posty: 5187
Rejestracja: pt lis 16, 2018 7:25 am
Kontakt:

klew pisze: pt lut 28, 2020 7:19 am Masz SuplaWebServer.cpp, które jest druga jednostką kompilacji i drugi raz zasysa klasę ds.
teraz rozumiem, ale nie mam pojęcia jak to przeskoczyć :?
https://gui-generic-builder.supla.io/
Awatar użytkownika
klew
Posty: 8184
Rejestracja: czw cze 27, 2019 12:16 pm
Lokalizacja: Wrocław

Jako workaround - wrzuć sobie do biblioteki do głównego katalogu plik DS18B20.cpp i wrzuć tam wszystkie definicje funkcji z pliku .h oraz linię inicjalizującą zmienną statyczna oneWireBus. Plik .h wyczyść odpowiednio.

To powinno pomóc, aby Twój program się kompilował, a ja w najbliższym czasie znajdę na to jakieś rozwiązanie.
Widzimy się na Supla Offline Party vol. 2 :!:
krycha88
Posty: 5187
Rejestracja: pt lis 16, 2018 7:25 am
Kontakt:

klew pisze: pt lut 28, 2020 8:54 am Jako workaround - wrzuć sobie do biblioteki do głównego katalogu plik DS18B20.cpp i wrzuć tam wszystkie definicje funkcji z pliku .h oraz linię inicjalizującą zmienną statyczna oneWireBus. Plik .h wyczyść odpowiednio.

To powinno pomóc, aby Twój program się kompilował, a ja w najbliższym czasie znajdę na to jakieś rozwiązanie.
wpadłeś na jakieś rozwiązanie? :)
https://gui-generic-builder.supla.io/
krycha88
Posty: 5187
Rejestracja: pt lis 16, 2018 7:25 am
Kontakt:

klew pisze: śr lut 26, 2020 11:02 pm
krycha88 pisze: śr lut 26, 2020 9:52 pm
Działa jako tako :D :D nie kombinuje już poczekam cierpliwie :)
W Twoim kodzie obiekty DS'ów są tworzone i one będą działać, natomiast wskaźnik dsSensors będzie wskazywał na tylko jeden obiekt - ostatnio stworzony DS.

Piszesz, że nie znasz wielkości tablicy, a tworzysz MAX_DS_COUNT obiektów, więc jest to trochę niespójne.

Jeśli nie potrzebujesz w kodzie odwoływać się do DS'ów, to wystarczy w pętli stworzyć odpowiednią ilość DSów:

Kod: Zaznacz cały

for (int i = 0; i < dsCount; i++) {
  new Supla::Sensor::DS18B20(dsPin[i], dsAddr[i]);
}
Przy czym zakładam, że wcześniej zainicjalizowałeś tablicę dsPin oraz dsAddr.

Jeśli natomiast chcesz się odwoływać do tych obiektów (np. czytać wartość i ją pokazać w web interface, albo na oled), to trzeba wskaźniki do tych obietków zapisać. Jeśli mamy ograniczenie do MAX_DS_COUNT, to:

Kod: Zaznacz cały

Supla::Sensor::DS18B20 *dsSensors[MAX_DS_COUNT];

void setup() {
  ....
  if (dsCount <= MAX_DS_COUNT) {
    for (int i = 0; i < dsCount; i++) {
      dsSensors[i] = new Supla::Sensor::DS18B20(dsPin[i], dsAddr[i]);
    }
  }
  ...
}
Jeśli nie chcesz się ograniczać do MAX_DS_COUNT, to w kompilatorze C++ na Arudino na ESP działają standardowe kontenery. Można więc użyć vector:

Kod: Zaznacz cały

std::vector<Supla::Sensor::DS18B20 *> dsSensors;

void setup() {
  ....
  for (int i = 0; i < dsCount; i++) {
    dsSensors.push_back(new Supla::Sensor::DS18B20(dsPin[i], dsAddr[i]));
  }
  ...
}

// aby dostać się do DSa, wystarczy:
dsSensors[4]->metodaOdpalanaNaDS18B20();
Kodu nie kompilowałem, także mogą tam być błędy
Bardzo polubiłem Vectory są bardzo wygodne, ale napotkałem na ich wadę. Samo wywołanie

Kod: Zaznacz cały

std::vector<Supla::Sensor::DS18B20 *> dsSensors;
alokuje pamięć pomimo, że nie zawiera żadnych elementów. Przy większej ilości czujników staje się to już problematyczne pomimo, że nie wykorzystuje się ich wszystkich.

Z dynamiczną tablicą też nie jest kolorowo bo wywołanie

Kod: Zaznacz cały

Supla::Sensor::DS18B20 *dsSensors[10];
również zajmuje pamięć ale porównując to do vectora zajmuje jej dużo więcej.


czytałem, że można napisać własny alokator dla vectorów ale czy to coś tutaj pomoże? Może jest inny sposób na zaoszczędzenie pamięci?
https://gui-generic-builder.supla.io/
Awatar użytkownika
klew
Posty: 8184
Rejestracja: czw cze 27, 2019 12:16 pm
Lokalizacja: Wrocław

krycha88 pisze: wt gru 22, 2020 1:59 pm Bardzo polubiłem Vectory są bardzo wygodne, ale napotkałem na ich wadę. Samo wywołanie

Kod: Zaznacz cały

std::vector<Supla::Sensor::DS18B20 *> dsSensors;
alokuje pamięć pomimo, że nie zawiera żadnych elementów. Przy większej ilości czujników staje się to już problematyczne pomimo, że nie wykorzystuje się ich wszystkich.

Z dynamiczną tablicą też nie jest kolorowo bo wywołanie

Kod: Zaznacz cały

Supla::Sensor::DS18B20 *dsSensors[10];
również zajmuje pamięć ale porównując to do vectora zajmuje jej dużo więcej.

czytałem, że można napisać własny alokator dla vectorów ale czy to coś tutaj pomoże? Może jest inny sposób na zaoszczędzenie pamięci?
Tak, vector to bardzo wygodny kontener. On musi allokować pamięć, bo sam jest obiektem. Implementacja zależy od kompilatora i biblioteki standardowej. Ale na pewno przechowuje tam wskaźnik na dane oraz rozmiar tablicy. Nie pamiętam, czy robi jakąś pre-allokacje na same dane. Pogoogluj sobie o std::vector i metodzie reserve().
Jedynym powodem, dlaczego tego nie używam w bibliotece jest brak implementacji kontenerów w kompilatorze na AVR (Arduino Mega).

Odnośnie tablicy wskaźników - nie musisz jej inicjalizować 10 elementami. Możesz zrobić wskaźnik do tablicy wskaźników i dynamicznie allokować pamięć wg potrzeb.

Kod: Zaznacz cały

  Supla::Sensor::DS18B20 **dsSensors;
  dsSensor = new Supla::Sensor::DS18B20[10];
Alternatywnie - wszystkie elementy w bibliotece dziedziczą po klasie Supla::Element. Każdy stworzony element zapisuje się na końcu listy, więc jest zawsze dostęp do wszystkich stworzonych elementów - tylko nie będziesz wiedział jakiego typu one są.
Możesz też próbować robić tablice wskaźników na klasy bazowe, aby zbytnio się nie rozdrabniać tam gdzie nie potrzeba (tzn. jeśli nie potrzebujesz wiedzieć dokładnie jakiego typu jest to obiekt).
Widzimy się na Supla Offline Party vol. 2 :!:
krycha88
Posty: 5187
Rejestracja: pt lis 16, 2018 7:25 am
Kontakt:

Dzięki za podpowiedzi, tablica wskaźników wydaje się w tym przypadku najbardziej optymalna.

Znalazłem jeszcze ciekawa implementację vectora dla Arduino która też powinna się sprawdzić
https://github.com/zacsketches/Arduino_Vector

Nie testowałem jeszcze :)
https://gui-generic-builder.supla.io/
ODPOWIEDZ

Wróć do „Arduino IDE”