Узнайте, как программировать Arduino

Несколько дней назад я понял, что не успеваю многое сделать, когда был дома. Решив восстановить свое выступление, я решил, что начну использовать Технику Помидора. Была только одна проблема: у меня не было Tomato Timer для моих сессий. Я мог бы использовать приложение / веб-сайт, но экраны - лучший источник отвлечения внимания. Я мог бы купить один, но давайте посмотрим правде в глаза: делать своими руками намного веселее (и помогает мне избежать реальной работы).

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

Независимо от вашего уровня мастерства, я надеюсь предложить вам кое-что в этой статье. Мы рассмотрим основные операции Arduino (запись, чтение, последовательный порт, задержки) и некоторые промежуточные концепции, такие как прерывания и код без задержки. Я дам ссылки на соответствующие концепции, которые могут вас заинтриговать, но вам необязательно знать их все, чтобы следовать по тексту. Статья предполагает базовые знания программирования, предпочтительно C, и базовые знания схем. Тем не менее, не стесняйтесь прыгать по разделам в соответствии с вашими навыками / интересами!

Весь код статьи находится в моем репозитории GitHub здесь: https://github.com/Psyf/Pomoduino. Детали, используемые в этом проекте, очень простые, и вы можете найти их в любом магазине электротехники / оборудования / товаров для любителей (/ в Интернете)

Схема

На схеме ниже показано, как вы должны соединить все вместе. Если вам нужно вспомнить, как работают макетные схемы, перейдите по этой ссылке: https://learn.sparkfun.com/tutorials/how-to-use-a макетная плата .

Использование: мы собираемся использовать 3 переменных резистора, чтобы настроить время учебы / работы - как долго будет длиться каждый учебный сеанс, как долго длится каждый перерыв и сколько сеансов мы хотим всего. Сопротивление - это то, что определяет то, что читает наш Arduino, и значения можно настроить, используя что-то вроде отвертки. (Если кому-то интересно, я предпочитаю 35 минут занятий, 10 минут перерыва и всего 3 занятия.)

Светодиоды образуют наш «экран» и сообщают нам, в какой момент учебной сессии мы находимся - гаснущий красный для учебы, постоянный зеленый для перерыва. Пьезо-динамик действует как будильник, поэтому мы можем сосредоточиться на работе, а не на освещении. У нас также есть удобная маленькая красная кнопка RESET на Arduino для «перезапуска» после того, как мы закончим один проход. На данный момент Arduino будет получать питание от USB-порта нашего ноутбука / настольного компьютера (мы рассмотрим альтернативные варианты питания позже в этой статье).

Пояснение: Красные провода обозначают питание (в данном случае 5 В), а черные провода условно обозначают соединение с землей (GND или 0 В). Цветовая маркировка проводов - всегда отличная идея.

Голубые провода - это входы от реостата (переменного резистора), идущего к контактам A3, A4 и A5 (A для аналогового). Светодиоды подключены к резисторам (для ограничения протекающего тока, иначе они лопнут) на одном конце и к цифровым выводам на другом. Позаботьтесь о правильном их подключении: более короткая ножка является отрицательной клеммой и подключается к резистору. Если вы будете очень резкими, вы заметите, как все красные светодиоды подключены к контактам со знаком ~ рядом с ними. Подробнее об этом позже.

Код

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

Мы будем кодировать в Arduino IDE, которая позаботится о многих низкоуровневых вещах (например, об импорте библиотек) в фоновом режиме, что сделает написание фактического кода очень увлекательным и легким.

Теперь основная часть вашего кода Arduino состоит из двух функций: setup () и loop (). Первая функция запускается один раз, чтобы, как вы уже догадались, настроить регистры внутри Arduino. Вторая функция - это то место, куда мы помещаем логику, чтобы работать в бесконечном цикле. Остальной код должен быть простым, процедурным на языке C и достаточно простым для понимания:

Если вы впервые играете с Arduino, это сразу нужно понять. Позвольте мне провести вас через это.

Мы начнем с нескольких директив препроцессора C, потому что это лучшая практика программирования и облегчает вашу жизнь, особенно при смене пинов. Мы также используем несколько глобальных переменных для хранения выводов светодиодов и состояния программы. Неиспользуемая переменная pause подскажет, что будет в следующих разделах;)

Теперь перейдем к функции setup (). pinMode - это функция, которая принимает номер контакта и устанавливает для него значение OUTPUT или INPUT, в зависимости от того, хотите ли вы использовать этот контакт для работы устройств (светодиоды и пьезо) или чтения с датчиков (здесь реостаты). Мы также настроили Последовательную связь, также известную как UART / USART, позвонив Serial.begin() со скоростью 9600 бод. Arduino может связываться с вашим ноутбуком (отправлять / получать данные) через порт USB или контакты RX (0) / Техас (1). Вот почему мы не используем контакты 0 и 1 в нашей схеме. Все эти функции устанавливают несколько байтов / регистров, чтобы оборудование было готово. Вы также можете сделать это на голом железе, и мы расскажем об этом в следующем руководстве.

Затем мы переходим к функции loop (). Вы можете думать о цикле как о while (1) - бесконечном цикле. Чтобы противодействовать этому, мы используем переменную over, которая обращается в 1, когда мы заканчиваем нашу учебную сессию. Затем мы вызываем некоторые функции, которые я сделал, а именно getSessTime, getNumSess and getBreakTime, которые имеют довольно очевидные имена и содержат похожий код. Все они используют функцию analogRead для чтения с аналоговых выводов (с префиксом A, если вы еще помните). Возвращаемое значение analogRead - от 0 (0 В) до 1023 (5 В), масштабируется линейно. Таким образом, мы можем изменить сопротивление потенциометров, чтобы изменить продолжительность программ в соответствии с нашими потребностями. Мы печатаем значения, которые собираемся использовать с Serial.println(). Вы можете увидеть значения, напечатанные в Serial Monitor (открываемом с помощью Ctrl + Shift + M).

Другие функции hold() и fadeAndHold() более интересны. Удержание используется для включения зеленых светодиодов во время перерывов. Он также ждет внутри функции в течение x минут, где x - это значение, которое мы указали. Давайте посмотрим на код блокировки:

// Any output pin can use this
// duration in minutes
void hold(int led, int duration) {    
    unsigned long targetTime = millis() + duration*1000*60;
    digitalWrite(led, HIGH);
    while (millis() < targetTime) continue; 
}

digitalWrite() принимает номер контакта и записывает дискретные HIGH (5V) или LOW (GND or 0V) на контакты. millis() дает нам время с момента запуска Arduino, и мы можем использовать это, чтобы подождать (в цикле while) в течение duration времени (которое было масштабировано с минут на миллисекунды, если вы заметили уравнение). Фактически мы можем написать ту же функцию, что и:

void hold(int led, int duration) {
    digitalWrite(led, HIGH); 
    delay(duration*1000*60); 
}

delay() делает то же самое. Так почему бы мне не выбрать более «чистый» код? Это потому, что delay() блокирует все остальные операции (включая внешние прерывания), поскольку Arduino имеет только одно ядро ​​/ поток. С другой стороны, millis() не блокирует прерывания, и мы можем использовать это, чтобы приостановить наш таймер в последней части статьи.

fadeAndHold() еще интереснее. Он использует функцию analogWrite(). Помните знаки ~ на каждом красном выводе светодиода? Эта тильда говорит нам, что выводы могут выводить аналоговый сигнал (значения от 0 В до 5 В), типа . Это позволяет нам управлять яркостью красных светодиодов. В коде мы увеличиваем яркость каждые 5 миллисекунд на 1 (0 представляет 0 В и 255 представляет 5 В), а затем, когда она достигает максимума, мы уменьшаем на 1. Достаточно стандартное затухание и появление. Как это делает Arduino? Он подделывает аналоговый выход с помощью техники, называемой широтно-импульсной модуляцией (сокращенно ШИМ). Вы можете узнать больше о том, как это делается с помощью переменных рабочих циклов, по предоставленной ссылке, или следите за моей статьей о ШИМ.

Остальная часть функции проста, если вы следовали ее указаниям. Мы продолжаем гаснуть светодиод, пока не истечет время для учебного сеанса, а затем держим его постоянно на ВЫСОКОМ уровне, чтобы показать, сколько сеансов мы завершили.

Функция tone() предоставляется библиотекой для пьезо. Он принимает в качестве 3 аргументов pin, frequency_of_sound и duration. Мы используем это для слуховой обратной связи после перерывов и после них.

Напишите код в среде Arduino IDE, выберите правильно port и board и нажмите "Загрузить". Если все пойдет хорошо, теперь у вас есть рабочий таймер Помидора. Итак, теперь вы можете настроить продолжительность, включить Arduino с помощью USB-кабеля и наслаждаться. Вы можете нажать кнопку RESET на Arduino, чтобы перезапустить программу.

Прерывания

