Contametri con arduino nano

Ciao a tutti, sono alle prese con un puiccolo grande progetto. Un contametri montato su una bobina che mi calcola i metri di filo e si aggiorna quando lo spessore del filo si sovrappone e sfalsa il conteggio.
Su d4 ho inserito un pulsante che salva i metri se premuto per 3 secondi. se premuto per 4 resetta i metri e se premuto una volta cambia il diametro del filo.
su d4 ho il pulsante, su d3 e d2 i sensori di prossimita' che funionano coin la calamita.
di sefguito il codice che ho usato.
Cosa sbaglio?

#include <U8g2lib.h>
#include <Wire.h>

// --- DISPLAY OLED
U8G2_SH1106_128X64_NONAME_F_HW_I2C display(U8G2_R0, U8X8_PIN_NONE);

// --- PIN
const int pulsantePin = 4;
const int sensore1Pin = 3;
const int sensore2Pin = 2;

// --- FILI SELEZIONABILI
float fili[] = {0.10,0.20,0.30,0.40,0.50,0.60,0.70,0.80,0.90,100.0,110.0,120.0,130.0,140.0};
int filoCorrente = 0;

// --- VARIABILI
volatile long contatore1 = 0;
volatile long contatore2 = 0;
volatile unsigned long lastImpulse1 = 0;
volatile unsigned long lastImpulse2 = 0;
const unsigned long debounceTime = 10;  // tempo in millisecondi

// --- METRI
float metriTotali = 0;
float metriVisuali = 0;

// --- DIAMETRI MANUALI
float diametri[] = {0.10,0.20,0.30,0.40,0.50,0.60,0.70,0.80,0.90,100.0,110.0,120.0,130.0,140.0}; 
int diametroCorrenteIndex = 0;

float larghezzaBobina = 128.0;

// --- SMOOTHING
const float metriVelocita = 0.5;

// --- PULSANTE LUNGA
unsigned long pressStart = 0;
bool buttonLongPressed = false;
bool saved = false;

void setup() {
  pinMode(pulsantePin, INPUT_PULLUP);
  pinMode(sensore1Pin, INPUT_PULLUP);
  pinMode(sensore2Pin, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(sensore1Pin), conta1, RISING);
  attachInterrupt(digitalPinToInterrupt(sensore2Pin), conta2, RISING);;

  display.begin();
  display.clearBuffer();
  Serial.begin(9600);
}

void loop() {
  gestisciPulsante();
  aggiornaMetri();
  aggiornaDisplay();
  delay(50);
}

void gestisciPulsante() {
  int statoPulsante = digitalRead(pulsantePin);

  if (statoPulsante == LOW) {
    // Pulsante premuto
    if (!buttonLongPressed) {
      pressStart = millis();
      buttonLongPressed = true;
    }
  } else {
    // Pulsante rilasciato
    if (buttonLongPressed) {
      unsigned long durata = millis() - pressStart;

      // Breve pressione (<2s) → cambia diametro
      if (durata < 2000) {
        diametroCorrenteIndex++;
        if(diametroCorrenteIndex >= (sizeof(diametri)/sizeof(diametri[0])))
          diametroCorrenteIndex = 0;

        // Reset totale e contatori prima di iniziare
        metriTotali = 0;
        metriVisuali = 0;
        contatore1 = 0;
        contatore2 = 0;
      }

      // Pressione 2-4s → salva
      else if (durata >= 2000 && durata < 4000 && !saved) {
        salvaMetri();
      }

      // Pressione ≥4s → reset se salvato
      else if (durata >= 4000 && saved) {
        resetMetri();
      }

      buttonLongPressed = false;
    }
  }
}
// --- CALCOLO METRI
void aggiornaMetri() {
  long giriTotali = contatore1 + contatore2;
  contatore1 = 0;
  contatore2 = 0;

  float spessore = fili[filoCorrente];

  float diametroCorrente = diametri[diametroCorrenteIndex];
  float metriPerGiro = 3.1416 * (diametroCorrente / 10.0);

  metriTotali += giriTotali * metriPerGiro;

  if(metriTotali < 0) metriTotali = 0;

  // smoothing
  if(metriVisuali < metriTotali) metriVisuali += metriVelocita;
  if(metriVisuali > metriTotali) metriVisuali -= metriVelocita;
}

