MKR1010 e-ink EPD

MKR1010 e-ink EPD completa la serie di articoli che hai letto sull’utilizzo del display di tipo e-paper con la MKR1010 WiFi Arduino e la libreria di Asuki Kono.

MKR1010 e-ink EPD e-ink paper 1in54 first test

Se hai letto gli articoli precedenti comprenderai facilmente come usare la libreria EPD per scrivere sul display eInk della Waveshare mostrato nelle foto.

Userai uno degli esempi che Asuki ha scritto per agevolarti nel test del display e nella realizzazione dei tuoi progetti.

Collega il display MKR1010 e-ink EPD

Il collegamento del display eInk alla mkr1010 wifi deve tener conto della tabella dei collegamenti che trovi sul sito del produttore del display a pagina 8 del manuale utente e seguendo la foto dei collegamenti che ho realizzato:

MKR1010 e-ink paper 1in54 connections

in cui distingui chiaramente i colori dei fili provenienti dal display per l’alimentazione a 3,3v ed i pin relativi alla comunicazione MISO/SCK ed il reset.

In ogni caso puoi seguire il seguente schema:

e-ink paper 1in54 tablein cui sono riportati a sinistra i pin lato display ed a destra i pin scelti sulla MKR1010 WiFi.

Conoscere i pin di collegamento del display sarà fondamentale per la stesura dello sketch.

Dovrai definire, infatti, nel programma quali pin utilizzare per le funzioni Reset, DC, CS, Busy.

Progetto di esempio per MKR1010 e-ink EPD

Il progetto MKR1010 e-ink EPD non è complesso concettualmente, lo scopo è visualizzare sul display prima una pagina di test con cerchi e quadrati vuoti e pieni:

e-ink paper 1in54 first image

e successivamente il mio logo con sovrapposto un contatore che inizia in contemporanea alla partenza dello sketch stesso:

MKR1010 e-ink paper 1in54 logo

e conta fino a quando è alimentato il sistema:

MKR1010 e-ink paper 1in54 without power

infatti la particolarità dell’eInk o ePaper display è che anche senza alimentazione, vedi immagine superiore, resta comunque visibile con l’ultima immagine visualizzata.

Tale comportamento è possibile grazie al fatto che il display una volta composta l’immagine non necessità, per sua natura, di alimentazione per mantenerla visibile. 

Avrai già compreso che questa tipologia di display sono a bassissimo consumo, prossimo al nullo, dopo aver visualizzato una immagine.

Lo sketch

di seguito lo sketch che ho utilizzato per il progetto MKR1010 e-ink EPD:

#include <SPI.h>
#include <EPD1in54.h>
#include <EPDPaint.h>
#include "imagedata.h"

#define COLORED     0
#define UNCOLORED   1

/**
  * Due to RAM not enough in Arduino UNO, a frame buffer is not allowed.
  * In this case, a smaller image buffer is allocated and you have to
  * update a partial display several times.
  * 1 byte = 8 pixels, therefore you have to set 8*N pixels at a time.
  */
unsigned char image[1024];
EPDPaint paint(image, 0, 0);    // width should be the multiple of 8
EPD1in54 epd(5, 6, 7, 4); // default reset: 8, dc: 9, cs: 10, busy: 7

