Связь нескольких ардуино по проводу

Синтаксис

  • Тела функций заключаются в фигурные скобки
  • Каждая команда заканчивается точкой с запятой
  • Метод применяется к объекту через точку. Пример:
  • Вызов функции или метода всегда заканчивается скобками, даже если функция не принимает параметров. Пример:
  • Разделитель десятичных дробей – точка. Пример: У запятой тут другое применение
  • Запятыми перечисляются аргументы функций и методов, а также членов массива. Пример: массив – Также запятая является самостоятельным оператором, но об этом поговорим отдельно в другом уроке
  • Одиночный символ заключается в одиночные кавычки
  • Строка и массив символов заключается в двойные кавычки
  • Имена переменных могут содержать латинские буквы в верхнем и нижнем регистре (большие и маленькие), цифры и нижнее подчеркивание. Пример: .
  • Имена переменных не могут начинаться с цифры. Только с буквы или нижнего подчёркивания
  • Регистр имеет значение, т.е. большая буква отличается от маленькой. Пример: переменные и – не одно и то же.

К синтаксису также можно отнести комментарии, т.к. в разных языках они выделяются по-разному. Комментарий это обычный текст, который игнорируется на этапе компиляции. Комментарии нужны для пояснения кода, как себе самому, так и другим возможным его читателям. В C++ у нас два типа комментариев:

  • Однострочный комментарий
    // однострочный комментарий
    // компилятор меня игнорирует =(
  • Многострочный комментарий
    /* Многострочный
    комментарий */

Резюмируя

Теперь по сути дела: датчики, их куча кучная, измерять можно ну просто всё, что вообще измеряется. Электроника: напряжение, ток, сопротивление, работа с переменным током, поля. Параметры микроклимата: температура, влажность, давление, содержание газов, скорость ветра, освещенность, что угодно. Интересных модулей тоже очень много: Bluetooth, сотовая связь, GPS, дисплеи различных типов и размеров, датчики присутствия, как ИК, так и микроволновые, модули для беспроводной связи ардуинок и многое другое.

Можно управлять абсолютно любой железкой, которая выполняет свою функцию просто при подаче питания: лампочка, светодиодная лента, электронагреватель, мотор или любой электропривод, электромагнит, соленоид-толкатель, и это все с любым напряжением питания. Но тут нужно кое что понять: Ардуино (точнее микроконтроллер) – логическое устройство, то есть по-хорошему она должна только отдавать команды другим устройствам, или принимать их от них. Это я к тому, что напрямую от ардуино не работают ни лампочки, ни моторчики, ни нагреватели, ни-хуче-го. Максимум – светодиод. С пониманием этого идём дальше. Чтобы ардуино включила или выключила (подала питание) на другое устройство, нужно устройство – посредник, например реле или транзистор. Ардуино управляет реле, а реле в свою очередь включает любую нужную нагрузку с любым напряжением питания и все такое, подробнее об этом поговорим отдельно.

Как суть всего выше написанного – возможности Ардуино по подключению и управлению различными железками практически безграничны, можно воплотить любую идею, даже самую безумную. Датчики что то измеряют, исполнительные устройства что то контролируют, в это же время ведётся отсылка данных куда-нибудь, что-то отображается на дисплее и контролируется при помощи кнопок. Романтика!

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

Краткое описание

Функция  begin() является методом класса Serial. Для работы с этим классом не нужно подключать внешних библиотек, т.к.  он встроен в среду разработки ардуино. Использовать Serial.begin() целесообразно один раз, в самом начале работы скетча, в функции void setup(). В качестве аргументов нужно указать скорость обмена данными через последовательный порт. Самым распространенным значением является 9600 – именно такая скорость обмена данными в мониторе порта Arduino IDE стоит по умолчанию. После выполнения функции ардуино будет способна как отправлять данные на внешние устройства, так и получать их.

Пример использования функции:

void setup(){
 Serial.begin(9600);
}

Другие популярные методы класса Serial:

  • print()
  • println()
  • read()
  • available()
  • parseInt()

На платах с несколькими «железными» последовательными портами, например, Arduino Mega, для каждого из них создается свой объект Serial, поэтому вместе с Serial могут встречаться вызовы объектов Serial1, Serial2, Serial3.

Питание от USB

