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:
#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:
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:
// 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:
// 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:
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.