oneDevice.js - 用NodeJS驅動硬件的驅動包

用NodeJS驅動硬件。node

心塞塞的,device被佔用了,只能叫onedevice了。這感受。。。。。。🙄git


這是什麼?

這是一個旨在用NodeJS驅動硬件的驅動包。開箱即用、完善的文檔、持續更新、友好的API。用樹莓派打造本身的智能家居。github

我對智能家居很是感興趣,並一直想要本身定製化diy,用智能化設備控制家裏的一切設備。因此有了這個,是我一直在用的,我會持續的集成更多型號的硬件驅動和傳感器,和更多抽象的接口。算法

DIY,個性化,定製化,徹底可控是oneDevice的核心。express

GitHub地址npm

最近更新

v0.0.2 @ 2018-04-22 17:19canvas

  • SSD1306新增showSystemInfo函數,用於監控系統信息。
  • 直接調用showSystemInfo便可在屏幕上面顯示內存、CPU統計信息、IP、時間。
  • stopSystemInfo函數用於中止刷新系統監控信息。
  • 增長GPIO引腳參考圖,用於查閱樹莓派引腳編號和功能。

報告bug

後續主要更新

  • 增長更多型號硬件驅動
  • 使用RxJS封裝更抽象的API,一句話完成條件監控、動做觸發
  • 更容易閱讀的文檔

test文件夾下面有測試例子,這些都在個人Linux raspberrypi 4.4.34-v7+ Raspbian GNU/Linux 8.0 (jessie)下面測試經過。瀏覽器

必看

0. 樹莓派須要先安裝BCM2835
1. 該軟件在樹莓派3B (Raspbian GNU/Linux 8.0 jessie)上面開發、測試
2. 最新文檔和代碼請查看https://github.com/LanFly/Device
3. 在除樹莓派以外的平臺經過npm安裝此軟件包可能會安裝失敗
4. 該軟件依賴不少第三方軟件,並修改了其中的代碼,感謝各位大佬的開源
5. 因我的時間有限,且硬件種類繁多,代碼中可能有還沒有測試到的bug,能夠經過下面的聯繫方式反饋給我並尋求幫助
6. 我會持續集成其它種類的驅動,若有硬件沒驅動的同窗,能夠反饋給我
7. i2c包的做者已經N年不更新了,裏面有個bug,pull request也不處理。使用ssd1306的時候可能會有點問題,本身改一行代碼就好了。
複製代碼

開始

能夠引入全部的模塊,也能夠單獨使用某一個模塊。緩存

  1. 引入全部模塊
var Device = require('onedevice');
複製代碼
  1. 引入某一個模塊
var XFS5152CE = require('onedevice/lib/device/xfs5152ce');
複製代碼

用法:

new Device(option);
複製代碼

option: Object

description: String服務器

此設備的描述。默認值:String('this device has not description')

model: String

此設備的具體型號,若是配置了此選項,則根據型號返回對應的硬件驅動實例。若是device沒有該型號的驅動,則返回null。

目前device集成的驅動型號有:

  • dht十一、dht2二、am2302: 溫溼度傳感器,支持這3個型號
  • xfs5152ce: 科大訊飛XFS5152CE文字轉語音芯片
  • ssd1306: SSD1306爲主控的OLED顯示屏,最大分辨率支持 128*128
  • 更多型號後續會持續集成

interface: String

此通用設備的通訊方式。該選項適用於建立一個自定義設備驅動,或者device沒有集成的設備。例如STC89C52RC單片機使用串口跟樹莓派通訊,則你能夠建立一個串口設備,讓你能和單片機交換數據,而後本身擴展應用層的邏輯驅動。

對於大部分的傳感器或模塊,都使用了常見的通訊協議,例如:i2c、uart、spi、one-wire。驅動這些設備相對來講簡單,由於你無需關心這些協議的原理,只須要處理髮送和接受數據。

device集成的科大訊飛驅動就是經過建立uart串口驅動,而後實現say,sleep,setVolume等函數的。

device目前集成的通訊協議有:

  • i2c: 同步串行總線
  • serial: 串行數據總線,即常見的串口
  • udp: 經過網絡使用udp協議交換數據的設備。例如常見的ESP8266 WiFi模塊,能夠經過WiFi無線傳輸數據。也可使用有線。
  • 後續會持續集成1-wire、SPI等協議。

address: String || Int

此設備的物理接口地址或者I2C從機設備的邏輯地址。若是此設備是串口協議的設備,則此字段表示設備在主機上的物理接口地址。例如USB轉串口設備,則它的地址多是字符串/dev/ttyUSB0

