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:

  1. Кнопка User (BUT1)
  2. Управление реле
  3. Ethernet LAN8710A
  4. microSD картридер
  5. IR приёмник и передатчик
  6. 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

/*  *  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) 

Полный код файла ir.ino

/*   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/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *