Přeskočit obsah

Displeje a grafický návrhář

Tato stránka popisuje, jak doplněk .newblk oznámí ESP IDE rozlišení vlastního displeje. Díky tomu grafický návrhář kreslí do správné velikosti, i když projekt používá jiný panel než původní OLED 128 × 64.

Platí pro doplňky s vlastním inicializačním blokem, například pro SPI LCD, OLED nebo e-paper. Neřeší elektrické zapojení konkrétního modulu ani jeho komunikační protokol. Ty jsou součástí ovladače v MicroPythonu.

Co návrhář potřebuje vědět

Grafický návrhář pracuje s pixely. Než otevře plátno, musí znát alespoň:

  • šířku displeje v pixelech,
  • výšku displeje v pixelech,
  • zda jde o monochromatický profil nebo profil označený jako rgb či tricolor.

Tyto údaje neposílá MicroPython z desky. Poskytne je JavaScriptová část doplňku přímo v prohlížeči. ESP IDE proto před otevřením návrháře vyhledá použitý inicializační blok na Blockly ploše a ze zaregistrovaného doplňku získá jeho profil.

Důležité rozdělení rolí

Registrace rozlišení ovlivňuje pouze návrhář v ESP IDE. Neinicializuje displej na desce a nemění Python generovaný blokem. Inicializační blok musí nadále správně vytvořit skutečný ovladač displeje v MicroPythonu.

Jak se vybírá aktivní displej

ESP IDE poskytuje v novějších verzích globální API window.ESPIDE_DISPLAY_TARGETS.

  1. Doplněk zaregistruje typ svého inicializačního bloku a funkci, která vrátí profil displeje.
  2. IDE sleduje Blockly plochu. Do výběru zahrne jen zaregistrované inicializační bloky, které jsou opravdu vložené v projektu.
  3. Aktivní je naposledy vytvořený nebo změněný platný inicializační blok. Pořadí použití se ukládá do dat bloku, takže zůstane zachované i po uložení a opětovném otevření projektu.
  4. Když aktivní inicializační blok smažeš, IDE použije předchozí platný blok. Pokud na ploše není žádný zaregistrovaný inicializační blok, návrhář použije výchozí profil 128 × 64 monochromatický.

Pouhá přítomnost bloku v toolboxu nic nemění. Je to záměr: v toolboxu může být současně OLED 128 × 64 i několik doplňků pro jiné displeje. Návrhář se řídí až blokem, který uživatel skutečně vložil do programu.

Nejjednodušší doplněk s pevným rozlišením

Pro displej, který má vždy stejné rozlišení, stačí do JavaScriptové části .newblk přidat následující registraci. Patří před delimiter <!toolbox!>.

var myDisplayRoot_ = typeof window !== "undefined" ? window : this;

if (myDisplayRoot_.ESPIDE_DISPLAY_TARGETS &&
    typeof myDisplayRoot_.ESPIDE_DISPLAY_TARGETS.register === "function") {
  myDisplayRoot_.ESPIDE_DISPLAY_TARGETS.register(
      "my_display_init", function() {
        return {
          profileId: "my-display-84x48",
          width: 84,
          height: 48,
          mode: "mono",
          label: "Můj displej — 84×48"
        };
      });
}

V tomto příkladu:

  • myDisplayRoot_ je bezpečný odkaz na globální objekt prohlížeče. V ESP IDE je to běžně window.
  • ESPIDE_DISPLAY_TARGETS je API vytvořené ESP IDE, nikoli proměnná doplňku.
  • "my_display_init" musí být přesně stejný text jako typ inicializačního bloku v Blockly.Blocks["my_display_init"] a v XML toolboxu <block type="my_display_init">.
  • profileId je stabilní interní identifikátor profilu. Neměň jej bez důvodu u dalších verzí stejného panelu.
  • width a height jsou rozměry v pixelech, nikoli fyzické rozměry v milimetrech nebo palcích.
  • label se zobrazí uživateli jako název vybraného profilu.
  • mode může být mono, rgb nebo tricolor. Pokud jej neuvedeš nebo použiješ jinou hodnotu, API jej normalizuje na mono.

Podmínka kolem register není zbytečná

Starší ESP IDE ještě ESPIDE_DISPLAY_TARGETS nezná. První část podmínky ověří, zda API existuje. Operátor && pak druhou část už nevyhodnotí, pokud první neplatí. Doplněk se proto ve starší verzi načte bez chyby, jen návrhář zůstane na starším fallbacku 128 × 64.

