お父にゃんの電子工作

お父にゃんの電子工作

暇なおじさんが、電子工作(主にラジオ製作)をして勝手な感想を書く

少し前に、Ali-ExpressでATGM336H(GPS+BDS)モジュールなるものを見つけたので買っておいた。532円だった。GPSモジュールがこの値段なら、かなり安いような気がする。

 

GPSはご存じアメリカの測位衛星。BDSは中国の測位衛星のことで、合わせ技で精度の良い測位が出来るそうなのである。

 

更に突っ込んで、Googleに訊いてみると、以下のような返答が有った。

--------------------------------------------------------

ATGM336Hは複数の衛星測位システム(GNSS)に対応しており、アメリカの「GPS」だけでなく、日本の「みちびき」や中国の「北斗(BeiDou)」など、世界中の主要な衛星信号を補足可能です。 [123]

補足可能な主な衛星システムは以下の通りです: [12]

  • GPS(アメリカ)
  • QZSS(日本:準天頂衛星システム「みちびき」)
  • BDS(中国:北斗衛星導航系統・BD2、BD3)
  • GALILEO(欧州)
  • GLONASS(ロシア) [1]

これらを同時に組み合わせて測位(マルチシステム連合測位)ができるため、ビル街など空が見える範囲が狭い場所でも、より安定して精度の高い位置情報を取得できます。

---------------------------------------------------------

ほぉ、それは素晴らしい。

是非、試してみなければならない。

 

さっそく開封してみる。

 

中身はこんな感じ

誘電体アンテナと思われるアンテナだが、結構分厚い

裏側

 

今回の回路図はこちら↓

今回もAli-Expressで300円弱で買えるRP2040 Zero(もどき)を使う。

ブレッドボードでこんな感じ

アンテナは線を引き出している面を下にしないと、受信感度が下がるらしい。

(写真のような方向で設置する)

 

スケッチはGeminiが書いてくれたコードをベースに試行錯誤して以下になった

#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include <TinyGPS++.h>
 
// --- LovyanGFXのRP2040-ZERO用カスタム設定 ---
class LGFX : public lgfx::LGFX_Device {
  lgfx::Panel_ST7789  _panel_instance;
  lgfx::Bus_SPI       _bus_instance;
public:
  LGFX(void) {
    { // SPIバスの設定
      auto cfg = _bus_instance.config();
      cfg.spi_host = 0;          // SPI0を使用
      cfg.spi_mode = 0;
      cfg.freq_write = 40000000; // 40MHz
      cfg.freq_read  = 16000000;
      cfg.pin_sclk = 2;          // GP2
      cfg.pin_mosi = 3;          // GP3
      cfg.pin_miso = -1;
      cfg.pin_dc   = 5;          // GP5
      _bus_instance.config(cfg);
      _panel_instance.setBus(&_bus_instance);
    }
    { // パネルの設定
      auto cfg = _panel_instance.config();
      cfg.pin_cs           = 6;  // GP6
      cfg.pin_rst          = 4;  // GP4
      cfg.pin_busy         = -1;
      cfg.panel_width      = 240;
      cfg.panel_height     = 320;
      cfg.offset_x         = 0;
      cfg.offset_y         = 0;
      cfg.offset_rotation  = 0;
      cfg.dummy_read_pixel = 8;
      cfg.readable         = true;
      cfg.invert           = true; // 色が反転する場合はfalseにする
      cfg.rgb_order        = false;
      _panel_instance.config(cfg);
    }
    setPanel(&_panel_instance);
  }
};
 
LGFX lcd;
TinyGPSPlus gps;
 
// 各衛星システムの捕捉(可視)数保持用変数
int sats_gps     = 0;
int sats_qzss    = 0; // みちびき
int sats_beidou  = 0;
int sats_glonass = 0;
int sats_galileo = 0;
 
String nmea_buffer = "";
 
// NMEA文字列から特定のカンマ位置のデータを切り出す関数
String getArgument(String data, char separator, int index) {
  int found = 0;
  int strIndex[] = {0, -1};
  int maxIndex = data.length() - 1;
  for (int i = 0; i <= maxIndex && found <= index; i++) {
    if (data.charAt(i) == separator || i == maxIndex) {
      found++;
      strIndex[0] = strIndex[1] + 1;
      strIndex[1] = (i == maxIndex) ? i + 1 : i;
    }
  }
  return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}
 
// 生のNMEAデータを解析してシステム別の衛星数を抽出
void parseNMEA(char c) {
  if (c == '\n' || c == '\r') {
    if (nmea_buffer.startsWith("$") && nmea_buffer.indexOf("GSV") > 0) {
      // GSV文の4番目の要素(インデックス3)がそのシステムの可視衛星数
      String sats_str = getArgument(nmea_buffer, ',', 3);
      int count = sats_str.toInt();
 
      if (nmea_buffer.startsWith("$GP")) sats_gps = count;
      else if (nmea_buffer.startsWith("$QZ")) sats_qzss = count;
      else if (nmea_buffer.startsWith("$BD") || nmea_buffer.startsWith("$GB")) sats_beidou = count;
      else if (nmea_buffer.startsWith("$GL")) sats_glonass = count;
      else if (nmea_buffer.startsWith("$GA")) sats_galileo = count;
    }
    nmea_buffer = "";
  } else if (nmea_buffer.length() < 100) {
    nmea_buffer += c;
  }
}
 
void setup() {
  // LovyanGFX初期化
  lcd.init();
  lcd.setRotation(1); // 横向き(320x240)
  lcd.fillScreen(TFT_BLACK);
 
  // GPSシリアル初期化 (UART0: GP0=TX, GP1=RX)
  Serial1.setTX(0);
  Serial1.setRX(1);
  Serial1.begin(9600);
 
  // 固定タイトルの描画
  lcd.setFont(&fonts::lgfxJapanGothic_20);
  lcd.setTextSize(1);
  lcd.setTextColor(TFT_CYAN);
  lcd.drawString("=== GNSS MONITOR ===", 10, 0);
}
 
void loop() {
  // GPSからデータを受信
  while (Serial1.available() > 0) {
    char c = Serial1.read();
    gps.encode(c);     // TinyGPS++で位置・高度をパース
    parseNMEA(c);      // 自前関数でシステム別衛星数をパース
  }
 
  // 1秒ごとに画面を更新
  static unsigned long last_display_time = 0;
  if (millis() - last_display_time > 1000) {
    last_display_time = millis();
    update_display();
  }
}
 
void update_display() {
  lcd.startWrite(); // 描画処理の最適化開始
 
  // 1. 位置情報の表示 (背景色をTFT_BLACKに指定して上書き時のチラつき防止)
  if (gps.location.isValid()) {
    lcd.setTextColor(TFT_WHITE, TFT_BLACK);
    lcd.setCursor(10, 25);  lcd.printf("緯度:%11.6f   ", gps.location.lat());
    lcd.setCursor(10, 50);  lcd.printf("経度: %11.6f   ", gps.location.lng());
  } else {
    lcd.setTextColor(TFT_YELLOW, TFT_BLACK);
    lcd.setCursor(10, 25);  lcd.print("緯度:Searching...   ");
    lcd.setCursor(10, 50);  lcd.print("緯度: Searching...   ");
  }
 
  // 2. 高度の表示
  if (gps.altitude.isValid()) {
    lcd.setTextColor(TFT_WHITE, TFT_BLACK);
    lcd.setCursor(10, 75);  lcd.printf("高度: %7.1f m    ", gps.altitude.meters());
  } else {
    lcd.setTextColor(TFT_YELLOW, TFT_BLACK);
    lcd.setCursor(10, 75);  lcd.print("高度: Searching...   ");
  }
 
  // 区切り線
  lcd.drawFastHLine(10, 105, 300, TFT_DARKGREY);
 
  // 3. 各システムごとの補足衛星数表示
  lcd.setTextColor(TFT_GREEN, TFT_BLACK);
  lcd.setCursor(10, 110); lcd.print("Satellites in View:");
 
  lcd.setTextColor(TFT_WHITE, TFT_BLACK);
  // %2d を使うことで桁数が変わっても表示位置がズレたりゴミが残ったりしません
  lcd.setCursor(20, 135); lcd.printf("GPS      : %2d  ", sats_gps);
  lcd.setCursor(20, 155); lcd.printf("みちびき : %2d  ", sats_qzss);
  lcd.setCursor(20, 175); lcd.printf("BeiDou   : %2d  ", sats_beidou);
  //lcd.setCursor(20, 195); lcd.printf("GLONASS  : %2d  ", sats_glonass);
  //lcd.setCursor(20, 215); lcd.printf("Galileo  : %2d  ", sats_galileo);
  lcd.setCursor(20, 200);
  lcd.printf("有効衛星数: %2d  ", gps.satellites.value());
 
  lcd.endWrite(); // 描画処理の最適化終了
}

 

電源を入れた直後は、衛星を捕捉していても、位置情報は表示しない。

Geminiによると・・・

---------------------------------------

GPSモジュールは、電源を入れた直後、衛星から「軌道情報(エフェメリス・アルマナック)」という重いデータをダウンロードし始めます。
このデータを受信し終えるまでは、どれだけ衛星が見えていても位置の計算が始まりません。
特に購入後初めての起動や、数日ぶりの起動のときは、空が見える場所で5分〜15分ほどそのまま電源を入れたまま待ってみてください。

------------------------------------------

にゃるほど

しばらくの間、空が見えるベランダで放置してみる。

 

15分後くらいに確認したら、位置情報が表示されるようになっていた。

 

小数点以下は、おじさんの家が特定されるのでボカしてある。

そこそこ、正確な位置を示しているのである。

 

一度表示するようになると、電源を入れて数秒で位置情報を表示するようになった。

軌道情報をちゃんと保持しているようだ。

 

あと、ATGM336Hそのものは、「みちびき」等も捕捉できるはずなのだが、どうもモジュール内の設定で、GPSとBDSしか対応していないようなのだ。

残念!

まぁ、仕方あるまい。

 

しかし、こんな価格で簡単に手に入るとは。あな恐ろし。

 

 

「世の中は進歩しているのニャ」