Одним из преимуществ ESP32 является то, что у него гораздо больше GPIO, чем у ESP8266. Вам не придется манипулировать или мультиплексировать контакты ввода-вывода. Однако есть несколько вещей, которые следует учитывать, поэтому внимательно прочитайте распиновку.
Если вы раньше играли с Arduino, то знаете, насколько просто сгенерировать ШИМ-сигнал с по.webpмощью этойanalogWrite() функции — просто укажите используемый вывод и рабочий цикл, и все готово.
Но с ESP32 это похоже на игру на более сложном уровне.Мы получаем больше элементов управления (ура!), но нам также приходится управлять ими с умом (что немного сложнее).ESP32 просит нас уточнить еще несколько вещей, таких как частота ШИМ, разрешение ШИМ, используемый канал и, конечно же, рабочий цикл и номер контакта.Уф, это звучит как много, но не волнуйтесь!
Это руководство научит вас всему, что вам нужно знать о ШИМ на ESP32, от основных концепций до практических примеров.
На ESP32 выход ШИМ возможен на всех контактах GPIO, за исключением четырех контактов GPIO, предназначенных только для ввода.Выделенные ниже GPIO поддерживают ШИМ.
ESP32 имеет два периферийных устройства ШИМ:периферийное устройство управления светодиодами (LEDC)ипериферийное устройство широтно-импульсного модулятора управления двигателем (MCPWM).
Периферийное устройство MCPWM предназначено для управления двигателем и включает в себя дополнительные функции, такие как мертвая зона и автоматическое торможение.С другой стороны, периферийное устройство LEDC специально разработано для управления светодиодами и включает в себя такие функции, как автоматическое затемнение, а также более продвинутые функции.Однако его можно использовать для генерации сигналов ШИМ для множества других целей.
В этом уроке мы сосредоточимся в первую очередь на периферийных устройствах LEDC.
Периферийное устройство LEDC, как и большинство контроллеров ШИМ, использует таймер для генерации сигналов ШИМ.
Думайте о таймере как о том, что он «тикает», считая до тех пор, пока не достигнет максимального значения, после чего он сбрасывается до нуля, и следующий цикл счета начинается снова.Время между этими сбросами (т. е. время, необходимое для достижения максимального значения) представляетчастоту ШИМи измеряется в герцах (Гц).Например, если мы укажем частоту 1 Гц, таймеру понадобится 1 секунда, чтобы отсчитать от 0 до максимального значения, прежде чем начать следующий цикл.Если мы укажем частоту 1000 Гц, таймеру понадобится всего 1 миллисекунда, чтобы отсчитать от 0 до максимального значения.
ESP32 может генерировать сигнал ШИМ с частотой до 40 МГц.
Итак, что же представляет собой это «максимальное» значение?«Максимальное» значение определяется разрешениемШИМ.Если разрешение ШИМ составляет «n» бит, таймер считает от 0 до 2n-1 перед сбросом.Например, если мы настроим таймер с частотой 1 Гц и разрешением 8 бит, таймеру понадобится 1 секунда, чтобы отсчитать от 0 до 255 (28).В случае частоты 1 Гц и разрешения 16 бит таймер все равно будет занимать 1 секунду, но будет считать от 0 до 65 535 (216).
Важно понимать, что когда у нас более высокое разрешение, у нас, по сути, больше «приращений таймера» в течение одного и того же заданного периода времени.Таким образом, мы имеем большую «детальность» во времени.
Разрешение ШИМ ESP32 можно регулировать от 1 до 16 бит.Это означает, что рабочий цикл может быть установлен на 65 536 (216) различных уровнях.Это дает вам точный контроль над такими вещами, как светодиоды, позволяющие им светиться с небольшими изменениями яркости, или моторы, позволяющие им работать с очень точной скоростью.
Далее мы определяемрабочий циклвыхода ШИМ.Рабочий цикл показывает, сколько тактов таймера будет оставаться на высоком уровне на выходе ШИМ, прежде чем он упадет на низкий уровень.Это значение сохраняется в регистре захвата/сравнения таймера (CCR).
Когда таймер сбрасывается, выход ШИМ становится высоким.Когда таймер достигает значения, хранящегося в регистре захвата/сравнения, выходной сигнал ШИМ переходит в низкий уровень.Однако таймер продолжает отсчет.Как только таймер достигает максимального значения, выход ШИМ снова становится высоким, и таймер сбрасывается, чтобы начать отсчет следующего периода.
Например, представьте, что мы хотим сгенерировать сигнал ШИМ с частотой 1000 Гц, разрешением 8 бит и рабочим циклом 75%.Учитывая 8-битное разрешение, максимальное значение таймера будет 255 (28-1).При частоте 1000 Гц таймеру потребуется 1 мс (0,001 с) для отсчета от 0 до 255. Коэффициент заполнения ШИМ установлен на 75 %, это означает, что значение 256 * 75 % = 192 будет храниться в регистр захвата/сравнения.В этом случае при сбросе таймера выход ШИМ будет установлен на высокий уровень.Выход ШИМ будет оставаться высоким до тех пор, пока счетчик не достигнет 192, после чего он переключится на низкий уровень.Как только таймер достигнет значения 255, выход ШИМ снова переключится на высокий уровень, и таймер сбросится, чтобы начать отсчет следующего периода.
Теперь обратим внимание на понятие канала.Канал представляет собой уникальный выходной сигнал ШИМ.
ESP32 имеет 16 каналов, что означает, что он может генерировать 16 уникальных сигналов ШИМ.Эти каналы разделены на две группы, каждая из которых содержит по 8 каналов: 8 высокоскоростных каналов и 8 низкоскоростных каналов.
Высокоскоростные каналы реализованы аппаратно и поэтому способны обеспечить автоматическое и безотказное изменение рабочего цикла ШИМ.С другой стороны, низкоскоростные каналы лишены этих функций и полагаются на программное обеспечение для изменения своего рабочего цикла.
В каждой группе имеется 4 таймера, совместно используемых 8 каналами, что означает, что каждые два канала используют один и тот же таймер.Поскольку частоту определяет таймер, важно понимать, что мы не можем регулировать частоту каждого канала независимо внутри пары.Однако мы можем управлять рабочим циклом ШИМ каждого канала независимо.
Подводя итог, можно сказать, что ESP32 имеет 16 каналов ШИМ, которые могут работать на восьми различных частотах, и каждый из этих каналов может работать с разным рабочим циклом.
Чтобы сгенерировать сигнал ШИМ на определенном выводе, вы «прикрепляете» этот вывод к каналу.Эта связь сообщает ESP32 о необходимости вывода сигнала ШИМ, сгенерированного каналом, на указанный вывод.К одному и тому же каналу можно подключить несколько контактов, что означает, что все они могут выводить один и тот же сигнал ШИМ.Несмотря на то, что все контакты GPIO поддерживают выход ШИМ, ESP32 имеет только 16 каналов, поэтому одновременно может генерироваться только 16 различных форм сигналов ШИМ.Это не ограничивает количество контактов, которые могут выводить сигналы ШИМ, но ограничивает разнообразие сигналов, которые могут выводиться одновременно.
Фактически, если у вас есть набор светодиодов, которые вы хотите мигать идеально синхронно, вы можете настроить один канал с определенной частотой и рабочим циклом, а затем подключить к нему все соответствующие контакты (которые подключены к светодиодам). канал.Однако при работе с сервоприводами, особенно в таких ситуациях, как роботизированная рука, где каждый сустав (сервопривод) должен управляться независимо, становится выгодным назначать разные контакты разным каналам.
ESP32 может генерировать сигнал ШИМ с частотой до 40 МГц, а разрешение ШИМ можно регулировать от 1 до 16 бит.Но это не значит, что можно одновременно установить частоту 40 МГц и разрешение 16 бит.Это связано с тем, что максимальная частота ШИМ и разрешение зависят от источника синхронизации.
Чтобы проиллюстрировать это, рассмотрим часы (будь то часы процессора или таймер, не имеет значения), работающие на частоте 40 МГц.При этом максимально достижимая частота ШИМ также составляет 40 МГц.Мы не можем генерировать ШИМ-волну быстрее, чем позволяют наши часы.
А что с разрешением?Что ж, разрешение на самом деле зависит от того, насколько точно мы можем разделить один период волны ШИМ на разные рабочие циклы.И вот в чем суть: для разделения волны ШИМ требуется тактовая частота процессора, работающая на частоте PWM_freq * 2PWM_solve.Почему?Потому что для создания этих рабочих циклов вам нужно иметь возможность создавать эти временные интервалы.
Отсюда становятся ясными два важных момента:
Согласнодокументации Espressif, источником тактовой частоты низкоскоростного таймера LEDC является тактовая частота APB 80 МГц.В качестве общего руководства следует стремиться поддерживать частоту PWM_freq * 2PWM_solveниже 80 МГц.
Кроме того, документация Espressif включает примеры, подтверждающие это:
Если все это не имеет для вас смысла, подумайте вот о чем: Arduino Uno обеспечивает форму ШИМ-сигнала ~490 Гц с разрядностью 8 бит.Этого более чем достаточно для плавного затухания светодиода.Так что всегда можно начать с этого (частота 500 Гц, разрешение 8 бит), а потом поиграться.
Давайте приступим к делу!Ядро ESP32 Arduino включает в себябиблиотеку LEDC, которая упрощает управление широтно-импульсной модуляцией (ШИМ) на ESP32.Хотя библиотека LEDC была разработана для управления светодиодами, ее также можно использовать и для других приложений, где полезны сигналы ШИМ, например, для воспроизведения «музыки» через пьезодинамики и приводные двигатели.
Следующие шаги показывают, как использовать библиотеку LEDC для генерации сигнала ШИМ с помощью ESP32 с использованием Arduino IDE.
Вот краткий пример наброска, показывающий, как затухать светодиод — идеально подходит для демонстрации генерации ШИМ на ESP32.
Схема подключения довольно проста.Возьмите светодиод и токоограничивающий резистор сопротивлением 330 Ом и поместите их на макетную плату, как показано на рисунке ниже.Подключите более длинную ножку светодиода (анод) к контакту GP18 через резистор сопротивлением 330 Ом, а более короткую ножку подключите к контакту заземления вашего ESP32.
Скопируйте приведенный ниже код в свою Arduino IDE.
const int PWM_CHANNEL = 0; // ESP32 has 16 channels which can generate 16 independent waveforms const int PWM_FREQ = 500; // Recall that Arduino Uno is ~490 Hz. Official ESP32 example uses 5,000Hz const int PWM_RESOLUTION = 8; // We'll use same resolution as Uno (8 bits, 0-255) but ESP32 can go up to 16 bits // The max duty cycle value based on PWM resolution (will be 255 if resolution is 8 bits) const int MAX_DUTY_CYCLE = (int)(pow(2, PWM_RESOLUTION) - 1); const int LED_OUTPUT_PIN = 18; const int DELAY_MS = 4; // delay between fade increments void setup() { // Sets up a channel (0-15), a PWM duty cycle frequency, and a PWM resolution (1 - 16 bits) // ledcSetup(uint8_t channel, double freq, uint8_t resolution_bits); ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION); // ledcAttachPin(uint8_t pin, uint8_t channel); ledcAttachPin(LED_OUTPUT_PIN, PWM_CHANNEL); } void loop() { // fade up PWM on given channel for(int dutyCycle = 0; dutyCycle <= MAX_DUTY_CYCLE; dutyCycle++){ ledcWrite(PWM_CHANNEL, dutyCycle); delay(DELAY_MS); } // fade down PWM on given channel for(int dutyCycle = MAX_DUTY_CYCLE; dutyCycle >= 0; dutyCycle--){ ledcWrite(PWM_CHANNEL, dutyCycle); delay(DELAY_MS); } }
Теперь загрузите код в свой ESP32.Вы увидите плавное изменение яркости светодиода от полностью выключенного до полностью горящего и обратно.
В начале эскиза определены несколько констант для настройки характеристик ШИМ.PWM_CHANNELСначала определяетсяконстанта и устанавливается ее значение 0. ESP32 имеет 16 каналов (от 0 до 15), каждый из которых может генерировать независимые сигналы.
ЗатемPWM_FREQопределяется и устанавливается на 500. Это частота нашего ШИМ-сигнала.Напомним, что Arduino Uno использует ~490 Гц.Этого достаточно для плавного затухания светодиода.
ДалееPWM_RESOLUTIONустановлено значение 8. Это разрешение (в битах) сигнала ШИМ.Хотя мы используем 8 бит (так же, как Arduino Uno), ESP32 может работать до 16 бит.
const int PWM_CHANNEL = 0; const int PWM_FREQ = 500; const int PWM_RESOLUTION = 8;
После этогоMAX_DUTY_CYCLEрассчитывается по формуле 2PWM_RESOLUTION−1.Это значение определяет максимально достижимый рабочий цикл на основе выбранного разрешения.
const int MAX_DUTY_CYCLE = (int)(pow(2, PWM_RESOLUTION) - 1);
После этогоLED_OUTPUT_PINему присвоено значение 18. Это контакт ESP32 GPIO, к которому подключен светодиод.
И, наконец,DELAY_MSопределяется и устанавливается равным 4. Это задержка (в миллисекундах) между приращениями для управления скоростью затухания светодиода.
const int LED_OUTPUT_PIN = 18; const int DELAY_MS = 4;
Во время настройкиledcSetup()вызывается функция для настройки свойств ШИМ с использованием ранее определенных констант.Эта функция принимает три аргумента: канал ШИМ, частоту ШИМ и разрешение ШИМ.
ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
ДалееledcAttachPin()функция используется для подключения вывода GPIO к каналу ШИМ, отвечающему за генерацию сигнала ШИМ.В этом случае сигнал ШИМ, генерируемыйPWM_CHANNEL, который соответствует каналу 0, появится наLED_OUTPUT_PIN, что соответствует GPIO 16.
ledcAttachPin(LED_OUTPUT_PIN, PWM_CHANNEL);
В цикле первыйforцикл итеративно увеличивает коэффициент заполнения от 0 до максимально возможного значения (MAX_DUTY_CYCLE).Это постепенно увеличивает яркость светодиода.
for(int dutyCycle = 0; dutyCycle <= MAX_DUTY_CYCLE; dutyCycle++){ ledcWrite(PWM_CHANNEL, dutyCycle); delay(DELAY_MS); }
Второйforцикл уменьшает рабочий цикл отMAX_DUTY_CYCLE0: это постепенно уменьшает яркость светодиода.
for(int dutyCycle = MAX_DUTY_CYCLE; dutyCycle >= 0; dutyCycle--){ ledcWrite(PWM_CHANNEL, dutyCycle); delay(DELAY_MS); }
В обоих циклах forledcWrite()функция используется для установки яркости светодиода.Эта функция принимает в качестве аргументов канал, генерирующий сигнал, и рабочий цикл.
ledcWrite(ledChannel, dutyCycle);
Вы можете получить один и тот же сигнал ШИМ на нескольких GPIO одновременно.Для этого вам просто нужно подключить эти GPIO к одному и тому же каналу.
Добавьте в схему еще два светодиода так же, как вы делали это с первым.Подключите их к GPIO 19 и 21.
На изображении ниже показано, как все подключить.
Теперь давайте изменим предыдущий пример, чтобы затухать три светодиода, используя один и тот же сигнал ШИМ из одного и того же канала.
const int PWM_CHANNEL = 0; // ESP32 has 16 channels which can generate 16 independent waveforms const int PWM_FREQ = 500; // Recall that Arduino Uno is ~490 Hz. Official ESP32 example uses 5,000Hz const int PWM_RESOLUTION = 8; // We'll use same resolution as Uno (8 bits, 0-255) but ESP32 can go up to 16 bits // The max duty cycle value based on PWM resolution (will be 255 if resolution is 8 bits) const int MAX_DUTY_CYCLE = (int)(pow(2, PWM_RESOLUTION) - 1); const int LED_1_OUTPUT_PIN = 18; const int LED_2_OUTPUT_PIN = 19; const int LED_3_OUTPUT_PIN = 21; const int DELAY_MS = 4; // delay between fade increments void setup() { // Sets up a channel (0-15), a PWM duty cycle frequency, and a PWM resolution (1 - 16 bits) // ledcSetup(uint8_t channel, double freq, uint8_t resolution_bits); ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION); // ledcAttachPin(uint8_t pin, uint8_t channel); ledcAttachPin(LED_1_OUTPUT_PIN, PWM_CHANNEL); ledcAttachPin(LED_2_OUTPUT_PIN, PWM_CHANNEL); ledcAttachPin(LED_3_OUTPUT_PIN, PWM_CHANNEL); } void loop() { // fade up PWM on given channel for(int dutyCycle = 0; dutyCycle <= MAX_DUTY_CYCLE; dutyCycle++){ ledcWrite(PWM_CHANNEL, dutyCycle); delay(DELAY_MS); } // fade down PWM on given channel for(int dutyCycle = MAX_DUTY_CYCLE; dutyCycle >= 0; dutyCycle--){ ledcWrite(PWM_CHANNEL, dutyCycle); delay(DELAY_MS); } }
Теперь загрузите код в свой ESP32.Вы увидите, что все три светодиода погаснут одновременно, поскольку все GPIO выдают один и тот же сигнал ШИМ.
Если вы сравните этот эскиз с первым, вы заметите, что они очень похожи, с небольшими отличиями.Давайте посмотрим на эти различия.
В глобальной областиопределены три дополнительные константы с именамиLED_1_OUTPUT_PIN,LED_2_OUTPUT_PINи , которым присвоены значения 18, 19 и 21 соответственно.LED_3_OUTPUT_PINЭто указывает на то, что мы имеем дело с тремя отдельными светодиодами, каждый из которых подключен к своему выводу GPIO на ESP32.
const int LED_1_OUTPUT_PIN = 18; const int LED_2_OUTPUT_PIN = 19; const int LED_3_OUTPUT_PIN = 21;
Затем при настройкеledcAttachPin()функция вызывается три раза, а не один раз, как в предыдущем коде.Каждый вызов функции подключает другой вывод GPIO (LED_1_OUTPUT_PIN,LED_2_OUTPUT_PIN,LED_3_OUTPUT_PIN) с одним и тем же каналом ШИМ (PWM_CHANNEL), что означает, что на все три светодиода будет выводиться один и тот же сигнал ШИМ.
ledcAttachPin(LED_1_OUTPUT_PIN, PWM_CHANNEL); ledcAttachPin(LED_2_OUTPUT_PIN, PWM_CHANNEL); ledcAttachPin(LED_3_OUTPUT_PIN, PWM_CHANNEL);
Обратите внимание: несмотря на добавление большего количества светодиодов, функция не измениласьloop().Это связано с тем, что один и тот же канал ШИМ управляет всеми светодиодами.
В этом примере эскиза показано, как уменьшить яркость светодиода с помощью потенциометра.
Удалите два дополнительных светодиода, которые вы добавили в схему, и добавьте потенциометр.Подключите один внешний контакт потенциометра к 3,3 В, противоположный внешний контакт к GND, а его средний контакт (движок) к GPIO 34.
На изображении ниже показано, как все подключить.
const int PWM_CHANNEL = 0; // ESP32 has 16 channels which can generate 16 independent waveforms const int PWM_FREQ = 500; // Recall that Arduino Uno is ~490 Hz. Official ESP32 example uses 5,000Hz const int PWM_RESOLUTION = 8; // We'll use same resolution as Uno (8 bits, 0-255) but ESP32 can go up to 16 bits // The max duty cycle value based on PWM resolution (will be 255 if resolution is 8 bits) const int MAX_DUTY_CYCLE = (int)(pow(2, PWM_RESOLUTION) - 1); const int LED_OUTPUT_PIN = 18; const int POT_PIN = 34; const int DELAY_MS = 100; // delay between fade increments void setup() { // Sets up a channel (0-15), a PWM duty cycle frequency, and a PWM resolution (1 - 16 bits) // ledcSetup(uint8_t channel, double freq, uint8_t resolution_bits); ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION); // ledcAttachPin(uint8_t pin, uint8_t channel); ledcAttachPin(LED_OUTPUT_PIN, PWM_CHANNEL); } void loop() { int dutyCycle = analogRead(POT_PIN); dutyCycle = map(dutyCycle, 0, 4095, 0, MAX_DUTY_CYCLE); ledcWrite(PWM_CHANNEL, dutyCycle); delay(DELAY_MS); }
Теперь попробуйте повернуть потенциометр до упора в одну сторону, затем до упора в другую.Следите за светодиодом;на этот раз вы увидите плавное изменение яркости светодиода: от полного выключения на одном конце ручки потенциометра до полного свечения на другом.
Снова!Между этим эскизом и первым есть лишь несколько отличий.Давайте посмотрим на эти различия.
Дополнительная константа с именемPOT_PINопределяется в глобальной области.Ему присвоено значение 34, что указывает на то, что потенциометр подключен к GPIO 34 на ESP32 и будет использоваться для динамического определения рабочего цикла и, следовательно, яркости светодиода.
const int POT_PIN = 34;
Затем в цикле вместо использования циклов for для постепенного увеличения и уменьшения яркости светодиодаanalogRead(POT_PIN)вызывается функция, которая принимает необработанные показания потенциометра.
int dutyCycle = analogRead(POT_PIN);
Показания потенциометра в диапазоне от 0 до 4095 затем преобразуются в новый диапазон от 0 доMAX_DUTY_CYCLEиспользованияmap()функции.Это сопоставление приводит значения потенциометра в соответствие с допустимыми значениями рабочего цикла ШИМ-сигнала.Это гарантирует возможность изменения яркости светодиода во всем диапазоне.
dutyCycle = map(dutyCycle, 0, 4095, 0, MAX_DUTY_CYCLE);
Наконец,ledcWrite()функция принимает это сопоставленное значение и применяет его непосредственно к сигналу ШИМ, регулируя яркость светодиода в режиме реального времени в зависимости от положения потенциометра.
ledcWrite(PWM_CHANNEL, dutyCycle);
В этой статье из серии «Основы ESP32» показано, как считывать аналоговые значения с помощью ESP32 с использованием Arduino IDE.
Это полезно для считывания данных с самых разных датчиков и переменных компонентов, включая, помимо прочего, подстроечные резисторы, джойстики, ползунки и чувствительные к силе резисторы.
ESP32 включает в себя два 12-битных АЦП SAR – АЦП1 и АЦП2 – и поддерживает измерения по 18 каналам (аналоговые контакты). ADC1 доступен на восьми GPIO (от 32 до 39), а ADC2 доступен на десяти GPIO (0, 2, 4, с 12 по 15 и с 25 по 27).
Однако плата DEVKIT V1 DOIT (версия с 30 GPIO) имеет всего 15 каналов АЦП, как показано на рисунке ниже.
АЦП вашего ESP32 имеет разрешение 12 бит, что означает, что он может обнаруживать 4096 (2 ^ 12) дискретных аналоговых уровней. Другими словами, он преобразует входные напряжения в диапазоне от 0 до 3,3 В (рабочее напряжение) в целочисленные значения от 0 до 4095. В результате разрешение составляет 3,3 В/4096 единиц, или 0,0008 В (0,8 мВ) на единицу.
Более того, разрешение АЦП и диапазон каналов можно задавать программно.
По правде говоря, АЦП — не самая сильная сторона ESP32. Есть несколько ограничений, о которых вам следует знать.
Контакты ADC2 нельзя использовать, когда включен Wi-Fi. Поскольку существует большая вероятность использования Wi-Fi на микроконтроллере, предназначенном для его использования, можно использовать только ADC1.
АЦП ESP32 может измерять напряжение только в диапазоне от 0 до 3,3 В. Вы не можете напрямую измерить аналоговое напряжение от 0 до 5 В.
В идеале при использовании АЦП можно было бы ожидать линейного поведения, но это не так. Преобразователи АЦП на ESP32 имеют нелинейный характер. Более подробную информацию об этом вы можете найти в обсуждении на GitHub .
На графике ниже хорошо видны нелинейности на нижнем и верхнем концах входного напряжения.
По сути, это означает, что ESP32 не может отличить 3,2 В от 3,3 В; измеренное значение будет таким же (4095). Точно так же он не может различать сигналы 0 В и 0,13 В; измеренное значение будет таким же (0).
Электрический шум АЦП предполагает небольшие колебания результатов измерений.
Однако это можно исправить добавлением конденсатора на выходе и передискретизацией.
Считать аналоговые значения с вывода GPIO очень просто. В Arduino IDE вы используете analogRead()
функцию, которая принимает в качестве аргумента номер контакта GPIO, который вы хотите прочитать.
analogRead(GPIO);
Чтобы продемонстрировать, как использовать АЦП на ESP32, мы будем использовать простой пример, который считывает аналоговое значение с потенциометра.
Давайте создадим простую схему потенциометра для этого примера.
Начните с вставки потенциометра в макет. Подключите средний контакт к контакту GPIO 34 на вашем ESP32. Наконец, соедините один из внешних контактов потенциометра (неважно какой) с контактом 3V3 ESP32, а другой — с землей.
Загрузите следующий эскиз на свой ESP32. Этот скетч просто считывает показания потенциометра и выводит результаты на последовательный монитор.
// Potentiometer is connected to GPIO 34 (Analog ADC1_CH6)
34;
// variable for storing the potentiometer value
0;
setup() {
Serial.115200);
1000);
}
loop() {
// Reading potentiometer value
potValue = analogRead(potPin);
Serial.<span);
Serial.println(potValue);
500);
}
После загрузки эскиза откройте последовательный монитор на скорости 115200 бод и нажмите кнопку EN на ESP32.
Вы должны увидеть значение от 0 до 4095, в зависимости от текущего поворота ручки, которое будет распечатано на последовательном мониторе. Попробуйте повернуть ручку потенциометра и посмотреть, как изменяются значения.
Эскиз начинается с определения контакта GPIO, к которому подключен потенциометр, в данном случае это GPIO 34.
34;
Также определена переменная для хранения значений потенциометра.
0;
В setup() мы инициализируем последовательную связь с ПК.
Serial.115200);
В цикле функция analogRead()
используется для считывания напряжения на potPin
. Возвращаемое значение сохраняется в переменной potValue
.
potValue = analogRead(potPin);
Наконец, значения, считанные с потенциометра, выводятся на последовательный монитор.
Serial.<span);
Serial.println(potValue);
potPin
не нужно устанавливать в качестве входных данных. Это делается автоматически каждый раз, когда вы звоните analogRead()
.
Есть и другие функции АЦП, которые могут быть полезны в других проектах:
analogReadMilliVolts(pin)
: получить значение АЦП для данного вывода/канала АЦП в милливольтах.analogReadResolution(bits)
: устанавливает биты выборки и разрешение чтения. По умолчанию установлено разрешение 12 бит. Диапазон: от 9 (0–511) до 12 бит (0–4095).analogSetWidth(bits)
: устанавливает аппаратные биты выборки и разрешение чтения. По умолчанию установлено разрешение 12 бит. Диапазон: от 9 до 12 бит. 9 бит = 0–511, 10 бит = 0–1023, 11 бит = 0–2047 и 12 бит = 0–4095.analogSetCycles(cycles)
: устанавливает количество циклов на выборку. По умолчанию — 8. Диапазон: от 1 до 255.analogSetSamples(samples)
: устанавливает количество выборок в диапазоне. По умолчанию — 1 образец. Оказывает эффект повышения чувствительности.analogSetClockp(clockp)
: устанавливает делитель тактовой частоты АЦП. По умолчанию — 1. Диапазон: от 1 до 255.analogSetAttenuation(attenuation)
: устанавливает входное затухание для всех выводов АЦП. По умолчанию ADC_11db
. Принятые значения:ADC_0db
: затухание не устанавливается (диапазон измеряемого входного напряжения = 100 мВ ~ 950 мВ).ADC_2_5db
: устанавливает затухание 1,34 (диапазон измеряемого входного напряжения = 100 мВ ~ 1250 мВ)ADC_6db
: устанавливает затухание 1,5 (диапазон измеряемого входного напряжения = 150 мВ ~ 1750 мВ)ADC_11db
: устанавливает затухание 3,6 (диапазон измеряемого входного напряжения = 150 мВ ~ 2450 мВ)analogSetPinAttenuation(pin, attenuation)
: Эта функция аналогична предыдущей, за исключением того, что она устанавливает входное затухание для указанного контакта.adcAttachPin(pin)
: подключает вывод к АЦП (также отключает любой другой аналоговый режим, который может быть включен) и возвращает true, если конфигурация прошла успешно, в противном случае возвращает false.adcStart(pin)
: запускает преобразование АЦП на шине подключенного контакта.adcBusy(pin)
: проверяет, выполняется ли в данный момент преобразование на шине АЦП вывода (возвращает ИСТИНА или ЛОЖЬ).resultadcEnd(pin)
: получает результат преобразования (ждет, если АЦП не завершился), возвращает 16-битное целое число.Более подробную информацию можно найти на readthedocs .
В этом проекте показано, как использовать протокол связи MQTT с ESP32 для публикации сообщений и подписки на темы. В качестве примера мы опубликуем показания датчика BME280 на панели управления Node-RED и будем управлять выходом ESP32. ESP32 мы будем программировать с помощью Arduino IDE.
Подробнее: ESP32 MQTT – публикация и подписка с помощью Arduino IDE
Узнайте, как публиковать показания датчика BME280 (температура, влажность и давление) через MQTT с помощью ESP32 на любой платформе, поддерживающей MQTT, или любом клиенте MQTT. В качестве примера мы опубликуем показания датчиков на панели мониторинга Node-RED, а ESP32 будет запрограммирован с использованием Arduino IDE.
Подробнее: ESP32 MQTT – публикация показаний датчика BME280 (Arduino IDE)
В этом руководстве по началу работы с MicroPython с ESP32 и ESP8266 мы узнаем, как использовать программу и прошивку для плат разработки ESP32 и ESP8266 с помощью MU Editor. Мы увидим, как загрузить и установить MU Editor, который мы будем использовать для записи прошивки и программ прошивки на наши платы ESP. В конце руководства вы сможете написать и прошить свою первую программу на свои платы ESP32 и ESP8266 с помощью этого удобного для начинающих редактора.
Подробнее: Программируем ESP32 и ESP8266 с помощью MU Editor и MicroPython.
Чип ESP32 имеет 48 контактов с множеством функций. Не все выводы доступны на всех макетных платах ESP32, и некоторые выводы нельзя использовать.
Есть много вопросов о том, как использовать GPIO ESP32. Какие пины следует использовать? Какие пины следует избегать в своих проектах? Этот пост призван стать простым и понятным справочным руководством по GPIO ESP32.
Подробнее: Справочник по распиновке ESP32: какие контакты GPIO следует использовать?
Когда ваш проект Интернета вещей питается от сетевого адаптера, вас не слишком заботит энергопотребление. Но если вы собираетесь питать свой проект от батареи, каждый мА имеет значение.
ESP32 может быть относительно энергоемким устройством в зависимости от того, в каком состоянии он находится. Обычно он потребляет около 75 мА при нормальной работе и около 240 мА при передаче данных через Wi-Fi.
Решение здесь состоит в том, чтобы снизить энергопотребление ESP32, воспользовавшись режимом глубокого сна.
Чтобы узнать больше о других режимах сна ESP32 и их энергопотреблении, посетите руководство ниже.
В режиме глубокого сна процессоры, большая часть оперативной памяти и вся цифровая периферия отключаются. Единственные части чипа, которые остаются работоспособными:
Чип потребляет ток от 0,15 мА (при включенном сопроцессоре ULP) до 10 мкА.
В режиме глубокого сна основной ЦП отключается, а сопроцессор UltraLowPower (ULP) может снимать показания датчиков и при необходимости активировать ЦП. Этот режим сна известен как режим, контролируемый датчиками ULP . Это полезно для разработки приложений, в которых ЦП необходимо разбудить внешним событием, таймером или их комбинацией, сохраняя при этом минимальное энергопотребление.
Вместе с ЦП отключается и основная память чипа. В результате все, что хранится в этой памяти, стирается и становится недоступным.
Поскольку память RTC сохраняется, ее содержимое сохраняется даже во время глубокого сна и может быть восстановлено после пробуждения чипа. Вот почему чип сохраняет данные о соединениях Wi-Fi и Bluetooth в памяти RTC перед переходом в глубокий сон.
Если вы хотите использовать данные после перезагрузки, сохраните их в памяти RTC, определив глобальную переменную с RTC_DATA_ATTR
атрибутом. Например,RTC_DATA_ATTR int myVar = 0;
После выхода из глубокого сна чип перезагружается со сбросом и начинает выполнение программы с начала.
ESP32 поддерживает запуск заглушки пробуждения для глубокого сна при выходе из глубокого сна. Эта функция запускается сразу же, как только чип просыпается – до запуска нормальной инициализации, загрузчика или кода ESP-IDF. После запуска заглушки пробуждения чип может снова перейти в спящий режим или продолжить обычный запуск ESP-IDF.
В отличие от других режимов сна, система не может автоматически перейти в режим глубокого сна. Функция esp_deep_sleep_start()
используется для перехода в глубокий сон сразу после настройки источников пробуждения.
ESP32 можно вывести из режима глубокого сна с помощью нескольких источников. Этими источниками являются:
Можно объединить несколько источников пробуждения, и в этом случае чип будет просыпаться при срабатывании одного из источников.
Предупреждение:
Можно перевести ESP32 в глубокий сон без настроенных источников пробуждения, и в этом случае чип остается в режиме глубокого сна на неопределенный срок, пока не будет применен внешний сброс.
Контроллер ESP32 RTC имеет встроенный таймер, который можно использовать для пробуждения ESP32 по истечении заранее определенного периода времени.
Эта функция особенно полезна в проекте, требующем отметки времени или ежедневных задач при сохранении низкого энергопотребления.
Функция esp_sleep_enable_timer_wakeup(time_in_us)
используется для настройки таймера в качестве источника пробуждения. Эта функция принимает количество времени в микросекундах (мкс).
Давайте посмотрим, как это работает, на примере из библиотеки. Откройте среду разработки Arduino, перейдите в меню «Файл» > «Примеры» > «ESP32» > «Deep Sleep» и откройте скетч TimerWakeUp .
Этот эскиз демонстрирует самый простой пример глубокого сна с таймером в качестве источника пробуждения и то, как хранить данные в памяти RTC, чтобы использовать их после перезагрузки.
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in seconds) */
RTC_DATA_ATTR int bootCount = 0;
/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
void setup(){
Serial.begin(115200);
delay(1000); //Take some time to open up the Serial Monitor
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
//Print the wakeup reason for ESP32
print_wakeup_reason();
/*
First we configure the wake up source
We set our ESP32 to wake up every 5 seconds
*/
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
" Seconds");
/*
Next we decide what all peripherals to shut down/keep on
By default, ESP32 will automatically power down the peripherals
not needed by the wakeup source, but if you want to be a poweruser
this is for you. Read in detail at the API docs
http://esp-idf.readthedocs.io/en/latest/api-reference/system/deep_sleep.html
Left the line commented as an example of how to configure peripherals.
The line below turns off all RTC peripherals in deep sleep.
*/
//esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
//Serial.println("Configured all RTC Peripherals to be powered down in sleep");
/*
Now that we have setup a wake cause and if needed setup the
peripherals state in deep sleep, we can now start going to
deep sleep.
In the case that no wake up sources were provided but deep
sleep was started, it will sleep forever unless hardware
reset occurs.
*/
Serial.println("Going to sleep now");
Serial.flush();
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop(){
//This is not going to be called
}
После загрузки эскиза откройте последовательный монитор и установите скорость передачи данных 115200 бит/с.
ESP32 просыпается каждые 5 секунд, печатает причину пробуждения и bootCount на последовательном мониторе и снова переходит в глубокий сон.
Теперь попробуйте перезагрузить ESP32, нажав кнопку EN, он должен снова сбросить bootCount на 1, что указывает на то, что память RTC полностью очищена.
Эти первые две строки кода определяют время, в течение которого ESP32 будет находиться в режиме ожидания.
В этом примере используется коэффициент преобразования микросекунд в секунды, поэтому вы можете установить в переменной время сна в секундах TIME_TO_SLEEP
. Здесь ESP32 переводится в режим глубокого сна на 5 секунд.
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in seconds) */
Как указывалось ранее, вы можете сохранять данные в памяти RTC ESP32 (8 КБ SRAM), которая не стирается во время глубокого сна. Однако он стирается при сбросе ESP32.
Чтобы сохранить данные в памяти RTC, вам просто нужно добавить RTC_DATA_ATTR
атрибут перед определением переменной. В этом примере bootCount
переменная сохраняется в памяти RTC. Он подсчитает, сколько раз ESP32 просыпался от глубокого сна.
RTC_DATA_ATTR int bootCount = 0;
Далее print_wakeup_reason()
определяется функция, которая выводит причину, по которой ESP32 вышел из глубокого сна.
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
При настройке сначала инициализируем последовательную связь с ПК.
Serial.begin(115200);
Затем переменная bootCount
увеличивается на единицу и выводится на последовательный монитор, чтобы показать, сколько раз ESP32 выходил из глубокого сна.
++bootCount;
Serial.println("Boot number: " + String(bootCount));
Затем print_wakeup_reason()
вызывается функция, но вы можете вызвать любую функцию, необходимую для выполнения желаемой задачи, например, считывания значения датчика.
print_wakeup_reason();
Далее мы настраиваем источник пробуждения таймера с помощью esp_sleep_enable_timer_wakeup(time_in_us)
функции. Здесь ESP32 настроен на пробуждение каждые 5 секунд.
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Наконец, ESP32 переводится в спящий режим путем вызова esp_deep_sleep_start()
функции.
esp_deep_sleep_start();
В этом эскизе ESP32 входит в глубокий сон в самой функции setup(), поэтому он никогда не достигает функции Loop(). Следовательно, loop()
функция остается пустой.
void loop(){
//This is not going to be called
}
Вы можете вывести ESP32 из глубокого сна, используя следующие сенсорные кнопки.
Включить ESP32 для пробуждения с помощью сенсорного контакта очень просто. В Arduino IDE вам просто нужно использовать esp_sleep_enable_touchpad_wakeup()
функцию.
Давайте подключим кабель к GPIO#15 (Touch#3), который будет действовать как источник пробуждения при касании. Вы можете прикрепить любой проводящий объект, например проволоку, алюминиевую фольгу, проводящую ткань, проводящую краску и т. д., к сенсорному контакту и превратить его в сенсорную панель.
Давайте посмотрим, как это работает, на примере из библиотеки. Откройте Arduino IDE, выберите «Файл» > «Примеры» > «ESP32» > «Deep Sleep» и откройте скетч TouchWakeUp .
Этот эскиз демонстрирует самый простой пример глубокого сна с прикосновением в качестве источника пробуждения и как хранить данные в памяти RTC, чтобы использовать их после перезагрузки.
#define Threshold 40 /* Greater the value, more the sensitivity */
RTC_DATA_ATTR int bootCount = 0;
touch_pad_t touchPin;
/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
/*
Method to print the touchpad by which ESP32
has been awaken from sleep
*/
void print_wakeup_touchpad(){
touchPin = esp_sleep_get_touchpad_wakeup_status();
switch(touchPin)
{
case 0 : Serial.println("Touch detected on GPIO 4"); break;
case 1 : Serial.println("Touch detected on GPIO 0"); break;
case 2 : Serial.println("Touch detected on GPIO 2"); break;
case 3 : Serial.println("Touch detected on GPIO 15"); break;
case 4 : Serial.println("Touch detected on GPIO 13"); break;
case 5 : Serial.println("Touch detected on GPIO 12"); break;
case 6 : Serial.println("Touch detected on GPIO 14"); break;
case 7 : Serial.println("Touch detected on GPIO 27"); break;
case 8 : Serial.println("Touch detected on GPIO 33"); break;
case 9 : Serial.println("Touch detected on GPIO 32"); break;
default : Serial.println("Wakeup not by touchpad"); break;
}
}
void callback(){
//placeholder callback function
}
void setup(){
Serial.begin(115200);
delay(1000); //Take some time to open up the Serial Monitor
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
//Print the wakeup reason for ESP32 and touchpad too
print_wakeup_reason();
print_wakeup_touchpad();
//Setup interrupt on Touch Pad 3 (GPIO15)
touchAttachInterrupt(T3, callback, Threshold);
//Configure Touchpad as wakeup source
esp_sleep_enable_touchpad_wakeup();
//Go to sleep now
Serial.println("Going to sleep now");
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop(){
//This will never be reached
}
После загрузки эскиза откройте последовательный монитор и установите скорость передачи данных 115200 бит/с.
Теперь при прикосновении к контакту ESP32 будет отображать на последовательном мониторе количество загрузок, причину пробуждения и то, какой GPIO был затронут.
Первая строка кода устанавливает пороговое значение для сенсорного контакта, равное 40. Чем выше пороговое значение, тем выше чувствительность. Вы можете изменить это значение в соответствии с вашим проектом.
#define Threshold 40 /* Greater the value, more the sensitivity */
Как указывалось ранее, вы можете сохранять данные в памяти RTC ESP32 (8 КБ SRAM), которая не стирается во время глубокого сна. Однако он стирается при сбросе ESP32.
Чтобы сохранить данные в памяти RTC, вам просто нужно добавить RTC_DATA_ATTR
атрибут перед определением переменной. В этом примере bootCount
переменная сохраняется в памяти RTC. Он подсчитает, сколько раз ESP32 просыпался от глубокого сна.
RTC_DATA_ATTR int bootCount = 0;
После этого определяется переменная с именем touchPin
типа touch_pad_t
(type enum), которая позже поможет нам распечатать GPIO, с помощью которого ESP32 выводится из спящего режима.
touch_pad_t touchPin;
Далее print_wakeup_reason()
определяется функция, которая выводит причину, по которой ESP32 вышел из глубокого сна.
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
Также определена функция print_wakeup_touchpad()
, которая печатает номер GPIO, по которому ESP32 выводится из глубокого сна.
void print_wakeup_touchpad(){
touchPin = esp_sleep_get_touchpad_wakeup_status();
switch(touchPin)
{
case 0 : Serial.println("Touch detected on GPIO 4"); break;
case 1 : Serial.println("Touch detected on GPIO 0"); break;
case 2 : Serial.println("Touch detected on GPIO 2"); break;
case 3 : Serial.println("Touch detected on GPIO 15"); break;
case 4 : Serial.println("Touch detected on GPIO 13"); break;
case 5 : Serial.println("Touch detected on GPIO 12"); break;
case 6 : Serial.println("Touch detected on GPIO 14"); break;
case 7 : Serial.println("Touch detected on GPIO 27"); break;
case 8 : Serial.println("Touch detected on GPIO 33"); break;
case 9 : Serial.println("Touch detected on GPIO 32"); break;
default : Serial.println("Wakeup not by touchpad"); break;
}
}
Далее callback()
определяется функция. Это не что иное, как процедура обслуживания прерываний (ISR), которая будет вызываться каждый раз, когда срабатывает сенсорное прерывание. Но, к сожалению, эта функция не выполняется, если ESP32 находится в глубоком сне. Поэтому эта функция остается пустой.
void callback(){
//placeholder callback function
}
При настройке сначала инициализируем последовательную связь с ПК.
Serial.begin(115200);
Затем переменная bootCount
увеличивается на единицу и выводится на последовательный монитор, чтобы показать, сколько раз ESP32 выходил из глубокого сна.
++bootCount;
Serial.println("Boot number: " + String(bootCount));
Затем вызываются функции print_wakeup_reason()
и print_wakeup_touchpad()
, но вы можете вызвать любую функцию, необходимую для выполнения желаемой задачи, например, считывания значения датчика.
print_wakeup_reason();
print_wakeup_touchpad();
Теперь прерывание необходимо привязать к одному из сенсорных выводов с нужным порогом чувствительности. Здесь прерывание привязано к сенсорной панели 3 (GPIO15).
touchAttachInterrupt(T3, callback, Threshold);
Далее мы настраиваем источник пробуждения при касании с помощью esp_sleep_enable_touchpad_wakeup()
функции.
esp_sleep_enable_touchpad_wakeup();
Наконец, ESP32 переводится в спящий режим путем вызова esp_deep_sleep_start()
функции.
esp_deep_sleep_start();
В этом эскизе ESP32 входит в глубокий сон самой функции setup()
, поэтому он никогда не достигает loop()
функции. Следовательно, loop()
функция остается пустой.
void loop(){
//This is not going to be called
}
Существует два типа внешних триггеров, выводящих ESP32 из режима глубокого сна.
Если вы хотите использовать вывод прерывания для вывода ESP32 из глубокого сна, вам придется использовать так называемые выводы RTC_GPIO . Эти GPIO направляются в маломощную подсистему RTC, поэтому их можно использовать, когда ESP32 находится в глубоком сне.
Выводы RTC_GPIO:
ESP32 можно настроить на выход из глубокого сна, когда один из выводов RTC_GPIO меняет свой логический уровень.
Эта esp_sleep_enable_ext0_wakeup(GPIO_PIN, LOGIC_LEVEL)
функция используется для включения этого источника пробуждения. Эта функция принимает два параметра. Первый — это номер контакта GPIO, а второй — логический уровень (НИЗКИЙ или ВЫСОКИЙ), по которому мы хотим инициировать пробуждение.
Поскольку ext0 использует ввод-вывод RTC для пробуждения ESP32, периферийные устройства RTC продолжают работать во время глубокого сна.
А поскольку модуль ввода-вывода RTC включен, вы можете воспользоваться внутренними повышающими или понижающими резисторами. Их необходимо настроить с помощью функций rtc_gpio_pullup_en()
и rtc_gpio_pulldown_en()
перед esp_deep_sleep_start()
вызовом.
Давайте подключим кнопку к GPIO#33, используя понижающий резистор 10 кОм.
Давайте посмотрим, как это работает, на примере из библиотеки. Откройте IDE Arduino, перейдите в меню «Файл» > «Примеры» > «ESP32» > «Deep Sleep» и откройте скетч «ExternalWakeUp» .
Этот эскиз демонстрирует самый простой пример глубокого сна с ext0 в качестве источника пробуждения.
#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex
RTC_DATA_ATTR int bootCount = 0;
/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
void setup(){
Serial.begin(115200);
delay(1000); //Take some time to open up the Serial Monitor
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
//Print the wakeup reason for ESP32
print_wakeup_reason();
/*
First we configure the wake up source
We set our ESP32 to wake up for an external trigger.
There are two types for ESP32, ext0 and ext1 .
ext0 uses RTC_IO to wakeup thus requires RTC peripherals
to be on while ext1 uses RTC Controller so doesnt need
peripherals to be powered on.
Note that using internal pullups/pulldowns also requires
RTC peripherals to be turned on.
*/
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low
//If you were to use ext1, you would use it like
//esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
//Go to sleep now
Serial.println("Going to sleep now");
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop(){
//This is not going to be called
}
После загрузки эскиза откройте последовательный монитор и установите скорость передачи данных 115200 бит/с.
Теперь, когда вы нажимаете кнопку, ESP32 будет отображать количество загрузок и причину пробуждения на последовательном мониторе. Попробуйте несколько раз и наблюдайте, как количество загрузок увеличивается с каждым нажатием кнопки. Также обратите внимание, что ext0 использует ввод-вывод RTC для пробуждения ESP32.
Первая строка кода устанавливает битовую маску. Это не требуется для внешнего пробуждения ext0, поэтому вы можете пока его игнорировать. Мы узнаем об этом во время объяснения кода внешнего пробуждения ext1.
#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex
Как указывалось ранее, вы можете сохранять данные в памяти RTC ESP32 (8 КБ SRAM), которая не стирается во время глубокого сна. Однако он стирается при сбросе ESP32.
Чтобы сохранить данные в памяти RTC, вам просто нужно добавить RTC_DATA_ATTR
атрибут перед определением переменной. В этом примере bootCount
переменная сохраняется в памяти RTC. Он подсчитает, сколько раз ESP32 просыпался от глубокого сна.
RTC_DATA_ATTR int bootCount = 0;
Далее print_wakeup_reason()
определяется функция, которая выводит причину, по которой ESP32 вышел из глубокого сна.
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
При настройке сначала инициализируем последовательную связь с ПК.
Serial.begin(115200);
Затем переменная bootCount
увеличивается на единицу и выводится на последовательный монитор, чтобы показать, сколько раз ESP32 выходил из глубокого сна.
++bootCount;
Serial.println("Boot number: " + String(bootCount));
Затем print_wakeup_reason()
вызывается функция, но вы можете вызвать любую функцию, необходимую для выполнения желаемой задачи, например, считывания значения датчика.
print_wakeup_reason();
Теперь внешний источник пробуждения ext0 настраивается с помощью этой esp_sleep_enable_ext0_wakeup(GPIO_PIN, LOGIC_LEVEL)
функции. Эта функция принимает два параметра. Первый — это номер контакта GPIO, а второй — логический уровень (НИЗКИЙ или ВЫСОКИЙ), по которому мы хотим инициировать пробуждение. В этом примере ESP32 настроен на пробуждение, когда логический уровень GPIO#33 становится ВЫСОКИМ.
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1);
Наконец, ESP32 переводится в спящий режим путем вызова esp_deep_sleep_start()
функции.
esp_deep_sleep_start();
В этом эскизе ESP32 входит в глубокий сон самой функции setup()
, поэтому он никогда не достигает loop()
функции. Следовательно, loop()
функция остается пустой.
void loop(){
//This is not going to be called
}
ESP32 можно настроить на выход из глубокого сна с помощью нескольких контактов. Помните, что эти контакты должны быть среди контактов GPIO RTC.
Поскольку источник пробуждения ext1 использует контроллер RTC, для него не требуется включение периферийных устройств RTC и памяти RTC. В этом случае внутренние подтягивающие и понижающие резисторы будут недоступны.
Чтобы использовать внутренние подтягивающие или понижающие резисторы, нам необходимо запросить, чтобы периферийные устройства RTC оставались включенными во время сна, и настроить подтягивающие/подтягивающие резисторы с помощью функций rtc_gpio_pullup_en()
и rtc_gpio_pulldown_en()
перед переходом в спящий режим.
Эта esp_sleep_enable_ext1_wakeup(BUTTON_PIN_MASK, LOGIC_LEVEL)
функция используется для включения этого источника пробуждения. Эта функция принимает два параметра. Первый — это битовая маска, которая сообщает ESP32, какие контакты мы хотим использовать, а второй параметр может быть одним из двух логических уровней, упомянутых ниже, для запуска пробуждения:
ESP_EXT1_WAKEUP_ANY_HIGH
)ESP_EXT1_WAKEUP_ALL_LOW
)Самый простой способ понять битовую маску — записать ее в двоичном формате. Вы можете видеть, что нумерация битов основана на обычной нумерации GPIO. Младший бит (LSB) представляет GPIO#0, а старший бит (MSB) представляет GPIO#39.
Поэтому, если вы хотите, чтобы GPIO просыпался, вы должны записать 1 в соответствующее место и 0 в каждый из оставшихся контактов. И, наконец, вам нужно преобразовать его в HEX.
Например, если вы хотите использовать GPIO#32 и GPIO#33 в качестве внешних источников пробуждения, битовая маска будет такой:
Давайте подключим две кнопки к GPIO#33 и GPIO#32, используя понижающие резисторы 10 КОм.
Давайте посмотрим, как это работает, на всё том же примере ExternalWakeUp из библиотеки. Еще раз откройте Arduino IDE, перейдите в меню «Файл» > «Примеры» > «ESP32» > «Deep Sleep» и откройте скетч «ExternalWakeup» .
Давайте внесем три изменения в эскиз, чтобы он у нас работал:
Изменения в эскизе выделены зеленый.
#define BUTTON_PIN_BITMASK 0x300000000
RTC_DATA_ATTR int bootCount = 0;
/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
void setup(){
Serial.begin(115200);
delay(1000); //Take some time to open up the Serial Monitor
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
//Print the wakeup reason for ESP32
print_wakeup_reason();
/*
First we configure the wake up source
We set our ESP32 to wake up for an external trigger.
There are two types for ESP32, ext0 and ext1 .
ext0 uses RTC_IO to wakeup thus requires RTC peripherals
to be on while ext1 uses RTC Controller so doesnt need
peripherals to be powered on.
Note that using internal pullups/pulldowns also requires
RTC peripherals to be turned on.
*/
//esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low
//If you were to use ext1, you would use it like
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
//Go to sleep now
Serial.println("Going to sleep now");
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop(){
//This is not going to be called
}
После загрузки эскиза откройте последовательный монитор и установите скорость передачи данных 115200 бит/с.
Теперь, когда вы нажмете кнопку, вы увидите нечто подобное на последовательном мониторе. Также обратите внимание, что ext1 использует контроллер RTC для пробуждения ESP32.
Этот код идентичен коду ext0, за исключением двух изменений.
В начале кода определяется битовая маска. Поскольку в этом примере мы используем контакты GPIO#32 и GPIO#33, маска имеет 1 в соответствующих позициях, 32 0 справа и 6 0 слева.
00000011 00000000 00000000 00000000 00000000 BIN = 0x300000000 HEX
#define BUTTON_PIN_BITMASK 0x300000000
И, наконец, ext1 включен в качестве источника пробуждения.
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
В этой статье мы познакомим вас с новым микроконтроллером ESP32. У всех нас есть некоторое представление о платах для разработки Arduino и о том, как они широко используются. Но когда дело доходит до ESP32, он имеет много преимуществ перед платами разработки Arduino. Старая версия этой платы — ESP8266. Итак, давайте подробно обсудим этот микроконтроллер. Мы рассмотрим его характеристики, основные функции, расположение контактов, питание этой платы, предпочтения по сравнению с Arduino и код мигающего светодиода, который может работать на этом микроконтроллере. Итак, приступим.
При работе с IoT часто необходимо отслеживать местоположение устройства для мониторинга. Служба глобального позиционирования (GPS) остается вариантом номер 1 для отслеживания на больших территориях. Этот проект покажет вам, как вы можете использовать GPS с ESP32 и отображать местоположение на Картах Google.
Узнайте, как программировать платы ESP32 и ESP8266 NodeMCU с помощью VS Code (Microsoft Visual Studio Code) с расширением PlatformIO IDE.
Подробнее: Начало работы с VS Code и PlatformIO IDE для ESP32 и ESP8266