Controle o seu Ozobot Evo com um Raspberry Pi via Bluetooth

Eu gosto de movê-lo para movê-lo: como reverter a engenharia do protocolo Bluetooth de um robô de brinquedo para controlá-lo com o código.

Raphael Bink Segue 24 de abr · 9 min ler

Embora você possa controlar o robô de brinquedo Ozobot Evo com um aplicativo, não há API ou tutorial como fazer o mesmo com o código, mas eu precisava disso para um pequeno projeto paralelo. Depois de algumas pesquisas no google eu tentei fazer engenharia reversa do Ozobot Evo sozinho, o que foi um desafio para mim porque não tive nenhuma experiência anterior nesse campo. Eventualmente consegui e neste artigo quero compartilhar minhas aprendizagens.

O objetivo

Para entrar no Reinforcement Learning (RL), eu queria montar um pequeno playground do mundo real com um brinquedo que eu pudesse controlar remotamente com o meu Raspberry Pi .

Eu escolhi perceber que com um Ozobot Evo e meu objetivo era codificar os seguintes dois recursos: Use o meu Raspberry Pi para fazer o movimento do Ozobot e definir seus LEDs para cores específicas. O artigo cobrirá, portanto, todas as etapas do entendimento e codificação necessárias para alcançar esse objetivo.

visão global

  1. Primeiro de tudo, usei o App nRF Connect para obter uma primeira visão geral da comunicação Bluetooth entre o aplicativo Ozobot Evo e o próprio robô.
  2. Então eu cheirei a comunicação Bluetooth com o relatório de bug do Android e analisei com o Wireshark e alguns gráficos.
  3. Depois de entender o protocolo, eu finalmente codifiquei a API do Python para controlar o Ozobot Evo com o meu Raspberry Pi.

1) Obtendo uma primeira ideia do protocolo Bluetooth

Eu usei o aplicativo Android Ozobot Evo para aprender algo sobre o protocolo Bluetooth. Para isso instalei também o App nRF Connect , que mostra os recursos de todos os dispositivos Bluetooth conectados ao seu Smartphone.

Use o aplicativo nRF Connect para obter a primeira visão geral

Na primeira captura de tela, você pode ver que há um dispositivo Bluetooth chamado “OzoEvo2” com o endereço MAC C5: 6D: A3: EC: 65: 70. O endereço MAC é a informação que precisamos para conectar a um dispositivo Bluetooth.

Se você se conectar com o robô de brinquedo no aplicativo Ozobot Evo, poderá verificar suas características no nRF Connect.

Características são os estados de um dispositivo que são expostos a outros dispositivos por meio de Bluetooth. Por exemplo, a cor dos LEDs ou a velocidade das rodas são alguns dos estados do Ozobot Evo. No aplicativo, vemos características diferentes, mas, no momento, não sabemos como interpretá-las ou usá-las. Como fazer isso, abordaremos na próxima seção. Portanto, apenas anotamos por enquanto.

2) Entenda a comunicação Bluetooth

Do nosso primeiro passo, obtivemos o nome e o endereço MAC do Ozobot Evo e aprendemos que está expondo alguns estados através de características. Para entender as características , usaremos o aplicativo Ozobot Evo para mover o robô de brinquedo e definir a cor do LED durante a gravação de toda a comunicação Bluetooth resultante .

Obtenha uma amostra do tráfego Bluetooth

O Android tem meios para farejar toda a sua comunicação Bluetooth, o que é bastante impressionante para o nosso projeto. Você tem que ativar o modo de desenvolvedor para o seu smartphone e, em seguida, você pode criar um relatório de bug que contém tudo o que aconteceu no seu telefone, incluindo a comunicação Bluetooth.

Para entender alguns padrões, criei dois relatórios de bugs. Na primeira, liguei e desliguei novamente várias vezes o LED no topo do Ozobot Evo. Na segunda amostra, eu dirigi o Ozobot para a frente por alguns segundos.

Depois de recuperar o relatório de erros no seu computador, você precisa detalhar bastante para encontrar o relatório Bluetooth:

 YOUR-BUG-REPORT.ZIP  FS  data  log  bt  btsnoop_hci.log 

Analise o tráfego Bluetooth com o Wireshark

O Wireshark é um analisador de pacotes de rede gratuito e vamos usá-lo para entender a comunicação entre o aplicativo Ozobot Evo e o próprio Ozobot. Você pode abrir os arquivos de log hci recuperados com o relatório de erros.

Definindo filtes em pacotes enviados entre o Raspberry Pi e o Ozobot

No início, costumo percorrer até encontrar algum tráfego entre os dispositivos nos quais estou interessado e definir um filtro para focar nesses dispositivos.

Aqui nós já vemos algo interessante: O Ozobot Evo tem agora um endereço MAC diferente (E4: 70: 7B: 5B: 39: 11), mas continua com o mesmo nome (OzoEvo2). Temos que ter isso em mente quando queremos nos conectar manualmente ao robô de brinquedo mais tarde.

Alterando as cores do LED

Verificando os pacotes do primeiro exemplo (ligando e desligando o LED no topo várias vezes), já podemos ver alguns padrões:

Tráfego Bluetooth para ligar e desligar o led no topo do Ozobot Evo várias vezes

Parece que os comandos para LED começam com o byte hexadecimal 44 seguido por um identificador do LED que consiste em dois bytes (ff ff). Depois disso, há 3 bytes alternando entre todos os 0 e alguns valores para a cor. Ver 3 bytes já é um forte indicador de ter um byte por cor (vermelho, verde, azul).

Outra informação que obtemos deste teste é que a característica destacada na captura de tela é pelo menos o endereço correto para mudar a cor dos LEDs.

Dirigindo o Ozobot Evo

Comparado ao comando do LED, dirigir era muito mais difícil de descobrir.

Tráfego Bluetooth de dirigir o Ozobot Evo em frente

Primeiro de tudo, vemos que há outra característica usada para o comando de condução .

Se olharmos para o valor das mensagens, vemos que há um byte com o valor 40 que aparentemente indica um comando de acionamento . Depois disso, torna-se um pouco mais difícil de entender. Vemos outros 6 bytes, dois deles mudando constantemente, embora eu tenha dirigido apenas o bot. Os dois bytes no final estão em “valor máximo” ff o tempo todo.

Como era difícil entender apenas olhando para cargas úteis, eu transformei os valores hexadecimais em números inteiros e plotei os bytes relevantes para explorar visualmente as cargas úteis .

Plotando os arrays de bytes do comando de acionamento: Id da mensagem no eixo X, valores inteiros dos bytes no eixo Y.

Ver o padrão torna um pouco mais claro o que está acontecendo. Parece que tem pares de 2 por 2 bytes . Um mostrando incrementar e decrementar cargas úteis, o outro mostrando um valor muito estável em toda a amostra.

App Ozobot Evo: Joystick

Depois de algumas tentativas e erros, meu palpite é que vemos as taxas de velocidade aumentando e diminuindo, porque o aplicativo Ozobot Evo está suavizando os comandos que são enviados para o Ozobot se você usar o joystick no aplicativo para direcionar.

Se vemos diferenças entre as rodas, isso se deveu ao fato de ter movido o joystick um pouco para a direita ou para a esquerda. Então, minha conclusão foi que o byte 2 e o byte 4 contêm a velocidade da roda esquerda e direita.

No que diz respeito aos bytes 3 e 5, eles parecem ser uma versão suavizada / cortada dos outros dois bytes. Novamente, após um longo período de tentativa e erro e verificando diferentes fontes online, minha conclusão foi que elas devem ser uma transformação baseada em operadores de bits .

Eventualmente, o conteúdo dos dois últimos bytes é menos difícil de ser entrelaçado quando você tenta enviar comandos e o Ozobot está apenas se movendo por um piscar de olhos: eles contêm a duração desejada da execução do comando em milissegundos.

3) codifique a API em Python

Equipado com o entendimento da parte 2), podemos agora passar para a implementação real da API do Ozobot Evo em Python.

Você pode encontrar o código e alguma documentação adicional em um repositório do Github: https://github.com/raphaelbink/raspberry-pi-ozobot-evo

Procure o dispositivo Bluetooth Ozobot Evo

Na primeira etapa, você precisa encontrar o dispositivo Bluetooth do Ozobot Evo e conectar-se a ele.

Para isso, eu uso o pacote bluepy

 de bluepy import btle 
robôs = []
scanner = btle.Scanner (). withDelegate (btle.DefaultDelegate ())
devices = scanner.scan (10,0)

Esta verificação irá, naturalmente, encontrar todos os dispositivos Bluetooth disponíveis, como telefones celulares, etc. Portanto, temos que percorrer os dispositivos encontrados e procurar aqueles cujos nomes começam com "OzoEvo".

Normalmente, enquanto projetamos dispositivos Bluetooth de engenharia reversa, nós nos concentramos no endereço MAC, mas cada Ozobot usa um monte de endereços MAC diferentes, nos quais está interagindo a cada inicialização.

 para dev em dispositivos: 
para (adtype, desc, value) em dev.getScanData ():
if (adtype == 9 e value.startswith ('OzoEvo')):
print (adtype, dev.addr, valor)
robots.append (Robô (dev.addr, valor))

Cada dispositivo está enviando dados de anúncio , que recuperamos com dev.getScanData() no snippet acima. Para obter o nome, só verificamos o adtype == 9.

Conecte-se ao Ozobot Evo via Bluetooth

A conexão consiste em duas etapas:

  1. Instanciar um objeto periférico
  2. Obtenha a característica certa do dispositivo Bluetooth para enviar comandos para

Instanciar um objeto periférico é bastante simples. Nós apenas temos que saber o endereço MAC do dispositivo de destino:

 _p = btle.Peripheral (robôs [0] ._ mac, btle.ADDR_TYPE_RANDOM) 

Em relação às características corretas , nós as obtivemos de nossa análise com Whireshark (como descrito acima na etapa 2). Temos uma característica para enviar comandos de unidade e outra para todos os outros tipos de comandos:

 TRANSMIT_UUID_DRIVE = "8903136c-5f13-4548-a885-c58779136702" TRANSMIT_UUID_CONTROL = "8903136c-5f13-4548-a885-c58779136703" 
_p_transmit_drive =
self._p.getCharacteristics (uuid = self.TRANSMIT_UUID_DRIVE) [0]
_p_transmit_control =
self._p.getCharacteristics (uuid = self.TRANSMIT_UUID_CONTROL) [0]

Criar comandos de unidade e LED

Tendo a conexão entre o Raspberry Pi e o Ozobot Evo tudo pronto, agora podemos enviar comandos para as características .

Para tornar isso mais conveniente, podemos escrever funções que tratam da transformação de nossos parâmetros para as cadeias de bytes que são a carga real dos comandos:

 unidade de def (self, leftWheel, rightWheel, duração): 
byteArray = []
byteArray.append (64)
byteArray.append (leftWheel e 255)
byteArray.append (leftWheel >> 8 e 255)
byteArray.append (rightWheel e 255)
byteArray.append (rightWheel >> 8 e 255)
byteArray.append (duração e 255)
byteArray.append (duração >> 8 e 255)
comando = bytes (byteArray)
return (comando)
def led (auto, led, vermelho, verde, azul):
byteArray = []
byteArray.append (68)
byteArray.append (led & 255)
byteArray.append (led >> 8 e 255)
byteArray.append (vermelho)
byteArray.append (verde)
byteArray.append (azul)
comando = bytes (byteArray)
return (comando)

Aqui podemos usar o entendimento que obtivemos sobre os payloads de comando da etapa 2. Um passo importante é converter o byteArrays em uma string hexadecimal de escape com a função bytes() . Esta função é nova no Python 3. #, então certifique-se de iniciar seu script python com python3 no seu Rasperry Pi.

Outra informação importante relacionada é que você tem que sudo sua execução de script porque você precisa de acesso root para poder interagir com os dispositivos Bluetooth em seu Raspberry Pi via Python.

Orientar o Ozobot Evo com Python

Tendo tudo isso preparado, há apenas um outro requisito complicado antes que você possa eventualmente controlar seu Ozobot Evo com seu Raspberry Pi via Bluetooth.

Após o arranque, o Ozobot Evo lança internamente algum comportamento aleatório. Eu acho que isso é para fazer parecer mais "vivo" e relacionável. O problema é que você não pode enviar comandos manuais para o Ozobot Evo até que você tenha desabilitado isso. Depois de outra longa sessão de tentativa e erro, encontrei um comando que está parando o comportamento aleatório e faz com que o Ozobot responda aos comandos manuais.

O comando que pára o comportamento aleatório do Ozobot Evo

Portanto, certifique-se de enviar o seguinte comando sempre primeiro:

 _p_transmit_control.write (b " x50  x02  x01") 

Somente após essa etapa, você pode enviar todos os comandos que você gosta para o Ozobot Evo com a função write e fazer com que ele realmente se mova e mude sua cor:

 _p_transmit_drive.write (drive (500,500,1000)) _p_transmit_control.write (led (255,100,100,20)) 

Resumindo

A engenharia reversa de um protocolo Bluetooth não é fácil se você não tiver nenhum conhecimento anterior como eu no início do projeto. Neste momento, gostaria de dar créditos a Kenneth Keiter , cuja lendária sessão de codificação em engenharia reversa de uma luz inteligente me ajudou a entrar no tópico.

Controlar as rodas e os LEDs do Ozobot Evo é apenas a ponta do iceberg em relação às diferentes funções do robô de brinquedo, então eu adoraria ver o que você está descobrindo sobre o protocolo Bluetooth.

Além disso, deixe-me saber sobre suas idéias e projetos construídos em cima da combinação do Ozobot Evo e o Raspberry Pi.

  • Neste artigo, há links afiliados para a Amazon.com