• Non ci sono risultati.

Sviluppo di una stampante inkjet ottimizzata per la realizzazione pattern di fattori di crescita cellulare

N/A
N/A
Protected

Academic year: 2021

Condividi "Sviluppo di una stampante inkjet ottimizzata per la realizzazione pattern di fattori di crescita cellulare"

Copied!
49
0
0

Testo completo

(1)

Scuola di Ingegneria

Corso di laurea triennale in Ingegneria Biomedica

Tesi di Laurea

Sviluppo di una stampante inkjet

ottimizzata per la realizzazione pattern

di fattori di crescita cellulare

Relatore:

Candidato:

Prof. Giovanni Vozzi

Gabriele Rossi

Correlatori:

Dr. Ing. Carmelo De Maria

Dr. Ing. Daniele Pasciuto

(2)

Indice

Introduzione

3

1 Analisi dei componenti

4

1.1. Specifiche

. . . 4

1.2. InkShield

. . . 4

1.3. LCD KeyPad Shield . . . 7

1.4. Arduino Mega 2560 . . . 8

1.5. Servomotore HS-645mg . . . 9

1.6. Modulo DC-DC Step-Down

. . . 10

1.7. Alimentatore switching 12V-2A . . . 10

2 Messa in opera

11

2.1. Analisi della libreria Arduino

. . . 11

2.2. Prove preliminari

. . . 12

Ø

Prova 1

. . . 13

Ø

Prova 2

. . . 13

Ø

Prova 3

. . . 15

2.3. Sincronizzazione con il movimento di un servomotore

. . . . 15

2.4. Montaggio finale

. . . 16

3 Programmazione

21

3.1. Breve descrizione dell’interfaccia grafica

. . . 22

3.2. Funzioni

. . . 22

Ø

float distanza (float alfa_d){}

. . . 22

Ø

void stampa_img (int n_img, bool pat){}

. . . 24

Ø

void print_ul(){}

. . . 27

Ø

void program_mode(){}

. . . 29

4 Analisi qualitativa e quantitativa

32

4.1. Cenni sulla stampa termica InkJet . . . 32

4.2. Prove con inchiostro . . . 32

4.3. Prove con fattori di crescita . . . 38

5 Conclusioni

42

(3)

Introduzione

Il lavoro descritto in questo elaborato si colloca nell’ambito di un progetto più ampio denominato BOOST. L’obiettivo scientifico del progetto è quello di capire come il bilanciamento tra le attività delle due popolazioni cellulari che regolano l’omeostasi ossea, osteoblasti e osteoclasti, porti all’insorgere di stati patologici e studiare come la geometria e le caratteristiche dell’ambiente extracellulare possano influenzare queste attività. Per fare questo, è necessario ricreare in vitro modelli del tessuto la cui geometria di partenza sia simile a quella del tessuto in esame, sia esso sano o malato.

Il modo per creare strutture riproducibili con caratteristiche micrometriche e nanometriche specifiche è quello della stampa 3D multimateriale. Attraverso la tecnologia di stampa Extrusion Bioprinting verrà creata una struttura tridimensionale del tessuto da riprodurre, nel quale verranno inseriti in modo mirato fattori di crescita specifici. All’interno di questo processo di fabbricazione, la necessità di depositare in maniera localizzata i fattori ha spinto ad utilizzare il sistema di stampa a getto di inchiostro.

Con questo elaborato si descrive il procedimento seguito per realizzare un dispositivo di supporto, necessario a chi si occupa della parte biologica del progetto, per testare la stampabilità dei fattori di crescita in esame con la tecnologia di stampa inkjet. Tali test verranno realizzati presso il Dipartimento di Scienza Applicata e della Tecnologia del Politecnico di Torino.

La macchina realizzata è in grado di effettuare una deposizione controllata del fattore a concentrazioni note con varie funzionalità. Inoltre, parte dell’elaborato è dedicata alla descrizione dei risultati che è possibile ottenere con la macchina realizzata, evidenziandone limiti e potenzialità.

L’elaborato è diviso nelle seguenti sezioni:

- Analisi dei componenti: vengono descritti i componenti elettronici utilizzati, soffermandosi sulle caratteristiche di maggior interesse. Partendo da una serie di specifiche, ciascun componente è stato scelto in modo da soddisfare caratteristiche di funzionamento, costo e facilità di interfacciamento.

- Messa in opera: viene descritto il processo costruttivo seguito per realizzare il dispositivo, discutendo le problematiche sorte in fase di lavorazione e le soluzioni adottate.

- Programmazione: questa parte è dedicato alla descrizione del codice sviluppato per il controllo del dispositivo.

- Analisi qualitativa e quantitativa: vengono effettuate delle analisi delle stampe effettuate con il dispositivo realizzato. La prima parte è dedicata alla verifica dei dati di targa della cartuccia e alla misura del massimo DPI ottenibile, successivamente viene discussa la stampa di soluzioni di fattori di crescita a concentrazioni note.

- Conclusioni: con le informazioni ricavate nel capitolo precedente sono state tratte delle conclusioni circa la stampabilità delle soluzioni di fattori di crescita e le caratteristiche della stampa in termini di DPI e volume stampato. È stato inoltre discusso un possibile algoritmo da implementare attraverso un programma di elaborazione grafica per ottenere una codifica di stampa a partire da delle immagini microCT del tessuto da riprodurre.

(4)

1. Analisi dei componenti

Prima di iniziare a realizzare il dispositivo, ci si è soffermati su ciascun componente, in modo da comprendere meglio come essi si potessero interfacciare e come sfruttare al meglio le loro potenzialità per poter soddisfare le specifiche di progetto.

1.1. Specifiche

Le specifiche di progetto possono essere riassunte nei seguenti punti: - La necessità di un facile ed intuitivo utilizzo.

- Dimensioni ridotte, in modo da poter essere usata sotto cappa biologica.

- Deve poter essere realizzata tramite componenti facilmente reperibili e di costo contenuto, che siano anche assemblabili e interfacciabili tra loro senza grosse difficoltà.

- La macchina dovrà essere in grado di stampare pochi semplici immagini, pattern e interpretare alcuni comandi inviati tramite interfaccia seriale.

- Deve prevedere la possibilità da parte dell’utente di scegliere il DPI e la velocità con cui le immagini verranno stampate.

1.2. InkShield

Il sistema di stampa è lo stesso che poi verrà impiegato nell’ambito del progetto BOOST. L’elemento fondamentale in base al quale sono state prese anche le successive decisioni è una scheda InkShield, acquistata in kit dal sito dello sviluppatore e successivamente assemblata in laboratorio. I connettori per la connessione alla scheda Arduino sono passanti e presentano sulla parte superiore un corrispondente connettore femmina che permette così di poter impilare più schede, anche di tipo diverso, su di uno stesso Arduino.

Figura 1: Scheda InkShield

Sulla scheda è implementato un semplice circuito che utilizza solamente componenti a foro passante, in modo da rendere l’assemblaggio della stessa facile ed intuitivo. Per il controllo della cartuccia questa scheda utilizza solamente 5 pin dell’Arduino i quali possono essere scelti utilizzando opportuni jumper che si trovano sulla scheda.

(5)

Figura 2: Schema elettrico della InkShield

Individuiamo nel circuito tre blocchi circuitali fondamentali:

1) Il blocco nominato come “20V Boost converter” è basato sull’integrato MC34063A. Si tratta di un’alimentatore DC-DC Step-Up che si occupa di innalzare la tensione di alimentazione fino a 20V circa, tensione necessaria per pilotare gli ugelli della testina di stampa. L’autore ha testato la testina di stampa per diverse tensioni ed ha individuato questa come la migliore per ottenere una stampa affidabile. La tensione di ingresso a questa parte del circuito è direttamente fornita attraverso il connettore a vite P1.

2) Il blocco “Ink Driver” contiene la logica di controllo e di potenza direttamente collegata alla testina di stampa. È costituito da un integrato logico CD4067, un multiplexer a 16 canali inserito con lo scopo di ridurre drasticamente il numero di pin necessari per controllare i 12 ugelli. Infatti, in questo modo è possibile indirizzare i segnali a ciascun ugello singolarmente utilizzando solamente i 4 pin di indirizzamento del multiplexer. L’impulso per la stampa arriva attraverso il pin comune del multiplexer che allo stesso tempo attiva un led di colore giallo (chiamato PULSE, posizionato sulla scheda) per una verifica visiva immediata del funzionamento. Le uscite del multiplexer sono poi collegate agli ugelli attraverso due integrati ULN2803, i quali contengono degli array di transistor Darlington alimentati dall’uscita del blocco precedente, i quali funzionano da interfaccia. Le uscite del multiplexer sono infatti ad una tensione più bassa di quella necessaria e non sono in grado di erogare abbastanza corrente per garantire un corretto funzionamento della testina. Le uscite di quest’ultimi sono quindi collegate ad un connettore per il collegamento del flat della cartuccia (connettore P5) e ad un connettore supplementare (connettore P4) che permette di collegare la cartuccia attraverso un cavo supplementare a 14 vie. Nel blocco è presente anche un led verde (chiamato POWER) il quale si accende quando la logica viene alimentata.

(6)

3) Il blocco “Arduino Connector” mostra tutti collegamenti con l’Arduino che si va ad interfacciare con la InkShield e in che modo i segnali vengono prelevati e smistati. Tramite i connettori JP1, JP2, JP3 e JP4 è possibile scegliere una delle due configurazioni possibili relative ai pin di indirizzamento del multiplexer, mentre attraverso i connettori JP5 e JP6 è possibile scegliere il pin di uscita per l’impulso di stampa tra il pin 2 ed il 12 di Arduino.

Va evidenziata infine la presenza del connettore JP9, il quale non è presente in tutte le versioni della scheda. Attraverso di esso è possibile scegliere se alimentare Arduino attraverso la linea di alimentazione a 12V, che alimenta anche il blocco “20V Boost converter”, oppure scegliere un’alimentazione separata. Per come la scheda è stata pensata è possibile inoltre notare che, indipendentemente da come il connettore JP9 viene posizionato, il multiplexer che pilota gli array di Darlington viene comunque alimentato dalla tensione a 5V stabilizzata proveniente dall’Arduino.

Figura 5: Cartuccia HP C6602A, nella confezione originale

La cartuccia utilizzata, fornita con il kit, è una HP C6602A di colore nero e presenta una testina di stampa con 12 ugelli, i quali sfruttano un meccanismo termico per l’espulsione di piccole gocce di inchiostro.

Figura 4: Cavo a 14pin per prolungare il collegamento alla cartuccia. A destra, una la piccola scheda per collegare il

cavo al flat del supporto.

Figura 3: Vista superiore del supporto per la cartuccia. Si notano i contatti elettrici sulla parete interna.

(7)

Figura 6: A sinistra una fotografia ingrandita a 4x della testina di stampa, si notano i 12 forellini circolari dal quale l’inchiostro viene espulso. A destra una fotografia con ingrandimento 10x del dettaglio degli ugelli, la grigia sovrapposta all'immagine è costituita da quadratini di lato

10 micron.

Gli ugelli hanno diametro di circa 80µm e distano tra loro 260µm. Da specifiche, la distanza tra gli ugelli è tale da stampare con risoluzione di 96 DPI e ciascun ugello espelle gocce del volume di 160pl. La cartuccia si aggancia al supporto tramite un meccanismo a molla e presenta 13 pad per le connessioni elettriche, posizionati sulla stessa faccia della cartuccia su cui è posizionata la testina. Dal supporto parte un cavo flat per la connessione al driver, il quale presenta la possibilità di essere piegato e fissato in modo che fuoriesca da uno dei due lati o superiormente.

All’interno, la cartuccia contiene 18ml di inchiostro trattenuti in una spugna, che verrà successivamente rimossa in modo da caricare le soluzioni da studiare.

1.3. LCD KeyPad Shield

Per creare un dispositivo facile da utilizzare anche da utenti senza un particolare training, abbiamo deciso di implementare uno schermo LCD e un keypad di navigazione attraverso il quale l’utente può muoversi all’interno di un menù che verrà successivamente descritto. Per questo è stato scelto di

utilizzare la versione base dell’ LCD KeyPad Shield.

Su questa scheda è presente uno schermo LCD retroilluminato blu e alcuni componenti utili al suo utilizzo, come un potenziometro da 10Kohm per la regolazione del contrasto e un transistor posizionato sulla massa della retroilluminazione attraverso il quale è possibile controllare la luminosità dello schermo via software. Lo schermo richiede sette pin di controllo, quattro per l’invio dei comandi paralleli (da D4 a D7, due per le operazioni di scrittura e la temporizzazione (D8 e D9) e uno per il controllo PWM della luminosità (il D10).

Gli altri componenti interessanti di questa scheda sono cinque pulsanti appositamente posizionati con lo scopo di funzionare da interfaccia per la navigazione in un menù grafico. Ognuno dei pulsanti è identificato da una serigrafia: RIGHT, LEFT, UP, DOWN e SELECT. Lo stato dei cinque pulsanti può essere letto effettuando una lettura analogica sul pin A0, poiché essi sono collegati in modo da formare un diverso partitore resistivo in base al pulsante premuto.

(8)

Figura 7: Scheda LCD KeyPad Shield. Il potenziometro blu in alto a destra è stato successivamente dissaldato e riposizionato sul lato inferiore del PCB, per motivi di ingombro

Questa scheda deve essere l’ultima di un eventuale impilamento, poiché presenta solo connettori maschio sulla parte inferiore che permettono il collegamento con Arduino, ma nessun connettore femmina sulla parte superiore per collegare altre schede, poiché queste coprirebbero sia l’LCD che i pulsanti.

Figura 8: Schema elettrico dell'LCD KeyPad Shield

1.4. Arduino Mega 2560

Come scheda di controllo è stato scelto un Arduino MEGA 2560 che unisce un gran numero di pin di uscita all’ampia disponibilità di memoria e potenza di elaborazione più che sufficiente allo scopo. Il modello scelto è basato sul microcontrollore ATmega2560, lavora ad una velocità di clock di 16MHz, dispone di una memoria da 256KB e può essere interfacciato alle altre schede senza particolari accorgimenti in termine di livelli dei segnali ed ingombro.

(9)

Figura 9: Scheda Arduino utilizzata

Le schede Arduino sono ampiamente documentate e altrettanto conosciute ed utilizzate da gran parte della comunità scientifica.

1.5. Servomotore HS-645mg

Per poter muovere la cartuccia di stampa durante l’espulsione dell’inchiostro o della soluzione da stampare, è stato utilizzato un servomotore prodotto dalla Hitec, modello HS-645mg. Questo tipo di attuatore è stato scelto data la facilità nel controllo, la caratteristica di non necessitare di driver esterni oltre quello embedded, e la disponibilità immediata in quanto già presente in laboratorio.

Figura 10: Servomotore montato nella sua posizione finale

Da specifiche, questo modello di servomotore è in grado di compiere una rotazione di 180° con una velocità di 250°/s, ha una coppia compresa tra 7,7 Kg×cm e 9,6Kg×cm (a seconda della tensione di alimentazione) ed è dotato internamente di un riduttore a ruote dentate metalliche a denti dritti. Deve essere alimentato con una tensione compresa tra 4,8V e 6V e la corrente massima assorbita è di 450mA.

(10)

Il servomotore si interfaccia tramite un cavo che termina in un connettore a tre pin (positivo, negativo e dati). Attraverso la linea dati il controllo si svolge per mezzo di un segnale PWM di ampiezza compresa tra 3V e 5V. Il servomotore risponde ad un duty cycle compreso tra 900µs e 2100µs, ruotando in senso orario con un angolo massimo di 197°.

1.6. Modulo DC-DC Step-Down

La necessità di utilizzare questo modulo è sorta durante le prime fasi di test della macchina per motivi che verranno discussi nel paragrafo 3.4. Il modulo è basato sull’integrato LM2596 con il quale, insieme a pochissimi altri componenti passivi, è possibile realizzare un regolatore di tensione di tipo step-down. La tensione in uscita può essere facilmente regolata per mezzo di un trimmer che si trova sul modulo, per un ampio range di tensioni di ingresso e di uscita ed una dissipazione di potenza contenuta, grazie anche all’alta efficienza del circuito. La corrente massima erogabile da questo modulo è di 3A, nettamente superiore al necessario.

Figura 11: Modulo DC-DC Step-Down

Questi moduli sono diventati di uso comune sia in ambito scientifico che hobbistico. Questo perché offrono prestazioni eccellenti unite ad un bassissimo costo ed un ingombro sempre più contenuto.

1.7. Alimentatore switching 12V-2A con connettore 5,5mm

La macchina è stata pensata per richiedere un’alimentazione proveniente da un'unica fonte a 12V, che fosse facilmente trasportabile ed eventualmente sostituibile. Per questo si è potuto utilizzare un comune alimentatore switching, molto compatto e facilmente collegabile sia alla rete che all’utilizzatore per mezzo di un connettore cilindrico da 5,5mm. Il modello scelto è in grado di erogare una corrente massima di 2A, più che sufficiente ad alimentare tutti gli apparati del nostro dispositivo.

(11)

2. Messa in opera

Come già detto, l’obiettivo è creare un dispositivo in grado di stampare semplici immagini o pattern più o meno autonomamente, quindi, si è pensato fin da subito di montare la cartuccia su di una guida mobile e di guidarne il movimento per mezzo di un motore passo-passo o di un servomotore.

2.1. Analisi della libreria Arduino

La libreria per l’utilizzo della InkShield è facilmente scaricabile dal sito dello sviluppatore e implementa pochissime semplici funzioni. Anzitutto, la libreria viene fornita in tre versioni e vengono richiamate con il comando #include nell’IDE di Arduino:

#include <InkShield.h>

#include <InkShieldMega.h>

#include <InkShieleLite.h>

La prima versione della libreria è da richiamare nel caso in cui la scheda Arduino sia un Arduino 2009 o versioni simili, la seconda invece è da richiamare nel caso in cui si stia utilizzando un Arduino MEGA. La versione Lite della libreria è da utilizzare nel caso in cui ci si trovi ad includere la gestione della InkShield in un programma molto complesso e, magari, anche con poco spazio in memoria disponibile. Questa ultima versione include routine più semplici e protegge gli ugelli da eventuali danneggiamenti dovuti ad errori di programmazione.

Dopo aver richiamato la libreria è necessario inizializzare la shield utilizzando una funzione apposita la cui sintassi rispecchia la configurazione circuitale della shield stessa:

InkShieldA0A3 MyInkShield (int pulse_pin); InkShieldA2A5 MyInkShield (int pulse_pin);

Si utilizza in comando InkShieldA0A3 se la scheda è stata collegata in modo che i pin di indirizzamento del multiplexer siano gli analogici da A0 a A3, si utilizza invece InkShieldA2A5 se invece la scheda è configurata sui pin da A2 a A5. Con questo comando si crea un oggetto di nome arbitrario, in questo caso MyInkShield per praticità, a cui si associa un intero che andrà ad indicare il pin per la trasmissione dell’impulso di stampa.

Terminata la configurazione, l’unica funzione disponibile per l’utilizzo è la seguente: MyInkShield.spray_ink(int strip);

Questa funzione, legata al nome che è stato dato all’oggetto InkShield nella configurazione, prende in ingresso un intero. L’intero, a 16 bit, viene letto come una mappatura bit a bit degli ugelli in questo modo:

Bit b15 b14 b13 b12 b11 b10 b9 b8 b7 b6 b5 b4 b3 b2 b1 b0

Ugello / / / / 12 11 10 9 8 7 6 5 4 3 2 1

I quattro bit più significativi non sono attivi, mentre i bit dal b11 al b0 comandano direttamente ciascun ugello. Ad ogni richiamo della funzione viene eseguito un ciclo di stampa, pertanto la funzione si occupa automaticamente di indirizzare i dati attraverso il multiplexer e dare gli impulsi con la giusta temporizzazione. Se ad esempio viene richiamata in questo modo:

(12)

MyInkShield.spray_ink(0x0FFF); // 0x0FFF = 0b 0000 1111 1111 1111

Ciò che si ottiene è la stampa di una linea completa, dovuta all’espulsione da parte di ciascuno dei 12 ugelli di una goccia di inchiostro.

2.2. Prove preliminari

In un primo momento, la cartuccia è stata montata su di una guida a ricircolo di sfere, utilizzando una squadra di alluminio su cui sono stati realizzati dei fori per il fissaggio su un lato del supporto della cartuccia e sull’altro per il fissaggio alla guida. Questo primo assemblaggio è stato realizzato per effettuare dei test preliminari, soprattutto per testare il corretto funzionamento della scheda e l’usabilità della libreria.

Il supporto per la cartuccia presenta quattro fori ellittici per il passaggio di altrettante viti di fissaggio e presenta anche due spine in plastica che funzionano da guida per il fissaggio verticale. I quattro fori sono distanziati come in Figura 12, e per il fissaggio alla squadra di alluminio sono state utilizzate quattro viti a intaglio M2.5x8, particolarmente adatte in quanto con queste misure le viti non hanno richiesto di allargare i fori sul supporto e non sporgevano dal lato interno della squadra. Il fissaggio è avvenuto in modo che il perno di guida sporgesse verso il basso della squadra di alluminio, in modo da non doversi troppo dilungare sulla precisione del fissaggio, dato che questo primo montaggio è stato realizzato con il solo scopo di effettuare dei test preliminari.

Figura 12: A sinistra, supporto e cartuccia fissati alla guida a ricircolo di sfere. A destra, le misure di fori e sporgenze del supporto per la cartuccia, il disegno è stato realizzato con il CAD SketchUp

Una volta montata la cartuccia sulla guida, piegando il cavo piatto di collegamento verso sinistra, si è provveduto al collegamento con la InkShield e alla sua configurazione.

(13)

La InkShild è stata configurata in modo che Arduino venga alimentato tramite USB e la InkShield venga invece alimentata attraverso il morsetto P1, al quale viene collegato un alimentatore da banco regolato a 12V. Questo è possibile semplicemente lasciando scollegato il ponticello JP9. Il pin scelto per la trasmissione dell’impulso di stampa è stato il Pin 2 mentre gli indirizzi per il multiplexer vengono trasmessi attraverso i pin analogici A0-A3, cortocircuitando i ponticelli che sulla scheda sono indicati come ABCD posizionando il ponticello verso l’esterno.

Come microcontrollore è stato inizialmente scelto un Arduino 2009, su di esso sono stati caricati alcuni semplici programmi di prova che vengono qui discussi in dettaglio.

Ø Prova 1

Attraverso le prime istruzioni viene inclusa la libreria adeguata all’Arduino utilizzato e viene creato un oggetto InkShield con la giusta configurazione.

Nel Setup viene eseguita per 20 volte l’istruzione MyInkShield.spray_ink(), passando un valore espresso in esadecimale corrispondente al binario 0b0000111111111111. Nessuna istruzione eseguita nel loop.

Figura 13: Scansione delle stampe effettuate nella Prova 1

Questo semplice sketch fa stampare 20 linee ad ogni riavvio del software. È stato scelta l’esecuzione all’avvio poiché è essenziale sincronizzare la stampa con il movimento manuale della testina e non rende necessario disalimentare l’Arduino per fermare l’esecuzione del programma. Le linee che vengono stampate sono piene, poiché tutti e 12 gli ugelli vengono coinvolti. Il risultato di questa prima prova è quello in Figura 13.

(14)

Le prime due istruzioni sono le solite della prova precedente, poiché la configurazione non è stata cambiata.

Nel Setup() vengono eseguiti per 20 volte due cicli. Il primo ciclo esegue MyInkShield.spray_ink() per 12 volte, passando di volta in volta una variabile il cui valore è ottenuto shiftando un 1 verso destra ogni volta tante volte quante l’indice del ciclo. Ad ogni esecuzione è stato inserito anche un piccolo ritardo con lo scopo di rendere più facile la visualizzazione dei risultati.

Il secondo ciclo è equivalente al primo, con la differenza che il bit viene shiftato verso sinistra fino a raggiungere la posizione meno significativa.

Nessuna istruzione eseguita nel loop.

Questo programma permette di stampare una serie di zigzag. In questo caso, ad ogni riavvio del software, vengono stampati 20 zig-zag, realizzati mandando in stampa progressivamente un intero costruito in modo da attivare un solo ugello alla volta ciclicamente, dal primo all’ultimo e successivamente dall’ultimo al primo.

La Tabella 1 è esplicativa del funzionamento di questo semplice programma e mette in evidenza l’attivazione progressiva e ciclica di ciascun ugello. Nella figura sottostante vediamo lo stampato effettivo. Ugello 1 2 3 4 5 6 7 8 9 10 11 12 Pr im o ci cl o FO R 0x0001 • 0x0002 • 0x0004 • 0x0008 • 0x0010 • 0x0020 • 0x0040 • 0x0080 • 0x0100 • 0x0200 • 0x0400 • 0x0800 • Se co nd o ci cl o FO R 0x0800 • 0x0400 • 0x0200 • 0x0100 • 0x0080 • 0x0040 • 0x0020 • 0x0010 • 0x0008 • 0x0004 • 0x0002 • 0x0001 • Tabella 1

(15)

Figura 14: Scansione delle stampe effettuate nella Prova 2

Ø Prova 3

La terza prova è stata fatta utilizzando uno degli esempi inclusi nella libreria, in particolare sono state fatte delle modifiche all’esempio “SerialTextInkShield”. Lo sketch permetteva originariamente di inviare delle lettere via seriale, le quali venivano immediatamente stampate utilizzando una mappatura bit a bit di ciascuna lettera, già inclusa nell’esempio. Le modifiche apportate hanno permesso di realizzare un semplice programma che, ad ogni riavvio del sistema, stampava la stringa “MESSAGGIO DI PROVA” per poi interrompersi. Anche in questo caso tutte le istruzioni sono state incluse nel Setup(), per permettere appunto una sola esecuzione all’avvio. Il risultato è quello in Figura 15.

Figura 15: Scansione delle stampe effettuate nella Prova 3

Questo esempio è stato testato con lo scopo di prendere dimestichezza con la mappatura bit a bit delle immagini, soprattutto lettere, e del modo in cui queste vengono riprodotte dalla testina di stampa, cosa che sarebbe poi servita in seguito.

Da notare come le stampe realizzate negli esempi non hanno una spaziatura regolare. Questo perché, una volta riavviato il programma, la cartuccia è stata mossa spingendola manualmente lungo la guida, pertanto non è stato possibile mantenere una velocità costante ed ottenere quindi stampe uniformi.

2.3. Sincronizzazione con il movimento di un servomotore

Dopo le prove preliminari, è stata effettuata un'altra prova per verificare il corretto funzionamento della InkShield contemporaneamente al movimento di un servomotore. Il codice utilizzato è simile a quello della Prova 1, con la differenza che è stata inclusa anche la libreria Servo.h disponibile nell’IDE di Arduino per il controllo di un servomotore, che viene genericamente chiamato myservo. Il servomotore è stato collegato al Pin 3 dell’Arduino, pin che non viene coinvolto nel controllo della InkShield ed è

(16)

adatto a questo fine poiché permette un controllo PWM.

Figura 16: Panoramica dell'esperimento a questo punto del lavoro

In questo caso si è scelto di non limitare l’esecuzione del programma al solo Setup(), ma di scrivere una funzione da eseguire ad ogni pressione di un pulsante. Il pulsante è stato collegato al pin 4 di Arduino, il quale è stato configurato in modo da sfruttare la funzionalità di pull-up interno della porta, poiché ricalca in modo migliore le condizioni di esercizio finali della macchina.

I risultati sono stati positivi ed è stata confermata la possibilità di controllare sia la InkShield che il servomotore contemporaneamente e in modo indipendente senza particolari complicazioni.

2.4. Montaggio finale

Dopo queste prove, ci si è dedicati a realizzare la macchina vera e propria. Per poter muovere su di una guida la cartuccia per mezzo del servomotore è stato necessario utilizzare un meccanismo che potesse trasformare il movimento rotatorio di un servomotore in un movimento lineare abbastanza ampio, per questo si è pensato di utilizzare una meccanismo biella-manovella.

Come supporto per realizzare questo meccanismo è stato utilizzato un profilato di allumino a sezione quadrata di dimensioni 60x60x138mm. Questo profilato presenza su ognuno dei lati due fessure longitudinali in cui possono essere inseriti dei dadi appositi per il fissaggio di altre strutture. Su di un lato, quello scelto per ospitare il meccanismo, è stato praticato un foro di dimensioni adatte ad ospitare il servomotore, in modo tale da far sporgere l’asse verso l’esterno ed ospitare invece il corpo del servomotore all’interno. Il servomotore è stato quindi fissato al profilato praticando su di esso quattro fori, poi filettati M3, al quale sono state avvitate altrettante viti a brugola M3 lunghe 12mm, utilizzando anche quattro rondelle della stessa misura per un fissaggio più stabile e per non rovinare le parti in plastica dell’involucro del servomotore. Sul profilato è stata praticata anche una scanalatura in modo da far uscire i cavi di collegamento del servomotore dal lato del profilato adiacente a quello su cui verrà poi fissato il meccanismo.

(17)

Sulla stessa faccia del profilato dal quale sporge il servomotore è stata fissata una piastra in alluminio di dimensioni 88x59x5mm utilizzando due viti a brugola a testa svasata e due dadi appositi per il profilato. Su questa sono stati montati quattro supporti in plastica, stampati in 3D, utilizzando quattro viti a brugola M3, i quali fissano due assi lisci paralleli di diametro 4mm lunghi 88mm sul quale si muoverà la cartuccia.

Il supporto in plastica per la cartuccia è stato fissato ad un carrello realizzato mediante una piastra di alluminio, di dimensioni 37x54x4mm sul cui retro sono stati applicate due piccoli parallelepipedi di alluminio contenenti quattro boccole aventi funzione di cuscinetti di strisciamento. A differenza delle prove preliminari, oltre ai fori per le quattro viti di fissaggio del supporto, si è provveduto a realizzare con accuratezza due fori per l’alloggiamento delle spine di centraggio, in modo che la cartuccia risulti perpendicolare alle guide.

Infine, il carrello è stato collegato al servomotore per mezzo di un meccanismo di spinta biella-manovella. La manovella del meccanismo è stata realizzata utilizzando uno degli accessori di serie del servomotore, costituito da un braccio di alluminio che presenta ad un’estremità un apposito alloggiamento per l’albero del servomotore e alcuni fori, sempre più distanti dal centro di rotazione, per il collegamento di aste o simili. L’ultimo foro, quello più distante dall’asse di rotazione, è stato filettato M2,5 per il fissaggio ad una delle due estremità della biella.

La biella è stata invece realizzata utilizzando una barretta di alluminio, alle cui estremità sono stati praticati dei fori per l’alloggiamento di due cuscinetti a sfera e di alcuni spessori, in modo da ridurre significativamente l’attrito tra le parti meccaniche in movimento.

Figura 17: Nell'immagine è possibile vedere il dettaglio del meccanismo. In questa fase, il servomotore non era ancora stato alloggiato nel foro praticato nel profilato.

Dopo alcune prove per accertarsi del funzionamento del meccanismo e che non ci fossero parti sotto sforzo, il profilato è stato fissato ad una piastra in PVC nera di dimensioni 198x158x10mm, per mezzo di due viti a brugola e dei dadi appositi per il profilato. Sulla parte superiore è stata invece fissata l’elettronica di controllo, costituita da un impilamento di una scheda Arduino MEGA nella parte più

bassa, la InkShield al centro e LCD KeyPad Shield nella parte in alto. In un primo momento, il tutto è

stato fissato utilizzando un supporto in plastica apposito per l’Arduino MEGA. Il cavo piatto per il controllo della testina è stato fatto passare all’interno del profilato e le due aperture laterali dello stesso

(18)

sono state chiuse per mezzo di due tappi in plastica appositi.

A questo punto ci si è dedicati alla programmazione, che verrà discussa in dettaglio nel capitolo 4.

Figura 18: Qui vediamo l'elettronica completa. I collegamenti dell'alimentazione sono stati fatti in modo preliminare su una piccola BreadBoard

In corso d’opera, tuttavia, è sorto un problema riguardante il sistema di alimentazione. In un primo momento è stato pensato di alimentare tutta l’elettronica per mezzo del connettore a vite presente sulla InkShield utilizzando un alimentatore a 12V e ponendo in ingresso ad Arduino questa tensione tramite la Vin, cortocircuitando il ponticello JP9. In questo modo però, la tensione a 12V viene abbassata fino a 5V dal regolatore di tensione della scheda Arduino, il quale si occupa di fornire l’alimentazione stabilizzata, oltre che alle schede, anche al servomotore. Tutto questo provoca una richiesta, in termini di corrente, troppo elevata per il regolatore dell’Arduino, il quale raggiunge in breve tempo una temperatura eccessiva per il suo corretto funzionamento.

Questo problema è stato risolto utilizzando un regolatore di tensione esterno mediante l’uso di un modulo DC-DC StepDown basato sull’integrato LM2595. Questo modulo, facilmente reperibile, permette di ottenere una tensione in uscita regolabile, per mezzo di un potenziometro presente su di esso, ed un’erogazione di corrente fino a 3A, dissipando poca potenza in calore. Il modulo è stato regolato in modo che la tensione di uscita fosse di 5,1V ed è stato saldato su un pezzo di basetta millefori insieme ad alcuni connettori, in modo da poter effettuare i collegamenti con le parti da alimentare.

Mediante un connettore a due pin al modulo è stato collegato direttamente il servomotore, mentre l’Arduino e le altre schede sono state collegate mediante due connettori a due pin, opportunamente saldati sulla millefori, in modo da innestarsi sul connettore identificato dall’etichetta “DIGITAL” dell’Arduino. Infatti, i due pin che precedono i pin dell’Arduino 22 e 23 sono collegati all’alimentazione stabilizzata a 5V, mentre i due pin successivi al 52 e 53 sono collegati alla massa. Cosi facendo, la tensione proveniente dal modulo viene inserita a monte del regolatore interno dell’Arduino, svincolandolo quindi dall’alimentare il tutto.

Dalla schedina realizzata partono due fili direttamente collegati all’ingresso del modulo, di colore rosso per la Vcc e nero per la GND. Il modulo può prendere in ingresso una tensione fino a 35V ma, per semplicità, si è scelto di collegare questi due fili in parallelo ai due fili che portano l’alimentazione a 12V

(19)

al connettore a vite della InkShield.

Figura 19: Pinout del connettore "Digital" di Arduino Mega

Risolto questo problema è stato realizzato un case in plastica che contenesse tutte le parti elettroniche. Il case è stato progettato utilizzando il CAD 3D SolidWorks ed è stato realizzato mediante un sistema di stampa 3D in ABS di colore blu.

La parte superiore del case presenza diverse aperture, le quali hanno funzioni specifiche:

- Sulla parte superiore è presente un’apertura rettangolare nel quale si inserisce lo schermo LCD e alcuni fori per i pulsanti. Quattro di essi sono stati realizzati di forma triangolare, a ricordare una freccia, per suggerire in modo intuitivo la loro funzione, mentre un altro foro di forma circolare, corrisponde al tasto select. I pulsanti per la navigazione sono stati stampati in PLA di colore verde, mentre il tasto select è stato realizzato alle macchine utensili utilizzando una barra in PVC bianco. Tutti i pulsanti hanno un’altezza tale da sporgere di circa 1mm dal case, per agevolare la pressione del pulsante stesso. A destra di questi pulsanti è presente un altro foro, dove è inserito un piccolo cilindretto in plastica rosso, anch’esso realizzato alle macchine utensili. Utilizzando una penna o uno strumento appuntito è possibile premerlo in modo da spingere il pulsante sottostante, il quale provoca il reset del programma e il riavvio del sistema. - Sulla faccia destra è presente un foro di forma rettangolare nel quale è stato alloggiato un

interruttore che interrompe il circuito di alimentazione.

- Sulla faccia posteriore è presente un foro di forma circolare dove è stato inserito il connettore di alimentazione femmina da 5,5mm. Il connettore è tenuto in posizione da un dado e da una rondella in plastica, posizionati nella parte interna. La massa del connettore è direttamente collegata al negativo del connettore a vite della InkShield, mentre il positivo è collegato all’interruttore.

- Sulla faccia sinistra è presente un foro rettangolare attraverso il quale è possibile accedere alla presa USB dell’Arduino. Lo scopo di questo è quello di rendere possibile una successiva riprogrammazione dell’Arduino senza necessità di riaprire il case, insieme anche all’utilizzo di alcune funzioni specifiche che necessitano una connessione seriale.

Sulla parte superiore è stato scritto in rientranza il nome del progetto e il nome dato alla macchina, cioè “BioPrint”. Sulle parti laterali sono presenti anche quattro fori per il passaggio di altrettante viti a testa svasata che fissano la parte superiore del case a quella inferiore.

La parte inferiore del case è invece una semplice piastra che presenta:

- Due fori per il passaggio di due viti a testa svasata per il fissaggio del case al profilato.

- Quattro sporgenze dove è possibile incastrare quattro dadi M3 in cui si avvitano le viti per il fissaggio della parte superiore.

- Una fessura per il passaggio del cavo piatto per il controllo della testina e un foro per il passaggio dei cavi del servomotore.

- Un canale in corrispondenza dell’apertura nella parte superiore per l’accesso al connettore USB di Arduino, realizzato con lo scopo di guidare in connettore in posizione

(20)

Inizialmente, si era pensato di fissare le parti elettroniche alla parte superiore del case, sfruttando i fori di fissaggio presenti su tutte e tre le schede nel quale sarebbero passate delle viti M3 di lunghezza 35mm. Queste sarebbero state avvitate in dei fori realizzati sotto la parte superiore del case e filettati successivamente.

Tuttavia questo non è stato possibile, perché ci si è accordi dopo la stampa di aver utilizzato delle misure errate per posizionare i fori di fissaggio al top del case.

È stato deciso allora di fissare al case solamente la scheda superiore, la LCD KeyPad Shield, utilizzando delle viti autofilettanti per fare presa nella plastica senza necessità di creare un foro filettato, e di lasciare che le altre schede stessero in posizione grazie alla sola pressione dei connettori.

Figura 20: Case completo e assemblato

L’assemblaggio del case termina questa fase costruttiva, per tutte le misure dettagliate è possibile rifarsi al CAD della macchina.

(21)

3. Programmazione

Il programma per il controllo della macchina è stato sviluppato utilizzando l’IDE Arduino, direttamente scaricabile dal sito, e comprende circa 700 righe di codice. In questa parte verranno discusse solo alcune delle funzioni implementate, perché discutere l’intero programma appesantirebbe inutilmente l’elaborato e perché gran parte del codice si occupa di gestire l’interfaccia grafica ed è ampiamente commentato nel codice stesso.

(22)

3.1. Breve descrizione dell’interfaccia

In Figura 21 è possibile vedere uno schema a blocchi dell’interfaccia grafica nel quale ci si può muovere utilizzando i cinque pulsanti di navigazione

Nel primo menù è possibile muoversi tra quattro voci:

- Predefined Images, porta ad un secondo menù nel quale è possibile scegliere tra alcune immagini e opzioni di stampa predefinite, come la stampa di uno smile, di una lettere A maiuscola, del nome del progetto BOOST e la stampa di un certo volume di soluzione. Quest’ultima voce, quando selezionata, conduce ad una parte in cui l’utente può scegliere quanti microlitri di soluzione stampare.

- Pattern, stampa un pattern pieno, con caratteristiche che dipendono da alcune variabili selezionabili dalle opzioni.

- Program Mode, entra in una modalità nel quale il sistema si mette in ascolto dei comandi seriali. Quando questa modalità è attiva, è possibile inviare dei pacchetti di dati contenenti una posizione e un bitmap da stampare.

- Options, conduce ad un menù nel quale l’utente può modificare alcune variabili:

• Print Array, tramite il quale l’utente può scegliere gli ugelli attivare durante la stampa del pattern.

• Dpi, ovvero è possibile modificare la densità di punti per pollice con il quale stampare sia le immagini predefinite che il pattern.

• Velocity, che va regolare la velocità di stampa, in percentuale.

Da notare che prima di ogni evento di stampa è sempre richiesta una conferma, in modo tale da non avviare per sbaglio la stampa e risparmiare cosi materiale.

3.2. Funzioni

In seguito vengono descritte alcune delle funzioni implementate, scelte secondo un criterio di complessità ed importanza.

Ø float distanza (float alfa_d){}

Questa prima funzione prende in ingresso un numero corrispondente ad un angolo espresso in gradi e restituisce un numero corrispondente ad una distanza in millimetri. Tutti i dati sono di tipo float, poiché alcune delle funzioni utilizzate prendono in ingresso solo numeri in virgola mobile, ed anche per una questione di precisione.

Lo scopo di questa funzione è quello di dare informazioni circa la posizione del carrello rispetto alla sua posizione iniziale, dando in ingresso un angolo in gradi che esprime la rotazione del servomotore rispetto alla condizione iniziale, in cui questo è posizionato in modo tale da porre la manovella verticalmente.

Inizialmente la funzione definisce alcune costanti che esprimono le dimensioni del meccanismo, dove alla prima si da la lunghezza della biella, alla seconda la lunghezza della manovella e alla terza la differenza di altezza tra l’asse del carrello e quella dell’asse del servomotore. Le misure sono espresse in millimetri e sono state misurate per mezzo di un calibro (risoluzione = 0,05mm).

const float l_biella = 41.00; const float l_manovella = 18.75; const float h = 10.00;

(23)

Dopo di che, l’angolo che è stato messo in ingresso alla funzione viene convertito da gradi a radianti, secondo l’equivalenza:

𝛼"#$%#&'% = 𝛼)"#$%∙ +

,-.°

La conversione è necessaria perché le funzioni di seno e coseno utilizzate successivamente necessitano in ingresso di un angolo espresso in radianti. Viene definita poi un’altra costante adimensionale, chiamata lambda, che esprime il rapporto tra la lunghezza della manovella e quella della biella del meccanismo.

Infine la funzione restituisce il risultato della seguente equazione: 𝑑 = 𝑙2#&34566# sin 𝛼"#$− 1 𝜆 1 − 𝜆 ∙ cos 𝛼"#$− ℎ 𝑙@%566# A

che permette di calcolare la distanza del carrello dalla posizione iniziale in funzione dell’angolo che la manovella forma con la verticale. Questa funzione viene utilizzata spesso, poiché ogni volta che si richiede la stampa è necessario conoscere

la posizione del

carrello ad ogni passo, in modo da mantenere un DPI costante anche se il movimento del carrello non ha andamento lineare. Di seguito è riportato il procedimento per ricavare la formula utilizzata.

𝐴𝐵 = 𝑙 𝑂𝐵 = 𝑟 𝑂𝐶 = 𝐴_𝑥 𝑂𝐷 = ℎ

x) 𝐴_𝑥 = 𝑙 ∙ cos 𝜋 − 𝛾 + 𝑟 ∙ cos(𝜑) à 𝐴_𝑥 = −𝑙 ∙ cos 𝛾 + 𝑟 ∙ cos(𝜑)

