Use Node.js DDP Client on Arduino Yun to Access Meteor Server
概述
在Arduino Yun上安裝 Node.js, 並測試與 Meteor 通過 DDP Client 通訊。
所用硬件
- Arduino Yun
- SD Card(至少 2GB,我使用的是 8G )
所用軟件
PC 軟件:
- Arduino IDE
- SSH Terminal(Putty / Screen)
升級 Arduino Yun固件
從網站上下載 Arduino Yun 最新的 openWRT 固件, 當前升級包的版本是 YunSysupgradeImage_v1.5.3.zip。
選用 MS-DOS 方式格式化 SD Card,將所下載的 ZIP 文件展開出的 bin 文件拷貝到 SD Card 的根目錄下。
將 SD Card 插入 Arduino Yun, 並重啓 Arduino Yun。正常情況下,應該能夠在 Web Page 點按 “RESET” 按鈕執行升級。
也可以用 SSH 登陸後,先檢查一下 /mnt/sda1 目錄下是否有升級文件,並執行以下命令。
# ls /mnt/sda1
openwrt-ar71xx-generic-yun-16M-squashfs-sysupgrade.bin
# run-sysupgrade /mnt/sda1/openwrt-ar71xx-generic-yun-16M-squashfs-sysupgrade.bin
- 升級完成後,選擇 ArduinoYun 的初始 WIFI,並通過 Web 頁面訪問 192.168.240.1,設置 Arduino Yun 的網絡連接。
利用 SD Card 擴展 Arduino Yun 空間
Arduino Yun 的板上存儲僅有 16M,要想安裝更多地東西就必須利用 SD Card 擴展磁盤空間。參考鏈接
重啓後,通過本地局域網,SSH 到 Arduino Yun上,檢查現有的分區情況。
# df Filesystem 1K-blocks Used Available Use% Mounted on rootfs 7104 364 6740 5% / /dev/root 7680 7680 0 100% /rom tmpfs 30560 148 30412 0% /tmp tmpfs 512 0 512 0% /dev /dev/mtdblock3 7104 364 6740 5% /overlay overlayfs:/overlay 7104 364 6740 5% / /dev/sda1 7746772 1008 7745764 0% /mnt/sda1
通過 USB 連接 Arduino Yun,打開 Arduino IDE 上傳此 ino 文件, 然後打開 Serial Monitor,在屏幕右下方選擇 “Newline”, 並按照提示進行操作。下面的操作記錄了我爲分區方式,供參考。需要注意的是,此處輸入的 4096 的意思是,將 4G 的空間保留爲 /mnt/sda1 下的數據存儲,剩下的空間留給根目錄。整個過程需要等待幾分鐘。
Open the Serial Monitor and double check the dropdown menu "Newline" has been selected. This sketch will format your micro SD card and use it as additional disk space for your Arduino Yun. Please ensure you have ONLY your micro SD card plugged in: no pen drives, hard drives or whatever. Do you wish to proceed (yes/no)? yes Starting Bridge... Ready to install utility software. Please ensure your Arduino Yun is connected to internet. Ready to proceed (yes/no)? yes Updating software list... Software list updated. Installing software (this will take a while)... e2fsprogs mkdosfs fdisk rsync installed Proceed with partitioning micro SD card (yes/no)? yes Enter the size of the data partition in MB: 4096 Partitioning (this will take a while)... Micro SD card correctly partitioned Creating 'arduino' folder structure... Copying files from Arduino Yun flash to micro SD card... Enabling micro SD as additional disk space... enabled We are done! Yeah! Now press the YUN RST button to apply the changes.
按下 YUN RST 後等待系統完全重啓,再次檢查分區情況,可以看到,Root 目錄已經被放置在 /dev/sda2 下了,大小已經被擴大。
# df Filesystem 1K-blocks Used Available Use% Mounted on rootfs 3556712 120920 3257464 4% / /dev/root 7680 7680 0 100% /rom tmpfs 30560 100 30460 0% /tmp tmpfs 512 0 512 0% /dev /dev/sda2 3556712 120920 3257464 4% /overlay overlayfs:/overlay 3556712 120920 3257464 4% / /dev/sda1 4186104 12 4186092 0% /mnt/sda1
安裝 Node.js
確認 Arduino Yun 可以正常聯網,然後執行以下安裝 Node.js, 原文參考鏈接
# opkg update
# opkg install node
安裝完成之後,可以用以下命令檢查 Node.js 安裝成功
# node -e "console.log('Hello_Yun')"
Hello_Yun
Arduino Yun 可用的所有 Package 可以在這裏找到。您還可以根據需要,安裝更多的 Node.js 基礎包,例如:
# opkg install node-serialport
# opkg install node-noble
設置 Swap 區
Arduino Yun 提供的 Node.js 包很有限,還需要通過 npm 安裝更多可擴展包。但是因爲 npm 所需的內存比較大,我們必須打開 Swap 才能夠順利執行,否則會報內存不足。
檢查現有的 Swap 設置
# free -m total used free shared buffers Mem: 61116 31712 29404 0 8364 -/+ buffers: 23348 37768 Swap: 0 0 0
SSH 登陸後,創建 Swap 分區,這一步要等比較長的時間。
# mkdir /swap # dd if=/dev/zero of=/swap/yunswapfile bs=1M count=1024 1024+0 records in 1024+0 records out
設置 Swapon,此時已經可以看到 Swap 區正確創建了。
# mkswap /swap/yunswapfile # swapon /swap/yunswapfile # free -m total used free shared buffers Mem: 61116 59704 1412 0 7676 -/+ buffers: 52028 9088 Swap: 1048572 0 1048572
設置啓動時自動加載 Swap。重啓 Arduino Yun 後,Swap 區應該正常。
# uci add fstab swap # uci set fstab.@swap[0].device=/swap/yunswapfile # uci set fstab.@swap[0].enabled=1 # uci set fstab.@swap[0].fstype=swap # uci set fstab.@swap[0].options=default # uci set fstab.@swap[0].enabled_fsck=0 # uci commit
安裝 DDP Client
此時,我們再使用 npm 安裝 DDP Client。我使用的 DDP Client 是 https://www.npmjs.com/package/ddp
# npm install ddp
# npm install underscore
# npm list
/root
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └─┬ [email protected]
│ │ └── [email protected]
│ └─┬ [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └─┬ [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├─┬ [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ └── [email protected]
│ ├─┬ [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ └── [email protected]
└── [email protected]
使用 DDP Client 訪問 Meteor
下面的參考實現展示瞭如何用 DDP Client 與 Meteor 交互。
var DDPClient = require("ddp"),
_ = require('underscore');
var ddpclient = new DDPClient({
// url: 'ws://192.168.199.240:3000/websocket'
url: 'ws://mcotton-01.chinacloudapp.cn/websocket'
});
var useremail = "[email protected]";
var pwd = "123456";
var user_id = null, token = null;
var appkit_weather_station, myappkit_weather_station, my_app_kit_id;
var data_observer, control_observer;
/*
* Connect to the Meteor Server
*/
ddpclient.connect(function (error, wasReconnect) {
// If autoReconnect is true, this callback will be invoked each time
// a server connection is re-established
if (error) {
console.log("DDP connection error!");
return;
}
if (wasReconnect) {
console.log("Reestablishment of a connection.");
}
console.log("connected!");
ddpclient.call("login", [
{user: {email: useremail}, password: pwd}
], function (err, result) {
console.log(result);
user_id = result.id;
token = result.token;
if (token) {
console.log("Logined!", user_id, token);
//var observer = ddpclient.observe("appkits");
/*
* Subscribe to a Meteor Collection
*/
ddpclient.subscribe(
"appkits", // name of Meteor Publish function to subscribe to
[], // any parameters used by the Publish function
function () { // callback when the subscription is complete
console.log("appkits all ==> ", ddpclient.collections.appkits);
appkit_weather_station = _(ddpclient.collections.appkits).findWhere({name: 'Weather Station'});
console.log("appkits weather_station ==> ", appkit_weather_station);
console.log("appkits weather_station id ==> ", appkit_weather_station._id);
// Create myAppKit
var myAppKit = ddpclient.call(
'myAppKitInsert',
[{
name: 'My Weather Station',
app_kit_id: appkit_weather_station._id
}],
function (err, result) {
console.log('myAppKitInsert, error: ' + error);
console.log('myAppKitInsert, result: ' + result._id);
my_app_kit_id = result._id;
/*
* Subscribe to a Meteor Collection
*/
ddpclient.subscribe(
"myappkits", // name of Meteor Publish function to subscribe to
[user_id], // any parameters used by the Publish function
function () { // callback when the subscription is complete
console.log("myappkits all ==> ", ddpclient.collections.myappkits);
console.log("appkit_weather_station id ==> ", appkit_weather_station._id);
myappkit_weather_station = _(ddpclient.collections.myappkits).filter({_id: my_app_kit_id});
console.log("myappkits weather_station ==> ", myappkit_weather_station);
}
);
ddpclient.subscribe(
"dataevents", // name of Meteor Publish function to subscribe to
[user_id], // any parameters used by the Publish function
function () { // callback when the subscription is complete
console.log("dataevents all ==> ", ddpclient.collections.dataevents);
}
);
ddpclient.subscribe(
"controlevents", // name of Meteor Publish function to subscribe to
[user_id], // any parameters used by the Publish function
function () { // callback when the subscription is complete
console.log("controlevents all ==> ", ddpclient.collections.controlevents);
}
);
/*
* observe DataEvents
*/
data_observer = ddpclient.observe("dataevents");
data_observer.added = function (id) {
console.log("[ADDED] to " + data_observer.name + ": " + id);
var event = _(ddpclient.collections.dataevents).findWhere({_id: id});
console.log("[ADDED] dataevents ", event)
};
/*
* observe ControlEvents
*/
control_observer = ddpclient.observe("controlevents");
control_observer.added = function (id) {
console.log("[ADDED] to " + control_observer.name + ": " + id);
var event = _(ddpclient.collections.controlevents).findWhere({_id: id});
console.log("[ADDED] controlevents ", event)
};
/*
* Send new Control Event to Server
* */
var newControlEvent = ddpclient.call(
'controlEventInsert',
[{
my_app_kit_id: my_app_kit_id,
control_name: "Status",
control_value: "true"
}],
function (err, result) {
console.log('controlEventInsert, error: ' + error);
console.log('controlEventInsert, result: ' + result._id);
});
}
);
}
);
}
});
////Debug information
//
//ddpclient.on('message', function (msg) {
// console.log("ddp message: " + msg);
//});
/*
ddpclient.on('socket-close', function (code, message) {
console.log("Close: %s %s", code, message);
});
ddpclient.on('socket-error', function (error) {
console.log("Error: %j", error);
});
*/
// close
setTimeout(function () {
// observer.stop();
ddpclient.close();
}, 5000);
});
代碼地址
https://github.com/iascchen/arduino_study/tree/master/src/yun_ddp
轉載請註明出處
Author : iascchen(at)gmail(dot)com
Date : 2015-6-3
Github : https://github.com/iascchen/arduino_study
新浪微博 : @問天鼓