Skip to content
Permalink
master
Switch branches/tags
Go to file
Latest commit 5c38738 Nov 11, 2020 History
2 contributors

Users who have contributed to this file

@adri @bkonings
377 lines (322 sloc) 11.6 KB
/*
* Original Author: Klusjesman
*
* Tested with STK500 + ATMega328P
* GCC-AVR compiler
*
* Modified by supersjimmie:
* Code and libraries made compatible with Arduino and ESP8266
* Tested with Arduino IDE v1.6.5 and 1.6.9
* For ESP8266 tested with ESP8266 core for Arduino v 2.1.0 and 2.2.0 Stable
* (See https://github.com/esp8266/Arduino/ )
*/
/*
CC11xx pins ESP pins Arduino pins Description
1 - VCC VCC VCC 3v3
2 - GND GND GND Ground
3 - MOSI 13=D7 Pin 11 Data input to CC11xx
4 - SCK 14=D5 Pin 13 Clock pin
5 - MISO/GDO1 12=D6 Pin 12 Data output from CC11xx / serial clock from CC11xx
6 - GDO0 ? Pin 2? Serial data to CC11xx
7 - GDO2 ? Pin ? output as a symbol of receiving or sending data
8 - CSN 15=D8 Pin 10 Chip select / (SPI_SS)
*/
#include <SPI.h>
#include <arduino_homekit_server.h>
#include <arduino-timer.h>
#include "IthoCC1101.h"
#include "IthoPacket.h"
#include "wifi_info.h"
#define LOG_D(fmt, ...) printf_P(PSTR(fmt "\n") , ##__VA_ARGS__);
typedef enum State
{
StateIgnore = 0,
StateStandby = 1,
StateLow = 2,
StateMedium = 3,
StateHigh = 4,
StateFull = 5,
StateTimerShort = 6,
StateTimerLong = 7
};
char *state_to_string[] = {"ignore", "standby", "low", "medium", "high", "full", "short_timer", "long_timer"};
typedef enum Action
{
ActionDeactivate = 1,
ActionActivate = 2,
ActionSpeedStandby = 3,
ActionSpeedLow = 4,
ActionSpeedMedium = 5,
ActionSpeedHigh = 6,
ActionSpeedFull = 7,
ActionTimerShortOn = 8,
ActionTimerShortOff = 9,
ActionTimerLongOn = 10,
ActionTimerLongOff = 11
};
char *action_to_string[] = {"none", "deactivate", "activate", "standby", "low", "medium", "high", "full", "short_timer_on", "short_timer_off", "long_timer_on", "long_timer_off"};
IthoCC1101 rf;
IthoPacket packet;
State current_state = StateLow;
State last_speed = StateLow;
Timer<1, millis> timer_short;
Timer<1, millis> timer_long;
int timer_short_ms = 3 * 60 * 1000; // 3 min
int timer_long_ms = 20 * 60 * 1000; // 20 min
//int timer_short_ms = 3 * 1000;
//int timer_long_ms = 10 * 1000;
// access the HomeKit characteristics defined in fan_accessory.c
extern "C" homekit_server_config_t config;
extern "C" homekit_characteristic_t ch_fan_active;
extern "C" homekit_characteristic_t ch_fan_rotation_speed;
extern "C" homekit_characteristic_t ch_timer_short_on;
extern "C" homekit_characteristic_t ch_timer_long_on;
static uint32_t next_heap_millis = 0;
void setup(void) {
Serial.begin(115200);
wifi_connect();
//homekit_storage_reset(); // to remove the previous HomeKit pairing storage when you first run this new HomeKit example
homekit_setup();
delay(500);
Serial.println("setup begin");
rf.init();
Serial.println("setup done");
sendRegister();
Serial.println("join command sent");
}
void loop() {
homekit_loop();
timer_short.tick();
timer_long.tick();
delay(10);
}
// Called when the fan value is enabled/disabled by iOS Home APP
void ch_fan_active_setter(const homekit_value_t value) {
LOG_D("Homekit sends active: %d", value.bool_value);
switch (value.bool_value) {
case true: transition(ActionActivate); break;
case false: transition(ActionDeactivate); break;
}
}
void ch_timer_short_setter(const homekit_value_t value) {
LOG_D("Homekit sends short timer: %d", value.bool_value);
if (value.bool_value) {
transition(ActionTimerShortOn);
} else {
transition(ActionTimerShortOff);
}
}
void ch_timer_long_setter(const homekit_value_t value) {
LOG_D("Homekit sends long timer: %d", value.bool_value);
if (value.bool_value) {
transition(ActionTimerLongOn);
} else {
transition(ActionTimerLongOff);
}
}
// Called when the fan rotation speed is changed by iOS Home APP
void ch_fan_rotation_speed_setter(const homekit_value_t value) {
LOG_D("Homekit sends rotation speed: %.6f", value.float_value);
if (compare_float(value.float_value, 0)) {
transition(ActionSpeedStandby);
} else if (compare_float(value.float_value, 25)) {
transition(ActionSpeedLow);
} else if (compare_float(value.float_value, 50)) {
transition(ActionSpeedMedium);
} else if (compare_float(value.float_value, 75)) {
transition(ActionSpeedHigh);
} else if (compare_float(value.float_value, 100)) {
transition(ActionSpeedFull);
}
}
bool time_long_expired(void *) {
LOG_D("Long timer expired");
transition(ActionTimerLongOff);
return false; // to repeat the action - false to stop
}
bool time_short_expired(void *) {
LOG_D("Short timer expired");
transition(ActionTimerShortOff);
return false; // to repeat the action - false to stop
}
// Determine what speed to set.
// Homekit often sends 'rotation speed' and 'active' at the same time.
// The state machine avoids invalid transitions.
State get_next_state(State current_state, Action action) {
switch (current_state) {
case StateStandby:
switch (action) {
case ActionActivate: return last_speed != StateStandby ? last_speed : StateLow;
case ActionSpeedLow: return StateLow;
case ActionSpeedMedium: return StateMedium;
case ActionSpeedHigh: return StateHigh;
case ActionSpeedFull: return StateFull;
case ActionTimerShortOn: return StateTimerShort;
case ActionTimerLongOn: return StateTimerLong;
}
break;
case StateLow:
switch (action) {
case ActionDeactivate: return StateStandby;
case ActionSpeedMedium: return StateMedium;
case ActionSpeedHigh: return StateHigh;
case ActionSpeedFull: return StateFull;
case ActionTimerShortOn: return StateTimerShort;
case ActionTimerLongOn: return StateTimerLong;
}
break;
case StateMedium:
switch (action) {
case ActionDeactivate: return StateStandby;
case ActionSpeedLow: return StateLow;
case ActionSpeedHigh: return StateHigh;
case ActionSpeedFull: return StateFull;
case ActionTimerShortOn: return StateTimerShort;
case ActionTimerLongOn: return StateTimerLong;
}
break;
case StateHigh:
switch (action) {
case ActionDeactivate: return StateStandby;
case ActionSpeedLow: return StateLow;
case ActionSpeedMedium: return StateMedium;
case ActionSpeedFull: return StateFull;
case ActionTimerShortOn: return StateTimerShort;
case ActionTimerLongOn: return StateTimerLong;
}
break;
case StateFull:
switch (action) {
case ActionDeactivate: return StateStandby;
case ActionSpeedLow: return StateLow;
case ActionSpeedMedium: return StateMedium;
case ActionSpeedHigh: return StateHigh;
case ActionTimerShortOn: return StateTimerShort;
case ActionTimerLongOn: return StateTimerLong;
}
break;
case StateTimerShort:
switch (action) {
case ActionTimerShortOn: return StateTimerShort; // Restart the timer if turned on again
case ActionTimerShortOff: timer_short.cancel(); return last_speed; // When the timer ends or is disabled, go back to the previous speed
case ActionTimerLongOn: timer_short.cancel(); return StateTimerLong; // A short timer can be overwritten by a long timer
// No other action can overwrite the timer
}
break;
case StateTimerLong:
switch (action) {
case ActionTimerLongOn: return StateTimerLong; // Restart the timer if turned on again
case ActionTimerLongOff: timer_long.cancel(); return last_speed; // When the timer ends or is disabled, go back to the previous speed
// No other action can overwrite the timer
}
break;
}
return StateIgnore;
}
void transition(Action action) {
State next_state = get_next_state(current_state, action);
LOG_D("Transition. Current state: '%s', action: '%s', next state: '%s'",
state_to_string[current_state],
action_to_string[action],
state_to_string[next_state]);
if (next_state == StateIgnore || current_state == next_state) {
LOG_D("Already good. Skipping transition.");
return;
}
// Change internal state
current_state = next_state;
if (next_state >= 1 && next_state <= 5) { // only set "speed" states
last_speed = current_state;
}
// Change Homekit active state
ch_fan_active.value.bool_value = current_state != StateStandby;
homekit_characteristic_notify(&ch_fan_active, ch_fan_active.value);
// Set Homekit timer state
ch_timer_short_on.value.bool_value = current_state == StateTimerShort;
ch_timer_long_on.value.bool_value = current_state == StateTimerLong;
homekit_characteristic_notify(&ch_timer_short_on, ch_timer_short_on.value);
homekit_characteristic_notify(&ch_timer_long_on, ch_timer_long_on.value);
// Change fan speed state and Homekit rotation speed
if (current_state == StateStandby) {
sendStandbySpeed();
ch_fan_rotation_speed.value.float_value = 0.0;
} else if (current_state == StateLow) {
sendLowSpeed();
ch_fan_rotation_speed.value.float_value = 25.0;
} else if (current_state == StateMedium || current_state == StateTimerShort) {
sendMediumSpeed();
ch_fan_rotation_speed.value.float_value = 50.0;
} else if (current_state == StateHigh || current_state == StateTimerLong) {
sendHighSpeed();
ch_fan_rotation_speed.value.float_value = 75.0;
} else if (current_state == StateFull) {
sendFullSpeed();
ch_fan_rotation_speed.value.float_value = 100.0;
}
homekit_characteristic_notify(&ch_fan_rotation_speed, ch_fan_rotation_speed.value);
// Start or restart timers
if (action == ActionTimerShortOn) {
timer_short.in(timer_short_ms, time_short_expired);
} else if (action == ActionTimerLongOn) {
timer_long.in(timer_long_ms, time_long_expired);
}
}
void homekit_setup() {
ch_fan_active.setter = ch_fan_active_setter;
ch_fan_rotation_speed.setter = ch_fan_rotation_speed_setter;
ch_timer_short_on.setter = ch_timer_short_setter;
ch_timer_long_on.setter = ch_timer_long_setter;
arduino_homekit_setup(&config);
}
void homekit_loop() {
arduino_homekit_loop();
const uint32_t t = millis();
if (t > next_heap_millis) {
// show heap info every 30 seconds
next_heap_millis = t + 30 * 1000;
LOG_D("Free heap: %d, HomeKit clients: %d", ESP.getFreeHeap(), arduino_homekit_connected_clients_count());
}
}
void sendRegister() {
Serial.println("sending join...");
rf.sendCommand(IthoJoin);
Serial.println("sending join done.");
}
void sendStandbySpeed() {
Serial.println("sending standby...");
rf.sendCommand(IthoStandby);
Serial.println("sending standby done.");
}
void sendLowSpeed() {
Serial.println("sending low...");
rf.sendCommand(IthoLow);
Serial.println("sending low done.");
}
void sendMediumSpeed() {
Serial.println("sending medium...");
rf.sendCommand(IthoMedium);
Serial.println("sending medium done.");
}
void sendHighSpeed() {
Serial.println("sending high...");
rf.sendCommand(IthoHigh);
Serial.println("sending high done.");
}
void sendFullSpeed() {
Serial.println("sending FullSpeed...");
rf.sendCommand(IthoFull);
Serial.println("sending FullSpeed done.");
}
int compare_float(float f1, float f2)
{
float precision = 0.00001;
if (((f1 - precision) < f2) &&
((f1 + precision) > f2))
{
return 1;
}
else
{
return 0;
}
}