Compare commits
5 Commits
690d567ec7
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 051c0c13c9 | |||
| 21da24695a | |||
| e9933da1a4 | |||
| 89ded8b119 | |||
| f3d5e4018b |
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -5,7 +5,7 @@
|
|||||||
"idf.openOcdConfigs": [
|
"idf.openOcdConfigs": [
|
||||||
"board/esp32s3-builtin.cfg"
|
"board/esp32s3-builtin.cfg"
|
||||||
],
|
],
|
||||||
"idf.port": "/dev/tty.BLTH",
|
"idf.port": "/dev/tty.usbmodem14301",
|
||||||
"idf.toolsPath": "/Users/tarassivas/.espressif",
|
"idf.toolsPath": "/Users/tarassivas/.espressif",
|
||||||
"idf.customExtraVars": {
|
"idf.customExtraVars": {
|
||||||
"IDF_TARGET": "esp32s3"
|
"IDF_TARGET": "esp32s3"
|
||||||
@@ -15,5 +15,6 @@
|
|||||||
"--background-index",
|
"--background-index",
|
||||||
"--query-driver=/Users/tarassivas/.espressif/tools/xtensa-esp-elf/esp-14.2.0_20241119/xtensa-esp-elf/bin/xtensa-esp32-elf-gcc",
|
"--query-driver=/Users/tarassivas/.espressif/tools/xtensa-esp-elf/esp-14.2.0_20241119/xtensa-esp-elf/bin/xtensa-esp32-elf-gcc",
|
||||||
"--compile-commands-dir=${workspaceFolder}/build"
|
"--compile-commands-dir=${workspaceFolder}/build"
|
||||||
]
|
],
|
||||||
|
"idf.flashType": "UART"
|
||||||
}
|
}
|
||||||
|
|||||||
11
.vscode/sftp.json
vendored
Normal file
11
.vscode/sftp.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "My Server",
|
||||||
|
"host": "localhost",
|
||||||
|
"protocol": "sftp",
|
||||||
|
"port": 22,
|
||||||
|
"username": "username",
|
||||||
|
"remotePath": "/",
|
||||||
|
"uploadOnSave": false,
|
||||||
|
"useTempFile": false,
|
||||||
|
"openSsh": false
|
||||||
|
}
|
||||||
39
README.md
39
README.md
@@ -4,8 +4,8 @@ watch-watch — вбудована система на ESP32-S3 для нагл
|
|||||||
|
|
||||||
## Основні можливості
|
## Основні можливості
|
||||||
- **Керування каналами живлення**: 5 незалежних ліній `EN` (GPIO 2, 4, 5, 18, 19), які можна увімкнути, вимкнути або перемкнути з коду чи CLI.
|
- **Керування каналами живлення**: 5 незалежних ліній `EN` (GPIO 2, 4, 5, 18, 19), які можна увімкнути, вимкнути або перемкнути з коду чи CLI.
|
||||||
- **Послідовний автотест**: у `app_main` реалізовано базову логіку — канали вмикаються по черзі з інтервалом 4 с, що дозволяє перевірити всі DC/DC.
|
- **Послідовний автотест**: у `app_main` реалізовано базову логіку — канали вмикаються по черзі з інтервалом 3 с, що дозволяє перевірити всі DC/DC без стрибків споживання.
|
||||||
- **Світлодіодний індикатор стану**: п’ять WS2812 (GPIO 8) показують роботу каналів — активний канал підсвічується яскраво-зеленим, увімкнені/вимкнені відображаються зеленим/синім, помилки — червоним.
|
- **Світлодіодний індикатор стану**: п’ять WS2812 (GPIO 8) світяться зеленим під час стартової затримки, після чого кожен канал сигналізує лише про дві події — відсутність VPN (два червоних блимання) та падіння APP (три жовтих блимання).
|
||||||
- **Моніторинг навантаження**: датчики INA226 вимірюють напругу, струм та потужність кожного каналу, інформація потрапляє в лог і CLI.
|
- **Моніторинг навантаження**: датчики INA226 вимірюють напругу, струм та потужність кожного каналу, інформація потрапляє в лог і CLI.
|
||||||
- **UART взаємодія з Raspberry Pi**: один UART через мультиплексор (A0/A1/A2) ділиться між п’ятьма Pi, дозволяючи надсилати службові повідомлення або обмінюватися даними.
|
- **UART взаємодія з Raspberry Pi**: один UART через мультиплексор (A0/A1/A2) ділиться між п’ятьма Pi, дозволяючи надсилати службові повідомлення або обмінюватися даними.
|
||||||
- **Нативний USB-CLI**: ESP32-S3 підключається до Raspberry Pi 5 по USB і стає CDC ACM пристроєм; командний інтерфейс дозволяє керувати каналами та дивитись стан у реальному часі.
|
- **Нативний USB-CLI**: ESP32-S3 підключається до Raspberry Pi 5 по USB і стає CDC ACM пристроєм; командний інтерфейс дозволяє керувати каналами та дивитись стан у реальному часі.
|
||||||
@@ -47,9 +47,12 @@ watch-watch — вбудована система на ESP32-S3 для нагл
|
|||||||
|
|
||||||
## Світлодіоди стану
|
## Світлодіоди стану
|
||||||
- IC WS2812 підключений до GPIO 8 (один ланцюг із 5 діодів).
|
- IC WS2812 підключений до GPIO 8 (один ланцюг із 5 діодів).
|
||||||
- Модуль `ws2812_status` синхронізує стан з DC/DC: увімкнені канали світяться зеленим, вимкнені — синім, активний у поточному циклі — яскраво-зеленим крапкою.
|
- Після старту всі п’ять діодів світяться сталим зеленим протягом затримки `heartbeat_start_delay_sec`, щоб показати фазу ініціалізації.
|
||||||
- За критичної помилки (наприклад, DCDC не ініціалізувався) всі індикатори стають червоними.
|
- Після завершення затримки вся стрічка гасне, а кожен канал індикує лише два типи подій:
|
||||||
- Колірні алгоритми можна кастомізувати у `main/ws2812_status.c`.
|
- VPN=0 — два червоних блимання по 200 мс із паузою між циклами;
|
||||||
|
- APP=0 — три жовтих блимання по 200 мс.
|
||||||
|
Якщо активні обидва попередження, вони програються послідовно (спочатку VPN, потім APP) із паузою 2 с між послідовностями.
|
||||||
|
- При критичній помилці (наприклад, DCDC не ініціалізувався) всі індикатори залишаються червоними.
|
||||||
- GPIO, кількість діодів та тактову частоту RMT можна змінити через `idf.py menuconfig` (розділ *Налаштування watch-watch*).
|
- GPIO, кількість діодів та тактову частоту RMT можна змінити через `idf.py menuconfig` (розділ *Налаштування watch-watch*).
|
||||||
|
|
||||||
## Моніторинг живлення (INA226)
|
## Моніторинг живлення (INA226)
|
||||||
@@ -61,14 +64,14 @@ watch-watch — вбудована система на ESP32-S3 для нагл
|
|||||||
## UART взаємодія та heartbeat
|
## UART взаємодія та heartbeat
|
||||||
- Загальний UART (типово UART1, GPIO17/16) підключений до аналогового мультиплексора, лінії адреси `A0/A1/A2` (GPIO 9/10/11) вибирають одну з Raspberry Pi.
|
- Загальний UART (типово UART1, GPIO17/16) підключений до аналогового мультиплексора, лінії адреси `A0/A1/A2` (GPIO 9/10/11) вибирають одну з Raspberry Pi.
|
||||||
- Модуль `uart_mux` серіалізує доступ до UART, надає API для `uart_mux_write/read` і періодично опитує UART на наявність heartbeat.
|
- Модуль `uart_mux` серіалізує доступ до UART, надає API для `uart_mux_write/read` і періодично опитує UART на наявність heartbeat.
|
||||||
- Після кожного вимірювання INA226 ESP32-S3 відправляє поточну телеметрію (`PWR <V> <I>`) до активної Raspberry Pi.
|
- Після кожного вимірювання INA226 ESP32-S3 відправляє поточну телеметрію (`PWR <V>V <I>A`) до активної Raspberry Pi.
|
||||||
- Якщо heartbeat від Pi не надходить протягом `CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC` (за замовчуванням 60 с), відповідний канал живлення вимикається й знову вмикається для примусового перезапуску.
|
- Якщо heartbeat від Pi не надходить протягом `CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC` (за замовчуванням 60 с), відповідний канал живлення вимикається й знову вмикається для примусового перезапуску.
|
||||||
- Команди CLI `uart send` / `uart read` дозволяють вручну надсилати/читати повідомлення, а в `app_main` можна реалізувати власні протоколи синхронізації.
|
- Команди CLI `uart send` / `uart read` дозволяють вручну надсилати/читати повідомлення, а в `app_main` можна реалізувати власні протоколи синхронізації.
|
||||||
|
|
||||||
## UART взаємодія з Raspberry Pi
|
## UART взаємодія з Raspberry Pi
|
||||||
- Шина UART (типово UART1, TX=GPIO17, RX=GPIO16) підключена до аналогового мультиплексора з адресними лініями A0/A1/A2 (GPIO 9/10/11), що дозволяє вибирати одну з 5 Raspberry Pi.
|
- Шина UART (типово UART1, TX=GPIO17, RX=GPIO16) підключена до аналогового мультиплексора з адресними лініями A0/A1/A2 (GPIO 9/10/11), що дозволяє вибирати одну з 5 Raspberry Pi.
|
||||||
- Модуль `uart_mux` гарантує серійний доступ: перед операцією він виставляє двійковий код каналу на A0-A2 та блокує UART м’ютексом.
|
- Модуль `uart_mux` гарантує серійний доступ: перед операцією він виставляє двійковий код каналу на A0-A2 та блокує UART м’ютексом.
|
||||||
- У `app_main` після вимірювань кожному Pi відправляється телеметрія (`CHx <V>V <I>mA`), а через CLI можна виконати `uart send <n> <msg>` або `uart read <n> [len]`.
|
- У `app_main` після вимірювань кожному Pi відправляється телеметрія (`CHx <V>V <I>A`), а через CLI можна виконати `uart send <n> <msg>` або `uart read <n> [len]`.
|
||||||
- Усі параметри (порт, швидкість, GPIO) доступні в `menuconfig → UART мультиплексор`.
|
- Усі параметри (порт, швидкість, GPIO) доступні в `menuconfig → UART мультиплексор`.
|
||||||
|
|
||||||
## USB CDC CLI
|
## USB CDC CLI
|
||||||
@@ -84,11 +87,29 @@ watch-watch — вбудована система на ESP32-S3 для нагл
|
|||||||
| `sense` | виміряти загальну напругу/струм/потужність |
|
| `sense` | виміряти загальну напругу/струм/потужність |
|
||||||
| `uart send <n> <msg>` | надіслати повідомлення у Raspberry Pi `n` |
|
| `uart send <n> <msg>` | надіслати повідомлення у Raspberry Pi `n` |
|
||||||
| `uart read <n> [len]` | прочитати відповідь від Raspberry Pi `n` |
|
| `uart read <n> [len]` | прочитати відповідь від Raspberry Pi `n` |
|
||||||
| `uart send <n> <msg>` | відправити текст у Raspberry Pi №n |
|
| `config show` | переглянути таймінги heartbeat/DCDC |
|
||||||
| `uart read <n> [len]` | прочитати дані з цільового Pi |
|
| `config set …` | змінити та зберегти таймінги / моніторинг |
|
||||||
|
| `reset` | м’яке перезавантаження ESP32-S3 |
|
||||||
|
| `bootloader` | перезавантажити ESP32-S3 у ROM bootloader для `esptool.py` |
|
||||||
|
|
||||||
CLI з’являється після того, як Raspberry Pi встановить DTR (наприклад, через `screen`, `picocom` або власний скрипт).
|
CLI з’являється після того, як Raspberry Pi встановить DTR (наприклад, через `screen`, `picocom` або власний скрипт).
|
||||||
|
|
||||||
|
### Керування таймінгами heartbeat
|
||||||
|
Команда `config` дозволяє зберігати параметри роботи watchdog та циклу heartbeat у NVS, і вони одразу набувають чинності без перезбирання прошивки:
|
||||||
|
|
||||||
|
1. `hb_period` — інтервал (сек) між опитуваннями/heartbeat у головному циклі.
|
||||||
|
2. `dcdc_off` — тривалість вимкнення каналу DCDC при автоматичному перезапуску (сек).
|
||||||
|
3. `hb_start` — затримка перед стартом опитування після завантаження (сек); застосовується як у головному циклі, так і у watchdog-завданні.
|
||||||
|
4. `hb_monitor` — значення `1` вмикає моніторинг heartbeat (за замовчуванням), `0` вимикає лише контроль/рестарти каналів, але тестові повідомлення heartbeat продовжують відправлятися.
|
||||||
|
5. `hb_miss` — кількість послідовних запитів без відповіді перед автоматичним перезапуском каналу (за замовчуванням 3). Значення зберігає watchdog та використовується в CLI для відображення статистики пропусків/рестартів.
|
||||||
|
|
||||||
|
### Прошивка без натискання BOOT
|
||||||
|
1. Під’єднайтесь до CLI та виконайте команду `bootloader`. ESP32-S3 перезавантажиться у ROM bootloader, а на хості з’явиться USB-пристрій `USB JTAG/serial`.
|
||||||
|
2. На Raspberry Pi запустіть `esptool.py` або `idf.py flash` і вкажіть новий порт (`/dev/ttyACM*` або `/dev/cu.usbmodem*`). Наприклад:
|
||||||
|
`esptool.py --chip esp32s3 --port /dev/ttyACM0 --before usb_reset --after no_reset write_flash 0x0 build/watch-watch.bin`
|
||||||
|
3. Після завершення прошивки виконайте `esptool.py --after hard_reset reset` або просто перезавантажте живлення — пристрій вийде з bootloader і повернеться до нормальної роботи.
|
||||||
|
Таким чином процедура оновлення доступна без натискання кнопки BOOT і може виконуватись безпосередньо з підключеної Raspberry Pi.
|
||||||
|
|
||||||
## Збірка та конфігурація
|
## Збірка та конфігурація
|
||||||
1. Встановіть ESP-IDF v5.5.1 (шлях `IDF_PATH` має вказувати на `/Users/tarassivas/esp/v5.5.1/esp-idf`).
|
1. Встановіть ESP-IDF v5.5.1 (шлях `IDF_PATH` має вказувати на `/Users/tarassivas/esp/v5.5.1/esp-idf`).
|
||||||
2. Один раз виконайте `idf.py reconfigure`, щоб підвантажити залежності з `idf_component.yml`.
|
2. Один раз виконайте `idf.py reconfigure`, щоб підвантажити залежності з `idf_component.yml`.
|
||||||
|
|||||||
24
kicad/.gitignore
vendored
Normal file
24
kicad/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Temporary and local KiCad artifacts
|
||||||
|
*-backups/
|
||||||
|
*.kicad_prl
|
||||||
|
*.kicad_pro-bak
|
||||||
|
*.kicad_sch-bak
|
||||||
|
*.kicad_pcb-bak
|
||||||
|
*.sch-bak
|
||||||
|
*.pcb-bak
|
||||||
|
*.bak
|
||||||
|
|
||||||
|
# Cache and automatically generated tables
|
||||||
|
sym-lib-table-bak
|
||||||
|
fp-lib-table-bak
|
||||||
|
*-cache.lib
|
||||||
|
*-cache.dcm
|
||||||
|
|
||||||
|
# Generated fabrication outputs (regenerate as needed)
|
||||||
|
*.net
|
||||||
|
*.xml
|
||||||
|
*.csv
|
||||||
|
*.tsv
|
||||||
|
*.pos
|
||||||
|
*.ipr
|
||||||
|
*.rpt
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"ARCHIVE_NAME": "", "EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false, "OPEN BROWSER": true, "NO_BACKUP_OPT": false}
|
||||||
105078
kicad/watch-watchkicad_pro/fp-info-cache
Normal file
105078
kicad/watch-watchkicad_pro/fp-info-cache
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
506
kicad/watch-watchkicad_pro/production/netlist.ipc
Normal file
506
kicad/watch-watchkicad_pro/production/netlist.ipc
Normal file
@@ -0,0 +1,506 @@
|
|||||||
|
P CODE 00
|
||||||
|
P UNITS CUST 0
|
||||||
|
P arrayDim N
|
||||||
|
317GND VIA MD0118PA00X+052000Y-017250X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+051500Y-017250X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+066063Y-030875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+066063Y-030875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+068875Y-025750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+070125Y-026750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+071813Y-029063X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+069188Y-028750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+072875Y-030125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+071625Y-030125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+048938Y-028500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+048938Y-026000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+048938Y-018500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+048875Y-021000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+048938Y-023500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+053563Y-020313X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+054188Y-019813X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055000Y-020625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055000Y-020125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+057438Y-020063X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+058125Y-019813X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063063Y-016313X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063063Y-017563X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063063Y-018688X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063063Y-019625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063063Y-020563X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+064313Y-021000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+064875Y-021688X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+066188Y-025188X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065750Y-025188X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065938Y-027625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+066625Y-027625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+066625Y-026813X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065875Y-026813X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063938Y-026875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+060688Y-026813X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+062875Y-028063X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+061000Y-028000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+059938Y-029000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+057813Y-028750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+056625Y-028750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+054188Y-028750X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+052625Y-017249X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+053313Y-018188X0236Y0000R000S1526506179
|
||||||
|
317ET-(J14-PIN_2) VIA MD0118PA00X+055750Y-019813X0236Y0000R000S1526506179
|
||||||
|
317ET-(J14-PIN_2) VIA MD0118PA00X+064188Y-024625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+068500Y-016125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+073813Y-016063X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+073313Y-017688X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+068563Y-017563X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+072438Y-019438X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+071813Y-019438X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+070375Y-019313X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+070125Y-019750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+072813Y-024188X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+071000Y-026750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065688Y-030000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065625Y-030875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065000Y-030875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063875Y-030875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063250Y-030813X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+057750Y-029750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+060813Y-029688X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+058188Y-029688X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+060250Y-029688X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+058813Y-029313X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+059813Y-029688X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+058813Y-029688X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+052750Y-024750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+052875Y-027500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+054125Y-027813X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055125Y-027813X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+058500Y-020875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+058813Y-017881X0236Y0000R000S1526506179
|
||||||
|
317+5V VIA MD0118PA00X+060063Y-019000X0236Y0000R000S1526506179
|
||||||
|
317+5V VIA MD0118PA00X+058188Y-019000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055500Y-025625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+057000Y-025375X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+057750Y-025250X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+059509Y-026962X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+057750Y-025750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+056563Y-020250X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+057250Y-030500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+057250Y-029750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+047250Y-030000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+047250Y-030625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+054000Y-029875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+054000Y-030500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+062000Y-017500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+060000Y-017125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+058375Y-017125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+056250Y-017125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+054875Y-018500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+054188Y-019375X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055750Y-018938X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+056250Y-018500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+056125Y-020250X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055438Y-020563X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+060750Y-021250X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+061125Y-025750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+062000Y-026875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+067875Y-026750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+066625Y-025188X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+066250Y-026813X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+066250Y-027625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+072875Y-025875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+070125Y-025625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+069000Y-025250X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+068250Y-025875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+071000Y-024250X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+070000Y-024250X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+067250Y-021875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+071000Y-020750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065875Y-016250X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065125Y-016250X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065875Y-018500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065250Y-018500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+062063Y-018688X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+062000Y-016313X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+060000Y-016125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+058250Y-016125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+047000Y-016875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+047000Y-016125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+052000Y-018000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+052000Y-020500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+052000Y-023000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+052000Y-025500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+052000Y-028000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+062500Y-030875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+062500Y-030375X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+062500Y-030000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+061250Y-030750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+061250Y-030375X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+061250Y-030000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+064750Y-029125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+064750Y-028750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+064750Y-028375X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063875Y-029125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063875Y-028750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063875Y-028375X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063375Y-029125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063375Y-028750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+063375Y-028375X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065250Y-029125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065250Y-028750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+065250Y-028375X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+067375Y-029375X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+070375Y-030625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+069500Y-030625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+076000Y-030625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+075375Y-030625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+075125Y-027750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+076125Y-026375X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+075500Y-026375X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+072875Y-025000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+072875Y-023500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+075375Y-022500X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+074875Y-020125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+070000Y-022125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+069000Y-022125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+068000Y-020875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+069000Y-028125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+068500Y-028125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+068000Y-028125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+066938Y-024563X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+064875Y-022625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+066750Y-022625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+062063Y-019625X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+060125Y-020125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+058063Y-020750X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+061875Y-025875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055375Y-026250X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+057625Y-027000X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+053375Y-025875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+053750Y-023125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+054000Y-020875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055000Y-021250X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055000Y-022125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055000Y-022875X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055000Y-023438X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055000Y-024125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+055000Y-025125X0236Y0000R000S1526506179
|
||||||
|
317GND VIA MD0118PA00X+062750Y-022625X0236Y0000R000S1526506179
|
||||||
|
317NET-(U3-GPIO0) VIA MD0118PA00X+069375Y-019250X0236Y0000R000S1526506179
|
||||||
|
317NET-(U3-GPIO0) VIA MD0118PA00X+060750Y-027250X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+062375Y-025750X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+064750Y-023250X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+064750Y-024875X0236Y0000R000S1526506179
|
||||||
|
317+5V VIA MD0118PA00X+060625Y-028500X0236Y0000R000S1526506179
|
||||||
|
317RX VIA MD0118PA00X+068500Y-026375X0236Y0000R000S1526506179
|
||||||
|
317TX VIA MD0118PA00X+068750Y-020625X0236Y0000R000S1526506179
|
||||||
|
317A0 VIA MD0118PA00X+069000Y-022875X0236Y0000R000S1526506179
|
||||||
|
317A1 VIA MD0118PA00X+068500Y-022875X0236Y0000R000S1526506179
|
||||||
|
317A2 VIA MD0118PA00X+068000Y-022875X0236Y0000R000S1526506179
|
||||||
|
317POWER_RAIL_IN VIA MD0118PA00X+067375Y-020250X0236Y0000R000S1526506179
|
||||||
|
317POWER_RAIL_IN VIA MD0118PA00X+065250Y-022000X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+062125Y-022000X0236Y0000R000S1526506179
|
||||||
|
317EN0 VIA MD0118PA00X+059000Y-027375X0236Y0000R000S1526506179
|
||||||
|
317EN1 VIA MD0118PA00X+058494Y-027625X0236Y0000R000S1526506179
|
||||||
|
317EN2 VIA MD0118PA00X+059500Y-027375X0236Y0000R000S1526506179
|
||||||
|
317EN3 VIA MD0118PA00X+059921Y-027375X0236Y0000R000S1526506179
|
||||||
|
317EN4 VIA MD0118PA00X+060250Y-027750X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+057000Y-022250X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+057539Y-024173X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+069625Y-022500X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+072000Y-020250X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+072750Y-029250X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+072750Y-027500X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+072000Y-026000X0236Y0000R000S1526506179
|
||||||
|
317+3.3V VIA MD0118PA00X+072000Y-024750X0236Y0000R000S1526506179
|
||||||
|
317EN0 VIA MD0118PA00X+058986Y-025688X0236Y0000R000S1526506179
|
||||||
|
317RX VIA MD0118PA00X+056929Y-023051X0236Y0000R000S1526506179
|
||||||
|
317TX VIA MD0118PA00X+057323Y-023051X0236Y0000R000S1526506179
|
||||||
|
317EN3 VIA MD0118PA00X+059921Y-025669X0236Y0000R000S1526506179
|
||||||
|
317EN2 VIA MD0118PA00X+059606Y-025669X0236Y0000R000S1526506179
|
||||||
|
317EN1 VIA MD0118PA00X+059155Y-025296X0236Y0000R000S1526506179
|
||||||
|
317A2 VIA MD0118PA00X+062205Y-023858X0236Y0000R000S1526506179
|
||||||
|
317A1 VIA MD0118PA00X+061811Y-023858X0236Y0000R000S1526506179
|
||||||
|
317A0 VIA MD0118PA00X+061417Y-023858X0236Y0000R000S1526506179
|
||||||
|
317EN4 VIA MD0118PA00X+060236Y-025669X0236Y0000R000S1526506179
|
||||||
|
317TX3 VIA MD0118PA00X+069750Y-028474X0236Y0000R000S1526506179
|
||||||
|
317TX0 VIA MD0118PA00X+070500Y-028474X0236Y0000R000S1526506179
|
||||||
|
317TX1 VIA MD0118PA00X+071250Y-028724X0236Y0000R000S1526506179
|
||||||
|
317TX2 VIA MD0118PA00X+071313Y-030438X0236Y0000R000S1526506179
|
||||||
|
317RX4 VIA MD0118PA00X+072250Y-021474X0236Y0000R000S1526506179
|
||||||
|
317RX4 VIA MD0118PA00X+074119Y-022474X0236Y0000R000S1526506179
|
||||||
|
327EN0.0 U9 -4 A01X+048905Y-019000X0719Y0256R180S2
|
||||||
|
327EN0.1 U9 -3 A01X+048905Y-018000X0719Y0256R180S2
|
||||||
|
327GND U9 -2 A01X+052595Y-018000X0719Y0256R180S2
|
||||||
|
327NET-(R1-PAD1) U9 -1 A01X+052595Y-019000X0719Y0256R180S2
|
||||||
|
317GND J14 -1 D0394PA00X+049813Y-016563X0669Y0669R270S0
|
||||||
|
317ET-(J14-PIN_2) J14 -2 D0394PA00X+050813Y-016563X0669Y0000R270S0
|
||||||
|
317+3.3V J14 -3 D0394PA00X+051813Y-016563X0669Y0000R270S0
|
||||||
|
327+3.3V C1 -1 A01X+053063Y-017249X0394Y0571R270S2
|
||||||
|
327GND C1 -2 A01X+053063Y-016501X0394Y0571R270S2
|
||||||
|
327GND J1 -S1 A01X+057122Y-016805X0858Y0787R180S2
|
||||||
|
327GND J1 -S1 A01X+057122Y-018352X0858Y0787R180S2
|
||||||
|
327GND J1 -S1 A01X+061146Y-016805X0858Y0787R180S2
|
||||||
|
327GND J1 -S1 A01X+061146Y-018352X0858Y0787R180S2
|
||||||
|
327GND J1 -B12 A01X+060394Y-018579X0236Y0453R180S2
|
||||||
|
327+5V J1 -B9 A01X+060079Y-018579X0236Y0453R180S2
|
||||||
|
327J1-SBU2-PADB8) J1 -B8 A01X+059823Y-018579X0118Y0453R180S2
|
||||||
|
327-(J1-D--PADA7) J1 -B7 A01X+059429Y-018579X0118Y0453R180S2
|
||||||
|
327-(J1-D+-PADA6) J1 -B6 A01X+058839Y-018579X0118Y0453R180S2
|
||||||
|
327NET-(J1-CC2) J1 -B5 A01X+058445Y-018579X0118Y0453R180S2
|
||||||
|
327+5V J1 -B4 A01X+058189Y-018579X0236Y0453R180S2
|
||||||
|
327GND J1 -B1 A01X+057874Y-018579X0236Y0453R180S2
|
||||||
|
327GND J1 -A12 A01X+057874Y-018579X0236Y0453R180S2
|
||||||
|
327+5V J1 -A9 A01X+058189Y-018579X0236Y0453R180S2
|
||||||
|
327J1-SBU1-PADA8) J1 -A8 A01X+058642Y-018579X0118Y0453R180S2
|
||||||
|
327-(J1-D--PADA7) J1 -A7 A01X+059035Y-018579X0118Y0453R180S2
|
||||||
|
327-(J1-D+-PADA6) J1 -A6 A01X+059232Y-018579X0118Y0453R180S2
|
||||||
|
327NET-(J1-CC1) J1 -A5 A01X+059626Y-018579X0118Y0453R180S2
|
||||||
|
327+5V J1 -A4 A01X+060079Y-018579X0236Y0453R180S2
|
||||||
|
327GND J1 -A1 A01X+060394Y-018579X0236Y0453R180S2
|
||||||
|
367N/C J1 D0256UA00X+057996Y-018156X0256Y0000R180S0
|
||||||
|
367N/C J1 D0256UA00X+060272Y-018156X0256Y0000R180S0
|
||||||
|
327TX TP4 -1 A01X+069500Y-020250X0787Y0000R000S2
|
||||||
|
317GND U3 -57 D0079PA00X+059823Y-022047X0197Y0000R270S3
|
||||||
|
317GND U3 -57 D0079PA00X+059134Y-022047X0197Y0000R270S3
|
||||||
|
317GND U3 -57 D0079PA00X+058445Y-022047X0197Y0000R270S3
|
||||||
|
317GND U3 -57 D0079PA00X+059823Y-022736X0197Y0000R270S3
|
||||||
|
327GND U3 -57 A02X+059134Y-022736X1575Y1575R270S3
|
||||||
|
327GND U3 -57 A01X+059134Y-022736X1575Y1575R270S2
|
||||||
|
317GND U3 -57 D0079PA00X+059134Y-022736X0197Y0000R270S3
|
||||||
|
317GND U3 -57 D0079PA00X+058445Y-022736X0197Y0000R270S3
|
||||||
|
317GND U3 -57 D0079PA00X+059823Y-023425X0197Y0000R270S3
|
||||||
|
317GND U3 -57 D0079PA00X+059134Y-023425X0197Y0000R270S3
|
||||||
|
317GND U3 -57 D0079PA00X+058445Y-023425X0197Y0000R270S3
|
||||||
|
327+3.3V U3 -56 A01X+057776Y-023760X0079Y0315R270S2
|
||||||
|
327+3.3V U3 -55 A01X+057776Y-023602X0079Y0315R270S2
|
||||||
|
327ET-(U3-XTAL_P) U3 -54 A01X+057776Y-023445X0079Y0315R270S2
|
||||||
|
327ET-(U3-XTAL_N) U3 -53 A01X+057776Y-023287X0079Y0315R270S2
|
||||||
|
327-GPIO46-PAD52) U3 -52 A01X+057776Y-023130X0079Y0315R270S2
|
||||||
|
327-GPIO45-PAD51) U3 -51 A01X+057776Y-022972X0079Y0315R270S2
|
||||||
|
327TX U3 -50 A01X+057776Y-022815X0079Y0315R270S2
|
||||||
|
327RX U3 -49 A01X+057776Y-022657X0079Y0315R270S2
|
||||||
|
327U3-MTMS-PAD48) U3 -48 A01X+057776Y-022500X0079Y0315R270S2
|
||||||
|
327U3-MTDI-PAD47) U3 -47 A01X+057776Y-022343X0079Y0315R270S2
|
||||||
|
327+3.3V U3 -46 A01X+057776Y-022185X0079Y0315R270S2
|
||||||
|
327U3-MTDO-PAD45) U3 -45 A01X+057776Y-022028X0079Y0315R270S2
|
||||||
|
327U3-MTCK-PAD44) U3 -44 A01X+057776Y-021870X0079Y0315R270S2
|
||||||
|
327-GPIO38-PAD43) U3 -43 A01X+057776Y-021713X0079Y0315R270S2
|
||||||
|
327-GPIO37-PAD42) U3 -42 A01X+058110Y-021378X0315Y0079R270S2
|
||||||
|
327-GPIO36-PAD41) U3 -41 A01X+058268Y-021378X0315Y0079R270S2
|
||||||
|
327-GPIO35-PAD40) U3 -40 A01X+058425Y-021378X0315Y0079R270S2
|
||||||
|
327-GPIO34-PAD39) U3 -39 A01X+058583Y-021378X0315Y0079R270S2
|
||||||
|
327-GPIO33-PAD38) U3 -38 A01X+058740Y-021378X0315Y0079R270S2
|
||||||
|
327PICLK_P-PAD37) U3 -37 A01X+058898Y-021378X0315Y0079R270S2
|
||||||
|
327PICLK_N-PAD36) U3 -36 A01X+059055Y-021378X0315Y0079R270S2
|
||||||
|
327U3-SPID-PAD35) U3 -35 A01X+059213Y-021378X0315Y0079R270S2
|
||||||
|
327U3-SPIQ-PAD34) U3 -34 A01X+059370Y-021378X0315Y0079R270S2
|
||||||
|
327-SPICLK-PAD33) U3 -33 A01X+059528Y-021378X0315Y0079R270S2
|
||||||
|
327-SPICS0-PAD32) U3 -32 A01X+059685Y-021378X0315Y0079R270S2
|
||||||
|
3273-SPIWP-PAD31) U3 -31 A01X+059843Y-021378X0315Y0079R270S2
|
||||||
|
3273-SPIHD-PAD30) U3 -30 A01X+060000Y-021378X0315Y0079R270S2
|
||||||
|
327VDD_SPI-PAD29) U3 -29 A01X+060157Y-021378X0315Y0079R270S2
|
||||||
|
327-SPICS1-PAD28) U3 -28 A01X+060492Y-021713X0079Y0315R270S2
|
||||||
|
327-GPIO21-PAD27) U3 -27 A01X+060492Y-021870X0079Y0315R270S2
|
||||||
|
327DP U3 -26 A01X+060492Y-022028X0079Y0315R270S2
|
||||||
|
327DM U3 -25 A01X+060492Y-022185X0079Y0315R270S2
|
||||||
|
327ET-(U3-GPIO18) U3 -24 A01X+060492Y-022343X0079Y0315R270S2
|
||||||
|
327ET-(U3-GPIO17) U3 -23 A01X+060492Y-022500X0079Y0315R270S2
|
||||||
|
327L_32K_N-PAD22) U3 -22 A01X+060492Y-022657X0079Y0315R270S2
|
||||||
|
327L_32K_P-PAD21) U3 -21 A01X+060492Y-022815X0079Y0315R270S2
|
||||||
|
327+3.3V U3 -20 A01X+060492Y-022972X0079Y0315R270S2
|
||||||
|
327ET-(U3-GPIO14) U3 -19 A01X+060492Y-023130X0079Y0315R270S2
|
||||||
|
327A2 U3 -18 A01X+060492Y-023287X0079Y0315R270S2
|
||||||
|
327A1 U3 -17 A01X+060492Y-023445X0079Y0315R270S2
|
||||||
|
327A0 U3 -16 A01X+060492Y-023602X0079Y0315R270S2
|
||||||
|
327SCL U3 -15 A01X+060492Y-023760X0079Y0315R270S2
|
||||||
|
327SDA U3 -14 A01X+060157Y-024094X0315Y0079R270S2
|
||||||
|
327RGB U3 -13 A01X+060000Y-024094X0315Y0079R270S2
|
||||||
|
327NET-(U3-GPIO7) U3 -12 A01X+059843Y-024094X0315Y0079R270S2
|
||||||
|
327NET-(U3-GPIO6) U3 -11 A01X+059685Y-024094X0315Y0079R270S2
|
||||||
|
327EN4 U3 -10 A01X+059528Y-024094X0315Y0079R270S2
|
||||||
|
327EN3 U3 -9 A01X+059370Y-024094X0315Y0079R270S2
|
||||||
|
327EN2 U3 -8 A01X+059213Y-024094X0315Y0079R270S2
|
||||||
|
327EN1 U3 -7 A01X+059055Y-024094X0315Y0079R270S2
|
||||||
|
327EN0 U3 -6 A01X+058898Y-024094X0315Y0079R270S2
|
||||||
|
327NET-(U3-GPIO0) U3 -5 A01X+058740Y-024094X0315Y0079R270S2
|
||||||
|
327T-(U3-CHIP_PU) U3 -4 A01X+058583Y-024094X0315Y0079R270S2
|
||||||
|
327+3.3V U3 -3 A01X+058425Y-024094X0315Y0079R270S2
|
||||||
|
327+3.3V U3 -2 A01X+058268Y-024094X0315Y0079R270S2
|
||||||
|
327ET-(U3-LNA_IN) U3 -1 A01X+058110Y-024094X0315Y0079R270S2
|
||||||
|
327ET-(U3-LNA_IN) TP1 -1 A01X+056375Y-026500X0787Y0000R000S2
|
||||||
|
327GND C15 -2 A01X+057087Y-021122X0394Y0571R270S2
|
||||||
|
327+3.3V C15 -1 A01X+057087Y-021870X0394Y0571R270S2
|
||||||
|
327ET-(U3-XTAL_N) Y1 -1 A01X+056250Y-022874X0453Y0394R180S2
|
||||||
|
327GND Y1 -2 A01X+055561Y-022874X0453Y0394R180S2
|
||||||
|
327GND Y1 -3 A01X+055561Y-023425X0453Y0394R180S2
|
||||||
|
327ET-(U3-XTAL_P) Y1 -4 A01X+056250Y-023425X0453Y0394R180S2
|
||||||
|
327GND C10 -2 A01X+055531Y-024173X0394Y0571R180S2
|
||||||
|
327ET-(U3-XTAL_P) C10 -1 A01X+056280Y-024173X0394Y0571R180S2
|
||||||
|
327GND C11 -2 A01X+055531Y-022126X0394Y0571R180S2
|
||||||
|
327ET-(U3-XTAL_N) C11 -1 A01X+056280Y-022126X0394Y0571R180S2
|
||||||
|
327DP R13 -2 A01X+058740Y-020202X0404Y0551R090S2
|
||||||
|
327-(J1-D+-PADA6) R13 -1 A01X+058740Y-019483X0404Y0551R090S2
|
||||||
|
327GND C13 -2 A01X+062374Y-022625X0394Y0571R000S2
|
||||||
|
327+3.3V C13 -1 A01X+061626Y-022625X0394Y0571R000S2
|
||||||
|
327GND C12 -2 A01X+057087Y-024921X0394Y0571R090S2
|
||||||
|
327+3.3V C12 -1 A01X+057087Y-024173X0394Y0571R090S2
|
||||||
|
327POWER_RAIL_IN U2 -10 A01X+065366Y-022801X0571Y0118R270S2
|
||||||
|
327POWER_RAIL_OUT U2 -9 A01X+065563Y-022801X0571Y0118R270S2
|
||||||
|
327POWER_RAIL_IN U2 -8 A01X+065760Y-022801X0571Y0118R270S2
|
||||||
|
327GND U2 -7 A01X+065957Y-022801X0571Y0118R270S2
|
||||||
|
327+3.3V U2 -6 A01X+066154Y-022801X0571Y0118R270S2
|
||||||
|
327SCL U2 -5 A01X+066154Y-024533X0571Y0118R270S2
|
||||||
|
327SDA U2 -4 A01X+065957Y-024533X0571Y0118R270S2
|
||||||
|
327~{ALERT}-PAD3) U2 -3 A01X+065760Y-024533X0571Y0118R270S2
|
||||||
|
327D-(U2-A0-PAD2) U2 -2 A01X+065563Y-024533X0571Y0118R270S2
|
||||||
|
327D-(U2-A1-PAD1) U2 -1 A01X+065366Y-024533X0571Y0118R270S2
|
||||||
|
327GND C16 -2 A01X+058110Y-025492X0394Y0571R090S2
|
||||||
|
327+3.3V C16 -1 A01X+058110Y-024744X0394Y0571R090S2
|
||||||
|
327ET-(U3-GPIO17) TP9 -1 A01X+063625Y-021875X0787Y0000R000S2
|
||||||
|
327POWER_RAIL_OUT R9 -2 A01X+064715Y-020000X0482Y1043R180S2
|
||||||
|
327POWER_RAIL_IN R9 -1 A01X+066535Y-020000X0482Y1043R180S2
|
||||||
|
327RX TP5 -1 A01X+069500Y-026250X0787Y0000R000S2
|
||||||
|
327ET-(J14-PIN_2) R6 -2 A01X+061855Y-025197X0404Y0551R000S2
|
||||||
|
327RGB R6 -1 A01X+061137Y-025197X0404Y0551R000S2
|
||||||
|
327ET-(U3-GPIO14) TP8 -1 A01X+063625Y-023500X0787Y0000R000S2
|
||||||
|
327GND C9 -2 A01X+066929Y-024075X0394Y0571R090S2
|
||||||
|
327+3.3V C9 -1 A01X+066929Y-023327X0394Y0571R090S2
|
||||||
|
327DM R12 -2 A01X+059528Y-020202X0404Y0551R090S2
|
||||||
|
327-(J1-D--PADA7) R12 -1 A01X+059528Y-019483X0404Y0551R090S2
|
||||||
|
327ET-(U3-GPIO18) TP10 -1 A01X+062125Y-020625X0787Y0000R000S2
|
||||||
|
317RX3 J3 -3 D0394PA00X+073750Y-025474X0669Y0000R000S0
|
||||||
|
317TX3 J3 -2 D0394PA00X+073750Y-024474X0669Y0000R000S0
|
||||||
|
317GND J3 -1 D0394PA00X+073750Y-023474X0669Y0669R000S0
|
||||||
|
327NET-(U3-GPIO6) TP6 -1 A01X+060079Y-026457X0787Y0000R000S2
|
||||||
|
327RX4 U10 -1 A01X+071500Y-021500X0768Y0236R090S2
|
||||||
|
327-(U10-A6-PAD2) U10 -2 A01X+071000Y-021500X0768Y0236R090S2
|
||||||
|
327TX U10 -3 A01X+070500Y-021500X0768Y0236R090S2
|
||||||
|
327-(U10-A7-PAD4) U10 -4 A01X+070000Y-021500X0768Y0236R090S2
|
||||||
|
327-(U10-A5-PAD5) U10 -5 A01X+069500Y-021500X0768Y0236R090S2
|
||||||
|
327GND U10 -6 A01X+069000Y-021500X0768Y0236R090S2
|
||||||
|
327GND U10 -7 A01X+068500Y-021500X0768Y0236R090S2
|
||||||
|
327GND U10 -8 A01X+068000Y-021500X0768Y0236R090S2
|
||||||
|
327A2 U10 -9 A01X+068000Y-023449X0768Y0236R090S2
|
||||||
|
327A1 U10 -10 A01X+068500Y-023449X0768Y0236R090S2
|
||||||
|
327A0 U10 -11 A01X+069000Y-023449X0768Y0236R090S2
|
||||||
|
327RX3 U10 -12 A01X+069500Y-023449X0768Y0236R090S2
|
||||||
|
327RX0 U10 -13 A01X+070000Y-023449X0768Y0236R090S2
|
||||||
|
327RX1 U10 -14 A01X+070500Y-023449X0768Y0236R090S2
|
||||||
|
327RX2 U10 -15 A01X+071000Y-023449X0768Y0236R090S2
|
||||||
|
327+3.3V U10 -16 A01X+071500Y-023449X0768Y0236R090S2
|
||||||
|
327+5V D6 -1 A01X+060625Y-029000X0354Y0472R180S2
|
||||||
|
327NET-(D6-A) D6 -2 A01X+059326Y-029000X0354Y0472R180S2
|
||||||
|
327NET-(J1-CC2) R11 -1 A01X+057493Y-019442X0404Y0551R180S2
|
||||||
|
327GND R11 -2 A01X+056775Y-019442X0404Y0551R180S2
|
||||||
|
327+5V C19 -1 A01X+062374Y-028500X0394Y0571R180S2
|
||||||
|
327GND C19 -2 A01X+061626Y-028500X0394Y0571R180S2
|
||||||
|
327T-(U3-CHIP_PU) R15 -1 A01X+058189Y-026176X0404Y0551R090S2
|
||||||
|
327+3.3V R15 -2 A01X+058189Y-026895X0404Y0551R090S2
|
||||||
|
327TX R7 -1 A01X+070641Y-020250X0404Y0551R000S2
|
||||||
|
327+3.3V R7 -2 A01X+071359Y-020250X0404Y0551R000S2
|
||||||
|
317GND H4 -1 D1063PA00X+075250Y-017125X2126Y0000R000S0
|
||||||
|
327N/C FID3 A01X+066750Y-030125X0591Y0000R000S2
|
||||||
|
327+3.3V C17 -1 A01X+064376Y-026500X0394Y0571R000S2
|
||||||
|
327GND C17 -2 A01X+065124Y-026500X0394Y0571R000S2
|
||||||
|
317GND J5 -1 D0394PA00X+075750Y-023474X0669Y0669R000S0
|
||||||
|
317TX1 J5 -2 D0394PA00X+075750Y-024474X0669Y0000R000S0
|
||||||
|
317RX1 J5 -3 D0394PA00X+075750Y-025474X0669Y0000R000S0
|
||||||
|
327TX4 U11 -1 A01X+071500Y-027500X0768Y0236R090S2
|
||||||
|
327-(U11-A6-PAD2) U11 -2 A01X+071000Y-027500X0768Y0236R090S2
|
||||||
|
327RX U11 -3 A01X+070500Y-027500X0768Y0236R090S2
|
||||||
|
327-(U11-A7-PAD4) U11 -4 A01X+070000Y-027500X0768Y0236R090S2
|
||||||
|
327-(U11-A5-PAD5) U11 -5 A01X+069500Y-027500X0768Y0236R090S2
|
||||||
|
327GND U11 -6 A01X+069000Y-027500X0768Y0236R090S2
|
||||||
|
327GND U11 -7 A01X+068500Y-027500X0768Y0236R090S2
|
||||||
|
327GND U11 -8 A01X+068000Y-027500X0768Y0236R090S2
|
||||||
|
327A2 U11 -9 A01X+068000Y-029449X0768Y0236R090S2
|
||||||
|
327A1 U11 -10 A01X+068500Y-029449X0768Y0236R090S2
|
||||||
|
327A0 U11 -11 A01X+069000Y-029449X0768Y0236R090S2
|
||||||
|
327TX3 U11 -12 A01X+069500Y-029449X0768Y0236R090S2
|
||||||
|
327TX0 U11 -13 A01X+070000Y-029449X0768Y0236R090S2
|
||||||
|
327TX1 U11 -14 A01X+070500Y-029449X0768Y0236R090S2
|
||||||
|
327TX2 U11 -15 A01X+071000Y-029449X0768Y0236R090S2
|
||||||
|
327+3.3V U11 -16 A01X+071500Y-029449X0768Y0236R090S2
|
||||||
|
327+3.3V TP3 -1 A01X+063375Y-025250X0787Y0000R000S2
|
||||||
|
317EN0.0 J7 -1 D0394PA00X+047250Y-018992X0669Y0669R180S0
|
||||||
|
317EN0.1 J7 -2 D0394PA00X+047250Y-017992X0669Y0000R180S0
|
||||||
|
327+3.3V C4 -1 A01X+056249Y-025125X0394Y0571R180S2
|
||||||
|
327GND C4 -2 A01X+055501Y-025125X0394Y0571R180S2
|
||||||
|
327GND SW1 -1 A01X+069300Y-016739X0630Y0551R180S2
|
||||||
|
327GND SW1 -1 A01X+072450Y-016739X0630Y0551R180S2
|
||||||
|
327NET-(U3-GPIO0) SW1 -2 A01X+069300Y-018511X0630Y0551R180S2
|
||||||
|
327NET-(U3-GPIO0) SW1 -2 A01X+072450Y-018511X0630Y0551R180S2
|
||||||
|
327NET-(R3-PAD1) U6 -1 A01X+052595Y-024000X0719Y0256R180S2
|
||||||
|
327GND U6 -2 A01X+052595Y-023000X0719Y0256R180S2
|
||||||
|
327EN2.1 U6 -3 A01X+048905Y-023000X0719Y0256R180S2
|
||||||
|
327EN2.0 U6 -4 A01X+048905Y-024000X0719Y0256R180S2
|
||||||
|
327NET-(R5-PAD1) U8 -1 A01X+052595Y-029000X0719Y0256R180S2
|
||||||
|
327GND U8 -2 A01X+052595Y-028000X0719Y0256R180S2
|
||||||
|
327EN4.1 U8 -3 A01X+048905Y-028000X0719Y0256R180S2
|
||||||
|
327EN4.0 U8 -4 A01X+048905Y-029000X0719Y0256R180S2
|
||||||
|
327+3.3V C7 -1 A01X+072250Y-029376X0394Y0571R090S2
|
||||||
|
327GND C7 -2 A01X+072250Y-030124X0394Y0571R090S2
|
||||||
|
327NET-(R4-PAD1) U7 -1 A01X+052595Y-026500X0719Y0256R180S2
|
||||||
|
327GND U7 -2 A01X+052595Y-025500X0719Y0256R180S2
|
||||||
|
327EN3.1 U7 -3 A01X+048905Y-025500X0719Y0256R180S2
|
||||||
|
327EN3.0 U7 -4 A01X+048905Y-026500X0719Y0256R180S2
|
||||||
|
327NET-(U3-GPIO0) R14 -1 A01X+059055Y-026176X0404Y0551R090S2
|
||||||
|
327+3.3V R14 -2 A01X+059055Y-026895X0404Y0551R090S2
|
||||||
|
327NET-(J1-CC1) R10 -1 A01X+060775Y-019442X0404Y0551R000S2
|
||||||
|
327GND R10 -2 A01X+061493Y-019442X0404Y0551R000S2
|
||||||
|
317EN1.0 J8 -1 D0394PA00X+047250Y-021500X0669Y0669R180S0
|
||||||
|
317EN1.1 J8 -2 D0394PA00X+047250Y-020500X0669Y0000R180S0
|
||||||
|
327NET-(U3-GPIO7) TP7 -1 A01X+061339Y-026457X0787Y0000R000S2
|
||||||
|
327+3.3V C8 -1 A01X+071374Y-024724X0394Y0571R180S2
|
||||||
|
327GND C8 -2 A01X+070626Y-024724X0394Y0571R180S2
|
||||||
|
327RX R8 -1 A01X+070641Y-026220X0404Y0551R000S2
|
||||||
|
327+3.3V R8 -2 A01X+071359Y-026220X0404Y0551R000S2
|
||||||
|
327NET-(R1-PAD1) R1 -1 A01X+053625Y-019016X0404Y0551R090S2
|
||||||
|
327EN0 R1 -2 A01X+053625Y-019734X0404Y0551R090S2
|
||||||
|
327+5V C20 -1 A01X+062375Y-027625X0394Y0571R180S2
|
||||||
|
327GND C20 -2 A01X+061627Y-027625X0394Y0571R180S2
|
||||||
|
317GND J2 -1 D0394PA00X+073750Y-027724X0669Y0669R000S0
|
||||||
|
317TX4 J2 -2 D0394PA00X+073750Y-028724X0669Y0000R000S0
|
||||||
|
317RX4 J2 -3 D0394PA00X+073750Y-029724X0669Y0000R000S0
|
||||||
|
327+3.3V C14 -1 A01X+056124Y-021250X0394Y0571R180S2
|
||||||
|
327GND C14 -2 A01X+055376Y-021250X0394Y0571R180S2
|
||||||
|
317GND J6 -1 D0394PA00X+075750Y-019474X0669Y0669R000S0
|
||||||
|
317TX0 J6 -2 D0394PA00X+075750Y-020474X0669Y0000R000S0
|
||||||
|
317RX0 J6 -3 D0394PA00X+075750Y-021474X0669Y0000R000S0
|
||||||
|
327N/C FID2 A01X+047750Y-016500X0591Y0000R000S2
|
||||||
|
327GND U1 -1 A01X+065281Y-027510X0787Y0591R090S2
|
||||||
|
327+3.3V U1 -2 A01X+064375Y-027510X0787Y0591R090S2
|
||||||
|
327+3.3V U1 -2 A01X+064375Y-029990X0787Y1496R090S2
|
||||||
|
327+5V U1 -3 A01X+063469Y-027510X0787Y0591R090S2
|
||||||
|
317EN3.0 J10 -1 D0394PA00X+047250Y-026500X0669Y0669R180S0
|
||||||
|
317EN3.1 J10 -2 D0394PA00X+047250Y-025500X0669Y0000R180S0
|
||||||
|
327NET-(R4-PAD1) R4 -1 A01X+053625Y-026516X0404Y0551R090S2
|
||||||
|
327EN3 R4 -2 A01X+053625Y-027234X0404Y0551R090S2
|
||||||
|
327NET-(R5-PAD1) R5 -1 A01X+053625Y-028984X0404Y0551R270S2
|
||||||
|
327EN4 R5 -2 A01X+053625Y-028266X0404Y0551R270S2
|
||||||
|
327NET-(R3-PAD1) R3 -1 A01X+053625Y-024016X0404Y0551R090S2
|
||||||
|
327EN2 R3 -2 A01X+053625Y-024734X0404Y0551R090S2
|
||||||
|
317GND H3 -1 D1063PA00X+054750Y-017125X2126Y0000R000S0
|
||||||
|
317EN4.0 J11 -1 D0394PA00X+047250Y-029000X0669Y0669R180S0
|
||||||
|
317EN4.1 J11 -2 D0394PA00X+047250Y-028000X0669Y0000R180S0
|
||||||
|
327+5V TP2 -1 A01X+062625Y-026500X0787Y0000R000S2
|
||||||
|
327+3.3V C18 -1 A01X+064376Y-025625X0394Y0571R000S2
|
||||||
|
327GND C18 -2 A01X+065124Y-025625X0394Y0571R000S2
|
||||||
|
317GND H1 -1 D1063PA00X+055625Y-029750X2126Y0000R000S0
|
||||||
|
317NET-(D6-A) J13 -1 D0394PA00X+059326Y-030486X0669Y0669R270S0
|
||||||
|
317GND J13 -2 D0394PA00X+060326Y-030486X0669Y0000R270S0
|
||||||
|
317POWER_RAIL_IN J12 -1 D0630PA00X+066937Y-017414X0787Y1024R180S0
|
||||||
|
317GND J12 -2 D0630PA00X+065559Y-017414X0787Y1024R180S0
|
||||||
|
317POWER_RAIL_OUT J12 -3 D0630PA00X+064181Y-017414X0787Y1024R180S0
|
||||||
|
317GND J4 -1 D0394PA00X+075750Y-027724X0669Y0669R000S0
|
||||||
|
317TX2 J4 -2 D0394PA00X+075750Y-028724X0669Y0000R000S0
|
||||||
|
317RX2 J4 -3 D0394PA00X+075750Y-029724X0669Y0000R000S0
|
||||||
|
327NET-(R2-PAD1) R2 -1 A01X+053625Y-021500X0404Y0551R090S2
|
||||||
|
327EN1 R2 -2 A01X+053625Y-022219X0404Y0551R090S2
|
||||||
|
327N/C FID1 A01X+073500Y-020000X0591Y0000R000S2
|
||||||
|
327NET-(R2-PAD1) U5 -1 A01X+052595Y-021500X0719Y0256R180S2
|
||||||
|
327GND U5 -2 A01X+052595Y-020500X0719Y0256R180S2
|
||||||
|
327EN1.1 U5 -3 A01X+048905Y-020500X0719Y0256R180S2
|
||||||
|
327EN1.0 U5 -4 A01X+048905Y-021500X0719Y0256R180S2
|
||||||
|
317EN2.0 J9 -1 D0394PA00X+047250Y-024000X0669Y0669R180S0
|
||||||
|
317EN2.1 J9 -2 D0394PA00X+047250Y-023000X0669Y0000R180S0
|
||||||
|
999
|
||||||
BIN
kicad/watch-watchkicad_pro/production/watch-watchkicad_pro.zip
Normal file
BIN
kicad/watch-watchkicad_pro/production/watch-watchkicad_pro.zip
Normal file
Binary file not shown.
27041
kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pcb
Normal file
27041
kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pcb
Normal file
File diff suppressed because it is too large
Load Diff
669
kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pro
Normal file
669
kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_pro
Normal file
@@ -0,0 +1,669 @@
|
|||||||
|
{
|
||||||
|
"board": {
|
||||||
|
"3dviewports": [],
|
||||||
|
"design_settings": {
|
||||||
|
"defaults": {
|
||||||
|
"apply_defaults_to_fp_fields": false,
|
||||||
|
"apply_defaults_to_fp_shapes": false,
|
||||||
|
"apply_defaults_to_fp_text": false,
|
||||||
|
"board_outline_line_width": 0.05,
|
||||||
|
"copper_line_width": 0.2,
|
||||||
|
"copper_text_italic": false,
|
||||||
|
"copper_text_size_h": 1.5,
|
||||||
|
"copper_text_size_v": 1.5,
|
||||||
|
"copper_text_thickness": 0.3,
|
||||||
|
"copper_text_upright": false,
|
||||||
|
"courtyard_line_width": 0.05,
|
||||||
|
"dimension_precision": 4,
|
||||||
|
"dimension_units": 3,
|
||||||
|
"dimensions": {
|
||||||
|
"arrow_length": 1270000,
|
||||||
|
"extension_offset": 500000,
|
||||||
|
"keep_text_aligned": true,
|
||||||
|
"suppress_zeroes": true,
|
||||||
|
"text_position": 0,
|
||||||
|
"units_format": 0
|
||||||
|
},
|
||||||
|
"fab_line_width": 0.1,
|
||||||
|
"fab_text_italic": false,
|
||||||
|
"fab_text_size_h": 1.0,
|
||||||
|
"fab_text_size_v": 1.0,
|
||||||
|
"fab_text_thickness": 0.15,
|
||||||
|
"fab_text_upright": false,
|
||||||
|
"other_line_width": 0.1,
|
||||||
|
"other_text_italic": false,
|
||||||
|
"other_text_size_h": 1.0,
|
||||||
|
"other_text_size_v": 1.0,
|
||||||
|
"other_text_thickness": 0.15,
|
||||||
|
"other_text_upright": false,
|
||||||
|
"pads": {
|
||||||
|
"drill": 1.0,
|
||||||
|
"height": 1.7,
|
||||||
|
"width": 1.7
|
||||||
|
},
|
||||||
|
"silk_line_width": 0.1,
|
||||||
|
"silk_text_italic": false,
|
||||||
|
"silk_text_size_h": 1.0,
|
||||||
|
"silk_text_size_v": 1.0,
|
||||||
|
"silk_text_thickness": 0.1,
|
||||||
|
"silk_text_upright": false,
|
||||||
|
"zones": {
|
||||||
|
"min_clearance": 0.4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"diff_pair_dimensions": [],
|
||||||
|
"drc_exclusions": [],
|
||||||
|
"meta": {
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"rule_severities": {
|
||||||
|
"annular_width": "error",
|
||||||
|
"clearance": "error",
|
||||||
|
"connection_width": "warning",
|
||||||
|
"copper_edge_clearance": "error",
|
||||||
|
"copper_sliver": "warning",
|
||||||
|
"courtyards_overlap": "error",
|
||||||
|
"creepage": "error",
|
||||||
|
"diff_pair_gap_out_of_range": "error",
|
||||||
|
"diff_pair_uncoupled_length_too_long": "error",
|
||||||
|
"drill_out_of_range": "error",
|
||||||
|
"duplicate_footprints": "warning",
|
||||||
|
"extra_footprint": "warning",
|
||||||
|
"footprint": "error",
|
||||||
|
"footprint_filters_mismatch": "ignore",
|
||||||
|
"footprint_symbol_mismatch": "warning",
|
||||||
|
"footprint_type_mismatch": "ignore",
|
||||||
|
"hole_clearance": "error",
|
||||||
|
"hole_to_hole": "warning",
|
||||||
|
"holes_co_located": "warning",
|
||||||
|
"invalid_outline": "error",
|
||||||
|
"isolated_copper": "warning",
|
||||||
|
"item_on_disabled_layer": "error",
|
||||||
|
"items_not_allowed": "error",
|
||||||
|
"length_out_of_range": "error",
|
||||||
|
"lib_footprint_issues": "warning",
|
||||||
|
"lib_footprint_mismatch": "warning",
|
||||||
|
"malformed_courtyard": "error",
|
||||||
|
"microvia_drill_out_of_range": "error",
|
||||||
|
"mirrored_text_on_front_layer": "warning",
|
||||||
|
"missing_courtyard": "ignore",
|
||||||
|
"missing_footprint": "warning",
|
||||||
|
"net_conflict": "warning",
|
||||||
|
"nonmirrored_text_on_back_layer": "warning",
|
||||||
|
"npth_inside_courtyard": "ignore",
|
||||||
|
"padstack": "warning",
|
||||||
|
"pth_inside_courtyard": "ignore",
|
||||||
|
"shorting_items": "error",
|
||||||
|
"silk_edge_clearance": "warning",
|
||||||
|
"silk_over_copper": "warning",
|
||||||
|
"silk_overlap": "warning",
|
||||||
|
"skew_out_of_range": "error",
|
||||||
|
"solder_mask_bridge": "error",
|
||||||
|
"starved_thermal": "error",
|
||||||
|
"text_height": "warning",
|
||||||
|
"text_on_edge_cuts": "error",
|
||||||
|
"text_thickness": "warning",
|
||||||
|
"through_hole_pad_without_hole": "error",
|
||||||
|
"too_many_vias": "error",
|
||||||
|
"track_angle": "error",
|
||||||
|
"track_dangling": "warning",
|
||||||
|
"track_segment_length": "error",
|
||||||
|
"track_width": "error",
|
||||||
|
"tracks_crossing": "error",
|
||||||
|
"unconnected_items": "error",
|
||||||
|
"unresolved_variable": "error",
|
||||||
|
"via_dangling": "warning",
|
||||||
|
"zones_intersect": "error"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"max_error": 0.005,
|
||||||
|
"min_clearance": 0.0,
|
||||||
|
"min_connection": 0.0,
|
||||||
|
"min_copper_edge_clearance": 0.5,
|
||||||
|
"min_groove_width": 0.0,
|
||||||
|
"min_hole_clearance": 0.25,
|
||||||
|
"min_hole_to_hole": 0.25,
|
||||||
|
"min_microvia_diameter": 0.2,
|
||||||
|
"min_microvia_drill": 0.1,
|
||||||
|
"min_resolved_spokes": 2,
|
||||||
|
"min_silk_clearance": 0.0,
|
||||||
|
"min_text_height": 0.8,
|
||||||
|
"min_text_thickness": 0.08,
|
||||||
|
"min_through_hole_diameter": 0.3,
|
||||||
|
"min_track_width": 0.0,
|
||||||
|
"min_via_annular_width": 0.1,
|
||||||
|
"min_via_diameter": 0.5,
|
||||||
|
"solder_mask_to_copper_clearance": 0.005,
|
||||||
|
"use_height_for_length_calcs": true
|
||||||
|
},
|
||||||
|
"teardrop_options": [
|
||||||
|
{
|
||||||
|
"td_onpthpad": true,
|
||||||
|
"td_onroundshapesonly": false,
|
||||||
|
"td_onsmdpad": true,
|
||||||
|
"td_ontrackend": false,
|
||||||
|
"td_onvia": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"teardrop_parameters": [
|
||||||
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
|
"td_target_name": "td_round_shape",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
|
"td_target_name": "td_rect_shape",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
|
"td_target_name": "td_track_end",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"track_widths": [],
|
||||||
|
"tuning_pattern_settings": {
|
||||||
|
"diff_pair_defaults": {
|
||||||
|
"corner_radius_percentage": 80,
|
||||||
|
"corner_style": 1,
|
||||||
|
"max_amplitude": 1.0,
|
||||||
|
"min_amplitude": 0.2,
|
||||||
|
"single_sided": false,
|
||||||
|
"spacing": 1.0
|
||||||
|
},
|
||||||
|
"diff_pair_skew_defaults": {
|
||||||
|
"corner_radius_percentage": 80,
|
||||||
|
"corner_style": 1,
|
||||||
|
"max_amplitude": 1.0,
|
||||||
|
"min_amplitude": 0.2,
|
||||||
|
"single_sided": false,
|
||||||
|
"spacing": 0.6
|
||||||
|
},
|
||||||
|
"single_track_defaults": {
|
||||||
|
"corner_radius_percentage": 80,
|
||||||
|
"corner_style": 1,
|
||||||
|
"max_amplitude": 1.0,
|
||||||
|
"min_amplitude": 0.2,
|
||||||
|
"single_sided": false,
|
||||||
|
"spacing": 0.6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"via_dimensions": [],
|
||||||
|
"zones_allow_external_fillets": false
|
||||||
|
},
|
||||||
|
"ipc2581": {
|
||||||
|
"dist": "",
|
||||||
|
"distpn": "",
|
||||||
|
"internal_id": "",
|
||||||
|
"mfg": "",
|
||||||
|
"mpn": ""
|
||||||
|
},
|
||||||
|
"layer_pairs": [],
|
||||||
|
"layer_presets": [],
|
||||||
|
"viewports": []
|
||||||
|
},
|
||||||
|
"boards": [],
|
||||||
|
"cvpcb": {
|
||||||
|
"equivalence_files": []
|
||||||
|
},
|
||||||
|
"erc": {
|
||||||
|
"erc_exclusions": [],
|
||||||
|
"meta": {
|
||||||
|
"version": 0
|
||||||
|
},
|
||||||
|
"pin_map": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"rule_severities": {
|
||||||
|
"bus_definition_conflict": "error",
|
||||||
|
"bus_entry_needed": "error",
|
||||||
|
"bus_to_bus_conflict": "error",
|
||||||
|
"bus_to_net_conflict": "error",
|
||||||
|
"different_unit_footprint": "error",
|
||||||
|
"different_unit_net": "error",
|
||||||
|
"duplicate_reference": "error",
|
||||||
|
"duplicate_sheet_names": "error",
|
||||||
|
"endpoint_off_grid": "warning",
|
||||||
|
"extra_units": "error",
|
||||||
|
"footprint_filter": "ignore",
|
||||||
|
"footprint_link_issues": "warning",
|
||||||
|
"four_way_junction": "ignore",
|
||||||
|
"global_label_dangling": "warning",
|
||||||
|
"hier_label_mismatch": "error",
|
||||||
|
"label_dangling": "error",
|
||||||
|
"label_multiple_wires": "warning",
|
||||||
|
"lib_symbol_issues": "warning",
|
||||||
|
"lib_symbol_mismatch": "warning",
|
||||||
|
"missing_bidi_pin": "warning",
|
||||||
|
"missing_input_pin": "warning",
|
||||||
|
"missing_power_pin": "error",
|
||||||
|
"missing_unit": "warning",
|
||||||
|
"multiple_net_names": "warning",
|
||||||
|
"net_not_bus_member": "warning",
|
||||||
|
"no_connect_connected": "warning",
|
||||||
|
"no_connect_dangling": "warning",
|
||||||
|
"pin_not_connected": "error",
|
||||||
|
"pin_not_driven": "error",
|
||||||
|
"pin_to_pin": "warning",
|
||||||
|
"power_pin_not_driven": "error",
|
||||||
|
"same_local_global_label": "warning",
|
||||||
|
"similar_label_and_power": "warning",
|
||||||
|
"similar_labels": "warning",
|
||||||
|
"similar_power": "warning",
|
||||||
|
"simulation_model_issue": "ignore",
|
||||||
|
"single_global_label": "ignore",
|
||||||
|
"unannotated": "error",
|
||||||
|
"unconnected_wire_endpoint": "warning",
|
||||||
|
"undefined_netclass": "error",
|
||||||
|
"unit_value_mismatch": "error",
|
||||||
|
"unresolved_variable": "error",
|
||||||
|
"wire_dangling": "error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"pinned_footprint_libs": [],
|
||||||
|
"pinned_symbol_libs": []
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"filename": "watch-watchkicad_pro.kicad_pro",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"net_settings": {
|
||||||
|
"classes": [
|
||||||
|
{
|
||||||
|
"bus_width": 12,
|
||||||
|
"clearance": 0.2,
|
||||||
|
"diff_pair_gap": 0.25,
|
||||||
|
"diff_pair_via_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.2,
|
||||||
|
"line_style": 0,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "Default",
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"priority": 2147483647,
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.2,
|
||||||
|
"via_diameter": 0.6,
|
||||||
|
"via_drill": 0.3,
|
||||||
|
"wire_width": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"clearance": 0.2,
|
||||||
|
"diff_pair_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.2,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "DigitalSig",
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"priority": 0,
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.3,
|
||||||
|
"via_diameter": 0.6,
|
||||||
|
"via_drill": 0.3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"version": 4
|
||||||
|
},
|
||||||
|
"net_colors": null,
|
||||||
|
"netclass_assignments": null,
|
||||||
|
"netclass_patterns": []
|
||||||
|
},
|
||||||
|
"pcbnew": {
|
||||||
|
"last_paths": {
|
||||||
|
"gencad": "",
|
||||||
|
"idf": "",
|
||||||
|
"netlist": "",
|
||||||
|
"plot": "",
|
||||||
|
"pos_files": "",
|
||||||
|
"specctra_dsn": "",
|
||||||
|
"step": "",
|
||||||
|
"svg": "",
|
||||||
|
"vrml": ""
|
||||||
|
},
|
||||||
|
"page_layout_descr_file": ""
|
||||||
|
},
|
||||||
|
"schematic": {
|
||||||
|
"annotate_start_num": 0,
|
||||||
|
"bom_export_filename": "${PROJECTNAME}.csv",
|
||||||
|
"bom_fmt_presets": [],
|
||||||
|
"bom_fmt_settings": {
|
||||||
|
"field_delimiter": ",",
|
||||||
|
"keep_line_breaks": false,
|
||||||
|
"keep_tabs": false,
|
||||||
|
"name": "CSV",
|
||||||
|
"ref_delimiter": ",",
|
||||||
|
"ref_range_delimiter": "",
|
||||||
|
"string_delimiter": "\""
|
||||||
|
},
|
||||||
|
"bom_presets": [],
|
||||||
|
"bom_settings": {
|
||||||
|
"exclude_dnp": false,
|
||||||
|
"fields_ordered": [
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Reference",
|
||||||
|
"name": "Reference",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Qty",
|
||||||
|
"name": "${QUANTITY}",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": true,
|
||||||
|
"label": "Value",
|
||||||
|
"name": "Value",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": true,
|
||||||
|
"label": "DNP",
|
||||||
|
"name": "${DNP}",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": true,
|
||||||
|
"label": "Exclude from BOM",
|
||||||
|
"name": "${EXCLUDE_FROM_BOM}",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": true,
|
||||||
|
"label": "Exclude from Board",
|
||||||
|
"name": "${EXCLUDE_FROM_BOARD}",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": true,
|
||||||
|
"label": "Footprint",
|
||||||
|
"name": "Footprint",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Datasheet",
|
||||||
|
"name": "Datasheet",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "постачальник",
|
||||||
|
"name": "постачальник",
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Постачальник",
|
||||||
|
"name": "Постачальник",
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Опис",
|
||||||
|
"name": "Description",
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "#",
|
||||||
|
"name": "${ITEM_NUMBER}",
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Sim.Pins",
|
||||||
|
"name": "Sim.Pins",
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Sim.Device",
|
||||||
|
"name": "Sim.Device",
|
||||||
|
"show": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filter_string": "",
|
||||||
|
"group_symbols": true,
|
||||||
|
"include_excluded_from_bom": true,
|
||||||
|
"name": "",
|
||||||
|
"sort_asc": true,
|
||||||
|
"sort_field": "Reference"
|
||||||
|
},
|
||||||
|
"connection_grid_size": 50.0,
|
||||||
|
"drawing": {
|
||||||
|
"dashed_lines_dash_length_ratio": 12.0,
|
||||||
|
"dashed_lines_gap_length_ratio": 3.0,
|
||||||
|
"default_line_thickness": 6.0,
|
||||||
|
"default_text_size": 50.0,
|
||||||
|
"field_names": [],
|
||||||
|
"intersheets_ref_own_page": false,
|
||||||
|
"intersheets_ref_prefix": "",
|
||||||
|
"intersheets_ref_short": false,
|
||||||
|
"intersheets_ref_show": false,
|
||||||
|
"intersheets_ref_suffix": "",
|
||||||
|
"junction_size_choice": 3,
|
||||||
|
"label_size_ratio": 0.375,
|
||||||
|
"operating_point_overlay_i_precision": 3,
|
||||||
|
"operating_point_overlay_i_range": "~A",
|
||||||
|
"operating_point_overlay_v_precision": 3,
|
||||||
|
"operating_point_overlay_v_range": "~V",
|
||||||
|
"overbar_offset_ratio": 1.23,
|
||||||
|
"pin_symbol_size": 25.0,
|
||||||
|
"text_offset_ratio": 0.15
|
||||||
|
},
|
||||||
|
"legacy_lib_dir": "",
|
||||||
|
"legacy_lib_list": [],
|
||||||
|
"meta": {
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"net_format_name": "",
|
||||||
|
"page_layout_descr_file": "",
|
||||||
|
"plot_directory": "",
|
||||||
|
"space_save_all_events": true,
|
||||||
|
"spice_current_sheet_as_root": false,
|
||||||
|
"spice_external_command": "spice \"%I\"",
|
||||||
|
"spice_model_current_sheet_as_root": true,
|
||||||
|
"spice_save_all_currents": false,
|
||||||
|
"spice_save_all_dissipations": false,
|
||||||
|
"spice_save_all_voltages": false,
|
||||||
|
"subpart_first_id": 65,
|
||||||
|
"subpart_id_separator": 0
|
||||||
|
},
|
||||||
|
"sheets": [
|
||||||
|
[
|
||||||
|
"e717a214-aafd-4c38-81fe-477ca8e64d07",
|
||||||
|
"Root"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"text_variables": {}
|
||||||
|
}
|
||||||
18687
kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_sch
Normal file
18687
kicad/watch-watchkicad_pro/watch-watchkicad_pro.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
BIN
kicad/watch-watchkicad_pro/watch-watchkicad_pro.pdf
Normal file
BIN
kicad/watch-watchkicad_pro/watch-watchkicad_pro.pdf
Normal file
Binary file not shown.
@@ -1,7 +1,8 @@
|
|||||||
idf_component_register(SRCS "main.c"
|
idf_component_register(SRCS "ws2812_status.c" "watch_config.c" "usb_cdc_cli.c" "main.c"
|
||||||
"dcdc_controller.c"
|
"dcdc_controller.c"
|
||||||
"usb_cdc_cli.c"
|
"usb_cdc_log.c"
|
||||||
"ws2812_status.c"
|
|
||||||
"ina226_monitor.c"
|
"ina226_monitor.c"
|
||||||
"uart_mux.c"
|
"uart_mux.c"
|
||||||
INCLUDE_DIRS ".")
|
INCLUDE_DIRS "."
|
||||||
|
REQUIRES nvs_flash driver esp_timer)
|
||||||
|
|||||||
@@ -1,5 +1,34 @@
|
|||||||
menu "Налаштування watch-watch"
|
menu "Налаштування watch-watch"
|
||||||
|
|
||||||
|
menu "DC/DC контролер"
|
||||||
|
|
||||||
|
config WATCH_DCDC_EN_GPIO_0
|
||||||
|
int "GPIO EN каналу 0"
|
||||||
|
range 0 48
|
||||||
|
default 2
|
||||||
|
|
||||||
|
config WATCH_DCDC_EN_GPIO_1
|
||||||
|
int "GPIO EN каналу 1"
|
||||||
|
range 0 48
|
||||||
|
default 4
|
||||||
|
|
||||||
|
config WATCH_DCDC_EN_GPIO_2
|
||||||
|
int "GPIO EN каналу 2"
|
||||||
|
range 0 48
|
||||||
|
default 5
|
||||||
|
|
||||||
|
config WATCH_DCDC_EN_GPIO_3
|
||||||
|
int "GPIO EN каналу 3"
|
||||||
|
range 0 48
|
||||||
|
default 18
|
||||||
|
|
||||||
|
config WATCH_DCDC_EN_GPIO_4
|
||||||
|
int "GPIO EN каналу 4"
|
||||||
|
range 0 48
|
||||||
|
default 19
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
config WATCH_WS2812_LED_COUNT
|
config WATCH_WS2812_LED_COUNT
|
||||||
int "Кількість статусних світлодіодів WS2812"
|
int "Кількість статусних світлодіодів WS2812"
|
||||||
range 1 30
|
range 1 30
|
||||||
@@ -2,15 +2,32 @@
|
|||||||
|
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
static const char *TAG = "dcdc";
|
static const char *TAG = "dcdc";
|
||||||
|
|
||||||
|
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_0
|
||||||
|
#define CONFIG_WATCH_DCDC_EN_GPIO_0 2
|
||||||
|
#endif
|
||||||
|
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_1
|
||||||
|
#define CONFIG_WATCH_DCDC_EN_GPIO_1 4
|
||||||
|
#endif
|
||||||
|
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_2
|
||||||
|
#define CONFIG_WATCH_DCDC_EN_GPIO_2 5
|
||||||
|
#endif
|
||||||
|
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_3
|
||||||
|
#define CONFIG_WATCH_DCDC_EN_GPIO_3 18
|
||||||
|
#endif
|
||||||
|
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_4
|
||||||
|
#define CONFIG_WATCH_DCDC_EN_GPIO_4 19
|
||||||
|
#endif
|
||||||
|
|
||||||
static const gpio_num_t s_dcdc_gpio_map[DCDC_CHANNEL_COUNT] = {
|
static const gpio_num_t s_dcdc_gpio_map[DCDC_CHANNEL_COUNT] = {
|
||||||
GPIO_NUM_2,
|
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_0,
|
||||||
GPIO_NUM_4,
|
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_1,
|
||||||
GPIO_NUM_5,
|
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_2,
|
||||||
GPIO_NUM_18,
|
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_3,
|
||||||
GPIO_NUM_19,
|
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_4,
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool s_initialized;
|
static bool s_initialized;
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* Developed by TComLab
|
||||||
|
* Version: v0.1
|
||||||
|
* Date: 2025-12-15
|
||||||
|
*/
|
||||||
|
|
||||||
#include "ina226_monitor.h"
|
#include "ina226_monitor.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
@@ -23,10 +29,10 @@
|
|||||||
#define CONFIG_WATCH_INA226_I2C_FREQ_HZ 400000
|
#define CONFIG_WATCH_INA226_I2C_FREQ_HZ 400000
|
||||||
#endif
|
#endif
|
||||||
#ifndef CONFIG_WATCH_INA226_CURRENT_LSB_uA
|
#ifndef CONFIG_WATCH_INA226_CURRENT_LSB_uA
|
||||||
#define CONFIG_WATCH_INA226_CURRENT_LSB_uA 100
|
#define CONFIG_WATCH_INA226_CURRENT_LSB_uA 50
|
||||||
#endif
|
#endif
|
||||||
#ifndef CONFIG_WATCH_INA226_SHUNT_MILLIOHM
|
#ifndef CONFIG_WATCH_INA226_SHUNT_MILLIOHM
|
||||||
#define CONFIG_WATCH_INA226_SHUNT_MILLIOHM 10
|
#define CONFIG_WATCH_INA226_SHUNT_MILLIOHM 100
|
||||||
#endif
|
#endif
|
||||||
#ifndef CONFIG_WATCH_INA226_ADDR
|
#ifndef CONFIG_WATCH_INA226_ADDR
|
||||||
#define CONFIG_WATCH_INA226_ADDR 0x40
|
#define CONFIG_WATCH_INA226_ADDR 0x40
|
||||||
@@ -45,11 +51,13 @@
|
|||||||
#if CONFIG_WATCH_INA226_ENABLED
|
#if CONFIG_WATCH_INA226_ENABLED
|
||||||
static const char *TAG = "ina226";
|
static const char *TAG = "ina226";
|
||||||
static bool s_initialized;
|
static bool s_initialized;
|
||||||
static float s_current_lsb_ma;
|
static float s_current_lsb_a;
|
||||||
static uint8_t s_address = CONFIG_WATCH_INA226_ADDR;
|
static uint8_t s_address = CONFIG_WATCH_INA226_ADDR;
|
||||||
static ina226_reading_t s_last_reading;
|
static ina226_reading_t s_last_reading;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Ініціалізує контролер INA226: налаштовує I2C, записує конфігурацію та
|
||||||
|
// калібрувальні значення, а також зберігає початковий стан вимірювань.
|
||||||
esp_err_t ina226_monitor_init(void)
|
esp_err_t ina226_monitor_init(void)
|
||||||
{
|
{
|
||||||
#if !CONFIG_WATCH_INA226_ENABLED
|
#if !CONFIG_WATCH_INA226_ENABLED
|
||||||
@@ -76,7 +84,7 @@ esp_err_t ina226_monitor_init(void)
|
|||||||
double calibration = 0.00512 / (current_lsb_a * shunt_ohms);
|
double calibration = 0.00512 / (current_lsb_a * shunt_ohms);
|
||||||
if (calibration > 0xFFFF) calibration = 0xFFFF;
|
if (calibration > 0xFFFF) calibration = 0xFFFF;
|
||||||
uint16_t calibration_value = (uint16_t)calibration;
|
uint16_t calibration_value = (uint16_t)calibration;
|
||||||
s_current_lsb_ma = (float)current_lsb_a * 1000.0f;
|
s_current_lsb_a = (float)current_lsb_a;
|
||||||
|
|
||||||
const uint16_t config_value = INA226_CONFIG_AVG_16 |
|
const uint16_t config_value = INA226_CONFIG_AVG_16 |
|
||||||
INA226_CONFIG_VBUS_1100US |
|
INA226_CONFIG_VBUS_1100US |
|
||||||
@@ -103,6 +111,7 @@ esp_err_t ina226_monitor_init(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Дозволяє швидко перевірити, чи був модуль INA226 успішно ініціалізований.
|
||||||
bool ina226_monitor_ready(void)
|
bool ina226_monitor_ready(void)
|
||||||
{
|
{
|
||||||
#if CONFIG_WATCH_INA226_ENABLED
|
#if CONFIG_WATCH_INA226_ENABLED
|
||||||
@@ -112,12 +121,14 @@ bool ina226_monitor_ready(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// INA226 вимірює єдиний канал живлення, тому повертаємо 1.
|
||||||
size_t ina226_monitor_channel_count(void)
|
size_t ina226_monitor_channel_count(void)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CONFIG_WATCH_INA226_ENABLED
|
#if CONFIG_WATCH_INA226_ENABLED
|
||||||
|
// Зчитує 16-бітний регістр INA226, використовуючи транзакцію write-then-read.
|
||||||
static esp_err_t ina226_read_register(uint8_t reg, uint16_t *out_value)
|
static esp_err_t ina226_read_register(uint8_t reg, uint16_t *out_value)
|
||||||
{
|
{
|
||||||
uint8_t value[2];
|
uint8_t value[2];
|
||||||
@@ -132,6 +143,7 @@ static esp_err_t ina226_read_register(uint8_t reg, uint16_t *out_value)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Виконує одне вимірювання напруги/струму через INA226 та кешує результат.
|
||||||
esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading)
|
esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading)
|
||||||
{
|
{
|
||||||
#if !CONFIG_WATCH_INA226_ENABLED
|
#if !CONFIG_WATCH_INA226_ENABLED
|
||||||
@@ -147,13 +159,13 @@ esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading)
|
|||||||
ESP_RETURN_ON_ERROR(ina226_read_register(INA226_REG_CURRENT, ¤t_raw), TAG, "current read failed");
|
ESP_RETURN_ON_ERROR(ina226_read_register(INA226_REG_CURRENT, ¤t_raw), TAG, "current read failed");
|
||||||
|
|
||||||
float voltage_v = (float)bus_raw * 1.25f / 1000.0f;
|
float voltage_v = (float)bus_raw * 1.25f / 1000.0f;
|
||||||
float current_ma = (int16_t)current_raw * s_current_lsb_ma;
|
float current_a = (int16_t)current_raw * s_current_lsb_a;
|
||||||
float power_mw = voltage_v * current_ma;
|
float power_w = voltage_v * current_a;
|
||||||
|
|
||||||
s_last_reading = (ina226_reading_t){
|
s_last_reading = (ina226_reading_t){
|
||||||
.voltage_v = voltage_v,
|
.voltage_v = voltage_v,
|
||||||
.current_ma = current_ma,
|
.current_a = current_a,
|
||||||
.power_mw = power_mw,
|
.power_w = power_w,
|
||||||
};
|
};
|
||||||
if (out_reading) {
|
if (out_reading) {
|
||||||
*out_reading = s_last_reading;
|
*out_reading = s_last_reading;
|
||||||
@@ -162,6 +174,7 @@ esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Повертає останній виміряний набір даних без нового звернення до шини I2C.
|
||||||
const ina226_reading_t *ina226_monitor_get_last(void)
|
const ina226_reading_t *ina226_monitor_get_last(void)
|
||||||
{
|
{
|
||||||
#if CONFIG_WATCH_INA226_ENABLED
|
#if CONFIG_WATCH_INA226_ENABLED
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* Developed by TComLab
|
||||||
|
* Version: v0.1
|
||||||
|
* Date: 2025-12-15
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -7,12 +13,17 @@
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
float voltage_v;
|
float voltage_v;
|
||||||
float current_ma;
|
float current_a;
|
||||||
float power_mw;
|
float power_w;
|
||||||
} ina226_reading_t;
|
} ina226_reading_t;
|
||||||
|
|
||||||
|
// Ініціалізує INA226: конфігурує I2C та калібрує вимірювач.
|
||||||
esp_err_t ina226_monitor_init(void);
|
esp_err_t ina226_monitor_init(void);
|
||||||
|
// true, якщо драйвер вже ініціалізований.
|
||||||
bool ina226_monitor_ready(void);
|
bool ina226_monitor_ready(void);
|
||||||
|
// INA226 підтримує один канал, але інтерфейс залишається узагальненим.
|
||||||
size_t ina226_monitor_channel_count(void);
|
size_t ina226_monitor_channel_count(void);
|
||||||
|
// Зчитує нові дані; out_reading може бути NULL, якщо дані не потрібні.
|
||||||
esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading);
|
esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading);
|
||||||
|
// Повертає кеш останнього вимірювання або NULL, якщо модуль неактивний.
|
||||||
const ina226_reading_t *ina226_monitor_get_last(void);
|
const ina226_reading_t *ina226_monitor_get_last(void);
|
||||||
|
|||||||
168
main/main.c
168
main/main.c
@@ -1,30 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Developed by TComLab
|
||||||
|
* Version: v0.1
|
||||||
|
* Date: 2025-12-15
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
#include "dcdc_controller.h"
|
#include "dcdc_controller.h"
|
||||||
#include "ina226_monitor.h"
|
#include "ina226_monitor.h"
|
||||||
#include "uart_mux.h"
|
#include "uart_mux.h"
|
||||||
#include "usb_cdc_cli.h"
|
#include "usb_cdc_cli.h"
|
||||||
|
#include "usb_cdc_log.h"
|
||||||
|
#include "watch_config.h"
|
||||||
#include "ws2812_status.h"
|
#include "ws2812_status.h"
|
||||||
|
|
||||||
static const char *TAG = "watch-watch";
|
static const char *TAG = "watch-watch";
|
||||||
|
static const char HB_MESSAGE[] = "{\"cmd\":\"status\"}\r\n";
|
||||||
|
|
||||||
|
static int noop_vprintf(const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
(void)fmt;
|
||||||
|
(void)args;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void app_main(void)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
|
esp_log_set_vprintf(noop_vprintf);
|
||||||
|
|
||||||
|
if (watch_config_init() != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Не вдалося ініціалізувати конфігурацію");
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "Конфігурацію завантажено");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usb_cdc_cli_init() == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "USB CDC CLI активовано");
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Не вдалося запустити USB CLI");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ws_ready = false;
|
||||||
|
if (ws2812_status_init() == ESP_OK) {
|
||||||
|
ws_ready = true;
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "WS2812 статусний індикатор недоступний");
|
||||||
|
}
|
||||||
|
|
||||||
|
const watch_config_t *cfg = watch_config_get();
|
||||||
|
const uint32_t start_delay_ms = cfg->heartbeat_start_delay_sec * 1000U;
|
||||||
|
TickType_t start_delay = pdMS_TO_TICKS(start_delay_ms);
|
||||||
|
if (start_delay > 0) {
|
||||||
|
ESP_LOGI(TAG, "Очікування %u с перед стартом опитування", cfg->heartbeat_start_delay_sec);
|
||||||
|
if (ws_ready) {
|
||||||
|
ws2812_status_set_startup_hold(start_delay_ms);
|
||||||
|
}
|
||||||
|
vTaskDelay(start_delay);
|
||||||
|
if (ws_ready) {
|
||||||
|
ws2812_status_set_startup_hold(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Запуск watch-watch systems");
|
||||||
if (dcdc_init() != ESP_OK) {
|
if (dcdc_init() != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Помилка ініціалізації DCDC контролера");
|
ESP_LOGE(TAG, "Помилка ініціалізації DCDC контролера");
|
||||||
ws2812_status_init();
|
if (ws_ready) {
|
||||||
ws2812_status_set_error(true);
|
ws2812_status_set_error(true);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ws2812_status_init() == ESP_OK) {
|
if (ws_ready) {
|
||||||
ws2812_status_refresh_from_dcdc();
|
ws2812_status_refresh_from_dcdc();
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "WS2812 статусний індикатор недоступний");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ina226_monitor_init() == ESP_OK) {
|
if (ina226_monitor_init() == ESP_OK) {
|
||||||
@@ -40,43 +94,97 @@ void app_main(void)
|
|||||||
ESP_LOGW(TAG, "UART мультиплексор недоступний");
|
ESP_LOGW(TAG, "UART мультиплексор недоступний");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usb_cdc_cli_init() != ESP_OK) {
|
const TickType_t mux_timeout = pdMS_TO_TICKS(600);
|
||||||
ESP_LOGW(TAG, "USB CDC CLI недоступний");
|
TickType_t channel_next_hb[DCDC_CHANNEL_COUNT] = {0};
|
||||||
} else {
|
bool channel_powered[DCDC_CHANNEL_COUNT] = {false};
|
||||||
ESP_LOGI(TAG, "USB CDC CLI запущено");
|
const TickType_t power_on_stagger = pdMS_TO_TICKS(3000);
|
||||||
}
|
ESP_LOGI(TAG, "Початок циклічного опитування всіх каналів");
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Початок послідовного ввімкнення каналів з інтервалом 4 с");
|
|
||||||
const TickType_t on_time = pdMS_TO_TICKS(4000);
|
|
||||||
|
|
||||||
size_t prev_channel = DCDC_CHANNEL_COUNT - 1;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
for (size_t ch = 0; ch < dcdc_channel_count(); ++ch) {
|
cfg = watch_config_get();
|
||||||
if (prev_channel != ch) {
|
TickType_t hb_period = pdMS_TO_TICKS(cfg->heartbeat_period_sec * 1000U);
|
||||||
dcdc_disable(prev_channel);
|
if (hb_period == 0) {
|
||||||
ws2812_status_set_channel_state(prev_channel, false);
|
hb_period = 1;
|
||||||
|
}
|
||||||
|
TickType_t now = xTaskGetTickCount();
|
||||||
|
size_t channels = dcdc_channel_count();
|
||||||
|
for (size_t ch = 0; ch < channels; ++ch) {
|
||||||
|
bool powered = dcdc_get_state(ch);
|
||||||
|
if (!powered) {
|
||||||
|
channel_powered[ch] = false;
|
||||||
|
ESP_LOGI(TAG, "-> Ввімкнення каналу %d", (int)ch);
|
||||||
|
if (dcdc_enable(ch) == ESP_OK) {
|
||||||
|
powered = true;
|
||||||
|
channel_powered[ch] = true;
|
||||||
|
channel_next_hb[ch] = now + hb_period;
|
||||||
|
if (ws_ready) {
|
||||||
|
ws2812_status_set_channel_state(ch, true);
|
||||||
|
}
|
||||||
|
vTaskDelay(power_on_stagger);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Не вдалося ввімкнути канал %d", (int)ch);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "-> Ввімкнення каналу %d", (int)ch);
|
if (!channel_powered[ch]) {
|
||||||
dcdc_enable(ch);
|
channel_powered[ch] = true;
|
||||||
ws2812_status_set_channel_state(ch, true);
|
channel_next_hb[ch] = now + hb_period;
|
||||||
ws2812_status_mark_active(ch);
|
continue;
|
||||||
vTaskDelay(on_time);
|
}
|
||||||
|
|
||||||
|
now = xTaskGetTickCount();
|
||||||
|
if (now < channel_next_hb[ch]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ina226_reading_t reading = {0};
|
ina226_reading_t reading = {0};
|
||||||
if (ina226_monitor_sample(&reading) == ESP_OK) {
|
if (ina226_monitor_ready() && ina226_monitor_sample(&reading) == ESP_OK) {
|
||||||
ESP_LOGI(TAG, "Живлення: %.2f В, %.1f мА, %.1f мВт",
|
ESP_LOGI(TAG, "Живлення: %.2f В, %.2f А, %.2f Вт",
|
||||||
reading.voltage_v, reading.current_ma, reading.power_mw);
|
reading.voltage_v, reading.current_a, reading.power_w);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uart_mux_ready()) {
|
if (uart_mux_ready()) {
|
||||||
char msg[64];
|
esp_err_t tx_err = uart_mux_write(ch,
|
||||||
int len = snprintf(msg, sizeof(msg), "PWR %.2fV %.0fmA\r\n",
|
(const uint8_t *)HB_MESSAGE,
|
||||||
reading.voltage_v, reading.current_ma);
|
sizeof(HB_MESSAGE) - 1,
|
||||||
uart_mux_write(ch, (const uint8_t *)msg, len, pdMS_TO_TICKS(100));
|
mux_timeout);
|
||||||
|
if (tx_err != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "Не вдалося надіслати heartbeat на канал %d: %s",
|
||||||
|
(int)ch, esp_err_to_name(tx_err));
|
||||||
|
} else {
|
||||||
|
uint8_t rx_buffer[CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN];
|
||||||
|
size_t received = 0;
|
||||||
|
esp_err_t rx_err = uart_mux_read(ch,
|
||||||
|
rx_buffer,
|
||||||
|
sizeof(rx_buffer),
|
||||||
|
&received,
|
||||||
|
mux_timeout);
|
||||||
|
if (rx_err == ESP_OK) {
|
||||||
|
if (received > 0) {
|
||||||
|
ESP_LOGI(TAG, "RX CH%u (%u байт після heartbeat)", (unsigned)ch, (unsigned)received);
|
||||||
|
ESP_LOG_BUFFER_HEX_LEVEL(TAG, rx_buffer, received, ESP_LOG_INFO);
|
||||||
|
uart_mux_process_rx(ch, rx_buffer, received);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Канал %d не відповів даними на heartbeat", (int)ch);
|
||||||
|
uart_mux_report_miss(ch);
|
||||||
|
}
|
||||||
|
} else if (rx_err == ESP_ERR_TIMEOUT) {
|
||||||
|
ESP_LOGW(TAG, "Час очікування відповіді з каналу %d перевищено", (int)ch);
|
||||||
|
uart_mux_report_miss(ch);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Помилка читання відповіді CH%u: %s",
|
||||||
|
(unsigned)ch, esp_err_to_name(rx_err));
|
||||||
|
uart_mux_report_miss(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "UART мультиплексор недоступний, очікування...");
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_channel = ch;
|
channel_next_hb[ch] = now + hb_period;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(50));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
303
main/uart_mux.c
303
main/uart_mux.c
@@ -1,6 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Developed by TComLab
|
||||||
|
* Version: v0.1
|
||||||
|
* Date: 2025-12-15
|
||||||
|
*/
|
||||||
|
|
||||||
#include "uart_mux.h"
|
#include "uart_mux.h"
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
#include "dcdc_controller.h"
|
#include "dcdc_controller.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
@@ -12,6 +20,8 @@
|
|||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
#include "ws2812_status.h"
|
||||||
|
#include "watch_config.h"
|
||||||
|
|
||||||
#ifndef CONFIG_WATCH_UART_MUX_CHANNELS
|
#ifndef CONFIG_WATCH_UART_MUX_CHANNELS
|
||||||
#define CONFIG_WATCH_UART_MUX_CHANNELS 5
|
#define CONFIG_WATCH_UART_MUX_CHANNELS 5
|
||||||
@@ -37,7 +47,171 @@ static size_t s_active_channel = SIZE_MAX;
|
|||||||
static bool s_initialized;
|
static bool s_initialized;
|
||||||
static int64_t s_last_heartbeat_us[UART_MUX_MAX_CHANNELS];
|
static int64_t s_last_heartbeat_us[UART_MUX_MAX_CHANNELS];
|
||||||
static TaskHandle_t s_watchdog_task;
|
static TaskHandle_t s_watchdog_task;
|
||||||
|
static uint8_t s_consecutive_miss[UART_MUX_MAX_CHANNELS];
|
||||||
|
static bool s_watchdog_armed[UART_MUX_MAX_CHANNELS];
|
||||||
|
static uint32_t s_total_miss_count[UART_MUX_MAX_CHANNELS];
|
||||||
|
static uint32_t s_restart_count[UART_MUX_MAX_CHANNELS];
|
||||||
|
|
||||||
|
static uint8_t uart_mux_get_miss_limit_from_config(void);
|
||||||
|
static void uart_mux_restart_channel(size_t channel);
|
||||||
|
static void uart_mux_record_miss(size_t channel, uint8_t miss_limit);
|
||||||
|
|
||||||
|
static TickType_t uart_mux_restart_delay_ticks(void)
|
||||||
|
{
|
||||||
|
const watch_config_t *cfg = watch_config_get();
|
||||||
|
uint32_t sec = cfg ? cfg->dcdc_restart_off_sec : 2U;
|
||||||
|
if (sec == 0) {
|
||||||
|
sec = 1;
|
||||||
|
}
|
||||||
|
return pdMS_TO_TICKS(sec * 1000U);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t uart_mux_get_miss_limit_from_config(void)
|
||||||
|
{
|
||||||
|
const watch_config_t *cfg = watch_config_get();
|
||||||
|
uint32_t limit = cfg ? cfg->heartbeat_miss_limit : 3U;
|
||||||
|
if (limit == 0) {
|
||||||
|
limit = 3U;
|
||||||
|
}
|
||||||
|
if (limit > UINT8_MAX) {
|
||||||
|
limit = UINT8_MAX;
|
||||||
|
}
|
||||||
|
return (uint8_t)limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_mux_restart_channel(size_t channel)
|
||||||
|
{
|
||||||
|
if (channel >= UART_MUX_MAX_CHANNELS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "CH%u: перезапуск живлення після відсутності відповіді", (unsigned)channel);
|
||||||
|
dcdc_disable(channel);
|
||||||
|
vTaskDelay(uart_mux_restart_delay_ticks());
|
||||||
|
dcdc_enable(channel);
|
||||||
|
s_consecutive_miss[channel] = 0;
|
||||||
|
s_watchdog_armed[channel] = false;
|
||||||
|
if (s_restart_count[channel] < UINT32_MAX) {
|
||||||
|
++s_restart_count[channel];
|
||||||
|
}
|
||||||
|
s_last_heartbeat_us[channel] = esp_timer_get_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_mux_record_miss(size_t channel, uint8_t miss_limit)
|
||||||
|
{
|
||||||
|
if (channel >= UART_MUX_MAX_CHANNELS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!dcdc_get_state(channel)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!s_watchdog_armed[channel]) {
|
||||||
|
s_watchdog_armed[channel] = true;
|
||||||
|
s_consecutive_miss[channel] = 0;
|
||||||
|
}
|
||||||
|
if (s_total_miss_count[channel] < UINT32_MAX) {
|
||||||
|
++s_total_miss_count[channel];
|
||||||
|
}
|
||||||
|
if (s_consecutive_miss[channel] < UINT8_MAX) {
|
||||||
|
++s_consecutive_miss[channel];
|
||||||
|
}
|
||||||
|
if (miss_limit == 0) {
|
||||||
|
miss_limit = 1;
|
||||||
|
}
|
||||||
|
if (s_consecutive_miss[channel] >= miss_limit) {
|
||||||
|
uart_mux_restart_channel(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool uart_mux_extract_numeric_field(const uint8_t *data,
|
||||||
|
size_t length,
|
||||||
|
const char *key,
|
||||||
|
int *out_value)
|
||||||
|
{
|
||||||
|
if (!data || !key || !out_value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const size_t key_len = strlen(key);
|
||||||
|
if (key_len == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < length; ++i) {
|
||||||
|
if (data[i] != '"') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size_t j = i + 1;
|
||||||
|
if (j + key_len >= length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (memcmp(&data[j], key, key_len) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
j += key_len;
|
||||||
|
if (j >= length || data[j] != '"') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++j;
|
||||||
|
while (j < length && isspace((unsigned char)data[j])) {
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
if (j >= length || data[j] != ':') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++j;
|
||||||
|
while (j < length && isspace((unsigned char)data[j])) {
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
bool negative = false;
|
||||||
|
if (j < length && (data[j] == '-' || data[j] == '+')) {
|
||||||
|
negative = (data[j] == '-');
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
bool has_digit = false;
|
||||||
|
int value = 0;
|
||||||
|
while (j < length && isdigit((unsigned char)data[j])) {
|
||||||
|
has_digit = true;
|
||||||
|
value = value * 10 + (data[j] - '0');
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
if (has_digit) {
|
||||||
|
*out_value = negative ? -value : value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_mux_decode_status_payload(const uint8_t *data,
|
||||||
|
size_t length,
|
||||||
|
bool *hb_ack,
|
||||||
|
bool *vpn_ok,
|
||||||
|
bool *app_ok)
|
||||||
|
{
|
||||||
|
int value = 0;
|
||||||
|
if (hb_ack) {
|
||||||
|
bool found = uart_mux_extract_numeric_field(data, length, "hb", &value);
|
||||||
|
*hb_ack = found && value == 2;
|
||||||
|
}
|
||||||
|
if (vpn_ok) {
|
||||||
|
bool found = uart_mux_extract_numeric_field(data, length, "VPN", &value);
|
||||||
|
*vpn_ok = found && value != 0;
|
||||||
|
}
|
||||||
|
if (app_ok) {
|
||||||
|
bool found = uart_mux_extract_numeric_field(data, length, "APP", &value);
|
||||||
|
*app_ok = found && value != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Перевіряє, чи містить буфер відповідь {"hb":2} від Raspberry Pi.
|
||||||
|
static bool uart_mux_contains_hb_ack(const uint8_t *data, size_t length)
|
||||||
|
{
|
||||||
|
bool ack = false;
|
||||||
|
uart_mux_decode_status_payload(data, length, &ack, NULL, NULL);
|
||||||
|
return ack;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Перемикає апаратний мультиплексор на вказаний канал під захистом мьютекса,
|
||||||
|
// оновлюючи таймстемп останнього heartbeat для контролю watchdog.
|
||||||
static esp_err_t uart_mux_select_locked(size_t channel)
|
static esp_err_t uart_mux_select_locked(size_t channel)
|
||||||
{
|
{
|
||||||
if (channel >= UART_MUX_MAX_CHANNELS) {
|
if (channel >= UART_MUX_MAX_CHANNELS) {
|
||||||
@@ -56,17 +230,34 @@ static esp_err_t uart_mux_select_locked(size_t channel)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Періодично опитує всі канали, щоб зчитати heartbeat та перезапускає DCDC,
|
||||||
|
// якщо канал «мовчить» довше за CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC.
|
||||||
static void uart_mux_watchdog_task(void *arg)
|
static void uart_mux_watchdog_task(void *arg)
|
||||||
{
|
{
|
||||||
const TickType_t poll_interval = pdMS_TO_TICKS(1000);
|
const TickType_t poll_interval = pdMS_TO_TICKS(10000);
|
||||||
const TickType_t read_timeout = pdMS_TO_TICKS(10);
|
const TickType_t read_timeout = pdMS_TO_TICKS(300);
|
||||||
const int64_t timeout_us = (int64_t)CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC * 1000000LL;
|
const int64_t timeout_us = (int64_t)CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC * 1000000LL;
|
||||||
uint8_t buffer[CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN];
|
uint8_t buffer[CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN];
|
||||||
|
const watch_config_t *cfg = watch_config_get();
|
||||||
|
TickType_t start_delay = pdMS_TO_TICKS(cfg->heartbeat_start_delay_sec * 1000U);
|
||||||
|
if (start_delay > 0) {
|
||||||
|
vTaskDelay(start_delay);
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
vTaskDelay(poll_interval);
|
vTaskDelay(poll_interval);
|
||||||
|
const watch_config_t *cfg = watch_config_get();
|
||||||
|
if (!cfg->heartbeat_monitor_enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint8_t miss_limit = uart_mux_get_miss_limit_from_config();
|
||||||
int64_t now = esp_timer_get_time();
|
int64_t now = esp_timer_get_time();
|
||||||
for (size_t ch = 0; ch < UART_MUX_MAX_CHANNELS; ++ch) {
|
for (size_t ch = 0; ch < UART_MUX_MAX_CHANNELS; ++ch) {
|
||||||
|
if (!dcdc_get_state(ch)) {
|
||||||
|
s_watchdog_armed[ch] = false;
|
||||||
|
s_consecutive_miss[ch] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(20)) == pdTRUE) {
|
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(20)) == pdTRUE) {
|
||||||
if (uart_mux_select_locked(ch) == ESP_OK) {
|
if (uart_mux_select_locked(ch) == ESP_OK) {
|
||||||
int read = uart_read_bytes(CONFIG_WATCH_UART_PORT,
|
int read = uart_read_bytes(CONFIG_WATCH_UART_PORT,
|
||||||
@@ -74,19 +265,37 @@ static void uart_mux_watchdog_task(void *arg)
|
|||||||
sizeof(buffer),
|
sizeof(buffer),
|
||||||
read_timeout);
|
read_timeout);
|
||||||
if (read > 0) {
|
if (read > 0) {
|
||||||
|
bool ack = false;
|
||||||
|
bool vpn_ok = false;
|
||||||
|
bool app_ok = false;
|
||||||
|
uart_mux_decode_status_payload(buffer, read, &ack, &vpn_ok, &app_ok);
|
||||||
|
ESP_LOGI(TAG, "UART0 RX CH%u (%d байт)%s",
|
||||||
|
(unsigned)ch, read, ack ? " [HB ACK]" : "");
|
||||||
|
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, read, ESP_LOG_INFO);
|
||||||
|
if (ack) {
|
||||||
s_last_heartbeat_us[ch] = now;
|
s_last_heartbeat_us[ch] = now;
|
||||||
|
s_consecutive_miss[ch] = 0;
|
||||||
|
s_watchdog_armed[ch] = true;
|
||||||
|
ws2812_status_set_service_state(ch, vpn_ok, app_ok);
|
||||||
|
} else {
|
||||||
|
uart_mux_record_miss(ch, miss_limit);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uart_mux_record_miss(ch, miss_limit);
|
||||||
|
ESP_LOGD(TAG, "UART0 RX CH%u: немає відповіді у watchdog (%u)",
|
||||||
|
(unsigned)ch, (unsigned)s_consecutive_miss[ch]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xSemaphoreGive(s_mutex);
|
xSemaphoreGive(s_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dcdc_get_state(ch) && s_watchdog_armed[ch] && s_consecutive_miss[ch] >= miss_limit) {
|
||||||
|
uart_mux_restart_channel(ch);
|
||||||
|
}
|
||||||
|
|
||||||
if (dcdc_get_state(ch) && s_last_heartbeat_us[ch] > 0 &&
|
if (dcdc_get_state(ch) && s_last_heartbeat_us[ch] > 0 &&
|
||||||
(now - s_last_heartbeat_us[ch]) > timeout_us) {
|
(now - s_last_heartbeat_us[ch]) > timeout_us) {
|
||||||
ESP_LOGW(TAG, "Heartbeat каналу %u втрачено, перезавантаження...", (unsigned)ch);
|
uart_mux_restart_channel(ch);
|
||||||
dcdc_disable(ch);
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
|
||||||
dcdc_enable(ch);
|
|
||||||
s_last_heartbeat_us[ch] = esp_timer_get_time();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,6 +303,8 @@ static void uart_mux_watchdog_task(void *arg)
|
|||||||
|
|
||||||
#endif // CONFIG_WATCH_UART_MUX_ENABLED
|
#endif // CONFIG_WATCH_UART_MUX_ENABLED
|
||||||
|
|
||||||
|
// Налаштовує GPIO-вибірники, драйвер UART та створює watchdog-задачу для
|
||||||
|
// мультиплексора; повторний виклик просто повертає ESP_OK.
|
||||||
esp_err_t uart_mux_init(void)
|
esp_err_t uart_mux_init(void)
|
||||||
{
|
{
|
||||||
#if !CONFIG_WATCH_UART_MUX_ENABLED
|
#if !CONFIG_WATCH_UART_MUX_ENABLED
|
||||||
@@ -144,6 +355,10 @@ esp_err_t uart_mux_init(void)
|
|||||||
int64_t now = esp_timer_get_time();
|
int64_t now = esp_timer_get_time();
|
||||||
for (size_t ch = 0; ch < UART_MUX_MAX_CHANNELS; ++ch) {
|
for (size_t ch = 0; ch < UART_MUX_MAX_CHANNELS; ++ch) {
|
||||||
s_last_heartbeat_us[ch] = now;
|
s_last_heartbeat_us[ch] = now;
|
||||||
|
s_consecutive_miss[ch] = 0;
|
||||||
|
s_watchdog_armed[ch] = false;
|
||||||
|
s_total_miss_count[ch] = 0;
|
||||||
|
s_restart_count[ch] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xTaskCreate(uart_mux_watchdog_task, "uart_mux_wd", 4096, NULL, 5, &s_watchdog_task) != pdPASS) {
|
if (xTaskCreate(uart_mux_watchdog_task, "uart_mux_wd", 4096, NULL, 5, &s_watchdog_task) != pdPASS) {
|
||||||
@@ -156,6 +371,7 @@ esp_err_t uart_mux_init(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Повертає ознаку ініціалізації модулю UART мультиплексора.
|
||||||
bool uart_mux_ready(void)
|
bool uart_mux_ready(void)
|
||||||
{
|
{
|
||||||
#if CONFIG_WATCH_UART_MUX_ENABLED
|
#if CONFIG_WATCH_UART_MUX_ENABLED
|
||||||
@@ -164,12 +380,14 @@ bool uart_mux_ready(void)
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
// Кількість доступних каналів, визначених у конфігурації.
|
||||||
size_t uart_mux_channel_count(void)
|
size_t uart_mux_channel_count(void)
|
||||||
{
|
{
|
||||||
return CONFIG_WATCH_UART_MUX_CHANNELS;
|
return CONFIG_WATCH_UART_MUX_CHANNELS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Перемикається на канал, передає буфер даних через загальний UART та
|
||||||
|
// захищає доступ до шини мьютексом, щоб уникнути гонок між задачами.
|
||||||
esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout)
|
esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout)
|
||||||
{
|
{
|
||||||
#if !CONFIG_WATCH_UART_MUX_ENABLED
|
#if !CONFIG_WATCH_UART_MUX_ENABLED
|
||||||
@@ -192,6 +410,10 @@ esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, Tic
|
|||||||
int written = uart_write_bytes(CONFIG_WATCH_UART_PORT, (const char *)data, length);
|
int written = uart_write_bytes(CONFIG_WATCH_UART_PORT, (const char *)data, length);
|
||||||
if (written < 0 || (size_t)written != length) {
|
if (written < 0 || (size_t)written != length) {
|
||||||
err = ESP_FAIL;
|
err = ESP_FAIL;
|
||||||
|
} else {
|
||||||
|
if (uart_wait_tx_done(CONFIG_WATCH_UART_PORT, timeout) != ESP_OK) {
|
||||||
|
err = ESP_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xSemaphoreGive(s_mutex);
|
xSemaphoreGive(s_mutex);
|
||||||
@@ -199,6 +421,8 @@ esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, Tic
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Читає дані з вказаного каналу, оновлюючи heartbeat під час успішного
|
||||||
|
// зчитування, і повертає кількість байтів через out_length.
|
||||||
esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout)
|
esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout)
|
||||||
{
|
{
|
||||||
#if !CONFIG_WATCH_UART_MUX_ENABLED
|
#if !CONFIG_WATCH_UART_MUX_ENABLED
|
||||||
@@ -231,3 +455,66 @@ esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, siz
|
|||||||
return err;
|
return err;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uart_mux_process_rx(size_t channel, const uint8_t *data, size_t length)
|
||||||
|
{
|
||||||
|
#if CONFIG_WATCH_UART_MUX_ENABLED
|
||||||
|
if (!s_initialized || channel >= UART_MUX_MAX_CHANNELS || !data || length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool ack = false;
|
||||||
|
bool vpn_ok = false;
|
||||||
|
bool app_ok = false;
|
||||||
|
uart_mux_decode_status_payload(data, length, &ack, &vpn_ok, &app_ok);
|
||||||
|
int64_t now = esp_timer_get_time();
|
||||||
|
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||||
|
s_last_heartbeat_us[channel] = now;
|
||||||
|
if (ack) {
|
||||||
|
s_consecutive_miss[channel] = 0;
|
||||||
|
s_watchdog_armed[channel] = true;
|
||||||
|
}
|
||||||
|
xSemaphoreGive(s_mutex);
|
||||||
|
}
|
||||||
|
if (ack) {
|
||||||
|
ws2812_status_set_service_state(channel, vpn_ok, app_ok);
|
||||||
|
} else {
|
||||||
|
uart_mux_record_miss(channel, uart_mux_get_miss_limit_from_config());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)channel;
|
||||||
|
(void)data;
|
||||||
|
(void)length;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_mux_report_miss(size_t channel)
|
||||||
|
{
|
||||||
|
#if CONFIG_WATCH_UART_MUX_ENABLED
|
||||||
|
if (!s_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uart_mux_record_miss(channel, uart_mux_get_miss_limit_from_config());
|
||||||
|
#else
|
||||||
|
(void)channel;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_mux_get_channel_stats(size_t channel, uart_mux_channel_stats_t *out_stats)
|
||||||
|
{
|
||||||
|
if (!out_stats) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if CONFIG_WATCH_UART_MUX_ENABLED
|
||||||
|
if (!s_initialized || channel >= UART_MUX_MAX_CHANNELS) {
|
||||||
|
out_stats->missed_heartbeats = 0;
|
||||||
|
out_stats->restart_count = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out_stats->missed_heartbeats = s_total_miss_count[channel];
|
||||||
|
out_stats->restart_count = s_restart_count[channel];
|
||||||
|
#else
|
||||||
|
(void)channel;
|
||||||
|
out_stats->missed_heartbeats = 0;
|
||||||
|
out_stats->restart_count = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Developed by TComLab
|
||||||
|
* Version: v0.1
|
||||||
|
* Date: 2025-12-15
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t missed_heartbeats;
|
||||||
|
uint32_t restart_count;
|
||||||
|
} uart_mux_channel_stats_t;
|
||||||
|
|
||||||
|
// Ініціалізує апаратний мультиплексор UART: GPIO, UART драйвер та watchdog.
|
||||||
esp_err_t uart_mux_init(void);
|
esp_err_t uart_mux_init(void);
|
||||||
|
// Повертає true, якщо драйвер готовий приймати виклики.
|
||||||
bool uart_mux_ready(void);
|
bool uart_mux_ready(void);
|
||||||
|
// Кількість доступних каналів мультиплексора.
|
||||||
size_t uart_mux_channel_count(void);
|
size_t uart_mux_channel_count(void);
|
||||||
|
// Відправляє буфер даних на вказаний канал з тайм-аутом очікування мьютекса.
|
||||||
esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout);
|
esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout);
|
||||||
|
// Зчитує дані з каналу, повертаючи кількість байтів через out_length.
|
||||||
esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout);
|
esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout);
|
||||||
|
// Повідомляє драйверу про дані, зчитані поза watchdog-ом (для оновлення станів).
|
||||||
|
void uart_mux_process_rx(size_t channel, const uint8_t *data, size_t length);
|
||||||
|
// Повідомляє про пропущений heartbeat (використовується основним циклом).
|
||||||
|
void uart_mux_report_miss(size_t channel);
|
||||||
|
// Повертає статистику по каналу (кількість пропущених відповідей та рестартів).
|
||||||
|
void uart_mux_get_channel_stats(size_t channel, uart_mux_channel_stats_t *out_stats);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "dcdc_controller.h"
|
#include "dcdc_controller.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "esp_system.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
@@ -18,8 +19,12 @@
|
|||||||
#include "tinyusb.h"
|
#include "tinyusb.h"
|
||||||
#include "tusb_cdc_acm.h"
|
#include "tusb_cdc_acm.h"
|
||||||
#include "ws2812_status.h"
|
#include "ws2812_status.h"
|
||||||
|
#include "watch_config.h"
|
||||||
|
#include "esp_system.h"
|
||||||
#include "ina226_monitor.h"
|
#include "ina226_monitor.h"
|
||||||
#include "uart_mux.h"
|
#include "uart_mux.h"
|
||||||
|
#include "soc/rtc_cntl_reg.h"
|
||||||
|
#include "soc/soc.h"
|
||||||
|
|
||||||
#define CLI_LINE_MAX_LEN 128
|
#define CLI_LINE_MAX_LEN 128
|
||||||
#define CLI_QUEUE_LEN 8
|
#define CLI_QUEUE_LEN 8
|
||||||
@@ -45,6 +50,10 @@ static bool s_host_ready;
|
|||||||
static void usb_cli_task(void *arg);
|
static void usb_cli_task(void *arg);
|
||||||
static void usb_cli_process_line(char *line);
|
static void usb_cli_process_line(char *line);
|
||||||
static void usb_cli_prompt(void);
|
static void usb_cli_prompt(void);
|
||||||
|
static void usb_cli_enter_bootloader(void);
|
||||||
|
static void usb_cli_handle_config(char *args);
|
||||||
|
static void usb_cli_print_config(void);
|
||||||
|
static void usb_cli_handle_reset(void);
|
||||||
|
|
||||||
static void usb_cli_write_raw(const char *data, size_t len)
|
static void usb_cli_write_raw(const char *data, size_t len)
|
||||||
{
|
{
|
||||||
@@ -112,10 +121,16 @@ static void usb_cli_print_status(void)
|
|||||||
usb_cli_printf("\r\nСтан каналів:\r\n");
|
usb_cli_printf("\r\nСтан каналів:\r\n");
|
||||||
for (size_t i = 0; i < dcdc_channel_count(); ++i) {
|
for (size_t i = 0; i < dcdc_channel_count(); ++i) {
|
||||||
dcdc_channel_t ch = (dcdc_channel_t)i;
|
dcdc_channel_t ch = (dcdc_channel_t)i;
|
||||||
usb_cli_printf(" - CH%u: %s (GPIO %d)\r\n",
|
uart_mux_channel_stats_t stats = {0};
|
||||||
|
if (uart_mux_ready()) {
|
||||||
|
uart_mux_get_channel_stats(i, &stats);
|
||||||
|
}
|
||||||
|
usb_cli_printf(" - CH%u: %s (GPIO %d) | miss=%u restart=%u\r\n",
|
||||||
(unsigned)i,
|
(unsigned)i,
|
||||||
dcdc_get_state(ch) ? "ON" : "OFF",
|
dcdc_get_state(ch) ? "ON" : "OFF",
|
||||||
(int)dcdc_get_gpio(ch));
|
(int)dcdc_get_gpio(ch),
|
||||||
|
(unsigned)stats.missed_heartbeats,
|
||||||
|
(unsigned)stats.restart_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,6 +139,18 @@ static void usb_cli_prompt(void)
|
|||||||
usb_cli_write_raw(PROMPT, strlen(PROMPT));
|
usb_cli_write_raw(PROMPT, strlen(PROMPT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void usb_cli_enter_bootloader(void)
|
||||||
|
{
|
||||||
|
usb_cli_printf("\r\nПерехід у режим прошивки esptool...\r\n"
|
||||||
|
"Після перезавантаження з’явиться ROM-порт USB CDC/Serial.\r\n"
|
||||||
|
"Запустіть esptool.py або idf.py flash та прошийте пристрій. "
|
||||||
|
"Для виходу з bootloader виконайте 'esptool.py --after hard_reset reset' або перезавантажте живлення.\r\n");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
tinyusb_driver_uninstall();
|
||||||
|
REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT);
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
|
|
||||||
static void usb_cli_handle_switch(const char *cmd, char *args)
|
static void usb_cli_handle_switch(const char *cmd, char *args)
|
||||||
{
|
{
|
||||||
dcdc_channel_t channel;
|
dcdc_channel_t channel;
|
||||||
@@ -154,8 +181,8 @@ static void usb_cli_handle_switch(const char *cmd, char *args)
|
|||||||
|
|
||||||
static void usb_cli_print_measurement(size_t channel, const ina226_reading_t *reading)
|
static void usb_cli_print_measurement(size_t channel, const ina226_reading_t *reading)
|
||||||
{
|
{
|
||||||
usb_cli_printf("\r\nCH%u: %.2f В, %.1f мА, %.1f мВт",
|
usb_cli_printf("\r\nCH%u: %.2f В, %.3f А, %.3f Вт",
|
||||||
(unsigned)channel, reading->voltage_v, reading->current_ma, reading->power_mw);
|
(unsigned)channel, reading->voltage_v, reading->current_a, reading->power_w);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_cli_handle_sense(char *args)
|
static void usb_cli_handle_sense(char *args)
|
||||||
@@ -246,14 +273,22 @@ static void usb_cli_process_line(char *line)
|
|||||||
if (strcasecmp(cmd, "help") == 0) {
|
if (strcasecmp(cmd, "help") == 0) {
|
||||||
usb_cli_printf(
|
usb_cli_printf(
|
||||||
"\r\nДоступні команди:\r\n"
|
"\r\nДоступні команди:\r\n"
|
||||||
" help - показати довідку\r\n"
|
" help - показати цю довідку\r\n"
|
||||||
" status - стан всіх каналів\r\n"
|
" status - показати стан усіх каналів DCDC\r\n"
|
||||||
" enable <n> - увімкнути канал n\r\n"
|
" enable <n> - увімкнути канал n (0..4)\r\n"
|
||||||
" disable <n> - вимкнути канал n\r\n"
|
" disable <n> - вимкнути канал n\r\n"
|
||||||
" toggle <n> - перемкнути канал n\r\n"
|
" toggle <n> - перемкнути канал n\r\n"
|
||||||
" sense [n] - показати напругу/струм/потужність (опц. канал)\r\n"
|
" sense - виміряти напругу/струм/потужність INA226\r\n"
|
||||||
" uart send <n> <msg> - відправити повідомлення Pi n\r\n"
|
" uart send <n> <msg> - надіслати текстове повідомлення в Raspberry Pi n\r\n"
|
||||||
" uart read <n> [len] - прочитати дані з Pi n\r\n");
|
" uart read <n> [len] - прочитати до [len] байт відповіді від Raspberry Pi n\r\n"
|
||||||
|
" config show - показати збережені таймінги heartbeat/DCDC\r\n"
|
||||||
|
" config set hb_period <сек> - змінити інтервал відправки heartbeat\r\n"
|
||||||
|
" config set dcdc_off <сек> - задати тривалість вимкнення DCDC при рестарті\r\n"
|
||||||
|
" config set hb_start <сек> - налаштувати затримку перед стартом опитування після boot\r\n"
|
||||||
|
" config set hb_monitor <0|1> - увімкнути/вимкнути контроль відповіді heartbeat\r\n"
|
||||||
|
" config set hb_miss <шт> - кількість пропусків відповіді до рестарту каналу\r\n"
|
||||||
|
" reset - м'яко перезавантажити ESP32-S3\r\n"
|
||||||
|
" bootloader - перезавантажити ESP32-S3 у ROM bootloader для esptool\r\n");
|
||||||
} else if (strcasecmp(cmd, "status") == 0) {
|
} else if (strcasecmp(cmd, "status") == 0) {
|
||||||
usb_cli_print_status();
|
usb_cli_print_status();
|
||||||
} else if (strcasecmp(cmd, "enable") == 0 ||
|
} else if (strcasecmp(cmd, "enable") == 0 ||
|
||||||
@@ -264,11 +299,109 @@ static void usb_cli_process_line(char *line)
|
|||||||
usb_cli_handle_sense(save_ptr);
|
usb_cli_handle_sense(save_ptr);
|
||||||
} else if (strcasecmp(cmd, "uart") == 0) {
|
} else if (strcasecmp(cmd, "uart") == 0) {
|
||||||
usb_cli_handle_uart(save_ptr);
|
usb_cli_handle_uart(save_ptr);
|
||||||
|
} else if (strcasecmp(cmd, "config") == 0) {
|
||||||
|
usb_cli_handle_config(save_ptr);
|
||||||
|
} else if (strcasecmp(cmd, "reset") == 0) {
|
||||||
|
usb_cli_handle_reset();
|
||||||
|
} else if (strcasecmp(cmd, "bootloader") == 0) {
|
||||||
|
usb_cli_enter_bootloader();
|
||||||
} else {
|
} else {
|
||||||
usb_cli_printf("\r\nНевідома команда '%s'\r\n", cmd);
|
usb_cli_printf("\r\nНевідома команда '%s'\r\n", cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void usb_cli_print_config(void)
|
||||||
|
{
|
||||||
|
const watch_config_t *cfg = watch_config_get();
|
||||||
|
usb_cli_printf(
|
||||||
|
"\r\nПоточні налаштування:\r\n"
|
||||||
|
" hb_period: %u с\r\n"
|
||||||
|
" dcdc_off: %u с\r\n"
|
||||||
|
" hb_start_delay: %u с\r\n"
|
||||||
|
" hb_monitor: %s\r\n"
|
||||||
|
" hb_miss_limit: %u зап.\r\n",
|
||||||
|
(unsigned)cfg->heartbeat_period_sec,
|
||||||
|
(unsigned)cfg->dcdc_restart_off_sec,
|
||||||
|
(unsigned)cfg->heartbeat_start_delay_sec,
|
||||||
|
cfg->heartbeat_monitor_enabled ? "on" : "off",
|
||||||
|
(unsigned)cfg->heartbeat_miss_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_cli_handle_config(char *args)
|
||||||
|
{
|
||||||
|
if (!args || *args == '\0') {
|
||||||
|
usb_cli_print_config();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ctx = NULL;
|
||||||
|
char *action = strtok_r(args, " ", &ctx);
|
||||||
|
if (!action || strcasecmp(action, "show") == 0) {
|
||||||
|
usb_cli_print_config();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcasecmp(action, "set") != 0) {
|
||||||
|
usb_cli_printf("\r\nВикористання: config show | config set <hb_period|dcdc_off|hb_start|hb_monitor|hb_miss> <знач>\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *param = strtok_r(NULL, " ", &ctx);
|
||||||
|
char *value_str = strtok_r(NULL, " ", &ctx);
|
||||||
|
if (!param || !value_str) {
|
||||||
|
usb_cli_printf("\r\nВкажіть параметр і значення в секундах\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t value = (uint32_t)strtoul(value_str, NULL, 10);
|
||||||
|
|
||||||
|
watch_config_t new_cfg = *watch_config_get();
|
||||||
|
if (strcasecmp(param, "hb_period") == 0) {
|
||||||
|
if (value == 0) {
|
||||||
|
usb_cli_printf("\r\nЗначення має бути більше нуля\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new_cfg.heartbeat_period_sec = value;
|
||||||
|
} else if (strcasecmp(param, "dcdc_off") == 0) {
|
||||||
|
if (value == 0) {
|
||||||
|
usb_cli_printf("\r\nЗначення має бути більше нуля\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new_cfg.dcdc_restart_off_sec = value;
|
||||||
|
} else if (strcasecmp(param, "hb_start") == 0 ||
|
||||||
|
strcasecmp(param, "hb_start_delay") == 0) {
|
||||||
|
if (value == 0) {
|
||||||
|
usb_cli_printf("\r\nЗначення має бути більше нуля\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new_cfg.heartbeat_start_delay_sec = value;
|
||||||
|
} else if (strcasecmp(param, "hb_monitor") == 0) {
|
||||||
|
if (value != 0 && value != 1) {
|
||||||
|
usb_cli_printf("\r\nhb_monitor приймає 0 або 1\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new_cfg.heartbeat_monitor_enabled = (value != 0);
|
||||||
|
} else if (strcasecmp(param, "hb_miss") == 0 ||
|
||||||
|
strcasecmp(param, "hb_miss_limit") == 0) {
|
||||||
|
if (value == 0) {
|
||||||
|
usb_cli_printf("\r\nhb_miss має бути більше нуля\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new_cfg.heartbeat_miss_limit = value;
|
||||||
|
} else {
|
||||||
|
usb_cli_printf("\r\nНевідомий параметр '%s'\r\n", param);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = watch_config_save(&new_cfg);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
usb_cli_printf("\r\nПараметр оновлено\r\n");
|
||||||
|
usb_cli_print_config();
|
||||||
|
} else {
|
||||||
|
usb_cli_printf("\r\nПомилка збереження конфігурації: %s\r\n", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void usb_cli_task(void *arg)
|
static void usb_cli_task(void *arg)
|
||||||
{
|
{
|
||||||
usb_cli_msg_t msg;
|
usb_cli_msg_t msg;
|
||||||
@@ -285,14 +418,17 @@ static void usb_cli_task(void *arg)
|
|||||||
usb_cli_process_line(line);
|
usb_cli_process_line(line);
|
||||||
line_len = 0;
|
line_len = 0;
|
||||||
}
|
}
|
||||||
|
usb_cli_write_raw("\r\n", 2);
|
||||||
usb_cli_prompt();
|
usb_cli_prompt();
|
||||||
} else if (ch == 0x7F || ch == '\b') {
|
} else if (ch == 0x7F || ch == '\b') {
|
||||||
if (line_len > 0) {
|
if (line_len > 0) {
|
||||||
line_len--;
|
line_len--;
|
||||||
|
usb_cli_write_raw("\b \b", 3);
|
||||||
}
|
}
|
||||||
} else if (isprint((unsigned char)ch)) {
|
} else if (isprint((unsigned char)ch)) {
|
||||||
if (line_len < CLI_LINE_MAX_LEN - 1) {
|
if (line_len < CLI_LINE_MAX_LEN - 1) {
|
||||||
line[line_len++] = ch;
|
line[line_len++] = ch;
|
||||||
|
usb_cli_write_raw(&ch, 1);
|
||||||
} else {
|
} else {
|
||||||
usb_cli_printf("\r\nРядок занадто довгий\r\n");
|
usb_cli_printf("\r\nРядок занадто довгий\r\n");
|
||||||
line_len = 0;
|
line_len = 0;
|
||||||
@@ -386,3 +522,9 @@ esp_err_t usb_cdc_cli_init(void)
|
|||||||
ESP_LOGI(TAG, "USB CDC CLI ініціалізовано");
|
ESP_LOGI(TAG, "USB CDC CLI ініціалізовано");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
static void usb_cli_handle_reset(void)
|
||||||
|
{
|
||||||
|
usb_cli_printf("\r\nПерезавантаження пристрою...\r\n");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
|
|||||||
66
main/usb_cdc_log.c
Normal file
66
main/usb_cdc_log.c
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#include "usb_cdc_log.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "tinyusb.h"
|
||||||
|
#include "tusb_cdc_acm.h"
|
||||||
|
|
||||||
|
static const char *TAG = "usb_log";
|
||||||
|
|
||||||
|
static bool s_initialized;
|
||||||
|
static bool s_host_ready;
|
||||||
|
|
||||||
|
static void usb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event)
|
||||||
|
{
|
||||||
|
(void)itf;
|
||||||
|
s_host_ready = event->line_state_changed_data.dtr;
|
||||||
|
if (s_host_ready) {
|
||||||
|
const char banner[] = "\r\nwatch-watch log over USB CDC\r\n";
|
||||||
|
tinyusb_cdcacm_write_queue(TINYUSB_CDC_ACM_0, (const uint8_t *)banner, sizeof(banner) - 1);
|
||||||
|
tinyusb_cdcacm_write_flush(TINYUSB_CDC_ACM_0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool usb_cdc_log_ready(void)
|
||||||
|
{
|
||||||
|
return s_host_ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t usb_cdc_log_init(void)
|
||||||
|
{
|
||||||
|
if (s_initialized) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tinyusb_config_t tusb_cfg = {
|
||||||
|
.device_descriptor = NULL,
|
||||||
|
.string_descriptor = NULL,
|
||||||
|
.external_phy = false,
|
||||||
|
#if (TUD_OPT_HIGH_SPEED)
|
||||||
|
.fs_configuration_descriptor = NULL,
|
||||||
|
.hs_configuration_descriptor = NULL,
|
||||||
|
.qualifier_descriptor = NULL,
|
||||||
|
#else
|
||||||
|
.configuration_descriptor = NULL,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(tinyusb_driver_install(&tusb_cfg), TAG, "TinyUSB init failed");
|
||||||
|
|
||||||
|
const tinyusb_config_cdcacm_t acm_cfg = {
|
||||||
|
.usb_dev = TINYUSB_USBDEV_0,
|
||||||
|
.cdc_port = TINYUSB_CDC_ACM_0,
|
||||||
|
.rx_unread_buf_sz = 64,
|
||||||
|
.callback_rx = NULL,
|
||||||
|
.callback_rx_wanted_char = NULL,
|
||||||
|
.callback_line_state_changed = usb_cdc_line_state_changed_callback,
|
||||||
|
.callback_line_coding_changed = NULL,
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(tusb_cdc_acm_init(&acm_cfg), TAG, "CDC init failed");
|
||||||
|
|
||||||
|
s_initialized = true;
|
||||||
|
ESP_LOGI(TAG, "USB CDC лог ініціалізовано (перенаправлення ESP_LOG вимкнено)");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
15
main/usb_cdc_log.h
Normal file
15
main/usb_cdc_log.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ініціалізує TinyUSB CDC та перенаправляє ESP_LOG у USB.
|
||||||
|
*/
|
||||||
|
esp_err_t usb_cdc_log_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Чи встановив хост DTR і готовий приймати лог.
|
||||||
|
*/
|
||||||
|
bool usb_cdc_log_ready(void);
|
||||||
141
main/watch_config.c
Normal file
141
main/watch_config.c
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#include "watch_config.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "nvs.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
|
||||||
|
#define WATCH_CONFIG_NAMESPACE "watchcfg"
|
||||||
|
#define KEY_HB_PERIOD "hb_period"
|
||||||
|
#define KEY_DCDC_OFF "dcdc_off"
|
||||||
|
#define KEY_HB_START "hb_start"
|
||||||
|
#define KEY_HB_MON "hb_mon"
|
||||||
|
#define KEY_HB_MISS "hb_miss"
|
||||||
|
|
||||||
|
#define DEFAULT_HB_PERIOD_SEC 4U
|
||||||
|
#define DEFAULT_DCDC_OFF_SEC 2U
|
||||||
|
#define DEFAULT_HB_START_SEC 2U
|
||||||
|
#define DEFAULT_HB_MISS_LIMIT 3U
|
||||||
|
|
||||||
|
static watch_config_t s_config = {
|
||||||
|
.heartbeat_period_sec = DEFAULT_HB_PERIOD_SEC,
|
||||||
|
.dcdc_restart_off_sec = DEFAULT_DCDC_OFF_SEC,
|
||||||
|
.heartbeat_start_delay_sec = DEFAULT_HB_START_SEC,
|
||||||
|
.heartbeat_monitor_enabled = true,
|
||||||
|
.heartbeat_miss_limit = DEFAULT_HB_MISS_LIMIT,
|
||||||
|
};
|
||||||
|
static const char *TAG = "watch_cfg";
|
||||||
|
static bool s_initialized;
|
||||||
|
|
||||||
|
static void watch_config_apply_defaults(void)
|
||||||
|
{
|
||||||
|
s_config.heartbeat_period_sec = DEFAULT_HB_PERIOD_SEC;
|
||||||
|
s_config.dcdc_restart_off_sec = DEFAULT_DCDC_OFF_SEC;
|
||||||
|
s_config.heartbeat_start_delay_sec = DEFAULT_HB_START_SEC;
|
||||||
|
s_config.heartbeat_monitor_enabled = true;
|
||||||
|
s_config.heartbeat_miss_limit = DEFAULT_HB_MISS_LIMIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void watch_config_clamp(watch_config_t *cfg)
|
||||||
|
{
|
||||||
|
if (!cfg->heartbeat_period_sec) {
|
||||||
|
cfg->heartbeat_period_sec = DEFAULT_HB_PERIOD_SEC;
|
||||||
|
}
|
||||||
|
if (!cfg->dcdc_restart_off_sec) {
|
||||||
|
cfg->dcdc_restart_off_sec = DEFAULT_DCDC_OFF_SEC;
|
||||||
|
}
|
||||||
|
if (!cfg->heartbeat_start_delay_sec) {
|
||||||
|
cfg->heartbeat_start_delay_sec = DEFAULT_HB_START_SEC;
|
||||||
|
}
|
||||||
|
cfg->heartbeat_monitor_enabled = cfg->heartbeat_monitor_enabled ? true : false;
|
||||||
|
if (!cfg->heartbeat_miss_limit) {
|
||||||
|
cfg->heartbeat_miss_limit = DEFAULT_HB_MISS_LIMIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t watch_config_init(void)
|
||||||
|
{
|
||||||
|
if (s_initialized) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = nvs_flash_init();
|
||||||
|
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||||
|
ESP_RETURN_ON_ERROR(nvs_flash_erase(), TAG, "NVS erase failed");
|
||||||
|
err = nvs_flash_init();
|
||||||
|
}
|
||||||
|
ESP_RETURN_ON_ERROR(err, TAG, "NVS init failed");
|
||||||
|
|
||||||
|
watch_config_apply_defaults();
|
||||||
|
|
||||||
|
nvs_handle_t handle = 0;
|
||||||
|
err = nvs_open(WATCH_CONFIG_NAMESPACE, NVS_READWRITE, &handle);
|
||||||
|
ESP_RETURN_ON_ERROR(err, TAG, "NVS open failed");
|
||||||
|
|
||||||
|
uint32_t value = 0;
|
||||||
|
if (nvs_get_u32(handle, KEY_HB_PERIOD, &value) == ESP_OK && value > 0) {
|
||||||
|
s_config.heartbeat_period_sec = value;
|
||||||
|
}
|
||||||
|
if (nvs_get_u32(handle, KEY_DCDC_OFF, &value) == ESP_OK && value > 0) {
|
||||||
|
s_config.dcdc_restart_off_sec = value;
|
||||||
|
}
|
||||||
|
if (nvs_get_u32(handle, KEY_HB_START, &value) == ESP_OK && value > 0) {
|
||||||
|
s_config.heartbeat_start_delay_sec = value;
|
||||||
|
}
|
||||||
|
uint8_t hb_mon = 1;
|
||||||
|
if (nvs_get_u8(handle, KEY_HB_MON, &hb_mon) == ESP_OK) {
|
||||||
|
s_config.heartbeat_monitor_enabled = (hb_mon != 0);
|
||||||
|
}
|
||||||
|
if (nvs_get_u32(handle, KEY_HB_MISS, &value) == ESP_OK && value > 0) {
|
||||||
|
s_config.heartbeat_miss_limit = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
nvs_close(handle);
|
||||||
|
s_initialized = true;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const watch_config_t *watch_config_get(void)
|
||||||
|
{
|
||||||
|
return &s_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t watch_config_save(const watch_config_t *cfg)
|
||||||
|
{
|
||||||
|
if (!cfg) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
watch_config_t tmp = *cfg;
|
||||||
|
watch_config_clamp(&tmp);
|
||||||
|
|
||||||
|
nvs_handle_t handle = 0;
|
||||||
|
ESP_RETURN_ON_ERROR(nvs_open(WATCH_CONFIG_NAMESPACE, NVS_READWRITE, &handle),
|
||||||
|
TAG, "NVS open failed");
|
||||||
|
|
||||||
|
esp_err_t err = nvs_set_u32(handle, KEY_HB_PERIOD, tmp.heartbeat_period_sec);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
err = nvs_set_u32(handle, KEY_DCDC_OFF, tmp.dcdc_restart_off_sec);
|
||||||
|
}
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
err = nvs_set_u32(handle, KEY_HB_START, tmp.heartbeat_start_delay_sec);
|
||||||
|
}
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
err = nvs_set_u8(handle, KEY_HB_MON, tmp.heartbeat_monitor_enabled ? 1 : 0);
|
||||||
|
}
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
err = nvs_set_u32(handle, KEY_HB_MISS, tmp.heartbeat_miss_limit);
|
||||||
|
}
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
err = nvs_commit(handle);
|
||||||
|
}
|
||||||
|
nvs_close(handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_config = tmp;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
18
main/watch_config.h
Normal file
18
main/watch_config.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t heartbeat_period_sec;
|
||||||
|
uint32_t dcdc_restart_off_sec;
|
||||||
|
uint32_t heartbeat_start_delay_sec;
|
||||||
|
bool heartbeat_monitor_enabled;
|
||||||
|
uint32_t heartbeat_miss_limit;
|
||||||
|
} watch_config_t;
|
||||||
|
|
||||||
|
esp_err_t watch_config_init(void);
|
||||||
|
const watch_config_t *watch_config_get(void);
|
||||||
|
esp_err_t watch_config_save(const watch_config_t *cfg);
|
||||||
@@ -4,6 +4,10 @@
|
|||||||
#include "led_strip.h"
|
#include "led_strip.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/timers.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
#ifndef CONFIG_WATCH_WS2812_LED_COUNT
|
#ifndef CONFIG_WATCH_WS2812_LED_COUNT
|
||||||
@@ -18,36 +22,174 @@
|
|||||||
|
|
||||||
#define WS2812_STATUS_GPIO ((gpio_num_t)CONFIG_WATCH_WS2812_GPIO)
|
#define WS2812_STATUS_GPIO ((gpio_num_t)CONFIG_WATCH_WS2812_GPIO)
|
||||||
#define WS2812_STATUS_RESOLUTION_HZ CONFIG_WATCH_WS2812_RMT_RESOLUTION
|
#define WS2812_STATUS_RESOLUTION_HZ CONFIG_WATCH_WS2812_RMT_RESOLUTION
|
||||||
|
#define WS2812_ANIM_REFRESH pdMS_TO_TICKS(100)
|
||||||
|
#define WS2812_ALERT_BLINK_PERIOD pdMS_TO_TICKS(200)
|
||||||
|
#define WS2812_ALERT_GAP_PERIOD pdMS_TO_TICKS(2000)
|
||||||
|
#define WS2812_VPN_ALERT_BLINKS 2
|
||||||
|
#define WS2812_APP_ALERT_BLINKS 3
|
||||||
|
#define WS2812_BRIGHTNESS_PERCENT 5
|
||||||
|
|
||||||
|
#define WS2812_VPN_SECTION_TICKS (WS2812_ALERT_BLINK_PERIOD * 2U * WS2812_VPN_ALERT_BLINKS)
|
||||||
|
#define WS2812_APP_SECTION_TICKS (WS2812_ALERT_BLINK_PERIOD * 2U * WS2812_APP_ALERT_BLINKS)
|
||||||
|
|
||||||
static const char *TAG = "ws2812";
|
static const char *TAG = "ws2812";
|
||||||
|
|
||||||
static led_strip_handle_t s_strip;
|
static led_strip_handle_t s_strip;
|
||||||
static bool s_led_state[WS2812_STATUS_LED_COUNT];
|
static bool s_channel_enabled[WS2812_STATUS_LED_COUNT];
|
||||||
|
static bool s_vpn_state[WS2812_STATUS_LED_COUNT];
|
||||||
|
static bool s_app_state[WS2812_STATUS_LED_COUNT];
|
||||||
|
static SemaphoreHandle_t s_ws_lock;
|
||||||
|
static TimerHandle_t s_animation_timer;
|
||||||
static bool s_error_state;
|
static bool s_error_state;
|
||||||
static size_t s_active_channel = SIZE_MAX;
|
static bool s_startup_hold;
|
||||||
|
static TickType_t s_startup_expire_tick;
|
||||||
|
static TickType_t s_alert_cycle_epoch;
|
||||||
|
static size_t s_active_vpn_alerts;
|
||||||
|
static size_t s_active_app_alerts;
|
||||||
|
|
||||||
|
static esp_err_t ws2812_status_apply(void);
|
||||||
|
static void ws2812_animation_timer_cb(TimerHandle_t timer);
|
||||||
|
static void ws2812_status_compute_color(size_t index,
|
||||||
|
TickType_t now_ticks,
|
||||||
|
uint8_t *r,
|
||||||
|
uint8_t *g,
|
||||||
|
uint8_t *b);
|
||||||
|
static bool ws2812_recalculate_alert_counts(void);
|
||||||
|
static uint8_t ws2812_apply_brightness(uint8_t component);
|
||||||
|
|
||||||
|
static void ws2812_animation_timer_cb(TimerHandle_t timer)
|
||||||
|
{
|
||||||
|
(void)timer;
|
||||||
|
ws2812_status_apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ws2812_startup_hold_active(TickType_t now_ticks)
|
||||||
|
{
|
||||||
|
if (!s_startup_hold) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (now_ticks >= s_startup_expire_tick) {
|
||||||
|
s_startup_hold = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ws2812_status_compute_color(size_t index,
|
||||||
|
TickType_t now_ticks,
|
||||||
|
uint8_t *r,
|
||||||
|
uint8_t *g,
|
||||||
|
uint8_t *b)
|
||||||
|
{
|
||||||
|
if (!r || !g || !b || index >= WS2812_STATUS_LED_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*r = 0;
|
||||||
|
*g = 0;
|
||||||
|
*b = 0;
|
||||||
|
|
||||||
|
if (s_error_state) {
|
||||||
|
*r = 40;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ws2812_startup_hold_active(now_ticks)) {
|
||||||
|
*g = 40;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!s_channel_enabled[index]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool vpn_alert = !s_vpn_state[index];
|
||||||
|
const bool app_alert = !s_app_state[index];
|
||||||
|
if (!vpn_alert && !app_alert) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool vpn_global = (s_active_vpn_alerts > 0);
|
||||||
|
const bool app_global = (s_active_app_alerts > 0);
|
||||||
|
|
||||||
|
TickType_t cycle_ticks = 0;
|
||||||
|
if (vpn_global) {
|
||||||
|
cycle_ticks += WS2812_VPN_SECTION_TICKS + WS2812_ALERT_GAP_PERIOD;
|
||||||
|
}
|
||||||
|
if (app_global) {
|
||||||
|
cycle_ticks += WS2812_APP_SECTION_TICKS + WS2812_ALERT_GAP_PERIOD;
|
||||||
|
}
|
||||||
|
if (cycle_ticks == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TickType_t blink_period = WS2812_ALERT_BLINK_PERIOD ? WS2812_ALERT_BLINK_PERIOD : 1;
|
||||||
|
TickType_t cycle_pos = (now_ticks - s_alert_cycle_epoch) % cycle_ticks;
|
||||||
|
|
||||||
|
if (vpn_global) {
|
||||||
|
if (cycle_pos < WS2812_VPN_SECTION_TICKS) {
|
||||||
|
if (vpn_alert) {
|
||||||
|
const bool on = ((cycle_pos / blink_period) % 2U) == 0U;
|
||||||
|
if (on) {
|
||||||
|
*r = 90;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cycle_pos -= WS2812_VPN_SECTION_TICKS;
|
||||||
|
if (cycle_pos < WS2812_ALERT_GAP_PERIOD) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cycle_pos -= WS2812_ALERT_GAP_PERIOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app_global) {
|
||||||
|
if (cycle_pos < WS2812_APP_SECTION_TICKS) {
|
||||||
|
if (app_alert) {
|
||||||
|
const bool on = ((cycle_pos / blink_period) % 2U) == 0U;
|
||||||
|
if (on) {
|
||||||
|
*r = 80;
|
||||||
|
*g = 80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cycle_pos -= WS2812_APP_SECTION_TICKS;
|
||||||
|
if (cycle_pos < WS2812_ALERT_GAP_PERIOD) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static esp_err_t ws2812_status_apply(void)
|
static esp_err_t ws2812_status_apply(void)
|
||||||
{
|
{
|
||||||
if (!s_strip) {
|
if (!s_strip) {
|
||||||
return ESP_ERR_INVALID_STATE;
|
return ESP_ERR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
if (!s_ws_lock) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
if (xSemaphoreTake(s_ws_lock, portMAX_DELAY) != pdTRUE) {
|
||||||
|
return ESP_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) {
|
const TickType_t now_ticks = xTaskGetTickCount();
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
for (size_t i = 0; i < WS2812_STATUS_LED_COUNT && err == ESP_OK; ++i) {
|
||||||
uint8_t r = 0, g = 0, b = 0;
|
uint8_t r = 0, g = 0, b = 0;
|
||||||
if (s_error_state) {
|
ws2812_status_compute_color(i, now_ticks, &r, &g, &b);
|
||||||
r = 40;
|
err = led_strip_set_pixel(s_strip,
|
||||||
} else if (s_active_channel == i) {
|
i,
|
||||||
g = 60;
|
ws2812_apply_brightness(g),
|
||||||
} else if (s_led_state[i]) {
|
ws2812_apply_brightness(r),
|
||||||
g = 18;
|
ws2812_apply_brightness(b));
|
||||||
} else {
|
|
||||||
b = 10;
|
|
||||||
}
|
}
|
||||||
ESP_RETURN_ON_ERROR(led_strip_set_pixel(s_strip, i, r, g, b), TAG,
|
if (err == ESP_OK) {
|
||||||
"led pixel set failed");
|
err = led_strip_refresh(s_strip);
|
||||||
}
|
}
|
||||||
|
|
||||||
return led_strip_refresh(s_strip);
|
xSemaphoreGive(s_ws_lock);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t ws2812_status_init(void)
|
esp_err_t ws2812_status_init(void)
|
||||||
@@ -62,7 +204,6 @@ esp_err_t ws2812_status_init(void)
|
|||||||
.led_model = LED_MODEL_WS2812,
|
.led_model = LED_MODEL_WS2812,
|
||||||
.flags.invert_out = false,
|
.flags.invert_out = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
led_strip_rmt_config_t rmt_cfg = {
|
led_strip_rmt_config_t rmt_cfg = {
|
||||||
.clk_src = RMT_CLK_SRC_DEFAULT,
|
.clk_src = RMT_CLK_SRC_DEFAULT,
|
||||||
.resolution_hz = WS2812_STATUS_RESOLUTION_HZ,
|
.resolution_hz = WS2812_STATUS_RESOLUTION_HZ,
|
||||||
@@ -75,12 +216,35 @@ esp_err_t ws2812_status_init(void)
|
|||||||
"Не вдалося створити RMT пристрій для WS2812");
|
"Не вдалося створити RMT пристрій для WS2812");
|
||||||
ESP_RETURN_ON_ERROR(led_strip_clear(s_strip), TAG, "clear fail");
|
ESP_RETURN_ON_ERROR(led_strip_clear(s_strip), TAG, "clear fail");
|
||||||
|
|
||||||
for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) {
|
s_ws_lock = xSemaphoreCreateMutex();
|
||||||
s_led_state[i] = false;
|
ESP_RETURN_ON_FALSE(s_ws_lock, ESP_ERR_NO_MEM, TAG, "mutex alloc failed");
|
||||||
}
|
|
||||||
s_active_channel = SIZE_MAX;
|
|
||||||
s_error_state = false;
|
|
||||||
|
|
||||||
|
const TickType_t now = xTaskGetTickCount();
|
||||||
|
for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) {
|
||||||
|
s_channel_enabled[i] = false;
|
||||||
|
s_vpn_state[i] = true;
|
||||||
|
s_app_state[i] = true;
|
||||||
|
}
|
||||||
|
s_error_state = false;
|
||||||
|
s_startup_hold = false;
|
||||||
|
s_startup_expire_tick = 0;
|
||||||
|
s_alert_cycle_epoch = now;
|
||||||
|
(void)ws2812_recalculate_alert_counts();
|
||||||
|
|
||||||
|
if (!s_animation_timer) {
|
||||||
|
s_animation_timer = xTimerCreate("ws2812_anim",
|
||||||
|
WS2812_ANIM_REFRESH,
|
||||||
|
pdTRUE,
|
||||||
|
NULL,
|
||||||
|
ws2812_animation_timer_cb);
|
||||||
|
ESP_RETURN_ON_FALSE(s_animation_timer, ESP_ERR_NO_MEM, TAG, "anim timer alloc failed");
|
||||||
|
ESP_RETURN_ON_FALSE(xTimerStart(s_animation_timer, 0) == pdPASS,
|
||||||
|
ESP_FAIL,
|
||||||
|
TAG,
|
||||||
|
"anim timer start failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)ws2812_recalculate_alert_counts();
|
||||||
return ws2812_status_apply();
|
return ws2812_status_apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,46 +253,115 @@ esp_err_t ws2812_status_set_channel_state(size_t channel, bool enabled)
|
|||||||
if (channel >= WS2812_STATUS_LED_COUNT) {
|
if (channel >= WS2812_STATUS_LED_COUNT) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
s_led_state[channel] = enabled;
|
if (!s_strip) {
|
||||||
return ws2812_status_apply();
|
return ESP_ERR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
s_channel_enabled[channel] = enabled;
|
||||||
esp_err_t ws2812_status_mark_active(size_t channel)
|
if (!enabled) {
|
||||||
{
|
s_vpn_state[channel] = true;
|
||||||
if (channel >= WS2812_STATUS_LED_COUNT) {
|
s_app_state[channel] = true;
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
}
|
||||||
s_active_channel = channel;
|
|
||||||
return ws2812_status_apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t ws2812_status_clear_active(void)
|
|
||||||
{
|
|
||||||
s_active_channel = SIZE_MAX;
|
|
||||||
return ws2812_status_apply();
|
return ws2812_status_apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t ws2812_status_set_error(bool has_error)
|
esp_err_t ws2812_status_set_error(bool has_error)
|
||||||
{
|
{
|
||||||
|
if (!s_strip) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
s_error_state = has_error;
|
s_error_state = has_error;
|
||||||
if (has_error) {
|
return ws2812_status_apply();
|
||||||
s_active_channel = SIZE_MAX;
|
}
|
||||||
|
|
||||||
|
esp_err_t ws2812_status_set_service_state(size_t channel, bool vpn_ok, bool app_ok)
|
||||||
|
{
|
||||||
|
if (channel >= WS2812_STATUS_LED_COUNT) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (!s_strip) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
bool changed = false;
|
||||||
|
if (s_vpn_state[channel] != vpn_ok) {
|
||||||
|
s_vpn_state[channel] = vpn_ok;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (s_app_state[channel] != app_ok) {
|
||||||
|
s_app_state[channel] = app_ok;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
(void)ws2812_recalculate_alert_counts();
|
||||||
|
s_alert_cycle_epoch = xTaskGetTickCount();
|
||||||
}
|
}
|
||||||
return ws2812_status_apply();
|
return ws2812_status_apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t ws2812_status_refresh_from_dcdc(void)
|
esp_err_t ws2812_status_refresh_from_dcdc(void)
|
||||||
{
|
{
|
||||||
|
if (!s_strip) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
const size_t available_channels = dcdc_channel_count();
|
const size_t available_channels = dcdc_channel_count();
|
||||||
const size_t count = available_channels < WS2812_STATUS_LED_COUNT
|
const size_t count = available_channels < WS2812_STATUS_LED_COUNT
|
||||||
? available_channels
|
? available_channels
|
||||||
: WS2812_STATUS_LED_COUNT;
|
: WS2812_STATUS_LED_COUNT;
|
||||||
|
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
s_led_state[i] = dcdc_get_state(i);
|
s_channel_enabled[i] = dcdc_get_state(i);
|
||||||
}
|
}
|
||||||
for (size_t i = count; i < WS2812_STATUS_LED_COUNT; ++i) {
|
for (size_t i = count; i < WS2812_STATUS_LED_COUNT; ++i) {
|
||||||
s_led_state[i] = false;
|
s_channel_enabled[i] = false;
|
||||||
|
s_vpn_state[i] = true;
|
||||||
|
s_app_state[i] = true;
|
||||||
}
|
}
|
||||||
|
(void)ws2812_recalculate_alert_counts();
|
||||||
|
s_alert_cycle_epoch = xTaskGetTickCount();
|
||||||
return ws2812_status_apply();
|
return ws2812_status_apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t ws2812_status_set_startup_hold(uint32_t duration_ms)
|
||||||
|
{
|
||||||
|
if (!s_strip) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
if (duration_ms == 0) {
|
||||||
|
s_startup_hold = false;
|
||||||
|
s_startup_expire_tick = 0;
|
||||||
|
return ws2812_status_apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
TickType_t duration_ticks = pdMS_TO_TICKS(duration_ms);
|
||||||
|
if (duration_ticks == 0) {
|
||||||
|
duration_ticks = 1;
|
||||||
|
}
|
||||||
|
s_startup_hold = true;
|
||||||
|
s_startup_expire_tick = xTaskGetTickCount() + duration_ticks;
|
||||||
|
return ws2812_status_apply();
|
||||||
|
}
|
||||||
|
static bool ws2812_recalculate_alert_counts(void)
|
||||||
|
{
|
||||||
|
size_t vpn = 0;
|
||||||
|
size_t app = 0;
|
||||||
|
for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) {
|
||||||
|
if (!s_channel_enabled[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!s_vpn_state[i]) {
|
||||||
|
++vpn;
|
||||||
|
}
|
||||||
|
if (!s_app_state[i]) {
|
||||||
|
++app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const bool changed = (vpn != s_active_vpn_alerts) || (app != s_active_app_alerts);
|
||||||
|
s_active_vpn_alerts = vpn;
|
||||||
|
s_active_app_alerts = app;
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t ws2812_apply_brightness(uint8_t component)
|
||||||
|
{
|
||||||
|
// Scale brightness down (component * 20%) with rounding to nearest.
|
||||||
|
const uint16_t scaled = (component * WS2812_BRIGHTNESS_PERCENT + 50U) / 100U;
|
||||||
|
return (uint8_t)scaled;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
@@ -14,7 +15,7 @@
|
|||||||
|
|
||||||
esp_err_t ws2812_status_init(void);
|
esp_err_t ws2812_status_init(void);
|
||||||
esp_err_t ws2812_status_set_channel_state(size_t channel, bool enabled);
|
esp_err_t ws2812_status_set_channel_state(size_t channel, bool enabled);
|
||||||
esp_err_t ws2812_status_mark_active(size_t channel);
|
|
||||||
esp_err_t ws2812_status_clear_active(void);
|
|
||||||
esp_err_t ws2812_status_set_error(bool has_error);
|
esp_err_t ws2812_status_set_error(bool has_error);
|
||||||
|
esp_err_t ws2812_status_set_service_state(size_t channel, bool vpn_ok, bool app_ok);
|
||||||
|
esp_err_t ws2812_status_set_startup_hold(uint32_t duration_ms);
|
||||||
esp_err_t ws2812_status_refresh_from_dcdc(void);
|
esp_err_t ws2812_status_refresh_from_dcdc(void);
|
||||||
|
|||||||
Reference in New Issue
Block a user