2015年5月31日 星期日

C#控制Arduino LED、Arduino傳送超音波測距數值至C#

本篇文章為C#傳送字元至Arduino、Arduino回傳字串至C#續文
修改串列埠連線設定方式,取消連線按鈕,改為直接由下拉式選單選擇後連線
新增Arduino回傳超音波測距數值至C#的功能

C#
程式碼
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;


namespace _0150531
{
    public partial class Form1 : Form
    {
        string dis, text2;
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            serialPort1.BaudRate = 9600;
            serialPort1.Parity = Parity.None;
            serialPort1.DataBits = 8;
            serialPort1.StopBits = StopBits.One;
            button1.Enabled = false;
            button2.Enabled = false;
            button3.Enabled = false;
            comboBox1.Items.AddRange(SerialPort.GetPortNames());
            label1.Text = "PC狀態:尚未連線";
        }
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            serialPort1.PortName = comboBox1.Text;
            serialPort1.Open();
            comboBox1.Enabled = false;
            button1.Enabled = true;
            button2.Enabled = true;
            button3.Enabled = true;
            timer1.Enabled = true;
            label1.Text = "PC狀態:連線中";
        }
        private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.Write("0");           
            comboBox1.Enabled =true;
            button1.Enabled = false;
            button2.Enabled = false;
            button3.Enabled = false;
            timer1.Enabled = false;
            label1.Text = "PC狀態:斷線中";
            label2.Text = "Arduino回傳:";
            serialPort1.Close();
        }
        private void button2_Click(object sender, EventArgs e)
        {
            serialPort1.Write("1");
            button3.Enabled = true;
            button2.Enabled = false;
            label1.Text = "送出指令:LED ON";
        }
        private void button3_Click(object sender, EventArgs e)
        {
            serialPort1.Write("0");
            button2.Enabled = true;
            button3.Enabled = false;
            label1.Text = "送出指令:LED OFF";
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            label2.Text = text2;
        }
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            dis = serialPort1.ReadLine();
            if (dis.IndexOf('C') == 0)
                text2 = "Arduino回傳:" + dis;
            else
                text2 = "wrong";
        }
    }
}

Arduino
這裡不用 Arduino 內建的 Ping 範例,而是使用 HC-SR04 Ultrasonic Sensor Library
這個 library把測距的公式都包成了函式庫,較方便使用
使用時必須先將下載的壓縮檔解壓縮
將 HCSR04Ultrasonic整個檔案夾複製到Arduino的libraries

#include <Ultrasonic.h>
#define TRIGGER_PIN  11
#define ECHO_PIN     12
Ultrasonic ultrasonic(TRIGGER_PIN, ECHO_PIN);
String dis;
void setup() {
   Serial.begin(9600);     // 開啟 Serial port, 通訊速率為 9600 bps
   // 初始化 LED 接腳
   pinMode(13, OUTPUT);
}      
void loop() {
   // 檢查是否有資料可供讀取
   if (Serial.available() > 0) {
     char inByte = Serial.read();
     switch (inByte) {
     case '0':   
       digitalWrite(13, LOW);
       break;
     case '1':
       digitalWrite(13, HIGH);
       break;
     default:
       digitalWrite(13, LOW);
     }    
   }
   else{
     distance();
   }  
}
void distance()
{
  float cmMsec;
  long microsec = ultrasonic.timing();
  cmMsec = ultrasonic.convert(microsec, Ultrasonic::CM); // 計算距離,單位: 公分
  dis=String(cmMsec);
  Serial.println("CM:"+dis);
  delay(50);
}

結果

上述的作法有一些問題存在
C#的計時器用來每隔一段固定時間顯示label2.Text
即表單上變更顯示Arduino回傳字串的時間間隔是由C#的計時器來決定
並非是串列埠收到data即變更顯示label2.Text
另外 當按下斷線按鈕時 很可能 serialPort1_DataReceived還在執行
(??不確定 但的確出現當掉的狀況)

因此變更作法
不再使用計時器固定時間變更顯示label2.Text
直接當串列埠收到資料時 即變更顯示label2.Text
但是 在serialPort1_DataReceived中採用透過 delegate 及 Invoke 來執行 addData()
這是因為 Windows Forms 是 Multi-threading 的機制
如果不這麼寫,直接在 serialPort1_DataReceived 把資料給label2
會遇到「跨執行緒作業無效」的錯誤
另外將計時器的角色做修改
利用 test2作為判斷 當test2=1時 addData()可執行
test2=0 則否
當按下斷線按鈕時 令test2=0 且啟動計時器
此時  addData()便不再執行
直到計時器時間到 再執行斷線serialPort1.Close();

結果
(1)當C#收到data即變更顯示
(2)解決按下斷線按鈕時出現當掉的狀況

修正後程式碼
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;


namespace _0150531
{
    public partial class Form1 : Form
    {
        int test = 1;
        string dis, text2;
        public delegate void AddDataDelegate();
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            serialPort1.BaudRate = 9600;
            serialPort1.Parity = Parity.None;
            serialPort1.DataBits = 8;
            serialPort1.StopBits = StopBits.One;
            button1.Enabled = false;
            button2.Enabled = false;
            button3.Enabled = false;
            comboBox1.Items.AddRange(SerialPort.GetPortNames());
            label1.Text = "PC狀態:尚未連線";
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            test = 1;
            serialPort1.PortName = comboBox1.Text;
            serialPort1.Open();
            comboBox1.Enabled = false;
            button1.Enabled = true;
            button2.Enabled = true;
            button3.Enabled = true;
            label1.Text = "PC狀態:連線中";
        }

        private void button1_Click(object sender, EventArgs e)
        {
                serialPort1.Write("0");
                comboBox1.Enabled = true;
                button1.Enabled = false;
                button2.Enabled = false;
                button3.Enabled = false;
                timer1.Enabled = true;
                test = 0;               
        }

        private void button2_Click(object sender, EventArgs e)
        {
            serialPort1.Write("1");
            button3.Enabled = true;
            button2.Enabled = false;
            label1.Text = "送出指令:LED ON";
        }

        private void button3_Click(object sender, EventArgs e)
        {
            serialPort1.Write("0");
            button2.Enabled = true;
            button3.Enabled = false;
            label1.Text = "送出指令:LED OFF";
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            serialPort1.Close();
            timer1.Enabled = false;
            label1.Text = "PC狀態:斷線中";
            label2.Text = "Arduino回傳:";
        }

        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (test==1)
                this.Invoke(new AddDataDelegate(addData));           
        }
        private void addData()
        {
            dis = serialPort1.ReadLine();
            if (dis.IndexOf('C') == 0)
                text2 = "Arduino回傳:" + dis;
            else
                text2 = "wrong";
            label2.Text = text2;
        }
    }
}

Arduino另一個程式
 #define trigPin  11
 #define echoPin  12

 float echotime, distance;
float obs_dis()
{
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(15);
  digitalWrite(trigPin, LOW);
  echotime=pulseIn(echoPin, HIGH);
  distance=echotime/2/29.1; 
}


 String dis;
 void setup() {
    Serial.begin(9600);     // 開啟 Serial port, 通訊速率為 9600 bps
    // 初始化 LED 接腳
   pinMode(trigPin, OUTPUT);
    pinMode(echoPin, INPUT);
   pinMode(13, OUTPUT);
 }      
 void loop() {
    // 檢查是否有資料可供讀取
   if (Serial.available() > 0) {
      char inByte = Serial.read();
      switch (inByte) {
      case '0':   
        digitalWrite(13, LOW);
        break;
      case '1':
        digitalWrite(13, HIGH);
        break;
      default:
        digitalWrite(13, LOW);
      }    
    }
    else{
      obs_dis();
      dis=String(distance);
   Serial.println("CM:"+dis);
   delay(50);
    }  
 }

蛙式運動車機構模擬(2006?)

2006(或2005?)的學生專題作品
利用SolidWorks畫出元件並組合
再以visual Nastran Desktop進行機構模擬
作的蠻好的,不過沒有完全成功
下坡模擬還OK,平地和上坡就做模擬不出來
visual Nastran Desktop作機構模擬只能假設成剛體碰撞
實際上輪子和地面接觸不會是剛體接觸
實際地面給輪子的作用力可能是一個重要模擬因素
其實我們也可做一些人工的假設條件,讓平地和上坡模擬可以成功
不過加入這種難以確認是否有意義的人工條件
就算讓模擬成功,也只是騙騙外行人
所以,最後就只做下坡模擬

機構爆炸圖
模擬結果

2015年5月30日 星期六

環保回收物分類機器人(2009)

2009學生作品

第十三屆TDK機器人創思與設計競賽(自動組)
競賽成績:三勝二敗

指導這組學生參加TDK競賽時,老實說,我還不懂機電整合和機器人
只懂一些繼電器、計時器基礎配線,以及氣壓迴路
感測器、單晶片可說完全不懂
學生能完成作品,完全是靠同事黃老師
我只負責教他們一些很基礎的東西
例如配線和C語言
另外就是出點子、加油打氣和籌經費、找資源

這屆參賽的成績是三勝二敗,可以說是前十六強吧
很可惜沒進決賽(前八強)
預賽前三場,很完美的滿分取勝
第四場一開始也很順利,只要贏了,就進決賽了
當時還在想,要開始傷腦筋晚上住宿的問題了
沒想到,瞬息萬變,最後一關機器人好像出問題了
而對手雖然一開始不順,最後卻順利跑完全程
這場就這樣敗了
在等待敗部賽時(第五場),檢查了一下機器人
似乎是感測發生問題,但時間太短,找不出哪個線路故障
所以第五場不意外地:敗

指導完這組學生,有很大收穫
以單晶片作為控制器的機電整合、自動控制系統的實作
要如何進行大致懂個八成
也因為這一次帶學生的經驗
讓我對機器人更加地投入
另外,系統的可靠性問題是個大學問ㄚ
這一點,到現在還是沒機會學習












可自動下降階梯之障礙物掃除機器人(2012)

指導學生參賽作品
2012第六屆全國大專學生創新設計實作競賽第三名

這個作品所有的機構與電控模組幾乎都是自己動手做的
包括循軌感測模組、紅外線障礙物感測模組、馬達驅動模組、 控制器模組
都是自行設計電路、佈線、洗電路板、焊接、測試到組合
由一顆顆感測元件、IC、電阻等電子元件,以及馬達,組出所有電控系統
機構部分也是,由一根根鋁擠型、螺絲等,最後能完成作品,真的還蠻感動的
只可惜太慢完成,參加2011TDK競賽前來不及做測試
還好在2012第六屆全國大專學生創新設計實作競賽,獲得第三名
也讓學生們重拾信心
早期的學生還真是刻苦耐勞ㄚ

   



C#傳送字元至Arduino、Arduino回傳字串至C#

本篇文章為C#傳送字元至Arduino續文
修改COM port設定方式,改為由下拉式選單選擇
新增Arduino回傳字串至C#的功能

C#
使用
comboBox1.Items.AddRange(SerialPort.GetPortNames());
將電腦中的串列埠加入comboBox選單
使用
serialPort1.PortName = comboBox1.Text;
設定 serialPort1.PortName為comboBox的文字
使用
serialPort1.ReadLine();
讀取Arduino回傳字串

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;

namespace _0150529
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            serialPort1.BaudRate = 9600;
            serialPort1.Parity = Parity.None;
            serialPort1.DataBits = 8;
            serialPort1.StopBits = StopBits.One;
            button2.Enabled = false;
            button3.Enabled = false;
            button4.Enabled = false;
            comboBox1.Items.AddRange(SerialPort.GetPortNames());
            label1.Text = "PC狀態:尚未連線";
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            serialPort1.PortName = comboBox1.Text;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.Open();
            button1.Enabled = false;
            button2.Enabled = true;
            button3.Enabled = true;
            button4.Enabled = true;
            label1.Text = "PC狀態:連線中";
        }

        private void button3_Click(object sender, EventArgs e)
        {
            serialPort1.Write("1");
            button4.Enabled = true;
            button3.Enabled = false;
            label1.Text = "送出指令:LED ON";
            label2.Text= "Arduino回傳:"+ serialPort1.ReadLine();
        }

        private void button4_Click(object sender, EventArgs e)
        {
            serialPort1.Write("0");
            button3.Enabled = true;
            button4.Enabled = false;
            label1.Text = "送出指令:LED OFF";
            label2.Text = "Arduino回傳:" + serialPort1.ReadLine();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            serialPort1.Write("0");
            serialPort1.Close();
            button1.Enabled = true;
            button2.Enabled = false;
            button3.Enabled = false;
            button4.Enabled = false;
            label1.Text = "PC狀態:斷線中";
            label2.Text = "Arduino回傳:";
        }
    }
}

Arduino程式碼
使用Serial.println傳送字串
此處配合C#端使用serialPort1.ReadLine()

void setup() {
   Serial.begin(9600);     // 開啟 Serial port, 通訊速率為 9600 bps
   // 初始化 LED 接腳
   pinMode(13, OUTPUT);
}       
void loop() {
   // 檢查是否有資料可供讀取
   if (Serial.available() > 0) {
     // 讀取進來的 byte
     char inByte = Serial.read();
     // 根據收到的字元決定要打開或關掉 LED
     switch (inByte) {
     case '0':    
       digitalWrite(13, LOW);
       delay(1);
       Serial.println("LED OFF");
       break;
     case '1': 
       digitalWrite(13, HIGH);
       delay(1);
       Serial.println("LED ON");
       break;
     default:
       // 關掉所有的 LED
       digitalWrite(13, LOW);
     }
   } 
}

