sábado, 13 de fevereiro de 2016

Solução caseira para segurança doméstica

As soluções de segurança que tenho visto são caras, então resolvi desenvolver uma solução caseira. Decidi que com um Raspberry Pi, uma câmera barata e um pouco de código, poderia fazer algo tão bom quanto sistemas dez vezes mais caros.

O mais importante é que o sistema deveria enviar notificações para o celular. O Whatsapp não permite robôs, então recorri ao Telegram.

Desenvolvi usando um Raspberry Pi B, mas o sistema final roda num Raspberry Pi A+ (que é ainda menor e mais barato). Acredito que funcionaria igualmente bem num Raspberry Pi Zero (que custa apenas US$5).

A ideia do projeto é bastante simples: a câmera, quando ativada por movimento, copia imagens para o Raspberry Pi e este, por sua vez, as encaminha para uma conta do Telegram. O Telegram entrega as fotos para o meu celular.

Se o código da câmera fosse aberto, ou ela oferecesse alguma API, seria possível que ela falasse diretamente com o Telegram. Ponto negativo para o código fechado.

Ingredientes
  • Um Raspberry Pi B (512MB de RAM, 700MHz de CPU e custa ~US$25);
  • Uma câmera D-Link 930LB (custa menos de R$200,00);
  • Uma conta no Telegram (Whatsapp não aceita robôs);
  • Um cartão de memória SDHC de 16GB (um de 8GB já resolveria);
  • Um pouco de Perl.
Passos

1. Gravar Raspbian no cartão.

2. Instalar um servidor de ftp (sudo apt-get install vsftpd).

3. Configurar o servidor de ftp (/etc/vsftpd.conf).

É preciso habilitar a possibilidade de escrever e de fazer login com contas locais:


local_enable=YES
write_enable=YES


4. Configurar um ramdisk (para não desgastar o cartão de memória, se o número de escritas fugir ao controle e porque é mais rápido).

É preciso adicionar esta linha ao arquivo /etc/fstab:


tmpfs  /home/pi/tmp  tmpfs  nodev,nosuid,size=1M 0 0


Esse comando cria um espaço de 1MB que fica montado no diretório tmp/ dentro do home do usuário pi, mas poderia ser qualquer outro lugar. Os arquivos não passam de 50KB, então essa área poderia ser muito menor.

5. Configurar a câmera.

Configurei a câmera para fazer o ftp da imagem quando for detectado um movimento (usei sensibilidade de 30%, porque ela envia muitas imagens com mais que isso).

O nome do arquivo indiquei como DCS-930LB e isso acaba resultando num arquivo JPEG chamado DCS-930LB.jpg.

6. Criar uma conta no Telegram.

É preciso procurar a conta @BotFather e conversar. Ele dá todas as instruções.

7. Programar em Perl (essa é a melhor parte).

É preciso instalar algumas bibliotecas, o curl, o cpanm e o módulo WWW::Telegram::BotAPI:

sudo apt-get install libnet-ssleay-perl libio-socket-ssl-perl --fix-missing
sudo apt-get install curl
sudo curl -L http://cpanmin.us | perl - --sudo App::cpanminus
sudo cpanm WWW::Telegram::BotAPI

Usei um modelo de robô e fiz algumas adaptações.

Em primeiro lugar, criei ums lista das contas autorizadas (para que estranhos não brinquem com meu robô):


my %chat_ids=(
  123456789=>'forinti',
  987654321=>'derpina'
);


Depois, mudei o loop principal para descansar 5s a cada iteração, verificar se a imagem mudou (pela hora de criação), e enviar a imagem se algo novo surgiu.


my $paused=0;
my $last_update=get_last_update();
while(1) {
  sleep(5);
  my $update=get_last_update();
  if(!$paused && $update>$last_update) {
    for my $id (keys %chat_ids) {
      $api->sendPhoto({
         chat_id=>$id, 
         photo=>{file=>'/home/pi/tmp/DCS-930LB.jpg'}
      });
    } 
    $last_update=$update;
  }
  ...


Finalmente, criei comandos para recuperar a última imagem (/li), parar o envio (/pause) e reiniciar o envio (/go).

sub get_last_update {
  return (stat('/home/pi/tmp/DCS-930LB.jpg'))[9];
}

sub get_last_image {
  return {
    method=>'sendPhoto',
    photo=>{ file=> '/home/pi/tmp/DCS-930LB.jpg' }
  };
}

sub pause {
  $paused=1;
  return 'Parou. Por enquanto...';
}

sub go {
  $paused=0;
  return 'Reiniciou.';
}


Resultado

Consegui produzir um sistema de segurança bastante simples e barato. Ele consome pouquíssima energia (menos de R$10,00 para operar um ano inteiro) e não precisa ser monitorado já que envia alertas. Por usar pouca energia, poderia funcionar ligado a uma bateria ou a um painel solar. Se conectasse a uma rede de celular, seria realmente resiliente.

terça-feira, 19 de janeiro de 2016

A linha mais comprida de um arquivo

Eu precisava descobrir quantas colunas tinha a linha mais comprida de um arquivo. O único porém é que o arquivo é muito grande.

Primeiro, tentei um pouco de shell.

while read line; do echo -n "$line" | wc -c; done< arquivao  | sort | uniq -c

Isso deveria apresentar o número de linhas com determinado comprimento e o número de ocorrências de cada comprimento (não há muita variação nos comprimentos das linhas, embora haja muitas linhas).

Infelizmente, não tive paciência de deixar esta pequena solução terminar. Resolvi averiguar se o Perl seria mais rápido.

perl -MData::Dumper -n -e '$n{length($_)}++; END {print Dumper(%n)}' arquivao


A solução é bem simples e muito mais rápida que a primeira. Com os parâmeteros -n e -e, o bloco de código é aplicado a cada linha do arquivo (último parâmetro). Cada tamanho de linha vira uma chave no hash %n e é incrementada a cada ocorrência. No fim, uso o módulo Data::Dumper para apresentar o que há dentro de %n.

sexta-feira, 15 de janeiro de 2016

Gotas de otimização Oracle

Há dois problemas de SQL comuns que vejo, com frequência, sendo resolvidos com complexidades e ineficiências desnecessárias.

O primeiro é o de precisar o número de registros de uma consulta junto com os dados. A solução ineficiente é a de executar a consulta duas vezes; usando count() primeiro e buscando os dados depois. As funções analíticas são pouco conhecidas ainda, infelizmente.


select count(1)
from pessoas;

select nome
from pessoas;


Usando a versão analítica de count(), as duas consultas podem ser unidas.

select nome, count(1) over ()
from pessoas


E ganha-se uma coluna com o total de linhas.

O segundo problema é o de retirar acentos. Já vi soluções com múltiplos ifs, com case, e até com muitos replace() aninhados.

A minha solução preferida é muito simples e fácil de ler:

select nome, translate(nome,
                'ÀÁÃÂÄÈÉÊËÙÚÛÜÌÍÎÏÒÓÔÕÖÇÑàáãâäèéêëùúûüìíîïòóôõöçñ',
                'AAAAAEEEEUUUUIIIIOOOOOCNaaaaaeeeeuuuuiiiiooooocn') 
from pessoas;


Talvez haja uma solução mais curta (sei que há em Java), mas não creio que haja uma tão legível.

quarta-feira, 13 de janeiro de 2016

Os problemas médicos da informática

O modelo de medicina no Brasil tem um problema fundamental. Os médicos são remunerados por procedimentos, exames, consultas, etc. Isto é, sua remuneração não aumenta com a saúde dos pacientes. Ela aumenta, isto sim, quanto pior for a saúde das pessoas.

Na informática, isso seria como pagar os programadores por linha de código. Fosse essa a nossa realidade, teríamos legiões de programadores Java e algumas poucas almas iluminadas usando Perl.

domingo, 3 de janeiro de 2016

Receitas

A comparação de programas e algoritmos com receitas é antiga e comum, mas sempre me incomodou.

Acho que achei uma maneira de melhorá-la.

Um programa é como uma receita, mas uma receita que requer cinco mil ingredientes, dois milhões de passos, e dezesseis panelas (para um fogão de quatro bocas).

quinta-feira, 31 de dezembro de 2015

2016

Em soma de potências de 2: 210+29+28+27+26+25 

Em binário: 11111100000

Em base 8: 3740

Em base 16: 7E0

Em base 32: 1V0

Em primos: 2 5 * 3 2 * 7

E o meu favorito: 2017-1.

quarta-feira, 23 de dezembro de 2015

Humanos e máquinas

Precisei consertar um Oracle Dataguard (um banco reserva que recebe todas as alterações do banco principal e que funciona como  cópia de segurança) e descobri algo sobre a produtividade dos seres humanos.

O banco reserva estava 17h atrás do banco principal quando resolvi o problema que impedia que o banco principal enviasse as alterações. Em 15 minutos os dois estavam sincronizados.

Isso quer dizer que os humanos levaram 17h para invocar transações suficientes para provocar 15 minutos de trabalho no banco secundário. Ademais, o banco primário funciona em duas máquinas, enquanto o secundário roda sobre apenas um servidor.

Evidentemente, o processamento usado em consultas não entra nessa conta. Por outro lado, muitas das alterações são provocadas por jobs (não precisam de interação humana). Mesmo assim, é interessante o fato de que um computador fez o trabalho de milhares de pessoas 68 vezes mais rapidamente.