Convertire variabile in nome

La versione di @Datman è basata più o meno sullo stesso principio e potrebbe impegnare un po' meno RAM. Dovresti però riorganizzarla come funzione visto che usi questa "ricerca" in due punti distinti del software.

Io però ho provato a compilare il tuo sketch (con la funzione trovaIndice()) ed ottengo questo risultato

C'è il warning sulla RAM, ma considerando che non fai moltissima allocazione dinamica di oggetti che potrebbero usare molta RAM (tipo variabili String) e sono quasi tutte variabili globali, secondo me puoi tranquillamente ignorare il warning e vivere sereno.

Se la libreria dell'oled e quella della SD supportano la macro F(), puoi risparmiare qualche byte di memoria con le istruzioni dove usi delle "literal string" che finiscono inutilmente in RAM (ovviamente il risparmio va a discapito dell'occupazione memoria in flash).
Ad esempio:

data.println("Da Hr Tm La Lo At Km Di Ve He Co Sa" );
può diventare e risparmiare 36 byte
data.println(F("Da Hr Tm La Lo At Km Di Ve He Co Sa"));

oppure
oled.println("TUO NOME GPS");
diventa e sono altry 12/13 byte di RAM recuperati
oled.println(F("TUO NOME GPS"));
1 Like

Piuttosto che usare i float, se proprio ti servono i mezzi gradi, usa gli int moltiplicando i gradi per 10! Esempio:

byte dir=(head+225)/450;
if(dir==8) dir=0;

Vedo che nel tuo codice c'è già:

int trovaIndice(float angolo)
{
  int _index;
  for (_index=0; _index<8; _index++)
  {
    float angoloDirezione = _index*45.0+22.50;
    float angoloMin = angoloDirezione-22.50;
    float angoloMax = angoloDirezione+22.50;
    if (angolo>=angoloMin && angolo<=angoloMax)
      return _index;
  }
  return _index;
}

Con angolo da 0 a 3599 anziché da 0 a 359,9 diventerebbe:

byte trovaIndice(int angolo)
{
  return (angolo+225)/450.0+0.5; // .0 e +0.5, se vuoi, sono per l'arrotondamento.
}

ma a quel punto puoi usare direttamente la formula, senza metterla in una funzione.

Visto che una buona soluzione è stata pubblicata mi permetto di mostrare cosa avevo pensato io

per prima cosa uso di una funzione, certamente,
funzione che deve restituire una stringa (ovvero char *)
ecco il suo prototipo

char * direzione(int bussola);

poi ho pensato di renderla parametrica e di usare una tabella (array) locale
e quindi un array di stringhe (puntatore a carattere)

char * nomi[]={};

e naturalmente tra le parentesi graffe ci va l'elencazione dei punti cardinali, compresi tra virgolette (essendo singole stringhe) e separati da virgole (essendo un'elencazione di elementi di un array)

poi ho pensati di renderla parametrica, calcolando il "passo di cambio" dei punti cardinali passando per due "sizeof"

e serve comunque di tenere in conto che il Nord comincia "prima" dell'angolo "zero", quindi serve di calcolare un offset da aggiungere, offset che sarà, banalmente, pari a mezzo passo

il tutto deve essere calcolato in aritmetica a virgola mobile, per tenere conto che se si volessero più dei classici e canonici 4 punti cardinali serve tenere conto di angoli minori o uguali a 22 gradi e mezzo

ecco quindi che servirà:
un giochino con un operatore modulo 360
un calcolo del passo
un calcolo dell'offset
una ulteriore operazione di modulo, ma stavolta eseguita in aritmetica float

e usare il valore così ottenuto come indice che indica il corretto elemento nell'array dei puntatori a carattere
puntatore a carattere che sarà oggetto del return della funzione
naturalmente i valori che non cambiano da una esecuzione e la successiva devono essere "static" per non dover ricalcolare tutte le volte

ecco quindi la funzione fatta e finita

char * direzione(int bussola)
{
#ifndef CARDINALI
#define CARDINALI "N","NE","E","SE","S","SW","W","NW"
#endif
   static char * nomi[] = {CARDINALI};
   bussola = bussola % 360;
   static int n = sizeof nomi / sizeof nomi[0];
   static float passo = 360 / n;
   static float offset = passo / 2;
   float grezzo = (bussola + offset);

   if (grezzo > 360)
   {
      grezzo = grezzo - 360;
   }

   return nomi[(int)(grezzo / passo)];
}

e un suo programma di prova minimale

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

   for (int i = 0; i < 360; i += 10)
   {
      Serial.print(i);
      Serial.print("  ");
      Serial.println(direzione(i));
   }
}

void loop(void)
{
   // nulla da fare
}

provare per credere

e già che ci siete, dato che la funzione è parametrica basta aggiungere in testa al programma questa riga:

#define CARDINALI "Settentrione","Oriente","Mezzogiorno","Occidente"

per avere una deliziosa sorpresa

naturalmente si possono usare tutte le direzioni che si desidera, anche non multiple di quattro

1 Like

Grazie per il cuoricino

stavo pensando di aggiungere anche il "mezzo passo":
fare in modo che scriva

