Synology PhotoStation性能优化

本文的内容已经过时,6.7.0以上的Photo Station已经没有严重的性能问题了。

我折腾过不少家用NAS方案,包括最早的WD My Book World Edition,后来的Joggler,再到后来的Raspberry Pi,这些方案除了性能存在一些问题以外,最大的缺点就是易用性存在问题,不但非“专业”人士用起来存在困难,就连我自己也对土法泡制的照片管理功能感到不满。直到两年前入了群晖(Synology)的家用NAS DS214play,事情才变得安逸起来。

平心而论,Synology的系统虽然功能强大,体验也还不错,但细节上做得其实真是挺糙的。最近就发现了它的照片管理软件PhotoStation出现了严重的性能问题,在照片库里只存了8万余张照片的情况下,打开首页要花费的时间已经超过了20秒,每打开一个文件夹都需要等待10秒以上,几乎不可用了。

以下分析以DSM 6.0为例,PhotoStation版本为6.4-3166。PhotoStation的安装路径为/volume1/@appstore/PhotoStation。

Synology的DSM系统,后台使用PostgreSQL数据库,前端是PHP页面。简单推理一下就可以知道,PhotoStation的性能瓶颈主要是在对照片索引数据库的访问上。性能调优的第一步就是先要找到哪些SQL查询占用了太多的时间。打开PostgreSQL记录SQL查询的开关,并查看所有SQL执行情况:

$ sudo su postgres
$ vi ~/postgresql.conf
log_statement = 'all'
$ psql photo postgres
# SELECT pg_reload_conf();
# \q
$ exit
$ sudo tail -f /var/log/postgresql.log

通过查看SQL执行记录,很容易发现几个明显的慢查询:

1.

SELECT COUNT(*) as total FROM photo_image; 
SELECT COUNT(*) as total FROM video;

2.

SELECT COUNT(logid), MAX(logid) FROM photo_log;

3.

SELECT * FROM (
    SELECT path AS filename, timetaken AS takendate, 
        create_time AS createdate, 'photo' AS type
    FROM photo_image
    WHERE path LIKE '/volume1/photo/%'
        AND path NOT LIKE '/volume1/photo/%/%' AND disabled = 'f'
    UNION
    SELECT path AS filename, mdate AS takendate,
        date AS createdate, 'video' AS type
    FROM video
    WHERE path LIKE '/volume1/photo/%'
        AND path NOT LIKE '/volume1/photo/%/%'
        AND disabled = 'f'
    ) AS totalCount; 

4.

SELECT path, resolutionx, resolutiony, version FROM (
    SELECT path, resolutionx, resolutiony, version,
        create_time, privilege_shareid, disabled
    FROM photo_image WHERE privilege_shareid IN (
        SELECT shareid FROM photo_share WHERE ref_shareid = (
            SELECT shareid FROM photo_share WHERE sharename = '2016'))
    AND disabled = 'f'
    UNION ALL
    SELECT path, resolutionx, resolutiony, 0 AS version, 
        date AS create_time, privilege_shareid, disabled
    FROM video WHERE privilege_shareid IN (
        SELECT shareid FROM photo_share WHERE ref_shareid = (
            SELECT shareid FROM photo_share WHERE sharename = '2016'))
    AND disabled = 'f') temp
ORDER BY create_time DESC LIMIT 1; 

优化的思路很简单,由于PhotoStation在正常情况下访问数据库所需要的读性能是远远大于写性能的,所以就通过牺牲写性能来逐一击破上面这些慢查询:

1. 程序的目的就是想知道系统中有多少张照片和多少个视频(而且其实并不需要精确知道,差不多就行),可惜对于PostgreSQL来说,由于它采用MVCC来解决并发问题,SELECT COUNT(*)是一个需要进行全表扫描的慢操作。解决方案就是用另外用一张表来存这两个表的总记录条数,并在原表上添加触发器来更新记录数。

CREATE TABLE photo_count (table_oid Oid PRIMARY KEY, count int);
ALTER TABLE photo_count OWNER TO "PhotoStation";
CREATE FUNCTION count_increment() RETURNS TRIGGER AS $_$
BEGIN
  UPDATE photo_count SET count = count + 1 WHERE table_oid = TG_RELID;
  RETURN NEW;
END $_$ LANGUAGE 'plpgsql';
CREATE FUNCTION count_decrement() RETURNS TRIGGER AS $_$
BEGIN
  UPDATE photo_count SET count = count - 1  WHERE table_oid = TG_RELID;
  RETURN NEW;
END $_$ LANGUAGE 'plpgsql';
CREATE TRIGGER photo_image_increment_trig AFTER INSERT ON photo_image 
  FOR EACH ROW EXECUTE PROCEDURE count_increment();
CREATE TRIGGER photo_image_decrement_trig AFTER DELETE ON photo_image 
  FOR EACH ROW EXECUTE PROCEDURE count_decrement();
CREATE TRIGGER video_increment_trig AFTER INSERT ON video 
  FOR EACH ROW EXECUTE PROCEDURE count_increment();
CREATE TRIGGER video_decrement_trig AFTER DELETE ON video 
  FOR EACH ROW EXECUTE PROCEDURE count_decrement();
INSERT INTO photo_count VALUES 
  ('photo_image'::regclass, (SELECT COUNT(*) FROM photo_count));
INSERT INTO photo_count VALUES 
  ('video'::regclass, (SELECT COUNT(*) FROM video));

然后在PHP程序中修改需要统计表数记录数的逻辑,在这里可以看到似乎同一个Session中只会查一次,但即使就是这一次,也已经慢得让人不开心了:

diff --git a/photo/include/file.php b/photo/include/file.php
index 541c5cb..7caa5de 100755
--- a/photo/include/file.php
+++ b/photo/include/file.php
@@ -536,8 +536,11 @@ class File {
 		if ($key && isset($_SESSION[SYNOPHOTO_ADMIN_USER][$key])) {
 			return $_SESSION[SYNOPHOTO_ADMIN_USER][$key];
 		}
-		$query = "SELECT count(*) as total FROM $table";
-
+		if ('photo_image' == $table || 'video' == $table) {
+			$query = "SELECT count as total FROM photo_count where table_oid='$table'::regclass";
+		} else {
+		    $query = "SELECT count(*) as total FROM $table";
+		}
 		$result = PHOTO_DB_Query($query);
 		if (!$result) {
 			// db query fail, won't update session value

2. 其实可以用跟前一个问题类似的方法去解决。但是这个其实只是一个没太多用处的操作日志表,所以我用更为简单粗暴的方法去解决这个问题:减少在数据库中保留日志的条数。直接修改PHP程序:

diff --git a/photo/include/log.php b/photo/include/log.php
index 1c982af..56385db 100644
--- a/photo/include/log.php
+++ b/photo/include/log.php
@@ -1,8 +1,8 @@
 <?php
 
 class PhotoLog {
-	const LIMIT = 100000;
-	const PURGECOUNT = 10000;
+	const LIMIT = 1000;
+	const PURGECOUNT = 100;
 	public static $SupportFormat = array("html", "csv");
 
 	public static function Debug($msg)

3. 第三个问题主要体现在对照片路径的处理上,为了选出位于某个路径下(不含子目录)的照片,程序采用了path LIKE ‘/path/%’ AND path NOT LIKE ‘/path/%/%’这样的查询条件。其实PostgreSQL在一定程度上是可以利用path字段上的索引来很好的优化这个查询的,但是实际运行中发现(通过在PostgreSQL的客户端中用explain和explain analyze分析查询)在某些情况下索引会失效,造成非常差的查询性能。解决方案还是用写性能来换读性能,先在表上加一个dirname字段并建立索引,按path算好文件所在的目录名写入dirname,然后把查询条件改为对dirname的查询,避免使用通配符和LIKE运算即可:

ALTER TABLE photo_image ADD COLUMN dirname TEXT NOT NULL DEFAULT '';
UPDATE photo_image 
    SET dirname = LEFT(path,LENGTH(path)-STRPOS(REVERSE(path),'/')+1);
ALTER TABLE video ADD COLUMN dirname TEXT NOT NULL DEFAULT '';
UPDATE video SET dirname = LEFT(path,LENGTH(path)-STRPOS(REVERSE(path),'/')+1);
CREATE INDEX dirname_index ON photo_image USING btree(dirname);
CREATE INDEX dirname_index ON video USING btree(dirname);
CREATE OR REPLACE FUNCTION set_dirname()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
BEGIN
  NEW.dirname := LEFT(NEW.path,LENGTH(NEW.path)-STRPOS(REVERSE(NEW.path),'/')+1);
  RETURN NEW;
END $function$
CREATE TRIGGER set_dirname_trigger 
BEFORE INSERT OR UPDATE ON photo_image 
FOR EACH ROW 
  EXECUTE PROCEDURE set_dirname();
CREATE TRIGGER set_dirname_trigger 
BEFORE INSERT OR UPDATE ON video 
FOR EACH ROW 
  EXECUTE PROCEDURE set_dirname();

同时修改PHP程序:

diff --git a/photo/include/photo/synophoto_csPhotoDB.php b/photo/include/photo/synophoto_csPhotoDB.php
index ac8f932..43e58ee 100755
--- a/photo/include/photo/synophoto_csPhotoDB.php
+++ b/photo/include/photo/synophoto_csPhotoDB.php
@@ -1607,10 +1607,8 @@ class csSYNOPhotoDB {
 		} else {
 			$albumRealPath = self::EscapeLikeParam(SYNOPHOTO_SERVICE_REAL_DIR_PATH."{$albumName}/");
 		}
-		$cond = "path LIKE ? {$this->escapeStr} AND path NOT LIKE ? {$this->escapeStr} AND disabled = 'f' ";
-		array_push($pathSqlParam, "{$albumRealPath}%");
-		array_push($pathSqlParam, "{$albumRealPath}%/%");
-
+		$cond = "dirname = ? {$this->escapeStr} AND disabled = 'f' ";
+		array_push($pathSqlParam, "{$albumRealPath}");
 		if (!$removePhoto) {
 			$photoQuery = "SELECT path as filename, timetaken as takendate, create_time as createdate, 'photo' as type
 FROM photo_image WHERE $cond";
 			$sqlParam = array_merge($sqlParam, $pathSqlParam);

4. 这个查询只是为了查询一个目录及其所有子目录中最新一个照片或视频,用其缩略图来作为目录的封面图片。群晖的工程师自己也知道这个查询很慢,所以还在程序中加了个逻辑,当照片视频数量大于200000时,放弃按日期排序,直接随机选一张。然而,这个查询实际上是可以简单优化的,明明不需要把所有的照片视频UNION到一起后再找出最新的一个,可以直接分别找出最新的照片和最新的视频,然后再到这两个中去取一个相对更新的就可以了。直接修改PHP代码实现:

diff --git a/photo/include/photo/synophoto_csPhotoAlbum.php b/photo/include/photo/synophoto_csPhotoAlbum.php
index ca128f0..f0e57e7 100755
--- a/photo/include/photo/synophoto_csPhotoAlbum.php
+++ b/photo/include/photo/synophoto_csPhotoAlbum.php
@@ -145,9 +145,11 @@ class csSYNOPhotoAlbum {
 		$cond .= " AND disabled = 'f'";
 
 		$table = "(" .
-				"SELECT path, resolutionx, resolutiony, version, create_time, privilege_shareid, disabled FROM pho
to_image WHERE $cond " .
+				"(SELECT path, resolutionx, resolutiony, version, create_time, privilege_shareid, disabled FRO
M photo_image WHERE $cond " .
+				"ORDER BY create_time DESC LIMIT 1)" .
 				"UNION ALL " .
-				"SELECT path, resolutionx, resolutiony, 0 as version, date as create_time, privilege_shareid, disa
bled FROM video WHERE $cond " .
+				"(SELECT path, resolutionx, resolutiony, 0 as version, date as create_time, privilege_shareid,
 disabled FROM video WHERE $cond " .
+				"ORDER BY create_time DESC LIMIT 1)" .
 		") temp";
 
 		// this may cost lots of time, so it won't sort by create_time if the total count exceeds the threshold (200,000)

做完以上优化,我的PhotoStation已经基本可以做到点进文件夹秒开了,至少我自己已经比较满意了。声明一下,其实我并不太懂数据库相关理论和技术,以上“优化”只能说是在我自己的实验中起到了优化的效果,也许其中一些并不太科学,希望这篇文章能起到抛砖引玉的作用。

重新安装PhotoStation或升级DSM系统会造成我们对程序所作的修改丢失,所以在修改完成后,务必做好备份。

另外,适时对PostgreSQL数据库进行VACUUM操作似乎可以起到提高访问性能的目的,尤其是在做过大量照片更新后。

解密GW-BASIC的加密文件

终于解密了一份1993年左右的BASIC代码,这么多年一直想看这份代码的内容,现在终于看到了,颇有一些唏嘘之感。

目前网上似乎搜不到中文资料介绍如何解密加密过的BASIC代码,我总结一下放在这里。聪明人可以直接跳到“解密方法二”阅读。

背景:

DOS下的GW-BASIC在保存代码时,可以存成tokenized、纯文本和加密三种格式,分别对应SAVE命令的默认参数、“,a”和“,p”参数。

对于用了,p参数保存的源代码,以后就只能LOAD到内存中执行,而不允许再LIST查看源代码了。本文介绍的就是用于解密查看,p参数保存的源代码的方法。

解密方法一:

参考GW-BASIC tokenised program format

原理是找到GW-BASIC中标记代码是否是加密的那个内存地址,然后用VAL命令的一个溢出Bug,修改这个内存地址的值。

第一步,运行一下如下的程序,找到加密标记的地址:

FOR I=1000 TO 16000:PRINT I: J=PEEK(I): POKE I,((J=0)AND 255) OR J: POKE I,J:NEXT I

这个程序会导致Illegal function call错误,记下出错前程序打印出来的数字。

第二步,正常用LOAD命令加载要加密的.BAS文件。

第三步,输入下面的程序,并把其中的a%[9]的值”1450″改为第一步中记录下来的数字。

dim a%[14]
a%[0]=0:a%[1]=&h2020:a%[2]=&h2020:a%[3]=&h2097
a%[4]=&h4553:a%[5]=&h2047:a%[6]=&H203A:a%[7]=&H2098
a%[8]=&H1C20:a%[9]=1450:a%[10]=&h112C
a%[11]=&h903A:a%[12]=0
b$=""
b$="123"+chr$(28)+":::"+chr$(137)+chr$(13)+mki$(varptr(a%[0]))+":"
print val(b$) 456

完成,现在已经可以用LIST命令正常列出解密后的代码了。

解密方法二:

当我还沉浸在成功解密了BAS文件的喜悦中时,无心的一次搜索让我又找到了更简单的解密方法

创建一个只有两个字节的UNPROT.BAS文件,这两个字节是0xFF 0x0A。

先LOAD要解密的.BAS,然后再LOAD一下这个UNPROT.BAS,然后就解密成功了。

如果是在DOS下要创建这么个文件还真有点麻烦,比较简单的做法是用DEBUG:

C:\>debug
-e 0100 ff 1a
-rcx
CX 0000
:0002
-n unprot.bas
-w
Writing 00002 bytes
-q

在Linux下使用“360随身WiFi 2”

某人说“360随身WiFi”价格还算良心,我也认同。昨天无意中看到2代开售,就随手撸了一个。当然,为了免邮费,不得不买了点别的东西凑单,于是还买了本价格是这个“360随身WiFi 2”近两倍《C语言点滴》回来看看。

到货,插到电脑上,Linux下没反应。Ralink的无线网卡系统不自带驱动我不惊呆,于是lsusb看了下。

Bus 001 Device 006: ID 148f:760b Ralink Technology, Corp.

好嘛,二代换芯片了,原来是RT5370的,现在换成不认识的了。不怕,把148f:760b放狗搜一下。不过搜完了就怕了,因为结果是0个。

打算先用Windows确认一下设备是好的,结果装了360官网的驱动后发现设备完全识别不出来……这可真是个大乌龙。到360官网的歪粉交流论坛上看看,有类似问题的看来不是个案。换了论坛上公布的新版本的驱动后Windows下工作正常。(截止我写这篇文章的时候,官网上的驱动已经更新成新的了,文件大小为10797000字节,论坛上讨论说设备识别不出来的那些贴子貌似也都直接消失了。)

继续回到Linux下折腾,可是不知道芯片是什么还是为难。从Ralink网站(现在叫Mediatek)上瞎找了几个Linux驱动,里面也没有符合760b这个idProduct的,抓瞎。

用百度搜了一下148f:760b,结果找到了360论坛上一篇新觧出炉的贴子,确认了芯片是MT7601。

剩下的事就简单了,在Ralink网站下载MT7601的Linux驱动,修改common/rtusb_dev_id.c文件,在

{USB_DEVICE(0x148f,0x7601)}, /* MT 6370 */

下面加一行

{USB_DEVICE(0x148f,0x760b)}, /* 360 Wifi */

按照README_STA_usb中的说明make和make install。然后modprobe一下mt7601Usta.ko这个内核模块,后面的事就妥妥的了。

还有个遗留问题,连不上WPA2 Enterprise的无线网络,暂时不管了,我对这个需求不强烈。

啥?这文章只说了怎么驱动这个网卡没说怎么在Linux实现AP的功能?哦,我本来也没打算用它在Linux下做AP来着。有兴趣的话可以试试hostapd/dnsmasq/iptables这老三样吧,我不知道能不能行,如果哪位朋友弄成了麻烦汇报一下,我很想学习学习,多谢了~

2013-10-12更新:MT7601的Linux驱动中似乎没有实现nl80211的接口,所以hostapd没法直接用。不知道还有什么办法能实现AP的功能,如有朋友知道,希望能不吝指教。

2014-08-12更新:留言区中的轩辕志瑜同学找到了一个支持AP模式的驱动,详细的信息请查看相关的文章:http://blog.csdn.net/sumang_87/article/details/38168877,github上的源代码:https://github.com/eywalink/mt7601u。感谢他的分享。

2015-03-12更新:如果是在Raspberry Pi上尝试编译驱动并且遇到困难,请参考《为Raspberry Pi 2编译内核模块

《爱上Raspberry Pi》诞生记

我的第一本译著《爱上Raspberry Pi》上个月底终于正式出版销售了。从开始有译书的想法到拿到成品书,正好5个月时间。我的Blog也正好长了这5个月的草,不过这之间没有因果关系。

《爱上Raspberry Pi》封面

《爱上Raspberry Pi》封面

今年上半年,闲暇的时间比较多,就玩了玩Raspberry Pi和Arduino,兴趣正浓时,看到论坛上有出版社的编辑在征集Raspberry Pi相关图书的译者,于是就与编辑联系了一下。那时正好看了几本Pi的英文书,第一次感觉到原来老外们也出了很多烂书,在看过的几本书中,Getting Started with Raspberry Pi这本书是那个时候内容相对说得过去的一本。由于编辑手头有几本书都已经找到译者,我就随口问了问这一本,没想到这位编辑正好在跟O’Reilly谈这本书的引进合同,真可谓无巧不成书了。

为了争取到翻译这本书的资格,我主动根据手头的电子书(盗版的,惭愧)先试译了一章,发给了编辑。于是,很顺利的,3月20日,我就与出版社签定了“委托翻译合同”。

“委托翻译合同”从名字就可以看出,译者的事就是翻译,翻完了就没你什么事了。稿费按不含空格的字数计算,每千字稿费低到难以想象。译稿的版权出版社以稿费的形式直接买断,译者只有属名权,出版后的书卖多卖少跟译者都无关了。出版社违约的话,只需部分支付稿费。现在大环境就是这个样子,也没啥办法。

4月15日,通过电子邮件联系了原书作者Matt Richardson,请他给写了个中文版序。后来才知道,出版社好像比较忌讳译者与作者直接联系,不过那时无知者无畏,反正中文版序是到手了。

合同交稿时间是4月20日,不过由于出版社到4月19日才拿到O’Reilly给的英文样书,而我又想根据英文纸书对译稿进行正式的审校,所以虽然那时翻译已经完成了,实际交稿的时间还是协商延迟到了5月15日,期间我自己把译稿通读了七八次,并请好友帮忙审阅了几轮。幸亏书不厚,才有机会这么细致的审核,不过看到最后,真是想吐了。

6月5日,出版社提供了打印出来的初校样,由译者进行审阅。实话说,这个初校样让我大跌眼镜,上面有各种排版错误,甚至还会出现原译稿中没有的错别字。校了3天,200多页的书中找出了100多处错误,其中属于原译稿错误的不到10处。

6月22日,我在家里的Raspberry Pi上用gor这个用Golang写的Blog系统搭建了一个小网站,用作本书的支持网站,提供勘误表和电路图,域名是http://rpi.freemindworld.com。不过后来为了保证网站稳定性,迁移到我的Linode上去了。Raspberry Pi上的那个Web服务换了个了域名,同步运行,域名改为http://rpirpi.freemindworld.com

7月中旬,进入三校阶段,遗憾的是三校的版本中依然是错误多多。咬着牙把三校版又仔细看了几遍,改了六七十处错误。8月上旬,核红稿校对中又发现了少量问题,不过总算错误数开始收敛了。

7月18日,出版社领到了书号:ISBN 978-7-03-038196-5。有了书号,我就在豆瓣上创建了相关的图书页面。

8月8日,又仔细把最后一个版本的核红稿给看了一遍,这次问题终于比较少了,修改了几个不痛不痒的小问题后,终于在8月8日下午进厂印刷。

8月19日,出版社拿到了印刷厂的校正本样书。8月25日,我拿到了第一本校正本样书,劳动终于结成了果实。

销售方面,各主要B2C电商中,8月23日,当当网第一个上架了此书,不过一直处于缺货状态,直到9月5日。其次是亚马逊中国,但很长一段时间上面只有第三方卖家出售,也是到9月初才开始有自营的。最早有现货可以正常发货的是北发图书网,而且还是所有B2C电商中售价最便宜的。

中文版图书的封面,最终是由O’Reilly来设计的,显然采用了与原书和这一系列书中文版一样的公版设计。7月底给了第一稿,按我的要求微调了一下以后,8月5日定稿。

总结一下出版过程,作为译者来说:与出版社编辑联系,确定翻译意向,试译,签定翻译合同,翻译,交稿,审校样,拿样书。

身边也有不少朋友表示对出书有兴趣,其实现在作译者与编辑那里存在一个信息不对称的问题。编辑们手上有很多书或选题找不到人来翻译或原创,而有心出书的人则不知道应该怎么去跟编辑联系。所以与编辑联系是整件事情的第一步,其实借助搜索引擎或微博,这件事情并不难完成。同时,与编辑之间建立友好、信任、合作的关系,会对整本书的出版过程起到重要推动作用。

最后帮我的编辑做个广告,目前还有几本有关Raspberry Pi的书在寻找合适的译者。书都不厚,如果想体验一下出书的乐趣和痛苦的朋友,可以通过我与编辑取得联系。 这几本书主要是Step-by-step的教一些Raspberry Pi的入门操作知识,技术含金量不高,但翻译起来会比较轻松。

打造增强型Raspberry Pi-红外遥控篇

上一期介绍了怎么给Raspberry Pi加上一个液晶屏,这期该介绍红外遥控了。

需要的硬件:

  • 一个红外接收管,型号可以是TSOP1238/TSOP2238等可以3.3V电压下工作的38KHz红外接收管。
  • 一个红外遥控器,可以是电视机、机顶盒等的遥控器。但别拿空调遥控器这种自带状态逻辑的遥控器来折磨自己。

连接红外接收管:

不同的红外接收管连线可能不太一样,需要参考相应的Datasheet。以TSOP 1238为例的话,当红外线接收窗朝向自己时,从左到右三个引脚分别为:

1. GND,接地,RPi PIN 6
2. VS,电源,接3.3V电源,RPi PIN 17
3. OUT,数据输出,接GPIO 24,RPi PIN 18

由于Raspberry Pi的GPIO只能接收3.3V的输入,所以红外接收管的电源务必要接3.3V的供电。我自己用的红外接收管的型号是TSOP 1838(引脚顺序是OUT, GND, VS),按lirc_rpi项目页面的说法,这个管子应该在5V供电下才能正常工作,不过我还是只接了3.3V,发现也能用。

系统软件安装:

lirc_rpi项目为Linux内核提供了支持GPIO口的红外接收管的驱动,在最新的Raspbian系统中应该已经包含。如果你的系统中没有这个模块,也许需要自己重新编译内核。具体可以参考lirc_rpi项目主页。

然后需要安装用户态的服务进程:

sudo apt-get install lirc

加载lirc_rpi内核模块:

sudo modprobe lirc_rpi gpio_in_pin=24 gpio_out_pin=23

注意,在加载lirc_rpi时必须指定输入端口是GPIO 24,跟实际接线一致。输出端口设为GPIO 23,是因为我不需要这个输出端口,而目前我们GPIO 23口是空着的。

测试红外接收是否正常:

sudo mode2 -d /dev/lirc0

如果按遥控器上的键,屏幕上能显示出一串pluse和space值的话,多半就是正常了。

配置下/etc/lirc/hardware.conf:

...
LIRCD_ARGS="--uinput"
...
DRIVER="default"
...
DEVICE="/dev/lirc0"

让lirc学习一下你遥控器上的按键,在我的例子中,请至少学习5个按键,分别做为上(up)、下(down)、左(left)、右(right)、选择(sel):

sudo /etc/init.d/lirc stop
#学习按键
irrecord -n -d /dev/lirc0 ~/lircd.conf
#把学习后生成的配置文件作为lircd的配置文件
sudo mv ~/lircd.conf /etc/lirc/lircd.conf
sudo /etc/init.d./lirc start

配置完后,可以用irw命令测试遥控器是否工作正常。

配置~/.lircrc.conf文件,把按键与需要触发的行为进行关联:

begin
    prog = lcdmenu 
    button = up
    config = up
end
begin
    prog = lcdmenu
    button = right
    config = right
end
begin
    prog = lcdmenu
    button = left
    config = left
end
begin
    prog = lcdmenu
    button = down
    config = down
end
begin
    prog = lcdmenu
    button = sel
    config = sel
end

为Raspberry Pi添加液晶屏控制菜单,我参考了Github上的RaspberryPiLcdMenu,这个项目实现了使用一个带五个小按钮的液晶屏套件作为硬件基础来显示与操作一个菜单的系统,我没有这个套件,而且我是用红外遥控器而不是小按钮来操作,所以需要重写跟按钮有关的代码,通过lirc提供的接口去获取遥控器的按键信息,很容易,可以参考我已经实现好的代码,在Buttons.py中:

git clone https://github.com/lifanxi/rpimenu.git

RaspberryPiLcdMenu项目提供了一个很灵活的菜单配置系统可以很方便的添加新的菜单项,我给它添加了遥控fmd播放豆瓣FM的菜单功能项(参考lcdmenu.xml)。

一切就绪,启动lcdmenu.py程序:

sudo python lcdmenu.py

如果你的配置没有问题,这时就可以在液晶屏上看到定制后的菜单,并可以用遥控器来遥控操作了,按上下键选项不同的菜单项,向右键进入子菜单,向左退回上一级菜单,选择键用于根据屏幕提示确定某些特定的操作。

添加了Douban.fm的菜单

添加了Douban.fm的菜单

豆瓣FM播放中,可以显示曲名和播放进度

豆瓣FM播放中,可以滚动显示曲名和播放进度

一切调试完成后,您还可以把加载lirc_rpi模块和启动lcdmenu.py的命令加到Raspberry Pi的启动脚本中,这样系统一启动就可以让液晶显示和菜单自动生效,菜单操作中已经预设了关机、重启、设置IP地址等功能,这对于headless使用Raspberry Pi的同学来说,是一件非常方便的事情。

参考资料:

  1. 红外遥控器lirc配置
  2. RaspberryPiLcdMenu
  3. lirc_rpi项目
  4. 用Raspberry Pi打造真正的“豆瓣FM”

打造增强型Raspberry Pi-液晶屏篇

对我来说,Raspberry Pi最吸引人的地方不是它的体积、功耗、性能之类的,而是它有两排很好用的接口,可以把软件和一些简单的硬件结合起来做点好玩的东西。对于学过做过单片机的同学来说,这些东西太小儿科了,可是对我来说还是一件非常新鲜的事情,在这里分享一下心得。

目标:为Raspberry Pi加上一个简易的液晶显示屏,可以用来显示Raspberry Pi的一些状态信息,也可以显示一个菜单,用红外线遥控器去操作菜单指挥Raspberry Pi执行相关的操作。

警告:任何时候,电流的速度都比你的反应要快,所以,在实验过程中,不正确的接线、不正确的程序、意外的短路、过载等都可能在瞬间烧掉你的Raspberry Pi或其它外围的硬件。Raspberry Pi的GPIO接口全都是没有保护电路的,一旦短路或输入过高电压,主芯片会马上被烧掉,基本上没有维修的可能性。

需要的硬件:

  • 一块面包板和若干连接线,用于组装电路。连接RPi与面板的线是母头转公头的,面包板上的连接线是两个公头的
  • 一块LCD1602液晶显示屏。如果你不会焊接,那就买一块预先焊好针脚的
  • 10K电位器一个
所需硬件

所需材料(其中红外接收管和遥控器这次用不上,下回要用)

面包板的使用方法:

可以把元件或接线直接插在面包板上连通电路,无需焊接。面包板上每一行的一组五个插孔之间是相互连通的,同一行上如果有两组五个插孔,则两组之间是断开的,适合插接集成电路或开关。面包板上行与行之间都是断开的。面包板两侧的如果有供电线插孔,则同一条供电线上的所有插孔都是连通的。

连接液晶屏:

LCD1602液晶屏提供了16列x2行的ASCII字符显示能力,工作电压5V,提供4位数据与8位数据两种工作模式,Raspberry Pi的GPIO口数量很有限,所以我们使用4位数据模式。

LCD1602液晶屏模块提供了16个引脚,我们只需接其中的12个即可:

1. VSS,接地,RPi PIN 6
2. VDD,接5V电源,PRi PIN 2
3. VO,液晶对比度调节,接电位器中间的引脚
4. RS,寄存器选择,接GPIO 14,RPi PIN 8
5. RW,读写选择,接地,表示写模式,PRi PIN 6
6. EN,使能信号,接GPIO 15,RPi PIN 10
7. D0,数据位0,4位工作模式下不用,不接
8. D1,数据位1,4位工作模式下不用,不接
9. D2,数据位2,4位工作模式下不用,不接
10. D3,数据位3,4位工作模式下不用,不接
11. D4,数据位4,接GPIO 17,RPi PIN 11
12. D5,数据位5,接GPIO 18,RPi PIN 12
13. D6,数据位6,接GPIO 27,RPi PIN 13
14. D7,数据位7,接GPIO 22,RPi PIN 15
15. A,液晶屏背光+,接5V,RPi PIN 2
16. K,液晶屏背光-,接地,RPi PIN 6

注意:

  1. 请注意GIPO引脚编号与Raspberry Pi上P1扩展口的PIN编号的区别,别搞混了。
  2. 如果你的液晶屏没有背光,则15,16脚可能不会引出,也就不用接线。
  3. LCD1602工作电压是5V,而RPi的GPIO口工作电压是3.3V,所以RW脚请确保接地进入写模式,否则尝试从LCD1602读取数据可能会烧掉RPi。
  4. VO脚接的是电位器,电位器的另外两个脚分别接5V和地。通过调节这个电位器,可以调节液晶屏的对比度。
  5. Raspberry Pi有Rev 1和Rev 2两个版本,它们对于PIN 13的定义是不同的。市面上现在大部分都是Rev 2版本,PIN 13对应GPIO 27。如果你的RPi是老的Rev 1版本,PIN 13对应是GPIO 21,你需要调整程序中的参数,把27改为21。

获取程序

git clone https://github.com/lifanxi/rpimenu.git

测试液晶屏:

Raspberry Pi通电后,正常情况下液晶屏会初始化。上面一行显示黑色方块,下面一行空白。如果屏幕一片空白或一片黑,可以尝试调节一下电位器,看看对比度是否合适。

直接以root权限运行程序包中的Adafruit_CharLCD.py,LCD上会显示两行字符:LCD 1602 Test, 123456789ABCDEF,这样就说明液晶屏已经工作正常了。

如果你想让液晶屏显示些别的东西,可以参考lcdmenu.py代码和Adafruit_CharLCD.py中所暴露的接口。

LCD1602液晶屏测试

LCD1602液晶屏测试(右上角的红棕两根线是预留给红外接收管的,目前插在那里相当于悬空,没用)

下期预告:

下期会介绍如何把红外线接收管接上去,并配置LIRC去接收遥控器的信号,从而实现用遥控器控制Raspberry Pi的目的。

参考资料:

  1. 面包板的使用
  2. Drive a 16×2 LCD with the Raspberry Pi
  3. Raspberry Pi GPIO接口信息

题外话:

细心的同学在上图中可以发现我的Raspberry Pi的电源口附近有一个体积和样子都很违和的电解电容。这里原本是一个给电源滤波用的贴片电解电容C6(15V, 220μF),但是我在给Raspberry Pi接线时,轻轻的在上面借了一点力,它就掉下来了……

根据Raspberry Pi官网Wiki的说法,C6掉了是常见病。只要你的电源质量不非常差,C6有没有都无所谓。所以,如果你的C6也掉了,除非你是焊接熟练工,或者跟我一样手贱,那可以找个相似型号的电容焊上去(电解电容,一定要注意极性),不然的话,不去管它是一个更好的选择。

用Raspberry Pi打造真正的“豆瓣FM”

想用床头的收音机收听豆瓣FM吗?如果你有一台Raspberry Pi,这个愿望就可以满足。

Raspberry Pi,中文名树莓派,是一台廉价的单板机,ARM架构,可以运行Linux操作系统,它由英国的树莓派基金会开发,目的是以低价硬件及自由软件促进校园中的计算机科学教育。是的,它设计初衷其实是给小朋友们学电脑用的,不是给你们这些Geek来折腾玩的。

用Raspberry Pi搭建一个豆瓣FM的调频广播台其实并不是一件很困难的事情,为实现这个目标,要解决的两个问题是:

  • 从豆瓣FM网站上下载MP3音乐码流并解码
  • 把音频信号调制成调频信号发射出去

造轮子的事情不要自己做,这两个问题其实已经都解决了:Github上的fmd项目实现了一个通过本地声卡播放豆瓣FM的服务器;这篇文章则介绍了如何写程序对音频信号进行调制并控制Raspberry Pi的GPIO端口把信号发送出去。所以剩下的事就是把它们组合起来就可以了。

组合代码这种脏活累活我也给大家做好了,可以从Github上获取。嗯,这份代码目前的质量是It just works,各位make一下就好,没事就不要vim它了……

git clone git://github.com/lifanxi/fmd.git

代码下载后在Raspberry Pi上可以直接make编译,如果缺少依赖,可能需要安装libcurl4-openssl-dev, libjson0-dev, libmpg123-dev, libao-dev这些依赖包(我用的是Raspbian发行版)。

fmd的使用方法可以参考Github上的说明,我所实现的扩展就是在fmd.conf配置文件中可以把driver配成”pifm”,然后把device配成88MHz~108MHz之间的某个指定的调频发射频率值(这是大多数国家调频收音机所支持的频率范围,如果你有别的无线电接收设备,这个值也可以设成是<250的任意值)来实现FM广播的发射。当driver配成pifm而不是默认的alsa时,fmd就会工作在FM广播模式。

注意,由于FM发射的代码需要直接mmap /dev/mem设备,所以把driver配成pifm让fmd工作在FM广播模式时,需要以root权限来执行fmd。以root权限运行fmd时,注意HOME目录可能会变成/root,所以相关配置文件也得改放到/root/.fmd中。

fmd运行起来后,按照文档的说明,可以telnet到127.0.0.1:10098上或用fmc执行play命令开始广播,把收音机(带收音功能的手机当然也可以)调到device参数所指定的频率上就可以收听了。

如果你的收音机离你的Raspberry Pi太远,收音效果不好,可以在Raspberry Pi主板上的GPIO 4口上插一根20cm左右长的杜邦线做天线,广播范围和信号强度就会大大提高了。

Raspberry Pi正在102.4 MHz上播放豆瓣FM的音乐

Raspberry Pi正在102.4 MHz上播放豆瓣FM的音乐

目前这个豆瓣FM广播台还有两个大问题需要解决:

  • 立体声调频广播:现在实现的版本是单声道的,理论上应该也可以把音频调制成立体声的。这需要一些我所不具备的调频广播的知识,待我有空再钻研钻研。
  • CPU占用率:现在的版本在做调制和发射时,对CPU的占用率比较高。如果Raspberry Pi正在忙于做别的事情的话,你可能会听到一些杂音或者是唱机/磁带机没电了的时候的音响效果。

其实吧,这样收听豆瓣FM可真是多此一举。直接把耳机插在Raspberry的耳机插孔用fmd播放不就完事了么……天行健,君子以折腾不息!

Kindle Paperwhite使用报告

从第一代Kindle出现到现在一直很关注E Ink设备,帮人买过Kindle 2, Kindle 3, Kindle 4各一台,不过自己倒是一直没有Kindle,这次Kindle Paperwhite发布,感觉有点中毒。虽然手上已经有了汉王N510、盛大Bambook,还有一台免费试用的网读电纸书测试机,不过考虑到一直以来开发calibre时没有Kindle作为测试机所带来的尴尬,于是就发布当天在Amazon上订购了一台。9月6日预定,Amazon10月4日发货,10月22日到手,无税,这次海淘基本顺利。

我的E Ink设备们

我的E Ink设备们:从左到右为汉王N510 (with OpenInkpot),盛大Bambook,网读,Kindle Paperwhite

Kindle Paperwhite的包装一如既往的使用Amazon的Frustration-Free Packaging包装盒,无需外包装加固且易于打开。打开包装后里面的东西也是老三样:Kindle主机、USB线、快速入门卡,无充电器。

由于E Ink屏不通电也能保持显示的特性,全新的Kindle屏幕上默认显示了电源开关的位置和开机方式,所以无须说明书即可轻松使用。开机后,默认照明光是开启的,并会自动进入一个使用引导的界面,通过步步指引,教会你使用Kindle所需要的基本技能。由于Kindle Paperwhite取消了所有的实体按键,这个入门指引的功能非常有必要,否则很可能不知道应该怎么操作。总的来说,Kindle从开箱到开机使用的整个过程,包括无线配置和Amazon帐号绑定过程,都设计得极为人性化,体验无可挑剔。

从握持的手感来说,Kindle Paperwhite明显比Kindle 4要重一些,跟Kindle Touch差不多。如果跟Kindle 3比的话总的来说是小巧了很多,而且设备边缘做的比前几代产品要圆滑一些,所以手感不错。之前非常纠结的没有了实体翻页键的问题,从实际使用上来说不算是非常严重的问题,而且大姆指的摆放位置也可以更舒适一些,不过触屏终究是不能提供一个理想的按键反馈,尤其是当设备反应有些迟缓的时候,无法判断是不是翻页成功了。

从显示效果来说,如果不仔细比对,全新的高分辨率屏(212 ppi)带来的体验提升并不是非常的明显,不过屏幕的对比度还是有了一个明显的提高,更“白底黑字”一些。Paperwhite支持黑体和宋体两种中文字体和6种英文字体切换,但默认的两种中文字体都比较丑陋。还好新的KF8文件格式很强大,包括calibre在内的一些第三方软件可以生成内嵌字体的awz3文件,从一定程度上可以弥补这个问题。

白天情况下最亮与最暗照明对比

白天情况下最亮与最暗照明对比,不是非常明显;但如果是晚上,最亮照明其实是非常亮的

Paperwhite最重要的新特性就是前置照明光, 照明光亮度可以有24档调节,但无法完全关闭。从总体感受来说,光照很柔合,看着还是很舒服的,效果远好于以前用过的一些外置前照LED灯。但照明还是没有想象中的那么均匀,尤其在晚上,屏幕下方是可以看到明显的4道照明光束,一定程度上来说也存在很多人所抱怨的照明光的红绿斑块的问题。如果你追求一种“完美”的均匀照明,恐怕Paperwhite还不能称得上是理想。

这次实际拥有并使用了Kindle后,感觉它跟想象中和以前的试用感受很一致,就是阅读体验确实做得很好,除了看书这件事本事以外,你不需要太多考虑别的东西。但是原生系统不支持EPUB格式以及对PDF的简陋的支持也是一大问题。对于普通中文用户来说,没有中文界面及中文输入法(意味着无法搜索书籍)、丑陋且不易更换的中文字体更是一个很不方便的地方。换句话说,对于一般的中文用户来说,在不谈第三方固件的前提下,Kindle并没有传说中的那么“神器”,某些国产品牌的电子书也没有传说中那样烂到不能让人接受。

细心的同学可以在上面的图片中注意到Paperwhite的书架上有一本“全球Kindle用户指南中文版”,这是似乎是Amazon第一次官方提供中文的用户指南,而且里面写明了Amazon.cn或中国境内销售商售出的Kindle由北京卓越亚马逊提供售后,并且还列出三包条款和一些中国GB标准适用性。原生系统中提供了两种中文字体,并且还提供了英汉词典和新华字典。不知道这些小细节是不是预示着Kindle已经做好了进入中国市场的前期准备。

最后吐槽一下正在免费试用的“网读”电子书,这款产品的最大的卖点是智能PDF重排,可以支持扫描版或非扫描版的PDF文件在6寸屏幕上的自动重新排版。对于扫描版的PDF,它会进行图像分割和拼接来实现重排,在E Ink设备有限的计算资源情况下,这个技术还是颇有可圈可点之处。但是,技术再牛,没有一个好的产品去支撑依然是个废品。以做技术POC的态度去做产品,最终恐怕是很难开拓出一个良好的市场的。虽然我很喜欢折腾E Ink设备,但最终我的目的还是阅读本身,我没有办法接受打开一个文件准备阅读时还要纠结于在N种“智能排版”模式中费心选择一种的阅读体验。我很敬重像网读这样专注于技术的公司,不过对于网读阅读器这个产品,试用后的结论还是:“退货”。

2012-11-09 更新:Amazon为Kindle Paper发布了新的5.3.0固件,提供了更美观的中文字体,还提供了中文界面和输入法,改善了漫画阅读功能。请参考http://www.amazon.com/gp/help/customer/display.html/?nodeId=201064850#download

坎坷的Nexus 7

一直对包括iPad在内的平板不是很感冒,因为一个连SSH都不能用得很爽的设备,我想不出来对我有什么实际用途,虽然我也坚定的认同平板电脑是一个极佳的娱乐平台。

前段时间尝试了几次海淘,发现比想象中的要靠谱,基本上从下单到收货也就是10天时间,这还包含了美国国内的慢吞吞的快递花的时间。于是心血来潮就在11号那天从Google Play海淘了一个Nexus 7,没想到就这样就栽倒海淘的坑里了。原因就是最近承运商UCS与海关之间产生了一些磨擦,导致15号开始无法向以前那样顺利及时的向国内发货。13号听到这个小道消息时,我的Nexus 7已经从Google发出,因为怕麻烦而且带着侥幸心理就没有选择马上退货,后果就是截止今天(我原本预计可以收到货的日子),它还躺在UCS的纽约仓库中没有上飞机,预计后面还有交关税的命运等着它。

昨天正好看到网上有人成本价出原包装的Nexus 7,于是迅速拿下,那哥们是因为海淘Nexus 7清关花了太长时间没忍住,就先淘宝入了,所以海淘回来的到手时就闲置了,只好转卖。回头我也得步他的后尘,出掉我自己淘的那个。顺丰速运一如既往的给力,今天一早我还没到公司,就已经把货送到了。

Nexus 7的包装没有传说的那么难拆,很轻松就打开了,撕开一次性封条,上面会显示出中文:“已拆封”。整个设备的重量比想像中的要重一些,如果长时间拿着看书的话,估计会比Kindle累很多。设备做工表面上看看很好,但实际上确实存在网上说的那些瑕疵,其中最明显的就是“翘屏”的问题--左侧的屏幕面板没有粘合平整,用手按上去有轻微松动感,同时屏幕上会出现水波纹。网上有很多自己动手维修的方式,不过据说华硕为Nexus 7提供全球质保,所以以后拿去华硕售后处理也许也是个不错的选择。

我一直只用过低端的Andorid手机,所以对于我来说Nexus 7的高配置和Android 4.1带来的流畅的操作感觉还是很让人满意的,只从流畅上来说,不输于iPad系列。屏幕总的来说也还算细腻,比iPad 2强。

虽然不是很必要,不过我还是在第一时间把Nexus给Unlock和root了,使用的工具就是很有名的Nexus Root Toolkit,工具对整个过程有一步步的指导,遇到各种异常也都有详细的解释,非常好用。对于我来说,使用SSHDroid通过远程访问操作设备和用OTG扩展存储也许是需要root的最重要原因,如果只是正常使用,完全可以不用去root它。

Nexus 7非常匪夷所思的只装备了前置摄像头,没有后置摄像头,决定了这个设备几乎没有办法用来拍照,前置摄像头性能也很一般,似乎并不支持自动对焦,识别条码稍有压力。所以最实际的用途就只能是来用视频聊天和人脸解锁了。人脸识别解锁的功能用起来很流畅,但总的来说还是感觉是个噱头。

NFC,周围没有别的NFC设备,只能用它读读IC卡了,杭州市民卡、浙江图书馆借阅证、南京市民卡、金陵通靠上去都会有反应,不过因为没有相应的程序来解码,所以读不出实际的信息。有空打算尝试一下看看能不能解码出一些东西来,参考nfcard项目。

重头戏,Google Now。它有多牛,看这个视频即可,但它的牛也仅限于视频中演示的这些,除此之外的基本上都是直接变成语音识别+搜索了。不过不管怎么说,Google Now的TTS合成语音比Siri是好听太多了。另外,如果把Google Now的语言切换成中文,也会失去所有“智能”功能,退化成语音识别+搜索。

试用过程中发现几个坑:

1. 设VPN连接前必须设置某一种设备锁,并设完VPN连接后就不能更改设备解锁方式了。所以我一开始设了Pattern解锁和VPN连接后,人脸识别解锁就不能选了,必须把VPN连接删掉后才能改为人脸解锁。

2. 没有挂VPN时,Google Now功能极残废,基本上不管跟他说什么都是直接语音识别后转到google.com.hk进行搜索。挂了美国VPN后就一切正常了。

3. Google Play Books不见了,在Google Play Market中看这个应用程序是已经安装的,但是没有运行按钮,在应用程序列表中也看不到它。原因是在中国这程序会自动被冻结,如果需要使用,可以安装“钛备份”解冻这个程序(需要root),并连接美国VPN。

总结:Nexus 7没有给我带来过多的惊喜,跟我想象中的一个Andorid平板设备基本一致。不过我感觉到如果好好的利用它,还是可以从某种程度上改变一些工作和生活的习惯的。虽然系统很流畅,但我觉得从易用性和整体的设计上,跟iPad比还是要逊色一些,技术青年也许能接受这样操作方式,但我觉得从大众的层面看,还是有很多可以改进的地方。

坎坷的Nexus 7

一直对包括iPad在内的平板不是很感冒,因为一个连SSH都不能用得很爽的设备,我想不出来对我有什么实际用途,虽然我也坚定的认同平板电脑是一个极佳的娱乐平台。

前段时间尝试了几次海淘,发现比想象中的要靠谱,基本上从下单到收货也就是10天时间,这还包含了美国国内的慢吞吞的快递花的时间。于是心血来潮就在11号那天从Google Play海淘了一个Nexus 7,没想到就这样就栽倒海淘的坑里了。原因就是最近承运商UCS与海关之间产生了一些磨擦,导致15号开始无法向以前那样顺利及时的向国内发货。13号听到这个小道消息时,我的Nexus 7已经从Google发出,因为怕麻烦而且带着侥幸心理就没有选择马上退货,后果就是截止今天(我原本预计可以收到货的日子),它还躺在UCS的纽约仓库中没有上飞机,预计后面还有交关税的命运等着它。

昨天正好看到网上有人成本价出原包装的Nexus 7,于是迅速拿下,那哥们是因为海淘Nexus 7清关花了太长时间没忍住,就先淘宝入了,所以海淘回来的到手时就闲置了,只好转卖。回头我也得步他的后尘,出掉我自己淘的那个。顺丰速递一如既往的给力,今天一早我还没到公司,就已经把货送到了。

Nexus 7的包装没有传说的那么难拆,很轻松就打开了,撕开一次性封条,上面会显示出中文:“已拆封”。整个设备的重量比想像中的要重一些,如果长时间拿着看书的话,估计会比Kindle累很多。设备做工表面上看看很好,但实际上确实存在网上说的那些瑕疵,其中最明显的就是“翘屏”的问题--左侧的屏幕面板没有粘合平整,用手按上去有轻微松动感,同时屏幕上会出现水波纹。网上有很多自己动手维修的方式,不过据说华硕为Nexus 7提供全球质保,所以以后拿去华硕售后处理也许也是个不错的选择。

我一直只用过低端的Andorid手机,所以对于我来说Nexus 7的高配置和Android 4.1带来的流畅的操作感觉还是很让人满意的,只从流畅上来说,不输于iPad系列。屏幕总的来说也还算细腻,比iPad 2强。

虽然不是很必要,不过我还是在第一时间把Nexus给Unlock和root了,使用的工具就是很有名的Nexus Root Toolkit,工具对整个过程有一步步的指导,遇到各种异常也都有详细的解释,非常好用。对于我来说,使用SSHDroid通过远程访问操作设备和用OTG扩展存储也许是需要root的最重要原因,如果只是正常使用,完全可以不用去root它。

Nexus 7非常匪夷所思的只装备了前置摄像头,没有后置摄像头,决定了这个设备几乎没有办法用来拍照,前置摄像头性能也很一般,似乎并不支持自动对焦,识别条码稍有压力。所以最实际的用途就只能是来用视频聊天和人脸解锁了。人脸识别解锁的功能用起来很流畅,但总的来说还是感觉是个噱头。

NFC,周围没有别的NFC设备,只能用它读读IC卡了,杭州市民卡、浙江图书馆借阅证、南京市民卡、金陵通靠上去都会有反应,不过因为没有相应的程序来解码,所以读不出实际的信息。有空打算尝试一下看看能不能解码出一些东西来,参考nfcard项目。

重头戏,Google Now。它有多牛,看这个视频即可,但它的牛也仅限于视频中演示的这些,除此之外的基本上都是直接变成语音识别+搜索了。不过不管怎么说,Google Now的TTS合成语音比Siri是好听太多了。另外,如果把Google Now的语言切换成中文,也会失去所有“智能”功能,退化成语音识别+搜索。

试用过程中发现两个坑:

1. 设VPN连接前必须设置某一种设备锁,并设完VPN连接后就不能更改设备解锁方式了。所以我一开始设了Pattern解锁和VPN连接后,人脸识别解锁就不能选了,必须把VPN连接删掉后才能改为人脸解锁。

2. 没有挂VPN时,Google Now功能极残废,基本上不管跟他说什么都是直接语音识别后转到google.com.hk进行搜索。挂了美国VPN后就一切正常了。

总结:Nexus 7没有给我带来过多的惊喜,跟我想象中的一个Andorid平板设备基本一致。不过我感觉到如果好好的利用它,还是可以从某种程度上改变一些工作和生活的习惯的。虽然系统很流畅,但我觉得从易用性和整体的设计上,跟iPad比还是要逊色一些,技术青年也许能接受这样操作方式,但我觉得从大众的层面看,还是有很多可以改进的地方。