UART1はあるけど使用不可という勘違い
ESP32-WROOM-32は、UART0/1/2の3つが使えるはずだったけど、ESP32のUART1 RXD/TXDピン周りが内部Flash用に占有されているから使用不可と知り、公式のdatasheetにも以下記載があったので、勝手に UART1はあっても使用不可 なんだと勘違いしてた。
しかし、本文にもある通り 普通のGPIOとしては使えませんよ と言っているに過ぎず、 UART1が使えない とは言っていない。
結論から言うと、もちろんUART1は使える。
ESP32-WROOM-32 integrates a 4 MB SPI flash, which is connected to GPIO6, GPIO7, GPIO8, GPIO9, GPIO10 and GPIO11. These six pins cannot be used as regular GPIOs.
esp32-wroom-32_datasheet_en.pdf
UART1の使い方
いきなり結論から言うと、以下のようにすれば、UART1のTX/RXピンをデフォルトピン以外に割り当てて使用することが可能。
以下例はUART1のデフォルトのRX/TXピンをGPIO26/27に変更して使用している。
void setup(void) { Serial1.begin(115200, SERIAL_8N1, 26, 27); } void loop(void) { Serial1.println("hoge"); Serial1.flush(); delay(1000); }
UART1周辺情報の確認
まずはdatasheetを確認。以下のようにU1RXD/TXD/RTS/CTSがそれぞれGPIO9/10/11/6にアサインされている。
公式datasheet記載の通りデフォルトのU1* ピンは全てGPIOとして使用不可なので、どうにかして別のピンに割り当てる必要がある。

HardwareSerial クラス定義
以降のコードはArduino Framework使用を前提とする。
framework-arduinoespressif32/cores/esp32/HardwareSerial.h を確認すると、以下のようなクラス定義となっている。begin() の rxPin/txPin にGPIO9/10以外のピンを指定すれば良さそうに思える。
が、その前に HardwareSerial コンストラクタ引数 uart_nr が気になる…。なんだこれ。
class HardwareSerial: public Stream { public: HardwareSerial(int uart_nr); 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); ... };
HardwareSerial コンストラクタ実装
コンストラクタや begin() は framework-arduinoespressif32/cores/esp32/HardwareSerial.cpp に実装されている。
HardwareSerial Serialx(1); のように自前でインスタンス作っても良いが、Serial1(1) インスタンスに対してそのまま begin() しても良いことがわかる。
#ifndef RX1 #define RX1 9 #endif #ifndef TX1 #define TX1 10 #endif #ifndef RX2 #define RX2 16 #endif #ifndef TX2 #define TX2 17 #endif #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) HardwareSerial Serial(0); HardwareSerial Serial1(1); HardwareSerial Serial2(2); #endif HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), _uart(NULL) {} void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms) { if(0 > _uart_nr || _uart_nr > 2) { log_e("Serial number is invalid, please use 0, 1 or 2"); return; } if(_uart) { end(); } if(_uart_nr == 0 && rxPin < 0 && txPin < 0) { rxPin = 3; txPin = 1; } if(_uart_nr == 1 && rxPin < 0 && txPin < 0) { rxPin = RX1; txPin = TX1; } if(_uart_nr == 2 && rxPin < 0 && txPin < 0) { rxPin = RX2; txPin = TX2; } _uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, 256, invert); ... }
HardwareSerial::uartBegin() 実装
更にFrameworkのコードを遡っていくと、ピンのアサインは以下のように実装されている。
pinMode() の INPUT や OUTPUT や割り込みなど、必要な設定は全て実装されていることがわかる。
uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t queueLen, bool inverted) { ... if(rxPin != -1) { uartAttachRx(uart, rxPin, inverted); } if(txPin != -1) { uartAttachTx(uart, txPin, inverted); } } void uartAttachRx(uart_t* uart, uint8_t rxPin, bool inverted) { if(uart == NULL || rxPin > 39) { return; } pinMode(rxPin, INPUT); pinMatrixInAttach(rxPin, UART_RXD_IDX(uart->num), inverted); uartEnableInterrupt(uart); } void uartAttachTx(uart_t* uart, uint8_t txPin, bool inverted) { if(uart == NULL || txPin > 39) { return; } pinMode(txPin, OUTPUT); pinMatrixOutAttach(txPin, UART_TXD_IDX(uart->num), inverted, false); }
まとめ
公式ドキュメントとFrameworkのコードはちゃんと読もう。