503 lines
19 KiB
C++
503 lines
19 KiB
C++
/*
|
|
_ _ _____ _______ _____ ____ _ __
|
|
| \ | | ____|_ _\ \ / / _ \| _ \| |/ /
|
|
| \| | _| | | \ \ /\ / / | | | |_) | ' /
|
|
| |\ | |___ | | \ V V /| |_| | _ <| . \
|
|
|_| \_|_____| |_| \_/\_/ \___/|_| \_\_|\_\
|
|
|
|
|
|
Modern 4G Connection Status Display
|
|
Get 4G status and signal quality from SQLite database config_table
|
|
|
|
Pour compiler:
|
|
g++ -I/var/www/moduleair_pro_4g/matrix/include -L/var/www/moduleair_pro_4g/matrix/lib /var/www/moduleair_pro_4g/matrix/screenNetwork/network_status.cc -o /var/www/moduleair_pro_4g/matrix/screenNetwork/network_status -lrgbmatrix -lsqlite3
|
|
|
|
Pour lancer:
|
|
sudo /var/www/moduleair_pro_4g/matrix/screenNetwork/network_status
|
|
*/
|
|
|
|
#include "led-matrix.h"
|
|
#include "graphics.h"
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <signal.h>
|
|
#include <atomic>
|
|
#include <sqlite3.h>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <chrono>
|
|
#include <ctime>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <math.h>
|
|
|
|
using rgb_matrix::RGBMatrix;
|
|
using rgb_matrix::Canvas;
|
|
|
|
std::atomic<bool> running(true);
|
|
|
|
// Define color codes for console output
|
|
#define RESET "\033[0m"
|
|
#define RED "\033[31m"
|
|
#define GREEN "\033[32m"
|
|
#define YELLOW "\033[33m"
|
|
#define BLUE "\033[34m"
|
|
#define MAGENTA "\033[35m"
|
|
#define CYAN "\033[36m"
|
|
#define WHITE "\033[37m"
|
|
|
|
// Path to the SQLite database
|
|
const std::string DB_PATH = "/var/www/moduleair_pro_4g/sqlite/sensors.db";
|
|
|
|
// Animation counter for connection indicators
|
|
int animation_frame = 0;
|
|
|
|
void signal_handler(int signum) {
|
|
running = false;
|
|
}
|
|
|
|
void log(const std::string& type, const std::string& message) {
|
|
std::string color;
|
|
|
|
if (type == "BLUE") color = BLUE;
|
|
else if (type == "GREEN") color = GREEN;
|
|
else if (type == "YELLOW") color = YELLOW;
|
|
else if (type == "RED") color = RED;
|
|
else color = WHITE;
|
|
|
|
std::cout << color << message << RESET << std::endl;
|
|
}
|
|
|
|
// Function to get 4G status from database
|
|
bool get_4g_status(sqlite3* db, std::string& network_status, int& signal_quality) {
|
|
sqlite3_stmt* stmt;
|
|
bool status_found = false;
|
|
bool signal_found = false;
|
|
|
|
log("BLUE", "Querying 4G status from database...");
|
|
|
|
// Get network status
|
|
const char* status_query = "SELECT value FROM config_table WHERE key='SARA_network_status'";
|
|
if (sqlite3_prepare_v2(db, status_query, -1, &stmt, NULL) == SQLITE_OK) {
|
|
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
network_status = (char*)sqlite3_column_text(stmt, 0);
|
|
status_found = true;
|
|
std::cout << " Network Status: " << network_status << std::endl;
|
|
} else {
|
|
std::cout << " No network status found in config_table" << std::endl;
|
|
network_status = "unknown";
|
|
}
|
|
sqlite3_finalize(stmt);
|
|
}
|
|
|
|
// Get signal quality
|
|
const char* signal_query = "SELECT value FROM config_table WHERE key='SARA_signal_quality'";
|
|
if (sqlite3_prepare_v2(db, signal_query, -1, &stmt, NULL) == SQLITE_OK) {
|
|
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
signal_quality = sqlite3_column_int(stmt, 0);
|
|
signal_found = true;
|
|
std::cout << " Signal Quality: " << signal_quality << " dBm" << std::endl;
|
|
} else {
|
|
std::cout << " No signal quality found in config_table" << std::endl;
|
|
signal_quality = -999;
|
|
}
|
|
sqlite3_finalize(stmt);
|
|
}
|
|
|
|
return status_found || signal_found;
|
|
}
|
|
|
|
// Function to draw signal strength bars
|
|
void draw_signal_bars(Canvas* canvas, int x, int y, int signal_rssi) {
|
|
rgb_matrix::Color bar_color;
|
|
rgb_matrix::Color bg_color(0, 0, 0);
|
|
|
|
// Convert RSSI to signal strength (0-5 bars)
|
|
// RSSI scale: 0-31 (where 31 is strongest)
|
|
int bars = 0;
|
|
if (signal_rssi >= 20) bars = 5; // Excellent (20-31)
|
|
else if (signal_rssi >= 15) bars = 4; // Good (15-19)
|
|
else if (signal_rssi >= 10) bars = 3; // Fair (10-14)
|
|
else if (signal_rssi >= 5) bars = 2; // Poor (5-9)
|
|
else if (signal_rssi >= 1) bars = 1; // Very Poor (1-4)
|
|
else bars = 0; // No signal (0)
|
|
|
|
// Choose color based on signal strength
|
|
if (bars >= 4) bar_color = rgb_matrix::Color(0, 255, 0); // Green - Excellent/Good
|
|
else if (bars >= 2) bar_color = rgb_matrix::Color(255, 255, 0); // Yellow - Fair/Poor
|
|
else if (bars >= 1) bar_color = rgb_matrix::Color(255, 165, 0); // Orange - Very Poor
|
|
else bar_color = rgb_matrix::Color(255, 0, 0); // Red - No signal
|
|
|
|
// Draw 5 signal bars of increasing height
|
|
for (int i = 0; i < 5; i++) {
|
|
int bar_height = (i + 1) * 2; // Heights: 2, 4, 6, 8, 10
|
|
int bar_x = x + i * 3;
|
|
|
|
// Clear the bar area first
|
|
for (int bx = 0; bx < 2; bx++) {
|
|
for (int by = 0; by < 10; by++) {
|
|
canvas->SetPixel(bar_x + bx, y - by, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
// Draw the bar if it should be active
|
|
if (i < bars) {
|
|
for (int bx = 0; bx < 2; bx++) {
|
|
for (int by = 0; by < bar_height; by++) {
|
|
canvas->SetPixel(bar_x + bx, y - by, bar_color.r, bar_color.g, bar_color.b);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Function to draw connection status icon
|
|
void draw_connection_icon(Canvas* canvas, int x, int y, const std::string& status) {
|
|
rgb_matrix::Color icon_color;
|
|
|
|
// Clear the icon area (20x20 pixels)
|
|
for (int ix = 0; ix < 20; ix++) {
|
|
for (int iy = 0; iy < 20; iy++) {
|
|
canvas->SetPixel(x + ix, y + iy, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
if (status == "connected") {
|
|
icon_color = rgb_matrix::Color(0, 255, 0); // Green
|
|
// Draw a checkmark
|
|
// Simplified checkmark pattern
|
|
for (int i = 0; i < 6; i++) {
|
|
canvas->SetPixel(x + 5 + i, y + 10 + i/2, icon_color.r, icon_color.g, icon_color.b);
|
|
canvas->SetPixel(x + 11 + i, y + 13 - i, icon_color.r, icon_color.g, icon_color.b);
|
|
}
|
|
} else if (status == "connecting") {
|
|
// Animated connecting indicator
|
|
icon_color = rgb_matrix::Color(255, 255, 0); // Yellow
|
|
int frame = animation_frame % 8;
|
|
|
|
// Draw rotating dots
|
|
for (int i = 0; i < 8; i++) {
|
|
if (i <= frame) {
|
|
int dot_x = x + 10 + (int)(8 * cos(i * M_PI / 4));
|
|
int dot_y = y + 10 + (int)(8 * sin(i * M_PI / 4));
|
|
canvas->SetPixel(dot_x, dot_y, icon_color.r, icon_color.g, icon_color.b);
|
|
canvas->SetPixel(dot_x+1, dot_y, icon_color.r, icon_color.g, icon_color.b);
|
|
}
|
|
}
|
|
} else if (status == "booting") {
|
|
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;
|
|
int center_y = y + 10;
|
|
|
|
// Clear area first
|
|
for (int cx = -8; cx <= 8; cx++) {
|
|
for (int cy = -8; cy <= 8; cy++) {
|
|
if (center_x + cx >= 0 && center_y + cy >= 0) {
|
|
canvas->SetPixel(center_x + cx, center_y + cy, 0, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
} 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);
|
|
canvas->SetPixel(center_x + i, center_y + 1, icon_color.r, icon_color.g, icon_color.b);
|
|
}
|
|
}
|
|
} else if (status == "disconnected" || status == "error") {
|
|
icon_color = rgb_matrix::Color(255, 0, 0); // Red
|
|
// Draw an X
|
|
for (int i = 0; i < 12; i++) {
|
|
canvas->SetPixel(x + 4 + i, y + 4 + i, icon_color.r, icon_color.g, icon_color.b);
|
|
canvas->SetPixel(x + 4 + i, y + 16 - i, icon_color.r, icon_color.g, icon_color.b);
|
|
}
|
|
} else {
|
|
icon_color = rgb_matrix::Color(128, 128, 128); // Gray for unknown
|
|
// Draw a question mark
|
|
// Simplified question mark
|
|
for (int i = 0; i < 8; i++) {
|
|
canvas->SetPixel(x + 6 + i, y + 4, icon_color.r, icon_color.g, icon_color.b);
|
|
canvas->SetPixel(x + 14, y + 5 + i/2, icon_color.r, icon_color.g, icon_color.b);
|
|
}
|
|
canvas->SetPixel(x + 10, y + 15, icon_color.r, icon_color.g, icon_color.b);
|
|
canvas->SetPixel(x + 11, y + 15, icon_color.r, icon_color.g, icon_color.b);
|
|
}
|
|
}
|
|
|
|
// Function to draw the entire 4G status screen
|
|
void draw_4g_screen(Canvas* canvas, const std::string& network_status, int signal_quality) {
|
|
rgb_matrix::Color white(255, 255, 255);
|
|
rgb_matrix::Color cyan(0, 255, 255);
|
|
rgb_matrix::Color bg_color(0, 0, 0);
|
|
|
|
rgb_matrix::Font title_font, value_font, small_font;
|
|
|
|
// Load fonts - try multiple font paths
|
|
bool fonts_loaded = false;
|
|
|
|
// Try the fonts from your working sensor display
|
|
if (title_font.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/6x9.bdf") &&
|
|
value_font.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/6x9.bdf") &&
|
|
small_font.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/6x9.bdf")) {
|
|
fonts_loaded = true;
|
|
std::cout << "Loaded fonts successfully (6x9)" << std::endl;
|
|
}
|
|
// Fallback: try alternative font names
|
|
else if (title_font.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/9x18B.bdf") &&
|
|
value_font.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/6x9.bdf") &&
|
|
small_font.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/4x6.bdf")) {
|
|
fonts_loaded = true;
|
|
std::cout << "Loaded fonts successfully (mixed)" << std::endl;
|
|
}
|
|
|
|
if (!fonts_loaded) {
|
|
std::cerr << "Error loading fonts! Check font paths:" << std::endl;
|
|
std::cerr << " Looking for: /var/www/moduleair_pro_4g/matrix/fonts/6x9.bdf" << std::endl;
|
|
std::cerr << " Looking for: /var/www/moduleair_pro_4g/matrix/fonts/9x18B.bdf" << std::endl;
|
|
std::cerr << " Looking for: /var/www/moduleair_pro_4g/matrix/fonts/4x6.bdf" << std::endl;
|
|
|
|
// Use a simple text-based display instead
|
|
rgb_matrix::Color white(255, 255, 255);
|
|
rgb_matrix::Color green(0, 255, 0);
|
|
rgb_matrix::Color red(255, 0, 0);
|
|
rgb_matrix::Color bg_color(0, 0, 0);
|
|
|
|
// Draw basic status without fonts
|
|
canvas->Clear();
|
|
|
|
// Draw simple status indicators using pixels
|
|
if (network_status == "connected") {
|
|
// Draw green rectangle
|
|
for (int x = 10; x < 30; x++) {
|
|
for (int y = 10; y < 20; y++) {
|
|
canvas->SetPixel(x, y, 0, 255, 0);
|
|
}
|
|
}
|
|
} else {
|
|
// Draw red rectangle
|
|
for (int x = 10; x < 30; x++) {
|
|
for (int y = 10; y < 20; y++) {
|
|
canvas->SetPixel(x, y, 255, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Clear screen
|
|
canvas->Clear();
|
|
|
|
// Draw title
|
|
rgb_matrix::DrawText(canvas, title_font, 2, title_font.baseline() + 2, cyan, &bg_color, "4G CONNECTION", 0);
|
|
|
|
// Draw connection status section
|
|
int status_y = 25;
|
|
rgb_matrix::DrawText(canvas, value_font, 2, status_y, white, &bg_color, "Status:", 0);
|
|
|
|
// Don't draw connection icon - removed for cleaner text-only display
|
|
|
|
// Draw status text
|
|
rgb_matrix::Color status_color;
|
|
std::string status_text = network_status;
|
|
std::transform(status_text.begin(), status_text.end(), status_text.begin(), ::toupper);
|
|
|
|
if (network_status == "connected") {
|
|
status_color = rgb_matrix::Color(0, 255, 0); // Green
|
|
} 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, 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
|
|
} else {
|
|
status_color = rgb_matrix::Color(128, 128, 128); // Gray
|
|
status_text = "UNKNOWN";
|
|
}
|
|
|
|
rgb_matrix::DrawText(canvas, value_font, 45, status_y, status_color, &bg_color, status_text.c_str(), 0);
|
|
|
|
// 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);
|
|
|
|
// Draw signal bars
|
|
draw_signal_bars(canvas, 45, signal_y - 2, signal_quality);
|
|
|
|
// Draw signal value
|
|
if (signal_quality != -999) {
|
|
std::stringstream ss;
|
|
ss << "RSSI: " << signal_quality;
|
|
rgb_matrix::DrawText(canvas, small_font, 70, signal_y - 3, white, &bg_color, ss.str().c_str(), 0);
|
|
|
|
// Draw signal quality text based on RSSI
|
|
std::string quality_text;
|
|
rgb_matrix::Color quality_color;
|
|
|
|
if (signal_quality >= 20) {
|
|
quality_text = "EXCELLENT";
|
|
quality_color = rgb_matrix::Color(0, 255, 0);
|
|
} else if (signal_quality >= 15) {
|
|
quality_text = "GOOD";
|
|
quality_color = rgb_matrix::Color(0, 255, 0);
|
|
} else if (signal_quality >= 10) {
|
|
quality_text = "FAIR";
|
|
quality_color = rgb_matrix::Color(255, 255, 0);
|
|
} else if (signal_quality >= 5) {
|
|
quality_text = "POOR";
|
|
quality_color = rgb_matrix::Color(255, 165, 0);
|
|
} else if (signal_quality >= 1) {
|
|
quality_text = "VERY POOR";
|
|
quality_color = rgb_matrix::Color(255, 0, 0);
|
|
} else {
|
|
quality_text = "NO SIGNAL";
|
|
quality_color = rgb_matrix::Color(255, 0, 0);
|
|
}
|
|
|
|
// Position quality text higher to avoid clipping
|
|
rgb_matrix::DrawText(canvas, small_font, 70, signal_y + 5, quality_color, &bg_color, quality_text.c_str(), 0);
|
|
} else {
|
|
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 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 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
|
|
auto now = std::chrono::system_clock::now();
|
|
auto time_t = std::chrono::system_clock::to_time_t(now);
|
|
auto tm = *std::localtime(&time_t);
|
|
|
|
std::stringstream time_ss;
|
|
time_ss << std::put_time(&tm, "%H:%M:%S");
|
|
|
|
rgb_matrix::DrawText(canvas, small_font, 2, 60, rgb_matrix::Color(100, 100, 100), &bg_color,
|
|
("Updated: " + time_ss.str()).c_str(), 0);
|
|
|
|
animation_frame++;
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
log("BLUE", "4G Status Display started");
|
|
|
|
// Handle signals for graceful exit
|
|
signal(SIGINT, signal_handler);
|
|
signal(SIGTERM, signal_handler);
|
|
|
|
// Initialize database connection
|
|
sqlite3* db;
|
|
std::cout << "Opening SQLite database at " << DB_PATH << std::endl;
|
|
int rc = sqlite3_open(DB_PATH.c_str(), &db);
|
|
if (rc) {
|
|
std::cerr << "Error opening SQLite database: " << sqlite3_errmsg(db) << std::endl;
|
|
sqlite3_close(db);
|
|
return 1;
|
|
}
|
|
|
|
// Initialize LED matrix
|
|
log("BLUE", "Initializing LED matrix...");
|
|
RGBMatrix::Options defaults;
|
|
defaults.hardware_mapping = "moduleair_pinout";
|
|
defaults.rows = 64;
|
|
defaults.cols = 128;
|
|
defaults.chain_length = 1;
|
|
defaults.parallel = 1;
|
|
defaults.row_address_type = 3;
|
|
defaults.show_refresh_rate = false;
|
|
defaults.brightness = 100;
|
|
defaults.pwm_bits = 1;
|
|
defaults.panel_type = "FM6126A";
|
|
defaults.disable_hardware_pulsing = false;
|
|
|
|
rgb_matrix::RuntimeOptions runtime_opt;
|
|
runtime_opt.gpio_slowdown = 4;
|
|
runtime_opt.daemon = 0;
|
|
runtime_opt.drop_privileges = 0;
|
|
|
|
Canvas *canvas = RGBMatrix::CreateFromOptions(defaults, runtime_opt);
|
|
if (canvas == NULL) {
|
|
std::cerr << "Error creating LED matrix canvas" << std::endl;
|
|
sqlite3_close(db);
|
|
return 1;
|
|
}
|
|
log("GREEN", "LED matrix initialized successfully");
|
|
|
|
// Main loop
|
|
log("BLUE", "Starting 4G status display loop");
|
|
while (running) {
|
|
std::string network_status;
|
|
int signal_quality;
|
|
|
|
// Get 4G status from database
|
|
bool data_success = get_4g_status(db, network_status, signal_quality);
|
|
|
|
if (!data_success) {
|
|
std::cerr << "Error retrieving 4G data from database" << std::endl;
|
|
network_status = "error";
|
|
signal_quality = -999;
|
|
}
|
|
|
|
// Draw the screen
|
|
draw_4g_screen(canvas, network_status, signal_quality);
|
|
|
|
// Sleep before next update
|
|
std::cout << "Update complete, sleeping for 5 seconds..." << std::endl;
|
|
for (int i = 0; i < 5 && running; i++) {
|
|
sleep(1); // Sleep in 1-second increments for responsive exit
|
|
}
|
|
}
|
|
|
|
// Clean up
|
|
std::cout << "Program terminating, cleaning up..." << std::endl;
|
|
canvas->Clear();
|
|
delete canvas;
|
|
sqlite3_close(db);
|
|
std::cout << "4G Status Display terminated" << std::endl;
|
|
return 0;
|
|
} |