From b9764460308998ad4a7432a5c006bfc536b66d74 Mon Sep 17 00:00:00 2001 From: Noah Metz Date: Tue, 7 Jul 2026 10:44:52 -0600 Subject: [PATCH] Moved menus to their own C files, updated L1 to 4040 footprint --- Makefile | 2 +- main/CMakeLists.txt | 3 + main/ble.c | 3 +- main/ble.h | 2 +- main/config.h | 6 +- main/draw.c | 17 +- main/events.h | 29 + main/font.h | 23 - main/main.c | 1243 +++++++++++++++++++------------------ main/menu_cmdr.c | 95 +++ main/menu_cmdr.h | 4 + main/menu_counters.c | 39 ++ main/menu_counters.h | 3 + main/menu_dice.c | 56 ++ main/menu_dice.h | 3 + main/menu_life.c | 81 +++ main/menu_life.h | 3 + main/menu_settings.c | 164 +++++ main/menu_settings.h | 3 + main/menus.c | 50 ++ main/menus.h | 30 + main/state.h | 6 +- pcb/commeownder.kicad_pcb | 1135 +++++++++++++++++---------------- pcb/commeownder.kicad_prl | 4 +- pcb/commeownder.kicad_sch | 2 +- sdkconfig.defaults | 3 + 26 files changed, 1801 insertions(+), 1208 deletions(-) create mode 100644 main/events.h create mode 100644 main/menu_cmdr.c create mode 100644 main/menu_cmdr.h create mode 100644 main/menu_counters.c create mode 100644 main/menu_counters.h create mode 100644 main/menu_dice.c create mode 100644 main/menu_dice.h create mode 100644 main/menu_life.c create mode 100644 main/menu_life.h create mode 100644 main/menu_settings.c create mode 100644 main/menu_settings.h create mode 100644 main/menus.c create mode 100644 main/menus.h diff --git a/Makefile b/Makefile index c84477f..4fe3671 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ PORT ?= /dev/ttyUSB0 # All firmware targets build with DEBUG=1 so serial commands and DBG lines # are always available for testing and monitoring. -IDF := $(ESP_ENV) && idf.py -DDEBUG=1 +IDF := $(ESP_ENV) && idf.py -b115200 -DDEBUG=1 .PHONY: all firmware sim test flash monitor flash-monitor clean firmware-clean sim-clean case case_live case_clean diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index c86f2de..a923743 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,4 +1,7 @@ idf_component_register(SRCS "main.c" "draw.c" "ble.c" "game.c" + "menus.c" + "menu_life.c" "menu_cmdr.c" "menu_counters.c" + "menu_dice.c" "menu_settings.c" INCLUDE_DIRS "." PRIV_REQUIRES driver nvs_flash bt vfs esp_adc) diff --git a/main/ble.c b/main/ble.c index 7a8746c..8bb2011 100644 --- a/main/ble.c +++ b/main/ble.c @@ -1,5 +1,6 @@ #include "ble.h" #include "state.h" +#include "esp_timer.h" #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" #include "services/gap/ble_svc_gap.h" @@ -99,7 +100,7 @@ static void ble_update_peer(const ble_addr_t *addr, const ble_payload_t *p) g_peers[slot].poison = p->poison; g_peers[slot].eliminated = p->eliminated; g_peers[slot].reset_cmd = p->reset_cmd; - g_peers[slot].last_seen = g_tick; + g_peers[slot].last_seen = esp_timer_get_time(); g_peers[slot].active = 1; memcpy(g_peers[slot].name, p->name, PLAYER_NAME_LEN); g_peers[slot].name[PLAYER_NAME_LEN] = '\0'; diff --git a/main/ble.h b/main/ble.h index 51a14df..d1e4921 100644 --- a/main/ble.h +++ b/main/ble.h @@ -20,7 +20,7 @@ typedef struct { uint8_t poison; uint8_t eliminated; uint8_t reset_cmd; - uint32_t last_seen; + uint64_t last_seen; int active; } ble_peer_t; diff --git a/main/config.h b/main/config.h index 5aa2740..7be9b93 100644 --- a/main/config.h +++ b/main/config.h @@ -38,14 +38,12 @@ #define BLE_PEER_TIMEOUT 3000 // ── Menus ───────────────────────────────────────────────────────────────────── -#define NUM_MENUS 5 +#define NUM_MENU_SLOTS 8 #define MENU_LIFE 0 #define MENU_CMDR 1 #define MENU_COUNTERS 2 #define MENU_DICE 3 -#define MENU_SETTINGS 4 - -extern const int menu_slot[NUM_MENUS]; +#define MENU_SETTINGS 7 // ── Dice ────────────────────────────────────────────────────────────────────── #define DICE_CSV_LEN 128 diff --git a/main/draw.c b/main/draw.c index 084ddc8..0d3476c 100644 --- a/main/draw.c +++ b/main/draw.c @@ -3,13 +3,12 @@ #include "game.h" #include "font.h" #include "state.h" +#include "menus.h" #include "driver/ledc.h" #include "driver/i2c.h" #include "freertos/FreeRTOS.h" #include #include - -const int menu_slot[NUM_MENUS] = {0, 1, 2, 3, 7}; const int die_sides[NUM_DICE_SIDES] = {4, 6, 8, 10, 12, 20, 100}; const char *setting_names[NUM_SETTINGS] = { "LED BRIGHTNESS", "STARTING LIFE", "NUM OPPONENTS", "BLE", @@ -21,7 +20,8 @@ const char *setting_names[NUM_SETTINGS] = { #define LEDC_TIMER_SEL LEDC_TIMER_0 #define LEDC_MODE_ LEDC_LOW_SPEED_MODE #define LEDC_DUTY_RES_ LEDC_TIMER_8_BIT -#define LEDC_FREQ_HZ_ 5000 +// RTC8M (8MHz) keeps LEDC alive during light sleep; max 8MHz/256 = 31.25kHz +#define LEDC_FREQ_HZ_ 977 // ── LED ─────────────────────────────────────────────────────────────────────── void led_init(void) @@ -31,7 +31,7 @@ void led_init(void) .timer_num = LEDC_TIMER_SEL, .duty_resolution = LEDC_DUTY_RES_, .freq_hz = LEDC_FREQ_HZ_, - .clk_cfg = LEDC_AUTO_CLK, + .clk_cfg = LEDC_USE_RC_FAST_CLK, }; ledc_timer_config(&timer); ledc_channel_config_t ch[] = { @@ -142,10 +142,11 @@ void oled_draw_header(void) { uint8_t pages[HEADER_PAGES][OLED_WIDTH]; memset(pages, 0xFF, sizeof(pages)); - for (int seg = 0; seg < NUM_MENUS; seg++) { + for (int seg = 0; seg < NUM_MENU_SLOTS; seg++) { + if (menus[seg] == NULL || menus[seg]->icon == NULL) continue; uint8_t bg = (seg == g_active_menu) ? 0x00 : 0xFF; - int sx = menu_slot[seg] * SEG_W; - const uint8_t (*icon)[ICON_W] = (seg == MENU_CMDR && g_ble_enabled) ? icon_net : icons[seg]; + int sx = seg * SEG_W; + const uint8_t (*icon)[ICON_W] = menus[seg]->icon; for (int col = 0; col < ICON_W; col++) { pages[0][sx+col] = bg ^ icon[0][col]; pages[1][sx+col] = bg ^ icon[1][col]; @@ -155,7 +156,7 @@ void oled_draw_header(void) // Slot 6 (cols 96-111): vertical battery icon, nub at top, fill from bottom // or lightning bolt when charging if (g_battery_pct >= 0) { - int show = (g_battery_pct >= BATT_LOW_PCT || (g_tick / 50) % 2 == 0); + int show = (g_battery_pct >= BATT_LOW_PCT || g_batt_blink); if (show) { if (!g_charging && !g_batt_full) { diff --git a/main/events.h b/main/events.h new file mode 100644 index 0000000..ab8832e --- /dev/null +++ b/main/events.h @@ -0,0 +1,29 @@ +#pragma once +#include + +typedef enum { + EVT_BUTTON, // GPIO edge: gpio, level, ts_us + EVT_HOLD_EXPIRE, // per-button hold timer fired: btn_idx + EVT_COMBO_SLEEP, // all-4 held 5s + EVT_COMBO_SETTINGS, // fwd+back held 1s + EVT_BATT_TICK, // 500ms periodic + EVT_PEER_TICK, // 1s periodic + EVT_AUTOSAVE, // one-shot 30s: flush NVS + EVT_LIFE_DELTA_EXP, // one-shot: clear life delta overlay + EVT_RESET_CMD_EXP, // one-shot: end reset_cmd broadcast + EVT_IDLE_SLEEP, // one-shot: idle timeout + EVT_LED_BREATHE, // 10ms periodic: breathing animation +} evt_type_t; + +typedef struct { + evt_type_t type; + int gpio; // EVT_BUTTON only + int level; // EVT_BUTTON only: 0=pressed 1=released + int64_t ts_us; // EVT_BUTTON only + int btn_idx; // EVT_HOLD_EXPIRE only: 0=fwd 1=left 2=right 3=back +} app_event_t; + +#define BTN_IDX_FWD 0 +#define BTN_IDX_LEFT 1 +#define BTN_IDX_RIGHT 2 +#define BTN_IDX_BACK 3 diff --git a/main/font.h b/main/font.h index c5029e6..8000efb 100644 --- a/main/font.h +++ b/main/font.h @@ -64,26 +64,3 @@ static const uint8_t font5x8[][5] = { {0x61,0x51,0x49,0x45,0x43}, // 'Z' }; -static const uint8_t icons[NUM_MENUS][2][ICON_W] = { - // Heart (life) - { {0x00,0x70,0xF8,0xFC,0xFC,0xFC,0xF8,0xF0,0xF0,0xF8,0xFC,0xFC,0xFC,0xF8,0x70,0x00}, - {0x00,0x00,0x00,0x01,0x03,0x07,0x0F,0x3F,0x3F,0x0F,0x07,0x03,0x01,0x00,0x00,0x00} }, - // Shield (commander damage) - { {0x00,0xFC,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFC,0x00,0x00}, - {0x00,0x07,0x0F,0x1F,0x3F,0x3F,0x3F,0x7F,0x7F,0x3F,0x3F,0x1F,0x0F,0x07,0x00,0x00} }, - // Plus (counters) - { {0x00,0x00,0x00,0xC0,0xC0,0xC0,0xF8,0xF8,0xF8,0xF8,0xC0,0xC0,0xC0,0x00,0x00,0x00}, - {0x00,0x00,0x00,0x03,0x03,0x03,0x1F,0x1F,0x1F,0x1F,0x03,0x03,0x03,0x00,0x00,0x00} }, - // D6 showing 6-face: square outline + 6 dots in 2x3 arrangement - { {0x00,0xFE,0x02,0x02,0x9A,0x9A,0x02,0x02,0x02,0x02,0x9A,0x9A,0x02,0x02,0xFE,0x00}, - {0x00,0x7F,0x40,0x40,0x59,0x59,0x40,0x40,0x40,0x40,0x59,0x59,0x40,0x40,0x7F,0x00} }, - // Cog (settings) - { {0x00,0x80,0xDC,0xFC,0xFC,0xB8,0x9C,0xFE,0xFE,0x9C,0xB8,0xFC,0xFC,0xDC,0x80,0x00}, - {0x00,0x01,0x3B,0x3F,0x3F,0x1D,0x39,0x7F,0x7F,0x39,0x1D,0x3F,0x3F,0x3B,0x01,0x00} }, -}; - -// Signal bars shown on MENU_CMDR slot when BLE is enabled -static const uint8_t icon_net[2][ICON_W] = { - {0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0xF8,0xF8,0x00,0xFE,0xFE,0x00,0x00}, - {0x00,0x00,0x00,0x38,0x38,0x00,0x3F,0x3F,0x00,0x3F,0x3F,0x00,0x3F,0x3F,0x00,0x00}, -}; diff --git a/main/main.c b/main/main.c index 9721cbf..fb59a1e 100644 --- a/main/main.c +++ b/main/main.c @@ -3,6 +3,8 @@ #include "game.h" #include "draw.h" #include "ble.h" +#include "events.h" +#include "menus.h" #include "driver/gpio.h" #include "driver/i2c.h" #include "driver/uart.h" @@ -12,17 +14,20 @@ #include "esp_err.h" #include "esp_random.h" #include "esp_sleep.h" +#include "esp_timer.h" +#include "driver/rtc_io.h" #include "esp_adc/adc_oneshot.h" #include "esp_adc/adc_cali.h" #include "esp_adc/adc_cali_scheme.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/queue.h" +#include #include #include #include // ── Global state ────────────────────────────────────────────────────────────── -int g_sleep_mode = 0; int g_battery_pct = -1; int g_charging = 0; int g_batt_full = 0; @@ -48,41 +53,431 @@ int g_name_cursor; int g_life_select; // Settings -int g_brightness_pct = 10; -int g_start_life_index = 2; // 40 -int g_num_opponents = 3; +int g_brightness_pct = 10; +int g_start_life_index = 2; +int g_num_opponents = 3; char g_player_name[PLAYER_NAME_LEN + 1] = "PLAYER 1"; -int g_ble_enabled = 0; -uint8_t g_led_max = 26; -int g_game_id_cursor = 0; -uint8_t g_game_id[2] = {BLE_GAME_ID_0, BLE_GAME_ID_1}; -int g_menu_hold_ms = 500; -int g_lr_hold_ms = 500; -int g_display_flip = 0; +int g_ble_enabled = 0; +uint8_t g_led_max = 26; +int g_game_id_cursor = 0; +uint8_t g_game_id[2] = {BLE_GAME_ID_0, BLE_GAME_ID_1}; +int g_menu_hold_ms = 500; +int g_lr_hold_ms = 500; +int g_display_flip = 0; // Peers ble_peer_t g_peers[MAX_BLE_PEERS]; -// Timing -uint32_t g_tick; +// Battery blink state toggled by batt timer +bool g_batt_blink; // Life delta overlay -int g_life_delta = 0; -int g_life_delta_tick = 0; +int g_life_delta = 0; int g_delta_timeout_ms = 1000; // Dice int g_dice_num = 1; -int g_dice_sides = 5; // index into die_sides: d20 +int g_dice_sides = 5; int g_dice_item = 0; char g_dice_csv[DICE_CSV_LEN]; int g_dice_rolled = 0; int g_dice_sum = 0; +// ── Event queue + timer handles ─────────────────────────────────────────────── +static QueueHandle_t g_evt_queue; +static esp_timer_handle_t s_hold_timer[4]; +static esp_timer_handle_t s_combo_all4_timer; +static esp_timer_handle_t s_combo_fb_timer; +static esp_timer_handle_t s_batt_timer; +static esp_timer_handle_t s_peer_timer; +static esp_timer_handle_t s_autosave_timer; +static esp_timer_handle_t s_life_delta_timer; +static esp_timer_handle_t s_reset_cmd_timer; +static esp_timer_handle_t s_idle_timer; +static esp_timer_handle_t s_led_breathe_timer; + +// ── Button ISR state ────────────────────────────────────────────────────────── +static const int BTN_GPIOS[4] = { + BTN_FORWARD_GPIO, BTN_LEFT_GPIO, BTN_RIGHT_GPIO, BTN_BACK_GPIO +}; +static int64_t s_press_time_us[4]; +static int64_t s_last_event_us[4]; +static uint8_t s_pressed_mask; +static bool s_btn_in_combo[4]; + +// ── LED breathing state ─────────────────────────────────────────────────────── +static float s_breath_phase; +static bool s_breathe_active; + +// ── Misc ───────────────────────────────────────────────────────────────────── +static bool s_settings_dirty; +static int s_pre_settings_menu = 0; + +// ── RTC memory (survives deep sleep) ───────────────────────────────────────── +RTC_DATA_ATTR static bool s_rtc_valid; +RTC_DATA_ATTR static int s_rtc_life, s_rtc_eliminated; +RTC_DATA_ATTR static int s_rtc_cmdr_damage[MAX_OPPONENTS]; +RTC_DATA_ATTR static int s_rtc_counters[NUM_COUNTERS]; +RTC_DATA_ATTR static int s_rtc_active_menu, s_rtc_active_opponent; +RTC_DATA_ATTR static int s_rtc_active_counter, s_rtc_active_player; +RTC_DATA_ATTR static int s_rtc_active_setting, s_rtc_name_cursor; +RTC_DATA_ATTR static int s_rtc_life_select; +RTC_DATA_ATTR static int s_rtc_sleep_mode; +RTC_DATA_ATTR static int s_rtc_ble_pre_sleep; + +#define MARK_DIRTY() do { \ + s_settings_dirty = true; \ + esp_timer_stop(s_autosave_timer); \ + esp_timer_start_once(s_autosave_timer, (int64_t)SAVE_DELAY * 10000LL); \ +} while(0) + +#define RESTART_LIFE_DELTA() do { \ + esp_timer_stop(s_life_delta_timer); \ + esp_timer_start_once(s_life_delta_timer, (int64_t)g_delta_timeout_ms * 1000LL); \ +} while(0) + +void mark_dirty(void) { + s_settings_dirty = true; + esp_timer_stop(s_autosave_timer); + esp_timer_start_once(s_autosave_timer, (int64_t)SAVE_DELAY * 10000LL); +} + +void restart_life_delta_timer(void) { + esp_timer_stop(s_life_delta_timer); + esp_timer_start_once(s_life_delta_timer, (int64_t)g_delta_timeout_ms * 1000LL); +} + +void start_reset_cmd_timer(void) { + esp_timer_stop(s_reset_cmd_timer); + esp_timer_start_once(s_reset_cmd_timer, 15000000LL); +} + +void restart_idle_timer(void) { + if (g_sleep_timeout_min > 0) { + esp_timer_stop(s_idle_timer); + esp_timer_start_once(s_idle_timer, + (int64_t)g_sleep_timeout_min * 60LL * 1000000LL); + } +} + +// ── ADC battery helpers ─────────────────────────────────────────────────────── +static void sample_battery(void) { + static int raw_buf[100]; + static int raw_idx = 0; + static int raw_count = 0; + int raw; + adc_oneshot_read(s_adc, ADC_CHANNEL_6, &raw); + raw_buf[raw_idx] = raw; + raw_idx = (raw_idx + 1) % 100; + if (raw_count < 100) raw_count++; + int sum = 0; + for (int i = 0; i < raw_count; i++) sum += raw_buf[i]; + int avg_raw = sum / raw_count; + int voltage_mv; + if (s_adc_cali_ok) + adc_cali_raw_to_voltage(s_adc_cali, avg_raw, &voltage_mv); + else + voltage_mv = avg_raw * 3100 / 4095; + int comp_mv = g_ble_enabled ? BATT_LOAD_COMP_BLE_MV : BATT_LOAD_COMP_MV; + int cell_mv = voltage_mv * 2 + comp_mv; + static const int lipo_mv[] = {3000,3300,3400,3500,3600,3700,3800,3900,4000,4100,4200}; + static const int lipo_pct[] = { 0, 3, 8, 18, 34, 50, 62, 72, 81, 92, 100}; + static const int N = sizeof(lipo_mv) / sizeof(lipo_mv[0]); + int pct; + if (cell_mv <= lipo_mv[0]) pct = 0; + else if (cell_mv >= lipo_mv[N-1]) pct = 100; + else { + int i = 0; + while (i < N-2 && cell_mv >= lipo_mv[i+1]) i++; + pct = lipo_pct[i] + (cell_mv - lipo_mv[i]) * + (lipo_pct[i+1] - lipo_pct[i]) / (lipo_mv[i+1] - lipo_mv[i]); + } + g_battery_pct = pct; + oled_draw_header(); +} + +// ── LED breathing sync ──────────────────────────────────────────────────────── +void led_sync(bool changed) { + int bthresh = start_life_opts[g_start_life_index] / 4; + bool should = (g_life > 0 && g_life < bthresh); + if (should && !s_breathe_active) { + s_breathe_active = true; + esp_timer_start_periodic(s_led_breathe_timer, 10000); + } else if (!should && s_breathe_active) { + s_breathe_active = false; + s_breath_phase = 0.0f; + esp_timer_stop(s_led_breathe_timer); + if (g_life == 0) led_update_for_count(0, 255); + else if (changed) led_update_for_count(g_life, 255); + } else if (!should && changed) { + led_update_for_count(g_life, 255); + } +} + +// ── Button action helpers ───────────────────────────────────────────────────── +static void nav_short(int d) { + menus_nav_short(d); +} + +static void nav_long(int d) { + int last = NUM_MENU_SLOTS - 1; + if (g_active_menu == last) return; + int next = g_active_menu; + do { + next = (next + d + last) % last; + } while (menus[next] == NULL); + g_active_menu = next; + esp_timer_stop(s_hold_timer[BTN_IDX_LEFT]); + esp_timer_stop(s_hold_timer[BTN_IDX_RIGHT]); + oled_draw_header(); + menus_draw(); +} + +static void lr_delta(int d) { + menus_lr_delta(d); +} + +// ── Deep sleep ──────────────────────────────────────────────────────────────── +static void enter_deep_sleep(int manual, int ble_pre) { + s_rtc_valid = true; + s_rtc_life = g_life; + s_rtc_eliminated = g_eliminated; + memcpy(s_rtc_cmdr_damage, g_cmdr_damage, sizeof(g_cmdr_damage)); + memcpy(s_rtc_counters, g_counters, sizeof(g_counters)); + s_rtc_active_menu = g_active_menu; + s_rtc_active_opponent = g_active_opponent; + s_rtc_active_counter = g_active_counter; + s_rtc_active_player = g_active_player; + s_rtc_active_setting = g_active_setting; + s_rtc_name_cursor = g_name_cursor; + s_rtc_life_select = g_life_select; + s_rtc_sleep_mode = manual; + s_rtc_ble_pre_sleep = ble_pre; + + if (s_settings_dirty) settings_save(); + + for (int i = 0; i < 4; i++) esp_timer_stop(s_hold_timer[i]); + esp_timer_stop(s_combo_all4_timer); esp_timer_stop(s_combo_fb_timer); + esp_timer_stop(s_batt_timer); esp_timer_stop(s_peer_timer); + esp_timer_stop(s_autosave_timer); esp_timer_stop(s_life_delta_timer); + esp_timer_stop(s_reset_cmd_timer); esp_timer_stop(s_idle_timer); + esp_timer_stop(s_led_breathe_timer); + + g_ble_enabled = 0; + ble_adv_update(); + + if (manual) { + oled_draw_sleep(); + oled_set_contrast(SLEEP_CONTRAST); + } else { + oled_set_contrast(0); + } + led_off(); + + if (manual) { + // Wait for all buttons to release before sleeping, otherwise EXT1 ALL_LOW + // triggers immediately (all 4 are still held from the wakeup combo). + while (!gpio_get_level(BTN_FORWARD_GPIO) || !gpio_get_level(BTN_LEFT_GPIO) || + !gpio_get_level(BTN_RIGHT_GPIO) || !gpio_get_level(BTN_BACK_GPIO)) + vTaskDelay(pdMS_TO_TICKS(10)); + vTaskDelay(pdMS_TO_TICKS(50)); + + // Manual sleep: wake when all 4 pressed simultaneously (deliberate gesture). + uint64_t wake_mask = (1ULL << BTN_FORWARD_GPIO) | (1ULL << BTN_LEFT_GPIO) + | (1ULL << BTN_RIGHT_GPIO) | (1ULL << BTN_BACK_GPIO); + for (int i = 0; i < 4; i++) { + rtc_gpio_pullup_en(BTN_GPIOS[i]); + rtc_gpio_hold_en(BTN_GPIOS[i]); + } + esp_sleep_enable_ext1_wakeup(wake_mask, ESP_EXT1_WAKEUP_ALL_LOW); + } else { + // Auto sleep: wake on LEFT button press (GPIO2 is the only RTC GPIO + // among our 4 buttons that supports EXT0 single-pin wakeup). + rtc_gpio_pullup_en(BTN_LEFT_GPIO); + rtc_gpio_hold_en(BTN_LEFT_GPIO); + esp_sleep_enable_ext0_wakeup(BTN_LEFT_GPIO, 0); + } + esp_deep_sleep_start(); +} + +// ── Timer callbacks ─────────────────────────────────────────────────────────── +static void cb_hold(void *arg) { + int bi = (int)arg; + app_event_t e = {.type = EVT_HOLD_EXPIRE, .btn_idx = bi}; + xQueueSend(g_evt_queue, &e, 0); +} +static void cb_combo_all4(void *arg) { + (void)arg; + app_event_t e = {.type = EVT_COMBO_SLEEP}; + xQueueSend(g_evt_queue, &e, 0); +} +static void cb_combo_fb(void *arg) { + (void)arg; + app_event_t e = {.type = EVT_COMBO_SETTINGS}; + xQueueSend(g_evt_queue, &e, 0); +} +static void cb_batt(void *arg) { + (void)arg; + app_event_t e = {.type = EVT_BATT_TICK}; + xQueueSend(g_evt_queue, &e, 0); +} +static void cb_peer(void *arg) { + (void)arg; + app_event_t e = {.type = EVT_PEER_TICK}; + xQueueSend(g_evt_queue, &e, 0); +} +static void cb_autosave(void *arg) { + (void)arg; + app_event_t e = {.type = EVT_AUTOSAVE}; + xQueueSend(g_evt_queue, &e, 0); +} +static void cb_life_delta(void *arg) { + (void)arg; + app_event_t e = {.type = EVT_LIFE_DELTA_EXP}; + xQueueSend(g_evt_queue, &e, 0); +} +static void cb_reset_cmd(void *arg) { + (void)arg; + app_event_t e = {.type = EVT_RESET_CMD_EXP}; + xQueueSend(g_evt_queue, &e, 0); +} +static void cb_idle(void *arg) { + (void)arg; + app_event_t e = {.type = EVT_IDLE_SLEEP}; + xQueueSend(g_evt_queue, &e, 0); +} +static void cb_led_breathe(void *arg) { + (void)arg; + app_event_t e = {.type = EVT_LED_BREATHE}; + xQueueSend(g_evt_queue, &e, 0); +} + +// ── GPIO ISR ────────────────────────────────────────────────────────────────── +static void IRAM_ATTR btn_isr(void *arg) { + int gpio = (int)arg; + app_event_t e = { + .type = EVT_BUTTON, + .gpio = gpio, + .level = gpio_get_level(gpio), + .ts_us = esp_timer_get_time(), + }; + BaseType_t woken = pdFALSE; + xQueueSendFromISR(g_evt_queue, &e, &woken); + portYIELD_FROM_ISR(woken); +} + +// ── Button event handler ────────────────────────────────────────────────────── +static int gpio_to_btn(int gpio) { + for (int i = 0; i < 4; i++) + if (BTN_GPIOS[i] == gpio) return i; + return -1; +} + +static void handle_button(const app_event_t *e) { + int bi = gpio_to_btn(e->gpio); + if (bi < 0) return; + int64_t ts = e->ts_us; + + // Debounce: ignore events within 20ms of last event on same GPIO + if (ts - s_last_event_us[bi] < 20000) return; + s_last_event_us[bi] = ts; + + if (e->level == 0) { + // ── FALLING edge (button pressed) ──────────────────────────────────── + s_pressed_mask |= (uint8_t)(1u << bi); + s_press_time_us[bi] = ts; + restart_idle_timer(); + + // Check all-4 combo + if (s_pressed_mask == 0xF) { + for (int i = 0; i < 4; i++) { + s_btn_in_combo[i] = true; + esp_timer_stop(s_hold_timer[i]); + } + esp_timer_stop(s_combo_fb_timer); + esp_timer_stop(s_combo_all4_timer); + esp_timer_start_once(s_combo_all4_timer, + (int64_t)COMBO_HOLD_SLEEP * 10000LL); + return; + } + + // Check fwd+back combo + if ((s_pressed_mask & 0x9) == 0x9) { // bits 0 (fwd) and 3 (back) + s_btn_in_combo[BTN_IDX_FWD] = true; + s_btn_in_combo[BTN_IDX_BACK] = true; + esp_timer_stop(s_hold_timer[BTN_IDX_FWD]); + esp_timer_stop(s_hold_timer[BTN_IDX_BACK]); + esp_timer_stop(s_combo_fb_timer); + esp_timer_start_once(s_combo_fb_timer, + (int64_t)COMBO_HOLD_SETTINGS * 10000LL); + return; + } + + // Check left+right simultaneous: don't fire either + if ((s_pressed_mask & 0x6) == 0x6) { // bits 1 (left) and 2 (right) + esp_timer_stop(s_hold_timer[BTN_IDX_LEFT]); + esp_timer_stop(s_hold_timer[BTN_IDX_RIGHT]); + return; + } + + if (s_btn_in_combo[bi]) return; + + if (bi == BTN_IDX_LEFT || bi == BTN_IDX_RIGHT) { + // LR: fire delta immediately on press + lr_delta(bi == BTN_IDX_RIGHT ? 1 : -1); + // Start hold timer for repeat + esp_timer_stop(s_hold_timer[bi]); + esp_timer_start_once(s_hold_timer[bi], + (int64_t)g_lr_hold_ms * 1000LL); + } else { + // FWD/BACK: start hold timer; action dispatched on expire or release + esp_timer_stop(s_hold_timer[bi]); + esp_timer_start_once(s_hold_timer[bi], + (int64_t)g_menu_hold_ms * 1000LL); + } + + } else { + // ── RISING edge (button released) ──────────────────────────────────── + int64_t hold_ms = (ts - s_press_time_us[bi]) / 1000; + esp_timer_stop(s_hold_timer[bi]); + s_pressed_mask &= (uint8_t)~(1u << bi); + + // Cancel combo timers if conditions no longer met + if (s_pressed_mask != 0xF) + esp_timer_stop(s_combo_all4_timer); + if ((s_pressed_mask & 0x9) != 0x9) + esp_timer_stop(s_combo_fb_timer); + + bool was_in_combo = s_btn_in_combo[bi]; + s_btn_in_combo[bi] = false; + + if (was_in_combo) return; + + if ((bi == BTN_IDX_FWD || bi == BTN_IDX_BACK) && hold_ms < g_menu_hold_ms) + nav_short(bi == BTN_IDX_FWD ? 1 : -1); + // LR: no action on release (action fires on press and hold-repeat) + } +} + +// ── Hold expire handler ─────────────────────────────────────────────────────── +static void handle_hold_expire(int bi) { + if (!(s_pressed_mask & (1u << bi))) return; // released before timer fired + + if (bi == BTN_IDX_LEFT || bi == BTN_IDX_RIGHT) { + lr_delta(bi == BTN_IDX_RIGHT ? 1 : -1); + // Repeat at HOLD_REPEAT interval + esp_timer_start_once(s_hold_timer[bi], (int64_t)HOLD_REPEAT * 10000LL); + } else { + // FWD or BACK long press: cycle menu + nav_long(bi == BTN_IDX_FWD ? 1 : -1); + esp_timer_start_once(s_hold_timer[bi], + (int64_t)g_menu_hold_ms * 1000LL); + } +} + // ── Serial command task ─────────────────────────────────────────────────────── #ifdef DEBUG -static void serial_print_state(void) -{ +static void serial_print_state(void) { printf("DBG STATE life=%d poison=%u cmdr=[%d,%d,%d,%d] counters=[%d,%d,%d] menu=%d ble=%d eliminated=%d\n", g_life, (unsigned)g_counters[0], g_cmdr_damage[0], g_cmdr_damage[1], g_cmdr_damage[2], g_cmdr_damage[3], @@ -91,8 +486,7 @@ static void serial_print_state(void) fflush(stdout); } -static void serial_cmd_task(void *arg) -{ +static void serial_cmd_task(void *arg) { (void)arg; char buf[64]; int pos = 0; @@ -117,13 +511,15 @@ static void serial_cmd_task(void *arg) for (int i = 0; i < MAX_OPPONENTS; i++) { snprintf(fmt, sizeof(fmt), "cmdr%d=%%d", i); if (sscanf(kv, fmt, &val) == 1) { - g_cmdr_damage[i] = val < 0 ? 0 : val; check_elimination(); break; + g_cmdr_damage[i] = val < 0 ? 0 : val; + check_elimination(); break; } } for (int i = 0; i < NUM_COUNTERS; i++) { snprintf(fmt, sizeof(fmt), "counter%d=%%d", i); if (sscanf(kv, fmt, &val) == 1) { - g_counters[i] = val < 0 ? 0 : val; check_elimination(); break; + g_counters[i] = val < 0 ? 0 : val; + check_elimination(); break; } } } @@ -136,9 +532,7 @@ static void serial_cmd_task(void *arg) } else if (strcmp(buf, "CLEARNVS") == 0) { nvs_handle_t nvs; if (nvs_open(NVS_NS, NVS_READWRITE, &nvs) == ESP_OK) { - nvs_erase_all(nvs); - nvs_commit(nvs); - nvs_close(nvs); + nvs_erase_all(nvs); nvs_commit(nvs); nvs_close(nvs); } settings_reset_defaults(); game_reset(); @@ -161,9 +555,10 @@ void app_main(void) led_init(); i2c_config_t i2c_cfg = { - .mode=I2C_MODE_MASTER, .sda_io_num=I2C_SDA_GPIO, .scl_io_num=I2C_SCL_GPIO, - .sda_pullup_en=GPIO_PULLUP_ENABLE, .scl_pullup_en=GPIO_PULLUP_ENABLE, - .master.clk_speed=I2C_FREQ_HZ, + .mode = I2C_MODE_MASTER, + .sda_io_num = I2C_SDA_GPIO, .scl_io_num = I2C_SCL_GPIO, + .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = I2C_FREQ_HZ, }; ESP_ERROR_CHECK(i2c_param_config(I2C_PORT, &i2c_cfg)); ESP_ERROR_CHECK(i2c_driver_install(I2C_PORT, I2C_MODE_MASTER, 0, 0, 0)); @@ -174,6 +569,7 @@ void app_main(void) } g_life = start_life_opts[g_start_life_index]; settings_load(); + menus_init(); check_elimination(); g_led_max = (uint8_t)(255 * g_brightness_pct / 100); @@ -196,21 +592,28 @@ void app_main(void) oled_set_flip(g_display_flip); oled_clear(); + // Button GPIO config: ANYEDGE ISRs gpio_config_t gpio_cfg = { .pin_bit_mask = (1ULL<= NUM_MENU_SLOTS || + (menus[g_active_menu] == NULL && g_active_menu != NUM_MENU_SLOTS - 1)) + g_active_menu = 0; + if (g_ble_enabled) ble_adv_update(); + } + oled_draw_header(); - oled_draw_life(); - led_update_for_count(g_life, 255); + menus_draw(); + led_sync(true); #ifdef DEBUG serial_print_state(); #endif - int prev_fwd=1, prev_right=1, prev_left=1, prev_back=1; - int hold_fwd=0, hold_back=0, hold_right=0, hold_left=0; - int combo_tick=0, combo_fired_hold=0; - int fwd_in_combo=0, back_in_combo=0; - int fwd_menu_fired=0, back_menu_fired=0; - // Require 2 consecutive released ticks before arming hold; prevents accidental - // hold fires when rapid taps have sub-10ms releases that the sampler misses. - int release_r=2, release_l=2; - int hold_armed_r=1, hold_armed_l=1; - int players_tick=0; - int settings_dirty=0, settings_save_tick=0; - int sleep_tick=0, sleep_fired=0, ble_pre_sleep=0; - uint32_t idle_ticks=0, batt_tick=0; - - gpio_wakeup_enable(BTN_FORWARD_GPIO, GPIO_INTR_LOW_LEVEL); - gpio_wakeup_enable(BTN_LEFT_GPIO, GPIO_INTR_LOW_LEVEL); - gpio_wakeup_enable(BTN_RIGHT_GPIO, GPIO_INTR_LOW_LEVEL); - gpio_wakeup_enable(BTN_BACK_GPIO, GPIO_INTR_LOW_LEVEL); - esp_sleep_enable_gpio_wakeup(); -#define MARK_DIRTY() do { settings_dirty = 1; settings_save_tick = 0; } while (0) - float breath_phase=0.0f; - - char opponent_labels[MAX_OPPONENTS][PLAYER_NAME_LEN+1]; - const char *opponent_label_ptrs[MAX_OPPONENTS]; - - while (1) { - g_tick++; - int menu_ticks = g_menu_hold_ms / 10; - int lr_ticks = g_lr_hold_ms / 10; - int fwd = gpio_get_level(BTN_FORWARD_GPIO); - int right = gpio_get_level(BTN_RIGHT_GPIO); - int left = gpio_get_level(BTN_LEFT_GPIO); - int back = gpio_get_level(BTN_BACK_GPIO); - int changed = 0; - - // ── Combo: all 4 buttons → sleep toggle ────────────────────────────── - int all4 = (fwd==0 && back==0 && left==0 && right==0); - if (all4) { - sleep_tick++; - if (!sleep_fired && sleep_tick == COMBO_HOLD_SLEEP) { - sleep_fired = 1; - hold_fwd = hold_back = hold_left = hold_right = 0; - combo_tick = 0; combo_fired_hold = 0; - if (!g_sleep_mode) { - g_sleep_mode = 1; - ble_pre_sleep = g_ble_enabled; - oled_draw_sleep(); - oled_set_contrast(SLEEP_CONTRAST); - led_off(); - if (g_ble_enabled) { g_ble_enabled = 0; ble_adv_update(); } + // ── Main event loop ─────────────────────────────────────────────────────── + app_event_t evt; + for (;;) { + xQueueReceive(g_evt_queue, &evt, portMAX_DELAY); + + switch (evt.type) { + + // ── Button edge ─────────────────────────────────────────────────────── + case EVT_BUTTON: + handle_button(&evt); + break; + + // ── Hold timer expired ──────────────────────────────────────────────── + case EVT_HOLD_EXPIRE: + handle_hold_expire(evt.btn_idx); + break; + + // ── All-4 sleep combo ───────────────────────────────────────────────── + case EVT_COMBO_SLEEP: + if (s_pressed_mask != 0xF) break; + enter_deep_sleep(1, g_ble_enabled); + break; // not reached + + // ── Fwd+back settings combo ─────────────────────────────────────────── + case EVT_COMBO_SETTINGS: + if ((s_pressed_mask & 0x9) != 0x9) break; + esp_timer_stop(s_hold_timer[BTN_IDX_LEFT]); + esp_timer_stop(s_hold_timer[BTN_IDX_RIGHT]); + { + int last = NUM_MENU_SLOTS - 1; + if (g_active_menu == last) { + g_active_menu = s_pre_settings_menu; } else { - g_sleep_mode = 0; - oled_set_contrast(0xCF); - g_ble_enabled = ble_pre_sleep; - if (g_ble_enabled) ble_adv_update(); - oled_draw_header(); - breath_phase = 0.0f; - led_update_for_count(g_life, 255); - changed = 1; + s_pre_settings_menu = g_active_menu; + g_active_menu = last; + g_active_setting = 0; + g_name_cursor = 0; + g_game_id_cursor = 0; } } - } else { - sleep_tick = 0; sleep_fired = 0; - } - - // ── Sleep mode: skip all further processing ─────────────────────────── - if (g_sleep_mode) { - prev_fwd = fwd; prev_right = right; prev_left = left; prev_back = back; - vTaskDelay(pdMS_TO_TICKS(10)); - continue; - } + oled_draw_header(); + menus_draw(); + break; - // ── Combo: FORWARD + BACK simultaneously (settings toggle) ────────── - int both = (fwd == 0 && back == 0); - - if (both) { - hold_fwd = hold_back = 0; - fwd_in_combo = back_in_combo = 1; - fwd_menu_fired = back_menu_fired = 0; - combo_tick++; - if (!combo_fired_hold && combo_tick == COMBO_HOLD_SETTINGS) { - if (g_active_menu == MENU_SETTINGS) { - g_active_menu = 0; - } else { - g_active_menu = MENU_SETTINGS; - g_active_setting = 0; - g_name_cursor = 0; - g_game_id_cursor = 0; - } - combo_fired_hold = 1; - hold_right = hold_left = players_tick = 0; + // ── Battery tick ────────────────────────────────────────────────────── + case EVT_BATT_TICK: { + // Charging/full GPIO state + int charging = !gpio_get_level(BATT_CHARGE_GPIO); + int batt_full = !gpio_get_level(BATT_FULL_GPIO); + if (charging != g_charging || batt_full != g_batt_full) { + g_charging = charging; + g_batt_full = batt_full; oled_draw_header(); - changed = 1; } - } else { - combo_tick = 0; - combo_fired_hold = 0; - - // ── FORWARD: long press = next menu (+ repeat), short press = sub-item fwd ── - if (fwd == 0) { - hold_fwd++; - if (g_active_menu != MENU_SETTINGS && !fwd_in_combo) { - if (hold_fwd == menu_ticks || - (hold_fwd > menu_ticks && (hold_fwd - menu_ticks) % menu_ticks == 0)) { - g_active_menu = (g_active_menu + 1) % (NUM_MENUS - 1); - hold_right = hold_left = players_tick = 0; - oled_draw_header(); - changed = 1; - fwd_menu_fired = 1; - } - } - } else { - if (prev_fwd == 0 && !fwd_in_combo && !fwd_menu_fired) { - if (hold_fwd < menu_ticks) { - if (g_active_menu == MENU_SETTINGS) { - if (g_active_setting == SET_PLAYER_NAME) { - g_name_cursor++; - if (g_name_cursor >= PLAYER_NAME_LEN) { - g_name_cursor = 0; - g_active_setting = (g_active_setting + 1) % NUM_SETTINGS; - g_game_id_cursor = 0; - } - } else if (g_active_setting == SET_GAME_ID) { - g_game_id_cursor++; - if (g_game_id_cursor >= GAME_ID_DIGITS) { - g_game_id_cursor = 0; - g_active_setting = (g_active_setting + 1) % NUM_SETTINGS; - } - } else { - g_active_setting = (g_active_setting + 1) % NUM_SETTINGS; - g_name_cursor = 0; - g_game_id_cursor = 0; - } - changed = 1; - } else if (g_active_menu == MENU_CMDR) { - if (g_ble_enabled) { - int next = (g_active_player + 1) % (MAX_BLE_PEERS + 1); - while (next != g_active_player && next != 0 && !g_peers[next - 1].active) - next = (next + 1) % (MAX_BLE_PEERS + 1); - g_active_player = next; - } else { - g_active_opponent = (g_active_opponent + 1) % g_num_opponents; - } - changed = 1; - } else if (g_active_menu == MENU_COUNTERS) { - g_active_counter = (g_active_counter + 1) % NUM_COUNTERS; - changed = 1; - } else if (g_active_menu == MENU_DICE) { - g_dice_item = (g_dice_item + 1) % 3; - changed = 1; - } else if (g_active_menu == MENU_LIFE) { - int nc = g_ble_enabled ? 0 : g_num_opponents; - if (g_ble_enabled) - for (int i = 0; i < MAX_BLE_PEERS; i++) - if (g_peers[i].active) nc++; - g_life_select = (g_life_select + 1) % (3 + nc); - changed = 1; - } - } - } - hold_fwd = 0; - fwd_in_combo = 0; - fwd_menu_fired = 0; - } - - // ── BACK: long press = prev menu (+ repeat), short press = sub-item back ── - if (back == 0) { - hold_back++; - if (g_active_menu != MENU_SETTINGS && !back_in_combo) { - if (hold_back == menu_ticks || - (hold_back > menu_ticks && (hold_back - menu_ticks) % menu_ticks == 0)) { - g_active_menu = (g_active_menu - 1 + (NUM_MENUS - 1)) % (NUM_MENUS - 1); - hold_right = hold_left = players_tick = 0; - oled_draw_header(); - changed = 1; - back_menu_fired = 1; - } - } - } else { - if (prev_back == 0 && !back_in_combo && !back_menu_fired) { - if (hold_back < menu_ticks) { - if (g_active_menu == MENU_SETTINGS) { - if (g_active_setting == SET_PLAYER_NAME) { - if (g_name_cursor > 0) { - g_name_cursor--; - } else { - g_active_setting = (g_active_setting - 1 + NUM_SETTINGS) % NUM_SETTINGS; - g_name_cursor = (g_active_setting == SET_PLAYER_NAME) ? PLAYER_NAME_LEN - 1 : 0; - g_game_id_cursor = (g_active_setting == SET_GAME_ID) ? GAME_ID_DIGITS - 1 : 0; - } - } else if (g_active_setting == SET_GAME_ID) { - if (g_game_id_cursor > 0) { - g_game_id_cursor--; - } else { - g_active_setting = (g_active_setting - 1 + NUM_SETTINGS) % NUM_SETTINGS; - g_name_cursor = (g_active_setting == SET_PLAYER_NAME) ? PLAYER_NAME_LEN - 1 : 0; - g_game_id_cursor = (g_active_setting == SET_GAME_ID) ? GAME_ID_DIGITS - 1 : 0; - } - } else { - g_active_setting = (g_active_setting - 1 + NUM_SETTINGS) % NUM_SETTINGS; - g_name_cursor = (g_active_setting == SET_PLAYER_NAME) ? PLAYER_NAME_LEN - 1 : 0; - g_game_id_cursor = (g_active_setting == SET_GAME_ID) ? GAME_ID_DIGITS - 1 : 0; - } - changed = 1; - } else if (g_active_menu == MENU_CMDR) { - if (g_ble_enabled) { - int total = MAX_BLE_PEERS + 1; - int next = (g_active_player - 1 + total) % total; - while (next != g_active_player && next != 0 && !g_peers[next - 1].active) - next = (next - 1 + total) % total; - g_active_player = next; - } else { - g_active_opponent = (g_active_opponent - 1 + g_num_opponents) % g_num_opponents; - } - changed = 1; - } else if (g_active_menu == MENU_COUNTERS) { - g_active_counter = (g_active_counter - 1 + NUM_COUNTERS) % NUM_COUNTERS; - changed = 1; - } else if (g_active_menu == MENU_DICE) { - g_dice_item = (g_dice_item - 1 + 3) % 3; - changed = 1; - } else if (g_active_menu == MENU_LIFE) { - int nc = g_ble_enabled ? 0 : g_num_opponents; - if (g_ble_enabled) - for (int i = 0; i < MAX_BLE_PEERS; i++) - if (g_peers[i].active) nc++; - int total = 3 + nc; - g_life_select = (g_life_select - 1 + total) % total; - changed = 1; - } - } - } - hold_back = 0; - back_in_combo = 0; - back_menu_fired = 0; - } - } - - // ── delta[0]: value change (right / left) ──────────────────────────── - int delta_r=0, delta_l=0; - if (right==0 && left==0) { - // both held: reset counters so they stay in sync and neither fires - hold_right=0; hold_left=0; release_r=0; release_l=0; - } else { - if (right==0) { - if (prev_right==1) { - delta_r=1; hold_right=0; - hold_armed_r = (release_r >= 2); release_r=0; - } else if (hold_armed_r && ++hold_right>=lr_ticks - && (hold_right-lr_ticks)%HOLD_REPEAT==0) { - delta_r=1; - } - } else { hold_right=0; if (release_r<2) release_r++; } - if (left==0) { - if (prev_left==1) { - delta_l=1; hold_left=0; - hold_armed_l = (release_l >= 2); release_l=0; - } else if (hold_armed_l && ++hold_left>=lr_ticks - && (hold_left-lr_ticks)%HOLD_REPEAT==0) { - delta_l=1; - } - } else { hold_left=0; if (release_l<2) release_l++; } - } - - if (delta_r || delta_l) { - int d = delta_r - delta_l; - if (g_active_menu == MENU_LIFE) { - if (g_life_select == 0) { - g_life += d; - g_life_delta += d; - g_life_delta_tick = 0; - check_elimination(); - } else if (g_life_select == 1) { - g_counters[COUNTER_STORM] += d; - if (g_counters[COUNTER_STORM] < 0) g_counters[COUNTER_STORM] = 0; - } else if (g_life_select == 2) { - g_counters[COUNTER_POISON] += d; - if (g_counters[COUNTER_POISON] < 0) g_counters[COUNTER_POISON] = 0; - check_elimination(); - } else { - int si = g_life_select - 3; - if (g_ble_enabled) { - int found = 0; - for (int i = 0; i < MAX_BLE_PEERS; i++) { - if (g_peers[i].active && found++ == si) { - g_cmdr_damage[i] += d; - if (g_cmdr_damage[i] < 0) g_cmdr_damage[i] = 0; - check_elimination(); - break; - } - } - } else if (si < g_num_opponents) { - g_cmdr_damage[si] += d; - if (g_cmdr_damage[si] < 0) g_cmdr_damage[si] = 0; - check_elimination(); - } - } - changed = 1; MARK_DIRTY(); - } else if (g_active_menu == MENU_CMDR) { - if (g_ble_enabled) { - if (g_active_player == 0) { - g_life += d; - g_life_delta += d; - g_life_delta_tick = 0; - check_elimination(); - } else { - int slot = g_active_player - 1; - g_cmdr_damage[slot] += d; - if (g_cmdr_damage[slot] < 0) g_cmdr_damage[slot] = 0; - check_elimination(); - } - } else { - g_cmdr_damage[g_active_opponent] += d; - if (g_cmdr_damage[g_active_opponent] < 0) g_cmdr_damage[g_active_opponent] = 0; - check_elimination(); - } - changed = 1; MARK_DIRTY(); - } else if (g_active_menu == MENU_COUNTERS) { - g_counters[g_active_counter] += d; - if (g_counters[g_active_counter] < 0) g_counters[g_active_counter] = 0; - changed = 1; MARK_DIRTY(); check_elimination(); - } else if (g_active_menu == MENU_DICE) { - if (g_dice_item == 0) { - g_dice_num += d; - if (g_dice_num < 1) g_dice_num = 1; - } else if (g_dice_item == 1) { - g_dice_sides = (g_dice_sides + d + NUM_DICE_SIDES) % NUM_DICE_SIDES; - } else { - int sides = die_sides[g_dice_sides]; - g_dice_sum = 0; - int pos = 0; - g_dice_csv[0] = '\0'; - for (int i = 0; i < g_dice_num; i++) { - int roll = (int)(esp_random() % (uint32_t)sides) + 1; - g_dice_sum += roll; - if (pos < DICE_CSV_LEN - 5) { - if (i > 0) g_dice_csv[pos++] = ','; - pos += snprintf(g_dice_csv + pos, DICE_CSV_LEN - pos, "%d", roll); - } - } - g_dice_rolled = 1; - } - changed = 1; - } else if (g_active_menu == MENU_SETTINGS) { - changed = 1; MARK_DIRTY(); - switch (g_active_setting) { - case SET_BRIGHTNESS: - g_brightness_pct += d; - if (g_brightness_pct < BRIGHTNESS_MIN) g_brightness_pct = BRIGHTNESS_MIN; - if (g_brightness_pct > BRIGHTNESS_MAX) g_brightness_pct = BRIGHTNESS_MAX; - g_led_max = (uint8_t)(255 * g_brightness_pct / 100); - break; - case SET_START_LIFE: - g_start_life_index += d; - if (g_start_life_index < 0) g_start_life_index = 0; - if (g_start_life_index >= NUM_LIFE_OPTS) g_start_life_index = NUM_LIFE_OPTS-1; - break; - case SET_NUM_OPP: - g_num_opponents += d; - if (g_num_opponents < 1) g_num_opponents = 1; - if (g_num_opponents > MAX_OPPONENTS) g_num_opponents = MAX_OPPONENTS; - if (g_active_opponent >= g_num_opponents) g_active_opponent = 0; - break; - case SET_PLAYER_NAME: { - char curr = g_player_name[g_name_cursor]; - const char *pos = strchr(NAME_CHARS, curr); - int idx = pos ? (int)(pos - NAME_CHARS) : 0; - idx = (idx + d + NUM_NAME_CHARS) % NUM_NAME_CHARS; - g_player_name[g_name_cursor] = NAME_CHARS[idx]; - break; - } - case SET_BLE: - g_ble_enabled += d; - if (g_ble_enabled < 0) g_ble_enabled = 0; - if (g_ble_enabled > 1) g_ble_enabled = 1; - ble_adv_update(); - oled_draw_header(); - break; - case SET_GAME_ID: { - int byte = g_game_id_cursor / 2; - int nibble = g_game_id_cursor % 2; - int val = nibble == 0 ? (g_game_id[byte] >> 4) & 0xF : g_game_id[byte] & 0xF; - val = (val + d + NUM_HEX_CHARS) % NUM_HEX_CHARS; - if (nibble == 0) - g_game_id[byte] = (g_game_id[byte] & 0x0F) | ((uint8_t)val << 4); - else - g_game_id[byte] = (g_game_id[byte] & 0xF0) | (uint8_t)val; - ble_adv_update(); - break; - } - case SET_RESET: - game_reset(); - ble_adv_update(); - break; - case SET_RESET_ALL: - game_reset(); - g_reset_cmd_ticks = 1500; - ble_adv_update(); - break; - case SET_MENU_HOLD: - g_menu_hold_ms += d * 10; - if (g_menu_hold_ms < HOLD_MS_MIN) g_menu_hold_ms = HOLD_MS_MIN; - if (g_menu_hold_ms > HOLD_MS_MAX) g_menu_hold_ms = HOLD_MS_MAX; - break; - case SET_LR_HOLD: - g_lr_hold_ms += d * 10; - if (g_lr_hold_ms < HOLD_MS_MIN) g_lr_hold_ms = HOLD_MS_MIN; - if (g_lr_hold_ms > HOLD_MS_MAX) g_lr_hold_ms = HOLD_MS_MAX; - break; - case SET_DISPLAY_FLIP: - g_display_flip ^= 1; - oled_set_flip(g_display_flip); - break; - case SET_DELTA_TIMEOUT: - g_delta_timeout_ms += d * 100; - if (g_delta_timeout_ms < DELTA_TIMEOUT_MIN) g_delta_timeout_ms = DELTA_TIMEOUT_MIN; - if (g_delta_timeout_ms > DELTA_TIMEOUT_MAX) g_delta_timeout_ms = DELTA_TIMEOUT_MAX; - break; - case SET_AUTO_SLEEP: - g_sleep_timeout_min += d; - if (g_sleep_timeout_min < 0) g_sleep_timeout_min = 0; - if (g_sleep_timeout_min > SLEEP_TIMEOUT_MAX) g_sleep_timeout_min = SLEEP_TIMEOUT_MAX; - idle_ticks = 0; - break; - } + // Low battery blink + if (g_battery_pct >= 0 && g_battery_pct < BATT_LOW_PCT) { + g_batt_blink = !g_batt_blink; + oled_draw_header(); } + // ADC sample + sample_battery(); + break; } - // ── Remote reset command ────────────────────────────────────────────── - if (g_reset_requested) { - g_reset_requested = 0; - game_reset(); - MARK_DIRTY(); - changed = 1; - ble_adv_update(); - } - - // ── reset_cmd broadcast countdown ──────────────────────────────────── - if (g_reset_cmd_ticks > 0 && --g_reset_cmd_ticks == 0) ble_adv_update(); - - // ── Peer expiry + player ID resolution ─────────────────────────────── - if (g_tick % 100 == 0) { + // ── Peer expiry + players refresh ───────────────────────────────────── + case EVT_PEER_TICK: for (int i = 0; i < MAX_BLE_PEERS; i++) { - if (g_peers[i].active && g_tick - g_peers[i].last_seen > BLE_PEER_TIMEOUT) { + if (g_peers[i].active && + esp_timer_get_time() - g_peers[i].last_seen > + (int64_t)BLE_PEER_TIMEOUT * 1000LL) { #ifdef DEBUG printf("DBG PEER_EXPIRE slot=%d name=%-8.8s\n", i, g_peers[i].name); fflush(stdout); @@ -679,169 +786,63 @@ void app_main(void) if (g_active_menu == MENU_LIFE && g_life_select >= 3 && g_ble_enabled) { int slot = 0; for (int j = 0; j < i; j++) if (g_peers[j].active) slot++; - if (g_life_select - 3 == slot) { g_life_select = 0; changed = 1; } + if (g_life_select - 3 == slot) { g_life_select = 0; menus_draw(); } } g_peers[i].active = 0; } } - } - - // ── Autosave settings ───────────────────────────────────────────────── - if (settings_dirty && ++settings_save_tick >= SAVE_DELAY) { - settings_save(); settings_dirty = 0; settings_save_tick = 0; - } - - // ── Life delta timeout ──────────────────────────────────────────────── - if (g_life_delta != 0) { - if (++g_life_delta_tick >= g_delta_timeout_ms / 10) { - g_life_delta = 0; - g_life_delta_tick = 0; - if (g_active_menu == MENU_LIFE) changed = 1; - } - } - - // ── BLE adv update on game state change ─────────────────────────────── - if (changed && (g_active_menu==MENU_LIFE || g_active_menu==MENU_COUNTERS || - g_active_menu==MENU_CMDR)) { - ble_adv_update(); - } - - // ── Players periodic refresh ────────────────────────────────────────── - if (g_active_menu == MENU_CMDR && g_ble_enabled) { - if (++players_tick >= 100) { - players_tick = 0; oled_draw_players(); - } - } else { players_tick = 0; } - - // ── LED ─────────────────────────────────────────────────────────────── - int bthresh = start_life_opts[g_start_life_index] / 4; - if (g_life == 0) { - led_update_for_count(0, 255); - } else if (g_life < bthresh) { - float speed = 1.0f + ((float)bthresh - g_life) / (float)(bthresh - 1) * 2.0f; - breath_phase += 2.0f * 3.14159265f * speed / 200.0f; - uint8_t sc = (uint8_t)(127.5f + 127.5f * sinf(breath_phase)); - led_update_for_count(g_life, sc); - } else if (changed) { - led_update_for_count(g_life, 255); - } - - // ── Battery voltage sampling ────────────────────────────────────────── - if (g_battery_pct >= 0 && g_battery_pct < BATT_LOW_PCT && g_tick % 50 == 0) - oled_draw_header(); - - // Update charging/full state every 50 ticks (0.5s); redraw header on change - if (g_tick % 50 == 0) { - int charging = !gpio_get_level(BATT_CHARGE_GPIO); - int batt_full = !gpio_get_level(BATT_FULL_GPIO); - if (charging != g_charging || batt_full != g_batt_full) { - g_charging = charging; - g_batt_full = batt_full; + if (g_active_menu == MENU_CMDR && g_ble_enabled) + oled_draw_players(); + // Handle pending remote reset + if (g_reset_requested) { + g_reset_requested = 0; + game_reset(); + MARK_DIRTY(); + ble_adv_update(); oled_draw_header(); + menus_draw(); + led_sync(true); } - } + break; - if (++batt_tick >= BATT_SAMPLE_TICKS) { - batt_tick = 0; - static int batt_raw_buf[100]; - static int batt_raw_idx = 0; - static int batt_raw_count = 0; - int raw; - adc_oneshot_read(s_adc, ADC_CHANNEL_6, &raw); - batt_raw_buf[batt_raw_idx] = raw; - batt_raw_idx = (batt_raw_idx + 1) % 100; - if (batt_raw_count < 100) batt_raw_count++; - int sum = 0; - for (int i = 0; i < batt_raw_count; i++) sum += batt_raw_buf[i]; - int avg_raw = sum / batt_raw_count; - int voltage_mv; - if (s_adc_cali_ok) - adc_cali_raw_to_voltage(s_adc_cali, avg_raw, &voltage_mv); - else - voltage_mv = avg_raw * 3100 / 4095; - int comp_mv = g_ble_enabled ? BATT_LOAD_COMP_BLE_MV : BATT_LOAD_COMP_MV; - int cell_mv = voltage_mv * 2 + comp_mv; // 100k/100k divider + load compensation - // LiPo discharge curve: piecewise linear interpolation. - // Voltage stays flat ~3.7-4.2V through most of charge; linear - // mapping reads too high. Table derived from typical LiPo OCV curve. - static const int lipo_mv[] = {3000,3300,3400,3500,3600,3700,3800,3900,4000,4100,4200}; - static const int lipo_pct[] = { 0, 3, 8, 18, 34, 50, 62, 72, 81, 92, 100}; - static const int N = sizeof(lipo_mv)/sizeof(lipo_mv[0]); - int pct; - if (cell_mv <= lipo_mv[0]) { - pct = 0; - } else if (cell_mv >= lipo_mv[N-1]) { - pct = 100; - } else { - int i = 0; - while (i < N-2 && cell_mv >= lipo_mv[i+1]) i++; - pct = lipo_pct[i] + (cell_mv - lipo_mv[i]) * - (lipo_pct[i+1] - lipo_pct[i]) / (lipo_mv[i+1] - lipo_mv[i]); + // ── Autosave ────────────────────────────────────────────────────────── + case EVT_AUTOSAVE: + if (s_settings_dirty) { + settings_save(); + s_settings_dirty = false; } - g_battery_pct = pct; - oled_draw_header(); - } + break; - // ── Idle tracking and auto-sleep ────────────────────────────────────── - if (fwd == 0 || back == 0 || left == 0 || right == 0) - idle_ticks = 0; - else - idle_ticks++; - - if (!g_sleep_mode && g_sleep_timeout_min > 0 && - idle_ticks >= (uint32_t)g_sleep_timeout_min * 60 * 100) { - idle_ticks = 0; - if (settings_dirty) { settings_save(); settings_dirty = 0; settings_save_tick = 0; } - oled_set_contrast(SLEEP_CONTRAST); - led_off(); - int ble_pre_auto = g_ble_enabled; - if (g_ble_enabled) { g_ble_enabled = 0; ble_adv_update(); } - esp_light_sleep_start(); - // woke up — restore - oled_set_contrast(0xCF); - g_ble_enabled = ble_pre_auto; - if (g_ble_enabled) ble_adv_update(); - breath_phase = 0.0f; - led_update_for_count(g_life, 255); - oled_draw_header(); - changed = 1; - } - - // ── Display dispatch ────────────────────────────────────────────────── - if (changed) { - switch (g_active_menu) { - case MENU_LIFE: - oled_draw_life(); - break; - case MENU_CMDR: - if (!g_ble_enabled) { - for (int i = 0; i < g_num_opponents; i++) { - snprintf(opponent_labels[i], sizeof(opponent_labels[i]), "CMD %d", i+1); - opponent_label_ptrs[i] = opponent_labels[i]; - } - oled_draw_list(opponent_label_ptrs, g_cmdr_damage, g_num_opponents, g_active_opponent); - } else { - oled_draw_players(); - } - break; - case MENU_COUNTERS: - oled_draw_list(counter_names, g_counters, NUM_COUNTERS, g_active_counter); - break; - case MENU_DICE: - oled_draw_dice(); - break; - case MENU_SETTINGS: - oled_draw_settings(); - break; + // ── Life delta timeout ──────────────────────────────────────────────── + case EVT_LIFE_DELTA_EXP: + if (menus[MENU_LIFE] && menus[MENU_LIFE]->on_tick) + menus[MENU_LIFE]->on_tick(&evt); + break; + + // ── Reset cmd broadcast end ─────────────────────────────────────────── + case EVT_RESET_CMD_EXP: + g_reset_cmd_ticks = 0; + ble_adv_update(); + break; + + // ── Idle sleep ──────────────────────────────────────────────────────── + case EVT_IDLE_SLEEP: + enter_deep_sleep(0, g_ble_enabled); + break; // not reached + + // ── LED breathing frame ─────────────────────────────────────────────── + case EVT_LED_BREATHE: { + int bthresh = start_life_opts[g_start_life_index] / 4; + if (g_life > 0 && g_life < bthresh) { + float speed = 1.0f + + ((float)bthresh - g_life) / (float)(bthresh - 1) * 2.0f; + s_breath_phase += 2.0f * 3.14159265f * speed / 200.0f; + uint8_t sc = (uint8_t)(127.5f + 127.5f * sinf(s_breath_phase)); + led_update_for_count(g_life, sc); } + break; } -#ifdef DEBUG - if (changed || g_tick % 1000 == 0) - serial_print_state(); -#endif - - prev_fwd = fwd; prev_right = right; prev_left = left; prev_back = back; - vTaskDelay(pdMS_TO_TICKS(10)); - } + } // switch + } // for(;;) } diff --git a/main/menu_cmdr.c b/main/menu_cmdr.c new file mode 100644 index 0000000..23b0d15 --- /dev/null +++ b/main/menu_cmdr.c @@ -0,0 +1,95 @@ +#include "menu_cmdr.h" +#include "menus.h" +#include "config.h" +#include "state.h" +#include "draw.h" +#include "game.h" +#include "ble.h" +#include "events.h" +#include + +// ── Offline vtable ──────────────────────────────────────────────────────────── + +static const uint8_t s_icon_shield[2][ICON_W] = { + // Shield (commander damage) + {0x00,0xFC,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFC,0x00,0x00}, + {0x00,0x07,0x0F,0x1F,0x3F,0x3F,0x3F,0x7F,0x7F,0x3F,0x3F,0x1F,0x0F,0x07,0x00,0x00}, +}; + +static const uint8_t s_icon_net[2][ICON_W] = { + // Signal bars (BLE commander) + {0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0xF8,0xF8,0x00,0xFE,0xFE,0x00,0x00}, + {0x00,0x00,0x00,0x38,0x38,0x00,0x3F,0x3F,0x00,0x3F,0x3F,0x00,0x3F,0x3F,0x00,0x00}, +}; + +static void cmdr_offline_nav_short(int d) +{ + g_active_opponent = (g_active_opponent + d + g_num_opponents) % g_num_opponents; +} + +static void cmdr_offline_lr_delta(int d) +{ + g_cmdr_damage[g_active_opponent] += d; + if (g_cmdr_damage[g_active_opponent] < 0) g_cmdr_damage[g_active_opponent] = 0; + check_elimination(); + mark_dirty(); +} + +static void cmdr_offline_draw(void) +{ + char opponent_labels[MAX_OPPONENTS][PLAYER_NAME_LEN + 1]; + const char *opponent_label_ptrs[MAX_OPPONENTS]; + for (int i = 0; i < g_num_opponents; i++) { + snprintf(opponent_labels[i], sizeof(opponent_labels[i]), "CMD %d", i + 1); + opponent_label_ptrs[i] = opponent_labels[i]; + } + oled_draw_list(opponent_label_ptrs, g_cmdr_damage, g_num_opponents, g_active_opponent); +} + +const menu_vtable_t menu_cmdr_offline_vtable = { + .nav_short = cmdr_offline_nav_short, + .lr_delta = cmdr_offline_lr_delta, + .draw = cmdr_offline_draw, + .on_tick = NULL, + .icon = s_icon_shield, +}; + +// ── BLE vtable ──────────────────────────────────────────────────────────────── + +static void cmdr_ble_nav_short(int d) +{ + int total = MAX_BLE_PEERS + 1; + int next = (g_active_player + d + total) % total; + while (next != g_active_player && next != 0 && !g_peers[next - 1].active) + next = (next + d + total) % total; + g_active_player = next; +} + +static void cmdr_ble_lr_delta(int d) +{ + if (g_active_player == 0) { + g_life += d; + g_life_delta += d; + restart_life_delta_timer(); + check_elimination(); + } else { + int slot = g_active_player - 1; + g_cmdr_damage[slot] += d; + if (g_cmdr_damage[slot] < 0) g_cmdr_damage[slot] = 0; + check_elimination(); + } + mark_dirty(); +} + +static void cmdr_ble_draw(void) +{ + oled_draw_players(); +} + +const menu_vtable_t menu_cmdr_ble_vtable = { + .nav_short = cmdr_ble_nav_short, + .lr_delta = cmdr_ble_lr_delta, + .draw = cmdr_ble_draw, + .on_tick = NULL, + .icon = s_icon_net, +}; diff --git a/main/menu_cmdr.h b/main/menu_cmdr.h new file mode 100644 index 0000000..763f083 --- /dev/null +++ b/main/menu_cmdr.h @@ -0,0 +1,4 @@ +#pragma once +#include "menus.h" +extern const menu_vtable_t menu_cmdr_offline_vtable; +extern const menu_vtable_t menu_cmdr_ble_vtable; diff --git a/main/menu_counters.c b/main/menu_counters.c new file mode 100644 index 0000000..e32ff5f --- /dev/null +++ b/main/menu_counters.c @@ -0,0 +1,39 @@ +#include "menu_counters.h" +#include "menus.h" +#include "config.h" +#include "state.h" +#include "draw.h" +#include "game.h" +#include "events.h" + +static const uint8_t s_icon_plus[2][ICON_W] = { + // Plus (counters) + {0x00,0x00,0x00,0xC0,0xC0,0xC0,0xF8,0xF8,0xF8,0xF8,0xC0,0xC0,0xC0,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x03,0x03,0x03,0x1F,0x1F,0x1F,0x1F,0x03,0x03,0x03,0x00,0x00,0x00}, +}; + +static void counters_nav_short(int d) +{ + g_active_counter = (g_active_counter + d + NUM_COUNTERS) % NUM_COUNTERS; +} + +static void counters_lr_delta(int d) +{ + g_counters[g_active_counter] += d; + if (g_counters[g_active_counter] < 0) g_counters[g_active_counter] = 0; + check_elimination(); + mark_dirty(); +} + +static void counters_draw(void) +{ + oled_draw_list(counter_names, g_counters, NUM_COUNTERS, g_active_counter); +} + +const menu_vtable_t menu_counters_vtable = { + .nav_short = counters_nav_short, + .lr_delta = counters_lr_delta, + .draw = counters_draw, + .on_tick = NULL, + .icon = s_icon_plus, +}; diff --git a/main/menu_counters.h b/main/menu_counters.h new file mode 100644 index 0000000..78b0d9b --- /dev/null +++ b/main/menu_counters.h @@ -0,0 +1,3 @@ +#pragma once +#include "menus.h" +extern const menu_vtable_t menu_counters_vtable; diff --git a/main/menu_dice.c b/main/menu_dice.c new file mode 100644 index 0000000..d73b82f --- /dev/null +++ b/main/menu_dice.c @@ -0,0 +1,56 @@ +#include "menu_dice.h" +#include "menus.h" +#include "config.h" +#include "state.h" +#include "draw.h" +#include "events.h" +#include "esp_random.h" +#include + +static const uint8_t s_icon_d6[2][ICON_W] = { + // D6 showing 6-face: square outline + 6 dots in 2x3 arrangement + {0x00,0xFE,0x02,0x02,0x9A,0x9A,0x02,0x02,0x02,0x02,0x9A,0x9A,0x02,0x02,0xFE,0x00}, + {0x00,0x7F,0x40,0x40,0x59,0x59,0x40,0x40,0x40,0x40,0x59,0x59,0x40,0x40,0x7F,0x00}, +}; + +static void dice_nav_short(int d) +{ + g_dice_item = (g_dice_item + d + 3) % 3; +} + +static void dice_lr_delta(int d) +{ + if (g_dice_item == 0) { + g_dice_num += d; + if (g_dice_num < 1) g_dice_num = 1; + } else if (g_dice_item == 1) { + g_dice_sides = (g_dice_sides + d + NUM_DICE_SIDES) % NUM_DICE_SIDES; + } else { + int sides = die_sides[g_dice_sides]; + g_dice_sum = 0; + int pos = 0; + g_dice_csv[0] = '\0'; + for (int i = 0; i < g_dice_num; i++) { + int roll = (int)(esp_random() % (uint32_t)sides) + 1; + g_dice_sum += roll; + if (pos < DICE_CSV_LEN - 5) { + if (i > 0) g_dice_csv[pos++] = ','; + pos += snprintf(g_dice_csv + pos, DICE_CSV_LEN - pos, "%d", roll); + } + } + g_dice_rolled = 1; + } +} + +static void dice_draw(void) +{ + oled_draw_dice(); +} + +const menu_vtable_t menu_dice_vtable = { + .nav_short = dice_nav_short, + .lr_delta = dice_lr_delta, + .draw = dice_draw, + .on_tick = NULL, + .icon = s_icon_d6, +}; diff --git a/main/menu_dice.h b/main/menu_dice.h new file mode 100644 index 0000000..4627945 --- /dev/null +++ b/main/menu_dice.h @@ -0,0 +1,3 @@ +#pragma once +#include "menus.h" +extern const menu_vtable_t menu_dice_vtable; diff --git a/main/menu_life.c b/main/menu_life.c new file mode 100644 index 0000000..2994b52 --- /dev/null +++ b/main/menu_life.c @@ -0,0 +1,81 @@ +#include "menu_life.h" +#include "menus.h" +#include "config.h" +#include "state.h" +#include "draw.h" +#include "game.h" +#include "ble.h" +#include "events.h" + +static const uint8_t s_icon_life[2][ICON_W] = { + // Heart (life) + {0x00,0x70,0xF8,0xFC,0xFC,0xFC,0xF8,0xF0,0xF0,0xF8,0xFC,0xFC,0xFC,0xF8,0x70,0x00}, + {0x00,0x00,0x00,0x01,0x03,0x07,0x0F,0x3F,0x3F,0x0F,0x07,0x03,0x01,0x00,0x00,0x00}, +}; + +static void life_nav_short(int d) +{ + int nc = g_ble_enabled ? 0 : g_num_opponents; + if (g_ble_enabled) + for (int i = 0; i < MAX_BLE_PEERS; i++) + if (g_peers[i].active) nc++; + int total = 3 + nc; + g_life_select = (g_life_select + d + total) % total; +} + +static void life_lr_delta(int d) +{ + if (g_life_select == 0) { + g_life += d; + g_life_delta += d; + restart_life_delta_timer(); + check_elimination(); + } else if (g_life_select == 1) { + g_counters[COUNTER_STORM] += d; + if (g_counters[COUNTER_STORM] < 0) g_counters[COUNTER_STORM] = 0; + } else if (g_life_select == 2) { + g_counters[COUNTER_POISON] += d; + if (g_counters[COUNTER_POISON] < 0) g_counters[COUNTER_POISON] = 0; + check_elimination(); + } else { + int si = g_life_select - 3; + if (g_ble_enabled) { + int found = 0; + for (int i = 0; i < MAX_BLE_PEERS; i++) { + if (g_peers[i].active && found++ == si) { + g_cmdr_damage[i] += d; + if (g_cmdr_damage[i] < 0) g_cmdr_damage[i] = 0; + check_elimination(); + break; + } + } + } else if (si < g_num_opponents) { + g_cmdr_damage[si] += d; + if (g_cmdr_damage[si] < 0) g_cmdr_damage[si] = 0; + check_elimination(); + } + } + mark_dirty(); +} + +static void life_draw(void) +{ + oled_draw_life(); +} + +static void life_on_tick(const app_event_t *evt) +{ + (void)evt; + if (g_life_delta != 0) { + g_life_delta = 0; + if (g_active_menu == MENU_LIFE) oled_draw_life(); + } +} + +const menu_vtable_t menu_life_vtable = { + .nav_short = life_nav_short, + .lr_delta = life_lr_delta, + .draw = life_draw, + .on_tick = life_on_tick, + .icon = s_icon_life, +}; diff --git a/main/menu_life.h b/main/menu_life.h new file mode 100644 index 0000000..2854ad5 --- /dev/null +++ b/main/menu_life.h @@ -0,0 +1,3 @@ +#pragma once +#include "menus.h" +extern const menu_vtable_t menu_life_vtable; diff --git a/main/menu_settings.c b/main/menu_settings.c new file mode 100644 index 0000000..ff63b40 --- /dev/null +++ b/main/menu_settings.c @@ -0,0 +1,164 @@ +#include "menu_settings.h" +#include "menu_cmdr.h" +#include "menus.h" +#include "config.h" +#include "state.h" +#include "draw.h" +#include "game.h" +#include "ble.h" +#include "events.h" +#include + +static const uint8_t s_icon_cog[2][ICON_W] = { + // Cog (settings) + {0x00,0x80,0xDC,0xFC,0xFC,0xB8,0x9C,0xFE,0xFE,0x9C,0xB8,0xFC,0xFC,0xDC,0x80,0x00}, + {0x00,0x01,0x3B,0x3F,0x3F,0x1D,0x39,0x7F,0x7F,0x39,0x1D,0x3F,0x3F,0x3B,0x01,0x00}, +}; + +static void settings_nav_short(int d) +{ + if (g_active_setting == SET_PLAYER_NAME) { + if (d > 0) { + g_name_cursor++; + if (g_name_cursor >= PLAYER_NAME_LEN) { + g_name_cursor = 0; + g_active_setting = (g_active_setting + 1) % NUM_SETTINGS; + g_game_id_cursor = 0; + } + } else { + if (g_name_cursor > 0) { + g_name_cursor--; + } else { + g_active_setting = (g_active_setting - 1 + NUM_SETTINGS) % NUM_SETTINGS; + g_name_cursor = (g_active_setting == SET_PLAYER_NAME) ? PLAYER_NAME_LEN - 1 : 0; + g_game_id_cursor = (g_active_setting == SET_GAME_ID) ? GAME_ID_DIGITS - 1 : 0; + } + } + } else if (g_active_setting == SET_GAME_ID) { + if (d > 0) { + g_game_id_cursor++; + if (g_game_id_cursor >= GAME_ID_DIGITS) { + g_game_id_cursor = 0; + g_active_setting = (g_active_setting + 1) % NUM_SETTINGS; + } + } else { + if (g_game_id_cursor > 0) { + g_game_id_cursor--; + } else { + g_active_setting = (g_active_setting - 1 + NUM_SETTINGS) % NUM_SETTINGS; + g_name_cursor = (g_active_setting == SET_PLAYER_NAME) ? PLAYER_NAME_LEN - 1 : 0; + g_game_id_cursor = (g_active_setting == SET_GAME_ID) ? GAME_ID_DIGITS - 1 : 0; + } + } + } else { + if (d > 0) { + g_active_setting = (g_active_setting + 1) % NUM_SETTINGS; + g_name_cursor = 0; + g_game_id_cursor = 0; + } else { + g_active_setting = (g_active_setting - 1 + NUM_SETTINGS) % NUM_SETTINGS; + g_name_cursor = (g_active_setting == SET_PLAYER_NAME) ? PLAYER_NAME_LEN - 1 : 0; + g_game_id_cursor = (g_active_setting == SET_GAME_ID) ? GAME_ID_DIGITS - 1 : 0; + } + } +} + +static void settings_lr_delta(int d) +{ + mark_dirty(); + switch (g_active_setting) { + case SET_BRIGHTNESS: + g_brightness_pct += d; + if (g_brightness_pct < BRIGHTNESS_MIN) g_brightness_pct = BRIGHTNESS_MIN; + if (g_brightness_pct > BRIGHTNESS_MAX) g_brightness_pct = BRIGHTNESS_MAX; + g_led_max = (uint8_t)(255 * g_brightness_pct / 100); + break; + case SET_START_LIFE: + g_start_life_index += d; + if (g_start_life_index < 0) g_start_life_index = 0; + if (g_start_life_index >= NUM_LIFE_OPTS) g_start_life_index = NUM_LIFE_OPTS - 1; + break; + case SET_NUM_OPP: + g_num_opponents += d; + if (g_num_opponents < 1) g_num_opponents = 1; + if (g_num_opponents > MAX_OPPONENTS) g_num_opponents = MAX_OPPONENTS; + if (g_active_opponent >= g_num_opponents) g_active_opponent = 0; + break; + case SET_PLAYER_NAME: { + char curr = g_player_name[g_name_cursor]; + const char *p = strchr(NAME_CHARS, curr); + int idx = p ? (int)(p - NAME_CHARS) : 0; + idx = (idx + d + NUM_NAME_CHARS) % NUM_NAME_CHARS; + g_player_name[g_name_cursor] = NAME_CHARS[idx]; + break; + } + case SET_BLE: + g_ble_enabled += d; + if (g_ble_enabled < 0) g_ble_enabled = 0; + if (g_ble_enabled > 1) g_ble_enabled = 1; + menus[MENU_CMDR] = g_ble_enabled ? &menu_cmdr_ble_vtable : &menu_cmdr_offline_vtable; + ble_adv_update(); + oled_draw_header(); + break; + case SET_GAME_ID: { + int byte = g_game_id_cursor / 2; + int nibble = g_game_id_cursor % 2; + int val = nibble == 0 ? (g_game_id[byte] >> 4) & 0xF : g_game_id[byte] & 0xF; + val = (val + d + NUM_HEX_CHARS) % NUM_HEX_CHARS; + if (nibble == 0) + g_game_id[byte] = (g_game_id[byte] & 0x0F) | ((uint8_t)val << 4); + else + g_game_id[byte] = (g_game_id[byte] & 0xF0) | (uint8_t)val; + ble_adv_update(); + break; + } + case SET_RESET: + game_reset(); + ble_adv_update(); + break; + case SET_RESET_ALL: + game_reset(); + g_reset_cmd_ticks = 1; + start_reset_cmd_timer(); + ble_adv_update(); + break; + case SET_MENU_HOLD: + g_menu_hold_ms += d * 10; + if (g_menu_hold_ms < HOLD_MS_MIN) g_menu_hold_ms = HOLD_MS_MIN; + if (g_menu_hold_ms > HOLD_MS_MAX) g_menu_hold_ms = HOLD_MS_MAX; + break; + case SET_LR_HOLD: + g_lr_hold_ms += d * 10; + if (g_lr_hold_ms < HOLD_MS_MIN) g_lr_hold_ms = HOLD_MS_MIN; + if (g_lr_hold_ms > HOLD_MS_MAX) g_lr_hold_ms = HOLD_MS_MAX; + break; + case SET_DISPLAY_FLIP: + g_display_flip ^= 1; + oled_set_flip(g_display_flip); + break; + case SET_DELTA_TIMEOUT: + g_delta_timeout_ms += d * 100; + if (g_delta_timeout_ms < DELTA_TIMEOUT_MIN) g_delta_timeout_ms = DELTA_TIMEOUT_MIN; + if (g_delta_timeout_ms > DELTA_TIMEOUT_MAX) g_delta_timeout_ms = DELTA_TIMEOUT_MAX; + break; + case SET_AUTO_SLEEP: + g_sleep_timeout_min += d; + if (g_sleep_timeout_min < 0) g_sleep_timeout_min = 0; + if (g_sleep_timeout_min > SLEEP_TIMEOUT_MAX) g_sleep_timeout_min = SLEEP_TIMEOUT_MAX; + restart_idle_timer(); + break; + } +} + +static void settings_draw(void) +{ + oled_draw_settings(); +} + +const menu_vtable_t menu_settings_vtable = { + .nav_short = settings_nav_short, + .lr_delta = settings_lr_delta, + .draw = settings_draw, + .on_tick = NULL, + .icon = s_icon_cog, +}; diff --git a/main/menu_settings.h b/main/menu_settings.h new file mode 100644 index 0000000..08cc3b1 --- /dev/null +++ b/main/menu_settings.h @@ -0,0 +1,3 @@ +#pragma once +#include "menus.h" +extern const menu_vtable_t menu_settings_vtable; diff --git a/main/menus.c b/main/menus.c new file mode 100644 index 0000000..2a20fbd --- /dev/null +++ b/main/menus.c @@ -0,0 +1,50 @@ +#include "menus.h" +#include "menu_life.h" +#include "menu_cmdr.h" +#include "menu_counters.h" +#include "menu_dice.h" +#include "menu_settings.h" +#include "state.h" +#include "ble.h" + +// Array index = header slot position (0–NUM_MENU_SLOTS-1). +// Initialized with offline CMDR vtable; menus_init() fixes up slot 1 based on g_ble_enabled. +const menu_vtable_t *menus[NUM_MENU_SLOTS] = { + [MENU_LIFE] = &menu_life_vtable, // slot 0 — life counter + [MENU_CMDR] = &menu_cmdr_offline_vtable, // slot 1 — commander damage (fixed by menus_init) + [MENU_COUNTERS] = &menu_counters_vtable, // slot 2 — counters + [MENU_DICE] = &menu_dice_vtable, // slot 3 — dice + [4] = NULL, // slot 4 — empty + [5] = NULL, // slot 5 — battery pct (special in oled_draw_header) + [6] = NULL, // slot 6 — battery icon (special in oled_draw_header) + [MENU_SETTINGS] = &menu_settings_vtable, // slot 7 — FWD+BACK combo to enter/exit +}; + +void menus_init(void) +{ + menus[MENU_CMDR] = g_ble_enabled ? &menu_cmdr_ble_vtable : &menu_cmdr_offline_vtable; +} + +void menus_nav_short(int d) +{ + const menu_vtable_t *m = menus[g_active_menu]; + if (m && m->nav_short) m->nav_short(d); + menus_draw(); +} + +void menus_lr_delta(int d) +{ + const menu_vtable_t *m = menus[g_active_menu]; + if (m && m->lr_delta) m->lr_delta(d); + menus_draw(); + if (g_active_menu == MENU_LIFE || g_active_menu == MENU_COUNTERS || + g_active_menu == MENU_CMDR) + ble_adv_update(); + led_sync(true); +} + +void menus_draw(void) +{ + const menu_vtable_t *m = menus[g_active_menu]; + if (m && m->draw) m->draw(); +} diff --git a/main/menus.h b/main/menus.h new file mode 100644 index 0000000..510acc9 --- /dev/null +++ b/main/menus.h @@ -0,0 +1,30 @@ +#pragma once +#include +#include +#include "config.h" +#include "events.h" + +typedef struct { + void (*nav_short)(int d); + void (*lr_delta)(int d); + void (*draw)(void); + void (*on_tick)(const app_event_t *evt); // NULL if unused + const uint8_t (*icon)[ICON_W]; // 2-page icon [2][ICON_W] +} menu_vtable_t; + +// Array index = header slot position (0–NUM_MENU_SLOTS-1). +// NULL = empty/non-interactive slot. +// Last slot (NUM_MENU_SLOTS-1) = FWD+BACK combo to enter/exit. +extern const menu_vtable_t *menus[NUM_MENU_SLOTS]; + +void menus_init(void); +void menus_nav_short(int d); +void menus_lr_delta(int d); +void menus_draw(void); + +// Helpers from main.c exposed for menu_*.c files +void mark_dirty(void); +void restart_life_delta_timer(void); +void restart_idle_timer(void); +void start_reset_cmd_timer(void); +void led_sync(bool changed); diff --git a/main/state.h b/main/state.h index 7fa676b..515869f 100644 --- a/main/state.h +++ b/main/state.h @@ -1,4 +1,5 @@ #pragma once +#include #include "config.h" #include "ble.h" @@ -39,12 +40,11 @@ extern int g_charging; // 1 when charger is connected (BATT_CHARGE extern int g_batt_full; // 1 when battery is full (BATT_FULL_GPIO low) extern int g_sleep_timeout_min; // 0 = disabled -// Timing -extern uint32_t g_tick; +// Battery blink toggle (driven by batt timer) +extern bool g_batt_blink; // Life delta overlay extern int g_life_delta; -extern int g_life_delta_tick; extern int g_delta_timeout_ms; // Dice diff --git a/pcb/commeownder.kicad_pcb b/pcb/commeownder.kicad_pcb index e70bc8d..8e350e0 100644 --- a/pcb/commeownder.kicad_pcb +++ b/pcb/commeownder.kicad_pcb @@ -7137,16 +7137,16 @@ ) ) ) - (footprint "Inductor_SMD:L_Taiyo-Yuden_MD-3030" + (footprint "Inductor_SMD:L_Taiyo-Yuden_MD-4040" (layer "B.Cu") (uuid "2381c86e-1e2a-4977-98f8-d798f77018ae") - (at 137.8 113.8 90) - (descr "Inductor, Taiyo Yuden, MD series, Taiyo-Yuden_MD-3030, 3.0mmx3.0mm") + (at 136.9 113.8 90) + (descr "Inductor, Taiyo Yuden, MD series, Taiyo-Yuden_MD-4040, 4.0mmx4.0mm") (tags "inductor taiyo-yuden md smd") (property "Reference" "L1" - (at -2.6 0 0) + (at 0 3 270) (layer "B.SilkS") - (uuid "60750ed8-aa28-40c2-a554-53620136c35c") + (uuid "8f5a172a-2592-4896-98e3-426fd35235d1") (effects (font (size 1 1) @@ -7156,9 +7156,9 @@ ) ) (property "Value" "2.2u" - (at 0 -3 90) + (at 0 -3.5 270) (layer "B.Fab") - (uuid "08a7fc85-939b-4ea9-a1b2-f8b579f4c302") + (uuid "aa1ea1d6-db39-4538-a71c-ff7a241802f6") (effects (font (size 1 1) @@ -7172,7 +7172,7 @@ (unlocked yes) (layer "B.Fab") (hide yes) - (uuid "6282e116-5b51-4747-86ae-22479f569890") + (uuid "0989e848-7084-4d64-9894-10816aa3f13f") (effects (font (size 1.27 1.27) @@ -7186,7 +7186,7 @@ (unlocked yes) (layer "B.Fab") (hide yes) - (uuid "53a5cd77-5812-47d6-b7bc-6e543c5f7958") + (uuid "dd640e5f-96f5-4010-afca-65e1af96d0fc") (effects (font (size 1.27 1.27) @@ -7196,11 +7196,11 @@ ) ) (property "Part" "LSXND4040MKL2R2MDG" - (at 0 0 270) + (at 0 0 90) (unlocked yes) (layer "B.Fab") (hide yes) - (uuid "3eb310dd-e4ae-4b4b-804d-07029ee6f1ba") + (uuid "8f938c1d-5068-416a-877d-8c66cf79574f") (effects (font (size 1 1) @@ -7222,8 +7222,8 @@ (attr smd) (duplicate_pad_numbers_are_jumpers no) (fp_line - (start -1.5 -1.6) - (end 1.5 -1.6) + (start -2 -2.1) + (end 2 -2.1) (stroke (width 0.12) (type solid) @@ -7232,8 +7232,8 @@ (uuid "f323efac-0dce-4c94-80c0-9c9acf62ba10") ) (fp_line - (start -1.5 1.6) - (end 1.5 1.6) + (start -2 2.1) + (end 2 2.1) (stroke (width 0.12) (type solid) @@ -7242,115 +7242,115 @@ (uuid "d75839e3-cab4-4a09-b08b-9439f57f2371") ) (fp_line - (start 1.8 -1.8) - (end 1.8 1.8) + (start 2.25 -2.25) + (end 2.25 2.25) (stroke (width 0.05) (type solid) ) (layer "B.CrtYd") - (uuid "a1a6f2c6-47af-47b1-a9e4-0e5e01f2a259") + (uuid "1daa8d95-70e2-4b2f-9cb3-bdfa03a363a3") ) (fp_line - (start -1.8 -1.8) - (end 1.8 -1.8) + (start -2.25 -2.25) + (end 2.25 -2.25) (stroke (width 0.05) (type solid) ) (layer "B.CrtYd") - (uuid "a1800ed0-fe5e-411b-a4e8-559f9062a378") + (uuid "a1a6f2c6-47af-47b1-a9e4-0e5e01f2a259") ) (fp_line - (start 1.8 1.8) - (end -1.8 1.8) + (start 2.25 2.25) + (end -2.25 2.25) (stroke (width 0.05) (type solid) ) (layer "B.CrtYd") - (uuid "1daa8d95-70e2-4b2f-9cb3-bdfa03a363a3") + (uuid "68a18bd9-b2a6-4a9e-bb4c-7c9388cdc527") ) (fp_line - (start -1.8 1.8) - (end -1.8 -1.8) + (start -2.25 2.25) + (end -2.25 -2.25) (stroke (width 0.05) (type solid) ) (layer "B.CrtYd") - (uuid "68a18bd9-b2a6-4a9e-bb4c-7c9388cdc527") + (uuid "a1800ed0-fe5e-411b-a4e8-559f9062a378") ) (fp_line - (start 1.5 -1.5) - (end 1.5 1.5) + (start 2 -2) + (end 2 2) (stroke (width 0.1) (type solid) ) (layer "B.Fab") - (uuid "8723c069-2ad6-407b-975f-29c6217ecfd6") + (uuid "da8ee948-f038-444b-92df-a4579b2967ab") ) (fp_line - (start -1.5 -1.5) - (end 1.5 -1.5) + (start -2 -2) + (end 2 -2) (stroke (width 0.1) (type solid) ) (layer "B.Fab") - (uuid "6017caa5-c3a9-4b21-ae17-14cb592b0edf") + (uuid "3cc6e2fb-1324-433b-9bff-f5d6f5b20f50") ) (fp_line - (start 1.5 1.5) - (end -1.5 1.5) + (start 2 2) + (end -2 2) (stroke (width 0.1) (type solid) ) (layer "B.Fab") - (uuid "3cc6e2fb-1324-433b-9bff-f5d6f5b20f50") + (uuid "6017caa5-c3a9-4b21-ae17-14cb592b0edf") ) (fp_line - (start -1.5 1.5) - (end -1.5 -1.5) + (start -2 2) + (end -2 -2) (stroke (width 0.1) (type solid) ) (layer "B.Fab") - (uuid "da8ee948-f038-444b-92df-a4579b2967ab") + (uuid "8723c069-2ad6-407b-975f-29c6217ecfd6") ) (fp_text user "${REFERENCE}" - (at 0 0 90) + (at 0 0 270) (layer "B.Fab") (uuid "67838003-cb5e-4dbc-bc64-755534995ae2") (effects (font - (size 0.7 0.7) - (thickness 0.105) + (size 1 1) + (thickness 0.15) ) (justify mirror) ) ) (pad "1" smd rect - (at -1.1 0 90) - (size 0.8 2.7) + (at -1.4 0 90) + (size 1.2 3.7) (layers "B.Cu" "B.Mask" "B.Paste") (net "Net-(U3-L2)") (pintype "passive") (uuid "44d6c8d2-671d-4302-9111-75ca52302ff5") ) (pad "2" smd rect - (at 1.1 0 90) - (size 0.8 2.7) + (at 1.4 0 90) + (size 1.2 3.7) (layers "B.Cu" "B.Mask" "B.Paste") (net "Net-(U3-L1)") (pintype "passive") (uuid "dcab7d0f-cce9-4831-b880-404932ad035f") ) (embedded_fonts no) - (model "${KICAD10_3DMODEL_DIR}/Inductor_SMD.3dshapes/L_Taiyo-Yuden_MD-3030.step" + (model "${KICAD10_3DMODEL_DIR}/Inductor_SMD.3dshapes/L_Taiyo-Yuden_MD-4040.step" (offset (xyz 0 0 0) ) @@ -15596,7 +15596,7 @@ ) ) ) - (gr_text "v0.5" + (gr_text "v0.6" (at 168.1 90.3 0) (layer "F.SilkS") (uuid "c7462689-1d2f-4321-93a3-1767c6be54a9") @@ -15608,7 +15608,7 @@ ) (justify left bottom) ) - (render_cache "v0.5" 0 + (render_cache "v0.6" 0 (polygon (pts (xy 168.492742 89.781217) (xy 168.492742 89.511573) (xy 168.627564 89.511573) (xy 168.627564 89.781217) @@ -15691,28 +15691,53 @@ ) (polygon (pts - (xy 170.719596 89.781217) (xy 170.719596 89.644197) (xy 171.11591 89.644197) (xy 171.11591 89.781217) + (xy 170.852128 89.781217) (xy 170.852128 89.644197) (xy 171.121772 89.644197) (xy 171.121772 89.781217) + ) + ) + (polygon + (pts + (xy 170.720878 89.648593) (xy 170.720878 89.511573) (xy 170.85799 89.511573) (xy 170.85799 89.648593) + ) + ) + (polygon + (pts + (xy 171.11591 89.648593) (xy 171.11591 89.511573) (xy 171.250732 89.511573) (xy 171.250732 89.648593) ) ) (polygon (pts - (xy 170.588346 89.648593) (xy 170.588346 89.511573) (xy 170.725457 89.511573) (xy 170.725457 89.648593) + (xy 171.24487 89.517435) (xy 171.24487 89.247791) (xy 171.381982 89.247791) (xy 171.381982 89.517435) ) ) (polygon (pts - (xy 171.11362 89.648593) (xy 171.11362 89.115167) (xy 171.250732 89.115167) (xy 171.250732 89.648593) + (xy 171.11591 89.253653) (xy 171.11591 89.118831) (xy 171.250732 89.118831) (xy 171.250732 89.253653) ) ) (polygon (pts - (xy 170.984385 89.126891) (xy 170.984385 88.98987) (xy 171.11591 88.98987) (xy 171.11591 89.126891) + (xy 170.852128 89.126891) (xy 170.852128 88.98987) (xy 171.121772 88.98987) (xy 171.121772 89.126891) ) ) (polygon (pts - (xy 170.588346 88.995732) (xy 170.588346 88.462306) (xy 171.250732 88.462306) (xy 171.250732 88.599326) - (xy 170.725457 88.599326) (xy 170.725457 88.858712) (xy 170.990247 88.858712) (xy 170.990247 88.995732) + (xy 170.588346 89.517435) (xy 170.588346 88.726088) (xy 170.725457 88.726088) (xy 170.725457 89.118831) + (xy 170.85799 89.118831) (xy 170.85799 89.253653) (xy 170.725457 89.253653) (xy 170.725457 89.517435) + ) + ) + (polygon + (pts + (xy 170.720878 88.73195) (xy 170.720878 88.59493) (xy 170.85799 88.59493) (xy 170.85799 88.73195) + ) + ) + (polygon + (pts + (xy 171.11591 88.73195) (xy 171.11591 88.59493) (xy 171.250732 88.59493) (xy 171.250732 88.73195) + ) + ) + (polygon + (pts + (xy 170.852128 88.599326) (xy 170.852128 88.462306) (xy 171.121772 88.462306) (xy 171.121772 88.599326) ) ) ) @@ -17285,6 +17310,14 @@ (net "VDD") (uuid "115b7600-adbc-44a9-987b-aa16f29a0f71") ) + (segment + (start 133.789998 116.06) + (end 132.729998 115) + (width 0.2) + (layer "F.Cu") + (net "VDD") + (uuid "1c27f70f-70e2-43f9-a545-302be2355928") + ) (segment (start 157.875704 113.575704) (end 158 113.7) @@ -17301,14 +17334,6 @@ (net "VDD") (uuid "281dff42-9df2-49cb-97be-7d5d810f6306") ) - (segment - (start 135.79 116.06) - (end 133.789998 116.06) - (width 0.2) - (layer "F.Cu") - (net "VDD") - (uuid "36f5df73-a564-4800-b97c-31b4f5927726") - ) (segment (start 158.076273 111.109512) (end 157.875704 111.310081) @@ -17325,14 +17350,6 @@ (net "VDD") (uuid "48db8b37-8734-4296-85ea-ce6a03e6fd7d") ) - (segment - (start 135.8 116.05) - (end 135.79 116.06) - (width 0.2) - (layer "F.Cu") - (net "VDD") - (uuid "5fe1cc5d-506a-47c1-b44e-aa2817992589") - ) (segment (start 132.15 96.15) (end 132.1 96.2) @@ -17357,6 +17374,14 @@ (net "VDD") (uuid "80d56228-8288-4051-b271-dee3acbee21d") ) + (segment + (start 132.729998 115) + (end 132.1 115) + (width 0.2) + (layer "F.Cu") + (net "VDD") + (uuid "984d6996-3366-4f64-804f-4044512169c2") + ) (segment (start 137.25 96.65) (end 135.05 96.65) @@ -17406,28 +17431,28 @@ (uuid "0c7d6a15-91b3-4723-89f2-55d73acf0e81") ) (via - (at 132.1 96.2) + (at 132.1 115) (size 0.6) (drill 0.3) (layers "F.Cu" "B.Cu") (net "VDD") - (uuid "32c05a37-10d9-4876-8a7e-8eaf661e89fb") + (uuid "26512bc2-69c5-4b00-b9e5-275aab1e0247") ) (via - (at 158 113.7) + (at 132.1 96.2) (size 0.6) (drill 0.3) (layers "F.Cu" "B.Cu") (net "VDD") - (uuid "72495bac-ea8c-42a5-a6d0-31b564574102") + (uuid "32c05a37-10d9-4876-8a7e-8eaf661e89fb") ) (via - (at 135.8 116.05) + (at 158 113.7) (size 0.6) (drill 0.3) (layers "F.Cu" "B.Cu") (net "VDD") - (uuid "9d7242fe-73bc-463c-884e-fa970fc15f0d") + (uuid "72495bac-ea8c-42a5-a6d0-31b564574102") ) (via (at 135.25 104.6) @@ -17446,60 +17471,60 @@ (uuid "034be32c-3226-4a44-9c3d-b034a36dafd2") ) (segment - (start 139.24 111) - (end 138.22 111) + (start 158.6 114.3) + (end 158 113.7) (width 0.2) (layer "B.Cu") (net "VDD") - (uuid "12f208b6-a260-454d-b4df-510b0b18ac04") + (uuid "065d6f77-3dcc-4b26-b729-cac6d2b9f48e") ) (segment - (start 163.875 99.175) - (end 163.875 100.584744) + (start 158 120) + (end 158.6 119.4) (width 0.2) (layer "B.Cu") (net "VDD") - (uuid "145a6a16-f69c-43dd-84bc-8a7a874f1519") + (uuid "0d741702-f371-4d50-b053-6fef862ab772") ) (segment - (start 159.951471 101.7) - (end 159.3125 102.338971) + (start 139.24 111) + (end 138.22 111) (width 0.2) (layer "B.Cu") (net "VDD") - (uuid "23c5cb2b-c13e-47db-9ecc-46538b53782b") + (uuid "12f208b6-a260-454d-b4df-510b0b18ac04") ) (segment - (start 135.8 111.6) - (end 135.8 116.05) + (start 163.875 99.175) + (end 163.875 100.584744) (width 0.2) (layer "B.Cu") (net "VDD") - (uuid "27cb23e8-a5ea-4a59-a5d0-252f99d2ce59") + (uuid "145a6a16-f69c-43dd-84bc-8a7a874f1519") ) (segment - (start 158.6 119.4) - (end 158.6 114.3) + (start 136.26 111) + (end 133 111) (width 0.2) (layer "B.Cu") (net "VDD") - (uuid "2b006a43-8840-4d14-90a2-b4a23d4dc822") + (uuid "23a7d9a9-875f-4f62-a880-e8784d196553") ) (segment - (start 135.25 104.6) - (end 135.8 105.15) + (start 159.951471 101.7) + (end 159.3125 102.338971) (width 0.2) (layer "B.Cu") (net "VDD") - (uuid "3d8e7944-1063-43fb-9b3c-a75d9dd62d18") + (uuid "23c5cb2b-c13e-47db-9ecc-46538b53782b") ) (segment - (start 135.8 116.05) - (end 139.75 120) + (start 132.55 111.45) + (end 132.55 111.5) (width 0.2) (layer "B.Cu") (net "VDD") - (uuid "3ea694ec-c18a-44b0-ad98-b9c483bbd9cb") + (uuid "2f2c1fc6-7553-4103-bfa3-0fb43c40699e") ) (segment (start 161.25 101) @@ -17517,14 +17542,6 @@ (net "VDD") (uuid "484bde60-e5b8-4549-9a44-3efc77d4f05e") ) - (segment - (start 158 120) - (end 158.6 119.4) - (width 0.2) - (layer "B.Cu") - (net "VDD") - (uuid "4976bbef-207f-4793-a963-80343b3b086f") - ) (segment (start 159.3125 107.3) (end 157.2 109.4125) @@ -17549,14 +17566,6 @@ (net "VDD") (uuid "51630aa7-c4d6-4758-b886-2d2be7877a91") ) - (segment - (start 136.38 111.02) - (end 135.8 111.6) - (width 0.2) - (layer "B.Cu") - (net "VDD") - (uuid "5ae17920-e14f-4220-94c9-7189c12cdbaf") - ) (segment (start 162.7 98) (end 163.875 99.175) @@ -17581,6 +17590,14 @@ (net "VDD") (uuid "7e2caf4c-46d2-4de3-b671-679faa41f753") ) + (segment + (start 131.9 112.15) + (end 131.9 114.9) + (width 0.2) + (layer "B.Cu") + (net "VDD") + (uuid "8149cf86-6808-4966-8c37-a7be3a91d771") + ) (segment (start 157.861323 111.324462) (end 157.375538 111.324462) @@ -17590,12 +17607,36 @@ (uuid "84ccd32e-8c4d-4c49-939f-cf34fb099f7c") ) (segment - (start 131.9 112.15) - (end 135.8 116.05) + (start 132.55 111.5) + (end 131.9 112.15) (width 0.2) (layer "B.Cu") (net "VDD") - (uuid "9b83ed35-56ca-4b25-9f57-5caba75aa197") + (uuid "864d06a9-d71c-4847-aede-870e360828d5") + ) + (segment + (start 137 120) + (end 158 120) + (width 0.2) + (layer "B.Cu") + (net "VDD") + (uuid "9120422e-ee23-4840-b01e-5b78c3036b1a") + ) + (segment + (start 135.25 109.99) + (end 136.28 111.02) + (width 0.2) + (layer "B.Cu") + (net "VDD") + (uuid "981782f7-0936-457a-b10a-b84b639d72ef") + ) + (segment + (start 158.6 119.4) + (end 158.6 114.3) + (width 0.2) + (layer "B.Cu") + (net "VDD") + (uuid "a09515b3-1103-424b-bcb4-8d773bfccec9") ) (segment (start 163.875 100.584744) @@ -17614,20 +17655,20 @@ (uuid "b0848d5e-7573-427d-b370-87bdc84d63d6") ) (segment - (start 135.8 105.15) - (end 135.8 111.6) + (start 138.2 111.02) + (end 136.28 111.02) (width 0.2) (layer "B.Cu") (net "VDD") - (uuid "b5810a77-857d-4a81-ab17-42eb0ac2f7d1") + (uuid "b31f1720-0650-4b8c-8dd4-bb46f7224d45") ) (segment - (start 138.2 111.02) - (end 136.38 111.02) + (start 136.28 111.02) + (end 136.26 111) (width 0.2) (layer "B.Cu") (net "VDD") - (uuid "b886b6f9-eda8-4455-bc86-4e49883c7834") + (uuid "c1ac786f-4b44-4caf-9d5a-6e9ea51cca7b") ) (segment (start 159.3125 102.338971) @@ -17638,12 +17679,12 @@ (uuid "c74d2a50-28c4-4243-a6b0-f49876088cc4") ) (segment - (start 139.75 120) - (end 158 120) + (start 133 111) + (end 132.55 111.45) (width 0.2) (layer "B.Cu") (net "VDD") - (uuid "caca0a11-79e3-4922-bea1-d6fceeae9283") + (uuid "cedbd35c-cd41-4223-bb01-c36553845a2c") ) (segment (start 160.55 101.7) @@ -17654,12 +17695,12 @@ (uuid "d980b4d6-f903-498f-8d02-e6b2b0598991") ) (segment - (start 158.6 114.3) - (end 158 113.7) + (start 131.9 114.9) + (end 137 120) (width 0.2) (layer "B.Cu") (net "VDD") - (uuid "deff7e5a-fff8-41e5-8176-3e0a3f9bb1a1") + (uuid "df5c96f5-eb00-4943-823a-f995181aae9f") ) (segment (start 132.1 96.2) @@ -17693,6 +17734,14 @@ (net "VDD") (uuid "f1894e88-0bde-4d40-a2c6-40652b9105a4") ) + (segment + (start 135.25 104.6) + (end 135.25 109.99) + (width 0.2) + (layer "B.Cu") + (net "VDD") + (uuid "fec5f38e-d2c8-4ff5-8905-f66e5eaa9a23") + ) (segment (start 139.01 100.5) (end 139 100.51) @@ -20461,6 +20510,126 @@ (net "/LED_R1") (uuid "199d9c3d-2e4f-410d-8fb5-f966cadae39a") ) + (segment + (start 136.9 115.2) + (end 137.9 114.2) + (width 0.2) + (layer "B.Cu") + (net "Net-(U3-L2)") + (uuid "42c7258f-5639-4a95-b9e5-a0d29909fea4") + ) + (segment + (start 137.9 114.2) + (end 140.8 114.2) + (width 0.2) + (layer "B.Cu") + (net "Net-(U3-L2)") + (uuid "6c295387-c814-48b3-939d-816b976fe568") + ) + (segment + (start 137.7 113.2) + (end 140.8 113.2) + (width 0.2) + (layer "B.Cu") + (net "Net-(U3-L1)") + (uuid "3a17d91f-6b42-47e5-8288-de6802c19eb8") + ) + (segment + (start 136.9 112.4) + (end 137.7 113.2) + (width 0.2) + (layer "B.Cu") + (net "Net-(U3-L1)") + (uuid "fedef87c-0a2e-48c2-b878-baef8729ce04") + ) + (segment + (start 137.81759 110.230939) + (end 141.446793 110.230939) + (width 0.2) + (layer "F.Cu") + (net "Net-(U3-EN)") + (uuid "375d83d3-ae70-461e-b53b-f37669fb3e2b") + ) + (segment + (start 141.446793 110.230939) + (end 143.280368 112.064514) + (width 0.2) + (layer "F.Cu") + (net "Net-(U3-EN)") + (uuid "6c19cb2c-b458-4ab7-b528-0c815f9fafea") + ) + (segment + (start 137.776814 110.271715) + (end 137.81759 110.230939) + (width 0.2) + (layer "F.Cu") + (net "Net-(U3-EN)") + (uuid "f750a0fb-7bda-4abe-a43d-7dc2ba86d71a") + ) + (via + (at 137.776814 110.271715) + (size 0.6) + (drill 0.3) + (layers "F.Cu" "B.Cu") + (net "Net-(U3-EN)") + (uuid "0995aeb3-8aa2-4589-a45c-5772a67d8b95") + ) + (via + (at 143.280368 112.064514) + (size 0.6) + (drill 0.3) + (layers "F.Cu" "B.Cu") + (net "Net-(U3-EN)") + (uuid "92e656db-e209-4e10-9bbd-9412e4674af1") + ) + (segment + (start 138.2 110) + (end 139.24 110) + (width 0.2) + (layer "B.Cu") + (net "Net-(U3-EN)") + (uuid "443af67c-f567-4855-ae01-46c51b085dae") + ) + (segment + (start 143.280368 112.064514) + (end 143.6 112.384146) + (width 0.2) + (layer "B.Cu") + (net "Net-(U3-EN)") + (uuid "56f706b9-4ea5-4549-8350-daba255653b7") + ) + (segment + (start 138.2 110) + (end 138.048529 110) + (width 0.2) + (layer "B.Cu") + (net "Net-(U3-EN)") + (uuid "76562345-e2d4-42ba-bde3-098f14e4620e") + ) + (segment + (start 143.6 113.7) + (end 143.6 112.7) + (width 0.2) + (layer "B.Cu") + (net "Net-(U3-EN)") + (uuid "877320bf-a59b-4bd4-8618-bf1fe8e3a61b") + ) + (segment + (start 138.048529 110) + (end 137.776814 110.271715) + (width 0.2) + (layer "B.Cu") + (net "Net-(U3-EN)") + (uuid "b1ec833d-cb42-4b2b-8993-8e7212b99e5b") + ) + (segment + (start 143.6 112.384146) + (end 143.6 112.7) + (width 0.2) + (layer "B.Cu") + (net "Net-(U3-EN)") + (uuid "d2c666e8-44bb-4ba4-8ba5-b35472818c35") + ) (segment (start 171.049999 102.165) (end 170.075 102.165) @@ -20693,126 +20862,6 @@ (net "Net-(Q4-B)") (uuid "89b9b1ae-2e09-4e54-91c2-0dbd8f89a2e1") ) - (segment - (start 137.81759 110.230939) - (end 141.446793 110.230939) - (width 0.2) - (layer "F.Cu") - (net "Net-(U3-EN)") - (uuid "375d83d3-ae70-461e-b53b-f37669fb3e2b") - ) - (segment - (start 141.446793 110.230939) - (end 143.280368 112.064514) - (width 0.2) - (layer "F.Cu") - (net "Net-(U3-EN)") - (uuid "6c19cb2c-b458-4ab7-b528-0c815f9fafea") - ) - (segment - (start 137.776814 110.271715) - (end 137.81759 110.230939) - (width 0.2) - (layer "F.Cu") - (net "Net-(U3-EN)") - (uuid "f750a0fb-7bda-4abe-a43d-7dc2ba86d71a") - ) - (via - (at 137.776814 110.271715) - (size 0.6) - (drill 0.3) - (layers "F.Cu" "B.Cu") - (net "Net-(U3-EN)") - (uuid "0995aeb3-8aa2-4589-a45c-5772a67d8b95") - ) - (via - (at 143.280368 112.064514) - (size 0.6) - (drill 0.3) - (layers "F.Cu" "B.Cu") - (net "Net-(U3-EN)") - (uuid "92e656db-e209-4e10-9bbd-9412e4674af1") - ) - (segment - (start 138.2 110) - (end 139.24 110) - (width 0.2) - (layer "B.Cu") - (net "Net-(U3-EN)") - (uuid "443af67c-f567-4855-ae01-46c51b085dae") - ) - (segment - (start 143.280368 112.064514) - (end 143.6 112.384146) - (width 0.2) - (layer "B.Cu") - (net "Net-(U3-EN)") - (uuid "56f706b9-4ea5-4549-8350-daba255653b7") - ) - (segment - (start 138.2 110) - (end 138.048529 110) - (width 0.2) - (layer "B.Cu") - (net "Net-(U3-EN)") - (uuid "76562345-e2d4-42ba-bde3-098f14e4620e") - ) - (segment - (start 143.6 113.7) - (end 143.6 112.7) - (width 0.2) - (layer "B.Cu") - (net "Net-(U3-EN)") - (uuid "877320bf-a59b-4bd4-8618-bf1fe8e3a61b") - ) - (segment - (start 138.048529 110) - (end 137.776814 110.271715) - (width 0.2) - (layer "B.Cu") - (net "Net-(U3-EN)") - (uuid "b1ec833d-cb42-4b2b-8993-8e7212b99e5b") - ) - (segment - (start 143.6 112.384146) - (end 143.6 112.7) - (width 0.2) - (layer "B.Cu") - (net "Net-(U3-EN)") - (uuid "d2c666e8-44bb-4ba4-8ba5-b35472818c35") - ) - (segment - (start 140.8 114.2) - (end 138.5 114.2) - (width 0.2) - (layer "B.Cu") - (net "Net-(U3-L2)") - (uuid "8fb0caa7-463e-468f-8440-a1fb2069a11e") - ) - (segment - (start 138.5 114.2) - (end 137.8 114.9) - (width 0.2) - (layer "B.Cu") - (net "Net-(U3-L2)") - (uuid "a7ced5f0-d986-4e04-81df-2ad57ee5e938") - ) - (segment - (start 140.8 113.2) - (end 138.3 113.2) - (width 0.2) - (layer "B.Cu") - (net "Net-(U3-L1)") - (uuid "6bb2c8cb-af2b-4a27-a93b-84ae6db9237e") - ) - (segment - (start 138.3 113.2) - (end 137.8 112.7) - (width 0.2) - (layer "B.Cu") - (net "Net-(U3-L1)") - (uuid "9a1696fe-c2b9-459c-939d-9317c2f815c5") - ) (zone (net "GND") (layer "F.Cu") @@ -20862,7 +20911,7 @@ (xy 164.50862 114.704966) (xy 164.509448 114.703729) (xy 164.56306 114.658923) (xy 164.632385 114.650216) (xy 164.695413 114.68037) (xy 164.726119 114.72862) (xy 164.727932 114.72787) (xy 164.790602 114.879172) (xy 164.790609 114.879185) (xy 164.87821 115.010288) (xy 164.878213 115.010292) (xy 164.989707 115.121786) - (xy 164.989711 115.121789) (xy 165.120814 115.20939) (xy 165.120827 115.209397) (xy 165.217646 115.2495) + (xy 164.989711 115.121789) (xy 165.120814 115.20939) (xy 165.120827 115.209397) (xy 165.192434 115.239057) (xy 165.266503 115.269737) (xy 165.421153 115.300499) (xy 165.421156 115.3005) (xy 165.421158 115.3005) (xy 165.578844 115.3005) (xy 165.578845 115.300499) (xy 165.733497 115.269737) (xy 165.879179 115.209394) (xy 166.010289 115.121789) (xy 166.121789 115.010289) (xy 166.209394 114.879179) (xy 166.213704 114.868775) @@ -20878,7 +20927,7 @@ (xy 167.642539 114.405185) (xy 167.688294 114.457989) (xy 167.6995 114.5095) (xy 167.6995 114.578846) (xy 167.730261 114.733489) (xy 167.730264 114.733501) (xy 167.790602 114.879172) (xy 167.790609 114.879185) (xy 167.87821 115.010288) (xy 167.878213 115.010292) (xy 167.989707 115.121786) (xy 167.989711 115.121789) - (xy 168.120814 115.20939) (xy 168.120827 115.209397) (xy 168.217646 115.2495) (xy 168.266503 115.269737) + (xy 168.120814 115.20939) (xy 168.120827 115.209397) (xy 168.192434 115.239057) (xy 168.266503 115.269737) (xy 168.421153 115.300499) (xy 168.421156 115.3005) (xy 168.421158 115.3005) (xy 168.578844 115.3005) (xy 168.578845 115.300499) (xy 168.733497 115.269737) (xy 168.879179 115.209394) (xy 169.010289 115.121789) (xy 169.121789 115.010289) (xy 169.139609 114.98362) (xy 169.14184 114.980281) (xy 169.195452 114.935476) @@ -20935,197 +20984,191 @@ (xy 132.091204 116.802335) (xy 132.17745 116.917544) (xy 132.177453 116.917547) (xy 132.292662 117.003793) (xy 132.292669 117.003797) (xy 132.427515 117.054091) (xy 132.427514 117.054091) (xy 132.434442 117.054835) (xy 132.487125 117.0605) (xy 135.09287 117.060499) (xy 135.152481 117.054091) (xy 135.287329 117.003796) - (xy 135.402544 116.917546) (xy 135.4407 116.866575) (xy 135.49663 116.824706) (xy 135.564157 116.81927) - (xy 135.566501 116.819736) (xy 135.566503 116.819737) (xy 135.622033 116.830782) (xy 135.721155 116.8505) - (xy 135.721158 116.8505) (xy 135.878844 116.8505) (xy 135.878845 116.850499) (xy 136.033497 116.819737) - (xy 136.179179 116.759394) (xy 136.310289 116.671789) (xy 136.421789 116.560289) (xy 136.509394 116.429179) - (xy 136.569737 116.283497) (xy 136.6005 116.128842) (xy 136.6005 115.971158) (xy 136.6005 115.971155) - (xy 136.600499 115.971153) (xy 136.569738 115.81651) (xy 136.569737 115.816503) (xy 136.569735 115.816498) - (xy 136.509397 115.670827) (xy 136.50939 115.670814) (xy 136.421789 115.539711) (xy 136.421786 115.539707) - (xy 136.310292 115.428213) (xy 136.310288 115.42821) (xy 136.252901 115.389865) (xy 136.179185 115.340609) - (xy 136.179172 115.340602) (xy 136.147726 115.327577) (xy 141.875975 115.327577) (xy 141.981236 115.371178) + (xy 135.402544 116.917546) (xy 135.488794 116.802331) (xy 135.539089 116.667483) (xy 135.545498 116.607873) + (xy 135.545497 115.512128) (xy 135.539089 115.452517) (xy 135.531537 115.43227) (xy 135.493429 115.330096) + (xy 135.493428 115.330094) (xy 135.492489 115.327577) (xy 141.875975 115.327577) (xy 141.981236 115.371178) (xy 141.98124 115.371179) (xy 142.126126 115.399999) (xy 142.126129 115.4) (xy 142.273871 115.4) (xy 142.273873 115.399999) (xy 142.41876 115.371179) (xy 142.418775 115.371175) (xy 142.524024 115.327578) (xy 142.524024 115.327577) (xy 142.200001 115.003554) (xy 142.2 115.003554) (xy 141.875975 115.327577) - (xy 136.147726 115.327577) (xy 136.033501 115.280264) (xy 136.033489 115.280261) (xy 135.878845 115.2495) - (xy 135.878842 115.2495) (xy 135.721158 115.2495) (xy 135.721155 115.2495) (xy 135.566501 115.280262) - (xy 135.56352 115.281167) (xy 135.561882 115.281181) (xy 135.560528 115.281451) (xy 135.560476 115.281193) - (xy 135.493653 115.281786) (xy 135.434543 115.244533) (xy 135.428266 115.236814) (xy 135.402548 115.202458) - (xy 135.402545 115.202455) (xy 135.402544 115.202454) (xy 135.40254 115.202451) (xy 135.287333 115.116206) - (xy 135.287326 115.116202) (xy 135.15248 115.065908) (xy 135.152481 115.065908) (xy 135.092881 115.059501) - (xy 135.092879 115.0595) (xy 135.092871 115.0595) (xy 135.092862 115.0595) (xy 132.487127 115.0595) - (xy 132.487121 115.059501) (xy 132.427514 115.065908) (xy 132.292669 115.116202) (xy 132.292662 115.116206) - (xy 132.177453 115.202452) (xy 132.17745 115.202455) (xy 132.091204 115.317664) (xy 132.0912 115.317671) - (xy 132.040682 115.453119) (xy 131.998811 115.509053) (xy 131.933347 115.53347) (xy 131.865074 115.518618) - (xy 131.815668 115.469213) (xy 131.8005 115.409786) (xy 131.8005 111.630213) (xy 131.820185 111.563174) - (xy 131.872989 111.517419) (xy 131.942147 111.507475) (xy 132.005703 111.5365) (xy 132.040682 111.58688) - (xy 132.0912 111.722328) (xy 132.091204 111.722335) (xy 132.17745 111.837544) (xy 132.177453 111.837547) - (xy 132.292662 111.923793) (xy 132.292669 111.923797) (xy 132.427515 111.974091) (xy 132.427514 111.974091) - (xy 132.434442 111.974835) (xy 132.487125 111.9805) (xy 135.09287 111.980499) (xy 135.152481 111.974091) - (xy 135.287329 111.923796) (xy 135.402544 111.837546) (xy 135.488794 111.722331) (xy 135.539089 111.587483) - (xy 135.545498 111.527873) (xy 135.545497 111.294093) (xy 135.565181 111.227055) (xy 135.617985 111.1813) - (xy 135.687144 111.171356) (xy 135.750699 111.200381) (xy 135.757178 111.206413) (xy 136.463179 111.912414) - (xy 136.496664 111.973737) (xy 136.499498 112.000095) (xy 136.499498 112.3955) (xy 136.479813 112.462539) - (xy 136.427009 112.508294) (xy 136.375498 112.5195) (xy 135.797127 112.5195) (xy 135.797121 112.519501) - (xy 135.737514 112.525908) (xy 135.602669 112.576202) (xy 135.602662 112.576206) (xy 135.487453 112.662452) - (xy 135.48745 112.662455) (xy 135.401204 112.777664) (xy 135.4012 112.777671) (xy 135.350906 112.912517) - (xy 135.344499 112.972116) (xy 135.344499 112.972123) (xy 135.344498 112.972135) (xy 135.344498 114.06787) - (xy 135.344499 114.067876) (xy 135.350906 114.127483) (xy 135.4012 114.262328) (xy 135.401204 114.262335) - (xy 135.48745 114.377544) (xy 135.487453 114.377547) (xy 135.602662 114.463793) (xy 135.602669 114.463797) - (xy 135.737515 114.514091) (xy 135.737514 114.514091) (xy 135.744442 114.514835) (xy 135.797125 114.5205) - (xy 138.40287 114.520499) (xy 138.462481 114.514091) (xy 138.597329 114.463796) (xy 138.712544 114.377546) - (xy 138.798794 114.262331) (xy 138.849089 114.127483) (xy 138.855498 114.067873) (xy 138.855497 113.626126) - (xy 140.875 113.626126) (xy 140.875 113.773873) (xy 140.90382 113.918759) (xy 140.903822 113.918767) - (xy 140.947421 114.024024) (xy 141.271446 113.7) (xy 141.271446 113.699999) (xy 141.251556 113.680109) - (xy 141.525 113.680109) (xy 141.525 113.719891) (xy 141.540224 113.756645) (xy 141.568355 113.784776) - (xy 141.605109 113.8) (xy 141.644891 113.8) (xy 141.681645 113.784776) (xy 141.709776 113.756645) - (xy 141.725 113.719891) (xy 141.725 113.7) (xy 141.978554 113.7) (xy 142.2 113.921446) (xy 142.421446 113.7) - (xy 142.2 113.478554) (xy 141.978554 113.7) (xy 141.725 113.7) (xy 141.725 113.680109) (xy 141.709776 113.643355) - (xy 141.681645 113.615224) (xy 141.644891 113.6) (xy 141.605109 113.6) (xy 141.568355 113.615224) - (xy 141.540224 113.643355) (xy 141.525 113.680109) (xy 141.251556 113.680109) (xy 140.947421 113.375974) - (xy 140.94742 113.375974) (xy 140.903823 113.481228) (xy 140.90382 113.48124) (xy 140.875 113.626126) - (xy 138.855497 113.626126) (xy 138.855497 113.496865) (xy 138.855497 112.972129) (xy 138.855496 112.972123) - (xy 138.855495 112.972116) (xy 138.849089 112.912517) (xy 138.834193 112.87258) (xy 138.798795 112.777671) - (xy 138.798791 112.777664) (xy 138.712545 112.662455) (xy 138.712542 112.662452) (xy 138.597333 112.576206) - (xy 138.597326 112.576202) (xy 138.46248 112.525908) (xy 138.462481 112.525908) (xy 138.402881 112.519501) - (xy 138.402879 112.5195) (xy 138.402871 112.5195) (xy 138.402863 112.5195) (xy 137.824498 112.5195) - (xy 137.757459 112.499815) (xy 137.711704 112.447011) (xy 137.700498 112.3955) (xy 137.700498 111.620941) - (xy 137.694355 111.598014) (xy 137.659575 111.468213) (xy 137.625403 111.409026) (xy 137.580518 111.331282) - (xy 137.517005 111.267769) (xy 137.483521 111.206445) (xy 137.488506 111.136754) (xy 137.530377 111.08082) - (xy 137.595842 111.056404) (xy 137.628875 111.05847) (xy 137.681855 111.069009) (xy 137.697971 111.072215) - (xy 137.697972 111.072215) (xy 137.855658 111.072215) (xy 137.855659 111.072214) (xy 138.010311 111.041452) - (xy 138.155993 110.981109) (xy 138.287103 110.893504) (xy 138.287106 110.893501) (xy 138.31285 110.867758) - (xy 138.374173 110.834273) (xy 138.400531 110.831439) (xy 141.146696 110.831439) (xy 141.213735 110.851124) - (xy 141.234377 110.867758) (xy 142.162679 111.79606) (xy 142.196164 111.857383) (xy 142.19118 111.927075) - (xy 142.149308 111.983008) (xy 142.09919 112.005358) (xy 141.981238 112.02882) (xy 141.981228 112.028823) - (xy 141.875974 112.07242) (xy 141.875974 112.072421) (xy 142.289095 112.485542) (xy 142.32258 112.546865) - (xy 142.317596 112.616557) (xy 142.289095 112.660904) (xy 142.27071 112.679289) (xy 142.256645 112.665224) - (xy 142.219891 112.65) (xy 142.180109 112.65) (xy 142.143355 112.665224) (xy 142.115224 112.693355) - (xy 142.1 112.730109) (xy 142.1 112.769891) (xy 142.115224 112.806645) (xy 142.129289 112.82071) - (xy 142.110904 112.839095) (xy 142.049581 112.87258) (xy 141.979889 112.867596) (xy 141.935542 112.839095) - (xy 141.522421 112.425974) (xy 141.52242 112.425974) (xy 141.478823 112.531228) (xy 141.47882 112.53124) - (xy 141.45 112.676126) (xy 141.45 112.823869) (xy 141.455291 112.850471) (xy 141.449062 112.920062) - (xy 141.406198 112.975239) (xy 141.381126 112.989221) (xy 141.300974 113.022421) (xy 141.624999 113.346446) - (xy 141.982796 112.98865) (xy 142.044119 112.955165) (xy 142.113811 112.960149) (xy 142.158158 112.98865) - (xy 142.2 113.030492) (xy 142.241842 112.98865) (xy 142.303165 112.955165) (xy 142.372857 112.960149) - (xy 142.417204 112.98865) (xy 142.864096 113.435542) (xy 142.897581 113.496865) (xy 142.892597 113.566557) - (xy 142.864096 113.610904) (xy 142.84571 113.629289) (xy 142.831645 113.615224) (xy 142.794891 113.6) - (xy 142.755109 113.6) (xy 142.718355 113.615224) (xy 142.690224 113.643355) (xy 142.675 113.680109) - (xy 142.675 113.719891) (xy 142.690224 113.756645) (xy 142.718355 113.784776) (xy 142.755109 113.8) - (xy 142.794891 113.8) (xy 142.831645 113.784776) (xy 142.84571 113.77071) (xy 142.864096 113.789096) - (xy 142.897581 113.850419) (xy 142.892597 113.920111) (xy 142.864096 113.964458) (xy 142.417204 114.41135) - (xy 142.355881 114.444835) (xy 142.286189 114.439851) (xy 142.241842 114.41135) (xy 142.2 114.369508) - (xy 142.158158 114.41135) (xy 142.096834 114.444834) (xy 142.027143 114.439849) (xy 141.982796 114.411349) - (xy 141.625001 114.053554) (xy 141.625 114.053554) (xy 141.300975 114.377577) (xy 141.381127 114.410778) - (xy 141.43553 114.45462) (xy 141.457594 114.520914) (xy 141.455291 114.549527) (xy 141.45 114.576129) - (xy 141.45 114.723873) (xy 141.47882 114.868759) (xy 141.478822 114.868767) (xy 141.522421 114.974024) - (xy 141.935542 114.560904) (xy 141.996865 114.527419) (xy 142.066557 114.532403) (xy 142.110904 114.560904) - (xy 142.129289 114.579289) (xy 142.115224 114.593355) (xy 142.1 114.630109) (xy 142.1 114.669891) - (xy 142.115224 114.706645) (xy 142.143355 114.734776) (xy 142.180109 114.75) (xy 142.219891 114.75) - (xy 142.256645 114.734776) (xy 142.284776 114.706645) (xy 142.3 114.669891) (xy 142.3 114.630109) - (xy 142.284776 114.593355) (xy 142.270709 114.579288) (xy 142.289094 114.560904) (xy 142.350417 114.527419) - (xy 142.420109 114.532403) (xy 142.464457 114.560904) (xy 142.877577 114.974024) (xy 142.877578 114.974024) - (xy 142.921175 114.868775) (xy 142.921179 114.86876) (xy 142.949999 114.723873) (xy 142.95 114.723871) - (xy 142.95 114.576129) (xy 142.944709 114.549532) (xy 142.950936 114.47994) (xy 142.993798 114.424762) - (xy 143.018873 114.410778) (xy 143.118047 114.369699) (xy 143.187517 114.36223) (xy 143.249996 114.393505) - (xy 143.285648 114.453594) (xy 143.2895 114.48426) (xy 143.2895 116.809982) (xy 143.269815 116.877021) - (xy 143.25 116.899723) (xy 143.25 117.96967) (xy 143.230255 117.949925) (xy 143.144745 117.900556) - (xy 143.04937 117.875) (xy 142.95063 117.875) (xy 142.855255 117.900556) (xy 142.769745 117.949925) - (xy 142.75 117.96967) (xy 142.75 116.900884) (xy 142.749999 116.900884) (xy 142.742418 116.902085) - (xy 142.74241 116.902087) (xy 142.577742 116.955591) (xy 142.423475 117.034195) (xy 142.283397 117.135967) - (xy 142.160967 117.258397) (xy 142.059195 117.398475) (xy 141.980591 117.552742) (xy 141.927085 117.717415) - (xy 141.9 117.888428) (xy 141.9 118) (xy 142.71967 118) (xy 142.699925 118.019745) (xy 142.650556 118.105255) - (xy 142.625 118.20063) (xy 142.625 118.29937) (xy 142.650556 118.394745) (xy 142.699925 118.480255) - (xy 142.71967 118.5) (xy 141.9 118.5) (xy 141.9 118.611571) (xy 141.927085 118.782584) (xy 141.980591 118.947257) - (xy 142.059195 119.101524) (xy 142.160967 119.241602) (xy 142.283397 119.364032) (xy 142.423475 119.465804) - (xy 142.577744 119.544408) (xy 142.742415 119.597914) (xy 142.742414 119.597914) (xy 142.749999 119.599115) - (xy 142.75 119.599114) (xy 142.75 118.53033) (xy 142.769745 118.550075) (xy 142.855255 118.599444) - (xy 142.95063 118.625) (xy 143.04937 118.625) (xy 143.144745 118.599444) (xy 143.230255 118.550075) - (xy 143.25 118.53033) (xy 143.25 119.599115) (xy 143.257584 119.597914) (xy 143.422255 119.544408) - (xy 143.576524 119.465804) (xy 143.716602 119.364032) (xy 143.839036 119.241598) (xy 143.899371 119.158552) - (xy 143.9547 119.115886) (xy 144.024313 119.109905) (xy 144.086109 119.14251) (xy 144.100008 119.15855) - (xy 144.120587 119.186874) (xy 144.160586 119.241928) (xy 144.283072 119.364414) (xy 144.423212 119.466232) - (xy 144.577555 119.544873) (xy 144.742299 119.598402) (xy 144.913389 119.6255) (xy 144.91339 119.6255) - (xy 145.08661 119.6255) (xy 145.086611 119.6255) (xy 145.257701 119.598402) (xy 145.422445 119.544873) - (xy 145.576788 119.466232) (xy 145.716928 119.364414) (xy 145.839414 119.241928) (xy 145.899991 119.15855) - (xy 145.955321 119.115885) (xy 146.024934 119.109906) (xy 146.086729 119.142512) (xy 146.100628 119.158551) - (xy 146.160967 119.241602) (xy 146.283397 119.364032) (xy 146.423475 119.465804) (xy 146.577744 119.544408) - (xy 146.742415 119.597914) (xy 146.742414 119.597914) (xy 146.749999 119.599115) (xy 146.75 119.599114) - (xy 146.75 118.53033) (xy 146.769745 118.550075) (xy 146.855255 118.599444) (xy 146.95063 118.625) - (xy 147.04937 118.625) (xy 147.144745 118.599444) (xy 147.230255 118.550075) (xy 147.25 118.53033) - (xy 147.25 119.599115) (xy 147.257584 119.597914) (xy 147.422255 119.544408) (xy 147.576524 119.465804) - (xy 147.716602 119.364032) (xy 147.839036 119.241598) (xy 147.899371 119.158552) (xy 147.9547 119.115886) - (xy 148.024313 119.109905) (xy 148.086109 119.14251) (xy 148.100008 119.15855) (xy 148.120587 119.186874) - (xy 148.160586 119.241928) (xy 148.283072 119.364414) (xy 148.423212 119.466232) (xy 148.577555 119.544873) - (xy 148.742299 119.598402) (xy 148.913389 119.6255) (xy 148.91339 119.6255) (xy 149.08661 119.6255) - (xy 149.086611 119.6255) (xy 149.257701 119.598402) (xy 149.422445 119.544873) (xy 149.576788 119.466232) - (xy 149.716928 119.364414) (xy 149.839414 119.241928) (xy 149.899991 119.15855) (xy 149.955321 119.115885) - (xy 150.024934 119.109906) (xy 150.086729 119.142512) (xy 150.100628 119.158551) (xy 150.160967 119.241602) - (xy 150.283397 119.364032) (xy 150.423475 119.465804) (xy 150.577744 119.544408) (xy 150.742415 119.597914) - (xy 150.742414 119.597914) (xy 150.749999 119.599115) (xy 150.75 119.599114) (xy 150.75 118.53033) - (xy 150.769745 118.550075) (xy 150.855255 118.599444) (xy 150.95063 118.625) (xy 151.04937 118.625) - (xy 151.144745 118.599444) (xy 151.230255 118.550075) (xy 151.25 118.53033) (xy 151.25 119.599115) - (xy 151.257584 119.597914) (xy 151.422255 119.544408) (xy 151.576524 119.465804) (xy 151.716602 119.364032) - (xy 151.839036 119.241598) (xy 151.899371 119.158552) (xy 151.9547 119.115886) (xy 152.024313 119.109905) - (xy 152.086109 119.14251) (xy 152.100008 119.15855) (xy 152.120587 119.186874) (xy 152.160586 119.241928) - (xy 152.283072 119.364414) (xy 152.423212 119.466232) (xy 152.577555 119.544873) (xy 152.742299 119.598402) - (xy 152.913389 119.6255) (xy 152.91339 119.6255) (xy 153.08661 119.6255) (xy 153.086611 119.6255) - (xy 153.257701 119.598402) (xy 153.422445 119.544873) (xy 153.576788 119.466232) (xy 153.716928 119.364414) - (xy 153.839414 119.241928) (xy 153.899991 119.15855) (xy 153.955321 119.115885) (xy 154.024934 119.109906) - (xy 154.086729 119.142512) (xy 154.100628 119.158551) (xy 154.160967 119.241602) (xy 154.283397 119.364032) - (xy 154.423475 119.465804) (xy 154.577744 119.544408) (xy 154.742415 119.597914) (xy 154.742414 119.597914) - (xy 154.749999 119.599115) (xy 154.75 119.599114) (xy 154.75 118.53033) (xy 154.769745 118.550075) - (xy 154.855255 118.599444) (xy 154.95063 118.625) (xy 155.04937 118.625) (xy 155.144745 118.599444) - (xy 155.230255 118.550075) (xy 155.25 118.53033) (xy 155.25 119.599115) (xy 155.257584 119.597914) - (xy 155.422255 119.544408) (xy 155.576524 119.465804) (xy 155.716598 119.364035) (xy 155.824199 119.256434) - (xy 155.885522 119.222949) (xy 155.955214 119.227933) (xy 156.011148 119.269804) (xy 156.01742 119.279019) - (xy 156.056319 119.342086) (xy 156.057288 119.343656) (xy 156.181344 119.467712) (xy 156.330666 119.559814) - (xy 156.497203 119.614999) (xy 156.599991 119.6255) (xy 157.400008 119.625499) (xy 157.400016 119.625498) - (xy 157.400019 119.625498) (xy 157.456302 119.619748) (xy 157.502797 119.614999) (xy 157.669334 119.559814) - (xy 157.818656 119.467712) (xy 157.942712 119.343656) (xy 158.034814 119.194334) (xy 158.089999 119.027797) - (xy 158.1005 118.925009) (xy 158.100499 118.25) (xy 158.100499 117.574998) (xy 158.100499 117.574992) - (xy 158.100406 117.574086) (xy 158.100297 117.573009) (xy 158.099632 117.566504) (xy 161.48 117.566504) - (xy 161.48 117.815) (xy 162.18 117.815) (xy 162.18 118.315) (xy 161.48 118.315) (xy 161.48 118.563495) - (xy 161.518427 118.756681) (xy 161.51843 118.756693) (xy 161.593807 118.938671) (xy 161.593814 118.938684) - (xy 161.703248 119.102462) (xy 161.703251 119.102466) (xy 161.842533 119.241748) (xy 161.842537 119.241751) - (xy 162.006315 119.351185) (xy 162.006328 119.351192) (xy 162.188308 119.426569) (xy 162.23 119.434862) - (xy 162.23 118.631988) (xy 162.23994 118.649205) (xy 162.295795 118.70506) (xy 162.364204 118.744556) - (xy 162.440504 118.765) (xy 162.519496 118.765) (xy 162.595796 118.744556) (xy 162.664205 118.70506) - (xy 162.72006 118.649205) (xy 162.73 118.631988) (xy 162.73 119.434862) (xy 162.77169 119.426569) - (xy 162.771692 119.426569) (xy 162.953671 119.351192) (xy 162.953684 119.351185) (xy 163.117462 119.241751) - (xy 163.117466 119.241748) (xy 163.256748 119.102466) (xy 163.256751 119.102462) (xy 163.366185 118.938684) - (xy 163.366192 118.938671) (xy 163.441569 118.756693) (xy 163.441572 118.756681) (xy 163.479999 118.563495) - (xy 163.48 118.563492) (xy 163.48 118.315) (xy 162.78 118.315) (xy 162.78 117.815) (xy 163.48 117.815) - (xy 163.48 117.566508) (xy 163.479999 117.566504) (xy 163.441572 117.373318) (xy 163.441569 117.373306) - (xy 163.366192 117.191328) (xy 163.366185 117.191315) (xy 163.256751 117.027537) (xy 163.256748 117.027533) - (xy 163.117466 116.888251) (xy 163.117462 116.888248) (xy 162.953684 116.778814) (xy 162.953671 116.778807) - (xy 162.771691 116.703429) (xy 162.771683 116.703427) (xy 162.73 116.695135) (xy 162.73 117.498011) - (xy 162.72006 117.480795) (xy 162.664205 117.42494) (xy 162.595796 117.385444) (xy 162.519496 117.365) - (xy 162.440504 117.365) (xy 162.364204 117.385444) (xy 162.295795 117.42494) (xy 162.23994 117.480795) - (xy 162.23 117.498011) (xy 162.23 116.695136) (xy 162.229999 116.695135) (xy 162.188316 116.703427) - (xy 162.188308 116.703429) (xy 162.006328 116.778807) (xy 162.006315 116.778814) (xy 161.842537 116.888248) - (xy 161.842533 116.888251) (xy 161.703251 117.027533) (xy 161.703248 117.027537) (xy 161.593814 117.191315) - (xy 161.593807 117.191328) (xy 161.51843 117.373306) (xy 161.518427 117.373318) (xy 161.48 117.566504) - (xy 158.099632 117.566504) (xy 158.096317 117.534055) (xy 158.089999 117.472203) (xy 158.034814 117.305666) - (xy 157.942712 117.156344) (xy 157.818656 117.032288) (xy 157.669334 116.940186) (xy 157.669332 116.940185) - (xy 157.663187 116.936395) (xy 157.66429 116.934605) (xy 157.619649 116.89529) (xy 157.6005 116.829091) - (xy 157.6005 114.586555) (xy 157.620185 114.519516) (xy 157.672989 114.473761) (xy 157.742147 114.463817) - (xy 157.760411 114.46884) (xy 157.760676 114.467969) (xy 157.766492 114.469732) (xy 157.766503 114.469737) - (xy 157.921153 114.500499) (xy 157.921156 114.5005) (xy 157.921158 114.5005) (xy 158.078844 114.5005) - (xy 158.078845 114.500499) (xy 158.233497 114.469737) (xy 158.379179 114.409394) (xy 158.510289 114.321789) - (xy 158.621789 114.210289) (xy 158.709394 114.079179) (xy 158.769737 113.933497) (xy 158.8005 113.778842) - (xy 158.8005 113.621158) (xy 158.8005 113.621155) (xy 158.800499 113.621153) (xy 158.769738 113.46651) - (xy 158.769737 113.466503) (xy 158.732239 113.375974) (xy 158.709397 113.320827) (xy 158.70939 113.320814) - (xy 158.621789 113.189711) (xy 158.621786 113.189707) (xy 158.512523 113.080444) (xy 158.479038 113.019121) - (xy 158.476204 112.992763) (xy 158.476204 111.871318) (xy 158.495889 111.804279) (xy 158.53131 111.768218) - (xy 158.586562 111.731301) (xy 158.698062 111.619801) (xy 158.785667 111.488691) (xy 158.84601 111.343009) - (xy 158.876773 111.188354) (xy 158.876773 111.03067) (xy 158.876773 111.030667) (xy 158.876772 111.030665) - (xy 158.858858 110.940605) (xy 158.84601 110.876015) (xy 158.843509 110.869977) (xy 158.78567 110.730339) - (xy 158.785663 110.730326) (xy 158.714838 110.624329) (xy 158.69396 110.557651) (xy 158.712445 110.490271) - (xy 158.764423 110.443581) (xy 158.81794 110.431438) (xy 159.897871 110.431438) (xy 159.897872 110.431438) - (xy 159.957483 110.42503) (xy 160.092331 110.374735) (xy 160.207546 110.288485) (xy 160.293796 110.17327) - (xy 160.344091 110.038422) (xy 160.345121 110.028838) (xy 160.371855 109.96429) (xy 160.429246 109.924439) - (xy 160.499071 109.921942) + (xy 135.492489 115.327577) (xy 135.488794 115.317669) (xy 135.488791 115.317664) (xy 135.402545 115.202455) + (xy 135.402542 115.202452) (xy 135.287333 115.116206) (xy 135.287326 115.116202) (xy 135.15248 115.065908) + (xy 135.152481 115.065908) (xy 135.092881 115.059501) (xy 135.092879 115.0595) (xy 135.092871 115.0595) + (xy 135.092863 115.0595) (xy 133.690095 115.0595) (xy 133.623056 115.039815) (xy 133.602414 115.023181) + (xy 133.217588 114.638355) (xy 133.217586 114.638352) (xy 133.098715 114.519481) (xy 133.098707 114.519475) + (xy 132.981023 114.451531) (xy 132.981023 114.45153) (xy 132.981018 114.451529) (xy 132.969423 114.444834) + (xy 132.961784 114.440423) (xy 132.903336 114.424762) (xy 132.809055 114.399499) (xy 132.679764 114.399499) + (xy 132.612725 114.379814) (xy 132.610873 114.378601) (xy 132.479185 114.290609) (xy 132.479172 114.290602) + (xy 132.333501 114.230264) (xy 132.333489 114.230261) (xy 132.178845 114.1995) (xy 132.178842 114.1995) + (xy 132.021158 114.1995) (xy 132.021155 114.1995) (xy 131.948691 114.213914) (xy 131.8791 114.207687) + (xy 131.823922 114.164824) (xy 131.800678 114.098934) (xy 131.8005 114.092297) (xy 131.8005 111.630213) + (xy 131.820185 111.563174) (xy 131.872989 111.517419) (xy 131.942147 111.507475) (xy 132.005703 111.5365) + (xy 132.040682 111.58688) (xy 132.0912 111.722328) (xy 132.091204 111.722335) (xy 132.17745 111.837544) + (xy 132.177453 111.837547) (xy 132.292662 111.923793) (xy 132.292669 111.923797) (xy 132.427515 111.974091) + (xy 132.427514 111.974091) (xy 132.434442 111.974835) (xy 132.487125 111.9805) (xy 135.09287 111.980499) + (xy 135.152481 111.974091) (xy 135.287329 111.923796) (xy 135.402544 111.837546) (xy 135.488794 111.722331) + (xy 135.539089 111.587483) (xy 135.545498 111.527873) (xy 135.545497 111.294093) (xy 135.565181 111.227055) + (xy 135.617985 111.1813) (xy 135.687144 111.171356) (xy 135.750699 111.200381) (xy 135.757178 111.206413) + (xy 136.463179 111.912414) (xy 136.496664 111.973737) (xy 136.499498 112.000095) (xy 136.499498 112.3955) + (xy 136.479813 112.462539) (xy 136.427009 112.508294) (xy 136.375498 112.5195) (xy 135.797127 112.5195) + (xy 135.797121 112.519501) (xy 135.737514 112.525908) (xy 135.602669 112.576202) (xy 135.602662 112.576206) + (xy 135.487453 112.662452) (xy 135.48745 112.662455) (xy 135.401204 112.777664) (xy 135.4012 112.777671) + (xy 135.350906 112.912517) (xy 135.344499 112.972116) (xy 135.344499 112.972123) (xy 135.344498 112.972135) + (xy 135.344498 114.06787) (xy 135.344499 114.067876) (xy 135.350906 114.127483) (xy 135.4012 114.262328) + (xy 135.401204 114.262335) (xy 135.48745 114.377544) (xy 135.487453 114.377547) (xy 135.602662 114.463793) + (xy 135.602669 114.463797) (xy 135.737515 114.514091) (xy 135.737514 114.514091) (xy 135.744442 114.514835) + (xy 135.797125 114.5205) (xy 138.40287 114.520499) (xy 138.462481 114.514091) (xy 138.597329 114.463796) + (xy 138.712544 114.377546) (xy 138.798794 114.262331) (xy 138.849089 114.127483) (xy 138.855498 114.067873) + (xy 138.855497 113.626126) (xy 140.875 113.626126) (xy 140.875 113.773873) (xy 140.90382 113.918759) + (xy 140.903822 113.918767) (xy 140.947421 114.024024) (xy 141.271446 113.7) (xy 141.271446 113.699999) + (xy 141.251556 113.680109) (xy 141.525 113.680109) (xy 141.525 113.719891) (xy 141.540224 113.756645) + (xy 141.568355 113.784776) (xy 141.605109 113.8) (xy 141.644891 113.8) (xy 141.681645 113.784776) + (xy 141.709776 113.756645) (xy 141.725 113.719891) (xy 141.725 113.7) (xy 141.978554 113.7) (xy 142.2 113.921446) + (xy 142.421446 113.7) (xy 142.2 113.478554) (xy 141.978554 113.7) (xy 141.725 113.7) (xy 141.725 113.680109) + (xy 141.709776 113.643355) (xy 141.681645 113.615224) (xy 141.644891 113.6) (xy 141.605109 113.6) + (xy 141.568355 113.615224) (xy 141.540224 113.643355) (xy 141.525 113.680109) (xy 141.251556 113.680109) + (xy 140.947421 113.375974) (xy 140.94742 113.375974) (xy 140.903823 113.481228) (xy 140.90382 113.48124) + (xy 140.875 113.626126) (xy 138.855497 113.626126) (xy 138.855497 113.496865) (xy 138.855497 112.972129) + (xy 138.855496 112.972123) (xy 138.855495 112.972116) (xy 138.849089 112.912517) (xy 138.834193 112.87258) + (xy 138.798795 112.777671) (xy 138.798791 112.777664) (xy 138.712545 112.662455) (xy 138.712542 112.662452) + (xy 138.597333 112.576206) (xy 138.597326 112.576202) (xy 138.46248 112.525908) (xy 138.462481 112.525908) + (xy 138.402881 112.519501) (xy 138.402879 112.5195) (xy 138.402871 112.5195) (xy 138.402863 112.5195) + (xy 137.824498 112.5195) (xy 137.757459 112.499815) (xy 137.711704 112.447011) (xy 137.700498 112.3955) + (xy 137.700498 111.620941) (xy 137.694355 111.598014) (xy 137.659575 111.468213) (xy 137.625403 111.409026) + (xy 137.580518 111.331282) (xy 137.517005 111.267769) (xy 137.483521 111.206445) (xy 137.488506 111.136754) + (xy 137.530377 111.08082) (xy 137.595842 111.056404) (xy 137.628875 111.05847) (xy 137.681855 111.069009) + (xy 137.697971 111.072215) (xy 137.697972 111.072215) (xy 137.855658 111.072215) (xy 137.855659 111.072214) + (xy 138.010311 111.041452) (xy 138.155993 110.981109) (xy 138.287103 110.893504) (xy 138.287106 110.893501) + (xy 138.31285 110.867758) (xy 138.374173 110.834273) (xy 138.400531 110.831439) (xy 141.146696 110.831439) + (xy 141.213735 110.851124) (xy 141.234377 110.867758) (xy 142.162679 111.79606) (xy 142.196164 111.857383) + (xy 142.19118 111.927075) (xy 142.149308 111.983008) (xy 142.09919 112.005358) (xy 141.981238 112.02882) + (xy 141.981228 112.028823) (xy 141.875974 112.07242) (xy 141.875974 112.072421) (xy 142.289095 112.485542) + (xy 142.32258 112.546865) (xy 142.317596 112.616557) (xy 142.289095 112.660904) (xy 142.27071 112.679289) + (xy 142.256645 112.665224) (xy 142.219891 112.65) (xy 142.180109 112.65) (xy 142.143355 112.665224) + (xy 142.115224 112.693355) (xy 142.1 112.730109) (xy 142.1 112.769891) (xy 142.115224 112.806645) + (xy 142.129289 112.82071) (xy 142.110904 112.839095) (xy 142.049581 112.87258) (xy 141.979889 112.867596) + (xy 141.935542 112.839095) (xy 141.522421 112.425974) (xy 141.52242 112.425974) (xy 141.478823 112.531228) + (xy 141.47882 112.53124) (xy 141.45 112.676126) (xy 141.45 112.823869) (xy 141.455291 112.850471) + (xy 141.449062 112.920062) (xy 141.406198 112.975239) (xy 141.381126 112.989221) (xy 141.300974 113.022421) + (xy 141.624999 113.346446) (xy 141.982796 112.98865) (xy 142.044119 112.955165) (xy 142.113811 112.960149) + (xy 142.158158 112.98865) (xy 142.2 113.030492) (xy 142.241842 112.98865) (xy 142.303165 112.955165) + (xy 142.372857 112.960149) (xy 142.417204 112.98865) (xy 142.864096 113.435542) (xy 142.897581 113.496865) + (xy 142.892597 113.566557) (xy 142.864096 113.610904) (xy 142.84571 113.629289) (xy 142.831645 113.615224) + (xy 142.794891 113.6) (xy 142.755109 113.6) (xy 142.718355 113.615224) (xy 142.690224 113.643355) + (xy 142.675 113.680109) (xy 142.675 113.719891) (xy 142.690224 113.756645) (xy 142.718355 113.784776) + (xy 142.755109 113.8) (xy 142.794891 113.8) (xy 142.831645 113.784776) (xy 142.84571 113.77071) + (xy 142.864096 113.789096) (xy 142.897581 113.850419) (xy 142.892597 113.920111) (xy 142.864096 113.964458) + (xy 142.417204 114.41135) (xy 142.355881 114.444835) (xy 142.286189 114.439851) (xy 142.241842 114.41135) + (xy 142.2 114.369508) (xy 142.158158 114.41135) (xy 142.096834 114.444834) (xy 142.027143 114.439849) + (xy 141.982796 114.411349) (xy 141.625001 114.053554) (xy 141.625 114.053554) (xy 141.300975 114.377577) + (xy 141.381127 114.410778) (xy 141.43553 114.45462) (xy 141.457594 114.520914) (xy 141.455291 114.549527) + (xy 141.45 114.576129) (xy 141.45 114.723873) (xy 141.47882 114.868759) (xy 141.478822 114.868767) + (xy 141.522421 114.974024) (xy 141.935542 114.560904) (xy 141.996865 114.527419) (xy 142.066557 114.532403) + (xy 142.110904 114.560904) (xy 142.129289 114.579289) (xy 142.115224 114.593355) (xy 142.1 114.630109) + (xy 142.1 114.669891) (xy 142.115224 114.706645) (xy 142.143355 114.734776) (xy 142.180109 114.75) + (xy 142.219891 114.75) (xy 142.256645 114.734776) (xy 142.284776 114.706645) (xy 142.3 114.669891) + (xy 142.3 114.630109) (xy 142.284776 114.593355) (xy 142.270709 114.579288) (xy 142.289094 114.560904) + (xy 142.350417 114.527419) (xy 142.420109 114.532403) (xy 142.464457 114.560904) (xy 142.877577 114.974024) + (xy 142.877578 114.974024) (xy 142.921175 114.868775) (xy 142.921179 114.86876) (xy 142.949999 114.723873) + (xy 142.95 114.723871) (xy 142.95 114.576129) (xy 142.944709 114.549532) (xy 142.950936 114.47994) + (xy 142.993798 114.424762) (xy 143.018873 114.410778) (xy 143.118047 114.369699) (xy 143.187517 114.36223) + (xy 143.249996 114.393505) (xy 143.285648 114.453594) (xy 143.2895 114.48426) (xy 143.2895 116.809982) + (xy 143.269815 116.877021) (xy 143.25 116.899723) (xy 143.25 117.96967) (xy 143.230255 117.949925) + (xy 143.144745 117.900556) (xy 143.04937 117.875) (xy 142.95063 117.875) (xy 142.855255 117.900556) + (xy 142.769745 117.949925) (xy 142.75 117.96967) (xy 142.75 116.900884) (xy 142.749999 116.900884) + (xy 142.742418 116.902085) (xy 142.74241 116.902087) (xy 142.577742 116.955591) (xy 142.423475 117.034195) + (xy 142.283397 117.135967) (xy 142.160967 117.258397) (xy 142.059195 117.398475) (xy 141.980591 117.552742) + (xy 141.927085 117.717415) (xy 141.9 117.888428) (xy 141.9 118) (xy 142.71967 118) (xy 142.699925 118.019745) + (xy 142.650556 118.105255) (xy 142.625 118.20063) (xy 142.625 118.29937) (xy 142.650556 118.394745) + (xy 142.699925 118.480255) (xy 142.71967 118.5) (xy 141.9 118.5) (xy 141.9 118.611571) (xy 141.927085 118.782584) + (xy 141.980591 118.947257) (xy 142.059195 119.101524) (xy 142.160967 119.241602) (xy 142.283397 119.364032) + (xy 142.423475 119.465804) (xy 142.577744 119.544408) (xy 142.742415 119.597914) (xy 142.742414 119.597914) + (xy 142.749999 119.599115) (xy 142.75 119.599114) (xy 142.75 118.53033) (xy 142.769745 118.550075) + (xy 142.855255 118.599444) (xy 142.95063 118.625) (xy 143.04937 118.625) (xy 143.144745 118.599444) + (xy 143.230255 118.550075) (xy 143.25 118.53033) (xy 143.25 119.599115) (xy 143.257584 119.597914) + (xy 143.422255 119.544408) (xy 143.576524 119.465804) (xy 143.716602 119.364032) (xy 143.839036 119.241598) + (xy 143.899371 119.158552) (xy 143.9547 119.115886) (xy 144.024313 119.109905) (xy 144.086109 119.14251) + (xy 144.100008 119.15855) (xy 144.120587 119.186874) (xy 144.160586 119.241928) (xy 144.283072 119.364414) + (xy 144.423212 119.466232) (xy 144.577555 119.544873) (xy 144.742299 119.598402) (xy 144.913389 119.6255) + (xy 144.91339 119.6255) (xy 145.08661 119.6255) (xy 145.086611 119.6255) (xy 145.257701 119.598402) + (xy 145.422445 119.544873) (xy 145.576788 119.466232) (xy 145.716928 119.364414) (xy 145.839414 119.241928) + (xy 145.899991 119.15855) (xy 145.955321 119.115885) (xy 146.024934 119.109906) (xy 146.086729 119.142512) + (xy 146.100628 119.158551) (xy 146.160967 119.241602) (xy 146.283397 119.364032) (xy 146.423475 119.465804) + (xy 146.577744 119.544408) (xy 146.742415 119.597914) (xy 146.742414 119.597914) (xy 146.749999 119.599115) + (xy 146.75 119.599114) (xy 146.75 118.53033) (xy 146.769745 118.550075) (xy 146.855255 118.599444) + (xy 146.95063 118.625) (xy 147.04937 118.625) (xy 147.144745 118.599444) (xy 147.230255 118.550075) + (xy 147.25 118.53033) (xy 147.25 119.599115) (xy 147.257584 119.597914) (xy 147.422255 119.544408) + (xy 147.576524 119.465804) (xy 147.716602 119.364032) (xy 147.839036 119.241598) (xy 147.899371 119.158552) + (xy 147.9547 119.115886) (xy 148.024313 119.109905) (xy 148.086109 119.14251) (xy 148.100008 119.15855) + (xy 148.120587 119.186874) (xy 148.160586 119.241928) (xy 148.283072 119.364414) (xy 148.423212 119.466232) + (xy 148.577555 119.544873) (xy 148.742299 119.598402) (xy 148.913389 119.6255) (xy 148.91339 119.6255) + (xy 149.08661 119.6255) (xy 149.086611 119.6255) (xy 149.257701 119.598402) (xy 149.422445 119.544873) + (xy 149.576788 119.466232) (xy 149.716928 119.364414) (xy 149.839414 119.241928) (xy 149.899991 119.15855) + (xy 149.955321 119.115885) (xy 150.024934 119.109906) (xy 150.086729 119.142512) (xy 150.100628 119.158551) + (xy 150.160967 119.241602) (xy 150.283397 119.364032) (xy 150.423475 119.465804) (xy 150.577744 119.544408) + (xy 150.742415 119.597914) (xy 150.742414 119.597914) (xy 150.749999 119.599115) (xy 150.75 119.599114) + (xy 150.75 118.53033) (xy 150.769745 118.550075) (xy 150.855255 118.599444) (xy 150.95063 118.625) + (xy 151.04937 118.625) (xy 151.144745 118.599444) (xy 151.230255 118.550075) (xy 151.25 118.53033) + (xy 151.25 119.599115) (xy 151.257584 119.597914) (xy 151.422255 119.544408) (xy 151.576524 119.465804) + (xy 151.716602 119.364032) (xy 151.839036 119.241598) (xy 151.899371 119.158552) (xy 151.9547 119.115886) + (xy 152.024313 119.109905) (xy 152.086109 119.14251) (xy 152.100008 119.15855) (xy 152.120587 119.186874) + (xy 152.160586 119.241928) (xy 152.283072 119.364414) (xy 152.423212 119.466232) (xy 152.577555 119.544873) + (xy 152.742299 119.598402) (xy 152.913389 119.6255) (xy 152.91339 119.6255) (xy 153.08661 119.6255) + (xy 153.086611 119.6255) (xy 153.257701 119.598402) (xy 153.422445 119.544873) (xy 153.576788 119.466232) + (xy 153.716928 119.364414) (xy 153.839414 119.241928) (xy 153.899991 119.15855) (xy 153.955321 119.115885) + (xy 154.024934 119.109906) (xy 154.086729 119.142512) (xy 154.100628 119.158551) (xy 154.160967 119.241602) + (xy 154.283397 119.364032) (xy 154.423475 119.465804) (xy 154.577744 119.544408) (xy 154.742415 119.597914) + (xy 154.742414 119.597914) (xy 154.749999 119.599115) (xy 154.75 119.599114) (xy 154.75 118.53033) + (xy 154.769745 118.550075) (xy 154.855255 118.599444) (xy 154.95063 118.625) (xy 155.04937 118.625) + (xy 155.144745 118.599444) (xy 155.230255 118.550075) (xy 155.25 118.53033) (xy 155.25 119.599115) + (xy 155.257584 119.597914) (xy 155.422255 119.544408) (xy 155.576524 119.465804) (xy 155.716598 119.364035) + (xy 155.824199 119.256434) (xy 155.885522 119.222949) (xy 155.955214 119.227933) (xy 156.011148 119.269804) + (xy 156.01742 119.279019) (xy 156.056319 119.342086) (xy 156.057288 119.343656) (xy 156.181344 119.467712) + (xy 156.330666 119.559814) (xy 156.497203 119.614999) (xy 156.599991 119.6255) (xy 157.400008 119.625499) + (xy 157.400016 119.625498) (xy 157.400019 119.625498) (xy 157.456302 119.619748) (xy 157.502797 119.614999) + (xy 157.669334 119.559814) (xy 157.818656 119.467712) (xy 157.942712 119.343656) (xy 158.034814 119.194334) + (xy 158.089999 119.027797) (xy 158.1005 118.925009) (xy 158.100499 118.25) (xy 158.100499 117.574998) + (xy 158.100499 117.574992) (xy 158.100406 117.574086) (xy 158.100297 117.573009) (xy 158.099632 117.566504) + (xy 161.48 117.566504) (xy 161.48 117.815) (xy 162.18 117.815) (xy 162.18 118.315) (xy 161.48 118.315) + (xy 161.48 118.563495) (xy 161.518427 118.756681) (xy 161.51843 118.756693) (xy 161.593807 118.938671) + (xy 161.593814 118.938684) (xy 161.703248 119.102462) (xy 161.703251 119.102466) (xy 161.842533 119.241748) + (xy 161.842537 119.241751) (xy 162.006315 119.351185) (xy 162.006328 119.351192) (xy 162.188308 119.426569) + (xy 162.23 119.434862) (xy 162.23 118.631988) (xy 162.23994 118.649205) (xy 162.295795 118.70506) + (xy 162.364204 118.744556) (xy 162.440504 118.765) (xy 162.519496 118.765) (xy 162.595796 118.744556) + (xy 162.664205 118.70506) (xy 162.72006 118.649205) (xy 162.73 118.631988) (xy 162.73 119.434862) + (xy 162.77169 119.426569) (xy 162.771692 119.426569) (xy 162.953671 119.351192) (xy 162.953684 119.351185) + (xy 163.117462 119.241751) (xy 163.117466 119.241748) (xy 163.256748 119.102466) (xy 163.256751 119.102462) + (xy 163.366185 118.938684) (xy 163.366192 118.938671) (xy 163.441569 118.756693) (xy 163.441572 118.756681) + (xy 163.479999 118.563495) (xy 163.48 118.563492) (xy 163.48 118.315) (xy 162.78 118.315) (xy 162.78 117.815) + (xy 163.48 117.815) (xy 163.48 117.566508) (xy 163.479999 117.566504) (xy 163.441572 117.373318) + (xy 163.441569 117.373306) (xy 163.366192 117.191328) (xy 163.366185 117.191315) (xy 163.256751 117.027537) + (xy 163.256748 117.027533) (xy 163.117466 116.888251) (xy 163.117462 116.888248) (xy 162.953684 116.778814) + (xy 162.953671 116.778807) (xy 162.771691 116.703429) (xy 162.771683 116.703427) (xy 162.73 116.695135) + (xy 162.73 117.498011) (xy 162.72006 117.480795) (xy 162.664205 117.42494) (xy 162.595796 117.385444) + (xy 162.519496 117.365) (xy 162.440504 117.365) (xy 162.364204 117.385444) (xy 162.295795 117.42494) + (xy 162.23994 117.480795) (xy 162.23 117.498011) (xy 162.23 116.695136) (xy 162.229999 116.695135) + (xy 162.188316 116.703427) (xy 162.188308 116.703429) (xy 162.006328 116.778807) (xy 162.006315 116.778814) + (xy 161.842537 116.888248) (xy 161.842533 116.888251) (xy 161.703251 117.027533) (xy 161.703248 117.027537) + (xy 161.593814 117.191315) (xy 161.593807 117.191328) (xy 161.51843 117.373306) (xy 161.518427 117.373318) + (xy 161.48 117.566504) (xy 158.099632 117.566504) (xy 158.096317 117.534055) (xy 158.089999 117.472203) + (xy 158.034814 117.305666) (xy 157.942712 117.156344) (xy 157.818656 117.032288) (xy 157.669334 116.940186) + (xy 157.669332 116.940185) (xy 157.663187 116.936395) (xy 157.66429 116.934605) (xy 157.619649 116.89529) + (xy 157.6005 116.829091) (xy 157.6005 114.586555) (xy 157.620185 114.519516) (xy 157.672989 114.473761) + (xy 157.742147 114.463817) (xy 157.760411 114.46884) (xy 157.760676 114.467969) (xy 157.766492 114.469732) + (xy 157.766503 114.469737) (xy 157.921153 114.500499) (xy 157.921156 114.5005) (xy 157.921158 114.5005) + (xy 158.078844 114.5005) (xy 158.078845 114.500499) (xy 158.233497 114.469737) (xy 158.379179 114.409394) + (xy 158.510289 114.321789) (xy 158.621789 114.210289) (xy 158.709394 114.079179) (xy 158.769737 113.933497) + (xy 158.8005 113.778842) (xy 158.8005 113.621158) (xy 158.8005 113.621155) (xy 158.800499 113.621153) + (xy 158.769738 113.46651) (xy 158.769737 113.466503) (xy 158.732239 113.375974) (xy 158.709397 113.320827) + (xy 158.70939 113.320814) (xy 158.621789 113.189711) (xy 158.621786 113.189707) (xy 158.512523 113.080444) + (xy 158.479038 113.019121) (xy 158.476204 112.992763) (xy 158.476204 111.871318) (xy 158.495889 111.804279) + (xy 158.53131 111.768218) (xy 158.586562 111.731301) (xy 158.698062 111.619801) (xy 158.785667 111.488691) + (xy 158.84601 111.343009) (xy 158.876773 111.188354) (xy 158.876773 111.03067) (xy 158.876773 111.030667) + (xy 158.876772 111.030665) (xy 158.858858 110.940605) (xy 158.84601 110.876015) (xy 158.843509 110.869977) + (xy 158.78567 110.730339) (xy 158.785663 110.730326) (xy 158.714838 110.624329) (xy 158.69396 110.557651) + (xy 158.712445 110.490271) (xy 158.764423 110.443581) (xy 158.81794 110.431438) (xy 159.897871 110.431438) + (xy 159.897872 110.431438) (xy 159.957483 110.42503) (xy 160.092331 110.374735) (xy 160.207546 110.288485) + (xy 160.293796 110.17327) (xy 160.344091 110.038422) (xy 160.345121 110.028838) (xy 160.371855 109.96429) + (xy 160.429246 109.924439) (xy 160.499071 109.921942) ) ) (filled_polygon @@ -21171,19 +21214,20 @@ (xy 145.137828 111.980939) (xy 145.137839 111.980938) (xy 145.172242 111.977239) (xy 145.241002 111.989643) (xy 145.29214 112.037252) (xy 145.3095 112.100528) (xy 145.3095 115.07333) (xy 145.309499 115.073348) (xy 145.309499 115.239054) (xy 145.309498 115.239054) (xy 145.338791 115.348374) (xy 145.350423 115.391785) - (xy 145.369311 115.424499) (xy 145.409423 115.493977) (xy 145.429479 115.528715) (xy 145.548349 115.647585) - (xy 145.548355 115.64759) (xy 146.644947 116.744182) (xy 146.678432 116.805505) (xy 146.673448 116.875197) - (xy 146.631576 116.93113) (xy 146.595588 116.949793) (xy 146.577743 116.955591) (xy 146.423475 117.034195) - (xy 146.283397 117.135967) (xy 146.160965 117.258399) (xy 146.160961 117.258404) (xy 146.100627 117.341448) - (xy 146.045297 117.384114) (xy 145.975684 117.390093) (xy 145.913889 117.357488) (xy 145.899991 117.341449) - (xy 145.89999 117.341447) (xy 145.839414 117.258072) (xy 145.716928 117.135586) (xy 145.576788 117.033768) - (xy 145.422445 116.955127) (xy 145.257701 116.901598) (xy 145.257699 116.901597) (xy 145.257698 116.901597) - (xy 145.126271 116.880781) (xy 145.086611 116.8745) (xy 144.913389 116.8745) (xy 144.881192 116.879599) - (xy 144.742301 116.901597) (xy 144.652818 116.930672) (xy 144.582976 116.932667) (xy 144.523144 116.896586) - (xy 144.492316 116.833885) (xy 144.4905 116.812741) (xy 144.4905 110.854939) (xy 144.49305 110.846253) - (xy 144.491762 110.837292) (xy 144.50274 110.813251) (xy 144.510185 110.7879) (xy 144.517025 110.781972) - (xy 144.520787 110.773736) (xy 144.543021 110.759446) (xy 144.562989 110.742145) (xy 144.573503 110.739857) - (xy 144.579565 110.735962) (xy 144.6145 110.730939) (xy 144.766 110.730939) + (xy 145.355165 115.399999) (xy 145.379358 115.4419) (xy 145.379359 115.441904) (xy 145.37936 115.441904) + (xy 145.429479 115.528714) (xy 145.429481 115.528717) (xy 145.548349 115.647585) (xy 145.548355 115.64759) + (xy 146.644947 116.744182) (xy 146.678432 116.805505) (xy 146.673448 116.875197) (xy 146.631576 116.93113) + (xy 146.595588 116.949793) (xy 146.577743 116.955591) (xy 146.423475 117.034195) (xy 146.283397 117.135967) + (xy 146.160965 117.258399) (xy 146.160961 117.258404) (xy 146.100627 117.341448) (xy 146.045297 117.384114) + (xy 145.975684 117.390093) (xy 145.913889 117.357488) (xy 145.899991 117.341449) (xy 145.89999 117.341447) + (xy 145.839414 117.258072) (xy 145.716928 117.135586) (xy 145.576788 117.033768) (xy 145.422445 116.955127) + (xy 145.257701 116.901598) (xy 145.257699 116.901597) (xy 145.257698 116.901597) (xy 145.126271 116.880781) + (xy 145.086611 116.8745) (xy 144.913389 116.8745) (xy 144.881192 116.879599) (xy 144.742301 116.901597) + (xy 144.652818 116.930672) (xy 144.582976 116.932667) (xy 144.523144 116.896586) (xy 144.492316 116.833885) + (xy 144.4905 116.812741) (xy 144.4905 110.854939) (xy 144.49305 110.846253) (xy 144.491762 110.837292) + (xy 144.50274 110.813251) (xy 144.510185 110.7879) (xy 144.517025 110.781972) (xy 144.520787 110.773736) + (xy 144.543021 110.759446) (xy 144.562989 110.742145) (xy 144.573503 110.739857) (xy 144.579565 110.735962) + (xy 144.6145 110.730939) (xy 144.766 110.730939) ) ) (filled_polygon @@ -21948,7 +21992,7 @@ (xy 168.249318 116.270382) (xy 168.249516 116.270397) (xy 168.250819 116.2705) (xy 168.74918 116.270499) (xy 168.749193 116.270498) (xy 168.750668 116.270382) (xy 168.760393 116.27) (xy 169.312844 116.27) (xy 169.272404 116.130805) (xy 169.238581 116.073612) (xy 169.221398 116.005888) (xy 169.238582 115.947369) - (xy 169.263639 115.905) (xy 169.272869 115.889393) (xy 169.30417 115.781654) (xy 169.317664 115.735208) + (xy 169.256133 115.917692) (xy 169.272869 115.889393) (xy 169.30417 115.781654) (xy 169.317664 115.735208) (xy 169.317664 115.735206) (xy 169.317665 115.735204) (xy 169.3205 115.699181) (xy 169.320499 115.30082) (xy 169.317665 115.264796) (xy 169.28968 115.16847) (xy 169.289879 115.098602) (xy 169.327821 115.039932) (xy 169.391459 115.011088) (xy 169.456205 115.019314) (xy 169.492964 115.03454) (xy 169.492968 115.03454) @@ -22060,13 +22104,13 @@ (xy 164.727594 116.120806) (xy 164.687156 116.26) (xy 165.239591 116.26) (xy 165.249318 116.260382) (xy 165.249516 116.260397) (xy 165.250819 116.2605) (xy 165.74918 116.260499) (xy 165.749193 116.260498) (xy 165.750668 116.260382) (xy 165.760393 116.26) (xy 166.312844 116.26) (xy 166.272404 116.120805) - (xy 166.238581 116.063612) (xy 166.221398 115.995888) (xy 166.238582 115.937369) (xy 166.257725 115.905) + (xy 166.238581 116.063612) (xy 166.221398 115.995888) (xy 166.238582 115.937369) (xy 166.250219 115.917692) (xy 166.272869 115.879393) (xy 166.314759 115.735208) (xy 166.317664 115.725208) (xy 166.317664 115.725206) (xy 166.317665 115.725204) (xy 166.3205 115.689181) (xy 166.320499 115.29082) (xy 166.317665 115.254796) (xy 166.272869 115.100607) (xy 166.212871 114.999156) (xy 166.195689 114.931434) (xy 166.208174 114.885268) - (xy 166.207063 114.884808) (xy 166.269735 114.733501) (xy 166.269737 114.733497) (xy 166.3005 114.578842) + (xy 166.207063 114.884808) (xy 166.256063 114.76651) (xy 166.269737 114.733497) (xy 166.3005 114.578842) (xy 166.3005 114.421158) (xy 166.3005 114.421155) (xy 166.300499 114.421153) (xy 166.298549 114.411349) - (xy 166.269737 114.266503) (xy 166.258861 114.240246) (xy 166.209397 114.120827) (xy 166.20939 114.120814) + (xy 166.269737 114.266503) (xy 166.258168 114.238572) (xy 166.209397 114.120827) (xy 166.20939 114.120814) (xy 166.121789 113.989711) (xy 166.121786 113.989707) (xy 166.010292 113.878213) (xy 166.010288 113.87821) (xy 165.879185 113.790609) (xy 165.879172 113.790602) (xy 165.733501 113.730264) (xy 165.733489 113.730261) (xy 165.578845 113.6995) (xy 165.578842 113.6995) (xy 165.421158 113.6995) (xy 165.421155 113.6995) @@ -22107,35 +22151,35 @@ (xy 162.633126 110.7705) (xy 162.702374 110.73052) (xy 162.814178 110.618716) (xy 162.814178 110.618714) (xy 162.824382 110.608511) (xy 162.824385 110.608506) (xy 164.819042 108.613851) (xy 164.880363 108.580368) (xy 164.88253 108.579917) (xy 164.883219 108.57978) (xy 164.88322 108.57978) (xy 165.037875 108.549017) - (xy 165.178893 108.490606) (xy 165.18355 108.488677) (xy 165.18355 108.488676) (xy 165.183557 108.488674) - (xy 165.314667 108.401069) (xy 165.426167 108.289569) (xy 165.513772 108.158459) (xy 165.513775 108.158452) - (xy 165.52929 108.120996) (xy 165.549878 108.095446) (xy 165.569541 108.069181) (xy 165.571689 108.068379) - (xy 165.57313 108.066592) (xy 165.604273 108.056226) (xy 165.635005 108.044764) (xy 165.637246 108.045251) - (xy 165.639424 108.044527) (xy 165.671217 108.052641) (xy 165.703278 108.059616) (xy 165.705727 108.061449) - (xy 165.707124 108.061806) (xy 165.731532 108.080767) (xy 165.863181 108.212416) (xy 165.896666 108.273739) - (xy 165.8995 108.300097) (xy 165.8995 111.162455) (xy 165.879815 111.229494) (xy 165.863185 111.250131) - (xy 165.782913 111.330404) (xy 165.705183 111.408134) (xy 165.64386 111.441618) (xy 165.641694 111.442069) - (xy 165.486352 111.472969) (xy 165.486342 111.472972) (xy 165.340671 111.53331) (xy 165.340658 111.533317) - (xy 165.209555 111.620918) (xy 165.209551 111.620921) (xy 165.098057 111.732415) (xy 165.098054 111.732419) - (xy 165.010453 111.863522) (xy 165.010446 111.863535) (xy 164.950108 112.009206) (xy 164.950105 112.009218) - (xy 164.919344 112.163861) (xy 164.919344 112.321554) (xy 164.950105 112.476197) (xy 164.950108 112.476209) - (xy 165.010446 112.62188) (xy 165.010453 112.621893) (xy 165.098054 112.752996) (xy 165.098057 112.753) - (xy 165.209551 112.864494) (xy 165.209555 112.864497) (xy 165.340658 112.952098) (xy 165.340671 112.952105) - (xy 165.467883 113.004797) (xy 165.486347 113.012445) (xy 165.599738 113.035) (xy 165.640997 113.043207) - (xy 165.641 113.043208) (xy 165.641002 113.043208) (xy 165.798688 113.043208) (xy 165.798689 113.043207) - (xy 165.953341 113.012445) (xy 166.099023 112.952102) (xy 166.230133 112.864497) (xy 166.341633 112.752997) - (xy 166.429238 112.621887) (xy 166.436954 112.60326) (xy 166.448741 112.574803) (xy 166.489581 112.476205) - (xy 166.508957 112.378793) (xy 166.520482 112.320858) (xy 166.552867 112.258947) (xy 166.554368 112.257418) - (xy 166.858506 111.953279) (xy 166.858511 111.953276) (xy 166.868714 111.943072) (xy 166.868716 111.943072) - (xy 166.98052 111.831268) (xy 167.032924 111.7405) (xy 167.059577 111.694337) (xy 167.1005 111.54161) - (xy 167.1005 111.383495) (xy 167.1005 107.920943) (xy 167.098955 107.915175) (xy 167.059577 107.768215) - (xy 167.019116 107.698135) (xy 166.98052 107.631284) (xy 166.868716 107.51948) (xy 166.868715 107.519479) - (xy 166.864385 107.515149) (xy 166.864374 107.515139) (xy 165.961416 106.612181) (xy 165.927931 106.550858) - (xy 165.932915 106.481166) (xy 165.974787 106.425233) (xy 166.040251 106.400816) (xy 166.049097 106.4005) - (xy 170.508883 106.4005) (xy 170.508899 106.400501) (xy 170.516495 106.400501) (xy 170.674606 106.400501) - (xy 170.674609 106.400501) (xy 170.827337 106.359577) (xy 170.896785 106.319481) (xy 170.964268 106.28052) - (xy 171.076072 106.168716) (xy 171.076072 106.168714) (xy 171.086276 106.158511) (xy 171.086279 106.158506) - (xy 171.48782 105.756966) (xy 171.549142 105.723482) + (xy 165.150544 108.502347) (xy 165.18355 108.488677) (xy 165.18355 108.488676) (xy 165.183557 108.488674) + (xy 165.314667 108.401069) (xy 165.426167 108.289569) (xy 165.513772 108.158459) (xy 165.52929 108.120996) + (xy 165.549878 108.095446) (xy 165.569541 108.069181) (xy 165.571689 108.068379) (xy 165.57313 108.066592) + (xy 165.604273 108.056226) (xy 165.635005 108.044764) (xy 165.637246 108.045251) (xy 165.639424 108.044527) + (xy 165.671217 108.052641) (xy 165.703278 108.059616) (xy 165.705727 108.061449) (xy 165.707124 108.061806) + (xy 165.731532 108.080767) (xy 165.863181 108.212416) (xy 165.896666 108.273739) (xy 165.8995 108.300097) + (xy 165.8995 111.162455) (xy 165.879815 111.229494) (xy 165.863185 111.250131) (xy 165.782913 111.330404) + (xy 165.705183 111.408134) (xy 165.64386 111.441618) (xy 165.641694 111.442069) (xy 165.486352 111.472969) + (xy 165.486342 111.472972) (xy 165.340671 111.53331) (xy 165.340658 111.533317) (xy 165.209555 111.620918) + (xy 165.209551 111.620921) (xy 165.098057 111.732415) (xy 165.098054 111.732419) (xy 165.010453 111.863522) + (xy 165.010446 111.863535) (xy 164.950108 112.009206) (xy 164.950105 112.009218) (xy 164.919344 112.163861) + (xy 164.919344 112.321554) (xy 164.950105 112.476197) (xy 164.950108 112.476209) (xy 165.010446 112.62188) + (xy 165.010453 112.621893) (xy 165.098054 112.752996) (xy 165.098057 112.753) (xy 165.209551 112.864494) + (xy 165.209555 112.864497) (xy 165.340658 112.952098) (xy 165.340671 112.952105) (xy 165.467883 113.004797) + (xy 165.486347 113.012445) (xy 165.599738 113.035) (xy 165.640997 113.043207) (xy 165.641 113.043208) + (xy 165.641002 113.043208) (xy 165.798688 113.043208) (xy 165.798689 113.043207) (xy 165.953341 113.012445) + (xy 166.099023 112.952102) (xy 166.230133 112.864497) (xy 166.341633 112.752997) (xy 166.429238 112.621887) + (xy 166.431945 112.615353) (xy 166.448741 112.574803) (xy 166.489581 112.476205) (xy 166.509358 112.376779) + (xy 166.520482 112.320858) (xy 166.552867 112.258947) (xy 166.554368 112.257418) (xy 166.858506 111.953279) + (xy 166.858511 111.953276) (xy 166.868714 111.943072) (xy 166.868716 111.943072) (xy 166.98052 111.831268) + (xy 167.032924 111.7405) (xy 167.059577 111.694337) (xy 167.1005 111.54161) (xy 167.1005 111.383495) + (xy 167.1005 107.920943) (xy 167.098955 107.915175) (xy 167.059577 107.768215) (xy 167.019116 107.698135) + (xy 166.98052 107.631284) (xy 166.868716 107.51948) (xy 166.868715 107.519479) (xy 166.864385 107.515149) + (xy 166.864374 107.515139) (xy 165.961416 106.612181) (xy 165.927931 106.550858) (xy 165.932915 106.481166) + (xy 165.974787 106.425233) (xy 166.040251 106.400816) (xy 166.049097 106.4005) (xy 170.508883 106.4005) + (xy 170.508899 106.400501) (xy 170.516495 106.400501) (xy 170.674606 106.400501) (xy 170.674609 106.400501) + (xy 170.827337 106.359577) (xy 170.896785 106.319481) (xy 170.964268 106.28052) (xy 171.076072 106.168716) + (xy 171.076072 106.168714) (xy 171.086276 106.158511) (xy 171.086279 106.158506) (xy 171.48782 105.756966) + (xy 171.549142 105.723482) ) ) (filled_polygon @@ -22187,8 +22231,28 @@ (filled_polygon (layer "B.Cu") (pts - (xy 148.942538 105.170185) (xy 148.988293 105.222989) (xy 148.999499 105.2745) (xy 148.999499 105.354733) - (xy 148.9995 105.354746) (xy 148.9995 111.264801) (xy 148.999499 111.264819) (xy 148.999499 111.430525) + (xy 136.129727 104.979213) (xy 136.134546 104.983781) (xy 138.24605 107.095285) (xy 138.24606 107.095296) + (xy 138.25039 107.099626) (xy 138.250391 107.099627) (xy 138.362195 107.211431) (xy 138.362197 107.211432) + (xy 138.362201 107.211435) (xy 138.49912 107.290484) (xy 138.499127 107.290488) (xy 138.596881 107.316681) + (xy 138.651853 107.331411) (xy 138.651854 107.331411) (xy 139.640234 107.331411) (xy 139.707273 107.351096) + (xy 139.709125 107.352309) (xy 139.840814 107.440301) (xy 139.840827 107.440308) (xy 139.936936 107.480117) + (xy 139.986503 107.500648) (xy 140.141153 107.53141) (xy 140.141156 107.531411) (xy 140.141158 107.531411) + (xy 140.298844 107.531411) (xy 140.298845 107.53141) (xy 140.453497 107.500648) (xy 140.599179 107.440305) + (xy 140.730289 107.3527) (xy 140.841789 107.2412) (xy 140.929394 107.11009) (xy 140.989737 106.964408) + (xy 141.0205 106.809753) (xy 141.0205 106.652069) (xy 141.0205 106.652066) (xy 141.020499 106.652064) + (xy 141.003241 106.565302) (xy 140.989737 106.497414) (xy 140.962346 106.431286) (xy 140.929397 106.351738) + (xy 140.92939 106.351725) (xy 140.841789 106.220622) (xy 140.841786 106.220618) (xy 140.730292 106.109124) + (xy 140.730288 106.109121) (xy 140.599185 106.02152) (xy 140.599172 106.021513) (xy 140.453501 105.961175) + (xy 140.453489 105.961172) (xy 140.298845 105.930411) (xy 140.298842 105.930411) (xy 140.141158 105.930411) + (xy 140.141155 105.930411) (xy 139.98651 105.961172) (xy 139.986498 105.961175) (xy 139.840827 106.021513) + (xy 139.840814 106.02152) (xy 139.709125 106.109513) (xy 139.642447 106.130391) (xy 139.640234 106.130411) + (xy 139.031008 106.130411) (xy 138.963969 106.110726) (xy 138.943327 106.094092) (xy 138.211416 105.362181) + (xy 138.177931 105.300858) (xy 138.182915 105.231166) (xy 138.224787 105.175233) (xy 138.290251 105.150816) + (xy 138.299097 105.1505) (xy 148.875499 105.1505) (xy 148.884184 105.15305) (xy 148.893145 105.151762) + (xy 148.917186 105.16274) (xy 148.942538 105.170185) (xy 148.948464 105.177024) (xy 148.956701 105.180786) + (xy 148.970991 105.203021) (xy 148.988293 105.222989) (xy 148.99058 105.233502) (xy 148.994476 105.239564) + (xy 148.999498 105.274494) (xy 148.999499 105.274498) (xy 148.999499 105.344744) (xy 148.999499 105.344746) + (xy 148.9995 105.35474) (xy 148.9995 111.264801) (xy 148.999499 111.264819) (xy 148.999499 111.430525) (xy 148.999498 111.430525) (xy 149.040424 111.58326) (xy 149.040425 111.583261) (xy 149.061735 111.62017) (xy 149.061736 111.620171) (xy 149.119477 111.720183) (xy 149.119481 111.720188) (xy 149.238349 111.839056) (xy 149.238355 111.839061) (xy 150.671221 113.271927) (xy 150.702615 113.32501) (xy 150.722849 113.394655) @@ -22256,7 +22320,7 @@ (xy 144.399999 114.303843) (xy 144.399999 114.096163) (xy 144.389193 114.006168) (xy 144.385891 113.997795) (xy 144.379607 113.928208) (xy 144.384818 113.911008) (xy 144.386073 113.906323) (xy 144.386073 113.906322) (xy 144.386074 113.90632) (xy 144.4005 113.796741) (xy 144.400499 113.60326) (xy 144.400499 113.603258) - (xy 144.400499 113.603256) (xy 144.386075 113.493688) (xy 144.386074 113.49368) (xy 144.38607 113.493672) + (xy 144.400499 113.603256) (xy 144.386972 113.500499) (xy 144.386074 113.49368) (xy 144.38607 113.493672) (xy 144.383971 113.485834) (xy 144.386321 113.485204) (xy 144.380165 113.427997) (xy 144.384205 113.414234) (xy 144.38397 113.414171) (xy 144.386072 113.406323) (xy 144.386074 113.40632) (xy 144.4005 113.296741) (xy 144.400499 113.10326) (xy 144.400499 113.103259) (xy 144.400499 113.103256) (xy 144.386075 112.993688) @@ -22267,7 +22331,7 @@ (xy 144.239753 112.240245) (xy 144.201862 112.21117) (xy 144.168723 112.172597) (xy 144.161468 112.159419) (xy 144.159577 112.152361) (xy 144.117255 112.079058) (xy 144.116639 112.077991) (xy 144.116628 112.077971) (xy 144.09748 112.044804) (xy 144.083213 111.991559) (xy 144.081465 111.991732) (xy 144.080868 111.985677) - (xy 144.080868 111.985672) (xy 144.050105 111.831017) (xy 144.03705 111.7995) (xy 143.989765 111.685341) + (xy 144.080868 111.985672) (xy 144.050105 111.831017) (xy 144.011475 111.737755) (xy 143.989765 111.685341) (xy 143.989758 111.685328) (xy 143.902157 111.554225) (xy 143.902154 111.554221) (xy 143.79066 111.442727) (xy 143.790656 111.442724) (xy 143.659553 111.355123) (xy 143.65954 111.355116) (xy 143.513869 111.294778) (xy 143.513857 111.294775) (xy 143.359213 111.264014) (xy 143.35921 111.264014) (xy 143.201526 111.264014) @@ -22292,7 +22356,7 @@ (xy 140.977145 110.27091) (xy 140.97879 110.25) (xy 140.45 110.25) (xy 140.45 110.75) (xy 140.019295 110.75) (xy 140.017643 110.729007) (xy 140.013775 110.715694) (xy 139.972495 110.573608) (xy 139.972492 110.5736) (xy 139.967266 110.564763) (xy 139.962916 110.548862) (xy 139.955023 110.53658) (xy 139.95 110.501645) - (xy 139.95 110.498352) (xy 139.967267 110.435233) (xy 139.972494 110.426395) (xy 139.974498 110.419499) + (xy 139.95 110.498352) (xy 139.967267 110.435233) (xy 139.972494 110.426395) (xy 139.980128 110.400121) (xy 140.017642 110.270997) (xy 140.017643 110.270991) (xy 140.0205 110.23469) (xy 140.0205 109.76531) (xy 140.017643 109.729007) (xy 140.012875 109.712597) (xy 139.972495 109.573608) (xy 139.972492 109.5736) (xy 139.967266 109.564763) (xy 139.95 109.501645) (xy 139.95 109.195494) (xy 140.45 109.195494) @@ -22312,26 +22376,10 @@ (xy 137.266521 109.649928) (xy 137.155027 109.761422) (xy 137.155024 109.761426) (xy 137.067423 109.892529) (xy 137.067416 109.892542) (xy 137.007078 110.038213) (xy 137.007075 110.038225) (xy 136.976314 110.192868) (xy 136.976314 110.2955) (xy 136.956629 110.362539) (xy 136.903825 110.408294) (xy 136.852314 110.4195) - (xy 136.5245 110.4195) (xy 136.457461 110.399815) (xy 136.411706 110.347011) (xy 136.4005 110.2955) - (xy 136.4005 105.549097) (xy 136.420185 105.482058) (xy 136.472989 105.436303) (xy 136.542147 105.426359) - (xy 136.605703 105.455384) (xy 136.612181 105.461416) (xy 138.24605 107.095285) (xy 138.24606 107.095296) - (xy 138.25039 107.099626) (xy 138.250391 107.099627) (xy 138.362195 107.211431) (xy 138.362197 107.211432) - (xy 138.362201 107.211435) (xy 138.49912 107.290484) (xy 138.499127 107.290488) (xy 138.596881 107.316681) - (xy 138.651853 107.331411) (xy 138.651854 107.331411) (xy 139.640234 107.331411) (xy 139.707273 107.351096) - (xy 139.709125 107.352309) (xy 139.840814 107.440301) (xy 139.840827 107.440308) (xy 139.936936 107.480117) - (xy 139.986503 107.500648) (xy 140.141153 107.53141) (xy 140.141156 107.531411) (xy 140.141158 107.531411) - (xy 140.298844 107.531411) (xy 140.298845 107.53141) (xy 140.453497 107.500648) (xy 140.599179 107.440305) - (xy 140.730289 107.3527) (xy 140.841789 107.2412) (xy 140.929394 107.11009) (xy 140.989737 106.964408) - (xy 141.0205 106.809753) (xy 141.0205 106.652069) (xy 141.0205 106.652066) (xy 141.020499 106.652064) - (xy 141.003241 106.565302) (xy 140.989737 106.497414) (xy 140.962346 106.431286) (xy 140.929397 106.351738) - (xy 140.92939 106.351725) (xy 140.841789 106.220622) (xy 140.841786 106.220618) (xy 140.730292 106.109124) - (xy 140.730288 106.109121) (xy 140.599185 106.02152) (xy 140.599172 106.021513) (xy 140.453501 105.961175) - (xy 140.453489 105.961172) (xy 140.298845 105.930411) (xy 140.298842 105.930411) (xy 140.141158 105.930411) - (xy 140.141155 105.930411) (xy 139.98651 105.961172) (xy 139.986498 105.961175) (xy 139.840827 106.021513) - (xy 139.840814 106.02152) (xy 139.709125 106.109513) (xy 139.642447 106.130391) (xy 139.640234 106.130411) - (xy 139.031008 106.130411) (xy 138.963969 106.110726) (xy 138.943327 106.094092) (xy 138.211416 105.362181) - (xy 138.177931 105.300858) (xy 138.182915 105.231166) (xy 138.224787 105.175233) (xy 138.290251 105.150816) - (xy 138.299097 105.1505) (xy 148.875499 105.1505) + (xy 136.580097 110.4195) (xy 136.513058 110.399815) (xy 136.492416 110.383181) (xy 135.886819 109.777584) + (xy 135.853334 109.716261) (xy 135.8505 109.689903) (xy 135.8505 105.179765) (xy 135.855827 105.161621) + (xy 135.856165 105.142713) (xy 135.869666 105.114492) (xy 135.870185 105.112726) (xy 135.871397 105.110875) + (xy 135.87274 105.108865) (xy 135.943764 105.002569) (xy 135.997374 104.957766) (xy 136.066699 104.949059) ) ) (filled_polygon @@ -22368,17 +22416,18 @@ (filled_polygon (layer "B.Cu") (pts - (xy 162.023966 104.252485) (xy 162.032697 104.251198) (xy 162.056977 104.262179) (xy 162.082539 104.269685) - (xy 162.088316 104.276352) (xy 162.096359 104.27999) (xy 162.110848 104.302356) (xy 162.128294 104.322489) - (xy 162.130519 104.332719) (xy 162.134348 104.338629) (xy 162.139499 104.373545) (xy 162.1395 104.373963) - (xy 162.139501 104.49918) (xy 162.139981 104.505293) (xy 162.139999 104.509938) (xy 162.14 104.510393) - (xy 162.14 105.539591) (xy 162.139618 105.549316) (xy 162.1395 105.550804) (xy 162.1395 105.550819) - (xy 162.1395 105.784799) (xy 162.139501 105.8) (xy 162.139501 106.049193) (xy 162.142335 106.085204) - (xy 162.185076 106.232321) (xy 162.19 106.266916) (xy 162.19 106.301645) (xy 162.172734 106.364763) - (xy 162.167507 106.3736) (xy 162.167504 106.373608) (xy 162.122357 106.529002) (xy 162.122356 106.529008) - (xy 162.1195 106.565302) (xy 162.1195 106.676) (xy 162.099815 106.743039) (xy 162.047011 106.788794) - (xy 161.9955 106.8) (xy 161.912 106.8) (xy 161.844961 106.780315) (xy 161.799206 106.727511) (xy 161.788 106.676) - (xy 161.788 104.374) (xy 161.807685 104.306961) (xy 161.860489 104.261206) (xy 161.912 104.25) (xy 162.0155 104.25) + (xy 162.082539 104.269685) (xy 162.128294 104.322489) (xy 162.1395 104.373999) (xy 162.1395 104.498094) + (xy 162.1395 104.498111) (xy 162.139501 104.49918) (xy 162.139722 104.501994) (xy 162.1398 104.503355) + (xy 162.139734 104.503635) (xy 162.14 104.510393) (xy 162.14 105.539591) (xy 162.139618 105.549316) + (xy 162.1395 105.550804) (xy 162.1395 105.550819) (xy 162.1395 105.784799) (xy 162.139501 105.8) + (xy 162.139501 106.049193) (xy 162.142335 106.085204) (xy 162.185076 106.232321) (xy 162.19 106.266916) + (xy 162.19 106.301645) (xy 162.172734 106.364763) (xy 162.167507 106.3736) (xy 162.167504 106.373608) + (xy 162.122357 106.529002) (xy 162.122356 106.529008) (xy 162.1195 106.565302) (xy 162.1195 106.676) + (xy 162.116949 106.684685) (xy 162.118238 106.693647) (xy 162.107259 106.717687) (xy 162.099815 106.743039) + (xy 162.092974 106.748966) (xy 162.089213 106.757203) (xy 162.066978 106.771492) (xy 162.047011 106.788794) + (xy 162.036496 106.791081) (xy 162.030435 106.794977) (xy 161.9955 106.8) (xy 161.912 106.8) (xy 161.844961 106.780315) + (xy 161.799206 106.727511) (xy 161.788 106.676) (xy 161.788 104.374) (xy 161.807685 104.306961) + (xy 161.860489 104.261206) (xy 161.912 104.25) (xy 162.0155 104.25) ) ) (filled_polygon @@ -22405,28 +22454,28 @@ (xy 168.549181 105.187252) (xy 168.524834 105.198288) (xy 168.507906 105.199474) (xy 168.507819 105.1995) (xy 168.507764 105.199484) (xy 168.507537 105.1995) (xy 166.692463 105.1995) (xy 166.625424 105.179815) (xy 166.579669 105.127011) (xy 166.569725 105.057853) (xy 166.573387 105.040905) (xy 166.597597 104.957573) - (xy 166.597598 104.957567) (xy 166.598891 104.941135) (xy 166.6005 104.920694) (xy 166.6005 104.856213) - (xy 166.620185 104.789174) (xy 166.672989 104.743419) (xy 166.742147 104.733475) (xy 166.748692 104.734596) - (xy 166.826129 104.75) (xy 166.973871 104.75) (xy 166.973873 104.749999) (xy 167.11876 104.721179) - (xy 167.118775 104.721175) (xy 167.224024 104.677578) (xy 167.224024 104.677577) (xy 166.723223 104.176777) - (xy 166.616857 104.070411) (xy 166.583372 104.009088) (xy 166.58292 104.006919) (xy 166.581843 104.001503) - (xy 166.572981 103.980109) (xy 166.8 103.980109) (xy 166.8 104.019891) (xy 166.815224 104.056645) - (xy 166.843355 104.084776) (xy 166.880109 104.1) (xy 166.919891 104.1) (xy 166.956645 104.084776) - (xy 166.984776 104.056645) (xy 167 104.019891) (xy 167 103.999999) (xy 167.253554 103.999999) (xy 167.253554 104.000001) - (xy 167.577929 104.324376) (xy 167.622069 104.324376) (xy 167.946446 104) (xy 167.926555 103.980109) - (xy 168.2 103.980109) (xy 168.2 104.019891) (xy 168.215224 104.056645) (xy 168.243355 104.084776) - (xy 168.280109 104.1) (xy 168.319891 104.1) (xy 168.356645 104.084776) (xy 168.384776 104.056645) - (xy 168.4 104.019891) (xy 168.4 103.980109) (xy 168.384776 103.943355) (xy 168.356645 103.915224) - (xy 168.319891 103.9) (xy 168.280109 103.9) (xy 168.243355 103.915224) (xy 168.215224 103.943355) - (xy 168.2 103.980109) (xy 167.926555 103.980109) (xy 167.622068 103.675622) (xy 167.57793 103.675622) - (xy 167.253554 103.999999) (xy 167 103.999999) (xy 167 103.980109) (xy 166.984776 103.943355) (xy 166.956645 103.915224) - (xy 166.919891 103.9) (xy 166.880109 103.9) (xy 166.843355 103.915224) (xy 166.815224 103.943355) - (xy 166.8 103.980109) (xy 166.572981 103.980109) (xy 166.554159 103.934669) (xy 166.546691 103.865201) - (xy 166.549645 103.852623) (xy 166.597597 103.687573) (xy 166.597598 103.687567) (xy 166.600499 103.650701) - (xy 166.6005 103.650694) (xy 166.6005 103.646308) (xy 166.620185 103.579269) (xy 166.672989 103.533514) - (xy 166.742147 103.52357) (xy 166.805703 103.552595) (xy 166.812181 103.558627) (xy 166.9 103.646446) - (xy 167.146446 103.4) (xy 168.053554 103.4) (xy 168.3 103.646446) (xy 168.546446 103.4) (xy 168.3 103.153554) - (xy 168.053554 103.4) (xy 167.146446 103.4) (xy 166.9 103.153554) (xy 166.807023 103.246531) (xy 166.7457 103.280015) + (xy 166.597598 104.957567) (xy 166.6005 104.920694) (xy 166.6005 104.856213) (xy 166.620185 104.789174) + (xy 166.672989 104.743419) (xy 166.742147 104.733475) (xy 166.748692 104.734596) (xy 166.826129 104.75) + (xy 166.973871 104.75) (xy 166.973873 104.749999) (xy 167.11876 104.721179) (xy 167.118775 104.721175) + (xy 167.224024 104.677578) (xy 167.224024 104.677577) (xy 166.723223 104.176777) (xy 166.616857 104.070411) + (xy 166.583372 104.009088) (xy 166.58292 104.006919) (xy 166.581843 104.001503) (xy 166.572981 103.980109) + (xy 166.8 103.980109) (xy 166.8 104.019891) (xy 166.815224 104.056645) (xy 166.843355 104.084776) + (xy 166.880109 104.1) (xy 166.919891 104.1) (xy 166.956645 104.084776) (xy 166.984776 104.056645) + (xy 167 104.019891) (xy 167 103.999999) (xy 167.253554 103.999999) (xy 167.253554 104.000001) (xy 167.577929 104.324376) + (xy 167.622069 104.324376) (xy 167.946446 104) (xy 167.926555 103.980109) (xy 168.2 103.980109) + (xy 168.2 104.019891) (xy 168.215224 104.056645) (xy 168.243355 104.084776) (xy 168.280109 104.1) + (xy 168.319891 104.1) (xy 168.356645 104.084776) (xy 168.384776 104.056645) (xy 168.4 104.019891) + (xy 168.4 103.980109) (xy 168.384776 103.943355) (xy 168.356645 103.915224) (xy 168.319891 103.9) + (xy 168.280109 103.9) (xy 168.243355 103.915224) (xy 168.215224 103.943355) (xy 168.2 103.980109) + (xy 167.926555 103.980109) (xy 167.622068 103.675622) (xy 167.57793 103.675622) (xy 167.253554 103.999999) + (xy 167 103.999999) (xy 167 103.980109) (xy 166.984776 103.943355) (xy 166.956645 103.915224) (xy 166.919891 103.9) + (xy 166.880109 103.9) (xy 166.843355 103.915224) (xy 166.815224 103.943355) (xy 166.8 103.980109) + (xy 166.572981 103.980109) (xy 166.554159 103.934669) (xy 166.546691 103.865201) (xy 166.549645 103.852623) + (xy 166.597597 103.687573) (xy 166.597598 103.687567) (xy 166.600499 103.650701) (xy 166.6005 103.650694) + (xy 166.6005 103.646308) (xy 166.620185 103.579269) (xy 166.672989 103.533514) (xy 166.742147 103.52357) + (xy 166.805703 103.552595) (xy 166.812181 103.558627) (xy 166.9 103.646446) (xy 167.146446 103.4) + (xy 168.053554 103.4) (xy 168.3 103.646446) (xy 168.546446 103.4) (xy 168.3 103.153554) (xy 168.053554 103.4) + (xy 167.146446 103.4) (xy 166.9 103.153554) (xy 166.807023 103.246531) (xy 166.7457 103.280015) (xy 166.676008 103.275031) (xy 166.620075 103.233159) (xy 166.600448 103.1882) (xy 166.599366 103.188515) (xy 166.551745 103.024606) (xy 166.551744 103.024603) (xy 166.551744 103.024602) (xy 166.515268 102.962925) (xy 166.498086 102.895204) (xy 166.503917 102.861963) (xy 166.530123 102.780188) (xy 166.53008 102.780109) diff --git a/pcb/commeownder.kicad_prl b/pcb/commeownder.kicad_prl index 25f8dd9..1eb4d3d 100644 --- a/pcb/commeownder.kicad_prl +++ b/pcb/commeownder.kicad_prl @@ -1,7 +1,7 @@ { "board": { "active_layer": 2, - "active_layer_preset": "Back Layers", + "active_layer_preset": "All Layers", "auto_track_width": true, "hidden_netclasses": [], "hidden_nets": [], @@ -53,7 +53,7 @@ "board_outline_area", "ly_points" ], - "visible_layers": "00000000_00000000_00000002_2200888c", + "visible_layers": "ffffffff_ffffffff_ffffffff_ffffffff", "zone_display_mode": 0 }, "git": { diff --git a/pcb/commeownder.kicad_sch b/pcb/commeownder.kicad_sch index 5695c0b..7985770 100644 --- a/pcb/commeownder.kicad_sch +++ b/pcb/commeownder.kicad_sch @@ -19066,7 +19066,7 @@ (justify right) ) ) - (property "Footprint" "Inductor_SMD:L_Taiyo-Yuden_MD-3030" + (property "Footprint" "Inductor_SMD:L_Taiyo-Yuden_MD-4040" (at 68.58 -21.59 0) (hide yes) (show_name no) diff --git a/sdkconfig.defaults b/sdkconfig.defaults index b01f745..29d7bfb 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -1,3 +1,6 @@ CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y CONFIG_BT_CONTROLLER_ENABLED=y +CONFIG_PM_ENABLE=y +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3