Продолжаем изучение платы OLIMEX ESP32-EVB и сегодня мы поговорим о её программировании. Напомню, что на небольшой квадрат текстолита размером 75х75 мм компания OLIMEX умудрилась поместить микроконтроллер ESP32, Ethernet физику LAN8710A, microSD картридер, IR приёмник и передатчик, CAN трансивер, 2 реле, разъёмы расширения, подсистему зарядки и обслуживания аккумулятора и прочие элементы — все эти компоненты плотно «посажены» на нестандартные GPIO и имеют свои особенности работы
Далее мы попробуем со всем этим разобраться — в результате вы сможете легко и просто использовать ESP32-EVB в своих проектах, а заодно повысите свою квалификацию в программировании и понимание работы контроллеров на ESP32.
❯ Распиновка
Для начала давайте ещё раз посмотрим на распиновку платы ESP32-EVB. Как вы видите, нет ни одного свободного GPIO, незанятого подключением к какому-нибудь компоненту на плате. Подробнее о распределении GPIO мы поговорим в отдельных разделах, посвящённых работе конкретных подсистем ESP32-EVB.

❯ План по коду
Далее мы рассмотрим работу и программирование всех составных частей контроллера ESP32-EVB. Вы сможете использовать этот код в своих проектах напрямую или модифицируя его для своих конкретных задач.
Список подсистем ESP32-EVB:
- Кнопка User (BUT1)
- Управление реле
- Ethernet LAN8710A
- microSD картридер
- IR приёмник и передатчик
- CAN интерфейс
Я буду приводить примеры кода в виде скетчей Arduino, но вы можете использовать для работы с ESP32-EVB вашу любимую среду программирования.
❯ Кнопка User (BUT1)
Начнём с программирования пользовательской кнопки BUT1, подключённой к GPIO34. Работа с кнопкой из скетча Arduino тривиальна и не должна вызывать у вас никаких проблем.

Код скетча получения состояния кнопки:
/* OLIMEX ESP32-EVB Button example */ #define BUTTON_PIN 34 void setup() { Serial.begin(115200); Serial.println(); Serial.println(F("Start OLIMEX ESP32-EVB Button example...")); pinMode(BUTTON_PIN, INPUT); } void loop() { Serial.println(digitalRead(BUTTON_PIN)); delay(100); }
Запускаем скетч, нажимаем на кнопку и видим результат наших действий в Serial мониторе: кнопка не нажата — вывод «1», при нажатии выводится «0».

❯ Управление реле
Управление реле ненамного сложнее, чем работа с кнопкой. Просто определяем в скетче GPIO, к которым подключены реле (32 и 33) и создаём код, который поочерёдно переключает их. Особенность этого скетча заключается в том, что он не просто поочерёдно переключает два реле, а при небольшой доработке может в режиме «бегущего огня» управлять любым (в разумных пределах, конечно) количеством реле на плате.

Код скетча управления реле:
/* OLIMEX ESP32-EVB Relays example */ byte pins[] = {32, 33}; byte pos = 0; void setup() { Serial.begin(115200); Serial.println(); Serial.println(F("Start OLIMEX ESP32-EVB Relays example...")); pinMode(pins[0], OUTPUT); pinMode(pins[1], OUTPUT); } void clear() { digitalWrite(pins[0], LOW); digitalWrite(pins[1], LOW); } void change(byte n) { clear(); digitalWrite(pins[n], HIGH); } void loop() { change(pos); Serial.print(F("ON Relay #")); Serial.println(pos); delay(10000); pos++; if (pos > 1) {pos = 0;} }
Если у вас есть какой-то другой контроллер с другим количеством реле на борту, то вы можете потренироваться, модифицировав этот скетч для вашего контроллера.
Результат работы нашего скетча (синхронно с выводом в Serial изменяются состояния и самих реле).

