arduino  RC Navy (2012)

Exemple asynchrone avec les librairies <RcTxSerial> et <RcRxSerial>:

Ajout voie proportionnelle avec <RcTxSerial> et <RcRxSerial>

Note: Cette application nécessite à elle seule une page spécifique, parce, bien que simples, les sketches côtés émission et côté réception méritent d'être détaillés.

I. Introduction

Les librairies <RcTxSerial> et <RcRxSerial> permettent de créer un lien série entre un émetteur RC et un récepteur RC. Ceci donne la possibilité d'envoyer n'importe quel message de l'émetteur vers le récepteur. D'où l'idée de transmettre dans le message la position non pas d'un, mais de 2 potentiomètres. Ainsi, une seule voie de l'émetteur est utilisée pour transporter la position de 2 potentiomètres, libérant la seconde voie pour un troisième potentiomètre. Côté réception, les positions des 2 potentiomètres sont extraites du message et sont utilisées pour générer 2 impulsions RC proportionnelles à ces positions.

II. Côté émetteur RC

II.1. Configuration matérielle

Ajout voie côté émetteur RC

ATTENTION: le montage tel que proposé ne fonctionne que pour les émetteurs dont les bornes extrêmes des potentiomètres sont alimentés en +5V. Vérifiez également la polarité avant de brancher! L'auteur de ces pages ne pourra être tenu pour responsable en cas de mauvais branchement! Il est possible d'adapter le montage à d'autres tensions d'alimentation.

La modification consiste à insérer un ATiny85 entre les 2 potentiomètres et la voie1 de l'émetteur. Afin de cadencer l'émission des caractères, il est nécessaire de connecter à l'ATtiny85 le signal PPM disponible sur la prise "écolage" de l'émetteur. Le signal PPM peut être indifféremment de modulation positive ou négative: la librairie <RcTxSerial> se charge d'extraire automatiquement l'horloge de période voisine de 20 ms.

Le message a le format suivant: xxyy<CR>
avec:

xx: la position du premier potentiomètre (de 00 à FF: soient 256 positions, ce qui est nettement suffisant)
yy: la position du second  potentiomètre (de 00 à FF: soient 256 positions, ce qui est nettement suffisant)
<CR>: caractère "Retour Chariot" (Carriage Return) qui va marquer la fin du message

Les positions sont codées en hexadécimal codé ASCII (de 00 à FF) puisque cette notation ne consomme que 2 caractères au lieu de 3 en décimal (000 à 255).
Le message est donc composé de
5 caractères au lieu de 7. Ceci à une importance, puisque cela va définir la périodicité d'envoi du message.

II.2. Envoi des caractères par l'ATtiny en utilisant la librairie <RcTxSerial>

Les caractères sont codés sur 8 bits en utilisant le standard ASCII.

Par exemple, le caractère 'e' est codé 65 en hexadécimal. La notation hexadécimale permet de représenter 8 bits sur 2 quartets de 4 bits.
Dans le cas du
caractère 'e', '6' est le quartet de poids fort et '5' est le quartet de poids faible.
Dans la notation hexadécimale, le quartet de poids fort a une pondération de 16 et celui de poids faible, une pondération de 1, on peut facilement retrouver la valeur décimale:
6 x 16 + 5 x 1 = 101.
Le lecteur pourra vérifier dans la table ASCII que 101 est le code en décimal du caractère 'e': donc 65 en hexadécimal et 101 en décimal représentent le même caractère ('e').

Ce qu'il faut retenir, c'est le codage hexadécimal de chaque caractère à transmettre. Voir les colonnes  "Hex" et "Caractère" de la la table ASCII.

Exemple d'émission de caractères

Les traces ci-dessus correspondent aux signaux "PPM" (espèce de peigne) et "voie 1" (montagne russe) sur l'ATtiny85.

La vue correspond à l'envoi des 4 caractères 'e', 'c', <CR> et <LF>. Il s'agit des 4 derniers caractères du message "Uptime: 123 sec<CR><LF>" du sketch de démo de la librairie <RcTxSerial> qui transmet le nombre de secondes écoulées depuis le démarrage du sketch.

On y voit qu'un demi caractère (quartet = 4 bits) est transmis à chaque train PPM: l'amplitude du signal correspond à la valeur du quartet: de 0 à 15. La valeur 16 signifie: "rien à transmettre" (visible en haut à droite juste après le quartet 'A'). L'amplitude est obtenue en utilisant une sortie PWM de l'ATtiny85, suivi d'un filtre RC passe-bas.

Il faut donc 2 trains PPM pour transmettre un caractère complet (2 quartets = 8 bits). L'émission d'un caractère prend donc: 2 x 20 = 40 ms.
Il est donc possible de transmettre au maximum: 1000 / 40 = 25 caractères par seconde.
Comme un caractère est composé de 8 bits, cela correspond à un débit de: 25 x 8 = 200 bauds

II.3. Le sketch côté émetteur

/*================= AJOUT D'UNE VOIE PROPORTIONNELLE SUR EMETTEUR RC  ========================
Ce sketch permet l'ajout d'une voie proportionnelle sur un émetteur RC.
Il montre comment utiliser la librairie <RcTxSerial>.
par RC Navy 2012

.------+---------------+------------------
< +5V
| | |
| | .------+------.
| | |
VDD |
| # 2| |
1 10K
| PotX #<-------+ +---###--+--> Voie
| # | | |
| | | ATtiny85 | === 47nF
# | 3| | |
PotY #<--------------+ |0 GND
# | | +
-----------< PPM (Ecolage/Trainer)
| | | GND |

| |  '------+------'
| | |
'------+---------------+------------------< GND(-)

================================================================================================*/
#include <EEPROM.h>
#include <RcTxSerial.h>

#define RCTX_PPM_PIN 0 /* Broche pour recevoir le Train PPM */
#define RCTX_PWM_PIN 1 /* Broche de sortie pour attaquer la voie de l'emetteur */
#define RCTX_FIFO_SIZE 16/* Taille de la Fifo d'emission */

#define POT_X_PIN 2 /* Broche Analog Input 2 (broche physique 3) */
#define POT_Y_PIN 3 /* Broche Analog Input 3 (broche physique 2) */
RcTxSerial TxSerial(RCTX_PPM_PIN, RCTX_PWM_PIN, RCTX_FIFO_SIZE); /* Declare TxSerial comme objet de type RcTxSerial */

//==============================================================================================
void setup()
{
RcTxSerial::init(0); /* Declare que la zone EEPROM pour la classe RcTxSerial commence a l'index 0 */
/* Les 3 lignes ci-dessous montrent comment effectuer la calibration */
//RcTxSerial::setMode(RC_TX_SERIAL_MODE_TEST_MIN); /* RC_TX_SERIAL_MODE_TEST_MIN pour calibrer le min, RC_TX_SERIAL_MODE_TEST_MAX pour calibrer le max, RC_TX_SERIAL_MODE_NORMAL quand calibration finie */
//TxSerial.setPwmMin(20); /* Valeur du PWM a l'emission pour avoir 1029 us a la reception (Calibration de l'impulsion Min) */
//TxSerial.setPwmMax(240);/* Valeur du PWM a l'emission pour avoir 1970 us a la reception (Calibration de l'impulsion Max)*/
}
//==============================================================================================
void loop()
{
static uint32_t StartMs = millis();
char Msg[10];
uint16_t PotX, PotY;

if((millis() - StartMs) >= 250) /* il faut cadencer l'emission: pas plus de 25 caracteres par seconde (200 bauds) */
{
/* On arrive ici toutes les 250 ms */
StartMs = millis(); /* Rearme le chrono */
PotX = analogRead(POT_X_PIN)/4; /* /4 pour avoir un résultat entre 0 et 255 (entre 00 et FF en hexadecimal) */
PotY = analogRead(POT_Y_PIN)/4; /* /4 pour avoir un résultat entre 0 et 255 (entre 00 et FF en hexadecimal) */
sprintf(Msg, "%02X%02X\r", PotX, PotY); /* Message formaté au format: XXYY<CR> -> 5 caracteres -> 200 ms */
TxSerial.print(Msg); /* Ecrit le message dans la FIFO d'emission */
}
RcTxSerial::refresh(); /* Permet de depiler les caracteres de la FIFO d'emission et de les emettre synchronises avec le Train PPM */

}
//============================ FIN DU PROGRAMME =================================================


