Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example for irrigation system #251

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/irrigation/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build/
firmware/
22 changes: 22 additions & 0 deletions examples/irrigation/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
PROGRAM = irrigation

EXTRA_COMPONENTS = \
extras/http-parser \
extras/dhcpserver \
$(abspath ../../components/esp8266-open-rtos/wifi_config) \
$(abspath ../../components/esp8266-open-rtos/cJSON) \
$(abspath ../../components/common/wolfssl) \
$(abspath ../../components/common/homekit)


FLASH_SIZE ?= 8
FLASH_MODE ?= dout
FLASH_SPEED ?= 40
HOMEKIT_SPI_FLASH_BASE_ADDR ?= 0x7A000

EXTRA_CFLAGS += -I../.. -DHOMEKIT_SHORT_APPLE_UUIDS

include $(SDK_PATH)/common.mk

monitor:
$(FILTEROUTPUT) --port $(ESPPORT) --baud 115200 --elf $(PROGRAM_OUT)
235 changes: 235 additions & 0 deletions examples/irrigation/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#include <stdio.h>
#include <string.h>
#include <espressif/esp_wifi.h>
#include <espressif/esp_sta.h>
#include <espressif/esp_system.h>
#include <esp/uart.h>
#include <esp8266.h>
#include <etstimer.h>
#include <esplibs/libmain.h>
#include <FreeRTOS.h>
#include <task.h>

#include <homekit/homekit.h>
#include <homekit/characteristics.h>
#include "wifi.h"


#define MAX_SERVICES 10

static void wifi_init() {
struct sdk_station_config wifi_config = {
.ssid = WIFI_SSID,
.password = WIFI_PASSWORD,
};

sdk_wifi_set_opmode(STATION_MODE);
sdk_wifi_station_set_config(&wifi_config);
sdk_wifi_station_connect();
}

bool pumpEnabled = false;
const uint8_t pump_gpio = 16;
bool valveOn = false;
bool pumpOn = false;
const uint8_t valve_gpios[4] = {
14,12,13,5
};
const size_t valve_count = sizeof(valve_gpios) / sizeof(*valve_gpios);

homekit_accessory_t *accessories[2];

bool task_running;
TaskHandle_t gate_task;

typedef struct {
int number;
uint8_t gpio;
homekit_characteristic_t* active;
homekit_characteristic_t* remaining_duration;
homekit_characteristic_t* set_duration;
homekit_characteristic_t* in_use;
} valve_t;

valve_t valves[4];

void start_stop_task(bool);
void update_valve_state(bool, valve_t*);
void on_update_active(homekit_characteristic_t *ch, homekit_value_t value, void *context);

// GPIO
void start_stop_pump(bool start) {
if(!pumpEnabled) {
return;
}
if(start) {
gpio_write(pump_gpio, pumpOn);
} else {
gpio_write(pump_gpio, !pumpOn);
}
}

void init_gpio() {
if(pumpEnabled) {
gpio_enable(pump_gpio, GPIO_OUTPUT);
gpio_write(pump_gpio, 0);
}
for (int i=0; i < valve_count; i++) {
gpio_enable(valve_gpios[i], GPIO_OUTPUT);
gpio_write(valve_gpios[i], !valveOn);
}
}

void set_and_notify(homekit_characteristic_t *ch, const homekit_value_t value) {
printf("set %s from %d to %d\n", ch->description, ch->value.int_value, value.int_value);
ch->value = value;
homekit_characteristic_notify(ch, value);
}

void gate_task_fn() {
while(true) {
vTaskDelay(pdMS_TO_TICKS(1000));
for (int i=0; i < valve_count; i++) {
valve_t valve = valves[i];
if(valve.in_use->value.int_value == 1 && valve.active->value.int_value == 1) {
int new_duration = valve.remaining_duration->value.int_value - 1;
// set without notify like the hap spec wants it :)
if(new_duration>0) {
valve.remaining_duration->value = HOMEKIT_UINT32(new_duration);
}
if(new_duration==0) {
set_and_notify(valve.remaining_duration, HOMEKIT_UINT32(new_duration));
}
if(new_duration == -1) {
set_and_notify(valve.in_use, HOMEKIT_UINT8(0));
set_and_notify(valve.active, HOMEKIT_UINT8(0));
start_stop_task(false);
}
}
}
}
}

