Mochileiro T.I
Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors

TEF6686 no Raspberry: como interagir com o chip

Existem módulos TEF6686 disponíveis no mercado, como o TDQ-230V, que nos possibilitam fazer uma ligação muito simples com placas de prototipagem como Arduíno, ESP32 ou Raspberry Pi via protocolo I2C. Nesse artigo, veja como conectar um desses módulos ao Raspberry Pi de forma descomplicada e como criar um breve programa em Python capaz de abrir comunicação com o TEF6686.

Anteriormente, escrevi um artigo sobre o FMDX Webserver e abordei a integração de equipamentos de rádio baseados no chip TEF6686 com o Raspberry Pi.

Na ocasião, me referi ao TEF6686 como um rádio completo, com tela, botões, bateria, alto-falantes e outros recursos — facilmente encontrado em sites de venda como o AliExpress.

Caso queira, você pode conferi-lo aqui. Além de escrever um pouco sobre o rádio TEF6686, ensino o que é e como montar um servidor FMDX completo conectado a ele.

No artigo de hoje, refiro-me ao chip TEF6686 propriamente dito ao invés do equipamento de rádio que o contém.

Trata-se de um circuito integrado que fornece todas as funcionalidades de um rádio tais como sintonia, volume, filtros e RDS.

No entanto, sozinho, este chip não faz nada. Ele precisa de outros componentes eletrônicos que, dentre outras coisas, tratem RF, gerem clock e cuidem da alimentação.

Para nossa sorte, existe um pequeno módulo que cuida de todas essas necessidades e entrega uma interface eletrônica pronta para uso!

Trata-se do módulo TDQ-230V — uma placa super compacta que contém o chip TEF6686 e todos os componentes eletrônicos extras fundamentais ao seu funcionamento.

Desta forma, podemos integrar facilmente o TEF6686 com placas de desenvolvimento como por exemplo o Raspberry Pi.

Isto abre a possibilidade de criarmos um rádio personalizado, barato e controlado por computador — excelente para o FMDX Webserver.

Ao longo deste artigo, você verá como conectar fisicamente a interface do TDQ-230V à GPIO do Raspberry Pi, bem como prepará-lo para operar no barramento I2C.

Além disso, você também aprenderá como interagir com o TEF6686 em um nível mais baixo com Python, através da biblioteca SMBus2.

Como exemplo, escreverei um pequeno programa que obtém o status de funcionamento e a versão do chip TEF668X contido no módulo TDQ.

Fique atento e boa leitura!

Referência

Antes de mais nada, preciso deixar meu sinceros agradecimentos ao Mirko Pavleski que disponibilizou seu projeto Simple TEF6686 Arduino + PC AM-FM Radio Receiver no Hackster.io.

O projeto dele serviu de referência para a construção deste artigo, cujo objetivo é tornar possível o funcionamento do TEF6686 no Raspberry Pi.

Desta maneira, adaptei as ligações elétricas para o RPi e converti parte do código originalmente escrito em C para Python, mais favorável a este ambiente.

Caso tenha interesse em construir um rádio controlado pelo Arduino, basta acessar o tutorial do Mirko. Ele é bem didático e, assim como eu, você certamente conseguirá fazê-lo funcionar.

Além disso, sugiro que também veja outros projetos dele, existem vários muito interessantes.

Preparando o ambiente

Dados os devidos créditos, vamos iniciar o nosso projeto. Primeiramente, você precisará de um Raspberry Pi, é claro.

Desta vez, ao invés de usar um RPi3 como fiz no artigo do FMDX, resolvi usar um RPi4. No entanto, sinta-se à vontade para utilizar o RPi3 ou posteriores.

Com o Raspberry em mãos, vamos precisar do Raspberry OS Lite adequado para o Pi escolhido. A instalação do SO é bastante simples com Pi Imager, portanto tomarei a liberdade de não abordá-la aqui.

Porém, caso precise de ajuda nessa etapa, basta seguir o passo a passo da instalação do RPi OS no artigo do FMDX WebServer, na seção Preparando o Raspberry Pi.

Um vez concluída a instalação, trabalharemos com o Raspberry via SSH. Este tema também já foi abordado no artigo anterior e portanto não o trarei novamente para não ficar cansativo.

Caso precise, ensino como acessar o RPi via SSH na seção Primeiro acesso com SSH, no mesmo artigo.

Superada a fase do Raspberry, você precisará do protagonista de hoje – o módulo TDQ-230V. Você o encontra facilmente no Aliexpress e por um preço bem inferior a um rádio TEF6686 completo.

TDQ-230V

Além disso, você poderá precisar de um circuito conversor de nível lógico, responsável apenas por converter a tensão de operação entre o barramento I2C do RPi e do TDQ:

Conversor Nível Lógico

Por fim, você precisará de alguns cabos jumper. Minha sugestão é adquirir tudo junto no Aliexpress, pois provavelmente você conseguirá um preço um pouco melhor e frete gratuito.

Para o propósito deste artigo, esses itens são suficientes. Vamos recapitular o material necessário:

  • Raspberry Pi 3 ou superior (com fonte de alimentação e cartão SD)
  • Módulo TDQ-230V
  • *Possivelmente um conversor de nível lógico 2 ou 4 canais (abaixo falo mais sobre isso)
  • Cabos de jumper

Conexões Elétricas

O esquema das conexões elétricas é relativamente simples, o que atrapalha um pouco é a adição do módulo conversor de nível lógico.

Mesmo assim, embora mais trabalhoso, não é nada complicado. Veja abaixo como fica a montagem do projeto:

Decidi incluir este conversor por segurança, uma vez que não encontrei informações confiáveis sobre a tensão de operação do barramento I2C dos TDQ-230V. (existem algumas versões diferentes)

O que encontrei é que alguns deles operam com o barramento em 3.3v e outros em 5v. Como o Raspberry Pi opera apenas em 3.3v, a tensão de 5v o danificaria.

Já que o conversor de nível lógico é uma peça muito barata, vale a pena adicionar essa camada de segurança.

No entanto, caso queira simplificar as coisas, basta investigar a ligação elétrica com o uso de um multímetro.

Ao alimentar o módulo, se as portas SDA/SCL estiverem energizadas com 3.3V, você pode dispensar o uso do conversor e conectar o TDQ diretamente no Raspberry Pi.

No meu caso, a minha versão do módulo TDQ-230V opera em 3.3v. Portanto, não precisei da conversão de tensão.

Feita essa importante observação, vamos recapitular o esquema das conexões elétricas, incluindo o conversor:

  • Pino 1 RPi (3.3v ) –> Pino LV do conversor
  • Pino 2 RPi (5v) –> Pino 5v do TDQ-230v
  • Pino 3 RPi (GPIO 2) –> Pino LV1 do conversor
  • Pino 4 RPi (5v) –> Pino HV do conversor
  • Pino 5 RPi (GPIO 3) –> Pino LV2 do conversor
  • Pino 6 RPi (GND) –> Pino GND do TDQ-230v
  • Pino 9 RPi (GND) –> Pino GND do conversor
  • Pino 14 RPi (GND) –> Pino GND do TDQ-230v
  • Pino 34 RPi (GND) –> Pino GND do conversor
  • Pino HV1 do conversor –> Pino SDA do TDQ-230V
  • Pino HV2 do conversor –> Pino SCL do TDQ-230V

Primeiro Teste do TEF6686 com Raspberry

Após tudo devidamente conectado, precisamos habilitar o barramento I2C no Raspberry Pi, um vez que ele não vem ativo por padrão.

O processo é bastante simples, acesse seu RPi via SSH e digite o seguinte comando:

sudo raspi-config

Você verá o seguinte:

Selecione a opção 3 Interface Options e tecle enter:

Em seguida, selecione a opção I5 I2C e tecle enter novamente:

Confirme pressionando enter sobre <Yes> e como resultado, você verá o seguinte:

Confirme com <Ok> e saia do raspi-config. Quando habilitado, o Raspberry Pi OS lista o barramento I2C como um novo dispositivo dentro de /dev.

Assim sendo, se listarmos o diretório /dev com o filtro *i2c*, devemos ver o seguinte:

ls /dev/*i2c*

O barramento que queremos normalmente é o i2c-1, conforme a imagem acima nos mostra. Caso você não tenha certeza, não se preocupe. Logo abaixo iremos confirmar essa informação.

Endereçamento I2C

O próximo passo é tentar fazer o primeiro contato com o TEF6686 pedindo que Raspberry Pi envie uma solicitação de address probing ao seu endereço no barramento.

Como o I2C, em teoria, pode trabalhar com aproximadamente 112 dispositivos no mesmo barramento, não sabemos ainda qual endereço pertence a ele.

O que vamos fazer agora é rodar um comando do pacote i2c-tools chamado i2cdetect para consultar todos os endereços do barramento e retornar aqueles cujos dispositivos respondem ao address probing.

Primeiramente atualize a lista de pacotes APT e em seguida, instale o i2c-tools da seguinte forma:

sudo apt update && sudo apt install i2c-tools

Agora, digite o comando i2cdetect conforme abaixo:

i2cdetect -y 1

O parâmetro 1 refere-se ao número do barramento I2C do Raspberry (/dev/i2c-1). Como resultado, você deverá ver o seguinte:

Veja que no cruzamento da coluna 4 com a linha 60 existe o valor 64. Isso significa que o TEF6686 respondeu com sucesso ao address probing do i2cdetect informando seu endereço.

Por padrão, os chips TEF6686 costumam vir com o endereço hexadecimal 64 (0x64). Como temos somente ele conectado ao barramento I2C, podemos nos certificar que o endereço bate.

Além disso, também podemos confirmar que o barramento ativo é de fato o número 1. Caso não fosse, o i2cdetect retornaria erro.

Neste cenário, troque o número 1 pelo número que o comando ls /dev/*i2c* retornou no passo anterior. Por exemplo, caso você veja /dev/i2c-2, poderia tentar rodar i2cdetect -y 2 e assim por diante.

Como o TEF6686 se comunica

Conforme vimos anteriormente, o TEF6686 utiliza o protocolo de comunicação I2C para trocar mensagens com seu controlador, por exemplo o Raspberry Pi.

Porém, o que mais nos interessa é como construir as mensagens e não como transmiti-las, afinal o I2C já se encarrega disso.

Nesse sentido, o chip possui uma API que dita as regras de construção das mensagens que desejamos enviar e receber pelo barramento.

Essa API é organizada em módulos, que são blocos funcionais independentes. Existem quatro módulos principais: FM (rádio FM), AM (rádio LW, MW e SW), ÁUDIO (processamento de áudio) e APPL (controle de sistema e aplicativo).

Cada módulo contém comandos que são responsáveis por operações específicas, como por exemplo sintonizar uma determinada frequência. (comando Tune_To)

Tanto módulos quanto comandos possuem identificadores numéricos cuja finalidade é simplificar a construção das mensagens.

Por exemplo, o módulo FM possui o ID 32. Já o módulo AM tem o ID 33. O comando Tune_To recebe o ID 1 e assim por diante.

Os comandos, por sua vez, possuem índices que permitem a escrita ou leitura de partes específicas de parâmetros, sem a necessidade de transmitir ou ler o conjunto completo.

Os índices, no entanto, não são suportados em todas as versões do TEF6686, como é o meu caso. Dessa maneira, seu valor ficará fixo em 1.

Por fim, cada índice pode conter zero ou mais parâmetros, dependendo do comando correspondente.

Em um primeiro momento, a compreensão destes conceitos pode parecer um pouco complicada. Vamos ver um exemplo teórico para ilustrar o que vimos até agora.

Sintonizar frequência FM

Suponha que queremos sintonizar a frequência FM de 89,30 MHz. O primeiro passo é identificar qual módulo é responsável por essa operação.

Já vimos que o TEF6686 possui quatro módulos distintos e um deles é o módulo FM, que agrupa todos os comandos relacionados a essa faixa de frequência. Seu código identificador é o número 32.

Em seguida, precisamos saber qual comando realiza operações de sintonia. Trata-se do Tune_To, cujo código identificador é o número 1.

Após o comando temos que informar o índice, que no nosso caso sempre será 1, uma vez que nosso chip não possui suporte a este recurso.

E por último passamos os dois parâmetros obrigatórios para este comando: modo e frequência. Utilizaremos o modo 1 (Preset) e a frequência 8930 (decimal).

Portanto, o comando completo ficaria da seguinte forma:

32, 1, 1, 1, 8930

Onde:

  • 32 – módulo FM
  • 1 – comando Tune_To
  • 1 – índice (padrão)
  • 1 – primeiro parâmetro do comando Tune_To (Modo)
  • 8930 – segundo parâmetro do comando Tune_To (Frequência)

Ao enviar essa mensagem via barramento I2C, o TEF6686 entende que deve sintonizar a frequência FM 89,30 MHz.

Esse exemplo vai ficar ainda mais claro mais adiante, quando o implementarmos em Python. Nada melhor que a prática para entender a teoria.

Existem dezenas de comandos definidos na API para várias funcionalidades do chip. Todos eles estão bem documentados no manual do fabricante.

Você pode encontrá-lo facilmente na internet através do Google.

Sequência de Inicialização

Uma vez compreendida a dinâmica da comunicação entre o controlador e o TEF6686, precisamos entender o que acontece com o chip assim que é ligado.

No momento da energização, ele entra no chamado Boot State. Nesse estágio, ele aceita somente um comando especial de inicialização e rejeita qualquer outro.

Como esse comando já vem definido pelo fabricante, a nossa tarefa é apenas montar a mensagem e enviá-la pelo barramento I2C.

Após o comando de inicialização, o TEF entra em Idle State e ativa o módulo 64 Appl (Aplicação), passando a aceitar seus respectivos comandos.

Neste estágio, precisamos informar o clock de referência para o chip continuar o processo de inicialização e por fim alcançar o Active State, onde todos os módulos tornam-se ativos.

Em outras palavras, o TEF6686 somente aceitará todos os comandos neste último estágio. Sendo assim, a inicialização é um processo obrigatório a cada vez que o chip é ligado.

Uma vez que o comando de inicialização possui uma mensagem muito grande, fica praticamente impossível mostrá-lo aqui.

No entanto, deixarei o código Python com a sequência completa de inicialização disponível para download mais adiante.

Dessa maneira você poderá facilmente entender como isso tudo funciona.

Raspberry Pi e TEF6686 com Python

Conforme prometido no início do artigo, vamos a uma breve implementação em Python para demonstrar o funcionamento do TEF6686 no Raspberry.

Para fazer a comunicação I2C entre os dois vamos utilizar a biblioteca SMBus2, facilmente instalável via Pip.

Contudo, primeiramente vamos criar um ambiente virtual para organizarmos as coisas. Crie uma pasta qualquer e dentro dela rode o seguinte:

python3 -m venv venv

Não se esqueça de ativá-lo:

source ./venv/bin/activate

Agora vamos instalar o SMBus2:

pip3 install smbus2

Isso é o suficiente para começarmos o projeto. À seguir veremos como implementar a sequência de inicialização.

Inicialização do TEF6686

Para inicializar o TEF6686, devemos realizar transmissões sequenciais de mensagens com pares de valores hexadecimais definidos pelo fabricante do chip.

Esses valores sempre terão tamanho de 1 byte cada. Portanto, cada par de valor (também chamado de palavra) sempre terá 2 bytes.

Cada mensagem tem início com um valor identificador (1C ou 1B). Em seguida, podem aparecer uma ou mais palavras hexadecimais, cuja quantidade varia conforme o identificador.

As mensagens com identificador 1C sempre terão como conteúdo uma única palavra. Por outro lado, as mensagens do tipo 1B podem conter qualquer quantidade de palavras em seu conteúdo.

No nosso exemplo, utilizei o tamanho de 12 palavras para as mensagens 1B, o mesmo padrão que Mirko Pavleski adotou no projeto dele.

Veja abaixo como é uma mensagem 1C e como ela é composta:

E como é uma mensagem 1B:

A sequência completa de inicialização do TEF6686 envolve várias dezenas de mensagens e portanto, é inviável colocá-la aqui!

No entanto deixarei nesse link o arquivo Python contendo toda a sequência pronta para uso. Note que no código os valores são colocados individualmente na estrutura de dados e não como pares.

Precisa ser desta forma porque o protocolo I2C transmite apenas 1 byte por vez. No entanto, a estrutura de cada mensagem continua obedecendo a regra identificador + par(es) de valor(es).

Uma vez implementada a sequência de inicialização, precisamos implementar um método que seja capaz de enviar todos esses valores para o TEF6686.

Faremos isso logo abaixo através da biblioteca SMBus2.

Enviar dados via I2C

Nada adianta ter uma bela estrutura de dados que represente a sequência de inicialização do TEF se não pudermos enviá-la pelo barramento de comunicação.

A biblioteca SMBus2 existe exatamente para essa finalidade. Com uma interface muito simples e poucas linhas de código, podemos facilmente realizar operações de leitura e escrita de dados via I2C.

Para enviar dados (escrever), precisamos somente informar o endereço do dispositivo e fornecer o conteúdo mensagem.

Já sabemos que o TEF6686 tem o endereço 0x64 e temos mensagens prontas para enviar. Dessa maneira, precisamos de apenas 3 linhas de código para exemplificar o processo de envio:

with SMBus(1) as bus: # SMBus(1) representa /dev/i2c-1
        # Cria a mensagem endereçada a 0x64 com o conteúdo 1C 0000
        msg = i2c_msg.write(0x64, [0x1C, 0x00, 0x00])
        # Envia a mensagem
        bus.i2c_rdwr(msg)

Veja que peguei como exemplo somente a primeira linha da nossa sequência de inicialização 1C 0000. Todas as demais mensagens seguirão o mesmo modelo de envio.

Nesse sentido, basta um simples FOR para percorrer toda a lista DSP_INIT do TEF_INIT.py:

import TEF_INIT
import time
from smbus2 import SMBus, i2c_msg

TEF_ADDR = 0x64

def write_buffer(buf):
    with SMBus(1) as bus:
        msg = i2c_msg.write(TEF_ADDR, buf)
        bus.i2c_rdwr(msg)

for buffer in TEF_INIT.DSP_INIT:
        length = len(buffer)
        if length == 2 and buffer[0] == 0xff:
            time.sleep(buffer[1]/1000)
        else:    
           write_buffer(buffer)

Esse trecho de código já é suficiente para inicializar o TEF6686 no Raspberry.

A única novidade nele é o sleep() de alguns milissegundos que ocorre em dois momentos da inicialização e são obrigatórios segundo o fabricante.

Depois de rodar esse código, você deve estar se perguntando: “E como sei se isso deu certo?”. Sabemos se tudo ocorreu conforme o esperado solicitando ao chip seu código de status.

Mas antes disso, precisamos aprender como receber dados do TEF6686.

Receber dados via I2C

O recebimento de dados com o SMBus2 é bastante semelhante ao processo de envio, exigindo apenas poucas linhas de código:

with SMBus(1) as bus: # SMBus(1) = /dev/i2c-1
        # Cria mensagem vazia
        # 0x64 = endereço TEF
        # 2 = número de bytes que desejamos ler
        msg = i2c_msg.read(0x64, 2)
        # Recebe a mensagem
        bus.i2c_rdwr(msg)
        # Converte os dados crus para bytes e os coloca em uma lista
        response = list(bytes(msg))

O exemplo acima solicita ao dispositivo 0x64, nosso TEF6686, o envio de dois bytes de dados (uma palavra), localizados em seu registrador.

É importante salientar que o TEF fornece uma palavra de informação por vez, ou seja, em pares de valores que totalizam dois bytes.

Assim sendo, sempre utilizaremos múltiplos de dois para definir o número de bytes que desejamos ler no método read() do SMBus.

Por exemplo, se queremos ler uma mensagem de três palavras, solicitamos ao SMBus2 a leitura de seis bytes. (2 bytes por palavra)

Além disso, precisamos ficar atentos ao meio de transmissão I2C. Conforme já vimos, ele trabalha byte a byte, enviando ou recebendo um único byte por vez.

Isso significa que a informação do exemplo acima, cujo tamanho é de dois bytes, chegará na lista da variável response em dois pedaços de um byte cada.

Precisamos concatenar esses dois pedaços para recompor a informação originalmente fornecida pelo TEF6686.

Tudo muito confuso na teoria, então vamos colocar em prática esses conceitos solicitando ao TEF seu código de status:

from smbus2 import SMBus, i2c_msg

TEF_ADDR = 0x64
# Monta mensagem que pede o código de status
# 64=Módulo (Appl), 128=Comando(get_status) e 1=Index
write = i2c_msg.write(TEF_ADDR, [64, 128, 1])
# Cria mensagem vazia com tamanho de 2 bytes (1 palavra)
# para receber os dados
read = i2c_msg.read(TEF_ADDR, 2)
with SMBus(1) as bus:
    # Envia solicitação e recebe os dados como resposta
    bus.i2c_rdwr(write, read)
# Converte dados crus para bytes
bytes_data = bytes(read)
# Monta a palavra (concatena o par de bytes)
word = (bytes_data[0] << 8) | bytes_data[1]
print(f'O código de status é:{word}')

O programa acima, quando executado após a inicialização do 6686, deverá retornar o código 2 (active state). Isso quer dizer que o chip está pronto para operar.

Observe que desta vez fizemos uma operação de envio e recebimento de dados de uma única vez. Esse recurso é muito útil visto que é bem comum solicitar informações ao TEF6686.

Além disso, veja também como fazemos o concatenação de bytes. Pegamos o primeiro byte, deslocamos oito bits à esquerda e aplicamos o resultado com o bitwise OR no segundo byte.

Não entendeu nada? A concatenação é realmente complicada, não se preocupe!

Como funciona a concatenação

Em Python e diversas outras linguagens, operadores bitwise servem para manipular bits individuais.

No nosso exemplo, o TEF precisa enviar o decimal 2 e monta essa informação em uma palavra de 16 bits (2 bytes), ou seja, 0000000000000010.

No entanto, o protocolo I2C consegue enviar apenas 1 byte por vez. Portanto, o próprio chip quebra a mensagem em duas partes iguais de 8 bits cada: 00000000 e 00000010.

O Raspberry precisa reconstruir a mensagem enviada pelo TEF6686, de modo que ela volte a ter 16 bits e tenha sua integridade restaurada.

Nesse sentido, pegamos primeiro byte recebido (00000000) e o deslocamos oito bits à esquerda. Dessa maneira, ele passa a ser 0000000000000000 e ter 16 bits de tamanho.

O segundo byte, por sua vez, (00000010) passa pela operação OR com o primeiro byte. Portanto temos:

0000000000000000 | 00000010 = 0000000000000010

O resultado disso também é 2, mas agora com o comprimento de uma palavra (2 bytes), igual a mensagem original montada dentro do TEF6686.

Com o intuito de esclarecer ainda mais o assunto, veja este outro exemplo. Suponha que o TEF queira nos enviar uma mensagem com o valor decimal 300.

Ele escreverá em sua memória interna a representação do número 300 em binário: 0000000100101100. (16 bits = 2 bytes = 1 palavra)

Em seguida, ele dividirá em duas partes de oito bits cada para transmitir via I2C: 00000001 e 00101100.

Perceba agora que, se não montarmos de volta os valores recebidos, a informação original se perde, pois convertendo individualmente para decimal temos: 00000001 = 1 e 00101100 = 44.

Porém, após o processo de concatenação, tudo fica certo:

0000000100000000 | 00101100 = 0000000100101100 (300)

Exemplo completo Raspberry Pi e TEF6686

Agora que já sabemos como estabelecer comunicação entre o Raspberry Pi e o TEF6686 com o SMBus2 do Python, podemos organizar tudo em um único programa.

O código que veremos a seguir será capaz de inicializar o chip (se necessário) e identificar sua versão de hardware e software.

Primeiramente crie um arquivo chamado constants.py, cole o conteúdo abaixo e copie-o para seu ambiente virtual.

Este arquivo apenas traduzirá os códigos recebidos para o nome da versão de seu TEF:

DEVICE_TYPE = {
    9: "TEF668X 'Lithio' series"
}

VARIANT = {
    1: "TEF6687 'Lithio FMSI'",
    3: "TEF6689 'Lithio FMSI DR'",
    9: "TEF6688 'Lithio DR'",
    14: "TEF6686 'Lithio'",
}

Agora cole o que código abaixo em outro arquivo com o nome que desejar (sugiro main.py) e deixe-o na mesma pasta que constants.py. Não se esqueça de copiar também o TEF_INIT.py do link anterior.

import TEF_INIT
from constants import DEVICE_TYPE, VARIANT
from smbus2 import SMBus, i2c_msg
import time

TEF_ADDR = 0x64

def write_buffer(buf):
    with SMBus(1) as bus:
        msg = i2c_msg.write(TEF_ADDR, buf)
        bus.i2c_rdwr(msg)

def regroup(raw_data):
    bytes_data = bytes(raw_data)
    words = []
    for i in range(0, len(bytes_data), 2):
        word = (bytes_data[i] << 8) | bytes_data[i+1]
        words.append(word)
    return words

def get_command(mdl, cmd, idx, words):
    write = i2c_msg.write(TEF_ADDR, [mdl, cmd, idx])
    read = i2c_msg.read(TEF_ADDR, 2 * words)
    with SMBus(1) as bus:
        bus.i2c_rdwr(write, read)
    return read

def get_operation_status():
    response = get_command(64, 128, 1, 1)
    return regroup(response)[0]

def get_identification():
    response = get_command(64, 130, 1, 3)
    data = list(bytes(response))
    return {
        'Type': DEVICE_TYPE.get(data[0], 'Unknown'),
        'Variant': VARIANT.get(data[1],'Unknown'),
        'Hardware Version': f'{data[2]}.{data[3]}', 
        'Firmware Version': f'{data[4]}.{data[5]}'
    }

def dsp_init():
    for buffer in TEF_INIT.DSP_INIT:
        length = len(buffer)
        if length == 2 and buffer[0] == 0xff:
            time.sleep(buffer[1]/1000)
        else:    
           write_buffer(buffer) 

while(True):
    status = get_operation_status()
    if (status == 0):
        print('Initializing...', end="")
        dsp_init()
        time.sleep(1)
        print('Done!', end='\n')
    else:
        break

print(get_identification())

Por fim execute main.py e descubra qual é a sua versão do TEF668X!

O programa possui todos os conceitos que vimos até agora: sequência de inicialização, envio, recebimento e concatenação de dados.

Ele apenas está melhor estruturado, favorecendo a criação de funções que implementem os comandos disponíveis no manual do chip.

Sinta-se à vontade para alterá-lo como quiser e melhorar ainda mais o funcionamento de seu Raspberry Pi com o TEF6686!

Conclusão

Neste artigo você pôde ver que fazer um TEF6686 funcionar com uma SBC como o Raspberry Pi não é algo tão extraordinário.

Apesar de necessitar de alguns conceitos mais específicos, a maior parte do trabalho é absorvida pela biblioteca SMBus2 que abstrai muito das complexidades do protocolo I2C.

Penso que a maior dificuldade está na escassez de informações do próprio TEF6686, que ficam limitadas apenas ao manual do usuário, não tendo muitas outras opções como em fóruns, artigos, etc.

Mesmo assim, com um pouco de paciência e dedicação, é totalmente possível compreender suas características e seu funcionamento apenas lendo a documentação.

O material que apresentei aqui é uma breve introdução sobre o tema e serve como pontapé inicial para a criação de outros projetos.

Minha pretensão é desenvolver um programa, baseado no que o Mirko Pavleski fez para o Arduino, capaz de suportar todas as funcionalidades do TEF6686 no Raspberry Pi.

Desta maneira, poderíamos rodar o FMDX Webserver no Raspberry e fazê-lo se conectar ao TEF6686, criando assim um ambiente 100% integrado.

Isso seria excelente para quem deseja fazer parte da comunidade FMDX, visto que dispensa o uso de um rádio completo que infelizmente é caro para nós brasileiros.

Portanto fique ligado nos próximos artigos, pois pretendo publicar os passos e o código de mais esse projeto!

Espero ter ajudado!

Até a próxima!