fbpixel
Controlar um controlador serial Maestro com Arduino

Controlar um controlador serial Maestro com Arduino

Quando se pretende controlar vários servomotores e são necessárias muitas entradas/saídas livres para operar o seu robô (para controlar o Hexana, por exemplo), é interessante usar um controlador serial de servomotor, como o Mini Maestro da Pololu.

Pré-requisitos: Programar com o Arduino, Comunicar com o seu Arduino, Controlar um servomotor com o Arduino

Material

  • Computador
  • Placa Arduino UNO
  • Cabo USB para ligar o Arduino ao computador
  • Cabo USB mini B para ligar o cartão Maestro ao computador
  • 3 cabos Dupont M/M
  • Controlador serial Mini Maestro
  • 1 ou mais servomotores

Introdução ao controlador serial Maestro

Quando utilizamos vários servomotores, especialmente quando o número de servomotores excede o número de saídas da placa eletrônica, é necessário usar um controlador de servomotor externo ao microcontrolador principal. Há vários tipos diferentes, e alguns permitem pilotar até 32 servomotores ao mesmo tempo.

Vamos apresentar aqui a placa Maestro da Pololu. Ela existe em diferentes formatos, com 6, 12, 18 ou 24 saídas para servomotor.

Com esta solução, a placa principal (a placa master Arduino ou Raspberry Pi) dá ordens ao controlador (placa slave) através de uma porta serial, que transmite as ordens e fornece energia aos servomotores.

Isso permite :

  • proteger o equipamento, separando a parte de energia da parte de controle;
  • reduzir o custo, diminuindo o número de entradas e saídas da placa principal;
  • usar diferentes tipos de placas (permite controlar 32 servomotores usando um Raspberry Pi ou mesmo um Arduino Mini).

As informações completas sobre o condutor serial Mini Maestro podem ser encontradas no site da Pololu.

Configuração e teste prévio do controlador serial Maestro

Para configurar o controlador serial Maestro, basta fazer o download do software e dos drivers para Windows (ou para Linux). Os procedimentos de instalação para Windows e para Linux estão disponíveis em inglês nos respectivos links.

Uma vez instalado o software Maestro Control Center e os drivers, conecte o controlador Maestro ao PC com o cabo USB.

Quando o controlador é ligado, o seu número de identificação aparece no canto superior esquerdo. As configurações podem ser acessadas pela aba “Serial Settings” (configurações seriais).

Utilize o software para testar o controlador serial Maestro e os servomotores, na aba “Status”. Não se esqueça de ligar uma fonte de alimentação ao bloco terminal do Mini Maestro, pois do contrário o servomotor não se moverá.

Os servomotores podem ter diferentes configurações de funcionamento. As configurações das saídas do controlador serial podem ser alteradas na aba “Channel Settings” (Configurações de Canal). Teste as configurações que se adaptam ao seu servomotor.

Esquema de ligação

A placa Arduino aceita uma tensão de alimentação entre 7 e 12V e pode ser alimentada pela entrada USB do computador. A lógica do controlador serial é alimentada pela saída de 5V do Arduino, e os servomotores são alimentados por uma bateria. Pode ser necessário acrescentar um regulador de tensão, caso a tensão de alimentação seja diferente da tensão nominal dos servomotores (por exemplo: um Lipo 2S fornece 7,4V enquanto a tensão nominal de um servomotor SG90 é de 5V).

Os terminais 2(Rx) e 3(Tx) da placa Arduino são ligados aos terminais TX e RX da placa Maestro, respectivamente.

O esquema de ligação pode ser facilmente transposto para os controladores seriais Mini Maestro 6, 12 e 18 canais.

Código básico para pilotar um controlador serial Maestro

Para pilotar o controlador serial com uma placa Arduino, é preciso enviar uma sequência de comandos através de uma porta serial. O programa a seguir permite enviar um comando de posição para os diferentes canais de um Mini Maestro utilizando o monitor serial da IDE do Arduino.

Antes de tudo, inicializamos a porta serial utilizada para comunicar com a placa Maestro.

#include <Maestro.h>
Maestro maestro(2,3);

Depois, adicionamos uma função para receber os dados enviados pelo monitor serial.

void readSerialPort(){
 while (Serial.available()) {
   delay(10);  
   if (Serial.available() >0) {
     char c = Serial.read();  //gets one byte from serial buffer
     msg += c;
   }
 }
}

Por razões práticas, enviamos apenas um comando através do monitor. Criamos então uma função para separar o ID do servomotor do valor da posição utilizando o caractere separador “x”.

void convertMsgToCmd(){
   if (msg.length() >0) {
     Serial.println(msg);
     sep = msg.indexOf('x');     
     m1 = msg.substring(0, sep); //get servo id
     m2 = msg.substring(sep+1, msg.length()); //get servo pos
     
     servoId=-1;
     servoPos=-1; 
    
     char carray1[6]; //magic needed to convert string to a number
     m1.toCharArray(carray1, sizeof(carray1));
     servoId = atoi(carray1);
     
     char carray2[6];
     m2.toCharArray(carray2, sizeof(carray2));
     servoPos = atoi(carray2);
      
     msg="";
 }
}

Finalmente, enviamos estes dados para a placa Maestro utilizando o protocolo Pololu, que nada mais é que um sequência de bytes contendo a seleção do protocolo, o ID da placa Maestro, a seleção do comando e o valor da posição.

void setTarget(unsigned char servo, unsigned int target){
  /* envoie la séquence de commande au contrôleur série Maestro pour actionner le servomoteur*/
  const int deviceId = 0x0C; //controller ID 12
  const int startByte = 0xAA; // Protocol selection
  const int targetCmd = 0x04; // Command ID
  
  maestro.write(startByte); //start byte
  maestro.write(deviceId); //device id
  maestro.write(targetCmd); //command number
  maestro.write(servo); //servo number
  maestro.write(target & 0x7F); // Send first 4bits
  maestro.write((target >> 7) & 0x7F); // Send last 4bits
  delay(3);
}

Tudo isso junto resulta no seguinte código:

/*----------------------------------------------------------------------------------------
 Ce programme permet de piloter différents servomoteurs à l'aide du moniteur série.
 Tapez YxZZZZ dans le terminal
 Y entre 0 et 5 pour Mini Maestro 6 voies (selon Maestro)
 ZZZZ entre 4000 et 8000 (selon servomoteur)

 Ex: Tapez 0x6000 dans le moniteur série

  Materiel:
  - 1x servomoteur ou plus
  - 1x Mini Maestro
  - 1x Arduino
  
 Auteur: Xavier Wiedmer
 http://www.aranacorp.com
 
----------------------------------------------------------------------------------------*/
#include "SoftwareSerial.h"
SoftwareSerial maestro(2,3);

String msg, m1, m2;
int sep, servoId=-1, servoPos=-1;


void setup() {
 Serial.begin(9600);
 pinMode(2, INPUT);
 pinMode(3, OUTPUT);
 maestro.begin(9600);
 Serial.println(F("Waiting for command (YxZZZZ): "));
}

void loop() {

 readSerialPort();
 convertMsgToCmd();
 
    //Apply command to servo
    if (servoId>=0 && servoPos>=0 && servoId<18 && servoPos>=500 && servoPos<=10000) {
     
       setTarget(servoId, servoPos);
       Serial.print(F("Command "));
       Serial.print(servoPos);
       Serial.print( F(" sent "));
       Serial.print(F("to servo "));
       Serial.println(servoId);
   
   servoId=-1;
   servoPos=-1;
   Serial.println(F("Waiting for command ... "));
 }
}

void setTarget(unsigned char servo, unsigned int target){
  /* envoie la séquence de commande au contrôleur série Maestro pour actionner le servomoteur*/
  const int deviceId = 0x0C; //controller ID 12
  const int startByte = 0xAA; // Protocol selection
  const int targetCmd = 0x04; // Command ID
  
  maestro.write(startByte); //start byte
  maestro.write(deviceId); //device id
  maestro.write(targetCmd); //command number
  maestro.write(servo); //servo number
  maestro.write(target & 0x7F); // Send first 4bits
  maestro.write((target >> 7) & 0x7F); // Send last 4bits
  delay(3);
}

void readSerialPort(){
  /*Permet de lire une commande provenant du terminal Arduino*/
 while (Serial.available()) {
   delay(10);  
   if (Serial.available() >0) {
     char c = Serial.read();  //gets one byte from serial buffer
     msg += c;
   }
 }
}

void convertMsgToCmd(){
  /*convertit le message provenant du terminal en commande à envoyer au contrôleur série*/
   if (msg.length() >0) {
     Serial.println(msg);
     sep = msg.indexOf('x');
     // expect a string like 0x0021 containing the two servo positions      
     m1 = msg.substring(0, sep); //get servo id
     m2 = msg.substring(sep+1, msg.length()); //get servo pos
     
     servoId=-1;
     servoPos=-1; 
    
     char carray1[6]; //magic needed to convert string to a number
     m1.toCharArray(carray1, sizeof(carray1));
     servoId = atoi(carray1);
     
     char carray2[6];
     m2.toCharArray(carray2, sizeof(carray2));
     servoPos = atoi(carray2);
      
     msg="";
 }
}

Biblioteca para pilotar um controlador serial Maestro

Se for utilizar controladores seriais Maestro em diferentes projetos, é prático codificar as regras de comunicação numa biblioteca. Desta forma, pode reutilizar a biblioteca sem copiar e colar e tornar o seu código mais leve. Pode escrever a sua própria biblioteca ou usar a biblioteca criada por Ryan Mulligan no Github.

Para criar uma biblioteca, criamos dois arquivos .cpp e .h que copiamos para a pasta Documents\Arduino\libraries\Maestro.

O arquivo Maestro.h

/*****************************************************************\
* Library header : Maestro.h
* Author : X.Wiedmer
* Version : v00
* Date : 05/03/2015
* Revision : 
*   v01 - 05/03/2015
* Description :
* Library to setup Maestro board
* www.aranacorp.com
\*****************************************************************/

#ifndef Maestro_h
#define Maestro_h
// Libraries
#include "Arduino.h"
#include "SoftwareSerial.h" 

/******************************************************************\
* CLASS DESCRIPTION
\******************************************************************/
class Maestro
{
  public:
    Maestro(int pinRx, int pinTx);
	 //~Maestro();
    void setTarget(unsigned char servo, unsigned int target);
    void stop(unsigned char servo);
    void begin(unsigned int baudrate);
    
  private:
    int _pinRx;
    int _pinTx;
    int _id;
    SoftwareSerial *_maestroSerial;
};

#endif

O arquivo Maestro.cpp

/*****************************************************************\
* Library : Maestro.cpp
* Author : X.Wiedmer
* Version : v00
* Date : 05/03/2015
* Revision : 
* 	v01 - 05/03/2015
* Description :
* Library to setup Maestro board
* www.aranacorp.com
\*****************************************************************/

//Libraries
#include "Arduino.h"
#include "Maestro.h"
#include "SoftwareSerial.h"

// Parameters
#define DELAY_WRITE 3
//set up maestro configuration
#define deviceId 0x0C //12
#define startByte 0xAA //
// Command list
#define targetCmd 0x04 //


/******************************************************************\
* PRIVATE FUNCTION: Constructor
*
* PARAMETERS:
* ~ void
*
* DESCRIPTIONS:
* object constructor 
\******************************************************************/
Maestro::Maestro(int pinRx, int pinTx)
{
  pinMode(pinRx, INPUT);
  pinMode(pinTx, OUTPUT);
  _pinRx = pinRx;
  _pinTx = pinTx;
  _maestroSerial = new SoftwareSerial(pinRx,pinTx);
}

/******************************************************************\
* PRIVATE FUNCTION: begin
*
* PARAMETERS:
* ~ baudrate (serial port speed)
*
* DESCRIPTIONS:
* Initialize serial port 
\******************************************************************/
void Maestro::begin(unsigned int baudrate)
{
  _maestroSerial->begin(baudrate);
}
/******************************************************************\
* PRIVATE FUNCTION: setTarget
*
* PARAMETERS:
* ~ servo ID number, target specified with integer
*
* DESCRIPTIONS:
* Send sequence of command so that the maestro board send the right
* pwm value to set servo to the desired position
\******************************************************************/
void Maestro::setTarget(unsigned char servo, unsigned int target)
{
  _maestroSerial->write(startByte); //start byte
  _maestroSerial->write(deviceId) ; //device id
  _maestroSerial->write(targetCmd); //command number
  _maestroSerial->write(servo); //servo number
  _maestroSerial->write(target & 0x7F); // Send first 4bits
  _maestroSerial->write((target >> 7) & 0x7F); // Send last 4bits
  delay(DELAY_WRITE);
}

/******************************************************************\
* PRIVATE FUNCTION: stop
*
* PARAMETERS:
* ~ servo ID number
*
* DESCRIPTIONS:
* Send sequence of command so that the maestro send nothing to the
* the servo
\******************************************************************/
void Maestro::stop(unsigned char servo)
{
  _maestroSerial->write(startByte); //start byte
  _maestroSerial->write(deviceId) ; //device id
  _maestroSerial->write(targetCmd); //command number
  _maestroSerial->write(servo); //servo number
  _maestroSerial->write((byte)0x00); // Send first 4bits
  _maestroSerial->write((byte)0x00); // Send last 4bits
  delay(DELAY_WRITE);
}

O programa fica assim:

/*----------------------------------------------------------------------------------------
 Ce programme permet de piloter différents servomoteurs à l'aide du moniteur série.
 Tapez YxZZZZ dans le terminal
 Y entre 0 et 5 pour Mini Maestro 6 voies (selon Maestro)
 ZZZZ entre 4000 et 8000 (selon servomoteur)

 Ex: Tapez 0x6000 dans le moniteur série

  Materiel:
  - 1 servomoteur ou plus
  - 1x Mini Maestro
  - 1x Arduino
  
 Auteur: Xavier Wiedmer
 http://www.aranacorp.com
 
----------------------------------------------------------------------------------------*/

#include <Maestro.h>
Maestro maestro(2,3);

String msg, m1, m2;
int sep, servoId=-1, servoPos=-1;

/************** Main Program **************/
void setup() {
 Serial.begin(9600);
 maestro.begin(9600);
 Serial.println("Waiting for command (YxZZZZ): ");
}

void loop() {

 readSerialPort();
 convertMsgToCmd();
 
    //Apply command to servo
    if (servoId>=0 && servoPos>=0 && servoId<18 && servoPos>=500 && servoPos<=10000) {
     
       maestro.setTarget(servoId, servoPos);
       Serial.print(F("Command "));
       Serial.print(servoPos);
       Serial.print( F(" sent "));
       Serial.print(F("to servo "));
       Serial.println(servoId);
   
   servoId=-1;
   servoPos=-1;
   Serial.println(F("Waiting for command ... "));
 }
}

/************** Functions **************/
void readSerialPort(){
 while (Serial.available()) {
   delay(10);  
   if (Serial.available() >0) {
     char c = Serial.read();  //gets one byte from serial buffer
     msg += c;
   }
 }
}

void convertMsgToCmd(){
   if (msg.length() >0) {
     Serial.println(msg);
     sep = msg.indexOf('x');
     
     m1 = msg.substring(0, sep); //get servo id
     m2 = msg.substring(sep+1, msg.length()); //get servo pos
     
     servoId=-1;
     servoPos=-1; //declare as number  
    
     char carray1[6]; //magic needed to convert string to a number
     m1.toCharArray(carray1, sizeof(carray1));
     servoId = atoi(carray1);
     
     char carray2[6];
     m2.toCharArray(carray2, sizeof(carray2));
     servoPos = atoi(carray2);
      
     msg="";
 }
}

Fique à vontade para comentar ou nos enviar uma mensagem se tiver dificuldades com o seu controlador serial.

Aplicações

Referências

Retrouvez nos tutoriels et d’autres exemples dans notre générateur automatique de code
La Programmerie

Controlar um controlador serial Maestro com Raspberry Pi

Controlar um controlador serial Maestro com Raspberry Pi

Quando se pretende controlar vários servomotores e são necessárias muitas entradas/saídas livres para operar o seu robô (para controlar o Hexana, por exemplo), é interessante usar um controlador serial para servomotor, como o Mini Maestro da Pololu..

Este tutorial mostra como configurar a placa Raspberry Pi 3 (ou Zero) para controlar o controlador serial Mini Maestro.

Pré-requisitos: Programar com Raspberry Pi, Controlar um servomotor com Raspberry Pi

Material

  • Computador
  • Raspberry Pi3 (ou Zero) + monitor + teclado
  • Cabo USB mini B para ligar o Maestro ao computador
  • 4 Cabos F/F
  • Controlador serial Mini Maestro
  • 1 ou mais servomotores

Introdução ao controlador serial Maestro

Quando utilizamos vários servomotores, especialmente quando o número de servomotores excede o número de saídas da placa eletrônica, é necessário usar um controlador de servomotor externo ao microcontrolador principal. Existem vários tipos, e alguns permitem pilotar até 32 servomotores ao mesmo tempo.

Vamos apresentar aqui a placa Maestro da Pololu. Ela existe em diferentes formatos, com 6, 12, 18 ou 24 saídas para servomotor.

Com esta solução, a placa principal (neste caso o Raspberry Pi) dá ordens ao controlador (placa slave) através de uma porta serial, que as transmite e fornece energia aos servomotores.

Isto permite :

  • proteger o equipamento, separando a parte de energia da parte de controle;
  • reduzir o custo, diminuindo o número de entradas e saídas da placa principal;
  • usar diferentes tipos de placas (permite controlar 32 servomotores usando um Raspberry Pi ou mesmo um Arduino Mini).

As informações completas sobre o condutor serial Mini Maestro podem ser encontradas no site da Pololu.

Configuração e teste prévio do controlador serial Maestro

Para configurar o controlador serial Maestro, basta fazer o download do software e dos drivers para Windows (ou para Linux). Os procedimentos de instalação para Windows e para Linux estão disponíveis em inglês nos respectivos links.

Uma vez instalado o software Maestro Control Center e os drivers, conecte o controlador Maestro ao PC com o cabo USB.

Quando o controlador é ligado, o seu número de identificação aparece no canto superior esquerdo. As configurações podem ser acessadas pela aba “Serial Settings” (configurações seriais).

Utilize o software para testar o controlador serial Maestro e os servomotores, na aba “Status”. Não se esqueça de ligar uma fonte de alimentação ao bloco terminal do Mini Maestro, pois do contrário o servomotor não se moverá.

Os servomotores podem ter diferentes configurações de funcionamento. As configurações das saídas do controlador serial podem ser alteradas na aba “Channel Settings” (Configurações de Canal). Teste as configurações que se adaptam ao seu servomotor.

Esquema de ligação

O controlador Maestro é alimentado pela saída de 5V do Raspberry Pi. Os terminais 8 (GPIO14 / UART0_TXD) e 10 (GPIO15/ UART0_RXD) do Raspberry Pi são ligados aos terminais RX e TX da placa Maestro, respectivamente.

Este esquema de ligação é facilmente transponível para os controladores seriais Mini Maestro 6, 12 e 18 canais.

Configuração do Raspberry Pi

Certifique-se de que o módulo PySerial esteja instalado no seu Raspberry.

Num terminal do Raspberry, digite:

$ python -m pip install pyserial

Depois copie a pasta que contém a biblioteca do Maestro.

$ git clone https://github.com/FRC4564/Maestro

Para comunicar com a placa Maestro, será necessária uma porta serial. Pode haver diferentes portas seriais. Para encontrar as portas seriais disponíveis, digite:

$ dmesg | grep tty

No Raspberry Pi3, a porta serial /dev/ttyAMAO é utilizada pelo módulo Bluetooth. Utilizaremos a porta /dev/ttySO para comunicar com o controlador Maestro.

Código básico para controlar um controlador serial Maestro

Para controlar o controlador serial com a placa Raspberry Pi, é preciso enviar uma sequência de comandos através de uma porta serial. O programa a seguir permite enviar um comando de posição para os vários canais de um Mini Maestro utilizando um terminal do microcontrolador Raspberry Pi3.

Para isso, utilizamos o módulo Maestro.py previamente baixado (https://github.com/FRC4564/Maestro). Para utilizá-lo, basta copiar a pasta obtida para a pasta do programa.

Antes de tudo, inicializamos a porta serial utilizada para comunicar com a placa Maestro. Depois adicionamos um código para transformar os dados enviados através do terminal em comandos de servomotor.

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import Maestro.maestro as maestro
import time

#define servo
servo = maestro.Controller(ttyStr='/dev/ttyS0')
min_imp=2000
max_imp=10000
step_imp=100

#infinite loop
while 1:
	  #user input
	  msg=raw_input("Enter servo id and command: YxZZZZ:  \n")

	  #convert msg into id and cmd
	  sep=msg.find("x")
	  m1=msg[:sep]
	  m2=msg[sep+1:]
	  servoId=int(m1)
	  servoCmd=int(m2)

	  #saturate input
	  servoId=min(max(0,servoId),6);
	  servoCmd=min(max(min_imp,servoCmd),max_imp);

	  print(msg)
	  print("servoId : {}    servoCmd : {} \n".format(servoId,servoCmd))

	  servo.setTarget(servoId,servoCmd)
	  time.sleep(0.1)

servo.close

Fique à vontade para comentar ou nos enviar uma mensagem se tiver dificuldades para controlar o seu controlador serial.

Aplicações

Fontes

Retrouvez nos tutoriels et d’autres exemples dans notre générateur automatique de code
La Programmerie

Comunicação I2C entre Raspberry Pi e Arduino

Comunicação I2C entre Raspberry Pi e Arduino

Em alguns projetos, pode ser interessante estabelecer uma comunicação I2C entre Raspberry Pi e Arduino. A potência de computação e as capacidades sem fios do Raspberry Pi, aliadas à capacidade de entrada e saída do Arduino, resultam num sistema de controle completo que permite pilotar qualquer projeto. Se a placa Raspberry Pi e a placa Arduino estiverem próximas, o barramento I2C é uma boa escolha como protocolo de comunicação. Isso também permite adicionar vários dispositivos no mesmo barramento e aumentar em dez vezes as capacidades do RaspberryPi.

Pré-requisitos: comunicação I2C com Arduino, Acesso remoto do Raspberry Pi com VNC

Material

  • Computador
  • Arduino UNO
  • Raspberry Pi 3B+
  • 3 Cabos jumper

Esquema de ligação

Para estabelecer a comunicação I2C entre Raspberry Pi e Arduino, é preciso ligar fisicamente o barramento que utiliza 3 pinos. A comunicação I2C se define por um barramento de dois fios (também chamado de TWI, Two Wire Interface) e um endereço. Os pinos utilizados para a comunicação I2C são normalmente fixados para cada dispositivo. Os dados (SDA Serial Data Line) são enviados para um pino e o relógio de sincronização (SLC Serial Clock Line), para o outro. As terras de ambas as placas devem estar conectadas para estabelecer uma referência comum de potencial.

  • SDA BCM2(RPI) <-> SDA A4(Arduino)
  • SCL BCM3(RPI) <-> SCL A5(Arduino)
  • GND (RPI) <-> GND(Arduino)

Configuração do Raspberry Pi

Para utilizar a interface I2C do Raspberry Pi, deve-se ativá-la no menu de configuração. Para isso, introduza o seguinte comando num terminal:

sudo raspi-config

No menu, selecione “5 – Opções de Interface” e em seguida “P5 I2C”, então valide.

Uma vez efetuada a ligação, é possível verificar os dispositivos conectados ao barramento digitando o seguinte comando no terminal:

i2cdetect -y 1

O Raspberry Pi devolve a lista de endereços detectados no barramento.

pi@raspberrypi:~ $ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- 0b -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --  

Vamos instalar a biblioteca smbus2, que permite gerenciar a comunicação I2C pelo lado do Raspberry Pi

pip3 install smbus2

Código

Código “Master” Python

Neste tutorial, vamos adotar a linguagem Python para o Raspberry Pi. A biblioteca utilizada para gerenciar a comunicação I2C é a smbus2.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Raspberry Pi to Arduino I2C Communication
#i2cdetect -y 1

#library
import sys
import smbus2 as smbus#,smbus2
import time

# Slave Addresses
I2C_SLAVE_ADDRESS = 11 #0x0b ou 11
I2C_SLAVE2_ADDRESS = 12
I2C_SLAVE3_ADDRESS = 13

# This function converts a string to an array of bytes.
def ConvertStringsToBytes(src):
  converted = []
  for b in src:
    converted.append(ord(b))
  return converted

def main(args):
    # Create the I2C bus
    I2Cbus = smbus.SMBus(1)
    with smbus.SMBus(1) as I2Cbus:
        slaveSelect = input("Which Arduino (1-3): ")
        cmd = input("Enter command: ")

        if slaveSelect == "1":
            slaveAddress = I2C_SLAVE_ADDRESS
        elif slaveSelect == "2":
            slaveAddress = I2C_SLAVE2_ADDRESS
        elif slaveSelect == "3":
            slaveAddress = I2C_SLAVE3_ADDRESS
        else:
            # quit if you messed up
            print(slaveSelect== "1")
            print(type(slaveSelect))
            print("no slave selected")
            quit()
        BytesToSend = ConvertStringsToBytes(cmd)
        print("Sent " + str(slaveAddress) + " the " + str(cmd) + " command.")
        print(BytesToSend )
        I2Cbus.write_i2c_block_data(slaveAddress, 0x00, BytesToSend)
        time.sleep(0.5)

        while True:
            try:
                data=I2Cbus.read_i2c_block_data(slaveAddress,0x00,16)
                print("recieve from slave:")
                print(data)
            except:
                print("remote i/o error")
                time.sleep(0.5)
    return 0

if __name__ == '__main__':
     try:
        main(sys.argv)
     except KeyboardInterrupt:
        print("program was stopped manually")
     input()

Código “Slave” Arduino

A biblioteca utilizada para gerir a comunicação I2C no lado Arduino é a Wire.h.

#include <Wire.h>

# define I2C_SLAVE_ADDRESS 11 // 12 pour l'esclave 2 et ainsi de suite

#define PAYLOAD_SIZE 2

void setup()
{
  Wire.begin(I2C_SLAVE_ADDRESS);
  Serial.begin(9600); 
  Serial.println("-------------------------------------I am Slave1");
  delay(1000);               
  Wire.onRequest(requestEvents);
  Wire.onReceive(receiveEvents);
}

void loop(){}

int n = 0;

void requestEvents()
{
  Serial.println(F("---> recieved request"));
  Serial.print(F("sending value : "));
  Serial.println(n);
  Wire.write(n);
}

void receiveEvents(int numBytes)
{  
  Serial.println(F("---> recieved events"));
  n = Wire.read();
  Serial.print(numBytes);
  Serial.println(F("bytes recieved"));
  Serial.print(F("recieved value : "));
  Serial.println(n);
}

Resultado

O Raspberry Pi envia o comando “slave” ao Arduino e recebe um quadro de dados proveniente do Arduino.

O Arduino recebe o comando “slave” e envia dois valores atualizados assim que recebe um comando do Raspberry Pi

Aplicação

  • Reconhecimento visual com Raspberry Pi; controle de servomotores e sensores com Arduino

Fontes

Retrouvez nos tutoriels et d’autres exemples dans notre générateur automatique de code
La Programmerie

Comunicação serial entre Raspberry Pi e Arduino

Comunicação serial entre Raspberry Pi e Arduino

Em alguns projetos, pode ser interessante estabelecer uma comunicação serial entre o Raspberry Pi e o Arduino. Com isso, aliamos a capacidade de processamento e a interface sem fios do Raspberry Pi com as entradas e saídas e a coleção de módulos do Arduino. O primeiro exemplo que vem em mente é uma aplicação domótica em que o Raspberry Pi oferece a interface de controle e a inteligência e o Arduino funciona como um autômato programável, agindo sobre os componentes no final da cadeia (luzes, radiador, ventilador, sensores, etc.).

Veremos agora como estabelecer uma comunicação serial entre Raspberry Pi e Arduino via entrada USB. Neste tutorial, utilizamos a placa Arduino UNO, mas ele pode ser adaptado a outros tipos de placa com ligação serial (Nano, Mega, Feather, EPS32, ESP8266, etc.).

Pré-requisitos: Comunicação serial com o Arduino, Acesso remoto ao Raspberry Pi com VNC

Material

  • Computador
  • Arduino UNO
  • Raspberry Pi 3B+
  • USB A Macho/USB B Macho

Esquema de ligação

Para estabelecer a comunicação serial entre Raspberry Pi e Arduino, basta ligá-los com um cabo USB adequado. No nosso caso, utilizamos um Raspberry Pi 3B+ e um Arduino UNO. Precisamos portanto de um cabo USBA Macho para USB B Macho.

Neste tutorial, vamos focar na ligação USB, mas também é possível criar uma comunicação serial utilizando os pinos Rx/Tx de ambos os componentes.

  • Tx GPIO14(RPI) <-> Rx 0(Arduino)
  • Rx GPIO15(RPI) <-> Tx 1(Arduino)
  • GND (RPI) <-> GND(Arduino)

Configuração do Raspberry Pi

Recordamos que, para utilizar o seu Raspberry Pi sem monitor ou teclado, é preciso configurar a conexão remota VNC.

Para utilizar a interface serial do Raspberry Pi, ela deve ser ativada no menu de configuração. Para isso, introduza o seguinte comando num terminal:

sudo raspi-config

No menu, selecione “5 – Opções de Interface” e depois “P6 Serial” e confirme.

Efetuada a conexão, pode verificar os dispositivos ligados à porta serial digitando o seguinte comando no terminal:

lsusb

O Raspberry Pi devolve uma lista de dispositivos conectados às entradas USB.

pi@raspberrypi:~ $ lsusb
Bus 001 Device 002: ID 2341:0043 Arduino SA Uno R3 (CDC ACM)
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Para encontrar o nome da entrada onde o Arduino está conectado, usamos o comando:

dmesg | grep "tty"

Esse comando devolve as mensagens do sistema relativas à porta serial. O nome da porta deve aparecer entre as últimas mensagens. No nosso caso, o nome é ttyACM0.

pi@raspberrypi:~ $ dmesg | grep "tty"
[    0.000000] Kernel command line: coherent_pool=1M 8250.nr_uarts=1 bcm2708_fb.fbwidth=1824 bcm2708_fb.fbheight=984 bcm2708_fb.fbswap=1 smsc95xx.macaddr=B8:27:EB:23:CF:07 vc_mem.mem_base=0x1ec00000 vc_mem.mem_size=0x20000000  console=ttyS0,115200 console=tty1 root=/dev/mmcblk0p7 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles
[    0.000636] console [tty1] enabled
[    0.951553] 20201000.serial: ttyAMA0 at MMIO 0x20201000 (irq = 81, base_baud = 0) is a PL011 rev2
[    0.954393] console [ttyS0] disabled
[    0.954466] 20215040.serial: ttyS0 at MMIO 0x0 (irq = 53, base_baud = 31250000) is a 16550
[    0.954570] console [ttyS0] enabled
[    5.378641] systemd[1]: Created slice system-serial\x2dgetty.slice.
[ 1455.402071] cdc_acm 1-1:1.0: ttyACM0: USB ACM device
[ 1581.980257] cdc_acm 1-1:1.0: ttyACM0: USB ACM device

Instalação da IDE do Arduino no Raspberry Pi

A melhor maneira de instalar a IDE do Arduino no Raspberry é utilizando o terminal. Basta introduzir as seguintes linhas de código:

mkdir ~/Applications
cd ~/Applications
wget https://downloads.arduino.cc/arduino-1.8.9-linuxarm.tar.xz
tar xvJf arduino-1.8.9-linuxarm.tar.xz
cd arduino-1.8.9/
 ./install.sh
rm ../arduino-1.8.9-linuxarm.tar.xz

Isso permitirá programar o Arduino diretamente a partir do Raspberry Pi.

Código

Código Arduino

A biblioteca utilizada para a comunicação serial do lado Arduino é a mesma usada para comunicar com o monitor serial: a Serial.h, que já conhecemos bem. Verifique se a velocidade de comunicação é a mesma para ambos os dispositivos (baudrate=9600), caso contrário a comunicação não funcionará.

String nom = "Arduino";
String msg;

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

void loop() {
  readSerialPort();

  if (msg != "") {
    sendData();
  }
  delay(500);
}

void readSerialPort() {
  msg = "";
  if (Serial.available()) {
    delay(10);
    while (Serial.available() > 0) {
      msg += (char)Serial.read();
    }
    Serial.flush();
  }
}

void sendData() {
  //write data
  Serial.print(nom);
  Serial.print(" received : ");
  Serial.print(msg);
}


Código Python

Neste tutorial, adotamos a linguagem Python no lado do Raspberry Pi. A biblioteca utilizada para gerir a comunicação serial é a serial.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# lsusb to check device name
#dmesg | grep "tty" to find port name

import serial,time


if __name__ == '__main__':
	
	print('Running. Press CTRL-C to exit.')
	with serial.Serial("/dev/ttyACM0", 9600, timeout=1) as arduino:
		time.sleep(0.1) #wait for serial to open
		if arduino.isOpen():
			print("{} connected!".format(arduino.port))
			try:
				while True:
					cmd=input("Enter command : ")
					arduino.write(cmd.encode())
					#time.sleep(0.1) #wait for arduino to answer
					while arduino.inWaiting()==0: pass
					if  arduino.inWaiting()>0: 
						answer=arduino.readline()
						print(answer)
						arduino.flushInput() #remove data after reading
			except KeyboardInterrupt:
				print("KeyboardInterrupt has been caught.")

Resultado

O Raspberry Pi envia o comando “Hello Arduino” ao Arduino, e este responde com o seu nome e o comando recebido.

Exemplo prático

Estabelecida a comunicação entre o Raspberry Pi e o Arduino, o interessante é poder controlar as entradas e saídas do Arduino e obter os valores dos sensores. Neste exemplo, vamos definir diferentes comandos:

  • um comando para obter os valores dos sensores
  • e outro para ligar e desligar o LED no pino 13.

Código Arduino

O Arduino envia dados ao receber o comando “data” e acende e apaga o LED ligado ao pino 13 de acordo com os comandos “led0” e “led1”.

const int ledPin=13;
String nom = "Arduino";
String msg;

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

void loop() {
  readSerialPort();

  if (msg == "data") {
    sendData();
  }else if(msg=="led0"){
    digitalWrite(ledPin,LOW);
    Serial.print(" Arduino set led to LOW");
  }else if(msg=="led1"){
    digitalWrite(ledPin,HIGH);
    Serial.print(" Arduino set led to HIGH");
  }
  delay(500);
}

void readSerialPort() {
  msg = "";
  if (Serial.available()) {
    delay(10);
    while (Serial.available() > 0) {
      msg += (char)Serial.read();
    }
    Serial.flush();
  }
}

void sendData() {
  //write data ledState x sensor1 x sensor2
  Serial.print(digitalRead(ledPin));
  Serial.print("x");
  Serial.print(analogRead(A0));
  Serial.print("x");
  Serial.print(analogRead(A1));
}


Código Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# lsusb to check device name
#dmesg | grep "tty" to find port name

import serial,time


if __name__ == '__main__':
	print('Running. Press CTRL-C to exit.')
	with serial.Serial("/dev/ttyACM0", 9600, timeout=1) as arduino:
		time.sleep(0.1) #wait for serial to open
		if arduino.isOpen():
			print("{} connected!".format(arduino.port))
			try:
				while True:
					cmd=input("Enter command (data,led0 or led1): ")
					arduino.write(cmd.encode())
					#time.sleep(0.1) #wait for arduino to answer
					
					while arduino.inWaiting()==0: pass
					if  arduino.inWaiting()>0: 
						answer=str(arduino.readline())
						print("---> {}".format(answer))
						if cmd=="data":
							dataList=answer.split("x")
							print("led state : {}".format(dataList[0]))
							print("Analog input A0 : {}".format(dataList[1]))
							print("Analog input A1: {}".format(dataList[2]))
							
							arduino.flushInput() #remove data after reading
							
			except KeyboardInterrupt:
				print("KeyboardInterrupt has been caught.")

Uma vez carregados e lançados os dois códigos, observamos que, ao introduzir o comando “data” no terminal, o Arduino retorna bytes contendo os valores dos sensores. É possível dividir esta resposta numa lista usando a função split() e o carectere “x”, e assim obter os valores dos sensores e o estado do LED. Com este código, podemos controlar o estado do LED no pino 13.

Este código pode ser modificado para controlar e visualizar qualquer entrada ou saída do Arduino no Raspberry Pi.

Aplicação

  • Criar uma interface gráfica de usuário (GUI) no Raspberry Pi para controlar um Arduino e obter os valores dos sensores

Fontes

Retrouvez nos tutoriels et d’autres exemples dans notre générateur automatique de code
La Programmerie

Usar um módulo PCA9685 com o Arduino

Usar um módulo PCA9685 com o Arduino

O módulo PCA9685 é um controlador de 16 canais que permite controlar 16 saídas PWM através de comunicação I2C. Seu uso libera entradas e saídas do seu microcontrolador, possibilitando conduzir até 16 LEDs ou servomotores (ou qualquer outro módulo que tome um sinal PWM como entrada) usando dois pinos (SCL e SDA). Com isso, os pinos do seu microcontrolador ficam livres para outros módulos, como sensores.

Pré-requisito: Conduzir um servomotor com o Arduino

Material

  • Computador
  • Arduino UNO
  • Cabo USB A Macho/B Macho
  • Módulo PCA9685

Princípio de funcionamento

O módulo é baseado no controlador PCA9685, que controla saídas PWM usando comunicação I2C e um relógio integrado. Este módulo comporta 6 pontes que permitem selecionar o endereço da placa e colocar, no mesmo barramento, até 62 controladores, para um total de 992 servomotores (endereços disponíveis de 0x40 a 0x7F).
Ele permite acionar saídas PWM com uma frequência ajustável e uma resolução de 12 bits. O módulo é compatível com microcontroladores de 5V e 3,3V.

Esquema

O módulo é equipado com um barramento I2C e uma entrada de energia. O barramento I2C se conecta da seguinte forma:

  • Pino A5 ou SCL no pino SCL do módulo
  • Pino A4 ou SDA no pino SDA do módulo
  • Pino 5V no pino Vcc do módulo
  • Pino GND no pino GND do módulo

Neste tutorial, usamos a placa Arduino UNO, mas ele pode ser adaptado a outros microcontroladores. Basta adaptar os pinos I2C disponíveis no microcontrolador em questão e, possivelmente, o código.

Código

Para utilizar o módulo PCA9685, usamos a biblioteca Adafruit_PWMServoDriver.h. As larguras de PWM são geralmente dadas em microssegundos por um período de 20ms (50Hz), mas estes valores podem mudar conforme o fornecedor e o servomotor utilizado. Nesse caso, é preciso modificar os valores do código para adaptá-los ao seu servomotor.
Aqui, utilizamos o servomotor MG90S, cujos gamas são de 400-2400µs por 20ms.

Para definir o comando PWM, a biblioteca apresenta duas funções: setPWM() e writeMicroseconds(). A função writeMicroseconds() permite utilizar diretamente os valores do construtor. Para a função setPWM, precisamos encontrar a largura de pulso correspondente em 4096 (2^12, 12bits).

Exemplo: A frequência é de 50Hz, logo um período de 20ms ou 20000µs.
pwmvalmin=400/20000*4096=81.92-> 90 arredondando com uma margem de segurança
pwmvalmax=2400/20000*4096=491.52 -> 480 arredondando com uma margem de segurança
Isso nos dá as seguintes equivalências:
pca.writeMicroseconds(i,400) equivale a pca.setPwm(i,0,90)
pca.writeMicroseconds(i,2400) equivale a pca.setPwm(i,0,480)
Utilize a função que melhor lhe convier.

//Libraries
#include <Wire.h>//https://www.arduino.cc/en/reference/wire
#include <Adafruit_PWMServoDriver.h>//https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library

//Constants
#define nbPCAServo 16

//Parameters
int MIN_IMP [nbPCAServo] ={500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500};
int MAX_IMP [nbPCAServo] ={2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500};
int MIN_ANG [nbPCAServo] ={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int MAX_ANG [nbPCAServo] ={180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180};

//Objects
Adafruit_PWMServoDriver pca= Adafruit_PWMServoDriver(0x40);

void setup(){
//Init Serial USB
Serial.begin(9600);
Serial.println(F("Initialize System"));
pca.begin();
pca.setPWMFreq(60);  // Analog servos run at ~60 Hz updates
}

void loop(){
  pcaScenario();
}

void pcaScenario(){/* function pcaScenario */ 
////Scenario to test servomotors controlled by PCA9685 I2C Module
for (int i=0; i<nbPCAServo; i++) {
  Serial.print("Servo");
  Serial.println(i);
      //int middleVal=((MAX_IMP[i]+MIN_IMP[i])/2)/20000*4096; // conversion µs to pwmval
      //pca.setPWM(i,0,middleVal);
      for(int pos=(MAX_IMP[i]+MIN_IMP[i])/2;pos<MAX_IMP[i];pos+=10){
        pca.writeMicroseconds(i,pos);delay(10);
      }
      for(int pos=MAX_IMP[i];pos>MIN_IMP[i];pos-=10){
        pca.writeMicroseconds(i,pos);delay(10);
      }
      for(int pos=MIN_IMP[i];pos<(MAX_IMP[i]+MIN_IMP[i])/2;pos+=10){
        pca.writeMicroseconds(i,pos);delay(10);
      }
      pca.setPin(i,0,true); // deactivate pin i
    }
}

int jointToImp(double x,int i){/* function jointToImp */ 
////Convert joint angle into pwm command value
     
      int imp=(x - MIN_ANG[i]) * (MAX_IMP[i]-MIN_IMP[i]) / (MAX_ANG[i]-MIN_ANG[i]) + MIN_IMP[i];
      imp=max(imp,MIN_IMP[i]);
      imp=min(imp,MAX_IMP[i]);
      
 return imp;
}

Aplicações

  • Controlar o robô Quadrina6

Fontes

Retrouvez nos tutoriels et d’autres exemples dans notre générateur automatique de code
La Programmerie