[FUNZIONA!] Scritte scorrevoli con MaxMatrix e 7219 [CON VIDEO]

Ciao a tutti

Sto cercando di modificare un programma per scritte scorrevoli su display a matrice di punti. Al momento ho solo una matrice 8x8, ma sto aspettando 2 moduli con 4 matrici ciascuno, per un totale di 8. Comunque, in piccolo, la scrittura base già funziona.
Vorrei poter richiamare, tramite un encoder, 5 o più scritte preimpostate. Purtroppo, però, non riesco proprio a districarmi con gli array di caratteri! Pensavo, semplicemente, di selezionare un valore di nMsg con l’encoder e, in base a quello, impostare l’array con uno switch/case, ma il compilatore indica errori!
Per permettere di scegliere il messaggio corretto, a ogni click dell’encoder dovrei anche far apparire per un secondo circa il numero del messaggio selezionato.

Al programma originale sono riuscito, dopo molti tentativi in modi diversi, ad aggiungere una pausa impostabile (da programma, per ora) facendo scrivere ripetutamente ’ ’ (spazio) alla fine del messaggio per un tempo pari alla pausa desiderata.

Mi potete aiutare?

Grazie!
Gianluca

/*  8x8 LED Matrix MAX7219 Scrolling Text
        Android Control via Bluetooth
        
 by Dejan Nedelkovski, www.HowToMechatronics.com
 Based on the following library:
 GitHub | riyas-org/max7219  https://github.com/riyas-org/max7219
 https://howtomechatronics.com/tutorials/arduino/8x8-led-matrix-max7219-tutorial-scrolling-text-android-control-via-bluetooth/
*/
#include <MaxMatrix.h>
#include <SoftwareSerial.h>
#include <avr/pgmspace.h>

byte dIn=8;   // DIN
byte clk=9;   // CLK
byte  cs=10;    // CS
byte maxInUse=2;    // Number of MAX7219's connected

MaxMatrix m(dIn, cs, clk, maxInUse);
SoftwareSerial Bluetooth(12, 11); // Rx, Tx per il modulo Bluetooth.

byte buffer[10];
char incomebyte;

int velocita=50; // 0...100
unsigned long pausa=3000; // Pausa in millisecondi.
int brightness=3;
char text[100];

int count=0;
char indicator;
int scrollSpeed=30;
unsigned long t0; // Per la pausa in printCharWithShift().
unsigned long t1; // Per la pressione prolungata del tasto.
uint8_t nMsg=1;
int E; // Risultato della routine encoder(): 1, -1, 0.
uint8_t S; // Lettura dei due valori dell'encoder.
uint8_t So;// Lettura precedente dell'encoder.
int X; // Usato in encoder() per evitare letture multiple.

PROGMEM const unsigned char CH[] = {
  3, 8, B00000000, B00000000, B00000000, B00000000, B00000000, // space
  1, 8, B01011111, B00000000, B00000000, B00000000, B00000000, // !
  3, 8, B00000011, B00000000, B00000011, B00000000, B00000000, // "
  5, 8, B00010100, B00111110, B00010100, B00111110, B00010100, // #
  4, 8, B00100100, B01101010, B00101011, B00010010, B00000000, // $
  5, 8, B01100011, B00010011, B00001000, B01100100, B01100011, // %
  5, 8, B00110110, B01001001, B01010110, B00100000, B01010000, // &
  1, 8, B00000011, B00000000, B00000000, B00000000, B00000000, // '
  3, 8, B00011100, B00100010, B01000001, B00000000, B00000000, // (
  3, 8, B01000001, B00100010, B00011100, B00000000, B00000000, // )
  5, 8, B00101000, B00011000, B00001110, B00011000, B00101000, // *
  5, 8, B00001000, B00001000, B00111110, B00001000, B00001000, // +
  2, 8, B10110000, B01110000, B00000000, B00000000, B00000000, // ,
  4, 8, B00001000, B00001000, B00001000, B00001000, B00000000, // -
  2, 8, B01100000, B01100000, B00000000, B00000000, B00000000, // .
  4, 8, B01100000, B00011000, B00000110, B00000001, B00000000, // /
  4, 8, B00111110, B01000001, B01000001, B00111110, B00000000, // 0
  3, 8, B01000010, B01111111, B01000000, B00000000, B00000000, // 1
  4, 8, B01100010, B01010001, B01001001, B01000110, B00000000, // 2
  4, 8, B00100010, B01000001, B01001001, B00110110, B00000000, // 3
  4, 8, B00011000, B00010100, B00010010, B01111111, B00000000, // 4
  4, 8, B00100111, B01000101, B01000101, B00111001, B00000000, // 5
  4, 8, B00111110, B01001001, B01001001, B00110000, B00000000, // 6
  4, 8, B01100001, B00010001, B00001001, B00000111, B00000000, // 7
  4, 8, B00110110, B01001001, B01001001, B00110110, B00000000, // 8
  4, 8, B00000110, B01001001, B01001001, B00111110, B00000000, // 9
  2, 8, B01010000, B00000000, B00000000, B00000000, B00000000, // :
  2, 8, B10000000, B01010000, B00000000, B00000000, B00000000, // ;
  3, 8, B00010000, B00101000, B01000100, B00000000, B00000000, // <
  3, 8, B00010100, B00010100, B00010100, B00000000, B00000000, // =
  3, 8, B01000100, B00101000, B00010000, B00000000, B00000000, // >
  4, 8, B00000010, B01011001, B00001001, B00000110, B00000000, // ?
  5, 8, B00111110, B01001001, B01010101, B01011101, B00001110, // @
  4, 8, B01111110, B00010001, B00010001, B01111110, B00000000, // A
  4, 8, B01111111, B01001001, B01001001, B00110110, B00000000, // B
  4, 8, B00111110, B01000001, B01000001, B00100010, B00000000, // C
  4, 8, B01111111, B01000001, B01000001, B00111110, B00000000, // D
  4, 8, B01111111, B01001001, B01001001, B01000001, B00000000, // E
  4, 8, B01111111, B00001001, B00001001, B00000001, B00000000, // F
  4, 8, B00111110, B01000001, B01001001, B01111010, B00000000, // G
  4, 8, B01111111, B00001000, B00001000, B01111111, B00000000, // H
  3, 8, B01000001, B01111111, B01000001, B00000000, B00000000, // I
  4, 8, B00110000, B01000000, B01000001, B00111111, B00000000, // J
  4, 8, B01111111, B00001000, B00010100, B01100011, B00000000, // K
  4, 8, B01111111, B01000000, B01000000, B01000000, B00000000, // L
  5, 8, B01111111, B00000010, B00001100, B00000010, B01111111, // M
  5, 8, B01111111, B00000100, B00001000, B00010000, B01111111, // N
  4, 8, B00111110, B01000001, B01000001, B00111110, B00000000, // O
  4, 8, B01111111, B00001001, B00001001, B00000110, B00000000, // P
  4, 8, B00111110, B01000001, B01000001, B10111110, B00000000, // Q
  4, 8, B01111111, B00001001, B00001001, B01110110, B00000000, // R
  4, 8, B01000110, B01001001, B01001001, B00110010, B00000000, // S
  5, 8, B00000001, B00000001, B01111111, B00000001, B00000001, // T
  4, 8, B00111111, B01000000, B01000000, B00111111, B00000000, // U
  5, 8, B00001111, B00110000, B01000000, B00110000, B00001111, // V
  5, 8, B00111111, B01000000, B00111000, B01000000, B00111111, // W
  5, 8, B01100011, B00010100, B00001000, B00010100, B01100011, // X
  5, 8, B00000111, B00001000, B01110000, B00001000, B00000111, // Y
  4, 8, B01100001, B01010001, B01001001, B01000111, B00000000, // Z
  2, 8, B01111111, B01000001, B00000000, B00000000, B00000000, // [
  4, 8, B00000001, B00000110, B00011000, B01100000, B00000000, // \
  2, 8, B01000001, B01111111, B00000000, B00000000, B00000000, // ]
  3, 8, B00000010, B00000001, B00000010, B00000000, B00000000, // hat
  4, 8, B01000000, B01000000, B01000000, B01000000, B00000000, // _
  2, 8, B00000001, B00000010, B00000000, B00000000, B00000000, // `
  4, 8, B00100000, B01010100, B01010100, B01111000, B00000000, // a
  4, 8, B01111111, B01000100, B01000100, B00111000, B00000000, // b
  4, 8, B00111000, B01000100, B01000100, B00101000, B00000000, // c
  4, 8, B00111000, B01000100, B01000100, B01111111, B00000000, // d
  4, 8, B00111000, B01010100, B01010100, B00011000, B00000000, // e
  3, 8, B00000100, B01111110, B00000101, B00000000, B00000000, // f
  4, 8, B10011000, B10100100, B10100100, B01111000, B00000000, // g
  4, 8, B01111111, B00000100, B00000100, B01111000, B00000000, // h
  3, 8, B01000100, B01111101, B01000000, B00000000, B00000000, // i
  4, 8, B01000000, B10000000, B10000100, B01111101, B00000000, // j
  4, 8, B01111111, B00010000, B00101000, B01000100, B00000000, // k
  3, 8, B01000001, B01111111, B01000000, B00000000, B00000000, // l
  5, 8, B01111100, B00000100, B01111100, B00000100, B01111000, // m
  4, 8, B01111100, B00000100, B00000100, B01111000, B00000000, // n
  4, 8, B00111000, B01000100, B01000100, B00111000, B00000000, // o
  4, 8, B11111100, B00100100, B00100100, B00011000, B00000000, // p
  4, 8, B00011000, B00100100, B00100100, B11111100, B00000000, // q
  4, 8, B01111100, B00001000, B00000100, B00000100, B00000000, // r
  4, 8, B01001000, B01010100, B01010100, B00100100, B00000000, // s
  3, 8, B00000100, B00111111, B01000100, B00000000, B00000000, // t
  4, 8, B00111100, B01000000, B01000000, B01111100, B00000000, // u
  5, 8, B00011100, B00100000, B01000000, B00100000, B00011100, // v
  5, 8, B00111100, B01000000, B00111100, B01000000, B00111100, // w
  5, 8, B01000100, B00101000, B00010000, B00101000, B01000100, // x
  4, 8, B10011100, B10100000, B10100000, B01111100, B00000000, // y
  3, 8, B01100100, B01010100, B01001100, B00000000, B00000000, // z
  3, 8, B00001000, B00110110, B01000001, B00000000, B00000000, // {
  1, 8, B01111111, B00000000, B00000000, B00000000, B00000000, // |
  3, 8, B01000001, B00110110, B00001000, B00000000, B00000000, // }
  4, 8, B00001000, B00000100, B00001000, B00000100, B00000000, // ~
};
void setup()
{
pinMode(6, INPUT_PULLUP); // Pulsante
if(!digitalRead(6)) selezione();
else
  {
  char testo0[100]="Testo di default";
  for(byte x=0; x<100; x++) text[x]=testo0[x];
  }

if(velocita>100) velocita=100;
scrollSpeed=110-velocita;
//Serial.begin(9600);

m.init(); // MAX7219 initialization
m.setIntensity(brightness); // initial led matrix intensity, 0-15
Bluetooth.begin(9600); // Default communication rate of the Bluetooth module
// Bluetooth.begin(38400); // Default communication rate of the Bluetooth module
}


