OK

Při poskytování služeb nám pomáhají soubory cookie. Používáním našich služeb vyjadřujete souhlas s naším používáním souborů cookie. Více informací

Úvodní stránka » Arduino » Nextion display - III.díl - první vlastní projekt druhá část

Nextion display - III.díl - první vlastní projekt, druhá část

Pro kompilaci displeje ho fyzicky nepotřebuje. Přesto Vám doporučím – kupte si ho a podívejte se na to, jak to vypadá na displeji. Je rozdíl mezi zobrazením na monitoru a displeji. Oko oklamete velmi snadno.

Tento článek budeme věnovat přímé interakci Arduina s displejem. Vyžaduje to poněkud jiný přístup než jste zvyklí.

Originální knihovna

Tzv. Tovární knihovna je ke stažení zde. Najdete tam stovky souborů, naprosto nepřehlednou knihovnu bez popisu, polofunkční manuál a mnoho příkladů pro arduino. Než to začnete stahovat, podívejte se jak se vytváří interakce s tlačítkem:

#include "Nextion.h"

NexButton b0 = NexButton(0, 1, "b0");

char buffer[100] = {0};

NexTouch *nex_listen_list[] =
{
    &b0,
    NULL
};

void b0PopCallback(void *ptr)
{
    uint16_t len;
    uint16_t number;
    NexButton *btn = (NexButton *)ptr;
    dbSerialPrintln("b0PopCallback");
    dbSerialPrint("ptr=");
    dbSerialPrintln((uint32_t)ptr);
    memset(buffer, 0, sizeof(buffer));

    btn->getText(buffer, sizeof(buffer));
   
    number = atoi(buffer);
    number += 1;

    memset(buffer, 0, sizeof(buffer));
    itoa(number, buffer, 10);

    btn->setText(buffer);
}

void setup(void)
{   
    nexInit();

    b0.attachPop(b0PopCallback, &b0);
   
    dbSerialPrintln("setup done");
}

void loop(void)
{
    nexLoop(nex_listen_list);
}

Teď si představte že ten projekt na kterém jsem pracoval před dvěma lety, měl 23 stran, z toho 7 stran mělo 10 podstran, 82 tlačítek a 130 polí kam se odesílala data při konkrétním zobrazení. Pro každé tlačítko, pro každé pole vytváříte kompletní strukturu.

Upřímně, tuto pro mě šílenost bez podpory, která oficiálně funguje pouze na MEGA a UNO a ve skutečnosti je tak špatně napsaná, že nefunguje vůbec, jsem zavrhnul hned na začátku. A to jsem úplně pominul jak plýtvá místem (na MEGA si to zabere 14% paměti RAM), které pro mě bylo klíčové.

Prostě jsem napsal vlastní kód, který dělá vše co jsem doposud potřeboval, i když tu nejsou zdaleka všechny funkce a má jeden drobný nedostatek – nevrací hodnotu zda se komunikace vydařila. V praxi jsem ji ale k ničemu nepotřeboval, protože to funguje.

Jdeme na to

Tento článek si rozdělíme na čtyři části. Nejprve se podíváme jak pracovat s programem pro Arduino, pak krátký návod jak ladit displej a nakonec se podíváme na dvě varianty práce s displejem, kdy si pouze rozsvítíme integrovanou diodu. Ukážeme si tak různá úskalí komunikace s displejem.

Práce s programem

Tak knihovnu jsme z výše uvedeného důvodu zahodili, proto jsem pro Vás připravil program, který obsahuje veškeré potřebné funkce a některé navíc, které ani knihovna neumí. Jinak do vašeho sketche můžete nahrát všechny funkce, které jsou uvedeny v tomto zdrojovém souboru, ty co nezavoláte Vám žádné místo ve výsledné kompilaci nezaberou, tedy pokud používáte Arduino IDE od verze 1.4. nahoru.

Jinak všechny funkce fungují na všech Arduinech bez rozdílu. Lze snadno využít i softvérovém rozhraní, jenom připomínám, dejte pozor na velikost přijímacího buferu.

Pro správnou funkčnost je zapotřebí:

Do záhlaví definovat typ sériového rozhraní:

#define nextion Serial

Tím říkáme, že využijeme standardní sériové rozhraní. Pokud chcete Softvare Serial, prostě ho uveďte, ale musíte ho nakonfigurovat zvlášť a přidat knihovnu.

Dále do funkce setup vložíme funkci:

nextion_init(9600);

Tím aktivujeme sériové rozhraní o rychlosti 9600bd. Je to stejné, jako by jste napsali Serial.begin(9600);

Displej má z výroby nastavenou rychlost komunikace 9600bd. Pokud chcete nastavit jinou, je zapotřebí zaslat správný příkaz, seznam viz minulý článek a pak je nextion_init(to co jste nastavili);