unsigned long timeStartMs;
unsigned long timeNowS;
unsigned long timeShowedS = 1000;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  if (epd.init(lutFullUpdate) != 0) {
    Serial.print("e-Paper init failed");
    return;
  }

  /**
   *  there are 2 memory areas embedded in the e-paper display
   *  and once the display is refreshed, the memory area will be auto-toggled,
   *  i.e. the next action of SetFrameMemory will set the other memory area
   *  therefore you have to clear the frame memory twice.
   */
  epd.clearFrameMemory(0xFF);   // bit set = white, bit reset = black
  epd.displayFrame();
  epd.clearFrameMemory(0xFF);   // bit set = white, bit reset = black
  epd.displayFrame();

  paint.setRotate(ROTATE_0);
  paint.setWidth(200);
  paint.setHeight(24);

  /* For simplicity, the arguments are explicit numerical coordinates */
  paint.clear(COLORED);
  paint.drawStringAt(30, 4, "Hello world!", &Font16, UNCOLORED);
  epd.setFrameMemory(paint.getImage(), 0, 10, paint.getWidth(), paint.getHeight());

  paint.clear(UNCOLORED);
  paint.drawStringAt(30, 4, "e-Paper Demo", &Font16, COLORED);
  epd.setFrameMemory(paint.getImage(), 0, 30, paint.getWidth(), paint.getHeight());

  paint.setWidth(64);
  paint.setHeight(64);

  paint.clear(UNCOLORED);
  paint.drawRectangle(0, 0, 40, 50, COLORED);
  paint.drawLine(0, 0, 40, 50, COLORED);
  paint.drawLine(40, 0, 0, 50, COLORED);
  epd.setFrameMemory(paint.getImage(), 16, 60, paint.getWidth(), paint.getHeight());

  paint.clear(UNCOLORED);
  paint.drawCircle(32, 32, 30, COLORED);
  epd.setFrameMemory(paint.getImage(), 120, 60, paint.getWidth(), paint.getHeight());

  paint.clear(UNCOLORED);
  paint.drawFilledRectangle(0, 0, 40, 50, COLORED);
  epd.setFrameMemory(paint.getImage(), 16, 130, paint.getWidth(), paint.getHeight());

  paint.clear(UNCOLORED);
  paint.drawFilledCircle(32, 32, 30, COLORED);
  epd.setFrameMemory(paint.getImage(), 120, 130, paint.getWidth(), paint.getHeight());
  epd.displayFrame();

  delay(2000);

  if (epd.init(lutPartialUpdate) != 0) {
    Serial.print("e-Paper init failed");
    return;
  }

  /**
   *  there are 2 memory areas embedded in the e-paper display
   *  and once the display is refreshed, the memory area will be auto-toggled,
   *  i.e. the next action of SetFrameMemory will set the other memory area
   *  therefore you have to set the frame memory and refresh the display twice.
   */
  epd.setFrameMemory(IMAGE_DATA);
  epd.displayFrame();
  epd.setFrameMemory(IMAGE_DATA);
  epd.displayFrame();

  timeStartMs = millis();
}

void loop() {
  // put your main code here, to run repeatedly:
  timeNowS = (millis() - timeStartMs) / 1000;
  if (timeNowS != timeShowedS) {
    timeShowedS = timeNowS;
    updateTime(timeShowedS);
  }
  delay(20);
}

void updateTime(unsigned long seconds) {
  char time_string[] = {'0', '0', ':', '0', '0', '\0'};
  time_string[0] = seconds / 60 / 10 + '0';
  time_string[1] = seconds / 60 % 10 + '0';
  time_string[3] = seconds % 60 / 10 + '0';
  time_string[4] = seconds % 60 % 10 + '0';

  paint.setWidth(96);
  paint.setHeight(32);
  paint.setRotate(ROTATE_0);

  paint.clear(UNCOLORED);
  paint.drawStringAt(0, 4, time_string, &Font24, COLORED);
  epd.setFrameMemory(paint.getImage(), 104, 100, paint.getWidth(), paint.getHeight());
  epd.displayFrame();
}

come spesso hai notato le prime linee 001-004 includono le librerie necessarie per il corretto funzionamento dello sketch.

Includi l’immagine pre convertita PROGMEM

Di queste la linea 004 include il file imagedata.h con il seguente codice:

/**
 *  @filename   :   imagedata.h
 *  @brief      :   head file for imagedata.cpp
 *
 *  Copyright (C) Waveshare     September 5 2017
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documnetation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to  whom the Software is
 * furished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

extern const unsigned char IMAGE_DATA[];

/* FILE END */

in cui è definita la matrice IMAGE_DATA[];

a sua volta impostata nel file imagedata.cpp che viane automativamnete incluso e nel quale avrai copiato la codifica di immagine da visualizzare.

Per la conversione dell’immagine puoi seguire questo tutorial.

ed in cui avrai qualcosa di simile a questo codice:

#include "imagedata.h"
#if defined(__AVR__) || defined(ARDUINO_ARCH_SAMD)
#include <avr/pgmspace.h>
#elif defined(ESP8266) || defined(ESP32)
#include <pgmspace.h>
#endif

const unsigned char IMAGE_DATA[] PROGMEM = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
....
..
..
.
};

Descrizione dello sketch

tornando allo sketch principale:

le linee 006-007: definisci due costanti COLORED e UNCOLORED da usare per accendere e spegnere i singoli pixel del display nella fase di disegno.

linee 015-016: definisci la matrice image da 1024 elementi e la imposti per gestirla con la libreria usando il metodo paint della EPD paint library per definire l’immagine che userai nello sketch;

linea 017: inizializza l’istanza epd impostando come valori dei pin a cui la MKR1010 è connessa al display in:

  • reset: 5
  • dc: 6
  • cs: 7
  • busy: 4

linee 019-021: imposta le tre variabili di timing da usare nello sketch;

linea 027: inizializza il display ePaper e-Ink usando il metodo init e controlla lo stato del return code per sapere se l’inizializzazione è andata a buon fine e puoi accedere al display;

linee 038-041: esegui due volte i medesimi due comandi, il primo clearFrameMemory pulisce la memoria ed il secondo displayFrame esegue lo switch alla memoria successiva.

Il display è dotato di due memorie, come leggi nel commento che precede la linea 038, per cui è necessario pulirle entrambe per poter partire da una situazione pulita.

Disegna per il display e-ink

linee 043-48: definisci una immagine, usando l’istanza paint, di 200 x 24 px, colore nero ( COLORED ) e ruotata di 0° ossia orizzontale;

linea 049: scrivi, nell’immagine che hai pre impostato, Hello world! con font 16 e partendo dal punto 30,4 dell’immagine con colore UNCOLRED ossia pixel bianchi, non attivando l’e-ink;

linea 050: usa il metodo setFrameMemory per impostare nella prima memoria del display l’immagine creata in posizione 0,10;

linee 052-054: scrivi sul display la frase e-Paper Demo sempre nella posizione 30,4 con font 16 ma in nero ( COLORED )

Nota che le linee 052-054 sono molto simili alle linee 048-050 in quanto l’obiettivo di entrambe è il medesimo.

Il display in questa fase è ancora in preparazione ma se chiedessi al display di visualizzare questa area di memoria otterresti:

e-ink paper 1in54 first image

le prime due linee, come in figura, ti mancherebbero le forme giometriche che dovrai disegnare.

Le linee 056-057 dello sketch MKR1010 e-ink EPD impostano una immagine da 64×64 pixel;

linea 059: setta lo sfondo dell’immagine a UNCOLORED, ossia senza colore, bianco;

linea 060: disegna un rettangolo di dimensioni 40×50 pixel, rispetto all’immagine appena creata e partendo dal punto 0,0:

linee 061-062: disegnano due linee incrociate tra loro e con gli estremi coincidenti con gli angoli della figura ( rettangolare ) appena creata;

linea 063: imposta l’immagine creata nella memoria del display alle coordinate 16,60;

linee 065-067: disegnano un cerchio, vuoto, solo con il bordo COLORED posizionato alle coordinate 120,60;

linee 069-075: usando i medesimi metodi visti fino a questo momento, procedi a creare un rettangolo pieno COLORED ( nero ) e successivamente un cerchio, pieno, nella posizione relativa dell’immagine 32×32 e ragigio 30 come il precedente vuoto ( linee 065-067 ) e posiziona il rettangolo in 16,130 ed il cerchio in 120,130;

linea 076: usa il metodo displayFrame per visualizzare l’immagine della memoria sul display;

linee 080-083: esegui nuovamente il check di comunicazione con il display;

Prepara e visualizza l’immagine in PROGMEM

linee 091-092: usando il metodo setFrameMemory imposta IMAGE_DATA nella prima memoria del display e passa alla seconda memoria in cui farai la medesima cosa con le linee 093-094;

linea 096: imposta il timeStartMs al valore corrente di millis();

linea 101: nella loop() imposta il valore corrente di timeNowS uguale al tempo trascorso dall’assegnazione del valore di timeStartMs diviso 1000;

linee 102-105: se il tempo trascorso è differente da timeShowedS reimposta quest’ultimo e e richiami la funzione updateTime passandole il valore di timeShowedS;

linea 110: imposta la variabile time_string[] come vettore di caratteri di 6 elementi;

linee 111-114: per ciascuno degli elementi 0,1,3 e 4 imposti i valori di tempo trascorso dall’avvio dello sketch calcolandoli sulla base di “seconds”, la variabile in input presa da  timeShowedS calcolata nella loop();

linee 116-120: imposta una immagine delle dimensioni 96,32 con angolo di rotazione 0 e sfondo di colore bianco ( UNCOLORED )

linea 121: scrivi il time_string, calcolato alle linee 116-120, alla posizione 0,4 con font 24 COLORED ( nero );

linee 122-123: imposta l’immagine creata nella memoria del display alla posizione 104,100 e visualizzala.

Se tutto lo sketch funziona in modo corretto dovresti quindi visualizzare sul tuo MKR1010 e-ink EPD la seguente immagine:

in cui il timer continua ad aggiornarsi ogni 1000 millisecondi ( un secondo ).

  • Questo sito ed i suoi contenuti è fornito "così com'è" e Mauro Alfieri non rilascia alcuna dichiarazione o garanzia di alcun tipo, esplicita o implicita, riguardo alla completezza, accuratezza, affidabilità, idoneità o disponibilità del sito o delle informazioni, prodotti, servizi o grafiche correlate contenute sul sito per qualsiasi scopo.
  • Ti chiedo di leggere e rispettare il regolamento del sito prima di utilizzarlo
  • Ti chiedo di leggere i Termini e Condizioni d'uso del sito prima di utilizzarlo
  • In qualità di Affiliato Amazon io ricevo un guadagno dagli acquisti idonei qualora siano presenti link al suddetto sito.

Permalink link a questo articolo: https://www.mauroalfieri.it/elettronica/mkr1010-e-ink-epd.html

6 commenti

Vai al modulo dei commenti

    • Giovanni il 17 Ottobre 2019 alle 08:32
    • Rispondi

    Ciao,

    ho appena acquistato questo modulo, e notavo il discorso delle due aree di memoria, e non riesco a capirlo: infatti alle righe 38-41 e 91-94 il metodo displayFrame è chiamato due volte di fila, mentre negli altri casi, quando vengono disegnate le linee, i cerchi ed il testo, il metodo è chiamato una sola volta, alla riga 76.
    Grazie

    1. Ciao Giovanni,
      in realtà quando lo richiami due volte stai lavorando su due pagine ( aree di memoria ), come hai già compreso.
      Quando lavori su una sola delle pagine lo richiami solo per quella e non per tutte.

        • Giovanni Romeo il 5 Novembre 2019 alle 14:37
        • Rispondi

        Ciao e grazie per la risposta. Non ho capito, è possibile lavorare su una sola delle pagine e aggiornare sempre e solo quella? Come si fa?

        Grazie,
        Giovanni

        1. Ciao Giovanni,
          lavoro sempre con due pagine, puoi caricare in entrambe la medesima informazione.

    • Paolo Orione il 20 Gennaio 2022 alle 18:05
    • Rispondi

    Grazie Mauro, il tuo articolo mi ha ispirato per mettere in cantiere un progetto di riaggiornamento della strumentazione della barca. Riesco a prelevare i dati nmea di tutti i trasduttori via rs 232 e credo che, con un po’ di codice, riuscirò a visualizzarli su uno schermo è-ink

    Saluti
    Paolo

    1. Ottimo Paolo,
      se vuoi quando lo avrai ultimato mi piacerebbe pubblicarlo, sarebbe utile per altri appassionati di elettronica e non solo.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.