Питание от USB – самый плохой способ питания ардуино-проекта. Почему? По линии питания +5V от USB стоит диод, выполняющий защитную функцию: он защищает порт USB компьютера от высокого потребления тока компонентами ардуино-проекта или от короткого замыкания (КЗ), которое может произойти по случайности/криворукости любителей ковырять макетные платы. КЗ продолжительностью менее секунды не успеет сильно навредить диоду и всё может обойтись, но продолжительное замыкание превращает диод в плавкий предохранитель, выпускающий облако синего дыма и спасающий порт компьютера от такой же участи.

К слову, ардуинки от производителя Robotdyn имеют самовосстанавливающийся предохранитель вместо такого костыля с диодом-смертником.

Слаботочный диод имеет ещё одну неприятную особенность: на нём падает напряжение, причем чем больше ток потребления схемы, тем сильнее падает напряжение питания. Пример: голая ардуина без всего потребляет около 20 мА, и от 5 Вольт на юсб после диода нам остаётся примерно 4.7 Вольт. Чем это плохо: опорное напряжение при использовании АЦП крайне нестабильно, не знаешь, что измеряешь (да, есть способ измерения опорного напряжения, но делать это нужно вручную). Некоторые железки чувствительны к напряжению питания, например LCD дисплеи: при питании от 5V они яркие и чёткие, при 4.7 вольтах (питание от юсб) они уже заметно теряют яркость. Если подвигать сервоприводом или включить реле – на диоде упадет ещё больше и дисплей практически погаснет. При коротких мощных нагрузках (выше 500-600ма) микроконтроллер перезапустится, так как напряжение упадет ниже плинтуса.

Вы наверное предложите заменить диод перемычкой, чтобы питать схему от USB большим током, например от powerbank’а. Так делать тоже нельзя, потому что дорожки на плате не рассчитаны на большие токи (дорожка 5V очень тонкая и идёт через всю плату). Я думаю, что можно будет снять 1-2 Ампера с пина 5V, но, скорее всего, напряжение просядет. Также при КЗ вы скорее всего попрощаетесь с дорожкой вообще. Питайте силовую часть схемы либо отдельно, либо от того же источника питайте Arduino.

Метод tick()

Метод занимается отправкой и приёмом данных по шине, то есть измеряет время и слушает/дёргает пин, что обеспечивает “асинхронную” работу интерфейса. Вызывать тик нужно как можно чаще, желательно как минимум в 4 раза чаще, чем микросекунд. То есть например для скорости 300 это будет микросекунд. Можно оставить его в и обеспечить его “прозрачное” выполнение без задержек, либо дополнительно положить в прерывание таймера с таким периодом. Логика работы автоматически переключается в зависимости от текущего режима: во всё время кроме отправки мы “слушаем” шину.

Также возвращает текущее состояние шины.

Не используйте мышку!

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

  • Автоформатирование – Arduino IDE умеет автоматически приводить ваш код в порядок (имеются в виду отступы, переносы строк и пробелы). Для автоматического форматирования используйте комбинацию CTRL+T на клавиатуре, либо Инструменты/АвтоФорматирование в окне IDE. Используйте чаще, чтобы сделать код красивым (каноничным, классическим) и более читаемым для других!

  • Скрытие частей кода – сворачивайте длинные функции и прочие куски кода для экономии места и времени на скроллинг. Включается здесь: Файл/Настройки/Включить сворачивание кода

  • Не используйте мышку! Чем выше становится ваш навык в программировании, тем меньше вы будете использовать мышку (да-да, как в фильмах про хакеров). Используйте обе руки для написания кода и перемещения по нему, вот вам несколько полезных комбинаций и хаков, которыми я пользуюсь ПОСТОЯННО:

    • Ctrl+← , Ctrl+→ – переместить курсор влево/вправо НА ОДНО СЛОВО
    • Home , End – переместить курсор в начало/конец строки
    • Shift+← , Shift+→ – выделить символ слева/справа от курсора
    • Shift+Ctrl+← , Shift+Ctrl+→ – выделить слово слева/справа от курсора
    • Shift+Home , Shift+End – выделить все символы от текущего положения курсора до начала/конца строки
    • Ctrl+Z – отменить последнее действие
    • Ctrl+Y – повторить отменённое действие
    • Ctrl+C – копировать выделенный текст
    • Ctrl+X – вырезать выделенный текст
    • Ctrl+V – вставить текст из буфера обмена

    Местные сочетания:

    • Ctrl+U – загрузить прошивку в Arduino
    • Ctrl+R – скомпилировать (проверить)
    • Ctrl+Shift+M – открыть монитор порта

    Также для отодвигания комментариев в правую часть кода используйте TAB, а не ПРОБЕЛ. Нажатие TAB перемещает курсор по некоторой таблице, из-за чего ваши комментарии будут установлены красиво на одном расстоянии за вдвое меньшее количество нажатий!

Плоттер

Помимо монитора последовательного порта, в Arduino IDE есть плоттер – построитель графиков в реальном времени по данным из последовательного порта. Достаточно отправить значение при помощи команды Serial.println(значение) и открыть плоттер по последовательному соединению, например построим график значения с аналогового пина A0:

Плоттер поддерживает несколько линий графиков одновременно, для их отображения нужно соблюдать следующий протокол отправки данных: значение1 пробел_или_запятая значение2 пробел_или_запятая значение3 пробел_или_запятая значениеN перенос_строки, то есть значения выводятся в одну строку, одно за другим по порядку, разделяются пробелом или запятой, и в конце обязательно перенос строки. Давайте выведем несколько случайных величин:

Вывод значений происходит каждые 10 миллисекунд, а каждые 300 миллисекунд значения обновляются. Получаем вот такой график:

Подписи графиков

В Arduino IDE с версии 1.8.10 добавили возможность подписать графики, для этого перед выводом нужно отправить названия в виде название 1, название 2, название n с переносом строки, и дальше просто выводить данные:

Светильник с управляемой яркостью Ардуино

Для этого занятия нам потребуется:

  • плата Arduino Uno / Arduino Nano / Arduino Mega;
  • макетная плата;
  • потенциометр;
  • 1 светодиод и резистор 220 Ом;
  • провода «папа-папа».

На этом занятии мы соберем электрическую схему светильника со светодиодом с управляемой яркостью. С помощью потенциометра мы сможем плавно изменять яркость светодиода, подключенного к пину 9. Потенциометр подключается крайними ножками к портам 5V и GND, со средней ножки снимается значение напряжения и подключается к аналоговому входу A0 микроконтроллера Ардуино.


Схема сборки светильника с управляемой яркостью

Соберите электрическую цепь, как на рисунке. Средняя ножка переменного резистора подключается к аналоговому порту A0, чтобы снимать показания напряжения. Какую из крайних ножек подключить к портам 5V и GND значения не имеет, изменится лишь направление вращения ручки потенциометра для увеличения яркости светодиода. После сборки схемы, подключите Arduino к компьютеру и загрузите скетч.

Скетч для Ардуино и потенциометра

// Присваиваем имя для пина со светодиодом (англ. «led»)
#define LED_PIN 9

// Присваиваем имя для пина с потенциометром (англ. «potentiometer»)
#define POT_PIN A0

void setup()
{
  // пин со светодиодом будет выходом (англ. «output»)
  pinMode(LED_PIN, OUTPUT);

  // пин с потенциометром будет входом (англ. «input»)
  pinMode(POT_PIN, INPUT);
  
  // Запускаем монитор последовательного порта
  // снимите комментарий // Serial.begin(9600);
}

void loop()
{
  // заявляем, что будем использовать 2 переменные - rotation и brightness
  // хранить в переменных будем только целые числа (англ. «integer»)
  int rotation, brightness;  

  // rotation равна значениям с потенциометра в интервале от 0 до 1023
  rotation = analogRead(POT_PIN);

  // переменная brightness будет равна rotation делённое на 4
  // brightness может быть только целым числом, дробная часть будет отброшена
  // в итоге переменная brightness будет находится в пределах от 0 до 255
  brightness = rotation / 4;

  // выдаём напряжение, рассчитанное по формуле brightness = rotation / 4
  analogWrite(LED_PIN, brightness);
  
  // Выдаем значение rotation на монитор последовательного порта
  // снимите комментарий // Serial.println(rotation);
  // снимите комментарий // delay(1000);
}

Ожидание ответа (v1.1)

Метод работает по такой же логике, как предыдущий, но не блокирует выполнение кода. Логика такая: вручную отправляем реквест и при помощи можем дождаться ответа и попытаться достучаться до приёмника, автоматически отправляя новые запросы через таймаут миллисекунд. Количество попыток ограничено заданным. Метод возвращает статусы:

Код Название Описание
Ничего не делаем
1 Ждём ответа
2 Ответ не получен
3 Получено подтверждение
4 Получены данные

Примеры находятся в examples/GBUS/wait_ack. В примере wait_ack_rx можно закомментировать отправку ответа и в мониторе порта увидеть, как передатчик отправляет несколько запросов перед тем, как выдать ошибку.

#if – условная компиляция

