展厅用多媒体播放系统

Background

学校科学楼一楼展示了机器人社团的优秀作品,但仅仅只有海报上的文字和图片介绍,比较枯燥无味。于是老师安排某学长制作了一套多媒体播放系统。学长基于现成的某品牌播(kan)(xi)(ji)、Arduino Uno、光强传感器和红外发射器制作了最初版本。原理是:当用户的手指按在装有光强传感器的玻璃上,作为主控器的 Arduino 检测到信号,通过红外发射器向播放器发射播放机配的遥控器的“播放”键所对应的红外信号,播放器接收到信号开始播放。

但控制时常失灵,主控器“一脸懵逼”:“这是天黑了,还是用户手指按下去了?嗯,那我先把红外信号发出去再说。” 加上播放器的屏幕本身质量就差,在闷热的展柜里一直亮着,没过一个星期就坏了。老的系统就一直搁置在那里。

然后学期结束的时候,这锅就我背了。

Solve It

既然光强传感器不靠谱,难以区分是否是手指真的按下了。那就干脆用电容式触摸传感器——它不仅可以准确判断是否是人手触摸的,而且可以隔着一定厚度的玻璃、亚克力等非导电材料感应。

 

既然原有的播放器屏幕不靠谱,那就需要使用普通的电脑液晶屏+电源管理系统——只有当视频播放的时候屏幕电源才打开,否则一直保持待机状态。这样不仅节省电能,还能延长寿命,避免屏幕长时间保持点亮而老化。

 

所以问题来了,播放器(这里指响应触摸事件并读取、解码视频文件,并输出视频信号的设备)用什么呢?就用树莓派(Raspberry Pi)吧——它是一种基于Linux系统的微型计算机。

 

但是。。。树莓派默认是将Micro SD卡作为 Rootfs 的,以前常常因为意外断电,导致Micro SD卡里的文件系统崩溃,树莓派无法启动。或是因为在开机状态下Micro SD卡松动,导致系统崩溃。这卡可真不靠谱。为了解决这个问题,我尝试在 Bootloader 阶段(在 Linux 系统内核启动之前)将Micro SD卡里的预打包的系统镜像复制到内存指定的区域里,然后以内存作为 Rootfs 启动操作系统。这样的话,一旦文件复制完毕,就不需要再依赖Micro SD卡。妈妈再也不用担心Micro SD卡松动或者意外掉电导致各种崩溃啦!

Additional Notes
Traditional Boot-up Sequence:

1. Bootloader
pass the kernel cmdline with "root=/dev/mmcblk0p2" option
load the kernel into RAM
boot the kernel up

2. Linux Kernel
Mount the filesystem in the Micro SD as Rootfs
Start the /sbin/init to initialize the system in the Rootfs
...

RamFS-based Boot-up Sequence:

1. Bootloader
pass the kernel cmdline with "root=/dev/ram0" option
load the kernel into RAM
load the Ramdisk image (aka. prebuilt system image) in the Micro SD into RAM
boot the kernel up

2. Linux Kernel
Mount the filesystem in the RAM as Rootfs
Start the /sbin/init to initialize the system in the Rootfs

...

Features

  • 带硬件加速的视频解码,支持[email protected]视频
  • 自动电源管理(通过HDMI输出开关实现)
  • 基于 RamFS 的 Linaro Ubuntu Vivid 15.04 的操作系统

Downloads

// TODO

The Plane Project [1] – 使用串口位运算传输飞行数据 / 控制请求

怎么传输飞行数据和控制请求是一个值得探究的问题。一开始我想用JSON,用Arduino的aJSON库和C#的写出了一些乱七八糟的代码,最后发现数据量、延时直接上天【500ms】,程序还经常跑飞,再加上无线模块的串口速率【19200】就更慢了ww,于是脑洞大开想到了位运算处理,将延时降到了【300ms】。

这是我最初使用JSON的Arduino核心代码:

void process_json() {
aJsonObject *temp_ex;

temp = aJson.getObjectItem(control_request, "Flaps");
if (temp) {
temp_ex = aJson.getObjectItem(temp, "l");
if (temp_ex)
if (temp_ex -> type == aJson_Int)
flaps.l = temp_ex -> valueint;

temp_ex = aJson.getObjectItem(temp, "r");
if (temp_ex)
if (temp_ex -> type == aJson_Int)
flaps.r = temp_ex -> valueint;
}

temp = aJson.getObjectItem(control_request, "Slaps");
if (temp) {
temp_ex = aJson.getObjectItem(temp, "l");
if (temp_ex)
if (temp_ex -> type == aJson_Int)
slaps.l = temp_ex -> valueint;

temp_ex = aJson.getObjectItem(temp, "r");
if (temp_ex)
if (temp_ex -> type == aJson_Int)
slaps.r = temp_ex -> valueint;
}

temp = aJson.getObjectItem(control_request, "Thro");
if (temp)
if (temp -> type == aJson_Int) throttle = temp -> valueint;

temp = aJson.getObjectItem(control_request, "SBak");
if (temp)
if (temp -> type == aJson_Int)
speed_brakes = temp -> valueint;

temp = aJson.getObjectItem(control_request, "Rud");
if (temp)
if (temp -> type == aJson_Int)
rudder = temp -> valueint;

temp = aJson.getObjectItem(control_request, "Ele");
if (temp)
if (temp -> type == aJson_Int)
elevator = temp -> valueint;

temp = aJson.getObjectItem(control_request, "Trim");
if (temp)
if (temp -> type == aJson_Int)
trim = temp -> valueint;

temp = aJson.getObjectItem(control_request, "Lig");
if (temp) {
temp_ex = aJson.getObjectItem(temp, "l1");
if (temp_ex)
if (temp_ex -> type == aJson_Int)
lights[1] = temp_ex -> valuebool;

temp_ex = aJson.getObjectItem(temp, "l2");
if (temp_ex)
if (temp_ex -> type == aJson_Int)
lights[2] = temp_ex -> valuebool;

temp_ex = aJson.getObjectItem(temp, "l3");
if (temp_ex)
if (temp_ex -> type == aJson_Int)
lights[3] = temp_ex -> valuebool;
}
}

void create_json() {

aJson.deleteItem(flight_data);
flight_data = aJson.createObject();

aJson.addNumberToObject(flight_data, "MSpd", motor_speed);
aJson.addNumberToObject(flight_data, "ASpd", air_speed);
aJson.addNumberToObject(flight_data, "GSpd", ground_speed);
aJson.addNumberToObject(flight_data, "Alt", altitude);
aJson.addNumberToObject(flight_data, "Hea", heading);

temp = aJson.createObject();
aJson.addNumberToObject(temp, "Lat", loc.lon);
aJson.addNumberToObject(temp, "Lou", loc.lat);
aJson.addItemToObject(flight_data, "Loc", temp);

temp = aJson.createObject();
aJson.addNumberToObject(temp, "x", acc.x);
aJson.addNumberToObject(temp, "y", acc.y);
aJson.addNumberToObject(temp, "z", acc.z);
aJson.addItemToObject(flight_data, "Acc", temp);

temp = aJson.createObject();
aJson.addNumberToObject(temp, "x", gyro.x);
aJson.addNumberToObject(temp, "y", gyro.y);
aJson.addNumberToObject(temp, "z", gyro.z);
aJson.addItemToObject(flight_data, "Gyro", temp);

temp = aJson.createObject();
aJson.addNumberToObject(temp, "x", angle.x);
aJson.addNumberToObject(temp, "y", angle.y);
aJson.addNumberToObject(temp, "z", angle.z);
aJson.addItemToObject(flight_data, "Angle", temp);

aJson.addNumberToObject(flight_data, "BtVolt", battery_voltage);

}

 

 

The Plane Project [Index]

就是因为这个坑!=_=

image

 

于是就开始填233

目前使用C#作为地面端语言,使用Saitak的F.L.Y. 5控制无人机,Arduino和Raspberry Pi作为机上主要控制系统,搭载各种作死的传感器,如空速计,倾角传感器,地磁传感器等,和各种舵机,使用串口通信模块和DTU进行地面与空中的通信。如果可能,我将会加入自动驾驶仪功能。

别忘了,以后还有更新哟~

mWeather

Summary:

科学课上学气温和气压,于是我开始脑洞大开,想造一个简单的气象监视系统,可以通过Web Browser查看气温、气压、湿度、空气质量变化曲线和当前值。使用了我的Raspberry Pi、一对Arduino Uno、一对无线模块APC220、温湿度传感器AM2303、气压传感器BMP180、尘埃传感器GP2Y1050AU0F、网络扩展板Ethernet Shield和一堆乱七八糟的外围电路。

Features:

  • 可以查看气温、大气压、湿度、空气质量变化曲线和当前值。
  • 仪表板页面采用响应式布局。
  • 支持导出数据表。
  • 使用Bootstrap。

Screenshot:

Demo:

// 已暂停工作 http://mbysensor.wicp.net:2333

Known Issues:

在不久的将来,随着数据量的逐日增大(24*60*4=5760条/天)和树莓派比较渣的性能,数据查询速度会越来越慢,平均每天减慢0.2s左右,然后就没有然后了 🙁

Raspberry Pi 监控

想监控小偷?想拍摄日出?你也许有时会有这类想法,只要你有一个树莓派,一个USB摄像头,还有Internet,你的愿望就能实现。下面我来介绍在Yeelink平台下的搭建 RPi 监控的方法。

材料:

  • 树莓派
  • USB 摄像头
  • Yeelink 账号
  • 网线或Wifi接收器

准备 Yeelink:

  1. 去 Yeelink 网站上注册一个账号(如果你之前有了,那就登录);
  2. 进入 用户中心 > 我的设备 > 增加新设备,填入相应的信息,保存;
  3. 在你增加的设备的页面中点击 增加一个传感器。
  4. 在 类型 中选择 图像传感器,填入其他信息,保存。

设置 RPi:

  1. 打开树莓派上的终端(或远程SSH连接);
  2. 如果需要,则安装摄像头驱动,此处省略N字;
  3. 连接摄像头和网络;
  4. 执行如下命令,若有正确的返回信息,那么恭喜,你的摄像头应该正常工作了!
    ls -l /dev/video0
  5. 执行如下命令,安装抓图软件;
    sudo apt-get install fswebcam
  6. 在/home/pi 中 新建一个文件yeelink.sh,写入以下内容;
    sudo fswebcam -d /dev/video0 -r 320x240 --bottom-banner --title "RaspberryPi @ Yeelink" --no-timestamp /home/pi/yeelink.jpg
    curl --request POST --data-binary @"/home/pi/yeelink.jpg" --header "U-ApiKey: *************" xxxxxxxxxxxxxxxxxxxx

    提示:请把*处替换成你的API Key(在 账户 > 我的账户设置 中),把x处替换成你的图像URL(在传感器页面中);

  7. 给脚本添加可执行权限;
    chmod +x yeelink.sh
  8. 编辑 crontab 列表,让脚本每隔1分钟运行一次;
    crontab -e

    加入下面一行:

    */1 * * * * /home/pi/yeelink.sh

嗯,这就搞定了,缓和一下激动的心情,等一小会儿,看看你的传感器页面的底部。

看到了什么?恭喜,我想你已经成功了。

若还是不行,请参阅下面几点:

  • 树莓派是否成功连接到互联网?
  • 摄像头驱动有没有装(如果需要)?
  • API KEY 或 图像URL 有没有敲错?
  • USB线是否过长(这可能是一个极易出现的问题)?
  • ……

这儿是一些建议:

  • 如果你发现上传的图片不够清楚,请在第6步中更改分辨率;
  • 但还有一点要注意的是,Yeelink的用户空间是有限的,你可能需要在第6步中改小分辨率。

最后感谢 Yeelink