This commit is contained in:
Your Name
2025-05-22 14:44:17 +02:00
parent ae62c472da
commit 8d1fd7ba63
10 changed files with 1024 additions and 184 deletions

View File

@@ -92,11 +92,13 @@ import json
import serial
import time
import busio
import requests
import re
import os
import traceback
import sys
import sqlite3
import RPi.GPIO as GPIO
from threading import Thread
from datetime import datetime
@@ -130,7 +132,6 @@ conn = sqlite3.connect("/var/www/moduleair_pro_4g/sqlite/sensors.db")
cursor = conn.cursor()
#get config data from SQLite table
def load_config_sqlite():
"""
@@ -235,6 +236,9 @@ def read_complete_response(serial_connection, timeout=2, end_of_response_timeout
'''
Fonction très importante !!!
Reads the complete response from a serial connection and waits for specific lines.
timeout -> temps d'attente de la réponse de la première ligne (assez rapide car le SARA répond direct avec la commande recue)
end_of_response_timeout -> le temps d'inactivité entre deux lignes imprimées (plus long dans certain cas: le SARA mouline avant de finir vraiment)
wait_for_lines -> si on rencontre la string la fonction s'arrete
'''
if wait_for_lines is None:
wait_for_lines = [] # Default to an empty list if not provided
@@ -336,121 +340,116 @@ def send_error_notification(device_id, error_type, additional_info=None):
return False
def modem_complete_reboot_and_reinitialize(modem_version, aircarto_profile_id):
def modem_hardware_reboot():
"""
Performs a complete modem restart sequence:
1. Reboots the modem using the appropriate command for its version
2. Waits for the modem to restart
3. Resets the HTTP profile
4. For SARA-R5, resets the PDP connection
Args:
modem_version (str): The modem version, e.g., 'SARA-R500' or 'SARA-R410'
aircarto_profile_id (int): The HTTP profile ID to reset
Returns:
bool: True if the complete sequence was successful, False otherwise
"""
print('<span style="color: orange;font-weight: bold;">🔄 Complete SARA reboot and reinitialize sequence 🔄</span>')
# Step 1: Reboot the modem - Integrated modem_software_reboot logic
print('<span style="color: orange;font-weight: bold;">🔄 Software SARA reboot! 🔄</span>')
# Use different commands based on modem version
if 'R5' in modem_version: # For SARA-R5 series
command = 'AT+CFUN=16\r' # Normal restart for R5
else: # For SARA-R4 series
command = 'AT+CFUN=15\r' # Factory reset for R4
ser_sara.write(command.encode('utf-8'))
response = read_complete_response(ser_sara, wait_for_lines=["OK", "ERROR"], debug=True)
print('<p class="text-danger-emphasis">')
print(response)
print("</p>", end="")
# Check if reboot command was acknowledged
reboot_success = response is not None and "OK" in response
if not reboot_success:
print("⚠️ Modem reboot command failed")
return False
# Step 2: Wait for the modem to restart (adjust time as needed)
print("Waiting for modem to restart...")
time.sleep(15) # 15 seconds should be enough for most modems to restart
# Step 3: Check if modem is responsive after reboot
print("Checking if modem is responsive...")
ser_sara.write(b'AT\r')
response_check = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=True)
if response_check is None or "OK" not in response_check:
print("⚠️ Modem not responding after reboot")
return False
print("✅ Modem restarted successfully")
# Step 4: Reset the HTTP Profile
print('<span style="color: orange;font-weight: bold;">🔧 Resetting the HTTP Profile</span>')
command = f'AT+UHTTP={aircarto_profile_id},1,"data.nebuleair.fr"\r'
ser_sara.write(command.encode('utf-8'))
responseResetHTTP = read_complete_response(ser_sara, timeout=5, end_of_response_timeout=5,
wait_for_lines=["OK", "+CME ERROR"], debug=True)
print('<p class="text-danger-emphasis">')
print(responseResetHTTP)
print("</p>", end="")
http_reset_success = responseResetHTTP is not None and "OK" in responseResetHTTP
if not http_reset_success:
print("⚠️ HTTP profile reset failed")
# Continue anyway, don't return False here
# Step 5: For SARA-R5, reset the PDP connection
pdp_reset_success = True
if modem_version == "SARA-R500":
print("⚠️ Need to reset PDP connection for SARA-R500")
# Activate PDP context 1
print('➡️ Activate PDP context 1')
command = f'AT+CGACT=1,1\r'
ser_sara.write(command.encode('utf-8'))
response_pdp1 = read_complete_response(ser_sara, wait_for_lines=["OK"])
print(response_pdp1, end="")
pdp_reset_success = pdp_reset_success and (response_pdp1 is not None and "OK" in response_pdp1)
time.sleep(1)
# Set the PDP type
print('➡️ Set the PDP type to IPv4 referring to the output of the +CGDCONT read command')
command = f'AT+UPSD=0,0,0\r'
ser_sara.write(command.encode('utf-8'))
response_pdp2 = read_complete_response(ser_sara, wait_for_lines=["OK"])
print(response_pdp2, end="")
pdp_reset_success = pdp_reset_success and (response_pdp2 is not None and "OK" in response_pdp2)
time.sleep(1)
# Profile #0 is mapped on CID=1
print('➡️ Profile #0 is mapped on CID=1.')
command = f'AT+UPSD=0,100,1\r'
ser_sara.write(command.encode('utf-8'))
response_pdp3 = read_complete_response(ser_sara, wait_for_lines=["OK"])
print(response_pdp3, end="")
pdp_reset_success = pdp_reset_success and (response_pdp3 is not None and "OK" in response_pdp3)
time.sleep(1)
# Activate the PSD profile
print('➡️ Activate the PSD profile #0: the IPv4 address is already assigned by the network.')
command = f'AT+UPSDA=0,3\r'
ser_sara.write(command.encode('utf-8'))
response_pdp4 = read_complete_response(ser_sara, wait_for_lines=["OK", "+UUPSDA"])
print(response_pdp4, end="")
pdp_reset_success = pdp_reset_success and (response_pdp4 is not None and ("OK" in response_pdp4 or "+UUPSDA" in response_pdp4))
time.sleep(1)
if not pdp_reset_success:
print("⚠️ PDP connection reset had some issues")
# Return overall success
return http_reset_success and pdp_reset_success
Performs a hardware reboot using transistors connected to pin 16 and 20:
pin 16 set to SARA GND
pin 20 set to SARA ON (not used)
LOW -> cut the current
HIGH -> current flow
"""
print('<span style="color: orange;font-weight: bold;">🔄 Hardware SARA reboot 🔄</span>')
SARA_power_GPIO = 16
SARA_ON_GPIO = 20
GPIO.setmode(GPIO.BCM) # Use BCM numbering
GPIO.setup(SARA_power_GPIO, GPIO.OUT) # Set GPIO17 as an output
GPIO.output(SARA_power_GPIO, GPIO.LOW)
time.sleep(2)
GPIO.output(SARA_power_GPIO, GPIO.HIGH)
time.sleep(2)
print("Checking if modem is responsive...")
for attempt in range(5):
ser_sara.write(b'AT\r')
response_check = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=True)
if response_check and "OK" in response_check:
print("✅ Modem is responsive after reboot.")
return True
print(f"⏳ Waiting for modem... attempt {attempt + 1}")
time.sleep(2)
else:
print("❌ Modem not responding after reboot.")
return False
def reset_PSD_CSD_connection():
"""
Function that reset the PSD CSD connection for the SARA R5
returns true or false
"""
print("Reseting PDP connection ")
pdp_reset_success = True
# Activate PDP context 1
print('➡️ Activate PDP context 1')
command = f'AT+CGACT=1,1\r'
ser_sara.write(command.encode('utf-8'))
response_pdp1 = read_complete_response(ser_sara, wait_for_lines=["OK"])
print(response_pdp1, end="")
pdp_reset_success = pdp_reset_success and (response_pdp1 is not None and "OK" in response_pdp1)
time.sleep(1)
# Set the PDP type
print('➡️ Set the PDP type to IPv4 referring to the output of the +CGDCONT read command')
command = f'AT+UPSD=0,0,0\r'
ser_sara.write(command.encode('utf-8'))
response_pdp2 = read_complete_response(ser_sara, wait_for_lines=["OK"])
print(response_pdp2, end="")
pdp_reset_success = pdp_reset_success and (response_pdp2 is not None and "OK" in response_pdp2)
time.sleep(1)
# Profile #0 is mapped on CID=1
print('➡️ Profile #0 is mapped on CID=1.')
command = f'AT+UPSD=0,100,1\r'
ser_sara.write(command.encode('utf-8'))
response_pdp3 = read_complete_response(ser_sara, wait_for_lines=["OK"])
print(response_pdp3, end="")
pdp_reset_success = pdp_reset_success and (response_pdp3 is not None and "OK" in response_pdp3)
time.sleep(1)
# Activate the PSD profile
print('➡️ Activate the PSD profile #0: the IPv4 address is already assigned by the network.')
command = f'AT+UPSDA=0,3\r'
ser_sara.write(command.encode('utf-8'))
response_pdp4 = read_complete_response(ser_sara, wait_for_lines=["OK", "+UUPSDA"])
print(response_pdp4, end="")
pdp_reset_success = pdp_reset_success and (response_pdp4 is not None and ("OK" in response_pdp4 or "+UUPSDA" in response_pdp4))
time.sleep(1)
if not pdp_reset_success:
print("⚠️ PDP connection reset had some issues")
return pdp_reset_success
def reset_server_hostname(profile_id):
"""
Function that reset server hostname (URL) connection for the SARA R5
returns true or false
"""
print("Reseting Server Hostname connection ")
http_reset_success = False # Default fallback
if profile_id == 0:
print('<span style="color: orange;font-weight: bold;">🔧 Resetting AirCarto HTTP Profile</span>')
command = f'AT+UHTTP={profile_id},1,"data.nebuleair.fr"\r'
ser_sara.write((command + '\r').encode('utf-8'))
response_SARA_5 = read_complete_response(ser_sara, wait_for_lines=["OK"])
print(response_SARA_5)
time.sleep(1)
http_reset_success = response_SARA_5 is not None and "OK" in response_SARA_5
if not http_reset_success:
print("⚠️ AirCarto HTTP profile reset failed")
elif profile_id ==1:
pass # TODO: implement handling for profile 1
else:
print(f"❌ Unsupported profile ID: {profile_id}")
http_reset_success = False
return http_reset_success
try:
'''
_ ___ ___ ____
@@ -463,11 +462,10 @@ try:
print('<h3>START LOOP</h3>')
#Local timestamp
print("Getting local timestamp")
cursor.execute("SELECT * FROM timestamp_table LIMIT 1")
row = cursor.fetchone() # Get the first (and only) row
rtc_time_str = row[1] # '2025-02-07 12:30:45'
print(rtc_time_str)
print(f"Getting local timestamp: {rtc_time_str}")
if rtc_time_str == 'not connected':
print("⛔ Atttention RTC module not connected⛔")
@@ -488,7 +486,7 @@ try:
# Convert to InfluxDB RFC3339 format with UTC 'Z' suffix
influx_timestamp = dt_object.strftime('%Y-%m-%dT%H:%M:%SZ')
rtc_status = "valid"
print(influx_timestamp)
#print(influx_timestamp)
#NEXTPM
print("Getting NPM values (last 6 measures)")
@@ -591,7 +589,7 @@ try:
payload_json["sensordatavalues"].append({"value_type": "CAIRSENS_NH3", "value": str(averages[2])})
print("Verify SARA R4 connection")
#print("Verify SARA R4 connection")
# Getting the LTE Signal
print("Getting LTE signal")
@@ -608,22 +606,47 @@ try:
#1. No answer at all form SARA
if response2 is None or response2 == "":
print("No answer from SARA module")
print("ATTENTION: No answer from SARA module")
print('🛑STOP LOOP🛑')
print("<hr>")
#Send notification (WIFI)
send_error_notification(device_id, "serial_error")
send_error_notification(device_id, "SERIAL ISSUE ->no answer from sara")
#Hardware Reboot
hardware_reboot_success = modem_hardware_reboot()
if hardware_reboot_success:
print("✅Modem successfully rebooted and reinitialized")
else:
print("⛔There were issues with the modem reboot/reinitialize process")
#end loop
sys.exit()
#2. si on a une erreur
#2. si on a une reponse du SARA mais c'est une erreur
elif "+CME ERROR" in response2:
print(f"SARA module returned error: {response2}")
print("The CSQ command is not supported by this module or in its current state")
print("ATTENTION: SARA is connected over serial but CSQ command not supported")
print('🛑STOP LOOP🛑')
print("<hr>")
#end loop
sys.exit()
#3. On peut avoir une erreur de type "Socket:bind: Treck error 222 : Invalid argument"
elif "Socket:bind: Treck error" in response2:
print(f"SARA module returned error: {response2}")
print("ATTENTION: low-level error from the Treck TCP/IP stack")
print('🛑STOP LOOP🛑')
print("<hr>")
#Send notification (WIFI)
send_error_notification(device_id, "SERIAL ISSUE -> Treck TCP/IP stack error")
#hardware reboot
hardware_reboot_success = modem_hardware_reboot()
if hardware_reboot_success:
print("✅Modem successfully rebooted and reinitialized")
else:
print("⛔There were issues with the modem reboot/reinitialize process")
#end loop
sys.exit()
@@ -643,7 +666,7 @@ try:
print("TRY TO RECONNECT:")
command = f'AT+COPS=1,2,"{selected_networkID}"\r'
ser_sara.write(command.encode('utf-8'))
responseReconnect = read_complete_response(ser_sara, timeout=20, end_of_response_timeout=20)
responseReconnect = read_complete_response(ser_sara, timeout=20, end_of_response_timeout=20, wait_for_lines=["OK", "+CME ERROR", "ERROR"], debug=True)
print('<p class="text-danger-emphasis">')
print(responseReconnect)
print("</p>", end="")
@@ -658,8 +681,14 @@ try:
'''
SEND TO AIRCARTO
____ _____ _ _ ____ _ ___ ____ ____ _ ____ _____ ___
/ ___|| ____| \ | | _ \ / \ |_ _| _ \ / ___| / \ | _ \_ _/ _ \
\___ \| _| | \| | | | | / _ \ | || |_) | | / _ \ | |_) || || | | |
___) | |___| |\ | |_| | / ___ \ | || _ <| |___ / ___ \| _ < | || |_| |
|____/|_____|_| \_|____/ /_/ \_\___|_| \_\\____/_/ \_\_| \_\|_| \___/
'''
print('<p class="fw-bold">➡SEND TO AIRCARTO SERVERS</p>', end="")
# Write Data to saraR4
# 1. Open sensordata_csv.json (with correct data size)
csv_string = ','.join(str(value) if value is not None else '' for value in payload_csv)
@@ -667,14 +696,14 @@ try:
print("Open JSON:")
command = f'AT+UDWNFILE="sensordata_csv.json",{size_of_string}\r'
ser_sara.write(command.encode('utf-8'))
response_SARA_1 = read_complete_response(ser_sara, wait_for_lines=[">"], debug=True)
response_SARA_1 = read_complete_response(ser_sara, wait_for_lines=[">"], debug=False)
print(response_SARA_1)
time.sleep(1)
#2. Write to shell
print("Write data to memory:")
ser_sara.write(csv_string.encode())
response_SARA_2 = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=True)
response_SARA_2 = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=False)
print(response_SARA_2)
#3. Send to endpoint (with device ID)
@@ -750,7 +779,7 @@ try:
# Get error code
print("Getting error code (11->Server connection error, 73->Secure socket connect error)")
print("Getting error code")
command = f'AT+UHTTPER={aircarto_profile_id}\r'
ser_sara.write(command.encode('utf-8'))
response_SARA_9 = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=False)
@@ -760,16 +789,35 @@ try:
# Extract just the error code
error_code = extract_error_code(response_SARA_9)
if error_code is not None:
# Display interpretation based on error code
if error_code == 0:
print('<p class="text-success">No error detected</p>')
elif error_code == 4:
print('<p class="text-danger">Error 4: Invalid server Hostname</p>')
send_error_notification(device_id, "UHTTPER (error n°4) -> Invalid Server Hostname")
server_hostname_resets = reset_server_hostname(aircarto_profile_id)
if server_hostname_resets:
print("✅server hostname reset successfully")
else:
print("⛔There were issues with the modem server hostname reinitialize process")
elif error_code == 11:
print('<p class="text-danger">Error 11: Server connection error</p>')
elif error_code == 22:
print('<p class="text-danger">⚠Error 22: PSD or CSD connection not established (SARA-R5 need to reset PDP conection)⚠️</p>')
send_error_notification(device_id, "UHTTPER (error n°22) -> PSD or CSD connection not established")
psd_csd_resets = reset_PSD_CSD_connection()
if psd_csd_resets:
print("✅PSD CSD connection reset successfully")
else:
print("⛔There were issues with the modem CSD PSD reinitialize process")
elif error_code == 26:
print('<p class="text-danger">Error 26: Connection timed out</p>')
send_error_notification(device_id, "UHTTPER (error n°26) -> Connection timed out")
elif error_code == 44:
print('<p class="text-danger">Error 44: Connection lost</p>')
send_error_notification(device_id, "UHTTPER (error n°44) -> Connection lost")
elif error_code == 73:
print('<p class="text-danger">Error 73: Secure socket connect error</p>')
else:
@@ -779,11 +827,11 @@ try:
#Software Reboot
software_reboot_success = modem_complete_reboot_and_reinitialize(modem_version, aircarto_profile_id)
if software_reboot_success:
print("Modem successfully rebooted and reinitialized")
else:
print("There were issues with the modem reboot/reinitialize process")
#software_reboot_success = modem_complete_reboot_and_reinitialize(modem_version, aircarto_profile_id)
#if software_reboot_success:
# print("Modem successfully rebooted and reinitialized")
#else:
# print("There were issues with the modem reboot/reinitialize process")
# 2.2 code 1 (✅✅HHTP / UUHTTPCR succeded✅✅)
else:
@@ -912,11 +960,11 @@ try:
send_error_notification(device_id, "sara_error")
#Software Reboot
software_reboot_success = modem_complete_reboot_and_reinitialize(modem_version, aircarto_profile_id)
if software_reboot_success:
print("Modem successfully rebooted and reinitialized")
else:
print("There were issues with the modem reboot/reinitialize process")
#software_reboot_success = modem_complete_reboot_and_reinitialize(modem_version, aircarto_profile_id)
#if software_reboot_success:
# print("Modem successfully rebooted and reinitialized")
#else:
# print("There were issues with the modem reboot/reinitialize process")
#5. empty json