Primo progetto sensore Hall

Sono riuscito a creare un progetto funzionante, ma vorrei un consiglio e un chiarimento su alcuni dubbi e curiosità che ho.
Ho preso un Arduino Micro (compatibile) che però vedo come Leonardo, ma penso che non cambi molto per il mio progetto.
Ho preso un sensore di Hall KY-035.
Ho guardato un pò di scketch e ho creato questo dopo molti tentativi.
Con questi componenti ho creato un freno a mano da collegare al PC.
Attualmente non lo ho ancora assemblato, ma sto testando il funzionamento a banco.

Questo è il mio codice:

#include <Joystick.h>  // seleziono libreria

int sensorPin = A0;                 // pin con magnetic sensor KY-035
int val;              

void setup() {                    // comando ripetuti 1 volta
  pinMode(A0, INPUT);// imposto pin INPUT (leggere segnali), OUTPUT (inviare segnali)
  Serial.begin(9600);            // velocità lettura
  Joystick.begin();
}

void loop() {                       // comando ripetuto in loop dopo il void setup
  val = analogRead(sensorPin);       // leggi i valori del sensore
  Serial.println(val);               // vedi i valori sul plotter

  delay(100);                            // velocità lettura segnale
  {int pot = analogRead(A0);
int mapped = map(pot,0,1023,0,255);
{Joystick.setRudder(mapped);}}               // asse impostato
  
}
  1. Mi sono messo delle note a fianco dei comandi per imparare a cosa servono, se qualcuno legge delle assurdità sono bene accette correzioni
  2. Non mi è ben chiara la funzione (pot,0,1023,0,255) penso sia il range di utilizzo del sensore, ma in che scala?
  3. Ho riscritto il mio Arduino parecchie volte intanto che sperimentavo, ma il mio dubbio è:
    ma ogni volta che scrivo qualcosa sovrascrive tutto il precedente codice o é il caso dopo tante scritture fare una pulizia? Userei il termine reset, ma ho visto che il Pin reset serve solo per riavviare.

Grazie

: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.

A quanto detto da UKHeliBob aggiungo ...
... evitate di utilizzare la traduzione automatica fatta dal browser ... vi impedisce di capire la lingua della sezione dove andate a scrivere. :roll_eyes:

Guglielmo

Dalla foto direi che per prima cosa dovresti fare un minimo di pratica con il saldatore, e mettere degli header (connettori) al quel povero Micro, evita di usare fili "intrecciati" dentro al buco, non solo per la stabilità dei segnali ma anche e soprattutto per evitare corti circuiti che potrebbero danneggiare il Micro.
In genere nelle confezioni sono presenti, ad esempio:


Una volta saldati potrai usarlo non solo più agevolmente ma potrai (dovrai) usare una breadboard, sulla quale inserirai quei piedini:

e con quella fare i collegamenti usando cavetti specifici (chiamati "Dupont"):

Per i prototipi sono gli elementi base.
Veniamo alle tue domande:

La funzione non è quella, quelli sono i parametri passati alla funzione "map()" la quale prende il primo valore (il contenuto della variabile "pot" in questo caso), e "mappa" il valore, che va da 0 a 1023, per farlo diventare tra 0 e 255.
Dato che è una funzione usata comunemente, suppongo che tu abbia veramente poche conoscenze di Arduino e di programmazione, comunque sul sito trovi molte pagine che spiegano il linguaggio e funzioni, ed anche la map() QUI ad esempio.

Si, ogni volta viene riscritto tutto, non serve nessun "reset".

Erano dei collegamenti volanti (forse troppo) per fare dei test, visto che non ero ancora sicuro della posizione dei pin, nella confezione ho trovato anche gli header,comunque ho un saldatore ed ho un minimo di manualità per saldare, quello che non ho proprio è la conoscenza del linguaggio di programmazione, ed è qui che fatico a capire certi passaggi, e nel forum o nel web non sempre trovo una risposta chiara ed univoca ai miei dubbi, ma molto probabilmente è anche dato dalla mia ingnoranza in materia.

