Arduino Mega2560 R3 cramée en pilotant servomoteur avec GUI

Salut à tous, j'ai acheté depuis peu une carte Arduino Mega 2560 R3 pour du prototypage. Je souhaite piloter un servomoteur à l'aide d'un GUI sur ordinateur. J'ai réussi ce projet, cependant lors de la phase de tests, après une dizaine de tests réussis, le servomoteur s'est emballé, a rejoint la position max: 180° et pouf de la fumée s'est échappée de ma carte: cramée. Je n'ai pas grande idée de pourquoi la carte a cramé et j'aimerais avoir vos avis et conseils svp.

Bon je travaille avec ce que j'ai sous la main alors voici le matériel utilisé:
un ServoMoteur ReadyToSky TD8325MG qui prend 4.8V-6.8V: https://fr.aliexpress.com/item/32298149426.html

La carte arduino Mega R3, reliée via USB au port Série du PC pour la communication avec le logiciel de commande que j'ai réalisé.

Et pour alimenter le tout... une alim de labo réglée sur environs 11.8V à 0.2 A

Le circuit est branché comme suit :
Schema servomotor controle.pdf (28,2 Ko)

voici le code qui tourne sur mon arduino:



#include <Arduino.h>
#include <Servo.h>



    //PINS//
#define SERVO_PIN 11

  //Providing parameters for motor control

enum etats{
  Init,
  ConnCheck,
  DataWait,
  DataDecode,
  WaitStart,
  RunServo,
  Stop,
  SendData,
  Reset,
};

Servo monServo;
bool serialWriteDone,dataSent;
char data;
uint8_t c1,c2;
uint32_t value;
etats etat;

void writeReturnToSerial(String ret){
  Serial.print('R');
  Serial.print('<');
  Serial.println(ret);
  Serial.print('>');

}

void writeDataToSerial(char c1, char c2 )
{
                Serial.print('!');
                Serial.print('<');
                Serial.print(c1);
                Serial.print(c2);
                Serial.print('>');
                Serial.print('!');
}

void writeStrToSerial(String rtrnStr)//, char char2 )
{
                Serial.print('!');
                Serial.print('<');
                Serial.println(rtrnStr);
                Serial.print('>');
                Serial.print('!');
}


 void StopServo()
 {
  monServo.write(0);
  delay(15);
 }

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

  //pinMode(SERVO_PIN, OUTPUT);
  monServo.attach(11);
  etat=Init;
  serialWriteDone=false;
  serialWriteDone=false;

}

void loop() {

  switch(etat){
    case Init:
      dataSent=false;
      monServo.write(0);
      PulseTime=0;
      value=0;
      c1=0;
      c2=0;
      serialWriteDone=false;
      if(Serial.available()){
          data=Serial.read();
          if(data=='H'){
            etat=ConnCheck;
          }else{etat=Init;}
      }
    break;
    case ConnCheck:
        if(!serialWriteDone){
          writeReturnToSerial("H_OK");
          serialWriteDone=true;
          
        }
        if(Serial.available()){
          data=Serial.read();
        }
        if (data=='S'){
          serialWriteDone=false;
          etat=DataWait;
          
        }else{etat=ConnCheck;}
    break;
    case DataWait:
        if(!serialWriteDone){
               serialWriteDone=true;
              writeReturnToSerial("LISTN_DATA");
           }
        if(Serial.available()>3){
          data=Serial.read();
          if(data=='<'){
            c1=Serial.read();
            data=Serial.read();
            if (data==','){
              c2=Serial.read();
              data=Serial.read();
            }
            
          }
          data=Serial.read();
          if(data='>'){
            serialWriteDone=false;
            etat=DataDecode;
          }
        }else{etat=DataWait;}

    break;
    case DataDecode:
        char c1_tmp;
         if(!serialWriteDone){
              writeReturnToSerial("D_DEC");
              serialWriteDone=true;
            }

          value=int(c1);
          value=value<<8;
          value=value|int(c2);
         
          serialWriteDone=false;
          etat=WaitStart;

    break;
    case WaitStart:
          if(!serialWriteDone){
              writeReturnToSerial("WAIT_S");
              serialWriteDone=true;
          }
          if(Serial.available()){data=Serial.read();}
          if (data=='G'){
            serialWriteDone=false;
            etat=RunServo;
        }else{etat=WaitStart;}
    break;
    case RunServo:
          if(!serialWriteDone){
              writeReturnToSerial("RUN_MOT");

              serialWriteDone=true;

          }
          if (Serial.available()){data=Serial.read();}
          if(data=='B'){
            serialWriteDone=false;
            StopServo();
            etat=Stop;
          }else{
                monServo.write(value);
                delay(15);
                etat=RunServo;
          }              

    break;
    case Stop:
        if (!serialWriteDone){
            writeReturnToSerial("STOPPED");
            serialWriteDone=true;
        }
        serialWriteDone=false;
        etat=SendData;
    break;

    case SendData:
       if(!serialWriteDone){
            writeReturnToSerial("DATA_S");
            serialWriteDone=true;
            }
        
       if(!dataSent){
          /*c2=uint8_t(PulseTime);
          c1=uint8_t(PulseTime>>8);*/
          c2=uint8_t(value);
          c1=0;
          writeDataToSerial(c1,c2);
          dataSent=true;
       }
            
       if (Serial.available()){
            data=Serial.read();
          }
          
          if (data=='R'){
            writeReturnToSerial("REIN_OK");
            etat=Init;
          }else{
           etat=SendData;}
    break;

  }

}

Enfin, j'ai réalisé le GUI sur delphi, en pascal. En voici le code:

unit ServoMotor_Control_Unit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, AdPacket, OoMisc, AdPort, AdSelCom, Vcl.StdCtrls;

type
  TForm11 = class(TForm)
    displayMemo: TMemo;
    comCombo: TComboBox;
    brCombo: TComboBox;
    refreshBtn: TButton;
    initBtn: TButton;
    startBtn: TButton;
    connectBtn: TButton;
    sendBtn: TButton;
    degreeEdit: TEdit;
    portCom: TApdComPort;
    commandReturn: TApdDataPacket;
    dataReturn: TApdDataPacket;

    procedure FormCreate(Sender: TObject);

    procedure dataCreate();
    procedure dataSend();

    procedure refreshBtnClick(Sender: TObject);
    procedure initBtnClick(Sender: TObject);
    procedure connectBtnClick(Sender: TObject);
    procedure sendBtnClick(Sender: TObject);
    procedure startBtnClick(Sender: TObject);
    procedure OnCommandString(Sender: TObject; Data: AnsiString);
    procedure OnDataString(Sender: TObject; Data: AnsiString);

  private
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;

var
  Form11: TForm11;
  dataSent, motorIsRunning:boolean;
  value, shiftVal, checkVal,retVal: integer;
  byte2,byte1,retByte1,retByte2: byte;

implementation

{$R *.dfm}

//~~~~~~~~~~~~ CTOR & DTOR ~~~~~~~~~~\\

procedure TForm11.FormCreate(Sender: TObject);
var
  i : Integer;

begin
  displayMemo.Lines.Clear;
  dataSent:=false;
  motorIsRunning:=false;
  sendBtn.Enabled:=false;
  startBtn.Enabled:=false;
  connectBtn.Enabled:=false;

  comCombo.Items.Clear;
  for i := 1 to 10 do
  begin
    if IsPortAvailable(i) then
      begin
         comCombo.Items.Add ('COM' + IntToStr (i));
      end;
  end;
  brCombo.ItemIndex:=5;

end;

//~~~~~~~ Functions & Procs. ~~~~~~~~~\\


procedure TForm11.dataCreate();

begin
    value:=0;
    byte1:=0;
    byte2:=0;

    value:=value or ((StrToInt(degreeEdit.Text) and integer(511)));
    if (value>180) then
    begin
      value:=180;
      displayMemo.Lines.Add('Value dépasse 180°: Val set to 180°');
    end;

    byte2:=byte(value);
    shiftVal:= value shr 8;
    byte1:= byte1 or byte(shiftVal);
    checkVal:= integer(byte1);
    checkVal:= checkVal shl 8;
    checkVal:= checkVal or integer(byte2);
end;

procedure TForm11.dataSend();
begin
  displayMemo.Lines.Add('<' + IntToStr(byte1) +','+ IntToStr(byte2) + '>');
  portCom.PutString('<' + Ansichar(byte1) +','+ Ansichar(byte2) + '>');
end;
//~~~~~~~~ Messages & Events ~~~~~~~~~~\\



procedure TForm11.refreshBtnClick(Sender: TObject);
begin
 comCombo.Items.Clear;
  TThread.CreateAnonymousThread(
    procedure
    var
    i : Integer;

    begin
       for i := 1 to 64 do
       begin
       Application.ProcessMessages;
        if IsPortAvailable(i) then
          begin
            comCombo.Items.Add ('COM' + IntToStr (i));
          end;
        end;
    end).Start
end;

procedure TForm11.initBtnClick(Sender: TObject);
begin
  if not(dataSent) then
  begin
  portCom.Baud:=StrToInt(brCombo.Text);
  portCom.Parity:=TParity.pNone;
  portCom.StopBits:=1;
  portCom.ComNumber:=StrToInt(comCombo.Text[4]);
  value:=0;
  byte1:=0;
  byte2:=0;
  degreeEdit.Clear;
  dataSent:=false;

  commandReturn.StartString:='R<';
  commandReturn.EndString:='>';

  dataReturn.StartString:='!<';
  dataReturn.EndString:='>!';

  displayMemo.Lines.Add(' ');
  displayMemo.Lines.Add('~~~ NEW SEQUENCE ~~~');
  displayMemo.Lines.Add('Initialisation port COM' + IntToStr(portCom.ComNumber)+ ':Ok');
  portCom.Open:=True;
  portCom.FlushOutBuffer;
  portCom.FlushInBuffer;
  connectBtn.Enabled:=true;
  end;

  if(dataSent) then
  begin
    portCom.PutChar('R');
  end;

end;



procedure TForm11.connectBtnClick(Sender: TObject);
begin
   portCom.PutChar('H');
end;

procedure TForm11.sendBtnClick(Sender: TObject);
begin
  dataCreate();
  portCom.PutChar('S');
end;

procedure TForm11.startBtnClick(Sender: TObject);
begin

    if not(motorIsRunning) then
    begin
      portCom.PutChar('G');
    end;

    if motorIsRunning then
    begin
      portCom.PutChar('B');
    end;
end;


procedure TForm11.OnCommandString(Sender: TObject; Data: AnsiString);
begin
       if Pos('H_OK',Data)>0 then
    begin
      displayMemo.Lines.Add('Handshake: OK');
      sendBtn.Enabled:=true;
    end;

    if (Pos('LISTN_DATA', Data) > 0 ) then
    begin
      //displayMemo.Lines.Add('Wait for Data');
      dataSend();

    end;

    if (Pos('D_DEC', Data) > 0 ) then
    begin
      //displayMemo.Lines.Add('dataDecode');
    end;

    if (Pos('WAIT_S', Data) > 0 ) then
    begin
      displayMemo.Lines.Add('Waiting Start');
      startBtn.Enabled:=true;
    end;

    if (Pos('RUN_MOT', Data) > 0 ) then
    begin
      motorIsRunning:=true;
      displayMemo.Lines.Add('Servo is running');
      startBtn.Caption:='Stop';
    end;

    if (Pos('STOPPED', Data) > 0 ) then
    begin
      displayMemo.Lines.Add('Motor Stopped');
      startBtn.Caption:='Start';
      startBtn.Enabled:=false;
      sendBtn.Enabled:=false;
      connectBtn.Enabled:=false;
    end;

    if (Pos('DATA_S', Data) > 0 ) then
    begin
      //displayMemo.Lines.Add('Receiving Data');
      dataSent:=true;
      initBtn.Caption:='Reinit.';
    end;

    if (Pos('REIN_OK', Data) > 0 ) then
    begin
      displayMemo.Lines.Add('Reset: OK');
      dataSent:=false;
      initBtn.Caption:='Init.';
      motorIsRunning:=false;
      sendBtn.Caption:='Send';
      sendBtn.Enabled:=false;
      startBtn.Enabled:=false;
      connectBtn.Enabled:=false;
      degreeEdit.Clear;
    end;
end;

procedure TForm11.OnDataString(Sender: TObject; Data: AnsiString);
begin
    retByte1:=byte(Data[1]);
    retByte2:=byte(Data[2]);

    retVal:=integer(retByte1);
    retVal:= retVal shl 8;
    retVal:=retVal or integer(retByte2);

    displayMemo.Lines.Add('Pulse width: '+ IntToStr(retVal)+'microS');

end;

end.

Voilà, j'espère que vous pourrez m'éclairer un peu. A noter, que l'alimentation est d'abord branchée sur une breadboard pour lier le condo. de découplage.
Je travaille dans un environnment avec de l'huile et j'ai découvert en revenant que pas maldh'uile avait coulé sous la breadboard (sous la partie alimentation). Cela a peut-être courtcircuité l'alim mais le condensateur semble intact.

Au plaisir de vous lire car j'ai peut-être fait une erreur bête qui m'échappe ou ai négligé une caractéristique de la carte ou du moteur et je ne compte pas cramer la future remplaçante de feu celle-ci.

Merci !

comment est alimenté le servo?

Bonjour,
le servo est alimenté via le port 5V de l'Arduino. Et l'Arduino est alimentée en environs 12v sur la pin Vin afin d'avoir la puissance nécessaire à la commande du servo.

Il est fort probable que le régulateur de la Mega n'a pas supporté d'alimenter le servo et il a probablement rendu l'âme.

C'est un srvo costaud. Les infos sur le servo sont:

Courant de ralenti: 4.8V/470mA 6.0V /650mA
Courant de charge: 4.8V/750mA 6.0V /950mA

donc en partant de 12V pour faire du 5V, il y a 7V aux bornes du régulateur, en admettant même que tu ais fait fonctionner le servo sans charge, la puissance dissipée dans le régulateur tourne autour de 5W. La carte n'est pas dimensionné pour dissiper une telle puissance.

As-tu essayé de faire fonctionner la carte sans le servo en l'alimentant par le port USB, ou avec un 5V de bonne qualité directement par la broche 5V de la carte?

EDIT: tu pourrais faire un photo bien nette du coté de l'alimentation (cette partie là)
image

1 Like

La cause de ton problème est très certainement ce que t'a dit "fdufnews". La manip qu'il te propose est judicieuse : si ton Mega 2560 fonctionne dans ces conditions, c'est que ce n'est que le régulateur qui mort. Si tu es bon bricoleur, tu devrais pouvoir le changer. Voire même le remplacer par un régulateur plus puissant que tu mettrais hors de la carte sur un petit radiateur.

Cordialement.

Pierre.

1 Like

Merci pour vos réponses à @ChPr et toi, cela pourrait expliquer que la carte ait cramé lors d'un test de maintien en position durant 1 minute ou 2. Voici une photo de la partie alim, et une de l'ensemble de la carte qui je pense vous indiquera aussi que même en changeant les régulateurs la carte est cuite ^^ mais sans cela j'aurais bien tenté la manip, merci du tuyau!

Partie Alim:


Carte et microprocesseur :


Le orobléme est que dans ces conditions, l'alimentation dite de labo est 1 alim 0,2A

Qu'est ce qui se passe si on tire plus de 0,2A?

Sur 1 vraie alim de Labo, si on limite I à 0,2A, c'est Vin qui s'écroule.
V usb prend le relais
On ne tire plus de courant sur Vin
Vin remonte
Vin prend le relais sur V usb
On tire du courant sur Vin qui s'écroule,

Un truc de ce genre non?

C'est pas terrible terrible.

En tout cas, il faut une alim de labo capable de fournir du courant pour le servo moteur ET pour l'Arduino, donc plus que 0,2A.

Ensuite, il vaudrait mieux alimenter le servo avec sa propre alim 5V prise par exemple sur l'alimentation de labo, et de bonne puissance (5V/750mA au mini)

Au fait, c'est quoi ces boursouflures?

Merci pour ta réponse. Ces boursoufflures c'est mon microprocesseur qui a cramé :melting_face: . La carte est foutue, je vais en commander une nouvelle mais tant que je ne suis pas sûr de ne pas la cramer je vais éviter de la brancher. Au début je n'alimentais que le moteur en puissance et la carte en 5V via USB. La carte envoyait simplement le signal impulsion au moteur. Cependant les mouvements de mon servomoteur étaient très saccadés et moyens. C'est pour cela que j'ai fini par alimenter la carte via Vin et le servomoteur directement depuis le 5V de la carte. Ne possédant pas le câble d'alimentation jack, j'ai dû procéder à partir de mon alim de labo, une basetech BT-305.

Bizarre que ce soit le microprocesseur qui crame alors que sa charge est faible (signal de commande du servo). N'aurais-tu pas inversé les deux fils de 5V et commande du servo ?

En tous cas veilles-y au prochain branchement.

Cordialement.

Pierre.

Si le régulateur crame et que pas de chance il se mette en court-circuit on retrouve 12V sur toute la carte.

Dans tous les cas le servo qui consomme beaucoup ne doit pas être alimenté par la carte Arduino mais directement par l'alimentation de puissance il faut 5V / 1A.
L'arduino peut être alimentée

  • soit par une alimentation séparée, plus sûr mais ne pas oublier de réunir toutes les masses,
  • soit par la même alimentation que le servo. Il y a un risque de perturber l'Arduino si l'alimentation s'écroule.

Le branchement était bon. Je commandais le servo depuis un quelques minutes déjà pour des tests en envoyant sa commande d'angle depuis le PC. Je n'avais pas identifié de pb. Je me suis penché sur mes codes et mon branchement ce matin, et ai modifié quelques trucs en m'inspirant de vos réponses. J'ai pu piloter le servo à l'aide d'une Arduino Nano, et sans la faire cramer YOUPI!
J'ai modifié la manière d'alimenter la carte et le servo moteur, en repartant sur mon idée de base: une alim en puissance pour le servomoteur uniquement avec une limite en courant plus élevée: je sors du 5.5V limité à 0.81 A.
La carte est alimentée et communique avec le PC via USB.

Dans le code Arduino, j'ai modifié mes états Init. et RunServo en ajoutant une condition pour écrire une seule fois la commande au moteur. Ainsi, plus de boucle sur une génération de signal.
Je pense au final que c'est la combinaison de toutes ces imprécisions de ma part qui m'a mené à cramer la Mega.

Pourquoi limiter le courant aussi bas.
Le servo est donné pour une consommation de 960mA max (et il n'est pas précisé si c'est une consommation moyenne ou crête) la carte Arduino suivant le modèle va consommer grosso modo entre 30 et 80mA. Donc il vaudrait mieux limiter au-dessus de 1A.
Limiter le courant trop bas n'est pas toujours une solution et peut conduire à des comportements erratiques si l'alimentation passe en limitation.

Hmm je vois, j'ai augmenté la limite en courant mais sans dépasser les 960mA car le servo et la carte ne partagent plus la même alimentation. Celle délivrant du 5.5V et à maintenant 950 mA est dédiée au servo. J'ai donc choisi de rester sous la limite constructeur. J'observe en revanche un "meilleur" fonctionnement du servo avec cette valeur.

Je ne vais pas répetter ce que dit @fdufnews , mais tu serre trop la bride.

Si par exemple le servo moteur rencontre un point dur à vaincre et qu'il a besoin d'un surplus momentané de courant, tu va rentré en limitation de courant.

C'est à dire que le courant serra limité à 960mA et la tension va chuter ( si tu limite I et diminue R, il faut bien que U diminue pour que U=RI reste vrai).

Donne toi une marge d'au moins 25%, (limite constructeur X 1,25) pour être à l'aise.

1 Like

Bien reçu je vais augmenter un peu. Simple curiosité, pourquoi les valeurs nominales constructeur ne sont pas suffisantes ? Ou plutôt pourquoi le constructeur fournit ces valeurs si elles ne sont pas suffisantes ?
Par ailleurs, dans mon cas, le couple du moteur en question est un peu overkill par rapport à l'effort demandé au moteur.

Parce que comme dit plus haut, la valeur indiquée par le constructeur est peut-être une valeur moyenne et non une valeur crête. Supposons que le moteur consomme 940mA 98% du temps et que 2% du temps il consomme 1,2A. La valeur moyenne sera inférieure à la limite mais ton alim va peut-être mal le supporter et se mettre en limitation ou devenir instable.

1 Like

@p0l0

Exactement, valeur nominale n'est pas égale à valeur maximale ou plutôt crête (occasionnelle).

1 Like

Bonjour

Dans cette annonce concernant le TD8325MG,
après avoir cliqué sur le bouton Voir plus on peut lire :


5. Courant de décrochage: 2600mA ± 10% - 3400mA ± 10%

et concernant les servo-moteurs et le terme décrochage (en anglais : stall):

Il existe une "traduction en français" au format pdf

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.