y) ℎ + 𝑙 ∙ sen 𝜋 − 𝛾 = r ∙ sen(𝜑) à ℎ + 𝑙 ∙ sen 𝛾 = r ∙ sen(𝜑) à sen 𝛾 ="

6∙ sen(𝜑) − S

6

conoscendo l’espressione di 𝑠𝑒𝑛 𝛾 è possibile ricavare cos (𝛾) come:

cos 𝛾 = 1 − 𝑟

𝑙sin 𝜑 − ℎ

𝑙

A

float alfa_rad = alfa_d * (3.14/180); float lambda = l_manovella/l_biella;

return l_manovella * ( sin (alfa_rad) - (1/lambda)*sqrt( 1 - pow( lambda * cos(alfa_rad) - h/l_biella , 2) ) );

a

j g

y

(24)

Sostituendo nella prima equazione questa espressione e chiamando "6 come l otteniamo 𝐴_𝑥 = 𝑟 ∙ cos(𝜑) − 𝑙 ∙ 1 − 𝑟 𝑙sin 𝜑 − ℎ 𝑙 A = 𝑟 ∙ cos(𝜑) − 1 𝜆 1 − 𝜆 ∙ sin 𝜑 − ℎ 𝑙 A

Tuttavia, vogliamo che la distanza Sb sia espressa in funzione dell’angolo a. Facciamo quindi delle

sostituzioni e, sfruttando gli angoli associati, otteniamo l’equazione ricercata.

𝐴_𝑥 = 𝑟 ∙ cos(𝜋 2− 𝛼) − 1 𝜆 1 − 𝜆 ∙ sin 𝜋 2− 𝛼 − ℎ 𝑙 A = 𝑟 ∙ sin(𝛼) − 1 𝜆 1 − 𝜆 ∙ cos 𝛼 − ℎ 𝑙 A

Ø void stampa_img(int n_img, bool pat){}

Questa funzione viene richiamata quando si richiede la stampa attraverso ognuna delle voci del menù “Predefined images” e “Pattern”, tranne che per “Print ul”.

La funzione prende in ingresso una variabile intera e una variabile booleana. La variabile booleana assume il valore vero quando viene richiesta la stampa del pattern, mentre la variabile intera può assumere:

- 0, se si richiede la stampa di uno smile; - 1, se si richiede la stampa della lettera “A”; - 2, se si richiede la stampa della stringa “BOOST”;

Nella prima parte della funzione, che non è stata inclusa per motivi di spazio, è presente una mappatura bit a bit delle tre immagini predefinite che possono essere stampate. Esse sono semplicemente array di interi a 16 bit, in un numero variabile a seconda dell’immagine, parzialmente codificati in esadecimale per rendere la scrittura più compatta.

Il servomotore viene controllato facendo uso

della funzione

myservo.writeMicroseconds(int). La scelta di utilizzare questa funzione deriva dal fatto che il servomotore può assumere delle posizioni quantificate. Utilizzando la funzione myservo.write(int) avremmo avuto a disposizione solamente 180 posizioni, cioè sarebbe stato possibile far muovere il servomotore di un solo grado alla volta senza potersi fermare in posizioni intermedie. Utilizzando invece questa funzione è possibile suddividere la rotazione di 180 in una scala di estremi 700-2300, estendendo il numero di posizioni raggiungibili fino a 1600. Poiché gli estremi di questa scala possono cambiare a seconda del servomotore utilizzato, il loro valore è stato assegnato ad una variabile definita nella parte iniziale del programma, in particolare, l’estremo superiore viene memorizzato nella variabile corsa_max, mentre l’estremo inferiore nella variabile corsa_min.

Tali variabili non assumono effettivamente i valori estremi dell’intervallo, ma sono state calibrate in modo da assumere i valori della scala corrispondenti al posizionamento verticale della manovella, per

int smile[12] = { …. };

int letter_A[12] = { …. };

int boost[58] = { …. };

float passo = 60.00 / (corsa_max - corsa_min); float dis_dots = 25.40 / dpi_p;

(25)

corsa_max, e al posizionamento tale per cui la biella si trova in posizione orizzontale, per corsa_min. Il loro valore è stato determinato per tentativi e non deve essere ricalibrato all’accezione, in quanto il servomotore associa ad un determinato duty cycle del controllo PWM una ed una sola posizione. L’ampiezza della rotazione del servomotore utile al movimento è di circa 60°, per questo viene definito un passo, cioè l’angolo viene diviso per il numero di posizioni intermedie possibili con la scala utilizzata. Questo viene fatto perché è necessario avere una scala in gradi per il calcolo della posizione assoluta per mezzo della funzione descritta precedentemente.

Successivamente, la variabile dis_dots assume un valore corrispondente alla distanza tra i punti da stampare, ottenuto dividendo una quantità espressa in millimetri corrispondente ad un pollice per il numero di punti che devono essere stampati in tale ampiezza. Il valore di questa variabile dipende quindi dal DPI impostato nelle opzioni.

Con le successive due righe viene costruito un intero utilizzando i valori dell’array di variabili booleane print_array[]. Nella voce apposita del menù opzioni è possibile scegliere quale degli ugelli attivare durante la stampa del pattern e, in queste due righe, viene costruito un intero da utilizzare durante la stampa sulla base degli ugelli attivati. Il numero a 16 bit viene costruito ponendo a 1 il bit corrispondente all’ugello nel caso la variabile booleana riferita ad esso sia vera, zero se invece è falsa.

Vengono definite due variabili, un ritardo minimo e uno massimo. Benché la velocità di movimento del servomotore non sia in alcun modo controllabile, perché è possibile controllarne solo la posizione e non la velocità di movimento, una modulazione simile viene effettuata semplicemente aumentando o diminuendo l’intervallo di tempo tra ogni cambio di posizione. La variabile vel assume quindi un valore, compreso tra il minimo e il massimo, scelto sulla base della velocità percentuale impostata nelle opzioni per mezzo della funzione map.

Viene definita una posizione di partenza e una di arrivo. Quella di partenza è inizializzata con il valore della funzione distanza calcolata sull’angolo iniziale, cioè l’angolo nullo, quella di arrivo viene inizializzata con zero.

Viene scritto sul display un messaggio. Questo viene fatto poiché altrimenti il display non mostrerebbe alcun messaggio per tutto il tempo in cui la macchina è impegnata nella stampa, dando cosi l’impressione di un blocco per un tempo che può essere anche di diversi secondi. Il messaggio rassicura quindi l’utente sul corretto funzionamento.

Viene inizializzato un contatore che verrà utilizzato per tenere traccia della colonna da mandare in stampa. Come detto in precedenza, le immagini sono memorizzate come array di interi, questo contatore serve per tenere il conto di quale degli interi viene utilizzato ad un certo passo.

La variabile start_print invece, viene utilizzata come punto di partenza per la stampa. Da alcune prove è stato evidenziato che il servomotore mostra un comportamento irregolare durante i primi passi,

long int spray = 0x0000;

for (int i = 0; i < 12; i++) spray = spray | (print_array[i] << i)

