Advertisement
DuboisP

ESP32_Nmea_GNSS

Jan 27th, 2025
111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.38 KB | Source Code | 0 0
  1. /*********
  2.  
  3.    target :    Heltec WiFi Lora 32(V2)
  4.                LCD2004 display on I2C
  5.                buttons * 2
  6.                voltage regulator LM2596D
  7.                GPS ublox neo8m + antenna
  8.  
  9.    provides :  satellites number used and viewed
  10.                speed in knots and kilometers/hour
  11.                cap in orientation and degrees
  12.                latitude in decimal degrees and degrees minutes seconds
  13.                longitude in decimal degrees and degrees minutes seconds
  14.                battery voltage
  15.                UTC date time by GPS
  16.                
  17.    OTA examples :    https://www.aranacorp.com/fr/programmer-un-esp32-via-wifi-avec-lide-arduino-ota/
  18.                      https://randomnerdtutorials.com/esp32-over-the-air-ota-programming/
  19.                      https://randomnerdtutorials.com/esp32-ota-over-the-air-arduino/
  20.                  
  21.    SPIFFS examples : https://randomnerdtutorials.com/install-esp32-filesystem-uploader-arduino-ide/
  22.                      https://www.programmingelectronics.com/spiffs-esp32/
  23.  
  24.    TinyGPS+ explanations : https://arduiniana.org/2013/09/tinygps-a-new-view-of-global-positioning/                  
  25.  
  26. *********/
  27.  
  28. // Import required libraries
  29. // if the library is global   use <>
  30. // if the library is local    use ""
  31. #include <ArduinoOTA.h>
  32. #include <ESPmDNS.h>
  33. #include <rom/ets_sys.h>
  34. #include <heltec.h>              // include <Arduino.h>, <SPI.h>, <Wire.h>
  35. #include <LiquidCrystal_I2C.h>
  36. #include <SPIFFS.h>
  37. #include <time.h>
  38. #include <HT_TinyGPS++.h>        // same as TinyGPSPlus
  39. #include <WiFi.h>
  40. #include <WiFiUdp.h>
  41. #include <Wire.h>
  42.  
  43. #define BAND  868E6              //you can set band here directly,e.g. 868E6,915E6
  44.  
  45. #define button0Pin      0        // GPIO 0 or PRG button
  46. #define batteryPin      37       // read battery tension 37 or A44.05/1.25
  47. #define batteryCoeff    0.0029   // 0.0029053 empirical coefficient from analog to tension. battery tension divided par analog value
  48. #define startPin        12       // GPIO 12
  49. #define sleepPin      13         // GPIO 13
  50. //#define touchPin     14          // GPIO 14
  51. //#define Treshold     -1          // touchpad sensibility  
  52. #define VextControlPin  21       // GPIO 21
  53. #define BattReadLoop    10
  54. #define BuiltInLed      25       // GPIO 25
  55.  
  56. #define tempo           2000     // 2 seconds
  57.  
  58. // HardwareSerial on passe les GPIO !
  59. #define rx0GPIO  3               // pin 34
  60. #define tx0GPIO  1               // pin 35
  61. //#define rx1GPIO 14               // not existing on Heltec WiFi Lora 32(V2), use HardweSerial if needed
  62. //#define tx1GPIO 12               // not existing on Heltec WiFi Lora 32(V2), use HardweSerial if needed
  63. #define rx2GPIO 16               // pin 27
  64. #define tx2GPIO 17               // pin 28
  65. #define baudrateSerial  38400    // vitesse communication Arduino - PC  
  66. #define baudrateSerial1 38400
  67. #define baudrateSerial2 38400
  68. #define GpsPort2 Serial2
  69.  
  70. #define LAT 0
  71. #define LNG 1
  72.  
  73. #define USING_SPIFFS                   // else using SD Card
  74.  
  75. #define SDA_GPIO   4  
  76. #define SCL_GPIO  15  
  77. #define numCols   20                   // for lcd
  78. #define numRows   4
  79. LiquidCrystal_I2C lcd(0x27, numCols, numRows);    // use this for I2C LCD.
  80.  
  81. // WIFI_MODE_NULL
  82. // WIFI_MODE_STA                       // WiFi station mode
  83. // WIFI_MODE_AP                        // WiFi soft-AP mode
  84. // WIFI_MODE_APSTA                     // WiFi station + soft-AP mode
  85.  
  86. #define AP_MODE
  87. #if defined AP_MODE
  88.    #define OCTET3 4
  89.    IPAddress localIP(192,168,OCTET3,10);
  90.    IPAddress subnetMaskIP(255,255,255,0);
  91.    IPAddress gatewayIP(192,168,OCTET3,99);
  92.    IPAddress broadcastIP;
  93.    IPAddress dhcp_startIP(192,168,OCTET3,11);  
  94. #else
  95.    IPAddress localIP, subnetMaskIP, gatewayIP, broadcastIP;
  96. #endif
  97. #define UDP_PORT 2010                 // 2010 used by ESP32_AIS_Nmea, but no problem
  98.  
  99. bool bLCDDisplay = false,
  100.    bDisplayFlap = true,
  101.    bWeHaveAFix = false,
  102.    bSpeed = false;
  103. uint32_t newMillis, oldMillis, oldMillis_x;
  104.  
  105. // objects delcarations
  106. NetworkUDP udp;                                 //The udp library class
  107. TinyGPSPlus GPS;
  108. TinyGPSCustom UsedSats(GPS, "GNGNS", 7);        // Fix data    total number of satellites in use   00-99
  109. TinyGPSCustom GAViewedSats(GPS, "GAGSV", 3);    // Galileo EU  total number of satellites in view
  110. TinyGPSCustom GBViewedSats(GPS, "GBGSV", 3);    // Beidou CH   total number of satellites in view
  111. TinyGPSCustom GLViewedSats(GPS, "GLGSV", 3);    // Glonass RU  total number of satellites in view
  112. TinyGPSCustom GPViewedSats(GPS, "GPGSV", 3);    // GPS USA     total number of satellites in view
  113.  
  114. // function prototype declarations
  115. String DD_to_DMS(float fValue);
  116. String * DecimalDegrees_to_DegreesMinutesSeconds(float fLat, float fLng);
  117. void Display_Init(void);
  118. void DisplayIMessage(String sMessage, bool bClear, bool bCRLN);
  119. void OTA_Init(void);
  120. void Storage_Init(void);
  121. void WiFi_Init(void);
  122. // end declarations
  123.  
  124. void IRAM_ATTR onSleepButtonEvent() {
  125.    esp_deep_sleep_start();
  126. }
  127.  
  128. void setup() {
  129.    Heltec.begin(false /*DisplayEnable Enable*/, false /*LoRa Enable*/, true /*Serial Enable*/, false /*LoRa use PABOOST*/, BAND /*LoRa RF working band*/);
  130.    Wire.begin(SDA_GPIO, SCL_GPIO);                 // not started by Heltec.begin(true) so it's needed
  131.  
  132.    pinMode(sleepPin, INPUT_PULLDOWN);                                        // GPIO13
  133.    attachInterrupt(digitalPinToInterrupt(sleepPin), onSleepButtonEvent, FALLING);
  134.  
  135.    // Configure le GPIO x comme source de réveil quand la tension vaut 3.3V
  136.    // esp_sleep_enable_ext0_wakeup(GPIO_NUM_12, HIGH);                     // normal, un seul GPIO
  137.    pinMode(startPin, INPUT_PULLDOWN);                                        // GPIO12
  138.    esp_sleep_enable_ext0_wakeup((gpio_num_t)startPin, HIGH);               // alternative, un seul GPIO
  139.    // esp_sleep_enable_ext1_wakeup(0x1000, ESP_EXT1_WAKEUP_ANY_HIGH);      // 2^12 = 0x1000 . si plusieurs GPIO 2^GPIOn1 + 2*^GPIOn1 + 2^GPIOnx >> hex
  140.  
  141.    Serial.begin(baudrateSerial);                                           // Serial0
  142.    GpsPort2.begin(baudrateSerial2, SERIAL_8N1, rx2GPIO, tx2GPIO);          // GPS connection
  143.    
  144.    Display_Init();
  145.    WiFi_Init();
  146.    OTA_Init();
  147.    
  148.    while (GpsPort2.available() > 0)
  149.       GPS.encode(GpsPort2.read());
  150.    if (GPS.charsProcessed() < 256)
  151.       DisplayIMessage("GPS not detected", false, true);
  152.    else
  153.       DisplayIMessage("GPS detected", false, true);
  154.    
  155.    Storage_Init();
  156.  
  157.    pinMode(VextControlPin, OUTPUT);
  158.    digitalWrite(VextControlPin, LOW);        // LOW to have 3.3V on pins 3 & 4
  159.    pinMode(batteryPin, INPUT);
  160.    pinMode(BuiltInLed, OUTPUT);    
  161.    digitalWrite(BuiltInLed, LOW);            // ON after Wifi connection so I set it OFF
  162.    
  163.    oldMillis = oldMillis_x = millis();
  164. }
  165.  
  166. void loop() {
  167.    static int iUsedSats, iViewedSats;
  168.    static String receivedString;
  169.    static float fLat, fLng, fCourse, fbatteryVoltage;
  170.    static char sBuffer0[16], sBufferCoord[33], dtDateTime[21];
  171.    String * sDMS;                            // array of String returned from called function
  172.    
  173.    ArduinoOTA.handle();
  174.  
  175.    if (GpsPort2.available() > 0) {
  176.       receivedString = GpsPort2.readStringUntil('\n');
  177.       Serial.println(receivedString);
  178.       udp.beginPacket(broadcastIP, UDP_PORT);
  179.       udp.printf(receivedString.c_str());
  180.       udp.endPacket();
  181.       for (int i=0; i < receivedString.length(); i++ )
  182.          GPS.encode(receivedString[i]); // (GpsPort2.read());
  183.    }
  184.    
  185.    newMillis = millis();
  186.    if (newMillis > oldMillis + tempo) {
  187.       oldMillis = newMillis;
  188.       fbatteryVoltage = 0;
  189.       for (int i = 0; i < BattReadLoop; i++) {
  190.          fbatteryVoltage += analogRead(batteryPin);
  191.          delay(6);
  192.       }
  193.       fbatteryVoltage = fbatteryVoltage * batteryCoeff / BattReadLoop;
  194.       fLat = GPS.location.lat();
  195.       fLng = GPS.location.lng();
  196.       iUsedSats = atoi(UsedSats.value());
  197.       iViewedSats = atoi(GAViewedSats.value()) +
  198.                   atoi(GBViewedSats.value()) +
  199.                   atoi(GLViewedSats.value()) +
  200.                   atoi(GPViewedSats.value());
  201.       if (iUsedSats < 3 || min(fLat, fLng) == 0) {
  202.          lcd.clear();
  203.          sprintf(sBuffer0,"Sat.%3d/%2d ", iUsedSats, iViewedSats);
  204.          lcd.setCursor(0, 0); lcd.print(sBuffer0);
  205.          lcd.setCursor(0, 3); lcd.print(String(fbatteryVoltage, 3) + "v  ");
  206.          bWeHaveAFix = false;
  207.       }
  208.       else
  209.          bWeHaveAFix = true;
  210.      
  211.       lcd.setCursor(0, 1); lcd.print("LAT");
  212.       lcd.setCursor(0, 2); lcd.print("LNG");
  213.       // to have the degree caracter https://www.letscontrolit.com/forum/viewtopic.php?t=2368
  214.       fCourse = GPS.course.isUpdated() ? GPS.course.deg() : 0;          
  215.       sprintf(sBuffer0,"%3s %03.0f%c", GPS.cardinal(fCourse), fCourse, byte(223));  
  216.       lcd.setCursor(12, 0); lcd.print(sBuffer0);
  217.          
  218.       if (bWeHaveAFix && bDisplayFlap) {
  219.          sprintf(sBufferCoord, "%+012.7f DD", fLat);     // https://cplusplus.com/reference/cstdio/printf/
  220.          lcd.setCursor(5, 1); lcd.print(sBufferCoord);  
  221.          sprintf(sBufferCoord, "%+012.7f DD", fLng);
  222.          lcd.setCursor(5, 2); lcd.print(sBufferCoord);  
  223.          sprintf(dtDateTime, "%02d-%02d-%02d %02d:%02d:%02dutc", GPS.date.year()-2000, GPS.date.month(), GPS.date.day(),
  224.                                                                GPS.time.hour(), GPS.time.minute(), GPS.time.second());
  225.          lcd.setCursor(0, 3); lcd.print(dtDateTime);
  226.       }
  227.       else if (bWeHaveAFix && !bDisplayFlap) {
  228.          if (bSpeed)  
  229.             sprintf(sBuffer0,"km/h %6.2f", (GPS.speed.isUpdated() ? GPS.speed.kmph() : 0));  
  230.          else
  231.             sprintf(sBuffer0,"kn   %6.2f", (GPS.speed.isUpdated() ? GPS.speed.knots() : 0));  
  232.          lcd.setCursor(0, 0); lcd.print(sBuffer0);
  233.          bSpeed = !bSpeed;
  234.  
  235.          sDMS = DecimalDegrees_to_DegreesMinutesSeconds(fLat, fLng);
  236.          lcd.setCursor(5, 1); lcd.print(sDMS[LAT]);
  237.          lcd.setCursor(5, 2); lcd.print(sDMS[LNG]);
  238.  
  239.          lcd.setCursor(0, 3); lcd.print(String(fbatteryVoltage, 3) + "v  ");
  240.          
  241.          sprintf(dtDateTime, "%02d:%02d:%02dutc", GPS.time.hour(), GPS.time.minute(), GPS.time.second());
  242.          lcd.setCursor(9, 3); lcd.print(dtDateTime);
  243.       }  
  244.       bDisplayFlap = !bDisplayFlap;
  245.    }
  246.    if (newMillis > oldMillis_x + (tempo * 5.5)) {
  247.       oldMillis_x = newMillis;
  248.       sprintf(sBuffer0,"Sat.%3d/%2d ", iUsedSats, iViewedSats);
  249.       lcd.setCursor(0, 0); lcd.print(sBuffer0);
  250.    }
  251. }
  252.  
  253. void WiFi_Init() {
  254. #if defined AP_MODE
  255.    #define HOSTNAME  "GNSS_Nmea"
  256.    #define SSID_NAME HOSTNAME
  257.    #define PASSWORD  "12345678"  // Mini. 8 car
  258.    // bool softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dhcp_lease_start = (uint32_t) 0);
  259.    // bool softAP(const char* ssid, const char* passphrase = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4, bool ftm_responder = false);
  260.    WiFi.softAPConfig(localIP, gatewayIP, subnetMaskIP, dhcp_startIP);
  261.    WiFi.softAP(SSID_NAME, PASSWORD, 1, 0, 8, false);
  262.    localIP = WiFi.softAPIP();
  263.    subnetMaskIP = WiFi.softAPSubnetMask();
  264.    broadcastIP = WiFi.softAPBroadcastIP();
  265. #else
  266.    #define HOSTNAME  "GNSS_Nmea"
  267.    #define SSID_NAME "ssid_name"       // to change
  268.    #define PASSWORD  "password"        // to change
  269.    DisplayIMessage("Connecting " + String(SSID_NAME), true, true );
  270.    WiFi.setHostname(HOSTNAME);
  271.    WiFi.begin(SSID_NAME, PASSWORD);
  272.    while (WiFi.status() != WL_CONNECTED) {
  273.       delay(250);
  274.       DisplayIMessage(".", false, false);
  275.    }
  276.    localIP = WiFi.localIP();
  277.    subnetMaskIP = WiFi.subnetMask();
  278.    gatewayIP = WiFi.gatewayIP();
  279.    broadcastIP = WiFi.broadcastIP();
  280. #endif
  281. DisplayIMessage("IP    " + localIP.toString(), true, true);
  282. //DisplayIMessage("Mask  " + subnetMask.toString(), false, true);
  283. //DisplayIMessage("Broad." + broadcastIP.toString(), false, true);
  284. // DisplayIMessage("GW   " + gatewayIP.toString(), false, true);
  285. DisplayIMessage("Port  " + String (UDP_PORT), false, true);
  286. }
  287.  
  288. void Storage_Init() {
  289. #if defined USING_SPIFFS
  290.    if(!SPIFFS.begin(true))
  291.       DisplayIMessage("Storage Mount Failed", false, true);
  292.    else {
  293.       DisplayIMessage("Storage Mount OK", false, true);
  294.    }
  295. #endif
  296.    delay(2500);
  297.    lcd.clear();
  298. }
  299.  
  300. void Display_Init() {
  301.    lcd.init();                      // I2C LCD init command
  302.    bLCDDisplay = true;
  303.    lcd.backlight();                 // I2C LCD turn backlight on
  304.    lcd.setCursor(3, 0); lcd.print("GNSS NMEA0183");  
  305.    lcd.setCursor(1, 2); lcd.print("version 2024-12-03");
  306.    lcd.setCursor(3, 3); lcd.print("by Patrick D.");  
  307.    delay(2500);
  308.    lcd.clear();
  309. }
  310.  
  311. void DisplayIMessage(String sMessage, bool bClear, bool bCRLN) {
  312.    static int nRow = 0;
  313.  
  314.    if (bLCDDisplay) {
  315.       if (bClear) {
  316.          lcd.clear();
  317.          lcd.setCursor(0, nRow=0);
  318.       }
  319.       if (bCRLN) {
  320.          lcd.setCursor(0, nRow++);
  321.          lcd.print(sMessage);
  322.       }
  323.       else
  324.          lcd.print(sMessage);
  325.    }
  326.    else
  327.       Serial.println(sMessage);
  328. }
  329.  
  330. String DD_to_DMS(float fValue) {
  331.    static int deg, arcMinutes;
  332.    static float minutesRemainder, arcSeconds;
  333.    static char buffer[16];
  334.  
  335.    fValue = abs(fValue);
  336.    deg = fValue;
  337.    minutesRemainder = abs(fValue - deg) * 60;
  338.    arcMinutes = minutesRemainder;
  339.    arcSeconds = (minutesRemainder - arcMinutes) * 60;
  340.    sprintf(buffer," %03d%c%02d'%05.2f\"", deg, byte(223), arcMinutes, arcSeconds);
  341.  
  342.    return String(buffer);
  343. }
  344.  
  345. String * DecimalDegrees_to_DegreesMinutesSeconds(float fLat, float fLng) {
  346.    /*    Latitude +N   0-90
  347.          Latitude -S
  348.          Longitude +E  0-180
  349.          Longitude -W */
  350.    static String sLat, sLng;
  351.    static String sDMS[2];                    // array of String to return
  352.    
  353.    // fLat = -fLat ; fLng = -fLng;           // to test South and West :-)
  354.    sLat = DD_to_DMS(fLat);
  355.    sLat.concat(fLat < 0 ? "S" : "N");
  356.    sDMS[LAT] = sLat;
  357.    sLng = DD_to_DMS(fLng);
  358.    sLng.concat(fLng < 0 ? "W" : "E");
  359.    sDMS[LNG] = sLng;
  360.  
  361.    return sDMS;
  362. }
  363.  
  364. void OTA_Init(){
  365.    // ArduinoOTA.setPort(3232);                    // Port defaults
  366.    ArduinoOTA.setHostname(HOSTNAME);               // Hostname defaults to esp3232-[MAC]
  367.    ArduinoOTA.setPassword(PASSWORD);               // No authentication by default
  368.    // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3   // Password can be set with it's md5 value as well
  369.    // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
  370.    ArduinoOTA
  371.    .onStart([]() {
  372.       String type;
  373.       if (ArduinoOTA.getCommand() == U_FLASH)
  374.          type = "sketch";
  375.       else // U_SPIFFS
  376.          type = "filesystem";
  377.          // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
  378.       Serial.println("Start updating " + type);
  379.    })
  380.    .onEnd([]() {
  381.       Serial.println("\nEnd");
  382.    })
  383.    .onProgress([](unsigned int progress, unsigned int total) {
  384.       Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  385.    })
  386.    .onError([](ota_error_t error) {
  387.       Serial.printf("Error[%u]: ", error);
  388.       if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
  389.       else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
  390.       else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
  391.       else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
  392.       else if (error == OTA_END_ERROR) Serial.println("End Failed");
  393.    });
  394.    ArduinoOTA.begin();
  395. }
  396.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement