绪论
有了iPhone却没有 HomeKit配件?年年看Apple发布会眼馋那些HomeKit功能?家中不少老旧的电气不想扔?或者......口袋里头没几个子儿?
没关系,简简单单20块之内基本可以让你家的一个电气实现智能化并接入HomeKit
或许你以为我要讲home assistant或者home bridge了,这两个我有所研究,但是我个人更喜欢今天要介绍的方法,原因大概有以下几点:
- 不需要一个中心服务器用来装管理软件
- 不需要花大价钱买个树莓派啥的装管理软件
- 不需要原有配件支持米家等平台,不论多老旧的设备都可以适用
- 设备小巧,便于安装与隐藏
硬件部分
一枚基于ESP8266的单片机
ESP8266作为一款十分畅销也十分合适的芯片,自带2.4G无线连接支持,性能也足够,官方也适配了Arduino,而且市面上各种产品令人眼花缭乱,耳熟能详的如nodeMCU、WeMos之类的就有不少
这里推荐使用ESP-01(S)搭配淘宝、拼多多上常见的小底座(有继电器、RGB灯带、DHT11等种类,基本一搜就能找到),不过有个限制,那就是设备最好要支持单线控制,否则接线会变得十分复杂,焊接工作啥的也会很繁琐,若是一定要用多根线控制,最好还是采用别的一些开发版
传感器/控制器
上面一节中提到的小底座其实已经包含了本节的一些,但是如若你想要控制一些别的东西或者获取一些别的信息,比如用红外控制空调,那么还要去采购红外模块(这里有个注意点,一定要买大功率的活着自己搭一个放大电路,否则直接接上红外灯珠的话功率会太小,只能超近距离控制)
其它
面包线、焊接设备与材料啥的都准备好
当然,你还要有一台iPhone(大于等于5s)或者iPad
代码部分
本篇采用的是Mixiaoxiao的Arduino-HomeKit-ESP8266仓库,安装的话可以直接在Arduino的库管理器中安装,也可以在Release里面下载,版本上略有不同,不过问题不大,本篇使用的是1.2.0版本,即库管理器中下载的版本
你要是懒得钻研例程,可以看看以下的分析,要是你愿意自己研读例程也无妨看看,以下按照我自己整理的一个开关的例程讲述
配件定义
首先,在my_accessory.c中,我们需要定义配件的各种基本信息以及控制项目或者传感器项目(统称characteristic),当你修改代码制作属于自己的配件时首先要改动的便是这里
第一部分是ACCESSORY_INFORMATION,这里面都是些类似名称、版本之类的基本信息,可以自由修改
第二部分就是配件独有的属性,若要操控别的硬件的话需要参考HAP文档,来确定具体属性及其数据类型
具体的内容请阅读以下代码段:
homekit_characteristic_t cha_on = HOMEKIT_CHARACTERISTIC_(ON, false);
homekit_characteristic_t cha_name = HOMEKIT_CHARACTERISTIC_(NAME, "JKHome Smart Socket");
homekit_accessory_t *accessories[] = {
HOMEKIT_ACCESSORY(.id = 1, .category = homekit_accessory_category_lightbulb, .services = (homekit_service_t*[]) {
HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics = (homekit_characteristic_t*[])
{
HOMEKIT_CHARACTERISTIC(NAME, "JKHome Smart Socket"),
HOMEKIT_CHARACTERISTIC(MANUFACTURER, "JKhome"),
HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "202111181033"),
HOMEKIT_CHARACTERISTIC(MODEL, "FEIJI"),
HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "1.0"),
HOMEKIT_CHARACTERISTIC(IDENTIFY, my_accessory_identify),
NULL
}),
HOMEKIT_SERVICE(LIGHTBULB, .primary = true, .characteristics = (homekit_characteristic_t*[])
{
&cha_on,
&cha_name,
NULL
}),
NULL
}),
NULL
};
第三部分可以设置配对时的密码
homekit_server_config_t accessory_config = {
.accessories = accessories,
.password = "200-00-907"
};
主程序
setup部分
这一部分分为对于配件的初始化以及对于HomeKit的初始化
对于配件的初始化包括一些类似I2C连接之类的操作,和别的项目中使用的一样
对于HomeKit的初始化基本就是设置配件属性控制函数,比如给“onoff”属性指定一个函数去控制
此外,这部分中还要设置好Wi-Fi的SSID与密码
具体的内容请阅读以下代码段:
extern "C" homekit_server_config_t accessory_config;
extern "C" homekit_characteristic_t cha_on;
const int switchPin = 0;
const char *ssid = "ssid";
const char *password = "password";
void setOnOff(const homekit_value_t v)
{
cha_on.value.bool_value = v.bool_value;
digitalWrite(switchPin, cha_on.value.bool_value ? LOW : HIGH);
}
void homekitSetup()
{
cha_on.setter = setOnOff;
arduino_homekit_setup(&accessory_config);
}
void setup()
{
pinMode(switchPin, OUTPUT);
digitalWrite(switchPin, LOW);
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.persistent(false);
WiFi.disconnect(false);
WiFi.setAutoReconnect(true);
WiFi.begin(ssid, password);
WiFi.setAutoConnect(true);
delay(1000);
homekitSetup();
}
loop部分
这部分主要负责传感器数据回传(这里请参见下文中的空调例程,此处没有)以及获取并执行指令(实质上是每次获取一下家庭应用中的设置并运行setter)
此外还负责输出监控信息等
具体的内容请阅读以下代码段:
static long long next_heap_millis = 0;
#define LOG_D(fmt, ...) printf_P(PSTR(fmt "\n") , ##__VA_ARGS__);
void homekitLoop()
{
arduino_homekit_loop();//运行一遍setter以及一些别的东西
const uint32_t t = millis();
if (t > next_heap_millis)
{
next_heap_millis = t + 5 * 1000;
LOG_D("Free heap: %d, HomeKit clients: %d", ESP.getFreeHeap(), arduino_homekit_connected_clients_count());
}
}
void loop()
{
homekitLoop();
delay(10);
}
其它例程
此处给出一个控制空调的例程以供参考
my_accessory.c:
#include <homekit/homekit.h>
#include <homekit/characteristics.h>
void my_accessory_identify(homekit_value_t _value)
{
printf("accessory identify\n");
}
homekit_characteristic_t ACName = HOMEKIT_CHARACTERISTIC_(NAME, "JKHome Air Conditioner - Kelon");
homekit_characteristic_t currentHeatingCoolingState = HOMEKIT_CHARACTERISTIC_(CURRENT_HEATING_COOLING_STATE, 0);
homekit_characteristic_t targetHeatingCoolingState = HOMEKIT_CHARACTERISTIC_(TARGET_HEATING_COOLING_STATE, 0);
homekit_characteristic_t currentTemperature = HOMEKIT_CHARACTERISTIC_(CURRENT_TEMPERATURE, 26.0);
homekit_characteristic_t targetTemperature = HOMEKIT_CHARACTERISTIC_(TARGET_TEMPERATURE, 26.0);
homekit_characteristic_t temperatureDisplayUnit = HOMEKIT_CHARACTERISTIC_(TEMPERATURE_DISPLAY_UNITS, 0);
homekit_characteristic_t currentRelativeHumidity = HOMEKIT_CHARACTERISTIC_(CURRENT_RELATIVE_HUMIDITY, 50.0);
homekit_accessory_t *accessories[] = {
HOMEKIT_ACCESSORY(.id = 1, .category = homekit_accessory_category_air_conditioner, .services = (homekit_service_t*[]) {
HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics = (homekit_characteristic_t*[]) {
HOMEKIT_CHARACTERISTIC(NAME, "JKHome Air Conditioner - Kelon"),
HOMEKIT_CHARACTERISTIC(MANUFACTURER, "JKhome"),
HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "202108270124"),
HOMEKIT_CHARACTERISTIC(MODEL, "Kelon"),
HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "1.1"),
HOMEKIT_CHARACTERISTIC(IDENTIFY, my_accessory_identify),
NULL
}),//Define the accessory.
HOMEKIT_SERVICE(THERMOSTAT, .primary = true, .characteristics = (homekit_characteristic_t*[]) {
&ACName,
¤tHeatingCoolingState,
&targetHeatingCoolingState,
¤tTemperature,
&targetTemperature,
&temperatureDisplayUnit,
¤tRelativeHumidity,
NULL
}),//Add a thermostat to this accessory.
NULL
}),
NULL
};
homekit_server_config_t accessory_config = {
.accessories = accessories,
.password = "200-00-907"
};
KelonAirConditioner.ino:
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <arduino_homekit_server.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Kelon.h>
#include <DHT.h>
extern "C" homekit_server_config_t accessory_config;
extern "C" homekit_characteristic_t currentHeatingCoolingState;
extern "C" homekit_characteristic_t targetHeatingCoolingState;
extern "C" homekit_characteristic_t currentTemperature;
extern "C" homekit_characteristic_t targetTemperature;
extern "C" homekit_characteristic_t temperatureDisplayUnit;
extern "C" homekit_characteristic_t currentRelativeHumidity;
static long long nextNotifyTime = 0;
const int IRPin = 3;//RX on ESP-01.
const int DHTPin = 2;//IO2 on ESP-01.
const char *ssid = "ssid";//Your WiFi SSID.
const char *password = "password";//Your WiFi Password, just ignore the encrypt method.
IRKelonAc AC(IRPin);
DHT DHTSensor(DHTPin, DHT11);//You can replace "DHT11" with "DHT21" and "DHT22", depend on your sensor.
void ACModeSetter(const homekit_value_t value)
{
currentHeatingCoolingState.value.uint8_value = value.uint8_value;
targetHeatingCoolingState.value.uint8_value = value.uint8_value;
updateac();
}
void ACTemperatureSetter(const homekit_value_t value)
{
targetTemperature.value.float_value = value.float_value;
updateac();
}
void temperatureDisplayUnitSetter(const homekit_value_t value)
{
temperatureDisplayUnit.value.uint8_value = value.uint8_value;
homekitNotify();
}
void updateac()
{
switch (targetHeatingCoolingState.value.uint8_value)
{
case 0:
AC.ensurePower(false);
break;
case 1:
AC.ensurePower(true);
AC.setMode(kKelonModeHeat);
break;
case 2:
AC.ensurePower(true);
AC.setMode(kKelonModeCool);
break;
case 3:
if (currentTemperature.value.float_value > targetTemperature.value.float_value + 1.0) AC.ensurePower(true), AC.setMode(kKelonModeCool);
else if (currentTemperature.value.float_value < targetTemperature.value.float_value - 1.0) AC.ensurePower(true), AC.setMode(kKelonModeHeat);
else AC.ensurePower(false);
break;
}
AC.setTemp((int)round(targetTemperature.value.float_value));
AC.setFan(kKelonFanMax);
AC.send();
homekitNotify();
}
void homekitNotify()
{
currentTemperature.value.float_value = round(DHTSensor.readTemperature() * 10.0) / 10.0;
currentRelativeHumidity.value.float_value = round(DHTSensor.readHumidity()) * 1.0;
if (!(currentTemperature.value.float_value > 0 && currentTemperature.value.float_value < 100)) currentTemperature.value.float_value = 26.0;//如若不能给出正常的传感器数值,则会连接失败,下同
if (!(currentRelativeHumidity.value.float_value > 0 || currentRelativeHumidity.value.float_value < 100)) currentRelativeHumidity.value.float_value = 50.0;
homekit_characteristic_notify(¤tHeatingCoolingState, currentHeatingCoolingState.value);
homekit_characteristic_notify(&targetHeatingCoolingState, targetHeatingCoolingState.value);
homekit_characteristic_notify(¤tTemperature, currentTemperature.value);
homekit_characteristic_notify(&targetTemperature, targetTemperature.value);
homekit_characteristic_notify(&temperatureDisplayUnit, temperatureDisplayUnit.value);
homekit_characteristic_notify(¤tRelativeHumidity, currentRelativeHumidity.value);
}
void homekitSetup()
{
targetHeatingCoolingState.setter = ACModeSetter;
targetTemperature.setter = ACTemperatureSetter;
temperatureDisplayUnit.setter = temperatureDisplayUnitSetter;
arduino_homekit_setup(&accessory_config);
}
void homekitLoop()
{
arduino_homekit_loop();
const uint32_t timer = millis();
if (timer > nextNotifyTime)
{
nextNotifyTime = timer + 2 * 1000;
homekitNotify();
}
}
void setup()
{
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.persistent(false);
WiFi.disconnect(false);
WiFi.setAutoReconnect(true);
WiFi.begin(ssid, password);
WiFi.setAutoConnect(true);
AC.begin();
DHTSensor.begin();
delay(1000);
homekitSetup();
}
void loop()
{
homekitLoop();
delay(10);
}
Comments NOTHING