1. 项目概述:用树莓派打造一块实时航班信息看板
几年前,我在机场候机时,盯着那块巨大的、不断刷新的航班信息显示屏,脑子里冒出一个想法:这玩意儿本质上不就是一台联网的电脑在驱动一块大屏幕吗?我能不能用家里闲置的树莓派,给自己也做一个迷你版的实时航班信息看板,放在书房或者工作室里,既能当个有格调的装饰,又能随时瞥一眼关注的航班动态?这个想法就是“Pi based real-time arrivals and departures board”项目的起点。它本质上是一个运行在树莓派上的本地化Web应用,通过调用公开的航班数据API,将到港和离港信息以类似机场大屏的样式,清晰地展示在连接的显示器或电视上。
这个项目非常适合那些喜欢动手折腾、对物联网和Web开发感兴趣的开发者或爱好者。它不只是一个简单的数据展示,更是一个融合了硬件选型、后端数据抓取与处理、前端动态展示以及系统服务化部署的综合性实践。你将亲手搭建一个从数据源到最终视觉呈现的完整链路。对于初学者,这是一个绝佳的、目标明确的全栈入门项目;对于有经验的开发者,则可以在数据缓存、界面美化、多数据源聚合等方面进行深度优化。最终,你会得到一块7x24小时稳定运行、信息实时更新的个性化航班信息屏,无论是用于关注亲友航班、追踪特定航线,还是单纯享受创造的乐趣,都极具实用价值和成就感。
2. 核心方案设计与技术选型解析
2.1 为什么选择LAMP(Linux, Apache, MySQL, PHP)栈?
在确定技术栈时,我首先考虑的是树莓派这个特定硬件平台的特性和项目的核心需求。树莓派运行的是Linux系统,这为服务器环境提供了天然基础。项目需要实现一个能通过浏览器访问的实时信息展示页面,这意味着需要一个Web服务器、一个后端处理语言和一个数据存储(或缓存)方案。
我选择了经典的LAMP栈,即 Linux (Raspberry Pi OS) + Apache + MySQL + PHP。这个选择基于以下几点考量:
- 资源友好与成熟稳定:Apache和PHP在树莓派上的资源占用经过多年优化,非常轻量。相较于Node.js或Python的一些重型框架,LAMP栈在内存有限的树莓派上运行更稳定,长时间开机不易出现内存泄漏问题。
- 部署与维护简单:在Debian系的Raspberry Pi OS上,通过
apt包管理器安装和配置Apache、PHP、MySQL几乎是一行命令的事情,社区资料极其丰富,任何问题都能快速找到解决方案。 - 开发效率高:对于这个以数据展示为核心的项目,PHP直接嵌入HTML的模式非常适合快速开发前端界面。我们不需要复杂的前后端分离架构,一个PHP文件就能完成数据获取、处理和渲染的全部工作,简化了项目结构。
- 数据缓存需求:航班API通常有调用频率限制。我们需要将获取到的数据临时存储起来,避免频繁请求导致IP被封。MySQL作为一个关系型数据库,虽然有点“杀鸡用牛刀”,但用它来存储航班记录、缓存时间戳非常可靠,也便于后续进行历史查询或数据分析等扩展。
注意:有人可能会问为什么不用SQLite?对于这个项目,SQLite确实是一个更轻量的选择。我选择MySQL是考虑到未来可能的扩展性,比如同时监控多个机场、存储更长时间的历史数据等。MySQL在树莓派上的性能完全足够,且其服务化特性更符合生产部署的思维习惯。
2.2 数据源的选择与API对接策略
项目的“实时”二字,完全依赖于可靠的数据源。市面上有不少提供航班数据的API服务,有免费的也有付费的。对于个人项目,我们优先考虑免费或低成本的方案。
- ** AviationStack**:这是一个非常流行的免费航班数据API,免费套餐提供每月500次请求,支持实时航班、历史航班、机场状态等多种查询。它的文档清晰,返回的JSON数据结构规范,非常适合入门。
- ** OpenSky Network**:这是一个由社区驱动的开源项目,提供全球空中交通的实时数据。其API完全免费,但数据更偏向于航空器状态(位置、高度、速度),对于格式化的航班号、计划时间等信息支持较弱,需要更多后期处理。
- ** Airport-Data.com 或其他航空数据聚合网站**:有些网站提供非官方的RSS或简易接口,但稳定性和数据准确性无法保证,不推荐用于需要可靠运行的项目。
我最终选择了AviationStack作为主要数据源。对接策略的核心在于“缓存与轮询”。我们绝不能在前端每次刷新页面时都直接调用API。正确的做法是:
- 编写一个独立的PHP数据抓取脚本(例如
fetch_flights.php)。 - 该脚本通过CURL调用AviationStack API,获取指定机场(如
airport=JFK)的实时航班数据。 - 脚本将获取到的JSON数据解析后,存入MySQL数据库的
flights表中,并记录本次抓取的时间戳。 - 设置一个系统Cron任务(例如每5分钟执行一次
fetch_flights.php),实现定时更新。 - 前端展示页面则直接从本地MySQL数据库读取最新的航班数据。这样,前端访问速度极快,且完全不受外部API响应速度和调用限制的影响。
3. 系统环境搭建与核心配置
3.1 树莓派系统准备与基础服务安装
首先,确保你的树莓派已经安装了最新版的Raspberry Pi OS(以前叫Raspbian)。通过SSH连接或直接接上键盘鼠标显示器进行操作。
第一步是更新系统并安装LAMP套件:
sudo apt update sudo apt upgrade -y sudo apt install apache2 php mysql-server php-mysql -y安装完成后,可以进行快速验证:
- Apache:在电脑浏览器访问树莓派的IP地址(如
http://192.168.1.100),应该能看到Apache的默认欢迎页面。 - PHP:创建一个测试文件
/var/www/html/info.php,内容为<?php phpinfo(); ?>。保存后访问http://192.168.1.100/info.php,应显示详细的PHP配置信息。 - MySQL:运行
sudo mysql_secure_installation进行初始安全设置,包括设置root密码、移除匿名用户等。
3.2 数据库设计与初始化
我们的数据存储需求并不复杂。主要需要两张表:
flights:存储航班信息。api_cache_meta:存储API调用的元数据,用于控制更新频率。
登录MySQL并创建数据库和表:
sudo mysql -u root -pCREATE DATABASE flight_board; USE flight_board; CREATE TABLE flights ( id INT AUTO_INCREMENT PRIMARY KEY, flight_iata VARCHAR(10), -- 航班号,如 ‘CA981’ flight_icao VARCHAR(10), departure_airport VARCHAR(10), -- 出发机场IATA代码 arrival_airport VARCHAR(10), -- 到达机场IATA代码 scheduled_departure DATETIME, estimated_departure DATETIME, scheduled_arrival DATETIME, estimated_arrival DATETIME, status VARCHAR(50), -- 状态:scheduled, active, landed, cancelled airline_name VARCHAR(100), last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_airport_status (arrival_airport, status), -- 为查询优化建立索引 INDEX idx_departure_airport (departure_airport) ); CREATE TABLE api_cache_meta ( id INT PRIMARY KEY DEFAULT 1, last_fetch_time TIMESTAMP, -- 最后一次成功抓取的时间 fetch_interval_seconds INT DEFAULT 300 -- 抓取间隔,默认300秒(5分钟) ); INSERT INTO api_cache_meta (id, last_fetch_time) VALUES (1, NOW());flights表的设计涵盖了航班的核心信息字段。api_cache_meta表是一个巧妙的控制开关。我们的抓取脚本在执行前,会先检查这个表,判断距离上次抓取是否已经超过了设定的间隔(如300秒),只有超过了才会真正调用API,否则直接退出,避免无效请求。
3.3 Apache与PHP的针对性优化
为了让我们的信息看板运行得更顺畅,需要对默认配置做一些调整。
Apache配置优化 (/etc/apache2/apache2.conf或站点配置文件):
- 保持连接:适当调整
KeepAlive为On,并设置KeepAliveTimeout为一个较小的值(如5秒),这对于需要频繁加载的前端页面有益。 - 禁用不必要的模块:树莓派资源有限,可以禁用一些用不到的模块,如
sudo a2dismod status autoindex,然后sudo systemctl restart apache2。 - 目录索引:确保你的项目目录配置了
DirectoryIndex index.php index.html,这样访问目录时会自动打开index.php。
PHP配置优化 (/etc/php/版本号/apache2/php.ini):
- 调整内存和时间限制:
memory_limit = 128M,max_execution_time = 30。对于抓取脚本,执行时间可以设长一点(如60秒),可在脚本内用set_time_limit()单独设置。 - 调整时区:
date.timezone = Asia/Shanghai(根据你的实际时区设置)。这是关键!确保系统、PHP、MySQL三者的时区一致,否则航班时间的显示会混乱。 - 启用必要的扩展:确保
extension=mysqli或extension=pdo_mysql已启用,用于连接数据库。
4. 核心功能模块实现详解
4.1 数据抓取脚本(Cron任务核心)
这是项目的“发动机”,负责定时从外部获取数据。创建文件/var/www/html/flight_board/fetch_data.php。
<?php // fetch_data.php set_time_limit(60); // 设置脚本最长运行时间为60秒 require_once('config.php'); // 包含数据库配置和API密钥 function fetchFlightsFromAPI($apiKey, $airportIata, $type = 'arrival') { $url = "http://api.aviationstack.com/v1/flights?access_key={$apiKey}&{$type}_icao={$airportIata}"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 15); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode == 200) { return json_decode($response, true); } else { error_log("[Flight Fetcher] API请求失败,HTTP代码: {$httpCode}"); return null; } } // 主逻辑 $conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); if ($conn->connect_error) { die("数据库连接失败: " . $conn->connect_error); } // 1. 检查是否需要抓取 $metaQuery = $conn->query("SELECT last_fetch_time, fetch_interval_seconds FROM api_cache_meta WHERE id=1"); $meta = $metaQuery->fetch_assoc(); $nextFetchTime = strtotime($meta['last_fetch_time']) + $meta['fetch_interval_seconds']; if (time() < $nextFetchTime) { echo "未到抓取时间。\n"; $conn->close(); exit(0); // 静默退出,Cron日志不会产生错误 } // 2. 调用API抓取数据(示例抓取上海浦东机场的到达航班) $apiData = fetchFlightsFromAPI(AVIATIONSTACK_KEY, 'ZSPD', 'arrival'); if (!$apiData || !isset($apiData['data'])) { error_log("[Flight Fetcher] 获取API数据失败或数据为空。"); $conn->close(); exit(1); // 非正常退出,Cron会记录错误 } // 3. 清空旧数据并插入新数据(简单策略,也可用UPDATE) $conn->query("TRUNCATE TABLE flights"); // 清空表,根据需求也可选择增量更新 $stmt = $conn->prepare("INSERT INTO flights (flight_iata, flight_icao, departure_airport, arrival_airport, scheduled_arrival, estimated_arrival, status, airline_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); foreach ($apiData['data'] as $flight) { // 数据清洗与格式化 $flightIata = $flight['flight']['iata'] ?? 'N/A'; $depAirport = $flight['departure']['icao'] ?? 'N/A'; $arrAirport = $flight['arrival']['icao'] ?? 'N/A'; $scheduledArrival = $flight['arrival']['scheduled'] ?? null; $estimatedArrival = $flight['arrival']['estimated'] ?? null; $status = $flight['flight_status'] ?? 'unknown'; $airline = $flight['airline']['name'] ?? 'Unknown Airline'; // 处理时间,假设API返回的是UTC时间,转换为本地时间存储 // 更佳实践:在数据库中统一存储UTC时间,在前端显示时转换。 $scheduledArrivalLocal = $scheduledArrival ? date('Y-m-d H:i:s', strtotime($scheduledArrival . ' UTC')) : null; $estimatedArrivalLocal = $estimatedArrival ? date('Y-m-d H:i:s', strtotime($estimatedArrival . ' UTC')) : null; $stmt->bind_param("ssssssss", $flightIata, $flight['flight']['icao'], $depAirport, $arrAirport, $scheduledArrivalLocal, $estimatedArrivalLocal, $status, $airline); $stmt->execute(); } $stmt->close(); // 4. 更新抓取元数据 $conn->query("UPDATE api_cache_meta SET last_fetch_time = NOW() WHERE id=1"); echo "数据抓取与更新完成,共处理 " . count($apiData['data']) . " 条航班记录。\n"; $conn->close(); ?>关键点解析:
- 错误处理与日志:脚本必须包含完善的错误处理。使用
error_log()将错误记录到系统日志(如/var/log/apache2/error.log),方便排查。Cron任务执行时,正常的echo输出会通过邮件发送给用户,而错误退出(exit(1))则会在Cron日志中标记为失败。 - 时间处理:航班时间处理是易错点。AviationStack API返回的时间通常是UTC。我选择在入库时转换为本地时间,简化前端显示逻辑。更严谨的做法是在数据库中用
TIMESTAMP类型存储UTC时间,前端用JavaScript根据用户时区转换。 - 数据更新策略:这里采用了简单的
TRUNCATE+INSERT全量更新。对于数据量小、变化频繁的场景是可行的。如果数据量大,应考虑增量更新,通过航班唯一标识(如flight_icao+scheduled_departure)判断插入或更新。
4.2 前端展示页面设计与实现
前端页面的目标是高度模拟机场信息屏的简洁、清晰、自动刷新的风格。创建/var/www/html/flight_board/index.php。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>实时航班信息看板 - ZSPD</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Courier New', monospace; } body { background-color: #000; color: #0f0; /* 经典绿 */ padding: 20px; } .header { text-align: center; margin-bottom: 30px; } .header h1 { font-size: 3em; letter-spacing: 5px; margin-bottom: 10px; } .header .subtitle { font-size: 1.5em; color: #8f8; } .board-container { display: flex; justify-content: space-around; flex-wrap: wrap; } .board { background-color: #111; border: 2px solid #0f0; border-radius: 10px; padding: 20px; margin: 10px; flex-grow: 1; min-width: 45%; } .board h2 { text-align: center; border-bottom: 1px solid #0f0; padding-bottom: 10px; margin-bottom: 15px; font-size: 2em; } table { width: 100%; border-collapse: collapse; } th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #333; } th { background-color: #222; font-size: 1.2em; } .flight-row:hover { background-color: #1a1a1a; } .status-scheduled { color: #8f8; } .status-active { color: #ff0; font-weight: bold; } .status-landed { color: #0af; } .status-cancelled { color: #f44; text-decoration: line-through; } .time { font-family: 'Segment7Standard', monospace; font-weight: normal; } /* 可选数码字体 */ .footer { text-align: center; margin-top: 30px; color: #666; font-size: 0.9em; } .last-updated { color: #8f8; } .blink { animation: blink 1s step-start infinite; } @keyframes blink { 50% { opacity: 0.5; } } </style> <script> // 页面自动刷新(每60秒) setTimeout(function() { window.location.reload(); }, 60000); // 客户端时间动态更新(可选) function updateClientTime() { const now = new Date(); document.getElementById('current-time').textContent = now.toLocaleTimeString('zh-CN'); } setInterval(updateClientTime, 1000); window.onload = updateClientTime; </script> </head> <body> <div class="header"> <h1>✈️ 航班信息实时看板</h1> <div class="subtitle">上海浦东国际机场 (ZSPD) | 当前时间: <span id="current-time"></span></div> <div class="last-updated">数据更新时间: <?php echo date('Y-m-d H:i:s'); ?></div> </div> <div class="board-container"> <!-- 到达航班板 --> <div class="board"> <h2><span class="blink">●</span> 到达航班 ARRIVALS</h2> <table> <thead><tr><th>航班号</th><th>出发地</th><th>计划到达</th><th>预计/实际到达</th><th>状态</th><th>航空公司</th></tr></thead> <tbody> <?php require_once('config.php'); $conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); $sql = "SELECT flight_iata, departure_airport, scheduled_arrival, estimated_arrival, status, airline_name FROM flights WHERE arrival_airport = 'ZSPD' ORDER BY scheduled_arrival ASC LIMIT 20"; $result = $conn->query($sql); if ($result->num_rows > 0) { while($row = $result->fetch_assoc()) { $statusClass = 'status-' . $row['status']; $displayTime = (!empty($row['estimated_arrival']) && $row['estimated_arrival'] != $row['scheduled_arrival']) ? $row['estimated_arrival'] : $row['scheduled_arrival']; echo "<tr class='flight-row'> <td><strong>{$row['flight_iata']}</strong></td> <td>{$row['departure_airport']}</td> <td class='time'>{$row['scheduled_arrival']}</td> <td class='time'>{$displayTime}</td> <td class='{$statusClass}'>{$row['status']}</td> <td>{$row['airline_name']}</td> </tr>"; } } else { echo "<tr><td colspan='6' style='text-align:center;'>暂无到达航班数据</td></tr>"; } $conn->close(); ?> </tbody> </table> </div> <!-- 离港航班板 --> <div class="board"> <h2><span class="blink">●</span> 离港航班 DEPARTURES</h2> <table> <thead><tr><th>航班号</th><th>目的地</th><th>计划起飞</th><th>预计起飞</th><th>状态</th><th>航空公司</th></tr></thead> <tbody> <?php // 注意:示例数据表只存储了到达航班。实际中需要抓取离港数据或修改查询逻辑。 // 这里仅为展示结构,假设有一个`departures`表或`flights`表中有`type`字段区分。 echo "<tr><td colspan='6' style='text-align:center;color:#888;'>离港航班功能需扩展数据抓取逻辑</td></tr>"; ?> </tbody> </table> </div> </div> <div class="footer"> <p>Powered by Raspberry Pi & AviationStack API | 本页面每分钟自动刷新</p> </div> </body> </html>前端设计要点:
- 复古终端风格:采用黑底绿字,搭配等宽字体,营造出经典的机场终端或旧式显示屏的视觉效果,辨识度高且富有极客感。
- 状态可视化:通过不同颜色区分航班状态(计划中、活动中、已降落、已取消),让用户一眼获取关键信息。
- 自动刷新:通过JavaScript的
setTimeout实现整页自动刷新(如每60秒),这是保持信息“实时”的最简单有效方式。更高级的做法是使用Ajax局部刷新数据,但整页刷新在树莓派上完全可行,且实现简单。 - 响应式布局:使用Flexbox,使看板在横屏和竖屏下都能有较好的显示效果,适配不同的显示器摆放方式。
4.3 自动化部署:Cron任务与开机自启
数据抓取脚本需要定时执行。我们使用Linux的Cron服务。
编辑Cron任务:
crontab -e添加一行(例如每5分钟执行一次,并将输出重定向到日志文件):
*/5 * * * * /usr/bin/php /var/www/html/flight_board/fetch_data.php >> /var/log/flight_fetcher.log 2>&1*/5 * * * *:每5分钟。/usr/bin/php:使用绝对路径调用PHP解释器。>> /var/log/flight_fetcher.log 2>&1:将脚本的标准输出和错误输出都追加到指定的日志文件中,便于后期监控和调试。
开机自启Apache和MySQL: Raspberry Pi OS默认已将这些服务设置为开机启动。可以通过以下命令确认和设置:
sudo systemctl enable apache2 sudo systemctl enable mysql设置浏览器开机全屏显示(可选): 如果你希望树莓派接上显示器后自动全屏展示看板,可以:
- 安装轻量级桌面环境(如LXDE)和Chromium浏览器(如果尚未安装)。
- 编辑自动启动文件
~/.config/lxsession/LXDE-pi/autostart,添加:@xset s off @xset -dpms @xset s noblank @chromium-browser --kiosk --incognito http://localhost/flight_board/ - 这样树莓派启动后会自动进入桌面并全屏无痕模式打开看板页面。
5. 进阶优化与功能扩展思路
基础版本完成后,你可以根据兴趣进行多方面优化,让项目更强大、更稳定。
5.1 性能与稳定性优化
数据库查询优化:
- 为
flights表的关键查询字段建立索引,如(arrival_airport, status),(departure_airport),可以大幅提升前端页面加载速度。 - 考虑对历史数据(如24小时前的航班)进行归档或清理,避免
flights表无限膨胀。可以写一个每日运行的Cron脚本执行DELETE操作。
- 为
前端渲染优化:
- Ajax局部刷新:将前端页面改为静态HTML,通过JavaScript定时(如每30秒)向一个单独的PHP接口(如
api/get_flights.php)发起Ajax请求,获取JSON格式的航班数据并动态更新表格,避免整页刷新带来的闪烁和资源重新加载。 - 虚拟滚动:如果航班数量非常多(如超过100条),可以考虑使用虚拟滚动技术,只渲染可视区域内的行,极大提升页面滚动性能。
- Ajax局部刷新:将前端页面改为静态HTML,通过JavaScript定时(如每30秒)向一个单独的PHP接口(如
错误恢复与监控:
- 在抓取脚本中加入更健壮的重试机制。如果API请求失败,可以休眠几秒后重试1-2次。
- 编写一个简单的“看门狗”脚本,定期检查
api_cache_meta表中的last_fetch_time。如果长时间未更新(如超过20分钟),则发送邮件或通过Telegram Bot、Server酱等通知自己。
5.2 功能扩展
多机场支持:
- 修改数据库结构,增加一个
airports表,存储要监控的机场代码和名称。 - 修改抓取脚本,循环遍历
airports表中的所有机场,分别调用API获取数据并存储。 - 前端页面可以通过下拉菜单或选项卡切换查看不同机场的信息。
- 修改数据库结构,增加一个
航班搜索与过滤:
- 在前端页面添加搜索框,允许用户按航班号、航空公司或状态进行实时过滤。
- 这需要结合Ajax,在用户输入时向后端发送搜索请求并动态更新表格。
数据可视化:
- 集成开源图表库(如Chart.js),在另一个页面展示统计信息,例如“今日航班准点率”、“最繁忙的航线”、“各航空公司航班数量分布”等。
语音播报(高级玩法):
- 利用树莓派的音频输出接口,结合文本转语音(TTS)引擎(如eSpeak或Google TTS API),当有重要航班状态变更(如从“计划中”变为“已起飞”)时,自动进行语音播报。
6. 常见问题与故障排查实录
在实际搭建和运行过程中,你几乎一定会遇到下面这些问题。这里是我踩过坑后的经验总结。
6.1 数据抓取失败
- 症状:Cron日志 (
/var/log/flight_fetcher.log) 显示错误,或者前端页面数据长时间不更新。 - 排查步骤:
- 手动测试脚本:在SSH中运行
php /path/to/fetch_data.php,查看具体报错信息。最常见的是PHP语法错误或数据库连接失败。 - 检查API密钥和网络:确认
config.php中的API密钥有效且未过期。用curl命令手动测试API接口是否能通。 - 检查Cron环境:Cron执行环境与用户Shell环境不同,可能找不到
php命令或环境变量。务必在Cron任务中使用绝对路径(/usr/bin/php)。 - 检查文件权限:确保Web服务器用户(通常是
www-data)对脚本文件有读取权限,对日志文件有写入权限。可以运行sudo chown -R www-data:www-data /var/www/html/flight_board并sudo chmod -R 755该目录。
- 手动测试脚本:在SSH中运行
6.2 前端页面显示异常
- 症状:页面布局错乱、没有样式、或者PHP代码被直接显示出来。
- 排查步骤:
- 检查Apache是否解析PHP:访问
http://树莓派IP/info.php,如果显示的是PHP代码而不是信息页面,说明Apache未配置PHP模块。执行sudo a2enmod php版本号并重启Apache。 - 检查文件扩展名和位置:确保主页面是
.php文件,并且放在Apache的文档根目录(通常是/var/www/html/)或其子目录下。 - 检查CSS/JS路径:如果样式丢失,检查HTML中引用的CSS和JS文件路径是否正确。使用浏览器开发者工具(F12)的“网络”选项卡,查看资源是否成功加载。
- 检查Apache是否解析PHP:访问
6.3 数据库连接错误
- 症状:页面显示“数据库连接失败”或类似错误。
- 排查步骤:
- 验证MySQL服务状态:
sudo systemctl status mysql。 - 检查连接参数:确认
config.php中的DB_HOST(通常是localhost)、DB_USER、DB_PASS、DB_NAME完全正确。 - 检查用户权限:用于连接的MySQL用户是否拥有对
flight_board数据库的足够权限(SELECT, INSERT, UPDATE, DELETE等)。可以在MySQL命令行中用该用户尝试连接和操作。 - 注意
localhost与127.0.0.1:在PHP的MySQLi连接中,有时使用localhost会通过Unix socket连接,而127.0.0.1强制使用TCP/IP。如果遇到奇怪连接问题,可以尝试切换。
- 验证MySQL服务状态:
6.4 时间显示错误
- 症状:网页上显示的航班时间与实际情况相差数小时。
- 解决方案:这是时区不一致的典型表现。必须确保“三层”时区统一:
- 系统时区:运行
sudo dpkg-reconfigure tzdata设置树莓派系统时区。 - PHP时区:在
php.ini中设置date.timezone = Asia/Shanghai(举例),并重启Apache。 - MySQL时区:在MySQL命令行中执行
SET GLOBAL time_zone = ‘+8:00’;并修改my.cnf配置文件在[mysqld]下添加default-time-zone = ‘+08:00’,然后重启MySQL。 最省事的做法是在PHP连接数据库后,立即执行一个SQL设置会话时区:$conn->query(“SET time_zone = ‘+08:00’”);。
- 系统时区:运行
6.5 树莓派性能与散热
- 症状:系统运行一段时间后变卡,或者自动关机。
- 经验之谈:树莓派长时间运行LAMP服务压力不大,但如果你同时运行了桌面环境并全屏运行浏览器,内存和CPU占用会比较高。
- 散热:务必为树莓派(尤其是Pi 4)安装散热片或小风扇。过热会导致CPU降频,影响性能。
- 内存优化:关闭不需要的桌面特效和服务。可以考虑使用轻量级的窗口管理器代替完整的桌面环境。
- 电源:使用官方或质量可靠的5V/3A电源适配器,供电不足会导致各种不稳定问题。
这个项目从构思到实现,最深的体会是“简单即美”。最初总想设计一个架构复杂、功能炫酷的系统,但最终稳定运行的,恰恰是这个用最经典的LAMP栈、配合Cron和简单前端实现的版本。它教会我,在资源受限的硬件上,可靠性和可维护性往往比技术的“新潮”更重要。当你看到那块自己搭建的小屏幕,像真正的机场大屏一样稳定地刷新着航班信息时,那种把想法变成实物的满足感,是单纯写代码无法比拟的。如果你也完成了这个项目,不妨试试给它加个外壳,或者换上一块老旧的显示器,让它成为你工作台上一个独特的风景线。