Il protocollo Custom CAN ID è ampiamente utilizzato nel settore dell'automazione industriale per il controllo dei dispositivi. In questo contesto, le schede Arduino rappresentano una soluzione pratica per l'integrazione di sistema e possono essere utilizzate come host controller per gli Smart Display, in particolare per i modelli privi di funzionalità touch. Grazie al protocollo Custom CAN ID, una scheda Arduino consente di controllare in modo efficace anche Smart Display non touch.
In questo articolo viene presentato un programma dimostrativo che utilizza Arduino UNO con un CAN Shield per controllare uno Smart Display tramite il protocollo Custom CAN ID, in abbinamento a un OLED Knob Display. Il programma consente di esplorare diverse funzioni e modalità di controllo degli Smart Display non touch. Rappresenta inoltre un utile riferimento per gli sviluppatori che desiderano approfondire l'integrazione di sistema e l'utilizzo di un OLED Knob Display per il controllo di Smart Display sia touch sia non touch. Il diagramma seguente illustra l'implementazione dell'applicazione.

Per realizzare questa applicazione sono necessari i seguenti componenti:
- Smart Display Custom CAN ID da 10,1”
- Arduino UNO
- CAN Bus Shield per Arduino UNO
- Modulo OLED Display Knob Rotator
- ST-Link CAN Bus Dongle
- GUI Builder v0.4.2 o versione successiva
- Arduino IDE v2.0.4
La demo è suddivisa in tre fasi:
- Progettazione del progetto demo in GUI Builder
- Build e upload del progetto
- Programmazione dell'host Arduino
Progettazione del progetto demo in GUI Builder
Per iniziare, avviare una versione aggiornata di GUI Builder e creare un nuovo progetto. Inserire il nome desiderato per il progetto e verificare che la cartella di destinazione venga selezionata automaticamente. Successivamente, scegliere il tipo di dispositivo; in questo esempio viene utilizzato un display da 10,1". Selezionare quindi il protocollo desiderato e mantenere l'orientamento del display nella modalità orizzontale predefinita. Scegliere un template UI vuoto e fare clic sul pulsante Create per creare il progetto. La figura seguente mostra i passaggi descritti.

Dopo la creazione del progetto viene visualizzata la seguente schermata.

Per realizzare lo scenario previsto dalla demo sono necessarie in totale quattro pagine. È quindi necessario aggiungerne altre tre. Fare clic sul pulsante Page, quindi utilizzare il pulsante Plus per aggiungere le tre pagine.

Il passaggio successivo consiste nell'aggiungere uno sfondo personalizzato. Accedere alla sezione Resource e selezionare l'opzione Background. Nell'opzione 2 sono disponibili tre sfondi predefiniti per le pagine Industry, Medical e Vehicle. Per aggiungere un nuovo sfondo, fare clic sul pulsante Add Widget nell'opzione 3. Successivamente, nell'opzione 4, fare clic sul pulsante con i tre punti (...) posizionato a sinistra del pulsante Reset. Da qui è possibile aggiungere uno sfondo personalizzato e sostituire quello predefinito utilizzato per la prima pagina.

A questo punto, nella sezione Pages è possibile selezionare lo stile di sfondo desiderato tra le opzioni disponibili, come illustrato nella figura seguente.

Procedere quindi con l'aggiunta di un Widget personalizzato. In base allo scenario della demo, è necessario utilizzare un Multistate Widget personalizzato per selezionare le pagine Industry, Medical e Vehicle. Tornare alla sezione Controls della prima pagina, quindi accedere alla sezione Resource e individuare l'opzione Multistate Widget. Fare clic sul pulsante Add Widget nell'opzione 2 e successivamente sul pulsante Modify nell'opzione 3. Nel passaggio 4, eliminare tutte le immagini predefinite associate al Widget. Nel passaggio 5, fare clic sul pulsante Add e, nel passaggio 6, selezionare una alla volta le immagini personalizzate dei Widget dalla cartella locale del computer. Infine, nel passaggio 7, fare clic su OK per aggiungere le immagini personalizzate all'elenco del Multistate Widget, come mostrato nella figura seguente.

Tornare alla sezione Controls della prima pagina. In base allo scenario della demo, selezionare il Multistate Widget personalizzato che verrà utilizzato per scegliere tra le pagine Industry, Medical e Vehicle. Trascinare il Widget nell'area di layout prevista per la pagina. A questo punto, la configurazione della prima pagina è completata.

Applicare la stessa procedura alle altre tre pagine create. Nella seconda pagina selezionare un Widget relativo all'ambito Industry, nella terza un Widget relativo all'ambito Medical e nella quarta un Widget relativo all'ambito Vehicle. Trascinare quindi ciascun Widget nell'area di layout della pagina corrispondente.

