星期二, 3月 28, 2017

Arduino筆記(十九):超音波測距模組

[2017/03/28]
在 2014年 3月曾寫過一篇「Raspberry Pi 筆記(十五):超音波測距離Raspberry」,將 Pi 連接超音波模組,測試一下實際的距離與感應測得的數據差多少,實測的結果誤差不到1公分。

最近繼續研究 Pi 跟 Arduino,也發現另有一個超音波測距的模組,HC-SR04,我改用 Arduino 來連接這個模組,也寫程式測試一下兩者得到的結果是否相同。

[超音波測距 HC-SR04]

模組工作原理:
• 採用I/O觸發測距,給至少為10us的高電位信號
• 模組自動發送 8個 40KHZ的方波,自動檢測是否有信號返回
• 有信號返回,通過I/O輸出一高電位,高電位持續的時間就是超聲波從發射到返回的時間
• 測試距離 =(高電位時間 x 聲速(340M / S))/ 2

[技術規格]
• 電源:DC5V/2mA
• 輸出電位( 1/ 0):5V/ 0V
• 精度:3mm
• 距離範圍:2 ~ 450cm
• 有效的角度:<15℃
• 觸發輸入信號:10uS TTL pulse
• ECHO輸出信號: Input TTL lever signal and the range in proportion
• 接線方式:VCC、trig (控制端)、echo (接收端)、GND

[材料]

• Arduino Uno  x 1
• HC-SR04測距模組  x1
• 連接線 x 4條

[接線圖]


• HC-SR04測距模組連接線路
HC-SR04 測距模組
Arduino
1 - VCC
VCC
2 - Trig – Trigger
Pin 12
3 - Echo
Pin 11
4 - GND
GND

[程式一]

以下先不呼叫函式,直接以音波在空氣中傳遞的大約速度,算出來回測得的時間,乘以空氣中傳遞聲音的速度,算出距離。程式中有一行 cm = (duration/2) / 29.1,各位會不會覺得奇怪,為什麼要除以 29.1 ? 那是因為在攝氏零度之海平面音速約為331.5公尺/秒,每升高1攝氏度,音速就增加0.607公尺/秒,可以列出一個公式:

•  音速 c = 331.5 + 0.607 * t  (其中 t 為攝氏溫度)。

    例: 攝氏20度時的音速約為: 331.5 + 0.607*20 = 343.64 公尺/秒

•  音速公尺/秒 換算成 公分/微秒:343.64 * 100 / 1000000 = 0.034364 公分/微秒,亦即

    音速每公分需要29.1 微秒:  1 / 0.034364 = 29.1 微秒/公分

    超音波發射的距離為來回,因此單程距離 = 時間差 / 2  再除以  29.1 微秒 / 公分,同樣的如果要換算成英吋,只要將 29.1 再乘以 2.54 即可。來看看程式的內容:
int trigPin = 12;                  //Trig Pin
int echoPin = 11;                  //Echo Pin
long duration, cm, inches;
 
void setup() {
  Serial.begin (9600);             // Serial Port begin
  pinMode(trigPin, OUTPUT);        //Define inputs and outputs 
  pinMode(echoPin, INPUT);
}
 
void loop()
{
  digitalWrite(trigPin, LOW);
  delayMicroseconds(5);
  digitalWrite(trigPin, HIGH);     // 給 Trig 高電位,持續 10微秒
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  pinMode(echoPin, INPUT);             // 讀取 echo 的電位
  duration = pulseIn(echoPin, HIGH);   // 收到高電位時的時間
 
  cm = (duration/2) / 29.1;         // 將時間換算成距離 cm 或 inch  
  inches = (duration/2) / 74; 

  Serial.print("Distance : ");  
  Serial.print(inches);
  Serial.print("in,   ");
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();
  
  delay(250);
}

[程式二]

網友將上述計算等方式,寫成一個函式,比較著名的是 NewPing,可以到 這裡 下載最新的函式庫,放到 Arduino 的預設 Libraries目錄,即可呼叫該函式,程式如下:
#include <NewPing.h>
#define TRIG_PIN    12          // trigger Pin
#define ECHO_PIN    11          // Echo Pin
#define MAX_DISTANCE 200        // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);    // 設定 NewPing 物件,並給與最遠測試距離

void setup() {
  Serial.begin(115200);         // 開啟串列埠顯示 ping 的距離值
}

void loop() {
  delay(50);                        // 等待 50 毫秒,需大於前程式說明的 29 秒
  unsigned int uS = sonar.ping();   // 送出 ping,並取得微秒 microseconds(uS) 時間
  Serial.print("Ping: ");
  Serial.print(sonar.convert_cm(uS));    // 換算時間為公分,如顯示 0,表示超出距離範圍
  Serial.println("cm");
}

[測試結果]

測試的結果還蠻正確的,跟規格提供的誤差值3mm差不多。接下來就要將 HC-SR04接到小車上去,看看遇到障礙物時,是否能自動轉彎或倒退。

[參考資料]

• Tim Eckel / Arduino New Ping:NewPing Arduino Library for Arduino
• Arduino Forum:NewPing Library Project Homepage
• Random Nerd Tutorials:Complete Guide for Ultrasonic Sensor HC-SR04
• 維基百科:音速
Share:

星期日, 3月 26, 2017

Raspberry Pi 筆記(三十八):系統語系與中文輸入法

[2017/03/26]
最近有朋友問到,Raspberry Pi 的作業系統都是英文,如果要當作一般個人電腦使用,無法呈現中文,也沒有中文輸入法。只要安裝字型及輸入法,Raspberry Pi就可作為簡單的文書處理器使用,我們就來看一下Raspberry Pi 系統有哪些中文字型可用,如何安裝輸入法。


[安裝中文字型]

因 Raspberry Pi 系統安裝完成後,預設是沒有中文字型,如果要在 Pi中顯示中文字型,就必須另外安裝,目前常用的字型有兩個,一是泉驛字型,另一個是文鼎字型,以下是安裝方法:

[文泉驛字型]
• 安裝文泉驛微米黑、文泉驛正黑、文泉驛點陣宋體
$ sudo apt-get install fonts-wqy-microhei fonts-wqy-zenhei xfonts-wqy

[文鼎字型]
• 安裝文鼎中文字型,舊的 package 名稱為 ttf-arphic-uming
$ sudo apt-get install fonts-arphic-uming 

如要安裝全部文鼎中文字型,可輸入以下指令:
$ sudo apt-get install fonts-arphic*

• 安裝日文字型及日文輸入法,舊的 package 名稱為 ttf-vlgothic
$ sudo apt-get install fonts-vlgothic scim-anthy

[教育部標準楷書]
$ sudo apt-get install fonts-moe-standard-kai

[cwTex圓體]
$ sudo apt-get install fonts-cwtex-yen

[更改語系]

如要設定語系及系統預設語言,需執行 raspi-config進行設定:
$ sudo raspi-config

• 選單 ➔ [ 4 Localisation Options ] ➔  [  I1 Change Locale ]


• 選擇語言  * [ zh_TW.UTF8 UTF8 ]  ➔  [OK]


• 如果選擇系統預設語言為 [ C.UTF-8 ],則選單及環境是英文,但是可顯示中文:


•  如果選擇系統預設語言為 [ zh_TW.UTF-8 ],則選單跟系統文字都是中文字:

[安裝輸入法]

安裝指令:
$ sudo apt-get install scim scim-tables-zh scim-chewing scim-gtk-immodule im-switch

安裝好 SCIM 輸入法後,切換按鍵如下:

•  Ctrl-Space :切換輸入中文或英文
•  Ctrl-Shift   :切換不同輸入法

SCIM 提供了不少中文輸入法 (套件 scim-tables-zh) ,從視窗桌面選中文輸入法,可以列出一堆,包括:倉頡、輕鬆、大易、注音等。


記得安裝完成上述的字型、輸入法等,最後要重新啟動系統,才可以正常顯示中文字型。

[參考資料]

• Raspberrypi.org:Traditional Chinese - 中文
• Hong Kong Linux User Group:Make Debian support Chinese
• GTW:樹莓派 Raspberry Pi 安裝中文輸入法與字型
Share:

Raspberry Pi 筆記(三十七):遠端桌面 VNC、Teamviewer 及RDP

[2017/3/26]
過去寫的一篇文章 - RASPBERRY PI 筆記(八),將安裝 FTP、Webmin、RDP、VNC等都放在一起,覺得太雜了,決定將這篇文章拆成幾個不同單元,分成遠端桌面、Web Server等主題。

這篇文章要談的是我如何安裝與設定軟體,讓用戶端可以不用很麻煩,每次都要到主機旁才能操作,安裝這三個遠端軟體其中一個,就可將遠端主機(如Raspberry Pi)的畫面傳回自己的電腦,不用親自跑到遠端主機就可以處理。有時協助朋友解決電腦問題,使用Teamviewer軟體,建立兩端連線,再利用電話,看著同一個畫面,討論處理的內容,減少很多要到場的麻煩。

以下紀錄我安裝 VNC、Teamview 及RDP 三套軟體的程序及方法:

[安裝VNC Server]

VNC是一種圖形桌面共享系統,可讓您從另一台電腦,執行 VNC Viewer,遠端控制另一台電腦 (執行 VNC Server) 的桌面界面。VNC Viewer將鍵盤,滑鼠或鍵盤事件發送到VNC 伺服器,並接收螢幕更新。

常見用來作為VNC Server的免費軟體就有好幾套,如 Tight VNC、Real VNC、Ultra VNC 等都是可以執行 VNC服務的系統。Raspbain 預設的 VNC版本是 RealVNC,只要到Config設定,即可開啟 VNC,執行以下指令進行設定:
$ sudo raspi-config

選擇 [5 Interfacing Options] ➔  [P3 VNC] ➔  [Yes]  即可啟動 VNC服務

如要手動安裝 RealVNC,指令如下:
$ sudo apt-get update
$ sudo apt-get install realvnc-vnc-server realvnc-vnc-viewer

