Update to new API version of ESP-KNX-IP

This commit is contained in:
Adrian Scillato 2018-04-09 22:42:51 -03:00 committed by GitHub
parent 2ce8faec62
commit 83d6049fe5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 356 additions and 185 deletions

View File

@ -41,7 +41,9 @@ void ESPKNXIP::__handle_root()
break;
case FEEDBACK_TYPE_FLOAT:
m += F("<span class='input-group-text'>");
m += feedbacks[i].options.float_options.prefix;
m += String(*(float *)feedbacks[i].data, feedbacks[i].options.float_options.precision);
m += feedbacks[i].options.float_options.suffix;
m += F("</span>");
break;
case FEEDBACK_TYPE_BOOL:
@ -52,7 +54,9 @@ void ESPKNXIP::__handle_root()
case FEEDBACK_TYPE_ACTION:
m += F("<input class='form-control' type='hidden' name='id' value='");
m += i;
m += F("' /><div class='input-group-append'><button type='submit' class='btn btn-primary'>Do this</button></div>");
m += F("' /><div class='input-group-append'><button type='submit' class='btn btn-primary'>");
m += feedbacks[i].options.action_options.btn_text;
m += F("</button></div>");
break;
}
m += F("</div></div></div>");
@ -67,6 +71,12 @@ void ESPKNXIP::__handle_root()
{
for (uint8_t i = 0; i < registered_callback_assignments; ++i)
{
// Skip empty slots
if ((callback_assignments[i].slot_flags & SLOT_FLAGS_USED) == 0)
{
continue;
}
// Skip disabled callbacks
if (callbacks[callback_assignments[i].callback_id].cond && !callbacks[callback_assignments[i].callback_id].cond())
{
continue;
@ -105,6 +115,12 @@ void ESPKNXIP::__handle_root()
m += F("<select class='form-control' name='cb'>");
for (callback_id_t i = 0; i < registered_callbacks; ++i)
{
// Skip empty slots
if ((callbacks[i].slot_flags & SLOT_FLAGS_USED) == 0)
{
continue;
}
// Skip disabled callbacks
if (callbacks[i].cond && !callbacks[i].cond())
{
continue;
@ -295,7 +311,7 @@ void ESPKNXIP::__handle_register()
goto end;
}
if (cb >= registered_callbacks)
if (!__callback_is_id_valid(cb))
{
DEBUG_PRINTLN(F("Invalid callback id"));
goto end;
@ -319,7 +335,7 @@ void ESPKNXIP::__handle_delete()
DEBUG_PRINT(id);
DEBUG_PRINTLN(F(""));
if (id >= registered_callback_assignments)
if (id >= registered_callback_assignments || (callback_assignments[id].slot_flags & SLOT_FLAGS_USED) == 0)
{
DEBUG_PRINTLN(F("ID wrong"));
goto end;

View File

@ -6,7 +6,21 @@
#include "esp-knx-ip.h"
ESPKNXIP::ESPKNXIP() : server(nullptr), registered_callback_assignments(0), registered_callbacks(0), registered_configs(0), registered_feedbacks(0)
char const *string_defaults[] =
{
"Do this",
"True",
"False",
""
};
ESPKNXIP::ESPKNXIP() : server(nullptr),
registered_callback_assignments(0),
free_callback_assignment_slots(0),
registered_callbacks(0),
free_callback_slots(0),
registered_configs(0),
registered_feedbacks(0)
{
DEBUG_PRINTLN();
DEBUG_PRINTLN("ESPKNXIP starting up");
@ -27,21 +41,21 @@ void ESPKNXIP::load()
restore_from_eeprom();
}
void ESPKNXIP::start(ESP8266WebServer *srv, bool espknxip_webpage)
void ESPKNXIP::start(ESP8266WebServer *srv)
{
server = srv;
if (espknxip_webpage) { __start_espknxip_webpage(); }
__start();
}
void ESPKNXIP::start()
{
server = new ESP8266WebServer(80);
__start_espknxip_webpage();
__start();
}
void ESPKNXIP::__start_espknxip_webpage()
void ESPKNXIP::__start()
{
if (server != nullptr)
{
server->on(ROOT_PREFIX, [this](){
__handle_root();
@ -82,10 +96,10 @@ void ESPKNXIP::__start_espknxip_webpage()
server->begin();
}
void ESPKNXIP::__start()
{
udp.listenMulticast(MULTICAST_IP, MULTICAST_PORT);
udp.onPacket([this](AsyncUDPPacket &packet) { __loop_knx(packet); });
udp.onPacket([this](AsyncUDPPacket &packet) {
DEBUG_PRINTLN("got packet");
__loop_knx(packet); });
}
void ESPKNXIP::save_to_eeprom()
@ -139,6 +153,26 @@ void ESPKNXIP::restore_from_eeprom()
for (uint8_t i = 0; i < MAX_CALLBACK_ASSIGNMENTS; ++i)
{
EEPROM.get(address, callback_assignments[i].address);
if (callback_assignments[i].address.value != 0)
{
// if address is not 0/0/0 then mark slot as used
callback_assignments[i].slot_flags |= SLOT_FLAGS_USED;
DEBUG_PRINTLN("used slot");
}
else
{
// if address is 0/0/0, then we found a free slot, yay!
// however, only count those slots, if we have not reached registered_callback_assignments yet
if (i < registered_callback_assignments)
{
DEBUG_PRINTLN("free slot before reaching registered_callback_assignments");
free_callback_assignment_slots++;
}
else
{
DEBUG_PRINTLN("free slot");
}
}
address += sizeof(address_t);
}
for (uint8_t i = 0; i < MAX_CALLBACK_ASSIGNMENTS; ++i)
@ -188,68 +222,106 @@ callback_assignment_id_t ESPKNXIP::__callback_register_assignment(address_t addr
if (registered_callback_assignments >= MAX_CALLBACK_ASSIGNMENTS)
return -1;
if (free_callback_assignment_slots == 0)
{
callback_assignment_id_t aid = registered_callback_assignments;
callback_assignments[aid].slot_flags |= SLOT_FLAGS_USED;
callback_assignments[aid].address = address;
callback_assignments[aid].callback_id = id;
registered_callback_assignments++;
DEBUG_PRINT("Assigned callback id >");
DEBUG_PRINT(id);
DEBUG_PRINT("/ga[");
DEBUG_PRINT(address.ga.area);
DEBUG_PRINT("/");
DEBUG_PRINT(address.ga.line);
DEBUG_PRINT("/");
DEBUG_PRINT(address.ga.member);
DEBUG_PRINTLN("]");
return aid;
}
void ESPKNXIP::callback_delete_assignment(callback_assignment_id_t id)
else
{
__callback_delete_assignment(id);
// find the free slot
for (callback_assignment_id_t aid = 0; aid < registered_callback_assignments; ++aid)
{
if (callback_assignments[aid].slot_flags & SLOT_FLAGS_USED)
{
// found a used slot
continue;
}
// and now an empty one
callback_assignments[aid].slot_flags |= SLOT_FLAGS_USED;
callback_assignments[aid].address = address;
callback_assignments[aid].callback_id = id;
free_callback_assignment_slots--;
return id;
}
}
}
void ESPKNXIP::__callback_delete_assignment(callback_assignment_id_t id)
{
if (id >= registered_callback_assignments)
return;
// TODO this can be optimized if we are deleting the last element
// as then we can decrement registered_callback_assignments
uint32_t dest_offset = 0;
uint32_t src_offset = 0;
uint32_t len = 0;
// clear slot and mark it as empty
callback_assignments[id].slot_flags = SLOT_FLAGS_EMPTY;
callback_assignments[id].address.value = 0;
callback_assignments[id].callback_id = 0;
if (id == registered_callback_assignments - 1)
{
DEBUG_PRINTLN("last cba deleted");
// If this is the last callback, we can delete it by decrementing registered_callbacks.
registered_callback_assignments--;
// However, if the assignment before this slot are also empty, we can decrement even further
// First check if this was also the first element
if (id == 0)
{
// start of array, so delete first entry
src_offset = 1;
// registered_ga_callbacks will be 1 in case of only one entry
// registered_ga_callbacks will be 2 in case of two entries, etc..
// so only copy anything, if there is it at least more then one element
len = (registered_callback_assignments - 1);
DEBUG_PRINTLN("really last cba");
// If this was the last, then we are done.
return;
}
else if (id == registered_callback_assignments - 1)
id--;
while(true)
{
// last element, don't do anything, simply decrement counter
DEBUG_PRINT("checking ");
DEBUG_PRINTLN((int32_t)id);
if ((callback_assignments[id].slot_flags & SLOT_FLAGS_USED) == 0)
{
DEBUG_PRINTLN("merged free slot");
// Slot before is empty
free_callback_assignment_slots--;
registered_callback_assignments--;
}
else
{
// somewhere in the middle
// need to calc offsets
// skip all prev elements
dest_offset = id; // id is equal to how many element are in front of it
src_offset = dest_offset + 1; // start after the current element
len = (registered_callback_assignments - 1 - id);
DEBUG_PRINTLN("aborted on used slot");
// Slot is used, abort
return;
}
if (len > 0)
id--;
if (id == CALLBACK_ASSIGNMENT_ID_MAX)
{
memmove(callback_assignments + dest_offset, callback_assignments + src_offset, len * sizeof(callback_assignment_t));
DEBUG_PRINTLN("abort on wrap");
// Wrap around, abort
return;
}
}
}
else
{
DEBUG_PRINTLN("free slot created");
// there is now one more free slot
free_callback_assignment_slots++;
}
}
registered_callback_assignments--;
bool ESPKNXIP::__callback_is_id_valid(callback_id_t id)
{
if (id < registered_callbacks)
return true;
if (callbacks[id].slot_flags & SLOT_FLAGS_USED)
return true;
return false;
}
callback_id_t ESPKNXIP::callback_register(String name, callback_fptr_t cb, void *arg, enable_condition_t cond)
@ -257,68 +329,108 @@ callback_id_t ESPKNXIP::callback_register(String name, callback_fptr_t cb, void
if (registered_callbacks >= MAX_CALLBACKS)
return -1;
if (free_callback_slots == 0)
{
callback_id_t id = registered_callbacks;
callbacks[id].slot_flags |= SLOT_FLAGS_USED;
callbacks[id].name = name;
callbacks[id].fkt = cb;
callbacks[id].cond = cond;
callbacks[id].arg = arg;
registered_callbacks++;
DEBUG_PRINT("Registered callback >");
DEBUG_PRINT(name);
DEBUG_PRINT("< @ ");
DEBUG_PRINTLN(id);
return id;
}
void ESPKNXIP::callback_delete_register(callback_id_t id)
{
if (id >= registered_callbacks)
return;
uint32_t dest_offset = 0;
uint32_t src_offset = 0;
uint32_t len = 0;
if (id == 0)
{
// start of array, so delete first entry
src_offset = 1;
// registered_ga_callbacks will be 1 in case of only one entry
// registered_ga_callbacks will be 2 in case of two entries, etc..
// so only copy anything, if there is it at least more then one element
len = (registered_callbacks - 1);
}
else if (id == registered_callbacks - 1)
{
// last element, don't do anything, simply decrement counter
}
else
{
// somewhere in the middle
// need to calc offsets
// skip all prev elements
dest_offset = id; // id is equal to how many element are in front of it
src_offset = dest_offset + 1; // start after the current element
len = (registered_callbacks - 1 - id);
}
if (len > 0)
// find the free slot
for (callback_id_t id = 0; id < registered_callbacks; ++id)
{
memmove(callbacks + dest_offset, callbacks + src_offset, len * sizeof(callback_t));
}
registered_callbacks--;
}
void ESPKNXIP::callback_assign(callback_id_t id, address_t val)
if (callbacks[id].slot_flags & SLOT_FLAGS_USED)
{
if (id >= registered_callbacks)
// found a used slot
continue;
}
// and now an empty one
callbacks[id].slot_flags |= SLOT_FLAGS_USED;
callbacks[id].name = name;
callbacks[id].fkt = cb;
callbacks[id].cond = cond;
callbacks[id].arg = arg;
free_callback_slots--;
return id;
}
}
}
void ESPKNXIP::callback_deregister(callback_id_t id)
{
if (!__callback_is_id_valid(id))
return;
__callback_register_assignment(val, id);
// clear slot and mark it as empty
callbacks[id].slot_flags = SLOT_FLAGS_EMPTY;
callbacks[id].fkt = nullptr;
callbacks[id].cond = nullptr;
callbacks[id].arg = nullptr;
if (id == registered_callbacks - 1)
{
// If this is the last callback, we can delete it by decrementing registered_callbacks.
registered_callbacks--;
// However, if the callbacks before this slot are also empty, we can decrement even further
// First check if this was also the first element
if (id == 0)
{
// If this was the last, then we are done.
return;
}
id--;
while(true)
{
if ((callbacks[id].slot_flags & SLOT_FLAGS_USED) == 0)
{
// Slot is empty
free_callback_slots--;
registered_callbacks--;
}
else
{
// Slot is used, abort
return;
}
id--;
if (id == CALLBACK_ASSIGNMENT_ID_MAX)
{
// Wrap around, abort
return;
}
}
}
else
{
// there is now one more free slot
free_callback_slots++;
}
}
callback_assignment_id_t ESPKNXIP::callback_assign(callback_id_t id, address_t val)
{
if (!__callback_is_id_valid(id))
return -1;
return __callback_register_assignment(val, id);
}
void ESPKNXIP::callback_unassign(callback_assignment_id_t id)
{
if (!__callback_is_id_valid(id))
return;
__callback_delete_assignment(id);
}
/**
@ -342,7 +454,7 @@ feedback_id_t ESPKNXIP::feedback_register_int(String name, int32_t *value, enabl
return id;
}
feedback_id_t ESPKNXIP::feedback_register_float(String name, float *value, uint8_t precision, enable_condition_t cond)
feedback_id_t ESPKNXIP::feedback_register_float(String name, float *value, uint8_t precision, char const *prefix, char const *suffix, enable_condition_t cond)
{
if (registered_feedbacks >= MAX_FEEDBACKS)
return -1;
@ -354,13 +466,15 @@ feedback_id_t ESPKNXIP::feedback_register_float(String name, float *value, uint8
feedbacks[id].cond = cond;
feedbacks[id].data = (void *)value;
feedbacks[id].options.float_options.precision = precision;
feedbacks[id].options.float_options.prefix = prefix ? strdup(prefix) : STRING_DEFAULT_EMPTY;
feedbacks[id].options.float_options.suffix = suffix ? strdup(suffix) : STRING_DEFAULT_EMPTY;
registered_feedbacks++;
return id;
}
feedback_id_t ESPKNXIP::feedback_register_bool(String name, bool *value, enable_condition_t cond)
feedback_id_t ESPKNXIP::feedback_register_bool(String name, bool *value, char const *true_text, char const *false_text, enable_condition_t cond)
{
if (registered_feedbacks >= MAX_FEEDBACKS)
return -1;
@ -371,13 +485,15 @@ feedback_id_t ESPKNXIP::feedback_register_bool(String name, bool *value, enable_
feedbacks[id].name = name;
feedbacks[id].cond = cond;
feedbacks[id].data = (void *)value;
feedbacks[id].options.bool_options.true_text = true_text ? strdup(true_text) : STRING_DEFAULT_TRUE;
feedbacks[id].options.bool_options.false_text = false_text ? strdup(false_text) : STRING_DEFAULT_FALSE;
registered_feedbacks++;
return id;
}
feedback_id_t ESPKNXIP::feedback_register_action(String name, feedback_action_fptr_t value, void *arg, enable_condition_t cond)
feedback_id_t ESPKNXIP::feedback_register_action(String name, feedback_action_fptr_t value, const char *btn_text, void *arg, enable_condition_t cond)
{
if (registered_feedbacks >= MAX_FEEDBACKS)
return -1;
@ -389,6 +505,7 @@ feedback_id_t ESPKNXIP::feedback_register_action(String name, feedback_action_fp
feedbacks[id].cond = cond;
feedbacks[id].data = (void *)value;
feedbacks[id].options.action_options.arg = arg;
feedbacks[id].options.action_options.btn_text = btn_text ? strdup(btn_text) : STRING_DEFAULT_DO_THIS;
registered_feedbacks++;

View File

@ -26,7 +26,7 @@
// Webserver related
#define USE_BOOTSTRAP 1 // [Default 1] Set to 1 to enable use of bootstrap CSS for nicer webconfig. CSS is loaded from bootstrapcdn.com. Set to 0 to disable
#define ROOT_PREFIX "/knx" // [Default ""] This gets prepended to all webserver paths, default is empty string "". Set this to "/knx" if you want the config to be available on http://<ip>/knx
#define ROOT_PREFIX "" // [Default ""] This gets prepended to all webserver paths, default is empty string "". Set this to "/knx" if you want the config to be available on http://<ip>/knx
#define DISABLE_EEPROM_BUTTONS 1 // [Default 0] Set to 1 to disable the EEPROM buttons in the web ui.
#define DISABLE_REBOOT_BUTTON 1 // [Default 0] Set to 1 to disable the reboot button in the web ui.
#define DISABLE_RESTORE_BUTTON 1 // [Default 0] Set to 1 to disable the "restore defaults" button in the web ui.
@ -282,6 +282,12 @@ typedef enum __config_flags
CONFIG_FLAGS_VALUE_SET = 1,
} config_flags_t;
typedef enum __slot_flags
{
SLOT_FLAGS_EMPTY = 0, // Empty slots have no flags
SLOT_FLAGS_USED = 1,
} slot_flags_t;
typedef struct __message
{
knx_command_type_t ct;
@ -295,13 +301,15 @@ typedef void (*callback_fptr_t)(message_t const &msg, void *arg);
typedef void (*feedback_action_fptr_t)(void *arg);
typedef uint8_t callback_id_t;
#define CALLBACK_ID_MAX UINT8_MAX
typedef uint8_t callback_assignment_id_t;
#define CALLBACK_ASSIGNMENT_ID_MAX UINT8_MAX
typedef uint8_t config_id_t;
typedef uint8_t feedback_id_t;
typedef struct __option_entry
{
char *name;
char const *name;
uint8_t value;
} option_entry_t;
@ -317,14 +325,29 @@ typedef struct __config
} data;
} config_t;
extern char const *string_defaults[];
#define STRING_DEFAULT_DO_THIS (string_defaults[0])
#define STRING_DEFAULT_TRUE (string_defaults[1])
#define STRING_DEFAULT_FALSE (string_defaults[2])
#define STRING_DEFAULT_EMPTY (string_defaults[3])
typedef struct __feedback_float_options
{
uint8_t precision;
char const *prefix;
char const *suffix;
} feedback_float_options_t;
typedef struct __feedback_bool_options
{
char const *true_text;
char const *false_text;
} feedback_bool_options_t;
typedef struct __feedback_action_options
{
void *arg;
char const *btn_text;
} feedback_action_options_t;
typedef struct __feedback
@ -334,6 +357,7 @@ typedef struct __feedback
enable_condition_t cond;
void *data;
union {
feedback_bool_options_t bool_options;
feedback_float_options_t float_options;
feedback_action_options_t action_options;
} options;
@ -341,6 +365,7 @@ typedef struct __feedback
typedef struct __callback
{
uint8_t slot_flags;
callback_fptr_t fkt;
enable_condition_t cond;
void *arg;
@ -349,6 +374,7 @@ typedef struct __callback
typedef struct __callback_assignment
{
uint8_t slot_flags;
address_t address;
callback_id_t callback_id;
} callback_assignment_t;
@ -358,17 +384,16 @@ class ESPKNXIP {
ESPKNXIP();
void load();
void start();
void start(ESP8266WebServer *srv, bool espknxip_webpage = true);
void start(ESP8266WebServer *srv);
void loop();
void save_to_eeprom();
void restore_from_eeprom();
callback_id_t callback_register(String name, callback_fptr_t cb, void *arg = nullptr, enable_condition_t cond = nullptr);
void callback_assign(callback_id_t id, address_t val);
void callback_delete_register(callback_id_t id);
void callback_delete_assignment(callback_assignment_id_t id);
callback_assignment_id_t callback_assign(callback_id_t id, address_t val);
void callback_deregister(callback_id_t id);
void callback_unassign(callback_assignment_id_t id);
void physical_address_set(address_t const &addr);
address_t physical_address_get();
@ -394,9 +419,9 @@ class ESPKNXIP {
// Feedback functions
feedback_id_t feedback_register_int(String name, int32_t *value, enable_condition_t cond = nullptr);
feedback_id_t feedback_register_float(String name, float *value, uint8_t precision = 2, enable_condition_t cond = nullptr);
feedback_id_t feedback_register_bool(String name, bool *value, enable_condition_t cond = nullptr);
feedback_id_t feedback_register_action(String name, feedback_action_fptr_t value, void *arg = nullptr, enable_condition_t = nullptr);
feedback_id_t feedback_register_float(String name, float *value, uint8_t precision = 2, char const *prefix = nullptr, char const *suffix = nullptr, enable_condition_t cond = nullptr);
feedback_id_t feedback_register_bool(String name, bool *value, char const *true_text = nullptr, char const *false_text = nullptr, enable_condition_t cond = nullptr);
feedback_id_t feedback_register_action(String name, feedback_action_fptr_t value, char const *btn_text = nullptr, void *arg = nullptr, enable_condition_t = nullptr);
// Send functions
void send(address_t const &receiver, knx_command_type_t ct, uint8_t data_len, uint8_t *data);
@ -484,7 +509,6 @@ class ESPKNXIP {
private:
void __start();
void __start_espknxip_webpage();
void __loop_knx(AsyncUDPPacket &packet);
// Webserver functions
@ -513,6 +537,8 @@ class ESPKNXIP {
void __config_set_options(config_id_t id, uint8_t val);
void __config_set_ga(config_id_t id, address_t const &val);
bool __callback_is_id_valid(callback_id_t id);
callback_assignment_id_t __callback_register_assignment(address_t address, callback_id_t id);
void __callback_delete_assignment(callback_assignment_id_t id);
@ -521,9 +547,11 @@ class ESPKNXIP {
AsyncUDP udp;
callback_assignment_id_t registered_callback_assignments;
callback_assignment_id_t free_callback_assignment_slots;
callback_assignment_t callback_assignments[MAX_CALLBACK_ASSIGNMENTS];
callback_id_t registered_callbacks;
callback_id_t free_callback_slots;
callback_t callbacks[MAX_CALLBACKS];
config_id_t registered_configs;

View File

@ -1,10 +1,13 @@
# datatypes
address_t DATA_TYPE
message_t DATA_TYPE
callback_id_t DATA_TYPE
callback_assignment_id_t DATA_TYPE
option_entry_t DATA_TYPE
config_id_t DATA_TYPE
enable_condition_t DATA_TYPE
callback_fptr_t DATA_TYPE
feedback_action_fptr_t DATA_TYPE
knx_command_type_t DATA_TYPE
# methods
@ -14,18 +17,25 @@ GA_to_address KEYWORD2
PA_to_address KEYWORD2
callback_register KEYWORD2
callback_assign KEYWORD2
callback_deregister KEYWORD2
callback_unassign KEYWORD2
physical_address_set KEYWORD2
physical_address_get KEYWORD2
config_register_string KEYWORD2
config_register_int KEYWORD2
config_register_ga KEYWORD2
config_register_bool KEYWORD2
config_register_options KEYWORD2
config_register_ga KEYWORD2
config_get_string KEYWORD2
config_get_int KEYWORD2
config_get_ga KEYWORD2
config_get_bool KEYWORD2
config_get_options KEYWORD2
config_get_ga KEYWORD2
config_set_string KEYWORD2
config_set_int KEYWORD2
config_set_ga KEYWORD2
config_set_bool KEYWORD2
config_set_options KEYWORD2
config_set_ga KEYWORD2
feedback_register_int KEYWORD2
feedback_register_float KEYWORD2
feedback_register_bool KEYWORD2