Errore in elaborazione che si risolve solo se utilizzo la seriale

Buongiorno a tutti,
ho un problema stranissimo in un codice. Si tratta di un Arduino Nano utilizzato per controllare un tornio che mi sono autocostruito.
In ingresso ho, tramite interrupt, un sensore di velocità costituito dalla solita rotellina che interrompe il fascio di luce. Misuro il tempo tra i "tic" (faccio la media degli ultimi 20 per avere una misura più affidabile) e così ricavo la velocità di rotazione del motore. La confronto con la velocità desiderata e, applicando una semplice operazione matematica, vado a modificare l'uscita in pwm di un pin che pilota, attraverso un mosfet, il motore. In questo modo posso mantenere costante la velocità del motore al variare del carico meccanico.
Fin quì tutto bene e tutto facile. Il problema è che lo spezzone di codice funziona bene solo se, all'interno del ciclo di verifica e controllo, metto delle istruzioni Serial.print che mi inviano sulla seriale le variabili utilizzate dal ciclo. Se tolgo i Serial.print il codice commette errori, sembra come se non riesca a leggere correttamente il valore di alcune variabili, quindi il calcolo della potenza da erogare fallisce ed il motore viene azionato alla massima velocità.
In calce c'è l'intero codice, le righe in questione sono quelle con Serial.print("Output: ");
Per completezza, vi segnalo che questo Arduino comunica via I2C con un secondo Arduino nano che gestisce l'interfaccia utente. Legge anche la corrente assorbita dal motore (per controllare che non superi i valori ammessi dal motore e dall'alimentatore) e pilota anche un motore stepper per azionare il carrello portautensili.
Il codice è sufficientemente commentato, se però qualcosa non vi è chiaro non esitate a chiedere.
La mia domanda è: perché se elimino (commento) il blocco dei Serial.print l'elaborazione produce risultati non corretti?
Grazie

Carlo

p.s. il codice è comunque incompleto, ci sono funzioni ereditate da una versione precedente e che non mi occorrono e che devo eliminare e ci sono altre cose che devo ancora implementare.


/*
 * Programma per la gestione del tornio.
 * Utilizza un motore stepper per il carrello ed un motore DC per il mandrino. 
 * Il motore del mandrino è collegato ad un encoder da 20 impulsi giro ed aziona il mandrino attraverso una riduzione 1:4.
 * La velocità  del motore DC viene controllata in PWM 
 * Lo stepper muove una vite senza fine attraverso una riduzione 1:3. Lo stepper viene azionato senza una libreria dedicata. 
 * Il singolo passo dello stepper corrisponde ad un avanzamento del carrello do 55,6 millesimi di mm.
 * Il sistema interagisce con l'utente attraverso un secondo arduino, che gestisce un modulo display + input
 * dal secondo arduino arrivano i comandi per impostare la velocità del motore DC ed i movimenti del carrello
 * al secondo arduino vengono inviati i dati sulla velocità corrente del motore e sulla posizione del carrello
 * la comunicazione avviene in I2C attraverso la libreria WIRE. Questo arduino è SLAVE
 * 
 * Le connessioni sono:
 * encoder del motore DC: pin 2
 * PWM del motore DC: pin 3 
 * Stepper : pin 7,8,9,10
 * Interruttori di fine corsa: 11 e 12
 * Ingresso amperometro A1 
 * Ingresso potenziometro A2
 * 
 * todo list. Togliere tutti i riferimenti a Potenziometro
 * 
 * 
 * 
 */

#include <Wire.h>

// Associazione pin di I/O

// motore mandrino
const byte PinMotore = 3;              // Pin di output PWM del motore DC del mandrino
const byte encoderPin1 = 2;            // Encoder del motore DC può essere solo 2 o 3, in quanto supportano l'interrupt.
const byte ampere_in = A1;             // Ingresso usato per misurare la corrente assorbita dal motore del mandrino

// Ponte ad H del motore Stepper:
const byte motor_pin_1 = 8;
const byte motor_pin_2 = 9;
const byte motor_pin_3 = 10;
const byte motor_pin_4 = 11;

// fine corsa del carrello
const byte fineDx = 7;
const byte fineSx = 6;

// altri pin
const byte pot_in = A2;                // imposto il pin di ingresso per il potenziometro


// variabili usate per la comunicazione I2C
volatile byte Codai2C [32];           // buffer di ingresso
volatile byte Codai2C_len = 0;        // lunghezza del buffer di ingresso 
byte Codai2C_tmp [64];                // buffer di lavoro, riceve dal buffer di ingresso
byte Codai2C_tmp_len = 0;             // lunghezza del buffer di lavoro
byte Elem_i2C_wrk = 0;                // appoggio per l'elemento del buffer in lavorazione. Più comodo che lavorare sull'array
byte Risposta[32] ;                   // buffer di uscita
byte Risposta_ptr ;                   // lunghezza del buffer di uscita.

