From c860e0068ba9c6dbb58cab0b1b6d5655689e2192 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 26 Oct 2020 11:29:12 +0100 Subject: [PATCH 1/7] Create readme.txt --- lib/headers/readme.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 lib/headers/readme.txt diff --git a/lib/headers/readme.txt b/lib/headers/readme.txt new file mode 100644 index 000000000..695df2dcb --- /dev/null +++ b/lib/headers/readme.txt @@ -0,0 +1 @@ +KNX header files. Workaround to exclude KNX library when not needed From d0c4bf59119f553afc2976bb0fd48d650ef50ab3 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 26 Oct 2020 11:31:24 +0100 Subject: [PATCH 2/7] Move KNX library to lib_div --- lib_div/esp-knx-ip-0.5.2/LICENSE | 21 + lib_div/esp-knx-ip-0.5.2/README.md | 94 +++ .../environment-sensor/environment-sensor.ino | 159 +++++ .../examples/sonoff/sonoff.ino | 183 +++++ .../examples/static-config/static-config.ino | 142 ++++ lib_div/esp-knx-ip-0.5.2/keywords.txt | 107 +++ lib_div/esp-knx-ip-0.5.2/library.json | 18 + .../esp-knx-ip-0.5.2/library.properties.off | 10 + lib_div/esp-knx-ip-0.5.2/src/DPT.h | 73 ++ .../src/esp-knx-ip-config.cpp | 358 ++++++++++ .../src/esp-knx-ip-conversion.cpp | 87 +++ .../esp-knx-ip-0.5.2/src/esp-knx-ip-send.cpp | 201 ++++++ .../src/esp-knx-ip-webserver.cpp | 540 ++++++++++++++ lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp | 664 ++++++++++++++++++ lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.h | 600 ++++++++++++++++ 15 files changed, 3257 insertions(+) create mode 100644 lib_div/esp-knx-ip-0.5.2/LICENSE create mode 100644 lib_div/esp-knx-ip-0.5.2/README.md create mode 100644 lib_div/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino create mode 100644 lib_div/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino create mode 100644 lib_div/esp-knx-ip-0.5.2/examples/static-config/static-config.ino create mode 100644 lib_div/esp-knx-ip-0.5.2/keywords.txt create mode 100644 lib_div/esp-knx-ip-0.5.2/library.json create mode 100644 lib_div/esp-knx-ip-0.5.2/library.properties.off create mode 100644 lib_div/esp-knx-ip-0.5.2/src/DPT.h create mode 100644 lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-config.cpp create mode 100644 lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-conversion.cpp create mode 100644 lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-send.cpp create mode 100644 lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-webserver.cpp create mode 100644 lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp create mode 100644 lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.h diff --git a/lib_div/esp-knx-ip-0.5.2/LICENSE b/lib_div/esp-knx-ip-0.5.2/LICENSE new file mode 100644 index 000000000..80f432a97 --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Nico Weichbrodt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/lib_div/esp-knx-ip-0.5.2/README.md b/lib_div/esp-knx-ip-0.5.2/README.md new file mode 100644 index 000000000..a93c09192 --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/README.md @@ -0,0 +1,94 @@ +# ESP-KNX-IP # + +This is a library for the ESP8266 to enable KNXnet/IP communication. It uses UDP multicast on 224.0.23.12:3671. +It is intended to be used with the Arduino platform for the ESP8266. + +## How to use ## + +The library is under development. API may change multiple times in the future. + +API documentation is available [here](https://github.com/envy/esp-knx-ip/wiki/API) + +A simple example: + +```c++ +#include + +const char* ssid = "my-ssid"; // your network SSID (name) +const char* pass = "my-pw"; // your network password + +config_id_t my_GA; +config_id_t param_id; + +int8_t some_var = 0; + +void setup() +{ + // Register a callback that is called when a configurable group address is receiving a telegram + knx.register_callback("Set/Get callback", my_callback); + knx.register_callback("Write callback", my_other_callback); + + int default_val = 21; + param_id = knx.config_register_int("My Parameter", default_val); + + // Register a configurable group address for sending out answers + my_GA = knx.config_register_ga("Answer GA"); + + knx.load(); // Try to load a config from EEPROM + + WiFi.begin(ssid, pass); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + } + + knx.start(); // Start everything. Must be called after WiFi connection has been established +} + +void loop() +{ + knx.loop(); +} + + +void my_callback(message_t const &msg, void *arg) +{ + switch (msg.ct) + { + case KNX_CT_WRITE: + // Save received data + some_var = knx.data_to_1byte_int(msg.data); + break; + case KNX_CT_READ: + // Answer with saved data + knx.answer1ByteInt(msg.received_on, some_var); + break; + } +} + +void my_other_callback(message_t const &msg, void *arg) +{ + switch (msg.ct) + { + case KNX_CT_WRITE: + // Write an answer somewhere else + int value = knx.config_get_int(param_id); + address_t ga = knx.config_get_ga(my_GA); + knx.answer1ByteInt(ga, (int8_t)value); + break; + } +} + +``` + +## How to configure (buildtime) ## + +Open the `esp-knx-ip.h` and take a look at the config options at the top inside the block marked `CONFIG` + +## How to configure (runtime) ## + +Simply visit the IP of your ESP with a webbrowser. You can configure the following: +* KNX physical address +* Which group address should trigger which callback +* Which group address are to be used by the program (e.g. for status replies) + +The configuration is dynamically generated from the code. diff --git a/lib_div/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino b/lib_div/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino new file mode 100644 index 000000000..02621bbc6 --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino @@ -0,0 +1,159 @@ +/* + * This is an example showing a simple environment sensor based on a BME280 attached via I2C. + * This sketch was tested on a WeMos D1 mini + */ + +#include +#include + +// WiFi config here +const char* ssid = "myssid"; +const char* pass = "mypassword"; + +#define LED_PIN D4 +#define UPDATE_INTERVAL 10000 + +unsigned long next_change = 0; + +float last_temp = 0.0; +float last_hum = 0.0; +float last_pres = 0.0; + +config_id_t temp_ga, hum_ga, pres_ga; +config_id_t hostname_id; +config_id_t update_rate_id, send_rate_id; +config_id_t enable_sending_id; +config_id_t enable_reading_id; + +Adafruit_BME280 bme; + +void setup() { + pinMode(LED_PIN, OUTPUT); + Serial.begin(115200); + + hostname_id = knx.config_register_string("Hostname", 20, String("env")); + enable_sending_id = knx.config_register_bool("Send on update", true); + update_rate_id = knx.config_register_int("Update rate (ms)", UPDATE_INTERVAL); + temp_ga = knx.config_register_ga("Temperature", show_periodic_options); + hum_ga = knx.config_register_ga("Humidity", show_periodic_options); + pres_ga = knx.config_register_ga("Pressure", show_periodic_options); + + knx.callback_register("Read Temperature", temp_cb); + knx.callback_register("Read Humidity", hum_cb); + knx.callback_register("Read Pressure", pres_cb); + + knx.feedback_register_float("Temperature (°C)", &last_temp); + knx.feedback_register_float("Humidity (%)", &last_hum); + knx.feedback_register_float("Pressure (hPa)", &last_pres, 0); + + // Load previous config from EEPROM + knx.load(); + + // Init sensor + if (!bme.begin(0x76)) { + Serial.println("Could not find a valid BME280 sensor, check wiring!"); + } + + // Init WiFi + WiFi.hostname(knx.config_get_string(hostname_id)); + WiFi.begin(ssid, pass); + + Serial.println(""); + Serial.print("[Connecting]"); + Serial.print(ssid); + + digitalWrite(LED_PIN, LOW); + while (WiFi.status() != WL_CONNECTED) { + digitalWrite(LED_PIN, HIGH); + delay(250); + Serial.print("."); + digitalWrite(LED_PIN, LOW); + delay(250); + } + digitalWrite(LED_PIN, HIGH); + + // Start knx + knx.start(); + + Serial.println(); + Serial.println("Connected to wifi"); + Serial.println(WiFi.localIP()); +} + +void loop() { + knx.loop(); + + unsigned long now = millis(); + + if (next_change < now) + { + next_change = now + knx.config_get_int(update_rate_id); + + last_temp = bme.readTemperature(); + last_hum = bme.readHumidity(); + last_pres = bme.readPressure()/100.0f; + + Serial.print("T: "); + Serial.print(last_temp); + Serial.print("°C H: "); + Serial.print(last_hum); + Serial.print("% P: "); + Serial.print(last_pres); + Serial.println("hPa"); + + if (knx.config_get_bool(enable_sending_id)) + { + knx.write_2byte_float(knx.config_get_ga(temp_ga), last_temp); + knx.write_2byte_float(knx.config_get_ga(hum_ga), last_hum); + knx.write_2byte_float(knx.config_get_ga(pres_ga), last_pres); + } + } + + delay(50); +} + +bool show_periodic_options() +{ + return knx.config_get_bool(enable_sending_id); +} + +bool enable_reading_callback() +{ + return knx.config_get_bool(enable_reading_id); +} + +void temp_cb(message_t const &msg, void *arg) +{ + switch (msg.ct) + { + case KNX_CT_READ: + { + knx.answer_2byte_float(msg.received_on, last_temp); + break; + } + } +} + +void hum_cb(message_t const &msg, void *arg) +{ + switch (msg.ct) + { + case KNX_CT_READ: + { + knx.answer_2byte_float(msg.received_on, last_hum); + break; + } + } +} + +void pres_cb(message_t const &msg, void *arg) +{ + switch (msg.ct) + { + case KNX_CT_READ: + { + knx.answer_2byte_float(msg.received_on, last_pres); + break; + } + } +} diff --git a/lib_div/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino b/lib_div/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino new file mode 100644 index 000000000..fc2400b7b --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino @@ -0,0 +1,183 @@ +#include + +// WiFi config here +const char* ssid = "ssid"; +const char* pass = "pass"; + +// Common +#define LED_PIN 13 + +// For Basic and S20 +#define BTN1_PIN 0 +#define CH1_PIN 12 + +// For 4CH +#define BTN2_PIN 9 +#define CH2_PIN 5 +#define BTN3_PIN 10 +#define CH3_PIN 4 +#define BTN4_PIN 14 +#define CH4_PIN 15 + +typedef enum __type_option +{ + SONOFF_TYPE_NONE = 0, + SONOFF_TYPE_BASIC = 1, + SONOFF_TYPE_S20 = 2, + SONOFF_TYPE_4CH = 3, + SONOFF_TYPE_4CH_PRO = 4, +} type_option_t; + +option_entry_t type_options[] = { + {"Sonoff Basic", SONOFF_TYPE_BASIC}, + {"Sonoff S20", SONOFF_TYPE_S20}, + {"Sonoff 4CH", SONOFF_TYPE_4CH}, + {"Sonoff 4CH Pro", SONOFF_TYPE_4CH_PRO}, + {nullptr, 0} +}; + +config_id_t hostname_id; +config_id_t type_id; + +typedef struct __sonoff_channel +{ + int pin; + int btn_pin; + config_id_t status_ga_id; + bool state; + bool last_btn_state; +} sonoff_channel_t; + +sonoff_channel_t channels[] = { + {CH1_PIN, BTN1_PIN, 0, false, false}, + {CH2_PIN, BTN2_PIN, 0, false, false}, + {CH3_PIN, BTN3_PIN, 0, false, false}, + {CH4_PIN, BTN4_PIN, 0, false, false}, +}; + +void setup() +{ + pinMode(LED_PIN, OUTPUT); + pinMode(BTN1_PIN, INPUT_PULLUP); + pinMode(BTN2_PIN, INPUT_PULLUP); + pinMode(BTN3_PIN, INPUT_PULLUP); + pinMode(BTN4_PIN, INPUT_PULLUP); + pinMode(CH1_PIN, OUTPUT); + pinMode(CH2_PIN, OUTPUT); + pinMode(CH3_PIN, OUTPUT); + pinMode(CH4_PIN, OUTPUT); + Serial.begin(115200); + + // Register the config options + hostname_id = knx.config_register_string("Hostname", 20, String("sonoff")); + type_id = knx.config_register_options("Type", type_options, SONOFF_TYPE_BASIC); + + channels[0].status_ga_id = knx.config_register_ga("Channel 1 Status GA"); + channels[1].status_ga_id = knx.config_register_ga("Channel 2 Status GA", is_4ch_or_4ch_pro); + channels[2].status_ga_id = knx.config_register_ga("Channel 3 Status GA", is_4ch_or_4ch_pro); + channels[3].status_ga_id = knx.config_register_ga("Channel 4 Status GA", is_4ch_or_4ch_pro); + + knx.callback_register("Channel 1", channel_cb, &channels[0]); + knx.callback_register("Channel 2", channel_cb, &channels[1], is_4ch_or_4ch_pro); + knx.callback_register("Channel 3", channel_cb, &channels[2], is_4ch_or_4ch_pro); + knx.callback_register("Channel 4", channel_cb, &channels[3], is_4ch_or_4ch_pro); + + knx.feedback_register_bool("Channel 1 is on", &(channels[0].state)); + knx.feedback_register_action("Toogle channel 1", toggle_chan, &channels[0]); + knx.feedback_register_bool("Channel 2 is on", &(channels[1].state), is_4ch_or_4ch_pro); + knx.feedback_register_action("Toogle channel 2", toggle_chan, &channels[1], is_4ch_or_4ch_pro); + knx.feedback_register_bool("Channel 3 is on", &(channels[2].state), is_4ch_or_4ch_pro); + knx.feedback_register_action("Toogle channel 3", toggle_chan, &channels[2], is_4ch_or_4ch_pro); + knx.feedback_register_bool("Channel 4 is on", &(channels[3].state), is_4ch_or_4ch_pro); + knx.feedback_register_action("Toogle channel 4", toggle_chan, &channels[3], is_4ch_or_4ch_pro); + + knx.load(); + + // Init WiFi + WiFi.hostname(knx.config_get_string(hostname_id)); + WiFi.begin(ssid, pass); + + Serial.println(""); + Serial.print("[Connecting]"); + Serial.print(ssid); + + digitalWrite(LED_PIN, LOW); + while (WiFi.status() != WL_CONNECTED) { + digitalWrite(LED_PIN, HIGH); + delay(500); + Serial.print("."); + digitalWrite(LED_PIN, LOW); + } + digitalWrite(LED_PIN, HIGH); + + // Start knx + knx.start(); + + Serial.println(); + Serial.println("Connected to wifi"); + Serial.println(WiFi.localIP()); +} + +void loop() +{ + knx.loop(); + + // Check local buttons + check_button(&channels[0]); + if (is_4ch_or_4ch_pro()) + { + check_button(&channels[1]); + check_button(&channels[2]); + check_button(&channels[3]); + } + + delay(50); +} + +bool is_basic_or_s20() +{ + uint8_t type = knx.config_get_options(type_id); + return type == SONOFF_TYPE_BASIC || type == SONOFF_TYPE_S20; +} + +bool is_4ch_or_4ch_pro() +{ + uint8_t type = knx.config_get_options(type_id); + return type == SONOFF_TYPE_4CH ||type == SONOFF_TYPE_4CH_PRO; +} + +void check_button(sonoff_channel_t *chan) +{ + bool state_now = digitalRead(chan->btn_pin) == HIGH ? true : false; + if (state_now != chan->last_btn_state && state_now == LOW) + { + chan->state = !chan->state; + digitalWrite(chan->pin, chan->state ? HIGH : LOW); + knx.write_1bit(knx.config_get_ga(chan->status_ga_id), chan->state); + } + chan->last_btn_state = state_now; +} + +void toggle_chan(void *arg) +{ + sonoff_channel_t *chan = (sonoff_channel_t *)arg; + chan->state = !chan->state; + digitalWrite(chan->pin, chan->state ? HIGH : LOW); + knx.write_1bit(knx.config_get_ga(chan->status_ga_id), chan->state); +} + +void channel_cb(message_t const &msg, void *arg) +{ + sonoff_channel_t *chan = (sonoff_channel_t *)arg; + switch (msg.ct) + { + case KNX_CT_WRITE: + chan->state = msg.data[0]; + Serial.println(chan->state ? "Toggle on" : "Toggle off"); + digitalWrite(chan->pin, chan->state ? HIGH : LOW); + knx.write_1bit(knx.config_get_ga(chan->status_ga_id), chan->state); + break; + case KNX_CT_READ: + knx.answer_1bit(msg.received_on, chan->state); + } +} diff --git a/lib_div/esp-knx-ip-0.5.2/examples/static-config/static-config.ino b/lib_div/esp-knx-ip-0.5.2/examples/static-config/static-config.ino new file mode 100644 index 000000000..54472dda3 --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/examples/static-config/static-config.ino @@ -0,0 +1,142 @@ +/* + * This is an example showing a simple environment sensor based on a BME280 attached via I2C. + * It shows, how the library can used to statically configure a device without a webserver for config. + * This sketch was tested on a WeMos D1 mini + */ + +#include +#include + +// WiFi config here +const char* ssid = "myssid"; +const char* pass = "mypassword"; + +#define LED_PIN D4 +#define UPDATE_INTERVAL 10000 + +unsigned long next_change = 0; + +float last_temp = 0.0; +float last_hum = 0.0; +float last_pres = 0.0; + +Adafruit_BME280 bme; + +// Group addresses to send to (1/1/1, 1/1/2 and 1/1/3) +address_t temp_ga = knx.GA_to_address(1, 1, 1); +address_t hum_ga = knx.GA_to_address(1, 1, 2); +address_t pres_ga = knx.GA_to_address(1, 1, 3); + +void setup() { + pinMode(LED_PIN, OUTPUT); + Serial.begin(115200); + + callback_id_t temp_cb_id = knx.callback_register("Read Temperature", temp_cb); + callback_id_t hum_cb_id =knx.callback_register("Read Humidity", hum_cb); + callback_id_t pres_cb_id =knx.callback_register("Read Pressure", pres_cb); + + // Assign callbacks to group addresses (2/1/1, 2/1/2, 2/1/3) + knx.callback_assign(temp_cb_id, knx.GA_to_address(2, 1, 1)); + knx.callback_assign(hum_cb_id, knx.GA_to_address(2, 1, 2)); + knx.callback_assign(pres_cb_id, knx.GA_to_address(2, 1, 3)); + + // Set physical address (1.1.1) + knx.physical_address_set(knx.PA_to_address(1, 1, 1)); + + // Do not call knx.load() for static config, it will try to load config from EEPROM which we don't have here + + // Init sensor + if (!bme.begin(0x76)) { + Serial.println("Could not find a valid BME280 sensor, check wiring!"); + } + + // Init WiFi + WiFi.hostname("env"); + WiFi.begin(ssid, pass); + + Serial.println(""); + Serial.print("[Connecting]"); + Serial.print(ssid); + + digitalWrite(LED_PIN, LOW); + while (WiFi.status() != WL_CONNECTED) { + digitalWrite(LED_PIN, HIGH); + delay(250); + Serial.print("."); + digitalWrite(LED_PIN, LOW); + delay(250); + } + digitalWrite(LED_PIN, HIGH); + + // Start knx, disable webserver by passing nullptr + knx.start(nullptr); + + Serial.println(); + Serial.println("Connected to wifi"); + Serial.println(WiFi.localIP()); +} + +void loop() { + knx.loop(); + + unsigned long now = millis(); + + if (next_change < now) + { + next_change = now + UPDATE_INTERVAL; + + last_temp = bme.readTemperature(); + last_hum = bme.readHumidity(); + last_pres = bme.readPressure()/100.0f; + + Serial.print("T: "); + Serial.print(last_temp); + Serial.print("°C H: "); + Serial.print(last_hum); + Serial.print("% P: "); + Serial.print(last_pres); + Serial.println("hPa"); + + knx.write_2byte_float(temp_ga, last_temp); + knx.write_2byte_float(hum_ga, last_hum); + knx.write_2byte_float(pres_ga, last_pres); + } + + delay(50); +} + +void temp_cb(message_t const &msg, void *arg) +{ + switch (msg.ct) + { + case KNX_CT_READ: + { + knx.answer_2byte_float(msg.received_on, last_temp); + break; + } + } +} + +void hum_cb(message_t const &msg, void *arg) +{ + switch (msg.ct) + { + case KNX_CT_READ: + { + knx.answer_2byte_float(msg.received_on, last_hum); + break; + } + } +} + +void pres_cb(message_t const &msg, void *arg) +{ + switch (msg.ct) + { + case KNX_CT_READ: + { + knx.answer_2byte_float(msg.received_on, last_pres); + break; + } + } +} diff --git a/lib_div/esp-knx-ip-0.5.2/keywords.txt b/lib_div/esp-knx-ip-0.5.2/keywords.txt new file mode 100644 index 000000000..59836ef05 --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/keywords.txt @@ -0,0 +1,107 @@ +# datatypes +address_t KEYWORD1 DATA_TYPE +message_t KEYWORD1 DATA_TYPE +callback_id_t KEYWORD1 DATA_TYPE +callback_assignment_id_t KEYWORD1 DATA_TYPE +option_entry_t KEYWORD1 DATA_TYPE +config_id_t KEYWORD1 DATA_TYPE +enable_condition_t KEYWORD1 DATA_TYPE +callback_fptr_t KEYWORD1 DATA_TYPE +feedback_action_fptr_t KEYWORD1 DATA_TYPE +knx_command_type_t KEYWORD1 DATA_TYPE + +# methods +setup KEYWORD2 +loop KEYWORD2 +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_bool KEYWORD2 +config_register_options KEYWORD2 +config_register_ga KEYWORD2 +config_get_string KEYWORD2 +config_get_int KEYWORD2 +config_get_bool KEYWORD2 +config_get_options KEYWORD2 +config_get_ga KEYWORD2 +config_set_string KEYWORD2 +config_set_int 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 +feedback_register_action KEYWORD2 +send_1bit KEYWORD2 +send_2bit KEYWORD2 +send_4bit KEYWORD2 +send_1byte_int KEYWORD2 +send_1byte_uint KEYWORD2 +send_2byte_int KEYWORD2 +send_2byte_uint KEYWORD2 +send_2byte_float KEYWORD2 +send_3byte_time KEYWORD2 +send_3byte_time KEYWORD2 +send_3byte_date KEYWORD2 +send_3byte_date KEYWORD2 +send_3byte_color KEYWORD2 +send_3byte_color KEYWORD2 +send_4byte_int KEYWORD2 +send_4byte_uint KEYWORD2 +send_4byte_float KEYWORD2 +send_14byte_string KEYWORD2 +write_1bit KEYWORD2 +write_2bit KEYWORD2 +write_4bit KEYWORD2 +write_1byte_int KEYWORD2 +write_1byte_uint KEYWORD2 +write_2byte_int KEYWORD2 +write_2byte_uint KEYWORD2 +write_2byte_float KEYWORD2 +write_3byte_time KEYWORD2 +write_3byte_time KEYWORD2 +write_3byte_date KEYWORD2 +write_3byte_date KEYWORD2 +write_3byte_color KEYWORD2 +write_3byte_color KEYWORD2 +write_4byte_int KEYWORD2 +write_4byte_uint KEYWORD2 +write_4byte_float KEYWORD2 +write_14byte_string KEYWORD2 +answer_1bit KEYWORD2 +answer_2bit KEYWORD2 +answer_4bit KEYWORD2 +answer_1byte_int KEYWORD2 +answer_1byte_uint KEYWORD2 +answer_2byte_int KEYWORD2 +answer_2byte_uint KEYWORD2 +answer_2byte_float KEYWORD2 +answer_3byte_time KEYWORD2 +answer_3byte_time KEYWORD2 +answer_3byte_date KEYWORD2 +answer_3byte_date KEYWORD2 +answer_3byte_color KEYWORD2 +answer_3byte_color KEYWORD2 +answer_4byte_int KEYWORD2 +answer_4byte_uint KEYWORD2 +answer_4byte_float KEYWORD2 +answer_14byte_string KEYWORD2 + +data_to_1byte_int KEYWORD2 +data_to_2byte_int KEYWORD2 +data_to_2byte_float KEYWORD2 +data_to_4byte_float KEYWORD2 +data_to_3byte_color KEYWORD2 +data_to_3byte_time KEYWORD2 +data_to_3byte_data KEYWORD2 + +# constants +knx LITERAL1 diff --git a/lib_div/esp-knx-ip-0.5.2/library.json b/lib_div/esp-knx-ip-0.5.2/library.json new file mode 100644 index 000000000..0e2b42361 --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/library.json @@ -0,0 +1,18 @@ +{ + "name": "ESP KNX IP Library", + "keywords": "knx, ethernet, mqtt, m2m, iot", + "description": "ESP8266 library for KNX/IP communication.", + "authors": [ + { + "name": "Nico Weichbrodt", + "maintainer": true + } + ], + "repository": { + "type": "git", + "url": "https://github.com/envy/esp-knx-ip.git" + }, + "version": "0.5.2", + "frameworks": "arduino", + "platforms": ["espressif8266", "espressif32"] +} diff --git a/lib_div/esp-knx-ip-0.5.2/library.properties.off b/lib_div/esp-knx-ip-0.5.2/library.properties.off new file mode 100644 index 000000000..f3b86de9c --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/library.properties.off @@ -0,0 +1,10 @@ +name=ESP KNX IP Library +version=0.5.2 +author=Nico Weichbrodt +maintainer=Nico Weichbrodt +sentence=ESP8266 library for KNX/IP communication. +paragraph=Build your own IoT devices with KNX/IP connectivity! +category=Communication +url=https://github.com/envy/esp-knx-ip +architectures=esp8266 +includes=esp-knx-ip.h diff --git a/lib_div/esp-knx-ip-0.5.2/src/DPT.h b/lib_div/esp-knx-ip-0.5.2/src/DPT.h new file mode 100644 index 000000000..3529d51af --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/src/DPT.h @@ -0,0 +1,73 @@ +/** + * esp-knx-ip library for KNX/IP communication on an ESP8266 + * Author: Nico Weichbrodt + * License: MIT + */ + +typedef enum __dpt_1_001 +{ + DPT_1_001_OFF = 0x00, + DPT_1_001_ON = 0x01, +} dpt_1_001_t; + +typedef enum __dpt_2_001 +{ + DPT_2_001_NO_OFF = 0b00, + DPT_2_001_NO_ON = 0b01, + DPT_2_001_YES_OFF = 0b10, + DPT_2_001_YES_ON = 0b11, +} dpt_2_001_t; + +typedef enum __dpt_3_007 +{ + DPT_3_007_DECREASE_STOP = 0x00, + DPT_3_007_DECREASE_100 = 0x01, + DPT_3_007_DECREASE_50 = 0x02, + DPT_3_007_DECREASE_25 = 0x03, + DPT_3_007_DECREASE_12 = 0x04, + DPT_3_007_DECREASE_6 = 0x05, + DPT_3_007_DECREASE_3 = 0x06, + DPT_3_007_DECREASE_1 = 0x07, + DPT_3_007_INCREASE_STOP = 0x08, + DPT_3_007_INCREASE_100 = 0x09, + DPT_3_007_INCREASE_50 = 0x0A, + DPT_3_007_INCREASE_25 = 0x0B, + DPT_3_007_INCREASE_12 = 0x0C, + DPT_3_007_INCREASE_6 = 0x0D, + DPT_3_007_INCREASE_3 = 0x0E, + DPT_3_007_INCREASE_1 = 0x0F, +} dpt_3_007_t; + +typedef enum __weekday +{ + DPT_10_001_WEEKDAY_NODAY = 0, + DPT_10_001_WEEKDAY_MONDAY = 1, + DPT_10_001_WEEKDAY_TUESDAY = 2, + DPT_10_001_WEEKDAY_WEDNESDAY = 3, + DPT_10_001_WEEKDAY_THURSDAY = 4, + DPT_10_001_WEEKDAY_FRIDAY = 5, + DPT_10_001_WEEKDAY_SATURDAY = 6, + DPT_10_001_WEEKDAY_SUNDAY = 7, +} weekday_t; + +typedef struct __time_of_day +{ + weekday_t weekday; + uint8_t hours; + uint8_t minutes; + uint8_t seconds; +} time_of_day_t; + +typedef struct __date +{ + uint8_t day; + uint8_t month; + uint8_t year; +} date_t; + +typedef struct __color +{ + uint8_t red; + uint8_t green; + uint8_t blue; +} color_t; diff --git a/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-config.cpp b/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-config.cpp new file mode 100644 index 000000000..8d2b7b39d --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-config.cpp @@ -0,0 +1,358 @@ +/** + * esp-knx-ip library for KNX/IP communication on an ESP8266 + * Author: Nico Weichbrodt + * License: MIT + */ + +#include "esp-knx-ip.h" + +/** + * Physical address functions + */ + +void ESPKNXIP::physical_address_set(address_t const &addr) +{ + physaddr = addr; +} + +address_t ESPKNXIP::physical_address_get() +{ + return physaddr; +} + +/** + * Configuration functions start here + */ +config_id_t ESPKNXIP::config_register_string(String name, uint8_t len, String _default, enable_condition_t cond) +{ + if (registered_configs >= MAX_CONFIGS) + return -1; + + if (_default.length() >= len) + return -1; + + config_id_t id = registered_configs; + + custom_configs[id].name = name; + custom_configs[id].type = CONFIG_TYPE_STRING; + custom_configs[id].len = sizeof(uint8_t) + len; + custom_configs[id].cond = cond; + if (id == 0) + custom_configs[id].offset = 0; + else + custom_configs[id].offset = custom_configs[id - 1].offset + custom_configs[id - 1].len; + + __config_set_string(id, _default); + + registered_configs++; + + DEBUG_PRINT("Registered config >"); + DEBUG_PRINT(name); + DEBUG_PRINT("< @ "); + DEBUG_PRINT(id); + DEBUG_PRINT("/string["); + DEBUG_PRINT(custom_configs[id].offset); + DEBUG_PRINT("+"); + DEBUG_PRINT(custom_configs[id].len); + DEBUG_PRINTLN("]"); + + return id; +} + +config_id_t ESPKNXIP::config_register_int(String name, int32_t _default, enable_condition_t cond) +{ + if (registered_configs >= MAX_CONFIGS) + return -1; + + config_id_t id = registered_configs; + + custom_configs[id].name = name; + custom_configs[id].type = CONFIG_TYPE_INT; + custom_configs[id].len = sizeof(uint8_t) + sizeof(int32_t); + custom_configs[id].cond = cond; + if (id == 0) + custom_configs[id].offset = 0; + else + custom_configs[id].offset = custom_configs[id - 1].offset + custom_configs[id - 1].len; + + __config_set_int(id, _default); + + registered_configs++; + + DEBUG_PRINT("Registered config >"); + DEBUG_PRINT(name); + DEBUG_PRINT("< @ "); + DEBUG_PRINT(id); + DEBUG_PRINT("/int["); + DEBUG_PRINT(custom_configs[id].offset); + DEBUG_PRINT("+"); + DEBUG_PRINT(custom_configs[id].len); + DEBUG_PRINTLN("]"); + + return id; +} + +config_id_t ESPKNXIP::config_register_bool(String name, bool _default, enable_condition_t cond) +{ + if (registered_configs >= MAX_CONFIGS) + return -1; + + config_id_t id = registered_configs; + + custom_configs[id].name = name; + custom_configs[id].type = CONFIG_TYPE_BOOL; + custom_configs[id].len = sizeof(uint8_t) + sizeof(uint8_t); + custom_configs[id].cond = cond; + if (id == 0) + custom_configs[id].offset = 0; + else + custom_configs[id].offset = custom_configs[id - 1].offset + custom_configs[id - 1].len; + + __config_set_bool(id, _default); + + registered_configs++; + + DEBUG_PRINT("Registered config >"); + DEBUG_PRINT(name); + DEBUG_PRINT("< @ "); + DEBUG_PRINT(id); + DEBUG_PRINT("/bool["); + DEBUG_PRINT(custom_configs[id].offset); + DEBUG_PRINT("+"); + DEBUG_PRINT(custom_configs[id].len); + DEBUG_PRINTLN("]"); + + return id; +} + +config_id_t ESPKNXIP::config_register_options(String name, option_entry_t *options, uint8_t _default, enable_condition_t cond) +{ + if (registered_configs >= MAX_CONFIGS) + return -1; + + if (options == nullptr || options->name == nullptr) + return -1; + + config_id_t id = registered_configs; + + custom_configs[id].name = name; + custom_configs[id].type = CONFIG_TYPE_OPTIONS; + custom_configs[id].len = sizeof(uint8_t) + sizeof(uint8_t); + custom_configs[id].cond = cond; + if (id == 0) + custom_configs[id].offset = 0; + else + custom_configs[id].offset = custom_configs[id - 1].offset + custom_configs[id - 1].len; + + custom_configs[id].data.options = options; + + __config_set_options(id, _default); + + registered_configs++; + + DEBUG_PRINT("Registered config >"); + DEBUG_PRINT(name); + DEBUG_PRINT("< @ "); + DEBUG_PRINT(id); + DEBUG_PRINT("/opt["); + DEBUG_PRINT(custom_configs[id].offset); + DEBUG_PRINT("+"); + DEBUG_PRINT(custom_configs[id].len); + DEBUG_PRINTLN("]"); + + return id; +} + +config_id_t ESPKNXIP::config_register_ga(String name, enable_condition_t cond) +{ + if (registered_configs >= MAX_CONFIGS) + return -1; + + config_id_t id = registered_configs; + + custom_configs[id].name = name; + custom_configs[id].type = CONFIG_TYPE_GA; + custom_configs[id].len = sizeof(uint8_t) + sizeof(address_t); + custom_configs[id].cond = cond; + if (id == 0) + custom_configs[id].offset = 0; + else + custom_configs[id].offset = custom_configs[id - 1].offset + custom_configs[id - 1].len; + + address_t t; + t.value = 0; + __config_set_ga(id, t); + + registered_configs++; + + DEBUG_PRINT("Registered config >"); + DEBUG_PRINT(name); + DEBUG_PRINT("< @ "); + DEBUG_PRINT(id); + DEBUG_PRINT("/ga["); + DEBUG_PRINT(custom_configs[id].offset); + DEBUG_PRINT("+"); + DEBUG_PRINT(custom_configs[id].len); + DEBUG_PRINTLN("]"); + + return id; +} + +void ESPKNXIP::__config_set_flags(config_id_t id, config_flags_t flags) +{ + DEBUG_PRINT("Setting flag @ "); + DEBUG_PRINT(custom_configs[id].offset); + DEBUG_PRINT(" to "); + DEBUG_PRINT(custom_config_data[custom_configs[id].offset], BIN); + DEBUG_PRINT(" | "); + DEBUG_PRINT(flags, BIN); + custom_config_data[custom_configs[id].offset] |= (uint8_t)flags; + DEBUG_PRINT(" = "); + DEBUG_PRINTLN(custom_config_data[custom_configs[id].offset], BIN); +} + +void ESPKNXIP::config_set_string(config_id_t id, String val) +{ + if (id >= registered_configs) + return; + if (custom_configs[id].type != CONFIG_TYPE_STRING) + return; + if (val.length() >= custom_configs[id].len) + return; + __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); + __config_set_string(id, val); +} + +void ESPKNXIP::__config_set_string(config_id_t id, String &val) +{ + memcpy(&custom_config_data[custom_configs[id].offset + sizeof(uint8_t)], val.c_str(), val.length()+1); +} + +void ESPKNXIP::config_set_int(config_id_t id, int32_t val) +{ + if (id >= registered_configs) + return; + if (custom_configs[id].type != CONFIG_TYPE_INT) + return; + __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); + __config_set_int(id, val); +} + +void ESPKNXIP::__config_set_int(config_id_t id, int32_t val) +{ + // This does not work for some reason: + // Could be due to pointer alignment + //int32_t *v = (int32_t *)(custom_config_data + custom_configs[id].offset); + //*v = val; + custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 0] = (uint8_t)((val & 0xFF000000) >> 24); + custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 1] = (uint8_t)((val & 0x00FF0000) >> 16); + custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 2] = (uint8_t)((val & 0x0000FF00) >> 8); + custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 3] = (uint8_t)((val & 0x000000FF) >> 0); +} + +void ESPKNXIP::config_set_bool(config_id_t id, bool val) +{ + if (id >= registered_configs) + return; + if (custom_configs[id].type != CONFIG_TYPE_BOOL) + return; + __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); + __config_set_bool(id, val); +} + +void ESPKNXIP::__config_set_bool(config_id_t id, bool val) +{ + custom_config_data[custom_configs[id].offset + sizeof(uint8_t)] = val ? 1 : 0; +} + +void ESPKNXIP::config_set_options(config_id_t id, uint8_t val) +{ + if (id >= registered_configs) + return; + if (custom_configs[id].type != CONFIG_TYPE_OPTIONS) + return; + + option_entry_t *cur = custom_configs[id].data.options; + while (cur->name != nullptr) + { + if (cur->value == val) + { + __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); + __config_set_options(id, val); + break; + } + cur++; + } +} + +void ESPKNXIP::__config_set_options(config_id_t id, uint8_t val) +{ + custom_config_data[custom_configs[id].offset + sizeof(uint8_t)] = val; +} + +void ESPKNXIP::config_set_ga(config_id_t id, address_t const &val) +{ + if (id >= registered_configs) + return; + if (custom_configs[id].type != CONFIG_TYPE_GA) + return; + __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); + __config_set_ga(id, val); +} + +void ESPKNXIP::__config_set_ga(config_id_t id, address_t const &val) +{ + custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 0] = val.bytes.high; + custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 1] = val.bytes.low; +} + +String ESPKNXIP::config_get_string(config_id_t id) +{ + if (id >= registered_configs) + return String(""); + + return String((char *)&custom_config_data[custom_configs[id].offset + sizeof(uint8_t)]); +} + +int32_t ESPKNXIP::config_get_int(config_id_t id) +{ + if (id >= registered_configs) + return 0; + + int32_t v = (custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 0] << 24) + + (custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 1] << 16) + + (custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 2] << 8) + + (custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 3] << 0); + return v; +} + +bool ESPKNXIP::config_get_bool(config_id_t id) +{ + if (id >= registered_configs) + return false; + + return custom_config_data[custom_configs[id].offset + sizeof(uint8_t)] != 0; +} + +uint8_t ESPKNXIP::config_get_options(config_id_t id) +{ + if (id >= registered_configs) + return false; + + return custom_config_data[custom_configs[id].offset + sizeof(uint8_t)]; +} + +address_t ESPKNXIP::config_get_ga(config_id_t id) +{ + address_t t; + if (id >= registered_configs) + { + t.value = 0; + return t; + } + + t.bytes.high = custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 0]; + t.bytes.low = custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 1]; + + return t; +} diff --git a/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-conversion.cpp b/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-conversion.cpp new file mode 100644 index 000000000..9dc2fd563 --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-conversion.cpp @@ -0,0 +1,87 @@ +/** + * esp-knx-ip library for KNX/IP communication on an ESP8266 + * Author: Nico Weichbrodt + * License: MIT + */ + +#include "esp-knx-ip.h" + +/** + * Conversion functions + */ + +bool ESPKNXIP::data_to_bool(uint8_t *data) +{ + return (data[0] & 0x01) == 1 ? true : false; +} + +int8_t ESPKNXIP::data_to_1byte_int(uint8_t *data) +{ + return (int8_t)data[1]; +} + +uint8_t ESPKNXIP::data_to_1byte_uint(uint8_t *data) +{ + return data[1]; +} + +int16_t ESPKNXIP::data_to_2byte_int(uint8_t *data) +{ + return (int16_t)((data[1] << 8) | data[2]); +} + +uint16_t ESPKNXIP::data_to_2byte_uint(uint8_t *data) +{ + return (uint16_t)((data[1] << 8) | data[2]); +} + +float ESPKNXIP::data_to_2byte_float(uint8_t *data) +{ + //uint8_t sign = (data[1] & 0b10000000) >> 7; + uint8_t expo = (data[1] & 0b01111000) >> 3; + int16_t mant = ((data[1] & 0b10000111) << 8) | data[2]; + return 0.01f * mant * pow(2, expo); +} + +time_of_day_t ESPKNXIP::data_to_3byte_time(uint8_t *data) +{ + time_of_day_t time; + time.weekday = (weekday_t)((data[1] & 0b11100000) >> 5); + time.hours = (data[1] & 0b00011111); + time.minutes = (data[2] & 0b00111111); + time.seconds = (data[3] & 0b00111111); + return time; +} + +date_t ESPKNXIP::data_to_3byte_data(uint8_t *data) +{ + date_t date; + date.day = (data[1] & 0b00011111); + date.month = (data[2] & 0b00001111); + date.year = (data[3] & 0b01111111); + return date; +} + +color_t ESPKNXIP::data_to_3byte_color(uint8_t *data) +{ + color_t color; + color.red = data[1]; + color.green = data[2]; + color.blue = data[3]; + return color; +} + +int32_t ESPKNXIP::data_to_4byte_int(uint8_t *data) +{ + return (int32_t)((data[1] << 24) | (data[2] << 16) | (data[3] << 8) | (data[4] << 0)); +} + +uint32_t ESPKNXIP::data_to_4byte_uint(uint8_t *data) +{ + return (uint32_t)((data[1] << 24) | (data[2] << 16) | (data[3] << 8) | (data[4] << 0)); +} + +float ESPKNXIP::data_to_4byte_float(uint8_t *data) +{ + return (float)((data[1] << 24) | (data[2] << 16) | (data[3] << 8) |data[4]); +} \ No newline at end of file diff --git a/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-send.cpp b/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-send.cpp new file mode 100644 index 000000000..e71e5954c --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-send.cpp @@ -0,0 +1,201 @@ +/** + * esp-knx-ip library for KNX/IP communication on an ESP8266 + * Author: Nico Weichbrodt + * License: MIT + */ + +#include "esp-knx-ip.h" + +/** + * Send functions + */ + +void ESPKNXIP::send(address_t const &receiver, knx_command_type_t ct, uint8_t data_len, uint8_t *data) +{ + if (receiver.value == 0) + return; + +#if SEND_CHECKSUM + uint32_t len = 6 + 2 + 8 + data_len + 1; // knx_pkt + cemi_msg + cemi_service + data + checksum +#else + uint32_t len = 6 + 2 + 8 + data_len; // knx_pkt + cemi_msg + cemi_service + data +#endif + DEBUG_PRINT(F("Creating packet with len ")); + DEBUG_PRINTLN(len) + uint8_t buf[len]; + knx_ip_pkt_t *knx_pkt = (knx_ip_pkt_t *)buf; + knx_pkt->header_len = 0x06; + knx_pkt->protocol_version = 0x10; + knx_pkt->service_type = __ntohs(KNX_ST_ROUTING_INDICATION); + knx_pkt->total_len.len = __ntohs(len); + cemi_msg_t *cemi_msg = (cemi_msg_t *)knx_pkt->pkt_data; + cemi_msg->message_code = KNX_MT_L_DATA_IND; + cemi_msg->additional_info_len = 0; + cemi_service_t *cemi_data = &cemi_msg->data.service_information; + cemi_data->control_1.bits.confirm = 0; +//cemi_data->control_1.bits.ack = 1; + cemi_data->control_1.bits.ack = 0; // ask for ACK? 0-no 1-yes + cemi_data->control_1.bits.priority = B11; + cemi_data->control_1.bits.system_broadcast = 0x01; + cemi_data->control_1.bits.repeat = 0x01; // 0 = repeated telegram, 1 = not repeated telegram + cemi_data->control_1.bits.reserved = 0; + cemi_data->control_1.bits.frame_type = 0x01; + cemi_data->control_2.bits.extended_frame_format = 0x00; + cemi_data->control_2.bits.hop_count = 0x06; + cemi_data->control_2.bits.dest_addr_type = 0x01; + cemi_data->source = physaddr; + cemi_data->destination = receiver; + //cemi_data->destination.bytes.high = (area << 3) | line; + //cemi_data->destination.bytes.low = member; + cemi_data->data_len = data_len; + cemi_data->pci.apci = (ct & 0x0C) >> 2; +//cemi_data->pci.apci = KNX_COT_NCD_ACK; + cemi_data->pci.tpci_seq_number = 0x00; + cemi_data->pci.tpci_comm_type = KNX_COT_UDP; // Type of communication: DATA PACKAGE or CONTROL DATA +//cemi_data->pci.tpci_comm_type = KNX_COT_NCD; // Type of communication: DATA PACKAGE or CONTROL DATA + memcpy(cemi_data->data, data, data_len); +//cemi_data->data[0] = (cemi_data->data[0] & 0x3F) | ((KNX_COT_NCD_ACK & 0x03) << 6); + cemi_data->data[0] = (cemi_data->data[0] & 0x3F) | ((ct & 0x03) << 6); + +#if SEND_CHECKSUM + // Calculate checksum, which is just XOR of all bytes + uint8_t cs = buf[0] ^ buf[1]; + for (uint32_t i = 2; i < len - 1; ++i) + { + cs ^= buf[i]; + } + buf[len - 1] = cs; +#endif + +#ifdef ESP_KNX_DEBUG + DEBUG_PRINT(F("Sending packet:")); + for (int i = 0; i < len; ++i) + { + DEBUG_PRINT(F(" 0x")); + DEBUG_PRINT(buf[i], 16); + } + DEBUG_PRINTLN(F("")); +#endif + + udp.beginPacketMulticast(MULTICAST_IP, MULTICAST_PORT, WiFi.localIP()); + udp.write(buf, len); + udp.endPacket(); + +} + +void ESPKNXIP::send_1bit(address_t const &receiver, knx_command_type_t ct, uint8_t bit) +{ + uint8_t buf[] = {(uint8_t)(bit & 0b00000001)}; + send(receiver, ct, 1, buf); +} + +void ESPKNXIP::send_2bit(address_t const &receiver, knx_command_type_t ct, uint8_t twobit) +{ + uint8_t buf[] = {(uint8_t)(twobit & 0b00000011)}; + send(receiver, ct, 1, buf); +} + +void ESPKNXIP::send_4bit(address_t const &receiver, knx_command_type_t ct, uint8_t fourbit) +{ + uint8_t buf[] = {(uint8_t)(fourbit & 0b00001111)}; + send(receiver, ct, 1, buf); +} + +void ESPKNXIP::send_1byte_int(address_t const &receiver, knx_command_type_t ct, int8_t val) +{ + uint8_t buf[] = {0x00, (uint8_t)val}; + send(receiver, ct, 2, buf); +} + +void ESPKNXIP::send_1byte_uint(address_t const &receiver, knx_command_type_t ct, uint8_t val) +{ + uint8_t buf[] = {0x00, val}; + send(receiver, ct, 2, buf); +} + +void ESPKNXIP::send_2byte_int(address_t const &receiver, knx_command_type_t ct, int16_t val) +{ + uint8_t buf[] = {0x00, (uint8_t)(val >> 8), (uint8_t)(val & 0x00FF)}; + send(receiver, ct, 3, buf); +} + +void ESPKNXIP::send_2byte_uint(address_t const &receiver, knx_command_type_t ct, uint16_t val) +{ + uint8_t buf[] = {0x00, (uint8_t)(val >> 8), (uint8_t)(val & 0x00FF)}; + send(receiver, ct, 3, buf); +} + +void ESPKNXIP::send_2byte_float(address_t const &receiver, knx_command_type_t ct, float val) +{ + float v = val * 100.0f; + int e = 0; + for (; v < -2048.0f; v /= 2) + ++e; + for (; v > 2047.0f; v /= 2) + ++e; + long m = (long)round(v) & 0x7FF; + short msb = (short) (e << 3 | m >> 8); + if (val < 0.0f) + msb |= 0x80; + uint8_t buf[] = {0x00, (uint8_t)msb, (uint8_t)m}; + send(receiver, ct, 3, buf); +} + +void ESPKNXIP::send_3byte_time(address_t const &receiver, knx_command_type_t ct, uint8_t weekday, uint8_t hours, uint8_t minutes, uint8_t seconds) +{ + weekday <<= 5; + uint8_t buf[] = {0x00, (uint8_t)(((weekday << 5) & 0xE0) | (hours & 0x1F)), (uint8_t)(minutes & 0x3F), (uint8_t)(seconds & 0x3F)}; + send(receiver, ct, 4, buf); +} + +void ESPKNXIP::send_3byte_date(address_t const &receiver, knx_command_type_t ct, uint8_t day, uint8_t month, uint8_t year) +{ + uint8_t buf[] = {0x00, (uint8_t)(day & 0x1F), (uint8_t)(month & 0x0F), year}; + send(receiver, ct, 4, buf); +} + +void ESPKNXIP::send_3byte_color(address_t const &receiver, knx_command_type_t ct, uint8_t red, uint8_t green, uint8_t blue) +{ + uint8_t buf[] = {0x00, red, green, blue}; + send(receiver, ct, 4, buf); +} + +void ESPKNXIP::send_4byte_int(address_t const &receiver, knx_command_type_t ct, int32_t val) +{ + uint8_t buf[] = {0x00, + (uint8_t)((val & 0xFF000000) >> 24), + (uint8_t)((val & 0x00FF0000) >> 16), + (uint8_t)((val & 0x0000FF00) >> 8), + (uint8_t)((val & 0x000000FF) >> 0)}; + send(receiver, ct, 5, buf); +} + +void ESPKNXIP::send_4byte_uint(address_t const &receiver, knx_command_type_t ct, uint32_t val) +{ + uint8_t buf[] = {0x00, + (uint8_t)((val & 0xFF000000) >> 24), + (uint8_t)((val & 0x00FF0000) >> 16), + (uint8_t)((val & 0x0000FF00) >> 8), + (uint8_t)((val & 0x000000FF) >> 0)}; + send(receiver, ct, 5, buf); +} + +void ESPKNXIP::send_4byte_float(address_t const &receiver, knx_command_type_t ct, float val) +{ + uint8_t buf[] = {0x00, ((uint8_t *)&val)[3], ((uint8_t *)&val)[2], ((uint8_t *)&val)[1], ((uint8_t *)&val)[0]}; + send(receiver, ct, 5, buf); +} + +void ESPKNXIP::send_14byte_string(address_t const &receiver, knx_command_type_t ct, const char *val) +{ + // DPT16 strings are always 14 bytes long, however the data array is one larger due to the telegram structure. + // The first byte needs to be zero, string start after that. + uint8_t buf[15] = {0x00}; + int len = strlen(val); + if (len > 14) + { + len = 14; + } + memcpy(buf+1, val, len); + send(receiver, ct, 15, buf); +} diff --git a/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-webserver.cpp b/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-webserver.cpp new file mode 100644 index 000000000..bdbc013fd --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip-webserver.cpp @@ -0,0 +1,540 @@ +/** + * esp-knx-ip library for KNX/IP communication on an ESP8266 + * Author: Nico Weichbrodt + * License: MIT + */ + +#include "esp-knx-ip.h" + +void ESPKNXIP::__handle_root() +{ + String m = F(""); +#if USE_BOOTSTRAP + m += F(""); + m += F(""); +#endif + m += F("
"); + m += F("

ESP KNX

"); + + // Feedback + + if (registered_feedbacks > 0) + { + m += F("

Feedback

"); + for (feedback_id_t i = 0; i < registered_feedbacks; ++i) + { + if (feedbacks[i].cond && !feedbacks[i].cond()) + { + continue; + } + m += F("
"); + m += F("
"); + m += F("
"); + m += feedbacks[i].name; + m += F("
"); + switch (feedbacks[i].type) + { + case FEEDBACK_TYPE_INT: + m += F(""); + m += String(*(int32_t *)feedbacks[i].data); + m += F(""); + break; + case FEEDBACK_TYPE_FLOAT: + m += F(""); + 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(""); + break; + case FEEDBACK_TYPE_BOOL: + m += F(""); + m += (*(bool *)feedbacks[i].data) ? F("True") : F("False"); + m += F(""); + break; + case FEEDBACK_TYPE_ACTION: + m += F("
"); + break; + } + m += F("
"); + m += F("
"); + } + } + + if (registered_callbacks > 0) + m += F("

Callbacks

"); + + if (registered_callback_assignments > 0) + { + 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; + } + address_t &addr = callback_assignments[i].address; + m += F("
"); + m += F("
"); + m += F("
"); + m += addr.ga.area; + m += F("/"); + m += addr.ga.line; + m += F("/"); + m += addr.ga.member; + m += F(""); + m += F(""); + m += callbacks[callback_assignments[i].callback_id].name; + m += F("
"); + m += F("
"); + m += F("
"); + m += F("
"); + } + } + + if (registered_callbacks > 0) + { + m += F("
"); + m += F("
"); + m += F(""); + m += F("
/
"); + m += F(""); + m += F("
/
"); + m += F(""); + m += F("
->
"); + m += F(""); + m += F("
"); + m += F("
"); + m += F("
"); + } + + m += F("

Configuration

"); + + // Physical address + m += F("
"); + m += F("
"); + m += F("
Physical address
"); + m += F(""); + m += F("
.
"); + m += F(""); + m += F("
.
"); + m += F(""); + m += F("
"); + m += F("
"); + m += F("
"); + + if (registered_configs > 0) + { + for (config_id_t i = 0; i < registered_configs; ++i) + { + // Check if this config option has a enable condition and if so check that condition + if (custom_configs[i].cond && !custom_configs[i].cond()) + continue; + + m += F("
"); + m += F("
"); + m += F("
"); + m += custom_configs[i].name; + m += F("
"); + + switch (custom_configs[i].type) + { + case CONFIG_TYPE_STRING: + m += F(""); + break; + case CONFIG_TYPE_INT: + m += F(""); + break; + case CONFIG_TYPE_BOOL: + m += F("
"); + m += F(""); + m += F("
"); + break; + case CONFIG_TYPE_OPTIONS: + { + m += F(""); + break; + } + case CONFIG_TYPE_GA: + address_t a = config_get_ga(i); + m += F(""); + m += F("
/
"); + m += F(""); + m += F("
/
"); + m += F(""); + break; + } + m += F(""); + m += F("
"); + m += F("
"); + m += F("
"); + } + } + +#if !(DISABLE_EEPROM_BUTTONS && DISABLE_RESTORE_BUTTON && DISABLE_REBOOT_BUTTON) + // EEPROM save and restore + m += F("
"); + // Save to EEPROM +#if !DISABLE_EEPROM_BUTTONS + m += F("
"); + m += F("
"); + m += F(""); + m += F(""); + m += F("
"); + m += F("
"); + // Restore from EEPROM + m += F("
"); + m += F("
"); + m += F(""); + m += F(""); + m += F("
"); + m += F("
"); +#endif +#if !DISABLE_RESTORE_BUTTON + // Load Defaults + m += F("
"); + m += F("
"); + m += F(""); + m += F("
"); + m += F("
"); +#endif +#if !DISABLE_REBOOT_BUTTON + // Reboot + m += F("
"); + m += F("
"); + m += F(""); + m += F("
"); + m += F("
"); +#endif + m += F("
"); // row +#endif + + // End of page + m += F("
"); + server->send(200, F("text/html"), m); +} + +void ESPKNXIP::__handle_register() +{ + DEBUG_PRINTLN(F("Register called")); + if (server->hasArg(F("area")) && server->hasArg(F("line")) && server->hasArg(F("member")) && server->hasArg(F("cb"))) + { + uint8_t area = server->arg(F("area")).toInt(); + uint8_t line = server->arg(F("line")).toInt(); + uint8_t member = server->arg(F("member")).toInt(); + callback_id_t cb = (callback_id_t)server->arg(F("cb")).toInt(); + + DEBUG_PRINT(F("Got args: ")); + DEBUG_PRINT(area); + DEBUG_PRINT(F("/")); + DEBUG_PRINT(line); + DEBUG_PRINT(F("/")); + DEBUG_PRINT(member); + DEBUG_PRINT(F("/")); + DEBUG_PRINT(cb); + DEBUG_PRINTLN(F("")); + + if (area > 31 || line > 7) + { + DEBUG_PRINTLN(F("Area or Line wrong")); + goto end; + } + + if (!__callback_is_id_valid(cb)) + { + DEBUG_PRINTLN(F("Invalid callback id")); + goto end; + } + address_t ga = {.ga={line, area, member}}; + __callback_register_assignment(ga, cb); + } +end: + server->sendHeader(F("Location"),F(__ROOT_PATH)); + server->send(302); +} + +void ESPKNXIP::__handle_delete() +{ + DEBUG_PRINTLN(F("Delete called")); + if (server->hasArg(F("id"))) + { + callback_assignment_id_t id = (callback_assignment_id_t)server->arg(F("id")).toInt(); + + DEBUG_PRINT(F("Got args: ")); + DEBUG_PRINT(id); + DEBUG_PRINTLN(F("")); + + if (id >= registered_callback_assignments || (callback_assignments[id].slot_flags & SLOT_FLAGS_USED) == 0) + { + DEBUG_PRINTLN(F("ID wrong")); + goto end; + } + + __callback_delete_assignment(id); + } +end: + server->sendHeader(F("Location"),F(__ROOT_PATH)); + server->send(302); +} + +void ESPKNXIP::__handle_set() +{ + DEBUG_PRINTLN(F("Set called")); + if (server->hasArg(F("area")) && server->hasArg(F("line")) && server->hasArg(F("member"))) + { + uint8_t area = server->arg(F("area")).toInt(); + uint8_t line = server->arg(F("line")).toInt(); + uint8_t member = server->arg(F("member")).toInt(); + + DEBUG_PRINT(F("Got args: ")); + DEBUG_PRINT(area); + DEBUG_PRINT(F(".")); + DEBUG_PRINT(line); + DEBUG_PRINT(F(".")); + DEBUG_PRINT(member); + DEBUG_PRINTLN(F("")); + + if (area > 31 || line > 7) + { + DEBUG_PRINTLN(F("Area or Line wrong")); + goto end; + } + + physaddr.bytes.high = (area << 4) | line; + physaddr.bytes.low = member; + } +end: + server->sendHeader(F("Location"),F(__ROOT_PATH)); + server->send(302); +} + +void ESPKNXIP::__handle_config() +{ + DEBUG_PRINTLN(F("Config called")); + if (server->hasArg(F("id"))) + { + config_id_t id = server->arg(F("id")).toInt(); + + DEBUG_PRINT(F("Got args: ")); + DEBUG_PRINT(id); + DEBUG_PRINTLN(F("")); + + if (id < 0 || id >= registered_configs) + { + DEBUG_PRINTLN(F("ID wrong")); + goto end; + } + + switch (custom_configs[id].type) + { + case CONFIG_TYPE_STRING: + { + String v = server->arg(F("value")); + if (v.length() >= custom_configs[id].len) + goto end; + __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); + __config_set_string(id, v); + break; + } + case CONFIG_TYPE_INT: + { + __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); + __config_set_int(id, server->arg(F("value")).toInt()); + break; + } + case CONFIG_TYPE_BOOL: + { + __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); + __config_set_bool(id, server->arg(F("value")).compareTo(F("on")) == 0); + break; + } + case CONFIG_TYPE_OPTIONS: + { + uint8_t val = (uint8_t)server->arg(F("value")).toInt(); + DEBUG_PRINT(F("Value: ")); + DEBUG_PRINTLN(val); + config_set_options(id, val); + break; + } + case CONFIG_TYPE_GA: + { + uint8_t area = server->arg(F("area")).toInt(); + uint8_t line = server->arg(F("line")).toInt(); + uint8_t member = server->arg(F("member")).toInt(); + if (area > 31 || line > 7) + { + DEBUG_PRINTLN(F("Area or Line wrong")); + goto end; + } + address_t tmp; + tmp.bytes.high = (area << 3) | line; + tmp.bytes.low = member; + __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); + __config_set_ga(id, tmp); + break; + } + } + } +end: + server->sendHeader(F("Location"),F(__ROOT_PATH)); + server->send(302); +} + +void ESPKNXIP::__handle_feedback() +{ +DEBUG_PRINTLN(F("Feedback called")); + if (server->hasArg(F("id"))) + { + config_id_t id = server->arg(F("id")).toInt(); + + DEBUG_PRINT(F("Got args: ")); + DEBUG_PRINT(id); + DEBUG_PRINTLN(F("")); + + if (id < 0 || id >= registered_feedbacks) + { + DEBUG_PRINTLN(F("ID wrong")); + goto end; + } + + switch (feedbacks[id].type) + { + case FEEDBACK_TYPE_ACTION: + { + feedback_action_fptr_t func = (feedback_action_fptr_t)feedbacks[id].data; + void *arg = feedbacks[id].options.action_options.arg; + func(arg); + break; + } + default: + DEBUG_PRINTLN(F("Feedback has no action")); + break; + } + } +end: + server->sendHeader(F("Location"),F(__ROOT_PATH)); + server->send(302); +} + +#if !DISABLE_RESTORE_BUTTONS +void ESPKNXIP::__handle_restore() +{ + DEBUG_PRINTLN(F("Restore called")); + memcpy(custom_config_data, custom_config_default_data, MAX_CONFIG_SPACE); +end: + server->sendHeader(F("Location"),F(__ROOT_PATH)); + server->send(302); +} +#endif + +#if !DISABLE_REBOOT_BUTTONS +void ESPKNXIP::__handle_reboot() +{ + DEBUG_PRINTLN(F("Rebooting!")); + server->sendHeader(F("Location"),F(__ROOT_PATH)); + server->send(302); + delay(1000); + ESP.restart(); + //while(1); +} +#endif + +#if !DISABLE_EEPROM_BUTTONS +void ESPKNXIP::__handle_eeprom() +{ + DEBUG_PRINTLN(F("EEPROM called")); + if (server->hasArg(F("mode"))) + { + uint8_t mode = server->arg(F("mode")).toInt(); + + DEBUG_PRINT(F("Got args: ")); + DEBUG_PRINT(mode); + DEBUG_PRINTLN(F("")); + + if (mode == 1) + { + // save + save_to_eeprom(); + } + else if (mode == 2) + { + // restore + restore_from_eeprom(); + } + } +end: + server->sendHeader(F("Location"),F(__ROOT_PATH)); + server->send(302); +} +#endif diff --git a/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp b/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp new file mode 100644 index 000000000..35cbb2161 --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp @@ -0,0 +1,664 @@ +/** + * esp-knx-ip library for KNX/IP communication on an ESP8266 + * Author: Nico Weichbrodt + * License: MIT + */ + +#include "esp-knx-ip.h" + +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"); + // Default physical address is 1.1.0 + physaddr.bytes.high = (/*area*/1 << 4) | /*line*/1; + physaddr.bytes.low = /*member*/0; + memset(callback_assignments, 0, MAX_CALLBACK_ASSIGNMENTS * sizeof(callback_assignment_t)); + memset(callbacks, 0, MAX_CALLBACKS * sizeof(callback_fptr_t)); + memset(custom_config_data, 0, MAX_CONFIG_SPACE * sizeof(uint8_t)); + memset(custom_config_default_data, 0, MAX_CONFIG_SPACE * sizeof(uint8_t)); + memset(custom_configs, 0, MAX_CONFIGS * sizeof(config_t)); +} + +void ESPKNXIP::load() +{ + memcpy(custom_config_default_data, custom_config_data, MAX_CONFIG_SPACE); + EEPROM.begin(EEPROM_SIZE); + restore_from_eeprom(); +} + +void ESPKNXIP::start(ESP8266WebServer *srv) +{ + server = srv; + __start(); +} + +void ESPKNXIP::start() +{ + server = new ESP8266WebServer(80); + __start(); +} + +void ESPKNXIP::__start() +{ + if (server != nullptr) + { + server->on(ROOT_PREFIX, [this](){ + __handle_root(); + }); + server->on(__ROOT_PATH, [this](){ + __handle_root(); + }); + server->on(__REGISTER_PATH, [this](){ + __handle_register(); + }); + server->on(__DELETE_PATH, [this](){ + __handle_delete(); + }); + server->on(__PHYS_PATH, [this](){ + __handle_set(); + }); +#if !DISABLE_EEPROM_BUTTONS + server->on(__EEPROM_PATH, [this](){ + __handle_eeprom(); + }); +#endif + server->on(__CONFIG_PATH, [this](){ + __handle_config(); + }); + server->on(__FEEDBACK_PATH, [this](){ + __handle_feedback(); + }); +#if !DISABLE_RESTORE_BUTTON + server->on(__RESTORE_PATH, [this](){ + __handle_restore(); + }); +#endif +#if !DISABLE_REBOOT_BUTTON + server->on(__REBOOT_PATH, [this](){ + __handle_reboot(); + }); +#endif + server->begin(); + } + udp.beginMulticast(WiFi.localIP(), MULTICAST_IP, MULTICAST_PORT); +} + +void ESPKNXIP::save_to_eeprom() +{ + uint32_t address = 0; + uint64_t magic = EEPROM_MAGIC; + EEPROM.put(address, magic); + address += sizeof(uint64_t); + EEPROM.put(address++, registered_callback_assignments); + for (uint8_t i = 0; i < MAX_CALLBACK_ASSIGNMENTS; ++i) + { + EEPROM.put(address, callback_assignments[i].address); + address += sizeof(address_t); + } + for (uint8_t i = 0; i < MAX_CALLBACK_ASSIGNMENTS; ++i) + { + EEPROM.put(address, callback_assignments[i].callback_id); + address += sizeof(callback_id_t); + } + EEPROM.put(address, physaddr); + address += sizeof(address_t); + + EEPROM.put(address, custom_config_data); + address += sizeof(custom_config_data); + + EEPROM.commit(); + DEBUG_PRINT("Wrote to EEPROM: 0x"); + DEBUG_PRINTLN(address, HEX); +} + +void ESPKNXIP::restore_from_eeprom() +{ + uint32_t address = 0; + uint64_t magic = 0; + EEPROM.get(address, magic); + if (magic != EEPROM_MAGIC) + { + DEBUG_PRINTLN("No valid magic in EEPROM, aborting restore."); + DEBUG_PRINT("Expected 0x"); + DEBUG_PRINT((unsigned long)(EEPROM_MAGIC >> 32), HEX); + DEBUG_PRINT(" 0x"); + DEBUG_PRINT((unsigned long)(EEPROM_MAGIC), HEX); + DEBUG_PRINT(" got 0x"); + DEBUG_PRINT((unsigned long)(magic >> 32), HEX); + DEBUG_PRINT(" 0x"); + DEBUG_PRINTLN((unsigned long)magic, HEX); + return; + } + address += sizeof(uint64_t); + EEPROM.get(address++, registered_callback_assignments); + 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) + { + EEPROM.get(address, callback_assignments[i].callback_id); + address += sizeof(callback_id_t); + } + EEPROM.get(address, physaddr); + address += sizeof(address_t); + + //EEPROM.get(address, custom_config_data); + //address += sizeof(custom_config_data); + uint32_t conf_offset = address; + for (uint8_t i = 0; i < registered_configs; ++i) + { + // First byte is flags. + config_flags_t flags = CONFIG_FLAGS_NO_FLAGS; + flags = (config_flags_t)EEPROM.read(address); + DEBUG_PRINT("Flag in EEPROM @ "); + DEBUG_PRINT(address - conf_offset); + DEBUG_PRINT(": "); + DEBUG_PRINTLN(flags, BIN); + custom_config_data[custom_configs[i].offset] = flags; + if (flags & CONFIG_FLAGS_VALUE_SET) + { + DEBUG_PRINTLN("Non-default value"); + for (int j = 0; j < custom_configs[i].len - sizeof(uint8_t); ++j) + { + custom_config_data[custom_configs[i].offset + sizeof(uint8_t) + j] = EEPROM.read(address + sizeof(uint8_t) + j); + } + } + + address += custom_configs[i].len; + } + + DEBUG_PRINT("Restored from EEPROM: 0x"); + DEBUG_PRINTLN(address, HEX); +} + +uint16_t ESPKNXIP::__ntohs(uint16_t n) +{ + return (uint16_t)((((uint8_t*)&n)[0] << 8) | (((uint8_t*)&n)[1])); +} + +callback_assignment_id_t ESPKNXIP::__callback_register_assignment(address_t address, callback_id_t id) +{ + 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++; + return aid; + } + else + { + // 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; + } + } + return -1; +} + +void ESPKNXIP::__callback_delete_assignment(callback_assignment_id_t id) +{ + // TODO this can be optimized if we are deleting the last element + // as then we can decrement registered_callback_assignments + + // 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) + { + DEBUG_PRINTLN("really last cba"); + // If this was the last, then we are done. + return; + } + + id--; + while(true) + { + 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 + { + DEBUG_PRINTLN("aborted on used slot"); + // Slot is used, abort + return; + } + id--; + if (id == CALLBACK_ASSIGNMENT_ID_MAX) + { + 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++; + } +} + +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) +{ + 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++; + return id; + } + else + { + // find the free slot + for (callback_id_t id = 0; id < registered_callbacks; ++id) + { + if (callbacks[id].slot_flags & SLOT_FLAGS_USED) + { + // 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; + } + } + return -1; +} + +void ESPKNXIP::callback_deregister(callback_id_t id) +{ + if (!__callback_is_id_valid(id)) + return; + + // 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); +} + +/** + * Feedback functions start here + */ + +feedback_id_t ESPKNXIP::feedback_register_int(String name, int32_t *value, enable_condition_t cond) +{ + if (registered_feedbacks >= MAX_FEEDBACKS) + return -1; + + feedback_id_t id = registered_feedbacks; + + feedbacks[id].type = FEEDBACK_TYPE_INT; + feedbacks[id].name = name; + feedbacks[id].cond = cond; + feedbacks[id].data = (void *)value; + + registered_feedbacks++; + + return id; +} + +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; + + feedback_id_t id = registered_feedbacks; + + feedbacks[id].type = FEEDBACK_TYPE_FLOAT; + feedbacks[id].name = name; + 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, char const *true_text, char const *false_text, enable_condition_t cond) +{ + if (registered_feedbacks >= MAX_FEEDBACKS) + return -1; + + feedback_id_t id = registered_feedbacks; + + feedbacks[id].type = FEEDBACK_TYPE_BOOL; + 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, const char *btn_text, void *arg, enable_condition_t cond) +{ + if (registered_feedbacks >= MAX_FEEDBACKS) + return -1; + + feedback_id_t id = registered_feedbacks; + + feedbacks[id].type = FEEDBACK_TYPE_ACTION; + feedbacks[id].name = name; + 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++; + + return id; +} + +void ESPKNXIP::loop() +{ + __loop_knx(); + if (server != nullptr) + { + __loop_webserver(); + } +} + +void ESPKNXIP::__loop_webserver() +{ + server->handleClient(); +} + +void ESPKNXIP::__loop_knx() +{ + int read = udp.parsePacket(); + + if (!read) + { + return; + } + DEBUG_PRINTLN(F("")); + DEBUG_PRINT(F("LEN: ")); + DEBUG_PRINTLN(read); + + uint8_t buf[read]; + udp.read(buf, read); + + DEBUG_PRINT(F("Got packet:")); + +#ifdef ESP_KNX_DEBUG + + for (int i = 0; i < read; ++i) + + { + DEBUG_PRINT(F(" 0x")); + DEBUG_PRINT(buf[i], 16); + } + +#endif + + DEBUG_PRINTLN(F("")); + + knx_ip_pkt_t *knx_pkt = (knx_ip_pkt_t *)buf; + + DEBUG_PRINT(F("ST: 0x")); + DEBUG_PRINTLN(__ntohs(knx_pkt->service_type), 16); + + if (knx_pkt->header_len != 0x06 && knx_pkt->protocol_version != 0x10 && knx_pkt->service_type != KNX_ST_ROUTING_INDICATION) + return; + + cemi_msg_t *cemi_msg = (cemi_msg_t *)knx_pkt->pkt_data; + + DEBUG_PRINT(F("MT: 0x")); + DEBUG_PRINTLN(cemi_msg->message_code, 16); + + if (cemi_msg->message_code != KNX_MT_L_DATA_IND) + return; + + DEBUG_PRINT(F("ADDI: 0x")); + DEBUG_PRINTLN(cemi_msg->additional_info_len, 16); + + cemi_service_t *cemi_data = &cemi_msg->data.service_information; + + if (cemi_msg->additional_info_len > 0) + cemi_data = (cemi_service_t *)(((uint8_t *)cemi_data) + cemi_msg->additional_info_len); + + DEBUG_PRINT(F("C1: 0x")); + DEBUG_PRINTLN(cemi_data->control_1.byte, 16); + + DEBUG_PRINT(F("C2: 0x")); + DEBUG_PRINTLN(cemi_data->control_2.byte, 16); + + DEBUG_PRINT(F("DT: 0x")); + DEBUG_PRINTLN(cemi_data->control_2.bits.dest_addr_type, 16); + + if (cemi_data->control_2.bits.dest_addr_type != 0x01) + return; + + DEBUG_PRINT(F("HC: 0x")); + DEBUG_PRINTLN(cemi_data->control_2.bits.hop_count, 16); + + DEBUG_PRINT(F("EFF: 0x")); + DEBUG_PRINTLN(cemi_data->control_2.bits.extended_frame_format, 16); + + DEBUG_PRINT(F("Source: 0x")); + DEBUG_PRINT(cemi_data->source.bytes.high, 16); + DEBUG_PRINT(F(" 0x")); + DEBUG_PRINTLN(cemi_data->source.bytes.low, 16); + + DEBUG_PRINT(F("Dest: 0x")); + DEBUG_PRINT(cemi_data->destination.bytes.high, 16); + DEBUG_PRINT(F(" 0x")); + DEBUG_PRINTLN(cemi_data->destination.bytes.low, 16); + + knx_command_type_t ct = (knx_command_type_t)(((cemi_data->data[0] & 0xC0) >> 6) | ((cemi_data->pci.apci & 0x03) << 2)); + + DEBUG_PRINT(F("CT: 0x")); + DEBUG_PRINTLN(ct, 16); + +#ifdef ESP_KNX_DEBUG + for (int i = 0; i < cemi_data->data_len; ++i) + { + DEBUG_PRINT(F(" 0x")); + DEBUG_PRINT(cemi_data->data[i], 16); + } +#endif + + DEBUG_PRINTLN(F("==")); + + // Call callbacks + for (int i = 0; i < registered_callback_assignments; ++i) + { + DEBUG_PRINT(F("Testing: 0x")); + DEBUG_PRINT(callback_assignments[i].address.bytes.high, 16); + DEBUG_PRINT(F(" 0x")); + DEBUG_PRINTLN(callback_assignments[i].address.bytes.low, 16); + if (cemi_data->destination.value == callback_assignments[i].address.value) + { + DEBUG_PRINTLN(F("Found match")); + if (callbacks[callback_assignments[i].callback_id].cond && !callbacks[callback_assignments[i].callback_id].cond()) + { + DEBUG_PRINTLN(F("But it's disabled")); +#if ALLOW_MULTIPLE_CALLBACKS_PER_ADDRESS + continue; +#else + return; +#endif + } + uint8_t data[cemi_data->data_len]; + memcpy(data, cemi_data->data, cemi_data->data_len); + data[0] = data[0] & 0x3F; + message_t msg = {}; + msg.ct = ct; + msg.received_on = cemi_data->destination; + msg.data_len = cemi_data->data_len; + msg.data = data; + callbacks[callback_assignments[i].callback_id].fkt(msg, callbacks[callback_assignments[i].callback_id].arg); +#if ALLOW_MULTIPLE_CALLBACKS_PER_ADDRESS + continue; +#else + return; +#endif + } + } + + return; +} + +// Global "singleton" object +ESPKNXIP knx; diff --git a/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.h b/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.h new file mode 100644 index 000000000..6834a6125 --- /dev/null +++ b/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.h @@ -0,0 +1,600 @@ +/** + * esp-knx-ip library for KNX/IP communication on an ESP8266 + * Author: Nico Weichbrodt + * License: MIT + */ + +#ifndef ESP_KNX_IP_H +#define ESP_KNX_IP_H + +/** + * CONFIG + * All MAX_ values must not exceed 255 (1 byte, except MAC_CONFIG_SPACE which can go up to 2 bytes, so 0xffff in theory) and must not be negative! + * Config space is restriced by EEPROM_SIZE (default 1024). + * Required EEPROM size is 8 + MAX_GA_CALLBACKS * 3 + 2 + MAX_CONFIG_SPACE which is 552 by default + */ +#define EEPROM_SIZE 1024 // [Default 1024] +#define MAX_CALLBACK_ASSIGNMENTS 10 // [Default 10] Maximum number of group address callbacks that can be stored +#define MAX_CALLBACKS 10 // [Default 10] Maximum number of callbacks that can be stored +#define MAX_CONFIGS 20 // [Default 20] Maximum number of config items that can be stored +#define MAX_CONFIG_SPACE 0x0200 // [Default 0x0200] Maximum number of bytes that can be stored for custom config + +#define MAX_FEEDBACKS 20 // [Default 20] Maximum number of feedbacks that can be shown + +// Callbacks +#define ALLOW_MULTIPLE_CALLBACKS_PER_ADDRESS 1 // [Default 0] Set to 1 to always test all assigned callbacks. This allows for multiple callbacks being assigned to the same address. If disabled, only the first assigned will be called. + +// Webserver related +#define USE_BOOTSTRAP 0 // [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:///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. + +// These values normally don't need adjustment +#ifndef MULTICAST_IP +#define MULTICAST_IP IPAddress(224, 0, 23, 12) // [Default IPAddress(224, 0, 23, 12)] +#else +#warning USING CUSTOM MULTICAST_IP +#endif + +#ifndef MULTICAST_PORT +#define MULTICAST_PORT 3671 // [Default 3671] +#else +#warning USING CUSTOM MULTICAST_PORT +#endif + +#define SEND_CHECKSUM 0 + +// Uncomment to enable printing out debug messages. +//#define ESP_KNX_DEBUG +/** + * END CONFIG + */ + +#include "Arduino.h" +#include +#include +#include +#include + +#include "DPT.h" + +#define EEPROM_MAGIC (0xDEADBEEF00000000 + (MAX_CONFIG_SPACE) + (MAX_CALLBACK_ASSIGNMENTS << 16) + (MAX_CALLBACKS << 8)) + +// Define where debug output will be printed. +#ifndef DEBUG_PRINTER +#define DEBUG_PRINTER Serial +#endif + +// Setup debug printing macros. +#ifdef ESP_KNX_DEBUG + #define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); } + #define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); } +#else + #define DEBUG_PRINT(...) {} + #define DEBUG_PRINTLN(...) {} +#endif + +#define __ROOT_PATH ROOT_PREFIX"/" +#define __REGISTER_PATH ROOT_PREFIX"/register" +#define __DELETE_PATH ROOT_PREFIX"/delete" +#define __PHYS_PATH ROOT_PREFIX"/phys" +#define __EEPROM_PATH ROOT_PREFIX"/eeprom" +#define __CONFIG_PATH ROOT_PREFIX"/config" +#define __FEEDBACK_PATH ROOT_PREFIX"/feedback" +#define __RESTORE_PATH ROOT_PREFIX"/restore" +#define __REBOOT_PATH ROOT_PREFIX"/reboot" + +/** + * Different service types, we are mainly interested in KNX_ST_ROUTING_INDICATION + */ +typedef enum __knx_service_type +{ + KNX_ST_SEARCH_REQUEST = 0x0201, + KNX_ST_SEARCH_RESPONSE = 0x0202, + KNX_ST_DESCRIPTION_REQUEST = 0x0203, + KNX_ST_DESCRIPTION_RESPONSE = 0x0204, + KNX_ST_CONNECT_REQUEST = 0x0205, + KNX_ST_CONNECT_RESPONSE = 0x0206, + KNX_ST_CONNECTIONSTATE_REQUEST = 0x0207, + KNX_ST_CONNECTIONSTATE_RESPONSE = 0x0208, + KNX_ST_DISCONNECT_REQUEST = 0x0209, + KNX_ST_DISCONNECT_RESPONSE = 0x020A, + + KNX_ST_DEVICE_CONFIGURATION_REQUEST = 0x0310, + KNX_ST_DEVICE_CONFIGURATION_ACK = 0x0311, + + KNX_ST_TUNNELING_REQUEST = 0x0420, + KNX_ST_TUNNELING_ACK = 0x0421, + + KNX_ST_ROUTING_INDICATION = 0x0530, + KNX_ST_ROUTING_LOST_MESSAGE = 0x0531, + KNX_ST_ROUTING_BUSY = 0x0532, + +// KNX_ST_RLOG_START = 0x0600, +// KNX_ST_RLOG_END = 0x06FF, + + KNX_ST_REMOTE_DIAGNOSTIC_REQUEST = 0x0740, + KNX_ST_REMOTE_DIAGNOSTIC_RESPONSE = 0x0741, + KNX_ST_REMOTE_BASIC_CONFIGURATION_REQUEST = 0x0742, + KNX_ST_REMOTE_RESET_REQUEST = 0x0743, + +// KNX_ST_OBJSRV_START = 0x0800, +// KNX_ST_OBJSRV_END = 0x08FF, +} knx_service_type_t; + +/** + * Differnt command types, first three are of main interest + */ +typedef enum __knx_command_type +{ + KNX_CT_READ = 0x00, + KNX_CT_ANSWER = 0x01, + KNX_CT_WRITE = 0x02, + KNX_CT_INDIVIDUAL_ADDR_WRITE = 0x03, + KNX_CT_INDIVIDUAL_ADDR_REQUEST = 0x04, + KNX_CT_INDIVIDUAL_ADDR_RESPONSE = 0x05, + KNX_CT_ADC_READ = 0x06, + KNX_CT_ADC_ANSWER = 0x07, + KNX_CT_MEM_READ = 0x08, + KNX_CT_MEM_ANSWER = 0x09, + KNX_CT_MEM_WRITE = 0x0A, +//KNX_CT_UNKNOWN = 0x0B, + KNX_CT_MASK_VERSION_READ = 0x0C, + KNX_CT_MASK_VERSION_RESPONSE = 0x0D, + KNX_CT_RESTART = 0x0E, + KNX_CT_ESCAPE = 0x0F, +} knx_command_type_t; + +/** + * cEMI message types, mainly KNX_MT_L_DATA_IND is interesting + */ +typedef enum __knx_cemi_msg_type +{ + KNX_MT_L_DATA_REQ = 0x11, + KNX_MT_L_DATA_IND = 0x29, + KNX_MT_L_DATA_CON = 0x2E, +} knx_cemi_msg_type_t; + +/** + * TCPI communication type + */ +typedef enum __knx_communication_type { + KNX_COT_UDP = 0x00, // Unnumbered Data Packet + KNX_COT_NDP = 0x01, // Numbered Data Packet + KNX_COT_UCD = 0x02, // Unnumbered Control Data + KNX_COT_NCD = 0x03, // Numbered Control Data +} knx_communication_type_t; + +/** + * acpi for KNX_COT_NCD + */ +typedef enum __knx_cot_ncd_ack_type { + KNX_COT_NCD_ACK = 0x10, // Inform positively reception of the Previouly received telegram + KNX_COT_NCD_NACK = 0x11, // Inform negatively reception of the Previouly received telegram +} knx_cot_ncd_ack_type_t; + +/** + * KNX/IP header + */ +typedef struct __knx_ip_pkt +{ + uint8_t header_len; // Should always be 0x06 + uint8_t protocol_version; // Should be version 1.0, transmitted as 0x10 + uint16_t service_type; // See knx_service_type_t + union + { + struct { + uint8_t first_byte; + uint8_t second_byte; + } bytes; + uint16_t len; + } total_len; // header_len + rest of pkt. This is a bit weird as the spec says this: If the total number of bytes transmitted is greater than 252 bytes, the first “Total Length” byte is set to FF (255). Only in this case the second byte includes additional length information + uint8_t pkt_data[]; // This is of type cemi_msg_t +} knx_ip_pkt_t; + +typedef struct __cemi_addi +{ + uint8_t type_id; + uint8_t len; + uint8_t data[]; +} cemi_addi_t; + +typedef union __address +{ + uint16_t value; + struct + { + uint8_t high; + uint8_t low; + } bytes; + struct __attribute__((packed)) + { + uint8_t line:3; + uint8_t area:5; + uint8_t member; + } ga; + struct __attribute__((packed)) + { + uint8_t line:4; + uint8_t area:4; + uint8_t member; + } pa; + uint8_t array[2]; +} address_t; + +typedef struct __cemi_service +{ + union + { + struct + { + // Struct is reversed due to bit order + uint8_t confirm:1; // 0 = no error, 1 = error + uint8_t ack:1; // 0 = no ack, 1 = ack + uint8_t priority:2; // 0 = system, 1 = high, 2 = urgent/alarm, 3 = normal + uint8_t system_broadcast:1; // 0 = system broadcast, 1 = broadcast + uint8_t repeat:1; // 0 = repeated telegram, 1 = not repeated telegram + uint8_t reserved:1; // always zero + uint8_t frame_type:1; // 0 = extended, 1 = standard + } bits; + uint8_t byte; + } control_1; + union + { + struct + { + // Struct is reversed due to bit order + uint8_t extended_frame_format:4; + uint8_t hop_count:3; + uint8_t dest_addr_type:1; // 0 = individual, 1 = group + } bits; + uint8_t byte; + } control_2; + address_t source; + address_t destination; + uint8_t data_len; // length of data, excluding the tpci byte + struct + { + uint8_t apci:2; // If tpci.comm_type == KNX_COT_UCD or KNX_COT_NCD, then this is apparently control data? + uint8_t tpci_seq_number:4; + uint8_t tpci_comm_type:2; // See knx_communication_type_t + } pci; + uint8_t data[]; +} cemi_service_t; + +typedef struct __cemi_msg +{ + uint8_t message_code; + uint8_t additional_info_len; + union + { +// cemi_addi_t additional_info[]; // Errors in GCC 10.1 + cemi_addi_t additional_info[10]; // Changed to arbitrary number to fix compilation + cemi_service_t service_information; + } data; +} cemi_msg_t; + +typedef enum __config_type +{ + CONFIG_TYPE_UNKNOWN, + CONFIG_TYPE_INT, + CONFIG_TYPE_BOOL, + CONFIG_TYPE_STRING, + CONFIG_TYPE_OPTIONS, + CONFIG_TYPE_GA, +} config_type_t; + +typedef enum __feedback_type +{ + FEEDBACK_TYPE_UNKNOWN, + FEEDBACK_TYPE_INT, + FEEDBACK_TYPE_FLOAT, + FEEDBACK_TYPE_BOOL, + FEEDBACK_TYPE_ACTION, +} feedback_type_t; + +typedef enum __config_flags +{ + CONFIG_FLAGS_NO_FLAGS = 0, + 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; + address_t received_on; + uint8_t data_len; + uint8_t *data; +} message_t; + +typedef bool (*enable_condition_t)(void); +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 const *name; + uint8_t value; +} option_entry_t; + +typedef struct __config +{ + config_type_t type; + String name; + uint8_t offset; + uint8_t len; + enable_condition_t cond; + union { + option_entry_t *options; + } 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 +{ + feedback_type_t type; + String name; + 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; +} feedback_t; + +typedef struct __callback +{ + uint8_t slot_flags; + callback_fptr_t fkt; + enable_condition_t cond; + void *arg; + String name; +} callback_t; + +typedef struct __callback_assignment +{ + uint8_t slot_flags; + address_t address; + callback_id_t callback_id; +} callback_assignment_t; + +// FastPrecisePowf from tasmota/support_float.ino +//extern float FastPrecisePowf(const float x, const float y); + +class ESPKNXIP { + public: + ESPKNXIP(); + void load(); + void start(); + 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); + 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(); + + // Configuration functions + config_id_t config_register_string(String name, uint8_t len, String _default, enable_condition_t cond = nullptr); + config_id_t config_register_int(String name, int32_t _default, enable_condition_t cond = nullptr); + config_id_t config_register_bool(String name, bool _default, enable_condition_t cond = nullptr); + config_id_t config_register_options(String name, option_entry_t *options, uint8_t _default, enable_condition_t cond = nullptr); + config_id_t config_register_ga(String name, enable_condition_t cond = nullptr); + + String config_get_string(config_id_t id); + int32_t config_get_int(config_id_t id); + bool config_get_bool(config_id_t id); + uint8_t config_get_options(config_id_t id); + address_t config_get_ga(config_id_t id); + + void config_set_string(config_id_t id, String val); + void config_set_int(config_id_t id, int32_t val); + void config_set_bool(config_id_t, bool val); + void config_set_options(config_id_t id, uint8_t val); + void config_set_ga(config_id_t id, address_t const &val); + + // 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, 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); + + void send_1bit(address_t const &receiver, knx_command_type_t ct, uint8_t bit); + void send_2bit(address_t const &receiver, knx_command_type_t ct, uint8_t twobit); + void send_4bit(address_t const &receiver, knx_command_type_t ct, uint8_t fourbit); + void send_1byte_int(address_t const &receiver, knx_command_type_t ct, int8_t val); + void send_1byte_uint(address_t const &receiver, knx_command_type_t ct, uint8_t val); + void send_2byte_int(address_t const &receiver, knx_command_type_t ct, int16_t val); + void send_2byte_uint(address_t const &receiver, knx_command_type_t ct, uint16_t val); + void send_2byte_float(address_t const &receiver, knx_command_type_t ct, float val); + void send_3byte_time(address_t const &receiver, knx_command_type_t ct, uint8_t weekday, uint8_t hours, uint8_t minutes, uint8_t seconds); + void send_3byte_time(address_t const &receiver, knx_command_type_t ct, time_of_day_t const &time) { send_3byte_time(receiver, ct, time.weekday, time.hours, time.minutes, time.seconds); } + void send_3byte_date(address_t const &receiver, knx_command_type_t ct, uint8_t day, uint8_t month, uint8_t year); + void send_3byte_date(address_t const &receiver, knx_command_type_t ct, date_t const &date) { send_3byte_date(receiver, ct, date.day, date.month, date.year); } + void send_3byte_color(address_t const &receiver, knx_command_type_t ct, uint8_t red, uint8_t green, uint8_t blue); + void send_3byte_color(address_t const &receiver, knx_command_type_t ct, color_t const &color) { send_3byte_color(receiver, ct, color.red, color.green, color.blue); } + void send_4byte_int(address_t const &receiver, knx_command_type_t ct, int32_t val); + void send_4byte_uint(address_t const &receiver, knx_command_type_t ct, uint32_t val); + void send_4byte_float(address_t const &receiver, knx_command_type_t ct, float val); + void send_14byte_string(address_t const &receiver, knx_command_type_t ct, const char *val); + + void write_1bit(address_t const &receiver, uint8_t bit) { send_1bit(receiver, KNX_CT_WRITE, bit); } + void write_2bit(address_t const &receiver, uint8_t twobit) { send_2bit(receiver, KNX_CT_WRITE, twobit); } + void write_4bit(address_t const &receiver, uint8_t fourbit) { send_4bit(receiver, KNX_CT_WRITE, fourbit); } + void write_1byte_int(address_t const &receiver, int8_t val) { send_1byte_int(receiver, KNX_CT_WRITE, val); } + void write_1byte_uint(address_t const &receiver, uint8_t val) { send_1byte_uint(receiver, KNX_CT_WRITE, val); } + void write_2byte_int(address_t const &receiver, int16_t val) { send_2byte_int(receiver, KNX_CT_WRITE, val); } + void write_2byte_uint(address_t const &receiver, uint16_t val) { send_2byte_uint(receiver, KNX_CT_WRITE, val); } + void write_2byte_float(address_t const &receiver, float val) { send_2byte_float(receiver, KNX_CT_WRITE, val); } + void write_3byte_time(address_t const &receiver, uint8_t weekday, uint8_t hours, uint8_t minutes, uint8_t seconds) { send_3byte_time(receiver, KNX_CT_WRITE, weekday, hours, minutes, seconds); } + void write_3byte_time(address_t const &receiver, time_of_day_t const &time) { send_3byte_time(receiver, KNX_CT_WRITE, time.weekday, time.hours, time.minutes, time.seconds); } + void write_3byte_date(address_t const &receiver, uint8_t day, uint8_t month, uint8_t year) { send_3byte_date(receiver, KNX_CT_WRITE, day, month, year); } + void write_3byte_date(address_t const &receiver, date_t const &date) { send_3byte_date(receiver, KNX_CT_WRITE, date.day, date.month, date.year); } + void write_3byte_color(address_t const &receiver, uint8_t red, uint8_t green, uint8_t blue) { send_3byte_color(receiver, KNX_CT_WRITE, red, green, blue); } + void write_3byte_color(address_t const &receiver, color_t const &color) { send_3byte_color(receiver, KNX_CT_WRITE, color); } + void write_4byte_int(address_t const &receiver, int32_t val) { send_4byte_int(receiver, KNX_CT_WRITE, val); } + void write_4byte_uint(address_t const &receiver, uint32_t val) { send_4byte_uint(receiver, KNX_CT_WRITE, val); } + void write_4byte_float(address_t const &receiver, float val) { send_4byte_float(receiver, KNX_CT_WRITE, val);} + void write_14byte_string(address_t const &receiver, const char *val) { send_14byte_string(receiver, KNX_CT_WRITE, val); } + + void answer_1bit(address_t const &receiver, uint8_t bit) { send_1bit(receiver, KNX_CT_ANSWER, bit); } + void answer_2bit(address_t const &receiver, uint8_t twobit) { send_2bit(receiver, KNX_CT_ANSWER, twobit); } + void answer_4bit(address_t const &receiver, uint8_t fourbit) { send_4bit(receiver, KNX_CT_ANSWER, fourbit); } + void answer_1byte_int(address_t const &receiver, int8_t val) { send_1byte_int(receiver, KNX_CT_ANSWER, val); } + void answer_1byte_uint(address_t const &receiver, uint8_t val) { send_1byte_uint(receiver, KNX_CT_ANSWER, val); } + void answer_2byte_int(address_t const &receiver, int16_t val) { send_2byte_int(receiver, KNX_CT_ANSWER, val); } + void answer_2byte_uint(address_t const &receiver, uint16_t val) { send_2byte_uint(receiver, KNX_CT_ANSWER, val); } + void answer_2byte_float(address_t const &receiver, float val) { send_2byte_float(receiver, KNX_CT_ANSWER, val); } + void answer_3byte_time(address_t const &receiver, uint8_t weekday, uint8_t hours, uint8_t minutes, uint8_t seconds) { send_3byte_time(receiver, KNX_CT_ANSWER, weekday, hours, minutes, seconds); } + void answer_3byte_time(address_t const &receiver, time_of_day_t const &time) { send_3byte_time(receiver, KNX_CT_ANSWER, time.weekday, time.hours, time.minutes, time.seconds); } + void answer_3byte_date(address_t const &receiver, uint8_t day, uint8_t month, uint8_t year) { send_3byte_date(receiver, KNX_CT_ANSWER, day, month, year); } + void answer_3byte_date(address_t const &receiver, date_t const &date) { send_3byte_date(receiver, KNX_CT_ANSWER, date.day, date.month, date.year); } + void answer_3byte_color(address_t const &receiver, uint8_t red, uint8_t green, uint8_t blue) { send_3byte_color(receiver, KNX_CT_ANSWER, red, green, blue); } + void answer_3byte_color(address_t const &receiver, color_t const &color) { send_3byte_color(receiver, KNX_CT_ANSWER, color); } + void answer_4byte_int(address_t const &receiver, int32_t val) { send_4byte_int(receiver, KNX_CT_ANSWER, val); } + void answer_4byte_uint(address_t const &receiver, uint32_t val) { send_4byte_uint(receiver, KNX_CT_ANSWER, val); } + void answer_4byte_float(address_t const &receiver, float val) { send_4byte_float(receiver, KNX_CT_ANSWER, val);} + void answer_14byte_string(address_t const &receiver, const char *val) { send_14byte_string(receiver, KNX_CT_ANSWER, val); } + + bool data_to_bool(uint8_t *data); + int8_t data_to_1byte_int(uint8_t *data); + uint8_t data_to_1byte_uint(uint8_t *data); + int16_t data_to_2byte_int(uint8_t *data); + uint16_t data_to_2byte_uint(uint8_t *data); + float data_to_2byte_float(uint8_t *data); + color_t data_to_3byte_color(uint8_t *data); + time_of_day_t data_to_3byte_time(uint8_t *data); + date_t data_to_3byte_data(uint8_t *data); + int32_t data_to_4byte_int(uint8_t *data); + uint32_t data_to_4byte_uint(uint8_t *data); + float data_to_4byte_float(uint8_t *data); + + static address_t GA_to_address(uint8_t area, uint8_t line, uint8_t member) + { + // Yes, the order is correct, see the struct definition above + address_t tmp = {.ga={line, area, member}}; + return tmp; + } + + static address_t PA_to_address(uint8_t area, uint8_t line, uint8_t member) + { + // Yes, the order is correct, see the struct definition above + address_t tmp = {.pa={line, area, member}}; + return tmp; + } + + private: + void __start(); + + void __loop_knx(); + + // Webserver functions + void __loop_webserver(); + void __handle_root(); + void __handle_register(); + void __handle_delete(); + void __handle_set(); +#if !DISABLE_EEPROM_BUTTONS + void __handle_eeprom(); +#endif + void __handle_config(); + void __handle_feedback(); +#if !DISABLE_RESTORE_BUTTONS + void __handle_restore(); +#endif +#if !DISABLE_REBOOT_BUTTONS + void __handle_reboot(); +#endif + + void __config_set_flags(config_id_t id, config_flags_t flags); + + void __config_set_string(config_id_t id, String &val); + void __config_set_int(config_id_t id, int32_t val); + void __config_set_bool(config_id_t id, bool val); + 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); + + //static inline float pow(float a, float b) { return FastPrecisePowf(a, b); } + + ESP8266WebServer *server; + address_t physaddr; + + WiFiUDP 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; + uint8_t custom_config_data[MAX_CONFIG_SPACE]; + uint8_t custom_config_default_data[MAX_CONFIG_SPACE]; + config_t custom_configs[MAX_CONFIGS]; + + feedback_id_t registered_feedbacks; + feedback_t feedbacks[MAX_FEEDBACKS]; + + uint16_t __ntohs(uint16_t); +}; + +// Global "singleton" object +extern ESPKNXIP knx; + +#endif From 7f95a51f0ada4e0aab5bd484eea837854a02e86f Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 26 Oct 2020 11:32:31 +0100 Subject: [PATCH 3/7] Add files via upload --- lib/headers/DPT.h | 73 +++++ lib/headers/esp-knx-ip.h | 600 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 673 insertions(+) create mode 100644 lib/headers/DPT.h create mode 100644 lib/headers/esp-knx-ip.h diff --git a/lib/headers/DPT.h b/lib/headers/DPT.h new file mode 100644 index 000000000..3529d51af --- /dev/null +++ b/lib/headers/DPT.h @@ -0,0 +1,73 @@ +/** + * esp-knx-ip library for KNX/IP communication on an ESP8266 + * Author: Nico Weichbrodt + * License: MIT + */ + +typedef enum __dpt_1_001 +{ + DPT_1_001_OFF = 0x00, + DPT_1_001_ON = 0x01, +} dpt_1_001_t; + +typedef enum __dpt_2_001 +{ + DPT_2_001_NO_OFF = 0b00, + DPT_2_001_NO_ON = 0b01, + DPT_2_001_YES_OFF = 0b10, + DPT_2_001_YES_ON = 0b11, +} dpt_2_001_t; + +typedef enum __dpt_3_007 +{ + DPT_3_007_DECREASE_STOP = 0x00, + DPT_3_007_DECREASE_100 = 0x01, + DPT_3_007_DECREASE_50 = 0x02, + DPT_3_007_DECREASE_25 = 0x03, + DPT_3_007_DECREASE_12 = 0x04, + DPT_3_007_DECREASE_6 = 0x05, + DPT_3_007_DECREASE_3 = 0x06, + DPT_3_007_DECREASE_1 = 0x07, + DPT_3_007_INCREASE_STOP = 0x08, + DPT_3_007_INCREASE_100 = 0x09, + DPT_3_007_INCREASE_50 = 0x0A, + DPT_3_007_INCREASE_25 = 0x0B, + DPT_3_007_INCREASE_12 = 0x0C, + DPT_3_007_INCREASE_6 = 0x0D, + DPT_3_007_INCREASE_3 = 0x0E, + DPT_3_007_INCREASE_1 = 0x0F, +} dpt_3_007_t; + +typedef enum __weekday +{ + DPT_10_001_WEEKDAY_NODAY = 0, + DPT_10_001_WEEKDAY_MONDAY = 1, + DPT_10_001_WEEKDAY_TUESDAY = 2, + DPT_10_001_WEEKDAY_WEDNESDAY = 3, + DPT_10_001_WEEKDAY_THURSDAY = 4, + DPT_10_001_WEEKDAY_FRIDAY = 5, + DPT_10_001_WEEKDAY_SATURDAY = 6, + DPT_10_001_WEEKDAY_SUNDAY = 7, +} weekday_t; + +typedef struct __time_of_day +{ + weekday_t weekday; + uint8_t hours; + uint8_t minutes; + uint8_t seconds; +} time_of_day_t; + +typedef struct __date +{ + uint8_t day; + uint8_t month; + uint8_t year; +} date_t; + +typedef struct __color +{ + uint8_t red; + uint8_t green; + uint8_t blue; +} color_t; diff --git a/lib/headers/esp-knx-ip.h b/lib/headers/esp-knx-ip.h new file mode 100644 index 000000000..6834a6125 --- /dev/null +++ b/lib/headers/esp-knx-ip.h @@ -0,0 +1,600 @@ +/** + * esp-knx-ip library for KNX/IP communication on an ESP8266 + * Author: Nico Weichbrodt + * License: MIT + */ + +#ifndef ESP_KNX_IP_H +#define ESP_KNX_IP_H + +/** + * CONFIG + * All MAX_ values must not exceed 255 (1 byte, except MAC_CONFIG_SPACE which can go up to 2 bytes, so 0xffff in theory) and must not be negative! + * Config space is restriced by EEPROM_SIZE (default 1024). + * Required EEPROM size is 8 + MAX_GA_CALLBACKS * 3 + 2 + MAX_CONFIG_SPACE which is 552 by default + */ +#define EEPROM_SIZE 1024 // [Default 1024] +#define MAX_CALLBACK_ASSIGNMENTS 10 // [Default 10] Maximum number of group address callbacks that can be stored +#define MAX_CALLBACKS 10 // [Default 10] Maximum number of callbacks that can be stored +#define MAX_CONFIGS 20 // [Default 20] Maximum number of config items that can be stored +#define MAX_CONFIG_SPACE 0x0200 // [Default 0x0200] Maximum number of bytes that can be stored for custom config + +#define MAX_FEEDBACKS 20 // [Default 20] Maximum number of feedbacks that can be shown + +// Callbacks +#define ALLOW_MULTIPLE_CALLBACKS_PER_ADDRESS 1 // [Default 0] Set to 1 to always test all assigned callbacks. This allows for multiple callbacks being assigned to the same address. If disabled, only the first assigned will be called. + +// Webserver related +#define USE_BOOTSTRAP 0 // [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:///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. + +// These values normally don't need adjustment +#ifndef MULTICAST_IP +#define MULTICAST_IP IPAddress(224, 0, 23, 12) // [Default IPAddress(224, 0, 23, 12)] +#else +#warning USING CUSTOM MULTICAST_IP +#endif + +#ifndef MULTICAST_PORT +#define MULTICAST_PORT 3671 // [Default 3671] +#else +#warning USING CUSTOM MULTICAST_PORT +#endif + +#define SEND_CHECKSUM 0 + +// Uncomment to enable printing out debug messages. +//#define ESP_KNX_DEBUG +/** + * END CONFIG + */ + +#include "Arduino.h" +#include +#include +#include +#include + +#include "DPT.h" + +#define EEPROM_MAGIC (0xDEADBEEF00000000 + (MAX_CONFIG_SPACE) + (MAX_CALLBACK_ASSIGNMENTS << 16) + (MAX_CALLBACKS << 8)) + +// Define where debug output will be printed. +#ifndef DEBUG_PRINTER +#define DEBUG_PRINTER Serial +#endif + +// Setup debug printing macros. +#ifdef ESP_KNX_DEBUG + #define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); } + #define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); } +#else + #define DEBUG_PRINT(...) {} + #define DEBUG_PRINTLN(...) {} +#endif + +#define __ROOT_PATH ROOT_PREFIX"/" +#define __REGISTER_PATH ROOT_PREFIX"/register" +#define __DELETE_PATH ROOT_PREFIX"/delete" +#define __PHYS_PATH ROOT_PREFIX"/phys" +#define __EEPROM_PATH ROOT_PREFIX"/eeprom" +#define __CONFIG_PATH ROOT_PREFIX"/config" +#define __FEEDBACK_PATH ROOT_PREFIX"/feedback" +#define __RESTORE_PATH ROOT_PREFIX"/restore" +#define __REBOOT_PATH ROOT_PREFIX"/reboot" + +/** + * Different service types, we are mainly interested in KNX_ST_ROUTING_INDICATION + */ +typedef enum __knx_service_type +{ + KNX_ST_SEARCH_REQUEST = 0x0201, + KNX_ST_SEARCH_RESPONSE = 0x0202, + KNX_ST_DESCRIPTION_REQUEST = 0x0203, + KNX_ST_DESCRIPTION_RESPONSE = 0x0204, + KNX_ST_CONNECT_REQUEST = 0x0205, + KNX_ST_CONNECT_RESPONSE = 0x0206, + KNX_ST_CONNECTIONSTATE_REQUEST = 0x0207, + KNX_ST_CONNECTIONSTATE_RESPONSE = 0x0208, + KNX_ST_DISCONNECT_REQUEST = 0x0209, + KNX_ST_DISCONNECT_RESPONSE = 0x020A, + + KNX_ST_DEVICE_CONFIGURATION_REQUEST = 0x0310, + KNX_ST_DEVICE_CONFIGURATION_ACK = 0x0311, + + KNX_ST_TUNNELING_REQUEST = 0x0420, + KNX_ST_TUNNELING_ACK = 0x0421, + + KNX_ST_ROUTING_INDICATION = 0x0530, + KNX_ST_ROUTING_LOST_MESSAGE = 0x0531, + KNX_ST_ROUTING_BUSY = 0x0532, + +// KNX_ST_RLOG_START = 0x0600, +// KNX_ST_RLOG_END = 0x06FF, + + KNX_ST_REMOTE_DIAGNOSTIC_REQUEST = 0x0740, + KNX_ST_REMOTE_DIAGNOSTIC_RESPONSE = 0x0741, + KNX_ST_REMOTE_BASIC_CONFIGURATION_REQUEST = 0x0742, + KNX_ST_REMOTE_RESET_REQUEST = 0x0743, + +// KNX_ST_OBJSRV_START = 0x0800, +// KNX_ST_OBJSRV_END = 0x08FF, +} knx_service_type_t; + +/** + * Differnt command types, first three are of main interest + */ +typedef enum __knx_command_type +{ + KNX_CT_READ = 0x00, + KNX_CT_ANSWER = 0x01, + KNX_CT_WRITE = 0x02, + KNX_CT_INDIVIDUAL_ADDR_WRITE = 0x03, + KNX_CT_INDIVIDUAL_ADDR_REQUEST = 0x04, + KNX_CT_INDIVIDUAL_ADDR_RESPONSE = 0x05, + KNX_CT_ADC_READ = 0x06, + KNX_CT_ADC_ANSWER = 0x07, + KNX_CT_MEM_READ = 0x08, + KNX_CT_MEM_ANSWER = 0x09, + KNX_CT_MEM_WRITE = 0x0A, +//KNX_CT_UNKNOWN = 0x0B, + KNX_CT_MASK_VERSION_READ = 0x0C, + KNX_CT_MASK_VERSION_RESPONSE = 0x0D, + KNX_CT_RESTART = 0x0E, + KNX_CT_ESCAPE = 0x0F, +} knx_command_type_t; + +/** + * cEMI message types, mainly KNX_MT_L_DATA_IND is interesting + */ +typedef enum __knx_cemi_msg_type +{ + KNX_MT_L_DATA_REQ = 0x11, + KNX_MT_L_DATA_IND = 0x29, + KNX_MT_L_DATA_CON = 0x2E, +} knx_cemi_msg_type_t; + +/** + * TCPI communication type + */ +typedef enum __knx_communication_type { + KNX_COT_UDP = 0x00, // Unnumbered Data Packet + KNX_COT_NDP = 0x01, // Numbered Data Packet + KNX_COT_UCD = 0x02, // Unnumbered Control Data + KNX_COT_NCD = 0x03, // Numbered Control Data +} knx_communication_type_t; + +/** + * acpi for KNX_COT_NCD + */ +typedef enum __knx_cot_ncd_ack_type { + KNX_COT_NCD_ACK = 0x10, // Inform positively reception of the Previouly received telegram + KNX_COT_NCD_NACK = 0x11, // Inform negatively reception of the Previouly received telegram +} knx_cot_ncd_ack_type_t; + +/** + * KNX/IP header + */ +typedef struct __knx_ip_pkt +{ + uint8_t header_len; // Should always be 0x06 + uint8_t protocol_version; // Should be version 1.0, transmitted as 0x10 + uint16_t service_type; // See knx_service_type_t + union + { + struct { + uint8_t first_byte; + uint8_t second_byte; + } bytes; + uint16_t len; + } total_len; // header_len + rest of pkt. This is a bit weird as the spec says this: If the total number of bytes transmitted is greater than 252 bytes, the first “Total Length” byte is set to FF (255). Only in this case the second byte includes additional length information + uint8_t pkt_data[]; // This is of type cemi_msg_t +} knx_ip_pkt_t; + +typedef struct __cemi_addi +{ + uint8_t type_id; + uint8_t len; + uint8_t data[]; +} cemi_addi_t; + +typedef union __address +{ + uint16_t value; + struct + { + uint8_t high; + uint8_t low; + } bytes; + struct __attribute__((packed)) + { + uint8_t line:3; + uint8_t area:5; + uint8_t member; + } ga; + struct __attribute__((packed)) + { + uint8_t line:4; + uint8_t area:4; + uint8_t member; + } pa; + uint8_t array[2]; +} address_t; + +typedef struct __cemi_service +{ + union + { + struct + { + // Struct is reversed due to bit order + uint8_t confirm:1; // 0 = no error, 1 = error + uint8_t ack:1; // 0 = no ack, 1 = ack + uint8_t priority:2; // 0 = system, 1 = high, 2 = urgent/alarm, 3 = normal + uint8_t system_broadcast:1; // 0 = system broadcast, 1 = broadcast + uint8_t repeat:1; // 0 = repeated telegram, 1 = not repeated telegram + uint8_t reserved:1; // always zero + uint8_t frame_type:1; // 0 = extended, 1 = standard + } bits; + uint8_t byte; + } control_1; + union + { + struct + { + // Struct is reversed due to bit order + uint8_t extended_frame_format:4; + uint8_t hop_count:3; + uint8_t dest_addr_type:1; // 0 = individual, 1 = group + } bits; + uint8_t byte; + } control_2; + address_t source; + address_t destination; + uint8_t data_len; // length of data, excluding the tpci byte + struct + { + uint8_t apci:2; // If tpci.comm_type == KNX_COT_UCD or KNX_COT_NCD, then this is apparently control data? + uint8_t tpci_seq_number:4; + uint8_t tpci_comm_type:2; // See knx_communication_type_t + } pci; + uint8_t data[]; +} cemi_service_t; + +typedef struct __cemi_msg +{ + uint8_t message_code; + uint8_t additional_info_len; + union + { +// cemi_addi_t additional_info[]; // Errors in GCC 10.1 + cemi_addi_t additional_info[10]; // Changed to arbitrary number to fix compilation + cemi_service_t service_information; + } data; +} cemi_msg_t; + +typedef enum __config_type +{ + CONFIG_TYPE_UNKNOWN, + CONFIG_TYPE_INT, + CONFIG_TYPE_BOOL, + CONFIG_TYPE_STRING, + CONFIG_TYPE_OPTIONS, + CONFIG_TYPE_GA, +} config_type_t; + +typedef enum __feedback_type +{ + FEEDBACK_TYPE_UNKNOWN, + FEEDBACK_TYPE_INT, + FEEDBACK_TYPE_FLOAT, + FEEDBACK_TYPE_BOOL, + FEEDBACK_TYPE_ACTION, +} feedback_type_t; + +typedef enum __config_flags +{ + CONFIG_FLAGS_NO_FLAGS = 0, + 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; + address_t received_on; + uint8_t data_len; + uint8_t *data; +} message_t; + +typedef bool (*enable_condition_t)(void); +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 const *name; + uint8_t value; +} option_entry_t; + +typedef struct __config +{ + config_type_t type; + String name; + uint8_t offset; + uint8_t len; + enable_condition_t cond; + union { + option_entry_t *options; + } 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 +{ + feedback_type_t type; + String name; + 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; +} feedback_t; + +typedef struct __callback +{ + uint8_t slot_flags; + callback_fptr_t fkt; + enable_condition_t cond; + void *arg; + String name; +} callback_t; + +typedef struct __callback_assignment +{ + uint8_t slot_flags; + address_t address; + callback_id_t callback_id; +} callback_assignment_t; + +// FastPrecisePowf from tasmota/support_float.ino +//extern float FastPrecisePowf(const float x, const float y); + +class ESPKNXIP { + public: + ESPKNXIP(); + void load(); + void start(); + 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); + 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(); + + // Configuration functions + config_id_t config_register_string(String name, uint8_t len, String _default, enable_condition_t cond = nullptr); + config_id_t config_register_int(String name, int32_t _default, enable_condition_t cond = nullptr); + config_id_t config_register_bool(String name, bool _default, enable_condition_t cond = nullptr); + config_id_t config_register_options(String name, option_entry_t *options, uint8_t _default, enable_condition_t cond = nullptr); + config_id_t config_register_ga(String name, enable_condition_t cond = nullptr); + + String config_get_string(config_id_t id); + int32_t config_get_int(config_id_t id); + bool config_get_bool(config_id_t id); + uint8_t config_get_options(config_id_t id); + address_t config_get_ga(config_id_t id); + + void config_set_string(config_id_t id, String val); + void config_set_int(config_id_t id, int32_t val); + void config_set_bool(config_id_t, bool val); + void config_set_options(config_id_t id, uint8_t val); + void config_set_ga(config_id_t id, address_t const &val); + + // 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, 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); + + void send_1bit(address_t const &receiver, knx_command_type_t ct, uint8_t bit); + void send_2bit(address_t const &receiver, knx_command_type_t ct, uint8_t twobit); + void send_4bit(address_t const &receiver, knx_command_type_t ct, uint8_t fourbit); + void send_1byte_int(address_t const &receiver, knx_command_type_t ct, int8_t val); + void send_1byte_uint(address_t const &receiver, knx_command_type_t ct, uint8_t val); + void send_2byte_int(address_t const &receiver, knx_command_type_t ct, int16_t val); + void send_2byte_uint(address_t const &receiver, knx_command_type_t ct, uint16_t val); + void send_2byte_float(address_t const &receiver, knx_command_type_t ct, float val); + void send_3byte_time(address_t const &receiver, knx_command_type_t ct, uint8_t weekday, uint8_t hours, uint8_t minutes, uint8_t seconds); + void send_3byte_time(address_t const &receiver, knx_command_type_t ct, time_of_day_t const &time) { send_3byte_time(receiver, ct, time.weekday, time.hours, time.minutes, time.seconds); } + void send_3byte_date(address_t const &receiver, knx_command_type_t ct, uint8_t day, uint8_t month, uint8_t year); + void send_3byte_date(address_t const &receiver, knx_command_type_t ct, date_t const &date) { send_3byte_date(receiver, ct, date.day, date.month, date.year); } + void send_3byte_color(address_t const &receiver, knx_command_type_t ct, uint8_t red, uint8_t green, uint8_t blue); + void send_3byte_color(address_t const &receiver, knx_command_type_t ct, color_t const &color) { send_3byte_color(receiver, ct, color.red, color.green, color.blue); } + void send_4byte_int(address_t const &receiver, knx_command_type_t ct, int32_t val); + void send_4byte_uint(address_t const &receiver, knx_command_type_t ct, uint32_t val); + void send_4byte_float(address_t const &receiver, knx_command_type_t ct, float val); + void send_14byte_string(address_t const &receiver, knx_command_type_t ct, const char *val); + + void write_1bit(address_t const &receiver, uint8_t bit) { send_1bit(receiver, KNX_CT_WRITE, bit); } + void write_2bit(address_t const &receiver, uint8_t twobit) { send_2bit(receiver, KNX_CT_WRITE, twobit); } + void write_4bit(address_t const &receiver, uint8_t fourbit) { send_4bit(receiver, KNX_CT_WRITE, fourbit); } + void write_1byte_int(address_t const &receiver, int8_t val) { send_1byte_int(receiver, KNX_CT_WRITE, val); } + void write_1byte_uint(address_t const &receiver, uint8_t val) { send_1byte_uint(receiver, KNX_CT_WRITE, val); } + void write_2byte_int(address_t const &receiver, int16_t val) { send_2byte_int(receiver, KNX_CT_WRITE, val); } + void write_2byte_uint(address_t const &receiver, uint16_t val) { send_2byte_uint(receiver, KNX_CT_WRITE, val); } + void write_2byte_float(address_t const &receiver, float val) { send_2byte_float(receiver, KNX_CT_WRITE, val); } + void write_3byte_time(address_t const &receiver, uint8_t weekday, uint8_t hours, uint8_t minutes, uint8_t seconds) { send_3byte_time(receiver, KNX_CT_WRITE, weekday, hours, minutes, seconds); } + void write_3byte_time(address_t const &receiver, time_of_day_t const &time) { send_3byte_time(receiver, KNX_CT_WRITE, time.weekday, time.hours, time.minutes, time.seconds); } + void write_3byte_date(address_t const &receiver, uint8_t day, uint8_t month, uint8_t year) { send_3byte_date(receiver, KNX_CT_WRITE, day, month, year); } + void write_3byte_date(address_t const &receiver, date_t const &date) { send_3byte_date(receiver, KNX_CT_WRITE, date.day, date.month, date.year); } + void write_3byte_color(address_t const &receiver, uint8_t red, uint8_t green, uint8_t blue) { send_3byte_color(receiver, KNX_CT_WRITE, red, green, blue); } + void write_3byte_color(address_t const &receiver, color_t const &color) { send_3byte_color(receiver, KNX_CT_WRITE, color); } + void write_4byte_int(address_t const &receiver, int32_t val) { send_4byte_int(receiver, KNX_CT_WRITE, val); } + void write_4byte_uint(address_t const &receiver, uint32_t val) { send_4byte_uint(receiver, KNX_CT_WRITE, val); } + void write_4byte_float(address_t const &receiver, float val) { send_4byte_float(receiver, KNX_CT_WRITE, val);} + void write_14byte_string(address_t const &receiver, const char *val) { send_14byte_string(receiver, KNX_CT_WRITE, val); } + + void answer_1bit(address_t const &receiver, uint8_t bit) { send_1bit(receiver, KNX_CT_ANSWER, bit); } + void answer_2bit(address_t const &receiver, uint8_t twobit) { send_2bit(receiver, KNX_CT_ANSWER, twobit); } + void answer_4bit(address_t const &receiver, uint8_t fourbit) { send_4bit(receiver, KNX_CT_ANSWER, fourbit); } + void answer_1byte_int(address_t const &receiver, int8_t val) { send_1byte_int(receiver, KNX_CT_ANSWER, val); } + void answer_1byte_uint(address_t const &receiver, uint8_t val) { send_1byte_uint(receiver, KNX_CT_ANSWER, val); } + void answer_2byte_int(address_t const &receiver, int16_t val) { send_2byte_int(receiver, KNX_CT_ANSWER, val); } + void answer_2byte_uint(address_t const &receiver, uint16_t val) { send_2byte_uint(receiver, KNX_CT_ANSWER, val); } + void answer_2byte_float(address_t const &receiver, float val) { send_2byte_float(receiver, KNX_CT_ANSWER, val); } + void answer_3byte_time(address_t const &receiver, uint8_t weekday, uint8_t hours, uint8_t minutes, uint8_t seconds) { send_3byte_time(receiver, KNX_CT_ANSWER, weekday, hours, minutes, seconds); } + void answer_3byte_time(address_t const &receiver, time_of_day_t const &time) { send_3byte_time(receiver, KNX_CT_ANSWER, time.weekday, time.hours, time.minutes, time.seconds); } + void answer_3byte_date(address_t const &receiver, uint8_t day, uint8_t month, uint8_t year) { send_3byte_date(receiver, KNX_CT_ANSWER, day, month, year); } + void answer_3byte_date(address_t const &receiver, date_t const &date) { send_3byte_date(receiver, KNX_CT_ANSWER, date.day, date.month, date.year); } + void answer_3byte_color(address_t const &receiver, uint8_t red, uint8_t green, uint8_t blue) { send_3byte_color(receiver, KNX_CT_ANSWER, red, green, blue); } + void answer_3byte_color(address_t const &receiver, color_t const &color) { send_3byte_color(receiver, KNX_CT_ANSWER, color); } + void answer_4byte_int(address_t const &receiver, int32_t val) { send_4byte_int(receiver, KNX_CT_ANSWER, val); } + void answer_4byte_uint(address_t const &receiver, uint32_t val) { send_4byte_uint(receiver, KNX_CT_ANSWER, val); } + void answer_4byte_float(address_t const &receiver, float val) { send_4byte_float(receiver, KNX_CT_ANSWER, val);} + void answer_14byte_string(address_t const &receiver, const char *val) { send_14byte_string(receiver, KNX_CT_ANSWER, val); } + + bool data_to_bool(uint8_t *data); + int8_t data_to_1byte_int(uint8_t *data); + uint8_t data_to_1byte_uint(uint8_t *data); + int16_t data_to_2byte_int(uint8_t *data); + uint16_t data_to_2byte_uint(uint8_t *data); + float data_to_2byte_float(uint8_t *data); + color_t data_to_3byte_color(uint8_t *data); + time_of_day_t data_to_3byte_time(uint8_t *data); + date_t data_to_3byte_data(uint8_t *data); + int32_t data_to_4byte_int(uint8_t *data); + uint32_t data_to_4byte_uint(uint8_t *data); + float data_to_4byte_float(uint8_t *data); + + static address_t GA_to_address(uint8_t area, uint8_t line, uint8_t member) + { + // Yes, the order is correct, see the struct definition above + address_t tmp = {.ga={line, area, member}}; + return tmp; + } + + static address_t PA_to_address(uint8_t area, uint8_t line, uint8_t member) + { + // Yes, the order is correct, see the struct definition above + address_t tmp = {.pa={line, area, member}}; + return tmp; + } + + private: + void __start(); + + void __loop_knx(); + + // Webserver functions + void __loop_webserver(); + void __handle_root(); + void __handle_register(); + void __handle_delete(); + void __handle_set(); +#if !DISABLE_EEPROM_BUTTONS + void __handle_eeprom(); +#endif + void __handle_config(); + void __handle_feedback(); +#if !DISABLE_RESTORE_BUTTONS + void __handle_restore(); +#endif +#if !DISABLE_REBOOT_BUTTONS + void __handle_reboot(); +#endif + + void __config_set_flags(config_id_t id, config_flags_t flags); + + void __config_set_string(config_id_t id, String &val); + void __config_set_int(config_id_t id, int32_t val); + void __config_set_bool(config_id_t id, bool val); + 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); + + //static inline float pow(float a, float b) { return FastPrecisePowf(a, b); } + + ESP8266WebServer *server; + address_t physaddr; + + WiFiUDP 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; + uint8_t custom_config_data[MAX_CONFIG_SPACE]; + uint8_t custom_config_default_data[MAX_CONFIG_SPACE]; + config_t custom_configs[MAX_CONFIGS]; + + feedback_id_t registered_feedbacks; + feedback_t feedbacks[MAX_FEEDBACKS]; + + uint16_t __ntohs(uint16_t); +}; + +// Global "singleton" object +extern ESPKNXIP knx; + +#endif From e413dcaea2f6dbb1c2a97550626a60b74f645961 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 26 Oct 2020 11:33:35 +0100 Subject: [PATCH 4/7] Update platformio_tasmota_env.ini --- platformio_tasmota_env.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio_tasmota_env.ini b/platformio_tasmota_env.ini index 7496c858d..e88e37e8a 100644 --- a/platformio_tasmota_env.ini +++ b/platformio_tasmota_env.ini @@ -43,7 +43,7 @@ lib_extra_dirs = [env:tasmota-knx] build_flags = ${common.build_flags} -DFIRMWARE_KNX_NO_EMULATION -lib_extra_dirs = lib_basic +lib_extra_dirs = lib_basic, lib_div [env:tasmota-sensors] build_flags = ${common.build_flags} -DFIRMWARE_SENSORS From e699c71c78456d0945ed6f52be6bafb882831866 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 26 Oct 2020 11:34:03 +0100 Subject: [PATCH 5/7] Update platformio_tasmota_env32.ini --- platformio_tasmota_env32.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index 819b6aa75..cb42e1e72 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -47,7 +47,7 @@ lib_extra_dirs = libesp32 [env:tasmota32-knx] extends = env:tasmota32 build_flags = ${common32.build_flags} -DFIRMWARE_KNX_NO_EMULATION -lib_extra_dirs = libesp32, lib_basic +lib_extra_dirs = libesp32, lib_basic, lib_div [env:tasmota32-sensors] extends = env:tasmota32 From 0989af6015b20d776996255cb054453fd8e697aa Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 26 Oct 2020 11:39:29 +0100 Subject: [PATCH 6/7] Use only KNX header files no need to include and compile whole KNX library --- tasmota/tasmota_globals.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tasmota/tasmota_globals.h b/tasmota/tasmota_globals.h index e245f3f2f..fa6a4f9fc 100644 --- a/tasmota/tasmota_globals.h +++ b/tasmota/tasmota_globals.h @@ -33,10 +33,10 @@ extern "C" { } #endif -//#ifdef USE_KNX // Enabling this will fail compilation. It has no impact if not used. (20180417) -#include +#include // KNX Header files have to be global else compile fails -> lib/headers +#ifdef USE_KNX void KNX_CB_Action(message_t const &msg, void *arg); -//#endif // USE_KNX +#endif // USE_KNX void DomoticzTempHumPressureSensor(float temp, float hum, float baro = -1); char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0'); From 3d48f1374a0f6602b940f65dfb97e05d781b94dc Mon Sep 17 00:00:00 2001 From: Jason2866 Date: Mon, 26 Oct 2020 10:47:58 +0000 Subject: [PATCH 7/7] del KNX from lib --- lib/esp-knx-ip-0.5.2/DPT.h | 73 -- lib/esp-knx-ip-0.5.2/LICENSE | 21 - lib/esp-knx-ip-0.5.2/README.md | 94 --- lib/esp-knx-ip-0.5.2/esp-knx-ip-config.cpp | 358 ---------- .../esp-knx-ip-conversion.cpp | 87 --- lib/esp-knx-ip-0.5.2/esp-knx-ip-send.cpp | 201 ------ lib/esp-knx-ip-0.5.2/esp-knx-ip-webserver.cpp | 540 -------------- lib/esp-knx-ip-0.5.2/esp-knx-ip.cpp | 664 ------------------ lib/esp-knx-ip-0.5.2/esp-knx-ip.h | 600 ---------------- .../environment-sensor/environment-sensor.ino | 159 ----- .../examples/sonoff/sonoff.ino | 183 ----- .../examples/static-config/static-config.ino | 142 ---- lib/esp-knx-ip-0.5.2/keywords.txt | 107 --- lib/esp-knx-ip-0.5.2/library.properties | 10 - 14 files changed, 3239 deletions(-) delete mode 100644 lib/esp-knx-ip-0.5.2/DPT.h delete mode 100644 lib/esp-knx-ip-0.5.2/LICENSE delete mode 100644 lib/esp-knx-ip-0.5.2/README.md delete mode 100644 lib/esp-knx-ip-0.5.2/esp-knx-ip-config.cpp delete mode 100644 lib/esp-knx-ip-0.5.2/esp-knx-ip-conversion.cpp delete mode 100644 lib/esp-knx-ip-0.5.2/esp-knx-ip-send.cpp delete mode 100644 lib/esp-knx-ip-0.5.2/esp-knx-ip-webserver.cpp delete mode 100644 lib/esp-knx-ip-0.5.2/esp-knx-ip.cpp delete mode 100644 lib/esp-knx-ip-0.5.2/esp-knx-ip.h delete mode 100644 lib/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino delete mode 100644 lib/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino delete mode 100644 lib/esp-knx-ip-0.5.2/examples/static-config/static-config.ino delete mode 100644 lib/esp-knx-ip-0.5.2/keywords.txt delete mode 100644 lib/esp-knx-ip-0.5.2/library.properties diff --git a/lib/esp-knx-ip-0.5.2/DPT.h b/lib/esp-knx-ip-0.5.2/DPT.h deleted file mode 100644 index 3529d51af..000000000 --- a/lib/esp-knx-ip-0.5.2/DPT.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * esp-knx-ip library for KNX/IP communication on an ESP8266 - * Author: Nico Weichbrodt - * License: MIT - */ - -typedef enum __dpt_1_001 -{ - DPT_1_001_OFF = 0x00, - DPT_1_001_ON = 0x01, -} dpt_1_001_t; - -typedef enum __dpt_2_001 -{ - DPT_2_001_NO_OFF = 0b00, - DPT_2_001_NO_ON = 0b01, - DPT_2_001_YES_OFF = 0b10, - DPT_2_001_YES_ON = 0b11, -} dpt_2_001_t; - -typedef enum __dpt_3_007 -{ - DPT_3_007_DECREASE_STOP = 0x00, - DPT_3_007_DECREASE_100 = 0x01, - DPT_3_007_DECREASE_50 = 0x02, - DPT_3_007_DECREASE_25 = 0x03, - DPT_3_007_DECREASE_12 = 0x04, - DPT_3_007_DECREASE_6 = 0x05, - DPT_3_007_DECREASE_3 = 0x06, - DPT_3_007_DECREASE_1 = 0x07, - DPT_3_007_INCREASE_STOP = 0x08, - DPT_3_007_INCREASE_100 = 0x09, - DPT_3_007_INCREASE_50 = 0x0A, - DPT_3_007_INCREASE_25 = 0x0B, - DPT_3_007_INCREASE_12 = 0x0C, - DPT_3_007_INCREASE_6 = 0x0D, - DPT_3_007_INCREASE_3 = 0x0E, - DPT_3_007_INCREASE_1 = 0x0F, -} dpt_3_007_t; - -typedef enum __weekday -{ - DPT_10_001_WEEKDAY_NODAY = 0, - DPT_10_001_WEEKDAY_MONDAY = 1, - DPT_10_001_WEEKDAY_TUESDAY = 2, - DPT_10_001_WEEKDAY_WEDNESDAY = 3, - DPT_10_001_WEEKDAY_THURSDAY = 4, - DPT_10_001_WEEKDAY_FRIDAY = 5, - DPT_10_001_WEEKDAY_SATURDAY = 6, - DPT_10_001_WEEKDAY_SUNDAY = 7, -} weekday_t; - -typedef struct __time_of_day -{ - weekday_t weekday; - uint8_t hours; - uint8_t minutes; - uint8_t seconds; -} time_of_day_t; - -typedef struct __date -{ - uint8_t day; - uint8_t month; - uint8_t year; -} date_t; - -typedef struct __color -{ - uint8_t red; - uint8_t green; - uint8_t blue; -} color_t; diff --git a/lib/esp-knx-ip-0.5.2/LICENSE b/lib/esp-knx-ip-0.5.2/LICENSE deleted file mode 100644 index 80f432a97..000000000 --- a/lib/esp-knx-ip-0.5.2/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Nico Weichbrodt - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/lib/esp-knx-ip-0.5.2/README.md b/lib/esp-knx-ip-0.5.2/README.md deleted file mode 100644 index a93c09192..000000000 --- a/lib/esp-knx-ip-0.5.2/README.md +++ /dev/null @@ -1,94 +0,0 @@ -# ESP-KNX-IP # - -This is a library for the ESP8266 to enable KNXnet/IP communication. It uses UDP multicast on 224.0.23.12:3671. -It is intended to be used with the Arduino platform for the ESP8266. - -## How to use ## - -The library is under development. API may change multiple times in the future. - -API documentation is available [here](https://github.com/envy/esp-knx-ip/wiki/API) - -A simple example: - -```c++ -#include - -const char* ssid = "my-ssid"; // your network SSID (name) -const char* pass = "my-pw"; // your network password - -config_id_t my_GA; -config_id_t param_id; - -int8_t some_var = 0; - -void setup() -{ - // Register a callback that is called when a configurable group address is receiving a telegram - knx.register_callback("Set/Get callback", my_callback); - knx.register_callback("Write callback", my_other_callback); - - int default_val = 21; - param_id = knx.config_register_int("My Parameter", default_val); - - // Register a configurable group address for sending out answers - my_GA = knx.config_register_ga("Answer GA"); - - knx.load(); // Try to load a config from EEPROM - - WiFi.begin(ssid, pass); - while (WiFi.status() != WL_CONNECTED) { - delay(500); - } - - knx.start(); // Start everything. Must be called after WiFi connection has been established -} - -void loop() -{ - knx.loop(); -} - - -void my_callback(message_t const &msg, void *arg) -{ - switch (msg.ct) - { - case KNX_CT_WRITE: - // Save received data - some_var = knx.data_to_1byte_int(msg.data); - break; - case KNX_CT_READ: - // Answer with saved data - knx.answer1ByteInt(msg.received_on, some_var); - break; - } -} - -void my_other_callback(message_t const &msg, void *arg) -{ - switch (msg.ct) - { - case KNX_CT_WRITE: - // Write an answer somewhere else - int value = knx.config_get_int(param_id); - address_t ga = knx.config_get_ga(my_GA); - knx.answer1ByteInt(ga, (int8_t)value); - break; - } -} - -``` - -## How to configure (buildtime) ## - -Open the `esp-knx-ip.h` and take a look at the config options at the top inside the block marked `CONFIG` - -## How to configure (runtime) ## - -Simply visit the IP of your ESP with a webbrowser. You can configure the following: -* KNX physical address -* Which group address should trigger which callback -* Which group address are to be used by the program (e.g. for status replies) - -The configuration is dynamically generated from the code. diff --git a/lib/esp-knx-ip-0.5.2/esp-knx-ip-config.cpp b/lib/esp-knx-ip-0.5.2/esp-knx-ip-config.cpp deleted file mode 100644 index 8d2b7b39d..000000000 --- a/lib/esp-knx-ip-0.5.2/esp-knx-ip-config.cpp +++ /dev/null @@ -1,358 +0,0 @@ -/** - * esp-knx-ip library for KNX/IP communication on an ESP8266 - * Author: Nico Weichbrodt - * License: MIT - */ - -#include "esp-knx-ip.h" - -/** - * Physical address functions - */ - -void ESPKNXIP::physical_address_set(address_t const &addr) -{ - physaddr = addr; -} - -address_t ESPKNXIP::physical_address_get() -{ - return physaddr; -} - -/** - * Configuration functions start here - */ -config_id_t ESPKNXIP::config_register_string(String name, uint8_t len, String _default, enable_condition_t cond) -{ - if (registered_configs >= MAX_CONFIGS) - return -1; - - if (_default.length() >= len) - return -1; - - config_id_t id = registered_configs; - - custom_configs[id].name = name; - custom_configs[id].type = CONFIG_TYPE_STRING; - custom_configs[id].len = sizeof(uint8_t) + len; - custom_configs[id].cond = cond; - if (id == 0) - custom_configs[id].offset = 0; - else - custom_configs[id].offset = custom_configs[id - 1].offset + custom_configs[id - 1].len; - - __config_set_string(id, _default); - - registered_configs++; - - DEBUG_PRINT("Registered config >"); - DEBUG_PRINT(name); - DEBUG_PRINT("< @ "); - DEBUG_PRINT(id); - DEBUG_PRINT("/string["); - DEBUG_PRINT(custom_configs[id].offset); - DEBUG_PRINT("+"); - DEBUG_PRINT(custom_configs[id].len); - DEBUG_PRINTLN("]"); - - return id; -} - -config_id_t ESPKNXIP::config_register_int(String name, int32_t _default, enable_condition_t cond) -{ - if (registered_configs >= MAX_CONFIGS) - return -1; - - config_id_t id = registered_configs; - - custom_configs[id].name = name; - custom_configs[id].type = CONFIG_TYPE_INT; - custom_configs[id].len = sizeof(uint8_t) + sizeof(int32_t); - custom_configs[id].cond = cond; - if (id == 0) - custom_configs[id].offset = 0; - else - custom_configs[id].offset = custom_configs[id - 1].offset + custom_configs[id - 1].len; - - __config_set_int(id, _default); - - registered_configs++; - - DEBUG_PRINT("Registered config >"); - DEBUG_PRINT(name); - DEBUG_PRINT("< @ "); - DEBUG_PRINT(id); - DEBUG_PRINT("/int["); - DEBUG_PRINT(custom_configs[id].offset); - DEBUG_PRINT("+"); - DEBUG_PRINT(custom_configs[id].len); - DEBUG_PRINTLN("]"); - - return id; -} - -config_id_t ESPKNXIP::config_register_bool(String name, bool _default, enable_condition_t cond) -{ - if (registered_configs >= MAX_CONFIGS) - return -1; - - config_id_t id = registered_configs; - - custom_configs[id].name = name; - custom_configs[id].type = CONFIG_TYPE_BOOL; - custom_configs[id].len = sizeof(uint8_t) + sizeof(uint8_t); - custom_configs[id].cond = cond; - if (id == 0) - custom_configs[id].offset = 0; - else - custom_configs[id].offset = custom_configs[id - 1].offset + custom_configs[id - 1].len; - - __config_set_bool(id, _default); - - registered_configs++; - - DEBUG_PRINT("Registered config >"); - DEBUG_PRINT(name); - DEBUG_PRINT("< @ "); - DEBUG_PRINT(id); - DEBUG_PRINT("/bool["); - DEBUG_PRINT(custom_configs[id].offset); - DEBUG_PRINT("+"); - DEBUG_PRINT(custom_configs[id].len); - DEBUG_PRINTLN("]"); - - return id; -} - -config_id_t ESPKNXIP::config_register_options(String name, option_entry_t *options, uint8_t _default, enable_condition_t cond) -{ - if (registered_configs >= MAX_CONFIGS) - return -1; - - if (options == nullptr || options->name == nullptr) - return -1; - - config_id_t id = registered_configs; - - custom_configs[id].name = name; - custom_configs[id].type = CONFIG_TYPE_OPTIONS; - custom_configs[id].len = sizeof(uint8_t) + sizeof(uint8_t); - custom_configs[id].cond = cond; - if (id == 0) - custom_configs[id].offset = 0; - else - custom_configs[id].offset = custom_configs[id - 1].offset + custom_configs[id - 1].len; - - custom_configs[id].data.options = options; - - __config_set_options(id, _default); - - registered_configs++; - - DEBUG_PRINT("Registered config >"); - DEBUG_PRINT(name); - DEBUG_PRINT("< @ "); - DEBUG_PRINT(id); - DEBUG_PRINT("/opt["); - DEBUG_PRINT(custom_configs[id].offset); - DEBUG_PRINT("+"); - DEBUG_PRINT(custom_configs[id].len); - DEBUG_PRINTLN("]"); - - return id; -} - -config_id_t ESPKNXIP::config_register_ga(String name, enable_condition_t cond) -{ - if (registered_configs >= MAX_CONFIGS) - return -1; - - config_id_t id = registered_configs; - - custom_configs[id].name = name; - custom_configs[id].type = CONFIG_TYPE_GA; - custom_configs[id].len = sizeof(uint8_t) + sizeof(address_t); - custom_configs[id].cond = cond; - if (id == 0) - custom_configs[id].offset = 0; - else - custom_configs[id].offset = custom_configs[id - 1].offset + custom_configs[id - 1].len; - - address_t t; - t.value = 0; - __config_set_ga(id, t); - - registered_configs++; - - DEBUG_PRINT("Registered config >"); - DEBUG_PRINT(name); - DEBUG_PRINT("< @ "); - DEBUG_PRINT(id); - DEBUG_PRINT("/ga["); - DEBUG_PRINT(custom_configs[id].offset); - DEBUG_PRINT("+"); - DEBUG_PRINT(custom_configs[id].len); - DEBUG_PRINTLN("]"); - - return id; -} - -void ESPKNXIP::__config_set_flags(config_id_t id, config_flags_t flags) -{ - DEBUG_PRINT("Setting flag @ "); - DEBUG_PRINT(custom_configs[id].offset); - DEBUG_PRINT(" to "); - DEBUG_PRINT(custom_config_data[custom_configs[id].offset], BIN); - DEBUG_PRINT(" | "); - DEBUG_PRINT(flags, BIN); - custom_config_data[custom_configs[id].offset] |= (uint8_t)flags; - DEBUG_PRINT(" = "); - DEBUG_PRINTLN(custom_config_data[custom_configs[id].offset], BIN); -} - -void ESPKNXIP::config_set_string(config_id_t id, String val) -{ - if (id >= registered_configs) - return; - if (custom_configs[id].type != CONFIG_TYPE_STRING) - return; - if (val.length() >= custom_configs[id].len) - return; - __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); - __config_set_string(id, val); -} - -void ESPKNXIP::__config_set_string(config_id_t id, String &val) -{ - memcpy(&custom_config_data[custom_configs[id].offset + sizeof(uint8_t)], val.c_str(), val.length()+1); -} - -void ESPKNXIP::config_set_int(config_id_t id, int32_t val) -{ - if (id >= registered_configs) - return; - if (custom_configs[id].type != CONFIG_TYPE_INT) - return; - __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); - __config_set_int(id, val); -} - -void ESPKNXIP::__config_set_int(config_id_t id, int32_t val) -{ - // This does not work for some reason: - // Could be due to pointer alignment - //int32_t *v = (int32_t *)(custom_config_data + custom_configs[id].offset); - //*v = val; - custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 0] = (uint8_t)((val & 0xFF000000) >> 24); - custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 1] = (uint8_t)((val & 0x00FF0000) >> 16); - custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 2] = (uint8_t)((val & 0x0000FF00) >> 8); - custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 3] = (uint8_t)((val & 0x000000FF) >> 0); -} - -void ESPKNXIP::config_set_bool(config_id_t id, bool val) -{ - if (id >= registered_configs) - return; - if (custom_configs[id].type != CONFIG_TYPE_BOOL) - return; - __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); - __config_set_bool(id, val); -} - -void ESPKNXIP::__config_set_bool(config_id_t id, bool val) -{ - custom_config_data[custom_configs[id].offset + sizeof(uint8_t)] = val ? 1 : 0; -} - -void ESPKNXIP::config_set_options(config_id_t id, uint8_t val) -{ - if (id >= registered_configs) - return; - if (custom_configs[id].type != CONFIG_TYPE_OPTIONS) - return; - - option_entry_t *cur = custom_configs[id].data.options; - while (cur->name != nullptr) - { - if (cur->value == val) - { - __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); - __config_set_options(id, val); - break; - } - cur++; - } -} - -void ESPKNXIP::__config_set_options(config_id_t id, uint8_t val) -{ - custom_config_data[custom_configs[id].offset + sizeof(uint8_t)] = val; -} - -void ESPKNXIP::config_set_ga(config_id_t id, address_t const &val) -{ - if (id >= registered_configs) - return; - if (custom_configs[id].type != CONFIG_TYPE_GA) - return; - __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); - __config_set_ga(id, val); -} - -void ESPKNXIP::__config_set_ga(config_id_t id, address_t const &val) -{ - custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 0] = val.bytes.high; - custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 1] = val.bytes.low; -} - -String ESPKNXIP::config_get_string(config_id_t id) -{ - if (id >= registered_configs) - return String(""); - - return String((char *)&custom_config_data[custom_configs[id].offset + sizeof(uint8_t)]); -} - -int32_t ESPKNXIP::config_get_int(config_id_t id) -{ - if (id >= registered_configs) - return 0; - - int32_t v = (custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 0] << 24) + - (custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 1] << 16) + - (custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 2] << 8) + - (custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 3] << 0); - return v; -} - -bool ESPKNXIP::config_get_bool(config_id_t id) -{ - if (id >= registered_configs) - return false; - - return custom_config_data[custom_configs[id].offset + sizeof(uint8_t)] != 0; -} - -uint8_t ESPKNXIP::config_get_options(config_id_t id) -{ - if (id >= registered_configs) - return false; - - return custom_config_data[custom_configs[id].offset + sizeof(uint8_t)]; -} - -address_t ESPKNXIP::config_get_ga(config_id_t id) -{ - address_t t; - if (id >= registered_configs) - { - t.value = 0; - return t; - } - - t.bytes.high = custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 0]; - t.bytes.low = custom_config_data[custom_configs[id].offset + sizeof(uint8_t) + 1]; - - return t; -} diff --git a/lib/esp-knx-ip-0.5.2/esp-knx-ip-conversion.cpp b/lib/esp-knx-ip-0.5.2/esp-knx-ip-conversion.cpp deleted file mode 100644 index 9dc2fd563..000000000 --- a/lib/esp-knx-ip-0.5.2/esp-knx-ip-conversion.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/** - * esp-knx-ip library for KNX/IP communication on an ESP8266 - * Author: Nico Weichbrodt - * License: MIT - */ - -#include "esp-knx-ip.h" - -/** - * Conversion functions - */ - -bool ESPKNXIP::data_to_bool(uint8_t *data) -{ - return (data[0] & 0x01) == 1 ? true : false; -} - -int8_t ESPKNXIP::data_to_1byte_int(uint8_t *data) -{ - return (int8_t)data[1]; -} - -uint8_t ESPKNXIP::data_to_1byte_uint(uint8_t *data) -{ - return data[1]; -} - -int16_t ESPKNXIP::data_to_2byte_int(uint8_t *data) -{ - return (int16_t)((data[1] << 8) | data[2]); -} - -uint16_t ESPKNXIP::data_to_2byte_uint(uint8_t *data) -{ - return (uint16_t)((data[1] << 8) | data[2]); -} - -float ESPKNXIP::data_to_2byte_float(uint8_t *data) -{ - //uint8_t sign = (data[1] & 0b10000000) >> 7; - uint8_t expo = (data[1] & 0b01111000) >> 3; - int16_t mant = ((data[1] & 0b10000111) << 8) | data[2]; - return 0.01f * mant * pow(2, expo); -} - -time_of_day_t ESPKNXIP::data_to_3byte_time(uint8_t *data) -{ - time_of_day_t time; - time.weekday = (weekday_t)((data[1] & 0b11100000) >> 5); - time.hours = (data[1] & 0b00011111); - time.minutes = (data[2] & 0b00111111); - time.seconds = (data[3] & 0b00111111); - return time; -} - -date_t ESPKNXIP::data_to_3byte_data(uint8_t *data) -{ - date_t date; - date.day = (data[1] & 0b00011111); - date.month = (data[2] & 0b00001111); - date.year = (data[3] & 0b01111111); - return date; -} - -color_t ESPKNXIP::data_to_3byte_color(uint8_t *data) -{ - color_t color; - color.red = data[1]; - color.green = data[2]; - color.blue = data[3]; - return color; -} - -int32_t ESPKNXIP::data_to_4byte_int(uint8_t *data) -{ - return (int32_t)((data[1] << 24) | (data[2] << 16) | (data[3] << 8) | (data[4] << 0)); -} - -uint32_t ESPKNXIP::data_to_4byte_uint(uint8_t *data) -{ - return (uint32_t)((data[1] << 24) | (data[2] << 16) | (data[3] << 8) | (data[4] << 0)); -} - -float ESPKNXIP::data_to_4byte_float(uint8_t *data) -{ - return (float)((data[1] << 24) | (data[2] << 16) | (data[3] << 8) |data[4]); -} \ No newline at end of file diff --git a/lib/esp-knx-ip-0.5.2/esp-knx-ip-send.cpp b/lib/esp-knx-ip-0.5.2/esp-knx-ip-send.cpp deleted file mode 100644 index e71e5954c..000000000 --- a/lib/esp-knx-ip-0.5.2/esp-knx-ip-send.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/** - * esp-knx-ip library for KNX/IP communication on an ESP8266 - * Author: Nico Weichbrodt - * License: MIT - */ - -#include "esp-knx-ip.h" - -/** - * Send functions - */ - -void ESPKNXIP::send(address_t const &receiver, knx_command_type_t ct, uint8_t data_len, uint8_t *data) -{ - if (receiver.value == 0) - return; - -#if SEND_CHECKSUM - uint32_t len = 6 + 2 + 8 + data_len + 1; // knx_pkt + cemi_msg + cemi_service + data + checksum -#else - uint32_t len = 6 + 2 + 8 + data_len; // knx_pkt + cemi_msg + cemi_service + data -#endif - DEBUG_PRINT(F("Creating packet with len ")); - DEBUG_PRINTLN(len) - uint8_t buf[len]; - knx_ip_pkt_t *knx_pkt = (knx_ip_pkt_t *)buf; - knx_pkt->header_len = 0x06; - knx_pkt->protocol_version = 0x10; - knx_pkt->service_type = __ntohs(KNX_ST_ROUTING_INDICATION); - knx_pkt->total_len.len = __ntohs(len); - cemi_msg_t *cemi_msg = (cemi_msg_t *)knx_pkt->pkt_data; - cemi_msg->message_code = KNX_MT_L_DATA_IND; - cemi_msg->additional_info_len = 0; - cemi_service_t *cemi_data = &cemi_msg->data.service_information; - cemi_data->control_1.bits.confirm = 0; -//cemi_data->control_1.bits.ack = 1; - cemi_data->control_1.bits.ack = 0; // ask for ACK? 0-no 1-yes - cemi_data->control_1.bits.priority = B11; - cemi_data->control_1.bits.system_broadcast = 0x01; - cemi_data->control_1.bits.repeat = 0x01; // 0 = repeated telegram, 1 = not repeated telegram - cemi_data->control_1.bits.reserved = 0; - cemi_data->control_1.bits.frame_type = 0x01; - cemi_data->control_2.bits.extended_frame_format = 0x00; - cemi_data->control_2.bits.hop_count = 0x06; - cemi_data->control_2.bits.dest_addr_type = 0x01; - cemi_data->source = physaddr; - cemi_data->destination = receiver; - //cemi_data->destination.bytes.high = (area << 3) | line; - //cemi_data->destination.bytes.low = member; - cemi_data->data_len = data_len; - cemi_data->pci.apci = (ct & 0x0C) >> 2; -//cemi_data->pci.apci = KNX_COT_NCD_ACK; - cemi_data->pci.tpci_seq_number = 0x00; - cemi_data->pci.tpci_comm_type = KNX_COT_UDP; // Type of communication: DATA PACKAGE or CONTROL DATA -//cemi_data->pci.tpci_comm_type = KNX_COT_NCD; // Type of communication: DATA PACKAGE or CONTROL DATA - memcpy(cemi_data->data, data, data_len); -//cemi_data->data[0] = (cemi_data->data[0] & 0x3F) | ((KNX_COT_NCD_ACK & 0x03) << 6); - cemi_data->data[0] = (cemi_data->data[0] & 0x3F) | ((ct & 0x03) << 6); - -#if SEND_CHECKSUM - // Calculate checksum, which is just XOR of all bytes - uint8_t cs = buf[0] ^ buf[1]; - for (uint32_t i = 2; i < len - 1; ++i) - { - cs ^= buf[i]; - } - buf[len - 1] = cs; -#endif - -#ifdef ESP_KNX_DEBUG - DEBUG_PRINT(F("Sending packet:")); - for (int i = 0; i < len; ++i) - { - DEBUG_PRINT(F(" 0x")); - DEBUG_PRINT(buf[i], 16); - } - DEBUG_PRINTLN(F("")); -#endif - - udp.beginPacketMulticast(MULTICAST_IP, MULTICAST_PORT, WiFi.localIP()); - udp.write(buf, len); - udp.endPacket(); - -} - -void ESPKNXIP::send_1bit(address_t const &receiver, knx_command_type_t ct, uint8_t bit) -{ - uint8_t buf[] = {(uint8_t)(bit & 0b00000001)}; - send(receiver, ct, 1, buf); -} - -void ESPKNXIP::send_2bit(address_t const &receiver, knx_command_type_t ct, uint8_t twobit) -{ - uint8_t buf[] = {(uint8_t)(twobit & 0b00000011)}; - send(receiver, ct, 1, buf); -} - -void ESPKNXIP::send_4bit(address_t const &receiver, knx_command_type_t ct, uint8_t fourbit) -{ - uint8_t buf[] = {(uint8_t)(fourbit & 0b00001111)}; - send(receiver, ct, 1, buf); -} - -void ESPKNXIP::send_1byte_int(address_t const &receiver, knx_command_type_t ct, int8_t val) -{ - uint8_t buf[] = {0x00, (uint8_t)val}; - send(receiver, ct, 2, buf); -} - -void ESPKNXIP::send_1byte_uint(address_t const &receiver, knx_command_type_t ct, uint8_t val) -{ - uint8_t buf[] = {0x00, val}; - send(receiver, ct, 2, buf); -} - -void ESPKNXIP::send_2byte_int(address_t const &receiver, knx_command_type_t ct, int16_t val) -{ - uint8_t buf[] = {0x00, (uint8_t)(val >> 8), (uint8_t)(val & 0x00FF)}; - send(receiver, ct, 3, buf); -} - -void ESPKNXIP::send_2byte_uint(address_t const &receiver, knx_command_type_t ct, uint16_t val) -{ - uint8_t buf[] = {0x00, (uint8_t)(val >> 8), (uint8_t)(val & 0x00FF)}; - send(receiver, ct, 3, buf); -} - -void ESPKNXIP::send_2byte_float(address_t const &receiver, knx_command_type_t ct, float val) -{ - float v = val * 100.0f; - int e = 0; - for (; v < -2048.0f; v /= 2) - ++e; - for (; v > 2047.0f; v /= 2) - ++e; - long m = (long)round(v) & 0x7FF; - short msb = (short) (e << 3 | m >> 8); - if (val < 0.0f) - msb |= 0x80; - uint8_t buf[] = {0x00, (uint8_t)msb, (uint8_t)m}; - send(receiver, ct, 3, buf); -} - -void ESPKNXIP::send_3byte_time(address_t const &receiver, knx_command_type_t ct, uint8_t weekday, uint8_t hours, uint8_t minutes, uint8_t seconds) -{ - weekday <<= 5; - uint8_t buf[] = {0x00, (uint8_t)(((weekday << 5) & 0xE0) | (hours & 0x1F)), (uint8_t)(minutes & 0x3F), (uint8_t)(seconds & 0x3F)}; - send(receiver, ct, 4, buf); -} - -void ESPKNXIP::send_3byte_date(address_t const &receiver, knx_command_type_t ct, uint8_t day, uint8_t month, uint8_t year) -{ - uint8_t buf[] = {0x00, (uint8_t)(day & 0x1F), (uint8_t)(month & 0x0F), year}; - send(receiver, ct, 4, buf); -} - -void ESPKNXIP::send_3byte_color(address_t const &receiver, knx_command_type_t ct, uint8_t red, uint8_t green, uint8_t blue) -{ - uint8_t buf[] = {0x00, red, green, blue}; - send(receiver, ct, 4, buf); -} - -void ESPKNXIP::send_4byte_int(address_t const &receiver, knx_command_type_t ct, int32_t val) -{ - uint8_t buf[] = {0x00, - (uint8_t)((val & 0xFF000000) >> 24), - (uint8_t)((val & 0x00FF0000) >> 16), - (uint8_t)((val & 0x0000FF00) >> 8), - (uint8_t)((val & 0x000000FF) >> 0)}; - send(receiver, ct, 5, buf); -} - -void ESPKNXIP::send_4byte_uint(address_t const &receiver, knx_command_type_t ct, uint32_t val) -{ - uint8_t buf[] = {0x00, - (uint8_t)((val & 0xFF000000) >> 24), - (uint8_t)((val & 0x00FF0000) >> 16), - (uint8_t)((val & 0x0000FF00) >> 8), - (uint8_t)((val & 0x000000FF) >> 0)}; - send(receiver, ct, 5, buf); -} - -void ESPKNXIP::send_4byte_float(address_t const &receiver, knx_command_type_t ct, float val) -{ - uint8_t buf[] = {0x00, ((uint8_t *)&val)[3], ((uint8_t *)&val)[2], ((uint8_t *)&val)[1], ((uint8_t *)&val)[0]}; - send(receiver, ct, 5, buf); -} - -void ESPKNXIP::send_14byte_string(address_t const &receiver, knx_command_type_t ct, const char *val) -{ - // DPT16 strings are always 14 bytes long, however the data array is one larger due to the telegram structure. - // The first byte needs to be zero, string start after that. - uint8_t buf[15] = {0x00}; - int len = strlen(val); - if (len > 14) - { - len = 14; - } - memcpy(buf+1, val, len); - send(receiver, ct, 15, buf); -} diff --git a/lib/esp-knx-ip-0.5.2/esp-knx-ip-webserver.cpp b/lib/esp-knx-ip-0.5.2/esp-knx-ip-webserver.cpp deleted file mode 100644 index bdbc013fd..000000000 --- a/lib/esp-knx-ip-0.5.2/esp-knx-ip-webserver.cpp +++ /dev/null @@ -1,540 +0,0 @@ -/** - * esp-knx-ip library for KNX/IP communication on an ESP8266 - * Author: Nico Weichbrodt - * License: MIT - */ - -#include "esp-knx-ip.h" - -void ESPKNXIP::__handle_root() -{ - String m = F(""); -#if USE_BOOTSTRAP - m += F(""); - m += F(""); -#endif - m += F("
"); - m += F("

ESP KNX

"); - - // Feedback - - if (registered_feedbacks > 0) - { - m += F("

Feedback

"); - for (feedback_id_t i = 0; i < registered_feedbacks; ++i) - { - if (feedbacks[i].cond && !feedbacks[i].cond()) - { - continue; - } - m += F("
"); - m += F("
"); - m += F("
"); - m += feedbacks[i].name; - m += F("
"); - switch (feedbacks[i].type) - { - case FEEDBACK_TYPE_INT: - m += F(""); - m += String(*(int32_t *)feedbacks[i].data); - m += F(""); - break; - case FEEDBACK_TYPE_FLOAT: - m += F(""); - 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(""); - break; - case FEEDBACK_TYPE_BOOL: - m += F(""); - m += (*(bool *)feedbacks[i].data) ? F("True") : F("False"); - m += F(""); - break; - case FEEDBACK_TYPE_ACTION: - m += F("
"); - break; - } - m += F("
"); - m += F("
"); - } - } - - if (registered_callbacks > 0) - m += F("

Callbacks

"); - - if (registered_callback_assignments > 0) - { - 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; - } - address_t &addr = callback_assignments[i].address; - m += F("
"); - m += F("
"); - m += F("
"); - m += addr.ga.area; - m += F("/"); - m += addr.ga.line; - m += F("/"); - m += addr.ga.member; - m += F(""); - m += F(""); - m += callbacks[callback_assignments[i].callback_id].name; - m += F("
"); - m += F("
"); - m += F("
"); - m += F("
"); - } - } - - if (registered_callbacks > 0) - { - m += F("
"); - m += F("
"); - m += F(""); - m += F("
/
"); - m += F(""); - m += F("
/
"); - m += F(""); - m += F("
->
"); - m += F(""); - m += F("
"); - m += F("
"); - m += F("
"); - } - - m += F("

Configuration

"); - - // Physical address - m += F("
"); - m += F("
"); - m += F("
Physical address
"); - m += F(""); - m += F("
.
"); - m += F(""); - m += F("
.
"); - m += F(""); - m += F("
"); - m += F("
"); - m += F("
"); - - if (registered_configs > 0) - { - for (config_id_t i = 0; i < registered_configs; ++i) - { - // Check if this config option has a enable condition and if so check that condition - if (custom_configs[i].cond && !custom_configs[i].cond()) - continue; - - m += F("
"); - m += F("
"); - m += F("
"); - m += custom_configs[i].name; - m += F("
"); - - switch (custom_configs[i].type) - { - case CONFIG_TYPE_STRING: - m += F(""); - break; - case CONFIG_TYPE_INT: - m += F(""); - break; - case CONFIG_TYPE_BOOL: - m += F("
"); - m += F(""); - m += F("
"); - break; - case CONFIG_TYPE_OPTIONS: - { - m += F(""); - break; - } - case CONFIG_TYPE_GA: - address_t a = config_get_ga(i); - m += F(""); - m += F("
/
"); - m += F(""); - m += F("
/
"); - m += F(""); - break; - } - m += F(""); - m += F("
"); - m += F("
"); - m += F("
"); - } - } - -#if !(DISABLE_EEPROM_BUTTONS && DISABLE_RESTORE_BUTTON && DISABLE_REBOOT_BUTTON) - // EEPROM save and restore - m += F("
"); - // Save to EEPROM -#if !DISABLE_EEPROM_BUTTONS - m += F("
"); - m += F("
"); - m += F(""); - m += F(""); - m += F("
"); - m += F("
"); - // Restore from EEPROM - m += F("
"); - m += F("
"); - m += F(""); - m += F(""); - m += F("
"); - m += F("
"); -#endif -#if !DISABLE_RESTORE_BUTTON - // Load Defaults - m += F("
"); - m += F("
"); - m += F(""); - m += F("
"); - m += F("
"); -#endif -#if !DISABLE_REBOOT_BUTTON - // Reboot - m += F("
"); - m += F("
"); - m += F(""); - m += F("
"); - m += F("
"); -#endif - m += F("
"); // row -#endif - - // End of page - m += F("
"); - server->send(200, F("text/html"), m); -} - -void ESPKNXIP::__handle_register() -{ - DEBUG_PRINTLN(F("Register called")); - if (server->hasArg(F("area")) && server->hasArg(F("line")) && server->hasArg(F("member")) && server->hasArg(F("cb"))) - { - uint8_t area = server->arg(F("area")).toInt(); - uint8_t line = server->arg(F("line")).toInt(); - uint8_t member = server->arg(F("member")).toInt(); - callback_id_t cb = (callback_id_t)server->arg(F("cb")).toInt(); - - DEBUG_PRINT(F("Got args: ")); - DEBUG_PRINT(area); - DEBUG_PRINT(F("/")); - DEBUG_PRINT(line); - DEBUG_PRINT(F("/")); - DEBUG_PRINT(member); - DEBUG_PRINT(F("/")); - DEBUG_PRINT(cb); - DEBUG_PRINTLN(F("")); - - if (area > 31 || line > 7) - { - DEBUG_PRINTLN(F("Area or Line wrong")); - goto end; - } - - if (!__callback_is_id_valid(cb)) - { - DEBUG_PRINTLN(F("Invalid callback id")); - goto end; - } - address_t ga = {.ga={line, area, member}}; - __callback_register_assignment(ga, cb); - } -end: - server->sendHeader(F("Location"),F(__ROOT_PATH)); - server->send(302); -} - -void ESPKNXIP::__handle_delete() -{ - DEBUG_PRINTLN(F("Delete called")); - if (server->hasArg(F("id"))) - { - callback_assignment_id_t id = (callback_assignment_id_t)server->arg(F("id")).toInt(); - - DEBUG_PRINT(F("Got args: ")); - DEBUG_PRINT(id); - DEBUG_PRINTLN(F("")); - - if (id >= registered_callback_assignments || (callback_assignments[id].slot_flags & SLOT_FLAGS_USED) == 0) - { - DEBUG_PRINTLN(F("ID wrong")); - goto end; - } - - __callback_delete_assignment(id); - } -end: - server->sendHeader(F("Location"),F(__ROOT_PATH)); - server->send(302); -} - -void ESPKNXIP::__handle_set() -{ - DEBUG_PRINTLN(F("Set called")); - if (server->hasArg(F("area")) && server->hasArg(F("line")) && server->hasArg(F("member"))) - { - uint8_t area = server->arg(F("area")).toInt(); - uint8_t line = server->arg(F("line")).toInt(); - uint8_t member = server->arg(F("member")).toInt(); - - DEBUG_PRINT(F("Got args: ")); - DEBUG_PRINT(area); - DEBUG_PRINT(F(".")); - DEBUG_PRINT(line); - DEBUG_PRINT(F(".")); - DEBUG_PRINT(member); - DEBUG_PRINTLN(F("")); - - if (area > 31 || line > 7) - { - DEBUG_PRINTLN(F("Area or Line wrong")); - goto end; - } - - physaddr.bytes.high = (area << 4) | line; - physaddr.bytes.low = member; - } -end: - server->sendHeader(F("Location"),F(__ROOT_PATH)); - server->send(302); -} - -void ESPKNXIP::__handle_config() -{ - DEBUG_PRINTLN(F("Config called")); - if (server->hasArg(F("id"))) - { - config_id_t id = server->arg(F("id")).toInt(); - - DEBUG_PRINT(F("Got args: ")); - DEBUG_PRINT(id); - DEBUG_PRINTLN(F("")); - - if (id < 0 || id >= registered_configs) - { - DEBUG_PRINTLN(F("ID wrong")); - goto end; - } - - switch (custom_configs[id].type) - { - case CONFIG_TYPE_STRING: - { - String v = server->arg(F("value")); - if (v.length() >= custom_configs[id].len) - goto end; - __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); - __config_set_string(id, v); - break; - } - case CONFIG_TYPE_INT: - { - __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); - __config_set_int(id, server->arg(F("value")).toInt()); - break; - } - case CONFIG_TYPE_BOOL: - { - __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); - __config_set_bool(id, server->arg(F("value")).compareTo(F("on")) == 0); - break; - } - case CONFIG_TYPE_OPTIONS: - { - uint8_t val = (uint8_t)server->arg(F("value")).toInt(); - DEBUG_PRINT(F("Value: ")); - DEBUG_PRINTLN(val); - config_set_options(id, val); - break; - } - case CONFIG_TYPE_GA: - { - uint8_t area = server->arg(F("area")).toInt(); - uint8_t line = server->arg(F("line")).toInt(); - uint8_t member = server->arg(F("member")).toInt(); - if (area > 31 || line > 7) - { - DEBUG_PRINTLN(F("Area or Line wrong")); - goto end; - } - address_t tmp; - tmp.bytes.high = (area << 3) | line; - tmp.bytes.low = member; - __config_set_flags(id, CONFIG_FLAGS_VALUE_SET); - __config_set_ga(id, tmp); - break; - } - } - } -end: - server->sendHeader(F("Location"),F(__ROOT_PATH)); - server->send(302); -} - -void ESPKNXIP::__handle_feedback() -{ -DEBUG_PRINTLN(F("Feedback called")); - if (server->hasArg(F("id"))) - { - config_id_t id = server->arg(F("id")).toInt(); - - DEBUG_PRINT(F("Got args: ")); - DEBUG_PRINT(id); - DEBUG_PRINTLN(F("")); - - if (id < 0 || id >= registered_feedbacks) - { - DEBUG_PRINTLN(F("ID wrong")); - goto end; - } - - switch (feedbacks[id].type) - { - case FEEDBACK_TYPE_ACTION: - { - feedback_action_fptr_t func = (feedback_action_fptr_t)feedbacks[id].data; - void *arg = feedbacks[id].options.action_options.arg; - func(arg); - break; - } - default: - DEBUG_PRINTLN(F("Feedback has no action")); - break; - } - } -end: - server->sendHeader(F("Location"),F(__ROOT_PATH)); - server->send(302); -} - -#if !DISABLE_RESTORE_BUTTONS -void ESPKNXIP::__handle_restore() -{ - DEBUG_PRINTLN(F("Restore called")); - memcpy(custom_config_data, custom_config_default_data, MAX_CONFIG_SPACE); -end: - server->sendHeader(F("Location"),F(__ROOT_PATH)); - server->send(302); -} -#endif - -#if !DISABLE_REBOOT_BUTTONS -void ESPKNXIP::__handle_reboot() -{ - DEBUG_PRINTLN(F("Rebooting!")); - server->sendHeader(F("Location"),F(__ROOT_PATH)); - server->send(302); - delay(1000); - ESP.restart(); - //while(1); -} -#endif - -#if !DISABLE_EEPROM_BUTTONS -void ESPKNXIP::__handle_eeprom() -{ - DEBUG_PRINTLN(F("EEPROM called")); - if (server->hasArg(F("mode"))) - { - uint8_t mode = server->arg(F("mode")).toInt(); - - DEBUG_PRINT(F("Got args: ")); - DEBUG_PRINT(mode); - DEBUG_PRINTLN(F("")); - - if (mode == 1) - { - // save - save_to_eeprom(); - } - else if (mode == 2) - { - // restore - restore_from_eeprom(); - } - } -end: - server->sendHeader(F("Location"),F(__ROOT_PATH)); - server->send(302); -} -#endif diff --git a/lib/esp-knx-ip-0.5.2/esp-knx-ip.cpp b/lib/esp-knx-ip-0.5.2/esp-knx-ip.cpp deleted file mode 100644 index 35cbb2161..000000000 --- a/lib/esp-knx-ip-0.5.2/esp-knx-ip.cpp +++ /dev/null @@ -1,664 +0,0 @@ -/** - * esp-knx-ip library for KNX/IP communication on an ESP8266 - * Author: Nico Weichbrodt - * License: MIT - */ - -#include "esp-knx-ip.h" - -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"); - // Default physical address is 1.1.0 - physaddr.bytes.high = (/*area*/1 << 4) | /*line*/1; - physaddr.bytes.low = /*member*/0; - memset(callback_assignments, 0, MAX_CALLBACK_ASSIGNMENTS * sizeof(callback_assignment_t)); - memset(callbacks, 0, MAX_CALLBACKS * sizeof(callback_fptr_t)); - memset(custom_config_data, 0, MAX_CONFIG_SPACE * sizeof(uint8_t)); - memset(custom_config_default_data, 0, MAX_CONFIG_SPACE * sizeof(uint8_t)); - memset(custom_configs, 0, MAX_CONFIGS * sizeof(config_t)); -} - -void ESPKNXIP::load() -{ - memcpy(custom_config_default_data, custom_config_data, MAX_CONFIG_SPACE); - EEPROM.begin(EEPROM_SIZE); - restore_from_eeprom(); -} - -void ESPKNXIP::start(ESP8266WebServer *srv) -{ - server = srv; - __start(); -} - -void ESPKNXIP::start() -{ - server = new ESP8266WebServer(80); - __start(); -} - -void ESPKNXIP::__start() -{ - if (server != nullptr) - { - server->on(ROOT_PREFIX, [this](){ - __handle_root(); - }); - server->on(__ROOT_PATH, [this](){ - __handle_root(); - }); - server->on(__REGISTER_PATH, [this](){ - __handle_register(); - }); - server->on(__DELETE_PATH, [this](){ - __handle_delete(); - }); - server->on(__PHYS_PATH, [this](){ - __handle_set(); - }); -#if !DISABLE_EEPROM_BUTTONS - server->on(__EEPROM_PATH, [this](){ - __handle_eeprom(); - }); -#endif - server->on(__CONFIG_PATH, [this](){ - __handle_config(); - }); - server->on(__FEEDBACK_PATH, [this](){ - __handle_feedback(); - }); -#if !DISABLE_RESTORE_BUTTON - server->on(__RESTORE_PATH, [this](){ - __handle_restore(); - }); -#endif -#if !DISABLE_REBOOT_BUTTON - server->on(__REBOOT_PATH, [this](){ - __handle_reboot(); - }); -#endif - server->begin(); - } - udp.beginMulticast(WiFi.localIP(), MULTICAST_IP, MULTICAST_PORT); -} - -void ESPKNXIP::save_to_eeprom() -{ - uint32_t address = 0; - uint64_t magic = EEPROM_MAGIC; - EEPROM.put(address, magic); - address += sizeof(uint64_t); - EEPROM.put(address++, registered_callback_assignments); - for (uint8_t i = 0; i < MAX_CALLBACK_ASSIGNMENTS; ++i) - { - EEPROM.put(address, callback_assignments[i].address); - address += sizeof(address_t); - } - for (uint8_t i = 0; i < MAX_CALLBACK_ASSIGNMENTS; ++i) - { - EEPROM.put(address, callback_assignments[i].callback_id); - address += sizeof(callback_id_t); - } - EEPROM.put(address, physaddr); - address += sizeof(address_t); - - EEPROM.put(address, custom_config_data); - address += sizeof(custom_config_data); - - EEPROM.commit(); - DEBUG_PRINT("Wrote to EEPROM: 0x"); - DEBUG_PRINTLN(address, HEX); -} - -void ESPKNXIP::restore_from_eeprom() -{ - uint32_t address = 0; - uint64_t magic = 0; - EEPROM.get(address, magic); - if (magic != EEPROM_MAGIC) - { - DEBUG_PRINTLN("No valid magic in EEPROM, aborting restore."); - DEBUG_PRINT("Expected 0x"); - DEBUG_PRINT((unsigned long)(EEPROM_MAGIC >> 32), HEX); - DEBUG_PRINT(" 0x"); - DEBUG_PRINT((unsigned long)(EEPROM_MAGIC), HEX); - DEBUG_PRINT(" got 0x"); - DEBUG_PRINT((unsigned long)(magic >> 32), HEX); - DEBUG_PRINT(" 0x"); - DEBUG_PRINTLN((unsigned long)magic, HEX); - return; - } - address += sizeof(uint64_t); - EEPROM.get(address++, registered_callback_assignments); - 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) - { - EEPROM.get(address, callback_assignments[i].callback_id); - address += sizeof(callback_id_t); - } - EEPROM.get(address, physaddr); - address += sizeof(address_t); - - //EEPROM.get(address, custom_config_data); - //address += sizeof(custom_config_data); - uint32_t conf_offset = address; - for (uint8_t i = 0; i < registered_configs; ++i) - { - // First byte is flags. - config_flags_t flags = CONFIG_FLAGS_NO_FLAGS; - flags = (config_flags_t)EEPROM.read(address); - DEBUG_PRINT("Flag in EEPROM @ "); - DEBUG_PRINT(address - conf_offset); - DEBUG_PRINT(": "); - DEBUG_PRINTLN(flags, BIN); - custom_config_data[custom_configs[i].offset] = flags; - if (flags & CONFIG_FLAGS_VALUE_SET) - { - DEBUG_PRINTLN("Non-default value"); - for (int j = 0; j < custom_configs[i].len - sizeof(uint8_t); ++j) - { - custom_config_data[custom_configs[i].offset + sizeof(uint8_t) + j] = EEPROM.read(address + sizeof(uint8_t) + j); - } - } - - address += custom_configs[i].len; - } - - DEBUG_PRINT("Restored from EEPROM: 0x"); - DEBUG_PRINTLN(address, HEX); -} - -uint16_t ESPKNXIP::__ntohs(uint16_t n) -{ - return (uint16_t)((((uint8_t*)&n)[0] << 8) | (((uint8_t*)&n)[1])); -} - -callback_assignment_id_t ESPKNXIP::__callback_register_assignment(address_t address, callback_id_t id) -{ - 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++; - return aid; - } - else - { - // 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; - } - } - return -1; -} - -void ESPKNXIP::__callback_delete_assignment(callback_assignment_id_t id) -{ - // TODO this can be optimized if we are deleting the last element - // as then we can decrement registered_callback_assignments - - // 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) - { - DEBUG_PRINTLN("really last cba"); - // If this was the last, then we are done. - return; - } - - id--; - while(true) - { - 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 - { - DEBUG_PRINTLN("aborted on used slot"); - // Slot is used, abort - return; - } - id--; - if (id == CALLBACK_ASSIGNMENT_ID_MAX) - { - 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++; - } -} - -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) -{ - 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++; - return id; - } - else - { - // find the free slot - for (callback_id_t id = 0; id < registered_callbacks; ++id) - { - if (callbacks[id].slot_flags & SLOT_FLAGS_USED) - { - // 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; - } - } - return -1; -} - -void ESPKNXIP::callback_deregister(callback_id_t id) -{ - if (!__callback_is_id_valid(id)) - return; - - // 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); -} - -/** - * Feedback functions start here - */ - -feedback_id_t ESPKNXIP::feedback_register_int(String name, int32_t *value, enable_condition_t cond) -{ - if (registered_feedbacks >= MAX_FEEDBACKS) - return -1; - - feedback_id_t id = registered_feedbacks; - - feedbacks[id].type = FEEDBACK_TYPE_INT; - feedbacks[id].name = name; - feedbacks[id].cond = cond; - feedbacks[id].data = (void *)value; - - registered_feedbacks++; - - return id; -} - -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; - - feedback_id_t id = registered_feedbacks; - - feedbacks[id].type = FEEDBACK_TYPE_FLOAT; - feedbacks[id].name = name; - 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, char const *true_text, char const *false_text, enable_condition_t cond) -{ - if (registered_feedbacks >= MAX_FEEDBACKS) - return -1; - - feedback_id_t id = registered_feedbacks; - - feedbacks[id].type = FEEDBACK_TYPE_BOOL; - 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, const char *btn_text, void *arg, enable_condition_t cond) -{ - if (registered_feedbacks >= MAX_FEEDBACKS) - return -1; - - feedback_id_t id = registered_feedbacks; - - feedbacks[id].type = FEEDBACK_TYPE_ACTION; - feedbacks[id].name = name; - 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++; - - return id; -} - -void ESPKNXIP::loop() -{ - __loop_knx(); - if (server != nullptr) - { - __loop_webserver(); - } -} - -void ESPKNXIP::__loop_webserver() -{ - server->handleClient(); -} - -void ESPKNXIP::__loop_knx() -{ - int read = udp.parsePacket(); - - if (!read) - { - return; - } - DEBUG_PRINTLN(F("")); - DEBUG_PRINT(F("LEN: ")); - DEBUG_PRINTLN(read); - - uint8_t buf[read]; - udp.read(buf, read); - - DEBUG_PRINT(F("Got packet:")); - -#ifdef ESP_KNX_DEBUG - - for (int i = 0; i < read; ++i) - - { - DEBUG_PRINT(F(" 0x")); - DEBUG_PRINT(buf[i], 16); - } - -#endif - - DEBUG_PRINTLN(F("")); - - knx_ip_pkt_t *knx_pkt = (knx_ip_pkt_t *)buf; - - DEBUG_PRINT(F("ST: 0x")); - DEBUG_PRINTLN(__ntohs(knx_pkt->service_type), 16); - - if (knx_pkt->header_len != 0x06 && knx_pkt->protocol_version != 0x10 && knx_pkt->service_type != KNX_ST_ROUTING_INDICATION) - return; - - cemi_msg_t *cemi_msg = (cemi_msg_t *)knx_pkt->pkt_data; - - DEBUG_PRINT(F("MT: 0x")); - DEBUG_PRINTLN(cemi_msg->message_code, 16); - - if (cemi_msg->message_code != KNX_MT_L_DATA_IND) - return; - - DEBUG_PRINT(F("ADDI: 0x")); - DEBUG_PRINTLN(cemi_msg->additional_info_len, 16); - - cemi_service_t *cemi_data = &cemi_msg->data.service_information; - - if (cemi_msg->additional_info_len > 0) - cemi_data = (cemi_service_t *)(((uint8_t *)cemi_data) + cemi_msg->additional_info_len); - - DEBUG_PRINT(F("C1: 0x")); - DEBUG_PRINTLN(cemi_data->control_1.byte, 16); - - DEBUG_PRINT(F("C2: 0x")); - DEBUG_PRINTLN(cemi_data->control_2.byte, 16); - - DEBUG_PRINT(F("DT: 0x")); - DEBUG_PRINTLN(cemi_data->control_2.bits.dest_addr_type, 16); - - if (cemi_data->control_2.bits.dest_addr_type != 0x01) - return; - - DEBUG_PRINT(F("HC: 0x")); - DEBUG_PRINTLN(cemi_data->control_2.bits.hop_count, 16); - - DEBUG_PRINT(F("EFF: 0x")); - DEBUG_PRINTLN(cemi_data->control_2.bits.extended_frame_format, 16); - - DEBUG_PRINT(F("Source: 0x")); - DEBUG_PRINT(cemi_data->source.bytes.high, 16); - DEBUG_PRINT(F(" 0x")); - DEBUG_PRINTLN(cemi_data->source.bytes.low, 16); - - DEBUG_PRINT(F("Dest: 0x")); - DEBUG_PRINT(cemi_data->destination.bytes.high, 16); - DEBUG_PRINT(F(" 0x")); - DEBUG_PRINTLN(cemi_data->destination.bytes.low, 16); - - knx_command_type_t ct = (knx_command_type_t)(((cemi_data->data[0] & 0xC0) >> 6) | ((cemi_data->pci.apci & 0x03) << 2)); - - DEBUG_PRINT(F("CT: 0x")); - DEBUG_PRINTLN(ct, 16); - -#ifdef ESP_KNX_DEBUG - for (int i = 0; i < cemi_data->data_len; ++i) - { - DEBUG_PRINT(F(" 0x")); - DEBUG_PRINT(cemi_data->data[i], 16); - } -#endif - - DEBUG_PRINTLN(F("==")); - - // Call callbacks - for (int i = 0; i < registered_callback_assignments; ++i) - { - DEBUG_PRINT(F("Testing: 0x")); - DEBUG_PRINT(callback_assignments[i].address.bytes.high, 16); - DEBUG_PRINT(F(" 0x")); - DEBUG_PRINTLN(callback_assignments[i].address.bytes.low, 16); - if (cemi_data->destination.value == callback_assignments[i].address.value) - { - DEBUG_PRINTLN(F("Found match")); - if (callbacks[callback_assignments[i].callback_id].cond && !callbacks[callback_assignments[i].callback_id].cond()) - { - DEBUG_PRINTLN(F("But it's disabled")); -#if ALLOW_MULTIPLE_CALLBACKS_PER_ADDRESS - continue; -#else - return; -#endif - } - uint8_t data[cemi_data->data_len]; - memcpy(data, cemi_data->data, cemi_data->data_len); - data[0] = data[0] & 0x3F; - message_t msg = {}; - msg.ct = ct; - msg.received_on = cemi_data->destination; - msg.data_len = cemi_data->data_len; - msg.data = data; - callbacks[callback_assignments[i].callback_id].fkt(msg, callbacks[callback_assignments[i].callback_id].arg); -#if ALLOW_MULTIPLE_CALLBACKS_PER_ADDRESS - continue; -#else - return; -#endif - } - } - - return; -} - -// Global "singleton" object -ESPKNXIP knx; diff --git a/lib/esp-knx-ip-0.5.2/esp-knx-ip.h b/lib/esp-knx-ip-0.5.2/esp-knx-ip.h deleted file mode 100644 index 6834a6125..000000000 --- a/lib/esp-knx-ip-0.5.2/esp-knx-ip.h +++ /dev/null @@ -1,600 +0,0 @@ -/** - * esp-knx-ip library for KNX/IP communication on an ESP8266 - * Author: Nico Weichbrodt - * License: MIT - */ - -#ifndef ESP_KNX_IP_H -#define ESP_KNX_IP_H - -/** - * CONFIG - * All MAX_ values must not exceed 255 (1 byte, except MAC_CONFIG_SPACE which can go up to 2 bytes, so 0xffff in theory) and must not be negative! - * Config space is restriced by EEPROM_SIZE (default 1024). - * Required EEPROM size is 8 + MAX_GA_CALLBACKS * 3 + 2 + MAX_CONFIG_SPACE which is 552 by default - */ -#define EEPROM_SIZE 1024 // [Default 1024] -#define MAX_CALLBACK_ASSIGNMENTS 10 // [Default 10] Maximum number of group address callbacks that can be stored -#define MAX_CALLBACKS 10 // [Default 10] Maximum number of callbacks that can be stored -#define MAX_CONFIGS 20 // [Default 20] Maximum number of config items that can be stored -#define MAX_CONFIG_SPACE 0x0200 // [Default 0x0200] Maximum number of bytes that can be stored for custom config - -#define MAX_FEEDBACKS 20 // [Default 20] Maximum number of feedbacks that can be shown - -// Callbacks -#define ALLOW_MULTIPLE_CALLBACKS_PER_ADDRESS 1 // [Default 0] Set to 1 to always test all assigned callbacks. This allows for multiple callbacks being assigned to the same address. If disabled, only the first assigned will be called. - -// Webserver related -#define USE_BOOTSTRAP 0 // [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:///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. - -// These values normally don't need adjustment -#ifndef MULTICAST_IP -#define MULTICAST_IP IPAddress(224, 0, 23, 12) // [Default IPAddress(224, 0, 23, 12)] -#else -#warning USING CUSTOM MULTICAST_IP -#endif - -#ifndef MULTICAST_PORT -#define MULTICAST_PORT 3671 // [Default 3671] -#else -#warning USING CUSTOM MULTICAST_PORT -#endif - -#define SEND_CHECKSUM 0 - -// Uncomment to enable printing out debug messages. -//#define ESP_KNX_DEBUG -/** - * END CONFIG - */ - -#include "Arduino.h" -#include -#include -#include -#include - -#include "DPT.h" - -#define EEPROM_MAGIC (0xDEADBEEF00000000 + (MAX_CONFIG_SPACE) + (MAX_CALLBACK_ASSIGNMENTS << 16) + (MAX_CALLBACKS << 8)) - -// Define where debug output will be printed. -#ifndef DEBUG_PRINTER -#define DEBUG_PRINTER Serial -#endif - -// Setup debug printing macros. -#ifdef ESP_KNX_DEBUG - #define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); } - #define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); } -#else - #define DEBUG_PRINT(...) {} - #define DEBUG_PRINTLN(...) {} -#endif - -#define __ROOT_PATH ROOT_PREFIX"/" -#define __REGISTER_PATH ROOT_PREFIX"/register" -#define __DELETE_PATH ROOT_PREFIX"/delete" -#define __PHYS_PATH ROOT_PREFIX"/phys" -#define __EEPROM_PATH ROOT_PREFIX"/eeprom" -#define __CONFIG_PATH ROOT_PREFIX"/config" -#define __FEEDBACK_PATH ROOT_PREFIX"/feedback" -#define __RESTORE_PATH ROOT_PREFIX"/restore" -#define __REBOOT_PATH ROOT_PREFIX"/reboot" - -/** - * Different service types, we are mainly interested in KNX_ST_ROUTING_INDICATION - */ -typedef enum __knx_service_type -{ - KNX_ST_SEARCH_REQUEST = 0x0201, - KNX_ST_SEARCH_RESPONSE = 0x0202, - KNX_ST_DESCRIPTION_REQUEST = 0x0203, - KNX_ST_DESCRIPTION_RESPONSE = 0x0204, - KNX_ST_CONNECT_REQUEST = 0x0205, - KNX_ST_CONNECT_RESPONSE = 0x0206, - KNX_ST_CONNECTIONSTATE_REQUEST = 0x0207, - KNX_ST_CONNECTIONSTATE_RESPONSE = 0x0208, - KNX_ST_DISCONNECT_REQUEST = 0x0209, - KNX_ST_DISCONNECT_RESPONSE = 0x020A, - - KNX_ST_DEVICE_CONFIGURATION_REQUEST = 0x0310, - KNX_ST_DEVICE_CONFIGURATION_ACK = 0x0311, - - KNX_ST_TUNNELING_REQUEST = 0x0420, - KNX_ST_TUNNELING_ACK = 0x0421, - - KNX_ST_ROUTING_INDICATION = 0x0530, - KNX_ST_ROUTING_LOST_MESSAGE = 0x0531, - KNX_ST_ROUTING_BUSY = 0x0532, - -// KNX_ST_RLOG_START = 0x0600, -// KNX_ST_RLOG_END = 0x06FF, - - KNX_ST_REMOTE_DIAGNOSTIC_REQUEST = 0x0740, - KNX_ST_REMOTE_DIAGNOSTIC_RESPONSE = 0x0741, - KNX_ST_REMOTE_BASIC_CONFIGURATION_REQUEST = 0x0742, - KNX_ST_REMOTE_RESET_REQUEST = 0x0743, - -// KNX_ST_OBJSRV_START = 0x0800, -// KNX_ST_OBJSRV_END = 0x08FF, -} knx_service_type_t; - -/** - * Differnt command types, first three are of main interest - */ -typedef enum __knx_command_type -{ - KNX_CT_READ = 0x00, - KNX_CT_ANSWER = 0x01, - KNX_CT_WRITE = 0x02, - KNX_CT_INDIVIDUAL_ADDR_WRITE = 0x03, - KNX_CT_INDIVIDUAL_ADDR_REQUEST = 0x04, - KNX_CT_INDIVIDUAL_ADDR_RESPONSE = 0x05, - KNX_CT_ADC_READ = 0x06, - KNX_CT_ADC_ANSWER = 0x07, - KNX_CT_MEM_READ = 0x08, - KNX_CT_MEM_ANSWER = 0x09, - KNX_CT_MEM_WRITE = 0x0A, -//KNX_CT_UNKNOWN = 0x0B, - KNX_CT_MASK_VERSION_READ = 0x0C, - KNX_CT_MASK_VERSION_RESPONSE = 0x0D, - KNX_CT_RESTART = 0x0E, - KNX_CT_ESCAPE = 0x0F, -} knx_command_type_t; - -/** - * cEMI message types, mainly KNX_MT_L_DATA_IND is interesting - */ -typedef enum __knx_cemi_msg_type -{ - KNX_MT_L_DATA_REQ = 0x11, - KNX_MT_L_DATA_IND = 0x29, - KNX_MT_L_DATA_CON = 0x2E, -} knx_cemi_msg_type_t; - -/** - * TCPI communication type - */ -typedef enum __knx_communication_type { - KNX_COT_UDP = 0x00, // Unnumbered Data Packet - KNX_COT_NDP = 0x01, // Numbered Data Packet - KNX_COT_UCD = 0x02, // Unnumbered Control Data - KNX_COT_NCD = 0x03, // Numbered Control Data -} knx_communication_type_t; - -/** - * acpi for KNX_COT_NCD - */ -typedef enum __knx_cot_ncd_ack_type { - KNX_COT_NCD_ACK = 0x10, // Inform positively reception of the Previouly received telegram - KNX_COT_NCD_NACK = 0x11, // Inform negatively reception of the Previouly received telegram -} knx_cot_ncd_ack_type_t; - -/** - * KNX/IP header - */ -typedef struct __knx_ip_pkt -{ - uint8_t header_len; // Should always be 0x06 - uint8_t protocol_version; // Should be version 1.0, transmitted as 0x10 - uint16_t service_type; // See knx_service_type_t - union - { - struct { - uint8_t first_byte; - uint8_t second_byte; - } bytes; - uint16_t len; - } total_len; // header_len + rest of pkt. This is a bit weird as the spec says this: If the total number of bytes transmitted is greater than 252 bytes, the first “Total Length” byte is set to FF (255). Only in this case the second byte includes additional length information - uint8_t pkt_data[]; // This is of type cemi_msg_t -} knx_ip_pkt_t; - -typedef struct __cemi_addi -{ - uint8_t type_id; - uint8_t len; - uint8_t data[]; -} cemi_addi_t; - -typedef union __address -{ - uint16_t value; - struct - { - uint8_t high; - uint8_t low; - } bytes; - struct __attribute__((packed)) - { - uint8_t line:3; - uint8_t area:5; - uint8_t member; - } ga; - struct __attribute__((packed)) - { - uint8_t line:4; - uint8_t area:4; - uint8_t member; - } pa; - uint8_t array[2]; -} address_t; - -typedef struct __cemi_service -{ - union - { - struct - { - // Struct is reversed due to bit order - uint8_t confirm:1; // 0 = no error, 1 = error - uint8_t ack:1; // 0 = no ack, 1 = ack - uint8_t priority:2; // 0 = system, 1 = high, 2 = urgent/alarm, 3 = normal - uint8_t system_broadcast:1; // 0 = system broadcast, 1 = broadcast - uint8_t repeat:1; // 0 = repeated telegram, 1 = not repeated telegram - uint8_t reserved:1; // always zero - uint8_t frame_type:1; // 0 = extended, 1 = standard - } bits; - uint8_t byte; - } control_1; - union - { - struct - { - // Struct is reversed due to bit order - uint8_t extended_frame_format:4; - uint8_t hop_count:3; - uint8_t dest_addr_type:1; // 0 = individual, 1 = group - } bits; - uint8_t byte; - } control_2; - address_t source; - address_t destination; - uint8_t data_len; // length of data, excluding the tpci byte - struct - { - uint8_t apci:2; // If tpci.comm_type == KNX_COT_UCD or KNX_COT_NCD, then this is apparently control data? - uint8_t tpci_seq_number:4; - uint8_t tpci_comm_type:2; // See knx_communication_type_t - } pci; - uint8_t data[]; -} cemi_service_t; - -typedef struct __cemi_msg -{ - uint8_t message_code; - uint8_t additional_info_len; - union - { -// cemi_addi_t additional_info[]; // Errors in GCC 10.1 - cemi_addi_t additional_info[10]; // Changed to arbitrary number to fix compilation - cemi_service_t service_information; - } data; -} cemi_msg_t; - -typedef enum __config_type -{ - CONFIG_TYPE_UNKNOWN, - CONFIG_TYPE_INT, - CONFIG_TYPE_BOOL, - CONFIG_TYPE_STRING, - CONFIG_TYPE_OPTIONS, - CONFIG_TYPE_GA, -} config_type_t; - -typedef enum __feedback_type -{ - FEEDBACK_TYPE_UNKNOWN, - FEEDBACK_TYPE_INT, - FEEDBACK_TYPE_FLOAT, - FEEDBACK_TYPE_BOOL, - FEEDBACK_TYPE_ACTION, -} feedback_type_t; - -typedef enum __config_flags -{ - CONFIG_FLAGS_NO_FLAGS = 0, - 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; - address_t received_on; - uint8_t data_len; - uint8_t *data; -} message_t; - -typedef bool (*enable_condition_t)(void); -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 const *name; - uint8_t value; -} option_entry_t; - -typedef struct __config -{ - config_type_t type; - String name; - uint8_t offset; - uint8_t len; - enable_condition_t cond; - union { - option_entry_t *options; - } 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 -{ - feedback_type_t type; - String name; - 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; -} feedback_t; - -typedef struct __callback -{ - uint8_t slot_flags; - callback_fptr_t fkt; - enable_condition_t cond; - void *arg; - String name; -} callback_t; - -typedef struct __callback_assignment -{ - uint8_t slot_flags; - address_t address; - callback_id_t callback_id; -} callback_assignment_t; - -// FastPrecisePowf from tasmota/support_float.ino -//extern float FastPrecisePowf(const float x, const float y); - -class ESPKNXIP { - public: - ESPKNXIP(); - void load(); - void start(); - 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); - 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(); - - // Configuration functions - config_id_t config_register_string(String name, uint8_t len, String _default, enable_condition_t cond = nullptr); - config_id_t config_register_int(String name, int32_t _default, enable_condition_t cond = nullptr); - config_id_t config_register_bool(String name, bool _default, enable_condition_t cond = nullptr); - config_id_t config_register_options(String name, option_entry_t *options, uint8_t _default, enable_condition_t cond = nullptr); - config_id_t config_register_ga(String name, enable_condition_t cond = nullptr); - - String config_get_string(config_id_t id); - int32_t config_get_int(config_id_t id); - bool config_get_bool(config_id_t id); - uint8_t config_get_options(config_id_t id); - address_t config_get_ga(config_id_t id); - - void config_set_string(config_id_t id, String val); - void config_set_int(config_id_t id, int32_t val); - void config_set_bool(config_id_t, bool val); - void config_set_options(config_id_t id, uint8_t val); - void config_set_ga(config_id_t id, address_t const &val); - - // 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, 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); - - void send_1bit(address_t const &receiver, knx_command_type_t ct, uint8_t bit); - void send_2bit(address_t const &receiver, knx_command_type_t ct, uint8_t twobit); - void send_4bit(address_t const &receiver, knx_command_type_t ct, uint8_t fourbit); - void send_1byte_int(address_t const &receiver, knx_command_type_t ct, int8_t val); - void send_1byte_uint(address_t const &receiver, knx_command_type_t ct, uint8_t val); - void send_2byte_int(address_t const &receiver, knx_command_type_t ct, int16_t val); - void send_2byte_uint(address_t const &receiver, knx_command_type_t ct, uint16_t val); - void send_2byte_float(address_t const &receiver, knx_command_type_t ct, float val); - void send_3byte_time(address_t const &receiver, knx_command_type_t ct, uint8_t weekday, uint8_t hours, uint8_t minutes, uint8_t seconds); - void send_3byte_time(address_t const &receiver, knx_command_type_t ct, time_of_day_t const &time) { send_3byte_time(receiver, ct, time.weekday, time.hours, time.minutes, time.seconds); } - void send_3byte_date(address_t const &receiver, knx_command_type_t ct, uint8_t day, uint8_t month, uint8_t year); - void send_3byte_date(address_t const &receiver, knx_command_type_t ct, date_t const &date) { send_3byte_date(receiver, ct, date.day, date.month, date.year); } - void send_3byte_color(address_t const &receiver, knx_command_type_t ct, uint8_t red, uint8_t green, uint8_t blue); - void send_3byte_color(address_t const &receiver, knx_command_type_t ct, color_t const &color) { send_3byte_color(receiver, ct, color.red, color.green, color.blue); } - void send_4byte_int(address_t const &receiver, knx_command_type_t ct, int32_t val); - void send_4byte_uint(address_t const &receiver, knx_command_type_t ct, uint32_t val); - void send_4byte_float(address_t const &receiver, knx_command_type_t ct, float val); - void send_14byte_string(address_t const &receiver, knx_command_type_t ct, const char *val); - - void write_1bit(address_t const &receiver, uint8_t bit) { send_1bit(receiver, KNX_CT_WRITE, bit); } - void write_2bit(address_t const &receiver, uint8_t twobit) { send_2bit(receiver, KNX_CT_WRITE, twobit); } - void write_4bit(address_t const &receiver, uint8_t fourbit) { send_4bit(receiver, KNX_CT_WRITE, fourbit); } - void write_1byte_int(address_t const &receiver, int8_t val) { send_1byte_int(receiver, KNX_CT_WRITE, val); } - void write_1byte_uint(address_t const &receiver, uint8_t val) { send_1byte_uint(receiver, KNX_CT_WRITE, val); } - void write_2byte_int(address_t const &receiver, int16_t val) { send_2byte_int(receiver, KNX_CT_WRITE, val); } - void write_2byte_uint(address_t const &receiver, uint16_t val) { send_2byte_uint(receiver, KNX_CT_WRITE, val); } - void write_2byte_float(address_t const &receiver, float val) { send_2byte_float(receiver, KNX_CT_WRITE, val); } - void write_3byte_time(address_t const &receiver, uint8_t weekday, uint8_t hours, uint8_t minutes, uint8_t seconds) { send_3byte_time(receiver, KNX_CT_WRITE, weekday, hours, minutes, seconds); } - void write_3byte_time(address_t const &receiver, time_of_day_t const &time) { send_3byte_time(receiver, KNX_CT_WRITE, time.weekday, time.hours, time.minutes, time.seconds); } - void write_3byte_date(address_t const &receiver, uint8_t day, uint8_t month, uint8_t year) { send_3byte_date(receiver, KNX_CT_WRITE, day, month, year); } - void write_3byte_date(address_t const &receiver, date_t const &date) { send_3byte_date(receiver, KNX_CT_WRITE, date.day, date.month, date.year); } - void write_3byte_color(address_t const &receiver, uint8_t red, uint8_t green, uint8_t blue) { send_3byte_color(receiver, KNX_CT_WRITE, red, green, blue); } - void write_3byte_color(address_t const &receiver, color_t const &color) { send_3byte_color(receiver, KNX_CT_WRITE, color); } - void write_4byte_int(address_t const &receiver, int32_t val) { send_4byte_int(receiver, KNX_CT_WRITE, val); } - void write_4byte_uint(address_t const &receiver, uint32_t val) { send_4byte_uint(receiver, KNX_CT_WRITE, val); } - void write_4byte_float(address_t const &receiver, float val) { send_4byte_float(receiver, KNX_CT_WRITE, val);} - void write_14byte_string(address_t const &receiver, const char *val) { send_14byte_string(receiver, KNX_CT_WRITE, val); } - - void answer_1bit(address_t const &receiver, uint8_t bit) { send_1bit(receiver, KNX_CT_ANSWER, bit); } - void answer_2bit(address_t const &receiver, uint8_t twobit) { send_2bit(receiver, KNX_CT_ANSWER, twobit); } - void answer_4bit(address_t const &receiver, uint8_t fourbit) { send_4bit(receiver, KNX_CT_ANSWER, fourbit); } - void answer_1byte_int(address_t const &receiver, int8_t val) { send_1byte_int(receiver, KNX_CT_ANSWER, val); } - void answer_1byte_uint(address_t const &receiver, uint8_t val) { send_1byte_uint(receiver, KNX_CT_ANSWER, val); } - void answer_2byte_int(address_t const &receiver, int16_t val) { send_2byte_int(receiver, KNX_CT_ANSWER, val); } - void answer_2byte_uint(address_t const &receiver, uint16_t val) { send_2byte_uint(receiver, KNX_CT_ANSWER, val); } - void answer_2byte_float(address_t const &receiver, float val) { send_2byte_float(receiver, KNX_CT_ANSWER, val); } - void answer_3byte_time(address_t const &receiver, uint8_t weekday, uint8_t hours, uint8_t minutes, uint8_t seconds) { send_3byte_time(receiver, KNX_CT_ANSWER, weekday, hours, minutes, seconds); } - void answer_3byte_time(address_t const &receiver, time_of_day_t const &time) { send_3byte_time(receiver, KNX_CT_ANSWER, time.weekday, time.hours, time.minutes, time.seconds); } - void answer_3byte_date(address_t const &receiver, uint8_t day, uint8_t month, uint8_t year) { send_3byte_date(receiver, KNX_CT_ANSWER, day, month, year); } - void answer_3byte_date(address_t const &receiver, date_t const &date) { send_3byte_date(receiver, KNX_CT_ANSWER, date.day, date.month, date.year); } - void answer_3byte_color(address_t const &receiver, uint8_t red, uint8_t green, uint8_t blue) { send_3byte_color(receiver, KNX_CT_ANSWER, red, green, blue); } - void answer_3byte_color(address_t const &receiver, color_t const &color) { send_3byte_color(receiver, KNX_CT_ANSWER, color); } - void answer_4byte_int(address_t const &receiver, int32_t val) { send_4byte_int(receiver, KNX_CT_ANSWER, val); } - void answer_4byte_uint(address_t const &receiver, uint32_t val) { send_4byte_uint(receiver, KNX_CT_ANSWER, val); } - void answer_4byte_float(address_t const &receiver, float val) { send_4byte_float(receiver, KNX_CT_ANSWER, val);} - void answer_14byte_string(address_t const &receiver, const char *val) { send_14byte_string(receiver, KNX_CT_ANSWER, val); } - - bool data_to_bool(uint8_t *data); - int8_t data_to_1byte_int(uint8_t *data); - uint8_t data_to_1byte_uint(uint8_t *data); - int16_t data_to_2byte_int(uint8_t *data); - uint16_t data_to_2byte_uint(uint8_t *data); - float data_to_2byte_float(uint8_t *data); - color_t data_to_3byte_color(uint8_t *data); - time_of_day_t data_to_3byte_time(uint8_t *data); - date_t data_to_3byte_data(uint8_t *data); - int32_t data_to_4byte_int(uint8_t *data); - uint32_t data_to_4byte_uint(uint8_t *data); - float data_to_4byte_float(uint8_t *data); - - static address_t GA_to_address(uint8_t area, uint8_t line, uint8_t member) - { - // Yes, the order is correct, see the struct definition above - address_t tmp = {.ga={line, area, member}}; - return tmp; - } - - static address_t PA_to_address(uint8_t area, uint8_t line, uint8_t member) - { - // Yes, the order is correct, see the struct definition above - address_t tmp = {.pa={line, area, member}}; - return tmp; - } - - private: - void __start(); - - void __loop_knx(); - - // Webserver functions - void __loop_webserver(); - void __handle_root(); - void __handle_register(); - void __handle_delete(); - void __handle_set(); -#if !DISABLE_EEPROM_BUTTONS - void __handle_eeprom(); -#endif - void __handle_config(); - void __handle_feedback(); -#if !DISABLE_RESTORE_BUTTONS - void __handle_restore(); -#endif -#if !DISABLE_REBOOT_BUTTONS - void __handle_reboot(); -#endif - - void __config_set_flags(config_id_t id, config_flags_t flags); - - void __config_set_string(config_id_t id, String &val); - void __config_set_int(config_id_t id, int32_t val); - void __config_set_bool(config_id_t id, bool val); - 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); - - //static inline float pow(float a, float b) { return FastPrecisePowf(a, b); } - - ESP8266WebServer *server; - address_t physaddr; - - WiFiUDP 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; - uint8_t custom_config_data[MAX_CONFIG_SPACE]; - uint8_t custom_config_default_data[MAX_CONFIG_SPACE]; - config_t custom_configs[MAX_CONFIGS]; - - feedback_id_t registered_feedbacks; - feedback_t feedbacks[MAX_FEEDBACKS]; - - uint16_t __ntohs(uint16_t); -}; - -// Global "singleton" object -extern ESPKNXIP knx; - -#endif diff --git a/lib/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino b/lib/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino deleted file mode 100644 index 02621bbc6..000000000 --- a/lib/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This is an example showing a simple environment sensor based on a BME280 attached via I2C. - * This sketch was tested on a WeMos D1 mini - */ - -#include -#include - -// WiFi config here -const char* ssid = "myssid"; -const char* pass = "mypassword"; - -#define LED_PIN D4 -#define UPDATE_INTERVAL 10000 - -unsigned long next_change = 0; - -float last_temp = 0.0; -float last_hum = 0.0; -float last_pres = 0.0; - -config_id_t temp_ga, hum_ga, pres_ga; -config_id_t hostname_id; -config_id_t update_rate_id, send_rate_id; -config_id_t enable_sending_id; -config_id_t enable_reading_id; - -Adafruit_BME280 bme; - -void setup() { - pinMode(LED_PIN, OUTPUT); - Serial.begin(115200); - - hostname_id = knx.config_register_string("Hostname", 20, String("env")); - enable_sending_id = knx.config_register_bool("Send on update", true); - update_rate_id = knx.config_register_int("Update rate (ms)", UPDATE_INTERVAL); - temp_ga = knx.config_register_ga("Temperature", show_periodic_options); - hum_ga = knx.config_register_ga("Humidity", show_periodic_options); - pres_ga = knx.config_register_ga("Pressure", show_periodic_options); - - knx.callback_register("Read Temperature", temp_cb); - knx.callback_register("Read Humidity", hum_cb); - knx.callback_register("Read Pressure", pres_cb); - - knx.feedback_register_float("Temperature (°C)", &last_temp); - knx.feedback_register_float("Humidity (%)", &last_hum); - knx.feedback_register_float("Pressure (hPa)", &last_pres, 0); - - // Load previous config from EEPROM - knx.load(); - - // Init sensor - if (!bme.begin(0x76)) { - Serial.println("Could not find a valid BME280 sensor, check wiring!"); - } - - // Init WiFi - WiFi.hostname(knx.config_get_string(hostname_id)); - WiFi.begin(ssid, pass); - - Serial.println(""); - Serial.print("[Connecting]"); - Serial.print(ssid); - - digitalWrite(LED_PIN, LOW); - while (WiFi.status() != WL_CONNECTED) { - digitalWrite(LED_PIN, HIGH); - delay(250); - Serial.print("."); - digitalWrite(LED_PIN, LOW); - delay(250); - } - digitalWrite(LED_PIN, HIGH); - - // Start knx - knx.start(); - - Serial.println(); - Serial.println("Connected to wifi"); - Serial.println(WiFi.localIP()); -} - -void loop() { - knx.loop(); - - unsigned long now = millis(); - - if (next_change < now) - { - next_change = now + knx.config_get_int(update_rate_id); - - last_temp = bme.readTemperature(); - last_hum = bme.readHumidity(); - last_pres = bme.readPressure()/100.0f; - - Serial.print("T: "); - Serial.print(last_temp); - Serial.print("°C H: "); - Serial.print(last_hum); - Serial.print("% P: "); - Serial.print(last_pres); - Serial.println("hPa"); - - if (knx.config_get_bool(enable_sending_id)) - { - knx.write_2byte_float(knx.config_get_ga(temp_ga), last_temp); - knx.write_2byte_float(knx.config_get_ga(hum_ga), last_hum); - knx.write_2byte_float(knx.config_get_ga(pres_ga), last_pres); - } - } - - delay(50); -} - -bool show_periodic_options() -{ - return knx.config_get_bool(enable_sending_id); -} - -bool enable_reading_callback() -{ - return knx.config_get_bool(enable_reading_id); -} - -void temp_cb(message_t const &msg, void *arg) -{ - switch (msg.ct) - { - case KNX_CT_READ: - { - knx.answer_2byte_float(msg.received_on, last_temp); - break; - } - } -} - -void hum_cb(message_t const &msg, void *arg) -{ - switch (msg.ct) - { - case KNX_CT_READ: - { - knx.answer_2byte_float(msg.received_on, last_hum); - break; - } - } -} - -void pres_cb(message_t const &msg, void *arg) -{ - switch (msg.ct) - { - case KNX_CT_READ: - { - knx.answer_2byte_float(msg.received_on, last_pres); - break; - } - } -} diff --git a/lib/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino b/lib/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino deleted file mode 100644 index fc2400b7b..000000000 --- a/lib/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino +++ /dev/null @@ -1,183 +0,0 @@ -#include - -// WiFi config here -const char* ssid = "ssid"; -const char* pass = "pass"; - -// Common -#define LED_PIN 13 - -// For Basic and S20 -#define BTN1_PIN 0 -#define CH1_PIN 12 - -// For 4CH -#define BTN2_PIN 9 -#define CH2_PIN 5 -#define BTN3_PIN 10 -#define CH3_PIN 4 -#define BTN4_PIN 14 -#define CH4_PIN 15 - -typedef enum __type_option -{ - SONOFF_TYPE_NONE = 0, - SONOFF_TYPE_BASIC = 1, - SONOFF_TYPE_S20 = 2, - SONOFF_TYPE_4CH = 3, - SONOFF_TYPE_4CH_PRO = 4, -} type_option_t; - -option_entry_t type_options[] = { - {"Sonoff Basic", SONOFF_TYPE_BASIC}, - {"Sonoff S20", SONOFF_TYPE_S20}, - {"Sonoff 4CH", SONOFF_TYPE_4CH}, - {"Sonoff 4CH Pro", SONOFF_TYPE_4CH_PRO}, - {nullptr, 0} -}; - -config_id_t hostname_id; -config_id_t type_id; - -typedef struct __sonoff_channel -{ - int pin; - int btn_pin; - config_id_t status_ga_id; - bool state; - bool last_btn_state; -} sonoff_channel_t; - -sonoff_channel_t channels[] = { - {CH1_PIN, BTN1_PIN, 0, false, false}, - {CH2_PIN, BTN2_PIN, 0, false, false}, - {CH3_PIN, BTN3_PIN, 0, false, false}, - {CH4_PIN, BTN4_PIN, 0, false, false}, -}; - -void setup() -{ - pinMode(LED_PIN, OUTPUT); - pinMode(BTN1_PIN, INPUT_PULLUP); - pinMode(BTN2_PIN, INPUT_PULLUP); - pinMode(BTN3_PIN, INPUT_PULLUP); - pinMode(BTN4_PIN, INPUT_PULLUP); - pinMode(CH1_PIN, OUTPUT); - pinMode(CH2_PIN, OUTPUT); - pinMode(CH3_PIN, OUTPUT); - pinMode(CH4_PIN, OUTPUT); - Serial.begin(115200); - - // Register the config options - hostname_id = knx.config_register_string("Hostname", 20, String("sonoff")); - type_id = knx.config_register_options("Type", type_options, SONOFF_TYPE_BASIC); - - channels[0].status_ga_id = knx.config_register_ga("Channel 1 Status GA"); - channels[1].status_ga_id = knx.config_register_ga("Channel 2 Status GA", is_4ch_or_4ch_pro); - channels[2].status_ga_id = knx.config_register_ga("Channel 3 Status GA", is_4ch_or_4ch_pro); - channels[3].status_ga_id = knx.config_register_ga("Channel 4 Status GA", is_4ch_or_4ch_pro); - - knx.callback_register("Channel 1", channel_cb, &channels[0]); - knx.callback_register("Channel 2", channel_cb, &channels[1], is_4ch_or_4ch_pro); - knx.callback_register("Channel 3", channel_cb, &channels[2], is_4ch_or_4ch_pro); - knx.callback_register("Channel 4", channel_cb, &channels[3], is_4ch_or_4ch_pro); - - knx.feedback_register_bool("Channel 1 is on", &(channels[0].state)); - knx.feedback_register_action("Toogle channel 1", toggle_chan, &channels[0]); - knx.feedback_register_bool("Channel 2 is on", &(channels[1].state), is_4ch_or_4ch_pro); - knx.feedback_register_action("Toogle channel 2", toggle_chan, &channels[1], is_4ch_or_4ch_pro); - knx.feedback_register_bool("Channel 3 is on", &(channels[2].state), is_4ch_or_4ch_pro); - knx.feedback_register_action("Toogle channel 3", toggle_chan, &channels[2], is_4ch_or_4ch_pro); - knx.feedback_register_bool("Channel 4 is on", &(channels[3].state), is_4ch_or_4ch_pro); - knx.feedback_register_action("Toogle channel 4", toggle_chan, &channels[3], is_4ch_or_4ch_pro); - - knx.load(); - - // Init WiFi - WiFi.hostname(knx.config_get_string(hostname_id)); - WiFi.begin(ssid, pass); - - Serial.println(""); - Serial.print("[Connecting]"); - Serial.print(ssid); - - digitalWrite(LED_PIN, LOW); - while (WiFi.status() != WL_CONNECTED) { - digitalWrite(LED_PIN, HIGH); - delay(500); - Serial.print("."); - digitalWrite(LED_PIN, LOW); - } - digitalWrite(LED_PIN, HIGH); - - // Start knx - knx.start(); - - Serial.println(); - Serial.println("Connected to wifi"); - Serial.println(WiFi.localIP()); -} - -void loop() -{ - knx.loop(); - - // Check local buttons - check_button(&channels[0]); - if (is_4ch_or_4ch_pro()) - { - check_button(&channels[1]); - check_button(&channels[2]); - check_button(&channels[3]); - } - - delay(50); -} - -bool is_basic_or_s20() -{ - uint8_t type = knx.config_get_options(type_id); - return type == SONOFF_TYPE_BASIC || type == SONOFF_TYPE_S20; -} - -bool is_4ch_or_4ch_pro() -{ - uint8_t type = knx.config_get_options(type_id); - return type == SONOFF_TYPE_4CH ||type == SONOFF_TYPE_4CH_PRO; -} - -void check_button(sonoff_channel_t *chan) -{ - bool state_now = digitalRead(chan->btn_pin) == HIGH ? true : false; - if (state_now != chan->last_btn_state && state_now == LOW) - { - chan->state = !chan->state; - digitalWrite(chan->pin, chan->state ? HIGH : LOW); - knx.write_1bit(knx.config_get_ga(chan->status_ga_id), chan->state); - } - chan->last_btn_state = state_now; -} - -void toggle_chan(void *arg) -{ - sonoff_channel_t *chan = (sonoff_channel_t *)arg; - chan->state = !chan->state; - digitalWrite(chan->pin, chan->state ? HIGH : LOW); - knx.write_1bit(knx.config_get_ga(chan->status_ga_id), chan->state); -} - -void channel_cb(message_t const &msg, void *arg) -{ - sonoff_channel_t *chan = (sonoff_channel_t *)arg; - switch (msg.ct) - { - case KNX_CT_WRITE: - chan->state = msg.data[0]; - Serial.println(chan->state ? "Toggle on" : "Toggle off"); - digitalWrite(chan->pin, chan->state ? HIGH : LOW); - knx.write_1bit(knx.config_get_ga(chan->status_ga_id), chan->state); - break; - case KNX_CT_READ: - knx.answer_1bit(msg.received_on, chan->state); - } -} diff --git a/lib/esp-knx-ip-0.5.2/examples/static-config/static-config.ino b/lib/esp-knx-ip-0.5.2/examples/static-config/static-config.ino deleted file mode 100644 index 54472dda3..000000000 --- a/lib/esp-knx-ip-0.5.2/examples/static-config/static-config.ino +++ /dev/null @@ -1,142 +0,0 @@ -/* - * This is an example showing a simple environment sensor based on a BME280 attached via I2C. - * It shows, how the library can used to statically configure a device without a webserver for config. - * This sketch was tested on a WeMos D1 mini - */ - -#include -#include - -// WiFi config here -const char* ssid = "myssid"; -const char* pass = "mypassword"; - -#define LED_PIN D4 -#define UPDATE_INTERVAL 10000 - -unsigned long next_change = 0; - -float last_temp = 0.0; -float last_hum = 0.0; -float last_pres = 0.0; - -Adafruit_BME280 bme; - -// Group addresses to send to (1/1/1, 1/1/2 and 1/1/3) -address_t temp_ga = knx.GA_to_address(1, 1, 1); -address_t hum_ga = knx.GA_to_address(1, 1, 2); -address_t pres_ga = knx.GA_to_address(1, 1, 3); - -void setup() { - pinMode(LED_PIN, OUTPUT); - Serial.begin(115200); - - callback_id_t temp_cb_id = knx.callback_register("Read Temperature", temp_cb); - callback_id_t hum_cb_id =knx.callback_register("Read Humidity", hum_cb); - callback_id_t pres_cb_id =knx.callback_register("Read Pressure", pres_cb); - - // Assign callbacks to group addresses (2/1/1, 2/1/2, 2/1/3) - knx.callback_assign(temp_cb_id, knx.GA_to_address(2, 1, 1)); - knx.callback_assign(hum_cb_id, knx.GA_to_address(2, 1, 2)); - knx.callback_assign(pres_cb_id, knx.GA_to_address(2, 1, 3)); - - // Set physical address (1.1.1) - knx.physical_address_set(knx.PA_to_address(1, 1, 1)); - - // Do not call knx.load() for static config, it will try to load config from EEPROM which we don't have here - - // Init sensor - if (!bme.begin(0x76)) { - Serial.println("Could not find a valid BME280 sensor, check wiring!"); - } - - // Init WiFi - WiFi.hostname("env"); - WiFi.begin(ssid, pass); - - Serial.println(""); - Serial.print("[Connecting]"); - Serial.print(ssid); - - digitalWrite(LED_PIN, LOW); - while (WiFi.status() != WL_CONNECTED) { - digitalWrite(LED_PIN, HIGH); - delay(250); - Serial.print("."); - digitalWrite(LED_PIN, LOW); - delay(250); - } - digitalWrite(LED_PIN, HIGH); - - // Start knx, disable webserver by passing nullptr - knx.start(nullptr); - - Serial.println(); - Serial.println("Connected to wifi"); - Serial.println(WiFi.localIP()); -} - -void loop() { - knx.loop(); - - unsigned long now = millis(); - - if (next_change < now) - { - next_change = now + UPDATE_INTERVAL; - - last_temp = bme.readTemperature(); - last_hum = bme.readHumidity(); - last_pres = bme.readPressure()/100.0f; - - Serial.print("T: "); - Serial.print(last_temp); - Serial.print("°C H: "); - Serial.print(last_hum); - Serial.print("% P: "); - Serial.print(last_pres); - Serial.println("hPa"); - - knx.write_2byte_float(temp_ga, last_temp); - knx.write_2byte_float(hum_ga, last_hum); - knx.write_2byte_float(pres_ga, last_pres); - } - - delay(50); -} - -void temp_cb(message_t const &msg, void *arg) -{ - switch (msg.ct) - { - case KNX_CT_READ: - { - knx.answer_2byte_float(msg.received_on, last_temp); - break; - } - } -} - -void hum_cb(message_t const &msg, void *arg) -{ - switch (msg.ct) - { - case KNX_CT_READ: - { - knx.answer_2byte_float(msg.received_on, last_hum); - break; - } - } -} - -void pres_cb(message_t const &msg, void *arg) -{ - switch (msg.ct) - { - case KNX_CT_READ: - { - knx.answer_2byte_float(msg.received_on, last_pres); - break; - } - } -} diff --git a/lib/esp-knx-ip-0.5.2/keywords.txt b/lib/esp-knx-ip-0.5.2/keywords.txt deleted file mode 100644 index 59836ef05..000000000 --- a/lib/esp-knx-ip-0.5.2/keywords.txt +++ /dev/null @@ -1,107 +0,0 @@ -# datatypes -address_t KEYWORD1 DATA_TYPE -message_t KEYWORD1 DATA_TYPE -callback_id_t KEYWORD1 DATA_TYPE -callback_assignment_id_t KEYWORD1 DATA_TYPE -option_entry_t KEYWORD1 DATA_TYPE -config_id_t KEYWORD1 DATA_TYPE -enable_condition_t KEYWORD1 DATA_TYPE -callback_fptr_t KEYWORD1 DATA_TYPE -feedback_action_fptr_t KEYWORD1 DATA_TYPE -knx_command_type_t KEYWORD1 DATA_TYPE - -# methods -setup KEYWORD2 -loop KEYWORD2 -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_bool KEYWORD2 -config_register_options KEYWORD2 -config_register_ga KEYWORD2 -config_get_string KEYWORD2 -config_get_int KEYWORD2 -config_get_bool KEYWORD2 -config_get_options KEYWORD2 -config_get_ga KEYWORD2 -config_set_string KEYWORD2 -config_set_int 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 -feedback_register_action KEYWORD2 -send_1bit KEYWORD2 -send_2bit KEYWORD2 -send_4bit KEYWORD2 -send_1byte_int KEYWORD2 -send_1byte_uint KEYWORD2 -send_2byte_int KEYWORD2 -send_2byte_uint KEYWORD2 -send_2byte_float KEYWORD2 -send_3byte_time KEYWORD2 -send_3byte_time KEYWORD2 -send_3byte_date KEYWORD2 -send_3byte_date KEYWORD2 -send_3byte_color KEYWORD2 -send_3byte_color KEYWORD2 -send_4byte_int KEYWORD2 -send_4byte_uint KEYWORD2 -send_4byte_float KEYWORD2 -send_14byte_string KEYWORD2 -write_1bit KEYWORD2 -write_2bit KEYWORD2 -write_4bit KEYWORD2 -write_1byte_int KEYWORD2 -write_1byte_uint KEYWORD2 -write_2byte_int KEYWORD2 -write_2byte_uint KEYWORD2 -write_2byte_float KEYWORD2 -write_3byte_time KEYWORD2 -write_3byte_time KEYWORD2 -write_3byte_date KEYWORD2 -write_3byte_date KEYWORD2 -write_3byte_color KEYWORD2 -write_3byte_color KEYWORD2 -write_4byte_int KEYWORD2 -write_4byte_uint KEYWORD2 -write_4byte_float KEYWORD2 -write_14byte_string KEYWORD2 -answer_1bit KEYWORD2 -answer_2bit KEYWORD2 -answer_4bit KEYWORD2 -answer_1byte_int KEYWORD2 -answer_1byte_uint KEYWORD2 -answer_2byte_int KEYWORD2 -answer_2byte_uint KEYWORD2 -answer_2byte_float KEYWORD2 -answer_3byte_time KEYWORD2 -answer_3byte_time KEYWORD2 -answer_3byte_date KEYWORD2 -answer_3byte_date KEYWORD2 -answer_3byte_color KEYWORD2 -answer_3byte_color KEYWORD2 -answer_4byte_int KEYWORD2 -answer_4byte_uint KEYWORD2 -answer_4byte_float KEYWORD2 -answer_14byte_string KEYWORD2 - -data_to_1byte_int KEYWORD2 -data_to_2byte_int KEYWORD2 -data_to_2byte_float KEYWORD2 -data_to_4byte_float KEYWORD2 -data_to_3byte_color KEYWORD2 -data_to_3byte_time KEYWORD2 -data_to_3byte_data KEYWORD2 - -# constants -knx LITERAL1 diff --git a/lib/esp-knx-ip-0.5.2/library.properties b/lib/esp-knx-ip-0.5.2/library.properties deleted file mode 100644 index f3b86de9c..000000000 --- a/lib/esp-knx-ip-0.5.2/library.properties +++ /dev/null @@ -1,10 +0,0 @@ -name=ESP KNX IP Library -version=0.5.2 -author=Nico Weichbrodt -maintainer=Nico Weichbrodt -sentence=ESP8266 library for KNX/IP communication. -paragraph=Build your own IoT devices with KNX/IP connectivity! -category=Communication -url=https://github.com/envy/esp-knx-ip -architectures=esp8266 -includes=esp-knx-ip.h