Kincony KC868-A8: старший брат из Ханчжоу. Часть 2. Программируем A8

xsbezt3hvfimclrbsmra7l5zute.jpeg

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

Мне нравится и Kincony KC868-A4 и KC868-A8, но наш сегодняшний подопытный с его 8-ю цифровыми входами, 8-ю реле на борту, I2C разъёмом и Ethernet интерфейсом смотрится значительно более внушительно и так и просится в какой-нибудь проект по домашней автоматизации. Поэтому мне было интересно разобраться, что там и как устроено, и как всем этим можно управлять.

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

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


Для начала давайте ознакомимся с полным списком всех функциональных блоков контроллера. Кроме ESP32 (ESP-WROOM-32) модуля, плата Kincony KC868-A8 содержит:

  • 8 цифровых опторазвязанных входов («сухой контакт»);
  • 2 аналоговых входа 0–5 В;
  • 8 реле 10А 220В;
  • 4 контакта для подключения температурных и прочих датчиков;
  • Разъёмы для подключения модулей приёмника/передатчика на 433 МГц;
  • Разъём I2C;
  • Ethernet LAN8270A.


Здесь сразу выделяется «фишка» контроллера KC868-A8 — сетевой Ethernet интерфейс на чипе LAN8270A. Наличие этого интерфейса значительно расширяет возможности контроллера, но за это приходится расплачиваться «потерей» 9 GPIO из и без того крайне скудного их набора.

Проблему нехватки свободных пинов микроконтроллера ESP32 инженеры Kincony решили путём использования расширителей портов — в данном случае 8 цифровых входов и 8 реле подключаются к ESP32 через микросхемы PCF8574P расширителей портов с I2C интерфейсом.

Также стоит отметить появление на плате (по сравнению с младшим братом KC868-A4) I2C разъёма для подключения дополнительного оборудования — это значительно расширяет возможности по адаптации контроллера к задачам ваших конкретных проектов.

Ниже представлена распиновка контроллера KC868-A8. В ближних к контроллеру столбцах указаны обозначения линий и подключений из принципиальной схемы, а в дальних — входы и выходы функциональных блоков контроллера KC868-A8.

unn_hlwrma1u6pyprgv5p-xr5da.png

Здесь нужно пояснить: на данный момент контроллер Kincony KC868-A8 имеет две версии — базовую и модернизированную V1.4. Сама схема распиновки относится к базовому варианту, а изменения, внесённые в версию V1.4, выделены в виде отдельного блока. В моём распоряжении имеется версия контроллера V1.4, поэтому всё дальнейшее повествование будет относиться к ней.

Ещё пара пояснений. Звёздочками (*) на схеме обозначены соединения, которые «запроектированы» инженерами Kincony, но отсутствуют в реальности на плате (по крайней мере, в базовой версии), а загадочные обозначения «DI9» и «DI10» относятся к «цифровым входам» 9 и 10 (после 8-и опторазвязанных цифровых входов на расширителе портов) — всё это креатив (недоработки или переработки) инженеров компании Kincony.

Теперь, после разбора распиновки контроллера, можно приступать к изучению программирования функциональных блоков Kincony KC868-A8.

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


Программировать контроллер Kincony KC868-A8 мы будем в среде Arduino 1.8.5. Я здесь не буду останавливаться на установке поддержки ESP32 в Arduino и прочих подготовительных действиях — в интернете есть огромное количество руководств на эту тему. Для работы с контроллером Kincony KC868-A8 нам нужно выбрать вариант «NodeMCU-32S».

wbjdfcm1wrxkuqsuthwn5xu7jy0.png

Здесь есть ещё один небольшой, но важный момент: в базовом варианте платы NodeMCU-32S I2C интерфейс завязан на пины 21 и 22, а в нашем контроллере Kincony KC868-A8 это пины 4 и 5. Соответственно, в файле

\hardware\espressif\esp32\variants\nodemcu-32s\pins_arduino.h


нужно поменять старое определение

static const uint8_t SDA = 21;
static const uint8_t SCL = 22;


на соответствующее нашему KC868-A8

static const uint8_t SDA = 4;
static const uint8_t SCL = 5;


иначе ни реле, ни цифровые входы контроллера работать не будут.

Теперь переходим непосредственно к программированию функциональных блоков KC868-A8.

▍ Программирование работы реле


Поскольку для управления работой реле используется расширитель портов на микросхеме PCF8574P, то и обращение к реле в коде отличается от стандартного. Расширитель работает по интерфейсу I2C и для работы с ним нужно установить специальную библиотеку PCF8574_library.

Чип PCF8574P обслуживания реле имеет адрес 0×24 на I2C шине и нужно соответствующим образом инициализировать объект для работы с ним. Сам код управления реле довольно прозрачен:

/*
  Kincony KC868-A8
  Relays example
*/

#include "Arduino.h"
#include "PCF8574.h"

#define I2C_RELAYS_ADR 0x24

PCF8574 pcf(I2C_RELAYS_ADR);

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

  pcf.pinMode(P0, OUTPUT);
  pcf.pinMode(P1, OUTPUT);
  pcf.pinMode(P2, OUTPUT);
  pcf.pinMode(P3, OUTPUT);
  pcf.pinMode(P4, OUTPUT);
  pcf.pinMode(P5, OUTPUT);
  pcf.pinMode(P6, OUTPUT);
  pcf.pinMode(P7, OUTPUT);

  Serial.print("Init PCF8574... ");
  if (pcf.begin()){Serial.println(F("Ok"));}
             else {Serial.println(F("Error"));}
}

void loop() {
  pcf.digitalWrite(P0, HIGH);
  Serial.print(F("Relay #")); Serial.print(P0); Serial.println(F(" ON"));
  delay(10000);
  pcf.digitalWrite(P0, LOW);
  Serial.print(F("Relay #")); Serial.print(P0); Serial.println(F(" OFF"));
  delay(10000);
}


И для вас не составит труда реализовать любую логику управления реле контроллера Kincony KC868-A8 на его основе. Результат вывода в Serial тестового скетча управления реле через расширитель портов PCF8574P:

l-fxqxx_fbkaeia0qk-zc3fu0ou.png

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


Контроллер Kincony KC868-A8 имеет 8 опторазвязанных цифровых входов «сухой контакт», которые подключены через I2C расширитель портов PCF8574P. Как и в случае с реле, программирование цифровых входов осуществляется при помощи библиотеки PCF8574_library.

Чип PCF8574P обслуживания цифровых входов имеет адрес 0×22 на I2C шине. Код, как и в случае с реле, тоже прост и прозрачен и не должен вызвать у вас никаких затруднений:

/*
  Kincony KC868-A8
  Digital input example
*/

#include "Arduino.h"
#include "PCF8574.h"

#define I2C_DIGITAL_ADR 0x22

PCF8574 pcf(I2C_DIGITAL_ADR);

void setup() {
  Serial.begin(115200);
  Serial.println(F("Start Kincony KC868-A8 Digital input example..."));

  pcf.pinMode(P0, INPUT);
  pcf.pinMode(P1, INPUT);
  pcf.pinMode(P2, INPUT);
  pcf.pinMode(P3, INPUT);
  pcf.pinMode(P4, INPUT);
  pcf.pinMode(P5, INPUT);
  pcf.pinMode(P6, INPUT);
  pcf.pinMode(P7, INPUT);

  Serial.print("Init PCF8574... ");
  if (pcf.begin()){Serial.println(F("Ok"));}
             else {Serial.println(F("Error"));}

  delay(20);
}

void loop() {
  byte val1 = pcf.digitalRead(P0);
  byte val2 = pcf.digitalRead(P1);
  byte val3 = pcf.digitalRead(P2);
  byte val4 = pcf.digitalRead(P3);
  byte val5 = pcf.digitalRead(P4);
  byte val6 = pcf.digitalRead(P5);
  byte val7 = pcf.digitalRead(P6);
  byte val8 = pcf.digitalRead(P7);
 
  if (val1 == LOW) Serial.println("Key1 pressed");
  if (val2 == LOW) Serial.println("Key2 pressed");
  if (val3 == LOW) Serial.println("Key3 pressed");
  if (val4 == LOW) Serial.println("Key4 pressed");
  if (val5 == LOW) Serial.println("Key5 pressed");
  if (val6 == LOW) Serial.println("Key6 pressed");
  if (val7 == LOW) Serial.println("Key7 pressed");
  if (val8 == LOW) Serial.println("Key8 pressed");
 
  delay(300);
}


Результат работы тестового скетча работы с цифровыми входами через I2C расширитель портов PCF8574P:

qudr2qpblhjm_nai1puqwosvk5w.png

Всё работает чётко и предсказуемо, так как и должно работать.

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


Kincony KC868-A8 имеет 2 аналоговых входа для сигналов 0–5 В. С этими входами у KC868-A8 есть некоторая запутанность — в разных версиях платы они разведены по-разному. Мы будем разбирать устройство аналоговых входов для версии V1.4 в которой они подключены к GPIO34 и GPIO35 (в базовой версии это GPIO32 и GPIO33).

Код также предельно прост и сводится к вызову стандартной функции analogRead () и последующему анализу возвращаемых ей значений.

/*
  Kincony KC868-A8
  Analog example
*/

#include "Arduino.h"

#define ANALOG_A1   34    // IO34 (V1.4)
#define ANALOG_A2   35    // IO35 (V1.4)

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

  pinMode(ANALOG_A1, INPUT);
  pinMode(ANALOG_A2, INPUT);
}

void loop() {
  Serial.printf("Current Reading A1 on Pin%d=%d\n", ANALOG_A1, analogRead(ANALOG_A1));
  Serial.printf("Current Reading A2 on Pin%d=%d\n", ANALOG_A2, analogRead(ANALOG_A2));
  delay(5000);
}

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


Со входами для датчиков у KC868-A8 тоже есть некоторая путаница — в разных версиях платы они тоже разведены по-разному. В версии V1.4 это пины 14, 13, 32, 33. К этим GPIO могут быть подключены датчики температуры, влажности или любые другие датчики, подходящий по типу подключения. Нужно только помнить, что на плате уже установлена подтяжка линий данных к напряжению питания.

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

/*
  Kincony KC868-A8
  DS18B20 example
*/

#include 

#define  LOW_ALARM 30
#define HIGH_ALARM 40

DS18B20 ds(14); // 14, 13, 32, 33 (V1.4)

void setup() {
  Serial.begin(115200);
  Serial.println(F("Start Kincony KC868-A8 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 выводится соответствующее сообщение.

skolgamh-yea0tgbd48olov4l5q.png

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

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


Здесь мы не будем подробно останавливаться на использовании приёмника и передатчика на 433 МГц, вы можете прочитать об этом в статье о плате KC868-A4. Упомяну только, что в KC868-A8 приёмник и передатчик подключены на GPIO15 и GPIO2.

А в том случае, если вам не нужна работа с беспроводной передачей данных на 433 МГц, вы можете использовать GPIO15 и GPIO2 для подключения каких-то своих компонентов (см. схемотехнику KC868-A8 из предыдущей статьи).

▍ Ethernet LAN8270A


Контроллер Kincony KC868-A8 имеет на борту чип Ethernet интерфейса LAN8270A и возможность подключения и работы по проводной Ethernet сети. При этом ESP32 даёт возможность работы по беспроводному Wi-Fi интерфейсу. То есть у нас появляется возможность подключать наш контроллер либо по проводному Ethernet, либо по беспроводному Wi-Fi, либо и по тому и по другому интерфейсу одновременно. Это позволяет очень гибко организовать управление контроллером в различных ситуациях (например, в случае потери связи по беспроводному каналу и т. п.).

Использование Ethernet интерфейса совместно с ESP32 довольно просто. Вначале нужно указать соответствующие библиотеки:

#include 
#include 


Затем тип используемой микросхемы:

#define ETH_TYPE        ETH_PHY_LAN8720


Пины управления:

#define ETH_CLK_MODE    ETH_CLOCK_GPIO17_OUT
#define ETH_MDC_PIN     23
#define ETH_MDIO_PIN    18


И прочие настройки интерфейса:

#define ETH_POWER_PIN   -1
#define ETH_ADDR        0
#define NRST            5


Далее идёт код тестового скетча, который инициализирует Ethernet интерфейс и производит запросы к сайту в интернете.

/*
  Kincony KC868-A8
  Ethernet example
*/

#include 
#include 

#define ETH_CLK_MODE    ETH_CLOCK_GPIO17_OUT
#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 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 setup() {
  Serial.begin(115200);
  Serial.println(F("Start Kincony KC868-A8 Ethernet example..."));

  WiFi.onEvent(WiFiEvent);

  pinMode(NRST, OUTPUT);
  digitalWrite(NRST, 0); delay(200);
  digitalWrite(NRST, 1); delay(200);
  digitalWrite(NRST, 0); delay(200);
  digitalWrite(NRST, 1);

  ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLK_MODE);
}

void loop() {
  if (eth_connected) {
    testClient("baidu.com", 80);
  }
  delay(10000);
}


Здесь: инициализируется отслеживание сетевых событий.

  WiFi.onEvent(WiFiEvent);


Производится запуск чипа LAN8270A (что в нашем случае излишне, поскольку на KC868-A8 пин NRST вообще не подключён к ESP32).

  pinMode(NRST, OUTPUT);
  digitalWrite(NRST, 0); delay(200);
  digitalWrite(NRST, 1); delay(200);
  digitalWrite(NRST, 0); delay(200);
  digitalWrite(NRST, 1);


И запускается Ethernet интерфейс с соответствующими параметрами:

  ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLK_MODE);


Далее в цикле отправляются запросы к сайту в интернете:

void loop() {
  if (eth_connected) {
    testClient("baidu.com", 80);
  }
  delay(10000);
}


А саму работу по отправке запросов выполняет функция testClient ().

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 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( )


отвечает за анализ, интерпретацию и визуализацию сетевых Ethernet событий.

Результат работы тестового скетча контроллера Kincony KC868-A8 по Ethernet интерфейсу:

fgc9ea2oxjohkl7pesuxxae_qhg.png

Всё работает чётко и предсказуемо: в процессе тестирования не было замечено никаких проблем с сетевой работой связки ESP32 и LAN8270A.

▍ Заключение


В этой статье мы рассмотрели примеры программирования различных функциональных блоков контроллера Kincony KC868-A8. Следующую статью мы посвятим разбору более сложных примеров работы с KC868-A8 и подробно разберём проблематику работы по двум интерфейсам — проводному Ethernet и беспроводному Wi-Fi.

oug5kh6sjydt9llengsiebnp40w.png

© Habrahabr.ru