2012/08/24

小白電池膨脹囉

我的小白MacBook跟了我快五年了,任勞任怨盡忠職守,前一陣子發現觸碰板的按鍵失靈了,嗯,修理太貴了,買隻滑鼠插上就好了,更何況最近也沒帶他出去,一直都放在桌上,以遠端遙控的方式使用,不太需要那個按鍵。

後來有一天,整理桌子時,拿起MacBook才發現,怎麼底部鼓起來了,不會是因為夏天而熱脹冷縮吧?哇,上網一查才發現,那個是電池啊,電池腫起來囉。

嗯,好髒,待會要清理。


底部,右下角就是膨脹的地方。


哇,鼓起來了,該不會爆炸吧?


上網搜尋後,得知不少人的小白也發生這種現象,還有人成功換到新電池的經驗,但那都是很久以前的事情了。


換顆新電池大概要2000~3000,嗯,算了,就冒著爆炸的危險繼續用吧,哈哈。

iOS儲存機密資料

怎麼在iOS上儲存機密資料呢,譬如密碼,這些資料必須加密後存放,不能被第三方輕易地竊取。到網路上搜尋後,大概會找到Apple官方的Keychain Service這套API,以及GenericKeychain這份範例程式碼。

經過一番奮鬥,詳細研讀Apple官方文件與網路上的教學文章後,我似乎勉強搞懂 SecItemCopyMatching、SecItemAdd、SecItemUpdate、SecItemDelete的用法與各種相關資料結構與常數的意義了。但是,這套API似乎太低階,當初設計的初衷大概考慮了讓多支軟體在同個環境內能共享或保護私密資料,對於我來說(只是想儲存專供我軟體使用的機密資料),非常難用,障礙很多,很容易出錯。

這種情形不就跟Joel說的一樣嗎,世界上有0.1%的人能學會C++寫程式,卻只有0.01%的人能搞懂ATL,而且每個人都留鬍子戴眼鏡,哇賽,還真的有這種讓人頭昏眼花的API啊。

或許Keychain Service還沒到那個地步,但實在不夠好用,還好,有人將Keychain Service雜七雜八的細節包了起來,提供一套輕便簡易的API,讓我們可以輕易地儲存機密資料,那就是STKeychain(前身為scifihifi-iphone)。

STKeychain原始碼放在GitHub的ldandersen / STUtils裡,位於子目錄Security裡,裡面有兩支檔案STKeychain.h與STKeychain.m,加入專案後,再加入Security.framework開發框架便完成了準備動作。(ldandersen / STUtils有許多子目錄,為其他不同功能,有興趣請自行試試看。)

因為我只使用STKeychain這兩支檔案,所以要把STKeychain.m前面的「#import "STUtils"」改成「STKeychain.h」。

STKeychain並未使用ARC,若你使用ARC的話記得在STKeychain.m檔加入編譯參數 -fno-objc-arc。

介面有三個,非常清楚。

首先是儲存,傳入你想儲存的username、password、serviceName,一般來說,username與serviceName是公開的,會儲存在別的地方,而這裡是把username與serviceName當做「鍵」的角色,將password儲存在安全的地方。參數updateExisting的意思是,若該筆資料已經存在的話要不要覆蓋過去,當出錯時可從參數error得到錯誤訊息,不需要就傳入nil;回傳BOOL代表儲存成功或失敗。
+ (BOOL)storeUsername:(NSString *)username andPassword:(NSString *)password forServiceName:(NSString *)serviceName updateExisting:(BOOL)updateExisting error:(NSError **)error;

然後是取出,傳入先前使用的username與serviceName,找出相對應的password回傳。
+ (NSString *)getPasswordForUsername:(NSString *)username andServiceName:(NSString *)serviceName error:(NSError **)error;

底下是刪除,應該無需多作說明。
+ (BOOL)deleteItemForUsername:(NSString *)username andServiceName:(NSString *)serviceName error:(NSError **)error;

這套API在iOS與Mac OS X上皆可使用,非常方便、簡單,太好囉,不用再跟Apple的Keychain Service奮鬥了!

2012/08/23