III. Côté récepteur RC

III.1. Configuration matérielle

Ajout voie côté récepteur RC

L'ATtiny85 se connecte sur la voie du récepteur, analyse les messages, et commande les 2 servos.
Le sketch fait appel aux librairies <RcRxSerial> pour recevoir les messages et <SoftRcPulseOut> pour générer les impulsions de commande des servos.

III.2. Le sketch côté récepteur

/*================= AJOUT D'UNE VOIE PROPORTIONNELLE SUR RECEPTEUR RC  ========================
Ce sketch permet l'ajout d'une voie proportionnelle sur un récepteur RC.
Il montre comment utiliser la librairie <RcRxSerial>.
par RC Navy 2012

.---------------+-----------+---.

| | | |
| ,------+------. | | =====
| |
VDD | | | .------'-'--.
| | |
1 '---------| |
| | +--------------| SERVO X |
| | | .---------| |
| | | | | | |
| |
ATtiny85 | | | '-----------'
| | | | | =====
| | | | | .------'-'--.
.-----------. | | |2 | '-----| |
| |-----' 0| +
--------------| SERVO Y |
| Voie1|--------------+ GND | | .-----| |

| |-----.  '------+------' | | | |
| | | | | | '-----------'
| RECEPTEUR | '---------------+-----------+---'
=====
| | .------'-'--.
| |-------------------------------------------| |
| Voie2|-------------------------------------------| SERVO Z |
| |-------------------------------------------| |
'-----------' | |
'-----------'

================================================================================================*/
#include <TinyPinChange.h> /* Librairie utilisee par <SoftRcPulseIn> */
#include <SoftRcPulseIn.h> /* Librairie utilisee par <RcRxSerial> */
#include <RcRxSerial.h> /* Librairie utilisee pour creer le port serie via ensemble RC (Cote Recepteur) */
#include <SoftRcPulseOut.h> /* Librairie utilisee pour piloter les servos */

#define RCRX_PPM_PIN 0

#define SERVO_X_PIN 1
#define SERVO_Y_PIN 2

#define NEUTRE_US 1500

#define RETOUR_CHARIOT 0x0D /* Code ASCII du Retour Chariot (Carriage Return) */

#define LONGUEUR_MSG 4
char Message[
LONGUEUR_MSG+1]; /* +1 pour 0 de fin de chaine qui remplace le CR */

RcRxSerial RxSerial(RCRX_PPM_PIN);

SoftRcPulseOut ServoX;
SoftRcPulseOut ServoY;

