尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于树莓派与LAMP栈构建实时公交信息显示系统

基于树莓派与LAMP栈构建实时公交信息显示系统 1. 项目概述用树莓派打造一块实时到离站信息板几年前我在一个本地社区中心做志愿者那里有个老旧的纸质公交时刻表信息更新不及时经常让等车的人白等半天。当时我就想能不能用身边现成的树莓派Raspberry Pi做一块电子屏实时显示公交或火车的到站离站信息这个想法就是今天要聊的“Pi based real-time arrivals and departures board”项目。简单说这就是一个基于树莓派微型电脑配合一块显示屏通过网络获取公共交通的实时数据并以清晰、美观的界面展示出来的信息板。它解决的痛点非常直接信息滞后。无论是家里的通勤者想掐点出门还是小型办公室、咖啡馆、社区公告栏需要一个公共信息展示终端一块能自动更新、免维护的实时信息板都非常实用。这个项目听起来有点极客但其实技术栈非常亲民核心就是树莓派、一个Web服务器比如Apache、以及处理数据和展示的PHP脚本。你不需要是嵌入式开发专家只要对Linux命令行和Web开发有基本了解就能跟着一步步做出来。它适合喜欢动手的开发者、创客甚至是有点技术热情的公共交通爱好者目标就是花最少的钱和精力获得一个稳定、可定制且完全属于自己的实时信息显示方案。2. 项目核心架构与方案选型2.1 为什么选择LAMPLinux, Apache, MySQL, PHP栈在树莓派上实现一个Web应用选择很多比如Python的Flask/Django或者Node.js。但我最终选择了经典的LAMP栈特别是ApachePHP的组合原因有几个。首先资源消耗与稳定性树莓派尤其是早期的型号计算资源和内存有限。Apache虽然被诟病“重”但在处理静态资源和并发连接不高的场景下比如一块信息板可能同时就几个内网用户访问其成熟度和稳定性是经过时间考验的。配合PHP-FPMFastCGI进程管理器可以有效管理PHP进程比用Python CGI或某些轻量级服务器更易于配置和维护。其次开发效率与生态PHP在数据处理和文本渲染方面非常直接。我们的核心任务就是定时抓取API数据、解析、然后渲染成HTML页面。PHP内建的cURL函数、json_decode以及模板字符串能力让这几步变得异常简单。市面上大量的公共交通API返回的都是JSON格式PHP处理起来得心应手。此外如果你想加入简单的数据缓存或日志记录MySQL/MariaDB的集成也非常顺畅。最后部署与运维的简便性在基于Debian的树莓派OS上安装Apache、PHP、MySQL几乎就是几条apt-get命令的事。配置也都有全球开发者积累的成熟文档。整个系统可以看作一个标准的Web服务器任何前端修改调整CSS样式或后端逻辑更新修改抓取频率都只需要更新文件并刷新浏览器即可无需重启整个系统或重新编译这对于后期维护和调试非常友好。2.2 硬件选型与连接考量硬件是项目的物理基础选对了事半功倍。树莓派型号推荐Raspberry Pi 3B 或 4B。3B性价比高性能足够4B的USB 3.0和更快的网络接口在处理数据时更有优势。如果追求极致低功耗和成本Zero 2 W也行但需注意其接口较少可能需要额外的转接器。显示屏这是体验的关键。官方触摸屏接口简单通过DSI连接即插即用但尺寸固定7寸价格较高。HDMI显示屏选择最多从10寸到24寸都有可以通过HDMI线连接。建议选择带有IPS面板的型号可视角度广适合多人从不同角度观看。务必确认显示器供电方式有些需要单独接电源有些可以通过树莓派的USB口供电功率需足够。电子墨水屏如果追求超低功耗和类纸显示效果可以考虑。但刷新率慢不适合频繁更新的实时信息更适合显示静态时刻表。供电与网络稳定的电源至关重要建议使用官方电源或输出电流≥2.5A的优质电源适配器供电不足会导致树莓派重启显示中断。网络连接优先使用有线以太网比Wi-Fi更稳定延迟更低对于定时抓取API数据来说可靠性更高。外壳与散热长期运行需要考虑散热。一个带有散热片或小风扇的亚克力外壳能有效防止CPU过热降频。如果放在公共场所还需要考虑外壳的坚固性和安全性。注意在购买HDMI显示器时留意其“休眠”功能。有些显示器长时间没有信号输入会自动休眠需要修改树莓派配置或显示器设置来禁用此功能确保屏幕常亮。3. 软件环境搭建与核心配置3.1 系统初始化与LAMP环境安装首先为树莓派安装最新的 Raspberry Pi OS Lite无桌面版以节省资源。烧录系统到SD卡后首次启动通过raspi-config进行基础设置扩展文件系统、更改密码、设置时区、启用SSH。接着通过有线网络连接并获取IP地址就可以用SSH客户端远程登录了。安装LAMP环境是一系列命令sudo apt update sudo apt upgrade -y sudo apt install apache2 -y sudo apt install php php-curl php-mysql php-mbstring php-xml php-gd -y sudo apt install mariadb-server -y安装Apache和PHP时php-curl是抓取API数据的必备扩展php-mbstring和php-xml用于处理可能的文本和XML格式数据虽然现在JSON是主流php-gd可能用于未来生成简单的图标。安装完成后验证Apache在浏览器访问树莓派的IP地址应看到Apache默认页面。验证PHP创建一个info.php文件sudo nano /var/www/html/info.php内容为?php phpinfo(); ?访问该文件应显示PHP配置信息。3.2 Apache与PHP的关键配置调优默认配置是为通用服务器设计的我们需要为信息板这个单一应用做优化。配置Apache虚拟主机可选但推荐在/etc/apache2/sites-available/下创建一个新配置文件例如arrivals.conf。这样可以将我们的应用隔离管理。VirtualHost *:80 ServerName arrivals.local # 或你的树莓派IP DocumentRoot /var/www/html/arrivals-board Directory /var/www/html/arrivals-board Options Indexes FollowSymLinks AllowOverride All Require all granted /Directory ErrorLog ${APACHE_LOG_DIR}/arrivals-error.log CustomLog ${APACHE_LOG_DIR}/arrivals-access.log combined /VirtualHost然后启用该站点并禁用默认站点sudo a2ensite arrivals.confsudo a2dissite 000-default.conf最后重启Apachesudo systemctl restart apache2。调整PHP配置编辑/etc/php/版本号/apache2/php.ini例如/etc/php/8.2/apache2/php.ini。max_execution_time 30单个脚本最大执行时间。抓取API可能超时可适当增大到60。memory_limit 128M内存限制。对于简单数据处理128M足够如果数据量巨大可调整。date.timezone Asia/Shanghai设置正确时区这对显示时间至关重要。display_errors Off和log_errors On生产环境务必关闭错误显示防止敏感信息泄露将错误记录到日志文件。启用必要模块确保mod_rewrite用于URL重写未来可能用到和headers模块已启用sudo a2enmod rewrite headers。3.3 数据源对接公共交通API的选择与使用这是项目的灵魂。你需要找到一个提供实时到离站数据的API。国内许多城市的公交集团或第三方平台如高德、百度地图开放平台都提供此类接口通常需要申请API Key。以获取某城市“123路公交车”即将到达“人民广场站”的数据为例一个典型的请求和响应可能如下请求GET https://api.transit.com/realtime?route123stop人民广场keyYOUR_API_KEY响应JSON{ status: success, data: { stop_name: 人民广场, upcoming: [ { route: 123, destination: 火车站, arrival_time: 2023-10-27T14:05:0008:00, estimated_arrival: 3, vehicle_id: BUS-1001 }, { route: 123, destination: 动物园, arrival_time: 2023-10-27T14:15:0008:00, estimated_arrival: 13, vehicle_id: BUS-1002 } ] } }在PHP中我们使用cURL来获取这个数据?php $api_url https://api.transit.com/realtime?route123stop人民广场keyYOUR_API_KEY; $ch curl_init($api_url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 仅用于测试生产环境应验证SSL证书 curl_setopt($ch, CURLOPT_TIMEOUT, 10); $response curl_exec($ch); $http_code curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http_code 200 $response) { $data json_decode($response, true); if ($data $data[status] success) { $upcoming_buses $data[data][upcoming]; // 处理数据... } else { // 处理API返回的错误 $upcoming_buses []; $error API数据解析失败; } } else { // 处理网络或请求错误 $upcoming_buses []; $error 网络请求失败 (HTTP . $http_code . ); } ?实操心得务必仔细阅读API文档了解调用频率限制、数据格式、错误码。将API Key存储在环境变量或配置文件如config.php中不要硬编码在脚本里。对于免费API调用频率可能受限需要合理设置缓存。4. 核心功能实现与前端展示4.1 后端数据抓取、解析与缓存策略单纯每次页面刷新都去请求API是不现实的既慢又容易触发频率限制。我们需要一个定时任务驱动的数据抓取和缓存机制。创建核心数据抓取脚本编写一个PHP脚本比如/var/www/html/arrivals-board/fetch_data.php。这个脚本负责调用API、解析JSON、并将处理后的数据存储起来。存储方式可以选择文件缓存将数据序列化后写入一个JSON文件。简单快速适合单点部署。SQLite数据库比文件更结构化便于查询历史数据或管理多个车站信息。Redis内存数据库速度极快但需要额外安装和配置。这里以文件缓存为例// fetch_data.php 核心部分 function fetchAndCacheData() { // ... 上面的cURL请求代码 ... if (!empty($upcoming_buses)) { // 添加抓取时间戳 $cache_data [ fetch_time time(), data $upcoming_buses ]; // 写入缓存文件 file_put_contents(/var/www/html/arrivals-board/cache/arrivals_cache.json, json_encode($cache_data)); file_put_contents(/var/www/html/arrivals-board/cache/arrivals_cache_backup.json, json_encode($cache_data)); // 备份 } } fetchAndCacheData();设置定时任务Cron Job让系统每分钟自动执行一次这个抓取脚本。crontab -e添加一行* * * * * /usr/bin/php /var/www/html/arrivals-board/fetch_data.php /dev/null 21这样数据每分钟更新一次对于公交信息来说通常足够实时。前端展示脚本读取缓存负责显示页面的PHP脚本如index.php不再直接请求API而是读取本地的缓存文件。// index.php $cache_file cache/arrivals_cache.json; if (file_exists($cache_file) (time() - filemtime($cache_file)) 70) { // 缓存70秒内有效 $cache_content file_get_contents($cache_file); $cached_data json_decode($cache_content, true); $upcoming_buses $cached_data[data]; $last_updated date(H:i:s, $cached_data[fetch_time]); } else { // 缓存过期或不存在 $upcoming_buses []; $last_updated 数据更新中...; // 可以尝试触发一次同步抓取或仅显示错误信息 }4.2 前端页面设计与自动刷新前端的目标是清晰、易读、在显示屏上美观。HTML结构设计一个全屏显示的表格或卡片列表。!DOCTYPE html html head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title实时公交信息 - 人民广场站/title link relstylesheet hrefstyle.css /head body div classcontainer header h1 人民广场站 实时到站信息/h1 p classupdate-time最后更新: span idlastUpdate?php echo $last_updated; ?/span/p /header main ?php if (empty($upcoming_buses)): ? div classno-data暂无即将到站车辆信息或数据正在更新.../div ?php else: ? table classarrivals-table thead.../thead tbody ?php foreach ($upcoming_buses as $bus): ? tr td classroute?php echo htmlspecialchars($bus[route]); ?/td td classdestination?php echo htmlspecialchars($bus[destination]); ?/td td classarrival ?php // 计算分钟数并显示 $mins intval($bus[estimated_arrival]); if ($mins 0) { echo span classimminent即将进站/span; } elseif ($mins 3) { echo span classsoon . $mins . 分钟/span; } else { echo $mins . 分钟; } ? /td td classvehicle?php echo htmlspecialchars($bus[vehicle_id]); ?/td /tr ?php endforeach; ? /tbody /table ?php endif; ? /main footer.../footer /div script srcscript.js/script /body /htmlCSS样式使用全屏布局大字体、高对比度颜色如黑底绿字或白底蓝字确保在远处也能看清。隐藏滚动条禁用文本选择。body, html { margin: 0; padding: 0; height: 100%; overflow: hidden; font-family: Arial, sans-serif; } .container { display: flex; flex-direction: column; height: 100vh; background: #111; color: #0f0; } .arrivals-table { width: 100%; border-collapse: collapse; font-size: 2.5vw; } .arrivals-table td, .arrivals-table th { padding: 20px; text-align: center; border-bottom: 1px solid #333; } .soon { color: #ff9900; font-weight: bold; } .imminent { color: #ff0000; font-weight: bold; animation: blink 1s infinite; } keyframes blink { 50% { opacity: 0.5; } }JavaScript自动刷新让页面定时如每30秒刷新数据而无需手动刷新整个页面。使用fetchAPI调用一个只返回数据的PHP端点如api.php然后更新DOM。// script.js function refreshData() { fetch(api.php) // 这个api.php只返回JSON格式的最新数据 .then(response response.json()) .then(data { // 更新表格中的行 updateTable(data.upcoming); document.getElementById(lastUpdate).textContent data.last_updated; }) .catch(error console.error(刷新数据失败:, error)); } // 每30秒刷新一次 setInterval(refreshData, 30000); // 页面加载后立即刷新一次 document.addEventListener(DOMContentLoaded, refreshData);4.3 进阶功能多站点支持与异常处理基础功能完成后可以考虑扩展。多站点/多线路轮播如果你关心多个车站的信息可以修改fetch_data.php循环请求多个API将数据合并缓存。前端则通过选项卡Tab或定时轮播使用JavaScriptsetInterval切换显示的数据集来展示。网络异常处理在抓取脚本中加入重试机制。如果第一次请求失败等待几秒后重试1-2次。可以将失败日志写入文件便于排查。function fetchWithRetry($url, $maxRetries 2) { for ($i 0; $i $maxRetries; $i) { $result makeHttpRequest($url); // 封装好的请求函数 if ($result[success]) { return $result; } if ($i $maxRetries) { sleep(3); // 等待3秒后重试 } } return [success false, error 所有重试均失败]; }系统状态监控创建一个简单的状态页面status.php显示缓存文件最后更新时间、API最后成功调用时间、树莓派负载和温度等方便远程检查。5. 部署、优化与故障排查实录5.1 系统部署与开机自启动为了让信息板在树莓派开机后自动运行我们需要将浏览器设置为全屏Kiosk模式。安装轻量级桌面环境可选如果使用Lite版系统需要先安装一个极简的桌面环境来运行浏览器如lxde或直接安装xserver-xorg和xinit。sudo apt install --no-install-recommends xserver-xorg x11-xserver-utils xinit openbox chromium-browser配置自动登录并启动浏览器编辑~/.bash_profile或创建~/.xinitrc文件。# ~/.xinitrc # 禁用屏幕保护和电源管理 xset s off xset -dpms xset s noblank # 以全屏模式启动Chromium并隐藏警告和错误提示 chromium-browser --noerrdialogs --disable-infobars --kiosk http://localhost/arrivals-board/设置自动启动X Window编辑/etc/systemd/system/autostart.service需要sudo权限。[Unit] DescriptionStart Arrivals Board Kiosk Aftergraphical.target [Service] Typesimple Userpi EnvironmentDISPLAY:0 ExecStart/usr/bin/startx /home/pi/.xinitrc Restarton-failure RestartSec5 [Install] WantedBymulti-user.target然后启用服务sudo systemctl enable autostart.service。踩坑记录Chromium在Kiosk模式下有时会因GPU内存不足导致页面渲染问题。可以在/boot/config.txt中增加gpu_mem128来分配更多内存给GPU。如果遇到浏览器崩溃可以考虑使用更稳定的Firefox ESR版本或者使用unclutter工具隐藏鼠标指针。5.2 性能优化与长期运行稳定性树莓派7x24小时运行优化很重要。禁用不必要的服务用sudo systemctl disable关闭蓝牙、Wi-Fi如果用有线、Avahi-daemon等服务。使用RAM磁盘存储临时文件将频繁读写的小文件如日志、临时缓存挂载到内存中减少SD卡磨损。sudo mkdir /tmp/ramdisk sudo mount -t tmpfs -o size10m tmpfs /tmp/ramdisk然后在PHP脚本中将日志路径指向这里。注意重启后数据会丢失只适合临时数据。监控SD卡健康定期检查SD卡剩余空间和寿命。可以设置一个Cron任务每周通过df -h和sudo smartctl -a /dev/mmcblk0如果支持检查并将报告发送到邮箱。配置日志轮转防止Apache或应用日志撑满磁盘。配置/etc/logrotate.d/apache2和自定义应用日志。5.3 常见问题与排查技巧在实际部署中你可能会遇到以下问题问题现象可能原因排查步骤与解决方案屏幕黑屏或无法显示1. 显示器未通电或信号线松动。2. 树莓派未启动。3. 浏览器未成功启动。1. 检查电源和HDMI线连接。2. 观察树莓派ACT和PWR指示灯。通过SSH尝试登录。3. 检查autostart.service状态sudo systemctl status autostart.service。查看Xorg日志journalctl -u autostart。页面显示“数据更新中”或空白1. API请求失败。2. 缓存文件权限问题。3. Cron任务未执行。1. 手动运行fetch_data.php看输出php /path/to/fetch_data.php。检查网络和API Key。2. 确保Web服务器用户如www-data对缓存目录有读写权限sudo chown -R www-data:www-data /path/to/cache。3. 检查Cron日志sudo grep CRON /var/log/syslog。页面布局错乱字体小CSS未正确加载或视口设置问题。1. 检查浏览器开发者工具F12的Console和Network标签看是否有CSS文件404错误。2. 确认HTML中meta nameviewport标签已设置。3. 检查CSS中的字体单位是否使用vw或vh进行响应式适配。信息更新延迟大1. API本身延迟。2. 树莓派网络慢或负载高。3. 缓存时间设置过长。1. 直接测试API响应速度。2. 使用htop查看CPU/内存占用ping测试网络延迟。3. 调整Cron任务频率如改为每30秒一次并相应调整前端JS的刷新间隔。注意不要超过API调用频率限制。树莓派运行一段时间后卡顿1. 内存泄漏或某个进程占用过高。2. CPU过热降频。3. SD卡读写频繁导致性能下降。1. 使用htop排序查看内存和CPU占用重启异常进程。2. 安装散热片或风扇使用vcgencmd measure_temp监控温度。3. 将日志、临时文件移到RAM磁盘减少对SD卡的写入。个人调试心得当问题出现时日志是你的第一手资料。确保PHP错误日志/var/log/apache2/error.log、自定义的应用日志以及Cron任务的日志都配置好并定期查看。对于前端问题别忘了树莓派上的浏览器也可以按F12打开开发者工具可能需要接上键盘这是调试JavaScript和CSS的利器。最后保持耐心硬件项目的调试往往需要结合软件和物理连接一起排查。
返回列表