使用 JavaScript 對(duì) Arduino 編程

一、向 Arduino 刷入 Firmata 固件

Firmata 是一種開放的類似于 MIDI 的協(xié)議。它可以將微控制器(microcontroller)變成“客戶端”,通過串口通信直接被“主機(jī)”電腦訪問和控制。
通過 Firmata 協(xié)議,可以在主機(jī)上使用 Ruby、Python、JavaScript 等多種語言與刷入了 Firmata 固件的 arduino 板“交流”。

首先安裝 firmata-party 工具:
$ npm install -g firmata-party
安裝成功后通過 USB 線將 Arduino 板連接至電腦,運(yùn)行以下命令:

$  firmata-party uno --debug
found uno on port /dev/tty.usbserial-14110
connected
reset complete.
flashing, please wait...
flash complete.

刷寫完畢后,可以通過 Firmata Test Program 進(jìn)行簡(jiǎn)單的測(cè)試。

Firmata Test

二、使用 JavaScript 編程

Blink LED

可以使用如下命令開始一個(gè)新的項(xiàng)目:

$ mkdir test && cd test
$ npm init -y
$ npm install --save firmata

編輯源代碼文件(blink_led.js)用來控制 LED 閃爍:

// 加載依賴庫
var Board = require('firmata');

// 連接初始化
Board.requestPort(function(error, port) {
    if (error) {
        console.log(error);
        return;
    }

    var board = new Board(port.comName);

    // 等待連接
    board.on("ready",function() {
        console.log("Ready!");
        var ledOn = true;

        // 設(shè)置 pin 13 為輸出引腳
        board.pinMode(13, board.MODES.OUTPUT);

        // 使 LED 閃爍
        setInterval(function() {
            if (ledOn) {
                console.log('ON');
                board.digitalWrite(13, board.HIGH);
            } else {
                console.log('OFF');
                board.digitalWrite(13, board.LOW);
            }
            ledOn = !ledOn;
        }, 1000);
    });
});

運(yùn)行效果如下:

$ node blink_led2.js
Ready!
ON
OFF
ON
...

代碼執(zhí)行后 arduino 板子上 13 號(hào)引腳上接的 LED 開始以 1 秒的間隔閃爍,同時(shí)電腦的終端界面不停地輸出 ON 和 OFF 表示當(dāng)前 LED 的開關(guān)狀態(tài)。

Analog Input

Arduino 板子上的 A0 - A5 引腳可以讀取模擬輸入信號(hào)。
示例代碼如下:

// 加載依賴庫
var Board = require('firmata');

// 引腳定義
const LED = 5;
const POT = 0;

// 初始化變量
var ledOn = 0;
var delay = 0;

// map 函數(shù)。將初始的數(shù)值范圍轉(zhuǎn)變成需要的數(shù)值范圍
function map(x, in_min, in_max, out_min, out_max)
{
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

// 連接初始化
Board.requestPort(function(error, port) {
    if (error) {
        console.log(error);
        return;
    }

    var board = new Board(port.comName);

    // 等待連接
    board.on("ready", function() {

        // 控制 LED 閃爍的 blink() 函數(shù)
        function blink() {
            board.digitalWrite(LED, ledOn);
            ledOn = !ledOn;
            setTimeout(blink, delay);
        }

        // 讀取并更新可變電阻提供的模擬輸入
        board.analogRead(0, function(d) {
            delay = map(d, 0, 1023, 400, 1600);
        });

        blink();
    });
});

線路連接圖如下:


線路連接圖

即通過 Arduino 板 A0 引腳可以讀取模擬輸入的功能(連接可變電阻的中間引腳),獲取可變電阻的取值(0-1023)。
再通過程序中的 map 函數(shù)將 0-1023 的取值轉(zhuǎn)變成 400-1600,作為接在 5 號(hào)引腳上 LED 的閃爍頻率。

程序運(yùn)行后,調(diào)節(jié)可變電阻的旋鈕,LED 即以不同的時(shí)間間隔(0.4s - 1.6s)閃爍。

PWM (Pulse-Width Modulation)

PWM 即脈寬調(diào)制,其機(jī)制為:通過 MCU 內(nèi)部的計(jì)時(shí)器設(shè)置引腳的狀態(tài),使該引腳在一個(gè)周期內(nèi)的某段時(shí)間保持 HIGH 狀態(tài),在該周期內(nèi)的其余時(shí)間保持 LOW 狀態(tài)。
平均下來看,該引腳的輸出電壓即介于最低和最高輸出電壓之間(即 0~5 V 之間)。所以一般可以近似地作為模擬電壓輸出。

// 加載依賴庫
var Board = require('firmata');

// 變量定義
const LED = 5;
var brightness = 0;
var fadeAmount = 5;

// 連接初始化
Board.requestPort(function(error, port) {
    if (error) {
        console.log(error);
        return;
    }

    var board = new Board(port.comName);

    // 等待連接
    board.on("ready", function() {

        // 設(shè)置 LED 引腳為 PWM 模式
        board.pinMode(LED, board.MODES.PWM);

        // 每隔 30 ms 提高或降低 LED 亮度,循環(huán)執(zhí)行
        function fadeLed() {
            brightness += fadeAmount;
            if (brightness ==0 || brightness == 255) {
                fadeAmount = -fadeAmount;
            }
            board.analogWrite(LED, brightness);
            setTimeout(fadeLed, 30);
        }
        fadeLed();
    });
});

該程序執(zhí)行后,5 號(hào)引腳上的 LED 燈先是緩慢變亮,達(dá)到最亮以后再緩慢變暗。依照此規(guī)則循環(huán)。

附錄:JavaScript 的串口通信

即使 Arduino 板子未刷入 Firmata 固件,運(yùn)行的是普通的程序。
也可以利用 Node.js 的 serialport 庫和串口通信完成 Arduino 與主機(jī)的對(duì)話。

首先初始化項(xiàng)目并安裝依賴庫:

$ mkdir test2 && cd test2
$ npm init -y
$ npm install --save-dev serialport
掃描串口設(shè)備
$ cat list_ports.js
var serialPort = require("serialport");
serialPort.list(function (error, ports) {
    ports.forEach(function(port) {
        console.log(port.comName);
    });
});
$ node list_ports.js
/dev/tty.Bluetooth-Incoming-Port
/dev/tty.usbserial-14110
從 Arduino 收取數(shù)據(jù)

首先通過 Arduino IDE 刷入以下 counter.ino 文件,作為發(fā)送端程序:

void setup() {
  Serial.begin(9600);
}

int i=0;
void loop() {
  Serial.println(i++);
  delay(1000);
}

上述代碼以 1000 ms 的間隔生成從 0 開始不斷遞增的數(shù)字并發(fā)送至串口。

電腦端的接收程序 read_port.js 代碼如下:

const SerialPort = require('serialport')
const Readline = require('@serialport/parser-readline')
const port = new SerialPort('/dev/cu.usbserial-14110')

const parser = port.pipe(new Readline({ delimiter: '\n' }))
parser.on('data', console.log)

運(yùn)行效果如下:
read_port

發(fā)送端不斷生成新的數(shù)字并發(fā)送至串口,接收端通過串口接收該數(shù)字后將其打印到終端輸出。

向 Arduino 發(fā)送數(shù)據(jù)

首先通過 Arduino IDE 刷入以下 receiver.ino 文件,作為接收端程序:

void setup() {
  Serial.begin(9600);
}

void loop() {
  int incoming = 0;
  if (Serial.available() > 0) {
    incoming = Serial.parseInt();

    if (Serial.read() == '\n') {
      Serial.println(incoming);
    }
  }
}

發(fā)送端的 write_port.js 程序代碼如下:

var SerialPort = require('serialport');
var Stream = require('stream');
var modem = 'cu.usbserial-14110';
var ws = new Stream();
ws.writable = true;

ws.write = function(data) {
    serialPort.write(data);
};

ws.end = function(buf) {
    console.log('bye');
}

var serialPort = new SerialPort('/dev/' + modem);

process.stdin.pipe(ws);

運(yùn)行效果如下:
write_port

電腦端發(fā)送程序運(yùn)行后($ node write_port.js),打開 Arduino IDE 的 Serial Monitor。
在命令行輸入任何一個(gè)數(shù)字并按回車進(jìn)行確認(rèn),Arduino 板子接收到該數(shù)字后,又將其打印到串口輸出(即 Serial Monitor 中顯示的內(nèi)容)

參考資料

Node.js for Embedded Systems: Using Web Technologies to Build Connected Devices 1st Edition
Node SerialPort

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容