Eu tenho um Arduino Uno conectado via USB, usando o cdc_acm
driver. Está disponível em /dev/ttyACM0
.
A convenção para a interface serial do Arduino é que o DTR
sinal seja usado para um sinal de reinicialização - ao usar o adaptador serial para USB integrado, o sinal DTR/RTS/DSR/CTS; ou, ao usar um cabo RS-232, os pinos 4 ou 5 (e possivelmente 6 ou 8) são conectados ao RESET
pino.
Este caminho de redefinição tem a importante vantagem de ser, se não verdadeiramente fora de banda, pelo menos muito próximo à segurança (devido a ser implementado por meio do controlador serial sempre fora de banda em conjunto com o usuário não normal - circuito watchdog controlável), e enquanto ele pode ser fisicamente desabilitado (através da fiação de um capacitor ou um resistor, dependendo do modelo, ao RESET
pino), fazê-lo arruína completamente este importante interruptor de interrupção e todos os utilitários associados.
Infelizmente, parece que , atualmente, o Linux sempre envia esse sinal quando qualquer programa se conecta a um dispositivo ACM por qualquer motivo e ( ao contrário do Windows ) não fornece nenhuma maneira confiável, mesmo vagamente conhecida, de evitar isso.
(Atualmente -hupcl
, "enviar um sinal de desligamento quando o último processo fechar o tty" e "desativar sinais -clocal
de controle do modem" não impedem que esse sinal seja enviado toda vez que o dispositivo é aberto .)
Quando um processo userland está abrindo um dispositivo serial como
/dev/ttyS0
ou/dev/ttyACM0
, o linux aumentará asDTR/RTS
linhas por padrão e as descartará ao fechá-lo.Ele faz isso chamando um
dtr_rts
retorno de chamada definido pelo driver.Infelizmente, ainda não existe nenhum sysctl ou similar que permita desabilitar esse comportamento irritante (pouco útil hoje em dia), então a única coisa que funciona é remover esse callback da
tty_port_operations
estrutura do driver e recompilar o módulo do driver.Você pode fazer isso para o
cdc-acm
driver comentando esta linha :Isso não impedirá que você use as
DTR/RTS
linhas por meio de ioctls seriais comoTIOCMSET
,TIOCMBIC
,TIOCMBIS
, que serão tratadas pelosacm_tty_tiocmset()
callbacks , etc daacm_ops
estrutura, como de costume.Hacks semelhantes podem ser usados com outros drivers; Eu pessoalmente usei isso com o
PL2303
driver usb -> serial.[A diferença é informativa; não será aplicado diretamente porque este site altera guias e espaços em branco]
Eu acho que há uma boa solução alternativa que resolve o problema. Em vez de ler os dados do dispositivo /dev/ttyUSB0 ou /dev/ttyACM0, eles podem ser lidos do pipe nomeado, por exemplo, /tmp/arduino e o programa simples (abaixo) copiaria os dados do dispositivo para o pipe e manteria o dispositivo aberto (evitando assim configurar DTR alto). Isso também evita lidar com todas as dificuldades de leitura do dispositivo. Com o pipe nomeado, ferramentas como cat, less -f podem ser usadas ou apenas qualquer programa com padrão open + read sem a necessidade de emitir comandos ioctl para controlar tty. O programa para copiar o dispositivo para o pipe seria executado, por exemplo, como um serviço inicial e copiar dados do dispositivo para o pipe (e talvez produzir alguns logs). O programa tem que cuidar do sinal SIGPIPE, para evitar que seja fechado ao fechar qualquer processo de leitura de pipe. A carga no servidor seria insignificante devido ao bloqueio intencional da entrada via fcntl. Eu testei e parece estar funcionando bem. Estou realmente tentando resolver o mesmo problema com a interface do Arduino. O bom efeito colateral é que a reinicialização do Arduino pode ser feita simplesmente reiniciando o serviço upstart sempre que necessário, pois define o DTR alto.
O script de shell para iniciá-lo seria (precisa ser reimplementado como serviço upstart):
Acho que o arquivo logrotate precisa usar copytruncate, porque o arquivo ainda está aberto (não tive chance de testar isso):
Nota adicional: Enquanto isso, percebi que, para evitar o tubo quebrado, provavelmente poderia ser alcançado com o uso de cat ou dd em combinação com o comando trap com o parâmetro PIPE, que estaria filtrando o sinal SIGPIPE também. A solução acima funciona para mim, então não estava experimentando o comando trap para obter resultados comparáveis.