Nord -> Nord-Est -> Est -> Sud-Est

purtroppo non funziona, scriverebbe Est-Sud invece di Sud-Est

Amen, non debbo strafare

grazie. potrei provare a scrivere char* con 16 settori e magari potrei scrivere " N ", "NNE", "NE ", "ENE", " E " anche se in realtà per usarlo nella pratica non serve, come didattica è di certo interessante.
i gradi dovrei dividerli in 11,25° e penso che potrebbe funzionare. (?!?)

programmare non è il mio hobby, a volte mi scordo anche di cose che conoscevo già come la funzione macro, troppo spesso mi capita di perdermi in un bicchier d'acqua.
proverò a farlo sulla soluzione proposta da cotestatnt che al momento è anche quella che alla fine ho capito meglio.
vi ringrazio, è molto interessante per me poter studiare da questi esempi.
ho testato la versione di cotestatnt e funziona perfettamente, c'era solo il solito errore del secondo carattere ripetuto che ho modificato inserendo lo spazio.
adesso mi tolgo lo sfizio di vedere se si può fare anche con 16 settori, se entra e se funziona.

edit//
nulla da fare con questa memoria, ho provato a semplificare scrivendo solo 2 caratteri strBussola[16] = {"N ", "Ne", "NE", "En", "E ", "Es", "SE", "Se", "S ", "So", "SO", "Os", "O ", "On", "NO", "No"};
penso che posso accontentarmi di una versione funzionante a 8 settori.
anche sulla prima versione con la cascata di IF sono certo che il risultato sarebbe lo stesso, forse anche peggio.

questo il codice sistemato. controllato adesso in tutte le direzioni e funziona bene.
ho sostituito Co con He in quanto credo sia più corretto considerare la bussola i gradi e heading la direzione verso il punto cardinale.

ho notato però che questa versione ci potrebbe mettere un pochino più tempo per riportare il dato heading/compass la catena di IF mi sembra più veloce con un dato un filo più brioso che viene aggiornato più spesso.
non so perchè alcuno dati sono istantanei mentre altri possono rimanere invariati anche per minuti.. tipo l'altitudine.. si aggiorna davvero di rado e spesso è sbagliata. l'errore lo imputo al fix 3d ma non cpaisco perchè debba aggiornarsi anche dopo 10 minuti dove magari ho percorso un dislivello.
avrò modo di confrontare la velocità die dati bussola anche perchè dipende molto dal meteo, se provo il gps di notte col cielo stellato funziona sempre meglio.

#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include <NMEAGPS.h>
#include <NeoSWSerial.h>

#define I2C_ADDRESS 0x3C
#define RST_PIN -1
#define MAX_CHARS 24
#define RX_pin 5
#define TX_pin 4
#define SHOW_INTERVAL 1
#define GPS_baud 9600
SSD1306AsciiWire oled;
NMEAGPS gps;
gps_fix fix;
float odo, Speed, alt, Dist, head, maxs = 0;
unsigned int  Sat = 0;
unsigned long tmp, var = 0;
int secondi, minuti = 0, ore = 0, fct = 2, fcl = 1;
NeoGPS::Location_t    lastLoc, base;
bool stScan = true, lastLocOK = false;
bool altOK = false;
bool Satok = false;
static NeoSWSerial gpsPort(RX_pin, TX_pin);
static constexpr int INITIAL_SHOW  = (2 * SHOW_INTERVAL) - 1;
int show          = INITIAL_SHOW;
const int   LED_PIN  = 3;
const float SPEED_LIMIT = 0.1; // speed limit value

void disp(int c, int r) {
  oled.clear();
  oled.setCursor(c, r);
}

const char* strBussola[8] = {"N ", "NE", "E ", "SE", "S ", "SO", "O ", "NO"};
int index;

int trovaIndice(float angolo) {
  int _index;
  for (_index = 0; _index < 8; _index++) {
    float angoloDirezione = _index * 45.0 + 22.50;
    float angoloMin = angoloDirezione - 22.50;
    float angoloMax = angoloDirezione + 22.50;
    if (angolo >= angoloMin && angolo <= angoloMax)
      return _index;
  }
  return _index;
}

void setup() {
  pinMode (LED_PIN, OUTPUT);
  Serial.begin(9600);
  gpsPort.begin(GPS_baud);
  Wire.begin();
  oled.begin(&SH1106_128x64, I2C_ADDRESS);
  oled.setFont(ZevvPeep8x16);
  oled.displayRemap(true); // inversione dello schermo display
  disp(15, 2);
  oled.println("tuo.nome GPS");
  delay(2000);
  oled.clear();
  const int cs_sd = 2;
  if (!SD.begin (cs_sd)) {
    oled.clear();
    disp(50, 2);
    oled.print("NO SD");
    delay(10000);
    oled.clear();
  }
  File data = SD.open("L.csv", FILE_WRITE);
  data.println("");
  data.println(F("Da Hr Tm La Lo At Km Di Ve Co He Sa" ));
  data.close();
}

