arduino  RC Navy (2012)

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

Ajout Multi-switch 8 sorties avec <RcTxSerial> et <RcRxSerial>

Ajout Multi-Switch 8 sorties  avec <RcTxSerial> et <RcRxSerial>

Ajout Multi-Swicth 8 sorties 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 de 8 interrupteurs en plus de la position d'un potentiomètre. Ainsi, la même voie de l'émetteur est utilisée pour transporter la position des 8 interrupteurs et du potentiomètre. Côté réception, les positions des 8 interrupteurs et du potentiomètre sont extraites du message et sont utilisées pour activer/désactiver chacune des 8 sorties et pour générer l'impulsions RC proportionnelles à la position du potentiomètre.

II. Côté émetteur RC

II.1. Configuration matérielle

Ajout Multi-Swich 8 sorties 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 ATiny84 muni de ses 8 interrupteurs entre le potentiomètre et la voie1 de l'émetteur. Afin de cadencer l'émission des caractères, il est nécessaire de connecter à l'ATtiny84 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 des 8 interrupteurs (de 00 à FF: soient 256 positions, 8 bits)
yy: la position du 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'ATtiny84.

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'ATtiny84, 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'UN MULTI-SWITCH 8 SORTIES SANS PERTE DE VOIE PROPORTIONNELLE SUR EMETTEUR RC  ========================
Ce sketch permet l'ajout d'un Multi-Switch 8 sorties sans perdre de voie proportionnelle sur un émetteur RC.
Il montre comment utiliser la librairie <RcTxSerial>.
par RC Navy 2012

.----------------------+------------------
< +5V
| |
| 8 x Inter .------+------.
| _n_ |
VDD |
| .-- --E1-+ |
| | _n_ | |
| +-- --E2-+ |
| | _n_ | |
| +-- --E3-+ |
| | _n_ | |
| +-- --E4-+ |
| | _n_ | |
| +-- --E5-+ |
| | _n_ | ATtiny84 |
| +-- --E6-+ |
| | _n_ | |
| +-- --E7-+ |
| | _n_ | |
| +-- --E8-+ |
| | | |1 10K
| | | +---###--+--> Voie
| | | | |
| | | | === 47nF
# | 3| | |
Pot #<--------------+ |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_PIN 2 /* Broche Analog Input 2 (broche physique 3) */

#define E1_PIN 1
#define E2_PIN 2
#define E3_PIN 3
#define E4_PIN 4
#define E5_PIN 5
#define E6_PIN 6
#define E7_PIN 7
#define E8_PIN 8

#define NBR_ENTREES 8

uint8_t IdxToPin[NBR_ENTREES] ={ E1_PIN, E2_PIN, E3_PIN, E4_PIN, E5_PIN, E6_PIN, E7_PIN, E8_PIN};

RcTxSerial TxSerial(RCTX_PPM_PIN, RCTX_PWM_PIN, RCTX_FIFO_SIZE); /* Declare TxSerial comme objet de type RcTxSerial */

//==============================================================================================
void setup()
{
uint8_t Idx;

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)*/
/* Preparation des entrees */
for(Idx = 0;Idx <
NBR_ENTREES;Idx++)
{
pinMode(IdxToPin[Idx], INPUT);
digitalWrite(
IdxToPin[Idx], HIGH); /* Activation des pull-ups internes */
}
}
//==============================================================================================
void loop()
{
static uint32_t StartMs = millis();
char Msg[10];
uint8_t Idx, Entrees = 0;
uint16_t Pot;

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 */
/* Acquisition des entrees */
for(Idx = 0;Idx
< NBR_ENTREES;Idx++)
{
if(digitalRead(
IdxToPin[Idx]))
{
Entrees |= _BV(Idx);
}
  }
Pot = analogRead(POT_PIN) / 4; /* /4 pour avoir un résultat entre 0 et 255 (entre 00 et FF en hexadecimal) */
sprintf(Msg, "%02X%02X\r", Entrees, Pot); /* 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 Mutli-Switch 8 sorties côté récepteur RC

L'ATtiny84 se connecte sur la voie du récepteur, analyse les messages, positionne les sorties et commande le servo.
Le sketch fait appel aux librairies <RcRxSerial> pour recevoir les messages et <SoftRcPulseOut> pour générer les impulsions de commande du servo.

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

/*================= AJOUT D'UN MULTI-SWITCH 8 SORTIES SANS PERTE DE VOIE PROPORTIONNELLE SUR EMETTEUR RC  ========================
Ce sketch permet l'ajout d'un Multi-Switch 8 sorties sans perdre de voie proportionnelle sur un émetteur RC.
Il montre comment utiliser la librairie <RcRxSerial>.
par RC Navy 2012

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

| | |
| ,------+------. |
| |
VDD | |
| |
+-S8-- |
| | | |
| | +-S7-- |
| | | |
| | +-S6-- |
| | | |
| | +-S5-- |
| | | |
| | ATtiny84 +-S4-- |
| | | |
| | +-S3-- |
| | | |
| | +-S2-- |
| | | |
| | +-S1-- | =====
| | | | .------'-'--.
.-----------. | | |2 '-----| |
| |-----' 0| +
-----------------| SERVO X |
| Voie1|--------------+ GND | .-----| |

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

================================================================================================*/
#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 VOIE_PIN 1

#define S1_PIN 1
#define S2_PIN 2
#define S3_PIN 3
#define S4_PIN 4
#define S5_PIN 5
#define S6_PIN 6
#define S7_PIN 7
#define S8_PIN 8

#define NBR_SORTIES 8

uint8_t IdxToPin[NBR_SORTIES] = {S1_PIN, S2_PIN, S3_PIN, S4_PIN, S5_PIN, S6_PIN, S7_PIN, S8_PIN};

#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 Voie;

//==============================================================================================
void setup()
{
uint8_t Idx;

Voie.
attach(VOIE_PIN);
Voie.write_us(NEUTRE_US);
/* Preparation des Sorties */
for(Idx=0;Idx <
NBR_SORTIES;Idx++)
{
pinMode(IdxToPin[Idx], OUTPUT);
digitalWrite(IdxToPin[Idx], LOW);
}
}
//==============================================================================================
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 OrdreVoie, OrdreSortie, Idx;
uint16_t OrdreVoie_us;

if(
MessageCoherent())
  {
/* OK: le message ne contient que des caracteres de 0 a 9 ou de A a F (Hexa) */
OrdreSortie = (ValeurQuartet(Message[0]) * 16) + ValeurQuartet(Message[1]);
for(Idx=0;Idx <
NBR_SORTIES;Idx++)
{
digitalWrite(IdxToPin[Idx],
OrdreSortie & 1); /* La valeur de sortie est dans le bit de poids faible (le + a droite) */
OrdreSortie >>= 1; /* Decale le contenu d'un bit vers la droite */
}
OrdreVoie = (ValeurQuartet(Message[2]) * 16) + ValeurQuartet(Message[3]);
OrdreVoie_us = map(OrdreVoie, 0, 255, 1000, 2000); /* Convertir l'ordre en largeur d'impulsion en us */
  Voie.write_us(OrdreVoie_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 =================================================

Retour