我向Adafruit買了個Tilt ball switch,可用來偵測是否傾斜。金屬小管子有兩隻腳,在管子內有顆金屬球,當管子成直立狀態,也就是腳朝下時,金屬球會落下接觸管子內的兩個接觸點接通兩隻腳,形成通路,若管子傾斜到一定程度後,就會斷路。
這是Tilt ball switch,tilt意思是傾斜、以管子裡的ball球控制、可當做switch開關使用。傾斜時可聽到裡頭球撞擊管子的聲音。
底下是接線圖,其實就跟一般開關沒什麼兩樣。管子的一隻腳接10k歐姆電阻後接5V,這隻腳也接Arduino的數位腳位2,另一隻腳接地。
以下是軟體部分。
int pin = 2;
int ledpin = 13;
void setup()
{
pinMode(pin, INPUT);
pinMode(ledpin, OUTPUT);
}
void loop()
{
int status;
status = digitalRead(pin);
digitalWrite(ledpin, status);
delay(100);
}
按照這份電路圖與這支程式,平常直立時,形成通路,所以腳位2會讀到LOW,於是LED不亮。若傾斜球開關,裡頭的球離開兩個接觸點,變成斷路,腳位2會讀到HIGH,於是點亮LED。
根據規格書,起作用的傾斜角度是30度,但實際狀況仍須試驗後才知道。這種感測器很便宜,號稱是窮人的加速度感測器,但用起來也不盡理想,並非從某個角度以上都是通路,而從該角度以下都是斷路,實際使用時,管子內的小球可能會亂跑,形成類似一般機械開關的彈跳(bounce)現象。
可到YouTube觀看實際使用時的影片。
這篇寫的有點簡略,關於「開關」,請參考我之前寫的「Arduino練習:以開關切換LED明滅狀態」。
This comment has been removed by the author.
ReplyDelete請問您使用哪個藍牙模組?
Delete譬如Bluetooth Modem - BlueSMiRF Silver
https://www.sparkfun.com/products/10269
http://goods.ruten.com.tw/item/show?21204112856596
ReplyDelete我用的是這個
找不到什麼資料耶。
Delete應該都是透過序列埠TX,傳送AT指令,
需查詢文件才知道查詢信號強度的指令是哪個,
然後從序列埠讀取回應資料並解析,抽出需要的部份。
你說「AT指令還會包含MAC位址,要怎麼單只把訊號強度值...」,你已經得到回應了嗎?那麼該回應資料的格式是什麼呢?
+INQ:1234:56:0,1F1F,-53
ReplyDelete後面的53是訊號強度,但它一次都要顯示這一整行,我只想把後面的53存入變數來計算。
這應該是ASCII字串吧,有一定的格式,
Delete你可以丟進產生String物件,http://arduino.cc/en/Reference/StringObject
然後使用lastIndexOf找出最後一個「,」的索引值,以+INQ:1234:56:0,1F1F,-53來說會是19,
然後用substring擷取出「-53」,參數應該要丟入19+1,也就是substring(20),
就可以得到字串-53,
再用toInt()轉成int,就可以純入一般int變數了。
順帶一問,為什麼您不一開始就說明使用哪個Bluetooth模組、以及已經取得的資料「+INQ:1234:56:0,1F1F,-53」呢?
因為我本來是想直接抓晶片的腳位來截取數值,不想使用指令,我實驗後發現指令很容易因為存取多筆資料而混亂,會出現很多亂碼。
Delete謝謝版主的建議,我來試試看!
This comment has been removed by the author.
ReplyDelete試試看吧
Deletevoid setup()
{
Serial.begin(115200);
char data[] = "+INQ:1234:56:0,1F1F,-53";
char temp[100];
Serial.print("Original data is ");
Serial.println(data);
String s = String(data);
s.toCharArray(temp, 100);
Serial.print("data in String object is ");
Serial.println(temp);
int idx = s.lastIndexOf(',');
Serial.print("index of , is ");
Serial.println(idx);f
String s2 = s.substring(idx+1);
s2.toCharArray(temp, 100);
Serial.print("the piece of data we want is ");
Serial.println(temp);
int rst = s2.toInt();
Serial.print("finally we got ");
Serial.println(rst);
}
void loop()
{
}
謝謝版主
Delete不過這個程式的功能好像沒辦法抓到一直持續輸出的信號,
我接收到的資料是這樣無限不停重複的串列埠資料
如下:
+INQ:84:00:d2:0e:58:49,5a0204,-51
+INQ:84:00:d2:0e:58:49,5a0204,-54
+INQ:84:00:d2:0e:58:49,5a0204,-47
+INQ:84:00:d2:0e:58:49,5a0204,-47
+INQ:84:00:d2:0e:58:49,5a0204,-46
+INQ:84:00:d2:0e:58:49,5a0204,-48
+INQ:84:00:d2:0e:58:49,5a0204,-46
+INQE
+INQS
+INQ:84:00:d2:0e:58:49,5a0204,-45
+INQ:84:00:d2:0e:58:49,5a0204,-48
+INQ:84:00:d2:0e:58:49,5a0204,-46
+INQ:84:00:d2:0e:58:49,5a0204,-46
+INQ:84:00:d2:0e:58:49,5a0204,-48
+INQ:84:00:d2:0e:58:49,5a0204,-47
+INQ:84:00:d2:0e:58:49,5a0204,-48
+INQ:84:00:d2:0e:58:49,5a0204,-48
+INQ:84:00:d2:0e:58:49,5a0204,-46
+INQE
+INQS
+INQ:84:00:d2:0e:58:49,5a0204,-46
+INQ:84:00:d2:0e:58:49,5a0204,-48
+INQ:84:00:d2:0e:58:49,5a0204,-46
+INQ:84:00:d2:0e:58:49,5a0204,-48
+INQ:84:00:d2:0e:58:49,5a0204,-47
+INQ:84:00:d2:0e:58:49,5a0204,-49
+INQ:84:00:d2:0e:58:49,5a0204,-48
+INQ:84:00:d2:0e:58:49,5a0204,-46
+INQ:84:00:d2:0e:58:49,5a0204,-48
+INQE
+INQS
char temp[100];
Delete請問這是甚麼意思???
我知道你會不停地收到資料,但我已經寫好解析的部份,不是嗎?
Delete你只要把資料丟進String,然後就可以用我寫的程式碼拿出「-46」那部分。
不過,除了+INQ:84:00:d2:0e:58:49,5a0204,-48這種格式的資料,你還會收到其他資料,我寫的程式並沒有判斷這點。
可以請您幫我解釋一下嗎?
Delete把資料丟進String??
怎麼做?
咳咳,所以說請看看C/C++的書籍。
Delete試試看吧
#define SERIAL_BAUDRATE 115200
#define MAX 100
#define EXTRACT_ERROR -32768
int extract(const char *data)
{
char temp[MAX];
Serial.print("Original data is ");
Serial.println(data);
String s = String(data);
s.toCharArray(temp, MAX);
Serial.print("data in String object is ");
Serial.println(temp);
if(s.startsWith("+INQ:")){
int idx = s.lastIndexOf(',');
Serial.print("index of , is ");
Serial.println(idx);
String s2 = s.substring(idx+1);
s2.toCharArray(temp, MAX);
Serial.print("the piece of data we want is ");
Serial.println(temp);
int rst = s2.toInt();
Serial.print("finally we got ");
Serial.println(rst);
return rst;
}
return EXTRACT_ERROR;
}
void setup()
{
Serial.begin(SERIAL_BAUDRATE);
}
void loop()
{
char data0[] = "+INQ:84:00:d2:0e:58:49,5a0204,-47";
char data1[] = "+INQ:1234:56:0,1F1F,-53";
char data2[] = "+INQ:84:00:d2:0e:58:49,5a0204,-48";
char data3[] = "+INQE";
char data4[] = "+INQS";
int rst[5];
rst[0] = extract(data0);
rst[1] = extract(data1);
rst[2] = extract(data2);
rst[3] = extract(data3);
rst[4] = extract(data4);
for(int i = 0; i < 5; i++){
if(rst[i] != EXTRACT_ERROR){
Serial.print("rst of data");
Serial.print(i);
Serial.print(" is ");
Serial.println(rst[i]);
}
else{
Serial.print("rst of data");
Serial.print(i);
Serial.println(" is not valid");
}
}
delay(3000);
}
我也貼在網路上了
http://pastebin.com/ksnSZa94
This comment has been removed by the author.
ReplyDelete我找不到你這個藍牙模組的datasheet。
Delete不知道它是怎麼運作。
請問您來自中國大陸嗎?姓名只有兩個字。
Delete中秋佳節耶,怎麼在這裡打發時間啊,呵呵:)。
偶素歹灣郎
Delete宅男一枚
啊,這樣啊,抱歉弄錯了。
Deletechar temp[100];
ReplyDelete宣告陣列,內含元素的型別為char,共有100個。
如果您看不懂這些程式碼的話,可能需要找本C語言的書看一看。
另外,Arduino的程式碼會用到一點點的C++,String就是個C++物件。
yehnan哥,您好。
Delete最近有一項棘手的任務想請教,需求功能如下:
DIO總共14個,扣掉前面2個(pin0,1)為傳輸用,
將其餘12個分為前6個(pin2~7)為DI,後6個(pin8~13)為DO。
但我要能從PC端下command控制,command格式為:
"W","XX","Y" → "XX"為"0~5",代表pin8~13;"Y"為"0"或"1",代表OFF/ON ("W"大小寫皆可)
"R","ZZ" → "ZZ"為"0~5",代表pin2~7 ("r"大小寫皆可)
例如:
輸入"W,2,1",pin10為ON,回傳"OK." → 此為DO command.
輸入"R,3",回傳pin5的狀態(0或1) → 此為DI command.
我目前用Switch..case..架構,但遇到瓶頸試不出來,
故請問yehnan哥了。
我的email:pfrhsd@gmail.com
謝謝!
嗯,就是經由序列埠、讀寫Arduino的腳位狀態。
Delete什麼瓶頸?
你只有描述需求,並沒有說明問題。
This comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDelete前輩
ReplyDelete為什麼您藍芽的鮑率都要設定在115200,和9600有甚麼不同嗎?
在網路上的範例程式都是以9600居多。
我程式碼裡的115200是Arduino Uno與電腦之間的序列傳輸速率。
Delete很多模組預設為9600,你這個藍牙模組也是。
YA~~
ReplyDelete我剛剛找到了這個藍芽的說明文件
http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Wireless/Bluetooth/CSR-BC417-datasheet.pdf
恭喜,慢慢看吧。這不是我本行。
Delete請問前輩
ReplyDelete下面這個
+INQ:84:00:d2:0e:58:49,5a0204,-48
最後一個 - 的索引值是多少?
該怎麼算?
如果這是ASCII,那麼一個字元應該佔一個byte,會在char陣列裡佔一個位置,索引值從0開始算。+的索引值是0,I的索引值是1,依此類推。
Delete前輩
ReplyDelete如果我要用藍芽的訊號強度來決定第9腳PWM的電壓高低該怎麼做呢??
請前輩指示
拿到藍芽訊號強度後,並且查出藍芽訊號強度的最大與最小值,
ReplyDelete然後使用map函式,
http://arduino.cc/en/Reference/Map
將強度轉成0~255之間的值,因為Arduino的類比輸出為8位元,
再用analogWrite將值輸出到第9腳,
http://arduino.cc/en/Reference/AnalogWrite
This comment has been removed by the author.
Delete不行。還是請你自己想吧。orz
Delete先試著玩玩analogWrite控制LED的亮度,
然後丟幾個值到map裡去,用看看,
查詢藍牙模組的文件,查出強度的最大與最小值,
然後再寫程式吧。
其實我是不知道該怎麼把藍芽的訊號強度抓出來,並利用它的強弱來控制東西。
ReplyDelete懇請前輩賜教
我沒別人可以問了
強度?之前不是已經寫好程式抓出來了嗎?
Delete如何利用強弱控制東西,就是我前幾則說的map與analogWrite。
建議您看看Arduino入門書吧,譬如
http://yehnan.blogspot.tw/2013/04/arduinoarduino.html
那本書我看過了,我不是完全不懂啦!!
ReplyDelete是有一些關鍵的問題導致程式沒辦法組合起來,
前輩上一個寫給我的程式裡,強度是存在哪個變數?
有點看不懂
把類似+INQ:84:00:d2:0e:58:49,5a0204,-48的字串傳給extract函式,就會回傳強度,是個int。
ReplyDeleteextract = map(extract, 0, 1023, 0, 179);
ReplyDelete這樣對嗎?
不對。
Delete你為什麼不編譯看看、執行看看呢?
直接告訴我嘛
ReplyDelete這對您來說又沒甚麼
我已經弄了快兩個月了
不想這麼做。
ReplyDelete加油。
怎麼這樣....
ReplyDelete好人做到底,不要只幫一半嘛~
我認為我只幫到5%而已,遠遠不足一半。
ReplyDelete關於以藍芽訊號強度控制第9腳PWM的電壓高低,我也已經說明該做的事情了。
從+INQ:84:00:d2:0e:58:49,5a0204,-48擷取出藍芽訊號強度後,儲存成int變數,並且查出藍芽訊號強度的最大與最小值,然後使用map函式http://arduino.cc/en/Reference/Map
將強度轉成0~255之間的值(因為Arduino的類比輸出為8位元),
再用analogWrite將該值輸出到第9腳,
http://arduino.cc/en/Reference/AnalogWrite
加油。
從+INQ:84:00:d2:0e:58:49,5a0204,-48擷取出藍芽訊號強度後,儲存成int變數,並且查出藍芽訊號強度的最大與最小值,然後使用map函式http://arduino.cc/en/Reference/Map
ReplyDelete將強度轉成0~255之間的值(因為Arduino的類比輸出為8位元),
這些簡單的我早會了......
我是不知道您的程式是如何運作的而已,
我本來是想說請您用控制PWM的方式來講解,
版主可能誤會我的意思了,我不是想拿現成的程式來用。
...用analogWrite將值輸出到第9腳...
ReplyDeleteanalogWrite就是控制PWM的函式。
http://arduino.cc/en/Reference/AnalogWrite
這我會啦
Delete這麼簡單
Great, problem solved.
Delete還沒解決啦!
ReplyDelete我看不懂您的程式,可以請您解釋一下這個程式的輸出和輸入是哪一個嗎?
Ps.這個程式是您寫的嗎?還是網路上抓的?如果是網路上抓的,可以告訴我網址嗎?
extract函式是我寫的,
ReplyDelete參數是指向字串"+INQ:84:00:d2:0e:58:49,5a0204,-47"的指標,
回傳int(訊號強度,也就是-47的部份)。
譬如
char data[] = "+INQ:84:00:d2:0e:58:49,5a0204,-47";
int rst = extract(data);
那麼rst就會存有-47。
ok
Delete明白了
我要問的就是這個
感謝前輩
請問前輩
Deletechar data[] = "+INQ:84:00:d2:0e:58:49,5a0204,-47";
沒辦法接收我的訊號,我要怎麼把訊號輸入進去??
因為訊號的值是會變動的,char data[] = 該怎麼接收會變動的資料呢?
Delete把你拿到的訊號,應該是個字串吧,譬如"+INQ:84:00:d2:0e:58:49,5a0204,-47",
Delete儲存成char陣列,或是用char指標指向該字串,
然後就可以呼叫extract。
如果拿到字串時已經是個char *,那就可以丟進extract了。
如果不是,那是什麼呢?一次收到一個字元嗎?那就將字元一個個放進char陣列裡,收集完全後再呼叫extract。
版主您寫的程式只是一直在抓char data[] = "+INQ:84:00:d2:0e:58:49,5a0204,-47";
Delete裡面的-47,並沒有接收到藍芽的訊號再貼出,是不是搞錯了??
我並不知道你抓到的藍牙訊號是什麼,你必須自己將訊號弄成字串放進char data[]裡。
Delete儲存訊號的變數沒辦法放進char data[]裡??
Delete啥?
Delete你抓到的藍牙訊號是什麼東西?是個ASCII字串吧?儲存在哪裡?
您是不是不太懂我的問題?
ReplyDelete沒錯。
ReplyDelete您也聽不懂我的回答,呵呵。
原來如此.....難怪那個程式..........
ReplyDelete我去問別人好了,這幾天叨擾了.........
void setup()
ReplyDelete{
Serial.begin(115200);
char data[] = "+INQ:1234:56:0,1F1F,-53";
char temp[100];
Serial.print("Original data is ");
Serial.println(data);
String s = String(data);
s.toCharArray(temp, 100);
Serial.print("data in String object is ");
Serial.println(temp);
int idx = s.lastIndexOf(',');
Serial.print("index of , is ");
Serial.println(idx);f
String s2 = s.substring(idx+1);
s2.toCharArray(temp, 100);
Serial.print("the piece of data we want is ");
Serial.println(temp);
int rst = s2.toInt();
Serial.print("finally we got ");
Serial.println(rst);
}
void loop()
{
}
char data[]寫在setup..........
後面還寫 = "+INQ:1234:56:0,1F1F,-53";
原來您真的不會.......
浪費我好多天............
您又沒有告訴我藍牙訊號從何而來,
Delete也只告訴我得到的是"+INQ:1234:56:0,1F1F,-53",
我當然也只能在程式碼裡寫死,
我給的是個extract函式,傳入符合"+INQ:1234:56:0,1F1F,-53"格式的訊號,便可抽取出最後代表訊號強度的數字(-53),
這不就是你最初的問題嗎?
> char data[]寫在setup..........
Delete我也只能寫死在程式裡,
您最初的問題是「要怎麼單只把訊號強度值存入Arduino的變數裡?」,
> +INQ:1234:56:0,1F1F,-53
> 後面的53是訊號強度,但它一次都要顯示這一整行,
> 我只想把後面的53存入變數來計算。
我寫了個extract函式,的確把你給的"+INQ:1234:56:0,1F1F,-53"分析後抓出最後那個數字並存成變數了。
嗯,不管如何。請問問其他人吧,說不定一下子就解決了。
DeleteWish you luck.
訊號當然是會一直變的..............這是基本...........
DeleteThis comment has been removed by the author.
Delete呃,我知道訊號會變,所以?
Delete之前說的「
我知道你會不停地收到資料,但我已經寫好解析的部份,不是嗎?
你只要把資料丟進String,然後就可以用我寫的程式碼拿出「-46」那部分。
」
我寫了解析訊號字串取得強度的部份,至於其他部分,還請自己動手。
既然會從串列埠接收資料,那就在每當接收到完整的一份資料後,進行解析,判斷並取出強度的部份。
啊,你的確說過會從串列埠不停地取得資料。
DeleteThis comment has been removed by the author.
Delete關於你手上的問題,我重新讀過一遍所有的留言後,根據知道的東西,可行方案應如下:
ReplyDelete想要達成的目標是不斷讀取藍牙訊號的強度,隨之調整LED亮度。
會從序列埠收到類似+INQ:84:00:d2:0e:58:49,5a0204,-47的字串,我假設這是ASCII字串,不僅有此格式的字串,也會有其他格式的字串,譬如+INQE與+INQS。
不過我不確定藍牙模組是否會自動不斷送出含有強度的字串,還是要下達指令後才會回傳。
在Arduino程式碼裡的loop(),撰寫程式從序列埠接收字串,應該要根據某種條件判斷該字串是否完整,完整收到字串後,應該存在char陣列裡。
然後便可丟給抽取強度部分的函式,之中要作判斷,判斷該字串是不是含有強度的那種字串,然後取出強度,是個int值。
有了強度值後,必須根據強度的最大最小值,利用map函式轉成介於0~255(8位元)的值,再經由analogWrite輸出到LED的腳位,控制LED亮度。
我沒用過這個藍牙模組,沒有讀過(也不想)研讀該藍牙模組的datasheet,僅根據留言裡的資訊,拼湊出上述或許可行的作法。
最後希望您能早日解決問題。
或許可到Arduino的論壇詢問,例如http://www.robofun.net/forum/index.php,或者可向原賣家詢問有無範例程式碼。
This comment has been removed by the author.
ReplyDelete= =...看過上面對話感覺很無言...還是別造口業好了。
ReplyDelete葉桑也太熱心了點,請他把你的blog讀完,或順便推一下自己寫的書就好啦...哈哈。
在下最近才剛接觸MCU且只會寫一點VB,上個月買零件從PIC開始玩起(便宜的12F677)但處處碰壁(因為資源較少),後來在您的blog上發現全open的Arduino便跳坑買了片Mega2560,正在逐步建立起各sensor的code準備用在自家的開心農場。
我沒玩過PIC。
Delete如果到書店看看微控制器的中文書的話,8051書最多,PIC與Arduino的書非常非常少。但若是英文書的話,都已經相當豐富。另外,聽說PIC在大陸也滿紅的。
Arduino的程式是C語言、加上極少的C++。
Good luck, happy playing Arduino.
藍芽可以讀字串,然後用switch去動作嗎? 因為有時傳的資料顯示出並不是完整一串,是有切割過的,但是如果把她串接出來的話,要如何用switch或字串比對(strcmp)?? 然而資料會保留不會被覆蓋掉
ReplyDelete> 用switch去動作嗎?
Delete啥?
> 並不是完整一串,是有切割過的
因為接收時並不一定會收到你想像中的「完整」一串。
須自己判斷,串連起來,找出你所謂的完整的資料。
用strcmp比較字串。
我想要放到陣列裡的字元比較完後,使led亮或暗,接著把陣列清空後,再繼續讀取新的資訊放到陣列內,再使led暗或亮,但是不知怎清掉並讀取新資訊,下面是我寫的程式碼
Deleteint LED = 13;
int i = 0;
char ary[128] = {0};
void setup() {
pinMode(LED, OUTPUT);
Serial.begin(9600); // Arduino起始鮑率:9600
Serial1.begin(9600); // HC-06 出廠的鮑率:每個藍牙晶片的鮑率都不太一樣,請務必確認
digitalWrite(LED, LOW);
}
char key[] = "led0";
char key1[] = "led1";
void loop() {
while (Serial1.available() > 0) {
char c = Serial1.read();
ary[i] = c;
i++;
}
if (strcmp(ary, key) == 0) {
digitalWrite(LED, LOW);
}
if (strcmp(ary, key1) == 0) {
digitalWrite(LED, HIGH);
}
}
大概在loop()裡加上i = 0;
Delete或許就可以了。
或許。