void start_stop_task(bool start) {
/* `task_running` ist just a helper because I can not access that
* information from `gate_task` with `vTaskGetInfo()`:
* `undefined reference to vTaskGetInfo` from compiler 😕
*/
if(start == true && task_running == false) {
task_running = true;
start_stop_pump(true);
vTaskResume(gate_task);
}
if(start == false) {
bool idle = true;
for (int i=0; i < valve_count; i++) {
if(valves[i].active->value.int_value == 1 || valves[i].in_use->value.int_value == 1) {
idle = false;
}
}
if(idle == true) {
task_running = false;
start_stop_pump(false);
printf("Suspend gate_task\n");
vTaskSuspend(gate_task);
}
}
}

void on_update_active(homekit_characteristic_t *ch, homekit_value_t value, void *context) {
valve_t *thisValve = (valve_t *) context;
printf("%s is now %d\n", thisValve->active->description, thisValve->active->value.int_value);
if(value.int_value == 1) {
set_and_notify(thisValve->remaining_duration, thisValve->set_duration->value);
update_valve_state(true, thisValve);
start_stop_task(true);
} else {
update_valve_state(false, thisValve);
}
}

/* relay controllers */
void update_valve_state(bool state, valve_t *thisValve) {
if(state) {
printf("--> open gate number: %d \n", thisValve->gpio);
gpio_write(thisValve->gpio, valveOn);
set_and_notify(thisValve->in_use, HOMEKIT_UINT8(1));
}
else {
printf("--> close gate number: %d \n", thisValve->gpio);
gpio_write(thisValve->gpio, !valveOn);
}
}

void valve_identify(homekit_value_t value) {
printf("Valve identify\n");
}

void init_accessory() {
homekit_service_t* services[MAX_SERVICES + 2];
homekit_service_t** s = services;

*(s++) = NEW_HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]) {
NEW_HOMEKIT_CHARACTERISTIC(NAME, "IrrigationSystem"),
NEW_HOMEKIT_CHARACTERISTIC(MANUFACTURER, "buschco"),
NEW_HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "123"),
NEW_HOMEKIT_CHARACTERISTIC(MODEL, "Valve System 1"),
NEW_HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "0.1"),
NEW_HOMEKIT_CHARACTERISTIC(IDENTIFY, valve_identify),
NULL
});

*(s++) = NEW_HOMEKIT_SERVICE(SERVICE_LABEL, .characteristics=(homekit_characteristic_t*[]) {
NEW_HOMEKIT_CHARACTERISTIC(SERVICE_LABEL_NAMESPACE, 1),
NULL
});

for (int i=0; i < valve_count; i++) {
valves[i].number = i;
valves[i].gpio = valve_gpios[i];
homekit_service_t *nextService = NEW_HOMEKIT_SERVICE(VALVE, .characteristics=(homekit_characteristic_t*[]) {
NEW_HOMEKIT_CHARACTERISTIC(SERVICE_LABEL_INDEX, i+1),
NEW_HOMEKIT_CHARACTERISTIC(VALVE_TYPE, 1),
NEW_HOMEKIT_CHARACTERISTIC(
ACTIVE,
0,
.callback=HOMEKIT_CHARACTERISTIC_CALLBACK(
on_update_active, .context=(void*)&valves[i]
),
),
NEW_HOMEKIT_CHARACTERISTIC(REMAINING_DURATION, 0),
NEW_HOMEKIT_CHARACTERISTIC(SET_DURATION, 10),
NEW_HOMEKIT_CHARACTERISTIC(IN_USE, 0),
NULL
});

valves[i].active = homekit_service_characteristic_by_type(nextService, "B0");
valves[i].in_use = homekit_service_characteristic_by_type(nextService, "D2");
valves[i].remaining_duration = homekit_service_characteristic_by_type(nextService, "D4");
valves[i].set_duration = homekit_service_characteristic_by_type(nextService, "D3");
*(s++) = nextService;
}

*(s++) = NULL;

accessories[0] = NEW_HOMEKIT_ACCESSORY(.category=homekit_accessory_category_sprinkler, .services=services);
accessories[1] = NULL;
}

homekit_server_config_t config = {
.accessories = accessories,
.password = "111-11-111",
.setupId="9CB8"
};

void user_init(void) {
uart_set_baud(0, 115200);
init_gpio();
init_accessory();
xTaskCreate(gate_task_fn, "gate_task", 256, NULL, tskIDLE_PRIORITY, &gate_task);
vTaskSuspend(gate_task);
task_running = false;
wifi_init();
homekit_server_init(&config);
printf("init complete\n");
}