На цій сторінці зібрано перелік практично всіх стандартних команд для Arduino, доповнений стислими поясненнями та прикладами використання. Деякі відомості взято з пошукових джерел, наприклад, Google, особливо щодо специфіки мови, інші напрацювання отримано шляхом експериментів. Детальнішу інформацію про застосування цих команд можна знайти у навчальних посібниках або в офіційній документації.
Структура скетча
Синтаксис, структура коду
/* */
Багаторядковий коментар.
/* цей код не
компілюється *///
Однорядковий коментар.
// цей код
// не компілюється;
Ставиться в кінці кожної дії.
void setup() {}
Функція, вміст якої виконується один раз під час запуску мікроконтролера.
void loop() {}
Функція, вміст якої виконується (або намагається виконуватися) «по колу» протягом усього часу роботи мікроконтроллеру.
#include
Директива, що дає змогу підключати до проєкту додаткові файли з кодом.
#include <Servo.h> // подключает библиотеку Servo.h
#include "Servo.h" // подключает библиотеку Servo.hУ чому відмінність <> і «»? Коли вказуємо назву «в лапках», компілятор спочатку шукає файл у папці зі скетчем, а потім у папці з бібліотеками. При використанні <галочок> компілятор шукає файл тільки в папці з бібліотеками.
#define
Директива, що дає команду препроцесору замінити вказану назву на вказане значення. Найчастіше таким чином оголошують константи:
#define MOTOR_PIN 10 // пін мотора 10
#define LED_PIN 3 // пін світлодіода 3Після компіляції всі слова MOTOR_PIN, що трапляються в тексті програми, будуть замінені на цифру 10, а LED_PIN – на цифру 3. Такий спосіб зберігання констант не використовує оперативну пам’ять мікроконтролера. Також define дає змогу робити т.зв. макро функції. Наприклад Ардуїнівська функція sq (квадрат) є макро, який під час компіляції перетворюється на множення:
#define sq(x) ((x)*(x))#if, #elif, #else, #endif
Директиви препроцесору, що дають змогу включати або виключати ділянки коду за умовою
#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
}Якщо параметру DEBUG встановити 1, то буде підключено бібліотеку Serial, якщо 0 – то ні. Таким чином отримуємо універсальний оптимізований проєкт із налагодженням.
#ifdef, #ifndef
Умовні директиви препроцесору, дають змогу вмикати або вимикати ділянки коду за умовою: ifdef – чи визначено? ifndef – чи не визначено?
#define TEST // визначаємо TEST
#ifdef TEST // якщо TEST визначено
#define VALUE 10 // визначити VALUE як 10
#else // якщо закоммент. #define TEST
#define VALUE 20 // визначити VALUE як 20
#endif // кінець умовиgoto
Оператор переходу в іншу частину коду за міткою. Не рекомендується до використання, завжди можна обійтися без нього. Як приклад використання – вихід із купи умов
for (byte r = 0; r < 255; r++) {
for (byte g = 255; g > -1; g--) {
for (byte b = 0; b < 255; b++) {
if (analogRead(0) > 250) {
// піти з порівнянь
goto bailout;
}
// ще код
}
}
}
bailout:
// перенеслися сюдиreturn
Оператор переривання функції, він же оператор повернення значення з функції.
Умови (if, switch)
if, else if, else
Оператор порівняння та його друзі.
// при виконанні однієї дії {} необов'язкові
if (a > b) c = 10; // якщо a більше b, то c = 10
else c = 20; // якщо ні, то c = 20
// замість порівняння можна використовувати лог. змінну
boolean myFlag, myFlag2;
if (myFlag) c = 10;
// складні умови
if (myflag && myFlag2) c = 10; // якщо обидва прапори true
// при виконанні двох і більше {} обов'язкові
if (myFlag) {
с = 10;
b = c;
} else {
с = 20;
b = a;
}
byte buttonState;
if (buttonState == 1) a = 10; // якщо buttonState 1
else if (buttonState == 2) a = 20; // якщо ні, але якщо buttonState 2
else a = 30; // якщо і це не вірно, то ось?
Скорочений запис умови: (логіка) ? правда : брехня.
int с = (a > b) ? 10 : -20; // якщо a > b, то с = 10. Якщо ні, то с = -20
boolean flag = true;
Serial.println( (flag) ? («прапор піднято») : («прапор опущено») );switch.. case
Оператор вибору, замінює конструкцію з else if.
switch (val) {
case 1:
// виконати, якщо val == 1
break;
case 2:
// виконати, якщо val == 2
break;
default:
// виконати, якщо val ні 1 ні 2
// default опціональний
break;
}Оператор break дуже важливий, дає змогу вийти з switch. Але можна використовувати так:
switch (val) {
case 1:
case 2:
case 3:
case 4:
// виконати, якщо val == 1, 2, 3 або 4
break;
case 5:
// виконати, якщо val == 5
break;
}Циклы (for, while)
for
Цикл – «лічильник». for (ініціалізація; умова; інкремент).
for (int i = 0; i < 10; i++) {
a = i; // a прийме значення від 0 до 9 на кожній ітерації
Serial.println(a); // виведення в порт
}
// для однієї дії {} не потрібні
for (int i = 0; i < 10; i++)
Serial.println(i); // виведення в портТакож використовується для створення замкнутих циклів, оскільки налаштування for необов’язкові. Вихід тільки через break або goto
for (;;;); // крутимося в циклі вічноwhile
Цикл із передумовою.
while (a < b) {
// виконується, поки a менше b
}Може бути використаний для створення замкнутого циклу, вихід тільки через break або goto
while (true) {
// крутимося в циклі вічно
}do.. while
Цикл із постумовою.
do {
// виконується, поки a менше b
} while (a < b);Відрізняється від while тим, що гарантовано виконається хоча б один раз
continue
Пропускає всі дії, що залишилися в тілі циклу, і переходить до наступної ітерації
break
Виходить із циклу
Оператори
Кома ,
Кома теж є оператором, використовується в таких випадках:
- Перерахування елементів у масивах
- Перерахування аргументів у функціях
- Виконання послідовності дій (зробити це І це)
Розглянемо третій випадок тут:
// оголосити a і b і дати їм значення
int a = 5, b = 10;
// присвоїти 3 до b
// додати 1 до b
// прирівняти до a
a = (b = 3, ++b); // a == 4
// оголосити i і j
// додавати i+1 і j+2
for (byte i = 0, j = 0; i < 10; i++, j += 2) {
// тут i змінюється від 0 до 9
// і j змінюється від 0 до 18
}Арифметичні
Арифметичні оператори – найпростіші та найзрозуміліші з усіх
- = присвоювання
- % залишок від ділення
- * множення
- / ділення
- + додавання
- – віднімання
Порівняння та логіка
- == рівність (a == b)
- != нерівність (a != b)
- = більше або дорівнює
- <= менше або дорівнює
- більше
- < менше
- ! логічне НЕ, заперечення. Аналог – оператор not
- && логічне І. Аналог – оператор and
- || логічне АБО. Аналог – оператор or
Складові оператори
- ++ (плюс плюс) інкремент: a++ рівносильно a = a + 1
- — (мінус мінус) декремент: a — рівносильно a = a – 1
- += складене додавання: a += 10 рівносильно a = a + 10
- -= складене віднімання: a -= 10 рівносильно a = a – 10
- *= складене множення: a *= 10 рівносильно a = a * 10
- /= складене ділення: a /= 10 рівносильно a = a / 10
- %= додати залишок від ділення: a %= 10 рівносильно a = a + a % 10
- &= складене бітове І: a &= b рівносильно a = a & b
- ^= складене виключне АБО: a ^= b рівносильно a = a ^ b
- |= складене АБО: a |= b рівносильно a = a | b
Бітові операції
- & бітове І
- << бітове зрушення вліво
- >> бітове зрушення вправо
- ^ бітове виключне АБО (аналогічний оператор – xor)
- | бітове АБО
- ~ бітове НЕ
Покажчики та посилання
- & – повертає адресу даних у пам’яті (адреса першого блоку даних)
- *- повертає значення за вказаною адресою
- -> – оператор непрямого звернення до членів і методів (для покажчиків на структури і класи). Є коротким записом конструкції через покажчик: a->b рівносильно (*a).b
Робота з даними
Типи даних, змінні
Змінна – елементарна комірка для зберігання даних (цифр). Змінні різних типів мають різний «розмір комірки» і мають різний ліміт на розмір числа.
| Назва | Альтернативна назва | Розмір | Діапазон | Особливість |
|---|---|---|---|---|
| boolean | bool | 1 байт | 0 або 1, true або false | Логічна змінна. bool на Arduino також займає 1 байт, а не біт! |
| char | – | 1 байт | -128… 127 | Зберігає номер символу з таблиці ASCII |
| – | int8_t | 1 байт | -128… 127 | Цілі числа |
| byte | uint8_t | 1 байт | 0… 255 | Цілі числа |
| int | int16_t, short | 2 байти | -32 768… 32 767 | Цілі числа |
| unsigned int | uint16_t, word | 2 байти | 0… 65 535 | Цілі числа |
| long | int32_t | 4 байти | -2 147 483 648… 2 147 483 647 | Цілі числа |
| unsigned long | uint32_t | 4 байти | 0… 4 294 967 295 | Цілі числа |
| float | – | 4 байти | -3.4E+38… 3.4E+38 | Числа з плаваючою комою (десяткові дроби). Точність: 6-7 знаків |
| double | – | 4 байти | -1.7E+308… 1.7E+308 | Для AVR те саме, що float. В інших випадках — 8 байтів |
| – | int64_t | 8 байтів | -(2^64)/2… (2^64)/2-1 | Цілі числа |
| – | uint64_t | 8 байтів | 0… 2^64-1 | Цілі числа |
Існує ще кілька спеціальних типів даних для символів.
wchar_t – 16 бітний символ
- char16_t – 2-х байтний char
- char32_t – 4-х байтний char
Також є таке поняття, як перевизначення типів даних (не створюючи нових типів), для цього використовується ключове слово typedef. Typedef працює таким чином: typedef <тип> <ім’я>; – створити новий тип даних <ім’я> на основі типу <тип>. Приклад:
typedef byte color;Створює тип даних під назвою color, який буде абсолютно ідентичний типу byte (тобто приймати 0-255). Тепер із цим типом можна створювати змінні:
color R, G, B;Створили три змінні типу color, який той самий byte, тільки в профіль. Це все!
Структури
Структура (struct) – дуже складний тип даних: сукупність різнотипних змінних, об’єднаних одним ім’ям.
struct <ярлик> {
<тип> <ім'я змінної 1>;
<тип> <ім'я змінної 2>;
<тип> <ім'я змінної 3>;
};Ярлик буде новим типом даних, і, використовуючи цей ярлик, можна оголошувати вже безпосередньо саму структуру:
<ярлик> <ім'я структури>; // оголосити одну структуру
<ярлик> <ім'я структури1>, <ім'я структури2>; // оголосити дві структури типу <ярлик>
<ярлик> <ім'я структури>[5]; // оголосити масив структурТакож є варіант оголошення структури без створення ярлика, тобто створюємо структуру, не оголошуючи її як тип даних зі своїм ім’ям.
struct {
<тип> <ім'я змінної 1>;
<тип> <ім'я змінної 2>;
<тип> <ім'я змінної 3>;
} <ім'я структури>;- Звернення до члена структури відбувається ось за такою схемою: <ім’я структури>.<ім’я змінної> і дає змогу змінювати або читати значення.
- Якщо дві структури мають однакову структуру (оголошені одним ярликом), то можна одну структуру просто прирівняти до іншої, всі змінні запишуться відповідно на свої місця.
- Ще одним зручним варіантом є присвоювання значення ось таким чином: <ім’я структури> = ( <ярлик> ) { <значення змінної 1>, <значення змінної 2>, <значення змінної 3> };
Перечисления
Перерахування (enum – enumeration) – тип даних, що являє собою набір іменованих констант, потрібен насамперед для зручності програміста.
Оголошення перерахування чимось схоже на оголошення структури:
enum <ярлик> {<ім'я1>, <ім'я2>, <ім'я3>, <ім'я4>, <ім'я5>};Таким чином ми оголосили ярлик. Тепер, використовуючи цей ярлик, можна оголосити саме перерахування:
<ярлик> <ім'я перерахування>;Так само як і у структур, можна оголосити перерахування без створення ярлика (навіщо нам зайвий рядок?):
enum {<ім'я1>, <ім'я2>, <ім'я3>, <ім'я4>, <ім'я5>} <ім'я перерахування>;Створене таким чином перерахування є змінною, яка може приймати зазначені для неї <імена>, також із цими іменами її можна порівнювати. Тепер найголовніше: імена для програми є числами, починаючи з 0 і далі по порядку збільшуючись на 1. В абстрактному прикладі вище <ім’я1> дорівнює 0, <ім’я2> дорівнює 1, <ім’я3> дорівнює 2, і так далі. Крім зазначених імен, перерахуванню можна прирівняти і число безпосередньо, але як би навіщо.
Класи
Класи в С++ – це основний і дуже потужний інструмент мови, більшість «бібліотек» є класами. Ієрархія така:
- Клас
- Об’єкт
- Властивості та методи
Клас оголошується таким чином:
class /*ім'я класу*/
{
private:
// список властивостей і методів для використання всередині класу
public:
// список методів доступних іншим функціям і об'єктам програми
protected:
// список засобів, доступних під час успадкування
};Масиви
Для оголошення масиву достатньо вказати квадратні дужки після імені змінної, тип даних – будь-який.
// вказуємо кількість комірок і все, далі можна з ними працювати
int myInts[6];
// вказуємо вміст комірок, компілятор сам порахує їхню кількість
int myPins[] = {2, 4, 8, 3, 6};
// вказуємо і те, і те, кількість клітинок у [ ] має збігатися з { } або бути більшою!
float Sens[3] = {0.2, 0.4, -8.5};
// зберігаємо символи
char message[6] = «hello»;
// пам'ятаємо, що порядок комірок починається з нуля!
myInts[0] = 10; // записати 10 у клітинку 0 масиву myIntsРядки (об’єкт String)
String – дуже потужний інструмент для роботи з рядками, тобто текстовими даними. Оголосити рядок можна кількома способами:
String string0 = «Hello String»; // заповнюємо словами в лапках
String string1 = String(«lol “) + String(”kek»); // сума двох рядків
String string2 = String('a'); // рядок із символу в одинарних лапках
String string3 = String(«This is string»); // конвертуємо рядок у String
String string4 = String(string3 + « more»); // складаємо рядок string3 з текстом у лапках
String string5 = String(13); // конвертуємо з числа в String
String string6 = String(20, DEC); // конвертуємо з числа із зазначенням базису (десятковий)
String string7 = String(45, HEX); // конвертуємо з числа із зазначенням базису (16-ричний)
String string8 = String(255, BIN); // конвертуємо з числа із зазначенням базису (двійковий)
String string9 = String(5.698, 3); // з float із зазначенням кількості знаків після коми (тут 3)
// можна формувати назву зі шматочків, наприклад для роботи з файлами
#define NAME «speed»
#define TYPE «-log»
#define EXT «.txt»
// при складанні досить вказати String 1 раз для першого рядка
String filename = String(NAME) + TYPE + EXT; // filename дорівнюватиме speed-log.txt
// доступ до елемента рядка працює за таким самим механізмом, як масив
string1[0] = «a»;
// тепер замість Hello String у нас aello StringРядки можна порівнювати, складати і віднімати, також для роботи з ними є купа функцій:
charAt()
myString.charAt(index);
Повертає елемент рядка myString під номером index. Аналог – myString[index];
setCharAt()
myString.setCharAt(index, val);
Записує в рядок myString символ val на позицію index. Аналог – myString[index] = val;
compareTo()
myString.compareTo(myString2);
- Повертає від’ємне число, якщо myString йде перед myString2.
- Повертає додатне число, якщо myString йде після myString2.
- Повертає 0, якщо рядки однакові.
concat()
myString.concat(value);
Приєднує value до рядка (value може мати будь-який числовий тип даних).
Повертає true у разі успішного виконання, false при помилці.
Аналог – додавання: myString + value.
endsWith()
myString.endsWith(myString2);
Перевіряє, чи закінчується myString символами з myString2.
У разі збігу повертає true.
startsWith()
myString.startsWith(myString2);
Перевіряє, чи починається myString символами з myString2.
У разі збігу повертає true.
equals()
myString.equals(myString2);
Повертає true, якщо myString збігається з myString2.
Регістр літер має значення.
equalsIgnoreCase()
myString.equalsIgnoreCase(myString2);
Повертає true, якщо myString збігається з myString2. Регістр літер не має значення.
indexOf()
myString.indexOf(val);
myString.indexOf(val, from);
Шукає і повертає номер (позицію) значення val у рядку, шукає зліва направо, повертає номер першого символа у збігу.
val може бути char або String, тобто можна шукати в рядку інший рядок або символ.
Можна почати пошук із заданої позиції from.
Якщо val не знайдено в рядку, повертає -1.
lastIndexOf()
myString.lastIndexOf(val);
myString.lastIndexOf(val, from);
Шукає і повертає номер (позицію) значення val у рядку, шукає справа наліво, повертає номер першого символа у збігу (зліва направо).
val може бути char або String, тобто можна шукати в рядку інший рядок або символ.
Можна почати пошук із заданої позиції from.
Якщо val не знайдено в рядку, повертає -1.
length()
myString.length();
Повертає довжину рядка у кількості символів.
remove()
myString.remove(index);
myString.remove(index, count);
Видаляє з рядка символи, починаючи з index і до кінця, або до вказаної кількості count.
replace()
myString.replace(substring1, substring2);
У рядку myString замінює послідовність символів substring1 на substring2.
String myString = "babushka Misha";
myString = myString.replace("babushka", "dedushka");
System.out.println(myString); // Виведе: dedushka Misha
reserve()
myString.reserve(size);
Резервує в пам’яті кількість байтів size для роботи з рядком.
c_str()
myString.trim();
Перетворює рядок у формат “C” (null-terminated string) і повертає вказівник на отриманий рядок.
substring()
myString.substring(from);
myString.substring(from, to);
Повертає частину рядка, що міститься в myString, починаючи з позиції from і до кінця, або до позиції to.
String myString = "lol kek 4eburek";
String chebur = myString.substring(8);
// рядок chebur містить "4eburek"
toCharArray()
myString.toCharArray(buf, len);
Розподіляє рядок в масив — буфер buf (типу char[]), починаючи з початку і до довжини len.
getBytes()
myString.getBytes(buf, len);
Копіює вказану кількість символів len (до unsigned int) в буфер buf (типу byte[])
toFloat()
myString.toFloat();
Повертає вміст рядка у тип даних float
toDouble()
myString.toDouble();
Повертає вміст рядка у тип даних double
toInt()
myString.toInt();
Цей метод перетворює рядок myString на значення типу int
String myString = "10500";
int val = myString.toInt();
// val теперь 10500toLowerCase()
myString.toLowerCase();
Цей метод змінює рядок myString, перетворюючи всі його символи на малі літери. Наприклад, якщо було “AAAAA”, стане “aaaaa”.
toUpperCase()
myString.toUpperCase();
Цей метод змінює рядок myString, перетворюючи всі його символи на великі літери. Наприклад, якщо було “aaaaa”, стане “AAAAA”
Специфікатори змінних
const— константа, змінна, яку не можна змінити (інакше буде помилка). const int val = 10;static— дозволяє оголосити локальну змінну всередині функції, і ця змінна не буде повторно ініціалізована при кожному виклику функції. Така собі локальна глобальна змінна.volatile— вказує компілятору, що змінну не слід оптимізувати, оскільки її значення може змінюватися зовні (наприклад, апаратно або в обробнику переривання). Такий специфікатор слід використовувати для змінних, значення яких можуть змінюватися в обробниках переривань або іншими потоками.extern— вказує компілятору, що ця змінна оголошена в іншому файлі програми, але ми хочемо використовувати саме її, а не створювати нову змінну з таким самим ім’ям у цьому файлі. Це дозволяє читати та змінювати змінні, створені в інших файлах (або бібліотеках).
Перетворення типів даних
Змінні різних типів даних можуть бути перетворені одна в одну — для цього достатньо вказати потрібний тип даних у дужках перед змінною, яку потрібно перетворити:
(тип_даних)змінна.
Результатом буде значення з новим типом даних, але тип самої змінної не зміниться (перетворення працює лише в межах однієї операції).
// змінна типу byte
byte val = 10;
// передаємо якійсь функції, яка очікує int
sendVal( (int)val );Таким чином можна перетворювати звичайні змінні, вказівники та інші типи даних.
А щодо рядків — їх ми вже розглядали вище.
- toInt()
- toFloat()
- toCharArray()
Інколи можна зустріти перетворення типів за допомогою оператора cast.
reinterpret_cast— Приведення типів без перевірки, безпосереднє вказівка компілятору. Застосовується лише в разі повної впевненості програміста у своїх діях. Не знімаєconstіvolatile, застосовується для приведення вказівника до вказівника, вказівника до цілого і навпаки.- static_cast — Перетворює вирази одного статичного типу в об’єкти і значення іншого статичного типу. Підтримується перетворення числових типів, вказівників і посилань по ієрархії наслідування як вгору, так і вниз. Перетворення перевіряється на рівні компіляції, і в разі помилки приведення типів буде виведено повідомлення.
dynamic_cast— Використовується для динамічного приведення типів під час виконання. У разі неправильного приведення типів для посилань викликається виключна ситуаціяstd::bad_cast, а для вказівників буде повернуто 0.- const_cast — найпростіше приведення типів. Знімає
constіvolatile, тобто константність і відмову від оптимізації компілятором змінної. Це перетворення перевіряється на рівні компіляції, і в разі помилки приведення типів буде виведено повідомлення.
Як користуватися: на прикладі попереднього прикладу
// переменная типа byte
byte val = 10;
// передаём какой-то функции, которая ожидает int
sendVal( static_cast<int>(val) );
