ESP32でGPSレシーバモジュールを使う

ESP32
TRIPART_0001_BURST20241208155224782_COVER.JPG

早速製作

1つ前の記事ではGSPレシーバモジュール GY-NEO6MV2 をArduinoに接続しましたが、今回はGY-NEO6MV2をESP32-DevkitCに接続します。

ESP32でシリアル通信

さて、GY-NEO6MV2を使用するにはシリアル通信ができなければいけないので、ESP32でシリアル通信を行う方法を確認します。

Arduino UNOでは、シリアル通信(UART通信)ができるインターフェイスが1つ(UART0)用意されています。UART0のRXは0番端子、TXは1番端子に割り当てられていますが、PCと接続するUSBシリアルインターフェイスと回路が共通のため、PCに接続したままで各種モジュールとシリアル通信を行う際は本来のUARTインターフェイスではなくSoftwareSerial.hライブラリによって一般のデジタル入出力端子を使用することがよく行われています。

それに対して、ESP32では、UART0、UART1、UART2の3つのハードウェアシリアルインターフェイスが標準で用意されているため、ほとんどの場合はこれらを使って通信することになると思います。

ハードウェア

以下に、ESP32で使用できるUARTインターフェイスについてまとめておきます。

UART0
UARTでの役割端子番号端子の他の役割
RX34GPIO3
TX35GPIO1
RTS36GPIO22
CTS31GPIO19

UART0は、ESP32-DevkitCではUSBインターフェイスに接続されているようです。よってPCと接続して動作を確認しながら…という使用法の場合、GPSレシーバなどを接続することができません。

UART1
UARTでの役割端子番号端子の他の役割
RX17GPIO9
TX18GPIO10
RTS19GPIO11
CTS20GPIO6

上記の端子は内蔵のフラッシュメモリに接続されている回路と共用となっています。そのためデフォルトの設定のままではUART1は使用できません。ただしESP32ではUARTで使用する端子を変更することができるため、空いているGPIOのいずれかに使用端子を変更してしまえば使用することができます。やり方は後述します。

UART2
UARTでの役割端子番号端子の他の役割
RX27GPIO16
TX28GPIO17
RTS21GPIO7
CTS22GPIO8

UART2はデフォルトの設定のままで使用できます。

ソフトウェア

Arduino IDEで開発を行う場合、UART0、UART1、UART2に対応したオブジェクト Serial、Serial1、Serial2を使用することができます。ライブラリのインクルードもオブジェクトの生成も必要ありません(独自のオブジェクトを使用することもできます)。

UART0、UART1、UART2はハードウェアで実装されたシリアル通信インターフェイスですが、ESP32では端子の割り当てをソフトウェアから変更することができます。このため、デフォルトでは使用できなくなってる UART1 も簡単に各種機器との通信に使用できます。

回路

今回はSerial1の端子の再割り当てを試してみることにしました。GPIO 22 を Serial1_RX、GPIO 22 を Serial1_TX として使用することにして、それぞれGPSモジュールのTX、RXと接続します。あとは3V3(ESP32 Devkit-Cの3.3V出力)とGPSモジュールのVCC、そしてGND同士を接続します。配線はこれらたったの4本だけです。

配線

回路図にもあるとおり、配線はジャンパ線4本だけですみます。
VCCの配線がムリヤリなことになっていますが、実際には6穴のブレッドボードを使用するとよいでしょう。

実物はこちら。片側6穴のブレッドボードを使用しています。

プログラム

今回はシンプルに、GPSモジュールからのデータをそのままPCのシリアルモニタで見られるだけのプログラムを作成します。

全ソース

C++
void setup() {
  Serial.begin(115200);
  Serial1.begin(9600, SERIAL_8N1, 22, 23);
  delay(1000);
}

void loop() {
  if(Serial1.available()>0){
    Serial.write(Serial1.read());
  }
}

プログラムの解説

シリアルインターフェイスの初期化

C++
  Serial.begin(115200);
  Serial1.begin(9600, SERIAL_8N1, 22, 23);
  delay(1000);

Setup()関数内では、UART0およびUART1を初期化しています。

UART0のためのオブジェクト Serial およびUART1のためのオブジェクト Serial1 について、現在のバージョンでは宣言は必要ありません。

C++
HardwareSerial Serial(1);

などとしている例も多く見られますが、Serial や Serial1 をそのまま使用する場合は不要です(違う名前のオブジェクトを使用したい場合は宣言が必要です)。

UART通信用オブジェクトの初期化には begin() 関数を使用します。

書式
C++
void begin(unsigned long baud, uint32_t config = SERIAL_8N1, int8_t rxPin = -1, int8_t txPin = -1, bool invert = false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 112)
引数

非常に引数が多いのですが、必須は第1引数のみです。今回は端子の再定義を行っているため、以下の4つの引数です。

名前意味
baudunsigned long通信速度(bps)
configuint32_t通信の条件(ビット数、パリティなど)。
SERIAL_8N1などの定義済み定数で指定すると便利。
rxPinint8_tRxとして使用するGPIOの番号
txPinint8_tTxとして使用するGPIOの番号
返却値

返却値はありません。

今回の例では、Serial(UART0、PCに接続)については速度(115200bps)のみ設定しています。
Serial1(UART1)については、端子をデフォルト値から Rx=GPIO22、Tx=GPIO23 に変更しています。第2引数はデフォルト値の通りですが、途中の引数を省略することができないためにこのような記述になっています。

begin()関数による設定後、すぐに読み書きをするとデータの取りこぼしが発生するため、delay(1000)で1秒間だけ待っています。

loop()関数
C++
void loop() {
  if(Serial1.available()>0){
    Serial.write(Serial1.read());
  }
}

Serial1.available()でGPSレシーバモジュールからデータが送られてきているか確認し、Serial1.read()で1バイトずつ読み取っては、ただちに Serial.write()でPCへ送信しています。

実行

Arduino IDEのシリアルモニタなどでESP32から送られてくる文字列をモニタしつつプログラムを起動すると、まもなく以下のようなデータが送られてきます。

Plaintext
$GPRMC,064924.00,V,,,,,,,081224,,,N*7D
$GPVTG,,,,,,,,,N*30
$GPGGA,064924.00,,,,,0,00,99.99,,,,,,*6B
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$GPGSV,1,1,02,08,,,23,27,,,36*72
$GPGLL,,,,,064924.00,V,N*47

まだ衛星を定位していない状態なのでほとんどのフィールドが空ですが、窓際などにおいて数分(初回起動だと20分程度)で位置データが受信できるようになります。

コメント

タイトルとURLをコピーしました