diff --git a/gen_parser.sh b/gen_parser.sh index 05df11d..64ffdab 100755 --- a/gen_parser.sh +++ b/gen_parser.sh @@ -1,5 +1,7 @@ #!/bin/bash +OS_TYPE=$(uname) + echo "Generating parser..." if [[ "language.owl" -nt main/parser.h ]] then @@ -15,11 +17,23 @@ then exit 1 fi - # remove minimum size of 4096 bytes (see https://github.com/zauberzeug/lizard/issues/23) - sed -i '' 's/while (n < size \|\| n < 4096)/while (n < size)/g' main/parser.h + echo "Detected OS: $OS_TYPE" + + # Apply the appropriate sed syntax based on the operating system + if [[ "$OS_TYPE" == "Darwin" ]]; then + # macOS + # remove minimum size of 4096 bytes (see https://github.com/zauberzeug/lizard/issues/23) + sed -i '' 's/while (n < size \|\| n < 4096)/while (n < size)/g' main/parser.h - # increase RESERVATION_AMOUNT to 11 (see https://github.com/zauberzeug/field_friend/issues/7) - sed -i '' 's/#define RESERVATION_AMOUNT 10/#define RESERVATION_AMOUNT 11/g' main/parser.h + # increase RESERVATION_AMOUNT to 11 (see https://github.com/zauberzeug/field_friend/issues/7) + sed -i '' 's/#define RESERVATION_AMOUNT 10/#define RESERVATION_AMOUNT 11/g' main/parser.h + else + # Linux (Ubuntu or others) + # sed did unwanted to the file, so we use perl instead + perl -pi -e 's/while \(n < size \|\| n < 4096\)/while (n < size)/g' main/parser.h + + perl -pi -e 's/#define RESERVATION_AMOUNT 10/#define RESERVATION_AMOUNT 11/g' main/parser.h + fi else echo "Nothing to do." fi diff --git a/main/main.cpp b/main/main.cpp index 006fd86..cc54c2b 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -32,6 +32,10 @@ #include #define BUFFER_SIZE 1024 +#define XON 0x11 +#define XOFF 0x13 +#define FILTER_MODE 0x80 +#define ID_TAG 0x81 Core_ptr core_module; @@ -206,13 +210,19 @@ void process_tree(owl_tree *const tree, bool from_expander) { const std::string module_type = identifier_to_string(constructor.module_type); const std::string expander_name = identifier_to_string(constructor.expander_name); const Module_ptr expander_module = Global::get_module(expander_name); - if (expander_module->type != expander) { + if (expander_module->type == expander) { + const Expander_ptr expander = std::static_pointer_cast(expander_module); + const std::vector arguments = compile_arguments(constructor.argument); + const Module_ptr proxy = std::make_shared(module_name, expander_name, module_type, expander, arguments); + Global::add_module(module_name, proxy); + } else if (expander_module->type == external_expander) { + const ExternalExpander_ptr external_expander = std::static_pointer_cast(expander_module); + const std::vector arguments = compile_arguments(constructor.argument); + const Module_ptr proxy = std::make_shared(module_name, expander_name, module_type, external_expander, arguments); + Global::add_module(module_name, proxy); + } else { throw std::runtime_error("module \"" + expander_name + "\" is not an expander"); } - const Expander_ptr expander = std::static_pointer_cast(expander_module); - const std::vector arguments = compile_arguments(constructor.argument); - const Module_ptr proxy = std::make_shared(module_name, expander_name, module_type, expander, arguments); - Global::add_module(module_name, proxy); } } else if (!statement.method_call.empty) { const struct parsed_method_call method_call = parsed_method_call_get(statement.method_call); @@ -359,7 +369,7 @@ void process_line(const char *line, const int len) { process_lizard(line); } } - +bool adress_mode = false; void process_uart() { static char input[BUFFER_SIZE]; while (true) { @@ -368,8 +378,52 @@ void process_uart() { break; } int len = uart_read_bytes(UART_NUM_0, (uint8_t *)input, pos + 1, 0); + + if (adress_mode && !(input[0] == ID_TAG)) { + // Keep this debug for multidevice testing + echo("Debug: 0 Character"); // Debug + echo("Debug: Adress is: %d", input[0]); + echo("Debug: it should be: %d", ID_TAG); + break; + } + len = check(input, len); - process_line(input, len); + for (int i = 0; i < len; ++i) { + if (input[i] == XOFF) { + uart_xon = false; + return; + } else if (input[i] == XON) { + uart_xon = true; + return; + } else if (input[i] == FILTER_MODE) { + if (Storage::get_device_id() == 0x00) { + echo("Debug: FILTER_MODE received and device id is not set. Will not switch to filter mode"); + return; + } + adress_mode = true; + uart_xon = false; + return; + } + } + + if (adress_mode) { + // Shift the input buffer 2 positions to the left + for (int i = 0; i < len - 2; i++) { + input[i] = input[i + 2]; + } + + // Null-terminate the new string + input[len - 2] = '\0'; + + // Update the length to reflect the removal of the first two characters + len -= 2; + } + + echo("Debug: Xon: %d", uart_xon); + // process_line(input, len); + if (uart_xon) { + process_line(input, len); + } } } @@ -395,6 +449,7 @@ void app_main() { uart_driver_install(UART_NUM_0, BUFFER_SIZE * 2, 0, 0, NULL, 0); uart_enable_pattern_det_baud_intr(UART_NUM_0, '\n', 1, 9, 0, 0); uart_pattern_queue_reset(UART_NUM_0, 100); + uart_xon = true; printf("\nReady.\n"); @@ -430,6 +485,10 @@ void app_main() { run_step(module); } } + if (adress_mode && !uart_xon) { + continue; + } + run_step(core_module); for (auto const &rule : Global::rules) { diff --git a/main/modules/core.cpp b/main/modules/core.cpp index c5400fb..fdf58c7 100644 --- a/main/modules/core.cpp +++ b/main/modules/core.cpp @@ -83,6 +83,17 @@ void Core::call(const std::string method_name, const std::vectorevaluate_string(), }; xTaskCreate(ota::ota_task, "ota_task", 8192, params, 5, nullptr); + } else if (method_name == "store") { + Module::expect(arguments, 1, integer); + if (arguments[0]->evaluate_number() < 1 || arguments[0]->evaluate_number() > 255) { + throw std::runtime_error("Core: ID out of range"); + } + uint8_t id = arguments[0]->evaluate_number(); + Storage::put_device_id(id); + } else if (method_name == "get") { // debug + Module::expect(arguments, 0); + echo("i am debug, remove me"); + echo("device_id: %d", Storage::get_device_id()); } else { Module::call(method_name, arguments); } diff --git a/main/modules/expander.cpp b/main/modules/expander.cpp index 7066084..3597aab 100644 --- a/main/modules/expander.cpp +++ b/main/modules/expander.cpp @@ -88,6 +88,12 @@ void Expander::call(const std::string method_name, const std::vectorname + "\""); } + } else if (method_name == "xoff") { // debug + Module::expect(arguments, 0); + this->serial->write_checked_line("\x13", 1); + } else if (method_name == "xon") { // debug + Module::expect(arguments, 0); + this->serial->write_checked_line("\x11", 1); } else { static char buffer[1024]; int pos = std::sprintf(buffer, "core.%s(", method_name.c_str()); diff --git a/main/modules/external_expander.cpp b/main/modules/external_expander.cpp new file mode 100644 index 0000000..fb94c72 --- /dev/null +++ b/main/modules/external_expander.cpp @@ -0,0 +1,81 @@ +#include "external_expander.h" + +#include "storage.h" +#include "utils/serial-replicator.h" +#include "utils/timing.h" +#include "utils/uart.h" +#include + +const char XON = 0x11; +const char XOFF = 0x13; +const char FILTER_MODE = 0x80; +const char ID = 0x81; + +ExternalExpander::ExternalExpander(const std::string name, + const ConstSerial_ptr serial, + const uint8_t device_id, + MessageHandler message_handler) + : Module(external_expander, name), serial(serial), device_id(device_id), message_handler(message_handler) { + serial->enable_line_detection(); + char buffer[1024] = ""; + int len = 0; + const unsigned long int start = millis(); + do { + if (millis_since(start) > 1000) { + echo("warning: external expander is not booting"); + break; + } + if (serial->available()) { + len = serial->read_line(buffer); + strip(buffer, len); + echo("%s: %s", name.c_str(), buffer); + } + } while (strcmp("Ready.", buffer)); + echo("Ready."); + serial->write_checked_line(&FILTER_MODE, 1); +} + +void ExternalExpander::step() { + static char buffer[1024]; + this->serial->write_checked_line_id(this->device_id, &XON, 1); + while (this->serial->has_buffered_lines()) { + int len = this->serial->read_line(buffer); + check(buffer, len); + if (buffer[0] == '!' && buffer[1] == '!') { // debug todo: Add tag to the message to identify the source -> !! to !!MSG + /* Don't trigger keep-alive from expander updates */ + this->message_handler(&buffer[2], false, true); + // echo("received message from %s: %s", this->name.c_str(), &buffer[2]); // debug + } else { + echo("%s: %s", this->name.c_str(), buffer); // debug + } + } + this->serial->write_checked_line_id(this->device_id, &XOFF, 1); + Module::step(); +} + +void ExternalExpander::call(const std::string method_name, const std::vector arguments) { + if (method_name == "run") { + Module::expect(arguments, 1, string); + std::string command = arguments[0]->evaluate_string(); + this->serial->write_checked_line_id(this->device_id, &XON, 1); + this->serial->write_checked_line_id(this->device_id, command.c_str(), command.size()); + this->serial->write_checked_line_id(this->device_id, &XOFF, 1); + echo("%s: %s", this->name.c_str(), command.c_str()); // Echo the command + } else if (method_name == "disconnect") { + Module::expect(arguments, 0); + this->serial->deinstall(); + echo("%s: disconnected", this->name.c_str()); // Echo the disconnection + } else if (method_name == "filter") { // debug -> actives filter mode + Module::expect(arguments, 0); + this->serial->write_checked_line(&FILTER_MODE, 1); + } else { + static char buffer[1024]; + int pos = std::sprintf(buffer, "core.%s(", method_name.c_str()); + pos += write_arguments_to_buffer(arguments, &buffer[pos]); + pos += std::sprintf(&buffer[pos], ")"); + this->serial->write_checked_line_id(this->device_id, &XON, 1); + this->serial->write_checked_line_id(this->device_id, buffer, pos); + this->serial->write_checked_line_id(this->device_id, &XOFF, 1); + echo("%s: %s", this->name.c_str(), buffer); // Echo the generic command + } +} diff --git a/main/modules/external_expander.h b/main/modules/external_expander.h new file mode 100644 index 0000000..54ec961 --- /dev/null +++ b/main/modules/external_expander.h @@ -0,0 +1,22 @@ +#pragma once + +#include "module.h" +#include "serial.h" +#include + +class ExternalExpander; +using ExternalExpander_ptr = std::shared_ptr; + +class ExternalExpander : public Module { +public: + const ConstSerial_ptr serial; + const uint8_t device_id; + MessageHandler message_handler; + + ExternalExpander(const std::string name, + const ConstSerial_ptr serial, + const uint8_t device_id, + MessageHandler message_handler); + void step() override; + void call(const std::string method_name, const std::vector arguments) override; +}; diff --git a/main/modules/module.cpp b/main/modules/module.cpp index fe61994..f10715f 100644 --- a/main/modules/module.cpp +++ b/main/modules/module.cpp @@ -12,6 +12,7 @@ #include "dunker_motor.h" #include "dunker_wheels.h" #include "expander.h" +#include "external_expander.h" #include "imu.h" #include "input.h" #include "linear_motor.h" @@ -79,6 +80,16 @@ Module_ptr Module::create(const std::string type, const gpio_num_t boot_pin = arguments.size() > 1 ? (gpio_num_t)arguments[1]->evaluate_integer() : GPIO_NUM_NC; const gpio_num_t enable_pin = arguments.size() > 2 ? (gpio_num_t)arguments[2]->evaluate_integer() : GPIO_NUM_NC; return std::make_shared(name, serial, boot_pin, enable_pin, message_handler); + } else if (type == "ExternalExpander") { + Module::expect(arguments, 2, identifier, integer); + std::string serial_name = arguments[0]->evaluate_identifier(); + Module_ptr module = Global::get_module(serial_name); + if (module->type != serial) { + throw std::runtime_error("module \"" + serial_name + "\" is no serial connection"); + } + const ConstSerial_ptr serial = std::static_pointer_cast(module); + uint8_t device_id = arguments[1]->evaluate_integer(); + return std::make_shared(name, serial, device_id, message_handler); } else if (type == "Bluetooth") { Module::expect(arguments, 1, string); std::string device_name = arguments[0]->evaluate_string(); diff --git a/main/modules/module.h b/main/modules/module.h index e3c0acf..5dcea16 100644 --- a/main/modules/module.h +++ b/main/modules/module.h @@ -33,6 +33,7 @@ enum ModuleType { dunker_wheels, analog, proxy, + external_expander, }; class Module; diff --git a/main/modules/proxy.cpp b/main/modules/proxy.cpp index 23eac17..8901a2a 100644 --- a/main/modules/proxy.cpp +++ b/main/modules/proxy.cpp @@ -1,5 +1,6 @@ #include "proxy.h" #include "driver/uart.h" +#include "uart.h" #include Proxy::Proxy(const std::string name, @@ -7,7 +8,7 @@ Proxy::Proxy(const std::string name, const std::string module_type, const Expander_ptr expander, const std::vector arguments) - : Module(proxy, name), expander(expander) { + : Module(proxy, name), expander(expander), external_expander(nullptr) { static char buffer[256]; int pos = std::sprintf(buffer, "%s = %s(", name.c_str(), module_type.c_str()); pos += write_arguments_to_buffer(arguments, &buffer[pos]); @@ -25,12 +26,39 @@ Proxy::Proxy(const std::string name, expander->serial->write_checked_line(buffer, pos); } +Proxy::Proxy(const std::string &name, + const std::string &expander_name, + const std::string &module_type, + std::shared_ptr external_expander, + const std::vector &arguments) + : Module(proxy, name), expander(nullptr), external_expander(external_expander) { + static char buffer[256]; + int pos = std::sprintf(buffer, "%s = %s(", name.c_str(), module_type.c_str()); + pos += write_arguments_to_buffer(arguments, &buffer[pos]); + pos += std::sprintf(&buffer[pos], "); "); + pos += std::sprintf(&buffer[pos], "%s.broadcast()", name.c_str()); + + if (module_type == "Input") { + this->properties["level"] = std::make_shared(0); + this->properties["active"] = std::make_shared(false); + } + external_expander->serial->write_checked_line_id(external_expander->device_id, "\x11", 1); + external_expander->serial->write_checked_line_id(external_expander->device_id, buffer, pos); + external_expander->serial->write_checked_line_id(external_expander->device_id, "\x13", 1); +} + void Proxy::call(const std::string method_name, const std::vector arguments) { static char buffer[256]; int pos = std::sprintf(buffer, "%s.%s(", this->name.c_str(), method_name.c_str()); pos += write_arguments_to_buffer(arguments, &buffer[pos]); pos += std::sprintf(&buffer[pos], ")"); - this->expander->serial->write_checked_line(buffer, pos); + if (expander) { + expander->serial->write_checked_line(buffer, pos); + } else if (external_expander) { + external_expander->serial->write_checked_line_id(external_expander->device_id, "\x11", 1); + external_expander->serial->write_checked_line_id(external_expander->device_id, buffer, pos); + external_expander->serial->write_checked_line_id(external_expander->device_id, "\x13", 1); + } } void Proxy::write_property(const std::string property_name, const ConstExpression_ptr expression, const bool from_expander) { @@ -41,7 +69,13 @@ void Proxy::write_property(const std::string property_name, const ConstExpressio static char buffer[256]; int pos = std::sprintf(buffer, "%s.%s = ", this->name.c_str(), property_name.c_str()); pos += expression->print_to_buffer(&buffer[pos]); - this->expander->serial->write_checked_line(buffer, pos); + if (expander) { + expander->serial->write_checked_line(buffer, pos); + } else if (external_expander) { + external_expander->serial->write_checked_line_id(external_expander->device_id, "\x11", 1); + external_expander->serial->write_checked_line_id(external_expander->device_id, buffer, pos); + external_expander->serial->write_checked_line_id(external_expander->device_id, "\x13", 1); + } } Module::get_property(property_name)->assign(expression); } diff --git a/main/modules/proxy.h b/main/modules/proxy.h index 8bb0f7c..c76b792 100644 --- a/main/modules/proxy.h +++ b/main/modules/proxy.h @@ -1,11 +1,13 @@ #pragma once #include "expander.h" +#include "external_expander.h" #include "module.h" class Proxy : public Module { private: const Expander_ptr expander; + const ExternalExpander_ptr external_expander; public: Proxy(const std::string name, @@ -13,6 +15,12 @@ class Proxy : public Module { const std::string module_type, const Expander_ptr expander, const std::vector arguments); + + Proxy(const std::string &name, + const std::string &expander_name, + const std::string &module_type, + std::shared_ptr external_expander, + const std::vector &arguments); void call(const std::string method_name, const std::vector arguments) override; void write_property(const std::string property_name, const ConstExpression_ptr expression, const bool from_expander) override; }; \ No newline at end of file diff --git a/main/modules/serial.cpp b/main/modules/serial.cpp index b58def0..06fa274 100644 --- a/main/modules/serial.cpp +++ b/main/modules/serial.cpp @@ -64,6 +64,30 @@ void Serial::write_checked_line(const char *message, const int length) const { } } +void Serial::write_checked_line_id(uint8_t id, const char *message, const int length) const { + // do the same as write_checked_line, but add the id at the beginning of the message + static char checksum_buffer[16]; + uint8_t checksum = 0; + int start = 0; + char id_tag = 0x81; + // ID-Tag und ID zur Prüfsumme hinzufügen + checksum ^= id_tag; + checksum ^= id; + for (unsigned int i = 0; i < length + 1; ++i) { + if (i >= length || message[i] == '\n') { + sprintf(checksum_buffer, "@%02x\n", checksum); + uart_write_bytes(this->uart_num, &id_tag, 1); + uart_write_bytes(this->uart_num, reinterpret_cast(&id), 1); + uart_write_bytes(this->uart_num, &message[start], i - start); + uart_write_bytes(this->uart_num, checksum_buffer, 4); + start = i + 1; + checksum = 0; + } else { + checksum ^= message[i]; + } + } +} + int Serial::available() const { if (!uart_is_driver_installed(this->uart_num)) { return 0; diff --git a/main/modules/serial.h b/main/modules/serial.h index fca4065..94ed51b 100644 --- a/main/modules/serial.h +++ b/main/modules/serial.h @@ -27,6 +27,7 @@ class Serial : public Module { int read_line(char *buffer) const; size_t write(const uint8_t byte) const; void write_checked_line(const char *message, const int length) const; + void write_checked_line_id(uint8_t id, const char *message, const int length) const; void flush() const; void clear() const; std::string get_output() const override; diff --git a/main/storage.cpp b/main/storage.cpp index 91014cf..7c838a4 100644 --- a/main/storage.cpp +++ b/main/storage.cpp @@ -108,3 +108,18 @@ void Storage::save_startup() { void Storage::clear_nvs() { Storage::put(""); } + +void Storage::put_device_id(uint8_t id) { + std::string id_str = std::to_string(id); + write(NAMESPACE, "device_id", id_str); +} + +uint8_t Storage::get_device_id() { + try { + std::string id_str = read(NAMESPACE, "device_id"); + return static_cast(std::stoi(id_str)); + } catch (const std::runtime_error &e) { + printf("Device ID not found: %s\n", e.what()); + return 0x00; + } +} diff --git a/main/storage.h b/main/storage.h index b246e26..8ee438a 100644 --- a/main/storage.h +++ b/main/storage.h @@ -16,4 +16,6 @@ class Storage { static void print_startup(const std::string substring = ""); static void save_startup(); static void clear_nvs(); + static void put_device_id(uint8_t id); + static uint8_t get_device_id(); }; \ No newline at end of file diff --git a/main/utils/uart.cpp b/main/utils/uart.cpp index 74dd051..a5feff0 100644 --- a/main/utils/uart.cpp +++ b/main/utils/uart.cpp @@ -4,7 +4,12 @@ #include #include +bool uart_xon = true; + void echo(const char *format, ...) { + if (!uart_xon) { + return; + } static char buffer[1024]; int pos = 0; diff --git a/main/utils/uart.h b/main/utils/uart.h index fd965be..ba52e5e 100644 --- a/main/utils/uart.h +++ b/main/utils/uart.h @@ -1,5 +1,7 @@ #pragma once +extern bool uart_xon; + void echo(const char *fmt, ...); int strip(char *buffer, int len); int check(char *buffer, int len);