// https://programmersqtcpp.blogspot.com/2022/03/applicazione-accesso-parcheggio.html
/*
Breve introduzione alla libreria SerialCmd
La libreria in questione permette di registrare dei comandi
tramite il metodo AddCmd() associando ad ognuno una funzione utente.
Il comando è una stringa letterare, es: "hlp" e la funzione utente
da associare è ad esempio uf_help. Nel setup usiamo allora il metodo
AddCmd() in questo modo:
mySerCmd.AddCmd( F("hlp"), SERIALCMD_FROMALL, uf_help );
Per le funzioni utente uso il prefisso uf_ da user function
così da non confonderle con altre funzione di supporto.
Adesso devo creare la mia funzione di nome uf_help e scrivo
come di seguito:
void uf_help() {
Serial.println("la funzione help è stata chiamata");
// qui possiamo stampare la lista dei comandi registrati
// e riconosciuta da SerialCmd.
}
** Le funzioni utente
Ogni funzione utente può se necessario ricavare degli argomenti
specificati dopo il comando. Gli argomenti saranno sempre delle
stringhe ma all'occorrenza si possono convertire in numeri.
Facciamo un esempio:
cir,0,0,160
Dove:
cir è il nome del comando
0 è la coordinata x
0 è la coordinata y
160 è il raggio in millimetri
Quindi il comando cir richiede che siano specificati a seguire
3 argomenti, x, y e r.
La funzione utente ha il compito di ricavare:
x e convertirlo in numero.
y e convertirlo in numero-
r e convertirlo in numero.
** Ricavare il prossimo argomento
Il metodo ReadNext() restituisce un puntatore valido all'argomento.
Se non specifichiamo un argomento o se la lista degli
argomenti è terminata ReadNext() restituisce un puntatore nullo.
Ogni funzione utente che richiede degli argomenti contiene sempre
la seguente dichiarazione:
char *args = nullptr;
Per ricavare l'argomento si usa sempre lo stesso pattern:
args = mySerCmd.ReadNext();
if (args) {
// entra qui se è stato trovato un argomento
}
Ricorda che ReadNext() restituisce nullptr se non trova
argomenti.
*/
#include <Servo.h>
#include <SerialCmd.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
// Crea l'oggetto mySerialCmd
// SERIALCMD_LF il serial monitor deve usare '\n' come terminatore
SerialCmd mySerCmd( Serial, SERIALCMD_LF );
// crea l'oggetto myServo.
Servo myServo;
/* gXxxxXxxx sono variabili globali generiche */
uint8_t gRow = 0;
uint8_t gCol = 0;
/* isNumber() restituisce true se arg è un numero decimale.
restituisce false se arg è una stringa
*/
bool isNumber(const char *arg) {
char *_arg = arg;
while (*_arg) {
if (isalpha(*_arg++))
return false;
}
return true;
}
/* info on system */
constexpr char *gigettoVersion = "0.1.1";
#if defined (__AVR_ATmega2560__)
constexpr char *mcuName = "ATmega2560";
#elif defined (__AVR_ATmega328P__)
constexpr char *mcuName = "ATmega328P";
#elif defined (__AVR_ATmega328__)
constexpr char *mcuName = "ATmega328";
#else
constexpr char *mcuName = "unknown";
#endif
void uf_infoSys() {
Serial.print(F("gigetto - Version:"));
Serial.println(gigettoVersion);
Serial.print(F("MCU:"));
Serial.print(mcuName);
Serial.print(", CLOCK:");
Serial.println(F_CPU);
Serial.print(F("avr-libc Ver:"));
Serial.println(F(__AVR_LIBC_VERSION_STRING__));
}
/* void uf_help().
(hlp) Stampa la lista dei comandi.
*/
void uf_help() {
Serial.println(F("srv:\t(servo) Imposta il servo <angle>."));
Serial.println(F("sc:\t(set cursor) Imposta il cursore a <row> <col>."));
Serial.println(F("p:\t(print) Stampa la stringa <text>."));
Serial.println(F("pc:\t(print clear) Stampa <n> spazi."));
Serial.println(F("ca:\t(clear all) Pulisce il display."));
Serial.println(F("hlp\tStampa su seriale informazioni sul sistema."));
}
/* void uf_clearAll().
(ca) pulisce il display.
*/
void uf_clearAll() {
lcd.clear();
}
/* void uf_setCursor().
(sc) imposta il cursore a <row> <col>.
Vedi void sh_print().
*/
void uf_setCursor() {
char *args = nullptr;
uint8_t error = 0;
uint8_t localRow = 0;
uint8_t localCol = 0;
// parse row
args = mySerCmd.ReadNext();
if (args) {
if (isNumber(args)) {
int val = atoi(args);
if ( (val < 0) && (val > 3) ) {
error = 1;
} else {
localRow = val;
}
}
}
// parse col
args = mySerCmd.ReadNext();
if (args) {
if (isNumber(args)) {
int val = atoi(args);
if ( (val < 0) && (val > 19) ) {
error = 1;
} else {
localCol = val;
}
}
}
if (error) {
Serial.println("Argomenti fuori range: row(0÷3) col (0÷19)");
return;
}
gRow = localRow;
gCol = localCol;
}
/* void uf_print().
(p) stampa la stringa <text> alle coordinate impostate
con il comando sc.
Vedi void sh_setCursor().
*/
void uf_print() {
char *args = nullptr;
// parse string the string
args = mySerCmd.ReadNext();
if (args) {
// nessun controllo sulla lunghezza della stringa
lcd.setCursor(gCol, gRow);
lcd.print(args);
}
}
/* void uf_printClear().
(pc) stampa n spazi alle coordinate impostate
con il comando sc.
Vedi void sh_setCursor().
*/
void uf_printClear() {
char *args = nullptr;
// parse number of space
args = mySerCmd.ReadNext();
if (args) {
// isNumber?
if (isNumber(args)) {
// se args è -10 abs(-10) = 10
int n = abs(atoi(args));
// crea un buffer di n + 1 byte azzerati
char s[n + 1] = {0};
// riempe il buffer s di n spazi
memset(s, ' ', n);
// stampa
lcd.setCursor(gCol, gRow);
lcd.print(s);
}
}
}
void uf_servo6() {
char *args = nullptr;
// parse number angle
args = mySerCmd.ReadNext();
if (args) {
// se args è un numero
if (isNumber(args)) {
// se args è -10, abs(-10) = 10
int angle = abs(atoi(args));
myServo.write(angle);
} else {
Serial.println("argument in not number");
}
} else {
Serial.println("devi specificare un argomento (angle)");
}
}
/* uf_loop() (lop) è un comando che blocca
il programma in un ciclo infinito senza
accettare più comandi.
*/
void uf_loop() {
Serial.println(SPL, HEX);
bool state = !digitalRead(LED_BUILTIN);
digitalWrite(LED_BUILTIN, state );
delay(1);
mySerCmd.ReadString("lop");
//Serial.println(SPH, HEX);
}
void setup() {
Serial.begin(115200);
delay ( 1000 );
pinMode ( LED_BUILTIN, OUTPUT );
// possiamo aggiungere massimo 8 comandi
// se servono più comandi occorre modificare
// il file header della libreia.
mySerCmd.AddCmd( F("sc"), SERIALCMD_FROMALL, uf_setCursor );
mySerCmd.AddCmd( F("p"), SERIALCMD_FROMALL, uf_print );
mySerCmd.AddCmd( F("pc"), SERIALCMD_FROMALL, uf_printClear );
mySerCmd.AddCmd( F("ca"), SERIALCMD_FROMALL, uf_clearAll );
mySerCmd.AddCmd( F("hlp"), SERIALCMD_FROMALL, uf_help );
mySerCmd.AddCmd( F("lop"), SERIALCMD_FROMALL, uf_loop );
mySerCmd.AddCmd( F("srv"), SERIALCMD_FROMALL, uf_servo6 );
lcd.init();
lcd.backlight();
myServo.attach(6);
uf_infoSys();
}
void loop() {
mySerCmd.ReadSer();
}