Nezapisuj registraci bez této ochrany přímo jako window.ESPIDE_DISPLAY_TARGETS.register(...). Ve starší verzi IDE by se doplněk při načítání zastavil chybou a jeho bloky by se nemusely přidat do toolboxu.

Doplněk s volbou více panelů

Jeden inicializační blok může nabízet více panelů. V takovém případě musí resolver vždy vrátit profil právě vybraného panelu. Hodnoty v dropdownu drž jako stabilní technické klíče; uživatelský překlad patří do viditelného textu položky.

var MY_DISPLAY_PROFILES_ = {
  LCD_84X48: {
    width: 84, height: 48,
    profileId: "my-lcd-84x48",
    label: "LCD 84×48"
  },
  EPD_400X300: {
    width: 400, height: 300,
    profileId: "my-epd-400x300",
    label: "E-paper 400×300"
  }
};

function myDisplayProfile_(key) {
  return MY_DISPLAY_PROFILES_[key] || MY_DISPLAY_PROFILES_.LCD_84X48;
}

if (myDisplayRoot_.ESPIDE_DISPLAY_TARGETS &&
    typeof myDisplayRoot_.ESPIDE_DISPLAY_TARGETS.register === "function") {
  myDisplayRoot_.ESPIDE_DISPLAY_TARGETS.register(
      "my_display_init", function(block) {
        var profile = myDisplayProfile_(block.getFieldValue("PANEL"));
        return {
          profileId: profile.profileId,
          width: profile.width,
          height: profile.height,
          mode: "mono",
          label: profile.label
        };
      });
}

block.getFieldValue("PANEL") čte aktuální hodnotu pole dropdownu z konkrétního bloku na ploše. To je důvod, proč resolver dostává argument block.

Pokud změna dropdownu mění rozlišení, je vhodné po změně výslovně označit blok jako naposledy použitý. Tento malý pomocník používá stejný princip jako doplňky WeAct e-paper:

function myDisplayMarkUsedLater_(block) {
  if (!myDisplayRoot_.setTimeout) return;
  myDisplayRoot_.setTimeout(function() {
    var targets = myDisplayRoot_.ESPIDE_DISPLAY_TARGETS;
    if (targets && typeof targets.markUsed === "function") {
      targets.markUsed(block);
    }
  }, 0);
}

Blockly.Blocks["my_display_init"] = {
  init: function() {
    var sourceBlock = this;
    var panelField = new Blockly.FieldDropdown(
        [["LCD 84×48", "LCD_84X48"], ["E-paper 400×300", "EPD_400X300"]],
        function(newValue) {
          myDisplayMarkUsedLater_(sourceBlock);
          return newValue;
        });
    this.appendDummyInput().appendField(panelField, "PANEL");
    // ... další vstupy inicializačního bloku ...
  }
};

Odložené volání přes setTimeout(..., 0) proběhne až po dokončení změny hodnoty pole. markUsed(block) zapíše pořadí použití pouze tehdy, když resolver pro blok vrací platný profil.

Aby fungovaly bloky Display i návrhář

Registrace rozlišení je pouze první část. Inicializační blok musí vygenerovat proměnné, se kterými už pracují společné bloky z kategorie Display:

display = MyDisplay(...)
buffer = display.buffer
fbuf = display.fbuf

Význam proměnných:

Proměnná Co musí poskytovat
display Ovladač fyzického displeje s metodou show() pro odeslání obrazu do panelu.
buffer Bajtové pole framebufferu. Doporučený zdroj je display.buffer. Používá jej živý náhled návrháře.
fbuf Objekt s kreslicím API MicroPythonu framebuf, například fill, pixel, line, rect, fill_rect, text, blit a scroll.

Nejjednodušší ovladač dědí z framebuf.FrameBuffer. Pak může být fbuf přímo stejný objekt jako display:

class MyDisplay(framebuf.FrameBuffer):
    def __init__(self, ...):
        self.buffer = bytearray(...)
        super().__init__(self.buffer, WIDTH, HEIGHT, framebuf.MONO_VLSB)
        self.fbuf = self

    def show(self):
        # Odeslání self.buffer do skutečného displeje.
        pass

Společné kreslicí bloky a kód z návrháře kreslí do fbuf. Blok pro překreslení volá display.show(). Pokud doplněk tyto proměnné nebo metodu neposkytne, profil v návrháři může mít správnou velikost, ale vygenerovaný obrázek nebude možné běžným způsobem vykreslit na panel.

Použij existující blok návrháře

Pro běžný displej není potřeba vytvářet vlastní blok pro ukládání grafické scény. Použij standardní blok espide_display_designer z kategorie Display. Při otevření automaticky převezme aktivní profil z registrovaného inicializačního bloku, zachová kompletní generátor ESP IDE včetně dynamických vrstev a podpory scene.dat.

Ruční rozlišení a zachování práce uživatele

V návrháři může uživatel zadat vlastní šířku a výšku. Tato volba platí pro právě upravovanou scénu, uloží se do rozšiřujících dat scény espide.adaptive-display-v1 a má přednost před aktuálním profilem displeje.

Při otevření se chování rozlišuje takto:

  • prázdná dosud neupravená scéna 128 × 64 převezme rozměry aktivního inicializačního bloku;
  • neprázdná scéna nebo scéna s ručně nastaveným rozlišením se sama nepřepočítá, když uživatel později změní inicializační blok;
  • návrhář ukáže hodnoty scény i aktivní profil a nabízí výslovnou akci pro použití profilu displeje.

Tím se zabrání nečekanému oříznutí nebo posunu již nakreslených objektů.

Rozměr musí být v rozsahu 1 až 1024 pixelů na každé ose a framebuffer pro monochromatický profil nesmí vyžadovat více než 65 535 bajtů. IDE potřebnou velikost počítá jako šířka × ceil(výška / 8).

Vlastní blok s tlačítkem návrháře (pokročilé)

Používej jej jen tehdy, když doplněk opravdu potřebuje vlastní Blockly blok, který vlastní samostatnou scénu. Standardní blok espide_display_designer je pro většinu displejů lepší volba.

API umí na vlastní blok připojit ukládání scény i tlačítko:

Blockly.Blocks["my_display_scene"] = {
  init: function() {
    this.appendDummyInput().appendField("obrazovka vlastního displeje");
    if (myDisplayRoot_.ESPIDE_DISPLAY_TARGETS &&
        typeof myDisplayRoot_.ESPIDE_DISPLAY_TARGETS.attachDesigner === "function") {
      myDisplayRoot_.ESPIDE_DISPLAY_TARGETS.attachDesigner(this, {
        buttonLabel: "Otevřít grafický návrhář"
      });
    }
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
  }
};

attachDesigner() zachová již existující mutation handlery bloku, uloží scénu do přidaných atributů mutation a přidá metody getDisplayDesignerScene() a setDisplayDesignerScene(). Generátor Pythonu vlastního bloku je ale stále odpovědností autora doplňku. Proto tento postup nepoužívej jen kvůli nastavení rozlišení.

Ve starším ESP IDE je i tato část chráněná podmínkou. Blok se může načíst, ale nemá adaptivní tlačítko návrháře.

Zpětná kompatibilita projektů a doplňků

Registrace profilu nemění typ vestavěného bloku návrháře ani základní formát scény. Novější informace o adaptivním rozlišení jsou přidány jako rozšiřující data scény.

  • Starší IDE bez ESPIDE_DISPLAY_TARGETS načte doplněk díky ochranné podmínce, ale používá původní návrhář 128 × 64.
  • Starší IDE zachová neznámá rozšiřující data scény, takže projekt lze znovu otevřít v nové verzi.
  • Jestliže starší IDE nezná vlastní typ bloku doplňku a doplněk v něm není nainstalovaný, tento blok nemůže načíst. To je běžné pravidlo pro každý .newblk doplněk, nezávisle na návrháři.

Kontrolní seznam autora doplňku pro displej

  • Inicializační blok existuje v Blockly.Blocks[...], generátoru Pythonu i XML toolboxu pod stejným typem.
  • Doplněk bezpečně registruje tento typ přes ESPIDE_DISPLAY_TARGETS.register(...).
  • Resolver vrací správné width, height, stabilní profileId a srozumitelný label.
  • Při více panelech resolver čte správné pole bloků a změna volby označí blok jako použitý.
  • Generovaný Python vytvoří display, buffer a fbuf před kreslicími bloky.
  • fbuf nabízí kreslicí API framebuf a display.show() odesílá framebuffer do skutečného displeje.
  • Běžně se používá vestavěný blok espide_display_designer; vlastní scénový blok přidej jen při skutečné potřebě.
  • Registrace i případné attachDesigner() jsou chráněné kontrolou existence API kvůli starším verzím ESP IDE.
  • .newblk stále obsahuje právě jeden delimiter <!toolbox!>.

Související stránky