執行 VNC Viewer,輸入遠端VNC Server主機的 IP,以及輸入登入帳號密碼:

按下 [Connect]後,輸入帳號密碼即可開啟遠端主機桌面。

在 Raspbian Jessie ( Debian 8)的環境下,可輸入以下幾個指令啟動或停止 VNC。

• 立即啟動VNC
$ sudo systemctl start vncserver-x11-serviced.service
• 下次開機時啟動VNC,且每次開機時都會自動啟動 VNC
$ sudo systemctl enable vncserver-x11-serviced.service
• 停止 VNC Server
$ sudo systemctl stop vncserver-x11-serviced.service
• 開機時,不再自動啟動 VNC 服務
$ sudo systemctl disable vncserver-x11-serviced.service


[安裝 TightVNC Server]

TightVNC 也是另一套蠻受歡迎的 VNC,可執行以下指令進行安裝:
$ sudo apt-get install tightvncserver

如有啟動VNC Server,可執行:
$ vncserver

第一次啟動時會要求密碼,您可輸入一次再輸入確認第二次密碼後,會詢問建立一個只能View的密碼,此時回覆No即可。

• 啟動VNC Session
$ sudo vncserver :1 -geometry 1024x728 -depth 24
• 停用VNC
$ sudo vncserver -kill :1 
• 啟動VNC服務
$ sudo service vncserver start 
• 停止VNC服務
$ sudo service vncserver stop

[設定螢幕解析度]
如要設定遠端連線開啟畫面時的解析度,可以執行 raspi-config 指令,進行設定
$ sudo raspi-config

選擇 [7 Advanced Options] ➔  [A5 Resolution] 設定解析度,如下圖。

[安裝Teamviewer]

TeamViewer是一個遠端控制軟體,相容於Microsoft Windows、Mac OS X、Linux、iOS、Android作業系統,也可以透過網頁瀏覽器連線已安裝TeamViewer的電腦。該軟體可以輕易的穿透防火牆。[維基百科]

目前 TeamViewer僅支援Raspberry Pi 2及 Pi 3,如果是 Pi 1和 Zero的使用者無法支援。如要下載程式,可以到 TeamViewer網站下載。以下是安裝的步驟與程序:

$ wget http://download.teamviewer.com/download/linux/version_11x/teamviewer-host_armhf.deb
$ sudo apt-get install -f
$ sudo dpkg -i teamviewer-host_armhf.deb;

這時在桌面上可以看到 TeamViewer的圖示,啟動後可看到可供遠端連線進來的 ID及密碼。

[安裝 XRDP Server]

遠端桌面協定(英語:Remote Desktop Protocol,縮寫:RDP)是一個多通道(multi-channel)的協定,讓使用者(用戶端或稱「本機電腦」)連上提供微軟終端機服務的電腦(伺服器端或稱「遠端電腦」)。大部份的Windows都有用戶端所需軟體。其他作業系統也有這些用戶端軟體,例如Linux、FreeBSD、Mac OS X。伺服端電腦方面,則聽取送到TCP 3389埠的資料。[維基百科]

另一種可以使用 Windows作業系統內建的遠端桌面,來當作VNC Viewer,就是在Server端安裝 XRDP,服務啟動後,就可以使用遠端桌面連接 Raspberry Pi。安裝指令如下:
$ sudo apt-get install xrdp

安裝後,從Windows作業系統執行「遠端桌面」,輸入Raspberry Pi的IP:

可以看到以下畫面,輸入帳號密碼後即可看到 Pi 的x-win桌面:


安裝 xrdp完成後,使用Windows遠端桌面連線時,會出現 error problem connecting,當時的情況有啟用VNC Server,不知是否是兩個設定衝突還是特殊的原因,重新安裝 tightvnc及 xrdp,就解決這個問題。

移除指令:
$ sudo apt-get remove xrdp vnc4server tightvncserver

安裝指令:
$ sudo apt-get install tightvncserver
$ sudo apt-get install xrdp


[參考資料]
• Raspberrypi.org:VNC (VIRTUAL NETWORK COMPUTING)
TightVNC Software
• TeamViewer
Share:

星期六, 3月 25, 2017

Arduino筆記(十八):Nokia 5510 單色顯示器

[2017/03/25]
今天發現一家新的電子材料行在市區開幕,就想說進去逛一下看看有沒有新的感測模組,剛好看到 Nokia 5510 LCD 這個模組,只覺得樹莓派有顯示畫面,如果是 Arduino 要如何將字元或圖像呈現在 LCD上。當下就花了 1百xx元買了一片,要來試看看 Arduino 如何將影像透過連接的 Pin 顯示到 LCD上。結果帶回去一試,螢幕中間那塊都變成深色,於是再拿回去店裡換一個新的。

有了顯示 LCD 後,我試著將 DS18B20這個測試溫度的IC 接上,將讀取的值顯示在 LCD上,請看以下實做的結果:


[材料]

• Arduino Uno x 1
• Nokia 5510 x 1
• DS18B20 溫度控制 IC x 1
• 可變電阻 x 1
• 連接線 x n條

[接線圖]



• Nokia 5110 LCD模組連接線路
Nokia 5510 LCD模組
Arduino
1 - RST – Reset
Pin 6
2 - CE – Chip Enable
Pin 7
3 - D/C – Data/Command Selection
Pin 5
4 - DIN – Serial Input
Pin 4
5 - CLK – Clock Input
Pin 3
6 - VCC – 3.3V
3.3V (VCC)
7- LIGHT – Backlight Control
*1
8- GND – Ground
GND

*1 : 連接可變電阻(中),可變電阻另兩腳分別接 3.3V 和 GND

• DS18B20 連接線路:
左腳接地;右腳接 5V;中間腳接 Arduino Pin 10及6.8 K Ohm後接 3.3V電源

[程式一]

程式內呼叫三個不同的函式庫,請到以下網址下載後,解壓縮放在執行 Arduino 資料夾內的 Libraries 目錄。

•  One Wire Library
•  PCD8544 library
•  DallasTemperature Library

#include <onewire.h>
#include <PCD8544.h>
#include <dallastemperature.h>

#define ONE_WIRE_BUS 10     // DB18B20 中間腳接的 Pin

// 設定 oneWire 與 DS18B20 通訊 
OneWire oneWire(ONE_WIRE_BUS); 

// 將 oneWire 參考值傳給 DB18B20 
DallasTemperature sensors(&oneWire);

// 畫一個笑臉
static const byte glyph[] = { B00010000, B00110100, B00110000, B00110100, B00010000 };

static PCD8544 lcd;

void setup() {
  // 與 PCD8544 相容的顯示器
  lcd.begin(84, 48);

  // 將笑臉圖案設定在位於 ASCII table 0 的位置.
  lcd.createChar(0, glyph);
  sensors.begin(); 
}

void loop() {
  static int counter = 0;

  // 寫入一段文字
  lcd.setCursor(0, 0);
  lcd.print("I'm Ceiling.");

  // 讀取 DS18B20溫度
  sensors.requestTemperatures();
  
  // 在畫面顯示計數器
  lcd.setCursor(0, 1);
  lcd.print(counter, DEC);
  lcd.write(' ');
  lcd.write(0);  // 畫一個笑臉

  // 將游標設定在第 3 列
  lcd.setCursor(1, 3);
  lcd.print("Temp: ");  
  lcd.print(sensors.getTempCByIndex(0));
  lcd.print("'C");  
    
  delay(200);
  counter++;
}

[程式二]

以下程式是顯示溫度,我保留原作者的程式碼,僅修改連接 LCD的 Pin 號,作者使用 LCD5110_Graph這個函式庫,可以將字型放大、縮小,讓大家參考。

/*
Author: Danny van den Brande, ArduinoSensors.nl. BlueCore Tech.
Hello world! this is a example project for the DS18b20 on a old Nokia 5110 LCD Display.
 */

#include <onewire.h>
#include <DallasTemperature.h>
#include <LCD5110_Graph.h>

#define ONE_WIRE_BUS 10

LCD5110 lcd(3,4,5,6,7);

extern unsigned char SmallFont[];
extern unsigned char BigNumbers[];
extern uint8_t borderRoundedIcon[];

char TempCelciusFahrenheit[6];

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

float tempC = 0;
float tempF = 0;

void setup(void)
{
  lcd.InitLCD();
  sensors.begin();
}

void loop(void)
{ 
  lcd.clrScr();
  lcd.drawBitmap(0, 0, borderRoundedIcon, 84, 48);
  sensors.requestTemperatures();
  tempC = sensors.getTempCByIndex(0);
  tempF = sensors.toFahrenheit(tempC);

//  convertToString(tempF); //these are Fahrenheit, uncomment when using degrees.
  convertToString(tempC);  //These are degrees, comment when using Fahrenheit.

  lcd.setFont(BigNumbers);
  lcd.print(TempCelciusFahrenheit,22,14); // You can set position of the text here.
  lcd.setFont(SmallFont);
  lcd.print("Temp.Celc.",17,5); //Comment when using Fahrenheit
  lcd.print("Temp.Fahr.",17,5); //Uncomment when using Celcius  lcd.update();  
  delay(1000);
}
void convertToString(float number)
{
   dtostrf(number, 3, 1, TempCelciusFahrenheit);
}

[執行結果]


[參考資料]

•  Sparkfun:Graphic LCD Hookup Guide
•  randomnerdtutorials.com:Complete Guide for Nokia 5110 LCD with Arduino
•  Danny van den Brande:Arduino - Nokia 5110 LCD temperature meter with the DS18B20
Share:

星期日, 3月 19, 2017

Arduino筆記(十七):即時時鐘 RTC 與 TM1637 四位數顯示器

[2017/03/19]
通常電腦關機之後重開機,能夠正確知道現在的時間,主要原因是主機板上有一個實時時鐘(RTC,real-time clock)晶片,搭配一顆電池;當電源關閉後,這個電池能持續提供時鐘運作。Arduino 為了成本考量,並沒有內建 RTC 晶片及電池,如果安裝 Arduino 網路擴展板,於開機時,可向網際網路的時間伺服器進行校時。如果沒有網路時,可以安裝 RTC 模組來校正時間,這也是我們今天要介紹的主題:RTC DS1302模組。

有一個額外的插曲:我兩三年前買的 RTC DS1302模組,換過新的電池後,依照線路圖接上,一點反應也沒有,在三確認了好幾次,還是不行,不知道是哪裡壞了,只好放棄。今天到光華商場再買一個新的,花了 50元,總算可以正常取得 RTC 的時間了。

[名詞]

實時時鐘(Real-time clock)是指可以像時鐘一樣輸出實際時間的電子裝置,一般會是積體電路,因此也稱為時鐘晶片。此名詞常用來表示在個人電腦、伺服器或嵌入式系統中有此機能的裝置,不過許多需要精確時的系統都會有此功能。
實時時鐘和定時器訊號(Clock signal)不同,後者只是數位電路中一個表示時間的方波訊號,而且不會以日常使用的時間單位表示。[維基百科]

網路時間協定(Network Time Protocol,簡稱NTP)是在資料網路潛伏時間可變的電腦系統之間通過封包交換進行時鐘同步的一個網路協定。自1985年以來,NTP是目前仍在使用的最古老的網際網路協定之一。[維基百科]

[材料]

• Arduino Uno  x 1
• DS1302 RTC 模組  x1
• TM1637 四位數七段顯示器模組 x1
• 連接線 x 11條

[接線圖]


• DS1302 與TM1637 模組連接線路
DS-1302模組
Arduino
 TM 1637四位數段顯示器
GND Ground
GND
 GND
VCC +5V
5V
 VCC
RST
Pin 2
 -
DAT
Pin 3
 -
CLK
Pin 4
 -
-
Pin 8
 DIO
-
Pin 9
 CLK

[程式]

#include <ds1302.h>
#include <tm1637display.h>

// DS1302 初始化設定
DS1302 rtc(2, 3, 4);

// 設定 TM1637 接腳
#define CLK 9
#define DIO 8

TM1637Display display(CLK, DIO);
boolean colon = true ;

String dw = "";
String hh = "";
String mm = "";
String ss = ""; 
float t = 0; 
  
void setup()
{
  // 設定時鐘執行模式,取消寫入保護
  rtc.halt(false);
  rtc.writeProtect(false);
  
  // Setup Serial connection
  Serial.begin(9600);
  display.setBrightness(0xA);
  
// 第一次設定寫入 DS1302 RTC時鐘,之後可以加上註解
//  rtc.setDOW(SUNDAY);         // 設定每週星期幾?
//  rtc.setTime(20, 16, 30);     // 設定24小時時間 20:16:30 
//  rtc.setDate(19, 3, 2017);   // 設定日期(日, 月, 年)
}

void loop()
{
  // 取得星期幾
  Serial.print(rtc.getDOWStr());
  Serial.print(" ");
  
  // 取得日期
  Serial.print(rtc.getDateStr());
  Serial.print(" -- ");

  // 取得時間
  dw = rtc.getTimeStr();
  Serial.println(dw);
  
  hh = dw.substring(0,2);          // 時數
  mm = dw.substring(3,5);          // 分鐘數
  ss = dw.substring(6,8);          // 秒數 

  // 顯示四位數中間的冒號
  uint8_t segto;
  int value = 1000;
  // 顯示 時:分
  int  t =  hh.toInt()*100  + mm.toInt();
  // 顯示 分:秒
  //  int t =  mm.toInt() *100 +ss.toInt();
  segto = 0x80 | display.encodeDigit((t / 100)%10);
  display.setSegments(&segto, 1, 1);
  delay(500);

  // 顯示時間
  display.showNumberDec(t, true);
  delay(500);
}

[參考資料]

•  Rinky Dink Electronics:DS1302 Library
•  Github:avishorp/Arduino library for TM1637 (LED Driver)
•  TMP1637 技術手冊


Share:

Arduino筆記(十六):讀取/寫入 SD 卡資料

[2017/03/19]
如果要在 Arduino 紀錄感測元件接收的數值,除了透過網路模組送出以外,開發板本身並不像 Raspberry Pi有外接 SD 或 Micro SD卡的介面,可以儲存在記憶卡上,要寫入資料到 SD卡上,就必須透過 SPI 的介面,才能將感測的資料寫入 SD卡。

我先測試一下 SD卡的模組是否能讀取/寫入檔案,如果可以,再改接網路擴展模組 W5100,看看相同的程式是否能存取W5100擴展板上 Micro SD卡的資料?結果如何呢?



[材料]

• Arduino Uno  x 1
• SD 卡模組  x1
• 連接線 x 6條

[接線圖]

• 註:上圖 DI 代表 SD卡模組 MOSI,DO代表 SD卡模組 MISO

• SD 卡模組連接線路
SD 卡模組
Arduino
GND
GND
+5V
5V
CS
Pin 4
MOSI
Pin 11
SCK (CLK)
Pin 13
MISO
Pin 12
SPI匯流排規定了4個保留邏輯訊號介面:(維基百科)
•  SCLK (Serial Clock):串行時鐘,由主機發出
•  MOSI (Master Output,Slave Input):主機輸出從機輸入訊號,由主機發出
•  MISO (Master Input,Slave Output):主機輸入從機輸出訊號,由從機發出
•  SS (Slave Selected) 或 Chip Select (CS):由主機發出,低電位有效

有關SPI的說明,可參考這篇:「Raspberry Pi 筆記(二):GPIO接腳與 I2C 及 SPI 安裝」。

[SD Library]

SD  Class
•  初始設定 SD卡及函式庫:SD.begin(cspin)
    cspin (選項): Arduino 連接SD卡模組 SS 或 CS的 Pin腳

•  檢查括號內的檔案名稱是否存在:exists()

•  建立目錄:SD.mkdir(filename)

•  開啟檔案:SD.open(filepath, mode)
   mode : FILE_READ  開啟唯讀檔案,從檔案開始處讀取.
               FILE_WRITE 開啟讀寫檔案,從檔案最後處開始寫入

•  移除檔案:SD.remove(filename)

•  移除目錄:SD.rmdir(filename)

File  Class
•  關閉檔案:file.close()

•  將data 字串寫到檔案中,不跳行:file.print(data)

•  將data 字串寫到檔案中,不跳行:file.println(data)

•  將 data 字串寫入檔案中:file.write(data)

[程式]

我參考Arduino IDE的兩個範例 cardinfo 跟 files,試著在 SD卡上,顯示 SD 卡的相關資訊,再建立一個檔案,寫入測試文字後,關閉檔案。以下是執行的測試程式:

#include <SPI.h>
#include <SD.h>

File myFile;

// 設定 SD library 功能變數:

Sd2Card card;
SdVolume volume;
SdFile root;

const int chipSelect = 4;

void setup() {

  Serial.begin(9600);  // 開啟通訊串列埠開啟
  while (!Serial) {    // 等待串列埠連線
  }

  //----------- 寫入檔案
  Serial.print("\nWaiting for SD card ready...");

  if (!SD.begin(4)) {
    Serial.println("Fail!");
    return;
  }
  Serial.println("Success!");

  myFile = SD.open("card.txt", FILE_WRITE);       // 開啟檔案,一次僅能開啟一個檔案
  
  if (myFile) {                                   // 假使檔案開啟正常
    Serial.print("Write to card.txt...");         
    myFile.println("Test to write data to SD card...");  // 繼續寫在檔案後面
    myFile.close();                               // 關閉檔案
    Serial.println("Completed!");
  } else {
    Serial.println("\n open file error ");    // 無法開啟時顯示錯誤
  }
  
   //----------- 顯示SD 卡資訊

  if (!card.init(SPI_HALF_SPEED, chipSelect)) {
    Serial.println("initialization failed. Check: SD Card");
    return;
  } else {
    Serial.println("============= Card Information ==================");
  }

  // 顯示SD卡類型

  Serial.print("Card type: ");
  switch (card.type()) {
    case SD_CARD_TYPE_SD1:
      Serial.println("SD1");
      break;
    case SD_CARD_TYPE_SD2:
      Serial.println("SD2");
      break;
    case SD_CARD_TYPE_SDHC:
      Serial.println("SDHC");
      break;
    default:
      Serial.println("Unknow");
  }

  // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
  if (!volume.init(card)) {
    Serial.println("Could not find FAT16/FAT32 partition.");
    return;
  }

  // 顯示類型和 FAT 空間大小
  uint32_t volumesize;
  Serial.print("Volume type is FAT");
  Serial.println(volume.fatType(), DEC);
  Serial.println();

  volumesize = volume.blocksPerCluster();    // clusters are collections of blocks
  volumesize *= volume.clusterCount();       // we'll have a lot of clusters
  volumesize *= 512;                            // SD card blocks are always 512 bytes
  Serial.print("Volume size (bytes): ");
  Serial.println(volumesize);
  Serial.print("Volume size (Kbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);
  Serial.print("Volume size (Mbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);

  Serial.println("\nFiles found on the card (name, date and size in bytes): ");
  root.openRoot(volume);

  // list all files in the card with date and size
  root.ls(LS_R | LS_DATE | LS_SIZE);

  Serial.println("================= Finished =====================");
}

void loop() {            // 在Setup()執行完成後,就等結束 
}

[執行結果]

換上W5100網路擴展板,程式一個字元都不需要更改,可以讀取擴展板上的 SD卡資料。我的 SD卡上有中文名稱,就會顯示部分中文沒問題,但大多數變成亂碼。檔案列表第一個 card.txt 就是測試時寫入的檔案。

[參考資料]

•  Arduino.cc :SD Library
Share:

Raspberry Pi 筆記(三十六):光照度感測模組- 模擬特雷門

[2017/03/19]
我們日常生活中有一種狀況是,當車輛開進室內停車場時,如果室內光線不足,很容易就影響駕駛者的視線。如果能在車輛進入較暗的室內時,自動開啟燈光,補足照明,就可以減少碰撞的發生。國內一般比較高級的車款都會配備這項功能,這個實驗我們嘗試以光照度感應的數值,判斷光線不足時,開啟 LED模擬打開車燈。另外一個有趣的實驗是「特雷門」樂器是用震盪頻率的改變來製造聲音。為什麼在這裡提到特雷門呢?因為我想試著用光度的變化來產生不同頻率的聲音。藉由線的變化,發出不同聲音,因為光度比較難掌控,發出來的聲音不夠準確,要演奏的難度很高,但是就當作是個好玩的實驗。


[特雷門]

特雷門這個樂器是在 1919年發明,樂器包含有兩個像天線的突出構造,一個是長直金屬桿,一個是環狀的水平金屬圈, 垂直桿控制音頻率高低,水平環狀圈控制音量大小。原理是利用手與天線構造的距離遠近,改變其電容之大小,而影響其振盪迴路之振盪頻率。
由於音色特殊,早期許多電影的恐怖氣氛都是用它來營造的。
不過因為手的位置沒有明顯的記號可參考,完全靠演奏者的感覺,音準控制不易,操作起來有相當的難度,極少人專精此項樂器。[維基百科]

來源:維基百科

[照度單位]

勒克斯 (Lux,通常簡寫為lx) 是一個標識照度的國際單位制單位,1流明每平方米面積,就是1勒克斯。也就是1勒克斯表示每平方米有1流明。

1勒克斯 = 1 流明/平方米 = 1 燭光·球面度/平方米(1 lx = 1 lm/m2= 1 cd·sr·m2)。

流明(英語:Lumen), 號為lm。是光通 的國際單位制的導出單位(derived unit),是從一光源放射出的可見光的量度。光通量(luminous flux)與輻射通量(radiant flux)不同,輻射通量是所有電磁波發光總功率的量度,而光通量量度則反映了人眼對不同波長光的變化敏感度。如果一個光源發射1燭光的發光強度到1個立體角的範圍裡,則到那個立體角的總發射光通量就是一流明。

[GY-30 光照度感測模組]

實驗所用的模組是 GY-30,主要是是 BH1750FVI 這個 IC 的功能,模組的特點包括:

• 電源:DC3 ~ 5V
• 感測範圍:1 ~ 65535 Lux
• 感測器內置16bit,AD轉換器直接數位輸出,省略複雜的計算
• 不區分環境光源、接近於視覺靈敏度的分光特性
• 可對廣泛的亮度進行1Lux的高精度測定
• 受到紅外線影響非常小
• 標準NXP IIC通信協定
• 模組內部包含通信電位轉換,與 5v 開發板直接連接


[接線圖]


• GY-30 模組連接線路
GY-30模組
Raspberry Pi
GND Ground
Pin 14 GND
ADD Address select
Pin 6 GND
SDA  I2C SDA
Pin 3 GPIO 2
SCL  I2C SCL
Pin 5 GPIO 3
VCC 3.3V
Pin 1 3.3V

Pin 11 GPIO 17 LED

[程式一]

當照度值小於300 時,開啟 LED 燈。

#!/usr/bin/python
import smbus
import time
import sys
import os
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD)

# Define some constants from the datasheet
DEVICE     = 0x23 # Default device I2C address

ONE_TIME_HIGH_RES_MODE_1 = 0x20
ONE_TIME_HIGH_RES_MODE_2 = 0x21

num = 0
LED = 11
GPIO.setup(LED,GPIO.OUT)

bus = smbus.SMBus(1)  # Rev 2 Pi uses 1
def convertToNumber(data):
  # 將 2位元轉成數字
  return ((data[1] + (256 * data[0])) / 1.2)

def readLight(addr=DEVICE):
  data = bus.read_i2c_block_data(addr,ONE_TIME_HIGH_RES_MODE_2)
  return convertToNumber(data)

def main():

  while True:
    num = readLight()
    print "Light Level : " + str(num) + " lx"
    if num < 300:
       GPIO.output(LED,GPIO.HIGH)
    else:
       GPIO.output(LED,GPIO.LOW)
    time.sleep(0.2)

if __name__=="__main__":
   main()


[程式一執行結果]


[程式二]

要執行本程式前,需先安裝 sox 套件,執行指令如下:

$ sudo apt-get install sox

安裝完成後,就有一個 play 程式可播放聲音,透過光的照度變化產生不同聲音:

#!/usr/bin/python
import smbus
import time
import sys
import os

DEVICE     = 0x23 # Default device I2C address

ONE_TIME_HIGH_RES_MODE_1 = 0x20
ONE_TIME_HIGH_RES_MODE_2 = 0x21

bus = smbus.SMBus(1)  # Rev 2 Pi uses 1

def convertToNumber(data):
  # 將 2位元轉成數字
return ((data[1] + (256 * data[0])) / 1.2) def readLight(addr=DEVICE): data = bus.read_i2c_block_data(addr,ONE_TIME_HIGH_RES_MODE_2) return convertToNumber(data) def main(): while True: print "Light Level : " + str(readLight()) + " lx"
    # 呼叫作業系統程式 play
    str2 = 'play -n -q synth .07 sin ' + str(readLight())
    os.system(str2)
#   time.sleep(0.1)  // Control Speed

if __name__=="__main__":
   main()

[程式二執行結果]


[參考資料]

• raspberrypi-spy:BH1750FVI I2C Digital Light Intensity Sensor
Share:

星期三, 3月 15, 2017

Arduino筆記(十五):控制伺服馬達 Servo

[2017/03/14]
先前寫的一篇文章:「Arduino筆記(八):控制步進馬達」,說明如何使用Arduino 的 Stepper 函式庫,控制步進馬達的正反轉。這篇文章要來實做一下伺服馬達的功能?同樣都是馬達,步進馬達和伺服馬達有和不同?同樣都可控制正轉反轉以及轉速,簡單的說最大的差別在於伺服馬達有回饋反授的機制,會將訊號傳回給驅動器;而步進馬達不會回傳訊號,這就是最大的不同。也因為這個回饋反授機制,讓伺服馬達轉動時的精確度較高,不會有失步的狀況。以下就來看看如何透過 Arduino 控制伺服馬達。

Adruino 開發板提供的電壓是 5V,如果供給 MG996R伺服馬達使用,在空轉情況下應該沒有問題,但如果接有其他電子零件,可能就會不夠,建議再加額外的電源給伺服馬達,本篇也會說明外接電力的方式。


[Servo Library]

•  attach(pin, min, max) :連接伺服馬達的控制線給 Arduino 的一個 Pin 腳
  pin : 在早期的 Arduino 只能指定 Pin 9 跟 Pin 10
  min (選項) : 對應於伺服上的最小 ( 0度) 角度的脈衝寬度 (以微秒為單位),預設為 544
  max (選項) : 對應於伺服上的最大 (180度) 角度的脈衝寬度 (微秒),預設為 2400

•  write(angle) :將值寫到伺服機相對應的控制軸
  angle: 寫到伺服機的值,從 0 到 180

•  read():讀取目前的伺服機值

[材料]

• Arduino Uno x 1
• 伺服馬達 (型號 MG996R) x1
• 連接線 x 3條

MG996R  伺服馬達規格
•  角度:180度 (對應的角度是-90度~+90度)
•  扭力:11KG 大扭力舵機/金屬齒輪伺服器
•  產品淨重: 55g
•  產品拉力: 9.4kg/cm(4.8V ), 11kg/cm(6V)
•  反應速度: 0.17sec/60degree(4.8v) 0.14sec/60degree(6v)
•  工作電壓: 4.8-7.2V
•  齒輪形式: 金屬齒輪

[接線圖一:讓伺服馬達正反轉] 

• MG996R連接線路
MG996R
Arduino
棕色線 GND
GND
紅色線 V+
5V
橘色線 PWM
Pin 9

• MG996R 直接連接接 Arduino 電源

• MG996R 外接電源,再連接 Arduino


[程式一]

#include <Servo.h>

Servo myservo;  // 建立一個 servo 物件,最多可建立 12個 servo

int pos = 0;    // 設定 Servo 位置的變數

void setup() {
  myservo.attach(9);  // 將 servo 物件連接到 pin 9
}

void loop() {
  // 正轉 180度
  for (pos = 0; pos <= 180; pos += 1) // 從 0 度旋轉到 180 度,每次 1 度 
  {     
    myservo.write(pos);               // 告訴 servo 走到 'pos' 的位置
    delay(15);                        // 等待 15ms 讓 servo 走到指定位置
  }

  // 反轉 180度
  for (pos = 180; pos >= 0; pos -= 1) // 從 180 度旋轉到 0 度,每次 1 度 
  { 
    myservo.write(pos);               // 告訴 servo 走到 'pos' 的位置

    delay(15);                        // 等待 15ms 讓 servo 走到指定位置
  }
}


[接線圖二:利用電阻控制伺服機] 


• MG996R及電阻連接線路
MG996R
Arduino
電阻
棕色線 GND
GND
Pin 1
紅色線 V+
5V
Pin 3
橘色線 PWM
Pin 9
-
-
Pin A2
Pin 2

[程式二]

#include <Servo.h>
Servo myservo;      // 建立一個 servo 物件,最多可建立 12個 servo

int r1_pin = 2;     // 可變電阻連接的 pin 腳
int r1_val;         // 從可變電阻來的數值 

void setup() 
{ 
  myservo.attach(9);  // 將 servo 物件連接到 pin 9
} 

void loop() 
{ 
  r1_val = analogRead(r1_pin);         // 從可變電阻讀取類比數值 (介於 0 跟 1023) 
  r1_val = map(r1_val, 0, 1023, 0, 179);  // 將類比值轉換為 servo 可讀取的範圍值(介於0 跟 180) 
  myservo.write(r1_val);                  // 設定 servo 位置 
  delay(15);                           // 等待 15ms 讓 servo 走到指定位置
} 


[實驗結果]


[參考資料]

•  MG996R Metal Gear Dual Ball Bearing Servo Datasheet
Arduino.cc:Servo library
• Arduino IDE Sample program

Share: