Controllo accessi con badge RFID. Integrazione avanzata con Raspberry Pi e Telegram

Introduzione

Controllo accessi con badge RFID: un sistema avanzato per la gestione degli accessi e dei permessi supportato dalla potenza della Raspberry Pi. Questa soluzione integrata è progettata per offrire un controllo degli accessi efficiente e flessibile, utilizzando la tecnologia RFID per garantire un’identificazione sicura e rapida degli utenti.

Al centro di questo ecosistema si trova un database SQLite3, un’opzione ideale per la memorizzazione e la gestione dei dati degli utenti e dei permessi. SQLite3 offre una combinazione unica di leggerezza, affidabilità e flessibilità, consentendo un rapido accesso ai dati e una gestione ottimizzata delle informazioni senza la necessità di un server separato.

Oltre alla gestione dei dati, il sistema include anche un server API REST realizzato tramite Flask che espone funzionalità CRUD per la gestione della tabella degli utenti. CRUD sta per Create, Read, Update e Delete (Crea, Leggi, Aggiorna e Cancella) cioè le operazioni principali che in genere si fanno su un record di un database. Questo permette agli amministratori di modificare dinamicamente le autorizzazioni degli utenti. Oltre a questo, le API REST consentono di visualizzare il registro degli accessi per un dato giorno. L’interfaccia API REST offre un’esperienza intuitiva e scalabile per l’interazione con il sistema, consentendo una facile integrazione con altre applicazioni e sistemi.

Inoltre, il sistema è arricchito da un bot Telegram dedicato, che consente agli utenti di ricevere notifiche istantanee sugli accessi tramite messaggi diretti e di eseguire comandi CRUD sulla tabella degli utenti (come quelle disponibili con le API REST) direttamente dalla piattaforma di messaggistica. Questo aggiunge un livello di flessibilità e accessibilità al sistema, consentendo agli utenti di gestire gli accessi ovunque si trovino.

Da un punto di vista tecnico, l’intero sistema è virtualizzato tramite Virtualenv (di cui parleremo più avanti), garantendo un ambiente isolato e gestibile per l’esecuzione delle applicazioni Python sulla Raspberry Pi. Questo approccio consente una maggiore sicurezza e stabilità del sistema, assicurando un’esperienza affidabile e coerente per gli utenti.

In sintesi, il controllo accessi RFID rappresenta una soluzione completa e all’avanguardia per la gestione degli accessi e dei permessi, integrando tecnologie avanzate come RFID, SQLite3, API REST e bot Telegram per offrire un’esperienza completa e altamente personalizzabile agli utenti finali.

Descrizione del funzionamento del controllo accessi con badge

Il nostro progetto di sicurezza domestica si basa su un sistema automatizzato che integra tecnologie avanzate per garantire l’accesso sicuro e controllato. Ecco come funziona:

  1. Inizializzazione del Sistema: Il sistema contiene sul database una tabella di nome users che contiene, per ogni riga, l’UUID di un utente, il suo nome e i suoi privilegi di accesso (“g” per granted e “d” per denied). Ogni volta che un tag RFID viene rilevato dal lettore, il sistema legge questa tabella e la carica su un dizionario (di cui parleremo in uno dei successivi paragrafi). Quindi il dizionario viene sempre aggiornato perché la tabella potrebbe essere stata modificata nel frattempo (per esempio ad un utente potrebbero essere stati cambiati i privilegi di accesso). Inoltre il sistema dispone di un piccolo buzzer (che verrà brevemente descritto più avanti) che emetterà un segnale acustico ogni volta che il lettore RFID ha rilevato un tag RFID (cioè un badge o un portachiavi dotato di tag RFID).
  1. Identificazione dell’Utente: Quando un badge viene avvicinato al modulo RFID, il sistema legge l’UUID (Universally Unique Identifier) del badge. Questo UUID è un identificatore univoco per ogni badge, utilizzato per distinguere gli utenti.
  1. Controllo delle Autorizzazioni: Dopo aver letto l’UUID, il sistema controlla il dizionario (che è stato appena aggiornato con la lettura della tabella users del database) per determinare se l’utente ha il permesso di accedere.
  1. Decisione di Accesso: In base al permesso associato all’UUID, il sistema decide se far scattare il relè, consentendo o negando l’accesso. Se l’UUID è associato a un permesso concesso (“g”), il relè si attiva, consentendo l’accesso. Se il permesso è negato (“d”), l’accesso viene negato. Inoltre, nel primo caso si accende un LED verde mentre nel secondo caso si accende un LED rosso. Il LED rosso rimane acceso anche quando il sistema è nello stato “inattivo” cioè sta aspettando che venga avvicinato un tag RFID al lettore.
  1. Registrazione degli Accessi: Ogni tentativo di accesso viene registrato sul database nella tabella accessusers . Questo fornisce un registro temporale degli accessi, utile per il monitoraggio e la sicurezza. Per ogni riga viene registrato l’UUID, la data, l’ora e se l’ingresso è stato concesso (“g”) o negato (“d”). Nel caso lo UUID non fosse presente nella tabella users, il tentativo di accesso verrebbe ugualmente registrato nel database ma con privilegio “n” (che sta per not recognized) e l’ingresso verrebbe negato (in quanto UUID sconosciuto al sistema).
  2. Notifiche Telegram: Contemporaneamente al tentativo di accesso (che può andare a buon fine o no), il sistema invia un messaggio di avviso al bot Telegram. Questo permette agli utenti di ricevere notifiche in tempo reale sugli accessi, aumentando il controllo e la sicurezza.
  3. Gestione delle Autorizzazioni tramite API REST e tramite bot Telegram: Il sistema è ulteriormente integrato con un server che espone API REST per la gestione delle autorizzazioni. Queste API permettono di leggere la tabella delle autorizzazioni, aggiungere un nuovo utente con i suoi privilegi, modificare i privilegi di un utente esistente o eliminare un utente ma anche leggere gli accessi avvenuti in un dato giorno. Tramite il bot Telegram si potrà aggiungere un nuovo utente (UUID) con i suoi privilegi o modificare i privilegi di un utente già esistente, eliminare un utente, leggere i privilegi di accesso di un dato utente. In entrambi i casi (API REST o bot Telegram) le modifiche alle autorizzazioni saranno permanenti in quanto verranno salvate sul database.

Cosa è la tecnologia RFID

Il Radio-Frequency Identification (RFID) è un sistema di identificazione automatica che utilizza campi elettromagnetici per trasferire dati tra un lettore e una tag RFID. Questa tecnologia è basata sulla comunicazione senza contatto e consente la memorizzazione e il recupero di informazioni da oggetti, animali o persone dotati di tag RFID. Ecco alcuni punti chiave relativi al funzionamento e all’utilizzo dell’RFID:

  1. Principio di funzionamento:
    • Un sistema RFID è composto da un lettore (o interrogatore) e una o più tag RFID.
    • La tag contiene un chip che memorizza un codice univoco o altre informazioni.
    • Quando la tag si trova nel campo elettromagnetico del lettore, riceve energia e trasmette i dati memorizzati al lettore.
  2. Frequenze di funzionamento:
    • Gli RFID operano a diverse frequenze, suddivise principalmente in bassa frequenza (LF), alta frequenza (HF), ultra alta frequenza (UHF), e frequenza molto alta (VHF).
    • Le diverse frequenze influenzano la distanza di lettura e la capacità di penetrare attraverso materiali.
  3. Tipi di tag RFID:
    • Le tag possono essere attive (con batteria) o passive (senza batteria).
    • Le tag passive sono alimentate dal campo del lettore e hanno una distanza di lettura più breve rispetto alle tag attive.
  4. Applicazioni comuni:
    • Controllo degli accessi: badge RFID per aperture automatiche o autenticazioni di sicurezza.
    • Logistica e tracciabilità: monitoraggio di merci in transito attraverso magazzini e catene di distribuzione.
    • Pagamenti contactless: sistemi di pagamento senza contatto come le carte contactless.
  5. Sicurezza e privacy:
    • L’RFID può presentare sfide legate alla sicurezza e alla privacy, con la possibilità di intercettare e clonare i dati.
    • Sono state sviluppate varie tecniche di crittografia per proteggere le informazioni scambiate tra i lettori e le tag.
  6. Implementazione con la Raspberry PI:
    • La Raspberry PI può essere utilizzata come lettore RFID, interfacciandosi con un modulo RFID compatibile.
    • Le informazioni lette dalla tag RFID possono essere utilizzate per attivare specifiche azioni o accedere a risorse.

L’utilizzo di RFID offre un’efficace e conveniente soluzione per molteplici applicazioni, consentendo l’automazione di processi e migliorando l’efficienza operativa.

Il kit RFID impiegato in questo progetto

Il kit, distribuito dall’azienda AZ-DELIVERY, si compone di un lettore RFID, un badge, un portachiavi (sempre RFID), due tipi di connettori da saldare sul lettore (uno con i contatti dritti e uno con i contatti ad angolo retto). Se non hai dimestichezza col mondo dei saldatori e vuoi provare a saldare il connettore in autonomia, ti consiglio di dare uno sguardo all’articolo Un altro tutorial su come saldare.

Sia il badge che il portachiavi contengono un codice che può essere letto dal lettore RFID. Nel mio caso il badge ha codice esadecimale 73051e99f1 mentre il portachiavi ha codice esadecimale a305cd0b60.

Il modulo di lettura RFID si interfaccia alla Raspberry PI tramite comunicazione SPI di cui parleremo nel paragrafo successivo.

Nella foto successiva potrai vedere il kit completo:

Kit RFID completo
Kit RFID completo

Interfaccia SPI

Il modulo lettore RFID usa l’interfaccia SPI, che è un protocollo seriale di comunicazione a 4 fili comunemente utilizzato in progetti embedded. Questi quattro fili sono:

  1. MISO (Master In Slave Out): questo è il pin attraverso il quale il modulo riceve dati dal dispositivo master, che di solito è l’Arduino o un altro microcontrollore.
  2. MOSI (Master Out Slave In): questo è il pin attraverso il quale il modulo invia dati al dispositivo master.
  3. SCK (Serial Clock): questo è il pin del clock che sincronizza la trasmissione dei dati tra il modulo e il dispositivo master.
  4. CS (Chip Select): questo pin viene utilizzato per selezionare il modulo RFID e inizializzare le operazioni di invio/ricezione dei dati.

Il buzzer


Il buzzer è un componente elettronico ampiamente utilizzato per generare segnali acustici. Esistono due tipi principali di buzzer, attivi e passivi:

  • Buzzer passivi: funzionano in modo simile agli altoparlanti: richiedono un segnale di ingresso, generalmente sotto forma di un’onda quadra PWM, per produrre suoni. Questo segnale può essere variato in ampiezza e frequenza per ottenere toni diversi, consentendo di suonare melodie e generare segnali sonori più complessi rispetto ai buzzer attivi.
  • Buzzer attivi: sono dotati di un integrato interno che genera una frequenza predefinita quando alimentati, senza la necessità di fornire un segnale di ingresso esterno. In pratica, basta applicare l’alimentazione per farli suonare alla frequenza prestabilita.

Il buzzer utilizzato in questo progetto è di tipo attivo.

Cosa è Flask


Flask è un framework leggero per la creazione di applicazioni web in Python. È flessibile, semplice da usare e offre un’ampia gamma di funzionalità per lo sviluppo di server di API RESTful.

Essenzialmente, Flask permette di definire delle “routes” o percorsi URL che corrispondono alle richieste dei client. Ogni route è associata ad una funzione Python che elabora la richiesta e restituisce una risposta al client. Questo approccio rende facile creare endpoint per le API REST, in cui le richieste HTTP (come GET, POST, PUT, DELETE) sono gestite in modo appropriato per accedere e manipolare le risorse del server.

Flask è basato su Werkzeug, un toolkit WSGI, e Jinja2, un motore di templating per Python. Utilizza un approccio “micro”, il che significa che fornisce solo il minimo indispensabile per creare applicazioni web, lasciando al programmatore la libertà di estendere le funzionalità secondo le proprie esigenze.

Grazie alla sua semplicità e flessibilità, Flask è ampiamente utilizzato per la creazione di server di API REST, fornendo un modo efficiente e scalabile per gestire le comunicazioni tra client e server in applicazioni web e servizi basati su microservizi. Più in particolare:

  1. Framework leggero: Flask è progettato per essere leggero e non intrusivo. Non impone una struttura rigida per le tue applicazioni, ma fornisce solo gli strumenti essenziali per creare server web.
  2. Routing flessibile: Con Flask, è possibile definire facilmente dei percorsi URL, noti come “routes”, che corrispondono alle richieste dei client. Ogni route è associata ad una funzione Python, chiamata “view function”, che elabora la richiesta e restituisce una risposta.
  3. Gestione delle richieste HTTP: Flask offre un’interfaccia intuitiva per gestire le varie operazioni HTTP come GET, POST, PUT, DELETE e altre. Questo consente di creare facilmente endpoint per le API REST, in cui le richieste HTTP sono elaborate in modo appropriato per accedere e manipolare le risorse del server.
  4. Estensioni modulari: Flask supporta un sistema di estensioni che consente di aggiungere funzionalità aggiuntive all’applicazione in modo modulare. Ci sono estensioni disponibili per quasi ogni esigenza, dall’autenticazione all’accesso al database e altro ancora.
  5. Integrazione con Werkzeug e Jinja2: Flask si basa su Werkzeug, un toolkit WSGI per Python, per gestire le richieste HTTP e le risposte. Utilizza anche Jinja2 come motore di templating per generare dinamicamente i contenuti HTML delle pagine web.
  6. Approccio “micro”: Flask adotta un approccio “micro”, il che significa che fornisce solo il minimo indispensabile per creare applicazioni web. Questo permette ai programmatori di mantenere il controllo completo sulle funzionalità dell’applicazione e di estenderle secondo le proprie esigenze.
  7. Semplicità e flessibilità: Grazie alla sua semplicità e flessibilità, Flask è ampiamente utilizzato per la creazione di server di API REST. È particolarmente adatto per progetti di dimensioni ridotte o medio-piccole, dove è importante mantenere il codice pulito, leggibile e facilmente gestibile.

In sintesi, Flask offre un modo efficiente e scalabile per creare server web e gestire le comunicazioni tra client e server in applicazioni web e servizi basati su microservizi. La sua flessibilità e facilità d’uso lo rendono una scelta popolare tra i programmatori Python per lo sviluppo di applicazioni web.

Cosa è Sqlite3

SQLite è un database leggero, veloce e autonomo progettato per gestire database locali in modo semplice ed efficiente. È una libreria software che fornisce un sistema di gestione di database relazionali (RDBMS) incorporato direttamente nelle applicazioni. A differenza dei tradizionali sistemi client-server, SQLite funziona senza la necessità di un server separato, rendendolo ideale per applicazioni embedded, mobile e web.

Con SQLite, è possibile creare, gestire e interrogare database direttamente dal proprio codice, senza dover installare o configurare un server di database separato. Grazie alla sua portabilità e leggerezza, SQLite è ampiamente utilizzato in una vasta gamma di applicazioni, tra cui applicazioni mobili, browser web, sistemi embedded, applicazioni desktop e altro ancora.

Una delle caratteristiche distintive di SQLite è la sua capacità di gestire database di piccole e medie dimensioni con estrema efficienza e velocità. Anche se è leggero, SQLite supporta molte delle caratteristiche avanzate dei database relazionali, tra cui transazioni ACID, vincoli di integrità referenziale, trigger, viste e molto altro ancora.

  1. Database embedded: SQLite3 è un database relazionale incorporato che opera direttamente su file di database senza la necessità di un server client separato. Questo lo rende ideale per l’integrazione in applicazioni software che richiedono la gestione dei dati senza l’installazione e la configurazione di un sistema di database separato.
  2. Zero configurazione: SQLite3 non richiede alcuna configurazione o amministrazione del server. Basta creare un file di database e SQLite3 è pronto per l’uso. Questo lo rende estremamente facile da utilizzare e adatto per progetti in cui la complessità e l’overhead di un database server tradizionale non sono necessari.
  3. Supporto completo SQL: SQLite3 supporta la maggior parte del linguaggio SQL standard, consentendo di creare, modificare e interrogare facilmente il database utilizzando le istruzioni SQL familiari come SELECT, INSERT, UPDATE e DELETE. Offre anche funzionalità avanzate come JOIN, GROUP BY, e ORDER BY per interrogare e analizzare i dati in modo efficace.
  4. Transazioni ACID: SQLite3 supporta transazioni ACID (Atomicity, Consistency, Isolation, Durability), garantendo che le operazioni di scrittura sul database siano sicure e affidabili. Le transazioni possono essere avviate, eseguite e commesse o rollbackate secondo necessità, garantendo l’integrità e la coerenza dei dati.
  5. Portabilità: SQLite3 è altamente portabile e può essere eseguito su una vasta gamma di piattaforme, compresi sistemi operativi desktop (Windows, macOS, Linux) e dispositivi embedded come smartphone, tablet e dispositivi IoT. Questa portabilità lo rende una scelta popolare per lo sviluppo di applicazioni multi-piattaforma.
  6. Dimensioni ridotte: SQLite3 è progettato per essere estremamente leggero e ha una piccola dimensione del file binario, occupando poche risorse di sistema e richiedendo una quantità minima di memoria per funzionare. Questo lo rende adatto per l’uso su dispositivi con risorse limitate, come smartphone e dispositivi IoT.
  7. Facilità d’uso: SQLite3 è progettato per essere semplice da usare e richiede solo poche righe di codice per iniziare a utilizzare il database in un’applicazione. Offre anche un’ampia documentazione e una vasta comunità di sviluppatori che forniscono supporto e risorse per l’apprendimento.

In sintesi, SQLite3 è una scelta eccellente per le applicazioni che richiedono un database leggero, incorporato e facile da usare. Offre un’ampia gamma di funzionalità SQL, una sicurezza e affidabilità delle transazioni, e una portabilità che lo rende adatto per una vasta gamma di progetti e piattaforme.

Cosa è un dizionario

Definizione di dizionario:

  • In programmazione, un dizionario è una struttura dati che consente di memorizzare coppie chiave-valore.
  • Ogni elemento del dizionario è costituito da una chiave unica associata a un valore.
  1. Struttura e accesso:
    • I dizionari sono strutturati in modo da consentire un accesso rapido ai valori tramite le chiavi.
    • L’accesso ai valori avviene specificando la chiave associata, consentendo un recupero efficiente delle informazioni.
  2. Chiavi e Valori:
    • Le chiavi in un dizionario sono di solito stringhe, numeri o altri tipi di dati immutabili.
    • I valori possono essere di qualsiasi tipo, inclusi numeri, stringhe, liste o addirittura altri dizionari.
  3. Utilità e applicazioni:
    • I dizionari sono ampiamente utilizzati per gestire dati strutturati e associare informazioni in modo flessibile.
    • Sono particolarmente utili quando si deve accedere ai dati tramite identificatori univoci anziché posizioni fisse.
  4. Operazioni comuni:
    • Aggiunta di nuove coppie chiave-valore: mioDizionario[chiave] = valore.
    • Rimozione di una coppia: delete mioDizionario[chiave].
    • Recupero del valore associato a una chiave: valore = mioDizionario[chiave].
  5. Iterazione:
    • È possibile iterare su tutte le chiavi, i valori o le coppie chiave-valore del dizionario.
    • Ciò consente di eseguire operazioni su tutti gli elementi del dizionario in modo efficiente.
    • È possibile estrarre la lista di tutte le chiavi o di tutti i valori presenti nel dizionario.
  6. Implementazione nella Raspberry PI:
    • In contesti di programmazione per la Raspberry PI, i dizionari sono spesso utilizzati per gestire configurazioni, associazioni chiave-valore dinamiche o mappare informazioni rilevanti.
  7. Efficienza e complessità:
    • L’efficienza nell’accesso ai dati tramite chiavi rende i dizionari una scelta ideale per situazioni in cui è necessario un recupero veloce delle informazioni.
    • La complessità temporale per le operazioni di ricerca, inserimento e cancellazione è spesso molto bassa rispetto ad altre strutture dati.

L’utilizzo di dizionari aggiunge un livello di flessibilità e organizzazione ai programmi, consentendo una gestione efficiente e dinamica delle informazioni.

Cosa è Virtualenv

Virtualenv è uno strumento che consente agli sviluppatori di creare e gestire ambienti Python isolati e indipendenti all’interno dei propri progetti. Con virtualenv, è possibile creare un ambiente Python isolato che contiene solo le dipendenze specifiche del progetto, senza interferire con altre installazioni Python sul sistema.

L’utilizzo di virtualenv offre numerosi vantaggi, tra cui la possibilità di mantenere ordine e coerenza tra le dipendenze dei vari progetti, la facilità di condivisione e distribuzione del codice e la capacità di testare applicazioni in un ambiente controllato e isolato. Inoltre, virtualenv consente agli sviluppatori di sperimentare con diverse versioni di Python e librerie senza influire sulle altre applicazioni o sul sistema operativo.

Una volta creato un ambiente virtuale con virtualenv, è possibile attivarlo per utilizzare le versioni specifiche di Python e le dipendenze del progetto. Questo consente agli sviluppatori di lavorare in modo efficiente e sicuro su progetti complessi senza preoccuparsi di conflitti di dipendenze o problemi di compatibilità.

Ora, esploreremo le caratteristiche e l’utilizzo di virtualenv in modo più dettagliato attraverso una serie di punti:

  1. Creazione di un ambiente virtuale: Con virtualenv, è possibile creare un ambiente virtuale in pochi passaggi, specificando il percorso della directory di destinazione e, eventualmente, la versione di Python da utilizzare.
  2. Attivazione e disattivazione dell’ambiente virtuale: Dopo aver creato un ambiente virtuale, è possibile attivarlo utilizzando uno script specifico generato da virtualenv. Una volta attivato, l’ambiente virtuale modifica le variabili di ambiente per puntare alla sua directory, isolando il progetto dagli altri ambienti Python installati sul sistema. Al termine del lavoro sul progetto, è possibile disattivare l’ambiente virtuale per tornare all’ambiente Python predefinito.
  3. Gestione delle dipendenze del progetto: Utilizzando un ambiente virtuale, è possibile installare le dipendenze del progetto in modo isolato, senza influire sulle altre installazioni Python sul sistema. Ciò consente di mantenere l’ordine e la coerenza tra le dipendenze dei vari progetti e semplifica la gestione delle versioni delle librerie.
  4. Condivisione e distribuzione del codice: Gli ambienti virtuali possono essere facilmente condivisi e distribuiti insieme al codice sorgente del progetto. Questo consente agli altri membri del team di riprodurre facilmente l’ambiente di sviluppo e garantire la coerenza tra le diverse installazioni.
  5. Testing e sviluppo sicuro: L’utilizzo di ambienti virtuali consente agli sviluppatori di testare le proprie applicazioni in un ambiente controllato e isolato, riducendo al minimo il rischio di conflitti di dipendenze o problemi di compatibilità. Inoltre, consente di sperimentare con diverse configurazioni e versioni di Python senza influire sul sistema operativo o sulle altre applicazioni.

Il set di API REST a disposizione

Il dispositivo comunica con l’esterno tramite un set di 5 API REST, 1 di tipo GET e 4 di tipo POST:

  • readusers (GET) restituisce il contenuto della tabella users contenente le autorizzazioni per ciascun utente;
  • insertuser (POST) aggiunge alla tabella users un utente con il suo UUID, nome e permessi di accesso tramite un Json del tipo:
                    {
                          "uuidUser": "73051e99f1",
                          "name" : "Mike",
                          "access" : "d"
                     }
  • moduser (POST) modifica nella tabella users un utente esistente con il suo UUID, nome e permessi di accesso tramite un Json del tipo:
                     {
                          "uuidUser": "73051e99f1",
                          "name" : "Mike",
                          "access" : "g"
                      }
  • deluser (POST) elimina un utente tramite il suo UUID con un Json del tipo:
                      {
                             "uuidUser": "1105cd0b60"
                       }
  • getdata (POST) restituisce l’elenco degli accessi registrati nel database corrispondenti ad un dato giorno identificato tramite un Json del tipo:
                      {
                           "year": "2024",
                           "month" : "3",
                           "day" : "17"
                       }

Il set di comandi Telegram a disposizione

Il bot Telegram riceve una notifica ogni volta che viene tentato un accesso (cioè ogni volta che un tag RFID viene rilevato dal sensore) che indica l’UUID e il privilegio di accesso ad esso associato. Ma è anche in grado di modificare i permessi contenuti nel database tramite i seguenti comandi:

  • /adduser uuid name permission aggiunge un utente con i suoi privilegi (g o d) (esempio: /adduser a305cd0b60 Paul d)
  • /moduser uuid name permission modifica i dati di un utente già esistente (esempio: /moduser a305cd0b60 Ringo g)
  • /del uuid rimuove un utente identificato dal proprio uuid (esempio: /del a305cd0b60)
  • /readuser uuid restituisce i dati dell’utente specificato (esempio /readuser a305cd0b60)

Di che componenti abbiamo bisogno per il controllo accessi RFID?

La lista dei componenti non è particolarmente lunga:

  • una breadboard per connettere la Raspberry PI agli altri componenti
  • alcuni fili DuPont (maschio – maschio, maschio – femmina, femmina – femmina)
  • un buzzer attivo di tipo KY-012
  • un resistore da 100Ω
  • un resistore da 82Ω
  • un LED verde
  • un LED rosso
  • un kit RFID come quello mostrato al paragrafo precedente
  • un modulo con doppio/singolo relè optoisolato
  • una (micro) SD card da non più di 32GB formattata in FAT32
  • un eventuale dongle WiFi USB per la Raspberry
  • e, ovviamente, una Raspberry !

Le SD card che ho usato per gli esperimenti sono una da 8GB e l’altra da 16GB.

Il progetto è stato testato con successo su una Raspberry Pi 1 Model B e su una Raspberry PI 3 Model B ma non è escluso che funzioni anche su altri modelli di Raspberry.

Realizzazione del progetto

Lo schema elettrico

Prima di realizzare il circuito vero e proprio diamo un’occhiata ai pinout delle due Raspberry utilizzate:

Pinout della Raspberry Pi 1 Model B (a sinistra) e della Raspberry Pi 3 Model B (a destra)
Pinout della Raspberry Pi 1 Model B (a sinistra) e della Raspberry Pi 3 Model B (a destra)

Come puoi osservare i due pinout coincidono per le prime 13 file.

Di seguito vediamo il pinout del modulo RFID:

Pinout del modulo RFID
Pinout del modulo RFID

Il terminale IRQ non verrà utilizzato.

Ed ecco il pinout del buzzer:

Pinout del buzzer attivo
Pinout del buzzer attivo

Useremo i terminali Ground – GND e Output signal -S.

Il modulo doppio relè

  1. Alimentazione:
    • Accetta un’ampia gamma di tensioni di alimentazione, di solito compresa tra 5V e 12V.
    • Il connettore di alimentazione è progettato per essere facilmente collegato a una sorgente di alimentazione esterna, come una batteria o un alimentatore.
  2. Relè:
    • Due relè a bordo, ciascuno con i propri contatti elettrici: comune (COM), normale aperto (NO) e normale chiuso (NC).
    • I contatti del relè sono progettati per gestire carichi di potenza. Tuttavia, le specifiche esatte dipendono dal modello specifico del modulo relè.
  3. Ingressi di Controllo:
    • Due ingressi di controllo (IN1 e IN2) che possono essere collegati a pin digitali di una scheda di sviluppo tipo Arduino.
    • Attivare uno di questi ingressi con un segnale logico alto (o basso, a seconda dei casi) attiverà il relè corrispondente.
  4. Indicatori LED:
    • Indicatori LED incorporati per ogni relè che indicano lo stato di attivazione (spesso con colori come rosso per attivato e spento per disattivato).
  5. Compatibilità Arduino:
    • Progettato per essere facilmente integrato con piattaforme di sviluppo come Arduino, rendendo il controllo dei relè un’operazione semplice e accessibile.
  6. Carichi Pilotabili:
    • In grado di pilotare una varietà di carichi elettrici come lampadine, motori, elettrovalvole, e altri dispositivi che richiedono controllo on/off.
    • Le specifiche esatte del carico dipendono dal modello del relè, ma spesso possono gestire carichi con tensioni alternate fino a 250V e correnti fino a 10A.

Questi moduli relè sono ampiamente utilizzati in progetti di domotica, automazione elettronica e controlli remoti, fornendo un’interfaccia sicura e controllata per dispositivi di potenza.

Un esempio di modulo con doppio relè optoisolato
Un esempio di modulo con doppio relè optoisolato

Vediamo ora lo schema elettrico del progetto, realizzato come al solito con Fritzing, in due versioni. Uno per la Raspberry Pi 1 Model B e l’altro per la Raspberry Pi 3 Model B:

Schema elettrico per la Raspberry Pi 1 Model B del controllo accessi con badge
Schema elettrico per la Raspberry Pi 1 Model B del controllo accessi con badge

Schema elettrico per la Raspberry Pi 3 Model B del controllo accessi con badge
Schema elettrico per la Raspberry Pi 3 Model B del controllo accessi con badge

È possibile che qualche modulo abbia bisogno di qualche connettore e quindi si renda necessario fare qualche saldatura. Se sei nuovo a questo argomento ti consiglio di dare una lettura all’articolo Un altro tutorial su come saldare.

Il modulo RFID è collegato alla porta SPI e all’alimentazione a 3.3V della Raspberry secondo lo schema :

RFIDGPIO RASPBERRY
3.3V3.3V
RST25
GNDGND
MISO9
MOSI10
CK11
SDA (che sarebbe il Chip Select)8

L’ingresso di controllo del modulo relè IN1 è collegato al GPIO 17 della Raspberry (anche se il modulo ha due relè, noi ne useremo solo uno) mentre i due pin 5V e GND della Raspberry vengono utilizzati per alimentare il modulo a doppio relè, collegando il pin 5V (lato Raspberry) al pin VCC (lato modulo a doppio relè) e i due pin GND (massa).

Noterai che sul modulo relè è presente un ponticello (disegnato in azzurro sul connettore sinistro) che collega i morsetti JD-VCC e VCC. Questo ponticello viene utilizzato per alimentare il modulo relè attraverso i terminali VCC e GND sul connettore destro. Senza questo ponticello, saremmo costretti ad alimentare il modulo con un alimentatore esterno.

Il buzzer è collegato al GPIO 22.

I LED sono collegati alla Raspberry al GPIO 18 tramite dei resistori per limitare la corrente che li attraversa ed evitare di bruciarli (e di bruciare le uscite digitali a cui sono collegati). Quello rosso sarà collegato al resistore da 100Ω, quello verde al resistore da 82Ω.

Il LED ha due terminali (chiamati anodo e catodo) e, come tutti i diodi, è un componente che ha una sua polarità: fa passare la corrente quando è polarizzato direttamente (cioè la tensione all’anodo è maggiore di quella al catodo) e blocca la corrente quando è polarizzato inversamente (cioè la tensione all’anodo è minore di quella al catodo). La tensione tra anodo e catodo, che indicheremo con Vd, varia a seconda del colore della luce emessa. In particolare abbiamo che:

  • Vd = 1.8 V per il LED rosso
  • Vd = 1.9 V per il LED giallo
  • Vd = 2 V per il LED verde
  • Vd = 2 V per il LED arancio
  • Vd = 3 V per il LED blu
  • Vd = 3 V per il LED bianco

Come facciamo ad identificare l’anodo e il catodo del LED? Lo facciamo osservando i suoi terminali. Il più lungo corrisponde all’anodo. Inoltre il corpo del LED presenta un appiattimento in un punto del bordo che indica che il terminale vicino è il catodo.

Quindi, se un LED non si accende è possibile che sia stato collegato al contrario. In questo caso, per farlo funzionare, è sufficiente invertirne i collegamenti.

Come si calcola la resistenza da collegare al LED?

Nota Bene: questo paragrafo tratta il calcolo della resistenza di limitazione in maniera teorica e richiede un minimo di conoscenza delle basi dell’Elettrotecnica. Pertanto non è fondamentale per la comprensione del resto del progetto e può essere saltato dal lettore non interessato a tali aspetti teorici.

Come abbiamo già detto, il resistore tra il generico GPIO e il LED serve a limitare la corrente che attraversa il LED. Ma come possiamo calcolare il suo valore di resistenza? Ci viene in soccorso la Legge di Ohm la quale dice che la differenza di potenziale ai capi di un resistore (cioè la tensione misurata agli estremi del resistore) è proporzionale alla corrente I che lo attraversa e la costante di proporzionalità è proprio il valore di resistenza del resistore R:

V2 - V1 = RI

Nota Bene: per amor di precisione bisogna puntualizzare che mentre il resistore è il componente fisico (l’oggetto vero e proprio), la resistenza è il suo valore. Quindi è improprio (anche se accade di frequente) chiamare il resistore col termine resistenza.

Possiamo vedere la Legge di Ohm su un semplice circuito costituito da un generatore di tensione (il cerchio a sinistra) e un resistore:

Rappresentazione della Legge di Ohm
Rappresentazione della Legge di Ohm

La tensione (o differenza di potenziale) V2 – V1 impressa dal generatore di tensione sul resistore è uguale al prodotto di R per I.

Vediamo ora uno schema leggermente più complesso dove sono presenti il solito generatore di tensione, il resistore e un LED rosso:

Circuito per il calcolo del resistore di limitazione della corrente sul LED
Circuito per il calcolo del resistore di limitazione della corrente sul LED

Nel nostro caso la Vg rappresenta la tensione presente all’uscita digitale della Raspberry quando è HIGH ed è pari quindi a 3.3V.

La Vd è la tensione ai capi del diodo (tra anodo e catodo) quando questo è polarizzato direttamente (cioè quando fa scorrere la corrente). Avendo scelto un LED rosso, sappiamo, dalla tabella precedente, che Vd = 1.8V.

Dobbiamo determinare il valore R del resistore. Abbiamo ancora una incognita: il valore della corrente I che deve scorrere nel circuito quando il pin è in stato HIGH.

Nota Bene: quando il pin digitale è nello stato LOW la sua tensione (cioè la Vg) è nulla, ne consegue che anche la corrente I nel circuito è nulla.

I LED in genere non sopportano correnti maggiori di 20mA, quindi imponiamo una corrente massima di 15mA per stare sul sicuro.

Per la Legge di Kirchhoff alle maglie (detta anche Legge di Kirchhoff delle tensioni) , abbiamo che:

Vg - Vr - Vd = 0

Da cui ricaviamo che:

Vr = Vg - Vd 

Passando ai valori reali, abbiamo che:

Vr = 3.3V - 1.8V

Ne risulta che:

Vr = 1.5V

Ma, per la Legge di Ohm, abbiamo che:

Vr = RI

da cui:

R = Vr / I

Sostituendo i valori reali:

R = 1.5V / 0.015A

Ne deriva un valore di R pari a 100Ω.

Seguendo un ragionamento analogo per il LED verde, avremo che

R = ((3.3V - 2V) / 0.015A) = 1.3V / 0.015A = 86.67Ω

Il valore commerciale più vicino è 82Ω. Ricalcolando la corrente avremo che questa sarà pari a circa 15.8 mA. Siamo ben dentro i limiti di sicurezza.

Come creare un bot Telegram

Telegram è un’applicazione di messaggistica istantanea e VoIP che può essere installata sul tuo smartphone (Android e iPhone) o computer (PC, Mac e Linux). Telegram ti consente di creare bot con cui il nostro dispositivo può interagire.

Creiamo ora il nostro bot!

Se non hai già Telegram, installalo e poi cerca il bot botFather. Fai clic sull’elemento visualizzato. Apparirà la seguente schermata:

Prima schermata del bot botFather
Prima schermata del bot botFather

Digita il comando /start per leggere le istruzioni:

Le istruzioni per la creazione del bot
Le istruzioni per la creazione del bot

Ora digita il comando /newbot per creare il tuo bot. Dagli un nome e uno username:

La creazione del nuovo bot
La creazione del nuovo bot

Se il tuo bot è stato creato con successo, riceverai un messaggio con un link per accedere al bot e al token del bot.
Salva il token del bot perché ti servirà in seguito affinché la board possa interagire con il bot.

Ecco come appare la schermata in cui è scritto il token del bot:

Il token del bot
Il token del bot

Chiunque conosca il nome utente del tuo bot può interagire con esso. Per filtrare i messaggi in modo da ignorare quelli che non provengono dal tuo account Telegram, devi utilizzare il tuo ID utente Telegram. Pertanto, quando il tuo bot Telegram riceve un messaggio, la nostra Raspberry PI saprà se proviene da noi (e quindi lo elaborerà) o da altri (e quindi lo ignorerà). Ma…..come troviamo questo ID?

Nel tuo account Telegram, cerca IDBot e avvia una conversazione con quel bot:

La prima schermata di IDBot
La prima schermata di IDBot

Quindi digita il comando /getid e lui ti risponderà col tuo ID:

Il risultato del comando /getid
Il risultato del comando /getid

A questo punto abbiamo creato il nostro bot e abbiamo tutti gli elementi per interfacciarlo con il nostro dispositivo: lo username, il token e lo userid.

Preparazione della Raspberry

Per poter installare l’applicazione sulla Raspberry è necessario fare alcuni passi preliminari ed installare alcuni software.

Iniziamo subito con l’installazione del sistema operativo.

Il sistema operativo scelto è una distribuzione fatta apposta per funzionare su tutti i tipi di Raspberry, anche le più datate. I test sono stati fatti su una Raspberry Pi 1 Model B e su una Raspberry PI 3 Model B.

Se la Raspberry non ha connessione Wireless nativa puoi usare un dongle WiFi da inserire in una delle sue prese USB.

Scarichiamo e installiamo il sistema operativo su SD card

Per scaricare l’ultima versione, vai all’indirizzo https://www.raspberrypi.com/software/operating-systems/

e portati alla sezione Raspberry Pi OS (Legacy). Scaricherai una versione che non ha ambiente grafico in modo che sia la più leggera possibile:

La distribuzione scelta
La distribuzione scelta

Il file scaricato sarà compresso in formato xz. Per decomprimerlo su Linux dovrai prima installare il tool:

sudo dnf install xz           su CentOS/RHEL/Fedora Linux.
sudo apt install xz-utils     su Ubuntu/Debian

e poi dare la riga di comando:

xz -d -v filename.xz

dove filename.xz è il nome del file che hai appena scaricato contenente il sistema operativo.

Su Windows sarà sufficiente utilizzare uno fra questi tool: 7-Zip, winRAR, WinZip.

Il risultato sarà un file con estensione img che è l’immagine da flashare sulla SD card della Raspberry.

Per flashare l’immagine sulla SD card userai il tool Balena Etcher che funziona sia su Linux che su Windows che su MACOS.

Il suo utilizzo è molto semplice: è sufficiente selezionare l’immagine da flashare, la SD card di destinazione e premere il pulsante Flash.

Ecco come appare la sua interfaccia:

L'interfaccia del tool Balena Etcher
L’interfaccia del tool Balena Etcher

A sinistra viene impostata l’immagine da flashare, al centro la SD card da flashare, a destra il pulsante per iniziare l’operazione di flashing.

Alla fine della operazione la SD card conterrà due partizioni: boot e rootfs. Nel gestore dei dispositivi su Linux appare un menu del genere:

Menu dei dispositivi su Linux
Menu dei dispositivi su Linux

Anche Windows mostrerà un menu del genere: dal tuo file explorer, alla voce Questo computer vedrai le 2 partizioni.

Ora, con un editor di testo, crea sul tuo computer un file che chiamerai wpa_supplicant.conf e che editerai in questo modo:

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=«your_ISO-3166-1_two-letter_country_code»
network={
     ssid="«your_SSID»"
     psk="«your_PSK»"
     key_mgmt=WPA-PSK
}

Dovrai sostituire le seguenti voci:

  • «your_ISO-3166-1_two-letter_country_code» con l’identificativo del tuo paese (per esempio per l’Italia è IT)
  • «your_SSID» con il nome SSID della tua rete WiFi
  • «your_PSK» con la password della rete WiFi

Poi crea un file vuoto che chiamerai ssh (senza nessuna estensione).

Le nuove distribuzioni non hanno il classico utente pi con password raspberry quindi, per poter entrare in SSH, dobbiamo provvedere in altro modo.

Con una Raspberry funzionante dobbiamo creare un file di nome userconf che conterrà l’utente che vogliamo creare con una versione criptata della password che gli vogliamo attribuire. Il formato sara quindi username:password-hash.

Supponiamo di voler tenere l’utente pi, dobbiamo creare la password-hash. Supponiamo di voler creare l’hash della password raspberry, sempre nella Raspberry dove abbiamo creato il file userconf. Dobbiamo dare il comando, da shell, seguente:

echo "raspberry" | openssl passwd -6 -stdin

Questo comando restituirà l’hash della password raspberry. Per esempio potrebbe essere una stringa così:

$6$ROOQWZkD7gkLyZRg$GsKVikua2e1Eiz3UNlxy1jsUFec4j9wF.CQt12mta/6ODxYJEB6xuAZzVerM3FU2XQ27.1tp9qJsqqXtXalLY.

Questo è l’hash della password raspberry che ho calcolato sulla mia Raspberry.

Il nostro file userconf quindi conterrà la seguente stringa:

pi:$6$ROOQWZkD7gkLyZRg$GsKVikua2e1Eiz3UNlxy1jsUFec4j9wF.CQt12mta/6ODxYJEB6xuAZzVerM3FU2XQ27.1tp9qJsqqXtXalLY.

NOTA BENE: è necessario calcolare l’hash con una Raspberry perché l’hash calcolato col computer utilizza un altro algoritmo che non consentirebbe alla Raspberry che stiamo preparando di riconoscere la password.

Alternativamente puoi scaricare dal link qua sotto il file userconf che ho creato io per avere utente pi con password raspberry.

Ora apri la partizione boot sulla SD card e copia dentro i tre files wpa_supplicant.conf, ssh e userconf. Rimuovi in sicurezza la SD card dal computer e inseriscila nella Raspberry.

Accendi la Raspberry, aspetta qualche minuto. Per poterti loggare in ssh alla Raspberry, dovrai scoprire quale sia il suo IP (quello che il router le ha assegnato tramite DHCP).

Per fare questo è sufficiente dare il comando da una shell del pc:

ping raspberrypi.local 

valido sia su Linux che su Windows (previa installazione di Putty su Windows).

Nel mio PC la Raspberry risponde così:

Risposta della Raspberry al ping
Risposta della Raspberry al ping

Ciò mi fa capire che l’IP assegnato è 192.168.43.27. Ovviamente l’IP assegnato alla tua Raspberry sarà diverso.

Alternativamente puoi usare il tool Angry IP Scanner oppure puoi accedere alle impostazioni del tuo router per vedere i dispositivi collegati via WiFi e scoprire che IP ha la Raspberry.

Per poter loggarti sulla Raspberry in ssh dai il comando da shell:

con password raspberry. Su Windows è necessario Putty. Ovviamente dovrai usare l’IP che ti è stato assegnato.

Una volta dentro la Raspberry dai i seguenti comandi per fare l’update del software:

sudo apt update
sudo apt upgrade

La password è sempre raspberry.

Configuriamo la timezone

Per configurare la timezone dai il comando:

sudo raspi-config

alla shell della Raspberry. Supponiamo di voler impostare il fuso orario di Roma.

Apparirà una schermata così:

Schermata iniziale del comando sudo raspi-config
Schermata iniziale del comando sudo raspi-config

Seleziona l’opzione sulla localizzazione e dai Ok:

Selezionata l'opzione sulla localizzazione
Selezionata l’opzione sulla localizzazione

Seleziona poi l’opzione sulla timezone e dai Ok:

Selezionata l'opzione della timezone
Selezionata l’opzione della timezone

Seleziona ora l’area geografica e dai Ok:

Selezionata l'area geografica
Selezionata l’area geografica

Infine seleziona la città e dai Ok:

Selezionata la città
Selezionata la città

Ecco fatto!

Riavvia la Raspberry dando il comando:

sudo reboot

e, dopo pochi minuti, rientra in ssh come hai fatto prima.

Dai il comando

date

La Raspberry dovrebbe ora mostrare data e ora corrette.

Impostiamo l’IP statico

Per fare in modo che la Raspberry abbia sempre lo stesso indirizzo IP, dobbiamo impostarlo in modo che sia statico. Nei miei test l’ho impostato a 192.168.1.190. Se non facessimo così, il router le assegnerebbe un IP diverso ad ogni riavvio il che ci costringerebbe ogni volta a cambiare l’indirizzo IP dell’API REST.

Procederemo in due passi:

  • imposteremo l’IP fisso nella Raspberry
  • imposteremo il router in modo che riservi quell’indirizzo alla nostra Raspberry

Per il primo punto, dai il comando:

nano /etc/dhcpcd.conf

per aprire il file dhcpcd.conf ed editarlo.

Alla fine del file dovrai aggiungere un blocco del genere:

interface [INTERFACE]
static_routers=[ROUTER IP]
static domain_name_servers=[DNS IP]
static ip_address=[STATIC IP ADDRESS YOU WANT]/24

dove:

  • [INTERFACE] è il nome dell’interfaccia WiFi (nel nostro caso sarà wlan0)
  • [ROUTER IP] è l’indirizzo del nostro router (in genere è qualcosa del tipo 192.168.0.1 oppure 192.168.1.1). Lo puoi trovare entrando nell’interfaccia di amministrazione del tuo modem/router
  • [DNS IP] è l’indirizzo del server DNS, che in genere coincide con il parametro [ROUTER IP] del modem/router
  • [STATIC IP ADDRESS YOU WANT] è l’indirizzo IP che vogliamo assegnare come IP fisso alla Raspberry

Quindi, supposto che [ROUTER IP] = [DNS IP] = 192.168.1.1 e che [STATIC IP ADDRESS YOU WANT] = 192.168.1.190, il blocco avrà un aspetto del genere:

interface wlan0
static_routers=192.168.1.1
static domain_name_servers=192.168.1.1
static ip_address=192.168.1.190/24

Riavvia la Raspberry sempre col comando

sudo reboot

e poi accedi nuovamente in ssh, questa volta con IP 192.168.1.190.

Come secondo passo imposteremo il router in modo che riservi l’indirizzo 192.168.1.190 alla nostra Raspberry. Ogni modem/router è diverso dagli altri ma più o meno si somigliano. Mostrerò qui come appare il mio.

Per entrare digito l’indirizzo 192.168.1.1 (perche il mio modem ha questo IP) sul browser e, dopo aver dato la password di amministratore, arrivo alla schermata principale. Da qui devo cercare la schermata relativa al controllo degli accessi.

Aggiunta di un IP statico per la Raspberry
Aggiunta di un IP statico per la Raspberry

Ci sarà un pulsante di aggiunta di un IP statico: aggiungi l’IP scelto abbinato al MAC address della scheda WiFi della Raspberry. Ti consiglio comunque di consultare il manuale di istruzioni del tuo modem/router per questa operazione.

