update
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,4 +10,5 @@ matrix/input_MHZ16.txt
|
|||||||
wifi_list.csv
|
wifi_list.csv
|
||||||
*.lock
|
*.lock
|
||||||
sqlite/*.db
|
sqlite/*.db
|
||||||
|
sqlite/*.db*
|
||||||
tests/
|
tests/
|
||||||
@@ -10,7 +10,7 @@ OUTPUT_FILE="/var/www/moduleair_pro_4g/wifi_list.csv"
|
|||||||
echo "-------------------"
|
echo "-------------------"
|
||||||
echo "-------------------"
|
echo "-------------------"
|
||||||
|
|
||||||
echo "NebuleAir pro started at $(date)"
|
echo "ModuleAir pro started at $(date)"
|
||||||
|
|
||||||
|
|
||||||
echo "getting RPI serial number"
|
echo "getting RPI serial number"
|
||||||
@@ -52,7 +52,7 @@ else
|
|||||||
echo "Connection: $CONN_SSID"
|
echo "Connection: $CONN_SSID"
|
||||||
|
|
||||||
# Update SQLite to reflect hotspot mode
|
# 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
|
# Lancer le tunnel SSH
|
||||||
#echo "Démarrage du tunnel SSH sur le port $SSH_TUNNEL_PORT..."
|
#echo "Démarrage du tunnel SSH sur le port $SSH_TUNNEL_PORT..."
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
@reboot /var/www/moduleair_pro_4g/boot_hotspot.sh >> /var/www/moduleair_pro_4g/logs/app.log 2>&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/reboot/start.py >> /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
|
|
||||||
|
|
||||||
0 0 * * * > /var/www/moduleair_pro_4g/logs/master.log
|
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 {} \;
|
||||||
|
|
||||||
|
|||||||
@@ -122,50 +122,108 @@ uSpot_profile_id = 1
|
|||||||
conn = sqlite3.connect("/var/www/moduleair_pro_4g/sqlite/sensors.db")
|
conn = sqlite3.connect("/var/www/moduleair_pro_4g/sqlite/sensors.db")
|
||||||
cursor = conn.cursor()
|
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:
|
Args:
|
||||||
key (str): Configuration key
|
key (str): Configuration key
|
||||||
value: Configuration value (any type)
|
value: Configuration value (any type)
|
||||||
value_type (str, optional): Force specific type ('str', 'int', 'float', 'bool')
|
value_type (str, optional): Force specific type ('str', 'int', 'float', 'bool')
|
||||||
If None, auto-detect from value type
|
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:
|
Returns:
|
||||||
bool: True if successful, False otherwise
|
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
|
# Auto-detect type if not specified
|
||||||
if value_type == 'bool':
|
if value_type is None:
|
||||||
str_value = '1' if value else '0'
|
if isinstance(value, bool):
|
||||||
|
value_type = 'bool'
|
||||||
|
elif isinstance(value, int):
|
||||||
|
value_type = 'int'
|
||||||
|
elif isinstance(value, float):
|
||||||
|
value_type = 'float'
|
||||||
else:
|
else:
|
||||||
str_value = str(value)
|
value_type = 'str'
|
||||||
|
|
||||||
# Use INSERT OR REPLACE to update existing or create new
|
# Convert value to string for storage
|
||||||
cursor.execute("""
|
if value_type == 'bool':
|
||||||
INSERT OR REPLACE INTO config_table (key, value, type)
|
str_value = '1' if value else '0'
|
||||||
VALUES (?, ?, ?)
|
else:
|
||||||
""", (key, str_value, value_type))
|
str_value = str(value)
|
||||||
|
|
||||||
conn.commit()
|
# Retry logic
|
||||||
print(f"✓ Config updated: {key} = {value} ({value_type})")
|
for attempt in range(max_retries):
|
||||||
return True
|
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
|
||||||
|
|
||||||
except Exception as e:
|
# Begin immediate transaction to acquire lock quickly
|
||||||
print(f"✗ Error updating config {key}: {e}")
|
conn.execute("BEGIN IMMEDIATE")
|
||||||
return False
|
|
||||||
|
# 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
|
#get config data from SQLite table
|
||||||
def load_config_sqlite():
|
def load_config_sqlite():
|
||||||
|
|||||||
Binary file not shown.
@@ -187,9 +187,9 @@ void draw_connection_icon(Canvas* canvas, int x, int y, const std::string& statu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (status == "booting") {
|
} else if (status == "booting") {
|
||||||
icon_color = rgb_matrix::Color(255, 165, 0); // Orange
|
icon_color = rgb_matrix::Color(255, 255, 0); // Changed from orange to yellow
|
||||||
// Draw a simple but visible rotating indicator for booting
|
// Draw a simple but visible rotating indicator for booting - EVEN FASTER rotation
|
||||||
int frame = animation_frame % 4; // Faster rotation (4 states instead of 12)
|
int frame = (animation_frame / 2) % 2; // Much faster rotation - update every 2 frames
|
||||||
|
|
||||||
// Draw a rotating cross/plus pattern
|
// Draw a rotating cross/plus pattern
|
||||||
int center_x = x + 10;
|
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
|
// Draw rotating lines based on frame - faster animation
|
||||||
if (frame == 0 || frame == 2) {
|
if (frame == 0) {
|
||||||
// Vertical line
|
// Vertical line
|
||||||
for (int i = -6; i <= 6; i++) {
|
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, 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);
|
canvas->SetPixel(center_x + 1, center_y + i, icon_color.r, icon_color.g, icon_color.b);
|
||||||
}
|
}
|
||||||
}
|
} else { // frame == 1
|
||||||
if (frame == 1 || frame == 3) {
|
|
||||||
// Horizontal line
|
// Horizontal line
|
||||||
for (int i = -6; i <= 6; i++) {
|
for (int i = -6; i <= 6; i++) {
|
||||||
canvas->SetPixel(center_x + i, center_y, icon_color.r, icon_color.g, icon_color.b);
|
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
|
// Only draw signal bars if connected
|
||||||
draw_signal_bars(canvas, 55, 30, signal_quality);
|
if (network_status == "connected") {
|
||||||
|
draw_signal_bars(canvas, 55, 30, signal_quality);
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "Using fallback display (no fonts)" << std::endl;
|
std::cout << "Using fallback display (no fonts)" << std::endl;
|
||||||
return;
|
return;
|
||||||
@@ -314,8 +315,7 @@ void draw_4g_screen(Canvas* canvas, const std::string& network_status, int signa
|
|||||||
int status_y = 25;
|
int status_y = 25;
|
||||||
rgb_matrix::DrawText(canvas, value_font, 2, status_y, white, &bg_color, "Status:", 0);
|
rgb_matrix::DrawText(canvas, value_font, 2, status_y, white, &bg_color, "Status:", 0);
|
||||||
|
|
||||||
// Draw connection icon
|
// Don't draw connection icon - removed for cleaner text-only display
|
||||||
draw_connection_icon(canvas, 40, status_y - 15, network_status);
|
|
||||||
|
|
||||||
// Draw status text
|
// Draw status text
|
||||||
rgb_matrix::Color status_color;
|
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") {
|
} else if (network_status == "connecting") {
|
||||||
status_color = rgb_matrix::Color(255, 255, 0); // Yellow
|
status_color = rgb_matrix::Color(255, 255, 0); // Yellow
|
||||||
} else if (network_status == "booting") {
|
} 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";
|
status_text = "BOOTING";
|
||||||
} else if (network_status == "disconnected" || network_status == "error") {
|
} else if (network_status == "disconnected" || network_status == "error") {
|
||||||
status_color = rgb_matrix::Color(255, 0, 0); // Red
|
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";
|
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
|
// Only show signal section if connected (NOT for booting, disconnected, or error)
|
||||||
if (network_status != "booting") {
|
if (network_status == "connected") {
|
||||||
// Draw signal strength section
|
// Draw signal strength section
|
||||||
int signal_y = 45;
|
int signal_y = 45;
|
||||||
rgb_matrix::DrawText(canvas, value_font, 2, signal_y, white, &bg_color, "Signal:", 0);
|
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);
|
rgb_matrix::DrawText(canvas, small_font, 70, signal_y - 3, rgb_matrix::Color(128, 128, 128), &bg_color, "NO DATA", 0);
|
||||||
}
|
}
|
||||||
} else {
|
} 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_x = 0; clear_x < 128; clear_x++) {
|
||||||
for (int clear_y = 35; clear_y < 55; clear_y++) {
|
for (int clear_y = 35; clear_y < 55; clear_y++) {
|
||||||
canvas->SetPixel(clear_x, clear_y, 0, 0, 0);
|
canvas->SetPixel(clear_x, clear_y, 0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show booting message instead of signal info
|
// Show appropriate message based on status
|
||||||
int boot_msg_y = 45;
|
int msg_y = 45;
|
||||||
rgb_matrix::DrawText(canvas, value_font, 15, boot_msg_y, rgb_matrix::Color(255, 165, 0), &bg_color, "Initializing modem...", 0);
|
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
|
// Draw timestamp
|
||||||
|
|||||||
Reference in New Issue
Block a user