iena
Posts: 19
Joined: Mon Feb 04, 2019 10:11 pm

LCD e processi paralleli

Mon Feb 04, 2019 10:31 pm

Ciao a tutti.

Sono nuovo nel mondo di Python e Raspberry ma non sono nuovo al mondo della programmazione: da più i 10 anni programmo in ambiente .NET

Mi sono messo in testa di fare una sorta di "giochino" che mi registri l'orario dei passaggi davanti ad una fotocellula e mi faccia vedere su un display l'orario corrente (aggiornato al secondo) e l'orario degli ultimi 4 passaggi.

Ora il display è un classico LCD 4x20 retroilluminato pilotato da un I2C. Non ho avuto particolari problemi a inizializzarlo e a scriveci sopra quello che mi serve.

Ho fatto una funzione che mi mostra sulla prima riga del display l'ora corrente e funziona correttamente.

Non avendo a disposizione immediata una fotocellula, ho fatto una seconda funzione che, a intervalli di due secondi, mi mostra l'orario sulla terza riga del display

Code: Select all

# aggiorno l'orario
def set_display_clock():
    now = datetime.datetime.now()
    while now.second != datetime.time.second:
        now = datetime.datetime.now()
        update_time_line(now)
        
# ogni 2 secondi scrivi l'ora sulla terza riga
def set_new_time():
    while True:
        add_display_time(datetime.datetime.now())
        time.sleep(2)
Il problema sorge quando devo fare girare questi thread in parallelo: sia usando il threading che usando il multiprocessing, quando la seconda funzione viene triggerata mi manda in palla il programma e di conseguenza il display.

Faccio così: dichiaro i due thread o i due processi esternamente al main()

Code: Select all

thread1 = multiprocessing.Process(target=set_display_clock)
thread2 = multiprocessing.Process(target=set_new_time)
E poi all'interno del main() provo a farli partire

Code: Select all

def main():
        
    # Initialise display
    lcd_init() 
    
    thread1.start()        
    thread2.start()
    
    while True:
        ciclo_infinito = 1
        
Ipotizzo che il problema sia nell'utilizzo delle risorse della GPIO o dell'I2C, ma data la mia ignoranza sull'argomento, non son riuscito a capire che strada devo seguire per risolvere questo problema. Anche perché inizialmente, essendo abituato con C#, ho avuto delle difficoltà a capire quale sia lo scope delle variabili.

Qualche idea/suggerimento?

Se servisse posso postare l'intero codice usato e la tipologia di display utilizzato.

Grazie

macca
Posts: 115
Joined: Tue Oct 16, 2012 9:14 am

Re: LCD e processi paralleli

Tue Feb 05, 2019 3:40 pm

La soluzione più semplice è non utilizzare i thread! A meno che il tuo programma finale non sia estremamente complesso, l'uso dei thread si può benissimo evitare.

Se proprio vuoi usare due thread, il problema probabilmente è che ad un certo punto entrambi vogliono pilotare il display e quindi lo incasinano. Devi usare una semaforica per accedere in modo esclusivo alla "risorsa" display da un thread in modo che l'altro aspetti il suo turno senza interferire. Banalmente basta una variable tipo displayOccupato = true/false ma sarebbe opportuno usare strumenti dedicati per evitare race conditions. Non sono molto avezzo a python ma credo che ci sia una classe Semaphore fatta per questo.

Ovviamente senza thread il problema non si pone.

iena
Posts: 19
Joined: Mon Feb 04, 2019 10:11 pm

Re: LCD e processi paralleli

Tue Feb 05, 2019 10:42 pm

Domanda: ma come posso io fare due operazioni in contemporanea senza usare i thread?
Intendo: da una parte devo visualizzare un orologio con aggiornamento al secondo, dall'altra devo restare in "ascolto" su una porta in attesa di un segnale (cambio di stato) dalla fotocellula.

Come posso ottenere ciò senza thread? Se ci sono suggerimenti benvenga :-)

nicolap8
Posts: 303
Joined: Mon Mar 13, 2017 9:45 pm

Re: LCD e processi paralleli

Tue Feb 05, 2019 11:06 pm

Tante cose si possono fare in tanti modi diversi ma... dipende!
Se i due processi devono avere temporizzazioni molto precise ed essere indipendenti (cioè non darsi fastidio) allora bisogna servirsi dei thread perché esistono proprio per questo.
Però se puoi permetterti qualche piccolo ritardo (il display lo guarda un umano, con tempi misurati in decimi di secondo) allora puoi evitare i thread e usare dei timer per sincronizzare i due processi.
Un sistema molto usato è quello delle macchine a stati: se disegnate bene possono fare miracoli!