//==============================================================================================
void setup()
{
ServoX.attach(SERVO_X_PIN);
ServoY.attach(SERVO_Y_PIN);
ServoX.write_us(NEUTRE_US);
ServoY.write_us(NEUTRE_US);
}
//==============================================================================================
void loop()
{
if(LitMessage() > 0)
{
InterpreteEtExecute();
}
SoftRcPulseOut::refresh(); /* Rafraichit la position des servos */
}
//==============================================================================================
int8_t LitMessage()
{
int8_t Ret = -1;
char RxChar;
static uint8_t Idx = 0;

if(
RxSerial.available())
{
RxChar =
RxSerial.read();
if(Idx < LONGUEUR_MSG)
{
if(RxChar == RETOUR_CHARIOT) /* Si retour chariot: fin de message */
{
Message[Idx] = 0;/* Remplace caractere CR par fin de chaine */
Ret = Idx;
Idx = 0; /* Re-positionne index pour prochain message */
}
else
{
Message[Idx] = RxChar;
Idx++;
}
}
else Idx = 0; /* Re-positionne index pour prochain message */
}
return(Ret);
}
//==============================================================================================
void InterpreteEtExecute()
{
uint8_t OrdreX, OrdreY;
uint16_t OrdreX_us, OrdreY_us;

if(
MessageCoherent())
  {
/* OK: le message ne contient que des caracteres de 0 a 9 ou de A a F (Hexa) */
OrdreX = (ValeurQuartet(Message[0]) * 16) + ValeurQuartet(Message[1]);
OrdreY =( ValeurQuartet(Message[2]) * 16) + ValeurQuartet(Message[3]);
OrdreX_us = map(OrdreX, 0, 255, 1000, 2000); /* Convertir l'ordre en largeur d'impulsion en us */
OrdreY_us = map(OrdreY, 0, 255, 1000, 2000); /* Convertir l'ordre en largeur d'impulsion en us */
  ServoX.write_us(OrdreX_us);
ServoY.write_us(OrdreY_us);
  }
}
//==============================================================================================
uint8_t MessageCoherent()
{
uint8_t Idx, Ret = 1;

/* Verifie que les 4 caracteres du message ne contiennent que 0 à 9 ou A à F */
for(Idx=0;Idx <
LONGUEUR_MSG;Idx++)
{
if(!(Message[Idx] >= '0' &&
Message[Idx] <= '9') && !(Message[Idx] >= 'A' && Message[Idx] <= 'F'))
{
Ret = 0;
break;
}
  }
return(Ret);
}
//==============================================================================================
uint8_t ValeurQuartet(char Char)
{
uint8_t Ret;

if(Char >= '0' && Char <= '9')
{
Ret = Char - '0';
  }
else
{
Ret = Char - 'A' + 10;
  }
return(Ret);
}
//============================ FIN DU PROGRAMME =================================================

IV. Possibilités des librairies <RcTxSerial> et <RcRxSerial>

IV.1. Plusieurs ports séries pour <RcTxSerial>

La librairie <RcTxSerial> autorise la déclaration de plusieurs liens séries au sein du même sketch. Cela signifie que le sketch est capable d'écrire des messages dans plusieurs ports séries différents. Cependant, le signal "Train PPM" doit être commun (d'ailleurs sur un émetteur, il n'y a qu'un seul signal "Train PPM"). Sur un ATtiny85, les broches attaquant les voies de l'émetteur seront les impérativement broches 0 et 1, les seules qui, avec arduino, autorisent un signal PWM à une fréquence suffisante pour tenir la cadence.

Exemple de déclaration de 2 ports séries en émission:

#define RCTX_PWM1_PIN      0
#define RCTX_PWM2_PIN      1

#define RCTX_PPM_PIN         2

RcTxSerial TxSerial1(RCTX_PPM_PIN, RCTX_PWM1_PIN, 16); /* Avec une FIFO de 16 octets */
RcTxSerial TxSerial2(
RCTX_PPM_PIN, RCTX_PWM2_PIN, 32); /* Avec une FIFO de 32 octets */

Il peut être intéressant de passer par plusieurs ports séries si la longueur des messages est importante afin d'avoir une meilleure réactivité (nombre de messages par seconde).

IV.2. Plusieurs ports séries pour <RcRxSerial>

La librairie <RcRxSerial> autorise la déclaration de plusieurs liens séries au sein du même sketch. Cela signifie que le sketch est capable de lire des messages dans plusieurs ports séries différents (donc à partir de plusieurs voies d'un récepteur).

Exemple de déclaration de 2 ports séries en réception:

#define RCRX_PPM1_PIN     0 /* Connecte par exemple a la voie N°1 du recepteur */
#define RCRX_PPM2_PIN     4
/* Connecte par exemple a la voie N°2 du recepteur */

RcRxSerial RxSerial1(RCRX_PPM1_PIN);
RcRxSerial RxSerial2(
RCRX_PPM2_PIN);

Retour