diff --git a/CHANGELOG.md b/CHANGELOG.md index 79e9ae4c..ac2c7fff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ All notable changes to SIMUT firmware. ## Unreleased **Salvar on the panel's alarm-limits editor now saves the limits that were on the screen at the tap, not what the editor holds when Core 0 gets to the event. This closes F3 of the v2.13.0 *Known* list. A display-alignment preview that was not saved no longer outlives the alignment screen, on the original panel and on the Ângulo: whichever way the screen is left except during a firmware update, Core 1 puts the saved offset back -and repaints, and the next entry starts from it. Both fixes were found by reading the code, and both were checked on the bench on 2026-10-31, on the Pico W (the original panel) and on the Pico 2 W (the Ângulo). A third change, not run on a board, closes F4: on the Ângulo, `GET /api/keypad` no longer describes the original panel's keypad. It sends the faces, the state and the policy, and names the route that lists the Ângulo's keys; the two Ângulo images get 208 and 216 B smaller and the other nine are byte for byte what they were. A last change, which moves thirteen names between two headers of the original panel's keypad, changes no image: all eleven are sha256-equal to `main`'s.** +and repaints, and the next entry starts from it. Both fixes were found by reading the code, and both were checked on the bench on 2026-10-31, on the Pico W (the original panel) and on the Pico 2 W (the Ângulo). A third change, checked on the Pico 2 W on 2026-10-31, closes F4: on the Ângulo, `GET /api/keypad` no longer describes the original panel's keypad. It sends the faces, the state and the policy, and names the route that lists the Ângulo's keys; the two Ângulo images get 208 and 216 B smaller and the other nine are byte for byte what they were. A last change, which moves thirteen names between two headers of the original panel's keypad, changes no image: all eleven are sha256-equal to `main`'s.** ### The alarm-limits save on the panel @@ -78,7 +78,7 @@ The gate warns for all three, as it did, because each is under one flash sector, - **How it is switched.** Not by an `#if`: by a UI hook, `uiKeypadTargets( )`, which answers `nullptr` on the original (`src/ui/UiDriverMembers.h`) and `"/api/ui"` on the Ângulo (defined in the class, `src/ui/angulo/Members.h`, so that it is a constant the handler sees). `handleApiKeypad( )` builds the Ângulo's answer when the hook answers and returns; otherwise its old statements run, untouched. Each side is a constant, so the branch an image does not take is not in the image: the Ângulo's images carry neither the grid's format string nor the popup's, and the six original images with the route carry no `"targets"`. The feature-sprawl count does not move, and `tools/ui_texts_allow.json` names the route's literal in `Members.h` as a route path and not text the panel draws. - **The bench tools.** `tools/panel_users_hw_test.py` and `tools/panel_fulltable_test.py` tap the original panel by the grid. They now read the route through one reader, `tools/keypad_answer.py`, which refuses an answer with `targets` and says to tap by `/api/ui`, as `tools/tft_ui/roteiro_ui.py` does (that script reads only `faces` and `kb`, and is untouched). `Rig` also checks once at its start, right after the web login, so a run stops before it makes the test accounts or re-points the telemetry, and it deletes the web login's own account (`smap`) before it stops. An answer with no `grid` and no `targets`, which is what a device older than v25 sends, still gets the v24 layout. `WebUI.h` does not read the route. - **The tests.** `tools/test_keypad_answer.py`, in CI's `gates` job: an original answer, an Ângulo answer (refused, with a message that names `/api/ui` and `tools/tft_ui/roteiro_ui.py`), an answer older than v25 and the groups keyboard with its popup, that each tool reads the route only through the reader, and that each of the tool's two refusals deletes the web login's account before it stops (red before those fixes, which the review asked for). It was red before the reader existed (the module was missing), and it bites: with the refusal taken out exactly one case fails, the Ângulo's, and each of the three other lines it holds fails exactly the cases it serves. Outside the repository, a script read the eleven `firmware.bin` as bytes: on `6981fd93` it fails on both Ângulo images (no `,"targets":"`, and the `"pop":[%d` and `"grid":[` format strings still there), and on this tree all 49 of its checks pass. The three images with no TFT (`pico_w_alpha`, `pico_w_air`, `pico_wire`) never compiled the route and carry neither answer. `Rig.keypad_faces( )` of `6981fd93` against the new one gave the same result and the same state on 15 answers that are not the Ângulo's. -- **Not run on a board.** The answers were proved from the images' bytes and by the host tests; no device was contacted. The Ângulo's PIN keypad was not driven through `/api/ui` for this change. +- **Checked on the Pico 2 W (2026-10-31).** The answers were first proved from the images' bytes and by the host tests. After the merge, on the bench's Pico 2 W, with the PIN keypad on the glass: on v2.13.0's release candidate the route sent the original's grid (152 × 44 at 6, 72) and `tools/keypad_answer.py` accepted it, which is the defect; on `main` at `2bead926` it sent `"targets":"/api/ui"` and neither `grid` nor `pop`, and the reader refused it. On both, a PIN typed by `/api/ui`'s targets reached Settings. The board was written back byte for byte afterwards. ### Flash (`GET /api/keypad`) diff --git a/CHANGELOG.pt-BR.md b/CHANGELOG.pt-BR.md index 6f8662cc..b2e2f14d 100644 --- a/CHANGELOG.pt-BR.md +++ b/CHANGELOG.pt-BR.md @@ -7,7 +7,7 @@ Todas as mudanças notáveis do firmware SIMUT. ## Não lançado **O Salvar do editor de limites de alarme do painel passa a salvar os limites que estavam na tela no toque, e não o que o editor guarda quando o Core 0 chega ao evento. Isto fecha o F3 da lista *Conhecido* da v2.13.0. Uma prévia do alinhamento da tela que não foi salva não sobrevive mais à saída da tela, no painel original e no Ângulo: por onde quer que a tela seja deixada, menos durante uma atualização do firmware, o Core 1 devolve o deslocamento gravado -e redesenha, e a entrada seguinte parte dele. As duas correções foram encontradas lendo o código, e as duas foram conferidas na bancada em 31/10/2026, no Pico W (o painel original) e no Pico 2 W (o Ângulo). Uma terceira mudança, que não rodou numa placa, fecha o F4: no Ângulo, o `GET /api/keypad` deixa de descrever o teclado do painel original. Ele manda as faces, o estado e a política, e nomeia a rota que lista as teclas do Ângulo; as duas imagens do Ângulo ficam 208 e 216 B menores e as outras nove são, byte a byte, o que eram. Uma última mudança, a de treze nomes entre dois cabeçalhos do teclado do painel original, não muda nenhuma imagem: as onze têm o mesmo sha256 da `main`.** +e redesenha, e a entrada seguinte parte dele. As duas correções foram encontradas lendo o código, e as duas foram conferidas na bancada em 31/10/2026, no Pico W (o painel original) e no Pico 2 W (o Ângulo). Uma terceira mudança, conferida no Pico 2 W em 31/10/2026, fecha o F4: no Ângulo, o `GET /api/keypad` deixa de descrever o teclado do painel original. Ele manda as faces, o estado e a política, e nomeia a rota que lista as teclas do Ângulo; as duas imagens do Ângulo ficam 208 e 216 B menores e as outras nove são, byte a byte, o que eram. Uma última mudança, a de treze nomes entre dois cabeçalhos do teclado do painel original, não muda nenhuma imagem: as onze têm o mesmo sha256 da `main`.** ### O Salvar dos limites de alarme no painel @@ -79,7 +79,7 @@ O portão avisa nas três, como já avisava, porque cada uma está a menos de um - **Como se escolhe.** Não por um `#if`: por um gancho do driver de UI, `uiKeypadTargets( )`, que responde `nullptr` no original (`src/ui/UiDriverMembers.h`) e `"/api/ui"` no Ângulo (definido na classe, em `src/ui/angulo/Members.h`, para ser uma constante que o handler enxerga). O `handleApiKeypad( )` monta a resposta do Ângulo quando o gancho responde e volta; senão, os comandos de antes rodam, intocados. Cada lado é uma constante, então o ramo que a imagem não toma não está na imagem: as imagens do Ângulo não levam a string de formato da grade nem a do popup, e as seis imagens originais com a rota não levam `"targets"`. A contagem do `check_feature_sprawl.py` não se move, e o `tools/ui_texts_allow.json` nomeia o literal da rota em `Members.h` como caminho de rota, e não texto que o painel desenha. - **As ferramentas de bancada.** O `tools/panel_users_hw_test.py` e o `tools/panel_fulltable_test.py` tocam o painel original pela grade. Agora leem a rota por um leitor só, o `tools/keypad_answer.py`, que recusa uma resposta com `targets` e diz para tocar pelo `/api/ui`, como faz o `tools/tft_ui/roteiro_ui.py` (esse script lê só `faces` e `kb`, e não foi tocado). O `Rig` também confere uma vez no começo, logo depois do login web, para a rodada parar antes de criar as contas de teste ou de apontar a telemetria para outro lugar, e apaga a conta do próprio login web (`smap`) antes de parar. Uma resposta sem `grid` e sem `targets`, que é o que manda um aparelho anterior à v25, continua recebendo o desenho da v24. A `WebUI.h` não lê a rota. - **Os testes.** O `tools/test_keypad_answer.py`, no job `gates` da CI: uma resposta do original, uma do Ângulo (recusada, com uma mensagem que nomeia o `/api/ui` e o `tools/tft_ui/roteiro_ui.py`), uma anterior à v25 e o teclado de grupos com o popup, que cada ferramenta lê a rota só pelo leitor, e que cada uma das duas recusas da ferramenta apaga a conta do login web antes de parar (vermelho antes desses consertos, que a revisão pediu). Ele estava vermelho antes de o leitor existir (faltava o módulo), e morde: com a recusa tirada falha exatamente um caso, o do Ângulo, e cada uma das outras três linhas que ele segura reprova exatamente os casos a que serve. Fora do repositório, um script leu os onze `firmware.bin` como bytes: no `6981fd93` ele reprova as duas imagens do Ângulo (sem `,"targets":"`, e com as strings de formato `"pop":[%d` e `"grid":[` ainda lá), e nesta árvore passam os 49 testes dele. As três imagens sem TFT (`pico_w_alpha`, `pico_w_air`, `pico_wire`) nunca compilaram a rota e não levam nenhuma das respostas. O `Rig.keypad_faces( )` do `6981fd93` contra o novo deu o mesmo resultado e o mesmo estado em 15 respostas que não são as do Ângulo. -- **Não rodou numa placa.** As respostas foram provadas pelos bytes das imagens e pelos testes do host; nenhum aparelho foi contatado. O teclado de PIN do Ângulo não foi dirigido pelo `/api/ui` para esta mudança. +- **Conferido no Pico 2 W (31/10/2026).** As respostas foram provadas primeiro pelos bytes das imagens e pelos testes do host. Depois do merge, no Pico 2 W da bancada, com o teclado de PIN na tela: na candidata da v2.13.0 a rota mandou a grade do original (152 × 44 em 6, 72) e o `tools/keypad_answer.py` a aceitou, que é o defeito; na `main` em `2bead926` ela mandou `"targets":"/api/ui"` e nem `grid` nem `pop`, e o leitor a recusou. Nas duas, um PIN digitado pelos alvos do `/api/ui` chegou aos Ajustes. A placa foi regravada byte a byte no fim. ### Flash (`GET /api/keypad`) diff --git a/docs/VERIFICATION.es-ES.md b/docs/VERIFICATION.es-ES.md index a5140b10..b63557a9 100644 --- a/docs/VERIFICATION.es-ES.md +++ b/docs/VERIFICATION.es-ES.md @@ -14,6 +14,7 @@ Lo que se ha medido en hardware real, de lo más reciente a lo más antiguo: | Fecha | Qué | Resultado | |---|---|---| +| 31/10/2026 | `GET /api/keypad` en el Ângulo (#311, F4), en la Pico 2 W, `main` en `2bead926` | Con el teclado de PIN en la pantalla: el candidato de la v2.13.0 (el control negativo) envió la cuadrícula del panel original (152 × 44 en 6, 72) y `tools/keypad_answer.py` la aceptó; `main` envió `"targets":"/api/ui"` y ni `grid` ni `pop`, y el lector la rechazó. En los dos, un PIN tecleado por los objetivos de `/api/ui` llegó al menú de ajustes, y la vuelta por inactividad llegó 34,6 s después del último toque. La placa se regrabó byte a byte al final | | 31/10/2026 | El Guardar de los límites de alarma en el panel (#307) y la alineación de la pantalla (#308) en `main` (`96f503aa`), en la Pico W (panel original) y en la Pico 2 W (Ângulo) | Alineación, primero en la v2.13.0 (en la Pico 2 W, su candidata) y después en `main`: una vista previa de un paso a la Izquierda, la vuelta por inactividad de 30 s (31,7 a 35,2 s después del último toque), una entrada nueva. La v2.13.0 devolvió el dashboard desplazado (−1, 0) en los dos paneles, y su entrada nueva tomó el desplazamiento por el guardado; `main` lo devolvió en (0, 0) y empezó la entrada nueva en el desplazamiento guardado. Límites, en `main`: el MIN un paso abajo y el MAX un paso arriba, después Guardar: de la configuración que informa la API web, solo cambiaron esos dos valores (1,9 → 1,8, 24,0 → 24,1), además de valores de estado como la longitud de la cola de telemetría de alarmas, con una escritura y un registro `APP_UI_ALARM_SAVED`, y un segundo Guardar los devolvió de la misma forma; en la Pico W el receptor de telemetría recibió un registro `alarm_lim` por Guardar, con los límites nuevos y la cuenta. Una ronda en la Pico W mostró el dashboard con el pie de la pantalla de alineación debajo en la primera captura tras la entrada: una captura rasgada por la vuelta por inactividad de 30 s, no un defecto del panel. La captura lee la pantalla de abajo hacia arriba, 16 filas cada vez, y deja correr el panel entre bloques; las 32 filas de abajo muestran la pantalla de alineación y el resto el dashboard, una frontera que corta el botón SAIR, algo que ninguna pantalla dibuja. El primer intento se cortó (`WEB_SCREENSHOT_ABORTED` a las 14:27:34), y el reintento se guardó unos 40 s después del toque que abrió la pantalla (el toque no tiene hora; estimado con las dos rondas limpias). No se sabe por qué se cortaron las capturas de esa ronda, cuatro seguidas. Las dos placas se regrabaron byte a byte al final | | 31/10/2026 | Candidato a release (v2.13.0) en la Pico W, firmado por el CI | Suite web con 67 aprobados, 0 fallos y 1 omitido en el candidato; actualización por el aire desde la v2.12.0 publicada en la release, el Air y el alpha: el `system.bin` idéntico byte a byte, todos los valores de configuración que informa la API web iguales (539 en la release, 530 en el alpha y el Air; tres sensores leyendo), el `.bkp` restaurado con 73 de 77 archivos idénticos en la release (los otros son el cursor de la telemetría, el bloque abierto del historial, el archivo del día y el log), el slot igual a la parte de la imagen (en el alpha y el Air salvo el sector de 4 KiB del banco de flash del BTstack); el candidato sobre sí mismo, comprobando su propia firma; de vuelta a la v2.12.0 por el aire y de nuevo hacia arriba, la configuración igual y el primer envío de cada versión visto en un receptor; una imagen sin firma (8), la del Air (7), la de la Pico 2 W (6) y el candidato con un byte cambiado (9) rechazadas, la configuración igual tras un reinicio; un envío cortado a 400 kB y un reinicio, con y sin el sistema de archivos lleno en medio: configuración intacta; la #283 en el Air, por primera vez en una placa: con el pack pt-BR solo en `/lang`, un despertar escribió el 567 una vez y nada del preámbulo del arranque, y el arranque siguiente registró `I18N_PACK_LOADED`; el techo de los archivos de idioma: la v2.12.0 rechaza el pack de 53.035 B y el candidato lo carga (el es-ES de 55.064 B también); 30 min sin reinicio con el cable del RESET de la PicoHand en el pin RUN (120 muestras, tres sensores con valor, heap libre de 54.932 a 55.068 B); `clk_peri` 133 MHz y `tft_hz` 66,50 MHz, la pantalla mirada por el mantenedor (sin píxeles sueltos, el toque responde), `/license` 200 con 16.400 B; la placa escrita de vuelta a su dump de llegada, byte a byte | | 30/10/2026 | Candidato a release (v2.13.0) en la Pico 2 W, firmado por el CI | Suite web con 67 aprobados, 0 fallos y 1 omitido en el candidato; desde la v2.12.0 por su `.uf2` de fábrica, por el aire: en el otro slot, a prueba, confirmada 45,7 s después de volver, los 527 valores de configuración que informa la API web iguales (tres sensores), y `/api/ui` 401 donde la v2.12.0 daba 302 (el Ángulo dibujado); el candidato con un byte cambiado (9) y una imagen firmada con la clave de banco (12) rechazados; el candidato sobre sí mismo, dos veces; el `.uf2` de fábrica del candidato copiado a la unidad del BOOTSEL desde el slot B, tras un control (BOOTSEL sin grabar nada: la placa siguió en el B): arriba en la 2.13.0 desde el slot A, la configuración igual; 30 min sin reinicio (118 lecturas), tres sensores leyendo, heap libre de 296.732 a 296.852 B; la retención entre el panel original de la v2.12.0 y el Ángulo (C5, 15 pasos, en los dos sentidos): los 527 valores iguales en cada paso, el número de tema guardado leído como dice la regla de `UI_DRIVERS.md` (el 7 como `simut_def` en el original), el pack de 53.035 B rechazado por la v2.12.0 y cargado por las dos imágenes 2.13.0, ningún cuadro de fábrica en la consola donde se capturó, 16 capturas de la pantalla juzgadas por la sesión (17 tomadas); `clk_peri` 150 MHz y `tft_hz` 37,50 MHz; la caminata táctil con 15 aprobados y 0 fallos; la caminata de idiomas con 66 aprobados y 0 fallos; la caminata completa del Ángulo con 10 aprobados en la tercera pasada (las dos primeras se detuvieron en una captura de pantalla cortada); la placa quedó en el candidato | diff --git a/docs/VERIFICATION.md b/docs/VERIFICATION.md index 27d18453..d35826d8 100644 --- a/docs/VERIFICATION.md +++ b/docs/VERIFICATION.md @@ -14,6 +14,7 @@ What has been measured on real hardware, latest first: | Date | What | Result | |---|---|---| +| 2026-10-31 | `GET /api/keypad` on the Ângulo (#311, F4), on the Pico 2 W, `main` at `2bead926` | With the PIN keypad on the glass: v2.13.0's release candidate (the negative control) sent the original panel's grid (152 × 44 at 6, 72) and `tools/keypad_answer.py` accepted it; `main` sent `"targets":"/api/ui"` and neither `grid` nor `pop`, and the reader refused it. On both, a PIN typed by `/api/ui`'s targets reached Settings, and the idle return came 34.6 s after the last tap. The board was written back byte for byte afterwards | | 2026-10-31 | The panel's alarm-limits save (#307) and the display alignment (#308) on `main` (`96f503aa`), on the Pico W (original panel) and the Pico 2 W (Ângulo) | Alignment, first on v2.13.0 (on the Pico 2 W, its release candidate) and then on `main`: a preview one step Left, the 30 s idle return (31.7 to 35.2 s after the last tap), a new entry. v2.13.0 brought the dashboard back shifted by (−1, 0) on both panels, and its new entry took the shift for the saved offset; `main` brought it back at (0, 0) and started the new entry at the saved offset. Limits, on `main`: MIN one step down and MAX one step up, then Salvar: of the configuration the web API reports, only those two values changed (1.9 → 1.8, 24.0 → 24.1), besides status values such as the alarm telemetry queue's length, with one save and one `APP_UI_ALARM_SAVED` record, and a second Salvar put them back the same way; on the Pico W the telemetry receiver got one `alarm_lim` record per Salvar, with the new limits and the account. One run on the Pico W showed the dashboard with the alignment screen's footer under it in its first screenshot after the entry: a screenshot torn by the 30 s idle return, not a defect of the panel. The screenshot reads the glass bottom up, 16 rows at a time, and lets the panel run between chunks; the bottom 32 rows show the alignment screen and the rest the dashboard, a boundary through the SAIR button that no screen draws. Its first try was cut (`WEB_SCREENSHOT_ABORTED` at 14:27:34), and the retry was saved about 40 s after the tap that opened the screen (the tap is not time-stamped; estimated from the two clean runs). Why that run's screenshots were cut, four in a row, is not known. Both boards were written back byte for byte afterwards | | 2026-10-31 | Release candidate (v2.13.0) on the Pico W, signed by the CI | Web suite 67 passed, 0 failed, 1 skipped on the candidate; update from the published v2.12.0 over the air on release, Air and alpha: `system.bin` identical byte for byte, every configuration value the web API reports unchanged (539 on the release, 530 on the alpha and the Air; three sensors reading), the `.bkp` restored with 73 of 77 files identical on the release (the others are the telemetry cursor, the open history block, the day's history file and the log), the slot equal to the image part (on the alpha and the Air but for the 4 KiB sector of BTstack's flash bank); the candidate over itself, checking its own signature; back to v2.12.0 over the air and up again, the configuration unchanged and each version's first send seen at a receiver; an unsigned image (8), the Air's (7), the Pico 2 W's (6) and the candidate with one byte changed (9) refused, the configuration unchanged after a reset; an upload cut at 400 kB and a reset, with and without the filesystem filled in between: configuration intact; #283 on the Air, for the first time on a board: with the pt-BR pack alone in `/lang`, a wake wrote 567 once and none of the boot preamble, and the boot after it logged `I18N_PACK_LOADED`; the language file ceiling: v2.12.0 refuses the 53,035 B pack and the candidate loads it (the 55,064 B es-ES one too); 30 min without a restart with the PicoHand's RESET wire on the RUN pin (120 samples, three sensors valued, free heap 54,932 to 55,068 B); `clk_peri` 133 MHz and `tft_hz` 66.50 MHz, the glass looked at by the maintainer (no stray pixels, the touch responds), `/license` 200 with 16,400 B; the board written back to its arrival dump, byte for byte | | 2026-10-30 | Release candidate (v2.13.0) on the Pico 2 W, signed by the CI | Web suite 67 passed, 0 failed, 1 skipped on the candidate; from v2.12.0 by its factory `.uf2`, over the air: into the other slot, on trial, kept 45.7 s after it was back, the 527 configuration values the web API reports unchanged (three sensors), and `/api/ui` 401 where v2.12.0 gave 302 (the Ângulo drawn); the candidate with one byte changed (9) and an image signed by the bench key (12) refused; the candidate over itself, twice; the candidate's factory `.uf2` copied onto the BOOTSEL drive from slot B, after a control (BOOTSEL with nothing written: the board stayed on B): up on 2.13.0 from slot A, the configuration unchanged; 30 min without a restart (118 reads), three sensors reading, free heap 296,732 to 296,852 B; the retention between v2.12.0's original panel and the Ângulo (C5, 15 steps, both directions): the 527 values unchanged at every step, the saved theme number read as the rule of `UI_DRIVERS.md` says (7 as `simut_def` on the original), the 53,035 B pack refused by v2.12.0 and loaded by both 2.13.0 images, no factory frame on the console where it was captured, 16 captures of the glass judged by the session (17 taken); `clk_peri` 150 MHz and `tft_hz` 37.50 MHz; the touch walk 15 passed, 0 failed; the language walk 66 passed, 0 failed; the Ângulo's full walk 10 passed on its third run (the first two stopped on a screenshot cut short); the board left on the candidate | diff --git a/docs/VERIFICATION.pt-BR.md b/docs/VERIFICATION.pt-BR.md index ce75f70d..f7fbdaea 100644 --- a/docs/VERIFICATION.pt-BR.md +++ b/docs/VERIFICATION.pt-BR.md @@ -14,6 +14,7 @@ O que foi medido no hardware real, do mais recente ao mais antigo: | Data | O quê | Resultado | |---|---|---| +| 31/10/2026 | `GET /api/keypad` no Ângulo (#311, F4), no Pico 2 W, `main` em `2bead926` | Com o teclado de PIN na tela: a candidata da v2.13.0 (o controle negativo) mandou a grade do painel original (152 × 44 em 6, 72) e o `tools/keypad_answer.py` a aceitou; a `main` mandou `"targets":"/api/ui"` e nem `grid` nem `pop`, e o leitor a recusou. Nas duas, um PIN digitado pelos alvos do `/api/ui` chegou aos Ajustes, e a volta por inatividade veio 34,6 s depois do último toque. A placa foi regravada byte a byte no fim | | 31/10/2026 | O Salvar dos limites de alarme no painel (#307) e o alinhamento da tela (#308) na `main` (`96f503aa`), no Pico W (painel original) e no Pico 2 W (Ângulo) | Alinhamento, primeiro na v2.13.0 (no Pico 2 W, a candidata dela) e depois na `main`: uma prévia de um passo para a Esquerda, a volta por inatividade de 30 s (31,7 a 35,2 s depois do último toque), uma entrada nova. A v2.13.0 trouxe o painel inicial de volta deslocado de (−1, 0) nos dois painéis, e a entrada nova tomou o deslocamento pelo gravado; a `main` o trouxe de volta em (0, 0) e começou a entrada nova no deslocamento gravado. Limites, na `main`: o MIN um passo abaixo e o MAX um passo acima, depois Salvar: da configuração que a API web informa, só esses dois valores mudaram (1,9 → 1,8, 24,0 → 24,1), além de valores de estado como o tamanho da fila de telemetria de alarmes, com uma gravação e um registro `APP_UI_ALARM_SAVED`, e um segundo Salvar os devolveu do mesmo jeito; no Pico W o receptor de telemetria recebeu um registro `alarm_lim` por Salvar, com os limites novos e a conta. Uma rodada no Pico W mostrou o painel inicial com o rodapé da tela de alinhamento embaixo dele na primeira captura depois da entrada: uma captura rasgada pela volta por inatividade de 30 s, não um defeito do painel. A captura lê o vidro de baixo para cima, 16 linhas por vez, e deixa o painel rodar entre os blocos; as 32 linhas de baixo mostram a tela de alinhamento e o resto o painel inicial, uma fronteira que corta o botão SAIR, o que nenhuma tela desenha. A primeira tentativa foi cortada (`WEB_SCREENSHOT_ABORTED` às 14:27:34), e a nova tentativa foi salva cerca de 40 s depois do toque que abriu a tela (o toque não tem horário; estimado pelas duas rodadas limpas). Não se sabe por que as capturas dessa rodada foram cortadas, quatro seguidas. As duas placas foram regravadas byte a byte no fim | | 31/10/2026 | Candidato à release (v2.13.0) no Pico W, assinado pelo CI | Suíte web com 67 aprovados, 0 falhas e 1 pulado na candidata; atualização pelo ar a partir da v2.12.0 publicada, na release, no Air e no alpha: o `system.bin` idêntico byte a byte, todos os valores de configuração que a API web informa iguais (539 na release, 530 no alpha e no Air; três sensores lendo), o `.bkp` restaurado com 73 de 77 arquivos idênticos na release (os outros são o cursor da telemetria, o bloco aberto do histórico, o arquivo do dia e o log), o slot igual à parte da imagem (no alpha e no Air, salvo o setor de 4 KiB do banco de flash do BTstack); a candidata sobre ela mesma, conferindo a própria assinatura; de volta à v2.12.0 pelo ar e de novo para cima, a configuração igual e o primeiro envio de cada versão visto num receptor; uma imagem sem assinatura (8), a do Air (7), a do Pico 2 W (6) e a candidata com um byte trocado (9) recusadas, a configuração igual depois de um reset; um envio cortado em 400 kB e um reset, com e sem o sistema de arquivos cheio no meio: configuração intacta; o #283 no Air, pela primeira vez numa placa: com o pacote pt-BR sozinho em `/lang`, um wake gravou o 567 uma vez e nada do preâmbulo do boot, e o boot seguinte registrou `I18N_PACK_LOADED`; o teto dos arquivos de idioma: a v2.12.0 recusa o pacote de 53.035 B e a candidata o carrega (o es-ES de 55.064 B também); 30 min sem reinício com o fio do RESET da PicoHand no pino RUN (120 amostras, três sensores com valor, heap livre de 54.932 a 55.068 B); `clk_peri` 133 MHz e `tft_hz` 66,50 MHz, o vidro olhado pelo mantenedor (sem pixels soltos, o toque responde), `/license` 200 com 16.400 B; a placa gravada de volta no dump de chegada, byte a byte | | 30/10/2026 | Candidato à release (v2.13.0) no Pico 2 W, assinado pelo CI | Suíte web com 67 aprovados, 0 falhas e 1 pulado na candidata; a partir da v2.12.0 pelo `.uf2` de fábrica, pelo ar: no outro slot, em teste, confirmada 45,7 s depois de voltar, os 527 valores de configuração que a API web informa iguais (três sensores), e `/api/ui` 401 onde a v2.12.0 dava 302 (o Ângulo desenhado); a candidata com um byte trocado (9) e uma imagem assinada pela chave de bancada (12) recusadas; a candidata sobre ela mesma, duas vezes; o `.uf2` de fábrica da candidata copiado para a unidade do BOOTSEL a partir do slot B, depois de um controle (BOOTSEL sem gravar nada: a placa ficou no B): de pé na 2.13.0 pelo slot A, a configuração igual; 30 min sem reinício (118 leituras), três sensores lendo, heap livre de 296.732 a 296.852 B; a retenção entre o painel original da v2.12.0 e o Ângulo (C5, 15 passos, nos dois sentidos): os 527 valores iguais em todo passo, o número de tema salvo lido como a regra do `UI_DRIVERS.md` diz (o 7 como `simut_def` no original), o pacote de 53.035 B recusado pela v2.12.0 e carregado pelas duas imagens 2.13.0, nenhum quadro de fábrica no console onde ele foi capturado, 16 capturas do vidro julgadas pela sessão (17 feitas); `clk_peri` 150 MHz e `tft_hz` 37,50 MHz; a caminhada de toque com 15 aprovados e 0 falhas; a caminhada de idiomas com 66 aprovados e 0 falhas; a caminhada completa do Ângulo com 10 aprovados na terceira rodada (as duas primeiras pararam numa captura de tela cortada); a placa ficou na candidata | diff --git a/docs/analysis/UI_DRIVERS.md b/docs/analysis/UI_DRIVERS.md index 948ef4a9..a7b398da 100644 --- a/docs/analysis/UI_DRIVERS.md +++ b/docs/analysis/UI_DRIVERS.md @@ -2199,7 +2199,7 @@ numbers come from CI. - **The text keyboard's buffers outlive it.** `showSettingsPassword( )` wipes `_kbBuffer` and `_kbConfirmBuf` when the keyboard opens, and the result screen wipes them for another try; leaving by X, or after a saved PIN, wipes neither, so the text stays in RAM until the keyboard opens again. Core 0's `newPwd[16]` (`AppManager_Events.cpp:486-488`) is not wiped after `panelSetUserPin( )`, which takes it as `const char *` and cannot. The legacy PIN's keyboard opens only from the CLI's `screen pwd`. - **`_pinCardsDirty`** is set by the keypad's actions and read only by the original's paint (`drawPinScreen( )`); the Ângulo never reads it, and repaints from `_repaintSettings`. - **The Ângulo's view-model builder clamps `_usersPage`** itself (`uaMontarIdentidade( )`, the account lists' case): duty 10's page clamp, whose hook (`uiPages( )`) does not exist yet. -- **`GET /api/keypad` on the Ângulo image (closed on 2026-10-31 by S12's F4, below).** It published the original's grid and popup geometry (section 4.2), not the keys on the glass; a client that taps by it would miss the Ângulo's keys (unverified on the bench). +- **`GET /api/keypad` on the Ângulo image (closed on 2026-10-31 by S12's F4, below).** It published the original's grid and popup geometry (section 4.2), not the keys on the glass; a client that taps by it would miss the Ângulo's keys (seen on the bench on 2026-10-31: on the Pico 2 W, v2.13.0's release candidate sent the original's cards, 152 × 44 at 6, 72, while the Ângulo draws its own). - **The Ângulo's two guards (D3).** The sensor menu tests the slot before the permission, so an unprivileged tap on an invalid slot no longer beeps; the menu always has a valid slot, so no tap reaches that case. Definir PIN and Excluir stay off a new account because `alvosConta( )` omits them (`telas/IdentidadeContas.cpp:292`), where the original has no such rows (`userEditRowCount( )` gives 3). **S10a: what it found and left open (2026-10-21).** None of these is S10a's to change, or each was left on purpose; section 2.14 has the numbers. @@ -2208,7 +2208,7 @@ numbers come from CI. - **The D5 units** (S10b-1 did not resolve them: it measured each one's keepers, and the splits are S10b-2's, below), whole until S10b-2 resolves each with a hook or a `UiChange` in place of the write across the cut: `DisplayManager_Users.cpp` (`PinKb::orderedFace( )`), `DisplayManager_Alarm.cpp` (`isSlotAlarming( )`), `drawSettingsLicense( )` (`_licOpening`), Calibration's two `show*` functions (the `g_*` caches) and `showApConfirm( )`. With them waited C7 (the Users draws), C8 (`PinKeypad.h`, section 4.2) and C11a. **Closed on 2026-10-28 (S10c, stage D), one unit at a time:** `DisplayManager_Users.cpp` (`PinKb::orderedFace( )`) by S10c D6a (`5f04277d`), whose move is C7; `DisplayManager_Alarm.cpp` (`isSlotAlarming( )`) by S10c D5 (`87875859`), whose move of the three alarm draws to `DisplayManager_AlarmUi.cpp` is C11a of the S10a plan; `drawSettingsLicense( )` (`_licOpening`) by S10c D4b-1 and D4b-2 (`a26d1199`, `6c216cc6`); Calibration's two `show*` functions (the `g_*` caches) by S10c D3a to D3c (`aea1f5f8`, `c67a1cb4`, `79ac927a`); `showApConfirm( )` by S10c D2 (`9fae2fa7`); and C8 (`PinKeypad.h`) by S10c C8 (`c5a26bcc`). S4's C11a (`1bbc4614`, the packet arrow) is another item, done then (section 2.18). - **`pico_w_test_https` has 12 B of real flash left** (measured 2026-10-21 with `readelf -l`): the last LOAD ends at 0x100FEFF4 and the FLASH region at 0x100FF000 (`memmap_default.ld`, length 1,044,480); at C0 the end was 0x100FEF9C, 100 B free, and C2's 88 B took most of it. PlatformIO's line ("used 1,032,436 bytes from 1,044,480") sums sections and reads as 12 KB free: the real headroom is the region's end minus the last LOAD's end. The image is `ota_exempt` and not a required check, but CI builds it, and the Users split added 160 B and it no longer linked. Every later step holds it within those 12 B: a split that is not byte-neutral cannot go in. **Since the sync of 2026-10-21 (S10b-0) it has 36 B** (#284 took 24 B from the image; the last LOAD ends at 0x100FEFDC), and no step of S10b-1 changed it. **Since the sync of 2026-10-23 (S10c stage D, merge `262962f5`) it has 4,300 B:** main's #288 serves its licence page from LittleFS, so the last LOAD ends at 0x100FDF34 (`readelf -l` on the merged image; main's own image ends there too). The image shrank by 4,264 B, not main's 4,256. Seven of the 8 B are the licence blob it dropped, 4,267 B on the branch and 4,260 B on main (`nm -S`, `LICENSE_PAGE_GZ`; the next symbol follows it with no padding in both); the eighth is not decoded. That is the room the S10b-2 splits waited for. - **F3 (closed on 2026-10-31 by #307, merged into `main` as `aaccce35`; the save checked on both panels on the bench the same day, `docs/VERIFICATION.md`).** `AppManager::panelSaveAlarmLimits( )` read the live working copy by reference after Salvar, with no copy and no lock (S8b's finding (a); the writers of the copy run on Core 1). Salvar now posts a copy into a one-entry box (`src/display/AlarmLimitsSnap.h`, `DisplayManager::_limitsSaved`) and Core 0 takes it out under `_stateMutex`; `editedAlarmRecord( )` is gone. The limit that stays, and the flash cost of each image, are in the CHANGELOG's Unreleased section. -- **F4 (closed on 2026-10-31, S12).** The Ângulo's `GET /api/keypad` published the original's grid and popup geometry (decision D3's record), not the keys on the glass: a route question for S10b or S12. The maintainer chose option (c) on 2026-10-31: on the Ângulo the route sends `faces`, `up`, `kb` and `policy` and `"targets":"/api/ui"`, and no `grid` and no `pop`; on the original the answer is what it was, byte for byte. The switch is a UI hook, `uiKeypadTargets( )` (`nullptr` in `src/ui/UiDriverMembers.h`, `"/api/ui"` in `src/ui/angulo/Members.h`), a constant on each side, so each image carries only its own answer's format strings; the nine other images keep their sha256 and the two Ângulo images shrink (the CHANGELOG's Unreleased section has the table). The two bench tools that tap the original panel by the grid read the route through `tools/keypad_answer.py` and refuse the Ângulo's answer. Not checked on a board. +- **F4 (closed on 2026-10-31, S12).** The Ângulo's `GET /api/keypad` published the original's grid and popup geometry (decision D3's record), not the keys on the glass: a route question for S10b or S12. The maintainer chose option (c) on 2026-10-31: on the Ângulo the route sends `faces`, `up`, `kb` and `policy` and `"targets":"/api/ui"`, and no `grid` and no `pop`; on the original the answer is what it was, byte for byte. The switch is a UI hook, `uiKeypadTargets( )` (`nullptr` in `src/ui/UiDriverMembers.h`, `"/api/ui"` in `src/ui/angulo/Members.h`), a constant on each side, so each image carries only its own answer's format strings; the nine other images keep their sha256 and the two Ângulo images shrink (the CHANGELOG's Unreleased section has the table). The two bench tools that tap the original panel by the grid read the route through `tools/keypad_answer.py` and refuse the Ângulo's answer. Checked on the Pico 2 W on 2026-10-31, against v2.13.0's release candidate (`docs/VERIFICATION.md`). - **Dead code found by the review of C2.** A host run over all 256 type bytes shows that no sensor type has zero limits, so the Ângulo's fallback of 2 and the original's zero return in the step are never taken; they are kept as written. - **Line citations that remain** outside C12's list: `DisplayManager_Touch.cpp:1684` in `src/ui/angulo/telas/GraficosOta.cpp` (the file ends before that line since S5 to S10a) and `Touch.cpp:117` in `src/AppManager_Commands.cpp`; they become names the day someone next touches them. A sweep of the Living documents for line citations whose line S10a changed (each cited line at `331266dc` against the tip, 2026-10-21) found seven: the two of `DisplayManager_Dashboard.cpp:243-247` and `:279-318` in `MODELO_DE_RECURSOS.md` (R-H16 and the display's resources row), true at `331266dc` and false since C5, and two stub lines in the notes of this table, now names; the other three, `DisplayManager.cpp:1106-1167`, `:1209` and `:1399-1630` in `MODELO_DE_RECURSOS.md`, were already false at `331266dc`, as are its `DisplayManager.cpp:200-228` citations and `DisplayManager_Touch.cpp:393-1640` (the lines drifted with S4 to S9 and with main). Recorded for main, not changed here: that document's R-D10 says the bridge `simutStateMutexHeldByCurrentCore( )` exists only in `DisplayManager.cpp`, which the Air excludes; since 2026-09-26 it is in `src/ConcurrencyAsserts.cpp` (that file's header says so), so the rule's reason no longer holds as written.