Quindi: determina con precisione quanto tempo di CPU serve ai tuoi processi, se e quanto possono essere interrotti, quanto jitter puoi permetterti, etc. Poi decidi cosa usare.

iena
Posts: 19
Joined: Mon Feb 04, 2019 10:11 pm

Re: LCD e processi paralleli

Wed Feb 06, 2019 8:21 am

Rispetto all'orologio interno del Raspi, un thread deve avere una precisione al decimo di secondo, l'altro al centesimo.

Mi spiego.

Un thread mi deve mostrare a display l'ora attuale. Questo orario è l'orario "ufficiale" del gioco, quello che i concorrenti usano per sincronizzare i propri cronometri.

L'altro thread invece è quello che resta "in ascolto" in attesa che scatti la fotocellula e la rilevazione di questo orario deve essere precisa al centesimo di secondo mentre la visualizzazione a display può anche attendere qualche istante.

Interessante il discorso sulle macchine a stati. Le ho sempre viste solo a livello teorico senza mai realmente applicarle ...

Qualche suggerimento?

Grazie mille :-)

macca
Posts: 115
Joined: Tue Oct 16, 2012 9:14 am

Re: LCD e processi paralleli

Wed Feb 06, 2019 9:10 am

iena wrote:
Tue Feb 05, 2019 10:42 pm
Domanda: ma come posso io fare due operazioni in contemporanea senza usare i thread?
Una dopo l'altra ?
L'esempio che hai postato usa ben tre thread che perdono la maggior parte del tempo ad aspettare qualcosa: set_display_clock aspetta che passi un secondo, set_new_time aspetta due secondi, il terzo poi (il main) è un loop vuoto che perde solo tempo. Puoi mettere tutto insieme uno dopo l'altro con delle variabili che ti indicano cosa fare.
In ogni caso il tuo problema è l'accesso al display che deve per forza essere serializzato in qualche modo.
iena wrote: Rispetto all'orologio interno del Raspi, un thread deve avere una precisione al decimo di secondo, l'altro al centesimo.
Se ti serve una tale precisione, forse è meglio ricorrere agli interrupt.
Questo dovrebbe dare un'indicazione su come procedere:
https://raspi.tv/2013/how-to-use-interr ... pio-part-3

iena
Posts: 19
Joined: Mon Feb 04, 2019 10:11 pm

Re: LCD e processi paralleli

Wed Feb 06, 2019 9:29 am

macca wrote:
Wed Feb 06, 2019 9:10 am
Una dopo l'altra ?
L'esempio che hai postato usa ben tre thread che perdono la maggior parte del tempo ad aspettare qualcosa: set_display_clock aspetta che passi un secondo, set_new_time aspetta due secondi, il terzo poi (il main) è un loop vuoto che perde solo tempo. Puoi mettere tutto insieme uno dopo l'altro con delle variabili che ti indicano cosa fare.
Sì sì, l'esempio è solo una versione "semplificata" dove facevo delle prove per accesso concorrenziale alla risorsa. Nel mio messaggio successivo ho specificato il reale funzionamento

nicolap8
Posts: 303
Joined: Mon Mar 13, 2017 9:45 pm

Re: LCD e processi paralleli

Thu Feb 07, 2019 9:39 pm

Visto che l'obiettivo è, mi pare di capire, imparare qualcosa di nuovo, usa i thread!
Non sono poi così complicati, tranne per quanto riguarda la comunicazione tra di loro, e rappresentano un oggetto potente e versatile.
N

iena
Posts: 19
Joined: Mon Feb 04, 2019 10:11 pm

Re: LCD e processi paralleli

Sat Feb 09, 2019 12:20 am

macca wrote:
Wed Feb 06, 2019 9:10 am
Se ti serve una tale precisione, forse è meglio ricorrere agli interrupt.
Questo dovrebbe dare un'indicazione su come procedere:
https://raspi.tv/2013/how-to-use-interr ... pio-part-3
Questa cosa degli interrupts mi ha aperto un mondo.

Ora non mi resta che risolvere il problema dell'accesso simultaneo al display senza andare a rendere instabile il realtime della visualizzazione dell'ora.

iena
Posts: 19
Joined: Mon Feb 04, 2019 10:11 pm

Re: LCD e processi paralleli

Sun Feb 10, 2019 11:47 am

macca wrote:
Wed Feb 06, 2019 9:10 am
Se ti serve una tale precisione, forse è meglio ricorrere agli interrupt.
Questo dovrebbe dare un'indicazione su come procedere:
https://raspi.tv/2013/how-to-use-interr ... pio-part-3
Dunque, ho rifatto tutto con gli interrupts, però ho un problema: da quando interrompo il fascio di luce della fotocellula a quando mi viene fatto il timestamp, passa anche mezzo secondo.