執行結果



2015年5月28日 星期四

C#傳送滑軌數值至Arduino

功能:C#視窗程式傳送字串至Arduino,該字串由四個整數所組合
說明:
(1)使用serialPort,
(2)使用二個GroupBox,分別各含三個RadioButton,依序代表"1"、"0"、"2",用以傳送至Arduino控制pin13 LED、pin 9LED
(3)使用二個trackBar,其對應數值傳送至Arduino控制pin13 LED閃爍速度、pin 9LED的亮度
(4)將欲傳送之字串與數值組成字串,並以逗點分開。例如13thLED選擇Blink、9thLED選擇ON(調光),二個trackBar的數值分別為100、23,組成字串"2,2,100,23,"
(5)Arduino接收字串後,將字串分開成"2"、"2"、"100"和"23"四個字串,再分別轉成整數
(6)採用計時器,當RadioBox被點選或trackBar被捲動,啟動計時器,計時時間到傳送字串,並關閉計時器。計時器之功能在此主要作為delay使用,避免trackBar被捲動傳送資料太快,Arduino端接收字串太慢,會導致接收到2組甚至3組字串

C#表單設計
程式碼
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;

namespace _0150525
{
    public partial class Form1 : Form
    {
        string LED13 = "0";
        string LED9 = "0";
        int LED13_delay = 100;
        int LED9_bright = 0;
        string send;
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            serialPort1.PortName = "COM18";
            serialPort1.BaudRate = 9600;
            serialPort1.Parity = Parity.None;
            serialPort1.DataBits = 8;
            serialPort1.StopBits = StopBits.One;
            groupBox1.Enabled = false;
            groupBox2.Enabled = false;
            trackBar1.Enabled = false;
            trackBar2.Enabled = false;
            button2.Enabled = false;
        }
        private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.Open();
            groupBox1.Enabled = true;
            groupBox2.Enabled = true;
            trackBar1.Enabled = false;
            trackBar2.Enabled = false;
            button1.Enabled = false;
            button2.Enabled = true;
            timer1.Enabled = true;
        }
        private void radioButton1_Click(object sender, EventArgs e)
        {
            LED13 = "1";
            trackBar1.Enabled = false;
            timer1.Enabled = true;
        }
        private void radioButton2_Click(object sender, EventArgs e)
        {
            LED13 = "0";
            trackBar1.Enabled = false;
            timer1.Enabled = true;
        }
        private void radioButton3_Click(object sender, EventArgs e)
        {
            LED13 = "2";
            trackBar1.Enabled = true;
            timer1.Enabled = true;
        }
        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            LED13_delay = trackBar1.Value;
            timer1.Enabled = true;
        }
        private void radioButton4_Click(object sender, EventArgs e)
        {
            LED9 = "1";
            trackBar2.Enabled = false;
            timer1.Enabled = true;
        }
        private void radioButton5_Click(object sender, EventArgs e)
        {
            LED9 = "0";
            trackBar2.Enabled = false;
            timer1.Enabled = true;
        }
        private void radioButton6_Click(object sender, EventArgs e)
        {
            LED9 = "2";
            trackBar2.Enabled = true;
            timer1.Enabled = true;
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            label1.Text = LED13;
            label2.Text = LED9;
            label3.Text = LED13_delay.ToString();
            label4.Text = LED9_bright.ToString();
            send = label1.Text + "," + label2.Text + "," + label3.Text + "," + label4.Text + ",";
            label5.Text = "傳送字串: " + send;
            serialPort1.Write(send);
            timer1.Enabled = false;
        }
        private void trackBar2_Scroll(object sender, EventArgs e)
        {
            LED9_bright = trackBar2.Value;
            timer1.Enabled = true;
        }
        private void button2_Click(object sender, EventArgs e)
        {
            send = "0,0,100,0";
            serialPort1.Write(send);
            serialPort1.Close();
            groupBox1.Enabled = false;
            groupBox2.Enabled = false;
            trackBar1.Enabled = false;
            trackBar2.Enabled = false;
            button2.Enabled = false;
            button1.Enabled = true;
        }
    }
}


Arduino程式碼
int inSlider1=100;
int inSlider2=0;
int Position1,Position2,Position3,Position4;
int R1,R2;
String myString,tempString;
void setup() {
   Serial.begin(9600); //Arduino起始鮑率:9600
   Serial.setTimeout(10);
   pinMode(13,OUTPUT);
   pinMode(9,OUTPUT);
}

void loop() {
   // 檢查是否有資料可供讀取
   Serial.flush();
   while (Serial.available()) {
     myString=Serial.readString();
     split();
     if (R1 !=2)
       {
        digitalWrite(13,R1);
       }
      else
       {
        digitalWrite(13,1);
        delay(inSlider1);
        digitalWrite(13,0);
        delay(inSlider1);
       }
     if (R2 !=2)
       {
        digitalWrite(9,R2);
       }
     else 
       {
         analogWrite(9,inSlider2);
       }
   }
}
void split(){
     Position1=myString.indexOf(',');
     Position2=myString.indexOf(',',Position1+1);
     Position3=myString.indexOf(',',Position2+1);
     Position4=myString.indexOf(',',Position3+1); 
     tempString=myString.substring(0,Position1);
     R1=tempString.toInt();
     tempString=myString.substring(Position1+1,Position2);
     R2=tempString.toInt();
     tempString=myString.substring(Position2+1,Position3);
     inSlider1=tempString.toInt();
     tempString=myString.substring(Position3+1,Position4);
     inSlider2=tempString.toInt();
}
說明
(1)為了避免暫存器中有多組資料,使用Serial.flush();清空暫存器
(2)參考藍芽傳送字串,此處仍然使用readString()來讀取字串,存放在myString,其資料型態是String
(3)split()用來分離字串,在split()中,採用indexOf()來找出逗點的位置,indexOf()是Arduino中String class的函數 
(4)Position1=myString.indexOf(',');找出第一個逗點的位置並存放在Position1。(註:位置編號是由0開始,即第一個字元是0,第二個字元是1.....)
Position2=myString.indexOf(',',Position1+1);找出第二個逗點的位置並存放在Position2,依此類推
(4)tempString=myString.substring(0,Position1);利用substring()分離出第一個逗點前的字串,並暫時存放tempString,此資料型態為String
(5)利用toInt()將字串轉為整數,toInt()是Arduino中String class的函數

結果
  
問題:
(1)13thLED的閃爍不固定
(2)當13thLED閃爍時,控制有lag現象

原因:
(1)使用while (Serial.available()),當尚未有新資料進來時,LED13不會改變,錯誤寫法
(2)Arduino端採用delay形成LED閃爍,當delay()在計時程式停在此處,無法多工

修改如下:
(1)將while改成if,當尚未有新資料進來時,LED13可持續狀態或閃爍
(2)採用millis()擷取時間,利用時間來判斷LED13在閃爍時,應亮或暗

程式碼
int inSlider1=100;
int inSlider2=0;
int Position1,Position2,Position3,Position4;
int R1,R2, R1_last;
int LED13_prev=0;
long time_cur,time_prev;
String myString,tempString;
void setup() {
   Serial.begin(9600); //Arduino起始鮑率:9600
   Serial.setTimeout(10);
   pinMode(13,OUTPUT);
   pinMode(9,OUTPUT);
}

void loop() {
   // 檢查是否有資料可供讀取
   //Serial.flush();
   if (Serial.available()) {
     myString=Serial.readString();
     split();
     if (R1 !=2)
       digitalWrite(13,R1);
     else
       {
        if (R1_last != R1)
          {
            digitalWrite(13,LED13_prev);
            time_prev=millis();
          }
        else
          blink_13();                
       }
     R1_last=R1; 
     if (R2 !=2)
       digitalWrite(9,R2);
     else 
       analogWrite(9,inSlider2);
   }
   else
   {
     if (R1 ==2)
       blink_13();
   }
}
void split(){
     Position1=myString.indexOf(',');
     Position2=myString.indexOf(',',Position1+1);
     Position3=myString.indexOf(',',Position2+1);
     Position4=myString.indexOf(',',Position3+1); 
     tempString=myString.substring(0,Position1);
     R1=tempString.toInt();
     tempString=myString.substring(Position1+1,Position2);
     R2=tempString.toInt();
     tempString=myString.substring(Position2+1,Position3);
     inSlider1=tempString.toInt();
     tempString=myString.substring(Position3+1,Position4);
     inSlider2=tempString.toInt();
}
void blink_13(){
     time_cur=millis();
     if (time_cur - time_prev > inSlider1)
       {
         if (LED13_prev == 0)
           LED13_prev=1;
         else
           LED13_prev=0;
         digitalWrite(13,LED13_prev);
         time_prev=time_cur;
       }
}

結果:
大幅改善,但C#的計時器時間不可設太小,當設太小時(例如5),trackBar2滑動時(即LED9調光),傳送資料太頻繁,會使得LED13閃爍受到干擾,設成50 OK
另外Serial.flush()可不使用











2015年5月24日 星期日

APP Inventor 2 藍芽傳送字元及滑軌數值至Arduino

功能:Android 手機APP傳送字串至Arduino
說明:
(1)使用藍芽,
(2)APP傳送不同按鍵所代表之字元、以及滑軌數值至Arduino
(3)使用SoftwareSerial,藍芽模組接pin10, 11,0,1留給Serial Monitor
(4)將欲傳送之字元與數值組成字串,並以逗點分開。例如傳送'a'和123,組成字串"a,123,"
(5)Arduino接收字串後,將字串分開成"a"和"123"二個字串,再分別轉成字元'a'和整數123

APP Inventor 2





















初始設定、藍芽選單、斷線設定
























宣告全域變數char_b儲存按鍵字元、整數num儲存滑軌數值、send儲存組合後字串

























使用計時器 計時時間設為20ms
當Slider位置改變時啟動計時器
計時時間到再傳送字串
這是將timer作為delay
避免滑軌移動太快,傳送字串太快,Arduino端接收字串太慢,會導致接收到2組甚至3組字串

button4~button12分別對應字元'a'~'i'
當按下按鍵時,同樣傳送組合字串,但不做delay


























上圖為button4對應程式
當button4按下,button4不可用,其餘可用
char_b設為a,組合字串send,再以藍芽Client傳送字串send
其他button5~12依照類似作法

Arduino程式
#include <SoftwareSerial.h>
#include <Wire.h>//引用二個函式庫SoftwareSerial及Wire
SoftwareSerial I2CBT(10,11);//定義PIN10及PIN11分別為RX及TX腳位
//注意此RX, TX分別接藍芽模組的TX, RX
int inSlider=0;
int Position1,Position2;
char temp[]="e";
char button;
String myString,tempString;
void setup() {
   Serial.begin(9600); //Arduino起始鮑率:9600
   I2CBT.begin(57600);
//藍牙鮑率:57600(注意!每個藍牙晶片的鮑率都不太一樣,請務必確認
   I2CBT.setTimeout(5);
   pinMode(9,OUTPUT);
   Serial.println("Hello"); 
}

void loop() {
   // 檢查是否有資料可供讀取
   while (I2CBT.available()) {
     myString=I2CBT.readString();
     split();
     analogWrite(9,inSlider);
     Serial.print(myString);
     Serial.print(" ");
     Serial.print(button);
     Serial.print(" ");
     Serial.println(inSlider);
   }
}
void split(){
     Position1=myString.indexOf(',');
     Position2=myString.indexOf(',',Position1+1); 
     tempString=myString.substring(0,Position1);
     strcpy(temp,tempString.c_str());
     button=temp[0];
     tempString=myString.substring(Position1+1,Position2);
     inSlider=tempString.toInt();
}
此處仍然使用readString()來讀取字串
存放在myString
其資料型態是String

使用split()來分離字串
在split()中,採用indexOf()來找出逗點的位置
indexOf()是Arduino中String class的函數

Position1=myString.indexOf(',');找出第一個逗點的位置並存放在Position1
(註:位置編號是由0開始,即第一個字元是0,第二個字元是1.....) 

Position2=myString.indexOf(',',Position1+1);找出第二個逗點的位置並存放在Position2

tempString=myString.substring(0,Position1);利用substring()分離出第一個逗點前的字串
並暫時存放tempString,此資料型態為String

strcpy(temp,tempString.c_str());利用c_str()將其由String轉為char[],並複製至temp[]
button=temp[0];字元button為temp[0]

tempString=myString.substring(Position1+1,Position2); 
利用substring()分離出第一個逗點和第二個逗點間的字串
並暫時存放tempString,此資料型態為String

inSlider=tempString.toInt();利用toInt()將字串轉為整數
toInt()和c_str()都是Arduino中String class的函數

以Serial Monitor觀察正確性



2015年5月18日 星期一

APP Inventor 2 藍芽傳送Slider數值至Arduino

APP Inventor 2
使用計時器 計時時間設為15ms
當Slider位置改變時啟動計時器
計時時間到再傳送Slider數值
這是將timer作為delay



這邊有趣的是如不用floor取整數
會出現xxx.5的數值??

Arduino

此處仍然使用readString()來讀取字串
存放在myString
其資料型態是String

處理方法(一)
以c_str()將他轉成char[]
將它複製到newString[]
最後以atoi轉成int

#include <SoftwareSerial.h>
#include <Wire.h>//引用二個函式庫SoftwareSerial及Wire
SoftwareSerial I2CBT(10,11);//定義PIN10及PIN11分別為RX及TX腳位
//注意此RX, TX分別接藍芽模組的TX, RX
int inSlider=0;
String myString;
char newString[]="000";
void setup() {
   Serial.begin(9600); //Arduino起始鮑率:9600
   I2CBT.begin(57600);
//藍牙鮑率:57600(注意!每個藍牙晶片的鮑率都不太一樣,請務必確認
   I2CBT.setTimeout(5);
   Serial.println("Hello");  
}
void loop() {
   // 檢查是否有資料可供讀取
   while (I2CBT.available()) {
     myString=I2CBT.readString();
     strcpy(newString,myString.c_str());
     inSlider=atoi(newString);
     if (inSlider<=500){
      Serial.println(inSlider);  
    }
     else{
      Serial.println("wrong");}
    }


方法(二)

直接使用Arduino的toInt()
將String轉整數 

#include <SoftwareSerial.h>
#include <Wire.h>//引用二個函式庫SoftwareSerial及Wire
SoftwareSerial I2CBT(10,11);//定義PIN10及PIN11分別為RX及TX腳位
//注意此RX, TX分別接藍芽模組的TX, RX
int inSlider=0;
String myString;

void setup() {
   Serial.begin(9600); //Arduino起始鮑率:9600
   I2CBT.begin(57600);
//藍牙鮑率:57600(注意!每個藍牙晶片的鮑率都不太一樣,請務必確認
   I2CBT.setTimeout(5);
   Serial.println("Hello");  
}
void loop() {
   // 檢查是否有資料可供讀取
   while (I2CBT.available()) {
     myString=I2CBT.readString();
     inSlider=myString.toInt();
     if (inSlider<=500){
      Serial.println(inSlider);  
    }
     else{
      Serial.println("wrong");}
    }














2015年5月17日 星期日

APP Inventor 2 藍芽傳送字串至 Arduino

功能:Android 手機APP傳送字串至Arduino
說明:
(1)使用藍芽,
(2)APP傳送字串至Arduino
(3)使用SoftwareSerial
 Arduino程式
#include <SoftwareSerial.h>
#include <Wire.h>//引用二個函式庫SoftwareSerial及Wire
SoftwareSerial I2CBT(10,11);//定義PIN10及PIN11分別為RX及TX腳位
//注意此RX, TX分別接藍芽模組的TX, RX
void setup() {
   Serial.begin(9600); //Arduino起始鮑率:9600
   I2CBT.begin(57600);
//藍牙鮑率:57600(注意!每個藍牙晶片的鮑率都不太一樣,請務必確認
   I2CBT.setTimeout(50);
   Serial.println("Hello");
}
void loop() {
   // 檢查是否有資料可供讀取
   while (I2CBT.available()) {
      Serial.println(I2CBT.readString());
    }
}
APP Inventor 2


APP Inventor 2 藍芽操控 Arduino LED

功能:Android 手機APP遙控Arduino LED,亮、滅、閃爍
說明:
(1)使用藍芽,
(2)APP傳送字元a或b,操控Arduino LED亮、滅,
(3)APP使用滑軌,改變計時器時間,計時時間到,偶數次傳送a,奇數次傳送b,藉以操控Arduino LED閃爍。
(4)藍芽模組設定參考Maa的部落格。

Arduino程式
方法一:(藍芽模組接pin0, 1)
void setup() {
   Serial.begin(57600);
   pinMode(13, OUTPUT);  //設定 pin13 為輸出,LED就接在這
}
void loop() {
   // 檢查是否有資料可供讀取
   if (Serial.available() > 0) {
     // 讀取進來的 byte
     int inByte = Serial.read();
     // 根據收到的字元決定要打開或關掉 LED
     switch (inByte) {
     case 'b':    
       digitalWrite(13, LOW);
       break;
     case 'a': 
       digitalWrite(13, HIGH);
       break;
     default:
       // 關掉所有的 LED
       digitalWrite(13, LOW);
     }
   } 
}
方法二:(藍芽模組接pin10, 11,0,1留給Serial Monitor)
Arduino程式
#include <SoftwareSerial.h>
#include <Wire.h>//引用二個函式庫SoftwareSerial及Wire
SoftwareSerial I2CBT(10,11);//定義PIN10及PIN11分別為RX及TX腳位
                                                   //注意此RX, TX分別接藍芽模組的TX, RX
void setup() {
   Serial.begin(9600); //Arduino起始鮑率:9600
   I2CBT.begin(57600);
//藍牙鮑率:57600(注意!每個藍牙晶片的鮑率都不太一樣,請務必確認
   pinMode(13, OUTPUT);  //設定 pin13 為輸出,LED就接在這
}
void loop() {
   // 檢查是否有資料可供讀取
   if (I2CBT.available() > 0) {
     // 讀取進來的 byte
     int inByte = I2CBT.read();
     // 根據收到的字元決定要打開或關掉 LED
     switch (inByte) {
     case 'b':    
       digitalWrite(13, LOW);
       Serial.println("LED OFF");
       break;
     case 'a': 
       digitalWrite(13, HIGH);
       Serial.println("LED ON");
       break;
     default:
       // 關掉所有的 LED
       digitalWrite(13, LOW);
     }
   } 
}

APP Inventor 2
Button3 斷線
Button4 LED 亮
Button5 LED 滅
Button6 LED 閃爍

初始設定
藍芽選單

  
斷線
LED亮

LED滅

LED閃爍

滑軌改變時改變計時器計時時間

計時器計時時間到傳送字元a或b

Arduino接收字串

方法一:
void setup(){
    Serial.begin(9600);
    Serial.println("Hello");
}

void loop(){

    String s = "";
    while (Serial.available()) {
        char c = Serial.read();
        if(c!='\n'){
            s += c;
        }
        delay(5);    // 沒有延遲的話 UART 串口速度會跟不上Arduino的速度,會導致資料不完整
    }

    if(s!=""){
        Serial.println(s);
    }
}
使用Serial Monitor測試即可

方法二:
或使用Serial.readString()
 void setup(){
    Serial.begin(9600);
    Serial.setTimeout(50);
    //Serial.setTimeout() sets the maximum milliseconds to wait for serial data 
    Serial.println("Hello");
}
void loop(){
    while (Serial.available()) {
      Serial.println(Serial.readString());
    }
}
疑惑?:為何網路上大家都使用前者?人云亦云?

C#傳送字元至Arduino

C#







加入
using System.IO.Ports;
程式碼
        private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.Open();
            button1.Enabled = false;
            button2.Enabled =true;
            button3.Enabled = true;
            button4.Enabled = false;
            label1.Text = "連線中";
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            serialPort1.PortName = "COM14";
            serialPort1.BaudRate = 9600;
            serialPort1.Parity = Parity.None;
            serialPort1.DataBits = 8;
            serialPort1.StopBits = StopBits.One;
            button2.Enabled = false;
            button3.Enabled = false;
            button4.Enabled = false;
            label1.Text = "尚未連線";
        }

        private void button2_Click(object sender, EventArgs e)
        {
            serialPort1.Write("0");
            serialPort1.Close();
            button1.Enabled = true;
            button2.Enabled = false;
            button3.Enabled = false;
            button4.Enabled = false;
            label1.Text = "斷線中";
        }

        private void button3_Click(object sender, EventArgs e)
        {
            serialPort1.Write("1");
            button4.Enabled = true;
            button3.Enabled = false;
            label1.Text = "LED ON";
        }

        private void button4_Click(object sender, EventArgs e)
        {
            serialPort1.Write("0");
            button3.Enabled = true;
            button4.Enabled = false;
            label1.Text = "LED OFF";
        }
Arduino程式
 // 結合 switch case 的 Serial port 讀取範例 V1
void setup() {
   Serial.begin(9600);     // 開啟 Serial port, 通訊速率為 9600 bps
   // 初始化 LED 接腳
   pinMode(13, OUTPUT);
}       
void loop() {
   // 檢查是否有資料可供讀取
   if (Serial.available() > 0) {
     // 讀取進來的 byte
     int inByte = Serial.read();
     // 根據收到的字元決定要打開或關掉 LED
     switch (inByte) {
     case '0':    
       digitalWrite(13, LOW);
       break;
     case '1': 
       digitalWrite(13, HIGH);
       break;
     default:
       // 關掉所有的 LED
       digitalWrite(13, LOW);
     }
   }