Índice:

Indo além do Standard Firmata - revisitado: 5 etapas
Indo além do Standard Firmata - revisitado: 5 etapas
Anonim
Indo além do Standard Firmata - revisitado
Indo além do Standard Firmata - revisitado

Há pouco tempo, fui contatado pelo Dr. Martyn Wheeler, um usuário do pymata4, para orientação sobre como adicionar suporte para o sensor de umidade / temperatura DHT22 à biblioteca do pymata4. A biblioteca pymata4, em conjunto com sua contraparte Arduino, FirmataExpress, permite aos usuários controlar e monitorar seus dispositivos Arduino remotamente. Após algumas rodadas de trocas de e-mail, o Dr. Wheeler foi bem-sucedido em modificar o pymata4 e o FirmataExpress. Como resultado, o suporte para os sensores DHT22 e DHT11 agora é uma parte padrão do pymata4 e FirmataExpress.

Em maio de 2014, escrevi um artigo sobre como adicionar suporte ao Firmata para dispositivos adicionais. Refletindo sobre aquele artigo, percebi o quanto mudou desde que coloquei a caneta no papel para escrever aquele artigo. Além deste artigo, o Dr. Wheeler documentou seus esforços, e você também pode verificar isso.

FirmataExpress é baseado em StandardFirmata e a estrutura de diretórios StandardFirmata evoluiu. Além disso, a API pymata4 também é um pouco diferente da API PyMata original de 2014. Achei que seria o momento perfeito para revisitar e atualizar esse artigo. Usando o trabalho do Dr. Wheeler como base, vamos explorar como estender a funcionalidade do pymata4 / FirmataExpress.

Antes de começar - algumas informações básicas sobre o Arduino / Firmata

Então, o que é Firmata? Citando a página da Web do Firmata, "Firmata é um protocolo genérico para comunicação com microcontroladores de software em um computador host."

O Arduino Firmata usa uma interface serial para transportar informações de comando e relatório entre um microcontrolador Arduino e um PC, normalmente usando um link serial / USB definido para 57600 bps. Os dados transferidos por meio desse link são binários e o protocolo é implementado em um modelo cliente / servidor.

O lado do servidor é carregado em um microcontrolador Arduino na forma de um esboço do Arduino. O esboço StandardFirmata, incluído com o IDE do Arduino, controla os pinos de E / S do Arduino, conforme comandado pelo cliente. Ele também relata alterações de pinos de entrada e outras informações de relatório de volta para o cliente. FirmataExpress é uma versão estendida do StandardFirmata. Ele funciona a uma velocidade de link serial de 115200 bps.

O cliente Arduino usado neste artigo é o pymata4. É um aplicativo Python executado em um PC. Ele envia comandos e recebe relatórios do servidor Arduino. Como o pymata4 é implementado em Python, ele é executado em computadores Windows, Linux (incluindo Raspberry Pi) e macOS.

Por que usar o Firmata?

Os microcontroladores Arduino são pequenos dispositivos maravilhosos, mas os recursos do processador e da memória são um tanto limitados. Para aplicativos que exigem muito do processador ou da memória, geralmente há pouca escolha a não ser descarregar a demanda de recursos em um PC para que o aplicativo seja bem-sucedido.

Mas essa não é a única razão para usar o StandardFirmata. Ao desenvolver aplicativos Arduino mais leves, um PC pode fornecer ferramentas e recursos de depuração não disponíveis diretamente em um microcontrolador Arduino. Usar um cliente e um servidor "fixos" ajuda a limitar a complexidade do aplicativo a um PC, que é mais facilmente gerenciado. Depois que o aplicativo é aperfeiçoado, ele pode ser traduzido em um esboço do Arduino personalizado e autônomo.

Por que usar o pymata4?

Sendo seu autor, é claro, sou tendencioso. Dito isso, é o único cliente Firmata baseado em Python que tem sido mantido continuamente nos últimos anos. Ele fornece uma API intuitiva e fácil de usar. Além de esboços baseados em StandardFirmata, ele suporta Firmata sobre WiFi para dispositivos como o ESP-8266 ao usar o esboço StandardFirmataWifI.

Além disso, o pymata4 foi projetado para ser facilmente estendido por um usuário para oferecer suporte a sensores e atuadores adicionais não suportados atualmente pelo StandardFirmata.

Etapa 1: Compreendendo o protocolo Firmata

Compreendendo o Protocolo Firmata
Compreendendo o Protocolo Firmata

O protocolo de comunicação do Arduino Firmata é derivado do protocolo MIDI, que usa um ou mais bytes de 7 bits para representar os dados.

