/* ____ ___ ____ _ _ _____ _______ _____ ____ _ __ / ___/ _ \___ \ | \ | | ____|_ _\ \ / / _ \| _ \| |/ / | | | | | |__) || \| | _| | | \ \ /\ / / | | | |_) | ' / | |__| |_| / __/ | |\ | |___ | | \ V V /| |_| | _ <| . \ \____\___/_____||_| \_|_____| |_| \_/\_/ \___/|_| \_\_|\_\ Combined CO2 + PM2.5 + PM10 + Network Status Display Shows 3 key measurements plus network connectivity status 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/screenSensors/displayCO2_PM_Network.cc -o /var/www/moduleair_pro_4g/matrix/screenSensors/displayCO2_PM_Network -lrgbmatrix -lsqlite3 Pour lancer: sudo /var/www/moduleair_pro_4g/matrix/screenSensors/displayCO2_PM_Network */ #include "led-matrix.h" #include "graphics.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using rgb_matrix::RGBMatrix; using rgb_matrix::Canvas; std::atomic running(true); // Define color codes #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"; 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 retrieve latest sensor data bool get_sensor_data(sqlite3* db, float& pm25, float& pm10, float& co2) { sqlite3_stmt* stmt; bool pm_success = false; bool co2_success = false; log("BLUE", "Querying sensor data from database..."); // Get PM2.5 and PM10 data from NPM table const char* pm_query = "SELECT PM25, PM10 FROM data_NPM ORDER BY rowid DESC LIMIT 1"; if (sqlite3_prepare_v2(db, pm_query, -1, &stmt, NULL) == SQLITE_OK) { if (sqlite3_step(stmt) == SQLITE_ROW) { pm25 = sqlite3_column_double(stmt, 0); pm10 = sqlite3_column_double(stmt, 1); pm_success = true; std::cout << " Retrieved PM values - PM2.5: " << pm25 << ", PM10: " << pm10 << std::endl; } sqlite3_finalize(stmt); } // Get CO2 data const char* co2_query = "SELECT CO2 FROM data_CO2 ORDER BY rowid DESC LIMIT 1"; if (sqlite3_prepare_v2(db, co2_query, -1, &stmt, NULL) == SQLITE_OK) { if (sqlite3_step(stmt) == SQLITE_ROW) { co2 = sqlite3_column_double(stmt, 0); co2_success = true; std::cout << " Retrieved CO2 value: " << co2 << " ppm" << std::endl; } sqlite3_finalize(stmt); } return pm_success && co2_success; } // Function to get network status bool get_network_status(sqlite3* db, std::string& network_status, std::string& wifi_status) { sqlite3_stmt* stmt; bool status_found = false; // Get 4G network status const char* network_query = "SELECT value FROM config_table WHERE key='SARA_network_status'"; if (sqlite3_prepare_v2(db, network_query, -1, &stmt, NULL) == SQLITE_OK) { if (sqlite3_step(stmt) == SQLITE_ROW) { network_status = (char*)sqlite3_column_text(stmt, 0); status_found = true; } else { network_status = "unknown"; } sqlite3_finalize(stmt); } // Get WiFi status const char* wifi_query = "SELECT value FROM config_table WHERE key='WIFI_status'"; if (sqlite3_prepare_v2(db, wifi_query, -1, &stmt, NULL) == SQLITE_OK) { if (sqlite3_step(stmt) == SQLITE_ROW) { wifi_status = (char*)sqlite3_column_text(stmt, 0); } else { wifi_status = "unknown"; } sqlite3_finalize(stmt); } return status_found; } // Function to get color based on air quality thresholds rgb_matrix::Color get_quality_color(float value, const std::string& type) { if (type == "pm25") { if (value < 10) return rgb_matrix::Color(0, 255, 0); // Green - Good else if (value < 20) return rgb_matrix::Color(255, 255, 0); // Yellow - Moderate else if (value < 50) return rgb_matrix::Color(255, 165, 0); // Orange - Degraded else return rgb_matrix::Color(255, 0, 0); // Red - Bad } else if (type == "pm10") { if (value < 15) return rgb_matrix::Color(0, 255, 0); // Green - Good else if (value < 30) return rgb_matrix::Color(255, 255, 0); // Yellow - Moderate else if (value < 75) return rgb_matrix::Color(255, 165, 0); // Orange - Degraded else return rgb_matrix::Color(255, 0, 0); // Red - Bad } else if (type == "co2") { if (value < 800) return rgb_matrix::Color(0, 255, 0); // Green - Good else if (value < 1500) return rgb_matrix::Color(255, 165, 0); // Orange - Moderate else return rgb_matrix::Color(255, 0, 0); // Red - Bad } return rgb_matrix::Color(255, 255, 255); // White default } // Function to get status text based on thresholds std::string get_status_text(float value, const std::string& type) { if (type == "pm25") { if (value < 10) return "BON"; else if (value < 20) return "MOYEN"; else if (value < 50) return "DEGRADE"; else return "MAUVAIS"; } else if (type == "pm10") { if (value < 15) return "BON"; else if (value < 30) return "MOYEN"; else if (value < 75) return "DEGRADE"; else return "MAUVAIS"; } else if (type == "co2") { if (value < 800) return "BON"; else if (value < 1500) return "AERER SVP"; else return "AERER VITE"; } return "UNKNOWN"; } // Function to draw network connection indicators void draw_network_indicators(Canvas* canvas, const std::string& network_status, const std::string& wifi_status) { rgb_matrix::Color green(0, 255, 0); rgb_matrix::Color red(255, 0, 0); rgb_matrix::Color orange(255, 165, 0); rgb_matrix::Color yellow(255, 255, 0); // Draw 4G indicator (small rectangle) rgb_matrix::Color net_color = (network_status == "connected") ? green : (network_status == "connecting" || network_status == "booting") ? yellow : red; for (int x = 100; x < 108; x++) { for (int y = 2; y < 8; y++) { canvas->SetPixel(x, y, net_color.r, net_color.g, net_color.b); } } // Draw WiFi indicator (small rectangle) rgb_matrix::Color wifi_color = (wifi_status == "connected") ? green : (wifi_status == "hotspot") ? orange : red; for (int x = 112; x < 120; x++) { for (int y = 2; y < 8; y++) { canvas->SetPixel(x, y, wifi_color.r, wifi_color.g, wifi_color.b); } } } // Main display function void draw_combined_screen(Canvas* canvas, float pm25, float pm10, float co2, const std::string& network_status, const std::string& wifi_status) { 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 if (!title_font.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/6x9.bdf") || !value_font.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/9x18B.bdf") || !small_font.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/6x9.bdf")) { std::cerr << "Error loading fonts!" << std::endl; return; } canvas->Clear(); // Draw title rgb_matrix::DrawText(canvas, title_font, 2, title_font.baseline() + 2, cyan, &bg_color, "CO2 + PM + NET", 0); // Draw network indicators in top right draw_network_indicators(canvas, network_status, wifi_status); // Format sensor values std::stringstream ss_pm25, ss_pm10, ss_co2; ss_pm25 << std::fixed << std::setprecision(1) << pm25; ss_pm10 << std::fixed << std::setprecision(1) << pm10; ss_co2 << std::fixed << std::setprecision(0) << co2; std::string str_pm25 = ss_pm25.str(); std::string str_pm10 = ss_pm10.str(); std::string str_co2 = ss_co2.str(); // Get colors and status for each measurement rgb_matrix::Color pm25_color = get_quality_color(pm25, "pm25"); rgb_matrix::Color pm10_color = get_quality_color(pm10, "pm10"); rgb_matrix::Color co2_color = get_quality_color(co2, "co2"); std::string pm25_status = get_status_text(pm25, "pm25"); std::string pm10_status = get_status_text(pm10, "pm10"); std::string co2_status = get_status_text(co2, "co2"); // Layout: 3 measurements with proper spacing to avoid overlap int y_start = 16; int y_spacing = 16; // Increased spacing to prevent overlap // PM2.5 - using smaller positioning to fit everything rgb_matrix::DrawText(canvas, small_font, 2, y_start, cyan, &bg_color, "PM2.5:", 0); rgb_matrix::DrawText(canvas, small_font, 32, y_start, white, &bg_color, str_pm25.c_str(), 0); rgb_matrix::DrawText(canvas, small_font, 55, y_start, pm25_color, &bg_color, pm25_status.c_str(), 0); // PM10 y_start += y_spacing; rgb_matrix::DrawText(canvas, small_font, 2, y_start, cyan, &bg_color, "PM10:", 0); rgb_matrix::DrawText(canvas, small_font, 32, y_start, white, &bg_color, str_pm10.c_str(), 0); rgb_matrix::DrawText(canvas, small_font, 55, y_start, pm10_color, &bg_color, pm10_status.c_str(), 0); // CO2 y_start += y_spacing; rgb_matrix::DrawText(canvas, small_font, 2, y_start, cyan, &bg_color, "CO2:", 0); rgb_matrix::DrawText(canvas, small_font, 28, y_start, white, &bg_color, str_co2.c_str(), 0); rgb_matrix::DrawText(canvas, small_font, 55, y_start, co2_color, &bg_color, co2_status.c_str(), 0); // Draw network status text at bottom - simplified to fit y_start += y_spacing; std::string net_text = std::string("4G:") + (network_status == "connected" ? "OK" : "NO") + std::string(" WiFi:") + (wifi_status == "connected" ? "OK" : "NO"); rgb_matrix::DrawText(canvas, small_font, 2, y_start, rgb_matrix::Color(150, 150, 150), &bg_color, net_text.c_str(), 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"); rgb_matrix::DrawText(canvas, small_font, 100, 62, rgb_matrix::Color(100, 100, 100), &bg_color, time_ss.str().c_str(), 0); } int main(int argc, char *argv[]) { log("BLUE", "CO2 + PM + Network 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 display loop"); while (running) { float pm25 = 0, pm10 = 0, co2 = 0; std::string network_status, wifi_status; // Get sensor data bool sensor_success = get_sensor_data(db, pm25, pm10, co2); bool network_success = get_network_status(db, network_status, wifi_status); if (!sensor_success) { std::cerr << "Error retrieving sensor data from database" << std::endl; } if (!network_success) { std::cerr << "Error retrieving network data from database" << std::endl; network_status = "unknown"; wifi_status = "unknown"; } // Draw the combined screen draw_combined_screen(canvas, pm25, pm10, co2, network_status, wifi_status); // Sleep before next update std::cout << "Update complete, sleeping for 5 seconds..." << std::endl; for (int i = 0; i < 5 && running; i++) { sleep(1); } } // Clean up std::cout << "Program terminating, cleaning up..." << std::endl; canvas->Clear(); delete canvas; sqlite3_close(db); std::cout << "CO2 + PM + Network Display terminated" << std::endl; return 0; }