Skocz do zawartości

Arduino w modelarstwie kolejowym


Pomocna odpowiedź

No cóż - kolega @prezesedi udał się na urlop, tak więc chwilowo nie mam dyżurnego testera. Na szczęście następna modyfikacja jest na tyle prosta, że mogłem ją sobie przetestować sam i założyć że "jak u mnie działa to i wszędzie powinno". Co najwyżej zmieni się wartości jakichś stałych ale to już w gotowym rozwiązaniu.

Przede wszystkim uwaga: w poprzedniej wersji wkradł się błąd. W funkcji initWiFi zamiast skorzystać z definicji MY_ID omyłkowo została wpisana wartość 0x20. Należy zamienić linię:

    macadr[5]=0x20;

na

    macadr[5]=MY_ID;

Bez tego nie będzie możliwe uruchomienie kilku urządzeń!

Ale wróćmy do powtarzania pakietów.

Można założyć, że:

  • powtórzenie powinno nastąpić po kilkudziesięciu milisekundach. Najlepiej, aby ten czas był w jakimś stopniu zrandomizowany, co powinno przynajmniej trochę zrównoważyć ruch.
  • ilość powtórzeń powinna być ograniczona do kilku - nie ma sensu powtarzać pakietu jeśli odbiornik zbyt długo nie reaguje (np. jest wyłączony)
  • próba wysłania kolejnego (innego) pakietu jednocześnie unieważnia poprzedni.

I następne założenie: nie będziemy zmieniać API funkcji WiFi, wszystkie zmiany mają być dokonane tylko w jednym miejscu (w pliku wifi.cpp).

Zacznijmy więc od wprowadzenia dodatkowych zmiennych:

static uint8_t dataBuffer[250]; // maksymalna długość pakietu to 250
static uint8_t dataLength;      // jak sama nazwa wskazuje
static uint8_t retryLeft;       // ile prób pozostało
static uint32_t lastSentTime;   // kiedy była ostatnia próba
static uint8_t retry_time;      // ile czasu trzeba odczekać

#define RETRY_COUNT 10

Jak widać, założyłem tu maksymalnie 10 powtórzeń pakietu. Kolej na funkcję, realizującą pojedynczą powtórkę:

static void retrySend()
{
    if (!retryLeft) return;
    if (millis() - lastSentTime < (uint32_t)retry_time) return;
    printf("Pozostało %d prób\r\n", retryLeft);
    retryLeft--;
    lastSentTime = millis();
    retry_time = random(40,60);
    esp_now_send(paneladr, dataBuffer, dataLength);
}

Musimy również lekko zmodyfikować funkcję onDataSent tak, aby po odebraniu potwierdzenia zaprzestać prób powtarzania. Modyfikacja w sumie polega na dodaniu jednej linijki:

static void onDataSent(uint8_t *mac, uint8_t status)
{
    if (!status) retryLeft=0; // panel przyjął dane, nie trzeba powtarzać
    printf("Master %s\r\n", status ? "nie odebrał pakietu" : "odebrał pakiet");
}

Natomiast funkcja sendToPanel musi być całkowicie przepisana. Poprzednio po prostu wysyłała ona pakiet - teraz musi zapamiętać jakie dane są wysyłane, aby umożliwić ich wysyłanie:

void sendToPanel(const uint8_t *data, uint8_t len)
{
    printf("Wysyłam pakiet\r\n");
    memcpy(dataBuffer, data, len);
    dataLength=len;
    retryLeft = RETRY_COUNT;
    lastSentTime = millis();
    retry_time = random(40,60);
    esp_now_send(paneladr, dataBuffer, dataLength);
}

W funkcji wifiLoop musimy dodać obsługę powtórzeń:

void wifiLoop()
{
    // jeśli dane są nieważne lub upłynęło za dużo czasu
    // od ostatniego ustawienia poproś o dane
    if (dataValid) {
        if (millis() - lastReceived >=10000) {
            dataValid=false;
            askData();
        }
    }
    else {
        if (millis() - lastAsk >=1000UL) askData();
    }
    retrySend();
}

Ostatnią rzeczą będzie inicjalizacja generatora pseudolosowego, Nie musimy tu używać żadnych specjalnych zabiegów: ponieważ wartość MY_ID (czyli ostatni oktet adresu MAC) jest inna dla każdego urządzenia, możemy jej użyć do zainicjalizowania generatora. To powinno wystarczyć, aby sekwencje dla każdego urządzenia były różne. W tym celu dodajemy jedną linijkę na początku funkcji initWiFi:

void initWiFi()
{
    randomSeed(MY_ID);

I to wszystko. Teraz powinno to działać lepiej. Kod jak zwykle w załączniku: semafor2.zip

Teraz czekamy na powrót z urlopu naszego kolegi, a potem zajmiemy się jedną z większych przeróbek - czyli dodaniem obsługi esp-now do panela. Czyli po prostu: stay tuned!

 

  • Lubię! 1
  • 4 tygodnie później...

Witam po dłuższej nieobecności.

w czasie nieobecności zakupiłem nową płytkę klona Arduino sądząc, że posiadana ma defekt (nie dało się wgrać żadnego kodu na ESP8266). Zrobiłem także reinstalację Windows'a, gdyż też już miał swój czas i sypał dziwnymi komunikatami.

Powróciłem do testowania kodu pisanego przez kolegę @ethanak i na nowym systemie okazuje się, że jednak płytka 8266 działa i ma się dobrze.

Aktualnie wykorzystuję starą płytkę jako sterownik semafora (odbiornik) oraz nową jako nadajnik sygnału. Wszystko ma się prawie dobrze i działa prawie bez zarzutu.

To prawie, to mały problem z diodami objawiający się żarzeniem teoretycznie wygaszonych LEDów. Kombinowałem z rezystorami lecz brak poprawy (od 0,2 do 1kohm). @ethanak zasugerował bym zapytał kolegów z działu elektroniki o rozwiązanie problemu.

 

Tak więc jestem zwarty i gotowy na nowe wyzwania.

  • Lubię! 1
46 minut temu, prezesedi napisał:

bym zapytał kolegów z działu elektroniki o rozwiązanie problemu.

 

Więc czemu nie zapytasz? Wątpię, aby ktoś niespecjalnie zainteresowany (szczególnie elektronik) czytał ten wątek. A ja nie mam pojęcia o co chodzi (tzn. u mnie działa)...

3 minuty temu, prezesedi napisał:

Możemy działać dalej

Na razie uzbrój się w cierpliwość - mam tę nieszczęsną bazę danych na głowie i przynajmniej jeden fragment razem z interfejsem muszę skończyć. Odezwę się.

No cóż - po dłuższej przerwie trzeba wrócić do makiety.

Dzisiaj trochę więcej kodu, więc nie będę omawiał każdej linijki - opiszę tylko najważniejsze rzeczy, reszta powinna być jasna po przeczytaniu kodu z załącznika.

Spróbujemy dodać sterowanie semaforami poprzez protokół esp-now. Na razie w najprostszy sposób (tzn. bez powtórek, o tym będzie później).

Ponieważ będziemy sterować nie tylko semaforami, ale również rozkładem jazdy (a jeśli komuś się zechce to i innymi urządzeniami) - warto zrobić coś w miarę uniwersalnego.

Załóżmy, że istnieje jakiś obiekt klasy Transceiver, który pośredniczy w wymianie danych między panelem a odbiornikiem (w naszym przypadku semaforem). Ponieważ do jeden odbiornik może obsłuzyć kilka urządzeń (np. do odbiornik typu "semafor" mogą być podłączone trzy tarcze, albo semafor i tarcza) w jakiś sposób trzeba ustalić które to urządzenie na którym odbiorniku. Stwórzmy sobie więc strukturę opisującą takie połączenie:

struct ETrans {
    void *parent; //wskaźnik do obiektu nadrzędnego (np. semafora)
    uint8_t rxId; // indeks transceivera w tablicy
    uint8_t rxPos; // indeks urządzenia w transceiverze
    uint8_t rxDataPos; // pozycja danych w buforze
    uint8_t rxDataLen; // długość danych w buforze
    uint8_t dataChanged; // dane zmienione, trzeba nadać

    // funkcja pobierająca ustawienia do transmisji
    void (*getData)(struct ETrans *et, uint8_t *buffer);
    
};

Taka struktura musi być częścią klasy opisującej urządzenia (w naszym przypadku Semafor), tak więc deklaracja klasy będzie wyglądała następująco:

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 danej 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;};

        // pobierz ustawienia LED

    uint8_t getcmd() {return _program[_current].prog;};
    

    // podłączenie do transceivera

    void attach();
    
    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;
    struct ETrans tx;
};

Jak widać, doszły tam dodatkowe funkcje, ale o nich potem.

Jak więc będzie wyglądać teraz inicjalizacja obiektu?

Przede wszystkim założyłem, że w zmiennej _whereto ustawienie najstarszego bitu będzie oznaczać, że jest to semafor zewnętrzny (radiowy). Tak więc nie można w takim przypadku skorzystać z makra WHERETO, trzeba stworzyć inne dla zewnętrznych semaforów:

#define EXTRSEM(exid, expos) (0x80 | ((exid) << 2) | expos)

Jak widać, pozycja urządzenia w odbiorniku to dwa najmłodsze bity, trzy następne to indeks transceivera. Tablica inicjalizacji obiektów będzie teraz wyglądać tak:

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(semaTable,WHERETO(2,0),"Sem3"), // semafor trzeci piny P0..P4
    Semafor(semaTable,EXTRSEM(0,0),"Sem4"), // semafor czwarty MAC 0x20 indeks 0
    Semafor(tarczaTable,WHERETO(0,5),"TM1"), // tarcza pierwsza piny P5..P6
    Semafor(tarczaTable,WHERETO(1,5),"TM2"), // tarcza druga piny P15..P16
    Semafor(tarczaTable,WHERETO(2,5),"TM3"), // tarcza trzecia piny P5..P6
    Semafor(tarczaTable,EXTRSEM(0,1),"TM4"), // tarcza czwarta MAC 0x20 indeks 1
};

Sem4 i TM4 są teraz podłączone do zewnętrznego odbiornika,

Sama inicjalizacja obiektu Semafor nieco się rozbudowała:

static void getSemaData(struct ETrans *t, uint8_t *databuf)
{
    Semafor *p=(Semafor *)(t->parent);
    databuf[t->rxDataPos] = p->getcmd();
}


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;

    // ustawienia dla zdalnego semafora

    if (_whereto & 0x80) {
        tx.parent = (void *)this;
        tx.rxId = (whereto>>2) & 7;
        tx.rxPos = whereto & 3;
        tx.rxDataPos = tx.rxPos;
        tx.rxDataLen=1;
        tx.getData = getSemaData;
    }
}

Dodatkowy kod pozwala na to, aby obiekt Semafor wiedział, do którego transceivera i na jakiej pozycji jest podłączony.


Zanim jednak pokażę dodatkowe funkcje klasy Semafor, wypadałoby powiedzieć parę słów o klasie Transceiver. Wygląda ona tak:

class Transceiver {
    public:
    Transceiver(uint8_t macid);
    void begin();
    void trySend();
    struct ETrans *slaves[4];
    uint8_t macid() {return _macadr[5];};
    void onReceive(const uint8_t *data, int len);
    private:
    uint8_t _sendAll;
    uint8_t _macadr[6];
    void send(uint8_t *data, uint8_t len);

};

//podajemy ostatni oktet adresu MAC

Transceiver::Transceiver(uint8_t macid)
{
    memcpy(_macadr,MACARRAY,5);
    _macadr[5]=macid;
    _sendAll=0;
    int i;
    for (i=0;i<4;i++) slaves[i]=NULL;
}

Trzeba oczywiście stworzyć tablicę zawierającą wszystkie obiekty Transceiver. Na razie mamy tylko jeden:

static Transceiver txs[]={
    Transceiver(0x20) 
};

static const int CNT_TRANS = sizeof(txs) / sizeof(txs[0]);


Najważniejsza jest w tej chwili tablica slaves, w której przechowywane są wskaźniki do odpowiednich struktur ETrans. Do połączenia tych struktur z rzeczywistym obiektem użyjemy prostej funkcji:

void attachEnowTx(struct ETrans *t)
{
    if (t->rxId >= CNT_TRANS) return;
    if (t->rxPos > 3) return;
    txs[t->rxId].slaves[t->rxPos] = t;
}

I odpowiednia metoda w klasie Semafor:

void Semafor::attach()
{
    if (_whereto & 0x80) {
        attachEnowTx(&tx);
    }
}

Teraz po wywołaniu tej attach() odpowiedni Transceiver wie już, który slave odpowiada któremu semaforowi.

Nasuwa się pytanie: dlaczego tablica slaves nie zawiera po prostu indeksów w tablicy semaforów? Odpowiedź jest prosta: Transceiver nie wie nic o istnieniu jakichś semaforów - a przecież w przyszłości ta sama tabela będzie zawierać adresy innych obiektów (np. rozkładu jazdy).

Z interesujących metod klasy Transceiver oprócz begin() (która po prostu jest wrapperem do esp_now_add_peer) mamy tu bardzo prostą metodę send:

void Transceiver::send(uint8_t *data, uint8_t len)
{
    esp_now_send(_macadr, (const uint8_t *)data, len);
}

Prosta na razie - bo potem trzeba będzie zaimplementować tam powtórzenia.

Drugą metodą jest trySend, która sprawdza czy któryś z obiektów w tablicy slaves nie ma nowych danych. Jeśli tak - pobiera informacje od wszystkich obiektów z tablicy i wywołuje send z poleceniem ustawienia wszystkich semaforów:

void Transceiver::trySend()
{
    int i,mx,n;uint8_t d;
    
    for (i=0, d=_sendAll; i<4; i++) if (slaves[i]) {
        d |= slaves[i]->dataChanged;
        slaves[i]->dataChanged=0;
    }
    _sendAll=0;
    if (!d) return;
    uint8_t buf[8];
    for (i=mx=0; i<4; i++) if (slaves[i]) {
        n=slaves[i]->rxDataPos + slaves[i]->rxDataLen;
        if (n > mx) mx=n;
        slaves[i]->getData(slaves[i], buf+1);
    }
    buf[0] = ENOW_SEMA_SETALL;
    this->send(buf, mx+1);
}

Warto jeszcze wspomnieć o tym, że semafor co jakiś czas wysyła prośbę o aktualizację danych. Taką prośbę obsługuje metoda onReceive:

void Transceiver::onReceive(const uint8_t *data, int len)
{
    if (data[0] == ENOW_GIMMA_DATA) {
        printf("Wysyłam nowe dane dla %02x\r\n",macadr[5]);
        _sendAll = 1;
        return;
    }
}

Samo zainicjalizowanie esp-now jest dość proste. W funkcji initWiFi mamy tu trochę więcej kodu - musimy między innymi ustawić odpowiedni adres MAC i wywołać funkcję inicjalizacji esp-now:

void onDataSent(const uint8_t *mac_addr, esp_now_send_status_t status)
{
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Dane odebrane przez slave" : "Slave nie odebrał danych");
}

void onDataReceive(const uint8_t *mac, const uint8_t *data, int len)
{
    int i;
    for (i=0;i<CNT_TRANS;i++) if (txs[i].macid() == mac[5]) {
        txs[i].onReceive(data, len);
        break;
    }
}


static void startenow()
{
    esp_now_init();
    esp_now_set_pmk(MYCIPHER);
    esp_now_register_send_cb(onDataSent);
    esp_now_register_recv_cb(onDataReceive);
    int i;
    for (i=0; i< CNT_TRANS; i++) txs[i].begin();
}

void initWiFi()
{
    WiFi.mode(WIFI_AP);
    memcpy(macadr,MACARRAY,5);
    macadr[5]=0x01;
    esp_wifi_set_mac(WIFI_IF_AP, &macadr[0]);
    WiFi.softAP("Kolejka","PrezesEdi7",ENOW_CHANNEL,0,4);
    startenow();
    startWebServer();
}

