Comportamento Eprom differente fra UNO e NANO EVERY ?

Buonasera a tutti, ho una domanda per voi.
Ho scritto un semplice programma che scrive la posizione di 4 servo motori servo nella eprom di arduino ogni qualvolta il motore si muove.
la posizione viene letta dalla eprom nella procedura di startup() e viene scritta nella porta pwm prima della funzione attach().
questo per evitare spostamenti di default dei servomotori al riavvio di arduino.
il problema è che su arduino UNO funziona correttamente, invece su arduino NANO EVERY no, ovvero le letture della eprom sono (a volte) errate.

#include <Servo.h>
#include <EEPROM.h>



const int servo01_MinDegrees  = 1000;  // motore 1 : piattaforma
const int servo01_MidDegrees  = 1200;
const int servo01_MaxDegrees  = 1400;
const int servo01_Interval    = 40; 
int servo01_Degrees           = 1;
int servo01_Position          = servo01_MidDegrees;
unsigned long servo01_previousMillis = 0;

const int servo02_MinDegrees  = 1000;  // motore 2 : bancone
const int servo02_MidDegrees  = 1200;
const int servo02_MaxDegrees  = 1400;
const int servo02_Interval    = 30;  
int servo02_Degrees           = 1;
int servo02_Position          = servo02_MidDegrees;
unsigned long servo02_previousMillis = 0;

const int servo03_MinDegrees  = 1000;  // motore 1 : bicchieri
const int servo03_MidDegrees  = 1200;
const int servo03_MaxDegrees  = 1400;
const int servo03_Interval    = 20;  
int servo03_Degrees           = 1;
int servo03_Position          = servo03_MidDegrees;
unsigned long servo03_previousMillis = 0;

const int servo04_MinDegrees  = 1000;  // motore 1 : gioconda
const int servo04_MidDegrees  = 1200;
const int servo04_MaxDegrees  = 1400;
const int servo04_Interval    = 10;  
int servo04_Degrees           = 1;
int servo04_Position          = servo04_MidDegrees;
unsigned long servo04_previousMillis = 0;



Servo servo01;  // create servo object to control a servo
Servo servo02;  // create servo object to control a servo
Servo servo03;  // create servo object to control a servo
Servo servo04;  // create servo object to control a servo



unsigned long currentMillis = 0;  // stores the value of millis() in each iteration of loop()

int eprom01 = servo01_MidDegrees;
int eprom02 = servo02_MidDegrees;
int eprom03 = servo03_MidDegrees;
int eprom04 = servo04_MidDegrees;

void setup() {

  Serial.begin(9600);

  delay(100);

  
  eprom01 = (EEPROM.read(1) * 10) + EEPROM.read(11);
  eprom02 = (EEPROM.read(2) * 10) + EEPROM.read(12);
  eprom03 = (EEPROM.read(3) * 10) + EEPROM.read(13);
  eprom04 = (EEPROM.read(4) * 10) + EEPROM.read(14);

  if ((eprom01 > servo01_MinDegrees) && (eprom01 < servo01_MaxDegrees)) {
    servo01_Position = eprom01;
  } else {
    servo01_Position = servo01_MidDegrees;
  }

  if ((eprom02 > servo02_MinDegrees) && (eprom02 < servo02_MaxDegrees)) {
    servo02_Position = eprom02;
  } else {
    servo02_Position = servo02_MidDegrees;
  }

  if ((eprom03 > servo03_MinDegrees) && (eprom03 < servo03_MaxDegrees)) {
    servo03_Position = eprom03;
  } else {
    servo03_Position = servo03_MidDegrees;
  }

  if ((eprom04 > servo04_MinDegrees) && (eprom04 < servo04_MaxDegrees)) {
    servo04_Position = eprom04;
  } else {
    servo04_Position = servo04_MidDegrees;
  }

  Serial.println("*** Starting BlaBlaBar ***");  // so we know what sketch is running

  Serial.print("servo 01:  ");
  Serial.print(eprom01);
  Serial.print(" - ");
  Serial.println(servo01_Position);

  Serial.print("servo 02:  ");
  Serial.print(eprom02);
  Serial.print(" - ");
  Serial.println(servo02_Position);

  Serial.print("servo 03:  ");
  Serial.print(eprom03);
  Serial.print(" - ");
  Serial.println(servo03_Position);

  Serial.print("servo 04:  ");
  Serial.print(eprom04);
  Serial.print(" - ");
  Serial.println(servo04_Position);



  servo01.writeMicroseconds(servo01_Position);              // sets the initial position
  servo02.writeMicroseconds(servo02_Position);              // sets the initial position
  servo03.writeMicroseconds(servo03_Position);              // sets the initial position
  servo04.writeMicroseconds(servo04_Position);              // sets the initial position

  delay(100);

  servo01.attach(3);
  servo02.attach(6);
  servo03.attach(9);
  servo04.attach(10);
}

