来自Wi-Fi专家的声音

 

SX-580为台灯的无线化赋能 (4)

撰写于:2014年10月23日
作者:silex Wi-Fi专家

在上一期中,我们在 busybox httpd 上实现了 CGI,并可以通过 CGI 操作来控制 GPIO 端口,从而使 LED 点亮或熄灭。 本期是最后一期,我们将进行 HTML 的外观改进和继电器主板的制作等。
改进用户界面
iPhone 显示屏的物理分辨率在 3G 之前的机型为 320 x 480,而从 Retina 显示屏开始,分辨率变成了 640 x 960(iPhone 4)或者 640 x 1136(iPhone 5)。 然而,由于普通网站是为 PC 设计的,因此在宽度为 320 或 640 的显示屏上会显示不全,为了防止这种情况,iPhone 的浏览器(Safari)默认会将页面宽度缩小至大约 1024 像素等效的尺寸来显示。这就是为什么之前的 HTML 和 CGI 测试显示的图像如此之小的原因。 这次我们将制作一个 240 x 320 分辨率的灯泡图像,并将其放大到 480 x 640 用于表示“当前状态”,而 ON/OFF 按钮则使用 240 x 240 分辨率的“iPhone 风格”的图像,并以原始尺寸使用。

图片包含 游戏机, 物体, 灯光, 灯描述已自动生成


上一次我们使用了 <input type="submit"> 来实现按钮,但这次我们将使用图像按钮。使用图像按钮提交表单可以通过 <input type="image" src=URL> 来实现,但这种类型的按钮不仅会传递出「被按下了」这一信息,还会通过参数 X 和 Y 附加「哪里被按下了」的信息。 因此,QUERY_STRING 会变成像 SW=ON&X=145&Y=137 这样。所以,不能再像上一次的脚本那样,仅通过环境变量 QUERY_STRING 的简单字符串匹配来判断 ON/OFF 状态了。
这是一个简单的解析器... 例如,可以使用 echo $QUERY_STRING | grep -q “SW=ON” 这样的脚本来判断 $QUERY_STRING 是否包含 SW=ON,但当我们使用 grep 实际建立一个 CGI 原型时,我们发现每次点击按钮都会访问带有不同坐标的 URL,导致浏览器将其判断为 “新的 URL”,并不断地在访问历史中记录。这不是一个使用起来很方便的方法。因此,这次我决定不使用表单,而是使用 <a href="URL?参数"> 这样的链接标签直接将参数传递给 CGI。

完成的 CGI 源代码如下所示,CGI 文件名也从 “gpiotest.cgi ”改为 “onoff.cgi”
 
#!/bin/sh
gpio_path=/sys/class/gpio/gpio56
if [ $QUERY_STRING = "SW=ON" ]; then
        echo 0 > $gpio_path/value
fi
if [ $QUERY_STRING = "SW=OFF" ]; then
        echo 1 > $gpio_path/value
fi
led_state=`cat $gpio_path/value`
echo "Content-type:text/html"
echo
echo "<html><head><title>SX-580-2700DM</title></head><body>"
echo '<div align="center">'
if [ $led_state = "1" ]; then
        echo '<img src="../bulb_off.jpg" alt="OFF"'
else
        echo '<img src="../bulb_on.jpg" alt="ON"'
fi
echo ' width=480 height="640"><br clear="all">'
echo '<table><tr><th>OFF</th><th>ON</th></tr>'
echo '<tr><td><a href="/cgi-bin/onoff.cgi?SW=OFF">'
echo '<img src="../button_off.jpg" alt="OFF" width=240 height=240></a>'
echo '</td><td><a href="/cgi-bin/onoff.cgi?SW=ON">'
echo '<img src="../button_on.jpg"  alt="ON"  width=240 height=240></a>'
echo '</td></tr></table>'
echo '</div></body></html>'
 
我将展示使用 onoff.cgi 的 iPhone 屏幕截图。 背景是白色的,上面有两个简洁明了的灯泡和按钮。如果你想精心设计,可以加入背景图片或者替换成 3D 渲染图像,这取决于预算和时间,可以发挥出各种创意(笑)。

改良CGI画面 

改进CGI画面


制作继电器主板
这是一份继电器主板的电路图。就像令人怀念的 “初次电子作品” 呢。虽然是无需特别说明的极为基础的电路,但是继电器驱动采用 PNP 晶体管,这是为了与 SX-580 GPIO LED 的点亮为负逻辑相匹配。

继电器底座电路图 继电器底座电路图


继电器使用的是直流3伏驱动的OMRON G6RL-14-ASI-DC3型号,交流侧可以承受10安培250伏交流电,,功率相当大。话虽如此,如果连接像吸尘器这样的设备,继电器的接点可能会烧毁或熔化,所以最好加入熔断器,但这次省略了。 在继电器的驱动线圈侧有飞轮二极管,接点侧有串联的薄膜电容器(0.1uF)和电阻(100 Ω)构成的火花消除器,这也是在"初次电子作品"中熟悉的电路,对吧。

包括主板在内的零件是在附近的Radioshack买的(他们的零件货架还在哦...这种值得尊敬的创业精神!),后来在发现缺少的零件时,我是通过Digi-Key补充购买的。交流电缆是在Target买的。20针的扁平电缆和连接器是碰巧在公司内部找到的。

在Radioshack购买的部件 在Radioshack购买的部件


下面是已完成电路板的图片。 这个似乎没有什么需要解释的,但需要注意的是OMRON G6RL 的引脚间距不是2.54mm。宽度方向是7.62mm,但长度方向是18.9mm、22.1mm、25.3mm,不能完美地适配到标准的蛇眼主板上。这次我通过锉削主板孔,插入后再多加了一些焊锡来固定。我没有检查通孔的间距,因为我以为2.54mm间距是理所当然的...即使是同为OMRON品牌的额定5A的G6B系列继电器,似乎是2.54mm的间距。

继电器主板整体视图 继电器主板整体视图

 

继电器底座组件表面 

继电器底座组件表面

 

继电器主板的布线面 继电器主板的布线面



将继电器主板连接到SX-580,并尝试向gpio56发送0/1信号。LED闪烁的同时,应该会伴随着电磁式继电器特有的“咔嗒 ”声,交流电路应该会随之ON/OFF切换。在交流侧连接一个轻便负载,比如台灯,尝试使用iPhone进行ON/OFF控制。


集成
好了,从这里开始是最后的集成工作。目前,HTML文件群被放置在/root目录下,看起来并不美观。另外,gpio56的初始化和httpd的启动也需要自动化。让我们尝试将这些集成到开发环境sx580sdk/snapshot中。

HTML文件群应该放置在何处会因系统而异,但在Linux系统中,通常会放在/usr/share/www目录下。目标系统上的根目录原型在开发环境中是sx580sdk/snapshot/staging/skeleton,所以……
 
mkdir sx580sdk/snapshot/staging/skeleton/usr/share/www
mkdir sx580sdk/snapshot/staging/skeleton/usr/share/www/cgi-bin

然后创建下列文件,并将文件放置在目录中。
 
sx580sdk/snapshot/staging/skeleton/usr/share/www/bulb_on.jpg
sx580sdk/snapshot/staging/skeleton/usr/share/www/bulb_off.jpg
sx580sdk/snapshot/staging/skeleton/usr/share/www/button_on.jpg
sx580sdk/snapshot/staging/skeleton/usr/share/www/button_off.jpg
sx580sdk/snapshot/staging/skeleton/usr/share/www/cgi-bin/onoff.cgi
 
busybox httpd可以通过命令行参数-h来指定主页目录,所以如果启动httpd为:
 
httpd -h /usr/share/www
 
WEB服务器将在/usr/share/www目录下运行。在初始化脚本sx580sdk/snapshot/staging/skeleton/etc/init.d/rcW的末尾添加gpio56的初始化代码,并同时添加启动httpd的代码。
 
#
# Initialize GPIO for AC control, start HTTP server
#
echo 56 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio56/direction
echo 1 > /sys/class/gpio/gpio56/value

httpd -h /usr/share/www

exit 0
 
好了,这样HTTP和CGI应该能够自动启动了,但是如果不明确输入像http://192.168.99.1/cgi-bin/onoff.cgi这样的URL,就无法连接,这有点不够友好。当然,如果先连接一次并将其添加为书签,以后就可以方便地访问了,但是如果在输入IP地址后能够自动跳转到CGI页面,那就更好了。因此,我在/usr/share/www/index.html放置了以下HTML代码,以便在默认情况下直接跳转到CGI页面。
 
<html>
<head>
<meta http-equiv="refresh" content="1;url=/cgi-bin/onoff.cgi">
</head>
<body>
<script type="text/javascript">
<!--
document.location.replace("/cgi-bin/onoff.cgi");
//-->
</script>
<a href="/cgi-bin/onoff.cgi">Click here</a>
</body>
</html>
 
document.location.replace(“/cgi-bin/onoff.cgi”); 是一条 JavaScript 命令,表示页面加载后跳转到 /cgi-bin/onoff.cgi。 如果浏览器不支持 JavaScript(尽管现在这样的浏览器很少见),或出于安全等原因禁止执行 JavaScript,我在HTML的head部分嵌入了元标签<meta http-equiv="refresh" content="1;url=/cgi-bin/onoff.cgi">,以指示1秒后刷新到/cgi-bin/onoff.cgi。为了应对JavaScript和元标签都不生效的极个别情况(虽然我认为现在几乎不可能发生),我嵌入了一个使用<a href="/cgi-bin/onoff.cgi">的链接,让用户可以“手动”跳转到CGI页面。




WPA2-PSK 安全设置
好了,到目前为止,我们使用的是SX-580 SDK默认提供的hostapd.conf配置文件,以 SSID=“SX-IMAPP-SDK ”和没有安全设置的方式作为 AP 运行。 最后,我想将其设置为使用WPA2-PSK安全模式。hostapd.conf配置文件中有很多选项,但要设置WPA2-PSK,仅需要添加四行就足够了。
 
#
# This file is a sample configuration file of hostapd.
#
interface=ath0
# bridge=br0
driver=ar6000
ssid=SX-580 Light Stand
channel_num=11
ignore_broadcast_ssid=0
auth_algs=1
ctrl_interface=/var/run/hostapd
wpa=2
wpa_passphrase=lightstand
wpa_key_mgmt=WPA-PSK
wpa_pairwise=CCMP
 
ssid= 可以设置为任何容易识别的名称。这次我特意设置了包含空格的"SX-580 Light Stand"。
wpa=2 指定了WPA的工作模式,1:仅限WPA1,2:仅限WPA2,3:WPA1/2混合。WPA1已经是逐渐过时的标准,所以这次我选择了2,即"仅限WPA2"。
wpa_passphrase= 用于指定密码字符串。请指定8到64个字符的长度。这次为了易于理解,我指定了"lightstand"(从安全角度来说,并不推荐过于简单的密码)。
wpa_key_mgmt= 应指定为"WPA-PSK"。如果指定为"WPA-EAP",则会启用所谓的企业安全模式,但这里不进行详细说明。
wpa_pairwise= 用于指定使用的加密模式,"CCMP":仅限CCMP-AES,"TKIP":仅限TKIP,"TKIP CCMP":TKIP/CCMP混合模式。TKIP也是逐渐过时的标准,所以这次我指定了仅使用CCMP。

另外,在WPA/WPA2中,可以为单播使用的配对密码(Pairwise Cipher)和多播使用的组密码(Group Cipher)使用不同的加密算法,但在hostapd中,通过设置wpa_pairwise,组密码会被自动选择(如果是TKIP或CCMP单一算法,则使用相同的算法;如果是CCMP/TKIP混合,则使用TKIP算法)。

对hostapd.conf进行上述修改并重新启动hostapd之后,SSID应该会变化,并且以WPA2-PSK安全模式运行。请使用iPhone连接并输入无线连接密码,确认是否能够成功连接。

运行中的台灯

 输入WPA2-PSK


和往常一样,hostapd.conf的“模板”位于开发环境的sx580sdk/snapshot/staging/skeleton/etc目录下,因此一旦确认它能在目标机上运行,那么就应该将这些更改反映回开发环境中。

最后,创建一个包含上述所有更改的固件,将其写入SX-580 SDK,并展示连接了继电器主板并把AC输出连接到台灯上的样子。

运行中的台灯 

运行中的台灯

 

总结
这个系列共四篇文章,您觉得如何呢?从技术角度来说,“将Wi-Fi接口设置为AP模式并连接设备(iPhone)”、“启动带有CGI的HTTP服务”、“通过CGI进行GPIO的0/1操作”、“通过GPIO驱动继电器”,每个单独的元素都是非常基础的,绝对不算难事。但是,为了实现“用iPhone操作的无线台灯”这一目的,我们必须依次解决每一个步骤(※注),然后将它们整合起来。这就是“创造事物”的基础。

(※注)本次的挑战包括了 “如果不运行 DHCP,仅靠hostapd无法完成 iPhone的Wi-Fi 连接”、“在BUSYBOX HTTP上运行CGI的方法”、“不使用Perl(只用Shell脚本)如何解析HTTP POST的参数”等事项。

在公司内部展示了“用iPhone操作的无线台灯”,获得了相当多的好评。从SX-580的规格来看,能做到这样的事情是“理所当然”的(※注),但是将自己的智能手机连接起来,通过点击屏幕就能让灯点亮或熄灭,这种“可以实际触摸到的”“可以用眼睛看到的”操作,在演示时似乎具有巨大的效果。这可能也是“创造事物”的基础之一。

(※注) 从技术角度来看,这只是对GPIO单个位的操作,所以在主板上LED的闪烁并没有变化。也可以说只是简单地在GPIO外部加上了继电器电路。但是,作为技术演示,伴随着勇敢的电磁式继电器的驱动声(笑)和台灯的闪烁,这具有很大的影响力。

本次制作的台灯只有ON/OFF的基本功能,但是通过巧妙设计AC侧的电路,也可以实现光量调节等高级功能。即使是ON/OFF操作,也可以增加“显示点亮时间(耗电量)”、“增加定时器功能”等附加功能。另外,这次我们使用了固定的HTTP服务器IP地址(192.168.99.1),但使用SX-580上的DNS服务器功能实现“即使随意输入URL也总是连接到SX-580 HTTP服务器(DNS重定向)”的功能也会很有趣。
 

 



嵌入式无线LAN模块产品介绍页面