{"id":5462,"date":"2023-02-17T09:59:00","date_gmt":"2023-02-17T08:59:00","guid":{"rendered":"https:\/\/blog.besharp.it\/?p=5462"},"modified":"2023-02-17T14:27:24","modified_gmt":"2023-02-17T13:27:24","slug":"creiamo-un-sensore-zigbee-iot-basato-su-greengrass","status":"publish","type":"post","link":"https:\/\/blog.besharp.it\/it\/creiamo-un-sensore-zigbee-iot-basato-su-greengrass\/","title":{"rendered":"Creiamo un sensore ZigBee IoT Basato su Greengrass"},"content":{"rendered":"\n
L’Internet of things (IoT) \u00e8 in continua evoluzione e si parla sempre pi\u00f9 spesso di dispositivi connessi per il grande pubblico e nei settori industriale, energetico e Smart City.<\/p>\n\n\n\n
Mentre nei prodotti IoT realizzati per gli utenti finali \u00e8 comune trovare dispositivi direttamente connessi a Internet tramite reti WiFi o 4G, tutta una serie di applicazioni meno visibili ai consumatori possono beneficiare di un’architettura composta da due distinti stack di rete.<\/p>\n\n\n\n
In questi casi, un gruppo di dispositivi IoT costituisce una rete locale utilizzando protocolli che possono essere molto diversi da quelli utilizzati su Internet.<\/p>\n\n\n\n
Il principale vantaggio \u00e8 la possibilit\u00e0 di operare reti di dispositivi connessi anche in aree non servite dalla rete 3\/4\/5G o dove \u00e8 impossibile connettere ogni dispositivo a Internet. Inoltre, questi dispositivi sono generalmente molto pi\u00f9 economici da acquistare e da produrre in serie.<\/p>\n\n\n\n
All’interno di queste reti viene spesso individuato un nodo particolare, che funge da coordinatore della rete, e che, se necessario, pu\u00f2 fungere da ponte per mettere in comunicazione la rete con il mondo esterno tramite Internet.<\/p>\n\n\n\n
Il protocollo ZigBee \u00e8 uno standard di comunicazione wireless basato sulla specifica IEEE 802.15.4 ed \u00e8 uno degli stack pi\u00f9 popolari per la creazione di una rete locale di dispositivi wireless.<\/p>\n\n\n\n
Durante il tempo che ci \u00e8 concesso dedicare alla ricerca e allo studio di nuovi servizi, abbiamo creato un playground per sperimentare Zigbee e Greengrass. Abbiamo sviluppato un PoC per testare sia la comunicazione Zigbee che vari aspetti di Greengrass.<\/p>\n\n\n\n
Questo articolo \u00e8 un diario su come abbiamo creato un dispositivo edge basato su Greengrass e lo abbiamo utilizzato per elaborare i dati raccolti da una rete di sensori in locale, sfruttando le funzionalit\u00e0 principali di AWS IoT per inserire i dati raccolti nella nostra applicazione cloud-native.<\/p>\n\n\n\n
Per preparare il terreno, abbiamo sviluppato in modo rapido e frugale un sensore wireless utilizzando un modulo XBee, un regolatore di tensione e una fotoresistenza. <\/p>\n\n\n\n
XBee utilizza lo standard Zigbee, aggiunge alcune funzionalit\u00e0 e lo racchiude nel suo piccolo pacchetto pulito; inoltre, il modulo \u00e8 molto pi\u00f9 economico e facile da ottenere rapidamente utilizzando i nostri attuali fornitori.<\/p>\n\n\n\n
Quindi quello che avevamo in mente era qualcosa di simile al diagramma seguente:<\/p>\n\n\n\n <\/p>\n\n\n\n Il sensore rileva la tensione derivata dalla fotoresistenza circa 2 volte al secondo e la trasmette sulla rete ZigBee al nodo coordinatore.<\/p>\n\n\n\n Il nodo coordinatore, che include Greengrass, legge i dati dalla porta seriale, analizza il pacchetto ed estrae il valore trasmesso dal sensore. Poich\u00e9 il valore \u00e8 solo un numero intero ed \u00e8 rumoroso, volevamo bufferizzare ed elaborare i punti dati in locale, calcolare il valore medio per un intervallo di tempo predefinito e inoltrare l’output verso IoT Core.<\/p>\n\n\n\n I dati puliti vengono quindi archiviati sia sullo shadow remoto dell\u2019oggetto, sia in un database per alimentare uno strumento di visualizzazione.<\/p>\n\n\n\n Per il sensore, abbiamo costruito una scheda prototipo con solo uno stadio di alimentazione, realizzata utilizzando un regolatore di tensione e un modulo XBee.<\/p>\n\n\n\n Il modulo ha tutto ci\u00f2 di cui abbiamo bisogno per soddisfare i nostri requisiti. Ha un ADC integrato e, ovviamente, \u00e8 in grado di unirsi o formare una rete Zigbee e inviare dati su di essa.<\/p>\n\n\n\n Fortunatamente non \u00e8 necessario un microcontrollore esterno, il modulo XBee pu\u00f2 essere configurato utilizzando XCTU<\/a>. Questo strumento \u00e8 un’applicazione gratuita che consente agli sviluppatori di interagire con i moduli Digi RF attraverso un’interfaccia grafica semplice da usare. Ti mostreremo come l’abbiamo utilizzato per configurare i moduli XBee.<\/p>\n\n\n\n Di seguito \u00e8 riportato lo schema del sensore, comprendente un’unit\u00e0 di alimentazione (il regolatore di tensione), il partitore di tensione a fotoresistenza e un circuito di ripristino del modulo.<\/p>\n\n\n\n Abbiamo scelto di alimentare la scheda utilizzando una batteria da 9V perch\u00e9 ne abbiamo alcune in ufficio e perch\u00e9 sono compatte, sicure e facili da trovare in qualsiasi negozio.<\/p>\n\n\n\n Poich\u00e9 questo \u00e8 solo un PoC, non ci andava di costruire qualcosa di alimentato con batterie al litio, e per lo scopo di questo progetto, il nostro regolatore di tensione e una batteria standard da 9V sono stati pi\u00f9 che sufficienti.<\/p>\n\n\n\n Una volta assemblata, la scheda ha un aspetto simile a questo<\/p>\n\n\n\n <\/p>\n\n\n\n Per il nodo coordinatore, abbiamo utilizzato un Raspberry Pi dotato di sistema operativo Raspbian, Greengrass e un semplice shield che abbiamo realizzato per alimentare e connettere il modulo XBee all’intestazione GPIO. Abbiamo utilizzato la porta seriale hardware integrata.<\/p>\n\n\n\n Lo shield<\/p>\n\n\n\n <\/p>\n\n\n\n Il coordinatore completamente assemblato<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n Dopo aver installato Greengrass sul Raspberry pi (puoi seguire la documentazione ufficiale o il nostro articolo che dovrebbe aiutarti a farlo in pochi passaggi), possiamo sviluppare il nostro codice sorgente.<\/p>\n\n\n\n Il compito del nostro Raspberry pi \u00e8 raccogliere i messaggi ricevuti tramite ZigBee, analizzarli e infine inviare il valore letto dal sensore tramite MQTT su un named shadow IoT Core. Fortunatamente, come accennato in precedenza, possiamo leggere facilmente questi dati collegandoci al dispositivo seriale Linux e leggendo i byte in ingresso. Il codice \u00e8 uno script Python piuttosto semplice:<\/p>\n\n\n\n Possiamo impacchettare questo script e distribuirlo grazie alla funzionalit\u00e0 pi\u00f9 utile di Greengrass: le distribuzioni automatizzate delle funzione Lambda.<\/p>\n\n\n\n Per fare ci\u00f2, dobbiamo solo creare una nuova funzione Lambda nel nostro account AWS e abilitare il versioning.<\/p>\n\n\n\n Dopo averlo fatto, per inviare il pacchetto al nostro core device Greengrass, dobbiamo creare un custom component e collegarlo all’ultima versione della funzione Lambda che abbiamo appena creato. Ricorda che i componenti Greengrass Lambda possono essere di due tipi:<\/p>\n\n\n\n La nostra scelta \u00e8 stata ovviamente quest’ultima, poich\u00e9 vogliamo raccogliere e inoltrare continuamente le informazioni lette dal dispositivo seriale.<\/p>\n\n\n\n Infine (questi passaggi sono parecchi, ma dopo averli eseguiti un paio di volte ti abitui al processo e diventa quasi meccanico) devi creare, o aggiornare se hai gi\u00e0 eseguito il processo una volta, una distribuzione e selezionare quali componenti distribuire con quella distribuzione. Nel nostro caso, vogliamo impacchettare la nostra funzione Lambda e il componente pubblico aws.greengrass.ShadowManager, che consente ai nostri dispositivi principali di pubblicare messaggi e sottoscriversi allo shadow topic di IoT Core.<\/p>\n\n\n\n Il core device dovrebbe quindi ricevere un nuovo job e installare o aggiornare i componenti selezionati con quella distribuzione. Questo dovrebbe richiedere un paio di minuti e, una volta terminata la fase di installazione, la funzione Lambda dovrebbe avviarsi automaticamente e trasmettere i dati al named shadow selezionato.<\/p>\n\n\n\n E qui sono iniziati i problemi\u2026<\/p>\n\n\n\n Dopo aver installato e distribuito la nostra funzione Lambda di prova, qualcosa ha preso una piega inaspettata: nessun dato \u00e8 stato inviato ad IoT Core.<\/p>\n\n\n\n Dopo una rapida indagine, abbiamo trovato un messaggio di errore abbastanza autoesplicativo nei log del dispositivo (puoi trovarli in \/greengrass\/v2\/logs\/)<\/p>\n\n\n\n Nessun problema, abbiamo pensato: abbiamo commesso un piccolo errore nell’autorizzazione del dispositivo nella configurazione della distribuzione. Abbiamo pensato che il componente Lambda necessitasse di autorizzazioni per accedere al dispositivo seriale e questo pu\u00f2 essere impostato nella “Configurazione del processo Linux” nella sezione “Dispositivo”, ma impostarlo non ci ha aiutato a risolvere il problema. <\/p>\n\n\n\n Dopo aver ricontrollato il tutto, l’errore continuava a persistere. Anche se la distribuzione specificava di consentire all’utente di accedere ai dispositivi, per sicurezza abbiamo aggiunto il nostro utente linux greengrass (ggc_user) al gruppo dialout.<\/p>\n\n\n\n Nulla cambi\u00f2.<\/p>\n\n\n\n “Operation not permitted” \u00e8 un errore diverso da “permission denied”. La prima volta che ho visto questo tipo di errore \u00e8 stato quando stavo sperimentando i container Docker, provando a modificare \/etc\/hosts\/: anche se sei root otterrai questo errore.<\/p>\n\n\n\n Dopo aver esplorato la nostra installazione, abbiamo scoperto che greengrass.service \u00e8 in esecuzione nella propria slice cgroup linux e non ha ottenuto l’autorizzazione per accedere al dispositivo della porta seriale.<\/p>\n\n\n\n Molte cose sono cambiate sotto il cofano nei sistemi Linux dopo l’adozione di systemd, anche se, il pi\u00f9 delle volte, sono completamente invisibili agli utenti finali e agli amministratori di sistema. I cgroup Linux dovrebbero avere il proprio articolo per spiegarli ma, in breve, sono la tecnologia chiave che consente l’esecuzione di Docker e di altre soluzioni di containerizzazione.<\/p>\n\n\n\n Con i cgroups puoi eseguire processi e controllare l’utilizzo delle risorse (come CPU, memoria e accesso ai dispositivi), anche se un programma viene eseguito come root. Se sei un vecchio amministratore di sistema come me, puoi considerarli come nuovi, pi\u00f9 utilizzabili e moderni chroot jail.<\/p>\n\n\n\n Con questo in mente, abbiamo prima cercato di convalidare la nostra ipotesi. Quando un demone si avvia in una slice, puoi trovare la sua configurazione nella directory Un file di sola scrittura chiamato devices.allow, contiene i dispositivi e le autorizzazioni a cui il nostro programma pu\u00f2 accedere.<\/p>\n\n\n\n Ogni processo (anche nei container) che viene avviato ottiene la sua directory con il suo file per le autorizzazioni. Ad esempio, la nostra funzione Lambda aveva la sua configurazione in “we need a more permanent solution to our problems” – Caiaphas – Jesus Christ superstar<\/strong><\/p>\n\n\n\n Ok, configuriamo la nostra unit\u00e0 systemd per permettere alle nostre funzioni Lambda di accedere alla porta seriale! <\/p>\n\n\n\n Abbiamo scoperto dalla documentazione di systemd che, aggiungendo la riga No, non funziona, abbiamo un altro problema:<\/p>\n\n\n\n Questo errore sembra ancora pi\u00f9 strano: significa che il processo di Greengrass sta provando a dare accesso per i block device alla nostra Lambda (mentre la nostra porta seriale \u00e8 un character device). <\/p>\n\n\n\n Per farla breve: dopo aver giocherellato con le autorizzazioni, non abbiamo trovato nulla che potesse consentire l’esecuzione del nostro processo, quindi abbiamo dovuto implementare una soluzione hacky. Fateci sapere nei commenti se avete suggerimenti.<\/p>\n\n\n\n Quindi, sapevamo che l’impostazione manuale delle autorizzazioni di processo avrebbe risolto il nostro problema. Abbiamo solo bisogno di automatizzare il processo di concessione delle autorizzazioni alla nostra Lambda una volta che \u00e8 stata avviata.<\/p>\n\n\n\n Un piccolo script con inotifywait fa il trucco:<\/p>\n\n\n\n Mettendo questo file in \/greegrass\/v2\/filemonitor.sh e aggiungendo una unit systemd (ad esempio \/etc\/systemd\/system\/filemonitor.service) con questo contenuto<\/p>\n\n\n\n ha risolto il problema! Non dimenticatevi di attivare il servizio con systemctl daemon-reload; Dopo aver risolto questo problema (che ci ha richiesto pi\u00f9 tempo del previsto), siamo finalmente riusciti a leggere i dati dalla porta seriale e quindi a inoltrarli a IoT Core tramite il nostro componente Lambda. Non appena i nuovi dati arrivano nel cloud AWS, una IoT Rule salva la nuova versione dello shadow nella nostra tabella DynamoDB.<\/p>\n\n\n\n Ci piace Greengrass, ci piace davvero, perch\u00e9 semplifica molto il processo di implementazione del nostro codice sui nostri dispositivi edge. <\/p>\n\n\n\n Vediamo tutti i vantaggi di avere un orchestratore che \u00e8 stato sviluppato e testato a fondo, ma a volte pu\u00f2 intralciarti quando vuoi eseguire attivit\u00e0 molto semplici (come leggere da una porta seriale, cosa che accade spesso nel mondo IoT! ), e finisci per dedicare molto alla ricerca della causa e delle possibili soluzioni (o se sei fortunato e hai un Damiano<\/a> come noi in beSharp, puoi accorciare molto il tempo di ricerca su Google con il suo aiuto).<\/p>\n\n\n\n Le nostre ricerche ci hanno permesso di apprendere molte cose su Linux e Greengrass, ad esempio abbiamo appreso che nella prima versione di Greengrass (v1) c’era effettivamente un componente che ti consente di connetterti a dispositivi seriali in modo pi\u00f9 sicuro con poche configurazioni sul lato IoT, senza alcun minaccioso script bash eseguito sul dispositivo<\/a>, ma al giorno in cui siamo scrivendo questo articolo, questa funzionalit\u00e0 non \u00e8 stata portata alla versione pi\u00f9 recente (v2).<\/p>\n\n\n\n Ci piacerebbe trovare una soluzione pi\u00f9 elegante, quindi se sei pi\u00f9 fortunato o conosci meglio di noi questo argomento, lascia un commento nella sezione sottostante per aiutare noi e tutti gli altri sysops a risolvere questo problema!<\/p>\n\n\n\n<\/figure>\n\n\n\n
L\u2019hardware<\/h2>\n\n\n\n
<\/figure>\n\n\n\n
<\/figure>\n\n\n\n
<\/figure>\n\n\n\n
<\/figure>\n\n\n\n
<\/figure>\n\n\n\n
<\/figure>\n\n\n\n
<\/figure><\/div>\n\n\n
Il software<\/h2>\n\n\n\n
import json\nimport time\nfrom serial import Serial, PARITY_NONE\nimport awsiot.greengrasscoreipc\nimport awsiot.greengrasscoreipc.client as client\nfrom awsiot.greengrasscoreipc.model import UpdateThingShadowRequest\n\nLIGHT_TOPIC = \"$aws\/things\/<your_iot_device_name>\/shadow\/name\/<named_shadow>\"\nQOS = QOS.AT_LEAST_ONCE\nipc_client = awsiot.greengrasscoreipc.connect()\n\n\ndef send_light_value(payload):\n\tupdate_thing_shadow_request = UpdateThingShadowRequest()\n\tupdate_thing_shadow_request.thing_name = \"p2bc-core-device\"\n\tupdate_thing_shadow_request.shadow_name = \"light\"\n\tupdate_thing_shadow_request.payload = json.dumps(\n \t{\"state\": {\"reported\": payload}})\n\top = ipc_client.new_update_thing_shadow()\n\top.activate(update_thing_shadow_request)\n\tfut = op.get_response()\n\n\tresult = fut.result(10)\n\treturn result.payload\n\n\nwith Serial('\/dev\/ttyS0', 9600, timeout=None, parity=PARITY_NONE, rtscts=1) as ser:\n\twhile True:\n \ts = ser.read(1)\n \tbuff = list(s)\n \tif len(buff) == 0 or buff[0] != 126:\n \tprint(\"continuing\")\n \tcontinue\n \tbuff = buff + list(ser.read(2))\n \tframe_length = buff[1] * 255 + buff[2]\n \tbuff = buff + list(ser.read(frame_length + 1))\n\tprint(f\"Buffer: {buff}\")\n \tprint(\"Sending to IoT core\")\n\tlight = get_light_intensity(buffer) # The implementation depends on the board you chose\n \tsend_light_value({\"light\": light})\n<\/pre>\n\n\n\n
\n
GreenGrass e le porte seriali: un rapporto problematico<\/h2>\n\n\n\n
could not open port \/dev\/ttyS0: [Errno 1] Operation not permitted: '\/dev\/ttyS0'.<\/code><\/p>\n\n\n\n
Ok, che cos\u2019\u00e8 una slice? <\/h2>\n\n\n\n
\/sys\/fs\/cgroup\/system.slice\/daemon-name\/<\/code> (
\/sys\/fs\/cgroup\/systemd\/system.slice\/greengrass.service<\/code>) <\/p>\n\n\n\n
\/sys\/fs\/cgroup\/systemd\/system.slice\/greengrass.service\/D79cAW6fOnnzHB9J4flf5rRNTphoM0KFRxiZY0-89ck <\/code>dalla documentazione del kernel Linux<\/a> abbiamo visto che scrivendo la stringa “magica” c *:* rwm nella directory del processo all\u2019interno del file devices.allow tutto funzionava correttamente.<\/p>\n\n\n\n
DeviceAllow=\/dev\/ttyS0 rw<\/code> nella nostra unit\u00e0 (\/etc\/systemd\/system\/greengrass.service) dovrebbe essere sufficiente. <\/p>\n\n\n\n
unable to create start process: failed to run container sandbox: container_linux.go:380: starting container process caused: process_linux.go:545: container init caused: process_linux.go:508: setting cgroup config for procHooks process caused: failed to write \"b *:* m\": write \/sys\/fs\/cgroup\/devices\/system.slice\/greengrass.service\/D79cAW6fOnnzHB9J4flf5rRNTphoM0KFRxiZY0-89ck\/devices.allow: operation not permitted.<\/code><\/p>\n\n\n\n
Sistemare tutto al volo<\/h2>\n\n\n\n
#!\/bin\/bash\ninotifywait --monitor \/sys\/fs\/cgroup\/devices\/system.slice\/greengrass.service --event create |\nwhile read dir action file; do\n echo \"**** $dir $action $file ***\"\n if [ $action == \"CREATE,ISDIR\" ]; then\n sleep 1\n echo \"c *:* rwm\" > $dir\/$file\/devices.allow\n fi\ndone<\/code><\/pre>\n\n\n\n
[Unit]
Description=Filemonitor
After=greengrass.service
[Service]
Type=simple
PIDFile=\/greengrass\/v2\/alts\/filemonitor.pid
RemainAfterExit=no
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target<\/code><\/p>\n\n\n\nsystemctl enable filemonitor<\/code>
Questo script monitora la directory cgroup Greengrass. Quando un nuovo processo si avvia, scriver\u00e0 nel suo file device.allow i permessi, permettendo cos\u00ec al nostro codice di aprirsi e leggere finalmente dal dispositivo seriale.<\/p>\n\n\n\nCosa ci portiamo a casa?<\/h2>\n\n\n\n
About Proud2beCloud<\/h2>\n\n\n\n