void loop()
{
printStringWithShift(text, scrollSpeed); // Printing the text

if(Bluetooth.available()) // Checks whether data is comming from the serial port
  {  
  indicator=Bluetooth.read();   // Starts reading the serial port, the first byte from the incoming data
  if(indicator=='1') // If we have pressed the "Send" button from the Android App, clear the previous text
    {
    for(int i=0; i<100; i++)
      {text[i]=0; m.clear();}
    while(Bluetooth.available()) // Read the whole data/string comming from the phone and put it into text[] array.
      {
      incomebyte=Bluetooth.read();
      text[count]=incomebyte;
      count++;
      }
    count=0;
    }
  
  else if(indicator=='2') // Adjusting the Scrolling Speed
    {
    String sS=Bluetooth.readString();
    scrollSpeed=150-sS.toInt(); // Milliseconds, subtraction because lower value means higher scrolling speed
    }
  
  else if(indicator=='3') // Adjusting the brightness
    {
    String sB=Bluetooth.readString();
    brightness=sB.toInt();
    m.setIntensity(brightness);
    }
  } // END if Bluetooth available
} // END loop



void selezione()
{
while(!digitalRead(6)); // Attende che venga lasciato il pulsante.
while(digitalRead(6)) // Continua a leggere l'encoder finché non premo
  { 
  encoder();
  nMsg+=E; E=0;
  if(nMsg<1) nMsg=1;
  else if(nMsg>9) nMsg=9;
  //printString (nMsg); delay(700);
  }
switch(nMsg)
  {
  case 1:
  char testo1[100]="Testo numero 1";
  for(byte x=0; x<100; x++) text[x]=testo1[x];
  break;

  case 2:
  char testo2[100]="Testo numero 2";
  for(byte x=0; x<100; x++) text[x]=testo2[x];
  break;
  }
}


