Метеостанция + построение графика + C#

Хоть я в работе с arduino новичок, и не все еще знаю о способностях данного аппарата, я решил поделиться своим успешным опытом, потому как знаю, каково это — искать то, что невозможно найти или этого просто нет. В данной статье я подробно опишу мой проект, который явился моей практикой в университете. Мне никто не навязывал, просто друг увлекся, показал мне. Меня конечно же эта штука заинтересовала и я решил попробовать. Моя программа не так сложна. 

Итак, работа заключается в следующем: с известного многим датчика DHT11 (датчик влажности и температуры) принимаем данные через COM порт, далее записываем данные в текстовый файл и по этим данным строим график изменений.

Arduino Uno

Arduino Uno построен на ATmega328. Платформа имеет 14 цифровых вход/выходов, 6 аналоговых входов, кварцевый генератор 16 МГц, разъем USB, силовой разъем, разъем ICSP и кнопку перезагрузки. Для работы необходимо подключить платформу к компьютеру посредством кабеля USB, либо подать питание при помощи адаптера AC/DC или батареи.

Характеристики:

Микроконтроллер ATmega328
Рабочее напряжение 5 В
Входное напряжение (рекомендуемое) 7-12 В
Входное напряжение (предельное) 6-20 В
Цифровые Входы/Выходы 14 
Аналоговые входы 6
Постоянный ток через вход/выход 40 мА
Постоянный ток для вывода 3.3 В 50 мА
Флеш-память    32 Кб (ATmega328) из которых 0.5 Кб используются для загрузчика
ОЗУ    2 Кб (ATmega328)
EEPROM 1 Кб (ATmega328)
Тактовая частота 16 МГц

Датчик DHT11

Относительная влажность:

  • разрешение = 16 Bit
  • повторяемость = ±1% 
  • точность = если 25℃, то ±5%
  • взаимозаменяемость = полностью взаимозаменяемый
  • время отклика = 1-2 сек
  • гистерезис = <±0,3% RH
  • стабильность = <±0,5% RH/год 

Температура:

  • разрешение = 16 Bit
  • повторяемость = ±0,2℃
  • диапазон = если 25℃ ± 2℃
  • время отклика = 1-2 сек

​Электрические характеристики:

  • питание = 3,5 — 5,5 V

Фотография датчика в разрезе:

DHT11 цифровой датчик температуры и влажности является составным датчиком, который содержит калиброванный цифровой выходной сигнал с показаниями температуры и влажности. Он имеет высокую надежность и превосходную долговременную стабильность работы. Датчик включает в себя резистивный сенсор влажности и компоненты NTC структуры для измерения температуры.

Подключение

1 — VCC — питание 3,5 — 5,5 V
2 — DATA — передача данных
3 — NC — не используется
4 — GND — отрицательное питание

Для подключения к arduino необходимо подключить между 1 и 2 ножкой резистор = 10кОм.

Схемы подключения

Схема:

Программный код (скетч для датчика DHT11)

 #include "DHT.h" #define DHTPIN 2     // у меня подключен ко 2 пину, поэтому 2 #define DHTTYPE DHT11   // указываем какой датчик DHT dht(DHTPIN, DHTTYPE); void setup() {   Serial.begin(9600); //указываем скорость передачи в бодах   dht.begin(); } void loop() {   int h = dht.readHumidity();//переменная для влажности   int t = dht.readTemperature();//переменная для температуры   if (isnan(t) || isnan(h)) {//проверка (что мы получаем на запрос из датчика - цифры?)     Serial.println("Failed to read from DHT");//сообщение об ошибке   }    else {     //Serial.print("Humidity: ");      Serial.print(h);//вывожу построчно - влажность      Serial.print("tn");//сдвиг каретки и начало строки     // Serial.print("Temperature: ");      Serial.print(t);//температура     Serial.print("n");     //Serial.println(" *C");   }   delay(60000);//задержка времени в милсек. = 1 мин.   //delay(3600000);//на каждый час }

Загружаю данный скетч, на каждую минуту и получаю данные в мониторе COM порта

34 — в % относительная влажность
25 — в ℃ температура

Далее я так сказать украшал свою программу, придавал ей товарный вид. Все это я делал в среде программирования Microsoft Visual Studio 2012 .NET.  Я сделал приложение, которое через COM порт получает данные и обрабатывает их. Получаю, записываю в файл. Тоже построчно. В первой строке значение, вторая строка — дата и время в момент получения данных. Это пригодится для построения графиков. Кто не знаком с работой в C# очень рекомендую, удобная вещь. Создаю форму с 2-мя текстбоксами, 1- будет выводится значение влажности, 2 — значение температуры. Из них создается файл, если не был создан, если был создан до дописываем в него. 2 файла — 1-График_температуры, 2-График_влажности. Названия можете придумать свои. Элементы, используемые в программе: textbox и textbox1, openFileDialog — для работы с выбором файла для графика, notifyIcon — для работы с иконкой, для сворачивания в трей.

