아두이노 조도센서 모니터링 PhotoCell
ReadingSerialPort MenuStrip ComboBox ProgressBar Button ListBox Button chart
1. Arduino 프로그램 아두이노는 PhotoCell과 저항을 직렬로 연결하여 전압 분배회로를 만든다. PhotoCell과 저항 사이의 전압을 ANALOG IN A0에 연결한다. PhotoCell의 저항이 작아지면 전압도 낮아지고 저항이 커지면 전압이 높아진다, 즉 A0의 값이 커진다. A0의 값은 0~1023까지의 숫자로 표현된다 setup() 에서는 Serial.begin(9600); 으로 시리얼 통신을 시작, Loop()에서 analogRead(A0) 함수로 A0의 값을 읽어서 Serial.println()으로 보낸다. 이 값을 C# 코드에서 읽을 것이다. 한번 출력한 후에는 1초간 delay(1000) 를 갖는다 이를 반복한다. 즉 1초에 한번씩 A0의 값을 시리얼로 보낸다
Arduino 회로
2. Serial 값을 읽어오기 장치관리자를 열어보면 포트에 COM3가 Arduino에 연결되어 있다는 것을 알 수 있다
C#에서 Serial통신 Serial Port에서 값을 받아와서 출력 using System.IO.Ports; namespace SerialComm { public partial class Form1 : Form SerialPort sPort; // 시리얼 포트 선언 public Form1() InitializeComponent(); sPort = new SerialPort("COM3", 9600); sPort.Open(); sPort.DataReceived += SPort_DataReceived; } // 시리얼 포트의 Data_Received 이벤트 private void SPort_DataReceived(object sender, SerialDataReceivedEventArgs e) string s = sPort.ReadLine(); this.BeginInvoke((new Action(delegate { showValue(s); }))); private void showValue(string s) label1.Text = "Serial Value : " + s; Serial Port에서 값을 받아와서 출력
3. COM Port 선택 항상 COM3가 아니므로 ComboBox를 사용하여 시리얼 포트를 선택하게 함 public Form1() { InitializeComponent(); // ComboBox foreach (var ports in SerialPort.GetPortNames()) comboBox1.Items.Add(ports); } comboBox1.Text = "Select Port"; private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) ComboBox cb = sender as ComboBox; sPort = new SerialPort( cb.SelectedItem.ToString() ); sPort.Open(); sPort.DataReceived += SPort_DataReceived; 항상 COM3가 아니므로 ComboBox를 사용하여 시리얼 포트를 선택하게 함
4. ProgressBar 추가 private void showValue(string s) { label1.Text = "Serial Value : " + s; // 아두이노의 A0에서 받는 값의 범위(0~1023) progressBar1.Minimum = 0; progressBar1.Maximum = 1023; progressBar1.Value = Int32.Parse(s); } GroupBox 로 ProgressBar와 두 개의 Label 을 감싸고 photoCell의 값을 표시함
5. ListBox의 추가 private void showValue(string s) { label1.Text = "Serial Value : " + s; // 아두이노의 A0에서 받는 값의 범위(0~1023) progressBar1.Minimum = 0; progressBar1.Maximum = 1023; progressBar1.Value = Int32.Parse(s); // ListBox에 시간과 값을 표시 string item = DateTime.Now.ToString() + "\t" + s; listBox1.Items.Add(item); // 계속 스크롤이 되도록 처리 listBox1.SelectedIndex = listBox1.Items.Count - 1; }
6. Chart 추가 public Form1() { InitializeComponent(); foreach (string s in SerialPort.GetPortNames()) comboBox1.Items.Add(s); comboBox1.Text = "Select Port"; // Chart Setting chartSetting(); } private void showValue(string s) { …. // Chart 표시 chart1.Series["PhotoCell"].Points.Add(v); }
chartSetting() private void chartSetting() { chart1.ChartAreas.Clear(); chart1.ChartAreas.Add("draw"); chart1.ChartAreas["draw"].AxisX.Minimum = 0; chart1.ChartAreas["draw"].AxisX.Maximum = xCount; chart1.ChartAreas["draw"].AxisX.Interval = xCount / 5; chart1.ChartAreas["draw"].AxisX.MajorGrid.LineColor = Color.White; chart1.ChartAreas["draw"].AxisX.MajorGrid.LineDashStyle = ChartDashStyle.Dash; chart1.ChartAreas["draw"].AxisY.Minimum = 0; chart1.ChartAreas["draw"].AxisY.Maximum = 1024; chart1.ChartAreas["draw"].AxisY.Interval = 200; chart1.ChartAreas["draw"].AxisY.MajorGrid.LineColor = Color.White; chart1.ChartAreas["draw"].AxisY.MajorGrid.LineDashStyle = ChartDashStyle.Dash; chart1.ChartAreas["draw"].BackColor = Color.Blue; chart1.Series.Clear(); chart1.Series.Add("PhotoCell"); chart1.Series["PhotoCell"].ChartType = SeriesChartType.Line; chart1.Series["PhotoCell"].Color = Color.LightGreen; chart1.Series["PhotoCell"].BorderWidth = 3; if(chart1.Legends.Count > 0) // 범례를 안보이게 한다 chart1.Legends.RemoveAt(0); }
7. 숫자 버튼 추가 public Form1() { InitializeComponent(); …. // 숫자 버튼 button1.BackColor = Color.Blue; button1.ForeColor = Color.White; button1.Text = ""; button1.Font = new Font("맑은 고딕", 16, FontStyle.Bold); } private void showValue(string s) // 숫자버튼 표시 button1.Text = sPort.PortName + "\n" + s;
8. Port 연결/비연결 Disconnect 했다가 다시 Connect 하면 숫자를 다시 읽어 들일 수 있다 // Connect Serial Port private void button2_Click(object sender, EventArgs e) { if (sPort.PortName == "") MessageBox.Show("Select Serial Port Available First", "Warning"); return; } sPort.Open(); sPort.DataReceived += SPort_DataReceived; // Connect 버튼 비활성화, Disconnect 버튼 활성화 button2.Enabled = false; button3.Enabled = true; // disconnect Serial Port private void button3_Click(object sender, EventArgs e) { sPort.Close(); button3.Enabled = false; button2.Enabled = true; } Disconnect 했다가 다시 Connect 하면 숫자를 다시 읽어 들일 수 있다
9. label1 -> 연결 시간 표시 public Form1() { …. label1.Text = "Connection Time : "; } private void showValue(string s) //label1.Text = "Serial Value : " + s; private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) ComboBox cb = sender as ComboBox; sPort = new SerialPort( cb.SelectedItem.ToString() ); sPort.Open(); sPort.DataReceived += SPort_DataReceived; label1.Text = "Connection Time : " + DateTime.Now.ToString();
10. 데이터 값에 시간 포함 class sensorData { public DateTime dt; public int value; // 생성자 public sensorData(DateTime dt, int value) this.dt = dt; this.value = value; } // 저장하기 위한 자료구조 List<sensorData> myData = new List<sensorData>(); private void showValue(string s) { int v = Int32.Parse(s); if (v < 0 || v > 1023) return; myData.Add(new sensorData(DateTime.Now, v)); … }
11. 메뉴 추가 파일 – 저장/읽어 오기
12. 저장하기 private void 저장ToolStripMenuItem_Click(object sender, EventArgs e) { SaveFileDialog saveFileDialog1 = new SaveFileDialog(); saveFileDialog1.Filter = "txt files(*.txt)|*.txt|All files(*.*)|*.*"; if(saveFileDialog1.ShowDialog() == DialogResult.OK) string fileName = saveFileDialog1.FileName; FileStream fs = new FileStream(fileName, FileMode.Create); StreamWriter sw = new StreamWriter(fs); foreach (var item in myData) string s = string.Format("{0}\t{1}", item.dt.ToString(), item.value.ToString()); sw.WriteLine(s); } sw.Close();
13. 읽어오기 - 1 Timer myTimer; private void 읽어오기ToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "txt files(*.txt)|*.txt|All files(*.*)|*.*"; if(ofd.ShowDialog() == DialogResult.OK) string fileName = ofd.FileName; string[] lines = File.ReadAllLines(fileName); foreach (var line in lines) string[] items; items = line.Split('\t'); myData.Add(new sensorData(Convert.ToDateTime(items[0]), Int32.Parse(items[1]) )); } label1.Text = "Connection Time : " + myData[0].dt.ToString(); myTimer = new Timer(); myTimer.Interval = 1; myTimer.Start(); myTimer.Tick += MyTimer_Tick;
13. 읽어오기 - 2 int Count = 0; // 현재 프로세싱 중인 데이터 인덱스 private void MyTimer_Tick(object sender, EventArgs e) { string line = ""; line += myData[Count].dt.ToString() + "\t" + myData[Count].value.ToString(); listBox1.Items.Add(line); listBox1.SelectedIndex = listBox1.Items.Count - 1; progressBar1.Value = myData[Count].value; button1.Text = myData[Count].value.ToString(); chart1.Series["PhotoCell"].Points.Add(myData[Count].value); // zoom을 위해 200개까지는 기본, 데이터 갯수가 많아지면 200개만 보이지만, 스크롤 나타남 chart1.ChartAreas["draw"].AxisX.Minimum = 0; chart1.ChartAreas["draw"].AxisX.Maximum = (Count >= xCount) ? Count : xCount; if (Count > xCount) chart1.ChartAreas["draw"].AxisX.ScaleView.Zoom(Count - xCount, Count); else chart1.ChartAreas["draw"].AxisX.ScaleView.Zoom(0, xCount); if (++Count >= myData.Count) myTimer.Stop(); }
14. Chart에 스크롤바 달기 chart1.ChartAreas["draw"].CursorX.AutoScroll = true; chart1.ChartAreas["draw"].AxisX.ScaleView.Zoomable = true; chart1.ChartAreas["draw"].AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll; chart1.ChartAreas["draw"].AxisX.ScrollBar.ButtonColor = Color.LightSteelBlue; // zoom을 위해 200개까지는 기본, 데이터 갯수가 많아지면 200개만 보이지만, 스크롤 나타남 chart1.ChartAreas["draw"].AxisX.Minimum = 0; chart1.ChartAreas["draw"].AxisX.Maximum = (myData.Count >= xCount) ? myData.Count : xCount; // change chart range : Zoom 사용 if (myData.Count > xCount) { chart1.ChartAreas["draw"].AxisX.ScaleView.Zoom (myData.Count - xCount, myData.Count); } else chart1.ChartAreas["draw"].AxisX.ScaleView.Zoom(0, xCount);
15. View All 과 Zoom 처리 // View All Button private void button4_Click(object sender, EventArgs e) { chart1.ChartAreas["draw"].AxisX.Minimum = 0; chart1.ChartAreas["draw"].AxisX.Maximum = myData.Count; chart1.ChartAreas["draw"].AxisX.ScaleView.Zoom(0, myData.Count); chart1.ChartAreas["draw"].AxisX.Interval = myData.Count / 4; } // Zoom Button private void button5_Click(object sender, EventArgs e) chart1.ChartAreas["draw"].AxisX.ScaleView.Zoom(myData.Count - xCount, myData.Count); chart1.ChartAreas["draw"].AxisX.Interval = xCount / 4;
16. 시뮬레이션 시리얼 포트의 Data_Received() 이벤트 대신 1초에 한번씩 시간과 데이터를 myData에 저장한다 이를 위해 Timer를 하나 추가한다 아두이노가 없다면, 시리얼 통신 대신 랜덤 숫자를 만들어서 처리하자 private void 시작ToolStripMenuItem_Click(object sender, EventArgs e) { // 1초에 한번씩 현재 시간과 랜덤 값을 myData에 저장 timer2 = new Timer(); timer2.Interval = 1000; timer2.Start(); timer2.Tick += Timer2_Tick; } private void 끝ToolStripMenuItem_Click(object sender, EventArgs e) timer2.Stop(); private void Timer2_Tick(object sender, EventArgs e) { Random r = new Random(); int v = r.Next(1023); myData.Add(new sensorData(DateTime.Now, v)); …