Съдържание:

Меню в Arduino и как да използвате бутони: 10 стъпки (със снимки)
Меню в Arduino и как да използвате бутони: 10 стъпки (със снимки)

Видео: Меню в Arduino и как да използвате бутони: 10 стъпки (със снимки)

Видео: Меню в Arduino и как да използвате бутони: 10 стъпки (със снимки)
Видео: Control Position and Speed of Stepper motor with L298N module using Arduino 2024, Декември
Anonim
Меню в Arduino и как да използвате бутони
Меню в Arduino и как да използвате бутони

В моя урок за Arduino 101 ще бъдете научени как да настроите вашата среда в Tinkercad. Използвам Tinkercad, защото това е доста мощна онлайн платформа, която ми позволява да демонстрирам набор от умения на учениците за изграждане на схеми. Чувствайте се свободни да изградите всички мои уроци, използвайки Arduino IDE и истински Arduino!

В този урок ще научим за бутоните! Трябва да знаем:

  • Как да ги свържете
  • Четенето на тяхната стойност
  • Дебаунс и защо е важно
  • Практическо приложение (създаване на меню)

Повечето хора смятат, че най -практичното нещо с бутон е да включвате и изключвате лампата. Ще, не тук! Ще използваме нашия, за да създадем меню и да зададем някои опции на Arduino.

Готов? Да започваме!

Стъпка 1: Настройте дъската

Настройте дъската
Настройте дъската
Настройте дъската
Настройте дъската

Първата стъпка е да поставите Arduino и Breadboard Small върху зоната за прототипиране. Проверете изображенията по -горе, за да видите как да свържете електрическите шини.

Breadboard Mini има две релси за захранване отгоре и отдолу. Ние ги свързваме към Arduino, за да можем да осигурим захранване на повече компоненти. По -късно в този урок ще използваме 3 бутона, така че ще имаме нужда от повече енергия. Нещото, което трябва да се отбележи, е, че на малка дъска захранващите релси се движат по дъската, хоризонтално. Това е различно от колоните в основната зона за прототипиране в средата; те вървят вертикално. Можете да използвате всеки от щифтовете за захранване, за да осигурите захранване на всяка колона в основната област в средата.

Когато добавяте захранване, използвайте черни и червени проводници съответно към отрицателния и положителния. Добавете проводници в края, които захранват захранването от другата страна на платката. Няма да използваме тази страна, но това е добра практика.

Стъпка 2: Добавете бутона и резистора

Добавете бутона и резистора
Добавете бутона и резистора
Добавете бутона и резистора
Добавете бутона и резистора
Добавете бутона и резистора
Добавете бутона и резистора

Добавете малък бутон от тавата за компоненти. Тя трябва да изглежда като тази на изображението. Уверете се, че това не е превключвател! Добавете и резистор. Щракнете върху него и задайте стойността му на 10kΩ. Това е достатъчно, за да издърпате щифта ниско, когато не е свързан, което е много важно по -късно в кода.

Поставете компонента в средата на дъската. Начинът, по който работи един бутон, е:

  • От ъгъл до ъгъл, бутонът не е свързан. Натискането на бутона затваря контактите и свързва ъглите.
  • Страните на бутона са свързани. Ако свържете проводник в горния ляв и долния ляв ъгъл, веригата ще се затвори.

Ето защо поставяме компонента в пространството в средата. Той гарантира, че ъглите не са свързани под щифтовете в дъската.

Следващата стъпка предоставя няколко изображения, които илюстрират тези точки.

Поставете резистора от долния десен щифт през колони, така че да седи хоризонтално.

Стъпка 3: Връзки с бутони

Връзки с бутони
Връзки с бутони
Връзки с бутони
Връзки с бутони

Изображенията по -горе ясно показват как бутоните се свързват. Винаги е било объркване, когато мислиш, че всичко е наред и не работи!

Сега, нека добавим проводниците.

  • Поставете червен проводник от положителен захранващ щифт към същата колона като долния десен щифт на бутона
  • Поставете черен проводник от отрицателен захранващ щифт към същата колона като резистора.
  • Поставете цветен проводник (не червен/черен) от горния ляв щифт към Digital Pin 2 на Arduino