{int pot = analogRead(A0);
int mapped = map(pot,0,1023,0,255)

Quindi questo comando prende i millivolt generati dal sensore sul pin A0 e li converte di 1\5 circa per darli al pc? Se cambio il valore 255, sul PC avrò una risposta più o meno sensibile?

Un ultima domanda, ma una volta che non mi serve più il plotter seriale, è meglio cancellare il comando o posso lasciarlo che non consuma risorse?

No. La funzione "analogRead()" restituisce un valore tra 0 e 1023 (10 bit) proporzionale al livello della tensione (0-5V) su quel pin (nel tuo caso "A0"). Poi la funzione "map()" converte questo range 0-1023 in un byte ossia 0-255 e lo registra nella variabile "mapped" che contiene, appunto, il valore tra 0 e 255, per cui 0 significa 0V, 255 è 5V.

La sensibilità non la puoi modificare, ma sta a te capire se il segnale che vuoi rilevare sia nel range 0-5V o meno. Se dovessero avere un range diverso dovresti prevedere un circuito esterno per riportarlo a 0-5V, abbassandolo o amplificandolo.
Mentre per la mappatura dipende da cosa devi farci: se mappi tra 0 a 255 avrai al massimo 256 livelli, se ti serve maggiore precisione dovresti non mappare e usare 0-1023 ma nel tuo caso se vuoi simulare un joystick penso che penso/spero che 256 livelli siano sufficienti.

Finisco con qualche altra considerazione sul codice.
Vedo questo:

void loop() {                       // comando ripetuto in loop dopo il void setup
  val = analogRead(sensorPin);       // leggi i valori del sensore
  Serial.println(val);               // vedi i valori sul plotter

  delay(100);                            // velocità lettura segnale
  {int pot = analogRead(A0);
int mapped = map(pot,0,1023,0,255);
{Joystick.setRudder(mapped);}}               // asse impostato
  
}

Ci sono troppe graffe, non sono necessarie (quelle da "{int pot ="). Inoltre tu leggi inutilmente due volte dal pin analogico, perché nel primo leggi da "sensorPin" che vale A0, nel secondo direttamente A0. Tra l'altro anche la variabile "pot" non ti serve, perché hai già la lettura dentro alla variabile globale "val".

Poi, per questioni di maggiore leggibilità, anche l'indentazione ti conviene mantenerla come da "standard", se vuoi puoi fartela sistemare dall'IDE stesso premendo Ctrl-T e vedi come esce.

Infine (per ora :wink: ) valori che all'interno del codice non cambiano (ossia sono "costanti") come ad esempio i pin di configurazione (per i quali basta definirli "byte" visto che non superano sicuramente il valore massimo 255), si impostano in genere in maiuscolo e vanno definiti come "const" o come "#define". Nel tuo caso, ad esempio, invece di:

int sensorPin = A0;                 // pin con magnetic sensor KY-035

potrebbe essere:

const byte SENSOR_PIN = A0;                 // pin con magnetic sensor KY-035

oppure:

#define SENSOR_PIN A0

Considera anche che con "delay(100)" avrai 10 letture al secondo, anche questo potrebbe essere un parametro variabile ossia messo in configurazione all'inizio.

E quindi lo imposterei così, ad esempio:

#include <Joystick.h>  // seleziono libreria

// CONFIGURAZIONE
const byte SENSOR_PIN = A0;     // pin con magnetic sensor KY-035
const int DELAY_LETTURA = 100;  // Millisecondi di attesa tra letture

// Variabili globali
int val;    // Valore letto dal pin analogico
int mapped; // Valore "mappato" verso il joystick

void setup() {                    // comando ripetuti 1 volta
  pinMode(A0, INPUT);// imposto pin INPUT (leggere segnali), OUTPUT (inviare segnali)
  Serial.begin(9600);            // velocità lettura
  Joystick.begin();
}

void loop() {
  // leggi i valori del sensore
  val = analogRead(sensorPin);
  mapped = map(pot,0,1023,0,255);
  // manda i valori anche sul plotter
  Serial.println(mapped);
  // asse impostato  
  Joystick.setRudder(mapped);
  delay(DELAY_LETTURA);
}

Provalo e vedi se hai compreso i consigli. :wink:

Ho provato il tuo codice, ma ho dovuto cambiare

 const byte SENSOR_PIN = A0;`

con

const byte sensorPin = A0;

ma non vedo la scritta byte colorata, e poi maiuscolo o minuscolo è indifferente o ha importanza nel codice?

Altra correzione l'ho fatta con

mapped = map(pot,0,1023,0,255);

ho messo

mapped = map(val, 0, 1023, 0, 255);

Ma dopo questa ultima correzione mi da il seguente errore:

exit status 1
Compilation error: exit status 1

Penso che non sia un problema nel codice perchè lo stesso errore me lo da con il codice che ho messo nell'Arduino ed era (e lo è tuttora) funzionante...

Certamente, il mio codice, come avevo scritto, era di "esempio", non completo/verificato (per farlo verificare a te... :wink: )
Per quella costante ovviamente dovresti fare al contrario, ossia lasciare "SENSOR_PIN" e cambiare il nome nella riga dove la usi (ossia "val = analogRead(sensorPin);" in "val = analogRead(SENSOR_PIN);").

Stessa cosa anche per la map(), ovviamente "pot" era la tua variabile, "val" quella che si dovrebbe usare ora, per cui questo hai fatto bene a farlo. :+1:

Il compilatore distingue maiuscole e minuscole quindi se qualcosa è definito in un certo modo devi continuare ad usare quello. Il discorso maiuscolo o minuscolo è rilevante solo per questo (es. "valore" è diverso da "Valore" e da "VALORE"). Per le costanti è la stessa cosa, se la definisci in un modo devi usarla sempre allo stesso, ma impostarle tutte in maiuscolo è una convenzione, al compilatore non interessa.

Prima di quelle righe dovrebbe scrivere qualcosa, quindi con l'errore. Allarga lo spazio dei messaggi del compilatore, e riporta qui l'output completo (racchiudi anche questo nel tag "code" come per il codice).

Volevi mettermi alla prova... ...bene, bene...

Fortunatamente sono curioso, e nonostante il progetto fosse già funzionante,
ho voluto approfondire meglio il sistema, anzi, ne approfitto per ringraziarti del tempo che mi stai dedicando.

Guardando il segnale del sensore nel plotter ho notato che a riposo ho delle oscillazioni:

Il sensore ora è montato nel freno a mano, e probabilmente non è abbastanza lontano dal magnete, ma gli spazi che ho sono quelli.
Una soluzione io l' ho trovata, e all'atto pratico non trovo grosse differenze di funzionamento, ma volevo un parere.
Ho provato a modificare questa riga di comando in questi 2 modi:
1:

mapped = map(val, 1, 1023, 0, 255);

2:

mapped = map(val, 0, 1023, 1, 255);

In qualsiasi dei 2 casi ho una linea piatta in posizione di riposo, ma quale soluzione è meglio usare?

Dimenticavo, l'errore lo ho risolto, era un problema nella libreria "Joystick.h", l'ho reinstallata, probabilmente ho modificato qualcosa quando ho aperto il file...

Mi sembrano oscillazioni minime, non dovute al sensore ma semplicemente alla conversione analogico-digitale interno, ossia ha sia una sua precisione (quindi piccole fluttuazioni nella conversione sono normali), unita ad eventuali interferenze emettromagnetiche (ad esempio sui cavi). Se vedi valori tra 140 e 141 significa che il pin restituisce valori attorno a 562 (più o meno tra 560 a 564). Se vuoi una risposta più lineare ti basta considerare al posto della lettura istantanea solamente il valore medio di un certo numero di letture (es. le ultime 4).

Nessuna delle due, lascia com'era ma ad ogni lettura memorizza gli ultimi "n" dati (in un array "circolare", ossia ad ogni lettura entra un dato ed esce il più vecchio) e considera la loro media. Fammi sapere se sai come realizzare questa cosa del buffer circolare, o se vuoi qualche "spunto" anche qui.

Sinceramente non saprei come creare questa "cosa" circolare...

Ma non è più semplice dargli una zona morta e far leggere il sensore da 142 in su, o convertire il segnale oltre i 565 e ignorare i segnali più bassi?

A far leggere un valore di media avrei una lettura meno precisa, e avrei il rischio comunque di vedere attivarsi il freno a mano in gioco se dovessi avere interferenze maggiori.

Ti ho citato questa cosa per farti iniziare ad acquisire il concetto di "array" (o "matrice" in italiano). Un array è una variabile che contiene un certo numero di dati e che sono identificati da un numero che va da 0 al valore massimo, o "indice". Ad esempio:

int dato[4];

Questo è un array di 4 elementi (il valore tra le parentesi quadre) di tipo "int". Per leggere il primo elemento si scrive "dato[0]" (l'indice degli array iniziano da zero), in secondo come "dato[1]" e così via.
E' come se disegnassi un rettangolo diviso in 4 quadrati, e dentro ad ognuno di questi ci scrivessi a matita il valore.
Puoi ad esempio mostrare sulla seriale tutti i valori usando l'istruzione "for()":

  for(int i = 0; i < 4; ++i)
    Serial.println(dato[i]);

Per il nostro caso, ora immagina una "pila" di piatti: quando ne lavi uno lo metti sopra, e quando te ne serve uno lo prendi da sotto. Ecco, la stessa cosa con le letture: crei una "pila" di dati, diciamo gli ultimi 4, con l'array di sopra. Se, per fare un esempio, ricevi i valori 4, poi 3, 7, 5 e 1, avresti una situazione di questo tipo:

leggo 4: 4 - - -
leggo 3: 3 4 - -
leggo 7: 7 3 4 -
leggo 5: 5 7 3 4
leggo 1: 1 5 7 3

Come vedi ad ogni dato abbiamo "spostato" verso destra i valori esistenti per inserire quello nuovo all'inizio, e nell'ultimo passo è "sparito" il 4.
Ma per ora tralasciamo questa cosa perché non è banalissima per chi inizia, ma ti assicuro che molto presto ti potrà servire...

Dipende da cosa vuoi ottenere, soprattutto in base a quale applicazione ha questa cosa, perché non ho ben capito cosa deve rilevare e cosa dovrebbe fare in base a ciò che rilevi. Hi parlato di "freno a mano": a te interessa avere un valore proporzionale alla distanza del sensore dal magnete, ossia la posizione del freno a mano (quindi ti serve un valore numerico specifico), o ti occorre solo sapere se è "tirato" (quindi solo se è "su" o "giù", quindi in valore booleano cioè "vero/falso")? O cosa altro?

Affatto, anzi è più precisa proprio perché in questo modo "filtri" ogni eventuale fluttuazione legata ai collegamenti ed alla variabilità del convertitore digitale-analogico. Ovvio che parlando di media si intende calcolata su una serie di letture molto ravvicinate tra loro di pochi decimi di secondo o anche meno, non certo di letture fatte ogni secondo o più...

Spiega meglio cosa intendi fare (ossia Arduino cosa deve leggere esattamente e cosa deve poi fare con questo dato?), e vediamo quale soluzione si possa trovare tra quelle più semplici vista la tua scarsa esperienza.

Ho fatto un freno a mano per un simulatore, e a me serve che abbia una risposta progressiva, non on\off, altrimenti avrei usato un altro sensore.

Ho ben capito il funzionamento, ma il mio problema è come scriverlo.

int dato[4];

con questo comando gli dico quanti valori prendo per fare la media.

for(int i = 0; i < 4; ++i)
    Serial.println(dato[i]);

con questi invio al Plotter la media dei 4 valori presi prima.

Ma a me sembra che manchi qualcosa;
non dovrei usare un comando (penso mapped) per inviare al pc i miei nuovi dati (la media delle ultime 4 letture)?

Questo è il mio nuovo codice:

#include <Joystick.h>  // seleziono libreria

// CONFIGURAZIONE
const byte SENSOR_PIN = A0;      // pin con magnetic sensor KY-035
const int DELAY_LETTURA = 100;  // Millisecondi di attesa tra letture

// Variabili globali
int val;     // Valore letto dal pin analogico
int mapped;  // Valore "mappato" verso il joystick
int dato[4];

void setup() {         // comando ripetuti 1 volta
  pinMode(A0, INPUT);  // imposto pin INPUT (leggere segnali), OUTPUT (inviare segnali)
  Serial.begin(9600);  // velocità lettura
  Joystick.begin();
}

void loop() {
  // leggi i valori del sensore
  val = analogRead(SENSOR_PIN);
  mapped = map(val, 0, 1023, 0, 255);
  
  // manda i valori anche sul plotter
  for(int i = 0; i < 4; ++i);
  Serial.println(dato[val]);
  // asse impostato
  Joystick.setRudder(mapped);
  delay(DELAY_LETTURA);
}

:exploding_head:

Forse ti sarebbe utile una media mobile

Prova a cercare "rigoletto il duca di Mantova"

E cercare anche di capire i due errori che ho fatto e poi corretto
Ti sarà certamente di aiuto

No, quelle istruzioni mandano sulla seriale i 4 valori letti, non la media. Ma era solo un esempio per spiegarti come si accede agli elementi dell'array.

Certo, ma prima devi accumulare gli ultimi 4 dati nell'array, e poi calcolare la media.
Quindi potresti ad esempio definire queste variabilie globali:

// Array con le ultime 4 letture (buffer circolare)
int dato[4];
// Valore della media calcolata
int media = 0;
// Numero di elementi nel buffer (se 0 il buffer è vuoto)
int p = 0;

Ora ad ogni lettura devi fare l'operazione di inserimento nel buffer e con questo calcolare la media.
L'inserimento lo fai semplicemente scrivendo dentro al primo elemento del buffer:

  dato[0] = val;

Prima di fare questo però devi "fare posto" nel buffer, come ho descritto nel mio precedente post. Quindi prima di mettere il nuovo dato nella prima posizione ("dato[0]") devi spostare gli altri tre, ossia:

  // Rotazione dei dati del buffer
  dato[3] = dato[2];
  dato[2] = dato[1];
  dato[1] = dato[0];
  // Nuovo dato
  dato[0] = val;

A quel punto puoi calcolare la media, ma devi sapere quanti elementi ci sono nel buffer, almeno fino a che non è pieno (ossia quando ci sono 4 elementi), e questo è lo scopo della variabile "p" che ti avevo indicato sopra. In pratica:

  media = 0;
  for (int i = 0; i < p; ++i)
    media += dato[i];
  media = media / p;

Per cui alla fine puoi mandare in uscita il valore della variabile "media", in prima battuta lo mandi alla seriale per visualizzare (poi quando tutto funziona puoi cambiare l'output e mandarlo a joystick):

  // Calcolo la media
  media = 0;
  for (int i = 0; i < p; ++i)
    media += dato[i];
  Serial.print(" somma=");Serial.print(media);
  media = media / p;
  Serial.print(" media=");Serial.print(media);
  Serial.println();

Prova a fare queste modifiche e vedi se ti funziona (in caso contrario, ovviamente posta il tuo nuovo codice così ne parliamo).

Ho un problema con il mio dispositivo, non so se è il cavo o è saltato, quindi non riesco a procedere con le mie prove, ma ieri sono arrivato a questo codice:

// 27.1.2025

#include <Joystick.h>  // seleziono libreria

// CONFIGURAZIONE
const byte SENSOR_PIN = A0;      // pin con magnetic sensor KY-035
const int DELAY_LETTURA = 100;  // Millisecondi di attesa tra letture

// Variabili globali
int val;     // Valore letto dal pin analogico
int mapped;  // Valore "mappato" verso il joystick
int dato[4]; // Numero di valori letti per la media (array)
int media = 0;
int p = 0;

void setup() {         // comando ripetuti 1 volta
  pinMode(A0, INPUT);  // imposto pin INPUT (leggere segnali), OUTPUT (inviare segnali)
  Serial.begin(9600);  // velocità lettura
  Joystick.begin();
  
}

void loop() {
  // leggi i valori del sensore
  val = analogRead(SENSOR_PIN);
  mapped = map(val, 0, 1023, 0, 255);

  // Rotazione dei dati del buffer
  dato[4] = dato[1];
  dato[3] = dato[2];
  dato[2] = dato[3];
  dato[1] = dato[4];
  // Nuovo dato
  dato[0] = val;
  
  
// asse impostato
  Joystick.setRudder(mapped);
  delay(DELAY_LETTURA);

// Calcolo la media
  media = 0;
  for (int i = 0; i < p; ++i)
    media += dato[i];
  Serial.print(" somma=");Serial.print(media);
  media = media / p;
  Serial.print(" media=");Serial.print(media);

  Serial.println(" somma=");Serial.println(media);
  media = media / p;
  Serial.println(" media=");Serial.println(media);


}

Premetto che non da i risultati che vorrei:

  1. Da una media di 516 e il mio valore dovrebbe essere 140\ 141
  2. Quando tiro la leva del freno a mano ho una lettura che va da circa 500 a circa 600, ho un range di utilizzo molto bassa.

Ho poi dei dubbi sui dati dei buffer, è giusto che parta con data 1 e tengo lo 0 per il

dato[0] = val

E' giusto come lancio il "plotter seriale", perchè vedo la lettura di 4 dati ma vedo il grafico solo del primo.

Ci sono una serie di errori
Dichiari p come intero e gli assegni il valore 0.
Questo valore non viene mai aggiornato nel resto del codice quindi nel loop fai un ciclo che non cicla

for (int i = 0; i < p; ++i)

successivamente dividi per 0

  media = media / p;
  Serial.print(" media=");Serial.print(media);

  Serial.println(" somma=");Serial.println(media);
  media = media / p;

Infine, l'errore più grave è nella gestione del buffer circolare: tu dichiari un array di 4 elementi interi, ma nel loop cerci di assegnare un valore ad un quinto elemento che non esiste.
L'indice degli array inizia sempre da 0, quindi hai 0, 1, 2, 3

dato[4] = dato[1];

Ciò detto, ci sono modi più efficienti per implementare un buffer circolare piuttosto che far scorrere ogni volta tutti gli elementi dell'array, ma per ora va bene anche cosi visto che ci sono lacune più importanti da colmare.

Sarebbe interessante vedere i dati grezzi, cioè il dato restituito da analogRead(), nelle due
posizioni opposte. La map già opera come un filtro, il valore 140, 141 è una oscillazione di una unita. Solo dopo avere visto i dati grezzi e la loro distribuzione si può pensare su come intervenire, magari basta un condensatore e una resistenza, oppure serve anche aggiungere una media mobile, o potrebbe bastare una media aritmetica.

Ciao.

Hehe, ovviamente fargli fare un tentativo e probabilmente sbagliare e quindi fargli capire gli errori era lo scopo di non dare la "pappa pronta", per cui hai fatto bene, grazie, mi hai preceduto :wink: