Raspi Picoで Grove LCD を使う (C++)

[**Grove Starter Kit for Raspberry Pi**](https://www.switch-science.com/catalog/7103/)に付属する[**Grove Shield for Pi Pico**](https://wiki.seeedstudio.com/Grove_Shield_for_Pi_Pico_V1.0/)と[**Grove - 16x2 LCD**](https://wiki.seeedstudio.com/Grove-16x2_LCD_Series/)をRaspberry Pi Picoで使う方法を説明します。
公式にはMicroPythonのサンプルコードがありますが、ここではC++を使って実装しました。

**Grove Starter Kit for Raspberry Pi**や**Grove Shield for Pi Pico**については、[こちら](https://qiita.com/hiro-han/items/ffd2fb2501a1aeef4549)の記事を参照してください。Starter Kitに付属する他のGroveモジュールの使い方へのリンクもこの記事にあります。

今回実装したソースコードは[Github](https://github.com/hiro-han/raspi-pico-grove-starter-kit)にアップしています。

# Grove - 16x2 LCDについて
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293692/1089e802-1d32-2cf2-2935-3fa6f3778ccc.png)

横１６文字、縦２行の文字を出力できるLCDです。文字と背景の色は変えられません。
PicoへはI2Cで接続します。よって、Grove Shieldの*I2C0*か*I2C1*に接続します。

公式のwikiでデバイスの詳細説明やArduinoを使ったサンプルコードが説明されています。
また、*Grove Shield for Pi Pico*のwikiでは、Pico + MicroPythonを使ったサンプルコードが説明されています。

- [LCDのwiki (Arduinoのサンプルコードあり)](https://wiki.seeedstudio.com/Grove-16x2_LCD_Series/)
- [Grove Shieldのwiki (MicroPytonのサンプルコードあり)](https://wiki.seeedstudio.com/Grove_Shield_for_Pi_Pico_V1.0/#project-of-detecting-temperature-and-humidity)
- [JDH_1804 データシート](https://files.seeedstudio.com/wiki/Grove-16x2_LCD_Series/res/JDH_1804_Datasheet.pdf)

# ソースコード
## 実装方針
今回ほしいのは**LCDモジュールをPicoで使うためのC++コード**です。しかし、以下３つのサンプルコードはいずれも今回ほしいコードから少しずつずれています。
そのため３つのサンプルコードを組み合わせて実装しました。

*参考にしたサンプルコード:*

1. [Grove-LCD RGB Backlight](https://github.com/Seeed-Studio/Grove_LCD_RGB_Backlight/archive/master.zip)
    - [LCDの公式wiki](https://wiki.seeedstudio.com/Grove-16x2_LCD_Series/)よりダウンロード可能
    - Arduino用のサンプルコード
    - *rgb_lcd.cpp* / *rgb_lcd.h*というC++で実装されたソースコードがある。
        - Arduinoで使うことを想定しているため、Picoではそのまま使えない。（具体的にはI2C通信の箇所を変更する必要がある）
        - 公式wikiでも説明されているが、背景色を自由に設定できるLCD用のコードとなっている。今回のLCDは背景色を変えられないので、背景色を設定する機能については使用できない
1. [Beginners Guide of Raspberry Pi Pico Based on MicroPython](https://files.seeedstudio.com/Seeed_EDU/Course_documents/Beginner's-Guide-for-Raspberry-Pi-Pico.zip)
    - Starter Kitのマニュアルや、それに含まれる複数のモジュールのサンプルコードが説明されている
    - Picoで使用できるMicroPythonのコード
1. [Picoのサンプルコード (pico-examples)](https://github.com/raspberrypi/pico-examples)
    - C++で実装されたサンプルコード
    - [LCDのサンプルコード](https://github.com/raspberrypi/pico-examples/tree/master/i2c/lcd_1602_i2c)もあるが、今回のLCD用というわけではない
    - Arduino用の*rgb_lcd.cpp*と比べるとかなり差分がある

実装方針は、はArduino用のソースコード(No.1)をベースとして、Arduinoに依存した実装をPicoサンプルコード(No.3)を参考にして修正しました。

## ソースコード
実装したコードをここに全て載せると長くなるので、重要な箇所だけ抜粋します。ソースコード全体は[Github](https://github.com/hiro-han/raspi-pico-grove-starter-kit/blob/main/src/lcd_1602.cpp)にアップしています。

- [Githubリポジトリ: raspi-pico-grove-starter-kit](https://github.com/hiro-han/raspi-pico-grove-starter-kit)
    - [LCD用のコード](https://github.com/hiro-han/raspi-pico-grove-starter-kit/blob/main/src/lcd_1602.cpp) 
    - [LCDを使うサンプルコード](https://github.com/hiro-han/raspi-pico-grove-starter-kit/blob/main/examples/lcd/lcd.cpp)

### 1. I2Cへデータ送信する処理
*Wire*はArduino用のライブラリなので、PicoのI2C用の関数**i2c_write_blocking()**に変更します。
変更前:

```c++:rgb_lcd.cpp
void i2c_send_byte(unsigned char dta) {
    Wire.beginTransmission(LCD_ADDRESS);        // transmit to device #4
    Wire.write(dta);                            // sends five bytes
    Wire.endTransmission();                     // stop transmitting
}

void i2c_send_byteS(unsigned char* dta, unsigned char len) {
    Wire.beginTransmission(LCD_ADDRESS);        // transmit to device #4
    for (int i = 0; i < len; i++) {
        Wire.write(dta[i]);
    }
    Wire.endTransmission();                     // stop transmitting
}
```
変更後:

```c++:lcd_1602.cpp
void Lcd1602::SendByte(unsigned char dta) {
  i2c_write_blocking(i2c_inst_, LCD_ADDRESS, &dta, 1, false);
}

void Lcd1602::SendByteS(const unsigned char* dta, unsigned char len) {
  i2c_write_blocking(i2c_inst_, LCD_ADDRESS, dta, len, false);
}
```

### 2. 文字列を出力する関数
文字列を出力するためにArduino用のサンプルでは`print()`関数を読んでいます（サンプルのHelloWorld.ino参照）が*rgb_lcd.cpp*にはそのような関数の実装がありません。
これは`rgb_lcdクラス`がPrintクラス`を継承しており、その継承元クラスの関数を呼んでいるためです。Picoにはそのようなクラスがないため、独自で実装する必要があります。

```c++:rgb_lcd.cpp
class rgb_lcd : public Print {
  ...
```

```c++:lcd_1602.cpp
void Lcd1602::Print(const std::string& str) {
  for (int i = 0; i < str.length(); i++) {
    unsigned char dta[2] = {LCD_SETCGRAMADDR, str.c_str()[i]};
    SendByteS(dta, 2);
  }
}
```

### 3. I2Cの初期化処理
PicoのI2Cを使うために、初期化処理を実装します。[Picoのサンプルコード](https://github.com/raspberrypi/pico-examples/blob/146680d625bb2238a84c01dee118351efb5ede4e/i2c/lcd_1602_i2c/lcd_1602_i2c.c#L137)では`i2c_default`を使うようになっています。これは実際には`i2c0`です。
I2C0とI2C1のそれぞれに対応できるように、以下のように実装します。

```c++:lcd_1602.cpp

Lcd1602::Lcd1602(const ShieldPort i2c) {
  if (i2c == ShieldPort::kI2C0) {
    i2c_inst_ = i2c0;
  } else if (i2c == ShieldPort::kI2C1) {
    i2c_inst_ = i2c1;
  } else {
    i2c_inst_ = i2c0;
  }

  i2c_init(i2c_inst_, 100 * 1000);
  ...
```

### 4. 背景色設定機能の削除
今回使用するLCDでは背景色の設定ができないため、該当する関数を削除します。

```c++
void setRGB(unsigned char r, unsigned char g, unsigned char b);
void setPWM(unsigned char color, unsigned char pwm);
void setColor(unsigned char color);
void setColorAll();
void setColorWhite();
```

### 5. その他変更箇所
クラス名や変数名、コーディングスタイルなどを自分の好みに変更しています。

## ビルド
pico-examplesと同じようなCMakeファイルを用意してビルドします。

```cmake:CMakeLists.txt

set(GROVE_LIB grove-sensors)
file(GLOB SOURCE *.cpp) 
add_library(${GROVE_LIB} STATIC ${SOURCE})
target_link_libraries(${GROVE_LIB} pico_stdlib hardware_i2c)
```

# 実行結果
![DSC_1112.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293692/4cc8f235-435b-978b-6894-e0f964c674b2.jpeg)

# 参考
- 今回実装したコード
    - [raspi-pico-grove-starter-kit](https://github.com/hiro-han/raspi-pico-grove-starter-kit)
    - [LCDを扱うコード](https://github.com/hiro-han/raspi-pico-grove-starter-kit/blob/main/src/lcd_1602.cpp)
    - [LCDを使うサンプル](https://github.com/hiro-han/raspi-pico-grove-starter-kit/blob/main/examples/lcd/lcd.cpp)
- [Raspi Pico用Grove Starter Kitを使ってみる](https://qiita.com/hiro-han/items/ffd2fb2501a1aeef4549)
- Grove LCD
    - [LCDのwiki (Arduinoのサンプルコードあり)](https://wiki.seeedstudio.com/Grove-16x2_LCD_Series/)
    - [JDH_1804 データシート](https://files.seeedstudio.com/wiki/Grove-16x2_LCD_Series/res/JDH_1804_Datasheet.pdf)
- Grove Shield
    - [Grove Shieldのwiki (MicroPytonのサンプルコードあり)](https://wiki.seeedstudio.com/Grove_Shield_for_Pi_Pico_V1.0/#project-of-detecting-temperature-and-humidity)
    - [Beginners Guide of Raspberry Pi Pico Based on MicroPython](https://files.seeedstudio.com/Seeed_EDU/Course_documents/Beginner's-Guide-for-Raspberry-Pi-Pico.zip)
- Pico
    - [Picoのサンプルコード (pico-examples)](https://github.com/raspberrypi/pico-examples)