@Lenciel

Octopress里插入带中文Gist的问题和解决

Gist是Github提供用来管理和分享代码片断或者文档片断的服务。和传统的snippet)管理系统相比,Gist因为实际上依托了Github的实现技术,所以具有存储在云端、自动的版本化、语法高亮支持的语言种类齐全、分享的便利性高等等优势。

Octopress里面引用你创建的gist的好处是显而易见的:你需要分享的内容在Gist那边被更好的管理了,而在日志里面用下面这样的语句就可以很容易的插入gist里面的内容:

{% gist gist_id [filename] %}

比如上篇文章,理论上源代码就是这么一句:

{% gist lenciel/637812a7dcbe8341b07b web_skill_list.md %}

当然之所以说理论上,是因为首先Octopress的markdown插件对unicode的支持是有bug的。所以直接插入带中文的gist在build的时候会看到Liquid Exception:

1
2
>>> Compass is watching for changes. Press Ctrl-C to Stop.
Liquid Exception: incompatible character encodings: UTF-8 and ASCII-8BIT in 2014-05-16-web-development-skill-set-and-reading-list.markdown

解决起来也很简单,只需要在lib/jekyll/converters/markdown.rb里面强制设定encoding就可以了:

lib/jekyll/converters/markdown.rb
1
2
3
4
5
6
7
@@ -120,7 +120,7 @@ def convert(content)
           rd = RDiscount.new(content, *@rdiscount_extensions)
           html = rd.to_html
           if rd.generate_toc and html.include?(@config['rdiscount']['toc_token'])
-            html.gsub!(@config['rdiscount']['toc_token'], rd.toc_content)
+            html.gsub!(@config['rdiscount']['toc_token'], rd.toc_content.force_encoding('utf-8'))
           end

接下来就是render出来的效果,样子丑到让人无法直视:

original gist render output

大概看了一下Octopress的文档,原来代码片断的样式(包括gist的渲染样式)都在_syntax.scss里面。这个文件2012年就没有更新过了,但是github的gist输出在2013年有过变化,所以就有些不匹配了。

打开自己的模板,大概调了一下这个sass,主要是和其他的代码高亮一致:

current gist render output

调整的地方见这里:

_sass/partials/_syntax.scss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
diff --git a/46501e4:sass/partials/_syntax.scss b/02b0441:sass/partials/_syntax.scss
index 137d475..5465286 100644
--- a/46501e4:sass/partials/_syntax.scss
+++ b/02b0441:sass/partials/_syntax.scss
@@ -22,10 +22,6 @@
   @include border-radius(0);
 }

-.line-data {
-   font-size: 13px;
-}
-
 figure.code, .gist-file, pre {
   @include box-shadow(rgba(#000, .06) 0 0 10px);
   .highlight pre { @include box-shadow(none); }
@@ -43,10 +39,8 @@ html .gist .gist-file {
     margin-bottom: 0;
   }
   .gist-syntax {
-    background: #01222d !important;
-    color: #d5dee2 !important;
-    padding: 0 2em;
     border-bottom: 0 !important;
+    background: none !important;
     .gist-highlight {
       background: $base03 !important;
     }
@@ -57,17 +51,15 @@ html .gist .gist-file {
   }
   .gist-meta {
    padding: .6em 0.8em;
+   border: 1px solid lighten($base02, 2) !important;
+   color: $base01;
    font-size: .7em !important;
    @if $solarized == light {
-     color: $base01;
-     border: 1px solid lighten($base02, 2) !important;
      background: lighten($base03, 2) $noise-bg;
      border: 1px solid $pre-border !important;
      border-top: 1px solid lighten($base03, 2) !important;
    } @else {
-     background: #01222d $noise-bg;
-     text-shadow: none !important;
-     color: #d5dee2;
+     background: $base02 $noise-bg;
    }
    @extend .sans;
    line-height: 1.5em;
@@ -77,7 +69,7 @@ html .gist .gist-file {
       &:hover { color: $base1 !important; }
     }
     a[href*='#file'] {
-      position: absolute; top: 0; left:0; right:0px;
+      position: absolute; top: 0; left:0; right:-10px;
       color: #474747 !important;
       @extend .code-title;
       &:hover { color: $link-color !important; }
@@ -221,7 +213,7 @@ pre, .highlight, .gist-highlight {
   &::-webkit-scrollbar-thumb:horizontal { background: $solar-scroll-thumb;  -webkit-border-radius: 4px; border-radius: 4px }
 }

-.highlight code {
+.highlight code { 
   @extend .pre-code; background: #000;
 }
 figure.code {
@@ -267,11 +259,3 @@ figure.code {
   text-shadow: #cbcccc 0 1px 0;
   padding-left: 3em;
 }
-
-.gist-file {
-  font-size:.8em !important;
-}
-
-table.lines{
-  width: 100%;
-}

How Does iBeacons Work

iBeacons这个怪怪的单词最近出镜率很高。它可以用的场景很多,考勤打卡,室内导航,各种关联位置的推送以及我们现在还想不到诡异的应用。但虽然满坑满谷的文章介绍如何应用iBeacons,但它工作的原理究竟是什么样的呢?

Bluetooth LE

Bluetooth Low Energy,也就是低功耗蓝牙,经常被缩写成BLE。它其实是2010年颁布的Bluetooth 4.0标准的一部分:说准确点儿就是由Nokia提出的本来叫Wibree的那部分。

话说11年本座给一个银行做押运的系统,需要在Android手机上加外设,让押运员持卡验明正身。当时考察了好些技术,蓝牙,NFC,IC卡…蓝牙被放弃的原因就是供电太麻烦而4.0低功耗的设备市面上根本就找不到。因为虽然标准是2010年就颁布了,但是4.0的协议向下不兼容,设备更新没有那么快。一晃几年过去了,你还是能看到市面上有三种主要的蓝牙设备型号:

  • Bluetooth: 仅支持传统蓝牙
  • Bluetooth Smart Ready: 传统低功耗通吃的蓝牙
  • Bluetooth Smart: 仅支持低功耗蓝牙

bluetooth smart ready logo

比较老的设备,以及大部分的汽车,家电等等,是仅支持传统意义上的Bluetooth的。

比较新的手机(iPhone 4S+,Samsung G3+),平板,笔记本等等都已经支持了完整的Bluetooth 4.0,所以都可以算是"Smart Ready"的。

Beacons,如前面所述,是仅支持低功耗这部分的标准,所以就是"Smart"的。这些设备一般一颗纽扣电池就可以保证它运行2年左右,发射频率和传统蓝牙一样都是2.4GHz-2.4835GHz,在无遮挡的情况下100米内都有信号,但传输速率比传统蓝牙慢:当然,低功耗蓝牙设计的目的也不是用来输送大量的数据。

BLE通信

BLE通信主要是两种模式:AdvertisingConnecting

Advertising是单向的。希望被发现的设备自主发射间隔从20ms到10s不等的数据包出来。发射间隔越短,电池寿命就会越短,但同时也会更快的被找到。数据包的组成为:

  • 1 byte
  • 4 bytes access adress
  • 2-39 bytes advertising channel PDU
  • 3 bytes CRC

bluetooth le packet

其中access address在这种模式下是固定的0x8E89BED6。换句话说,Advertising模式就是对外以一个固定的读取地址和固定的时间间隔不断的发送数据。

PDU的2个bytes的头包含了size/type等payload的信息,接着是device的MAC地址,最后是最多31个bytes的实际payload数据。

当一个设备被找到之后,连接可以切换到Connecting模式。这种情况下,设备可以对外提供可读写的服务(被称为GATT profile)。比如一个智能热水器,可以提供一个服务让你读取以及设置出水的温度。

关于Connecting到BLE设备,苹果的教程或者更专业的EE times的介绍都可以看看。但其实beacons没有使用Connecting模式,它只工作在Advertising模式下。

beacons如何使用BLE

beacons仅仅工作在Advertising模式下。因此,可以说所谓的iBeacons就是iOS下对BLE的Advertising工作模式里数据的定义和封装。

以Estimote的beacon为例,下面是它的PDU数据(使用XCode的插件可以对蓝牙设备抓包):

1
02 01 06 1A FF 4C 00 02 15 B9 40 7F 30 F5 F8 46 6E AF F9 25 55 6B 57 FE 6D 00 49 00 0A C5

可以看到整个payload数据是30bytes,非常符合协议最多31bytes的要求。实际上,这份数据的格式的确是苹果精心设计了的:

1
2
3
4
5
02 01 06 1A FF 4C 00 02 15: iBeacon prefix (fixed)
B9 40 7F 30 F5 F8 46 6E AF F9 25 55 6B 57 FE 6D: proximity UUID (here: Estimote’s fixed UUID)
00 49: major
00 0A: minor
C5: 2’s complement of measured TX power

bluetooth le ibeacon packet

换句哈说,如果你想试玩一下iBeacons技术,在买到beacon之前,其实你手边的任何新一点儿的iPhone或者Mac设备都可以拿来作为iBeacon的发射端或者接收端。当然,万能的树莓也可以改造成beacon。

iBeacon数据格式

02 01 06 1A FF 4C 00 02 15是固定不变的prefix。

Proximity UUID用来标识beacon。比如商店里面一批beacon都是用来提供打折活动的推送给顾客,那么这批beacon的Proximity UUID就应该是一样的。

Major Number (2 bytes,例子里面是0×0049,也就是73) 是用来对一批beacon进行分组的。比如商店里面的所有的beacon可以配置成相同的Major Number

Minor Number (2 bytes,例子里面是0x000A,也就是10)用来标识单个的beacon。因为商店里面每个beacon都有不同的Minor Number,才能知道顾客是处在什么位置。

测距

数据里面的最后一位,TX Power,就是用来表征你离某个beacon的距离的。它测距离主要就是用所谓的RSSI(Received Signal Strength Indication)去算,这个算法在iOS里面是已经集成了的,即便是需要在Android平台上面实现一遍也不是那么的复杂

另外需要注意的就是,无线信号总会因为障碍物以及其他的信号源更快的衰减。所以测算出来的距离只是一个有大概。

iOS集成

苹果既然敢在beacon前面加个i当然是做了很多增强的。对应用开发者来说,最主要的就是当用户靠近iBeacons设备的时候,App即使不在前台也可以被唤醒,并发送notification给用户。

需要注意的是,因为beacon是周期性的发送,而手机在进入省电模式的时候探测蓝牙型号也是周期性的。这两个周期很可能会踩到边界,造成在实际应用中,有的手机甚至会需要15分钟左右的时间才能找到beacon。

iOS下详细的应用开发教程可以见这里。更多的例子可以参考设备商Estimote的iOS SDK和Android。

如何购买设备

现在整个供货情况还是接不上趟的。我的Estimote已经订了4个多礼拜了还杳无音讯。因此最快的上手方案仍然是改造你的手机或者用Raspberry Pi自己搭个玩。但是如果你不满足(一般是满足不了的)这种玩法可以试试:

另外,高通也有做自己的解决方案叫Gimbal,不但提供了Android和iOS两套SDK,价格也很平易近人:5刀一个,值得拥有,:)