UIPickerView與NSTimer

朋友問我一個問題,在畫面上顯示UIPickerView,另外顯示一個UILabel進行倒數計時(譬如說從12.0秒倒數到0.0秒,每0.1秒更新)。

聽起來很簡單,不就用個NSTimer就搞定了嗎?可惜不是。

當倒數計時並更新UILabel時,若手指撥動UIPickerView,那麼倒數計時會暫停、UILabel也不會更新。



真正的原因我不清楚,大概是因為UIPickerView需要追蹤使用者的手指觸控事件,所以佔據了thread的runloop,使得NSTimer無法觸發吧。

解法之一是在另一個thread使用NSTimer:

float countdown = 120;

- (void)timerFireMethod:(NSTimer*)timer
{
    if(countdown > 0){
        countdown--;
        self.label.text = [NSString stringWithFormat:@"%2.1f", countdown/10];
        NSLog(@"%2.1f", countdown/10);
    }
    else{
        [self suspendTimer];
    }
}

- (void)suspendTimer
{
    if(_timer != nil)
    {
        [_timer invalidate];
        _timer = nil;
       
        CFRunLoopStop([_runLoop getCFRunLoop]);
       
        _timerThread = nil;
    }
}

- (void)_startTimerThread
{
    @autoreleasepool {

        _runLoop = [NSRunLoop currentRunLoop];
        _timer = [NSTimer scheduledTimerWithTimeInterval:0.1
                                               target:self
                                             selector:@selector(timerFireMethod:)
                                             userInfo:nil
                                              repeats:YES] ;
        [_runLoop run];
    }
}

- (void)resumeTimer
{
    if(_timer == nil)
    {
        _timerThread = [[NSThread alloc] initWithTarget:self
                                               selector:@selector(_startTimerThread)
                                                 object:nil];
        [_timerThread start];
    }
}

其中resumeTimer、suspendTimer是使用介面,把你想要執行的程式寫在timerFireMethod:裡。

解法之二是使用GCD(Grand Central Dispatch):

float countdown;

-(void)tick
{
    self.label.text = [NSString stringWithFormat:@"%2.1f", countdown/10];
}
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
  
    countdown = 120;
  
    queue = dispatch_queue_create("com.example.MyQueue",  NULL);
  
    ds_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
  
    dispatch_source_set_timer(ds_timer,  dispatch_time(DISPATCH_TIME_NOW,  0),  0.1*NSEC_PER_SEC, 0);
    dispatch_source_set_event_handler(ds_timer,  ^(){
        if(countdown > 0){
            NSLog(@"tick %2.1f", countdown/10);
            countdown--;
            [self performSelectorOnMainThread:@selector(tick) withObject:nil waitUntilDone:YES];
        }
        else{
            //over
        }
    });
    dispatch_resume(ds_timer);
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
  
    dispatch_suspend(ds_timer);
    dispatch_release(ds_timer);
    dispatch_release(queue);
}

利用timer dispatch source便能每隔一段時間產生出某事件,然後指定想要執行的程式碼,要注意的是,想要更新iOS的畫面時,必須在main thread裡進行,所以上面程式碼使用了 [self performSelectorOnMainThread:@selector(tick) withObject:nil waitUntilDone:YES];這個方法。

應該也能以NSOperation與NSOperationQueue解決,但我還沒試。


參考文章:


使用ARC時的一個小陷阱:UIColor回傳的CGColor

自從開始使用ARC(Automatic Reference Counting)後,我覺得這算是Objective-C的一個重大進步,但是,但是,並不代表寫程式時不需要思考如何管理物件與記憶體了,還是要跟一堆關鍵字(strong、weak、__bridge_transfer、等等)打交道,還是要遵循一堆使用記憶體的相關守則與慣例,以前要想著release與retain,現在則要想著物件彼此間的關係圖。

底下記錄一個絆倒我的ARC陷阱,跟UIColor回傳CGColor有關。

在某個方法裡,我建立了UIColor物件,並以它的CGColor方法回傳CGColorRef,然後使用。

在某方法裡:
{

  CGColorRef cgcolor = [UIColor colorWithRed: 0.2 
                                                                  green: 0.3 
                                                                     blue: 0.4 
                                                                   alpha: 1.0].CGColor;

  [[self.view layer] setBackgroundColor: cgcolor];
}

糟了,發生記憶體錯誤當掉了,怎麼會這樣呢?

使用ARC以前:上面程式碼裡的UIColor物件會被autorelease,我們知道,在方法回傳結束前,這個物件會是存在的,所以cgcolor會指向存在的物件。

使用ARC以後:UIColor物件並沒有被任何變數指向,所以,ARC很聰明地將它釋放掉了,哈哈。

解法很多,我比較想採用的是這一個:

{
  UIColor color = [UIColor colorWithRed: 0.2 
                                                       green: 0.3 
                                                          blue: 0.4 
                                                        alpha: 1.0];

  [[self.view layer] setBackgroundColor: color.CGColor];
}

這麼一來,color是個strong變數,可以確保該物件不會被釋放,直到用完為止。

其他解法還有:以CGColorRetain與CGColorRelease抓住cgcolor。

另外,底下的寫法看似合理,但卻是個有問題的寫法:

{
  UIColor color = [UIColor colorWithRed: 0.2 
                                                       green: 0.3 
                                                          blue: 0.4 
                                                        alpha: 1.0];
  CGColorRef cgcolor = color.CGColor;
  [[self.view layer] setBackgroundColor: cgcolor];
}

color物件在呼叫CGColor方法後就無處使用,所以ARC很可能會在setBackgroundColor之前就是放掉color物件,以致於cgcolor指向不存在的記憶體空間。



參考文章:

2012/08/13

Raspberry Pi測試音訊介面

Raspberry Pi有兩個音訊輸出介面,一個是標準的3.5 mm TRS端子(也就是一般大小的耳機插座∕插頭),另一個是HDMI(HDMI可輸出影像與聲音)。

這篇要簡單測試一下能否發出聲音。

我用的是2012-07-15-wheezy-raspbian.img這支映像檔。

如果你用的是較舊的Debian squeeze,請安裝ALSA音效驅動程式並載入核心。
$ sudo apt-get install alsa-utils
$ sudo modprobe snd_bcm2835

若是wheezy的話,預設已經開啟了,所以上面不需執行上面那兩道指令。

然後就能以底下指令念出"front center"這兩個英文單字。
$ sudo aplay /usr/share/sounds/alsa/Front_Center.wav

音效輸出預設為自動模式,你可以底下指令指定之。
$ sudo amixer cset numid=3 n

n可以是0、1、2。
0:自動
1:耳機插座
2:HDMI

另外,rpi裡有些範例程式,請以底下指令編譯。
$ cd /opt/vc/src/hello_pi/
$ ./rebuild.sh
$ cd hello_audio

然後,就能執行並發出"嗚嗚"聲。
./hello_audio.bin
./hello_audio.bin 1 (從HDMI輸出)

在命令列模式下,有支不錯的播放器omxplayer,你可試試看播放mp3檔。我發現少部分我某些mp3檔不能播放。

注意,使用者帳號必須屬於"audio"群組,才能存取音效卡。

另外,有支更好用的軟體,叫做moc(music on console),可以在命令列模式下在背景播放歌曲,而且它有類視窗介面,用起來更方便。



參考資料:
RS components Raspberry Pi 音頻與USB快閃磁碟安裝示範(國語) 
Raspberry Pi官方論壇的討論串No sound Rasbian Wheezy
Raspberry Pi維基百科R-Pi Troubleshooting的Sound部分

Raspberry Pi安裝中文環境

我個人認為,Raspberry Pi(以下簡稱rpi)執行X Window的速度並不足以當做一台個人電腦作為日常生活使用,不過當做家庭劇院、媒體播放器卻非常合適;我很少在rpi上使用X Window,也不太需要中文顯示與輸入,不過,Raspberry Pi的作業系統為Linux,其安裝中文環境的程序其實沒什麼兩樣,底下是我將步驟簡略記錄下來。

我使用的是2012-07-15-wheezy-raspbian.img這支映像檔。

先更新一下套件清單。
$ sudo apt-get update

安裝中文字型,文泉驛微米黑、文泉驛正黑、文泉驛點陣宋體。
$ sudo apt-get install ttf-wqy-microhei ttf-wqy-zenhei xfonts-wqy

然後將locale設定成繁體中文。
$ sudo dpkg-reconfigure locales

繁體中文的部份,我選了三個zh_TW BIG5、zh_TW.EUC_TW EUC-TW、zh_TW.UTF-8 UTF-8。


然後將zh_TW.UTF-8設定為預設的locale。


打開瀏覽器,哇,可以顯示中文囉。


然後要安裝中文輸入法。
$ sudo apt-get install scim scim-tables-zh scim-chewing

其中scim-chewing是注音的輸入法,若是大陸用的拼音輸入法,請安裝scim-pinyin。

重開機後,以Ctrl-Space即可切換輸入法,輸入中文。


視窗右下角可看到輸入法的圖示。


不只有注音,還有其他輸入法可切換喔。



參考資料:
RS components Raspberry Pi 中文設置環境安裝示範(上) (國語)
RS components Raspberry Pi 中文設置環境安裝示範(下) (國語)
RS components Raspberry Pi 智能拼音輸入法安裝示範(國語)
ericsk 的 Ubuntu Linux 筆記 - 中文輸入法 - 使用 SCIM
令 Debian 支援中文及其他語言
SCIM(smart common input method)官方網站

2012/08/06

Raspberry Pi的GPIO外接板(分線板、原型板、緩衝板、擴充板)

Raspberry Pi(以下簡稱rpi)板子上有26(2x13)個GPIO針腳(其中6個保留不用,3個為5V、3.3V、GND,所以還有17個),這些GPIO腳位的運作電壓為3.3V,勉強可以點亮LED,若要驅動馬達或其他高電壓大電流的東西,就需要電晶體放大器等元件。

但直接使用這些針腳的話不僅不方便,還會有損害rpi的可能性,所以最好有一塊加入緩衝、保護機制的外接板,這種板子有的簡單有的複雜,各自具有不同的功能,譬如一般來說,分線板(breakout board)僅是將晶片或各種電子元件的針腳拉出來方便使用,如此而已;原型板(prototyping board)上頭會有未使用的區域,讓我們可放置各種電子元件,拆裝容易;緩衝板(buffer board)則加入保護機制避免因短路、錯接而損害原有元件;擴充板(expansion board),或稱擴展板,則加入其他元件擴增功能。

底下介紹幾塊板子,有些是商品要賣錢的,有些是作者將製作方法放上網路,大家可以自行製作。

Adafruit Pi Cobbler Kit,分線板,必須自己焊接。可插入麵包板以便連接其他電子元件。

另有較新的Pi T-Cobbler Breakout Kit

Mike Cook的Breakout分線板與Buffer Board緩衝板,詳細的製作方法,你可自行選擇要不要加入保護機制,要不要放上LED。




Gert van Loo的Gertboard,光看就很複雜,具有不少功能,必須自己組裝焊接,於2012年8月開賣。附註:Gert是rpi的硬體設計師之一。


Pi-Face,直接插在rpi上頭,應該是由曼徹斯特大學電腦科學系研發。


Daniel Amesberger的RasPiComm – a Raspberry Pi piggyback board。小巧精緻、自行製作,功能有RS-485 port、RS-232('serial') port、5個輸入(連接到板子上的搖桿與螺絲終端接點)、2個帶有LED的5V輸出(也連接到螺絲終端接點)、I2C、有後備電池供電的即時時鐘(RTC)。


Adafruit Prototyping Pi Plate,原型板,必須自己焊接。


树莓派论坛树莓派扩展板第一版第二版树莓派全功能GPIO扩展板
第一版


第二版


Quick2Wire的Interface Board,預計2012年9月開賣。


當然啦,還有其他許許多多不同種類的外接板,請參閱rpi維基百科的RPi Expansion Boards

以充電電池供給音樂盒子

我買了一個叫做"音樂盒子"的電子產品,小小一個,功能不少。

1. 插入含有mp3檔的USB隨身碟,或是插入含有mp3檔的microSD記憶卡,聆聽歌曲。
2. 可聆聽FM廣播。
3. 可當做一般的喇叭,連接iPhone或其他音訊輸出裝置。
4. 可當時鐘、鬧鐘。

從下圖可看到喇叭、電源開關、以及四個LED顯示器,用來顯示歌曲順序、FM頻率、 音量、時間。


從下圖可到看按鈕與插孔。稍微練習一下後,應該就能熟悉各個按鈕的功用;插孔有USB插座、USB Mini插座、耳機插座、microSD插槽。


還有附一條連接線,有三頭(耳機插頭、USB插頭、USB Mini插頭)。


我第一次碰到這種連接線,剛開始時還不知道怎麼用,多虧店員耐心介紹。
1. 欲聆聽FM的話,插入耳機插頭,把線當做天線使用。
2. 想充電的話,插入USB Mini插頭,而USB插頭則插入電腦或USB Hub。
3. 想當做其他設備的喇叭的話,插入USB Mini插頭,而耳機插頭則插入其他音訊輸出設備(如iPhone)。

這個產品內含了充電電池,但容量似乎不大,所以我想要以原有的三號AA充電電池來供給電力。

首先買個USB micro的頭,如下將5V與GND針腳焊接線材。


我用了六顆三號充電電池,總電壓約為8.09V,然後經由線性穩壓器LM7805,經過穩壓變壓後得到4.97V(理論上是5V)。


然後接上去。


底下是完成圖,插上那隻很久很久以前買的USB隨身碟(居然只有128 MB),哇,成功囉。


不過,也不太可能全部帶著走吧,哈哈,純粹好玩而已。

電腦主機大掃除

我的桌上電腦用了很久了,可能七八年吧,尚在服役中,真是辛苦了。主機板、CPU、電源供應器、顯示卡都沒換過,換過的應該只有硬碟吧,前陣子換了SSD固態硬碟,效能提昇了不少,讚。

之前也有大概清理過,機殼風扇、CPU風扇、主機殼內的灰塵、等等,這次是第一次把全部東西拆開,好好地清理一番。

除了光碟機跟硬碟,全部都拆下來囉。


最上面的小風扇是機殼風扇,中間是電源供應器,左下角是主機板、旁邊是顯示卡。清出來的灰塵真是多啊,為了不讓畫面噁心,就不拍照上傳了。


這是主機板上的晶片風扇,已經有點故障了,剛開機有時不轉或轉速太低,BIOS會逼逼叫,然後才會正常運轉,不過,若以手撥弄的話,會卡卡的轉不動,該是換新品的時刻了。


這應該是主機板廠商專屬規格的風扇,很難拆。


這是拆下風扇後的主機板晶片,我很懷疑那些"黃色"的地方,該不會是燒焦了吧。


這是到電子材料行買的風扇,勉強裝上去了。


完成囉,趕快裝機,還好沒東西被我弄壞,那就得不償失了。以主機板附的偵測軟體查看,嗯,主機板溫度還好,CPU溫度還是很高耶,大概是因為散熱膏都乾掉了吧,其他數據也都正常,但"電源風扇"的轉速居然高達7000,有沒有搞錯啊。


後來去買了25元的2g散熱膏,重新塗上,嗯,CPU果然降到47度了,太好了,風扇也不需轉那麼快,減少噪音,太好了。

嗯,這台電腦還好好的,真是不錯。記得我以前的電腦,用了幾年後,就會出現"突然關機或重新開機"的症狀,這台尚未出現,讚。

2012/08/02

以三號AA電池供給電力給Raspberry Pi

這篇要以一般的三號AA電池供電給Raspberry Pi(以下簡稱rpi)。

注意:以下所述僅為玩票性質,我只是想試試看玩一玩而已,若你真的打算製作可靠的可攜式電池給rpi,請見本篇末的參考資料。

rpi的電源輸入端是一個USB Micro-B插座,電壓為5V(4.75V~5.25V),電流需要700mA(Model B),消耗功率為3.5W。重點是要有USB Micro-B插頭、供給5V的電壓。

下圖是完成圖:
1,螢幕。
2,rpi板子。
3,USB Micro-B插頭。
4,變壓、穩壓。
5,三號AA電池。


這是rpi板子,請見右上角,USB Micro-B插頭已經插進去了。


要買個USB Micro-B插頭,找出其中5V與GND的腳位,焊接連接線。(很小,很不好焊,焊完後需以萬用電錶的導通檔位測試。)

(買一般的USB連接線的話,就不必辛苦焊接了。但我只買插頭部分,比較省錢。)

這裡用了LM7805線性穩壓器,可以穩定地輸出5V。如果不使用穩壓器直接以電池供電的話,這麼做並不理想,電池(乾電池、充電電池)的電壓會隨著時間而減低,而且很難組合出適合rpi的電壓。


我原本使用了四顆充電電池,總電壓只有5.65V,以致於輸出電壓不夠,所以又加了一顆乾電池,總電壓變成7.05V,經過穩壓器後輸出電壓為4.98V,正好。


底下是穩壓器的線路,LM7805有三根腳(在此假設圖中最下方為1號腳),1號腳要接輸入的正極,2號腳接輸入的負極。

然後是0.47μF電解電容,正極接LM7805的1號腳,負極接LM7805的2號腳。(我查閱的電路圖是0.33μF,但我手邊沒有,改用0.47μF。)

然後是0.1μF陶瓷電容,無極性,一腳接LM7805的2號腳,一腳接LM7805的3號腳。

最後,LM7805穩壓器的輸出為2號腳(負極、下圖的橘線)、3號腳(5V、下圖的黃線)。




好,如果你不怕弄壞rpi板子的話,就勇敢地接上去吧。嘿,如果有東西燒壞了,可別怪我喔。

哇,成功囉,正常開機囉。


登入了。


執行X Window也沒問題。


呼~還好我的rpi沒燒壞。不過,我前面已經說過,這只是玩票性質而已,沒多久我就拔掉不用了。

如果你想製作可靠的可攜式電池的話,請參考Running a Raspberry Pi from batteries,裡面介紹了各種方案,線性穩壓器(linear regulator)、切換式穩壓器(switching regulator)、直流變壓器、市面上販賣的電池包、等等,而且還教我們計算電池的供電時間,非常棒的一篇文章。


另外,也可到rpi的官方論壇,以關鍵字「battery、AA」搜尋,有很多人都在討論各種可行方法。

另外,這裡FS: Raspberry Pi Battery Pack and Solar Charger Ver 5,以四顆充電電池與太陽能面板供給rpi電力,相當不錯。

2012/08/01

Raspberry Pi可以跑Android 4.0囉

Raspberry Pi常見問答集裡,其中有一問題是「能不能跑Android啊」,原本答案是否定的,但是,現在可以囉!Raspberry Pi基金會發佈了Android 4.0(Ice Cream Sandwich)移植成功可在Raspberry Pi上運行的消息,請先看看影片Android ICS on Raspberry Pi 吧。

心動了嗎,趕快下載映像檔試試看吧。

注意,這些都不是正式釋出版本,沒有硬體加速,跑起來很慢。

CyanogenMOD 9 映像檔。Android 4.0 (ICS)。
CyanogenMOD 7.2 映像檔。Android 2.3 (Gingerbread)。
CyanogenMOD 7.2 映像檔(with Ethernet Menu)

我先試了CyanogenMOD 9 映像檔,哇,出現ANDROID字樣囉,


然後出現時間日期與鎖頭,


可惜,等了許久卻出現錯誤訊息,整個當掉不動了,唉。


換CyanogenMOD 7.2 映像檔試試看,


開機成功囉,非常慢,連滑鼠移動都很慢,


大部分軟體都不能執行,


進去設定選單看看,


這是Android 2.3版。


底下是Raspberry Pi官方網頁秀出來的截圖,嗚~我也想要4.0啦。



參考資料:
树莓派论坛,[新闻]Raspberry Pi 运行Android ICS(4.0)提供下载地址