per qualche lucina in più...

Che è naturalmente il seguito del mio precedente "per un pugno di bottoni"
la in quel topic parlavamo di come estendere gli ingressi utili di un arduino, "tirando fuori" ogni possibile estensione dalla nostra povera scheda Arduino

adesso sì è presentata, in un'altra discussione l'esigenza di "mettere" molte lucine, e la cosa sembra un po' la naturale estensione della storia dei pulsanti

voi lo sapete, che parlo poco nelle discussioni altrui, in particolare se lo OP latita, però io le idee le ho avute e le ho sviluppate (un poco) quindi, per non disperderle al vento, le lascio scritte qui

un breve riassunto:
facciamo caso di avere un elevato numero di postazioni (elevato potrebbe essere tipo 400-600, per una MEGA, oppure 50-60 per una UNO)
che richiedono abbastanza sporadicamente intervento inatteso (se fosse atteso non ci sarebbe problema) da parte di un operatore, ma sporadicamente vuol proprio dire sporadicamente, anche meno di una volta a settimana

verrebbe comodo avere una "lucetta" per postazione per indicare, magari anche da lontano, dove intervenire, e ho aggiunto io, anche un pulsante per "segnalare" intervento avvenuto

il tutto con dei cosi e delle complessità ancora a livello "umano"

ora, a parte il fatto che ho idee "innovative" su APA102, seriali e catene di lucine, ma questo è altra storia
la soluzione meno "sanguinosa" la ho vista in un bel multiplex di led, e siccome i vari interventi sono sporadici il multiplex deve essere adattivo come velocità, dato che su magari 500 "luci" solo una o due sono "alle volte" accese

ecco quindi l'idea: un doppio array di piedini, chiamiamoli anodi e catodi , con riferimento al diodo led che vi sarà collegato

ma solo un array monodimensionale di elementi accesi-spenti

il multiplex scandisce la matrice bidimensionale di piedini, ma si ferma e accende (sopratutto si ferma) solo se l'elemento deve essere acceso, ecco quindi la cadenza variabile di scansione del multiplex
fatta l'accensione si girano i piedini, si porta in interdizione il led e a questo punto quello che era il catodo diventa un ingresso pulluppato, il vecchio anodo un'uscita a massa e se il pulsante fosse premuto il catodo NON sente il pullup

adesso arrivano schema (per una matrice demo) e programma di esempio

// di Nelson-StandardOil
// IDE 1.8.13
// Progetto:
// Scopo: aggiungere pulsanti ai led multiplati

// piedini, lato anodo del led
byte anodo[] = {2, 3, 4}; // numeri di piena fantasia
byte anodi = sizeof anodo / sizeof anodo[0];
// piedini, lato catodo del led
byte catodo[] = {5, 6, 7}; // tre piedini di anodo e tre di catodo  a fare 9 led
byte catodi = sizeof catodo / sizeof catodo[0];



// i singoli gruppi LED-pulsante, chiamati selettori, sono indirizzati comandati ed interrogati da una matrice, mappata su un array
byte selettore[(sizeof anodo / sizeof anodo[0]) * (sizeof catodo / sizeof catodo[0])];
// sembra incredibile, ma è allocazione statica
int selettori = sizeof selettore / sizeof selettore[0];



// serve un ingresso di comando, per provocare l'accensione del led

#define COMANDO 10
// il debounce è hardware

// il tempo in mS per un passo di multiplex
#define SLICE 5


void setup(void)
{
   Serial.begin(9600);
   Serial.println("Progetto pulsanti");

   for (int i = 0; i < anodi; i++)
   {
      pinMode(anodo[i], INPUT);
   }

   for (int i = 0; i < catodi; i++)
   {
      pinMode(catodo[i], INPUT);
   }

   // tengo i piedini in input, per evitare false accensioni dei led
   // l'ingresso di comando, debounce hardware, logica positiva (pulldown)
   pinMode(COMANDO, INPUT);
}


