Skip to content

Commit

Permalink
SA600/900 (RM-ECOW-V1.1.0) support (ClemensElflein#10)
Browse files Browse the repository at this point in the history
* Add schematics

* Original FW backup

* LEDcontrol base- & sub-class separation for RM-ECOW-V100

* Nifty restructuration

* Restructured Button code

* Reimplemented magic buttons and finished button & LED restructuration

* Split up Hardware Mods and added RM-ECOW-V1.0.0

* Evaluated Hall Support

* Evaluated Hall Support

* Fixed links

* Started backport of C500- STM32 & GD32. Broken!

* Finished backport of C500- STM & GD32

* Passed by reference instead value

* Adapted Emergency for common C500 & RMECOWV100 usage

* Add stock-firmware builds to CI

* Add stock-firmware to CI job 'tagged-release'

* Refactored again for SAxPRO backport. SAxPRO code isn't working yet!

* Refactoring done. SAxPRO compiles. Added Hatch class

* Fix HALL_MOD build

* New binary location

* Fix model typo

* Finished SAxPRO and refactoring

* Some final fixes

* Fix packet

* Fix out of bounds array access

* Configure std LEDs as NC-LEDs

* Final RL fixes

* Fix docking states and backlight handling

* Fix GD32F3x PLL

* Activate boot anim, adjust charge min. value

* Fix compile error

* Some typo, version, link

* Fix NX80i naming

* OEM FW backup

* RM-EC3-V1.1 schematics

* Finish ST75256 controller driver

* NX100i general done

* Moved ST75256 to controller namespace

* Change namespace

* Change namespace

* Change namespace

* Made display a abstract Display class

* Refactor display as abstract Display class

* Fix missing subscription

* Fix widget arrangement

* Add emergencies STOP & HALL

* Fix emergency, finish NX100i implementation

* Final cosmetic changes

* Adapt SAxPRO to new structure

* Add NX100i to the docs

* Minor corrections

* CI build of NX100i FW

* Actualized meaning of the LEDs for NX100i

* Optimize UC1698 and flush_cb()

* Remove "Next Language" info

* Add circuit diagram

* Fix once-only header identifier

* Support SA650 RM-ECOW-V1.1.0  (9-Button/11-LED) CoverUI

* Change magic button for SA650

* Change alive LED to be software driven (non hwtimer)

* Add Buttons & LEDs for SA650

* Fix countdown anim

* Fix emergency clear LED countdown

* Add SA650

* Fix some links

* Fix SA650 ToC link

* Add SA650

* Add new SA650 CoverUI

* Add new model info
  • Loading branch information
Apehaenger authored Jul 5, 2024
1 parent 6e33024 commit 65433fa
Show file tree
Hide file tree
Showing 155 changed files with 39,442 additions and 2,316 deletions.
72 changes: 69 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,59 @@ jobs:
name: open-mower-pico-firmware
path: artifacts/

firmware-stock:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO Core
run: pip install --upgrade platformio

- name: Run PlatformIO
run: pio run -d Firmware/CoverUI --environment YF_C500_STM32 --environment YF_C500_STM32_RAIN --environment YF_C500_STM32_RAIN_HALL --environment YF_C500_STM32_HALL --environment YF_C500_GD32 --environment YF_C500_GD32_RAIN --environment YF_C500_GD32_RAIN_HALL --environment YF_C500_GD32_HALL --environment YF_RM-ECOW-V100_STM32 --environment YF_RM-ECOW-V100_STM32_STOP --environment YF_RM-EC3-V11_STM32 --environment YF_RM-EC3-V11_STM32_HALL --environment YF_RM-EC3-V11_STM32_HALL_STOP --environment YF_RM-EC3-V11_STM32_STOP --environment YF_SAXPRO_STM32 --environment YF_RM-ECOW-V110_GD32 --environment YF_RM-ECOW-V110_GD32_HALL --environment YF_RM-ECOW-V110_GD32_HALL_STOP --environment YF_RM-ECOW-V110_GD32_STOP

- name: Copy Artifacts
run: |
mkdir artifacts/
cp Firmware/CoverUI/.pio/build/YF_C500_STM32/firmware.bin ./artifacts/firmware_C500_STM32.bin
cp Firmware/CoverUI/.pio/build/YF_C500_STM32_RAIN/firmware.bin ./artifacts/firmware_C500_STM32_RAIN.bin
cp Firmware/CoverUI/.pio/build/YF_C500_STM32_RAIN_HALL/firmware.bin ./artifacts/firmware_C500_STM32_RAIN_HALL.bin
cp Firmware/CoverUI/.pio/build/YF_C500_STM32_HALL/firmware.bin ./artifacts/firmware_C500_STM32_HALL.bin
cp Firmware/CoverUI/.pio/build/YF_C500_GD32/firmware.bin ./artifacts/firmware_C500_GD32.bin
cp Firmware/CoverUI/.pio/build/YF_C500_GD32_RAIN/firmware.bin ./artifacts/firmware_C500_GD32_RAIN.bin
cp Firmware/CoverUI/.pio/build/YF_C500_GD32_RAIN_HALL/firmware.bin ./artifacts/firmware_C500_GD32_RAIN_HALL.bin
cp Firmware/CoverUI/.pio/build/YF_C500_GD32_HALL/firmware.bin ./artifacts/firmware_C500_GD32_HALL.bin
cp Firmware/CoverUI/.pio/build/YF_RM-ECOW-V100_STM32/firmware.bin ./artifacts/firmware_RM-ECOW-V100_STM32.bin
cp Firmware/CoverUI/.pio/build/YF_RM-ECOW-V100_STM32_STOP/firmware.bin ./artifacts/firmware_RM-ECOW-V100_STM32_STOP.bin
cp Firmware/CoverUI/.pio/build/YF_RM-EC3-V11_STM32/firmware.bin ./artifacts/firmware_RM-EC3-V11_STM32.bin
cp Firmware/CoverUI/.pio/build/YF_RM-EC3-V11_STM32_HALL/firmware.bin ./artifacts/firmware_RM-EC3-V11_STM32_HALL.bin
cp Firmware/CoverUI/.pio/build/YF_RM-EC3-V11_STM32_HALL_STOP/firmware.bin ./artifacts/firmware_RM-EC3-V11_STM32_HALL_STOP.bin
cp Firmware/CoverUI/.pio/build/YF_RM-EC3-V11_STM32_STOP/firmware.bin ./artifacts/firmware_RM-EC3-V11_STM32_STOP.bin
cp Firmware/CoverUI/.pio/build/YF_SAXPRO_STM32/firmware.bin ./artifacts/firmware_YF_SAXPRO_STM32.bin
cp Firmware/CoverUI/.pio/build/YF_RM-ECOW-V110_GD32/firmware.bin ./artifacts/firmware_RM-ECOW-V110_GD32.bin
cp Firmware/CoverUI/.pio/build/YF_RM-ECOW-V110_GD32_HALL/firmware.bin ./artifacts/firmware_RM-ECOW-V110_GD32_HALL.bin
cp Firmware/CoverUI/.pio/build/YF_RM-ECOW-V110_GD32_HALL_STOP/firmware.bin ./artifacts/firmware_RM-ECOW-V110_GD32_HALL_STOP.bin
cp Firmware/CoverUI/.pio/build/YF_RM-ECOW-V110_GD32_STOP/firmware.bin ./artifacts/firmware_RM-ECOW-V110_GD32_STOP.bin
- name: Step 3 - Use the Upload Artifact GitHub Action
uses: actions/upload-artifact@v3
with:
name: open-mower-stock-firmware
path: artifacts/

tagged-release:
runs-on: ubuntu-latest
if: github.ref_type == 'tag' && startsWith(github.ref_name, 'v')
needs: firmware
needs: [firmware, firmware-stock]

steps:
- uses: actions/checkout@v3
with:
Expand All @@ -66,6 +114,14 @@ jobs:
- name: Create firmware zip
run: zip -r release/firmware.zip firmware

- uses: actions/download-artifact@v3
with:
name: open-mower-stock-firmware
path: firmware-stock

- name: Create firmware-stock zip
run: zip -r firmware-stock.zip firmware-stock

- uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
Expand Down Expand Up @@ -97,7 +153,7 @@ jobs:
pre-release-latest-firmware:
runs-on: ubuntu-latest
if: github.ref_type != 'tag'
needs: firmware
needs: [firmware, firmware-stock]
steps:
- uses: actions/checkout@v3
with:
Expand All @@ -111,10 +167,20 @@ jobs:
- name: Create firmware zip
run: zip -r firmware.zip firmware

- uses: actions/download-artifact@v3
with:
name: open-mower-stock-firmware
path: firmware-stock

- name: Create firmware-stock zip
run: zip -r firmware-stock.zip firmware-stock

- uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
title: "Latest Firmware"
automatic_release_tag: "latest"
prerelease: true
files: firmware.zip
files: |
firmware.zip
firmware-stock.zip
25 changes: 22 additions & 3 deletions Firmware/CoverUI/BttnCtl.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ enum TYPE
Set_Buzzer = 0xB1,
Set_LEDs = 0xB2,
Get_Button = 0xB3,
Get_Emergency = 0xB4,
Get_Rain = 0xB5
Get_Emergency = 0xB4, // Stock-CoverUI
Get_Rain = 0xB5, // Stock-CoverUI
Get_Subscribe = 0xB6
};


enum LED_state {
LED_off = 0b000,
LED_blink_slow = 0b101,
Expand All @@ -32,6 +32,14 @@ enum Emergency_state
Emergency_lift2 = 0b10000
};

// CoverUI subscription topic_bitmask
enum Topic_state
{
Topic_set_leds = 1 << 0,
Topic_set_ll_status = 1 << 1,
Topic_set_hl_state = 1 << 2,
};

#pragma pack(push, 1)
struct msg_get_version
{
Expand Down Expand Up @@ -109,4 +117,15 @@ struct msg_event_emergency
} __attribute__((packed));
#pragma pack(pop)

// Cover UI might subscribe in what data it's interested to receive
#pragma pack(push, 1)
struct msg_event_subscribe
{
uint8_t type; // Command type
uint8_t topic_bitmask; // Bitmask of data subscription(s), see Topic_state
uint16_t interval; // Interval (ms) how often to send topic(s)
uint16_t crc; // CRC 16
} __attribute__((packed));
#pragma pack(pop)

#endif // _BttnCtl_HEADER_FILE_
50 changes: 50 additions & 0 deletions Firmware/CoverUI/YardForce/ButtonDebouncer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* @file ButtonDebouncer.cpp
* @author Apehaenger ([email protected])
* @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);
};
57 changes: 0 additions & 57 deletions Firmware/CoverUI/YardForce/ButtonDebouncer.hpp

This file was deleted.

116 changes: 116 additions & 0 deletions Firmware/CoverUI/YardForce/Buttons.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* @file Buttons.cpp
* @author Apehaenger ([email protected])
* @brief YardForce CoverUI Buttons class for OpenMower https://github.com/ClemensElflein/OpenMower
* @version 0.6
* @date 2023-11-05
*
* @copyright Copyright (c) 2023
*
*/
#include <Arduino.h>
#include <stdint.h>
#include <map>
#include "include/Buttons.hpp"
#include "../BttnCtl.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 exists 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(std::pair<uint32_t, ButtonDebouncer>(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 (std::map<uint32_t, ButtonDebouncer>::iterator it = debouncer_by_gpio_port_nr_map.begin(); it != debouncer_by_gpio_port_nr_map.end(); ++it)
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())
{
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 debouncer_it->second.is_pressed(btn_def_it->second.pin);
}
}
return false;
};

/**
* @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));
}
Loading

0 comments on commit 65433fa

Please sign in to comment.