Défi Robot : Aerocat BT

L'Aerocat BT est un modèle réduit d'aéroglisseur commandé en Bluetooth via une application Android. A l'issue des 36 heures de projet un concours suivant 3 épreuves sera organisé :

1. Le plus rapide en ligne droite

2. Le plus maniable parcours en slalomant

3. Le plus endurant

La solution doit être démontable : Tous les composants fournis peuvent être démontés à l'aide d'outils classique.

Composants

Carte microcontrôleur :

Raspberry pico 2

Module BT

Module Grove :

Serial Bluetooth V3.01

Servo moteur

Power HD 1600A

Driver moteur

40 A 2-4 S LiPo BEC 5V 3A

Mteur brushless

A2212/6T 2200KV

Moteur Brushless

Schéma électrique

Programme test sans Appli Android

Pour piloter à la'ide deux moniteur série, le servo-moteur et le moteur Brushless vous pouvez utiliser le programme ci-dessous :

1
/*
2
3
 Moteur Brushless et SERVO controle grace au moniteur serie :
4
5
  Pour le pilotage du moteur brushless l'intialisation doit se faire a la vitesse min 
6
  
7
 L'utilisateur envoie 2 nombres separes par un espace au terminal serie 
8
      Le premier est un identifiant :
9
         - 200 pour la commande du brushless 
10
         - 201 pour la commande du SERVO 
11
      Le second est l'angle ou la vitesse compris entre 0 et 180.
12
13
Exemple : 
14
Si on souhaite piloter le servo a un angle 90 l'appli envoie
15
    201 90.
16
17
Pour piloter le brushless a une vitess de 50 on envoie
18
     200 50. 
19
*/
20
#include <Servo.h>
21
22
Servo servo;
23
Servo brushless;
24
25
const int SERVO_PIN = 16;
26
const int BRUSHLESS_PIN = 18;
27
28
const int VITESSE_MAX   = 100;
29
30
int code = 0;
31
int value = 0;
32
33
// buffer réception
34
char buffer[32];
35
uint8_t idx = 0;
36
37
void setup() {
38
  Serial.begin(9600);
39
40
  servo.attach(SERVO_PIN,544, 2400);
41
  brushless.attach(BRUSHLESS_PIN, 1000, 2000);
42
43
  servo.write(90);
44
  brushless.write(0);
45
46
  Serial.println("System ready");
47
}
48
49
void loop() {
50
  while (Serial.available() > 0) {
51
    char c = Serial.read();
52
53
    // fin de trame
54
    if (c == '\n') {
55
      buffer[idx] = '\0';
56
      parseBuffer();
57
      idx = 0;
58
    }
59
    else {
60
      if (idx < sizeof(buffer) - 1) {
61
        buffer[idx++] = c;
62
      }
63
    }
64
  }
65
}
66
67
void parseBuffer() {
68
  int c = 0;
69
  int v = 0;
70
71
  // parsing simple "code value"
72
  if (sscanf(buffer, "%d %d", &c, &v) == 2) {
73
    code = c;
74
    value = v;
75
76
    executeCommand(code, value);
77
  }
78
  else {
79
    Serial.println("Parse error");
80
  }
81
}
82
83
void executeCommand(int code, int value) {
84
85
  if (code == 200) {
86
    // brushless (ESC)
87
    value = constrain(value, 0, VITESSE_MAX);
88
    //brushless.writeMicroseconds(map(value, 0, 100, 1000, 2000));
89
    brushless.write(value);
90
    Serial.print("ESC: ");
91
    Serial.println(value);
92
  }
93
94
  else if (code == 201) {
95
    // servo
96
    value = constrain(value, 0, 180);
97
    servo.write(value);
98
99
    Serial.print("SERVO: ");
100
    Serial.println(value);
101
  }
102
103
  else {
104
    Serial.println("Unknown code");
105
  }
106
}

Configuration du module :

1
// Definition des fonctions 
2
3
// sendATCommand permet d'envoyer des commande AT pour configurer le module BT
4
// cmd : chaine de commande AT ("AT")
5
// timeout : 2000 ms pat defaut si pas de reception
6
// retourne 
7
//    Vrai si la la commande a bien ete prise en compte
8
//    Faux si timout depasse
9
// 
10
bool sendATCommand(const char* cmd, unsigned long timeout = 2000);
11
12
// fatalError Affiche un message d'erreur et boucle indefiniement en faisant 
13
// clignoter la LED et affiche le message d'erreur au terminal serie 
14
void fatalError(const char* message);
15
16
// requireATCommand Teste la commande AT cmd saisie en paramètre avec gestion 
17
// de l'erreur si reponse trop logue     
18
void requireATCommand(const char* cmd);
19
20
// Efface le buffer de Serial1. vider complètement le buffer série… et s’assurer 
21
// qu’il n’y a plus rien qui arrive pendant un certain temps
22
void clearSerial1Buffer(unsigned long silenceMs=200);
23
void setup() 
24
{
25
    pinMode(LED_BUILTIN,OUTPUT);
26
    Serial.begin(9600);
27
    while (!Serial);    // Attendre que Serial dispo
28
29
    Serial1.begin(9600);               // Commande AT  
30
    requireATCommand("AT");           // AT   
31
    requireATCommand("AT+DEFAULT");   // AT+DEFAULT
32
    requireATCommand("AT+NAMEMyBT"); // AT+NAME MyBT
33
    requireATCommand("AT+PIN1234");   // AT+PIN 1234
34
    clearSerial1Buffer();
35
}
36
void loop() 
37
{
38
}
39
void requireATCommand(const char* cmd) {
40
    if (!sendATCommand(cmd)) {
41
        fatalError(cmd);
42
    }
43
}
44
45
void fatalError(const char* message) {
46
    Serial.println(message);
47
48
    while (true) {
49
        digitalWrite(LED_BUILTIN, HIGH);
50
        delay(100);
51
        digitalWrite(LED_BUILTIN, LOW);
52
        delay(900);
53
    }
54
}
55
56
bool sendATCommand(const char* cmd, unsigned long timeout) 
57
{
58
    while (Serial1.available() > 0) {  // Vider le buffer
59
        Serial1.read();
60
    }
61
    Serial1.print(cmd);               // Envoyer la commande AT
62
63
    unsigned long start = millis();  // memoriser l'instant avant la boucle
64
    char buffer[32] = "";                    
65
    uint8_t index = 0;
66
67
    while (millis() - start < timeout) {  // Tant que  temps dans la boucle pas dépassé
68
        if (Serial1.available()) {        //  
69
            char c = Serial1.read();      // On lit la réponse du module
70
71
            if (index < sizeof(buffer) - 1) { // on evite les deassement du buffer
72
                buffer[index++] = c;      //         on stoque le caractere dans le buffer
73
                buffer[index] = '\0';     //         Met caractère fin de chaine
74
            }
75
76
            if (strstr(buffer, "OK") != nullptr) { // Si la chaîne buffer contient le texte "OK"
77
                Serial.println("OK recu");        //  
78
                return true;
79
            }
80
        }
81
    }
82
83
    Serial.println("Timeout"); 
84
    return false; 
85
}
86
void clearSerial1Buffer(unsigned long silenceMs) 
87
{
88
    unsigned long lastByteTime = millis();
89
90
    while (millis() - lastByteTime < silenceMs) { 
91
        while (Serial1.available() > 0) {
92
            Serial1.read();
93
            lastByteTime = millis();
94
        }
95
    }
96
}

Programme final :

1
/*
2
3
 Moteur Brushless et SERVO connectes :
4
5
  Pour le pilotage du moteur brushless l'intialisation doit se faire a la vitesse min 
6
  Le smartphone envoit 2 octets bruts 
7
      Le premier est un identifiant :
8
         - 200 pour la commande du brusless 
9
         - 201 pour la commande du SERVO 
10
      Le second est l'angle ou la vitesse compris entre 0 et 180.
11
Si on souhaite piloter le servo a un angle 90 l'appli envoit en BT 201 puis 90.
12
Pour piloter le brushless a une vitess de 50 on envoit 200 puis 50. 
13
*/
14
#include <Servo.h>
15
16
const int VITESSE_MAX   = 100;
17
const int VITESSE_MIN   = 0;
18
const int BRUSHLESS_PIN = 18;
19
Servo brushless;
20
21
const int SERVO_PIN = 16;
22
Servo servo;
23
24
void setup() 
25
{
26
  Serial.begin(9600);
27
  Serial1.begin(9600);
28
  servo.attach(SERVO_PIN,544, 2400);
29
  brushless.attach(BRUSHLESS_PIN, 1000, 2000); // Brushless sur la broche 9 
30
  brushless.write(0); // vitess min
31
  Serial.println("commande moteur brushless ");
32
33
}
34
void loop() 
35
{
36
  uint8_t Code  = 0;        // Donnee transmise par le module Bluetooth
37
  uint8_t Angle =   0;
38
  if(Serial1.available()==2) // Si 2 octets sont disponible pour etre lu
39
  {
40
    Code  = Serial1.read(); //    Lecture du code 200 pour vitesse et 201 pour l'angle du servo
41
    
42
    Serial.print("code = ");
43
    Serial.println(Code);  //
44
    if(Code == 200)
45
    {
46
      Angle = Serial1.read(); //    Lecture de l'angle
47
      Serial.print("angle = ");
48
      Serial.println(Angle);  //
49
50
      if (Angle>= 0 && Angle <= 180) 
51
      {
52
        if(Angle > VITESSE_MAX ) Angle = VITESSE_MAX;
53
        if(Angle < VITESSE_MIN ) Angle = VITESSE_MIN;
54
        brushless.write(Angle);
55
      } 
56
    }else if (Code == 201) 
57
    { 
58
      Angle = Serial1.read();    //    Lecture de l'angle
59
    
60
      Serial.print("angle = ");
61
      Serial.println(Angle);  //
62
      if (Angle>= 0 && Angle <= 180) 
63
      {
64
        servo.write(Angle);
65
      } 
66
 
67
    }
68
  }
69
}