CtrlJ pen sketch ti mostra come sia possibile realizzare e personalizzare l’algoritmo o firmware del progetto.
Per capire bene lo sketch è bene che tu abbia letto e compreso i precedenti articoli dedicati al progetto stesso:
- CtrlJ pen – penna automatica controllo fluidi
- CtrlJ pen elettronica
- CtrlJ pen montaggio eletronica
dividerò questo articolo in parti per rendere più semplice la comprensione.
In particolare nel primo articolo hai letto delle
Funzioni principali:
- controllo del flusso di uscita;
- controllo della retroazione;
- reset della corsa;
- variazione dei parametri di configurazione.
e delle
Funzioni accessorie:
- visualizzazione grafica dello stantuffo;
- menu delle impostazioni;
- memorizzazione delle impostazioni.
e saranno quelle che andrai a definire nello sketch arduino.
Come sai la presenza di un display grafico OLED 0.91″ 128×32 semplifica la visualizzazione delle informazioni e delle impostazioni:
e ti aiuta anche per fornire istruzioni a chi utilizza il progetto sia in fase di configurazione che di utilizzo.
Video relativo al menu e le impostazioni
guardando il video vedrai come si alternano le schermate di configurazione del menu e ti sarà più facile comprendere lo sketch:
per entrare nella modalità di configurazione dovrai tenere premuto per 2 secondi il terzo tasto dalla penna.
In un prossimo articolo vedremo i settaggi passo-passo della CtrlJ pen sketch.
Passiamo adesso allo sketch.
CtrlJ pen sketch
CtrlJ pen sketch è il firmware che dovrai caricare sul micro pro beetle per controllare la penna e l’erogazione dei fluidi:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | #include <Arduino.h> #include <U8g2lib.h> #ifdef U8X8_HAVE_HW_SPI #include <SPI.h> #endif #ifdef U8X8_HAVE_HW_I2C #include <Wire.h> #endif #define Bt1 MISO //11 #define Bt2 MOSI //12 #define Bt3 SCK //13 #define A 14 #define B 9 #define C 10 #define D 11 int TOTAL_OF_STEPS_PER_REV=512; int NUMBER_OF_STEPS_PER_REV=512; int NUMBER_OF_STEPS_PER_PRESS=256; int MICROSECOND_STEP_DELAY=1000; int OLD_MICROSECOND_STEP_DELAY=MICROSECOND_STEP_DELAY; int TOTAL_STEPS=55; int STEPS_EXECUTED=0; boolean arrowPos = false ; // Button Press Count unsigned long keyPrevMillis = 0; const unsigned long keySampleIntervalMs = 25000; byte longKeyPressCountMax = 6; byte longKeyPressCount = 0; byte prevKeyState = HIGH; // button is active low // Mode byte mode = 0; // 0 - normal; 1 - menu boolean isFirstPage = true ; U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE); // Adafruit Feather ESP8266/32u4 Boards + FeatherWing OLED void u8g2_prepare( void ) { u8g2.setFont(u8g2_font_6x10_tf); u8g2.setFontRefHeightExtendedText(); u8g2.setDrawColor(1); u8g2.setFontPosTop(); u8g2.setFontDirection(0); } void setup() { Serial.begin(15200); pinMode(Bt1,INPUT_PULLUP); pinMode(Bt2,INPUT_PULLUP); pinMode(Bt3,INPUT_PULLUP); pinMode(A,OUTPUT); pinMode(B,OUTPUT); pinMode(C,OUTPUT); pinMode(D,OUTPUT); u8g2.begin(); u8g2.clearBuffer(); u8g2_prepare(); homePage(); u8g2.sendBuffer(); delay(500); } void loop() { draw(); Serial.print( digitalRead(Bt1) ); Serial.print( "\t" ); Serial.print( digitalRead(Bt2) ); Serial.print( "\t" ); Serial.print( digitalRead(Bt3) ); Serial.print( "\n" ); // Settings button pressed if (millis() - keyPrevMillis >= keySampleIntervalMs) { keyPrevMillis = millis(); byte currKeyState = digitalRead(Bt3); if ((prevKeyState == HIGH) && (currKeyState == LOW)) { keyPress(); } else if ((prevKeyState == LOW) && (currKeyState == HIGH)) { keyRelease(); } else if (currKeyState == LOW) { longKeyPressCount++; } prevKeyState = currKeyState; } } void shortKeyPress() { Serial.println( "short" ); if ( isFirstPage ) { OLD_MICROSECOND_STEP_DELAY=MICROSECOND_STEP_DELAY; MICROSECOND_STEP_DELAY=900; int i=0; while (i<NUMBER_OF_STEPS_PER_REV*10) { if ( digitalRead(Bt3) == LOW ) { break ; }; backwardStep(); i++; } STEPS_EXECUTED -= (NUMBER_OF_STEPS_PER_REV/(NUMBER_OF_STEPS_PER_PRESS-i)); MICROSECOND_STEP_DELAY=OLD_MICROSECOND_STEP_DELAY; } } void longKeyPress() { Serial.println( "long" ); mode=1; } void keyPress() { Serial.println( "key press" ); longKeyPressCount = 0; } void keyRelease() { Serial.println( "key release" ); if (longKeyPressCount >= longKeyPressCountMax) { longKeyPress(); } else { shortKeyPress();} } void draw( void ) { u8g2.clearBuffer(); u8g2_prepare(); switch (mode) { case 0: fPage(); break ; case 1: m1Page(); break ; case 2: m2Page(); break ; case 3: m3Page(); break ; } u8g2.sendBuffer(); delay(200); } void homePage() { u8g2.setFont(u8g2_font_9x15_tf); u8g2.drawStr( 0, 0, "Solder paste" ); u8g2.drawStr( 0, 20, "dispenser v1" ); } void fPage() { isFirstPage= true ; char str[30]; int i=0; sprintf (str, "Steps for rev.: %d" , NUMBER_OF_STEPS_PER_REV); u8g2.drawStr( 0, 0, str); u8g2.drawFrame(0,12,110,7); u8g2.drawBox((STEPS_EXECUTED*(110/TOTAL_STEPS)),12,110-(STEPS_EXECUTED*(110/TOTAL_STEPS)),7); u8g2.drawStr( 0, 24, "Settings: " ); u8g2.drawFrame(58,28,15,4); u8g2.drawFrame(84,28,15,4); u8g2.drawFrame(113,28,15,4); u8g2.drawBox(62,21,7,8); u8g2.drawBox(88,21,7,8); u8g2.drawBox(117,24,7,5); if ( digitalRead(Bt1) == LOW ) { while (i<NUMBER_OF_STEPS_PER_PRESS) { if ( digitalRead(Bt3) == LOW ) { break ; }; forwardStep(); i++; } STEPS_EXECUTED += (NUMBER_OF_STEPS_PER_REV/(NUMBER_OF_STEPS_PER_PRESS-i)); } if ( digitalRead(Bt2) == LOW ) { while (i<NUMBER_OF_STEPS_PER_PRESS) { if ( digitalRead(Bt3) == LOW ) { break ; }; backwardStep(); i++; } STEPS_EXECUTED -= (NUMBER_OF_STEPS_PER_REV/(NUMBER_OF_STEPS_PER_PRESS-i)); } if (STEPS_EXECUTED > TOTAL_STEPS || STEPS_EXECUTED < 0) STEPS_EXECUTED=0; arrowPos=!arrowPos; if (arrowPos) { /* Up */ u8g2.drawBox(120,9,2,6); u8g2.drawTriangle(118,15,124,15,121,18); } else { /* Down */ u8g2.drawBox(120,12,2,6); u8g2.drawTriangle(118,18,124,18,121,21); } } void menuButton() { u8g2.drawStr( 0, 12, "Down" ); u8g2.drawStr( 32, 12, "Up" ); u8g2.drawStr( 54, 12, "Confirm" ); u8g2.drawFrame( 4,28,15,4); u8g2.drawFrame(30,28,15,4); u8g2.drawFrame(72,28,15,4); u8g2.drawBox( 8,21,7,8); u8g2.drawBox(34,21,7,8); u8g2.drawBox(76,24,7,5); } void m1Page() { isFirstPage= false ; char str[30]; sprintf (str, "Steps per rev.: %d" , NUMBER_OF_STEPS_PER_REV); u8g2.drawStr( 0, 0, str); menuButton(); if ( (digitalRead(Bt1) == LOW ) && NUMBER_OF_STEPS_PER_REV < TOTAL_OF_STEPS_PER_REV) NUMBER_OF_STEPS_PER_REV += 10; if ( (digitalRead(Bt2) == LOW ) && NUMBER_OF_STEPS_PER_REV > 0 ) NUMBER_OF_STEPS_PER_REV--; if ( digitalRead(Bt3) == LOW ) mode = 2; } void m2Page() { isFirstPage= false ; char str[30]; sprintf (str, "Steps per press: %d" , NUMBER_OF_STEPS_PER_PRESS); u8g2.drawStr( 0, 0, str); menuButton(); if ( (digitalRead(Bt1) == LOW ) && NUMBER_OF_STEPS_PER_PRESS < NUMBER_OF_STEPS_PER_REV) NUMBER_OF_STEPS_PER_PRESS += 10; if ( (digitalRead(Bt2) == LOW ) && NUMBER_OF_STEPS_PER_PRESS > 0 ) NUMBER_OF_STEPS_PER_PRESS--; if ( digitalRead(Bt3) == LOW ) mode = 3; } void m3Page() { isFirstPage= false ; char str[30]; sprintf (str, "Delay per fases: %d" , MICROSECOND_STEP_DELAY); u8g2.drawStr( 0, 0, str); menuButton(); if ( (digitalRead(Bt1) == LOW ) && MICROSECOND_STEP_DELAY < 3000) MICROSECOND_STEP_DELAY += 10; if ( (digitalRead(Bt2) == LOW ) && MICROSECOND_STEP_DELAY > 0 ) MICROSECOND_STEP_DELAY--; if ( digitalRead(Bt3) == LOW ) mode = 0; } void forwardStep(){ Serial.println( "forwardStep" ); write(1,0,0,1); delayMicroseconds(MICROSECOND_STEP_DELAY); write(0,0,0,1); delayMicroseconds(MICROSECOND_STEP_DELAY); write(0,0,1,1); delayMicroseconds(MICROSECOND_STEP_DELAY); write(0,0,1,0); delayMicroseconds(MICROSECOND_STEP_DELAY); write(0,1,1,0); delayMicroseconds(MICROSECOND_STEP_DELAY); write(0,1,0,0); delayMicroseconds(MICROSECOND_STEP_DELAY); write(1,1,0,0); delayMicroseconds(MICROSECOND_STEP_DELAY); write(1,0,0,0); delayMicroseconds(MICROSECOND_STEP_DELAY); } void backwardStep(){ Serial.println( "backwardStep" ); write(1,0,0,0); delayMicroseconds(MICROSECOND_STEP_DELAY); write(1,1,0,0); delayMicroseconds(MICROSECOND_STEP_DELAY); write(0,1,0,0); delayMicroseconds(MICROSECOND_STEP_DELAY); write(0,1,1,0); delayMicroseconds(MICROSECOND_STEP_DELAY); write(0,0,1,0); delayMicroseconds(MICROSECOND_STEP_DELAY); write(0,0,1,1); delayMicroseconds(MICROSECOND_STEP_DELAY); write(0,0,0,1); delayMicroseconds(MICROSECOND_STEP_DELAY); write(1,0,0,1); delayMicroseconds(MICROSECOND_STEP_DELAY); } void write( int a, int b, int c, int d){ digitalWrite(A,a); digitalWrite(B,b); digitalWrite(C,c); digitalWrite(D,d); } |
le prime due linee includono le librerie necessarie al controllo e funzionamento del display OLED.
le linee 004-009: definiscono quale libreria di comunicazione includere in funzione della modalità di connessione del display che desideri utilizzare, nel progetto CtrlJ pen sketch il display OLED è connesso in modalità IIC;
linee 011-013: definisci i pin a cui sono connessi i bottoni di controllo. Nota che il pin è definito mediante la costante che ne definisce la funzione per esempio il pin 11 è MISO come puoi vedere nello schema:
la definizione dei pin di controllo del motore 28BYJ-48 avviene alle linee 015-018 in cui ciascuno dei pin è definito con una lettera: A,B,C e D e corrisponde allo schema dei collegamenti:
nelle linee 020-027: definisci le variabili in cui memorizzerai i valori relativi alla configurazione della penna:
20 21 22 23 24 25 26 27 | int TOTAL_OF_STEPS_PER_REV=512; int NUMBER_OF_STEPS_PER_REV=512; int NUMBER_OF_STEPS_PER_PRESS=256; int MICROSECOND_STEP_DELAY=1000; int OLD_MICROSECOND_STEP_DELAY=MICROSECOND_STEP_DELAY; int TOTAL_STEPS=55; int STEPS_EXECUTED=0; boolean arrowPos = false ; |
iniziando dalla prima:
TOTAL_OF_STEPS_PER_REV: rappresenta il numero totali di passi necessari al motore per compiere un giro completo dell’albero di 360°;
NUMBER_OF_STEPS_PER_REV: è il numero di passi che vuoi impostare per ogni rotazione;
NUMBER_OF_STEPS_PER_PRESS: è il numero di passi che saranno eseguiti alla pressione del tasto di avanzamento o retroazione;
MICROSECOND_STEP_DELAY: il tempo di delay in microsecondi tra un passo ed il successivo;
OLD_MICROSECOND_STEP_DELAY: è un valore di storage per memorizzare il valore precedentemente impostato e di default parte come MICROSECOND_STEP_DELAY;
TOTAL_STEPS: il numero totale di giri che il motore può compiere dalla posizione più arretrata del pistone alla posizione in cui il pistone è a finecorsa;
STEPS_EXECUTED: memorizza il valore dei passi eseguiti durante l’utilizzo della CtrlJ pen;
arrowPos: serve a gestire la visualizzazione grafica della freccia che ti invita a premere il terzo tasto;
Gestione dei bottoni
la gestione dei bottoni richiede l’uso di alcune variabili e costanti:
29 30 31 32 33 34 | // Button Press Count unsigned long keyPrevMillis = 0; const unsigned long keySampleIntervalMs = 25000; byte longKeyPressCountMax = 6; byte longKeyPressCount = 0; byte prevKeyState = HIGH; // button is active low |
la prima variabile keyPrevMillis serve a memorizzare il valore in millis() dal quale iniziare a contare il tempo che passa;
linea 031: intervallo di tempo, in millisecondi, di cui tener conto;
la linea 032: quanti intervalli di tempo, definiti nella precedente costante, devono trascorrere prima di considerare la pressione una “lunga” pressione, in questo esempio 6 x 25000 = 150 000 = 2,5 sec;
linea 033: la variabile longKeyPressCount ti serve per mantenere il conteggio degli intervalli da 25000 millisecondi mentre essi vengono contati;
infine, per la parte di gestione dei pulsanti, la variabile: prevKeyState memorizza lo stato della precedente pressione del pulsante;
CtrlJ pen sketch – config e display
Il menu di configurazione del CtrlJ pen sketch e la gestione del display OLED richiedono alcune funzioni, iniziamo dalle impostazioni:
36 37 38 39 40 41 42 43 44 45 46 47 48 | // Mode byte mode = 0; // 0 - normal; 1 - menu boolean isFirstPage = true ; U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE); // Adafruit Feather ESP8266/32u4 Boards + FeatherWing OLED void u8g2_prepare( void ) { u8g2.setFont(u8g2_font_6x10_tf); u8g2.setFontRefHeightExtendedText(); u8g2.setDrawColor(1); u8g2.setFontPosTop(); u8g2.setFontDirection(0); } |
le linee 036-038: definiscono la variabile mode che varia per indicarti se sei in fase di configurazione oppure no e la isFirstPage che ti serve per capire quando stai visualizzando la prima pagina;
la linea 040: imposta e inizializza il display in modalità IIC con la libreria u8glib2;
la funzione u8g2_prepare, come dice il nome stesso, serve a definire le caratteristiche principali ( tipo di font, dimensione, colore principale, direzione del font ) dei font che utilizzerai con il display;
Funzione Setup
la funzione setup della CtrlJ pen sketch come sai è una funzione di inizializzazione di tutte le parti principali del codice arduino:
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | void setup() { Serial.begin(15200); pinMode(Bt1,INPUT_PULLUP); pinMode(Bt2,INPUT_PULLUP); pinMode(Bt3,INPUT_PULLUP); pinMode(A,OUTPUT); pinMode(B,OUTPUT); pinMode(C,OUTPUT); pinMode(D,OUTPUT); u8g2.begin(); u8g2.clearBuffer(); u8g2_prepare(); homePage(); u8g2.sendBuffer(); delay(500); } |
e la userai per impostare le principali impostazioni quali la modalità di utilizzo dei pin e della modalità display.
Le linee 053-060: impostano la modalità INPUT_PULLUP per i pin relativi ai tre bottoni e OUTPUT per i pin di controllo del driver stepper del 28BYJ-48;
la linea 062: inizializza la libreria u8g2 che gestirà il display grafico OLED;
linee 064-065: pulisci il display dalle precedenti impostazioni e richiamoi la funzione u8g2_prepare vista nel paragrafo precedente;
linea 066: richiama la funzione HomePage() che, come immagini, imposta e disegna la prima pagina o main page visualizzata sul display;
linea 067: usando il metodo sendBuffer() della libreria u8g2 invii le impostazioni al display;
per questa prima parte dell’articolo CtrlJ pen sketch hai visto molte delle linee necessarie allo sketch stesso per funzionare.
Nei prossimi articoli entreremo nel dettaglio delle funzioni di loop() e di gestione della penna.