// --- DISPLAY
void aggiornaDisplay() {
  display.clearBuffer();

  int shiftX = 5;

  // --- METRI CENTRALI
  display.setFont(u8g2_font_fub35_tr);
  char bufferMetri[10];
  dtostrf(metriVisuali,4,1,bufferMetri);
  int w = display.getStrWidth(bufferMetri);
  display.drawStr((128 - w)/2 + shiftX, 40, bufferMetri);

  // --- DIAMETRO IN BASSO CENTRATO
  float diametroCorrente = diametri[diametroCorrenteIndex];
  display.setFont(u8g2_font_ncenB08_tr);

  char bufferD[6];
  dtostrf(diametroCorrente,4,1,bufferD);

  int wD = display.getStrWidth(bufferD);
  display.drawStr((128 - wD)/2, 62, bufferD);

  display.sendBuffer();
}

// --- INTERRUPT
void conta1() {
  unsigned long now = millis();
  if (now - lastImpulse1 > debounceTime) {
    contatore1++;
    lastImpulse1 = now;
  }
}

void conta2() {
  unsigned long now = millis();
  if (now - lastImpulse2 > debounceTime) {
    contatore2--;
    lastImpulse2 = now;
  }
}

// --- SALVA / RESET
void salvaMetri() {
  saved = true;
  Serial.print("Salvato: ");
  Serial.println(metriTotali);
  // TODO: aggiungi qui SD/EEPROM
}

void resetMetri() {
  metriTotali = 0;
  metriVisuali = 0;
  contatore1 = 0;
  contatore2 = 0;
  saved = false;
  Serial.println("Reset effettuato!");
}

:warning:
Ti segnalo che, nella sezione in lingua Inglese, si può scrivere SOLO in Inglese ... quindi, per favore, la prossima volta presta più attenzione in quale sezione metti i tuoi post; questa volta esso è stato spostato, da un moderatore della sezione di lingua Inglese, nella sezione di lingua Italiana ... la prossima volta potrebbe venire direttamente eliminato. Grazie.

... magari se ci dici sia di che Arduino parliamo, che quale è il problema che riscontri con il codice, ci aiuti ad aiutarti :roll_eyes:

Guglielmo

quando lo provo non funziona
forze devo gestire meglio la distanza delle calamite... vi aggiorno

... NON hai riposto alle mie domande :roll_eyes:

Guglielmo

Oltre alla domanda di Guglielmo aggiungo che il programma mi sembra non chiaro

Ti proporrei di commentarlo, così ci chiariamo (e ti chiarisci) la logica di funzionamento

nel titolo ho scritto che è un Arduino Nano…. basta leggere.
per quanto riguarda il problema, si impalla quando legge le calamite .
mi è venuto il dubbio che siano i sensori quando faccio le prove in bianco.
appena lo monto e le distanze sono giuste ci aggiorno se il problema si ripresenta e li possiamo controllare se per caso il codice ha qualche intoppo nella lettura.

Certo ... peccato che Arduino Nano è una FAMIGLIA e ne esistono un'infinità di versioni ... basta andare nello store :smiling_face_with_horns:

Comunque, se diamo per scontato che è un Arduino Nano R3, quindi basato su AVR ...

... fai attenzione all'utilizzo della millis() che, all'interno delle ISR, NON avanza ed è ferma. Vero che tu leggi solo un valore statico che non avanza dentro la ISR, ma è bene saperlo.

In ogni caso è comunque sbagliato il concetto ...
... i pin che vengono utilizzati con ISR DEVONO avere il debouncing hardware (basta una resistenza ed un condensatore) e non giochini software che, con le ISR, possono sempre dare problemi.

Guglielmo

Se per caso?

Quel codice è una collezione di mine vaganti e bombe inesplose

A partire da qui…

sto andando a provarlo.
due sensori, due magneti e il codice aggiornato.
ho lasciato come diametro solo lo 0,80


#include <U8g2lib.h>
#include <Wire.h>
#include <EEPROM.h> // 1. Aggiunta libreria memoria

// ---------------- DISPLAY ----------------
U8G2_SH1106_128X64_NONAME_F_HW_I2C display(U8G2_R0, U8X8_PIN_NONE);

// ---------------- PIN ----------------
const int pulsantePin = 4;
const int sensore1Pin = 3;
const int sensore2Pin = 2;

// ---------------- CONTATORI ----------------
volatile long contatore1 = 0;
volatile long contatore2 = 0;
volatile unsigned long lastImpulse1 = 0;
volatile unsigned long lastImpulse2 = 0;
const unsigned long debounceTime = 3;

// ---------------- METRI ----------------
float metriTotali = 0;
float metriVisuali = 0;
const float metriVelocita = 0.5;
int addrEEPROM = 0; // 2. Indirizzo di memoria

// ---------------- PULSANTE ----------------
unsigned long pressStart = 0;
bool buttonPressed = false;
bool showMessage = false;
unsigned long messageStart = 0;
char messageChar = ' ';

// ---------------- FRECCIA DINAMICA ----------------
float frecciaPos = 0;   
bool frecciaAttiva = false;
int direzione = 0;      

// ---------------- SETUP ----------------
void setup() {
  pinMode(pulsantePin, INPUT_PULLUP);
  pinMode(sensore1Pin, INPUT_PULLUP);
  pinMode(sensore2Pin, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(sensore1Pin), conta1, RISING);
  attachInterrupt(digitalPinToInterrupt(sensore2Pin), conta2, RISING);

  // 3. RECUPERA DATI SALVATI
  EEPROM.get(addrEEPROM, metriTotali);
  if (isnan(metriTotali)) metriTotali = 0; // Se la memoria è vuota, metti 0
  metriVisuali = metriTotali;

  display.begin();
  display.clearBuffer();
  Serial.begin(9600);
}

// ---------------- LOOP ----------------
void loop() {
  gestisciPulsante();
  aggiornaMetri();
  aggiornaDisplay();
}

// ---------------- INTERRUPT ----------------
void conta1() {
  unsigned long now = millis();
  if (now - lastImpulse1 > debounceTime) {
    contatore1++;
    lastImpulse1 = now;
  }
}

void conta2() {
  unsigned long now = millis();
  if (now - lastImpulse2 > debounceTime) {
    contatore2--;
    lastImpulse2 = now;
  }
}

// ---------------- PULSANTE ----------------
void gestisciPulsante() {
  static unsigned long ultimoCambio = 0;
  static bool statoAttuale = HIGH;
  static bool ultimoStato = HIGH;
  const unsigned long debounceDelay = 30;

  bool lettura = digitalRead(pulsantePin);
  if (lettura != ultimoStato) ultimoCambio = millis();

  if ((millis() - ultimoCambio) > debounceDelay) {
    if (lettura != statoAttuale) {
      statoAttuale = lettura;
      if (statoAttuale == LOW) {
        pressStart = millis();
        buttonPressed = true;
      } else {
        if (buttonPressed) {
          unsigned long durata = millis() - pressStart;
          if (durata >= 2000) resetMetri(); 
          else if (durata > 50) salvaMetri(); 
        }
        buttonPressed = false;
      }
    }
  }
  ultimoStato = lettura;
}

// ---------------- CALCOLO METRI ----------------
void aggiornaMetri() {
  long c1, c2;
  
  // Protezione per non perdere impulsi mentre leggiamo
  noInterrupts();
  c1 = contatore1; contatore1 = 0;
  c2 = contatore2; contatore2 = 0;
  interrupts();

  long giriTotali = c1 + c2;

  float metriPerGiro = 3.1416 * (0.80 / 10.0);
  metriTotali += giriTotali * metriPerGiro;
  if (metriTotali < 0) metriTotali = 0;

  if (metriVisuali < metriTotali) metriVisuali += metriVelocita;
  if (metriVisuali > metriTotali) metriVisuali -= metriVelocita;

  if (giriTotali > 0) direzione = 1;        
  else if (giriTotali < 0) direzione = -1;  
  else if (metriVisuali == metriTotali) direzione = 0;

  if (direzione != 0) {
    frecciaAttiva = true;
    if (direzione == 1) frecciaPos -= 2.56; 
    else frecciaPos += 2.56;                
    if (frecciaPos < -8 || frecciaPos > 128) frecciaPos = (direzione == 1 ? 128 : -8);
  } else frecciaAttiva = false; 
}

// ---------------- DISPLAY ----------------
void aggiornaDisplay() {
  display.clearBuffer();
  display.setFont(u8g2_font_fub35_tr);
  char buffer[10];
  dtostrf(metriVisuali, 4, 1, buffer);
  int w = display.getStrWidth(buffer);
  display.drawStr((128 - w)/2, 50, buffer);

  if (frecciaAttiva) {
    display.setFont(u8g2_font_ncenB08_tr);
    int x = (int)frecciaPos;
    char c[3];
    if (direzione == 1) { c[0] = '<'; c[1] = '-'; c[2] = 0; } 
    else { c[0] = '-'; c[1] = '>'; c[2] = 0; }
    display.drawStr(x, 10, c);
  }

  if (showMessage) {
    display.setFont(u8g2_font_ncenB08_tr);
    char buf[2]; buf[0] = messageChar; buf[1] = 0;
    display.drawStr(110, 62, buf);
    if (millis() - messageStart > 1500) showMessage = false;
  }
  display.sendBuffer();
}

// ---------------- SALVA / RESET ----------------
void salvaMetri() {
  EEPROM.put(addrEEPROM, metriTotali); // 4. SALVATAGGIO REALE
  messageChar = 'S';
  messageStart = millis();
  showMessage = true;
  Serial.print("Salvato in EEPROM: ");
  Serial.println(metriTotali);
}

void resetMetri() {
  metriTotali = 0;
  metriVisuali = 0;
  EEPROM.put(addrEEPROM, metriTotali); // 5. RESET MEMORIA
  messageChar = 'R';
  messageStart = millis();
  showMessage = true;
  Serial.println("Reset effettuato!");
}

Come può essere?... Fose deve essere =, non +=...

• giriTotali → numero di giri (può essere positivo o negativo)
• metriPerGiro → quanti metri fai con un giro di bobina
• += → somma al totale già accumulato

Come può essere negativo?

Tutto essere potrebbe

Se giri totali in realtà parziali è tutte le volte a zero uguagliando

Se non sono giri totali, non chiamarli giriTotali!
I giri totali sono il totale e la lunghezza totale è uguale al numero totale dei giri per la lunghezza unitaria.

Fosse l'unico errore
In realta' anche il calcolo della circonferenza è fantasioso
Peggio ancora:
Invece di risolvere gli errori attuali ne sta introducendo di potenzialmente nuovi cercando anche di usare la eprom prima di mettere a posto quello che (non) e' riuscito a fare finora

Chi e' che parlava di autostrada per l'inferno?

Non ho approfondito...
Per tenere conto del diametro che cresce, bisognerebbe stimare gli strati, in base allo spazio disponibile e al numero di spire già avvolte con il filo del diametro impostato.

avevo intenzione di misurare ogni 50 metri e segnare il eeprom il valore

adesso sembra funzionare, i sensori sono messi vicini e segna i metri in una direzione e nell'altra. devo ptovare se la misurazione è reale. Ho inserito la circonferenza della bobina e la sua lunghezza per calcolare quanto raccoglie da vuota e da piena... c'è solo da provarla e apportare qualche correzione.

#include <U8g2lib.h>
#include <Wire.h>

// ---------------- DISPLAY ----------------
U8G2_SH1106_128X64_NONAME_F_HW_I2C display(U8G2_R0, U8X8_PIN_NONE);

// ---------------- PIN ----------------
const int pulsantePin = 4;
const int sensore1Pin = 3; // A (interrupt)
const int sensore2Pin = 2; // B (direzione)

// ---------------- CONTATORE ----------------
volatile long contatore = 0;
volatile unsigned long lastImpulse = 0;
const unsigned long debounceTime = 3;

// ---------------- METRI ----------------
float metriTotali = 0;
float metriVisuali = 0;
const float metriVelocita = 0.5;

// ---------------- PULSANTE ----------------
unsigned long pressStart = 0;
bool buttonPressed = false;
bool showMessage = false;
unsigned long messageStart = 0;
char messageChar = ' ';

// ---------------- FRECCIA ----------------
float frecciaPos = 0;
bool frecciaAttiva = false;
int direzione = 0; // 1=raccoglie, -1=lascia, 0=fermo

// ---------------- DATI BOBINA ----------------
const float circonferenzaBobina = 0.377; // metri (377 mm)
const float spessoreFilo = 0.0008;       // metri (0,80 mm)
const float larghezzaBobina = 0.128;     // metri (128 mm)

// ---------------- SETUP ----------------
void setup() {
  pinMode(pulsantePin, INPUT_PULLUP);
  pinMode(sensore1Pin, INPUT_PULLUP);
  pinMode(sensore2Pin, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(sensore1Pin), conta, RISING);

  display.begin();
  display.clearBuffer();
  Serial.begin(9600);
}

// ---------------- LOOP ----------------
void loop() {
  gestisciPulsante();
  aggiornaMetri();
  aggiornaDisplay();
}

// ---------------- INTERRUPT ----------------
void conta() {
  unsigned long now = millis();
  if (now - lastImpulse > debounceTime) {

    int statoB = digitalRead(sensore2Pin);

    if (statoB == HIGH) {
      contatore++;   // avanti
    } else {
      contatore--;   // indietro
    }

    lastImpulse = now;
  }
}

// ---------------- PULSANTE ----------------
void gestisciPulsante() {
  static unsigned long ultimoCambio = 0;
  static bool statoAttuale = HIGH;
  static bool ultimoStato = HIGH;
  const unsigned long debounceDelay = 30;

  bool lettura = digitalRead(pulsantePin);

  if (lettura != ultimoStato) ultimoCambio = millis();

  if ((millis() - ultimoCambio) > debounceDelay) {
    if (lettura != statoAttuale) {
      statoAttuale = lettura;

      if (statoAttuale == LOW) {
        pressStart = millis();
        buttonPressed = true;
      } 
      else {
        if (buttonPressed) {
          unsigned long durata = millis() - pressStart;
          if (durata >= 2000) resetMetri();
          else if (durata > 50) salvaMetri();
        }
        buttonPressed = false;
      }
    }
  }

  ultimoStato = lettura;
}

// ---------------- CALCOLO METRI ----------------
void aggiornaMetri() {
  long impulsi = contatore;
  contatore = 0;

  // METRI REALI PER GIRO
  metriTotali += impulsi * circonferenzaBobina;

  if (metriTotali < 0) metriTotali = 0;

  // smoothing visuale
  if (metriVisuali < metriTotali) metriVisuali += metriVelocita;
  if (metriVisuali > metriTotali) metriVisuali -= metriVelocita;

  // direzione freccia
  if (impulsi > 0) direzione = 1;
  else if (impulsi < 0) direzione = -1;
  else direzione = 0;

  // freccia animata
  if (direzione != 0) {
    frecciaAttiva = true;

    if (direzione == 1) frecciaPos -= 128.0 / 50.0;
    else frecciaPos += 128.0 / 50.0;

    if (frecciaPos < -8 || frecciaPos > 128) {
      frecciaPos = (direzione == 1 ? 128 : -8);
    }
  } else {
    frecciaAttiva = false;
  }
}

// ---------------- DISPLAY ----------------
void aggiornaDisplay() {
  display.clearBuffer();

  display.setFont(u8g2_font_fub35_tr);
  char buffer[10];
  dtostrf(metriVisuali, 4, 1, buffer);
  int w = display.getStrWidth(buffer);
  display.drawStr((128 - w)/2, 50, buffer);

  if (frecciaAttiva) {
    display.setFont(u8g2_font_ncenB08_tr);
    int x = (int)frecciaPos;
    int y = 10;

    char c[3];
    if (direzione == 1) { c[0] = '<'; c[1] = '-'; c[2] = 0; }
    else { c[0] = '-'; c[1] = '>'; c[2] = 0; }

    display.drawStr(x, y, c);
  }

  if (showMessage) {
    display.setFont(u8g2_font_ncenB08_tr);
    char buf[2]; buf[0] = messageChar; buf[1] = 0;
    display.drawStr(110, 62, buf);

    if (millis() - messageStart > 1500) showMessage = false;
  }

  display.sendBuffer();
}

// ---------------- SALVA / RESET ----------------
void salvaMetri() {
  messageChar = 'S';
  messageStart = millis();
  showMessage = true;

  Serial.print("Salvato: ");
  Serial.println(metriTotali);
}

void resetMetri() {
  metriTotali = 0;
  metriVisuali = 0;
  contatore = 0;

  messageChar = 'R';
  messageStart = millis();
  showMessage = true;

  Serial.println("Reset effettuato!");
}

altro buco... sembrava funzionare... ma è svampato tutto... un modulo lm2596 collegato ad una batteria 12 volt da auto. forse dovrei aggiungere Modulo di protezione da sovratensione OVP 5,5 V 2,5 A 30 V Mini protezione alimentazione?