Coordinate con magnetometro

buonasera a tutti
ho tralasciato il topic del gps x aprirne uno nuovo sul magnetometro, per la rilevazione dei punti cardinali, dato che il gps non era attendibili, lo sketch funziona bene, soltanto che come avevo scritto mi dava interferenza sulla seriale, inquanto sulla seriale transitano troppi valori, dal potenziometro x regolare il volume del player, alle coordinate x la bussola, fino ad arrivare alla velocità ( sempre se dla inserisco).
questo è lo sketch che uso per la bussola

#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>
 
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1    // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C // See datasheet for Address
 
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
 Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);
 
void setup(void)
{
  Serial.begin(9600);
  Serial.println("HMC5883 Magnetometer Test");
 

  if (!mag.begin())
  {
   
    Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
    while (1);
  }
 
  
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
  {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); 
  }
 
  display.clearDisplay();
  display.display();
  delay(500);
}
 
void loop(void)
{
 
  sensors_event_t event;
  mag.getEvent(&event);
 
  
  float heading = atan2(event.magnetic.y, event.magnetic.x);
 
  float declinationAngle = 0.06;
  heading += declinationAngle;
 
  if (heading < 0)
    heading += 2 * PI;
   
  if (heading > 2 * PI)
    heading -= 2 * PI;
   
  float headingDegrees = heading * 180 / M_PI;
 
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
   
  display.setTextSize(2);
  display.setCursor(10, 10);
  display.print(headingDegrees, 0);
  display.print((char)247);
   
  display.setTextSize(3);
  display.setCursor(10, 40);
 
  if (headingDegrees >= 337.5 || headingDegrees < 22.5)
    display.print("N");
  else if (headingDegrees >= 22.5 && headingDegrees < 67.5)
    display.print("NE");
  else if (headingDegrees >= 67.5 && headingDegrees < 112.5)
    display.print("E");
  else if (headingDegrees >= 112.5 && headingDegrees < 157.5)
    display.print("SE");
  else if (headingDegrees >= 157.5 && headingDegrees < 202.5)
    display.print("S");
  else if (headingDegrees >= 202.5 && headingDegrees < 247.5)
    display.print("SW");
  else if (headingDegrees >= 247.5 && headingDegrees < 292.5)
    display.print("W");
  else if (headingDegrees >= 292.5 && headingDegrees < 337.5)
    display.print("NW");
    
  display.display();
 
}

il tutto funziona, sul display ssd1306 si vedono le coordinate e i gradi, adesso un mio amico che purtroppo non mi può aiutare, mi ha suggerito x evitare conflitti di creare una variabile di tipo char e di convertire i valori in gradi in stringa, e assegnargli un identificativo, cosi anche per il volume che per la velocità.
adesso il mio problema è come creo tutto ciò e lo invio tramite la seriale? in rete ho visto alcuni esempi ed ho scritto questo

char send [20]; 
sprintf(send, "1; %f", headingDegrees);
Serial.write(send);

ma sul monitor seriale non compaiono i gradi ma soltanto ?1;
grazie mille x l aiuto

il codice l ho inserito dopo display.display

#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>
 
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1    // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C // See datasheet for Address
 
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
 
/* Assign a unique ID to this sensor at the same time */
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);
 
void setup(void)
{
  Serial.begin(9600);
  Serial.println("HMC5883 Magnetometer Test");
 
  /* Initialise the sensor */
  if (!mag.begin())
  {
    /* There was a problem detecting the HMC5883 ... check your connections */
    Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
    while (1);
  }
 
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
  {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Don't proceed, loop forever
  }
 
  display.clearDisplay();
  display.display();
  delay(500);
}
 
void loop(void)
{
  /* Get a new sensor event */
  sensors_event_t event;
  mag.getEvent(&event);
 
  // Calculate heading when the magnetometer is level, then correct for signs of axis.
  float heading = atan2(event.magnetic.y, event.magnetic.x);
 
  float declinationAngle = 0.06;
  heading += declinationAngle;
 
  // Correct for when signs are reversed.
  if (heading < 0)
    heading += 2 * PI;
 
  // Check for wrap due to addition of declination.
  if (heading > 2 * PI)
    heading -= 2 * PI;
 
  // Convert radians to degrees for readability.
  float headingDegrees = heading * 180 / M_PI;
 
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
 
  // Display angle in degrees
  display.setTextSize(2);
  display.setCursor(10, 10);
  display.print(headingDegrees, 0);
  display.print((char)247);
 
  // Display direction in bold letters
  display.setTextSize(3);
  display.setCursor(10, 40);
 
  if (headingDegrees >= 337.5 || headingDegrees < 22.5)
    display.print("N");
  else if (headingDegrees >= 22.5 && headingDegrees < 67.5)
    display.print("NE");
  else if (headingDegrees >= 67.5 && headingDegrees < 112.5)
    display.print("E");
  else if (headingDegrees >= 112.5 && headingDegrees < 157.5)
    display.print("SE");
  else if (headingDegrees >= 157.5 && headingDegrees < 202.5)
    display.print("S");
  else if (headingDegrees >= 202.5 && headingDegrees < 247.5)
    display.print("SW");
  else if (headingDegrees >= 247.5 && headingDegrees < 292.5)
    display.print("W");
  else if (headingDegrees >= 292.5 && headingDegrees < 337.5)
    display.print("NW");
    
  display.display();
 char send[20];
 sprintf(send, "1; %f", headingDegrees);
 Serial.write(send);
  delay(500);
}

Non puoi usare su Arduino la sprintf() per i float (è una limitazione di Arduino). Devi usare dtostrf():

  dtostrf(headingDegrees, 4, 2, send);
  Serial.write(send);
1 Like

grazie mille, ma l identificativo? come gli assegno un identificativo che poi invio sulla seriale, inquanto dall altra parte, cioè su vb.net l intera stringa ppoi la splitto e prendo solo le coordinare, cosi facendo posso usare un identificativo per ogni cosa che transita dalla seriale.
grazie
ho provato ad aggiunger

dtostrf(send, "1, %f", headingDegrees);

ma mi da errore

Compilation error: cannot convert 'char*' to 'double' for argument '1' to 'char* dtostrf(double, signed char, unsigned char, char*)'

purtroppo per evitare confusione sulla seriale devo identificare con un numero o una lettere ogni cosa.
grazie mille

ho semplificato tutto il codice x inviare alla seriale soltanto i gradi del magnetometro, rimane sempre il problema di creare un identificativo dentro la quale inserire i gradi
questo è il codice semplificato

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>
 

Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);

void setup(void)
{
  Serial.begin(9600);
}

void loop()
{
  sensors_event_t event;
  mag.getEvent(&event);
  float heading = atan2(event.magnetic.y, event.magnetic.x);
  float declinationAngle = 0.06;
  heading += declinationAngle;
  if (heading < 0) {
    heading += 2 * PI;
  }
  if (heading > 2 * PI) {
    heading -= 2 * PI;
  }
  float headingDegrees = heading * 180 / M_PI;
    Serial.println(headingDegrees);
    delay(500);
    
}

ho provato come mi ha suggerito "docdoc", ma non c'è nessun identificativo, anzi sulla seriale i gradi sono ancora più lunghi con 2 virgole.

Non è del tutto vero, puoi, consumando un po' più di memoria (circa 1.5 KBytes) ed è anche facile attivare la cosa ...
... avevo spiegato in dettaglio come fare QUI :wink:

Guglielmo

Perchè cerchi di usarla come sprintf(), ma la sintassi è diversa, qui è spiegato come si usa.

Ciao, Ale.

Beh perdonami, non andare "a caso", ma quando leggi una nuova funzione per prima cosa vai sempre a cercare la sintassi ed esempi (ad esempio QUI) e capire come funziona e come si usa. E capirai che vuole 4 parametri, e il secondo parametro non è un formato.

La dtostrf() converte un float in stringa, se ti serve inviare anche l'identificativo ti basta farlo o in sequenza:

  dtostrf(headingDegrees, 4, 2, send);
  Serial.write("1; ");
  Serial.write(send);

Ci sono varie soluzioni comunque, ad esempio sfruttando un secondo buffer ("deg") che usi per convertire il float in stringa, che userai poi nell'altro buffer ("send") concatenandolo con l'identificativo:

  char deg[8];
  dtostrf(headingDegrees, 4, 2, deg);
  sprintf(send, "1; %s", deg);
  Serial.write(send);

o anche usando le funzioni strcpy() e strcat():

  char deg[8];
  dtostrf(headingDegrees, 4, 2, deg);
  strcpy(send, "1; ");
  strcat(send, headingDegrees);
  Serial.write(send);

grazie mille ho scritto il tutto così,

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>
 

Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);

void setup(void)
{
  Serial.begin(9600);
}

void loop()
{
  sensors_event_t event;
  mag.getEvent(&event);
  float heading = atan2(event.magnetic.y, event.magnetic.x);
  float declinationAngle = 0.06;
  heading += declinationAngle;
  if (heading < 0) {
    heading += 2 * PI;
  }
  if (heading > 2 * PI) {
    heading -= 2 * PI;
  }
  float headingDegrees = heading * 180 / M_PI;
  char send[20];
    char deg[8];
  dtostrf(headingDegrees, 4, 2, deg);
  strcpy(send, "1 ");
  sprintf(send, "1 %s", deg);
  Serial.println(send);
  Serial.write(send);
  delay(2000);
    
}

e in prima parte funziona, ma perchè sulla seriale dopo il primo numero il secondo si va a concatenare al prima e cosi anche gli altri?
1 314.78
1 314.781 314.78
1 314.781 228.44
1 228.441 314.88
1 314.881 301.34

questo era quello che volevo. ditemi se è corretto.
facendo la medesima cosa anche con il controllo del potenziometro, se sostituisco il valore 1 con 2 ed headingDegrees con VAL non dovrei più avere confusione sulla seriale, giusto?

Perché stai scrivendo due volte sulla seriale:

  Serial.println(send);
  Serial.write(send);

Se vuoi vedere i dati in colonna lascia solo la Serial.println() che aggiunge anche i terminatori di riga ('\r' e '\n') .

Si ma dipende anche da COSA vuoi ottenere e soprattutto da cos'è che deve poi "usare" questi dati. Se sulla seriale vuoi rappresentarli contemporaneamente è una cosa (e quindi devi sempre mandarli entrambi su una stessa riga, tipo "1; 314.00; 2; 85\r\n"), oppure se vuoi avere singole righe è un'altra (quindi ogni riga è composta da due parametri separati da ";", per cui avrai prima "1; 314.00\r\n" e poi "2; 85\r\n"). Se vuoi avere in grafico sul Serial plotter devi usare un'altra sintassi, e così via...

Ma, ripeto, la cosa migliore è prima capire chi/cosa deve ricevere questi dati, quale formato richiede, e cosa deve farne.

ok grazie mille

ho provato a collegare il tutto nello sketch principale, e come sempre, non funziona...
questo è il codice completo, in parte, perchè dovrei modificare l invio sulla seriale di VAL che riguarda il volume. Come posso faccio anche un disegno di tutti i collegamenti

#include "RTClib.h"
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>
 
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);

LiquidCrystal_I2C lcd(0x27,16,2);  

RTC_DS3231 rtc;
DateTime now;

char str[3];
int stato1 = 0;
int ledPin = 9;
int analogPin = 0;
int val = 0;

int rele1 = 10;
int rele2 = 11;
int val0=0;

void setup () {
  Serial.begin(9600);

  lcd.backlight();
  lcd.init();
  Wire.begin();
  //pulsanti set e reset
  pinMode(6, INPUT);
  pinMode(7, INPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(rele1, OUTPUT);
  pinMode(rele2, OUTPUT);
  digitalWrite(10,HIGH);
  digitalWrite(11,HIGH);

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while(true);
  }

  if (! rtc.lostPower()) {
    Serial.println("RTC is NOT running, let's set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    
  }

}

void loop () {
  ORA();
  bussola();
  char lettera = Serial.read();
  
   if (lettera == 'A')
{
  digitalWrite(rele1,LOW);
  delay (100);
  digitalWrite(rele1,HIGH);

}
if (lettera == 'O')
{
  digitalWrite(rele2,LOW);
  delay (100);
  digitalWrite(rele2,HIGH);
}

   int val = 0;
  // Somma tre letture consecutive
  for(int i=0; i<3; ++i)
    val += analogRead(A0);
  // Calcola la media
  val = val/3;
  // Riporta il valore tra 0 e 100
  val = map(val,0,1023,0,100);
  // Se il valore è cambiato
  if (val < val0-1 || val > val0+1) {
    // Manda il dato come singolo byte
    Serial.write(val);
    val0 = val;
  }
  delay(100);

}
void bussola(){
  sensors_event_t event;
  mag.getEvent(&event);
  float heading = atan2(event.magnetic.y, event.magnetic.x);
  float declinationAngle = 0.06;
  heading += declinationAngle;
  if (heading < 0) {
    heading += 2 * PI;
  }
  if (heading > 2 * PI) {
    heading -= 2 * PI;
  }
  float headingDegrees = heading * 180 / M_PI;
  char send[8];
    char deg[8];
  dtostrf(headingDegrees, 4, 2, deg);
  strcpy(send, "B ");
  sprintf(send, "B %s", deg);
  Serial.println(send);
  delay(500);
}
void ORA(){
switch(stato1){
    case 0:
      displayTime();
      break;  
    case 1:
      setHour();
      break;
    case 2:
      setMinute();
      break;
    case 3:
      setSecond();
      break;
    case 4:
      setyear();
      break;
    case 5:
      setmonth();
      break;  
    case 6:
      setday();
      break;  
    
  }   
}


unsigned long t1, dt1;
bool FIRST = true;

void displayTime() {
  if (FIRST) {
    lcd.clear();
    t1 = millis();
    FIRST = false;
  }        
  
  dt1 = millis() - t1;
  if (dt1 > 1000) {
    now = rtc.now();
    lcd.setCursor(4,1);  
    
    sprintf(str, "%02d", now.hour());
    lcd.print(str);
    lcd.print(':');
    sprintf(str, "%02d", now.minute());
    lcd.print(str);
    lcd.print(':');
    sprintf(str, "%02d", now.second());
    lcd.print(str); 
    
    lcd.setCursor(3,0);  
    
    sprintf(str, "%02d", now.day());
    lcd.print(str);
    lcd.print('/');
    sprintf(str, "%02d", now.month());
    lcd.print(str);
    lcd.print('/');
    sprintf(str, "%02d", now.year());
    lcd.print(str); 

    t1 = millis();    
  }

  if (digitalRead(6)) {
    //imposta ora
    stato1 = 1;  
    delay(300);
    FIRST = true;
  }
}

int seth = 0;

void setHour() {
  if (FIRST) {
    lcd.clear();
    now = rtc.now();
    seth = now.hour();
    lcd.setCursor(0,0);
    lcd.print("Imposta ora");      
    FIRST = false;
  }        
  lcd.setCursor(0,1);      
  sprintf(str, "%02d", seth);
  lcd.print(str);

  if (digitalRead(7)) {
    seth++;
    if (seth >= 24) seth = 0;
    delay(200);  
  }
  if (digitalRead(6)) {
    //salva ora scelta e passa ai minuti
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), seth, now.minute(), now.second()));
    stato1 = 2;
    FIRST = true;
    delay(200);
  }
}

int setm;
void setMinute(){
  if (FIRST) {
    lcd.clear();
    now = rtc.now();
    setm = now.minute();
    lcd.setCursor(0,0);
    lcd.print("Imposta minuti");      
    FIRST = false;
  }        
  lcd.setCursor(0,1);      
  sprintf(str, "%02d", setm);
  lcd.print(str);

  if (digitalRead(7)) {
    setm++;
    if (setm >= 60) setm = 0;
    delay(200);  
  }
  if (digitalRead(6)) {
    //salva min scelta e passa ai sec
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), setm, now.second()));
    stato1 = 3;
    delay(200);
    FIRST = true;
  }  
}

int sets = 0;

void setSecond(){
  if (FIRST) {
    lcd.clear();
    now = rtc.now();
    sets = now.second();
    lcd.setCursor(0,0);
    lcd.print("Imposta sec.");      
    FIRST = false;
  }        
  lcd.setCursor(0,1);      
  sprintf(str, "%02d", sets);
  lcd.print(str);

  if (digitalRead(7)) {
    sets++;
    if (sets >= 60) sets = 0;
    delay(200);  
  }
  if (digitalRead(6)) {
    //salva sec scelta e passa ai minuti
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), sets));
    stato1 = 4;
    delay(200);
    FIRST = true;
  }  
}

int sety = 0;

void setyear(){
  if (FIRST) {
    lcd.clear();
    now = rtc.now();
    sety = now.year();
    lcd.setCursor(0,0);
    lcd.print("Imposta anno");      
    FIRST = false;
  }        
  lcd.setCursor(0,1);      
  sprintf(str, "%02d", sety);
  lcd.print(str);

  if (digitalRead(7)) {
    sety++;
    if (sety >= 9999) sety = 0;
    delay(200);  
  }
  if (digitalRead(6)) {
    //salva sec scelta e passa ai minuti
    rtc.adjust(DateTime(sety, now.month(), now.day(), now.hour(), now.minute(), now.second()));
    stato1 = 5;
    delay(200);
    FIRST = true;
  }  
}

int setmo = 0;

void setmonth(){
  if (FIRST) {
    lcd.clear();
    now = rtc.now();
    setmo = now.month();
    lcd.setCursor(0,0);
    lcd.print("Imposta mese");      
    FIRST = false;
  }        
  lcd.setCursor(0,1);      
  sprintf(str, "%02d", setmo);
  lcd.print(str);

  if (digitalRead(7)) {
    setmo++;
    if (setmo >= 12) setmo = 0;
    delay(200);  
  }
  if (digitalRead(6)) {
    //salva sec scelta e passa ai minuti
    rtc.adjust(DateTime(now.year(), setmo, now.day(), now.hour(), now.minute(), now.second()));
    stato1 = 6;
    delay(200);
    FIRST = true;
  }  
}

int setd = 0;

void setday(){
  if (FIRST) {
    lcd.clear();
    now = rtc.now();
    setd = now.day();
    lcd.setCursor(0,0);
    lcd.print("Imposta giorno");      
    FIRST = false;
  }        
  lcd.setCursor(0,1);      
  sprintf(str, "%02d", setd);
  lcd.print(str);

  if (digitalRead(7)) {
    setd++;
    if (setd >= 32) setd = 0;
    delay(200);  
  }
  if (digitalRead(6)) {
    //salva sec scelta e passa ai minuti
    rtc.adjust(DateTime(now.year(), now.month(), setd, now.hour(), now.minute(), now.second()));
    stato1 = 0;
    delay(200);
    FIRST = true;
  }  
}



Primo, indenta decentemente (in prima battuta puoi farlo fare all'IDE stesso premendo Ctrl-T). Non è una questione "estetica" ma oltre a rendere più facilmente leggibile il codice (per te ma anche per noi) serve anche per poter vedere eventuali problemi di strutture non corrette (es. un "else" che finisce nella "if()" sbagliata..).

Secondo, dire "non funziona" non aiuta. O dici in che senso "non funziona", spiegando cosa fa o non fa e cosa dovrebbe fare (e magari anche l'output della seriale), oppure per noi, per cercare di capire "non funziona" significa andare a scaricare il tuo codice e provarlo e puoi immaginare che non sia così "immediato".

Terzo, scrivi sempre dei commenti per descrivere lo scopo delle principali variabili e delle funzioni. Anche questo serve sia a te (perché se riprenderai un codice scritto un anno prima non è detto che tu possa ricordare subito tutto) sia a noi (per capire lo scopo di alcune cose).

Per finire quindi, da un'occhiata rapida del codice mi sembra che tu non abbia definito come usare la seriale: parlando in generale, se deve essere un programma ad usarla devi definire quale sia il formato del tuo "protocollo di comunicazione" ed evitare di mandare testi "informativi" che potrebbero dare fastidio al ricevente, se deve essere una comunicazione "umana" ossia usata tramite Serial monitor, allora puoi fare tutto ciò che risulti "leggibile".
Dico questo perché ad esempio nel tuo programma hai previsto attualmente sia l'invio di dati binari:

  if (val < val0-1 || val > val0+1) {
    // Manda il dato come singolo byte
    Serial.write(val);
    val0 = val;
  }

sia l'oggetto dell'attuale discussione, ossia i dati del magnetometro e relativo identificativo del "canale":

  dtostrf(headingDegrees, 4, 2, deg);
  sprintf(send, "B %s", deg);
  Serial.println(send);
  delay(500);

(Nota: la riga "strcpy(send, "B ");" era un mio refuso del precedente messaggio quindi l'ho rimossa, ma non influisce nel risultato)
In questo caso quindi sulla seriale hai sia dei singoli byte inviati (byte, non valori decimali scritti in chiaro, quindi ad esempio se mandi il byte con valore 11 sulla seriale non vedrai "11" ma solo un punto interrogativo!), sia una riga formattata in quel modo ossia una stringa con "B" (che immagino sia il "nuovo" identificativo del canale, ossia del dato) poi uno spazio, poi il valore in gradi, ed infine il terminatore di riga "\r\n".
Quindi la prima domanda che ti pongo è: questi dati in uscita sulla seriale chi dovrà riceverli? Se è un programma, allora devi prima definire il formato del tuo protocollo (che potrà essere binario o sotto forma di stringa, quello che risulterà più utile al ricevente), poi potrai iniziare a ragionare su come scrivere i dati. Se invece è (almeno per ora) destinato alla visualizzazione "umana" allora evita di scrivere byte ed usa solo stringhe, e con righe separate (println).

Poi comunque vorrei farti notare una cosa: dato che "headingDegrees" è un dato relativo ad una direzione in gradi, saprai immagino/spero quante cifre e quindi quanti caratteri occorrano, no? Saranno 3 cifre intere (0-359), più la virgola, e poi diciamo 2 decimali. Quanti caratteri sono in totale? Che valore hai messo nella dtostrf() (della quale spero tu abbia letto il funzionamento e los copo dei parametri)?

Aggiungo ... studiati gli esempi e guarda bene cosa occorre fare per farlo funzionare ... manca il metodo begin() nel setup() !

Inoltre la Wire.begin() va messa prima di qualsiasi altra cosa che usi il bus I2C ... spostala all'inizio del setup().

Guglielmo

sembrerebbe che adesso il tutto, funzioni. il mio non funziona lo so era un po generico, ma voleva significare che sul serial monitor non compariva niente, adesso ho messo un po di descrizioni cosi da poter capire qualcosa, ed ho inizializato il magnetometro che prima era sfuggito dal copia e in colla, e sistemato anche l identificativo del magnetometro da inviare a visual basic. Cmq arduino sarà collegato tramite pc, ad un programma di visual basic, alla quale servirà x:

  • blocca e sbloccare le portiere dell auto
  • controllare il volume del player tramite un potenziometro
  • indicare la direzione di marcia treamite il magnetometro
  • vedere la data e l orario tramite un lcd 16x2
    questo è il codice:
#include "RTClib.h"
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>
 
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345); //identifiacativo magnetometro

LiquidCrystal_I2C lcd(0x27,16,2);  //display lcd x la visualizzazione dell orario

RTC_DS3231 rtc;  
DateTime now;

char str[3];
int stato1 = 0;
int ledPin = 9;
int analogPin = 0;
int val = 0;

int rele1 = 10; //relè x sbloccare porta
int rele2 = 11; //rele x bloccare porta
int val0=0;

void setup () {
   Wire.begin();
  Serial.begin(9600);

  lcd.backlight();
  lcd.init();
 
  //pulsanti set e reset
  pinMode(6, INPUT);
  pinMode(7, INPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(rele1, OUTPUT);
  pinMode(rele2, OUTPUT);
  digitalWrite(10,HIGH);
  digitalWrite(11,HIGH);

  if (! rtc.begin()) {                         //inizializzo l rtc
    Serial.println("Couldn't find RTC");
    while(true);
  }

  if (! rtc.lostPower()) {
    Serial.println("RTC is NOT running, let's set the time!");     
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    
  }
  if(!mag.begin())  //inizializzo il magnetometro
  {
    /* There was a problem detecting the HMC5883 ... check your connections */
    Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
    while(1);
  }

}

void loop () {
  ORA();
  bussola();
  char lettera = Serial.read();
  
   if (lettera == 'A')
{
  digitalWrite(rele1,LOW);
  delay (100);                       // attivo o disattivo i rele in base al comando ricevuto da visual basic
  digitalWrite(rele1,HIGH);

}
if (lettera == 'O')
{
  digitalWrite(rele2,LOW);
  delay (100);
  digitalWrite(rele2,HIGH);
}

   int val = 0;
  // Somma tre letture consecutive
  for(int i=0; i<3; ++i)
    val += analogRead(A0);
  // Calcola la media
  val = val/3;
  // Riporta il valore tra 0 e 100            //calcolo la media dei valori del potenziometro x il controllo del volume e li invio tramite la seriale, 
  val = map(val,0,1023,0,100);                 // visualbasic cosi regolerà il volume del player
  // Se il valore è cambiato
  if (val < val0-1 || val > val0+1) {
    // Manda il dato come singolo byte
    Serial.write(val);
    val0 = val;
  }
  delay(100);

}
void bussola(){                                    //evento bussola per indicare i gradi del magnetometro, 
  sensors_event_t event;
  mag.getEvent(&event);
  float heading = atan2(event.magnetic.y, event.magnetic.x);
  float declinationAngle = 0.06;
  heading += declinationAngle;
  if (heading < 0) {
    heading += 2 * PI;
  }
  if (heading > 2 * PI) {
    heading -= 2 * PI;
  }
  float headingDegrees = heading * 180 / M_PI;
  char send[8];
    char deg[8];
  dtostrf(headingDegrees, 4, 2, deg);
  strcpy(send, "B;");
  sprintf(send, "B;%s", deg);
  Serial.println(send);
  delay(500);
}
void ORA(){
switch(stato1){
    case 0:
      displayTime();
      break;  
    case 1:
      setHour();
      break;
    case 2:
      setMinute();
      break;
    case 3:
      setSecond();
      break;
    case 4:
      setyear();
      break;
    case 5:
      setmonth();
      break;  
    case 6:
      setday();
      break;  
    
  }   
}


unsigned long t1, dt1;
bool FIRST = true;

void displayTime() {
  if (FIRST) {
    lcd.clear();
    t1 = millis();
    FIRST = false;
  }        
  
  dt1 = millis() - t1;
  if (dt1 > 1000) {
    now = rtc.now();
    lcd.setCursor(4,1);  
    
    sprintf(str, "%02d", now.hour());
    lcd.print(str);
    lcd.print(':');
    sprintf(str, "%02d", now.minute());
    lcd.print(str);
    lcd.print(':');
    sprintf(str, "%02d", now.second());
    lcd.print(str); 
    
    lcd.setCursor(3,0);  
    
    sprintf(str, "%02d", now.day());
    lcd.print(str);
    lcd.print('/');
    sprintf(str, "%02d", now.month());
    lcd.print(str);
    lcd.print('/');
    sprintf(str, "%02d", now.year());
    lcd.print(str); 

    t1 = millis();    
  }

  if (digitalRead(6)) {
    //imposta ora
    stato1 = 1;  
    delay(300);
    FIRST = true;
  }
}

int seth = 0;

void setHour() {
  if (FIRST) {
    lcd.clear();
    now = rtc.now();
    seth = now.hour();
    lcd.setCursor(0,0);
    lcd.print("Imposta ora");      
    FIRST = false;
  }        
  lcd.setCursor(0,1);      
  sprintf(str, "%02d", seth);
  lcd.print(str);

  if (digitalRead(7)) {
    seth++;
    if (seth >= 24) seth = 0;
    delay(200);  
  }
  if (digitalRead(6)) {
    //salva ora scelta e passa ai minuti
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), seth, now.minute(), now.second()));
    stato1 = 2;
    FIRST = true;
    delay(200);
  }
}

