tcpdump工具是分析网络协议和数据包的利器,也可以在Android上使用(需要root)。
首先在android上安装tcpdump
wget http://www.strazzere.com/android/tcpdump
adb push tcpdump /data/local/tmp/tcpdump
adb chmod 755 /data/local/tmp/tcpdump
然后使用root用户启动tcpdump,在android上进行相应的操作后,按ctrl+c中断
adb shell
shell@android:/ $ su
root@android:/ # /data/local/tmp/tcpdump -h
tcpdump version 3.9.8
libpcap version 0.9.8
Usage: tcpdump [-aAdDeflLnNOpqRStuUvxX] [-c count] [ -C file_size ]
[ -E algo:secret ] [ -F file ] [ -i interface ] [ -M secret ]
[ -r file ] [ -s snaplen ] [ -T type ] [ -w file ]
[ -W filecount ] [ -y datalinktype ] [ -Z user ]
[ expression ]
root@android:/ # /data/local/tmp/tcpdump -p -vv -s 0 w /sdcard/capture.pcap
树莓派使用了一个无线网卡连接家里的无线路由器,在实际使用过程中发现连续运行多天后会掉线,而且掉线后基本上就再也连不上网了,需要重启树莓派才能恢复,十分麻烦。
假设无线路由器IP是192.168.1.1,于是每隔15分钟检查一下,是否能从树莓派上ping通路由器;如果不能则重启无线网络,脚本如下:
network.sh
#!/bin/bash
export PATH=/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
ping_count() {
count=0
`timeout 5 ping 192.168.1.1 | while read LINE; do
{
if [[ "${LINE}" =~ "64 bytes from" ]]; then
let "count = $count + 1"
echo "export count=$count"
fi
}
done`
echo $count
}
if [[ $(ping_count) < 1 ]]; then
ifconfig wlan0
ifconfig wlan0 down
sleep 1
ifconfig wlan0 up
sleep 1
netcfg -r wlan0-Hugo-Nas
sleep 5
if [[ $(ping_count) < 1 ]]; then
echo "Fatal error: wifi is down, rebooting now..."
reboot
fi
fi
树莓派提供了一个连接头让我们访问CPU的17个GPIO接口,如下图

这些接口可配置成输入或输出。本文主要讨论GPIO引脚作为输出时电流的限制。
阻抗 (impendance)
阻抗和和电阻的区别(resistance)在于电阻的阻值是固定的,不会随着电流变化,阻抗则不然,可能随着外部变化,如电流或频率变化。从另一个角度来说,电阻是线性的,但阻抗不是。比如放大器的阻抗会随着输出的信号频率变化。
树莓派的的每个GPIO引脚都有一个寄存器可以设置引脚的驱动强度,也就是在保持输出电压为逻辑0和1的情况下,可以改变阻抗的大小从而改变GPIO引脚的输出电流大小。
通过如下电路测量相同电流下不同阻抗对应的GPIO电压输出(其中用到了一个电位器调节电流保持恒定):

通过计算后,下表是当输出电流为2,4 … 16mA时,对应的阻抗大小以及如果发生短路时的短路电流大小。
树莓派的GPIO接口数目有限,驱动一个步进电机需要占用4个, 一个Nokia 5110液晶也要占4个, 传感器输入至少需要一个,多玩几个外设后接口就不够用了。如果接口可以复用就可以让树莓派驱动更多的外设了,本文讨论如何使用74HC595集成电路芯片来扩展树莓派的I/O接口。
芯片介绍
SN74HC595N是德州仪器公司生产的集成电路芯片,是一个8位串行输入变串行输出或并行输出移位寄存器,具有高阻关断,高电平和低电平三态输出。在IO扩充上,可以最多串联15片,也就是高达120个IO扩充。

(注意到芯片上的小凹槽了吗,拿芯片的时候以这个为参考物就不会搞反了)
接口的常用命名方式有以下两种:
| 接口代号(编号) |
说明 |
接口代号(编号) |
说明 |
| Q7’(9) |
serial data output |
QH’ (9) |
serial data output |
| MR (10) |
Master Reset (Active Low) |
SRCLR (10) |
Shift register CLeaR |
| SH_CP (11) |
shift register clock input |
SRCLK (11) |
Shift Register CLocK input |
| ST_CP (12) |
storage register clock input |
RCLK (12) |
storage Register CLocK input |
| OE (13) |
output enable input (Active Low) |
OE (13) |
Output Enable |
| DS (14) |
serial data input |
SER (14) |
SERial data input |
| Qx (15,1-7) |
data output |
Qx (15,1-7) |
data output |
背景介绍
把网站托管在树莓派上后如果家里停电或是宽带故障,会造成网站中断。本文提供一个免费的解决方案(前提是你需要有自己的一个域名,并由DNSPod解析)
DNSPod
首先需要在DNSPod里设置好需要failover的域名CNAME:比如hugozhu.myalert.info

其中默认指向pi.myalert.info, 这是一个域名的A Record,会由运行在树莓派上的脚本来更新动态IP,国外则指向github。当停电时我们需要自动把`默认`这条纪录修改成github。
使用下面命令获得相应CNAME的domain_id:
curl -k https://dnsapi.cn/Domain.List -d "login_email=xxx&login_password=xxx"
使用下面命令获得相应CNAME的record_id:
背景介绍
前面的文章(见参考链接)已经介绍了如何使用按键作为树莓派的输入。在实际应用中可以通过按下按键循环显示预先设定的脚本输出到显示屏幕,需求如下:
- 如果按键不被触动,则定时5秒执行脚本获取最新内容显示;
- 因为不同的脚本获取内容速度会不一样,我们要求如果超过500ms脚本还未返回,需要在屏幕上显示“loading…”这样的过渡内容,如果脚本在500ms内返回,则不显示。
使用Goroutine和Channel可以很方便的实现这个需求。
代码
var screen_chan chan int
var switch_chan = make(chan bool)
func main() {
//a goroutine: 检查按键是否被按
go func() {
last_time := time.Now().UnixNano() / 1000000
btn_pushed := 0
total_mode := 3
for msg := range WiringPiISR(PIN_GPIO_6, INT_EDGE_FALLING) {
if msg > -1 {
n := time.Now().UnixNano() / 1000000
delta := n - last_time
if delta > 300 { //如果两次按键变化的间隔时间<300ms,是因为接触信号不稳定可以忽略掉
last_time = n
btn_pushed++
screen_chan <- btn_pushed % total_mode
}
}
}
}()
//a goroutine: 根据管道消息刷新屏幕
go loop_update_display()
//选择确实的屏幕内容脚本编号
screen_chan <- 0
//a goroutine: 定时5s向管道发送更新屏幕内容的信号
ticker := time.NewTicker(5 * time.Second)
go func() {
for {
<-ticker.C
screen_chan <- -1
}
}()
...
}
func loop_update_display() {
current_screen := 0
for msg := range screen_chan {
switch_screen := false
if msg >= 0 {
//说明是按钮触发的消息,而不是定时器触发的(-1)
if msg != current_screen {
//btn pushed
current_screen = msg
switch_screen = true
go func() {
select {
case <-time.After(500 * time.Millisecond):
display_loading()
<-switch_chan
case <-switch_chan:
}
}()
}
}
switch current_screen {
case 0:
display_screen0()
case 1:
display_screen1()
case 2:
display_screen2()
}
if switch_screen {
switch_chan <- true
}
}
}
名词定义
执行体 - Go里的Goroutine或Java中的Thread
背景介绍
内存模型的目的是为了定义清楚变量的读写在不同执行体里的可见性。理解内存模型在并发编程中非常重要,因为代码的执行顺序和书写的逻辑顺序并不会完全一致,甚至在编译期间编译器也有可能重排代码以最优化CPU执行, 另外还因为有CPU缓存的存在,内存的数据不一定会及时更新,这样对内存中的同一个变量读和写也不一定和期望一样。
和Java的内存模型规范类似,Go语言也有一个内存模型,相对JMM来说,Go的内存模型比较简单,Go的并发模型是基于CSP(Communicating Sequential Process)的,不同的Goroutine通过一种叫Channel的数据结构来通信;Java的并发模型则基于多线程和共享内存,有较多的概念(violatie, lock, final, construct, thread, atomic等)和场景,当然java.util.concurrent并发工具包大大简化了Java并发编程。
Go内存模型规范了在什么条件下一个Goroutine对某个变量的修改一定对其它Goroutine可见。
Happens Before
在一个单独的Goroutine里,对变量的读写和代码的书写顺序一致。比如以下的代码:
package main
import (
"log"
)
var a, b, c int
func main() {
a = 1
b = 2
c = a + 2
log.Println(a, b, c)
}
(!未完!)
除了SPI协议外,树莓派还支持I2C。I2C是为了连接低速周边装置设计的,只需要用两根线(SDA和SCL,也就是树莓派的端口8和9-wiringPi编号)。
I2C

上图是一个主控使用I2C驱动3个设备的示意图
参考链接
- http://zh.wikipedia.org/wiki/I²C
- https://projects.drogon.net/raspberry-pi/wiringpi/i2c-library/
WiringPi是树莓派上比较好的一个开发库,是用C语言写的。使用cgo,我们可以在Go语言里方便的调用WiringPI的函数,于是我包装了一个WiringPi-Go,目前支持wiringPi的基本功能,硬件SPI协议驱动Nokia 5110屏幕,以及中断,未来还会增加PWM和I2C协议的支持。
下面是一个完整的使用例子,结合了之前的两个电路:链接1,链接2
通过push button可以切换液晶屏显示不同脚本的输出内容。
lcd_switch.go
package main
import (
. "github.com/hugozhu/rpi"
"github.com/hugozhu/rpi/pcd8544"
"log"
"os/exec"
"time"
)
const (
DIN = PIN_MOSI
SCLK = PIN_SCLK
DC = PIN_GPIO_2
RST = PIN_GPIO_0
CS = PIN_CE0
PUSHBUTTON = PIN_GPIO_6
CONTRAST = 40 //may need tweak for each Nokia 5110 screen
)
var screen_chan chan int
var TOTAL_MODES = 3
func init() {
WiringPiSetup()
pcd8544.LCDInit(SCLK, DIN, DC, CS, RST, CONTRAST)
screen_chan = make(chan int, 1)
}
func main() {
//a goroutine to check button push event
go func() {
last_time := time.Now().UnixNano() / 1000000
btn_pushed := 0
for pin := range WiringPiISR(PUSHBUTTON, INT_EDGE_FALLING) {
if pin > -1 {
n := time.Now().UnixNano() / 1000000
delta := n - last_time
if delta > 300 { //software debouncing
log.Println("btn pushed")
last_time = n
btn_pushed++
screen_chan <- btn_pushed % TOTAL_MODES //switch the screen display
}
}
}
}()
//a groutine to update display every 5 seconds
go loop_update_display()
//set screen 0 to be default display
screen_chan <- 0
ticker := time.NewTicker(5 * time.Second)
for {
<-ticker.C
screen_chan <- -1 //refresh current screen every 5 seconds
}
}
func loop_update_display() {
current_screen := 0
for screen := range screen_chan {
if screen >= 0 {
if screen != current_screen {
//btn pushed
current_screen = screen
display_loading()
}
}
switch current_screen {
case 0:
display_screen0()
case 1:
display_screen1()
case 2:
display_screen2()
}
}
}
func display_loading() {
pcd8544.LCDclear()
pcd8544.LCDdrawstring(0, 20, "Loading ...")
pcd8544.LCDdisplay()
}
func display_screen0() {
out, err := exec.Command("/bin/screen_0.sh").CombinedOutput()
if err != nil {
out = []byte(err.Error())
}
pcd8544.LCDclear()
pcd8544.LCDdrawstring(0, 0, string(out))
pcd8544.LCDdisplay()
}
func display_screen1() {
out, err := exec.Command("/bin/screen_1.sh").CombinedOutput()
if err != nil {
out = []byte(err.Error())
}
pcd8544.LCDclear()
pcd8544.LCDdrawstring(0, 0, string(out))
pcd8544.LCDdisplay()
}
func display_screen2() {
out, err := exec.Command("/bin/screen_2.sh").CombinedOutput()
if err != nil {
out = []byte(err.Error())
}
pcd8544.LCDclear()
pcd8544.LCDdrawstring(0, 0, string(out))
pcd8544.LCDdisplay()
}
夏天到了,树莓派的CPU温度也开始节节攀升,虽然我们也可以用云服务cosm来监控,但每5分钟采样一次精度不够高,每分钟采样一次则上传次数又太多了点。最好的方法还是使用tsar这样的工具本地高频(如每1分钟)采样,然后再定时将5分钟的均值上传到cosm绘图。
Tsar是淘宝的一个用来收集服务器系统和应用信息的采集报告工具,如收集服务器的系统信息(cpu,mem等),以及应用数据(nginx、swift等),收集到的数据存储在服务器磁盘上,可以随时查询历史信息,也可以将数据发送到nagios报警。Tsar能够比较方便的增加模块,只需要按照tsar的要求编写数据的采集函数和展现函数,就可以把自定义的模块加入到tsar中。
更新
[2013-04-14] mod_rpi已经被合并到了主干代码:https://github.com/alibaba/tsar/blob/master/modules/mod_rpi.c 只需要增加文件:/etc/tsar/conf.d/rpi.conf,内容为以下即可开始使用mod_rpi模块:
mod_rpi on
####add it to tsar default output
output_stdio_mod mod_rpi
mod_rpi模块开发方法
首先按照安装说明,见https://github.com/alibaba/tsar将tsar和tsardevel安装好。