Теперь, после разогрева на простых примерах, давайте перейдём к более сложным задачам программирования компонентов, расположенных на плате ESP32-EVB.
❯ Ethernet LAN8710A
Как я уже заметил в первой статье, на плате ESP32-EVB установлен чип Ethernet физики LAN8710A вместо привычного нам по другим контроллерам и более простого LAN8720A. Программно оба чипа совместимы, поэтому тестовый скетч будет иметь привычный вид.
Исходные данные:
Используем внешний кварц, установленный на плате ESP32-EVB.
#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN
Указываем номера GPIO настраиваемых пинов ADDR, MDC и MDIO.
#define ETH_ADDR 0 #define ETH_MDC_PIN 23 #define ETH_MDIO_PIN 18
На плате ESP32-EVB вывод NRST микросхемы LAN8710A подключён к плюсу питания и предусмотрена перемычка для его замыкания на землю, но он не подключён к микроконтроллеру ESP32, поэтому «сбрасываем» в скетче пин NRST на «пустой» GPIO5 (если вы будете использовать CAN интерфейс, то вместо GPIO5 нужно будет указать любой другой «свободный»).
#define NRST 5

Код скетча работы с Ethernet:
/* OLIMEX ESP32-EVB Ethernet example */ #include <ETH.h> #include <SPI.h> #define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN #define ETH_POWER_PIN -1 #define ETH_TYPE ETH_PHY_LAN8720 #define ETH_ADDR 0 #define ETH_MDC_PIN 23 #define ETH_MDIO_PIN 18 #define NRST 5 static bool eth_connected = false; void setup() { Serial.begin(115200); Serial.println(); Serial.println(F("Start OLIMEX ESP32-EVB Ethernet example...")); WiFi.onEvent(WiFiEvent); ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLK_MODE); } void WiFiEvent(WiFiEvent_t event) { switch (event) { case SYSTEM_EVENT_ETH_START: Serial.println("ETH Started"); ETH.setHostname("esp32-ethernet"); break; case SYSTEM_EVENT_ETH_CONNECTED: Serial.println("ETH Connected"); break; case SYSTEM_EVENT_ETH_GOT_IP: Serial.print("ETH MAC: "); Serial.print(ETH.macAddress()); Serial.print(", IPv4: "); Serial.print(ETH.localIP()); if (ETH.fullDuplex()) {Serial.print(", FULL_DUPLEX");} Serial.print(", "); Serial.print(ETH.linkSpeed()); Serial.println("Mbps"); eth_connected = true; break; case SYSTEM_EVENT_ETH_DISCONNECTED: Serial.println("ETH Disconnected"); eth_connected = false; break; case SYSTEM_EVENT_ETH_STOP: Serial.println("ETH Stopped"); eth_connected = false; break; default: break; } } // WiFiEvent( ) void testClient(const char *host, uint16_t port) { Serial.print("\nconnecting to "); Serial.println(host); WiFiClient client; if (!client.connect(host, port)) { Serial.println("connection failed"); return; } client.printf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", host); while (client.connected() && !client.available()); while (client.available()) { Serial.write(client.read()); } Serial.println("closing connection\n"); client.stop(); } void loop() { if (eth_connected) { testClient("baidu.com", 80); } delay(10000); }
Здесь мы обращаемся к внешнему серверу (www.baidu.com) и получаем от него ответ. На скриншоте видно, что LAN8710A прекрасно работает на плате ESP32-EVB. Таким образом вы можете использовать для связи Wi-Fi и/или Ethernet соединения в своих проектах.

❯ microSD картридер
Если вы попробуете «завести» microSD картридер на плате OLIMEX ESP32-EVB при помощи стандартной библиотеки и её примеров, то у вас ничего не получится — здесь дело в «мудрёном» подключении GPIO микроконтроллера ESP32 и, плюс к тому (видимо, чтобы с гарантией никто ничего не смог завести), использовании режима «SDMMC_HOST_FLAG_1BIT».

