Semafori stradali

Gestione semaforo

  • Una classe Timer4 con due registri
  • Una classe LSS (Lanterna Semaforica Stradale)
  • Controllo dei led attraverso shitf register (74595)

Work in Progress...

#include <SPI.h>

#define TIMER4_DEBUG
#include "Semafori.h"
#include "sr595.h"

const byte PIN_CS   =  4;   

const byte PIN_START   =  7;

SR59516 sr16(PIN_CS);

uint16_t interval[] = { 3000  // tempo di permanenza verde
                                   , 1000   // tempo di permanenza giallo
                                   , 800     // tempo di permanenza rosso
                                     };
// nota: mentre il semaforo A si evolve da verde al rosso, il semaforo B
// resta fermo sul rosso per il tempo 3000 + 1000 + 800 = 4800ms.
// Poi resta fermo A e si evolve B e così via per sempre.

// creare istanza di LSS richiede come argomenti:
// * Una istanza di classe SR59516 (vedi sr16)
// * 3 argomenti bit-verde, bit-giallo e bit-rosso
// Esempio: LSS   lss2(
//               sr16,  // istanza di classe SR59516
//                 10,  // bit 10, pin sr2:Q2
//                 11,  // bit 11, pin sr2:Q3
//                 12   // bit 12, pin sr2:Q4 
//                   );

LSS lss5(sr16, 2, 3, 4);
LSS lss35(sr16, 5, 6, 7);
LSS lss2(sr16, 10, 11, 12);
LSS lss24(sr16, 13, 14, 15);

//const char *strColor[] = { "Verde", "Giallo", "Rosso" };
void setup() {
 
    Serial.begin(115200);
  
    pinMode(PIN_START, INPUT_PULLUP);
    pinMode(PIN_CS, OUTPUT);

    digitalWrite(PIN_CS, HIGH);
    SPI.begin();
    // ad ogni lanterna è possibile assegnare un ID 0÷255  
    lss2.setId(42);
    lss24.setId(24);
    lss5.setId(53);
    lss35.setId(35);
           
    Timer4 timer4(interval);

    // prima di potere usare una istanza di classe LSS
    // è necessario assegnargli un registro.
    // Un registro è sostanzialmente una variabile uint8_t
    // che assume un valore da 0÷2:
    // 0 verde, 1 giallo, 2 rosso.

    lss5.setRegister(timer4.getRegG());
    lss2.setRegister(timer4.getRegR());
    lss24.setRegister(timer4.getRegR());
    lss35.setRegister(timer4.getRegG());
    // avvia il timer 
    timer4.start();

    while(true) {
      // timer4.run() // restituisce true allo scadere dei 
      // seguenti tempi: uint16_t interval[] = { 3000, 1000, 800 };
      bool event = timer4.run();
      if (event) {
          // aggiorna lo stato di tutte le lanterne
          lss2.show();
          lss5.show();
          lss24.show();
          lss35.show();
      }
    }

}

void loop() { 
    
}

La classe LSS

Questa classe "è stupida", stupida nel senso che non cronometra i tempi, ma si limita
ad accendere/spegnere il led della lanterna in base al valore del registro m_register.

class LSS {
  public:
    
    LSS(SR59516 &sr, uint8_t pinG, uint8_t pinY, uint8_t pinR) :
        m_srPtr(&sr), m_pinG(pinG), m_pinY(pinY), m_pinR(pinR)
        {}

    void setId(uint8_t id) {
        m_id = id;
    }
    
    uint8_t id() const {
        return m_id;
    }

    void setRegister(uint8_t *reg) {
        m_register = reg;
    }
    uint8_t *getRegister() const {
        return m_register;
    }
    void printReg() {
      Serial.println(*m_register);
    }

    void show() {
        if (m_register == nullptr)
            return; 
        // piccola modifica: solo i primi 3 bit, gli altri 5 portano altre informazioni
        switch(*m_register & 0x07) {
          case 0:  // VERDE
            m_srPtr->set(m_pinR, false);
            m_srPtr->set(m_pinG, true);
            m_cColor = Color::GREEN;
            break;
          case 1:  // GIALLO
            m_srPtr->set(m_pinG, false);
            m_srPtr->set(m_pinY, true);
            m_cColor = Color::YELLOW;
            break;
          case 2:  // ROSSO
            m_srPtr->set(m_pinY, false);
            m_srPtr->set(m_pinR, true);
            m_cColor = Color::RED;
            break;
         
        }
        m_srPtr->update();
    }

    Color color() const {
        return m_cColor;
    }
    
  private:
    SR59516 *m_srPtr;
    Color m_cColor;
    uint8_t m_id;
    uint8_t *m_register = nullptr;
     
    uint8_t m_pinG;
    uint8_t m_pinY;
    uint8_t m_pinR;
  
};

La classe Timer4

Questa classe è stata realizzata al solo scopo di testare il concetto di
registro condiviso come canale di comunicazione, per cui tutto è discutibile.
Il registro è però di proprietà di Timer4 (vedi m_reg[]) che potrebbe essere limitante.
Il puntatore m_t deve puntare ad un array di 3 elementi (vedi nello sketch uint16_t interval[]). I valori dei 3 tempi specificati dovrebbero essere modificabili a run-time
senza side effect, per realizzare ciò, Timer4 dovrebbe ad ogni ciclo copiarsi l'array dei tempi, in questo modo possiamo modificare interval[] ad ogni istante e le nuove tempistiche saranno eseguite nel prossimo ciclo semaforico.

class Timer4 {
  public:             
    Timer4(uint16_t *t) : m_t(t)
    {}
    void start() {
      m_timer = millis();
    }
    
    uint8_t *getRegG() {
      return &m_reg[0];
    }

    uint8_t *getRegR() {
      return &m_reg[1];
    }

    bool run() {
        if (m_timeEvent) {
            m_timeEvent = false;
            return true;
        }
            
        bool t_event = false;
        if (millis() - m_timer >= m_t[m_tIdx]) {
#ifdef TIMER4_DEBUG
          Serial.print("reg[");
          Serial.print(m_regIdx);
          Serial.print("].");
          Serial.print(m_t[m_tIdx]);
          Serial.print(".");
          Serial.println(m_reg[m_regIdx]);
          //Serial.print("\t");
#endif
          if (m_reg[m_regIdx] != Color::RED) {
              m_reg[m_regIdx]++;
          } else {
              m_regIdx = !m_regIdx;
              m_reg[m_regIdx]++;
              m_reg[m_regIdx] = m_reg[m_regIdx] % 3;
          }
          m_tIdx++;
          m_tIdx = m_tIdx % 3;
          m_timer = millis();
          t_event = true;
        } else {
          t_event = false;

        }
        return t_event;
    }
  private:
    uint32_t m_timer;     // per salvare il valore di millis()
    bool m_timeEvent = true;
    uint16_t *m_t = nullptr;  // puntatore ad array di 3 tempi in ms
    uint8_t m_tIdx;  // indice di m_t
    uint8_t m_reg[2] = { Color::GREEN, Color::RED }; // registro sincronizzato
    uint8_t m_regIdx; // indice registro sincronizzato
};
1 Like

Riprendo l'argomento Semafori, perché è evidente che mi sono appassionato.
Di seguito alcuni link a enti e documenti importanti.

Documenti

  1. tmutcd Il link punta ad una pagina in cui è possibile scaricare un corposo documento chiamato Texas Manual on Uniform Traffic Control Devices (tmutcd).

  2. Traffic Signal Operations Handbook, il link punta ad una pagina da cui scaricare il libro, il quale contenuto riassume e condensa l'argomento che è di una complessità inimmaginabile.

Software

  1. Sumo - Si tratta di un software open sources molto potente; pensate che è possibile avviare un simulazione di traffico su una mappa scaricata con openstreetmap. La macchinine, accelerano, frenano e possono saturare una o più strade. Ops, non è mica facile usare questo software, ma ci sono esempi base da simulare.

Controller

  1. TS2 NEMA - Il link punta ad immagini di Controller; Tutt'altro che bassa tecnologia ad alto prezzo.

Software per arduino

Non ho trovano nulla di valido da postare. Io ho al momento 4 lanterne stradali e 4 pedonali (no prenotazione) su incrocio, usando SPI Hardware e 3x74hc595 24 uscite, cioè 8 lanterne. Per l'hardware bastano poche porte digitali per realizzare il bit Chip Select.

Ciao.

1 Like

Perché non li hai saputi trovare

Per cominciare basta questo:

Ma a cercare ...

No non mi riferisco a codice per accendere qualche led che imita una o più lanterne semaforiche, ma a software/hardware open source di controller simili a quelli in campo professionale.

Anche un controller semplice limitato a lavora solo in pre-timed mode è complesso, poiché non si tratta di scrivere codice per uno specifico incrocio, ma di scrivere codice che una volta configurato si adatta alle diverse esigenze.

Ecco perché non ho trovato nulla, e quel post è conosciuto da quando lo hai scritto. :smiling_face_with_sunglasses:

Ciao.

Un riassunto di ciò che ho compreso fino a questo momento sulle centraline semaforiche.

Un controller semaforico (abbr. TLC Traffic Light Controller) è un dispositivo hardware programmabile (o configurabile). Nel caso in cui sia programmabile si intende che il firmware che gira sul controller è capace di eseguire del codice binario specificamente progettato per quell'incrocio dagli ingegneri del traffico. Il sistema una volta installato deve ricevere il firmware sviluppato dagli ingegneri del traffico. Una volta reso operativo e chiuso l'armadio, il controller ha diverse modalità di conduzione.

Modalita pre-timed

Anche se il controller può lavorare in altre modalità può essere stato programmato per operare sempre in questa modalità. In questa modalità le tempistiche sono fisse e prestabiliti in base ad una tabella giornaliera, divisa per fasce orarie. Questa tabella che può essere modificata via remota, che in presenza, ma visto la lunghezza di questa tabella modificarla profondamente tramite l'interfaccia del controller non è operazione pratica. Perché questa complicazione? Pensate ad un incrocio trafficato da tanti pedoni nell'ora di pranzo, tanti lavoratori si muovono per raggiungere il posto preferito in cui fare pausa pranzo, allora il controller può privilegiare il traffico pedonale, sacrificando un poco quello veicolare.

Actuated

Si tratta della modalità dal funzionamento più complesso legata principalmente al numero di sensori interrati o meno che forniscono informazioni circa la quantità di auto in fila in attesa del verde, dall'altro lato ci sono solo 5 auto, quindi meglio dare il verde alla corsia con più auto. In questa modalità il sistema lavora regolando le tempistiche in base a vari sensori, tuttavia se qualcosa dovesse andare storta il sistema può commutare in modo misto o addirittura ripiegare sul modo pre-timed, resta sempre controllabile, interrogabile e monitorato da remoto.

Riflessioni

Osservando la demo di sumo si deduce che ci sono 10 lanterne, quindi 30 output, non contando i passaggi pedonali che nella demo non ci sono, e perché non ci sono? ci potrebbero essere?
Tutte domande a cui un ingegnere del traffico deve rispondere. Il tutto è affascinante, quindi non si tratta solo di accendere delle lucine in modo da imitare i semafori, ma di qualcosa che si comporti come nella realtà ma in piccolo, cioè al posto delle lanterne e dell'armadio, una scheda Arduino con abbastanza memoria flash e uno o due moduli 74hc595x3, un modulo 8 lanterne, 2 moduli 16 lanterne, io ho la picopurple con 16MB di flash e quindi no problem.

