ESP8266 и ESP32 поддерживают Arduino-фреймворк, но с некоторыми отличиями и добавлениями. Рассмотрим некоторые из них.
Математика #
Деление на 0 #
В отличие от AVR, деление на 0 приводит к критической ошибке и перезагрузке МК - стараемся этого избегать.
Типы данных #
int,size_t- 32 битdouble- 64 бит- Указатель/ссылка - 32 бит
- Выравнивание памяти - 4 байта
При передаче данных между ESP и условно AVR, где int 16 битный, лучше использовать явные типы данных - short/long, а лучше int16_t/int32_t и так далее
Arduino-функции #
map() #
В функции map(val, min, max, to_min, to_max) нет защиты от деления на 0, поэтому если min равен max - МК перезагрузится. Если min и max задаются какими-то внешними условиями - проверяйте их равенство вручную и исключайте вызов map() с такими аргументами.
min() и max() #
Функции min() и max() реализованы как функции, а не как макросы, поэтому должны использоваться с данными одного типа. Использование переменных разного типа приведёт к ошибке компиляции:
unsigned long v;
v = max(v, 123); // ошибка
v = max(v, 123ul); // ОК
analogWrite #
У ESP32 (тестировалось на версии SDK 2.x) после вызова analogWrite пин перестаёт работать как обычный GPIO - не реагирует на digitalWrite, что не соответствует Ардуино-фреймворку, где digitalWrite отключает ШИМ. Для отключения генерации ШИМ нужно вручную вызвать ledcDetachPin(пин).
printf #
В отличие от классических Arduino, в ESP к классу Print добавлен метод printf - форматированный вывод (см. урок про него). Это означает, что можно печатать с форматированием в разные интерфейсы, например выводить в монитор порта как
Serial.printf("Hello %d world", 12); // Hello 12 world
Прерывания #
- В обработчике нельзя использовать динамическое выделение и перераспределение памяти (
new,malloc,realloc), соответственно работать соStringтоже нельзя - В прерывании нельзя использовать задержки
- Функция-обработчик должна быть объявлена со специальным атрибутом, он размещает код функции в оперативной памяти:
- ESP8266 -
IRAM_ATTR - ESP32 -
ARDUINO_ISR_ATTR
- ESP8266 -
// IRAM_ATTR void isr() {} // ESP8266
// void ARDUINO_ISR_ATTR isr() {} // ESP32
void setup() {
attachInterrupt(1, isr, RISING);
}
Есть ещё атрибут DRAM_ATTR - у объявленной с ним функции находящиеся внутри static const переменные также будут размещены в оперативной памяти. При использовании IRAM_ATTR такие переменные компилятор может разместить во Flash памяти.
EEPROM #
Основной урок про EEPROM
EEPROM является эмуляцией из Flash памяти, поэтому мы можем выбрать нужный размер:
- Перед началом работы нужно вызвать
EEPROM.begin(4.. 4096)с указанием размера области памяти в байтах - Для применения изменений в памяти нужно вызвать
EEPROM.commit() - В некоторых версиях SDK отсутствует
EEPROM.update()иEEPROM.length() - У Flash памяти небольшой ресурс - всего около 10'000 перезаписей. У фирменной памяти Winbond (можно найти на некоторых моделях ESP-12 и прочих) - около 50'000 перезаписей
Важно: EEPROM реализован следующим образом: после запуска EEPROM.begin(4.. 4096) содержимое EEPROM указанного размера дублируется в оперативной памяти. После любого изменения и вызова EEPROM.commit() стирается весь блок Flash памяти (4 кБ) и записывается заново. Таким образом ресурс "EEPROM" памяти у ESP вырабатывается довольно быстро и весь сразу, а не по ячейкам.
Вместо EEPROM используйте библиотеку FileData для удобного хранения любых данных в файловой системе
PROGMEM #
Основной урок про PROGMEM
ESP8266 #
PROGMEM работает как на AVR, без него данные могут располагаться в оперативной памяти. Для совместимости есть даже _P и pgm_-функции из avrlibs.
ESP32 #
Начиная с 2.x версии, const-данные (строки, массивы и т.д.) автоматически располагаются во Flash памяти и сами читаются оттуда. Arduino PROGMEM API полностью поддерживается для совместимости со скетчами от AVR/ESP8266, но по сути ничего не делает.
UART/Serial #
Основной урок про Serial
ESP8266 #
Имеет 1.5 (полтора) аппаратных UART:
Serial(UART0) - подключен к USB-UART для прошивки и отладки- RX -
3 - TX -
1 - При вызове
Serial.swap()(послеSerial.begin()) пины переместятся на TX15и RX13. После повторного вызова - обратно
- RX -
Serial1- доступен только на отправку- RX - нет
- TX -
2
ESP32 #
Имеет 3 аппаратных UART:
| UART | Serial | RX | TX |
|---|---|---|---|
| UART0 | Serial |
3 | 1 |
| UART1 | Serial1 |
9* | 10* |
| UART2 | Serial2 |
16 | 17 |
- *
Serial1по умолчанию использует системные пины и не будет работать!
На ESP32 пины аппаратных UART можно переназначить на любые другие (кроме системных, SPI Flash, а также внимательнее к тем которые только для чтения):
void begin(uint32_t baud); // стандартные пины
void begin(uint32_t baud, uint32_t config = SERIAL_8N1, int8_t rxPin, int8_t txPin); // свои пины
void setup() {
Serial.begin(9600, SERIAL_8N1, 1, 2);
Serial1.begin(9600, SERIAL_8N1, 3, 4);
Serial2.begin(115200, SERIAL_8N1, 5, 6);
}
I2C/Wire #
Основной урок про Wire
ESP8266 #
Используется программная реализация I2C, стандартные пины как на Arduino Nano:
- SDA -
4 - SCL -
5
При запуске можно переназначить пины на любые другие:
void begin(); // стандартные пины
void begin(int sda, int scl); // свои пины
void begin(int sda, int scl, uint8_t addr); // свои пины, свой адрес (режим slave)
ESP32 #
Имеет 2 аппаратных I2C: системные объекты Wire и Wire1. При вызове begin() устанавливаются стандартные пины:
- SDA -
21 - SCL -
22
При запуске можно переназначить пины на любые другие (кроме системных, SPI Flash, а также внимательнее к тем которые только для чтения). Порядок аргументов отличается от ESP8266:
void begin(); // стандартные пины
bool begin(int sda, int scl, uint32_t frequency = 0); // свои пины, частота
bool begin(uint8_t addr, int sda, int scl, uint32_t frequency); // адрес (slave), пины, частота
Для использования Wire1 нужно обязательно указать ему пины!
void setup() {
Wire.begin(); // Wire на пинах 21 22
Wire1.begin(23, 24); // Wire1 на пинах 23 24
}
SPI #
Основной урок про SPI
ESP8266 #
Один аппаратный SPI, пины привязаны к стандартным:
| MISO | MOSI | SCK | SS |
|---|---|---|---|
| 12 | 13 | 14 | 15 |
Пин SS должен работать в режиме OUTPUT. Если он будет INPUT с сигналом LOW - SPI перейдёт в режим Slave.
void setup() {
SPI.begin();
pinMode(SS, OUTPUT);
}
ESP32 #
Несколько аппаратных SPI, но доступны только два - HSPI и VSPI, их пины по умолчанию (см. распиновку конкретной платы, на некоторых моделях пины отличаются):
| SPI | MISO | MOSI | SCK | SS |
|---|---|---|---|---|
| VSPI | 19 | 23 | 18 | 5 |
| HSPI | 12 | 13 | 14 | 15 |
Системный объект SPI - это VSPI. При вызове begin() будет работать на указанных выше пинах. Пины можно указать любые другие (кроме системных, SPI Flash, а также внимательнее к тем которые только для чтения):
void begin(); // запуск на стандартных пинах
void begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss); // свои пины
Для использования второго SPI (HSPI) нужно самостоятельно создать объект класса SPIClass и передать ему константу HSPI:
SPIClass hSPI(HSPI); // создание HSPI
// точно так же можно создать VSPI - "синоним" системного объекта SPI
// SPIClass vSPI(VSPI); // == SPI
void setup() {
// VSPI, стандартные пины
SPI.begin();
// VSPI, свои пины
SPI.begin(4, 5, 6, 7);
pinMode(7, OUTPUT); // обязательно для SS (SPI Master)
// HSPI, стандартные пины
hSPI.begin()
// HSPI, свои пины
hSPI.begin(8, 9, 10, 11);
pinMode(11, OUTPUT); // обязательно для SS (SPI Master)
}
Полезные страницы #
- Набор GyverKIT – наш большой стартовый набор Arduino, продаётся в России
- Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress
- Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту ([email protected])
- Поддержать автора за работу над уроками