Last active
June 8, 2024 05:01
-
-
Save ksasao/5fa6174f2d1efd9f75277bdab009ab01 to your computer and use it in GitHub Desktop.
M5StickC と u-blox ZED-F9P でRTK測位をするサンプル。M5StickCとZED-F9Pモジュールとの接続はI2C、RTCM3はAndroid スマホからの Bluetooth Serial 経由で受信する実装です。https://twitter.com/ksasao/status/1655098208903888901
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <M5GFX.h> | |
| #include <M5StickCPlus.h> | |
| #include "BluetoothSerial.h" | |
| #include <TinyGPS++.h> | |
| #include <SparkFun_u-blox_GNSS_Arduino_Library.h> | |
| SFE_UBLOX_GNSS myGNSS; | |
| // Location of Destination | |
| double destLat = 35.5000000; | |
| double destLon = 139.6000000; | |
| TinyGPSPlus gps; // for calculate distance | |
| M5GFX display; | |
| char txt[64]; | |
| // Bluetooth Serial | |
| uint64_t chipid; | |
| char chipname[256]; | |
| BluetoothSerial SerialBT; | |
| //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= | |
| //Used for GGA sentence parsing from incoming NMEA | |
| bool ggaSentenceStarted = false; | |
| bool ggaSentenceComplete = false; | |
| bool ggaTransmitComplete = true; //Goes true once we transmit GGA to the caster | |
| char ggaSentence[128] = {0}; | |
| byte ggaSentenceSpot = 0; | |
| int ggaSentenceEndSpot = 0; | |
| //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= | |
| void drawText(double meter, int fix){ | |
| bool isKilo = true; | |
| display.setTextColor(TFT_WHITE, TFT_BLACK); | |
| display.setTextWrap(false); | |
| display.setTextDatum(textdatum_t::top_right); | |
| display.setTextSize(0.75,1); | |
| display.setFont(&fonts::Font8); | |
| if(meter<0 || meter > 9999.9 * 1000.0){ | |
| sprintf(txt," -----"); | |
| }else if (meter > 999.99*1000.0){ // 1000km以上 | |
| sprintf(txt," %.1f", meter/1000.0); | |
| }else if (meter > 99.999*1000.0){ // 100km以上 | |
| sprintf(txt," %.2f", meter/1000.0); | |
| }else if (meter > 9.9999*1000.0){ // 10km以上 | |
| sprintf(txt," %.3f", meter/1000.0); | |
| }else if (meter > 999.99){ // 1km以上 | |
| isKilo = false; | |
| sprintf(txt," %.1f", meter); | |
| }else{ | |
| isKilo = false; | |
| sprintf(txt," %.2f", meter); | |
| } | |
| display.drawString(txt, 235,10); | |
| display.setFont(&fonts::lgfxJapanGothic_40); | |
| if(isKilo){ | |
| display.drawString("km", 230,85); | |
| }else{ | |
| display.drawString(" m", 230,85); | |
| } | |
| display.setTextDatum(textdatum_t::top_left); | |
| if(fix == 1){ | |
| sprintf(txt,"Floating "); | |
| }else if(fix == 2){ | |
| sprintf(txt,"Fix "); | |
| }else{ | |
| sprintf(txt,"None "); | |
| } | |
| display.drawString(txt, 5,85); | |
| } | |
| void updateStatus(double lat, double lon, int fix){ | |
| double distance = -1; | |
| distance = | |
| TinyGPSPlus::distanceBetween( | |
| lat, | |
| lon, | |
| destLat, | |
| destLon); | |
| if (!display.displayBusy()) | |
| { | |
| display.startWrite(); | |
| drawText(distance,fix); | |
| display.endWrite(); | |
| } | |
| } | |
| void setup() | |
| { | |
| M5.begin(); | |
| Serial.begin(115200); | |
| Serial.println(F("Receive RTCM3 via Bluetooth Serial for RTK positioning")); | |
| Wire.begin(); //Start I2C | |
| Wire.setClock(80000); | |
| delay(2000); // wait a second | |
| Serial.print("Location of Destination: "); | |
| Serial.print(destLat,7); | |
| Serial.print(","); | |
| Serial.print(destLon,7); | |
| Serial.println(); | |
| display.init(); | |
| display.setRotation(1); | |
| updateStatus(0,0,0); | |
| Serial.println("Started."); | |
| while (myGNSS.begin() == false) //Connect to the Ublox module using Wire port | |
| { | |
| Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring or wait a mitune.")); | |
| delay(2000); | |
| } | |
| Serial.println(F("u-blox module connected")); | |
| // Set F9P | |
| myGNSS.setI2COutput(COM_TYPE_UBX| COM_TYPE_NMEA); //Set the I2C port to output UBX only (turn off NMEA noise) | |
| myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3); | |
| myGNSS.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // // Set the differential mode - ambiguities are fixed whenever possible | |
| myGNSS.setNavigationFrequency(1); | |
| // Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA | |
| myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_GP); | |
| myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C); //Verify the GGA sentence is enabled | |
| myGNSS.setAutoPVT(true); | |
| myGNSS.setI2CTransactionSize(128); | |
| //myGNSS.enableDebugging(); | |
| // Bluetooth Serial | |
| chipid = ESP.getEfuseMac(); | |
| sprintf( chipname, "M5RTK_%04X", (uint16_t)(chipid >> 32)); | |
| Serial.printf("Bluetooth: %s\n", chipname); | |
| SerialBT.begin(chipname); | |
| while (Serial.available()) // Empty the serial buffer | |
| Serial.read(); | |
| } | |
| //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= | |
| long last = 0; | |
| size_t rtcmCount = 0; | |
| uint8_t rtcmData[512 * 4]; //Most incoming data is around 500 bytes but may be larger | |
| void loop() | |
| { | |
| M5.update(); | |
| //Collect any available RTCM data | |
| while (SerialBT.available()) | |
| { | |
| rtcmData[rtcmCount++] = SerialBT.read(); | |
| if (rtcmCount == sizeof(rtcmData)){ | |
| rtcmCount = 0; | |
| break; | |
| } | |
| } | |
| // BluetoothSerialのバッファサイズは512バイト固定だが、 | |
| // F9P への I2C 経由でのRTCM3データ送信は 1Kバイト程度を連続的に | |
| // 行わないとFix解が得られないためバッファリングしてまとめて転送する | |
| if (rtcmCount > 1024) | |
| { | |
| //Push RTCM to GNSS module over I2C | |
| myGNSS.pushRawData(rtcmData, rtcmCount); | |
| Serial.print(F("Pushed ")); | |
| Serial.print(rtcmCount); | |
| Serial.println(F(" RTCM bytes to ZED")); | |
| rtcmCount = 0; | |
| } | |
| long now = millis(); | |
| if (now-last>1000)// || myGNSS.getPVT() && (myGNSS.getInvalidLlh() == false)) | |
| { | |
| myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it. | |
| last = now; | |
| if(ggaSentenceComplete){ | |
| Serial.println(ggaSentence); | |
| SerialBT.println(ggaSentence); | |
| ggaTransmitComplete = true; | |
| ggaSentenceComplete = false; | |
| } | |
| printNmeaData(); | |
| } | |
| } | |
| //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= | |
| void printNmeaData() | |
| { | |
| char buff[256]; | |
| int year = myGNSS.getYear(); | |
| int month = myGNSS.getMonth(); | |
| int day = myGNSS.getDay(); | |
| int hour = myGNSS.getHour(); | |
| int minute = myGNSS.getMinute(); | |
| int sec = myGNSS.getSecond(); | |
| int msec = myGNSS.getMillisecond(); | |
| double lat = myGNSS.getLatitude() / 10000000.0; | |
| double lon = myGNSS.getLongitude() / 10000000.0; | |
| double alt = myGNSS.getAltitude() / 1000.0; | |
| double altMSL = myGNSS.getAltitudeMSL() / 1000.0; | |
| double geoid = alt - altMSL; | |
| int fixType = myGNSS.getFixType(); //0=no fix, 1=dead reckoning, 2=2D, 3=3D, 4=GNSS, 5=Time fix | |
| int rtk = myGNSS.getCarrierSolutionType(); //0=No solution, 1=Float solution, 2=Fixed solution | |
| long accuracy = myGNSS.getPositionAccuracy(); | |
| updateStatus(lat,lon,rtk); | |
| Serial.print("Lat: "); | |
| Serial.print(lat,7); | |
| Serial.print(" Lon: "); | |
| Serial.print(lon,7); | |
| Serial.print(" Fix: "); | |
| Serial.print(fixType); | |
| Serial.print(" RTK: "); | |
| Serial.print(rtk); | |
| Serial.print(" Accuracy: "); | |
| Serial.print(accuracy); | |
| Serial.print(" mm"); | |
| Serial.println(); | |
| } | |
| //This function gets called from the SparkFun u-blox Arduino Library | |
| //As each NMEA character comes in you can specify what to do with it | |
| //We will look for and copy the GGA sentence | |
| void SFE_UBLOX_GNSS::processNMEA(char incoming) | |
| { | |
| //Serial.print(incoming); | |
| //Take the incoming char from the u-blox I2C port and check to see if we should record it or not | |
| if (incoming == '$' && ggaTransmitComplete == true) | |
| { | |
| ggaSentenceStarted = true; | |
| ggaSentenceSpot = 0; | |
| ggaSentenceEndSpot = sizeof(ggaSentence); | |
| ggaSentenceComplete = false; | |
| } | |
| if (ggaSentenceStarted == true) | |
| { | |
| ggaSentence[ggaSentenceSpot++] = incoming; | |
| //Make sure we don't go out of bounds | |
| if (ggaSentenceSpot == sizeof(ggaSentence)) | |
| { | |
| //Start over | |
| ggaSentenceStarted = false; | |
| } | |
| else if (incoming == '*') | |
| { | |
| //We're near the end. Keep listening for two more bytes to complete the CRC | |
| ggaSentenceEndSpot = ggaSentenceSpot + 2; | |
| } | |
| else if (ggaSentenceSpot == ggaSentenceEndSpot){ | |
| if(strncmp(ggaSentence+3,"GGA",3) == 0){ | |
| ggaSentence[ggaSentenceSpot] = '\0'; //Terminate this string | |
| ggaSentenceComplete = true; | |
| ggaTransmitComplete = false; //We are ready for transmission | |
| ggaSentenceStarted = false; | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment