在树莓派上使用Phantomjs自动登录微博

使用过新浪开放平台的朋友都知道用户对小应用(用户数较少的)的授权Token很容易过期,自动续期要求授权过的用户在过期前重新打开授权页。如果你想实现一个自动备份自己微博的App,就不得每天(周)自己去访问授权页(想死的心都有了吧?)。这里介绍一种通过脚本自动登录微博获取最新oAuth token的方法(需要微博登录名和密码),合适自己玩。将脚本部署在树莓派上后,我再也不用每周都去登录一次授权页了,只是收到报警消息后(经常是帐号被冻结了)需要手动处理一下。 Phantomjs Phantomjs 是一个开源的,没有界面可运行在命令行,跨平台,基于WebKit的全功能浏览器,可以用来做网站自动化测试。从源代码编译比较费时间,可以直接下载二进制版本,树莓派的版本在这里可下载。Phantomjs下载好了后就一个可执行文件,依赖非常少,我很喜欢这种方式。 代码 以下代码使用提供的微博用户名和密码登录,获得Token后还会打开微博首页看帐号是否被冻结了。 var page = require('webpage').create(), system = require('system'), fs = require('fs'), address; var weibo_userid = system.args[1] var weibo_passwd = system.args[2] var startUrl = "https://api.weibo.com/oauth2/authorize?client_id=<your_app_key>&redirect_uri=<your_return_url>/&response_type=token"; var verify_weibo_freeze = false; page.onResourceReceived = function (res,network) { if (res.stage == "end") { // console.log("\t<-" + res.url); if (res.url.indexOf("authorize?client_id")>0) { startUrl = res.url } if (res.url.indexOf("?access_token")>0) { var pos1 = res.url.indexOf("access_token=") var pos2 = res.url.indexOf("&") var access_token = res.url.substring(pos1+"access_token=".length, pos2) console. [Read More]

在Mac上使用Sublime 3写Go代码

Sublime 是一个相当好用的文本编辑器,界面简洁,功能强大。最近Sublime 3 Beta 出来了, 体验了一下,发现启动速度比之前快了很多。 下载安装 下载地址: http://www.sublimetext.com/3 安装Package Control Sublime 支持插件来丰富其功能,package control 本身也是一个插件,可以用来管理其他插件,所以我们要先安装Package Control,Sublime 3需要安装Pacakge Control Alpha. cd "Library/Application Support/Sublime Text 3" cd Packages/ git clone https://github.com/wbond/sublime_package_control.git "Package Control" cd "Package Control" git checkout python3 安装GoSublime 重启Sublime后 按cmd+shift+p (OS X)或press ctrl+shift+p (Windows, Linux) 在弹出的输入框中输入 PacInstall 选择 Package Control: Install Package 在稍后弹出的输入框内输入"Gosublime",选择安装 好了,就这样可以开始写Go代码了。 Tips 我的Sublime配置 { "font_face": "Microsoft YaHei", "font_options": [ ], "font_size": 18.0, "line_padding_top": 0 } 按快捷键Shift + Command + L可以按列编辑 [Read More]

替换树莓派的U盘

除了SD卡上的存储,树莓派还可以使用U盘来做存储,有时候我们可能需要替换已有的U盘为更大容量的。在Mac上可以采用下面的方法:

  1. 备份已有的U盘,把U盘从树莓派上拔下来插在Mac上,找出U盘对应的盘符(下例为/dev/disk2

    20:51:19 hugozhu-mac-mini ~ $ diskutil list
    /dev/disk0
       #:                       TYPE NAME                    SIZE       IDENTIFIER
       0:      GUID_partition_scheme                        *500.1 GB   disk0
       1:                        EFI                         209.7 MB   disk0s1
       2:                  Apple_HFS Macintosh HD            499.2 GB   disk0s2
       3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
    /dev/disk2
       #:                       TYPE NAME                    SIZE       IDENTIFIER
       0:     FDisk_partition_scheme                        *2.1 GB     disk2
    

    使用 dd 命令把U盘拷贝到raspberrypi.img

    sudo dd if=/dev/disk2 of=raspberrypi.img conv=notrunc
    
  2. 从Mac上取下旧U盘,把新的U盘插入同一个USB口,注意新U盘容量要大于旧的

    sudo dd of=/dev/disk2 if=raspberrypi.img conv=notrunc
    
  3. 把新U盘插入树莓派,并mount上,用以下命令把U盘的多余空间用起来

    resize2fs /dev/sda
    

Java并发中正确使用volatile

前几天并发编程群里有同学对volatile的用法提出了疑问,刚好我记得Twitter有关实时搜索的这个PPT对这个问题解释的很清晰并有一个实际的应用场景,于是周末把这个问题摘录了一些和并发相关的内容如下: 并发 - 定义 悲观锁 - Pressimistic locking 一个线性在执行一个操作时持有对一个资源的独占锁。(互斥) 一般用在冲突比较可能发生的场景下 乐观锁 - Optimistic locking 尝试采用原子操作,而不需要持有锁;冲突可被检测,如果发生冲突,具有相应的重试逻辑 通常用在冲突较少发生的场景下 非阻塞算法 - Non-blocking algorithm 算法确保对线程间竞争共享资源时候,不会因为互斥而使任一线程的执行无限延迟; 无锁算法 - Lock-free algorithm 如果系统整个流程的执行是无阻塞的(系统某一部分可能被短暂阻塞),这种非阻塞算法就是无锁的。 无锁算法比传统的基于锁的算法对系统的开销更小,且更容易在多核多CPU处理器上扩展; 在实时系统中可以避免锁带来的延迟; CAS (compare and swap)或LL/SC(load linked/store conditional),以及内存屏障相关的指令经常被用在算法实现中。 无等待算法 - Wait-free algorithm 如果每个线程的执行都是无阻塞的,这种非阻塞算法就是无等待的(比无锁算法更好) Java的并发 Java的内存模型并不保证一个线程可以一直以程序执行的顺序看到另一个线程对变量的修改,除非两个线程都跨越了同一个内存屏障。(Safe publication) Java内存模型 代码顺序规则 一个线程内的每个动作 happens-before 同一个线程内在代码顺序上在其后的所有动作 volatile变量规则 对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入 传递性 如果A happens-before B, B happens-before C,那 A happens-before C Safe publication案例 class VolatileExample { int x = 0; volatile int b = 0; private void write() { x = 5; b = 1; } private void read() { int dummy = b; while (x! [Read More]

在Android上使用tcpdump

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. [Read More]

使用Ping来检查网络连通性

树莓派使用了一个无线网卡连接家里的无线路由器,在实际使用过程中发现连续运行多天后会掉线,而且掉线后基本上就再也连不上网了,需要重启树莓派才能恢复,十分麻烦。 假设无线路由器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. [Read More]

树莓派的GPIO接口输出电流限制

树莓派提供了一个连接头让我们访问CPU的17个GPIO接口,如下图

这些接口可配置成输入或输出。本文主要讨论GPIO引脚作为输出时电流的限制。

阻抗 (impendance)

阻抗和和电阻的区别(resistance)在于电阻的阻值是固定的,不会随着电流变化,阻抗则不然,可能随着外部变化,如电流或频率变化。从另一个角度来说,电阻是线性的,但阻抗不是。比如放大器的阻抗会随着输出的信号频率变化。

树莓派的的每个GPIO引脚都有一个寄存器可以设置引脚的驱动强度,也就是在保持输出电压为逻辑0和1的情况下,可以改变阻抗的大小从而改变GPIO引脚的输出电流大小。

通过如下电路测量相同电流下不同阻抗对应的GPIO电压输出(其中用到了一个电位器调节电流保持恒定):

通过计算后,下表是当输出电流为2,4 … 16mA时,对应的阻抗大小以及如果发生短路时的短路电流大小。

可以看出短路电流都是超过16mA的。

一个发光二极管压降约为1.52.0v,工作电流为310v

GPIO引脚的电流是通过板上的3.3V电压调整器输出的,树莓派是按平均每个引脚3mA来设计的,所以总的电流不能超过17 * 3 = 51mA。

结论

树莓派引脚电流大小的限制是:每个引脚最大输出电流为16毫安(mA),且同一时刻所有引脚的总输出电流不超过51毫安

参考链接

  1. http://www.thebox.myzen.co.uk/Raspberry/Understanding_Outputs.html

使用8位移位寄存器74HC595扩展树莓派的IO端口

树莓派的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 控制流程 如果要在8个引脚输出01010101 [Read More]

树莓派网站容灾:利用DNSPod,Google App Engine和Github

背景介绍 把网站托管在树莓派上后如果家里停电或是宽带故障,会造成网站中断。本文提供一个免费的解决方案(前提是你需要有自己的一个域名,并由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: curl -k https://dnsapi.cn/Record.List -d "login_email=xxx&login_password=xxx&domain_id=xxx" Google App Engine 切换DNS脚本 package dnspod import ( "io/ioutil" "net/http" "net/url" "strings" ) const ( login_email = "<your_login_email>" login_password = "<your_login_password>" format = "json" domain_id = "<domain_id>" record_id = "<record_id>" sub_domain = "<your_subdomain>" record_type = "CNAME" record_line = "默认" ttl = "600" ) func Update(client *http.Client, cname string) string { body := url. [Read More]

使用Goroutine和Channel实现按键超时交互

背景介绍 前面的文章(见参考链接)已经介绍了如何使用按键作为树莓派的输入。在实际应用中可以通过按下按键循环显示预先设定的脚本输出到显示屏幕,需求如下: 如果按键不被触动,则定时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. [Read More]