本文旨在補充《超圖解Arduino互動設計入門》第五章「序列埠通訊程式」,以及第十章「透過序列埠調整燈光亮度」的讀取序列埠值。動手做10-3與10-4的程式碼,都有一個判斷傳入值是介於’0’~’9’的條件式,例如,底下是diy10_4.ino的程式片段:
if (Serial.available()) { // 讀取傳入的字元值 while ((chr = Serial.read()) != '\n') { // 確認輸入的字元介於'0'和'9',且索引i小於3(確保僅讀取前三個字) if (chr >= '0' && chr < = '9' && i < 3) { data[i] = chr; i++; } } : : }
將此上傳程式到Arduino,再開啟「序列埠監控視窗」,並輸入"123"字串,將能看到Arduino接收到完整的數字123:
若修改程式碼,刪除「確認輸入的字元介於'0'和'9'」的判斷條件:
if (Serial.available()) { // 讀取傳入的字元值 while ((chr = Serial.read()) != '\n') { // 確認索引i小於3(確保僅讀取前三個字) if ( i < 3 ) { data[i] = chr; i++; } } : : }
再度測試程式碼,Arduino都只能收到第一個字元。
這是因為當程式察覺到有序列資料進入時,就開始連續讀取三個字元。可是,序列資料的傳入速度遠不及程式迴圈的執行速度,所以存入data陣列的第2和第3個元素值都是-1(代表序列埠沒有輸入值):
因此,上面的程式碼必須加入判斷條件;除了篩選出數字之外,還可以用排除 -1 的方式,像這樣:
if (Serial.available()) { // 讀取傳入的字元值 while ((chr = Serial.read()) != '\n') { // 確認字元值不等於-1,而且索引i小於3(確保僅讀取前三個字) if (chr != -1 && i < 3 ) { data[i] = chr; i++; } } : : }
補充說明,-1的字元值會在「序列埠監控視窗」顯示成 'ÿ'。所以,假如序列埠監控視窗顯示出 'ÿ' 字元,可能需要檢查序列通訊程式是否有正確收到資料。
非常感謝趙老師快速的回覆!!謝謝您!!
不客氣~
請問老師,在超圖解Arduino 互動設計入門(第二版),有關於IRremote.h 的使用,範例12-2 中的編繹過程有個問題
IRrecv irrecv(RECV_PIN); 顯示”IRrecv” does not name a type. 以及以下描述:
diy12_2.ino: In function ‘void loop()’:
diy12_2:17: error: ‘irrecv’ was not declared in this scope
diy12_2:17: error: ‘results’ was not declared in this scope
請問,這個問題原因在那裡? 謝謝.
hi john:
請先把光碟「程式庫」資料夾裡的”IRremote”資料夾,複製到Arduino安裝路徑裡的”libraries”資料夾,或者「我的文件」裡的”Arduino\libraries”路徑。
以後開啟Arduino軟體,就能正確編譯程式了。
thanks,
jeffrey
老師您好
請問有沒有方法能夠不透過序列埠控制視窗來直接控制arduino呢?謝謝
hi eli:
arduino軟體內建的「序列埠監控視窗」其實是一個序列埠通訊程式,你可以用其他序列埠通訊軟體取代,例如:AccessPort和CoolTerm,只要序列通訊協議(如:傳輸速率)一致即可。
thanks,
jeffrey
趙老師您好:
請問Arduino的A0~A5,能當示波器一樣,讀入sin波的訊號嗎?還是只能讀入一般直流電的訊號,因為我拿來接信號產生器,讀出來的數值都是正值! 沒辦法讀出負半週期的值! 謝謝!
hi jenson:
Arduino的類比輸入埠預設只能接受0~5V的訊號,超過這個範圍的類比訊號(含負值),都要先經過額外的電路,將訊號縮限在0~5V之內。最基本的方法是採用電阻分壓,請先參閱:
我週一或週二再補充說明。
thanks,
jeffrey
假設輸入的類比訊號電壓介於-10V和10V之間,連接Arduino控制板時,要先經過以下的電阻分壓電路,將訊號電壓限制在0~5V之內:
經過電阻分壓之後,輸入Arduino的電壓值會降低,而且會低於Arduino的工作電壓(5V)。
假設我們預期的最高輸入值是4V,R1, R2和R3電阻值的計算方式如下:當輸入訊號為負電位時,電流將朝R1和R2方向流動,不會經過R3,因為「接地」的電位高於「負電位」。
所以R1和R2將構成分壓電路,Arduino的類比輸入埠將檢測到0V。為了方便計算,取R2為1KΩ,我們先求取最大負電壓的分壓電阻值,從底下的算式得知R1值為400Ω:
當輸入訊號高於5V時,將不會有電流流過R1,此時R2和R3形成分壓電路:
從底下的算式得知R1值約670Ω:
如果你需要放大輸入的類比訊號電壓,可使用運算放大器,詳細請參閱書本6-14頁,運算放大器的偏壓電路。
thanks,
jeffrey
老師您好,我想詢問直接將數字經由序列埠傳達給arduino在如何撰寫呢??我設定的數字是溫度值0~30度,但我如何傳達數字給arduino??請老師協助
hi yuan:
10-10頁「動手做10-3:透過序列埠調整燈光亮度」的程式碼,就能符合你需求,請自行修改程式。
thanks,
jeffrey
趙老師你好!我參照老師新版的ARDUINO超圖解互動設計入門2的第5-18頁
我程式碼打跟上面完全一樣,但打開序列負監控視窗都沒有任何訊息,按下Reset鍵也是一樣
我的版子是OZONE的,但應該也是可以用吧,我一直找不到原因,請老師協助!
你的控制板是Leonardo相容板,請在初始化序列埠敘述之後,加入底下的while迴圈試試看:
thanks,
jeffrey
謝謝老師!!它顯示出來了:))
不客氣~
老師您好,我目前使用Arduino MEGA 2560,想透過電腦傳訊息(經由Serial)控制Arduino,在由Arduino傳同樣的訊息(經由Serial1)控制其他模組,但是我遇到問題了!
電腦控制Arduino沒問題,但是Arduino沒辦法經由(Serial1)傳遞同樣的訊息給其他模組,執行過Serial1.write(“有字元”)後,Serial1.available()一直回傳”0″,導致Serial1.read()持續回傳”-1″,請問這大概是甚麼問題呢?是我對Serial1.write()不夠了解嗎?
抱歉問題有點長和模糊,但困擾我很久了,希望老師能解惑,謝謝!
hi andy:
根據你的描述,你有兩個Arduino控制板,分別是A和B;電腦接A,A的Serial1(18和19腳)接B的Serial1(19和18腳),在A上執行Serial1.write(),但是B的Serial1.read()始終讀不到資料?請問你的接線正確嗎?
thanks,
jeffrey
老師您好,謝謝您的回覆,我目前是用USB控制Arduino,Arduino上有外接一個無線通訊晶片(從18and19接線),想測試從電腦上直接下命令(AT command),看看Arduino上的外接無線通訊晶片有沒有回應,不是有兩塊Arduino,不知道這樣敘述夠不夠清楚,謝謝老師!!
謝謝老師!我想我發現程式碼中的問題了!
想順便請問老師,我有購買Arduino 互動設計入門第二版,請問書裡面有沒有Serial.write()的相關章節呢?
或是更詳細的Serial函數教學(除了第五章”序列埠通信”之外),再次謝謝老師熱心回應!
Serial.write()和Serial.print()的主要差異是print()會將輸入的數字或文字都轉換成字元,而write()則會把數字轉換成對應的ASCII字元。
例如:
Serial.print(65); // 輸出’6’和’5′
Serial.write(65); // 輸出’A’
thanks,
jeffrey
謝謝!!
老師你好,我之前想透過監控視窗顯示從0.0上升到1.0、再從1.0下降到0.0
程式碼是:
double brightness = 0.0;
double fade = 0.1;
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println(brightness);
brightness = brightness + fade;
if(brightness == 0.0 || brightness == 1.0)fade = -fade;
}
我試過把double改成int,小數改為整數就可以運作正常,但不知道為什麼小數就只會一直加上去,完全不理if的條件…請老師幫忙解惑!!
hi yen:
浮點數運算經常會遇到精確度的問題,以你的需求來說,最簡單的解決方法是先用整數運算,再換算成浮點數:
thanks,
jeffrey
謝謝老師~
另外可以稍微講一下他進行浮點數運算遇到的問題嗎?
簡單的說,十進位的小數部份轉換成二進位,要不斷地乘以2,直到小數部份為0;0.1轉成二進位會變成無限循環的數字,但電腦的記憶體容量有限,無法表示無限長度的數字,所以會出現誤差。詳細請參閱冼鏡光老師的「使用浮點數最最基本的觀念」這篇文章說明。
thanks,
jeffrey
恩恩 原來如此!! 謝謝老師的講解^^
老師您好,我想用Arduino來取代示波器讀取電路中訊號的頻率(300kHz附近),是否是如您上面回答,用分壓電路的方法使訊號限制在0-5V,再計數讀到最大值(1023)n次的時間t,然後得到頻率為n/t。不知道這樣的想法是否正確,還是有其他方法?
hi yun:
搜尋 “arduino oscilloscope diy” 關鍵字,即可找到許多相關的範例,像這個結合MATLAB和Arduino的示波器範例。
thanks,
jeffrey
老師您好
我是德明的學生 想請問您
我用RN131當作wifi module
之後用android寫了一個socket按按鈕回傳數字
rn131跟 手機都接上熱點 而rn131也接上了arduino
但我不知道arduino要寫什麼才能收到回傳的數字
煩請老師解答了!!
hi DanYiChen:
我猜想你的Android程式不是採用HTTP協定,請參考Arduino IDE軟體 “檔案 > 範例 > Ethernet > Telnet Client” 範例中的loop()迴圈程式寫法。
thanks,
jeffrey
恩恩!!謝謝老師的講解^^
老師您好我想請問一下
如何讓感測器測得的值 顯示再序列埠
使用繼電器去做判斷序列埠那邊測得值是否該斷電
例如我現在感測器得值 序列埠顯示8
繼電器的控制程式有一段是
val = analogRead(SensorPin);
if (val>=8);
{
digitalWrite(relay1,HIGH); // 繼電器1導通;
digitalWrite(relay2,LOW); // 繼電器2開關斷開;
但好像不會如我寫的程式去做動作
請參閱5-22頁說明,把 if (val>=8) 改成 if (val>=’8′)。
thanks,
jeffrey
老師您好
看過5-22頁
如果我的感測器在序列埠中顯示7.44
我程式改if (val>=’8′);是字元類型的判斷
這樣好像會無法動作
有辦法感測器測出得在序列埠顯示5.5
我繼電器本身去做判斷嗎
你可以用String類型物件的toFloat()方法將字串轉換成浮點數字,以這個範例程式來說:
編譯並上傳程式碼到Arduino後,在序列埠監控視窗輸入13.5(預設的行結尾請選用 “NL(newline)”),監控視窗將顯示:Input data: 13.50
thanks,
jeffrey
請問老師
要是遇到讀取類比訊號時再用serial.write TX RX傳到第二塊面板
但 傳遞過慢 導致已經動完去還沒顯示數值,這該怎麼處理
Arduino Uno的類比數位轉換器的時脈頻率是125 KHz,每一次轉換都要需要13個週期,所以類比取樣頻率是9615 Hz。
請把序列鮑率提高到115200。
thanks,
jeffrey
請問老師
因要讀取一個表頭數值
但要先發送一串16進制過去後,表頭會回報一串16進制數值回來
但該如何做才能確認它有傳回來且設定讀取幾筆資料呢?
在UART序列埠收送的資料,沒有「進制」之分,反正通常都是一次傳送一個位元組,你可以說,一個資料是介於0~255的十進位,或者0x00~0xFF的16進位,兩者的本質是一樣的。
你只要依照規格書的要求(傳輸速度和內容)傳遞資料,並等待接收就好啦~
thanks,
jeffrey
請問老師:
動手做10-3和10-4都有用到 if(Serial.available()) 這個判斷式
Serial.available()這個函數會顯示出序列埠暫存的字元(好像是64個字元???)
我用Uon板在Void loop{}內只寫了Serial.println( Serial.available() );
然後一直從序列埠送出字串給Arduino Uno,但好想到63之後就不會再增加數值了
那如果我們輸入的總字元數超過64會發生什麼樣的變化???
我是著在這兩個範例執行時輸入很多的字元,但是都不會發生錯誤
所以很好奇暫存滿了後會媽生什麼事情
10-3是用Serial.read()來抓下一個字元,如果暫存滿了之後它會是如何運作的呢?
10-4是用i++去撈出新輸入字串的第一個暫存位置,,如果暫存滿了之後它會是如何運作的呢?
Arduino Uno板預設的序列埠緩衝區是64位元組沒錯,請參閱5-20頁底下的說明。
若緩衝區資料大於64位元組,將無法再接收新進的資料。
thanks,
jeffrey
那有什麼方法可以直接清空緩存嗎?
上網搜尋好像沒有直接清除序列埠緩存的函式
但有看到說用Serial.read()取得資料號就會清除已讀取資料
不知道還有沒有其他的方式
確認某個程式物件具有哪些功能,可直接查閱官方的指令說明文件,例如Serial說明頁,沒看到直接清空緩存的指令(註:1.0版之前用flush)。
thanks,
jeffrey
老師 我想請問一下 我想用processing接收arduino的序列埠的值 但是我傳到processing的時候 都會是ASCII碼
例如:從arduino傳1過去 processing接收到的值會是 49 13 10 1
有沒有辦法只接收到1就好了呢??
你應該在Processing中,把ASCII轉換成字元值。
thanks,
jeffrey
您好想請問一下
我有兩塊板子mega2560+ramps1.4和Pro Micro
在Pro Micro上有紅外線壁障模組,當偵測到障礙物時想寫入指令到mega2560這張板子裡
目前卡在不知道如何寫入指令到mega2560這張板子裡,請問有辦法嗎謝謝
你可以用序列埠或I2C介面串連兩個板子。
thanks,
jeffrey
但mega2560上已經插了ramps1.4沒辦法用I2C的方式
那請問序列埠的方式要如何讓兩個不同COM的板子溝通呢謝謝
可以使用I2C,但如果要用UART通訊,可以用SoftwareSerial自訂接腳。
thanks,
jeffrey
老師你好,
請問available在UNO板能用,
但是在D1板不能跑進去,請問該怎麼解決呢?
謝謝您!
#include
SoftwareSerial Serial2(0,1);
while (Serial2.available()) {
char c;
c =Serial2.read();
}
軟體序列埠請接其他腳。
thanks,
jeffrey
您好,想請問如果arduino預設rxtx只有一組,可以利用SoftwareSerial增加txrx腳位嗎?
可以
老師您好
最近小弟在專題遇到了一些問題,想請老師相助
是有關於樹莓派python與Arduino通訊的問題
我想實現的是當python傳送一個訊號給Arduino時Arduino會去控制8×8點矩陣顯示上或下箭頭
但現在只能實現一半,當我傳送1時可以顯示上箭頭,但當我繼續輸入2訊號時卻不能改變箭頭方向
下方是Arduino的程式碼
void loop() {
if(Serial.available()){
if(date = Serial.read()){
while(date == ‘1’){
p1();
date = Serial.read();
}
while(date == ‘2’){
p2();
date = Serial.read();
}
}
}
}
下方為python程式碼
import serial
Port = “/dev/ttyUSB0” # 串口
baudRate = 9600 # 波特率
ser = serial.Serial(Port, baudRate, timeout=1)
while True:
a = int(input(“輸入信號”))
if a == 1:
send = ‘1’ # 发送给arduino的数据
elif a == 2:
send = ‘2’
elif a == 3:
send = ‘3’
else:
break
ser.write(send.encode())
str = ser.readline().decode() # 获取arduino发送的数据
if(str == ‘U\r\n’):
print(‘上’)
if(str == ‘D\r\n’):
print(‘下’)
if(str == ‘L\r\n’):
print(‘左’)
ser.close()
應改成類似;
老師謝謝您的回覆
還想請教一個問題
就是因為我想呈現的是傳送一個訊號後顯示的箭頭是會持續顯示的
直到訊號改變之後箭頭跟著改變(像是給1顯示上,給2顯示下)
那老師說的方法我也試過了,也試過很多其他方法
但出來的結果不是燈只閃一下就是會持續亮但無法再改變箭頭方向
所以想請老師指點指點
你的程式架構應該類似這樣:
已經解決我的問題了
非常謝謝老師!
不客氣
老師您好~
看了這個網頁介紹取從序列埠取輸入字元方式
我照其主程式用法,可編譯及燒錄,但燒錄進ESP8266後
序列埠只要輸入任何一個值+enter鍵就會出現像是錯誤文字出來~
不知道問題在哪?麻煩老師解答謝謝
主程式
int i=0;
char chr;
char Data[4];
// 算時間用的
int startTime;
int Duration; //已經過時間1
int OnTime=3000; //1000=1秒,可自訂時間10秒
void setup() {
Serial.begin(115200); //set up serial library baud rate to 9600
//算時間程式區
startTime=millis();
}
void loop() {
if (Serial.available()) {
while ((chr = Serial.read()) != ‘\n’) {
// 確認字元值不等於-1,而且索引i小於3(確保僅讀取前三個字)
if (chr != -1 && i =OnTime){
Serial.print(“\n每區間顯示: “);
Serial.print(“Data= “);
Serial.print(Data);
startTime=millis();
}
}
========
以下是呈現的序列埠錯誤訊息
————— CUT HERE FOR EXCEPTION DECODER —————
Soft WDT reset
>>>stack>>>
ctx: cont
sp: 3ffffdd0 end: 3fffffc0 offset: 01a0
3fffff70: 3fffdad0 3ffee4e8 3ffee50c 40202585
3fffff80: 00000031 00000000 3ffee50c 4020110b
3fffff90: 3fffdad0 00000000 3ffee50c 40201093
3fffffa0: feefeffe 00000000 3ffee560 4020190c
3fffffb0: feefeffe feefeffe 3ffe85dc 40100b51
<<<stack<<<
————— CUT HERE FOR EXCEPTION DECODER —————
ets Jan 8 2013,rst cause:2, boot mode:(3,6)
load 0x4010f000, len 3460, room 16
tail 4
chksum 0xcc
load 0x3fff20b8, len 40, room 4
tail 4
chksum 0xc9
csum 0xc9
v00041c60
~ld
i = OnTime 的等號要改成 == (兩個等號)
老師您好~我還是會發生可編譯,可寫入,但會出現序列阜視窗錯誤碼
但該開發版跑其他wifi相關伺服器程式則沒問題~
序列阜速度也跟程式碼一樣
我後來取這頁另一個範例,程式碼只有很陽春一點點,一樣情形
不知道問題在哪?煩請解惑謝謝~
硬體是ESP8266
主程式如下
int i=0;
char chr;
char data[4];
void setup() {
Serial.begin(115200); //set up serial library baud rate to 9600
}
void loop() {
if (Serial.available()) {
// 讀取傳入的字元值
while ((chr = Serial.read()) != ‘\n’) {
// 確認字元值不等於-1,而且索引i小於3(確保僅讀取前三個字)
if (chr != -1 && i >>stack>>>
ctx: cont
sp: 3ffffd90 end: 3fffffc0 offset: 01a0
3fffff30: 00000003 00000100 0001c200 00000000
3fffff40: 4020240b 3ffef1c4 00000000 40202416
3fffff50: 3fffff80 3ffef1dc 3ffee4d4 3ffee53c
3fffff60: 007a1200 0f818351 3ffee400 3ffee53c
3fffff70: 3fffdad0 3ffee4d4 3ffee4b0 402024c5
3fffff80: 00000031 3ffee4d4 3ffee528 402010ab
3fffff90: 3fffdad0 00000000 3ffee528 4020106f
3fffffa0: feefeffe 00000000 3ffee528 4020184c
3fffffb0: feefeffe feefeffe 3ffe85d8 40100b51
<<<stack<<<
————— CUT HERE FOR EXCEPTION DECODER —————
ets Jan 8 2013,rst cause:2, boot mode:(3,6)
load 0x4010f000, len 3460, room 16
tail 4
chksum 0xcc
load 0x3fff20b8, len 40, room 4
tail 4
chksum 0xc9
csum 0xc9
v00041b80
~ld
再看了一下你上一個程式,邏輯是錯的,根本沒有計算時間間隔,請自行修改。