行き当たりばったり電子工作

昔の論理IC電子工作少年が、四苦八苦しながら現在の電子工作に挑戦する話。

GPS高度計改良版

以前、GPS高度計の記事を書きましたが、その後に少し改良?しました。

以前の記事はこちら ↓
jnz-tech.hatenablog.com
jnz-tech.hatenablog.com

何を変えたかというと、次の3点です。

  • GPSの受信をアンテナ一体型のユニットに変更
  • マイコンをEPS32に変更
  • OLEDディスプレイをSPI接続からI2C接続へ

これらによって、ものすごく回路がシンプルになりました。

GPSアンテナ一体型ユニットは、秋月電子の「GPS受信機 シリアル出力タイプ(先バラ) みちびき2機(194/195)対応 1PPS出力付 GT-502MGG-N」を利用しました(名前が長い)。
akizukidenshi.com

これはGPSアンテナ内部にシリアル変換回路が入っているため、そのままシリアル通信(UART)で読むことができます。これで以前の記事に書いたシリアル出力の基板が不要になりました。また、このモジュールの電源電圧は3~5Vになっています。OLEDディスプレイも3.3V駆動なので、ESP32を使うことで、以前の記事で使っていたレベル変換モジュールも不要になりました。

ということで、回路はいたってシンプル。

GPS高度計改良版接続図

単にESP32に繋ぐだけです。

プログラムの方の変更点は、以下の3点

  • 以前は Arduino 用にソフトウェアシリアルを利用していましたが、ESP32には無いのでUART2を用いた(51行目など)
  • GPSモジュールからのNMEAフォーマットのヘッダ部分が若干変わっていたのでを修正(12行目あたりのGPGGA→GNGGA)
  • 時間表示の追加(145行目あたり)と、それに伴ったレイアウト変更(捕捉衛星数表示をニコちゃんマークの右下にするとか)
#include <TinyGPSPlus.h>
#include <U8g2lib.h>
#include <Wire.h>

// GPS baud
static const uint32_t GPSBaud = 9600;

// The TinyGPSPlus object
TinyGPSPlus gps;

// Tiny GPS Custom object
TinyGPSCustom gps_tim (gps, "GNGGA", 1); // 時刻
TinyGPSCustom gps_alt (gps, "GNGGA", 9); // 高度
TinyGPSCustom gps_sat (gps, "GNGGA", 7); // 衛星数
TinyGPSCustom gps_qlt (gps, "GNGGA", 6); // 測位モード
TinyGPSCustom gps_spd (gps, "GNRMC", 7); // 速度(ノット)
TinyGPSCustom gps_dir (gps, "GNRMC", 8); // 方位

// 方位の定義。16番目は WAIT
static const char *c_dir[17] = {
  "  N   ",
  "N-N-E ",
  " N-E  ",
  "E-N-E ",
  "  E   ",
  "E-S-E ",
  " S-E  ",
  "S-S-E ",
  "  S   ",
  "S-S-W ",
  " S-W  ",
  "W-S-W ",
  "  W   ",
  "W-N-W ",
  " N-W  ",
  "N-N-W ",
  "WAIT  "};

// 前回方位保管用
float f_olddir=999;

// 前回分保管用
int   i_oldmin=999;

// OLE object
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 22, /* data=*/ 21, /* reset=*/ U8X8_PIN_NONE);


void setup() {
  Serial.begin(9600);
  Serial2.begin(GPSBaud); // TX=16,RX=17 がデフォルト

  // LCD初期化
  delay(1000);
  u8g2.begin();
  u8g2.setPowerSave(0);
  u8g2.setFlipMode(0);

}

void loop() {
  // 変数宣言
  float f_alt;
  float f_spd;
  float f_dir;
  int   i_dir;
  int   i_qlt;
  int   i_hour;
  int   i_min;
  char hour[5];
  char min[5];
  char buf[100];

  // 時刻の取得とJSTへの変換
  strcpy (buf, gps_tim.value());
  strncpy (hour, buf, 2);
  strncpy (min, buf+2, 2);
  i_hour = atoi(hour);
  i_hour = i_hour + 9;
  if (i_hour > 23){
    i_hour = i_hour - 24;
  }
  i_min = atoi(min);

  // 更新があったときのみ表示を変更する
  if (i_min != i_oldmin || gps_alt.isUpdated() || gps_sat.isUpdated() || gps_qlt.isUpdated() || gps_spd.isUpdated() || gps_dir.isUpdated()) {
    // 標高を数値に変換  
    f_alt=atof(gps_alt.value());

    // 速度を数値に変更してkm/h換算
    f_spd=atof(gps_spd.value()) * 1.852;
  
    // 速度が 8km/h 以上で方位を更新する
    if (f_spd >= 8.0) {
      // 方位を元に配列引数を算出
      f_dir=atof(gps_dir.value());
    }
    else {
      f_dir=f_olddir;
    }

    // 方位を元に方位文字配列引数を算出
    if (f_dir < 360) {
      float f_tmp=f_dir + 11.25;
      if (f_tmp >= 360.0) {
        f_tmp -= 360.0;
      }
      i_dir=(int)(f_tmp/22.5);
    }
    else {
      i_dir = 16;
    }

    // 方位を保存
    f_olddir = f_dir;

    // LCDに表示
    u8g2.clearBuffer();

    // 速度表示
    u8g2.setFont(u8g2_font_6x13_mr);
    u8g2.setCursor(3,15);
    dtostrf(f_spd, 4, 0, buf);
    u8g2.print(F("Spd : ")); u8g2.print(buf); u8g2.print(" km/h");

    // 方位表示
    u8g2.setFont(u8g2_font_6x13_mr);
    u8g2.setCursor(3,30);
    if (f_dir > 360) {
      u8g2.print(F("Dir : WAIT"));
    }
    else {
      dtostrf(f_dir, 4, 0, buf);
      u8g2.print(F("Dir : ")); u8g2.print(buf); u8g2.print(" deg  ");
      u8g2.setCursor(95,30);
      u8g2.print(c_dir[i_dir]);
    }
    
    // 高度表示
    u8g2.setFont(u8g2_font_6x13_mr);
    u8g2.setCursor(3,45);
    dtostrf(f_alt, 4, 0, buf);
    u8g2.print(F("Alt : ")); u8g2.print(buf); u8g2.print(" m  ");

    // 時間表示
    u8g2.setFont(u8g2_font_6x13_mr);
    u8g2.setCursor(3,60);
    sprintf(buf, "%02d:%02d", i_hour, i_min);
    u8g2.print(F("JST : "));u8g2.print(buf);
    i_oldmin = i_min;

    // 衛星数表示
    u8g2.setFont(u8g2_font_5x8_mr);
    u8g2.setCursor(115,63);
    sprintf(buf, "%02s", gps_sat.value());
    u8g2.print(buf);

    // 測位方式表示
    i_qlt = atoi(gps_qlt.value());
    u8g2.setFont(u8g2_font_emoticons21_tr);
    u8g2.drawGlyph(93,60,0x24-i_qlt*2);
    
    // 画面出力
    u8g2.sendBuffer();
  }

  // delay付きのGPS受信
  smartDelay(1000);
}

// delay付きのGPS受信
static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (Serial2.available())
      gps.encode(Serial2.read());
  } while (millis() - start < ms);
}


表示はこんな感じになりました。

GPS高度計表示

なかなかいい感じ♪
上の写真のケースについては、次回に書こうと思います。