dashIO: firmware, bugs, e o que a IA nao sabe sobre o seu chip
📋 Índice
- O que o firmware faz
- Vibe coding como ferramenta de curiosidade
- O que a IA erra em embedded
- Tres momentos onde o bug virou entendimento
- O IMU que travava junto com a rede
- O LoRa que sumia depois do primeiro TX
- O kernel panic do SD que queria ser pendrive
- O parser NMEA que a IA nao quis usar biblioteca
- Assets no SD sem precisar copiar nada
- O que ainda falta
- Sobre o processo de vibe coding em si
No post anterior sobre o Cardputer ADV eu terminei com uma promessa: "O dashIO merece um post so dele." Esse eh o em breve.
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.
Antes de qualquer coisa: dashIO aqui eh o nome do firmware que eu fiz pro Cardputer. Nao tem nada a ver com dashio.io, 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.
O que o firmware faz
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 "dashIO", voce abre 192.168.4.1 em
qualquer browser e tem o hardware todo na frente em tempo real.
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.
Vibe coding como ferramenta de curiosidade
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.
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.
A IA virou o interprete entre o que eu entendia e o que o compilador aceitava.
O que muda quando voce tem base tecnica e entra num dominio adjacente com IA eh diferente de "pedir pra IA escrever codigo". 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.
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.
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.
Eu fiquei no meio. Entendi o suficiente pra depurar. Mas nao saberia ter escrito do zero. E tudo bem com isso.
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.
O que a IA erra em embedded
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.
Ela sabe o que ta escrito. O que nao ta escrito, voce descobre sozinho.
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.
Bugs tem personalidade em embedded. Nao tem stack trace com numero de linha. O sintoma eh "o radio some depois do primeiro TX" ou "o firmware trava so quando tem muito trafego de rede" ou "kernel panic na inicializacao do pendrive, mas so as vezes". Entender o sintoma ja eh metade do trabalho. A IA ajudou a gerar hipoteses. Confirmar ou descartar foi por minha conta.
Tres momentos onde o bug virou entendimento
O IMU que travava junto com a rede
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.
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.
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.
O mutex sozinho resolve o acesso concorrente, mas tem um detalhe chato: se voce da Take e
esquece de chamar Give numa saida antecipada da funcao, o mutex trava pra sempre. Deadlock
silencioso, firmware congela, sem mensagem de erro obvia.
A IA sugeriu um wrapper RAII pra isso, equivalente ao lock_guard do C++ padrao. Em embedded
com Arduino framework voce implementa na mao. O destrutor chama Give 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.
O LoRa que sumia depois do primeiro TX
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 startReceive()
explicitamente. Se nao chamar, o radio fica em idle. Pacotes passam e o firmware nem sabe.
Levou um tempo ate fechar a causa porque o sintoma era intermitente. "Transmitiu uma vez, depois sumiu da rede." Pode ser hardware, pode ser driver, pode ser configuracao de frequencia errada.
A solucao foi ISR com flag. A IA gerou o padrao. Mas junto com ele veio o IRAM_ATTR na
declaracao da ISR, e eu perguntei por que.
A resposta foi o momento mais interessante do processo. Sem IRAM_ATTR, 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.
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?
O kernel panic do SD que queria ser pendrive
O ESP32-S3 tem USB nativo. Com a biblioteca USB_MSC 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.
A sequencia natural parecia obvia: desmontar o SD do firmware, iniciar o MSC, expor os blocos.
A IA gerou exatamente isso, com SD.end() antes de iniciar o MSC.
Kernel panic toda vez.
O motivo ficou claro depois de ler o codigo interno do driver FATFS da Espressif. O SD.end()
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.
A solucao foi contraintuitiva: nao chamar SD.end() 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.
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.
O parser NMEA que a IA nao quis usar biblioteca
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 $GPRMC e $GPGGA sao so 80
linhas e reduzia uma dependencia.
Eu fui junto. E aprendi o protocolo no processo.
NMEA representa longitude como DDDMM.MMMMM, 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.
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.
Assets no SD sem precisar copiar nada
Uma friccao classica de firmware web: onde ficam os arquivos HTML, CSS e JavaScript?
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.
Flash o firmware, coloca o SD vazio, liga o Cardputer. O dashboard abre no browser sem copiar nada manualmente. Dahora.
Tem um custo: o firmware ficou grande, a partition table usa huge_app.csv, e OTA padrao nao
funciona sem ajustar as particoes. Trade-off que eu aceitei consciente.
O que ainda falta
O repo ta publico em github.com/raphazilla/dashIO, mas tem paradas que ainda nao estao prontas.
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.
Na lista do que vem a seguir:
- Preview de imagens no Media (atualmente lista arquivos mas nao renderiza)
- Drag-and-drop no upload do Media
- Integracao com Meshtastic via API serial
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.
Sobre o processo de vibe coding em si
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.
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.
Fica pra proxima.
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 pagina sobre.