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()可不使用











沒有留言:

張貼留言