Skocz do zawartości
Komentator

Kurs Intel Edison - #6 - programy w Arduino, LCD, czujniki

Pomocna odpowiedź

html_mig_img
Tym razem zajmiemy się kontynuacją tematu programowania platformy Intel Edison z poziomu Arduino IDE. Ta szybka powtórka pozwoli nam od następnego odcinka zajmować się już znacznie ciekawszym tematem, czyli IoT.Najpierw sprawdźmy jednak, jak szybko, dzięki Arduino można wykorzystać wyświetlacz LCD oraz różne czujniki (m.in. ciśnienia i temperatury).

UWAGA, to tylko wstęp! Dalsza część artykułu dostępna jest na blogu.

Przeczytaj całość »

Poniżej znajdują się komentarze powiązane z tym wpisem.

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Cześć, Edison to fajna zabawka, wrażenia z kursu jeszcze opiszę (co jest aktualne, a co się trochę zdeaktualizowało).

Napotkałem pewien problem, pogrzebałem w czeluściach Internetu i nie mogę nigdzie znaleźć sensownego rozwiązania. Mianowicie chodzi o programowanie obiektowe w Arduino. Tak aby nie mieć wszystkiego w jednej funkcji loop, która może mieć nawet tysiące linii kody tylko dzielić kod na klasy, najlepiej te klasy zapisywać w oddzielnych plikach czyli tak bardziej współcześnie (pamiętam jedną z firm, w której pracowałem, pod koniec lat 2000, w której były pliki po 3500 linii kodu zawierajace w sobie PHP, MySQL, HTML, JavaScript i dla urozmaicenia czasami coś jeszcze).

Do rzeczy. Poeksperymentowałem i napisałem prosty wrapper do LCD. Podobieństwa do znanych języków i wskazówki z błędów kompilatora dały taki rezultat.

 

#include <LiquidCrystal_I2C.h>

class MessageGenerator {
    String lines[2];
    bool clear = false;

    LiquidCrystal_I2C *screen;

  public: MessageGenerator() {
      this->screen = new LiquidCrystal_I2C(0x27, 16, 2);
      this->screen->init();
      this->screen->backlight();
      this->screen->noAutoscroll();
    }

  public: ~MessageGenerator() {
      free(this->screen);
      free(lines);
    }

  public: void setLine(String s, int l) {
      String oldV = lines[l];
      if (oldV != s) {
        lines[l] = s;
        clear = true;
      }
    }

  public: void display() {
      if (clear) {
        this->screen->clear();
        clear = false;
      }

      this->screen->setCursor(0, 0);
      this->screen->print(lines[0]);
      this->screen->setCursor(0, 1);
      this->screen->print(lines[1]);
    }
};

MessageGenerator *lcd;// = new MessageGenerator();

void setup() {
  Serial.begin(9600);
  Serial.println("setup arduino");

  lcd = new MessageGenerator();

  lcd->setLine("Have a Nice Day", 0);
  lcd->display();

}

Działa to losowo tj. czasami działa, a czasami nie działa. Nie do końca wiem dlaczego. Nie wiem czy Arduino jest bardziej podobny do C czy bardziej do C++ to też nie wiem jak tego używać np. czy jest jakiś garbage collector czy też manualnie zarządzać pamięcią, jak przydzielać adresy obiektom (zwyczajnie używać keyword new czy np. malloc aby zarezerwować jakiś blok pamięci). Obecnie czasami działa to dobrze, a czasami mam na wyświetlaczu dyskotekę lub aplikacja wiesza się w momencie wywoływania konstruktora klasy na wywołaniu this->screen->init(). Zaglądałem do wnętrza tej biblioteki LiquidCrystal_I2C i tam w init dużo ciekawego się nie dzieje poza tym, że jest powoływana instancja klasy Wire. Miałem już koncepcję, że nie deallocuję, ale nie bardzo wiem jak to debugować - tu niestety minimalizm IDE Arduino daje o sobie znać, a do Eclipse nie mam cierpliwości, choć pewnie wrócę do pomysłu konfiguracji Eclipse za pomocą Sloebera (niestety na Mac OS X nie chce działać tak automatycznie i idiotoodpornie jak to opisują na niektórych forach).

Jak na Edisonie i w ogóle na Arduino działają sketche. Czy w danej chwili działa tylko jeden sketch czy też może działać kilka w tym samym czasie. Myślałem, że wgranie nowego sketcha ubija poprzedni. Próbowałem też w konsoli zrobić ps | grep sketch i dalej na niektórych procesach wykonać kill -9, ale to nie daje oczekiwanych efektów - tam jest jakiś proces sketch_restart. Efekt jest taki, że gdy wyświetlacz mi nie działa to nawet uruchomienie przykładowego sketcha nie powoduje, że on działa. Czy po restarcie urządzenia/odłączenia zasilania system bootuje się z aktywnym ostatnio wgranym sketchem? Czasami się też tak zdarza, że na wyświetlaczu widzę napisy, których być nie powinno, gdyż te fragmenty kodu są w danym uruchomieniu zakomentowane.

