update
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user