void loop() {

  currentMillis = millis();

  servo01_Sweep();
  servo02_Sweep();
  servo03_Sweep();
  servo04_Sweep();
}


void servo01_Sweep() {
  if (currentMillis - servo01_previousMillis >= servo01_Interval) {
    servo01_previousMillis += servo01_Interval;

    servo01_Position = servo01_Position + servo01_Degrees;
    if ((servo01_Position >= servo01_MaxDegrees) || (servo01_Position <= servo01_MinDegrees)) {
      servo01_Degrees = -servo01_Degrees;
      servo01_Position = servo01_Position + servo01_Degrees;
    }
    servo01.writeMicroseconds(servo01_Position);
    
    EEPROM.write(1, servo01_Position / 10);
    EEPROM.write(11, servo01_Position % 10);
  }
}

void servo02_Sweep() {
  if (currentMillis - servo02_previousMillis >= servo02_Interval) {
    servo02_previousMillis += servo02_Interval;

    servo02_Position = servo02_Position + servo02_Degrees;
    if ((servo02_Position >= servo02_MaxDegrees) || (servo02_Position <= servo02_MinDegrees)) {
      servo02_Degrees = -servo02_Degrees;
      servo02_Position = servo02_Position + servo02_Degrees;
    }
    servo02.writeMicroseconds(servo02_Position);
    EEPROM.write(2, servo02_Position / 10);
    EEPROM.write(12, servo02_Position % 10);
  }
}

void servo03_Sweep() {
  if (currentMillis - servo03_previousMillis >= servo03_Interval) {
    servo03_previousMillis += servo03_Interval;

    servo03_Position = servo03_Position + servo03_Degrees;
    if ((servo03_Position >= servo03_MaxDegrees) || (servo03_Position <= servo03_MinDegrees)) {
      servo03_Degrees = -servo03_Degrees;
      servo03_Position = servo03_Position + servo03_Degrees;
    }
    servo03.writeMicroseconds(servo03_Position);
    EEPROM.write(3, servo03_Position / 10);
    EEPROM.write(13, servo03_Position % 10);
  }
}

void servo04_Sweep() {
  if (currentMillis - servo04_previousMillis >= servo04_Interval) {
    servo04_previousMillis += servo04_Interval;

    servo04_Position = servo04_Position + servo04_Degrees;
    if ((servo04_Position >= servo04_MaxDegrees) || (servo04_Position <= servo04_MinDegrees)) {
      servo04_Degrees = -servo04_Degrees;
      servo04_Position = servo04_Position + servo04_Degrees;
    }
    servo04.writeMicroseconds(servo04_Position);
    EEPROM.write(4, servo04_Position / 10);
    EEPROM.write(14, servo04_Position % 10);
  }
}

Perdona ma NON è il modo corretto di salvare/recuperare degli interi nella EEPROM ...
... esistono due bellissime funzioni put() e get() che fanno tutto loro in funzione del tipo dato che gli si passa ... perché creare cose che in alcuni casi funzionano ed in altri no???

Rimpiazza quei modi di salvare e recuperare i dati nella EEPROM con le migliori funzioni EEPROM put() ed EEPROM.get() semplicemente ricordando che su AVR un 'int' occupa 2 bytes e quindi incrementando ogni volta l'indirizzo di due.

Guglielmo

1 Like

Oltre al consiglio di Guglielmo, volevo darti uno spunto diciamo "stilistico".

Quando si ha a che fare con svariate variabili che riguardano più "oggetti" simili (ossia con una stessa struttura), meglio evitare di fare sfilze di variabili "servo01_*", "servo02_*" eccetera, così come le "eprom*" e altre. Imparare ad usare array e classi è la cosa non solo stilisticamente migliore, ma anche molto più compatta, gestibile, e leggibile.

Ti faccio un esempio "riassemblando" diversamente qualche parte del tuo codice, del quale ovviamente non entro in dettaglio relativo a ciò che dovrebbe fare, visto che oltretutto mi sembra che sia solo una porzione di codice. Ed ovviamente non l'ho provato, prendilo con le molle ossia devi verificare tu che inserito nel tuo codice funzioni correttamente e faccia ciò che dovrebbe fare lo sketch
Quindi ad esempio per iniziare, al posto di quelle definizioni potresti creare una classe:

class SrvData {
  public:
    int MinDegrees = 1000;
    int MidDegrees = 1200;
    int MaxDegrees = 1400;
    int Interval = 10; 
    int Degrees = 1;
    int Position = 1200;
    unsigned long previousMillis = 0;
    byte Pin = 3;
    Servo servo;
    int Address = 0; // EEPROM address
    SrvData(byte sPin, int sMinDegrees, int sMidDegrees, int sMaxDegrees, int sInterval, int sDegrees, int sPosition, int sAddress)
    {
      Pin = sPin;
      MinDegrees = sMinDegrees;
      MidDegrees = sMidDegrees;
      MaxDegrees = sMaxDegrees;
      Interval = sInterval;
      Degrees = sDegrees;
      Position = sPosition;
      Address = sAddress;
      servo.attach(Pin);
    }

    void ReadPosition() {
      int eprom; // Valore letto dall'EEPROM
      EEPROM.get(Address, eprom);
      if ((eprom > MinDegrees) && (eprom < MaxDegrees))
        Position = eprom;
      else
        Position = MidDegrees;
      servo.writeMicroseconds(Position);
    }

    void Sweep() {
      if (millis() - previousMillis >= Interval) {
        previousMillis += Interval;
        Position = Position + Degrees;
        if ((Position >= MaxDegrees) || (Position <= MinDegrees)) {
          Degrees = -Degrees;
          Position = Position + Degrees;
        }
        servo.writeMicroseconds(Position);
        EEPROM.put(Address, Position);
      }
    }
};

Come vedi, ho creato una classe che è "incaricata" di gestire tutte le cose relative ad un servo. Con questa ne ho fatto un array di 4 elementi, ed a quel punto l'inizializzazione dei valori diventa molto più compatta:

SrvData srvData[4] = {
  {3,1000,1200,1400,40,1,1200,0},
  {6,1000,1200,1400,30,1,1200,2},
  {9,1000,1200,1400,20,1,1200,4},
  {10,1000,1200,1400,10,1,1200,6}
};

Ma anche il setup diventa semplicemente:

  Serial.println("*** Starting BlaBlaBar ***");  // so we know what sketch is running
  for(int s=0; s<4; ++s) {
    srvData[s].ReadPosition();
    Serial.print("servo ");Serial.print(s+1);Serial.print(": ");
    Serial.println(srvData[s].Position);
  }

Come vedi, in un unico ciclo ho gestito TUTTO quello che serve per inizializzare l'"oggetto" SrvData.
E da quel momento puoi buttare le funzioni "servo*_Sweep" visto che è già nella classe SrvData quindi il loop diventa:

void loop() {
  for(int s=0; s<4; ++s) 
    srvData[s].Sweep();
}

PS: comunque scrivere continuamente nella EEPROM non è il massimo, perché le EEPROM non hanno una durata infinita ma un certo numero di scritture prima di iniziare a "dare i numeri"....
PS2: sinceramente non ho ben capito lo scopo di questo codice che muove i servo, e mettendo il codice su Wokwi questi si muovono in modo "isterico"... :wink:

1 Like

Grazie mille Guglielmo per la tua risposta. :+1:
Questo weekend provo a sostituire le funzioni write() e read() con le funzioni get() e put() e sono sicuro che risolveranno il mio problema.

Grazie mille Docdoc.

praticamente mi hai riscritto tutto il programma :blush: grazie grazie!!!

devo essere sincero, questo è il primo programma "serio" con arduino che scrivo (dopo aver giocato un po con lo starter kit). non ho usato classi e array perchè come primo obiettivo avevo quello di far funzionare il mio progetto.

in pratica, con alcuni amici abbiamo realizzato un carro del carnevale di Viareggio in miniatuta (scala 1 a 20) e i 4 servo motori moviventano le maschere sul carro.

purtroppo ho veramente poco tempo da dedicare al software ma sicuramente seguirò i tuoi consigli.

scrivere continuamente sulla eprom è l'unico modo che conosco per memorizzare la posizione dei servo motori nel caso di spegnimento improvviso del sistema.
devo memorizzare questi volori e leggeri duranti la procedura di startup e scriverli nelle porte pwm dei servo prima di richiamare la funzione attach(). Questo per evitare che i servo vadano nella posizione di default (90 gradi) al riavvio del sistema con bruschi spostamenti.

cmq grazie docdoc! ti terrò aggiornato

Scrivere continuamente sulla EEPROM ... la distrugge e poi cessa di funzionare correttamente.

Considera che ogni cella di memoria è garantita per un massimo di 100'000 scritture, che sembrano tante, ma, già se ne fai solo 1 al secondo, la distruggi in poco più di 27 ore.

Devi trovare un'altra soluzione ... :roll_eyes:

Guglielmo

In tal caso ti basta inserire nel costruttore la chiamata a "ReadPosition()" prima di "attach".
Ma a quel punto non serve più neanche specificare "Position" nel costruttore, visto che in ReadPosition la imposti (o da Eeprom o da MidDegrees), che diventerebbe quindi:

    SrvData(byte sPin, int sMinDegrees, int sMidDegrees, int sMaxDegrees, int sInterval, int sDegrees, int sAddress)
    {
      Pin = sPin;
      MinDegrees = sMinDegrees;
      MidDegrees = sMidDegrees;
      MaxDegrees = sMaxDegrees;
      Interval = sInterval;
      Degrees = sDegrees;
      Address = sAddress;
      ReadPosition();
      servo.attach(Pin);
    }

Devi togliere quindi anche quel parametro dall'inizializzazione:

SrvData srvData[4] = {
  {3,1000,1200,1400,40,1,0},
  {6,1000,1200,1400,30,1,2},
  {9,1000,1200,1400,20,1,4},
  {10,1000,1200,1400,10,1,6}
};

Se poi gli altri parametri sono sempre uguali, visto che sono preimpostati nella classe, puoi anche cambiare il costruttore per impostare solo quelli necessari e differenti tra un servo e l'altro, ad esempio:

...
    SrvData(byte sPin, int sInterval, int sAddress)
    {
      Pin = sPin;
      Interval = sInterval;
      Address = sAddress;
      ReadPosition();
      servo.attach(Pin);
    }
...
SrvData srvData[4] = {
  {3,40,0},
  {6,30,2},
  {9,20,4},
  {10,10,6}
};
...

Resta comunque il problema del numero di scritture in EEPROM, devi evitare di scrivere continuamente l'ultimo valore, oppure devi fare altro, ad esempio scrivere su una scheda SD...

1 Like

mmmm ... non cambia molto ... non è che abbiano vita maggiore ... conviene piuttosto una FRAM esterna o una NVSRAM (... su queste ultime, c'è anche un mio articolo su Elettronica In 252, Marzo 2021 :wink:) che hanno una vita praticamente illimitata (sono equivalenti a delle RAM).

Guglielmo

1 Like

Riporto da internet:

An SD card’s lifespan has everything to do with how frequently it is used. According to HowStuffWorks, memory cells in micro SD cards can undergo up to 10,000 write-and-erase cycles before wearing out.

Ovviamente, come per le EEPROM, parliamo di riscritture sulle stesse celle/blocchi di SD.

Esistono tecniche per utilizzare a rotazione le varie celle della EEPROM o i vari settori della SD aumentando quindi proporzionalmente la vita delle due cose.

Guglielmo

Ok per la EEPROM, per le SD non puoi essere te a decidere dove scrivere (anche se ti da l'impressione di farlo), per cui puoi solo sperare che lo faccia per conto suo.

Ciao, Ale.

Certo, però ... puoi usare una tecnica in cui scrivi, in append, i dati in un file che quindi, piano piano, userà sezioni sempre diverse della SD.

Guglielmo

Si ma la scheda SD puoi non solo estrarla e farne una copia ma anche buttarla e sostituirla con un'altra se non funziona, per la EEPROM devi buttare tutto Arduino... :wink:

:grin: :grin: :grin: ... pure questo è vero !

Guglielmo