Również czujnik temperatury i wilgotności działa u mnie losowo, tylko tutaj wynik losowania z opcją działanie jest rzadszy (często go nie wykrywa, z braku odpowiedniego modelu kupiłem w Botlandzie Si7021, szczęśliwie jest biblioteka do Arduino, sprawdzałem na przykładach i również zachowuje się losowo). O problemy podejrzewałem luźne wtyki na broadboardzie, ale po podłączeniu bezpośrednio do płytki Intela też nie działa.

Mam jeszcze pytanie o programowanie wielowątkowe na Arduino. Czy to w ogóle jest możliwe i praktykowane? Jak rozumiem zasadniczo jest do dyspozycji jeden runloop, który jest zawarty w funkcji loop. Czy jest możliwość wydzielenia jakiejś operacji do innego wątku czy kolejki lub stworzenia własnego, dodatkowego runloopa? To tak bardziej na przyszłość mnie zaczęło zastanawiać. Wiem, że jakieś złudzenie wielozadaniowości można uzyskać za pomocą millis() (pisałeś o tym w ostatniej lekcji drugiej części kursu Arduino), ale to jednak trochę karkołomna implementacja.

Próbowałem Googlać, ale większość odpowiedzi o OOP pokazuje tutoriale jak zbudować własną bibliotekę do Arduino, a to nie jest moim celem - przynajmniej narazie.

W zasadzie mogłem to napisać jako pomysł na artykuł czyli programowanie w Arduino dla średniozaawansowanych.

P.s. Czy w Arduino dostępne są bloki/funkcje anonimowe/clousures czy jak to się może nazywać?

P.s. 2 Chciałem połączyć wszystkie lekcje w jeden układ aby było fajniej i sobie trochę skomplikowałem. 😉

P.s. 3 Jeśli nie masz czasu odpisywać na moje dziesiątki pytań, a masz jakiś reference handbook do programowania w Arduino to się również ucieszę. Na arduino.cc (https://www.arduino.cc/reference/en/#page-title) nie znalazłem odpowiedzi na nurtujące mnie kwestie.

P.s. 4 Miałem pomysł aby zrobić kurs Arduino za pomocą Edisona (uniknąłbym kupowania kolejnych gratów, a większość z nich już mam) tylko zastanawiam się czy to napewno dobry pomysł.

P.s. 5 Z góry bardzo dziękuję za odpowiedź, na ten kurs niestety nie mogę Wam zrobić donate kupując zestaw w Botlandzie bo już zestawów nie mają.

P.s. 6 Dla zainteresowanych mój losowo działający sketch.

#include <Adafruit_Si7021.h>
#include <LiquidCrystal_I2C.h>

class MessageGenerator {
    String lines[2];
    bool clear = false;

    LiquidCrystal_I2C *screen;

  public: MessageGenerator() {
      this->screen = new LiquidCrystal_I2C(0x27, 16, 2);
      this->screen->init();
      this->screen->backlight();
      this->screen->noAutoscroll();
    }

  public: ~MessageGenerator() {
      free(this->screen);
      free(lines);
    }

  public: void setLine(String s, int l) {
      String oldV = lines[l];
      if (oldV != s) {
        lines[l] = s;
        clear = true;
      }
    }

  public: void display() {
      if (clear) {
        this->screen->clear();
        clear = false;
      }

      //    screen.home();
      this->screen->setCursor(0, 0);
      this->screen->print(lines[0]);
      this->screen->setCursor(0, 1);
      this->screen->print(lines[1]);
    }
};

MessageGenerator *lcd;// = new MessageGenerator();

Adafruit_Si7021 sensor = Adafruit_Si7021();

const int ALARM_PIN = 2;
const int DOOR_PIN = 3;
const int MOTION_PIN = 4;
const int LIGHT_PIN = 0;

const int kDuration = 100;

int lastLightLevel = 0;

double i = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("setup arduino");

  lcd = new MessageGenerator();

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(ALARM_PIN, OUTPUT);
  pinMode(DOOR_PIN, INPUT_PULLUP);
  pinMode(MOTION_PIN, INPUT_PULLUP);
  digitalWrite(ALARM_PIN, HIGH);

  lcd->setLine("Have a Nice Day", 0);
  lcd->display();

//  if (!sensor.begin()) {
//    lcd->setLine("Sensor failure", 1);
//    lcd->display();
//    sensor.reset();
//  }

}

void loop() {
  digitalWrite(ALARM_PIN, HIGH);
  monitorAlarm();

  i += double(double(kDuration) / 1000);

  int ii = (int)i;
  if (ii % 3 == 0) {
    displayLightLevel();
  } else if (ii % 3 == 1) {
    displayHumidity();
  } else if (ii % 3 == 2) {
    displayTemperature();
  }

  delay(kDuration);
}

void monitorAlarm() {
  bool doorOpened = digitalRead(DOOR_PIN);
  bool motionDetected = digitalRead(MOTION_PIN);
  if (doorOpened || motionDetected) {
    //    digitalWrite(ALARM_PIN, LOW);
    digitalWrite(LED_BUILTIN, HIGH);
    Serial.print("Alarm triggered! ");
    if(doorOpened) {
      Serial.print("DOOR ");
    }
    if(motionDetected) {
      Serial.print("MOTION ");
    }
    Serial.println("");
    lcd->setLine("ALARM!", 0);
    lcd->display();
  } else {
    lcd->setLine("Alarm Ended", 0);
    lcd->display();
    digitalWrite(ALARM_PIN, HIGH);
    digitalWrite(LED_BUILTIN, LOW);
  }
}

void displayLightLevel() {
  int val = analogRead(LIGHT_PIN);

  if (abs(val - lastLightLevel) > 10) {
    Serial.print("Light lvl = ");
    Serial.println(val);
  }

  String s = "Light " + String(val);

  lcd->setLine(s, 1);
  lcd->display();

  lastLightLevel = val;
}

void displayHumidity() {
  int val = (int)sensor.readHumidity();
  String s = "Humidity " + String(val) + "%";
  Serial.println(s);
  lcd->setLine(s, 1);
  lcd->display();
}

void displayTemperature() {
  int val = (int)sensor.readTemperature();
  String s = "Temperature " + String(val);
  Serial.println(s);
  lcd->setLine(s, 1);
  lcd->display();
}

 

Edytowano przez jordanj

Udostępnij ten post


Link to post
Share on other sites
14 godzin temu, jordanj napisał:

Mianowicie chodzi o programowanie obiektowe w Arduino. Tak aby nie mieć wszystkiego w jednej funkcji loop, która może mieć nawet tysiące linii kody tylko dzielić kod na klasy, najlepiej te klasy zapisywać w oddzielnych plikach czyli tak bardziej współcześnie (pamiętam jedną z firm, w której pracowałem, pod koniec lat 2000, w której były pliki po 3500 linii kodu zawierajace w sobie PHP, MySQL, HTML, JavaScript i dla urozmaicenia czasami coś jeszcze).

Programowanie obiektowe to nie jedyne wyjście. Może też zwyczajnie pisać program bazując na funkcjach, które wydzielisz do innych plików. Jeśli chcesz zobaczyć jak to działa w Arduino to zerknij tutaj: Arduino, co w środku… – #3 – źródło wbudowanych(?) funkcji

14 godzin temu, jordanj napisał:

Jak na Edisonie i w ogóle na Arduino działają sketche. Czy w danej chwili działa tylko jeden sketch czy też może działać kilka w tym samym czasie. Myślałem, że wgranie nowego sketcha ubija poprzedni. Próbowałem też w konsoli zrobić ps | grep sketch i dalej na niektórych procesach wykonać kill -9, ale to nie daje oczekiwanych efektów - tam jest jakiś proces sketch_restart. Efekt jest taki, że gdy wyświetlacz mi nie działa to nawet uruchomienie przykładowego sketcha nie powoduje, że on działa. Czy po restarcie urządzenia/odłączenia zasilania system bootuje się z aktywnym ostatnio wgranym sketchem? Czasami się też tak zdarza, że na wyświetlaczu widzę napisy, których być nie powinno, gdyż te fragmenty kodu są w danym uruchomieniu zakomentowane.

Niestety w tej chwili ciężko powiedzieć jak powinno być... Kurs został zarchiwizowany, ponieważ Intel przestał rozwijać Edisona. Możliwe, że natrafiasz na bugi, które nie zostały rozwiązane. W teorii miała być pełna kompatybilność z Arduino, ale z czasem pojawiały się różne (mniej lub bardziej widoczne) rozbieżności.

14 godzin temu, jordanj napisał:

Mam jeszcze pytanie o programowanie wielowątkowe na Arduino. Czy to w ogóle jest możliwe i praktykowane? Jak rozumiem zasadniczo jest do dyspozycji jeden runloop, który jest zawarty w funkcji loop. Czy jest możliwość wydzielenia jakiejś operacji do innego wątku czy kolejki lub stworzenia własnego, dodatkowego runloopa? To tak bardziej na przyszłość mnie zaczęło zastanawiać. Wiem, że jakieś złudzenie wielozadaniowości można uzyskać za pomocą millis() (pisałeś o tym w ostatniej lekcji drugiej części kursu Arduino), ale to jednak trochę karkołomna implementacja.

Nie ma czegoś takiego jak wielowątkowość. W uproszczeniu jest jedna pętla i tyle. Jedyna opcja to mądre napisanie zawartości tej pętli. W przypadku Arduino i ogólnie małych mikrokontrolerów nie praktykuje się raczej czegoś takiego. Jest za mało zasobów na prawdziwe, wielowątkowe aplikacje. Przy trochę większych układach stosuje się np. systemy czasu rzeczywistego, w których są domyślnie wbudowane narzędzia do wielowątkowości. Zerknij np. na: https://www.freertos.org/RTOS_ports.html

Udostępnij ten post


Link to post
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!

Gość
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...