Um painel para a placa
Vitrine e Arduino no mesmo programa: gráfico ao vivo, botão que liga o relé, e o cuidado com a thread.
A Vitrine desenha painéis, e a placa produz números: juntar os dois é o caso que fecha o ciclo — ver o sensor num gráfico e agir com um botão. Mas há um detalhe que decide se funciona: a página inteira roda de novo a cada interação, e uma conexão serial não pode ser aberta a cada volta.
adopt Arcane.Vitrine as V
adopt Arcane.IoT as IoT
// V.recurso guarda o objeto POR PROCESSO: a porta abre uma vez
mark @V.recurso
action placa():
p := IoT.conectar_simulada("uno")
p.modo(13, "saida")
p.modo(14, "analogico")
p.relatar_analogico(0)
yield p
assert placa().info()["pinos"] is 20
assert placa() is placa() // a MESMA porta, na segunda chamada
placa().fechar()O painel inteiro#
adopt Arcane.Vitrine as V
adopt Arcane.IoT as IoT
mark @V.recurso
action abrir():
p := IoT.conectar_simulada("uno")
p.modo(13, "saida")
p.modo(14, "analogico")
p.relatar_analogico(0)
p.amostragem(200)
yield p
action pagina():
p := abrir()
V.titulo("Estufa")
leitura := p.analogico(0)
colunas := V.colunas(3)
colunas[0].metrica("Leitura", leitura)
colunas[1].metrica("Temperatura", $"{round(IoT.tmp36(leitura), 1)} °C")
colunas[2].metrica("Aquecedor", "ligado" given p.ler(13) otherwise "desligado")
given V.botao("Alternar aquecedor"):
p.escrever(13, not p.ler(13))
V.atualizar_a_cada(2)
// Sonda: o painel testado sem navegador
s := V.testar(pagina)
s.rodar()
assert "Estufa" in s.texto()
s.clicar("Alternar aquecedor")
assert "Aquecedor" in s.texto()O tempo real da Vitrine é por pergunta#
V.atualizar_a_cada(2) reexecuta a página de dois em dois segundos — é polling, e está escrito assim de propósito. A Vitrine não empurra dado (não usa WebSocket nem SSE), e para um painel de sensor isso é suficiente: o que muda a cada 200 ms não precisa ser visto a cada 200 ms.
Uma rota do Kiln, para a placa#
Quando o que se quer é uma API — outro sistema lendo o sensor —, o Kiln serve. E aqui vale o aviso que o check dá sozinho:
adopt Arcane.IoT as IoT
// o Kiln atende UM PEDIDO POR THREAD, e a placa é uma só:
// duas rotas escrevendo no mesmo pino ao mesmo tempo perdem ordens.
// A resposta é um mutex em volta da placa.
adopt Arcane.Concurrent as C
trava := C.mutex()
placa := IoT.conectar_simulada("uno")
placa.modo(13, "saida")
action ligar(valor):
placa.escrever(13, valor)
yield placa.ler(13)
// com_trava toma a trava, roda, e a SOLTA mesmo se o corpo estourar —
// soltar na mão funciona até o dia em que o corpo levanta, e aí a
// próxima thread espera para sempre.
assert C.com_trava(trava, lambda => ligar(yes)) is yes
assert C.com_trava(trava, lambda => ligar(no)) is no
placa.fechar()O dataforge check avisa quando uma route escreve num nome que vem de fora (escrita-concorrente), e uma placa compartilhada é o caso exato: a concorrência é invisível para quem escreve a rota.