Продолжаем ультимативный гайд по контроллеру Kincony KC868-A4, начатый в предыдущей статье. Сегодня мы подробно разберём распиновку KC868-A4 и познакомимся с принципами программирования компонентов (функциональных блоков) этого контроллера. Все примеры будут сопровождаться готовым рабочим кодом, который вы можете использовать в своих проектах.

Начнём мы с распиновки центрального модуля ESP32-S и разбора того, что и как подключено к нему инженерами компании Kincony.

▍ Распиновка KC868-A4


Микроконтроллер ESP32 всем хорош, но есть у него один врождённый недостаток — крайне малое число свободных GPIO контактов. И каждый, кто хочет спроектировать контроллер на ESP32 или хотя бы просто применить его в своём более-менее развитом проекте, вынужден каким-то образом решать эту проблему.

Приходится либо применять расширители портов, либо пытаться как-то «вместить невмещаемое» и пытаться по крохам распределять имеющиеся GPIO и отказываться от тех или иных компонентов, которые могли бы быть установлены на плату.

Инженеры компании Kincony так и сделали — постарались подобрать набор из, на их взгляд, наиболее востребованных компонентов (функциональных блоков), куда вошли:

  • 4 цифровых входа
  • 4 аналоговых входа
  • 2 аналоговых выхода
  • 4 реле
  • Датчик(и) температуры/влажности
  • Интерфейс RS232
  • Модули (R/T) 433 МГц
  • Инфракрасные (IR) модули (R/T)
  • Пищалка (buzzer)

Этот набор забрал все свободные GPIO контроллера ESP32, больше не осталось ни одного контакта, к которому вы могли бы подключить что-то своё. Например, индикатор, который не помешал бы любому контроллеру.

Но глядя на то, что инженеры Kincony установили на плату KC868-A4, можно сказать, что у них получилось довольно сбалансированное решение для небольших проектов и целей обучения программированию микроконтроллеров.

Проблема заключается в том, что если вы захотите подключить какую-то свою деталь или модуль к KC868-A4, то без паяльника и соответствующей квалификации у вас ничего не получится — свободных для подключения GPIO просто нет.

С другой стороны, идеология подобных контроллеров и не предусматривает подобных вольностей со стороны пользователей — вы покупаете готовое решение и просто используете тот набор возможностей, которое оно предоставляет.

Итак, ниже представлено сочинение инженеров компании Kincony на тему «Как я распределил GPIO контакты ESP32», записанное мной в виде таблицы. В ближних к контроллеру столбцах указаны обозначения линий и подключений из принципиальной схемы (см. предыдущую статью), а в дальних — входы и выходы функциональных блоков контроллера KC868-A4.



В целом неплохо, основным достоинством такого расклада GPIO я бы назвал то, что всё это реально работает и не мешает друг другу.

Если бы проектированием Kincony KC868-A4 занимался я, то я бы в первую очередь избавился от подключения беспроводных модулей на 433 МГц, как архаичного, в пользу чего-нибудь более современного, типа nRF24 или LoRa, вывел бы контакты для подключения оборудования по I2C и SPI и повесил бы цифровые входы/выходы на расширители портов. А также добавил бы блок часов реального времени и вывел блок реле в отдельный модуль на DIN-рейку. Но тогда это был бы уже совсем другой контроллер, поэтому давайте спустимся с небес на землю и продолжим разбор Kincony KC868-A4.

С распределением GPIO контактов ESP32 всё более-менее понятно и теперь, вооружившись этой информацией, давайте приступим к собственно программированию отдельных функциональных блоков Kincony KC868-A4 и начнём мы с программной среды.

▍ Программная среда для KC868-A4


Программировать контроллер Kincony KC868-A4 можно в любой подходящей для этого среде — вы можете использовать для этого вашу любимую IDE, я же буду приводить примеры для среды Arduino 1.8.5. При этом предполагается, что вы обладаете необходимой квалификацией и знакомы с работой в Arduino IDE и умеете программировать микроконтроллер ESP32.

Для работы с Kincony KC868-A4, из всего списка поддерживаемых контроллеров вам нужно выбрать вариант «NodeMCU-32S». Это прозрачно намекает на то, что Kincony KC868-A4 является расширенной и дополненной различной периферией версией NodeMCU-32S.



Остальные настройки видны на скриншоте, вам нужно выставить у себя такие же (кроме номера порта, который у вас будет своим). Теперь переходим к разбору примеров программирования функциональных блоков контроллера Kincony KC868-A4.

▍ Программируем реле KC868-A4


Начнём мы конечно с управления реле, как с самого простого и востребованного функционала подобных контроллеров. Реле, как это видно на вышеприведённой схеме распиновки, подключены к GPIO 2, 15, 5, 4. Для примера управления реле контроллера Kincony KC868-A4 создадим скетч, который по очереди переключает реле, создавая эффект «бегущего огня».

/*
  Kincony KC868-A4
  Relays example
*/

byte pins[] = {2, 15, 5, 4};
byte pos = 0;

void setup() {
  Serial.begin(115200);
  Serial.println(F("Start Kincony KC868-A4 Relays example..."));

  pinMode(pins[0], OUTPUT);
  pinMode(pins[1], OUTPUT);
  pinMode(pins[2], OUTPUT);
  pinMode(pins[3], OUTPUT);
}

void clear() {
  digitalWrite(pins[0], LOW);
  digitalWrite(pins[1], LOW);
  digitalWrite(pins[2], LOW);
  digitalWrite(pins[3], 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 > 3) {pos = 0;}
}

Этого примера вполне достаточно, чтобы вы на его основе могли реализовать любую логику управления реле контроллера Kincony KC868-A4. Вот результат вывода в Serial нашего тестового скетча:



▍ Buzzer и Tone


Как я уже отметил ранее, плата Kincony KC868-A4 снабжена приятным дополнением в виде пьезоэлектрического излучателя. Он может помочь вам сделать удобное звуковое оповещение о различных состояниях (авария, какие-то события и т. д.) контроллера. Вот простой пример работы с пищалкой.

/*
  Kincony KC868-A4
  Buzzer example
*/

#define BUZZER_PIN 18

void setup() {
  Serial.begin(115200);
  Serial.println(F("Start Kincony KC868-A4 Buzzer example..."));
    
  pinMode(BUZZER_PIN, OUTPUT);
}

void loop() {
  digitalWrite(BUZZER_PIN, HIGH); delay(500);
  digitalWrite(BUZZER_PIN, LOW);  delay(500);
}

Вышеприведённого примера достаточно для простого привлечения внимания к контроллеру при возникновении какого-либо события, но если использовать тоновую окраску звучания, то можно выделить каждый тип события своим отдельным звуком (мелодией). Вот пример использования различных тонов при оповещении.

/*
  Kincony KC868-A4
  Buzzer Tone example
*/

#define BUZZER_PIN 18
const int TONE_PWM_CHANNEL = 0;

void setup() {
  Serial.begin(115200);
  Serial.println(F("Start Kincony KC868-A4 Buzzer Tone example..."));
  
  ledcAttachPin(BUZZER_PIN, TONE_PWM_CHANNEL);
}

void loop() {
  ledcWriteNote(TONE_PWM_CHANNEL, NOTE_C, 4); delay(500);
  ledcWriteNote(TONE_PWM_CHANNEL, NOTE_D, 4); delay(500);
  ledcWriteNote(TONE_PWM_CHANNEL, NOTE_E, 4); delay(500);
  ledcWriteNote(TONE_PWM_CHANNEL, NOTE_F, 4); delay(500);
  ledcWriteNote(TONE_PWM_CHANNEL, NOTE_G, 4); delay(500);
  ledcWriteNote(TONE_PWM_CHANNEL, NOTE_A, 4); delay(500);
  ledcWriteNote(TONE_PWM_CHANNEL, NOTE_B, 4); delay(500);
  ledcWriteNote(TONE_PWM_CHANNEL, NOTE_C, 5); delay(500);
}


▍ DAC


Kincony KC868-A4 снабжён двумя цифро-аналоговыми преобразователями (DAC), которые могут формировать напряжение в диапазоне от 0 до 10 вольт. Это может пригодиться для управления различным оборудованием. Ниже приведён пример генерации постоянного напряжения 5 вольт на выходе DAC1 контроллера.

/*
  Kincony KC868-A4
  DAC example
*/

#define DAC1 26
#define DAC2 25

void setup() {
  Serial.begin(115200);
  Serial.println(F("Start Kincony KC868-A4 DAC example..."));
  
  int value = 127; // 255 = 10V
  dacWrite(DAC1, value);
}

void loop() {

}

Понятно, что генерация постоянного напряжения — это только небольшая область применения DAC, в основном востребована динамическая генерация (периодических) сигналов. Далее вы можете видеть пример кода для генерации пилообразного сигнала на выходе DAC1.

/*
  Kincony KC868-A4
  DAC Saw example
*/

#define DAC1 26
#define DAC2 25

byte value = 0;

void setup() {
  Serial.begin(115200);
  Serial.println(F("Start Kincony KC868-A4 DAC Saw example..."));
}

void saw() {
  value++;
}

void loop() {
  dacWrite(DAC1, value);
  saw();
}

Небольшой модификацией этого кода вы можете добиться генерации сигналов контроллером Kincony KC868-A4 практически любой формы.



▍ Цифровые входы


Контроллер Kincony KC868-A4 имеет 4 опторазвязанных цифровых входа «сухой контакт», которые подключены на GPIO 36, 39, 27 и 14. Обслуживание этих входов предельно просто и осуществляется практически одной функцией digitalRead() — вы получаете текущее состояние любого из входов контроллера и далее используете его в коде по своему усмотрению.

/*
  Kincony KC868-A4
  Digital Input example
*/

#define INPUT_PIN1 36
#define INPUT_PIN2 39
#define INPUT_PIN3 27
#define INPUT_PIN4 14

void setup() {
  Serial.begin(115200);
  Serial.println(F("Start Kincony KC868-A4 Digital Input example..."));
  pinMode(INPUT_PIN1, INPUT);
}

void loop() {
  Serial.println(digitalRead(INPUT_PIN1));
  delay(10);
}


▍ Аналоговые входы


Kincony KC868-A4 имеет 4 «аналоговых» входа, которые подключены на GPIO 32, 33, 34 и 35. Как уже было отмечено в первой статье, формированием уровней для сигналов 0–5 В и 4-20 мА занимаются входные каскады, которые формируют 2 входа для напряжения (GPIO 32 и 33) и 2 входа для тока (GPI 34 и 35). В коде работа с обоими типами входных сигналов не различается, она осуществляется функцией analogRead() с разрешением 4096.

/*
  Kincony KC868-A4
  Analog Input example
*/

#define ANALOG_PIN1 32 // INA1 0-5V
#define ANALOG_PIN2 33 // INA2 0-5V
#define ANALOG_PIN3 34 // INA3 4-20 mA
#define ANALOG_PIN4 35 // INA4 4-20 mA

int value1 = 0;

void setup() {
  Serial.begin(115200);
  Serial.println(F("Start Kincony KC868-A4 Analog Input example..."));
  pinMode(ANALOG_PIN1, INPUT);
}

void loop() {
  value1 = analogRead(ANALOG_PIN1); // 0-4096
  delay(1000);
  Serial.printf("Value on pin %d = %d\n", ANALOG_PIN1, value1);
}


▍ Датчики температуры и влажности


Kincony KC868-A4 имеет колодку 3V, S, GND для подключения датчика температуры DS18B20. К этой колодке можно также подключить сеть из нескольких датчиков DS18B20, датчик влажности или любой другой датчик, подходящий по типу подключения. Нужно только помни��ь, что на плате уже установлена подтяжка линии данных к напряжению питания.

Для работы с датчиками температуры DS18B20 требуются библиотеки DS18B20 и OneWire. Ниже приведён код для одного датчика DS18B20, подключённого к плате KC868-A4.

/*
  Kincony KC868-A4
  DS18B20 example
*/

#include <DS18B20.h>

#define  LOW_ALARM 30
#define HIGH_ALARM 40

DS18B20 ds(13);

void setup() {
  Serial.begin(115200);
  Serial.println(F("Start Kincony KC868-A4 DS18B20 example..."));
  
  ds.doConversion();
  while (ds.selectNext()) {
    ds.setAlarms(LOW_ALARM, HIGH_ALARM);
  }
}

void loop() {
  ds.doConversion();

  while (ds.selectNextAlarm()) {
    Serial.print("Alarm Low: ");   Serial.print(ds.getAlarmLow());  Serial.println(" °C");
    Serial.print("Alarm High: ");  Serial.print(ds.getAlarmHigh()); Serial.println(" °C");
    Serial.print("Temperature: "); Serial.print(ds.getTempC());     Serial.println(" °C\n");
  }

  delay(2000);
}

В коде выставляются пороги срабатывания и, в случае выхода контролируемой температуры за определённые значения, в Serial выводится соответствующее сообщение.



Библиотека DS18B20 содержит множество примеров использования датчиков температуры DS18B20 на все случаи жизни — вы легко можете использовать их для своих проектов.

▍ Приёмник и передатчик на 433 МГц


Как вы уже знаете, в состав контроллера Kincony KC868-A4 входят модули приёмника и передатчика на 433 МГц. Эти модули могут быть использованы для управления различной техникой и приёма данных с датчиков, использующих диапазон 433 МГц для передачи своих сигналов.

Приёмник подключен на GPIO 19 контроллера, а передатчик на GPIO 21. Для работы с беспроводной передачей и приёмом данных используется популярная библиотека RC-Switch. Вот пример посылки данных и управляющих команд беспроводным модулем на 433 МГц контроллера Kincony KC868-A4.

/*
  Kincony KC868-A4
  433 Transmit example
 */

#include <RCSwitch.h>
RCSwitch mySwitch = RCSwitch();

#define DELAY_MS 500

void setup() {
  Serial.begin(115200);
  Serial.println(F("Start Kincony KC868-A4 433 Transmit example..."));
  
  mySwitch.enableTransmit(digitalPinToInterrupt(21));
}

void loop() {
  mySwitch.switchOn ("11111", "00010");      delay(DELAY_MS);
  mySwitch.switchOff("11111", "00010");      delay(DELAY_MS);
  mySwitch.send(5393, 24);                   delay(DELAY_MS);
  mySwitch.send("000000000001010100010001"); delay(DELAY_MS);
  mySwitch.sendTriState("00000FFF0F0F");     delay(DELAY_MS); 
}

Наглядная визуализация работы этого скетча в программе SDRSharp. Видны посылки в эфир данных контроллером каждые 500 миллисекунд.



Аналогичным образом вы можете использовать примеры приёма (GPIO 19) сигналов в диапазоне 433 МГц, идущие в составе библиотеки RC-Switch.

▍ Инфракрасные (IR) приёмник и передатчик


Завершим мы обзор программных модулей Kincony KC868-A4 примером работы с приёмником (GPIO 23, IRD) и излучателем (GPIO 22, IRS) инфракрасных сигналов. Используя эти приёмник и передатчик, вы можете организовать управление любой техникой, которая использует инфракрасные пульты для своей работы. Вы можете как записывать коды от ваших пультов управления, так и выдавать их (коды) в эфир для управления вашим оборудованием.

Для работы с инфракрасными приёмником и передатчиком используется библиотека Arduino-IRremote. Рассмотрим пример, созданный на её основе. В этом тестовом скетче мы будем принимать инфракрасный сигнал от бытового пульта управления, идентифицировать этот сигнал (производителя оборудования, частоту сигнала, протокол управления, и код нажатой клавиши), а также посылать в эфир записанный сигнал, нажимая на функциональную клавишу контроллера Kincony KC868-A4.

Исходные данные:

  • IR приёмник: GPIO 23
  • IR излучатель: GPIO 22
  • Функциональная кнопка: GPIO 0
  • STATUS_PIN используется в скетче для индикации событий, но поскольку в Kincony KC868-A4 задействованы практически все GPIO ESP32 (и также занят GPIO2), то встроенный светодиод (D2) переопределён на (условно) свободный пин D12.

Полный код скетча содержит 2 файла: основной (ir_example.ino) и файл с настройками (PinDefinitionsAndMore.h). В файле PinDefinitionsAndMore.h нужно изменить номера GPIO в соответствии с распиновкой контроллера Kincony KC868-A4 (строки с настройками помечены тремя восклицательными знаками).

#define LED_BUILTIN 12 // !!!
#define IR_RECEIVE_PIN       23  // !!!
#define IR_SEND_PIN          22  // !!!
#define APPLICATION_PIN      0   // !!!

Полный код файла 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 12 // !!!
 
//#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       23  // !!!
#define IR_SEND_PIN          22  // !!!
#define TONE_PIN                27  // D27 25 & 26 are DAC0 and 1
#define APPLICATION_PIN      0   // !!!


#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_example.ino
/*
  Kincony KC868-A4
  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>

/*
 * Define macros for input and output pin etc.
 */
#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(F("Start Kincony KC868-A4 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);

  /*
  Serial.print(F("Ready to receive IR signals of protocols: "));
  printActiveIRProtocols (&Serial);
  Serial.print(F("at pin "));
    
#if defined(ARDUINO_ARCH_STM32) || defined(ESP8266)
  Serial.println(IR_RECEIVE_PIN_STRING);
#else
  Serial.println(IR_RECEIVE_PIN);
#endif

  Serial.print(F("Ready to send IR signals at pin "));
#if defined(ARDUINO_ARCH_STM32) || defined(ESP8266)
  Serial.println(IR_SEND_PIN_STRING);
#else
  Serial.print(IR_SEND_PIN);
#endif
    
  Serial.print(F(" on press of button at pin "));
  Serial.println(SEND_BUTTON_PIN);

  Serial.print(F("LED_BUILTIN: ")); Serial.println(LED_BUILTIN);
  Serial.print(F("STATUS_PIN: ")); Serial.println(STATUS_PIN);
  Serial.print(F("TONE_PIN: ")); Serial.println(TONE_PIN);
  Serial.print(F("IR_RECEIVE_PIN: ")); Serial.println(IR_RECEIVE_PIN);
  Serial.print(F("IR_SEND_PIN: ")); Serial.println(IR_SEND_PIN);
  Serial.print(F("APPLICATION_PIN: ")); Serial.println(APPLICATION_PIN);
  */

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

  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» контроллера Kincony KC868-A4.



▍ Заключение


Во второй статье цикла мы рассмотрели «атомарные» примеры программирования различных функциональных блоков контроллера Kincony KC868-A4. Используя эту информацию, вы можете легко начать программировать контроллер под свои задачи — всё расписано «от и до»: фото, схемы, распиновка, примеры кода, ссылки на библиотеки, скриншоты, пояснения и т. д.

В следующей статье мы рассмотрим более сложные примеры работы с KC868-A4, такие, как работа с беспроводной Wi-Fi частью и удалённое управление контроллером через интернет при помощи мессенджеров Telegram и/или Whatsapp.