= Εργασία Εξαμήνου Iot's Καραμιχάλης Κωνσταντίνος cse47455 :nofooter: :toc: right :toc-title: Πίνακας περιεχομένων :toclevels: 6 :sectnums: :data-uri: :icons: font :ext-relative: .adoc :Figure-caption: Εικόνα {empty} + [WARNING] ==== [.text-center] Πριν ξεκινήσω να χρησιμοποιώ ένα σύστημα *LINUX* αντικαθιστώ το *VIM* με *NANO* [.text-center] *sudo apt-get remove --purge vim** + *sudo apt-get install nano* + ==== [.text-center] [small]*_"Χρησιμοποιούσα το Vim για περίπου 2 χρόνια, κυρίως γιατι δεν ήξερα πως να κάνω exit"_* {empty} + toc::[] == Σκοπός εργασίας [.text-justify] {nbsp}{nbsp}{nbsp}{nbsp} Ο σκοπός της εργασίας μας είναι η η ανάγνωση δεδομένων απο έναν αισθητήρα κίνησης η αποθήκευση ή και αποστολή τους σε έναν server σε ένα σμήνος. === Ανάγνωση δεδομένων και αποστολή τους [.text-justify] Ο τρόπος που επιλέχθηκε για την πραγματοποίησει της εργασίας είναι : * Χρήση του Arduino nano για την ανάγνωση των δεδομένων του αισθητήρα. * Χρήση του Raspberry PI Zero W για την επεξεργασία και την αποστολή των δεδομένων μας στον server. [.text-center] image::./photos/raspbduino.png[150,150] == Συνδεσμολογία και επικοινωνία [.text-justify] {nbsp}{nbsp}{nbsp}{nbsp} Για την επικοινωνία μεταξύ raspberry και arduino, επιλέχθηκε να γίνει χρήση του πρωτόκολλου I2C και όχι του πρωτόκολλου σειριακής επικοινωνίας. Αυτη η επιλογή μας δίνει το πλεονέκτημα, να μπορούμε να χρησιμοποιήσουμε το Serial Monitor του arduino, ταυτοχρονα με την επικοινωνία του με το raspberry για χρήση debugging ή για οτιδήποτε άλλο. Το μειονέκτημα της επιλογής είναι άυξηση της πολυπλοκότητας του κωδικα μας αλλά και την ανάγκη ειδικών διατάξεων για την πραγματοποιήσει της επικοινωνίας (Logic level converter). {empty} + [.text-center] image::./photos/i2clogo.png[200,200] {empty} + {nbsp}{nbsp}{nbsp}{nbsp} Το arduino χρησιμοποιεί λογική των 5V στις εισόδους/εξόδους του ενώ το raspberry 3,3V, αυτό έχει ως αποτέλεσμα ενώ μπορέι το arduino να διαβάσει τα δεδομένα που του αποστέλλονται απο το raspberry στην αποστολή των δεδομένων απο το arduino προς το raspberry υπάρχει μεγάλος κίνδυνος να δημιουργήσουμε μόνιμη καταστροφή στις εισόδους του ή και ολοκληρωτική καταστροφή του μικροεπεξεργαστη του raspberry. Για την αποφυγή οποιουδήποτε προβλήματος και την ακεραιότητα των δεδομένων μας παρεμβάλλουμε στην μεταξύ τους σύνδεση εναν logic level converter. [.text-center] image::./photos/raspberry-pi-arduino-i2c-communication_bb-1080x675.png[750,750,title="Συνδεσμολογία"] [.text-center] image::./photos/LevelConverter.jpg[title="Logic level converter"] {empty} + == Κώδικας arduino (Slave) [source,arduino] ---- #include <1> #include #define SLAVE_ADDRESS 0x16 <2> const int xpin = A0; // x-axis of the accelerometer <3> const int ypin = A1; // y-axis <3> const int zpin = A2; // z-axis (only on 3-axis models) <3> int index = 0; <4> char data[40]; <5> void setup() { Serial.begin(9600); <6> pinMode(xpin, INPUT); pinMode(ypin, INPUT); <7> pinMode(zpin, INPUT); // initialize i2c as slave Wire.begin(SLAVE_ADDRESS); <2> Wire.setClock(400000); <2> Wire.onRequest(sendData); <8> } void loop() { // Nothing to do at last !!!! } void sendData() { read_lilypad(); <9> Wire.write(data[index]); <10> ++index; if (index >= 40) { index = 0; } } void read_lilypad () { int x_input = analogRead(xpin); <11> int y_input = analogRead(ypin); <11> int z_input = analogRead(zpin); <11> float x_zero_G = 515.39; <12> float y_zero_G = 508.57; <12> float z_zero_G = 513.19; <12> float scale = 102.3; <13> String x_value = String(((float)x_input - x_zero_G) / scale); <14> String y_value = String(((float)y_input - x_zero_G) / scale); <14> String z_value = String(((float)z_input - x_zero_G) / scale); <14> String lilypad = "Lilypad x: " + x_value + " y: " + y_value + " z: " + z_value; <15> lilypad.toCharArray(data, 40); <16> } ---- <1> Βιβλιοθήκες για την λειτουργία του προγραμμάτος <2> Δήλωση διέυθυνσης του Arduino στο δίκτύο I2C, Ένταξη στο δικτυο I2C ως slave, Ορισμός της ταχύτητας επικοινωνίας μεταξύ master και slave <3> Ονοματοδοσία ακροδεκτών <4> Μετρητής <5> Πίνακας για την αποθήκευση των προς αποστολή δεδομένων <6> Εκκίνηση σειριακής επικοινωνίας <7> Δήλωση των ακροδεκτών ως είσοδο <8> Δήλωση ποιας συνάρτησης θα χρησιμοποιηθεί όταν ο master του I2C δικτυώματος ζητήσει δεδομένα <9> Κλήση συνάρτησης read_lilypad <10> Αποστολή των δεδομένων στον master 1Byte κάθε φορά <11> Διάβασμα των τιμών απο τον αισθητήρα. <12> Οι τιμές zero_G είναι οι τιμές που πρέπει αναμένουμε απο τους αισθητήρες όταν βρίσκονται σε ακινησία. Τις αφαιρούμε απο τις μετρήσεις μας για να έχουμε σωστά αποτέλεσμα στις μετρήσεις μας. <13> Scale είναι το μέγεθος που περιμένουμε να μεταβληθεί η τιμή απο τον αισθητήρα μας όταν η επιτάχυνση σε κάποιον άξονα είναι ίση με 1G <14> Αφαιρώντας απο την τιμή του αισθητήρα την τιμή _zero και το αποτέλεσμα τους το διαιρέσουμε με το scale έχουμε ώς αποτέλεσμα την επιτάχυνση ως προς το κάθε άξονα σε G έπειτα περναμε με την τιμή αυτή σε ένα string. <15> Δημιουργία string με κατάλληλα μηνύματα και τις τιμές απο τον αισθητήρα <16> Αποθήκευση του string στον πίνακα Data, ώστε να γίνει η αποστολή του στον master. {empty} + == Κώδικας Python στο Raspberry PI (Master) [source,python] ---- import smbus <1> import time <1> import signal <1> import re <1> from datetime import datetime <1> bus = smbus.SMBus(1) <2> address = 0x16 <3> print ("Reading Data from I2C bus") while True: try: now = datetime.now() <4> data = "" <5> for i in range(0, 50): data += chr(bus.read_byte(address)) <6> date_time = now.strftime("%m/%d/%Y, %H:%M:%S") <7> file = open("sensor_logs", "a") <8> file.write(date_time +" "+ data + "\n") <9> file.close() <10> find_values = re.findall(r'[-\d]*[.][\d]+', data) <11> create_list = list(map(str,find_values)) <12> x_value = (create_list[0]) <13> y_value = (create_list[1]) <13> z_value = (create_list[2]) <13> print (data) time.sleep(1) <14> except KeyboardInterrupt: <15> print("\n"+"Interrupt detected Stopping program") exit(0) ---- <1> Βιβλιοθήκες για την λειτουργία του προγραμμάτος. <2> Επιλογή ποιου απο τα 2 I2C κανάλια χρησιμοποιούμε. <3> Δήλωση της διέυθυνσης του Slave που θα ζητήσουμε δεδομένα. <4> Δημιουργία Timestamp και αποθήκευση της τιμής στην μεταβλητη now. <5> Δημιουργία μεταβλητης για την αποθήκευση των δεδομένων που θα ληφθούν απο το Arduino. <6> Βρογχος για την λήψη των 50 Bytes από το Arduino 1 προς 1. <7> Μορφοποίησει του Timestamp. <8> Άνοιγμα του αρχείου έαν υπάρχει και εγγραφή των τιμών στο τέλος του αρχείου, αλλιώς δημιουργία του αρχέιου και εγγραφή των τιμών. <9> Εγγραφή των δεδομένων με Timestamp στο αρχείου. <10> Κλείσιμο του αρχέιου. <11> "Καθαρισμός" του αρχέιου με χρήση Regex ώστε να απομονώσουμε μόνο τις τιμές. <12> Δημιουργία λίστας με τις απομονωμένες τιμες. <13> Ανάθεση των τιμών της λίστας σε μεταβλητή. <14> Περίμενε ένα δευτερόλεπτο μέχρι να διαβάσεις πάλι τιμές (εάν δεν το βάλουμε ο κανάλι επικοινωνίας θα μέινει μόνιμα ανοιχτό και θα πέρνουμε συνέχεια δεδομένα). <15> Αναγνωρίζει την διακόπή του προγραμμάτος απο τον χρήστη και "καθαρίζει" ότι προσωρινό αρχείο είχε δημιουργηθεί κατά την εκτέλεση του προγραμμάτος μας. {empty} + == Graphana & influxDB [.text-center] image::./photos/influxgraph.png[350,350] [.text-justify] {nbsp}{nbsp}{nbsp}{nbsp} Για την πραγματοποίηση της εργασίας μας χρησιμοποιήθηκε το εργαλέιο Graphana για την εικονικοποίηση των δεδομένων σε γραφήματα και η για την αποθήκευση των δεδομένων επιλέχθηκε η βάση δεδομένων InfluxDB. {empty} + [.text-left] image::./photos/graphana.png[100,100] === Graphana [.text-justify] {nbsp}{nbsp}{nbsp}{nbsp} Το Graphana είναι μία δικτυακή εφαρμογή που με τις κατάλληλες ρυθμίσεις μπορέι να παρέχει διαγράμματα, γραφήματα η ακόμα και ειδοποιήσεις με τα δεδομένα που διαβάζει απο τα data sources της. https://grafana.com/ {empty} + [.text-left] image::./photos/influx.png[100,100] === InfluxDb {nbsp}{nbsp}{nbsp}{nbsp} Η βάση δεδομένων InfluxDB είναι παρομοια με μία βάση τύπου SQL αλλά με αρκετές διαφορές. Η InfluxDB είναι σχεδιασμένη ειδικα για να διαχειρίζεται δεδομένα προσανατολισμένα προς τον χρόνο. Ενώ και οι κλασσικές SQL βάσεις έχουν την ικανοτητα να διαχειριστούν τέτοιου τύπου δεδομένα δεν έχουν την ικανότητα να διαχειριστούν μεγάλο όγκο και υψηλής συχνότητα τέτοιου τύπου δεδομένα. Αυτό το κενό ήρθε να καλύψει η InfluxDB με την ικανότητα της για ανάλυση και αποθήκευση τέτοιου τύπου δεδομένων σε πραγματικό χρόνο. https://www.influxdata.com/ === Εγκατάσταση Graphana & influxDB [WARNING] [.text-justify] {nbsp}{nbsp}{nbsp}{nbsp} Ο οδηγός αυτός απευθύνεται για Εγκατάσταση είτε σε Raspberry Pi 3 και νεότερο, είτε σε υπολογιστές που έχουν εγκαταστήσει το Raspberry Pi OS για οποιαδήποτε, άλλη χρήση θα δημιουργηθούν μικροπροβλήματα που θα πρέπει να αντιμετωπιστούν απο εσάς ανάλογα το σύστημα σας. + [.text-center] Η Εγκατάσταση καθώς και η χρήση των παραπάνω εργαλέιων έγινε σε Raspberry Pi 4 [.text-justify] {nbsp}{nbsp}{nbsp}{nbsp} To script εγκαθιστά και περαιτερω εργαλέια που ίσως να μην είναι χρήσιμα για όλους όπως Node RED, Log2ram, Mosquitto MQTT Broker etc. Δίνεται όμως η επιλογή για μη εγκατάσταση τους ==== Κατέβασμα και εκτέλεση του αυτοματοποιημένου script Κατεβάζουμε το script με την εντολή: [.text-center] wget https://bitbucket.org/api/2.0/snippets/scargill/kAR5qG/master/files/script.sh Στην συνέχεια εκτελούμε: [.text-center] bash ./script.sh [.text-justify] {nbsp}{nbsp}{nbsp}{nbsp} Η Εγκατάσταση θα ξεκινήσει και μετά απο την ολοκλήρωση κάποιων πρώτων εγκαταστάσεων θα γίνει η πρώτη διακοπή για να μας ρωτήσει τι θα χρειαστούμε να εγκαταστήσουμε [.text-center] image::./script_install_photos/1st_stop_&_selections2.png[750,750,title="Επιλογή"] [IMPORTANT] Δεν πρέπει να επιλέξουμε ταυτόχρονα nodenew με τα nodejs και nodered επιλέγουμε μόνο nodenew Η δευτερη διακοπή θα μας ζητήσει εάν θέλουμε να αλλάξουμε το Hostname η να το αφήσουμε Default [.text-center] image::./script_install_photos/2nd_stop_hostname_selection2.png[750,750,title="Hostname"] Η τριτη θα μας ζητήσει να θέσουμε ένα username για το Dashboard του node red Dashboard [.text-center] image::./script_install_photos/3rd_stop_Node_red_dashboard_username.png[750,750,title="Dashboard username"] Η τέταρτη διακοπή θα μας ζητήσει να θέσουμε password για το Dashboard του node red Dashboard και η πέμπτη την επιβεβαίωση του κωδικού [.text-center] image::./script_install_photos/4th_stop_Node_red_dashboard_password.png[750,750,title="Dashboard password"] Η έκτη διακοπή θα μας ζητήσει ένα admin name για το Node-Red και για το MQTT [.text-center] image::./script_install_photos/6th_stop_admin_name_node_red_&_MQQT.png[750,750,title="Admin name"] Η Έβδομη διακοπή θα μας ζητήσει ένα admin password για το Node-Red και για το MQTT και η όγδοη την επιβεβαίωση του κωδικού [.text-center] image::./script_install_photos/7th_stop_Node_red_&_MQQT._password.png[750,750,title="Admin password"] Η ένατη διακοπή θα μα ζητήσει να επιβεβαιώσουμε κάποιες πληροφορίες για πως γίνεται η αποκτηση IP στο σύστημα μας [.text-center] image::./script_install_photos/8th_stop_smb.conf.png[750,750,title="SMB Conf"] [TIP] [.text-justify] Μετά απο αυτές τις διακοπές η εγκατάσταση θα συνεχίστεί και θα διαρκέσει αρκετή ώρα κατά πάσα πιθανότητα πάνω απο 25 λεπτά. [NOTE] [.text-justify] Υπάρχει πιθανότητα η εγκατάσταση να διακοπέι ώστε να μας γίνουν περαιτερω ερωτήσεις για την εγκατάσταση επιλέγουμε ανάλογα με της ιδιοσυγκρασίες του συστηματός μας Τέλος αφού ολοκληρωθέι η εγκατάσταση κάνουμε επανεκκίνηση του συστήματος μας. === Ρύθμιση InfluxDB [.text-justify] {nbsp}{nbsp}{nbsp}{nbsp} Αφού ολοκληρωθέι η επανεκκίνηση του συστήματος μας χρειάζονται 2 αλλαγές στο αρχείο ρυθμίσεων της InfluxDB. + Με καποιον editor της επιλογης μας ανοίγουμε το αρχείο infuxdb.conf με δικαιώματα sudo. [.text-center] sudo nano /etc/influxldb/influxdb.conf Και αναζητούμε τις εγγραφές [source] ------------------- [http] # Determines whether HTTP endpoint is enabled. enabled = true <1> # Determines whether the Flux query endpoint is enabled. # flux-enabled = false # Determines whether the Flux query logging is enabled. # flux-log-enabled = false # The bind address used by the HTTP service. bind-address = ":8086" <2> ------------------- <1> Αφαιρούμε την δίεση για να ενεργοποιηθεί η λειτουργία. <2> Αφαιρούμε την δίεση για να ενεργοποιηθεί η λειτουργία. [.text-justify] Στην σύνέχεια δημιουργούμε έναν χρήστη με δικαιώματα διαχειριστή στην βάση δεδομένων. + Στην γραμμή εντολών πληκτρολογούμε influx [source] ------------------- pi@raspberry:~ $ influx Connected to http://localhost:8086 version 1.8.3 InfluxDB shell version 1.8.3 > CREATE USER "pi" WITH PASSWORD 'raspberry' WITH ALL PRIVILEGES > SHOW USERS user admin ---- ---- pi true > exit ------------------- === Ρύθμιση Graphana [.text-justify] {nbsp}{nbsp}{nbsp}{nbsp} Ανοίγουμε έναν browser και πληκτρολογούμε την διευθυνση του υπολογιστή που εγκαταστήσαμε το grafana και βάζουμε την port 3000 πχ http://127.0.0.1:3000 . Θα μας ζητηθεί να εισαγουμε username και password και τα δύο είναι admin, αφού γίνει η σύνδεση θα μας ζητηθέι να αλλάξουμε το αρχικό password βάζουμε ένα της επιλογής μας και συνεχίζουμε. [.text-justify] Στο κεντρικό μενού πηγαίνουμε configuration Data Source [.text-center] image::./script_install_photos/14th_graphana_data_source.png[750,750,title="Data Source"] Επιλέγουμε Add data source [.text-center] image::./script_install_photos/add_data_source.png[750,750,title="Add data source"] Και μετά InfluxDB [.text-center] image::./script_install_photos/select_InfluxDB.png[750,750,title="Add data source"] Στις ρυθμίσεις για το influxDB βάζουμε [.text-center] image::./script_install_photos/15th_graphana_data_source_config.png[750,750,title="Add data source",link="./script_install_photos/15th_graphana_data_source_config.png"] Στην συνέχεια επιστρέφουμε στο κεντρικό μενου και επιλεγουμε Create import [.text-center] image::./script_install_photos/12th_graphana_import_small.png[750,750,title="Import"] Upload JSON file [.text-center] image::./script_install_photos/13th_graphana_import_menu.png[750,750,title="Json"] και ανεβάζουμε το αρχείο link:json{ext-relative}[JSON] == Προσθήκες στον κώδικα της Python {nbsp}{nbsp}{nbsp}{nbsp} Για να μπορέσουμε να τοποθετήσουμε τα δεδομένα στην βάση που μόλις δημιουργήσαμε θα πρέπει να γίνουν κάποιες προσθήκες στον κώδικα μας έτσι ώστε να στέλνονται τα δεδομένα ταυτόχρονα με τις άλλες λειτουργίες. [source, python] -------------------------- import smbus import time import re from datetime import datetime from influxdb import InfluxDBClient client = InfluxDBClient('127.0.0.1', 8086, 'test', 'test', 'sensor') <1> bus = smbus.SMBus(1) address = 0x16 while True: try: now = datetime.now() data = "" for i in range(0, 40): data += chr(bus.read_byte(address)); date_time = now.strftime("%m/%d/%Y, %H:%M:%S") file = open("sensor_logs", "a") file.write(date_time +" "+ data + "\n") file.close() find_values = re.findall(r'[-]{0,1}[\d]*[\.][\d]+', data) create_list = list(map(str,find_values)) print (create_list) print(create_list[0]) print(create_list[1]) print(create_list[2]) x_value = (create_list[0]) y_value = (create_list[1]) z_value = (create_list[2]) client.write(["logg,sensor_type=lilypad x_value="+x_value+",y_value="+y_value+",z_value="+z_value+""],{'db':'sensor'},204,'line') <2> print (date_time+ " " + data) print("\n") time.sleep(1); except KeyboardInterrupt: print("\n"+"Interrupt detected Stopping program") exit(0) -------------------------- <1> Δημιουργία Σύνδεσης με την βάση (IP που βρίσκεται η βάση δεδομένων μας, Port που βρίσκεται η βάση δεδομένων μας, username του χρήστη για την βάση, password του χρήστη για την βάση, επιλογή βάσης) <2> Αποστολή δεδομένων με την βάση (δημιουργία entry στην βάση sensor με τα δεδομένα που στάλθηκαν απο το Arduino) [.text-justify] {nbsp}{nbsp}{nbsp}{nbsp} Με τις προσθήκες αυτές πλέον ο κώδικας της Python λαμβάνει τα δεδομένα, τα γράφει σε ένα αρχείο τοπικά, τα εμφανίζει στην οθόνη μας και τα στέλνει προς την βάση δεδομένων για αποθήκευση.