LineBot控制micro:bit

2017年6月多研究了LineBot,到了8月多,又碰上了micro:bit的熱潮,也花了一些時間玩玩,心中早想把這兩種東西搞在一起,卻直到十二月才動手,其間有碰到一些盲點,但很高興所有的事情都能克服。

許多人用手機控制micro:bit大部份都是用App Inventor2來寫手機程式控制micro:bit,因為之前有稍稍研究了一下LineBot,大部份人手機都有Line,所以,若能用Line來控制micro:bit,應該也挺有趣的。之前寫了許多有關LineBot的文章,Line要和microbit連結,需要有相關LineBot的背景知識,這一篇文章不會再贅述,就請有需要的朋友看看以前我有關的LineBot的文章吧!

Line可以和micro:bit連線溝通

node.js執行LineBot程式當做Line和micro:bit的中介

一、安裝LineBot用的package

LineBot端的程式是使用node.js,今天這個程式需要有的package有三個:linebot、express,以及serialport,前二個package是LineBot執行時所必需的,serialport這個package則是LineBot程式和micro:bit用序列埠溝通時所必需用到的,所以請先用rmp安裝好這三個package。(node.js的安裝及package的安裝,請參考安裝node.js申請heroku)

二、安裝micro:bit和電腦連接的mbed驅動程式。(相關序列埠介紹及驅動程式,請參考序列埠和micro:bit溝通)

三、在node.js的環境中建立程式,取名index.js,程式內容如下:

var linebot = require('linebot');
var express = require('express');
var SerialPort = require('serialport');

var bot = linebot({
  channelId: '這裡改成自己Line的channelId',
  channelSecret: '這裡改成自己Line的channelSecret',
  channelAccessToken: '這裡改成自己Line的channelAccessToken'
});

//以下的com5請自行修改成自己的micro:bit和電腦連接時的com port名稱
//但是baudRate,115200的值,請勿修改
var mySerial = new SerialPort('com5', {baudRate: 115200});
var myId;
const Delimiter = SerialPort.parsers.Delimiter;
const parser = mySerial.pipe(new Delimiter({ delimiter: Buffer.from('\n') }));

//這一段的程式是專門處理當有人傳送文字訊息給LineBot時,我們的處理回應
bot.on('message', function(event) {
  if (event.message.type = 'text') {
    myId=event.source.userId;
    var msg = event.message.text;
    console.log(msg);
    sendToMicrobit(msg);
  }
});

function sendToMicrobit(msgFromLine){
  var myMsg='';
  if (msgFromLine=='溫度')
     myMsg='temperature\n';
  else if (msgFromLine=='光線')
     myMsg='light\n';
  else if (msgFromLine=='清除'){
     myMsg='clear\n';
     bot.push(myId,'micro:bit的LED畫面已清除!')    
  } else if (msgFromLine.startsWith('聲音')){
     myMsg=msgFromLine.replace('聲音','tn')+'\n';
     bot.push(myId,'micro:bit發出聲音:'+msgFromLine.replace('聲音',''));
  } else if (!isNaN(msgFromLine)){
     bot.push(myId,'已傳送編號'+msgFromLine+'的圖案到micro:bit');
     myMsg=msgFromLine+'\n';
  } else{
     bot.push(myId,'您好,目前可以用的指令有\n溫度(會回報溫度)\n光線(會回報光值)\n0~39(microbit的LED會顯示編號圖案)\n清除(清掉LED畫面)\n聲音加上簡譜數字(例如聲音123)');
     myMsg='\n';
  }
  mySerial.write(myMsg, function(err) {
    if (err) {
      return console.log('Error on write: ', err.message);
    }
    console.log('訊息已傳送至micro:bit:'+myMsg);
  });
}

parser.on('data', function (data) {
  var myString=data.toString();
  console.log('Data:',myString);
  if (myString.startsWith('temperature')){
     myString=myString.replace('temperature','溫度');
  } else if (myString.startsWith('light')){
     myString=myString.replace('light','光線');
  }  else if (myString==='A\r' || myString==='B\r'){
     myString=myString.replace('\r','')+'鍵已被按下!';
  }
  bot.push(myId,myString);
});


mySerial.on('error', function(err) {
  console.log('Error: ', err.message);
})

const app = express();
const linebotParser = bot.parser();
app.post('/', linebotParser);

var server = app.listen(process.env.PORT || 8080, function() {
  var port = server.address().port;
  console.log('目前的port是', port);
});

四、程式該放在哪裡?

因為LineBot需要https的伺服器,以前我的文章都是教大家要把程式放上heroku(https://www.heroku.com/),heroku會提供空間以及程式環境,最重要的是提供https讓Line來界接。但是,micro:bit無法連上heroku,所以,LineBot的程式不能放在heroku上,只能放在電腦上來連接,所以,LineBot的程式也得要放在本機的電腦上。有一個程式叫做ngrok,它能夠提供https,並且提供轉址,讓Line的訊息能夠透過ngrok轉址到您的電腦上,就算是電腦在區域網路的虛擬IP也可以,ngrok的網址 https://ngrok.com/,下載適合的ngrok程式後,在命令列執行指令,指令如下:

  • ngrok http 8080

ngrok會取得一個https的網址(如下圖圖片所示),將自己取得的https網址填入Line的Message API的Webhook URL,讓Line的伺服器可以識別得到您的電腦。這個ngrok程式不要關掉,如果關掉再重新啟動ngrok,又會重新取得一個不一樣的https網址。

執行ngrok,以取得https網址

將取得的網址填入Message API裡的Webhook URL欄位裡

五、micro:bit的程式如下,請在makecode裡將以下的程式貼上Javascript頁面,下載後存入micro:bit裡

let tons: number[] = []
let myBeat = 0
let myNumber = 0
let myString = ""
tons = [262, 294, 330, 349, 392, 440, 494, 523, 587]
input.onButtonPressed(Button.A, () => {
    serial.writeLine("A")
})
input.onButtonPressed(Button.B, () => {
    serial.writeLine("B")
})
function playMusic() {
    myString = myString.substr(2)
    myBeat = 1
    for (let i = 0; i < myString.length; i++) {
        if (i == (myString.length - 1)) {
            music.playTone(tons[parseInt(myString.charAt(i)) - 1], music.beat(BeatFraction.Whole))
        } else {
            if (myString.charAt(i + 1) == "-") {
                myBeat = BeatFraction.Double
            } else if (myString.charAt(i + 1) == "~") {
                myBeat = BeatFraction.Half
            } else {
                myBeat = BeatFraction.Whole
            }
            music.playTone(tons[parseInt(myString.charAt(i)) - 1], music.beat(myBeat))
            if ((myString.charAt(i + 1) == "-") || (myString.charAt(i + 1) == "~")) {
                i++
            }
        }
    }
}
serial.onDataReceived(serial.delimiters(Delimiters.NewLine), () => {
    myString = serial.readUntil(serial.delimiters(Delimiters.NewLine))
    myNumber = parseInt(myString)
    if (myString == "temperature") {
        serial.writeLine("temperature=" + input.temperature())
        basic.showNumber(input.temperature())
    } else if (myString == "light") {
        serial.writeLine("light=" + input.lightLevel())
        basic.showNumber(input.lightLevel())
    } else if (myString == "clear") {
        basic.clearScreen()
    } else if (myString.substr(0, 2) == "tn") {
        playMusic()
    } else if (myString == "0") {
        basic.showIcon(IconNames.Heart)
    } else if (myNumber > 0 && myNumber < 40) {
        basic.showIcon(myNumber)
    }
})

六、將micro:bit與電腦連接(要用raspberry pi 也可以),連接要用藍芽或是序列埠都行,只要com port和上面index.js裡面的com port一樣即可,啟動index.js,指令:

  • node index.js

接下來就可以用Line和micro:bit連線下指令了,程式裡可以用的指令如下:

1.溫度(會回報溫度)

2.光線(會回報光值)

3.輸入0~39,microbit的LED會顯示編號相對應的圖案)

4.清除(清掉LED畫面)

5.聲音加上簡譜數字,例如:聲音123,會發出Do、Re、Me的聲音各一拍;數字後面接著減號,則那個音會演奏2拍,數字後面接著~,則那個音會演奏半拍,例如下指令:聲音1-2~3,Do是2拍,Re是半拍,Me是1拍。

Line的執行畫面