Ciao.

1 Like

Magari partendo dalla mia "macchina a stati finiti universale" si potrebbe fare qualcosa
PS grazie al Dinamico duo per averla revisionata
A buon rendere

Però bisogna mettere ancora qualche dettaglio

Sincrona con l'ora?
Serve un rtc

Programma sostituibile on fly?
Serve un lettore SD

Comandabile dall'esterno?
Serve una porta di comunicazione

Comunque
Vedo che i semafori vanno... hanno "mercato"

Io stesso avevo cominciato con quello

Come si dice:
Piatto ricco mi ci ficco?

Prego

Mi/ci dispiace di aver criticato alcune tue scelte, poi rivelatesi corrette

Il futuro (ma molto vicino...) penso che sia l'aggiustamento dei tempi tramite una sola telecamera posta in alto, al centro dell'incrocio, e l'IA che rileva le code e i pedoni in attesa. Dovrebbe anche dare rosso per tutti i veicoli finché l'incrocio non torna libero.

A nessuno di questi interrogativi ho una risposta precisa e motivata, però serve quanto meno provare a darsi una risposta.

Sicuramente è possibile, ma nonostante lo abbia letto 100 volte non sono riuscito a trarne alcuno spunto, si vedrà in futuro alla ennesima lettura.

Si penso che un RTC ci voglia nel caso in cui si decide per la tabella giornaliera.

No, almeno per adesso la vedo troppo complicata, dal mio specifico punto di vista, sulla pi-pico posso scrivere in flash memory a run-time, cosa scriverò però non lo so; codice binario, scripting, o semplice file di configurazione progressivo o meno. No a questa domando proprio non ho idea se si o se no, boooh forse? :grinning_face:
No un momento, ci ho ripensato, escludo la UNO, ma via libera per la mega che ha tanta flash e allora si per la SD, per altre schede come la pi-pico con 16MB sarebbe opzionale la SD.

Direi proprio di si, quanto meno la seriale. Se c'è solo il programma di questo giorno, che magari viene eseguito in 20 minuti, mi serve la seriale per inviare il programma del giorno seguente.

Io ho provato a rispondere, ma tra 10 minuti potrei rispondere diversamente, questo perché non ho le idee chiare e precise.
Di certo so che mi limito al controller pre-timed, quindi niente sensori, ma durante lo sviluppo del codice chi ne è capace può pensare di prevederli, alle volte certe cose vengono naturali e ti accorgi che il codice che hai sottomano si presta a modifiche che non lo stravolgono.

Altre domando a cui si dovrà rispondere riguardano l'hardware di attuazione, io propendo per lo shift register.


74hc595 x 3


Lanterne

L'unica preoccupazione riguarda il verde, in cui uno shift potrebbe controllare 3 verdi di 3 lanterne, 15mA x 3, siamo ancora dentro i 70mA massimi per devices.
Q0 verde
Q1 giallo
Q2 rosso
Q3 verde
Q4 giallo
Q5 rosso
Q6 verde
Q7 giallo

Peccato che nelle lanterne non abbiano previsto 3 BC817.

Ciao.

Quasi sicuramente ne bastano molti meno, con i led moderni 5 mA sono in genere già più che sufficienti.

Ciao, Ale.

Si era per non modificare o aggiungere altre resistenze. Al link su ali scrivono che il verde assorbe 15mA.

Piuttosto sto pensando alle schede con Vio 3.3V e lo shift alimentato a 5V, serve un traslatore per SPI o comunque uno generico 3.3->5.0, quale?
Volendo si può cablare anche il pin OE da collegare ad una uscita PWM, ma anche no, in ogni caso serve un traslatore anche per questo pin. Al posto dei traslatori open drain preferire trovare qualcosa che lavora anche come buffer, basata ad es. su "LVxT Family of Single Supply Translating Logic Gates" di texas.

Ciao.

Perchè a 5V?

Ciao, Ale.

Siete perdonati

Dopo saldo fattura 120gg

No so, credevo fosse meglio, cioè la resa luminosa non varia in base alla Vio della scheda, poi gli alimentatori a 5V sono più diffusi rispetto a quelli a 3.3V, pensavo di uniformare la resa, o forse qualcosa che ho letto sul datasheet del 595, a proposito sapevate che c'è la variante 74HCS595 con ingressi Trigger?

Io ad esempio non ho alimentatore a 3.3V, vabbé che basta uno step-down ma dovere specificare che serve alimentare a 3.3V quando si usa una scheda con Vio di 3.3V ecc. Inoltre quei traslatori sono fighi, perché lavorano da buffer e puoi facilmente allungare il bus.

Ci devo riflettere ora non so se è una buona scelta quella di alimentare fisso a 5V, l'unico buon motivo è che non cambia la resa.

Ciao.

Certamente
sipo per le uscite e piso per gli ingressi

Pochi pin e fatto

No, stai derapando
Arduino da il giusto comando
In sede di installazione è l'hardware di potenza che da potenza
Non è un problema di arduino

Così se domani hai il raffreddore e non puoi il semaforo non va?

Programma è sempre "programma completo"

Idee confuse vedo

No, si, no magari ci ripenso

Decido io:
Rtc si
Sd si

Codice binario è escluso
Se vogliamo poter cambiare al volo la programmazione da bordo auto di servizio serve script

Ecco il vero ostacolo
Creare un linguaggio di scripting che permetta di descrivere un incrocio e i suoi sensori
E anche descrivere gli stati delle pagine
E le successioni degli stati

Senza toccare il codice (che se vuole essere certificato non si può mica compilare alla bisogna e tocasciarlo ogni volta che si cambia idea)

No, non parliamo di certificazione, assolutamente qualcosa che si comporti bene sia simile al controller pro ma il suo scopo è didattico e non uscirà mai dal laboratorio.

Nel mio caso rimane opzionale, poiché scrivo in flash come fosse un file system.

Questa è una strada lontana per me, posso solo mostrarti parte di quello che ho sperimentato:

#define ENDL      255
// Dichiarazione e formazione dei gruppi.
uint8_t id0[] =       {0, ENDL};          // Lanterna a nord lss0, id=0
uint8_t id0_102[] =   {0, 6, ENDL};       // Lanterne id=0 e id=102
uint8_t id2[] =       {2, ENDL};          // Lanterna id=2
uint8_t id2_100[] =   {2, 4, ENDL};       // Lanterne id=2 e id=100
uint8_t id1_3_100[] = {1, 3, 4, ENDL};    // Lanterne id=1, id=3, id=100
uint8_t id1_3[] =     {1, 3, ENDL};       // Lanterne id=1 e id=3
uint8_t id101[] =     {5, ENDL};          // Lanterna id=101
uint8_t id101_102_103[] = {5, 6, 7, ENDL};  // Lanterne id=101, 102 e 103
uint8_t id101_103[] =     {5, 7, ENDL};             // Lanterne id=101 e 103

#define DIV 3

#define TPH0 30000UL  / DIV
#define TPH1 3000UL   / DIV
#define TPH2 1000UL   / DIV
#define TPH3 30000UL  / DIV
#define TPH4 3000UL   / DIV
#define TPH5 1000UL   / DIV
#define TPH6 30000UL  / DIV
#define TPH7 3000UL   / DIV
#define TPH8 1000UL   / DIV
#define TPH9 30000UL  / DIV
#define TPH10 3000UL  / DIV
#define TPH11 1000UL  / DIV

struct Phase {
    uint8_t *outs;
    uint32_t ms;
    
};