int delay_max = 100; int delay_min = 10; int vel = map (velocity_p, 0, 100, delay_max, delay_min);

float corsa_start = distanza (0); float corsa_arrive = 0;

lcd.setCursor(0,0); lcd.print("Printing...");

int colonna = 0; float start_print = distanza (passo*30);

(26)

probabilmente a causa dei giochi tra le parti meccaniche. Utilizzando questa variabile, a cui è stato dato arbitrariamente il valore di distanza corrispondente a quello dopo 30 passi, la stampa comincia superata la zona di comportamento instabile, permettendo di ottenere così immagini costituite di punti equi spaziati anche nella parte iniziale.

Questa è l’unica parte in cui vengono azionati il servomotore e la InkShield.

Dalle condizioni imposte nel ciclo, è possibile vedere che questo viene ripetuto per il numero di posizioni possibili tra il minimo e il massimo della scala utilizzata per il controllo del servomotore.

Ad ogni iterazione, la posizione del servomotore viene decrementata di un’unità, fino a raggiungere il valore di corsa_min. Con questo procedimento, il servomotore compie un angolo di circa 60 gradi, suddiviso in corsa_max-corsa_min posizioni differenti.

Il delay inserito ha durata vel millisecondi, e corrisponde al tempo in cui ciascuna posizione viene mantenuta. Questo sistema è ciò che più si può avvicinare ad un controllo di velocità del movimento del servomotore, poiché il servomotore impiega un tempo sempre uguale e non controllabile per muoversi da una posizione ad un'altra.

Alla variabile corsa_arrive viene assegnato il valore della posizione raggiunta dal servomotore, quindi, si controlla se la posizione raggiunta dista dalla posizione salvata in corsa_start di una quantità maggiore o uguale alla distanza che deve intercorrere tra i punti per rispettare il DPI impostato. Se questo avviene, allora è necessario stampare un nuovo punto, quindi vengono eseguite delle istruzioni di stampa e successivamente si salva in corsa_start la posizione del punto appena stampato. La condizione i >= 30 è stata inserita per i motivi di irregolarità di funzionamento iniziale del servomotore descritti in precedenza.

Le istruzioni di stampa sono semplicemente costituite da quattro MyInkShield.spray_ink(), una per ciascuna immagine da stampare, che si attivano o disattivano in base ai valori delle variabili date in ingresso alla funzione generale, in modo da considerare il bitmap adeguato.

Da notare infine la presenza di un’altra struttura if all’interno di quest’ultima parte. Questa interviene nel caso in cui non si stia stampando il pattern e serve per decidere quando è il momento di passare all’intero successivo tra quelli che mappano l’immagine (incrementando l’indice colonna), oltre a interrompere la stampa quando viene raggiunta la fine dell’array.

for ( float i = 0.0; i < (corsa_max - corsa_min); i++){

myservo.writeMicroseconds(corsa_max - i); delay(vel);

corsa_arrive = distanza (passo*i);

if ( corsa_arrive - corsa_start >= (dis_dots - calibrazione) && i >= 30){

if(corsa_arrive - start_print >= 0.210 && !pat){ start_print = corsa_arrive;

colonna++;

if (colonna == 13 && n_img != 2) break; if (colonna == 59 && n_img == 2) break; }

if (n_img == 0 && !pat) MyInkShield.spray_ink(smile[colonna]); if (n_img == 1 && !pat) MyInkShield.spray_ink(letter_A[colonna]); if (n_img == 2 && !pat) MyInkShield.spray_ink(boost[colonna]); if (pat) MyInkShield.spray_ink(spray);

corsa_start = corsa_arrive; delay(vel);

} }

(27)

Figura 22: Rappresentazione del BitMap utilizzato per la lettera "A" e per lo Smile

La condizione della struttura è pensata in modo da mantenere le proporzioni dell’immagine al variare del DPI. Il DPI verticale è costante e, da specifiche, è tale da spaziare i punti di 210µm. Le immagini sono state mappate in modo da essere costituite da pixel quadrati, pertanto l’intero considerato non viene cambiato (in pratica, non si passa al pixel successivo) fin quando orizzontalmente il pixel non ha raggiunto la dimensione di 210µm. Se non fosse stata inserita questa parte, le immagini apparirebbero sempre più schiacciate orizzontalmente all’aumentare del DPI.

Ø void print_ul(){}

La maggior parte del codice è dedicato alla gestione dell’interfaccia grafica che consiste in una schermata dove viene richiesto di indicare la quantità, in picolitri, da stampare. Questa quantità, aumenta o diminuisce di 160pl alla volta, con un minimo che è proprio 160pl.

Dalle specifiche della cartuccia

sappiamo che ciascun ugello eietta ogni volta una goccia che ha volume medio di 160pl, pertanto, non è possibile stampare volumi minori, mentre volumi maggiori possono essere solo multipli di questa quantità.

La parte di codice dedicata

effettivamente alla stampa è quella che viene eseguita nel caso in cui la variabile button_read assuma il valore 5, cioè quando viene premuto il tasto Select. In questo caso viene richiesta una conferma di avvio stampa e se la funzione print_event() restituisce true, il codice viene eseguito.

Alla fine dell’esecuzione, il servomotore torna alla posizione iniziale.

reprint = true; bool ret = false;

while (true){ if (reprint){

lcd.setCursor(3,0); lcd.print("How many?"); lcd.setCursor(3,1); lcd.print("-"); if(ul_quantity < 10) lcd.setCursor(7,1); if(ul_quantity >= 10) lcd.setCursor(6,1); if(ul_quantity == 100) lcd.setCursor(5,1);

lcd.print(ul_quantity); lcd.print("pl"); lcd.setCursor(11,1); lcd.print("+"); } reprint = read_LCD_buttons(); switch (button_read){ case 1: ul_quantity += 160; break; case 4: if (ul_quantity > 160 ) ul_quantity -= 160; break; case 5: if (print_event()) { ---> Codice operativo <--- }

myservo.writeMicroseconds(corsa_max); lcd.clear();

return; } } }

(28)

Nelle prime tre righe di questa parte, oltre a stampare sul display il solito messaggio “Printing...”, il servomotore viene fatto posizionare a metà dalla sua corsa. Il ritardo di mezzo secondo è inserito per dare il tempo al servomotore di posizionarsi e stabilizzarsi.

Vengono create tre variabili intere. La prima prende inizialmente valore zero, codificato in esadecimale.

Nella variabile ul_quantity è contenuta la quantità in picolitri da stampare. È stato scelto di stampare il volume in questo modo:

- viene stampata una goccia per ugello, fino a raggiungere i 1920pl, cioè una goccia per ciascuno dei 12 ugelli.

- Per valori superiori, le gocce vengono sovrapposte a quelle già stampate.

È quindi necessario sapere il numero di gocce corrispondenti al volume memorizzato nella variabile ul_quantity e questo viene fatto semplicemente dividendola per 160. Trovato il numero di gocce, si cerca quante volte è necessario stampare una riga intera, cioè quante volte è necessario far stampare una goccia a ciascuno dei 12 ugelli. Ciò si ottiene effettuando una divisione intera per 12 del numero di gocce, e il numero di righe intere da stampare è memorizzato nella variabile total.

Sottraendo il volume stampato tramite righe intere da ul_quantity e dividendo per 160, si ottiene invece il numero di gocce rimanenti per raggiungere il volume richiesto, memorizzato in parzial.

Es:

> ul_quantity = 4480;

4480 / 160 = 28 gocce

28 / 12 = 2 righe intere

( 4480 – 2*160*12 ) / 160 = 4 gocce singole

Figura 23: Rappresentazione della stampa descritta nell'esempio

12 Tripla passata Doppia passata 1 Ug el li

lcd.setCursor(0,0); lcd.print("Printing...");

myservo.writeMicroseconds( corsa_max - (corsa_max - corsa_min) / 2 ); delay(500);

int spray = 0x0000;

int total = (ul_quantity / 160 ) / 12;

(29)

In questo caso, verranno stampate due righe complete sovrapposte e quattro gocce singole, stampate rispettivamente dai primi quattro ugelli sulle due righe precedentemente stampate.

Alla variabile spray viene assegnato un valore costruito in modo tale da presentare in binario tanti uni quante sono le gocce singole da stampare, partendo dalla cifra meno significativa e andando a scorrere ad ogni passo dell’iterazione

verso quella più significativa. Quindi, spray viene data in ingresso alla funzioneMyInkShield.spray_ink(),

che stampa in questo modo il numero calcolato di gocce singole.

Infine, vengono stampate le linee intere di gocce, il cui numero è memorizzato nella variabile total, in modo da raggiungere il volume di stampa desiderato. Un ulteriore piccolo ritardo è inserito per permettere il corretto svolgimento delle operazioni di stampa.

Ø void program_mode(){

Questa funzione permette un controllo della stampa per via seriale. Quando viene attivata, il sistema si mette in una condizione di “ascolto”, ovvero, si predispone a ricevere comandi seriali opportunamente codificati in modo da controllare la posizione e l’array di stampa da utilizzare una volta raggiunta la posizione stessa.

Tramite seriale vengono letti di volta in volta due valori, un valore di tipo float corrispondente alla posizione da raggiungere, e un intero da utilizzare come codifica dell’array di stampa da utilizzare una volta raggiunta la posizione desiderata.

Le prime righe stampano su seriale dei messaggi che comunicano all’utente l’ingresso nella modalità Program Mode e le variabili che possono essere comunicate, specificando l’ordine, il tipo di variabile e i valori ammissibili.

Inizialmente, vengono definite delle variabili. Si definisce un passo allo stesso modo della funzione stampa_img(), mentre le altre tre variabili vengono inizializzate con la posizione iniziale, rispettivamente espressa come distanza in millimetri, scala PWM utilizzata e gradi. Il dalay iniziale è inserito con funzione di anti rimbalzo.

for( int i = 0; i < total; i++) MyInkShield.spray_ink( 0x0FFF ); delay(500);

for ( int i = 0; i < parzial; i++) spray = spray | ( 1 < i); MyInkShield.spray_ink( spray );

delay(500);

float passo = 60.00 / (corsa_max - corsa_min); float actual_position = distanza (0);

float actual_position_servo = corsa_max; float actual_position_servoGrad = 0; Serial.println("Welcome to Program Mode!" ); Serial.println();

Serial.println( "Communicate two variables in the following order:" ); Serial.println( "> float type, corresponding to the position to be assumed" ); Serial.println( " on the cartridge (permissible values 0.0 <= X <= 17.0);" ); Serial.println( "> integer type, corresponding to the print array encoding" ); Serial.println( " (permissible values between 0x0000 and 0b0FFF);" );

(30)

Si entra in un loop infinito, che permette cosi di mettersi in “ascolto” dei comandi seriali fino all’uscita da questa modalità.

Dalla modalità si esce premendo il tasto Select della tastiera, pertanto all’inizio di questa parte viene letto lo stato dei pulsanti. Se il valore analogico letto corrisponde alla pressione del tasto, allora l’iterazione viene interrotta e tramite il comando return si torna al menu precedente. Da notare che, a differenza del resto del codice, per leggere lo stato dei pulsanti non è stata utilizzata la funzione read_LCD_buttons() ma una lettura analogica diretta. Il motivo è che all’interno della funzione read_LCD_buttons() è presente un altro loop infinito (cioè una struttura del tipo while(true)) dal quale si esce solo al momento della pressione di un pulsante. Questo non avrebbe permesso l’esecuzione di altro codice fino alla pressione di un pulsante, cosa che in altri casi torna utile ma in questo caso la rende inadeguata. La lettura analogica diretta permette di poter eseguire codice e allo stesso tempo leggere lo stato dei pulsanti.

Vengono inizializzate delle variabili da utilizzare per la lettura seriale. Una variabile di tipo float che conterrà la nuova posizione da raggiungere letta dalla seriale, una variabile di tipo intero nel quale verrà salvato l’array di stampa e una variabile booleana che prenderà il valore true nel momento in cui nuovi dati verranno ricevuti dalla seriale.

I dati vengono letti dalla seriale quando la funzione Serial.available() assume un valore diverso da zero. Questo accade quando nel buffer seriale sono presenti dati che ancora non sono stati letti e, per come è strutturata questa parte, si assume che i dati comunicati siano sempre trasmessi nel giusto formato.

Viene eseguito un controllo sui due valori ricevuti per verificare se questi hanno valori ammissibili. La cartuccia ha una corsa di poco superiore ai 17mm, la posizione massima non deve superare questo valore. Allo stesso modo, l’array di stampa deve contenere al più i primi 12 bit uguali a uno.

Nel caso in cui uno dei due valori comunicati non sia ammissibile, il sistema stampa su seriale un messaggio di errore e la variabile recived torna falsa, in modo che la parte di programma successiva non si attivi con i dati ricevuti.

while(true){

int return_read = analogRead(button);

---> Codice operativo <---

if (return_read > 555 && return_read < 790){ lcd.clear(); delay(100); return; } } float position_recived = 0.0; int spray = 0x0000;

bool recived = false; if (Serial.available()){ position_recived = Serial.read(); spray = Serial.read(); recived = true; } if ( position_recived > 17.00 || position_recived < 0){ recived = false;

Serial.println(“The inserted position is not permissible”); }

if ( spray > 0x0FFF){ recived = false;

Serial.println(“The Print Array is invalid”); }

(31)

Quando dei dati vengono ricevuti e sono ammissibili si arriva a questo punto del programma e la variabile recived è vera. Le due strutture if che si trovano, servono entrambe a muovere il servomotore in posizione: la prima si attiva nel caso in cui la posizione attuale sia minore di quella da raggiungere (cioè, guardando frontalmente la macchina, se la cartuccia si trova più a destra rispetto alla posizione da raggiungere) mentre la seconda si attiva nel caso in cui la posizione attuale sia maggiore di quella da raggiungere (cioè se la cartuccia si trova più a sinistra rispetto alla posizione da raggiungere).

All’interno, si trovano due strutture while, che eseguono la parte di codice relativa fin quando la posizione attuale non diventa maggiore o uguale, nel primo caso, o minore o uguale, nel secondo, alla posizione ricevuta. Questo viene fatto un passo alla volta, andando a calcolare ad ogni iterazione la posizione attuale facendo uso delle variabili definite all’inizio della funzione. Quando la condizione non è più verificata, la posizione si può considerare raggiunta, pertanto, il ciclo while si interrompe, si attende 200 millisecondi per dare modo al servomotore di stabilizzarsi e quindi viene stampato l’array di stampa ricevuto.

Come già detto all’inizio del paragrafo, il resto del codice non è stato descritto in questa relazione, ma è ampiamente descritto nel codice stesso attraverso l’inserimento di numerosi commenti. Esso è quasi esclusivamente dedicato all’interfaccia grafica e alla gestione di menù, puntatori e periferiche di ingresso.

if (recived){

if (actual_position < position_recived){ while ( actual_position < position_recived){ actual_position_servo--;

myservo.writeMicroseconds(actual_position_servo); actual_position_servoGrad += passo;

actual_position = distanza (actual_position_servoGrad);

} }

if (actual_position > position_recived){ while(actual_position > position_recived){ actual_position_servo++;

myservo.writeMicroseconds(actual_position_servo); actual_position_servoGrad -= passo;

actual_position = distanza (actual_position_servoGrad); }

} delay(200);

MyInkShield.spray_ink(spray); }

Riferimenti

Documenti correlati

• Nel caso b) dire qual’è la reciproca posizione delle rette al variare di λ∈R.. Fasci di piani

Il principale impegno è stato il coordinamento delle attività finalizzate a bandire una gara europea per l’affidamento dei servizi di sviluppo e di gestione del

- Giugno 2015: Società italiana di diitto internazionale, XX° convegno annuale, L'incidenza del diritto non scritto sul diritto internazionale ed europeo, con una relazione dal

Psicologia delle Comunicazioni Sociali (M-PSI/05 – 6CFU), per il Corso di Laurea Magistrale in Psicologia (LM51) dell'Università di Chieti-Pescara “G.. D'Annunzio”- 7

datore di lavoro Confartigianato Imprese Pesaro e Urbino, Strada Statale Adriatica 35, 61100, Pesaro, PU in partnership con la Facoltà di Economia dell’Università di

“Optimal Bayesian sequential sampling rules for the economic evaluation of health technologies”, forthcoming in Journal of the Royal Statistical Society – Series A, vol.. 177 (2),

2021-2022, Titolare dell’insegnamento Storia moderna, Laurea triennale in Beni Culturali, Università di Torino (6 cfu, 36 ore); Titolare dell’insegnamento Storia

Protective Role of the Association of Serenoa repens, Lycopene and Selenium from the Randomized Clinical Study. 18) Arcoraci V, Atteritano M, Squadrito F, D'Anna R, Marini H,