若是此設備是使用I2C協議的設備,則它的值表示該I2C設備的邏輯地址。例如SSD1306模塊,它的值多是Int0x3C,通常用16進製表示,固然你喜歡也能夠用10進製表示。

device: String

I2C設備在主機上的物理接口地址。I2C設備用2個地址來標識,第一個是物理接口地址,第二個是I2C從機的邏輯地址。爲何要2個地址?由於樹莓派GPIO提供了2個I2C物理接口,device須要知道你的設備跟主機上的哪一個接口相連。

它有下面2個可選值:

  1. String '/dev/i2c-0'
  2. String '/dev/i2c-1'

你可使用i2cdetect工具來掃描接在總線上的全部I2C設備,並列出他們的邏輯地址。該工具須要先安裝才能使用。

科大訊飛 XFS5152CE

xfs5152ce這款文字轉語言芯片可使用I2C、SPI、串口等方式通訊,使用3.3V便可知足供電,我使用的是某寶上面的xfs5152ce模塊,帶3W功放,我使用5V電源比較穩定。

這裏使用串口通訊驅動模塊,波特率通常爲9600,波特率根據本身的狀況設置。

var Device = require('onedevice');

var xfs5152ce = new Device({
    description: 'xfs5152ce module',
    model: 'xfs5152ce',
    baudRate: 9600,
    address: '/dev/ttyUSB0'
});
複製代碼

上面返回一個串口驅動的實例,address參數是串口的地址。爲何是串口驅動?由於咱們用的是串口方式驅動芯片的。你須要等待串口打開成功才能使用芯片。

xfs5152ce.on('open', function(next){
    xfs5152ce.say('你好,科大訊飛!');
    next();
});

xfs5152ce.on('error', function(error, next){
    console.log(error);
    next();
});
複製代碼

最好在open回調事件中使用串口功能,在open事件觸發後,你就能夠發送任意長度的文本給芯片朗讀。

say函數會自動維護朗讀隊列。若是當前的文本沒有朗讀完,再次調用say函數會放到隊列中,朗讀完後自動從隊列中取出第一個朗讀,直到隊列中全部的都朗讀完。

open,error,data事件都支持中間件,這跟express框架的洋蔥模型是差很少的。next方法表示下一個中間件,它支持參數,表示更改傳遞給下一個中間件的數據,此更改只做用下一個中間件。請看下面例子。

// 假設串口返回的數據是 data === 1
xfs5152ce.on('data', function(data, next){
    console.log('md-1-start: ', data);
    next(data + 1);
    console.log('md-1-end: ', data);
}).on('data', function(data, next){
    console.log('md-2-start: ', data);
    next();
    console.log('md-2-end: ', data);
}).on('data', function(data, next){
    console.log('md-3-start: ', data);
    next();
    console.log('md-3-end: ', data);
});
複製代碼

上面的執行結果以下:

md-1-start: 1
md-2-start: 2
md-3-start: 1
md-3-end: 1
md-2-end: 2
md-1-end: 1
複製代碼

XFS5152CE驅動實例實現瞭如下函數:

function: say(text, imediately, callback)

@params {String} text: 必需。要朗讀的文本。能夠帶上文本控制標記

@params {Boolean} imediately: 可選。是否當即朗讀。若是爲true,會中斷當前正在朗讀的指令。

@params {function} callback: 可選。發送朗讀指令成功的回調。

儘快朗讀文本。驅動實現了語音指令緩存,上位機能夠連續的屢次調用朗讀函數,而後去處理其它任務。驅動會按照前後順序,自動朗讀完全部語音指令。若是imediately爲true,則會中止正在朗讀的全部文本,轉而當即朗讀text。

function: setVolume(volume)

@params {integer} volume: 設置朗讀的音量。對正在朗讀的文本無效。

設置朗讀的音量。可選值爲1到10之間的整數。包含1和10。1位最小音量,10爲最大音量。默認音量爲3。設置音量後僅對後面的語音指令有效,對正在朗讀的文本不影響。後面優化成實時設置音量。

function: getVolume()

@return {integer}

返回朗讀的音量。

function: sleep(callback)

@params {function} callback: 進入省電模式成功後的回調

讓芯片進入省電模式。發送省電指令後,芯片當即中止當前全部指令,此時芯片的工做電流爲5mA。

function: wakeup(callback)

@params {function} callback: 退出省電模式成功後的回調

讓芯片恢復到待合成模式。進入待合成模式時,驅動會自動朗讀當前未朗讀的文本。

function: voiceLength()

@return {integer}

返回當前未朗讀的語音合成指令數。每調用一次say函數,就會增長一個語音合成指令數量。

function: isBusy()

@return {boolean}

返回芯片是否正在朗讀文本。

function: on(type, callback)

@params {string} type: 監聽的事件類型,能夠是data、open、error

@params {function} callback: 當事件觸發時須要執行的函數

註冊指定事件的回調,當事件發生時會執行回調函數。能夠鏈式調用。採用和express類似的洋蔥模型。

事件類型不一樣,callback回調的參數也不一樣:

data: function(data, next) data是接收到的數據。也多是上一個中間件中調用next(data)傳遞過來的data

open: function(next)

error: function(error, next) error是發生錯誤時的信息。


DHT十一、DHT2二、AM2302系列設備

這些設備比較簡單,使用單總線的方式,只須要一根數據線和樹莓派相連。剩下2根是電源線,分別鏈接正極和負極。也能夠不使用電源線,只使用一根數據線完成數據的讀取、發送,而且從數據線上獲取設備所須要的電源。具體請另行查閱單總線的寄生電路。

var dht11 = new Device({
    model: 'dht11',
    description: 'dht11 sensor',
    address: 4
});
複製代碼

address是傳感器的數據線鏈接在樹莓派GPIO的編號。採用BCM GPIO編號。

dht11.fetch(function(error, temperature, humidity){
    if (error) {
        console.log(error.message);
    }else{
        console.log('溫度: ' + temperature.toFixed(2) + ' 溼度: ' + humidity.toFixed(2));
    }
});
複製代碼

讀取傳感器數據比較簡單。注意:DHT11採樣週期是1s,建議連續讀取數值至少間隔1s以上,不然可能會引發錯誤。DHT22採樣週期2s,建議連續讀取數值至少間隔2s以上。


SSD1306 OLED顯示屏

適合市面上常見的以SSD1306爲主控的小型OLED顯示屏,分辨率在128128之內。大於128128分辨率的未測試,底層驅動使用的是oled-ssd1306-i2cnpm包,它能驅動的均可以驅動。

var ssd1306 = new Device({
    description: 'ssd1306',
    width: 128,
    height: 64,
    address: 0x3c,
    device: '/dev/i2c-1'
});
複製代碼

width、height分別表示分辨率的寬和高。address是i2c的從機地址,這個因硬件設備有所不一樣,你們本身瞭解清楚。device是i2c設備接在樹莓派上的接口地址,這個也是因人而異。對於SPI接口的OLED,目前尚未集成驅動,後續會加上。

如今,我要如何控制屏幕顯示的內容?

1. 讓顯示屏顯示內容,有3種方法,最簡單的一種是直接顯示一張PNG圖片。

ssd1306.drawPNG('path/to/image.png', false, function(error){
    if (error) {
        console.log(error);
    }else{
        console.log('顯示PNG圖片完成');
    }
});
複製代碼

注意,只能爲PNG圖片。爲達到最理想的顯示效果,PNG圖片的分辨率最好和OLED屏幕的分辨率一致。OLED是隻有單色的,不支持彩色,因此顯示的時候圖片會自動轉換爲單色圖片,而後再顯示。有色值的像素點在屏幕上會被點亮,透明的像素點會被熄滅。

2. 圖片是靜態的,我想要由程序實時繪製屏幕該怎麼辦呢?這個也很簡單,使用canvas就行了。

var Canvas = require('canvas');

var canvas = new Canvas(128, 64);
var ctx = canvas.getContext('2d');

ctx.fillStyle = '#FFF';
ctx.fillText("Hello world", 0, 0);
// 上面5行代碼應該不須要解釋吧。都是canvas操做
// 結果就是在canvas上會在左上角顯示一行白色的'hello world'文字

// 而後把canvas的內容顯示到屏幕上
ssd1306.drawCanvas(canvas);
複製代碼

這時你應該能在屏幕上看到'hello world'文字了。drawCanvas函數會把canvas上的像素點一一對應到屏幕上繪製(固然也支持只繪製指定部分的canvas)。因此,你的canvas顯示什麼,屏幕就會顯示什麼。經過程序不斷的更新canvas,而後在合適的時機調用drawCanvas函數刷新屏幕,你就能夠控制屏幕了。

使用canvas的好處不須要我解釋了,學習成本低,完善的API文檔,大量的教程,和各類強大的canvas庫。

須要注意的是,爲了達到最理想的顯示效果,canvas的分辨率最好和屏幕的分辨率一致,而且使用單色繪製canvas,建議用#FFF。由於不管如何,你的OLED都是單色的屏幕,沒法顯示彩色。雖然有彩色的OLED,但目前還沒集成驅動。後續會集成更大尺寸的彩色LCD、TFT屏幕驅動。這是個巨大的工程,但願有懂這方面的軟硬件工程師協助我。

在node中使用canvas須要先安裝。API和在瀏覽器HTML5中的canvas稍微有點區別。請自行查閱資料安裝並使用。

3. 我不想安裝node-canvas,同時又要程序實時繪製屏幕怎麼辦?也簡單,使用內置的簡易圖形API繪製。

前面說了,這是基於oled-ssd1306-i2c封裝的。它自身已經提供了一套簡易的圖形API用於操做屏幕。好比常見的繪製像素,直線,矩形,圓,文字等。使用這個的好處是不須要安裝node-canvas,沒有依賴。輕便、簡單。怎麼用?

它的全部API都掛載在驅動實例的oled屬性上。例如:

ssd1306.oled.drawLine(1, 1, 128, 64, 1);
複製代碼

上面使用自帶的API繪製一條從(1, 1) 到 (128, 64) 的直線。oled屬性就是oled-ssd1306-i2c的實例。更多API請自行查閱它的文檔。

目前node驅動SSD1306屏幕有個缺點,就是刷新率過低,在個人樹莓派3B上最高也就10幀。使用Python能達到30幀流暢。若是使用C++,則能夠很容易達到60幀。我正在努力嘗試提升它的刷新率。

SSD1306 API:

function: drawCanvas(canvas, config)

@params {Canvas} canvas: 要繪製的node-canvas。

@params {Object} config: 可選。配置要繪製的canvas的區域。默認從左上角開始繪製和屏幕同樣大的區域。它有下列幾個屬性:

{
    sx: 開始繪製的點的橫座標。
    sy: 開始繪製的點的縱座標
    sw: 要繪製的矩形的寬度
    sh: 要繪製的矩形的高度
}
複製代碼

複製canvas的像素,一一對應到屏幕上並顯示。經過ctx.getImageData函數獲取canvas的像素信息。繪製前,會處理掉canvas的彩色像素。建議使用#FFF顏色進行繪製。有顏色的像素會被點亮,其他會被關閉。

function: drawPNG(filename, dither, callback)

@params {string} filename: 要繪製的PNG圖片路徑

@params {boolean} dither: 可選。是否啓用抖動算法處理圖片像素,默認爲false。

@params {function} callback: 繪製圖片完成時的回調。若是繪製錯誤,回調函數經過參數傳遞error。成功時爲null。

在屏幕上繪製PNG圖片。爲達到最理想的顯示效果,PNG圖片最好是單色的,背景透明。經過pngparsenpm包處理圖片的像素信息。抖動算法使用的是floyd-steinbergnpm包。

function: showSystemInfo(second)

@params {Number} second: 刷新屏幕的間隔時間,單位秒。若是不傳,或傳0,則只顯示一次。不然會自動每隔幾秒刷新一次。

顯示系統信息,在屏幕上顯示CPU、內存的統計信息,以及IP地址、時間。若是指定了一個時間,則會自動每隔幾秒刷新。

function: stopSystemInfo()

中止刷新系統信息


通用性設備API


UART串口設備

串口使用很是廣泛,也很是簡單。建立一個串口設備很容易。在樹莓派上面使用串口最簡單的方式就是某寶買一個USB轉串口,例如常見的使用ch340芯片的STC下載器,只須要10塊錢包郵,免驅,還自帶3.3V和5V電源接口,很是方便。

var stc89c52rc = new Device({
    description: 'stc89c52rc',
    interface: 'serial',
    address: '/dev/ttyUSB0',
    baudRate: 9600
});
複製代碼

上面是使用USB轉串口和單片機的串口引腳相連,單片機設置好波特率,樹莓派和單片機之間就能夠互相傳輸數據。串口是基於serialport這個很是受歡迎的npm包封裝的。串口設備的serial屬性是serialport的實例,該屬性下面有全部它的方法和屬性。可自行查閱它的文檔。

串口驅動實例有下面這些方法:

function: on(type, callback)

請參考XFS5152CE部分API文檔

function: write(data, callback)

@params {buffer || array} data: 要發送的數據。能夠是buffer或者是array,固然也能夠是字符串,以二進制流發送。

@params {function} callback: 發送數據成功後的回調。若是發送錯誤,則經過參數傳遞error。

向串口設備發送數據。發送時以二進制流發送。

function: driver(name, driver)

@params {string} name: 驅動的名字

@params {function} driver: 實現驅動的方法

未通用設備註冊自定義驅動。實際就是向實例中添加一個屬性名爲name的driver函數。這樣實例即可以直接調用這個函數。只不過driver函數的運行時this指向該實例。例如xfs5152ce就是經過建立通用串口設備,而後註冊驅動函數實現的。

serial.driver('say', function(text, imed, cb){
    ......
}
複製代碼

I2C總線設備

I2C設備稍複雜點,由於它是一對多的。數據的讀取和發送都是經過主機控制。樹莓派自帶I2C接口,因此不須要其它硬件就可使用。不少模塊也是使用i2c協議的,例如常見的AT24C02 CMOS EEPROM存儲器就是使用I2C的典型。單片機教程中常用該例子進行I2C操做的學習。

var i2c = new Device({
    description: 'test i2c',
    interface: 'i2c',
    address: 0x3c,
    device: '/dev/i2c-1'
});
複製代碼

注意i2c通用設備的address地址不是指i2c的物理接口地址,而是指的從機的邏輯地址。device纔是指接口的物理接口地址。i2c驅動是基於i2c這個很是受歡迎的npm包封裝的。實例的i2c屬性就是i2c的實例,它擁有全部的方法和屬性,能夠自行查閱i2c的文檔。SSD1306就是經過建立通用i2c設備,而後經過寄存器操做驅動屏幕。

i2c實例有如下方法:

function: driver(name, driver)

參考上面的driver文檔。

由於i2c方法太多,這裏就沒有進行封裝了,能夠經過i2c屬性訪問各函數。


UDP網絡設備

有些更高級的模塊使用網絡進行數據傳輸。例如ESP 8266 WiFi模塊,能夠將串口的數據經過WiFi以UDP或TCP協議發送出去。這個就好玩了,經過將8266和單片機串口相連,能夠很容易的讓單片機實現網絡鏈接。讓單片機去採集數據,經過網絡發送給樹莓派,或者樹莓派發送控制命令,遠程遙控單片機。8266模塊使用UDP是最簡單的方法。

var esp = new Device({
    description: 'stc89c52rc-wifi',
    interface: 'udp',
    type: 'udp4',
    address: '127.0.0.1',
    port: 8266,
    remoteAddress: '192.168.1.80',
    remotePort: 8266,
    reuseAddr: false
});
複製代碼

使用udp須要配置在本地監聽服務器的地址和端口,同時也要指定對方的IP地址和端口。通常給8266模塊配置固定IP。

UDP驅動實例有如下方法:

function: on(type, callback)

@params {string} type: 事件類型。能夠取值爲: listening、close、error、message。

@params {function} callback: 事件發生時的回調。不一樣的事件類型有不一樣的參數。具體以下:

@type: listening 當本地監聽服務器啓動後發生
    沒有參數

@type: close 當本地監聽服務器關閉後發生
    沒有參數

@type: error 當監聽服務器發生錯誤時發生
    callback(error)

@type: message 當服務器接受到數據時發生
    callback(data, remote, next)
    
    data是接收到的數據。buffer類型。
    remote是node dgram的內置對象。請參考node文檔。
    next請參考上面xfs5152ce驅動的文檔。
複製代碼

function: write(data, callback)

@params {buffer} data: 要發送的數據。

@params {function} callback(error): 發送數據成功或失敗後的回調。

function: driver(name, driver)

參考上面的driver文檔。


寫驅動不易,除了編寫軟件外,還須要購買硬件實物,沒法像軟件同樣即寫即所得。

還有不少不完善的地方,不管是API設計到代碼組織,都須要好好思考。後續會增長更多驅動和完善通用性驅動的功能。大部分驅動都是以通用性設備做爲基礎,而後按照硬件要求的數據格式進行傳輸,達到讓硬件工做的方式。因此有必要完善好通用性驅動的功能。

更友好的API文檔也是後續要作的事。

總之,這只是開始。

你們用的開心就好,反正也是給我本身用的。


change log

v0.0.2 @ 2018-04-22 17:19

  • SSD1306新增showSystemInfo函數,用於監控系統信息。
  • 直接調用showSystemInfo便可在屏幕上面顯示內存、CPU統計信息、IP、時間。
  • stopSystemInfo函數用於中止刷新系統監控信息。
  • 增長GPIO引腳參考圖,用於查閱樹莓派引腳編號和功能。

v0.0.1 @ 2018-01-01 23:27

  • 增長訊飛XFS5152CE文字轉語音芯片的驅動
  • 增長SSD1306 OLED屏幕的驅動。
  • 支持在SSD1306屏幕上直接繪製canvas,更友好的圖形API(我正在儘可能提升刷新率)
  • 包含UART串口、I2C、UDP驅動
  • 包含DHT-十一、DHT-22溫度溼度傳感器驅動
相關文章
相關標籤/搜索