Продолжаем разбираться с Arduino в рамках «выходного» цикла статей #МастеримДома. Итак, на сегодняшний день у нас имеется устройство на платформе Arduino Uno для измерения концентрации CO2 в помещении и, соответственно, небольшой скетч для преобразования сигнала с датчика MQ135 в эту самую концентрацию. А сегодня мы перейдем непосредственно к теме управления устройством Arduino с помощью программы, написанной в Delphi 10.3 Rio.
Если Вы уже ознакомились со всеми предыдущими статьями (раз, два, три), то вам должно уже быть понятно в чем заключается суть управления Arduino из какой-либо сторонней программы, написанной вообще на любом языке будь то Delphi, Python или любой другой язык. Суть работы будет всегда одна — научиться читать и отправлять данные по средствам COM-порта (RS-232).
Таким образом, сегодня мы сделаем следующее:
- Напишем небольшую программку в Delphi, которая будет читать данные с COM-порта и отправлять команды для Arduino
- Доработаем наш скетч для того Arduino «умела» принимать и обрабатывать отправленные ей команды.
Работа с COM-портом в Delphi
Конечно, было бы намного интереснее (с точки зрения обучения программированию) написать свои собственные методы работы с COM-портом, расписать то, как работать с этими портами и так далее, но я решил просто воспользоваться уже готовыми решениями для Delphi коих тысячи.
В качестве основных компонентов для работы с COM-портами в Delphi я выбрал библиотеку ComPort Library. Несмотря на то, что эта библиотека не обновлялась довольно давно, версия для Delphi XE вполне спокойно скомплировалась и установилась в Delphi 10.3.
После установки на панели компонентов появится новая вкладка CPortLib, содержащая следующие компоненты для работы с RS-232 в Delphi:
Теперь, прежде, чем приступать к разработке приложения, определимся с алгоритмом работы. Итак, наша программа будет:
- Считывать числовые значения из COM-порта. Здесь в качестве числовых значений я подразумеваю, во-первых, значение концентрации в ppm и, во-вторых, калибровочные данные с датчика (R0).
- Отсылать Arduino следующие команды:
- «1» — начать передачу калибровочных данных
- «2» — прекратить передачу всех данных
- «любой символ» — начать или возобновить передачу данных о концентрации
Итак, интерфейс программы будет вот таким:
На форме расположена кнопка для настройки и подключения к COM-порту, список (TComboBox) для выбора одной из трех команд, описанных выше, метки (TLabel) для вывода среднего значения передаваемой величины и количества передач, Memo для вывода всего потока данных и, собственно, сам компонент TComPort для работы с портом.
Код события OnClick для кнопки подключения следующий:
procedure TfrmMain.Button1Click(Sender: TObject); begin ArduinoCom.ShowSetupDialog;//вызываем диалог настройки порта ArduinoCom.Open; //пробуем открыть порт if ArduinoCom.Connected then lbConnect.Caption:='Подключен на '+ArduinoCom.Port else lbConnect.Caption:='Не подключен'; end;
Сам клик по кнопке вызовет вот такой диалог настройки для компонента (форма диалога входит в библиотеку):
После того, как подключение к порту прошло успешно мы можем считывать данные и проводить необходимые вычисления. Для этого в секции private формы я объявил две переменные:
private aCount: integer; aAvg: double;
aCount — количество измерений; aAvg — среднее значение измеряемой величины.
У компонента TComPort пишем вот такой обработчик события OnRxChar:
procedure TfrmMain.ArduinoComRxChar(Sender: TObject; Count: Integer); var Str: string; aNext: double; begin ArduinoCom.ReadStr(Str, Count);//читаем строку от Arduino memData.Text:=memData.Text+Str;//записываем строку в Memo aNext:=StrToFloat(Trim(str),FS);//полученное числовое значение if aCount=0 then //если это первое значение - оно же и будет средним aAvg:=aNext else //иначе рассчитываем среднее значение aAvg:=aAvg*(aCount/(aCount+1))+aNext/(aCount+1); inc(aCount);//наращиваем счётчик UpdateForm;//обновляем метки на форме end;
Процедура UpdateForm простая:
procedure TfrmMain.UpdateForm; begin lbAvg.Caption:=FloatToStrF(aAvg, fffixed, 6,2); lbCount.Caption:=IntToStr(aCount); end;
Соответственно, при выборе значения в ComboBox мы должны обнулить все значения, отправить Arduino команду и начать отсчёт заново:
procedure TfrmMain.cbCommandChange(Sender: TObject); begin //отправляем команду Arduino case cbCommand.ItemIndex of 0:ArduinoCom.WriteStr('3');//ppm 1:ArduinoCom.WriteStr('1');//ro 2:ArduinoCom.WriteStr('2');//---- end; memData.Text:=EmptyStr; aCount:=0; aAvg:=0; UpdateForm; end;
А при запуске программы не забыть выставить первоначальные значения всех переменных:
procedure TfrmMain.FormCreate(Sender: TObject); begin aAvg:=0; aCount:=0; FS.DecimalSeparator:='.'; UpdateForm; end;
В принципе, на сегодня это и всё, что нам необходимо, чтобы научиться управлять Arduino из Delphi. Теперь перейдем к скетчу Arduino и научим наше устройство «понимать» отправленные ей команды.
Скетч Arduino
Как я говорил, ещё в первой статье посвященной Arduino и Delphi, необходимо чуть-чуть разобраться с С++. Я не скажу, что представленный ниже скетч — это идеальный код (уверен, что можно всё сделать короче и понятнее), но скетч работает и даёт ожидаемый результат. Итак, код скетча для Arduino следующий:
#include <MQ135.h>//подключаем библиотеку для работы с MQ135 #define analogPin A0 // аналоговый выход MQ135 подключен к пину A0 Arduino MQ135 gasSensor = MQ135(analogPin); bool Zero = false; //если True - с датчика читается значение Ro bool Conc = true; //если True - с датчика читается концентрация float Value = 0; //значение с датчика void setup() { Serial.setTimeout(1000); //настраиваем тай-маут для порта Serial.begin(9600); // инициализация последовательного порта pinMode(analogPin, INPUT); // режим работы аналогового пина delay(1000); // даём датчику небольшое время на разогрев } void loop() { if (Serial.available() > 0) { //если есть доступные данные // считываем строку String incomingStr = Serial.readString(); //пробуем преобразовать строку в число int Command = incomingStr.toInt(); switch (Command) { //выбираем, что передавать в порт и передавать ли вообще case 1: Zero = true; Conc = false; break; case 2: Zero = false; Conc = false; break; default: Zero = false; Conc = true; } } if (Zero) Value = gasSensor.getRZero(); // чтение R0 else if (Conc) Value = gasSensor.getPPM(); //чтение концентрации if (Zero || Conc) //стоит ли что-то передавать Serial.println(Value); // выдача в последовательный порт delay(1000); // задержка перед выводом следующего значения }
В принципе, по комментариям в коде должна стать понятной логика скетча, а именно:
1. По умолчанию стартуем с чтением концентрации
2. Если в порт пришло что-либо, то пробуем прочитать строку
3. Если строка представляет собой число 1 или 2, то либо передаем R0, либо прерываем какую-либо передачу вообще
4. Если пришла непонятная строка или, как в случае с нашей программой в Delphi — значение 3, то начинаем передавать значение концентрации.
Теперь можно загрузить скетч в Arduino, запустить нашу Delphi-программку и посмотреть на её работу.
Результат
Передача данных о концентрации CO2 в помещении
Аналогичным образом передаются калибровочные данные:
Таким образом, можно констатировать, что управлять Arduino с помощью программ, написанных в Delphi вполне возможно и делается это относительно просто. Более того, если необходимо, то на «плечи» Delphi можно перенести и все необходимые расчёты (всё же на борту Arduino не так уж и много памяти).
Здравствуйте! Пожалуйста, покажите подробнее этапы работы с библиотекой ComPort Library (а можно с сопроводительными иллюстрациями) вплоть до появления вкладки CPortLib на панели компонентов. Благодарю!