void loop(void)
{
   int tasto = 0;

   // se c'è impulso di comando
   if (digitalRead(COMANDO))
   {
      // simuliamo la necessità di un intervento da parte dell'operatore
      // quindi accendiamo un led a caso
      int p = random(selettori);
      selettore[p] = 1;
      Serial.print("Necessità di intervento alla postazione numero: ");
      Serial.println(p);
   }

   //adesso passiamo alla funzione che accende e mantiene accesi i led, interrogando contemporaneamente i pulsanti associati

   if ((tasto = premuto()) > -1)
   {
      // se restituisce -1 nessun tasto premuto
      // altri valori negativi riservati per usi futuri
      // se restituisce positivo o 0 pulsante corrispondete premuto
      // per prima cosa spengo il LED
      selettore[tasto] = 0;
      // e poi faccio qualcosa, che corriponderà all'azione relativa al completamento dell'operazione indicata dal led
      // tipicamente lo trasmetto via seriale
      Serial.print("Postazione numero: ");
      Serial.print(tasto);
      Serial.println(" raggiunta e intervento eseguito");
   }
}


int premuto(void)
{
   /*
      scorre la matrice di scansione
      accende i relativi led
      e aspetta la pressione del tasto
      al primo tasto che sente premuto ne ritorna il numero
   */
   for (int i = 0; i < selettori; i++)
   {
      // per ogni selettore
      if (selettore[i])
      {
         // se attivo lo accende
         // calcolo quale anodo e quale catodo attivare
         int a = i / anodi;
         int c = i - a * anodi;
         // faccio le pinmode
         pinMode(anodo[a], OUTPUT);
         pinMode(catodo[c], OUTPUT);
         digitalWrite(anodo[a], 1);
         digitalWrite(catodo[a], 0);
         // polarizzazione diretta, il led si accende
         // per il multiplex, tengo attivo un tempo di multiplex
         delay(SLICE);
         // adesso vedo se il suo pulsante è premuto
         // l'anodo diventa -
         digitalWrite(anodo[a], 0);
         //il catodo diventa un ingresso pulluppato
         pinMode(catodo[c], INPUT_PULLUP);

         // se l'ingresso non sente il 5V significa che il pulsante è premuto

         if (!digitalRead(catodo[c]))
         {
            // rimetto comunque a posto anodo e catodo
            pinMode(catodo[c], INPUT);
            pinMode(anodo[a], INPUT);
            return i;
            // pulsante premuto, valore restituito, lavoro finito
         }

         // se non sono uscito il tasto non è premuto
         // rimetto comunque a posto anodo e catodo
         pinMode(catodo[c], INPUT);
         pinMode(anodo[a], INPUT);
      }
   }

   // se siamo arrivati qui nessun selettore è stato trovato premuto
   return -1;
}

rimane sempre il fatto che non ho così tanto hardware per fare le prove, il programma e lo schema sono di principio, ma lo schema è elementare e il programma compila
quindi una eventuale messa a punto non dovrebbe essere difficile

e adesso buon appetito

ma come si ridimiensiona l'immagine? non lo trovo più...

Standardoil:
...
ma come si ridimiensiona l'immagine? non lo trovo più...

aggiungi spazio width=xxx dopo image nelle quadre ... "image width=500" <- o la larghezza che vuoi, puoi impostare anche height=xxx nello stesso modo ma non so se poi te le deforma se non sono in proporzione ... con solo width l'altezza la proporziona da solo ...

non ho così tanto hardware per fare le prove

Ti ho spedito 500 led e 500 pulsanti, quando ti arrivano voglio vedere il modello fisico funzionante :stuck_out_tongue:
(sto scherzando, ovviamente :D)

Etemenanki:
Ti ho spedito 500 led e 500 pulsanti, quando ti arrivano voglio vedere il modello fisico funzionante

E i 18km di filo?

ne ho mandati 20, non si sa mai ... :smiley:

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.

aggiungiamo una possibile estensione per poter comandare un po' come se fosse una matrice normale, quindi anche bottoni premuti contemporaneamente e/o interruttori (le due cose sono simili)

notoriamente non si può usare una matrice di pulsanti (o interruttori, simile è il concetto) se esiste a possibilità che alcuni pulsanti siano chiusi contemporaneamente

serve in quel caso di mettere dei diodi in serie ai contatti

ecco quindi la versione di "per qualche lucina in più" che permette questo

multiplex_2

se vi serve provatela e fatemi sapere

EDIT
Ovvio che il programma non va più bene
Il programma è stato pensato per gestire luci ed occasionalmente pulsanti

Qui si deve invece mettere la classica scansione a matrice di pulsanti e multiplex di led
Invertendo le polarità a seconda di cosa si sta facendo
E tenendo in alta impedenza i piedini non interessati all'azione immediata

ok, dopo un po' di bagordi (e a voi non interessa il perché) sono tornato sull'argomento

cominciamo col dire che ho pensato che già che ci metto un diodo anti-pressione-multipla-di-tanti-tasti, tantovale metterci un LED, che così ho anche l'evidenza visiva che il pulsante è premuto...

però un led ha una caduta di tensione, che serve di considerare
quindi quello che dico va bene solo per MCU a 5Volt e led di segnalazione del tasto premuto obbligatoriamente rossi
con questa maniera si possono mettere fino a N/2*N/2 LED su N pin di arduino CON altrettanti pulsanti, CON altrettanti LED che segnalano pulsante premuto, e più di così io non sono capace di fare

ecco lo schema, per 9 LED più 9 pulsanti ognuno col suo bravo LED rosso di segnalazione:

come vedete si tratta di una cosa un po' convenzionale,
il programma: è poco più di una manciata di funzioni, che permettono di gestire la matrice come un array x*y di pulsanti

sono sicuro si possa migliorare, ma voglio lasciare questo come margine per gli interressati

per intanto presento le varie funzioni, che basta includere (copiincollare) oppure anche copiarsi in una libreria personale

naturalmente una struttura per descrivere i pulsanti:

typedef struct
{
   bool premuto;
   bool acceso;

} Tasto;

le varie dichiarazioni necessarie, che mi sembrano anche auto-esplicative...


/ matrice 3 per 3
// 3 anodi e tre catodi
// un po' di dichiarazioni
byte anodi[] = {2, 3, 4};
byte catodi[] = {5, 6, 7};
byte Anodi = sizeof anodi / sizeof anodi[0];
byte Catodi = sizeof catodi / sizeof catodi[0];
// l'array di tasti, diviso per righe e colonne
Tasto tasti[sizeof catodi / sizeof catodi[0]][ sizeof anodi / sizeof anodi[0]];


// un paio di abbreviazioni
#define catodo catodi[c]
#define anodo anodi[a]

e le funzioni ausiliari di interrogazione tasti e settaggio luci
nelle due versioni: a matrice oppure come singolo array lineare

// una serie di comandi semplici per accendere un singolo led
// potrebbero essere comodi...
// le versioni che sembrano indirizzare un array monodimensionale
void accendi(byte numero)
{
   accendi(numero / Anodi, numero % Anodi);
}
void spegni(byte numero)
{
   spegni(numero / Anodi, numero % Anodi);
}
bool statoled(byte numero)
{
   return  statoled(numero / Anodi, numero % Anodi);
}
bool pulsante(byte numero)
{
   return pulsante(numero / Anodi, numero % Anodi);
}


// e le loro versioni a matrice
void accendi(byte c, byte a)
{
   tasti[c][a].acceso = 1;
}
void spegni(byte c, byte a)
{
   tasti[c][a].acceso = 0;
}

bool statoled(byte c, byte a)
{
   return tasti[c][a].acceso;
}
bool pulsante(byte c, byte a)
{
   return tasti[c][a].premuto;
}

poi la sola funzione da chiamare "spesso" nella loop, funzione che "faso tuto mi"
legge i tasti e aggiorna l'array ed accende i LED leggendo dall'array, in una sola chiamata


