From 74df7e2645ba2caa4234aaa773f31bce042408af38cf2e2ad87c4dbcf05e8c7a Mon Sep 17 00:00:00 2001 From: AidarKC Date: Sun, 24 May 2026 19:29:42 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8E=20Solana=20PDA=20=D0=B8=20ESP32-=D0=BF=D0=BE?= =?UTF-8?q?=D0=B4=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 15 + ...5-24_0300_python_обвязка_telegram_codex.md | 21 - ...-05-24_0818_agent-bot-heartbeat-restart.md | 26 - .../2026-05-24_0922_agent-bot-channel-mode.md | 21 - ...-05-24_0928_удаление_java_агента_кодера.md | 16 - ...2026-05-24_1032_agent-bot-групповой-чат.md | 23 - Dev_Docs/Solana/user_pda/README.md | 365 ++++++++++++ .../CODEX_PORTING_GUIDE.md | 119 ++++ .../ESP32-S3-Touch-AMOLED-2.16/README.md | 25 + .../original-firmware/README.md | 11 + .../original-firmware/backup_factory.sh | 19 + .../restore_factory_backup.sh | 21 + .../reference/README.md | 17 + .../test-device/README.md | 18 + .../test-device/burn.sh | 51 ++ .../test-device/simple_av_test/audio_hal.h | 136 +++++ .../test-device/simple_av_test/es7210.cpp | 549 ++++++++++++++++++ .../test-device/simple_av_test/es7210.h | 260 +++++++++ .../test-device/simple_av_test/es8311.c | 443 ++++++++++++++ .../test-device/simple_av_test/es8311.h | 227 ++++++++ .../test-device/simple_av_test/es8311_reg.h | 76 +++ .../simple_av_test/simple_av_test.ino | 272 +++++++++ VERSION.properties | 4 +- 23 files changed, 2626 insertions(+), 109 deletions(-) delete mode 100644 Dev_Docs/Pending_Features/2026-05-24_0300_python_обвязка_telegram_codex.md delete mode 100644 Dev_Docs/Pending_Features/2026-05-24_0818_agent-bot-heartbeat-restart.md delete mode 100644 Dev_Docs/Pending_Features/2026-05-24_0922_agent-bot-channel-mode.md delete mode 100644 Dev_Docs/Pending_Features/2026-05-24_0928_удаление_java_агента_кодера.md delete mode 100644 Dev_Docs/Pending_Features/2026-05-24_1032_agent-bot-групповой-чат.md create mode 100644 Dev_Docs/Solana/user_pda/README.md create mode 100644 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/CODEX_PORTING_GUIDE.md create mode 100644 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/README.md create mode 100644 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/README.md create mode 100755 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/backup_factory.sh create mode 100755 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/restore_factory_backup.sh create mode 100644 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/README.md create mode 100644 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/README.md create mode 100755 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/burn.sh create mode 100644 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/audio_hal.h create mode 100644 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es7210.cpp create mode 100644 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es7210.h create mode 100644 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es8311.c create mode 100644 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es8311.h create mode 100644 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es8311_reg.h create mode 100644 ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/simple_av_test.ino diff --git a/.gitignore b/.gitignore index 34a8e4b..a89d924 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,18 @@ shine-solana/shine/scripts/**/keypairs/ shine-solana/shine/scripts/**/runs/ shine-solana/shine/scripts/**/*.env shine-solana/shine/scripts/**/TEMP_*.md + +# Локальные артефакты и внешние материалы ESP32-подпроекта +ESP32/**/.git/ +ESP32/**/.idea/ +ESP32/**/.arduino-build/ +ESP32/**/official-demo/ +ESP32/**/original-firmware/*.bin +ESP32/**/original-firmware/*.bin.sha256 +ESP32/**/*.elf +ESP32/**/*.map +ESP32/**/*.merged.bin +ESP32/**/*.uf2 +ESP32/**/*.o +ESP32/**/*.d +ESP32/**/*.a diff --git a/Dev_Docs/Pending_Features/2026-05-24_0300_python_обвязка_telegram_codex.md b/Dev_Docs/Pending_Features/2026-05-24_0300_python_обвязка_telegram_codex.md deleted file mode 100644 index 258d12f..0000000 --- a/Dev_Docs/Pending_Features/2026-05-24_0300_python_обвязка_telegram_codex.md +++ /dev/null @@ -1,21 +0,0 @@ -# Python-обвязка Telegram → Codex (упрощённый сервис) - -- краткое описание фичи: - - добавлен новый упрощённый сервис `SHiNE-agent-bot-coder/py_bot_service.py`; - - сервис работает через long-polling Telegram, принимает только текст, ведёт историю в `JSONL`; - - добавлены команды `/status`, `/queue`, `/stop`, `/cancel`, `/new`, `/help`; - - systemd unit переключён на запуск Python-сервиса. - -- что проверять: - - `systemctl --user status shine-agent-bot-coder` показывает `active (running)`; - - бот отвечает на `/status` одним сообщением без дублей; - - тестовый текстовый запрос получает финальный ответ от Codex; - - `/stop` корректно останавливает текущую задачу; - - `/new` переносит текущую историю в `data/history/archive`. - -- ожидаемый результат: - - стабильная обработка текстовых задач без зависаний и двойной обработки; - - только один инстанс сервиса (через lock `data/py_app.lock`). - -- статус: - - pending diff --git a/Dev_Docs/Pending_Features/2026-05-24_0818_agent-bot-heartbeat-restart.md b/Dev_Docs/Pending_Features/2026-05-24_0818_agent-bot-heartbeat-restart.md deleted file mode 100644 index e6bbdea..0000000 --- a/Dev_Docs/Pending_Features/2026-05-24_0818_agent-bot-heartbeat-restart.md +++ /dev/null @@ -1,26 +0,0 @@ -# Heartbeat и перезапуск агента-кодера - -## Краткое описание - -Изменена логика Telegram-сервиса агента-кодера: -- аварийное сообщение о долгой работе отправляется только после 2 минут молчания Codex; -- при дальнейшем молчании статус повторяется каждые 2 минуты; -- добавлена команда `/restart_service` с алиасом `/restart` для перезапуска сервиса через systemd. - -## Что проверить - -1. Запустить долгую задачу, в которой Codex регулярно отправляет промежуточные сообщения. -2. Убедиться, что дополнительное сообщение `всё ещё выполняется` не появляется без 2 минут молчания. -3. Запустить или смоделировать задачу, где Codex молчит больше 2 минут. -4. Проверить, что бот присылает статус с общим временем работы задачи и повторяет его каждые 2 минуты молчания. -5. Отправить `/restart_service` из разрешённого Telegram-аккаунта. -6. Проверить, что сервис завершился и был поднят systemd заново. -7. Проверить, что история JSONL сохранилась и не была очищена без команды `/new`. - -## Ожидаемый результат - -Сервис не шумит регулярными статусами при нормальной работе Codex, но сообщает о подозрительном молчании. Команда `/restart_service` перезапускает сервис без ручного входа в консоль. - -## Статус - -pending diff --git a/Dev_Docs/Pending_Features/2026-05-24_0922_agent-bot-channel-mode.md b/Dev_Docs/Pending_Features/2026-05-24_0922_agent-bot-channel-mode.md deleted file mode 100644 index d459778..0000000 --- a/Dev_Docs/Pending_Features/2026-05-24_0922_agent-bot-channel-mode.md +++ /dev/null @@ -1,21 +0,0 @@ -# Канальный режим агента-кодера - -## Краткое описание -Сервис `SHiNE-agent-bot-coder` теперь принимает сообщения из Telegram-канала `@shine_writing`. - -Сообщения Айдара (`@AidarKC` / `@aidarkc`) ставятся в очередь как задачи Codex, а ответы отправляются обратно в тот же канал. Сообщения других авторов в канале сохраняются в историю как дополнительный контекст и не выполняются как команды. - -## Что проверить -- Отправить текстовое сообщение от Айдара в канал `@shine_writing`. -- Убедиться, что бот принял задачу, обработал её и ответил в этот же канал. -- Отправить сообщение от другого автора в этот канал. -- Убедиться, что бот не запускает задачу по сообщению другого автора. -- Проверить, что сообщение другого автора появилось в JSONL-истории как контекст. - -## Ожидаемый результат -- Команды Айдара из канала выполняются так же, как личные сообщения. -- Ответы бота публикуются в канал. -- Сообщения других авторов сохраняются в историю, но не исполняются. - -## Статус -pending diff --git a/Dev_Docs/Pending_Features/2026-05-24_0928_удаление_java_агента_кодера.md b/Dev_Docs/Pending_Features/2026-05-24_0928_удаление_java_агента_кодера.md deleted file mode 100644 index d95e40a..0000000 --- a/Dev_Docs/Pending_Features/2026-05-24_0928_удаление_java_агента_кодера.md +++ /dev/null @@ -1,16 +0,0 @@ -# Удаление старой Java-реализации агента-кодера - -- краткое описание фичи: - Старая Java-реализация `SHiNE-agent-bot-coder` удалена, потому что рабочим вариантом сервиса является Python-скрипт `py_bot_service.py`. - -- что именно проверять: - 1. Gradle-проект больше не содержит подпроект `SHiNE-agent-bot-coder`. - 2. Локальный systemd-сервис `shine-agent-bot-coder` запускает `py_bot_service.py`. - 3. Telegram-бот принимает сообщение от Айдара и отвечает через Python-сервис. - 4. Команды `/status`, `/queue`, `/new` и `/restart_service` работают как раньше. - -- ожидаемый результат: - Удаление Java-кода не влияет на текущую работу Python-сервиса агента-кодера. - -- статус: - `pending` diff --git a/Dev_Docs/Pending_Features/2026-05-24_1032_agent-bot-групповой-чат.md b/Dev_Docs/Pending_Features/2026-05-24_1032_agent-bot-групповой-чат.md deleted file mode 100644 index 986f099..0000000 --- a/Dev_Docs/Pending_Features/2026-05-24_1032_agent-bot-групповой-чат.md +++ /dev/null @@ -1,23 +0,0 @@ -# Групповой чат агента-кодера - -## Что сделано -- Сервис `SHiNE-agent-bot-coder` теперь сохраняет сообщения участников обычной Telegram-группы и supergroup как контекст. -- На сообщения других участников группы сервис отвечает в тот же чат коротким подтверждением `Получил сообщение.`, но не создаёт задачу Codex. -- При миграции обычной группы в supergroup сервис запоминает новый `chat_id` и перенаправляет ответы туда. -- Команды и задачи по-прежнему выполняются только от Айдара (`@aidarkc` / `@AidarKC`). - -## Что проверять -1. Написать сообщение от другого участника в группе `@shine_writing`. -2. Убедиться, что бот ответил на него в группе коротким подтверждением получения. -3. Написать задачу от Айдара в этой же группе. -4. Убедиться, что ответ приходит в группу, а не в личные сообщения. -5. Убедиться, что после миграции group → supergroup ответы не теряются. - -## Ожидаемый результат -- Чужие сообщения попадают в историю как контекст. -- Чужие сообщения получают ACK в группе, но не попадают в очередь задач. -- Сообщение Айдара создаёт задачу и получает ответ в актуальном supergroup-чате. -- Ошибка Telegram `group chat was upgraded to a supergroup chat` больше не ломает отправку ответа. - -## Статус -pending diff --git a/Dev_Docs/Solana/user_pda/README.md b/Dev_Docs/Solana/user_pda/README.md new file mode 100644 index 0000000..532666b --- /dev/null +++ b/Dev_Docs/Solana/user_pda/README.md @@ -0,0 +1,365 @@ +# Solana user_pda: итоговый целевой формат пользовательской записи + +Документ описывает целевой формат пользовательской PDA-записи `user_pda` для Solana-программы `shine_users`. + +Это не формат основного блокчейна SHiNE и не документация по `AddBlock`. Основной блокчейн SHiNE описан отдельно в `Dev_Docs/Blockchain/`. + +Статус документа: итоговый согласованный целевой формат. Текущий код Solana-модуля пока использует старый линейный формат записи; этот документ должен стать основой для изменения кода, тестов и интеграции с сервером. + +## 1. Назначение user_pda + +`user_pda` хранит публичное состояние пользователя в Solana: + +- логин пользователя; +- неизменяемые параметры создания записи; +- корневой публичный ключ пользователя; +- ключ устройства; +- данные одного или нескольких пользовательских блокчейнов SHiNE; +- серверные данные пользователя, если пользователь выступает сервером; +- серверы доступа пользователя; +- счетчики/лимиты; +- подпись записи. + +На первом этапе поддерживается один пользовательский блокчейн SHiNE, но формат блока блокчейна сразу допускает повторение таких блоков в будущем. + +## 2. Адрес PDA + +Адрес пользовательской PDA вычисляется по логину: + +- seed prefix: `login=`; +- второй seed: нормализованный логин в нижнем регистре; +- program id: программа `shine_users`. + +Один логин соответствует одной `user_pda`. + +## 3. Общие правила кодирования + +- Числа кодируются в Little Endian. +- `u8`, `u16`, `u32`, `u64` имеют обычный фиксированный размер. +- Публичный ключ Solana/Ed25519: 32 байта. +- Ed25519-подпись: 64 байта. +- SHA-256/Solana hash: 32 байта. +- Строка переменной длины: `len: u8` + `bytes[len]` в UTF-8. +- Arweave `tx_id`: строка переменной длины. Ожидаемая практическая длина base64url tx id - 43 байта, но формат хранит длину явно. +- Все типизированные блоки после фиксированного заголовка начинаются с `block_type: u8` и `block_version: u8`. +- Отдельный `block_len` у типизированных блоков не хранится: блоки парсятся по известным полям, счетчикам и строкам с `len: u8`. + +## 4. Верхний формат записи + +Первые 9 полей фиксированы и идут строго в указанном порядке. Это общий заголовок записи. + +| N | Поле | Тип | Размер | Правило | +|---|------|-----|--------|---------| +| 1 | `magic` | bytes | 5 | Всегда `SHiNE`. | +| 2 | `format_major` | `u8` | 1 | Для нового формата: `2`. | +| 3 | `format_minor` | `u8` | 1 | Для первой версии нового формата: `0`. | +| 4 | `record_len` | `u16` | 2 | Длина полезной записи от `magic` до `signature` включительно, без padding. | +| 5 | `created_at_ms` | `u64` | 8 | Время создания записи, Unix time в миллисекундах. Не меняется. | +| 6 | `updated_at_ms` | `u64` | 8 | Время последнего обновления записи. | +| 7 | `record_number` | `u32` | 4 | Номер версии записи пользователя. При создании `0`, при обновлении +1. | +| 8 | `prev_record_hash` | bytes | 32 | Хэш unsigned-части предыдущей записи. При создании 32 нулевых байта. | +| 9 | `login` | string | `1 + len` | Логин пользователя. Не меняется. | + +После первых 9 полей идет набор типизированных блоков: + +```text +UserPdaRecordV2 +- fixed_header: поля 1..9 +- blocks_count: u8 +- blocks: TypedBlock[blocks_count] +- signature: [u8; 64] +- padding: bytes до размера PDA, если нужен +``` + +`blocks_count` входит в unsigned-часть записи и подписывается. + +## 5. Типы блоков + +Зарезервированные значения `block_type`: + +| block_type | Блок | Назначение | +|------------|------|------------| +| `1` | `RootKeyBlock` | Корневой ключ пользователя. | +| `2` | `DeviceKeyBlock` | Ключ устройства пользователя. | +| `3` | `BlockchainRegistryBlock` | Один или несколько блокчейнов пользователя. | +| `30` | `ServerProfileBlock` | Серверные данные пользователя. | +| `40` | `AccessServersBlock` | Серверы доступа/relay. | +| `50` | `TrustedStateBlock` | Счетчик trusted-связей. | +| `255` | `ReservedBlock` | Зарезервировано, пока не используется. | + +Правила: + +- неизвестный `block_type` в `format_major = 2` считается ошибкой; +- обязательные блоки: `RootKeyBlock`, `DeviceKeyBlock`, `BlockchainRegistryBlock`; +- необязательные блоки: `ServerProfileBlock`, `AccessServersBlock`, `TrustedStateBlock`; +- каждый обязательный блок должен встречаться ровно один раз; +- порядок блоков в записи фиксируется для простоты проверки: + `RootKey`, `DeviceKey`, `BlockchainRegistry`, `ServerProfile`, `AccessServers`, `TrustedState`. + +## 6. RootKeyBlock + +Смена `root_key` пока не проектируется и не реализуется. Блок фиксирует только стадию `0`. + +```text +RootKeyBlock +- block_type: u8 = 1 +- block_version: u8 = 0 +- root_key: [u8; 32] +``` + +Правила: + +- при создании задается корневой публичный ключ пользователя; +- при обновлении `root_key` должен совпадать с предыдущей записью; +- ротация root-key будет отдельным форматом/сценарием в будущем. + +## 7. DeviceKeyBlock + +Смена `device_key` пока также не проектируется как отдельная ротация. В версии `0` хранится один ключ устройства. + +```text +DeviceKeyBlock +- block_type: u8 = 2 +- block_version: u8 = 0 +- device_key: [u8; 32] +``` + +Правила: + +- при создании задается текущий публичный ключ устройства; +- при обновлении ключ устройства может быть обновлен только если это отдельно разрешено бизнес-логикой инструкции; +- история устройств и несколько устройств в этом формате не хранятся. + +## 8. BlockchainRegistryBlock + +Блок хранит данные пользовательских блокчейнов SHiNE. Сейчас используется один блокчейн, но структура сразу сделана как список. + +```text +BlockchainRegistryBlock +- block_type: u8 = 3 +- block_version: u8 = 0 +- blockchain_count: u8 +- blockchain_records: BlockchainRecord[blockchain_count] +``` + +Правила: + +- на первом этапе `blockchain_count = 1`; +- в будущем можно увеличить количество записей без изменения смысла `BlockchainRecord`; +- каждый `BlockchainRecord` описывает один пользовательский SHiNE-блокчейн. + +## 9. BlockchainRecord + +```text +BlockchainRecord +- blockchain_type: u8 +- blockchain_name: string +- blockchain_public_key: [u8; 32] +- paid_limit_bytes: u64 +- used_bytes: u64 +- last_block_number: u32 +- last_block_hash: [u8; 32] +- last_block_signature: [u8; 64] +- arweave_present: u8 +- arweave_tx_id: string, только если arweave_present = 1 +``` + +`blockchain_type`: + +| Значение | Смысл | +|----------|-------| +| `1` | Основной пользовательский SHiNE-блокчейн. | + +Поля: + +- `blockchain_name` - строковое имя пользовательского блокчейна, например `login-001`. На первом этапе для основного блокчейна пользователя используется имя вида `-001`, потому что это первый блокчейн этого пользователя. +- `blockchain_public_key` - публичный ключ блокчейна пользователя. +- `paid_limit_bytes` - оплаченный лимит хранения/записей в байтах. +- `used_bytes` - сколько байт уже занято в пользовательском SHiNE-блокчейне. +- `last_block_number` - номер последнего известного блока пользовательского блокчейна. +- `last_block_hash` - хэш последнего известного блока. +- `last_block_signature` - подпись хэша специального сообщения о вершине блокчейна ключом `blockchain_public_key`. +- `arweave_present` - `0`, если ссылки нет; `1`, если ссылка есть. +- `arweave_tx_id` - Arweave transaction id, где лежит выгруженный пользовательский канал/состояние. + +Arweave `tx_id` - обычное поле внутри записи конкретного блокчейна. Solana-программа не проверяет, что такой Arweave transaction действительно существует и содержит корректные данные; это ответственность клиента/сервера/пользователя. + +## 10. Правила обновления BlockchainRecord + +При обновлении записи: + +- `blockchain_type` для существующей записи не меняется; +- `blockchain_public_key` пока не ротируется автоматически; смена ключа требует отдельного согласованного сценария; +- `paid_limit_bytes` может только увеличиваться или оставаться прежним; +- при увеличении `paid_limit_bytes` пользователь платит комиссию в Solana по тарифам программы; +- `used_bytes` может только увеличиваться или оставаться прежним; +- `last_block_number` может только увеличиваться или оставаться прежним; +- `used_bytes <= paid_limit_bytes`; +- если `last_block_number` увеличился, то должны быть переданы новый `last_block_hash` и новая `last_block_signature`; +- `last_block_signature` проверяется через Ed25519-инструкцию Solana: подпись должна соответствовать хэшу сообщения `LastBlockState` и `blockchain_public_key`; +- `arweave_tx_id` можно добавить или заменить на новый, если пользователь выгрузил более актуальное состояние в Arweave; +- уменьшать лимит, число блоков или занятый размер нельзя. + +Сообщение `LastBlockState`, которое хэшируется и подписывается ключом `blockchain_public_key`: + +```text +LastBlockState +- constant: bytes = "SHiNE_LAST_BLOCK" +- login: string +- blockchain_name: string +- last_block_number: u32 +- last_block_hash: [u8; 32] +- used_bytes: u64 +``` + +Алгоритм: + +```text +message = SHA-256(LastBlockState bytes) +last_block_signature = Ed25519(blockchain_public_key, message) +``` + +Причина проверки подписи `LastBlockState`: `root_key` управляет Solana-записью пользователя, а `blockchain_public_key` подтверждает состояние конкретного пользовательского блокчейна. Подписывается не голый хэш, а связка логина, имени блокчейна, номера последнего блока, хэша последнего блока и занятого размера. + +## 11. ServerProfileBlock + +Блок присутствует, если пользователь выступает сервером. + +```text +ServerProfileBlock +- block_type: u8 = 30 +- block_version: u8 = 0 +- is_server: u8 +- server_key: [u8; 32], только если is_server = 1 +- server_address: string, только если is_server = 1 +- sync_servers_count: u8, только если is_server = 1 +- sync_servers: string[sync_servers_count], только если is_server = 1 +``` + +Правила: + +- `is_server = 0` означает, что серверных данных нет; +- `is_server = 1` означает, что пользователь публикует серверный профиль; +- `sync_servers_count` максимум `32`; +- `server_address` - строковый адрес сервера в формате, который будет отдельно закреплен на уровне приложения; +- `sync_servers` - логины пользователей системы, через которых этот сервер пытается синхронизироваться. Solana-программа не обязана проверять, что эти логины действительно зарегистрированы как серверы. + +## 12. AccessServersBlock + +Блок хранит серверы доступа/relay для пользователя. + +```text +AccessServersBlock +- block_type: u8 = 40 +- block_version: u8 = 0 +- access_servers_count: u8 +- access_servers: string[access_servers_count] +``` + +Правила: + +- блок может отсутствовать, если серверы доступа не заданы; +- список может обновляться при изменении маршрутизации пользователя; +- `access_servers` - логины пользователей системы, используемых как серверы доступа/relay. Solana-программа не обязана проверять, что эти логины действительно зарегистрированы как серверы; +- точная семантика выбора сервера доступа определяется клиентской/серверной логикой SHiNE. + +## 13. TrustedStateBlock + +Пока trusted-логика не реализована полностью, поэтому блок хранит только счетчик. + +```text +TrustedStateBlock +- block_type: u8 = 50 +- block_version: u8 = 0 +- trusted_count: u8 = 0 +``` + +Пока блок с доверенными лицами не реализуется, потому что полный формат trusted-логики еще не составлен. В будущем trusted-связи, очереди, таймеры и подтверждения должны быть вынесены в отдельный формат. + +## 14. Подпись user_pda + +Подписывается не вся PDA целиком, а unsigned-часть записи: + +- от `magic` до последнего байта последнего типизированного блока включительно; +- включая `record_len`, `blocks_count`, все заголовки блоков и тела блоков; +- без поля `signature`; +- без padding. + +Алгоритм: + +```text +message = hash(unsigned_record_bytes) +signature = Ed25519(root_key, message) +``` + +Solana-программа проверяет подпись через встроенную Ed25519-инструкцию. Подписантом должен быть `root_key` из `RootKeyBlock`. + +Смену формата подписи сейчас не трогаем. + +## 15. Регистрация пользователя + +При регистрации: + +- PDA еще не должна существовать; +- логин проходит проверку формата и login guard; +- `record_number = 0`; +- `prev_record_hash = 0x00...00`; +- `created_at_ms = updated_at_ms`; +- обязательные блоки присутствуют; +- создается минимум один `BlockchainRecord`; +- стартовый `paid_limit_bytes` равен стартовому бонусу плюс оплаченный дополнительный лимит; +- `used_bytes <= paid_limit_bytes`; +- пользователь платит регистрационную комиссию; +- если покупается дополнительный лимит, пользователь платит комиссию за этот лимит; +- вся unsigned-часть записи подписана `root_key`. + +## 16. Обновление пользователя + +При обновлении: + +- PDA должна существовать; +- `login`, `created_at_ms`, `root_key` не меняются; +- `record_number = previous_record_number + 1`; +- `prev_record_hash` равен хэшу unsigned-части предыдущей записи; +- `updated_at_ms` обновляется; +- unsigned-часть новой записи подписана `root_key`; +- лимиты блокчейнов могут только увеличиваться; +- занятый размер и номер последнего блока не могут уменьшаться; +- при увеличении оплаченного лимита пользователь доплачивает комиссию; +- Arweave `tx_id` может быть пустым или обновленным, но его содержимое Solana не валидирует. + +## 17. Отличия от старого линейного формата + +Старый формат после `login` хранил поля линейно: + +- `root_key_status`; +- `root_key`; +- `blockchain_key_status`; +- `blockchain_key`; +- `device_key_status`; +- `device_key`; +- `chain_number`; +- `balance`; +- серверные поля; +- access-серверы; +- `trusted_count`; +- `reserved`; +- `signature`. + +Новый целевой формат сохраняет первые 9 фиксированных полей как заголовок, но дальше переходит на типизированные блоки: + +- ключи становятся отдельными блоками; +- данные блокчейна становятся расширенным блоком со своим публичным ключом, лимитом, занятым размером, вершиной цепочки и Arweave `tx_id`; +- серверные данные и access-серверы отделяются от данных блокчейна; +- расширение формата делается добавлением новых версий блоков или новых `block_type`, а не вставкой полей в середину линейной записи. + +## 18. Что пока не входит в формат + +Пока не проектируем: + +- ротацию `root_key`; +- сложную ротацию `device_key`; +- ротацию `blockchain_public_key`; +- проверку содержимого Arweave transaction; +- хранение полной истории пользовательского блокчейна внутри Solana; +- подключение Solana-модуля к сборке/деплою основного сервера SHiNE. diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/CODEX_PORTING_GUIDE.md b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/CODEX_PORTING_GUIDE.md new file mode 100644 index 0000000..618e2ae --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/CODEX_PORTING_GUIDE.md @@ -0,0 +1,119 @@ +# ESP32-S3-Touch-AMOLED-2.16 Codex Guide + +Этот файл переносится в другие проекты как готовая инструкция для Codex по этой плате. + +## 1) Что это за плата + +- Модель: `Waveshare ESP32-S3-Touch-AMOLED-2.16` +- MCU: `ESP32-S3` (flash 16MB, PSRAM 8MB) +- Экран: AMOLED, физически 480x480, углы скруглены (часть крайних пикселей может быть невидима) +- Touch: CST92xx +- IMU: QMI8658 +- Аудио: + - DAC/вывод (динамик): ES8311 + - ADC/вход (микрофоны): ES7210 + +## 2) Что уже установлено в этой среде + +- Ubuntu +- `arduino-cli 1.4.0` +- `esp32:esp32` core `3.3.5` +- `esptool` из `~/.arduino15/packages/esp32/tools/esptool_py/5.1.0/esptool` +- USB порт платы: обычно `/dev/ttyACM0` + +Проверка: + +```bash +arduino-cli version +arduino-cli core list +arduino-cli board list +ls -l /dev/ttyACM0 +``` + +## 3) Структура подпроекта (эталон) + +- `official-demo/` — официальный repo Waveshare (примеры+библиотеки) +- `original-firmware/` — backup/restore заводской прошивки +- `test-device/` — прошивки и `burn.sh` +- `reference/` — заметки и ссылки + +## 4) Бэкап перед любыми экспериментами + +```bash +cd ESP32-S3-Touch-AMOLED-2.16/original-firmware +./backup_factory.sh +``` + +Ожидаемый результат: +- `factory-full-16mb.bin` +- `factory-full-16mb.bin.sha256` + +Восстановление: + +```bash +./restore_factory_backup.sh +``` + +## 5) Деплой (прошивка) — стандарт + +Главный скрипт: + +```bash +cd ESP32-S3-Touch-AMOLED-2.16/test-device +./burn.sh +``` + +Режимы: +- `hello` — базовый экран +- `widgets` — экран+touch+IMU (официальный пример) +- `audio` — тест аудио тракта +- `simple` — кастомный интеграционный тест (экран, touch, запись/воспроизведение, VU, tilt) + +## 6) Как писать код под эту плату (важно) + +1. **Экран** + - Рабочее разрешение использовать `480x480`. + - Не рисовать критичный текст/кнопки впритык к краю; держать safe margin (`~20px+`) из-за скругленных углов. + - Не делать полный `fillScreen` в каждом loop: только частичные обновления (`fillRect`/локальные перерисовки), иначе мерцание. + +2. **Touch** + - Настройка CST: + - `setMaxCoordinates(480, 480)` + - `setSwapXY(true)` + - `setMirrorXY(true, false)` + - Обрабатывать touch по IRQ + `getPoint`. + - После смещения UI обязательно пересчитывать hitbox кнопок. + +3. **Аудио** + - Для динамика инициализировать `ES8311`. + - Для микрофона обязательно инициализировать `ES7210`; без этого запись может быть пустой. + - Для отладки записи показывать VU/peak на экране во время `RECORD`. + - Для быстрой проверки тракта всегда держать кнопку `BEEP` (тон), чтобы отделить проблему динамика от проблемы микрофона. + +4. **IMU** + - QMI8658 обновлять с ограниченной частотой (например 80–150 мс для UI-строки), чтобы не шуметь перерисовками. + +5. **Стабильность UI** + - Статика: рисуется один раз в setup. + - Динамика: отдельная зона, перерисовывать только по изменению данных. + +## 7) Рекомендуемый workflow для Codex + +1. Проверить порт и инструменты. +2. Если новая плата/первый запуск — сделать backup flash. +3. Собрать и залить `simple`. +4. Пройти ручной чек: + - экран отображает текст без обрезки, + - touch срабатывает по кнопкам, + - `BEEP` слышно, + - VU двигается во время записи, + - `PLAY` воспроизводит записанное, + - `Tilt` меняется при повороте. +5. Только после этого усложнять приложение. + +## 8) Ссылки + +- Product page: https://www.waveshare.com/product/arduino/boards-kits/esp32-s3/esp32-s3-touch-amoled-2.16.htm +- Docs: https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-2.16 +- Arduino setup: https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-2.16/Development-Environment-Setup-Arduino +- Official examples: https://github.com/waveshareteam/ESP32-S3-Touch-AMOLED-2.16 diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/README.md b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/README.md new file mode 100644 index 0000000..77cf99d --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/README.md @@ -0,0 +1,25 @@ +# ESP32-S3-Touch-AMOLED-2.16 + +Подпроект для Waveshare `ESP32-S3-Touch-AMOLED-2.16`. + +Структура: + +- `official-demo/` — официальный репозиторий примеров Waveshare +- `original-firmware/` — резервная копия заводской прошивки +- `test-device/` — скрипты быстрой проверки устройства +- `reference/` — локальные заметки по документации и железу + +Примечание по git: + +- `official-demo/` держать как локальный внешний checkout из `https://github.com/waveshareteam/ESP32-S3-Touch-AMOLED-2.16`, в основной git его не добавлять. +- `original-firmware/*.bin` — локальный дамп конкретной платы, в git не добавлять. +- `.arduino-build/` и готовые `.bin/.elf/.map` — сборочные артефакты, в git не добавлять. + +Быстрый старт: + +1. Сделать backup текущей прошивки: + - `cd original-firmware && ./backup_factory.sh` +2. Залить тест экрана/тача: + - `cd ../test-device && ./burn.sh widgets` +3. Залить тест динамика: + - `cd ../test-device && ./burn.sh audio` diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/README.md b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/README.md new file mode 100644 index 0000000..7b64787 --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/README.md @@ -0,0 +1,11 @@ +# Factory Firmware Backup + +Здесь хранится полный дамп флеша текущего состояния платы. + +Файлы: +- `factory-full-16mb.bin` — полный дамп флеша `0x000000..0xFFFFFF` +- `factory-full-16mb.bin.sha256` — контрольная сумма + +Скрипты: +- `backup_factory.sh` — снять резервную копию +- `restore_factory_backup.sh` — восстановить резервную копию на плату diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/backup_factory.sh b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/backup_factory.sh new file mode 100755 index 0000000..39bdede --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/backup_factory.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PORT="${PORT:-/dev/ttyACM0}" +ESPTOOL="${ESPTOOL:-$HOME/.arduino15/packages/esp32/tools/esptool_py/5.1.0/esptool}" +BAUD="${BAUD:-921600}" +OUT_BIN="${ROOT_DIR}/factory-full-16mb.bin" + +echo "== Port: ${PORT}" +echo "== Output: ${OUT_BIN}" +echo "== Esptool: ${ESPTOOL}" +echo "== Baud: ${BAUD}" + +"${ESPTOOL}" --port "${PORT}" --baud "${BAUD}" read-flash 0 0x1000000 "${OUT_BIN}" +sha256sum "${OUT_BIN}" | tee "${OUT_BIN}.sha256" + +echo +echo "== Backup done." diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/restore_factory_backup.sh b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/restore_factory_backup.sh new file mode 100755 index 0000000..c607078 --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/original-firmware/restore_factory_backup.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PORT="${PORT:-/dev/ttyACM0}" +ESPTOOL="${ESPTOOL:-$HOME/.arduino15/packages/esp32/tools/esptool_py/5.1.0/esptool}" +IN_BIN="${IN_BIN:-${ROOT_DIR}/factory-full-16mb.bin}" + +if [[ ! -f "${IN_BIN}" ]]; then + echo "Backup file not found: ${IN_BIN}" >&2 + exit 1 +fi + +echo "== Port: ${PORT}" +echo "== Input: ${IN_BIN}" +echo "== Esptool: ${ESPTOOL}" + +"${ESPTOOL}" --port "${PORT}" --baud 921600 write-flash 0 "${IN_BIN}" + +echo +echo "== Restore done." diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/README.md b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/README.md new file mode 100644 index 0000000..571f438 --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/README.md @@ -0,0 +1,17 @@ +# Reference Notes + +Модель: Waveshare `ESP32-S3-Touch-AMOLED-2.16` (SKU 33969/33970) + +Полезные страницы: +- Product: https://www.waveshare.com/product/arduino/boards-kits/esp32-s3/esp32-s3-touch-amoled-2.16.htm +- Docs home: https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-2.16 +- Arduino setup: https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-2.16/Development-Environment-Setup-Arduino +- Official examples: https://github.com/waveshareteam/ESP32-S3-Touch-AMOLED-2.16 + +Ключевое по железу (по документации): +- ESP32-S3R8, 16MB flash, 8MB PSRAM +- AMOLED 2.16" 480x480 +- Capacitive touch (CST9220) +- IMU QMI8658 +- Audio codec ES8311 (playback) и ES7210 (microphones) +- TF slot (SD), сейчас без карты diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/README.md b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/README.md new file mode 100644 index 0000000..d8aae70 --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/README.md @@ -0,0 +1,18 @@ +# Test Device + +Скрипт заливает официальные Arduino-примеры для быстрой проверки платы. + +Для режимов `widgets`, `audio` и `hello` рядом должен лежать локальный checkout `official-demo/` из официального репозитория Waveshare. В основной git он не добавляется, потому что это большой внешний набор примеров, библиотек, прошивок и артефактов. + +Режимы: +- `widgets` — экран + touch + IMU (пример `05_LVGL_Widgets`) +- `audio` — динамик/аудио-кодек (пример `07_ES8311`) +- `hello` — базовый тест экрана (пример `01_HelloWorld`) +- `simple` — простой кастомный тест: экран + touch + запись/проигрывание + наклон (IMU) + +Запуск: + +- `./burn.sh widgets` +- `./burn.sh audio` +- `./burn.sh hello` +- `./burn.sh simple` diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/burn.sh b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/burn.sh new file mode 100755 index 0000000..e967baa --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/burn.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BOARD_DIR="$(cd "${ROOT_DIR}/.." && pwd)" +DEMO_BASE="${BOARD_DIR}/official-demo/examples/Arduino-v3.3.5" +MODE="${1:-widgets}" +PORT="${PORT:-/dev/ttyACM0}" +FQBN="${FQBN:-esp32:esp32:esp32s3:USBMode=hwcdc,CDCOnBoot=cdc,UploadSpeed=921600,CPUFreq=240,FlashMode=dio,FlashSize=16M,PartitionScheme=app3M_fat9M_16MB,PSRAM=opi}" +BUILD_DIR="${BUILD_DIR:-${ROOT_DIR}/.arduino-build/build-${MODE}}" +OUT_DIR="${OUT_DIR:-${ROOT_DIR}/.arduino-build/out-${MODE}}" + +case "${MODE}" in + hello) SKETCH_DIR="${DEMO_BASE}/examples/01_HelloWorld" ;; + widgets) SKETCH_DIR="${DEMO_BASE}/examples/05_LVGL_Widgets" ;; + audio) SKETCH_DIR="${DEMO_BASE}/examples/07_ES8311" ;; + simple) SKETCH_DIR="${ROOT_DIR}/simple_av_test" ;; + *) + echo "Unknown mode: ${MODE}" >&2 + echo "Use one of: hello, widgets, audio, simple" >&2 + exit 2 + ;; +esac + +echo "== Mode: ${MODE}" +echo "== Sketch: ${SKETCH_DIR}" +echo "== Port: ${PORT}" +echo "== FQBN: ${FQBN}" + +mkdir -p "${BUILD_DIR}" "${OUT_DIR}" + +arduino-cli compile \ + --clean \ + --fqbn "${FQBN}" \ + --build-path "${BUILD_DIR}" \ + --output-dir "${OUT_DIR}" \ + --library "${DEMO_BASE}/libraries/GFX_Library_for_Arduino" \ + --library "${DEMO_BASE}/libraries/SensorLib" \ + --library "${DEMO_BASE}/libraries/XPowersLib" \ + --library "${DEMO_BASE}/libraries/lvgl" \ + --library "${DEMO_BASE}/libraries/Mylibrary" \ + "${SKETCH_DIR}" + +arduino-cli upload \ + -p "${PORT}" \ + --fqbn "${FQBN}" \ + --input-dir "${OUT_DIR}" \ + "${SKETCH_DIR}" + +echo +echo "== Done." diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/audio_hal.h b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/audio_hal.h new file mode 100644 index 0000000..7ed0f89 --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/audio_hal.h @@ -0,0 +1,136 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _AUDIO_HAL_H_ +#define _AUDIO_HAL_H_ + +#define AUDIO_HAL_VOL_DEFAULT 60 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Select media hal codec mode + */ +typedef enum { + AUDIO_HAL_CODEC_MODE_ENCODE = 1, /*!< select adc */ + AUDIO_HAL_CODEC_MODE_DECODE, /*!< select dac */ + AUDIO_HAL_CODEC_MODE_BOTH, /*!< select both adc and dac */ + AUDIO_HAL_CODEC_MODE_LINE_IN, /*!< set adc channel */ +} audio_hal_codec_mode_t; + +/** + * @brief Select adc channel for input mic signal + */ +typedef enum { + AUDIO_HAL_ADC_INPUT_LINE1 = 0x00, /*!< mic input to adc channel 1 */ + AUDIO_HAL_ADC_INPUT_LINE2, /*!< mic input to adc channel 2 */ + AUDIO_HAL_ADC_INPUT_ALL, /*!< mic input to both channels of adc */ + AUDIO_HAL_ADC_INPUT_DIFFERENCE, /*!< mic input to adc difference channel */ +} audio_hal_adc_input_t; + +/** + * @brief Select channel for dac output + */ +typedef enum { + AUDIO_HAL_DAC_OUTPUT_LINE1 = 0x00, /*!< dac output signal to channel 1 */ + AUDIO_HAL_DAC_OUTPUT_LINE2, /*!< dac output signal to channel 2 */ + AUDIO_HAL_DAC_OUTPUT_ALL, /*!< dac output signal to both channels */ +} audio_hal_dac_output_t; + +/** + * @brief Select operating mode i.e. start or stop for audio codec chip + */ +typedef enum { + AUDIO_HAL_CTRL_STOP = 0x00, /*!< set stop mode */ + AUDIO_HAL_CTRL_START = 0x01, /*!< set start mode */ +} audio_hal_ctrl_t; + +/** + * @brief Select I2S interface operating mode i.e. master or slave for audio codec chip + */ +typedef enum { + AUDIO_HAL_MODE_SLAVE = 0x00, /*!< set slave mode */ + AUDIO_HAL_MODE_MASTER = 0x01, /*!< set master mode */ +} audio_hal_iface_mode_t; + +/** + * @brief Select I2S interface samples per second + */ +typedef enum { + AUDIO_HAL_08K_SAMPLES, /*!< set to 8k samples per second */ + AUDIO_HAL_11K_SAMPLES, /*!< set to 11.025k samples per second */ + AUDIO_HAL_16K_SAMPLES, /*!< set to 16k samples in per second */ + AUDIO_HAL_22K_SAMPLES, /*!< set to 22.050k samples per second */ + AUDIO_HAL_24K_SAMPLES, /*!< set to 24k samples in per second */ + AUDIO_HAL_32K_SAMPLES, /*!< set to 32k samples in per second */ + AUDIO_HAL_44K_SAMPLES, /*!< set to 44.1k samples per second */ + AUDIO_HAL_48K_SAMPLES, /*!< set to 48k samples per second */ +} audio_hal_iface_samples_t; + +/** + * @brief Select I2S interface number of bits per sample + */ +typedef enum { + AUDIO_HAL_BIT_LENGTH_16BITS = 1, /*!< set 16 bits per sample */ + AUDIO_HAL_BIT_LENGTH_24BITS, /*!< set 24 bits per sample */ + AUDIO_HAL_BIT_LENGTH_32BITS, /*!< set 32 bits per sample */ +} audio_hal_iface_bits_t; + +/** + * @brief Select I2S interface format for audio codec chip + */ +typedef enum { + AUDIO_HAL_I2S_NORMAL = 0, /*!< set normal I2S format */ + AUDIO_HAL_I2S_LEFT, /*!< set all left format */ + AUDIO_HAL_I2S_RIGHT, /*!< set all right format */ + AUDIO_HAL_I2S_DSP, /*!< set dsp/pcm format */ +} audio_hal_iface_format_t; + +/** + * @brief I2s interface configuration for audio codec chip + */ +typedef struct { + audio_hal_iface_mode_t mode; /*!< audio codec chip mode */ + audio_hal_iface_format_t fmt; /*!< I2S interface format */ + audio_hal_iface_samples_t samples; /*!< I2S interface samples per second */ + audio_hal_iface_bits_t bits; /*!< i2s interface number of bits per sample */ +} audio_hal_codec_i2s_iface_t; + +/** + * @brief Configure media hal for initialization of audio codec chip + */ +typedef struct { + audio_hal_adc_input_t adc_input; /*!< set adc channel */ + audio_hal_dac_output_t dac_output; /*!< set dac channel */ + audio_hal_codec_mode_t codec_mode; /*!< select codec mode: adc, dac or both */ + audio_hal_codec_i2s_iface_t i2s_iface; /*!< set I2S interface configuration */ +} audio_hal_codec_config_t; + +#ifdef __cplusplus +} +#endif + +#endif //__AUDIO_HAL_H__ diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es7210.cpp b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es7210.cpp new file mode 100644 index 0000000..c0569cc --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es7210.cpp @@ -0,0 +1,549 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifdef ESP32 + +#include +#include +#include "esp_log.h" +#include "es7210.h" + + +#define I2S_DSP_MODE_A 0 +#define MCLK_DIV_FRE 256 + + +#define ES7210_MCLK_SOURCE FROM_CLOCK_DOUBLE_PIN /* In master mode, 0 : MCLK from pad 1 : MCLK from clock doubler */ +#define FROM_PAD_PIN 0 +#define FROM_CLOCK_DOUBLE_PIN 1 + + +static TwoWire *es7210wire; +static es7210_gain_value_t gain; + +/* + * Clock coefficient structer + */ +struct _coeff_div_es7210 { + uint32_t mclk; /* mclk frequency */ + uint32_t lrck; /* lrck */ + uint8_t ss_ds; + uint8_t adc_div; /* adcclk divider */ + uint8_t dll; /* dll_bypass */ + uint8_t doubler; /* doubler enable */ + uint8_t osr; /* adc osr */ + uint8_t mclk_src; /* select mclk source */ + uint32_t lrck_h; /* The high 4 bits of lrck */ + uint32_t lrck_l; /* The low 8 bits of lrck */ +}; + +static const char *TAG = "ES7210"; + +static es7210_input_mics_t mic_select = (es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4); + +/* Codec hifi mclk clock divider coefficients + * MEMBER REG + * mclk: 0x03 + * lrck: standard + * ss_ds: -- + * adc_div: 0x02 + * dll: 0x06 + * doubler: 0x02 + * osr: 0x07 + * mclk_src: 0x03 + * lrckh: 0x04 + * lrckl: 0x05 +*/ +static const struct _coeff_div_es7210 coeff_div[] = { + //mclk lrck ss_ds adc_div dll doubler osr mclk_src lrckh lrckl + /* 8k */ + {12288000, 8000 , 0x00, 0x03, 0x01, 0x00, 0x20, 0x00, 0x06, 0x00}, + {16384000, 8000 , 0x00, 0x04, 0x01, 0x00, 0x20, 0x00, 0x08, 0x00}, + {19200000, 8000 , 0x00, 0x1e, 0x00, 0x01, 0x28, 0x00, 0x09, 0x60}, + {4096000, 8000 , 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + + /* 11.025k */ + {11289600, 11025, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00}, + + /* 12k */ + {12288000, 12000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00}, + {19200000, 12000, 0x00, 0x14, 0x00, 0x01, 0x28, 0x00, 0x06, 0x40}, + + /* 16k */ + {4096000, 16000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, + {19200000, 16000, 0x00, 0x0a, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x80}, + {16384000, 16000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00}, + {12288000, 16000, 0x00, 0x03, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00}, + + /* 22.05k */ + {11289600, 22050, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + + /* 24k */ + {12288000, 24000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + {19200000, 24000, 0x00, 0x0a, 0x00, 0x01, 0x28, 0x00, 0x03, 0x20}, + + /* 32k */ + {12288000, 32000, 0x00, 0x03, 0x00, 0x00, 0x20, 0x00, 0x01, 0x80}, + {16384000, 32000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + {19200000, 32000, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x58}, + + /* 44.1k */ + {11289600, 44100, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, + + /* 48k */ + {12288000, 48000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, + {19200000, 48000, 0x00, 0x05, 0x00, 0x01, 0x28, 0x00, 0x01, 0x90}, + + /* 64k */ + {16384000, 64000, 0x01, 0x01, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00}, + {19200000, 64000, 0x00, 0x05, 0x00, 0x01, 0x1e, 0x00, 0x01, 0x2c}, + + /* 88.2k */ + {11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80}, + + /* 96k */ + {12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80}, + {19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8}, +}; + +static esp_err_t es7210_write_reg(uint8_t reg_addr, uint8_t data) +{ + + es7210wire->beginTransmission(ES7210_ADDR); + es7210wire->write(reg_addr); + es7210wire->write(data); + return es7210wire->endTransmission(); + +} + +static esp_err_t es7210_update_reg_bit(uint8_t reg_addr, uint8_t update_bits, uint8_t data) +{ + uint8_t regv; + regv = es7210_read_reg(reg_addr); + regv = (regv & (~update_bits)) | (update_bits & data); + return es7210_write_reg(reg_addr, regv); +} + +static int get_coeff(uint32_t mclk, uint32_t lrck) +{ + for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) { + if (coeff_div[i].lrck == lrck && coeff_div[i].mclk == mclk) + return i; + } + return -1; +} + +int8_t get_es7210_mclk_src(void) +{ + return ES7210_MCLK_SOURCE; +} + +int es7210_read_reg(uint8_t reg_addr) +{ + uint8_t data; + es7210wire->beginTransmission(ES7210_ADDR); + es7210wire->write(reg_addr); + es7210wire->endTransmission(false); + es7210wire->requestFrom(ES7210_ADDR, (size_t)1); + data = es7210wire->read(); + return (int)data; +} + +esp_err_t es7210_config_sample(audio_hal_iface_samples_t sample) +{ + uint8_t regv; + int coeff; + int sample_fre = 0; + int mclk_fre = 0; + esp_err_t ret = ESP_OK; + switch (sample) { + case AUDIO_HAL_08K_SAMPLES: + sample_fre = 8000; + break; + case AUDIO_HAL_11K_SAMPLES: + sample_fre = 11025; + break; + case AUDIO_HAL_16K_SAMPLES: + sample_fre = 16000; + break; + case AUDIO_HAL_22K_SAMPLES: + sample_fre = 22050; + break; + case AUDIO_HAL_24K_SAMPLES: + sample_fre = 24000; + break; + case AUDIO_HAL_32K_SAMPLES: + sample_fre = 32000; + break; + case AUDIO_HAL_44K_SAMPLES: + sample_fre = 44100; + break; + case AUDIO_HAL_48K_SAMPLES: + sample_fre = 48000; + break; + default: + ESP_LOGE(TAG, "Unable to configure sample rate %dHz", sample_fre); + break; + } + mclk_fre = sample_fre * MCLK_DIV_FRE; + coeff = get_coeff(mclk_fre, sample_fre); + if (coeff < 0) { + ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_fre, mclk_fre); + return ESP_FAIL; + } + /* Set clock parammeters */ + if (coeff >= 0) { + /* Set adc_div & doubler & dll */ + regv = es7210_read_reg(ES7210_MAINCLK_REG02) & 0x00; + regv |= coeff_div[coeff].adc_div; + regv |= coeff_div[coeff].doubler << 6; + regv |= coeff_div[coeff].dll << 7; + ret |= es7210_write_reg(ES7210_MAINCLK_REG02, regv); + /* Set osr */ + regv = coeff_div[coeff].osr; + ret |= es7210_write_reg(ES7210_OSR_REG07, regv); + /* Set lrck */ + regv = coeff_div[coeff].lrck_h; + ret |= es7210_write_reg(ES7210_LRCK_DIVH_REG04, regv); + regv = coeff_div[coeff].lrck_l; + ret |= es7210_write_reg(ES7210_LRCK_DIVL_REG05, regv); + } + return ret; +} + +esp_err_t es7210_mic_select(es7210_input_mics_t mic) +{ + esp_err_t ret = ESP_OK; + mic_select = mic; + if (mic_select & (ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4)) { + for (int i = 0; i < 4; i++) { + ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00); + } + ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0xff); + ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0xff); + if (mic_select & ES7210_INPUT_MIC1) { + ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC1"); + ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00); + ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0x00); + ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x10, 0x10); + } + if (mic_select & ES7210_INPUT_MIC2) { + ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC2"); + ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00); + ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0x00); + ret |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x10, 0x10); + } + if (mic_select & ES7210_INPUT_MIC3) { + ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC3"); + ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x15, 0x00); + ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0x00); + ret |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x10, 0x10); + } + if (mic_select & ES7210_INPUT_MIC4) { + ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC4"); + ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x15, 0x00); + ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0x00); + ret |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x10, 0x10); + } + } else { + ESP_LOGE(TAG, "Microphone selection error"); + return ESP_FAIL; + } + return ret; +} + +esp_err_t es7210_adc_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg) +{ + esp_err_t ret = ESP_OK; + + es7210wire = tw; + + ret |= es7210_write_reg(ES7210_RESET_REG00, 0xff); + ret |= es7210_write_reg(ES7210_RESET_REG00, 0x41); + ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, 0x1f); + ret |= es7210_write_reg(ES7210_TIME_CONTROL0_REG09, 0x30); /* Set chip state cycle */ + ret |= es7210_write_reg(ES7210_TIME_CONTROL1_REG0A, 0x30); /* Set power on state cycle */ + // ret |= es7210_write_reg(ES7210_ADC12_HPF2_REG23, 0x2a); /* Quick setup */ + // ret |= es7210_write_reg(ES7210_ADC12_HPF1_REG22, 0x0a); + // ret |= es7210_write_reg(ES7210_ADC34_HPF2_REG20, 0x0a); + // ret |= es7210_write_reg(ES7210_ADC34_HPF1_REG21, 0x2a); + /* Set master/slave audio interface */ + audio_hal_codec_i2s_iface_t *i2s_cfg = & (codec_cfg->i2s_iface); + switch (i2s_cfg->mode) { + case AUDIO_HAL_MODE_MASTER: /* MASTER MODE */ + ESP_LOGI(TAG, "ES7210 in Master mode"); + // ret |= es7210_update_reg_bit(ES7210_MODE_CONFIG_REG08, 0x01, 0x01); + ret |= es7210_write_reg(ES7210_MODE_CONFIG_REG08, 0x20); + /* Select clock source for internal mclk */ + switch (get_es7210_mclk_src()) { + case FROM_PAD_PIN: + ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x00); + break; + case FROM_CLOCK_DOUBLE_PIN: + ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x80); + break; + default: + ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x00); + break; + } + break; + case AUDIO_HAL_MODE_SLAVE: /* SLAVE MODE */ + ESP_LOGI(TAG, "ES7210 in Slave mode"); + break; + default: + break; + } + ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0xC3); /* Select power off analog, vdda = 3.3V, close vx20ff, VMID select 5KΩ start */ + ret |= es7210_write_reg(ES7210_MIC12_BIAS_REG41, 0x70); /* Select 2.87v */ + ret |= es7210_write_reg(ES7210_MIC34_BIAS_REG42, 0x70); /* Select 2.87v */ + ret |= es7210_write_reg(ES7210_OSR_REG07, 0x20); + ret |= es7210_write_reg(ES7210_MAINCLK_REG02, 0xc1); /* Set the frequency division coefficient and use dll except clock doubler, and need to set 0xc1 to clear the state */ + ret |= es7210_config_sample(i2s_cfg->samples); + ret |= es7210_mic_select(mic_select); + ret |= es7210_adc_set_gain_all(GAIN_0DB); + return ESP_OK; +} + +esp_err_t es7210_adc_deinit() +{ + return ESP_OK; +} + +esp_err_t es7210_config_fmt(audio_hal_iface_format_t fmt) +{ + esp_err_t ret = ESP_OK; + uint8_t adc_iface = 0; + adc_iface = es7210_read_reg(ES7210_SDP_INTERFACE1_REG11); + adc_iface &= 0xfc; + switch (fmt) { + case AUDIO_HAL_I2S_NORMAL: + ESP_LOGD(TAG, "ES7210 in I2S Format"); + adc_iface |= 0x00; + break; + case AUDIO_HAL_I2S_LEFT: + case AUDIO_HAL_I2S_RIGHT: + ESP_LOGD(TAG, "ES7210 in LJ Format"); + adc_iface |= 0x01; + break; + case AUDIO_HAL_I2S_DSP: + if (I2S_DSP_MODE_A) { + ESP_LOGD(TAG, "ES7210 in DSP-A Format"); + adc_iface |= 0x03; + } else { + ESP_LOGD(TAG, "ES7210 in DSP-B Format"); + adc_iface |= 0x13; + } + break; + default: + adc_iface &= 0xfc; + break; + } + ret |= es7210_write_reg(ES7210_SDP_INTERFACE1_REG11, adc_iface); + /* Force ADC1/2 output to SDOUT1 and ADC3/4 output to SDOUT2 */ + ret |= es7210_write_reg(ES7210_SDP_INTERFACE2_REG12, 0x00); + return ret; +} + +esp_err_t es7210_set_bits(audio_hal_iface_bits_t bits) +{ + esp_err_t ret = ESP_OK; + uint8_t adc_iface = 0; + adc_iface = es7210_read_reg(ES7210_SDP_INTERFACE1_REG11); + adc_iface &= 0x1f; + switch (bits) { + case AUDIO_HAL_BIT_LENGTH_16BITS: + adc_iface |= 0x60; + break; + case AUDIO_HAL_BIT_LENGTH_24BITS: + adc_iface |= 0x00; + break; + case AUDIO_HAL_BIT_LENGTH_32BITS: + adc_iface |= 0x80; + break; + default: + adc_iface |= 0x60; + break; + } + ret |= es7210_write_reg(ES7210_SDP_INTERFACE1_REG11, adc_iface); + return ret; +} + +esp_err_t es7210_adc_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface) +{ + esp_err_t ret = ESP_OK; + ret |= es7210_set_bits(iface->bits); + ret |= es7210_config_fmt(iface->fmt); + ret |= es7210_config_sample(iface->samples); + return ret; +} + +esp_err_t es7210_start(uint8_t clock_reg_value) +{ + esp_err_t ret = ESP_OK; + ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, clock_reg_value); + ret |= es7210_write_reg(ES7210_POWER_DOWN_REG06, 0x00); + // ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0x40); + ret |= es7210_write_reg(ES7210_MIC1_POWER_REG47, 0x00); + ret |= es7210_write_reg(ES7210_MIC2_POWER_REG48, 0x00); + ret |= es7210_write_reg(ES7210_MIC3_POWER_REG49, 0x00); + ret |= es7210_write_reg(ES7210_MIC4_POWER_REG4A, 0x00); + ret |= es7210_mic_select(mic_select); + return ret; +} + +esp_err_t es7210_stop(void) +{ + esp_err_t ret = ESP_OK; + ret |= es7210_write_reg(ES7210_MIC1_POWER_REG47, 0xff); + ret |= es7210_write_reg(ES7210_MIC2_POWER_REG48, 0xff); + ret |= es7210_write_reg(ES7210_MIC3_POWER_REG49, 0xff); + ret |= es7210_write_reg(ES7210_MIC4_POWER_REG4A, 0xff); + ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B,0xff); + ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0xff); + // ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0xc0); + ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, 0x7f); + ret |= es7210_write_reg(ES7210_POWER_DOWN_REG06, 0x07); + return ret; +} + +esp_err_t es7210_adc_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state) +{ + static uint8_t regv; + esp_err_t ret = ESP_OK; + // ESP_LOGW(TAG, "ES7210 only supports ADC mode"); + ret = es7210_read_reg(ES7210_CLOCK_OFF_REG01); + if ((ret != 0x7f) && (ret != 0xff)) { + regv = es7210_read_reg(ES7210_CLOCK_OFF_REG01); + } + if (ctrl_state == AUDIO_HAL_CTRL_START) { + ESP_LOGI(TAG, "The ES7210_CLOCK_OFF_REG01 value before stop is %x",regv); + ret |= es7210_start(regv); + } else { + ESP_LOGW(TAG, "The codec is about to stop"); + regv = es7210_read_reg(ES7210_CLOCK_OFF_REG01); + ret |= es7210_stop(); + } + return ESP_OK; +} + +esp_err_t es7210_adc_set_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t gain) +{ + esp_err_t ret_val = ESP_OK; + + if (gain < GAIN_0DB) { + gain = GAIN_0DB; + } + + if (gain > GAIN_37_5DB) { + gain = GAIN_37_5DB; + } + + if (mic_mask & ES7210_INPUT_MIC1) { + ret_val |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x0f, gain); + } + if (mic_mask & ES7210_INPUT_MIC2) { + ret_val |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x0f, gain); + } + if (mic_mask & ES7210_INPUT_MIC3) { + ret_val |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x0f, gain); + } + if (mic_mask & ES7210_INPUT_MIC4) { + ret_val |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x0f, gain); + } + + return ret_val; +} + +esp_err_t es7210_adc_set_gain_all(es7210_gain_value_t gain) +{ + esp_err_t ret = ESP_OK; + uint32_t max_gain_vaule = 14; + if (gain < 0) { + gain = (es7210_gain_value_t) 0; + } else if (gain > max_gain_vaule) { + gain = (es7210_gain_value_t) max_gain_vaule; + } + ESP_LOGD(TAG, "SET: gain:%d", gain); + if (mic_select & ES7210_INPUT_MIC1) { + ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x0f, gain); + } + if (mic_select & ES7210_INPUT_MIC2) { + ret |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x0f, gain); + } + if (mic_select & ES7210_INPUT_MIC3) { + ret |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x0f, gain); + } + if (mic_select & ES7210_INPUT_MIC4) { + ret |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x0f, gain); + } + return ret; +} + +esp_err_t es7210_adc_get_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t *gain) +{ + int regv = 0; + uint8_t gain_value; + if (mic_mask & ES7210_INPUT_MIC1) { + regv = es7210_read_reg(ES7210_MIC1_GAIN_REG43); + } else if (mic_mask & ES7210_INPUT_MIC2) { + regv = es7210_read_reg(ES7210_MIC2_GAIN_REG44); + } else if (mic_mask & ES7210_INPUT_MIC3) { + regv = es7210_read_reg(ES7210_MIC3_GAIN_REG45); + } else if (mic_mask & ES7210_INPUT_MIC4) { + regv = es7210_read_reg(ES7210_MIC4_GAIN_REG46); + } else { + ESP_LOGE(TAG, "No MIC selected"); + return ESP_FAIL; + } + if (regv == ESP_FAIL) { + return regv; + } + gain_value = (regv & 0x0f); /* Retain the last four bits for gain */ + *gain = (es7210_gain_value_t) gain_value; + ESP_LOGI(TAG, "GET: gain_value:%d", gain_value); + return ESP_OK; +} + +esp_err_t es7210_adc_set_volume(int volume) +{ + esp_err_t ret = ESP_OK; + ESP_LOGD(TAG, "ADC can adjust gain"); + return ret; +} + +esp_err_t es7210_set_mute(bool enable) +{ + ESP_LOGD(TAG, "ES7210 SetMute :%d", enable); + return ESP_OK; +} + +void es7210_read_all(void) +{ + for (int i = 0; i <= 0x4E; i++) { + uint8_t reg = es7210_read_reg(i); + ets_printf("REG:%02x, %02x\n", reg, i); + } +} + +#endif diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es7210.h b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es7210.h new file mode 100644 index 0000000..58efe86 --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es7210.h @@ -0,0 +1,260 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _ES7210_H +#define _ES7210_H + +#include "audio_hal.h" +#include + +typedef enum { + ES7210_AD1_AD0_00 = 0x40, + ES7210_AD1_AD0_01 = 0x41, + ES7210_AD1_AD0_10 = 0x42, + ES7210_AD1_AD0_11 = 0x43, +} es7210_address_t; + +/* ES7210 address*/ +#define ES7210_ADDR ES7210_AD1_AD0_00 + +#ifdef __cplusplus +extern "C" { +#endif + +#define ES7210_RESET_REG00 0x00 /* Reset control */ +#define ES7210_CLOCK_OFF_REG01 0x01 /* Used to turn off the ADC clock */ +#define ES7210_MAINCLK_REG02 0x02 /* Set ADC clock frequency division */ +#define ES7210_MASTER_CLK_REG03 0x03 /* MCLK source $ SCLK division */ +#define ES7210_LRCK_DIVH_REG04 0x04 /* lrck_divh */ +#define ES7210_LRCK_DIVL_REG05 0x05 /* lrck_divl */ +#define ES7210_POWER_DOWN_REG06 0x06 /* power down */ +#define ES7210_OSR_REG07 0x07 +#define ES7210_MODE_CONFIG_REG08 0x08 /* Set master/slave & channels */ +#define ES7210_TIME_CONTROL0_REG09 0x09 /* Set Chip intial state period*/ +#define ES7210_TIME_CONTROL1_REG0A 0x0A /* Set Power up state period */ +#define ES7210_SDP_INTERFACE1_REG11 0x11 /* Set sample & fmt */ +#define ES7210_SDP_INTERFACE2_REG12 0x12 /* Pins state */ +#define ES7210_ADC_AUTOMUTE_REG13 0x13 /* Set mute */ +#define ES7210_ADC34_MUTERANGE_REG14 0x14 /* Set mute range */ +#define ES7210_ADC34_HPF2_REG20 0x20 /* HPF */ +#define ES7210_ADC34_HPF1_REG21 0x21 +#define ES7210_ADC12_HPF1_REG22 0x22 +#define ES7210_ADC12_HPF2_REG23 0x23 +#define ES7210_ANALOG_REG40 0x40 /* ANALOG Power */ +#define ES7210_MIC12_BIAS_REG41 0x41 +#define ES7210_MIC34_BIAS_REG42 0x42 +#define ES7210_MIC1_GAIN_REG43 0x43 +#define ES7210_MIC2_GAIN_REG44 0x44 +#define ES7210_MIC3_GAIN_REG45 0x45 +#define ES7210_MIC4_GAIN_REG46 0x46 +#define ES7210_MIC1_POWER_REG47 0x47 +#define ES7210_MIC2_POWER_REG48 0x48 +#define ES7210_MIC3_POWER_REG49 0x49 +#define ES7210_MIC4_POWER_REG4A 0x4A +#define ES7210_MIC12_POWER_REG4B 0x4B /* MICBias & ADC & PGA Power */ +#define ES7210_MIC34_POWER_REG4C 0x4C + + + +typedef enum { + ES7210_INPUT_MIC1 = 0x01, + ES7210_INPUT_MIC2 = 0x02, + ES7210_INPUT_MIC3 = 0x04, + ES7210_INPUT_MIC4 = 0x08 +} es7210_input_mics_t; + +typedef enum gain_value{ + GAIN_0DB = 0, + GAIN_3DB, + GAIN_6DB, + GAIN_9DB, + GAIN_12DB, + GAIN_15DB, + GAIN_18DB, + GAIN_21DB, + GAIN_24DB, + GAIN_27DB, + GAIN_30DB, + GAIN_33DB, + GAIN_34_5DB, + GAIN_36DB, + GAIN_37_5DB, +} es7210_gain_value_t; + +/* + * @brief Initialize ES7210 ADC chip + * + * @param[in] codec_cfg: configuration of ES7210 + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es7210_adc_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg); + +/** + * @brief Deinitialize ES7210 ADC chip + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es7210_adc_deinit(); + +/** + * @brief Configure ES7210 ADC mode and I2S interface + * + * @param[in] mode: codec mode + * @param[in] iface: I2S config + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +esp_err_t es7210_adc_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface); + +/** + * @brief Control ES7210 ADC chip + * + * @param[in] mode: codec mode + * @param[in] ctrl_state: start or stop progress + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +esp_err_t es7210_adc_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state); + +/** + * @brief Set gain of given mask + * + * @param[in] mic_mask Mask of MIC channel + * + * @param[in] gain: gain + * + * gain : value + * GAIN_0DB : 1 + * GAIN_3DB : 2 + * GAIN_6DB : 3 + * · + * · + * · + * GAIN_30DB : 10 + * GAIN_33DB : 11 + * GAIN_34_5DB : 12 + * GAIN_36DB : 13 + * GAIN_37_5DB : 14 + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es7210_adc_set_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t gain); + +/** + * @brief Set gain (Note: the enabled microphone sets the same gain) + * + * @param[in] gain: gain + * + * gain : value + * GAIN_0DB : 1 + * GAIN_3DB : 2 + * GAIN_6DB : 3 + * · + * · + * · + * GAIN_30DB : 10 + * GAIN_33DB : 11 + * GAIN_34_5DB : 12 + * GAIN_36DB : 13 + * GAIN_37_5DB : 14 + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es7210_adc_set_gain_all(es7210_gain_value_t gain); + +/** + * @brief Get MIC gain + * + * @param mic_mask Selected MIC + * @param gain Pointer to `es7210_gain_value_t` + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es7210_adc_get_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t *gain); + +/** + * @brief Set volume + * + * @param[in] volume: volume + * + * @return + * - ESP_OK + */ +esp_err_t es7210_adc_set_volume(int volume); + +/** + * @brief Set ES7210 ADC mute status + * + * @return + * - ESP_FAIL + * - ESP_OK + */ +esp_err_t es7210_set_mute(bool enable); + +/** + * @brief Select ES7210 mic + * + * @param[in] mic: mics + * + * @return + * - ESP_FAIL + * - ESP_OK + */ +esp_err_t es7210_mic_select(es7210_input_mics_t mic); + +/** + * @brief Read regs of ES7210 + * + * @param[in] reg_addr: reg_addr + * + * @return + * - ESP_FAIL + * - ESP_OK + */ +int es7210_read_reg(uint8_t reg_addr); + +/** + * @brief Read all regs of ES7210 + */ +void es7210_read_all(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _ES7210_H_ */ diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es8311.c b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es8311.c new file mode 100644 index 0000000..6b83860 --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es8311.c @@ -0,0 +1,443 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "es8311.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "esp_err.h" +#include "esp_check.h" +#include "es8311_reg.h" +#include "esp32-hal-i2c.h" + + +typedef struct { + unsigned int port; + uint16_t dev_addr; +} es8311_dev_t; + +/* + * Clock coefficient structure + */ +struct _coeff_div { + uint32_t mclk; /* mclk frequency */ + uint32_t rate; /* sample rate */ + uint8_t pre_div; /* the pre divider with range from 1 to 8 */ + uint8_t pre_multi; /* the pre multiplier with 0: 1x, 1: 2x, 2: 4x, 3: 8x selection */ + uint8_t adc_div; /* adcclk divider */ + uint8_t dac_div; /* dacclk divider */ + uint8_t fs_mode; /* double speed or single speed, =0, ss, =1, ds */ + uint8_t lrck_h; /* adclrck divider and daclrck divider */ + uint8_t lrck_l; + uint8_t bclk_div; /* sclk divider */ + uint8_t adc_osr; /* adc osr */ + uint8_t dac_osr; /* dac osr */ +}; + +/* codec hifi mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /*!port, es->dev_addr, write_buf, sizeof(write_buf), 1000); +} + +static inline esp_err_t es8311_read_reg(es8311_handle_t dev, uint8_t reg_addr, uint8_t *reg_value) +{ + es8311_dev_t *es = (es8311_dev_t *) dev; + size_t readCount = 0; + return i2cWriteReadNonStop(es->port, es->dev_addr, ®_addr, 1, reg_value, 1, 1000, &readCount); +} + +/* +* look for the coefficient in coeff_div[] table +*/ +static int get_coeff(uint32_t mclk, uint32_t rate) +{ + for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) { + return i; + } + } + + return -1; +} + +esp_err_t es8311_sample_frequency_config(es8311_handle_t dev, int mclk_frequency, int sample_frequency) +{ + uint8_t regv; + + /* Get clock coefficients from coefficient table */ + int coeff = get_coeff(mclk_frequency, sample_frequency); + + if (coeff < 0) { + ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_frequency, mclk_frequency); + return ESP_ERR_INVALID_ARG; + } + + const struct _coeff_div *const selected_coeff = &coeff_div[coeff]; + + /* register 0x02 */ + ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG02, ®v), TAG, "I2C read/write error"); + regv &= 0x07; + regv |= (selected_coeff->pre_div - 1) << 5; + regv |= selected_coeff->pre_multi << 3; + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG02, regv), TAG, "I2C read/write error"); + + /* register 0x03 */ + const uint8_t reg03 = (selected_coeff->fs_mode << 6) | selected_coeff->adc_osr; + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG03, reg03), TAG, "I2C read/write error"); + + /* register 0x04 */ + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG04, selected_coeff->dac_osr), TAG, "I2C read/write error"); + + /* register 0x05 */ + const uint8_t reg05 = ((selected_coeff->adc_div - 1) << 4) | (selected_coeff->dac_div - 1); + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG05, reg05), TAG, "I2C read/write error"); + + /* register 0x06 */ + ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG06, ®v), TAG, "I2C read/write error"); + regv &= 0xE0; + + if (selected_coeff->bclk_div < 19) { + regv |= (selected_coeff->bclk_div - 1) << 0; + } else { + regv |= (selected_coeff->bclk_div) << 0; + } + + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG06, regv), TAG, "I2C read/write error"); + + /* register 0x07 */ + ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG07, ®v), TAG, "I2C read/write error"); + regv &= 0xC0; + regv |= selected_coeff->lrck_h << 0; + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG07, regv), TAG, "I2C read/write error"); + + /* register 0x08 */ + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG08, selected_coeff->lrck_l), TAG, "I2C read/write error"); + + return ESP_OK; +} + +static esp_err_t es8311_clock_config(es8311_handle_t dev, const es8311_clock_config_t *const clk_cfg, es8311_resolution_t res) +{ + uint8_t reg06; + uint8_t reg01 = 0x3F; // Enable all clocks + int mclk_hz; + + /* Select clock source for internal MCLK and determine its frequency */ + if (clk_cfg->mclk_from_mclk_pin) { + mclk_hz = clk_cfg->mclk_frequency; + } else { + mclk_hz = clk_cfg->sample_frequency * (int)res * 2; + reg01 |= BIT(7); // Select BCLK (a.k.a. SCK) pin + } + + if (clk_cfg->mclk_inverted) { + reg01 |= BIT(6); // Invert MCLK pin + } + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG01, reg01), TAG, "I2C read/write error"); + + ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG06, ®06), TAG, "I2C read/write error"); + if (clk_cfg->sclk_inverted) { + reg06 |= BIT(5); + } else { + reg06 &= ~BIT(5); + } + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG06, reg06), TAG, "I2C read/write error"); + + /* Configure clock dividers */ + return es8311_sample_frequency_config(dev, mclk_hz, clk_cfg->sample_frequency); +} + +static esp_err_t es8311_resolution_config(const es8311_resolution_t res, uint8_t *reg) +{ + switch (res) { + case ES8311_RESOLUTION_16: + *reg |= (3 << 2); + break; + case ES8311_RESOLUTION_18: + *reg |= (2 << 2); + break; + case ES8311_RESOLUTION_20: + *reg |= (1 << 2); + break; + case ES8311_RESOLUTION_24: + *reg |= (0 << 2); + break; + case ES8311_RESOLUTION_32: + *reg |= (4 << 2); + break; + default: + return ESP_ERR_INVALID_ARG; + } + return ESP_OK; +} + +static esp_err_t es8311_fmt_config(es8311_handle_t dev, const es8311_resolution_t res_in, const es8311_resolution_t res_out) +{ + uint8_t reg09 = 0; // SDP In + uint8_t reg0a = 0; // SDP Out + + ESP_LOGI(TAG, "ES8311 in Slave mode and I2S format"); + uint8_t reg00; + ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_RESET_REG00, ®00), TAG, "I2C read/write error"); + reg00 &= 0xBF; + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, reg00), TAG, "I2C read/write error"); // Slave serial port - default + + /* Setup SDP In and Out resolution */ + es8311_resolution_config(res_in, ®09); + es8311_resolution_config(res_out, ®0a); + + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SDPIN_REG09, reg09), TAG, "I2C read/write error"); + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SDPOUT_REG0A, reg0a), TAG, "I2C read/write error"); + + return ESP_OK; +} + +esp_err_t es8311_microphone_config(es8311_handle_t dev, bool digital_mic) +{ + uint8_t reg14 = 0x1A; // enable analog MIC and max PGA gain + + /* PDM digital microphone enable or disable */ + if (digital_mic) { + reg14 |= BIT(6); + } + es8311_write_reg(dev, ES8311_ADC_REG17, 0xC8); // Set ADC gain @todo move this to ADC config section + + return es8311_write_reg(dev, ES8311_SYSTEM_REG14, reg14); +} + +esp_err_t es8311_init(es8311_handle_t dev, const es8311_clock_config_t *const clk_cfg, const es8311_resolution_t res_in, const es8311_resolution_t res_out) +{ + ESP_RETURN_ON_FALSE( + (clk_cfg->sample_frequency >= 8000) && (clk_cfg->sample_frequency <= 96000), + ESP_ERR_INVALID_ARG, TAG, "ES8311 init needs frequency in interval [8000; 96000] Hz" + ); + if (!clk_cfg->mclk_from_mclk_pin) { + ESP_RETURN_ON_FALSE(res_out == res_in, ESP_ERR_INVALID_ARG, TAG, "Resolution IN/OUT must be equal if MCLK is taken from SCK pin"); + } + + + /* Reset ES8311 to its default */ + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, 0x1F), TAG, "I2C read/write error"); + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, 0x00), TAG, "I2C read/write error"); + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, 0x80), TAG, "I2C read/write error"); // Power-on command + + /* Setup clock: source, polarity and clock dividers */ + ESP_RETURN_ON_ERROR(es8311_clock_config(dev, clk_cfg, res_out), TAG, ""); + + /* Setup audio format (fmt): master/slave, resolution, I2S */ + ESP_RETURN_ON_ERROR(es8311_fmt_config(dev, res_in, res_out), TAG, ""); + + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG0D, 0x01), TAG, "I2C read/write error"); // Power up analog circuitry - NOT default + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG0E, 0x02), TAG, "I2C read/write error"); // Enable analog PGA, enable ADC modulator - NOT default + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG12, 0x00), TAG, "I2C read/write error"); // power-up DAC - NOT default + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG13, 0x10), TAG, "I2C read/write error"); // Enable output to HP drive - NOT default + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_ADC_REG1C, 0x6A), TAG, "I2C read/write error"); // ADC Equalizer bypass, cancel DC offset in digital domain + ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_DAC_REG37, 0x08), TAG, "I2C read/write error"); // Bypass DAC equalizer - NOT default + + return ESP_OK; +} + +void es8311_delete(es8311_handle_t dev) +{ + free(dev); +} + +esp_err_t es8311_voice_volume_set(es8311_handle_t dev, int volume, int *volume_set) +{ + if (volume < 0) { + volume = 0; + } else if (volume > 100) { + volume = 100; + } + + int reg32; + if (volume == 0) { + reg32 = 0; + } else { + reg32 = ((volume) * 256 / 100) - 1; + } + + // provide user with real volume set + if (volume_set != NULL) { + *volume_set = volume; + } + return es8311_write_reg(dev, ES8311_DAC_REG32, reg32); +} + +esp_err_t es8311_voice_volume_get(es8311_handle_t dev, int *volume) +{ + uint8_t reg32; + ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_DAC_REG32, ®32), TAG, "I2C read/write error"); + + if (reg32 == 0) { + *volume = 0; + } else { + *volume = ((reg32 * 100) / 256) + 1; + } + return ESP_OK; +} + +esp_err_t es8311_voice_mute(es8311_handle_t dev, bool mute) +{ + uint8_t reg31; + ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_DAC_REG31, ®31), TAG, "I2C read/write error"); + + if (mute) { + reg31 |= BIT(6) | BIT(5); + } else { + reg31 &= ~(BIT(6) | BIT(5)); + } + + return es8311_write_reg(dev, ES8311_DAC_REG31, reg31); +} + +esp_err_t es8311_microphone_gain_set(es8311_handle_t dev, es8311_mic_gain_t gain_db) +{ + return es8311_write_reg(dev, ES8311_ADC_REG16, gain_db); // ADC gain scale up +} + +esp_err_t es8311_voice_fade(es8311_handle_t dev, const es8311_fade_t fade) +{ + uint8_t reg37; + ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_DAC_REG37, ®37), TAG, "I2C read/write error"); + reg37 &= 0x0F; + reg37 |= (fade << 4); + return es8311_write_reg(dev, ES8311_DAC_REG37, reg37); +} + +esp_err_t es8311_microphone_fade(es8311_handle_t dev, const es8311_fade_t fade) +{ + uint8_t reg15; + ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_ADC_REG15, ®15), TAG, "I2C read/write error"); + reg15 &= 0x0F; + reg15 |= (fade << 4); + return es8311_write_reg(dev, ES8311_ADC_REG15, reg15); +} + +void es8311_register_dump(es8311_handle_t dev) +{ + for (int reg = 0; reg < 0x4A; reg++) { + uint8_t value; + ESP_ERROR_CHECK(es8311_read_reg(dev, reg, &value)); + printf("REG:%02x: %02x", reg, value); + } +} + +es8311_handle_t es8311_create(const unsigned int port, const uint16_t dev_addr) +{ + es8311_dev_t *sensor = (es8311_dev_t *) calloc(1, sizeof(es8311_dev_t)); + sensor->port = port; + sensor->dev_addr = dev_addr; + return (es8311_handle_t) sensor; +} diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es8311.h b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es8311.h new file mode 100644 index 0000000..2439d81 --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es8311.h @@ -0,0 +1,227 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief ES8311 driver + */ + +#pragma once + +#include "esp_types.h" +#include "esp_err.h" + +/* ES8311 address: CE pin low - 0x18, CE pin high - 0x19 */ +#define ES8311_ADDRRES_0 0x18u // Leaving this here for backward compatibility +#define ES8311_ADDRESS_0 0x18u +#define ES8311_ADDRESS_1 0x19u + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *es8311_handle_t; + +typedef enum { + ES8311_MIC_GAIN_MIN = -1, + ES8311_MIC_GAIN_0DB, + ES8311_MIC_GAIN_6DB, + ES8311_MIC_GAIN_12DB, + ES8311_MIC_GAIN_18DB, + ES8311_MIC_GAIN_24DB, + ES8311_MIC_GAIN_30DB, + ES8311_MIC_GAIN_36DB, + ES8311_MIC_GAIN_42DB, + ES8311_MIC_GAIN_MAX +} es8311_mic_gain_t; + +typedef enum { + ES8311_FADE_OFF = 0, + ES8311_FADE_4LRCK, // 4LRCK means ramp 0.25dB/4LRCK + ES8311_FADE_8LRCK, + ES8311_FADE_16LRCK, + ES8311_FADE_32LRCK, + ES8311_FADE_64LRCK, + ES8311_FADE_128LRCK, + ES8311_FADE_256LRCK, + ES8311_FADE_512LRCK, + ES8311_FADE_1024LRCK, + ES8311_FADE_2048LRCK, + ES8311_FADE_4096LRCK, + ES8311_FADE_8192LRCK, + ES8311_FADE_16384LRCK, + ES8311_FADE_32768LRCK, + ES8311_FADE_65536LRCK +} es8311_fade_t; + +typedef enum es8311_resolution_t { + ES8311_RESOLUTION_16 = 16, + ES8311_RESOLUTION_18 = 18, + ES8311_RESOLUTION_20 = 20, + ES8311_RESOLUTION_24 = 24, + ES8311_RESOLUTION_32 = 32 +} es8311_resolution_t; + +typedef struct es8311_clock_config_t { + bool mclk_inverted; + bool sclk_inverted; + bool mclk_from_mclk_pin; // true: from MCLK pin (pin no. 2), false: from SCLK pin (pin no. 6) + int mclk_frequency; // This parameter is ignored if MCLK is taken from SCLK pin + int sample_frequency; // in Hz +} es8311_clock_config_t; + +/** + * @brief Initialize ES8311 + * + * There are two ways of providing Master Clock (MCLK) signal to ES8311 in Slave Mode: + * 1. From MCLK pin: + * For flexible scenarios. A clock signal from I2S master is routed to MCLK pin. + * Its frequency must be defined in clk_cfg->mclk_frequency parameter. + * 2. From SCLK pin: + * For simpler scenarios. ES8311 takes its clock from SCK pin. MCLK pin does not have to be connected. + * In this case, res_in must equal res_out; clk_cfg->mclk_frequency parameter is ignored + * and MCLK is calculated as MCLK = clk_cfg->sample_frequency * res_out * 2. + * Not all sampling frequencies are supported in this mode. + * + * @param dev ES8311 handle + * @param[in] clk_cfg Clock configuration + * @param[in] res_in Input serial port resolution + * @param[in] res_out Output serial port resolution + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Sample frequency or resolution invalid + * - Else fail + */ +esp_err_t es8311_init(es8311_handle_t dev, const es8311_clock_config_t *const clk_cfg, const es8311_resolution_t res_in, + const es8311_resolution_t res_out); + +/** + * @brief Set output volume + * + * Volume paramter out of <0, 100> interval will be truncated. + * + * @param dev ES8311 handle + * @param[in] volume Set volume (0 ~ 100) + * @param[out] volume_set Volume that was set. Same as volume, unless volume is outside of <0, 100> interval. + * This parameter can be set to NULL, if user does not need this information. + * + * @return + * - ESP_OK success + * - Else fail + */ +esp_err_t es8311_voice_volume_set(es8311_handle_t dev, int volume, int *volume_set); + +/** + * @brief Get output volume + * + * @param dev ES8311 handle + * @param[out] volume get volume (0 ~ 100) + * + * @return + * - ESP_OK success + * - Else fail + */ +esp_err_t es8311_voice_volume_get(es8311_handle_t dev, int *volume); + +/** + * @brief Print out ES8311 register content + * + * @param dev ES8311 handle + */ +void es8311_register_dump(es8311_handle_t dev); + +/** + * @brief Mute ES8311 output + * + * @param dev ES8311 handle + * @param[in] enable true: mute, false: don't mute + * @return + * - ESP_OK success + * - Else fail + */ +esp_err_t es8311_voice_mute(es8311_handle_t dev, bool enable); + +/** + * @brief Set Microphone gain + * + * @param dev ES8311 handle + * @param[in] gain_db Microphone gain + * @return + * - ESP_OK success + * - Else fail + */ +esp_err_t es8311_microphone_gain_set(es8311_handle_t dev, es8311_mic_gain_t gain_db); + +/** + * @brief Configure microphone + * + * @param dev ES8311 handle + * @param[in] digital_mic Set to true for digital microphone + * @return + * - ESP_OK success + * - Else fail + */ +esp_err_t es8311_microphone_config(es8311_handle_t dev, bool digital_mic); + +/** + * @brief Configure sampling frequency + * + * @note This function is called by es8311_init(). + * Call this function explicitly only if you want to change sample frequency during runtime. + * @param dev ES8311 handle + * @param[in] mclk_frequency MCLK frequency in [Hz] (MCLK or SCLK pin, depending on bit register01[7]) + * @param[in] sample_frequency Required sample frequency in [Hz], e.g. 44100, 22050... + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG cannot set clock dividers for given MCLK and sampling frequency + * - Else I2C read/write error + */ +esp_err_t es8311_sample_frequency_config(es8311_handle_t dev, int mclk_frequency, int sample_frequency); + +/** + * @brief Configure fade in/out for ADC: voice + * + * @param dev ES8311 handle + * @param[in] fade Fade ramp rate + * @return + * - ESP_OK success + * - Else I2C read/write error + */ +esp_err_t es8311_voice_fade(es8311_handle_t dev, const es8311_fade_t fade); + +/** + * @brief Configure fade in/out for DAC: microphone + * + * @param dev ES8311 handle + * @param[in] fade Fade ramp rate + * @return + * - ESP_OK success + * - Else I2C read/write error + */ +esp_err_t es8311_microphone_fade(es8311_handle_t dev, const es8311_fade_t fade); + +/** + * @brief Create ES8311 object and return its handle + * + * @param[in] port I2C port number + * @param[in] dev_addr I2C device address of ES8311 + * + * @return + * - NULL Fail + * - Others Success + */ +es8311_handle_t es8311_create(const unsigned int port, const uint16_t dev_addr); + +/** + * @brief Delete ES8311 object + * + * @param dev ES8311 handle + */ +void es8311_delete(es8311_handle_t dev); + +#ifdef __cplusplus +} +#endif diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es8311_reg.h b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es8311_reg.h new file mode 100644 index 0000000..0ff8d5e --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/es8311_reg.h @@ -0,0 +1,76 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +/* + * ES8311_REGISTER NAME_REG_REGISTER ADDRESS + */ +#define ES8311_RESET_REG00 0x00 /*reset digital,csm,clock manager etc.*/ + +/* + * Clock Scheme Register definition + */ +#define ES8311_CLK_MANAGER_REG01 0x01 /* select clk src for mclk, enable clock for codec */ +#define ES8311_CLK_MANAGER_REG02 0x02 /* clk divider and clk multiplier */ +#define ES8311_CLK_MANAGER_REG03 0x03 /* adc fsmode and osr */ +#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */ +#define ES8311_CLK_MANAGER_REG05 0x05 /* clk divier for adc and dac */ +#define ES8311_CLK_MANAGER_REG06 0x06 /* bclk inverter and divider */ +#define ES8311_CLK_MANAGER_REG07 0x07 /* tri-state, lrck divider */ +#define ES8311_CLK_MANAGER_REG08 0x08 /* lrck divider */ +/* + * SDP + */ +#define ES8311_SDPIN_REG09 0x09 /* dac serial digital port */ +#define ES8311_SDPOUT_REG0A 0x0A /* adc serial digital port */ +/* + * SYSTEM + */ +#define ES8311_SYSTEM_REG0B 0x0B /* system */ +#define ES8311_SYSTEM_REG0C 0x0C /* system */ +#define ES8311_SYSTEM_REG0D 0x0D /* system, power up/down */ +#define ES8311_SYSTEM_REG0E 0x0E /* system, power up/down */ +#define ES8311_SYSTEM_REG0F 0x0F /* system, low power */ +#define ES8311_SYSTEM_REG10 0x10 /* system */ +#define ES8311_SYSTEM_REG11 0x11 /* system */ +#define ES8311_SYSTEM_REG12 0x12 /* system, Enable DAC */ +#define ES8311_SYSTEM_REG13 0x13 /* system */ +#define ES8311_SYSTEM_REG14 0x14 /* system, select DMIC, select analog pga gain */ +/* + * ADC + */ +#define ES8311_ADC_REG15 0x15 /* ADC, adc ramp rate, dmic sense */ +#define ES8311_ADC_REG16 0x16 /* ADC */ +#define ES8311_ADC_REG17 0x17 /* ADC, volume */ +#define ES8311_ADC_REG18 0x18 /* ADC, alc enable and winsize */ +#define ES8311_ADC_REG19 0x19 /* ADC, alc maxlevel */ +#define ES8311_ADC_REG1A 0x1A /* ADC, alc automute */ +#define ES8311_ADC_REG1B 0x1B /* ADC, alc automute, adc hpf s1 */ +#define ES8311_ADC_REG1C 0x1C /* ADC, equalizer, hpf s2 */ +/* + * DAC + */ +#define ES8311_DAC_REG31 0x31 /* DAC, mute */ +#define ES8311_DAC_REG32 0x32 /* DAC, volume */ +#define ES8311_DAC_REG33 0x33 /* DAC, offset */ +#define ES8311_DAC_REG34 0x34 /* DAC, drc enable, drc winsize */ +#define ES8311_DAC_REG35 0x35 /* DAC, drc maxlevel, minilevel */ +#define ES8311_DAC_REG37 0x37 /* DAC, ramprate */ +/* + *GPIO + */ +#define ES8311_GPIO_REG44 0x44 /* GPIO, dac2adc for test */ +#define ES8311_GP_REG45 0x45 /* GP CONTROL */ +/* + * CHIP + */ +#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */ +#define ES8311_CHD2_REGFE 0xFE /* CHIP ID2 */ +#define ES8311_CHVER_REGFF 0xFF /* VERSION */ +#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */ + +#define ES8311_MAX_REGISTER 0xFF diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/simple_av_test.ino b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/simple_av_test.ino new file mode 100644 index 0000000..504f6bf --- /dev/null +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/test-device/simple_av_test/simple_av_test.ino @@ -0,0 +1,272 @@ +#include +#include +#include +#include "ESP_I2S.h" +#include "esp_check.h" +#include "Arduino_GFX_Library.h" +#include "pin_config.h" +#include "TouchDrvCSTXXX.hpp" +#include "SensorQMI8658.hpp" +#include "es8311.h" +#include "es7210.h" + +static const int DISP_W = 480; +static const int DISP_H = 480; +static const int SAFE = 20; +static const uint32_t SAMPLE_RATE = 16000; +static const size_t REC_SECONDS = 2; +static const size_t AUDIO_BYTES = SAMPLE_RATE * REC_SECONDS * 2 * 2; +static const int I2C_NUM = 0; + +I2SClass i2s; +TouchDrvCST92xx touch; +SensorQMI8658 qmi; +IMUdata acc; + +Arduino_DataBus *bus = new Arduino_ESP32QSPI(LCD_CS, LCD_SCLK, LCD_SDIO0, LCD_SDIO1, LCD_SDIO2, LCD_SDIO3); +Arduino_CO5300 *gfx = new Arduino_CO5300(bus, LCD_RESET, 0, DISP_W, DISP_H, 0, 0, 0, 0); + +uint8_t *audioBuf = nullptr; +size_t audioLen = 0; +bool touchIrq = false; +String statusLine = "Ready"; +String tiltLine = "Tilt: FLAT"; +String audioLine = "Audio: empty"; +uint32_t lastTiltMs = 0; +bool micReady = false; + +void IRAM_ATTR onTouchInt() { touchIrq = true; } + +esp_err_t codecInit() { + es8311_handle_t esHandle = es8311_create(I2C_NUM, ES8311_ADDRRES_0); + ESP_RETURN_ON_FALSE(esHandle, ESP_FAIL, "AV", "es8311 create failed"); + const es8311_clock_config_t clk = { + .mclk_inverted = false, + .sclk_inverted = false, + .mclk_from_mclk_pin = true, + .mclk_frequency = SAMPLE_RATE * 256, + .sample_frequency = SAMPLE_RATE, + }; + ESP_ERROR_CHECK(es8311_init(esHandle, &clk, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16)); + ESP_RETURN_ON_ERROR(es8311_sample_frequency_config(esHandle, clk.mclk_frequency, clk.sample_frequency), "AV", "clock cfg failed"); + ESP_RETURN_ON_ERROR(es8311_microphone_config(esHandle, false), "AV", "mic cfg failed"); + ESP_RETURN_ON_ERROR(es8311_voice_volume_set(esHandle, 90, NULL), "AV", "volume failed"); + ESP_RETURN_ON_ERROR(es8311_microphone_gain_set(esHandle, (es8311_mic_gain_t)6), "AV", "mic gain failed"); + return ESP_OK; +} + +esp_err_t micInit() { + audio_hal_codec_config_t cfg = { + .adc_input = AUDIO_HAL_ADC_INPUT_ALL, + .codec_mode = AUDIO_HAL_CODEC_MODE_ENCODE, + .i2s_iface = { + .mode = AUDIO_HAL_MODE_SLAVE, + .fmt = AUDIO_HAL_I2S_NORMAL, + .samples = AUDIO_HAL_16K_SAMPLES, + .bits = AUDIO_HAL_BIT_LENGTH_16BITS, + }, + }; + esp_err_t ret = ESP_OK; + ret |= es7210_adc_init(&Wire, &cfg); + ret |= es7210_adc_config_i2s(cfg.codec_mode, &cfg.i2s_iface); + ret |= es7210_adc_set_gain( + (es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2), + (es7210_gain_value_t)GAIN_24DB); + ret |= es7210_adc_set_gain( + (es7210_input_mics_t)(ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4), + (es7210_gain_value_t)GAIN_24DB); + ret |= es7210_adc_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START); + return ret; +} + +void drawStaticUi() { + gfx->fillScreen(RGB565_BLACK); + gfx->setTextSize(2); + gfx->setTextColor(RGB565_WHITE); + gfx->setCursor(SAFE + 16, SAFE + 10); + gfx->println("Simple AV + Tilt Test"); + + gfx->drawRect(24, 76, 132, 64, RGB565_GREEN); + gfx->setCursor(44, 100); + gfx->println("RECORD"); + + gfx->drawRect(174, 76, 132, 64, RGB565_YELLOW); + gfx->setCursor(212, 100); + gfx->println("PLAY"); + + gfx->drawRect(324, 76, 132, 64, RGB565_CYAN); + gfx->setCursor(360, 100); + gfx->println("BEEP"); +} + +void drawDynamic() { + gfx->fillRect(20, 166, 440, 140, RGB565_BLACK); + gfx->setTextSize(2); + gfx->setTextColor(RGB565_WHITE); + gfx->setCursor(28, 178); + gfx->println(statusLine); + gfx->setTextColor(RGB565_CYAN); + gfx->setCursor(28, 212); + gfx->println(tiltLine); + gfx->setTextColor(RGB565_ORANGE); + gfx->setCursor(28, 246); + gfx->println(audioLine); + gfx->drawRect(28, 278, 300, 20, RGB565_DARKGREY); +} + +void updateTilt() { + if (!qmi.getDataReady()) return; + if (!qmi.getAccelerometer(acc.x, acc.y, acc.z)) return; + String next = "Tilt: FLAT"; + if (acc.x > 0.8f) next = "Tilt: RIGHT"; + else if (acc.x < -0.8f) next = "Tilt: LEFT"; + else if (acc.y > 0.8f) next = "Tilt: UP"; + else if (acc.y < -0.8f) next = "Tilt: DOWN"; + if (next != tiltLine) { + tiltLine = next; + drawDynamic(); + } +} + +void playBeep() { + static int16_t tone[16000]; + for (int i = 0; i < 8000; ++i) { + float s = sinf(2.0f * 3.1415926f * 440.0f * ((float)i / SAMPLE_RATE)); + int16_t v = (int16_t)(s * 6000.0f); + tone[i * 2] = v; + tone[i * 2 + 1] = v; + } + statusLine = "Playing test beep..."; + drawDynamic(); + i2s.write((uint8_t *)tone, 8000 * 2 * sizeof(int16_t)); + statusLine = "Beep done"; + drawDynamic(); +} + +void recordAudio() { + if (!audioBuf || !micReady) { + statusLine = "No audio buffer"; + drawDynamic(); + return; + } + statusLine = "Recording 2 sec..."; + drawDynamic(); + size_t total = 0; + uint16_t peakAll = 0; + while (total < AUDIO_BYTES) { + size_t chunk = min((size_t)4096, AUDIO_BYTES - total); + size_t got = i2s.readBytes((char *)(audioBuf + total), chunk); + if (!got) break; + total += got; + + int16_t *s = (int16_t *)(audioBuf + total - got); + size_t n = got / sizeof(int16_t); + uint16_t peak = 0; + for (size_t i = 0; i < n; i++) { + uint16_t a = (uint16_t)abs((int)s[i]); + if (a > peak) peak = a; + } + if (peak > peakAll) peakAll = peak; + + int bar = map((int)peak, 0, 20000, 0, 296); + if (bar < 0) bar = 0; + if (bar > 296) bar = 296; + gfx->fillRect(30, 280, 296, 16, RGB565_BLACK); + uint16_t c = (peak > 12000) ? RGB565_RED : ((peak > 6000) ? RGB565_YELLOW : RGB565_GREEN); + gfx->fillRect(30, 280, bar, 16, c); + } + audioLen = total; + audioLine = (total > 0) ? "Audio: recorded" : "Audio: empty"; + if (total > 0) { + statusLine = String("Record done, peak=") + String((int)peakAll); + } else { + statusLine = "Record failed"; + } + drawDynamic(); +} + +void playAudio() { + if (!audioLen) { + statusLine = "No recording. Try BEEP."; + drawDynamic(); + return; + } + statusLine = "Playing recording..."; + drawDynamic(); + size_t wrote = i2s.write(audioBuf, audioLen); + statusLine = (wrote > 0) ? "Play done" : "Play failed"; + drawDynamic(); +} + +void handleTouch() { + if (!touchIrq) return; + touchIrq = false; + int16_t x[1]; + int16_t y[1]; + uint8_t touched = touch.getPoint(x, y, 1); + if (!touched) return; + + int px = x[0]; + int py = y[0]; + if (py < 76 || py > 140) return; + if (px >= 24 && px <= 156) recordAudio(); + else if (px >= 174 && px <= 306) playAudio(); + else if (px >= 324 && px <= 456) playBeep(); +} + +void setup() { + Serial.begin(115200); + Wire.begin(IIC_SDA, IIC_SCL); + + pinMode(PA, OUTPUT); + digitalWrite(PA, HIGH); + + if (!gfx->begin()) Serial.println("gfx begin failed"); + bus->writeC8D8(0x36, 0xA0); + gfx->setBrightness(200); + + pinMode(TP_RST, OUTPUT); + digitalWrite(TP_RST, LOW); + delay(30); + digitalWrite(TP_RST, HIGH); + delay(50); + touch.setPins(TP_RST, TP_INT); + touch.begin(Wire, 0x5A, IIC_SDA, IIC_SCL); + touch.setMaxCoordinates(480, 480); + touch.setSwapXY(true); + touch.setMirrorXY(true, false); + attachInterrupt(TP_INT, onTouchInt, FALLING); + + if (qmi.begin(Wire, QMI8658_L_SLAVE_ADDRESS, IIC_SDA, IIC_SCL)) { + qmi.configAccelerometer(SensorQMI8658::ACC_RANGE_4G, SensorQMI8658::ACC_ODR_1000Hz, SensorQMI8658::LPF_MODE_0); + qmi.enableAccelerometer(); + } else { + tiltLine = "Tilt: IMU not found"; + } + + i2s.setPins(9, 45, 8, 10, 42); + if (!i2s.begin(I2S_MODE_STD, SAMPLE_RATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO, I2S_STD_SLOT_BOTH)) { + statusLine = "I2S init failed"; + } else if (codecInit() != ESP_OK) { + statusLine = "ES8311 init failed"; + } else if (micInit() != ESP_OK) { + statusLine = "ES7210 init failed"; + } else { + micReady = true; + } + + audioBuf = (uint8_t *)ps_malloc(AUDIO_BYTES); + if (!audioBuf) statusLine = "No PSRAM buffer"; + + drawStaticUi(); + drawDynamic(); +} + +void loop() { + handleTouch(); + if (millis() - lastTiltMs > 120) { + lastTiltMs = millis(); + updateTilt(); + } + delay(5); +} diff --git a/VERSION.properties b/VERSION.properties index 6c437af..569dcef 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.88 -server.version=1.2.82 +client.version=1.2.89 +server.version=1.2.83