Verifica ora che la Raspberry si connetta alla rete dando il comando:

ping www.google.com

Se ottieni la risposta al ping la rete è connessa. Se ottieni un messaggio del tipo “Network is unreachable” dai il comando

sudo route add default gw [ROUTER IP]  

dove [ROUTER IP] è il gateway che nel nostro caso è l’IP del router, cioè 192.168.1.1

Installiamo alcuni indispensabili tool e l’ambiente virtuale

Procediamo ora installando alcuni tool indispensabili.

Innanzitutto installerai il programma di gestione del database sqlite digitando il comando:

sudo apt install sqlite3

Poi dobbiamo installare il comando pip3 che ci consente di installare ulteriori pacchetti e librerie per Python.

Sulla shell di Raspberry dai il comando:

sudo apt install python3-pip

che installerà pip3.

A questo punto devi installare il comando che crea l’ambiente virtuale dando il seguente comando:

sudo pip3 install virtualenv

Virtualenv , di cui abbiamo discusso in un apposito paragrafo, è un ambiente virtuale che rimane isolato dal resto del sistema operativo. Una volta creato, ci consente di installare al suo interno pacchetti e librerie della versione che ci serve senza che queste entrino in conflitto con quelle di sistema. In questo modo possiamo creare un ambiente in cui ogni progetto può avere le sue librerie e i suoi requisiti (con versioni differenti) senza rischiare che vadano in conflitto fra loro e con quelle di sistema.

Ora, nella home dell’utente pi, crea la cartella testRFID col comando

mkdir testRFID

ed entra nella cartella appena creata col comando

cd testRFID

Ora crea l’ambiente virtuale col comando

virtualenv env

e attivalo col comando

source env/bin/activate

NOTA: quando vuoi disattivare un ambiente virtuale ti è sufficiente dare il comando

deactivate

A questo punto devi installare alcune librerie dando i seguenti comandi:

pip install pyTelegramBotAPI

pip install pytz

pip install RPi.GPIO

python3 -m pip install spidev

python3 -m pip install mfrc522

pip install flask

Ora devi abilitare il bus SPI perché possa interagire col lettore RFID.

Dai il comando

sudo raspi-config

Apparirà il menu principale:

Menu principale di configurazione
Menu principale di configurazione

Seleziona il punto 3:

Selezione delle opzioni per le interfacce
Selezione delle opzioni per le interfacce

Vai al punto 4 per attivare la SPI:

Seleziona il bus SPI
Seleziona il bus SPI

Dai la conferma premendo su Yes:

Confermiamo l'abilitazione del bus SPI
Confermiamo l’abilitazione del bus SPI

Ci apparirà la schermata di conferma:

Il bus SPI è abilitato
Il bus SPI è abilitato

Usciamo quindi dal menu di configurazione:

Uscita dal menu di configurazione
Uscita dal menu di configurazione

Scarica ora, dal link qui sotto, gli script python e il file di database che dovrai poi caricare sulla Raspberry.

Una volta che hai scompattato la cartella, trasferisci i files appena scompattati verso la cartella /home/pi/testRFID della Raspberry.

Su Linux puoi aprire una shell sulla cartella appena scompattata e dare il comando:

rsync -avzP * [email protected]:/home/pi/testRFID

Su Windows puoi utilizzare FileZilla come indicato in questo link: https://howtoraspberrypi.com/transfer-files-raspberry-ssh/

Una volta fatto il trasferimento dovresti trovare nella cartella testRFID i files app.py, config.py , db_test_rfid_1.db,  teleg.py,  test.py oltre alla cartella env che fa parte di Virtualenv.

In breve ecco cosa fanno questi script:

  • db_test_rfid_1.db è il file che contiene il database. Il database contiene due tabelle: users contiene gli utenti col loro uuid, il nome e i privilegi di accesso, accessusers contiene registrati tutti i tentativi di accesso con l’uuid dell’utente seguita da data, ora e privilegi di accesso
  • test.py è il file che controlla gli accessi, aziona il relè, i LED e il buzzer e manda la notifica al bot Telegram sul tentativo di accesso. Inoltre registra il tentativo di accesso sulla tabella accessusers del database
  • app.py è il file che contiene il server che utilizza Flask per la gestione delle API REST
  • teleg.py è il file che gestisce il set di comandi provenienti dal bot Telegram
  • config.py è il file che contiene i parametri di configurazione di Telegram e la timezone. Il file che troverai nel pacchetto contiene un TOKEN e un CHAT_ID fittizi. Nel tuo file dovrai inserire i valori determinati nel paragrafo “Come creare un bot Telegram”. Inoltre dovrai definire i parametri TIMEZONE e TIMEZONE_COMMON_NAME relativi alla tua regione/città.

NOTA: il file db_test_rfid_1.db contiene, a titolo di esempio iniziale, nella tabella users due utenti fittizi e nella tabella accessusers alcuni accessi fittizi. Potrai manipolare questi dati con le funzionalità del bot Telegram e/o con le API REST.

Utilizzo

A questo punto apri altre 3 shell (oltre quella già in uso) dove ti loggherai in ssh alla Raspberry con il comando di prima:

e password raspberry.

Per ciascuna nuova shell aperta portati nella cartella di lavoro col comando

cd testRFID

e per ciascuna attiva l’ambiente virtuale:

source env/bin/activate

Nella prima shell, che avevi già aperta quando hai installato le librerie e che ha l’ambiente virtuale già attivo, lancia lo script test.py col comando:

python test.py

nella seconda shell lancia lo script teleg.py:

python teleg.py

e nella terza shell lancia lo script app.py:

python app.py

Se tutto va bene il sistema è completamente funzionante nel controllo degli accessi col primo script, nel controllo del bot Telegram col secondo script e nel controllo del server delle API REST col terzo script.

La quarta shell ci serve per controllare i dati nel database.

Dai il comando:

sqlite3

e si aprirà la shell dei comandi di sqlite. Dando il comando:

.open db_test_rfid_1.db

ti collegherai al database (che è proprio il file db_test_rfid_1.db).

Se per esempio dai il comando

.tables

verranno elencate le tabelle presenti, cioè users e accessusers.

Se dai il comando:

select * from users;

potrai vedere gli utenti col loro codice, il nome e i privilegi di accesso.

Se dai il comando:

select * from accessusers;

potrai vedere gli accessi degli utenti col loro codice, la data, l’ora e i privilegi di accesso.

Per ulteriori approfondimenti sui comandi di Sqlite puoi leggere la guida ufficiale al link https://sqlite.org/cli.html

Test del funzionamento

Prima di tutto è doverosa una precisazione. Nei paragrafi che seguono possiamo osservare il funzionamento del sistema col bot Telegram nel primo e il funzionamento con le API REST nel secondo ma bisogna sottolineare che questa “divisione” delle funzionalità è stata fatta per semplicità. In realtà le funzionalità coesistono e funzionano contemporaneamente (a patto che tutti gli script siano in esecuzione).

Testiamo gli accessi e la gestione utenti con il bot Telegram

Nel video seguente possiamo osservare il funzionamento del sistema col controllo degli accessi, la loro registrazione sul database, le notifiche sul bot Telegram e la gestione degli utenti tramite il bot. Tale gestione è abbastanza semplice e ricalca gli esempi del precedente paragrafo “Il set di comandi Telegram a disposizione”.

Testiamo gli accessi e la gestione utenti con le API REST

Nel video seguente possiamo osservare il funzionamento del sistema col controllo degli accessi, la loro registrazione sul database, la gestione degli utenti tramite l’uso delle API REST con il programma Postman. Fai riferimento al paragrafo precedente “Il set di API REST a disposizione” per vederne l’elenco. È da sottolineare come la struttura tipica del comando da dare su Postman è del tipo:

(GET/POST) IP_RASPBERRY:5000/commandname

Abbiamo inizialmente il tipo di API (GET o POST) selezionata tramite il menù a tendina alla sinistra della barra dell’URL. Segue poi l’IP_RASPBERRY che nel nostro caso abbiamo fissato in 192.168.1.190. L’IP è seguito dalla porta 5000 su cui rimane in ascolto il server Flask. Conclude il vero e proprio comando commandname (per esempio readusers oppure deluser e così via).

In particolare, per quanto riguarda le API di tipo POST, esse prevedono di fornire come dato di ingresso dell’API un documento Json. Per fornire questo Json, dovrai selezionare la voce Body che sta sotto la barra dell’URL. Poi seleziona la voce raw (sotto Body) e poi, sul menù a tendina sulla destra, seleziona la voce JSON al posto di Text. Puoi vedere un esempio nella immagine sottostante:

Esempio di API REST di tipo POST con i paramentri passati tramite Json
Esempio di API REST di tipo POST con i paramentri passati tramite Json

Diamo un’occhiata agli script

Esaminiamo ora i tre script che costituiscono il nostro sistema.

Lo script test.py

Come già anticipato, è lo script che controlla gli accessi, aziona il relè, i LED e il buzzer e manda la notifica al bot Telegram sul tentativo di accesso. Inoltre registra il tentativo di accesso sulla tabella accessusers del database.

Inizialmente vengono importate le librerie necessarie:

import telebot
import config
import datetime
import pytz
import json
import traceback
import sqlite3
import RPi.GPIO as GPIO
from mfrc522 import SimpleMFRC522
import time
import datetime

Poi viene ricavata la timezone dal file config.py:

P_TIMEZONE = pytz.timezone(config.TIMEZONE)
TIMEZONE_COMMON_NAME = config.TIMEZONE_COMMON_NAME

Viene istanziato il bot Telegram e notificato all’utente (su Telegram) che lo script è in funzione:

bot = telebot.TeleBot(config.TOKEN)

bot.send_message(config.CHAT_ID, 'Hi! I\'m online and ready!\nUse the command /help to see the available commands.')

Vengono poi definiti i GPIO che comandano il relè, i LED e il buzzer come uscite. Viene inoltre istanziato il lettore (reader) RFID:

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(17,GPIO.OUT)    # RELE
GPIO.setup(18,GPIO.OUT)    # LED
GPIO.setup(22,GPIO.OUT)    # BUZZER
 
reader = SimpleMFRC522()

Segue la definizione della funzione sanitize che fa in modo che la stringa in ingresso venga trasformata in una stringa a due caratteri, nel caso fosse inizialmente composta da un solo carattere. Questo ci serve per una questione puramente estetica per trasformare ore, minuti e secondi da oggetti del tipo “1”, “8”, “3” a oggetti del tipo “01”, “08”, “03”:

def sanitize(value):
   if len(value) == 1:
       return "0" + value
   else:
       return value

Viene poi inizializzato il dizionario vuoto:

diz = {}

Esso sarà aggiornato costantemente con i dati presenti nella tabella users del database e verrà consultato dal programma ogni volta che deve decidere se un certo utente può accedere o no.

Inizia ora un loop infinito che controlla se un badge è stato avvicinato al lettore RFID, fa emettere il suono in caso affermativo, aggiorna il dizionario per decidere se l’utente è riconosciuto o meno e , nel caso lo fosse, se può entrare (attivando o meno il relè) e scrive nella tabella accessusers il tentativo di accesso con UUID, data e ora e un flag (“g” per granted, “d” per denied e “n” per not recognized):

while True:
   # GPIO.cleanup()
    time.sleep(1)
    try:
        status,TagType = reader.read_no_block()
#        print(status)
        if status == None:
            #print ("No Card Found")
            pass
        elif status != None:
            GPIO.output(22,GPIO.HIGH)     #  BUZZER ON
            time.sleep(0.1)
            GPIO.output(22,GPIO.LOW)     #  BUZZER OFF
            dbconnect = sqlite3.connect("db_test_rfid_1.db");
            dbconnect.row_factory = sqlite3.Row;
            cursor = dbconnect.cursor();

            cursor.execute('SELECT * FROM users');
            for row in cursor:
                diz[row['uuidUser']] = row['access']
            print (diz)
            print ("\nCard Found!")
            id,text = reader.read()
            print(id)
            idHex = hex(id)[2:]
            print(idHex) 
            now = datetime.datetime.now() 
            year_s = str(now.year)
            month_s = str(now.month)
            day_s = str(now.day)
            hour_s = str(now.hour)
            minute_s = str(now.minute)
            second_s = str(now.second)

            print("Current year:", year_s)
            print("Current month:", month_s)
            print("Current day:", day_s)
            print("Current hour:", hour_s)
            print("Current minutes:",minute_s)
            print("Current seconds:", second_s)

            qry = "select *  from accessusers"
            cursor.execute(qry)
            lista = []
            for row in cursor:
                boh = int(row['id'])
                lista.append(boh)
            if lista == []:
                num = 1
            else:
                num = max(lista)
                num = num + 1
            if(idHex not in diz):
                print("code not recognized: access denied!")  
                bot.send_message(config.CHAT_ID, 'User ' + idHex + " not recognized: access denied at " + sanitize(day_s) + "/"+ sanitize(month_s) + "/" + year_s + "  " + sanitize(hour_s) + ":" + sanitize(minute_s) + ":" + sanitize(second_s))    
                cursor.execute('''insert into accessusers values (?, ?, ?, ?, ?, ?, ?, ?, ?)''', (num, idHex, day_s, month_s,  year_s, hour_s, minute_s, second_s,  "n"));
                dbconnect.commit();
                GPIO.output(18,GPIO.LOW)     #  LED OFF
            elif(diz[idHex] == "g"):
                print("access granted")
                bot.send_message(config.CHAT_ID, 'User ' + idHex + " recognized: access granted at " + sanitize(day_s) + "/"+ sanitize(month_s) + "/" + year_s + "  " + sanitize(hour_s) + ":" + sanitize(minute_s) + ":" + sanitize(second_s))
                cursor.execute('''insert into accessusers values (?, ?, ?, ?, ?, ?, ?, ?, ?)''', (num, idHex, day_s, month_s,  year_s, hour_s, minute_s, second_s,  "g"));
                dbconnect.commit();
                GPIO.output(18,GPIO.HIGH)     #  LED ON
                GPIO.output(17,GPIO.HIGH)     #  RELE ON
                time.sleep(5)
                GPIO.output(18,GPIO.LOW)     #  LED OFF
                GPIO.output(17,GPIO.LOW)     #  RELE OFF
            elif(diz[idHex] == "d"):
                print("access denied")
                bot.send_message(config.CHAT_ID, 'User ' + idHex + " recognized: access denied at " + sanitize(day_s) + "/"+ sanitize(month_s) + "/" + year_s + "  " + sanitize(hour_s) + ":" + sanitize(minute_s) + ":" + sanitize(second_s)) 
                cursor.execute('''insert into accessusers values (?, ?, ?, ?, ?, ?, ?, ?, ?)''', (num, idHex, day_s, month_s,  year_s, hour_s, minute_s, second_s,  "d"));
                dbconnect.commit();
                GPIO.output(18,GPIO.LOW)     #  LED OFF
            else:
                print("code not recognized: access denied")
            diz = {}
    except: 
        print("errore")
    
   

Lo script teleg.py

Come già anticipato, è lo script che gestisce il set di comandi provenienti dal bot Telegram.

All’inizio vengono importate le librerie necessarie, caricata la timezone dal file config.py e istanziato il bot Telegram:

import telebot
import config
import datetime
import pytz
import json
import traceback
import sqlite3

P_TIMEZONE = pytz.timezone(config.TIMEZONE)
TIMEZONE_COMMON_NAME = config.TIMEZONE_COMMON_NAME

bot = telebot.TeleBot(config.TOKEN)

Seguono poi gli handler dei comandi che arrivano dal bot.

Il primo è help:

@bot.message_handler(commands=['help'])
def help_command(message):
   bot.send_message(
       message.chat.id,
       'Greetings, here the list of all available commands:\n\n' +
       '*/adduser uuid name permission* adds a user with its privileges (g or d) (example: /adduser a305cd0b60 Paul d)\n\n' +
       '*/moduser uuid name permission* update a user with its privileges (g or d) if already in database (example: /moduser a305cd0b60 Mike d)\n\n' +
       '*/del uuid* removes a user (example: /del a305cd0b60)\n\n' +
       '*/readuser uuid* returns the privileges of the specified user (example /readuser a305cd0b60)\n', parse_mode= 'Markdown'
   )

Esso dà una lista dei comandi disponibili e, per ciascuno, un esempio di utilizzo.

Poi troviamo l’handler del che si occupa di eliminare un utente dal database (cioè dalla tabella users) dando in ingresso il suo UUID:

@bot.message_handler(commands=['del'])
def del_command(message):
   arguments = message.text.split(" ")
   if len(arguments) != 2:
       bot.send_message(
           message.chat.id,
           'Wrong number of arguments'
       )
       return
   uuid = arguments[1]
   dbconnect = sqlite3.connect("db_test_rfid_1.db")
   dbconnect.row_factory = sqlite3.Row
   cursor = dbconnect.cursor()
   qry = "delete from users where uuidUser = " + "'" + uuid + "'"
   print(qry)
   cursor.execute(qry)
   dbconnect.commit()
   dbconnect.close()   
   bot.send_message(
       message.chat.id,
       'Deleted ' + uuid + ' user.'
   )

Segue l’handler adduser che aggiunge un nuovo utente alla tabella users del database ma solo se non è già presente (i parametri in ingresso sono UUID, nome utente e permessi):

@bot.message_handler(commands=['adduser'])
def adduser_command(message):
    arguments = message.text.split(" ")
    if len(arguments) != 4:
        bot.send_message(
            message.chat.id,
            'Wrong number of arguments'
        )
        return
    uuid = arguments[1]
    nameuser = arguments[2]
    permission = arguments[3]

    dbconnect = sqlite3.connect("db_test_rfid_1.db")
    dbconnect.row_factory = sqlite3.Row
    cursor = dbconnect.cursor()

    qry  = "select *  from users where uuidUser = " + "'" + uuid + "'"
    row =  cursor.execute(qry)
    res = row.fetchone() 
    if res is not  None:
        print("user in db")
        bot.send_message(
           message.chat.id,
           'User ' + uuid + ' already in  database. Nothing to do.'
        )

        return 
    print("user not in db")
    qry = "select *  from users"
    cursor.execute(qry)
    lista = []
    for row in cursor:
        boh = int(row['id'])
        lista.append(boh)

    if lista == []:
        num = 1
    else:
        num = max(lista)
        num = num + 1
    cursor.execute('''insert into users values (?, ?, ?, ?)''', (num, uuid, nameuser, permission));
    dbconnect.commit()
    dbconnect.close()

    bot.send_message(
       message.chat.id,
       'Added ' + uuid + ' user to database.'
   )

Abbiamo poi l’handler moduser che consente di modificare un utente nella tabella users del database (ma solo se già presente). I parametri in ingresso sono UUID, nome utente e permessi:

@bot.message_handler(commands=['moduser'])
def moduser_command(message):
    arguments = message.text.split(" ")
    if len(arguments) != 4:
        bot.send_message(
            message.chat.id,
            'Wrong number of arguments'
        )
        return
    uuid = message.text.split(" ")[1]
    nameuser = message.text.split(" ")[2]
    permission = message.text.split(" ")[3]

    dbconnect = sqlite3.connect("db_test_rfid_1.db")
    dbconnect.row_factory = sqlite3.Row
    cursor = dbconnect.cursor()
    num = 0
    qry  = "select *  from users where uuidUser = " + "'" + uuid + "'"
    row =  cursor.execute(qry)
    res = row.fetchone() 
    if res is  None:
        print("user not in db")
        return 
    else:
        num = res[0]

    print("user in db")
    print(num)

    qry = "delete from users where id = " + "'" + str(num) + "'"  
    cursor.execute(qry)
    dbconnect.commit()
    cursor.execute('''insert into users values (?, ?, ?, ?)''', (num, uuid, nameuser, permission));
    dbconnect.commit()
    dbconnect.close()

    bot.send_message(
       message.chat.id,
       'Modified ' + uuid + ' user in database.'
   )

Infine troviamo l’handler readuser che restituisce le informazioni sull’utente selezionato (sempre se presente nel database). Il parametro in ingresso è l’UUID:

@bot.message_handler(commands=['readuser'])
def readuser_command(message):
   arguments = message.text.split(" ")
   if len(arguments) != 2:
       bot.send_message(
           message.chat.id,
           'Wrong number of arguments'
       )
       return
   uuid = arguments[1]
   msg = ""
   dbconnect = sqlite3.connect("db_test_rfid_1.db")
   dbconnect.row_factory = sqlite3.Row
   cursor = dbconnect.cursor()
   qry  = "select *  from users where uuidUser = " + "'" + uuid + "'"
   row =  cursor.execute(qry)
   res = row.fetchone() 
   if res is  None:
       print("user not in db")
       bot.send_message(
          message.chat.id,
          'User ' + uuid + ' not in database.'
       )
       return 
   else:
       qry  = "select *  from users where uuidUser = " + "'" + uuid + "'"
       row =  cursor.execute(qry)
       for row in cursor:
           msg =  row['uuidUser'] + " " + row['name'] + " " +  row['access']


       dbconnect.close()

       bot.send_message(
          message.chat.id,
          msg
       )

Lo script termina con la funzione:

bot.polling(none_stop=True)

che tiene il bot in un perenne stato di polling, cioè di interrogazione continua dei messaggi in arrivo per poi poterli gestire con i vari handler.

Lo script app.py

Come già anticipato, è lo script che contiene il server che utilizza Flask per la gestione delle API REST

Come sempre inizia con l’importazione delle librerie necessarie:

from flask import Flask
from flask import request
import json
import sqlite3

Segue poi la funzione sanitize che abbiamo già incontrato in precedenza.

Viene istanziata l’app Flask che gestirà le richieste provenienti dalle API REST:

app = Flask(__name__)

Seguono poi i vari handler delle API REST.

Il primo è “/” e restituisce il classico “Hello world”.

Segue poi l’handler getdata mappato sull’API /getdata di tipo POST che restituisce gli accessi avvenuti in un dato giorno (dato come Json contenente anno, mese e giorno in input):

@app.route('/getdata',  methods=['POST'])
def getdata():
    request_data = request.get_json()
    year_j = request_data['year']
    month_j = request_data['month']
    day_j = request_data['day']
    print(year_j + "/" + month_j + "/" + day_j)

    dbconnect = sqlite3.connect("db_test_rfid_1.db")
    dbconnect.row_factory = sqlite3.Row
    cursor = dbconnect.cursor()
    qry = "SELECT  * from accessusers where year = " + str(year_j) + " and month = " + str(month_j) + " and  day = " + str(day_j)
    cursor.execute(qry)
    innerdata = {}
    outerdata = {}
    for row in cursor:
       hms = sanitize(str(row['hour'])) + ":" + sanitize(str(row['minutes'])) + ":" + sanitize(str(row['seconds']))
       uuid = str(row['uuidUser'])
       acc = str(row['access'])
       innerdata[uuid] = acc
       outerdata[hms] = innerdata
       innerdata = {}
    dbconnect.close()
    json_data = json.dumps(outerdata)

    return  json_data

Abbiamo poi l’handler readusers mappato sull’API /readusers di tipo GET che restituisce la lista degli utenti presenti nella tabella users del database:

@app.route('/readusers',  methods=['GET'])
def readusers():
    dbconnect = sqlite3.connect("db_test_rfid_1.db")
    dbconnect.row_factory = sqlite3.Row
    cursor = dbconnect.cursor()
    qry = "SELECT  * from users"
    cursor.execute(qry)
    innerdata = {}
    outerdata = {}
    for row in cursor:
        idrow = row['id']
        lista = [row['uuidUser'], row['name'], row['access']]
        innerdata = lista
        lista = []
        outerdata[idrow] = innerdata
        innerdata = {}
    dbconnect.close()
    json_data = json.dumps(outerdata)

    return  json_data

Incontriamo poi l’handler insertuser mappato sull’API /insertuser di tipo POST che inserisce un nuovo utente nella tabella users del database (se non già presente) con UUID, nome e privilegi di accesso:

@app.route('/insertuser',  methods=['POST'])
def insertuser():
    request_data = request.get_json()
    uuidUser_j = request_data['uuidUser']
    name_j = request_data['name']
    access_j = request_data['access']

    dbconnect = sqlite3.connect("db_test_rfid_1.db")
    dbconnect.row_factory = sqlite3.Row
    cursor = dbconnect.cursor()
 
    qry  = "select *  from users where uuidUser = " + "'" + str(uuidUser_j) + "'"
    row =  cursor.execute(qry)
    res = row.fetchone() 
    if res is not  None:
        print("user in db")
        return 'user in db'
    print("user not in db")
    qry = "select *  from users"
    cursor.execute(qry)
    lista = []
    for row in cursor:
        boh = int(row['id'])
        lista.append(boh)

    if lista == []:
        num = 1
    else:
        num = max(lista)
        num = num + 1
    cursor.execute('''insert into users values (?, ?, ?, ?)''', (num, uuidUser_j, name_j, access_j));
    dbconnect.commit()
    dbconnect.close()
    return 'Done!'

Arriviamo all’handler deluser mappato sull’API /deluser di tipo POST che elimina un utente (identificato da un certo UUID) dalla tabella users del database (se presente nel database stesso):

@app.route('/deluser',  methods=['POST'])
def deluser():
    request_data = request.get_json()
    uuidUser_j = request_data['uuidUser']
    dbconnect = sqlite3.connect("db_test_rfid_1.db")
    dbconnect.row_factory = sqlite3.Row
    cursor = dbconnect.cursor()
    qry = "delete from users where uuidUser = " + "'" + str(uuidUser_j) + "'"
    print(qry)
    cursor.execute(qry)
    dbconnect.commit()
    dbconnect.close()
    return 'Done!'

Terminiamo con l’handler moduser mappato sull’API /moduser di tipo POST che modifica un utente già presente nella tabella users del database (con i dati UUID, nome e privilegi di accesso forniti tramite Json):

@app.route('/moduser',  methods=['POST'])
def moduser():
    request_data = request.get_json()
    uuidUser_j = request_data['uuidUser']
    name_j = request_data['name']
    access_j = request_data['access']

    dbconnect = sqlite3.connect("db_test_rfid_1.db")
    dbconnect.row_factory = sqlite3.Row
    cursor = dbconnect.cursor()
    num = 0
    qry  = "select *  from users where uuidUser = " + "'" + str(uuidUser_j) + "'"
    row =  cursor.execute(qry)
    res = row.fetchone() 
    if res is  None:
        print("user not in db")
        return 'user not in db'
    else:
        num = res[0]

    print("user in db")
    print(num)

    qry = "delete from users where id = " + "'" + str(num) + "'"  
    cursor.execute(qry)
    dbconnect.commit()
    cursor.execute('''insert into users values (?, ?, ?, ?)''', (num, uuidUser_j, name_j, access_j));
    dbconnect.commit()
    dbconnect.close()

    return "Done!"

Lo script termina con la parte che lancia l’applicazione Flask e rimane in attesa di richieste da parte del client sull’IP della Raspberry (nel nostro caso 192.168.1.190) sulla porta 5000:

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

Osservazioni finali

Gli script sopra menzionati però vengono interrotti nella loro esecuzione se chiudiamo la shell di appartenenza. Per esempio, se chiudo la shell dove ho lanciato lo script test.py, questo verrà killato (ucciso, la sua esecuzione verrà terminata). Per fare in modo che l’esecuzione dei nostri script non venga interrotta alla chiusura della shell di appartenenza possiamo lanciarli in un modo leggermente modificato. In questo caso è sufficiente una shell sola (ovviamente posizionata dentro la cartella di lavoro e con l’ambiente virtuale attivo) e dare, in successione, i seguenti comandi:

nohup python test.py &

nohup python teleg.py &

nohup python app.py &

In questo modo continueranno a girare anche se chiudiamo le shell. Potremmo fermarli solo col comando kill applicato al PID di ciascuno script in esecuzione oppure spegnendo (o riavviando) la Raspberry.

Newsletter

Se vuoi essere aggiornato sui nuovi articoli, iscriviti alla newsletter. Prima dell’iscrizione alla newsletter leggi la pagina Privacy Policy (UE)

Se ti vuoi disiscrivere dalla newsletter clicca sul link che troverai nella mail della newsletter.

Inserisci il tuo nome
Inserisci la tua email
0 0 votes
Valutazione articolo
guest
0 Commenti
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
Torna in alto