Visualizzazione post con etichetta GPS. Mostra tutti i post
Visualizzazione post con etichetta GPS. Mostra tutti i post

venerdì 23 agosto 2024

Quectel LC29HDA

Cavalcando l'onda della banda 5 del GPS ho provato il Quectel LC29H DA (rover) e BS (base) che ha la possibilita' di RTK (esiste anche il rover DEA che costa un po' di piu' ed offre un campionamento a 10 Hz)

Piccola nota: per attivare l'uscita sulla UART si deve spostare il microswitch dal lato dove sono posti la porta USB ed i refori dalla porta seriale. L'alimentazione del modulo e' a 3.7 V


Il software di riferimento per questo dispositivo e' QGNSS della Quectel che ha incluso il proprio Ntrip Client. Al contrario di quanto indicato su altri siti la velocita' di default della porta e' 115200 

Come Ntrip server ho impiegato euref-ip.asi.it porta 2101 con la stazione IGMI00ITA


Si ottiene in pochi secondi RTK Fix 

Come si vede il posizionamento di prova e' sostanzialmente nelle specifiche ma si nota che i punti si distribuiscono su nuvole distinte 

La cosa curiosa con questo dispositivo e' che non funziona di default con altri software differenti da QGNSS. Il problema e' il software QGNSS di fatto non effettua nessun calcolo ma trasmette i dati dal server NTRIP verso LC29H che effettua i calcoli sul modulo e poi restituisce indietro una stringa NMEA con i dati gia' corretti

Io volevo trasmettere i dati dell'antenna via Lora verso un computer remoto e questa impostazione non e' applicabile. 

E' possibile modificare la configurazione in modo che LC29H trasmetta solo messaggi RTCM3 e non efffettui calcoli in modo poi da poter passare il calcolo ad RTKNAVI. Per fare cio' 

$PQTMRESTOREPAR*13 
$PQTMCFGRCVRMODE,W,2*29
$PQTMSAVEPAR*5A 
# reset
$PAIR432,1*22 # output RTCM3 MSM7 messages
$PAIR062,0,01*0F # Enable NMEA GGA message
$PQTMSAVEPAR*5A # save PQTM params to flash

Per la trasmissione Lora ho provato dei moduli E32 868T30D che sono compatibili con il voltaggio a 3.3 V in modo da essere alimentati direttamente da LC29H

Per programmarli ho utilizzato il modulo E15- USB-T2 spostando il jumper dell'alimentazione a 3.3 V e rimuovendo i jumper M0 ed M1 con il software RF Settings 





lunedì 30 ottobre 2023

Trimble GeoXT 2005 Series nel 2023

Ho trovato in cassetto dell'ufficio sepolto da qualche mio predecessore un Trimble GeoXT, un dispositivo GPS che usavo all'Universita'. Era una macchina estremamente costosa che includeva un palmare con Pocket PC

Mi e' stato detto che il GPS era stato accantonato perche' non reggeva la batteria....ovviamente la mia domanda e' stata..si potra' usare nel 2023 un dispositivo del 2005 


Primo aspetto: la batteria non e' assolutamente rovinata...il dispositivo e' rimasto acceso per ore (>5) 

Secondo aspetto: Non ho a disposizione l'accessorio che espone la porta seriale quindi l'unico modo di dialogare con il dispositivo sono Active Sync o via Ethernet (il cradle ha una porta Ethernet RJ45). Per usare Active Sync si puo' usare solo Windows XP (non c'e' compatibilita' con le versioni successive). In macchina Virtualbox Active Sync funziona

Terzo aspetto: anche il dispositivo va in rete via RJ45 c'e' da mettere in conto che oramai i certificati SSL nel dispositivo sono scaduti e non possono essere aggiornati. Ci si puo' scordare di navigare il Web ed anche TerraSync non scarica aggiornamenti degli almanacchi delle effemeridi dei satelliti

Quarto aspetto: non sono riuscito a connettere la WiFi all'hotspot. Le autenticazioni sono WEP (ovviamente ...siamo nel 2004) e WPA. Provando WPA non riesco a connettermi con Access Point moderni

A questo punto il dispositivo funziona in acquisizione??

Premesso che l'antenna e' solo una L1 a 12 canali che puo' essere postprocessata (e quindi non so quanta valga la pena perderci tempo avendo a disposizione delle antenne L1+L2 a costo minore e prestazione migliore) i primi tentativi di salvare i dati su TerraSync sono risultati vani.

Il dispositivo era molto lento nell'aggancio dei satelliti ma questo e' abbastanza ovvio dato che usa un almanacco del 2004. Una volta acquisito il fix Terrasync si rifiutava di salvare i dati per un problema sull'orologio

Ho modificato a mano l'orologio di Windows CE. A questo punto il fix e' stato molto piu' veloce ma persisteva il problema. Controllando ho visto che Terrasync riportava automaticamente l'orologio di sistema al 2004




La soluzione (trovata su un forum) e' stata quella di sincronizzare l'ora tramite ActiveSync (in pratica acquisendo la data dal PC Desktop), poi lanciare TerraSync. A questo punto tutto inizia a funzionare in modo corretto

Ne vale la pena??

Per funzionare funziona ma c'e' da dire che i dati di fase per il postprocessing vengono salvati nel formato binario di Trimble e quindi c'e' da fare un ulteriore passaggio per passarli in Rinex peraltro con un software ConverttoRinex che attualmente non riesco a farlo funzionare in macchina virtuale (problemi con le librerie DotNet) 




mercoledì 27 marzo 2019

Gps Kalman Filter con Arduino Uno

Partendo da questo link volevo provare a testare il filtro Kalman sui dati di un GPS ma privo della contributo di una IMU (come fatto qui)

La libreria Arduino utilizzata (questo il link) e' una conversione di un codice in C originario

Mettendo a confronto la linea blu dei dati non filtrati con quella rossa dei dati filtrati e lasciando ferma l'antenna GPS si osserva che il filtro funziona ma non limita le tipiche fluttuazioni (anche molto significative) del segnale GPS




Questo test e' stato effettuato con due antenne a basso costo: un primo modulo senza marca di derivazione cinese ed una antenna con predisposizione per PixHawk denominata gygpsv5 con a bordo un Ublox NEO6M-V2


Utilizzando il codice scaricato da Internet senza particolari modifiche tutto funzionava con il modulo cinese ma non con Ublox nonostante i messaggi NMEA fossero correttamente ricevuti da Arduino.

Salvando i dati si capisce il motivo

Neo 6P
$GLGSV,3,3,09,88,20,181,*56
$GNGLL,4347.03855,N,01113.80955,E,132645.00,A,A*7C
$GNRMC,132646.00,A,4347.03855,N,01113.80955,E,0.030,,270319,,,A*6B
$GNVTG,,T,,M,0.030,N,0.055,K,A*3E
$GNGGA,132646.00,4347.03855,N,01113.80955,E,1,08,1.35,42.1,M,45.5,M,,*76
$GNGSA,A,3,12,24,25,32,,,,,,,,,2.61,1.35,2.23*1E
$GNGSA,A,3,68,86,77,87,,,,,,,,,2.61,1.35,2.23*12

GPS
$GPGSV,2,2,08,32,33,290,40,14,26,308,23,31,04,310,,34,,,*47
$GPRMC,133332.000,A,4347.0429,N,01113.8140,E,0.21,175.07,270319,,,A*60
$GPGGA,133333.000,4347.0428,N,01113.8140,E,1,5,1.55,36.1,M,47.6,M,,*6E
$GPGSA,A,3,12,25,24,29,32,,,,,,,,1.83,1.55,0.98*00
$GPGSV,2,133,37,25,58,293,33,24,46,141,33,29,37,207,25*75
$GPGSV,2,2,08,32,33,290,39,14,26,308,22,31,04,310,,34,,,*48
$GPRMC,133333.000,A,4347.0428,N,01113.8140,E,0.35,175.07,270319,,,A*65
$GPGGA,133334.000,4347.0427,N,01113.8141,E,1,5,1.55,36.1,M,47.6,M,,*67
$GPGSA,A,3,12,25,24,29,32,,,,,,,,1.83,1.55,0.98*00


Il modulo cinese usa delle frasi NMEA che iniziano per GP***** mentre Ublox usa frasi che usaon un prefisso GN****. La libreria TinyGPS riconosce solo il primo prefisso e quindi non sa come trattare i dati dell'Ublox ...e' stato sufficiente editare la libreria sostituendo le stringhe per rendere compatibile TinyGPS con UBlox.....ma come mai questa differenza

GN e' un prefisso generico che si usa per qualunque sorgenti date (Gps americano, Beidu, Glonass etc) mentre il prefisso GP e' relativo solo al GPS americano. (Glonass per esempio ha il prefisso GL).

-----------------------------------------------------------------
#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <GPSFilter.h>

#define RXPIN 2
#define TXPIN 3

#define GPSBAUD 9600

TinyGPS gps;
SoftwareSerial uart_gps(RXPIN, TXPIN);

GPSFilter f(2.5);

unsigned long lastUpdate;
unsigned long lastSentence;
float c;

void setup()
{
 Serial.begin(9600);
 uart_gps.begin(GPSBAUD);
 lastSentence = millis();
 lastUpdate = millis();
}


void loop()
{
 while(uart_gps.available()) {    
   char c = uart_gps.read();
    
   if(gps.encode(c)) {
     int year;
     byte month, day, hour, minute, second, hundredths;
     unsigned long age;
     gps.crack_datetime(&year,&month,&day,&hour,&minute,&second,&hundredths,&age); //Get the date from the GPS
     unsigned long date; 
     gps.get_datetime(&date,0,0);
     if(date == TinyGPS::GPS_INVALID_DATE){
        Serial.println(F("Invalid Age"));
         continue;
     }
     if(age == TinyGPS::GPS_INVALID_AGE) {
         Serial.println(F("Waiting for more valid data"));
         continue;
     }
     float latitude, longitude;
     double dlat,dlong;
     gps.f_get_position(&latitude, &longitude);

     double ulat,ulong;
     ulat = (double)latitude;
     ulong = (double)longitude;
     c = (millis() - lastSentence)/1000;
     f.update_velocity2d(ulat,ulong,c);
     lastSentence = millis();
  
     if((millis() - lastUpdate) >= 500 || lastUpdate == NULL){
          f.get_lat_long(&dlat,&dlong);
         Serial.print(latitude,8);
         Serial.print(";");
         Serial.print(longitude,8);
         Serial.print(";");
         Serial.print(dlat,8);
         Serial.print(";");
         Serial.println(dlong,8);     
        
         }
          lastUpdate = millis();
   }
 }
}

domenica 24 marzo 2019

Filtro EKF con GPS ed IMU su Android

Per continuare la sperimentazione sui filtri applicati ai sensori ho provato ad usare l' Extended Kalman Filter, un metodo matematico che permette la fusione di dati GPS e derivanti da IMU (per una descrizione abbastanza semplice del metodo si puo' andare qui,

Visto che la matematica non e' banale ho usato un progetto gia' fatto basato su Android. A questo link si trova la descrizione che usa la fusione di velocita' e dati geografici per ridurre le fluttuazioni del segnale GPS. Una libreria EKF per Arduino si trova a questo link basato su questo codice Matlab (l'esempio che usa EKF per GPS si trova nella sottodirectory /extras/c/ con dati di pseudorange, non direttamente lat/lon). Per una breve trattazione matematica invece qui

Un altro sito con una trattazione matematica semplice si trova qui

E' disponibile una app per Android a questo indirizzo GitHub


La prova e' stata effettuata mantenendo il sensore fermo e leggendo ad intervalli di tempo la distanza cumulata (ovvero la somma degli errori dei falsi movimenti registrati dal sensore).
Si evidenzia che il tempo di campionamento del GPS e del'accelerometro sono molto differenti e gli aggiornamento del filtro sono scalati sul sensore piu' lento

Distanza cumulata in metri
Come si vede il filtro impiega circa 100 secondi per stabilizzzarsi

Eliminando i primi due punti si ha una deriva media di 0.0015 m/sec, un dato incredibile considerando che con il solo GPS per ogni secondo la distanza e' di oltre 1 m

La scheda PixHawk contiene all'interno un algoritmo di fusione dei sensori per GPS ed IMU basato su EKF

giovedì 7 febbraio 2019

U-Blox U-Center e Wine

U-Center di U-Blox funziona correttamente in Wine su Debian
L'unica accortezza e' che si deve creare un symlink della porta USB verso un COM virtuale con

ln -s /dev/ttyACM0 /home/luca/.wine/dosdevices/com5




a questo punto il GPS verra' visto in U-Center sotto COM5


venerdì 25 gennaio 2019

Progetto Zero

GPS ad alimentazione solare con registrazione dati su SD Card e sleep mode ed attivazione programmabile tramite orologio interno

Componenti:
Arduino Uno
SeedStudio Solar Charger Shield
Data recoder shield
RTC DS3231
GPS con uscita seriale
Lipo 10.000 mAh
Pannello solare 6V
SD Card 2Gb



Il rele' deve essere collegato nella configurazione NA (normalmente aperto)
La seriale del GPS e' collegata direttamente alla seriale dell'Arduino perche' usare una SoftwareSerial crea problemi con la modalita' Sleep

Tra i vari aggiustamenti che ho fatto al programma c'e' anche un ridimensionamento dell'array del buffer. Con un buffer da 200 byte il programma si compilava, veniva lanciato su Arduino ma la IDE segnalava che il poco spazio di memoria rimasto libero (un centinaio di bytes) poteva rendere il programma instabile....in effetti durante l'esecuzione il buffer veniva riempito con caratteri a caso e con pezzi della memoria. Ridimensionato il buffer il problema e' scomparso
------------------------------------------------------------------------------
#include <RTClibExtended.h>
#include <Wire.h>
#include <LowPower.h>

#include <stdlib.h>
#include <avr/interrupt.h>      // library for interrupts handling
#include <avr/sleep.h>          // library for sleep
#include <avr/power.h>          // library for power control

// SD Card headers
#include <SPI.h>
#include <SD.h>



#define wakePin 2    //use interrupt 0 (pin 2) and run function wakeUp when pin 2 gets LOW

RTC_DS3231 rtc;      //we are using the DS3231 RTC


int contatore;
const unsigned int dim_buffer = 100;
int SerialData[dim_buffer];


const int chipSelect = 10;
char incomingByte = 0;   // for incoming serial data

char filen[10];
String filename;
char filenb[10];
String filenameb;


DateTime now;


//-------------------------------------------------

void wakeUp()
{


}

//------------------------------------------------------------

void setup() {


  pinMode(7, OUTPUT);
  

  delay(3000);
  // Inizializzazione SD
  if (!SD.begin(chipSelect)) {
    //Serial.println("Card failed, or not present");
    while (1);   

    //if (!sd.begin(chipSelect)) {
    //Serial.println("Card failed, or not present");
    //while (1);    Serial.begin(9600);

  }

  delay(3000);

  //Set pin D2 as INPUT for accepting the interrupt signal from DS3231
  pinMode(wakePin, INPUT);;

  //Initialize communication with the clock
  Wire.begin();
  rtc.begin();
  rtc.adjust(DateTime(__DATE__, __TIME__));   //set RTC date and time to COMPILE time
  delay(500);
  now = rtc.now();


  //clear any pending alarms
  rtc.armAlarm(1, false);
  rtc.clearAlarm(1);
  rtc.alarmInterrupt(1, false);
  rtc.armAlarm(2, false);
  rtc.clearAlarm(2);
  rtc.alarmInterrupt(2, false);

  rtc.writeSqwPinMode(DS3231_OFF);
  delay(1000);

}

//------------------------------------------------------------

void loop() {
   Serial.begin(9600);
    while (!Serial) {
  }
 
  Serial.println("inizio");

  digitalWrite(7, HIGH);


  now = rtc.now();
  sprintf(filen, "%02d%02d%02d%02d.txt", now.month(), now.day(), now.hour(), now.minute());
  String filename(filen);
  delay(2500);

  Serial.println(filename);


  File dataFile = SD.open(filename, FILE_WRITE);


  for (uint32_t i = 0; i <= 4000; i++) {
    contatore = 0;
    while (contatore < 98)
    {
      if (Serial.available() > 0) {
        SerialData[contatore] = Serial.read();
        //dataFile.print(char(Serial.read()));
        contatore++;

        //Serial.print(char(SerialData[contatore-1]));
        //dataFile.print(" ");
        //Serial.println(contatore);
      }

    }
    

    if (dataFile) {
      for (int s = 0; s < contatore - 1; s++) {
        Serial.print(SerialData[s]);
        Serial.print("  ");
        Serial.print(char(SerialData[s]));
        if (SerialData[s] != 255) {
                dataFile.print(char(SerialData[s]));
                Serial.println("  I");               
                }
                else
                {
                  Serial.println("E");
                
                }
        }
      }
    

    //dataFile.write(SerialData,197);
    memset(SerialData,0,sizeof(SerialData));

  }
  dataFile.close();
  Serial.println("Finito");

  // salva la tensione della batteria
  sprintf(filenb, "%02d%02d%02d%02d.bat", now.month(), now.day(), now.hour(), now.minute());
  String filenameb(filenb);

  File dataFileb = SD.open(filenameb, FILE_WRITE);
  if (dataFileb) {
    String bat = String(analogRead(A0), DEC);
    dataFileb.println(bat);
    dataFileb.close();
  }

  // spenge antenna GPS
  Serial.end();
  digitalWrite(7, LOW);
  delay(500);

  now = rtc.now();
  DateTime nextAlarm = now + TimeSpan(0, 4, 0, 0);


  delay(3000);
  rtc.setAlarm(ALM1_MATCH_HOURS, nextAlarm.minute(), nextAlarm.hour(), nextAlarm.second());   //set your wake-up time here
  rtc.alarmInterrupt(1, true);
  delay(3000); // wait for console
  attachInterrupt(0, wakeUp, LOW);       //use interrupt 0 (pin 2) and run function wakeUp when pin 2 gets LOW
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);   //arduino enters sleep mode here
  detachInterrupt(0);                                    //execution resumes from here after wake-up
  //When exiting the sleep mode we clear the alarm
  rtc.armAlarm(1, false);
  rtc.clearAlarm(1);
  rtc.alarmInterrupt(1, false);

  delay(3000);

}

mercoledì 2 gennaio 2019

Arduino GPS Datalogger

Ho provato a riconvertire un vecchio progetto in un GPS Datalogger
Lo shield aggiunto presenta un lettore SD Card ed un modulo DS1307 con il GPS collegato su Software Serial


I principali problemi:
1) pur acquisendo a 9600 dalla seriale ho scoperto che la scrittura della SD Card e' piu' lenta ..da questo la necessita' di fare un buffer. La scrittura di un blocco di byte e' molto piu' veloce di aprire il file in append, scrivere il byte, chiudere il file

2) il nome del file di acquisizione (che viene creato sulla base del tempo acquisito dall'RTC del DS1307) deve essere nel formato 8+3 del vecchio MSDOS (nel mio caso ho preferito usare due caratteri per mese, due carattteri per giorni, due caratteri per ora ed infine due caratteri per i minuti di inizio acquisizione)

3) questo semplice script segnala comunque che la memoria dell'Arduino e' ad limite critico

non ho ancora provato la funzione di deep sleep

--------------------------------------------------------------------------------------
// Low Power header
#include "LowPower.h"

// RTC headers
#include <Wire.h>
#include "RTClib.h"

// SD Card headers
#include <SPI.h>
#include <SD.h>

// Seriale dove invia il GPS
#include <SoftwareSerial.h>


RTC_DS1307 rtc;

const unsigned long SECOND = 1000;
const unsigned long HOUR = 3600 * SECOND;

unsigned long contatore = 0;
const unsigned int dim_buffer = 250;
byte SerialData[dim_buffer]; 

Sd2Card card;
SdVolume volume;
SdFile root;

const int chipSelect = 10;
char incomingByte = 0;   // for incoming serial data

char filen[10];
String filename;


// impostazione della seriale secondaria del GPS
SoftwareSerial mySerial(8, 9); // RX, TX

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

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

  // Inizializzazione SD
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    while (1);
  }

  mySerial.begin(9600);

  contatore = 0;

  Serial.println("Inizializzazione terminata.");
}

void loop() {

  DateTime now = rtc.now();
  
  sprintf(filen, "%02d%02d%02d%02d.txt", now.month(), now.day(), now.hour(), now.minute());
  String filename(filen);
  delay(2500);

  for (int i=0; i <= 100000; i++){
        contatore = 0;
        while (mySerial.available()) {
            SerialData[contatore] = mySerial.read();
            contatore++;
          }

      
        File dataFile = SD.open(filename, FILE_WRITE);
            if (dataFile) {
              for (int s=0; s < contatore; s++){
                  dataFile.print(char(SerialData[s]));
              }
              dataFile.close();
            }
          
  }
  Serial.println("Finita acquisizione");

  //entra in sleep mode
  delay(HOUR);
  //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF,SPI_OFF, USART0_OFF, TWI_OFF);

}

mercoledì 12 settembre 2018

Unbox GPS Tracker K8

Mi e' stato prestato un il GPS Tracker K8 perche' non presenta segni di vita. Ho provato a smontarlo per vedere se riuscivo a trovare qualche connettore di programmazione o seriale da cui poter interagire


In generale l'interazione avviene attravero una App FindME (che chiede peraltro permessi per nulla giustificati per esempio al GPS, alla fotocamera od al microfono) oppure inviando SMS alla numero della SIM interna. Da quanto risulta dalla scheda tecnica il posizionamento avviene tramite GPS o LBS (nel secondo caso i dati sono gestiti da un server esterno per la triangolazione delle antenne). La connessione non avviene direttamente tra il telefono ed il tracker ma tramite il server remoto della ditta produttrice via rete dati




Per aprire si alzano i due coperchi (in realta' quello giusto e' quello privo dell'adesivo dell'IMEI).Il disco metallico e' la calamita che e' incollata sopra la scheda


All'interno c'e' una LiPo da 380 mAh posta al di sotto della scheda. Nell'angolo in alto a destra della foto soprastante si vede un cubetto di colore piu' chiaro. Corrisponde al microfono. Sulla sinistra sulla flat di colore marrone ci sono i due led verde e rosso


Una volta rimossa la calamita e l'adesivo si osserva la scritta XMG001-E-GPS con sottostante quella che sembra la data di produzione. La parte superiore dell'integrato e' schermata da un coperchio metallico mentre al di sotto c'e' l'alloggiamento della SIM. Sono inoltre connesse due antenne s


Alla fine non sono riuscito a trovare nessuna connesione di debug.....ci sono delle piazzole libere sul pcb ma sono minuscole e senza scritte

PS ho controllato l'IMEI e non sembra un codice valido con lo schema ufficiale (il codice di controllo non risulta valido). Fra le altre cose ho scoperto che l'IMEI dei dispositivi e' di tipo prevedibile (non e' difficile forgiare un IMEI valido) e visto che ll'IMEI e' la username per il cloud e la password di default e' 123456 non e' difficile fare scherzi a utenti che non hanno modificato la password  di default

mercoledì 8 novembre 2017

Precise Point Positioning con Nexus 9 ed Android 7.1.1

Con l'introduzione delle API di misura Raw Gnss avevo gia' provato qualche tempo fa la applicazione di test fornita da Google, ovvero GnssLogger, ma senza un programma per gestire il file di log l'esperimento non era andato molto in la'

Adesso ho trovato su questo link uno script in Python che converte il file di log in un formato Rinex che puo' essere dato in pasto ad RTKLib per il post processing dei dati 

Attenzione: lo script funziona su Python 3.x e non su Python 2.x

Per la prova ho usato un tablet Nexus 9 (senza supporto cellulare) che come riportato da questo link e'
uno dei pochi dispositivi certificati per funzionare con le API Raw Gnss (anzi e' uno dei pochi con la piena funzionalita')

Si usa quindi GnssLogger (vedi link per l'apk) per l'acquisizione dei dati, lo script android_to_rinex.py per la conversione in Rinex ed RTKPost in modalita' PPP per il post processing


Il risultato finale non e' niente male perche' l'errore si riduce a circa 1 m sui tre assi ed i dati riescono a convergere in meno di un minuto

venerdì 20 ottobre 2017

GPS su Android N

Un esempio di uso del GPS su Android N con getione dei permessi. Evidenziate in giallo le parti di codice di gestione dei permessi


---------------------------------------------------
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.lucainnocenti.gpstest">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />


    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true"        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
---------------------------------------------------



---------------------------------------------------
package com.example.lucainnocenti.gpstest;

import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity implements LocationListener{

    LocationManager locationManager;
    String provider;
    public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;

    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        provider = locationManager.getBestProvider(new Criteria(), false);

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            checkLocationPermission();
            return;
        }
        Location location = locationManager.getLastKnownLocation(provider);

        if (location != null) {

            Log.i("Location Info", "Location achieved!");

        } else {

            Log.i("Location Info", "No location :(");

        }
    }

    @Override    protected void onResume() {
        super.onResume();

        if (checkLocationPermission()) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                locationManager.requestLocationUpdates(provider, 400, 1, this);
            }
        }

    }

    @Override    protected void onPause() {
        super.onPause();

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            checkLocationPermission();
            return;
        }
        locationManager.removeUpdates(this);

    }

    @Override    public void onLocationChanged(Location location) {

        Double lat = location.getLatitude();
        Double lng = location.getLongitude();

        Log.i("Location info: Lat", lat.toString());
        Log.i("Location info: Lng", lng.toString());

    }

    @Override    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override    public void onProviderEnabled(String provider) {

    }

    @Override    public void onProviderDisabled(String provider) {

    }

    public void getLocation(View view) {

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

            return;
        }
        Location location = locationManager.getLastKnownLocation(provider);

        onLocationChanged(location);


    }

    public boolean checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

            if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION)) {


                new AlertDialog.Builder(this)
                        .setTitle("Permesso GPS")
                        .setMessage("Permesso GPS")
                        .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                            @Override                            public void onClick(DialogInterface dialogInterface, int i) {
                                //Prompt the user once explanation has been shown                                ActivityCompat.requestPermissions(MainActivity.this,
                                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                        MY_PERMISSIONS_REQUEST_LOCATION);
                            }
                        })
                        .create()
                        .show();


            } else {
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION);
            }
            return false;
        } else {
            return true;
        }
    }

    @Override    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_LOCATION: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                        locationManager.requestLocationUpdates(provider, 400, 1, this);
                    }

                } else {

                    // permission denied,

                }
                return;
            }

        }
    }
}

Debugger integrato ESP32S3

Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede  nell'ID USB della porta Jtag. Nel modulo...