Arduino обмен данными с компьютером. Arduino и ПК: обмен данными

Обмен данными между двумя платами Arduino - очень полезная фича для многих проектов.

Например, можно чтобы одна плата Arduino управляла моторами, а вторая использовалась для подключения сенсоров и передачи управляющих сигналов на первый микроконтроллер. Реализовать обмен данными между двумя Arduino можно с использованием с помощью последовательного (serial) интерфейса. Для этого будут использоваться контакты RX и TX.

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

На рисунке ниже показана схема подключения двух контроллеров Arduino. На схеме показаны две платы Arduino Uno, но можно использовать и Arduino Mega, если использовать соответствующие контакты RX и TX.

Обратите внимание, что необходимо объединить контакты Gnd.

В противном случае, обмен данными происходить не будет! При подключении контакт TX подключается к RX, а RX - к TX.

Особенности программы

При использовании серийного интерфейса, данные передаются в байтах. Эти байты считываются вторым Arduino по одному. Если мы передаем символы, преобразовать их в байты не проблема. Но если мы передаем и символы и цифровые значения, данные могут интерпретироваться некорректно, так как число и символ могут принимать одинаковые значения в байтах. Кроме того, числа могут не поместиться в один байт... Самое простое решение этой проблемы - не передавать символы и числа одновременно.

Простой скетч

Можно настроить передачу данных между двумя Arduino с использованием примера Physical Pixel .

Загрузите скетч Physical Pixel, который можно найти в Arduino IDE в папке: File >> Examples >> Communication, на ваш Arduino.

На вторую плату Arduino загрузите следующий скетч:

Serial.begin(9600);

Serial.print("H");

Serial.print("L");

Когда код начинает работать, светодиод на 13 пине Arduino должен загораться и тухнуть с частотой 0.5 Гц. Для того, чтобы удостоверится в работоспособности скетча, можете изменить значение задержки (delay).

Символ "H" в приведенном выше скетче зажигает светодиод, а символ "L" отвечает за отключение светодиода. Можно расширить этот список символов для выполнения других действий.

Но этот код недостаточно гибкий и для решения более комплексных и сложных задач может не подойти.

Расширенный скетч

Есть второй вариант скетча для передачи данных от одного Arduino к другому. Для этого все данные с Arduino, который передает информацию, преобразовываются в символы. Вторая плата Arduino считывает поступающие данные тоже в символах. На практике данные, конечно, передаются в байтах, но наш Arduino отлично справляется с конвертацией символов в байты и обратно.

Скетч для Arduino, передающего данные

Скетч для микроконтроллера, который передает данные, конвертирует символы в байты и, если необходимо, преобразовывает числовые значения в символы, перед тем как превратить их в байты. Пример скетча приведен ниже.

// скетч для Arduino, который передает данные

Serial.begin(9600);

int value=1234; // будет гораздо веселее, если это будут данные с какого-то сенсора

itoa(value, str, 10); // преобразует данные в массив символов

Serial.write(str, 4);

Скетч для Arduino, принимающего данные

Второй микроконтроллер Arduino получит массив данных в байтах и начнет их интерпретировать. Скетч для платы-ресивера приведен ниже.

// скетч для Arduino, который принимает данные

Serial.begin(9600);

Serial1.begin(9600);

if (Serial1.available()) {

while(Serial1.available() && i<4) {

str = Serial1.read();

Serial.println(str,4);

В программе, которая приведена выше, есть один недостаток. В результате мы будем получать массив символов, а не чисел...

Для преобразования массива символов в числовой, можно воспользоваться функцией, код которой представлен ниже.

void CharToFloat(char* chars, double* value, int count) {

float multiplier;

float front =0.0, behind =0.0;

// перед точкой

while(chars[i]!="." && i<count) {

if (chars[i]==".") {

for(int j=i; j>0; j--) {

for(int k=q; k>1; k--) {

multiplier *= 10;

front+=(chars[l]-"0")*multiplier;

// после точки

while(chars[n]!="\0" && i<count) {

if (chars[n]=="\0") {

for(int j=n-1; j>i; j--) {

for(int k=q-(i+2); k>=0; k--) {

multiplier = 0.1*multiplier;

behind+=(chars[l]-"0")*multiplier;

Оставляйте Ваши комментарии, вопросы и делитесь личным опытом ниже. В дискуссии часто рождаются новые идеи и проекты!

Для связи микроконтроллера с компьютером чаще всего применяют COM-порт. В этой статье мы покажем, как передать команды управления из компьютера и передать данные с контроллера.

Подготовка к работе

Большинство микроконтроллеров обладают множеством портов ввода-вывода. Для связи с ПК наиболее пригоден из них протокол UART. Это протокол последовательной асинхронной передачи данных. Для его преобразования в интерфейс USB на плате есть конвертор USB-RS232 – FT232RL.
Для выполнения примеров их этой статьи вам будет достаточно только Arduino-совместимая плата. Мы используем . Убедитесь, что на вашей плате установлен светодиод, подключенный к 13му выводу и есть кнопка для перезагрузки.

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

int symbol = 33 ; void setup() { Serial. begin(9600 ) ; Serial. println(" ASCII Table ~ Character Map " ) ; } void loop() { Serial. write(symbol) ; Serial. print(" , dec: " ) ; Serial. print(symbol) ; Serial. print(" , hex: " ) ; Serial. print(symbol, HEX) ; Serial. print(" , oct: " ) ; Serial. print(symbol, OCT) ; Serial. print(" , bin: " ) ; Serial. println(symbol, BIN) ; if (symbol = = 126 ) { while (true) { continue ; } } symbol+ + ; }

Переменная symbol хранит код символа. Таблица начинается со значения 33 и заканчивается на 126, поэтому изначально переменной symbol присваивается значение 33.
Для запуска работа порта UART служит функция Serial.begin() . Единственный ее параметр – это скорость. О скорости необходимо договариваться на передающей и приемной стороне заранее, так как протокол передачи асинхронный. В рассматриваемом примере скорость 9600бит/с.
Для записи значения в порт используются три функции:

  1. Serial.write() – записывает в порт данные в двоичном виде.
  2. Serial.print() может иметь много значений, но все они служат для вывода информации в удобной для человека форме. Например, если информация, указанная как параметр для передачи, выделена кавычками – терминальная программа выведет ее без изменения. Если вы хотите вывести какое-либо значение в определенной системе исчисления, то необходимо добавить служебное слово: BIN-двоичная, OCT – восьмеричная, DEC – десятичная, HEX – шестнадцатеричная. Например, Serial.print(25,HEX) .
  3. Serial.println() делает то же, что и Serial.print() , но еще переводит строку после вывода информации.

Для проверки работы программы необходимо, чтобы на компьютере была терминальная программа, принимающая данные из COM-порта. В Arduino IDE уже встроена такая. Для ее вызова выберите в меню Сервис->Монитор порта. Окно этой утилиты очень просто:

Теперь нажмите кнопку перезагрузки. МК перезагрузится и выведет таблицу ASCII:

Обратите внимание на вот эту часть кода:

if (symbol = = 126 ) { while (true) { continue ; } }

Она останавливает выполнение программы. Если вы ее исключите – таблица будет выводиться бесконечно.
Для закрепления полученных знаний попробуйте написать бесконечный цикл, который будет раз в секунду отправлять в последовательный порт ваше имя. В вывод добавьте номера шагов и не забудьте переводить строку после имени.

Отправка команд с ПК

Прежде чем этим заниматься, необходимо получить представление относительного того, как работает COM-порт.
В первую очередь весь обмен происходит через буфер памяти. То есть когда вы отправляете что-то с ПК устройству, данные помещаются в некоторый специальный раздел памяти. Как только устройство готово – оно вычитывает данные из буфера. Проверить состояние буфера позволяет функция Serial.avaliable() . Эта функция возвращает количество байт в буфере. Чтобы вычитать эти байты необходимо воспользоваться функцией Serial.read() . Рассмотрим работу этих функций на примере:

int val = 0 ; void setup() { Serial. begin(9600 ) ; } void loop() { if (Serial. available() > 0 ) { val = Serial. read() ; Serial. print(" I received: " ) ; Serial. write(val) ; Serial. println() ; } }

После того, как код будет загружен в память микроконтроллера, откройте монитор COM-порта. Введите один символ и нажмите Enter. В поле полученных данных вы увидите: “I received: X” , где вместо X будет введенный вами символ.
Программа бесконечно крутится в основном цикле. В тот момент, когда в порт записывается байт функция Serial.available() принимает значение 1, то есть выполняется условие Serial.available() > 0 . Далее функция Serial.read() вычитывает этот байт, тем самым очищая буфер. После чего при помощи уже известных вам функций происходит вывод.
Использование встроенного в Arduino IDE монитора COM-порта имеет некоторые ограничения. При отправке данных из платы в COM-порт вывод можно организовать в произвольном формате. А при отправке из ПК к плате передача символов происходит в соответствии с таблицей ASCII. Это означает, что когда вы вводите, например символ “1”, через COM-порт отправляется в двоичном виде “00110001” (то есть “49” в десятичном виде).
Немного изменим код и проверим это утверждение:

int val = 0 ; void setup() { Serial. begin(9600 ) ; } void loop() { if (Serial. available() > 0 ) { val = Serial. read() ; Serial. print(" I received: " ) ; Serial. println(val, BIN) ; } }

После загрузки, в мониторе порта при отправке “1” вы увидите в ответ: “I received: 110001”. Можете изменить формат вывода и просмотреть, что принимает плата при других символах.

Управление устройством через COM-порт

Очевидно, что по командам с ПК можно управлять любыми функциями микроконтроллера. Загрузите программу, управляющую работой светодиода:

int val = 0 ; void setup() { Serial. begin(9600 ) ; } void loop() { if (Serial. available() > 0 ) { val = Serial. read() ; if (val= = "H" ) digitalWrite(13 , HIGH) ; if (val= = "L" ) digitalWrite(13 , LOW) ; } }

При отправке в COM-порт символа “H” происходит зажигание светодиода на 13ом выводе, а при отправке “L” светодиод будет гаснуть.
Если по результатам приема данных из COM-порта вы хотите, чтобы программа в основном цикле выполняла разные действия, можно выполнять проверку условий в основном цикле. Например.

Загрузим стандартный пример «Physical Pixel» через меню File\Examples\4.Communication\PhysicalPixel. Эта программа ждет данные от компьютера. При получении символа ‘H’ тестовый индикатор загорается, при получении символа ‘L’ – гаснет. Разберем ее исходный код:

int outputPin = 13 ; //здесь храним номер контакта
int val; //здесь будет храниться принятый символ

void setup()
{
Serial.begin (9600 ) ; //установка порта на скорость 9600 бит/сек
pinMode(outputPin, OUTPUT) ; //устанавливаем 13 контакт в режим вывода
}

void loop()
{
if (Serial.available () ) { //если есть принятый символ,
val = Serial.read () ; // то читаем его и сохраняем в val
if (val == "H" ) { // если принят симовол "H",...
digitalWrite(outputPin, HIGH) ; // то включаем светодиод
}
if (val == "L" ) { // если принят симовол "L",
digitalWrite(outputPin, LOW) ; // то выключаем светодиод
}
}
}

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

Как проверить работу этой программы после того, как Вы загрузите ее в микроконтроллер? Нужно найти способ отправлять символы на COM-порт компьютера, чтобы микроконтроллер принимал и обрабатывал их. Существует множество вариантов решения этой задачи.

Используем встроенный в среду разработки Arduino монитор COM-порта

Это наиболее простой, и понятный начинающим метод.

Монитор COM-порта запускается через меню Tools\Serial Monitor, либо через панель инструментов. В старых версиях ПО монитор был доступен только через панель инструментов: . Вызвав монитор убедитесь, что выбрана та же самая скорость обмена, что и в программе микроконтроллера. Теперь можно вводить любые символы в поле ввода справа, и нажимать кнопку «Send» – введенные символы будут отправлены в порт, и там их примет Ваша программа. Введите там латинскую букву «H», нажмите «Send» – тестовый светодиод загорится. Если послать «L» – погаснет. Кстати, все данные, которые Ваша программа будет посылать на COM-порт будут выводиться в окне снизу.

Используем программу эмуляции терминала HyperTerminal

Это немного более сложный в реализации вариант обмена.

В состав Windows обычно включена программа эмуляции терминала HyperTerminal. В Windows XP ее можно найти в меню Пуск \ Все программы \ Программы \ Стандартные \ Связь \ HyperTerminal. При запуске нужно отказаться от создания подключения, выбрать меню Файл \ Свойства. В появившемся диалоге выбрать свой COM-порт, нажать «Настроить», и настроить параметры связи в соответствии с рисунком:

Можно воспользоваться другим эмулятором терминала - все они обычно имеют подобный функционал и схожие настройки.

Нажмите «OK» в обоих окнах, и попав в основное окно программы, любую клавишу на клавиатуре – HyperTerminal подключится к COM-порту. Теперь все набираемые на клавиатуре символы попадают через COM-порт в микроконтроллер, а все, что отправляет микроконтроллер, попадает на экран. Понажимайте клавиши «H» и «L» (следите за выбранным языком, и регистром) – тестовый светодиод должен загораться и гаснуть.

Напишем собственную программу для ПК!

Этот вариант для настоящих энтузиастов, желающих программировать не только Freeduino, но и ПК. А почему бы и нет? Нам не потребуется изучать детали программирования последовательного порта под Windows, или еще какие-то сложные вещи. Специально для решения подобных простых задач существует язык Processing (http://processing.org), очень похожий синтаксисом и даже средой разработки на программное обеспечение Arduino.

Установите и запустите Processing – Вы увидите среду разработки, похожую на Arduino.

Исходный код программы для языка Processing есть в комментариях ниже основного текста примера Physical Pixel. Здесь он приведен с минимальными изменениями – мы исправили открытие порта, чтобы можно было легко заменить его номер:

import processing.serial.* ;
Serial port;
void setup()
{
size(200 , 200 ) ;
noStroke() ;
frameRate(10 ) ;
port = new Serial(this , "COM5" , 9600 ) ; // !!! Здесь прописать свой COM-порт!!!
}
boolean mouseOverRect() //Возвращает истину, если курсор внутри квадрата
{
return ((mouseX >= 50 ) && (mouseX <= 150 ) && (mouseY >= 50 ) & (mouseY <= 150 ) ) ;
}
void draw()
{
background(#222222 ) ;
if (mouseOverRect() ) // Если курсор внутри квадрата….
{
fill(#BBBBB0) ; // сменить цвет на поярче
port.write ("H" ) ; // послать "H" в микроконтроллер
} else { // если не внутри...
fill(#666660 ) ; // сменить цвет на потемнее
port.write ("L" ) ; // послать "L" в микроконтроллер
}
rect(50 , 50 , 100 , 100 ) ; // нарисовать квадрат
}

Запустите программу (через меню Sketch \ Run) – появится окно с квадратом, при помещении в который курсора мыши, будет загораться светодиод на Freeduino.

Описание языка Processing и его возможностей выходит за рамки этого простого повествования, но во многих примерах для Arduino в комментариях ниже основного текста программы представлен код Processing для ПК, взаимодействующий с Freeduino.

Коммуникация по последовательному порту , по умному называемая как универсальный асинхронный прием / передача (UART), как правило, используется для программирования и отладки Arduino через порт USB. Существуют разные датчики и приборы, которые используют UART как метод основной связи, и иногда нам нужно объединять два и больше Arduino между собой для обмена информацией.

Тем не менее, у большинства Arduino имеется только один последовательный порт, который используется при связи по USB. Но как же связать такой контроллер с другим? Конечно использование Arduino типа Mega или подобного решает эту задачу, ведь у него до четырех последовательных портов, но если нужна связь с простыми платами из линейки Ардуино, тут нужно искать другой выход. Существует особая программная библиотека, которая имитирует UART порт на других цифровых контактах. У нее имеются несколько недостатков, но в общем она работает.

Так что нам понадобится для демонстрации подобной коммуникации:

2 Arduino контроллера

Соединительные провода

Выполните следующие шаги, для подключения двух Arduino UNO, с помощью программного последовательного порта:

1. Например, воспользуемся выводами 8 и 9 для RX и TX на обоих Arduino, соедините контакт 8 на одном Arduino с контактом 9 на другом, и контакт 9 на первом с контактом 8 на втором.

2. Соедините общий провод GND обеих Arduino вместе.

3. Подключите один Arduino к USB компьютера, и соедините вывод 5В этого контроллера с таким же выводом другого или подайте на второй отдельное питание.

Вот реализация с использованием выводов 8 и 9 для RX и TX:



Следующий код разделен на две части. Arduino мастер будет получать команды от компьютера и передавать их по программному последовательному порту. Вот первая часть кода:

// Подключаем библиотеку Software Serial

#include

// Объявляем задействованные дискретные каналы контроллера для связи

Serial.begin(9600); // Обычная скорость передачи данных

softSerial.begin(9600); // инициализация программного последовательного порта

// Проверяем получение команд от компьютера

if (Serial.available()){

// Отправляем полученную команду компьютера на программный UART

А вот и код подчиненного (слейва), который интерпретирует символы, отправленные от мастера. Если пришол символ «а», он включит встроенный светодиод. Если получен символ «х», то светодиод будет потушен:

// Подключение библиотеки Software Serial

#include

// Назначение задействованных дискретных каналов

SoftwareSerial softSerial(8, 9); // RX, TX

// Дискретный канал, на котором висит встроенный светодиод

softSerial.begin(9600); // Инициализация программного последовательного порта

pinMode(LED, OUTPUT); // Определение светодиодного вывода как выход

// Проверяем, есть ли что-нибудь в буфере программного последовательного порта

if (softSerial.available()){

// Читаем один символ из буфера программного последовательного порта и сохраняем его переменную com

int com = softSerial.read();

// Действуем соответственно полученному символу

if (com == "x"){

// Выключение светодиода

digitalWrite(LED, LOW);

else if (com == "a"){

// Включение светодиода

digitalWrite(LED, HIGH);

Как это работает

Программный последовательный порт имитирует стандартный последовательный порт на различных цифровых выводах Arduino. Это довольно удобно в целом, но нужно понимать, что это программная имитация, не поддержанная аппаратно. Это означает, что при этом тратятся общие ресурсы контроллера, в частности время выполнения цикла программы и памяти. А вообще, оно работает просто как обычный последовательный порт. Все функции, присутствующие в нормальном последовательном порте также присутствуют и в программном.

Разбор кода

Для начала взглянем на программу мастера, который получает команды по обычному последовательному порту с компьютера и отправляет подчиненному контроллеру. В начале кода мы подключаем библиотеку SoftwareSerial.h

SoftwareSerial softSerial(8, 9); // RX, TX

При этом будет вызвана параллельная связь, в данном случае программная. Она будет использовать вывод 8 для чтения (RX) и вывод 9 для передачи (TX). Далее подробнее остановимся на том, какие именно выводы следует выбирать.

Используя объявленный объект библиотеки, мы можем использовать все функции, характерные для обычного аппаратного параллельного порта, такие как softSerial.read(), softSerial.write() и так далее. В следующей части кода мы проверяем пришло ли что-нибудь с аппаратного порта. И если что-то пришло, мы считываем это и отправляем в программный порт:

if (Serial.available()){

softSerial.write(Serial.read());

В коде подчиненного контроллера использована самая простая реализация управления светодиодом командами через последовательный порт с одной только разницей, что тут используются команды с программного порта. Меняется только синтаксис и вместо привычных функций Serial.read(), Serial.available() и так далее нужно писать softSerial.read() и softSerial.available().

Программный UART имеет некоторые важные ограничения и недостатки. Вот некоторые из них.

Использование выводов

Мы не можем использовать любые дискретные выводы плат Arduino для организации программного порта. Для Tx, вообще-то можем использовать любые, но для Rx можно использовать только те, которые поддерживают внешние прерывания. У плат Arduino Leonardo и Micro могут быть использованы только выводы 8, 9, 10, 11, 14, 15 и 16, в то время как у Mega или Mega 2560 могут быть использованы только выводы 10, 11, 12, 13, 50, 51, 52, 53, 62, 63, 64, 65, 66, 67, 68 и 69.

Другие программные параллельные коммуникации

Можно организовывать и более одной программной последовательной связи, однако одновременно данные может получать только одно соединение. Это может становиться причиной потери данных. Но существует альтернативная библиотека программного параллельного порта, написанная Полом Стофрегеном, которая призвана решить как раз данную проблему. http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html

Ему просто жизненно необходимо обмениваться с микроконтроллером информацией. Возникают ситуации, когда нужно вручную, управляя с ПК или ноутбука активировать ту или иную функцию в программе микроконтроллера.

Но давайте ближе к делу. Обмениваться данными с Arduino не так сложно, но вся загвоздка в том, что данные передаются посимвольно, а это очень плохо. В поисках этой проблемы пришлось провести достаточно долгое время, пока на хабрахабре не наткнулся на одну замечательную библиотеку. Автор реализовал в ней функцию приемки чисел, т.е. вы можете отправлять контроллеру числа, состоящие более, чем из одной цифры, и он все отработает корректно. Качайте библиотеку (ссылка), распаковывайте ее в hardwarelibraries, и переходим к практике.

Первым делом напишем скетч, и загрузим его в Arduino (Freeduino)

#include void setup() {

Serial.begin(9600); // устанавливаем скорость порта

PinMode(9, OUTPUT); // устанавливаем 9 ногу как выход для динамика

} void loop()

Long int Number; Serial.print(«Enter number: «);

Number = SerialInput.InputNumber(); // ВВодим число Serial.print(«Result = «);

Serial.println(Number * Number, DEC);

Beep(500);

} void beep(unsigned char delayms){

AnalogWrite(9, 20); // значение должно находится между 0 и 255

// поэкспериментируйте для получения хорошего тона

AnalogWrite(9, 0); // 0 — выключаем пьезо

Delay(delayms); // пауза delayms мс

Что все это значит. Постарался код снабдить подробными комментариями, вроде все должно быть понятно. Данный скетч просит ввести вас любое число, после чего выдает его квадрат, и воспроизводит звуковой сигнал через подсоединенный к 9 пину пьезо-динамик.

И вот, самое интересное — пришло время пробовать. Для коммутации с контроллером я рекомендую использовать бесплатную программу putty . В настройках Connection type выберите Serial и вместо COM1 впишите корректный номер порта (можно подглядеть в среде программирования Arduino меню Tools->Serial Port). Нажимаем Open, и видим в консоли надпись Enter number, вводим любое число (в рамках разумного), жмем Enter, и видим результат.

Все, можно радоваться и прыгать от радости. Естественно все это можно улучшить, например сперва вывести отправить с контроллера в консоль менюшку, в которой подробно расписать команды. Например вводите число 0 — включается светодиодная подсветка, нажимаете 1 — гаснет. Таким образом можете хоть 100500 команд засунуть, лишь бы хватило памяти микроконтроллера (которой так мало). А о том, как расширить доступную память поговорим в следующий раз.

UPD: часть кода порезал парсер движка, поэтому вот исходник