int setm;
void setMinute(){
  if (FIRST) {
    lcd.clear();
    now = rtc.now();
    setm = now.minute();
    lcd.setCursor(0,0);
    lcd.print("Imposta minuti");      
    FIRST = false;
  }        
  lcd.setCursor(0,1);      
  sprintf(str, "%02d", setm);
  lcd.print(str);

  if (digitalRead(7)) {
    setm++;
    if (setm >= 60) setm = 0;
    delay(200);  
  }
  if (digitalRead(6)) {
    //salva min scelta e passa ai sec
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), setm, now.second()));
    stato1 = 3;
    delay(200);
    FIRST = true;
  }  
}

int sets = 0;

void setSecond(){
  if (FIRST) {
    lcd.clear();
    now = rtc.now();
    sets = now.second();
    lcd.setCursor(0,0);
    lcd.print("Imposta sec.");      
    FIRST = false;
  }        
  lcd.setCursor(0,1);      
  sprintf(str, "%02d", sets);
  lcd.print(str);

  if (digitalRead(7)) {
    sets++;
    if (sets >= 60) sets = 0;
    delay(200);  
  }
  if (digitalRead(6)) {
    //salva sec scelta e passa ai minuti
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), sets));
    stato1 = 4;
    delay(200);
    FIRST = true;
  }  
}

int sety = 0;

void setyear(){
  if (FIRST) {
    lcd.clear();
    now = rtc.now();
    sety = now.year();
    lcd.setCursor(0,0);
    lcd.print("Imposta anno");      
    FIRST = false;
  }        
  lcd.setCursor(0,1);      
  sprintf(str, "%02d", sety);
  lcd.print(str);

  if (digitalRead(7)) {
    sety++;
    if (sety >= 9999) sety = 0;
    delay(200);  
  }
  if (digitalRead(6)) {
    //salva sec scelta e passa ai minuti
    rtc.adjust(DateTime(sety, now.month(), now.day(), now.hour(), now.minute(), now.second()));
    stato1 = 5;
    delay(200);
    FIRST = true;
  }  
}

int setmo = 0;

void setmonth(){
  if (FIRST) {
    lcd.clear();
    now = rtc.now();
    setmo = now.month();
    lcd.setCursor(0,0);
    lcd.print("Imposta mese");      
    FIRST = false;
  }        
  lcd.setCursor(0,1);      
  sprintf(str, "%02d", setmo);
  lcd.print(str);

  if (digitalRead(7)) {
    setmo++;
    if (setmo >= 12) setmo = 0;
    delay(200);  
  }
  if (digitalRead(6)) {
    //salva sec scelta e passa ai minuti
    rtc.adjust(DateTime(now.year(), setmo, now.day(), now.hour(), now.minute(), now.second()));
    stato1 = 6;
    delay(200);
    FIRST = true;
  }  
}

int setd = 0;

void setday(){
  if (FIRST) {
    lcd.clear();
    now = rtc.now();
    setd = now.day();
    lcd.setCursor(0,0);
    lcd.print("Imposta giorno");      
    FIRST = false;
  }        
  lcd.setCursor(0,1);      
  sprintf(str, "%02d", setd);
  lcd.print(str);

  if (digitalRead(7)) {
    setd++;
    if (setd >= 32) setd = 0;
    delay(200);  
  }
  if (digitalRead(6)) {
    //salva sec scelta e passa ai minuti
    rtc.adjust(DateTime(now.year(), now.month(), setd, now.hour(), now.minute(), now.second()));
    stato1 = 0;
    delay(200);
    FIRST = true;
  }  
}



Ok, sembra andare meglio (anche se non hai fatto il Ctrl-T per indentare correttamente tutto il codice). Però se sarà un programma a gestire la comunicazione, dovresti per prima cosa togliere di mezzo tutti i "Serial.print()" che non rispettano il formato del protocollo (es. "Serial.println("Couldn't find RTC");" eccetera). Inoltre ti consiglio di definire come variabile globale il buffer che usi per comporre le risposte, e dimensionato per poter contenere la stringa più lunga prevista (ossia metti in testa al programma un "char outBuf[20]" ad esempio per definire un buffer stringa di massimo 19 caratteri).

Poi hai ancora quella "Serial.write()" di un singolo byte che non ha alcun senso: se le informazioni devono andare via seriale nel formato diciamo "CV\r\n" dove "C" è il codice del parametro e "V" il relativo valore (come stringa decimale ad esempio), devi definire quindi quali sono i codici parametri e rispettare SEMPRE quel formato. Non penso sia necessario aggiungere un separatore come il punto e virgola (e neanche lo spazio) tra comando e valore, rende più semplice l'interpretazione lato VB, ma se preferisci puoi usare un punto e virgola.

Quindi per la lettura del potenziometro ad esempio non fare:

    // Manda il dato come singolo byte
    Serial.write(val);

bensì qualcosa tipo:

    sprintf(outBuf, "P;%d", val);
    Serial.println(outBuf);

Una cosa analoga devi farlo anche per i comandi che dal programma VB devi mandare al codice, devi definire cosa possa mandare ed il relativo formato e significato. Tutto questo si chiama "definire un protocollo di comunicazione".

Poi per la lettura dalla seriale prima di tentare di leggere verifica se "Serial.available()" restituisce un valore maggiore di zero, ed in tal caso leggi il dato. Se nel protocollo prevedi un terminatore di riga (i caratteri '\r' e '\n', che puoi impostare anche in VB) come per i dati che mandi da Arduino, dovresti accumulare in un buffer tutti i caratteri ricevuti fino a che non ricevi '\n' (nel frattempo ignorando il classico CR ossia '\r') quando potrai interpretare la stringa che hai ricevuto. Se, come in questo caso, si limita ad un solo carattere, puoi anche fare una cosa più semplice, ma se ad esempio da VB devi mandare un comando con un parametro ti serve comunque qualcosa di più strutturato, ma lo vedrai in seguito.

Poi, in generale, metti all'inizio del codice le costanti con la "configurazione" (tipicamente i pin, da definire come "const byte"), e con identificatori sempre tutti in maiuscolo (per distinguere le variabili dalle costanti).
E per finire (per ora :wink: ) sappi che non è necesasrio chiamare "sprintf()" per ogni elemento da inviare, ad esempio per l'orario puoi fare tutto con una sola istruzione:

    sprintf(outBuf, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());

Quindi vedi questa versione (cerca di capire cosa ho modificato) e provala, io non l'ho provata, ma fallo tu e se funziona puoi partire da questa:

#include "RTClib.h"
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>

// CONFIGURAZIONE
const byte P_LED = 9;
const byte P_SET = 6;
const byte P_RESET = 7;
const byte P_RELE_APRE = 10;    //relè x sbloccare porta
const byte P_RELE_BLOCCA = 11;  //rele x bloccare porta

Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);  //identifiacativo magnetometro

LiquidCrystal_I2C lcd(0x27, 16, 2);  //display lcd x la visualizzazione dell orario

RTC_DS3231 rtc;
DateTime now;

char str[3];
int stato1 = 0;
int val = 0;

int val0 = 0;

// Buffer per la composizione delle stringhe da inviare via seriale
char outBuf[20];

void setup() {
  Wire.begin();
  Serial.begin(9600);

  lcd.backlight();
  lcd.init();

  //pulsanti set e reset
  pinMode(P_SET, INPUT);
  pinMode(P_RESET, INPUT);
  pinMode(P_LED, OUTPUT);
  pinMode(P_RELE_APRE, OUTPUT);
  pinMode(P_RELE_BLOCCA, OUTPUT);
  digitalWrite(P_RELE_APRE, HIGH);
  digitalWrite(P_RELE_BLOCCA, HIGH);

  if (!rtc.begin()) {  //inizializzo l rtc
    Serial.println("Couldn't find RTC");
    while (true)
      ;
  }

  if (!rtc.lostPower()) {
    //Serial.println("RTC is NOT running, let's set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  if (!mag.begin())  //inizializzo il magnetometro
  {
    /* There was a problem detecting the HMC5883 ... check your connections */
    Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
    while (1)
      ;
  }
}

void loop() {
  MostraOra();
  bussola();
  if (Serial.available()) {
    // Gestione comandi da VB
    // Per ora leggo solo un singolo carattere per volta
    char comando = Serial.read();
    switch (comando) {
      case 'A':
        digitalWrite(P_RELE_APRE, LOW);
        delay(100);  // attivo o disattivo i rele in base al comando ricevuto da visual basic
        digitalWrite(P_RELE_APRE, HIGH);
        break;
      case 'O':
        digitalWrite(P_RELE_BLOCCA, LOW);
        delay(100);
        digitalWrite(P_RELE_BLOCCA, HIGH);
        break;
      default:
        // Ignoro qualsiasi altro carattere
        break;
    }
  }
  int val = 0;
  // Somma tre letture consecutive
  for (int i = 0; i < 3; ++i)
    val += analogRead(A0);
  // Calcola la media
  val = val / 3;
  // Riporta il valore tra 0 e 100            //calcolo la media dei valori del potenziometro x il controllo del volume e li invio tramite la seriale,
  val = map(val, 0, 1023, 0, 100);  // visualbasic cosi regolerà il volume del player
  // Se il valore è cambiato
  if (val < val0 - 1 || val > val0 + 1)  {
    // Manda il dato ("P" = potenziometro)
    sprintf(outBuf, "P;%d", val);
    Serial.println(outBuf);
    val0 = val;
  }
  delay(100);
}

void bussola() {  //evento bussola per indicare i gradi del magnetometro,
  sensors_event_t event;
  mag.getEvent(&event);
  float heading = atan2(event.magnetic.y, event.magnetic.x);
  float declinationAngle = 0.06;
  heading += declinationAngle;
  if (heading < 0) {
    heading += 2 * PI;
  }
  if (heading > 2 * PI) {
    heading -= 2 * PI;
  }
  float headingDegrees = heading * 180 / M_PI;
  // Manda la lettura ("B"=bussola)
  char deg[8];
  // Gradi: ###.## quindi 6 caraatteri totali, inclusi 2 decimali)
  dtostrf(headingDegrees, 6, 2, deg);
  sprintf(outBuf, "B;%s", deg);
  Serial.println(outBuf);
  delay(500);
}

void MostraOra() {
  switch (stato1) {
    case 0:
      displayTime();
      break;
    case 1:
      setHour();
      break;
    case 2:
      setMinute();
      break;
    case 3:
      setSecond();
      break;
    case 4:
      setyear();
      break;
    case 5:
      setmonth();
      break;
    case 6:
      setday();
      break;
  }
}


unsigned long t1, dt1;
bool isFirst = true;

void displayTime() {
  if (isFirst) {
    lcd.clear();
    t1 = millis();
    isFirst = false;
  }

  dt1 = millis() - t1;
  if (dt1 > 1000) {
    now = rtc.now();
    lcd.setCursor(4, 1);
    sprintf(str, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
    lcd.print(str);
    lcd.setCursor(3, 0);
    sprintf(str, "%02d/%02d/%02d", now.day(), now.month(), now.year());
    lcd.print(str);
    t1 = millis();
  }

  if (digitalRead(P_SET)) {
    //imposta ora
    stato1 = 1;
    delay(300);
    isFirst = true;
  }
}

int seth = 0;

void setHour() {
  if (isFirst) {
    lcd.clear();
    now = rtc.now();
    seth = now.hour();
    lcd.setCursor(0, 0);
    lcd.print("Imposta ora");
    isFirst = false;
  }
  lcd.setCursor(0, 1);
  sprintf(str, "%02d", seth);
  lcd.print(str);

  if (digitalRead(P_RESET)) {
    seth++;
    if (seth >= 24) seth = 0;
    delay(200);
  }
  if (digitalRead(P_SET)) {
    //salva ora scelta e passa ai minuti
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), seth, now.minute(), now.second()));
    stato1 = 2;
    isFirst = true;
    delay(200);
  }
}

int setm;
void setMinute() {
  if (isFirst) {
    lcd.clear();
    now = rtc.now();
    setm = now.minute();
    lcd.setCursor(0, 0);
    lcd.print("Imposta minuti");
    isFirst = false;
  }
  lcd.setCursor(0, 1);
  sprintf(str, "%02d", setm);
  lcd.print(str);

  if (digitalRead(P_RESET)) {
    setm++;
    if (setm >= 60) setm = 0;
    delay(200);
  }
  if (digitalRead(P_SET)) {
    //salva min scelta e passa ai sec
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), setm, now.second()));
    stato1 = 3;
    delay(200);
    isFirst = true;
  }
}

int sets = 0;
void setSecond() {
  if (isFirst) {
    lcd.clear();
    now = rtc.now();
    sets = now.second();
    lcd.setCursor(0, 0);
    lcd.print("Imposta sec.");
    isFirst = false;
  }
  lcd.setCursor(0, 1);
  sprintf(str, "%02d", sets);
  lcd.print(str);

  if (digitalRead(P_RESET)) {
    sets++;
    if (sets >= 60) sets = 0;
    delay(200);
  }
  if (digitalRead(P_SET)) {
    //salva sec scelta e passa ai minuti
    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), sets));
    stato1 = 4;
    delay(200);
    isFirst = true;
  }
}

int sety = 0;
void setyear() {
  if (isFirst) {
    lcd.clear();
    now = rtc.now();
    sety = now.year();
    lcd.setCursor(0, 0);
    lcd.print("Imposta anno");
    isFirst = false;
  }
  lcd.setCursor(0, 1);
  sprintf(str, "%02d", sety);
  lcd.print(str);

  if (digitalRead(P_RESET)) {
    sety++;
    if (sety >= 9999) sety = 0;
    delay(200);
  }
  if (digitalRead(P_SET)) {
    //salva sec scelta e passa ai minuti
    rtc.adjust(DateTime(sety, now.month(), now.day(), now.hour(), now.minute(), now.second()));
    stato1 = 5;
    delay(200);
    isFirst = true;
  }
}

int setmo = 0;
void setmonth() {
  if (isFirst) {
    lcd.clear();
    now = rtc.now();
    setmo = now.month();
    lcd.setCursor(0, 0);
    lcd.print("Imposta mese");
    isFirst = false;
  }
  lcd.setCursor(0, 1);
  sprintf(str, "%02d", setmo);
  lcd.print(str);

  if (digitalRead(P_RESET)) {
    setmo++;
    if (setmo >= 12) setmo = 0;
    delay(200);
  }
  if (digitalRead(P_SET)) {
    //salva sec scelta e passa ai minuti
    rtc.adjust(DateTime(now.year(), setmo, now.day(), now.hour(), now.minute(), now.second()));
    stato1 = 6;
    delay(200);
    isFirst = true;
  }
}

int setd = 0;
void setday() {
  if (isFirst) {
    lcd.clear();
    now = rtc.now();
    setd = now.day();
    lcd.setCursor(0, 0);
    lcd.print("Imposta giorno");
    isFirst = false;
  }
  lcd.setCursor(0, 1);
  sprintf(str, "%02d", setd);
  lcd.print(str);

  if (digitalRead(P_RESET)) {
    setd++;
    if (setd >= 32) setd = 0;
    delay(200);
  }
  if (digitalRead(P_SET)) {
    //salva sec scelta e passa ai minuti
    rtc.adjust(DateTime(now.year(), now.month(), setd, now.hour(), now.minute(), now.second()));
    stato1 = 0;
    delay(200);
    isFirst = true;
  }
}

Grazie mille, stasera o domani provo il tutto. Molto chiare nella spiegazione, ti ringrazio molto

grazie mille, il tutto funziona, soltanto l orologio rimane inchiodato e non si muove, ho copiato il mio codice e funziona. sul monitor seriale adesso ho sia la P x il potenziometro, sia la B x le coordinate della bussola, adesso devo vedere perchè sul mio programma in visual basi la viaribile che dovrebbe conere i dati riceventi dalla seriale di ardunio è nulla.
grazie ancora

questo è quello che leggo dal serial monitor

P;52
B;300.90
B;301.02
B;301.06
B;300.86
B;301.26
B;300.98
B;300.90
B;301.10
B;301.14
B;301.10