// Pre-timed phases
Phase phases[] = {
    
    // 4 lanterne stradali, e 4 pedonali.
    // Divisione in 12 PHASES
    // Si inizia sempre da nord che commuta su verde.
    { id0_102, TPH0 },       // GREEN,  PHASE 0    
    { id0, TPH1 },           // YELLOW, PHASE 1
    { id0, TPH2 },           // RED,    PHASE 2
    { id1_3_100, TPH3 },     // GREEN,  PHASE 3
    { id1_3, TPH4 },         // YELLOW, PHASE 4
    { id1_3, TPH5 },         // RED,    PHASE 5
    { id101_103, TPH6 },    // GREEN,  PHASE 6
    { id101_102_103, TPH7 },  // YELLOW,  PHASE 7
    { id101_102_103, TPH8},   // RED,     PHASE 8
    { id2, TPH9 },            // GREEN,   PHASE 9
    { id2_100, TPH10 },        // YELLOW,  PHASE 10
    { id2_100, TPH11 },         // RED,     PHASE 11
    { nullptr, 0 }
};

NOTA: All'accensione tutte le lanterne sono rosse per 2 secondi.
TPH0, 3, 6 e 9 sono i tempi di permanenza verde sul gruppo specificato prima,
tutti hanno lo stesso valore ma possono differire ovviamente. Anche gli altri tempi possono differire ma sono dipendenti dalla geometria dell'incrocio e dai limiti di velocità. Il giallo permane minimo 3 secondi e massimo 6, il rosso non ricordo con certezza ma diciamo 0 a 2, come specificato qui Sezione Red Clearance .
Nella struct Phase prima c'era anche il colore, rimosso perché ciclico:

    colorCounter++;
    colorCounter = colorCounter % 3;

phases dato in pasto ad una funzione va avanti all'infinito ripetendo le 12 fasi.
La cosa bella sarebbe se sumo potesse generare codice arduino specifico per ogni incrocio progettato e verificato al simulatore. In un colpo solo semplifichiamo lato arduino. Serve qualcuno capace con python e sumo.

Ciao.

Quali? Io probabilmente sono sfortunato, ne ho di 2 tipi e non ne funziona nemmeno uno.

Ciao, Ale.

Capisco, su ali non trovo questo tipo di porte logiche traslatori prodotti da Texas (TI)
Vai in fondo pagina a questo link:

e scarichi le varie AN

Di base si tratta di una famiglia (LVxT) di porte logiche in singolo contenitore.

Su ali c'è solo il TXS0108E di texas, hai provato anche quello?

Ciao.

Si, quello e quegli altri con i fet e le resistenze.
Se usi la stessa Vcc per l'MCU mi pare la meglio, puoi anche cambiarle tra 5 e 3.3V, tutto continuerà a funzionare.
Per la luminosità e corrente dei led non starei molto a preccuparmi, tempo stavo testando dei led di un assortimento comprato su Amazon (vari colori e 3/5 mm), facevo delle prove con l'alimentatore e delle resistenze per vedere il minimo di corrente che potevo usare, succede che con il catodo collegato al negativo tocco con una mano il positivo dell'alimentatore e con l'altra l'anodo del led, e questo si accende (debolmente, ma si accende!).

Ciao, Ale.

Ok, la mi esperienza pratica con il led è molto diversa, ad esempio, ho un carica batteria per cellulare che ha un led rosso che mi fa luce quando di notte mi alzo dal letto per fare plin plin. Che cacchio di led è, mai capitato di comprare un led simile.
Stessa cosa con del led 3mm smontati da lavatrice LG, rosso pazzesco con 5mA, compro gli stessi (all'apparenza) led e delusione, neanche spinti a 20mA fanno la stessa luce. Scopro poi comunque che dello stesso led (liteon) produce smd da 6mc a 16 mc, cambia di poco la sigla, ma cambia tantissimo la luminosità.

Comunque appena mi decido compro quelle lanterne su ali e vediamo che ciofeca sono.

Ciao.

1 Like