<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>⛬ Raphazilla</title><link>https://raphazilla.rocks</link><description>Build · Break · Repeat</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><lastBuildDate>Wed, 27 May 2026 23:44:08 GMT</lastBuildDate><generator>marmite</generator><image><url>https://raphazilla.rocks/media/gallery/avatar.jpg</url><title></title><link></link></image><item><title>dashIO: firmware, bugs, e o que a IA nao sabe sobre o seu chip</title><link>https://raphazilla.rocks/dashio-firmware-bugs-e-o-que-a-ia-nao-sabe-sobre-o-seu-chip.html</link><description><![CDATA["O firmware do Cardputer ADV foi inteiramente vibe coded. Nao como atalho, mas como chave de acesso a um dominio onde tinha curiosidade e barreiras. O que funcionou, o que quebrou, e o que aprendi depurando o que a IA errou."]]></description><author>rapha</author><category>esp32</category><category>iot</category><category>firmware</category><category>m5stack</category><category>freertos</category><category>arduino</category><category>homelab</category><category>vibe-coding</category><guid>https://raphazilla.rocks/dashio-firmware-bugs-e-o-que-a-ia-nao-sabe-sobre-o-seu-chip.html</guid><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><source url="https://raphazilla.rocks">tag-firmware</source><content:encoded><![CDATA[<p>No <a href="2026-05-02-m5stack-cardputer-do-lora-ao-dashio.html">post anterior sobre o Cardputer ADV</a>
eu terminei com uma promessa: &quot;O dashIO merece um post so dele.&quot; Esse eh o em breve.</p>
<p>Mas tem uma coisa que nao contei la. Muda a forma de ler esse post. Eu nao escrevi esse
firmware. Uma IA escreveu. Eu dirigi, revisei, testei, e depurei quando quebrou. Mas nao
saberia ter escrito do zero.</p>
<figure>
<img src="media/gallery/2026-05-02-dashio-cardputer-device.jpg"
  alt="M5Stack Cardputer ADV com antena LoRa encaixada e display mostrando a interface do dashIO">
<figcaption>O dispositivo com o CAP LoRa-1262 encaixado. O AP sobe automaticamente no boot.</figcaption>
</figure>
<p>Antes de qualquer coisa: dashIO aqui eh o nome do firmware que eu fiz pro Cardputer. Nao tem
nada a ver com <a href="https://dashio.io">dashio.io</a>, que eh uma plataforma proprietaria de IoT com
protocolo proprio via BLE e MQTT. A coincidencia de nome vai confundir alguma busca no Google.
To considerando renomear, mas por enquanto eh dashIO mesmo.</p>
<h2><a href="#o-que-o-firmware-faz" aria-hidden="true" class="anchor" id="o-que-o-firmware-faz"></a>O que o firmware faz</h2>
<p>A ideia central eh simples: o Cardputer vira um servidor web autonomo. Sem internet, sem app,
sem nada externo. Cria um access point Wi-Fi chamado &quot;dashIO&quot;, voce abre <code>192.168.4.1</code> em
qualquer browser e tem o hardware todo na frente em tempo real.</p>
<p>Seis abas: Dashboard com status geral, Sensors com grafico do IMU ao vivo, Radio pra controlar
o LoRa em runtime, Terminal REPL via WebSocket, Media pra gerenciar arquivos no SD e Admin pra
Wi-Fi e configuracoes gerais.</p>
<figure>
<img src="media/gallery/2026-05-02-dashio-home.jpg"
  alt="Dashboard do dashIO mostrando chips de status, System info, Battery e GPS">
<figcaption>Dashboard principal no browser. Esse eh o estado do hardware em tempo real, sem app instalado.</figcaption>
</figure>
<h2><a href="#vibe-coding-como-ferramenta-de-curiosidade" aria-hidden="true" class="anchor" id="vibe-coding-como-ferramenta-de-curiosidade"></a>Vibe coding como ferramenta de curiosidade</h2>
<p>Firmware em C++ pra ESP32 nao eh o meu dominio. Sou sysadmin, trabalho com sistemas, redes,
containers, automacao. Entendo concorrencia, sei o que eh um mutex, ja depurei race conditions
em producao. Mas escrever ISRs, gerenciar a memoria de um microcontrolador e lidar com as
idiossincrasias do Arduino framework nao eh parada que eu faria de cabeca.</p>
<p>A barreira nao era intelectual. Era de vocabulario. Eu sabia o que queria construir, sabia
por que certos problemas acontecem, mas nao sabia o nome das pecas em C++ embarcado o
suficiente pra transformar isso em codigo.</p>
<p>A IA virou o interprete entre o que eu entendia e o que o compilador aceitava.</p>
<p>O que muda quando voce tem base tecnica e entra num dominio adjacente com IA eh diferente de
&quot;pedir pra IA escrever codigo&quot;. A base que eu ja tinha mudou a qualidade das perguntas. Quando
a IA gerou codigo de inicializacao do SX1262 com pinagem errada pro Cardputer ADV, eu soube
que tava errado porque entendia o hardware. Quando ela sugeriu separar Wi-Fi e sensores em
cores diferentes do ESP32-S3, eu entendi o motivo antes de aceitar, porque concorrencia nao
eh bagulho novo pra mim.</p>
<p>Tem uma imagem que gosto: IA como estagiario brilhante que nao sabe nada do seu ambiente
especifico. Sabe tudo que ta na documentacao. Nao sabe nada que voce so aprende debugando
o hardware na sua frente. Util. Mas o estagiario as vezes te ensina coisa tambem. Nao porque
eh mais inteligente. Porque leu mais documentacao do que voce algum dia vai ler.</p>
<p>Tem gente que faz a distincao: se voce revisa, testa e entende todo o codigo gerado, isso nao
eh vibe coding. Eh usar a IA como assistente de digitacao. Vibe coding de verdade eh quando
voce aceita o output sem entender e reza pra funcionar.</p>
<p>Eu fiquei no meio. Entendi o suficiente pra depurar. Mas nao saberia ter escrito do zero.
E tudo bem com isso.</p>
<p>A parte mais honesta de contar eh que ver o hardware respondendo pela primeira vez, o browser
abrindo o dashboard no telefone sem nenhuma infraestrutura intermediaria, tem uma qualidade de
empolgacao que eh dificil de explicar sem soar dramtico. Voce teve a ideia, trouxe pro mundo,
funciona. Nao importa quanto do codigo a IA escreveu. O que importa eh que voce entendeu o
suficiente pra que funcionasse.</p>
<h2><a href="#o-que-a-ia-erra-em-embedded" aria-hidden="true" class="anchor" id="o-que-a-ia-erra-em-embedded"></a>O que a IA erra em embedded</h2>
<p>A IA nao tem o mapa de memoria do seu chip. Ela nao sabe que o ESP32-S3 do seu modulo especifico
tem uma configuracao de PSRAM diferente da que ta na documentacao oficial. Ela nao sabe que
a versao do RadioLib que voce ta usando mudou uma API em relacao ao exemplo que ela tem na
cabeca. Ela nao sabe que o driver FATFS da Espressif tem um comportamento contraintuitivo que
so aparece na interacao com USB MSC.</p>
<p>Ela sabe o que ta escrito. O que nao ta escrito, voce descobre sozinho.</p>
<p>No processo do dashIO, a IA errou na pinagem do GPS na primeira tentativa, sugeriu uma API
do ArduinoJson que nao existia mais na versao que eu tava usando, e gerou um padrao de
inicializacao do SD que causava kernel panic. Em cada caso, o processo de descobrir por que
nao funcionava foi onde o aprendizado aconteceu.</p>
<p>Bugs tem personalidade em embedded. Nao tem stack trace com numero de linha. O sintoma eh
&quot;o radio some depois do primeiro TX&quot; ou &quot;o firmware trava so quando tem muito trafego de rede&quot;
ou &quot;kernel panic na inicializacao do pendrive, mas so as vezes&quot;. Entender o sintoma ja eh metade
do trabalho. A IA ajudou a gerar hipoteses. Confirmar ou descartar foi por minha conta.</p>
<h2><a href="#tres-momentos-onde-o-bug-virou-entendimento" aria-hidden="true" class="anchor" id="tres-momentos-onde-o-bug-virou-entendimento"></a>Tres momentos onde o bug virou entendimento</h2>
<h3><a href="#o-imu-que-travava-junto-com-a-rede" aria-hidden="true" class="anchor" id="o-imu-que-travava-junto-com-a-rede"></a>O IMU que travava junto com a rede</h3>
<p>O grafico de sensores travava toda vez que chegava trafego de rede. Fui olhar pro IMU, pro
WebSocket, nao achei nada. Demorou ate cair a ficha: o stack Wi-Fi da Espressif roda eventos
no Core 0 e eu tava lendo o IMU no mesmo lugar. Toda vez que o Wi-Fi processava um pacote,
o IMU esperava. Bang.</p>
<p>O ESP32-S3 tem dois cores. A maioria dos firmwares Arduino ignora isso e bota tudo no Core 1,
que eh o padrao do runtime. Pra firmware simples funciona. Pra um com WebServer, Wi-Fi, leitura
de IMU e LoRa ao mesmo tempo, nao funciona do jeito que voce espera.</p>
<p>A IA sugeriu separar os dois cores quando eu descrevi o problema. Core 0 fica com WebServer e
Wi-Fi. Core 1 le os sensores a cada 100ms. Estado compartilhado protegido por mutex FreeRTOS.</p>
<p>O mutex sozinho resolve o acesso concorrente, mas tem um detalhe chato: se voce da <code>Take</code> e
esquece de chamar <code>Give</code> numa saida antecipada da funcao, o mutex trava pra sempre. Deadlock
silencioso, firmware congela, sem mensagem de erro obvia.</p>
<p>A IA sugeriu um wrapper RAII pra isso, equivalente ao <code>lock_guard</code> do C++ padrao. Em embedded
com Arduino framework voce implementa na mao. O destrutor chama <code>Give</code> automaticamente quando
o objeto sai de escopo, independente de como a funcao termina. Simples. Mas eu so entendi por
que era necessario depois de ver o deadlock acontecer uma vez sem ele.</p>
<h3><a href="#o-lora-que-sumia-depois-do-primeiro-tx" aria-hidden="true" class="anchor" id="o-lora-que-sumia-depois-do-primeiro-tx"></a>O LoRa que sumia depois do primeiro TX</h3>
<p>O SX1262 tem um comportamento que nao eh obvio na documentacao do RadioLib: depois de um TX em
modo blocking, o chip nao volta automaticamente ao modo RX. Voce precisa chamar <code>startReceive()</code>
explicitamente. Se nao chamar, o radio fica em idle. Pacotes passam e o firmware nem sabe.</p>
<p>Levou um tempo ate fechar a causa porque o sintoma era intermitente. &quot;Transmitiu uma vez, depois
sumiu da rede.&quot; Pode ser hardware, pode ser driver, pode ser configuracao de frequencia errada.</p>
<p>A solucao foi ISR com flag. A IA gerou o padrao. Mas junto com ele veio o <code>IRAM_ATTR</code> na
declaracao da ISR, e eu perguntei por que.</p>
<p>A resposta foi o momento mais interessante do processo. Sem <code>IRAM_ATTR</code>, a ISR fica na flash,
que pode estar no PSRAM dependendo da configuracao de memoria. Flash tem latencia. Se o SX1262
dispara a interrupcao e a ISR demora pra responder porque ta sendo carregada da flash, voce pode
perder a janela de leitura ou causar crash por violacao de acesso. O atributo forca o compilador
a colocar a funcao na RAM interna, que tem latencia garantida.</p>
<p>Eu nao teria chegado nisso sozinho. Nao teria feito a pergunta certa se nao tivesse lendo e
entendendo o codigo antes de aceitar. Ta ligado?</p>
<h3><a href="#o-kernel-panic-do-sd-que-queria-ser-pendrive" aria-hidden="true" class="anchor" id="o-kernel-panic-do-sd-que-queria-ser-pendrive"></a>O kernel panic do SD que queria ser pendrive</h3>
<p>O ESP32-S3 tem USB nativo. Com a biblioteca <code>USB_MSC</code> da Espressif voce expoe o cartao SD
como se fosse um pendrive. A ideia era dahora pro firmware: usuario conecta o USB, o SD aparece
como drive, arrasta arquivos, pronto.</p>
<p>A sequencia natural parecia obvia: desmontar o SD do firmware, iniciar o MSC, expor os blocos.
A IA gerou exatamente isso, com <code>SD.end()</code> antes de iniciar o MSC.</p>
<p>Kernel panic toda vez.</p>
<p>O motivo ficou claro depois de ler o codigo interno do driver FATFS da Espressif. O <code>SD.end()</code>
desregistra o driver de disco do FATFS, mas o MSC usa esse mesmo driver diretamente pra ler e
escrever blocos. Quando o MSC tentava acessar o disco via driver que nao existia mais, panico
no kernel.</p>
<p>A solucao foi contraintuitiva: nao chamar <code>SD.end()</code> em momento algum. O driver FATFS fica
registrado. O MSC usa ele. O firmware bloqueia qualquer acesso proprio ao SD via flag enquanto
o MSC ta ativo.</p>
<p>A IA nao sabia disso. Nao tinha como saber, porque eh um comportamento que so aparece na
interacao entre dois subsistemas especificos da Espressif. Mas quando eu descrevi o sintoma e
o stack trace, ela chegou perto da causa e eu fechei o restante.</p>
<figure>
<img src="media/gallery/2026-05-02-dashio-terminal.jpg"
  alt="Terminal web do dashIO mostrando output do comando help e prompt dashIO:/>">
<figcaption>O Terminal via WebSocket. Cada cliente tem CWD proprio, entao dois browsers abertos nao interferem um no outro.</figcaption>
</figure>
<h2><a href="#o-parser-nmea-que-a-ia-nao-quis-usar-biblioteca" aria-hidden="true" class="anchor" id="o-parser-nmea-que-a-ia-nao-quis-usar-biblioteca"></a>O parser NMEA que a IA nao quis usar biblioteca</h2>
<p>Tem um momento no processo que foi diferente dos outros. Eu ia usar TinyGPS++ pra parsear
NMEA. A IA sugeriu implementar o parser na mao, porque pra <code>$GPRMC</code> e <code>$GPGGA</code> sao so 80
linhas e reduzia uma dependencia.</p>
<p>Eu fui junto. E aprendi o protocolo no processo.</p>
<p>NMEA representa longitude como <code>DDDMM.MMMMM</code>, onde os dois ultimos digitos antes do ponto sao
minutos, nao fracao de grau. Eh o tipo de detalhe que qualquer um que trabalha com GPS aprende
da pior forma: o dado entra errado, a posicao no mapa parece certa mas ta alguns quilometros
deslocada, e voce fica olhando pra pinagem durante meia hora antes de olhar pra unidade. Vacilo
classico.</p>
<p>Aprender o protocolo direto foi mais util do que ter a biblioteca fazendo isso por baixo dos
panos. Nao eh uma decisao de engenharia sofisticada. Foi um rabbit hole maneiro que valeu a
pena.</p>
<h2><a href="#assets-no-sd-sem-precisar-copiar-nada" aria-hidden="true" class="anchor" id="assets-no-sd-sem-precisar-copiar-nada"></a>Assets no SD sem precisar copiar nada</h2>
<p>Uma friccao classica de firmware web: onde ficam os arquivos HTML, CSS e JavaScript?</p>
<p>A opcao mais comum eh PROGMEM, onde tudo fica na flash e voce reflasha a cada mudanca nos
assets. A IA sugeriu uma solucao diferente: um script Python de pre-build que le os assets,
gera arrays PROGMEM no build, e no primeiro boot o firmware extrai esses arrays pro SD
automaticamente. A proxima requisicao ja encontra os arquivos la.</p>
<p>Flash o firmware, coloca o SD vazio, liga o Cardputer. O dashboard abre no browser sem copiar
nada manualmente. Dahora.</p>
<p>Tem um custo: o firmware ficou grande, a partition table usa <code>huge_app.csv</code>, e OTA padrao nao
funciona sem ajustar as particoes. Trade-off que eu aceitei consciente.</p>
<figure>
<img src="media/gallery/2026-05-02-dashio-sensors.jpg"
  alt="Aba Sensors do dashIO mostrando grafico em tempo real do acelerometro e giroscopio BMI270">
<figcaption>Sensors em tempo real. O grafico mantem 60 pontos; cada frame chega por WebSocket a 200ms.</figcaption>
</figure>
<h2><a href="#o-que-ainda-falta" aria-hidden="true" class="anchor" id="o-que-ainda-falta"></a>O que ainda falta</h2>
<p>O repo ta publico em <a href="https://github.com/raphazilla/dashIO">github.com/raphazilla/dashIO</a>,
mas tem paradas que ainda nao estao prontas.</p>
<p>A aba Radio ainda nao tem screenshot com pacote LoRa real nesse post. Funcionalmente ta
completa, transmite e recebe com RSSI e SNR corretos, mas quero registrar uma sessao real de
comunicacao entre dois nos antes de considerar essa parte documentada. O SD como pendrive
tambem nao tem foto, ainda.</p>
<p>Na lista do que vem a seguir:</p>
<ul>
<li>Preview de imagens no Media (atualmente lista arquivos mas nao renderiza)</li>
<li>Drag-and-drop no upload do Media</li>
<li>Integracao com Meshtastic via API serial</li>
</ul>
<p>Esse ultimo eh o que mais me interessa. O Cardputer com LoRa ativo e Meshtastic no outro no ja
tem o hardware. So falta o codigo.</p>
<h2><a href="#sobre-o-processo-de-vibe-coding-em-si" aria-hidden="true" class="anchor" id="sobre-o-processo-de-vibe-coding-em-si"></a>Sobre o processo de vibe coding em si</h2>
<p>Esse post foi sobre o firmware. Mas tem um post separado que preciso escrever sobre o processo
de vibe coding em si, porque tem mais a falar do que cabe aqui.</p>
<p>O que funcionou, o que nao funcionou, quando a IA te leva pro caminho errado com muita
confianca, e como a base tecnica que voce ja tem muda completamente a qualidade da colaboracao.
Tem tambem a questao de onde fica o aprendizado quando boa parte do codigo foi gerado. Eu tenho
uma visao sobre isso que nao eh simples de resumir num paragrafo.</p>
<p>Fica pra proxima.</p>
<p>Se voce ta mexendo com ESP32, firmware, ou ja entrou nesse rabbit hole de vibe coding com
hardware, me conta o que ta fazendo. As redes estao la na <a href="sobre.html">pagina sobre</a>.</p>
]]></content:encoded></item></channel></rss>