diff --git a/SARA/UDP/sendUDP_message.py b/SARA/UDP/sendUDP_message.py new file mode 100644 index 0000000..39e6461 --- /dev/null +++ b/SARA/UDP/sendUDP_message.py @@ -0,0 +1,130 @@ +''' + ____ _ ____ _ + / ___| / \ | _ \ / \ + \___ \ / _ \ | |_) | / _ \ + ___) / ___ \| _ < / ___ \ + |____/_/ \_\_| \_\/_/ \_\ + +Script to set the PDP context for the SARA R5 + + +/usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/UDP/sendUDP_message.py + +''' +import serial +import time +import sys +import json +import re + + + +ser_sara = serial.Serial( + port='/dev/ttyAMA2', + baudrate=115200, #115200 ou 9600 + parity=serial.PARITY_NONE, #PARITY_NONE, PARITY_EVEN or PARITY_ODD + stopbits=serial.STOPBITS_ONE, + bytesize=serial.EIGHTBITS, + timeout = 2 +) + +def read_complete_response(serial_connection, timeout=2, end_of_response_timeout=2, wait_for_lines=None, debug=True): + ''' + Fonction très importante !!! + Reads the complete response from a serial connection and waits for specific lines. + ''' + if wait_for_lines is None: + wait_for_lines = [] # Default to an empty list if not provided + + response = bytearray() + serial_connection.timeout = timeout + end_time = time.time() + end_of_response_timeout + start_time = time.time() + + while True: + elapsed_time = time.time() - start_time # Time since function start + if serial_connection.in_waiting > 0: + data = serial_connection.read(serial_connection.in_waiting) + response.extend(data) + end_time = time.time() + end_of_response_timeout # Reset timeout on new data + + # Decode and check for any target line + decoded_response = response.decode('utf-8', errors='replace') + for target_line in wait_for_lines: + if target_line in decoded_response: + if debug: + print(f"[DEBUG] 🔎 Found target line: {target_line} (in {elapsed_time:.2f}s)") + return decoded_response # Return response immediately if a target line is found + elif time.time() > end_time: + if debug: + print(f"[DEBUG] Timeout reached. No more data received.") + break + time.sleep(0.1) # Short sleep to prevent busy waiting + + # Final response and debug output + total_elapsed_time = time.time() - start_time + if debug: + print(f"[DEBUG] ⏱️ elapsed time: {total_elapsed_time:.2f}s. ⏱️") + # Check if the elapsed time exceeded 10 seconds + if total_elapsed_time > 10 and debug: + print(f"[ALERT] 🚨 The operation took too long 🚨") + print(f'[ALERT] ⚠️{total_elapsed_time:.2f}s⚠️') + + return response.decode('utf-8', errors='replace') # Return the full response if no target line is found + +try: + print('Start script') + + # Increase verbosity + command = f'AT+CMEE=2\r' + ser_sara.write(command.encode('utf-8')) + response_SARA_1 = read_complete_response(ser_sara, wait_for_lines=["OK", "ERROR"]) + print(response_SARA_1, end="") + time.sleep(1) + + # 1. Create SOCKET + print('➡️Create SOCKET') + command = f'AT+USOCR=17\r' + ser_sara.write(command.encode('utf-8')) + response_SARA_1 = read_complete_response(ser_sara, wait_for_lines=["OK", "ERROR"]) + print(response_SARA_1, end="") + time.sleep(1) + + # 2. Retreive Socket ID + match = re.search(r'\+USOCR:\s*(\d+)', response_SARA_1) + if match: + socket_id = match.group(1) + print(f"Socket ID: {socket_id}") + else: + print("Failed to extract socket ID") + + + #3. Connect to UDP server + print("Connect to server:") + command = f'AT+USOCO={socket_id},"192.168.0.20",4242\r' + ser_sara.write(command.encode('utf-8')) + response_SARA_2 = read_complete_response(ser_sara, wait_for_lines=["OK", "+CME ERROR", "ERROR"], debug=False) + print(response_SARA_2) + + # 4. Write data and send + print("Write data:") + command = f'AT+USOWR={socket_id},10\r' + ser_sara.write(command.encode('utf-8')) + response_SARA_2 = read_complete_response(ser_sara, wait_for_lines=["@","OK", "+CME ERROR", "ERROR"], debug=False) + print(response_SARA_2) + + ser_sara.write("1234567890".encode()) + response_SARA_2 = read_complete_response(ser_sara, wait_for_lines=["@","OK", "+CME ERROR", "ERROR"], debug=False) + print(response_SARA_2) + + #Close socket + print("Close socket:") + command = f'AT+USOCL={socket_id}\r' + ser_sara.write(command.encode('utf-8')) + response_SARA_2 = read_complete_response(ser_sara, wait_for_lines=["OK", "+CME ERROR", "ERROR"], debug=False) + print(response_SARA_2) + + +except Exception as e: + print("An error occurred:", e) + traceback.print_exc() # This prints the full traceback \ No newline at end of file diff --git a/html/admin.html b/html/admin.html index c6873cb..20a2443 100755 --- a/html/admin.html +++ b/html/admin.html @@ -118,10 +118,25 @@ +
+ + +
+
+
+ + +
+ +
@@ -365,6 +380,9 @@ window.onload = function() { const checkbox_nmp5channels = document.getElementById("check_NPM_5channels"); const checkbox_wind = document.getElementById("check_WindMeter"); const checkbox_uSpot = document.getElementById("check_uSpot"); + const checkbox_aircarto = document.getElementById("check_aircarto"); + const checkbox_miotiq = document.getElementById("check_miotiq"); + const checkbox_bme = document.getElementById("check_bme280"); const checkbox_envea = document.getElementById("check_envea"); const checkbox_solar = document.getElementById("check_solarBattery"); @@ -378,7 +396,9 @@ window.onload = function() { checkbox_noise.checked = response["NOISE"]; checkbox_uSpot.checked = response["send_uSpot"]; - + checkbox_aircarto.checked = response["send_aircarto"]; + checkbox_miotiq.checked = response["send_miotiq"]; + // If envea is enabled, show the envea sondes container if (response["envea"]) { add_sondeEnveaContainer(); diff --git a/loop/SARA_send_data_v2.py b/loop/SARA_send_data_v2.py index aa90626..c2312dd 100755 --- a/loop/SARA_send_data_v2.py +++ b/loop/SARA_send_data_v2.py @@ -57,6 +57,18 @@ CSV PAYLOAD (AirCarto Servers) 25 -> Wind speed 26 -> Wind direction +CSV FOR UDP (miotiq) + {device_id},{timestamp},{PM1},{PM25},{PM10},{temp},{hum},{press},{avg_noise},{max_noise},{min_noise},{envea_no2},{envea_h2s},{envea_o3},{4g_signal_quality} + 0 -> device ID + 1 -> timestamp + 2 -> PM1 + 3 -> PM2.5 + 4 -> PM10 + 5 -> temp + 6 -> hum + 7 -> press + + JSON PAYLOAD (Micro-Spot Servers) Same as NebuleAir wifi Endpoint: @@ -124,6 +136,8 @@ if uptime_seconds < 120: #Payload CSV to be sent to data.nebuleair.fr payload_csv = [None] * 30 +#Payload UPD to be sent to miotiq +payload_udp = [None] * 30 #Payload JSON to be sent to uSpot payload_json = { "nebuleairid": "XXX", @@ -211,8 +225,10 @@ device_longitude_raw = config.get('longitude_raw', 0) modem_version=config.get('modem_version', "") Sara_baudrate = config.get('SaraR4_baudrate', 115200) selected_networkID = int(config.get('SARA_R4_neworkID', 0)) +send_miotiq = config.get('send_miotiq', True) +send_aircarto = config.get('send_aircarto', True) send_uSpot = config.get('send_uSpot', False) #envoi sur MicroSpot () -npm_5channel = config.get('npm_5channel', False) #5 canaux du NPM +npm_5channel = config.get('npm_5channel', False) #5 canaux du NPM envea_cairsens= config.get('envea', False) wind_meter= config.get('windMeter', False) bme_280_config = config.get('BME280', False) @@ -221,6 +237,7 @@ NOISE_sensor = config.get('NOISE', False) #update device id in the payload json payload_json["nebuleairid"] = device_id +payload_udp[0] = device_id # Skip execution if modem_config_mode is true if modem_config_mode: @@ -648,6 +665,11 @@ try: payload_csv[18] = npm_temp payload_csv[19] = npm_hum + #add data to payload UDP + payload_udp[2] = PM1 + payload_udp[3] = PM25 + payload_udp[4] = PM10 + #Add data to payload JSON payload_json["sensordatavalues"].append({"value_type": "NPM_P0", "value": str(PM1)}) payload_json["sensordatavalues"].append({"value_type": "NPM_P1", "value": str(PM10)}) @@ -863,15 +885,90 @@ try: print('

') print(responseReconnect) print("

", end="") - print('🛑STOP LOOP🛑') print("
") - #on arrete le script pas besoin de continuer sys.exit() else: print("Signal Quality:", signal_quality) + + + ''' + ____ _____ _ _ ____ _ _ ____ ____ + / ___|| ____| \ | | _ \ | | | | _ \| _ \ + \___ \| _| | \| | | | | | | | | | | | |_) | + ___) | |___| |\ | |_| | | |_| | |_| | __/ + |____/|_____|_| \_|____/ \___/|____/|_| + + + ''' + + if send_miotiq: + print('

➡️SEND TO MIOTIQ

', end="") + + #create UDP socket (will return socket number) -> 17 is UDP protocol and 6 is TCP protocol + # IF ERROR -> need to create the PDP connection + print("Create Socket:") + command = f'AT+USOCR=17\r' + ser_sara.write(command.encode('utf-8')) + response_SARA_1 = read_complete_response(ser_sara, wait_for_lines=["OK", "+CME ERROR", "ERROR"], debug=False) + print('

') + print(response_SARA_1) + print("

", end="") + + if "+CME ERROR" in response_SARA_1: + print('⚠️ATTENTION: need to reset PDP connection⚠️') + 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") + + + #Retreive Socket ID + match = re.search(r'\+USOCR:\s*(\d+)', response_SARA_1) + if match: + socket_id = match.group(1) + print(f"Socket ID: {socket_id}") + else: + print("Failed to extract socket ID") + + #Connect to UDP server + print("Connect to server:") + command = f'AT+USOCO={socket_id},"192.168.0.20",4242\r' + ser_sara.write(command.encode('utf-8')) + response_SARA_2 = read_complete_response(ser_sara, wait_for_lines=["OK", "+CME ERROR", "ERROR"], debug=False) + print('

') + print(response_SARA_2) + print("

", end="") + + #prepare data + csv_udp_string = ','.join(str(value) if value is not None else '' for value in payload_udp) + size_of_udp_string = len(csv_udp_string) + + # 4. Write data and send + print("Write data:") + command = f'AT+USOWR={socket_id},{size_of_udp_string}\r' + ser_sara.write(command.encode('utf-8')) + response_SARA_2 = read_complete_response(ser_sara, wait_for_lines=["@","OK", "+CME ERROR", "ERROR"], debug=False) + print(response_SARA_2) + + ser_sara.write(csv_udp_string.encode()) + response_SARA_2 = read_complete_response(ser_sara, wait_for_lines=["@","OK", "+CME ERROR", "ERROR"], debug=False) + print(response_SARA_2) + + + #Close socket + print("Close socket:") + command = f'AT+USOCL={socket_id}\r' + ser_sara.write(command.encode('utf-8')) + response_SARA_2 = read_complete_response(ser_sara, wait_for_lines=["OK", "+CME ERROR", "ERROR"], debug=False) + print('

') + print(response_SARA_2) + print("

", end="") + + ''' ____ _____ _ _ ____ _ ___ ____ ____ _ ____ _____ ___ @@ -882,329 +979,331 @@ try: ''' - print('

➡️SEND TO AIRCARTO SERVERS

', 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) - size_of_string = len(csv_string) - 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=False) - print('

') - print(response_SARA_1) - print("

", end="") + if send_aircarto: - time.sleep(1) + print('

➡️SEND TO AIRCARTO SERVERS

', 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) + size_of_string = len(csv_string) + 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=False) + print('

') + print(response_SARA_1) + print("

", end="") - #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=False) - print(f'

{response_SARA_2.strip()}

', end="") + time.sleep(1) - #3. Send to endpoint (with device ID) - print("Send data (POST REQUEST):") - command= f'AT+UHTTPC={aircarto_profile_id},4,"/pro_4G/data.php?sensor_id={device_id}&lat={device_latitude_raw}&long={device_longitude_raw}&datetime={influx_timestamp}","aircarto_server_response.txt","sensordata_csv.json",4\r' - #print("sending:") - #print('

') - #print(command) - #print("

", end="") + #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=False) + print(f'

{response_SARA_2.strip()}

', end="") - ser_sara.write(command.encode('utf-8')) + #3. Send to endpoint (with device ID) + print("Send data (POST REQUEST):") + command= f'AT+UHTTPC={aircarto_profile_id},4,"/pro_4G/data.php?sensor_id={device_id}&lat={device_latitude_raw}&long={device_longitude_raw}&datetime={influx_timestamp}","aircarto_server_response.txt","sensordata_csv.json",4\r' + #print("sending:") + #print('

') + #print(command) + #print("

", end="") - response_SARA_3 = read_complete_response(ser_sara, timeout=5, end_of_response_timeout=120, wait_for_lines=["+UUHTTPCR", "+CME ERROR", "ERROR"], debug=True) - #print("receiving:") - print('

') - print(response_SARA_3) - print("

", end="") + ser_sara.write(command.encode('utf-8')) - # si on recoit la réponse UHTTPCR - if "+UUHTTPCR" in response_SARA_3: - print("✅ Received +UUHTTPCR response.") + response_SARA_3 = read_complete_response(ser_sara, timeout=5, end_of_response_timeout=120, wait_for_lines=["+UUHTTPCR", "+CME ERROR", "ERROR"], debug=True) + #print("receiving:") + print('

') + print(response_SARA_3) + print("

", end="") - # Les types de réponse + # si on recoit la réponse UHTTPCR + if "+UUHTTPCR" in response_SARA_3: + print("✅ Received +UUHTTPCR response.") - # 1.La commande n'a pas fonctionné - # +CME ERROR: No connection to phone - # +CME ERROR: Operation not allowed + # Les types de réponse - # 2.La commande fonctionne: elle renvoie un code - # +UUHTTPCR: ,, - # : 1 pour sucess et 0 pour fail - # +UUHTTPCR: 0,4,1 -> OK ✅ - # +UUHTTPCR: 0,4,0 -> error ⛔ + # 1.La commande n'a pas fonctionné + # +CME ERROR: No connection to phone + # +CME ERROR: Operation not allowed - # Split response into lines - lines = response_SARA_3.strip().splitlines() + # 2.La commande fonctionne: elle renvoie un code + # +UUHTTPCR: ,, + # : 1 pour sucess et 0 pour fail + # +UUHTTPCR: 0,4,1 -> OK ✅ + # +UUHTTPCR: 0,4,0 -> error ⛔ - # 1.Vérifier si la réponse contient un message d'erreur CME - if "+CME ERROR" in lines[-1]: - print("*****") - print('ATTENTION: CME ERROR') - print("error:", lines[-1]) - print("*****") + # Split response into lines + lines = response_SARA_3.strip().splitlines() - # Gestion de l'erreur spécifique - if "No connection to phone" in lines[-1]: - print("No connection to the phone. Retrying or reset may be required.") - # Actions spécifiques pour ce type d'erreur (par exemple, réinitialiser ou tenter de reconnecter) - # need to reconnect to network - # and reset HTTP profile (AT+UHTTP=0) -> ne fonctionne pas.. - # tester un reset avec CFUN 15 - # 1.Reconnexion au réseau (AT+COPS) - command = f'AT+COPS=1,2,{selected_networkID}\r' - #command = f'AT+COPS=0\r' - ser_sara.write(command.encode('utf-8')) - responseReconnect = read_complete_response(ser_sara) - print("Response reconnect:") - print(responseReconnect) - print("End response reconnect") - - elif "Operation not allowed" in lines[-1]: - print("Operation not allowed. This may require a different configuration.") - # Actions spécifiques pour ce type d'erreur - - # Clignotement LED rouge en cas d'erreur - led_thread = Thread(target=blink_led, args=(24, 5, 0.5)) - led_thread.start() - - else: - # 2.Si la réponse contient une réponse UUHTTPCR - # Extract UUHTTPCR response code from the last line - - http_response = lines[-1] # "+UUHTTPCR: 0,4,0" - parts = http_response.split(',') - - # 2.1 code 0 (HTTP failed) ⛔⛔⛔ - # -> GET error code - # -> reboot module - if len(parts) == 3 and parts[-1] == '0': # The third value indicates success + # 1.Vérifier si la réponse contient un message d'erreur CME + if "+CME ERROR" in lines[-1]: print("*****") - print('⛔ATTENTION: HTTP operation failed') + print('ATTENTION: CME ERROR') + print("error:", lines[-1]) print("*****") - print("Blink red LED") - # Run LED blinking in a separate thread - led_thread = Thread(target=blink_led, args=(24, 5, 0.5)) - led_thread.start() - # Get error code - 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) - print('

') - print(response_SARA_9) - print("

", end="") - - # 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('

No error detected

') - # INVALID SERVER HOSTNAME - elif error_code == 4: - print('

Error 4: AirCarto - Invalid server Hostname

') - send_error_notification(device_id, "UHTTPER (error n°4) -> AirCarto 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") - # SERVER CONNECTION ERROR - elif error_code == 11: - print('

Error 11: Server connection error

') - 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") - # PSD OR CSD ERROR - elif error_code == 22: - print('

⚠️Error 22: PSD or CSD connection not established (SARA-R5 need to reset PDP conection)⚠️

') - 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") - # CONNECTION TIMED OUT - elif error_code == 26: - print('

Error 26: Connection timed out

') - send_error_notification(device_id, "UHTTPER (error n°26) -> Connection timed out") - # CONNECTION LOST - elif error_code == 44: - print('

Error 44: Connection lost

') - send_error_notification(device_id, "UHTTPER (error n°44) -> Connection lost") - # SECURE SOCKET ERROR - elif error_code == 73: - print('

Error 73: Secure socket connect error

') - else: - print(f'

Unknown error code: {error_code}

') - else: - print('

Could not extract error code from response

') - - - - # 2.2 code 1 (✅✅HHTP / UUHTTPCR succeded✅✅) - else: - print('✅✅HTTP operation successful.') - print("Blink blue LED") - led_thread = Thread(target=blink_led, args=(23, 5, 0.5)) - led_thread.start() - - #4. Read reply from server - print("Reply from server:") - command = f'AT+URDFILE="aircarto_server_response.txt""\r' - ser_sara.write((command + '\r').encode('utf-8')) - response_SARA_4 = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=False) - print('

') - print(response_SARA_4) - print("

", end="") - - #Parse the server datetime - # Extract just the date from the response - date_string = None - server_datetime = "" - date_start = response_SARA_4.find("Date: ") - if date_start != -1: - date_end = response_SARA_4.find("\n", date_start) - date_string = response_SARA_4[date_start + 6:date_end].strip() - print(f'
Server date: {date_string}
', end="") - - # Optionally convert to datetime object - try: - from datetime import datetime - server_datetime = datetime.strptime( - date_string, - "%a, %d %b %Y %H:%M:%S %Z" - ) - #print(f'

Parsed datetime: {server_datetime}

') - except Exception as e: - print(f'

Error parsing date: {e}

') - - # Get RTC time from SQLite - cursor.execute("SELECT * FROM timestamp_table LIMIT 1") - row = cursor.fetchone() - rtc_time_str = row[1] # '2025-02-07 12:30:45' or '2000-01-01 00:55:21' or 'not connected' - print(f'
RTC time: {rtc_time_str}
', end="") - - # Compare times if both are available - if server_datetime and rtc_time_str != 'not connected': - try: - # Convert RTC time string to datetime - rtc_datetime = datetime.strptime(rtc_time_str, '%Y-%m-%d %H:%M:%S') - - # Calculate time difference in seconds - time_diff = abs((server_datetime - rtc_datetime).total_seconds()) - - print(f'
Time difference: {time_diff:.2f} seconds
', end="") - - # Check if difference is more than 60 seconds - # and update the RTC clock - if time_diff > 60: - print(f'
⚠️ RTC time differs from server time by {time_diff:.2f} seconds!
', end="") - # Format server time for RTC update - server_time_formatted = server_datetime.strftime('%Y-%m-%d %H:%M:%S') - - #update RTC module do not wait for answer, non blocking - #/usr/bin/python3 /var/www/nebuleair_pro_4g/RTC/set_with_browserTime.py '2024-01-30 12:48:39' - # Launch RTC update script as non-blocking subprocess - import subprocess - update_command = [ - "/usr/bin/python3", - "/var/www/nebuleair_pro_4g/RTC/set_with_browserTime.py", - server_time_formatted - ] - - # Execute the command without waiting for result - subprocess.Popen(update_command, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) - - print(f'
➡️ Updating RTC with server time: {server_time_formatted}
', end="") - - else: - print(f'
✅ RTC time is synchronized with server time (within 60 seconds)
') - - except Exception as e: - print(f'

Error comparing times: {e}

') - - - #Si non ne recoit pas de réponse UHTTPCR - #on a peut être une ERROR de type "+CME ERROR: No connection to phone" ou "Operation not allowed" ou "ERROR" - else: - print('No UUHTTPCR response') - print("Blink red LED") - # Run LED blinking in a separate thread - led_thread = Thread(target=blink_led, args=(24, 5, 0.5)) - led_thread.start() - #Vérification de l'erreur - print("Getting type of error") - # Split the response into lines and search for "+CME ERROR:" - lines2 = response_SARA_3.strip().splitlines() - for line in lines2: - if "+CME ERROR" in line: - error_message = line.split("+CME ERROR:")[1].strip() - print("*****") - print('⚠️ATTENTION: CME ERROR⚠️') - print(f"Error type: {error_message}") - print("*****") - # Handle "No connection to phone" error - if error_message == "No connection to phone": - print('📞Try reconnect to network📞') - #IMPORTANT! - # Reconnexion au réseau (AT+COPS) + # Gestion de l'erreur spécifique + if "No connection to phone" in lines[-1]: + print("No connection to the phone. Retrying or reset may be required.") + # Actions spécifiques pour ce type d'erreur (par exemple, réinitialiser ou tenter de reconnecter) + # need to reconnect to network + # and reset HTTP profile (AT+UHTTP=0) -> ne fonctionne pas.. + # tester un reset avec CFUN 15 + # 1.Reconnexion au réseau (AT+COPS) command = f'AT+COPS=1,2,{selected_networkID}\r' #command = f'AT+COPS=0\r' ser_sara.write(command.encode('utf-8')) - responseReconnect = read_complete_response(ser_sara, timeout=5, end_of_response_timeout=120, wait_for_lines=["OK", "+CME ERROR"], debug=True) - print('

') + responseReconnect = read_complete_response(ser_sara) + print("Response reconnect:") print(responseReconnect) - print("

", end="") - # Handle "Operation not allowed" error - if error_message == "Operation not allowed": - print('❓Try Resetting the HTTP Profile❓') - command = f'AT+UHTTP={aircarto_profile_id},1,"data.nebuleair.fr"\r' - ser_sara.write(command.encode('utf-8')) - responseResetHTTP_profile = read_complete_response(ser_sara, timeout=5, end_of_response_timeout=5, wait_for_lines=["OK", "+CME ERROR"], debug=True) + print("End response reconnect") + + elif "Operation not allowed" in lines[-1]: + print("Operation not allowed. This may require a different configuration.") + # Actions spécifiques pour ce type d'erreur + + # Clignotement LED rouge en cas d'erreur + led_thread = Thread(target=blink_led, args=(24, 5, 0.5)) + led_thread.start() + + else: + # 2.Si la réponse contient une réponse UUHTTPCR + # Extract UUHTTPCR response code from the last line + + http_response = lines[-1] # "+UUHTTPCR: 0,4,0" + parts = http_response.split(',') + + # 2.1 code 0 (HTTP failed) ⛔⛔⛔ + # -> GET error code + # -> reboot module + if len(parts) == 3 and parts[-1] == '0': # The third value indicates success + print("*****") + print('⛔ATTENTION: HTTP operation failed') + print("*****") + print("Blink red LED") + # Run LED blinking in a separate thread + led_thread = Thread(target=blink_led, args=(24, 5, 0.5)) + led_thread.start() + + # Get error code + 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) print('

') - print(responseResetHTTP_profile) + print(response_SARA_9) print("

", end="") - check_lines = responseResetHTTP_profile.strip().splitlines() - for line in check_lines: - if "+CME ERROR: Operation not allowed" in line: - print('⚠️ATTENTION: CME ERROR⚠️') - print('❓Try Reboot the module❓') - #Software Reboot - if "ERROR" in line: - print("⛔Attention ERROR!⛔") - #Send notification (WIFI) - send_error_notification(device_id, "SARA CME ERROR") + # Extract just the error code + error_code = extract_error_code(response_SARA_9) - #Hardware Reboot - hardware_reboot_success = modem_hardware_reboot() - if hardware_reboot_success: - print("✅Modem successfully rebooted and reinitialized") + if error_code is not None: + # Display interpretation based on error code + if error_code == 0: + print('

No error detected

') + # INVALID SERVER HOSTNAME + elif error_code == 4: + print('

Error 4: AirCarto - Invalid server Hostname

') + send_error_notification(device_id, "UHTTPER (error n°4) -> AirCarto 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") + # SERVER CONNECTION ERROR + elif error_code == 11: + print('

Error 11: Server connection error

') + 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") + # PSD OR CSD ERROR + elif error_code == 22: + print('

⚠️Error 22: PSD or CSD connection not established (SARA-R5 need to reset PDP conection)⚠️

') + 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") + # CONNECTION TIMED OUT + elif error_code == 26: + print('

Error 26: Connection timed out

') + send_error_notification(device_id, "UHTTPER (error n°26) -> Connection timed out") + # CONNECTION LOST + elif error_code == 44: + print('

Error 44: Connection lost

') + send_error_notification(device_id, "UHTTPER (error n°44) -> Connection lost") + # SECURE SOCKET ERROR + elif error_code == 73: + print('

Error 73: Secure socket connect error

') + else: + print(f'

Unknown error code: {error_code}

') + else: + print('

Could not extract error code from response

') + + + + # 2.2 code 1 (✅✅HHTP / UUHTTPCR succeded✅✅) else: - print("⛔There were issues with the modem reboot/reinitialize process") + print('✅✅HTTP operation successful.') + print("Blink blue LED") + led_thread = Thread(target=blink_led, args=(23, 5, 0.5)) + led_thread.start() + + #4. Read reply from server + print("Reply from server:") + command = f'AT+URDFILE="aircarto_server_response.txt""\r' + ser_sara.write((command + '\r').encode('utf-8')) + response_SARA_4 = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=False) + print('

') + print(response_SARA_4) + print("

", end="") + + #Parse the server datetime + # Extract just the date from the response + date_string = None + server_datetime = "" + date_start = response_SARA_4.find("Date: ") + if date_start != -1: + date_end = response_SARA_4.find("\n", date_start) + date_string = response_SARA_4[date_start + 6:date_end].strip() + print(f'
Server date: {date_string}
', end="") + + # Optionally convert to datetime object + try: + from datetime import datetime + server_datetime = datetime.strptime( + date_string, + "%a, %d %b %Y %H:%M:%S %Z" + ) + #print(f'

Parsed datetime: {server_datetime}

') + except Exception as e: + print(f'

Error parsing date: {e}

') + + # Get RTC time from SQLite + cursor.execute("SELECT * FROM timestamp_table LIMIT 1") + row = cursor.fetchone() + rtc_time_str = row[1] # '2025-02-07 12:30:45' or '2000-01-01 00:55:21' or 'not connected' + print(f'
RTC time: {rtc_time_str}
', end="") + + # Compare times if both are available + if server_datetime and rtc_time_str != 'not connected': + try: + # Convert RTC time string to datetime + rtc_datetime = datetime.strptime(rtc_time_str, '%Y-%m-%d %H:%M:%S') + + # Calculate time difference in seconds + time_diff = abs((server_datetime - rtc_datetime).total_seconds()) + + print(f'
Time difference: {time_diff:.2f} seconds
', end="") + + # Check if difference is more than 60 seconds + # and update the RTC clock + if time_diff > 60: + print(f'
⚠️ RTC time differs from server time by {time_diff:.2f} seconds!
', end="") + # Format server time for RTC update + server_time_formatted = server_datetime.strftime('%Y-%m-%d %H:%M:%S') + + #update RTC module do not wait for answer, non blocking + #/usr/bin/python3 /var/www/nebuleair_pro_4g/RTC/set_with_browserTime.py '2024-01-30 12:48:39' + # Launch RTC update script as non-blocking subprocess + import subprocess + update_command = [ + "/usr/bin/python3", + "/var/www/nebuleair_pro_4g/RTC/set_with_browserTime.py", + server_time_formatted + ] + + # Execute the command without waiting for result + subprocess.Popen(update_command, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + + print(f'
➡️ Updating RTC with server time: {server_time_formatted}
', end="") + + else: + print(f'
✅ RTC time is synchronized with server time (within 60 seconds)
') + + except Exception as e: + print(f'

Error comparing times: {e}

') - #5. empty json - print("Empty SARA memory:") - command = f'AT+UDELFILE="sensordata_csv.json"\r' - ser_sara.write((command + '\r').encode('utf-8')) - response_SARA_5 = read_complete_response(ser_sara, wait_for_lines=["OK","+CME ERROR"], debug=True) - print('

') - print(response_SARA_5) - print("

", end="") + #Si non ne recoit pas de réponse UHTTPCR + #on a peut être une ERROR de type "+CME ERROR: No connection to phone" ou "Operation not allowed" ou "ERROR" + else: + print('No UUHTTPCR response') + print("Blink red LED") + # Run LED blinking in a separate thread + led_thread = Thread(target=blink_led, args=(24, 5, 0.5)) + led_thread.start() + #Vérification de l'erreur + print("Getting type of error") + # Split the response into lines and search for "+CME ERROR:" + lines2 = response_SARA_3.strip().splitlines() + for line in lines2: + if "+CME ERROR" in line: + error_message = line.split("+CME ERROR:")[1].strip() + print("*****") + print('⚠️ATTENTION: CME ERROR⚠️') + print(f"Error type: {error_message}") + print("*****") + # Handle "No connection to phone" error + if error_message == "No connection to phone": + print('📞Try reconnect to network📞') + #IMPORTANT! + # Reconnexion au réseau (AT+COPS) + command = f'AT+COPS=1,2,{selected_networkID}\r' + #command = f'AT+COPS=0\r' + ser_sara.write(command.encode('utf-8')) + responseReconnect = read_complete_response(ser_sara, timeout=5, end_of_response_timeout=120, wait_for_lines=["OK", "+CME ERROR"], debug=True) + print('

') + print(responseReconnect) + print("

", end="") + # Handle "Operation not allowed" error + if error_message == "Operation not allowed": + print('❓Try Resetting the HTTP Profile❓') + command = f'AT+UHTTP={aircarto_profile_id},1,"data.nebuleair.fr"\r' + ser_sara.write(command.encode('utf-8')) + responseResetHTTP_profile = read_complete_response(ser_sara, timeout=5, end_of_response_timeout=5, wait_for_lines=["OK", "+CME ERROR"], debug=True) + print('

') + print(responseResetHTTP_profile) + print("

", end="") + check_lines = responseResetHTTP_profile.strip().splitlines() + for line in check_lines: + if "+CME ERROR: Operation not allowed" in line: + print('⚠️ATTENTION: CME ERROR⚠️') + print('❓Try Reboot the module❓') + #Software Reboot - if "+CME ERROR" in response_SARA_5: - print("⛔ Attention CME ERROR ⛔") - + if "ERROR" in line: + print("⛔Attention ERROR!⛔") + #Send notification (WIFI) + send_error_notification(device_id, "SARA CME 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") + + + #5. empty json + print("Empty SARA memory:") + command = f'AT+UDELFILE="sensordata_csv.json"\r' + ser_sara.write((command + '\r').encode('utf-8')) + response_SARA_5 = read_complete_response(ser_sara, wait_for_lines=["OK","+CME ERROR"], debug=True) + print('

') + print(response_SARA_5) + print("

", end="") + + if "+CME ERROR" in response_SARA_5: + print("⛔ Attention CME ERROR ⛔") + diff --git a/sqlite/set_config.py b/sqlite/set_config.py index 2aeedbe..a75dbc0 100644 --- a/sqlite/set_config.py +++ b/sqlite/set_config.py @@ -41,7 +41,9 @@ config_entries = [ ("SARA_R4_network_status", "connected", "str"), ("SARA_R4_neworkID", "20810", "int"), ("WIFI_status", "connected", "str"), + ("send_aircarto", "1", "bool"), ("send_uSpot", "0", "bool"), + ("send_miotiq", "0", "bool"), ("npm_5channel", "0", "bool"), ("envea", "0", "bool"), ("windMeter", "0", "bool"),