Условная компиляция является весьма мощным инструментом, при помощи которого можно вмешиваться в компиляцию кода и делать его очень универсальным как для пользователя, так и для железа. Рассмотрим директивы условной компиляции:

  • – если
  • – если определено
  • – если не определено
  • – иначе
  • – иначе если
  • – конец условия
  • – проверка, определён ли

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

#define USE_DISPLAY 1 // настройка для пользователя

#if (USE_DISPLAY == 1)
#include <библиотека дисплея.h>
#endif

void setup() {
#if (USE_DISPLAY == 1)
  // дисплей.инициализация
#endif

}
void loop() {
}
#define SENSOR_TYPE 3   // настройка для пользователя

// подключение выбранной библиотеки
#if (SENSOR_TYPE == 1 || SENSOR_TYPE == 2)
#include <библиотека сенсора 1 и 2.h>
#elif (SENSOR_TYPE == 3)
#include <библиотека сенсора 3.h>
#else <библиотека сенсора 4.h>
#endif
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// код для ATmega1280 и ATmega2560
#elif defined(__AVR_ATmega32U4__)
// код для ATmega32U4
#elif defined(__AVR_ATmega1284__)
// код для ATmega1284
#else
// код для остальных МК
#endif

Условные директивы #if #else

Помимо директивы , сообщающей препроцессору о необходимости замены набора символов набором символов, есть ещё условные директивы, позволяющие заниматься так называемой условной компиляцией: обладая такой же логикой, как if-else, данные конструкции позволяют делать некоторый выбор перед компиляцией самого кода. Отличным примером является само “ядро” Ардуино – большинство функций написаны со спецификой каждого процессора, и перед компиляцией кода из множества вариантов реализации функции выбирается тот, который соответствует текущему выбранному микроконтроллеру. Проще говоря, условная компиляция позволяет по условиям включать или исключать тот или иной код из основной компиляции, т.е. сначала препроцессор анализирует код, что-то в него включается, что-то нет, и затем проходит компиляция.

Также например мы не можем объявить какую-либо константу или макро через более одного раза, это приведёт к ошибке. Условная компиляция позволяет сделать ветвящуюся конструкцию, где такое возможно. Для условной компиляции нам доступны директивы , , , , ,

  • – аналог в логической конструкции
  • – аналог в логической конструкции
  • – аналог в логической конструкции
  • – директива, завершающая условную конструкцию
  • – если “определено”
  • – если “не определено”
  • – данный оператор возвращает если указанное слово “определено” через , и – если нет. Используется для конструкций условной компиляции.

Как ими пользоваться давайте посмотрим на примере:

#define TEST 1    // определяем TEST как 1
#if (TEST == 1)   // если TEST 1
#define VALUE 10  // определить VALUE как 10
#elif (TEST == 0) // TEST 0
#define VALUE 20  // определить VALUE как 20
#else             // если нет
#define VALUE 30  // определить VALUE как 30
#endif            // конец условия

Таким образом мы получили задефайненную константу , которая зависит от “настройки” . Конструкция позволяет включать или исключать куски кода перед компиляцией, вот например кусочек про отладку:

#define DEBUG 1

void setup() {
#if (DEBUG == 1)
  Serial.begin(9600);
  Serial.println("Hello!");
#endif
}

Таким образом при помощи настройки можно включить или исключить любой кусок кода.

Есть у препроцессора ещё две директивы: и , они позволяют включать или исключать участки кода по условию: – определено ли? – не определено ли? Определено или не определено – речь идёт конечно же о

#define TEST      // определяем TEST 

#ifdef TEST       // если TEST определено 
#define VALUE 10  // определить VALUE как 10 
#else             // если закоммент. #define TEST
#define VALUE 20  // определить VALUE как 20 
#endif            // конец условия

Именно на условной компиляции строится вся универсальность библиотек для Arduino, ведь при выборе платы автоматически “создаётся” дефайн на название микроконтроллера, выглядят они так:

  • И далее в этом стиле

Это позволяет создавать универсальный код, используя конструкцию с или :

#if defined(__AVR_ATmega32U4__)
// код для Leonardo/Micro/Pro Micro

#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// код для Mega (1280 или 2560)

#elif defined(__AVR_ATmega328P__)
// код для UNO/Nano/Pro Mini

#endif

Таким образом микроконтроллерам (платам Arduino) разных моделей будет доступен персональный кусок кода, который будет передан компилятору при выборе этой платы из списка плат.

Читай продвинутый урок по директивам препроцессора, если хочешь узнать больше!

Оцените статью
Рейтинг автора
5
Материал подготовил
Илья Коршунов
Наш эксперт
Написано статей
134
Добавить комментарий