// Imposto le variabili per la gestione del motore
int Setpoint = 0;                     // utilizzata per definire la velocità target effettiva. Deriva dalla MotorSpeedImpostata ed è influenzata dall'assorbimento di corrente
int MotorSpeedCorrente = 0;           // velocità corrente del motore. 
int Output = 0;                       // valore del PWM applicato al motore
int Output_tmp = 0;                   // temporanea di appoggio per l'output del motore
unsigned long CheckPrecedente = 0;    // variabile per la temporizzazione dei cicli di verifica velocità del motore
int MotorAmpereCorrente = 0;          // Assorbimento corrente in Ampere del motore.
const int MaxCorrenteMotore = 500;    // Massimo assorbimento di corrente del motore, da impostare dopo prove sperimentali con l'alimentatore
const int MotorAccellMax = 20;        // accelerazione massima tollerata dall'alimentatore, da impostare dopo prove sperimentali con l'alimentatore
int Passaggio = 0;                    // conteggia quante misurazioni di corrente sono state sopra la soglia massima e quindi sono state causa di limitazione del numero di giri del motore
int OutputPrec = 0;                   // memorizza il precedente valore di output per il motore, necessita alla avviolento()
double delta = 0;                     // differenza tra la velocità impostata e quella corrente

//Imposto le variabili da esportare nel modulo di gestione del motore e dello stepper
// Motore mandrino
int MotorSpeedImpostata = 0;           // velocità attesa del mandrino, espressa in giri al minuto
bool MotoreAcceso = false;             // Stato del motore del mandrino
// Stepper
int StepperSpeed = 0;                  // velocità  del carrello, espresso in millesimi di mm per giro del mandrino
long PercorsoDaFare = 0;               // Persorso che deve compiere il carrello, alla velocità impostata da StepperSpeed o dal Potenziometro
int PosizioneAttualeCarrello = 0;      // posizione corrente del carrello, rispetto alla posizione di partenza
bool Potenziometro = false;            // definisce se il potenziometro può azionare il carrello 
//long PosizionefinaleCarrello = 0;      // posizione di destinazione del carrello, rispetto alla posizione di partenza
//long EstremoSinistroCarrello = 0;      // limite sinistro della corsa del carrello, rispetto alla posizione di partenza
//long EstremoDestroCarrello = 0;        // limite destro della corsa del carrello, rispetto alla posizione di partenza
//static long LunghezzaCarrello = 10000; // Lunghezza fisica del carrello, da impostare bene dopo averla misurata.



// definisco le dimensioni dei singoli passi elementari
const byte NumeroSettori = 80;          // numero di settori dalla ruota (20) dell'encoder del motore DC * il rapporto di trasmissione (4)
const float LunghezzaPasso = 55.6;      // espressa in millesimi di millimetro
float MM_Per_Giro = 0;                  // quanti mm devono essere percorsi per ogni giro
float MM_Per_Settore = 0;               // quanti mm devono essere percorsi per ogni settore dell'encoder
volatile float MM_DaFare = 0;           // coda di mm ancora da percorrere
float MM_Temp = 0;                      // variabile di appoggio
long encoderValue = 0;                  // valore corrente dell'encoder motore. Deve essere long perché¨ richiede il segno.
long time1 ;                            // usata nella routine di avanzamento dello stepper per non superare la velocità massima
volatile long t[20] ;                   // usata nell'interrupt per calcolare la velocità del motore DC. Memorizza i tempi degli ultimi 20 passaggi.
volatile byte tic = 0 ;                 // usata nell'interrupt per calcolare la velocità del motore DC. Memorizza il passaggio corrente.
long t0_tmp;                            // usata nella giri() per calcolare la velocità del motore DC
int vel ;                               // usata nella giri() per calcolare la velocità del motore DC
//const volatile long TempoMinimo = 7000; // tempo minimo in microsecondi tra due step
long TempoMinimo = 7000;
//volatile long PassoCorrente =0;       // passo corrente dello stepper del carrello 0,1,2,3
int PassoCorrente=0;
boolean IntEnable = false;              // Gli interrupt dell'encoder del mandrino sono abilitati?
int passi = 0;
int passitotali = 0;
boolean CarrelloAutomatico = false;     // il carrello si muove automaticamente con il motore, secondo il passo ed il verso impostati, oppure manualmente secondo l'impostazione del potenziometro.
int pot = 0;
int direzionePot = 0;
int i,k;                                // variabili di iterazione dei cicli for
byte Azione;                            // variabile di appoggio per la gestione delle sub-azioni all'interno della routine di gestione dei comandi provenienti da I2C

void setup() {
  // inizializzo la seriale ed il led per debug.
  //Serial.begin(115200);

  // inizializzo il riferimento per l'ingresso analogico per la misurazione della corrente assorbita dal motore
  analogReference(INTERNAL);
  
  //inizializzo la libreria
  Wire.setWireTimeout(3000, true);
  //imposto l’indirizzo dello slave
  Wire.begin(0x04);

  //eventi per la ricezione del dato
  //e per la richiesta del dato
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);

  //inizializzo le porte dello Stepper
  pinMode(motor_pin_1, OUTPUT);
  pinMode(motor_pin_2, OUTPUT);
  pinMode(motor_pin_3, OUTPUT);
  pinMode(motor_pin_4, OUTPUT);
  digitalWrite(motor_pin_1, LOW);
  digitalWrite(motor_pin_2, LOW);
  digitalWrite(motor_pin_3, LOW);
  digitalWrite(motor_pin_4, LOW);

  // Inizializzo la parte relativa all'encoder motore
  pinMode(encoderPin1, INPUT); 
  digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
  // abilito l'interrupt per la lettura dell'encoder motore del mandrino
  attachInterrupt(digitalPinToInterrupt(encoderPin1), encoderMotore, FALLING );

  // inizializzo in PullUp i due switch di fine corsa
  pinMode(fineDx, INPUT);
  digitalWrite(fineDx,HIGH);
  pinMode(fineSx, INPUT);
  digitalWrite(fineSx,HIGH);

  // inizializzo il pin di comando del motore
  pinMode(PinMotore, OUTPUT);
  setPwmFrequency(PinMotore, 1); // Imposto la frequenza del PWM a 31250Hz, in modo da eliminare il fischio del motore.  
                                 // verificare se è possibile scendere
                                 // A codice consolidato, sostituire la funzione alla sola riga necessaria.
  analogWrite(PinMotore,0);
  //Serial.println("Avvio");
 
  MotoreAcceso = true;
  MotorSpeedImpostata = 500;
// impostazioni di default, necessarie per l'utilizzo del tornio senza il modulo di comando
  
//  CarrelloAutomatico = true;   
//  pot = 3;
//  direzionePot=-1;



}