До сих пор мы могли только СБРОСИТЬ нашу программу, но не ПРИОСТАНОВИТЬ ее. Лично мне нравится делать паузу, если я хочу пойти в туалет или что-то в этом роде. Как мы это реализуем? Очевидно, мы можем добавить переключатель, который мы можем продолжать читать после каждой второй инструкции. Но это было бы очень наивным подходом, сделавшим код неуклюжим и уродливым (и сведя на нет смысл этого раздела). Лучшим вариантом будет Прерывания.

Думайте о прерываниях как о вызовах службы экстренной помощи. Каждый раз, когда Arduino запускает / обнаруживает прерывание, он останавливает все, что делает, и обрабатывает прерывание, как описано в Процедуре обслуживания прерывания (сокращенно ISR). Звучит модно, но это всего лишь еще одна функция. Мы должны сделать эту функцию короткой, простой и быстрой, чтобы она могла быстро справиться с «чрезвычайной ситуацией» и вернуться к исходному потоку программы. Мы добавим переключатель для прерывания, как показано на измененной принципиальной схеме ниже. Остальная часть схемы осталась нетронутой.

Arduino имеет «внешние» прерывания и прерывания «со сменой вывода». Вы можете выполнять внешние прерывания только с 2 контактами, INT0 и INT1 (контакты 2 и 3 соответственно). Смена контактов может происходить на всех контактах, но мы сосредоточимся на «внешних» прерываниях, поскольку смена контактов немного сложнее и заслуживает отдельной статьи.

Итак, теперь, когда вы понимаете, почему мы выбрали контакт 2 для входа переключателя, мы можем двигаться дальше. Если вам интересно, мы не выбрали контакт 3, потому что в библиотеке Piezo указано, что контакты 3 и 11 должны оставаться свободными. Откуда я знаю? Я прочитал документацию.

Теперь посмотрим на изменения в коде. Очевидно, у нас есть новый #define для переключателя. В нашей функции setup () у нас также есть эта строка attachInterrupt(digitalPinToInterrupt(START_BUTTON_PIN), pause_ISR, HIGH); Она сообщает Arduino, что START_BUTTON_PIN должен рассматриваться как контакт прерывания и что каждый раз, когда он читает HIGH, мы должны вызывать функцию pause_ISR() Давайте посмотрим, pause_ISR () просто поворачивает флаг pause в 1 и вызывает функцию pause_Exec(), которая в основном простаивает (используя бесконечный цикл), пока мы снова не щелкнем выключателем. Обратите внимание, что это противоречит хорошей практике прерывания. Прерывания должны быть быстрыми, а не блокирующими, как сейчас. Но поскольку это простой пример, нам этого вполне достаточно.

Именно из-за этого прерывания мы не использовали delay () нигде в нашем коде. Видите ли, когда мы вызываем delay (), даже прерывания не могут их прервать. Так что, если бы вам довелось щелкнуть переключателем за миллисекунды, в которых программа находилась в delay () (что вполне вероятно, учитывая, как часто мы вызывали бы это), вы бы пропустили выполнение ISR, и программа продолжала бы работать, как если бы ничего не произошло. Полный код приведен ниже для справки:

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

Будущие улучшения

  1. Прямо сейчас мы можем только настроить время до начала работы схемы и проверить настройку с помощью последовательного монитора. Можете ли вы придумать элегантное решение проблемы настройки продолжительности во время работы? Я оставлю это как упражнение и буду ждать решений, которые вы опубликуете в комментариях ниже.
  2. До сих пор мы питали наши проекты через USB-кабель, но это не очень удобно для такого простого устройства, как это. Можем ли мы снизить энергопотребление схемы и запитать ее от маленькой батареи? Мы можем использовать довольно много приемов, от простых до довольно сложных. Мы скоро рассмотрим этот интересный аспект в следующей статье.
  3. Еще одно улучшение - подключить это к Интернету, отправить данные на сервер и провести некоторый анализ данных, прежде чем показывать красивые графики для отслеживания времени / закономерностей нашего исследования.
  4. Еще одно улучшение - воспроизведение какой-то музыки во время работы программы. Сэмплы пьезо музыки доступны в Интернете. Сложность - это многопоточность программы с использованием библиотек. Я не знаю, смогу ли я сделать это удовлетворительно, но полагаю, что мне просто нужно будет попробовать после прохождения курса Операционная система реального времени в следующем семестре. Следите за обновлениями!

Надеюсь, вы убрали что-то ценное из этого руководства. Если вы впервые играете с Arduino, поздравляем с началом вашего пути! Вы можете поиграть с примерами кода в среде Arduino IDE или посетить веб-сайт Arduino, чтобы узнать больше. Следите за другими связанными с Arduino статьями, а также о Dabbler in Destress.

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