Tag Archives: Lattapanda

[LattePanda] 使用LattePanda打造Line機器人

本文章將來介紹一下如何在LattePanda上面部署一個LINE智慧對話機器人,並拿它來控制LattePanda上的Arduino IO。

作者/攝影  袁佑緣
時間  3小時
成本 時間就是成本
難度  * * *
材料表
  • LattePanda
  • 手機

STEP1. LINE開發者帳號與取用LINE Message API

首先, 我們需要先在 LINE 的開發者官方網站 (https://developers.line.me/en/) 註冊一個LINE開發者帳號。

請點選右邊的 Start using Message API, 接下來系統將會提示使用者完成三個設定步驟以完成新增一個通道。

首先,你必須先選取一個提供者,也就是告訴系統是誰提供這個APP的服務。

接下來,請填上關於取用Messaging API 的必要資訊。

最後一步,確認玩設定都沒錯後,按下confirm,你將會看到一個新增的通道出現在一開始的console頁面。

STEP2. 安裝Node.js與下載 line-bot-sdk-nodejs library

在我們繼續完成 LINE Bot細部設定之前,我們必須先安裝一下LINE Bot的軟體開發環境。 我們開發LINE Bot所使用的程式語言是 node.js,所以請點選以下網址(https://nodejs.org/en/download/) 將Windows版本的node.js下載下來並安裝在LattePanda上面。

安裝完後,我們還需要用到LINE Bot SDK ,請到以下的github 網址(https://github.com/line/line-bot-sdk-nodejs) 下載並解壓縮官方的node.js 版 LINE Bot SDK。

如果有讀者想要查這個LINE Bot SDK的詳細使用資訊的話,可以點選此網址 (https://line.github.io/line-bot-sdk-nodejs/) 到官方的gitbook上看進一步的API使用說明,以及如何去用javascript來撰寫機器人的對話功能。

最後請打開命令提示字元(按下快捷鍵 <Win+R> 並輸入”cmd”叫出command prompt), 並輸入以下的命令將工作目錄移動到方才解壓縮的LINE Bot SDK資料夾下的echo-bot範例(line-bot-sdk-nodejs/examples/echo-bot)。

cd THE_ECHO-BOT_EXAPMLE_FOLDER(ex: C:\Users\lattepanda\line-bot-sdk-nodejs\examples\echo-bot)

接下來請輸入以下的指令來安裝需要用到的node.js套件,在這個範例中,我們會需要安裝兩個額外的套件,一個是用來開啟伺服器的express套件,另外一個則是LINE Bot的SDK函式庫。

npm install

 

STEP3. 用ngrok來將本地伺服器開放到網路上

在前面的步驟中,我們雖然可以開啟一個本地的伺服器,但是為了要能夠取用網路上的 LINE Messaging API,我們必須使用一些工具來將LattePanda上的服務開放到雲端上。

 

請點選此網址(https://ngrok.com/download)來下載ngrok,這套軟體可以讓使用者在本地伺服器與網際網路建立一個加密的網路通道。

下載完成後,請將壓縮檔解壓縮,並點選ngrok.exe,呼叫出一個命令提示字元。

請在上面輸入以下的指令在localhostport 3000開啟一個http的通道。

ngrok http 3000

成功開啟後,將會看到如以下的畫面。你將會得到兩個開放的網址,一個是走HTTP,另外一個則是走有加密的HTTPS,請記住這個連結,待會我們將要把這個連接填到LINE Bot上的回呼位址上。

STEP4. 設定Channel Secret/Webhook/Token

請點選剛剛在 STEP2 新增的APP,我們將要完成LINE Bot上最後三個主要的溝通憑證。

第一個是設定頻道的金鑰,請按下issue並複製這段的金鑰到echo-bot的範例資料夾中的index.js檔中。

接下來,同樣地也請點選issue並把channelaccess token貼到index.jsconfig中。

// create LINE SDK config from env variables
const config = {
  channelSecret: 'PASTE_YOUR_CHANNEL_SECRET',
  channelAccessToken: 'PASTE_YOUR_ACCESS_TOKEN',
};
index.js

最後,我們的APP需要設定一個webhook網址,請填上你在STEP3 中開啟的ngrok所得到的網址,並在最後面加上 “/callback” 。例如: “20c21167.ngrok.io/callback”.

STEP5. LineBot 測試

請用智慧型手機打開LINE APP並掃描這個機器人的QR code來加好友。

接下來回到LattePanda上輸入以下的指令執行echo-bot程式並確認ngrok通道已經正確開啟了且webhook的網址也有設定在LINE Bot console 頁面了。

node .

請在上面手機上用LINE打上一些文字看看LINE Bot會有什麼反應吧!

 

STEP6. 使用 serial來與Arduino做溝通

請下載以下的Arduino程式碼到 LattePanda上的 Arduino Leonardo核心。

這個程式中,我們時做了一個簡單的serial控制的LED開關,如果使用者在序列埠監控視窗中輸入一個大寫的T,LED燈就會被打開,如果輸入大寫的F,LED燈則會被關閉。

void setup()
{
    Serial.begin(9600);
    pinMode(LED_BUILTIN, OUTPUT);
    
}

void loop()
{
    if(Serial.available())
    {
        char cmd = Serial.read();
        if(cmd == 'T')
            digitalWrite(LED_BUILTIN, HIGH);
        else if(cmd == 'F')
            digitalWrite(LED_BUILTIN, LOW);
        else
            Serial.println("Enter T/F to turn ON/OFF LED");
    }
}

STEP7. 在LattePanda上使用LineBot 來控制LED

我們想在LattePanda上使用node.js取用serial來控制LED燈,所以我們必須再新增一個新的node.js函式庫。請在範例echo-bot中的資料夾中打開 package.json ,並如以下的範例加上 serialport 這個套件來讓node.js可以使用Serial來與Arduino做溝通。

package.json

{
  "name": "echo-bot",
  "version": "0.0.0",
  "description": "An example LINE bot just to echo messages",
  "main": "index.js",
  "scripts": {
    "start": "node ."
  },
  "dependencies": {
    "@line/bot-sdk": "^5.0.0",
    "express": "^4.15.2",
    "serialport": "^6.0.4"
  }
}

改完之後別忘了輸入以下的指令來更新node.js的套件。

npm install

現在我們要在原本的 echo-bot 中的index.js 程式中加入Serial的溝通功能。

首先,請如以下的範例加入serialport的設定,並注意設定好指定的COM Port,也就是在LattePanda上的Arduino Leonardo連接到的COM Port,例如:COM2。

 

index.js

'use strict';

const line = require('@line/bot-sdk');
const express = require('express');
const SerialPort = require('serialport');
const serialPort = new SerialPort('COM2', {
    baudRate: 9600
});

最後請修改echo-bot預設的handler函式中的功能,加入以下的程式碼好讓node.js根據不同的情形透過Serial送出大寫的T跟F到Arduino 。

 

 

index.js

// event handler
function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    // ignore non-text-message event
    return Promise.resolve(null);
  }

  if(event.message.text == 'ON') {
      serialPort.write('T');
      const echo = {type: 'text', text: 'Turn ON LED'};
      return client.replyMessage(event.replyToken, echo);
  } else if(event.message.text == 'OFF'){
      serialPort.write('F');
      const echo = {type: 'text', text: 'Turn OFF LED'};
      return client.replyMessage(event.replyToken, echo);
  } else {
      // create a echoing text message
      const echo = {type: 'text', text: event.message.text};
      return client.replyMessage(event.replyToken, echo);
  }
}

現在請重新執行index.js並確認ngrok有正確打開(STEP3),接下來就可以在手機上測試看看是否在LINE上輸入ON/OFF,LINE Bot就會去自動開關LattePanda上的LED燈囉!

以下是本文章的範例LINE Bot範例影片。

 

相關文章:

[LattePanda] 使用Google Assistant玩互動語音助理

作者/攝影  袁佑緣
時間  1小時
成本
難度  * * * * *
材料表
  • LattePanda
  • 螢幕
  • 鍵盤
  • 滑鼠
  • 喇叭
  • 麥克風

本文說明如何使用 LattePanda搭配Google推出的Google Assistant服務,來打造一個智慧助手,一起來看看是如何完成的吧!

 

STEP0. 事前準備

為了要能夠使用語音服務,除了我們的小電腦LattePanda外,請額外再準備USB麥克風以及喇叭,另外本次的範例會需要連到Google 雲端的服務,所以請確認LattePanda能夠連到網路。

 

STEP1. 首先我們要到先設定一個Google Developer Project

請點選以下的網址到Google Cloud Platform,如下圖。

https://console.cloud.google.com/project

接著請點選”CREATE PROJECT”,建立一個新的專案,如下圖,建立一個新專案名稱為my-google-assistant。

初始設定完成後,要等幾分鐘才會建立完成,回到Platform的右上角可以看到正在建立新專案。

 

STEP2. 接著我們要完成Google Assistant API的設定

首先,請連到Google API的設定網頁(https://console.developers.google.com/apis/api/embeddedassistant.googleapis.com/overview),並把Google Assistant API設定為”ENABLE”。

接著,為了要取用Google雲端的API,我們必須先有一個認證的client端,所以請連到以下的網址來建立一組oauth client ID。

https://console.developers.google.com/apis/credentials/oauthclient

而如果還沒有設定過Consent Screen的話,系統會提示您填上必要的設定,請點選”Configure consent screen”,設定如下圖。

最後回到”Create client ID”這一步,在Application Type填上Other即可。

到這裡,我們就已經完成了Client ID的設定囉!

最後,請把這個Client ID資訊下載到電腦上(請點選右方的下載箭頭),待會在進行Google Assistant認證的時候會用到。

STEP3.設定Google Acitvity

 

在進到安裝Google Assistant 的程式之前,我們要先設定一下這個Google帳號的

Activity,請連到以下的網址進行設定。

 

https://myaccount.google.com/activitycontrols

 

至於什麼是Activity呢?簡單說就是允許Google能否取用某些個人資訊,例如聲音、App資訊等,為了要能夠讓Google助手能正常運作,請將以下三個Activity設為Enabled。

 

Web & App Activity

Device Information

Voice & Audio Activity

STEP4.安裝Google Assistant SDK Python函式庫

我們使用的Google Assistant SDK是基於Python這個程式語言下去實做的,所以必須先在電腦上安裝Python的執行環境。

請到Python的官網(https://www.python.org/downloads/windows/)下載最新版的安裝檔。

接下來執行下載的Python安裝檔,並記得將勾選”Add Python to PATH”的選項,讓Python的執行位址加到Windows的系統變數中。

再來,請按下Windows的快捷鍵<Win>+R,啟動Windows快速開啟,並打上cmd呼叫Windows Command Prompt。

請在開啟的Command Prompt中輸入以下指令

 

pip install –upgrade setuptools

 

呼叫Python的套件管理員更新setuptools這個套件。

接著再輸入以下指令

 

pip install –upgrade google-auth-oauthlib[tool]

 

把Google認證oauth client的工具安裝到系統中。

還記得我們在STEP2中下載的Client ID JSON檔嗎?請將下載的JSON檔的位址複製起來,並改到以下指令中的”path/to/client_secret_XXXXX.json”

 

在Command Prompt中輸入以下指令以完成認證。

 

google-oauthlib-tool –client-secrets path/to/client_secret_XXXXX.json –scope https://www.googleapis.com/auth/assistant-sdk-prototype –save

 

Google會自動提示您在瀏覽器中完成認證,認證完成如下圖。

認證完的credential.json會自動存在電腦的系統上,Windows環境中預設會存在AppData中,使用者以後在啟動Google Assistant時不用擔心需要再次認證。

最後我們看到Google Assistant SDK的source,前面有說過這個SDK是基於Python這款程式語言去實做的,使用上非常容易上手,而官方的github網址在這邊(https://github.com/googlesamples/assistant-sdk-python)。

裡面有紀錄如何安裝SDK,以及如何去使用提供的函式庫功能,另外還有一些現成的範例可以讓入門的使用者去做體驗,如果讀者想要擴充、修改自己的Google Assistant的功能,可以下載(Download ZIP)整個專案來去修改,有興趣的朋友不妨去嘗試看看!

 

而本文章為了快速的demo,在這邊直接使用包好的assistant-sdk範例套件,讓使用者可以省去下載、手動安裝的過程,所以請在Command Prompt中輸入以下指令

 

pip install google-assistant-sdk[samples]

 

最後我們輸入以下的指令來執行一個簡單的範例pushtotalk。

 

googlesamples-assistant-pushtotalk

 

這支pushtotalk會提示使用者按下Enter鍵並開啟Google Assistant的語音對談功能,請確定您的麥克風與喇叭都有接到LattePanda上,並確認前面的步驟都設定正確,接下來就可以來玩玩看Google Assistant有什麼功能吧!

 

 

相關文章:

 

 

 

[LattePanda] 作品發表:中文手指閱讀器

這次跟各位讀者分享的作品,是淡江大學電機系CILAB實驗室朱永龍同學發表的專題,CILAB在互動式學習、人機互動、嵌入式系統、影像分析與辨識 、模式識別是主要研究方向,近年在小朋友學習、弱勢族群、MAKE領域發表相當多作品,更多作品可以到這裡

作者/攝影 徐豐智/朱永龍

本次作品為中文手指閱讀器,希望提供視障者一個輕巧且易於攜帶的閱讀器;將閱讀器穿戴在視障者的食指上,協助他們閱讀紙本書籍上的中文文字。視障者使用手指閱讀器時,系統首先會利用震動提示的方式,來引導視障者將食指移動到適當的閱讀位置(觸覺回饋)。系統辨識書籍上的中文文字完成後,透過朗讀文字提供視障者聽覺上的回饋。

 

來認識一下視障者的相關資訊吧

一般日常生活中閱讀文字是不可或缺的事情,除了閱讀書籍外,包括閱讀藥物罐上的說明,操作家電(例如微波爐)上的按鈕,閱讀車站內看板上相關訊息(例如位置、樓層)時,都需要辨識這些文字訊息。對視障者來說,理解這些文字訊息是一件困難的任務。對「中途失明者」來說更是如此,所謂的「中途失明者」是指年齡為15歲以上之視障者,他們原本擁有正常的視力,後來因為疾病或意外傷害導致失明。這些視障者未曾接受過特殊教育,他們普遍呈現摸讀速度緩慢、連續性不足、缺乏效率等問題。(*註解[1]-[5])

 

為了協助視障者閱讀文字,近年來國內外許多研究者提出許多研究成果。主要可分為穿戴式裝備或是手持式裝置兩大類型的輔具。(*註解[6]-[13])

  • 穿戴式輔具:

將身體當為攝影機或相機的載具,並在攝影機擷取影像後,搜尋影像中可能存在的文字區塊加以辨識。學者Yi與Tain將小型攝影機架設在眼鏡上,並透過影像分析與文字辨識技術,辨識出書寫在物品上的文字。Hanif與Prevost 同樣將小型攝影機架設在眼鏡上的方式,來辨識招牌上的文字,並提供一個震動回饋來與視障者互動。Mattar等人[8]設計一個頭戴式攝影機,來辨識招牌上的文字。Ezaki等人將攝影機架設在肩膀上,來辨識招牌上的文字。

  • 手持式輔具:

手持式裝置的特色就是視障者透過PDA或智慧型手機作為影像擷取裝置。學者Peters 等人[10]以PDA的相機擷取影像來辨識鈔票、條碼與商品上的標籤。Shen等人[11]使用智慧型手機辨識門牌或路標,並提供震動回饋來與視障者互動。

 

現在技術上常遇到的問題

  • 在擷取影像時,影像品質容易受到光源、焦距、反光等影響。
  • 在複雜背景中尋找文字區域也不容易。
  • 需要耗費大量的運算資源(處理時間可能需要數秒至數十秒)。
  • 很難辨別出哪些文字資訊才是視障者真正關心的資訊。

由MIT多媒體實驗室Shilkrot等人在2014與2015年所提出的穿戴式手指閱讀器。協助視障者閱讀紙本書籍上的英文文字。當系統辨識書籍上的英文文字後,利用語音朗讀文字的方式,讓視障者得知書籍上的文字內容為何。

這次主題以辨識繁體中文為主,將閱讀器套在食指上並指向書籍,閱讀器將透過震動提示引導視障者閱讀書中文字,閱讀器也將透過攝影機逐一朗讀出書籍上的文字內容,閱讀器也可以幫助外籍人士學習中文字。

View post on imgur.com

裡面的硬體裝置包含(1)小型攝影機、(2)震動馬達、(3)電容式觸控開關,(4)控制馬達的微控制系統。我們利用小型攝影機擷取影像資訊來判斷手指與書本文字的相對位置,並進一步擷取文字字元的影像。

 

我們來看看硬體

閱讀器裡面安置一個(5)電容式觸控開關,手指可藉由觸摸開關來切換閱讀器的中文閱讀模式、閱讀器內部也安置四個具震動功能之微型馬達,電腦端的控制系統可以即時控制馬達震動,並透過震動的提示資訊,引導食指移動到適當的閱讀位置。(6)LattePanda做我們文字辨識的後端處理系統,配合閱讀器可以隨時做文字處理,並隨時提供給使用者語音回饋。

View post on imgur.com

View post on imgur.com

情景一 單字閱讀模式

閱讀器擁有中文單字閱讀模式,使用者手指指向文字時,閱讀器將只偵測手指上方所指到的中文單字,單字閱讀模式不使用振動馬達,並且關閉其它跳行偵測和引導換行等震動提示功能。

View post on imgur.com

單一字閱讀

 

情景二 文章閱讀書籍文字

視障者在閱讀文章時並無法順利的將手指指在適當的文字位置上,文章閱讀模式在閱讀書籍時,閱讀器會透過震動馬達的震動來提示視障者,並引導食指移動到適當的文字位置,方便閱讀器做文字辨識。

我們在手指閱讀器內部安置了四個具震動功能之微型馬達。電腦端依據不同的使用情景設計了三種主要的引導機制(A)引導使用者到距離手指最接近的文字段落,並引導手指移至該行文字的開頭位置(圖4(a))。(B) 當手指出現跳行或偏移到其他橫行文字的情況時,閱讀器透過震動提示的方式,引導手指移回原本的閱讀的位置(圖(b))。(C) 當手指移動到此橫行文字段落的結尾邊界時,將提示回到行頭並到達下一行從頭開始閱讀(圖(c))。

View post on imgur.com

(a) 引導至開頭

View post on imgur.com

(b) 跳行提醒

View post on imgur.com

 

(c) 閱讀至結尾邊界時(震動引導機制

 

中文字元切割

當手指閱讀器將指尖前方的影像資訊傳送至電腦端後,CILAB開發一套中文字元擷取與辨識演算法來辨識影像中的文字。下圖是系統流程圖,這個系統利用影像前處理(灰階、Otsu、Opening、Closing)等演算法,找出指尖位置與傾斜角度,再偵測出行高等相關資訊來做文字擷取切割出單一的文字圖,透過中文字元辨識以及文字轉語音讓閱讀器念出中文。

View post on imgur.com

系統流程圖

 

若是針對整張影像做二色化處理,其二色化結果非常容易受到光源以及其他因素影像,經常出現文字破損的情況,也進一步影響文字切割以及中文字元辨識(OCR)的準確性。設計者採用區域式(local-based)的二色化處理擷取出完整度較好的文字影像,並提升文字切割以及中文字元辨識(OCR)的準確性。

另外採用投影掃描線法來偵測橫行,圖的灰色區域代表掃描線經過文字區塊(圖6a)。當系統偵測橫行並計算出行高後,我們會根據指尖位置,往上方延伸兩倍行高的高度,以及四倍行高的寬度,進行區域影像的切割(如圖(a)之紅色矩形區域),並針對此矩形區域使用Otsu threshold再次進行二色化處理,圖7(b)為此矩形區域進行二色化處理後之結果。

View post on imgur.com

橫向投影掃描線法

View post on imgur.com

View post on imgur.com

 

 

接著使用垂直投影的方式來切割中文字元(圖(a)),此種方式相較於切割英文字元會有較為複雜的問題,對於某些中文字來說(例如:化、川),以垂直投影進行文字切割時,一個完整的中文字元會被分割成兩個或三個獨立的文字區塊。

我們發現中文字的外觀都非常接近方正型,我們根據中文字的行高資訊,在做文字切割時,將部分寬度較小的且鄰近的中文字元合併成一個中文字元,圖 (b)為成功切割出一中文字元。

View post on imgur.com

圖(a)垂直投影掃描線

View post on imgur.com

(b)切割獨立元

 

註解

[1] 柯明期, 中途失明者適應與重建之研究。師範大學特殊教育所碩士論文, 2004。

[2] 李佳玲, 中高齡視障者電腦使用動機及對圖書館電子化資源服務需求之研究。臺灣大學圖書資訊學研究所碩士論文, 2013。

[3] W. Jeong, “Emotions in information seeking of blind people,” in Diane Nahl and Dania Bilal (Eds.), Information and Emotion: The Emergent Affective Paradigm in Information Behavior Research and Theory pp. 267-278, 2007.

[4] 陳怡佩。視覺障礙兒童及青少年的資訊需求。臺灣圖書館管理季刊, 2(3), pp. 32-43, 2006.

[5] K. Carey, “The opportunities and challenges of the digital age: a blind user’s perspective,” Library Trends 55(4): 767-784, 2007.

[6] C. Yi, and Y. Tian, “Assistive text reading from complex background for blind persons,” in Camera-Based Document Analysis and Recognition. Springer, 15–28, 2012.

[7] S. M. Hanif, and L. Prevost, “Texture based text detection in natural scene images-a help to blind and visually impaired persons,” In CVHI, 2007.

[8] M. Mattar, A. Hanson, and E. Learned-Miller, “Sign classification using local and meta-features,” in IEEE CVPR Workshops, pp. 26–26, 2005.

[9] N. Ezaki, M. Bulacu, and L. Schomaker, “Text detection from natural scene images: towards a system for visually impaired persons,” in Proc. of ICPR, vol. 2, pp. 683–686, 2004.

[10] J.-P. Peters, C. Thillou, and S. Ferreira, “Embedded reading device for blind people: a user-centered design.” in Proc. of IEEE ISIT, pp. 217–222, 2004.

[11] H. Shen, and J. M. Coughlan, “Towards a real-time system for finding and reading signs for visually impaired users,” In Proc. of ICCHP, pp. 41–47, 2012.

[12] R. Shilkrot, J. Huber, C. Liu, P. Maes, and S. C. Nanayakkara, “Fingerreader: A wearable device to support text reading on the go,” in CHI EA, ACM, pp. 2359–2364, 2014.

[13] R. Shilkrot, J. Huber, M. E. Wong, P. Maes, and S. C. Nanayakkara, “Fingerreader: A wearable device to explore printed text on the go,” in ACM CHI 2015, pp. 2363–2372, 2015.

 

相關文章:

 

 

 

 

 

 

 

 

 

 

 

[LattePanda] 使用C# 來做藍牙4.0 iBeacon的門鎖系統

在學這篇教學前必須要先會一些事情,首先必須要會在Lattepanda上使用visual studio

(LattePanda 拿鐵熊貓教學:Arduino與Visual Studio環境設定)。

再來必須已經了解如何在visual studio裡使用C#控制Arduino的I/O

(LattePanda 拿鐵熊貓教學#1:LED 閃爍,使用Visual Studio)。

在這篇教學中會使用visual studio的C#來開發簡單的門鎖系統,一般在藍牙使用上都是使用藍牙傳輸資料,但在這篇是使用藍牙4.0中的iBeacon模式,在iBeacon模式中可以得知接收器與發射器的距離,而這個距離就可以當作我們決定要不要開門的條件,所以這樣連按按鈕的動作都可以省去。

作者/攝影  黃品叡
時間  3 小時 以上
難度  * * * * * * *
材料表
  • Lattepanda
  • USB to UART模組*1
  • HM-10模組(2個以上)*1
  • 電磁鎖*1
[請注意] 這邊教學是必須有一些基礎,首先前情提要中的Lattapanda上使用Visual Studio是基本,再來是必須要會開啟Window Forms C#、會使用基本的工具箱,並且可以編譯程式。最後是對於電路有基本的認識,能夠認識基本電路符號,並且能將簡單電路圖在麵包版上實現。

 

在這篇教學中分成硬體、軟體兩個部分,(1)硬體部分是電路的連接, (2)軟體部分是Visual Studio C#的程式撰寫。

 

(1)=====硬體部分:=====

在硬體連接的電路圖如下圖。

使用的有

  • 兩個<LED>燈(顯示門鎖是開啟還是關閉)
  • 一個12V的<LOCK>電磁鎖
  • 一個<USB to UART模組>
  • 兩個<HM10>的BLE模組,一個安裝至LattePanda,一個由使用者持有

下圖Lattepanda上左方USB孔上接上USB to UART模組(紅色),可接上HM10 BLE模組。

對應電路圖,接在麵包版上電路如下圖

整體看起來會是像下圖

換個角度再放一張圖,如下

在電磁鎖的部分基本上可以任意替換,在這裡我將它鎖在木板上,能夠表示他有鎖住或打開而已,其安裝後的圖如下(上方是一塊木板連結鐵塊,下面則是一個「ㄇ」字形的木板連結電磁鐵的線圈部分)。

(2)=====軟體部分:=====

我使用的程式開發環境是Windows 10、Visual studio 2017、C#,那我們就直接進入程式部分。

程式我分成(1)介面、(2)初始化、(3)Serial連接、(4)開啟backgroundWorker、(5)讀取iBeacon資訊、(6)iBeacon資訊解碼、(7)連結Arduino,這幾個部分。

1.介面

Form1中的介面配置如下圖

在最上面有一個textBox 命名為 textbox_door
中間是一個listView 命名為 listView_door
按鈕左邊是 button_connect
按鈕中間是button_findBeacon
按鈕右邊是button_loopFind
最下面的listBox 命名為 listBox_msg
在背景工具中有
serialPort 命名為 serialPort_beacon
backgroundWorker 命名為 backgroundWorker_findBeacon
timer 命名為 timer1

 

各個介面的命名可以參考下圖:

 

命名如果不一樣,請記得在程式中自行修改。

 

2.初始化

初始化的部分先從 include開始:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.IO.Ports; //serial
using LattePanda.Firmata; //Arduino

以上是所有要用到的namespace。

 

在全域變數裡:

Arduino arduino = new Arduino("COM3", 57600);
bool[] openlist = new bool[3];

(全域變數記得是寫在 class Form1 裡面,但是在一般函式外面)

 

在Form1()建構式裡:

public Form1()
{
InitializeComponent();
ColumnHeader header1, header2;
header1 = new ColumnHeader();
header2 = new ColumnHeader();

header1.Text = "--Time--";
header1.TextAlign = HorizontalAlignment.Center;
header1.Width = 90;
header2.Text = "--ID--";
header2.TextAlign = HorizontalAlignment.Center;
header2.Width = 70;

listView_door.Columns.Add(header1);
listView_door.Columns.Add(header2);

//which ID can open the lock
openlist[0] = false;
openlist[1] = true;
openlist[2] = true;

//arduino setting
arduino.pinMode(5, Arduino.OUTPUT);//pin of red LED
arduino.pinMode(6, Arduino.OUTPUT);//pin of green LED
arduino.pinMode(10, Arduino.OUTPUT);//pin of the lock

//set the door close first
doorOpen(false);
textBox_door.BackColor = Color.Red;
}

(這裡面設定了一些介面的東西,一些初始值還有門鎖狀態)

 

3.Serial連接

Serial 連接在C#中非常簡單,我是用一個按鈕,按下後連結Serial程式如下:

private void button_connect_Click(object sender, EventArgs e)
{
serialPort_beacon = new SerialPort("COM6", 9600, Parity.None, 8, StopBits.One);

if (!serialPort_beacon.IsOpen)
{
try
{
serialPort_beacon.Open();
listBox_msg.Items.Add("Connect");
}
catch
{
MessageBox.Show("Serial open error!");
}
}
else
listBox_msg.Items.Add("Opened");
}

 

(這個COM是USB to URAT晶片的COM角)

 

4.開啟backgroundWorker

首先是按鈕觸發尋找Beacon:

private void button_findBeacon_Click(object sender, EventArgs e)
{
//send DISC
string msgSend = "AT+DISI?";
byte[] buffer = System.Text.Encoding.Default.GetBytes(msgSend);
if (serialPort_beacon.IsOpen)
if (backgroundWorker_findBeacon.IsBusy != true)
{serialPort_beacon.Write(buffer, 0, buffer.Length);
backgroundWorker_findBeacon.RunWorkerAsync();
}
else
MessageBox.Show("already finding");
else
MessageBox.Show("Serial is close");
}

 

(在這裡是開啟一個backgroundWorker,而這裡只是單純地尋找一次Beacon而已)

若是要持續尋找,可以開啟一個timer來持續觸發,程式如下:

private void button_loopFind_Click(object sender, EventArgs e)
{
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
//send DISC
string msgSend = "AT+DISI?";
byte[] buffer = System.Text.Encoding.Default.GetBytes(msgSend);
if (serialPort_beacon.IsOpen)
if (backgroundWorker_findBeacon.IsBusy != true)
{
serialPort_beacon.DiscardInBuffer();
serialPort_beacon.Write(buffer, 0, buffer.Length);
backgroundWorker_findBeacon.RunWorkerAsync();
}
else
Console.Write("already finding");
else
MessageBox.Show("Serial is close");
}

(這裡開一個Timer讓我們可以每一段時間就開啟backgroundWorker)

 

5.讀取iBeacon資訊

這裡就是backgroundWorker裡所做的事情

private void backgroundWorker_findBeacon_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;

char[] buffer = new char[256];
string msg_read = "";
bool stringCheck = false;
//read data
byte loopCount = 0;

while (true)
{
if (serialPort_beacon.BytesToRead > 0)
{
//serial read to msg_read
serialPort_beacon.Read(buffer, 0, buffer.Length);
for (int i = 0; i < buffer.Length && buffer[i] != '\0'; i++)
msg_read += buffer[i];

//if msg_read end with "OK+DISCE" then stop reading
if (msg_read.Length > 8 && msg_read.IndexOf("OK+DISCE") != -1)
{
stringCheck = true;
break;

}
}
else
{
//timeout
System.Threading.Thread.Sleep(500);
loopCount++;
string dot = "";
for (int i = 1; i <= loopCount; i++)
dot = dot + ". ";

if (loopCount > 1)
this.Invoke((MethodInvoker)(() => listBox_msg.Items.RemoveAt(listBox_msg.Items.Count - 1)));
if (loopCount > 15)
break;
this.Invoke((MethodInvoker)(() => listBox_msg.Items.Add(dot)));
}
}

//if didn't read anything than prient "time out"
if (msg_read == "")
this.Invoke((MethodInvoker)(() => listBox_msg.Items.Add("Time out")));
else
{
if (stringCheck == false)
//if have read something but not iBeacon info
this.Invoke((MethodInvoker)(() => listBox_msg.Items.Add(msg_read)));
else
{
Tuple<int[], int[], int[], int> result = deCodeDISI(msg_read, 2);
this.Invoke((MethodInvoker)(() => listBox_msg.Items.Add("minor : " + result.Item2[0].ToString())));
this.Invoke((MethodInvoker)(() => listBox_msg.Items.Add("RSS : " + result.Item3[0].ToString())));
//if is close enough and it's in open list then open the lock
if (result.Item3[0] > -40 && openlist[result.Item2[0]] == true)
{
this.Invoke((MethodInvoker)(() => textBox_door.BackColor = Color.Green));
ListViewItem item1 = new ListViewItem(DateTime.Now.ToShortTimeString());
item1.SubItems.Add(result.Item2[0].ToString());
this.Invoke((MethodInvoker)(() => listView_door.Items.Add(item1)));
doorOpen(true);
}
else
{
this.Invoke((MethodInvoker)(() => textBox_door.BackColor = Color.Red));
doorOpen(false);
}
}
}
}

 

(這裡的程式包含好多東西,上半部分是在處理Serial read的問題,並且在讀取中有動態的「…」做顯示,若沒有收到資料則會有「time out」的資訊出現。下半部分是將得到的資訊做判斷,判斷要不要開門)

 

6.iBeacon資訊解碼

這裡是「deCodeDISI」副函式的程式部分:

private Tuple<int[], int[], int[], int> deCodeDISI(string serialData, int maxDiviceCount)
{

//OK+DISIS  OK+DISC : Factory ID : iBeacon UUID : Major+Minor+Measured : MAC : RSSI  OK+DISCE
//OK+DISISOK+DISC:4C000215:74278BDAB64445208F0C720EAF059935:11110001C5:88C25532ED1E:-032OK+DISCE

string DataRemain = serialData;
int[] FactoryID = new int[maxDiviceCount];
string[] UUID = new string[maxDiviceCount];
int[] Major = new int[maxDiviceCount];
int[] Minor = new int[maxDiviceCount];
string[] MAC = new string[maxDiviceCount];
int[] RSSvalue = new int[maxDiviceCount];
DataRemain = DataRemain.Substring(0, serialData.IndexOf("OK+DISCE"));
int count = 0;
while (true)
{
int findNum = DataRemain.IndexOf(":");
if (findNum == -1)
{
Console.Write("deCode done!");
break;
}
else
{
//Factory ID (length 8)
string FactoryID_str = DataRemain.Substring(findNum + 1, 8);
DataRemain = DataRemain.Substring(findNum + 9);
FactoryID[count] = Convert.ToInt32(FactoryID_str, 16);

//iBeacon UUID
findNum = DataRemain.IndexOf(":");
string UUID_str = DataRemain.Substring(findNum + 1, 32);
DataRemain = DataRemain.Substring(findNum + 33);
UUID[count] = UUID_str;

//Major
findNum = DataRemain.IndexOf(":");
string Major_str = DataRemain.Substring(findNum + 1, 4);
DataRemain = DataRemain.Substring(findNum + 5);
Major[count] = Convert.ToInt32(Major_str);
//Minor
string Minor_str = DataRemain.Substring(0, 4);
DataRemain = DataRemain.Substring(findNum + 4);
Minor[count] = Convert.ToInt32(Minor_str);

//MAC
findNum = DataRemain.IndexOf(":");
string MAC_str = DataRemain.Substring(findNum + 1, 12);
DataRemain = DataRemain.Substring(findNum + 13);
MAC[count] = MAC_str;

//RSS
findNum = DataRemain.IndexOf(":");
string RSS_str = DataRemain.Substring(findNum + 1, 4);
DataRemain = DataRemain.Substring(findNum + 5);
RSSvalue[count] = Convert.ToInt32(RSS_str);

count++;
}
}

return Tuple.Create(Major, Minor, RSSvalue, count);
}

(這就是解碼iBeacon的資訊的部分)

 

7.連結Arduino

跟Arduino的部分非常簡單,只有控制I/O的部分,D10腳位控制繼電器、D5控制紅色LED、D6控制綠色LED燈,副程式如下:

private void doorOpen(bool open)
{
if (open == false)//door close
{
//lock the door and red LED on
arduino.digitalWrite(10, Arduino.HIGH);
arduino.digitalWrite(5, Arduino.HIGH);
arduino.digitalWrite(6, Arduino.LOW);
}
else//door open
{
//unlock the door and green LED on
arduino.digitalWrite(10, Arduino.LOW);
arduino.digitalWrite(5, Arduino.LOW);
arduino.digitalWrite(6, Arduino.HIGH);
}
}

 

軟體部分就是以上程式了,其中包含了非常多的細節,像是處理Serial的部分,或是執行續(就是backgroundWorker)裡的處理,為什麼用Invoke,Tuple的用法,解碼的處理…等,這些初學的話建議複製副函式,直接用就好,裡面是什麼等對於程式更清楚後再慢慢回來看。

而當然這些副函式並不能稱作完美,例如並沒有對於不完整的iBeacon資訊做處理的地方,只有稍微確認,和在資訊結尾做非常簡單的處理,在這裡主要就是帶大家入門,在深入的部分,就請大家自己研究瞜~

 

 

相關文章: