Skip to content

Commit 0e6a191

Browse files
committed
Example03 and Example04
1 parent 97606ef commit 0e6a191

File tree

8 files changed

+781
-7
lines changed

8 files changed

+781
-7
lines changed

examples/Example02_Switch/switch.ino

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
/*
22
* switch.ino
33
*
4+
* Created on: 2020-05-15
5+
* Author: Mixiaoxiao (Wang Bin)
6+
*
7+
* HAP section 8.38 Switch
8+
* An accessory contains a switch.
9+
*
410
* This example shows how to:
511
* 1. define a switch accessory and its characteristics (in my_accessory.c).
612
* 2. get the switch-event sent from iOS Home APP.
7-
* 3. report the switch value to HomeKit
8-
*
9-
* Created on: 2020-05-15
10-
* Author: Mixiaoxiao (Wang Bin)
13+
* 3. report the switch value to HomeKit.
1114
*
15+
* You should:
16+
* 1. read and use the Example01_TemperatureSensor with detailed comments
17+
* to know the basic concept and usage of this library before other examples。
18+
* 2. erase the full flash or call homekit_storage_reset() in setup()
19+
* to remove the previous HomeKit pairing storage and
20+
* enable the pairing with the new accessory of this new HomeKit example.
1221
*/
1322

1423
#include <Arduino.h>
@@ -20,6 +29,7 @@
2029
void setup() {
2130
Serial.begin(115200);
2231
wifi_connect(); // in wifi_info.h
32+
//homekit_storage_reset(); // to remove the previous HomeKit pairing storage when you first run this new HomeKit example
2333
my_homekit_setup();
2434
}
2535

@@ -29,7 +39,7 @@ void loop() {
2939
}
3040

3141
//==============================
32-
// Homekit setup and loop
42+
// HomeKit setup and loop
3343
//==============================
3444

3545
// access your HomeKit characteristics defined in my_accessory.c
@@ -41,9 +51,9 @@ static uint32_t next_heap_millis = 0;
4151
#define PIN_SWITCH 2
4252

4353
//Called when the switch value is changed by iOS Home APP
44-
void cha_switch_on_setter(const homekit_value_t value){
54+
void cha_switch_on_setter(const homekit_value_t value) {
4555
bool on = value.bool_value;
46-
cha_switch_on.value.bool_value = on;//sync the value
56+
cha_switch_on.value.bool_value = on; //sync the value
4757
LOG_D("Switch: %s", on ? "ON" : "OFF");
4858
digitalWrite(PIN_SWITCH, on ? LOW : HIGH);
4959
}
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
/*
2+
* ESPButton.h
3+
* Ticker-scan based Button Handler with debounce.
4+
* Default scan interval is 16ms (60FPS).
5+
* All added Buttons are scanned by a global Ticker (by os timer).
6+
*
7+
* Usage:
8+
* in setup():
9+
* pinMode(pin, INPUT_PULLUP / INPUT /... );
10+
* ESPButton.add(id, pin, pin_down_digital);
11+
* ESPButton.setCallback(...); // handle your ButtonEvent by id
12+
* ESPButton.begin(); // call begin to start scan.
13+
* in loop():
14+
* ESPButton.loop(); // will notify the event in loop (not in interrupt timer)
15+
*
16+
* Created on: 2020-03-08
17+
* Last update: 2020-04-14
18+
* Author: Wang Bin
19+
*/
20+
21+
#ifndef ESPBUTTON_H_
22+
#define ESPBUTTON_H_
23+
24+
#include <Arduino.h>
25+
#include <functional>
26+
#include <Ticker.h>
27+
28+
#define ESPBUTTON_DEBUG(message, ...) //printf_P(PSTR("[%7d] ESPButton: " message "\n"), millis(), ##__VA_ARGS__)
29+
30+
typedef struct _ESPButtonEntry {
31+
uint8_t id = -1;
32+
uint8_t pin = -1;
33+
uint8_t pin_down_digital = LOW; //按下状态时的digital值
34+
35+
bool stable_down = false; //稳定状态的下的按键状态 (true: down, false: up)
36+
uint32_t stable_threshold = 40; //如果按键状态维持一段时间未变化就认为是stable
37+
bool is_stable = false; //当前是否是稳定状态,只有稳定状态下才会进一步处理
38+
bool raw_down = false; //未消抖的原始按键状态
39+
uint32_t raw_changed_time = 0; //未消抖的原始按键状态的改变的时间
40+
41+
//void (*ext_digitalRead)(uint8_t pin);
42+
//如果该pin不是ESP芯片的引脚,而是从其他的扩展芯片读取的,就需要传入ext_digitalRead
43+
std::function<uint8_t(uint8_t pin)> ext_digitalRead = nullptr;
44+
//======
45+
bool longclicked = false; //用来保证一次按下只能触发一次长按
46+
bool down_handled = false; //标志着是否已经处理了该次down按下事件(比如已经触发了长按)
47+
//down->up,在规定时间内再次down就是双击,否则超时就是单击
48+
bool wait_doubleclick = false; //标志着是否等待着双击事件
49+
uint32_t down_time = 0; //ms
50+
uint32_t up_time = 0;
51+
52+
uint32_t longclick_threshold = 5000;
53+
uint32_t doubleclick_threshold = 150; //按下释放后在此时间间隔内又按下认为是双击
54+
55+
bool longclick_enable = true;
56+
bool doubleclick_enable = true;
57+
//======
58+
struct _ESPButtonEntry *next;
59+
} ESPButtonEntry;
60+
61+
enum ESPButtonEvent {
62+
ESPBUTTONEVENT_NONE = 0,
63+
ESPBUTTONEVENT_SINGLECLICK,
64+
ESPBUTTONEVENT_DOUBLECLICK,
65+
ESPBUTTONEVENT_LONGCLICK
66+
};
67+
class ESPButtonClass;
68+
69+
static void _esp32_ticker_cb(ESPButtonClass *esp_button);
70+
71+
class ESPButtonClass {
72+
73+
public:
74+
75+
typedef std::function<void(uint8_t id, ESPButtonEvent event)> espbutton_callback;
76+
77+
Ticker ticker;
78+
ESPButtonEntry *entries = nullptr;
79+
espbutton_callback callback;
80+
ESPButtonEvent notify_event = ESPBUTTONEVENT_NONE;
81+
uint8_t notify_id = 0;
82+
83+
ESPButtonClass() {
84+
}
85+
~ESPButtonClass() {
86+
}
87+
88+
void begin() {
89+
ticker.detach();
90+
#if defined(ESP8266)
91+
ticker.attach_ms(16, std::bind(&ESPButtonClass::tick, this));
92+
#elif defined(ESP32)
93+
ticker.attach_ms(16, _esp32_ticker_cb, this);
94+
#endif
95+
}
96+
97+
ESPButtonEntry* add(uint8_t _id, uint8_t _pin, uint8_t _pin_down_digital,
98+
bool _doubleclick_enable = false, bool _longclick_enable = true) {
99+
ESPButtonEntry *entry = new ESPButtonEntry();
100+
entry->id = _id;
101+
entry->pin = _pin;
102+
entry->pin_down_digital = _pin_down_digital;
103+
entry->doubleclick_enable = _doubleclick_enable;
104+
entry->longclick_enable = _longclick_enable;
105+
106+
//初始化entry的状态??暂时不需要,我们就是认为按键默认就是未按下的
107+
//entry->laststate_is_down = digitalReadEntryIsDown(entry);
108+
//加入链表
109+
entry->next = entries;
110+
entries = entry;
111+
return entry;
112+
}
113+
114+
void setCallback(espbutton_callback _callback) {
115+
callback = _callback;
116+
}
117+
118+
PGM_P getButtonEventDescription(ESPButtonEvent e) {
119+
switch (e) {
120+
case ESPBUTTONEVENT_SINGLECLICK:
121+
return PSTR("SingleClick");
122+
case ESPBUTTONEVENT_DOUBLECLICK:
123+
return PSTR("DoubleClick");
124+
case ESPBUTTONEVENT_LONGCLICK:
125+
return PSTR("LongClick");
126+
default:
127+
return PSTR("<unknown event>");
128+
}
129+
}
130+
131+
void tick() {
132+
ESPButtonEntry *entry = entries;
133+
while (entry) {
134+
tickEntry(entry);
135+
entry = entry->next;
136+
}
137+
}
138+
139+
void loop() {
140+
if (callback && (notify_event != ESPBUTTONEVENT_NONE)) {
141+
callback(notify_id, notify_event);
142+
notify_id = 0;
143+
notify_event = ESPBUTTONEVENT_NONE;
144+
}
145+
}
146+
147+
private:
148+
149+
bool digitalReadEntryIsDown(ESPButtonEntry *entry) {
150+
if (entry->ext_digitalRead) {
151+
return entry->ext_digitalRead(entry->pin) == entry->pin_down_digital;
152+
}
153+
return digitalRead(entry->pin) == entry->pin_down_digital;
154+
}
155+
156+
void tickEntry(ESPButtonEntry *entry) {
157+
const uint32_t t = millis();
158+
const bool down = digitalReadEntryIsDown(entry);
159+
if (down != entry->raw_down) {
160+
entry->raw_down = down;
161+
entry->is_stable = false;
162+
entry->raw_changed_time = t;
163+
ESPBUTTON_DEBUG("change (%s)", down ? PSTR("down") : PSTR("up"));
164+
} else { // down == raw_down
165+
// 在stable_threshold时间内一直没有变化,认为是stable
166+
if (!entry->is_stable) {
167+
if (t - entry->raw_changed_time > entry->stable_threshold) {
168+
ESPBUTTON_DEBUG("t: %d, raw: %d", t, entry->raw_changed_time);ESPBUTTON_DEBUG("stable (%s)", down ? PSTR("down") : PSTR("up"));
169+
entry->is_stable = true;
170+
}
171+
}
172+
}
173+
if (!entry->is_stable) {
174+
//ESPBUTTON_DEBUG("not stable");
175+
return;
176+
}
177+
//以上代码能检测出超过一定时间的稳定了的状态,等稳定了之后再做处理
178+
179+
if (entry->stable_down == down) {
180+
handleEntryUnchanged(entry);
181+
return;
182+
} else {
183+
entry->stable_down = down;
184+
handleEntryChanged(entry);
185+
}
186+
187+
}
188+
189+
void handleEntryChanged(ESPButtonEntry *entry) {
190+
const bool down = entry->stable_down;
191+
//仅有单击事件就在down的时候直接回调? 暂时不这么做,类比实体开关,按下不松手的时候,就是一直开着的状态
192+
//逻辑如下:
193+
//单击:按下->释放->且释放一段时间内没有第二次按下
194+
//双击:按下->释放->且释放一段时间内执行第二次按下时触发
195+
//长按:按下一段时间内未释放
196+
if (down) { //down
197+
if (entry->wait_doubleclick && entry->doubleclick_enable) {
198+
//规定时间内第二次down了,认为是双击
199+
//亲测,一般情况下我的双击up->第二次down的间隔是80~100左右
200+
ESPBUTTON_DEBUG("doubleclick, wait %d", (millis() - entry->up_time));
201+
entry->down_handled = true;
202+
notifyEvent(entry, ESPBUTTONEVENT_DOUBLECLICK);
203+
} else {
204+
//第一次按下
205+
entry->down_handled = false;
206+
}
207+
entry->down_time = millis();
208+
entry->longclicked = false;
209+
entry->wait_doubleclick = false;
210+
} else { //up
211+
if (!entry->down_handled) {
212+
if (entry->doubleclick_enable) {
213+
//在loop中延时等待第二次按下
214+
entry->up_time = millis();
215+
entry->wait_doubleclick = true;
216+
} else {
217+
entry->down_handled = true;
218+
notifyEvent(entry, ESPBUTTONEVENT_SINGLECLICK);
219+
}
220+
}
221+
}
222+
223+
}
224+
225+
void handleEntryUnchanged(ESPButtonEntry *entry) {
226+
bool down = entry->stable_down;
227+
if (down) { //down
228+
if (entry->longclick_enable) {
229+
if (!entry->longclicked && !entry->down_handled) {
230+
if (millis() - entry->down_time > entry->longclick_threshold) {
231+
entry->longclicked = true;
232+
entry->down_handled = true;
233+
notifyEvent(entry, ESPBUTTONEVENT_LONGCLICK);
234+
}
235+
}
236+
}
237+
} else { //up
238+
entry->longclicked = false;
239+
if (entry->wait_doubleclick && entry->doubleclick_enable) {
240+
if (millis() - entry->up_time > entry->doubleclick_threshold) {
241+
entry->wait_doubleclick = false;
242+
entry->down_handled = true;
243+
//key2DoClick();
244+
notifyEvent(entry, ESPBUTTONEVENT_SINGLECLICK);
245+
}
246+
}
247+
248+
}
249+
}
250+
251+
void notifyEvent(ESPButtonEntry *entry, ESPButtonEvent event) {
252+
ESPBUTTON_DEBUG("Button(%d): %s", entry->id, getButtonEventDescription(event));
253+
// Save the Event and notify it in loop
254+
if (notify_event != ESPBUTTONEVENT_NONE) {
255+
ESPBUTTON_DEBUG("Warnning! Previous Button Event is not handled in loop!");
256+
}
257+
notify_event = event;
258+
notify_id = entry->id;
259+
// if (callback) {
260+
// callback(entry->id, event);
261+
// }
262+
}
263+
264+
};
265+
266+
ESPButtonClass ESPButton;
267+
268+
static void _esp32_ticker_cb(ESPButtonClass *esp_button) {
269+
esp_button->tick();
270+
}
271+
272+
#endif /* ESPBUTTON_H_ */
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* my_accessory.c
3+
* Define the accessory in C language using the Macro in characteristics.h
4+
*
5+
* Created on: 2020-05-16
6+
* Author: Mixiaoxiao (Wang Bin)
7+
*/
8+
9+
#include <homekit/homekit.h>
10+
#include <homekit/characteristics.h>
11+
12+
void my_accessory_identify(homekit_value_t _value) {
13+
printf("accessory identify\n");
14+
}
15+
16+
// Stateless Programmable Switch (HAP section 8.37)
17+
// required: PROGRAMMABLE_SWITCH_EVENT
18+
// optional: NAME, SERVICE_LABEL_INDEX
19+
20+
// format: uint8; HAP section 9.75; write the .getter function and always return "null" for reading
21+
homekit_characteristic_t cha_programmable_switch_event = HOMEKIT_CHARACTERISTIC_(PROGRAMMABLE_SWITCH_EVENT, 0);
22+
23+
homekit_accessory_t *accessories[] = {
24+
HOMEKIT_ACCESSORY(.id=1, .category=homekit_accessory_category_switch, .services=(homekit_service_t*[]) {
25+
HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]) {
26+
HOMEKIT_CHARACTERISTIC(NAME, "Stateless Programmable Switch"),
27+
HOMEKIT_CHARACTERISTIC(MANUFACTURER, "Arduino HomeKit"),
28+
HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "0123456"),
29+
HOMEKIT_CHARACTERISTIC(MODEL, "ESP8266/ESP32"),
30+
HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "1.0"),
31+
HOMEKIT_CHARACTERISTIC(IDENTIFY, my_accessory_identify),
32+
NULL
33+
}),
34+
HOMEKIT_SERVICE(STATELESS_PROGRAMMABLE_SWITCH, .primary=true, .characteristics=(homekit_characteristic_t*[]){
35+
HOMEKIT_CHARACTERISTIC(NAME, "Stateless Programmable Switch"),
36+
&cha_programmable_switch_event,
37+
NULL
38+
}),
39+
NULL
40+
}),
41+
NULL
42+
};
43+
44+
homekit_server_config_t config = {
45+
.accessories = accessories,
46+
.password = "111-11-111"
47+
};
48+
49+

0 commit comments

Comments
 (0)