OLIMEX ESP32-EVB: программируем непрограммируемое
Продолжаем изучение платы 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
#include
#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 .
*
*/
/*
* 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
#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
#include "PinDefinitionsAndMore.h"
//#define EXCLUDE_EXOTIC_PROTOCOLS // saves around 900 bytes program space
#include
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
#include
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 лет после начала его производства) и поздравляю.