Raspberry Pi Pico でCO2センサーを作成
Raspberry Pi Pico と Pimoroni Pico Display Pack と NDIR (Non Dispersive Infrared:非分散型赤外線吸収) CO2センサーである MH-Z19C を使って CO2 センサーを作成します。 どの部品も比較的入手しやすいと思います。 既製品の CO2 センサーを買うより安く (約5000円) 実現できます。
アプリケーションは Pico-SDK を使って、C/C++ で作成します。アプリケーションは以下の機能を持ちます。
- 最大18時間のCO2濃度の履歴を持ち、4分、40分、96分、16時間の範囲でグラフ表示可能。
- [A] ボタンを押すと、バックライトが 50% に暗くなる。
- [B] ボタンを押すと、グラフの時間範囲が10倍 (4 → 40分、96分 → 16時間)。
- [X] ボタンを押すと、グラフの時間範囲が24倍 (4 → 96分、40分 → 16時間) 。
- [Y] ボタンを押すと、縦方向の表示が 400-3080ppm と 400-1740ppm の切り替え。
- RGB LED は 600ppm 以下なら緑、800ppm 以下なら黃、1000ppm 以下ならオレンジ、1500ppm なら赤、それ以上は明るい赤に点灯。
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:黒) に接続しました。
公式サイトの Raspberry Pi Pico のピン配置 を参考にして下さい。
CO2 センサーの規格
Model No. | MH-Z19C |
---|---|
Detection Gas | CO2 |
Working voltage | 5.0 ± 0.1V DC |
Average current | < 40mA (@5V power supply) |
Peak current | 125mA (@5V power supply) |
Interface level | 3.3 V (Compatible with 5V) |
Detection Range | 400 - 5000ppm(optional) |
Output signal | Serial Port (UART) (TTL level 3.3V) |
PWM | |
Preheat time | 1 min |
Response Time | T90 < 120 s |
Working temperature | -10 ~ 50 ℃ |
Working humidity | 0 - 95% RH (No condensation) |
Weight | 5 g |
Lifespan | > 5 years |
CO2濃度読み取りコマンド
Raspberry Pi Pico から次の9バイトを送ると、結果としてCO2濃度を含む9バイトが返ってきます。
コマンドの送信
CO2濃度の取得コマンドは次の1種類なので、9バイト目のチェックサムの値も決まっています。uart.write(バイト配列) を実行するだけです。
Byte0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 | Byte8 |
---|---|---|---|---|---|---|---|---|
Start Byte | Reserved | Command | - | - | - | - | - | Checksum |
0xFF | 0x01 | 0x86 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x79 |
コマンド実行結果の受信
結果の取得は res=uart.read(9) で9バイトを受信し、HIGH8bit * 256 + LOW8bit がppm を単位とした CO2濃度となります。
Byte0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 | Byte8 |
---|---|---|---|---|---|---|---|---|
Start Byte | Command | Conc. | 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 で実行したところ、以下のパッケージのインストールが試みられました。
パッケージ | バージョン |
---|---|
autoconf | 2.69-11.1 |
automake | 1:1.16.2-4ubuntu1 |
build-essential | 12.8ubuntu3 |
cmake | 3.16.3-3ubuntu2 |
g++ | 4:10.2.0-1ubuntu1 |
gcc | 4:10.2.0-1ubuntu1 |
libtool | 2.4.6-14 |
libusb-1.0-0-dev | 2:1.0.23-2build1 |
gcc-arm-none-eabi | 15:9-2019-q4-0ubuntu1 |
gdb-multiarch | 9.2-0ubuntu2 |
libftdi-dev | 0.20-4build8 |
texinfo | 6.7.0.dfsg.2-5 |
git | 1: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 は自動的にリセットされて、書き込んだアプリケーションがフラッシュメモリ上で起動します。
Raspberry Pi Pico の CO2センサー
Raspberry Pi Pico には Pimoroni Pico Display Pack と接続するためにヘッダピンを使ってしまっています。 CO2センサー の MH-Z19C は直接配線をはんだ付けしました。
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 が起動します。
- 最大18時間のCO2濃度の履歴を持ち、4分、40分、96分、16時間の範囲でグラフ表示可能。
- [A] ボタンを押すと、バックライトが 50% に暗くなる。
- [B] ボタンを押すと、グラフの時間範囲が10倍 (4 → 40分、96分 → 16時間)。
- [X] ボタンを押すと、グラフの時間範囲が24倍 (4 → 96分、40分 → 16時間) 。
- [Y] ボタンを押すと、縦方向の表示が 400-3080ppm と 400-1740ppm の切り替え。
- RGB LED は 600ppm 以下なら緑、800ppm 以下なら黃、1000ppm 以下ならオレンジ、1500ppm なら赤、それ以上は明るい赤に点灯。
モバイルバッテリーで76時間24分経過した状態です。CO2センサーの消費電力が比較的大きいので、移動しないのであれば USBのACアダプターで給電したほうが良さそうです。