void scorre()
{
   // scorre la matrice per leggere i tasti
   for (int c = 0; c < Catodi; c++)
   {
      for (int a = 0; a < Anodi; a++)
      {
         // se devo accendere il led:
         if (tasti[c][a].acceso)
         {
            // anodo +
            pinMode(anodo, OUTPUT);
            digitalWrite(anodo, 1);
            // catodo -
            pinMode(catodo, OUTPUT);
            digitalWrite(catodo, 0);
            // pausa
         }

         pinMode(catodo, INPUT_PULLUP);

         // fine della scansione in scrittura
         for (int a = 0; a < Anodi; a++)
         {
            pinMode(anodo, INPUT);
         }

         pinMode(anodo, OUTPUT);
         digitalWrite(anodo, 0);
         // leggo il tasto
         tasti[c][a].premuto = !digitalRead(catodo);
      }
   }
}

naturalmente ci vuole una setup


void setup(void)
{
   Serial.begin(9600);
   pinMode(13, OUTPUT);
   Serial.println("inizio programma");
   Serial.flush();
}

e una loop, che vi dovete costruire voi, che faccia quello che volete voi
io ne metto una di esempio, che legge i tasti e ne scrive lo stato sulla seriale
e accende in sequenza i LED

void loop(void)
{
   static int i = 0;
   scorre();

   if (temporizzatore('a'))
   {
      digitalWrite(13, !digitalRead(13));
      temporizzatore('a', 100);

      if (statoled(i))
      {
         spegni(i);
      }

      else
      {
         accendi(i);
      }

      i++;
      i = i % 10;

      for (int c = 0; c < Catodi; c++)
      {
         for (int a = 0; a < Anodi; a++)
            if (tasti[c][a].premuto)
            {
               Serial.print(c);
               Serial.print(a);
            }

            else
            {
               Serial.print("--");
            }
      }

      Serial.println();
   }
}

come vedete ricorre alla mia libreria del tempo, ma ne ho già parlato in abbondanza...

come al solito termino con una "coda" di commenti

la luminosità del LED di segnalazione è un po' ballerina, e quella del LED principale è un po' limitata

ma da una UNO con solo sei pin usati non si può pretendere molto di più

Consideratela un esempio fonte di ispirazione per chi cerca queste cose, e sono tanti, anche di recente

di nuovo: siccome io ho provato e so che va non venitemi a dire che non va perché, al solito, prima vi plonko e poi vi dimentico...

ps, volete il programma completo?
eccolo

/* di Nelson-StandardOil
   IDE 1.8.19 -
   Progetto:
   Scopo: prova di matrice led e pulsanti con uso di interruttori
   Data inizio lavori: 03/05/22
*/


// matrice 3 per 3
// 3 anodi e tre catodi
// un po' di dichiarazioni
byte anodi[] = {2, 3, 4};
byte catodi[] = {5, 6, 7};
byte Anodi = sizeof anodi / sizeof anodi[0];
byte Catodi = sizeof catodi / sizeof catodi[0];


// un paio di abbreviazioni
#define catodo catodi[c]
#define anodo anodi[a]

// gli include
#include "tempodellelibrerie.h"



// la struttura che descrive il singolo tasto
typedef struct
{
   bool premuto;
   bool acceso;

} Tasto;

// l'array di tasti, diviso per righe e colonne
Tasto tasti[sizeof catodi / sizeof catodi[0]][ sizeof anodi / sizeof anodi[0]];



// una serie di comandi semplici per accendere un singolo led
// potrebbero essere comodi...
// le versioni che sembrano indirizzare un array monodimensionale
void accendi(byte numero)
{
   accendi(numero / Anodi, numero % Anodi);
}
void spegni(byte numero)
{
   spegni(numero / Anodi, numero % Anodi);
}
bool statoled(byte numero)
{
   return  statoled(numero / Anodi, numero % Anodi);
}
bool pulsante(byte numero)
{
   return pulsante(numero / Anodi, numero % Anodi);
}


// e le loro versioni a matrice
void accendi(byte c, byte a)
{
   tasti[c][a].acceso = 1;
}
void spegni(byte c, byte a)
{
   tasti[c][a].acceso = 0;
}

bool statoled(byte c, byte a)
{
   return tasti[c][a].acceso;
}
bool pulsante(byte c, byte a)
{
   return tasti[c][a].premuto;
}


