/*
sercmd-evo is demo application of the SerialCmd library.
Enter cmdl in serial monitor input to list commands.
Read Readme.md for detail.
Copyright (C) 2022 Maurilio Pizzurro
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
// commenta la riga seguente per abilitare il comando wreg,
// ma prima leggi il Readme.md.
#define DISABLE_WREG
#include <LiquidCrystal.h>
#include "MTScrollText.h"
#include "SerialCmd.h"
LiquidCrystal lcd( 12, 11, 10, 9, 8, 7 );
bool isBlinking = false; // Indicates whether blinking is active or not
uint8_t ledStatus = LOW; // BUILTIN_LED status (OFF/ON)
uint8_t blinkingCnt = 0; // Number of led status changes before turning off blinking
uint32_t blinkingTime = 0; // Time of led status change
uint32_t blinkingLast = 0; // Last millis() in which the status of the led was changed
uint8_t scrollSpeed = 50;
//extern uint32_t timer0_millis;
MTScrollText_P infoProg( 20 );
uint8_t scroolInRow = 3;
char userScrollBuffer[40] = {0};
MTScrollText userText(10);
SerialCmd mySerCmd( Serial, SERIALCMD_LF ); // Initialize the SerialCmd constructor using the "Serial" port
void sendOK() {
mySerCmd.Print ( F("OK \r\n") );
}
void set_LEDON() {
isBlinking = false;
ledStatus = HIGH;
digitalWrite( LED_BUILTIN, HIGH );
sendOK();
}
void set_LEDOF() {
isBlinking = false;
ledStatus = LOW;
digitalWrite( LED_BUILTIN, LOW );
sendOK();
}
void set_LEDBL () {
char *sParam = nullptr;
//
sParam = mySerCmd.readNext();
if ( sParam == nullptr ) {
mySerCmd.Print( F("ERROR: Missing blinking time \r\n") );
return;
}
blinkingCnt = 0;
blinkingTime = strtoul( sParam, NULL, 10 );
blinkingLast = millis();
isBlinking = true;
sendOK();
}
// mostra copyright info scorrevole su riga 0 di LCD
void info() {
bool tf = !infoProg.isScroll();
infoProg.setScroll(tf);
if (tf == false) {
const char *infoP = infoProg.clear();
lcd.setCursor(0, 0); // info scorre su riga 0
lcd.print(infoP); // pulisce riga 0
}
}
// imposta la velocità di scorrimento
void setScrollSpeed() {
char *sParam = nullptr;
//
sParam = mySerCmd.readNext();
if (sParam) {
uint8_t speed = strtoul( sParam, nullptr, 10 );
if ((speed <5) || speed > 100) {
Serial.println(F("argomento fuori range (5÷100)."));
return;
}
scrollSpeed = speed;
} else {
Serial.println(F("Manca l'argomento espresso in ms, es: speed,20"));
}
}
// stampa su lcd il valore di millis()
void millisToLcd() {
// se info sta "scrollando" disabilita lo scroll chiamando info
if (infoProg.isScroll() == true)
info();
// compare su lcd al posto di info
char cl[21] = {0};
memset(cl, ' ', 20);
lcd.setCursor(0,0);
lcd.print(cl);
lcd.setCursor(0,0);
lcd.print("millis = ");
lcd.print(millis());
}
// prende un numero di riga (1÷3) e un testo da fare scorrere su lcd
// esempio di comando: MSST,2,il testo scorre su riga 2.ù
// il testo non deve superare i 39 caratteri.
void myStringScroll() {
char *sParam = nullptr;
//
sParam = mySerCmd.readNext();
if (sParam) {
scroolInRow = strtoul( sParam, nullptr, 10 );
sParam = mySerCmd.readNext();
if (sParam) {
if (strlen(sParam) > 39) {
Serial.println("stringa troppo lunga");
return;
}
memset(userScrollBuffer, ' ', 39);
strcpy(userScrollBuffer, sParam);
userText.setText(userScrollBuffer);
} else {
lcd.setCursor(0, scroolInRow);
lcd.print(userText.clear());
}
} else {
Serial.println(F("Manca l'argomento per la riga del display."));
}
}
// helper function
int16_t resolveAddress(const char *par) {
int16_t addr = -1;
addr = strtoul( par, nullptr, 10 ); // prova con base 10
if (addr == 0) {
addr = strtoul( par, nullptr, 16 ); // prova con base 16
}
return addr;
}
// prende due argomenti <sa> start address e <ea> end address
// i due argomenti possono essere sia decimali che esadecimali.
// Es: DMEM,0x100,0x8FF.
// DMEM,0,0xFF
void dumpMemory() {
int16_t iniRam = 0x100;
int16_t lenRam = 0x8FF - iniRam;
char *sParam = nullptr;
//
sParam = mySerCmd.readNext();
if (sParam) {
// ricava se possibile l'argomento <sa> start address
int16_t _iniRam = resolveAddress(sParam);
if (_iniRam < 0) {
Serial.println(F("<sa> non può essere negativo"));
return;
} else if (_iniRam >= 0x8FF) {
Serial.println(F("Indirizzo <sa> maggiore di 2303"));
return;
}
sParam = mySerCmd.readNext();
if (sParam) {
// ricava se possibile l'argomento <ea> end address
int16_t _endRam = resolveAddress(sParam);
if (_endRam < _iniRam) {
Serial.println("<sa> deve essere minori di <ea>");
return;
}
if ((_endRam > 0x8FF) || (_endRam <= 0)) {
Serial.println(F("argomento <ea> non valido (max 2303)."));
return;
}
iniRam = _iniRam;
lenRam = _endRam - _iniRam;
}
}
dumpRam(iniRam, lenRam);
}
// stampa su seriale il dump della RAM, da indirizzo <start>
// <len> celle.
void dumpRam(uint16_t start, uint16_t len) {
// spazzoliamo tutta la ram cella per cella.
// come da datasheet inizio da 0x100
const char *ptrToRam = (char*)start;
// mi servono 16 caselle da 0÷15
uint8_t counter15 = 15;
Serial.print(F("addr\t00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"
" 0123456789ABCDEF" ));
// Nota per il 328 RAMEND vale 2303
char rowBuff[17] = {0};
for (uint16_t t=0; t<= len; t++) {
if (counter15 == 15) {
Serial.print("\n");
Serial.print((uint16_t)ptrToRam, HEX);
Serial.print("\t");
}
if ((counter15 > -1) && (counter15 < 16)) {
// vogliamo vedere i caratteri quindi
// dal puntatore con * otteniamo il puntato
// e poi il cast a char
char b = (char)*ptrToRam++;
// se b non è rappresentabile
// stampo il punto ".".
char buff2[3] = {0};
sprintf(buff2, "%02x", (uint8_t)b);
Serial.print(buff2);
Serial.print(' ');
if ((b < 32) || (b > 126)) {
rowBuff[15-counter15] = '.';
} else {
rowBuff[15-counter15] = b;
}
}
if (counter15 == 0 )
Serial.print(rowBuff);
counter15--;
counter15 = counter15 % 30;
}
Serial.println();
}
void commandList() {
Serial.println(F("LEDON - accende il led"));
Serial.println(F("LEDOF - spegne il led"));
Serial.println(F("LEDBL,ms - lampeggia il led per ms millisecondi"));
Serial.println(F("millis - Mostra su LCD il valore di millis()"));
Serial.println(F("info - Mostra/Nasconde copyright su lcd"));
Serial.println(F("MSST,row,text - Mostra il testo 'text' alla riga 'row"));
Serial.println(F(" Es:MSST,2,il mio testo scorrevole su riga 2 "));
Serial.println(F("DMEM - dump della memoria ram"));
Serial.println(F("DMEM,<sa>,<ea> - dump della memoria da indirizzo\n"
" <sa> a <ea> decimale o esadecimale"));
Serial.println(F("rreg,<addr> - read register legge in ram all'indirizzo <addr>"));
#ifndef DISABLE_WREG
Serial.println(F("wreg,<addr>,<value> - write register scrive in ram\n"
" all'indirizzo <addr> il valore <value>."));
#endif
Serial.println(F("cmdl - Mostra questa lista dei comandi."));
Serial.println(F("Leggi Readme.md per i dettagli"));
//Serial.println(timer0_millis);
//Serial.println((uint16_t)&timer0_millis, HEX);
}
// legge il valore da un indirizzo di memoria
void readRegister() {
int16_t regAddr = -1;
char *sParam = nullptr;
//
sParam = mySerCmd.readNext();
if (sParam) {
regAddr = resolveAddress(sParam);
if (regAddr < 0) {
Serial.println("Indirizzo del registro non valido");
return;
}
}
uint8_t *addr = (uint8_t*)regAddr;
uint8_t b = (uint8_t)*addr;
Serial.print(F("DEC:"));
Serial.print(b, DEC);
Serial.print(F("\tHEX:"));
Serial.print(b, HEX);
Serial.print(F("\tBIN:"));
Serial.println(b, BIN);
}
// scrive su indirizzo di memoria
void writeRegister() {
int16_t regAddr = -1;
int16_t regValue = -1;
char *sParam = nullptr;
//
sParam = mySerCmd.readNext();
if (sParam) {
regAddr = resolveAddress(sParam);
if (regAddr < 0) {
Serial.println(F("Indirizzo <addr> del registro non valido"));
return;
}
sParam = mySerCmd.readNext();
if (sParam) {
regValue = resolveAddress(sParam);
if (regValue < 0) {
Serial.println(F("<reg_value> invalido"));
return;
}
}
}
uint8_t *addr = (uint8_t*)regAddr;
*addr = (uint8_t)regValue;
}
void setup() {
delay ( 500 );
pinMode ( LED_BUILTIN, OUTPUT );
digitalWrite( LED_BUILTIN, ledStatus );
Serial.begin( 115200 );
while ( !Serial ) {
delay ( 100 );
ledStatus = !ledStatus;
digitalWrite ( LED_BUILTIN, ledStatus );
}
mySerCmd.addCmd( F("LEDON"), SERIALCMD_FROMALL, set_LEDON );
mySerCmd.addCmd( F("LEDOF"), SERIALCMD_FROMALL, set_LEDOF );
mySerCmd.addCmd( F("LEDBL"), SERIALCMD_FROMALL, set_LEDBL );
mySerCmd.addCmd( F("info"), SERIALCMD_FROMALL, info );
mySerCmd.addCmd( F("speed"), SERIALCMD_FROMALL, setScrollSpeed );
mySerCmd.addCmd( F("millis"), SERIALCMD_FROMALL, millisToLcd );
mySerCmd.addCmd( F("MSST"), SERIALCMD_FROMALL, myStringScroll );
mySerCmd.addCmd( F("DMEM"), SERIALCMD_FROMALL, dumpMemory );
mySerCmd.addCmd( F("cmdl"), SERIALCMD_FROMALL, commandList );
mySerCmd.addCmd( F("rreg"), SERIALCMD_FROMALL, readRegister );
#ifndef DISABLE_WREG
mySerCmd.addCmd( F("wreg"), SERIALCMD_FROMALL, writeRegister );
#endif
infoProg.setText(F("SerialCmd: Copyright (C) 2013 - 2022 Guglielmo Braguglia"
" MTScrollText: Copyright (C) 2022 Maurilio Pizzurro "), false);
// INFO: Program running ... \r\n la dovrai vedere in RAM
mySerCmd.Print( "INFO: Program running ... \r\n" );
lcd.begin( 20, 4 );
}
uint32_t oldMillis = 0;
void loop() {
if (millis() - oldMillis >= scrollSpeed) {
char *infoP = infoProg.doScroll();
char *userT = userText.doScroll();
oldMillis = millis();
if (infoProg.isScroll()) {
lcd.setCursor(0, 0);
lcd.print(infoP);
}
if (userText.isScroll()) {
lcd.setCursor(0, scroolInRow);
lcd.print(userT);
}
}
if ( isBlinking && ( millis() - blinkingLast > blinkingTime ) ) {
ledStatus = !ledStatus;
digitalWrite( LED_BUILTIN, ledStatus );
blinkingCnt++;
blinkingLast += blinkingTime;
}
//
if ( blinkingCnt >= 10 ) {
blinkingCnt = 0;
mySerCmd.readString( F("LEDOF") );
}
// I comandi inseriti li vedi in RAM e ciò che vedi
// è il ring buffer della seriale, ad esempio
// inserisci i seguenti comandi: DMEM invio, info invio, DMEM invio
// troverai in ram DMEM.info.DMEM.
// Mentre i comandi registrati sono in RAM e li vedi in sequenza
// di registrazione.
mySerCmd.read();
}