Функція millis() в Ардуїно, багатозадачність
Функція millis Arduino відіграє важливу роль при реалізації багатозадачності в програмі. На нашому веб-сайті опубліковано безліч проектів на Arduino, які використовують функцію millis(), в яких необхідно проводити відлік часу. Розглянемо застосування команди millis() в Ардуїно для управління часом і, як використовувати millis без затримки delay у скетчі на прикладі миготливого світлодіода.
Необхідні компоненти:
- Arduino Uno / Arduino Nano / Arduino Mega
- світлодіод та резистор
- макетна плата
- конектори
Функція millis Arduino повертає кількість мілісекунд, що пройшли з моменту запуску програми. Лічильник часу обнулюється, коли значення змінної unsigned long переповнюється (приблизно через 50 днів). Функція millis у мові Arduino дозволяє мікроконтролеру бути багатозадачним, оскільки виконання програми не зупиняється, інші операції можуть виконуватися паралельно.
Функція millis() у мові Ардуїно
Для миготіння світлодіодом або будь-якої періодично виконуваної задачі зазвичай використовується функція delay, однак ця команда зупиняє виконання програми і плата Arduino не може виконувати інші операції. Щоб подолати цей істотний недолік у плати Ардуїно функцію delay замінюють на функцію millis. Таким чином відбувається створення багатозадачності мікроконтролера Ардуїно.
Багатозадачність в Arduino за допомогою millis
Коли ви використовуєте функцію delay у програмі, виконання всіх команд зупиняється на задану кількість мілісекунд. У цьому полягає основна різниця між millis() та delay(). При підключенні датчиків необхідно отримувати безперервні дані, тому затримка функція замінюється на millis. У прикладі з миготливим світлодіодом ми зробимо затримку (паузу) за допомогою millis без delay.
Скетч миготіння світлодіодом без delay на Ардуїно
#define LED 13 unsigned long timer; boolean ledState = 0; void setup() < pinMode(LED, OUTPUT); Serial.begin(9600); timer = millis(); >void loop() < if (millis() - timer >1000) < ledState=!ledState; // Змінюємо стан світлодіода digitalWrite(LED, ledState); timer = millis(); >Serial.print("Time: "); Serial.println(timer); >Висновок. Команда Arduino millis не зупиняє виконання програми, а починає відлік з початку лічильника мілісекунд. На відміну від цієї функції, delay та delayMicroseconds зупиняють виконання програми на задану кількість мілісекунд або мікросекунд відповідно. Застосування тієї чи іншої паузи мікроконтролера, тобто. Багатозадачність Arduino Uno залежить від поставленого завдання.
Arduino має гарний набір функцій, з якими можна працювати з часом. Один з них є Мілліс (), інструкція, яка показує час у мілісекундах з моменту включення плати Arduino. Це може здатися абсурдним і служить лише для того, щоб дізнатися, коли плита була включена, але правда в тому, що вона має набагато більше практичних застосувань.
за приклад, його можна використовувати для визначення часу, що минув між двома або більше подіями, запобігання брязкоту (брязкоту) кнопки і т. д. Його також можна використовувати для відображення часу виконання на критичних етапах коду, гарантуючи, що програма працює в реальному часі.
Функція Millis ()
Як я вже згадував, мілліс-функція Arduino використовується для вимірювання часу, і це робиться в мілісекунди (мс), звідси та її назва. Іншими словами, числове значення, яке ця функція повертає, коли ви включаєте його до свого ескізу, є тимчасовими даними, вираженими в цій одиниці.
Ви повинні знати, що максимальне значення цієї змінної дорівнює без знаку довготобто довго без знака. Це важливо тому, що при використанні меншого розміру можуть виникнути проблеми з логікою.Крім того, ви повинні знати, що він може тривати до 50 днів (4.320.000.000 XNUMX XNUMX XNUMX мс), як тільки він досягне цього значення, він перезапуститься і знову почнеться з нуля.
Ще вам потрібно знати, що функція millis не використовує параметри.
Інші часові функції Arduino
Arduino має інші функції, пов'язані з часом, які ви можете використовувати у своєму коді. Один з них є знаменита затримка (), але це ще не все:
- затримка (): Це найпоширеніша з усіх функцій Arduino. Він також використовує мілісекунди як millis (). І він також буде мати тип unsigned long, крім відсутності значення, що повертається. Він використовується в основному для введення пауз у виконанні програми в багатьох програмах.
- delayMicroseconds(): менше використовується в скетчах, в цьому випадку він все ще беззнаковий, без значення, що повертається, і в цьому випадку він використовує мікросекунди. В даний час максимальне значення може бути досягнуто з точністю 16 383, а мінімальне – 3 мкс. Якщо потрібно обробляти більш тривале очікування, рекомендується використовувати delay ().
- мікро(): також повертає числове значення в мікросекундах (мкс) з початку виконання програми платою Arduino. Тобто це схоже на millis(), але з іншим модулем. Фактично він також використовує беззнаковий довгий тип і не використовує параметри. Але він має деякі додаткові відмінності, наприклад, він скидається і запускається з нуля після досягнення 70 хвилин. Що стосується його роздільної здатності 4 мкс, або, іншими словами, значення, що повертається завжди буде кратним чотирма (4, 8, 12, 16, …). Пам'ятайте, що 1000 мкс дорівнює 1 мс, а 1.000.000 дорівнює 1 с.
Приклади Millis () у Arduino IDE
Все це слова, і найкращий вид функції millis () показує кілька прикладів простих ескізів IDE Arduino, щоб ви могли побачити деякі програми та варіанти використання. Отже, ось деякі практичні приклади.
1-Приклад для поясніть використання з millis ():
unsigned long inicio, fin, transcurrido; // Declarar las variables a use void setup() < Serial.begin(9600); //Iniciar la comunicación serial >void loop() <inicio=millis(); //Consultar ms desde que inició lexecució del sketch delay(1000); //Espera 1 segundo fin=millis(); //Consultar ms fin del sketch transcurrido=fin-inicio; //Calcula el tiempo desde la última lectura Serial.println(transcurrido); //Muestra el resultado en el monitor serial delay(500); // Esperar medio segundo >
Виміряйте час між двома послідовними повідомленнями:
unsigned long tiempo1 = 0; //Declaramos las variables e iniciamos a 0 unsigned long tiempo2 = 0; unsigned long diferenciaTiempo = 0; void setup() < Serial.begin(9600); Serial.println("Envia la letra A/a por la terminal serial"); >void loop() < if(Serial.available() >0) < char datoRecibido = Serial.read(); if(datoRecibido == 'A' || datoRecibido == 'a')< tiempo1 = millis(); Serial.println("Envia la letra B/b por la terminal Serial"); >else if(datoRecibido == 'b' && datoRecibido == 'B') < tiempo2 = millis(); diferenciaTiempo = tiempo1-tiempo2; Serial.print("El tiempo transcurrido entre el primer y último dato enviado es: "); Serial.print(diferenciaTiempo); >> >Робити блимати світлодіодом з millis ():
int estatLed; //Almacena el estado del LED (Encendido o apagado) int periodo = 100; //Tiempo que está el LED encendido o apagado unsigned long tiempoAnterior = 0; //Almacena tiempo de referencia para comparar void setup() < pinMode(13,OUTPUT); //Configura el pin 13 com a salida para el LED >void loop() < if(millis()-tiempoAnterior>=periodo) < //Evalua si ha transcurrido el período programado estadoLed=!estadoLed; //Cambia el estado del LED cada 100ms digitalWrite(13,estadoLed); //Actualiza el estado del LED al actual tiempoAnterior=millis(); //Almacena el tiempo actual como referencia >>
Створити простий секвенсор для надсилання тексту через послідовний монітор через різні проміжки часу за допомогою millis ():
#define INTERVALO_MENSAJE1 3000 #define INTERVALO_MENSAJE2 5000 #define INTERVALO_MENSAJE3 7000 #define INTERVALO_MENSAJE4 15000 unsigned long tiempo_1 = 0; unsigned long tiempo_2 = 0; unsigned long tiempo_3 = 0; unsigned long tiempo_4 = 0; void print_tiempo(unsigned long tiempo_millis); void setup() < Serial.begin(9600); >void loop() < if(millis() >tiempo_1 + INTERVALO_MENSAJE1) < tiempo_1 = millis(); print_tiempo(tiempo_1); Serial.println("Soy"); >if(millis() > tiempo_2 + INTERVALO_MENSAJE2) < tiempo_2 = millis(); print_tiempo(tiempo_2); Serial.println("Un mensaje"); >if(millis() > tiempo_3 + INTERVALO_MENSAJE3) < tiempo_3 = millis(); print_tiempo(tiempo_3); Serial.println("De"); >if(millis() > tiempo_4 + INTERVALO_MENSAJE4) < tiempo_4 = millis(); print_tiempo(tiempo_4); Serial.println("Esperanza"); >> void print_tiempo(unsigned long tiempo_millis) < Serial.print("Tiempo: "); Serial.print(tiempo_millis/1000); Serial.print("s - "); >Ви вже знаєте, що для Додаткову інформацію Ви можете завантажити безкоштовний курс програмування Arduino у форматі PDF.
Будьте першим, щоб коментувати
Ми з вами вже багато разів розглядали конструкцію таймера millis() , який дозволяє налагодити логіку роботи коду по таймерах. Мінусом цього способу є необхідність постійно опитувати конструкцію таймера, щоб перевіряти, чи не спрацював він. Відповідно код у головному циклі має бути "прозорим", тобто не містити затримок, довгих замкнутих циклів і просто блокуючих шматків. Якщо для таймерів з довгим періодом (хвилина, 5 секунд) це не так критично, то для виконання дій з високою заданою частотою будь-яка маленька затримка в головному циклі може стати великою проблемою! Виходом із ситуації може стати переривання за таймером.Згадайте урок про апаратні переривання: переривання дозволяє "вийти" з будь-якої виконуваної на даний момент ділянки коду в основному циклі, виконати потрібний блок коду, який знаходиться всередині переривання, і повернутися туди, звідки вийшли, і продовжити виконання. Таким чином, це практично паралельне виконання завдань. У цьому уроці ми навчимося робити це з апаратного таймера. Навіщо використовувати переривання по таймеру?
- Генерація сигналів
- Вимірювання часу
- Паралельне виконання завдань
- Виконання завдання через заданий період часу
- І багато іншого
Таймери
Переривання генеруються окремим апаратним таймером, який знаходиться у мікроконтролері десь поруч із обчислювальним ядром. Апаратний таймер, він же лічильник, Займається дуже простим завданням: вважає "тики" тактового генератора (який задає частоту роботи всієї системи) і, залежно від режиму роботи, може смикати ногами або давати сигнал на мікроконтролер при певних значеннях лічильника. Таким чином "дозвіл" роботи таймера – один тик (такт) генератора, що задає, при 16 МГц це 0.0625 мікросекунди. Другий важливий момент для розуміння: таймер-лічильник працює і вважає імпульси паралельно до обчислювального ядра. Саме тому генерація ШІМ сигналу навіть на високій частоті абсолютно не впливає на виконання коду – воно все відбувається паралельно. В Ардуїно нано (atmega328) у нас три таких таймери, і кожен може активувати незалежне переривання за своїм періодом. Що стосується рахунку часу: функції millis() і micros() якраз працюють на перериванні таймера 0. Якщо переналаштувати таймер 0 – у нас пропаде коректний рахунок часу (і, можливо, ШІМ на пінах 5 і 6). Деякі бібліотеки також використовують переривання таймерів, наприклад Servo використовує перший, а вбудована функція tone() – другий.Також ми обговорювали в уроках, що таймери займаються генерацією ШІМ сигналу на своїх пінах, і при перенастроюванні таймера ШІМ може відключитися, почати працювати в іншому режимі або змінити частоту. На відміну від генерації ШІМ сигналу та апаратних переривань, управління перериваннями по таймерах не реалізовано розробниками Ардуїно в ядрі та стандартних бібліотеках, тому працюватимемо з перериваннями за допомогою сторонніх бібліотек. Можна працювати з таймером безпосередньо, як описано в датасіті, але це не входить у цей курс уроків. Для першого і другого таймерів можна знайти старі бібліотеки, які називаються timerOne і timerTwo. У мене є своя бібліотека – GyverTimers, яка дозволяє гнучко налаштувати всі таймери на atmega328 (Arduino UNO/Nano) та atmega2560 (Arduino Mega). Завантажити бібліотеку можна за прямим посиланням, також про неї є окрема сторінка у мене на сайті, з описом, документацією та прикладами. Розглянемо основні інструменти бібліотеки.
Бібліотека GyverTimers
Бібліотека GyverTimers дозволяє генерувати переривання по таймеру із заданою частотою на вибраному каналі таймера або на декількох каналах відразу зі зсувом по фазі: переривання відбуватимуться з однаковою частотою, але "зрушені" один щодо одного. Також можна задати дію для виведення таймера: увімкнути/вимкнути/перемикати: таймер буде керувати обраним піном незалежно від обчислювального ядра МК, таким чином можна генерувати меандр (квадратний сигнал), причому як однотактний, так і дво- і тритактний з зсувом по фазі, що налаштовується . Для Arduino Nano/UNO/Pro Mini є три таймери: Timer0, Timer1, Timer2. Для Arduino MEGA – п'ять: Timer0, Timer1, Timer2, Timer3, Timer4, Timer5. У бібліотеці таймери описані як об'єкти, звернення відбувається зазвичай через точку. Наприклад, Timer1.stop();
Таблиця таймерів ATmega328p
| Таймер | Розрядність | Частоти | Періоди | Виходи | Пін Arduino | Пін МК |
| Timer0 | 8 біт | 61 Гц. 1 МГц | 16 384.. 1 мкс | CHANNEL_A | D6 | PD6 |
| CHANNEL_B | D5 | PD5 | ||||
| Timer1 | 16 біт | 0.24 Гц. 1 МГц | 4200000.. 1 мкс | CHANNEL_A | D9 | PB1 |
| CHANNEL_B | D10 | PB2 | ||||
| Timer2 | 8 біт | 61 Гц. 1 МГц | 16 384.. 1 мкс | CHANNEL_A | D11 | PB3 |
| CHANNEL_B | D3 | PD3 |
Таблиця таймерів ATmega2560
| Таймер | Розрядність | Частоти | Періоди | Виходи | Пін Arduino | Пін МК |
| Timer0 | 8 біт | 61 Гц. 1 МГц | 16 384.. 1 мкс | CHANNEL_A | 13 | PB7 |
| CHANNEL_B | 4 | PG5 | ||||
| Timer1 | 16 біт | 0.24 Гц. 1 МГц | 4200000.. 1 мкс | CHANNEL_A | 11 | PB5 |
| CHANNEL_B | 12 | PB6 | ||||
| CHANNEL_C | 13 | PB7 | ||||
| Timer2 | 8 біт | 61 Гц. 1 МГц | 16 384.. 1 мкс | CHANNEL_A | 10 | PB4 |
| CHANNEL_B | 9 | PH6 | ||||
| Timer3 | 16 біт | 0.24 Гц. 1 МГц | 4200000.. 1 мкс | CHANNEL_A | 5 | PE3 |
| CHANNEL_B | 2 | PE4 | ||||
| CHANNEL_C | 3 | PE5 | ||||
| Timer4 | 16 біт | 0.24 Гц. 1 МГц | 4200000.. 1 мкс | CHANNEL_A | 6 | PH3 |
| CHANNEL_B | 7 | PH4 | ||||
| CHANNEL_C | 8 | PH5 | ||||
| Timer5 | 16 біт | 0.24 Гц. 1 МГц | 4200000.. 1 мкс | CHANNEL_A | 46 | PL3 |
| CHANNEL_B | 45 | PL4 | ||||
| CHANNEL_C | 44 | PL5 |
Максимальний період
У таблиці вище наведено діапазони 16 МГц тактування. Для іншого системного клаку максимальний період вважається за формулою, де F_CPU – системна частота Гц:
- 8 біт таймери: (1000000UL / F_CPU) * (1024 * 256)
- 16 біт таймери: (1000000UL / F_CPU) * (1024 * 65536)
Налаштування частоти/періоду
- setPeriod(період); – встановлення періоду в мікросекундах та запуск таймера. Повертає реальний період мкс (точність обмежена дозволом таймера).
- setFrequency(частота); – встановлення частоти в Герцах та запуск таймера. Повертає реальну частоту Гц (точність обмежена дозволом таймера).
- setFrequencyFloat(частота float); – встановлення частоти в Герцах та запуск таймера, дозволені десяткові дроби. Повертає реальну частоту (точність обмежена роздільною здатністю таймера).
Контроль роботи таймера
- pause(); – призупинити рахунок таймера, не скидаючи лічильник
- resume(); – продовжити рахунок після паузи
- stop(); – зупинити рахунок та скинути лічильник
- restart(); – перезапустити таймер (скинути лічильник)
Переривання
- enableISR(канал, фаза); – запустити переривання на вибраному каналі з вибраним зсувом фази.Якщо нічого не вказувати, буде вибраний канал A та фаза 0
- Канал – CHANNEL_A, CHANNEL_B або CHANNEL_С (див. таблицю вище!)
- Фаза – чисельне значення 0-359
Бібліотека дає прямий доступ до переривання без "Ардуїнівських" attachInterruptщо дозволяє скоротити час виклику функції-обробника переривання. Переривання з настроєною частотою оброблятиметься в блоці виду ISR(канал) <> , приклад:
ISR(TIMER1_A) < // ваш код >ISR(TIMER1_B) < // ваш код >ISR(TIMER2_B) < // ваш код >ISR(TIMER0_A) < // ваш код >
Апаратні виходи
- outputEnable (канал, режим); – увімкнути керування апаратним виходом таймера
- Канал: CHANNEL_A або CHANNEL_B (+ CHANNEL_C у ATmega2560, див. таблицю таймерів).
- Режим: TOGGLE_PIN , CLEAR_PIN , SET_PIN (переключити/вимкнути/включити пін для спрацювання таймера)
- Канал: CHANNEL_A або CHANNEL_B (+ CHANNEL_C у Mega2560, див. таблицю таймерів)
- Канал: CHANNEL_A або CHANNEL_B (+ CHANNEL_C у ATmega2560, див. таблицю таймерів).
- Стан: HIGH або LOW
Важливо: при генерації меандру реальна частота буде вдвічі меншою від заданої через особливості роботи самого таймера. Див. Приклади з меандром.
Зсув фази (з 1.6)
За допомогою phaseShift(source, angle) можна зрушити переривання або перемикання пінів на вибраному каналі source по фазі angle – кут зсуву в градусах від 0 до 360.
- У 8-бітових таймерів можна задати зсув лише у другого каналу ( CHANNEL_B )
- У 16-бітних можна рухати всі три канали
Налаштування за замовчуванням
За допомогою методу setDefault() можна скинути налаштування таймера на "Ардуїнівські" умовчання: частоту та режим роботи.
Приклади
// Демонстрація всіх функцій бібліотеки #include "GyverTimers.h" void setup() < // Переналаштувати таймер і задати йому період чи частоту // Усі функції повертають реальний період / частоту, які можуть відрізнятися від введених Timer2.setPeriod(10000);// Задати конкретний період 10000 мкс (100 гц), поверне реальний період мкс Timer0.setFrequency(250); // Задати частоту переривань таймера Гц, поверне реальну частоту в герцах Timer1.setFrequencyFloat(50.20); // Задати частоту точніше, в дробових числах, актуально для низьких частот і таймера 1 // З цього моменту таймер вже переналаштований і ганяє з виірваною частотою / періодом // Підключити переривання таймера, з цього моменту переривання викликатимуться Timer0.enableISR() ; // Підключити стандартне переривання, канал А, без сдига фаз Timer2.enableISR(CHANNEL_B, 180); // Підключити переривання таймера 2, канал B, початкова фаза – 180 градусів Timer1.enableISR(CHANNEL_A, 60); // Підключити переривання каналу А, встановити фазу для каналу А доступно тільки для таймера 1! Timer1.enableISR(CHANNEL_B, 120); // Підключити друге переривання таймера 1, і задати фазовий зсув цього потоку // Переривання вже почне викликатися // Якщо раптом переривання потрібно відключити, не зупиняючи таймер Timer1.disableISR(CHANNEL_B); // З цього моменту переривання B більше не буде викликатись // Якщо потрібно призупинити таймер ПОВНІСТТЮ, апаратно Timer2.pause(); // З цього моменту таймер стоїть на місці, вміст лічильника залишається недоторканим // Тепер таймер можна повернути до ладу Timer2.resume(); // Таймер продовжив рахувати з того місця // Якщо потрібно повністю зупинити таймер і скинути вміст лічильника Timer1.stop(); // Таймер стоїть, лічильник скинутий // Повертаємо таймер до ладу Timer1.restart(); // Таймер перезапущено, почав рахувати з початку // Якщо потрібно повернути стандартні Arduino – налаштування таймера Timer0.setDefault(); // Тепер таймер працює у станлартному режимі >// вектори переривань ISR(TIMER1_A) < >ISR(TIMER1_B) <>ISR(TIMER2_B) < >ISR(TIMER0_A) < >void loop()
// Приклад простої генерації переривань апаратним таймером #include "GyverTimers.h" setup() < Serial.begin(9600); Timer1.setFrequency(3); // Високоточний таймер 1 для першого переривання, частота - 3 Герца //Timer1.setPeriod(333333); // те саме! Частота 3 Гц це період 333333 мікросекунд //Timer1.setFrequencyFloat(4.22); // Якщо потрібна дробова частота Гц Timer1.enableISR(); // Запускаємо переривання (за замовчуванням. канал А) // запустимо другий таймер Timer2.setPeriod(10000); // Встановлюємо період таймера 10000 мкс -> 100 гц Timer2.enableISR(CHANNEL_A); // Або просто .enableISR(), запускаємо переривання на каналі А таймера 2 pinMode(13, OUTPUT); // блиматимемо > void loop() <> // Переривання А таймера 1 ISR(TIMER1_A) < // пишемо в серіал Serial.println("timer1"); >// Переривання А таймера 2 ISR(TIMER2_A) < // блимаємо digitalWrite(13, !digitalRead(13)); >// Приклад генерації двоканальних переривань на таймері з РІВНИМ періодом, але зрушених по фазі // два потоки переривань зі зсувом 180 градусів (повна інверсія) #include "GyverTimers.h" void setup() < Serial.begin(9600); Serial.print("Real timer frequency is : "); // Виведемо реальну частоту, реальна може відрізнятися від заданої (обмежено роздільною здатністю таймера) Serial.println(Timer1.setFrequencyFloat(2.50)); // Частота переривань - 2.5 гц, використовуй .setFrequency(. ) для цілих чисел delay(1000); Timer1.enableISR(CHANNEL_A, 0); // Перший канал - А, початкова фаза - 0 градусів Timer1.enableISR(CHANNEL_B, 180); // Другий канал - B, початкова фаза - 180 градусів > void loop() <> // два переривання однією таймері ISR(TIMER1_A) < Serial.println(" Channel A interrupt !"); // Переривання А >ISR(TIMER1_B) < Serial.println(" Channel B interrupt !"); // Переривання B>//Приклад генерації меандра на таймері 2 каналі B (D3 на Arduino UNO) #include "GyverTimers.h" void setup() < pinMode(3, OUTPUT); // налаштувати пін як вихід // через особливості генерації меандру таймером // частоту потрібно вказувати вдвічі більше за потрібну! Timer2.setFrequency(500 * 2); // Налаштувати частоту таймера в Гц Timer2.outputEnable (CHANNEL_B, TOGGLE_PIN); // у момент спрацьовування таймера пін перемикатиметься >void loop()
// Приклад генерації двотактного меандру на таймері 2 (піни D3 та D11) #include "GyverTimers.h" void setup() <pinMode(3, OUTPUT); // Налаштувати пін як вихід pinMode(11, OUTPUT); // налаштувати пін як вихід // через особливості генерації меандру таймером // частоту потрібно вказувати вдвічі більше за потрібну! Timer2.setFrequency(15000 * 2); // Налаштувати частоту в Гц і запустити таймер. Меандр на 15 кГц Timer2.outputEnable(CHANNEL_A, TOGGLE_PIN); // у момент спрацювання таймера пін перемикатиметься Timer2.outputEnable(CHANNEL_B, TOGGLE_PIN); // у момент спрацьовування таймера пін перемикатиметься Timer2.outputState(CHANNEL_A, HIGH); // задаємо початковий стан піна 11 Timer2.outputState (CHANNEL_B, LOW); // задаємо початковий стан піна 3 > void loop ()Відео
Корисні сторінки
- Набір GyverKIT – великий стартовий набір Arduino моєї розробки, що продається в Росії
- Каталог посилань на дешеві Ардуїни, датчики, модулі та інші залізниці з AliExpress у перевірених продавців
- Добірка бібліотек для Arduino, найцікавіших і найкорисніших, офіційних і не дуже
- Повна документація з мови Ардуїно, всі вбудовані функції та макроси, всі доступні типи даних
- Збірник корисних алгоритмів для написання скетчів: структура коду, таймери, фільтри, парсинг даних
- Відео уроки з програмування Arduino з каналу “Нотатки Ардуїнника” – одні з найдокладніших у рунеті
- Підтримати автора за роботу над уроками
- Зворотній зв'язок – повідомити про помилку в уроці або запропонувати додаток ([email protected])