void scorre()
{
   // scorre la matrice per leggere i tasti
   for (int c = 0; c < Catodi; c++)
   {
      for (int a = 0; a < Anodi; a++)
      {
         // se devo accendere il led:
         if (tasti[c][a].acceso)
         {
            // anodo +
            pinMode(anodo, OUTPUT);
            digitalWrite(anodo, 1);
            // catodo -
            pinMode(catodo, OUTPUT);
            digitalWrite(catodo, 0);
            // pausa
         }

         pinMode(catodo, INPUT_PULLUP);

         // fine della scansione in scrittura
         for (int a = 0; a < Anodi; a++)
         {
            pinMode(anodo, INPUT);
         }

         pinMode(anodo, OUTPUT);
         digitalWrite(anodo, 0);
         // leggo il tasto
         tasti[c][a].premuto = !digitalRead(catodo);
      }
   }
}

void setup(void)
{
   Serial.begin(9600);
   pinMode(13, OUTPUT);
   Serial.println("inizio programma");
   Serial.flush();
}
void loop(void)
{
   static int i = 0;
   scorre();

   if (temporizzatore('a'))
   {
      digitalWrite(13, !digitalRead(13));
      temporizzatore('a', 100);

      if (statoled(i))
      {
         spegni(i);
      }

      else
      {
         accendi(i);
      }

      i++;
      i = i % 10;

      for (int c = 0; c < Catodi; c++)
      {
         for (int a = 0; a < Anodi; a++)
            if (tasti[c][a].premuto)
            {
               Serial.print(c);
               Serial.print(a);
            }

            else
            {
               Serial.print("--");
            }
      }

      Serial.println();
   }
}

Ok, bella gente

ne ho tratto una librerietta (in C intendiamoci, solo .h come mia abitudine)
per poter usare questa roba in progetti più complessi

il testo della libreria, salvatevela dove volete, io in "matriled.h" sottodirectory sottodirectory: portable / sketchbook / libraries / Nelson


/* la libreria matriled si aspetta di trovare dichiarati ed inizializzati gli array dei catodi, degli anodi e
   dichiara la funzioni scorre() che aggiorna letture ed accensioni
   setta(LED, valore) che alza o abbassa il led
   accendi(LED) accende il led
   spegni(LED) spegna il led
   statoled(LED) restituisce acceso o spento
   pulsante(LED) che restituisce premuto o no il pulsante associato al led
   LED puo' essere a due cifre (catodo, anodo) o a una cifre sola (indice da zero all'ultimo come array monodimensionale)
   rende inoltre disponibili le seguenti macro e variabili
   Anodi: il numero di anodi (sizeof dell'array)
   Catodi: anche
   Tasti: prodotto Anodi per Catodi, ovvero il complessivo dei Tasti ( dei LED)
   anodo: abbreviazione dell'anodo corrente (anodi[a])
   catodo: anche
*/

// un po' di dichiarazioni
byte Anodi = sizeof anodi / sizeof anodi[0];
byte Catodi = sizeof catodi / sizeof catodi[0];

// un paio di abbreviazioni
#define catodo catodi[c]
#define anodo anodi[a]
#define Tasti (Anodi * Catodi)


// la struttura che descrive il singolo tasto
typedef struct
{
   bool premuto;
   bool acceso;

} Tasto;

// l'array di tasti, diviso per righe e colonne
Tasto tasti[sizeof catodi / sizeof catodi[0]][ sizeof anodi / sizeof anodi[0]];


// una serie di comandi semplici per accendere un singolo led
// potrebbero essere comodi...


// e le versioni a matrice
void setta(byte c, byte a, byte valore)
{
   tasti[c][a].acceso = valore;
}
void accendi(byte c, byte a)
{
   tasti[c][a].acceso = 1;
}
void spegni(byte c, byte a)
{
   tasti[c][a].acceso = 0;
}
bool statoled(byte c, byte a)
{
   return tasti[c][a].acceso;
}
bool pulsante(byte c, byte a)
{
   return tasti[c][a].premuto;
}
// le versioni che sembrano indirizzare un array monodimensionale
void setta(byte numero, byte valore)
{
   setta(numero / Anodi , numero % Anodi, valore);
}
void accendi(byte numero)
{
   accendi(numero / Anodi, numero % Anodi);
}
void spegni(byte numero)
{
   spegni(numero / Anodi, numero % Anodi);
}
bool statoled(byte numero)
{
   return  statoled(numero / Anodi, numero % Anodi);
}
bool pulsante(byte numero)
{
   return pulsante(numero / Anodi, numero % Anodi);
}


// la funzione di scorrimento matrice ed aggiornamento LED/Pulsanti
void scorre()
{
   // scorre la matrice per accendere i led
   for (byte c = 0; c < Catodi; c++)
   {
      for (byte a = 0; a < Anodi; a++)
      {
         // se devo accendere il led:
         if (tasti[c][a].acceso)
         {
            // anodo +
            pinMode(anodo, OUTPUT);
            digitalWrite(anodo, 1);
            // catodo -
            pinMode(catodo, OUTPUT);
            digitalWrite(catodo, 0);
            // pausa
            pinMode(catodo, INPUT);
            pinMode(anodo, INPUT);
         }
      }
   }

   // fine della scansione in scrittura

   // inizio scansione in lettura
   for (byte c = 0; c < Catodi; c++)
   {
      pinMode(catodo, INPUT_PULLUP);

      for (byte a = 0; a < Anodi; a++)
      {
         pinMode(anodo, OUTPUT);
         digitalWrite(anodo, 0);
         // leggo il tasto
         tasti[c][a].premuto = !digitalRead(catodo);
         pinMode(anodo, INPUT);
      }

      pinMode(catodo, INPUT);
   }
}

e un programma di esempio:

/* di Nelson-StandardOil
   IDE 1.8.19 -
   Progetto:
   Scopo: prova di matrice led e pulsanti con uso di interruttori
   se non si preme alcun tasto fa una animazione tipo supercar
   se si preme un tasto ferma l'animazione e accende il "successivo" al tasto premuto
   il led del pin 13 lampeggia di un tempo suo, ad indicare che il programma non è bloccante
   Data inizio lavori: 03/06/22
*/
#include "tempodellelibrerie.h"

// matrice 3 per 3
// 3 anodi e tre catodi
// un po' di dichiarazioni
byte anodi[] = {2, 3, 4};
byte catodi[] = {5, 6, 7};



/* la libreria matriled si aspetta di trovare dichiarati ed inizializzati gli array dei catodi, degli anodi e
   dichiara la funzioni scorre() che aggiorna letture ed accensioni
   setta(LED, valore) che alza o abbassa il led
   accendi(LED) accende il led
   spegni(LED) spegna il led
   statoled(LED) restituisce acceso o spento
   pulsante(LED) che restituisce premuto o no il pulsante associato al led
   LED puo' essere a due cifre (catodo, anodo) o a una cifre sola (indice da zero all'ultimo come array monodimensionale)
   rende inoltre disponibili le seguenti macro e variabili
   Anodi: il numero di anodi (sizeof dell'array)
   Catodi: anche
   Tasti: prodotto Anodi per Catodi, ovvero il complessivo dei Tasti ( dei LED)
   anodo: abbreviazione dell'anodo corrente (anodi[a])
   catodo: anche
*/

#include "matriled.h"


void setup(void)
{
   Serial.begin(9600);
   pinMode(13, OUTPUT);
   Serial.println("inizio programma");
   Serial.print("Tasti riconosciuti: ");
   Serial.println(Tasti);
   Serial.flush();
}
void loop(void)
{
   static byte i = 0;

   if (temporizzatore('b'))
   {
      temporizzatore('b', 254);
      digitalWrite(13, !digitalRead(13));
   }

   scorre();
   byte si = 1;

   for (byte j = 0; j < Tasti; j++)
   {
      if (pulsante(j))
      {
         si = 0;
         break;
      }
   }

   if (si)
   {
      if (temporizzatore('a'))
      {
         temporizzatore('a', 200);
         setta(i, !statoled(i));
         i++;
         i = i % Tasti;
      }
   }

   else
   {
      for (byte i = 0; i < Tasti; i++)
      {
         setta((i + 1) % Tasti, pulsante(i));
      }
   }
}

e con questo per il momento chiudo questo argomento

ma intendo usarlo per farne un semaforo...