1-wire LEGO LED stripe

This post is part 2 of 3 of  Rede de controlo 1-wire

E agora um primeira experiência com múltiplos DS2413 numa mesma microLAN…

Cada dispositivo 1-wire tem um ID distinto gravado em fábrica:

$sudo ls /mnt/1wire/
3A.4C2B13000000  81.086D33000000  settings    structure  uncached
3A.B0E216000000  bus.1          statistics  system

Vemos que temos 3 dispositivos de 2 classes diferentes.  A classe 81 é a do Master Controller (o adaptador USB DS9490R) e a classe  3A é a do DS2413. Temos portanto dois dispositivos DS2413 na rede, cada um com o seu ID:

  • 4C2B13000000
  • B0E216000000

Na forma como o owfs lida com 1-wire o par “Classe.ID” funciona como um endereço. É possível definir alias mas funcionam apenas localmente (isto é: se movermos a microLAN do meu Ubuntu para o LEGO ev3dev tẽm de ser redefinidos) e na minha primeira experiência com alias perdi o acesso aos dispositivos por isso deixo para outra altura.

Sendo da mesma classe, estes dois dispositivos partilham os mesmos atributos (PIO.A, PIO.B e PIO.ALL por exemplo). Se tiver um LED em cada PIO destes devices podemos por exemplo acender todos os LEDs percorrendo todos os dispositivos da classe 3A e escrevendo “1,1” na subpasta PIO.ALL.

O programa abaixo acende cada LED em sequência, primeiro num sentido depois no outro:

import sys, traceback
from time import sleep
from subprocess import call

# B0E216000000/PIO.A = primeiro LED
# B0E216000000/PIO.B = segundo LED
# 4C2B13000000/PIO.A = terceiro LED
# 4C2B13000000/PIO.B = quarto LED

def main():
    try:

# apagar tudo ao comecar

    call("echo 0,0 > /mnt/1wire/3A.B0E216000000/PIO.ALL", shell=True);
    call("echo 0,0 > /mnt/1wire/3A.4C2B13000000/PIO.ALL", shell=True);

        while True:
        # apaga tudo
        call("echo 0,0 > /mnt/1wire/3A.B0E216000000/PIO.ALL", shell=True);
        call("echo 0,0 > /mnt/1wire/3A.4C2B13000000/PIO.ALL", shell=True);

                call("echo 1 > /mnt/1wire/3A.B0E216000000/PIO.A", shell=True); # LED1
                sleep(0.25);

        # apaga tudo
        call("echo 0,0 > /mnt/1wire/3A.B0E216000000/PIO.ALL", shell=True);
        call("echo 0,0 > /mnt/1wire/3A.4C2B13000000/PIO.ALL", shell=True);

                call("echo 1 > /mnt/1wire/3A.B0E216000000/PIO.B", shell=True); # LED2
                sleep(0.25);

        # apaga tudo
        call("echo 0,0 > /mnt/1wire/3A.B0E216000000/PIO.ALL", shell=True);
        call("echo 0,0 > /mnt/1wire/3A.4C2B13000000/PIO.ALL", shell=True);

        call("echo 1 > /mnt/1wire/3A.4C2B13000000/PIO.A", shell=True); # LED3
                sleep(0.25);

        # apaga tudo
        call("echo 0,0 > /mnt/1wire/3A.B0E216000000/PIO.ALL", shell=True);
        call("echo 0,0 > /mnt/1wire/3A.4C2B13000000/PIO.ALL", shell=True);

        call("echo 1 > /mnt/1wire/3A.4C2B13000000/PIO.B", shell=True); # LED4
                sleep(0.25);

        # apaga tudo
        call("echo 0,0 > /mnt/1wire/3A.B0E216000000/PIO.ALL", shell=True);
        call("echo 0,0 > /mnt/1wire/3A.4C2B13000000/PIO.ALL", shell=True);

        call("echo 1 > /mnt/1wire/3A.4C2B13000000/PIO.A", shell=True); # LED3
                sleep(0.25);

        # apaga tudo
        call("echo 0,0 > /mnt/1wire/3A.B0E216000000/PIO.ALL", shell=True);
        call("echo 0,0 > /mnt/1wire/3A.4C2B13000000/PIO.ALL", shell=True);

                call("echo 1 > /mnt/1wire/3A.B0E216000000/PIO.B", shell=True); # LED2
                sleep(0.25);


    except (KeyboardInterrupt, SystemExit):
        print "Exiting...";
    except Exception:
        traceback.print_exc(file=sys.stdout);

# apagar tudo antes de sair

    call("echo 0,0 > /mnt/1wire/3A.B0E216000000/PIO.ALL", shell=True);
    call("echo 0,0 > /mnt/1wire/3A.4C2B13000000/PIO.ALL", shell=True);
    sys.exit(0);

if __name__ == "__main__":
    main()

 

E o programa abaixo acende as luzes aleatoriamente, mais próprio para um concerto ou uma discoteca (com tempo hei-de estudar a forma de usar as rotinas PyAudio para medir a intensidade de uma música e controlar as luzes ao ritmo desta embora duvide que o EV3 tenha CPU para tal).

 

 

import sys, traceback
from time import sleep
from subprocess import call
from random import randrange

# B0E216000000/PIO.A = primeiro LED
# B0E216000000/PIO.B = segundo LED
# 4C2B13000000/PIO.A = terceiro LED
# 4C2B13000000/PIO.B = quarto LED


def main():
    try:

# apagar tudo ao comecar

    call("echo 0,0 > /mnt/1wire/3A.B0E216000000/PIO.ALL", shell=True);
    call("echo 0,0 > /mnt/1wire/3A.4C2B13000000/PIO.ALL", shell=True);

        while True:
        # apaga tudo
        call("echo 0,0 > /mnt/1wire/3A.B0E216000000/PIO.ALL", shell=True);
        call("echo 0,0 > /mnt/1wire/3A.4C2B13000000/PIO.ALL", shell=True);

        if (randrange(2) == 1):
                    call("echo 1 > /mnt/1wire/3A.B0E216000000/PIO.A", shell=True); # LED1

        if (randrange(2) == 1):
                    call("echo 1 > /mnt/1wire/3A.B0E216000000/PIO.B", shell=True); # LED2

        if (randrange(2) == 1):
            call("echo 1 > /mnt/1wire/3A.4C2B13000000/PIO.A", shell=True); # LED3

        if (randrange(2) == 1):
            call("echo 1 > /mnt/1wire/3A.4C2B13000000/PIO.B", shell=True); # LED4
                
                sleep(0.25);


    except (KeyboardInterrupt, SystemExit):
        print "Exiting...";
    except Exception:
        traceback.print_exc(file=sys.stdout);

# apagar tudo antes de sair

    call("echo 0,0 > /mnt/1wire/3A.B0E216000000/PIO.ALL", shell=True);
    call("echo 0,0 > /mnt/1wire/3A.4C2B13000000/PIO.ALL", shell=True);
    sys.exit(0);

if __name__ == "__main__":
    main()

 

1-wire LED

This post is part 1 of 3 of  Rede de controlo 1-wire

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)
Wire D

Apesar da datasheet da MAXIM indicar o pinout achei esta foto menos dada a confusões:

Sheepwalk Electronics: Pinout DS9490R

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):

1wire-led

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.

 

 

 

 

LEGO Power Functions MOD: SuperLamp

No meu rover AD4M4ST0R estava a utilizar 3 conjuntos 8870 (LEGO Power Functions Light) para iluminar o ambiente antes de tirar uma foto com a webcam. Como os LEDs utilizados pela LEGO consomem muito pouco (entre 3 a 4 mA quando usados no EV3) foram necessários 6 para poder tirar fotos satisfatórias em ambientes escuros.

Eu acho o conjunto 8870 um pouco desajeitado com aquela peça 2×2 intermédia. E também acho caro os €6 ou mais custam avulso (os que tenho vieram com os conjuntos 8293 (LEGO Power Functions Accessory Box).

Numa conversa no fórum PLUG ocorreu aproveitar os LEDs de uma lanterna ou candeeiro baratos, ontem recuperei essa ideia ao encontrar no Leroy Merlin estas lanternas por €2.99:

A lanterna funciona com 3 pilhas AAA. A acreditar no rótulo, consome 360 mW o que a 4.5V significa 80 mA, não é exagerado para o EV3. Ligando duas em série nem precisaria preocupar-me com a tensão, pode ser ligado directamente a uma bateria Power Functions ou a uma porta NXT/EV3.

O corpo da lanterna é em alumínio. Desenroscando a parte de trás da lanterna podemos retirar o suporte das pilhas mas como a parte frontal (com os LEDs e o «vidro») não é desenroscável usei um lápis sem bico para fazer pressão por dentro e retirar:

Os seis LED estão soldados a uma placa de circuito impresso circular com duas pistas também circulares, concêntricas. Os LEDs estão montados em paralelos, todos os ânodos numa primeira pista e todos os cátodos numa segunda pista. A pista interior estava ligada ao suporte das pilhas e é o positivo, a pista exterior fazia contacto com o corpo da lanterna e é o negativo.

Não parece existir nenhuma forma de limitação de currente (numa série de lanternas «gambiarra» de origem chinesa, com 24 a 28 LEDs e também 3 pilhas AAA, encontrei sempre uma resistência de 1.5Ω de 1W).

Para testar liguei o positivo de uma conjunto de pilhas Power Functions à pista interior de uma das placas e o negativo à pista exterior da outra placa. Depois liguei as restantes pistas (pista exterior da primeira placa e pista interior da segunda placa):

E funciona!

Com um multímetro em modo Amperímetro verifica-se o consumo:

Uhmmmm… bastante mais que os 80 mA esperados. Mas aceitável à mesma para o propósito em vista (períodos curtos, controlo PWM).

Confirmadas as ligações soldei os fios e isolei com fita de electricista:

Pode-se ver na parte superior da imagem acima que usei fichas «mini-banana» junto ao terminal Power Functions de modo a poder trocar a polaridade se necessário).

A «LEGOlização» foi mais uma vez conseguida à custa do famigerado Kragle (cola de contacto transparente)

E finalmente a montagem no AD4M4ST0R:

Com o ev3dev utilizei o segunte comando para comparar consumos:

cat /sys/class/power_supply/legoev3-battery/current_now

Para cada cenário medi 10 vezes e calculei a média (sempre com o EV3 com bateria ligada ao carregador):

  • Em repouso: 317 mA
  • Com 6 LEDs Power Functions @100%: 340 mA
  • Com SuperLamps @100%:  579 mA

Portanto os 6 LEDs Power Functions consomem 23 mA e as minhas SuperLamps consomem 262 mA (onze vezes mais!). É um valor bastante superior aos 181 mA medidos com o multímetro e o suporte de pilhas Power Functions e que já faz considerar a hipótese de adicionar um díodo do tipo 1N400x para reduzir um pouco a tensão aplicada aos LEDs e com isso reduzir a corrente.

 

 

 

AD4M4ST0R – controlando LEDs

This post is part 5 of 9 of  AD4M4ST0R - um rover LEGO

A qualidade das fotos tiradas pela webcam Logitech C170 não é má… desde que haja suficiente luz ambiente. Por isso lembrei-me de ligar LED’s Power Functions a uma das portas de saída ainda disponíveis para funcionar como «flashes» ao tirar a fotografia.

Infelizmente a versão actual do ev3dev ainda não permite o controlo individual das portas (foi aberto hoje um issue, depois de uma troca de ideias) por isso a única maneira é enganar o ev3dev fazendo-o acreditar que tem um motor.

Os motores e os sensores EV3 têm uma funcionalidade «Auto-ID» que permite ao EV3 identificar que dispositivo foi ligado a cada porta (género “Plug and Play”). Nos videos do utilizador “TecnicRobot” ele utiliza um par de resistências para que os seus dispositivos de saída pareçam ser motores EV3.

Para isso fiz um cabo semelhante a este (não tinha resistências de 1KΩ e 10 KΩ à mão mas como o que interessa é a proporção usei de 2.2KΩ e 22KΩ) e funciona.

(o pinout do cabo EV3 foi baseado neste)

    EV3 Cable                             PF Cable
                                           - 9V
White    Pin 1 (M1)     -------------------- C2 (middle)
Black    Pin 2 (M2)     -------------------- C1 (middle)
Red      Pin 3 (GND)    ----[R1]---x       - GND
Green    Pin 4 (POWER)  ----[R2]---x
Yellow   Pin 5 (TACHO0) -----------x
Blue     Pin 6 (TACHO1) -------- Pin 3 (GND)

R2 ~10xR1
R1 = 1K, 2K2, 3K3...
R2 = 10K, 22K, 33K...
dmesg:
tacho-motor tacho-motor2: Tacho motor registered.
ev3-tacho-motor outC:motor: A Tacho Motor connected to port outC gpio 93 irq 194

Para acender os LEDs a meia intensidade:

echo 50 > /sys/class/tacho-motor/tacho-motor2/duty_cycle_sp
echo 1 > /sys/class/tacho-motor/tacho-motor2/run

Para acender na máxima intensidade:

echo 100 > /sys/class/tacho-motor/tacho-motor2/duty_cycle_sp
echo 1 > /sys/class/tacho-motor/tacho-motor2/run

ad4m4st0r-02 (parece-me que é altura de fazer um cabo decente e dar uma arrumação aos cabos do AD4M4ST0R).

Durante a noite, numa sala com pouca iluminação (halogéneo)

ad4m4st0r-03Durante a manhã, no mesmo local com iluminação média (natural mas indirecta):

ad4m4st0r-04A terceira imagem foi tirada com um segundo par de LED’s Power Functions o que me levou na noite seguinte a acrescentar um terceiro par:

ad4m4st0r-06As 4 imagens correspondem a Duty Cycles de 0%, 33%, 67% e 100% (ou seja os LED’s apagados, o equivalente a um par aceso, o equivalente a dois pares acesos e os três pares acesos).

Assim que me for possível vou tentar medir a luminosidade ambiente com um EV3 Color Sensor (45506) para determinar automaticamente o Duty Cycle a aplicar aos LEDs antes de cada fotografia.