Mi spiego: tramite un ciclo mi stampo a video l'ora corrente aggiornata ogni secondo

Code: Select all

    while True:
        now = datetime.datetime.now()
        
        if now.second != previous.second:
            print(now)
            previous = now
            time.sleep(0.1)
In questo modo vedo esattamente l'orario di sistema.

Code: Select all

2019-02-10 12:19:50.000005
2019-02-10 12:19:51.000011
2019-02-10 12:19:52.000002
2019-02-10 12:19:53.000002
2019-02-10 12:19:54.000007
2019-02-10 12:19:55.000007
2019-02-10 12:19:56.000007
2019-02-10 12:19:57.000007
2019-02-10 12:19:58
2019-02-10 12:19:59.000004
La precisione mi soddisfa.

Ora, se poco prima dello scattare il secondo io faccio scattare la fotocellula, il tempo segnato è sempre successivo a quanto atteso.

Questo il mio codice per l'interrupt:

Code: Select all

def my_callback(channel):
    time_to_write = datetime.datetime.now()
    print('** {}.{} **'.format(str(time_to_write.second).zfill(2), str(time_to_write.microsecond)[0:2]))
    
GPIO.add_event_detect(16, GPIO.FALLING, callback=my_callback, bouncetime=500)
E questo l'output. Ho fatto scattare la fotocellula poco prima del secondo "28"

Code: Select all

2019-02-10 12:20:26.000012
2019-02-10 12:20:27.000002
2019-02-10 12:20:28.000003
 ** 28.20 **
2019-02-10 12:20:29.000008
2019-02-10 12:20:30.000001
2019-02-10 12:20:31.000010
Sinceramente non so se il problema sia a livello hardware o se magari sto sbagliando qualcosa in fase di programmazione ... Le specifiche della fotocellula mi danno un massimo di 18ms di ritardo. Quello che vedo a monitor è decisamente troppo.

macca
Posts: 115
Joined: Tue Oct 16, 2012 9:14 am

Re: LCD e processi paralleli

Mon Feb 11, 2019 9:51 am

iena wrote:
Sun Feb 10, 2019 11:47 am
Ora, se poco prima dello scattare il secondo io faccio scattare la fotocellula, il tempo segnato è sempre successivo a quanto atteso.
Scusa, ma chi sei, l'uomo bionico ? Come fai ad essere certo che non sei tu a muoverti troppo lentamente di fronte alla fotocellula (suppongo sia tu a far scattare la fotocellula con una mano o altro) ?

Queste misure non si fanno così. Se vuoi misurare il tempo di reazione, usa un altro GPIO come controllo. Quando ricevi l'evento dalla fotocellula, metti un altro GPIO a 1. Collega ad entrambi un analizzatore logico e misura il tempo intercorso tra i due cambi di stato. Se non hai la strumentazione adatta, il mio consiglio è di non fasciarti la testa prima di essertela rotta, secondo me ti stai mettendo un sacco di problemi senza ancora nemmeno aver scritto una riga di codice definitivo. Fai il tuo programma, provalo "sul campo" e vedi come va, magari il Raspberry non è adatto a quello che vuoi fare, non è un sistema realtime.

iena
Posts: 19
Joined: Mon Feb 04, 2019 10:11 pm

Re: LCD e processi paralleli

Mon Feb 11, 2019 10:44 am

So che la misura non è precisa, ma, dando per buono il timestamp che io vedo a monitor, nel momento in cui io faccio scattare la fotocellula circa 50 cent prima che scatti il secondo, vedo che il timestamp segnato è di almeno 20 cent SUCCESSIVO allo scattare del secondo.

Per capirsi: faccio scattare la fotocellula circa alle 11:36:20.500 (mi aspetto un errore inferiore a 0.1 secondi) ma il timestamp che vedo a monitor è circa delle 11:36:21.200

Ripeto: so per certo che la misura non è precisa, ma, facendo gare di regolarità, ho una buona dimestichezza con le fotocellule e con i cronometri. So stimare un ritardo o un anticipo sulla fotocellula nell'ordine dei decimi di secondo. Sono piuttosto sicuro della mia affermazione. Qua si parla di ritardi clamorosi nell'ordine del mezzo secondo.

In effetti la cosa che mi preoccupa è che sia proprio il raspberry a non essere adeguato, anche se, facendo semplicemente accendere un led allo scattare della fotocellula, la reazione che vedo è pressoché istantanea, ben lontana dal ritardo che vedo nel mio timestamp.

Grazie comunque del suggerimento. Solo una domanda: cos'è un analizzatore logico? Si tratta di strumentazione hardware o di qualche programma?

iena
Posts: 19
Joined: Mon Feb 04, 2019 10:11 pm

Re: LCD e processi paralleli

Mon Feb 11, 2019 11:17 am

Aggiungo un po' di info relative alla ricerca che sto facendo.

https://www.tablix.org/~avian/blog/arch ... nse_times/

Da quanto emerge da questi esperimenti pare che la mia latenza decisamente non sia una questione hardware.

Anche perché i ritardi che ho rilevato non sarebbero accettabili nel retrogaming.

Mi sa che mi conviene rimettermi a studiare :D

macca
Posts: 115
Joined: Tue Oct 16, 2012 9:14 am

Re: LCD e processi paralleli

Mon Feb 11, 2019 11:26 am

iena wrote:
Mon Feb 11, 2019 10:44 am
Ripeto: so per certo che la misura non è precisa, ma, facendo gare di regolarità, ho una buona dimestichezza con le fotocellule e con i cronometri. So stimare un ritardo o un anticipo sulla fotocellula nell'ordine dei decimi di secondo. Sono piuttosto sicuro della mia affermazione. Qua si parla di ritardi clamorosi nell'ordine del mezzo secondo.

In effetti la cosa che mi preoccupa è che sia proprio il raspberry a non essere adeguato, anche se, facendo semplicemente accendere un led allo scattare della fotocellula, la reazione che vedo è pressoché istantanea, ben lontana dal ritardo che vedo nel mio timestamp.
Ok, si tratta forse di misurare degli intervalli di tempo ? Perchè potresti provare a vedere se il ritardo si mantiene costante (o comunque entro una tolleranza accettabile) e utilizzare la differenza tra le due letture anzichè il valore assoluto dell'orologio.

Ci sono anche altre possibilità software che possono migliorare la situazione. Qui usi Python che aggiunge un layer di complessità ad un sistema che è già piuttosto complesso, si può provare a fare un programma in C per togliere un po' di strati, fino ad arrivare al cosiddetto "bare metal" cioè far andare un programma senza sistema operativo con accesso diretto all'hardware (come accade per esempio con una board tipo Arduino). Ma non è certamente una cosa da principianti.
Grazie comunque del suggerimento. Solo una domanda: cos'è un analizzatore logico? Si tratta di strumentazione hardware o di qualche programma?
E' un hardware che registra i cambi di stato delle linee di I/O e permette di visualizzare un grafico, calcolare gli intervalli di tempo tramite un software apposito. Non è uno strumento molto comune specie se non ti occupi di hardware a livello professionale.

nicolap8
Posts: 303
Joined: Mon Mar 13, 2017 9:45 pm

Re: LCD e processi paralleli

Mon Feb 11, 2019 8:46 pm

Così a occhio il problema è che fai l'aggiornamento del display DENTRO l'interrupt: queste cose non si fanno MAI! E intendo proprio MAI.
L'interrupt deve essere sempre una routine semplice, veloce e che usa il minimo indispensabile di risorse. Dentro puoi settare un flag e mettere via il tempo da mostrare, nient'altro.
Poi in un ciclo "idle" controlli il flag e se settato ti prendi il valore e lo resetti. Quindi puoi fare tutte le elaborazioni che servono con (relativa) calma.

nicolap8
Posts: 303
Joined: Mon Mar 13, 2017 9:45 pm

Re: LCD e processi paralleli

Mon Feb 11, 2019 8:51 pm

macca wrote:
Mon Feb 11, 2019 11:26 am
iena wrote:
Mon Feb 11, 2019 10:44 am
Solo una domanda: cos'è un analizzatore logico? Si tratta di strumentazione hardware o di qualche programma?
E' un hardware che registra i cambi di stato delle linee di I/O e permette di visualizzare un grafico, calcolare gli intervalli di tempo tramite un software apposito. Non è uno strumento molto comune specie se non ti occupi di hardware a livello professionale.
Questo: http://dangerousprototypes.com/blog/open-logic-sniffer/
Ti permette di fare un'analisi su linee dati, in particolare di protocolli di comunicazione.
È un giocattolo carinissimo ;-) ma un po' complesso.

iena
Posts: 19
Joined: Mon Feb 04, 2019 10:11 pm

Re: LCD e processi paralleli

Tue Feb 12, 2019 7:42 am

nicolap8 wrote:
Mon Feb 11, 2019 8:46 pm
Così a occhio il problema è che fai l'aggiornamento del display DENTRO l'interrupt: queste cose non si fanno MAI! E intendo proprio MAI.
L'interrupt deve essere sempre una routine semplice, veloce e che usa il minimo indispensabile di risorse. Dentro puoi settare un flag e mettere via il tempo da mostrare, nient'altro.
Poi in un ciclo "idle" controlli il flag e se settato ti prendi il valore e lo resetti. Quindi puoi fare tutte le elaborazioni che servono con (relativa) calma.
Grazie mille. Questo è un ottimo suggerimento. In effetti è la prima volta che ho a che fare con dell'hardware (sono un programmatore software, mi occupo di elaborazione dati da/per DB e fatturazione, pens te che [email protected]) e non ho mai avuto a che fare con interrupt e affini. Questo tipo di suggermenti per me sono oro :-)

iena
Posts: 19
Joined: Mon Feb 04, 2019 10:11 pm

Re: LCD e processi paralleli

Tue Feb 12, 2019 7:44 am

nicolap8 wrote:
Mon Feb 11, 2019 8:51 pm
Questo: http://dangerousprototypes.com/blog/open-logic-sniffer/
Ti permette di fare un'analisi su linee dati, in particolare di protocolli di comunicazione.
È un giocattolo carinissimo ;-) ma un po' complesso.
Fico. Grazie mille. Mi sa che dovrò rompere le scatole a quei miei compagni di università che non sento da una decina di anni che so che si occupano di queste cose :P

nicolap8
Posts: 303
Joined: Mon Mar 13, 2017 9:45 pm

Re: LCD e processi paralleli

Tue Feb 12, 2019 9:03 pm

iena wrote:
Tue Feb 12, 2019 7:42 am
nicolap8 wrote:
Mon Feb 11, 2019 8:46 pm
Così a occhio il problema è che fai l'aggiornamento del display DENTRO l'interrupt: queste cose non si fanno MAI! E intendo proprio MAI.
L'interrupt deve essere sempre una routine semplice, veloce e che usa il minimo indispensabile di risorse. Dentro puoi settare un flag e mettere via il tempo da mostrare, nient'altro.
Poi in un ciclo "idle" controlli il flag e se settato ti prendi il valore e lo resetti. Quindi puoi fare tutte le elaborazioni che servono con (relativa) calma.
Grazie mille. Questo è un ottimo suggerimento. In effetti è la prima volta che ho a che fare con dell'hardware (sono un programmatore software, mi occupo di elaborazione dati da/per DB e fatturazione, pens te che [email protected]) e non ho mai avuto a che fare con interrupt e affini. Questo tipo di suggermenti per me sono oro :-)
Prego!
Io mi occupo di questo e quello: in questi giorni litigo con la (maledetta) fattura elettronica :-(

iena
Posts: 19
Joined: Mon Feb 04, 2019 10:11 pm

Re: LCD e processi paralleli

Wed Feb 13, 2019 9:05 am

nicolap8 wrote:
Tue Feb 12, 2019 9:03 pm
Io mi occupo di questo e quello: in questi giorni litigo con la (maledetta) fattura elettronica :-(
Se ti servono info a riguardo contattami pure: ormai sono cintura nera di Fattura Elettronica. Ho sviluppato un prodotto interno per la mia azienda (con tanto di canale sullo SdI) e mi occupo della fattura elettronica per alcuni clienti che sparano fuori qualcosa come 2500/3000 fatture per ogni job e ne lanciano almeno 3 o 4 a settimana :-)

iena
Posts: 19
Joined: Mon Feb 04, 2019 10:11 pm

Re: LCD e processi paralleli

Mon Feb 18, 2019 8:57 am

Alla fine ho risolto in questo modo:

Il thread principale in un loop mi aggiorna l'orario ufficiale a display.

Un interrupt scatenato dalla fotocellula mi salva l'orario su una variabile.

Un secondo thread verifica se l'orario sulla variabile globale è cambiato e, in tal caso, mi aggiorna il passaggio sul display.

Ho dovuto giocare un po' con i vari sleep dei cicli in quanto mi sono accorto che, se l'interrupt della fotocellula cadeva finché il thread principale era in sleep, il timestamp mi veniva salvato solo all'istante subito successivo all'aggiornamento dell'orario.

Inoltre ho aggiunto un buzzer che ad ogni secondo mi fa un rumore tipo "clock" che mi permette di sincronizzare in maniera parecchio precisa il cronometro.

Come primo esperimento con Raspi + Python posso ritenermi soddisfatto. Vediamo ora di provarlo sul campo :-) poi dovrò capire come "inscatolare" il tutto per rendelo trasportabile.

Grazie a tutti per il supporto.

Return to “Italiano”