Проверете изображенията по -горе, за да се уверите, че окабеляването ви е правилно.

Стъпка 4: Кодът …

Кодът…
Кодът…
Кодът…
Кодът…

Нека да разгледаме кода за основен бутон.

Отворете редактора на кодове и променете от Блокове на Текст. Изчистете предупреждението, което се появява. Доволни сме от текста!

Знаете основната настройка, така че нека да дефинираме бутона и да направим основно четене. Ще отпечатаме изхода на Serial.

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

// Определете константи

#define button 2 void setup () {pinMode (button, INPUT); Serial.begin (9600); } void loop () {// Прочетете цифровия щифт, за да проверите състоянието на натиснат бутон int = digitalRead (бутон); // Бутонът връща HIGH, ако е натиснат, LOW, ако не, ако (натиснат == HIGH) {Serial.println ("Натиснат!"); }}

Добре, това работи!

По същество всичко, което правим, е да проверяваме състоянието на цифровия пин всеки път, когато кодът се затвори. Ако щракнете върху Стартиране на симулацията и натиснете бутона, ще видите серийния монитор (щракнете върху бутона под кода) на дисплея "Натиснат!" многократно.

Една функция, която ще видите в горния код, е оценката на състоянието if (). Всичко, което прави кодът, е да задава въпрос и да преценява дали е вярно, в този случай. Използваме знака за равенство (двойни знаци за равенство, като този: ==), за да проверим дали стойността на променливата е равна на определена стойност. DigitalRead () връща HIGH или LOW.

Използвайки if () else if / else можем да проверим много условия или всички условия и ако се върнете към основите на Arduino, ще видите някои от сравненията, които можете да направите.

Сега … Нашият код може да изглежда завършен … Но имаме проблем.

Вижте, това работи много добре, когато сте в симулатора. Но истинското електричество има шум, особено електрониката с постоянен ток. Така че нашият бутон може понякога да върне фалшиво отчитане. И това е проблем, защото вашият проект може да не отговори по правилния начин за потребителя.

Нека го поправим!

Стъпка 5: Малко отстраняване

Малко отстраняване
Малко отстраняване

Ние използваме процедура, наречена debounce, за да преодолеем проблема си с бутоните. Това по същество изчаква определен период от време между натискането на бутона и действителния отговор на натискането. Все още се чувства естествено за потребителя (освен ако не направите времето твърде дълго). Можете също да го използвате за проверка на продължителността на пресата, така че да можете да реагирате различно всеки път. Не е необходимо да сменяте нито едно от окабеляванията!

Нека да разгледаме кода:

#дефинирайте бутон 2#дефинирайте debounceTimeout 100

Първата промяна е в глобалния обхват. Ще запомните, че тук дефинираме променливи, които много от функциите ни могат да използват, или тези, които не могат да бъдат нулирани всеки път, когато цикълът се задейства. И така, ние добавихме debounceTimeout към дефинираните константи. Направихме това 100 (което по -късно ще се преведе на 100ms), но може да бъде по -кратко. Повече и ще се чувства неестествено.

long int lastDebounceTime;

Тази променлива е декларирана под константи. Това е дълъг тип int, който по същество ни позволява да съхраняваме дълги числа в паметта. Нарекохме го lastDebounceTime.

Не е нужно да променяме нищо във функцията void setup (). Нека оставим този.

void loop () {// Прочетете цифровия щифт, за да проверите състоянието на бутона int натиснат = digitalRead (бутон); дълъг int currentTime = millis (); // Код на бутона}

Първата промяна, която правим във функцията loop (), е под повикването да прочетете бутона. Трябва да следим текущото време. Функцията millis () връща текущото време на часовника от стартирането на Arduino за милисекунди. Трябва да съхраняваме това в дълга променлива тип int.

Сега трябва да се уверим, че сме наясно с времето от натискане на бутона, така че нулираме таймера, когато не е натиснат. Погледни:

void loop () {// Прочетете цифровия щифт, за да проверите състоянието на бутона int натиснат = digitalRead (бутон); дълъг int currentTime = millis (); if (натиснат == LOW) {// Нулирайте времето за броене, докато бутонът не е натиснат lastDebounceTime = currentTime; } // Код на бутона}

Алгоритъмът if (натиснат == LOW) проверява дали бутонът не е натиснат. Ако не е, тогава кодът съхранява текущото време от последния дебюн. По този начин при всяко натискане на бутона имаме момент във времето, от който можем да проверим кога е натиснат бутонът. След това можем да направим бързо математическо изчисление, за да видим колко дълго е натиснат бутона и да реагираме правилно. Нека да разгледаме останалата част от кода:

void loop () {// Прочетете цифровия щифт, за да проверите състоянието на бутона int натиснат = digitalRead (бутон); дълъг int currentTime = millis (); if (натиснат == LOW) {// Нулирайте времето за броене, докато бутонът не е натиснат lastDebounceTime = currentTime; } // Бутонът е бил натиснат за определен период от време, ако (((currentTime - lastDebounceTime)> debounceTimeout)) {// Ако времето за изчакване е достигнато, бутонът е натиснат! Serial.println ("Пресовано!"); }}

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

Пуснете кода си и проверете дали работи. Ако имате грешки, проверете кода си!

Нека сега разгледаме един практически пример.

Стъпка 6: Създаване на меню

Съставяне на меню
Съставяне на меню

Бутоните са интересни, защото има толкова много възможности с тях! В този пример ще направим меню. Да предположим, че сте създали това наистина страхотно устройство и се нуждаете от потребителите, за да могат да променят опциите, за да включат или изключат определени неща или да зададат определена стойност за настройка. Този дизайн с три бутона може да направи това!

Така че за този проект се нуждаем от:

  • Три бутона
  • Три резистора, настроени на 10 kΩ

Вече имаме едно от тях, просто ни трябват другите две. Така че добавете тези към дъската. Окабеляването е малко по -сложно, но само защото исках да го запазя наистина компактен. Можете да следвате същия модел за първия бутон или да следвате изображението по -горе.

Трите бутона са опция за отваряне/следващо меню, опция за промяна (както в, променете настройката) и бутон за запазване/затваряне на менюто.

Свържете го, нека да разгледаме кода!

Стъпка 7: Разбивка на кода - глобално

Добре, това ще бъде дълга стъпка, но ще премина през всеки раздел от кода.

Първо, нека разгледаме необходимите глобални променливи.

// Определете константи #дефинирайте менюто Бутон 2 #дефинирайте менюто Изберете 3 #дефинирайте менюто Запазете 4 #дефинирайте debounceTimeout 50 // Дефинирайте променливи int menuButtonPreviousState = LOW; int menuSelectPreviousState = НИСКО; int menuSavePreviousState = НИСКО; long int lastDebounceTime; // Опции на менюто char * menuOptions = {"Check Temp", "Check Light"}; bool featureSetting = {false, false}; bool menuMode = false; bool menuNeedsPrint = false; int optionSelected = 0;

Тези три блока са доста подобни на това, което видяхме преди. В първия дефинирах трите бутона и времето за изчакване. За тази част от проекта съм го настроил на 50ms, така че е необходимо умишлено натискане, за да работи.

Вторият блок е всички променливи. Трябва да следим бутонаPreviousState и трябва да следим последнотоDebounceTime. Това са всички променливи тип int, но последната е дълга, защото предполагам, че имаме нужда от място в паметта.

Блокът с опции на менюто има няколко нови функции. Първо, char * (да, това е умишлена звездичка), която е символна/низ литерална променлива. Това е указател към статично хранилище в паметта. Не можете да го промените (както можете в Python, например). Този ред char *menuOptions създава масив от низови литерали. Можете да добавите колкото искате елементи от менюто.

Променливата bool featureSetting е само масивът от стойности, който представлява всеки елемент от менюто. Да, можете да съхранявате всичко, което искате, просто променете типа на променливата (всички те трябва да са от един и същи тип). Сега може да има по -добри начини за управление на това, като речници или кортежи, но това е просто за това приложение. Вероятно бих създал едно от последните в разгърнато приложение.

Проследих менюто Mode, така че ако исках други неща на дисплея си, бих могъл да направя това. Също така, ако имах сензорна логика, бих могъл да направя пауза по време на работа с менюто, в случай че нещо е в конфликт. Имам променлива menuNeedsPrint, защото искам да отпечатам менюто в определено време, а не само през цялото време. И накрая, имам променлива optionSelected, така че мога да следя избраната опция, когато имам достъп до нея на няколко места.

Нека разгледаме следващия набор от функции.

Стъпка 8: Разбивка на кода - Настройка и персонализирани функции

Функцията setup () е достатъчно лесна, само три входни декларации:

void setup () {pinMode (menuSelect, INPUT); pinMode (menuSave, INPUT); pinMode (menuSelect, INPUT); Serial.begin (9600); }

Следват трите персонализирани функции. Нека разгледаме първите две, после последната поотделно.

Нуждаем се от две функции, които връщат известна информация. Причината е, че искаме да се уверим, че това е нещо, четимо от човека. Също така ще помогне при отстраняване на грешки в кода, ако имаме проблем. Код:

// Функция за връщане на текущо избраната опцияchar *ReturnOptionSelected () {char *menuOption = menuOptions [optionSelected]; // опция за връщане Избрано меню за връщанеOption; } // Функция за връщане на състоянието на текущо избраната опция char *ReturnOptionStatus () {bool optionSetting = featureSetting [optionSelected]; char *optionSettingVal; if (optionSetting == false) {optionSettingVal = "False"; } else {optionSettingVal = "Вярно"; } // опция за връщанеSetting return optionSettingVal; }

Функцията char *ReturnOptionSelected () проверява избраната опция (ако виждате по -горе, ние задаваме променлива да следи това) и издърпва низовия литерал от масива, който създадохме по -рано. След това го връща като тип char. Знаем това, защото функцията показва типа на връщане.

Втората функция, char *ReturnOptionStatus () чете състоянието на опцията, записана в масива, и връща низов литерал, който представлява стойността. Например, ако настройката, която сме съхранили, е невярна, бих върнал „False“. Това е така, защото ние показваме на потребителя тази променлива и е по -добре да запазим цялата тази логика заедно. Бих могъл да го направя по -късно, но има повече смисъл да го направя тук.

// Функция за превключване на текущата optionbool ToggleOptionSelected () {featureSetting [optionSelected] =! FeatureSetting [optionSelected]; връщане true; }

Функцията bool ToggleOptionSelected () е удобна функция за промяна на стойността на настройката, която сме избрали в менюто. Той просто обръща стойността. Ако имате по -сложен набор от опции, това може да е съвсем различно. Връщам true в тази функция, защото моето обратно повикване (обаждането по -късно в кода, който задейства тази функция) очаква истинен/фалшив отговор. Сигурен съм на 100%, че това ще работи, така че не съм имал предвид, че не работи, но бих в разгърнато приложение (за всеки случай).

Стъпка 9: Цикълът…

Функцията loop () е доста дълга, така че ще го направим на части. Можете да приемете всичко под гнездата в тази функция:

void loop () {

// Работете тук <-----}

Добре, видяхме това преди:

// Прочетете бутоните int menuButtonPressed = digitalRead (menuButton); int menuSelectPressed = digitalRead (menuSelect); int menuSavePressed = digitalRead (menuSave); // Вземете текущото време long int currentTime = millis (); if (menuButtonPressed == LOW && menuSelectPressed == LOW && menuSavePressed == LOW) {// Нулиране на времето за броене, докато бутонът не е натиснат lastDebounceTime = currentTime; menuButtonPreviousState = НИСКО; menuSelectPreviousState = НИСКО; menuSavePreviousState = НИСКО; }

Всичко, което трябваше да направя тук, беше да добавя трите извиквания на digitalRead () и да се уверя, че съм отчел факта, че ако всички бутони са ниски, трябва да нулираме таймера (lastDebounceTime = currentTime) и да зададем всички предишни състояния на ниско. Също така съхранявам millis () в currentTime.

Следващият раздел се намира в линията

if ((((currentTime - lastDebounceTime)> debounceTimeout)) {

// Работете тук <----}

Има три раздела. Да, можех да ги преместя в техните собствени функции, но за по -простота запазих трите основни алгоритма на бутоните тук.

if ((menuButtonPressed == HIGH) && (menuButtonPreviousState == LOW)) {if (menuMode == false) {menuMode = true; // Нека потребителят знае Serial.println ("Менюто е активно"); } else if (menuMode == true && optionSelected = 1) {// Нулиране на опция optionSelected = 0; } // Отпечатване на менюто menuNeedsPrint = true; // Превключване на бутона prev. състояние само за показване на менюто // ако бутонът е освободен и натиснат отново menuButtonPreviousState = menuButtonPressed; // би било ВИСОКО}

Това първо се обработва, когато menuButtonPressed е ВИСОК, или когато е натиснат бутонът на менюто. Той също така проверява дали предишното състояние е LOW, така че бутонът трябваше да бъде освободен, преди да бъде натиснат отново, което спира програмата да задейства постоянно едно и също събитие отново и отново.

След това проверява, че ако менюто не е активно, го активира. Той ще отпечата първата избрана опция (която е първият елемент в масива menuOptions по подразбиране. Ако натиснете бутона втори или трети (и т.н.) път, ще получите следващата опция в списъка. Нещо, което мога да поправя, е че когато стигне до края, се връща в началото на цикъла. Това може да прочете дължината на масива и да улесни връщането назад, ако промените броя на опциите, но засега това беше просто.

Последната малка секция (// Отпечатва менюто) очевидно отпечатва менюто, но също така задава предишното състояние на ВИСОК, така че същата функция няма да се върти (вижте бележката ми по -горе за проверка дали бутонът е бил НИСКИ).

// menuSelect е натиснат, предоставя logicif ((menuSelectPressed == HIGH) && (menuSelectPreviousState == LOW)) {if (menuMode) {// Промяна на избраната опция // В момента това е само вярно/невярно // но може да бъде нещо bool toggle = ToggleOptionSelected (); if (превключване) {menuNeedsPrint = true; } else {Serial.println ("Нещо се обърка. Моля, опитайте отново"); }} // Превключване на състоянието само за превключване, ако е освободено и натиснато отново menuSelectPreviousState = menuSelectPressed; }

Този бит код обработва бутона MenuSelectPressed по същия начин, с изключение на този път просто задействаме функцията ToggleOptionSelected (). Както казах по -рано, можете да промените тази функция, така че да прави повече, но това е всичко, от което се нуждая.

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

if ((menuSavePressed == HIGH) && (menuSavePreviousState == LOW)) {// Излезте от менюто // Тук можете да направите подреждане // или да запишете в EEPROM menuMode = false; Serial.println ("Менюто излезе"); // Превключва състоянието, така че менюто излиза само веднъж menuSavePreviousState = menuSavePressed; }}

Тази функция обработва бутона SaveSave, който просто излиза от менюто. Тук можете да имате опция за анулиране или запазване, може би да направите почистване или да запишете в EEPROM. Просто отпечатвам „Менюто е излязло“и настройвам състоянието на бутона на ВИСОК, за да не се повтаря.

if (menuMode && menuNeedsPrint) {// Ние сме отпечатали менюто, така че освен ако // нещо не се случи, няма нужда да го отпечатваме отново menuNeedsPrint = false; char *optionActive = ReturnOptionSelected (); char *optionStatus = ReturnOptionStatus (); Serial.print ("Избрано:"); Serial.print (optionActive); Serial.print (":"); Serial.print (optionStatus); Serial.println (); }

Това е алгоритъмът menuPrint, който се задейства само когато менюто е активно и когато променливата menuNeedsPrint е зададена на true.

Това определено би могло да бъде преместено в собствената му функция, но за простота..!

Е, това е! Вижте следващата стъпка за целия кодов блок.

Стъпка 10: Окончателен блок код

// Определете константи

#define menuButton 2 #define menuSelect 3 #define menuSave 4 #define debounceTimeout 50 int menuButtonPreviousState = LOW; int menuSelectPreviousState = НИСКО; int menuSavePreviousState = НИСКО; // Дефиниране на променливи long int lastDebounceTime; bool lightSensor = true; bool tempSensor = true; // Опции на менюто char * menuOptions = {"Check Temp", "Check Light"}; bool featureSetting = {false, false}; bool menuMode = false; bool menuNeedsPrint = false; int optionSelected = 0; // Функция за настройка

void setup () {pinMode (menuSelect, INPUT); pinMode (menuSave, INPUT); pinMode (menuSelect, INPUT); Serial.begin (9600); }

// Функция за връщане на текущо избраната опция char *ReturnOptionSelected () {char *menuOption = menuOptions [optionSelected]; // опция за връщане Избрано меню за връщанеOption; } // Функция за връщане на състоянието на текущо избраната опция char *ReturnOptionStatus () {bool optionSetting = featureSetting [optionSelected]; char *optionSettingVal; if (optionSetting == false) {optionSettingVal = "False"; } else {optionSettingVal = "Вярно"; } // опция за връщанеSetting return optionSettingVal; } // Функция за превключване на текущата опция bool ToggleOptionSelected () {featureSetting [optionSelected] =! FeatureSetting [optionSelected]; връщане true; } // Основният цикъл

void loop () {// Прочетете бутоните int menuButtonPressed = digitalRead (menuButton); int menuSelectPressed = digitalRead (menuSelect); int menuSavePressed = digitalRead (menuSave); // Вземете текущото време long int currentTime = millis (); if (menuButtonPressed == LOW && menuSelectPressed == LOW && menuSavePressed == LOW) {// Нулиране на времето за броене, докато бутонът не е натиснат lastDebounceTime = currentTime; menuButtonPreviousState = НИСКО; menuSelectPreviousState = НИСКО; menuSavePreviousState = НИСКО; } if ((((currentTime - lastDebounceTime)> debounceTimeout)) {// Ако времето за изчакване е достигнато, бутонът е натиснат!

// menuButton е натиснат, предоставя логика

// Задейства се само когато бутонът е бил освободен преди, ако ((menuButtonPressed == HIGH) && (menuButtonPreviousState == LOW)) {if (menuMode == false) {menuMode = true; // Нека потребителят знае Serial.println ("Менюто е активно"); } else if (menuMode == true && optionSelected = 1) {// Нулиране на опция optionSelected = 0; } // Отпечатване на менюто menuNeedsPrint = true; // Превключване на бутона prev. състояние само за показване на менюто // ако бутонът е освободен и натиснат отново menuButtonPreviousState = menuButtonPressed; // Ще бъде HIGH} // menuSelect е натиснат, предоставете логика if ((menuSelectPressed == HIGH) && (menuSelectPreviousState == LOW)) {if (menuMode) {// Промяна на избраната опция // В момента това е просто true/false // но може да бъде нещо bool toggle = ToggleOptionSelected (); if (превключване) {menuNeedsPrint = true; } else {Serial.print ("Нещо се обърка. Моля, опитайте отново"); }} // Превключване на състоянието само за превключване, ако е освободено и натиснато отново menuSelectPreviousState = menuSelectPressed; } if ((menuSavePressed == HIGH) && (menuSavePreviousState == LOW)) {// Излезте от менюто // Тук можете да направите подреждане // или да запишете в EEPROM menuMode = false; Serial.println ("Менюто излезе"); // Превключва състоянието, така че менюто излиза само веднъж menuSavePreviousState = menuSavePressed; }} // Отпечатайте текущата опция от менюто активна, но я отпечатайте само веднъж, ако (menuMode && menuNeedsPrint) {// Отпечатали сме менюто, така че ако нещо не се случи //, няма нужда да го отпечатваме отново menuNeedsPrint = false; char *optionActive = ReturnOptionSelected (); char *optionStatus = ReturnOptionStatus (); Serial.print ("Избрано:"); Serial.print (optionActive); Serial.print (":"); Serial.print (optionStatus); Serial.println (); }}}

Веригата е достъпна на сайта на Tinkercad. Вградих схемата по -долу, за да видите и вие!

Както винаги, ако имате въпроси или проблеми, моля, уведомете ме!

Препоръчано: