Продолжаем изучение платы 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 лет после начала его производства) и поздравляю.
Комментарии (30)
shadrap
11.01.2023 13:28Что за ETH.h у вас и зачем ему NRST ?
Кстати пробовали сделать что-то большее чем просто открыть хост? Например UDP запросы?
smart_alex Автор
11.01.2023 14:05+1Пробовал :) На аналогичном железе даже запускал сервер на двух интерфейсах:
https://habr.com/ru/post/547044/
FGV
11.01.2023 15:42плотно «посажены» на нестандартные GPIO
что значит на нестандартные gpio?
smart_alex Автор
11.01.2023 18:22+1В ESP32 есть дефолтное распределение GPIO для HSPI и VSPI. На плате ESP32-EVB распределение GPIO не совпадает с дефолтным, что заставляет делать лишние телодвижения и переопределять GPIO, чтобы всё работало.
Тоже касается i2C подключений на плате и прочего.
А «плотно» потому, что на плате вообще нет свободных GPIO (это видно на распиновке).
FGV
11.01.2023 19:26+1В ESP32 есть дефолтное распределение GPIO для HSPI и VSPI
Так это не дефолтное, а минуя матрицу ввода-вывода (которая кстати ограничивает скорость переключения ног, что если и критично то скорее только для SPI экранов).
что заставляет делать лишние телодвижения и переопределять GPIO, чтобы всё работало
Эм, я так понимаю что бы работало в среде ардуино для определенных библиотек? Просто в esp-idf (Development Framework от самой эспрессиф) при инициализации периферии в обязательном порядке передаются номера ног.
Так что то, что авторы библиотек для ардуино и разработчики ESP32-EVB используют разные номера ног не делает подключение нестандартным.
smart_alex Автор
11.01.2023 19:35+1Совершенно верно - чтобы работало в среде Ардуино. В статье все примеры для Ардуино.
Про нативный еsp-idf - это совсем другая история.
smart_pic
11.01.2023 16:51В тестовом скетче мы примем инфракрасный сигнал от пульта управления, идентифицируем его (производителя оборудования, частоту сигнала, протокол управления и код нажатой клавиши), а также пошлём в эфир записанный нами сигнал, нажимая на функциональную кнопку контроллера ESP32-EVB.
А подробнее можно , особенно про частоту сигнала. Как вам ее удалось получить, ведь аппаратной поддержки такой возможности нет.
smart_alex Автор
11.01.2023 18:25Я ничего не выдумывал и использовал только библиотечные функции. Подробнее сейчас сказать не могу (в дороге).
smart_pic
12.01.2023 05:58частоту сигнала
ИК посылки невозможно определить ни какими библиотеками и программными способами , так для ES, ардуино , Флиппер и тому подобным не заложено аппаратно.
smart_alex Автор
12.01.2023 06:03Уважаемый smart_pic, я ни в коем случае не спорю с вами (скрее всего вы правы), просто для ответа по существу нужно предметно разбираться с кодом - помотрю чуть позже.
rexen
11.01.2023 19:56Интересно было бы услышать один-два сценария применения, учитывая то, что какая-нибудь Orange Pi куда распространённее, а по периферии - не хуже, учитывая то, что за цену Апельсинки можно обвешать любым набором - и релюшки и датчики и прочее. Энергопотребление? Спорное преимущество, учитывая наличие LAN, предполагающего стационарность.
Это извечная проблема всех микро-ПК - поддержка и цена. Если ни то ни другое - не конукурентноспособны, то есть сомнения в перспективах.
Поэтому народ и юзает во всяких Smart-Home и IoT либо Малинки, либо Апельсинки, либо вообще всякие ширпотребные роутеры, андроид-приставки, БУ-шные смарты, планшеты, которые вообще - за копейки. Либо БУшные же платформы х86.
smart_alex Автор
11.01.2023 20:09Интересно было бы услышать один-два сценария применения
Ну, тут два определяющих фактора: либо фантазия DIY-щика, либо совпадение конкретной задачи с ТТХ какого-то (в данном случае ESP32-EVB) контроллера.
Поэтому народ и юзает во всяких Smart-Home и IoT либо Малинки, либо Апельсинки
Это не тот народ. У меня уже несколько лет пылятся на полке Pi3 и Orange Pi Zero и нет никакого желания их оттуда доставать. Но это долгий разговор о предпочтениях в IoT архитектуре.
Причём пылятся не фигурально, а в прямом смысле слова.
YDR
11.01.2023 20:12Малинки-Апельсинки-Бананки - это все-таки Linux. А если надо RTOS, то FreeRTOS ближе к железу и реал-тайму.
ну и 100хBananaPi существенно дороже, чем 100хESP32
rexen
11.01.2023 22:36Я думаю большинству читателей интересней УД в плане автоматизации своего быта. Банально сеть датчиков и сеть исполняющих устройств. RTOS - это всё-таки больше из мира промки, а не дома.
Про Бананы я и не говорил - они вроде как и подороже и с коммьюнити хуже.
Меня вообще - в связи с поднятой темой статьи - больше интересовали именно решения на базе LAN. Беспроводные - не люблю. А тянуть сигнальные линии от хаты с сервером куда-нибудь в гараж - не феншуйно. Итого мне виделась система УД для частного дома в виде HA-сервера на базе Андроида/Линукса с непосредственно подчинённой - ближайшей - периферией, располагающейся тут же в стойке или щитке и сетью удалённых устройств, общающихся-питающихся по LAN. И вот слэйвы в виде 10-долларовых Апельсинок сюда хорошо подходят. Дешевле только LAN-шилды для микроконтроллеров - типа 5100.
Сабж тут - ни туда-ни сюда.
smart_alex Автор
12.01.2023 05:53Сейчас столько различного железа, что можно сделать как угодно - это вопрос личных архитектурных предпочтений, квалификации и фантазии разработчика.
Я сторонник распределённой IoT архитектуры, где каждый контроллер ("умный" и самодостаточный) сочетает в себе и логику и управление и веб-интерфейс и коммуникационные возможности, а Linux миникомпьютер обслуживает только функции высшего уровня типа агрегации данных со всей системы, их хранения в SQL базе, визуализации и т. п.
При таком подходе систему трудно вывести из строя, а поломка или остановка на профилактику "главного" Linux компьютера никак не сказывается на работоспособности системы.
Вот пример реализации такого сервера в одном из моих проектов:
https://hi-lab.ru/arduino-mega-server/ams-pro/projects/main-server
smart_pic
12.01.2023 06:33Меня вообще - в связи с поднятой темой статьи - больше интересовали именно решения на базе LAN.
И они есть , только за потоком инфы про ESP, малинки и ардуино их не видят.
Почему все предлагают решения на базе ESP ? - потому что это раскручено , у всех на слуху сочетание знакомых букв, а по факту все это DIY наборы предназначенные для любителей что то себе попрограммировать, и практически не интересуют интеграторов оборудования направления УД , да и не только. И это сильно видно особенно в сегменте решений на базе ПРОВОДНОГО LAN . То что к ESP стали прикручивать проводной LAN в дополнение к Wi-Fi, говорит от том что в этом сегменте есть большой потенциал и очень мало универсальных контроллеров с проводным LAN. Но DIY решения когда нужно допрограммировать (и представленная плата из статьи в том числе) не интересна для интеграторов, и их нельзя из коробки использовать как универсальное решение для широкого круга задач автоматизации.
smart_alex Автор
12.01.2023 06:48Так весь мой блог посвящён поиску нормального контроллера для DIY. Выпускается всё, что угодно, кроме того, что нужно.
rexen
12.01.2023 09:25Я под акронимом LAN подразумевал именно проводной её вариант. Беспроводной обычно все так и кличут - Wi-Fi. Только вот "проводной" мод ESP-шки - т.е. герой статьи - вынужден конкурировать с упомянутыми минимальными версиями Апельсинки, стоящими 10 Евро. Сабж - от 20 Евро. Спрашивается, "а смысл"? Потому и попросил обрисовать вариант, когда переплата вдвое была бы оправдана.
smart_alex Автор
12.01.2023 09:43Смысл здесь в предпочтениях. ESP32— это невероятно мощная вещь, которая может реализовать и управление оборудованием, и веб-интерфейс, и многослойную логику, и хранение данных, и их визуализацию, и коммуникации по Ethernet, Wi-Fi, Bluetooth, LoRa, nRF24 и ещё по куче протоколов и интерфейсов, причём делать это одновременно.
И при этом это МИКРОКОНТРОЛЛЕР, а не миникомпьютер (со всеми его достоинствами и недостатками).
Другими словами, Linux компьютер просто НЕ НУЖЕН в 99% случаев, но если он вам нравится — используйте на здоровье.
rexen
12.01.2023 09:56Да при чём тут "нравится - не нравится"? Я же написал - этот ваш Олимекс в два раза дороже Апельсинки. Вот и всё. CPU там или MCU - не суть важно. Чуть большее потребление Апельсинки уравновешивается её бОльшей универсальностью.
Мне была бы интересна версия на базе ESP и проводного LAN, но БЕЗ прочего обвеса. Это было бы прежде всего дешевле. А то два обязательных набортных реле - где-то мало, а где-то вообще не нужно. Не сомневаюсь, что на базе сабжа можно в каком-то конкретном случае состряпать конечное решение. Ну например домофон - ту тебе релюшка на управление замком и релюшка на освещение подъезда. ИК для ПДУ возможно. Но в целом - двойственное впечатление. Не зря все многочисленные микро-ПК всё-таки в виде конструкторов выпускают - база+шилды. А не монолитом.
smart_alex Автор
12.01.2023 10:02OLIMEX такой же мой, как и ваш :)
По поводу применимости я уже раза два написал, что это вопрос предпочтений и конкретной задачи.
А если вам нежен ESP32 без всего, но с LAN, то, если я ничего не путаю, на Али продаются как раз такие мини-платы.
rexen
12.01.2023 11:33Кстати, да:
я как-то проглядел их давно когда искал альтернативы. И, к слову, оказывается у Олимекса точно такое же есть, но, как обычно, минимум раза в полтора дороже. Ну хоть не в разы - и то хорошо - уже можно и повыбирать - "безродный китай" или какой-никакой бренд.
maxwolf
13.01.2023 03:49Вот, кстати, да — почему бы на этой плате не сделать PoE?
smart_alex Автор
13.01.2023 06:31Дело в том, что кроме разработчиков OLIMEX этого никто не может сделать (поскольку это их плата), а эти разработчики, как и все железячники, находятся "на своей волне" и воплощают в железе своё понимание того, как это должно быть.
И по опыту могу сказать, крайне трудно поддаются влиянию извне и попыткам им что-то подсказать.
rexen
12.01.2023 20:40Кстати, если я правильно помню курсы ОСРВ из универа, то эту самую RTOS можно и из Линукса и из Винды сделать — вопрос лишь в том, насколько глубоко мы должны погрузиться по уровням абстракции от железа. Так-то аппаратные прерывания, в т.ч. по таймеру — они в любой ОС есть.
anwender95
12.01.2023 08:38Прочитал статью, но так и не понял, что тут запрограммировано из "непрограммируемого".
smart_alex Автор
12.01.2023 08:50Это зависит от квалификации и кажется простым, только когда выложено на тарелочке с голубой каёмочкой, проверено и протестировано.
Пока я разбирался с финтами этой платы типа режима 1BIT доступа к SD карте памяти - мне это совсем не казалось лёгким занятием.
YDR
Подробно и наглядно! Супер! Спасибо!
я так понял, WiFi нет? странный подход производителей... Но ладно, пусть так.
smart_alex Автор
Как это нет Wi-Fi? :) Это же ESP32, Wi-Fi само собой есть, просто в статье я рассматривал программирование специфических для платы ESP32-EVB вещей.