Esp32 One + L76X GPS Module tachograph

Posted by vMan on Fri, 17 Sep 2021 00:22:39 +0200

preface

Use esp32 one + GPS module + OLED small screen to make a portable low-power velocimeter

1, What is Esp32 One?

Esp32 One is a development board compatible with raspberry pie pin developed by Weixue electronics based on the custom pin of Xtensa LX6 dual core processor.

2, Using hardware

Esp32 One x1
L76X GPS Module x1
0.96 "Oled screen x1
Button x1
1k resistance x1
Some problems of DuPont line

3, Hardware construction steps

1. Connect Oled to Esp32 One.

The 4-pin 0.96-inch Oled screen is adopted, the SPI communication is connected to Esp32, and the corresponding Oled SCL SDA pin is connected to the SPI pin of Esp32 chip

Access GPOS.26 GPIO.33 through circuit diagram

Test the oled screen, develop it with arduino ide, and open the routine to test hello world

2. Connect L76X GPS Module to Esp32

The L76X GPS Module has five interfaces. The color of the cable is different from the usual pin line.
The green flat cable corresponds to the positive interface of VCC and is connected to positive 3.3~5v
The Yellow cable corresponds to GND grounding interface grounding
The blue cable corresponds to the TX port, which is connected through the hard serial port. Definition io33, corresponding pin 38
The black flat cable corresponds to the RX port, which is connected through the hard serial port. Define io32 and the corresponding pin is 35
The red cable corresponds to PPS (not connected here)

3. Key pull-down 1k Ω resistance

The opposite side of the key is welded with DuPont wire. At one end, 1k Ω resistance is used to pull down the resistance. One end is connected with positive 3.3v, one end is connected with io13, corresponding to GPIO pin 12, and one end is grounded.

4, Development code side

The program is developed using arduino ide. U8G2 library needs to be installed. Open arduino ide and select project → load library → manage library.
Search u8g2 download library.

1. Analyzing GPS database

void GPSRES() {
  while (MySerial_stm32.available()) {
    // comdata = "";// Cache reset
    int i = 0;
    memset(comdata, 0, sizeof(comdata));
    memset(rc, 0, sizeof(rc));
    //    Serial.print(MySerial_stm32.available());
    while (MySerial_stm32.available() > 0)//Whether there is data on the circular serial port
    {
      //      Serial.print(char(MySerial_stm32.read()));
      char res = char(MySerial_stm32.read());
      if (res == '$') {
        
        //        Serial.print("comdataLine:");
        //        Serial.println(comdata);
        //        Serial.println("over");
        if (strstr(comdata, "GNRMC")) {
          Serial.println("enter");
          Analyze(strstr(comdata, "GNRMC"));
        }
        Display();
        ButtonEvent();
        Serial.println("LoopRun");
        //char *rc2 = strstr(comdata, "GNRMC");
        //        Find_String(comdata, "GNRM", "\n", rc);

        i = 0;
        memset(comdata, 0, sizeof(comdata));
        //        delayMicroseconds (500) ;
      }
      else {
        comdata[i] += res;//Overlay data to comdata
        i++;

        //      delay(1);// Delay waiting for response

      }
      delayMicroseconds (1400) ;

    }
    //    Find_String(comdata, "$GNRM", "\n", rc);
    //    Serial.print("comdata:");
    //    Serial.println(comdata);
    //    Serial.println("over");
    //    Serial.println(rc);


  }
}

2. Screen display

void Display() {
  Serial.println(UTC);
  Serial.println(State);
  Serial.println(Speed);
  Serial.println(Direction);
  Serial.println(UTCDate);
  Serial.println(FinalSpeed);

  if (LastSpeed < FinalSpeed) {
    for (int i = 0; i < (millis() - lastSpeedTime) / SpeedUpdateTime; i++) {
      LastSpeed++;
      if (LastSpeed >= FinalSpeed) {
        break;
      }
    }

  }
  if (LastSpeed > FinalSpeed) {

    for (int i = 0; i < (millis() - lastSpeedTime) / SpeedUpdateTime; i++) {
      LastSpeed--;
      if (LastSpeed <= FinalSpeed) {
        break;
      }
    }
  }

  lastSpeedTime = millis();
  u8g2.firstPage();
  do {
    if (State == "A") {

      if ((millis() - lastUpdateTime) > 10000) {
        weeprom();

        lastUpdateTime = millis();
      }
      u8g2.setFont(u8g2_font_t0_12_tf);  // use chinese2 for all the glyphs of "Hello world"
      u8g2.setFontDirection(0);
      u8g2.setCursor(0, 12);
      u8g2.print(year);
      u8g2.print("/");
      u8g2.print(month);
      u8g2.print("/");
      u8g2.print(day);
      u8g2.print("    ");
      //Time display
      u8g2.print(hour);
      u8g2.print(":");
      u8g2.print(UTC.charAt(2));
      u8g2.print(UTC.charAt(3));
      u8g2.print(":");
      u8g2.print(UTC.charAt(4));
      u8g2.println(UTC.charAt(5));
      //        lastUpdateTime = millis();
      //      }
      u8g2.setFont(u8g2_font_luBS24_tn);
      u8g2.setFontDirection(0);
      u8g2.setCursor(0, 42);
      u8g2.print(LastSpeed);
      u8g2.setFont(u8g2_font_logisoso16_tr);  // use chinese2 for all the glyphs of "Hello world"
      u8g2.setFontDirection(0);
      u8g2.setCursor(68, 42);
      u8g2.print(F(" Km/h"));
      u8g2.setFont(u8g2_font_logisoso16_tr);
      u8g2.setCursor(0, 64);
      if (kmFlag == 0) {
        u8g2.setFont(u8g2_font_t0_12_tf);
        u8g2.setCursor(4, 60);
        u8g2.print("T");
        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(16, 64);
        u8g2.print((int)(Tkm + 0.5));
      }
      else if (kmFlag == 1) {
        u8g2.setFont(u8g2_font_t0_12_tf);  // use chinese2 for all the glyphs of "Hello world"
        u8g2.setCursor(4, 56);
        u8g2.print("1");
        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(16, 64);
        u8g2.print(km);
      }
      else if (kmFlag == 2) {
        u8g2.setFont(u8g2_font_t0_12_tf);  // use chinese2 for all the glyphs of "Hello world"
        u8g2.setCursor(4, 64);
        u8g2.print("2");
        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(16, 64);
        u8g2.print(km2);
      }
      u8g2.setFont(u8g2_font_t0_12_tf);
      u8g2.print("Km");


      if (FinalDirection != -1) {

        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(78, 64);
        if (FinalDirection >= 0 && FinalDirection < 22.5 || FinalDirection > 337.5 && FinalDirection <= 360)
          u8g2.print("N");
        else if (FinalDirection > 22.5 && FinalDirection < 67.5)
          u8g2.print("Ne");
        else if (FinalDirection > 67.5 && FinalDirection < 112.5)
          u8g2.print("E");
        else if (FinalDirection > 112.5 && FinalDirection < 157.5)
          u8g2.print("Se");
        else if (FinalDirection > 157.5 && FinalDirection < 202.5)
          u8g2.print("S");
        else if (FinalDirection > 202.5 && FinalDirection < 247.5)
          u8g2.print("Sw");
        else if (FinalDirection > 247.5 && FinalDirection < 292.5)
          u8g2.print("W");
        else if (FinalDirection > 292.5 && FinalDirection < 337.5)
          u8g2.print("Nw");
        u8g2.setFont(u8g2_font_t0_12_tf);
        u8g2.setCursor(101, 64);

        u8g2.print("^");
        u8g2.print(FinalDirection);
      }
      else {
        u8g2.setFont(u8g2_font_t0_12_tf);
        u8g2.setCursor(78, 60);
        u8g2.print("calculat");
      }
    }
    else {
      if (year != 0) {
        u8g2.setFont(u8g2_font_t0_12_tf);  // use chinese2 for all the glyphs of "Hello world"
        u8g2.setFontDirection(0);
        u8g2.setCursor(0, 12);
        u8g2.print(year);
        u8g2.print("/");
        u8g2.print(month);
        u8g2.print("/");
        u8g2.print(day);
        u8g2.print("    ");
        //Time display
        u8g2.print(hour);
        u8g2.print(":");
        u8g2.print(UTC.charAt(2));
        u8g2.print(UTC.charAt(3));
        u8g2.print(":");
        u8g2.print(UTC.charAt(4));
        u8g2.println(UTC.charAt(5));
      }
      if (kmFlag == 0) {
        u8g2.setFont(u8g2_font_t0_12_tf);
        u8g2.setCursor(4, 60);
        u8g2.print("T");
        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(16, 64);
        u8g2.print((int)(Tkm + 0.5));
      }
      else if (kmFlag == 1) {
        u8g2.setFont(u8g2_font_t0_12_tf);  // use chinese2 for all the glyphs of "Hello world"
        u8g2.setCursor(4, 56);
        u8g2.print("1");
        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(16, 64);
        u8g2.print(km);
      }
      else if (kmFlag == 2) {
        u8g2.setFont(u8g2_font_t0_12_tf);  // use chinese2 for all the glyphs of "Hello world"
        u8g2.setCursor(4, 64);
        u8g2.print("2");
        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(16, 64);
        u8g2.print(km2);
      }
      u8g2.setFont(u8g2_font_t0_12_tf);
      u8g2.print("Km");
      u8g2.setFont(u8g2_font_logisoso16_tr);  // use chinese2 for all the glyphs of "Hello world"
      u8g2.setFontDirection(0);
      u8g2.setCursor(16, 40);
      if (flag == 0)
        u8g2.print(F("Find GPS")); // write something to the internal memory
      else if (flag == 1)
        u8g2.print(F("Find GPS.")); // write something to the internal memory
      else if (flag == 2)
        u8g2.print(F("Find GPS..")); // write something to the internal memory
      else if (flag == 3)
        u8g2.print(F("Find GPS...")); // write something to the internal memory
      if ((millis() - lastUpdateTime) > 500) {
        flag++;
        digitalWrite(pin, state);
        state = !state;
        lastUpdateTime = millis();
      }

      if (flag >= 4) {
        flag = 0;
      }
    }
  } while ( u8g2.nextPage() );
}

3. GPS velocity analysis

void Analyze(char *res) {
  //  char *Res = (char*)malloc(512);
  //  strcpy(Res,res);
  char arr[512] = {0};
  char* tmp = NULL;
  memcpy(arr, res, sizeof(arr));
  Serial.print("rcData:");
  Serial.println(arr);
  Serial.println("over");
  if (arr)//If comdata has data
  {
    int j = 0;
    for (int i = 1; i < 128; i++) {

      if (arr[i] == ',') {
        pos[j] = i;
        j++;
      }
    }
    UTC = "";
    State = "";
    Speed = "";
    Direction = "";
    UTCDate = "";
    int i;
    Serial.println(j);
    if (j == 12) {

      Serial.println(rc);
      for (i = pos[0] + 1; i < pos[1]; i++) {
        UTC += arr[i];
      }
      for (int i = pos[1] + 1; i < pos[2]; i++) {
        State += arr[i];
      }
      for (int i = pos[6] + 1; i < pos[7]; i++) {
        Speed += arr[i];
      }
      for (int i = pos[7] + 1; i < pos[8]; i++) {
        Direction += arr[i];
      }
      for (int i = pos[8] + 1; i < pos[9]; i++) {
        UTCDate += arr[i];
      }
      Serial.print("UTC:");
      Serial.println(UTC);
      Serial.print("State:");
      Serial.println(State);
      Serial.print("Speed:");
      Serial.println(Speed);
      Serial.print("Direction:");
      Serial.println(Direction);
      Serial.print("UTCDate:");
      Serial.println(UTCDate);
      if (UTC.length() > 0) {
        utc_to_local_time(8);
      }
      else {
        Serial.println("noUTC");
        year = 0; //particular year
        month = 0; //month
        day = 0; //date
        hour = 0; //hour
        mins = 0; //minute
        sec = 0; //second
      }

    }
  }

  if (State == "V") {
    speedflag = 1;
    Serial.println("NoSite");
  }
  else {
    if (speedflag == 1) {
      lastTime = millis();
      speedflag = 0;
    }
    int str_len = Speed.length() + 1;
    Speed.toCharArray(Speeds, str_len);
    FinalSpeed = (int)(atof(Speeds) * 1.852 + 0.5);
    Serial.println(FinalSpeed);
    int str_len2 = Direction.length() + 1;
    if (Direction.length() == 0) {
      FinalDirection = -1;
    }
    else {
      Direction.toCharArray(Directions, str_len2);
      FinalDirection = (int)(atof(Directions) + 0.5);
    }

    km += (((millis() - lastTime) * (atof(Speeds) * 1.852)) / 3600000);
    km2 += (((millis() - lastTime) * (atof(Speeds) * 1.852)) / 3600000);
    Tkm += (((millis() - lastTime) * (atof(Speeds) * 1.852)) / 3600000);
    lastTime = millis();
    SpeedUpdateTime = 100 - abs(FinalSpeed - LastSpeed);
    if (SpeedUpdateTime >= 100 || SpeedUpdateTime <= 0) {
      SpeedUpdateTime = 1;
    }
  }
}

4. GPS international time analysis

//UTC time is converted to time in any time zone. If it is converted to Beijing time, timezone can be transmitted to 8
void utc_to_local_time(int timezone)
{
  year = 2000 + (int)(UTCDate.charAt(4) - '0') * 10 + (int)(UTCDate.charAt(5) - '0');
  month = (int)(UTCDate.charAt(2) - '0') * 10 + (int)(UTCDate.charAt(3) - '0');
  day = (int)(UTCDate.charAt(0) - '0') * 10 + (int)(UTCDate.charAt(1) - '0');
  hour = (int)(UTC.charAt(0) - '0') * 10 + (int)(UTC.charAt(1) - '0');
  int lastday = 0;      //last day of this month
  int lastlastday = 0;    //last day of last month

  hour   = hour + timezone;

  //January, February, March, April, may, June, July, August, September, October, November and December
  if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)
  {
    lastday = 31;//Days of this month
    lastlastday = 30;//The number of days of last month should be added here

    if (month == 3)
    {
      if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)) //if this is lunar year
        lastlastday = 29;
      else
        lastlastday = 28;
    }

    if (month == 8 || month == 1) //It should be August and January, because the number of days in the previous month (July and December) of August and January is 31 days
      lastlastday = 31;
  }
  else if (month == 4 || month == 6 || month == 9 || month == 11)
  {
    lastday = 30;
    lastlastday = 31;
  }
  else
  {
    lastlastday = 31;

    if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0))
      lastday = 29;
    else
      lastday = 28;
  }

  if (hour >= 24) // if >24, day+1
  {
    hour -= 24;
    day += 1;
    if (day > lastday) // next month, day-lastday of this month
    {
      day -= lastday;
      month += 1;
      if (month > 12) // next year, month-12
      {
        month -= 12;
        year += 1;
      }
    }
  }

  if (hour < 0) // if <0, day-1
  {
    hour += 24;
    day -= 1;
    if (day < 1) // month-1, day=last day of last month
    {
      day = lastlastday;
      month -= 1;
      if (month < 1) // last year, month=12
      {
        month = 12;
        year -= 1;
      }
    }
  }
}

5. Button switching function

void ButtonEvent(){
  if (digitalRead(26)) {
    delay(10);
    btn_flag = 1;
    unsigned long i = millis();
    Serial.println(i);
    Serial.println(millis());
    while (digitalRead(26) && btn_flag == 1) {
      // wdt_reset();
      if (millis() - i > 2500) {
        if (kmFlag == 1)
          km = 0;
        else if (kmFlag == 2)
          km2 = 0;
        Serial.print("ResKm:");
        Serial.println(kmFlag);
        Serial.println(km);
        Serial.println(km2);
        weeprom();
        btn_flag = 0;
        //Display();
        delay(100);
      }
    }
    if (millis() - i < 500) {
      kmFlag++;
      if (kmFlag >= 3) {
        kmFlag = 0;
      }
      Serial.println(kmFlag);
    }
  }
}

Overall code

#include <Arduino.h>
#include <U8g2lib.h>
#include "EEPROM.h"
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
#include <stdio.h>

#Include < hardwareserial. H > / / import the ESP32 serial port operation library. Using this library, we can map the serial port to other pins for use
// #include <avr/wdt.h>
#define SDA_PIN A4
#define SCL_PIN A5
int FirstDownload = 0;
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

HardwareSerial MySerial_stm32(1);
unsigned long lastUpdateTime = 0;          //Record the last time the screen was refreshed
unsigned long lastSpeedTime = 0;          //Record the last time the screen was refreshed
unsigned long SpeedUpdateTime = 10;
const unsigned long updateInterval = 200; // Screen refresh interval 200ms
int x;//Cache function
//char *rc = NULL;// String function
char comdata[256] = "";
char rc[128] = "";
int pos[12];
String UTC = "";
String State = "";
String Speed = "";
char Speeds[20] = "";
int FinalSpeed = 0;
int LastSpeed = 0;
String Direction = "";
char Directions[20] = "";
int FinalDirection = 0;
String UTCDate = "";
int flag = 0;
double Tkm = 0;
double km = 0;
double km2 = 0;
char kms[10] = "";
unsigned long lastTime = 0;
int speedflag = 1;
unsigned short kmFlag = 0;
int eeAddress = 0;
unsigned long year;  //particular year
unsigned int month;  //month
unsigned int day; //date
unsigned int hour;   //hour
unsigned int mins;  //minute
unsigned long sec;  //second

int pin = 13;
volatile int state = LOW;
unsigned int duration;

int btn_flag = 1;

void setup()
{

  //Define baud rate
  //  wdt_enable(WDTO_4S);
  //  Serial2.begin
  Serial.begin(115200);
  MySerial_stm32.begin(9600, SERIAL_8N1, 33, 32);
  MySerial_stm32.setRxBufferSize(1024);
  u8g2.begin();
  u8g2.enableUTF8Print();
  pinMode(26, INPUT);
  pinMode(pin, OUTPUT);
  if (FirstDownload == 1) {
    EEPROM.writeDouble(eeAddress, Tkm);
    eeAddress += sizeof(double);
    EEPROM.writeDouble(eeAddress, km);
    eeAddress += sizeof(double);
    EEPROM.writeDouble(eeAddress, km2);
    eeAddress += sizeof(double);
    EEPROM.writeInt(eeAddress, kmFlag);
  }
  Tkm = EEPROM.readDouble(eeAddress);
  eeAddress = sizeof(double);
  km = EEPROM.readDouble(eeAddress);
  eeAddress += sizeof(double);
  km2 = EEPROM.readDouble(eeAddress);
  eeAddress += sizeof(double);
  kmFlag = EEPROM.readInt(eeAddress);

  Serial.println(Tkm);
  Serial.println(km);
  Serial.println(km2);
  Serial.println(kmFlag);
}
void loop()
{
  GPSRES();

  // wdt_reset();
  
}

void GPSRES() {
  while (MySerial_stm32.available()) {
    // comdata = "";// Cache reset
    int i = 0;
    memset(comdata, 0, sizeof(comdata));
    memset(rc, 0, sizeof(rc));
    //    Serial.print(MySerial_stm32.available());
    while (MySerial_stm32.available() > 0)//Whether there is data on the circular serial port
    {
      //      Serial.print(char(MySerial_stm32.read()));
      char res = char(MySerial_stm32.read());
      if (res == '$') {
        
        //        Serial.print("comdataLine:");
        //        Serial.println(comdata);
        //        Serial.println("over");
        if (strstr(comdata, "GNRMC")) {
          Serial.println("enter");
          Analyze(strstr(comdata, "GNRMC"));
        }
        Display();
        ButtonEvent();
        Serial.println("LoopRun");
        //char *rc2 = strstr(comdata, "GNRMC");
        //        Find_String(comdata, "GNRM", "\n", rc);

        i = 0;
        memset(comdata, 0, sizeof(comdata));
        //        delayMicroseconds (500) ;
      }
      else {
        comdata[i] += res;//Overlay data to comdata
        i++;

        //      delay(1);// Delay waiting for response

      }
      delayMicroseconds (1400) ;

    }
    //    Find_String(comdata, "$GNRM", "\n", rc);
    //    Serial.print("comdata:");
    //    Serial.println(comdata);
    //    Serial.println("over");
    //    Serial.println(rc);


  }
}
void Analyze(char *res) {
  //  char *Res = (char*)malloc(512);
  //  strcpy(Res,res);
  char arr[512] = {0};
  char* tmp = NULL;
  memcpy(arr, res, sizeof(arr));
  Serial.print("rcData:");
  Serial.println(arr);
  Serial.println("over");
  if (arr)//If comdata has data
  {
    int j = 0;
    for (int i = 1; i < 128; i++) {

      if (arr[i] == ',') {
        pos[j] = i;
        j++;
      }
    }
    UTC = "";
    State = "";
    Speed = "";
    Direction = "";
    UTCDate = "";
    int i;
    Serial.println(j);
    if (j == 12) {

      Serial.println(rc);
      for (i = pos[0] + 1; i < pos[1]; i++) {
        UTC += arr[i];
      }
      for (int i = pos[1] + 1; i < pos[2]; i++) {
        State += arr[i];
      }
      for (int i = pos[6] + 1; i < pos[7]; i++) {
        Speed += arr[i];
      }
      for (int i = pos[7] + 1; i < pos[8]; i++) {
        Direction += arr[i];
      }
      for (int i = pos[8] + 1; i < pos[9]; i++) {
        UTCDate += arr[i];
      }
      Serial.print("UTC:");
      Serial.println(UTC);
      Serial.print("State:");
      Serial.println(State);
      Serial.print("Speed:");
      Serial.println(Speed);
      Serial.print("Direction:");
      Serial.println(Direction);
      Serial.print("UTCDate:");
      Serial.println(UTCDate);
      if (UTC.length() > 0) {
        utc_to_local_time(8);
      }
      else {
        Serial.println("noUTC");
        year = 0; //particular year
        month = 0; //month
        day = 0; //date
        hour = 0; //hour
        mins = 0; //minute
        sec = 0; //second
      }

    }
  }

  if (State == "V") {
    speedflag = 1;
    Serial.println("NoSite");
  }
  else {
    if (speedflag == 1) {
      lastTime = millis();
      speedflag = 0;
    }
    int str_len = Speed.length() + 1;
    Speed.toCharArray(Speeds, str_len);
    FinalSpeed = (int)(atof(Speeds) * 1.852 + 0.5);
    Serial.println(FinalSpeed);
    int str_len2 = Direction.length() + 1;
    if (Direction.length() == 0) {
      FinalDirection = -1;
    }
    else {
      Direction.toCharArray(Directions, str_len2);
      FinalDirection = (int)(atof(Directions) + 0.5);
    }

    km += (((millis() - lastTime) * (atof(Speeds) * 1.852)) / 3600000);
    km2 += (((millis() - lastTime) * (atof(Speeds) * 1.852)) / 3600000);
    Tkm += (((millis() - lastTime) * (atof(Speeds) * 1.852)) / 3600000);
    lastTime = millis();
    SpeedUpdateTime = 100 - abs(FinalSpeed - LastSpeed);
    if (SpeedUpdateTime >= 100 || SpeedUpdateTime <= 0) {
      SpeedUpdateTime = 1;
    }
  }
}
void Display() {
  Serial.println(UTC);
  Serial.println(State);
  Serial.println(Speed);
  Serial.println(Direction);
  Serial.println(UTCDate);
  Serial.println(FinalSpeed);

  if (LastSpeed < FinalSpeed) {
    for (int i = 0; i < (millis() - lastSpeedTime) / SpeedUpdateTime; i++) {
      LastSpeed++;
      if (LastSpeed >= FinalSpeed) {
        break;
      }
    }

  }
  if (LastSpeed > FinalSpeed) {

    for (int i = 0; i < (millis() - lastSpeedTime) / SpeedUpdateTime; i++) {
      LastSpeed--;
      if (LastSpeed <= FinalSpeed) {
        break;
      }
    }
  }

  lastSpeedTime = millis();
  u8g2.firstPage();
  do {
    if (State == "A") {

      if ((millis() - lastUpdateTime) > 10000) {
        weeprom();

        lastUpdateTime = millis();
      }
      u8g2.setFont(u8g2_font_t0_12_tf);  // use chinese2 for all the glyphs of "Hello world"
      u8g2.setFontDirection(0);
      u8g2.setCursor(0, 12);
      u8g2.print(year);
      u8g2.print("/");
      u8g2.print(month);
      u8g2.print("/");
      u8g2.print(day);
      u8g2.print("    ");
      //Time display
      u8g2.print(hour);
      u8g2.print(":");
      u8g2.print(UTC.charAt(2));
      u8g2.print(UTC.charAt(3));
      u8g2.print(":");
      u8g2.print(UTC.charAt(4));
      u8g2.println(UTC.charAt(5));
      //        lastUpdateTime = millis();
      //      }
      u8g2.setFont(u8g2_font_luBS24_tn);
      u8g2.setFontDirection(0);
      u8g2.setCursor(0, 42);
      u8g2.print(LastSpeed);
      u8g2.setFont(u8g2_font_logisoso16_tr);  // use chinese2 for all the glyphs of "Hello world"
      u8g2.setFontDirection(0);
      u8g2.setCursor(68, 42);
      u8g2.print(F(" Km/h"));
      u8g2.setFont(u8g2_font_logisoso16_tr);
      u8g2.setCursor(0, 64);
      if (kmFlag == 0) {
        u8g2.setFont(u8g2_font_t0_12_tf);
        u8g2.setCursor(4, 60);
        u8g2.print("T");
        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(16, 64);
        u8g2.print((int)(Tkm + 0.5));
      }
      else if (kmFlag == 1) {
        u8g2.setFont(u8g2_font_t0_12_tf);  // use chinese2 for all the glyphs of "Hello world"
        u8g2.setCursor(4, 56);
        u8g2.print("1");
        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(16, 64);
        u8g2.print(km);
      }
      else if (kmFlag == 2) {
        u8g2.setFont(u8g2_font_t0_12_tf);  // use chinese2 for all the glyphs of "Hello world"
        u8g2.setCursor(4, 64);
        u8g2.print("2");
        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(16, 64);
        u8g2.print(km2);
      }
      u8g2.setFont(u8g2_font_t0_12_tf);
      u8g2.print("Km");


      if (FinalDirection != -1) {

        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(78, 64);
        if (FinalDirection >= 0 && FinalDirection < 22.5 || FinalDirection > 337.5 && FinalDirection <= 360)
          u8g2.print("N");
        else if (FinalDirection > 22.5 && FinalDirection < 67.5)
          u8g2.print("Ne");
        else if (FinalDirection > 67.5 && FinalDirection < 112.5)
          u8g2.print("E");
        else if (FinalDirection > 112.5 && FinalDirection < 157.5)
          u8g2.print("Se");
        else if (FinalDirection > 157.5 && FinalDirection < 202.5)
          u8g2.print("S");
        else if (FinalDirection > 202.5 && FinalDirection < 247.5)
          u8g2.print("Sw");
        else if (FinalDirection > 247.5 && FinalDirection < 292.5)
          u8g2.print("W");
        else if (FinalDirection > 292.5 && FinalDirection < 337.5)
          u8g2.print("Nw");
        u8g2.setFont(u8g2_font_t0_12_tf);
        u8g2.setCursor(101, 64);

        u8g2.print("^");
        u8g2.print(FinalDirection);
      }
      else {
        u8g2.setFont(u8g2_font_t0_12_tf);
        u8g2.setCursor(78, 60);
        u8g2.print("calculat");
      }
    }
    else {
      if (year != 0) {
        u8g2.setFont(u8g2_font_t0_12_tf);  // use chinese2 for all the glyphs of "Hello world"
        u8g2.setFontDirection(0);
        u8g2.setCursor(0, 12);
        u8g2.print(year);
        u8g2.print("/");
        u8g2.print(month);
        u8g2.print("/");
        u8g2.print(day);
        u8g2.print("    ");
        //Time display
        u8g2.print(hour);
        u8g2.print(":");
        u8g2.print(UTC.charAt(2));
        u8g2.print(UTC.charAt(3));
        u8g2.print(":");
        u8g2.print(UTC.charAt(4));
        u8g2.println(UTC.charAt(5));
      }
      if (kmFlag == 0) {
        u8g2.setFont(u8g2_font_t0_12_tf);
        u8g2.setCursor(4, 60);
        u8g2.print("T");
        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(16, 64);
        u8g2.print((int)(Tkm + 0.5));
      }
      else if (kmFlag == 1) {
        u8g2.setFont(u8g2_font_t0_12_tf);  // use chinese2 for all the glyphs of "Hello world"
        u8g2.setCursor(4, 56);
        u8g2.print("1");
        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(16, 64);
        u8g2.print(km);
      }
      else if (kmFlag == 2) {
        u8g2.setFont(u8g2_font_t0_12_tf);  // use chinese2 for all the glyphs of "Hello world"
        u8g2.setCursor(4, 64);
        u8g2.print("2");
        u8g2.setFont(u8g2_font_logisoso16_tr);
        u8g2.setCursor(16, 64);
        u8g2.print(km2);
      }
      u8g2.setFont(u8g2_font_t0_12_tf);
      u8g2.print("Km");
      u8g2.setFont(u8g2_font_logisoso16_tr);  // use chinese2 for all the glyphs of "Hello world"
      u8g2.setFontDirection(0);
      u8g2.setCursor(16, 40);
      if (flag == 0)
        u8g2.print(F("Find GPS")); // write something to the internal memory
      else if (flag == 1)
        u8g2.print(F("Find GPS.")); // write something to the internal memory
      else if (flag == 2)
        u8g2.print(F("Find GPS..")); // write something to the internal memory
      else if (flag == 3)
        u8g2.print(F("Find GPS...")); // write something to the internal memory
      if ((millis() - lastUpdateTime) > 500) {
        flag++;
        digitalWrite(pin, state);
        state = !state;
        lastUpdateTime = millis();
      }

      if (flag >= 4) {
        flag = 0;
      }
    }
  } while ( u8g2.nextPage() );
}
void weeprom() {
  Serial.println("w to eeprom");
  digitalWrite(pin, state);
  state = !state;
  EEPROM.writeInt(eeAddress, kmFlag);
  eeAddress -= sizeof(double);
  EEPROM.writeDouble(eeAddress, km2);
  eeAddress -= sizeof(double);
  EEPROM.writeDouble(eeAddress, km);
  EEPROM.writeDouble(0, Tkm);
}

void ButtonEvent(){
  if (digitalRead(26)) {
    delay(10);
    btn_flag = 1;
    unsigned long i = millis();
    Serial.println(i);
    Serial.println(millis());
    while (digitalRead(26) && btn_flag == 1) {
      // wdt_reset();
      if (millis() - i > 2500) {
        if (kmFlag == 1)
          km = 0;
        else if (kmFlag == 2)
          km2 = 0;
        Serial.print("ResKm:");
        Serial.println(kmFlag);
        Serial.println(km);
        Serial.println(km2);
        weeprom();
        btn_flag = 0;
        //Display();
        delay(100);
      }
    }
    if (millis() - i < 500) {
      kmFlag++;
      if (kmFlag >= 3) {
        kmFlag = 0;
      }
      Serial.println(kmFlag);
    }
  }
}
//UTC time is converted to time in any time zone. If it is converted to Beijing time, timezone can be transmitted to 8
void utc_to_local_time(int timezone)
{
  year = 2000 + (int)(UTCDate.charAt(4) - '0') * 10 + (int)(UTCDate.charAt(5) - '0');
  month = (int)(UTCDate.charAt(2) - '0') * 10 + (int)(UTCDate.charAt(3) - '0');
  day = (int)(UTCDate.charAt(0) - '0') * 10 + (int)(UTCDate.charAt(1) - '0');
  hour = (int)(UTC.charAt(0) - '0') * 10 + (int)(UTC.charAt(1) - '0');
  int lastday = 0;      //last day of this month
  int lastlastday = 0;    //last day of last month

  hour   = hour + timezone;

  //January, February, March, April, may, June, July, August, September, October, November and December
  if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)
  {
    lastday = 31;//Days of this month
    lastlastday = 30;//The number of days of last month should be added here

    if (month == 3)
    {
      if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)) //if this is lunar year
        lastlastday = 29;
      else
        lastlastday = 28;
    }

    if (month == 8 || month == 1) //It should be August and January, because the number of days in the previous month (July and December) of August and January is 31 days
      lastlastday = 31;
  }
  else if (month == 4 || month == 6 || month == 9 || month == 11)
  {
    lastday = 30;
    lastlastday = 31;
  }
  else
  {
    lastlastday = 31;

    if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0))
      lastday = 29;
    else
      lastday = 28;
  }

  if (hour >= 24) // if >24, day+1
  {
    hour -= 24;
    day += 1;
    if (day > lastday) // next month, day-lastday of this month
    {
      day -= lastday;
      month += 1;
      if (month > 12) // next year, month-12
      {
        month -= 12;
        year += 1;
      }
    }
  }

  if (hour < 0) // if <0, day-1
  {
    hour += 24;
    day -= 1;
    if (day < 1) // month-1, day=last day of last month
    {
      day = lastlastday;
      month -= 1;
      if (month < 1) // last year, month=12
      {
        month = 12;
        year -= 1;
      }
    }
  }
}

5, Overall operation effect

There is some deviation between the measured speed and the mobile phone speed measurement. The reason is that the signal frequency received by GPS is slow. Using more advanced GPS can solve the problem.

summary

The key to the development of Esp32 One is to find the corresponding pin. The programming pin can choose ESP IDF development.
On the basis of this project, the longitude and latitude can be analyzed. In addition, Nb IOT transmits the longitude and latitude information to the cloud, which can be forwarded to the mobile APP
For specific applications, you can view the GPS mobile phone positioning written before.
On the basis of this project, it can be further extended. Considering that there is no satellite positioning signal indoors, esp32 One can be used to connect WiFi judgment equipment. When there is no WiFi indoors and outdoors, Nb IOT equipment can be used to connect to the cloud to transmit longitude and latitude information, speed information and time information to the cloud for processing. Later development history track, real-time speed display, etc.

Topics: IoT stm32