Jak widać, funkcja onDataSent służy na razie tylko do wyświetlania odpowiedniej informacji, a onDataReceive po prostu przekazuje cały bufor do odpowiedniego obiektu Transceiver.

Ponieważ metoda trySend musi być wysoływana okresowo, ostatnia funkcja (wywoływana z głównej pętli loop) dba o jej wywołanie dla każdego obiektu Transceiver:

void WiFiLoop()
{
    int i;
    for (i=0;i<CNT_TRANS;i++) txs[i].trySend();
}

I to w telegraficznym skrócie wszystko na dziś. Na razie można spróbować załadować do naszego ESP kod z załącznika i sprawdzić działanie: kolejpan1.zip

Oczywiście na pewno okaże się, że nie za każdym razem polecenie ustawienia jakiegoś sygnału dotrze do semafora. Ale naprawieniem tego mankamentu spróbuję zająć się następnym razem. Tak że - stay tuned!
 

  • Lubię! 2

Dzień dobry, kod przetestowany - działa. Co prawda odległość pomiędzy nadajnikiem a odbiornikiem wynosi c.a. 2cm, ale działa.

Czekam na nową płytkę stykową, bo ostatnia jaką zamówiłem miała defekt i odesłałem.

 

Tak więc, potwierdzam - sterowanie z klawiatury semaforem drogą radiową, oraz wyświetlanie zadanego sygnały na wyświetlaczu oled.

16 godzin temu, prezesedi napisał:

Tak więc, potwierdzam - sterowanie z klawiatury semaforem drogą radiową, oraz wyświetlanie zadanego sygnały na wyświetlaczu oled.

A więc idziemy dalej.

Na wstępie może przypomnę jakie zasady przyjąłem:

  • dla każdego Transceivera ważny jest tylko jeden ostatni komunikat;
  • maksymalna ilość powtórzeń to 10;
  • odstęp czasowy pomiędzy powtórkami jest wybierany losowo w okolicy 50 msec;
  • każdy obiekt Transceiver działa niezależnie.

A więc zaczynamy!

Na pierwszy ogień idzie klasa Transceiver, która w tej chwili wygląda tak:

class Transceiver {
    public:
    Transceiver(uint8_t macid);
    void begin();
    void trySend();
    struct ETrans *slaves[4];
    uint8_t macid() {return _macadr[5];};
    void onReceive(const uint8_t *data, int len);
    void onSent(int status);

    private:

    uint8_t datalen; // rozmiar danych w buforze
    uint8_t retries; // ile prób pozostało
    uint8_t retrytime; // odstęp czasowy w msec
    uint8_t _sendAll;  // czy są nowe dane?
    uint32_t sendTimer; // ostatnio wysłane
    uint8_t buf[16]; // bufor danych
    uint8_t _macadr[6];

    void send(uint8_t *data, uint8_t len);

};

Tak jak wspominałem poprzednio, metoda send() trochę się skomplikowała. Dane przeznaczone do wysłania są dodatkowo kopiowane do bufora, oraz ustawiane są zmienne odpowiadające za powtórzenia. Co prawda długość danych nie powinna przekraczać kilku bajtów, ale na wszelki wypadek aby nie dochodziło do przepełnienia bufora cały mechanizm powtórek nie będzie uruchamiany gdyby w przyszłości jakaś funkcja próbowała wysłać więcej niż 16 bajtów:

void Transceiver::send(uint8_t *data, uint8_t len)
{
    if (len > 16) { // za długi bufor, nie będzie powtórek
        retries=0;
        esp_now_send(_macadr, (const uint8_t *)data, len);
        return;
    }
    retries=10; // 10 powtórek
    retrytime = random(40,60); // czas do następnej powtórki
    memcpy(buf, data, len);
    datalen=len;
    sendTimer = millis();
    esp_now_send(_macadr, (const uint8_t *)data, len);
}

Dodatkowo, po wysłaniu danych będzie wywoływana metoda onSent. Jak widać jest bardzo prosta: po prostu po otrzymaniu potwierdzenia ustawia zmienną "retries" na zero (co oznacza, że żadnych powtórek nie będzie), a w przypadku błędu na wszelki wypadek ustawia timer:

void Transceiver::onSent(int status)
{
    if (!status) { // potwierdzone
        retries=0;
        return;
    }
    else {  //error
        if (!retries) return; // wystarczy tego dobrego
        sendTimer = millis();
    }
}

Następna zmiana dotyczy metody trySend():

void Transceiver::trySend()
{
    int i,mx,n;uint8_t d;
    
    for (i=0, d=_sendAll; i<4; i++) if (slaves[i]) {
        d |= slaves[i]->dataChanged;
        slaves[i]->dataChanged=0;
    }
    _sendAll=0;

    if (!d) { // nie mamy nic do wysłania, więc może powtórka?
        if (!retries) return; // jednak nic
        if (millis() - sendTimer < retrytime) return; // jeszcze nie czas
        printf("Retry %d\r\n", retries);
        if (esp_now_send(_macadr, buf, datalen)) {
            printf("Błąd wysyłki - rezygnuję\r\n");
            retries=0;
        }
        retries--;
        sendTimer=millis();
        retrytime = random(40,60);
        return;
    }
    uint8_t buf[8];
    for (i=mx=0; i<4; i++) if (slaves[i]) {
        n=slaves[i]->rxDataPos + slaves[i]->rxDataLen;
        if (n > mx) mx=n;
        slaves[i]->getData(slaves[i], buf+1);
    }
    buf[0] = ENOW_SEMA_SETALL;
    this->send(buf, mx+1);
}

Jeśli dany Transceiver nie ma nowych danych do wysłania, może ma jakieś zaległe powtórki? Jeśli tak, i przyszedł już czas na wysłanie powtórki jest ona wysyłana, pczy czym zmniejszana jest wartość retries i wybierany losowy czas do następnej powtórki.

Pozostaje tylko wywołanie metody onSent dla właściwego Transceivera. Lekka modyfikacja funkcji onDataSent:

void onDataSent(const uint8_t *mac_addr, esp_now_send_status_t status)
{
    Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Dane odebrane przez slave" : "Slave nie odebrał danych");
    int i;
    for (i=0;i<CNT_TRANS;i++) if (txs[i].macid() == mac_addr[5]) {
        txs[i].onSent(status);
        break;
    }
}

załatwia nam tę sprawę.

I to praktycznie wszystko - teraz semafor nie powinien gubić przesłanych pakietów.

Jak zwykle kod w załączniku:kolejpan2.zip

W dalszej kolejności zajmę się rozkładem jazdy, a więc jak zwykle: stay tuned!


 

  • Lubię! 1
2 minuty temu, prezesedi napisał:

Wszystkie sygnały wywoływane z klawiatury działają.

Spróbuj na większej odległości - mi przez trzy ściany na ok. 5m gubi jakąś połowę pakietów, ale to w niczym nie przeszkadza.

31 minut temu, prezesedi napisał:

działanie bez problemu.

Zerknij na serial monitor w panelu - zobaczysz że nie wszystkie polecenia przychodzą za pierwszym razem.

(edytowany)

tak wygląda to w monitorze:

Wysyłam nowe dane dla 01

Dane odebrane przez slave
SEL Semafor

Wysyłam nowe dane dla 01

Dane odebrane przez slave
SEMA Sem4

SEMA Sem4 PROG S4

ZAPISANO Sem4 S4

Dane odebrane przez slave
SEL Tarcza

SEMA TM4

SEMA TM4 PROG Sz

ZAPISANO TM4 Sz

Dane odebrane przez slave
Wysyłam nowe dane dla 01

Dane odebrane przez slave
SEL Tarcza

SEMA TM4

SEMA TM4 PROG Sz

ZAPISANO TM4 Sz

Dane odebrane przez slave
SEL Semafor

SEMA Sem4

SEMA Sem4 PROG Sz

ZAPISANO Sem4 Sz

Dane odebrane przez slave
SEL Semafor

SEMA Sem4

SEMA Sem4 PROG S5

ZAPISANO Sem4 S5

Dane odebrane przez slave
SEL Semafor

SEMA Sem4

SEMA Sem4 PROG S10

ZAPISANO Sem4 S10

Dane odebrane przez slave
SEL Semafor

SEMA Sem4

SEMA Sem4 PROG S11

ZAPISANO Sem4 S11

Dane odebrane przez slave
SEL Semafor

SEMA Sem4

SEMA Sem4 PROG S12

ZAPISANO Sem4 S12

Dane odebrane przez slave
SEL Semafor

SEMA Sem4

SEMA Sem4 PROG S1

ZAPISANO Sem4 S1

Dane odebrane przez slave
SEL Semafor

SEMA Sem4

SEMA Sem4 PROG S13

ZAPISANO Sem4 S13

Dane odebrane przez slave
SEL Semafor

SEMA Sem4

SEMA Sem4 PROG S2

ZAPISANO Sem4 S2

Dane odebrane przez slave
SEL Tarcza

SEMA TM4

SEMA TM4 PROG Ms2

ZAPISANO TM4 Ms2

Dane odebrane przez slave
SEL Semafor

SEMA Sem4

SEMA Sem4 PROG S12

ZAPISANO Sem4 S12

Dane odebrane przez slave
SEL Tarcza

SEMA TM4

SEMA TM4 PROG Ms1

ZAPISANO TM4 Ms1

Dane odebrane przez slave
SEL Semafor

SEMA Sem4

SEMA Sem4 PROG S1

ZAPISANO Sem4 S1

Dane odebrane przez slave
Wysyłam nowe dane dla 01

Dane odebrane przez slave
Wysyłam nowe dane dla 01

Dane odebrane przez slave

raz pojawił się wpis - Retry 10. Niestety zdążyłem wyczyścić monitor

edit:

Przesunąłem płytkę o ok. 0,5m dalej (na parapet) i mamy już inne wpisy:

Wysyłam nowe dane dla 01

Slave nie odebrał danych
Retry 10

Slave nie odebrał danych
Retry 9

Slave nie odebrał danych
Retry 8

Slave nie odebrał danych
Retry 7

Slave nie odebrał danych
Retry 6

Slave nie odebrał danych
Retry 5

Slave nie odebrał danych
Retry 4

Slave nie odebrał danych
Retry 3

Slave nie odebrał danych
Retry 2

Slave nie odebrał danych
Retry 1

Slave nie odebrał danych
Wysyłam nowe dane dla 01

Slave nie odebrał danych
Retry 10

Slave nie odebrał danych
Retry 9

Slave nie odebrał danych
Retry 8

Slave nie odebrał danych
Retry 7

Slave nie odebrał danych
Retry 6

Slave nie odebrał danych
Retry 5

Slave nie odebrał danych
Retry 4

Slave nie odebrał danych
Retry 3

Slave nie odebrał danych
Retry 2

Slave nie odebrał danych
Retry 1

Slave nie odebrał danych
Slave nie odebrał danych
Retry 10

Slave nie odebrał danych
Retry 9

Slave nie odebrał danych
Retry 8

Slave nie odebrał danych
Retry 7

Slave nie odebrał danych
Retry 6

Slave nie odebrał danych
Retry 5

Slave nie odebrał danych
Retry 4

Slave nie odebrał danych
Retry 3

Slave nie odebrał danych
Retry 2

Slave nie odebrał danych
Retry 1

Slave nie odebrał danych
Dane odebrane przez slave
Wysyłam nowe dane dla 01

Dane odebrane przez slave
Wysyłam nowe dane dla 01

Dane odebrane przez slave
Wysyłam nowe dane dla 01

Dane odebrane przez slave
Wysyłam nowe dane dla 01

Dane odebrane przez slave
Wysyłam nowe dane dla 01

Dane odebrane przez slave

 

Edytowano przez prezesedi

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...