void printCharWithShift(char c, int shift_speed)
{  
if (c<32) return;
c-=32;
memcpy_P(buffer, CH+7*c, 7);
m.writeSprite(32, 0, buffer);
m.setColumn(32+buffer[0], 0);
for(int i=0; i<buffer[0]+1; i++)
  {delay(shift_speed); m.shiftLeft(false, false);}
}

void printStringWithShift(char*s, int shift_speed)
{
while(*s!=0)
  {printCharWithShift(*s, shift_speed); s++;}
if(*s==0)
  {
  t0=millis();
  while(millis()-t0<pausa)
    {
    printCharWithShift(' ', shift_speed);
    }
  }
}



void printString(char*s)
{
int col=0;
while(*s!=0)
  {
  if(*s<32) continue;
  char c=*s-32;
  memcpy_P(buffer, CH+7*c, 7);
  m.writeSprite(col, 0, buffer);
  m.setColumn(col+buffer[0], 0);
  col+=buffer[0]+1;
  s++;
  }
}

void encoder()
{
//            PD 76543210
// S=3-(PIND>>3)&B00000011; Serviva per l'encoder su PD3 e PD4.
// S=3-PIND&B00000011; Gli I/O 0 e 1 sono PD0 e PD1, perciò non devo scorrere a destra.
S=3-((PIND>>4)&B00000011); // Gli I/O 4 e 5 sono PD4 e PD5, perciò non devo scorrere a destra di 4 bit. 
// Il valore binario di S rappresenta A e B. Il centrale dell'encoder è a massa, quindi faccio complemento a 3 (11).  
S^=S>>1; // Faccio XOR (^) fra S (gray) e il suo bit 1, facendolo scorrere a Dx: AB XOR A,
         // ottenendo un binario che per ogni scatto fa 0-1-2-3-0 oppure 0-3-2-1-0.
if (S!=So && S==0) X=0;
if (X==0)
  {
  if (So==1&&S==2)
    {E=1; X=1;;}
  if (So==3&&S==2)
    {E=-1; X=1;;}
  if (S==0)
    {E=0; X=0;}
  So=S;  
  }
}

Nel listato che ho riportato, vado a copiare un testo diverso, carattere per carattere, in text, ma non funziona... :frowning:

Se dopo aver dichiarato all'inizio
char text[100];

nel setup scrivo
text="pippo";

il compilatore risponde:
error: incompatible types in assignement of 'const char [6] to 'char [100].

Se, però, all'inizio dichiaro
char text[100]="pippo";

va tutto bene...

Se dichiaro
char text="ciao ";

cioè ciao e 46 spazi
e poi nel setup scrivo

for(int x=0; x<50; x++) {text[x]="pippo";}

sulla matrice scorre una fila di Q…

Ho copiato e incollato il tuo codice, a parte dover eliminare il riferimento alla libreia MaxMatrix che non ho (e non voglio installare) compilando non ho errori di compilazione e guardando così il programma la selezione del testo dovrebbe funzionare a livello logico.
Supponendo che il problema stia nella linea che hai commentato che richiama la funzione printString non può funzinare come vorresti in quando tu passi un byte

uint8_t nMsg = 1;

ma la fuzione si aspetta un puntatore ad un array di char poiché suppongo tu voglia stampare via via il testo che stai selezionando.
Credo che per arrivare al tuo obiettivo dovresti creare una matrice di messaggi tipo:

char sMessages[9][100] = {"Primo messaggio", "Secondo Messaggio", "Terzo", ..., "Nono mesaggio"};

e nella funzione printString passare l'indice del messaggio selezionato come uint8_t e non come puntatore come ora. Dentro la funzione vai via via a stampare i caratteri creandoti un puntatore tipo

char* pippo = sMessages[nMsg];

per mantenere la compatibilità del resto delel funzioni che usi con uarray di char e non matrici.
Spero di non aver errato in quanto non ho potuto provare nulla ma ho scritto a "memoria".
Poi una domanda... perché fai uso di for per copiare i caratteri da un vettore ad un altro e non usi le funzioni apposite? Con i tuoi for copi sempre e comunque 100 caratteri anche quando hai oltrepassato il terminatore di stringa, cosa abbastanza superflua

Sono prove che ho fatto cercando di far uscire qualcosa...

La linea con printString che ho commentato l'ho messa lì solo perché vorrei far apparire per un secondo il numero della stringa richiamata: 1, 2, 3... Proprio perché l'ho trasformato in commento non crea problemi.

Datman:
Se dopo aver dichiarato all'inizio
char text[100];

nel setup scrivo
text="pippo";

il compilatore risponde:
error: incompatible types in assignement of 'const char [6] to 'char [100].

Se, però, all'inizio dichiaro
char text[100]="pippo";

va tutto bene...

Certo che ti da errore, gli array di char non sono stringe (intese come classe String) a cui puoi assegnare in quel modo la variabile text di fatto è un puntatore alla prima cella dell'array tu provi ad assegnargli un array di char e non si può fare. Occorre usare le funzioni per copiare/concatenare le stringhe classiche del c (strncpy, strncat, ecc.) mentre se lo fai durante la dichiarazione il compilatore fa il lovoro di assegnare ogni singolo char alla relativa posizione di memoria

Grrr... Questo è uno dei problemi di Arduino: tutti in rete parlano di String e si trova pochissimo sugli array di caratteri. Appena si prova ad allontanarsi un po' dalle cose che vanno bene solo per giocare con 10 righe di programma, la documentazione va cercata col lanternino... (o bisogna già conoscere bene il C...) :frowning:

Datman:
Grrr... Questo è uno dei problemi di Arduino: tutti in rete parlano di String e si trova pochissimo sugli array di caratteri. Appena si prova ad allontanarsi un po' dalle cose che vanno bene solo per giocare con 10 righe di programma, la documentazione va cercata col lanternino... (o bisogna già conoscere bene il C...) :frowning:

Purtroppo per far avvicinare una grande platea di utenti si tende a rendergli la vita facile in fase di primo approccio quindi String e delay gisuto per citare due esempi. Poi fuoriusciti dagli esempi di base si incontrano i problemi e occorre si iniziare a studiare il C. Comunque lavorare con gli array di char bastano poche funzioni tipo queste:
snprintf
itoa
atoi
strncpy
strncat
strtok
Con le quali si fa quasi tutto e alla fine capito che alla fine occorre sempre avere il terminatore (\0) (che viene messo in automatico se inizializzi con char pippo[10] = "ciao") poi funzionano con la medesima logica, alla fine si riesce a padroneggiarle con poche ore di studio e prove

Se poso permettermi un piccolo appunto al tuo codice, le varibili locali o globali che siano se proprio non ti piace farle parlanti almeno non dichiararle di un solo carattere è un casino andare a ricercarle nel programma la classica i usata nei for se poi devi ricercarla ai voglia a quante occorrenze non volute ti trovi, già usare una cosa del tipo idx rende la vita più semplice in caso di ricerche, se poi dichiari una roba del tipo idxScanCharArray anche la leggibilità ne giova.

Ti riferisci a E, X, S, So? Sono variabili usate solo per la lettura dell'encoder. Ho inventato da zero quella funzione e ormai la copio così com'è. L'unica variabile che interessa è E, come Encoder, che può valere:
0 se non è stato mosso
-1 se è stato ruotato in senso antiorario
+1 se è stato ruotato in senso orario.

La "i" in for(int i=0; ...) non l'ho scelta io: fa parte del programma che ho copiato.

Mi rifrisco anche a m, t0, t1 che non è che rendono proprio idea di cosa siano e a cosa servano. Poi, per carità, ognuno ha il suo stile e, se non deve rispettare regole di progetto, può usare i,j,k,x,y quanto vuole :slight_smile:

Anche m sta nella parte che ho copiato! :slight_smile:

I nomi t0, t1, ..., tn li uso abitualmente per le letture di millis(). Per che cosa sono usate è scritto accanto alla dichiarazione, nella prima scheda (il primo file). Per il resto ormai uso variabili più intuitive, seppure brevi perché la memoria della mia scheda video cerebrale è limitata! :smiley: Un nome breve lo riconosco immediatamente, visivamente, e lo distinguo in un attimo come una mela da un'arancia, mentre un nome lungo devo leggerlo tutto e interpretarlo per capire che cos'è, come per distinguere "Oggi è una giornata con tanto sole" da "Oggi è una giornata con poco sole".

Certo, ognuno fa come si trova meglio :slight_smile:

de gustibus non disputandum est

:smiley:
A parte queste poche variabili, cercherò di essere comprensibile! :slight_smile:

Sto cercando di intercettare la "è" e la "é" per farle apparire sulla matrice. Ho già aggiunto le righe nell'array CH e i due caratteri appaiono se le sostituisco, ad esempio, alla lettera "a" (ASCII 97), che è l'elemento 65 dell'array (l'array inizia dallo spazio [ASCII 32], quindi 97-32=65).
Se scrivo
if(c==65) c=95;
quindi, anziché "Ciao" appare "Cièo", a conferma del corretto funzionamento del carattere. Che valore devo mettere, allora, per la "è" e la "é"?

Ho provato con 232, 233,138, 139 e 105, ma non funziona. Ho provato anche a fare dei Serial.print e Serial.write, ma non sono riuscito a scoprire il numero che devo mettere :frowning:

Grazie :slight_smile:

Non comprendo la necessità di ricrearsi la tabella ascii nella progmem, perché non stampi direttamente il codice del carattere anziché cercarlo in una tabella?
Comunque la tabella ASCII standard termina a 127 che è il DEL i caratteri che cerchi tu sono nella tabella ASCII estesa e la è viene rappresentata da 138 mentre la é non è prevista nella tabella ASCII

E' una matrice di punti 8x8, quindi ogni carattere è definito punto per punto.

Ah già che pirla che sono :slight_smile: