diff --git a/.gitignore b/.gitignore index cd79c0f..784128e 100755 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ matrix/input_MHZ16.txt wifi_list.csv *.lock sqlite/*.db +sqlite/*.db* tests/ \ No newline at end of file diff --git a/boot_hotspot.sh b/boot_hotspot.sh index 5f354a3..7b71688 100755 --- a/boot_hotspot.sh +++ b/boot_hotspot.sh @@ -10,7 +10,7 @@ OUTPUT_FILE="/var/www/moduleair_pro_4g/wifi_list.csv" echo "-------------------" echo "-------------------" -echo "NebuleAir pro started at $(date)" +echo "ModuleAir pro started at $(date)" echo "getting RPI serial number" @@ -52,7 +52,7 @@ else echo "Connection: $CONN_SSID" # Update SQLite to reflect hotspot mode - sqlite3 /var/www/nebuleair_pro_4g/sqlite/sensors.db "UPDATE config_table SET value='connected' WHERE key='WIFI_status'" + sqlite3 /var/www/moduleair_pro_4g/sqlite/sensors.db "UPDATE config_table SET value='connected' WHERE key='WIFI_status'" # Lancer le tunnel SSH #echo "Démarrage du tunnel SSH sur le port $SSH_TUNNEL_PORT..." diff --git a/cron_jobs b/cron_jobs index 28d0d87..d785d6d 100755 --- a/cron_jobs +++ b/cron_jobs @@ -2,10 +2,9 @@ @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/reboot/start.py >> /var/www/moduleair_pro_4g/logs/app.log 2>&1 - -@reboot /var/www/moduleair_pro_4g/run_every_10_seconds.sh +#@reboot sleep 30 && /usr/bin/python3 /var/www/moduleair_pro_4g/SARA/reboot/start.py >> /var/www/moduleair_pro_4g/logs/app.log 2>&1 0 0 * * * > /var/www/moduleair_pro_4g/logs/master.log +0 0 * * * find /var/www/nebuleair_pro_4g/logs -name "*.log" -type f -exec truncate -s 0 {} \; diff --git a/loop/SARA_send_data_v2.py b/loop/SARA_send_data_v2.py index 0d1700d..28cd293 100755 --- a/loop/SARA_send_data_v2.py +++ b/loop/SARA_send_data_v2.py @@ -122,50 +122,108 @@ uSpot_profile_id = 1 conn = sqlite3.connect("/var/www/moduleair_pro_4g/sqlite/sensors.db") cursor = conn.cursor() -def update_config_sqlite(key, value, value_type=None): +def update_config_sqlite(key, value, value_type=None, max_retries=4, retry_delay=1.0): """ - Update or insert a configuration value in SQLite config table + Update or insert a configuration value in SQLite config table with retry logic Args: key (str): Configuration key value: Configuration value (any type) value_type (str, optional): Force specific type ('str', 'int', 'float', 'bool') If None, auto-detect from value type + max_retries (int): Maximum number of retry attempts (default: 4) + retry_delay (float): Delay between retries in seconds (default: 1.0) Returns: bool: True if successful, False otherwise """ - try: - # Auto-detect type if not specified - if value_type is None: - if isinstance(value, bool): - value_type = 'bool' - elif isinstance(value, int): - value_type = 'int' - elif isinstance(value, float): - value_type = 'float' - else: - value_type = 'str' - - # Convert value to string for storage - if value_type == 'bool': - str_value = '1' if value else '0' + + # Auto-detect type if not specified + if value_type is None: + if isinstance(value, bool): + value_type = 'bool' + elif isinstance(value, int): + value_type = 'int' + elif isinstance(value, float): + value_type = 'float' else: - str_value = str(value) - - # Use INSERT OR REPLACE to update existing or create new - cursor.execute(""" - INSERT OR REPLACE INTO config_table (key, value, type) - VALUES (?, ?, ?) - """, (key, str_value, value_type)) - - conn.commit() - print(f"✓ Config updated: {key} = {value} ({value_type})") - return True - - except Exception as e: - print(f"✗ Error updating config {key}: {e}") - return False + value_type = 'str' + + # Convert value to string for storage + if value_type == 'bool': + str_value = '1' if value else '0' + else: + str_value = str(value) + + # Retry logic + for attempt in range(max_retries): + try: + # Use WAL mode and immediate transaction for better concurrency + conn.execute("PRAGMA journal_mode=WAL") + conn.execute("PRAGMA busy_timeout=30000") # Wait up to 30 seconds for lock + + # Begin immediate transaction to acquire lock quickly + conn.execute("BEGIN IMMEDIATE") + + # Use INSERT OR REPLACE to update existing or create new + cursor.execute(""" + INSERT OR REPLACE INTO config_table (key, value, type) + VALUES (?, ?, ?) + """, (key, str_value, value_type)) + + conn.commit() + print(f"✓ Config updated: {key} = {value} ({value_type}) [attempt {attempt + 1}]") + return True + + except sqlite3.OperationalError as e: + if "database is locked" in str(e).lower() or "locked" in str(e).lower(): + print(f"⚠ Database locked on attempt {attempt + 1}/{max_retries} for key '{key}': {e}") + + # Rollback any pending transaction + try: + conn.rollback() + except: + pass + + # If not the last attempt, wait and retry + if attempt < max_retries - 1: + print(f"⏳ Retrying in {retry_delay} seconds...") + time.sleep(retry_delay) + # Increase delay slightly for each retry (exponential backoff) + retry_delay *= 1.2 + else: + print(f"✗ Failed to update config {key} after {max_retries} attempts: database remains locked") + return False + else: + # Non-lock related operational error + print(f"✗ Operational error updating config {key}: {e}") + try: + conn.rollback() + except: + pass + return False + + except Exception as e: + # Other unexpected errors + print(f"✗ Unexpected error updating config {key} on attempt {attempt + 1}: {e}") + try: + conn.rollback() + except: + pass + + # For unexpected errors, don't retry unless it's clearly a temporary issue + if "busy" in str(e).lower() or "locked" in str(e).lower(): + if attempt < max_retries - 1: + print(f"⏳ Retrying in {retry_delay} seconds...") + time.sleep(retry_delay) + retry_delay *= 1.2 + continue + + return False + + # If we get here, all retries failed + print(f"✗ Failed to update config {key} after {max_retries} attempts") + return False #get config data from SQLite table def load_config_sqlite(): diff --git a/matrix/screenNetwork/network_status b/matrix/screenNetwork/network_status index a1b8096..f079f5d 100644 Binary files a/matrix/screenNetwork/network_status and b/matrix/screenNetwork/network_status differ diff --git a/matrix/screenNetwork/network_status.cc b/matrix/screenNetwork/network_status.cc index 5523956..b87ecda 100644 --- a/matrix/screenNetwork/network_status.cc +++ b/matrix/screenNetwork/network_status.cc @@ -187,9 +187,9 @@ void draw_connection_icon(Canvas* canvas, int x, int y, const std::string& statu } } } else if (status == "booting") { - icon_color = rgb_matrix::Color(255, 165, 0); // Orange - // Draw a simple but visible rotating indicator for booting - int frame = animation_frame % 4; // Faster rotation (4 states instead of 12) + icon_color = rgb_matrix::Color(255, 255, 0); // Changed from orange to yellow + // Draw a simple but visible rotating indicator for booting - EVEN FASTER rotation + int frame = (animation_frame / 2) % 2; // Much faster rotation - update every 2 frames // Draw a rotating cross/plus pattern int center_x = x + 10; @@ -204,15 +204,14 @@ void draw_connection_icon(Canvas* canvas, int x, int y, const std::string& statu } } - // Draw rotating lines based on frame - if (frame == 0 || frame == 2) { + // Draw rotating lines based on frame - faster animation + if (frame == 0) { // Vertical line for (int i = -6; i <= 6; i++) { canvas->SetPixel(center_x, center_y + i, icon_color.r, icon_color.g, icon_color.b); canvas->SetPixel(center_x + 1, center_y + i, icon_color.r, icon_color.g, icon_color.b); } - } - if (frame == 1 || frame == 3) { + } else { // frame == 1 // Horizontal line for (int i = -6; i <= 6; i++) { canvas->SetPixel(center_x + i, center_y, icon_color.r, icon_color.g, icon_color.b); @@ -297,8 +296,10 @@ void draw_4g_screen(Canvas* canvas, const std::string& network_status, int signa } } - // Draw signal bars anyway - draw_signal_bars(canvas, 55, 30, signal_quality); + // Only draw signal bars if connected + if (network_status == "connected") { + draw_signal_bars(canvas, 55, 30, signal_quality); + } std::cout << "Using fallback display (no fonts)" << std::endl; return; @@ -314,8 +315,7 @@ void draw_4g_screen(Canvas* canvas, const std::string& network_status, int signa int status_y = 25; rgb_matrix::DrawText(canvas, value_font, 2, status_y, white, &bg_color, "Status:", 0); - // Draw connection icon - draw_connection_icon(canvas, 40, status_y - 15, network_status); + // Don't draw connection icon - removed for cleaner text-only display // Draw status text rgb_matrix::Color status_color; @@ -327,7 +327,7 @@ void draw_4g_screen(Canvas* canvas, const std::string& network_status, int signa } else if (network_status == "connecting") { status_color = rgb_matrix::Color(255, 255, 0); // Yellow } else if (network_status == "booting") { - status_color = rgb_matrix::Color(255, 165, 0); // Orange + status_color = rgb_matrix::Color(255, 255, 0); // Changed from orange to yellow status_text = "BOOTING"; } else if (network_status == "disconnected" || network_status == "error") { status_color = rgb_matrix::Color(255, 0, 0); // Red @@ -336,10 +336,10 @@ void draw_4g_screen(Canvas* canvas, const std::string& network_status, int signa status_text = "UNKNOWN"; } - rgb_matrix::DrawText(canvas, value_font, 65, status_y, status_color, &bg_color, status_text.c_str(), 0); + rgb_matrix::DrawText(canvas, value_font, 45, status_y, status_color, &bg_color, status_text.c_str(), 0); - // Only show signal section if NOT in booting mode - if (network_status != "booting") { + // Only show signal section if connected (NOT for booting, disconnected, or error) + if (network_status == "connected") { // Draw signal strength section int signal_y = 45; rgb_matrix::DrawText(canvas, value_font, 2, signal_y, white, &bg_color, "Signal:", 0); @@ -383,16 +383,30 @@ void draw_4g_screen(Canvas* canvas, const std::string& network_status, int signa rgb_matrix::DrawText(canvas, small_font, 70, signal_y - 3, rgb_matrix::Color(128, 128, 128), &bg_color, "NO DATA", 0); } } else { - // Clear the signal area completely when booting + // Clear the signal area completely for all non-connected states for (int clear_x = 0; clear_x < 128; clear_x++) { for (int clear_y = 35; clear_y < 55; clear_y++) { canvas->SetPixel(clear_x, clear_y, 0, 0, 0); } } - // Show booting message instead of signal info - int boot_msg_y = 45; - rgb_matrix::DrawText(canvas, value_font, 15, boot_msg_y, rgb_matrix::Color(255, 165, 0), &bg_color, "Initializing modem...", 0); + // Show appropriate message based on status + int msg_y = 45; + int msg_y_second_line = 55; + + if (network_status == "booting") { + rgb_matrix::DrawText(canvas, value_font, 2, msg_y, rgb_matrix::Color(255, 255, 0), &bg_color, "Initializing modem...", 0); + } else if (network_status == "disconnected") { + rgb_matrix::DrawText(canvas, value_font, 20, msg_y, rgb_matrix::Color(255, 0, 0), &bg_color, "Connection lost", 0); + rgb_matrix::DrawText(canvas, value_font, 20, msg_y_second_line, rgb_matrix::Color(255, 0, 0), &bg_color, " try reconnect", 0); + + } else if (network_status == "error") { + rgb_matrix::DrawText(canvas, value_font, 25, msg_y, rgb_matrix::Color(255, 0, 0), &bg_color, "Modem error", 0); + rgb_matrix::DrawText(canvas, value_font, 20, msg_y_second_line, rgb_matrix::Color(255, 0, 0), &bg_color, " try reconnect", 0); + + } else if (network_status == "connecting") { + rgb_matrix::DrawText(canvas, value_font, 20, msg_y, rgb_matrix::Color(255, 255, 0), &bg_color, "Connecting...", 0); + } } // Draw timestamp