<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>protoclolos - MCU &amp; FPGA</title>
	<atom:link href="https://mcu.tec.br/categorias/protoclolos/feed/" rel="self" type="application/rss+xml" />
	<link>https://mcu.tec.br</link>
	<description>Microcontroladores &#38; FPGA</description>
	<lastBuildDate>Sun, 19 Apr 2026 18:27:01 +0000</lastBuildDate>
	<language>pt-BR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://mcu.tec.br/wp-content/uploads/2025/02/Robo-para-o-site-MCU.tec_.br-512x512-1-150x150.png</url>
	<title>protoclolos - MCU &amp; FPGA</title>
	<link>https://mcu.tec.br</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Baud Rate: o que é, origem, tabela completa e como escolher a taxa ideal na comunicação serial</title>
		<link>https://mcu.tec.br/protoclolos/uart-serial/baud-rate-o-que-e-origem-tabela-completa-e-como-escolher-a-taxa-ideal-na-comunicacao-serial/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=baud-rate-o-que-e-origem-tabela-completa-e-como-escolher-a-taxa-ideal-na-comunicacao-serial</link>
					<comments>https://mcu.tec.br/protoclolos/uart-serial/baud-rate-o-que-e-origem-tabela-completa-e-como-escolher-a-taxa-ideal-na-comunicacao-serial/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Delfino]]></dc:creator>
		<pubDate>Mon, 20 Apr 2026 18:21:56 +0000</pubDate>
				<category><![CDATA[UART (Serial)]]></category>
		<guid isPermaLink="false">https://mcu.tec.br/?p=1426</guid>

					<description><![CDATA[<p>Entenda de forma didática o que é baud rate, sua origem histórica ligada à telegrafia de Émile Baudot e como essa taxa influencia diretamente a comunicação serial em sistemas embarcados modernos. Neste artigo, você verá uma explicação clara sobre a diferença entre baud rate e bits por segundo, além de compreender o impacto do tempo de bit na transmissão de dados. Apresentamos também uma tabela completa com os principais baud rates, suas velocidades reais e duração dos bits, além de destacar as taxas mais utilizadas como 9600, 115200 e 921600 baud, explicando em quais contextos são aplicadas, desde sistemas industriais até IoT e debug de firmware.</p>
<p>The post <a href="https://mcu.tec.br/protoclolos/uart-serial/baud-rate-o-que-e-origem-tabela-completa-e-como-escolher-a-taxa-ideal-na-comunicacao-serial/">Baud Rate: o que é, origem, tabela completa e como escolher a taxa ideal na comunicação serial</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Quando trabalhamos com sistemas embarcados, comunicação serial ou redes industriais, um dos primeiros termos que aparece é <strong>baud rate</strong> (ou taxa de transmissão). Embora muitas vezes tratado de forma superficial como “velocidade da comunicação”, o conceito possui uma origem histórica interessante e nuances técnicas importantes que impactam diretamente a confiabilidade e o desempenho dos sistemas.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">O que é Baud Rate</h3>



<p>O termo <em>baud</em> representa a <strong>quantidade de símbolos transmitidos por segundo</strong> em um canal de comunicação. Em sistemas simples, como UART (Universal Asynchronous Receiver/Transmitter), cada símbolo geralmente corresponde a um bit, e por isso, na prática, baud rate costuma ser tratado como equivalente a <strong>bits por segundo (bps)</strong>.</p>



<p>No entanto, tecnicamente, eles não são sempre iguais. Em sistemas mais complexos (como modulações QAM), um único símbolo pode carregar múltiplos bits. Mas no contexto mais comum em microcontroladores — como STM32, ESP32 ou RP2040 — temos:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>1 baud ≈ 1 bit por segundo</strong></p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Origem do termo</h3>



<p>O nome <em>baud</em> vem de <strong>Émile Baudot</strong>, engenheiro francês que desenvolveu um dos primeiros sistemas de telegrafia digital no século XIX. Seu sistema utilizava códigos binários para transmitir caracteres, sendo um dos precursores diretos da comunicação digital moderna.</p>



<p>Na época, a preocupação principal era sincronizar transmissor e receptor em meios físicos limitados, como linhas telegráficas. Essa limitação influenciou diretamente as primeiras taxas padronizadas que ainda vemos hoje.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Como o Baud Rate é utilizado na prática</h3>



<p>Em sistemas modernos, especialmente embarcados, o baud rate é fundamental em protocolos como:</p>



<ul class="wp-block-list">
<li>UART (comunicação serial clássica)</li>



<li>RS-232, RS-485 (ambientes industriais)</li>



<li>Comunicação com módulos (GPS, GSM, Bluetooth)</li>



<li>Bootloaders e debug via terminal serial</li>
</ul>



<p>Cada taxa define <strong>quanto tempo dura cada bit</strong>, o que impacta diretamente:</p>



<ul class="wp-block-list">
<li>sincronização entre dispositivos</li>



<li>tolerância a ruído</li>



<li>distância da comunicação</li>



<li>consumo de energia</li>
</ul>



<p>Por exemplo, em <strong>9600 baud</strong>, cada bit dura aproximadamente <strong>104 µs</strong>, enquanto em <strong>115200 baud</strong>, dura apenas <strong>8.68 µs</strong>, exigindo maior precisão de clock.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Entendendo a diferença entre velocidade teórica e real</h3>



<p>Na tabela fornecida, aparecem dois valores importantes:</p>



<ul class="wp-block-list">
<li><strong>Speed (bytes/s)</strong> → taxa teórica considerando 8 bits por byte</li>



<li><strong>Actual speed (bytes/s)</strong> → taxa real considerando overhead da UART</li>
</ul>



<p>Esse overhead vem do formato típico de transmissão:</p>



<pre class="wp-block-code"><code>1 bit de start + 8 bits de dados + 1 bit de stop = 10 bits por byte
</code></pre>



<p>Ou seja:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>A eficiência real é de aproximadamente <strong>80%</strong> (8/10)</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Tabela de Baud Rates</h3>



<p>Abaixo está a tabela organizada com os dados fornecidos:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Bauds</th><th>Bits/s</th><th>Duração do bit</th><th>Velocidade (bytes/s)</th><th>Velocidade real (bytes/s)</th><th>Duração real por byte</th></tr></thead><tbody><tr><td>50</td><td>50</td><td>20.000 ms</td><td>6.25</td><td>5</td><td>200.000 ms</td></tr><tr><td>75</td><td>75</td><td>13.333 ms</td><td>9.375</td><td>7.5</td><td>133.333 ms</td></tr><tr><td>110</td><td>110</td><td>9.091 ms</td><td>13.75</td><td>11</td><td>90.909 ms</td></tr><tr><td>134</td><td>134</td><td>7.463 ms</td><td>16.75</td><td>13.4</td><td>74.627 ms</td></tr><tr><td>150</td><td>150</td><td>6.667 ms</td><td>18.75</td><td>15</td><td>66.667 ms</td></tr><tr><td>200</td><td>200</td><td>5.000 ms</td><td>25</td><td>20</td><td>50.000 ms</td></tr><tr><td>300</td><td>300</td><td>3.333 ms</td><td>37.5</td><td>30</td><td>33.333 ms</td></tr><tr><td>600</td><td>600</td><td>1.667 ms</td><td>75</td><td>60</td><td>16.667 ms</td></tr><tr><td>1200</td><td>1200</td><td>833.333 µs</td><td>150</td><td>120</td><td>8.333 ms</td></tr><tr><td>1800</td><td>1800</td><td>555.556 µs</td><td>225</td><td>180</td><td>5.556 ms</td></tr><tr><td>2400</td><td>2400</td><td>416.667 µs</td><td>300</td><td>240</td><td>4.167 ms</td></tr><tr><td>4800</td><td>4800</td><td>208.333 µs</td><td>600</td><td>480</td><td>2.083 ms</td></tr><tr><td>9600</td><td>9600</td><td>104.167 µs</td><td>1200</td><td>960</td><td>1.042 ms</td></tr><tr><td>19200</td><td>19200</td><td>52.083 µs</td><td>2400</td><td>1920</td><td>520.833 µs</td></tr><tr><td>28800</td><td>28800</td><td>34.722 µs</td><td>3600</td><td>2880</td><td>347.222 µs</td></tr><tr><td>38400</td><td>38400</td><td>26.042 µs</td><td>4800</td><td>3840</td><td>260.417 µs</td></tr><tr><td>57600</td><td>57600</td><td>17.361 µs</td><td>7200</td><td>5760</td><td>173.611 µs</td></tr><tr><td>76800</td><td>76800</td><td>13.021 µs</td><td>9600</td><td>7680</td><td>130.208 µs</td></tr><tr><td>115200</td><td>115200</td><td>8.681 µs</td><td>14400</td><td>11520</td><td>86.806 µs</td></tr><tr><td>230400</td><td>230400</td><td>4.340 µs</td><td>28800</td><td>23040</td><td>43.403 µs</td></tr><tr><td>460800</td><td>460800</td><td>2.170 µs</td><td>57600</td><td>46080</td><td>21.701 µs</td></tr><tr><td>576000</td><td>576000</td><td>1.736 µs</td><td>72000</td><td>57600</td><td>17.361 µs</td></tr><tr><td>921600</td><td>921600</td><td>1.085 µs</td><td>115200</td><td>92160</td><td>10.851 µs</td></tr></tbody></table></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Velocidades mais usadas e seus contextos</h3>



<p>Na prática, algumas taxas se tornaram padrão de mercado:</p>



<p><strong>9600 baud</strong><br>Muito utilizado em sistemas legados, sensores simples e comunicação robusta em ambientes com ruído. Ideal quando confiabilidade é mais importante que velocidade.</p>



<p><strong>19200 e 38400 baud</strong><br>Utilizados em sistemas industriais e automação, oferecendo bom equilíbrio entre velocidade e estabilidade.</p>



<p><strong>57600 baud</strong><br>Comum em comunicação com módulos embarcados intermediários (GPS, modems antigos).</p>



<p><strong>115200 baud</strong><br>O padrão mais usado atualmente para debug serial, comunicação com PCs e bootloaders. É rápido o suficiente para logs e transferência moderada de dados.</p>



<p><strong>230400 a 921600 baud</strong><br>Usados em aplicações mais exigentes, como:</p>



<ul class="wp-block-list">
<li>streaming de dados de sensores</li>



<li>comunicação com displays</li>



<li>transferência de arquivos</li>



<li>aplicações IoT de maior throughput</li>
</ul>



<p>Porém, nessas velocidades, entram desafios importantes:</p>



<ul class="wp-block-list">
<li>precisão do clock (erro percentual)</li>



<li>interferência eletromagnética</li>



<li>qualidade do layout de PCB</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Considerações de Engenharia</h3>



<p>Ao escolher o baud rate em um projeto embarcado, você deve considerar:</p>



<ul class="wp-block-list">
<li><strong>Clock do sistema</strong>: erros de divisão podem gerar falhas de sincronização</li>



<li><strong>Comprimento do cabo</strong>: quanto maior, menor deve ser a taxa</li>



<li><strong>Ambiente</strong>: ruído industrial exige taxas menores</li>



<li><strong>Buffer e DMA</strong>: taxas altas exigem melhor gestão de dados</li>
</ul>



<p>Uma decisão errada aqui pode gerar desde perda de dados até travamentos intermitentes difíceis de depurar.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/><p>The post <a href="https://mcu.tec.br/protoclolos/uart-serial/baud-rate-o-que-e-origem-tabela-completa-e-como-escolher-a-taxa-ideal-na-comunicacao-serial/">Baud Rate: o que é, origem, tabela completa e como escolher a taxa ideal na comunicação serial</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://mcu.tec.br/protoclolos/uart-serial/baud-rate-o-que-e-origem-tabela-completa-e-como-escolher-a-taxa-ideal-na-comunicacao-serial/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1426</post-id>	</item>
		<item>
		<title>FreeRTOS com lwIP: Arquitetura, Boas Práticas e Exemplos Reais de Sistemas Embarcados em Rede</title>
		<link>https://mcu.tec.br/rtos/freertos-com-lwip-arquitetura-boas-praticas-e-exemplos-reais-de-sistemas-embarcados-em-rede/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=freertos-com-lwip-arquitetura-boas-praticas-e-exemplos-reais-de-sistemas-embarcados-em-rede</link>
					<comments>https://mcu.tec.br/rtos/freertos-com-lwip-arquitetura-boas-praticas-e-exemplos-reais-de-sistemas-embarcados-em-rede/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Delfino]]></dc:creator>
		<pubDate>Fri, 06 Mar 2026 09:25:08 +0000</pubDate>
				<category><![CDATA[lwIP]]></category>
		<category><![CDATA[RTOS]]></category>
		<category><![CDATA[freertos]]></category>
		<category><![CDATA[LWIP]]></category>
		<guid isPermaLink="false">https://mcu.tec.br/?p=1082</guid>

					<description><![CDATA[<p>Este artigo apresenta um guia completo e aprofundado sobre o uso do FreeRTOS em sistemas embarcados que utilizam a pilha de rede lwIP. O conteúdo aborda desde os fundamentos arquiteturais da integração entre RTOS e TCP/IP até padrões de projeto aplicados em firmware de produção. São exploradas as APIs do lwIP (RAW, Netconn e BSD Sockets), a função da TCP/IP thread, o uso correto de mailboxes, semáforos e filas do FreeRTOS, além de exemplos práticos em C envolvendo servidores TCP, arquitetura Gatekeeper, produtor/consumidor, framing de protocolos TCP, múltiplos clientes, timeouts, keepalive, backpressure e integração com Task Monitor e Watchdog. O artigo é voltado a desenvolvedores de sistemas embarcados que buscam robustez, previsibilidade temporal e boas práticas para aplicações conectadas em microcontroladores Cortex-M, ESP32 e plataformas similares, indo além de exemplos simplificados e focando em soluções aplicáveis ao ambiente industrial e de campo.</p>
<p>The post <a href="https://mcu.tec.br/rtos/freertos-com-lwip-arquitetura-boas-praticas-e-exemplos-reais-de-sistemas-embarcados-em-rede/">FreeRTOS com lwIP: Arquitetura, Boas Práticas e Exemplos Reais de Sistemas Embarcados em Rede</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></description>
										<content:encoded><![CDATA[<h3 class="wp-block-heading"><strong>Introdução: FreeRTOS em sistemas com lwIP</strong></h3>



<p>A combinação do <strong>FreeRTOS</strong> com a pilha de rede <strong>lwIP (Lightweight IP)</strong> é hoje uma das arquiteturas mais comuns em sistemas embarcados conectados, especialmente em microcontroladores Cortex-M (STM32, NXP, RP2040 com stack externa, ESP32 quando abstraído), onde há restrição severa de memória, tempo real e consumo energético. O FreeRTOS fornece o <strong>modelo concorrente determinístico</strong> (tarefas, prioridades, sincronização), enquanto o lwIP entrega uma <strong>implementação enxuta da pilha TCP/IP</strong>, compatível com IPv4, IPv6, TCP, UDP, ICMP, ARP, DHCP, DNS e HTTP, entre outros protocolos.</p>



<p>O ponto crítico — e frequentemente negligenciado — é que o lwIP <strong>não é apenas uma biblioteca de rede</strong>, mas sim um <em>framework cooperativo</em> que precisa ser corretamente integrado ao <strong>modelo de execução do RTOS</strong>. Dependendo da configuração (RAW API, Netconn API ou BSD Sockets API), o lwIP pode operar de forma <strong>event-driven</strong>, <strong>thread-safe</strong> ou <strong>thread-aware</strong>, exigindo decisões arquiteturais bem fundamentadas. Uma escolha inadequada costuma resultar em <em>deadlocks</em>, <em>stack overflow</em>, perda de pacotes, latências imprevisíveis ou até <em>hard faults</em> difíceis de diagnosticar.</p>



<p>Em sistemas com FreeRTOS, o lwIP normalmente é encapsulado em <strong>uma ou mais tarefas dedicadas</strong>, além de callbacks executados a partir de interrupções (ETH IRQ, DMA RX/TX) ou timers de software. Isso cria um ambiente híbrido onde <strong>código de interrupção, tarefas e a pilha TCP/IP</strong> precisam cooperar com rigor: uso correto de semáforos, mutexes, mailboxes (queues), buffers e prioridades. O entendimento dessa interação é essencial para construir aplicações robustas como <strong>servidores HTTP, MQTT brokers/clients, WebSockets, OTA, Modbus/TCP, Profinet sobre TCP/IP</strong>, entre outras.</p>



<p>Nesta série de artigos, este material assume um <strong>cenário típico e realista</strong>:</p>



<ul class="wp-block-list">
<li>FreeRTOS rodando como sistema operacional principal</li>



<li>lwIP configurado com <strong>TCP/IP thread dedicada</strong></li>



<li>Interface Ethernet (MAC + PHY) ou driver de rede equivalente</li>



<li>Comunicação entre tarefas da aplicação e a pilha de rede</li>



<li>Ênfase em <strong>boas práticas, arquitetura correta e exemplos práticos em C</strong></li>
</ul>



<p>Nas próximas seções, vamos avançar progressivamente, começando pela <strong>arquitetura interna do lwIP quando integrado ao FreeRTOS</strong>, passando pelas <strong>formas de integração (RAW, Netconn e Sockets)</strong>, até chegar a <strong>exemplos completos</strong> como servidor HTTP, cliente TCP e sincronização entre tarefas da aplicação e a pilha de rede.</p>



<h3 class="wp-block-heading"><strong>Arquitetura do lwIP em sistemas com FreeRTOS</strong></h3>



<p>Quando o lwIP é integrado a um sistema com FreeRTOS, ele deixa de ser apenas uma pilha TCP/IP “passiva” e passa a fazer parte ativa do <strong>modelo concorrente do sistema</strong>. O ponto central dessa arquitetura é a chamada <strong>TCP/IP thread</strong> (normalmente criada por <code>tcpip_init()</code>), que se torna o <strong>contexto exclusivo e soberano</strong> para quase todas as operações internas do lwIP. Isso significa que estruturas críticas como <em>PCB (Protocol Control Blocks)</em>, buffers <code>pbuf</code>, timers TCP e estados de conexão <strong>não são thread-safe por padrão</strong> e devem ser manipulados apenas dentro desse contexto controlado.</p>



<p>Na prática, o lwIP organiza seu funcionamento em três grandes domínios de execução: <strong>interrupções de hardware</strong>, <strong>TCP/IP thread</strong> e <strong>tarefas da aplicação</strong>. As interrupções normalmente vêm do driver Ethernet (IRQ do MAC ou DMA RX/TX), cujo papel é mínimo: sinalizar que há pacotes recebidos ou buffers liberados, evitando qualquer processamento pesado. Essas interrupções acordam a TCP/IP thread por meio de <em>semaphores</em> ou <em>mailboxes</em>, garantindo que todo o processamento de protocolos ocorra fora do contexto de ISR, preservando determinismo e estabilidade do sistema.</p>



<p>A TCP/IP thread funciona como um <strong>dispatcher de eventos de rede</strong>. Ela processa pacotes recebidos, executa timers periódicos (TCP retransmission, ARP aging, DHCP renewal), gerencia conexões e chama callbacks registrados pela aplicação. É aqui que reside uma armadilha comum: <strong>callbacks do lwIP não executam no contexto da task da aplicação</strong>, mas sim dentro da TCP/IP thread. Isso exige extremo cuidado ao acessar recursos compartilhados, como filas do FreeRTOS, buffers globais ou drivers de periféricos, sob pena de criar condições de corrida difíceis de rastrear.</p>



<p>As tarefas da aplicação, por sua vez, <strong>nunca devem acessar diretamente estruturas internas do lwIP</strong>, exceto quando se utiliza APIs explicitamente thread-safe (como Netconn ou Sockets). Quando a arquitetura é bem desenhada, as tasks da aplicação se comunicam com a TCP/IP thread usando <strong>mensagens, filas ou chamadas assíncronas</strong>, mantendo uma separação clara entre lógica de negócio e infraestrutura de rede. Esse desacoplamento é um dos fatores-chave para escalabilidade e manutenibilidade do firmware.</p>



<p>Do ponto de vista conceitual, pense no lwIP como um <strong>ator central</strong> que serializa todo o tráfego de rede, enquanto o FreeRTOS fornece o <strong>orquestrador de concorrência</strong> ao redor dele. Quando esse modelo é respeitado, o sistema se comporta de forma previsível mesmo sob carga elevada de rede. Quando é violado — por exemplo, chamando funções RAW do lwIP a partir de tasks arbitrárias — os sintomas surgem como bugs intermitentes, travamentos aleatórios ou degradação severa de desempenho.</p>



<p>Na próxima seção, vamos entrar em um dos pontos mais críticos e confusos para quem trabalha com lwIP:<br><strong>as três APIs disponíveis (RAW API, Netconn API e BSD Sockets API)</strong>, explicando quando usar cada uma, suas vantagens, limitações e impactos diretos na arquitetura FreeRTOS.</p>



<h3 class="wp-block-heading"><strong>As APIs do lwIP no contexto do FreeRTOS: RAW, Netconn e Sockets</strong></h3>



<p>Um dos pontos que mais geram confusão — e erros arquiteturais — no uso do lwIP com FreeRTOS é a existência de <strong>três APIs distintas</strong>, cada uma com <strong>modelos mentais, custos e implicações completamente diferentes</strong>. A escolha da API não é apenas uma questão de preferência sintática; ela define <strong>como as tasks interagem com a pilha TCP/IP</strong>, quais garantias de <em>thread-safety</em> existem e qual será o impacto em latência, uso de memória e complexidade do sistema.</p>



<p>A <strong>RAW API</strong> é a forma mais direta e eficiente de usar o lwIP. Ela é totalmente <strong>event-driven</strong>, baseada em callbacks, e opera <strong>exclusivamente no contexto da TCP/IP thread</strong>. Não existe bloqueio, não existem semáforos implícitos, nem cópia adicional de buffers. Cada evento de rede — conexão estabelecida, dados recebidos, erro ou fechamento — dispara uma função de callback registrada previamente. Isso torna a RAW API ideal para sistemas com <strong>restrições severas de RAM e tempo real rígido</strong>, mas ao custo de maior complexidade cognitiva. Em sistemas FreeRTOS, a RAW API <strong>não deve ser chamada diretamente a partir de tasks da aplicação</strong>; qualquer interação precisa ser mediada por mensagens ou funções como <code>tcpip_callback()</code>.</p>



<p>Já a <strong>Netconn API</strong> atua como uma camada intermediária entre a RAW API e o modelo tradicional de threads. Ela encapsula a lógica baseada em callbacks dentro de uma API <strong>bloqueante e thread-safe</strong>, usando <em>mailboxes</em> e <em>semaphores</em> internos. Para o desenvolvedor FreeRTOS, isso significa poder escrever código sequencial — <code>netconn_accept()</code>, <code>netconn_recv()</code>, <code>netconn_write()</code> — dentro de uma task, sem violar as regras internas do lwIP. O custo disso é um pequeno aumento de consumo de memória e latência, além de menos controle fino sobre eventos de baixo nível. Em projetos industriais, a Netconn API costuma ser um <strong>equilíbrio muito saudável entre robustez e simplicidade</strong>.</p>



<p>A <strong>BSD Sockets API</strong>, por sua vez, é a mais familiar para quem vem do mundo Linux ou POSIX. Ela oferece funções como <code>socket()</code>, <code>bind()</code>, <code>listen()</code>, <code>accept()</code>, <code>recv()</code> e <code>send()</code>, com semântica muito próxima à de sistemas operacionais completos. Internamente, ela é construída sobre a Netconn API, herdando suas características e custos. Em FreeRTOS, a Sockets API facilita a portabilidade de código legado e acelera o desenvolvimento inicial, mas frequentemente induz a <strong>arquiteturas pobres</strong>, com tasks bloqueantes demais, pilha grande e baixa previsibilidade temporal se não for bem planejada.</p>



<p>A decisão correta normalmente segue este raciocínio: se o sistema é <strong>altamente restrito e orientado a eventos</strong>, use RAW API; se precisa de <strong>clareza, robustez e integração limpa com FreeRTOS</strong>, use Netconn; se o foco é <strong>portabilidade e velocidade de desenvolvimento</strong>, a Sockets API pode ser aceitável — desde que o impacto em recursos seja cuidadosamente controlado. Em sistemas críticos, misturar APIs sem critério é uma receita certa para problemas difíceis de depurar.</p>



<p>Na próxima seção, vamos aprofundar exatamente <strong>como o FreeRTOS e o lwIP se comunicam internamente</strong>, explorando <em>mailboxes</em>, <em>semaphores</em>, timers e a função <code>tcpip_init()</code>, com diagramas conceituais e exemplos práticos em C.</p>



<h3 class="wp-block-heading"><strong>Integração interna entre FreeRTOS e lwIP: mailboxes, semáforos e <code>tcpip_init()</code></strong></h3>



<p>A espinha dorsal da integração entre FreeRTOS e lwIP é o <strong>mecanismo de mensagens assíncronas</strong> que garante que todo o processamento da pilha TCP/IP ocorra em um <strong>único contexto controlado</strong>. Esse contexto é criado pela chamada a <code>tcpip_init()</code>, responsável por inicializar o lwIP e criar a <strong>TCP/IP thread</strong>, além de suas estruturas de sincronização internas. Entender exatamente o que acontece aqui é essencial para evitar violações de <em>thread-safety</em> e erros sutis de concorrência.</p>



<p>Quando <code>tcpip_init()</code> é chamada, o lwIP cria internamente uma task do FreeRTOS (normalmente chamada de <code>tcpip_thread</code>) e associa a ela uma <strong>mailbox principal</strong>. Essa mailbox funciona como uma fila de mensagens do RTOS, por onde chegam eventos como pacotes recebidos, timers expirados ou callbacks solicitados por outras tasks. O modelo é deliberadamente serial: a TCP/IP thread processa <strong>uma mensagem por vez</strong>, garantindo que as estruturas internas do lwIP nunca sejam acessadas concorrentemente.</p>



<p>As <strong>interrupções de rede</strong> (por exemplo, RX DMA do Ethernet MAC) não processam protocolos diretamente. Em vez disso, elas apenas notificam o sistema — geralmente liberando um semáforo ou enviando um ponteiro de buffer para uma mailbox. O driver Ethernet, então, acorda a TCP/IP thread, que passa a processar os pacotes no contexto correto. Esse desenho é fundamental para manter <strong>latência previsível</strong> e evitar execução de código pesado em ISR, algo especialmente crítico em sistemas com FreeRTOS.</p>



<p>Para permitir que tasks da aplicação interajam com o lwIP sem violar esse modelo, o lwIP fornece funções como <code>tcpip_callback()</code> e <code>tcpip_try_callback()</code>. Essas funções permitem que uma task qualquer solicite a execução de uma função <strong>dentro da TCP/IP thread</strong>, de forma assíncrona. Esse padrão é a base de arquiteturas seguras quando se utiliza a RAW API, pois impede acessos diretos às estruturas internas da pilha a partir de múltiplos contextos.</p>



<p>A Netconn e a Sockets API se apoiam nesse mesmo mecanismo, mas encapsulam toda essa complexidade. Quando uma task chama <code>netconn_recv()</code>, por exemplo, o que acontece por baixo dos panos é um diálogo entre <strong>mailboxes internas</strong>, semáforos e a TCP/IP thread. A task da aplicação fica bloqueada de forma controlada, enquanto o lwIP continua operando normalmente. Isso explica por que essas APIs são thread-safe, mas também por que consomem mais RAM e stack.</p>



<p>Um erro clássico em projetos é tentar “otimizar” esse fluxo acessando diretamente buffers, chamando funções RAW a partir de tasks comuns ou usando mutexes do FreeRTOS para proteger estruturas do lwIP. Essa abordagem <strong>não funciona</strong> e quebra premissas internas do stack. O modelo correto não é proteger o lwIP com mutexes, mas <strong>respeitar o confinamento de contexto imposto pela TCP/IP thread</strong>.</p>



<p>Na próxima seção, vamos aplicar esses conceitos de forma concreta, construindo um <strong>exemplo prático de inicialização do lwIP com FreeRTOS</strong>, incluindo criação de tarefas, configuração do driver Ethernet e verificação do fluxo de dados.</p>



<h3 class="wp-block-heading"><strong>Exemplo prático: inicialização do lwIP em um sistema FreeRTOS</strong></h3>



<p>Nesta seção vamos sair do plano conceitual e entrar no <strong>código real</strong>, mostrando como um sistema típico FreeRTOS + lwIP é inicializado. O objetivo aqui não é apenas “fazer funcionar”, mas <strong>entender por que cada passo existe</strong>, qual o contexto de execução envolvido e quais são os erros clássicos que devem ser evitados.</p>



<p>Assumiremos um cenário bastante comum em projetos industriais:</p>



<ul class="wp-block-list">
<li>FreeRTOS já inicializado</li>



<li>Interface Ethernet com driver próprio (MAC + PHY)</li>



<li>lwIP configurado para uso com <strong>TCP/IP thread dedicada</strong></li>



<li>Uso futuro de Netconn ou Sockets API</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>5.1 Ordem correta de inicialização</strong></h4>



<p>A ordem de inicialização é <strong>crítica</strong>. Um erro frequente é criar tasks de aplicação que usam rede <strong>antes</strong> do lwIP estar totalmente operacional.</p>



<p>A sequência correta, em alto nível, é:</p>



<ol class="wp-block-list">
<li>Inicializar hardware básico (clock, GPIO, PHY, MAC)</li>



<li>Inicializar o lwIP (<code>tcpip_init</code>)</li>



<li>Configurar interface de rede (<code>netif</code>)</li>



<li>Subir a interface (<code>netif_set_up</code>)</li>



<li>Só então criar tasks da aplicação que usam rede</li>



<li>Iniciar o scheduler do FreeRTOS</li>
</ol>



<p>Essa ordem garante que nenhuma task tente acessar a pilha TCP/IP fora do contexto correto.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>5.2 Inicializando o lwIP (<code>tcpip_init</code>)</strong></h4>



<p>O primeiro ponto de integração direta entre FreeRTOS e lwIP é a chamada a <code>tcpip_init()</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#include "lwip/tcpip.h"

static void lwip_init_done(void *arg)
{
    /* Callback chamado quando a TCP/IP thread está pronta */
    (void)arg;
}

void lwip_stack_init(void)
{
    tcpip_init(lwip_init_done, NULL);
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">lwip/tcpip.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">lwip_init_done</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">arg</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* Callback chamado quando a TCP/IP thread está pronta */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    (</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span><span style="color: #D8DEE9">arg</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">lwip_stack_init</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">tcpip_init</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">lwip_init_done</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>O que acontece aqui, de fato:</p>



<ul class="wp-block-list">
<li>O lwIP cria internamente a <strong>TCP/IP thread</strong> como uma task do FreeRTOS</li>



<li>São criadas mailboxes internas para troca de mensagens</li>



<li>Timers internos do TCP/IP são registrados</li>



<li>O callback <code>lwip_init_done()</code> é executado <strong>no contexto da TCP/IP thread</strong>, não na task chamadora</li>
</ul>



<p>Esse detalhe é importante: qualquer código executado nesse callback já está em um <strong>contexto seguro para chamadas RAW</strong>, caso necessário.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>5.3 Configuração da interface de rede (<code>netif</code>)</strong></h4>



<p>Após a inicialização do lwIP, precisamos registrar a interface de rede:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#include "lwip/netif.h"
#include "lwip/ip_addr.h"
#include "lwip/dhcp.h"

static struct netif netif_eth;

void netif_config(void)
{
    ip4_addr_t ipaddr;
    ip4_addr_t netmask;
    ip4_addr_t gw;

    IP4_ADDR(&amp;ipaddr, 0, 0, 0, 0);   /* DHCP */
    IP4_ADDR(&amp;netmask, 0, 0, 0, 0);
    IP4_ADDR(&amp;gw, 0, 0, 0, 0);

    netif_add(&amp;netif_eth,
              &amp;ipaddr,
              &amp;netmask,
              &amp;gw,
              NULL,
              ethernetif_init,
              tcpip_input);

    netif_set_default(&amp;netif_eth);
    netif_set_up(&amp;netif_eth);

    dhcp_start(&amp;netif_eth);
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">lwip/netif.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">lwip/ip_addr.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">lwip/dhcp.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">struct</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">netif</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">netif_eth</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">netif_config</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">ip4_addr_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ipaddr</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">ip4_addr_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">netmask</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">ip4_addr_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">gw</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">IP4_ADDR</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ipaddr</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">   </span><span style="color: #616E88">/* DHCP */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">IP4_ADDR</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">netmask</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">IP4_ADDR</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">gw</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">netif_add</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">netif_eth</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ipaddr</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">netmask</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">gw</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #D8DEE9">NULL</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #D8DEE9">ethernetif_init</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #D8DEE9">tcpip_input</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">netif_set_default</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">netif_eth</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">netif_set_up</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">netif_eth</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">dhcp_start</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">netif_eth</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Aqui aparecem alguns pontos arquiteturais importantes:</p>



<ul class="wp-block-list">
<li><code>ethernetif_init</code> é o <strong>driver de baixo nível</strong>, responsável por integrar MAC/PHY ao lwIP</li>



<li><code>tcpip_input</code> garante que pacotes recebidos sejam entregues <strong>à TCP/IP thread</strong>, e não processados no contexto errado</li>



<li>O uso de DHCP é opcional, mas comum em sistemas conectados</li>
</ul>



<p>Essa função normalmente é chamada <strong>após</strong> <code>tcpip_init()</code> e <strong>antes</strong> da criação das tasks de aplicação.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>5.4 Criação das tasks da aplicação</strong></h4>



<p>Somente depois que a pilha está pronta é que criamos tasks que usam rede:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>void network_task(void *argument)
{
    /* A partir daqui, é seguro usar Netconn ou Sockets API */
    for (;;)
    {
        /* Lógica de rede */
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void app_start(void)
{
    xTaskCreate(network_task,
                "NetTask",
                1024,
                NULL,
                tskIDLE_PRIORITY + 2,
                NULL);
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">network_task</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">argument</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* A partir daqui, é seguro usar Netconn ou Sockets API */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">;;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #616E88">/* Lógica de rede */</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">vTaskDelay</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">pdMS_TO_TICKS</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">1000</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">app_start</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xTaskCreate</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">network_task</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">NetTask</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #B48EAD">1024</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">NULL</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">tskIDLE_PRIORITY</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Note que:</p>



<ul class="wp-block-list">
<li>A task <strong>não interage diretamente com estruturas RAW</strong></li>



<li>O tamanho da stack já precisa considerar buffers de rede</li>



<li>A prioridade deve ser pensada em conjunto com a TCP/IP thread</li>
</ul>



<p>Um erro comum é dar prioridade muito baixa para a TCP/IP thread e alta para tasks de aplicação, o que resulta em <strong>timeouts, perda de pacotes e conexões instáveis</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>5.5 Erros clássicos nesta fase</strong></h4>



<p>Alguns problemas recorrentes que surgem exatamente nesse ponto:</p>



<ul class="wp-block-list">
<li>Criar sockets antes do <code>netif_set_up</code></li>



<li>Chamar funções RAW a partir de tasks comuns</li>



<li>Executar processamento pesado no driver Ethernet (ISR)</li>



<li>Stack insuficiente para tasks de rede</li>



<li>Prioridade inadequada da TCP/IP thread</li>
</ul>



<p>Todos esses erros levam a sintomas difíceis de diagnosticar, como travamentos aleatórios ou falhas intermitentes de comunicação.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>Na próxima seção, vamos construir um <strong>exemplo completo de servidor TCP/HTTP</strong>, mostrando como uma task FreeRTOS usa a <strong>Netconn ou Sockets API</strong> corretamente, com explicação detalhada de cada chamada.</p>



<h3 class="wp-block-heading"><strong>Exemplo completo: servidor TCP usando Sockets API em uma task FreeRTOS</strong></h3>



<p>Aqui vamos montar um servidor TCP simples (estilo “echo server” + base para HTTP) rodando em uma <strong>task do FreeRTOS</strong> usando a <strong>BSD Sockets API do lwIP</strong>. A ideia é te dar um esqueleto real que funciona em 80% dos firmwares conectados: uma task aceita conexões, recebe dados, responde e fecha. Em seguida, vou apontar onde normalmente você evolui para HTTP, MQTT, Modbus/TCP etc.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>6.1 Requisitos e premissas</strong></h4>



<p>Para usar Sockets no lwIP você normalmente precisa ter (no <code>lwipopts.h</code>):</p>



<ul class="wp-block-list">
<li><code>LWIP_SOCKET=1</code></li>



<li><code>LWIP_NETCONN=1</code> (sockets geralmente dependem dela)</li>



<li><code>LWIP_TCP=1</code></li>



<li><code>LWIP_DNS=1</code> (se quiser resolver nomes)</li>



<li><code>LWIP_DHCP=1</code> (se usar DHCP)</li>
</ul>



<p>E lembrar do modelo: <strong>a task de sockets é sua</strong>, mas o lwIP continua processando rede na <strong>TCP/IP thread</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>6.2 Código do servidor TCP (com comentários didáticos)</strong></h4>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#include "FreeRTOS.h"
#include "task.h"

#include "lwip/sockets.h"
#include "lwip/inet.h"
#include &lt;string.h>
#include &lt;stdio.h>

/* Ajuste conforme sua aplicação */
#define SERVER_PORT     5000
#define RX_BUF_SIZE     1024
#define LISTEN_BACKLOG  4

static void tcp_server_task(void *arg)
{
    (void)arg;

    int listen_fd = -1;
    int client_fd = -1;

    struct sockaddr_in addr;
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);

    /* Buffer local: cuidado com stack! */
    char rx_buf&#91;RX_BUF_SIZE&#93;;

    /* 1) Criar socket TCP */
    listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (listen_fd &lt; 0) {
        /* Em sistemas embarcados: logue e reinicie a task ou sinalize falha */
        vTaskDelete(NULL);
    }

    /* 2) Preencher estrutura de endereço */
    memset(&amp;addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    addr.sin_addr.s_addr = htonl(INADDR_ANY); /* escuta em todas interfaces */

    /* 3) Bind: amarra o socket à porta */
    if (bind(listen_fd, (struct sockaddr *)&amp;addr, sizeof(addr)) &lt; 0) {
        closesocket(listen_fd);
        vTaskDelete(NULL);
    }

    /* 4) Listen: coloca em modo servidor */
    if (listen(listen_fd, LISTEN_BACKLOG) &lt; 0) {
        closesocket(listen_fd);
        vTaskDelete(NULL);
    }

    for (;;)
    {
        /* 5) Accept: bloqueia esperando cliente */
        client_fd = accept(listen_fd, (struct sockaddr *)&amp;client_addr, &amp;client_len);
        if (client_fd &lt; 0) {
            /* Se accept falhar, normalmente você continua tentando */
            vTaskDelay(pdMS_TO_TICKS(100));
            continue;
        }

        /* 6) Loop de recepção */
        for (;;)
        {
            int n = recv(client_fd, rx_buf, sizeof(rx_buf) - 1, 0);
            if (n &lt;= 0) {
                /* n == 0 -> cliente fechou; n &lt; 0 -> erro */
                break;
            }

            rx_buf&#91;n&#93; = '\0';

            /* 7) Aqui entra a lógica da aplicação.
                  Para um echo server: devolve os bytes recebidos. */
            send(client_fd, rx_buf, n, 0);

            /* Exemplo: se quiser encerrar ao receber "quit" */
            if (strncmp(rx_buf, "quit", 4) == 0) {
                break;
            }
        }

        /* 8) Fecha conexão do cliente */
        closesocket(client_fd);
        client_fd = -1;
    }
}

/* Função de arranque */
void start_tcp_server(void)
{
    /* Stack precisa ser dimensionada: sockets + buffer local */
    xTaskCreate(tcp_server_task,
                "TCPServer",
                2048,                  /* ajuste real conforme seu MCU/heap */
                NULL,
                tskIDLE_PRIORITY + 2,  /* prioridade média */
                NULL);
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">FreeRTOS.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">task.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">lwip/sockets.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">lwip/inet.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">stdio</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/* Ajuste conforme sua aplicação */</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SERVER_PORT</span><span style="color: #D8DEE9FF">     </span><span style="color: #B48EAD">5000</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">RX_BUF_SIZE</span><span style="color: #D8DEE9FF">     </span><span style="color: #B48EAD">1024</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">LISTEN_BACKLOG</span><span style="color: #D8DEE9FF">  </span><span style="color: #B48EAD">4</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">tcp_server_task</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">arg</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    (</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span><span style="color: #D8DEE9">arg</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">listen_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #B48EAD">1</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #B48EAD">1</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">struct</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sockaddr_in</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">addr</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">struct</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sockaddr_in</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">client_addr</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">socklen_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">client_len</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">client_addr</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* Buffer local: cuidado com stack! */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">char</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rx_buf</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">RX_BUF_SIZE</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* 1) Criar socket TCP */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">listen_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">socket</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">AF_INET</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SOCK_STREAM</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">IPPROTO_TCP</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #616E88">/* Em sistemas embarcados: logue e reinicie a task ou sinalize falha */</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">vTaskDelete</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* 2) Preencher estrutura de endereço */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">memset</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">addr</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">addr</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">addr</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">sin_family</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">AF_INET</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">addr</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">sin_port</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">htons</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">SERVER_PORT</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">addr</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">sin_addr</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">s_addr</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">htonl</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">INADDR_ANY</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">/* escuta em todas interfaces */</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* 3) Bind: amarra o socket à porta */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">bind</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">struct</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sockaddr</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">addr</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">addr</span><span style="color: #D8DEE9FF">)) </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">closesocket</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">vTaskDelete</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* 4) Listen: coloca em modo servidor */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">listen</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">LISTEN_BACKLOG</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">closesocket</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">vTaskDelete</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">;;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #616E88">/* 5) Accept: bloqueia esperando cliente */</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">accept</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">struct</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sockaddr</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">client_addr</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">client_len</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #616E88">/* Se accept falhar, normalmente você continua tentando */</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">vTaskDelay</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">pdMS_TO_TICKS</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">100</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">continue;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #616E88">/* 6) Loop de recepção */</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">;;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">n</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">recv</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">client_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rx_buf</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">rx_buf</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">n</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #616E88">/* n == 0 -&gt; cliente fechou; n &lt; 0 -&gt; erro */</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">break;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">rx_buf</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">n</span><span style="color: #D8DEE9FF">&#93; </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #EBCB8B">\0</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #616E88">/* 7) Aqui entra a lógica da aplicação.</span></span>
<span class="line"><span style="color: #616E88">                  Para um echo server: devolve os bytes recebidos. */</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">send</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">client_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rx_buf</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">n</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #616E88">/* Exemplo: se quiser encerrar ao receber &quot;quit&quot; */</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">strncmp</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">rx_buf</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">quit</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">4</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">break;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #616E88">/* 8) Fecha conexão do cliente */</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">closesocket</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #B48EAD">1</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/* Função de arranque */</span></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">start_tcp_server</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* Stack precisa ser dimensionada: sockets + buffer local */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xTaskCreate</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tcp_server_task</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">TCPServer</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #B48EAD">2048</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">                  </span><span style="color: #616E88">/* ajuste real conforme seu MCU/heap */</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">NULL</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">tskIDLE_PRIORITY</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">/* prioridade média */</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>6.3 Pontos críticos (onde a maioria erra)</strong></h4>



<p>A primeira armadilha é stack. Repare que <code>rx_buf[1024]</code> está na stack da task. Em Cortex-M, 2 KB de stack pode ficar apertado se você começar a montar respostas HTTP grandes, fazer parsing pesado ou usar <code>printf</code>. Uma prática robusta é mover buffers grandes para heap/estático e deixar stack para variáveis pequenas.</p>



<p>A segunda armadilha é bloqueio infinito. <code>accept()</code> e <code>recv()</code> bloqueiam. Isso pode ser OK em um servidor dedicado, mas se você precisa de shutdown limpo, watchdog cooperativo ou multiplexar atividades, você vai querer <strong>timeouts</strong> via <code>setsockopt()</code> com <code>SO_RCVTIMEO</code> e <code>SO_SNDTIMEO</code>, ou usar <code>select()</code> para multiplexar.</p>



<p>A terceira armadilha é “fazer tudo nessa task”. Em firmware sério, o servidor de sockets vira um <strong>front-end</strong> que entrega payloads para outras tasks via <strong>queues</strong> (produtor/consumidor), mantendo o servidor leve e previsível. Caso contrário, você mistura rede + lógica de negócio + acesso a periféricos no mesmo contexto e cria latência ruim e travamentos em cascata.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>6.4 Melhorando com timeouts (essencial para robustez)</strong></h4>



<p>Um exemplo direto para evitar travar em <code>recv()</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &amp;tv, sizeof(tv));
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">struct</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">timeval</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tv</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">tv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tv_sec</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">5</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">tv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tv_usec</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #88C0D0">setsockopt</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">client_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SOL_SOCKET</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SO_RCVTIMEO</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">tv</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tv</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<p>Com isso, <code>recv()</code> retorna erro após 5 s sem dados, e você pode encerrar a conexão ou apenas continuar.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>Na próxima seção, vamos fazer exatamente o que transforma esse exemplo em arquitetura “de produção”:</p>



<ul class="wp-block-list">
<li><strong>Task de sockets como Gatekeeper</strong></li>



<li>Entrega de dados para uma <strong>fila FreeRTOS</strong></li>



<li>Um ou mais <strong>consumidores</strong> processando requisições</li>



<li>Resposta retornando ao cliente de forma segura</li>
</ul>



<h3 class="wp-block-heading"><strong>Arquitetura de produção: servidor de sockets como Gatekeeper + filas FreeRTOS (produtor/consumidor)</strong></h3>



<p>Quando você coloca lwIP + FreeRTOS em um produto real, a pergunta não é “como abrir um socket”, mas sim: <strong>como garantir previsibilidade, isolamento de falhas e escalabilidade</strong> quando há múltiplas conexões, parsing de protocolo, acesso a periféricos e regras de negócio rodando ao mesmo tempo. O padrão mais sólido aqui é tratar a task de rede como um <strong>Gatekeeper</strong> (porteiro): ela faz o mínimo necessário (I/O de rede e framing básico), empacota mensagens e entrega para a aplicação via <strong>Queue</strong>. As tasks de aplicação, por sua vez, consomem essas mensagens e devolvem respostas por um caminho igualmente controlado.</p>



<p>A razão técnica é simples: a pilha de rede precisa de tempo de CPU para manter conexões (ACKs, retransmissões, janelas TCP), e isso pode ser prejudicado se você faz parsing pesado, acesso a flash, escrita em SD, controle de motores ou logs complexos dentro da mesma task que está segurando o socket. Além disso, erros na lógica de negócio não podem derrubar a infraestrutura de rede. Com Gatekeeper + filas, você desacopla os domínios: rede continua estável, aplicação pode reiniciar tasks de processamento, e o sistema ganha uma arquitetura “defensiva”.</p>



<p>A seguir, vamos montar um exemplo funcional e didático. Ele não é “o menor possível”: ele é “o mais correto possível” para firmware.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>7.1 Estruturas de mensagem e filas</strong></h4>



<p>Vamos definir duas filas:</p>



<ul class="wp-block-list">
<li><code>rxQueue</code>: mensagens recebidas da rede para a aplicação</li>



<li><code>txQueue</code>: respostas da aplicação para serem transmitidas pela task de rede</li>
</ul>



<p>Cada mensagem precisa carregar, no mínimo:</p>



<ul class="wp-block-list">
<li><strong>identificador da conexão</strong> (socket do cliente ou um handle abstrato)</li>



<li><strong>payload</strong> (bytes recebidos)</li>



<li><strong>tamanho</strong></li>



<li><strong>metadados</strong> (opcional: IP/porta, timestamp etc.)</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#include "FreeRTOS.h"
#include "queue.h"
#include &lt;stdint.h>

#define MAX_PAYLOAD 512

typedef struct
{
    int client_fd;                 /* Identifica a conexão */
    uint16_t len;
    uint8_t payload&#91;MAX_PAYLOAD&#93;;  /* Mensagem "copiada" para trânsito seguro */
} NetMessage_t;

static QueueHandle_t rxQueue;
static QueueHandle_t txQueue;

void net_queues_init(void)
{
    rxQueue = xQueueCreate(8, sizeof(NetMessage_t));
    txQueue = xQueueCreate(8, sizeof(NetMessage_t));
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">FreeRTOS.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">queue.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">stdint</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">MAX_PAYLOAD</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">512</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">typedef</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">struct</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">client_fd</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">                 </span><span style="color: #616E88">/* Identifica a conexão */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">len</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">payload</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">MAX_PAYLOAD</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">/* Mensagem &quot;copiada&quot; para trânsito seguro */</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NetMessage_t</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">QueueHandle_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rxQueue</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">QueueHandle_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">txQueue</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">net_queues_init</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">rxQueue</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">xQueueCreate</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">8</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">NetMessage_t</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">txQueue</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">xQueueCreate</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">8</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">NetMessage_t</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p><strong>Observação crítica:</strong> aqui estamos usando cópia de payload. Em sistemas mais exigentes, você pode migrar para <em>zero-copy</em> com <code>pbuf</code> (RAW API) ou pool de buffers. Mas começar com cópia é geralmente a forma mais estável de construir algo robusto e debugar rápido.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>7.2 Task Gatekeeper: recebe do socket e entrega na <code>rxQueue</code></strong></h4>



<p>A task de rede agora vira uma “central de I/O”: aceita conexões, recebe dados e empacota mensagens para a fila.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

#include "lwip/sockets.h"
#include "lwip/inet.h"
#include &lt;string.h>

#define SERVER_PORT 5000

static void socket_gatekeeper_task(void *arg)
{
    (void)arg;

    int listen_fd;
    struct sockaddr_in addr;

    listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (listen_fd &lt; 0) vTaskDelete(NULL);

    memset(&amp;addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(listen_fd, (struct sockaddr *)&amp;addr, sizeof(addr)) &lt; 0) {
        closesocket(listen_fd);
        vTaskDelete(NULL);
    }

    if (listen(listen_fd, 4) &lt; 0) {
        closesocket(listen_fd);
        vTaskDelete(NULL);
    }

    for (;;)
    {
        int client_fd = accept(listen_fd, NULL, NULL);
        if (client_fd &lt; 0) {
            vTaskDelay(pdMS_TO_TICKS(50));
            continue;
        }

        /* Exemplo simples: atende 1 cliente por vez.
           Em produção você pode criar uma task por cliente OU usar select(). */
        for (;;)
        {
            NetMessage_t msg;
            int n = recv(client_fd, msg.payload, MAX_PAYLOAD, 0);
            if (n &lt;= 0) break;

            msg.client_fd = client_fd;
            msg.len = (uint16_t)n;

            /* Entrega para aplicação: se a fila estiver cheia, você precisa decidir política:
               - descartar
               - bloquear por tempo limitado
               - desconectar cliente
            */
            if (xQueueSend(rxQueue, &amp;msg, pdMS_TO_TICKS(20)) != pdPASS) {
                /* Política simples: descarta e segue */
            }

            /* Antes de receber mais, veja se há resposta pronta para este cliente */
            NetMessage_t out;
            if (xQueueReceive(txQueue, &amp;out, 0) == pdPASS) {
                if (out.client_fd == client_fd &amp;&amp; out.len > 0) {
                    send(client_fd, out.payload, out.len, 0);
                } else {
                    /* Se veio resposta de outro cliente, em produção você
                       rotearia corretamente (mapa de conexões, etc.) */
                }
            }
        }

        closesocket(client_fd);
    }
}

void start_socket_gatekeeper(void)
{
    xTaskCreate(socket_gatekeeper_task,
                "NetGate",
                2048,
                NULL,
                tskIDLE_PRIORITY + 3, /* Um pouco acima da aplicação */
                NULL);
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">FreeRTOS.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">task.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">queue.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">lwip/sockets.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">lwip/inet.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SERVER_PORT</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">5000</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">socket_gatekeeper_task</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">arg</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    (</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span><span style="color: #D8DEE9">arg</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">listen_fd</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">struct</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sockaddr_in</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">addr</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">listen_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">socket</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">AF_INET</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SOCK_STREAM</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">IPPROTO_TCP</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #88C0D0">vTaskDelete</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">memset</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">addr</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">addr</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">addr</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">sin_family</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">AF_INET</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">addr</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">sin_port</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">htons</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">SERVER_PORT</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">addr</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">sin_addr</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">s_addr</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">htonl</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">INADDR_ANY</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">bind</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">struct</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sockaddr</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">addr</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">addr</span><span style="color: #D8DEE9FF">)) </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">closesocket</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">vTaskDelete</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">listen</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">4</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">closesocket</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">vTaskDelete</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">;;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">accept</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NULL</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">vTaskDelay</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">pdMS_TO_TICKS</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">50</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">continue;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #616E88">/* Exemplo simples: atende 1 cliente por vez.</span></span>
<span class="line"><span style="color: #616E88">           Em produção você pode criar uma task por cliente OU usar select(). */</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">;;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">NetMessage_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">msg</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">n</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">recv</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">client_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">msg</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">payload</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">MAX_PAYLOAD</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">n</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">break;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">msg</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">client_fd</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">msg</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">len</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF">)</span><span style="color: #D8DEE9">n</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #616E88">/* Entrega para aplicação: se a fila estiver cheia, você precisa decidir política:</span></span>
<span class="line"><span style="color: #616E88">               - descartar</span></span>
<span class="line"><span style="color: #616E88">               - bloquear por tempo limitado</span></span>
<span class="line"><span style="color: #616E88">               - desconectar cliente</span></span>
<span class="line"><span style="color: #616E88">            */</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">xQueueSend</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">rxQueue</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">msg</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">pdMS_TO_TICKS</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">20</span><span style="color: #D8DEE9FF">)) </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">pdPASS</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #616E88">/* Política simples: descarta e segue */</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #616E88">/* Antes de receber mais, veja se há resposta pronta para este cliente */</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">NetMessage_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">out</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">xQueueReceive</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">txQueue</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">pdPASS</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">len</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">send</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">client_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">payload</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">len</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #616E88">/* Se veio resposta de outro cliente, em produção você</span></span>
<span class="line"><span style="color: #616E88">                       rotearia corretamente (mapa de conexões, etc.) */</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">closesocket</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">start_socket_gatekeeper</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xTaskCreate</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">socket_gatekeeper_task</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">NetGate</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #B48EAD">2048</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">NULL</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">tskIDLE_PRIORITY</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">/* Um pouco acima da aplicação */</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p><strong>Por que esse desenho é bom?</strong> Porque o Gatekeeper mantém I/O simples e previsível: ele não interpreta protocolo nem acessa hardware. Ele apenas “empurra bytes”.</p>



<p><strong>Crítica (importante):</strong> este exemplo atende um cliente por vez. Para múltiplos clientes, as duas opções típicas são:</p>



<ol class="wp-block-list">
<li><strong>uma task por conexão</strong> (mais simples, mas consome RAM/stack)</li>



<li><strong><code>select()</code></strong> com um loop único multiplexando sockets (mais eficiente, mais complexo)</li>
</ol>



<p>Na próxima seção eu te mostro os dois modelos, mas antes precisamos fechar o ciclo com o consumidor.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>7.3 Task consumidora: processa mensagem e devolve resposta</strong></h4>



<p>Agora a lógica de aplicação recebe mensagens da <code>rxQueue</code>, interpreta e devolve uma resposta pela <code>txQueue</code>.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include &lt;string.h>

static void app_worker_task(void *arg)
{
    (void)arg;

    for (;;)
    {
        NetMessage_t in;
        if (xQueueReceive(rxQueue, &amp;in, portMAX_DELAY) == pdPASS)
        {
            NetMessage_t out;
            memset(&amp;out, 0, sizeof(out));

            out.client_fd = in.client_fd;

            /* Exemplo de "protocolo": comandos ASCII */
            if (in.len >= 4 &amp;&amp; memcmp(in.payload, "ping", 4) == 0) {
                const char *resp = "pong\n";
                out.len = (uint16_t)strlen(resp);
                memcpy(out.payload, resp, out.len);
            } else {
                const char *resp = "unknown\n";
                out.len = (uint16_t)strlen(resp);
                memcpy(out.payload, resp, out.len);
            }

            /* Envia resposta para o Gatekeeper transmitir */
            (void)xQueueSend(txQueue, &amp;out, pdMS_TO_TICKS(50));
        }
    }
}

void start_app_worker(void)
{
    xTaskCreate(app_worker_task,
                "AppWorker",
                2048,
                NULL,
                tskIDLE_PRIORITY + 2,
                NULL);
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">FreeRTOS.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">task.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">queue.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">app_worker_task</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">arg</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    (</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span><span style="color: #D8DEE9">arg</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">;;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">NetMessage_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">xQueueReceive</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">rxQueue</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;in</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">portMAX_DELAY</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">pdPASS</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">NetMessage_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">out</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">memset</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">out</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">in</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">client_fd</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #616E88">/* Exemplo de &quot;protocolo&quot;: comandos ASCII */</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">in</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">len</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&gt;=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">4</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">memcmp</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">in</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">payload</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ping</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">4</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #D8DEE9FF"> *resp </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">pong</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">len</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF">)</span><span style="color: #88C0D0">strlen</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">resp</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">memcpy</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">payload</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">resp</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">len</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #D8DEE9FF"> *resp </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">unknown</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">len</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF">)</span><span style="color: #88C0D0">strlen</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">resp</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">memcpy</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">payload</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">resp</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">len</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #616E88">/* Envia resposta para o Gatekeeper transmitir */</span></span>
<span class="line"><span style="color: #D8DEE9FF">            (</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span><span style="color: #88C0D0">xQueueSend</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">txQueue</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">out</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">pdMS_TO_TICKS</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">50</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">start_app_worker</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xTaskCreate</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">app_worker_task</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">AppWorker</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #B48EAD">2048</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">NULL</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">tskIDLE_PRIORITY</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Aqui você ganhou algo decisivo: se o parsing for pesado, você pode criar <strong>vários workers consumidores</strong> (ou uma pool) sem mexer na task de rede. E se um worker travar, você pode reiniciá-lo sem derrubar a rede (principalmente se tiver Task Monitor/Watchdog como você já vem tratando na série).</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>7.4 Ajustes essenciais para “produção”</strong></h4>



<p>O exemplo acima é a forma didática mais direta, mas em firmware real você geralmente aplica melhorias:</p>



<ul class="wp-block-list">
<li><strong>Roteamento de respostas por conexão:</strong> em vez de uma <code>txQueue</code> única, você cria uma fila por cliente (ou um mapa de conexões).</li>



<li><strong>Política de backpressure:</strong> se <code>rxQueue</code> enche, você reduz leitura do socket, aplica <code>recv</code> com timeout, ou desconecta cliente.</li>



<li><strong>Framing:</strong> TCP é stream, não pacote. Seu “comando” pode vir partido em múltiplos <code>recv()</code>. Então você implementa <em>framing</em> (por delimitador <code>\n</code>, por tamanho fixo, ou por header com length).</li>



<li><strong>Stack e heap:</strong> payload copiado custa RAM; quando necessário, migra para pool de buffers.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>Na próxima seção, vamos atacar exatamente o que separa “demo” de “produto”:<br><strong>TCP é stream — então precisamos de framing e parser robusto</strong>, além de <strong>timeouts</strong> e <strong>manuseio de múltiplos clientes</strong>.</p>



<h3 class="wp-block-heading"><strong> TCP é stream: framing robusto + múltiplos clientes (task por conexão vs <code>select()</code>)</strong></h3>



<p>Se tem um erro conceitual que derruba sistemas FreeRTOS + lwIP “do nada”, é tratar <code>recv()</code> como se cada chamada retornasse uma mensagem completa. <strong>TCP não entrega mensagens — entrega um fluxo (stream)</strong>. Isso significa que um comando <code>ping\n</code> pode chegar como <code>p</code>, depois <code>ing\n</code>, ou pode chegar junto com outro comando colado, ou pode vir com bytes extras. Se você não implementar <strong>framing</strong>, sua aplicação vai funcionar em bancada e falhar sob carga, ruído de rede, latência variável ou retransmissões.</p>



<p>A solução arquitetural é definir um protocolo com framing claro. Os dois modelos mais comuns em firmware são: (1) <strong>delimitador</strong> (por exemplo <code>\n</code>) e (2) <strong>length-prefix</strong> (header com tamanho). Vou te mostrar os dois, e em seguida mostro como isso muda quando você tem múltiplos clientes: ou você cria <strong>uma task por conexão</strong> (simples, mais RAM) ou usa <strong><code>select()</code></strong> (eficiente, mais complexo).</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>8.1 Framing por delimitador (<code>\n</code>)</strong></h4>



<p>Esse é o mais simples: você acumula bytes num buffer até achar <code>\n</code>. Cada linha vira um comando. É ótimo para console TCP, debug remoto, protocolos ASCII e comandos humanos. O cuidado é evitar overflow: se o cliente mandar uma linha enorme sem <code>\n</code>, você precisa cortar, descartar ou desconectar.</p>



<p>Exemplo de acumulador por conexão:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#include &lt;string.h>
#include &lt;stdint.h>

#define LINE_BUF_SZ 256

typedef struct
{
    uint8_t buf&#91;LINE_BUF_SZ&#93;;
    uint16_t used;
} LineFramer_t;

/**
 * @brief Alimenta o framer com bytes recebidos e extrai 0..N linhas completas.
 *        Retorna 1 quando extraiu uma linha, 0 quando ainda não tem linha.
 */
static int framer_try_get_line(LineFramer_t *f, const uint8_t *data, uint16_t len,
                               uint8_t *out_line, uint16_t *out_len)
{
    /* Copia com proteção */
    uint16_t space = (LINE_BUF_SZ - 1) - f->used;
    if (len > space) {
        /* Política simples: reset (em produção: desconectar cliente) */
        f->used = 0;
        return 0;
    }

    memcpy(&amp;f->buf&#91;f->used&#93;, data, len);
    f->used += len;
    f->buf&#91;f->used&#93; = 0;

    /* Procura delimitador */
    uint8_t *nl = (uint8_t*)memchr(f->buf, '\n', f->used);
    if (!nl) return 0;

    uint16_t line_len = (uint16_t)(nl - f->buf) + 1; /* inclui '\n' */
    memcpy(out_line, f->buf, line_len);
    *out_len = line_len;

    /* Remove a linha do buffer (shift) */
    uint16_t remaining = f->used - line_len;
    memmove(f->buf, &amp;f->buf&#91;line_len&#93;, remaining);
    f->used = remaining;

    return 1;
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">stdint</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">LINE_BUF_SZ</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">256</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">typedef</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">struct</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">buf</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">LINE_BUF_SZ</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">used</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">LineFramer_t</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #8FBCBB">brief</span><span style="color: #616E88"> Alimenta o framer com bytes recebidos e extrai 0..N linhas completas.</span></span>
<span class="line"><span style="color: #616E88"> *        Retorna 1 quando extraiu uma linha, 0 quando ainda não tem linha.</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">framer_try_get_line</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">LineFramer_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">f</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">len</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                               </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">out_line</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">out_len</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* Copia com proteção */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">space</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">LINE_BUF_SZ</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">used</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">len</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">space</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #616E88">/* Política simples: reset (em produção: desconectar cliente) */</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">used</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">memcpy</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">buf</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">used</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">len</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">used</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">len</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">buf</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">used</span><span style="color: #D8DEE9FF">&#93; </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* Procura delimitador */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">nl</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">uint8_t</span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF">)</span><span style="color: #88C0D0">memchr</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">buf</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&#39;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">used</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9">nl</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">line_len</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF">)(</span><span style="color: #D8DEE9">nl</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">buf</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">/* inclui &#39;\n&#39; */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">memcpy</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">out_line</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">buf</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">line_len</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">out_len</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">line_len</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* Remove a linha do buffer (shift) */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">remaining</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">used</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">line_len</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">memmove</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">buf</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">buf</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">line_len</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">remaining</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">used</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">remaining</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Uso típico no loop do socket: você chama <code>recv()</code>, joga os bytes no framer e enquanto ele conseguir extrair linhas você envia para a fila (<code>rxQueue</code>).</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>8.2 Framing por header com tamanho (length-prefix)</strong></h4>



<p>Esse é o padrão de protocolos binários robustos. Você define, por exemplo:</p>



<ul class="wp-block-list">
<li>2 bytes: <code>len</code> (big-endian)</li>



<li><code>len</code> bytes: payload</li>
</ul>



<p>Vantagem: não depende de delimitador e funciona bem com binário. Desvantagem: você precisa validar tamanho, lidar com endianess e limites.</p>



<p>Estrutura de parsing por estados:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#include &lt;stdint.h>
#include &lt;string.h>

#define MAX_FRAME 512

typedef enum { ST_LEN1, ST_LEN2, ST_PAYLOAD } ParseState_t;

typedef struct
{
    ParseState_t st;
    uint16_t expected;
    uint16_t got;
    uint8_t payload&#91;MAX_FRAME&#93;;
} LenFramer_t;

static void lenframer_init(LenFramer_t *f)
{
    f->st = ST_LEN1;
    f->expected = 0;
    f->got = 0;
}

/**
 * @brief Consome bytes e extrai frames completos. Retorna 1 quando extrai um frame.
 */
static int lenframer_feed(LenFramer_t *f, const uint8_t *data, uint16_t len,
                          uint8_t *out_payload, uint16_t *out_len)
{
    for (uint16_t i = 0; i &lt; len; i++)
    {
        uint8_t b = data&#91;i&#93;;

        switch (f->st)
        {
            case ST_LEN1:
                f->expected = ((uint16_t)b) &lt;&lt; 8;
                f->st = ST_LEN2;
                break;

            case ST_LEN2:
                f->expected |= b;
                if (f->expected == 0 || f->expected > MAX_FRAME) {
                    /* Frame inválido: reset (em produção: desconectar) */
                    lenframer_init(f);
                    break;
                }
                f->got = 0;
                f->st = ST_PAYLOAD;
                break;

            case ST_PAYLOAD:
                f->payload&#91;f->got++&#93; = b;
                if (f->got >= f->expected) {
                    memcpy(out_payload, f->payload, f->expected);
                    *out_len = f->expected;
                    lenframer_init(f);
                    return 1; /* extraiu 1 frame */
                }
                break;
        }
    }
    return 0;
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">stdint</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">string</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">MAX_FRAME</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">512</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">typedef</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">enum</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ST_LEN1</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ST_LEN2</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ST_PAYLOAD</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ParseState_t</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">typedef</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">struct</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">ParseState_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">st</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">expected</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">got</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">payload</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">MAX_FRAME</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">LenFramer_t</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">lenframer_init</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">LenFramer_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">f</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">st</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ST_LEN1</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">expected</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">got</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #8FBCBB">brief</span><span style="color: #616E88"> Consome bytes e extrai frames completos. Retorna 1 quando extrai um frame.</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">lenframer_feed</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">LenFramer_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">f</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">len</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                          </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">out_payload</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">out_len</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">len</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">b</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">switch</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">st</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ST_LEN1</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">expected</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> ((</span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF">)</span><span style="color: #D8DEE9">b</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">&lt;&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">8</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">st</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ST_LEN2</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">break;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ST_LEN2</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">expected</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">|=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">b</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">expected</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">||</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">expected</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">MAX_FRAME</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #616E88">/* Frame inválido: reset (em produção: desconectar) */</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">lenframer_init</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">f</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">break;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">got</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">st</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ST_PAYLOAD</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">break;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ST_PAYLOAD</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">payload</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">got</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">&#93; </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">b</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">got</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&gt;=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">expected</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">memcpy</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">out_payload</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">payload</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">expected</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">out_len</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">f</span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9">expected</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">lenframer_init</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">f</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">/* extraiu 1 frame */</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">break;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Esse modelo é extremamente estável em ambiente real porque mantém o protocolo bem definido.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>8.3 Múltiplos clientes: duas arquiteturas</strong></h4>



<p>Agora vem o ponto de engenharia de firmware: <strong>como atender N conexões</strong> sem destruir RAM e sem perder previsibilidade.</p>



<p><strong>Opção A — Uma task por conexão (modelo simples)</strong></p>



<ul class="wp-block-list">
<li>Ao aceitar um cliente, você cria uma task dedicada para ele.</li>



<li>Cada task tem seu framer + buffer + loop <code>recv()</code>.</li>



<li>Fácil de entender e depurar.</li>



<li>Custo: cada task consome stack e TCB; em MCU pequeno isso vira limite rápido.</li>
</ul>



<p>Padrão típico:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>static void client_task(void *arg)
{
    int client_fd = (int)(intptr_t)arg;
    LineFramer_t fr;
    fr.used = 0;

    for (;;)
    {
        uint8_t tmp&#91;128&#93;;
        int n = recv(client_fd, tmp, sizeof(tmp), 0);
        if (n &lt;= 0) break;

        uint8_t line&#91;LINE_BUF_SZ&#93;;
        uint16_t line_len;

        while (framer_try_get_line(&amp;fr, tmp, (uint16_t)n, line, &amp;line_len)) {
            /* aqui você empacota e manda para rxQueue */
        }
    }

    closesocket(client_fd);
    vTaskDelete(NULL);
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">client_task</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">arg</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF">)(</span><span style="color: #D8DEE9">intptr_t</span><span style="color: #D8DEE9FF">)</span><span style="color: #D8DEE9">arg</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">LineFramer_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fr</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">fr</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">used</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">;;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tmp</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #B48EAD">128</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">n</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">recv</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">client_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tmp</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tmp</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">n</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">break;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">line</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">LINE_BUF_SZ</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">line_len</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">while</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">framer_try_get_line</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">fr</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tmp</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF">)</span><span style="color: #D8DEE9">n</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">line</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">line_len</span><span style="color: #D8DEE9FF">)) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #616E88">/* aqui você empacota e manda para rxQueue */</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">closesocket</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">vTaskDelete</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p><strong>Opção B — <code>select()</code> (modelo eficiente)</strong></p>



<ul class="wp-block-list">
<li>Uma única task monitora vários sockets ao mesmo tempo.</li>



<li>Melhor uso de RAM (uma task só), bom para dezenas de conexões.</li>



<li>Custo: mais complexo; exige tabela de conexões e estados por cliente.</li>
</ul>



<p>Se seu firmware precisa escalar, <code>select()</code> costuma ser o caminho. Se precisa ser simples e você terá poucos clientes, task por conexão é perfeito.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>8.4 Um detalhe que muita gente ignora: prioridade e latência</strong></h4>



<p>Não adianta ter framing perfeito se a task que atende sockets fica “no fim da fila” e perde janela TCP. Em geral, uma prática saudável é:</p>



<ul class="wp-block-list">
<li>TCP/IP thread: prioridade média-alta (depende do port e do vendor)</li>



<li>Gatekeeper de sockets: prioridade logo abaixo da TCP/IP thread</li>



<li>Workers de aplicação: prioridade média</li>



<li>Logs/telemetria: prioridade baixa</li>
</ul>



<p>Isso evita um cenário em que o processamento de aplicação “engole” a CPU e a rede degrada.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>Na próxima seção, vamos fechar a arquitetura com os componentes que tornam isso resiliente em produto:</p>



<ul class="wp-block-list">
<li><strong>timeouts completos</strong> (recv/send/connect/accept)</li>



<li><strong>keepalive TCP</strong></li>



<li><strong>watchdog e task monitor aplicados ao domínio de rede</strong></li>



<li><strong>política de backpressure</strong> quando filas enchem</li>



<li>e um exemplo “quase HTTP” com parser mínimo</li>
</ul>



<h3 class="wp-block-heading"><strong>Robustez em rede: timeouts, keepalive, backpressure e integração com Task Monitor/Watchdog</strong></h3>



<p>Até aqui você já tem uma arquitetura correta. Agora entramos no que diferencia <strong>firmware de laboratório</strong> de <strong>firmware de campo</strong>: resiliência. Em sistemas FreeRTOS + lwIP, a maior parte das falhas em produção não vem de bugs óbvios, mas de <strong>bloqueios silenciosos</strong>, <strong>clientes mal-comportados</strong>, <strong>perda intermitente de link</strong>, <strong>filas saturadas</strong> e <strong>tarefas que parecem vivas, mas não fazem progresso</strong>. Esta seção trata exatamente desses pontos — com decisões práticas de engenharia.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>9.1 Timeouts em sockets: nunca confie em bloqueios infinitos</strong></h4>



<p>Chamadas como <code>accept()</code>, <code>recv()</code> e <code>send()</code> <strong>bloqueiam por padrão</strong>. Em firmware, isso é perigoso: se um cliente trava no meio de uma transmissão, sua task pode ficar presa indefinidamente, impedindo shutdown limpo, watchdog cooperativo e diagnóstico.</p>



<p>A regra é simples: <strong>todo socket deve ter timeout</strong>.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>static void socket_set_timeouts(int fd, int sec)
{
    struct timeval tv;
    tv.tv_sec = sec;
    tv.tv_usec = 0;

    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &amp;tv, sizeof(tv));
    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &amp;tv, sizeof(tv));
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">socket_set_timeouts</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sec</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">struct</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">timeval</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">tv</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">tv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tv_sec</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sec</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">tv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">tv_usec</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setsockopt</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SOL_SOCKET</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SO_RCVTIMEO</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">tv</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tv</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setsockopt</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SOL_SOCKET</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SO_SNDTIMEO</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">tv</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">tv</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Use isso logo após <code>accept()</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>int client_fd = accept(listen_fd, NULL, NULL);
if (client_fd >= 0) {
    socket_set_timeouts(client_fd, 5); /* 5s é um bom ponto inicial */
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">accept</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">listen_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NULL</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">client_fd</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&gt;=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">socket_set_timeouts</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">client_fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">5</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">/* 5s é um bom ponto inicial */</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Com isso:</p>



<ul class="wp-block-list">
<li><code>recv()</code> retorna <code>&lt; 0</code> após timeout</li>



<li>você pode <strong>encerrar a conexão</strong>, <strong>reavaliar estado</strong> ou <strong>alimentar watchdog</strong></li>



<li>evita tarefas “zumbis”</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>9.2 TCP Keepalive: detectando clientes mortos</strong></h4>



<p>Em redes reais, cliente pode cair sem fechar socket (queda de Wi-Fi, cabo puxado, crash). O TCP keepalive permite detectar isso.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>int enable = 1;
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &amp;enable, sizeof(enable));

int idle = 10;    /* segundos ociosos antes do probe */
int interval = 5; /* intervalo entre probes */
int count = 3;    /* probes antes de declarar morto */

setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &amp;idle, sizeof(idle));
setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &amp;interval, sizeof(interval));
setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &amp;count, sizeof(count));
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">enable</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #88C0D0">setsockopt</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SOL_SOCKET</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SO_KEEPALIVE</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">enable</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">enable</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">idle</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">10</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* segundos ociosos antes do probe */</span></span>
<span class="line"><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">interval</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">5</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">/* intervalo entre probes */</span></span>
<span class="line"><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">count</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">/* probes antes de declarar morto */</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">setsockopt</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">IPPROTO_TCP</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">TCP_KEEPIDLE</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">idle</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">idle</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #88C0D0">setsockopt</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">IPPROTO_TCP</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">TCP_KEEPINTVL</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">interval</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">interval</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #88C0D0">setsockopt</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">IPPROTO_TCP</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">TCP_KEEPCNT</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">count</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">sizeof</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">count</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<p>Nem todos os ports do lwIP suportam todas essas opções, mas quando disponíveis, <strong>reduzem drasticamente conexões zumbis</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>9.3 Backpressure: o que fazer quando as filas enchem</strong></h4>



<p>Esse ponto é crítico e raramente tratado em exemplos. Se sua <code>rxQueue</code> enche, <strong>o sistema está dizendo que não consegue processar dados na mesma taxa em que recebe</strong>. Ignorar isso leva a latência crescente e colapso.</p>



<p>Existem quatro políticas clássicas:</p>



<ol class="wp-block-list">
<li><strong>Descartar mensagens novas</strong><br>Simples, mas pode quebrar protocolo.</li>



<li><strong>Bloquear o Gatekeeper por tempo limitado</strong><code>if (xQueueSend(rxQueue, &amp;msg, pdMS_TO_TICKS(50)) != pdPASS) { /* timeout: decide política */ }</code></li>



<li><strong>Parar de ler do socket temporariamente</strong><br>Com timeout em <code>recv()</code>, você simplesmente não chama <code>recv</code> por um ciclo.</li>



<li><strong>Desconectar cliente agressivo</strong> (muito comum em produto)<br>Se um cliente gera backpressure constante, ele é o problema.</li>
</ol>



<p>Em firmware sério, a política 4 costuma ser a mais segura.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>9.4 Integração com Task Monitor: detectar “tarefas vivas porém travadas”</strong></h4>



<p>Uma task bloqueada em <code>recv()</code> <strong>não parece travada</strong> para o scheduler — ela está em estado <em>Blocked</em>. Por isso, Task Monitor precisa ir além de “task rodando ou não”: precisa verificar <strong>progresso lógico</strong>.</p>



<p>Modelo simples: <em>heartbeat por domínio</em>.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>volatile uint32_t net_heartbeat = 0;

void socket_gatekeeper_task(void *arg)
{
    for (;;)
    {
        net_heartbeat++;
        /* accept / recv / processamento */
    }
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">volatile</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint32_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">net_heartbeat</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">socket_gatekeeper_task</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">arg</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">;;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">net_heartbeat</span><span style="color: #81A1C1">++;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #616E88">/* accept / recv / processamento */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>O Task Monitor verifica:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>static uint32_t last_net_hb = 0;

void task_monitor(void)
{
    if (net_heartbeat == last_net_hb) {
        /* rede não progrediu */
        /* ação: reiniciar task, reiniciar interface, logar */
    }
    last_net_hb = net_heartbeat;
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint32_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">last_net_hb</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">task_monitor</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">net_heartbeat</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">last_net_hb</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #616E88">/* rede não progrediu */</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #616E88">/* ação: reiniciar task, reiniciar interface, logar */</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">last_net_hb</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">net_heartbeat</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Isso detecta:</p>



<ul class="wp-block-list">
<li>deadlocks</li>



<li>starvation</li>



<li>loops bloqueados por erro lógico</li>
</ul>



<p>Muito mais eficaz que apenas checar se a task existe.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>9.5 Watchdog cooperativo aplicado à rede</strong></h4>



<p>Nunca alimente o watchdog <strong>dentro da task de sockets diretamente</strong>. Isso mascara falhas. O correto é:</p>



<ul class="wp-block-list">
<li>task de rede atualiza estado/heartbeat</li>



<li><strong>task de supervisão</strong> decide se o sistema está saudável</li>



<li>só ela alimenta o watchdog</li>
</ul>



<p>Exemplo conceitual:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>void supervisor_task(void *arg)
{
    for (;;)
    {
        if (net_ok &amp;&amp; app_ok &amp;&amp; sensors_ok) {
            feed_watchdog();
        }
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">supervisor_task</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">arg</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">;;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">net_ok</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">app_ok</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sensors_ok</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">feed_watchdog</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">vTaskDelay</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">pdMS_TO_TICKS</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">500</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Assim, falhas de rede <strong>resetam o sistema</strong>, em vez de ficarem ocultas.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading"><strong>9.6 Mini-HTTP: aplicação prática dos conceitos</strong></h4>



<p>Para fechar, um exemplo conceitual de servidor estilo HTTP minimalista:</p>



<ul class="wp-block-list">
<li>framing por <code>\r\n\r\n</code></li>



<li>timeout curto</li>



<li>parsing mínimo</li>



<li>resposta fixa</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>if (strstr(request, "GET /status")) {
    const char *resp =
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/plain\r\n"
        "Connection: close\r\n"
        "\r\n"
        "OK\n";
    send(fd, resp, strlen(resp), 0);
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">strstr</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">request</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">GET /status</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">char</span><span style="color: #D8DEE9FF"> *resp </span><span style="color: #81A1C1">=</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">HTTP/1.1 200 OK</span><span style="color: #EBCB8B">\r\n</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Content-Type: text/plain</span><span style="color: #EBCB8B">\r\n</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Connection: close</span><span style="color: #EBCB8B">\r\n</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #EBCB8B">\r\n</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">OK</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">send</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">fd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">resp</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">strlen</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">resp</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Em firmware, <strong>HTTP raramente precisa ser completo</strong>. Muitas vezes é só um protocolo humano para debug, status e configuração inicial. Quanto mais simples, mais confiável.</p><p>The post <a href="https://mcu.tec.br/rtos/freertos-com-lwip-arquitetura-boas-praticas-e-exemplos-reais-de-sistemas-embarcados-em-rede/">FreeRTOS com lwIP: Arquitetura, Boas Práticas e Exemplos Reais de Sistemas Embarcados em Rede</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://mcu.tec.br/rtos/freertos-com-lwip-arquitetura-boas-praticas-e-exemplos-reais-de-sistemas-embarcados-em-rede/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1082</post-id>	</item>
		<item>
		<title>OTA, FOTA e SOTA em IoT: Diferenças, Riscos e Boas Práticas em Projetos Reais</title>
		<link>https://mcu.tec.br/protoclolos/ota-fota-e-sota-em-iot-diferencas-riscos-e-boas-praticas-em-projetos-reais/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=ota-fota-e-sota-em-iot-diferencas-riscos-e-boas-praticas-em-projetos-reais</link>
					<comments>https://mcu.tec.br/protoclolos/ota-fota-e-sota-em-iot-diferencas-riscos-e-boas-praticas-em-projetos-reais/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Delfino]]></dc:creator>
		<pubDate>Mon, 23 Feb 2026 23:17:15 +0000</pubDate>
				<category><![CDATA[protoclolos]]></category>
		<guid isPermaLink="false">https://mcu.tec.br/?p=1314</guid>

					<description><![CDATA[<p>OTA, FOTA e SOTA são pilares fundamentais para a manutenção e evolução de dispositivos IoT modernos. Este artigo apresenta uma análise técnica e aprofundada sobre atualizações Over-The-Air, explicando as diferenças entre atualização de firmware (FOTA) e atualização de software (SOTA), seus impactos arquiteturais, níveis de risco, requisitos de segurança e implicações práticas em projetos IoT reais. O texto aborda como essas tecnologias são utilizadas em sistemas embarcados distribuídos, veículos definidos por software, gateways IoT e ambientes industriais, destacando desafios como conectividade intermitente, segurança, heterogeneidade de hardware e risco de falhas em campo. Também são discutidas boas práticas essenciais, como uso de bootloaders seguros, particionamento de memória, rollback, assinaturas digitais e estratégias de atualização gradual. O conteúdo é indicado para engenheiros de sistemas embarcados, desenvolvedores IoT, arquitetos de software e pesquisadores que buscam compreender como projetar soluções OTA robustas, seguras e escaláveis ao longo do ciclo de vida do produto.</p>
<p>The post <a href="https://mcu.tec.br/protoclolos/ota-fota-e-sota-em-iot-diferencas-riscos-e-boas-praticas-em-projetos-reais/">OTA, FOTA e SOTA em IoT: Diferenças, Riscos e Boas Práticas em Projetos Reais</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></description>
										<content:encoded><![CDATA[<div class="root-eb-toc-j0oas wp-block-essential-blocks-table-of-contents"><div class="eb-parent-wrapper eb-parent-eb-toc-j0oas "><div class="eb-toc-container eb-toc-j0oas  eb-toc-is-not-sticky eb-toc-not-collapsible eb-toc-initially-not-collapsed eb-toc-scrollToTop style-1 list-style-none" data-scroll-top="false" data-scroll-top-icon="fas fa-angle-up" data-collapsible="false" data-sticky-hide-mobile="false" data-sticky="false" data-scroll-target="scroll_to_toc" data-copy-link="false" data-editor-type="" data-hide-desktop="false" data-hide-tab="false" data-hide-mobile="false" data-itemCollapsed="false" data-highlight-scroll="false"><div class="eb-toc-header"><h2 class="eb-toc-title">Table of Contents</h2></div><div class="eb-toc-wrapper " data-headers="[{&quot;level&quot;:2,&quot;content&quot;:&quot;Introdu\u00e7\u00e3o e Contexto: OTA, FOTA e SOTA&quot;,&quot;text&quot;:&quot;Introdu\u00e7\u00e3o e Contexto: OTA, FOTA e SOTA&quot;,&quot;link&quot;:&quot;eb-table-content-0&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;OTA (Over-The-Air): Conceito, Arquitetura e Funcionamento&quot;,&quot;text&quot;:&quot;OTA (Over-The-Air): Conceito, Arquitetura e Funcionamento&quot;,&quot;link&quot;:&quot;ota-over-the-air-conceito-arquitetura-e-funcionamento&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;FOTA (Firmware Over-The-Air): Atualiza\u00e7\u00e3o de Firmware e Implica\u00e7\u00f5es T\u00e9cnicas&quot;,&quot;text&quot;:&quot;FOTA (Firmware Over-The-Air): Atualiza\u00e7\u00e3o de Firmware e Implica\u00e7\u00f5es T\u00e9cnicas&quot;,&quot;link&quot;:&quot;eb-table-content-2&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;SOTA (Software Over-The-Air): Atualiza\u00e7\u00e3o de Software e Evolu\u00e7\u00e3o Funcional&quot;,&quot;text&quot;:&quot;SOTA (Software Over-The-Air): Atualiza\u00e7\u00e3o de Software e Evolu\u00e7\u00e3o Funcional&quot;,&quot;link&quot;:&quot;eb-table-content-3&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Compara\u00e7\u00e3o Pr\u00e1tica entre OTA, FOTA e SOTA&quot;,&quot;text&quot;:&quot;Compara\u00e7\u00e3o Pr\u00e1tica entre OTA, FOTA e SOTA&quot;,&quot;link&quot;:&quot;eb-table-content-4&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Boas Pr\u00e1ticas, Desafios e Riscos em OTA, FOTA e SOTA em Projetos IoT Reais&quot;,&quot;text&quot;:&quot;Boas Pr\u00e1ticas, Desafios e Riscos em OTA, FOTA e SOTA em Projetos IoT Reais&quot;,&quot;link&quot;:&quot;eb-table-content-5&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Conclus\u00e3o: OTA, FOTA e SOTA como Pilar da Sustentabilidade em IoT&quot;,&quot;text&quot;:&quot;Conclus\u00e3o: OTA, FOTA e SOTA como Pilar da Sustentabilidade em IoT&quot;,&quot;link&quot;:&quot;eb-table-content-6&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Refer\u00eancias&quot;,&quot;text&quot;:&quot;Refer\u00eancias&quot;,&quot;link&quot;:&quot;eb-table-content-7&quot;}]" data-visible="[true,true,true,true,true,true]" data-delete-headers="[{&quot;label&quot;:&quot;Introdu\u00e7\u00e3o e Contexto: OTA, FOTA e SOTA&quot;,&quot;value&quot;:&quot;introdu\u00e7\u00e3o-e-contexto-ota-fota-e-sota&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;OTA (Over-The-Air): Conceito, Arquitetura e Funcionamento&quot;,&quot;value&quot;:&quot;ota-over-the-air-conceito-arquitetura-e-funcionamento&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;FOTA (Firmware Over-The-Air): Atualiza\u00e7\u00e3o de Firmware e Implica\u00e7\u00f5es T\u00e9cnicas&quot;,&quot;value&quot;:&quot;fota-firmware-over-the-air-atualiza\u00e7\u00e3o-de-firmware-e-implica\u00e7\u00f5es-t\u00e9cnicas&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;SOTA (Software Over-The-Air): Atualiza\u00e7\u00e3o de Software e Evolu\u00e7\u00e3o Funcional&quot;,&quot;value&quot;:&quot;sota-software-over-the-air-atualiza\u00e7\u00e3o-de-software-e-evolu\u00e7\u00e3o-funcional&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Compara\u00e7\u00e3o Pr\u00e1tica entre OTA, FOTA e SOTA&quot;,&quot;value&quot;:&quot;compara\u00e7\u00e3o-pr\u00e1tica-entre-ota-fota-e-sota&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Boas Pr\u00e1ticas, Desafios e Riscos em OTA, FOTA e SOTA em Projetos IoT Reais&quot;,&quot;value&quot;:&quot;boas-pr\u00e1ticas-desafios-e-riscos-em-ota-fota-e-sota-em-projetos-iot-reais&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Conclus\u00e3o: OTA, FOTA e SOTA como Pilar da Sustentabilidade em IoT&quot;,&quot;value&quot;:&quot;conclus\u00e3o-ota-fota-e-sota-como-pilar-da-sustentabilidade-em-iot&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Refer\u00eancias&quot;,&quot;value&quot;:&quot;refer\u00eancias&quot;,&quot;isDelete&quot;:false}]" data-smooth="true" data-top-offset=""><div class="eb-toc__list-wrap"><ul class='eb-toc__list'><li><a href="#eb-table-content-0">Introdução e Contexto: OTA, FOTA e SOTA</a><li><a href="#ota-over-the-air-conceito-arquitetura-e-funcionamento">OTA (Over-The-Air): Conceito, Arquitetura e Funcionamento</a><li><a href="#eb-table-content-2">FOTA (Firmware Over-The-Air): Atualização de Firmware e Implicações Técnicas</a><li><a href="#eb-table-content-3">SOTA (Software Over-The-Air): Atualização de Software e Evolução Funcional</a><li><a href="#eb-table-content-4">Comparação Prática entre OTA, FOTA e SOTA</a><li><a href="#eb-table-content-5">Boas Práticas, Desafios e Riscos em OTA, FOTA e SOTA em Projetos IoT Reais</a><li><a href="#eb-table-content-6">Conclusão: OTA, FOTA e SOTA como Pilar da Sustentabilidade em IoT</a><li><a href="#eb-table-content-7">Referências</a></ul></div></div></div></div></div>


<hr class="wp-block-separator has-alpha-channel-opacity"/><p>The post <a href="https://mcu.tec.br/protoclolos/ota-fota-e-sota-em-iot-diferencas-riscos-e-boas-praticas-em-projetos-reais/">OTA, FOTA e SOTA em IoT: Diferenças, Riscos e Boas Práticas em Projetos Reais</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://mcu.tec.br/protoclolos/ota-fota-e-sota-em-iot-diferencas-riscos-e-boas-praticas-em-projetos-reais/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1314</post-id>	</item>
		<item>
		<title>Protocolos para Redes de Sensores e IoT: LEACH, PEGASIS, TDMA, 6TiSCH e WirelessHART</title>
		<link>https://mcu.tec.br/protoclolos/protocolos-para-redes-de-sensores-e-iot-leach-pegasis-tdma-6tisch-e-wirelesshart/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=protocolos-para-redes-de-sensores-e-iot-leach-pegasis-tdma-6tisch-e-wirelesshart</link>
					<comments>https://mcu.tec.br/protoclolos/protocolos-para-redes-de-sensores-e-iot-leach-pegasis-tdma-6tisch-e-wirelesshart/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Delfino]]></dc:creator>
		<pubDate>Sat, 07 Feb 2026 18:13:47 +0000</pubDate>
				<category><![CDATA[IoT]]></category>
		<category><![CDATA[protoclolos]]></category>
		<category><![CDATA[6TiSCH]]></category>
		<category><![CDATA[comunicação sem fio industrial]]></category>
		<category><![CDATA[eficiência energética]]></category>
		<category><![CDATA[firmware embarcado]]></category>
		<category><![CDATA[IEEE 802.15.4e]]></category>
		<category><![CDATA[IIoT]]></category>
		<category><![CDATA[iot industrial]]></category>
		<category><![CDATA[LEACH]]></category>
		<category><![CDATA[PEGASIS]]></category>
		<category><![CDATA[protocolos de comunicação]]></category>
		<category><![CDATA[redes de sensores sem fio]]></category>
		<category><![CDATA[redes determinísticas]]></category>
		<category><![CDATA[sistemas embarcados]]></category>
		<category><![CDATA[TDMA]]></category>
		<category><![CDATA[TSCH]]></category>
		<category><![CDATA[WirelessHART]]></category>
		<category><![CDATA[WSN]]></category>
		<guid isPermaLink="false">https://mcu.tec.br/?p=1269</guid>

					<description><![CDATA[<p>Este artigo apresenta uma análise técnica e didática dos principais protocolos utilizados em Redes de Sensores Sem Fio (Wireless Sensor Networks – WSNs) e sua evolução até o IoT industrial moderno. São explorados em profundidade os protocolos LEACH (Low-Energy Adaptive Clustering Hierarchy) e PEGASIS (Power-Efficient Gathering in Sensor Information Systems), amplamente estudados no meio acadêmico por sua eficiência energética e estratégias de organização da rede, bem como o papel fundamental do TDMA (Time Division Multiple Access) na construção de sistemas determinísticos e previsíveis. O texto avança para protocolos industriais consolidados, como 6TiSCH (IPv6 over TSCH IEEE 802.15.4e) e WirelessHART, destacando seus mecanismos de sincronização temporal, salto de frequência, confiabilidade, interoperabilidade e adequação a sistemas embarcados de baixo consumo. Ao longo do artigo, são discutidos os conceitos de clusterização, roteamento em cadeia, agendamento temporal e comunicação determinística, sempre conectando teoria, firmware embarcado e contexto de uso real em aplicações industriais, ambientais e de infraestrutura crítica. O conteúdo é direcionado a engenheiros, estudantes e profissionais que desejam compreender as bases conceituais que sustentam o IoT moderno e o Industrial IoT, indo além de protocolos de aplicação e explorando a camada estrutural das redes distribuídas de sensores.</p>
<p>The post <a href="https://mcu.tec.br/protoclolos/protocolos-para-redes-de-sensores-e-iot-leach-pegasis-tdma-6tisch-e-wirelesshart/">Protocolos para Redes de Sensores e IoT: LEACH, PEGASIS, TDMA, 6TiSCH e WirelessHART</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></description>
										<content:encoded><![CDATA[<div class="root-eb-toc-sssef wp-block-essential-blocks-table-of-contents"><div class="eb-parent-wrapper eb-parent-eb-toc-sssef "><div class="eb-toc-container eb-toc-sssef  eb-toc-is-not-sticky eb-toc-not-collapsible eb-toc-initially-not-collapsed eb-toc-scrollToTop style-1 list-style-none" data-scroll-top="false" data-scroll-top-icon="fas fa-angle-up" data-collapsible="false" data-sticky-hide-mobile="false" data-sticky="false" data-scroll-target="scroll_to_toc" data-copy-link="false" data-editor-type="" data-hide-desktop="false" data-hide-tab="false" data-hide-mobile="false" data-itemCollapsed="false" data-highlight-scroll="false"><div class="eb-toc-header"><h2 class="eb-toc-title">Table of Contents</h2></div><div class="eb-toc-wrapper " data-headers="[{&quot;level&quot;:3,&quot;content&quot;:&quot;Introdu\u00e7\u00e3o&quot;,&quot;text&quot;:&quot;Introdu\u00e7\u00e3o&quot;,&quot;link&quot;:&quot;eb-table-content-0&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;LEACH \u2013 Low-Energy Adaptive Clustering Hierarchy&quot;,&quot;text&quot;:&quot;LEACH \u2013 Low-Energy Adaptive Clustering Hierarchy&quot;,&quot;link&quot;:&quot;leach-low-energy-adaptive-clustering-hierarchy&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;PEGASIS \u2013 Power-Efficient Gathering in Sensor Information Systems&quot;,&quot;text&quot;:&quot;PEGASIS \u2013 Power-Efficient Gathering in Sensor Information Systems&quot;,&quot;link&quot;:&quot;pegasis-power-efficient-gathering-in-sensor-information-systems&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;TDMA \u2013 Time Division Multiple Access&quot;,&quot;text&quot;:&quot;TDMA \u2013 Time Division Multiple Access&quot;,&quot;link&quot;:&quot;tdma-time-division-multiple-access&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;6TiSCH \u2013 IPv6 over the TSCH mode of IEEE 802.15.4e&quot;,&quot;text&quot;:&quot;6TiSCH \u2013 IPv6 over the TSCH mode of IEEE 802.15.4e&quot;,&quot;link&quot;:&quot;6tisch-ipv6-over-the-tsch-mode-of-ieee-802154e&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;WirelessHART \u2013 Highway Addressable Remote Transducer (Wireless)&quot;,&quot;text&quot;:&quot;WirelessHART \u2013 Highway Addressable Remote Transducer (Wireless)&quot;,&quot;link&quot;:&quot;wirelesshart-highway-addressable-remote-transducer-wireless&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Tabela comparativa entre os protocolos analisados&quot;,&quot;text&quot;:&quot;Tabela comparativa entre os protocolos analisados&quot;,&quot;link&quot;:&quot;tabela-comparativa-entre-os-protocolos-analisados&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Conclus\u00e3o&quot;,&quot;text&quot;:&quot;Conclus\u00e3o&quot;,&quot;link&quot;:&quot;eb-table-content-7&quot;}]" data-visible="[true,true,true,true,true,true]" data-delete-headers="[{&quot;label&quot;:&quot;Introdu\u00e7\u00e3o&quot;,&quot;value&quot;:&quot;introdu\u00e7\u00e3o&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;LEACH \u2013 Low-Energy Adaptive Clustering Hierarchy&quot;,&quot;value&quot;:&quot;leach-low-energy-adaptive-clustering-hierarchy&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;PEGASIS \u2013 Power-Efficient Gathering in Sensor Information Systems&quot;,&quot;value&quot;:&quot;pegasis-power-efficient-gathering-in-sensor-information-systems&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;TDMA \u2013 Time Division Multiple Access&quot;,&quot;value&quot;:&quot;tdma-time-division-multiple-access&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;6TiSCH \u2013 IPv6 over the TSCH mode of IEEE 802.15.4e&quot;,&quot;value&quot;:&quot;6tisch-ipv6-over-the-tsch-mode-of-ieee-802154e&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;WirelessHART \u2013 Highway Addressable Remote Transducer (Wireless)&quot;,&quot;value&quot;:&quot;wirelesshart-highway-addressable-remote-transducer-wireless&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Tabela comparativa entre os protocolos analisados&quot;,&quot;value&quot;:&quot;tabela-comparativa-entre-os-protocolos-analisados&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Conclus\u00e3o&quot;,&quot;value&quot;:&quot;conclus\u00e3o&quot;,&quot;isDelete&quot;:false}]" data-smooth="true" data-top-offset=""><div class="eb-toc__list-wrap"><ul class='eb-toc__list'><li><a href="#eb-table-content-0">Introdução</a><li><a href="#leach-low-energy-adaptive-clustering-hierarchy">LEACH – Low-Energy Adaptive Clustering Hierarchy</a><li><a href="#pegasis-power-efficient-gathering-in-sensor-information-systems">PEGASIS – Power-Efficient Gathering in Sensor Information Systems</a><li><a href="#tdma-time-division-multiple-access">TDMA – Time Division Multiple Access</a><li><a href="#6tisch-ipv6-over-the-tsch-mode-of-ieee-802154e">6TiSCH – IPv6 over the TSCH mode of IEEE 802.15.4e</a><li><a href="#wirelesshart-highway-addressable-remote-transducer-wireless">WirelessHART – Highway Addressable Remote Transducer (Wireless)</a><ul class='eb-toc__list'><li><a href="#tabela-comparativa-entre-os-protocolos-analisados">Tabela comparativa entre os protocolos analisados</a><li><a href="#eb-table-content-7">Conclusão</a></li></ul></ul></div></div></div></div></div>


<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Introdução</h3>



<p>Redes de sensores sem fio e sistemas embarcados de baixo consumo formam a espinha dorsal de grande parte das aplicações modernas de Internet das Coisas (IoT), especialmente nos domínios industrial, ambiental e de infraestrutura crítica. Nesses cenários, os desafios não se restringem à aquisição de dados, mas envolvem de forma central <strong>como os nós se organizam, como compartilham o meio de comunicação e como preservam energia ao longo de anos de operação</strong>. É nesse contexto que surgem protocolos voltados à eficiência energética, à previsibilidade temporal e à confiabilidade da comunicação.</p>



<p>Protocolos como <strong>LEACH</strong> e <strong>PEGASIS</strong> emergem inicialmente no meio acadêmico como respostas diretas às limitações físicas dos nós sensores, propondo novas formas de organização da rede para reduzir o custo energético das transmissões. Esses trabalhos estabelecem fundamentos conceituais importantes, como agregação de dados, hierarquização e comunicação cooperativa, que influenciaram profundamente a evolução das Wireless Sensor Networks e, posteriormente, do IoT.</p>



<p>À medida que as aplicações avançam para ambientes industriais e de missão crítica, a necessidade de <strong>determinismo, robustez e interoperabilidade</strong> torna-se dominante. Técnicas como <strong>TDMA</strong> passam a ser adotadas como base estrutural, permitindo controle preciso do tempo, do consumo e da latência. Sobre esse alicerce, surgem protocolos industriais como <strong>6TiSCH</strong> e <strong>WirelessHART</strong>, que consolidam esses princípios em padrões amplamente utilizados no Industrial IoT.</p>



<p>Este artigo apresenta uma análise progressiva desses protocolos, explorando seus conceitos fundamentais, modos de funcionamento e contextos de uso, com foco especial na relação direta entre <strong>arquitetura de rede, firmware embarcado e eficiência energética</strong>. Ao compreender essa trajetória, torna-se possível enxergar o IoT não como um conjunto de tecnologias isoladas, mas como o resultado de décadas de refinamento conceitual no campo das redes de sensores e sistemas embarcados distribuídos.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/><p>The post <a href="https://mcu.tec.br/protoclolos/protocolos-para-redes-de-sensores-e-iot-leach-pegasis-tdma-6tisch-e-wirelesshart/">Protocolos para Redes de Sensores e IoT: LEACH, PEGASIS, TDMA, 6TiSCH e WirelessHART</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://mcu.tec.br/protoclolos/protocolos-para-redes-de-sensores-e-iot-leach-pegasis-tdma-6tisch-e-wirelesshart/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1269</post-id>	</item>
		<item>
		<title>Protocolos auto-sincronizáveis em sistemas embarcados</title>
		<link>https://mcu.tec.br/protoclolos/uart-serial/protocolos-auto-sincronizaveis-em-sistemas-embarcados/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=protocolos-auto-sincronizaveis-em-sistemas-embarcados</link>
					<comments>https://mcu.tec.br/protoclolos/uart-serial/protocolos-auto-sincronizaveis-em-sistemas-embarcados/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Delfino]]></dc:creator>
		<pubDate>Sat, 17 Jan 2026 10:20:00 +0000</pubDate>
				<category><![CDATA[UART (Serial)]]></category>
		<category><![CDATA[CBOR]]></category>
		<category><![CDATA[COBS]]></category>
		<category><![CDATA[comunicação serial robusta]]></category>
		<category><![CDATA[framing serial]]></category>
		<category><![CDATA[protocolos auto-sincronizáveis]]></category>
		<category><![CDATA[protocolos binários]]></category>
		<category><![CDATA[protocolos seriais]]></category>
		<category><![CDATA[sistemas embarcados]]></category>
		<category><![CDATA[SLIP]]></category>
		<category><![CDATA[UART embarcado]]></category>
		<guid isPermaLink="false">https://mcu.tec.br/?p=1108</guid>

					<description><![CDATA[<p>Entenda como funcionam os protocolos auto-sincronizáveis SLIP, COBS e CBOR em sistemas embarcados. Veja exemplos em C, comparações técnicas, boas e más escolhas de engenharia e descubra como projetar comunicações seriais robustas para UART, SPI e IoT.</p>
<p>The post <a href="https://mcu.tec.br/protoclolos/uart-serial/protocolos-auto-sincronizaveis-em-sistemas-embarcados/">Protocolos auto-sincronizáveis em sistemas embarcados</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></description>
										<content:encoded><![CDATA[<div class="root-eb-toc-3810l wp-block-essential-blocks-table-of-contents"><div class="eb-parent-wrapper eb-parent-eb-toc-3810l "><div class="eb-toc-container eb-toc-3810l  eb-toc-is-not-sticky eb-toc-not-collapsible eb-toc-initially-not-collapsed eb-toc-scrollToTop style-1 list-style-none" data-scroll-top="false" data-scroll-top-icon="fas fa-angle-up" data-collapsible="false" data-sticky-hide-mobile="false" data-sticky="false" data-scroll-target="scroll_to_toc" data-copy-link="false" data-editor-type="" data-hide-desktop="false" data-hide-tab="false" data-hide-mobile="false" data-itemCollapsed="false" data-highlight-scroll="false"><div class="eb-toc-header"><h2 class="eb-toc-title">Table of Contents</h2></div><div class="eb-toc-wrapper " data-headers="[{&quot;level&quot;:3,&quot;content&quot;:&quot;Conceito, motiva\u00e7\u00e3o e panorama geral&quot;,&quot;text&quot;:&quot;Conceito, motiva\u00e7\u00e3o e panorama geral&quot;,&quot;link&quot;:&quot;eb-table-content-0&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;O que significa \u201cauto-sincroniza\u00e7\u00e3o\u201d na pr\u00e1tica&quot;,&quot;text&quot;:&quot;O que significa \u201cauto-sincroniza\u00e7\u00e3o\u201d na pr\u00e1tica&quot;,&quot;link&quot;:&quot;eb-table-content-1&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Vis\u00e3o geral dos protocolos que vamos estudar&quot;,&quot;text&quot;:&quot;Vis\u00e3o geral dos protocolos que vamos estudar&quot;,&quot;link&quot;:&quot;eb-table-content-2&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Boas e m\u00e1s decis\u00f5es logo no in\u00edcio&quot;,&quot;text&quot;:&quot;Boas e m\u00e1s decis\u00f5es logo no in\u00edcio&quot;,&quot;link&quot;:&quot;eb-table-content-3&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;SLIP (Serial Line Internet Protocol): simplicidade que cobra seu pre\u00e7o&quot;,&quot;text&quot;:&quot;SLIP (Serial Line Internet Protocol): simplicidade que cobra seu pre\u00e7o&quot;,&quot;link&quot;:&quot;eb-table-content-4&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Como o SLIP funciona (byte a byte)&quot;,&quot;text&quot;:&quot;Como o SLIP funciona (byte a byte)&quot;,&quot;link&quot;:&quot;como-o-slip-funciona-byte-a-byte&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Exemplo conceitual de framing SLIP&quot;,&quot;text&quot;:&quot;Exemplo conceitual de framing SLIP&quot;,&quot;link&quot;:&quot;exemplo-conceitual-de-framing-slip&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Implementa\u00e7\u00e3o did\u00e1tica \u2014 Encoder SLIP em C&quot;,&quot;text&quot;:&quot;Implementa\u00e7\u00e3o did\u00e1tica \u2014 Encoder SLIP em C&quot;,&quot;link&quot;:&quot;eb-table-content-7&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Decoder SLIP (lado cr\u00edtico)&quot;,&quot;text&quot;:&quot;Decoder SLIP (lado cr\u00edtico)&quot;,&quot;link&quot;:&quot;eb-table-content-8&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Onde o SLIP \u00e9 uma boa escolha&quot;,&quot;text&quot;:&quot;Onde o SLIP \u00e9 uma boa escolha&quot;,&quot;link&quot;:&quot;eb-table-content-9&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Onde o SLIP \u00e9 uma m\u00e1 escolha (e por qu\u00ea)&quot;,&quot;text&quot;:&quot;Onde o SLIP \u00e9 uma m\u00e1 escolha (e por qu\u00ea)&quot;,&quot;link&quot;:&quot;eb-table-content-10&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Conclus\u00e3o parcial&quot;,&quot;text&quot;:&quot;Conclus\u00e3o parcial&quot;,&quot;link&quot;:&quot;eb-table-content-11&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;COBS (Consistent Overhead Byte Stuffing): framing determin\u00edstico e recupera\u00e7\u00e3o real&quot;,&quot;text&quot;:&quot;COBS (Consistent Overhead Byte Stuffing): framing determin\u00edstico e recupera\u00e7\u00e3o real&quot;,&quot;link&quot;:&quot;eb-table-content-12&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Ideia central do COBS&quot;,&quot;text&quot;:&quot;Ideia central do COBS&quot;,&quot;link&quot;:&quot;ideia-central-do-cobs&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Exemplo conceitual&quot;,&quot;text&quot;:&quot;Exemplo conceitual&quot;,&quot;link&quot;:&quot;exemplo-conceitual&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Encoder COBS \u2014 implementa\u00e7\u00e3o did\u00e1tica em C&quot;,&quot;text&quot;:&quot;Encoder COBS \u2014 implementa\u00e7\u00e3o did\u00e1tica em C&quot;,&quot;link&quot;:&quot;eb-table-content-15&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Decoder COBS \u2014 recupera\u00e7\u00e3o previs\u00edvel&quot;,&quot;text&quot;:&quot;Decoder COBS \u2014 recupera\u00e7\u00e3o previs\u00edvel&quot;,&quot;link&quot;:&quot;eb-table-content-16&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Por que COBS \u00e9 realmente auto-sincroniz\u00e1vel&quot;,&quot;text&quot;:&quot;Por que COBS \u00e9 realmente auto-sincroniz\u00e1vel&quot;,&quot;link&quot;:&quot;eb-table-content-17&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Boas e m\u00e1s escolhas com COBS&quot;,&quot;text&quot;:&quot;Boas e m\u00e1s escolhas com COBS&quot;,&quot;link&quot;:&quot;eb-table-content-18&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Compara\u00e7\u00e3o direta: SLIP \u00d7 COBS&quot;,&quot;text&quot;:&quot;Compara\u00e7\u00e3o direta: SLIP \u00d7 COBS&quot;,&quot;link&quot;:&quot;eb-table-content-19&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Conclus\u00e3o parcial&quot;,&quot;text&quot;:&quot;Conclus\u00e3o parcial&quot;,&quot;link&quot;:&quot;eb-table-content-20&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;CBOR (Concise Binary Object Representation): sem\u00e2ntica bin\u00e1ria n\u00e3o \u00e9 framing&quot;,&quot;text&quot;:&quot;CBOR (Concise Binary Object Representation): sem\u00e2ntica bin\u00e1ria n\u00e3o \u00e9 framing&quot;,&quot;link&quot;:&quot;eb-table-content-21&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;O que o CBOR realmente define&quot;,&quot;text&quot;:&quot;O que o CBOR realmente define&quot;,&quot;link&quot;:&quot;o-que-o-cbor-realmente-define&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Exemplo conceitual: CBOR vs JSON&quot;,&quot;text&quot;:&quot;Exemplo conceitual: CBOR vs JSON&quot;,&quot;link&quot;:&quot;exemplo-conceitual-cbor-vs-json&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Erro cl\u00e1ssico: \u201cvou ler CBOR direto da UART\u201d&quot;,&quot;text&quot;:&quot;Erro cl\u00e1ssico: \u201cvou ler CBOR direto da UART\u201d&quot;,&quot;link&quot;:&quot;eb-table-content-24&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Uso correto: CBOR como payload, COBS como transporte&quot;,&quot;text&quot;:&quot;Uso correto: CBOR como payload, COBS como transporte&quot;,&quot;link&quot;:&quot;uso-correto-cbor-como-payload-cobs-como-transporte&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Exemplo did\u00e1tico \u2014 gerando CBOR (C)&quot;,&quot;text&quot;:&quot;Exemplo did\u00e1tico \u2014 gerando CBOR (C)&quot;,&quot;link&quot;:&quot;eb-table-content-26&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Encapsulando CBOR com COBS&quot;,&quot;text&quot;:&quot;Encapsulando CBOR com COBS&quot;,&quot;link&quot;:&quot;encapsulando-cbor-com-cobs&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Boas e m\u00e1s escolhas com CBOR&quot;,&quot;text&quot;:&quot;Boas e m\u00e1s escolhas com CBOR&quot;,&quot;link&quot;:&quot;eb-table-content-28&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Compara\u00e7\u00e3o direta: COBS \u00d7 CBOR&quot;,&quot;text&quot;:&quot;Compara\u00e7\u00e3o direta: COBS \u00d7 CBOR&quot;,&quot;link&quot;:&quot;eb-table-content-29&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Conclus\u00e3o parcial&quot;,&quot;text&quot;:&quot;Conclus\u00e3o parcial&quot;,&quot;link&quot;:&quot;eb-table-content-30&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Compara\u00e7\u00e3o geral, decis\u00f5es de projeto e checklist de boas pr\u00e1ticas&quot;,&quot;text&quot;:&quot;Compara\u00e7\u00e3o geral, decis\u00f5es de projeto e checklist de boas pr\u00e1ticas&quot;,&quot;link&quot;:&quot;eb-table-content-31&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Compara\u00e7\u00e3o t\u00e9cnica consolidada&quot;,&quot;text&quot;:&quot;Compara\u00e7\u00e3o t\u00e9cnica consolidada&quot;,&quot;link&quot;:&quot;eb-table-content-32&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Arquiteturas t\u00edpicas (boas escolhas)&quot;,&quot;text&quot;:&quot;Arquiteturas t\u00edpicas (boas escolhas)&quot;,&quot;link&quot;:&quot;eb-table-content-33&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;M\u00e1s decis\u00f5es cl\u00e1ssicas (e por que falham)&quot;,&quot;text&quot;:&quot;M\u00e1s decis\u00f5es cl\u00e1ssicas (e por que falham)&quot;,&quot;link&quot;:&quot;eb-table-content-34&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Checklist de boas pr\u00e1ticas (engenharia de firmware)&quot;,&quot;text&quot;:&quot;Checklist de boas pr\u00e1ticas (engenharia de firmware)&quot;,&quot;link&quot;:&quot;eb-table-content-35&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Regra pr\u00e1tica para n\u00e3o errar&quot;,&quot;text&quot;:&quot;Regra pr\u00e1tica para n\u00e3o errar&quot;,&quot;link&quot;:&quot;eb-table-content-36&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Conclus\u00e3o do artigo&quot;,&quot;text&quot;:&quot;Conclus\u00e3o do artigo&quot;,&quot;link&quot;:&quot;eb-table-content-37&quot;}]" data-visible="[true,true,true,true,true,true]" data-delete-headers="[{&quot;label&quot;:&quot;Conceito, motiva\u00e7\u00e3o e panorama geral&quot;,&quot;value&quot;:&quot;conceito-motiva\u00e7\u00e3o-e-panorama-geral&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;O que significa \u201cauto-sincroniza\u00e7\u00e3o\u201d na pr\u00e1tica&quot;,&quot;value&quot;:&quot;o-que-significa-auto-sincroniza\u00e7\u00e3o-na-pr\u00e1tica&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Vis\u00e3o geral dos protocolos que vamos estudar&quot;,&quot;value&quot;:&quot;vis\u00e3o-geral-dos-protocolos-que-vamos-estudar&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Boas e m\u00e1s decis\u00f5es logo no in\u00edcio&quot;,&quot;value&quot;:&quot;boas-e-m\u00e1s-decis\u00f5es-logo-no-in\u00edcio&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;SLIP (Serial Line Internet Protocol): simplicidade que cobra seu pre\u00e7o&quot;,&quot;value&quot;:&quot;slip-serial-line-internet-protocol-simplicidade-que-cobra-seu-pre\u00e7o&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Como o SLIP funciona (byte a byte)&quot;,&quot;value&quot;:&quot;como-o-slip-funciona-byte-a-byte&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Exemplo conceitual de framing SLIP&quot;,&quot;value&quot;:&quot;exemplo-conceitual-de-framing-slip&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Implementa\u00e7\u00e3o did\u00e1tica \u2014 Encoder SLIP em C&quot;,&quot;value&quot;:&quot;implementa\u00e7\u00e3o-did\u00e1tica-encoder-slip-em-c&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Decoder SLIP (lado cr\u00edtico)&quot;,&quot;value&quot;:&quot;decoder-slip-lado-cr\u00edtico&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Onde o SLIP \u00e9 uma boa escolha&quot;,&quot;value&quot;:&quot;onde-o-slip-\u00e9-uma-boa-escolha&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Onde o SLIP \u00e9 uma m\u00e1 escolha (e por qu\u00ea)&quot;,&quot;value&quot;:&quot;onde-o-slip-\u00e9-uma-m\u00e1-escolha-e-por-qu\u00ea&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Conclus\u00e3o parcial&quot;,&quot;value&quot;:&quot;conclus\u00e3o-parcial&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;COBS (Consistent Overhead Byte Stuffing): framing determin\u00edstico e recupera\u00e7\u00e3o real&quot;,&quot;value&quot;:&quot;cobs-consistent-overhead-byte-stuffing-framing-determin\u00edstico-e-recupera\u00e7\u00e3o-real&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Ideia central do COBS&quot;,&quot;value&quot;:&quot;ideia-central-do-cobs&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Exemplo conceitual&quot;,&quot;value&quot;:&quot;exemplo-conceitual&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Encoder COBS \u2014 implementa\u00e7\u00e3o did\u00e1tica em C&quot;,&quot;value&quot;:&quot;encoder-cobs-implementa\u00e7\u00e3o-did\u00e1tica-em-c&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Decoder COBS \u2014 recupera\u00e7\u00e3o previs\u00edvel&quot;,&quot;value&quot;:&quot;decoder-cobs-recupera\u00e7\u00e3o-previs\u00edvel&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Por que COBS \u00e9 realmente auto-sincroniz\u00e1vel&quot;,&quot;value&quot;:&quot;por-que-cobs-\u00e9-realmente-auto-sincroniz\u00e1vel&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Boas e m\u00e1s escolhas com COBS&quot;,&quot;value&quot;:&quot;boas-e-m\u00e1s-escolhas-com-cobs&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Compara\u00e7\u00e3o direta: SLIP \u00d7 COBS&quot;,&quot;value&quot;:&quot;compara\u00e7\u00e3o-direta-slip-\u00d7-cobs&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Conclus\u00e3o parcial&quot;,&quot;value&quot;:&quot;conclus\u00e3o-parcial&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;CBOR (Concise Binary Object Representation): sem\u00e2ntica bin\u00e1ria n\u00e3o \u00e9 framing&quot;,&quot;value&quot;:&quot;cbor-concise-binary-object-representation-sem\u00e2ntica-bin\u00e1ria-n\u00e3o-\u00e9-framing&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;O que o CBOR realmente define&quot;,&quot;value&quot;:&quot;o-que-o-cbor-realmente-define&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Exemplo conceitual: CBOR vs JSON&quot;,&quot;value&quot;:&quot;exemplo-conceitual-cbor-vs-json&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Erro cl\u00e1ssico: \u201cvou ler CBOR direto da UART\u201d&quot;,&quot;value&quot;:&quot;erro-cl\u00e1ssico-vou-ler-cbor-direto-da-uart&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Uso correto: CBOR como payload, COBS como transporte&quot;,&quot;value&quot;:&quot;uso-correto-cbor-como-payload-cobs-como-transporte&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Exemplo did\u00e1tico \u2014 gerando CBOR (C)&quot;,&quot;value&quot;:&quot;exemplo-did\u00e1tico-gerando-cbor-c&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Encapsulando CBOR com COBS&quot;,&quot;value&quot;:&quot;encapsulando-cbor-com-cobs&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Boas e m\u00e1s escolhas com CBOR&quot;,&quot;value&quot;:&quot;boas-e-m\u00e1s-escolhas-com-cbor&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Compara\u00e7\u00e3o direta: COBS \u00d7 CBOR&quot;,&quot;value&quot;:&quot;compara\u00e7\u00e3o-direta-cobs-\u00d7-cbor&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Conclus\u00e3o parcial&quot;,&quot;value&quot;:&quot;conclus\u00e3o-parcial&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Compara\u00e7\u00e3o geral, decis\u00f5es de projeto e checklist de boas pr\u00e1ticas&quot;,&quot;value&quot;:&quot;compara\u00e7\u00e3o-geral-decis\u00f5es-de-projeto-e-checklist-de-boas-pr\u00e1ticas&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Compara\u00e7\u00e3o t\u00e9cnica consolidada&quot;,&quot;value&quot;:&quot;compara\u00e7\u00e3o-t\u00e9cnica-consolidada&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Arquiteturas t\u00edpicas (boas escolhas)&quot;,&quot;value&quot;:&quot;arquiteturas-t\u00edpicas-boas-escolhas&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;M\u00e1s decis\u00f5es cl\u00e1ssicas (e por que falham)&quot;,&quot;value&quot;:&quot;m\u00e1s-decis\u00f5es-cl\u00e1ssicas-e-por-que-falham&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Checklist de boas pr\u00e1ticas (engenharia de firmware)&quot;,&quot;value&quot;:&quot;checklist-de-boas-pr\u00e1ticas-engenharia-de-firmware&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Regra pr\u00e1tica para n\u00e3o errar&quot;,&quot;value&quot;:&quot;regra-pr\u00e1tica-para-n\u00e3o-errar&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Conclus\u00e3o do artigo&quot;,&quot;value&quot;:&quot;conclus\u00e3o-do-artigo&quot;,&quot;isDelete&quot;:false}]" data-smooth="true" data-top-offset=""><div class="eb-toc__list-wrap"><ul class='eb-toc__list'><li><a href="#eb-table-content-0">Conceito, motivação e panorama geral</a><li><a href="#eb-table-content-1">O que significa “auto-sincronização” na prática</a><li><a href="#eb-table-content-2">Visão geral dos protocolos que vamos estudar</a><li><a href="#eb-table-content-3">Boas e más decisões logo no início</a><li><a href="#eb-table-content-4">SLIP (Serial Line Internet Protocol): simplicidade que cobra seu preço</a><ul class='eb-toc__list'><li><a href="#como-o-slip-funciona-byte-a-byte">Como o SLIP funciona (byte a byte)</a><li><a href="#exemplo-conceitual-de-framing-slip">Exemplo conceitual de framing SLIP</a><li><a href="#eb-table-content-7">Implementação didática — Encoder SLIP em C</a><li><a href="#eb-table-content-8">Decoder SLIP (lado crítico)</a><li><a href="#eb-table-content-9">Onde o SLIP é uma boa escolha</a><li><a href="#eb-table-content-10">Onde o SLIP é uma má escolha (e por quê)</a><li><a href="#eb-table-content-11">Conclusão parcial</a></li></ul><li><a href="#eb-table-content-12">COBS (Consistent Overhead Byte Stuffing): framing determinístico e recuperação real</a><ul class='eb-toc__list'><li><a href="#ideia-central-do-cobs">Ideia central do COBS</a><li><a href="#exemplo-conceitual">Exemplo conceitual</a><li><a href="#eb-table-content-15">Encoder COBS — implementação didática em C</a><li><a href="#eb-table-content-16">Decoder COBS — recuperação previsível</a><li><a href="#eb-table-content-17">Por que COBS é realmente auto-sincronizável</a><li><a href="#eb-table-content-18">Boas e más escolhas com COBS</a><li><a href="#eb-table-content-19">Comparação direta: SLIP × COBS</a><li><a href="#eb-table-content-20">Conclusão parcial</a></li></ul><li><a href="#eb-table-content-21">CBOR (Concise Binary Object Representation): semântica binária não é framing</a><ul class='eb-toc__list'><li><a href="#o-que-o-cbor-realmente-define">O que o CBOR realmente define</a><li><a href="#exemplo-conceitual-cbor-vs-json">Exemplo conceitual: CBOR vs JSON</a><li><a href="#eb-table-content-24">Erro clássico: “vou ler CBOR direto da UART”</a><li><a href="#uso-correto-cbor-como-payload-cobs-como-transporte">Uso correto: CBOR como payload, COBS como transporte</a><li><a href="#eb-table-content-26">Exemplo didático — gerando CBOR (C)</a><li><a href="#encapsulando-cbor-com-cobs">Encapsulando CBOR com COBS</a><li><a href="#eb-table-content-28">Boas e más escolhas com CBOR</a><li><a href="#eb-table-content-29">Comparação direta: COBS × CBOR</a><li><a href="#eb-table-content-30">Conclusão parcial</a></li></ul><li><a href="#eb-table-content-31">Comparação geral, decisões de projeto e checklist de boas práticas</a><ul class='eb-toc__list'><li><a href="#eb-table-content-32">Comparação técnica consolidada</a><li><a href="#eb-table-content-33">Arquiteturas típicas (boas escolhas)</a><li><a href="#eb-table-content-34">Más decisões clássicas (e por que falham)</a><li><a href="#eb-table-content-35">Checklist de boas práticas (engenharia de firmware)</a><li><a href="#eb-table-content-36">Regra prática para não errar</a><li><a href="#eb-table-content-37">Conclusão do artigo</a></li></ul></ul></div></div></div></div></div>


<h3 class="wp-block-heading"><strong>Conceito, motivação e panorama geral</strong></h3>



<p>Em sistemas embarcados reais, <strong>dados seriais não chegam “limpos”</strong>. Há ruído elétrico, bytes perdidos, reinicializações assíncronas, buffers cheios e tarefas concorrentes. Protocolos <strong>auto-sincronizáveis</strong> surgem exatamente para resolver esse cenário: eles permitem <strong>recuperar o alinhamento do fluxo</strong> mesmo quando o receptor começa a ler no meio de um pacote, sem depender de resets globais ou estados externos.</p>



<p>Um protocolo é auto-sincronizável quando o receptor consegue, <strong>observando apenas o fluxo de bytes</strong>, reencontrar limites válidos de mensagem após erros. Isso contrasta com abordagens frágeis como “leia N bytes e confie”, que falham assim que um único byte se perde. Em UART, SPI em modo streaming, CDC-ACM, ou qualquer link byte-oriented, essa propriedade <strong>define se o sistema se recupera sozinho ou trava silenciosamente</strong>.</p>



<p>Nesta série, vamos analisar três abordagens amplamente usadas — <strong>SLIP</strong>, <strong>COBS</strong> e <strong>CBOR</strong> — não como “formatos bonitos”, mas como <strong>decisões de engenharia</strong>. Vamos discutir <strong>o que cada uma garante</strong>, <strong>o que não garante</strong>, <strong>onde usar</strong>, <strong>onde evitar</strong>, e <strong>como implementar corretamente</strong> em firmware de produção.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">O que significa “auto-sincronização” na prática</h3>



<p>Do ponto de vista do firmware, auto-sincronização implica três capacidades essenciais:</p>



<ol class="wp-block-list">
<li><strong>Delimitação inequívoca</strong><br>O receptor precisa identificar onde uma mensagem começa e termina, mesmo após bytes inválidos.</li>



<li><strong>Recuperação após erro</strong><br>Um erro local (byte perdido, extra, corrompido) <strong>não pode invalidar todo o fluxo</strong>.</li>



<li><strong>Parsing incremental (streaming)</strong><br>O protocolo deve permitir processamento byte-a-byte, sem exigir buffers gigantes ou leituras bloqueantes.</li>
</ol>



<p>SLIP, COBS e CBOR resolvem esses pontos de formas <strong>fundamentalmente diferentes</strong>, e entender essas diferenças é o que separa firmware de laboratório de firmware industrial.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Visão geral dos protocolos que vamos estudar</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Protocolo</th><th>Natureza</th><th>Resolve framing</th><th>Auto-sincroniza</th><th>Detecta erro</th><th>Uso típico</th></tr></thead><tbody><tr><td><strong>SLIP</strong></td><td>Delimitador + escape</td><td>Parcial</td><td>Fraca</td><td>Não</td><td>Debug simples, legado</td></tr><tr><td><strong>COBS</strong></td><td>Codificação de comprimento</td><td>Forte</td><td>Forte</td><td>Não</td><td>UART robusta, SPI streaming</td></tr><tr><td><strong>CBOR</strong></td><td>Serialização binária estruturada</td><td>Não (sozinho)</td><td>Parcial</td><td>Opcional</td><td>RPC, IoT, payload semântico</td></tr></tbody></table></figure>



<p>Um erro comum é tratar <strong>CBOR como protocolo de transporte</strong> — ele <strong>não é</strong>. CBOR define <strong>como os dados são codificados</strong>, não <strong>como são enquadrados</strong> no fluxo. Por isso, em sistemas bem projetados, CBOR quase sempre aparece <strong>sobre</strong> COBS ou outro método de framing.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p></p>



<h3 class="wp-block-heading">Boas e más decisões logo no início</h3>



<p><strong>Boas escolhas</strong></p>



<ul class="wp-block-list">
<li>Pensar em framing <strong>antes</strong> de escrever o parser</li>



<li>Separar claramente: <em>transporte</em> × <em>serialização</em></li>



<li>Assumir que bytes <strong>serão perdidos</strong> em algum momento</li>
</ul>



<p><strong>Más escolhas</strong></p>



<ul class="wp-block-list">
<li>Usar <code>scanf()</code> ou parsing por strings</li>



<li>Depender de timeouts para “adivinhar” fim de pacote</li>



<li>Achar que CRC substitui framing (não substitui)</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/><p>The post <a href="https://mcu.tec.br/protoclolos/uart-serial/protocolos-auto-sincronizaveis-em-sistemas-embarcados/">Protocolos auto-sincronizáveis em sistemas embarcados</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://mcu.tec.br/protoclolos/uart-serial/protocolos-auto-sincronizaveis-em-sistemas-embarcados/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1108</post-id>	</item>
		<item>
		<title>Zephyr no ESP32: ADC Contínuo com DMA, VAD e Processamento de Sinais em Tempo Real</title>
		<link>https://mcu.tec.br/rtos/zephyr-no-esp32-adc-continuo-com-dma-vad-e-processamento-de-sinais-em-tempo-real/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=zephyr-no-esp32-adc-continuo-com-dma-vad-e-processamento-de-sinais-em-tempo-real</link>
					<comments>https://mcu.tec.br/rtos/zephyr-no-esp32-adc-continuo-com-dma-vad-e-processamento-de-sinais-em-tempo-real/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Delfino]]></dc:creator>
		<pubDate>Tue, 13 Jan 2026 20:01:18 +0000</pubDate>
				<category><![CDATA[lwIP]]></category>
		<category><![CDATA[RTOS]]></category>
		<guid isPermaLink="false">https://mcu.tec.br/?p=1061</guid>

					<description><![CDATA[<p>Aprenda como usar o Zephyr OS no ESP32 para implementar leitura contínua do ADC com DMA, detecção de atividade de voz (VAD) e processamento de sinais em tempo real. Este artigo apresenta uma arquitetura completa baseada em threads produtor/consumidor, configuração do ADC via Device Tree, uso de buffers eficientes com slab e message queue, além de técnicas de DSP para estimar a frequência dominante do sinal coletado. São abordadas estratégias práticas de VAD em software e em hardware (ESP32-P4), priorização de tarefas, diagnóstico de gargalos e boas práticas para sistemas embarcados críticos. Um guia técnico, detalhado e aplicável a projetos de áudio, vibração e análise espectral em microcontroladores ESP32 com Zephyr.</p>
<p>The post <a href="https://mcu.tec.br/rtos/zephyr-no-esp32-adc-continuo-com-dma-vad-e-processamento-de-sinais-em-tempo-real/">Zephyr no ESP32: ADC Contínuo com DMA, VAD e Processamento de Sinais em Tempo Real</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></description>
										<content:encoded><![CDATA[<div class="root-eb-toc-p2mmp wp-block-essential-blocks-table-of-contents"><div class="eb-parent-wrapper eb-parent-eb-toc-p2mmp "><div class="eb-toc-container eb-toc-p2mmp  eb-toc-is-not-sticky eb-toc-not-collapsible eb-toc-initially-not-collapsed eb-toc-scrollToTop style-1 list-style-none" data-scroll-top="false" data-scroll-top-icon="fas fa-angle-up" data-collapsible="false" data-sticky-hide-mobile="false" data-sticky="false" data-scroll-target="scroll_to_toc" data-copy-link="false" data-editor-type="" data-hide-desktop="false" data-hide-tab="false" data-hide-mobile="false" data-itemCollapsed="false" data-highlight-scroll="false"><div class="eb-toc-header"><h2 class="eb-toc-title">Table of Contents</h2></div><div class="eb-toc-wrapper " data-headers="[{&quot;level&quot;:2,&quot;content&quot;:&quot;Vis\u00e3o geral da solu\u00e7\u00e3o no Zephyr para ESP32: ADC cont\u00ednuo (DMA), VAD e pipeline produtor\/consumidor&quot;,&quot;text&quot;:&quot;Vis\u00e3o geral da solu\u00e7\u00e3o no Zephyr para ESP32: ADC cont\u00ednuo (DMA), VAD e pipeline produtor\/consumidor&quot;,&quot;link&quot;:&quot;eb-table-content-0&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;O ponto cr\u00edtico: \u201cZephyr puro\u201d vs \u201cdriver cont\u00ednuo do ESP-IDF\u201d&quot;,&quot;text&quot;:&quot;O ponto cr\u00edtico: \u201cZephyr puro\u201d vs \u201cdriver cont\u00ednuo do ESP-IDF\u201d&quot;,&quot;link&quot;:&quot;eb-table-content-1&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;VAD no ESP32: duas realidades (hardware x software)&quot;,&quot;text&quot;:&quot;VAD no ESP32: duas realidades (hardware x software)&quot;,&quot;link&quot;:&quot;vad-no-esp32-duas-realidades-hardware-x-software&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Pipeline proposta (alto n\u00edvel)&quot;,&quot;text&quot;:&quot;Pipeline proposta (alto n\u00edvel)&quot;,&quot;link&quot;:&quot;eb-table-content-3&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Onde o DeviceTree entra (mesmo usando ADC cont\u00ednuo do ESP-IDF)&quot;,&quot;text&quot;:&quot;Onde o DeviceTree entra (mesmo usando ADC cont\u00ednuo do ESP-IDF)&quot;,&quot;link&quot;:&quot;eb-table-content-4&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;DeviceTree no Zephyr (ESP32): configurando ADC e o \u201cn\u00f3 da aplica\u00e7\u00e3o\u201d para io-channels, + base do projeto&quot;,&quot;text&quot;:&quot;DeviceTree no Zephyr (ESP32): configurando ADC e o \u201cn\u00f3 da aplica\u00e7\u00e3o\u201d para io-channels, + base do projeto&quot;,&quot;link&quot;:&quot;eb-table-content-5&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;2.1 boards\/esp32s3_devkitc.overlay (exemplo) \u2014 ADC + n\u00f3 da aplica\u00e7\u00e3o&quot;,&quot;text&quot;:&quot;2.1 boards\/esp32s3_devkitc.overlay (exemplo) \u2014 ADC + n\u00f3 da aplica\u00e7\u00e3o&quot;,&quot;link&quot;:&quot;eb-table-content-6&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;2.2 prj.conf \u2014 drivers e infraestrutura (threads, msgq, ring buffer opcional)&quot;,&quot;text&quot;:&quot;2.2 prj.conf \u2014 drivers e infraestrutura (threads, msgq, ring buffer opcional)&quot;,&quot;link&quot;:&quot;22-prjconf-drivers-e-infraestrutura-threads-msgq-ring-buffer-opcional&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;2.3 Contrato produtor\/consumidor: bloco de amostras + evento (VAD start\/end)&quot;,&quot;text&quot;:&quot;2.3 Contrato produtor\/consumidor: bloco de amostras + evento (VAD start\/end)&quot;,&quot;link&quot;:&quot;23-contrato-produtorconsumidor-bloco-de-amostras-evento-vad-startend&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;2.4 Esqueleto funcional das threads (sem DMA\/VAD ainda)&quot;,&quot;text&quot;:&quot;2.4 Esqueleto funcional das threads (sem DMA\/VAD ainda)&quot;,&quot;link&quot;:&quot;24-esqueleto-funcional-das-threads-sem-dmavad-ainda&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Aquisi\u00e7\u00e3o cont\u00ednua \u201ctipo DMA\u201d no ESP32 (ADC continuous mode), VAD delimitando stream e envio para o consumidor&quot;,&quot;text&quot;:&quot;Aquisi\u00e7\u00e3o cont\u00ednua \u201ctipo DMA\u201d no ESP32 (ADC continuous mode), VAD delimitando stream e envio para o consumidor&quot;,&quot;link&quot;:&quot;eb-table-content-10&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;3.1 Ajustes de prj.conf para usar o driver cont\u00ednuo do ESP-IDF&quot;,&quot;text&quot;:&quot;3.1 Ajustes de prj.conf para usar o driver cont\u00ednuo do ESP-IDF&quot;,&quot;link&quot;:&quot;eb-table-content-11&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;3.2 Produtor com ADC cont\u00ednuo (DMA): inicializa\u00e7\u00e3o + loop de leitura&quot;,&quot;text&quot;:&quot;3.2 Produtor com ADC cont\u00ednuo (DMA): inicializa\u00e7\u00e3o + loop de leitura&quot;,&quot;link&quot;:&quot;eb-table-content-12&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;3.3 Encaixe do VAD em hardware (ESP32-P4) no mesmo contrato de eventos&quot;,&quot;text&quot;:&quot;3.3 Encaixe do VAD em hardware (ESP32-P4) no mesmo contrato de eventos&quot;,&quot;link&quot;:&quot;33-encaixe-do-vad-em-hardware-esp32-p4-no-mesmo-contrato-de-eventos&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;3.4 Atualizando o main.c para usar o produtor real&quot;,&quot;text&quot;:&quot;3.4 Atualizando o main.c para usar o produtor real&quot;,&quot;link&quot;:&quot;34-atualizando-o-mainc-para-usar-o-produtor-real&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Consumidor: estimando a frequ\u00eancia principal do stream e imprimindo na UART&quot;,&quot;text&quot;:&quot;Consumidor: estimando a frequ\u00eancia principal do stream e imprimindo na UART&quot;,&quot;link&quot;:&quot;eb-table-content-15&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;4.1 Par\u00e2metros do DSP&quot;,&quot;text&quot;:&quot;4.1 Par\u00e2metros do DSP&quot;,&quot;link&quot;:&quot;eb-table-content-16&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;4.2 Implementa\u00e7\u00e3o do Goertzel + janela Hann (tudo em C puro)&quot;,&quot;text&quot;:&quot;4.2 Implementa\u00e7\u00e3o do Goertzel + janela Hann (tudo em C puro)&quot;,&quot;link&quot;:&quot;eb-table-content-17&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;4.3 Consumidor completo: acumulando janela, processando em VOICE_END (e tamb\u00e9m \u201cao vivo\u201d)&quot;,&quot;text&quot;:&quot;4.3 Consumidor completo: acumulando janela, processando em VOICE_END (e tamb\u00e9m \u201cao vivo\u201d)&quot;,&quot;link&quot;:&quot;eb-table-content-18&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;4.4 Resultado esperado na UART&quot;,&quot;text&quot;:&quot;4.4 Resultado esperado na UART&quot;,&quot;link&quot;:&quot;44-resultado-esperado-na-uart&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Fechando o projeto: mapeando ADC pelo DeviceTree, janela deslizante com overlap e Goertzel em ponto fixo (Q31)&quot;,&quot;text&quot;:&quot;Fechando o projeto: mapeando ADC pelo DeviceTree, janela deslizante com overlap e Goertzel em ponto fixo (Q31)&quot;,&quot;link&quot;:&quot;fechando-o-projeto-mapeando-adc-pelo-devicetree-janela-deslizante-com-overlap-e-goertzel-em-ponto-fixo-q31&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;5.1 Produtor: traduzindo adc_dt_spec (DeviceTree) para unidade\/canal do ESP-IDF&quot;,&quot;text&quot;:&quot;5.1 Produtor: traduzindo adc_dt_spec (DeviceTree) para unidade\/canal do ESP-IDF&quot;,&quot;link&quot;:&quot;51-produtor-traduzindo-adc_dt_spec-devicetree-para-unidadecanal-do-esp-idf&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;5.2 Consumidor: janela deslizante com overlap (50%) para suavizar a estimativa&quot;,&quot;text&quot;:&quot;5.2 Consumidor: janela deslizante com overlap (50%) para suavizar a estimativa&quot;,&quot;link&quot;:&quot;52-consumidor-janela-deslizante-com-overlap-50-para-suavizar-a-estimativa&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;5.3 Goertzel em ponto fixo (Q31): quando voc\u00ea quer custo determin\u00edstico&quot;,&quot;text&quot;:&quot;5.3 Goertzel em ponto fixo (Q31): quando voc\u00ea quer custo determin\u00edstico&quot;,&quot;link&quot;:&quot;eb-table-content-23&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;5.4 Checklist de integra\u00e7\u00e3o (para voc\u00ea n\u00e3o \u201cperder o fio\u201d)&quot;,&quot;text&quot;:&quot;5.4 Checklist de integra\u00e7\u00e3o (para voc\u00ea n\u00e3o \u201cperder o fio\u201d)&quot;,&quot;link&quot;:&quot;eb-table-content-24&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Pacote \u201ccopi\u00e1vel\u201d: \u00e1rvore de arquivos, CMakeLists.txt, main.c consolidado e calibra\u00e7\u00e3o pr\u00e1tica&quot;,&quot;text&quot;:&quot;Pacote \u201ccopi\u00e1vel\u201d: \u00e1rvore de arquivos, CMakeLists.txt, main.c consolidado e calibra\u00e7\u00e3o pr\u00e1tica&quot;,&quot;link&quot;:&quot;eb-table-content-25&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;6.1 \u00c1rvore de arquivos sugerida&quot;,&quot;text&quot;:&quot;6.1 \u00c1rvore de arquivos sugerida&quot;,&quot;link&quot;:&quot;eb-table-content-26&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;6.2 CMakeLists.txt&quot;,&quot;text&quot;:&quot;6.2 CMakeLists.txt&quot;,&quot;link&quot;:&quot;62-cmakeliststxt&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;6.3 main.c consolidado (2 threads reais)&quot;,&quot;text&quot;:&quot;6.3 main.c consolidado (2 threads reais)&quot;,&quot;link&quot;:&quot;63-mainc-consolidado-2-threads-reais&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;6.4 app_stream.h (com par\u00e2metros DSP + mensagens)&quot;,&quot;text&quot;:&quot;6.4 app_stream.h (com par\u00e2metros DSP + mensagens)&quot;,&quot;link&quot;:&quot;eb-table-content-29&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;6.5 Build\/flash (exemplo)&quot;,&quot;text&quot;:&quot;6.5 Build\/flash (exemplo)&quot;,&quot;link&quot;:&quot;65-buildflash-exemplo&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;6.6 Calibra\u00e7\u00e3o pr\u00e1tica: como fazer \u201cfuncionar de primeira\u201d sem ficar ca\u00e7ando fantasma&quot;,&quot;text&quot;:&quot;6.6 Calibra\u00e7\u00e3o pr\u00e1tica: como fazer \u201cfuncionar de primeira\u201d sem ficar ca\u00e7ando fantasma&quot;,&quot;link&quot;:&quot;eb-table-content-31&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Robustez em campo: prioridades, backpressure (slab\/msgq), telemetria e VAD com histerese&quot;,&quot;text&quot;:&quot;Robustez em campo: prioridades, backpressure (slab\/msgq), telemetria e VAD com histerese&quot;,&quot;link&quot;:&quot;robustez-em-campo-prioridades-backpressure-slabmsgq-telemetria-e-vad-com-histerese&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;7.1 Prioridades e lat\u00eancia: o produtor precisa ser \u201cquase intoc\u00e1vel\u201d&quot;,&quot;text&quot;:&quot;7.1 Prioridades e lat\u00eancia: o produtor precisa ser \u201cquase intoc\u00e1vel\u201d&quot;,&quot;link&quot;:&quot;eb-table-content-33&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;7.2 Backpressure: quando slab ou msgq enchem, voc\u00ea precisa saber \u201cpor qu\u00ea\u201d&quot;,&quot;text&quot;:&quot;7.2 Backpressure: quando slab ou msgq enchem, voc\u00ea precisa saber \u201cpor qu\u00ea\u201d&quot;,&quot;link&quot;:&quot;eb-table-content-34&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;7.3 Uma \u201ctask\u201d leve de diagn\u00f3stico (telemetria na UART sem travar o pipeline)&quot;,&quot;text&quot;:&quot;7.3 Uma \u201ctask\u201d leve de diagn\u00f3stico (telemetria na UART sem travar o pipeline)&quot;,&quot;link&quot;:&quot;eb-table-content-35&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;7.4 VAD por energia com histerese: reduz \u201cchattering\u201d (entra\/sai toda hora)&quot;,&quot;text&quot;:&quot;7.4 VAD por energia com histerese: reduz \u201cchattering\u201d (entra\/sai toda hora)&quot;,&quot;link&quot;:&quot;74-vad-por-energia-com-histerese-reduz-chattering-entrasai-toda-hora&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;7.5 Diagn\u00f3stico de \u201cperda de dados\u201d: como interpretar os sintomas&quot;,&quot;text&quot;:&quot;7.5 Diagn\u00f3stico de \u201cperda de dados\u201d: como interpretar os sintomas&quot;,&quot;link&quot;:&quot;eb-table-content-37&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;VAD em hardware (ESP32-P4) vs VAD em software + Conclus\u00e3o e SEO&quot;,&quot;text&quot;:&quot;VAD em hardware (ESP32-P4) vs VAD em software + Conclus\u00e3o e SEO&quot;,&quot;link&quot;:&quot;eb-table-content-38&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;8.1 VAD em hardware (ESP32-P4): quando faz sentido usar&quot;,&quot;text&quot;:&quot;8.1 VAD em hardware (ESP32-P4): quando faz sentido usar&quot;,&quot;link&quot;:&quot;81-vad-em-hardware-esp32-p4-quando-faz-sentido-usar&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;8.2 VAD em software: por que ainda \u00e9 extremamente relevante&quot;,&quot;text&quot;:&quot;8.2 VAD em software: por que ainda \u00e9 extremamente relevante&quot;,&quot;link&quot;:&quot;eb-table-content-40&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;8.3 S\u00edntese arquitetural do artigo&quot;,&quot;text&quot;:&quot;8.3 S\u00edntese arquitetural do artigo&quot;,&quot;link&quot;:&quot;eb-table-content-41&quot;}]" data-visible="[true,true,true,true,true,true]" data-delete-headers="[{&quot;label&quot;:&quot;Vis\u00e3o geral da solu\u00e7\u00e3o no Zephyr para ESP32: ADC cont\u00ednuo (DMA), VAD e pipeline produtor\/consumidor&quot;,&quot;value&quot;:&quot;vis\u00e3o-geral-da-solu\u00e7\u00e3o-no-zephyr-para-esp32-adc-cont\u00ednuo-dma-vad-e-pipeline-produtorconsumidor&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;O ponto cr\u00edtico: \u201cZephyr puro\u201d vs \u201cdriver cont\u00ednuo do ESP-IDF\u201d&quot;,&quot;value&quot;:&quot;o-ponto-cr\u00edtico-zephyr-puro-vs-driver-cont\u00ednuo-do-esp-idf&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;VAD no ESP32: duas realidades (hardware x software)&quot;,&quot;value&quot;:&quot;vad-no-esp32-duas-realidades-hardware-x-software&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Pipeline proposta (alto n\u00edvel)&quot;,&quot;value&quot;:&quot;pipeline-proposta-alto-n\u00edvel&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Onde o DeviceTree entra (mesmo usando ADC cont\u00ednuo do ESP-IDF)&quot;,&quot;value&quot;:&quot;onde-o-devicetree-entra-mesmo-usando-adc-cont\u00ednuo-do-esp-idf&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;DeviceTree no Zephyr (ESP32): configurando ADC e o \u201cn\u00f3 da aplica\u00e7\u00e3o\u201d para io-channels, + base do projeto&quot;,&quot;value&quot;:&quot;devicetree-no-zephyr-esp32-configurando-adc-e-o-n\u00f3-da-aplica\u00e7\u00e3o-para-io-channels-base-do-projeto&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;2.1 boards\/esp32s3_devkitc.overlay (exemplo) \u2014 ADC + n\u00f3 da aplica\u00e7\u00e3o&quot;,&quot;value&quot;:&quot;21-boardsesp32s3_devkitcoverlay-exemplo-adc-n\u00f3-da-aplica\u00e7\u00e3o&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;2.2 prj.conf \u2014 drivers e infraestrutura (threads, msgq, ring buffer opcional)&quot;,&quot;value&quot;:&quot;22-prjconf-drivers-e-infraestrutura-threads-msgq-ring-buffer-opcional&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;2.3 Contrato produtor\/consumidor: bloco de amostras + evento (VAD start\/end)&quot;,&quot;value&quot;:&quot;23-contrato-produtorconsumidor-bloco-de-amostras-evento-vad-startend&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;2.4 Esqueleto funcional das threads (sem DMA\/VAD ainda)&quot;,&quot;value&quot;:&quot;24-esqueleto-funcional-das-threads-sem-dmavad-ainda&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Aquisi\u00e7\u00e3o cont\u00ednua \u201ctipo DMA\u201d no ESP32 (ADC continuous mode), VAD delimitando stream e envio para o consumidor&quot;,&quot;value&quot;:&quot;aquisi\u00e7\u00e3o-cont\u00ednua-tipo-dma-no-esp32-adc-continuous-mode-vad-delimitando-stream-e-envio-para-o-consumidor&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;3.1 Ajustes de prj.conf para usar o driver cont\u00ednuo do ESP-IDF&quot;,&quot;value&quot;:&quot;31-ajustes-de-prjconf-para-usar-o-driver-cont\u00ednuo-do-esp-idf&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;3.2 Produtor com ADC cont\u00ednuo (DMA): inicializa\u00e7\u00e3o + loop de leitura&quot;,&quot;value&quot;:&quot;32-produtor-com-adc-cont\u00ednuo-dma-inicializa\u00e7\u00e3o-loop-de-leitura&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;3.3 Encaixe do VAD em hardware (ESP32-P4) no mesmo contrato de eventos&quot;,&quot;value&quot;:&quot;33-encaixe-do-vad-em-hardware-esp32-p4-no-mesmo-contrato-de-eventos&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;3.4 Atualizando o main.c para usar o produtor real&quot;,&quot;value&quot;:&quot;34-atualizando-o-mainc-para-usar-o-produtor-real&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Consumidor: estimando a frequ\u00eancia principal do stream e imprimindo na UART&quot;,&quot;value&quot;:&quot;consumidor-estimando-a-frequ\u00eancia-principal-do-stream-e-imprimindo-na-uart&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;4.1 Par\u00e2metros do DSP&quot;,&quot;value&quot;:&quot;41-par\u00e2metros-do-dsp&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;4.2 Implementa\u00e7\u00e3o do Goertzel + janela Hann (tudo em C puro)&quot;,&quot;value&quot;:&quot;42-implementa\u00e7\u00e3o-do-goertzel-janela-hann-tudo-em-c-puro&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;4.3 Consumidor completo: acumulando janela, processando em VOICE_END (e tamb\u00e9m \u201cao vivo\u201d)&quot;,&quot;value&quot;:&quot;43-consumidor-completo-acumulando-janela-processando-em-voice_end-e-tamb\u00e9m-ao-vivo&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;4.4 Resultado esperado na UART&quot;,&quot;value&quot;:&quot;44-resultado-esperado-na-uart&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Fechando o projeto: mapeando ADC pelo DeviceTree, janela deslizante com overlap e Goertzel em ponto fixo (Q31)&quot;,&quot;value&quot;:&quot;fechando-o-projeto-mapeando-adc-pelo-devicetree-janela-deslizante-com-overlap-e-goertzel-em-ponto-fixo-q31&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;5.1 Produtor: traduzindo adc_dt_spec (DeviceTree) para unidade\/canal do ESP-IDF&quot;,&quot;value&quot;:&quot;51-produtor-traduzindo-adc_dt_spec-devicetree-para-unidadecanal-do-esp-idf&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;5.2 Consumidor: janela deslizante com overlap (50%) para suavizar a estimativa&quot;,&quot;value&quot;:&quot;52-consumidor-janela-deslizante-com-overlap-50-para-suavizar-a-estimativa&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;5.3 Goertzel em ponto fixo (Q31): quando voc\u00ea quer custo determin\u00edstico&quot;,&quot;value&quot;:&quot;53-goertzel-em-ponto-fixo-q31-quando-voc\u00ea-quer-custo-determin\u00edstico&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;5.4 Checklist de integra\u00e7\u00e3o (para voc\u00ea n\u00e3o \u201cperder o fio\u201d)&quot;,&quot;value&quot;:&quot;54-checklist-de-integra\u00e7\u00e3o-para-voc\u00ea-n\u00e3o-perder-o-fio&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Pacote \u201ccopi\u00e1vel\u201d: \u00e1rvore de arquivos, CMakeLists.txt, main.c consolidado e calibra\u00e7\u00e3o pr\u00e1tica&quot;,&quot;value&quot;:&quot;pacote-copi\u00e1vel-\u00e1rvore-de-arquivos-cmakeliststxt-mainc-consolidado-e-calibra\u00e7\u00e3o-pr\u00e1tica&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;6.1 \u00c1rvore de arquivos sugerida&quot;,&quot;value&quot;:&quot;61-\u00e1rvore-de-arquivos-sugerida&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;6.2 CMakeLists.txt&quot;,&quot;value&quot;:&quot;62-cmakeliststxt&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;6.3 main.c consolidado (2 threads reais)&quot;,&quot;value&quot;:&quot;63-mainc-consolidado-2-threads-reais&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;6.4 app_stream.h (com par\u00e2metros DSP + mensagens)&quot;,&quot;value&quot;:&quot;64-app_streamh-com-par\u00e2metros-dsp-mensagens&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;6.5 Build\/flash (exemplo)&quot;,&quot;value&quot;:&quot;65-buildflash-exemplo&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;6.6 Calibra\u00e7\u00e3o pr\u00e1tica: como fazer \u201cfuncionar de primeira\u201d sem ficar ca\u00e7ando fantasma&quot;,&quot;value&quot;:&quot;66-calibra\u00e7\u00e3o-pr\u00e1tica-como-fazer-funcionar-de-primeira-sem-ficar-ca\u00e7ando-fantasma&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Robustez em campo: prioridades, backpressure (slab\/msgq), telemetria e VAD com histerese&quot;,&quot;value&quot;:&quot;robustez-em-campo-prioridades-backpressure-slabmsgq-telemetria-e-vad-com-histerese&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;7.1 Prioridades e lat\u00eancia: o produtor precisa ser \u201cquase intoc\u00e1vel\u201d&quot;,&quot;value&quot;:&quot;71-prioridades-e-lat\u00eancia-o-produtor-precisa-ser-quase-intoc\u00e1vel&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;7.2 Backpressure: quando slab ou msgq enchem, voc\u00ea precisa saber \u201cpor qu\u00ea\u201d&quot;,&quot;value&quot;:&quot;72-backpressure-quando-slab-ou-msgq-enchem-voc\u00ea-precisa-saber-por-qu\u00ea&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;7.3 Uma \u201ctask\u201d leve de diagn\u00f3stico (telemetria na UART sem travar o pipeline)&quot;,&quot;value&quot;:&quot;73-uma-task-leve-de-diagn\u00f3stico-telemetria-na-uart-sem-travar-o-pipeline&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;7.4 VAD por energia com histerese: reduz \u201cchattering\u201d (entra\/sai toda hora)&quot;,&quot;value&quot;:&quot;74-vad-por-energia-com-histerese-reduz-chattering-entrasai-toda-hora&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;7.5 Diagn\u00f3stico de \u201cperda de dados\u201d: como interpretar os sintomas&quot;,&quot;value&quot;:&quot;75-diagn\u00f3stico-de-perda-de-dados-como-interpretar-os-sintomas&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;VAD em hardware (ESP32-P4) vs VAD em software + Conclus\u00e3o e SEO&quot;,&quot;value&quot;:&quot;vad-em-hardware-esp32-p4-vs-vad-em-software-conclus\u00e3o-e-seo&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;8.1 VAD em hardware (ESP32-P4): quando faz sentido usar&quot;,&quot;value&quot;:&quot;81-vad-em-hardware-esp32-p4-quando-faz-sentido-usar&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;8.2 VAD em software: por que ainda \u00e9 extremamente relevante&quot;,&quot;value&quot;:&quot;82-vad-em-software-por-que-ainda-\u00e9-extremamente-relevante&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;8.3 S\u00edntese arquitetural do artigo&quot;,&quot;value&quot;:&quot;83-s\u00edntese-arquitetural-do-artigo&quot;,&quot;isDelete&quot;:false}]" data-smooth="true" data-top-offset=""><div class="eb-toc__list-wrap"><ul class='eb-toc__list'><li><a href="#eb-table-content-0">Visão geral da solução no Zephyr para ESP32: ADC contínuo (DMA), VAD e pipeline produtor/consumidor</a><ul class='eb-toc__list'><li><a href="#eb-table-content-1">O ponto crítico: “Zephyr puro” vs “driver contínuo do ESP-IDF”</a><li><a href="#vad-no-esp32-duas-realidades-hardware-x-software">VAD no ESP32: duas realidades (hardware x software)</a><li><a href="#eb-table-content-3">Pipeline proposta (alto nível)</a><li><a href="#eb-table-content-4">Onde o DeviceTree entra (mesmo usando ADC contínuo do ESP-IDF)</a></li></ul><li><a href="#eb-table-content-5">DeviceTree no Zephyr (ESP32): configurando ADC e o “nó da aplicação” para io-channels, + base do projeto</a><ul class='eb-toc__list'><li><a href="#eb-table-content-6">2.1 boards/esp32s3_devkitc.overlay (exemplo) — ADC + nó da aplicação</a><li><a href="#22-prjconf-drivers-e-infraestrutura-threads-msgq-ring-buffer-opcional">2.2 prj.conf — drivers e infraestrutura (threads, msgq, ring buffer opcional)</a><li><a href="#23-contrato-produtorconsumidor-bloco-de-amostras-evento-vad-startend">2.3 Contrato produtor/consumidor: bloco de amostras + evento (VAD start/end)</a><li><a href="#24-esqueleto-funcional-das-threads-sem-dmavad-ainda">2.4 Esqueleto funcional das threads (sem DMA/VAD ainda)</a></li></ul><li><a href="#eb-table-content-10">Aquisição contínua “tipo DMA” no ESP32 (ADC continuous mode), VAD delimitando stream e envio para o consumidor</a><ul class='eb-toc__list'><li><a href="#eb-table-content-11">3.1 Ajustes de prj.conf para usar o driver contínuo do ESP-IDF</a><li><a href="#eb-table-content-12">3.2 Produtor com ADC contínuo (DMA): inicialização + loop de leitura</a><li><a href="#33-encaixe-do-vad-em-hardware-esp32-p4-no-mesmo-contrato-de-eventos">3.3 Encaixe do VAD em hardware (ESP32-P4) no mesmo contrato de eventos</a><li><a href="#34-atualizando-o-mainc-para-usar-o-produtor-real">3.4 Atualizando o main.c para usar o produtor real</a></li></ul><li><a href="#eb-table-content-15">Consumidor: estimando a frequência principal do stream e imprimindo na UART</a><ul class='eb-toc__list'><li><a href="#eb-table-content-16">4.1 Parâmetros do DSP</a><li><a href="#eb-table-content-17">4.2 Implementação do Goertzel + janela Hann (tudo em C puro)</a><li><a href="#eb-table-content-18">4.3 Consumidor completo: acumulando janela, processando em VOICE_END (e também “ao vivo”)</a><li><a href="#44-resultado-esperado-na-uart">4.4 Resultado esperado na UART</a></li></ul><li><a href="#fechando-o-projeto-mapeando-adc-pelo-devicetree-janela-deslizante-com-overlap-e-goertzel-em-ponto-fixo-q31">Fechando o projeto: mapeando ADC pelo DeviceTree, janela deslizante com overlap e Goertzel em ponto fixo (Q31)</a><ul class='eb-toc__list'><li><a href="#51-produtor-traduzindo-adc_dt_spec-devicetree-para-unidadecanal-do-esp-idf">5.1 Produtor: traduzindo adc_dt_spec (DeviceTree) para unidade/canal do ESP-IDF</a><li><a href="#52-consumidor-janela-deslizante-com-overlap-50-para-suavizar-a-estimativa">5.2 Consumidor: janela deslizante com overlap (50%) para suavizar a estimativa</a><li><a href="#eb-table-content-23">5.3 Goertzel em ponto fixo (Q31): quando você quer custo determinístico</a><li><a href="#eb-table-content-24">5.4 Checklist de integração (para você não “perder o fio”)</a></li></ul><li><a href="#eb-table-content-25">Pacote “copiável”: árvore de arquivos, CMakeLists.txt, main.c consolidado e calibração prática</a><ul class='eb-toc__list'><li><a href="#eb-table-content-26">6.1 Árvore de arquivos sugerida</a><li><a href="#62-cmakeliststxt">6.2 CMakeLists.txt</a><li><a href="#63-mainc-consolidado-2-threads-reais">6.3 main.c consolidado (2 threads reais)</a><li><a href="#eb-table-content-29">6.4 app_stream.h (com parâmetros DSP + mensagens)</a><li><a href="#65-buildflash-exemplo">6.5 Build/flash (exemplo)</a><li><a href="#eb-table-content-31">6.6 Calibração prática: como fazer “funcionar de primeira” sem ficar caçando fantasma</a></li></ul><li><a href="#robustez-em-campo-prioridades-backpressure-slabmsgq-telemetria-e-vad-com-histerese">Robustez em campo: prioridades, backpressure (slab/msgq), telemetria e VAD com histerese</a><ul class='eb-toc__list'><li><a href="#eb-table-content-33">7.1 Prioridades e latência: o produtor precisa ser “quase intocável”</a><li><a href="#eb-table-content-34">7.2 Backpressure: quando slab ou msgq enchem, você precisa saber “por quê”</a><li><a href="#eb-table-content-35">7.3 Uma “task” leve de diagnóstico (telemetria na UART sem travar o pipeline)</a><li><a href="#74-vad-por-energia-com-histerese-reduz-chattering-entrasai-toda-hora">7.4 VAD por energia com histerese: reduz “chattering” (entra/sai toda hora)</a><li><a href="#eb-table-content-37">7.5 Diagnóstico de “perda de dados”: como interpretar os sintomas</a></li></ul><li><a href="#eb-table-content-38">VAD em hardware (ESP32-P4) vs VAD em software + Conclusão e SEO</a><ul class='eb-toc__list'><li><a href="#81-vad-em-hardware-esp32-p4-quando-faz-sentido-usar">8.1 VAD em hardware (ESP32-P4): quando faz sentido usar</a><li><a href="#eb-table-content-40">8.2 VAD em software: por que ainda é extremamente relevante</a><li><a href="#eb-table-content-41">8.3 Síntese arquitetural do artigo</a></li></ul></ul></div></div></div></div></div>


<h2 class="wp-block-heading">Visão geral da solução no Zephyr para ESP32: ADC contínuo (DMA), VAD e pipeline produtor/consumidor</h2>



<p>Quando você tenta fazer “captura de ruidos como da magnectostricção” a partir de ADC no ESP32 com o Zephyr, aparecem três problemas clássicos ao mesmo tempo: </p>



<ol class="wp-block-list">
<li><strong>aquisição contínua em alta taxa</strong> (idealmente com DMA para não “matar” a CPU), </li>



<li><strong>delimitação de trechos úteis</strong> (detectar início/fim de fala para não processar silêncio), e </li>



<li><strong>processamento em tempo real</strong> (calcular alguma métrica — aqui, a frequência principal — e jogar na UART). </li>
</ol>



<p>A arquitetura mais robusta para isso, no mundo de sistemas em tempo real, é separar responsabilidades em <em>threads</em> com um canal bem definido de troca de dados (fila/ring buffer), exatamente o tipo de organização que livros de padrões para sistemas em tempo real tratam como base de escalabilidade e diagnósticos previsíveis.</p>



<h3 class="wp-block-heading">O ponto crítico: “Zephyr puro” vs “driver contínuo do ESP-IDF”</h3>



<p>O Zephyr tem um caminho bem canônico para ADC via <strong>DeviceTree + ADC API</strong> (por exemplo o sample <code>adc_dt</code>) (<a href="https://docs.zephyrproject.org/latest/samples/drivers/adc/adc_dt/README.html?utm_source=chatgpt.com">docs.zephyrproject.org</a>) e possui binding específico para ADC do ESP32 (<code>espressif,esp32-adc</code>) (<a href="https://docs.zephyrproject.org/latest/build/dts/api/bindings/adc/espressif%2Cesp32-adc.html?utm_source=chatgpt.com">docs.zephyrproject.org</a>). Isso resolve bem leituras “por amostra” e configurações padrão.</p>



<p>Só que <strong>voz por ADC</strong> normalmente pede <strong>stream contínuo</strong>, e no ecossistema Espressif a forma “oficial” para isso é o <strong>ADC Continuous Mode driver</strong>, que foi desenhado para conversões contínuas e leitura de resultados em buffer (tipicamente alimentado por DMA) (<a href="https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/adc_continuous.html?utm_source=chatgpt.com">Espressif Systems</a>). Na prática, para cumprir o requisito (“leitura contínua por DMA do ADC”), o caminho mais realista é:</p>



<ul class="wp-block-list">
<li>usar <strong>Zephyr como RTOS</strong> (threads, filas, logging/uart), <strong>mas</strong></li>



<li>usar o <strong>driver de ADC contínuo do ESP-IDF</strong> por baixo (como “módulo”/componente), expondo para o Zephyr uma API de aquisição contínua.</li>
</ul>



<p>Isso evita reinventar o mecanismo de aquisição contínua e te dá taxas estáveis.</p>



<h3 class="wp-block-heading">VAD no ESP32: duas realidades (hardware x software)</h3>



<p>Aqui há uma nuance importante:</p>



<ul class="wp-block-list">
<li><strong>ESP32-P4</strong> tem módulo <strong>VAD em hardware</strong> documentado pela Espressif (<a href="https://docs.espressif.com/projects/esp-idf/en/stable/esp32p4/api-reference/peripherals/vad.html?utm_source=chatgpt.com">Espressif Systems</a>). Nesse caso, faz sentido “delimitar stream” com evento/flag do próprio periférico.</li>



<li>Em vários outros ESP32 (ex.: ESP32 clássico, S3, etc.), é comum usar <strong>VAD por software</strong> vindo do ecossistema de áudio/speech da Espressif (ex.: ESP-SR/ADF). Há documentação de VAD em software no contexto dessas libs (<a href="https://espressif-docs.readthedocs-hosted.com/projects/esp-adf/en/latest/api-reference/speech-recognition/esp_vad.html?utm_source=chatgpt.com">espressif-docs.readthedocs-hosted.com</a>).</li>
</ul>



<p>Eu vou estruturar como se fosse <strong>uma pipeline genérica</strong> onde o “VAD” é um bloco que produz eventos <code>VOICE_START</code>/<code>VOICE_END</code>. A implementação desse bloco pode ser:</p>



<ul class="wp-block-list">
<li><strong>hardware VAD</strong> (se for ESP32-P4), ou</li>



<li><strong>software VAD</strong> (ESP-SR/ADF) quando o chip não oferecer o periférico.</li>
</ul>



<h3 class="wp-block-heading">Pipeline proposta (alto nível)</h3>



<p>A solução fica muito limpa com <strong>duas threads</strong> e um “canal” entre elas:</p>



<ol class="wp-block-list">
<li><strong>Thread Produtora (adc_stream_thread)</strong>
<ul class="wp-block-list">
<li>Inicializa ADC contínuo (driver do ESP-IDF) e começa a receber “frames” (blocos) de amostras.</li>



<li>Alimenta o bloco de VAD (hardware ou software).</li>



<li>Quando o VAD indicar <strong>fala ativa</strong>, empacota blocos de amostras e envia para a fila/ring buffer do consumidor.</li>



<li>Quando indicar <strong>fim da fala</strong>, envia um “marcador” (metadado) de fim de stream.</li>
</ul>
</li>



<li><strong>Thread Consumidora (dsp_uart_thread)</strong>
<ul class="wp-block-list">
<li>Recebe blocos do stream “recortado” pelo VAD.</li>



<li>Acumula um buffer mínimo (janela) e calcula a <strong>frequência principal</strong> (pitch/peak espectral) e imprime na UART.</li>
</ul>
</li>
</ol>



<p>Essa separação é a base para desempenho e previsibilidade: a thread de aquisição não fica “presa” em FFT/Goertzel; a thread de DSP não perde amostra porque alguém resolveu imprimir logs demais.</p>



<h3 class="wp-block-heading">Onde o DeviceTree entra (mesmo usando ADC contínuo do ESP-IDF)</h3>



<p>Mesmo que o “motor” de aquisição contínua venha do ESP-IDF, você ainda quer o <strong>DeviceTree</strong> como fonte única de verdade para:</p>



<ul class="wp-block-list">
<li>qual ADC está ativo,</li>



<li>quais canais/pinos estão habilitados,</li>



<li>ganho/atenuação/resolução,</li>



<li>e a UART de saída (console).</li>
</ul>



<p>O Zephyr já tem binding do ADC do ESP32 (<a href="https://docs.zephyrproject.org/latest/build/dts/api/bindings/adc/espressif%2Cesp32-adc.html?utm_source=chatgpt.com">docs.zephyrproject.org</a>) e o modelo de amostragem por DT é bem estabelecido no sample <code>adc_dt</code> (<a href="https://docs.zephyrproject.org/latest/samples/drivers/adc/adc_dt/README.html?utm_source=chatgpt.com">docs.zephyrproject.org</a>). A ideia prática é: <strong>o DT descreve os canais</strong>, e o o código traduz isso para a configuração do driver contínuo.</p>



<p>Na próxima seção), eu vou te entregar:</p>



<ul class="wp-block-list">
<li>um <strong>overlay <code>.overlay</code></strong> de exemplo configurando ADC e um “nó” de aplicação com <code>io-channels</code>,</li>



<li>o esqueleto completo das duas threads (produtor/consumidor) com filas,</li>



<li>e o desenho do contrato de mensagens (bloco de amostras + eventos VAD).</li>
</ul><p>The post <a href="https://mcu.tec.br/rtos/zephyr-no-esp32-adc-continuo-com-dma-vad-e-processamento-de-sinais-em-tempo-real/">Zephyr no ESP32: ADC Contínuo com DMA, VAD e Processamento de Sinais em Tempo Real</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://mcu.tec.br/rtos/zephyr-no-esp32-adc-continuo-com-dma-vad-e-processamento-de-sinais-em-tempo-real/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1061</post-id>	</item>
		<item>
		<title>UART não é Porta Serial: Como Projetar Protocolos Robustos em Sistemas Embarcados</title>
		<link>https://mcu.tec.br/protoclolos/uart-serial/uart-nao-e-porta-serial-como-projetar-protocolos-robustos-em-sistemas-embarcados/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=uart-nao-e-porta-serial-como-projetar-protocolos-robustos-em-sistemas-embarcados</link>
					<comments>https://mcu.tec.br/protoclolos/uart-serial/uart-nao-e-porta-serial-como-projetar-protocolos-robustos-em-sistemas-embarcados/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Delfino]]></dc:creator>
		<pubDate>Sun, 04 Jan 2026 00:42:48 +0000</pubDate>
				<category><![CDATA[UART (Serial)]]></category>
		<category><![CDATA[buffer UART]]></category>
		<category><![CDATA[comunicação embarcada]]></category>
		<category><![CDATA[comunicação serial]]></category>
		<category><![CDATA[CRC em UART]]></category>
		<category><![CDATA[engenharia de firmware]]></category>
		<category><![CDATA[firmware robusto]]></category>
		<category><![CDATA[framing UART]]></category>
		<category><![CDATA[FreeRTOS UART]]></category>
		<category><![CDATA[parsing defensivo]]></category>
		<category><![CDATA[protocolo UART]]></category>
		<category><![CDATA[protocolos binários]]></category>
		<category><![CDATA[ring buffer UART]]></category>
		<category><![CDATA[sistemas distribuídos embarcados]]></category>
		<category><![CDATA[sistemas embarcados]]></category>
		<category><![CDATA[UART]]></category>
		<category><![CDATA[USART]]></category>
		<category><![CDATA[Zephyr UART]]></category>
		<guid isPermaLink="false">https://mcu.tec.br/?p=1102</guid>

					<description><![CDATA[<p>UART é frequentemente tratada como uma simples porta serial para uso com printf e terminais, mas essa abordagem falha rapidamente em sistemas embarcados reais. Este artigo apresenta uma visão técnica e madura sobre UART como uma decisão de arquitetura, não como um periférico trivial. Ao longo do texto, discutimos bufferização correta, framing explícito, detecção de início de quadro, validação de integridade com CRC, parsing defensivo, recuperação de erros, perda de pacotes e re-sincronização. Com exemplos práticos em C comparando abordagens erradas e corretas, o artigo mostra como transformar UART em uma verdadeira camada de transporte para sistemas distribuídos embarcados, usados em ambientes industriais, robóticos e médicos. Ideal para engenheiros que desejam sair do nível de demos e construir firmware robusto, previsível e confiável para operação contínua em campo.</p>
<p>The post <a href="https://mcu.tec.br/protoclolos/uart-serial/uart-nao-e-porta-serial-como-projetar-protocolos-robustos-em-sistemas-embarcados/">UART não é Porta Serial: Como Projetar Protocolos Robustos em Sistemas Embarcados</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></description>
										<content:encoded><![CDATA[<div class="root-eb-toc-p8biy wp-block-essential-blocks-table-of-contents"><div class="eb-parent-wrapper eb-parent-eb-toc-p8biy "><div class="eb-toc-container eb-toc-p8biy  eb-toc-is-not-sticky eb-toc-not-collapsible eb-toc-initially-not-collapsed eb-toc-scrollToTop style-1 list-style-none" data-scroll-top="false" data-scroll-top-icon="fas fa-angle-up" data-collapsible="false" data-sticky-hide-mobile="false" data-sticky="false" data-scroll-target="scroll_to_toc" data-copy-link="false" data-editor-type="" data-hide-desktop="false" data-hide-tab="false" data-hide-mobile="false" data-itemCollapsed="false" data-highlight-scroll="false"><div class="eb-toc-header"><h2 class="eb-toc-title">Table of Contents</h2></div><div class="eb-toc-wrapper " data-headers="[{&quot;level&quot;:2,&quot;content&quot;:&quot;UART n\u00e3o \u00e9 uma porta: \u00e9 uma decis\u00e3o de arquitetura&quot;,&quot;text&quot;:&quot;UART n\u00e3o \u00e9 uma porta: \u00e9 uma decis\u00e3o de arquitetura&quot;,&quot;link&quot;:&quot;eb-table-content-0&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;O erro cl\u00e1ssico: UART como \u201clinha de printf\u201d&quot;,&quot;text&quot;:&quot;O erro cl\u00e1ssico: UART como \u201clinha de printf\u201d&quot;,&quot;link&quot;:&quot;eb-table-content-1&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;A mudan\u00e7a de mentalidade: UART como canal bruto&quot;,&quot;text&quot;:&quot;A mudan\u00e7a de mentalidade: UART como canal bruto&quot;,&quot;link&quot;:&quot;eb-table-content-2&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;UART como espelho do engenheiro&quot;,&quot;text&quot;:&quot;UART como espelho do engenheiro&quot;,&quot;link&quot;:&quot;uart-como-espelho-do-engenheiro&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Bufferiza\u00e7\u00e3o n\u00e3o \u00e9 opcional: \u00e9 o primeiro contrato com a realidade&quot;,&quot;text&quot;:&quot;Bufferiza\u00e7\u00e3o n\u00e3o \u00e9 opcional: \u00e9 o primeiro contrato com a realidade&quot;,&quot;link&quot;:&quot;eb-table-content-4&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;O erro cl\u00e1ssico: processar dados no contexto da interrup\u00e7\u00e3o&quot;,&quot;text&quot;:&quot;O erro cl\u00e1ssico: processar dados no contexto da interrup\u00e7\u00e3o&quot;,&quot;link&quot;:&quot;eb-table-content-5&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;A abordagem correta: ISR m\u00ednima + buffer expl\u00edcito&quot;,&quot;text&quot;:&quot;A abordagem correta: ISR m\u00ednima + buffer expl\u00edcito&quot;,&quot;link&quot;:&quot;eb-table-content-6&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Consumir bytes \u00e9 diferente de interpretar mensagens&quot;,&quot;text&quot;:&quot;Consumir bytes \u00e9 diferente de interpretar mensagens&quot;,&quot;link&quot;:&quot;eb-table-content-7&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Bufferiza\u00e7\u00e3o como base para escalabilidade&quot;,&quot;text&quot;:&quot;Bufferiza\u00e7\u00e3o como base para escalabilidade&quot;,&quot;link&quot;:&quot;eb-table-content-8&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Um crit\u00e9rio simples de maturidade&quot;,&quot;text&quot;:&quot;Um crit\u00e9rio simples de maturidade&quot;,&quot;link&quot;:&quot;eb-table-content-9&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Framing: bytes sem fronteiras n\u00e3o s\u00e3o mensagens&quot;,&quot;text&quot;:&quot;Framing: bytes sem fronteiras n\u00e3o s\u00e3o mensagens&quot;,&quot;link&quot;:&quot;eb-table-content-10&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;O erro cl\u00e1ssico: confiar em delimitadores \u201chumanos\u201d&quot;,&quot;text&quot;:&quot;O erro cl\u00e1ssico: confiar em delimitadores \u201chumanos\u201d&quot;,&quot;link&quot;:&quot;eb-table-content-11&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Framing \u00e9 uma decis\u00e3o de protocolo, n\u00e3o de conveni\u00eancia&quot;,&quot;text&quot;:&quot;Framing \u00e9 uma decis\u00e3o de protocolo, n\u00e3o de conveni\u00eancia&quot;,&quot;link&quot;:&quot;eb-table-content-12&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Abordagem correta 1: marcador de in\u00edcio + comprimento&quot;,&quot;text&quot;:&quot;Abordagem correta 1: marcador de in\u00edcio + comprimento&quot;,&quot;link&quot;:&quot;eb-table-content-13&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Abordagem correta 2: delimitador com escape (SLIP-like)&quot;,&quot;text&quot;:&quot;Abordagem correta 2: delimitador com escape (SLIP-like)&quot;,&quot;link&quot;:&quot;abordagem-correta-2-delimitador-com-escape-slip-like&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Framing define se o sistema sobrevive ao erro&quot;,&quot;text&quot;:&quot;Framing define se o sistema sobrevive ao erro&quot;,&quot;link&quot;:&quot;framing-define-se-o-sistema-sobrevive-ao-erro&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Uma regra pr\u00e1tica&quot;,&quot;text&quot;:&quot;Uma regra pr\u00e1tica&quot;,&quot;link&quot;:&quot;eb-table-content-16&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Integridade e parsing defensivo: confiar em bytes \u00e9 ingenuidade&quot;,&quot;text&quot;:&quot;Integridade e parsing defensivo: confiar em bytes \u00e9 ingenuidade&quot;,&quot;link&quot;:&quot;eb-table-content-17&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;O erro cl\u00e1ssico: \u201cse chegou, \u00e9 v\u00e1lido\u201d&quot;,&quot;text&quot;:&quot;O erro cl\u00e1ssico: \u201cse chegou, \u00e9 v\u00e1lido\u201d&quot;,&quot;link&quot;:&quot;eb-table-content-18&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Integridade n\u00e3o \u00e9 luxo, \u00e9 requisito&quot;,&quot;text&quot;:&quot;Integridade n\u00e3o \u00e9 luxo, \u00e9 requisito&quot;,&quot;link&quot;:&quot;eb-table-content-19&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Exemplo correto: CRC expl\u00edcito no frame&quot;,&quot;text&quot;:&quot;Exemplo correto: CRC expl\u00edcito no frame&quot;,&quot;link&quot;:&quot;eb-table-content-20&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Parsing defensivo: nunca confie no emissor&quot;,&quot;text&quot;:&quot;Parsing defensivo: nunca confie no emissor&quot;,&quot;link&quot;:&quot;parsing-defensivo-nunca-confie-no-emissor&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;UART como superf\u00edcie de ataque (mesmo sem rede)&quot;,&quot;text&quot;:&quot;UART como superf\u00edcie de ataque (mesmo sem rede)&quot;,&quot;link&quot;:&quot;eb-table-content-22&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Regra de ouro&quot;,&quot;text&quot;:&quot;Regra de ouro&quot;,&quot;link&quot;:&quot;regra-de-ouro&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Recupera\u00e7\u00e3o, perda e por que sistemas reais n\u00e3o assumem perfei\u00e7\u00e3o&quot;,&quot;text&quot;:&quot;Recupera\u00e7\u00e3o, perda e por que sistemas reais n\u00e3o assumem perfei\u00e7\u00e3o&quot;,&quot;link&quot;:&quot;eb-table-content-24&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;O erro cl\u00e1ssico: assumir que \u201cn\u00e3o acontece\u201d&quot;,&quot;text&quot;:&quot;O erro cl\u00e1ssico: assumir que \u201cn\u00e3o acontece\u201d&quot;,&quot;link&quot;:&quot;eb-table-content-25&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Sistemas reais assumem falha como condi\u00e7\u00e3o normal&quot;,&quot;text&quot;:&quot;Sistemas reais assumem falha como condi\u00e7\u00e3o normal&quot;,&quot;link&quot;:&quot;eb-table-content-26&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Timeouts: o rel\u00f3gio tamb\u00e9m \u00e9 parte do protocolo&quot;,&quot;text&quot;:&quot;Timeouts: o rel\u00f3gio tamb\u00e9m \u00e9 parte do protocolo&quot;,&quot;link&quot;:&quot;eb-table-content-27&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Re-sincroniza\u00e7\u00e3o expl\u00edcita&quot;,&quot;text&quot;:&quot;Re-sincroniza\u00e7\u00e3o expl\u00edcita&quot;,&quot;link&quot;:&quot;eb-table-content-28&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;Perda de pacotes n\u00e3o \u00e9 exce\u00e7\u00e3o&quot;,&quot;text&quot;:&quot;Perda de pacotes n\u00e3o \u00e9 exce\u00e7\u00e3o&quot;,&quot;link&quot;:&quot;eb-table-content-29&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;UART como transporte muda o jogo&quot;,&quot;text&quot;:&quot;UART como transporte muda o jogo&quot;,&quot;link&quot;:&quot;uart-como-transporte-muda-o-jogo&quot;},{&quot;level&quot;:3,&quot;content&quot;:&quot;A frase que resume tudo&quot;,&quot;text&quot;:&quot;A frase que resume tudo&quot;,&quot;link&quot;:&quot;a-frase-que-resume-tudo&quot;},{&quot;level&quot;:1,&quot;content&quot;:&quot;Checklist de Boas Pr\u00e1ticas \u2014 UART como Camada de Transporte&quot;,&quot;text&quot;:&quot;Checklist de Boas Pr\u00e1ticas \u2014 UART como Camada de Transporte&quot;,&quot;link&quot;:&quot;eb-table-content-32&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;1. Modelo Mental e Arquitetura&quot;,&quot;text&quot;:&quot;1. Modelo Mental e Arquitetura&quot;,&quot;link&quot;:&quot;1-modelo-mental-e-arquitetura&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;2. Interrup\u00e7\u00f5es (ISR)&quot;,&quot;text&quot;:&quot;2. Interrup\u00e7\u00f5es (ISR)&quot;,&quot;link&quot;:&quot;eb-table-content-34&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;3. Bufferiza\u00e7\u00e3o&quot;,&quot;text&quot;:&quot;3. Bufferiza\u00e7\u00e3o&quot;,&quot;link&quot;:&quot;eb-table-content-35&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;4. Framing&quot;,&quot;text&quot;:&quot;4. Framing&quot;,&quot;link&quot;:&quot;4-framing&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;5. Integridade&quot;,&quot;text&quot;:&quot;5. Integridade&quot;,&quot;link&quot;:&quot;5-integridade&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;6. Parsing Defensivo&quot;,&quot;text&quot;:&quot;6. Parsing Defensivo&quot;,&quot;link&quot;:&quot;6-parsing-defensivo&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;7. Recupera\u00e7\u00e3o e Robustez&quot;,&quot;text&quot;:&quot;7. Recupera\u00e7\u00e3o e Robustez&quot;,&quot;link&quot;:&quot;eb-table-content-39&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;8. Perda, Repeti\u00e7\u00e3o e Ordem&quot;,&quot;text&quot;:&quot;8. Perda, Repeti\u00e7\u00e3o e Ordem&quot;,&quot;link&quot;:&quot;eb-table-content-40&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;9. Tempo e Determinismo&quot;,&quot;text&quot;:&quot;9. Tempo e Determinismo&quot;,&quot;link&quot;:&quot;9-tempo-e-determinismo&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;10. Seguran\u00e7a e Confiabilidade&quot;,&quot;text&quot;:&quot;10. Seguran\u00e7a e Confiabilidade&quot;,&quot;link&quot;:&quot;eb-table-content-42&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;11. Testabilidade&quot;,&quot;text&quot;:&quot;11. Testabilidade&quot;,&quot;link&quot;:&quot;11-testabilidade&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;12. Maturidade do Sistema&quot;,&quot;text&quot;:&quot;12. Maturidade do Sistema&quot;,&quot;link&quot;:&quot;12-maturidade-do-sistema&quot;},{&quot;level&quot;:2,&quot;content&quot;:&quot;Regra Final&quot;,&quot;text&quot;:&quot;Regra Final&quot;,&quot;link&quot;:&quot;regra-final&quot;}]" data-visible="[true,true,true,true,true,true]" data-delete-headers="[{&quot;label&quot;:&quot;UART n\u00e3o \u00e9 uma porta: \u00e9 uma decis\u00e3o de arquitetura&quot;,&quot;value&quot;:&quot;uart-n\u00e3o-\u00e9-uma-porta-\u00e9-uma-decis\u00e3o-de-arquitetura&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;O erro cl\u00e1ssico: UART como \u201clinha de printf\u201d&quot;,&quot;value&quot;:&quot;o-erro-cl\u00e1ssico-uart-como-linha-de-printf&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;A mudan\u00e7a de mentalidade: UART como canal bruto&quot;,&quot;value&quot;:&quot;a-mudan\u00e7a-de-mentalidade-uart-como-canal-bruto&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;UART como espelho do engenheiro&quot;,&quot;value&quot;:&quot;uart-como-espelho-do-engenheiro&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Bufferiza\u00e7\u00e3o n\u00e3o \u00e9 opcional: \u00e9 o primeiro contrato com a realidade&quot;,&quot;value&quot;:&quot;bufferiza\u00e7\u00e3o-n\u00e3o-\u00e9-opcional-\u00e9-o-primeiro-contrato-com-a-realidade&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;O erro cl\u00e1ssico: processar dados no contexto da interrup\u00e7\u00e3o&quot;,&quot;value&quot;:&quot;o-erro-cl\u00e1ssico-processar-dados-no-contexto-da-interrup\u00e7\u00e3o&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;A abordagem correta: ISR m\u00ednima + buffer expl\u00edcito&quot;,&quot;value&quot;:&quot;a-abordagem-correta-isr-m\u00ednima-buffer-expl\u00edcito&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Consumir bytes \u00e9 diferente de interpretar mensagens&quot;,&quot;value&quot;:&quot;consumir-bytes-\u00e9-diferente-de-interpretar-mensagens&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Bufferiza\u00e7\u00e3o como base para escalabilidade&quot;,&quot;value&quot;:&quot;bufferiza\u00e7\u00e3o-como-base-para-escalabilidade&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Um crit\u00e9rio simples de maturidade&quot;,&quot;value&quot;:&quot;um-crit\u00e9rio-simples-de-maturidade&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Framing: bytes sem fronteiras n\u00e3o s\u00e3o mensagens&quot;,&quot;value&quot;:&quot;framing-bytes-sem-fronteiras-n\u00e3o-s\u00e3o-mensagens&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;O erro cl\u00e1ssico: confiar em delimitadores \u201chumanos\u201d&quot;,&quot;value&quot;:&quot;o-erro-cl\u00e1ssico-confiar-em-delimitadores-humanos&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Framing \u00e9 uma decis\u00e3o de protocolo, n\u00e3o de conveni\u00eancia&quot;,&quot;value&quot;:&quot;framing-\u00e9-uma-decis\u00e3o-de-protocolo-n\u00e3o-de-conveni\u00eancia&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Abordagem correta 1: marcador de in\u00edcio + comprimento&quot;,&quot;value&quot;:&quot;abordagem-correta-1-marcador-de-in\u00edcio-comprimento&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Abordagem correta 2: delimitador com escape (SLIP-like)&quot;,&quot;value&quot;:&quot;abordagem-correta-2-delimitador-com-escape-slip-like&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Framing define se o sistema sobrevive ao erro&quot;,&quot;value&quot;:&quot;framing-define-se-o-sistema-sobrevive-ao-erro&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Uma regra pr\u00e1tica&quot;,&quot;value&quot;:&quot;uma-regra-pr\u00e1tica&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Integridade e parsing defensivo: confiar em bytes \u00e9 ingenuidade&quot;,&quot;value&quot;:&quot;integridade-e-parsing-defensivo-confiar-em-bytes-\u00e9-ingenuidade&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;O erro cl\u00e1ssico: \u201cse chegou, \u00e9 v\u00e1lido\u201d&quot;,&quot;value&quot;:&quot;o-erro-cl\u00e1ssico-se-chegou-\u00e9-v\u00e1lido&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Integridade n\u00e3o \u00e9 luxo, \u00e9 requisito&quot;,&quot;value&quot;:&quot;integridade-n\u00e3o-\u00e9-luxo-\u00e9-requisito&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Exemplo correto: CRC expl\u00edcito no frame&quot;,&quot;value&quot;:&quot;exemplo-correto-crc-expl\u00edcito-no-frame&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Parsing defensivo: nunca confie no emissor&quot;,&quot;value&quot;:&quot;parsing-defensivo-nunca-confie-no-emissor&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;UART como superf\u00edcie de ataque (mesmo sem rede)&quot;,&quot;value&quot;:&quot;uart-como-superf\u00edcie-de-ataque-mesmo-sem-rede&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Regra de ouro&quot;,&quot;value&quot;:&quot;regra-de-ouro&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Recupera\u00e7\u00e3o, perda e por que sistemas reais n\u00e3o assumem perfei\u00e7\u00e3o&quot;,&quot;value&quot;:&quot;recupera\u00e7\u00e3o-perda-e-por-que-sistemas-reais-n\u00e3o-assumem-perfei\u00e7\u00e3o&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;O erro cl\u00e1ssico: assumir que \u201cn\u00e3o acontece\u201d&quot;,&quot;value&quot;:&quot;o-erro-cl\u00e1ssico-assumir-que-n\u00e3o-acontece&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Sistemas reais assumem falha como condi\u00e7\u00e3o normal&quot;,&quot;value&quot;:&quot;sistemas-reais-assumem-falha-como-condi\u00e7\u00e3o-normal&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Timeouts: o rel\u00f3gio tamb\u00e9m \u00e9 parte do protocolo&quot;,&quot;value&quot;:&quot;timeouts-o-rel\u00f3gio-tamb\u00e9m-\u00e9-parte-do-protocolo&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Re-sincroniza\u00e7\u00e3o expl\u00edcita&quot;,&quot;value&quot;:&quot;re-sincroniza\u00e7\u00e3o-expl\u00edcita&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Perda de pacotes n\u00e3o \u00e9 exce\u00e7\u00e3o&quot;,&quot;value&quot;:&quot;perda-de-pacotes-n\u00e3o-\u00e9-exce\u00e7\u00e3o&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;UART como transporte muda o jogo&quot;,&quot;value&quot;:&quot;uart-como-transporte-muda-o-jogo&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;A frase que resume tudo&quot;,&quot;value&quot;:&quot;a-frase-que-resume-tudo&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Checklist de Boas Pr\u00e1ticas \u2014 UART como Camada de Transporte&quot;,&quot;value&quot;:&quot;checklist-de-boas-pr\u00e1ticas-uart-como-camada-de-transporte&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;1. Modelo Mental e Arquitetura&quot;,&quot;value&quot;:&quot;1-modelo-mental-e-arquitetura&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;2. Interrup\u00e7\u00f5es (ISR)&quot;,&quot;value&quot;:&quot;2-interrup\u00e7\u00f5es-isr&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;3. Bufferiza\u00e7\u00e3o&quot;,&quot;value&quot;:&quot;3-bufferiza\u00e7\u00e3o&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;4. Framing&quot;,&quot;value&quot;:&quot;4-framing&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;5. Integridade&quot;,&quot;value&quot;:&quot;5-integridade&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;6. Parsing Defensivo&quot;,&quot;value&quot;:&quot;6-parsing-defensivo&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;7. Recupera\u00e7\u00e3o e Robustez&quot;,&quot;value&quot;:&quot;7-recupera\u00e7\u00e3o-e-robustez&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;8. Perda, Repeti\u00e7\u00e3o e Ordem&quot;,&quot;value&quot;:&quot;8-perda-repeti\u00e7\u00e3o-e-ordem&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;9. Tempo e Determinismo&quot;,&quot;value&quot;:&quot;9-tempo-e-determinismo&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;10. Seguran\u00e7a e Confiabilidade&quot;,&quot;value&quot;:&quot;10-seguran\u00e7a-e-confiabilidade&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;11. Testabilidade&quot;,&quot;value&quot;:&quot;11-testabilidade&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;12. Maturidade do Sistema&quot;,&quot;value&quot;:&quot;12-maturidade-do-sistema&quot;,&quot;isDelete&quot;:false},{&quot;label&quot;:&quot;Regra Final&quot;,&quot;value&quot;:&quot;regra-final&quot;,&quot;isDelete&quot;:false}]" data-smooth="true" data-top-offset=""><div class="eb-toc__list-wrap"><ul class='eb-toc__list'><li><a href="#eb-table-content-0">UART não é uma porta: é uma decisão de arquitetura</a><ul class='eb-toc__list'><li><a href="#eb-table-content-1">O erro clássico: UART como “linha de printf”</a><li><a href="#eb-table-content-2">A mudança de mentalidade: UART como canal bruto</a><li><a href="#uart-como-espelho-do-engenheiro">UART como espelho do engenheiro</a></li></ul><li><a href="#eb-table-content-4">Bufferização não é opcional: é o primeiro contrato com a realidade</a><ul class='eb-toc__list'><li><a href="#eb-table-content-5">O erro clássico: processar dados no contexto da interrupção</a><li><a href="#eb-table-content-6">A abordagem correta: ISR mínima + buffer explícito</a><li><a href="#eb-table-content-7">Consumir bytes é diferente de interpretar mensagens</a><li><a href="#eb-table-content-8">Bufferização como base para escalabilidade</a><li><a href="#eb-table-content-9">Um critério simples de maturidade</a></li></ul><li><a href="#eb-table-content-10">Framing: bytes sem fronteiras não são mensagens</a><ul class='eb-toc__list'><li><a href="#eb-table-content-11">O erro clássico: confiar em delimitadores “humanos”</a><li><a href="#eb-table-content-12">Framing é uma decisão de protocolo, não de conveniência</a><li><a href="#eb-table-content-13">Abordagem correta 1: marcador de início + comprimento</a><li><a href="#abordagem-correta-2-delimitador-com-escape-slip-like">Abordagem correta 2: delimitador com escape (SLIP-like)</a><li><a href="#framing-define-se-o-sistema-sobrevive-ao-erro">Framing define se o sistema sobrevive ao erro</a><li><a href="#eb-table-content-16">Uma regra prática</a></li></ul><li><a href="#eb-table-content-17">Integridade e parsing defensivo: confiar em bytes é ingenuidade</a><ul class='eb-toc__list'><li><a href="#eb-table-content-18">O erro clássico: “se chegou, é válido”</a><li><a href="#eb-table-content-19">Integridade não é luxo, é requisito</a><li><a href="#eb-table-content-20">Exemplo correto: CRC explícito no frame</a><li><a href="#parsing-defensivo-nunca-confie-no-emissor">Parsing defensivo: nunca confie no emissor</a><li><a href="#eb-table-content-22">UART como superfície de ataque (mesmo sem rede)</a><li><a href="#regra-de-ouro">Regra de ouro</a></li></ul><li><a href="#eb-table-content-24">Recuperação, perda e por que sistemas reais não assumem perfeição</a><ul class='eb-toc__list'><li><a href="#eb-table-content-25">O erro clássico: assumir que “não acontece”</a><li><a href="#eb-table-content-26">Sistemas reais assumem falha como condição normal</a><li><a href="#eb-table-content-27">Timeouts: o relógio também é parte do protocolo</a><li><a href="#eb-table-content-28">Re-sincronização explícita</a><li><a href="#eb-table-content-29">Perda de pacotes não é exceção</a><li><a href="#uart-como-transporte-muda-o-jogo">UART como transporte muda o jogo</a><li><a href="#a-frase-que-resume-tudo">A frase que resume tudo</a></li></ul><li><a href="#eb-table-content-32">Checklist de Boas Práticas — UART como Camada de Transporte</a><ul class='eb-toc__list'><li><a href="#1-modelo-mental-e-arquitetura">1. Modelo Mental e Arquitetura</a><li><a href="#eb-table-content-34">2. Interrupções (ISR)</a><li><a href="#eb-table-content-35">3. Bufferização</a><li><a href="#4-framing">4. Framing</a><li><a href="#5-integridade">5. Integridade</a><li><a href="#6-parsing-defensivo">6. Parsing Defensivo</a><li><a href="#eb-table-content-39">7. Recuperação e Robustez</a><li><a href="#eb-table-content-40">8. Perda, Repetição e Ordem</a><li><a href="#9-tempo-e-determinismo">9. Tempo e Determinismo</a><li><a href="#eb-table-content-42">10. Segurança e Confiabilidade</a><li><a href="#11-testabilidade">11. Testabilidade</a><li><a href="#12-maturidade-do-sistema">12. Maturidade do Sistema</a><li><a href="#regra-final">Regra Final</a></li></ul></ul></div></div></div></div></div>


<h2 class="wp-block-heading"><strong>UART não é uma porta: é uma decisão de arquitetura</strong></h2>



<p>Para a maioria dos engenheiros, o primeiro contato com um microcontrolador envolve ligar um conversor USB-UART, abrir um terminal serial e enviar caracteres. Essa experiência inicial cria um vício conceitual perigoso: tratar UART como se fosse uma “porta de texto”, algo próximo de um <code>printf()</code> com fio. Esse modelo mental funciona em <em>demos</em>, <em>provas de conceito</em> e exemplos de datasheet — mas ele quebra imediatamente quando o sistema passa a existir no mundo real.</p>



<p>UART não é uma porta, não é um periférico isolado e definitivamente não é um canal confiável por definição. UART é apenas um <strong>mecanismo físico de serialização de bits</strong>, sem noção de mensagens, pacotes, estados, erros ou intenção semântica. Tudo o que faz sentido — início, fim, significado, validade, recuperação — <strong>precisa ser projetado em cima dela</strong>. Quando isso não é feito, o sistema não “falha de vez”; ele falha de forma intermitente, imprevisível e difícil de depurar.</p>



<p>Um teste simples revela rapidamente o nível de maturidade de um firmware: <em>o que acontece quando um byte se perde?</em> Se a resposta for “nunca acontece” ou “o terminal não mostra”, o sistema já está arquiteturalmente comprometido. Em sistemas reais há ruído elétrico, interrupções concorrentes, buffers cheios, clocks ligeiramente desalinhados, resets parciais e firmware sendo atualizado em campo. UART expõe todas essas fragilidades sem piedade.</p>



<h3 class="wp-block-heading">O erro clássico: UART como “linha de printf”</h3>



<p>O padrão mais comum — e mais frágil — de uso de UART é este:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Abordagem frágil e não determinística
void loop(void) {
    char cmd&#91;32&#93;;
    scanf("%s", cmd);

    if (strcmp(cmd, "ON") == 0) {
        led_on();
    } else if (strcmp(cmd, "OFF") == 0) {
        led_off();
    }
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// &#x274c; Abordagem frágil e não determinística</span></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">loop</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">char</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">cmd</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #B48EAD">32</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">scanf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">%s</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">cmd</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">strcmp</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">cmd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ON</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">led_on</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">strcmp</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">cmd</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">OFF</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">led_off</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Esse código pressupõe coisas que <strong>não são garantidas pela UART</strong>:</p>



<ul class="wp-block-list">
<li>Que os dados chegam completos</li>



<li>Que não há bytes extras ou truncados</li>



<li>Que o buffer nunca estoura</li>



<li>Que o alinhamento entre transmissor e receptor é perfeito</li>



<li>Que ruído não existe</li>



<li>Que o tempo não importa</li>
</ul>



<p>Além disso, <code>scanf()</code> bloqueia a execução, mistura parsing com transporte, não detecta framing e não oferece nenhuma estratégia de recuperação. Funciona em bancada, falha em produção.</p>



<h3 class="wp-block-heading">A mudança de mentalidade: UART como <em>canal bruto</em></h3>



<p>Um sistema robusto parte do princípio oposto:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>UART não transporta comandos, transporta bytes.<br>Quem transporta mensagens é o protocolo que você desenha.</strong></p>
</blockquote>



<p>Isso muda completamente a arquitetura. Em vez de “ler comandos”, o firmware passa a:</p>



<ol class="wp-block-list">
<li><strong>Receber bytes de forma assíncrona</strong></li>



<li><strong>Bufferizar dados</strong></li>



<li><strong>Detectar início e fim de quadros</strong></li>



<li><strong>Validar integridade</strong></li>



<li><strong>Interpretar significado</strong></li>



<li><strong>Responder ou recuperar estado</strong></li>
</ol>



<p>Um primeiro passo correto é separar <strong>recepção</strong> de <strong>interpretação</strong>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> UART tratada como stream bruto
#define RX_BUFFER_SIZE 128

volatile uint8_t rx_buffer&#91;RX_BUFFER_SIZE&#93;;
volatile uint16_t rx_head = 0;

void USART_IRQHandler(void) {
    uint8_t byte = USART_ReadByte();
    rx_buffer&#91;rx_head++&#93; = byte;
    rx_head %= RX_BUFFER_SIZE;
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// &#x2705; UART tratada como stream bruto</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">RX_BUFFER_SIZE</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">128</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">volatile</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rx_buffer</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">RX_BUFFER_SIZE</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">volatile</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint16_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rx_head</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">USART_IRQHandler</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">byte</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">USART_ReadByte</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">rx_buffer</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">rx_head</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">&#93; </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">byte</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">rx_head</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">%=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">RX_BUFFER_SIZE</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Aqui não há “comandos”, apenas <strong>dados crus entrando no sistema</strong>. Isso é proposital. O firmware assume que os bytes podem estar incompletos, desalinhados ou corrompidos — e projeta em cima disso.</p>



<p>A partir desse ponto, tudo o que vier depois — framing, parsing, validação — deixa de ser improviso e passa a ser <strong>engenharia deliberada</strong>.</p>



<h3 class="wp-block-heading">UART como espelho do engenheiro</h3>



<p>UART é brutal porque ela não esconde nada. Não há retransmissão automática, não há ordenação garantida, não há controle de fluxo implícito. Se o sistema funciona bem sobre UART, ele provavelmente funcionará bem sobre SPI, CAN, RS-485, TCP ou rádio. Se ele só funciona quando tudo dá certo, então ele não funciona.</p>



<p>Projetar bem sobre UART é um divisor de águas. É o ponto em que o engenheiro deixa de “fazer firmware” e começa a <strong>construir sistemas distribuídos embarcados</strong>, capazes de sobreviver ao mundo real.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/><p>The post <a href="https://mcu.tec.br/protoclolos/uart-serial/uart-nao-e-porta-serial-como-projetar-protocolos-robustos-em-sistemas-embarcados/">UART não é Porta Serial: Como Projetar Protocolos Robustos em Sistemas Embarcados</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://mcu.tec.br/protoclolos/uart-serial/uart-nao-e-porta-serial-como-projetar-protocolos-robustos-em-sistemas-embarcados/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1102</post-id>	</item>
		<item>
		<title>Proteção Inteligente para Barramentos I²C: Como Resistores em Série e Diodos Schottky Aumentam a Confiabilidade de Sistemas Embarcados</title>
		<link>https://mcu.tec.br/protoclolos/i2c/protecao-inteligente-para-barramentos-i%c2%b2c-como-resistores-em-serie-e-diodos-schottky-aumentam-a-confiabilidade-de-sistemas-embarcados/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=protecao-inteligente-para-barramentos-i%25c2%25b2c-como-resistores-em-serie-e-diodos-schottky-aumentam-a-confiabilidade-de-sistemas-embarcados</link>
					<comments>https://mcu.tec.br/protoclolos/i2c/protecao-inteligente-para-barramentos-i%c2%b2c-como-resistores-em-serie-e-diodos-schottky-aumentam-a-confiabilidade-de-sistemas-embarcados/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Delfino]]></dc:creator>
		<pubDate>Thu, 04 Dec 2025 21:16:29 +0000</pubDate>
				<category><![CDATA[I2C]]></category>
		<guid isPermaLink="false">https://mcu.tec.br/?p=931</guid>

					<description><![CDATA[<p>A proteção de barramentos I²C em sistemas embarcados é essencial para garantir confiabilidade, estabilidade e segurança na comunicação entre microcontroladores e sensores distribuídos. Este artigo explica de forma didática e tecnicamente rigorosa como a combinação de resistores em série e diodos Schottky — aplicada individualmente em cada ramificação — pode evitar travamentos, reduzir interferências eletromagnéticas, limitar correntes de curto e desacoplar capacitâncias parasitas. A análise inclui fundamentos teóricos, modelagem elétrica, cálculos reais e recomendações de projeto. Ideal para engenheiros, técnicos e estudantes que buscam construir sistemas I²C mais robustos, especialmente em ambientes ruidosos, cabeamentos longos e aplicações industriais. Aprenda como dimensionar corretamente cada componente e adote práticas profissionais para aumentar a resiliência do seu barramento I²C.</p>
<p>The post <a href="https://mcu.tec.br/protoclolos/i2c/protecao-inteligente-para-barramentos-i%c2%b2c-como-resistores-em-serie-e-diodos-schottky-aumentam-a-confiabilidade-de-sistemas-embarcados/">Proteção Inteligente para Barramentos I²C: Como Resistores em Série e Diodos Schottky Aumentam a Confiabilidade de Sistemas Embarcados</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>O barramento I²C é amplamente utilizado em sistemas embarcados pela sua simplicidade, baixo custo e capacidade de interligar múltiplos dispositivos utilizando apenas duas linhas: <strong>SDA</strong> (dados) e <strong>SCL</strong> (clock). No entanto, apesar de sua popularidade, o I²C é um barramento sensível a ruído, capacitância de cabos, falhas de dispositivos e travamentos ocasionados por curtos em um único módulo. Em aplicações reais — especialmente quando vários sensores estão distribuídos em cabos longos, ramificações (“stubs”) ou em ambientes eletricamente ruidosos — confiar no I²C sem mecanismos adicionais de proteção pode comprometer a integridade do sistema inteiro.</p>



<p>Para mitigar esses problemas, diversos projetistas implementam técnicas de robustez que vão além do básico. Entre elas, destaca-se a combinação de <strong>resistores em série</strong> e <strong>diodos Schottky</strong>, aplicada individualmente em cada ramificação de módulo. Essa técnica protege contra curtos, limita corrente, reduz interferências eletromagnéticas, desacopla capacitâncias parasitas e, sobretudo, isola falhas de forma que <strong>nenhum módulo pode travar o barramento completo</strong>.</p>



<p>A topologia analisada neste artigo é ilustrada no circuito abaixo, no qual um <strong>Arduino Uno R3</strong> se comunica com quatro dispositivos I²C (U1 a U4). Cada dispositivo é conectado por meio de um resistor em série (R1 a R4) e um diodo Schottky (D1 a D4), formando uma estrutura de proteção individual por ramificação. Essa abordagem aumenta significativamente a confiabilidade do barramento, mesmo na presença de cabos longos, dispositivos de procedência variada ou ambientes sujeitos a picos eletromagnéticos.</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="476" src="https://mcu.tec.br/wp-content/uploads/2025/11/image-32-1024x476.png" alt="" class="wp-image-932" srcset="https://mcu.tec.br/wp-content/uploads/2025/11/image-32-1024x476.png 1024w, https://mcu.tec.br/wp-content/uploads/2025/11/image-32-300x139.png 300w, https://mcu.tec.br/wp-content/uploads/2025/11/image-32-768x357.png 768w, https://mcu.tec.br/wp-content/uploads/2025/11/image-32.png 1124w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading">Resistores em Série no Barramento I²C (R1–R4)</h2>



<p>Quando olhamos para o esquema, vemos que cada módulo I²C (U1, U2, U3, U4) não está ligado “direto” ao barramento SDA/SCL. Em vez disso, há sempre <strong>um resistor em série</strong> (R1, R2, R3, R4) entre o barramento principal e o pino correspondente do módulo.<br>Isso não é um mero detalhe: é uma escolha de projeto importante para <strong>proteção</strong>, <strong>estabilidade de sinal</strong> e <strong>robustez do sistema</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">1.1 Função básica: limitar corrente em falhas</h3>



<p>O I²C é um barramento “open-drain/open-collector”:</p>



<ul class="wp-block-list">
<li>Os dispositivos <strong>apenas puxam a linha para o nível baixo (0)</strong>.</li>



<li>O nível alto é obtido por meio dos <strong>resistores de pull-up</strong>.</li>
</ul>



<p>Se, por algum motivo, um módulo entrar em falha e <strong>curto-circuitar SDA ou SCL para GND</strong> (ou mesmo para VCC), sem resistor série a corrente seria limitada somente pela resistência interna dos transistores – o que pode facilmente danificar o módulo, o microcontrolador ou ambos.</p>



<p>Com o resistor série, a corrente máxima é aproximada por:</p>



<p>\[<br>I_{máx} \approx \frac{V_{CC}}{R_{série}}<br>\]



<p>Exemplo típico:</p>



<ul class="wp-block-list">
<li>\( V_{CC} = 5,\text{V} \)</li>



<li>\( R_{série} = 220,\Omega \)</li>
</ul>



<p>\[<br>I_{máx} = \frac{5}{220} \approx 22{,}7,\text{mA}<br>\]



<p>Esse valor é alto, mas <strong>muitíssimo menor</strong> do que dezenas ou centenas de miliampères que poderiam circular num curto direto.<br>Na prática, essa limitação:</p>



<ul class="wp-block-list">
<li>Protege o driver de saída do Arduino.</li>



<li>Impede que uma falha em um único módulo destrua o barramento todo.</li>



<li>Dá tempo para o sistema detectar o problema (via watchdog, varredura de dispositivos, etc.) sem dano físico imediato.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">1.2 Função de “damping”: redução de reflexões e ringing</h3>



<p>Fisicamente, o conjunto <strong>fio + trilha + entrada do CI</strong> se comporta como um sistema <strong>RLC distribuído</strong>, com:</p>



<ul class="wp-block-list">
<li>Resistência série (do fio e do driver)</li>



<li>Indutância parasita do cabo</li>



<li>Capacitância parasita entre condutores e para o terra</li>
</ul>



<p>Quando o driver muda o nível lógico (especialmente em bordas rápidas), o sinal pode “oscilar” (ringing), produzir overshoot e undershoot, ou mesmo reflexões ao longo do cabo.</p>



<p>O resistor em série aumenta a resistência vista pelo circuito e, em conjunto com a impedância característica do cabo ((Z_0)), ajuda a amortecer essas oscilações:</p>



<ul class="wp-block-list">
<li>sem resistor: \( R_{\text{total}} \approx R_{\text{driver}} \)</li>



<li>com resistor: \( R_{\text{total}} \approx R_{\text{driver}} + R_{série} \)</li>
</ul>



<p>Se fizermos:</p>



<p>\[<br>R_{\text{total}} ;\approx; Z_0<br>\]



<p>o sistema tende a ficar <strong>criticamento amortecido</strong>, minimizando reflexões.</p>



<p>Não é preciso calcular (Z_0) exatamente em projetos simples, mas entender a ideia é importante: <strong>o resistor em série está “matando” parte da energia de alta frequência</strong> que causaria problemas de integridade de sinal.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">1.3 Controle da constante de tempo RC por módulo</h3>



<p>Cada ramificação do barramento possui sua <strong>capacitância própria</strong>:</p>



<ul class="wp-block-list">
<li>Capacitância dos pinos SDA/SCL do CI</li>



<li>Capacitância do cabo local</li>



<li>Parasitismos de trilha e conector</li>
</ul>



<p>Sem resistor série, toda essa capacitância é vista diretamente pelo barramento.<br>Com o resistor série, criamos um pequeno filtro RC:</p>



<p>\[<br>\tau_{\text{ramo}} = R_{série} \cdot C_{\text{ramo}}<br>\]



<p>Exemplo:</p>



<ul class="wp-block-list">
<li>\( R_{série} = 220,\Omega \)</li>



<li>\( C_{\text{ramo}} = 20,\text{pF} \)</li>
</ul>



<p>\[<br>\tau_{\text{ramo}} = 220 \cdot 20\times10^{-12} = 4{,}4,\text{ns}<br>\]



<p>Esse valor é <strong>desprezível</strong> frente ao tempo de subida típico em I²C padrão (ordem de centenas de nanosegundos a microssegundos). Ou seja:</p>



<ul class="wp-block-list">
<li>Não atrapalha a velocidade de 100 kHz ou 400 kHz.</li>



<li>Ajuda a “desacoplar” capacitâncias de cada módulo, evitando que um único dispositivo com capacitância alta degrade todo o barramento.</li>
</ul>



<p>Na prática, escolhemos (R_{série}) num compromisso:</p>



<ul class="wp-block-list">
<li><strong>Grande o suficiente</strong> para limitar corrente e amortecer ringing.</li>



<li><strong>Pequeno o suficiente</strong> para não degradar o tempo de subida global do barramento.</li>
</ul>



<p>Valores usuais: <strong>100 Ω a 470 Ω</strong> por linha e por módulo.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">1.4 Efeito sobre o tempo de subida do barramento</h4>



<p>O tempo de subida do I²C é dominado pelos resistores de pull-up e pela <strong>capacitância total</strong> vista pelo barramento:</p>



<p>\[<br>t_r \approx 0{,}8473 \cdot R_{pullup} \cdot C_T<br>\]



<p>onde:</p>



<ul class="wp-block-list">
<li>\(C_T = C_{cabos} + \sum C_{entradas}\)</li>
</ul>



<p>Os resistores em série <strong>quase não alteram diretamente essa equação</strong>, pois o pull-up continua ligado ao barramento principal.<br>O que eles fazem é:</p>



<ul class="wp-block-list">
<li>Reduzir o quanto da capacitância de cada ramo é de fato “sentida” nas bordas rápidas.</li>



<li>Atuar como “isoladores suaves” entre o barramento comum e os módulos.</li>
</ul>



<p>Resultado prático: <strong>maior número de módulos e cabos</strong> podem ser usados sem violar os tempos de subida especificados para o modo de operação (Standard, Fast, etc.).</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">1.5 Justificativa da escolha de valor (exemplo 220 Ω)</h4>



<p>Vamos supor:</p>



<ul class="wp-block-list">
<li>I²C a 100 kHz</li>



<li>VCC = 5 V</li>



<li>Pull-up de 4,7 kΩ</li>



<li>Múltiplos módulos, cada um com 20–30 pF</li>



<li>Cabos com 100–200 pF no total</li>
</ul>



<p>Queremos:</p>



<ol class="wp-block-list">
<li><strong>Corrente segura em falha</strong>
<ul class="wp-block-list">
<li>Com 220 Ω, vimos que o máximo é ~23 mA em curto.</li>



<li>Muitos CIs aguentam picos dessa ordem sem queimar instantaneamente.</li>
</ul>
</li>



<li><strong>Damping razoável</strong>
<ul class="wp-block-list">
<li>220 Ω + resistência do driver (~20–30 Ω) já é suficiente para amortecer a maioria dos cabos curtos/médios.</li>
</ul>
</li>



<li><strong>Impacto mínimo na forma de onda em operação normal</strong>
<ul class="wp-block-list">
<li>O resistor só “aparece” quando há transição ou curto.</li>



<li>Durante nível estável, a queda de tensão é pequena, pois a corrente é baixa (corrente de pull-up).</li>
</ul>
</li>
</ol>



<p>Assim, o valor típico de <strong>220 Ω</strong> é um bom compromisso entre:</p>



<ul class="wp-block-list">
<li>Proteção</li>



<li>Integridade de sinal</li>



<li>Simplicidade de cálculo</li>



<li>Custo e disponibilidade (valor comercial padrão)</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Diodos Schottky de Proteção no Barramento I²C (D1–D4)</h2>



<p>Cada módulo I²C do circuito possui um diodo Schottky ligado entre o ramo individual do módulo e o barramento principal SDA/SCL.<br>O conjunto <strong>resistor série + diodo Schottky</strong> cria um mecanismo de proteção inteligente que aumenta a robustez do barramento, evitando travamentos e reduzindo efeitos de falhas.</p>



<p>Este capítulo explica em profundidade <strong>por que o diodo está ali</strong>, <strong>por que ele é do tipo Schottky</strong>, <strong>como ele se comporta eletricamente</strong>, e <strong>como sua escolha impacta a confiabilidade do sistema</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">2.1 Motivação principal: evitar que um módulo defeituoso derrube o barramento inteiro</h3>



<p>Em um barramento I²C padrão, se um único dispositivo entrar em falha e “grudar” SDA ou SCL em nível baixo (0 V), todo o sistema deixa de funcionar.<br>Isso é chamado de:</p>



<h4 class="wp-block-heading"><strong>bus hanging</strong> ou <strong>bus lock</strong></h4>



<p>O diodo Schottky foi colocado justamente para impedir isso.</p>



<p>Suponha que o módulo U2 trave seu SDA em 0 V (falha interna).<br>Sem o diodo:</p>



<ul class="wp-block-list">
<li>O 0 V se propaga diretamente para o barramento.</li>



<li>Todos os outros dispositivos ficam impedidos de enviar ou receber dados.</li>



<li>O Arduino perde o controle de todo o barramento.</li>
</ul>



<p>Com o diodo Schottky na configuração típica (ânodo no módulo, cátodo no barramento):</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>módulo ---- R ----+---- barramento
                       |
                      D (Schottky)
                       |
                   GND (via módulo)

</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">módulo</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">----</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">R</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">----+----</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">barramento</span></span>
<span class="line"><span style="color: #D8DEE9FF">                       </span><span style="color: #81A1C1">|</span></span>
<span class="line"><span style="color: #D8DEE9FF">                      </span><span style="color: #88C0D0">D</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">Schottky</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                       </span><span style="color: #81A1C1">|</span></span>
<span class="line"><span style="color: #D8DEE9FF">                   </span><span style="color: #88C0D0">GND</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">via</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">módulo</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>



<p>A linha do módulo pode cair a 0 V, mas o barramento só cai <strong>até a queda direta do diodo (~0,2 a 0,3 V)</strong>.</p>



<p>Como o nível “LOW” em I²C é aceito até aproximadamente:</p>



<p>\[<br>V_{IL} \leq 0{,}3 \cdot V_{CC}<br>\]



<p>E com Vcc=5 V:</p>



<p>\[<br>V_{IL} \leq 1{,}5,\text{V}<br>\]



<p>Então, se o barramento cair para apenas <strong>0,2 V</strong>, isso <em>ainda é um LOW válido</em>, e o master consegue recuperar o controle — especialmente se houver mecanismos como clock stretching ou re-varredura de dispositivos.</p>



<p>Em outras palavras:</p>



<h3 class="wp-block-heading"><strong>A falha fica confinada apenas ao ramo do módulo defeituoso, e não contamina todo o barramento.</strong></h3>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">2.2 Como o Schottky atua eletricamente no instante da falha</h2>



<p>Suponha:</p>



<ul class="wp-block-list">
<li>O módulo trava SDA em 0 V.</li>



<li>O barramento está em nível alto (puxado por Rpull-up, ~5 V).</li>



<li>A linha do módulo (lado depois do resistor) vai instantaneamente a 0 V.</li>
</ul>



<p>A diferença de tensão passa a ser:</p>



<p>\[<br>V_{barramento} &#8211; V_{ramo} = 5,\text{V}<br>\]



<p>Quando:</p>



<p>\[<br>V_{barramento} &#8211; V_{ramo} > V_f<br>\]



<p>Onde \(V_f\) (queda direta) é:</p>



<ul class="wp-block-list">
<li>~0,2–0,3 V (Schottky)</li>



<li>~0,6–0,7 V (diodo de silício comum)</li>
</ul>



<p>Assim que essa condição se estabelece, o diodo conduz <strong>imediatamente</strong>, drenando para o módulo grande parte da corrente que iria para o barramento — ou seja:</p>



<ul class="wp-block-list">
<li><strong>Protege o barramento</strong></li>



<li><strong>Protege os outros módulos</strong></li>



<li><strong>Minimiza a queda de tensão que o barramento sofre</strong></li>
</ul>



<p>O efeito prático é:</p>



<p>O barramento não vai a zero — fica “abaixo do ideal”, mas ainda dentro da faixa aceitável para LOW I²C.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">2.3 Por que Schottky e não diodos comuns?</h3>



<p>A escolha do Schottky é essencial por <strong>três características físicas</strong>:</p>



<h4 class="wp-block-heading">1) Baixíssima queda de tensão direta ((V_f))</h4>



<p>Enquanto um diodo de silício comum tem (V_f \approx 0{,}6) a 0,7 V:</p>



<p>\[<br>V_{f,\text{Si}} \approx 0.7,\text{V}<br>\]



<p>O Schottky tem:</p>



<p>\[<br>V_{f,\text{Schottky}} \approx 0.2\text{–}0.3,\text{V}<br>\]



<p>No contexto do I²C:</p>



<ul class="wp-block-list">
<li>Uma queda de 0,7 V derrubaria o barramento para valores próximos de LOW marginal.</li>



<li>Já 0,2 V mantém o barramento operando dentro das margens elétricas <strong>com total segurança</strong>.</li>
</ul>



<p>A norma I²C permite aproximadamente:</p>



<p>\[<br>V_{OL,max} = 0{,}4,\text{V}<br>\]



<p>Portanto, o Schottky é a única escolha que garante operação dentro da especificação.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">2) Corrente de saturação rápida e baixa capacitância</h4>



<p>Schottys possuem:</p>



<ul class="wp-block-list">
<li><strong>Capacitância muito baixa</strong><br>Importante para não prejudicar o tempo de subida SDA/SCL.</li>



<li><strong>Resposta extremamente rápida</strong><br>Essencial para proteger contra transientes.</li>
</ul>



<p>Em I²C:</p>



<ul class="wp-block-list">
<li>Bordas não são rápidas como SPI, mas ainda assim a transitória causada por ruído ou falha precisa ser drenada <strong>antes</strong> de causar erro de leitura ou travamento.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">3) Corrente reversa maior, mas controlável</h4>



<p>Schottkys têm corrente reversa (leakage) maior, porém esse valor é:</p>



<ul class="wp-block-list">
<li>Da ordem de µA</li>



<li>Bem pequeno comparado às correntes naturais de pull-up</li>



<li>Completamente seguro dentro da operação I²C</li>
</ul>



<p>O resistor série ajuda ainda a limitar qualquer efeito adverso dessa corrente reversa, tornando a combinação “R série + Schottky” ideal.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">2.4 Análise matemática do efeito do diodo sobre o barramento</h3>



<p>Suponha:</p>



<ul class="wp-block-list">
<li>Barramento em nível alto: (5,\text{V})</li>



<li>Módulo em falha em (0,\text{V})</li>



<li>Diodo conduz com (V_f = 0{,}25,\text{V})</li>



<li>Resistor série em 220 Ω</li>
</ul>



<p>A queda do barramento depende do divisor dinâmico formado por:</p>



<ul class="wp-block-list">
<li>Rpull-up</li>



<li>Resistência efetiva do diodo (muito menor)</li>



<li>Resistência série</li>
</ul>



<p>Quando o diodo conduz, ele apresenta uma resistência dinâmica (r_d) pequena (alguns ohms).<br>O barramento sente algo como um divisor:</p>



<p>\[<br>V_{barramento} \approx \frac{R_{pull-up}}{R_{pull-up} + r_d + R_{série}} \cdot V_{CC}<br>\]



<p>Se tomarmos, por exemplo:</p>



<ul class="wp-block-list">
<li>\(R_{pull-up} = 4700,\Omega\)</li>



<li>\(R_{série} = 220,\Omega\)</li>



<li>\(r_d = 5,\Omega\)</li>
</ul>



<p>Então:</p>



<p>\[<br>V_{barramento} = \frac{4700}{4700 + 220 + 5} \cdot 5<br>= \frac{4700}{4925} \cdot 5<br>\approx 4{,}77,\text{V}<br>\]



<p>O barramento praticamente <strong>não cai nada</strong> – a queda ocorre no ramo do módulo defeituoso.</p>



<p>Isso confirma matematicamente que:</p>



<p><strong>O Schottky mantém o barramento íntegro mesmo com falha em um dos dispositivos.</strong></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">2.5 Função adicional: proteção contra picos de tensão e ruído</h3>



<p>Cabos longos, EMI e ESD podem gerar picos de tensão:</p>



<p>\[<br>V_{transiente} > V_{CC}<br>\]



<p>O diodo Schottky, polarizado da forma como está, absorve excesso de tensão sempre que:</p>



<p>\[<br>V_{ramo} > V_{barramento} + V_f<br>\]



<p>Ou seja, ele atua também como:</p>



<ul class="wp-block-list">
<li><strong>Clamper</strong> contra sobretensão local</li>



<li><strong>Equalizador</strong> entre barramento e ramos</li>



<li><strong>Proteção coletiva</strong> contra ruído em qualquer módulo</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">2.6 Como o diodo interage com o resistor série</h2>



<p>O par <strong>R + Schottky</strong> forma um circuito de proteção complementar:</p>



<ol class="wp-block-list">
<li><strong>O resistor limita corrente.</strong></li>



<li><strong>O diodo define um caminho seguro de desvio.</strong></li>
</ol>



<p>Se o módulo entra em curto:</p>



<ul class="wp-block-list">
<li>O resistor impede correntes destrutivas.</li>



<li>O diodo impede o módulo de derrubar o barramento.</li>
</ul>



<p>Se há ruído ou transiente:</p>



<ul class="wp-block-list">
<li>O diodo absorve picos.</li>



<li>O resistor suaviza a energia e amortiza pulsos rápidos.</li>
</ul>



<p>Se há capacitância excessiva num módulo:</p>



<ul class="wp-block-list">
<li>O resistor reduz o impacto no barramento.</li>



<li>O diodo mantém a integridade do barramento caso a capacitância gere oscilações.</li>
</ul>



<p>Essa combinação é muito usada em:</p>



<ul class="wp-block-list">
<li>Barramentos I²C industriais</li>



<li>Sensores distribuídos</li>



<li>Sistemas robustos tolerantes a falhas</li>



<li>Aplicações automotivas de baixa velocidade</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Conclusão</h2>



<p>Os diodos Schottky desempenham três funções fundamentais:</p>



<ol class="wp-block-list">
<li><strong>Isolamento de falhas</strong> – evitando travamento total do barramento.</li>



<li><strong>Clamping de tensão</strong> – protegendo contra picos ou ruído.</li>



<li><strong>Proteção cooperativa</strong> com o resistor – mantendo o barramento íntegro.</li>
</ol>



<p>Sua escolha é motivada por:</p>



<ul class="wp-block-list">
<li>Baixa queda direta (essencial para I²C)</li>



<li>Alta velocidade</li>



<li>Baixa capacitância</li>



<li>Comportamento previsível em transientes</li>
</ul>



<p>Essa estratégia permite que o sistema opere com vários módulos, longos cabos e falhas eventuais, sem travamento nem danos físicos.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Modelagem do Sistema, Síntese Técnica e Diretrizes de Dimensionamento</h2>



<p>Tendo entendido separadamente o papel dos resistores em série e dos diodos Schottky, podemos agora consolidar o raciocínio em um modelo único que descreve matematicamente o comportamento do barramento, justificando cada escolha de componente e permitindo dimensionar adequadamente valores para aplicações reais. Este capítulo funciona como síntese técnica e guia de projeto.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">3.1 Modelo elétrico equivalente de cada ramificação</h3>



<p>Cada módulo I²C é visto pelo barramento principal através de três elementos:</p>



<ol class="wp-block-list">
<li><strong>R_série</strong> (220 Ω típico)</li>



<li><strong>Diodo Schottky</strong></li>



<li><strong>Capacitância parasita + carga do CI</strong></li>
</ol>



<p>Um modelo simplificado pode ser representado assim:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>      ┌───────────── Barramento SDA/SCL
      │
      R_série
      │
      +─── C_módulo (entrada CI)
      │
      D Schottky (ânodo no ramo, cátodo no barramento)
      │
   GND (referência do módulo)
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">      ┌───────────── </span><span style="color: #D8DEE9">Barramento</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SDA</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">SCL</span></span>
<span class="line"><span style="color: #D8DEE9FF">      │</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">R_série</span></span>
<span class="line"><span style="color: #D8DEE9FF">      │</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF">─── </span><span style="color: #88C0D0">C_módulo</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">entrada</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">CI</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">      │</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">D</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Schottky</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">ânodo</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">no</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ramo</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">cátodo</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">no</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">barramento</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">      │</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #88C0D0">GND</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">referência</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">do</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">módulo</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span></code></pre></div>



<p>Sob operação normal, o diodo fica <strong>reversamente polarizado</strong> e praticamente invisível.<br>A comunicação ocorre através do RC formado por:</p>



<p>\[<br>\tau = R_{série} \cdot C_{módulo}<br>\]



<p>Nas transições, esse pequeno RC desacopla a capacitância do módulo e melhora a integridade de sinal.</p>



<p>Sob falha, o diodo passa a conduzir, criando um caminho alternativo que impede o módulo de derrubar o barramento.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">3.2 Análise dos três estados possíveis</h3>



<h4 class="wp-block-heading">Estado 1 – Operação normal</h4>



<ul class="wp-block-list">
<li>Correntes muito pequenas fluem no ramo.</li>



<li>O resistor série é praticamente transparente.</li>



<li>O diodo fica bloqueado.</li>



<li>O tempo de subida é dominado por:</li>
</ul>



<p>\[<br>t_r = 0{,}8473 \cdot R_{pull-up} \cdot C_{total}<br>\]



<p>e <strong>não</strong> por R_série.</p>



<h4 class="wp-block-heading">Estado 2 – Transientes / ruído</h4>



<p>Se o ramo sofre sobretensão:</p>



<p>\[<br>V_{ramo} > V_{barramento} + V_f<br>\]



<p>O Schottky conduz e “equaliza” tensões, evitando picos, protegendo o CI e melhorando EMC.</p>



<p>Se o barramento sofre ruído negativo (undershoot), o resistor série limita a corrente e remove energia do pulso.</p>



<h4 class="wp-block-heading">Estado 3 – Falha em módulo (curto para GND)</h4>



<ul class="wp-block-list">
<li>A linha do ramo fica em 0 V.</li>



<li>O barramento tentaria cair a 0 V, mas o diodo entra na condução reversa:</li>
</ul>



<p>\[<br>V_{barramento} \approx V_f = 0{,}2 \sim 0{,}3 V<br>\]



<p>Esse valor ainda é considerado <strong>LOW válido</strong> pela especificação I²C.</p>



<p>O resistor série limita a corrente:</p>



<p>\[<br>I = \frac{V_{CC}}{R_{série}}<br>\]



<p>Impedindo danos e mantendo o barramento operável.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">3.3 A queda de tensão no barramento durante falha (análise mais precisa)</h3>



<p>Para entender exatamente o quanto o barramento abaixa em caso de curto, podemos modelar como um divisor entre:</p>



<ul class="wp-block-list">
<li>Rpull-up</li>



<li>Resistência dinâmica do diodo (r_d, típica 3 a 10 Ω)</li>



<li>R_série</li>
</ul>



<p>A tensão final é:</p>



<p>\[<br>V_{barr} \approx \frac{R_{pull-up}}{R_{pull-up}+R_{série}+r_d} \cdot V_{CC}<br>\]



<p>Exemplo realista:</p>



<ul class="wp-block-list">
<li>(R_{pull-up} = 4{,}7 k\Omega)</li>



<li>(R_{série} = 220 \Omega)</li>



<li>(r_d = 5 \Omega)</li>



<li>(V_{CC} = 5 V)</li>
</ul>



[<br>V_{barr} = \frac{4700}{4700 + 220 + 5} \cdot 5<br>\approx 4{,}77 V<br>]



<p>Ou seja:</p>



<p><strong>O barramento praticamente NÃO cai, mesmo com o módulo em curto!</strong></p>



<p>E esta é a beleza da topologia: fixamos a queda no <strong>ramo</strong>, não no barramento.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">3.4 Dimensão ideal dos resistores série</h3>



<p>Regras práticas:</p>



<h4 class="wp-block-heading">(1) Proteção contra falha</h4>



<p>Para limitar a corrente a menos de 25 mA no pior caso:</p>



<p>\[<br>R_{série} \geq \frac{V_{CC}}{0{,}025}<br>\]



<p>Para 5 V:</p>



<p>\[<br>R_{série} \geq 200\ \Omega<br>\]



<p>Por isso 220 Ω é um valor tão comum.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">(2) Impacto no tempo de subida</h3>



<p>Para que o resistor série não atrapalhe a velocidade do I²C (até 400 kHz):</p>



<p>\[<br>R_{série} \cdot C_{entrada} \ll t_r<br>\]



<p>Com:</p>



<ul class="wp-block-list">
<li>\(C_{entrada} \approx 15\text{–}30 pF\)</li>



<li>\(t_r \approx 300\text{–}1000 ns\)</li>
</ul>



<p>Então:</p>



<p>\[<br>R_{série} \ll \frac{t_r}{C_{entrada}}<br>\]



<p>Exemplo:</p>



<p>\[<br>\frac{300 ns}{30 pF} = 10 k\Omega<br>\]



<p>220 Ω está muito abaixo desse limite, logo <strong>não interfere no tempo de subida</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">(3) Redução de ringing</h4>



<p>Para cabos com:</p>



<ul class="wp-block-list">
<li>\(L \approx 500 nH\)</li>



<li>\(C \approx 50 pF/m\)</li>
</ul>



<p>Impedância característica aproximada:</p>



<p>\[<br>Z_0 \approx \sqrt{\frac{L}{C}}<br>\approx \sqrt{\frac{500 nH}{50 pF}}<br>\approx 100\ \Omega<br>\]



<p>O resistor série deve ser da mesma ordem de grandeza.<br>Como o driver já oferece ~20–40 Ω internos, usamos:</p>



<p>\[<br>R_{série} \approx 100\Omega &#8211; R_{driver}<br>\]



<p>E chegamos novamente a valores entre 100 e 220 Ω.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">3.5 Dimensionamento ideal do diodo Schottky</h3>



<p>Critérios:</p>



<h4 class="wp-block-heading">(1) Baixa queda direta (V_f)</h4>



<p>Deve atender:</p>



<p>\[<br>V_f &lt; V_{OL,max}<br>\]



<p>Na prática:</p>



<ul class="wp-block-list">
<li>I²C admite até 0,4 V para LOW</li>



<li>Schottky típico oferece 0,2–0,3 V</li>
</ul>



<h4 class="wp-block-heading">(2) Capacitância baixa</h4>



<p>Evitar impacto nas bordas:</p>



<p>\[<br>C_D &lt; 20\ \text{pF}<br>\]



<h4 class="wp-block-heading">(3) Corrente de pico segura</h4>



<p>Em falhas, a corrente é:</p>



<p>\[<br>I = \frac{V_{CC}}{R_{série}}<br>\approx 23 mA<br>\]



<p>O diodo deve suportar isso confortavelmente, com folga.</p>



<h4 class="wp-block-heading">(4) Tempo de resposta muito rápido</h4>



<p>Absorver transientes antes que eles atinjam os CIs.</p>



<p>Diodos recomendados:</p>



<ul class="wp-block-list">
<li>BAT54</li>



<li>1N5819 (para correntes maiores, mas capacitância mais alta)</li>



<li>RB751</li>



<li>PMEG2010</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">3.6 Comparação com outras técnicas de proteção</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Técnica</th><th>Protege contra curto?</th><th>Protege contra ruído?</th><th>Mantém barramento ativo?</th><th>Custo</th><th>Comentário</th></tr></thead><tbody><tr><td><strong>Somente pull-up</strong></td><td>Não</td><td>Não</td><td>Não</td><td>Baixo</td><td>Padrão básico, frágil</td></tr><tr><td><strong>Somente R série</strong></td><td>Parcial</td><td>Parcial</td><td>Não</td><td>Baixo</td><td>Não evita bus lock</td></tr><tr><td><strong>Somente Schottky</strong></td><td>Não</td><td>Sim</td><td>Parcial</td><td>Médio</td><td>Corrente pode ser alta demais</td></tr><tr><td><strong>R série + Schottky (este circuito)</strong></td><td><strong>Sim</strong></td><td><strong>Sim</strong></td><td><strong>Sim</strong></td><td>Médio</td><td>Melhor relação custo/benefício</td></tr><tr><td>Buffer I²C dedicado</td><td>Sim</td><td>Sim</td><td>Sim</td><td>Alto</td><td>Solução profissional para longas distâncias</td></tr><tr><td>Isolador digital</td><td>Sim</td><td>Sim</td><td>Sim</td><td>Muito alto</td><td>Aplicações industriais e alta EMC</td></tr></tbody></table></figure>



<p>A solução adotada no circuito analisado é uma das mais equilibradas para:</p>



<ul class="wp-block-list">
<li>projetos maker,</li>



<li>sensores distribuídos,</li>



<li>robótica,</li>



<li>telemetria,</li>



<li>automação residencial,</li>



<li>painéis e cabos longos.</li>
</ul>



<h2 class="wp-block-heading">Diretrizes Práticas de Projeto, Recomendações de Uso e Exemplos de Dimensionamento</h2>



<p>Este capítulo final reúne <strong>regras práticas</strong>, <strong>cenários de aplicação</strong> e <strong>exemplos concretos de cálculo</strong> para orientar o uso correto da topologia <em>resistor série + diodo Schottky</em> em projetos reais com barramento I²C.<br>Aqui é onde a teoria se traduz em procedimentos de engenharia aplicáveis ao dia a dia.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">4.1 Quando essa topologia é recomendada</h3>



<p>O uso de resistores série e diodos Schottky é extremamente útil quando o barramento I²C opera sob uma ou mais das seguintes condições:</p>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Múltiplos dispositivos distribuídos em cabos longos</h4>



<p>Tipicamente mais de 30–40 cm por ramificação.</p>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Ambientes com ruído elétrico significativo</h4>



<p>Como motores, solenoides, inversores, relés, rádios ou fontes chaveadas próximas.</p>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Alta confiabilidade é necessária</h4>



<p>Sistemas que <strong>não podem parar</strong> caso um único módulo apresente falha.</p>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Uso de sensores ou placas com qualidade variável</h4>



<p>Módulos de baixa procedência podem sofrer falhas de linha ou travamentos esporádicos.</p>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> A topologia física contém ramificações (“stubs”) no barramento</h4>



<p>Cada stub adiciona capacitância, reflexões e risco de travamento.</p>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Há troca frequente de módulos (plug and play)</h4>



<p>Evita danos causados por ligações incorretas ou conexões intermitentes.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">4.2 Quando <strong>não</strong> é necessário usar esta topologia</h3>



<p>A solução é exagero se:</p>



<ul class="wp-block-list">
<li>O barramento é muito curto (poucos centímetros).</li>



<li>O número de dispositivos é baixo (1 ou 2).</li>



<li>A aplicação é extremamente sensível ao tempo de borda (raríssimo em I²C ≤ 400 kHz).</li>



<li>Há buffers dedicados I²C (P82B96, TCA4311, PCA9515, etc.)</li>
</ul>



<p>Em tais casos, resistores série podem até trazer pequenas vantagens, mas não são indispensáveis.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">4.3 Guia completo para escolher o valor de <strong>R_série</strong></h3>



<p>A escolha correta depende de três fatores:<br>(1) corrente máxima aceitável,<br>(2) qualidade de sinal,<br>(3) capacitância do ramo.</p>



<h4 class="wp-block-heading">4.3.1 Critério de segurança (corrente de curto)</h4>



<p>Queremos:</p>



<p>\[<br>I_{máx} \le 25,mA<br>\]



<p>Logo:</p>



<p>\[<br>R_{série} \ge \frac{V_{CC}}{0,025}<br>\]



<p>Para:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>VCC</th><th>R_série mínimo</th></tr></thead><tbody><tr><td>3,3 V</td><td>≥ 132 Ω</td></tr><tr><td>5,0 V</td><td>≥ 200 Ω</td></tr></tbody></table></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">4.3.2 Critério de integridade de sinal (amortecimento)</h3>



<p>Se o cabo do ramo tem:</p>



<ul class="wp-block-list">
<li>Indutância típica: 500 nH/m</li>



<li>Capacitância típica: 40–60 pF/m</li>
</ul>



<p>Impedância característica aproximada:</p>



<p>\[<br>Z_0 \approx \sqrt{\frac{L}{C}} \approx 100,\Omega<br>\]



<p>O resistor deve ser próximo de:</p>



<p>\[<br>R_{série} + R_{driver} \approx Z_0<br>\]



<p>Como o driver do microcontrolador já tem ~20–50 Ω, adicionamos:</p>



<p>\[<br>R_{série} \approx 100 &#8211; R_{driver} \approx 100 &#8211; 30 = 70,\Omega<br>\]



<p>Mas como precisamos também do critério de segurança (≥200 Ω), optamos por:</p>



<h4 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Valor ideal: <strong>150 a 330 Ω</strong></h4>



<p>O valor clássico é <strong>220 Ω</strong> pelo excelente compromisso.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">4.3.3 Critério de atraso</h4>



<p>O resistor não deve comprometer o tempo de subida:</p>



[<br>R_{série} \cdot C_{ramo} \ll t_r<br>]



<p>Com I²C Standard Mode (t_r ≈ 1000 ns) e C=30 pF:</p>



[<br>R_{máx} \ll \frac{1000ns}{30pF} \approx 33k\Omega<br>]



<p>Ou seja, <strong>220 Ω está muito longe de prejudicar o desempenho</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">4.4 Guia completo para escolher o diodo Schottky</h3>



<p>Critérios fundamentais:</p>



<h4 class="wp-block-heading">1) Queda direta deve ser menor que o LOW permitido</h4>



<p>\[<br>V_f &lt; 0{,}4,V<br>\]



<p>Schottky típico: 0,2–0,3 V → atende perfeitamente.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">2) Capacitância baixa</h4>



<p>\[<br>C_D &lt; 20\ pF<br>\]



<p>Para não “roubar” corrente do pull-up e nem prejudicar bordas.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">3) Corrente de pico suficiente</h4>



<p>Suporta:</p>



<p>\[<br>I_{pico} \approx \frac{V_{CC}}{R_{série}} \approx 23,mA<br>\]



<p>Qualquer Schottky padrão SMD atende.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">4) Velocidade de comutação muito alta</h3>



<p>Diodos comuns sofrem com recuperação reversa (reverse recovery), causando:</p>



<ul class="wp-block-list">
<li>Overshoot</li>



<li>Oscilações</li>



<li>Correntes negativas</li>
</ul>



<p>O Schottky não apresenta recuperação reversa — ideal para I²C.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">4.4.1 Diodos recomendados</h4>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Modelo</th><th>Capacitância</th><th>V_f</th><th>Observação</th></tr></thead><tbody><tr><td><strong>BAT54</strong></td><td>10 pF</td><td>0.25 V</td><td>Melhor custo/benefício</td></tr><tr><td><strong>RB751</strong></td><td>5 pF</td><td>0.22 V</td><td>Baixíssima capacitância</td></tr><tr><td><strong>PMEG2010</strong></td><td>40 pF</td><td>0.24 V</td><td>Correntes maiores</td></tr><tr><td><strong>1N5819</strong></td><td>110 pF</td><td>0.3 V</td><td>Serve, mas capacitância maior</td></tr></tbody></table></figure>



<p>Para I²C, BAT54 é o mais popular.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">4.5 Exemplo completo de dimensionamento</h3>



<p>Sistema hipotético:</p>



<ul class="wp-block-list">
<li>4 módulos I²C (cada um com 25 pF)</li>



<li>1 metro de cabo por módulo (~50 pF/m)</li>



<li>Tensão de operação: 5 V</li>



<li>I²C a 100 kHz</li>
</ul>



<h4 class="wp-block-heading">1) Capacitância do ramo</h4>



<p>\[<br>C_{ramo} = 25pF + 50pF = 75pF<br>\]



<h4 class="wp-block-heading">2) Resistência série (critério de segurança)</h4>



<p>\[<br>R_{série} \ge \frac{5V}{25mA} = 200Ω<br>\]



<p>Escolhemos 220 Ω.</p>



<h4 class="wp-block-heading">3) Verificação de atraso:</h4>



<p>\[<br>\tau = R_{série} \cdot C_{ramo}<br>= 220 \cdot 75pF = 16,5ns<br>\]



<p>Muito menor que o limite (~1000 ns) → OK.</p>



<h4 class="wp-block-heading">4) Verificação de amortecimento:</h4>



<p>Impedância aproximada do cabo:</p>



<p>\[<br>Z_0 \approx 100Ω<br>\]



<p>Com Rdriver ≈ 30 Ω:</p>



<p>\[<br>R_{total} = R_{driver} + R_{série}<br>= 30Ω + 220Ω<br>= 250Ω<br>\]



<p>Acima de Z0 → subamortece levemente, o que é bom para ruído, sem risco de overshoot.</p>



<h4 class="wp-block-heading">5) Escolha do diodo</h4>



<p>Usamos BAT54:</p>



<ul class="wp-block-list">
<li>V_f = 0.24 V</li>



<li>C_D = 10 pF</li>



<li>Isup ≈ 200 mA (muito acima dos 23 mA de falha)</li>
</ul>



<h4 class="wp-block-heading">Resultado</h4>



<p>Sistema <strong>robusto</strong>, <strong>tolerante a falhas</strong>, com <strong>integridade de sinal excelente</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">4.6 Recomendações profissionais finais</h3>



<h5 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Sempre usar resistores série ao conectar vários módulos distribuídos</h5>



<p>Especialmente se os cabos forem longos ou de má qualidade.</p>



<h5 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Usar Schottky de baixa capacitância</h5>



<p>Evite modelos “gordos” como 1N5819 quando puder.</p>



<h5 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Para cabos muito longos (&gt;2 m), considerar buffers I²C</h5>



<p>Como PCA9515 ou TCA9517.</p>



<h5 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Evitar “estrelas” em alta velocidade</h5>



<p>Barramentos com muitos stubs podem exigir resistência maior.</p>



<h5 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Proteger fisicamente os cabos</h5>



<p>Amarrar os pares SCL/SDA juntos reduz acoplamento de ruído.</p>



<h5 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2714.png" alt="✔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Sempre medir a forma de onda com osciloscópio</h5>



<p>A validação visual das bordas é parte essencial do processo.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">4.7 Conclusão Final</h3>



<p>A arquitetura do circuito analisado — resistores em série combinados com diodos Schottky — representa uma solução elegante e eficiente para aumentar a robustez do barramento I²C sem elevar muito o custo ou a complexidade. Esse método protege contra:</p>



<ul class="wp-block-list">
<li>Curto nos módulos</li>



<li>Picos de tensão</li>



<li>Capacitância excessiva</li>



<li>Falhas internas de CI</li>



<li>Ruído em cabos longos</li>



<li>Travamentos (bus lock)</li>
</ul>



<p>E tudo isso com impacto mínimo no desempenho elétrico e no tempo de subida do barramento.</p><p>The post <a href="https://mcu.tec.br/protoclolos/i2c/protecao-inteligente-para-barramentos-i%c2%b2c-como-resistores-em-serie-e-diodos-schottky-aumentam-a-confiabilidade-de-sistemas-embarcados/">Proteção Inteligente para Barramentos I²C: Como Resistores em Série e Diodos Schottky Aumentam a Confiabilidade de Sistemas Embarcados</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://mcu.tec.br/protoclolos/i2c/protecao-inteligente-para-barramentos-i%c2%b2c-como-resistores-em-serie-e-diodos-schottky-aumentam-a-confiabilidade-de-sistemas-embarcados/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">931</post-id>	</item>
		<item>
		<title>Thermochron e Hygrochron iButtons: Guia Completo de Uso, Modelos e Exemplo de Data Logger com OneWire</title>
		<link>https://mcu.tec.br/sensores/thermochron-e-hygrochron-ibuttons-guia-completo-de-uso-modelos-e-exemplo-de-data-logger-com-onewire/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=thermochron-e-hygrochron-ibuttons-guia-completo-de-uso-modelos-e-exemplo-de-data-logger-com-onewire</link>
					<comments>https://mcu.tec.br/sensores/thermochron-e-hygrochron-ibuttons-guia-completo-de-uso-modelos-e-exemplo-de-data-logger-com-onewire/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Delfino]]></dc:creator>
		<pubDate>Sun, 30 Nov 2025 20:39:20 +0000</pubDate>
				<category><![CDATA[OneWire]]></category>
		<category><![CDATA[Sensores]]></category>
		<guid isPermaLink="false">https://mcu.tec.br/?p=925</guid>

					<description><![CDATA[<p>Os iButtons das famílias Thermochron e Hygrochron são data loggers compactos e robustos que registram temperatura — e umidade, no caso dos Hygrochron — utilizando o protocolo OneWire. Este artigo apresenta, de forma didática e acessível para iniciantes, os principais modelos disponíveis no mercado, suas faixas de operação, capacidade de memória e precisão. Explicamos como funcionam as missões de coleta, o armazenamento interno com timestamp e mostramos um exemplo prático de implementação em microcontroladores como ESP32 e STM32. Um guia ideal para quem deseja criar sistemas autônomos de monitoramento ambiental.</p>
<p>The post <a href="https://mcu.tec.br/sensores/thermochron-e-hygrochron-ibuttons-guia-completo-de-uso-modelos-e-exemplo-de-data-logger-com-onewire/">Thermochron e Hygrochron iButtons: Guia Completo de Uso, Modelos e Exemplo de Data Logger com OneWire</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></description>
										<content:encoded><![CDATA[<h1 class="wp-block-heading"><strong>Introdução aos Thermochron e Hygrochron iButtons</strong></h1>



<p>Os iButtons da família <strong>Thermochron</strong> e <strong>Hygrochron</strong> representam uma das formas mais simples e confiáveis de implementar <em>data loggers</em> (registradores de dados) em aplicações industriais, laboratoriais e logísticas. Fabricados originalmente pela Dallas Semiconductor (hoje Maxim Integrated/Analog Devices), esses dispositivos encapsulam sensores de <strong>temperatura</strong> — e no caso da linha Hygrochron, também <strong>umidade relativa</strong> — dentro de um invólucro metálico de aço inoxidável no formato de uma pequena moeda. Esse encapsulamento robusto protege o circuito interno contra choques mecânicos, poeira, umidade e variações ambientais, tornando-os especialmente adequados para monitoramento de longo prazo.</p>



<p>O grande diferencial dos iButtons é a filosofia de uso baseada em <strong>missões</strong>: o usuário configura o dispositivo com um intervalo de amostragem (por exemplo, a cada 5 minutos), define alarmes ou limites de operação e, a partir daí, o iButton registra automaticamente os dados no seu próprio armazenamento interno, com carimbo de tempo (timestamp). Quando a missão termina, os dados são lidos via <strong>OneWire</strong>, um barramento extremamente simples que utiliza apenas um fio de dados e o retorno de terra.</p>


<div class="wp-block-image">
<figure class="alignleft size-full"><img decoding="async" width="397" height="238" src="https://mcu.tec.br/wp-content/uploads/2025/11/image-29.png" alt="" class="wp-image-926" srcset="https://mcu.tec.br/wp-content/uploads/2025/11/image-29.png 397w, https://mcu.tec.br/wp-content/uploads/2025/11/image-29-300x180.png 300w, https://mcu.tec.br/wp-content/uploads/2025/11/image-29-240x145.png 240w" sizes="(max-width: 397px) 100vw, 397px" /></figure>
</div>


<p>Esses dispositivos são amplamente aplicados em monitoramento de transporte de alimentos e vacinas, controle ambiental de câmaras frias, rastreamento de temperatura em cadeias de suprimentos e até monitoramento de equipamentos industriais. A simplicidade do barramento OneWire permite integrá-los de forma prática em sistemas embarcados, como ESP32, STM32, AVR e outros microcontroladores populares.</p>



<p>A seguir, iniciaremos a apresentação dos principais modelos de Thermochron e suas características técnicas.</p>



<h1 class="wp-block-heading"><strong>Modelos de Thermochron e Hygrochron iButtons</strong></h1>



<p>A família Thermochron é composta por vários modelos, cada um projetado para faixas de temperatura, resoluções e capacidades de memória específicas. Essa diversidade permite selecionar o dispositivo ideal para diferentes condições ambientais, desde monitoramento de produtos refrigerados até aplicações de alta temperatura. Já a família Hygrochron incorpora, além do sensor térmico, um sensor de umidade relativa, voltado para monitoramento ambiental completo.</p>



<p>A tabela a seguir resume os modelos Thermochron mais comuns e suas principais especificações técnicas. Essas características influenciam diretamente a precisão do monitoramento e a quantidade de dados que podem ser armazenados em uma única missão.</p>



<p><strong>Modelos Thermochron:</strong></p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Modelo</th><th>Faixa de Temperatura</th><th>Precisão</th><th>Resolução</th><th>Memória (Amostras)</th></tr></thead><tbody><tr><td>DS1921G</td><td>-40°C a +85°C</td><td>±1°C de -30°C a +70°C</td><td>0.5°C</td><td>2048</td></tr><tr><td>DS1921H</td><td>+15°C a +46°C</td><td>±1°C</td><td>0.125°C</td><td>2048</td></tr><tr><td>DS1921Z</td><td>-5°C a +26°C</td><td>±1°C</td><td>0.125°C</td><td>2048</td></tr><tr><td>DS1922L</td><td>-40°C a +85°C</td><td>±0.5°C de -10°C a +65°C</td><td>0.5°C ou 0.0625°C</td><td>8192 ou 4096</td></tr><tr><td>DS1922T</td><td>0°C a +125°C</td><td>±0.5°C de +20°C a +75°C</td><td>0.5°C ou 0.0625°C</td><td>8192 ou 4096</td></tr><tr><td>DS1922E</td><td>+15°C a +140°C</td><td>±1.5°C de +110°C a +140°C</td><td>0.5°C ou 0.0625°C</td><td>8192 ou 4096</td></tr><tr><td>DS1925L</td><td>-40°C a +85°C</td><td>±0.5°C</td><td>0.5°C ou 0.0625°C</td><td>122k ou 61k</td></tr></tbody></table></figure>



<p>Os modelos <strong>DS1921</strong> são mais simples, voltados para aplicações gerais com baixa resolução e menor memória, enquanto a série <strong>DS1922</strong> já oferece maior resolução, precisão aprimorada e memória superior. O <strong>DS1925L</strong> representa uma geração mais recente, com impressionantes 122 mil amostras — ideal para longas missões.</p>



<p>A linha Hygrochron, representada principalmente pelo <strong>DS1923</strong>, registra simultaneamente temperatura e umidade relativa. Ele utiliza o mesmo encapsulamento e protocolo OneWire, facilitando a integração em sistemas existentes.</p>



<p>Com essa visão geral dos modelos, podemos avançar para o capítulo seguinte, onde explicaremos como esses dispositivos funcionam como data loggers na prática.</p>



<h1 class="wp-block-heading"><strong>Como os Thermochron e Hygrochron Funcionam como Data Loggers</strong></h1>



<p>O funcionamento dos Thermochron e Hygrochron como data loggers é baseado em um conceito simples e extremamente eficiente: <strong>a missão</strong>. Uma missão consiste em um conjunto de parâmetros configurados no dispositivo antes do início da coleta: intervalo de amostragem, limites de alarme, modo de operação de memória e eventuais ajustes de resolução. Após configurado, o iButton passa a registrar automaticamente os dados ambientais no seu armazenamento interno, sem depender de qualquer intervenção externa.</p>


<div class="wp-block-image">
<figure class="alignright size-full is-resized"><img decoding="async" width="275" height="183" src="https://mcu.tec.br/wp-content/uploads/2025/11/image-30.png" alt="" class="wp-image-927" style="width:451px;height:auto"/></figure>
</div>


<p>O microcontrolador interno do iButton registra cada amostra com um timestamp. Esse relógio interno pode ser sincronizado durante a inicialização da missão, garantindo que o histórico gerado possa ser analisado posteriormente de forma cronológica. A memória interna funciona como um buffer circular ou como uma memória linear, dependendo da configuração escolhida. No primeiro modo, quando a memória enche, os dados mais antigos são sobrescritos; já no modo linear, a gravação cessa ao atingir a capacidade total.</p>



<p>No caso dos modelos Hygrochron, os sensores de temperatura e umidade são registrados simultaneamente e armazenados de modo intercalado, mantendo a coerência temporal entre os dados. Essa característica os torna especialmente úteis em monitoramento ambiental em depósitos, galpões e transporte sensível.</p>



<p>A comunicação com o data logger é feita via barramento <strong>OneWire</strong>, que utiliza apenas um único fio de dados mais o retorno de terra. Toda a configuração e leitura dos dados é realizada por comandos trocados entre o microcontrolador hospedeiro (como um ESP32 ou STM32) e o iButton. O OneWire também fornece alimentação parasita para alguns modelos, eliminando a necessidade de fonte externa durante a comunicação.</p>



<p>Agora que entendemos como o processo de registro funciona, podemos seguir para o próximo capítulo, onde apresentaremos um exemplo de implementação prática com microcontrolador.</p>



<h1 class="wp-block-heading"><strong>Exemplo Prático: Criando um Data Logger com Thermochron/Hygrochron e Microcontroladores</strong></h1>



<p>Para demonstrar o uso dos iButtons Thermochron e Hygrochron em um sistema real, vamos considerar uma implementação baseada em microcontroladores populares como <strong>ESP32</strong> ou <strong>STM32</strong>. A lógica é essencialmente a mesma para qualquer plataforma, já que a comunicação ocorre por meio do protocolo <strong>OneWire</strong>, um barramento simples, robusto e amplamente suportado.</p>



<p>Nosso objetivo será apresentar um exemplo didático que:</p>



<ol class="wp-block-list">
<li>Detecta um iButton no barramento OneWire.</li>



<li>Inicia uma missão configurando o intervalo de amostragem.</li>



<li>Lê periodicamente o status do dispositivo.</li>



<li>Faz a leitura completa dos dados registrados.</li>
</ol>



<p>A seguir, apresento um pseudocódigo em estilo C, com foco na clareza para iniciantes, mas mantendo o rigor técnico necessário para compreensão do protocolo.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><strong>Pseudocódigo em C para um Data Logger com iButton</strong></h2>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#include "onewire.h"
#include "thermochron.h"
#include &lt;stdio.h>

// Exemplo: pino GPIO onde o OneWire está conectado
#define ONEWIRE_PIN 4

void app_main(void)
{
    onewire_bus_t ow;
    onewire_init(&amp;ow, ONEWIRE_PIN);

    uint8_t rom&#91;8&#93;;

    // Etapa 1: Procurar dispositivo no barramento
    if (!onewire_search(&amp;ow, rom)) {
        printf("Nenhum iButton encontrado.\n");
        return;
    }

    printf("iButton encontrado! ROM = ");
    for (int i = 0; i &lt; 8; i++) printf("%02X ", rom&#91;i&#93;);
    printf("\n");

    // Etapa 2: Iniciar missão
    thermochron_config_t config;
    config.sample_rate = 60;    // amostragem a cada 60 segundos
    config.start_delay = 0;     // iniciar imediatamente
    config.high_alarm = 30;     // alarme de temperatura alta (°C)
    config.low_alarm = 0;       // alarme de temperatura baixa (°C)

    if (!thermochron_start_mission(&amp;ow, rom, &amp;config)) {
        printf("Falha ao iniciar missão.\n");
        return;
    }

    printf("Missão iniciada com sucesso.\n");

    // Etapa 3: Aguardar coleta e verificar status
    while (1) {
        thermochron_status_t status;
        if (thermochron_read_status(&amp;ow, rom, &amp;status)) {
            printf("Amostras registradas: %d\n", status.samples);
            printf("Temperatura atual: %.2f°C\n", status.current_temp);
        }
        sleep(60);
    }

    // Etapa 4: Leitura completa da memória (após finalização)
    thermochron_log_t log;
    if (thermochron_read_log(&amp;ow, rom, &amp;log)) {
        for (int i = 0; i &lt; log.count; i++) {
            printf("T%d = %.2f°C\n", i, log.values&#91;i&#93;);
        }
    }
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">onewire.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">thermochron.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">stdio</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// Exemplo: pino GPIO onde o OneWire está conectado</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">4</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">app_main</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">onewire_bus_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ow</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">onewire_init</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ow</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #B48EAD">8</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Etapa 1: Procurar dispositivo no barramento</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #88C0D0">onewire_search</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ow</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">)) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Nenhum iButton encontrado.</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">iButton encontrado! ROM = </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">8</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">) </span><span style="color: #88C0D0">printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">%02X </span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">&#93;)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Etapa 2: Iniciar missão</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">thermochron_config_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">config</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">config</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">sample_rate</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">60</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">// amostragem a cada 60 segundos</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">config</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">start_delay</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">     </span><span style="color: #616E88">// iniciar imediatamente</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">config</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">high_alarm</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">30</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">     </span><span style="color: #616E88">// alarme de temperatura alta (°C)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">config</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">low_alarm</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">       </span><span style="color: #616E88">// alarme de temperatura baixa (°C)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #88C0D0">thermochron_start_mission</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ow</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">config</span><span style="color: #D8DEE9FF">)) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Falha ao iniciar missão.</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Missão iniciada com sucesso.</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Etapa 3: Aguardar coleta e verificar status</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">while</span><span style="color: #D8DEE9FF"> (</span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">thermochron_status_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">status</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">thermochron_read_status</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ow</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">status</span><span style="color: #D8DEE9FF">)) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Amostras registradas: %d</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">status</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">samples</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Temperatura atual: %.2f°C</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">status</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">current_temp</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">sleep</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">60</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Etapa 4: Leitura completa da memória (após finalização)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">thermochron_log_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">log</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">thermochron_read_log</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ow</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">log</span><span style="color: #D8DEE9FF">)) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">count</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">T%d = %.2f°C</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">log</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">values</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">&#93;)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><strong>Explicação do Código</strong></h2>



<p>O código é estruturado para ser facilmente adaptado a frameworks como ESP-IDF ou HAL da STMicroelectronics:</p>



<ul class="wp-block-list">
<li><strong>onewire_init()</strong> configura o GPIO no modo adequado para o barramento.</li>



<li><strong>onewire_search()</strong> encontra o iButton conectado e lê seu código ROM único de 64 bits.</li>



<li><strong>thermochron_start_mission()</strong> envia os comandos necessários para configurar a missão no Thermochron ou Hygrochron.</li>



<li><strong>thermochron_read_status()</strong> permite acompanhar o andamento da gravação, útil para sistemas embarcados que precisam exibir dados em tempo real.</li>



<li><strong>thermochron_read_log()</strong> extrai todos os valores armazenados ao final da missão.</li>
</ul>



<p>Esse modelo é suficiente para construir sistemas reais de aquisição ambiental, podendo ser ampliado com RTC externo, armazenamento em SD card ou envio remoto via MQTT/WiFi.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h1 class="wp-block-heading"><strong>Conclusão e Recomendações de Uso</strong></h1>



<p>Os Thermochron e Hygrochron iButtons representam uma solução extremamente prática, robusta e tecnicamente elegante para monitoramento ambiental em formato de <em>data logger</em>. Seu encapsulamento metálico, aliado ao protocolo OneWire, torna-os ideais para aplicações em que simplicidade de instalação, resistência física e confiabilidade são essenciais. A filosofia baseada em “missões” elimina a necessidade de supervisão contínua, permitindo que o dispositivo realize amostragens de maneira completamente autônoma ao longo de semanas ou meses.</p>



<p>A diversidade de modelos disponíveis permite escolher a solução exata para cada cenário: desde o DS1921, adequado a aplicações gerais e de baixo custo, até o DS1925L, com grande capacidade de memória para longas campanhas de medição. Para aplicações em que umidade também é um parâmetro crítico, o Hygrochron (como o DS1923) oferece monitoramento completo de ambiente em um único encapsulamento.</p>



<p>Do ponto de vista de implementação em sistemas embarcados, o barramento OneWire simplifica a conectividade e facilita a integração com plataformas como ESP32, STM32, AVR ou mesmo SBCs como Raspberry Pi. O exemplo apresentado demonstra que, com poucas funções de controle e leitura, é possível configurar uma missão, acompanhar sua execução e extrair os dados coletados de maneira ordenada e precisa.</p>



<p>Assim, seja para monitoramento de transporte de alimentos e vacinas, gestão ambiental de galpões, rastreamento térmico de equipamentos industriais ou projetos educacionais, os iButtons Thermochron e Hygrochron permanecem como ferramentas confiáveis e acessíveis para coleta de dados ambientais em escala compacta.</p><p>The post <a href="https://mcu.tec.br/sensores/thermochron-e-hygrochron-ibuttons-guia-completo-de-uso-modelos-e-exemplo-de-data-logger-com-onewire/">Thermochron e Hygrochron iButtons: Guia Completo de Uso, Modelos e Exemplo de Data Logger com OneWire</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://mcu.tec.br/sensores/thermochron-e-hygrochron-ibuttons-guia-completo-de-uso-modelos-e-exemplo-de-data-logger-com-onewire/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">925</post-id>	</item>
		<item>
		<title>Sistema de Ronda com OneWire e iButton DS1904 usando ESP32: Implementação Completa, Multithread e à Prova de Falhas</title>
		<link>https://mcu.tec.br/algoritimos/sistema-de-ronda-com-onewire-e-ibutton-ds1904-usando-esp32-implementacao-completa-multithread-e-a-prova-de-falhas/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=sistema-de-ronda-com-onewire-e-ibutton-ds1904-usando-esp32-implementacao-completa-multithread-e-a-prova-de-falhas</link>
					<comments>https://mcu.tec.br/algoritimos/sistema-de-ronda-com-onewire-e-ibutton-ds1904-usando-esp32-implementacao-completa-multithread-e-a-prova-de-falhas/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Delfino]]></dc:creator>
		<pubDate>Sun, 30 Nov 2025 20:08:26 +0000</pubDate>
				<category><![CDATA[Algoritimos]]></category>
		<category><![CDATA[OneWire]]></category>
		<category><![CDATA[Sensores]]></category>
		<guid isPermaLink="false">https://mcu.tec.br/?p=920</guid>

					<description><![CDATA[<p>Este artigo apresenta uma implementação completa e profissional de um sistema de ronda baseado no barramento OneWire e no iButton DS1904, utilizando o microcontrolador ESP32 com FreeRTOS. Explicamos de forma detalhada como funciona o protocolo OneWire, como construir um driver seguro com mutex e critical sections, como ler o ID único e o relógio interno do DS1904 e como integrar tudo em uma arquitetura multithread que inclui tasks de ronda e de buzina com sincronização via notificações. O sistema detecta atrasos acima de 10 minutos, aciona alarme e registra eventos de forma organizada. A modularização do código facilita expansão para múltiplos pontos, integração com backend e auditoria profissional. Uma solução robusta, didática e ideal para aplicações de segurança patrimonial e automação.</p>
<p>The post <a href="https://mcu.tec.br/algoritimos/sistema-de-ronda-com-onewire-e-ibutton-ds1904-usando-esp32-implementacao-completa-multithread-e-a-prova-de-falhas/">Sistema de Ronda com OneWire e iButton DS1904 usando ESP32: Implementação Completa, Multithread e à Prova de Falhas</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></description>
										<content:encoded><![CDATA[<h3 class="wp-block-heading">O que é o barramento OneWire e por que ele é interessante para sistemas de segurança</h3>



<p>O barramento OneWire é um padrão de comunicação serial desenvolvido originalmente pela Dallas Semiconductor (hoje Maxim Integrated / Analog Devices) que se destaca por um detalhe bem atraente para sistemas embarcados: ele usa apenas <strong>uma linha de dados</strong> mais o <strong>GND</strong> comum. Ou seja, em vez de precisar de vários fios como em SPI ou de duas linhas como em I²C, o OneWire permite que o microcontrolador converse com vários dispositivos usando apenas um único pino de E/S (GPIO) e um resistor de pull-up. Em aplicações como controle de acesso, identificação de pessoas e rondas de vigilância, essa simplicidade reduz custo, facilita instalação em campo e torna o sistema mais robusto na prática.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="640" height="240" src="https://mcu.tec.br/wp-content/uploads/2025/11/image-28.png" alt="" class="wp-image-922" srcset="https://mcu.tec.br/wp-content/uploads/2025/11/image-28.png 640w, https://mcu.tec.br/wp-content/uploads/2025/11/image-28-300x113.png 300w" sizes="(max-width: 640px) 100vw, 640px" /></figure>
</div>


<p>Do ponto de vista elétrico, o OneWire usa um <strong>barramento open-drain</strong> (ou open-collector): nenhum dispositivo “empurra” o sinal para nível alto; todos apenas puxam a linha para o nível baixo quando necessário. Quem mantém a linha em nível lógico alto é um <strong>resistor de pull-up</strong> ligado à alimentação (típico de 4,7 kΩ em 3,3 V ou 5 V, dependendo dos dispositivos). O ESP32, atuando como mestre do barramento, configura um GPIO em modo open-drain e coordena toda a comunicação, gerando os pulsos de tempo corretos para reset, escrita e leitura de bits, enquanto os dispositivos OneWire (os escravos) respondem em instantes bem definidos de tempo. Isso significa que o protocolo é extremamente sensível a temporização, e é por isso que, mais à frente, vamos falar em <strong>código thread-safe</strong> e no cuidado com interrupções e tasks do FreeRTOS.</p>



<p>Cada dispositivo OneWire possui um <strong>código ROM de 64 bits único no mundo</strong>, o que o torna perfeito para uso como “chave” de identificação. Os famosos iButtons são exatamente isso: pequenos “botões metálicos” encapsulando um CI OneWire, que pode armazenar dados e/ou fornecer um ID único. No nosso artigo vamos focar no <strong>DS1904</strong>, um iButton que reúne um <strong>RTC (relógio em tempo real)</strong> com interface OneWire. Ele não só permite identificar o vigilante pelo ID único, como também possibilita registrar horário de passagem nos pontos de ronda com alta confiabilidade temporal, algo ideal para sistemas de auditoria e segurança.</p>



<p>Na prática, o barramento OneWire funciona com uma relação bem clara de <strong>mestre–escravos</strong>: apenas o ESP32 inicia as comunicações, envia comandos e define as janelas de tempo em que os escravos podem responder. Os escravos nunca falam “sozinhos”; eles apenas respondem quando o mestre permite, o que simplifica o controle da linha e evita colisões. A comunicação é feita em “slots de tempo” bem definidos: o mestre puxa a linha para baixo por alguns microssegundos para escrever um bit 0 ou 1, ou cria uma janela de leitura onde o escravo pode, ou não, puxar a linha para baixo para indicar o valor do bit. Apesar de parecer complicado à primeira vista, esse esquema é bastante estável e, uma vez encapsulado em uma <strong>camada de driver bem escrita</strong>, o programador de aplicação passa a enxergar o barramento apenas como funções de “ler ROM”, “escrever comando”, “ler dados”, de forma bem amigável.</p>



<p>Em sistemas de segurança e ronda, a combinação <strong>“barramento simples + dispositivos robustos”</strong> é muito valiosa. Um iButton pode ser instalado em cada ponto de verificação (posto de ronda), o vigilante carrega seu “botão” pessoal (ou a central possui iButtons por ponto, dependendo da lógica do projeto) e o ESP32, ligado a um leitor OneWire em cada ponto, registra a passagem. Como o barramento permite cabos mais longos que protocolos como I²C (desde que respeitados alguns cuidados de impedância, resistência e capacitância de cabo, que discutiremos em capítulo específico), podemos ter um cabeamento relativamente extenso ligando diversos leitores ou uma placa central com vários canais.</p>



<p>Ao longo dos próximos capítulos, vamos sair dessa visão conceitual e entrar em detalhes práticos: primeiro sobre sinais e temporização do OneWire, depois sobre como implementar um <strong>driver OneWire thread-safe no ESP32</strong> usando FreeRTOS, em seguida como conversar especificamente com o <strong>iButton DS1904</strong>, e finalmente como integrar tudo em um <strong>sistema de ronda</strong> que dispara uma buzina se o vigilante demorar mais de 10 minutos para alcançar o próximo ponto, usando tasks independentes e <strong>notificações de tarefa (thread notify)</strong> para ativar e desativar o alarme.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Como funciona a comunicação OneWire (reset, presença, leitura e escrita)</h3>



<p>Para trabalhar de forma segura com o barramento OneWire, é essencial entender como a troca de dados acontece no nível elétrico e temporal. Mesmo que futuramente você utilize bibliotecas prontas, compreender esses detalhes permite ajustar o driver para cabos longos, vários dispositivos no barramento e cenários onde interrupções podem afetar a temporização — algo especialmente relevante no ESP32, que possui Wi-Fi, Bluetooth, tasks concorrentes e muitas interrupções.</p>



<p>A comunicação OneWire segue quatro operações fundamentais:</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">1. <strong>Reset Pulse (pulso de reset)</strong></h4>



<p>Toda transação começa com o mestre (ESP32) puxando a linha para baixo por aproximadamente <strong>480 µs</strong>. Este pulso longo serve como anúncio de que o mestre quer iniciar uma nova sessão no barramento.</p>



<p>Após soltar a linha, o mestre aguarda entre <strong>15 µs e 60 µs</strong>, período em que qualquer escravo presente deve puxar a linha para baixo por cerca de <strong>60 a 240 µs</strong>, sinalizando o chamado <strong>Presence Pulse</strong>.<br>Esse evento confirma que ao menos um dispositivo OneWire está conectado e pronto para receber comandos.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">2. <strong>Presence Pulse (pulso de presença)</strong></h4>



<p>Quando um dispositivo OneWire reconhece o pulso de reset, ele responde com um pulso curto de nível baixo, indicando que está disponível.<br>O mestre então prossegue enviando comandos.</p>



<p>Para o sistema de ronda, essa etapa é crítica: se o leitor de iButton não receber o Presence Pulse, significa que o vigilante não encostou o DS1904 corretamente — algo que a aplicação deve detectar.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">3. <strong>Escrita de Bits (Write 0 / Write 1)</strong></h4>



<p>A escrita funciona em <strong>slots de tempo</strong> de aproximadamente 60–70 µs:</p>



<ul class="wp-block-list">
<li><strong>Write 1:</strong><br>O mestre puxa a linha para baixo por <strong>6–10 µs</strong> e então libera.<br>O escravo interpreta isso como bit “1”.</li>



<li><strong>Write 0:</strong><br>O mestre mantém a linha baixa por <strong>60 µs</strong> completos.<br>O escravo interpreta isso como bit “0”.</li>
</ul>



<p>Note como o tempo determina tudo — qualquer jitter ou atraso pode corromper a comunicação.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">4. <strong>Leitura de Bits (Read Slot)</strong></h4>



<p>A leitura funciona assim:</p>



<ol class="wp-block-list">
<li>O mestre abaixa a linha por ~6 µs.</li>



<li>Libera a linha.</li>



<li>Após ~9 µs, ele amostra o valor lógico.</li>



<li>O escravo pode (ou não) puxar a linha para baixo para informar um “0”.</li>



<li>Se não fizer nada, a linha permanece em “1”.</li>
</ol>



<p>Essa janela precisa ser muito precisa. É aqui que uma implementação thread-safe no ESP32 faz toda a diferença — qualquer preempção pode quebrar a leitura.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Por que isso importa para o ESP32?</h3>



<p>O ESP32 é extremamente multitarefa:</p>



<ul class="wp-block-list">
<li>interrupções de Wi-Fi,</li>



<li>Bluetooth,</li>



<li>tarefas de FreeRTOS concorrentes,</li>



<li>watchdog timers.</li>
</ul>



<p>Se o código OneWire não for protegido, a temporização pode falhar e o DS1904 será mal interpretado.</p>



<p>Por isso:</p>



<ul class="wp-block-list">
<li>Usaremos <strong>gpio_set_level()</strong> com pino configurado em <strong>open-drain</strong>.</li>



<li>Desativaremos interrupções apenas dentro das janelas críticas.</li>



<li>Utilizaremos <strong>mutex</strong> para garantir thread safety.</li>



<li>Encapsularemos toda a temporização crítica em funções atômicas.</li>
</ul>



<p>Nos próximos capítulos você terá um driver completo e seguro.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Por que o OneWire é especialmente útil com o DS1904?</h3>



<p>O DS1904 combina duas características úteis:</p>



<ul class="wp-block-list">
<li><strong>ID único (ROM de 64 bits)</strong> — permite identificar quem está executando a ronda.</li>



<li><strong>RTC interno</strong> — registra horário da leitura, permitindo auditoria confiável.</li>
</ul>



<p>Quando o vigilante encosta o iButton no leitor, o sistema pode:</p>



<ol class="wp-block-list">
<li>Ler o ID → identifica o funcionário.</li>



<li>Ler o relógio interno → captura timestamp sólido.</li>



<li>Gravar ponto de ronda.</li>



<li>Resetar um timer de 10 minutos até o próximo ponto.</li>
</ol>



<p>Se o próximo ponto não for alcançado dentro do tempo, o sistema aciona uma buzina.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Construção de um Driver OneWire Seguro no ESP32: Mutex + Critical Sections (FreeRTOS)</h3>



<p>Para trabalhar corretamente com o barramento OneWire no ESP32 é preciso mais que simplesmente manipular GPIOs. O protocolo exige <strong>temporização em microssegundos extremamente estrita</strong>, e isso pode ser facilmente prejudicado pelo ambiente multitarefa do FreeRTOS, interrupções internas do chip (Wi-Fi/Bluetooth), watchdogs e trocas de contexto. Se o tempo de um pulso variar alguns microssegundos, uma leitura pode ser interpretada como “0” em vez de “1”, corrompendo toda a comunicação.</p>



<p>Por esse motivo, a abordagem mais robusta combina duas camadas de proteção:</p>



<ol class="wp-block-list">
<li><strong>Mutex (Semáforo de exclusão mútua)</strong><br>Impede que duas tasks tentem usar o barramento ao mesmo tempo.</li>



<li><strong>Critical Sections (taskENTER_CRITICAL)</strong><br>Desabilitam interrupções durante trechos de tempo crítico, garantindo que o código não seja interrompido bem no meio de um slot OneWire (que dura cerca de 60 µs).</li>
</ol>



<p>Usar apenas mutex não é suficiente, pois o FreeRTOS pode preemptar a task dentro da janela sensível do protocolo. Já usar apenas critical sections é perigoso, pois tasks podem interferir entre si. A solução mais segura é combinar os dois, garantindo simultaneamente <strong>exclusão mútua entre threads e preservação absoluta de temporização</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Arquitetura Geral do Driver</h2>



<p>Nosso driver OneWire para o ESP32 será composto por:</p>



<ul class="wp-block-list">
<li>inicialização do GPIO em modo <strong>open-drain</strong> (fundamental para barramentos OneWire);</li>



<li>um <strong>mutex global</strong> para proteger chamadas de alto nível;</li>



<li>um <strong>spinlock</strong> para controlar critical sections e impedir interrupções nos pulsos;</li>



<li>funções críticas (bit a bit) protegidas por critical sections;</li>



<li>funções de alto nível (reset, write byte, read byte) protegidas por mutex.</li>
</ul>



<p>Dessa forma, garantimos:</p>



<ul class="wp-block-list">
<li>estabilidade do protocolo mesmo com Wi-Fi ativo;</li>



<li>robustez quando várias tasks coexistem;</li>



<li>nenhum risco de corrupção por interrupções no meio da leitura;</li>



<li>compatibilidade com qualquer dispositivo OneWire, incluindo nosso foco: o iButton DS1904.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Código completo do driver OneWire (ESP32 + FreeRTOS, thread-safe e timing-safe)</h2>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "driver/gpio.h"
#include "esp32/rom/ets_sys.h"

#define ONEWIRE_PIN 4

// Mutex para exclusão mútua entre tasks
static SemaphoreHandle_t ow_mutex = NULL;

// Spinlock / mux para critical section (desabilita interrupções no core)
static portMUX_TYPE ow_spinlock = portMUX_INITIALIZER_UNLOCKED;

/**
 * @brief Inicializa o barramento OneWire no pino definido.
 *        Configura o GPIO como open-drain e cria o mutex.
 */
void onewire_init(void)
{
    if (ow_mutex == NULL) {
        ow_mutex = xSemaphoreCreateMutex();
    }

    gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL &lt;&lt; ONEWIRE_PIN),
        .mode = GPIO_MODE_INPUT_OUTPUT_OD,   // open-drain
        .pull_up_en = GPIO_PULLUP_DISABLE,   // usar pull-up EXTERNO
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE
    };
    gpio_config(&amp;io_conf);

    gpio_set_level(ONEWIRE_PIN, 1); // libera a linha
}

/**
 * @brief Envia pulso de reset e detecta presence pulse em modo protegido.
 *
 * @return true se algum dispositivo respondeu (presence detectado).
 */
bool onewire_reset(void)
{
    bool presence = false;

    // Garante que só uma task use o barramento por vez
    xSemaphoreTake(ow_mutex, portMAX_DELAY);

    // Região crítica: temporização de reset em microssegundos
    taskENTER_CRITICAL(&amp;ow_spinlock);

    // 1) Mestre puxa a linha para LOW por ~480µs
    gpio_set_level(ONEWIRE_PIN, 0);
    ets_delay_us(480);

    // 2) Libera a linha (pull-up externo leva a HIGH)
    gpio_set_level(ONEWIRE_PIN, 1);

    // 3) Aguarda 70µs e lê o presence
    ets_delay_us(70);
    presence = (gpio_get_level(ONEWIRE_PIN) == 0);

    // 4) Espera o restante da janela de reset
    ets_delay_us(410);

    taskEXIT_CRITICAL(&amp;ow_spinlock);

    xSemaphoreGive(ow_mutex);

    return presence;
}

/**
 * @brief Envia um único bit no barramento (slot de escrita).
 *        Protegido por critical section para manter temporização.
 */
static inline void onewire_write_bit(int bit)
{
    taskENTER_CRITICAL(&amp;ow_spinlock);

    // Início do slot: sempre puxa para LOW
    gpio_set_level(ONEWIRE_PIN, 0);

    if (bit) {
        // Write '1' – pulso curto LOW, depois libera
        ets_delay_us(6);
        gpio_set_level(ONEWIRE_PIN, 1);
        ets_delay_us(64);  // completa o slot (~70µs)
    } else {
        // Write '0' – mantém LOW por quase todo o slot
        ets_delay_us(60);
        gpio_set_level(ONEWIRE_PIN, 1);
        ets_delay_us(10);
    }

    taskEXIT_CRITICAL(&amp;ow_spinlock);
}

/**
 * @brief Lê um único bit do barramento (slot de leitura).
 *        Protegido por critical section para garantir a janela de amostragem.
 */
static inline int onewire_read_bit(void)
{
    int bit;

    taskENTER_CRITICAL(&amp;ow_spinlock);

    // Início do slot de leitura: mestre puxa para LOW rapidamente
    gpio_set_level(ONEWIRE_PIN, 0);
    ets_delay_us(6);

    // Libera linha para que o escravo possa responder
    gpio_set_level(ONEWIRE_PIN, 1);

    // Aguarda ~9µs até o ponto de amostragem
    ets_delay_us(9);
    bit = gpio_get_level(ONEWIRE_PIN);

    // Espera fim do slot (~55µs restantes)
    ets_delay_us(55);

    taskEXIT_CRITICAL(&amp;ow_spinlock);

    return bit;
}

/**
 * @brief Escreve um byte (8 bits LSB-first) no barramento.
 *        NÃO usa mutex nem critical direto – assume que quem chamar
 *        já está com o mutex travado. A temporização está dentro
 *        das funções de bit com critical section.
 */
static inline void onewire_write_byte_internal(uint8_t data)
{
    for (int i = 0; i &lt; 8; i++) {
        onewire_write_bit((data >> i) &amp; 0x01);
    }
}

/**
 * @brief Lê um byte (8 bits LSB-first) do barramento.
 *        Também assume mutex já travado.
 */
static inline uint8_t onewire_read_byte_internal(void)
{
    uint8_t val = 0;

    for (int i = 0; i &lt; 8; i++) {
        val |= (onewire_read_bit() &lt;&lt; i);
    }

    return val;
}

/**
 * @brief Função pública para escrever um byte no barramento
 *        com proteção de mutex (thread-safe).
 */
void onewire_write_byte(uint8_t data)
{
    xSemaphoreTake(ow_mutex, portMAX_DELAY);
    onewire_write_byte_internal(data);
    xSemaphoreGive(ow_mutex);
}

/**
 * @brief Função pública para ler um byte no barramento
 *        com proteção de mutex (thread-safe).
 */
uint8_t onewire_read_byte(void)
{
    xSemaphoreTake(ow_mutex, portMAX_DELAY);
    uint8_t r = onewire_read_byte_internal();
    xSemaphoreGive(ow_mutex);
    return r;
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">freertos/FreeRTOS.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">freertos/semphr.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">driver/gpio.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">esp32/rom/ets_sys.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">4</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// Mutex para exclusão mútua entre tasks</span></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">SemaphoreHandle_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ow_mutex</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NULL</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// Spinlock / mux para critical section (desabilita interrupções no core)</span></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">portMUX_TYPE</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ow_spinlock</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">portMUX_INITIALIZER_UNLOCKED</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #8FBCBB">brief</span><span style="color: #616E88"> Inicializa o barramento OneWire no pino definido.</span></span>
<span class="line"><span style="color: #616E88"> *        Configura o GPIO como open-drain e cria o mutex.</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_init</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">ow_mutex</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">ow_mutex</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">xSemaphoreCreateMutex</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">gpio_config_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">io_conf</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #D8DEE9">pin_bit_mask</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (1</span><span style="color: #D8DEE9">ULL</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #D8DEE9">mode</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">GPIO_MODE_INPUT_OUTPUT_OD</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">   </span><span style="color: #616E88">// open-drain</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #D8DEE9">pull_up_en</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">GPIO_PULLUP_DISABLE</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">   </span><span style="color: #616E88">// usar pull-up EXTERNO</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #D8DEE9">pull_down_en</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">GPIO_PULLDOWN_DISABLE</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #D8DEE9">intr_type</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">GPIO_INTR_DISABLE</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">gpio_config</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">io_conf</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">gpio_set_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// libera a linha</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #8FBCBB">brief</span><span style="color: #616E88"> Envia pulso de reset e detecta presence pulse em modo protegido.</span></span>
<span class="line"><span style="color: #616E88"> *</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #8FBCBB">return</span><span style="color: #616E88"> true se algum dispositivo respondeu (presence detectado).</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_reset</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">presence</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Garante que só uma task use o barramento por vez</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xSemaphoreTake</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ow_mutex</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">portMAX_DELAY</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Região crítica: temporização de reset em microssegundos</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">taskENTER_CRITICAL</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ow_spinlock</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 1) Mestre puxa a linha para LOW por ~480µs</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">gpio_set_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ets_delay_us</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">480</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 2) Libera a linha (pull-up externo leva a HIGH)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">gpio_set_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 3) Aguarda 70µs e lê o presence</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ets_delay_us</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">70</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">presence</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">gpio_get_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 4) Espera o restante da janela de reset</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ets_delay_us</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">410</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">taskEXIT_CRITICAL</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ow_spinlock</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xSemaphoreGive</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ow_mutex</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">presence</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #8FBCBB">brief</span><span style="color: #616E88"> Envia um único bit no barramento (slot de escrita).</span></span>
<span class="line"><span style="color: #616E88"> *        Protegido por critical section para manter temporização.</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">inline</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_write_bit</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">bit</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">taskENTER_CRITICAL</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ow_spinlock</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Início do slot: sempre puxa para LOW</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">gpio_set_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">bit</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Write &#39;1&#39; – pulso curto LOW, depois libera</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ets_delay_us</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">6</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">gpio_set_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ets_delay_us</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">64</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// completa o slot (~70µs)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Write &#39;0&#39; – mantém LOW por quase todo o slot</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ets_delay_us</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">60</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">gpio_set_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ets_delay_us</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">10</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">taskEXIT_CRITICAL</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ow_spinlock</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #8FBCBB">brief</span><span style="color: #616E88"> Lê um único bit do barramento (slot de leitura).</span></span>
<span class="line"><span style="color: #616E88"> *        Protegido por critical section para garantir a janela de amostragem.</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">inline</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_read_bit</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">bit</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">taskENTER_CRITICAL</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ow_spinlock</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Início do slot de leitura: mestre puxa para LOW rapidamente</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">gpio_set_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ets_delay_us</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">6</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Libera linha para que o escravo possa responder</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">gpio_set_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Aguarda ~9µs até o ponto de amostragem</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ets_delay_us</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">9</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">bit</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">gpio_get_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Espera fim do slot (~55µs restantes)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ets_delay_us</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">55</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">taskEXIT_CRITICAL</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">ow_spinlock</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">bit</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #8FBCBB">brief</span><span style="color: #616E88"> Escreve um byte (8 bits LSB-first) no barramento.</span></span>
<span class="line"><span style="color: #616E88"> *        NÃO usa mutex nem critical direto – assume que quem chamar</span></span>
<span class="line"><span style="color: #616E88"> *        já está com o mutex travado. A temporização está dentro</span></span>
<span class="line"><span style="color: #616E88"> *        das funções de bit com critical section.</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">inline</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_write_byte_internal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">8</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">onewire_write_bit</span><span style="color: #D8DEE9FF">((</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&gt;&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x01</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #8FBCBB">brief</span><span style="color: #616E88"> Lê um byte (8 bits LSB-first) do barramento.</span></span>
<span class="line"><span style="color: #616E88"> *        Também assume mutex já travado.</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">inline</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_read_byte_internal</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">val</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">8</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">val</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">|=</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">onewire_read_bit</span><span style="color: #D8DEE9FF">() </span><span style="color: #81A1C1">&lt;&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">val</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #8FBCBB">brief</span><span style="color: #616E88"> Função pública para escrever um byte no barramento</span></span>
<span class="line"><span style="color: #616E88"> *        com proteção de mutex (thread-safe).</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_write_byte</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xSemaphoreTake</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ow_mutex</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">portMAX_DELAY</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">onewire_write_byte_internal</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xSemaphoreGive</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ow_mutex</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">/**</span></span>
<span class="line"><span style="color: #616E88"> * </span><span style="color: #ECEFF4">@</span><span style="color: #8FBCBB">brief</span><span style="color: #616E88"> Função pública para ler um byte no barramento</span></span>
<span class="line"><span style="color: #616E88"> *        com proteção de mutex (thread-safe).</span></span>
<span class="line"><span style="color: #616E88"> */</span></span>
<span class="line"><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_read_byte</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xSemaphoreTake</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ow_mutex</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">portMAX_DELAY</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">r</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_read_byte_internal</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xSemaphoreGive</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ow_mutex</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">r</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Interpretação didática dessa arquitetura</h2>



<p>O driver acima entrega três garantias fundamentais:</p>



<h3 class="wp-block-heading">1. <strong>Ninguém interrompe uma operação OneWire</strong></h3>



<p>Ao usar <code>taskENTER_CRITICAL</code>, garantimos que nenhuma interrupção (Wi-Fi, Bluetooth, timers internos) vai alterar o tempo dos pulsos.<br>O protocolo OneWire depende de:</p>



<ul class="wp-block-list">
<li>6–10 µs para escrever “1”</li>



<li>60 µs para escrever “0”</li>



<li>janelas de amostragem entre 6 e 15 µs</li>
</ul>



<p>Qualquer preempção quebraria essas janelas.</p>



<h3 class="wp-block-heading">2. <strong>Nenhuma task concorre pelo barramento</strong></h3>



<p>O mutex impede que tasks paralelas afetem o barramento.<br>Exemplo:</p>



<ul class="wp-block-list">
<li>task de ronda lendo DS1904</li>



<li>task de diagnóstico testando presença</li>



<li>task de logs lendo ROM</li>
</ul>



<p>Sem mutex → desastre.</p>



<h3 class="wp-block-heading">3. <strong>Bloqueio apenas onde é realmente necessário</strong></h3>



<p>A critical section só envolve:</p>



<ul class="wp-block-list">
<li>write_bit</li>



<li>read_bit</li>



<li>reset</li>
</ul>



<p>Ou seja, trechos curtíssimos.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Comunicando com o iButton DS1904: Leitura do ID e do Relógio RTC</h3>



<p>Com o driver OneWire já estável, thread-safe e protegido contra variações de temporização, podemos finalmente trabalhar diretamente com o <strong>iButton DS1904</strong>, que é o “coração” do nosso sistema de ronda. Ele combina duas funções que interessam muito em segurança:</p>



<ol class="wp-block-list">
<li><strong>Identificação única (ROM de 64 bits)</strong><br>Cada DS1904 possui um código único mundialmente, usado para identificar o vigilante.</li>



<li><strong>RTC interno (Relógio de Tempo Real)</strong><br>Permite registrar o momento exato em que o vigilante passou pelo ponto de ronda, tornando o sistema auditável.</li>
</ol>



<p>O protocolo do DS1904 segue estritamente o padrão OneWire: a primeira etapa de qualquer comunicação é enviar um <strong>comando ROM</strong> (Match ROM, Read ROM, Skip ROM), seguido pelos <strong>comandos de função específicos do dispositivo</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">4.1 Estrutura da ROM de 64 bits</h2>



<p>O código ROM possui:</p>



<ul class="wp-block-list">
<li>8 bits: <strong>Family Code</strong></li>



<li>48 bits: <strong>Número de série</strong></li>



<li>8 bits: <strong>CRC</strong></li>
</ul>



<p>Para o DS1904, o <strong>Family Code é 0x24</strong>.</p>



<p>Esse ID é perfeito para uso como chave de autenticação do vigilante.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">4.2 Comandos ROM importantes</h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Comando</th><th>Código</th><th>Função</th></tr></thead><tbody><tr><td><strong>READ ROM</strong></td><td>0x33</td><td>Lê ID quando há apenas um dispositivo no barramento</td></tr><tr><td><strong>MATCH ROM</strong></td><td>0x55</td><td>Seleciona um dispositivo específico</td></tr><tr><td><strong>SKIP ROM</strong></td><td>0xCC</td><td>Usa quando há apenas um dispositivo no barramento</td></tr></tbody></table></figure>



<p>Para um sistema com leitor único por ponto de ronda, podemos usar o <strong>SKIP ROM</strong>, mas como vamos registrar vigilantes <em>diferentes</em>, utilizaremos preferencialmente o <strong>READ ROM</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">4.3 Comandos específicos do DS1904</h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Comando</th><th>Código</th><th>Descrição</th></tr></thead><tbody><tr><td><strong>READ CLOCK</strong></td><td>0x66</td><td>Lê o contador RTC (32 bits)</td></tr><tr><td><strong>WRITE CLOCK</strong></td><td>0x99</td><td>Ajusta o valor do clock</td></tr></tbody></table></figure>



<p>O RTC do DS1904 conta segundos desde um “epoch” interno do chip.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">4.4 Função: Ler o ID (ROM)</h2>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>bool ds1904_read_rom(uint8_t rom&#91;8&#93;)
{
    if (!onewire_reset()) return false;

    // Comando READ ROM
    onewire_write_byte(0x33);

    for (int i = 0; i &lt; 8; i++)
        rom&#91;i&#93; = onewire_read_byte();

    return true;
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ds1904_read_rom</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #B48EAD">8</span><span style="color: #D8DEE9FF">&#93;)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #88C0D0">onewire_reset</span><span style="color: #D8DEE9FF">()) </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Comando READ ROM</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">onewire_write_byte</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">0x33</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">8</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">&#93; </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_read_byte</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading">Explicação</h3>



<ol class="wp-block-list">
<li><strong>Reset</strong> – sincroniza barramento.</li>



<li><strong>READ ROM (0x33)</strong> – pede que o chip envie seu ID.</li>



<li>Le os 8 bytes de ID.</li>



<li>Guarda em <code>rom[8]</code>.</li>
</ol>



<p>Esse ID será usado para identificar qual vigilante tocou o iButton no ponto.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">4.5 Função: Selecionar um DS1904 pelo ID (MATCH ROM)</h2>



<p>Quando temos um único vigilante por ponto, ainda assim é bom garantir que estamos falando com o DS1904 correto, especialmente se o sistema crescer.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>bool ds1904_match_rom(uint8_t rom&#91;8&#93;)
{
    if (!onewire_reset()) return false;

    onewire_write_byte(0x55); // MATCH ROM

    for (int i = 0; i &lt; 8; i++)
        onewire_write_byte(rom&#91;i&#93;);

    return true;
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ds1904_match_rom</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #B48EAD">8</span><span style="color: #D8DEE9FF">&#93;)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #88C0D0">onewire_reset</span><span style="color: #D8DEE9FF">()) </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">onewire_write_byte</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">0x55</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// MATCH ROM</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">8</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">onewire_write_byte</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">&#93;)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">4.6 Função: Ler o relógio interno (RTC)</h2>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>bool ds1904_read_clock(uint32_t *clock_value)
{
    uint8_t rom&#91;8&#93;;

    // Identifica qual DS1904 está conectado
    if (!ds1904_read_rom(rom))
        return false;

    // Seleciona o dispositivo pelo ID
    if (!ds1904_match_rom(rom))
        return false;

    // Envia comando READ CLOCK
    onewire_write_byte(0x66);

    uint32_t val = 0;
    for (int i = 0; i &lt; 4; i++) {
        val |= ((uint32_t)onewire_read_byte()) &lt;&lt; (8 * i);
    }

    *clock_value = val;
    return true;
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ds1904_read_clock</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uint32_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">clock_value</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #B48EAD">8</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Identifica qual DS1904 está conectado</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #88C0D0">ds1904_read_rom</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Seleciona o dispositivo pelo ID</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #88C0D0">ds1904_match_rom</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Envia comando READ CLOCK</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">onewire_write_byte</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">0x66</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint32_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">val</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">4</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">val</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">|=</span><span style="color: #D8DEE9FF"> ((</span><span style="color: #D8DEE9">uint32_t</span><span style="color: #D8DEE9FF">)</span><span style="color: #88C0D0">onewire_read_byte</span><span style="color: #D8DEE9FF">()) </span><span style="color: #81A1C1">&lt;&lt;</span><span style="color: #D8DEE9FF"> (</span><span style="color: #B48EAD">8</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">clock_value</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">val</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading">Explicação detalhada</h3>



<ol class="wp-block-list">
<li>Primeiro lemos a ROM → identificamos o chip.</li>



<li>Depois usamos MATCH ROM → selecionamos explicitamente esse DS1904.</li>



<li>Enviamos <strong>READ CLOCK (0x66)</strong>.</li>



<li>O chip devolve 4 bytes LSB-first.</li>



<li>Montamos o valor de 32 bits.</li>
</ol>



<p>Esse valor é o timestamp interno do DS1904 (não é o mesmo epoch do Unix; a conversão vem depois).</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">4.7 Validação do vigilante</h2>



<p>O sistema pode ter uma tabela (em flash, EEPROM ou servidor) com os IDs autorizados.<br>Por exemplo:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>uint8_t vigilantes_autorizados[]&#91;8&#93; = {
    { 0x24, 0xA2, 0x17, 0x03, 0x4B, 0x00, 0x00, 0x9F },
    { 0x24, 0x98, 0xFF, 0x22, 0x17, 0x00, 0x00, 0xE4 }
};
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">vigilantes_autorizados</span><span style="color: #D8DEE9FF">[]&#91;</span><span style="color: #B48EAD">8</span><span style="color: #D8DEE9FF">&#93; </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x24</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0xA2</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x17</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x03</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x4B</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x00</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x00</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x9F</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x24</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x98</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0xFF</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x22</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x17</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x00</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x00</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0xE4</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<p>Após ler o ID da ROM, o sistema verifica:</p>



<ul class="wp-block-list">
<li>se o vigilante é autorizado,</li>



<li>se está no ponto correto,</li>



<li>registra horário,</li>



<li>notifica o sistema para reiniciar o cronômetro de 10 minutos.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Agora já podemos:</h3>



<ul class="wp-block-list">
<li><strong>Identificar o vigilante</strong> pelo ID único do DS1904.</li>



<li><strong>Registrar o horário exato</strong> usando o RTC interno.</li>



<li>Integrar isso com o sistema de ronda.</li>
</ul>



<h3 class="wp-block-heading">Sistema de Ronda Multithread: Tasks, Notificações e Buzina de Alarme (10 minutos)</h3>



<p>Agora vamos juntar tudo em uma aplicação prática com o ESP32 rodando FreeRTOS: um <strong>sistema de ronda</strong> em que o vigilante precisa se identificar com seu iButton DS1904 em cada ponto de verificação. Se ele demorar <strong>mais de 10 minutos</strong> entre um ponto e outro, o sistema aciona uma <strong>buzina de alarme</strong>.</p>



<p>A ideia central é usar:</p>



<ul class="wp-block-list">
<li><strong>Uma task para cuidar da ronda</strong> (leitura do DS1904, validação e registro);</li>



<li><strong>Uma task para controlar a buzina</strong>, baseada em <strong>notificações de tarefa</strong> (<code>task notifications</code>);</li>



<li>O <strong>driver OneWire thread-safe</strong> que você já viu (mutex + critical sections).</li>
</ul>



<p>Assim, cada “ação” importante fica em uma thread separada e bem definida:</p>



<ul class="wp-block-list">
<li>Task 1 → “Ronda”: lê o iButton (OneWire + DS1904) e, a cada passagem válida, <strong>notifica</strong> a task da buzina para reiniciar o “relógio” de 10 minutos.</li>



<li>Task 2 → “Buzina”: fica bloqueada esperando notificações; se não receber nenhuma por 10 minutos, considera atraso na ronda e <strong>liga a buzina</strong>, mantendo-a ligada até a próxima notificação.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">5.1 Conceito de Task Notification no FreeRTOS</h2>



<p>Em vez de usar filas ou timers de software, vamos usar as <strong>notificações de tarefa</strong>:</p>



<ul class="wp-block-list">
<li><code>ulTaskNotifyTake()</code> pode bloquear uma task <strong>até</strong>:
<ul class="wp-block-list">
<li>Receber uma notificação (de outra task), ou</li>



<li>Expirar um tempo máximo de espera (timeout).</li>
</ul>
</li>
</ul>



<p>Esse padrão encaixa perfeitamente no nosso requisito:</p>



<ul class="wp-block-list">
<li>“Se em 10 minutos <strong>não</strong> chegar uma nova notificação (ou seja, o vigilante não passou no próximo ponto), toca a buzina.”</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">5.2 Pinos de hardware</h2>



<p>Vamos supor:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#define ONEWIRE_PIN  4   // Já usado no driver OneWire
#define BUZZER_PIN   5   // Saída digital para a buzina
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ONEWIRE_PIN</span><span style="color: #D8DEE9FF">  </span><span style="color: #B48EAD">4</span><span style="color: #D8DEE9FF">   </span><span style="color: #616E88">// Já usado no driver OneWire</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">BUZZER_PIN</span><span style="color: #D8DEE9FF">   </span><span style="color: #B48EAD">5</span><span style="color: #D8DEE9FF">   </span><span style="color: #616E88">// Saída digital para a buzina</span></span>
<span class="line"></span></code></pre></div>



<p>A buzina pode ser acionada diretamente (se for de baixa corrente) ou via transistor / relé, dependendo do projeto.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">5.3 Inicialização da buzina</h2>



<p>Uma função simples para configurar o pino de saída:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>static void buzzer_init(void)
{
    gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL &lt;&lt; BUZZER_PIN),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE
    };
    gpio_config(&amp;io_conf);

    gpio_set_level(BUZZER_PIN, 0); // buzina inicialmente desligada
}

static void buzzer_on(void)
{
    gpio_set_level(BUZZER_PIN, 1);
}

static void buzzer_off(void)
{
    gpio_set_level(BUZZER_PIN, 0);
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">buzzer_init</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">gpio_config_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">io_conf</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #D8DEE9">pin_bit_mask</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> (1</span><span style="color: #D8DEE9">ULL</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">BUZZER_PIN</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #D8DEE9">mode</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">GPIO_MODE_OUTPUT</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #D8DEE9">pull_up_en</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">GPIO_PULLUP_DISABLE</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #D8DEE9">pull_down_en</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">GPIO_PULLDOWN_DISABLE</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #D8DEE9">intr_type</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">GPIO_INTR_DISABLE</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">gpio_config</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">io_conf</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">gpio_set_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">BUZZER_PIN</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// buzina inicialmente desligada</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">buzzer_on</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">gpio_set_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">BUZZER_PIN</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">buzzer_off</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">gpio_set_level</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">BUZZER_PIN</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">5.4 Handles das tasks</h2>



<p>Vamos declarar os handles globalmente:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>static TaskHandle_t buzzerTaskHandle = NULL;
static TaskHandle_t rondaTaskHandle  = NULL;
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">TaskHandle_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">buzzerTaskHandle</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NULL</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">TaskHandle_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rondaTaskHandle</span><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NULL</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<ul class="wp-block-list">
<li><code>buzzerTaskHandle</code>: necessário para que a task de ronda possa chamar <code>xTaskNotifyGive()</code> e sinalizar eventos de “passagem de ponto”.</li>



<li><code>rondaTaskHandle</code>: aqui não é estritamente necessário que outra task o notifique, mas deixamos declarado para extensões futuras (como supervisão externa, comandos remotos etc.).</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">5.5 Task da Buzina (esperando notificação ou timeout de 10 minutos)</h2>



<p>Essa task é o “relógio de segurança” do sistema:</p>



<ul class="wp-block-list">
<li>Ela espera receber uma notificação da task de ronda toda vez que o vigilante passa em um ponto;</li>



<li>Se <strong>10 minutos</strong> se passarem sem notificação, ela entende que houve atraso e aciona a buzina.</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>void buzzer_task(void *pvParameters)
{
    const TickType_t dez_minutos = pdMS_TO_TICKS(10 * 60 * 1000);

    buzzer_off();

    for (;;)
    {
        // Espera por uma notificação por até 10 minutos
        uint32_t notified = ulTaskNotifyTake(pdTRUE, dez_minutos);

        if (notified > 0) {
            // Recebeu notificação ANTES de 10 minutos:
            // significa que o vigilante passou em mais um ponto a tempo.
            // Garante que a buzina esteja desligada e reinicia o ciclo.
            buzzer_off();
            // volta ao início do loop e espera novamente até 10min
        } else {
            // Timeout: NÃO houve notificação em 10 minutos.
            // Considera atraso de ronda -> aciona buzina.
            buzzer_on();

            // Agora espera até a próxima notificação para desligar a buzina.
            ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
            buzzer_off();
            // Após desligar, volta ao loop e o ciclo recomeça (mais 10min)
        }
    }
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">buzzer_task</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">pvParameters</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">TickType_t</span><span style="color: #D8DEE9FF"> dez_minutos </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">pdMS_TO_TICKS</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">10</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">60</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1000</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">buzzer_off</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">;;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Espera por uma notificação por até 10 minutos</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">uint32_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">notified</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ulTaskNotifyTake</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">pdTRUE</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">dez_minutos</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">notified</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// Recebeu notificação ANTES de 10 minutos:</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// significa que o vigilante passou em mais um ponto a tempo.</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// Garante que a buzina esteja desligada e reinicia o ciclo.</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">buzzer_off</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// volta ao início do loop e espera novamente até 10min</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// Timeout: NÃO houve notificação em 10 minutos.</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// Considera atraso de ronda -&gt; aciona buzina.</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">buzzer_on</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// Agora espera até a próxima notificação para desligar a buzina.</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">ulTaskNotifyTake</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">pdTRUE</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">portMAX_DELAY</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">buzzer_off</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// Após desligar, volta ao loop e o ciclo recomeça (mais 10min)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p><strong>Leitura didática:</strong></p>



<ul class="wp-block-list">
<li><code>ulTaskNotifyTake(pdTRUE, dez_minutos)</code> bloqueia a task:
<ul class="wp-block-list">
<li>Se a task receber uma notificação antes de 10 minutos → <code>notified > 0</code>.</li>



<li>Se o tempo esgotar sem notificação → retorna 0 (timeout).</li>
</ul>
</li>



<li>Cada chamada a <code>xTaskNotifyGive(buzzerTaskHandle)</code> feita pela task de ronda indica:
<ul class="wp-block-list">
<li>“O vigilante passou em um ponto. Resetar o contador de 10 minutos e manter/desligar buzina.”</li>
</ul>
</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">5.6 Task da Ronda (leitura do DS1904 e reinício do timer de 10 minutos)</h2>



<p>A task de ronda faz:</p>



<ol class="wp-block-list">
<li>Fica monitorando o barramento OneWire esperando um DS1904 ser conectado ao leitor;</li>



<li>Quando detecta e lê um iButton:
<ul class="wp-block-list">
<li>Lê a ROM (ID do vigilante);</li>



<li>Lê o clock interno (timestamp);</li>



<li>Faz a validação/autenticação (pode ser uma checagem simples em memória ou em servidor);</li>



<li><strong>Notifica a task da buzina</strong> para resetar o contador de 10 minutos;</li>



<li>Registra log (por enquanto via <code>printf</code>, mas você pode depois mandar para flash, SD, servidor etc.).</li>
</ul>
</li>
</ol>



<p>Um exemplo simples:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>extern bool ds1904_read_rom(uint8_t rom&#91;8&#93;);
extern bool ds1904_read_clock(uint32_t *clock_value);
// As funções acima foram apresentadas no capítulo anterior.

void ronda_task(void *pvParameters)
{
    uint8_t  rom&#91;8&#93;;
    uint32_t clock_val;

    for (;;)
    {
        // Tenta detectar um DS1904 no barramento
        if (onewire_reset())
        {
            // Lê ID do vigilante
            if (ds1904_read_rom(rom))
            {
                // Lê o relógio interno
                if (ds1904_read_clock(&amp;clock_val))
                {
                    // Aqui você poderia validar o ID contra uma lista de vigilantes autorizados
                    // e registrar o horário de passagem deste ponto.
                    printf("Ronda: ID DS1904 = ");
                    for (int i = 0; i &lt; 8; i++) {
                        printf("%02X ", rom&#91;i&#93;);
                    }
                    printf(" | Clock = %u segundos\n", (unsigned)clock_val);

                    // Notifica a task da buzina: vigilante passou neste ponto
                    if (buzzerTaskHandle != NULL) {
                        xTaskNotifyGive(buzzerTaskHandle);
                    }

                    // Aguarda o vigilante remover o iButton antes de aceitar nova leitura
                    vTaskDelay(pdMS_TO_TICKS(2000));
                }
            }
        }
        else
        {
            // Nenhum DS1904 detectado no momento, espera um pouco e tenta de novo
            vTaskDelay(pdMS_TO_TICKS(500));
        }
    }
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">extern</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ds1904_read_rom</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #B48EAD">8</span><span style="color: #D8DEE9FF">&#93;)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">extern</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ds1904_read_clock</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uint32_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">clock_value</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #616E88">// As funções acima foram apresentadas no capítulo anterior.</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ronda_task</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">pvParameters</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #B48EAD">8</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">uint32_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">clock_val</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">;;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Tenta detectar um DS1904 no barramento</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">onewire_reset</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// Lê ID do vigilante</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">ds1904_read_rom</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">                </span><span style="color: #616E88">// Lê o relógio interno</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #88C0D0">ds1904_read_clock</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">clock_val</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">                    </span><span style="color: #616E88">// Aqui você poderia validar o ID contra uma lista de vigilantes autorizados</span></span>
<span class="line"><span style="color: #ECEFF4">                    </span><span style="color: #616E88">// e registrar o horário de passagem deste ponto.</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Ronda: ID DS1904 = </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">int</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">8</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">++</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">%02X </span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF">&#93;)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">printf</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C"> | Clock = %u segundos</span><span style="color: #EBCB8B">\n</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">unsigned</span><span style="color: #D8DEE9FF">)</span><span style="color: #D8DEE9">clock_val</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">                    </span><span style="color: #616E88">// Notifica a task da buzina: vigilante passou neste ponto</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">buzzerTaskHandle</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NULL</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">xTaskNotifyGive</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">buzzerTaskHandle</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">                    </span><span style="color: #616E88">// Aguarda o vigilante remover o iButton antes de aceitar nova leitura</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">vTaskDelay</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">pdMS_TO_TICKS</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">2000</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">else</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// Nenhum DS1904 detectado no momento, espera um pouco e tenta de novo</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">vTaskDelay</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">pdMS_TO_TICKS</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">500</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p><strong>Explicação didática:</strong></p>



<ul class="wp-block-list">
<li><code>onewire_reset()</code> → verifica se há um dispositivo OneWire presente.</li>



<li><code>ds1904_read_rom()</code> → obtém o ID do iButton (quem é o vigilante).</li>



<li><code>ds1904_read_clock()</code> → obtém o timestamp do RTC interno (quando ele passou).</li>



<li><code>xTaskNotifyGive(buzzerTaskHandle)</code> → informa à task da buzina: “recomece a contar 10 minutos a partir de agora; se ninguém passar até lá, dispare o alarme”.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">5.7 Função <code>app_main</code>: amarrando tudo</h2>



<p>Por fim, precisamos inicializar o driver OneWire, o pino da buzina e criar as tasks:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>void app_main(void)
{
    // Inicializa barramento OneWire (driver thread-safe)
    onewire_init();

    // Inicializa o pino da buzina
    buzzer_init();

    // Cria a task da buzina
    xTaskCreate(
        buzzer_task,
        "buzzer_task",
        2048,
        NULL,
        5,
        &amp;buzzerTaskHandle
    );

    // Cria a task da ronda
    xTaskCreate(
        ronda_task,
        "ronda_task",
        4096,
        NULL,
        6,              // prioridade ligeiramente maior que a da buzina, se desejar
        &amp;rondaTaskHandle
    );

    // Opcional: você poderia, aqui, enviar uma notificação inicial para a buzina
    // caso queira começar a contagem imediatamente após o sistema iniciar:
    // if (buzzerTaskHandle != NULL) {
    //     xTaskNotifyGive(buzzerTaskHandle);
    // }
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">app_main</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Inicializa barramento OneWire (driver thread-safe)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">onewire_init</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Inicializa o pino da buzina</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">buzzer_init</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Cria a task da buzina</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xTaskCreate</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">buzzer_task</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">buzzer_task</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #B48EAD">2048</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">NULL</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #B48EAD">5</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">buzzerTaskHandle</span></span>
<span class="line"><span style="color: #D8DEE9FF">    )</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Cria a task da ronda</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xTaskCreate</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">ronda_task</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ronda_task</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #B48EAD">4096</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">NULL</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #B48EAD">6</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">              </span><span style="color: #616E88">// prioridade ligeiramente maior que a da buzina, se desejar</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">rondaTaskHandle</span></span>
<span class="line"><span style="color: #D8DEE9FF">    )</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Opcional: você poderia, aqui, enviar uma notificação inicial para a buzina</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// caso queira começar a contagem imediatamente após o sistema iniciar:</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// if (buzzerTaskHandle != NULL) {</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">//     xTaskNotifyGive(buzzerTaskHandle);</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// }</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">5.8 Resumindo a lógica do sistema</h2>



<ul class="wp-block-list">
<li><strong>Quando o sistema inicia</strong>, a task da buzina começa esperando por uma notificação de “passagem de ponto”.</li>



<li><strong>Cada vez que o vigilante encosta o DS1904</strong>:
<ul class="wp-block-list">
<li>A task de ronda lê o ID e o relógio;</li>



<li>Registra o evento (log);</li>



<li>Chama <code>xTaskNotifyGive(buzzerTaskHandle)</code> → reinicia o cronômetro de 10 minutos na task da buzina.</li>
</ul>
</li>



<li><strong>Se em 10 minutos</strong> não houver <strong>nova</strong> notificação:
<ul class="wp-block-list">
<li><code>ulTaskNotifyTake()</code> volta por timeout;</li>



<li>A task da buzina chama <code>buzzer_on()</code>;</li>



<li>O alarme fica ligado até que o vigilante finalmente passe em algum ponto (causando nova notificação).</li>
</ul>
</li>
</ul>



<p>Assim, você obtém um sistema de ronda simples, mas bem estruturado:</p>



<ul class="wp-block-list">
<li>Driver OneWire robusto (mutex + critical section);</li>



<li>Integração com iButton DS1904 para identificação e horário;</li>



<li>Lógica de ronda baseada em tasks e notificações, sem necessidade de timers externos.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Modularizando o Sistema e Caminhos de Evolução</h3>



<p>Até aqui trabalhamos tudo em um único bloco de código para facilitar a compreensão passo a passo. Em um projeto real, porém, é importante separar responsabilidades em módulos bem definidos, com headers claros e arquivos <code>.c</code> independentes. Isso facilita manutenção, testes, reuso em outros projetos e, principalmente, evita que o código de aplicação (como a lógica da ronda) fique misturado com detalhes de baixo nível do barramento OneWire ou do DS1904.</p>



<p>Uma organização simples e já bem profissional poderia ser algo como:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>main/
  ├─ main.c          // app_main, criação das tasks
  ├─ onewire.c       // driver de barramento OneWire
  ├─ onewire.h
  ├─ ds1904.c        // funções específicas do iButton DS1904
  ├─ ds1904.h
  ├─ ronda.c         // lógica da ronda e task correspondente
  ├─ ronda.h
  ├─ buzzer.c        // controle da buzina e task
  └─ buzzer.h
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">main</span><span style="color: #81A1C1">/</span></span>
<span class="line"><span style="color: #D8DEE9FF">  ├─ </span><span style="color: #D8DEE9">main</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF">          </span><span style="color: #616E88">// app_main, criação das tasks</span></span>
<span class="line"><span style="color: #D8DEE9FF">  ├─ </span><span style="color: #D8DEE9">onewire</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF">       </span><span style="color: #616E88">// driver de barramento OneWire</span></span>
<span class="line"><span style="color: #D8DEE9FF">  ├─ </span><span style="color: #D8DEE9">onewire</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span></span>
<span class="line"><span style="color: #D8DEE9FF">  ├─ </span><span style="color: #D8DEE9">ds1904</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF">        </span><span style="color: #616E88">// funções específicas do iButton DS1904</span></span>
<span class="line"><span style="color: #D8DEE9FF">  ├─ </span><span style="color: #D8DEE9">ds1904</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span></span>
<span class="line"><span style="color: #D8DEE9FF">  ├─ </span><span style="color: #D8DEE9">ronda</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF">         </span><span style="color: #616E88">// lógica da ronda e task correspondente</span></span>
<span class="line"><span style="color: #D8DEE9FF">  ├─ </span><span style="color: #D8DEE9">ronda</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span></span>
<span class="line"><span style="color: #D8DEE9FF">  ├─ </span><span style="color: #D8DEE9">buzzer</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">c</span><span style="color: #D8DEE9FF">        </span><span style="color: #616E88">// controle da buzina e task</span></span>
<span class="line"><span style="color: #D8DEE9FF">  └─ </span><span style="color: #D8DEE9">buzzer</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span></span>
<span class="line"></span></code></pre></div>



<p>A ideia é que <code>main.c</code> apenas “monte o Lego”: chame inicializações, crie tasks e faça a cola entre os módulos, enquanto cada arquivo <code>.c</code> se concentra em uma responsabilidade única, com a interface pública declarada no respectivo <code>.h</code>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">6.1 – Interface do driver OneWire (<code>onewire.h</code> / <code>onewire.c</code>)</h4>



<p>O módulo <code>onewire</code> encapsula toda a complexidade do protocolo. A aplicação não precisa conhecer detalhes de slots de tempo, pulses de reset ou critical sections. Ela apenas chama funções como <code>onewire_reset()</code>, <code>onewire_write_byte()</code> e <code>onewire_read_byte()</code>. Um header enxuto e funcional poderia ser:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// onewire.h
#pragma once
#include &lt;stdbool.h>
#include &lt;stdint.h>

void onewire_init(void);
bool onewire_reset(void);
void onewire_write_byte(uint8_t data);
uint8_t onewire_read_byte(void);
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// onewire.h</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">pragma</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">once</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">stdbool</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">stdint</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_init</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_reset</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_write_byte</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">onewire_read_byte</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<p>Dentro de <code>onewire.c</code> fica tudo aquilo que já desenvolvemos: configuração do GPIO em open-drain, criação do mutex, uso do <code>portMUX_TYPE</code> e das critical sections, além das funções internas de leitura e escrita de bits. A aplicação vê um conjunto de funções simples, mas que internamente respeitam o timing e são thread-safe.</p>



<p>Essa separação também permite, futuramente, portar o driver para outro microcontrolador apenas reescrevendo <code>onewire.c</code> e mantendo o mesmo <code>onewire.h</code>, algo muito útil quando se pensa em bibliotecas reutilizáveis.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">6.2 – Interface específica do DS1904 (<code>ds1904.h</code> / <code>ds1904.c</code>)</h4>



<p>O DS1904 é apenas um dos muitos dispositivos OneWire possíveis. Por isso, faz sentido ter um módulo próprio para ele, que dependa de <code>onewire.h</code> mas esconda seus detalhes de comandos de função e formatação de dados. Um header típico poderia ser:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// ds1904.h
#pragma once
#include &lt;stdbool.h>
#include &lt;stdint.h>

#define DS1904_FAMILY_CODE 0x24

bool ds1904_read_rom(uint8_t rom&#91;8&#93;);
bool ds1904_match_rom(const uint8_t rom&#91;8&#93;);
bool ds1904_read_clock(uint32_t *clock_value);
bool ds1904_write_clock(uint32_t clock_value);
bool ds1904_is_family_ds1904(const uint8_t rom&#91;8&#93;);
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// ds1904.h</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">pragma</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">once</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">stdbool</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9">stdint</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">h</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">define</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">DS1904_FAMILY_CODE</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0x24</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ds1904_read_rom</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #B48EAD">8</span><span style="color: #D8DEE9FF">&#93;)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ds1904_match_rom</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #B48EAD">8</span><span style="color: #D8DEE9FF">&#93;)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ds1904_read_clock</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uint32_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">clock_value</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ds1904_write_clock</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uint32_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">clock_value</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ds1904_is_family_ds1904</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uint8_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rom</span><span style="color: #D8DEE9FF">&#91;</span><span style="color: #B48EAD">8</span><span style="color: #D8DEE9FF">&#93;)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<p>No <code>ds1904.c</code>, você implementa as funções usando a API do <code>onewire</code>. A função <code>ds1904_is_family_ds1904()</code> pode, por exemplo, verificar se o primeiro byte da ROM (family code) é igual a <code>0x24</code>, descartando dispositivos de outro tipo no mesmo barramento. As funções de leitura de clock seguem o padrão que já vimos: <code>onewire_reset()</code>, envio de comando ROM, envio do comando de função (<code>READ CLOCK</code>), leitura de 4 bytes e composição de um <code>uint32_t</code>.</p>



<p>Essa camada é onde você aplica toda a “semântica” do DS1904: saber que aqueles 4 bytes representam um contador de segundos desde um epoch específico, saber que o family code deve ser verificado, tratar eventuais erros de CRC se você desejar algo mais sofisticado, entre outros cuidados.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">6.3 – Módulo da buzina (<code>buzzer.h</code> / <code>buzzer.c</code>)</h4>



<p>O controle da buzina também merece um módulo próprio, mesmo sendo algo aparentemente simples. No header, você declara a interface tanto para inicialização quanto para a task de monitoramento:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// buzzer.h
#pragma once
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

extern TaskHandle_t buzzerTaskHandle;

void buzzer_init(void);
void buzzer_on(void);
void buzzer_off(void);
void buzzer_task(void *pvParameters);
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// buzzer.h</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">pragma</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">once</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">freertos/FreeRTOS.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">freertos/task.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">extern</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">TaskHandle_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">buzzerTaskHandle</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">buzzer_init</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">buzzer_on</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">buzzer_off</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">buzzer_task</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">pvParameters</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<p>No <code>buzzer.c</code> ficam a configuração do GPIO, as funções <code>buzzer_on()</code> e <code>buzzer_off()</code> e a <code>buzzer_task()</code> com a lógica da notificação e do timeout de 10 minutos. Desse modo, qualquer outro ponto do código que queira acionar ou garantir que a buzina esteja desligada não precisa saber qual pino é usado ou como a task está implementada. Basta chamar as funções declaradas no header.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">6.4 – Módulo de ronda (<code>ronda.h</code> / <code>ronda.c</code>)</h4>



<p>A lógica de negócio da ronda também deve ficar isolada em um módulo: é ele que sabe o que significa “passar no ponto de verificação”, como validar o DS1904, como registrar o horário e como notificar a buzina.</p>



<p>Um header simples poderia ser:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// ronda.h
#pragma once
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

extern TaskHandle_t rondaTaskHandle;

void ronda_task(void *pvParameters);
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// ronda.h</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">pragma</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">once</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">freertos/FreeRTOS.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">freertos/task.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">extern</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">TaskHandle_t</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">rondaTaskHandle</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ronda_task</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">*</span><span style="color: #D8DEE9">pvParameters</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<p>Dentro de <code>ronda.c</code> você inclui <code>ds1904.h</code> e implementa a lógica que já desenhamos: loop contínuo, tentativa de detecção de iButton, leitura de ROM e clock, validação de ID (por enquanto em tabela fixa, futuramente em configuração externa), registro em log e chamada a <code>xTaskNotifyGive(buzzerTaskHandle)</code> sempre que uma passagem for considerada válida. Essa divisão deixa claro que a task de ronda é uma “task de aplicação”, enquanto <code>onewire</code> e <code>ds1904</code> são camadas de infraestrutura.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">6.5 – Arquivo principal do projeto (<code>main.c</code>)</h4>



<p>Com todos os módulos prontos, <code>main.c</code> se torna bastante simples, o que é uma boa característica em sistemas embarcados bem estruturados. Ele passa a ser essencialmente um “ponto de entrada” que faz a orquestração inicial:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// main.c
#include "onewire.h"
#include "ds1904.h"
#include "buzzer.h"
#include "ronda.h"

void app_main(void)
{
    onewire_init();
    buzzer_init();

    xTaskCreate(
        buzzer_task,
        "buzzer_task",
        2048,
        NULL,
        5,
        &amp;buzzerTaskHandle
    );

    xTaskCreate(
        ronda_task,
        "ronda_task",
        4096,
        NULL,
        6,
        &amp;rondaTaskHandle
    );

    // Se desejar iniciar já contando 10 minutos a partir do boot:
    // if (buzzerTaskHandle != NULL) {
    //     xTaskNotifyGive(buzzerTaskHandle);
    // }
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// main.c</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">onewire.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ds1904.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">buzzer.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">#</span><span style="color: #D8DEE9">include</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ronda.h</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">app_main</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">void</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">onewire_init</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">buzzer_init</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xTaskCreate</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">buzzer_task</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">buzzer_task</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #B48EAD">2048</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">NULL</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #B48EAD">5</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">buzzerTaskHandle</span></span>
<span class="line"><span style="color: #D8DEE9FF">    )</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">xTaskCreate</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">ronda_task</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ronda_task</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #B48EAD">4096</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #D8DEE9">NULL</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #B48EAD">6</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9">rondaTaskHandle</span></span>
<span class="line"><span style="color: #D8DEE9FF">    )</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Se desejar iniciar já contando 10 minutos a partir do boot:</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// if (buzzerTaskHandle != NULL) {</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">//     xTaskNotifyGive(buzzerTaskHandle);</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// }</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Perceba como <code>app_main()</code> não sabe nada de detalhes de pulso OneWire, nem de como o DS1904 lê o clock, tampouco de como a buzina é acionada em nível elétrico. Ele apenas chama funções em uma sequência lógica. Essa é uma característica importante de uma boa arquitetura: cada camada conhece apenas o necessário da camada imediatamente abaixo.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">6.6 – Extensões naturais do sistema de ronda</h4>



<p>Uma vez modularizado, fica muito mais fácil evoluir esse sistema para cenários mais complexos, sem transformar o código em um monolito incontrolável. Algumas extensões bem naturais são:</p>



<p>Uma delas é o suporte a múltiplos pontos de ronda. Hoje, a lógica trata implicitamente “um ponto” e estima o atraso com base no tempo entre uma passagem e outra. Você pode avançar para um modelo onde cada ponto de ronda tenha um identificador lógico, talvez armazenado junto com a leitura do DS1904 ou mapeado em tabela. A partir disso, é possível validar não apenas se o vigilante está rodando dentro do tempo, mas também se ele segue uma sequência de pontos obrigatória (por exemplo, ponto 1 → ponto 2 → ponto 3 → retorno à base). Essa lógica continua no módulo <code>ronda.c</code>, que passa a decidir quando uma passagem é válida e quando deve ser registrada como falha.</p>



<p>Outra evolução interessante é o registro persistente dos eventos. Em vez de apenas imprimir no console com <code>printf</code>, você pode criar uma função de log que escreva em uma memória externa, cartão SD ou envie via rede para um servidor central, dependendo do contexto da aplicação. A beleza da modularização é que isso pode ser encapsulado em um módulo específico, como <code>log_ronda.c</code>, consumido pela task de ronda sem alterar a API de <code>onewire</code> ou <code>ds1904</code>.</p>



<p>Além disso, o controle da buzina pode ser sofisticado para um padrão mais amigável ou mais agressivo, como disparos intermitentes, escalonamento de níveis de alarme ou integração com outros atuadores (luzes de corredor, mensagens em um painel, envio de notificação para uma central). Tudo isso continua concentrado no módulo <code>buzzer.c</code>, mantendo o resto do sistema limpo.</p>



<p>Por fim, o tratamento de erros pode ser expandido para detectar e discriminar diferentes tipos de falhas, como ausência de iButton, iButton com family code incorreto, falha na leitura do clock, problemas no barramento (cabo rompido, curto), ou ainda falhas na própria task (por exemplo, watchdog acionando se a task travar). Cada tipo de erro pode ser mapeado para ações diferentes, desde simples logs até acionar alarmes distintos ou bloquear o sistema. Essas estratégias de robustez ganham clareza quando o código está modularizado e cada componente tem uma responsabilidade bem definida.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Estratégias de Validação da Ronda, Estruturas de Dados e Integração com Backend</h3>



<p>Agora que o sistema básico está funcionando (driver OneWire, DS1904, tasks de ronda e buzina), vale a pena dar um passo para trás e enxergar isso como um <strong>sistema de rondas completo</strong>, não apenas “código que lê um iButton e toca uma buzina”. Aqui entra a parte de <strong>modelagem</strong>, <strong>validação</strong> e <strong>integração</strong> – que é o que transforma um protótipo em um produto de segurança utilizável.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">7.1 Representando vigilantes, pontos e eventos de ronda</h2>



<p>Na prática, o sistema lida com três tipos principais de informação:</p>



<ul class="wp-block-list">
<li><strong>Vigilante</strong> – quem está fazendo a ronda (identificado pelo ROM do DS1904).</li>



<li><strong>Ponto de ronda</strong> – onde o vigilante está (ponto físico, posto, setor).</li>



<li><strong>Evento de ronda</strong> – quando alguém passou em determinado ponto.</li>
</ul>



<p>Mesmo em um ESP32, vale a pena ter uma estrutura minimamente organizada, por exemplo:</p>



<pre class="wp-block-code"><code>typedef struct {
    uint8_t rom&#91;8];      // ID completo do DS1904
    uint32_t codigo;     // ID lógico interno (ex: 1001, 1002...)
} Vigilante_t;

typedef struct {
    uint16_t id_ponto;   // Identificador lógico do ponto (1, 2, 3...)
    uint32_t tempo_max;  // tempo máximo em segundos até o próximo ponto
} PontoRonda_t;

typedef struct {
    uint32_t timestamp_rtc;  // valor lido do DS1904
    uint16_t id_ponto;       // ponto onde ocorreu
    uint32_t id_vigilante;   // código lógico do vigilante
    bool atraso;             // true se chegou atrasado
} EventoRonda_t;
</code></pre>



<p>A lógica de negócio (em <code>ronda.c</code>) passa a trabalhar com esses tipos, em vez de apenas imprimir o ROM bruto. Isso facilita:</p>



<ul class="wp-block-list">
<li>gravar eventos em memória;</li>



<li>enviar para um servidor;</li>



<li>reprocessar logs depois (auditoria).</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">7.2 Validando a sequência e o tempo entre pontos</h2>



<p>No exemplo base, consideramos apenas “10 minutos sem notificação → buzina”. Mas um sistema de ronda real costuma ter:</p>



<ul class="wp-block-list">
<li><strong>Uma sequência obrigatória de pontos</strong> (por exemplo: 1 → 2 → 3 → 4 → 1 → …);</li>



<li><strong>Tempos máximos diferentes</strong> entre cada trecho (ex: 5 min entre 1 e 2, 12 min entre 2 e 3, etc.).</li>
</ul>



<p>Você pode modelar isso como um pequeno <strong>autômato de estados</strong> ou simplesmente como uma lista ordenada de pontos:</p>



<pre class="wp-block-code"><code>PontoRonda_t rota&#91;] = {
    { .id_ponto = 1, .tempo_max = 300 },   // 5 minutos
    { .id_ponto = 2, .tempo_max = 600 },   // 10 minutos
    { .id_ponto = 3, .tempo_max = 480 },   // 8 minutos
    { .id_ponto = 4, .tempo_max = 900 }    // 15 minutos
};

static uint8_t indice_ponto_atual = 0;
</code></pre>



<p>Quando o vigilante passa em um ponto, a lógica pode verificar:</p>



<ol class="wp-block-list">
<li><strong>Se ele está no ponto esperado</strong> (por exemplo, se esperávamos o ponto 2 e ele apareceu no 4, pode ser uma falha de rota).</li>



<li><strong>Quanto tempo passou desde o último evento válido</strong>:
<ul class="wp-block-list">
<li>lê <code>timestamp_rtc</code> do DS1904,</li>



<li>compara com o timestamp anterior,</li>



<li>verifica se excede <code>tempo_max</code> do trecho atual.</li>
</ul>
</li>
</ol>



<p>Isso permite:</p>



<ul class="wp-block-list">
<li>tocar a buzina não apenas por “10 minutos absolutos”, mas por atraso em relação à rota;</li>



<li>marcar eventos com <code>atraso = true</code> para relatório posterior;</li>



<li>identificar “pulos de ponto” ou rondas feitas fora de ordem.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">7.3 Tratamento de erros e robustez</h2>



<p>Aplicações em campo sempre lidam com condições imperfeitas. Alguns exemplos:</p>



<ul class="wp-block-list">
<li>o vigilante encosta o iButton de forma rápida demais e a leitura falha;</li>



<li>cabo do leitor OneWire com mau contato;</li>



<li>DS1904 de outro funcionário (não autorizado) é usado.</li>
</ul>



<p>A aplicação deve tratar isso de forma clara:</p>



<ul class="wp-block-list">
<li>Se <code>onewire_reset()</code> falhar repetidamente, você pode:
<ul class="wp-block-list">
<li>acender um LED de “falha de leitura”;</li>



<li>registrar um log de erro (cabo rompido, sem dispositivo, etc.).</li>
</ul>
</li>



<li>Se o <code>family code</code> da ROM não for <code>0x24</code>, você pode recusar a leitura e indicar “dispositivo inválido”.</li>



<li>Se o ID do vigilante não estiver na lista de autorizados:
<ul class="wp-block-list">
<li>ignorar o evento para fins de ronda;</li>



<li>opcionalmente registrar como tentativa de uso não autorizado.</li>
</ul>
</li>
</ul>



<p>Essa robustez faz diferença enorme na prática: evita falsas acusações ao funcionário (“não passou no ponto”) quando, na verdade, era um problema de cabo ou leitor.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">7.4 Integração com backend (HTTP, MQTT, etc.)</h2>



<p>Um ESP32 conectado (Wi-Fi/Ethernet) abre espaço para monitorar a ronda em tempo real:</p>



<ul class="wp-block-list">
<li>cada <code>EventoRonda_t</code> pode ser:
<ul class="wp-block-list">
<li>enviado via <strong>HTTP POST</strong> para um servidor;</li>



<li>publicado em um tópico <strong>MQTT</strong> (por exemplo, <code>empresa/ronda/ponto1</code>);</li>



<li>armazenado localmente em flash/SD e sincronizado depois.</li>
</ul>
</li>
</ul>



<p>Exemplo conceitual em MQTT (sem entrar no código de rede):</p>



<ul class="wp-block-list">
<li>Tópico: <code>ronda/vigilante/&lt;id_vigilante>/ponto/&lt;id_ponto></code></li>



<li>Payload (JSON): <code>{ "timestamp_rtc": 123456789, "atraso": false, "rota_ok": true }</code></li>
</ul>



<p>No servidor, um painel web pode:</p>



<ul class="wp-block-list">
<li>desenhar timeline da ronda;</li>



<li>destacar atrasos;</li>



<li>gerar relatórios diários/semanais.</li>
</ul>



<p>A modularização ajuda muito: <code>ronda.c</code> apenas chama uma função “enviar_evento_ronda()” que você implementa em um módulo de comunicação. Assim, mudar de HTTP para MQTT ou vice-versa não quebra o núcleo do sistema.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">7.5 Segurança lógica e prevenção de fraude</h2>



<p>Um sistema de ronda também precisa se proteger contra alguns comportamentos maliciosos, por exemplo:</p>



<ul class="wp-block-list">
<li>um vigilante tentando “burlar” o sistema encostando o iButton várias vezes no mesmo ponto;</li>



<li>alguém copiando um DS1904 ou tentando usar um token não autorizado.</li>
</ul>



<p>Algumas ideias de mitigação:</p>



<ul class="wp-block-list">
<li><strong>Tempo mínimo entre leituras no mesmo ponto</strong>: por exemplo, ignorar leituras no mesmo ponto em janelas menores que X segundos.</li>



<li><strong>Cross-check com dados externos</strong>: cruzar os horários de ronda com controle de acesso (catraca, portão) para ver se o vigilante realmente estava na empresa.</li>



<li><strong>Assinatura dos registros</strong>: se você envia dados a um backend, pode usar um segredo compartilhado ou um token para evitar que um dispositivo não autorizado injete eventos falsos na rede.</li>
</ul>



<p>A base tecnológica (OneWire + DS1904 + ESP32) já te dá uma fundação forte; o restante é desenho de políticas.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">7.6 Amarrando tudo com o barramento OneWire</h2>



<p>Fechando o ciclo:</p>



<ul class="wp-block-list">
<li>O <strong>OneWire</strong> simplifica o hardware: um único fio de dados + GND, resistor de pull-up e encapsulamento robusto (iButton).</li>



<li>O driver thread-safe com mutex + critical sections torna a solução confiável mesmo com multitarefa pesada.</li>



<li>O <strong>DS1904</strong> entrega identificação única e RTC no mesmo dispositivo, perfeito para sistemas de ronda.</li>



<li>As tasks do FreeRTOS e as notificações permitem uma lógica clara:
<ul class="wp-block-list">
<li>task de ronda detecta passagem → notifica task da buzina → reseta janela de 10 minutos;</li>



<li>task da buzina vigia atrasos e aciona o alarme.</li>
</ul>
</li>
</ul>



<p>A partir daqui, o que diferencia um protótipo de um <strong>produto comercial</strong> é principalmente:</p>



<ul class="wp-block-list">
<li>qualidade da modelagem de dados;</li>



<li>robustez contra falhas;</li>



<li>capacidade de integrar com sistemas maiores (servidor, app, dashboard).</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading"><strong>Resumo Final</strong></h3>



<p>O barramento OneWire se destaca por sua simplicidade elétrica — apenas um fio de dados mais o GND — e pela robustez dos seus dispositivos, como os iButtons. Essa combinação torna a tecnologia especialmente adequada para sistemas de ronda e controle de presença em ambientes industriais e de segurança. No núcleo do OneWire estão operações temporizadas em microssegundos (reset, presence pulse, read/write), que precisam ser rigorosamente respeitadas para que haja comunicação estável. Por isso, a implementação em microcontroladores multitarefa, como o ESP32, exige mecanismos que garantam tanto exclusão mútua entre tarefas quanto proteção contra interrupções que possam distorcer a temporização. A solução prática é combinar <strong>mutex</strong> (para evitar concorrência entre tasks) com <strong>critical sections</strong> (para desabilitar interrupções nos momentos em que o protocolo requer precisão temporal).</p>



<p>O iButton DS1904 é particularmente atraente para aplicações de ronda porque reúne dois elementos essenciais: um <strong>ID único mundialmente</strong> e um <strong>RTC interno</strong> que fornece um timestamp confiável sempre que o vigilante encosta o dispositivo no leitor. Isso permite que o sistema identifique quem passou e exatamente quando passou em cada ponto de verificação, registrando eventos com precisão. Ao modularizar o código em camadas — <code>onewire</code> (nivel baixo), <code>ds1904</code> (interpretação do dispositivo), <code>ronda</code> (lógica de negócio) e <code>buzzer</code> (atuador) — obtém-se um sistema flexível, fácil de manter e altamente reutilizável. A task de ronda lê o DS1904, valida o vigilante, gera um evento e envia uma <strong>notificação</strong> para a task da buzina, que reinicia uma janela de tempo pré-definida (por padrão, 10 minutos). Se nenhuma nova notificação chegar dentro desse período, a buzina é acionada até que uma nova passagem seja registrada. Essa lógica usa diretamente recursos do FreeRTOS como <code>ulTaskNotifyTake()</code> e <code>xTaskNotifyGive()</code>, que fornecem uma forma eficiente de sincronização entre tasks sem necessidade de filas ou timers adicionais.</p>



<p>Uma vez estruturado, o sistema pode evoluir naturalmente: é possível incluir vários pontos de ronda, sequências obrigatórias, tempos distintos entre pontos, tratamento de erros avançado (device inválido, cabo danificado, leitura incompleta), registro persistente em memória ou cartão SD, e integração com servidores remotos via HTTP ou MQTT. Cada leitura DS1904 pode se transformar em um evento auditável, permitindo painéis administrativos que acompanham a ronda em tempo real. O sistema também pode incorporar segurança lógica para prevenir abusos, como leituras repetidas no mesmo ponto, uso de tokens não autorizados ou tentativas de fraude.</p>



<p>Em síntese, o OneWire associado ao DS1904 e ao ESP32 forma uma solução elegante, robusta e de fácil expansão para implementar rondas profissionais. A comunicação é simples no hardware, confiável no software graças ao tratamento cuidadoso do protocolo, e rica em possibilidades quando inserida em uma arquitetura modular e multitarefa. O resultado é um sistema de ronda eficiente, escalável e pronto para usos reais em segurança patrimonial, industrial e corporativa.</p><p>The post <a href="https://mcu.tec.br/algoritimos/sistema-de-ronda-com-onewire-e-ibutton-ds1904-usando-esp32-implementacao-completa-multithread-e-a-prova-de-falhas/">Sistema de Ronda com OneWire e iButton DS1904 usando ESP32: Implementação Completa, Multithread e à Prova de Falhas</a> first appeared on <a href="https://mcu.tec.br">MCU & FPGA</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://mcu.tec.br/algoritimos/sistema-de-ronda-com-onewire-e-ibutton-ds1904-usando-esp32-implementacao-completa-multithread-e-a-prova-de-falhas/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">920</post-id>	</item>
	</channel>
</rss>