void loop() {
  tmp = (millis() / 1000);
  secondi = tmp - (var * 10) + fct;
  if (secondi > 60) {
    secondi = secondi - 60;
  }
  if (secondi == 60) {
    minuti = minuti + 1;
    var = var + 6;
  }
  if (((minuti == 10) || (minuti == 20) || (minuti == 30) || (minuti == 40) || (minuti == 50) || (minuti == 60)) && (secondi == 1))   {
    fct = fct + fcl;
  }
  if (minuti >= 60) {
    minuti = 0;
    ore = ore + 1;
  }
  if (ore >= 24) {
    ore = 0;
  }
  if (gps.available( gpsPort )) {
    gps_fix fix = gps.read();
    show = (show + 1) % SHOW_INTERVAL;
    if (fix.valid.speed && (fix.speed_kph() > SPEED_LIMIT)) {
      digitalWrite( LED_PIN, HIGH );
    } else {
      digitalWrite( LED_PIN, LOW );
    }
    if (fix.valid.location) {
      if (lastLocOK) {
        odo += fix.location.DistanceKm( lastLoc );
        Speed = fix.speed_kph();
      }
      lastLoc   = fix.location;
      lastLocOK = true;
      if (stScan) {
        stScan = false;
        base   = fix.location;
      } else {
        Dist = ( fix.location.DistanceKm( base ) );
      }
    }
    if ( Speed > maxs)          maxs = Speed;
    if (fix.valid.satellites ) {
      Sat = fix.satellites ;
      Satok = true;
    }


    if (fix.valid.heading ) {
      head = fix.heading() ;
      index = trovaIndice(head);
    }

    if (fix.valid.altitude) {
      alt = fix.altitude();
      altOK = true;
    }

    if (show == 0) {
      char displayBufffer[MAX_CHARS];
      oled.setCursor(0, 0);
      snprintf(displayBufffer, MAX_CHARS, "Km:%2d.%1d",  (int)odo, (int)(odo * 100) % 100);
      oled.println(displayBufffer);
      oled.setCursor(65, 0);
      snprintf(displayBufffer, MAX_CHARS, "Di:%2d.%1d",  (int)Dist, (int)(Dist * 100) % 100);
      oled.println(displayBufffer);
      snprintf(displayBufffer, MAX_CHARS, "Ve:%2d.%1d", (int)Speed, (int)(Speed * 10) % 10);
      oled.println(displayBufffer);
      oled.setCursor(65, 2);
      snprintf(displayBufffer, MAX_CHARS, "Al:%2d.%1d", (int)alt, (int)(alt * 100) % 100);
      oled.println(displayBufffer);

      snprintf(displayBufffer, MAX_CHARS, "Vm:%2d.%1d", (int)maxs, (int)(maxs * 10) % 10);
      oled.println(displayBufffer);
      oled.setCursor(65, 4);
      snprintf(displayBufffer, MAX_CHARS, "He: %s\n", (strBussola[index]));
      oled.println(displayBufffer);
      snprintf(displayBufffer, MAX_CHARS, "%d:%d:%d ", (int)ore, (int)minuti, (int)secondi);
      oled.println(displayBufffer);
      oled.setCursor(65, 6);
      snprintf(displayBufffer, MAX_CHARS, "Co:%2d.%1d", (int)head, (int)(head * 100) % 100);
      oled.println(displayBufffer);
    }
    File data = SD.open("L.csv", FILE_WRITE);
    data.print(fix.dateTime.hours + 2);
    data.print(":");
    data.print(fix.dateTime.minutes);
    data.print(" ");
    data.print(fix.dateTime.date);
    data.print("/");
    data.print(fix.dateTime.month);
    data.print(" ");
    data.print(ore);
    data.print(":");
    data.print(minuti);
    data.print(":");
    data.print(secondi);
    data.print(" ");
    data.print(fix.latitude(), 6);
    data.print(" ");
    data.print(fix.longitude(), 6);
    data.print(" ");
    data.print(alt);
    data.print(" ");

    data.print(odo);
    data.print(" ");
    data.print(Dist);
    data.print(" ");
    data.print(Speed);
    data.print(" ");
    data.print(head);
    data.print(" ");
    data.print(strBussola[index]);
    data.print(" ");
    data.print(Sat);
    data.println();
    data.close();
  }
}

grazie per l'aiuto.

Se le cose semplici non ti piacciono... va bene così.
Anche il calcolo del tempo:

unsigned long t=milllis()/1000; // secondi totali trascorsi
byte secondi = t%60;
byte minuti = (t/60)%60;
byte ore = (t/3600)%24;

oppure, risparmiando una variabile unsigned long:

byte secondi = (t/1000)%60;
byte minuti = (t/60000)%60;
byte ore = (t/3600000)%24;

Tieni presente che, per essere ragionevolmente preciso (entro un minuto al mese), devi usare una scheda con il clock del microcontrollore generato da un vero quarzo, non da un risuonatore ceramico come quello montato in Arduino Uno (almeno in quello originale, ma anche nella maggior parte degli altri), eventualmente aggiungendo anche un compensatore per la taratura fine (se vuoi che stia entro pochi secondi al mese di errore).

1 Like

Grazie, quell'esempio di calcolo del tempo non ricordo dove l'ho trovato, ha funzionato bene ed è sempre rimasto così.
tutto il programma è sommariamente un collage di esempi adattati.
sono certo che si capisce, non ho la pretesa di scrivere il programma di mio pugno anche se nel tempo sono riuscito a capire abbastanza bene come funziona in tutte le sue parti.
magari come vedi ci sono linee del codice che potrebbero essere semplificate o migliorate e ti ringrazio per questa modifica del contatore che hai scritto.
in origine sono partito da un programma scritto per tinygps convertito in neogps perché con tinygps non sono riuscito a fare un calcolo sensato della distanza percorsa (odometro) cosa che oggi forse potrei anche provare a ottenere usando distance between ma ormai non so nemmeno quanto possa meritare tornare auna versione dello stesso programma basata su tinygps.
Si sono susseguite tante prove e modifiche, alcune farina del mio sacco e altre con gratitudine verso chi mi ha aiutato e mi ha dedicato del tempo per migliorarlo.
ad oggi il risultato è molto buono, almeno io lo uso questo gps con una certa soddisfazione.
la precisione è irrilevante per come lo uso io, ieri ho camminato 16km in circa 3 ore, siamo al massimo possibile per i miei piedi.

il codice con la tua semplificazione funzionante.
adesso vorrei riprovare a dividere la rosa dei venti in 16 settori e vedere se il guadagno di memoria potrà permetterlo.
poi non so, ho finito anche la fantasia su cosa potremmo spremere fuori da un modulo gps.
ho rinunciato a scrivere l'altitudine massima o giocare con l'altitudine perché genera molti errori e false rilevazioni altrimenti poteva essere carino avere anche un log sui dislivelli percorsi ma non funzionerebbe mai bene, penso andrebbe fatto con un tipo diverso di sensore.

#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include <NMEAGPS.h>
#include <NeoSWSerial.h>
#define I2C_ADDRESS 0x3c
#define RST_PIN -1
#define MAX_CHARS 24
#define RX_pin 5
#define TX_pin 4
#define SHOW_INTERVAL 1
#define GPS_baud 9600
SSD1306AsciiWire oled;
NMEAGPS gps;
gps_fix fix;
float odo, Speed, alt, Dist, head, maxs = 0;
unsigned int  Sat = 0;
NeoGPS::Location_t    lastLoc, base;
bool stScan = true, lastLocOK = false;
bool altOK = false;
bool Satok = false;
static NeoSWSerial gpsPort(RX_pin, TX_pin);
static constexpr int INITIAL_SHOW  = (2 * SHOW_INTERVAL) - 1;
int show          = INITIAL_SHOW;
const int   LED_PIN  = 3;
const float SPEED_LIMIT = 0.1; // speed limit 50 oppure 0,1 fix 
void disp(int c, int r) {
  oled.clear();
  oled.setCursor(c, r);
}
const char* strBussola[8] = {"N ", "NE", "E ", "SE", "S ", "SO", "O ", "NO"};
int index;
int trovaIndice(float angolo) {
  int _index;
  for (_index = 0; _index < 8; _index++) {
    float angoloDirezione = _index * 45.0 + 22.50;
    float angoloMin = angoloDirezione - 22.50;
    float angoloMax = angoloDirezione + 22.50;
    if (angolo >= angoloMin && angolo <= angoloMax)
      return _index;
  }
  return _index;
}
void setup() {
  pinMode (LED_PIN, OUTPUT);
  Serial.begin(9600);
  gpsPort.begin(GPS_baud);
  Wire.begin();
  oled.begin(&SH1106_128x64, I2C_ADDRESS);
  oled.setFont(ZevvPeep8x16);
  //oled.displayRemap(true); //inversione dello schermo 
  disp(20, 2);
  oled.println(F("IL TUO NOME"));
  delay(1500);
  oled.setCursor(24, 5);
  oled.println(F("GPS LOGGER"));
  delay(2500);
  oled.clear();
  const int cs_sd = 2;
  if (!SD.begin (cs_sd)) {
    oled.clear();
    disp(40, 2);
    oled.print("NO SD"); 
    delay(10000);
    oled.clear();
  }
  File data = SD.open("L.csv", FILE_WRITE);
  data.println("");
  data.println(F("Da Hr Tm La Lo At Km Di Ve Co He Sa" ));
  data.close();
}

void loop() {

  unsigned long tmp = millis() / 1000;
  byte secondi = tmp % 60;
  byte minuti = (tmp / 60) % 60;
  byte ore = (tmp / 3600) % 24;


  if (gps.available( gpsPort )) {
    gps_fix fix = gps.read();
    show = (show + 1) % SHOW_INTERVAL;
    if (fix.valid.speed && (fix.speed_kph() > SPEED_LIMIT)) {
      digitalWrite( LED_PIN, HIGH );
    } else {
      digitalWrite( LED_PIN, LOW );
    }
    if (fix.valid.location) {
      if (lastLocOK) {
        odo += fix.location.DistanceKm( lastLoc );
        Speed = fix.speed_kph();
      }
      lastLoc   = fix.location;
      lastLocOK = true;
      if (stScan) {
        stScan = false;
        base   = fix.location;
      } else {
        Dist = ( fix.location.DistanceKm( base ) );
      }
    }
    if ( Speed > maxs)          maxs = Speed;
    if (fix.valid.satellites ) {
      Sat = fix.satellites ;
      Satok = true;
    }


    if (fix.valid.heading ) {
      head = fix.heading() ;
      index = trovaIndice(head);
    }

    if (fix.valid.altitude) {
      alt = fix.altitude();
      altOK = true;
    }

    if (show == 0) {
      char displayBufffer[MAX_CHARS];
      oled.setCursor(0, 0);
      snprintf(displayBufffer, MAX_CHARS, "Km:%2d.%1d",  (int)odo, (int)(odo * 100) % 100);
      oled.println(displayBufffer);
      oled.setCursor(65, 0);
      snprintf(displayBufffer, MAX_CHARS, "Di:%2d.%1d",  (int)Dist, (int)(Dist * 100) % 100);
      oled.println(displayBufffer);
      snprintf(displayBufffer, MAX_CHARS, "Ve:%2d.%1d", (int)Speed, (int)(Speed * 100) % 100);
      oled.println(displayBufffer);
      oled.setCursor(65, 2);
      snprintf(displayBufffer, MAX_CHARS, "Al:%2d.%1d", (int)alt, (int)(alt * 100) % 100);
      oled.println(displayBufffer);

      snprintf(displayBufffer, MAX_CHARS, "Vm:%2d.%1d", (int)maxs, (int)(maxs * 100) % 100);
      oled.println(displayBufffer);
      oled.setCursor(65, 4);
      snprintf(displayBufffer, MAX_CHARS, "He: %s\n", (strBussola[index]));
      oled.println(displayBufffer);
      snprintf(displayBufffer, MAX_CHARS, "%d:%d:%d ", (int)ore, (int)minuti, (int)secondi);
      oled.println(displayBufffer);
      oled.setCursor(65, 6);
      snprintf(displayBufffer, MAX_CHARS, "Co:%2d.%1d", (int)head, (int)(head * 100) % 100);
      oled.println(displayBufffer);
    }
    File data = SD.open("L.csv", FILE_WRITE);
    data.print(fix.dateTime.hours + 2);
    data.print(":");
    data.print(fix.dateTime.minutes);
    data.print(" ");
    data.print(fix.dateTime.date);
    data.print("/");
    data.print(fix.dateTime.month);
    data.print(" ");
    data.print(ore);
    data.print(":");
    data.print(minuti);
    data.print(":");
    data.print(secondi);
    data.print(" ");
    data.print(fix.latitude(), 6);
    data.print(" ");
    data.print(fix.longitude(), 6);
    data.print(" ");
    data.print(alt);
    data.print(" ");

    data.print(odo);
    data.print(" ");
    data.print(Dist);
    data.print(" ");
    data.print(Speed);
    data.print(" ");
    data.print(head);
    data.print(" ");
    data.print(strBussola[index]);
    data.print(" ");
    data.print(Sat);
    data.println();
    data.close();
  }
}

la divisione della rosa dei venti in 16 settori l'avrei interpretata in questo modo, non so se sia corretto ma rende comunque errore di memoria 27994 programmi e 1548 memoria dinamica.
perché questo? cosa gli cambia?

const char* strBussola[16] = {"N ", "Ne", "NE", "En", "E ", "Es",
                              "SE", "Se", "S ", "So", "SO", "Os", "O ", "On", "NO", "No"
                             };
int index;

int trovaIndice(float angolo) {
  int _index;
  for (_index = 0; _index < 16; _index++) {
    float angoloDirezione = _index * 45.0 + 11.25;
    float angoloMin = angoloDirezione - 11.25;
    float angoloMax = angoloDirezione + 11.25;
    if (angolo >= angoloMin && angolo <= angoloMax)
      return _index;

comunque non è poi troppo rilevante sapere se andando a sud est si sia più spostatati verso sud oppure verso est, giusto una prova.

sono riuscito a caricarlo senza errori di memoria togliendo la virgola a qualche dato come velocità e altitudine (e forse è anche meglio) ma ho sbagliato a fare i conti perché adesso a compass 180° mi indica est.

ho corretto 45 con 22.25 e forse ci sono.
vagando per strada come lo scemo del villaggio pare funzionare bene.

float angoloDirezione = _index * 22.25 + 11.25;

ho tolto la virgola a vari dati, tanto sapere che l'altitudine è 47 metri oppure 47.5 non cambia nulla
stessa cosa per velocità istantanea e velocità massima, anzi tutto il display sembra più ordinato e facilmente leggibile.

questo è il codice con 16 settori heading

#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include <NMEAGPS.h>
#include <NeoSWSerial.h>

#define I2C_ADDRESS 0x3C
#define RST_PIN -1
#define MAX_CHARS 24
#define RX_pin 5
#define TX_pin 4
#define SHOW_INTERVAL 1
#define GPS_baud 9600
SSD1306AsciiWire oled;
NMEAGPS gps;
gps_fix fix;
float odo, Dist, head, maxs = 0;
unsigned int Speed, alt, Sat = 0;

NeoGPS::Location_t    lastLoc, base;
bool stScan = true, lastLocOK = false;
bool altOK = false;
bool Satok = false;
static NeoSWSerial gpsPort(RX_pin, TX_pin);
static constexpr int INITIAL_SHOW  = (2 * SHOW_INTERVAL) - 1;
int show          = INITIAL_SHOW;
const int   LED_PIN  = 3;
const float SPEED_LIMIT = 0.1; // speed limit value

void disp(int c, int r) {
  oled.clear();
  oled.setCursor(c, r);
}

const char* strBussola[16] = {"N ", "Ne", "NE", "nE", "E ", "sE",
                              "SE", "Se", "S ", "So", "SO", "sO", "O ", "nO", "NO", "No"
                             };
int index;

int trovaIndice(float angolo) {
  int _index;
  for (_index = 0; _index < 16; _index++) {
    float angoloDirezione = _index * 22.25 + 11.25;
    float angoloMin = angoloDirezione - 11.25;
    float angoloMax = angoloDirezione + 11.25;
    if (angolo >= angoloMin && angolo <= angoloMax)
      return _index;
  }
  return _index;
}

void setup() {
  pinMode (LED_PIN, OUTPUT);
  Serial.begin(9600);
  gpsPort.begin(GPS_baud);
  Wire.begin();
  oled.begin(&SH1106_128x64, I2C_ADDRESS);
  oled.setFont(ZevvPeep8x16);
  //oled.displayRemap(true); // inversione dello schermo display
  disp(20, 2);
  oled.println(F("IL TUO NOME"));
  delay(1500);
  oled.setCursor(24, 5);
  oled.println(F("GPS LOGGER"));
  delay(2500);
  oled.clear();
  const int cs_sd = 2;
  if (!SD.begin (cs_sd)) {
    oled.clear();
    disp(50, 2);
    oled.print("SD");
    delay(10000);
    oled.clear();
  }
  File data = SD.open("L.csv", FILE_WRITE);
  data.println("");
  data.println(F("D H T La Lo At Km Di Ve He Co Sa" ));
  data.close();
}

void loop() {
  unsigned long tmp = millis() / 1000;
  byte secondi = tmp % 60;
  byte minuti = (tmp / 60) % 60;
  byte ore = (tmp / 3600) % 24;

  if (gps.available( gpsPort )) {
    gps_fix fix = gps.read();
    show = (show + 1) % SHOW_INTERVAL;
    if (fix.valid.speed && (fix.speed_kph() > SPEED_LIMIT)) {
      digitalWrite( LED_PIN, HIGH );
    } else {
      digitalWrite( LED_PIN, LOW );
    }
    if (fix.valid.location) {
      if (lastLocOK) {
        odo += fix.location.DistanceKm( lastLoc );
        Speed = fix.speed_kph();
      }
      lastLoc   = fix.location;
      lastLocOK = true;
      if (stScan) {
        stScan = false;
        base   = fix.location;
      } else {
        Dist = ( fix.location.DistanceKm( base ) );
      }
    }
    if ( Speed > maxs)          maxs = Speed;
    if (fix.valid.satellites ) {
      Sat = fix.satellites ;
      Satok = true;
    }


    if (fix.valid.heading ) {
      head = fix.heading() ;
      index = trovaIndice(head);
    }

    if (fix.valid.altitude){ 
      alt = fix.altitude();
     altOK = true;
    }

    if (show == 0) {
      char displayBufffer[MAX_CHARS];
      oled.setCursor(0, 0);
      snprintf(displayBufffer, MAX_CHARS, "Km:%2d.%1d",  (int)odo, (int)(odo * 100) % 100);
      oled.println(displayBufffer);
      oled.setCursor(65, 0);
      snprintf(displayBufffer, MAX_CHARS, "Ve:%2d", (int)Speed, (int)(Speed * 100) % 100);
      oled.println(displayBufffer);
      snprintf(displayBufffer, MAX_CHARS, "Di:%2d.%1d",  (int)Dist, (int)(Dist * 100) % 100);
      
      oled.println(displayBufffer);
     oled.setCursor(65, 2);
     snprintf(displayBufffer, MAX_CHARS, "Vm:%2d", (int)maxs, (int)(maxs * 100) % 100);
      
      oled.println(displayBufffer);

      snprintf(displayBufffer, MAX_CHARS, "Al:%2d", (int)alt, (int)(alt * 100) % 100);
      oled.println(displayBufffer);
      oled.setCursor(65, 4);
      snprintf(displayBufffer, MAX_CHARS, "He:%2d", (int)head, (int)(head * 100) % 100);
      oled.println(displayBufffer);
      snprintf(displayBufffer, MAX_CHARS, "%d:%d:%d ", (int)ore, (int)minuti, (int)secondi);
      oled.println(displayBufffer);
      oled.setCursor(65, 6);
      snprintf(displayBufffer, MAX_CHARS, "Co: %s\n", (strBussola[index]));
      
      oled.println(displayBufffer);
    }
    File data = SD.open("L.csv", FILE_WRITE);
    data.print(fix.dateTime.hours + 2);
    data.print(":");
    data.print(fix.dateTime.minutes);
    data.print(" ");
    data.print(fix.dateTime.date);
    data.print("/");
    data.print(fix.dateTime.month);
    data.print(" ");
    data.print(ore);
    data.print(":");
    data.print(minuti);
    data.print(":");
    data.print(secondi);
    data.print(" ");
    data.print(fix.latitude(), 6);
    data.print(" ");
    data.print(fix.longitude(), 6);
    data.print(" ");
    data.print(alt);
    data.print(" ");

    data.print(odo);
    data.print(" ");
    data.print(Dist);
    data.print(" ");
    data.print(Speed);
    data.print(" ");
    data.print(head);
    data.print(" ");
    data.print(strBussola[index]);
    data.print(" ");
    data.print(Sat);
    data.println();
    data.close();
  }
}

la rosa dei venti l'ho dovuta fare con soli 2 caratteri, spero risulti chiara. la maiuscola è la direzione dominante.
se conoscete un modo migliore, al momento non mi viene a mente altro.
"N ", "Ne", "NE", "nE", "E ", "sE", "SE", "Se", "S ", "So", "SO", "sO", "O ", "nO", "NO", "No

Questo è uno dei problemi di Arduino: chiunque inventa e pubblica, tanto più gli ultimi arrivati pubblicano con orgoglio ciò che hanno messo insieme copiando qua e là e modificando (anche a caso, finché non esce qualche cosa...) e poi si diffondono cose di ogni tipo...

a me sembra normale, vedendo la cosa sotto un punto di vista biologico si potrebbe definire alla base della biodiversità.
per altro la scienza va avanti spesso per tentativi ed errori, poi arriva uno che riprende in mano una mezza scoperta, la rielabora e a volte nasce qualcosa di buono.
perfino per caso.
tra l'altro io ero abituato a studiare sui libri, oggi si studia sui siti e questo comporta che ognuno insegna spesso in modo approssimativo o superficiale se non anche del tutto errato o peggio con la voglia di stupire o strafare. un libro per essere stampato richiedeva un editore e un certo controllo del contenuto ma francamente io non conosco libri dive potrei studiare questa materia in modo chiaro e approfondito. spesso imparo meglio vedendo esempi pratici e soluzioni dirette sul programma piuttosto che andare su siti dove spiegano a livello generale e teorico come fare le cose.

1 Like

stasera ho provato per una decina di chilometri e la suddivisione a 16 settori va benissimo.
unica cosa a volte la bussola viene fuori con gradi impossibili tipo 925 non so dire da cosa possa dipendere è un problema che a volte ho riscontrato anche col conteggio dei satelliti che riporta numeri davvero eccessivi.
potrebbe dipendere da decimali scritti male ? tipo 925 che magari sono 92,5 non so come interpretare questo tipo di errori. se dipendono la programma scritto male oppure dal modulo gps che riceve o riporta dati errati.
tra l'altro head è dichiarato come float ma non penso che sia necessario unsigned int potrebbe essere più corretto visto che si tratta di un dato in gradi numero positivo da zero a 360

in questo modo limando qualcosa dalle linee snprinf sarei riuscito ad avere i 16 settori scritti con 3 lettere.
unico problema a volte i gradi bussola sono matti ovvero vanno oltre 360 e ancora non capisco perché
li ho definiti come head unsigned int quindi dove sbaglio a pensare che non devono avere decimali ma essere numeri interi compresi tra 0 e 360° ???
mi chiedo se dovrei definire che head non può mai superare 360
lo stesso tipo di errore di sat che se non sbaglio a memoria non dovrebbe mai superare 31 satelliti.
ad ogni modo il codice che sto provando con Co mpass 3 caratteri è questo
funziona.

#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include <NMEAGPS.h>
#include <NeoSWSerial.h>

#define I2C_ADDRESS 0x3C
#define RST_PIN -1
#define MAX_CHARS 24
#define RX_pin 5
#define TX_pin 4
#define SHOW_INTERVAL 1
#define GPS_baud 9600
SSD1306AsciiWire oled;
NMEAGPS gps;
gps_fix fix;
float odo, Dist;
unsigned int head, Speed, alt, Sat = 0, maxs = 0;

NeoGPS::Location_t    lastLoc, base;
bool stScan = true, lastLocOK = false;
bool altOK = false;
bool Satok = false;
static NeoSWSerial gpsPort(RX_pin, TX_pin);
static constexpr int INITIAL_SHOW  = (2 * SHOW_INTERVAL) - 1;
int show          = INITIAL_SHOW;
const int   LED_PIN  = 3;
const float SPEED_LIMIT = 0.1; // speed limit value

void disp(int c, int r) {
  oled.clear();
  oled.setCursor(c, r);
}

const char* strBussola[16] = {"N  ", "NNe", "NE ", "EnE", "E  ", "EsE",
                              "SE ", "SSe", "S  ", "SSo", "SO ", "OsO", "O  ", "OnO", "NO ", "NNo"
                             };
int index;

int trovaIndice(float angolo) {
  int _index;
  for (_index = 0; _index < 16; _index++) {
    float angoloDirezione = _index * 22.25 + 11.25;
    float angoloMin = angoloDirezione - 11.25;
    float angoloMax = angoloDirezione + 11.25;
    if (angolo >= angoloMin && angolo <= angoloMax)
      return _index;
  }
  return _index;
}

void setup() {
  pinMode (LED_PIN, OUTPUT);
  Serial.begin(9600);
  gpsPort.begin(GPS_baud);
  Wire.begin();
  oled.begin(&SH1106_128x64, I2C_ADDRESS);
  oled.setFont(ZevvPeep8x16);
  //oled.displayRemap(true); // inversione dello schermo display
  disp(0, 2);
  oled.println(F("CHRISTIAN WRIGHT"));
  delay(1500);
  oled.setCursor(24, 5);
  oled.println(F("GPS LOGGER"));
  delay(2500);
  oled.clear();
  const int cs_sd = 2;
  if (!SD.begin (cs_sd)) {
    oled.clear();
    disp(50, 2);
    oled.print(F("SD"));
    delay(10000);
    oled.clear();
  }
  File data = SD.open("L.csv", FILE_WRITE);
  data.println("");
  data.println(F("D H T La Lo At Km Di Ve He Co Sa" ));
  data.close();
}

void loop() {
  unsigned long tmp = millis() / 1000;
  byte secondi = tmp % 60;
  byte minuti = (tmp / 60) % 60;
  byte ore = (tmp / 3600) % 24;

  if (gps.available( gpsPort )) {
    gps_fix fix = gps.read();
    show = (show + 1) % SHOW_INTERVAL;
    if (fix.valid.speed && (fix.speed_kph() > SPEED_LIMIT)) {
      digitalWrite( LED_PIN, HIGH );
    } else {
      digitalWrite( LED_PIN, LOW );
    }
    if (fix.valid.location) {
      if (lastLocOK) {
        odo += fix.location.DistanceKm( lastLoc );
        Speed = fix.speed_kph();
      }
      lastLoc   = fix.location;
      lastLocOK = true;
      if (stScan) {
        stScan = false;
        base   = fix.location;
      } else {
        Dist = ( fix.location.DistanceKm( base ) );
      }
    }
    if ( Speed > maxs)          maxs = Speed;
    if (fix.valid.satellites ) {
      Sat = fix.satellites ;
      Satok = true;
    }


    if (fix.valid.heading ) {
      head = fix.heading() ;
      index = trovaIndice(head);
    }

    if (fix.valid.altitude){ 
      alt = fix.altitude();
     altOK = true;
    }

    if (show == 0) {
      char displayBufffer[MAX_CHARS];
      oled.setCursor(0, 0);
      snprintf(displayBufffer, MAX_CHARS, "Km: %d.%d",  (int)odo, (int)(odo * 100) % 100);
      oled.println(displayBufffer);
      oled.setCursor(65, 0);
      snprintf(displayBufffer, MAX_CHARS, "Ve: %d", (int)Speed, (int)(Speed * 100) % 100);
      oled.println(displayBufffer);
      snprintf(displayBufffer, MAX_CHARS, "Di: %d.%d",  (int)Dist, (int)(Dist * 100) % 100);
      
      oled.println(displayBufffer);
     oled.setCursor(65, 2);
     snprintf(displayBufffer, MAX_CHARS, "Vm: %d", (int)maxs, (int)(maxs * 100) % 100);
      
      oled.println(displayBufffer);

      snprintf(displayBufffer, MAX_CHARS, "Al: %d", (int)alt, (int)(alt * 100) % 100);
      oled.println(displayBufffer);
      oled.setCursor(65, 4);
      snprintf(displayBufffer, MAX_CHARS, "He: %d", (int)head, (int)(head * 100) % 100);
      oled.println(displayBufffer);
      snprintf(displayBufffer, MAX_CHARS, "%d:%d:%d ", (int)ore, (int)minuti, (int)secondi);
      oled.println(displayBufffer);
      oled.setCursor(65, 6);
      snprintf(displayBufffer, MAX_CHARS, "Co: %s\n", (strBussola[index]));
      
      oled.println(displayBufffer);
    }
    File data = SD.open("L.csv", FILE_WRITE);
    data.print(fix.dateTime.hours + 2);
    data.print(":");
    data.print(fix.dateTime.minutes);
    data.print(" ");
    data.print(fix.dateTime.date);
    data.print("/");
    data.print(fix.dateTime.month);
    data.print(" ");
    data.print(ore);
    data.print(":");
    data.print(minuti);
    data.print(":");
    data.print(secondi);
    data.print(" ");
    data.print(fix.latitude(), 6);
    data.print(" ");
    data.print(fix.longitude(), 6);
    data.print(" ");
    data.print(alt);
    data.print(" ");

    data.print(odo);
    data.print(" ");
    data.print(Dist);
    data.print(" ");
    data.print(Speed);
    data.print(" ");
    data.print(head);
    data.print(" ");
    data.print(strBussola[index]);
    data.print(" ");
    data.print(Sat);
    data.println();
    data.close();
  }
}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.