Build e upload del progetto
Dopo aver creato correttamente le quattro pagine e inserito i rispettivi Widget in ciascuna di esse, verificare che nella pagina iniziale sia presente il Multistate Widget personalizzato per la selezione delle pagine Industry, Medical e Vehicle. A questo punto è possibile procedere con la build del progetto. Nella sezione Page Information, fare clic sul pulsante Device e selezionare Device Properties. Verificare che le impostazioni del protocollo siano corrette, quindi selezionare la porta COM appropriata e impostare il Baud Rate desiderato. Fare clic sul pulsante "Connect to Device" per stabilire la connessione con il dispositivo. Infine, fare clic sul pulsante Upload, come mostrato nella figura seguente, per avviare il caricamento del progetto e attendere il completamento dell'operazione.

Al termine dell'upload del progetto, fare clic sul pulsante OK come mostrato nella figura seguente.

Una volta completato l'upload, viene visualizzata la schermata iniziale, come mostrato nella figura seguente.

Dopo la schermata iniziale, il display passa alla prima pagina. A questo punto, la build e l'upload del progetto tramite GUI Builder sono completati, come mostrato nella figura seguente.

Programmazione dell'host Arduino
Il codice riportato di seguito inizia con la sezione includes, nella quale vengono importate le librerie e i file header necessari per il programma. Tra questi sono incluse librerie come Adafruit_SSD1327, Adafruit_FT6206, Adafruit_GFX e SPI, oltre a file header personalizzati come mcp2515_can.h, lcdgfx_gui.h e lcdgfx.h.
Dopo la sezione includes è presente la sezione defines, nella quale vengono definite costanti specifiche e configurazioni dei pin. Questa sezione comprende, tra gli altri, i parametri relativi al rotary encoder, le dimensioni del display OLED, le impostazioni del CAN Bus e le configurazioni dei Widget.
Infine, un'istruzione condizionale verifica il tipo di microcontrollore utilizzato e seleziona l'interfaccia seriale appropriata. Se il codice viene eseguito su un microcontrollore SAMD, viene utilizzata l'interfaccia SerialUSB; negli altri casi viene utilizzata l'interfaccia Serial standard.
In sintesi, il codice riportato di seguito importa le librerie e i file header necessari, definisce le principali costanti e configurazioni dei pin e seleziona l'interfaccia seriale appropriata in base al tipo di microcontrollore utilizzato.

Successivamente vengono dichiarate e inizializzate alcune variabili e i pin necessari al funzionamento del programma. Ad esempio:
SPI_CS_PIN: questa costante è impostata sul valore 9 e identifica il pin Chip Select utilizzato per la comunicazione SPI.
CAN_INT_PIN: questa costante è impostata sul valore 2 e identifica il pin di interrupt del CAN Bus.
CAN: viene creata un'istanza della classe mcp2515_can e inizializzata con il valore SPI_CS_PIN. L'istanza viene utilizzata per la comunicazione sul CAN Bus.
ts: viene creata un'istanza della classe Adafruit_FT6206, utilizzata per la gestione delle funzioni touchscreen.
display: viene creata un'istanza della classe DisplaySSD1327_128x128_SPI e inizializzata con la configurazione dei pin necessaria per la comunicazione SPI con il display SSD1327.

La sezione di codice seguente dichiara e inizializza diversi array composti da variabili di tipo unsigned char. La funzione dei principali array è la seguente:
configMsg: array composto da 8 elementi unsigned char, utilizzato per rappresentare un messaggio di configurazione.
PageIndex_0 - PageIndex_3: ciascun array è composto da 8 elementi unsigned char e rappresenta gli indici di pagina da 0 a 3.
displayMode: array composto da 8 elementi unsigned char, utilizzato per rappresentare la modalità di visualizzazione.
Sono inoltre presenti diversi array denominati stmp, stmp1, stmp2 fino a stmp12. Anche questi array sono composti da 8 elementi unsigned char e vengono utilizzati per memorizzare i byte di dati relativi ai diversi Widget o componenti. I nomi degli array consentono di distinguere il tipo di dati e il Widget a cui sono associati.
In sintesi, questi array contengono i principali set di dati utilizzati dal programma, tra cui messaggi di configurazione, indici di pagina, modalità di visualizzazione e dati relativi ai Widget.

Il codice seguente definisce la funzione bitmapDemo, che riceve come parametro intero imageIndex. La funzione ha il compito di disegnare un'immagine sull'OLED Knob Display.
Le immagini specifiche, insieme alle relative posizioni e dimensioni, sono definite nel file header Image.h.

La funzione seguente attende una risposta sul CAN Bus eseguendo ripetutamente una breve attesa e verificando il valore del flag Recv. Quando viene ricevuta una risposta, il flag Recv viene reimpostato a 0.

Il codice seguente definisce la funzione changePageFunction, utilizzata per modificare il valore della pagina nel sistema di comunicazione CAN Bus. In base al parametro pageValue ricevuto, la funzione invia un messaggio sul CAN Bus per modificare la pagina. Il messaggio trasmesso varia in funzione del valore di pageValue e corrisponde ai diversi array PageIndex.

La funzione handleTouchEvents gestisce gli eventi touch sul touchscreen. Rileva gli input touch, converte le coordinate e svolge le azioni previste in base alla pressione e alla durata del tocco. Utilizza inoltre la funzione changePageFunction per aggiornare la pagina corrente in base all'input ricevuto. Ad esempio, quando viene rilevata una pressione prolungata, changePageFunction viene richiamata con il valore nowPage per tornare alla Home Page. Quando invece viene rilevata una pressione breve, changePageFunction viene richiamata con il valore nowPage per passare alla pagina selezionata.

Il codice riportato di seguito corrisponde alla funzione setup di Arduino, eseguita una sola volta all'avvio del programma. La funzione si occupa di inizializzare i pin, avviare la comunicazione seriale, configurare il display, impostare il CAN Bus e inizializzare il controller touchscreen.
Per prima cosa vengono impostate le modalità dei pin pinA, pinB, pinC ed EN_Pin. I pin vengono configurati come ingressi con resistenze di pull-up abilitate oppure come uscita. Il pin di uscita EN_Pin viene quindi impostato a livello logico High.
Successivamente viene avviata la comunicazione seriale con Baud Rate pari a 115200 e il programma attende che la connessione venga stabilita.
Viene quindi inizializzato l'oggetto display e vengono configurate impostazioni specifiche, come Font e Color. Una Bitmap viene disegnata sul display nella posizione prevista, seguita da un'attesa di 2 secondi.
Viene configurato un interrupt sul pin CAN_INT_PIN e viene assegnata la funzione MCP2515_ISR come routine da eseguire quando si verifica l'interrupt. L'interrupt viene attivato sul Falling Edge del segnale.
Il CAN Bus viene inizializzato con un Baud Rate di 250k. Se l'inizializzazione non riesce, il programma attende per un breve intervallo e riprova. Una volta completata correttamente l'inizializzazione, il CAN Bus viene impostato in Normal Mode e viene visualizzato un messaggio di conferma sull'uscita seriale.
Successivamente vengono configurati Filter Mask e Filter per consentire la ricezione di tutti i frame, inclusi gli Extended Frame.
Il controller touchscreen viene quindi inizializzato. Se l'inizializzazione non riesce, viene visualizzato un messaggio di errore e il programma entra in un ciclo infinito.
Infine, quando il touchscreen viene avviato correttamente, viene visualizzato un messaggio di conferma sull'uscita seriale.

Questa funzione sembra essere una routine di servizio dell'interrupt (ISR) per MCP2515, il controller CAN. Quando viene generato un interrupt, la funzione imposta un flag e visualizza un messaggio.

Le funzioni seguenti gestiscono lo stato del Knob. La funzione setKnobState aggiorna lo stato del Knob e imposta un timer per rilevare la condizione di Idle, mentre isKnobIdle verifica se il Knob si trova attualmente in stato Idle in base al valore del timer.

La funzione redrawScreen() aggiorna il display in base allo stato corrente del Knob. Verifica eventuali variazioni di stato, cancella il contenuto visualizzato e disegna la Bitmap appropriata in funzione dello stato del Knob.

La seguente sezione della funzione loop() legge lo stato di tre pin e determina un nuovo stato in base ai relativi valori. Se il nuovo stato è diverso da quello precedente, vengono eseguite diverse operazioni, tra cui l'aggiornamento del Counter, di MultiStateValue, di GaugeValue e dei valori delle altre variabili associate ai Widget.

Nella sezione successiva della funzione loop() viene gestito il caso in cui il Knob ruota in una determinata direzione. In base alla direzione di rotazione, vengono aggiornate diverse variabili e richiamate le funzioni necessarie. Al termine, viene aggiornato il valore dello stato precedente in preparazione alla successiva esecuzione di loop().

Di seguito è riportata la parte rimanente della funzione loop(). Dopo il blocco di codice precedente vengono eseguite le seguenti operazioni.
La variabile oldState viene aggiornata con il valore di newState.
Se nowPage è uguale a 0, oppure se il numero di pagina è 1, il codice verifica se MultiStateValue è diverso da LastMultiStateValue. Se i due valori sono differenti, LastMultiStateValue viene aggiornato con il valore di MultiStateValue e il valore di MultiStateValue viene memorizzato nell'array stmp1. Successivamente, viene inviato tramite CAN Bus un messaggio contenente il valore di MultiStateValue utilizzando il Multi-State ID 0x716.

Se nowPage è uguale a 1, oppure se il numero di pagina è 2, vengono eseguiti controlli analoghi su diverse variabili associate ai Widget, tra cui CircleProgressValue, HorizontalSliderValue, VerticalSliderValue, TemperatureValue, GaugeValue, FixedWidgetValue e IndicatorValue. Se uno di questi valori è diverso dal rispettivo valore precedente, quest'ultimo viene aggiornato e il nuovo valore viene trasmesso tramite CAN Bus utilizzando l'ID corrispondente.

Se nowPage è uguale a 2, oppure se il numero di pagina è 3, vengono eseguiti controlli analoghi sui Widget disponibili nella pagina 3, ad esempio CircleProgressValue, HorizontalSliderValue, GraphValue e AnimationValue. Se uno di questi valori è diverso dal rispettivo valore precedente, quest'ultimo viene aggiornato e il nuovo valore viene trasmesso tramite CAN Bus utilizzando l'ID corrispondente.

Se nowPage è uguale a 3, oppure se il numero di pagina è 4, vengono eseguiti controlli analoghi sui Widget disponibili nella pagina, ad esempio HorizontalSliderValue, GaugeValue, FixedWidgetValue, AnimationValue, IndicatorValue e ToggleButtonValue. Se uno di questi valori è diverso dal rispettivo valore precedente, quest'ultimo viene aggiornato e il nuovo valore viene trasmesso tramite CAN Bus utilizzando l'ID corrispondente.

Dopo il blocco di codice precedente all'interno della funzione loop(), viene richiamata la funzione handleTouchEvents() per gestire gli eventi touch.
Se il Knob si trova in stato Idle, ovvero se isKnobIdle() restituisce 1, la funzione setKnobState() imposta lo stato del Knob su KNOB_IDLE. Successivamente viene richiamata la funzione redrawScreen() per ridisegnare la schermata. Con questa parte si conclude il codice del progetto demo.

Dopo aver verificato e caricato correttamente il programma tramite Arduino IDE, la schermata dell'OLED Knob Display cambia: dall'immagine dello Smart Display passa all'immagine Touch o Rotate. A questo punto, sia lo Smart Display sia l'OLED Knob Display visualizzano la Home Page predefinita.

Ora è possibile eseguire gli scenari previsti dalla demo. Dalla Home Page, ruotando il Rotator è possibile selezionare una delle tre pagine disponibili: Industry, Medical e Vehicle, come mostrato nella figura seguente.

Dopo aver effettuato la selezione, con un breve tocco sull'OLED Knob Display lo Smart Display passa immediatamente alla pagina selezionata.

Vediamo nuovamente come eseguire lo scenario demo. Partendo dalla Home Page, ruotare il Rotator per selezionare una delle altre tre pagine. Nell'esempio seguente viene selezionata la pagina Medical.

Dopo aver selezionato la pagina Medical, è sufficiente toccare l'OLED Knob Display per accedere alla pagina Medical selezionata sullo Smart Display.

Ruotando il Rotator dell'OLED Knob Display verso sinistra o verso destra è possibile controllare i diversi Widget presenti nella pagina, come illustrato nella figura seguente.

Effettuando una pressione prolungata sull'OLED Knob Display, il sistema torna automaticamente alla Home Page, ovvero Page 1. Inoltre, se l'OLED Knob Display non viene né ruotato né toccato per 10 secondi, il display viene ripristinato e torna alla Home Page.

A questo punto, la configurazione della demo con Smart Display da 10,1" e OLED Knob Rotator Display è completata. La demo consente di controllare e passare da una pagina all'altra tramite pressioni brevi o prolungate sull'OLED Knob Rotator Display e di interagire con i diversi Widget disponibili in ciascuna pagina.
Per scaricare il programma e consultare ulteriori dettagli sul progetto demo, fare riferimento al repository GitHub: https://bit.ly/3oT0o5M.
La configurazione hardware effettiva è mostrata nella figura seguente.

Questo programma presenta un esempio pratico di utilizzo di una scheda Arduino con un OLED Knob Rotator Display e il protocollo Custom CAN ID per gestire diverse pagine e passare da una pagina all'altra tramite pressioni brevi o prolungate. Consente inoltre di interagire con i diversi Widget disponibili in ciascuna pagina e di controllarne le relative funzioni. Il programma rappresenta un utile riferimento sia per chi si avvicina per la prima volta a questo tipo di sviluppo sia per gli sviluppatori più esperti che desiderano approfondire la programmazione dell'host e le relative applicazioni nel controllo degli Smart Display, in particolare dei modelli privi di funzionalità touch.