Estou a reproduzir em LEGO um concerto dos Xutos&Pontapés. A posição dos elementos da banda em palco vai ser controlada por um controlador (ainda não decidi se um Raspberry Pi ou um LEGO EV3) e quero poder controlar também as luzes dos projectores:
Se as luzes acendessem todas em simultâneo as ligações eléctricas seriam simples e seriam necessários passar apenas 2 cabos (positivo e negativo, comuns a todos os LEDs) pela estrutura por cima do palco. Mas eu quero um controlo individual dos projectores e pelo menos 10 deles. Isso significa pelo menos 11 cabos (10 positivos e 1 negativo comum) e apesar de ser possível (consigo passar 4 ou 5 cabos de jumper por entre um furo Technic por isso bem arrumados os cabos até nem dariam muito nas vistas) obriga-me a ter pelo menos 10 portas digitais para o controlo (o que também é possível com circuitos adicionais mas não me cativa). E se quiser aumentar o número de projectores a coisa começa a complicar-se.
Comecei logo a pensar num bus de controlo e pendurar micro-circuitos de controlo ao longo do bus. O ideal mesmo seriam LEDs I2C mas não encontrei nada suficientemente pequeno ou funcional por isso optei por uma microLAN 1-wire.
O termo “1-wire” é enganador: são necessários pelo menos 2 cabos num bus 1-wire (dados e massa) e na maioria das aplicações práticas será necessário um terceiro para alimentar os dispositivos (a norma prevê um modo “parasita” em que os dispositivos retiram alimentação da ligação prevista para dados mas isso apenas se aplica a dispositivos de muito baixo consumo como sensores de temperatura). Mesmo assim 3 cabos é muito bom para controlar quantos LEDs quiser (se decidir mais 2 ou 3 projectores não preciso passar mais 2 ou 3 cabos nem desencantar mais 2 ou 3 portas digitais no controlador).
A Maxim fabrica o DS2413, um circuito extremamente reduzido que implementa 2 portas digitais bidireccionais. É um chip SMD mas a Adafruit fornece um kit com o chip já montado numa placa de circuito impresso sendo só necessário soldar 4 headers e eventualmente uma resistência (tudo fornecido com o kit) para podermos utilizar na nossa microLAN.
Uhmmm… qual microLAN?
É possível implementar uma microLAN com o Raspberry Pi como master utilizando uma porta GPIO e uma resistência de pull-up. Mas é algo específico para o Raspberry, prefiro algo que possa replicar no meu PC ou no LEGO EV3. Por isso encomendei à RS Online um Maxim DS9490R que é um dispositivo USB que funciona como master controller numa microLAN e é suportado pelo Linux.
No meu portátil (Ubuntu 14.04) foi só ligar:
$ dmesg (...) [18011.890734] usb 2-1.3: new full-speed USB device number 7 using ehci-pci [18011.983566] usb 2-1.3: New USB device found, idVendor=04fa, idProduct=2490 [18011.983576] usb 2-1.3: New USB device strings: Mfr=0, Product=0, SerialNumber=0 [18012.021996] Driver for 1-wire Dallas network protocol. [18012.026144] usbcore: registered new interface driver DS9490R
Não fui ver que módulos foram carregados na altura mas penso que foram ‘ds2490’ e ‘wire’ (existe também um módulo ‘ds9490r’ mas não está presente no meu Ubuntu).
Após detectar uma microLAN o kernel cria uma pasta ‘/sys/bus/w1’ para dispositivos 1-wire:
$ ls /sys/bus/w1/devices 81-000000336d08 w1_bus_master1
“81-” identifica o DS9490
A ligação do DS2413 à microLAN é muito simples: o DS9490R tem uma ficha RJ12, idêntica às RJ11 dos telefones mas com 6 contactos. Felizmente dos 6 contactos apenas os 2 centrais são necessários pelo que podemos usar um cabo normal de telefone só com dois condutores:
- 3 – OW (OW 1-Wire Data)
- 4 – GND_OW (1-Wire Return)
Apesar da datasheet da MAXIM indicar o pinout achei esta foto menos dada a confusões:
A Sheepwalk Electronics vende alguns produtos 1-wire (inclusive o DS9490R) e publica no seu site alguma informação útil relacionada com 1-wire.
Depois de soldar o header à placa da Adafruit foi só fazer as ligações (com cabos de jumper ou crocodilos, o que houver à mão):
Com 3 baterias AA NiMH (cerca de 3.8V) e um LED branco de alto brilho (cerca de 3.2V) temos 0.6V na resistência por isso se esta for de 1KΩ o consumo do LED será de 0.6 mA de corrente, bastante abaixo dos 20 mA tolerados por cada porta e suficiente para um teste (no palco LEGO irei puxar mais por cada LED, pelo menos 10 mA cada e alimentação será de outro tipo).
NOTA: com o kit da Adafruit vem uma resistência de 4.7KΩ para pull-up da linha ‘1-Wire Data’. Deveria ligar esta resistência ao pino 1 da ficha RJ12 (+5V vindos do bus USB do host onde estiver ligado o controlador, neste caso o PC) mas não me agradou muito a ideia e como suspeito que internamente o controlador já preveja isso optei deliberadamente por não usar o pull-up… aparentemente sem consequências.
Pouco depois de ligar o cabo telefónico ao controlador USB o nosso circuito é imediatamente reconhecido:
[ 706.356777] 0x81: count=17, status: 01 00 20 40 05 04 04 00 20 53 00 00 00 01 00 00 a5 [ 706.356800] [ 706.356808] enable flag: 1 [ 706.356812] 1-wire speed: 0 [ 706.356816] strong pullup duration: 20 [ 706.356819] programming pulse duration: 40 [ 706.356822] pulldown slew rate control: 5 [ 706.356825] write-1 low time: 4 [ 706.356829] data sample offset/write-0 recovery time: 4 [ 706.356832] reserved (test register): 0 [ 706.356835] device status flags: 20 [ 706.356839] communication command byte 1: 53 [ 706.356842] communication command byte 2: 0 [ 706.356845] communication command buffer status: 0 [ 706.356848] 1-wire data output buffer status: 0 [ 706.356852] 1-wire data input buffer status: 1 [ 706.356855] reserved: 0 [ 706.356858] reserved: 0 [ 706.356861] new device detect: a5 [ 707.897674] w1_master_driver w1_bus_master1: Family 3a for 3a.00000012fdf4.44 is not registered.
Infelizmente diz que a famíla 3A não está registrada – o suporte nativo do kernel a dispositivos 1-wire não se extende [ainda?] ao DS2413. É pena, assim não temos nada útil dentro de ‘/sys/bus/w1/devices/3a-00000012fdf4’. Mas tudo bem, já esperava isso, existe o projecto owfs dedicado especificamente ao 1-wire:
sudo apt-get install owfs Reading package lists... Done Building dependency tree Reading state information... Done The following extra packages will be installed: libow-2.8-15 owfs-common owfs-fuse owftpd owhttpd owserver Suggested packages: owfs-doc The following NEW packages will be installed: libow-2.8-15 owfs owfs-common owfs-fuse owftpd owhttpd owserver 0 upgraded, 7 newly installed, 0 to remove and 0 not upgraded. Need to get 296 kB of archives. After this operation, 1,364 kB of additional disk space will be used. Do you want to continue? [Y/n]
Após a instalação descobrimos que temos 3 serviços a correr:
- owserver
- owhttpd
- owftpd
Embora não me pareçam necessários, nesta fase vou deixá-los a correr. Descobrimos também que o owfs impede o kernel de carregar os drivers que referi acima de modo a não ocorrerem conflitos:
$ cat /etc/modprobe.d/libow-2.8-15.conf blacklist ds9490r blacklist ds2490 blacklist wire
Para aceder à microLAN usamos o comando ‘owfs’ mas primeiro é necessário definir um mountpoint onde o owfs vai criar uma estrutura de pastas virtuais que mapeiam cada dispositivo (escolhi ‘/mnt/1wire’):
sudo mkdir /mnt/1wire sudo owfs -u -m /mnt/1wire
Para confirmar que funciona:
$ sudo ls /mnt/1wire 81.086D33000000 bus.1 settings statistics structure system uncached
temos 1 dispositivo na microLAN (o master controller DS9490R). Vamos agora ligar o DS2413 à microLAN – passadas algumas dezenas de segundos temos:
$ sudo ls /mnt/1wire 3A.F4FD12000000 bus.1 statistics system 81.086D33000000 settings structure uncached
agora já temos algo útil dentro do device ‘3A.F4FD12000000’:
$ sudo ls /mnt/1wire/3A.F4FD12000000 address family PIO.A PIO.BYTE r_locator sensed.B alias id PIO.ALL r_address sensed.A sensed.BYTE crc8 locator PIO.B r_id sensed.ALL type
Ligando um LED a PIO.A podemos acendê-lo:
$ sudo sh -c "echo 1 > /mnt/1wire/3A.F4FD12000000/PIO.A"
e voltar a apagá-lo:
$ sudo sh -c "echo 0 > /mnt/1wire/3A.F4FD12000000/PIO.A"
(os comandos são muito feios porque o owfs tem de correr com previlégios de root e eu não sou root no meu Ubuntu; em Raspberry Pi ou ev3dev bastaria:
echo 1 > /mnt/1wire/3A.F4FD12000000/PIO.A echo 0 > /mnt/1wire/3A.F4FD12000000/PIO.A
)
E agora um script python para piscar o LED a cada meio segundo:
import sys, traceback from time import sleep from subprocess import call def main(): try: while True: call("echo 1 > /mnt/1wire/3A.F4FD12000000/PIO.A", shell=True); sleep(0.5); call("echo 0 > /mnt/1wire/3A.F4FD12000000/PIO.A", shell=True); sleep(0.5); except (KeyboardInterrupt, SystemExit): print "Exiting..."; except Exception: traceback.print_exc(file=sys.stdout); call("echo 0 > /mnt/1wire/3A.F4FD12000000/PIO.A", shell=True); sys.exit(0); if __name__ == "__main__": main()
Os meus agradecimentos à PTRobotics por incluírem o Adafruit DS2413 na sua lista de produtos a meu pedido.