void loop() {
   // elaboro il movimento del carrello, se automatico
   //EseguiCarrelloAutomatico();

   // eseguo il controllo della velocità del motore ogni 200 millisecondi (5 volte al secondo) per evitare variazioni troppo brusche
    MotorAmpereCorrente = Ampere();
    //  Serial.println(MotorAmpereCorrente);
    if (millis() > CheckPrecedente + 100){
      CheckPrecedente = millis();  
      if (MotoreAcceso){
        MotorSpeedCorrente = Giri();
        Serial.begin(115200);
        Serial.print("MotorSpeedCorrente: ");
        Serial.println(MotorSpeedCorrente);
        Setpoint = MotorSpeedImpostata;

        // verifico gli assorbimenti di corrente
        if (MotorAmpereCorrente > MaxCorrenteMotore){  // il motore assorbe troppo, ne riduco i giri del 35% ad ogni passaggio, quindi azzero dopo 300 mS in caso di blocco meccanico.
            Setpoint = MotorSpeedImpostata * (0,7 - Passaggio/3);
            Setpoint = max(Setpoint, 0); // evito numeri negativi
            Passaggio++;
        }else{
            Setpoint = MotorSpeedImpostata;
            Passaggio = 0;          
        }

        // occorre alla avviolento()
        OutputPrec = Output;  

        // calcolo la differenza tra la velocità corrente e quella voluta, per applicare la dovuta correzione alla potenza del motore
        delta = ((Setpoint - MotorSpeedCorrente)/20);
        Output_tmp=Output + int(delta);
        Output = constrain(Output_tmp,0,255); // mi accerto che l'output sia compreso nell'intervallo ammesso 
        Output = avviolento(); // eseguo un avvio lento per evitare l'impuntamento dell'alimentatore
        if (MotorSpeedCorrente==0){Output=min(Output,50);}  // non permette di arrivare a dare la massima potenza al motore se questo non si muove

// non rimuovere questi print, senza il motore funziona male. (da indagare)
          Serial.print("Output: ");
          Serial.print(int(Output));
          Serial.print(" Output_tmp: ");
          Serial.print(int(Output_tmp));
          Serial.print(" delta: ");
        //  Serial.println(int(delta));
        analogWrite(PinMotore, int(Output));
             Serial.end();
        // verifico se il motore è fermo, ovvero se l'ultimo interrupt sia stato ricevuto oltre 200 mS prima di adesso
        // nel caso, imposto tutto a zero
        noInterrupts(); // disabilito gli interrupt per evitare che ci siano sovrapposizioni con eventuali movimenti manuali del mandrino
        if((micros()-t[0])>200000){
           // azzero tutte le letture precedenti
           for (i=0;i<20;i++){
            t[i] =0;
           }
           // imposto la velocità corrente a zero
           MotorSpeedCorrente =0;
        }
        interrupts();
      }else{
        Output=0;  // necessario anche per effettuare un nuovo riavvio lento.
        analogWrite(PinMotore, Output);  // spengo il motore
        MotorSpeedCorrente = 0;
      }
    }

  //elaboro il dato letto dal bus i2C
    /* 
     il dato è strutturato con un byte di direttiva, seguito da altri byte con i dati.
     In caso di richiesta dati, questa routine prepara la risposta, che verrà inviata quando arriverà la requestEvent()
     */

  if (Codai2C_len>0) {                                      // c'è almeno un byte proveninte da I2C da aggiungere a quelli da lavorare
     noInterrupts();
     for (i=0;i<Codai2C_len;i++){
         Codai2C_tmp[Codai2C_tmp_len+i] = Codai2C[i+1];     // trasferisco i byte ricevuti per elaborarli localmente
     }
     Codai2C_tmp_len = Codai2C_tmp_len + Codai2C_len ;      // aggiorno la lunghezza del buffer dei byte da lavorare
     Codai2C_len = 0;                                       // azzero il buffer di ingresso, ormai integralmente copiato nel buffer di lavoro
     if (Wire.available()){                                 // se ci sono ancora byte in coda I2c da leggere, non letti perchè il buffer era pieno, li leggo adesso e li elaboro al prossimo giro.
        receiveEvent();
      }
     interrupts();
  }

  if (Codai2C_tmp_len>0) {            // c'è almeno un byte da lavorare
     Elem_i2C_wrk=Codai2C_tmp[0];
     // estraggo da primo byte i tre sottocampi
     byte Elem_1 = Elem_i2C_wrk >> 6 ;
     byte Elem_2 = (Elem_i2C_wrk & 0b00110000) >> 4;
     byte Elem_3 = Elem_i2C_wrk & 0b00001111;
    /* Serial.begin(115200);
     Serial.print("Elem_1:");
     Serial.print(Elem_1);
     Serial.print(" - Elem_2:");
     Serial.print(Elem_2);
     Serial.print(" - Elem_3:");
     Serial.println(Elem_3);
     Serial.end();*/
     scoda(1);
     switch (Elem_1 ){       
        case 0:               // Richiesta di invio stato          
        //   MotorSpeedCorrente = Giri();
      //     MotorAmpereCorrente = 700;
      //     PosizioneAttualeCarrello = 543;
           Risposta_ptr=byte(7);                           // Riporto il numero di Byte valorizzati, per un invio corretto
           Risposta[0]=byte(1);                            // rispondo con un codice 1, per verifica
           Risposta[1]=lowByte(MotorSpeedCorrente);        // 8 bit bassi della velocità del motore
           Risposta[2]=highByte(MotorSpeedCorrente);       // 8 bit alti della velocità del motore
           Risposta[3]=lowByte(MotorAmpereCorrente);       // 8 bit bassi della corrente del motore
           Risposta[4]=highByte(MotorAmpereCorrente);      // 8 bit alti della corrente del motore
           Risposta[5]=lowByte(PosizioneAttualeCarrello);  // 8 bit bassi della posizione attuale del carrello
           Risposta[6]=highByte(PosizioneAttualeCarrello); // 8 bit alti della posizione attuale del carrello
  /*         Serial.begin(115200);
           Serial.print(MotorSpeedCorrente);
           Serial.print("-");
           Serial.print(Risposta[0]);
           Serial.print("-");
           Serial.print(Risposta[1]);
           Serial.print("-");
           Serial.println(Risposta[2]);
           Serial.end();*/
           break;
           
        case 1:              // Carrello
       //     Serial.println(F("Comando 1"));
          switch (Elem_2 ){
             case 1: // intera corsa veloce del carrello verso il limite sinistro per impostare la posizione zero. Eseguibile solo con motore fermo.
                if (!MotoreAcceso){                // 
                    while(digitalRead(fineSx)){    // eseguo prima una corsa veloce fino allo stop,
                    FaiUnPasso(-1);
                    }
                   for (i=1;i<20;i++){            // poi torno indietro di 20 passi
                      FaiUnPasso(1);
                   }
                   while(digitalRead(fineSx)){    // e li rieseguo lentamente fino allo stop
                      FaiUnPasso(-1);
                      delay(200);
                   }
                   PosizioneAttualeCarrello = 0;  // in modo da avere una lettura precisa dello zero.
                }
                break;
             case 2: 
                switch(Elem_3){
                  case 1: //Step +1
                     FaiUnPasso(1);
                     break;
                  case 2: //Step -1
                     FaiUnPasso(-1);
                     break;
                  case 3: //Step +5
                     for (i=1;i<5;i++){            
                      FaiUnPasso(1);
                     }
                     break;
                  case 4: //Step -5
                     for (i=1;i<5;i++){            
                      FaiUnPasso(-1);
                     }
                     break;
                  }
                break;
             case 3: 
                break;
             case 4: // intera corsa veloce del carrello verso il limite sinistro per impostare la posizione zero. Eseguibile solo con motore fermo.
                if (!MotoreAcceso){                // 
                    while(digitalRead(fineSx)){    // eseguo prima una corsa veloce fino allo stop,
                    FaiUnPasso(-1);
                    }
                   for (i=1;i<20;i++){            // poi torno indietro di 20 passi
                      FaiUnPasso(1);
                   }
                   while(digitalRead(fineSx)){    // e li rieseguo lentamente fino allo stop
                      FaiUnPasso(-1);
                      delay(200);
                   }
                   PosizioneAttualeCarrello = 0;  // in modo da avere una lettura precisa dello zero.
                }
                break;
             case 5: // carrello manuale, motore stepper spento
                CarrelloAutomatico = false;
                Potenziometro = false;
                StepperOff();
                break;
             case 6: // carrello comandato dal Joystick
                CarrelloAutomatico = false;
                Potenziometro = true;
                break;


 /*
        case 5: // carrello a fine corsa sinistra
        case 6: // carrello a fine corsa destra
        case 7: // imposta posizione corrente come fine corsa sinistro
        case 8: // imposta posizione corrente come fine corsa destro
        case 9: // tbd
        case 10: // imposta direzione e passo del carrello: due byte: direzione e passo
           break;
*/

             default:
    /*            Serial.print(F("Codice Oggetto non valido: "));
                Serial.print("Elem_1: ");
                Serial.print(Elem_1);          
                Serial.print(" - Elem_1: ");
                Serial.println(Elem_2);      */    
                break;     
              }
       
        case 2:               // motore mandrino 
           //Serial.println(F("Comando 2"));
         //  Serial.println(Elem_2);
           switch (Elem_2 ){
             case 1:
                MotoreAcceso = true;
                // imposta velocità motore mandrino, usa due byte dalla coda di ingresso.
                MotorSpeedImpostata = Codai2C_tmp[0] + Codai2C_tmp[1] * 256;
 //               Serial.print(F("MotorSpeedImpostata = "));
 //               Serial.println (MotorSpeedImpostata);
                scoda(2);  // elimino i due byte utilizzati
                break;
             case 2: // motore mandrino off
                MotoreAcceso = false;
          //      Serial.println(F("Motore spento"));
                break;
             default:
           /*     Serial.print(F("Codice Oggetto non valido: "));
                Serial.print("Elem_1: ");
                Serial.print(Elem_1);          
                Serial.print(" - Elem_1: ");
                Serial.println(Elem_2);       */   
                break;     
              }

        case 3:                //impostazioni
         //  Serial.println(F("Comando 3"));
        
        default:
        //   Serial.print(F("Codice Comando non valido: "));
        //   Serial.println(Elem_1);          
           break;     
     } 

 }
}

void scoda(byte numero){  //accorcia la coda di lavoro di "numero" byte, togliendoli dalla testa.
  for (i=0;i<Codai2C_tmp_len-numero;i++){
    Codai2C_tmp[i]=Codai2C_tmp[i+numero];
  }
  Codai2C_tmp_len = Codai2C_tmp_len - numero; 
}
  
int Ampere(){
   return analogRead(ampere_in); // ogni unità rappresenta 100 mA, da tarare con le misure effettive.
}

double avviolento(){  
   // accelera gradualmente per evitare il picco di assorbimento del motore
   return min(Output,(OutputPrec+MotorAccellMax));  
}

void EseguiCarrelloAutomatico(){
    // imposto il modo di funzionamento del carrello in base all'impostazione proveniente dalla tastiera
    // in Automatico segue gli interrupt del mandrino, in manuale segue il potenziometro o lascio il motore in folle per uno spostamento manuale
   if(CarrelloAutomatico){
        carrello();
   }else{
        // spengo il motore, lasciando libero il carrello
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, LOW);
        digitalWrite(motor_pin_3, LOW);
        digitalWrite(motor_pin_4, LOW);
     }  
}

void carrello() {
  pot=0;
  if (Potenziometro){  // serve ancora?
    pot = map(analogRead(pot_in), 0, 1023, +500, -500);
    pot = pot/70;
    direzionePot = (pot > 0) ? 1:-1; // per valori positivi la direzione richiesta è in avanti.
    pot = pot * pot * direzionePot;
  }
  // se il potenziometro è al centro (tra -2 e + 2) non c'è avanzamento. La condizione è indicata dal led
  if (pot > -2 && pot < 2 ) {
    // spengo il motore
    StepperOff();
  }else{
    // il potenziometro definisce quanti millesimi di millimetro per ogni giro del mandrino, variabile tra 0 e 1500
    MM_Per_Giro = (abs(pot)-2)*30;
    MM_Per_Settore = MM_Per_Giro/(NumeroSettori);  // definisco il passo per ogni settore dell'encoder
  }

  if (( micros()- time1)> 10000000){  // timeout motore, lo spegne per non bruciarlo se fermo da oltre 10 secondi.
     StepperOff();
  }
  noInterrupts();
  MM_Temp=MM_DaFare;
  interrupts();
  
  if (abs(MM_Temp)>=LunghezzaPasso){
    int deltapassi=int(MM_Temp/LunghezzaPasso)*direzionePot;
    passi=passi+deltapassi;
    passitotali=passitotali+deltapassi;
    MM_Temp=MM_Temp - abs(deltapassi*LunghezzaPasso);
    }
  if (passi > 0){
    FaiUnPasso(1);
    passi--;
    noInterrupts();
    MM_DaFare=MM_Temp;
    interrupts();  
  }
  if (passi < 0){
    FaiUnPasso(-1);
    passi++;
    noInterrupts();
    MM_DaFare=MM_Temp;
    interrupts();  
  }
}

void encoderMotore(){
   MM_DaFare=MM_DaFare+MM_Per_Settore;
   tic++;
   if (tic>20){
    tic=1;
   }
   t[0]=micros()-t[tic];
   t[tic]=micros();
}