Firmata foi projetado para ser extensível pelo usuário. O mecanismo que fornece essa extensibilidade é o protocolo de mensagens System Exclusive (SysEx).

O formato de uma mensagem SysEx, conforme definido pelo Protocolo Firmata, é mostrado na ilustração acima. Ele começa com um byte START_SYSEX com um valor fixo de hexadecimal 0xF0 e é seguido por um byte de comando SysEx exclusivo. O valor do byte de comando deve estar no intervalo hexadecimal 0x00-0x7F. O byte de comando é seguido por um número não especificado de bytes de dados de 7 bits. Finalmente, a mensagem é encerrada com um byte END_SYSEX, com um valor fixo de hexadecimal 0xF7.

Codificação / decodificação de dados Firmata

Visto que a parte dos dados do usuário de uma mensagem SysEx consiste em uma série de bytes de 7 bits, você pode se perguntar como se representa um valor maior que 128 (0x7f)? O Firmata codifica esses valores desmontando-os em vários blocos de bytes de 7 bits antes que os dados sejam empacotados no link de dados. O byte menos significativo (LSB) de um item de dados é enviado primeiro, seguido por componentes cada vez mais significativos do item de dados por convenção. O byte mais significativo (MSB) do item de dados é o último item de dados enviado.

Como é que isso funciona?

Digamos que desejamos incorporar um valor 525 na porção de dados de uma mensagem SysEx. Como um valor de 525 é claramente maior do que um valor de 128, precisamos dividi-lo ou desmontá-lo em "blocos" de bytes de 7 bits.

Aqui está como isso é feito.

O valor de 525 em decimal é equivalente ao valor hexadecimal de 0x20D, um valor de 2 bytes. Para obter o LSB, mascaramos o valor fazendo o AND com 0x7F. As implementações "C" e Python são mostradas abaixo:

// Implementação "C" para isolar LSB

int max_distance_LSB = max_distance & 0x7f; // mascarar o byte inferior # implementação Python para isolar LSB max_distance_LSB = max_distance & 0x7F # mascarar o byte inferior

Após o mascaramento, max_distance_LSB conterá 0x0d. 0x20D & 0x7F = 0x0D.

Em seguida, precisamos isolar o MSB para este valor de 2 bytes. Para fazer isso, vamos deslocar o valor de 0x20D para a direita, 7 casas.

// Implementação de "C" para isolar MSB de valor de 2 bytes

int max_distance_MSB = max_distance >> 7; // desloca o byte de ordem superior # implementação do Python para isolar MSB do valor de 2 bytes max_distance_MSB = max_distance >> 7 # shift para obter o byte superior Após o deslocamento, max_distance_MSB conterá um valor de 0x04.

Quando os dados empacotados "fragmentados" são recebidos, eles precisam ser remontados em um único valor. Aqui está como os dados são remontados em "C" e Python

// Implementação de "C" para remontar os 2 bytes, // Valores de 7 bits em um único valor int max_distance = argv [0] + (argv [1] << 7); # Implementação do Python para remontar os valores de 2 bytes, # 7 bits em um único valor max_distance = data [0] + (data [1] << 7)

Após a remontagem, o valor mais uma vez é igual a 525 decimal ou 0x20D hexadecimal.

Este processo de desmontagem / remontagem pode ser executado pelo cliente ou servidor.

Etapa 2: vamos começar

O suporte a um novo dispositivo requer mudanças no servidor residente do Arduino e no cliente Python residente no PC. O trabalho do Dr. Wheeler será usado para ilustrar as modificações necessárias.

Talvez a etapa mais importante seja decidir se deseja integrar uma biblioteca de dispositivos de suporte existente no lado Arduino da equação ou escrever a sua própria. Recomenda-se que, se você puder encontrar uma biblioteca existente, seja muito mais simples usá-la do que escrever a sua própria do zero.

Para suporte a dispositivos DHT, o Dr. Wheeler baseou seu código de extensão na biblioteca DHTNew. Muito habilmente, o Dr. Wheeler dividiu a funcionalidade da biblioteca DHTNew entre os lados do Arduino e pymata4 da equação para fornecer bloqueio mínimo no lado do Arduino.

Se olharmos para DHTNew, ele executa todos os seguintes:

  • Define o modo de saída digital do pino selecionado.
  • Registra um sinal codificado para recuperar os valores mais recentes de umidade e temperatura.
  • Verifica e relata quaisquer erros.
  • Calcula os valores de temperatura e umidade legíveis a partir dos dados brutos recuperados.

Para manter as coisas o mais eficientes possível no lado do FirmataExpress, o Dr. Wheeler transferiu as rotinas de conversão de dados do Arduino para o pymata4.

Etapa 3: Modificando FirmataExpress para suporte DHT

A árvore de diretórios FirmataExpress

Abaixo estão todos os arquivos que compõem o repositório FirmataExpress. Esta árvore é idêntica à de StandardFiramata, apenas que alguns dos nomes de arquivo refletem o nome do repositório.

Os arquivos que precisam de modificação são aqueles que têm um asterisco (*) ao lado deles.

FirmataExpress

├── * Boards.h

├── exemplos

│ └── FirmataExpress

│ ├── boardx

│ ├── * FirmataExpress.ino

│ ├── LICENSE.txt

│ └── Makefile

├── * FirmataConstants.h

├── * FirmataDefines.h

├── FirmataExpress.cpp

├── FirmataExpress.h

├── FirmataMarshaller.cpp

├── FirmataMarshaller.h

├── FirmataParser.cpp

└── FirmataParser.h

Vejamos cada um dos arquivos e as alterações feitas.

Boards.h

Este arquivo contém definições de macro do tipo pino para cada um dos tipos de placa suportados. Ele define o número máximo de dispositivos com suporte quando mais de um dispositivo precisa ser compatível.

Para o dispositivo DHT, até 6 dispositivos podem ser conectados ao mesmo tempo e este valor é definido como:

#ifndef MAX_DHTS

#define MAX_DHTS 6 #endif

Além disso, as macros do tipo pino podem ser definidas opcionalmente para o novo dispositivo, seja para todos os tipos de placa ou apenas aquelas que são do seu interesse. Essas macros são usadas principalmente para fins de relatório e não são usadas para controlar os dispositivos. Essas macros definem os pinos que suportam o dispositivo:

# define IS_PIN_DHT (p) (IS_PIN_DIGITAL (p) && (p) - 2 <MAX_DHTS)

Bem como uma macro para definir uma conversão pin-number.

#define PIN_TO_DHT (p) PIN_TO_DIGITAL (p)

FirmataConstants.h

Este arquivo contém o número da versão do firmware, que você pode querer modificar para manter o controle de qual versão carregou em seu Arduino. Ele também contém os valores da mensagem Firmata, incluindo as mensagens Firmata SysEx.

Você precisará atribuir uma nova mensagem ou conjunto de mensagens para o seu dispositivo neste arquivo. Para o DHT, duas mensagens foram adicionadas. Um configura um pino como um pino “DHT” e o outro, como uma mensagem repórter, ao enviar os dados DHT mais recentes de volta para o cliente.

estático const int DHT_CONFIG = 0x64;

const estático int DHT_DATA = 0x65;

Os modos de pin também são especificados neste arquivo. Para o DHT, um novo modo de pino foi criado:

static const int PIN_MODE_DHT = 0x0F; // pino configurado para DHT

Ao adicionar um novo modo de pin, o TOTAL_PIN_MODES deve ser ajustado:

estático const int TOTAL_PIN_MODES = 17;

FirmataDefines.h

Este arquivo deve ser atualizado para refletir as novas mensagens adicionadas a FirmataConstants.h:

#ifdef DHT_CONFIG # undef DHT_CONFIG #endif #define DHT_CONFIG firmata:: DHT_CONFIG // Solicitação DHT #ifdef DHT_DATA #undef DHT_DATA #endif #define DHT_DATA firmata:: DHT_CONFIG // DHT request #ifdef DHT_DATA #undef DHT_DATA #endif #define DHT_DATA firmata:: DHT_DATADADE # reply_MODHTDHT # reply_defineHTODHT # PIN_MODHTDHT #HTundef # PIN_MODHTDHTODHTDHT_HT_HTOD_HTOD_HTOD_HTOD_HTOD_HTDHT_HTOD_HTODHTDHTDHTDHT #HT_HT_HT_HTODHT #HTDHT_HTHT_HT_HTHTDHT -HT_HTHT_HTHT -HT -HTDHTHTDHT #HTHT -HT_HTHT -HT_HTHT -HT #HT -HT #HTHT -HT -HTHT_HT_HTHT_HTHT_HTHT -:: PIN_MODE_DHT

FirmataExpress.ino

Nesta discussão, cobriremos os “pontos altos” das mudanças feitas neste esboço do Arduino.

Para que o FirmataExpress suporte até seis dispositivos DHT simultaneamente, 3 matrizes foram criadas para controlar cada um dos números de pinos associados do dispositivo, seu valor WakeUpDelay e o tipo de dispositivo, que é DHT22 ou DHT11:

// sensores DHT

int numActiveDHTs = 0; // número de DHTs conectados uint8_t DHT_PinNumbers [MAX_DHTS]; uint8_t DHT_WakeUpDelay [MAX_DHTS]; uint8_t DHT_TYPE [MAX_DHTS];

Como os dois tipos de dispositivo requerem aproximadamente 2 segundos entre as leituras, precisamos nos certificar de que lemos cada DHT apenas uma vez no período de 2 segundos. Alguns dispositivos, como os dispositivos DHT e os sensores de distância HC-SR04, são acessados apenas periodicamente. Isso lhes dá tempo para interagir com seus ambientes.

uint8_t nextDHT = 0; // indexar em dht para o próximo dispositivo a ser lido

uint8_t currentDHT = 0; // Mantém o controle de qual sensor está ativo. int dhtNumLoops = 0; // Número de destino de vezes através do loop b4 acessando um DHT int dhtLoopCounter = 0; // Contador de loop

Configurando e lendo o dispositivo DHT

Quando FirmataExpress recebe um comando SysEx para configurar um pino para operação DHT, ele verifica se o número máximo de dispositivos DHT não foi excedido. Se o novo DHT puder ser suportado, as matrizes DHT serão atualizadas. Se o tipo DHT for desconhecido, uma mensagem de string SysEx é criada e transmitida de volta para pymata4

caso DHT_CONFIG: int DHT_Pin = argv [0]; int DHT_type = argv [1]; if (numActiveDHTs <MAX_DHTS) {if (DHT_type == 22) {DHT_WakeUpDelay [numActiveDHTs] = 1; } else if (DHT_type == 11) {DHT_WakeUpDelay [numActiveDHTs] = 18; } else {Firmata.sendString ("ERRO: TIPO DE SENSOR DESCONHECIDO, SENSORES VÁLIDOS SÃO 11, 22"); pausa; } // teste o sensor DHT_PinNumbers [numActiveDHTs] = DHT_Pin; DHT_TYPE [numActiveDHTs] = DHT_type; setPinModeCallback (DHT_Pin, PIN_MODE_DHT);

O FirmataExpress então tenta se comunicar com o dispositivo DHT. Se houver algum erro, ele forma uma mensagem SysEx com os dados do erro e envia a mensagem SysEx de volta para pymat4. A variável _bits contém os dados retornados pelo dispositivo DHT para processamento adicional por pymata4, se desejado.

Firmata.write (START_SYSEX);

Firmata.write (DHT_DATA); Firmata.write (DHT_Pin); Firmata.write (DHT_type); para (uint8_t i = 0; i> 7 & 0x7f); } Firmata.write (abs (rv)); Firmata.write (1); Firmata.write (END_SYSEX);

Se dados válidos forem retornados, o número de DHTs ativos é incrementado. Uma variável que mantém o controle de quantas iterações de loop devem ser concluídas antes de verificar se há dados no próximo DHT também é ajustada. Esta variável garante que não importa quantos DHTs sejam adicionados ao sistema, todos eles serão lidos em um período de 2 segundos.

int rv = readDhtSensor (numActiveDHTs);

if (rv == DHTLIB_OK) {numActiveDHTs ++; dhtNumLoops = dhtNumLoops / numActiveDHTs; // Tudo certo }

Se um ou mais dispositivos DHT foram configurados na função de loop do esboço, então o próximo dispositivo DHT é lido. Os dados válidos ou seu status de erro são retornados para pymata4 na forma de uma mensagem SysEx:

if (dhtLoopCounter ++> dhtNumLoops) {if (numActiveDHTs) {int rv = readDhtSensor (nextDHT); uint8_t current_pin = DHT_PinNumbers [nextDHT]; uint8_t current_type = DHT_TYPE [nextDHT]; dhtLoopCounter = 0; currentDHT = nextDHT; if (nextDHT ++> = numActiveDHTs - 1) {nextDHT = 0; } if (rv == DHTLIB_OK) {// TESTE CHECKSUM uint8_t soma = _bits [0] + _bits [1] + _bits [2] + _bits [3]; if (_bits [4]! = soma) {rv = -1; }} // envia a mensagem de volta com um status de erro Firmata.write (START_SYSEX); Firmata.write (DHT_DATA); Firmata.write (current_pin); Firmata.write (current_type); para (uint8_t i = 0; i <sizeof (_bits) - 1; ++ i) {Firmata.write (_bits ); // Firmata.write (_bits ;} Firmata.write (abs (rv)); Firmata.write (0); Firmata.write (END_SYSEX);}}

O código usado para se comunicar com o dispositivo DHT é derivado diretamente da biblioteca DHTNew:

int readDhtSensor (int index) {

// INIT BUFFERVAR PARA RECEBER DADOS uint8_t mask = 128; uint8_t idx = 0; // BUFFER VAZIO // memset (_bits, 0, sizeof (_bits)); for (uint8_t i = 0; i 5 BYTES for (uint8_t i = 40; i! = 0; i--) {loopCnt = DHTLIB_TIMEOUT; while (digitalRead (pin) == LOW) {if (--loopCnt == 0) return DHTLIB_ERROR_TIMEOUT;} uint32_t t = micros (); loopCnt = DHTLIB_TIMEOUT; while (digitalRead (pin) == HIGH) {if (--loopCnt == 0) return DHTLIB_ERROR_TIMEOUT;} if ((micros () - t)> 40) {_bits [idx] | = máscara;} máscara >> = 1; if (máscara == 0) // próximo byte? {Máscara = 128; idx ++;}} retornar DHTLIB_OK;}

Etapa 4: Modificando Pymata4 para suporte DHT

private_constants.h

Para oferecer suporte ao DHT, precisamos adicionar o novo tipo de pino e mensagens SysEx a este arquivo:

# modos de pino INPUT = 0x00 # pino definido como entrada OUTPUT = 0x01 # pino definido como saída ANALOG = 0x02 # pino analógico no modo de entrada analógica PWM = 0x03 # pino digital no modo de saída PWM SERVO = 0x04 # pino digital no modo de saída Servo I2C = 0x06 # pino incluído na configuração I2C STEPPER = 0x08 # qualquer pino no modo stepper SERIAL = 0x0a PULLUP = 0x0b # Qualquer pino no modo pullup SONAR = 0x0c # Qualquer pino no modo SONAR TONE = 0x0d # Qualquer pino no modo tom PIXY = 0x0e # reservado para o modo de câmera pixy DHT = 0x0f # DHT sensor IGNORE = 0x7f # DHT SysEx command messages DHT_CONFIG = 0x64 # dht config command DHT_DATA = 0x65 # dht sensor reply

O tipo de pino adicionado e os comandos SysEx devem corresponder aos valores em FirmataConstants.h adicionados a FirmataExpress.

pymata4.py

Pymata4 usa um dicionário Python para associar rapidamente uma mensagem Firmata recebida a um manipulador de mensagens. O nome deste dicionário é report_dispatch.

O formato de uma entrada de dicionário é:

{MessageID: [message_handler, número de bytes de dados a serem processados]}

Uma entrada foi adicionada ao dicionário para lidar com as mensagens DHT recebidas:

{PrivateConstants. DHT_DATA: [self._dht_read_response, 7]}

Os 7 bytes de dados na mensagem são o número do pino digital do Arduino, o tipo de dispositivo DHT (22 ou 11) e os 5 bytes de dados brutos.

O método _dht_read_response verifica se há erros relatados. Se não houver erros relatados, a umidade e a temperatura são calculadas usando o algoritmo transferido da biblioteca Arduino DHTNew.

Os valores calculados são relatados por meio de um método de retorno de chamada fornecido pelo usuário. Eles também são armazenados na estrutura de dados pin_data interna. O último valor relatado pode ser recuperado pesquisando pin_data usando o método dht_read.

Configurando um novo dispositivo DHT

Ao adicionar um novo dispositivo DHT, o método set_pin_mode_dht é chamado. Este método atualiza o pin_data para pinos digitais. Ele também cria e envia uma mensagem DHT_CONFIG SysEx para FirmataExpress.

Etapa 5: Concluindo

Como vimos, adicionar suporte Firmata para um novo dispositivo requer que você modifique o código do servidor Arduino FirmataExpress e o código do cliente pymata4 baseado em Python. O código FirmataExpress pode ser desafiador para depurar. Um método chamado printData foi adicionado ao FirmataExpress para ajudar na depuração. Este método permite enviar valores de dados do FirmataExpress e imprimi-los no console do pymata4.

Esta função requer um ponteiro para uma string de caracteres e o valor que você deseja visualizar. Se o valor dos dados estiver contido em uma variável chamada argc, você poderá chamar printData com os parâmetros a seguir.

printData ((char *) "argc =", argc);

Se você tiver alguma dúvida, deixe um comentário, e ficarei feliz em responder.

Boa codificação!

Recomendado: