diff --git a/Firmware/CoverUI/YardForce/ButtonDebouncer.cpp b/Firmware/CoverUI/YardForce/ButtonDebouncer.cpp deleted file mode 100644 index 41c67e0..0000000 --- a/Firmware/CoverUI/YardForce/ButtonDebouncer.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file ButtonDebouncer.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI Button-Debouncer class for OpenMower https://github.com/ClemensElflein/OpenMower - * Debouncing is done by continuos simple shifting the port states into an state array for later processing. - * See Jack Ganssle debouncing http://www.ganssle.com/debouncing-pt2.htm - * For code simplicity/speed, I debounce all pins, regardless if it has a button or not. Button separation has to be done by calling class. - * @version 0.3 - * @date 2023-10-26 - * - * @copyright Copyright (c) 2023 - * - */ -#include "include/ButtonDebouncer.hpp" - -void ButtonDebouncer::process_state(const uint32_t gpio_port_nr) // Has to get called regulary i.e. by timer (5ms) and store the (buttons) port state within states_ array -{ -#ifdef MCU_STM32 - auto gpio_port = get_GPIO_Port(gpio_port_nr); - states_[state_index_] = gpio_port->IDR ^ 0xFFFF; // XOR changes for pull-up states_ -#else - states_[state_index_] = GPIO_ISTAT(gpio_port[gpio_port_nr]) ^ 0xFFFF; // XOR changes for pull-up states_ -#endif - - // Debounce - uint16_t laststate_debounced_ = state_debounced_; - unsigned int i; - for (i = 0, state_debounced_ = 0xFFFF; i < NUM_BUTTON_STATES; i++) - state_debounced_ &= states_[i]; - - // Circular buffer index - state_index_++; - if (state_index_ >= NUM_BUTTON_STATES) - state_index_ = 0; - - // Save what changed - state_changed_ = state_debounced_ ^ laststate_debounced_; -}; - -/** - * @brief Return boolean true if the given pin's button is pressed. - * Take into notice that the returned state is already debounced. - * - * @param pin digital_pin - * @return true if pressed, false if not pressed - */ -bool ButtonDebouncer::is_pressed(uint8_t pin) -{ - return state_debounced_ & digitalPinToBitMask(pin); -}; diff --git a/Firmware/CoverUI/YardForce/Buttons.cpp b/Firmware/CoverUI/YardForce/Buttons.cpp deleted file mode 100644 index 621eca3..0000000 --- a/Firmware/CoverUI/YardForce/Buttons.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/** - * @file Buttons.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI Buttons class for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.7 - * @date 2024-09-30 - * - * @copyright Copyright (c) 2023, 2024 - * - */ -#include -#include - -#include - -#include "../BttnCtl.h" -#include "include/main.h" - -#ifdef MCU_STM32 -#define DIGITAL_PIN_TO_PORT_NR(p) (STM_PORT(digitalPinToPinName(p))) -#else // MCU_GD32 -#define DIGITAL_PIN_TO_PORT_NR(p) (GD_PORT_GET(DIGITAL_TO_PINNAME(p))) -#endif - -extern void sendMessage(void *message, size_t size); - -/** - * @brief Setup GPIOs - * - */ -void Buttons::setup() { - for (auto const &it : kBtnDefByNumMap) // Loop over Button-Num -> button pin map - { - // Create debouncer if not already exist for this Pin's GPIO_Port_Nr - uint32_t gpio_port_nr = DIGITAL_PIN_TO_PORT_NR(it.second.pin); - auto debouncer = debouncer_by_gpio_port_nr_map.find(gpio_port_nr); - if (debouncer == debouncer_by_gpio_port_nr_map.end()) - debouncer_by_gpio_port_nr_map.insert(etl::pair(gpio_port_nr, ButtonDebouncer())); - - pinMode(it.second.pin, INPUT_PULLUP); - } -}; - -/** - * @brief Process GPIO states by debouncer. Has to get called regulary i.e. by timer (5ms) - * - */ -void Buttons::process_states() { - for (auto &it : debouncer_by_gpio_port_nr_map) - it.second.process_state(it.first); -}; - -/** - * @brief Get corresponding LED num for button num - * - * @param button_nr - * @return uint8_t LED num. -1 of not exists. - */ -int8_t Buttons::get_led(uint8_t button_nr) { - auto btn_def_it = kBtnDefByNumMap.find(button_nr); // Find button_nr and get iterator pair - if (btn_def_it != kBtnDefByNumMap.end()) - return btn_def_it->second.led_num; - - return -1; -} - -/** - * @brief Return boolean true if the given button number is pressed. - * Take into notice that the returned state is already debounced. - * - * @param uint8_t button_nr - * @return true if pressed, false if not pressed - */ -bool Buttons::is_pressed(uint8_t button_nr) { - auto btn_def_it = kBtnDefByNumMap.find(button_nr); // Find button_nr and get iterator pair - if (btn_def_it == kBtnDefByNumMap.end()) - return false; - - uint32_t gpio_port_nr = DIGITAL_PIN_TO_PORT_NR(btn_def_it->second.pin); - auto debouncer_it = debouncer_by_gpio_port_nr_map.find(gpio_port_nr); // Find debouncer and get iterator pair - if (debouncer_it == debouncer_by_gpio_port_nr_map.end()) - return false; - - return debouncer_it->second.is_pressed(btn_def_it->second.pin); -}; - -/** - * @brief Return ButtonNum of the first detected pressed button. - * Take into notice that the returned state is already debounced. - * - * @return uint8_t 0 = none pressed, >0 = ButtonNum - */ -uint8_t Buttons::is_pressed() { - for (auto const &it : kBtnDefByNumMap) // Loop over Button-Num -> button pin map - if (is_pressed(it.first)) - return it.first; - - return 0; -}; - -/** - * @brief Send 'rain' message via COBS with last read rain-sensor- value (together with (currently static) threshold) - * - */ -void Buttons::send(uint16_t button_id, uint8_t press_duration) { - msg_event_button msg = { - .type = Get_Button, - .button_id = button_id, - .press_duration = press_duration}; - sendMessage(&msg, sizeof(msg)); -} diff --git a/Firmware/CoverUI/YardForce/Display.cpp b/Firmware/CoverUI/YardForce/Display.cpp deleted file mode 100644 index 28438ca..0000000 --- a/Firmware/CoverUI/YardForce/Display.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @file Display.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI abstract display class for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-27 - * - * @copyright Copyright (c) 2023 - */ -#include "include/main.h" -#include "include/Display.hpp" -#include "include/subscription.h" - -namespace yardforce -{ - namespace display - { - - /** - * @brief Tell LVGL that milliseconds have been elapsed. - * Required for anim or similar. Should be called in a high priority routine like hwtimer. - * - * @param ms - */ - void Display::tick_inc(uint8_t ms) - { - lv_tick_inc(ms); - } - - void Display::loop_low_prio() - { - lv_timer_handler(); - - subscription::subscribe((Topic_set_ll_status | Topic_set_hl_state), 500); - - check_backlight(); - } - - /** - * @brief Set backlight LED state and set/reset timeout counter - * - * @param state LED_state, default LED_on - * @param timeout in ms when to switch off - */ - void Display::set_backlight(LED_state t_state, uint32_t t_timeout) - { - ::leds.set(config.backlight_led_num, t_state); - if (t_state != LED_off) - { - backlight_timeout = millis() + t_timeout; - } - backlight_state = t_state; - } - - LED_state Display::check_backlight() - { - if (backlight_state == LED_off) - return backlight_state; - - if (millis() < backlight_timeout) - return backlight_state; - - set_backlight(LED_off); - return backlight_state; - } - - void Display::start_anncmnt(uint32_t t_timeout_ms, AnncmntType t_type) - { - anncmnt = { - .timeout = millis() + t_timeout_ms, - .type = t_type}; - } - - Display::AnncmntType Display::get_anncmnt() - { - if (!anncmnt.timeout) - return AnncmntType::none; - - if (millis() < anncmnt.timeout) - return anncmnt.type; - - anncmnt.timeout = 0; - return AnncmntType::none; - } - } // namespace display -} // namespace yardforce \ No newline at end of file diff --git a/Firmware/CoverUI/YardForce/Emergency.cpp b/Firmware/CoverUI/YardForce/Emergency.cpp deleted file mode 100644 index 5dbabcd..0000000 --- a/Firmware/CoverUI/YardForce/Emergency.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/** - * @file Emergency.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI Emergency class for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.3 - * @date 2023-11-05 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "include/Emergency.hpp" -#include "../BttnCtl.h" - -extern void sendMessage(void *message, size_t size); - -void Emergency::setup() -{ - for (size_t i = 0; i < kNumEmergencies; i++) - { - auto pin_state = *(kEmergencyPinStatesPtr + i); - pinMode(pin_state.pin, pin_state.pin_mode); - } -}; - -/** - * @brief Read all related hall-sensors and update state_ - * - */ -void Emergency::read() -{ - state_ = 0; // We might have more emergency sensors and switch than OM Emergency_states. So we need to OR them instead of assign them 1:1 - for (size_t i = 0; i < kNumEmergencies; i++) - { - auto pin_state = *(kEmergencyPinStatesPtr + i); - if (digitalRead(pin_state.pin) == HIGH) - state_ |= pin_state.state; - } - if (state_) - state_ |= Emergency_state::Emergency_latch; -}; - -/** - * @brief Send 'emergency' message via COBS with latest state_ - * - */ -void Emergency::send() -{ - msg_event_emergency msg = { - .type = Get_Emergency, - .state = state_}; - sendMessage(&msg, sizeof(msg)); - state_last_sent_ = state_; -}; - -/** - * @brief read() related hall sensors and send if a new emergency occurred. - * Get called constantly by quick (5ms) timer - * - */ -void Emergency::read_and_send_if_emergency() -{ - read(); - - if (state_ & Emergency_state::Emergency_latch && !(state_last_sent_ & Emergency_state::Emergency_latch)) - { - send(); - next_periodic_cycle = millis() + PERIODIC_SEND_CYCLE; - } -}; - -/** - * @brief Periodically send emergency state. - * Get called by fast (100ms) timer. - * An active emergency state get send on each call. - * An inactive emergency state get only every PERIODIC_SEND_CYCLE - */ -void Emergency::periodic_send() -{ - // Active emergency - if (state_ & Emergency_state::Emergency_latch) - { - send(); - next_periodic_cycle = millis() + PERIODIC_SEND_CYCLE; - return; - } - - // Inactive emergency - if (millis() < next_periodic_cycle) - return; - - send(); - next_periodic_cycle = millis() + PERIODIC_SEND_CYCLE; -}; diff --git a/Firmware/CoverUI/YardForce/Hatch.cpp b/Firmware/CoverUI/YardForce/Hatch.cpp deleted file mode 100644 index 20cd8f8..0000000 --- a/Firmware/CoverUI/YardForce/Hatch.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file Hatch.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI Hatch for OpenMower https://github.com/ClemensElflein/OpenMower - * This is for YardForce models (like SA, SC or NX), whose CoverUI is behind a hatch. - * Those need some special (button) handling because opening the hatch (to reach the buttons), triggers stop-emergency. - * @version 0.2 - * @date 2024-09-30 - * - * @copyright Copyright (c) 2023, 2024 - * - */ - -#include - -#include "include/main.h" - -/** - * @brief Handle any kind of pressed button result like: - * 1. Convert a button id to some other value - * 2. Trigger count-down for hatch-close - * 3. Queue fake (delayed) buttons - * 4. Handle fake_button_queue and probably directly send Get_Button packet - * - * @param button_id - * @param press_time - * @return unsigned int of the probably converted or ignored button ID - */ -unsigned int Hatch::handle_button(unsigned int button_id, uint32_t press_time) -{ - leds.set(LED_NUM_LIFTED, LED_blink_slow); - - return button_id + 0; -}; - -void Hatch::queue_button(uint8_t button_id, uint8_t press_duration, uint32_t delay) -{ - fake_button_queue.push({button_id, press_duration, millis() + delay}); -}; - -/** - * @brief Loop over fake_button_queue and process(send) ready ones - */ -void Hatch::process_queued() -{ - if (fake_button_queue.empty() || fake_button_queue.full()) - return; - - auto first = fake_button_queue.front(); - if (millis() >= first.delay_end) { - buttons.send(first.button_id, first.press_duration); - fake_button_queue.pop(); - } -}; diff --git a/Firmware/CoverUI/YardForce/LEDcontrol.cpp b/Firmware/CoverUI/YardForce/LEDcontrol.cpp index 94c820d..81d6b12 100644 --- a/Firmware/CoverUI/YardForce/LEDcontrol.cpp +++ b/Firmware/CoverUI/YardForce/LEDcontrol.cpp @@ -2,33 +2,28 @@ * @file LEDcontrol.cpp * @author Apehaenger (joerg@ebeling.ws) * @brief YardForce Classic 500 CoverUI LED driver for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.3 - * @date 2023-10-25 + * @version 0.4 + * @date 2024-10-02 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023, 2024 * */ -#include -#include #include "include/LEDcontrol.hpp" -#include "../BttnCtl.h" // LED_state is defined in BttnCtl.h -LEDcontrol::LEDcontrol(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds) : kPinByLedNumPtr(t_kPinByLedNumPtr), kNumLeds(t_kNumLeds){}; -LEDcontrol::LEDcontrol(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds, void (LEDcontrol::*t_set_base10_leds_cb)(char digit)) : kPinByLedNumPtr(t_kPinByLedNumPtr), kNumLeds(t_kNumLeds), set_base10_leds_cb(t_set_base10_leds_cb){}; +#include +#include -/** - * @brief Setup LED GPIOs - * - */ -void LEDcontrol::setup() -{ - for (size_t p = 0; p < kNumLeds; p++) - { +#include "../BttnCtl.h" // LED_state is defined in BttnCtl.h + +LEDcontrol::LEDcontrol(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds, void (LEDcontrol::*t_set_base10_leds_cb)(char digit)) + : kPinByLedNumPtr(t_kPinByLedNumPtr), kNumLeds(t_kNumLeds), set_base10_leds_cb(t_set_base10_leds_cb) { + // Set GPIO modes + for (size_t p = 0; p < kNumLeds; p++) { auto pin = *(kPinByLedNumPtr + p); if (pin != LED_PIN_NC) pinMode(pin, OUTPUT); } -} +}; /** * @brief Set any of known LED_state states for the given LED num. @@ -37,23 +32,20 @@ void LEDcontrol::setup() * @param state * @param change_state Indicate if the state get written to led_states_bin_ buffer */ -void LEDcontrol::set(uint8_t led_num, LED_state state, bool change_state) -{ +void LEDcontrol::set(uint8_t led_num, LED_state state, bool change_state) { auto pin = *(kPinByLedNumPtr + led_num); - if (pin != LED_PIN_NC) - { - switch (state) - { - case LED_state::LED_on: - digitalWrite(pin, HIGH); - break; - case LED_state::LED_off: - digitalWrite(pin, LOW); - break; - case LED_state::LED_blink_slow: - case LED_state::LED_blink_fast: - // Get handled by timers - break; + if (pin != LED_PIN_NC) { + switch (state) { + case LED_state::LED_on: + digitalWrite(pin, HIGH); + break; + case LED_state::LED_off: + digitalWrite(pin, LOW); + break; + case LED_state::LED_blink_slow: + case LED_state::LED_blink_fast: + // Get handled by timers + break; } } if (change_state) @@ -67,10 +59,7 @@ void LEDcontrol::set(uint8_t led_num, LED_state state, bool change_state) * @param change_state indicate if the state get written to led_states_bin_ buffer */ void LEDcontrol::toggle(uint8_t led_num, bool change_state) { - if (has(led_num, LED_state::LED_off)) - set(led_num, LED_state::LED_on, change_state); - else - set(led_num, LED_state::LED_off, change_state); + has(led_num, LED_state::LED_off) ? set(led_num, LED_state::LED_on, change_state) : set(led_num, LED_state::LED_off, change_state); } /** @@ -79,52 +68,37 @@ void LEDcontrol::toggle(uint8_t led_num, bool change_state) { * * @param all_state */ -void LEDcontrol::set(uint64_t all_state) -{ - for (unsigned int led = 0; led <= LED_NUM_OM_MAX && led < kNumLeds; led++) - { +void LEDcontrol::set(uint64_t all_state) { + for (unsigned int led = 0; led <= LED_NUM_OM_MAX && led < kNumLeds; led++) { uint8_t led_state = (all_state >> (3 * led)) & 0b111; set(led, static_cast(led_state)); } } -LED_state LEDcontrol::get(uint8_t led_num) -{ +LED_state LEDcontrol::get(uint8_t led_num) { return (LED_state)((led_states_bin_ >> (3 * led_num)) & 0b111); } -void LEDcontrol::force_(uint8_t led_num, bool force, uint32_t *force_type_) -{ +void LEDcontrol::force_(uint8_t led_num, bool force, uint32_t *force_type_) { uint32_t led_bin = 1 << led_num; - - if (force) - *force_type_ |= led_bin; - else - *force_type_ &= ~led_bin; + force ? *force_type_ |= led_bin : *force_type_ &= ~led_bin; } -void LEDcontrol::force_off(uint8_t led_num, bool force) -{ +void LEDcontrol::force_off(uint8_t led_num, bool force) { force_(led_num, force, &force_led_off_); - if (force) - set(led_num, LED_state::LED_off, false); // Directly set without changing state - else - set(led_num, get(led_num), false); // Restore state + force ? set(led_num, LED_state::LED_off, false) // Directly set without changing state + : set(led_num, get(led_num), false); // Restore state } -void LEDcontrol::force_on(uint8_t led_num, bool force) -{ +void LEDcontrol::force_on(uint8_t led_num, bool force) { force_(led_num, force, &force_led_on_); - if (force) - set(led_num, LED_state::LED_on, false); // Directly set without changing state - else - set(led_num, get(led_num), false); // Restore state + force ? set(led_num, LED_state::LED_on, false) // Directly set without changing state + : set(led_num, get(led_num), false); // Restore state } -void LEDcontrol::change_led_states_(uint8_t led_num, LED_state state) -{ - led_states_bin_ &= ~((uint64_t)(0b111) << (3 * led_num)); // Be safe for future LED_state changes and mask out the whole led - led_states_bin_ |= (uint64_t)(state) << (3 * led_num); // Set new state +void LEDcontrol::change_led_states_(uint8_t led_num, LED_state state) { + led_states_bin_ &= ~((uint64_t)(0b111) << (3 * led_num)); // Be safe for future LED_state changes and mask out the whole led + led_states_bin_ |= (uint64_t)(state) << (3 * led_num); // Set new state } /** @@ -135,33 +109,27 @@ void LEDcontrol::change_led_states_(uint8_t led_num, LED_state state) * @return true * @return false */ -bool LEDcontrol::has(uint8_t led_num, LED_state state) -{ +bool LEDcontrol::has(uint8_t led_num, LED_state state) { return (led_states_bin_ >> (3 * led_num) & (uint64_t)(0b111)) == (uint64_t)(state); } -void LEDcontrol::blink_timer_elapsed(LED_state blink_state) -{ +void LEDcontrol::blink_timer_elapsed(LED_state blink_state) { // Synchronous blink is only for cosmetic nature, probably only interesting for a nice looking CoverUITest static std::map sync_blink_map = { {LED_state::LED_blink_slow, LED_state::LED_on}, {LED_state::LED_blink_fast, LED_state::LED_on}}; - if (blink_state != LED_state::LED_blink_fast && blink_state != LED_state::LED_blink_slow) // Ensure that this method only get called for blinking LED states + if (blink_state != LED_state::LED_blink_fast && blink_state != LED_state::LED_blink_slow) // Ensure that this method only get called for blinking LED states return; - for (size_t led_num = 0; led_num < kNumLeds; led_num++) - { - if (has(led_num, blink_state) && !(force_led_off_ & (1 << led_num))) - { - set(led_num, sync_blink_map[blink_state], false); // Set LED without state change + for (size_t led_num = 0; led_num < kNumLeds; led_num++) { + if (has(led_num, blink_state) && !(force_led_off_ & (1 << led_num))) { + set(led_num, sync_blink_map[blink_state], false); // Set LED without state change } } // Synchronous toggle - if (sync_blink_map[blink_state] == LED_state::LED_on) - sync_blink_map[blink_state] = LED_state::LED_off; - else - sync_blink_map[blink_state] = LED_state::LED_on; + sync_blink_map[blink_state] == LED_state::LED_on ? sync_blink_map[blink_state] = LED_state::LED_off + : sync_blink_map[blink_state] = LED_state::LED_on; } /** @@ -169,8 +137,7 @@ void LEDcontrol::blink_timer_elapsed(LED_state blink_state) * * @param led_num */ -void LEDcontrol::identify(uint8_t led_num) -{ +void LEDcontrol::identify(uint8_t led_num) { force_off(led_num, false); force_on(led_num, true); delay(100); @@ -200,15 +167,12 @@ void LEDcontrol::identify(uint8_t led_num) * @param handler * @param boolean abort_running aborts a currently running sequence */ -void LEDcontrol::sequence_start(void (LEDcontrol::*handler)(), bool abort_running) -{ +void LEDcontrol::sequence_start(void (LEDcontrol::*handler)(), bool abort_running) { if (abort_running) - { - set(led_states_bin_); // Restore states - } + set(led_states_bin_); // Restore states if (seq_start_tick_ > 0 && !abort_running) - return; // There's already/still a running sequence + return; // There's already/still a running sequence seq_step_ = 0; seq_start_tick_ = millis(); @@ -219,12 +183,11 @@ void LEDcontrol::sequence_start(void (LEDcontrol::*handler)(), bool abort_runnin * @brief Process LED sequence * Get called by 5ms timer (which is pretty fast enough for our LED sequences) */ -void LEDcontrol::process_sequence() -{ +void LEDcontrol::process_sequence() { if (seq_start_tick_ == 0) - return; // No sequence + return; // No sequence - (this->*seq_handler_)(); // Call sequence handler + (this->*seq_handler_)(); // Call sequence handler } /** @@ -233,20 +196,18 @@ void LEDcontrol::process_sequence() * @param step_rate in ms * @return uint16_t step (n>1) or 0 if the next step isn't reached now */ -uint16_t LEDcontrol::seq_get_next_step_(uint16_t step_rate) -{ +uint16_t LEDcontrol::seq_get_next_step_(uint16_t step_rate) { static uint16_t last_step_tick = 0; - uint16_t step_tick = ((((millis() - seq_start_tick_) + (step_rate - 1))) / step_rate) * step_rate; // Round to the next nearest multiple of + uint16_t step_tick = ((((millis() - seq_start_tick_) + (step_rate - 1))) / step_rate) * step_rate; // Round to the next nearest multiple of if (step_tick == last_step_tick) - return 0; // Not a new step + return 0; // Not a new step last_step_tick = step_tick; return ++seq_step_; } -void LEDcontrol::show_num(uint16_t num) -{ +void LEDcontrol::show_num(uint16_t num) { seq_num_value_ = num; sequence_start(&LEDcontrol::seq_num_handler_); } @@ -255,47 +216,45 @@ void LEDcontrol::show_num(uint16_t num) * @brief Sequence handler for displaying an uint16_t as base10 values * number by number. Use i.e. for FW version, or error display */ -void LEDcontrol::seq_num_handler_() -{ +void LEDcontrol::seq_num_handler_() { + if (set_base10_leds_cb == nullptr) + return; + const uint8_t steps_per_char = 10; - static char s_buf[6]; // Current largest number is a uint16_t, whose max. = 65535 but probably need to be made dynamic!! + static char s_buf[6]; // Current largest number is a uint16_t, whose max. = 65535 but probably need to be made dynamic!! static uint8_t s_num_chars; - static uint8_t s_cur_idx; // Current displaying digit index + static uint8_t s_cur_idx; // Current displaying digit index - uint16_t step = seq_get_next_step_(100); // Animation sequence runs in 100ms steps + uint16_t step = seq_get_next_step_(100); // Animation sequence runs in 100ms steps - if (step == 0) // Next sequence step not reached now + if (step == 0) // Next sequence step not reached now return; - if (step == 1) // Sequence start, init vars - { + if (step == 1) { // Sequence start, init vars s_cur_idx = 0; itoa(seq_num_value_, s_buf, 10); s_num_chars = std::strlen(s_buf); - force_off_num_seq_leds(true); // Force related signalling LEDs off + force_off_num_seq_leds(true); // Force related signalling LEDs off return; } - if (step >= (s_num_chars + 1) * steps_per_char) // End (last char sent) - { - force_off_num_seq_leds(false); // Un-Force related signalling LEDs - seq_start_tick_ = 0; // Sequence end + if (step >= (s_num_chars + 1) * steps_per_char) { // End (last char sent) + force_off_num_seq_leds(false); // Un-Force related signalling LEDs + seq_start_tick_ = 0; // Sequence end return; } - uint16_t sub_step = (s_cur_idx + 1) * steps_per_char; // Per digit steps + uint16_t sub_step = (s_cur_idx + 1) * steps_per_char; // Per digit steps - if (step == sub_step) // Num Display start - { - set(LED_NUM_LIFTED, LED_state::LED_on, false); // New number indicator on - (this->*set_base10_leds_cb)(s_buf[s_cur_idx]); // Set base10 LEDs callback + if (step == sub_step) { // Num Display start + set(LED_NUM_LIFTED, LED_state::LED_on, false); // New number indicator on + (this->*set_base10_leds_cb)(s_buf[s_cur_idx]); // Set base10 LEDs callback return; } - if (step == sub_step + 1) // +100ms - { - set(LED_NUM_LIFTED, LED_state::LED_off, false); // New number indicator off + if (step == sub_step + 1) { // +100ms + set(LED_NUM_LIFTED, LED_state::LED_off, false); // New number indicator off s_cur_idx++; return; } diff --git a/Firmware/CoverUI/YardForce/README.md b/Firmware/CoverUI/YardForce/README.md index b2e32f6..027f3b4 100644 --- a/Firmware/CoverUI/YardForce/README.md +++ b/Firmware/CoverUI/YardForce/README.md @@ -71,7 +71,7 @@ Please check here, what modifications are required for your CoverUI model: - [YardForce Classic 500**B**, RM-ECOW-V1.3.1](README-MOD-YF-C500B_RM-ECOW-V1.3.1.md) - [YardForce SA/SC/NX-Type 10 Buttons, 12 LEDs, RM-ECOW-V1.0.0](README-MOD-YF-SASCNX_RM-ECOW-V1.0.0.md) - [YardForce SA/SC/NX-Type 9 Buttons, 11 LEDs, RM-ECOW-V1.1.0](README-MOD-YF-SASCNX_RM-ECOW-V1.1.0.md) -- [YardForce SA/SC/NX-Type 18 Buttons, 3 LEDs, 256*64 Pixel LC-Display, RM-EC3-V1.1](README-MOD-YF-SASCNX_RM-EC3-V1.1.md) +- [YardForce NX100i (SA/SC/NX-Type) 18 Buttons, 3 LEDs, 256*64 Pixel LC-Display, RM-EC3-V1.1](README-MOD-YF-SASCNX_RM-EC3-V1.1.md) - [YardForce SA/SC-PRO-Type 6 Buttons, 240*160 Dot-Matrix-Display, HS49067](README-MOD-YF-SASC_PRO_HS49067.md) @@ -155,7 +155,7 @@ Once flashed, the CoverUI should show you a quick LED animation when powered on. ### Meaning of the LEDs: -| C500(B) | SA/SC/NX-Type,
9 Buttons, 11 LEDs | SA/SC/NX-Type,
10 Buttons, 12 LEDs | NX100i
(18 Buttons, 3 LEDs, 256*64 Pixel LC-Display), SA/SC/NX-Type | SA/SC-PRO
(240*160 Pixel LC-Display) | Remark | +| C500(B) | SA/SC/NX-Type,
9 Buttons, 11 LEDs | SA/SC/NX-Type,
10 Buttons, 12 LEDs | NX100i
(SA/SC/NX-Type),
18 Buttons, 3 LEDs,
256*64 Pixel LC-Display | SA/SC-PRO
(240*160 Pixel LC-Display) | Remark | | :-------: | :-----: | :-------: | :----: | :----: | ----- | | 2hr - 8hr | 4H - 10H | 4H - 10H | | | 4 digi GPS quality progressbar. Blink = No GPS-Fix | S1 | :heavy_check_mark: | :heavy_check_mark: | | | ROS State:
On = Running (idle)
Blink-slow = Autonomous mode (mowing, (un-)docking)
Blink-fast = Area recording
Off = ROS not running | @@ -169,7 +169,7 @@ Once flashed, the CoverUI should show you a quick LED animation when powered on. ### Button usage: -| C500(B) | SA/SC/NX-Type,
9 Buttons, 11 LEDs | SA/SC/NX-Type,
10 Buttons, 12 LEDs | NX100i
(18 Buttons, 3 LEDs, 256*64 Pixel LC-Display), SA/SC/NX-Type | SA/SC-PRO
(240*160 Pixel LC-Display) | Remark | +| C500(B) | SA/SC/NX-Type,
9 Buttons, 11 LEDs | SA/SC/NX-Type,
10 Buttons, 12 LEDs | NX100i (SA/SC/NX-Type),
18 Buttons, 3 LEDs,
256*64 Pixel LC-Display | SA/SC-PRO
(240*160 Pixel LC-Display) | Remark | | :-------: | :----: | :---------------------------------: | :-----: | :----: | ----- | | Home | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | Go home (docking station) | | | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | Start | Start mowing or continue | @@ -177,7 +177,7 @@ Once flashed, the CoverUI should show you a quick LED animation when powered on. | S2 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | Skip over to next area if 'mowing'. Long press (2-3s) in 'idle', delete all recorded areas! | | Lock | :heavy_check_mark: | :heavy_check_mark: | :x: | Enter | Long press (2-3s) reset emergency | | OK+Sun | Lock+10H | Setup+10H | MENU+BACK | :x: | Trigger power-on animation.
This is mainly for mounting validation, to ensure that you hit all LED guidance holes | -| OK+Clock | Lock+4H | Setup+4H | MENU+0 | :x: | Display CoverUI- firmware version. LED only models will show the version as a number sequence[^2], which should show (as of writing) 205 | +| OK+Clock | Lock+4H | Setup+4H | MENU+0 | :x: | Display CoverUI- firmware version. LED only models will show the version as a number sequence[^2], which should show (as of writing) 206 | | Mon | 4H | 4H | 1 | | Volume up[^3] | | Tue | 6H | 6H | 2 | | Volume down[^3] | | Wed | :x: | 8H | 3 | Back | ~~Next language~~ Language selection has moved to mower_config[^3] | @@ -218,7 +218,7 @@ check section [usage](#usage) about the [LED](#meaning-of-the-leds) and [Button] | 1.00 | - Stock-Cable support for rain & hall sensors
- Classic 500B support | 2023-05-13 | NA | First OM's CoverUI port for YardForce Classic 500 | 2023-05-01 -[^4]: Press magic button combo 'Display CoverUI- firmware version' (see [Button Usage](#button-usage)) to get your installed FW version displayed +[^4]: Press sys-req button combo 'Display CoverUI- firmware version' (see [Button Usage](#button-usage)) to get your installed FW version displayed ## Contributing diff --git a/Firmware/CoverUI/YardForce/include/ABC_Display.hpp b/Firmware/CoverUI/YardForce/include/ABC_Display.hpp new file mode 100644 index 0000000..8e3af6c --- /dev/null +++ b/Firmware/CoverUI/YardForce/include/ABC_Display.hpp @@ -0,0 +1,120 @@ +/** + * @file Display.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce CoverUI abstract display class header for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2023-11-27 + * + * @copyright Copyright (c) 2023 + */ +#ifndef YARDFORCE_ABC_DISPLAY_HPP +#define YARDFORCE_ABC_DISPLAY_HPP + +#define LVGL_BUFFER_MULTIPLIER 10 +#define LVGL_TIMER_HANDLER_PERIOD_MS 10 // 10ms lv_timer_handler() soft period + +#include +#include + +#include "../BttnCtl.h" +#include "include/main.h" +#include "include/subscription.h" + +#define BACKLIGHT_TIMEOUT_MS 120000 // 2 minutes + +namespace yardforce { +namespace display { +class AbstractDisplay { + public: + struct Config { + uint8_t backlight_led_num; + }; + + enum AnncmntType { + none, + close_hatch, + version, + }; + + struct Anncmnt // Announcement + { + uint32_t timeout; // Timeout (ms) + AnncmntType type; + }; + + AbstractDisplay(Config t_config) : config(t_config) {}; + + virtual bool init() = 0; // Init GPIOs, comms, as well as display + + /** + * @brief Tell LVGL that milliseconds have been elapsed. + * Required for anim or similar. Should be called in a high priority routine like hwtimer. + * + * @param ms + */ + void tick_inc(uint8_t ms) { + lv_tick_inc(ms); + } + + // Low priority loop for display changes. Also does lv_timer_handler() and subscription + virtual void loop_low_prio() { + lv_timer_handler(); + subscription::subscribe((Topic_set_ll_status | Topic_set_hl_state), 500); + check_backlight(); + } + + /** + * @brief Set backlight LED state and set/reset timeout counter + * + * @param state LED_state, default LED_on + * @param timeout in ms when to switch off + */ + void set_backlight(LED_state t_state = LED_on, uint32_t t_timeout = BACKLIGHT_TIMEOUT_MS) { + ::leds.set(config.backlight_led_num, t_state); + if (t_state != LED_off) + backlight_timeout = millis() + t_timeout; + backlight_state = t_state; + } + + LED_state check_backlight() { + if (backlight_state == LED_off) + return backlight_state; + + if (millis() < backlight_timeout) + return backlight_state; + + set_backlight(LED_off); + return backlight_state; + } + + // Start a new announcement + void start_anncmnt(uint32_t t_timeout_ms, AnncmntType t_type) { + anncmnt = { + .timeout = millis() + t_timeout_ms, + .type = t_type}; + } + + // Get announcement type if an announcement is running + AnncmntType get_anncmnt() { + if (!anncmnt.timeout) + return AnncmntType::none; + + if (millis() < anncmnt.timeout) + return anncmnt.type; + + anncmnt.timeout = 0; + return AnncmntType::none; + } + + protected: + Config config; + Anncmnt anncmnt; + + // Backlight handling + LED_state backlight_state; + uint32_t backlight_timeout; +}; // class Display + +} // namespace display +} // namespace yardforce +#endif // YARDFORCE_ABC_DISPLAY_HPP \ No newline at end of file diff --git a/Firmware/CoverUI/YardForce/include/ABC_Hatch.hpp b/Firmware/CoverUI/YardForce/include/ABC_Hatch.hpp new file mode 100644 index 0000000..2a6b419 --- /dev/null +++ b/Firmware/CoverUI/YardForce/include/ABC_Hatch.hpp @@ -0,0 +1,64 @@ +/** + * @file Hatch.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce CoverUI Abstract Hatch class for OpenMower https://github.com/ClemensElflein/OpenMower + * This is for YardForce models (like SA, SC or NX), whose CoverUI is behind a hatch. + * Those need some special (button) handling because opening the hatch (to reach the buttons), triggers stop-emergency. + * @version 0.3 + * @date 2024-09-30 + * + * @copyright Copyright (c) 2023, 2024 + * + */ +#ifndef YARDFORCE_ABC_HATCH_HPP +#define YARDFORCE_ABC_HATCH_HPP + +#include +#include + +const unsigned int FAKE_BUTTON_QUEUE_SIZE = 10; + +class AbstractHatch { + public: + struct FakeButton { + uint8_t button_id; + uint8_t press_duration; // 0 for single press, 1 for long, 2 for very long press + uint32_t delay_end; + }; + + /** + * @brief Handle any kind of pressed button result like: + * 1. Convert a button id to some other value + * 2. Trigger count-down for hatch-close + * 3. Queue fake (delayed) buttons + * 4. Handle fake_button_queue and probably directly send Get_Button packet + * + * @param button_id + * @param press_time + * @return unsigned int of the probably converted or ignored button ID + */ + virtual unsigned int handle_button(unsigned int button_id, unsigned int press_cnt) = 0; + + void queue_button(uint8_t button_id, uint8_t press_duration, uint32_t delay) { + fake_button_queue.push({button_id, press_duration, millis() + delay}); + }; + + /** + * @brief Loop over fake_button_queue and process(send) ready ones + */ + void process_queued() { + if (fake_button_queue.empty() || fake_button_queue.full()) + return; + + auto first = fake_button_queue.front(); + if (millis() >= first.delay_end) { + buttons.send(first.button_id, first.press_duration); + fake_button_queue.pop(); + } + }; + + protected: + etl::queue fake_button_queue; +}; + +#endif // YARDFORCE_ABC_HATCH_HPP diff --git a/Firmware/CoverUI/YardForce/include/ButtonDebouncer.hpp b/Firmware/CoverUI/YardForce/include/ButtonDebouncer.hpp index f5f3f8b..c572312 100644 --- a/Firmware/CoverUI/YardForce/include/ButtonDebouncer.hpp +++ b/Firmware/CoverUI/YardForce/include/ButtonDebouncer.hpp @@ -1,14 +1,14 @@ /** * @file ButtonDebouncer.hpp * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI Button-Debouncer header for OpenMower https://github.com/ClemensElflein/OpenMower + * @brief YardForce CoverUI Button-Debouncer Class for OpenMower https://github.com/ClemensElflein/OpenMower * Debouncing is done by continuos simple shifting the port states into an state array for later processing. * See Jack Ganssle debouncing http://www.ganssle.com/debouncing-pt2.htm * For code simplicity/speed, I debounce all pins, regardless if it has a button or not. Button separation has to be done by calling class. - * @version 0.3 - * @date 2023-10-26 + * @version 0.4 + * @date 2024-10-01 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023, 2024 * */ #ifndef YARDFORCE_BUTTONDEBOUNCER_HPP @@ -16,19 +16,54 @@ #include -#define NUM_BUTTON_STATES 8 // * 5ms timer = 40ms bouncing-button states = debounced after 40ms +#define NUM_BUTTON_STATES 8 // * 5ms timer = 40ms bouncing-button states = debounced after 40ms -class ButtonDebouncer -{ -public: - void process_state(const uint32_t gpio_port_nr); // Has to get called regulary i.e. by timer (5ms) and store the (buttons) port state within states_ array - bool is_pressed(uint8_t pin); +class ButtonDebouncer { + public: + /** + * @brief Has to get called regulary i.e. by timer (5ms), which store the (buttons) port state within states_ array. + * + * @param gpio_port_nr which shall be debounced + */ + void process_state(const uint32_t gpio_port_nr) { +#ifdef MCU_STM32 + auto gpio_port = get_GPIO_Port(gpio_port_nr); + states_[state_index_] = gpio_port->IDR ^ 0xFFFF; // XOR changes for pull-up states_ +#else + states_[state_index_] = GPIO_ISTAT(gpio_port[gpio_port_nr]) ^ 0xFFFF; // XOR changes for pull-up states_ +#endif -private: - uint16_t states_[NUM_BUTTON_STATES]; // GPIO port state recorder (every time interval = 5ms) - uint8_t state_index_ = 0; // Index for next states_ store positions - volatile uint16_t state_debounced_; // Debounced buttons state - uint16_t state_changed_; // Just changed buttons + // Debounce + uint16_t laststate_debounced_ = state_debounced_; + unsigned int i; + for (i = 0, state_debounced_ = 0xFFFF; i < NUM_BUTTON_STATES; i++) + state_debounced_ &= states_[i]; + + // Circular buffer index + state_index_++; + if (state_index_ >= NUM_BUTTON_STATES) + state_index_ = 0; + + // Save what changed + state_changed_ = state_debounced_ ^ laststate_debounced_; + } + + /** + * @brief Return boolean true if the given pin's button is pressed. + * Take into notice that the returned state is already debounced. + * + * @param pin digital_pin + * @return true if pressed, false if not pressed + */ + bool is_pressed(uint8_t pin) { + return state_debounced_ & digitalPinToBitMask(pin); + } + + private: + uint16_t states_[NUM_BUTTON_STATES]; // GPIO port state recorder (every time interval = 5ms) + uint8_t state_index_ = 0; // Index for next states_ store positions + volatile uint16_t state_debounced_; // Debounced buttons state + uint16_t state_changed_; // Just changed buttons }; -#endif // YARDFORCE_BUTTONDEBOUNCER_HPP +#endif // YARDFORCE_BUTTONDEBOUNCER_HPP diff --git a/Firmware/CoverUI/YardForce/include/Buttons.hpp b/Firmware/CoverUI/YardForce/include/Buttons.hpp index b87734a..eca6636 100644 --- a/Firmware/CoverUI/YardForce/include/Buttons.hpp +++ b/Firmware/CoverUI/YardForce/include/Buttons.hpp @@ -1,9 +1,9 @@ /** * @file Buttons.hpp * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI Buttons header for OpenMower https://github.com/ClemensElflein/OpenMower + * @brief YardForce CoverUI Buttons class for OpenMower https://github.com/ClemensElflein/OpenMower * @version 0.7 - * @date 2024-09-30 + * @date 2024-10-01 * * @copyright Copyright (c) 2023, 2024 * @@ -17,7 +17,8 @@ #include -#include "ButtonDebouncer.hpp" +#include "../../BttnCtl.h" +#include "../include/ButtonDebouncer.hpp" // Logic button numbers. Take attention that OM known buttons need to have the same logic number! // 0 is reserved for no-button return @@ -56,7 +57,17 @@ #define BTN_8_NUM 56 // ASCII 8, i.e. RM-EC3-V11 #define BTN_9_NUM 57 // ASCII 9, i.e. RM-EC3-V11 -#define MAX_BTN_GPIO_PORTS 6 // Our MCUs have GPIOA-F as a max = 6 +#define BTN_HAS_NO_LED -1 + +#define MAX_BTN_GPIO_PORTS 6 // Our MCUs do have GPIO-Ports A-F = 6 + +#ifdef MCU_STM32 +#define DIGITAL_PIN_TO_PORT_NR(p) (STM_PORT(digitalPinToPinName(p))) +#else // MCU_GD32 +#define DIGITAL_PIN_TO_PORT_NR(p) (GD_PORT_GET(DIGITAL_TO_PINNAME(p))) +#endif + +extern void sendMessage(void *message, size_t size); class Buttons { public: @@ -64,22 +75,98 @@ class Buttons { uint8_t pin; int8_t led_num; // Corresponding LED num. -1 is none. }; + const std::map &kBtnDefByNumMap; // Ref to Map of Button-Num -> ButtonDef (pin & corresponding LED num) - const std::map &kBtnDefByNumMap; // Map of Button-Num -> ButtonDef (pin & corresponding LED num) - etl::map debouncer_by_gpio_port_nr_map; // Map of GPIO Port Nr -> debouncer object + Buttons(const std::map &t_kBtnDefByNumMap) : kBtnDefByNumMap(t_kBtnDefByNumMap) { + // Loop over Button-Num -> button pin map + for (auto const &it : kBtnDefByNumMap) { + // Create debouncer if not already exist for this Pin's GPIO_Port_Nr + uint32_t gpio_port_nr = DIGITAL_PIN_TO_PORT_NR(it.second.pin); + auto debouncer = debouncer_by_gpio_port_nr_map.find(gpio_port_nr); + if (debouncer == debouncer_by_gpio_port_nr_map.end()) + debouncer_by_gpio_port_nr_map.insert(etl::pair(gpio_port_nr, ButtonDebouncer())); + + pinMode(it.second.pin, INPUT_PULLUP); + } + } + + /** + * @brief Handle CoverUI specific keys like BootUp-Anim or version number display. + * Should be continuously called <= 500ms. + */ + virtual void handle_sys_req() {} - Buttons(const std::map &t_kBtnDefByNumMap) : kBtnDefByNumMap(t_kBtnDefByNumMap) {}; + /** + * @brief Get corresponding LED num for button num + * + * @param button_nr + * @return uint8_t LED num. -1 of not exists. + */ + int8_t get_led(uint8_t button_nr) { + auto btn_def_it = kBtnDefByNumMap.find(button_nr); // Find button_nr and get iterator pair + if (btn_def_it != kBtnDefByNumMap.end()) + return btn_def_it->second.led_num; - virtual ~Buttons() = default; + return -1; + } - int8_t get_led(uint8_t button_nr); - bool is_pressed(uint8_t button_nr); - uint8_t is_pressed(); + /** + * @brief Return ButtonNum of the first detected pressed button. + * Take into notice that the returned state is already debounced. + * + * @return uint8_t 0 = none pressed, >0 = ButtonNum + */ + uint8_t is_pressed() { + for (auto const &it : kBtnDefByNumMap) // Loop over Button-Num -> button pin map + if (is_pressed(it.first)) + return it.first; - void process_states(); + return 0; + } - void setup(); - void send(uint16_t button_id, uint8_t press_duration); + /** + * @brief Return boolean true if the given button number is pressed. + * Take into notice that the returned state is already debounced. + * + * @param uint8_t button_nr + * @return true if pressed, false if not pressed + */ + bool is_pressed(uint8_t button_nr) { + auto btn_def_it = kBtnDefByNumMap.find(button_nr); // Find button_nr and get iterator pair + if (btn_def_it == kBtnDefByNumMap.end()) + return false; + + uint32_t gpio_port_nr = DIGITAL_PIN_TO_PORT_NR(btn_def_it->second.pin); + auto debouncer_it = debouncer_by_gpio_port_nr_map.find(gpio_port_nr); // Find debouncer and get iterator pair + if (debouncer_it == debouncer_by_gpio_port_nr_map.end()) + return false; + + return debouncer_it->second.is_pressed(btn_def_it->second.pin); + } + + /** + * @brief Process GPIO states by debouncer. Has to get called regulary i.e. by timer (5ms) + * + */ + void process_states() { + for (auto &it : debouncer_by_gpio_port_nr_map) + it.second.process_state(it.first); + } + + /** + * @brief Send 'rain' message via COBS with last read rain-sensor- value (together with (currently static) threshold) + * + */ + void send(uint16_t button_id, uint8_t press_duration) { + msg_event_button msg = { + .type = Get_Button, + .button_id = button_id, + .press_duration = press_duration}; + sendMessage(&msg, sizeof(msg)); + } + + private: + etl::map debouncer_by_gpio_port_nr_map; // Map of GPIO Port Nr -> debouncer object }; #endif // YARDFORCE_BUTTONS_HPP diff --git a/Firmware/CoverUI/YardForce/include/Display.hpp b/Firmware/CoverUI/YardForce/include/Display.hpp deleted file mode 100644 index 6362bf5..0000000 --- a/Firmware/CoverUI/YardForce/include/Display.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @file Display.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI abstract display class header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-27 - * - * @copyright Copyright (c) 2023 - */ -#ifndef YARDFORCE_DISPLAY_HPP -#define YARDFORCE_DISPLAY_HPP - -#define LVGL_BUFFER_MULTIPLIER 10 -#define LVGL_TIMER_HANDLER_PERIOD_MS 10 // 10ms lv_timer_handler() soft period - -#include -#include "../BttnCtl.h" - -#define BACKLIGHT_TIMEOUT_MS 120000 // 2 minutes - -namespace yardforce -{ - namespace display - { - class Display - { - public: - struct Config - { - uint8_t backlight_led_num; - }; - - enum AnncmntType - { - none, - close_hatch, - version, - }; - - struct Anncmnt // Announcement - { - uint32_t timeout; // Timeout (ms) - AnncmntType type; - }; - - Display(Config t_config) : config(t_config){}; - - virtual bool init() = 0; // Init GPIOs, comms, as well as display - - void tick_inc(uint8_t ms); - virtual void loop_low_prio(); // Low priority loop for display changes. Also does lv_timer_handler() and subscription - - void set_backlight(LED_state t_state = LED_on, uint32_t t_timeout = BACKLIGHT_TIMEOUT_MS); - LED_state check_backlight(); - - void start_anncmnt(uint32_t t_timeout_ms, AnncmntType t_type); // Start a new announcement - AnncmntType get_anncmnt(); // Get announcement type if an announcement is running - - protected: - Config config; - Anncmnt anncmnt; - - // Backlight handling - LED_state backlight_state; - uint32_t backlight_timeout; - }; // class Display - - } // namespace display -} // namespace yardforce -#endif // YARDFORCE_DISPLAY_HPP \ No newline at end of file diff --git a/Firmware/CoverUI/YardForce/include/Emergency.hpp b/Firmware/CoverUI/YardForce/include/Emergency.hpp index 9385e70..b258583 100644 --- a/Firmware/CoverUI/YardForce/include/Emergency.hpp +++ b/Firmware/CoverUI/YardForce/include/Emergency.hpp @@ -1,11 +1,11 @@ /** * @file Emergency.hpp * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI Emergency header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.3 - * @date 2023-11-05 + * @brief YardForce CoverUI Emergency class for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.4 + * @date 2024-10-02 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023, 2024 * */ @@ -13,36 +13,98 @@ #define YARDFORCE_EMERGENCY_HPP #include + #include "../../BttnCtl.h" -#define PERIODIC_SEND_CYCLE 1000 // Periodic cycle (ms) how often to send emergency state (if there's no active emergency) +#define PERIODIC_SEND_CYCLE 1000 // Periodic cycle (ms) how often to send emergency state (if there's no active emergency) -struct EmergencyPinStateDef -{ - uint8_t pin; - uint8_t pin_mode; - Emergency_state state; -}; +extern void sendMessage(void *message, size_t size); +class Emergency { + public: + struct PinStateDef { + uint8_t pin; + uint8_t pin_mode; + Emergency_state state; + }; -class Emergency -{ -public: - const EmergencyPinStateDef *kEmergencyPinStatesPtr; // Pointer to an array of EmergencyPinStateDef's (order doesn't matter) + const PinStateDef *kPinStatesPtr; // Pointer to an array of PinStateDef's (order doesn't matter) const size_t kNumEmergencies; - Emergency(const EmergencyPinStateDef *t_kEmergencyPinStatesPtr, const size_t t_kNumEmergencies) : kEmergencyPinStatesPtr(t_kEmergencyPinStatesPtr), kNumEmergencies(t_kNumEmergencies){}; + Emergency(const PinStateDef *t_kPinStatesPtr, const size_t t_kNumEmergencies) : kPinStatesPtr(t_kPinStatesPtr), kNumEmergencies(t_kNumEmergencies) { + for (size_t i = 0; i < kNumEmergencies; i++) { + auto pin_state = *(kPinStatesPtr + i); + pinMode(pin_state.pin, pin_state.pin_mode); + } + }; + + /** + * @brief Read all related hall-sensors and update state_ + * + */ + void read() { + state_ = 0; // We might have more emergency sensors and switch than OM Emergency_states. So we need to OR them instead of assign them 1:1 + for (size_t i = 0; i < kNumEmergencies; i++) { + auto pin_state = *(kPinStatesPtr + i); + if (digitalRead(pin_state.pin) == HIGH) + state_ |= pin_state.state; + } + if (state_) + state_ |= Emergency_state::Emergency_latch; + } + + /** + * @brief Send 'emergency' message via COBS with latest state_ + * + */ + void send() { + msg_event_emergency msg = { + .type = Get_Emergency, + .state = state_}; + sendMessage(&msg, sizeof(msg)); + state_last_sent_ = state_; + } + + /** + * @brief read() related hall sensors and send if a new emergency occurred. + * Get called constantly by quick (5ms) timer + * + */ + void read_and_send_if_emergency() { + read(); + + if (state_ & Emergency_state::Emergency_latch && !(state_last_sent_ & Emergency_state::Emergency_latch)) { + send(); + next_periodic_cycle = millis() + PERIODIC_SEND_CYCLE; + } + } + + /** + * @brief Periodically send emergency state. + * Get called by fast (100ms) timer. + * An active emergency state get send on each call. + * An inactive emergency state get only every PERIODIC_SEND_CYCLE + */ + void periodic_send() { + // Active emergency + if (state_ & Emergency_state::Emergency_latch) { + send(); + next_periodic_cycle = millis() + PERIODIC_SEND_CYCLE; + return; + } + + // Inactive emergency + if (millis() < next_periodic_cycle) + return; - void setup(); - void read(); - void send(); - void read_and_send_if_emergency(); - void periodic_send(); + send(); + next_periodic_cycle = millis() + PERIODIC_SEND_CYCLE; + } -private: + private: volatile uint32_t next_periodic_cycle = PERIODIC_SEND_CYCLE; - volatile uint8_t state_ = 0; // Current emergency state, set by read() - uint8_t state_last_sent_ = 0; // Copy of last sent state_ + volatile uint8_t state_ = 0; // Current emergency state, set by read() + uint8_t state_last_sent_ = 0; // Copy of last sent state_ }; -#endif // YARDFORCE_EMERGENCY_HPP +#endif // YARDFORCE_EMERGENCY_HPP diff --git a/Firmware/CoverUI/YardForce/include/Hatch.hpp b/Firmware/CoverUI/YardForce/include/Hatch.hpp deleted file mode 100644 index 4f8c012..0000000 --- a/Firmware/CoverUI/YardForce/include/Hatch.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @file Hatch.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI Hatch header for OpenMower https://github.com/ClemensElflein/OpenMower - * This is for YardForce models (like SA, SC or NX), whose CoverUI is behind a hatch. - * Those need some special (button) handling because opening the hatch (to reach the buttons), triggers stop-emergency. - * @version 0.3 - * @date 2024-09-30 - * - * @copyright Copyright (c) 2023, 2024 - * - */ -#ifndef YARDFORCE_HATCH_HPP -#define YARDFORCE_HATCH_HPP - -#include -#include - -#define FAKE_BUTTON_QUEUE_SIZE 10 - -class Hatch -{ -public: - struct FakeButton - { - uint8_t button_id; - uint8_t press_duration; // 0 for single press, 1 for long, 2 for very long press - uint32_t delay_end; - }; - - virtual ~Hatch() = default; - - unsigned int handle_button(unsigned int button_id, uint32_t press_time); - void queue_button(uint8_t button_id, uint8_t press_duration, uint32_t delay); - void process_queued(); - -protected: - etl::queue fake_button_queue; -}; - -#endif // YARDFORCE_HATCH_HPP diff --git a/Firmware/CoverUI/YardForce/include/LEDcontrol.hpp b/Firmware/CoverUI/YardForce/include/LEDcontrol.hpp index 2585afc..db485d9 100644 --- a/Firmware/CoverUI/YardForce/include/LEDcontrol.hpp +++ b/Firmware/CoverUI/YardForce/include/LEDcontrol.hpp @@ -8,16 +8,17 @@ * @copyright Copyright (c) 2023 * */ -#ifndef YARDFORCE_LEDCONTROL_H -#define YARDFORCE_LEDCONTROL_H +#ifndef YARDFORCE_LEDCONTROL_HPP +#define YARDFORCE_LEDCONTROL_HPP #include #include + #include "../../BttnCtl.h" -#define LED_PIN_NC 0xffffffff // Not Connected (virtual LED) +#define LED_PIN_NC 0xffffffff // Not Connected (virtual LED) -#define LED_NUM_OM_MAX 17 // Highest LED num controlled by OM LL FW +#define LED_NUM_OM_MAX 17 // Highest LED num controlled by OM LL FW // Some handy LED-num defines #define LED_NUM_CHARGE 0 @@ -32,21 +33,17 @@ #define LED_NUM_2HR 17 #define LED_NUM_REAR 18 -class LEDcontrol -{ -public: - const uint32_t *kPinByLedNumPtr; // Pointer to an array of LED pins, indexed by OM-LED-Num - const size_t kNumLeds; // Number of defined LEDs - void (LEDcontrol::*set_base10_leds_cb)(char digit); // Callback pointer to set_base10_leds_cb(char digit) method +class LEDcontrol { + public: + const uint32_t *kPinByLedNumPtr; // Pointer to an array of LED pins, indexed by OM-LED-Num + const size_t kNumLeds; // Number of defined LEDs + void (LEDcontrol::*set_base10_leds_cb)(char digit); // Callback pointer to set_base10_leds_cb(char digit) method - LEDcontrol(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds); - LEDcontrol(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds, void (LEDcontrol::*t_set_base10_leds_cb)(char digit)); + LEDcontrol(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds, void (LEDcontrol::*t_set_base10_leds_cb)(char digit) = nullptr); virtual ~LEDcontrol() = default; - void setup(); - - void blink_timer_elapsed(LED_state blink_state); // Get called by responsible blink timer + void blink_timer_elapsed(LED_state blink_state); // Get called by responsible blink timer void force_off(uint8_t led_num, bool force); // Switch/force LED num off, independent of it's running state virtual void force_off_num_seq_leds(bool force) {}; // Force off all show_num() / sequence-num LEDs @@ -60,32 +57,32 @@ class LEDcontrol virtual void set_base10_leds(char digit) {}; // Set LED representation for a single digit (by default empty) void toggle(uint8_t led_num, bool change_state = true); // Set any of known LED_state states for a specific LED - virtual unsigned int boot_animation() { return 0; }; // A short boot animation which return the amount of ms it will take + virtual unsigned int boot_animation() { return 0; }; // A short boot animation which return the amount of ms it will take // ***** Sequence stuff ***** - void process_sequence(); // Called by timer for LED sequences like animation or FW version display - void sequence_start(void (LEDcontrol::*handler)(), bool abort_running = false); // Start the given sequence method - virtual void sequence_animate_handler(){}; // A short LED Animation handler - virtual void sequence_countdown_handler(){}; // A short LED Countdown handler - void show_num(uint16_t); // Display a number via Mon-Sun + Lifted LED + void process_sequence(); // Called by timer for LED sequences like animation or FW version display + void sequence_start(void (LEDcontrol::*handler)(), bool abort_running = false); // Start the given sequence method + virtual void sequence_animate_handler() {}; // A short LED Animation handler + virtual void sequence_countdown_handler() {}; // A short LED Countdown handler + void show_num(uint16_t); // Display a number via Mon-Sun + Lifted LED -protected: - uint64_t led_states_bin_ = 0; // Binary representation of all LEDs. Each LED gets three bits (19*3=57) for the current state (see BtnCtrl.h) - uint32_t seq_start_tick_ = 0; // ms tick when sequence started. Indicator for a running sequence if > 0 - uint16_t seq_get_next_step_(uint16_t step_rate); // Get next sequence step for the given step-rate. Return 0 if the next step isn't reached now. + protected: + uint64_t led_states_bin_ = 0; // Binary representation of all LEDs. Each LED gets three bits (19*3=57) for the current state (see BtnCtrl.h) + uint32_t seq_start_tick_ = 0; // ms tick when sequence started. Indicator for a running sequence if > 0 + uint16_t seq_get_next_step_(uint16_t step_rate); // Get next sequence step for the given step-rate. Return 0 if the next step isn't reached now. -private: - uint32_t force_led_on_ = 0; // Binary representation of a "forced LED on" led_states_bin_ overrule - uint32_t force_led_off_ = 0; // Binary representation of a "forced LED off" led_states_bin_ overrule + private: + uint32_t force_led_on_ = 0; // Binary representation of a "forced LED on" led_states_bin_ overrule + uint32_t force_led_off_ = 0; // Binary representation of a "forced LED off" led_states_bin_ overrule - void change_led_states_(uint8_t led_num, LED_state state); // Change led_states_bin_ for the given LED num and state - void force_(uint8_t led_num, bool force, uint32_t *force_type_); // Switch/force LED num on or off, independent on it's running state + void change_led_states_(uint8_t led_num, LED_state state); // Change led_states_bin_ for the given LED num and state + void force_(uint8_t led_num, bool force, uint32_t *force_type_); // Switch/force LED num on or off, independent on it's running state - uint16_t seq_step_ = 0; // Step position of the current running sequence - uint16_t seq_num_value_; // To lazy now to build a common member function pointer with variable function arg length + uint16_t seq_step_ = 0; // Step position of the current running sequence + uint16_t seq_num_value_; // To lazy now to build a common member function pointer with variable function arg length - void seq_num_handler_(); // Sequence handler for displaying a number via Mon-Sun + Lifted LED - void (LEDcontrol::*seq_handler_)(void); // Member function ptr to sequence handler + void seq_num_handler_(); // Sequence handler for displaying a number via Mon-Sun + Lifted LED + void (LEDcontrol::*seq_handler_)(void); // Member function ptr to sequence handler }; -#endif // YARDFORCE_LEDCONTROL_H +#endif // YARDFORCE_LEDCONTROL_HPP diff --git a/Firmware/CoverUI/YardForce/include/main.h b/Firmware/CoverUI/YardForce/include/main.h index f6d7bb4..b2e1fda 100644 --- a/Firmware/CoverUI/YardForce/include/main.h +++ b/Firmware/CoverUI/YardForce/include/main.h @@ -11,7 +11,8 @@ #ifndef __YARDFORCE_MAIN_H #define __YARDFORCE_MAIN_H -#include // Stock CoverUI is build now via Arduino framework (instead of HAL), which is ATM the only framework with STM32F030R8 and GD32F330R8 support +#include // Stock CoverUI is build now via Arduino framework (instead of HAL), which is ATM the only framework with STM32F030R8 and GD32F330R8 support + #include "datatypes.h" #if defined(FIRMWARE_VERSION) && !(FIRMWARE_VERSION == 200) @@ -23,15 +24,15 @@ #define FIRMWARE_VERSION 206 #if defined(MDL_C500) -#include "model_C500.h" +#include "../model/C500/assembly.hpp" #elif defined(MDL_RMECOWV100) -#include "model_RM-ECOW-V100.h" +#include "../model/RM-ECOW-V100/assembly.hpp" #elif defined(MDL_RMECOWV110) -#include "model_RM-ECOW-V110.h" +#include "../model/RM-ECOW-V110/assembly.hpp" #elif defined(MDL_RMEC3V11) -#include "model_RM-EC3-V11.h" +#include "../model/RM-EC3-V11/assembly.hpp" #elif defined(MDL_SAXPRO) -#include "model_SAxPRO.h" +#include "../model/SAxPRO/assembly.hpp" #else #pragma GCC error "Missing model header!" #include @@ -69,4 +70,4 @@ extern void uart_putc(HardwareSerial *Serial, uint8_t c); extern bool uart_is_readable(HardwareSerial *Serial); extern void Force_LED_off(uint8_t led_num, bool force); -#endif // __YARDFORCE_MAIN_H \ No newline at end of file +#endif // __YARDFORCE_MAIN_H \ No newline at end of file diff --git a/Firmware/CoverUI/YardForce/include/model/Display_RM-EC3-V11.cpp b/Firmware/CoverUI/YardForce/include/model/Display_RM-EC3-V11.cpp deleted file mode 100644 index 377078e..0000000 --- a/Firmware/CoverUI/YardForce/include/model/Display_RM-EC3-V11.cpp +++ /dev/null @@ -1,349 +0,0 @@ -/** - * @file Display_RM-EC3-V11.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI RM-EC3-V1.1 display driver class for OpenMower https://github.com/ClemensElflein/OpenMower - * This JLX25664 dispay is 4-level (2 Bit) gray scale capable, but for ease of development, it's driven as monochrome display. - * @version 0.1 - * @date 2023-11-27 - * - * @copyright Copyright (c) 2023 - */ -#include "Display_RM-EC3-V11.hpp" -#include -#include "../subscription.h" -#include "../main.h" - -// C images -LV_IMG_DECLARE(OM_Logo_120x54x1); -LV_IMG_DECLARE(OM_Wordmark_240x35x1); - -namespace yardforce -{ - namespace display - { - static controller::ST75256 st75256; // Controller driver - - // LVGL buffers - static lv_disp_draw_buf_t lv_disp_buf; - static lv_color_t lv_buf_1[ST75256_DISPLAY_WIDTH * LVGL_BUFFER_MULTIPLIER]; - static lv_color_t lv_buf_2[ST75256_DISPLAY_WIDTH * LVGL_BUFFER_MULTIPLIER]; - - // Status Screen Widgets - lvgl::WidgetLedSymbol *v_led_heartbeat, *v_led_ros, - *v_led_emergency_wheel, *v_led_emergency, *v_led_emergency_stop, - *v_led_bat, *v_led_gps, *v_led_charge, *v_led_power; - lvgl::WidgetBar *bar_gps, *bar_bat; - lvgl::WidgetTextTicker *text_ticker_status; - - bool main_screen_active = false; // Initialized and active - bool last_docked_state_; // true = docked, false = undocked - - bool DisplayRMEC3V11::init() - { - // Init LCD display controller - st75256.init(); - - // Init LVGL - lv_init(); - lv_disp_drv_init(&lv_disp_drv); // Basic LVGL display driver initialization - lv_disp_drv.draw_buf = &lv_disp_buf; // Set an initialized buffer - lv_disp_drv.rounder_cb = rounder_cb; // Round x coordinated so that it fit for our 3 RGB pixel/per dot display - lv_disp_drv.flush_cb = flush_cb; // Set a flush callback to draw to the display - lv_disp_drv.hor_res = ST75256_DISPLAY_WIDTH; // Set the horizontal resolution in pixels - lv_disp_drv.ver_res = ST75256_DISPLAY_HEIGHT; // Set the vertical resolution in pixels - lv_disp_draw_buf_init(&lv_disp_buf, lv_buf_1, lv_buf_2, ST75256_DISPLAY_WIDTH * LVGL_BUFFER_MULTIPLIER); // Initialize `disp_buf` with the buffer(s) - lv_disp_t *disp; - disp = lv_disp_drv_register(&lv_disp_drv); // Register the driver and save the created display objects - lv_obj_set_style_bg_color(lv_scr_act(), lv_color_white(), LV_PART_MAIN); // No background color - - openmower_anim(); - //mainScreen(); - - return true; - } - - /** - * @brief Rounder callback will round the display area to a multiple of 8, on y axis (because one DDRAM byte are 8 y-pixels (page), per column) - * - * @param disp_drv - * @param area - */ - void DisplayRMEC3V11::rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) - { - area->y1 = area->y1 - (area->y1 % 8); // Round down to neares multiple of 8 - area->y2 = (area->y2 + 8) - (area->y2 % 8) - 1; // Round up to nearest multiple of 8, minus -1 - } - - /** - * @brief Flush display buffer to display controller. - * - * @param disp_drv - * @param area - * @param t_color_p - */ - void DisplayRMEC3V11::flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *t_color_p) - { - size_t cols = (area->x2 - area->x1) + 1; // Num of columns for this flush area - uint8_t page_pixel_buffer[cols]; // Store 8 y pixel (LSB = top) for area of x - uint8_t y_shift = 0; - - st75256.send_ctrl(0x30); // EXT1=0, EXT0=0 - st75256.set_column_address(area->x1, area->x2); - st75256.set_page_address(area->y1 / 8, (area->y2 - 7) / 8); // ATTENTION: Will only work with a correct rounder_cb() - st75256.send_ctrl(0b01011100); // [10] Write Data - - for (size_t y = area->y1; y <= area->y2; y++) - { - for (size_t x = 0; x < cols; x++) - { - t_color_p->full ? page_pixel_buffer[x] &= ~(1 << y_shift) : page_pixel_buffer[x] |= (1 << y_shift); - t_color_p++; - } - if (y_shift < 7) - { - y_shift++; - continue; - } - st75256.send_data(page_pixel_buffer, cols); - y_shift = 0; - } - lv_disp_flush_ready(disp_drv); - } - - void DisplayRMEC3V11::set_undocked() - { - v_led_power->set(LED_off); - v_led_charge->set(LED_off); - bar_bat->set_range(BATT_ABS_MIN, BATT_ABS_MAX); - bar_bat->bar_label = FA_SYMBOL_BATTERY " %d V"; - last_docked_state_ = false; - } - - void DisplayRMEC3V11::mainScreen() - { - lv_obj_clean(lv_scr_act()); - - // On the left side of the status bar we do have functional status symbols like heartbeat and ROS - v_led_ros = new lvgl::WidgetLedSymbol(FA_SYMBOL_ROS, LV_ALIGN_TOP_LEFT, 0, -1); // Leftmost - - // In the middle, we do have emergencies - v_led_emergency = new lvgl::WidgetLedSymbol(FA_SYMBOL_EMERGENCY, LV_ALIGN_TOP_MID, 0, -1); // Centered - v_led_emergency_wheel = new lvgl::WidgetLedSymbol(FA_SYMBOL_EMERGENCY_WHEEL, LV_ALIGN_TOP_MID, -14 - TOP_STATUS_BAR_GAP_PX - 2, -1); // Left of centered - // TODO: if next level LL proto - // v_led_heartbeat = new WidgetLedSymbol(FA_SYMBOL_HEARTBEAT, LV_ALIGN_TOP_MID, -(2 * 14) - (2 * TOP_STATUS_BAR_GAP_PX) - 2, -1); // 2nd left of centered - v_led_emergency_stop = new lvgl::WidgetLedSymbol(FA_SYMBOL_EMERGENCY_STOP, LV_ALIGN_TOP_MID, 14 + TOP_STATUS_BAR_GAP_PX, -1); // Right of centered - - // On the right side, mowing status like, charging, docking, ... - v_led_power = new lvgl::WidgetLedSymbol(FA_SYMBOL_PLUG, LV_ALIGN_OUT_TOP_RIGHT, (ST75256_DISPLAY_WIDTH - (1 * 14)), -1); // Rightmost - v_led_charge = new lvgl::WidgetLedSymbol(FA_SYMBOL_CHARGE, LV_ALIGN_OUT_TOP_RIGHT, (ST75256_DISPLAY_WIDTH - (2 * 14) - TOP_STATUS_BAR_GAP_PX), -1); - v_led_gps = new lvgl::WidgetLedSymbol(FA_SYMBOL_GPS1, LV_ALIGN_OUT_TOP_RIGHT, (ST75256_DISPLAY_WIDTH - (3 * 14) - (2 * TOP_STATUS_BAR_GAP_PX)), -1); - v_led_bat = new lvgl::WidgetLedSymbol(FA_SYMBOL_BATTERY, LV_ALIGN_OUT_TOP_RIGHT, (ST75256_DISPLAY_WIDTH - (4 * 14) - (3 * TOP_STATUS_BAR_GAP_PX)), -1); - - // GPS & Battery bars - bar_gps = new lvgl::WidgetBar(FA_SYMBOL_GPS2 " %d %%", LV_ALIGN_TOP_LEFT, 0, 19, (ST75256_DISPLAY_WIDTH / 2) - 1, 21); - bar_bat = new lvgl::WidgetBar(FA_SYMBOL_BATTERY " %d %%", LV_ALIGN_TOP_RIGHT, 0, 19, (ST75256_DISPLAY_WIDTH / 2) - 1, 21); - - // Mower status text (ticker) - text_ticker_status = new lvgl::WidgetTextTicker(LV_ALIGN_TOP_MID, 0, 42, ST75256_DISPLAY_WIDTH, 40); - - // Set defined state - set_undocked(); - bar_bat->set_value(BATT_ABS_MIN); - - main_screen_active = true; - } - - void DisplayRMEC3V11::anim_x_cb(void *var, int32_t v) - { - lv_obj_set_x((lv_obj_t *)var, v); - } - - void DisplayRMEC3V11::openmower_anim_delay() - { - delay(1000); - mainScreen(); - } - - // Probably static void - void DisplayRMEC3V11::openmower_anim() - { - const uint8_t wm_start_offset = 20; - main_screen_active = false; - - lv_obj_clean(lv_scr_act()); - - // Mower Logo - img_logo - lv_obj_t *img_logo = lv_img_create(lv_scr_act()); - lv_img_set_src(img_logo, &OM_Logo_120x54x1); - lv_obj_align(img_logo, LV_ALIGN_RIGHT_MID, 0, 0); - - // OpenMower Wordmark - img_wordmark - lv_obj_t *img_wordmark = lv_img_create(lv_scr_act()); - lv_img_set_src(img_wordmark, &OM_Wordmark_240x35x1); - lv_obj_align(img_wordmark, LV_ALIGN_RIGHT_MID, OM_Wordmark_240x35x1.header.w + wm_start_offset, 0); - - // Anim of logo - lv_anim_t al; - lv_anim_init(&al); - lv_anim_set_var(&al, img_logo); - lv_anim_set_values(&al, 0, -ST75256_DISPLAY_WIDTH); - lv_anim_set_time(&al, 1800); - lv_anim_set_delay(&al, 1200); - lv_anim_set_exec_cb(&al, (lv_anim_exec_xcb_t)anim_x_cb); - lv_anim_set_path_cb(&al, lv_anim_path_ease_in); - // lv_anim_set_repeat_count(&al, LV_ANIM_REPEAT_INFINITE); - lv_anim_start(&al); - - // Anim of Wordmark - lv_anim_t aw; - lv_anim_init(&aw); - lv_anim_set_var(&aw, img_wordmark); - lv_anim_set_values(&aw, OM_Wordmark_240x35x1.header.w + wm_start_offset, -((ST75256_DISPLAY_WIDTH - OM_Wordmark_240x35x1.header.w) / 2)); - lv_anim_set_time(&aw, 1800); - lv_anim_set_delay(&aw, 1900); - lv_anim_set_exec_cb(&aw, (lv_anim_exec_xcb_t)anim_x_cb); - lv_anim_set_path_cb(&aw, lv_anim_path_ease_in_out); - lv_anim_set_deleted_cb(&aw, (lv_anim_deleted_cb_t)openmower_anim_delay); // Set a callback to indicate when the animation is deleted (idle) - // lv_anim_set_repeat_count(&aw, LV_ANIM_REPEAT_INFINITE); - lv_anim_start(&aw); - } - - void DisplayRMEC3V11::loop_low_prio() - { - Display::loop_low_prio(); - - // LEDs & Buttons to main status screen conversion - if (!main_screen_active) - return; // Still in OM anim - - char status_ticker[STATUS_TICKER_LENGTH] = ""; - - // GPS - if (subscription::recv_hl_state.gps_quality < 25) - v_led_gps->set(LED_on); - else if (subscription::recv_hl_state.gps_quality < 50) - v_led_gps->set(LED_blink_fast); - else if (subscription::recv_hl_state.gps_quality < 75) - v_led_gps->set(LED_blink_slow); - else - v_led_gps->set(LED_off); - bar_gps->set_value(subscription::recv_hl_state.gps_quality); - - // V-Battery vLED - if (subscription::recv_ll_status.v_battery >= (BATT_EMPTY + 2.0f)) - v_led_bat->set(LED_off); - else - v_led_bat->set(LED_on); - - // Docked (Plug) & Charging (charge-station) - if (subscription::recv_ll_status.v_charge > 20.0f) // Docked - { - if (!last_docked_state_) - { - v_led_power->set(LED_on); - bar_bat->set_range(100, 1100); - bar_bat->bar_label = FA_SYMBOL_CHARGE " %d mA"; - set_backlight(); - last_docked_state_ = true; - } - bar_bat->set_value(subscription::recv_ll_status.charging_current * 1000); - - if (subscription::recv_ll_status.charging_current < 0.15f) - v_led_charge->set(LED_off); - else if (subscription::recv_ll_status.charging_current >= 0.15f && subscription::recv_ll_status.charging_current <= 0.8f) - v_led_charge->set(LED_blink_slow); - else if (subscription::recv_ll_status.charging_current > 0.8f) - v_led_charge->set(LED_blink_fast); - } - else // Undocked - { - if (last_docked_state_) - { - set_undocked(); - } - bar_bat->set_value(subscription::recv_ll_status.v_battery); - } - - // HL Mode & SubMode - switch (subscription::recv_hl_state.current_mode & 0b111111) - { - case HighLevelMode::MODE_IDLE: - v_led_ros->set(LED_on); - strncpy(status_ticker, "Idle", STATUS_TICKER_LENGTH); - break; - case HighLevelMode::MODE_AUTONOMOUS: - v_led_ros->set(LED_blink_slow); - if ((subscription::recv_hl_state.current_mode >> 6) & 0b01) - strncpy(status_ticker, "Docking", STATUS_TICKER_LENGTH); - else if ((subscription::recv_hl_state.current_mode >> 6) & 0b10) - strncpy(status_ticker, "Undocking", STATUS_TICKER_LENGTH); - else - strncpy(status_ticker, "Mowing", STATUS_TICKER_LENGTH); - break; - case HighLevelMode::MODE_RECORDING: - v_led_ros->set(LED_blink_fast); - if ((subscription::recv_hl_state.current_mode >> 6) & 0b01) - strncpy(status_ticker, "Record area outline", STATUS_TICKER_LENGTH); - else if ((subscription::recv_hl_state.current_mode >> 6) & 0b10) - strncpy(status_ticker, "Record obstacle", STATUS_TICKER_LENGTH); - break; - default: // We currently don't have a real "ROS Running" identifier. Let's use the current mode. - v_led_ros->set(LED_off); - strncpy(status_ticker, "Waiting for ROS...", STATUS_TICKER_LENGTH); - break; - } - - // ----- Most important text-states, last! ----- - - // Emergencies - static bool last_stop_button = false; - if (subscription::recv_ll_status.emergency_bitmask & LL_EMERGENCY_BITS_STOP) // Stop switch - { - v_led_emergency_stop->set(LED_blink_fast); - strncpy(status_ticker, EMERGENCY_CLEAR_TEXT, STATUS_TICKER_LENGTH); - - if (!last_stop_button) // Backlight on cover- open - set_backlight(); - last_stop_button = true; - } - else - { - v_led_emergency_stop->set(LED_off); - last_stop_button = false; - } - - if (subscription::recv_ll_status.emergency_bitmask & LL_EMERGENCY_BITS_LIFT) // Lifted or tilted - { - v_led_emergency_wheel->set(LED_blink_fast); - strncpy(status_ticker, EMERGENCY_CLEAR_TEXT, STATUS_TICKER_LENGTH); - } - else - v_led_emergency_wheel->set(LED_off); - - if (subscription::recv_ll_status.emergency_bitmask & LL_EMERGENCY_BIT_LATCH) // Emergency latch (no LL heartbeat or emergency by ROS) - { - v_led_emergency->set(LED_blink_slow); - strncpy(status_ticker, EMERGENCY_CLEAR_TEXT, STATUS_TICKER_LENGTH); - } - else - v_led_emergency->set(LED_off); - - // ----- Announcement ----- - switch (auto ann = get_anncmnt()) - { - case AnncmntType::close_hatch: - sprintf(status_ticker, "Close hatch in %i sec.", ((anncmnt.timeout - millis()) / 1000) + 1); - break; - case AnncmntType::version: - sprintf(status_ticker, "Version %i", FIRMWARE_VERSION); - break; - default: - break; - } - - text_ticker_status->set_text(status_ticker); - } - } // namespace display -} // namespace yardforce - -yardforce::display::DisplayRMEC3V11 display(yardforce::display::Display::Config{.backlight_led_num = LED_NUM_BACKLIGHT}); diff --git a/Firmware/CoverUI/YardForce/include/model/Display_RM-EC3-V11.hpp b/Firmware/CoverUI/YardForce/include/model/Display_RM-EC3-V11.hpp deleted file mode 100644 index 86d0903..0000000 --- a/Firmware/CoverUI/YardForce/include/model/Display_RM-EC3-V11.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @file Display_RM-EC3-V11.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI JLX25664 display driver for NX100i OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-27 - * - * @copyright Copyright (c) 2023 - */ -#ifndef YARDFORCE_DISPLAY_RMEC3V11_HPP -#define YARDFORCE_DISPLAY_RMEC3V11_HPP - -#define ST75256_DISPLAY_WIDTH 256 -#define ST75256_DISPLAY_HEIGHT 64 - -// JLX25664G Tscyc min = 80ns = 12.5MHz -// ST32F401 = 84MHz / 8 = 10.5MHz -#define ST75256_SPI_BAUDRATEPRESCALER SPI_BAUDRATEPRESCALER_8 - -#define LVGL_BUFFER_MULTIPLIER 10 -#define LVGL_TIMER_HANDLER_PERIOD_MS 10 // 10ms lv_timer_handler() soft period - -#define TOP_STATUS_BAR_GAP_PX 5 // Amount of (gap) pixels between top status-bar icons/symbols -#define EMERGENCY_CLEAR_TEXT "Emergency! Press [OK], close hatch and stay back, to clear emergency state." - -#include "../Display.hpp" -#include "../ST75256.hpp" -#include -#include "../LEDcontrol.hpp" -#include "../WidgetLedSymbol.hpp" -#include "../WidgetBar.hpp" -#include "../WidgetTextTicker.hpp" -#include "../subscription.h" - -#define BACKLIGHT_TIMEOUT_MS 120000 // 2 minutes -#define STATUS_TICKER_LENGTH 100 // TODO - -namespace yardforce -{ - namespace display - { - class DisplayRMEC3V11 : public Display - { - public: - DisplayRMEC3V11(Config t_config) : Display(t_config){}; - - bool init() override; // Init GPIOs, comms, as well as LVGL & display - - void loop_low_prio() override; // Low priority loop for display changes. Also does lv_timer_handler() and subscription - - void openmower_anim(); - static void openmower_anim_delay(); - static void mainScreen(); - - protected: - lv_disp_drv_t lv_disp_drv; // LVGL driver - - static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area); - static void flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *t_color_p); - - static void anim_x_cb(void *var, int32_t v); - - static void set_undocked(); - }; - } // namespace display -} // namespace yardforce - -extern yardforce::display::DisplayRMEC3V11 display; - -#endif // YARDFORCE_DISPLAY_RMEC3V11_HPP \ No newline at end of file diff --git a/Firmware/CoverUI/YardForce/include/model/Display_SAxPRO.cpp b/Firmware/CoverUI/YardForce/include/model/Display_SAxPRO.cpp deleted file mode 100644 index f1b3f4a..0000000 --- a/Firmware/CoverUI/YardForce/include/model/Display_SAxPRO.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/** - * @file Display_SAxPRO.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI WYM240128K1 display driver class for SAxPRO OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.3 - * @date 2023-11-29 - * - * @copyright Copyright (c) 2023 - */ -#include "Display_SAxPRO.hpp" -#include -#include "../subscription.h" -#include "../main.h" - -// C images -LV_IMG_DECLARE(OM_Logo_120x54x1); -LV_IMG_DECLARE(OM_Wordmark_240x35x1); - -namespace yardforce -{ - namespace display - { - static controller::UC1698 uc1698; // Display controller - - // LVGL buffers - static lv_disp_draw_buf_t lv_disp_buf; - static lv_color_t lv_buf_1[UC1698_DISPLAY_WIDTH * LVGL_BUFFER_MULTIPLIER]; - static lv_color_t lv_buf_2[UC1698_DISPLAY_WIDTH * LVGL_BUFFER_MULTIPLIER]; - - // Status Screen Widgets - lvgl::WidgetLedSymbol *v_led_heartbeat, *v_led_ros, - *v_led_emergency_wheel, *v_led_emergency, *v_led_emergency_stop, - *v_led_bat, *v_led_gps, *v_led_charge, *v_led_power; - lvgl::WidgetBar *bar_gps, *bar_bat; - lvgl::WidgetTextTicker *text_ticker_status; - - bool main_screen_active = false; // Initialized and active - bool last_docked_state_; // true = docked, false = undocked - - /** - * @brief Rounder callback will round the display area to a multiple of 3, on x axis (RGB control lines of a pixel are connected to 3 monochrome pixels) - * - * @param disp_drv - * @param area - */ - void DisplaySAXPRO::rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) - { - area->x1 = area->x1 - (area->x1 % 3); // Round down to neares multiple of 3 - area->x2 = (area->x2 + 3) - (area->x2 % 3) - 1; // Round up to nearest multiple of 3, minus -1 - } - - /** - * @brief Flush display buffer to display controller. - * Done via uc1698::drawPixelTriplet() method, which doesn't look as efficient like direct data write, - * but save the call to a further pixel-color-callback, as well as another buffer, which sounds more expensive. - * - * @param disp_drv - * @param area - * @param t_color_p - */ - void DisplaySAXPRO::flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *t_color_p) - { -#ifdef BENCHMARK - cycle_cnt_flush_cb_.start(); -#endif - size_t cols16b = ((area->x2 - area->x1) + 1) / 3; // Num of 16 bit columns for this flush area - size_t cols8b = cols16b * 2; - uint8_t row_buffer[cols8b]; // Buffer for one row 2*8-bit (every 16 bit col holds 3 pixel) - - uc1698.setWindowProgramArea(area->x1, area->x2, area->y1, area->y2); - - for (size_t y = area->y1; y <= area->y2; y++) - { - for (size_t x = 0; x < cols8b; x += 2) // ATTENTION: Will only work with a correct rounder_cb()! - { - // Color is inverted (0 = black but pixel off / >0 = white but pixel on) but UC1698 "[16] Set Inverse Display" is set - row_buffer[x] = (0b11111000 * t_color_p->full | (0b00000111 * (t_color_p + 1)->full)); - row_buffer[x + 1] = (0b11100000 * (t_color_p + 1)->full) | (0b00011111 * (t_color_p + 2)->full); - t_color_p += 3; - } - uc1698.writeData(row_buffer, cols8b); - } - lv_disp_flush_ready(disp_drv); -#ifdef BENCHMARK - cycle_cnt_flush_cb_.stop(); -#endif - } - - void DisplaySAXPRO::set_undocked() - { - v_led_power->set(LED_off); - v_led_charge->set(LED_off); - bar_bat->set_range(BATT_ABS_MIN, BATT_ABS_MAX); - bar_bat->bar_label = FA_SYMBOL_BATTERY " %d V"; - last_docked_state_ = false; - } - - void DisplaySAXPRO::mainScreen() - { -#ifdef BENCHMARK - volatile auto perf_test = cycle_cnt_flush_cb_; -#endif - // On the left side of the status bar we do have functional status symbols like heartbeat and ROS - v_led_ros = new lvgl::WidgetLedSymbol(FA_SYMBOL_ROS, LV_ALIGN_TOP_LEFT, 0, 0); // Leftmost - - // In the middle, we do have emergencies - v_led_emergency = new lvgl::WidgetLedSymbol(FA_SYMBOL_EMERGENCY, LV_ALIGN_TOP_MID, 0, 0); // Centered - v_led_emergency_wheel = new lvgl::WidgetLedSymbol(FA_SYMBOL_EMERGENCY_WHEEL, LV_ALIGN_TOP_MID, -14 - TOP_STATUS_BAR_GAP_PX - 2, 0); // Left of centered - // TODO: if next level LL proto - // v_led_heartbeat = new WidgetLedSymbol(FA_SYMBOL_HEARTBEAT, LV_ALIGN_TOP_MID, -(2 * 14) - (2 * TOP_STATUS_BAR_GAP_PX) - 2, 0); // 2nd left of centered - v_led_emergency_stop = new lvgl::WidgetLedSymbol(FA_SYMBOL_EMERGENCY_STOP, LV_ALIGN_TOP_MID, 14 + TOP_STATUS_BAR_GAP_PX, 0); // Right of centered - - // On the right side, mowing status like, charging, docking, ... - v_led_power = new lvgl::WidgetLedSymbol(FA_SYMBOL_PLUG, LV_ALIGN_OUT_TOP_RIGHT, (240 - (1 * 14)), 0); // Rightmost - v_led_charge = new lvgl::WidgetLedSymbol(FA_SYMBOL_CHARGE, LV_ALIGN_OUT_TOP_RIGHT, (240 - (2 * 14) - TOP_STATUS_BAR_GAP_PX), 0); - v_led_gps = new lvgl::WidgetLedSymbol(FA_SYMBOL_GPS1, LV_ALIGN_OUT_TOP_RIGHT, (240 - (3 * 14) - (2 * TOP_STATUS_BAR_GAP_PX)), 0); - v_led_bat = new lvgl::WidgetLedSymbol(FA_SYMBOL_BATTERY, LV_ALIGN_OUT_TOP_RIGHT, (240 - (4 * 14) - (3 * TOP_STATUS_BAR_GAP_PX)), 0); - - // GPS & Battery bars - bar_gps = new lvgl::WidgetBar(FA_SYMBOL_GPS2 " %d %%", LV_ALIGN_TOP_MID, 0, 30, UC1698_DISPLAY_WIDTH, 21); - bar_bat = new lvgl::WidgetBar(FA_SYMBOL_BATTERY " %d %%", LV_ALIGN_TOP_MID, 0, 60, UC1698_DISPLAY_WIDTH, 21); - - // Mower status text (ticker) - text_ticker_status = new lvgl::WidgetTextTicker(LV_ALIGN_TOP_MID, 0, 95, UC1698_DISPLAY_WIDTH); - - // Set defined state - set_undocked(); - bar_bat->set_value(BATT_ABS_MIN); - - main_screen_active = true; - } - - void DisplaySAXPRO::anim_x_cb(void *var, int32_t v) - { - lv_obj_set_x((lv_obj_t *)var, v); - } - - void DisplaySAXPRO::openmower_anim() - { - main_screen_active = false; - - // Mower Logo - img_logo - lv_obj_t *img_logo = lv_img_create(lv_scr_act()); - lv_img_set_src(img_logo, &OM_Logo_120x54x1); - lv_obj_align(img_logo, LV_ALIGN_CENTER, 0, -25); - - // OpenMower Wordmark - img_wordmark - lv_obj_t *img_wordmark = lv_img_create(lv_scr_act()); - lv_img_set_src(img_wordmark, &OM_Wordmark_240x35x1); - lv_obj_align(img_wordmark, LV_ALIGN_CENTER, 0, 25); - - // Anim of logo - lv_anim_t al; - lv_anim_init(&al); - lv_anim_set_var(&al, img_logo); - lv_anim_set_values(&al, 0, -((UC1698_DISPLAY_WIDTH / 2) + (OM_Logo_120x54x1.header.w / 2))); - lv_anim_set_time(&al, 1500); - lv_anim_set_delay(&al, 1000); - lv_anim_set_exec_cb(&al, (lv_anim_exec_xcb_t)anim_x_cb); - lv_anim_set_path_cb(&al, lv_anim_path_ease_in); - lv_anim_start(&al); - - // Anim of Wordmark - lv_anim_t aw; - lv_anim_init(&aw); - lv_anim_set_var(&aw, img_wordmark); - lv_anim_set_values(&aw, 0, (UC1698_DISPLAY_WIDTH / 2) + (OM_Wordmark_240x35x1.header.w / 2) + 20); - lv_anim_set_time(&aw, 1400); - lv_anim_set_delay(&aw, 1500); - lv_anim_set_exec_cb(&aw, (lv_anim_exec_xcb_t)anim_x_cb); - lv_anim_set_path_cb(&aw, lv_anim_path_ease_in); - lv_anim_set_deleted_cb(&aw, (lv_anim_ready_cb_t)mainScreen); // Set a callback to indicate when the animation is deleted (idle) - lv_anim_start(&aw); - } - - bool DisplaySAXPRO::init() - { - // Init UC1698 display controller - if (!uc1698.init()) - { - return false; - } - - // Init LVGL - lv_init(); - lv_disp_drv_init(&lv_disp_drv); // Basic LVGL display driver initialization - lv_disp_drv.draw_buf = &lv_disp_buf; // Set an initialized buffer - lv_disp_drv.rounder_cb = rounder_cb; // Round x coordinated so that it fit for our 3 RGB pixel/per dot display - lv_disp_drv.flush_cb = flush_cb; // Set a flush callback to draw to the display - lv_disp_drv.hor_res = UC1698_DISPLAY_WIDTH; // Set the horizontal resolution in pixels - lv_disp_drv.ver_res = UC1698_DISPLAY_HEIGHT; // Set the vertical resolution in pixels - lv_disp_draw_buf_init(&lv_disp_buf, lv_buf_1, lv_buf_2, UC1698_DISPLAY_WIDTH * LVGL_BUFFER_MULTIPLIER); // Initialize `disp_buf` with the buffer(s) - lv_disp_t *disp; - disp = lv_disp_drv_register(&lv_disp_drv); // Register the driver and save the created display objects - lv_obj_set_style_bg_color(lv_scr_act(), lv_color_white(), LV_PART_MAIN); // No background color - - openmower_anim(); - // mainScreen(); - - return true; - } - - /** - * @brief Regular loop() function, which get called by a low priority hardware timer (approx. 10ms). - * Handles i.e. LVGL timers or LED-2-Display logic. - * Has to be a lower priority routine than tick_inc(), otherwise all LVGL timers (or LEDcontrol-Animations) do not work - */ - void DisplaySAXPRO::loop_low_prio() - { - Display::loop_low_prio(); - - // LEDs & Buttons to main status screen conversion - if (!main_screen_active) - return; // Still in OM anim - - char status_ticker[STATUS_TICKER_LENGTH] = ""; - - // GPS - if (subscription::recv_hl_state.gps_quality < 25) - v_led_gps->set(LED_on); - else if (subscription::recv_hl_state.gps_quality < 50) - v_led_gps->set(LED_blink_fast); - else if (subscription::recv_hl_state.gps_quality < 75) - v_led_gps->set(LED_blink_slow); - else - v_led_gps->set(LED_off); - bar_gps->set_value(subscription::recv_hl_state.gps_quality); - - // V-Battery vLED - if (subscription::recv_ll_status.v_battery >= (BATT_EMPTY + 2.0f)) - v_led_bat->set(LED_off); - else - v_led_bat->set(LED_on); - - // Docked (Plug) & Charging (charge-station) - if (subscription::recv_ll_status.v_charge > 20.0f) // Docked - { - if (!last_docked_state_) - { - v_led_power->set(LED_on); - bar_bat->set_range(100, 1100); - bar_bat->bar_label = FA_SYMBOL_CHARGE " %d mA"; - set_backlight(); - last_docked_state_ = true; - } - bar_bat->set_value(subscription::recv_ll_status.charging_current * 1000); - - if (subscription::recv_ll_status.charging_current < 0.15f) - v_led_charge->set(LED_off); - else if (subscription::recv_ll_status.charging_current >= 0.15f && subscription::recv_ll_status.charging_current <= 0.8f) - v_led_charge->set(LED_blink_slow); - else if (subscription::recv_ll_status.charging_current > 0.8f) - v_led_charge->set(LED_blink_fast); - } - else // Undocked - { - if (last_docked_state_) - { - set_undocked(); - } - bar_bat->set_value(subscription::recv_ll_status.v_battery); - } - - // HL Mode & SubMode - switch (subscription::recv_hl_state.current_mode & 0b111111) - { - case HighLevelMode::MODE_IDLE: - v_led_ros->set(LED_on); - strncpy(status_ticker, "Idle", STATUS_TICKER_LENGTH); - break; - case HighLevelMode::MODE_AUTONOMOUS: - v_led_ros->set(LED_blink_slow); - if ((subscription::recv_hl_state.current_mode >> 6) & 0b01) - strncpy(status_ticker, "Docking", STATUS_TICKER_LENGTH); - else if ((subscription::recv_hl_state.current_mode >> 6) & 0b10) - strncpy(status_ticker, "Undocking", STATUS_TICKER_LENGTH); - else - strncpy(status_ticker, "Mowing", STATUS_TICKER_LENGTH); - break; - case HighLevelMode::MODE_RECORDING: - v_led_ros->set(LED_blink_fast); - if ((subscription::recv_hl_state.current_mode >> 6) & 0b01) - strncpy(status_ticker, "Record area outline", STATUS_TICKER_LENGTH); - else if ((subscription::recv_hl_state.current_mode >> 6) & 0b10) - strncpy(status_ticker, "Record obstacle", STATUS_TICKER_LENGTH); - break; - default: // We currently don't have a real "ROS Running" identifier. Let's use the current mode. - v_led_ros->set(LED_off); - strncpy(status_ticker, "Waiting for ROS...", STATUS_TICKER_LENGTH); - break; - } - - // ----- Most important text-states, last! ----- - - // Emergencies - static bool last_stop_button = false; - if (subscription::recv_ll_status.emergency_bitmask & LL_EMERGENCY_BITS_STOP) // Stop switch - { - v_led_emergency_stop->set(LED_blink_fast); - strncpy(status_ticker, EMERGENCY_CLEAR_TEXT, STATUS_TICKER_LENGTH); - - if (!last_stop_button) // Backlight on cover- open - set_backlight(); - last_stop_button = true; - } - else - { - v_led_emergency_stop->set(LED_off); - last_stop_button = false; - } - - if (subscription::recv_ll_status.emergency_bitmask & LL_EMERGENCY_BITS_LIFT) // Lifted or tilted - { - v_led_emergency_wheel->set(LED_blink_fast); - strncpy(status_ticker, EMERGENCY_CLEAR_TEXT, STATUS_TICKER_LENGTH); - } - else - v_led_emergency_wheel->set(LED_off); - - if (subscription::recv_ll_status.emergency_bitmask & LL_EMERGENCY_BIT_LATCH) // Emergency latch (no LL heartbeat or emergency by ROS) - { - v_led_emergency->set(LED_blink_slow); - strncpy(status_ticker, EMERGENCY_CLEAR_TEXT, STATUS_TICKER_LENGTH); - } - else - v_led_emergency->set(LED_off); - - // ----- Announcement ----- - switch (auto ann = get_anncmnt()) - { - case AnncmntType::close_hatch: - sprintf(status_ticker, "Close hatch in %i sec.", ((anncmnt.timeout - millis()) / 1000) + 1); - break; - case AnncmntType::version: - sprintf(status_ticker, "Version %i", FIRMWARE_VERSION); - break; - default: - break; - } - - text_ticker_status->set_text(status_ticker); - } - - } // namespace display -} // namespace yardforce - -yardforce::display::DisplaySAXPRO display(yardforce::display::Display::Config{.backlight_led_num = LED_NUM_BACKLIGHT}); diff --git a/Firmware/CoverUI/YardForce/include/model/Display_SAxPRO.hpp b/Firmware/CoverUI/YardForce/include/model/Display_SAxPRO.hpp deleted file mode 100644 index 5f61ede..0000000 --- a/Firmware/CoverUI/YardForce/include/model/Display_SAxPRO.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @file Display_SAxPRO.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce CoverUI WYM240128K1 display driver for SAxPRO OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.3 - * @date 2023-11-29 - * - * @copyright Copyright (c) 2023 - */ -#ifndef YARDFORCE_DISPLAY_SAXPRO_HPP -#define YARDFORCE_DISPLAY_SAXPRO_HPP - -#define UC1698_DISPLAY_WIDTH 240 -#define UC1698_DISPLAY_HEIGHT 128 - -#define LVGL_BUFFER_MULTIPLIER 10 -#define LVGL_TIMER_HANDLER_PERIOD_MS 10 // 10ms lv_timer_handler() soft period - -#define TOP_STATUS_BAR_GAP_PX 5 // Amount of (gap) pixels between top status-bar icons/symbols -#define EMERGENCY_CLEAR_TEXT "Emergency! Press [Enter], close hatch and stay back, to clear emergency state." - -#include "../Display.hpp" -#include "../UC1698.hpp" -#include -#include "../LEDcontrol.hpp" -#include "../WidgetLedSymbol.hpp" -#include "../WidgetBar.hpp" -#include "../WidgetTextTicker.hpp" -#include "../subscription.h" - -#define BACKLIGHT_TIMEOUT_MS 120000 // 2 minutes -#define STATUS_TICKER_LENGTH 100 - -// Enable for benchmarking specific code -// #define BENCHMARK - -#ifdef BENCHMARK -#include "include/CortexMCycleCounter.hpp" -#endif - -namespace yardforce -{ - namespace display - { - class DisplaySAXPRO : public Display - { - public: - DisplaySAXPRO(Config t_config) : Display(t_config){}; - - bool init() override; // Init GPIOs, comms, as well as LVGL & display - - void loop_low_prio() override; // Low priority loop for display changes. Also does lv_timer_handler() and subscription - - void openmower_anim(); - static void mainScreen(); - - protected: - lv_disp_drv_t lv_disp_drv; // LVGL driver - - static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area); - static void flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *t_color_p); - - static void anim_x_cb(void *var, int32_t v); - - static void set_undocked(); - }; - -#ifdef BENCHMARK - static CortexMCycleCounter cycle_cnt_flush_cb_; -#endif - - } // namespace display -} // namespace yardforce - -extern yardforce::display::DisplaySAXPRO display; - -#endif // YARDFORCE_DISPLAY_SAXPRO_HPP \ No newline at end of file diff --git a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_C500.cpp b/Firmware/CoverUI/YardForce/include/model/LEDcontrol_C500.cpp deleted file mode 100644 index 52828d0..0000000 --- a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_C500.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/** - * @file LEDcontrol_C500.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce Classic 500 LED driver class for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.2 - * @date 2023-11-06 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "LEDcontrol_C500.hpp" - -const uint32_t kPinByLedNum[] = { - // Order derived from LowLevel "enum LED_id" - // C500 - // Use LED num Original Alternative - LED_PIN_CHARGE, // 0 - LED_PIN_BAT, // 1 - LED_PIN_WIRE, // 2 - LED_PIN_LIFTED, // 3 - LED_PIN_SUN, // 4 SUN(6) - LED_PIN_SAT, // 5 SAT(5) - LED_PIN_FRI, // 6 FRI(4) - LED_PIN_THU, // 7 THU(3) - LED_PIN_WED, // 8 WED(2) - LED_PIN_TUE, // 9 TUE(1) - LED_PIN_MON, // 10 MON(0) - LED_PIN_LOCK, // 11 - LED_PIN_S2, // 12 digit 5 - LED_PIN_S1, // 13 digit 4 - LED_PIN_8HR, // 14 8HR digit 3 - LED_PIN_6HR, // 15 6HR digit 2 - LED_PIN_4HR, // 16 4HR digit 1 - LED_PIN_2HR, // 17 2HR digit 0 - LED_PIN_REAR // 18 -}; - -// Numeric (base10) representation of LEDs. -// I.e. Digit 0 = 4H(0), ... Then S2(5) + S1(4) = 9. -// -// |------- Bit 6 = MON(0) -// ||------ Bit 5 = TUE(1) -// |||----- Bit 4 = WED(2) -// ||||---- Bit 3 = THU(3) -// |||||--- Bit 2 = FRI(4) -// ||||||-- Bit 1 = SAT(5) -// |||||||- Bit 0 = SUN(6) -const uint8_t kBase10Leds[] = { - 0b1000000, // = 0 - 0b0100000, - 0b0010000, - 0b0001000, - 0b0000100, - 0b0000010, - 0b0000001, - 0b0100001, - 0b0010001, - 0b0001001}; // = 9 - - /** - * @brief Animate sequence handler. Has to be started by sequence_start() - */ - void LEDcontrolC500::sequence_animate_handler() - { - unsigned int step = seq_get_next_step_(LED_ANIMATE_DELAY); // Animation sequence runs in 15ms steps - - if (step == 0) // Next sequence step not reached now - return; - else if (step >= 1 && step <= kNumLeds) // LED on - { - set(kNumLeds - step, LED_state::LED_on, false); - return; - } - else if (step >= (kNumLeds + 1) && step <= (2 * kNumLeds)) // LED off - { - set((2 * kNumLeds) - step, LED_state::LED_off, false); - return; - } - else - { - seq_start_tick_ = 0; // Sequence end - set(led_states_bin_); // Restore states - return; - } - } - - /** - * @brief A quick boot/power-on animation, also used as jammed button indicator - * - * @return unsigned int ms how long it will take to play - */ - unsigned int LEDcontrolC500::boot_animation() // A short boot animation which return the amount of ms it will take - { - sequence_start(&LEDcontrol::sequence_animate_handler); - return ((kNumLeds + 1) * LED_ANIMATE_DELAY); - } - - /** - * @brief Set base10 related LEDs for the given (numeric) character - * - * @param digit numeric character - */ - void LEDcontrolC500::set_base10_leds(char digit) - { - for (uint8_t bit = 0; bit <= 6; bit++) // We've 6 LEDs for base10 number representation - { - bool on = (kBase10Leds[digit - '0'] >> bit) & 0b1; - unsigned int led = bit + 12; - set(bit + 4, on ? LED_state::LED_on : LED_state::LED_off, false); - } - } - - void LEDcontrolC500::force_off_num_seq_leds(bool force) - { - force_off(LED_NUM_LIFTED, force); // Num change signalling LED - for (unsigned int i = 4; i <= 10; i++) // Base10 related LEDs - force_off(i, force); - } - -LEDcontrolC500 leds(kPinByLedNum, sizeof(kPinByLedNum) / sizeof(uint32_t), &LEDcontrol::set_base10_leds); // Main LED controller object diff --git a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_C500.hpp b/Firmware/CoverUI/YardForce/include/model/LEDcontrol_C500.hpp deleted file mode 100644 index d7c25a4..0000000 --- a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_C500.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @file LEDcontrol_C500.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce Classic 500 LED driver header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.2 - * @date 2023-11-06 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_LEDCONTROL_C500_HPP -#define YARDFORCE_LEDCONTROL_C500_HPP - -#include -#include "../LEDcontrol.hpp" - -// 1st row: 2,4,6,8HR -#define LED_PIN_2HR PA4 -#define LED_PIN_4HR PA5 -#define LED_PIN_6HR PA6 -#define LED_PIN_8HR PA7 - -// 2nd row: S1, S2, LOCK -#define LED_PIN_S1 PA0 -#define LED_PIN_S2 PA1 -#define LED_PIN_LOCK PC4 - -// 3rd row: Mon-Sun -#define LED_PIN_MON PA15 -#define LED_PIN_TUE PC10 -#define LED_PIN_WED PC11 -#define LED_PIN_THU PC12 -#define LED_PIN_FRI PD2 -#define LED_PIN_SAT PB3 -#define LED_PIN_SUN PB4 - -// 4th row: Lifted, Wire, Bat, Charge -#define LED_PIN_LIFTED PC0 -#define LED_PIN_WIRE PC1 -#define LED_PIN_BAT PC2 -#define LED_PIN_CHARGE PC3 - -// Backside -#define LED_PIN_REAR PB0 - -#define LED_ANIMATE_DELAY 15 // Delay (ms) between LEDs of animate sequence (boot/power-on anim) - -class LEDcontrolC500 : public LEDcontrol -{ -public: - LEDcontrolC500(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds, void (LEDcontrol::*t_set_base10_leds_callback)(char digit)) : LEDcontrol(t_kPinByLedNumPtr, t_kNumLeds, t_set_base10_leds_callback) {} - - void sequence_animate_handler() override; - - unsigned int boot_animation(); // A short boot animation which return the amount of ms it will take - void set_base10_leds(char digit) override; - void force_off_num_seq_leds(bool force) override; -}; - -extern LEDcontrolC500 leds; // Main LED controller object - -#endif // YARDFORCE_LEDCONTROL_C500_HPP diff --git a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-EC3-V11.cpp b/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-EC3-V11.cpp deleted file mode 100644 index 8a6ffa3..0000000 --- a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-EC3-V11.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/** - * @file LEDcontrol_RM-EC3-V11.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce LED driver for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-16 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "LEDcontrol_RM-EC3-V11.hpp" - -const uint32_t kPinByLedNum[] = { - // Order derived from LowLevel "enum LED_id" - // C500 - // Use LED num Original Alternative - LED_PIN_NC, // 0 - LED_PIN_NC, // 1 - LED_PIN_NC, // 2 - LED_PIN_NC, // 3 - LED_PIN_NC, // 4 SUN - LED_PIN_NC, // 5 SAT - LED_PIN_NC, // 6 FRI - LED_PIN_NC, // 7 THU - LED_PIN_NC, // 8 WED - LED_PIN_NC, // 9 TUE - LED_PIN_NC, // 10 MON - LED_PIN_NC, // 11 - LED_PIN_S2, // 12 digit 5 - LED_PIN_S1, // 13 digit 4 - LED_PIN_NC, // 14 8HR digit 3 - LED_PIN_NC, // 15 6HR digit 2 - LED_PIN_NC, // 16 4HR digit 1 - LED_PIN_NC, // 17 2HR digit 0 - LED_PIN_NC, // 18 - LED_PIN_SETUP, // 19 - LED_PIN_BACKLIGHT // 20 -}; - -const unsigned int kLedAnimOrder[] = {13, 19, 12}; -const unsigned int kNumFrontLeds = sizeof(kLedAnimOrder) / sizeof(unsigned int); - -// Numeric (base10) representation of LEDs. -// I.e. Digit 0 = 4H(0), ... Then S2(5) + S1(4) = 9. -// -// |------ Bit 5 = 4H(0) -// ||----- Bit 4 = 6H(1) -// |||---- Bit 3 = 8H(2) -// ||||--- Bit 2 = 10H(3) -// |||||-- Bit 1 = S1(4) -// ||||||- Bit 0 = S2(5) -/*const uint8_t kBase10Leds[] = { - 0b100000, // = 0 - 0b010000, - 0b001000, - 0b000100, - 0b000010, - 0b000001, - 0b010001, - 0b001001, - 0b000101, - 0b000011}; // = 9*/ - -void LEDcontrolRMEC3V11::sequence_animate_handler() -{ - uint16_t step = seq_get_next_step_(LED_ANIMATE_DELAY); // Animation sequence runs in 15ms steps - - if (step == 0) // Next sequence step not reached now - return; - else if (step >= 1 && step <= kNumFrontLeds) // LED on - { - set(kLedAnimOrder[step - 1], LED_state::LED_on, false); - return; - } - else if (step >= (kNumFrontLeds + 1) && step <= (2 * kNumFrontLeds)) // LED off - { - set(kLedAnimOrder[step - kNumFrontLeds - 1], LED_state::LED_off, false); - return; - } - else - { - seq_start_tick_ = 0; // Sequence end - set(led_states_bin_); // Restore states - return; - } -} - -void LEDcontrolRMEC3V11::show_countdown_state(unsigned int sec, LED_state state) -{ - unsigned int sec_left = 5 - sec; - - for (size_t i = 1; i <= 4; i++) - { - uint8_t led_num = 18 - i; - if (sec_left >= i) // Remaining secs is greater than this LED - { - leds.set(led_num, state, false); - } - else - { - leds.set(led_num, LED_off, false); - force_off(led_num, true); - } - } -}; - -/** - * @brief Countdown LED animation handler - */ -void LEDcontrolRMEC3V11::sequence_countdown_handler() -{ - uint16_t step = seq_get_next_step_(100); // Animation sequence run in 1000ms steps - - if (step == 0) // Next sequence step not reached now - return; - else if (step == 1 || step == 11 || step == 21 || step == 31 || step == 41) - { - show_countdown_state(((step - 1) / 10) + 1, LED_blink_fast); - return; - } - else if (step < 41) - { - return; - } - else - { - seq_start_tick_ = 0; // Sequence end - set(led_states_bin_); // Restore states - for (size_t i = 1; i <= 4; i++) - force_off(18 - i, false); - return; - } -} - -/** - * @brief A quick boot/power-on animation, also used as jammed button indicator - * - * @return unsigned int ms how long it will take to play - */ -unsigned int LEDcontrolRMEC3V11::boot_animation() // A short boot animation which return the amount of ms it will take -{ - sequence_start(&LEDcontrol::sequence_animate_handler); - return ((kNumFrontLeds + 1) * LED_ANIMATE_DELAY); -} - -/** - * @brief Set base10 related LEDs for the given (numeric) character - * - * @param digit numeric character - */ -/*void LEDcontrolRMEC3V11::set_base10_leds(char digit) -{ - for (uint8_t bit = 0; bit <= 5; bit++) // We've 5 LEDs for base10 number representation - { - bool on = (kBase10Leds[digit - '0'] >> bit) & 0b1; - unsigned int led = bit + 12; - set(led, on ? LED_state::LED_on : LED_state::LED_off, false); - } -} - -void LEDcontrolRMEC3V11::force_off_num_seq_leds(bool force) -{ - force_off(LED_NUM_LIFTED, force); // Num change signalling LED - for (unsigned int i = 12; i <= 17; i++) // Base10 related LEDs - force_off(i, force); -}*/ - -LEDcontrolRMEC3V11 leds(kPinByLedNum, sizeof(kPinByLedNum) / sizeof(uint32_t), &LEDcontrol::set_base10_leds); // Main LED controller object diff --git a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-EC3-V11.hpp b/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-EC3-V11.hpp deleted file mode 100644 index 8fd75c9..0000000 --- a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-EC3-V11.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @file LEDcontrol_RM-EC3-V11.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce LED driver header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-16 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_LEDCONTROL_RMEC3V11_HPP -#define YARDFORCE_LEDCONTROL_RMEC3V11_HPP - -#include -#include "../LEDcontrol.hpp" - -// 1st row: Lifted, Wire (WLAN), Battery empty, Charge -#define LED_PIN_S1 PC4 -#define LED_PIN_SETUP PA1 -#define LED_PIN_S2 PC5 -#define LED_PIN_BACKLIGHT PA8 - -#define LED_NUM_BACKLIGHT 20 - -#define LED_ANIMATE_DELAY 50 // Delay (ms) between LEDs of animate sequence (boot/power-on anim) - -class LEDcontrolRMEC3V11 : public LEDcontrol -{ -public: - LEDcontrolRMEC3V11(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds, void (LEDcontrol::*t_set_base10_leds_callback)(char digit)) : LEDcontrol(t_kPinByLedNumPtr, t_kNumLeds, t_set_base10_leds_callback){}; - - void sequence_animate_handler() override; - void sequence_countdown_handler() override; - - unsigned int boot_animation() override; // A short boot animation which return the amount of ms it will take - //void set_base10_leds(char digit) override; - //void force_off_num_seq_leds(bool force) override; - -private: - void show_countdown_state(unsigned int sec, LED_state state); -}; - -extern LEDcontrolRMEC3V11 leds; - -#endif // YARDFORCE_LEDCONTROL_RMEC3V11_HPP diff --git a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-ECOW-V100.cpp b/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-ECOW-V100.cpp deleted file mode 100644 index ae4c71a..0000000 --- a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-ECOW-V100.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @file LEDcontrol_RM-ECOW-V100.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce LED driver for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.2 - * @date 2023-11-05 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "LEDcontrol_RM-ECOW-V100.hpp" - -const uint32_t kPinByLedNum[] = { - // Order derived from LowLevel "enum LED_id" - // C500 - // Use LED num Original Alternative - LED_PIN_CHARGE, // 0 - LED_PIN_BAT, // 1 - LED_PIN_WIRE, // 2 - LED_PIN_LIFTED, // 3 - LED_PIN_NC, // 4 SUN - LED_PIN_NC, // 5 SAT - LED_PIN_NC, // 6 FRI - LED_PIN_NC, // 7 THU - LED_PIN_NC, // 8 WED - LED_PIN_NC, // 9 TUE - LED_PIN_NC, // 10 MON - LED_PIN_LOCK, // 11 - LED_PIN_S2, // 12 digit 5 - LED_PIN_S1, // 13 digit 4 - LED_PIN_10H, // 14 8HR digit 3 - LED_PIN_8H, // 15 6HR digit 2 - LED_PIN_6H, // 16 4HR digit 1 - LED_PIN_4H, // 17 2HR digit 0 - LED_PIN_REAR, // 18 - LED_PIN_SETUP // 19 -}; - -const unsigned int kLedAnimOrder[] = {3, 2, 1, 0, 17, 16, 15, 14, 13, 19, 12, 11}; -const unsigned int kNumFrontLeds = sizeof(kLedAnimOrder) / sizeof(unsigned int); - -// Numeric (base10) representation of LEDs. -// I.e. Digit 0 = 4H(0), ... Then S2(5) + S1(4) = 9. -// -// |------ Bit 5 = 4H(0) -// ||----- Bit 4 = 6H(1) -// |||---- Bit 3 = 8H(2) -// ||||--- Bit 2 = 10H(3) -// |||||-- Bit 1 = S1(4) -// ||||||- Bit 0 = S2(5) -const uint8_t kBase10Leds[] = { - 0b100000, // = 0 - 0b010000, - 0b001000, - 0b000100, - 0b000010, - 0b000001, - 0b010001, - 0b001001, - 0b000101, - 0b000011}; // = 9 - -void LEDcontrolRMECOWV100::sequence_animate_handler() -{ - uint16_t step = seq_get_next_step_(LED_ANIMATE_DELAY); // Animation sequence runs in 15ms steps - - if (step == 0) // Next sequence step not reached now - return; - else if (step >= 1 && step <= kNumFrontLeds) // LED on - { - set(kLedAnimOrder[step - 1], LED_state::LED_on, false); - return; - } - else if (step >= (kNumFrontLeds + 1) && step <= (2 * kNumFrontLeds)) // LED off - { - set(kLedAnimOrder[step - kNumFrontLeds - 1], LED_state::LED_off, false); - return; - } - else - { - seq_start_tick_ = 0; // Sequence end - set(led_states_bin_); // Restore states - return; - } -} - -void LEDcontrolRMECOWV100::show_countdown_state(unsigned int sec, LED_state state) -{ - unsigned int sec_left = 5 - sec; - - for (size_t i = 1; i <= 4; i++) - { - uint8_t led_num = 18 - i; - if (sec_left >= i) // Remaining secs is greater than this LED - { - leds.set(led_num, state, true); // change_state = true because blink states get handled by timer and this need to be stored - } - else - { - leds.set(led_num, LED_off, true); - force_off(led_num, true); - } - } -}; - -/** - * @brief Countdown LED animation handler - */ -void LEDcontrolRMECOWV100::sequence_countdown_handler() -{ - uint16_t step = seq_get_next_step_(100); // Animation sequence run in 1000ms steps - - if (step == 0) // Next sequence step not reached now - return; - else if (step == 1 || step == 11 || step == 21 || step == 31 || step == 41) - { - show_countdown_state(((step - 1) / 10) + 1, LED_blink_fast); - return; - } - else if (step < 41) - { - return; - } - else - { - seq_start_tick_ = 0; // Sequence end - set(led_states_bin_); // Restore states - for (size_t i = 1; i <= 4; i++) - force_off(18 - i, false); - return; - } -} - -/** - * @brief A quick boot/power-on animation, also used as jammed button indicator - * - * @return unsigned int ms how long it will take to play - */ -unsigned int LEDcontrolRMECOWV100::boot_animation() // A short boot animation which return the amount of ms it will take -{ - sequence_start(&LEDcontrol::sequence_animate_handler); - return ((kNumFrontLeds + 1) * LED_ANIMATE_DELAY); -} - -/** - * @brief Set base10 related LEDs for the given (numeric) character - * - * @param digit numeric character - */ -void LEDcontrolRMECOWV100::set_base10_leds(char digit) -{ - for (uint8_t bit = 0; bit <= 5; bit++) // We've 5 LEDs for base10 number representation - { - bool on = (kBase10Leds[digit - '0'] >> bit) & 0b1; - unsigned int led = bit + 12; - set(led, on ? LED_state::LED_on : LED_state::LED_off, false); - } -} - -void LEDcontrolRMECOWV100::force_off_num_seq_leds(bool force) -{ - force_off(LED_NUM_LIFTED, force); // Num change signalling LED - for (unsigned int i = 12; i <= 17; i++) // Base10 related LEDs - force_off(i, force); -} - -LEDcontrolRMECOWV100 leds(kPinByLedNum, sizeof(kPinByLedNum) / sizeof(uint32_t), &LEDcontrol::set_base10_leds); // Main LED controller object diff --git a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-ECOW-V100.hpp b/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-ECOW-V100.hpp deleted file mode 100644 index 76e1221..0000000 --- a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-ECOW-V100.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file LEDcontrol_RM-ECOW-V100.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce LED driver header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.2 - * @date 2023-11-05 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_LEDCONTROL_RMECOWV100_HPP -#define YARDFORCE_LEDCONTROL_RMECOWV100_HPP - -#include -#include "../LEDcontrol.hpp" - -// 1st row: Lifted, Wire (WLAN), Battery empty, Charge -#define LED_PIN_LIFTED PB11 -#define LED_PIN_WIRE PB12 -#define LED_PIN_BAT PB14 -#define LED_PIN_CHARGE PB15 - -// 2nd row: 4, 6, 8, 10HR -#define LED_PIN_4H PA7 -#define LED_PIN_6H PC5 -#define LED_PIN_8H PB1 -#define LED_PIN_10H PB10 - -// 3rd row: S1, Setup (WLAN), S2 -#define LED_PIN_S1 PA5 -#define LED_PIN_SETUP PF5 -#define LED_PIN_S2 PC7 - -// 4th row: Lock -#define LED_PIN_LOCK PA1 - -// Backside -#define LED_PIN_REAR PB13 - -#define LED_ANIMATE_DELAY 20 // Delay (ms) between LEDs of animate sequence (boot/power-on anim) - -class LEDcontrolRMECOWV100 : public LEDcontrol -{ -public: - LEDcontrolRMECOWV100(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds, void (LEDcontrol::*t_set_base10_leds_callback)(char digit)) : LEDcontrol(t_kPinByLedNumPtr, t_kNumLeds, t_set_base10_leds_callback){}; - - void sequence_animate_handler() override; - void sequence_countdown_handler() override; - - unsigned int boot_animation() override; // A short boot animation which return the amount of ms it will take - void set_base10_leds(char digit) override; - void force_off_num_seq_leds(bool force) override; - -private: - void show_countdown_state(unsigned int sec, LED_state state); -}; - -extern LEDcontrolRMECOWV100 leds; - -#endif // YARDFORCE_LEDCONTROL_RMECOWV100_HPP diff --git a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-ECOW-V110.cpp b/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-ECOW-V110.cpp deleted file mode 100644 index db05872..0000000 --- a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-ECOW-V110.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/** - * @file LEDcontrol_RM-ECOW-V110.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce LED driver for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2024-06-27 - * - * @copyright Copyright (c) 2024 - * - */ -#include "LEDcontrol_RM-ECOW-V110.hpp" - -#include - -const uint32_t kPinByLedNum[] = { - // Order derived from LowLevel "enum LED_id" - // C500 - // Use LED num Original Alternative - LED_PIN_CHARGE, // 0 - LED_PIN_BAT, // 1 - LED_PIN_WIRE, // 2 - LED_PIN_LIFTED, // 3 - LED_PIN_NC, // 4 SUN - LED_PIN_NC, // 5 SAT - LED_PIN_NC, // 6 FRI - LED_PIN_NC, // 7 THU - LED_PIN_NC, // 8 WED - LED_PIN_NC, // 9 TUE - LED_PIN_NC, // 10 MON - LED_PIN_LOCK, // 11 - LED_PIN_S2, // 12 digit 5 - LED_PIN_S1, // 13 digit 4 - LED_PIN_10H, // 14 8HR digit 3 - LED_PIN_8H, // 15 6HR digit 2 - LED_PIN_6H, // 16 4HR digit 1 - LED_PIN_4H, // 17 2HR digit 0 - LED_PIN_REAR, // 18 -}; - -const unsigned int kLedAnimOrder[] = {3, 2, 1, 0, 17, 16, 15, 14, 13, 12, 11}; -const unsigned int kNumFrontLeds = sizeof(kLedAnimOrder) / sizeof(unsigned int); - -// Numeric (base10) representation of LEDs. -// I.e. Digit 0 = 4H(0), ... Then S2(5) + S1(4) = 9. -// -// |------ Bit 5 = 4H(0) -// ||----- Bit 4 = 6H(1) -// |||---- Bit 3 = 8H(2) -// ||||--- Bit 2 = 10H(3) -// |||||-- Bit 1 = S1(4) -// ||||||- Bit 0 = S2(5) -const uint8_t kBase10Leds[] = { - 0b100000, // = 0 - 0b010000, - 0b001000, - 0b000100, - 0b000010, - 0b000001, - 0b010001, - 0b001001, - 0b000101, - 0b000011}; // = 9 - -void LEDcontrolRMECOWV110::sequence_animate_handler() { - uint16_t step = seq_get_next_step_(LED_ANIMATE_DELAY); // Animation sequence runs in 15ms steps - - if (step == 0) // Next sequence step not reached now - return; - else if (step >= 1 && step <= kNumFrontLeds) // LED on - { - set(kLedAnimOrder[step - 1], LED_state::LED_on, false); - return; - } else if (step >= (kNumFrontLeds + 1) && step <= (2 * kNumFrontLeds)) // LED off - { - set(kLedAnimOrder[step - kNumFrontLeds - 1], LED_state::LED_off, false); - return; - } else { - seq_start_tick_ = 0; // Sequence end - set(led_states_bin_); // Restore states - return; - } -} - -void LEDcontrolRMECOWV110::show_countdown_state(unsigned int sec, LED_state state) { - unsigned int sec_left = 5 - sec; - - for (size_t i = 1; i <= 4; i++) { - uint8_t led_num = 18 - i; - if (sec_left >= i) // Remaining secs is greater than this LED - { - leds.set(led_num, state, true); // change_state = true because blink states get handled by timer and this need to be stored - } else { - leds.set(led_num, LED_off, true); - force_off(led_num, true); - } - } -}; - -/** - * @brief Countdown LED animation handler - */ -void LEDcontrolRMECOWV110::sequence_countdown_handler() { - uint16_t step = seq_get_next_step_(100); // Animation sequence run in 1000ms steps - - if (step == 0) // Next sequence step not reached now - return; - else if (step == 1 || step == 11 || step == 21 || step == 31 || step == 41) { - show_countdown_state(((step - 1) / 10) + 1, LED_blink_fast); - return; - } else if (step < 41) { - return; - } else { - seq_start_tick_ = 0; // Sequence end - set(led_states_bin_); // Restore states - for (size_t i = 1; i <= 4; i++) - force_off(18 - i, false); - return; - } -} - -/** - * @brief A quick boot/power-on animation, also used as jammed button indicator - * - * @return unsigned int ms how long it will take to play - */ -unsigned int LEDcontrolRMECOWV110::boot_animation() // A short boot animation which return the amount of ms it will take -{ - sequence_start(&LEDcontrol::sequence_animate_handler); - return ((kNumFrontLeds + 1) * LED_ANIMATE_DELAY); -} - -/** - * @brief Set base10 related LEDs for the given (numeric) character - * - * @param digit numeric character - */ -void LEDcontrolRMECOWV110::set_base10_leds(char digit) { - for (uint8_t bit = 0; bit <= 5; bit++) // We've 5 LEDs for base10 number representation - { - bool on = (kBase10Leds[digit - '0'] >> bit) & 0b1; - unsigned int led = bit + 12; - set(led, on ? LED_state::LED_on : LED_state::LED_off, false); - } -} - -void LEDcontrolRMECOWV110::force_off_num_seq_leds(bool force) { - force_off(LED_NUM_LIFTED, force); // Num change signalling LED - for (unsigned int i = 12; i <= 17; i++) // Base10 related LEDs - force_off(i, force); -} - -LEDcontrolRMECOWV110 leds(kPinByLedNum, sizeof(kPinByLedNum) / sizeof(uint32_t), &LEDcontrol::set_base10_leds); // Main LED controller object diff --git a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-ECOW-V110.hpp b/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-ECOW-V110.hpp deleted file mode 100644 index 46c3f97..0000000 --- a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_RM-ECOW-V110.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file LEDcontrol_RM-ECOW-V110.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce LED driver header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2024-06-27 - * - * @copyright Copyright (c) 2024 - * - */ -#ifndef YARDFORCE_LEDCONTROL_RMECOWV110_HPP -#define YARDFORCE_LEDCONTROL_RMECOWV110_HPP - -#include -#include "../LEDcontrol.hpp" - -// 1st row: Lifted, Wire (WLAN), Battery empty, Charge -#define LED_PIN_LIFTED PA0 -#define LED_PIN_WIRE PA4 -#define LED_PIN_BAT PC4 -#define LED_PIN_CHARGE PA6 - -// 2nd row: 4, 6, 8, 10HR -#define LED_PIN_4H PA7 -#define LED_PIN_6H PC5 -#define LED_PIN_8H PB1 -#define LED_PIN_10H PB10 - -// 3rd row: S1, Reserved for Setup (WLAN), S2 -#define LED_PIN_S1 PA5 -#define LED_PIN_S2 PC7 - -// 4th row: Lock -#define LED_PIN_LOCK PA1 - -// Backside -#define LED_PIN_REAR PB0 - -#define LED_ANIMATE_DELAY 20 // Delay (ms) between LEDs of animate sequence (boot/power-on anim) - -class LEDcontrolRMECOWV110 : public LEDcontrol -{ -public: - LEDcontrolRMECOWV110(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds, void (LEDcontrol::*t_set_base10_leds_callback)(char digit)) : LEDcontrol(t_kPinByLedNumPtr, t_kNumLeds, t_set_base10_leds_callback){}; - - void sequence_animate_handler() override; - void sequence_countdown_handler() override; - - unsigned int boot_animation() override; // A short boot animation which return the amount of ms it will take - void set_base10_leds(char digit) override; - void force_off_num_seq_leds(bool force) override; - -private: - void show_countdown_state(unsigned int sec, LED_state state); -}; - -extern LEDcontrolRMECOWV110 leds; - -#endif // YARDFORCE_LEDCONTROL_RMECOWV110_HPP diff --git a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_SAxPRO.cpp b/Firmware/CoverUI/YardForce/include/model/LEDcontrol_SAxPRO.cpp deleted file mode 100644 index f96c100..0000000 --- a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_SAxPRO.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @file LEDcontrol_SAxPRO.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce SAxPRO LED driver for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.2 - * @date 2023-11-07 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "LEDcontrol_SAxPRO.hpp" - -const uint32_t kPinByLedNum[] = { - // Order derived from LowLevel "enum LED_id" - // C500 - // Use LED num Original Alternative - LED_PIN_NC, // 0 - LED_PIN_NC, // 1 - LED_PIN_NC, // 2 - LED_PIN_NC, // 3 - LED_PIN_NC, // 4 SUN(6) - LED_PIN_NC, // 5 SAT(5) - LED_PIN_NC, // 6 FRI(4) - LED_PIN_NC, // 7 THU(3) - LED_PIN_NC, // 8 WED(2) - LED_PIN_NC, // 9 TUE(1) - LED_PIN_NC, // 10 MON(0) - LED_PIN_NC, // 11 - LED_PIN_NC, // 12 digit 5 - LED_PIN_NC, // 13 digit 4 - LED_PIN_NC, // 14 8HR digit 3 - LED_PIN_NC, // 15 6HR digit 2 - LED_PIN_NC, // 16 4HR digit 1 - LED_PIN_NC, // 17 2HR digit 0 - LED_PIN_NC, // 18 - LED_PIN_BACKLIGHT // 19 -}; - -LEDcontrol leds(kPinByLedNum, sizeof(kPinByLedNum) / sizeof(uint32_t)); // Main LED controller object diff --git a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_SAxPRO.hpp b/Firmware/CoverUI/YardForce/include/model/LEDcontrol_SAxPRO.hpp deleted file mode 100644 index 9c70f9d..0000000 --- a/Firmware/CoverUI/YardForce/include/model/LEDcontrol_SAxPRO.hpp +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @file LEDcontrol_SAxPRO.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce SAxPRO LED driver for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.2 - * @date 2023-11-07 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_LEDCONTROL_SAXPRO_HPP -#define YARDFORCE_LEDCONTROL_SAXPRO_HPP - -#include -#include "../LEDcontrol.hpp" - -#define LED_PIN_BACKLIGHT PA11 // SAxPRO only has one LED, the backlight LED -#define LED_NUM_BACKLIGHT 19 - -extern LEDcontrol leds; // Main LED controller object - -#endif // YARDFORCE_LEDCONTROL_SAXPRO_HPP diff --git a/Firmware/CoverUI/YardForce/include/model/buttons_C500.cpp b/Firmware/CoverUI/YardForce/include/model/buttons_C500.cpp deleted file mode 100644 index 52955e9..0000000 --- a/Firmware/CoverUI/YardForce/include/model/buttons_C500.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @file buttons_C500.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce Classic 500 CoverUI Buttons for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.5 - * @date 2023-11-07 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "buttons_C500.h" -#include "../main.h" - -// Map logic button number to pin. Use the same order as in OM FW so that they get scanned in a comparable order! -const std::map kBtnDefByButtonNumMap = { - {BTN_CLK_NUM, {BTN_CLK_PIN, -1}}, - {BTN_HOME_NUM, {BTN_HOME_PIN, -1}}, - {BTN_PLAY_NUM, {BTN_PLAY_PIN, -1}}, - {BTN_S1_NUM, {BTN_S1_PIN, 13}}, - {BTN_S2_NUM, {BTN_S2_PIN, 12}}, - {BTN_LOCK_NUM, {BTN_LOCK_PIN, 11}}, - {BTN_OK_NUM, {BTN_OK_PIN, -1}}, - {BTN_SUN_NUM, {BTN_SUN_PIN, 4}}, - {BTN_MON_NUM, {BTN_MON_PIN, 10}}, - {BTN_TUE_NUM, {BTN_TUE_PIN, 9}}, - {BTN_WED_NUM, {BTN_WED_PIN, 8}}, - {BTN_THU_NUM, {BTN_THU_PIN, 7}}, - {BTN_FRI_NUM, {BTN_FRI_PIN, 6}}, - {BTN_SAT_NUM, {BTN_SAT_PIN, 5}}}; - -Buttons buttons(kBtnDefByButtonNumMap); - -/** - * @brief Check if one of the "magic buttons" got pressed and do his function. - * SETUP + 4H = Display FW version - * SETUP + 10H = LED animation - */ -void magic_buttons() -{ - if (!buttons.is_pressed(BTN_OK_NUM)) - return; - - if (buttons.is_pressed(BTN_SUN_NUM)) - leds.sequence_start(&LEDcontrol::sequence_animate_handler); - else if (buttons.is_pressed(BTN_CLK_NUM)) - leds.show_num(FIRMWARE_VERSION); - return; -}; diff --git a/Firmware/CoverUI/YardForce/include/model/buttons_C500.h b/Firmware/CoverUI/YardForce/include/model/buttons_C500.h deleted file mode 100644 index 1c0ae99..0000000 --- a/Firmware/CoverUI/YardForce/include/model/buttons_C500.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @file buttons_C500.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce Classic 500 CoverUI Buttons header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.5 - * @date 2023-11-07 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_BUTTONS_C500_H -#define YARDFORCE_BUTTONS_C500_H - -#include -#include "../Buttons.hpp" - -#define BTN_CLK_PIN PF4 -#define BTN_OK_PIN PF5 -#define BTN_S1_PIN PB2 -#define BTN_S2_PIN PB10 -#define BTN_LOCK_PIN PB11 -#define BTN_MON_PIN PB12 -#define BTN_TUE_PIN PB13 -#define BTN_WED_PIN PB14 -#define BTN_THU_PIN PB15 -#define BTN_FRI_PIN PC6 -#define BTN_SAT_PIN PC7 -#define BTN_SUN_PIN PC8 -#define BTN_PLAY_PIN PA11 -#define BTN_HOME_PIN PA12 - -extern Buttons buttons; - -#define HAS_MAGIC_BUTTONS -void magic_buttons(); - -#endif // YARDFORCE_BUTTONS_C500_H diff --git a/Firmware/CoverUI/YardForce/include/model/buttons_RM-EC3-V11.cpp b/Firmware/CoverUI/YardForce/include/model/buttons_RM-EC3-V11.cpp deleted file mode 100644 index 25dc89e..0000000 --- a/Firmware/CoverUI/YardForce/include/model/buttons_RM-EC3-V11.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file buttons_RM-EC3-V11.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-EC3-V1.1 CoverUI Buttons for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-16 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "../main.h" - -// Map logic button number to pin. Use the same order as in OM FW so that they get scanned in a comparable order! -const std::map kBtnDefByButtonNumMap = { - {BTN_HOME_NUM, {BTN_HOME_PIN, -1}}, - {BTN_PLAY_NUM, {BTN_PLAY_PIN, -1}}, - {BTN_S1_NUM, {BTN_S1_PIN, LED_NUM_S1}}, - {BTN_S2_NUM, {BTN_S2_PIN, LED_NUM_S2}}, - {BTN_SETUP_NUM, {BTN_SETUP_PIN, 19}}, - {BTN_MENU_NUM, {BTN_MENU_PIN, -1}}, - {BTN_1_NUM, {BTN_1_PIN, -1}}, - {BTN_2_NUM, {BTN_2_PIN, -1}}, - {BTN_3_NUM, {BTN_3_PIN, -1}}, - {BTN_4_NUM, {BTN_4_PIN, -1}}, - {BTN_5_NUM, {BTN_5_PIN, -1}}, - {BTN_6_NUM, {BTN_6_PIN, -1}}, - {BTN_7_NUM, {BTN_7_PIN, -1}}, - {BTN_8_NUM, {BTN_8_PIN, -1}}, - {BTN_9_NUM, {BTN_9_PIN, -1}}, - {BTN_BACK_NUM, {BTN_BACK_PIN, -1}}, - {BTN_0_NUM, {BTN_0_PIN, -1}}, - {BTN_OK_NUM, {BTN_OK_PIN, -1}}}; - -Buttons buttons(kBtnDefByButtonNumMap); - -/** - * @brief Check if one of the "magic buttons" got pressed and do his function. - * SETUP + 4H = Display FW version - * SETUP + 10H = LED animation - */ -void magic_buttons() -{ - if (!buttons.is_pressed(BTN_MENU_NUM)) - return; - - if (buttons.is_pressed(BTN_BACK_NUM)) - display.openmower_anim(); - else if (buttons.is_pressed(BTN_0_NUM)) - display.start_anncmnt(2000, yardforce::display::Display::AnncmntType::version); - - return; -}; diff --git a/Firmware/CoverUI/YardForce/include/model/buttons_RM-EC3-V11.h b/Firmware/CoverUI/YardForce/include/model/buttons_RM-EC3-V11.h deleted file mode 100644 index 65a723d..0000000 --- a/Firmware/CoverUI/YardForce/include/model/buttons_RM-EC3-V11.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @file buttons_RM-EC3-V11.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-EC3-V1.1 CoverUI Buttons header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-16 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_BUTTONS_RMEC3V11_H -#define YARDFORCE_BUTTONS_RMEC3V11_H - -#include -#include "../Buttons.hpp" - -#define BTN_S1_PIN PC14 -#define BTN_S2_PIN PC13 -#define BTN_PLAY_PIN PC7 -#define BTN_HOME_PIN PC2 -#define BTN_SETUP_PIN PA0 - -#define BTN_MENU_PIN PB4 - -#define BTN_1_PIN PB8 -#define BTN_2_PIN PB5 -#define BTN_3_PIN PB3 -#define BTN_4_PIN PD2 -#define BTN_5_PIN PC12 -#define BTN_6_PIN PC11 -#define BTN_7_PIN PC10 -#define BTN_8_PIN PB14 -#define BTN_9_PIN PC6 -#define BTN_BACK_PIN PA15 -#define BTN_0_PIN PB9 -#define BTN_OK_PIN PB13 - -extern Buttons buttons; - -#define HAS_MAGIC_BUTTONS -extern void magic_buttons(); - -#endif // YARDFORCE_BUTTONS_RMEC3V11_H diff --git a/Firmware/CoverUI/YardForce/include/model/buttons_RM-ECOW-V100.cpp b/Firmware/CoverUI/YardForce/include/model/buttons_RM-ECOW-V100.cpp deleted file mode 100644 index 4862d83..0000000 --- a/Firmware/CoverUI/YardForce/include/model/buttons_RM-ECOW-V100.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @file buttons_RM-ECOW-V100.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.0.0 CoverUI Buttons for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.5 - * @date 2023-11-05 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "../main.h" - -// Map logic button number to pin. Use the same order as in OM FW so that they get scanned in a comparable order! -const std::map kBtnDefByButtonNumMap = { - {BTN_HOME_NUM, {BTN_HOME_PIN, -1}}, - {BTN_PLAY_NUM, {BTN_PLAY_PIN, -1}}, - {BTN_S1_NUM, {BTN_S1_PIN, LED_NUM_S1}}, - {BTN_S2_NUM, {BTN_S2_PIN, LED_NUM_S2}}, - {BTN_LOCK_NUM, {BTN_LOCK_PIN, 11}}, - {BTN_4H_NUM, {BTN_4H_PIN, 17}}, - {BTN_6H_NUM, {BTN_6H_PIN, 16}}, - {BTN_8H_NUM, {BTN_8H_PIN, 15}}, - {BTN_10H_NUM, {BTN_10H_PIN, 14}}, - {BTN_SETUP_NUM, {BTN_SETUP_PIN, 19}}}; - -Buttons buttons(kBtnDefByButtonNumMap); - -/** - * @brief Check if one of the "magic buttons" got pressed and do his function. - * SETUP + 4H = Display FW version - * SETUP + 10H = LED animation - */ -void magic_buttons() -{ - if (!buttons.is_pressed(BTN_SETUP_NUM)) - return; - - if (buttons.is_pressed(BTN_10H_NUM)) - leds.sequence_start(&LEDcontrol::sequence_animate_handler); - else if (buttons.is_pressed(BTN_4H_NUM)) - leds.show_num(FIRMWARE_VERSION); - return; -}; diff --git a/Firmware/CoverUI/YardForce/include/model/buttons_RM-ECOW-V100.h b/Firmware/CoverUI/YardForce/include/model/buttons_RM-ECOW-V100.h deleted file mode 100644 index 7d7afff..0000000 --- a/Firmware/CoverUI/YardForce/include/model/buttons_RM-ECOW-V100.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @file buttons_RM-ECOW-V100.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.0.0 CoverUI Buttons header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.5 - * @date 2023-11-05 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_BUTTONS_RMECOWV100_H -#define YARDFORCE_BUTTONS_RMECOWV100_H - -#include - -#include "../Buttons.hpp" - -#define BTN_S1_PIN PA4 -#define BTN_S2_PIN PC6 -#define BTN_LOCK_PIN PA0 -#define BTN_4H_PIN PA6 -#define BTN_6H_PIN PC4 -#define BTN_8H_PIN PB0 -#define BTN_10H_PIN PB2 -#define BTN_PLAY_PIN PC11 -#define BTN_HOME_PIN PC12 -#define BTN_SETUP_PIN PF4 - -extern Buttons buttons; - -#define HAS_MAGIC_BUTTONS -extern void magic_buttons(); - -#endif // YARDFORCE_BUTTONS_RMECOWV100_H diff --git a/Firmware/CoverUI/YardForce/include/model/buttons_RM-ECOW-V110.cpp b/Firmware/CoverUI/YardForce/include/model/buttons_RM-ECOW-V110.cpp deleted file mode 100644 index d73ecb8..0000000 --- a/Firmware/CoverUI/YardForce/include/model/buttons_RM-ECOW-V110.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @file buttons_RM-ECOW-V110.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.1.0 CoverUI Buttons for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2024-06-27 - * - * @copyright Copyright (c) 2024 - * - */ -#include - -#include "../main.h" - -// Map logic button number to pin. Use the same order as in OM FW so that they get scanned in a comparable order! -const std::map kBtnDefByButtonNumMap = { - {BTN_HOME_NUM, {BTN_HOME_PIN, -1}}, - {BTN_PLAY_NUM, {BTN_PLAY_PIN, -1}}, - {BTN_S1_NUM, {BTN_S1_PIN, LED_NUM_S1}}, - {BTN_S2_NUM, {BTN_S2_PIN, LED_NUM_S2}}, - {BTN_LOCK_NUM, {BTN_LOCK_PIN, 11}}, - {BTN_4H_NUM, {BTN_4H_PIN, 17}}, - {BTN_6H_NUM, {BTN_6H_PIN, 16}}, - {BTN_8H_NUM, {BTN_8H_PIN, 15}}, - {BTN_10H_NUM, {BTN_10H_PIN, 14}} -}; - -Buttons buttons(kBtnDefByButtonNumMap); - -/** - * @brief Check if one of the "magic buttons" got pressed and do his function. - * SETUP + 4H = Display FW version - * SETUP + 10H = LED animation - */ -void magic_buttons() { - if (!buttons.is_pressed(BTN_LOCK_NUM)) - return; - - if (buttons.is_pressed(BTN_10H_NUM)) - leds.sequence_start(&LEDcontrol::sequence_animate_handler); - else if (buttons.is_pressed(BTN_4H_NUM)) - leds.show_num(FIRMWARE_VERSION); - return; -}; diff --git a/Firmware/CoverUI/YardForce/include/model/buttons_RM-ECOW-V110.h b/Firmware/CoverUI/YardForce/include/model/buttons_RM-ECOW-V110.h deleted file mode 100644 index 1bf1085..0000000 --- a/Firmware/CoverUI/YardForce/include/model/buttons_RM-ECOW-V110.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file buttons_RM-ECOW-V110.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.1.0 CoverUI Buttons header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2024-06-27 - * - * @copyright Copyright (c) 2024 - * - */ -#ifndef YARDFORCE_BUTTONS_RMECOWV110_H -#define YARDFORCE_BUTTONS_RMECOWV110_H - -#include -#include "../Buttons.hpp" - -#define BTN_S1_PIN PB11 -#define BTN_S2_PIN PC6 -#define BTN_LOCK_PIN PB12 -#define BTN_4H_PIN PB14 -#define BTN_6H_PIN PB15 -#define BTN_8H_PIN PB13 -#define BTN_10H_PIN PB2 -#define BTN_PLAY_PIN PC11 -#define BTN_HOME_PIN PC12 - -extern Buttons buttons; - -#define HAS_MAGIC_BUTTONS -extern void magic_buttons(); - -#endif // YARDFORCE_BUTTONS_RMECOWV110_H diff --git a/Firmware/CoverUI/YardForce/include/model/buttons_SAxPRO.cpp b/Firmware/CoverUI/YardForce/include/model/buttons_SAxPRO.cpp deleted file mode 100644 index 5df9c43..0000000 --- a/Firmware/CoverUI/YardForce/include/model/buttons_SAxPRO.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @file buttons_SAxPRO.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce SAxPRO CoverUI Buttons for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.2 - * @date 2023-11-02 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "../main.h" - -// Map logic button number to pin. Use the same order as in OM FW so that they get scanned in a comparable order! -const std::map kBtnDefByButtonNumMap = { - {BTN_HOME_NUM, {BTN_HOME_PIN, -1}}, - {BTN_PLAY_NUM, {BTN_PLAY_PIN, -1}}, - {BTN_OK_NUM, {BTN_OK_PIN, -1}}, - {BTN_UP_NUM, {BTN_UP_PIN, -1}}, - {BTN_DOWN_NUM, {BTN_DOWN_PIN, -1}}, - {BTN_BACK_NUM, {BTN_BACK_PIN, -1}}}; - -Buttons buttons(kBtnDefByButtonNumMap); diff --git a/Firmware/CoverUI/YardForce/include/model/buttons_SAxPRO.h b/Firmware/CoverUI/YardForce/include/model/buttons_SAxPRO.h deleted file mode 100644 index bea54a8..0000000 --- a/Firmware/CoverUI/YardForce/include/model/buttons_SAxPRO.h +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @file buttons_SAxPRO.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce SAxPRO CoverUI Buttons for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.2 - * @date 2023-11-02 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_BUTTONS_SAXPRO_H -#define YARDFORCE_BUTTONS_SAXPRO_H - -#include -#include "../Buttons.hpp" - -#define BTN_PLAY_PIN PC0 // or Start -#define BTN_HOME_PIN PC1 -#define BTN_UP_PIN PB14 -#define BTN_DOWN_PIN PB13 -#define BTN_OK_PIN PB12 // or Enter -#define BTN_BACK_PIN PB15 - -extern Buttons buttons; - -#endif // YARDFORCE_BUTTONS_SAXPRO_H diff --git a/Firmware/CoverUI/YardForce/include/model/emergency_C500.cpp b/Firmware/CoverUI/YardForce/include/model/emergency_C500.cpp deleted file mode 100644 index b3ad67c..0000000 --- a/Firmware/CoverUI/YardForce/include/model/emergency_C500.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @file emergency_C500.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce Classic 500 CoverUI Emergency header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-10-31 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "emergency_C500.h" -#include "../Emergency.hpp" - -const EmergencyPinStateDef kEmergencyPinStateDefs[] = { - {PIN_HALL_STOP_WHITE, INPUT, Emergency_state::Emergency_stop1}, - {PIN_HALL_STOP_YELLOW, INPUT, Emergency_state::Emergency_stop2}, - {PIN_HALL_WHEEL_RED, INPUT, Emergency_state::Emergency_lift1}, - {PIN_HALL_WHEEL_BLUE, INPUT, Emergency_state::Emergency_lift2}}; - -Emergency emergency(kEmergencyPinStateDefs, sizeof(kEmergencyPinStateDefs) / sizeof(EmergencyPinStateDef)); diff --git a/Firmware/CoverUI/YardForce/include/model/emergency_C500.h b/Firmware/CoverUI/YardForce/include/model/emergency_C500.h deleted file mode 100644 index 9e32618..0000000 --- a/Firmware/CoverUI/YardForce/include/model/emergency_C500.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @file emergency_C500.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce Classic 500 CoverUI Emergency header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-10-31 - * - * @copyright Copyright (c) 2023 - * - */ - -#ifndef YARDFORCE_EMERGENCY_C500_H -#define YARDFORCE_EMERGENCY_C500_H - -#include -#include "../Emergency.hpp" - -#define PIN_HALL_STOP_WHITE PC5 -#define PIN_HALL_STOP_YELLOW PB6 -#define PIN_HALL_WHEEL_RED PB7 -#define PIN_HALL_WHEEL_BLUE PB8 - -extern Emergency emergency; - -#endif // YARDFORCE_EMERGENCY_C500_H diff --git a/Firmware/CoverUI/YardForce/include/model/emergency_RM-EC3-V11.cpp b/Firmware/CoverUI/YardForce/include/model/emergency_RM-EC3-V11.cpp deleted file mode 100644 index b006e76..0000000 --- a/Firmware/CoverUI/YardForce/include/model/emergency_RM-EC3-V11.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file emergency_RM-EC3-V11.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-EC3-V1.1 CoverUI Emergency class for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-28 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "emergency_RM-EC3-V11.h" -#include "../Emergency.hpp" - -const EmergencyPinStateDef kEmergencyPinStateDefs[] = { -#ifdef MOD_HALL - {PIN_HALL1, INPUT_PULLUP, Emergency_state::Emergency_lift1}, - {PIN_HALL2, INPUT_PULLUP, Emergency_state::Emergency_lift1}, - {PIN_HALL3, INPUT_PULLUP, Emergency_state::Emergency_lift2}, - {PIN_HALL4, INPUT_PULLUP, Emergency_state::Emergency_lift2}, -#endif -#ifdef MOD_STOP - {PIN_STOP1, INPUT_PULLUP, Emergency_state::Emergency_stop1}, - {PIN_STOP2, INPUT_PULLUP, Emergency_state::Emergency_stop2}, -#endif -}; - -Emergency emergency(kEmergencyPinStateDefs, sizeof(kEmergencyPinStateDefs) / sizeof(EmergencyPinStateDef)); diff --git a/Firmware/CoverUI/YardForce/include/model/emergency_RM-EC3-V11.h b/Firmware/CoverUI/YardForce/include/model/emergency_RM-EC3-V11.h deleted file mode 100644 index 72f9a5d..0000000 --- a/Firmware/CoverUI/YardForce/include/model/emergency_RM-EC3-V11.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file emergency_RM-EC3-V11.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-EC3-V1.1 CoverUI Emergency header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-28 - * - * @copyright Copyright (c) 2023 - * - */ - -#ifndef YARDFORCE_EMERGENCY_RMEC3V11_H -#define YARDFORCE_EMERGENCY_RMEC3V11_H - -#include -#include "../Emergency.hpp" - -#define PIN_STOP1 PC15 -#define PIN_STOP2 PB6 - -#define PIN_HALL1 PB15 -#define PIN_HALL2 PC0 -#define PIN_HALL3 PC8 -#define PIN_HALL4 PC9 - -extern Emergency emergency; - -#endif // YARDFORCE_EMERGENCY_RMEC3V11_H diff --git a/Firmware/CoverUI/YardForce/include/model/emergency_RM-ECOW-V100.cpp b/Firmware/CoverUI/YardForce/include/model/emergency_RM-ECOW-V100.cpp deleted file mode 100644 index 0787fd8..0000000 --- a/Firmware/CoverUI/YardForce/include/model/emergency_RM-ECOW-V100.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @file emergency_RM-ECOW-V100.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.0.0 CoverUI Emergency class for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.3 - * @date 2024-09-28 - * - * @copyright Copyright (c) 2023, 2024 - * - */ -#include "emergency_RM-ECOW-V100.h" - -#include - -#include "../Emergency.hpp" - -const EmergencyPinStateDef kEmergencyPinStateDefs[] = { -#ifdef MOD_HALL - {PIN_HALL1, INPUT_PULLUP, Emergency_state::Emergency_lift1}, - {PIN_HALL2, INPUT_PULLUP, Emergency_state::Emergency_lift1}, - {PIN_HALL3, INPUT_PULLUP, Emergency_state::Emergency_lift2}, - {PIN_HALL4, INPUT_PULLUP, Emergency_state::Emergency_lift2}, -#endif -#ifdef MOD_STOP - {PIN_STOP_JP5, INPUT_PULLUP, Emergency_state::Emergency_stop1}, - {PIN_STOP_JP6, INPUT_PULLUP, Emergency_state::Emergency_stop2} -#endif -}; - -Emergency emergency(kEmergencyPinStateDefs, sizeof(kEmergencyPinStateDefs) / sizeof(EmergencyPinStateDef)); diff --git a/Firmware/CoverUI/YardForce/include/model/emergency_RM-ECOW-V100.h b/Firmware/CoverUI/YardForce/include/model/emergency_RM-ECOW-V100.h deleted file mode 100644 index a08c842..0000000 --- a/Firmware/CoverUI/YardForce/include/model/emergency_RM-ECOW-V100.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @file emergency_RM-ECOW-V100.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.0.0 CoverUI Emergency header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.3 - * @date 2024-09-28 - * - * @copyright Copyright (c) 2023, 2024 - * - */ - -#ifndef YARDFORCE_EMERGENCY_RMECOWV100_HPP -#define YARDFORCE_EMERGENCY_RMECOWV100_HPP - -#include - -#include "../Emergency.hpp" - -#define PIN_STOP_JP5 PC10 -#define PIN_STOP_JP6 PA15 - -#define PIN_HALL1 PA8 // LIFT -#define PIN_HALL2 PF6 // LIFTX -#define PIN_HALL3 PA12 // LBUMP -#define PIN_HALL4 PA11 // RBUMP - -extern Emergency emergency; - -#endif // YARDFORCE_EMERGENCY_RMECOWV100_HPP diff --git a/Firmware/CoverUI/YardForce/include/model/emergency_RM-ECOW-V110.cpp b/Firmware/CoverUI/YardForce/include/model/emergency_RM-ECOW-V110.cpp deleted file mode 100644 index 08039b4..0000000 --- a/Firmware/CoverUI/YardForce/include/model/emergency_RM-ECOW-V110.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file emergency_RM-ECOW-V110.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.1.0 CoverUI Emergency class for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2024-06-30 - * - * @copyright Copyright (c) 2024 - * - */ -#include -#include "emergency_RM-ECOW-V110.h" -#include "../Emergency.hpp" - -const EmergencyPinStateDef kEmergencyPinStateDefs[] = { -#ifdef MOD_HALL - {PIN_HALL1, INPUT_PULLUP, Emergency_state::Emergency_lift1}, - {PIN_HALL2, INPUT_PULLUP, Emergency_state::Emergency_lift1}, - {PIN_HALL3, INPUT_PULLUP, Emergency_state::Emergency_lift2}, - {PIN_HALL4, INPUT_PULLUP, Emergency_state::Emergency_lift2}, -#endif -#ifdef MOD_STOP - {PIN_STOP_JP6, INPUT_PULLUP, Emergency_state::Emergency_stop1}, - {PIN_STOP_JP8, INPUT_PULLUP, Emergency_state::Emergency_stop2}, -#endif -}; - -Emergency emergency(kEmergencyPinStateDefs, sizeof(kEmergencyPinStateDefs) / sizeof(EmergencyPinStateDef)); diff --git a/Firmware/CoverUI/YardForce/include/model/emergency_RM-ECOW-V110.h b/Firmware/CoverUI/YardForce/include/model/emergency_RM-ECOW-V110.h deleted file mode 100644 index a8a83ee..0000000 --- a/Firmware/CoverUI/YardForce/include/model/emergency_RM-ECOW-V110.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file emergency_RM-ECOW-V110.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.1.0 CoverUI Emergency header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2024-06-30 - * - * @copyright Copyright (c) 2024 - * - */ - -#ifndef YARDFORCE_EMERGENCY_RMECOWV110_HPP -#define YARDFORCE_EMERGENCY_RMECOWV110_HPP - -#include -#include "../Emergency.hpp" - -#define PIN_STOP_JP6 PC10 -#define PIN_STOP_JP8 PA15 - -#define PIN_HALL1 PA8 // LIFT -#define PIN_HALL2 PA9 // LIFTX -#define PIN_HALL3 PC8 // LBUMP -#define PIN_HALL4 PC9 // RBUMP - -extern Emergency emergency; - -#endif // YARDFORCE_EMERGENCY_RMECOWV110_HPP diff --git a/Firmware/CoverUI/YardForce/include/model/hatch_RM-EC3-V11.cpp b/Firmware/CoverUI/YardForce/include/model/hatch_RM-EC3-V11.cpp deleted file mode 100644 index dd23d4e..0000000 --- a/Firmware/CoverUI/YardForce/include/model/hatch_RM-EC3-V11.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file hatch_RM-EC3-V11.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-EC3-V1.1 CoverUI Hatch for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-16 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "../model_RM-EC3-V11.h" - -unsigned int HatchRMEC3V11::handle_button(unsigned int button_id, unsigned int press_cnt) -{ - // If backlight is off, skip first button press - if (display.check_backlight() == LED_off) - { - display.set_backlight(); - return 0; // Skip handling of first button-press if backlight was off - } - display.set_backlight(); - - // Clear emergency = Enter (OK) button - if (button_id == BTN_OK_NUM) - { - display.start_anncmnt(4000, yardforce::display::Display::AnncmntType::close_hatch); - queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec - return 0; // Don't return button now (hatch is still open) - } - - // Play & Home button - if (button_id == BTN_HOME_NUM || button_id == BTN_PLAY_NUM) - { - display.start_anncmnt(4000, yardforce::display::Display::AnncmntType::close_hatch); - queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec - queue_button(button_id, 0, 5000); - return 0; // Don't return button now (hatch is still open) - } - - if (button_id == BTN_1_NUM) - return BTN_MON_NUM; // Volume up - - if (button_id == BTN_2_NUM) - return BTN_TUE_NUM; // Volume down - - if (button_id == BTN_3_NUM) - return BTN_WED_NUM; // Next language - - return button_id; -}; - -HatchRMEC3V11 hatch; diff --git a/Firmware/CoverUI/YardForce/include/model/hatch_RM-EC3-V11.hpp b/Firmware/CoverUI/YardForce/include/model/hatch_RM-EC3-V11.hpp deleted file mode 100644 index e3a69b5..0000000 --- a/Firmware/CoverUI/YardForce/include/model/hatch_RM-EC3-V11.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @file hatch_RM-EC3-V11.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-EC3-V1.1 CoverUI Hatch header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-16 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_HATCH_RMEC3V11_HPP -#define YARDFORCE_HATCH_RMEC3V11_HPP - -#include -#include "../Hatch.hpp" - -class HatchRMEC3V11 : public Hatch -{ -public: - - unsigned int handle_button(unsigned int button_id, unsigned int press_cnt); -}; - -extern HatchRMEC3V11 hatch; - -#endif // YARDFORCE_HATCH_RMEC3V11_HPP diff --git a/Firmware/CoverUI/YardForce/include/model/hatch_RM-ECOW-V100.cpp b/Firmware/CoverUI/YardForce/include/model/hatch_RM-ECOW-V100.cpp deleted file mode 100644 index 5f6ea9b..0000000 --- a/Firmware/CoverUI/YardForce/include/model/hatch_RM-ECOW-V100.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @file hatch_RM-ECOW-V100.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.0.0 CoverUI Hatch for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-03 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "../model_RM-ECOW-V100.h" - -unsigned int HatchRMECOWV100::handle_button(unsigned int button_id, unsigned int press_cnt) -{ - if (button_id == BTN_LOCK_NUM && press_cnt >= 2) - { - leds.sequence_start(&LEDcontrol::sequence_countdown_handler); // Close hatch countdown - queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec - return 0; // Don't return button now (hatch is still open) - } - - if (button_id == BTN_HOME_NUM || button_id == BTN_PLAY_NUM || button_id == BTN_S1_NUM || button_id == BTN_S2_NUM) - { - if (press_cnt > 10 || true) // Be sure that the queue isn't filled with "wait for release" loops - { - leds.sequence_start(&LEDcontrol::sequence_countdown_handler); // Close hatch countdown - queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec - queue_button(button_id, 0, 5000); - return 0; // Don't return button now (hatch is still open) - } - } - - if (button_id == BTN_4H_NUM) - return BTN_MON_NUM; // Volume up - - if (button_id == BTN_6H_NUM) - return BTN_TUE_NUM; // Volume down - - return button_id; -}; - -HatchRMECOWV100 hatch; diff --git a/Firmware/CoverUI/YardForce/include/model/hatch_RM-ECOW-V100.hpp b/Firmware/CoverUI/YardForce/include/model/hatch_RM-ECOW-V100.hpp deleted file mode 100644 index ab9f1d7..0000000 --- a/Firmware/CoverUI/YardForce/include/model/hatch_RM-ECOW-V100.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @file hatch_RM-ECOW-V100.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.0.0 CoverUI Hatch header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-03 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_HATCH_RMECOWV100_HPP -#define YARDFORCE_HATCH_RMECOWV100_HPP - -#include -#include "../Hatch.hpp" - -class HatchRMECOWV100 : public Hatch -{ -public: - - unsigned int handle_button(unsigned int button_id, unsigned int press_cnt); -}; - -extern HatchRMECOWV100 hatch; - -#endif // YARDFORCE_HATCH_RMECOWV100_HPP diff --git a/Firmware/CoverUI/YardForce/include/model/hatch_RM-ECOW-V110.cpp b/Firmware/CoverUI/YardForce/include/model/hatch_RM-ECOW-V110.cpp deleted file mode 100644 index 7475b2e..0000000 --- a/Firmware/CoverUI/YardForce/include/model/hatch_RM-ECOW-V110.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @file hatch_RM-ECOW-V110.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.1.0 CoverUI Hatch for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2024-06-27 - * - * @copyright Copyright (c) 2024 - * - */ -#include -#include "../model_RM-ECOW-V110.h" - -unsigned int HatchRMECOWV110::handle_button(unsigned int button_id, unsigned int press_cnt) -{ - if (button_id == BTN_LOCK_NUM && press_cnt >= 2) - { - leds.sequence_start(&LEDcontrol::sequence_countdown_handler); // Close hatch countdown - queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec - return 0; // Don't return button now (hatch is still open) - } - - if (button_id == BTN_HOME_NUM || button_id == BTN_PLAY_NUM || button_id == BTN_S1_NUM || button_id == BTN_S2_NUM) - { - if (press_cnt > 10 || true) // Be sure that the queue isn't filled with "wait for release" loops - { - leds.sequence_start(&LEDcontrol::sequence_countdown_handler); // Close hatch countdown - queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec - queue_button(button_id, 0, 5000); - return 0; // Don't return button now (hatch is still open) - } - } - - if (button_id == BTN_4H_NUM) - return BTN_MON_NUM; // Volume up - - if (button_id == BTN_6H_NUM) - return BTN_TUE_NUM; // Volume down - - return button_id; -}; - -HatchRMECOWV110 hatch; diff --git a/Firmware/CoverUI/YardForce/include/model/hatch_RM-ECOW-V110.hpp b/Firmware/CoverUI/YardForce/include/model/hatch_RM-ECOW-V110.hpp deleted file mode 100644 index e672abe..0000000 --- a/Firmware/CoverUI/YardForce/include/model/hatch_RM-ECOW-V110.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @file hatch_RM-ECOW-V110.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.1.0 CoverUI Hatch header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2024-06-27 - * - * @copyright Copyright (c) 2024 - * - */ -#ifndef YARDFORCE_HATCH_RMECOWV110_HPP -#define YARDFORCE_HATCH_RMECOWV110_HPP - -#include -#include "../Hatch.hpp" - -class HatchRMECOWV110 : public Hatch -{ -public: - - unsigned int handle_button(unsigned int button_id, unsigned int press_cnt); -}; - -extern HatchRMECOWV110 hatch; - -#endif // YARDFORCE_HATCH_RMECOWV110_HPP diff --git a/Firmware/CoverUI/YardForce/include/model/hatch_SAxPRO.cpp b/Firmware/CoverUI/YardForce/include/model/hatch_SAxPRO.cpp deleted file mode 100644 index 827c9e0..0000000 --- a/Firmware/CoverUI/YardForce/include/model/hatch_SAxPRO.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file hatch_SAxPRO.cpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce SAxPRO CoverUI Hatch for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-11 - * - * @copyright Copyright (c) 2023 - * - */ -#include -#include "../model_SAxPRO.h" - -unsigned int HatchSAXPRO::handle_button(unsigned int button_id, unsigned int press_cnt) -{ - // If backlight is off, skip first button press - if (display.check_backlight() == LED_off) - { - display.set_backlight(); - return 0; // Skip handling of first button-press if backlight was off - } - display.set_backlight(); - - // Clear emergency = Enter (OK) button - if (button_id == BTN_OK_NUM) - { - display.start_anncmnt(4000, yardforce::display::Display::AnncmntType::close_hatch); - queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec - return 0; // Don't return button now (hatch is still open) - } - - // Play & Home button - if (button_id == BTN_HOME_NUM || button_id == BTN_PLAY_NUM) - { - display.start_anncmnt(4000, yardforce::display::Display::AnncmntType::close_hatch); - queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec - queue_button(button_id, 0, 5000); - return 0; // Don't return button now (hatch is still open) - } - - if (button_id == BTN_UP_NUM) - return BTN_MON_NUM; // Volume up - - if (button_id == BTN_DOWN_NUM) - return BTN_TUE_NUM; // Volume down - - if (button_id == BTN_BACK_NUM) - return BTN_WED_NUM; // Next language - - return button_id; -}; - -HatchSAXPRO hatch; diff --git a/Firmware/CoverUI/YardForce/include/model/hatch_SAxPRO.hpp b/Firmware/CoverUI/YardForce/include/model/hatch_SAxPRO.hpp deleted file mode 100644 index 4e51939..0000000 --- a/Firmware/CoverUI/YardForce/include/model/hatch_SAxPRO.hpp +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @file hatch_SAxPRO.hpp - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce SAxPRO CoverUI Hatch header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-11 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_HATCH_SAXPRO_HPP -#define YARDFORCE_HATCH_SAXPRO_HPP - -#include -#include "../Hatch.hpp" - -class HatchSAXPRO : public Hatch -{ -public: - unsigned int handle_button(unsigned int button_id, unsigned int press_cnt); -}; - -extern HatchSAXPRO hatch; - -#endif // YARDFORCE_HATCH_SAXPRO_HPP diff --git a/Firmware/CoverUI/YardForce/include/model_C500.h b/Firmware/CoverUI/YardForce/include/model_C500.h deleted file mode 100644 index 52b3156..0000000 --- a/Firmware/CoverUI/YardForce/include/model_C500.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @file model_C500.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce Classic 500 model header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-10-30 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_MDL_C500_H -#define YARDFORCE_MDL_C500_H - -#include "model/LEDcontrol_C500.hpp" -#include "model/buttons_C500.h" - -#ifdef MOD_EMERGENCY -#include "model/emergency_C500.h" -#endif - -// LowLevel Serial Pins -#define UART_LL_RX PA3 -#define UART_LL_TX PA2 - -#endif // YARDFORCE_MDL_C500_H diff --git a/Firmware/CoverUI/YardForce/include/model_RM-EC3-V11.h b/Firmware/CoverUI/YardForce/include/model_RM-EC3-V11.h deleted file mode 100644 index 56fd12a..0000000 --- a/Firmware/CoverUI/YardForce/include/model_RM-EC3-V11.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file model_RM-EC3-V11.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-EC3-V1.1 model header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-11-16 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_MDL_RMEC3V11_H -#define YARDFORCE_MDL_RMEC3V11_H - -#include "model/LEDcontrol_RM-EC3-V11.hpp" -#include "model/buttons_RM-EC3-V11.h" -#include "model/hatch_RM-EC3-V11.hpp" -#include "model/Display_RM-EC3-V11.hpp" - -#ifdef MOD_EMERGENCY -#include "model/emergency_RM-EC3-V11.h" -#endif - -// LowLevel Serial Pins -#define UART_LL_RX PA3 -#define UART_LL_TX PA2 - -#endif // YARDFORCE_MDL_RMEC3V11_H diff --git a/Firmware/CoverUI/YardForce/include/model_RM-ECOW-V100.h b/Firmware/CoverUI/YardForce/include/model_RM-ECOW-V100.h deleted file mode 100644 index e4329c6..0000000 --- a/Firmware/CoverUI/YardForce/include/model_RM-ECOW-V100.h +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @file model_RM-ECOW-V100.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.0.0 model header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2023-10-23 - * - * @copyright Copyright (c) 2023 - * - */ -#ifndef YARDFORCE_MDL_RMECOWV100_H -#define YARDFORCE_MDL_RMECOWV100_H - -#include "model/LEDcontrol_RM-ECOW-V100.hpp" -#include "model/buttons_RM-ECOW-V100.h" -#include "model/hatch_RM-ECOW-V100.hpp" - -#ifdef MOD_EMERGENCY -#include "model/emergency_RM-ECOW-V100.h" -#endif - -// LowLevel Serial Pins -#define UART_LL_RX PA3 -#define UART_LL_TX PA2 - -#endif // YARDFORCE_MDL_RMECOWV100_H diff --git a/Firmware/CoverUI/YardForce/include/model_RM-ECOW-V110.h b/Firmware/CoverUI/YardForce/include/model_RM-ECOW-V110.h deleted file mode 100644 index ebaa002..0000000 --- a/Firmware/CoverUI/YardForce/include/model_RM-ECOW-V110.h +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @file model_RM-ECOW-V110.h - * @author Apehaenger (joerg@ebeling.ws) - * @brief YardForce RM-ECOW-V1.1.0 model header for OpenMower https://github.com/ClemensElflein/OpenMower - * @version 0.1 - * @date 2024-06-27 - * - * @copyright Copyright (c) 2024 - * - */ -#ifndef YARDFORCE_MDL_RMECOWV110_H -#define YARDFORCE_MDL_RMECOWV110_H - -#include "model/LEDcontrol_RM-ECOW-V110.hpp" -#include "model/buttons_RM-ECOW-V110.h" -#include "model/hatch_RM-ECOW-V110.hpp" - -#ifdef MOD_EMERGENCY -#include "model/emergency_RM-ECOW-V110.h" -#endif - -// LowLevel Serial Pins -#define UART_LL_RX PA3 -#define UART_LL_TX PA2 - -#endif // YARDFORCE_MDL_RMECOWV110_H diff --git a/Firmware/CoverUI/YardForce/main.cpp b/Firmware/CoverUI/YardForce/main.cpp index 325d56b..656c4a7 100644 --- a/Firmware/CoverUI/YardForce/main.cpp +++ b/Firmware/CoverUI/YardForce/main.cpp @@ -9,26 +9,26 @@ * */ #include "include/main.h" -#include // Stock CoverUI is build now via Arduino framework (instead of HAL), which is ATM the only framework with STM32F030R8 and GD32F330R8 support -#include // Required for framework-arduinogd32 + +#include // Stock CoverUI is build now via Arduino framework (instead of HAL), which is ATM the only framework with STM32F030R8 and GD32F330R8 support +#include // Required for framework-arduinogd32 // ----- Timer ----- #ifdef MCU_STM32 #ifdef STM32F030x8 -#define TIM_SLOW TIM6 // Basic timer -#define TIM_FAST TIM17 // General purpose timer -#define TIM_EVENT TIM14 // General purpose timer -#define TIM_QUICK TIM16 // General purpose timer +#define TIM_SLOW TIM6 // Basic timer +#define TIM_FAST TIM17 // General purpose timer +#define TIM_EVENT TIM14 // General purpose timer +#define TIM_QUICK TIM16 // General purpose timer #elif defined(STM32F4xx) -#define TIM_SLOW TIM3 // General purpose timer -#define TIM_FAST TIM4 // General purpose timer -#define TIM_EVENT TIM9 // General purpose timer -#define TIM_QUICK TIM10 // General purpose timer +#define TIM_SLOW TIM3 // General purpose timer +#define TIM_FAST TIM4 // General purpose timer +#define TIM_EVENT TIM9 // General purpose timer +#define TIM_QUICK TIM10 // General purpose timer #endif #define TIM_DEFAULT_PREEMPT_PRIO TIM_IRQ_PRIO #define TIM_DEFAULT_SUB_PRIO TIM_IRQ_SUBPRIO -HardwareTimer *hwtimer(TIM_TypeDef *instance, uint32_t freq, callback_function_t callback, uint32_t preemptPriority = TIM_DEFAULT_PREEMPT_PRIO, uint32_t subPriority = TIM_DEFAULT_SUB_PRIO) -{ +HardwareTimer *hwtimer(TIM_TypeDef *instance, uint32_t freq, callback_function_t callback, uint32_t preemptPriority = TIM_DEFAULT_PREEMPT_PRIO, uint32_t subPriority = TIM_DEFAULT_SUB_PRIO) { HardwareTimer *Timer = new HardwareTimer(instance); Timer->setOverflow(freq, HERTZ_FORMAT); Timer->setInterruptPriority(preemptPriority, subPriority); @@ -36,12 +36,11 @@ HardwareTimer *hwtimer(TIM_TypeDef *instance, uint32_t freq, callback_function_t Timer->resume(); return Timer; } -#else // GD32 +#else // GD32 #define TIM_SLOW TIMER16 #define TIM_FAST TIMER15 #define TIM_QUICK TIMER14 -HardwareTimer *hwtimer(uint32_t instance, uint32_t freq, timerCallback_t callback, uint32_t preemptPriority = 0, uint32_t subPriority = 0) -{ +HardwareTimer *hwtimer(uint32_t instance, uint32_t freq, timerCallback_t callback, uint32_t preemptPriority = 0, uint32_t subPriority = 0) { HardwareTimer *Timer = new HardwareTimer(instance); Timer->setPeriodTime(freq, FORMAT_HZ); // FIXME: GD's HW timer class doesn't has INT preemption- and sub- priorities implemented yet. Will become an issue latest with GD32 & LVGL! @@ -57,16 +56,19 @@ uint32_t alive_cycle_next = 0; #define TIM_QUICK_FREQUENCY 200 // Hz #define TIM_QUICK_PERIOD_MS (1.0 / TIM_QUICK_FREQUENCY * 1000) // Milliseconds -HardwareTimer *timer_slow; // Used for blink-slow LEDs and magic buttons -HardwareTimer *timer_fast; // Used for blink-fast LEDs -#ifdef YARDFORCE_DISPLAY_HPP -HardwareTimer *timer_event; // Used for lv_timer_handler() and LED/Value to display logic conversion +HardwareTimer *timer_slow; // Used for blink-slow LEDs and magic buttons +HardwareTimer *timer_fast; // Used for blink-fast LEDs +#ifdef YARDFORCE_ABC_DISPLAY_HPP +HardwareTimer *timer_event; // Used for lv_timer_handler() and LED/Value to display logic conversion #endif -HardwareTimer *timer_quick; // Button debouncer and LED sequences +HardwareTimer *timer_quick; // Button debouncer and LED sequences // Forward declaration, see ../main.cpp extern void core1(); extern void getDataFromBuffer(); +#ifdef YARDFORCE_EMERGENCY_HPP +extern Emergency emergency; +#endif void timer_slow_callback_wrapper(); void timer_fast_callback_wrapper(); @@ -79,13 +81,12 @@ Rain rain; #endif #ifdef MCU_STM32 -HardwareSerial serial_ll(UART_LL_RX, UART_LL_TX); // Serial connection to LowLevel MCU, JP2 Pin 1+3 -#else // MCU_GD32 -HardwareSerial serial_ll((uint8_t)UART_LL_RX, (uint8_t)UART_LL_TX, 1); // Serial connection to LowLevel MCU, J6/JP2 Pin 1+3 +HardwareSerial serial_ll(UART_LL_RX, UART_LL_TX); // Serial connection to LowLevel MCU, JP2 Pin 1+3 +#else // MCU_GD32 +HardwareSerial serial_ll((uint8_t)UART_LL_RX, (uint8_t)UART_LL_TX, 1); // Serial connection to LowLevel MCU, J6/JP2 Pin 1+3 #endif -void setup() -{ +void setup() { #ifdef MDL_RMECOWV100 // RM-ECOW-V1.0.0 might have a populated ESP-WROOM-02 module. // Disable it, because one might have made MOD_HALL but flash or test another FW variant @@ -93,36 +94,30 @@ void setup() digitalWrite(PF7, LOW); // Disable ESP-WROOM-02 module so that it's GPIOs doesn't collide with ours #endif - leds.setup(); - buttons.setup(); -#ifdef YARDFORCE_DISPLAY_HPP +#ifdef YARDFORCE_ABC_DISPLAY_HPP if (!display.init()) - display.set_backlight(LED_blink_fast, 60000); // TODO: Make some better assert handling than 60 sec. fast blink + display.set_backlight(LED_blink_fast, 60000); // TODO: Make some better assert handling than 60 sec. fast blink else display.set_backlight(); #endif -#ifdef YARDFORCE_EMERGENCY_HPP - emergency.setup(); -#endif // We've hardware timer on mass, let's use them. - timer_slow = hwtimer(TIM_SLOW, 2, timer_slow_callback_wrapper, 30); // 2Hz (500ms) timer, used for LED-blink-slow and magic buttons - timer_fast = hwtimer(TIM_FAST, 10, timer_fast_callback_wrapper, 20); // 10Hz (100ms) timer, used for LED-blink-fast -#ifdef YARDFORCE_DISPLAY_HPP - timer_event = hwtimer(TIM_EVENT, 100, timer_event_callback_wrapper, 10); // 100Hz (10ms) timer, used for displays lv_timer_handler() and LED-2-Display logic + timer_slow = hwtimer(TIM_SLOW, 2, timer_slow_callback_wrapper, 30); // 2Hz (500ms) timer, used for LED-blink-slow and magic buttons + timer_fast = hwtimer(TIM_FAST, 10, timer_fast_callback_wrapper, 20); // 10Hz (100ms) timer, used for LED-blink-fast +#ifdef YARDFORCE_ABC_DISPLAY_HPP + timer_event = hwtimer(TIM_EVENT, 100, timer_event_callback_wrapper, 10); // 100Hz (10ms) timer, used for displays lv_timer_handler() and LED-2-Display logic #endif // Don't increase value of this timers preemptPriority parameter higher than the default, // as this timer callback also handles HardwareSerial as well as button-debouncer, // which would mess expected processing of those. - timer_quick = hwtimer(TIM_QUICK, TIM_QUICK_FREQUENCY, timer_quick_callback_wrapper); // 200Hz (5ms) timer, used for Buttons debouncer and LED- sequences + timer_quick = hwtimer(TIM_QUICK, TIM_QUICK_FREQUENCY, timer_quick_callback_wrapper); // 200Hz (5ms) timer, used for Buttons debouncer and LED- sequences serial_ll.begin(115200); delay(100); // Some required stupid delay, dunno why :-/ // "Hi there" and jammed button mounting detection - do - { + do { // LED blink to say it's alive // (this processing delay is also required to get the debouncer filled with a consistent state (NUM_BUTTON_STATES * 5ms) delay(leds.boot_animation() + 500); @@ -134,36 +129,30 @@ void setup() * @brief Stupid timer callback wrapper to work-around callback_function_t and timerCallback_t framework-arduino differences. * Also, framework-arduinogd32 implementation doesn't support callback arguments nor std::bind and thus no ptr to member functionality! */ -void timer_slow_callback_wrapper() -{ +void timer_slow_callback_wrapper() { leds.blink_timer_elapsed(LED_state::LED_blink_slow); -#ifdef HAS_MAGIC_BUTTONS - magic_buttons(); -#endif #ifdef MOD_RAIN rain.process(); #endif } -void timer_event_callback_wrapper() -{ -#ifdef YARDFORCE_DISPLAY_HPP +void timer_event_callback_wrapper() { +#ifdef YARDFORCE_ABC_DISPLAY_HPP display.loop_low_prio(); #endif } -void timer_fast_callback_wrapper() -{ +void timer_fast_callback_wrapper() { #ifdef YARDFORCE_EMERGENCY_HPP emergency.periodic_send(); #endif + buttons.handle_sys_req(); leds.blink_timer_elapsed(LED_state::LED_blink_fast); } -void timer_quick_callback_wrapper() -{ +void timer_quick_callback_wrapper() { getDataFromBuffer(); -#ifdef YARDFORCE_DISPLAY_HPP +#ifdef YARDFORCE_ABC_DISPLAY_HPP display.tick_inc(TIM_QUICK_PERIOD_MS); #endif #ifdef YARDFORCE_EMERGENCY_HPP @@ -177,58 +166,50 @@ void timer_quick_callback_wrapper() * Some dump OM wrapper for not polluting original code to much * ****************************************************************/ -bool uart_is_readable(HardwareSerial *Serial) -{ +bool uart_is_readable(HardwareSerial *Serial) { return Serial->available(); } -void uart_putc(HardwareSerial *Serial, uint8_t c) -{ +void uart_putc(HardwareSerial *Serial, uint8_t c) { Serial->write(c); } -void Force_LED_off(uint8_t led_num, bool force) -{ - leds.force_off(led_num, force); // This only effect blink states +void Force_LED_off(uint8_t led_num, bool force) { + leds.force_off(led_num, force); // This only effect blink states } -void buzzer_program_put_words(PIO pio, unsigned int sm, uint32_t repeat, uint32_t duration, uint32_t gap) -{ +void buzzer_program_put_words(PIO pio, unsigned int sm, uint32_t repeat, uint32_t duration, uint32_t gap) { // YFC500 doesn't has (not yet?) a buzzer on CoverUI } // ----- YardForce specific main loop, mainly does the same as ../main.cpp::core1() unsigned int last_button_id = 0; unsigned int last_button_cnt; -void loop() -{ +void loop() { // Scan the buttons in the same order as original OM FW does - for (auto const &it : buttons.kBtnDefByNumMap) // Loop over Button-Num -> button pin map + for (auto const &it : buttons.kBtnDefByNumMap) // Loop over Button-Num -> button pin map { - uint32_t start = millis(); // start press_timeout measurement - if (buttons.is_pressed(it.first)) - { + uint32_t start = millis(); // start press_timeout measurement + if (buttons.is_pressed(it.first)) { if (it.first != last_button_id) last_button_cnt = 0; last_button_id = it.first; int8_t led_num = buttons.get_led(last_button_id); - if (led_num >= 0) + if (led_num > BTN_HAS_NO_LED) Force_LED_off(led_num, true); - while (buttons.is_pressed(it.first) && (millis() - start) < 1000) // wait for button released but max. 1000ms + while (buttons.is_pressed(it.first) && (millis() - start) < 1000) // wait for button released but max. 1000ms ; - if (buttons.is_pressed(it.first)) // Still pressed + if (buttons.is_pressed(it.first)) // Still pressed { - if (last_button_cnt < 2) - { + if (last_button_cnt < 2) { last_button_cnt++; if (led_num >= 0) for (int i = 0; i < last_button_cnt; i++) leds.identify(led_num); } - } - else // Button released + } else // Button released { -#ifdef YARDFORCE_HATCH_HPP +#ifdef YARDFORCE_ABC_HATCH_HPP last_button_id = hatch.handle_button(last_button_id, last_button_cnt); #endif if (last_button_id) @@ -242,7 +223,7 @@ void loop() } } -#ifdef YARDFORCE_HATCH_HPP +#ifdef YARDFORCE_ABC_HATCH_HPP hatch.process_queued(); #endif diff --git a/Firmware/CoverUI/YardForce/model/C500/LEDcontrol.hpp b/Firmware/CoverUI/YardForce/model/C500/LEDcontrol.hpp new file mode 100644 index 0000000..347bb20 --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/C500/LEDcontrol.hpp @@ -0,0 +1,81 @@ +/** + * @file C500/LEDcontrol.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce RM-ECOW-V1x LED controller model for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.3 + * @date 2024-10-02 + * + * @copyright Copyright (c) 2023, 2024 + * + */ +#ifndef YARDFORCE_C500_LEDCONTROL_HPP +#define YARDFORCE_C500_LEDCONTROL_HPP + +#include + +#include "../../include/LEDcontrol.hpp" + +#define LED_ANIMATE_DELAY 15 // Delay (ms) between LEDs of animate sequence (boot/power-on anim) + +// These constants should be declared i.e. in assembly.cpp +extern const uint8_t kBase10Leds[]; + +class LEDcontrolC500 : public LEDcontrol { + public: + LEDcontrolC500(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds, void (LEDcontrol::*t_set_base10_leds_callback)(char digit)) : LEDcontrol(t_kPinByLedNumPtr, t_kNumLeds, t_set_base10_leds_callback) {}; + + /** + * @brief Animate sequence handler. Has to be started by sequence_start() + */ + void sequence_animate_handler() override { + unsigned int step = seq_get_next_step_(LED_ANIMATE_DELAY); // Animation sequence runs in 15ms steps + + if (step == 0) // Next sequence step not reached now + return; + else if (step >= 1 && step <= kNumLeds) // LED on + { + set(kNumLeds - step, LED_state::LED_on, false); + return; + } else if (step >= (kNumLeds + 1) && step <= (2 * kNumLeds)) // LED off + { + set((2 * kNumLeds) - step, LED_state::LED_off, false); + return; + } else { + seq_start_tick_ = 0; // Sequence end + set(led_states_bin_); // Restore states + return; + } + } + + /** + * @brief A quick boot/power-on animation, also used as jammed button indicator + * + * @return unsigned int ms how long it will take to play + */ + unsigned int boot_animation() override { + sequence_start(&LEDcontrol::sequence_animate_handler); + return ((kNumLeds + 1) * LED_ANIMATE_DELAY); + } + + /** + * @brief Set base10 related LEDs for the given (numeric) character + * + * @param digit numeric character + */ + void set_base10_leds(char digit) override { + for (uint8_t bit = 0; bit <= 6; bit++) // We've 6 LEDs for base10 number representation + { + bool on = (kBase10Leds[digit - '0'] >> bit) & 0b1; + unsigned int led = bit + 12; + set(bit + 4, on ? LED_state::LED_on : LED_state::LED_off, false); + } + } + + void force_off_num_seq_leds(bool force) override { + force_off(LED_NUM_LIFTED, force); // Num change signalling LED + for (unsigned int i = 4; i <= 10; i++) // Base10 related LEDs + force_off(i, force); + } +}; + +#endif // YARDFORCE_C500_LEDCONTROL_HPP diff --git a/Firmware/CoverUI/YardForce/model/C500/assembly.cpp b/Firmware/CoverUI/YardForce/model/C500/assembly.cpp new file mode 100644 index 0000000..c21989d --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/C500/assembly.cpp @@ -0,0 +1,96 @@ +/** + * @file C500/assembly.cpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce RM-ECOW-V1.0.0 CoverUI Assembly for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2024-10-01 + * + * @copyright Copyright (c) 2024 + * + */ +#include "assembly.hpp" + +#include "../../include/Emergency.hpp" +#include "LEDcontrol.hpp" + +// ----- LEDs ----- + +const uint32_t kPinByLedNum[] = { + // Order derived from LowLevel "enum LED_id" + // C500 + // Use LED num Original Alternative + LED_PIN_CHARGE, // 0 + LED_PIN_BAT, // 1 + LED_PIN_WIRE, // 2 + LED_PIN_LIFTED, // 3 + LED_PIN_SUN, // 4 SUN(6) + LED_PIN_SAT, // 5 SAT(5) + LED_PIN_FRI, // 6 FRI(4) + LED_PIN_THU, // 7 THU(3) + LED_PIN_WED, // 8 WED(2) + LED_PIN_TUE, // 9 TUE(1) + LED_PIN_MON, // 10 MON(0) + LED_PIN_LOCK, // 11 + LED_PIN_S2, // 12 digit 5 + LED_PIN_S1, // 13 digit 4 + LED_PIN_8HR, // 14 8HR digit 3 + LED_PIN_6HR, // 15 6HR digit 2 + LED_PIN_4HR, // 16 4HR digit 1 + LED_PIN_2HR, // 17 2HR digit 0 + LED_PIN_REAR // 18 +}; + +// Numeric (base10) representation of LEDs. +// I.e. Digit 0 = 4H(0), ... Then S2(5) + S1(4) = 9. +// +// |------- Bit 6 = MON(0) +// ||------ Bit 5 = TUE(1) +// |||----- Bit 4 = WED(2) +// ||||---- Bit 3 = THU(3) +// |||||--- Bit 2 = FRI(4) +// ||||||-- Bit 1 = SAT(5) +// |||||||- Bit 0 = SUN(6) +const uint8_t kBase10Leds[] = { + 0b1000000, // = 0 + 0b0100000, + 0b0010000, + 0b0001000, + 0b0000100, + 0b0000010, + 0b0000001, + 0b0100001, + 0b0010001, + 0b0001001}; // = 9 + +// Main LED controller object +LEDcontrolC500 leds(kPinByLedNum, sizeof(kPinByLedNum) / sizeof(uint32_t), &LEDcontrol::set_base10_leds); + +// ----- Buttons ----- + +// Map logic button number to pin. Use the same order as in OM FW so that they get scanned in a comparable order! +const std::map kBtnDefByButtonNumMap = { + {BTN_CLK_NUM, {BTN_CLK_PIN, -1}}, + {BTN_HOME_NUM, {BTN_HOME_PIN, -1}}, + {BTN_PLAY_NUM, {BTN_PLAY_PIN, -1}}, + {BTN_S1_NUM, {BTN_S1_PIN, 13}}, + {BTN_S2_NUM, {BTN_S2_PIN, 12}}, + {BTN_LOCK_NUM, {BTN_LOCK_PIN, 11}}, + {BTN_OK_NUM, {BTN_OK_PIN, -1}}, + {BTN_SUN_NUM, {BTN_SUN_PIN, 4}}, + {BTN_MON_NUM, {BTN_MON_PIN, 10}}, + {BTN_TUE_NUM, {BTN_TUE_PIN, 9}}, + {BTN_WED_NUM, {BTN_WED_PIN, 8}}, + {BTN_THU_NUM, {BTN_THU_PIN, 7}}, + {BTN_FRI_NUM, {BTN_FRI_PIN, 6}}, + {BTN_SAT_NUM, {BTN_SAT_PIN, 5}}}; + +ButtonsC500 buttons(kBtnDefByButtonNumMap); + +// ----- Emergency ----- +const Emergency::PinStateDef kEmergencyPinStateDefs[] = { + {PIN_HALL_STOP_WHITE, INPUT, Emergency_state::Emergency_stop1}, + {PIN_HALL_STOP_YELLOW, INPUT, Emergency_state::Emergency_stop2}, + {PIN_HALL_WHEEL_RED, INPUT, Emergency_state::Emergency_lift1}, + {PIN_HALL_WHEEL_BLUE, INPUT, Emergency_state::Emergency_lift2}}; + +Emergency emergency(kEmergencyPinStateDefs, sizeof(kEmergencyPinStateDefs) / sizeof(Emergency::PinStateDef)); diff --git a/Firmware/CoverUI/YardForce/model/C500/assembly.hpp b/Firmware/CoverUI/YardForce/model/C500/assembly.hpp new file mode 100644 index 0000000..bccb7b9 --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/C500/assembly.hpp @@ -0,0 +1,98 @@ +/** + * @file C500/assembly.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce Classic500 CoverUI Assembly for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2024-10-02 + * + * @copyright Copyright (c) 2024 + * + */ +#ifndef YARDFORCE_MDL_C500_ASSEMBLY_HPP +#define YARDFORCE_MDL_C500_ASSEMBLY_HPP + +#include "../../include/Buttons.hpp" +#include "../../include/main.h" +#include "LEDcontrol.hpp" + +#ifdef MOD_EMERGENCY +#include "../../include/Emergency.hpp" +#endif + +// ***** LEDs ***** +// 1st row: 2,4,6,8HR +#define LED_PIN_2HR PA4 +#define LED_PIN_4HR PA5 +#define LED_PIN_6HR PA6 +#define LED_PIN_8HR PA7 +// 2nd row: S1, S2, LOCK +#define LED_PIN_S1 PA0 +#define LED_PIN_S2 PA1 +#define LED_PIN_LOCK PC4 +// 3rd row: Mon-Sun +#define LED_PIN_MON PA15 +#define LED_PIN_TUE PC10 +#define LED_PIN_WED PC11 +#define LED_PIN_THU PC12 +#define LED_PIN_FRI PD2 +#define LED_PIN_SAT PB3 +#define LED_PIN_SUN PB4 +// 4th row: Lifted, Wire, Bat, Charge +#define LED_PIN_LIFTED PC0 +#define LED_PIN_WIRE PC1 +#define LED_PIN_BAT PC2 +#define LED_PIN_CHARGE PC3 +// Backside +#define LED_PIN_REAR PB0 + +// ***** Buttons ***** +#define BTN_CLK_PIN PF4 +#define BTN_OK_PIN PF5 +#define BTN_S1_PIN PB2 +#define BTN_S2_PIN PB10 +#define BTN_LOCK_PIN PB11 +#define BTN_MON_PIN PB12 +#define BTN_TUE_PIN PB13 +#define BTN_WED_PIN PB14 +#define BTN_THU_PIN PB15 +#define BTN_FRI_PIN PC6 +#define BTN_SAT_PIN PC7 +#define BTN_SUN_PIN PC8 +#define BTN_PLAY_PIN PA11 +#define BTN_HOME_PIN PA12 + +// ***** Emergency ***** +#define PIN_HALL_STOP_WHITE PC5 +#define PIN_HALL_STOP_YELLOW PB6 +#define PIN_HALL_WHEEL_RED PB7 +#define PIN_HALL_WHEEL_BLUE PB8 + +extern LEDcontrolC500 leds; + +class ButtonsC500 : public Buttons { + public: + ButtonsC500(const std::map &t_kBtnDefByNumMap) : Buttons(t_kBtnDefByNumMap) {}; + + /** + * @brief Check if one of the internal button functionality got pressed and do it's function. + * OK + CLK = Display FW version + * OK + SUN = LED animation + */ + void handle_sys_req() override { + if (!is_pressed(BTN_OK_NUM)) + return; + + if (is_pressed(BTN_SUN_NUM)) + leds.sequence_start(&LEDcontrol::sequence_animate_handler); + else if (is_pressed(BTN_CLK_NUM)) + leds.show_num(FIRMWARE_VERSION); + return; + }; +}; +extern ButtonsC500 buttons; + +// LowLevel Serial Pins +#define UART_LL_RX PA3 +#define UART_LL_TX PA2 + +#endif // YARDFORCE_MDL_C500_ASSEMBLY_HPP diff --git a/Firmware/CoverUI/YardForce/model/RM-EC3-V11/Display.cpp b/Firmware/CoverUI/YardForce/model/RM-EC3-V11/Display.cpp new file mode 100644 index 0000000..b38189c --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/RM-EC3-V11/Display.cpp @@ -0,0 +1,329 @@ +/** + * @file Display_RM-EC3-V11.cpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce CoverUI RM-EC3-V1.1 display driver class for OpenMower https://github.com/ClemensElflein/OpenMower + * This JLX25664 dispay is 4-level (2 Bit) gray scale capable, but for ease of development, it's driven as monochrome display. + * @version 0.1 + * @date 2023-11-27 + * + * @copyright Copyright (c) 2023 + */ +#include "Display.hpp" + +#include + +#include "../../include/main.h" +#include "../../include/subscription.h" + +// C images +LV_IMG_DECLARE(OM_Logo_120x54x1); +LV_IMG_DECLARE(OM_Wordmark_240x35x1); + +namespace yardforce { +namespace display { + +static controller::ST75256 st75256; // Controller driver + +// LVGL buffers +static lv_disp_draw_buf_t lv_disp_buf; +static lv_color_t lv_buf_1[ST75256_DISPLAY_WIDTH * LVGL_BUFFER_MULTIPLIER]; +static lv_color_t lv_buf_2[ST75256_DISPLAY_WIDTH * LVGL_BUFFER_MULTIPLIER]; + +// Status Screen Widgets +lvgl::WidgetLedSymbol *v_led_heartbeat, *v_led_ros, + *v_led_emergency_wheel, *v_led_emergency, *v_led_emergency_stop, + *v_led_bat, *v_led_gps, *v_led_charge, *v_led_power; +lvgl::WidgetBar *bar_gps, *bar_bat; +lvgl::WidgetTextTicker *text_ticker_status; + +bool main_screen_active = false; // Initialized and active +bool last_docked_state_; // true = docked, false = undocked + +bool DisplayRMEC3V11::init() { + // Init LCD display controller + st75256.init(); + + // Init LVGL + lv_init(); + lv_disp_drv_init(&lv_disp_drv); // Basic LVGL display driver initialization + lv_disp_drv.draw_buf = &lv_disp_buf; // Set an initialized buffer + lv_disp_drv.rounder_cb = rounder_cb; // Round x coordinated so that it fit for our 3 RGB pixel/per dot display + lv_disp_drv.flush_cb = flush_cb; // Set a flush callback to draw to the display + lv_disp_drv.hor_res = ST75256_DISPLAY_WIDTH; // Set the horizontal resolution in pixels + lv_disp_drv.ver_res = ST75256_DISPLAY_HEIGHT; // Set the vertical resolution in pixels + lv_disp_draw_buf_init(&lv_disp_buf, lv_buf_1, lv_buf_2, ST75256_DISPLAY_WIDTH * LVGL_BUFFER_MULTIPLIER); // Initialize `disp_buf` with the buffer(s) + lv_disp_t *disp; + disp = lv_disp_drv_register(&lv_disp_drv); // Register the driver and save the created display objects + lv_obj_set_style_bg_color(lv_scr_act(), lv_color_white(), LV_PART_MAIN); // No background color + + openmower_anim(); + // mainScreen(); + + return true; +} + +/** + * @brief Rounder callback will round the display area to a multiple of 8, on y axis (because one DDRAM byte are 8 y-pixels (page), per column) + * + * @param disp_drv + * @param area + */ +void DisplayRMEC3V11::rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) { + area->y1 = area->y1 - (area->y1 % 8); // Round down to neares multiple of 8 + area->y2 = (area->y2 + 8) - (area->y2 % 8) - 1; // Round up to nearest multiple of 8, minus -1 +} + +/** + * @brief Flush display buffer to display controller. + * + * @param disp_drv + * @param area + * @param t_color_p + */ +void DisplayRMEC3V11::flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *t_color_p) { + size_t cols = (area->x2 - area->x1) + 1; // Num of columns for this flush area + uint8_t page_pixel_buffer[cols]; // Store 8 y pixel (LSB = top) for area of x + uint8_t y_shift = 0; + + st75256.send_ctrl(0x30); // EXT1=0, EXT0=0 + st75256.set_column_address(area->x1, area->x2); + st75256.set_page_address(area->y1 / 8, (area->y2 - 7) / 8); // ATTENTION: Will only work with a correct rounder_cb() + st75256.send_ctrl(0b01011100); // [10] Write Data + + for (size_t y = area->y1; y <= area->y2; y++) { + for (size_t x = 0; x < cols; x++) { + t_color_p->full ? page_pixel_buffer[x] &= ~(1 << y_shift) : page_pixel_buffer[x] |= (1 << y_shift); + t_color_p++; + } + if (y_shift < 7) { + y_shift++; + continue; + } + st75256.send_data(page_pixel_buffer, cols); + y_shift = 0; + } + lv_disp_flush_ready(disp_drv); +} + +void DisplayRMEC3V11::set_undocked() { + v_led_power->set(LED_off); + v_led_charge->set(LED_off); + bar_bat->set_range(BATT_ABS_MIN, BATT_ABS_MAX); + bar_bat->bar_label = FA_SYMBOL_BATTERY " %d V"; + last_docked_state_ = false; +} + +void DisplayRMEC3V11::mainScreen() { + lv_obj_clean(lv_scr_act()); + + // On the left side of the status bar we do have functional status symbols like heartbeat and ROS + v_led_ros = new lvgl::WidgetLedSymbol(FA_SYMBOL_ROS, LV_ALIGN_TOP_LEFT, 0, -1); // Leftmost + + // In the middle, we do have emergencies + v_led_emergency = new lvgl::WidgetLedSymbol(FA_SYMBOL_EMERGENCY, LV_ALIGN_TOP_MID, 0, -1); // Centered + v_led_emergency_wheel = new lvgl::WidgetLedSymbol(FA_SYMBOL_EMERGENCY_WHEEL, LV_ALIGN_TOP_MID, -14 - TOP_STATUS_BAR_GAP_PX - 2, -1); // Left of centered + // TODO: if next level LL proto + // v_led_heartbeat = new WidgetLedSymbol(FA_SYMBOL_HEARTBEAT, LV_ALIGN_TOP_MID, -(2 * 14) - (2 * TOP_STATUS_BAR_GAP_PX) - 2, -1); // 2nd left of centered + v_led_emergency_stop = new lvgl::WidgetLedSymbol(FA_SYMBOL_EMERGENCY_STOP, LV_ALIGN_TOP_MID, 14 + TOP_STATUS_BAR_GAP_PX, -1); // Right of centered + + // On the right side, mowing status like, charging, docking, ... + v_led_power = new lvgl::WidgetLedSymbol(FA_SYMBOL_PLUG, LV_ALIGN_OUT_TOP_RIGHT, (ST75256_DISPLAY_WIDTH - (1 * 14)), -1); // Rightmost + v_led_charge = new lvgl::WidgetLedSymbol(FA_SYMBOL_CHARGE, LV_ALIGN_OUT_TOP_RIGHT, (ST75256_DISPLAY_WIDTH - (2 * 14) - TOP_STATUS_BAR_GAP_PX), -1); + v_led_gps = new lvgl::WidgetLedSymbol(FA_SYMBOL_GPS1, LV_ALIGN_OUT_TOP_RIGHT, (ST75256_DISPLAY_WIDTH - (3 * 14) - (2 * TOP_STATUS_BAR_GAP_PX)), -1); + v_led_bat = new lvgl::WidgetLedSymbol(FA_SYMBOL_BATTERY, LV_ALIGN_OUT_TOP_RIGHT, (ST75256_DISPLAY_WIDTH - (4 * 14) - (3 * TOP_STATUS_BAR_GAP_PX)), -1); + + // GPS & Battery bars + bar_gps = new lvgl::WidgetBar(FA_SYMBOL_GPS2 " %d %%", LV_ALIGN_TOP_LEFT, 0, 19, (ST75256_DISPLAY_WIDTH / 2) - 1, 21); + bar_bat = new lvgl::WidgetBar(FA_SYMBOL_BATTERY " %d %%", LV_ALIGN_TOP_RIGHT, 0, 19, (ST75256_DISPLAY_WIDTH / 2) - 1, 21); + + // Mower status text (ticker) + text_ticker_status = new lvgl::WidgetTextTicker(LV_ALIGN_TOP_MID, 0, 42, ST75256_DISPLAY_WIDTH, 40); + + // Set defined state + set_undocked(); + bar_bat->set_value(BATT_ABS_MIN); + + main_screen_active = true; +} + +void DisplayRMEC3V11::anim_x_cb(void *var, int32_t v) { + lv_obj_set_x((lv_obj_t *)var, v); +} + +void DisplayRMEC3V11::openmower_anim_delay() { + delay(1000); + mainScreen(); +} + +// Probably static void +void DisplayRMEC3V11::openmower_anim() { + const uint8_t wm_start_offset = 20; + main_screen_active = false; + + lv_obj_clean(lv_scr_act()); + + // Mower Logo - img_logo + lv_obj_t *img_logo = lv_img_create(lv_scr_act()); + lv_img_set_src(img_logo, &OM_Logo_120x54x1); + lv_obj_align(img_logo, LV_ALIGN_RIGHT_MID, 0, 0); + + // OpenMower Wordmark - img_wordmark + lv_obj_t *img_wordmark = lv_img_create(lv_scr_act()); + lv_img_set_src(img_wordmark, &OM_Wordmark_240x35x1); + lv_obj_align(img_wordmark, LV_ALIGN_RIGHT_MID, OM_Wordmark_240x35x1.header.w + wm_start_offset, 0); + + // Anim of logo + lv_anim_t al; + lv_anim_init(&al); + lv_anim_set_var(&al, img_logo); + lv_anim_set_values(&al, 0, -ST75256_DISPLAY_WIDTH); + lv_anim_set_time(&al, 1800); + lv_anim_set_delay(&al, 1200); + lv_anim_set_exec_cb(&al, (lv_anim_exec_xcb_t)anim_x_cb); + lv_anim_set_path_cb(&al, lv_anim_path_ease_in); + // lv_anim_set_repeat_count(&al, LV_ANIM_REPEAT_INFINITE); + lv_anim_start(&al); + + // Anim of Wordmark + lv_anim_t aw; + lv_anim_init(&aw); + lv_anim_set_var(&aw, img_wordmark); + lv_anim_set_values(&aw, OM_Wordmark_240x35x1.header.w + wm_start_offset, -((ST75256_DISPLAY_WIDTH - OM_Wordmark_240x35x1.header.w) / 2)); + lv_anim_set_time(&aw, 1800); + lv_anim_set_delay(&aw, 1900); + lv_anim_set_exec_cb(&aw, (lv_anim_exec_xcb_t)anim_x_cb); + lv_anim_set_path_cb(&aw, lv_anim_path_ease_in_out); + lv_anim_set_deleted_cb(&aw, (lv_anim_deleted_cb_t)openmower_anim_delay); // Set a callback to indicate when the animation is deleted (idle) + // lv_anim_set_repeat_count(&aw, LV_ANIM_REPEAT_INFINITE); + lv_anim_start(&aw); +} + +void DisplayRMEC3V11::loop_low_prio() { + AbstractDisplay::loop_low_prio(); + + // LEDs & Buttons to main status screen conversion + if (!main_screen_active) + return; // Still in OM anim + + char status_ticker[STATUS_TICKER_LENGTH] = ""; + + // GPS + if (subscription::recv_hl_state.gps_quality < 25) + v_led_gps->set(LED_on); + else if (subscription::recv_hl_state.gps_quality < 50) + v_led_gps->set(LED_blink_fast); + else if (subscription::recv_hl_state.gps_quality < 75) + v_led_gps->set(LED_blink_slow); + else + v_led_gps->set(LED_off); + bar_gps->set_value(subscription::recv_hl_state.gps_quality); + + // V-Battery vLED + if (subscription::recv_ll_status.v_battery >= (BATT_EMPTY + 2.0f)) + v_led_bat->set(LED_off); + else + v_led_bat->set(LED_on); + + // Docked (Plug) & Charging (charge-station) + if (subscription::recv_ll_status.v_charge > 20.0f) // Docked + { + if (!last_docked_state_) { + v_led_power->set(LED_on); + bar_bat->set_range(100, 1100); + bar_bat->bar_label = FA_SYMBOL_CHARGE " %d mA"; + set_backlight(); + last_docked_state_ = true; + } + bar_bat->set_value(subscription::recv_ll_status.charging_current * 1000); + + if (subscription::recv_ll_status.charging_current < 0.15f) + v_led_charge->set(LED_off); + else if (subscription::recv_ll_status.charging_current >= 0.15f && subscription::recv_ll_status.charging_current <= 0.8f) + v_led_charge->set(LED_blink_slow); + else if (subscription::recv_ll_status.charging_current > 0.8f) + v_led_charge->set(LED_blink_fast); + } else // Undocked + { + if (last_docked_state_) { + set_undocked(); + } + bar_bat->set_value(subscription::recv_ll_status.v_battery); + } + + // HL Mode & SubMode + switch (subscription::recv_hl_state.current_mode & 0b111111) { + case HighLevelMode::MODE_IDLE: + v_led_ros->set(LED_on); + strncpy(status_ticker, "Idle", STATUS_TICKER_LENGTH); + break; + case HighLevelMode::MODE_AUTONOMOUS: + v_led_ros->set(LED_blink_slow); + if ((subscription::recv_hl_state.current_mode >> 6) & 0b01) + strncpy(status_ticker, "Docking", STATUS_TICKER_LENGTH); + else if ((subscription::recv_hl_state.current_mode >> 6) & 0b10) + strncpy(status_ticker, "Undocking", STATUS_TICKER_LENGTH); + else + strncpy(status_ticker, "Mowing", STATUS_TICKER_LENGTH); + break; + case HighLevelMode::MODE_RECORDING: + v_led_ros->set(LED_blink_fast); + if ((subscription::recv_hl_state.current_mode >> 6) & 0b01) + strncpy(status_ticker, "Record area outline", STATUS_TICKER_LENGTH); + else if ((subscription::recv_hl_state.current_mode >> 6) & 0b10) + strncpy(status_ticker, "Record obstacle", STATUS_TICKER_LENGTH); + break; + default: // We currently don't have a real "ROS Running" identifier. Let's use the current mode. + v_led_ros->set(LED_off); + strncpy(status_ticker, "Waiting for ROS...", STATUS_TICKER_LENGTH); + break; + } + + // ----- Most important text-states, last! ----- + + // Emergencies + static bool last_stop_button = false; + if (subscription::recv_ll_status.emergency_bitmask & LL_EMERGENCY_BITS_STOP) // Stop switch + { + v_led_emergency_stop->set(LED_blink_fast); + strncpy(status_ticker, EMERGENCY_CLEAR_TEXT, STATUS_TICKER_LENGTH); + + if (!last_stop_button) // Backlight on cover- open + set_backlight(); + last_stop_button = true; + } else { + v_led_emergency_stop->set(LED_off); + last_stop_button = false; + } + + if (subscription::recv_ll_status.emergency_bitmask & LL_EMERGENCY_BITS_LIFT) // Lifted or tilted + { + v_led_emergency_wheel->set(LED_blink_fast); + strncpy(status_ticker, EMERGENCY_CLEAR_TEXT, STATUS_TICKER_LENGTH); + } else + v_led_emergency_wheel->set(LED_off); + + if (subscription::recv_ll_status.emergency_bitmask & LL_EMERGENCY_BIT_LATCH) // Emergency latch (no LL heartbeat or emergency by ROS) + { + v_led_emergency->set(LED_blink_slow); + strncpy(status_ticker, EMERGENCY_CLEAR_TEXT, STATUS_TICKER_LENGTH); + } else + v_led_emergency->set(LED_off); + + // ----- Announcement ----- + switch (auto ann = get_anncmnt()) { + case AnncmntType::close_hatch: + sprintf(status_ticker, "Close hatch in %i sec.", ((anncmnt.timeout - millis()) / 1000) + 1); + break; + case AnncmntType::version: + sprintf(status_ticker, "Version %i", FIRMWARE_VERSION); + break; + default: + break; + } + + text_ticker_status->set_text(status_ticker); +} +} // namespace display +} // namespace yardforce + +yardforce::display::DisplayRMEC3V11 display(yardforce::display::AbstractDisplay::Config{.backlight_led_num = LED_NUM_BACKLIGHT}); \ No newline at end of file diff --git a/Firmware/CoverUI/YardForce/model/RM-EC3-V11/Display.hpp b/Firmware/CoverUI/YardForce/model/RM-EC3-V11/Display.hpp new file mode 100644 index 0000000..5878e43 --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/RM-EC3-V11/Display.hpp @@ -0,0 +1,68 @@ +/** + * @file Display_RM-EC3-V11.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce CoverUI JLX25664 display driver for NX100i OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2023-11-27 + * + * @copyright Copyright (c) 2023 + */ +#ifndef YARDFORCE_DISPLAY_RMEC3V11_HPP +#define YARDFORCE_DISPLAY_RMEC3V11_HPP + +#define ST75256_DISPLAY_WIDTH 256 +#define ST75256_DISPLAY_HEIGHT 64 + +// JLX25664G Tscyc min = 80ns = 12.5MHz +// ST32F401 = 84MHz / 8 = 10.5MHz +#define ST75256_SPI_BAUDRATEPRESCALER SPI_BAUDRATEPRESCALER_8 + +#define LVGL_BUFFER_MULTIPLIER 10 +#define LVGL_TIMER_HANDLER_PERIOD_MS 10 // 10ms lv_timer_handler() soft period + +#define TOP_STATUS_BAR_GAP_PX 5 // Amount of (gap) pixels between top status-bar icons/symbols +#define EMERGENCY_CLEAR_TEXT "Emergency! Press [OK], close hatch and stay back, to clear emergency state." + +// clang-format off +#include "../../include/ABC_Display.hpp" +#include "../../include/ST75256.hpp" +#include +#include "../../include/WidgetBar.hpp" +#include "../../include/WidgetLedSymbol.hpp" +#include "../../include/WidgetTextTicker.hpp" +#include "../../include/subscription.h" +// clang-format on + +#define BACKLIGHT_TIMEOUT_MS 120000 // 2 minutes +#define STATUS_TICKER_LENGTH 100 // TODO + +namespace yardforce { +namespace display { +class DisplayRMEC3V11 : public AbstractDisplay { + public: + DisplayRMEC3V11(Config t_config) : AbstractDisplay(t_config) {}; + + bool init() override; // Init GPIOs, comms, as well as LVGL & display + + void loop_low_prio() override; // Low priority loop for display changes. Also does lv_timer_handler() and subscription + + void openmower_anim(); + static void openmower_anim_delay(); + static void mainScreen(); + + protected: + lv_disp_drv_t lv_disp_drv; // LVGL driver + + static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area); + static void flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *t_color_p); + + static void anim_x_cb(void *var, int32_t v); + + static void set_undocked(); +}; +} // namespace display +} // namespace yardforce + +extern yardforce::display::DisplayRMEC3V11 display; + +#endif // YARDFORCE_DISPLAY_RMEC3V11_HPP \ No newline at end of file diff --git a/Firmware/CoverUI/YardForce/model/RM-EC3-V11/LEDcontrol.hpp b/Firmware/CoverUI/YardForce/model/RM-EC3-V11/LEDcontrol.hpp new file mode 100644 index 0000000..ceaa9fc --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/RM-EC3-V11/LEDcontrol.hpp @@ -0,0 +1,98 @@ +/** + * @file RM-EC3-V11/LEDcontrol.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce RM-EC3-V11 LED controller model for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2024-10-02 + * + * @copyright Copyright (c) 2023, 2024 + * + */ +#ifndef YARDFORCE_RMEC3V11_LEDCONTROL_HPP +#define YARDFORCE_RMEC3V11_LEDCONTROL_HPP + +#include + +#include "../../include/LEDcontrol.hpp" + +#define LED_ANIMATE_DELAY 50 // Delay (ms) between LEDs of animate sequence (boot/power-on anim) + +// These constants should be declared i.e. in assembly.cpp +extern const unsigned int kNumFrontLeds; +extern const unsigned int kLedAnimOrder[]; + +class LEDcontrolRMEC3V11 : public LEDcontrol { + public: + LEDcontrolRMEC3V11(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds, void (LEDcontrol::*t_set_base10_leds_callback)(char digit)) : LEDcontrol(t_kPinByLedNumPtr, t_kNumLeds, t_set_base10_leds_callback) {}; + + /** + * @brief Animate sequence handler. Has to be started by sequence_start() + */ + void sequence_animate_handler() override { + uint16_t step = seq_get_next_step_(LED_ANIMATE_DELAY); // Animation sequence runs in 15ms steps + + if (step == 0) // Next sequence step not reached now + return; + else if (step >= 1 && step <= kNumFrontLeds) { // LED on + set(kLedAnimOrder[step - 1], LED_state::LED_on, false); + return; + } else if (step >= (kNumFrontLeds + 1) && step <= (2 * kNumFrontLeds)) { // LED off + set(kLedAnimOrder[step - kNumFrontLeds - 1], LED_state::LED_off, false); + return; + } else { + seq_start_tick_ = 0; // Sequence end + set(led_states_bin_); // Restore states + return; + } + } + + void show_countdown_state(unsigned int sec, LED_state state) { + unsigned int sec_left = 5 - sec; + + for (size_t i = 1; i <= 4; i++) { + uint8_t led_num = 18 - i; + if (sec_left >= i) // Remaining secs is greater than this LED + { + set(led_num, state, false); + } else { + set(led_num, LED_off, false); + force_off(led_num, true); + } + } + }; + + /** + * @brief Countdown LED animation handler + */ + void sequence_countdown_handler() { + uint16_t step = seq_get_next_step_(100); // Animation sequence run in 1000ms steps + + if (step == 0) // Next sequence step not reached now + return; + else if (step == 1 || step == 11 || step == 21 || step == 31 || step == 41) { + show_countdown_state(((step - 1) / 10) + 1, LED_blink_fast); + return; + } else if (step < 41) { + return; + } else { + seq_start_tick_ = 0; // Sequence end + set(led_states_bin_); // Restore states + for (size_t i = 1; i <= 4; i++) + force_off(18 - i, false); + return; + } + } + + /** + * @brief A quick boot/power-on animation, also used as jammed button indicator + * + * @return unsigned int ms how long it will take to play + */ + unsigned int boot_animation() // A short boot animation which return the amount of ms it will take + { + sequence_start(&LEDcontrol::sequence_animate_handler); + return ((kNumFrontLeds + 1) * LED_ANIMATE_DELAY); + } +}; + +#endif // YARDFORCE_RMEC3V11_LEDCONTROL_HPP diff --git a/Firmware/CoverUI/YardForce/model/RM-EC3-V11/assembly.cpp b/Firmware/CoverUI/YardForce/model/RM-EC3-V11/assembly.cpp new file mode 100644 index 0000000..574f694 --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/RM-EC3-V11/assembly.cpp @@ -0,0 +1,144 @@ +/** + * @file RM-EC3-V11/assembly.cpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce RM-EC3-V1.1 CoverUI Assembly for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2024-10-02 + * + * @copyright Copyright (c) 2024 + * + */ +#include "assembly.hpp" + +#include "../../include/Emergency.hpp" +#include "Display.hpp" + +// ----- LEDs ----- + +const uint32_t kPinByLedNum[] = { + // Order derived from LowLevel "enum LED_id" + // C500 + // Use LED num Original Alternative + LED_PIN_NC, // 0 + LED_PIN_NC, // 1 + LED_PIN_NC, // 2 + LED_PIN_NC, // 3 + LED_PIN_NC, // 4 SUN + LED_PIN_NC, // 5 SAT + LED_PIN_NC, // 6 FRI + LED_PIN_NC, // 7 THU + LED_PIN_NC, // 8 WED + LED_PIN_NC, // 9 TUE + LED_PIN_NC, // 10 MON + LED_PIN_NC, // 11 + LED_PIN_S2, // 12 digit 5 + LED_PIN_S1, // 13 digit 4 + LED_PIN_NC, // 14 8HR digit 3 + LED_PIN_NC, // 15 6HR digit 2 + LED_PIN_NC, // 16 4HR digit 1 + LED_PIN_NC, // 17 2HR digit 0 + LED_PIN_NC, // 18 + LED_PIN_SETUP, // 19 + LED_PIN_BACKLIGHT // 20 +}; + +const unsigned int kLedAnimOrder[] = {13, 19, 12}; +const unsigned int kNumFrontLeds = sizeof(kLedAnimOrder) / sizeof(unsigned int); + +// Main LED controller object +LEDcontrolRMEC3V11 leds(kPinByLedNum, sizeof(kPinByLedNum) / sizeof(uint32_t), &LEDcontrol::set_base10_leds); + +// ----- Buttons ----- + +// Map logic button number to pin. Use the same order as in OM FW so that they get scanned in a comparable order! +const std::map kBtnDefByButtonNumMap = { + {BTN_HOME_NUM, {BTN_HOME_PIN, -1}}, + {BTN_PLAY_NUM, {BTN_PLAY_PIN, -1}}, + {BTN_S1_NUM, {BTN_S1_PIN, LED_NUM_S1}}, + {BTN_S2_NUM, {BTN_S2_PIN, LED_NUM_S2}}, + {BTN_SETUP_NUM, {BTN_SETUP_PIN, 19}}, + {BTN_MENU_NUM, {BTN_MENU_PIN, -1}}, + {BTN_1_NUM, {BTN_1_PIN, -1}}, + {BTN_2_NUM, {BTN_2_PIN, -1}}, + {BTN_3_NUM, {BTN_3_PIN, -1}}, + {BTN_4_NUM, {BTN_4_PIN, -1}}, + {BTN_5_NUM, {BTN_5_PIN, -1}}, + {BTN_6_NUM, {BTN_6_PIN, -1}}, + {BTN_7_NUM, {BTN_7_PIN, -1}}, + {BTN_8_NUM, {BTN_8_PIN, -1}}, + {BTN_9_NUM, {BTN_9_PIN, -1}}, + {BTN_BACK_NUM, {BTN_BACK_PIN, -1}}, + {BTN_0_NUM, {BTN_0_PIN, -1}}, + {BTN_OK_NUM, {BTN_OK_PIN, -1}}}; + +/** + * @brief Check if one of the internal button functionality got pressed and do it's function. + * MENU + 0 = Display FW version + * MENU + BACK = LED animation + */ +void ButtonsRMEC3V11::handle_sys_req() { + if (!is_pressed(BTN_MENU_NUM)) + return; + + if (is_pressed(BTN_BACK_NUM)) + display.openmower_anim(); + else if (is_pressed(BTN_0_NUM)) + display.start_anncmnt(2000, yardforce::display::AbstractDisplay::AnncmntType::version); + + return; +}; + +ButtonsRMEC3V11 buttons(kBtnDefByButtonNumMap); + +// ----- Emergency ----- + +const Emergency::PinStateDef kEmergencyPinStateDefs[] = { +#ifdef MOD_HALL + {PIN_HALL1, INPUT_PULLUP, Emergency_state::Emergency_lift1}, + {PIN_HALL2, INPUT_PULLUP, Emergency_state::Emergency_lift1}, + {PIN_HALL3, INPUT_PULLUP, Emergency_state::Emergency_lift2}, + {PIN_HALL4, INPUT_PULLUP, Emergency_state::Emergency_lift2}, +#endif +#ifdef MOD_STOP + {PIN_STOP1, INPUT_PULLUP, Emergency_state::Emergency_stop1}, + {PIN_STOP2, INPUT_PULLUP, Emergency_state::Emergency_stop2}, +#endif +}; + +Emergency emergency(kEmergencyPinStateDefs, sizeof(kEmergencyPinStateDefs) / sizeof(Emergency::PinStateDef)); + +// ----- Hatch ----- + +unsigned int HatchRMEC3V11::handle_button(unsigned int button_id, unsigned int press_cnt) { + // If backlight is off, skip first button press + if (display.check_backlight() == LED_off) { + display.set_backlight(); + return 0; // Skip handling of first button-press if backlight was off + } + display.set_backlight(); + + // Clear emergency = Enter (OK) button + if (button_id == BTN_OK_NUM) { + display.start_anncmnt(4000, yardforce::display::AbstractDisplay::AnncmntType::close_hatch); + queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec + return 0; // Don't return button now (hatch is still open) + } + + // Play & Home button + if (button_id == BTN_HOME_NUM || button_id == BTN_PLAY_NUM) { + display.start_anncmnt(4000, yardforce::display::AbstractDisplay::AnncmntType::close_hatch); + queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec + queue_button(button_id, 0, 5000); + return 0; // Don't return button now (hatch is still open) + } + + if (button_id == BTN_1_NUM) + return BTN_MON_NUM; // Volume up + + if (button_id == BTN_2_NUM) + return BTN_TUE_NUM; // Volume down + + return button_id; +} + +HatchRMEC3V11 hatch; diff --git a/Firmware/CoverUI/YardForce/model/RM-EC3-V11/assembly.hpp b/Firmware/CoverUI/YardForce/model/RM-EC3-V11/assembly.hpp new file mode 100644 index 0000000..667c069 --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/RM-EC3-V11/assembly.hpp @@ -0,0 +1,93 @@ +/** + * @file RM-EC3-V11/assembly.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce RM-EC3-V1.1 CoverUI Assembly for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2024-10-01 + * + * @copyright Copyright (c) 2024 + * + */ +#ifndef YARDFORCE_MDL_RMEC3V11_ASSEMBLY_HPP +#define YARDFORCE_MDL_RMEC3V11_ASSEMBLY_HPP + +#include "../../include/Buttons.hpp" +#include "../../include/main.h" +#include "LEDcontrol.hpp" + +// FIXME: AbstractDisplay depends on leds +extern LEDcontrolRMEC3V11 leds; +#include "Display.hpp" + +#ifdef MOD_EMERGENCY +#include "../../include/Emergency.hpp" +#endif + +// ***** LEDs ***** + +// 1st row: Lifted, Wire (WLAN), Battery empty, Charge +#define LED_PIN_S1 PC4 +#define LED_PIN_SETUP PA1 +#define LED_PIN_S2 PC5 +#define LED_PIN_BACKLIGHT PA8 +// Backside +#define LED_NUM_BACKLIGHT 20 + +// ***** Buttons ***** + +#define BTN_S1_PIN PC14 +#define BTN_S2_PIN PC13 +#define BTN_PLAY_PIN PC7 +#define BTN_HOME_PIN PC2 +#define BTN_SETUP_PIN PA0 + +#define BTN_MENU_PIN PB4 + +#define BTN_1_PIN PB8 +#define BTN_2_PIN PB5 +#define BTN_3_PIN PB3 +#define BTN_4_PIN PD2 +#define BTN_5_PIN PC12 +#define BTN_6_PIN PC11 +#define BTN_7_PIN PC10 +#define BTN_8_PIN PB14 +#define BTN_9_PIN PC6 +#define BTN_BACK_PIN PA15 +#define BTN_0_PIN PB9 +#define BTN_OK_PIN PB13 + +// ***** Emergency ***** + +#define PIN_STOP1 PC15 +#define PIN_STOP2 PB6 + +#define PIN_HALL1 PB15 +#define PIN_HALL2 PC0 +#define PIN_HALL3 PC8 +#define PIN_HALL4 PC9 + +class ButtonsRMEC3V11 : public Buttons { + public: + ButtonsRMEC3V11(const std::map &t_kBtnDefByNumMap) : Buttons(t_kBtnDefByNumMap) {}; + + void handle_sys_req() override; +}; +extern ButtonsRMEC3V11 buttons; + +// ----- Hatch ----- + +#include "../../include/ABC_Hatch.hpp" + +class HatchRMEC3V11 : public AbstractHatch { + public: + unsigned int handle_button(unsigned int button_id, unsigned int press_cnt); +}; + +extern HatchRMEC3V11 hatch; + +// ----- LowLevel Serial ----- + +#define UART_LL_RX PA3 +#define UART_LL_TX PA2 + +#endif // YARDFORCE_MDL_RMEC3V11_ASSEMBLY_HPP diff --git a/Firmware/CoverUI/YardForce/model/RM-ECOW-V100/assembly.cpp b/Firmware/CoverUI/YardForce/model/RM-ECOW-V100/assembly.cpp new file mode 100644 index 0000000..9459ab0 --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/RM-ECOW-V100/assembly.cpp @@ -0,0 +1,104 @@ +/** + * @file RM-ECOW-V100/assembly.cpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce RM-ECOW-V1.0.0 CoverUI Assembly for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2024-10-01 + * + * @copyright Copyright (c) 2024 + * + */ +#include "assembly.hpp" + +#include "../../include/Emergency.hpp" + +// ----- LEDs ----- + +const uint32_t kPinByLedNum[] = { + // Order derived from LowLevel "enum LED_id" + // C500 + // Use LED num Original Alternative + LED_PIN_CHARGE, // 0 + LED_PIN_BAT, // 1 + LED_PIN_WIRE, // 2 + LED_PIN_LIFTED, // 3 + LED_PIN_NC, // 4 SUN + LED_PIN_NC, // 5 SAT + LED_PIN_NC, // 6 FRI + LED_PIN_NC, // 7 THU + LED_PIN_NC, // 8 WED + LED_PIN_NC, // 9 TUE + LED_PIN_NC, // 10 MON + LED_PIN_LOCK, // 11 + LED_PIN_S2, // 12 digit 5 + LED_PIN_S1, // 13 digit 4 + LED_PIN_10H, // 14 8HR digit 3 + LED_PIN_8H, // 15 6HR digit 2 + LED_PIN_6H, // 16 4HR digit 1 + LED_PIN_4H, // 17 2HR digit 0 + LED_PIN_REAR, // 18 + LED_PIN_SETUP // 19 +}; + +const unsigned int kLedAnimOrder[] = {3, 2, 1, 0, 17, 16, 15, 14, 13, 19, 12, 11}; +const unsigned int kNumFrontLeds = sizeof(kLedAnimOrder) / sizeof(unsigned int); + +// Numeric (base10) representation of LEDs. +// I.e. Digit 0 = 4H(0), ... Then S2(5) + S1(4) = 9. +// +// |------ Bit 5 = 4H(0) +// ||----- Bit 4 = 6H(1) +// |||---- Bit 3 = 8H(2) +// ||||--- Bit 2 = 10H(3) +// |||||-- Bit 1 = S1(4) +// ||||||- Bit 0 = S2(5) +const uint8_t kBase10Leds[] = { + 0b100000, // = 0 + 0b010000, + 0b001000, + 0b000100, + 0b000010, + 0b000001, + 0b010001, + 0b001001, + 0b000101, + 0b000011}; // = 9 + +// Main LED controller object +LEDcontrolRMECOWV1x leds(kPinByLedNum, sizeof(kPinByLedNum) / sizeof(uint32_t), &LEDcontrol::set_base10_leds); + +// ----- Buttons ----- + +// Map logic button number to pin. Use the same order as in OM FW so that they get scanned in a comparable order! +const std::map kBtnDefByButtonNumMap = { + {BTN_HOME_NUM, {BTN_HOME_PIN, BTN_HAS_NO_LED}}, + {BTN_PLAY_NUM, {BTN_PLAY_PIN, BTN_HAS_NO_LED}}, + {BTN_S1_NUM, {BTN_S1_PIN, LED_NUM_S1}}, + {BTN_S2_NUM, {BTN_S2_PIN, LED_NUM_S2}}, + {BTN_LOCK_NUM, {BTN_LOCK_PIN, 11}}, + {BTN_4H_NUM, {BTN_4H_PIN, 17}}, + {BTN_6H_NUM, {BTN_6H_PIN, 16}}, + {BTN_8H_NUM, {BTN_8H_PIN, 15}}, + {BTN_10H_NUM, {BTN_10H_PIN, 14}}, + {BTN_SETUP_NUM, {BTN_SETUP_PIN, 19}}}; + +ButtonsRMECOWV100 buttons(kBtnDefByButtonNumMap); + +// ----- Emergency ----- +const Emergency::PinStateDef kEmergencyPinStateDefs[] = { +#ifdef MOD_HALL + {PIN_HALL1, INPUT_PULLUP, Emergency_state::Emergency_lift1}, + {PIN_HALL2, INPUT_PULLUP, Emergency_state::Emergency_lift1}, + {PIN_HALL3, INPUT_PULLUP, Emergency_state::Emergency_lift2}, + {PIN_HALL4, INPUT_PULLUP, Emergency_state::Emergency_lift2}, +#endif +#ifdef MOD_STOP + {PIN_STOP_JP5, INPUT_PULLUP, Emergency_state::Emergency_stop1}, + {PIN_STOP_JP6, INPUT_PULLUP, Emergency_state::Emergency_stop2} +#endif +}; + +Emergency emergency(kEmergencyPinStateDefs, sizeof(kEmergencyPinStateDefs) / sizeof(Emergency::PinStateDef)); + +// ----- Hatch ----- +HatchRMECOWV1x hatch; diff --git a/Firmware/CoverUI/YardForce/model/RM-ECOW-V100/assembly.hpp b/Firmware/CoverUI/YardForce/model/RM-ECOW-V100/assembly.hpp new file mode 100644 index 0000000..018752c --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/RM-ECOW-V100/assembly.hpp @@ -0,0 +1,93 @@ +/** + * @file RM-ECOW-V100/assembly.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce RM-ECOW-V1.0.0 CoverUI Assembly for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2024-10-01 + * + * @copyright Copyright (c) 2024 + * + */ +#ifndef YARDFORCE_MDL_RMECOWV100_ASSEMBLY_HPP +#define YARDFORCE_MDL_RMECOWV100_ASSEMBLY_HPP + +#include "../../include/Buttons.hpp" +#include "../../include/main.h" +#include "../RM-ECOW-V1x/LEDcontrol.hpp" + +#ifdef MOD_EMERGENCY +#include "../../include/Emergency.hpp" +#endif + +// ***** LEDs ***** +// 1st row: Lifted, Wire (WLAN), Battery empty, Charge +#define LED_PIN_LIFTED PB11 +#define LED_PIN_WIRE PB12 +#define LED_PIN_BAT PB14 +#define LED_PIN_CHARGE PB15 +// 2nd row: 4, 6, 8, 10HR +#define LED_PIN_4H PA7 +#define LED_PIN_6H PC5 +#define LED_PIN_8H PB1 +#define LED_PIN_10H PB10 +// 3rd row: S1, Setup (WLAN), S2 +#define LED_PIN_S1 PA5 +#define LED_PIN_SETUP PF5 +#define LED_PIN_S2 PC7 +// 4th row: Lock +#define LED_PIN_LOCK PA1 +// Backside +#define LED_PIN_REAR PB13 + +// ***** Buttons ***** +#define BTN_S1_PIN PA4 +#define BTN_S2_PIN PC6 +#define BTN_LOCK_PIN PA0 +#define BTN_4H_PIN PA6 +#define BTN_6H_PIN PC4 +#define BTN_8H_PIN PB0 +#define BTN_10H_PIN PB2 +#define BTN_PLAY_PIN PC11 +#define BTN_HOME_PIN PC12 +#define BTN_SETUP_PIN PF4 + +// ***** Emergency ***** +#define PIN_STOP_JP5 PC10 +#define PIN_STOP_JP6 PA15 + +#define PIN_HALL1 PA8 // LIFT +#define PIN_HALL2 PF6 // LIFTX +#define PIN_HALL3 PA12 // LBUMP +#define PIN_HALL4 PA11 // RBUMP + +extern LEDcontrolRMECOWV1x leds; + +class ButtonsRMECOWV100 : public Buttons { + public: + ButtonsRMECOWV100(const std::map &t_kBtnDefByNumMap) : Buttons(t_kBtnDefByNumMap) {}; + + /** + * @brief Check if one of the internal button functionality got pressed and do it's function. + * SETUP + 4H = Display FW version + * SETUP + 10H = LED animation + */ + void handle_sys_req() override { + if (!is_pressed(BTN_SETUP_NUM)) + return; + + if (is_pressed(BTN_10H_NUM)) + leds.sequence_start(&LEDcontrol::sequence_animate_handler); + else if (is_pressed(BTN_4H_NUM)) + leds.show_num(FIRMWARE_VERSION); + return; + }; +}; +extern ButtonsRMECOWV100 buttons; + +#include "../RM-ECOW-V1x/Hatch.hpp" + +// LowLevel Serial Pins +#define UART_LL_RX PA3 +#define UART_LL_TX PA2 + +#endif // YARDFORCE_MDL_RMECOWV100_ASSEMBLY_HPP diff --git a/Firmware/CoverUI/YardForce/model/RM-ECOW-V110/assembly.cpp b/Firmware/CoverUI/YardForce/model/RM-ECOW-V110/assembly.cpp new file mode 100644 index 0000000..6776dc1 --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/RM-ECOW-V110/assembly.cpp @@ -0,0 +1,103 @@ +/** + * @file RM-ECOW-V100/assembly.cpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce RM-ECOW-V1.0.0 CoverUI Assembly for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2024-10-01 + * + * @copyright Copyright (c) 2024 + * + */ +#include "assembly.hpp" + +#include "../../include/Emergency.hpp" + +// ----- LEDs ----- + +const uint32_t kPinByLedNum[] = { + // Order derived from LowLevel "enum LED_id" + // C500 + // Use LED num Original Alternative + LED_PIN_CHARGE, // 0 + LED_PIN_BAT, // 1 + LED_PIN_WIRE, // 2 + LED_PIN_LIFTED, // 3 + LED_PIN_NC, // 4 SUN + LED_PIN_NC, // 5 SAT + LED_PIN_NC, // 6 FRI + LED_PIN_NC, // 7 THU + LED_PIN_NC, // 8 WED + LED_PIN_NC, // 9 TUE + LED_PIN_NC, // 10 MON + LED_PIN_LOCK, // 11 + LED_PIN_S2, // 12 digit 5 + LED_PIN_S1, // 13 digit 4 + LED_PIN_10H, // 14 8HR digit 3 + LED_PIN_8H, // 15 6HR digit 2 + LED_PIN_6H, // 16 4HR digit 1 + LED_PIN_4H, // 17 2HR digit 0 + LED_PIN_REAR, // 18 +}; + +const unsigned int kLedAnimOrder[] = {3, 2, 1, 0, 17, 16, 15, 14, 13, 12, 11}; +const unsigned int kNumFrontLeds = sizeof(kLedAnimOrder) / sizeof(unsigned int); + +// Numeric (base10) representation of LEDs. +// I.e. Digit 0 = 4H(0), ... Then S2(5) + S1(4) = 9. +// +// |------ Bit 5 = 4H(0) +// ||----- Bit 4 = 6H(1) +// |||---- Bit 3 = 8H(2) +// ||||--- Bit 2 = 10H(3) +// |||||-- Bit 1 = S1(4) +// ||||||- Bit 0 = S2(5) +const uint8_t kBase10Leds[] = { + 0b100000, // = 0 + 0b010000, + 0b001000, + 0b000100, + 0b000010, + 0b000001, + 0b010001, + 0b001001, + 0b000101, + 0b000011}; // = 9 + +// Main LED controller object +LEDcontrolRMECOWV1x leds(kPinByLedNum, sizeof(kPinByLedNum) / sizeof(uint32_t), &LEDcontrol::set_base10_leds); + +// ----- Buttons ----- + +// Map logic button number to pin. Use the same order as in OM FW so that they get scanned in a comparable order! +const std::map kBtnDefByButtonNumMap = { + {BTN_HOME_NUM, {BTN_HOME_PIN, -1}}, + {BTN_PLAY_NUM, {BTN_PLAY_PIN, -1}}, + {BTN_S1_NUM, {BTN_S1_PIN, LED_NUM_S1}}, + {BTN_S2_NUM, {BTN_S2_PIN, LED_NUM_S2}}, + {BTN_LOCK_NUM, {BTN_LOCK_PIN, 11}}, + {BTN_4H_NUM, {BTN_4H_PIN, 17}}, + {BTN_6H_NUM, {BTN_6H_PIN, 16}}, + {BTN_8H_NUM, {BTN_8H_PIN, 15}}, + {BTN_10H_NUM, {BTN_10H_PIN, 14}} +}; + +ButtonsRMECOWV110 buttons(kBtnDefByButtonNumMap); + +// ----- Emergency ----- +const Emergency::PinStateDef kEmergencyPinStateDefs[] = { +#ifdef MOD_HALL + {PIN_HALL1, INPUT_PULLUP, Emergency_state::Emergency_lift1}, + {PIN_HALL2, INPUT_PULLUP, Emergency_state::Emergency_lift1}, + {PIN_HALL3, INPUT_PULLUP, Emergency_state::Emergency_lift2}, + {PIN_HALL4, INPUT_PULLUP, Emergency_state::Emergency_lift2}, +#endif +#ifdef MOD_STOP + {PIN_STOP_JP6, INPUT_PULLUP, Emergency_state::Emergency_stop1}, + {PIN_STOP_JP8, INPUT_PULLUP, Emergency_state::Emergency_stop2}, +#endif +}; + +Emergency emergency(kEmergencyPinStateDefs, sizeof(kEmergencyPinStateDefs) / sizeof(Emergency::PinStateDef)); + +// ----- Hatch ----- +HatchRMECOWV1x hatch; diff --git a/Firmware/CoverUI/YardForce/model/RM-ECOW-V110/assembly.hpp b/Firmware/CoverUI/YardForce/model/RM-ECOW-V110/assembly.hpp new file mode 100644 index 0000000..c119517 --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/RM-ECOW-V110/assembly.hpp @@ -0,0 +1,91 @@ +/** + * @file RM-ECOW-V110/assembly.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce RM-ECOW-V1.0.0 CoverUI Assembly for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2024-10-02 + * + * @copyright Copyright (c) 2024 + * + */ +#ifndef YARDFORCE_MDL_RMECOWV100_ASSEMBLY_HPP +#define YARDFORCE_MDL_RMECOWV100_ASSEMBLY_HPP + +#include "../../include/Buttons.hpp" +#include "../../include/main.h" +#include "../RM-ECOW-V1x/LEDcontrol.hpp" + +#ifdef MOD_EMERGENCY +#include "../../include/Emergency.hpp" +#endif + +// ***** LEDs ***** +// 1st row: Lifted, Wire (WLAN), Battery empty, Charge +#define LED_PIN_LIFTED PA0 +#define LED_PIN_WIRE PA4 +#define LED_PIN_BAT PC4 +#define LED_PIN_CHARGE PA6 +// 2nd row: 4, 6, 8, 10HR +#define LED_PIN_4H PA7 +#define LED_PIN_6H PC5 +#define LED_PIN_8H PB1 +#define LED_PIN_10H PB10 +// 3rd row: S1, Reserved for Setup (WLAN), S2 +#define LED_PIN_S1 PA5 +#define LED_PIN_S2 PC7 +// 4th row: Lock +#define LED_PIN_LOCK PA1 +// Backside +#define LED_PIN_REAR PB0 + +// ***** Buttons ***** +#define BTN_S1_PIN PB11 +#define BTN_S2_PIN PC6 +#define BTN_LOCK_PIN PB12 +#define BTN_4H_PIN PB14 +#define BTN_6H_PIN PB15 +#define BTN_8H_PIN PB13 +#define BTN_10H_PIN PB2 +#define BTN_PLAY_PIN PC11 +#define BTN_HOME_PIN PC12 + +// ***** Emergency ***** +#define PIN_STOP_JP6 PC10 +#define PIN_STOP_JP8 PA15 + +#define PIN_HALL1 PA8 // LIFT +#define PIN_HALL2 PA9 // LIFTX +#define PIN_HALL3 PC8 // LBUMP +#define PIN_HALL4 PC9 // RBUMP + +extern LEDcontrolRMECOWV1x leds; + +class ButtonsRMECOWV110 : public Buttons { + public: + ButtonsRMECOWV110(const std::map &t_kBtnDefByNumMap) : Buttons(t_kBtnDefByNumMap) {}; + + /** + * @brief Check if one of the "magic buttons" got pressed and do his function. + * LOCK + 4H = Display FW version + * LOCK + 10H = LED animation + */ + void handle_sys_req() override { + if (!is_pressed(BTN_LOCK_NUM)) + return; + + if (is_pressed(BTN_10H_NUM)) + leds.sequence_start(&LEDcontrol::sequence_animate_handler); + else if (is_pressed(BTN_4H_NUM)) + leds.show_num(FIRMWARE_VERSION); + return; + }; +}; +extern ButtonsRMECOWV110 buttons; + +#include "../RM-ECOW-V1x/Hatch.hpp" + +// LowLevel Serial Pins +#define UART_LL_RX PA3 +#define UART_LL_TX PA2 + +#endif // YARDFORCE_MDL_RMECOWV100_ASSEMBLY_HPP diff --git a/Firmware/CoverUI/YardForce/model/RM-ECOW-V1x/Hatch.hpp b/Firmware/CoverUI/YardForce/model/RM-ECOW-V1x/Hatch.hpp new file mode 100644 index 0000000..6bdda7d --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/RM-ECOW-V1x/Hatch.hpp @@ -0,0 +1,50 @@ +/** + * @file Hatch.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce RM-ECOW-V1.x CoverUI concrete- Hatch header for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2024-10-01 + * + * @copyright Copyright (c) 2024 + * + */ +#ifndef YARDFORCE_RMECOWV1X_HATCH_HPP +#define YARDFORCE_RMECOWV1X_HATCH_HPP + +#include + +#include "../../include/ABC_Hatch.hpp" +#include "../../include/main.h" + +class HatchRMECOWV1x : public AbstractHatch { + public: + unsigned int handle_button(unsigned int button_id, unsigned int press_cnt) { + if (button_id == BTN_LOCK_NUM && press_cnt >= 2) { + leds.sequence_start(&LEDcontrol::sequence_countdown_handler); // Close hatch countdown + queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec + return 0; // Don't return button now (hatch is still open) + } + + if (button_id == BTN_HOME_NUM || button_id == BTN_PLAY_NUM || button_id == BTN_S1_NUM || button_id == BTN_S2_NUM) { + if (press_cnt > 10 || true) // Be sure that the queue isn't filled with "wait for release" loops + { + leds.sequence_start(&LEDcontrol::sequence_countdown_handler); // Close hatch countdown + queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec + queue_button(button_id, 0, 5000); + return 0; // Don't return button now (hatch is still open) + } + } + + if (button_id == BTN_4H_NUM) + return BTN_MON_NUM; // Volume up + + if (button_id == BTN_6H_NUM) + return BTN_TUE_NUM; // Volume down + + return button_id; + } +}; + +extern HatchRMECOWV1x hatch; + +#endif // YARDFORCE_RMECOWV1X_HATCH_HPP diff --git a/Firmware/CoverUI/YardForce/model/RM-ECOW-V1x/LEDcontrol.hpp b/Firmware/CoverUI/YardForce/model/RM-ECOW-V1x/LEDcontrol.hpp new file mode 100644 index 0000000..d16f7b8 --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/RM-ECOW-V1x/LEDcontrol.hpp @@ -0,0 +1,115 @@ +/** + * @file RM-ECOW-V1x/LEDcontrol.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce RM-ECOW-V1x LED controller model for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.3 + * @date 2024-10-02 + * + * @copyright Copyright (c) 2023, 2024 + * + */ +#ifndef YARDFORCE_RMECOWV1X_LEDCONTROL_HPP +#define YARDFORCE_RMECOWV1X_LEDCONTROL_HPP + +#include + +#include "../../include/LEDcontrol.hpp" + +#define LED_ANIMATE_DELAY 20 // Delay (ms) between LEDs of animate sequence (boot/power-on anim) + +// These constants should be declared i.e. in assembly.cpp +extern const unsigned int kNumFrontLeds; +extern const unsigned int kLedAnimOrder[]; +extern const uint8_t kBase10Leds[]; + +class LEDcontrolRMECOWV1x : public LEDcontrol { + public: + LEDcontrolRMECOWV1x(const uint32_t *t_kPinByLedNumPtr, const size_t t_kNumLeds, void (LEDcontrol::*t_set_base10_leds_callback)(char digit)) : LEDcontrol(t_kPinByLedNumPtr, t_kNumLeds, t_set_base10_leds_callback) {}; + + void sequence_animate_handler() override { + uint16_t step = seq_get_next_step_(LED_ANIMATE_DELAY); // Animation sequence runs in 15ms steps + + if (step == 0) // Next sequence step not reached now + return; + else if (step >= 1 && step <= kNumFrontLeds) { // LED on + set(kLedAnimOrder[step - 1], LED_state::LED_on, false); + return; + } else if (step >= (kNumFrontLeds + 1) && step <= (2 * kNumFrontLeds)) { // LED off + set(kLedAnimOrder[step - kNumFrontLeds - 1], LED_state::LED_off, false); + return; + } else { + seq_start_tick_ = 0; // Sequence end + set(led_states_bin_); // Restore states + return; + } + } + + /** + * @brief Countdown LED animation handler + */ + void sequence_countdown_handler() override { + uint16_t step = seq_get_next_step_(100); // Animation sequence run in 1000ms steps + + if (step == 0) // Next sequence step not reached now + return; + else if (step == 1 || step == 11 || step == 21 || step == 31 || step == 41) { + show_countdown_state_(((step - 1) / 10) + 1, LED_blink_fast); + return; + } else if (step < 41) { + return; + } else { + seq_start_tick_ = 0; // Sequence end + set(led_states_bin_); // Restore states + for (size_t i = 1; i <= 4; i++) + force_off(18 - i, false); + return; + } + } + + /** + * @brief A quick boot/power-on animation, also used as jammed button indicator + * + * @return unsigned int ms how long it will take to play + */ + unsigned int boot_animation() override { + sequence_start(&LEDcontrol::sequence_animate_handler); + return ((kNumFrontLeds + 1) * LED_ANIMATE_DELAY); + } + + /** + * @brief Set base10 related LEDs for the given (numeric) character + * + * @param digit numeric character + */ + void set_base10_leds(char digit) override { + for (uint8_t bit = 0; bit <= 5; bit++) // We've 5 LEDs for base10 number representation + { + bool on = (kBase10Leds[digit - '0'] >> bit) & 0b1; + unsigned int led = bit + 12; + set(led, on ? LED_state::LED_on : LED_state::LED_off, false); + } + } + + void force_off_num_seq_leds(bool force) override { + force_off(LED_NUM_LIFTED, force); // Num change signalling LED + for (unsigned int i = 12; i <= 17; i++) // Base10 related LEDs + force_off(i, force); + } + + private: + void show_countdown_state_(unsigned int sec, LED_state state) { + unsigned int sec_left = 5 - sec; + + for (size_t i = 1; i <= 4; i++) { + uint8_t led_num = 18 - i; + if (sec_left >= i) { // Remaining secs is greater than this LED + set(led_num, state, true); // change_state = true because blink states get handled by timer and this need to be stored + } else { + set(led_num, LED_off, true); + force_off(led_num, true); + } + } + } +}; + +#endif // YARDFORCE_RMECOWV1X_LEDCONTROL_HPP diff --git a/Firmware/CoverUI/YardForce/model/SAxPRO/Display.cpp b/Firmware/CoverUI/YardForce/model/SAxPRO/Display.cpp new file mode 100644 index 0000000..861ba3a --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/SAxPRO/Display.cpp @@ -0,0 +1,328 @@ +/** + * @file Display_SAxPRO.cpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce CoverUI WYM240128K1 display driver class for SAxPRO OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.3 + * @date 2023-11-29 + * + * @copyright Copyright (c) 2023 + */ +#include "Display.hpp" + +#include + +#include "../../include/main.h" +#include "../../include/subscription.h" + +// C images +LV_IMG_DECLARE(OM_Logo_120x54x1); +LV_IMG_DECLARE(OM_Wordmark_240x35x1); + +namespace yardforce { +namespace display { +static controller::UC1698 uc1698; // Display controller + +// LVGL buffers +static lv_disp_draw_buf_t lv_disp_buf; +static lv_color_t lv_buf_1[UC1698_DISPLAY_WIDTH * LVGL_BUFFER_MULTIPLIER]; +static lv_color_t lv_buf_2[UC1698_DISPLAY_WIDTH * LVGL_BUFFER_MULTIPLIER]; + +// Status Screen Widgets +lvgl::WidgetLedSymbol *v_led_heartbeat, *v_led_ros, + *v_led_emergency_wheel, *v_led_emergency, *v_led_emergency_stop, + *v_led_bat, *v_led_gps, *v_led_charge, *v_led_power; +lvgl::WidgetBar *bar_gps, *bar_bat; +lvgl::WidgetTextTicker *text_ticker_status; + +bool main_screen_active = false; // Initialized and active +bool last_docked_state_; // true = docked, false = undocked + +/** + * @brief Rounder callback will round the display area to a multiple of 3, on x axis (RGB control lines of a pixel are connected to 3 monochrome pixels) + * + * @param disp_drv + * @param area + */ +void DisplaySAXPRO::rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) { + area->x1 = area->x1 - (area->x1 % 3); // Round down to neares multiple of 3 + area->x2 = (area->x2 + 3) - (area->x2 % 3) - 1; // Round up to nearest multiple of 3, minus -1 +} + +/** + * @brief Flush display buffer to display controller. + * Done via uc1698::drawPixelTriplet() method, which doesn't look as efficient like direct data write, + * but save the call to a further pixel-color-callback, as well as another buffer, which sounds more expensive. + * + * @param disp_drv + * @param area + * @param t_color_p + */ +void DisplaySAXPRO::flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *t_color_p) { +#ifdef BENCHMARK + cycle_cnt_flush_cb_.start(); +#endif + size_t cols16b = ((area->x2 - area->x1) + 1) / 3; // Num of 16 bit columns for this flush area + size_t cols8b = cols16b * 2; + uint8_t row_buffer[cols8b]; // Buffer for one row 2*8-bit (every 16 bit col holds 3 pixel) + + uc1698.setWindowProgramArea(area->x1, area->x2, area->y1, area->y2); + + for (size_t y = area->y1; y <= area->y2; y++) { + for (size_t x = 0; x < cols8b; x += 2) // ATTENTION: Will only work with a correct rounder_cb()! + { + // Color is inverted (0 = black but pixel off / >0 = white but pixel on) but UC1698 "[16] Set Inverse Display" is set + row_buffer[x] = (0b11111000 * t_color_p->full | (0b00000111 * (t_color_p + 1)->full)); + row_buffer[x + 1] = (0b11100000 * (t_color_p + 1)->full) | (0b00011111 * (t_color_p + 2)->full); + t_color_p += 3; + } + uc1698.writeData(row_buffer, cols8b); + } + lv_disp_flush_ready(disp_drv); +#ifdef BENCHMARK + cycle_cnt_flush_cb_.stop(); +#endif +} + +void DisplaySAXPRO::set_undocked() { + v_led_power->set(LED_off); + v_led_charge->set(LED_off); + bar_bat->set_range(BATT_ABS_MIN, BATT_ABS_MAX); + bar_bat->bar_label = FA_SYMBOL_BATTERY " %d V"; + last_docked_state_ = false; +} + +void DisplaySAXPRO::mainScreen() { +#ifdef BENCHMARK + volatile auto perf_test = cycle_cnt_flush_cb_; +#endif + // On the left side of the status bar we do have functional status symbols like heartbeat and ROS + v_led_ros = new lvgl::WidgetLedSymbol(FA_SYMBOL_ROS, LV_ALIGN_TOP_LEFT, 0, 0); // Leftmost + + // In the middle, we do have emergencies + v_led_emergency = new lvgl::WidgetLedSymbol(FA_SYMBOL_EMERGENCY, LV_ALIGN_TOP_MID, 0, 0); // Centered + v_led_emergency_wheel = new lvgl::WidgetLedSymbol(FA_SYMBOL_EMERGENCY_WHEEL, LV_ALIGN_TOP_MID, -14 - TOP_STATUS_BAR_GAP_PX - 2, 0); // Left of centered + // TODO: if next level LL proto + // v_led_heartbeat = new WidgetLedSymbol(FA_SYMBOL_HEARTBEAT, LV_ALIGN_TOP_MID, -(2 * 14) - (2 * TOP_STATUS_BAR_GAP_PX) - 2, 0); // 2nd left of centered + v_led_emergency_stop = new lvgl::WidgetLedSymbol(FA_SYMBOL_EMERGENCY_STOP, LV_ALIGN_TOP_MID, 14 + TOP_STATUS_BAR_GAP_PX, 0); // Right of centered + + // On the right side, mowing status like, charging, docking, ... + v_led_power = new lvgl::WidgetLedSymbol(FA_SYMBOL_PLUG, LV_ALIGN_OUT_TOP_RIGHT, (240 - (1 * 14)), 0); // Rightmost + v_led_charge = new lvgl::WidgetLedSymbol(FA_SYMBOL_CHARGE, LV_ALIGN_OUT_TOP_RIGHT, (240 - (2 * 14) - TOP_STATUS_BAR_GAP_PX), 0); + v_led_gps = new lvgl::WidgetLedSymbol(FA_SYMBOL_GPS1, LV_ALIGN_OUT_TOP_RIGHT, (240 - (3 * 14) - (2 * TOP_STATUS_BAR_GAP_PX)), 0); + v_led_bat = new lvgl::WidgetLedSymbol(FA_SYMBOL_BATTERY, LV_ALIGN_OUT_TOP_RIGHT, (240 - (4 * 14) - (3 * TOP_STATUS_BAR_GAP_PX)), 0); + + // GPS & Battery bars + bar_gps = new lvgl::WidgetBar(FA_SYMBOL_GPS2 " %d %%", LV_ALIGN_TOP_MID, 0, 30, UC1698_DISPLAY_WIDTH, 21); + bar_bat = new lvgl::WidgetBar(FA_SYMBOL_BATTERY " %d %%", LV_ALIGN_TOP_MID, 0, 60, UC1698_DISPLAY_WIDTH, 21); + + // Mower status text (ticker) + text_ticker_status = new lvgl::WidgetTextTicker(LV_ALIGN_TOP_MID, 0, 95, UC1698_DISPLAY_WIDTH); + + // Set defined state + set_undocked(); + bar_bat->set_value(BATT_ABS_MIN); + + main_screen_active = true; +} + +void DisplaySAXPRO::anim_x_cb(void *var, int32_t v) { + lv_obj_set_x((lv_obj_t *)var, v); +} + +void DisplaySAXPRO::openmower_anim() { + main_screen_active = false; + + // Mower Logo - img_logo + lv_obj_t *img_logo = lv_img_create(lv_scr_act()); + lv_img_set_src(img_logo, &OM_Logo_120x54x1); + lv_obj_align(img_logo, LV_ALIGN_CENTER, 0, -25); + + // OpenMower Wordmark - img_wordmark + lv_obj_t *img_wordmark = lv_img_create(lv_scr_act()); + lv_img_set_src(img_wordmark, &OM_Wordmark_240x35x1); + lv_obj_align(img_wordmark, LV_ALIGN_CENTER, 0, 25); + + // Anim of logo + lv_anim_t al; + lv_anim_init(&al); + lv_anim_set_var(&al, img_logo); + lv_anim_set_values(&al, 0, -((UC1698_DISPLAY_WIDTH / 2) + (OM_Logo_120x54x1.header.w / 2))); + lv_anim_set_time(&al, 1500); + lv_anim_set_delay(&al, 1000); + lv_anim_set_exec_cb(&al, (lv_anim_exec_xcb_t)anim_x_cb); + lv_anim_set_path_cb(&al, lv_anim_path_ease_in); + lv_anim_start(&al); + + // Anim of Wordmark + lv_anim_t aw; + lv_anim_init(&aw); + lv_anim_set_var(&aw, img_wordmark); + lv_anim_set_values(&aw, 0, (UC1698_DISPLAY_WIDTH / 2) + (OM_Wordmark_240x35x1.header.w / 2) + 20); + lv_anim_set_time(&aw, 1400); + lv_anim_set_delay(&aw, 1500); + lv_anim_set_exec_cb(&aw, (lv_anim_exec_xcb_t)anim_x_cb); + lv_anim_set_path_cb(&aw, lv_anim_path_ease_in); + lv_anim_set_deleted_cb(&aw, (lv_anim_ready_cb_t)mainScreen); // Set a callback to indicate when the animation is deleted (idle) + lv_anim_start(&aw); +} + +bool DisplaySAXPRO::init() { + // Init UC1698 display controller + if (!uc1698.init()) { + return false; + } + + // Init LVGL + lv_init(); + lv_disp_drv_init(&lv_disp_drv); // Basic LVGL display driver initialization + lv_disp_drv.draw_buf = &lv_disp_buf; // Set an initialized buffer + lv_disp_drv.rounder_cb = rounder_cb; // Round x coordinated so that it fit for our 3 RGB pixel/per dot display + lv_disp_drv.flush_cb = flush_cb; // Set a flush callback to draw to the display + lv_disp_drv.hor_res = UC1698_DISPLAY_WIDTH; // Set the horizontal resolution in pixels + lv_disp_drv.ver_res = UC1698_DISPLAY_HEIGHT; // Set the vertical resolution in pixels + lv_disp_draw_buf_init(&lv_disp_buf, lv_buf_1, lv_buf_2, UC1698_DISPLAY_WIDTH * LVGL_BUFFER_MULTIPLIER); // Initialize `disp_buf` with the buffer(s) + lv_disp_t *disp; + disp = lv_disp_drv_register(&lv_disp_drv); // Register the driver and save the created display objects + lv_obj_set_style_bg_color(lv_scr_act(), lv_color_white(), LV_PART_MAIN); // No background color + + openmower_anim(); + // mainScreen(); + + return true; +} + +/** + * @brief Regular loop() function, which get called by a low priority hardware timer (approx. 10ms). + * Handles i.e. LVGL timers or LED-2-Display logic. + * Has to be a lower priority routine than tick_inc(), otherwise all LVGL timers (or LEDcontrol-Animations) do not work + */ +void DisplaySAXPRO::loop_low_prio() { + AbstractDisplay::loop_low_prio(); + + // LEDs & Buttons to main status screen conversion + if (!main_screen_active) + return; // Still in OM anim + + char status_ticker[STATUS_TICKER_LENGTH] = ""; + + // GPS + if (subscription::recv_hl_state.gps_quality < 25) + v_led_gps->set(LED_on); + else if (subscription::recv_hl_state.gps_quality < 50) + v_led_gps->set(LED_blink_fast); + else if (subscription::recv_hl_state.gps_quality < 75) + v_led_gps->set(LED_blink_slow); + else + v_led_gps->set(LED_off); + bar_gps->set_value(subscription::recv_hl_state.gps_quality); + + // V-Battery vLED + if (subscription::recv_ll_status.v_battery >= (BATT_EMPTY + 2.0f)) + v_led_bat->set(LED_off); + else + v_led_bat->set(LED_on); + + // Docked (Plug) & Charging (charge-station) + if (subscription::recv_ll_status.v_charge > 20.0f) // Docked + { + if (!last_docked_state_) { + v_led_power->set(LED_on); + bar_bat->set_range(100, 1100); + bar_bat->bar_label = FA_SYMBOL_CHARGE " %d mA"; + set_backlight(); + last_docked_state_ = true; + } + bar_bat->set_value(subscription::recv_ll_status.charging_current * 1000); + + if (subscription::recv_ll_status.charging_current < 0.15f) + v_led_charge->set(LED_off); + else if (subscription::recv_ll_status.charging_current >= 0.15f && subscription::recv_ll_status.charging_current <= 0.8f) + v_led_charge->set(LED_blink_slow); + else if (subscription::recv_ll_status.charging_current > 0.8f) + v_led_charge->set(LED_blink_fast); + } else // Undocked + { + if (last_docked_state_) { + set_undocked(); + } + bar_bat->set_value(subscription::recv_ll_status.v_battery); + } + + // HL Mode & SubMode + switch (subscription::recv_hl_state.current_mode & 0b111111) { + case HighLevelMode::MODE_IDLE: + v_led_ros->set(LED_on); + strncpy(status_ticker, "Idle", STATUS_TICKER_LENGTH); + break; + case HighLevelMode::MODE_AUTONOMOUS: + v_led_ros->set(LED_blink_slow); + if ((subscription::recv_hl_state.current_mode >> 6) & 0b01) + strncpy(status_ticker, "Docking", STATUS_TICKER_LENGTH); + else if ((subscription::recv_hl_state.current_mode >> 6) & 0b10) + strncpy(status_ticker, "Undocking", STATUS_TICKER_LENGTH); + else + strncpy(status_ticker, "Mowing", STATUS_TICKER_LENGTH); + break; + case HighLevelMode::MODE_RECORDING: + v_led_ros->set(LED_blink_fast); + if ((subscription::recv_hl_state.current_mode >> 6) & 0b01) + strncpy(status_ticker, "Record area outline", STATUS_TICKER_LENGTH); + else if ((subscription::recv_hl_state.current_mode >> 6) & 0b10) + strncpy(status_ticker, "Record obstacle", STATUS_TICKER_LENGTH); + break; + default: // We currently don't have a real "ROS Running" identifier. Let's use the current mode. + v_led_ros->set(LED_off); + strncpy(status_ticker, "Waiting for ROS...", STATUS_TICKER_LENGTH); + break; + } + + // ----- Most important text-states, last! ----- + + // Emergencies + static bool last_stop_button = false; + if (subscription::recv_ll_status.emergency_bitmask & LL_EMERGENCY_BITS_STOP) // Stop switch + { + v_led_emergency_stop->set(LED_blink_fast); + strncpy(status_ticker, EMERGENCY_CLEAR_TEXT, STATUS_TICKER_LENGTH); + + if (!last_stop_button) // Backlight on cover- open + set_backlight(); + last_stop_button = true; + } else { + v_led_emergency_stop->set(LED_off); + last_stop_button = false; + } + + if (subscription::recv_ll_status.emergency_bitmask & LL_EMERGENCY_BITS_LIFT) // Lifted or tilted + { + v_led_emergency_wheel->set(LED_blink_fast); + strncpy(status_ticker, EMERGENCY_CLEAR_TEXT, STATUS_TICKER_LENGTH); + } else + v_led_emergency_wheel->set(LED_off); + + if (subscription::recv_ll_status.emergency_bitmask & LL_EMERGENCY_BIT_LATCH) // Emergency latch (no LL heartbeat or emergency by ROS) + { + v_led_emergency->set(LED_blink_slow); + strncpy(status_ticker, EMERGENCY_CLEAR_TEXT, STATUS_TICKER_LENGTH); + } else + v_led_emergency->set(LED_off); + + // ----- Announcement ----- + switch (auto ann = get_anncmnt()) { + case AnncmntType::close_hatch: + sprintf(status_ticker, "Close hatch in %i sec.", ((anncmnt.timeout - millis()) / 1000) + 1); + break; + case AnncmntType::version: + sprintf(status_ticker, "Version %i", FIRMWARE_VERSION); + break; + default: + break; + } + + text_ticker_status->set_text(status_ticker); +} + +} // namespace display +} // namespace yardforce + +yardforce::display::DisplaySAXPRO display(yardforce::display::AbstractDisplay::Config{.backlight_led_num = LED_NUM_BACKLIGHT}); diff --git a/Firmware/CoverUI/YardForce/model/SAxPRO/Display.hpp b/Firmware/CoverUI/YardForce/model/SAxPRO/Display.hpp new file mode 100644 index 0000000..1742ded --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/SAxPRO/Display.hpp @@ -0,0 +1,75 @@ +/** + * @file Display_SAxPRO.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce CoverUI WYM240128K1 display driver for SAxPRO OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.3 + * @date 2023-11-29 + * + * @copyright Copyright (c) 2023 + */ +#ifndef YARDFORCE_DISPLAY_SAXPRO_HPP +#define YARDFORCE_DISPLAY_SAXPRO_HPP + +#define UC1698_DISPLAY_WIDTH 240 +#define UC1698_DISPLAY_HEIGHT 128 + +#define LVGL_BUFFER_MULTIPLIER 10 +#define LVGL_TIMER_HANDLER_PERIOD_MS 10 // 10ms lv_timer_handler() soft period + +#define TOP_STATUS_BAR_GAP_PX 5 // Amount of (gap) pixels between top status-bar icons/symbols +#define EMERGENCY_CLEAR_TEXT "Emergency! Press [Enter], close hatch and stay back, to clear emergency state." + +// clang-format off +#include "../../include/ABC_Display.hpp" +#include "../../include/UC1698.hpp" +#include +#include "../../include/WidgetLedSymbol.hpp" +#include "../../include/WidgetBar.hpp" +#include "../../include/WidgetTextTicker.hpp" +#include "../../include/subscription.h" +// clang-format on + +#define BACKLIGHT_TIMEOUT_MS 120000 // 2 minutes +#define STATUS_TICKER_LENGTH 100 + +// Enable for benchmarking specific code +// #define BENCHMARK + +#ifdef BENCHMARK +#include "include/CortexMCycleCounter.hpp" +#endif + +namespace yardforce { +namespace display { +class DisplaySAXPRO : public AbstractDisplay { + public: + DisplaySAXPRO(Config t_config) : AbstractDisplay(t_config) {}; + + bool init() override; // Init GPIOs, comms, as well as LVGL & display + + void loop_low_prio() override; // Low priority loop for display changes. Also does lv_timer_handler() and subscription + + void openmower_anim(); + static void mainScreen(); + + protected: + lv_disp_drv_t lv_disp_drv; // LVGL driver + + static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area); + static void flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *t_color_p); + + static void anim_x_cb(void *var, int32_t v); + + static void set_undocked(); +}; + +#ifdef BENCHMARK +static CortexMCycleCounter cycle_cnt_flush_cb_; +#endif + +} // namespace display +} // namespace yardforce + +extern yardforce::display::DisplaySAXPRO display; + +#endif // YARDFORCE_DISPLAY_SAXPRO_HPP \ No newline at end of file diff --git a/Firmware/CoverUI/YardForce/model/SAxPRO/assembly.cpp b/Firmware/CoverUI/YardForce/model/SAxPRO/assembly.cpp new file mode 100644 index 0000000..f1d7332 --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/SAxPRO/assembly.cpp @@ -0,0 +1,94 @@ +/** + * @file SAxPRO/assembly.cpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce SAxPRO CoverUI Assembly for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2024-10-02 + * + * @copyright Copyright (c) 2024 + * + */ +#include "assembly.hpp" + +#include "../../include/Emergency.hpp" +#include "Display.hpp" + +// ----- LEDs ----- + +const uint32_t kPinByLedNum[] = { + // Order derived from LowLevel "enum LED_id" + // C500 + // Use LED num Original Alternative + LED_PIN_NC, // 0 + LED_PIN_NC, // 1 + LED_PIN_NC, // 2 + LED_PIN_NC, // 3 + LED_PIN_NC, // 4 SUN(6) + LED_PIN_NC, // 5 SAT(5) + LED_PIN_NC, // 6 FRI(4) + LED_PIN_NC, // 7 THU(3) + LED_PIN_NC, // 8 WED(2) + LED_PIN_NC, // 9 TUE(1) + LED_PIN_NC, // 10 MON(0) + LED_PIN_NC, // 11 + LED_PIN_NC, // 12 digit 5 + LED_PIN_NC, // 13 digit 4 + LED_PIN_NC, // 14 8HR digit 3 + LED_PIN_NC, // 15 6HR digit 2 + LED_PIN_NC, // 16 4HR digit 1 + LED_PIN_NC, // 17 2HR digit 0 + LED_PIN_NC, // 18 + LED_PIN_BACKLIGHT // 19 +}; + +// Main LED controller object +LEDcontrol leds(kPinByLedNum, sizeof(kPinByLedNum) / sizeof(uint32_t)); // Main LED controller object + +// ----- Buttons ----- + +// Map logic button number to pin. Use the same order as in OM FW so that they get scanned in a comparable order! +const std::map kBtnDefByButtonNumMap = { + {BTN_HOME_NUM, {BTN_HOME_PIN, -1}}, + {BTN_PLAY_NUM, {BTN_PLAY_PIN, -1}}, + {BTN_OK_NUM, {BTN_OK_PIN, -1}}, + {BTN_UP_NUM, {BTN_UP_PIN, -1}}, + {BTN_DOWN_NUM, {BTN_DOWN_PIN, -1}}, + {BTN_BACK_NUM, {BTN_BACK_PIN, -1}}}; + +Buttons buttons(kBtnDefByButtonNumMap); + +// ----- Hatch ----- + +unsigned int HatchSAXPRO::handle_button(unsigned int button_id, unsigned int press_cnt) { + // If backlight is off, skip first button press + if (display.check_backlight() == LED_off) { + display.set_backlight(); + return 0; // Skip handling of first button-press if backlight was off + } + display.set_backlight(); + + // Clear emergency = Enter (OK) button + if (button_id == BTN_OK_NUM) { + display.start_anncmnt(4000, yardforce::display::AbstractDisplay::AnncmntType::close_hatch); + queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec + return 0; // Don't return button now (hatch is still open) + } + + // Play & Home button + if (button_id == BTN_HOME_NUM || button_id == BTN_PLAY_NUM) { + display.start_anncmnt(4000, yardforce::display::AbstractDisplay::AnncmntType::close_hatch); + queue_button(BTN_LOCK_NUM, 2, 4500); // Send long-press Lock button in 4.5 sec + queue_button(button_id, 0, 5000); + return 0; // Don't return button now (hatch is still open) + } + + if (button_id == BTN_UP_NUM) + return BTN_MON_NUM; // Volume up + + if (button_id == BTN_DOWN_NUM) + return BTN_TUE_NUM; // Volume down + + return button_id; +}; + +HatchSAXPRO hatch; \ No newline at end of file diff --git a/Firmware/CoverUI/YardForce/model/SAxPRO/assembly.hpp b/Firmware/CoverUI/YardForce/model/SAxPRO/assembly.hpp new file mode 100644 index 0000000..613231b --- /dev/null +++ b/Firmware/CoverUI/YardForce/model/SAxPRO/assembly.hpp @@ -0,0 +1,58 @@ +/** + * @file SAxPRO/assembly.hpp + * @author Apehaenger (joerg@ebeling.ws) + * @brief YardForce SAxPRO CoverUI Assembly for OpenMower https://github.com/ClemensElflein/OpenMower + * @version 0.1 + * @date 2024-10-02 + * + * @copyright Copyright (c) 2024 + * + */ +#ifndef YARDFORCE_MDL_SAXPRO_ASSEMBLY_HPP +#define YARDFORCE_MDL_SAXPRO_ASSEMBLY_HPP + +#include "../../include/Buttons.hpp" +#include "../../include/LEDcontrol.hpp" +#include "../../include/main.h" + +// FIXME: AbstractDisplay depends on leds +extern LEDcontrol leds; +#include "Display.hpp" + +#ifdef MOD_EMERGENCY +#include "../../include/Emergency.hpp" +#endif + +// ***** LEDs ***** + +#define LED_PIN_BACKLIGHT PA11 // SAxPRO only has one LED, the backlight LED +#define LED_NUM_BACKLIGHT 19 + +// ***** Buttons ***** + +#define BTN_PLAY_PIN PC0 // or Start +#define BTN_HOME_PIN PC1 +#define BTN_UP_PIN PB14 +#define BTN_DOWN_PIN PB13 +#define BTN_OK_PIN PB12 // or Enter +#define BTN_BACK_PIN PB15 + +extern Buttons buttons; + +// ----- Hatch ----- + +#include "../../include/ABC_Hatch.hpp" + +class HatchSAXPRO : public AbstractHatch { + public: + unsigned int handle_button(unsigned int button_id, unsigned int press_cnt); +}; + +extern HatchSAXPRO hatch; + +// ----- LowLevel Serial ----- + +#define UART_LL_RX PA3 +#define UART_LL_TX PA2 + +#endif // YARDFORCE_MDL_SAXPRO_ASSEMBLY_HPP diff --git a/Firmware/CoverUI/platformio.ini b/Firmware/CoverUI/platformio.ini index 7b75de0..57870b4 100644 --- a/Firmware/CoverUI/platformio.ini +++ b/Firmware/CoverUI/platformio.ini @@ -44,17 +44,14 @@ build_src_filter = - - + - - - - - - + - - - - - - - - + - - - - - build_flags = @@ -76,7 +73,7 @@ debug_tool = stlink board = disco_f030r8 build_src_filter = ${env.build_src_filter} - + + + build_flags = ${env.build_flags} -DMCU_STM32 @@ -101,8 +98,7 @@ debug_server = board = disco_f030r8 build_src_filter = ${env.build_src_filter} - + - + + + build_flags = ${env.build_flags} -DMCU_STM32 @@ -128,9 +124,7 @@ debug_server = board = disco_f030r8 build_src_filter = ${env.build_src_filter} - + - + - + + + build_flags = ${env.build_flags} -DMCU_STM32 @@ -157,8 +151,7 @@ debug_server = board = disco_f030r8 build_src_filter = ${env.build_src_filter} - + - + + + build_flags = ${env.build_flags} -DMCU_STM32 @@ -191,7 +184,7 @@ board = genericGD32F330R8 extra_scripts = pre:YardForce/util/apply_gd32f3x0_patches.py build_src_filter = ${env.build_src_filter} - + + + build_flags = ${env.build_flags} -DMCU_GD32 @@ -220,8 +213,7 @@ board = genericGD32F330R8 extra_scripts = pre:YardForce/util/apply_gd32f3x0_patches.py build_src_filter = ${env.build_src_filter} - + - + + + build_flags = ${env.build_flags} -DMCU_GD32 @@ -250,9 +242,7 @@ board = genericGD32F330R8 extra_scripts = pre:YardForce/util/apply_gd32f3x0_patches.py build_src_filter = ${env.build_src_filter} - + - + - + + + build_flags = ${env.build_flags} -DMCU_GD32 @@ -274,7 +264,6 @@ debug_server = reset_config none separate - [env:YF_C500_GD32_HALL] platform = https://github.com/CommunityGD32Cores/platform-gd32.git platform_packages = @@ -283,8 +272,7 @@ board = genericGD32F330R8 extra_scripts = pre:YardForce/util/apply_gd32f3x0_patches.py build_src_filter = ${env.build_src_filter} - + - + + + build_flags = ${env.build_flags} -DMCU_GD32 @@ -313,8 +301,7 @@ debug_server = board = disco_f030r8 build_src_filter = ${env.build_src_filter} - + - + + + build_flags = ${env.build_flags} -DMCU_STM32 @@ -338,9 +325,7 @@ debug_server = board = disco_f030r8 build_src_filter = ${env.build_src_filter} - + - + - + + + build_flags = ${env.build_flags} -DMCU_STM32 @@ -366,9 +351,7 @@ debug_server = board = disco_f030r8 build_src_filter = ${env.build_src_filter} - + - + - + + + build_flags = ${env.build_flags} -DMCU_STM32 @@ -394,9 +377,7 @@ debug_server = board = disco_f030r8 build_src_filter = ${env.build_src_filter} - + - + - + + + build_flags = ${env.build_flags} -DMCU_STM32 @@ -427,8 +408,7 @@ debug_server = board = disco_f030r8 build_src_filter = ${env.build_src_filter} - + - + + + build_flags = ${env.build_flags} -DMCU_STM32 @@ -452,9 +432,7 @@ debug_server = board = disco_f030r8 build_src_filter = ${env.build_src_filter} - + - + - + + + build_flags = ${env.build_flags} @@ -481,9 +459,7 @@ debug_server = board = disco_f030r8 build_src_filter = ${env.build_src_filter} - + - + - + + + build_flags = ${env.build_flags} @@ -511,9 +487,7 @@ debug_server = board = disco_f030r8 build_src_filter = ${env.build_src_filter} - + - + - + + + build_flags = ${env.build_flags} @@ -548,8 +522,7 @@ board = genericGD32F330R8 extra_scripts = pre:YardForce/util/apply_gd32f3x0_patches.py build_src_filter = ${env.build_src_filter} - + - + + + build_flags = ${env.build_flags} -DMCU_GD32 @@ -577,9 +550,7 @@ board = genericGD32F330R8 extra_scripts = pre:YardForce/util/apply_gd32f3x0_patches.py build_src_filter = ${env.build_src_filter} - + - + - + + + build_flags = ${env.build_flags} -DMCU_GD32 @@ -609,9 +580,7 @@ board = genericGD32F330R8 extra_scripts = pre:YardForce/util/apply_gd32f3x0_patches.py build_src_filter = ${env.build_src_filter} - + - + - + + + build_flags = ${env.build_flags} -DMCU_GD32 @@ -642,9 +611,7 @@ board = genericGD32F330R8 extra_scripts = pre:YardForce/util/apply_gd32f3x0_patches.py build_src_filter = ${env.build_src_filter} - + - + - + + + build_flags = ${env.build_flags} -DMCU_GD32 @@ -674,12 +641,10 @@ debug_server = board = genericSTM32F401RC build_src_filter = ${env.build_src_filter} - + + + + - + + + - + + build_flags = @@ -701,14 +666,11 @@ debug_build_flags = -O0 -g2 -ggdb2 board = genericSTM32F401RC build_src_filter = ${env.build_src_filter} - + + + + - + + + - + + - + build_flags = ${env.build_flags} @@ -731,14 +693,11 @@ debug_build_flags = -O0 -g2 -ggdb2 board = genericSTM32F401RC build_src_filter = ${env.build_src_filter} - + + + + - + + + - + + - + build_flags = ${env.build_flags} @@ -762,14 +721,11 @@ debug_build_flags = -O0 -g2 -ggdb2 board = genericSTM32F401RC build_src_filter = ${env.build_src_filter} - + + + + - + + + - + + - + build_flags = ${env.build_flags} @@ -799,12 +755,10 @@ board_upload.maximum_size = 262144 ; STM32F030RCx has 256k Flash build_src_filter = ${env.build_src_filter} - + + + + - + + + - + + build_flags = diff --git a/README.md b/README.md index 7606ccf..98eadc6 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This repository contains the firmware files required to use the mowers CoverUI ( 1. Custom DIY CoverUI-PCB which replaces the stock (top-cover) PCB.
Please check the [Cover UI board assembly instructions]() for more detailed infos.
- Also available in [Vermut's Shop](https://shop.devops.care/openmower/29-openmower-012x-assembled-board.html) (as part of his 'Upgrade Kit') + Also available in [Vermut's Shop](https://shop.devops.care/10-openmower) (as part of his 'Upgrade Kit') 1. Stock YardForce CoverUI for the following models: 1. Classic 500 (STM32 & GD32 MCU) 2. Classic 500B (STM32 & GD32 MCU) @@ -20,7 +20,7 @@ This repository contains the firmware files required to use the mowers CoverUI ( 6. SAxPro (Rev6) 6 Buttons 240*128 Pixel LC-Display (STM32 MCU) For this, you need to solder some cables and flash a modified firmware to it.
- Please check the detailed [Stock CoverUI Readme](Firmware/CoverUI/YardForce/README.md) (as you've also the option to use your stock hall, Stop and rain cables). + Please check the detailed [Stock CoverUI Readme](Firmware/CoverUI/YardForce/README.md) (as you've also the option to use your stock 'hall', 'stop' and 'rain' cables). ## License