Doporučuji pro začátek nechat výchozí rychlost, jinak se příště do displeje nemusíte dostat.

Dále do funkce loop vložíme funkci:

check_display();

Tato funkce nečiní nic jiného, než že nám kontroluje příchozí data a pokud nějaká jsou spustí další kroky. Tuto funkci můžete samozřejmě vložit, kam si vzpomenete, i do jiné části programu. Prostě vždy když chcete kontrolovat data.

A nakonec mimo hlavní smyčku vložíme dvě samostatné funkce:

void touch_Return(String page_ID, String component_ID, String touch_event) {…}

void page_Return(String page_ID) {…}

První „touch_Return“ se spouští sama po stisku jakéhokoliv tlačítka, prostě čehokoli, čemu jsme v displeji řekli, ať nám vrátí data. V rámci této funkce se pak kontrolujete, co bylo spuštěno a provádíte potřebné akce.

Druhá „page_Return“ se spouští v případě, že jsme stránce v displeji dali příkaz „sendme“ Tím nám displej vrátí číslo zobrazené stránky. Opět zkontrolujeme o kterou stránku se jedná a můžeme ji tak naplnit příslušnými daty.

Celý program je ke stažení zde.

Jednotlivé funkce jsou uloženy pro přehlednost v záložkách a najdete zde i podrobný popis jednotlivých funkcí.

Jednu nevýhodu oproti originální knihovně moje řešení má. Vůbec se tady nepočítá s tím, že Vám bude displej vracet jiné hodnoty (např. chybová hlášení) než jaká si vyžádáte. Otázka je, co by jste s nimi dělali. Až budete dělat profi program, možná Vám to chybět bude, ale pokud vše otestujete jak se má, nebudete to potřebovat. Navíc chybová hlášení vidíte přímo při testování v Nextion editoru, tam se zastavit nedají.

Ladění displeje

K samotnému ladění fyzicky displej nepotřebujeme. Nicméně doporučuji ho mít a vidět to naživo, minimálně před finální kompilací. Zapojení je jednoduché, ale je zapotřebí dodržovat přesný postup, jinak se nikam nedostanete. Připojte své Arduino k PC, spusťte Arduino IDE a nahrajte program. Arduino nechte zapojené a zavřete Arduino IDE, nebo zruště výběr správného komunikačního portu. Pak můžete spustit ladící program v Nextion Editoru, vyberte mód komunikace s Arduinem, vyberte USB port a vše jede jak má. Když zapomenete vypnout Arduino IDE, Nextion editor se Vám k Arduinu nepřihlásí a někdy budete muset restartovat počítač, aby Vám to opět začalo fungovat. Když současně chcete, vidět zobrazení i na fyzickém displeji, připojte ho k libovolnému USB portu a postupujte stejně jako u Arduina.

Praktická ukázka – tiskneme tlačítko poprvé

Použijeme upravený HMI soubor zminulého článku, ke stažení zde. Jsou zde drobné změny, použijeme nepřepínací tlačítko a proměnnou. Displej bude podle stisknutí řídit i změnu zobrazení zda je rozsvíceno či nikoli, podle nastavení proměnné, kterou jsme si tam přidali a to v rámci vlastního kódu. Zpět do displeje nám Arduino nebude nic posílat.

#define nextion Serial // port pro komunikaci s displejem Nextion
#define LED 13 // nastaveni nazvu pin
//------------------------------------------------------------------
void setup() {
  pinMode (LED, OUTPUT); // nastaveni vystupu
  nextion_init(9600); // inicializace displeje a nastaveni rychlosti (mozno ponechat prazdne)
}
//------------------------------------------------------------------
void loop() {
  check_display(); // kontrola prijatych dat
}
//------------------------------------------------------------------
void touch_Return(String page_ID, String component_ID, String touch_event) {
  if (page_ID == "0") {
    if (component_ID == "1") {
      digitalWrite(LED, !digitalRead(LED)); // prevraceni hodnoty vystupu
    }
  }
}
//------------------------------------------------------------------
void page_Return(String page_ID) {}
//------------------------------------------------------------------
String Nextion_receive(boolean read_data) { //returns generic

  boolean answer = false; // znacka
  char bite; // promenna pro ulozeni znaku
  String cmd; // promenna pro ulozeni textu
  byte countEnd = 0; // pocitadlo
  unsigned long previous; // cas spusteni
  int timeout = 1000; // doba po kterou se ceka na prichozi data
  previous = millis();

  do { // cekani na spravnou odpoved
    if (nextion.available() > 0) { // kdyz jsou k dispozici data, precti data
      bite = nextion.read();
      cmd += bite;
      if ((byte)bite == 0xff) countEnd++;
      if (countEnd == 3) answer = true;
    }
  }
  while (!answer && !((unsigned long)(millis() - previous) >= timeout)); // ceka na spravnou hodnotu, nebo uplynuti casu

  if (read_data) { // read general data
    if (cmd[0] == 0x65) { // Touch event return data
      // 0X65 + Page ID + Component ID + TouchEvent + End
      touch_Return(String(cmd[1], DEC), String(cmd[2], DEC), String(cmd[3], DEC));
    }
    else if (cmd[0] == 0x66) { // Current page ID number returns
      // 0X66 + Page ID + End
      page_Return(String(cmd[1], DEC));
    }
    else if (cmd[0] == 0x67) { // Touch coordinate data returns
      // 0X67++ Coordinate X High-order+Coordinate X Low-order+Coordinate Y High-order+Coordinate Y Low-order+TouchEvent State+End
    }
    else if (cmd[0] == 0x68) { // Touch Event in sleep mode
      // 0X68++Coordinate X High-order+Coordinate X Low-order+Coordinate Y High-order+Coordinate Y Low-order+TouchEvent State+End
    }
  }
  else { //read get data
    if (cmd[0] == 0x70) { // String variable data returns
      // X70+Variable Content in ASCII code+End
      return cmd;
    }
    else if (cmd[0] == 0x71) { // Numeric variable data returns
      // 0X71+variable binary data(4 bytes little endian mode, low in front)+End
      return cmd;
    }
  }
}
//------------------------------------------------------------------
void check_display() { // kontrola prijatych dat
  if (nextion.available() > 0) // kontroluje obsah pameti, pokud nen nic odeslano, dalsi cast programu se neprovede
  {
    Nextion_receive(true); // precist hodnoty z serial portu
  }
}
//------------------------------------------------------------------
void nextion_init(int speed_init) { // nastaveni pri spusteni displeje
  nextion.begin(speed_init);
}

Co děláme? Provedeme nezbytné nastavení a pak v loop kontrolujeme, zda z displeje přišla nějaká data. Pokud dorazila, zkontrolujeme ve funkci touch_Return zda ze správného tlačítka a pak převrátíme stav výstupu. Displej si jede po svojí linii.

Tento příklad má jedno úskalí. Ušetří nám sice místo v Arduino, protože část programu je napsána přímo v displeji, ale protože nemáme zpětnou vazbu, nemusí korespondovat to, co nám zobrazuje ikona na displeji se skutečně rozsvícenou diodou na Arduinu.

Praktická ukázka – tiskneme tlačítko podruhé

Poupravíme HMI soubor – ke stažení zde. Tady jsme odstranili proměnnou a změnu zobrazení rozsvícené diody necháme na vlastním Arduinu. Jenom mu posíláme informaci, že jsme stiskli tlačítko.

#define nextion Serial // port pro komunikaci s displejem Nextion
#define LED 13 // nastaveni nazvu pin
//------------------------------------------------------------------
void setup() {
  pinMode (LED, OUTPUT); // nastaveni vystupu
  nextion_init(9600); // inicializace displeje a nastaveni rychlosti (mozno ponechat prazdne)
}
//------------------------------------------------------------------
void loop() {
  check_display(); // kontrola prijatych dat
}
//------------------------------------------------------------------
void touch_Return(String page_ID, String component_ID, String touch_event) {
  if (page_ID == "0") {
    if (component_ID == "1") {
      if (digitalRead(LED)) { // kdy je dioda rozsvicena
        digitalWrite(LED, LOW); // zhasni diodu
        visible(F("p0"), true); // zobraz ikolu zhasnute led
      }
      else {
        digitalWrite(LED, HIGH); // rozsvit diodu
        visible(F("p1"), true); // zobraz ikolu rozsvicene led
      }
    }
  }
}
//------------------------------------------------------------------
void page_Return(String page_ID) {}
//------------------------------------------------------------------
String Nextion_receive(boolean read_data) { //returns generic

  boolean answer = false; // znacka
  char bite; // promenna pro ulozeni znaku
  String cmd; // promenna pro ulozeni textu
  byte countEnd = 0; // pocitadlo
  unsigned long previous; // cas spusteni
  int timeout = 1000; // doba po kterou se ceka na prichozi data
  previous = millis();

  do { // cekani na spravnou odpoved
    if (nextion.available() > 0) { // kdyz jsou k dispozici data, precti data
      bite = nextion.read();
      cmd += bite;
      if ((byte)bite == 0xff) countEnd++;
      if (countEnd == 3) answer = true;
    }
  }
  while (!answer && !((unsigned long)(millis() - previous) >= timeout)); // ceka na spravnou hodnotu, nebo uplynuti casu

  if (read_data) { // read general data
    if (cmd[0] == 0x65) { // Touch event return data
      // 0X65 + Page ID + Component ID + TouchEvent + End
      touch_Return(String(cmd[1], DEC), String(cmd[2], DEC), String(cmd[3], DEC));
    }
    else if (cmd[0] == 0x66) { // Current page ID number returns
      // 0X66 + Page ID + End
      page_Return(String(cmd[1], DEC));
    }
    else if (cmd[0] == 0x67) { // Touch coordinate data returns
      // 0X67++ Coordinate X High-order+Coordinate X Low-order+Coordinate Y High-order+Coordinate Y Low-order+TouchEvent State+End
    }
    else if (cmd[0] == 0x68) { // Touch Event in sleep mode
      // 0X68++Coordinate X High-order+Coordinate X Low-order+Coordinate Y High-order+Coordinate Y Low-order+TouchEvent State+End
    }
  }
  else { //read get data
    if (cmd[0] == 0x70) { // String variable data returns
      // X70+Variable Content in ASCII code+End
      return cmd;
    }
    else if (cmd[0] == 0x71) { // Numeric variable data returns
      // 0X71+variable binary data(4 bytes little endian mode, low in front)+End
      return cmd;
    }
  }
}
//------------------------------------------------------------------
void check_display() { // kontrola prijatych dat
  if (nextion.available() > 0) // kontroluje obsah pameti, pokud nen nic odeslano, dalsi cast programu se neprovede
  {
    Nextion_receive(true); // precist hodnoty z serial portu
  }
}
//------------------------------------------------------------------
void nextion_init(int speed_init) { // nastaveni pri spusteni displeje
  nextion.begin(speed_init);
}
//------------------------------------------------------------------
void visible(String componentID, boolean visible) { // skryti/zobrazeni komponenty
  String vis = "vis " + componentID + "," + String(visible);
  send_Command(vis.c_str());
}
//------------------------------------------------------------------
void send_Command(const char* cmd) { // odeslani dat do displeje
  nextion.print(cmd);
  nextion.write(0xFF);
  nextion.write(0xFF);
  nextion.write(0xFF);
  nextion.flush();
}

Co děláme? Obligátní nastavení a pak po kontrole stisknutého tlačítka ověříme stav diody. Když je rozsvícená, zhasneme ji a odešleme displeji příkaz pro zobrazení příslušné ikony. Když je zhasnutá, rozsvítíme ji a odešleme příkaz pro zobrazení ikony. To samozřejmě funguje pouze v případě, že se ikony překrývají. Pokud jsou vedle sebe, je zapotřebí přidat příkaz pro skrytí příslušné ikony.

Tento příklad už žádné úskalí nemá, vše funguje jak má, ale zabírá nám více místa v Arduinu a může dojít k jisté časové prodlevě což nervózní uživatele, může vést k opětovnému stisknutí tlačítka a další změně stavu. Záleží na tom, jak máte postavený celý program a jak upřednostňujete komunikaci s displejem.

Závěr

V skečích jsem odmazal funkce displeje, které nevyužijeme, to aby to bylo kratší. Také je vidět, že pro úsporu paměti RAM je možné využít u názvů prvků makro F().

Kterou variantu zvolit. Osobně se přikláním k variantě dvě. Prostě kontroluji, co dělám. Samozřejmě to lze uchopit ještě stokrát jinak, jenom jsem Vám chtěl ukázat, že je zapotřebí domyslet co se může stát a podle toho tomu přizpůsobit program, a ne vše cpát slepě do displeje, protože to umí. A opravdu umí.

Příště pokročíme, začneme listovat mezi stránkami a ukážeme si práci s nastavením jasu.

JB

jaroslav.bohac@arduinotech.cz

Přidat komentář

Zvýrazněné položky jsou povinné.

Přehled komentářů

  1. rozpoznani stranky (Tomas, 17.2.2017 12:25:30) Odpovědět | Zobrazit odpovědi

    Dobrý den,
    jak tedy arduino pozná že jsme na stránce 1 kdyz displej naběhne na stranu 1 neodešle žádné info a tím tedy nejde použít

    void page_Return(String page_ID) {
    if (page_ID == "0") {
    // posilej hodnoty
    }
    }

    1. Re: rozpoznani stranky (Jaroslav Boháč, 21.2.2017 19:12:22) Odpovědět

      Toto je zapotřebí naprogramovat na displeji. V programu pro displej tam funkce send me po načtení stránky. Tato funkce odešle ID načtené stránky, tak řekneme arduinu, že je zobrazena strana xy a podle toho se naprogramuje příslušná akce ve funkci page_return.


TOP produkty

Arduino MEGA2560

Arduino MEGA2560
424 Kč s DPH

NodeMCU s ESP8266

NodeMCU s ESP8266
350 Kč s DPH

Kontakt

Ing. Petr Foltýn
Kunčice pod Ondřejníkem 814, 73913
TOPlist