2017年1月5日 星期四

「晚餐吃什麼 - 找附近餐廳」 - 我的第一個 Android APP

不知道要吃什麼嗎?「晚餐吃什麼」可以列出附近的餐廳,並且幫你從中挑選一家!你也可以根據自己的需求,指定搜尋距離以及餐廳類型。
「晚餐吃什麼?附近有什麼吃的?」

「晚餐吃什麼 - 找附近餐廳」是我的第一個 Android APP。它的目標是為了解決人生最重要的一個疑問:

晚餐到底要吃什麼?


這個 APP 可以在 Google Play 下載,連結為:https://play.google.com/store/apps/details?id=com.pychen0918.wheretoeat

搜尋附近餐廳,隨機選出餐廳


無法決定要吃什麼嗎?這個 APP 可以列出指定距離與類型的附近餐廳,甚至幫你從中隨機挑選一家。選擇餐廳後,可以直接觀看餐廳照片、評價、距離、營業時間及其他基本資訊。還可以開啟地圖進行導航、直接撥號給餐廳詢問或是訂位、或者開啟瀏覽器,連上餐廳網站等,相當方便易用。

無論是出外旅行時,使用這個 APP 尋找當地美食;或是在一成不變的上班族生活中,利用「晚餐吃什麼」在公司附近尋找食物,都可以為你省去煩惱時間。

  一覽餐廳照片、評價、距離、營業時間等資訊。還可以直接打開地圖觀看餐廳所在地、撥打電話訂位、或連上餐廳網站

選擇餐廳類別,快速瀏覽餐廳列表


餐廳類型包含一般餐廳、咖啡簡餐、酒吧夜店等多種選擇。不想出門的話,也可以選擇只尋找擁有外送服務的店家。在餐廳列表中,可以非常快速瀏覽餐廳名稱、類型、評價、距離等資訊,方便進行挑選。


可使用餐廳類別挑選、過濾附近餐廳,選項包含一般餐廳、咖啡簡餐、酒吧夜店等  根據指定條件,列出附近餐廳。可以快速瀏覽餐廳名稱、類型、評價、距離等重要資訊


自訂關鍵字搜尋功能、觀看顧客評論


如果沒有適合的類別,也可以選擇自訂關鍵字進行搜尋,它會從餐廳名稱、提供的餐點內容、顧客評論等等資訊,找出相符的店家。如果有想吃的料理種類(例如日式料理、牛排等),或是指定餐廳名稱(例如麥當勞、星巴克等),都可以在這裡搜尋。它還會記錄最近使用過的幾條關鍵字,方便下次使用。

在餐廳詳細資訊中,也可以看到顧客最近的評價與評等,讓你藉此挑選餐廳。


 可以自訂關鍵字進行搜尋。例如使用餐廳名稱(例如麥當勞、星巴克)或是餐點類別(例如日式料理、牛排店)等等  在餐廳資訊頁面中,會列出顧客的最近評論與時間

分享餐廳列表與詳細資訊、全球皆可使用


無論在餐廳列表頁面或是餐廳資訊頁面,都有分享按鈕,可以透過 Line、Facebook、Gmail 等等 APP ,將資訊發送給其他人,參與討論。

「晚餐吃什麼」支援中英文介面,同時可以顯示日文、韓文及世界各國文字。由於資料來源是 Google 地圖服務,這個 APP 在世界各地都可以使用。

下載連結、聲明


這個 APP 可以在 Google Play 商店下載,歡迎各位留下評論:
https://play.google.com/store/apps/details?id=com.pychen0918.wheretoeat

「晚餐吃什麼」所使用的圖示來自 www.flaticon.com ,為免費授權。由 Freepik、Madebyoliver、Pixel Buddha、Vectors Market等作者製作。(Icons made by Freepik, Madebyoliver, Pixel Buddha, and Vectors Market from www.flaticon.com)

2016年3月28日 星期一

台灣花季地圖

這份「台灣花季地圖」紀錄了我從網路上收集而來的賞花資訊。包括台灣各地的賞花景點以及它們的花期。希望大家能有機會利用此地圖,安排出外旅遊行程。

本地圖連結為
https://www.google.com/maps/d/viewer?mid=zRgPfb9BIGoE.kpr_bUJzRbC0





使用說明


地圖上每個標記都是一個賞花景點,不同顏色代表不同的行政區域。點選圖示後會顯示該地點在不同月份盛開的花卉種類等等資訊。



點選左上角的圖示可以進一步開啟地點列表,方便尋找。



電腦版使用者可以利用地圖左下角的 + / - 符號,或是滑鼠滾輪來放大、縮小地圖。在地圖的空白地方按住滑鼠左鍵,可以移動地圖。

點選右上角的方框圖示,或進入本地圖連結
https://www.google.com/maps/d/viewer?mid=zRgPfb9BIGoE.kpr_bUJzRbC0
可以進入全螢幕模式,將有更大的空間瀏覽地圖。

在全螢幕模式中,可以使用搜尋功能(點選列表上端的放大鏡圖示),搜尋想要尋找的地點或是花卉種類等等。



資料來源


這份地圖的資料來源主要來自於
其中特別感謝玩台灣賞花的管理人員、以及初心小格的格主小白鯨同意分享資料。


更多資料


這份地圖將會不定時更新,並修改、加入更多的賞花資料,歡迎大家經常回來點選觀看。另外,也歡迎大家提供自己知道的賞花景點資訊。

2016年1月31日 星期日

Node.js 的中文與英文髒話過濾

在聊天室之類的應用之中,我們時常會需要使用到髒話過濾的功能。這篇文章簡單描述一下個人在後端過濾中文與英文髒話的方法。

Mikey Wilson, 2002

檢測髒話


首先,讓我們試著判斷一個句子是否含有任何髒話。注意中文與英文的判斷方式略有不同,底下將分開討論。

2016年1月6日 星期三

利用 Async.js 平行處理多筆 MongoDB 查詢

在使用 MongoDB 資料庫的時候,我們常常需要同時送出多筆查詢,並在所有資料都讀取完畢後進行下一步動作,例如將資料顯示在網頁上。這篇文章將會簡單介紹利用 Async.jsmongoose 達成上述需求的方法與程式碼。

Copyright jaketrent.com

Async.js 簡介


在 javascript 中雖然有著 callback 機制,可以在事件完成後接著執行其他動作。但如果要等待很多事件完成,可能會寫成很多層的 callback:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
var findData = function(input, callback){
    var data = new Data;
    findMoreData(inputA, function(dataA){
        data.add(dataA);
        findMoreData(inputB, function(dataB){
            data.add(dataB);
            findMoreData(inputC, function(dataC){
                ...
            });
        });
    });
    callback(data);
}

這種寫法不僅難看,而且由於並非平行處理,在每個 findMoreData 函式中等待 I/O 處理的時間都會累積起來,影響效能。

我們可以使用 Async.js 套件提供的 parallel 方法,來改寫上面的程式碼:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
var async = require('async');
var findData = function(input, callback){
    async.parallel({
        A: function(cb){
            findMoreData(inputA, function(dataA){
                cb(null, dataA);
            });
        },
        B: function(cb){
            findMoreData(inputB, function(dataB){
                cb(null, dataB);
            });
        }, 
        ...
    }, 
    // 這裡是 async.parallel 的 callback function
    function(err, results) {
        // 在這裡 results 的內容是個物件:{A: dataA, B: dataB, ...}
        callback(results);
    });
}

上面的 async.parallel 方法接受一個物件當作參數,裡面包含各個需要平行處理的工作。範例中為了獲得資料 A,我們呼叫了  findMoreData 來處理,而在 findMoreData 等待 I/O 處理的時間中,parallel 會試著繼續處理資料 B 的部分。因此,我們可以平行化的處理這些工作,不用等待前一個完成才接著執行。

程式碼中的 cb 是 Async.js 的一個 callback function。執行後,parallel 就會認為這部分的工作已經完成,而我們要確保每個工作都會呼叫到 cb,整個 parallel 的處理流程才會正常結束。cb 接受兩個參數,第一個是 error 物件(可以為 null),第二個則是我們要的資料,它會存到 results 裡面。

等到獲得所有資料,最後一段的 callback function就會被執行,讓使用者可以處理後續工作。

parallel 與 Mongoose 的整合


對於 Async.js 有簡單的認識後,我們正式把 mongoose 讀取資料的 API 整合進來:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var async = require('async');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var dataASchema = new Schema({...});
var dataAModel = mongoose.model('dataA', dataASchema);
...
var findData = function(callback){
    async.parallel({
        A: function(cb){
            dataAModel.find({}, function(err, data, count){
                cb(null, data);
            });
        },
        B: function(cb){
            dataBModel.find({}, function(err, data, count){
                cb(null, data);
            });
        }, 
        ...
    }, 
    function(err, results) {
        callback(results);
    });
}

這邊用 find 函式來查詢資料庫的資料,並把得到的全部結果原封不動的存放到 results 去。藉由修改 find 的參數,我們也可以在這邊針對不同資料,自訂各項查詢條件。

改用 each 取得資料


使用 parallel 可以自訂要同步執行的不同工作,因此有著很大的彈性。但有時候我們所需要的只是針對不同資料執行相同的工作,用 parallel 必須要不斷的重複相同的程式碼(如上例中,不斷重複 find 的動作),看起來有些雜亂。

Async.js 另外提供其他流程控制與資料處理方法,其中一個方法是 each,它可以針對多筆資料平行處理相同的一件工作。例如以下範例,使用 each 來取得資料:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
var findData = function(array, callback){
    var results = {};
    async.each(array, function(item, cb){
        var model = mongoose.model(item);
        model.find({},function(err, data, count){
            results[item] = data;
            cb();
        });
    },
    function(err){
        callback(results);
    });
}

findData(['dataA', 'dataB'], function(results){
    // results 物件的內容為 {'dataA': ... , 'dataB': ...}
    // 省略部分為資料庫中的資料
});

我們輸入一個參數 array,裡面存放不同 MongoDB collection 的名字,async.each 會去讀取這些 collection 名字,再利用 find 找到當中的所有資料,存放到物件變數 results 當中,最後呼叫 cb 結束。

結論


這篇文章簡單介紹 Async.js 裡的 parallel 與 each 兩種方法,並配合 mongoose 來達到平行處理多筆 MongoDB 查詢的方法。

2015年10月21日 星期三

在 Raspberry Pi 上使用和測試 XBee

這篇文章介紹把 XBee module 裝到 Raspberry Pi 上面的方法,並且使用 PC 上的 XCTU 軟體與 Node.js 測試它的連線。

硬體材料


下面是我們所需要的硬體材料:
  • 兩片 XBee module,請務必使用相同規格。
  • 四條杜邦線,母對母佳。
  • 一台樹莓派
  • 任何一種可以把 XBee module 連接上電腦的方法與材料。例如使用 SparkFun XBee Explorer,或是 Arduino XBee shield



PC端設定


在這裡我用的是Arduino XBee shield。把它如下圖的方式插好,接上電腦。注意有個開關會控制XBee的資料輸出,記得把它扳到USB那端去


另外,為了不讓 Arduino 裡的程式碼干擾 XBee 運作,請先燒錄 BareMinimum 範例程式,或是任何不會使用 serial console 輸出的程式碼。

測試軟體與模組設定


到 Digi International 的官方網站去,下載 XCTU 這套軟體。它有兩種版本,一個是較新的6.x.x版,有著比較漂亮好操作的介面,另一種是舊版的5.2.8.6。我們底下的說明以新版的6.2.0為主,而網路上有多數教學資源是使用舊版本。

打開 XCTU,點選主畫面左上的按鈕,新增 XBee Module。


如果你的模組有正確連接上 PC,它應該會以 COM port 的方式出現在列表中。點選正確的 COM port 後,按下 "Finish" 讓軟體連線。



順利連線後,可以在主畫面左側看到它。點選它會開始讀取它的出廠設定。我們可以在畫面右端看到許多設定,首先請特別注意下面幾個選項:

  • CH (Operating Channel):這是指 XBee module 的工作頻道。注意 XBee - 或者說 ZigBee/802.15.4 - 使用的是 2.4GHz,會跟 Wi-Fi / 802.11 衝突。如果環境中有 Wi-Fi 訊號,請改用不同頻道或是避開它。更多資訊請參考這裡
  • ID (Network ID):要互相通訊的XBee module,必須把此值都設定為相同。每個XBee module只會接收、轉發跟自己相同ID的訊息。
  • CE (Routing/Messaging Mode):在 ZigBee 的網路中,每個節點都會是 Coordinator、Router 或 End Device 其中之一。在這邊我們可以把 Module 都設定成 Router 以方便測試。
  • DH/DL (Destination Address):要通訊的端點位址。在單純一對一的通訊中,可以把此欄位設定為對端的 SH/DL (Source Address)值,雙方會以 unicast 的方式互通。如果把 DH 設定為 0,DL 設定為 FFFF,則封包會以廣播方式發送。在這邊我們把兩片 Module 都設定為廣播。
  • NI (Node Identifier):給 XBee module 取個名稱,方便辨識。

設定完成後,按下上方的鉛筆圖案,將設定寫入 XBee module。記得兩片 Module 必須要擁有相同的 CH、ID 值,並且要有適當的 DH/DL。




樹莓派端的準備工作


Raspberry Pi B+的接線方式如下圖,也就是分別是把XBee module的3.3V電源、Tx、Rx與Ground接到Raspberry Pi的對應腳位。如果你用的是不同的板子,請注意更換到對應的針腳。

Copyright https://dzone.com/articles/connecting-xbee-raspberry-pi

另外,在 Raspbian 中,UART  Tx/Rx 腳位預設是給 serial console 使用。我們要改接 XBee 的話,需要做出一些對應的修改。

首先編輯 /boot/cmdline.txt 檔案,把所有 ttyAMA0 相關的字眼都移除。最後檔案內容看起來會像這個樣子。

dwc_otg.lpm_enable=0 console=115200 console=tty1 root=/dev/mmcblk0p6 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait

然後停用 tty console:
sudo systemctl stop serial-getty@ttyAMA0.service
sudo systemctl disable serial-getty@ttyAMA0.service


如果有在 /etc/initab 設定過 tty console,也記得拿掉任何會占用到 ttyAMA0 的服務。

連線測試


我們會進行兩種測試,一種是測試雙方連線的訊號強度,另一種則是進行訊息的收發。

訊號強度測試


首先我們回到 XCTU,找到目前與PC連線中的裝置,再點旁邊的藍色小按鈕,軟體會自動搜尋網路上的其他節點。當全部跑完後,選擇我們要連線的對象,按下 "Add selected devices" 按鈕。




完成後,再到畫面上方點選工具箱按鈕,選擇 "Range Test"。


在下一個畫面中,分別在左側點選目前連線到 PC 的 Module,再到右側點選我們早先搜尋到的其他 Module。下面有些測試項目可以調整。我們使用 "Cluster ID 0x12" 這個測試方式。它會送出一串帶有 0x12 這個特殊 Cluster ID 的封包。收到這個 ID 封包的裝置,會將其收到的內容原封不動的送回。

最後,按下 "Start Range Test" 開始測試。畫面會顯示封包的丟失率,以及 RSSI 值等等。能成功跑完此測試代表我們的兩片裝置都能正常運作,連接的線路也都正確。



封包收發測試


我們接著在 Raspberry Pi 上面撰寫 Node.js 程式,用來收發封包。目標是在 XCTU 上收到 Raspberry 送過來的 XBee 封包,同時在 XCTU 也可以送出封包給 Raspberry Pi。

首先在 XCTU 上開啟 console 模式。點選右上角的電腦終端機符號,並在主畫面點選連線的符號。XCTU 就會跟 XBee module 建立終端機連線。



接著到 Raspberry Pi 這邊,先透過 NPM 安裝兩個必要套件:

npm install xbee-api serialport

如果安裝過程中出了錯誤,通常都是套件庫裡面沒有適合目前平台與 Node.js 版本的套件。請試著參考這篇,安裝新版的 npm 與 Node.js。

接著開啟任何你喜歡的文字編輯器,輸入下面這段程式碼:

 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
var util = require('util');
var SerialPort = require('serialport').SerialPort;
var xbee_api = require('xbee-api');
var C = xbee_api.constants;

var xbeeAPI = new xbee_api.XBeeAPI({
    api_mode: 1
});
var serialport = new SerialPort("/dev/ttyAMA0", {
    baudrate: 9600,
    parser: xbeeAPI.rawParser()
});

serialport.on("open", function () {
    var frame_obj = { 
        type: 0x10, 
        id: 0x01, 
        destination64: "000000000000ffff",
        broadcastRadius: 0x00,
        options: 0x00, 
        data: "Hello world" 
    };  
    serialport.write(xbeeAPI.buildFrame(frame_obj));
    console.log('Sent to serial port.');
});

serialport.on('data', function (data) {
    console.log('data received: ' + data);
});

// All frames parsed by the XBee will be emitted here
xbeeAPI.on("frame_object", function (frame) {
    console.log(">>", frame);
});

此程式會以廣播方式,送出一個包含 "Hello World" 字串的封包,接著等待任何進來的封包,並在螢幕上顯示出來。我們直接以 node 指令執行這段程式碼。

回到 XCTU,應該可以看到以紅字顯示的訊息,這就是我們剛剛送來的封包。可以將整段封包內容全選複製起來,然後使用工具箱內的 "Frames Interpreter" 來檢視它的內容。



接著我們要產生封包送回去。打開工具箱內的 "Frames Generator" 工具。注意裡面要調整的參數:
  • Mode:選擇 API 1,我們上面的 Node.js 程式碼預期收到 API 1 格式的封包。
  • Frame Type:請選擇 "0x10 - Transmit Request"。
  • 64-bit dest. ID:請輸入000000000000FFFF,也就是廣播位址。
  • RF data:在ASCII這邊輸入你想要夾帶的訊息。
完成後,按下 "Copy Frame" 按鈕,將整個封包複製起來,再按下 Close 離開。





回到主畫面後,按下 Send Packet 欄位的小加號按鈕,切換到 HEX 模式後,貼上我們剛剛複製的內容。完成後,按下 Add Packet 離開。


最後一步了,在主畫面選擇我們剛剛建好的新封包,按下右側的 "Send selected packet" 按鈕。主畫面上會以藍字顯示我們剛剛丟出去的訊息。


回到 Raspberry Pi 上,它會將我們剛剛送出的訊息顯示出來,大功告成!



結論


這篇文章紀錄了我在 Raspberry Pi 上使用和測試 XBee module 的過程,包括一開始的接線到作業系統上的調整。同時也列出了使用 PC 軟體 XCTU 對兩片 XBee module 進行測試與設定的方法及步驟。我們除了可以使用 XCTU 內建的工具測試訊號強度以外,還可以使用文中附上的 Node.js 程式碼,測試封包發送與接收的能力。

參考資料


xbee-api on NPM, by jouz
https://www.npmjs.com/package/xbee-api
"Connecting XBee to Raspberry Pi", by Sony Arouje
https://dzone.com/articles/connecting-xbee-raspberry-pi
"XBee 模組通訊實驗", by 網昱多媒體
"XBEE S1 – Sending Remote AT commands using API packet to toggle an I/O", by alselectro

2015年10月15日 星期四

在Raspberry Pi上面安裝Node.js與MongoDB

這文章簡單記錄在Raspbian上面安裝Node.js與MongoDB的方式。


Node.js


Raspbian套件庫裡面的Node.js版本很舊,大約是0.10.x,無法符合許多新套件的需求。網路上可以找到很多安裝方式,但他們大多不可行。下面我只列出一個最簡單有用的方法。

wget http://node-arm.herokuapp.com/node_latest_armhf.deb
sudo dpkg -i node_latest_armhf.deb

這兩行指令會從node-arm.herokuapp.com這個網站下載打包好,供ARM使用的deb檔案,再用dpkg安裝它。

node的版本是4.0.0,npm版本是2.14.2。

MongoDB


官方沒有提供給ARM使用的版本,所以我們可以自己下載原始碼進行編譯,或是下載別人編譯好的檔案。

我們可以在這裡找到一個事先編譯好的版本:
https://github.com/brice-morin/ArduPi

直接使用mongodb-rpi/bin裡面的檔案即可。比較完整的指令為:

svn co https://github.com/brice-morin/ArduPi/tree/trunk/mongodb-rpi
sudo cp -r mongodb-rpi/mongo/ /opt/
sudo ln -s /opt/mongo/bin/* /usr/local/bin/

這幾行指令會先用svn下載我們要的檔案,把MongoDB的執行檔都安裝到/opt下面,再建立symbolic link,方便我們存取。

mongo的版本是2.1.1。

總結


網路上有很多在Raspbian安裝這兩樣東西的文章,但多數無法成功運作。在這邊把一個在我的板子上(Raspberry Pi B+)可以運作的方式記錄下來,供有需要的人參考。


(2016/3/16補充:關於 Node.js 在樹莓派上面的安裝方式,可以參考 http://swf.com.tw/?p=836 這篇文章,有許多詳細資訊。安裝 MongoDB 的方式,可以參考 http://swf.com.tw/?p=833)

2015年9月14日 星期一

快速建立與開放本地HTTP伺服器至網際網路

這篇文章簡單敘述如何使用node.js的http-server套件與ngrok,快速在本地端建立HTTP伺服器並公開至網際網路。

Copyright ngrok.com

快速建立HTTP伺服器


在開發網站的過程中我們經常需要建立HTTP伺服器進行測試。雖然在Linux上有很多HTTP伺服器套件可用,但他們大多需要相當繁複的設定。當只是在本地端進行初步開發之時,我們其實可以使用下面介紹的http-server,快速建立本地端伺服器。

我們先在本地端寫一個簡單的index.html網頁:


接著透過npm取得http-server套件
npm install -g http-server
安裝完後,在index.html的目錄下執行http-server



預設情況下,它會抓取當前目錄下的網頁,並在port 8080上建立伺服器。此時可用本地瀏覽器連到http://localhost:8080看看是否已經正確執行。

將本地伺服器開放到網際網路


在協同開發過程可能還會需要將此網站提供給他人使用,此時我們可以利用ngrok將本地網站開放到網路上。

首先先去ngrok的官方網站下載軟體。在這裡以Linux為例,將下載下來的zip檔解壓後,透過下面的指令執行:
./ngrok http 8080
這個指令會將本地port 8080的HTTP伺服器開放出去。執行後可以看到這個畫面:



其他人就可以透過圖中的這個網址連線到這台伺服器。


注意這個網域名稱是會改變的,也就是每次重新使用ngrok時都會產生不同的名稱。如果有固定域名的需求,可以利用ngrok的付費服務達成。

TL;DR:


npm install -g http-server
http-server ./
ngrok http 8080

透過http-server與ngrok,只要簡單的幾個指令,不需要繁複的設定就可以建立起HTTP伺服器,很適合開發階段使用

http-server套件的網頁,可以在這裡看更詳細的使用方式:

ngrok的官方網站:
https://ngrok.com/


(附註:使用python也可以快速建立HTTP server:python -m SimpleHTTPServer 8080)