void FaiUnPasso(int p){ // riceve solo +1 o -1 in quanto il numero dei passi rimanenti viene gestito dalla routine chiamante, 
                        // quindi è necessario solo conoscere il passo corrente ed in quale verso occorre fare il prossimo.
  // inibisco il movimento se ho il fine corsa attivo (basso)e sblocco lo Stepper
  if (digitalRead(fineSx)==LOW && p<0){
    p=0;
    StepperOff();
  }
  if (digitalRead(fineDx)==LOW && p>0){
    p=0;
    StepperOff();
  } 
    // definisco quale è la posizione corrente e quindi qual'è quella successiva
   PassoCorrente = PassoCorrente + p;
    if (PassoCorrente >= 4) {
       PassoCorrente-=4;
    }  
    if (PassoCorrente < 0) {
       PassoCorrente+=4;
    }
    long del=time1 - micros() + TempoMinimo;
    if (del>0){
      delayMicroseconds(del); //attendo il tempo minimo dal passo precedente per fare il passo.
    }
    Serial.begin(115200);
    Serial.println(PassoCorrente);
    Serial.end();
    switch (PassoCorrente) {   
        case 0:  // 1010
          digitalWrite(motor_pin_1, HIGH);
          digitalWrite(motor_pin_2, LOW);
          digitalWrite(motor_pin_3, HIGH);
          digitalWrite(motor_pin_4, LOW);
        break;
        case 1:  // 0110
          digitalWrite(motor_pin_1, LOW);
          digitalWrite(motor_pin_2, HIGH);
          digitalWrite(motor_pin_3, HIGH);
          digitalWrite(motor_pin_4, LOW);
        break;
        case 2:  //0101
          digitalWrite(motor_pin_1, LOW);
          digitalWrite(motor_pin_2, HIGH);
          digitalWrite(motor_pin_3, LOW);
          digitalWrite(motor_pin_4, HIGH);
        break;
        case 3:  //1001
          digitalWrite(motor_pin_1, HIGH);
          digitalWrite(motor_pin_2, LOW);
          digitalWrite(motor_pin_3, LOW);
          digitalWrite(motor_pin_4, HIGH);
        break;
      }
      
    time1=micros();
}  

void StepperOff(){
        digitalWrite(motor_pin_1, LOW);
        digitalWrite(motor_pin_2, LOW);
        digitalWrite(motor_pin_3, LOW);
        digitalWrite(motor_pin_4, LOW);
  }
  
int Giri(){
   // prendo il tempo dei 20 passaggi registrati dall'interrupt e lo trasformo in giri al minuto. 
   // Ogni giro del mandrino corrisponde a 80 tic (20 dello stepper per 4 della riduzione), quindi un giro al secondo (60 giri/minuto) prevede un tic ogni 12500 microsecondi
   // che devo moltiplicare per 20 perchè t[0] contiene il tempo sugli ultimi 20 tic, per fare media. Infine moltiplico per 60 per avere i giri al minuto
   //12500*20*60=15.000.000
   // non metto variabili per non rallentare l'esecuzione.
   noInterrupts();
   t0_tmp=t[0];
   interrupts();
   vel=15000000/t0_tmp; 
   return max(vel,0); // evito eventuali numeri negativi
  }  
 
void setPwmFrequency(int pin, int divisor) {
 byte mode;
 if(pin == 5 || pin == 6 || pin == 9 || pin == 10) {
   switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 64: mode = 0x03; break;
      case 256: mode = 0x04; break;
      case 1024: mode = 0x05; break;
     default: return;
    }
    if(pin == 5 || pin == 6) {
      TCCR0B = TCCR0B & 0b11111000 | mode;
    } else {
      TCCR1B = TCCR1B & 0b11111000 | mode;
    }
  } else if(pin == 3 || pin == 11) {
    switch(divisor) {
     case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 32: mode = 0x03; break;
      case 64: mode = 0x04; break;
      case 128: mode = 0x05; break;
      case 256: mode = 0x06; break;
      case 1024: mode = 0x7; break;
     default: return;
    }
    TCCR2B = TCCR2B & 0b11111000 | mode;
  }
}

// queste sono le funzioni che ricevono e trasmettono i dati I2C

void receiveEvent() //questo evento viene generato quando sul bus è presente un dato da leggere
{
  while(Wire.available()){
    if (Codai2C_len < 31){ // leggo il dato solo se ho spazio nell'array, altrimenti attendo l'esecuzione manuale della routine, invocata dalla loop()
       Codai2C_len++;
       Codai2C[Codai2C_len] = Wire.read();
    }
  }
}

void requestEvent(int qty)
{
//questo evento viene generato quando il master
//richiede ad uno specifico slave
//una richiesta di dati

//spedisco il dato al Master
     Wire.write(Risposta,Risposta_ptr);
}

Se al posto dei Serial.print metti delay (20); che succede?...

Comunque, a parte che il casting si fa con
Serial.print ((int) Output);

anziché con
Serial.print (int(Output));

non capisco perché tu non faccia semplicemente
Serial.print (Output);
con Output che mi sembra essere decisamente intero...

analogWrite accetta una variabile a 8 bit, da 0 a 255, non una int a 16 bit!

int deltapassi = int (MM_Temp/LunghezzaPasso) * direzionePot;
int (MM_Temp/LunghezzaPasso)
???... deltapassi viene comunque intero (e non approssimato!).

Grazie. Oggi farò la prova con il delay(), anche se in ogni caso non capisco come possa un ritardo nell'esecuzione del codice modificarne il comportamento.
Per le altre osservazioni, questi errori sono il retaggio di versioni precedenti di questo programma e che devo ancora ottimizzare.
Per quanto riguarda l'analogWrite, il valore della variabile Output viene comunque limitato al range ammesso, infatti poco prima puoi trovare l'istruzione Output = constrain(Output_tmp,0,255), quindi la conversione da Int a Byte intrinseca nell'analogWrite non dovrebbe generare problemi. Peraltro la stessa documentazione della funzione ammette l'utilizzo di una variabile int come parametro analogWrite() - Arduino Reference

Grazie comunque delle tue osservazioni.

Carlo

Niente da fare.
ho provato con il delay(), sia a 20 che a 100 o 500, senza esito.
Ho anche sistemato la questione dei "int", ma anche questo non ha dato esito.
Ho ristretto il campo alle Serial.print necessarie e sono (anche senza casting)

          Serial.print(Output);
          Serial.print(Output_tmp);
        analogWrite(PinMotore, Output);

Ho anche provato a sostituire le Serial.print con altri utilizzi delle due variabili (le ho usate per valorizzare una variabile nuova, definita solo per questo scopo) ma ancora senza esito

// non rimuovere questi print, senza il motore funziona male. (da indagare)
       //   Serial.print(Output);
       //   Serial.print(Output_tmp);
       int tmp=Output;
       tmp = Output_tmp;
        analogWrite(PinMotore, Output);

p.s. l'altra parte che mi hai evidenziato (quella del deltapassi) è destinata ad essere stravolta, visto che quella funzionalità l'ho rimossa ed andrà sostituita da altro. cmq farò tesoro delle tue osservazioni anche in quella sede.

Grazie

Carlo

UPPO!
Possibile che nessuno abbia idea del problema?
C'è qualche guru a cui passare il codice oggetto per vedere cosa combina il compilatore?
Grazie

Carlo

Chissà come va a interagire il Serial.print con l'analogWrite...

... in NESSUN modo !

Guglielmo

Peraltro è un problema che mi porto dietro da molto tempo su questo progetto. Inizialmente avevo pensato che gli errori dipendessero da overflow in alcune operazioni matematiche ed allora ho portato tutte le variabili a long, per poi fare la conversione finale solo al momento di effettuare la analogWrite (è per questo motivo che nel codice ci sono molti casting che non sono necessari. Dopo i test ho riportato le variabili a int o byte ma sono rimasti dei casting sparsi per il codice, adesso sto pulendo tutto). Rimane però sempre aperta la domanda: come mai sbaglia e, soprattutto, come mai l'errore sparisce quando faccio la serial.print? Stavo anche pensando di fare la decompilazione del codice oggetto nei due casi, per vedere se è il compilatore che fa qualche casino, ma non l'ho mai fatta e non saprei da dove cominciare.

Ma ti pare che il compilatore sbaglia?
E roba collaudata

Fai delle prove con un codice ridotto per isolare il problema.

Il massimo che si può fare è tradurre il .hex e il .elf in codice assembly. Purtroppo arduino ide usa delle flag che riducono la dimensione del firmware (Os, -flto, ecc) ed difficilissimo seguire
il flusso, anche se fossi un guru più guru dei guru ne ricaveresti poche informazioni e anche incerte.

Io invece ho una curiosità perché la Serial.begin() non sta nel setup come di solito? Un buon motivo potrebbe essere che durante il loop cambi velocità di trasmissione ma non lo fai, quindi perché lo fai? :thinking: :thinking:

Messe li le due serial print impegnano minimamente la cpu e lo fanno solo per scrivere nel _tx_buffer della seriale. Certamente poi per spedirle realmente interviene l'interrupt hardware della seriale ma anche questo influisce solo posticipando di poco il momento in cui usi anagloWrite. Ma dici che anche sostituendo con delay il problema rimane.

Poi ci sono dei delay(200) sparsi qui e la, questi non influenzano il controllo della velocità?

Ma a questo punto io proverei il solo motore DC e il controllo
di avvio e mantenimento, riscrivendo tutto al minimo.

Comunque se vuoi ottenere il file asm del tuo programma puoi usare avr-objdump ma per lavorare con il .elf che contiene simboli di debug.

Ciao.

Hai ragione, mi sono espresso male. Intendevo dire che il compilatore fa qualcosa di diverso da quello che mi aspetto....

Si, il Serial.begin() è nel punto sbagliato. E' che alle volte quando scasini tutto per fare un debug su un problema assurdo, finisci per aggiungere casino al casino. In ogni modo non dovrebbe dare problemi e non è lui a modificare il comportamento del codice.
Per quanto riguarda i delay sparsi, servono volutamente per rallentare l'esecuzione del codice, in quanto deve adattarsi alla velocità con cui risponde la meccanica (che ha una discreta inerzia, trattandosi di un motore elettrico che aziona un mandrino di un tornio). Senza ritardi, le reazioni del codice sarebbero troppo veloci e sproporzionate agli eventi che le hanno scatenate ed avrei delle oscillazioni.
Ora sto rivendendo tutto lo spezzone di codice, eliminando tutte le nidificazioni dei calcoli e rivedendo bene la tipologia delle variabili. Non sarebbe la prima volta che una nidificazione produce risultati diversi da quelli che mi aspetto. Chissà che non avvenga il miracolo, ma se non accade proverò ad estrarre solo le parti essenziali a questa funzione e le proverò isolate.
Grazie

Io mi concentrerei solo sul controllo di velocità così da snellire il codice e spezzarlo in tot funzioni. C'è una classe arduino per il PID (proporzionale integrativo derivativo) potrebbe essere un ottimo punto di partenza. Ricordo che c'è chi ha permesso tramite seriale di modificare i coefficienti P, I, D con l'algoritmo in corsa. Ciao.

Grazie.
la PID l'ho provata, ma non andava bene. E' passato molto tempo e non ricordo quali problemi avessi riscontrato, cmq feci prima a scriverla ex-novo. Il mio problema comunque non è la logica di controllo della velocità, quella è abbastanza banale, ma di capire come mai alcune variabili assumono valori apparentemente astrusi. Ad esempio, in una nuova versione del codice (ho portato all'interno della Loop alcune variabili, definendole Static, per farle allocare diversamente dal compilatore ed evitare commistioni con altre variabili), una variabile viene regolarmente corrotta ogni tot (diciamo 30-40) cicli di elaborazione, cicli ovviamente tutti uguali. Sembrerebbe come se qualche contatore vada in overflow e corrompa la variabile che ha dopo in memoria, ma non ho contatori nel ciclo....
Cmq su questo punto sto ancora facendo analisi, vi terrò aggiornati.

Carlo

Capitato anche a me e non ricordo i dettagli.

Tutte le volte che mi è capitata una cosa simile ho puntato il dito sulla cattiva gestione degli array e quasi sempre ci ho preso. La troppa confidenza con i puntatori porta ad errori e poi scovarli è snervante.

Altra cosa che aiuta a scovare gli errori e a non introdurne è l'ordine e la suddivisione.

Prova a replicare il comportamento anomalo in uno sketch minimale così da poterlo guardare a quattro occhi.

Ciao.

La variabile i è globale e mantiene il suo valore.
Io non userei mai una variabile globale di nome così breve all'interno di un ciclo. i, j, k ecc sono sempre locali e mai static. Ancora peggio se si tratta di una variabile indice di array. I guai arrivano a gratis ma perché andarsela a cercare?

Ciao.

Si, la i l'ho usata in modo maldestro, ma non era quello che influenzava il codice...
Alla fine ho trovato l'elemento che influiva negativamente sulle variabili... la routine che gestisce l'interrupt della ruota ottica, quella che legge il numero di giri del motore, alterava delle variabili non di sua competenza. Adesso l'ho ridisegnata completamente e non da più problemi.

Grazie a tutti per la collaborazione.

Carlo

Si ho visto che quella variabile prima la azzeri e quindi va bene, ma è ancora più difficile andare a caccia di bug quando devi stare attento alla i, j, k ecc.

Nei cicli uso (quasi) sempre variabili locali, cioè:

for (uint8_t i=0;i<Codai2C_tmp_len-numero;i++) {
    // la variabile i ha ambito di visibilità locale 
    Codai2C_tmp[i]=Codai2C_tmp[i+numero];
}

Ottimo, adesso oltre ad aggiungere funzionalità facciamo attenzione quanto più possibile a non aggiungere bug. :wink:
Ciao.

La cosa curiosa è che la routine di interrupt non citava esplicitamente altre variabili, quindi in teoria non avrebbe dovuto fare casini, resta però il fatto che nel momento in cui l'ho disattivata i problemi sono scomparsi. L'ho quindi riscritta ex-novo, peraltro ottimizzandola, ed ora funziona tutto alla perfezione.
Questa è la precedente

void encoderMotore(){
   MM_DaFare=MM_DaFare+MM_Per_Settore;
   tic++;
   if (tic>20){
    tic=1;
   }
   t[0]=micros()-t[tic];
   t[tic]=micros();
}

Questa è quella nuova.

void encoderMotore(){  
   long TempoEncoder = micros();
   MM_DaFare=MM_DaFare+MM_Per_Settore;
   tempo_tic0=TempoEncoder-tempi_tic[Indicetic];  
   tempi_tic[Indicetic]=TempoEncoder;
   MotorSpeedMisurata=int(15000000/tempo_tic0);
   Indicetic++;
   if (Indicetic>20){
      Indicetic=0;
   }
}

La differenza è che la prima scriveva i dati in un array e poi, dalla giri(), leggevo il valore che mi interessava, adesso ho abolito la giri() ed il calcolo della velocità lo faccio direttamente all'interno dell'interrupt, valorizzando le variabili globali MotorSpeedMisurata e tempo_tic0, che poi leggo nella routine di gestione del motore (la MM_DaFare era già gestita in questo modo già nella versione precedente)
n.b. la variabile Indicetic mi occorre in lettura anche in altre parti del codice, per questo è globale

Un ulteriore miglioramento della stabilità in altre parti del codice l'ho ottenuto utilizzando la macro ATOMIC invece del semplice NoInterrupts() e interrupts()

Rimarrebbe da capire perché la Serial.print risolveva il problema, ma credo che questo rimarrà un mistero.

Carlo