Raspberry Pi Pico でCO2センサーを作成

Raspberry Pi Pico Pimoroni Pico Display Pack と NDIR (Non Dispersive Infrared:非分散型赤外線吸収) CO2センサーである MH-Z19C を使って CO2 センサーを作成します。 どの部品も比較的入手しやすいと思います。 既製品の CO2 センサーを買うより安く (約5000円) 実現できます。


CO2_jun_pico04H2_16.jpg


アプリケーションは Pico-SDK を使って、C/C++ で作成します。アプリケーションは以下の機能を持ちます。

CO2センサーを MicroPython で動作確認

まず、Raspberry Pi Pico と MH-Z19C NDIR (非分散型赤外線吸収法) CO2 Module をブレッドボードで仮組みして、CO2センサーの動作を MicroPython で簡単に確認しました。 色々条件を変えながら試すには MicroPython は便利です。

MH-Z19C の接続

Raspberry Pi Pico と CO2 センサー間は、電源系 (Vin, GND) の2本と、通信系 (Tx, Rx) の4本の接続が必要です。 MH-Z19Cモジュールの RX は Pico の UART1 TX (GP4:6ピン)、TX は Pico の UART1 RX (GP5:7ピン) に接続します。電源は40ピン(Vin:オレンジ) と 23ピン(GND:黒) に接続しました。

CO2S.jpg


公式サイトの Raspberry Pi Pico のピン配置 を参考にして下さい。

CO2 センサーの規格
Model No. MH-Z19C
Detection GasCO2
Working voltage5.0 ± 0.1V DC
Average current< 40mA (@5V power supply)
Peak current125mA (@5V power supply)
Interface level3.3 V (Compatible with 5V)
Detection Range400 - 5000ppm(optional)
Output signalSerial Port (UART) (TTL level 3.3V)
PWM
Preheat time1 min
Response TimeT90 < 120 s
Working temperature-10 ~ 50 ℃
Working humidity0 - 95% RH (No condensation)
Weight 5 g
Lifespan > 5 years
CO2濃度読み取りコマンド

Raspberry Pi Pico から次の9バイトを送ると、結果としてCO2濃度を含む9バイトが返ってきます。

コマンドの送信

CO2濃度の取得コマンドは次の1種類なので、9バイト目のチェックサムの値も決まっています。uart.write(バイト配列) を実行するだけです。

Byte0Byte1Byte2Byte3Byte4Byte5Byte6Byte7Byte8
Start ByteReservedCommand- - - - - Checksum
0xFF0x010x860x000x000x000x000x000x79
コマンド実行結果の受信

結果の取得は res=uart.read(9) で9バイトを受信し、HIGH8bit * 256 + LOW8bit がppm を単位とした CO2濃度となります。

Byte0Byte1Byte2Byte3Byte4Byte5Byte6Byte7Byte8
Start ByteCommandConc. Conc. ----Checksum
0xFF 0x86 HIGH8bit LOW8bit - - - - Checksum

MicroPython のダウンロード

ここから MicroPython の実行ファイル (rp2-pico-20210205-unstable-v1.14-8-g1f800cac3.uf2) をダウンロードします。

Raspberry Pi Pico への MicroPython の書込み

BOOTSELボタンを押しながら、MicroUSBケーブルで Pico を PC に接続すると、Pico は USBメモリとして認識されます。 そこにダウンロードしたUF2ファイルを書き込みます。 書込みが終了すると、Raspberry Pi Pico は自動的にリセットされて、フラッシュ上のMicroPython が起動した状態になります。

MicroPython の実行

USB経由で Raspberry Pi Pico の MicroPython とシリアル通信を行います。 まず、/dev/ttyACM0 を読み書きできるようにパーミッションを変更します。 次に「cu」というコマンドを使用してシリアル通信を行います。 入っていない場合は「sudo apt install cu」で簡単にインストールできます。「Connected.」の表示のあと、エンターを押すとコマンド入力できる状態になります。

$ sudo chmod 777 /dev/ttyACM0
[sudo] password for jun:
$ cu --line /dev/ttyACM0 --speed 115200
Connected.

>>>
CO2濃度の読み取り

今回は手抜きですが、以下のPython のソースをPythonのコンソールにペーストして実行しました。Raspberry Pi Pico と MH-Z19C の通信は、「9600ボー、データビット 8、パリティなし、ストップビット 1」 として設定します。

import utime
from machine import UART, Pin

com = bytearray([0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79])
uart = UART(1, 9600, parity=None, stop=1, bits=8, tx=Pin(4), rx=Pin(5))

uart.write(com)
utime.sleep(0.1)
res=uart.read(9)
print(res[2]*256+res[3])

このソースを Python のコンソールにペーストすると CO2濃度が ppm で返ります。 上記の簡単なコードで CO2センサーの動作確認ができたので、安心して Pico Display Pack でグラフィカルに表示するコードを C/C++ で作成できます。


C/C++ で Raspberry Pi Pico 用アプリケーションの開発

Getting started with Raspberry Pi Pico - C/C++ development with Raspberry Pi Pico には Raspberry Pi で開発する方法が書かれていますが、 ここでは、普通のPC用の Linux (Ubuntu 20.10) を使うことにします。 開発環境が Raspberry Pi でなくても、問題なくRaspberry Pi Pico のアプリケーションが作成できます。

開発環境のセットアップ

Raspberry Pi のセットアップ用のスクリプト (pico_setup.sh) をダウンロードして、実行権限を付けて実行します。 ARM用の VSCode のインストールはエラーとなるのでスキップする設定(SKIP_VSCODE=1)で実行します。 Raspberry Pi のUARTの設定もスキップします。

$ wget https://raw.githubusercontent.com/raspberrypi/pico-setup/master/pico_setup.sh
$ chmod +x pico_setup.sh
$ SKIP_VSCODE=1 SKIP_UART=1 ./pico_setup.sh

インストールされていなければ、必要なパッケージがインストールされます。 Ubuntu 20.10 で実行したところ、以下のパッケージのインストールが試みられました。

パッケージバージョン
autoconf2.69-11.1
automake1:1.16.2-4ubuntu1
build-essential12.8ubuntu3
cmake3.16.3-3ubuntu2
g++4:10.2.0-1ubuntu1
gcc4:10.2.0-1ubuntu1
libtool2.4.6-14
libusb-1.0-0-dev2:1.0.23-2build1
gcc-arm-none-eabi15:9-2019-q4-0ubuntu1
gdb-multiarch9.2-0ubuntu2
libftdi-dev0.20-4build8
texinfo6.7.0.dfsg.2-5
git1:2.27.0-1ubuntu1.1

pico-sdk の確認

pico というフォルダが作成されて、その中に7つのフォルダができます。

$ tree -L 1 pico
pico
|-- openocd
|-- pico-examples
|-- pico-extras
|-- pico-playground
|-- pico-sdk
|-- picoprobe
`-- picotool

pico-sdk のサンプルプログラム

サンプルプログラムは pico-examples フォルダの中にあり、Raspberry Pi Pico で実行できるファイルは pico-examples/build フォルダ以下にビルドされています。 例えば、/pico-examples/build/hello_world/usb にあるファイルのうち hello_usb.uf2 が転送して実行可能なファイルになります。

実行ファイル

実行ファイルは build フォルダ以下に作成されています。BOOTSELボタンを押しながら、MicroUSBケーブルで Pico を PC に接続すると、Pico は USBメモリとして認識されます。 そこに UF2 ファイルを書き込みます。 書込みが終了すると、Raspberry Pi Pico は自動的にリセットされて、書き込んだアプリケーションがフラッシュメモリ上で起動した状態になります。

jun@B550A:~/pico/pico-examples/build/hello_world/usb$ ls -lt
total 1604
-rw-rw-r-- 1 jun jun  67072 Apr 12 23:28 hello_usb.uf2
-rw-rw-r-- 1 jun jun 546835 Apr 12 23:28 hello_usb.dis
-rwxrwxr-x 1 jun jun  33384 Apr 12 23:28 hello_usb.bin
-rw-rw-r-- 1 jun jun  93963 Apr 12 23:28 hello_usb.hex
-rwxrwxr-x 1 jun jun 542104 Apr 12 23:28 hello_usb.elf
-rw-rw-r-- 1 jun jun 314200 Apr 12 23:28 hello_usb.elf.map
drwxrwxr-x 3 jun jun   4096 Apr 12 23:28 CMakeFiles
-rw-rw-r-- 1 jun jun 118008 Apr 12 23:28 Makefile
-rw-rw-r-- 1 jun jun    999 Apr 12 23:28 cmake_install.cmake
ソース

ソースは buildフォルダの外、pico-examples/hello_world/usb/ にあります。

jun@B550A:~/pico/pico-examples/hello_world/usb$ ls -l
total 8
-rw-rw-r-- 1 jun jun 626 Apr 12 23:28 CMakeLists.txt
-rw-rw-r-- 1 jun jun 283 Apr 12 23:28 hello_usb.c

Pimoroni Pico Display Pack用のライブラリ

Pimoroni Pico Display Pack用のライブラリをインストールします。 pico ディレクトリで git コマンドを使って、ライブラリをインストールします。

jun@B550A:~$ cd pico
jun@B550A:~/pico$ git clone https://github.com/pimoroni/pimoroni-pico.git
Cloning into 'pimoroni-pico'...
remote: Enumerating objects: 572, done.
remote: Counting objects: 100% (572/572), done.
remote: Compressing objects: 100% (342/342), done.
remote: Total 3198 (delta 295), reused 413 (delta 217), pack-reused 2626
Receiving objects: 100% (3198/3198), 775.97 KiB | 8.53 MiB/s, done.
Resolving deltas: 100% (1835/1835), done.

pimoroni-pico ディレクトリが pico-sdk と同じレベルに存在することを確認します。

jun@B550A:~/pico$ tree . -L 1
.
|-- openocd
|-- pico-examples
|-- pico-extras
|-- pico-playground
|-- pico-sdk
|-- picoprobe
|-- picotool
`-- pimoroni-pico

プロジェクトの作成

プロジェクトのサンプルとして、Pimoroni Pico Display Pack に文字を表示するアプリケーションを C/C++ で作成します。


Pimoroni Pico Display Pack で文字表示

プログラムを C++ で作成して、Pico Display Pack に文字を表示します。 CMake で独自プロジェクトを作成してビルドする手順を示します。

今回のプロジェクト用のフォルダ名を 「pico_02」 として、pico-sdk と pimoroni-pico と同じレベルに作成しました。 drivers と libraries は pimoroni-pico から必要なファイルだけを「pico_02」にコピーします。 source はこれから作成する アプリケーションのソースを格納します。 build ディレクトリはビルドした結果が格納されるディレクトリなので、最初は空で構いません。

pico
|-- pico_02
|   |-- build/
|   |-- drivers/
|   |-- libraries/
|   |-- source/
|   |-- CMakeLists.txt
|   `-- pico_sdk_import.cmake
|
|-- pico-sdk/
`-- pimoroni-pico/

libraries と drivers 以下のファイルは、pimoroni-pico から必要なファイルのみをコピーします。 pimoroni-pico/libraries 以下のpico_display と pico_graphics と CMakeLists.txt をコピーして、プロジェクトに加えます。

cd ~/pico
mkdir pico_02
cd pico_02/
cp -a ../pico-sdk/external/pico_sdk_import.cmake .
mkdir build source libraries drivers
cp -a ../pimoroni-pico/libraries/CMakeLists.txt libraries
cp -a ../pimoroni-pico/libraries/pico_display libraries
cp -a ../pimoroni-pico/libraries/pico_graphics libraries
cp -a ../pimoroni-pico/drivers/CMakeLists.txt drivers
cp -a ../pimoroni-pico/drivers/st7789 drivers

以上で pico_02 ディレクトリには次のようなディレクトリとファイルが存在します。

pico_02$ tree .
.
|-- build
|-- drivers
|   |-- CMakeLists.txt
|   `-- st7789
|       |-- CMakeLists.txt
|       |-- st7789.cmake
|       |-- st7789.cpp
|       `-- st7789.hpp
|-- libraries
|   |-- CMakeLists.txt
|   |-- pico_display
|   |   |-- CMakeLists.txt
|   |   |-- README.md
|   |   |-- pico_display.cmake
|   |   |-- pico_display.cpp
|   |   `-- pico_display.hpp
|   `-- pico_graphics
|       |-- CMakeLists.txt
|       |-- README.md
|       |-- font.hpp
|       |-- font6_data.hpp
|       |-- font8_data.hpp
|       |-- pico_graphics.cmake
|       |-- pico_graphics.cpp
|       |-- pico_graphics.hpp
|       `-- types.cpp
|-- pico_sdk_import.cmake
`-- source

7 directories, 21 files

赤字の部分の CMakeLists.txt の一部をコメントアウトする修正と、pico_02 直下にCMakeLists.txt の作成、source 以下に CMakeLists.txt と、Pico Display Pack で文字表示するためのソースファイル、font_test.cc を作成します。

./CMakeLists.txt
cmake_minimum_required(VERSION 3.13)

# Pull in PICO SDK (must be before project)
include(pico_sdk_import.cmake)

project(font_test C CXX ASM)

# Initialize the SDK
pico_sdk_init()

add_subdirectory(drivers)
add_subdirectory(libraries)
add_subdirectory(source)
libraries/CMakeLists.txt

不要な行を先頭に # を追加してコメントアウトします。

# add_subdirectory(breakout_roundlcd)
add_subdirectory(pico_graphics)
add_subdirectory(pico_display)
# add_subdirectory(pico_unicorn)
# add_subdirectory(pico_scroll)
# add_subdirectory(pico_explorer)
# add_subdirectory(pico_rgb_keypad)
drivers/CMakeLists.txt

不要な行を先頭に # を追加してコメントアウトします。

add_subdirectory(st7789)
# add_subdirectory(msa301)
# add_subdirectory(rv3028)
# add_subdirectory(vl53l1x)
source/CMakeLists.txt
add_executable(
  font_test
  font_test.cc
)

target_include_directories(font_test
  PUBLIC ${PROJECT_SOURCE_DIR}/libraries/pico_display
  )

# Pull in pico libraries that we need
target_link_libraries(font_test pico_stdlib hardware_spi hardware_pwm hardware_dma pico_display)

# create map/bin/hex file etc.
pico_add_extra_outputs(font_test)
source/font_test.cc
#include <string.h>
#include <math.h>

#include "pico_display.hpp"
#include "font8_data.hpp"

using namespace pimoroni;

uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT];
PicoDisplay pico_display(buffer);

int main() {
  pico_display.init();
  pico_display.set_backlight(100);
  pico_display.set_pen(0, 0, 240);
  pico_display.clear();
  buffer[0] = 0xff;
  pico_display.update();
  pico_display.set_font(&font8);
  pico_display.set_pen(255, 255, 255);
  pico_display.text("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
                     Point(0, 10), 240, 1);
  pico_display.text("abcdefghijklmnopqrstuvwxyz!#$%&'()[]",
                     Point(0, 20), 240, 1);
  pico_display.text("ABCDEFGHIJKLMNOPQRS",
                     Point(0, 40), 240, 2);
  pico_display.text("abcdefghijkl'()[]",
                     Point(0, 60), 240, 3);
  pico_display.set_pen(255, 255, 0);
  pico_display.text("ABC2345FGH",
                     Point(0, 90), 240, 4);
  buffer[0] = 0xff;
  pico_display.update();
}

ビルド

cd pico_02/build
cmake ..
make

ビルドした結果のファイルは、pico_02/build/source/ 以下に生成されます。

jun@B550A:~/pico_new/pico_02/build$ cd source/
jun@B550A:~/pico_new/pico_02/build/source$ ls -lt
total 2612
-rw-rw-r-- 1 jun jun  627651 Apr 17 01:44 font_test.dis
-rw-rw-r-- 1 jun jun   73728 Apr 17 01:44 font_test.uf2
-rwxrwxr-x 1 jun jun   36824 Apr 17 01:44 font_test.bin
-rw-rw-r-- 1 jun jun  103638 Apr 17 01:44 font_test.hex
-rwxrwxr-x 1 jun jun 1049072 Apr 17 01:44 font_test.elf
-rw-rw-r-- 1 jun jun  766760 Apr 17 01:44 font_test.elf.map
drwxrwxr-x 4 jun jun    4096 Apr 17 01:44 CMakeFiles
-rw-rw-r-- 1 jun jun   83379 Apr 17 01:43 Makefile
-rw-rw-r-- 1 jun jun     990 Apr 17 01:43 cmake_install.cmake
drwxrwxr-x 4 jun jun    4096 Apr 17 01:43 elf2uf2

Raspberry Pi Pico で実行

BOOTSELボタンを押しながら、MicroUSBケーブルで Pico を PC に接続すると、Pico は USBメモリとして認識されます。 そこに font_test.uf2 をコピーします。 書込みが終了すると、Raspberry Pi Pico は自動的にリセットされて、書き込んだアプリケーションがフラッシュメモリ上で起動します。

PicoDispFont8.jpg


Raspberry Pi Pico の CO2センサー

Raspberry Pi Pico には Pimoroni Pico Display Pack と接続するためにヘッダピンを使ってしまっています。 CO2センサー の MH-Z19C は直接配線をはんだ付けしました。

MHZ19C_PICO2.jpg


CO2センサー用のプロジェクトの作成

pico-sdk や pimoroni-pico が存在するフォルダに、pico_04というフォルダを作成します。 プロジェクト名を uart_mhz19c として、CO2センサーを実現するソースファイルを pico_04/source/uart_mhz19c.cpp として配置することとします。

pico_02 ディレクトリのコピー

前のプロジェクト(pico_02) を pico_04 としてコピーして、source 以下のファイルと pico_04/CMakeLists.txt を書き換えます。

cd ~/pico
cp -a pico_02 pico_04

プロジェクト(uart_mhz19c)

jun@B550A:~/pico$ tree . -L 1
..
|-- openocd
|-- pico-examples
|-- pico-extras
|-- pico-playground
|-- pico-project-generator
|-- pico-sdk
|-- pico_04
|-- picoprobe
|-- picotool
`-- pimoroni-pico
pico_04/CMakeLists.txt

プロジェクト を uart_mhz19c とします。

cmake_minimum_required(VERSION 3.13)

# Pull in PICO SDK (must be before project)
include(pico_sdk_import.cmake)

project(uart_mhz19c C CXX ASM)

# Initialize the SDK
pico_sdk_init()

add_subdirectory(drivers)
add_subdirectory(libraries)
add_subdirectory(source)
pico_04/source/CMakeLists.txt

ソースファイル(uart_mhz19c.cpp) と実行バイナリの名前(uart_mhz19c) を指定します。

add_executable(
  uart_mhz19c
  uart_mhz19c.cpp
)

target_include_directories(uart_mhz19c
  PUBLIC ${PROJECT_SOURCE_DIR}/libraries/pico_display
  )

# Pull in pico libraries that we need
target_link_libraries(uart_mhz19c pico_stdlib hardware_spi hardware_pwm hardware_dma pico_display)

# create map/bin/hex file etc.
pico_add_extra_outputs(uart_mhz19c)
pico_04/source/uart_mhz19c.cpp

CO2センサーを実現するソースコードです。 各種初期化の後、1秒毎のタイマー割り込みで、経過時間の時分秒を計算して、CO2濃度を64KBのリングバッファに記録します。 メインループでは、CO2濃度の取得と、ボタンの状態取得、表示する画面を作成して、Display Pack に転送して表示します。

/*
  uart_mhz19c.cpp
  2021-04-13 Jun Mizutani
*/

#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
#include "pico/stdlib.h"
#include "hardware/uart.h"
#include "hardware/irq.h"

#include "pico_display.hpp"
#include "font8_data.hpp"

using namespace pimoroni;
uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT];
PicoDisplay pico_display(buffer);

#define UART_ID uart0

unsigned char  cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
unsigned char  recv[256];
char           str[256];
unsigned short ppms[65536];
unsigned int   idx = 0;
unsigned int   ppm = 0;

void setup_pico_display()
{
    pico_display.init();
    pico_display.set_backlight(100);
    pico_display.set_pen(0, 0, 240);
    pico_display.clear();
    pico_display.update();
    pico_display.set_font(&font8);
    pico_display.set_pen(255, 255, 255);
}

static int rx_counts = 0;
static long int sec_total = 0;
static int sec = 0;
static int min = 0;
static int hour = 0;

bool a_pressed = false;
bool b_pressed = false;
bool x_pressed = false;
bool y_pressed = false;

// UART の受信
void on_uart_recv()
{
    unsigned char c;
    while (uart_is_readable(UART_ID)) {
        c = uart_getc(UART_ID);
        if (rx_counts < 255) recv[rx_counts++] = c;
    }
}

// UART の初期化
void setup_uart()
{
    uart_init(UART_ID, 9600);
    gpio_set_function(0, GPIO_FUNC_UART);
    gpio_set_function(1, GPIO_FUNC_UART);
    int actual = uart_set_baudrate(UART_ID, 9600);
    uart_set_hw_flow(UART_ID, false, false);
    uart_set_format(UART_ID, 8, 1, UART_PARITY_NONE);
    uart_set_fifo_enabled(UART_ID, false);
    int UART_IRQ = UART_ID == uart0 ? UART0_IRQ : UART1_IRQ;
    irq_set_exclusive_handler(UART_IRQ, on_uart_recv);
    irq_set_enabled(UART_IRQ, true);
    uart_set_irq_enables(UART_ID, true, false);
}

// 1秒毎のタイマー割り込み用のコールバック関数
// 起動後の経過時間 (時分秒)を取得
// 1秒毎のCO2濃度を16384個のリングバッファに記録
bool repeating_timer_callback(struct repeating_timer *t)
{
    sec_total++;
    sec = sec_total % 60;
    min = sec_total / 60;
    hour = min / 60;
    min = min % 60;
    // リングバッファのインデックスの更新
    idx++;
    idx = idx & 0xFFFF;
    ppms[idx] = ppm;
    return true;
}

// 画面の座標 (x, y) に col色の点を描画する
// 範囲(0..239, 0..134)外には書かない
void plot(int x, int y, int col)
{
   int wy = 134 - y;
   if ((x < 0) || (x > 239)) return;
   if ((wy < 0) || (wy > 134)) return;
   buffer[wy * 240 + x] = col;
}

// CO2濃度の65536個のリングバッファから i 秒前の値を取得
unsigned int get_ppm_history(int i)
{
    return ppms[(0x10000 + idx - i) & 0xFFFF];
}

// CO2濃度の値をグラフとして表示
// 右下方向に4ドットの点として表示
void plot_ppms()
{
    // [Y]表示スケール 400-3080ppm と 400-1740ppm
    int m = y_pressed ? 10 : 20;
    // [B]:10倍、[X]:24倍、[BX]:240倍 の時間スケール
    int t = b_pressed ? 10 : 1;
    int h = x_pressed ? 24 : 1;
    int scale = t * h;
    int pen = pico_display.create_pen(0, 255, 0);
    for (int i=0; i<240; i++) {
        int y = (get_ppm_history(i * scale) - 400) / m;
        plot(i, y, pen);
        plot(i+1, y, pen);
        plot(i, y+1, pen);
        plot(i+1, y+1, pen);
    }
    // 時間スケールの表示
    pico_display.set_pen(0, 255, 0);
    int step = 60;
    if (scale == 240) {
        pico_display.text("16 hrs", Point(172, 115), 240, 2);
        step = 15;
    } else if (scale == 10) {
        pico_display.text(" 40min", Point(172, 115), 240, 2);
    } else if (scale == 24) {
        pico_display.text(" 96min", Point(172, 115), 240, 2);
        step = 75;
    } else {
        pico_display.text(" 4 min", Point(172, 115), 240, 2);
    }
    pen = pico_display.create_pen(255, 255, 0);
    for (int i=0; i<240; i++) {
        if ((i % step) == 0) 
            for (int y=24; y<34; y++)
              plot(i, y, pen);
    }
}

// センサーからのデータのチェックサム計算用
unsigned char check_sum(unsigned char *recv)
{
    char sum;
    for(int i=1; i<8; i++) {
       sum += recv[i];
    }
    return (0xff - sum + 1);
}

// メイン
int main()
{
    setup_pico_display();
    setup_uart();

    // リピートタイマーの設定
    struct repeating_timer timer;
    add_repeating_timer_ms(-1000, repeating_timer_callback, NULL, &timer);
    
    // リングバッファの初期化
    for (int i; i<0x10000; i++) ppms[i] = 400;

    while (1) {
        rx_counts = 0;
        for (int i=0; i<9; i++) uart_putc_raw(UART_ID, cmd[i]);
        // 受信待ち 9600bps 
        sleep_ms(100);
        // 画面消去
        pico_display.set_pen(0, 0, 180);
        pico_display.clear();

        // Aボタンでバックライトの設定
        if (a_pressed) pico_display.set_backlight(50);
        else pico_display.set_backlight(100);

        // A,B,X,Y ボタンの状態表示
        pico_display.set_pen(255, 80, 255);
        if(pico_display.is_pressed(pico_display.A)) a_pressed = not a_pressed;
        if(pico_display.is_pressed(pico_display.B)) b_pressed = not b_pressed;
        if(pico_display.is_pressed(pico_display.X)) x_pressed = not x_pressed;
        if(pico_display.is_pressed(pico_display.Y)) y_pressed = not y_pressed;
        if (a_pressed) pico_display.text("A", Point(190, 0), 240, 2);
        else pico_display.text(".", Point(190, 0), 240, 2);
        if (b_pressed) pico_display.text("B", Point(200, 0), 240, 2);
        else pico_display.text(".", Point(200, 0), 240, 2);
        if (x_pressed) pico_display.text("X", Point(210, 0), 240, 2);
        else pico_display.text(".", Point(210, 0), 240, 2);
        if (y_pressed) pico_display.text("Y", Point(220, 0), 240, 2);
        else pico_display.text(".", Point(220, 0), 240, 2);

        // 経過時間の表示
        pico_display.set_pen(255, 255, 255);
        sprintf(str, " %02d:%02d:%02d", hour, min, sec);
        pico_display.text(str, Point(0, 0), 240, 2);

        // センサーとの通信状態の表示
        pico_display.set_pen(255, 255, 0);
        sprintf(str, " %2X,%2X %2d", check_sum(recv), recv[8], rx_counts);
        pico_display.text(str, Point(100, 0), 240, 2);
        
        if (rx_counts > 0) {
          // CO2濃度の取得
          ppm = recv[2]*256 + recv[3];

          // CO2濃度に応じた RGB LED の点灯
          if (ppm < 600) pico_display.set_led(0, 50.0, 0);
          else if (ppm < 800) pico_display.set_led(20, 40.0, 0);
          else if (ppm < 1000) pico_display.set_led(40.0,30.0,0);
          else if (ppm < 1500) pico_display.set_led(50.0, 0, 0);
          else pico_display.set_led(100.0, 0, 0);

          pico_display.set_pen(0, 255, 255);
          pico_display.text("CO2 Sensor", Point(10, 30), 240, 3);

          // CO2濃度の表示
          sprintf(str, "%5d ppm", ppm);
          pico_display.set_pen(255, 255, 255);
          pico_display.text(str, Point(20, 70), 240, 4);
        } else {
          pico_display.set_pen(255, 50, 50);
          pico_display.text("waiting", Point(30, 110), 240, 3);
        }
        // グラフの表示
        plot_ppms();
        pico_display.update();
        sleep_ms(900);
    }
}

ビルド

cd pico_04/build
cmake ..
make

ビルドした結果のファイルは、pico_04/build/source/ 以下にできます。

jun@B550A:~/pico/jun-pico04/build/source$ ls -ltF
total 2896
-rw-rw-r-- 1 jun jun  830345 Apr 13 23:38 uart_mhz19c.dis
-rw-rw-r-- 1 jun jun   96256 Apr 13 23:38 uart_mhz19c.uf2
-rwxrwxr-x 1 jun jun   47924 Apr 13 23:38 uart_mhz19c.bin*
-rw-rw-r-- 1 jun jun  134873 Apr 13 23:38 uart_mhz19c.hex
-rwxrwxr-x 1 jun jun 1053116 Apr 13 23:38 uart_mhz19c.elf*
-rw-rw-r-- 1 jun jun  774536 Apr 13 23:38 uart_mhz19c.elf.map
drwxrwxr-x 4 jun jun    4096 Apr 13 23:38 CMakeFiles/
-rw-rw-r-- 1 jun jun   83814 Apr 13 23:38 Makefile
-rw-rw-r-- 1 jun jun     989 Apr 13 23:38 cmake_install.cmake
drwxrwxr-x 4 jun jun    4096 Apr 13 23:38 elf2uf2/

Raspberry Pi Pico に転送して実行

BOOTSELボタンを押しながら、MicroUSBケーブルで Pico を PC に接続すると、Pico は USBメモリとして認識されます。 そこに ~/pico/jun-pico04/build/source にある uart_mhz19c.uf2 をコピーします。 書込みが終了すると、Raspberry Pi Pico は自動的にリセットされて、フラッシュ上の uart_mhz19c.uf2 が起動します。


CO2_jun_pico04H_16.jpg


モバイルバッテリーで76時間24分経過した状態です。CO2センサーの消費電力が比較的大きいので、移動しないのであれば USBのACアダプターで給電したほうが良さそうです。


続く...



このページの目次