diff --git a/NPM/get_data_modbus_v3.py b/NPM/get_data_modbus_v3.py index 965e653..b766141 100755 --- a/NPM/get_data_modbus_v3.py +++ b/NPM/get_data_modbus_v3.py @@ -39,7 +39,7 @@ import sqlite3 import time # Connect to the SQLite database -conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db") +conn = sqlite3.connect("/var/www/moduleair_pro_4g/sqlite/sensors.db") cursor = conn.cursor() def load_config(config_file): @@ -52,7 +52,7 @@ def load_config(config_file): return {} # Load the configuration data -config_file = '/var/www/nebuleair_pro_4g/config.json' +config_file = '/var/www/moduleair_pro_4g/config.json' config = load_config(config_file) npm_solo_port = config.get('NPM_solo_port', '') #port du NPM solo diff --git a/README.md b/README.md index 3c563e9..21b9fc4 100755 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ git config --global user.email "paulvuarambon@gmail.com" git config --global user.name "PaulVua" sudo gh repo clone aircarto/moduleair_pro_4g /var/www/moduleair_pro_4g sudo mkdir /var/www/moduleair_pro_4g/logs -sudo touch /var/www/moduleair_pro_4g/logs/app.log /var/www/moduleair_pro_4g/logs/loop.log matrix/input_NPM.txt matrix/input_MHZ16.txt +sudo touch /var/www/moduleair_pro_4g/logs/app.log /var/www/moduleair_pro_4g/logs/loop.log /var/www/moduleair_pro_4g/matrix/input_NPM.txt /var/www/moduleair_pro_4g/matrix/input_MHZ16.txt /var/www/moduleair_pro_4g/wifi_list.csv sudo cp /var/www/moduleair_pro_4g/config.json.dist /var/www/moduleair_pro_4g/config.json sudo chmod -R 777 /var/www/moduleair_pro_4g/ ``` @@ -53,6 +53,25 @@ www-data ALL=(ALL) NOPASSWD: /usr/bin/ssh www-data ALL=(ALL) NOPASSWD: /usr/bin/python3 * ``` +## I2C + +Decibel meter, BME280 and the RTC module (DS3231) is connected via I2C. +Need to activate by modifying `sudo nano /boot/firmware/config.txt` + +``` +dtparam=i2c_arm=on +``` + +And authorize access to `/dev/i2c-1`. + +``` +sudo chmod 777 /dev/i2c-1 +``` + +Attention: sometimes activation with config.txt do not work, you need to activate i2c with `sudo raspi-config` and go to "Interface" -> I2C -> enable. +It is possible to manage raspi-config only with cli: `sudo raspi-config nonint do_i2c 0` +I2C addresses: use `sudo i2cdetect -y 1` to check the connected devices + ## Matrix LED ### Library diff --git a/RTC/read.py b/RTC/read.py new file mode 100755 index 0000000..b3c1cf6 --- /dev/null +++ b/RTC/read.py @@ -0,0 +1,77 @@ +''' + ____ _____ ____ + | _ \_ _/ ___| + | |_) || || | + | _ < | || |___ + |_| \_\|_| \____| + +Script to read time from RTC module +I2C connection +Address 0x68 +/usr/bin/python3 /var/www/moduleair_pro_4g/RTC/read.py +''' +import smbus2 +import time +import json +from datetime import datetime + +# DS3231 I2C address +DS3231_ADDR = 0x68 + +# Registers for DS3231 +REG_TIME = 0x00 + +def bcd_to_dec(bcd): + return (bcd // 16 * 10) + (bcd % 16) + +def read_time(bus): + """Try to read and decode time from the RTC module (DS3231).""" + try: + data = bus.read_i2c_block_data(DS3231_ADDR, REG_TIME, 7) + seconds = bcd_to_dec(data[0] & 0x7F) + minutes = bcd_to_dec(data[1]) + hours = bcd_to_dec(data[2] & 0x3F) + day = bcd_to_dec(data[4]) + month = bcd_to_dec(data[5]) + year = bcd_to_dec(data[6]) + 2000 + return datetime(year, month, day, hours, minutes, seconds) + except OSError: + return None # RTC module not connected + +def main(): + # Read RTC time + bus = smbus2.SMBus(1) + # Try to read RTC time + rtc_time = read_time(bus) + + # Get current system time + system_time = datetime.now() #local + utc_time = datetime.utcnow() #UTC + + # If RTC is not connected, set default message + # Calculate time difference (in seconds) if RTC is connected + if rtc_time: + rtc_time_str = rtc_time.strftime('%Y-%m-%d %H:%M:%S') + time_difference = int((utc_time - rtc_time).total_seconds()) # Convert to int + else: + rtc_time_str = "not connected" + time_difference = "N/A" # Not applicable + + # Print both times + #print(f"RTC module Time: {rtc_time.strftime('%Y-%m-%d %H:%M:%S')}") + #print(f"Sys local Time: {system_time.strftime('%Y-%m-%d %H:%M:%S')}") + #print(f"Sys UTC Time: {utc_time.strftime('%Y-%m-%d %H:%M:%S')}") + + # Create JSON output + time_data = { + "rtc_module_time":rtc_time_str, + "system_local_time": system_time.strftime('%Y-%m-%d %H:%M:%S'), + "system_utc_time": utc_time.strftime('%Y-%m-%d %H:%M:%S'), + "time_difference_seconds": time_difference + } + + print(json.dumps(time_data, indent=4)) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/RTC/save_to_db.py b/RTC/save_to_db.py new file mode 100755 index 0000000..ab069c5 --- /dev/null +++ b/RTC/save_to_db.py @@ -0,0 +1,90 @@ +''' + ____ _____ ____ + | _ \_ _/ ___| + | |_) || || | + | _ < | || |___ + |_| \_\|_| \____| + +Script to read time from RTC module and save it to DB +I2C connection +Address 0x68 +/usr/bin/python3 /var/www/moduleair_pro_4g/RTC/save_to_db.py +''' +import smbus2 +import time +import json +from datetime import datetime +import sqlite3 + +# Connect to (or create if not existent) the database +conn = sqlite3.connect("/var/www/moduleair_pro_4g/sqlite/sensors.db") +cursor = conn.cursor() + +# DS3231 I2C address +DS3231_ADDR = 0x68 + +# Registers for DS3231 +REG_TIME = 0x00 + +def bcd_to_dec(bcd): + return (bcd // 16 * 10) + (bcd % 16) + +def read_time(bus): + """Try to read and decode time from the RTC module (DS3231).""" + try: + data = bus.read_i2c_block_data(DS3231_ADDR, REG_TIME, 7) + seconds = bcd_to_dec(data[0] & 0x7F) + minutes = bcd_to_dec(data[1]) + hours = bcd_to_dec(data[2] & 0x3F) + day = bcd_to_dec(data[4]) + month = bcd_to_dec(data[5]) + year = bcd_to_dec(data[6]) + 2000 + return datetime(year, month, day, hours, minutes, seconds) + except OSError: + return None # RTC module not connected + +def main(): + # Read RTC time + bus = smbus2.SMBus(1) + # Try to read RTC time + rtc_time = read_time(bus) + + # Get current system time + system_time = datetime.now() #local + utc_time = datetime.utcnow() #UTC + + # If RTC is not connected, set default message + # Calculate time difference (in seconds) if RTC is connected + if rtc_time: + rtc_time_str = rtc_time.strftime('%Y-%m-%d %H:%M:%S') + time_difference = int((utc_time - rtc_time).total_seconds()) # Convert to int + else: + rtc_time_str = "not connected" + time_difference = "N/A" # Not applicable + + # Print both times + #print(f"RTC module Time: {rtc_time.strftime('%Y-%m-%d %H:%M:%S')}") + #print(f"Sys local Time: {system_time.strftime('%Y-%m-%d %H:%M:%S')}") + #print(f"Sys UTC Time: {utc_time.strftime('%Y-%m-%d %H:%M:%S')}") + + # Create JSON output + time_data = { + "rtc_module_time":rtc_time_str, + "system_local_time": system_time.strftime('%Y-%m-%d %H:%M:%S'), + "system_utc_time": utc_time.strftime('%Y-%m-%d %H:%M:%S'), + "time_difference_seconds": time_difference + } + + #print(json.dumps(time_data, indent=4)) + + cursor.execute("UPDATE timestamp_table SET last_updated = ? WHERE id = 1", (rtc_time_str,)) + + # Commit and close the connection + conn.commit() + conn.close() + + #print("Sensor data saved successfully!") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/RTC/set_with_NTP.py b/RTC/set_with_NTP.py new file mode 100755 index 0000000..5e0593a --- /dev/null +++ b/RTC/set_with_NTP.py @@ -0,0 +1,97 @@ +""" +Script to set the RTC using an NTP server. +RPI needs to be connected to the internet (WIFI). +Requires ntplib and pytz: +sudo pip3 install ntplib pytz --break-system-packages + +/usr/bin/python3 /var/www/moduleair_pro_4g/RTC/set_with_NTP.py + +""" +import smbus2 +import time +from datetime import datetime +import ntplib +import pytz # For timezone handling + +# DS3231 I2C address +DS3231_ADDR = 0x68 + +# Registers for DS3231 +REG_TIME = 0x00 + +def bcd_to_dec(bcd): + """Convert BCD to decimal.""" + return (bcd // 16 * 10) + (bcd % 16) + +def dec_to_bcd(dec): + """Convert decimal to BCD.""" + return (dec // 10 * 16) + (dec % 10) + +def set_time(bus, year, month, day, hour, minute, second): + """Set the RTC time.""" + # Convert the time to BCD format + second_bcd = dec_to_bcd(second) + minute_bcd = dec_to_bcd(minute) + hour_bcd = dec_to_bcd(hour) + day_bcd = dec_to_bcd(day) + month_bcd = dec_to_bcd(month) + year_bcd = dec_to_bcd(year - 2000) # DS3231 uses year from 2000 + + # Write time to DS3231 + bus.write_i2c_block_data(DS3231_ADDR, REG_TIME, [ + second_bcd, + minute_bcd, + hour_bcd, + 0x01, # Day of the week (1=Monday, etc.) + day_bcd, + month_bcd, + year_bcd + ]) + +def read_time(bus): + """Read the RTC time.""" + data = bus.read_i2c_block_data(DS3231_ADDR, REG_TIME, 7) + second = bcd_to_dec(data[0] & 0x7F) + minute = bcd_to_dec(data[1]) + hour = bcd_to_dec(data[2] & 0x3F) + day = bcd_to_dec(data[4]) + month = bcd_to_dec(data[5]) + year = bcd_to_dec(data[6]) + 2000 + return (year, month, day, hour, minute, second) + +def get_internet_time(): + """Get the current time from an NTP server.""" + ntp_client = ntplib.NTPClient() + response = ntp_client.request('pool.ntp.org') + utc_time = datetime.utcfromtimestamp(response.tx_time) + return utc_time + +def main(): + bus = smbus2.SMBus(1) + + # Get the current time from the RTC + year, month, day, hours, minutes, seconds = read_time(bus) + rtc_time = datetime(year, month, day, hours, minutes, seconds) + + # Get current UTC time from an NTP server + try: + internet_utc_time = get_internet_time() + print(f"Time from Internet (UTC) : {internet_utc_time.strftime('%Y-%m-%d %H:%M:%S')}") + except Exception as e: + print(f"Error retrieving time from the internet: {e}") + return + + # Print current RTC time + print(f"Actual RTC Time : {rtc_time.strftime('%Y-%m-%d %H:%M:%S')}") + + # Set the RTC to UTC time + set_time(bus, internet_utc_time.year, internet_utc_time.month, internet_utc_time.day, + internet_utc_time.hour, internet_utc_time.minute, internet_utc_time.second) + + # Read and print the new time from RTC + year, month, day, hour, minute, second = read_time(bus) + rtc_time_new = datetime(year, month, day, hour, minute, second) + print(f"New RTC Time (UTC) : {rtc_time_new.strftime('%Y-%m-%d %H:%M:%S')}") + +if __name__ == "__main__": + main() diff --git a/RTC/set_with_browserTime.py b/RTC/set_with_browserTime.py new file mode 100755 index 0000000..0519b9c --- /dev/null +++ b/RTC/set_with_browserTime.py @@ -0,0 +1,47 @@ +""" +Script to set the RTC using the browser time. + +/usr/bin/python3 /var/www/moduleair_pro_4g/RTC/set_with_browserTime.py '2024-01-30 12:48:39' + +""" + +import sys +from datetime import datetime +import smbus2 + +# DS3231 I2C address +DS3231_ADDR = 0x68 +REG_TIME = 0x00 + +def dec_to_bcd(dec): + """Convert decimal to BCD.""" + return (dec // 10 * 16) + (dec % 10) + +def set_rtc(bus, year, month, day, hour, minute, second): + """Set the RTC time.""" + second_bcd = dec_to_bcd(second) + minute_bcd = dec_to_bcd(minute) + hour_bcd = dec_to_bcd(hour) + day_bcd = dec_to_bcd(day) + month_bcd = dec_to_bcd(month) + year_bcd = dec_to_bcd(year - 2000) # RTC stores years since 2000 + + bus.write_i2c_block_data(DS3231_ADDR, REG_TIME, [ + second_bcd, minute_bcd, hour_bcd, 0x01, day_bcd, month_bcd, year_bcd + ]) + +def main(): + if len(sys.argv) != 2: + print("Usage: python3 set_rtc.py 'YYYY-MM-DD HH:MM:SS'") + sys.exit(1) + + rtc_time_str = sys.argv[1] + rtc_time = datetime.strptime(rtc_time_str, '%Y-%m-%d %H:%M:%S') + + bus = smbus2.SMBus(1) + set_rtc(bus, rtc_time.year, rtc_time.month, rtc_time.day, + rtc_time.hour, rtc_time.minute, rtc_time.second) + print(f"RTC updated to: {rtc_time}") + +if __name__ == "__main__": + main() diff --git a/boot_hotspot.sh b/boot_hotspot.sh new file mode 100755 index 0000000..567cae9 --- /dev/null +++ b/boot_hotspot.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# Script to check if wifi is connected and start hotspot if not +# will also retreive unique RPi ID and store it to deviceID.txt +# Script launched by cron job +# @reboot /var/www/moduleair_pro_4g/boot_hotspot.sh >> /var/www/moduleair_pro_4g/logs/app.log 2>&1 + +OUTPUT_FILE="/var/www/moduleair_pro_4g/wifi_list.csv" +JSON_FILE="/var/www/moduleair_pro_4g/config.json" + + + +echo "-------------------" +echo "-------------------" + +echo "NebuleAir pro started at $(date)" + +# Blink GPIO 23 and 24 five times +for i in {1..5}; do + # Turn GPIO 23 and 24 ON + gpioset gpiochip0 23=1 24=1 + #echo "LEDs ON" + sleep 1 + + # Turn GPIO 23 and 24 OFF + gpioset gpiochip0 23=0 24=0 + #echo "LEDs OFF" + sleep 1 +done + +echo "getting SARA R4 serial number" +# Get the last 8 characters of the serial number and write to text file +serial_number=$(cat /proc/cpuinfo | grep Serial | awk '{print substr($3, length($3) - 7)}') +# Define the JSON file path +# Use jq to update the "deviceID" in the JSON file +jq --arg serial_number "$serial_number" '.deviceID = $serial_number' "$JSON_FILE" > temp.json && mv temp.json "$JSON_FILE" +echo "id: $serial_number" +#get the SSH port for tunneling +SSH_TUNNEL_PORT=$(jq -r '.sshTunnel_port' "$JSON_FILE") + +#need to wait for the network manager to be ready +sleep 20 +# Get the connection state of wlan0 +STATE=$(nmcli -g GENERAL.STATE device show wlan0) + +# Check if the state is 'disconnected' +if [ "$STATE" == "30 (disconnected)" ]; then + echo "wlan0 is disconnected." + echo "need to perform a wifi scan" + # Perform a wifi scan and save its output to a csv file + # nmcli device wifi list + nmcli -f SSID,SIGNAL,SECURITY device wifi list | awk 'BEGIN { OFS=","; print "SSID,SIGNAL,SECURITY" } NR>1 { print $1,$2,$3 }' > "$OUTPUT_FILE" + # Start the hotspot + echo "Starting hotspot..." + sudo nmcli device wifi hotspot ifname wlan0 ssid nebuleair_pro password nebuleaircfg + + # Update JSON to reflect hotspot mode + jq --arg status "hotspot" '.WIFI_status = $status' "$JSON_FILE" > temp.json && mv temp.json "$JSON_FILE" + + +else + echo "Success: wlan0 is connected!" + CONN_SSID=$(nmcli -g GENERAL.CONNECTION device show wlan0) + echo "Connection: $CONN_SSID" + + #update config JSON file + jq --arg status "connected" '.WIFI_status = $status' "$JSON_FILE" > temp.json && mv temp.json "$JSON_FILE" + + sudo chmod 777 "$JSON_FILE" + + # Lancer le tunnel SSH + echo "Démarrage du tunnel SSH sur le port $SSH_TUNNEL_PORT..." + # Start the SSH agent if it's not already running + eval "$(ssh-agent -s)" + # Add your SSH private key + ssh-add /home/airlab/.ssh/id_rsa + #connections details + REMOTE_USER="airlab_server1" # Remplacez par votre nom d'utilisateur distant + REMOTE_SERVER="aircarto.fr" # Remplacez par l'adresse de votre serveur + LOCAL_PORT=22 # Port local à rediriger + MONITOR_PORT=0 # Désactive la surveillance de connexion autossh + + #autossh -M "$MONITOR_PORT" -f -N -R "$SSH_TUNNEL_PORT:localhost:$LOCAL_PORT" "$REMOTE_USER@$REMOTE_SERVER" -p 50221 + # ssh -f -N -R 52221:localhost:22 -p 50221 airlab_server1@aircarto.fr + ssh -i /var/www/.ssh/id_rsa -f -N -R "$SSH_TUNNEL_PORT:localhost:$LOCAL_PORT" -p 50221 -o StrictHostKeyChecking=no "$REMOTE_USER@$REMOTE_SERVER" + + #Check if the tunnel was created successfully + if [ $? -eq 0 ]; then + echo "Tunnel started successfully!" + else + echo "Error: Unable to start the tunnel!" + exit 1 + fi +fi +echo "-------------------" diff --git a/connexion.sh b/connexion.sh new file mode 100755 index 0000000..7069064 --- /dev/null +++ b/connexion.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +#script started by the user via the web interface +#$1 and $2 are SSID and Password (arguments passed via the php script launcher.php) + +echo "-------" +echo "Start connexion shell script at $(date)" + + +#disable hotspot +echo "Disable Hotspot:" +sudo nmcli connection down Hotspot +sleep 10 + +echo "Start connection with:" +echo "SSID: $1" +echo "Password: $2" +sudo nmcli device wifi connect "$1" password "$2" + +#check if connection is successfull +if [ $? -eq 0 ]; then + echo "Connection to $1 is successfull" +else + echo "Connection to $1 failed" + echo "Restarting hotspot..." + #enable hotspot + sudo nmcli connection up Hotspot +fi +echo "End connexion shell script" +echo "-------" + + diff --git a/cron_jobs b/cron_jobs index 7a65a58..1be3c50 100755 --- a/cron_jobs +++ b/cron_jobs @@ -1,4 +1,11 @@ @reboot chmod 777 /dev/ttyAMA* /dev/i2c-1 +@reboot /var/www/moduleair_pro_4g/boot_hotspot.sh >> /var/www/moduleair_pro_4g/logs/app.log 2>&1 + +@reboot sleep 30 && /usr/bin/python3 /var/www/moduleair_pro_4g/SARA/sara_setURL.py ttyAMA2 data.moduleair.fr 0 >> /var/www/moduleair_pro_4g/logs/app.log 2>&1 + @reboot /var/www/moduleair_pro_4g/run_every_10_seconds.sh +0 0 * * * > /var/www/moduleair_pro_4g/logs/master.log + + diff --git a/html/assets/img/logoModuleAirColor_long.png b/html/assets/img/logoModuleAirColor_long.png old mode 100644 new mode 100755 diff --git a/html/launcher.php b/html/launcher.php index d77cbd2..a3a9f1b 100755 --- a/html/launcher.php +++ b/html/launcher.php @@ -402,7 +402,7 @@ if ($type == "wifi_connect") { $script_path = '/var/www/moduleair_pro_4g/connexion.sh'; $log_file = '/var/www/moduleair_pro_4g/logs/app.log'; - shell_exec("$script_path $SSID $PASS >> $log_file 2>&1 &"); + shell_exec("$script_path $SSID $PASS >> $log_file 2>&1 &"); #on passe ici en argument le nom du réseau WIFI et le Password #$output = shell_exec('sudo nmcli connection down Hotspot'); #$output2 = shell_exec('sudo nmcli device wifi connect "AirLab" password "123plouf"'); diff --git a/master.py b/master.py index eeb5391..7466ce8 100755 --- a/master.py +++ b/master.py @@ -13,20 +13,21 @@ Attention: First time: need to create the service file --->sudo nano /etc/systemd/system/master_nebuleair.service - +--> +sudo nano /etc/systemd/system/master_nebuleair.service +<-- ⬇️ [Unit] Description=Master manager for the Python loop scripts After=network.target [Service] -ExecStart=/usr/bin/python3 /var/www/nebuleair_pro_4g/master.py +ExecStart=/usr/bin/python3 /var/www/moduleair_pro_4g/master.py Restart=always User=root -WorkingDirectory=/var/www/nebuleair_pro_4g -StandardOutput=append:/var/www/nebuleair_pro_4g/logs/master.log -StandardError=append:/var/www/nebuleair_pro_4g/logs/master_errors.log +WorkingDirectory=/var/www/moduleair_pro_4g +StandardOutput=append:/var/www/moduleair_pro_4g/logs/master.log +StandardError=append:/var/www/moduleair_pro_4g/logs/master_errors.log [Install] WantedBy=multi-user.target @@ -56,8 +57,8 @@ import json import os # Base directory where scripts are stored -SCRIPT_DIR = "/var/www/nebuleair_pro_4g/" -CONFIG_FILE = "/var/www/nebuleair_pro_4g/config.json" +SCRIPT_DIR = "/var/www/moduleair_pro_4g/" +CONFIG_FILE = "/var/www/moduleair_pro_4g/config.json" def load_config(): """Load the configuration file to determine which scripts to run.""" diff --git a/sqlite/create_db.py b/sqlite/create_db.py old mode 100644 new mode 100755 diff --git a/sqlite/flush_old_data.py b/sqlite/flush_old_data.py old mode 100644 new mode 100755 diff --git a/sqlite/read.py b/sqlite/read.py old mode 100644 new mode 100755 diff --git a/sqlite/read_select_date.py b/sqlite/read_select_date.py old mode 100644 new mode 100755 diff --git a/sqlite/write.py b/sqlite/write.py old mode 100644 new mode 100755