Мне понадобилось некоторое время, чтобы решить этот ребус (зато в очередной раз прокачал свои навыки программирования), далее я предлагаю вам готовое, проверенное и гарантированно работающее решение «microSD картридер на плате OLIMEX ESP32-EVB».
Исходные данные:
MISO: GPIO15 MOSI: GPIO2 SCK: GPIO14 CS: GPIO17
Причём на распиновке GPIO17 обозначен как SPI_CS, а на схеме пин CS картридера подключён к плюсу питания. Код работы с картридером:
/* OLIMEX ESP32-EVB SD example */ #include "FS.h" #include "SD_MMC.h" #include "SPI.h" void setup() { Serial.begin(115200); Serial.println(); Serial.println(F("Start OLIMEX ESP32-EVB SD example...")); SPI.begin(14, 15, 2, 17); if (!SD_MMC.begin("/sdcard", true)) { Serial.println("Card mount failed"); return; } uint8_t cardType = SD_MMC.cardType(); if (cardType == CARD_NONE){ Serial.println("No card attached"); return; } Serial.print("Card Type: "); if (cardType == CARD_MMC) {Serial.println("MMC");} else if (cardType == CARD_SD) {Serial.println("SDSC");} else if (cardType == CARD_SDHC){Serial.println("SDHC");} else {Serial.println("UNKNOWN");} uint64_t cardSize = SD_MMC.cardSize() / (1024*1024); Serial.printf("Card Size: %llu MB\n", cardSize); listDir(SD_MMC, "/", 0); Serial.printf("Total: %llu MB\n", SD_MMC.totalBytes() / (1024*1024)); Serial.printf( "Used: %llu MB\n", SD_MMC.usedBytes() / (1024*1024)); } // setup void listDir(fs::FS &fs, const char * dirname, uint8_t levels) { Serial.printf("Listing directory: %s\n", dirname); File root = fs.open(dirname); if (!root) {Serial.println("Failed open directory"); return;} if (!root.isDirectory()) {Serial.println("Not a directory"); return;} File file = root.openNextFile(); while (file){ if (file.isDirectory()) { Serial.print(" DIR : "); Serial.println(file.name()); if (levels){ listDir(fs, file.name(), levels -1); } } else { Serial.print(" FILE: "); Serial.print(file.name()); Serial.print(" SIZE: "); Serial.println(file.size()); } file = root.openNextFile(); } } // listDir( ) void loop(){ }
Здесь я бы обратил ваше внимание на строку
if (!SD_MMC.begin("/sdcard", true)) {
где «true» и включает режим «SDMMC_HOST_FLAG_1BIT». Ниже я привожу две строчки из библиотеки SD_MMC для пояснения того, как это работает изнутри:
bool begin(const char * mountpoint="/sdcard", bool mode1bit=false, bool format_if_mount_failed=false); host.flags = SDMMC_HOST_FLAG_1BIT; //use 1-line SD mode
В результате мы можем лицезреть маленькое чудо — работу microSD картридера на плате OLIMEX ESP32-EVB. Значение этого трудно переоценить — возможность использовать microSD карт памяти значительно расширяет функционал ESP32-EVB.

❯ IR приёмник и передатчик
Плюс ко всем прелестям, ESP32-EVB имеет встроенные инфракрасные (IR) приёмник и передатчик, подключённые, соответственно, на GPIO39 и GPIO12. Это отличное дополнение функционала платы, которое вы, я не сомневаюсь, найдёте как использовать.

Для работы с инфракрасными приёмником и передатчиком используется библиотека Arduino-IRremote. В тестовом скетче мы примем инфракрасный сигнал от пульта управления, идентифицируем его (производителя оборудования, частоту сигнала, протокол управления и код нажатой клавиши), а также пошлём в эфир записанный нами сигнал, нажимая на функциональную кнопку контроллера ESP32-EVB.
Исходные данные:
- IR приёмник: GPIO39
- IR излучатель: GPIO12
- Функциональная кнопка: GPIO34
- STATUS_PIN: используется в скетче для индикации событий, но поскольку в ESP32-EVB задействованы все GPIO ESP32 (и нет специального светодиода), то этот пин переопределён на неиспользуемый в данном скетче пин D4 (U1TXD). В реальном проекте вам нужно будет либо найти «свободный» пин для STATUS_PIN, либо переделать скетч и удалить код, ответственный за работу светодиода.
Полный код скетча содержит 2 файла: основной (ir_example.ino) и файл с настройками (PinDefinitionsAndMore.h). В файле PinDefinitionsAndMore.h нужно изменить номера GPIO в соответствии с распиновкой контроллера ESP32-EVB (строки с настройками помечены тремя восклицательными знаками).
#define LED_BUILTIN 4 // !!! #define IR_RECEIVE_PIN 39 // !!! #define IR_SEND_PIN 12 // !!! #define APPLICATION_PIN 34 // !!!
/* * PinDefinitionsAndMore.h * * Contains pin definitions for IRremote examples for various platforms * as well as definitions for feedback LED and tone() and includes * * Copyright (C) 2021 Armin Joachimsmeyer * armin.joachimsmeyer@gmail.com * * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. * * Arduino-IRremote is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/gpl.html>. * */ /* * Pin mapping table for different platforms * * Platform IR input IR output Tone * ----------------------------------------- * DEFAULT/AVR 2 3 4 * ATtinyX5 0 4 3 * ATtiny167 9 8 5 // Digispark pro number schema * ATtiny167 3 2 7 * ATtiny3217 10 11 3 // TinyCore schema * ATtiny1604 2 PA5/3 % * SAMD21 3 4 5 * ESP8266 14 // D5 12 // D6 % * ESP32 15 4 27 * BluePill PA6 PA7 PA3 * APOLLO3 11 12 5 */ #define LED_BUILTIN 4 // !!! //#define _IR_MEASURE_TIMING // For debugging purposes. #if defined(ESP8266) #define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW #define IR_RECEIVE_PIN 14 // D5 #define IR_RECEIVE_PIN_STRING "D5" #define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED #define IR_SEND_PIN_STRING "D6" #define _IR_TIMING_TEST_PIN 13 // D7 #define APPLICATION_PIN 0 // D3 #define tone(...) void() // tone() inhibits receive timer #define noTone(a) void() #define TONE_PIN 42 // Dummy for examples using it #elif defined(ESP32) #include <Arduino.h> #define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. void tone(uint8_t _pin, unsigned int frequency){ ledcAttachPin(_pin, TONE_LEDC_CHANNEL); ledcWriteTone(TONE_LEDC_CHANNEL, frequency); } void tone(uint8_t _pin, unsigned int frequency, unsigned long duration){ ledcAttachPin(_pin, TONE_LEDC_CHANNEL); ledcWriteTone(TONE_LEDC_CHANNEL, frequency); delay(duration); ledcWriteTone(TONE_LEDC_CHANNEL, 0); } void noTone(uint8_t _pin){ ledcWriteTone(TONE_LEDC_CHANNEL, 0); } #define IR_RECEIVE_PIN 39 // !!! #define IR_SEND_PIN 12 // !!! #define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 #define APPLICATION_PIN 34 // !!! #elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill in 2 flavors // Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() #define IR_RECEIVE_PIN PA6 #define IR_RECEIVE_PIN_STRING "PA6" #define IR_SEND_PIN PA7 #define IR_SEND_PIN_STRING "PA7" #define TONE_PIN PA3 #define _IR_TIMING_TEST_PIN PA5 #define APPLICATION_PIN PA2 #elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) #include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". saves 370 bytes program space and 38 bytes RAM for digistump core #define IR_RECEIVE_PIN 0 #define IR_SEND_PIN 4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. #define TONE_PIN 3 #define _IR_TIMING_TEST_PIN 3 #elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) #include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" // For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. # if defined(ARDUINO_AVR_DIGISPARKPRO) #define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 //#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards #define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 #define TONE_PIN 5 // PA7 #define _IR_TIMING_TEST_PIN 10 // PA4 # else #define IR_RECEIVE_PIN 3 #define IR_SEND_PIN 2 #define TONE_PIN 7 # endif #elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board #include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program space // Pin 6 is TX pin 7 is RX #define IR_RECEIVE_PIN 3 // INT1 #define IR_SEND_PIN 4 #define TONE_PIN 9 #define _IR_TIMING_TEST_PIN 8 #elif defined(__AVR_ATtiny3217__) #define IR_RECEIVE_PIN 10 #define IR_SEND_PIN 11 #define TONE_PIN 3 #define APPLICATION_PIN 5 #elif defined(__AVR_ATtiny1604__) #define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. #define IR_SEND_PIN 3 #define APPLICATION_PIN 5 #define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() #define noTone(a) void() #define TONE_PIN 42 // Dummy for examples using it # elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ || defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ || defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ || defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ || defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ || defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ || defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) #define IR_RECEIVE_PIN 2 #define IR_SEND_PIN 13 #define TONE_PIN 4 #define APPLICATION_PIN 5 #define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. #define _IR_TIMING_TEST_PIN 7 #elif defined(ARDUINO_ARCH_APOLLO3) #define IR_RECEIVE_PIN 11 #define IR_SEND_PIN 12 #define TONE_PIN 5 #elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE #define IR_RECEIVE_PIN 2 #define IR_SEND_PIN 3 #define TONE_PIN 4 #define APPLICATION_PIN 5 #define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. #define _IR_TIMING_TEST_PIN 7 #elif defined(TEENSYDUINO) #define IR_RECEIVE_PIN 2 #define IR_SEND_PIN 3 #define TONE_PIN 4 #define APPLICATION_PIN 5 #define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. #define _IR_TIMING_TEST_PIN 7 #elif defined(__AVR__) // Default as for ATmega328 like on Uno, Nano etc. #define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. #define IR_SEND_PIN 3 #define TONE_PIN 4 #define APPLICATION_PIN 5 #define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. #define _IR_TIMING_TEST_PIN 7 #elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) #define IR_RECEIVE_PIN 2 #define IR_SEND_PIN 3 #define TONE_PIN 4 #define APPLICATION_PIN 5 #define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. #define _IR_TIMING_TEST_PIN 7 // On the Zero and others we switch explicitly to SerialUSB #define Serial SerialUSB // Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. // Attention!!! D2 and D4 are switched on these boards!!! // If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. //#undef LED_BUILTIN //#define LED_BUILTIN 24 // PB11 // As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. //#undef LED_BUILTIN //#define LED_BUILTIN 25 // PB03 //#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW #elif defined (NRF51) // BBC micro:bit #define IR_RECEIVE_PIN 2 #define IR_SEND_PIN 3 #define APPLICATION_PIN 1 #define _IR_TIMING_TEST_PIN 4 #define tone(...) void() // no tone() available #define noTone(a) void() #define TONE_PIN 42 // Dummy for examples using it #else #warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. // Default valued for unidentified boards #define IR_RECEIVE_PIN 2 #define IR_SEND_PIN 3 #define TONE_PIN 4 #define APPLICATION_PIN 5 #define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. #define _IR_TIMING_TEST_PIN 7 #endif // defined(ESP8266) #if !defined (FLASHEND) #define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined #endif /* * Helper macro for getting a macro definition as string */ #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x)
/* OLIMEX ESP32-EVB IR example * ReceiveAndSend.cpp * * * Record and play back last received IR signal at button press. * The logic is: * If the button is pressed, send the IR code. * If an IR code is received, record it. * * An example for simultaneous receiving and sending is in the UnitTest example. * * An IR detector/demodulator must be connected to the input IR_RECEIVE_PIN. * * A button must be connected between the input SEND_BUTTON_PIN and ground. * A visible LED can be connected to STATUS_PIN to provide status. * * Initially coded 2009 Ken Shirriff http://www.righto.com * * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. * */ #include <Arduino.h> #include "PinDefinitionsAndMore.h" //#define EXCLUDE_EXOTIC_PROTOCOLS // saves around 900 bytes program space #include <IRremote.hpp> int SEND_BUTTON_PIN = APPLICATION_PIN; int STATUS_PIN = LED_BUILTIN; int DELAY_BETWEEN_REPEAT = 50; // On the Zero and others we switch explicitly to SerialUSB #if defined(ARDUINO_ARCH_SAMD) #define Serial SerialUSB #endif struct storedIRDataStruct { // Storage for the recorded code IRData receivedIRData; // extensions for sendRaw uint8_t rawCode[RAW_BUFFER_LENGTH]; // durations if raw uint8_t rawCodeLength; // length of code } sStoredIRData; int lastButtonState; void storeCode(IRData *aIRReceivedData); void sendCode(storedIRDataStruct *aIRDataToSend); void setup() { Serial.begin(115200); Serial.println(); Serial.println(F("Start OLIMEX ESP32-EVB IR example...")); // Just to know which program is running on my Arduino //Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); // Start the receiver, enable feedback LED, take LED feedback pin from the internal boards definition IrSender.begin(IR_SEND_PIN, ENABLE_LED_FEEDBACK); // Specify send pin and enable feedback LED at default feedback LED pin pinMode(STATUS_PIN, OUTPUT); pinMode(SEND_BUTTON_PIN, INPUT); // !!! } // setup // Stores the code for later playback in sStoredIRData void storeCode(IRData *aIRReceivedData) { if (aIRReceivedData->flags & IRDATA_FLAGS_IS_REPEAT) {Serial.println(F("Ignore repeat")); return;} if (aIRReceivedData->flags & IRDATA_FLAGS_IS_AUTO_REPEAT) {Serial.println(F("Ignore autorepeat")); return;} if (aIRReceivedData->flags & IRDATA_FLAGS_PARITY_FAILED) {Serial.println(F("Ignore parity error")); return;} sStoredIRData.receivedIRData = *aIRReceivedData; // Copy decoded data if (sStoredIRData.receivedIRData.protocol == UNKNOWN) { Serial.print(F("Received unknown code and store ")); Serial.print(IrReceiver.decodedIRData.rawDataPtr->rawlen - 1); Serial.println(F(" timing entries as raw ")); IrReceiver.printIRResultRawFormatted(&Serial, true); // output the results in RAW format sStoredIRData.rawCodeLength = IrReceiver.decodedIRData.rawDataPtr->rawlen - 1; IrReceiver.compensateAndStoreIRResultInArray(sStoredIRData.rawCode); // store current raw data in dedicated array for later usage } else { IrReceiver.printIRResultShort(&Serial); sStoredIRData.receivedIRData.flags = 0; // clear flags -esp. repeat- for later sending Serial.println(); } } // storeCode( ) void sendCode(storedIRDataStruct *aIRDataToSend) { if (aIRDataToSend->receivedIRData.protocol == UNKNOWN) { // raw IrSender.sendRaw(aIRDataToSend->rawCode, aIRDataToSend->rawCodeLength, 38); // 38 KHz Serial.print(F("Sent raw ")); Serial.print(aIRDataToSend->rawCodeLength); Serial.println(F(" marks or spaces")); } else { IrSender.write(&aIRDataToSend->receivedIRData, NO_REPEATS); // write func switch for different protocols Serial.print(F("Sent: ")); printIRResultShort(&Serial, &aIRDataToSend->receivedIRData); } } void loop() { int buttonState = digitalRead(SEND_BUTTON_PIN); // active LOW // Serial.print(F("=>")); Serial.println(buttonState); !!! if (lastButtonState == LOW && buttonState == HIGH) { Serial.println(F("Button released")); IrReceiver.start(); // re-enable receiver } // Check for static button state if (buttonState == LOW) { IrReceiver.stop(); // Button pressed send stored data or repeat Serial.println(F("Button pressed, now sending")); digitalWrite(STATUS_PIN, HIGH); if (lastButtonState == buttonState) { sStoredIRData.receivedIRData.flags = IRDATA_FLAGS_IS_REPEAT; } sendCode(&sStoredIRData); digitalWrite(STATUS_PIN, LOW); delay(DELAY_BETWEEN_REPEAT); // Wait a bit between retransmissions } else if (IrReceiver.available()) { // Button is not pressed, check for incoming data storeCode(IrReceiver.read()); IrReceiver.resume(); } lastButtonState = buttonState; } // loop
Результат работы скетча: сначала мы принимаем IR сигнал от пульта и декодируем его, а затем посылаем в эфир (дублируем), нажимая на кнопку «User» контроллера ESP32-EVB.

❯ CAN интерфейс
И последняя подсистема ESP32-EVB, которую мы разберём в этой статье — это CAN интерфейс. Для работы нам понадобится библиотека arduino-esp32-can-demo. Тестовый скетч принимает данные по CAN протоколу и отсылает по нему свои сообщения.

Код скетча работы с CAN интерфейсом:
/* OLIMEX ESP32-EVB CAN example */ #include <ESP32CAN.h> #include <CAN_config.h> CAN_device_t CAN_cfg; void setup() { Serial.begin(115200); Serial.println(); Serial.println(F("Start OLIMEX ESP32-EVB CAN example...")); CAN_cfg.speed = CAN_SPEED_1000KBPS; CAN_cfg.tx_pin_id = GPIO_NUM_5; CAN_cfg.rx_pin_id = GPIO_NUM_35; CAN_cfg.rx_queue = xQueueCreate(10, sizeof(CAN_frame_t)); ESP32Can.CANInit(); } // setup void loop() { CAN_frame_t rx_frame; //receive next CAN frame from queue if (xQueueReceive(CAN_cfg.rx_queue,&rx_frame, 3*portTICK_PERIOD_MS) == pdTRUE) { if (rx_frame.FIR.B.FF == CAN_frame_std) printf("New standard frame"); else printf("New extended frame"); if (rx_frame.FIR.B.RTR == CAN_RTR) printf(" RTR from 0x%08x, DLC %d\r\n",rx_frame.MsgID, rx_frame.FIR.B.DLC); else { printf(" from 0x%08x, DLC %d\n",rx_frame.MsgID, rx_frame.FIR.B.DLC); for (int i = 0; i < 8; i++) { printf("%c\t", (char)rx_frame.data.u8[i]); } printf("\n"); } } else { rx_frame.FIR.B.FF = CAN_frame_std; rx_frame.MsgID = 1; rx_frame.FIR.B.DLC = 8; rx_frame.data.u8[0] = 'h'; rx_frame.data.u8[1] = 'e'; rx_frame.data.u8[2] = 'l'; rx_frame.data.u8[3] = 'l'; rx_frame.data.u8[4] = 'o'; rx_frame.data.u8[5] = 'c'; rx_frame.data.u8[6] = 'a'; rx_frame.data.u8[7] = 'n'; ESP32Can.CANWriteFrame(&rx_frame); } } // loop
Для нас главным участком скетча является конфигурация CAN протокола для нашей платы ESP32-EVB, где, в частности, указываются TX (5) и RX (35) пины интерфейса.
CAN_cfg.speed = CAN_SPEED_1000KBPS; CAN_cfg.tx_pin_id = GPIO_NUM_5; CAN_cfg.rx_pin_id = GPIO_NUM_35; CAN_cfg.rx_queue = xQueueCreate(10, sizeof(CAN_frame_t)); ESP32Can.CANInit();
Далее вы можете модифицировать этот скетч под ваши задачи или поискать в интернете более продвинутые примеры работы с CAN протоколом.
❯ Заключение
До этого цикла статей, в интернете не существовало вменяемого руководства по контроллеру OLIMEX ESP32-EVB и его программированию, теперь, воспользовавшись информацией из этой и предыдущей статей, вы можете с лёгкостью применять OLIMEX ESP32-EVB для реализации своих IoT проектов.
С чем я всех нас (и компанию OLIMEX, которая наконец-то получила руководство по своему контроллеру ESP32-EVB, через 6 лет после начала его производства) и поздравляю.
ссылка на оригинал статьи https://habr.com/ru/company/timeweb/blog/709238/

Добавить комментарий