Вот запуск приложения

Для просмотра графика, нажимаем на кнопку — Демонстрация графика и выбираем файл, т.е. что мы хотим увидеть (влажность или температуру)

Далее нажимаем — Открыть, и увидим график

Это график относительной влажности. Есть свойство графика — он масштабируется по оси Х. Если большой интервал, мы мышкой выделяем зону, которая нам интересна и он приближает. Если общий график, то на оси Х около 0 нажмем «-» и график будет общим.

Также я немного усовершенствовал программу, я сделал так, чтобы она сворачивалась в трей. Потому что, чтобы работать, она должна быть открыта, или запущена, ее можно свернуть как окно, но вероятно что она будет мешать. А так очень удобно.

1ый значок в трее — программа. По нажатию левой кнопки мыши сворачивается или разворачивается программа.

Привожу исходник своей программы

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.IO.Ports; using System.IO;  namespace arduino {     public partial class Form1 : Form     {         public Form1()         {             InitializeComponent();              notifyIcon1.Visible = false;             this.notifyIcon1.MouseClick += new MouseEventHandler(notifyIcon1_MouseClick);             this.Resize += new System.EventHandler(this.Form1_Resize);              // Открываем порт, и задаем скорость в 9600 бод             serialPort1.PortName = "COM6";             serialPort1.BaudRate = 9600;             serialPort1.DtrEnable = true;             serialPort1.Open();             serialPort1.DataReceived += serialPort1_DataReceived;             serialPort1.DataReceived += serialPort1_DataReceived1;         }         //****** поток ком порта         private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)         {             string vlag = serialPort1.ReadLine();             this.BeginInvoke(new LineReceivedEvent(LineReceived), vlag);         }          private void serialPort1_DataReceived1(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)         {             string temp = serialPort1.ReadLine();             this.BeginInvoke(new LineReceivedEvent1(LineReceived1), temp);         }          //запись влажности         private delegate void LineReceivedEvent(string vlag);         private void LineReceived(string vlag)         {             textBox1.Text = vlag;             string path = "График_влажности.txt";             string date = DateTime.Now.ToString();             // Создание файла и запись в него             using (StreamWriter sw = File.AppendText(path))             {                 sw.WriteLine(vlag);                 sw.WriteLine(date);             }         }          //запись температуры         private delegate void LineReceivedEvent1(string temp);         private void LineReceived1(string temp)         {             textBox2.Text = temp.ToString();             string path = "График_температуры.txt";             string date = DateTime.Now.ToString();             // Создание файла и запись в него             using (StreamWriter sw = File.AppendText(path))             {                 sw.WriteLine(temp);                 sw.WriteLine(date);             }         }          private void button1_Click(object sender, EventArgs e)         {             if (openFileDialog1.ShowDialog() == DialogResult.OK)             {                 chart1.ChartAreas[0].AxisX.ScaleView.Zoom(0,30);                 chart1.ChartAreas[0].CursorX.IsUserEnabled = true;                 chart1.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;                 chart1.ChartAreas[0].AxisX.ScaleView.Zoomable = true;                 chart1.ChartAreas[0].AxisX.ScrollBar.IsPositionedInside = true;                  //chart1.ChartAreas[0].AxisY.ScaleView.Zoom(0, 30);                 //chart1.ChartAreas[0].CursorY.IsUserEnabled = true;                 //chart1.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;                 //chart1.ChartAreas[0].AxisY.ScaleView.Zoomable = true;                 //chart1.ChartAreas[0].AxisY.ScrollBar.IsPositionedInside = true;                  StreamReader streamReader = new StreamReader(openFileDialog1.FileName);                 chart1.Series[0].Points.Clear();                 while (!streamReader.EndOfStream)                 {                                string Y = streamReader.ReadLine();                     string X = streamReader.ReadLine();                                          chart1.Series[0].Color = Color.Red;                     chart1.Series[0].BorderWidth = 1;                     chart1.Series[0].Points.AddXY(X, Y);                 }                 streamReader.Close();             }          }          private void Form1_Resize(object sender, EventArgs e)         {             if (this.WindowState == FormWindowState.Minimized)             {                 this.Hide();                 notifyIcon1.Visible = true;             }         }          private void notifyIcon1_MouseClick(object sender, MouseEventArgs e)         {             this.Show();             this.WindowState = FormWindowState.Normal;             notifyIcon1.Visible = false;         }     } }