Skocz do zawartości

Arduino w modelarstwie kolejowym


prezesedi

Pomocna odpowiedź

(edytowany)

Podpiąłem wszystko jak należy, wgrałem brakujące biblioteki. Po załadowaniu kodu na wyświetlaczu pojawił się napis "MAKIETA V4"

Ledy podpięte do P15 i P16, wprowadzenie 'D' '0' '*' daje czerwoną migającą, a na wyświetlaczu "D Sz"

MISTRZOSTWO

 

EDIT.

Pytanie:

czy możemy zamienić (na wyświetlaczu "D Sz") na (na wyświetlaczu "Tar.2 Sz")

Edytowano przez prezesedi
Link do komentarza
Share on other sites

3 godziny temu, prezesedi napisał:

czy możemy zamienić (na wyświetlaczu "D Sz") na (na wyświetlaczu "Tar.2 Sz")

I właśnie o tym jest (między innymi) następna część 🙂

Spróbujmy teraz zmusić wyświetlacz do wyświetlania bardziej sensownych rzeczy.

Zacznijmy od tego, że numerek semafora niewiele nam mówi, warto by było wprowadzić jakąś nazwę. W tym celu w klasie Semafor musimy:

  • dodać dodatkową zmienną prywatną, w której będzie zapamiętana nazwa;
  • dodać parametr do konstruktora;
  • i wreszcie dopisać funkcję, która zwróci nam nazwę semafora.

Deklaracja klasy semafor wyglądać teraz będzie tak:

class Semafor {
    public:
    Semafor(const SemaProg *program, uint8_t whereto, const char *name);
    void set(uint8_t command);
    void update();
    
    // pobranie aktualnie świecących LED
    uint8_t get() {return currentLed;};

    // przesunięcie dla skonstruowania danych do PCF-a
    uint8_t whereto() {return _whereto;};

    // czy na dajen pozycji jest program?
    bool prog(uint8_t n) {return _program[n].prog != 0;};

    // pobierz nazwę danej pozycji
    const char *pgname(uint8_t n) {return _program[n].name;};

    // pobierz numer aktualnie wyświetlanej pozycji
    uint8_t current() {return _current;};

    // pobierz nazwę semafora
    const char *name() {return _name;};
    
    // prowizoryczna tablica konwersji liczby 1..14 na znak 1..9 0 A-D
    // static const char *sema2c; nie będzie już potrzebna
    
    private:
    const struct SemaProg *_program;
    uint8_t _whereto;
    uint8_t ledMask;
    uint8_t blinkMask;
    uint8_t currentLed;
    uint8_t _current;
    uint32_t startTime;
    const char *_name;
};


a funkcja konstruktora:

Semafor::Semafor(const SemaProg *program, uint8_t whereto, const char *name)
{
    _program=program;
    _whereto=whereto;
    _name=name;
    _current = SEM_NEUTRAL;
    ledMask = ledMask = _program[_current].prog & 0x1f;
    blinkMask=0;
}

Teraz zamiast wyświetlać jakiś głupi numerek możemy w funkcji wyświetlania na monitorze serial zamienić wszystkie wystąpienia

Semafor::sema2c[kbdSema]

na

semafor[semaforMap[kbdSema]-1].name()

Podobnie w funkcji wyświetlacza, z tym że tam dodatkowo musimy zmienić wypisywanie jednego znaku

display.write(Semafor::sema2c[kbdSema])

na wypisywanie całej nazwy:

display.print(semafor[semaforMap[kbdSema]-1].name());

Pozostaje tylko usunięcie z pliku semafor.cpp niepotrzebnej już linii:

const char *Semafor::sema2c=" 1234567890ABCD ";

oraz oczywiście dodanie nazw w deklaracji tablicy, np:

static Semafor semafor[]={
    Semafor(semaTable,WHERETO(0,0),"Sem1"), // semafor pierwszy piny P0..P4
    Semafor(semaTable,WHERETO(1,0),"Sem2"), // semafor drugi piny P10..P14
    Semafor(tarczaTable,WHERETO(0,5),"TM1"), // tarcza pierwsza piny P5..P6
    Semafor(tarczaTable,WHERETO(1,5),"TM2") // tarcza druga piny P15..P16
};

I już możemy cieszyć się ładnymi nazwami na wyświetlaczu i w monitorze serial. Przy okazji: nazwa semafora nie może być dłuższa niż 5 znaków (więcej się nie zmieści na wyświetlaczu), ale to powinno wystarczyć.

No, ale to dopiero początek.

Przede wszystkim po wprowadzeniu i zatwierdzeniu ustawienia semafora wyświetlacz po prostu gaśnie, a chciałoby się dostać jakąś informację że coś zostało zapisane. Oprócz tego warto móc wyświetlić sobie bieżący stan wszystkich semaforów.

Zacznijmy od wprowadzenia dodatkowych zmiennych. Niech displayMode oznacza, co się w danej chwili wyświetla. Oprócz tego - jako że wyświetlacze OLED mają tendencję do wypalania - trzeba automatycznie po jakimś czasie wygasić wyświetlacz, a więc musimy zapamiętać kiedy zostało wprowadzone polecenie wyświetlania. W pliku display.cpp dodajemy gdzieś na początku:

enum {
    DPM_BLANK=0, // wyświetlacz wygaszony
    DPM_KBD, // stan klawiatury
    DPM_SET, // ustawienie zostało zaakceptowane
    DPM_SHOW // wyświetlanie wszystkich semaforów
};

static uint8_t displayMode = DPM_BLANK;
static uint32_t displayTimer=0;

Potrzebna teraz będzie funkcja, która będzie pilnować wygaszania wyświetlacza. W tym samym pliku dodajemy:
 

void updateDisplay()
{
    uint32_t delta;
    if (displayStatus != 1) return;
    switch(displayMode) {
        case DPM_SET:
        delta = 2000UL; // dwie sekundy "Zapisano"
        break;

        case DPM_SHOW:
        delta = 10000UL; // dziesięć sekund stanu
        break;

        default:
        return;
    }
    if (millis() - displayTimer >= delta) {
        displayMode = DPM_BLANK;
        display.clearDisplay();
        display.display();
        return;
    }    
}

Oczywiście funkcję należy zadeklarować w pliku Makieta.h:

extern void updateDisplay();

i wywołanie funkcji dopisać na końcu funkcji loop() w *.ino.

Czyli mamy już bazę do dalszej pracy.

Musimy teraz nieco zmienić funkcję realizującą samo wyświetlanie. Dodając dodatkowy parametr informujemy funkcję, że ma wyświetlić potwierdzenie. Czyli:

void displaySemaState(uint8_t sema, uint8_t prog, bool done)
{
    if (displayStatus != 1) return;
    display.clearDisplay();
    if (sema) {
        displayMode = DPM_KBD;
        display.setTextSize(2);
        display.setTextColor(SSD1306_WHITE);
        display.setCursor(10, done ? 16 : 8);
        display.print(semafor[semaforMap[sema]-1].name());
        if (prog) {
            display.write(' ');
            display.print(semafor[semaforMap[sema]-1].pgname(prog));
            if (done) {
                display.setCursor(16,0);
                display.print("Zapisano");
                displayMode = DPM_SET;
                displayTimer = millis();
            }
        }
    }
    else {
        displayMode = DPM_BLANK;
    }
    display.display();
}

Pamiętajmy o zmianie deklaracji funkcji w Makieta.h:

extern void displaySemaState(uint8_t sema=0, uint8_t prog=0, bool done=false);

Jak widać, funkcja z parametrem "done" równym true zapisuje stan wyświetlacza w displayMode, zapamiętuje moment wydania polecenia i wyświetla odpowiednią informację. Funkcja updateDisplay() będzie pilnować, aby wygasić wyświetlacz po dwóch sekundach.

Musimy jeszcze nieco zmodyfikować funkcje obsługi klawiatury - muszą teraz podawać więcej informacji do funkcji wyświetlania. W pliku getcommand.cpp musimy wprowadzić nieco zmian. Przede wszystkim funkcja displayKbdStatus musi wyłapać zatwierdzenie ustawienia:

static void displayKbdStatus(bool done=false)
{
    if (done) {
        displaySemaState(kbdSema, kbdProg, true);
        Serial.print("ZAPISANO ");
        Serial.print(semafor[semaforMap[kbdSema]-1].name());
        Serial.print(" ");
        Serial.println(semafor[semaforMap[kbdSema]-1].pgname(kbdProg));
    }
    else if (!kbdStatus) {
        displaySemaState();
        Serial.println("--");
    }
    else if (kbdStatus == KBD_SEMA) {
        Serial.print("SEMA ");
        Serial.println(semafor[semaforMap[kbdSema]-1].name());
        displaySemaState(kbdSema);
    }
    else {
        displaySemaState(kbdSema, kbdProg);
        Serial.print("SEMA ");
        Serial.print(semafor[semaforMap[kbdSema]-1].name());
        Serial.print(" PROG ");
        Serial.println(semafor[semaforMap[kbdSema]-1].pgname(kbdProg));
    }
}

Natomiast funkcja getCommand() musi poprzednią funkcję o tym poinformować. W tym celu zmieniamy:

   

 if (g == KEY_ASTERISK) {
        kbdStatus = KBD_RELAX;
        displayKbdStatus();
        return (semaforMap[kbdSema] << 8) | kbdProg;
    }

na:

    if (g == KEY_ASTERISK) {
        displayKbdStatus(true);
        kbdStatus = KBD_RELAX;
        return (semaforMap[kbdSema] << 8) | kbdProg;
    }

Po dokonaniu tych zmian powinno się nam ładnie wyświetlać potwierdzenie ustawienia.

Ale to jeszcze nie wszystko.

Chcemy, aby istniała możliwość wyświetlenia stanu wszystkich semaforów np. po naciśnięciu gwiazdki. Niestety - funkcja getCommand zwracała nam dotychczas tylko polecenie zmiany ustawienia konkretnego semafora. Na szczęście nie wszystkie bity są tu znaczące, umówmy się więc, że ustawiony najstarszy bit będzie oznaczał wykonanie innej funkcji.

Przede wszystkim musimy zmodyfikować funkcję getCommand. W pliku Makieta.h wprowadzamy dodatkowe definicje:

#define CMD_IS_FUNCTION(a) ((a) & 0x8000)
#define CMD_DISPLAYALL 0x8000

a w getCommand musimy zamienić:

    if (!kbdStatus) {
        if (g == KEY_HASH || g == KEY_ASTERISK) return 0;

na:

    if (!kbdStatus) {
        if (g == KEY_HASH) return 0;
        if (g == KEY_ASTERISK) return CMD_DISPLAYALL;

Natomiast w głównej pętli loop() zamieniamy:

    if (cmd) { // mamy coś do zrobienia
        semafor[SEM_NUM(cmd)].set(SEM_CMD(cmd));
    }

na coś bardziej skomplikowanego:

    if (cmd) { // mamy coś do zrobienia
        if (CMD_IS_FUNCTION(cmd)) {
            displayAllSema();
        }
        else {
            semafor[SEM_NUM(cmd)].set(SEM_CMD(cmd));
        }
    }

Pozostaje nam tylko dopisanie funkcji displayAllSema w pliku display.cpp:

void displayAllSema()
{
    int i;
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
        
    for (i=0;i<CNT_SEMA;i++) {
        display.setCursor(64 * (i&1), (i/2) * 8);
        display.print(semafor[i].name());
        display.print(' ');
        display.print(semafor[i].pgname(semafor[i].current()));
    }
    display.display();
    displayMode = DPM_SHOW;
    displayTimer = millis();
}

oraz zadeklarowanie jej w Makieta.h:

extern void displayAllSema();

Po skompilowaniu i załadowaniu powinniśmy uzyskać zadowalający efekt.

Tym razem nie będę pokazywać pełnych treści poszczególnych plików, załączę jedynie plik przygotowany do wgrania na Arduino: prezesedi5.zip

To tyle na dzisiaj - dość duża przeróbka, więc na razie wystarczy.

Oczywiście - program jest przystosowany do maksymalnie czterech semaforów i jednego ekspandera. W dodatku funkcja realizująca wyświetlanie stanu wszystkich semaforów nie reaguje na zmianę stanu w trakcie wyświetlania. Trzeba będzie temu zaradzić.

I jeszcze jedno: następna wersja będzie już ta ostateczną, do której będziemy mogli wykorzystać Arduino Uno. Następne będą wymagać ESP32, ale za to będą miały więcej możliwości. Tak że jak zwykle - stay tuned!

 

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

Po 22giej udzielę informacji nt. działania wersji "prezesedi5". Na tę chwilę, będąc w pracy, mogę jedynie poczytać na telefonie.

Niestety uroki bezpieczeństwa danych 😒

  • Lubię! 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

3 godziny temu, prezesedi napisał:

Niestety uroki bezpieczeństwa danych

Skąd ja to znam...

Kiedyś dawno temu, jak GaduGadu działał na jakimś swoim dziwnym porcie, kierownik w pewnej firmie dał zadanie adminowi, żeby wyciął GaduGadu bo pracownicy mają nie gadać a pracować.

Zgłosił się do mnie na GG znajomy (był nocnym stróżem w tej firmie), że nic poza GG mu nie działa. Bliższe zapoznanie się z tematem pozwoliło stwierdzić, że genialny admin zamiast na routerze zablokować porty GG zablokował całą resztę oprócz tych portów 🙂

Na szybko postawiliśmy jakiegoś VPN-a na tym porcie, ale nigdy się nie dowiedziałem co ów admin usłyszał jak przyszedł do roboty 🙂

3 godziny temu, prezesedi napisał:

Po 22giej udzielę informacji

A ja się właśnie zastanawiam nad apką na smartfona do sterowania semaforami - walor raczej edukacyjny ale pozwoli pokazać co potrafi ESP32 🙂

 

Link do komentarza
Share on other sites

4 minuty temu, ethanak napisał:

A ja się właśnie zastanawiam nad apką na smartfona do sterowania semaforami - walor raczej edukacyjny ale pozwoli pokazać co potrafi ESP32 🙂

Hmmmm.

W sumie modele ze względu na sposób zasilania dzieli się na dwa rodzaje:

zasilane AC

zasilane DC (mnie te interesują).

i właśnie przy zasilaniu DC (DCC) prym w sterowaniu wiodą dwie firmy (Piko i Roco). Osobiście posiadam sterowanie Piko, ale wiem, że ci posiadający sterowanie Roco wraz z odpowiednim routerem (model Z21) mogą za pomocą smartfona/tabletu sterować wszystkim. Niestety nie miałem nigdy z tym styczności, więc to tylko taka ciekawostka.

Przepraszam, za offtopic. To tak w nawiązaniu do aplikacji na smartfon.

Link do komentarza
Share on other sites

4 minuty temu, prezesedi napisał:

To tak w nawiązaniu do aplikacji na smartfon.

To ja powiem tak: w sumie jeśli możesz czymś sterować za pomocą ESP32 (a najprawdopodobniej możesz) to prawdopodobnie możesz tym sterować za pomocą smartfona, tabletu czy czegokolwiek. Kwestia potrzeby i chęci...

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

33 minuty temu, ethanak napisał:

A ja się właśnie zastanawiam nad apką na smartfona

Przepraszam że się wtrącę, można by powiedzieć że to aż prosi się na (z)użycie jakiegoś starego tableta z w miarę dużym ekranem. Wtedy to byłby pulpit na miarę 21 wieku. 

Link do komentarza
Share on other sites

Wiedziałem że o to zapytasz, i wiem też że może to u mnie trwać zdecydowanie ponad czas waszej cierpliwości 😄 no i nie jestem zbyt dobry w UX no ale siłą rzeczy zapytam: to miałby być web app czy połączenie przez BT || WiFi UDP?

Edytowano przez _LM_
Link do komentarza
Share on other sites

3 minuty temu, ethanak napisał:

Co prawda większość tego typu rozwiązań to BT, ale ja bym postawił AP na ESP32 i leciał po tcp/udp.

IMHO osobiście bym napisał REST API na ESP (AP) i sterował tym po HTTP 😄 Ale każdy ma inne preferencje.

Link do komentarza
Share on other sites

Całość komunikacji zrobić przez UDP i po sprawie, tyle że dochodzi parser po stronie ESP ale to chyba nie problem?

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.