├── .gitignore ├── bmp ├── hpa.png ├── ppm.png ├── degc.png ├── step.png ├── digit16.png ├── digit32.png └── percent.png ├── img ├── board.jpg ├── example.jpg ├── layout.png ├── connection.png └── mh-z19c-dim.png ├── stl ├── case_back.stl ├── case_front.stl └── pole_attach.stl ├── src ├── digit16.hpp ├── digit32.hpp ├── images.hpp ├── digit32.cpp ├── digit16.cpp ├── mhz19c.hpp ├── ls027b4dh01.hpp ├── graph.hpp ├── msb1stimage.hpp ├── bme280.hpp ├── pico_env_mon.cpp └── images.cpp ├── Makefile.sample.mk ├── make_image_data.sh ├── CMakeLists.txt ├── LICENSE ├── make_image_data.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | build/ 3 | Makefile 4 | -------------------------------------------------------------------------------- /bmp/hpa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/bmp/hpa.png -------------------------------------------------------------------------------- /bmp/ppm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/bmp/ppm.png -------------------------------------------------------------------------------- /bmp/degc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/bmp/degc.png -------------------------------------------------------------------------------- /bmp/step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/bmp/step.png -------------------------------------------------------------------------------- /img/board.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/img/board.jpg -------------------------------------------------------------------------------- /bmp/digit16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/bmp/digit16.png -------------------------------------------------------------------------------- /bmp/digit32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/bmp/digit32.png -------------------------------------------------------------------------------- /bmp/percent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/bmp/percent.png -------------------------------------------------------------------------------- /img/example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/img/example.jpg -------------------------------------------------------------------------------- /img/layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/img/layout.png -------------------------------------------------------------------------------- /img/connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/img/connection.png -------------------------------------------------------------------------------- /stl/case_back.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/stl/case_back.stl -------------------------------------------------------------------------------- /stl/case_front.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/stl/case_front.stl -------------------------------------------------------------------------------- /img/mh-z19c-dim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/img/mh-z19c-dim.png -------------------------------------------------------------------------------- /stl/pole_attach.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/pico-env-mon/HEAD/stl/pole_attach.stl -------------------------------------------------------------------------------- /src/digit16.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DIGIT16_HPP 2 | #define DIGIT16_HPP 3 | 4 | #include "msb1stimage.hpp" 5 | 6 | int digit16_draw_char(Msb1stImage &dest, int x, int y, char c); 7 | int digit16_draw_string(Msb1stImage &dest, int x, int y, const char *s); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/digit32.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DIGIT32_HPP 2 | #define DIGIT32_HPP 3 | 4 | #include "msb1stimage.hpp" 5 | 6 | int digit32_draw_char(Msb1stImage &dest, int x, int y, char c); 7 | int digit32_draw_string(Msb1stImage &dest, int x, int y, const char *s); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/images.hpp: -------------------------------------------------------------------------------- 1 | #ifndef IMAGES_HPP 2 | #define IMAGES_HPP 3 | 4 | #include "msb1stimage.hpp" 5 | extern Msb1stImage img_digit16; 6 | extern Msb1stImage img_digit32; 7 | extern Msb1stImage img_degc; 8 | extern Msb1stImage img_percent; 9 | extern Msb1stImage img_hpa; 10 | extern Msb1stImage img_ppm; 11 | extern Msb1stImage img_step; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /Makefile.sample.mk: -------------------------------------------------------------------------------- 1 | .PHONY: all install clean 2 | 3 | REPO_DIR=$(shell pwd) 4 | SRC_DIR=src 5 | BUILD_DIR=build 6 | 7 | BIN_NAME=pico_env_mon.uf2 8 | BIN=$(BUILD_DIR)/$(BIN_NAME) 9 | 10 | SRC_LIST=$(wildcard $(SRC_DIR)/*.*) 11 | 12 | all: $(BIN) 13 | 14 | $(BIN): $(SRC_LIST) CMakeLists.txt 15 | mkdir -p $(BUILD_DIR) 16 | cd $(BUILD_DIR) \ 17 | && cmake .. \ 18 | && make -j 19 | @echo "------------------------------" 20 | @echo "UF2 File:" 21 | @echo $(REPO_DIR)/$(BIN) 22 | @ls -l $(REPO_DIR)/$(BIN) 23 | 24 | install: $(BIN) 25 | sudo mkdir -p /mnt/e 26 | sudo mount -t drvfs e: /mnt/e 27 | cp $(BIN) /mnt/e/. 28 | 29 | clean: 30 | rm -rf build 31 | -------------------------------------------------------------------------------- /make_image_data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo '#include "msb1stimage.hpp"' > ./src/images.cpp 4 | 5 | ./make_image_data.py ./bmp/digit16.png img_digit16 >> ./src/images.cpp 6 | ./make_image_data.py ./bmp/digit32.png img_digit32 >> ./src/images.cpp 7 | 8 | ./make_image_data.py ./bmp/degc.png img_degc >> ./src/images.cpp 9 | ./make_image_data.py ./bmp/percent.png img_percent >> ./src/images.cpp 10 | ./make_image_data.py ./bmp/hpa.png img_hpa >> ./src/images.cpp 11 | ./make_image_data.py ./bmp/ppm.png img_ppm >> ./src/images.cpp 12 | 13 | ./make_image_data.py ./bmp/step.png img_step >> ./src/images.cpp 14 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | # Pull in PICO SDK (must be before project) 4 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 5 | include(${PICO_SDK_PATH}/pico_sdk_init.cmake) 6 | 7 | project(pico_env_mon C CXX ASM) 8 | set(CMAKE_C_STANDARD 11) 9 | set(CMAKE_CXX_STANDARD 17) 10 | 11 | # Initialize the SDK 12 | pico_sdk_init() 13 | 14 | add_executable(pico_env_mon) 15 | 16 | target_sources( 17 | pico_env_mon PRIVATE 18 | src/pico_env_mon.cpp 19 | src/digit16.cpp 20 | src/digit32.cpp 21 | src/images.cpp 22 | ) 23 | 24 | target_link_libraries(pico_env_mon PRIVATE pico_stdlib hardware_spi) 25 | pico_add_extra_outputs(pico_env_mon) 26 | -------------------------------------------------------------------------------- /src/digit32.cpp: -------------------------------------------------------------------------------- 1 | #include "stdint.h" 2 | #include "msb1stimage.hpp" 3 | #include "images.hpp" 4 | 5 | int digit32_draw_char(Msb1stImage &dest, int x, int y, char c) { 6 | int w, h = 32; 7 | if ('0' <= c && c <= '9') { 8 | w = 20; 9 | dest.draw_image(img_digit32, x, y, w, h, w * (c - '0'), 0); 10 | } 11 | else if (c == ' ') { 12 | w = 20; 13 | // white space 14 | } 15 | else if (c == '-') { 16 | w = 12; 17 | dest.draw_image(img_digit32, x, y, w, h, 208, 0); 18 | } 19 | else { 20 | w = 8; 21 | dest.draw_image(img_digit32, x, y, w, h, 200, 0); 22 | } 23 | return w; 24 | } 25 | 26 | int digit32_draw_string(Msb1stImage &dest, int x, int y, const char *s) { 27 | const char *ptr = s; 28 | char c; 29 | while ((c = *(ptr++))) { 30 | x += digit32_draw_char(dest, x, y, c); 31 | x += 4; 32 | } 33 | return x; 34 | } 35 | -------------------------------------------------------------------------------- /src/digit16.cpp: -------------------------------------------------------------------------------- 1 | #include "stdint.h" 2 | #include "msb1stimage.hpp" 3 | #include "images.hpp" 4 | 5 | int digit16_draw_char(Msb1stImage &dest, int x, int y, char c) { 6 | int w, h = 16; 7 | if ('0' <= c && c <= '9') { 8 | w = 10; 9 | dest.draw_image(img_digit16, x, y, w, h, w * (c - '0'), 0); 10 | } 11 | else if (c == ' ') { 12 | w = 6; 13 | // white space 14 | } 15 | else if (c == '-') { 16 | w = 6; 17 | dest.draw_image(img_digit16, x, y, w, h, 104, 0); 18 | } 19 | else if (c == '/') { 20 | w = 6; 21 | dest.draw_image(img_digit16, x, y, w, h, 110, 0); 22 | } 23 | else { 24 | w = 4; 25 | dest.draw_image(img_digit16, x, y, w, h, 100, 0); 26 | } 27 | return w; 28 | } 29 | 30 | int digit16_draw_string(Msb1stImage &dest, int x, int y, const char *s) { 31 | const char *ptr = s; 32 | char c; 33 | while ((c = *(ptr++))) { 34 | x += digit16_draw_char(dest, x, y, c); 35 | x += 2; 36 | } 37 | return x; 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 shapoco 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /make_image_data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from PIL import Image 4 | 5 | import sys 6 | import numpy as np 7 | 8 | arg_input_image = sys.argv[1] 9 | arg_array_name = sys.argv[2] 10 | 11 | img = Image.open(arg_input_image) 12 | width, height = img.size 13 | img_pixels = np.array([[img.getpixel((x,y)) for x in range(width)] for y in range(height)]) 14 | 15 | print() 16 | print('const uint8_t %s_data[] = {' % arg_array_name) 17 | print(' %d, %d, %d, %d,' % (width % 256, width // 256, height % 256, height // 256)) 18 | 19 | index = 0 20 | stride = (width + 7) // 8 21 | length = stride * height 22 | 23 | for y in range(height): 24 | for x_step in range(stride): 25 | byte = 0 26 | for x_sub in range(8): 27 | x = x_step * 8 + x_sub 28 | if x < width: 29 | pixel = img_pixels[y, x] 30 | if pixel[0] != 0: 31 | byte |= 1 << (7 - x_sub) 32 | if (index % 16 == 0): 33 | print(' ', end='') 34 | print('0x%02x, ' % byte, end='') 35 | if (index % 16 == 15) or (index == length - 1): 36 | print() 37 | index += 1 38 | 39 | print('};') 40 | print() 41 | print('Msb1stImage %s((uint8_t*)%s_data);' % (arg_array_name, arg_array_name)) 42 | -------------------------------------------------------------------------------- /src/mhz19c.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MHZ19C_HPP 2 | #define MHZ19C_HPP 3 | 4 | #include "stdint.h" 5 | #include "pico/stdlib.h" 6 | #include "hardware/uart.h" 7 | 8 | class MHZ19C { 9 | public: 10 | uart_inst_t * const uart; 11 | const int pin_tx; 12 | const int pin_rx; 13 | 14 | MHZ19C(uart_inst_t * uart, int pin_tx, int pin_rx) : 15 | uart(uart), pin_tx(pin_tx), pin_rx(pin_rx) 16 | { } 17 | 18 | void init() { 19 | uart_init(uart, 9600); 20 | gpio_set_function(pin_tx, GPIO_FUNC_UART); 21 | gpio_set_function(pin_rx, GPIO_FUNC_UART); 22 | 23 | // disable auto calibration 24 | uint8_t cmd[] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}; 25 | uint8_t resp[9]; 26 | uart_write_blocking(uart, cmd, 9); 27 | sleep_ms(100); 28 | while (uart_is_readable(uart)){ 29 | uart_read_blocking(uart, resp, 1); 30 | } 31 | sleep_ms(100); 32 | 33 | // dummy read 34 | int dummy; 35 | measure(&dummy); 36 | } 37 | 38 | bool measure(int *result) { 39 | uint8_t cmd[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; 40 | uint8_t resp[9]; 41 | uart_write_blocking(uart, cmd, 9); 42 | uart_read_blocking(uart, resp, 9); 43 | 44 | if (resp[0] == 0xff && resp[1] == 0x86) { 45 | *result = resp[2] * 256 + resp[3]; 46 | return true; 47 | } 48 | else { 49 | *result = -1; 50 | return false; 51 | } 52 | } 53 | }; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/ls027b4dh01.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LS027B4DH01_HPP 2 | #define LS027B4DH01_HPP 3 | 4 | #include "stdint.h" 5 | #include "pico/stdlib.h" 6 | #include "hardware/spi.h" 7 | #include "msb1stimage.hpp" 8 | 9 | static constexpr int SCREEN_WIDTH = 400; 10 | static constexpr int SCREEN_HEIGHT = 240; 11 | static constexpr int SCREEN_STRIDE = (SCREEN_WIDTH + 7) / 8; 12 | 13 | class LcdScreen : public Msb1stImage { 14 | private: 15 | uint8_t buff_data[SCREEN_STRIDE * SCREEN_HEIGHT]; 16 | public: 17 | LcdScreen() : Msb1stImage(SCREEN_WIDTH, SCREEN_HEIGHT, (uint8_t*)buff_data) {} 18 | }; 19 | 20 | class LcdDriver { 21 | private: 22 | bool _com_state = 0; 23 | public: 24 | spi_inst_t * const spi; 25 | const int pin_scs; 26 | const int pin_disp; 27 | const int pin_extcomin; 28 | 29 | LcdDriver(spi_inst_t *spi, int pin_scs, int pin_disp, int pin_extcomin) : 30 | spi(spi), pin_scs(pin_scs), pin_disp(pin_disp), pin_extcomin(pin_extcomin) 31 | { } 32 | 33 | void init() { 34 | gpio_init(pin_scs); 35 | gpio_init(pin_disp); 36 | gpio_init(pin_extcomin); 37 | gpio_set_dir(pin_scs, GPIO_OUT); 38 | gpio_set_dir(pin_disp, GPIO_OUT); 39 | gpio_set_dir(pin_extcomin, GPIO_OUT); 40 | gpio_put(pin_scs, 0); 41 | gpio_put(pin_disp, 0); 42 | gpio_put(pin_extcomin, 0); 43 | } 44 | 45 | void disp_on() { gpio_put(pin_disp, 1); } 46 | void disp_off() { gpio_put(pin_disp, 0); } 47 | 48 | static uint8_t revert_byte(uint8_t b) { 49 | return 50 | ((b << 7) & 0x80) | 51 | ((b << 5) & 0x40) | 52 | ((b << 3) & 0x20) | 53 | ((b << 1) & 0x10) | 54 | ((b >> 1) & 0x08) | 55 | ((b >> 3) & 0x04) | 56 | ((b >> 5) & 0x02) | 57 | ((b >> 7) & 0x01); 58 | } 59 | 60 | void write(const uint8_t *data, int height = SCREEN_HEIGHT, int dest_y = 0) { 61 | uint8_t spi_byte; 62 | 63 | uint8_t gate_line = dest_y + 1; 64 | 65 | // Make sure EXTCOMIN is low for next rising edge 66 | gpio_put(pin_extcomin, 0); 67 | 68 | // chip select 69 | gpio_put(pin_scs, 1); 70 | sleep_us(3); // tsSCS minimum setup time is 3 us 71 | 72 | // SHARP LCD must have liquid crystal cell polarity inverted at between 0.5 and 20 Hz to prevent charge built up over time 73 | // Period between COM inversions must be kept as equal as practical 74 | // When EXTMODE is HIGH, EXTCOMIN rising edge arms polarity inversion to occur on next SCS falling edge 75 | gpio_put(pin_extcomin, 1); // Arm COM inversion, once per full frame 76 | sleep_us(1); // twEXTCOMIN minimum high time is 1 us 77 | gpio_put(pin_extcomin, 0); 78 | 79 | // mode select 80 | spi_byte = revert_byte(0x01); 81 | spi_write_blocking(spi, &spi_byte, 1); 82 | for (int src_y = 0; src_y < height; src_y++) { 83 | // line select 84 | spi_byte = revert_byte(gate_line); 85 | spi_write_blocking(spi, &spi_byte, 1); 86 | 87 | // write line 88 | spi_write_blocking(spi, data, SCREEN_STRIDE); 89 | 90 | // dummy write 91 | spi_byte = 0x00; 92 | spi_write_blocking(spi, &spi_byte, 1); 93 | 94 | data += SCREEN_STRIDE; 95 | gate_line += 1; 96 | } 97 | 98 | // dummy write 99 | spi_byte = 0x00; 100 | spi_write_blocking(spi, &spi_byte, 1); 101 | 102 | // chip deselect 103 | sleep_us(1); // thSCS is minimum 1 us 104 | gpio_put(pin_scs, 0); 105 | sleep_us(1); // twSCSL is minimum 1 us 106 | 107 | } 108 | 109 | }; 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /src/graph.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GRAPH_HPP 2 | #define GRAPH_HPP 3 | 4 | #include "stdint.h" 5 | #include "math.h" 6 | #include "msb1stimage.hpp" 7 | 8 | class Graph { 9 | public: 10 | // number of samples 11 | static const int DEPTH = 240; 12 | 13 | // size 14 | static const int WIDTH = DEPTH; 15 | static const int HEIGHT = 60; 16 | 17 | static const int VERTICAL_LINE_STEP = WIDTH / 8; 18 | 19 | // position 20 | const int left, top; 21 | 22 | // minimum vertical range 23 | const float min_range; 24 | 25 | float min_log[DEPTH]; 26 | float max_log[DEPTH]; 27 | int num_data = 0; 28 | 29 | float total_min = 0; 30 | float total_max = 0; 31 | 32 | float horizontal_line_step = 1; 33 | 34 | Graph(int x, int y, float min_range) : 35 | left(x), top(y), min_range(min_range) 36 | { } 37 | 38 | void push(float value, bool shift) { 39 | if (num_data == 0) { 40 | num_data = 1; 41 | min_log[0] = value; 42 | max_log[0] = value; 43 | } 44 | else if (shift) { 45 | for (int i = DEPTH - 1; i >= 1; i--) { 46 | min_log[i] = min_log[i-1]; 47 | max_log[i] = max_log[i-1]; 48 | } 49 | min_log[0] = value; 50 | max_log[0] = value; 51 | if (num_data < DEPTH) { 52 | num_data += 1; 53 | } 54 | } 55 | else { 56 | if (min_log[0] > value) min_log[0] = value; 57 | if (max_log[0] < value) max_log[0] = value; 58 | } 59 | 60 | if (num_data == 0) { 61 | total_min = 0; 62 | total_max = 0; 63 | } 64 | else { 65 | total_min = min_log[0]; 66 | total_max = max_log[0]; 67 | for (int i = 1; i < num_data; i++) { 68 | if (min_log[i] < total_min) total_min = min_log[i]; 69 | if (max_log[i] > total_max) total_max = max_log[i]; 70 | } 71 | } 72 | 73 | } 74 | 75 | void render(Msb1stImage &dest) { 76 | if (num_data <= 0) return; 77 | 78 | int x_plot = left + WIDTH - 1; 79 | float total_center = (total_min + total_max) / 2; 80 | float y_range = total_max - total_min; 81 | if (y_range < min_range) y_range = min_range; 82 | float y_zoom = HEIGHT / y_range; 83 | 84 | int y_offset = top + HEIGHT / 2 + total_center * y_zoom; 85 | for (int i = 0; i < num_data; i++) { 86 | int y_top = y_offset - max_log[i] * y_zoom; 87 | int y_bottom = y_offset - min_log[i] * y_zoom; 88 | 89 | dest.fill_rect(x_plot, y_top - 1, 1, y_bottom - y_top + 2, 0); 90 | dest.fill_rect_with_pattern( 91 | x_plot, y_bottom, 1, top + HEIGHT - y_bottom); 92 | 93 | x_plot -= 1; 94 | } 95 | 96 | float level_top = (y_offset - top) / y_zoom; 97 | float level_bottom = (y_offset - (top + HEIGHT)) / y_zoom; 98 | float y_scale = level_top - level_bottom; 99 | 100 | // step of horizontal lines 101 | if (y_scale > 1000) horizontal_line_step = 500; 102 | else if (y_scale > 500) horizontal_line_step = 200; 103 | else if (y_scale > 200) horizontal_line_step = 100; 104 | else if (y_scale > 100) horizontal_line_step = 50; 105 | else if (y_scale > 50) horizontal_line_step = 20; 106 | else if (y_scale > 20) horizontal_line_step = 10; 107 | else if (y_scale > 10) horizontal_line_step = 5; 108 | else if (y_scale > 5) horizontal_line_step = 2; 109 | else if (y_scale > 2) horizontal_line_step = 1; 110 | else if (y_scale > 1) horizontal_line_step = 0.5f; 111 | else horizontal_line_step = 0.2f; 112 | 113 | // draw horizontal lines 114 | float level = horizontal_line_step * ceilf(level_bottom / horizontal_line_step); 115 | while (level < level_top) { 116 | int y_line = y_offset - level * y_zoom; 117 | dest.draw_horizontal_dotted_line(left, y_line, WIDTH); 118 | level += horizontal_line_step; 119 | } 120 | 121 | // draw vertical lines 122 | for (int x_line = left + WIDTH - 1; x_line > left; x_line -= VERTICAL_LINE_STEP) { 123 | dest.draw_vertical_dotted_line(x_line, top, HEIGHT); 124 | } 125 | } 126 | }; 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /src/msb1stimage.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MSB1STIMAGE_HPP 2 | #define MSB1STIMAGE_HPP 3 | 4 | #include "stdint.h" 5 | 6 | class Msb1stImage { 7 | public: 8 | const int width; 9 | const int height; 10 | const int stride; 11 | uint8_t *data; 12 | 13 | Msb1stImage(int w, int h, uint8_t *data) : 14 | width(w), height(h), stride((w + 7) / 8), data(data) 15 | { } 16 | 17 | Msb1stImage(uint8_t *data) : 18 | width((int)data[0] + ((int)data[1] << 8)), 19 | height((int)data[2] + ((int)data[3] << 8)), 20 | stride((width + 7) / 8), 21 | data(data + 4) 22 | { } 23 | 24 | static void clip_copy_rect(int sw, int sh, int dw, int dh, int *sl, int *st, int *dl, int *dt, int *w, int *h) { 25 | // s: source, d: destination 26 | // w: width, h: height 27 | // l: left, t: top, r: right, b: bottom 28 | if (*sl < 0) { *dl += *sl; *w += *sl; *sl = 0; } 29 | if (*st < 0) { *dl += *sl; *h += *st; *st = 0; } 30 | if (*dl < 0) { *sl += *dl; *w += *dl; *dl = 0; } 31 | if (*dt < 0) { *sl += *dl; *h += *dt; *dt = 0; } 32 | int sr = *sl + *w; 33 | int sb = *st + *h; 34 | int dr = *dl + *w; 35 | int db = *dt + *h; 36 | if (sr > sw) { *w -= (sr-sw); dr -= (sr-sw); } 37 | if (sb > sh) { *h -= (sb-sh); db -= (sb-sh); } 38 | if (dr > dw) { *w -= (dr-dw); } 39 | if (db > dh) { *h -= (db-dh); } 40 | } 41 | 42 | static void clip_fill_rect(int dw, int dh, int *dl, int *dt, int *w, int *h) { 43 | int sl = 0, st = 0; // dummy 44 | clip_copy_rect(dw, dh, dw, dh, &sl, &st, dl, dt, w, h); 45 | } 46 | 47 | void set_pixel(int x, int y, uint8_t color) { 48 | if (x < 0 || x >= width || y < 0 || y >= height) return; 49 | 50 | int i = y * stride + (x / 8); 51 | uint8_t mask = 1 << (~x & 0x7); 52 | if (color) { 53 | data[i] |= mask; 54 | } 55 | else { 56 | data[i] &= ~mask; 57 | } 58 | } 59 | 60 | uint8_t get_pixel(int x, int y) { 61 | if (x < 0 || x >= width || y < 0 || y >= height) return 0; 62 | 63 | int i = y * stride + (x / 8); 64 | uint8_t mask = 1 << (~x & 0x7); 65 | return data[i] & mask ? 1 : 0; 66 | } 67 | 68 | void invert_pixel(int x, int y) { 69 | if (x < 0 || x >= width || y < 0 || y >= height) return; 70 | 71 | int i = y * stride + (x / 8); 72 | uint8_t mask = 1 << (~x & 0x7); 73 | data[i] ^= mask; 74 | } 75 | 76 | void fill_rect(int x0, int y0, int w, int h, uint8_t color) { 77 | clip_fill_rect(width, height, &x0, &y0, &w, &h); 78 | int x1 = x0 + w; 79 | int y1 = y0 + h; 80 | for (int y = y0; y < y1; y++) { 81 | for (int x = x0; x < x1; x++) { 82 | set_pixel(x, y, color); 83 | } 84 | } 85 | } 86 | 87 | void fill_rect_with_pattern(int x0, int y0, int w, int h) { 88 | clip_fill_rect(width, height, &x0, &y0, &w, &h); 89 | int x1 = x0 + w; 90 | int y1 = y0 + h; 91 | for (int y = y0; y < y1; y++) { 92 | for (int x = x0; x < x1; x++) { 93 | set_pixel(x, y, (x + y) & 1); 94 | } 95 | } 96 | } 97 | 98 | void draw_horizontal_dotted_line(int x0, int y0, int w) { 99 | int h = 1; 100 | clip_fill_rect(width, height, &x0, &y0, &w, &h); 101 | if (h < 1) return; 102 | int x1 = x0 + w; 103 | for (int x = x0; x < x1; x += 3) { 104 | invert_pixel(x, y0); 105 | } 106 | } 107 | 108 | void draw_vertical_dotted_line(int x0, int y0, int h) { 109 | int w = 1; 110 | clip_fill_rect(width, height, &x0, &y0, &w, &h); 111 | if (w < 1) return; 112 | int y1 = y0 + h; 113 | for (int y = y0; y < y1; y += 3) { 114 | invert_pixel(x0, y); 115 | } 116 | } 117 | 118 | void clear(uint8_t color) { 119 | uint8_t value = color ? 0xff : 0x00; 120 | for (int i = 0; i < stride * height; i++) { 121 | data[i] = value; 122 | } 123 | } 124 | 125 | void draw_image(Msb1stImage &src, int dx0, int dy0, int w, int h, int sx0, int sy0) { 126 | clip_copy_rect(src.width, src.height, width, height, &sx0, &sy0, &dx0, &dy0, &w, &h); 127 | int sx1 = sx0 + w; 128 | int sy1 = sy0 + h; 129 | 130 | int dy = dy0; 131 | for (int sy = sy0; sy < sy1; sy++) { 132 | int dx = dx0; 133 | for (int sx = sx0; sx < sx1; sx++) { 134 | set_pixel(dx, dy, src.get_pixel(sx, sy)); 135 | dx += 1; 136 | } 137 | dy += 1; 138 | } 139 | } 140 | 141 | void draw_image(Msb1stImage &src, int dx, int dy) { 142 | draw_image(src, dx, dy, src.width, src.height, 0, 0); 143 | } 144 | 145 | }; 146 | 147 | #endif 148 | -------------------------------------------------------------------------------- /src/bme280.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BME280_HPP 2 | #define BME280_HPP 3 | 4 | #include "stdint.h" 5 | #include "pico/stdlib.h" 6 | #include "hardware/spi.h" 7 | 8 | class BME280 { 9 | 10 | public: 11 | spi_inst_t * const spi; 12 | const int pin_csb; 13 | 14 | uint16_t dig_T1; 15 | int16_t dig_T2; 16 | int16_t dig_T3; 17 | uint16_t dig_P1; 18 | int16_t dig_P2; 19 | int16_t dig_P3; 20 | int16_t dig_P4; 21 | int16_t dig_P5; 22 | int16_t dig_P6; 23 | int16_t dig_P7; 24 | int16_t dig_P8; 25 | int16_t dig_P9; 26 | uint8_t dig_H1; 27 | int16_t dig_H2; 28 | uint8_t dig_H3; 29 | int16_t dig_H4; 30 | int16_t dig_H5; 31 | int8_t dig_H6; 32 | 33 | int32_t t_fine; 34 | 35 | BME280(spi_inst_t *spi, int pin_csb) : 36 | spi(spi), pin_csb(pin_csb) 37 | { } 38 | 39 | void cs_select() { 40 | asm volatile("nop \n nop \n nop"); 41 | gpio_put(pin_csb, 0); 42 | asm volatile("nop \n nop \n nop"); 43 | } 44 | 45 | void cs_deselect() { 46 | asm volatile("nop \n nop \n nop"); 47 | gpio_put(pin_csb, 1); 48 | asm volatile("nop \n nop \n nop"); 49 | } 50 | 51 | void write_reg(uint8_t addr, uint8_t data) { 52 | uint8_t buff[2]; 53 | buff[0] = addr & 0x7f; 54 | buff[1] = data; 55 | cs_select(); 56 | spi_write_blocking(spi, buff, 2); 57 | cs_deselect(); 58 | sleep_ms(10); 59 | } 60 | 61 | void read_reg(uint8_t addr, uint8_t *buff, uint16_t len) { 62 | addr |= 0x80; 63 | cs_select(); 64 | spi_write_blocking(spi, &addr, 1); 65 | sleep_ms(10); 66 | spi_read_blocking(spi, 0, buff, len); 67 | cs_deselect(); 68 | sleep_ms(10); 69 | } 70 | 71 | void init() { 72 | uint8_t buff[26]; 73 | 74 | gpio_init(pin_csb); 75 | gpio_set_dir(pin_csb, GPIO_OUT); 76 | gpio_put(pin_csb, 1); 77 | sleep_ms(100); 78 | 79 | read_reg(0x88, buff, 26); 80 | 81 | dig_T1 = (uint16_t)buff[0] | ((uint16_t)buff[1] << 8); 82 | dig_T2 = (uint16_t)buff[2] | ((uint16_t)buff[3] << 8); 83 | dig_T3 = (uint16_t)buff[4] | ((uint16_t)buff[5] << 8); 84 | 85 | dig_P1 = (uint16_t)buff[6] | ((uint16_t)buff[7] << 8); 86 | dig_P2 = (uint16_t)buff[8] | ((uint16_t)buff[9] << 8); 87 | dig_P3 = (uint16_t)buff[10] | ((uint16_t)buff[11] << 8); 88 | dig_P4 = (uint16_t)buff[12] | ((uint16_t)buff[13] << 8); 89 | dig_P5 = (uint16_t)buff[14] | ((uint16_t)buff[15] << 8); 90 | dig_P6 = (uint16_t)buff[16] | ((uint16_t)buff[17] << 8); 91 | dig_P7 = (uint16_t)buff[18] | ((uint16_t)buff[19] << 8); 92 | dig_P8 = (uint16_t)buff[20] | ((uint16_t)buff[21] << 8); 93 | dig_P9 = (uint16_t)buff[22] | ((uint16_t)buff[23] << 8); 94 | 95 | dig_H1 = buff[25]; 96 | 97 | read_reg(0xe1, buff, 7); 98 | 99 | dig_H2 = (uint16_t)buff[0] | ((uint16_t)buff[1] << 8); 100 | dig_H3 = (int8_t)buff[2]; 101 | dig_H4 = ((uint16_t)buff[3] << 4) | (buff[4] & 0xf); 102 | dig_H5 = ((buff[4] >> 4) & 0xf) | ((uint16_t)buff[5] << 4); 103 | dig_H6 = (int8_t)buff[6]; 104 | 105 | write_reg(0xf2, 0x1); 106 | write_reg(0xf4, 0x24); 107 | } 108 | 109 | int32_t compensate_T(int32_t adc_T) 110 | { 111 | int32_t var1, var2, T; 112 | var1 = ((((adc_T>>3) - ((int32_t)dig_T1<<1))) * ((int32_t)dig_T2)) >> 11; 113 | var2 = (((((adc_T>>4) - ((int32_t)dig_T1)) * ((adc_T>>4) - ((int32_t)dig_T1))) >> 12) * 114 | ((int32_t)dig_T3)) >> 14; 115 | t_fine = var1 + var2; 116 | T = (t_fine * 5 + 128) >> 8; 117 | return T; 118 | } 119 | 120 | uint32_t compensate_P(int32_t adc_P) 121 | { 122 | int64_t var1, var2, p; 123 | var1 = ((int64_t)t_fine) - 128000; 124 | var2 = var1 * var1 * (int64_t)dig_P6; 125 | var2 = var2 + ((var1*(int64_t)dig_P5)<<17); 126 | var2 = var2 + (((int64_t)dig_P4)<<35); 127 | var1 = ((var1 * var1 * (int64_t)dig_P3)>>8) + ((var1 * (int64_t)dig_P2)<<12); 128 | var1 = (((((int64_t)1)<<47)+var1))*((int64_t)dig_P1)>>33; 129 | if (var1 == 0) 130 | { 131 | return 0; 132 | } 133 | p = 1048576-adc_P; 134 | p = (((p<<31)-var2)*3125)/var1; 135 | var1 = (((int64_t)dig_P9) * (p>>13) * (p>>13)) >> 25; 136 | var2 = (((int64_t)dig_P8) * p) >> 19; 137 | p = ((p + var1 + var2) >> 8) + (((int64_t)dig_P7)<<4); 138 | return (uint32_t)p; 139 | } 140 | 141 | uint32_t compensate_H(int32_t adc_H) 142 | { 143 | int32_t v_x1_u32r; 144 | v_x1_u32r = (t_fine - ((int32_t)76800)); 145 | v_x1_u32r = (((((adc_H << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1_u32r)) + 146 | ((int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)dig_H6)) >> 10) * (((v_x1_u32r * 147 | ((int32_t)dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) * 148 | ((int32_t)dig_H2) + 8192) >> 14)); 149 | v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4)); 150 | v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r); 151 | v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r); 152 | return (uint32_t)(v_x1_u32r>>12); 153 | } 154 | 155 | void read_raw(int32_t *temperature, int32_t *humidity, int32_t *pressure) { 156 | uint8_t buff[8]; 157 | write_reg(0xf4, 0x25); 158 | sleep_ms(20); 159 | read_reg(0xf7, buff, 8); 160 | *pressure = ((uint32_t) buff[0] << 12) | ((uint32_t) buff[1] << 4) | (buff[2] >> 4); 161 | *temperature = ((uint32_t) buff[3] << 12) | ((uint32_t) buff[4] << 4) | (buff[5] >> 4); 162 | *humidity = (uint32_t) buff[6] << 8 | buff[7]; 163 | } 164 | 165 | void read_env(float *temperature, float *humidity, float *pressure) { 166 | int32_t h, p, t; 167 | read_raw(&t, &h, &p); 168 | *temperature = compensate_T(t) / 100.0f; 169 | *pressure = compensate_P(p) / 25600.0f; 170 | *humidity = compensate_H(h) / 1024.0f; 171 | } 172 | }; 173 | 174 | #endif 175 | -------------------------------------------------------------------------------- /src/pico_env_mon.cpp: -------------------------------------------------------------------------------- 1 | //#define PICO_DEFAULT_SPI 0 2 | //#define PICO_DEFAULT_SPI_TX_PIN 19 3 | //#define PICO_DEFAULT_SPI_RX_PIN 20 4 | //#define PICO_DEFAULT_SPI_SCK_PIN 18 5 | 6 | #include 7 | 8 | #include "pico/stdlib.h" 9 | #include "pico/binary_info.h" 10 | #include "hardware/spi.h" 11 | 12 | #include "ls027b4dh01.hpp" 13 | #include "images.hpp" 14 | #include "digit16.hpp" 15 | #include "digit32.hpp" 16 | 17 | #include "graph.hpp" 18 | 19 | #include "bme280.hpp" 20 | 21 | #include "mhz19c.hpp" 22 | 23 | static const int LCD_TOGGLE_INTERVAL_MS = 500; 24 | 25 | static const int SAMPLING_INTERVAL_MS = 5000; 26 | 27 | static const int GRAPH_TIME_RANGE_H = 24; 28 | static const int GRAPH_SHIFT_INTERVAL_MS = GRAPH_TIME_RANGE_H * 3600 * 1000 / Graph::DEPTH; 29 | 30 | // 温度は湿度・気圧の補正用であり気温よりやや高いため適当に補正する 31 | // 補正値の適正値はセンサの使用条件により異なる 32 | static const float TEMPERATURE_OFFSET = -1.5f; 33 | 34 | LcdScreen screen; 35 | LcdDriver lcd(spi_default, 20, 22, 21); 36 | 37 | BME280 bme280(spi_default, 17); 38 | MHZ19C mhz19c(uart0, 0, 1); 39 | 40 | Graph graph_t(0, 0, 1.0f); // temperature 41 | Graph graph_h(0, 60, 10.0f); // humidity 42 | Graph graph_p(0, 120, 1.0f); // pressure 43 | Graph graph_c(0, 180, 10.0f); // CO2 44 | 45 | static void sample(bool shift); 46 | 47 | int main() { 48 | stdio_init_all(); 49 | 50 | spi_init(spi_default, 2000 * 1000); 51 | gpio_set_function(PICO_DEFAULT_SPI_RX_PIN, GPIO_FUNC_SPI); 52 | gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI); 53 | gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI); 54 | 55 | lcd.init(); 56 | bme280.init(); 57 | mhz19c.init(); 58 | screen.clear(1); 59 | lcd.write(screen.data); 60 | lcd.disp_on(); 61 | 62 | absolute_time_t t_next_lcd_toggle = make_timeout_time_ms(LCD_TOGGLE_INTERVAL_MS); 63 | int sampling_interval_counter = SAMPLING_INTERVAL_MS; 64 | int graph_shift_interval_counter = 0; 65 | 66 | while (true) { 67 | // keep interval 68 | sleep_until(t_next_lcd_toggle); 69 | t_next_lcd_toggle = delayed_by_ms(t_next_lcd_toggle, LCD_TOGGLE_INTERVAL_MS); 70 | 71 | sampling_interval_counter += LCD_TOGGLE_INTERVAL_MS; 72 | graph_shift_interval_counter += LCD_TOGGLE_INTERVAL_MS; 73 | 74 | // sampling timing 75 | if (sampling_interval_counter > SAMPLING_INTERVAL_MS) { 76 | sampling_interval_counter -= SAMPLING_INTERVAL_MS; 77 | 78 | // graph shift timing 79 | bool shift = false; 80 | if (graph_shift_interval_counter > GRAPH_SHIFT_INTERVAL_MS) { 81 | graph_shift_interval_counter -= GRAPH_SHIFT_INTERVAL_MS; 82 | shift = true; 83 | } 84 | 85 | sample(shift); 86 | } 87 | 88 | // LCD update 89 | lcd.write(screen.data); 90 | } 91 | } 92 | 93 | static void sample(bool shift) { 94 | // read BME280 95 | float temperature, pressure, humidity; 96 | bme280.read_env(&temperature, &humidity, &pressure); 97 | temperature += TEMPERATURE_OFFSET; 98 | 99 | // read CO2 100 | int co2; 101 | mhz19c.measure(&co2); 102 | 103 | // enter new value to the graphs 104 | graph_t.push(temperature, shift); 105 | graph_h.push(humidity, shift); 106 | graph_p.push(pressure, shift); 107 | graph_c.push(co2, shift); 108 | 109 | int x_value = 245; 110 | char s[8]; 111 | 112 | absolute_time_t t_start = get_absolute_time(); 113 | 114 | screen.clear(1); 115 | 116 | // temperature 117 | { 118 | int y = graph_t.top; 119 | int x_unit; 120 | 121 | // graph 122 | graph_t.render(screen); 123 | 124 | // current value 125 | sprintf(s, "%-.1f", temperature); 126 | x_unit = digit32_draw_string(screen, x_value, y + 4, s); 127 | screen.draw_image(img_degc, x_unit, y + 16); 128 | 129 | // scale, min/max 130 | screen.draw_image(img_step, x_value, y + 40); 131 | sprintf(s, "%-.1f %-.1f/%-.1f", graph_t.horizontal_line_step, graph_t.total_min, graph_t.total_max); 132 | digit16_draw_string(screen, x_value + img_step.width + 2, y + 40, s); 133 | 134 | // horizontal line 135 | screen.fill_rect(0, y + graph_t.HEIGHT - 1, screen.width, 1, 0); 136 | } 137 | 138 | // humidity 139 | { 140 | int y = graph_h.top; 141 | int x_unit; 142 | 143 | // graph 144 | graph_h.render(screen); 145 | 146 | // current value 147 | sprintf(s, "%-.1f", humidity); 148 | x_unit = digit32_draw_string(screen, x_value, y + 4, s); 149 | screen.draw_image(img_percent, x_unit, y + 16); 150 | 151 | // scale, min/max 152 | screen.draw_image(img_step, x_value, y + 40); 153 | sprintf(s, "%-.1f %-.1f/%-.1f", graph_h.horizontal_line_step, graph_h.total_min, graph_h.total_max); 154 | digit16_draw_string(screen, x_value + img_step.width + 2, y + 40, s); 155 | 156 | // horizontal line 157 | screen.fill_rect(0, y + graph_h.HEIGHT - 1, screen.width, 1, 0); 158 | } 159 | 160 | // pressure 161 | { 162 | int y = graph_p.top; 163 | int x_unit; 164 | 165 | // graph 166 | graph_p.render(screen); 167 | 168 | // current value 169 | sprintf(s, "%-.0f", pressure); 170 | x_unit = digit32_draw_string(screen, x_value, y + 4, s); 171 | screen.draw_image(img_hpa, x_unit, y + 16); 172 | 173 | // scale, min/max 174 | screen.draw_image(img_step, x_value, y + 40); 175 | sprintf(s, "%-.1f %-.0f/%-.0f", graph_p.horizontal_line_step, graph_p.total_min, graph_p.total_max); 176 | digit16_draw_string(screen, x_value + img_step.width + 2, y + 40, s); 177 | 178 | // horizontal line 179 | screen.fill_rect(0, y + graph_p.HEIGHT - 1, screen.width, 1, 0); 180 | } 181 | 182 | // CO2 183 | { 184 | int y = graph_c.top; 185 | int x_unit; 186 | 187 | // graph 188 | graph_c.render(screen); 189 | 190 | // current value 191 | sprintf(s, "%d", co2); 192 | x_unit = digit32_draw_string(screen, x_value, y + 4, s); 193 | screen.draw_image(img_ppm, x_unit, y + 16); 194 | 195 | // scale, min/max 196 | screen.draw_image(img_step, x_value, y + 40); 197 | sprintf(s, "%-.0f %-.0f/%-.0f", graph_c.horizontal_line_step, graph_c.total_min, graph_c.total_max); 198 | digit16_draw_string(screen, x_value + img_step.width + 2, y + 40, s); 199 | } 200 | 201 | absolute_time_t t_end = get_absolute_time(); 202 | 203 | // show screen update time 204 | //int64_t t_elapsed_us = absolute_time_diff_us(t_start, t_end); 205 | //sprintf(s, "%ld", t_elapsed_us); 206 | //digit16_draw_string(screen, 0, 0, s); 207 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pico-env-mon 2 | 3 | ~~秋月電子だけ~~ ネットで買える部品でできる環境モニタです。 4 | 5 | 温度、湿度、気圧、CO2濃度を 5秒間隔で取得し、24時間のトレンドグラフとともに表示します。 6 | 7 | 反射型のディスプレイなので寝室等でも眩しくありません。 8 | 9 | ---- 10 | 11 | ## 作例 12 | 13 | 3Dプリンタで作ったケースに収めた例です。 14 | 15 | ![作例の写真](img/example.jpg) 16 | 17 | ![基板の写真](img/board.jpg) 18 | 19 | ---- 20 | 21 | ## 使用部品 22 | 23 | - ⛔ : 2024/12/09 現在 在庫なし 24 | - ❓ : 使用実績なし、動作未確認。購入前によくご確認ください 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 | 50 | 51 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 71 | 72 | 73 | 74 | 75 | 80 | 81 | 82 | 83 | 84 | 85 | 88 | 89 | 90 | 91 | 92 | 93 | 96 | 97 | 98 | 99 | 100 | 101 | 105 | 106 | 107 | 108 | 109 | 110 | 114 | 115 | 116 | 117 | 118 | 122 | 123 | 124 | 125 | 126 | 127 | 131 | 132 | 133 | 134 | 135 | 136 | 139 | 140 | 141 | 142 | 143 | 144 | 148 | 149 | 150 | 151 | 152 | 156 | 157 | 158 | 159 | 160 | 164 | 165 | 166 | 167 | 168 | 169 | 172 | 173 | 174 | 175 | 176 | 177 | 180 | 181 | 182 |
項目型番・仕様購入先 (例)備考
マイコンRaspberry Pi Pico
(RP2040)
37 | 秋月: 116132 38 |
LCD
(右のいずれか)
LS027B7DH01 45 | DigiKey: 425-2907-ND 46 | *1
LS027B7DH01A 52 | DigiKey: 425-2908-ND 53 |
LS027B4DH01 58 | 秋月(単品): 104944
59 | 秋月(キット): 106875
60 | スイッチサイエンス: 1607
61 | 共立: N1BSC7⛔ 62 |
LCD 用コネクタ
ブレイクアウトボード
(右のいずれか)
AE-CNCONV-10P-0.5 69 | 秋月: 107253 70 |
EPA0139 76 | 楽天: 201808epa0139
77 | Amazon: B07H2FTQJM
78 | Aliexpress 検索❓ 79 |
温湿度・気圧センサAE-BME280 86 | 秋月: 109421 87 |
CO2 センサMH-Z19C 94 | 秋月: 116142 95 | *2
ゼロ点補正スイッチ適当なタクトスイッチ 102 | 秋月: 検索 103 | (例: 103647) 104 |
バイパスコンデンサセラミック 0.1uF 111 | 秋月: 検索 112 | (例: 113582) 113 |
セラミック 1uF 119 | 秋月: 検索 120 | (例: 115874) 121 |
ノイズ対策用
追加コンデンサ
電解コンデンサ 47uF 128 | 秋月: 検索 129 | (例: 110270) 130 |
センサ固定用
ピンヘッダ
FHU-1x42SG 137 | 秋月: 105779 138 |
ユニバーサル基板
(右のいずれか)
秋月Bタイプ 2.54mm 145 | 秋月: 検索 146 | (例: 100181) 147 | *2, *3
秋月Bタイプ 1.27mm 153 | 秋月: 検索 154 | (例: 117829) 155 |
配線類UEW など 161 | 秋月: 検索 162 | (例: 109583) 163 |
USB電源5V 170 | 秋月: 113658 171 | *4
microUSBケーブル通信用のもの 178 | 秋月: 107607 179 |
183 | 184 | \*1) LS027B7DH01 と LS027B7DH01A の違い: 185 | 186 | > フィックスAの付いている部品は、1つのディスプレイにつき最大2ピクセル(ドット抜け)の欠陥を許容しますが、サフィックスAの付いていない部品は1ピクセルの欠陥も許容しません。
187 | > [Sharp LS027B7DH01とLS027B7DH01Aの違いについて - 日本語フォーラム / 部品品番 - Electronic Component and Engineering Solution Forum - TechForum │ DigiKey](https://forum.digikey.com/t/sharp-ls027b7dh01-ls027b7dh01a/17466) 188 | 189 | \*2) :warning: MH-Z19C には購入時点でピンヘッダが付いていてそれ自体は 2.54mm ピッチですが、2 つのヘッダの間隔が 2.54mm ピッチのユニバーサル基板に適合しません。1.27mm ピッチのユニバーサル基板を使用するか、ピンヘッダを外してスズメッキ線等で配線してください。 190 | 191 | ![](img/mh-z19c-dim.png) 192 | 193 | \*3) :warning: ケース用の STL データを使用する場合は適切な大きさにカットする必要があります (後述)。 194 | 195 | \*4) :warning: 市販の一般的な USB-AC アダプタでかまいませんが、電圧が低いものや不安定なものだと LCD の動作が不安定になることがあります。 196 | 197 | ---- 198 | 199 | ## 接続 200 | 201 | |Pico|LS027B7DH01(A)
LS027B4DH01|BME280|MH-Z19C| 202 | |:--|:--|:--|:--| 203 | |`VBUS`|`VDD`, `VDDA`, `EXTMODE`
セラミック 1uF||`Vin`
セラミック 1uF (+ 電解 47uF)| 204 | |`3V3`||`VDD`|| 205 | |`GND`|`VSS`, `VSSA`|`GND`|`GND`| 206 | |`GPIO0` (uart0 TX)|||`RX`| 207 | |`GPIO1` (uart1 RX)|||`TX`| 208 | |`GPIO16` (spi0 RX)||`SDO`| 209 | |`GPIO17` (spi0 CSn)||`CSB`| 210 | |`GPIO18` (spi0 SCLK)|`SCLK`|`SCK`| 211 | |`GPIO19` (spi0 TX)|`SI`|`SDI`| 212 | |`GPIO20`|`SCS`|| 213 | |`GPIO21`|`EXTCOMIN`|| 214 | |`GPIO22`|`DISP`
セラミック 0.1uF|| 215 | 216 | ![接続図](img/connection.png) 217 | 218 | ---- 219 | 220 | ## 配線・組み立てについて 221 | 222 | ### 注意事項 223 | 224 | :warning: LCD のフレキケーブルは非常に破損しやすいので慎重に扱ってください。 225 | 226 | :warning: CO2センサー MH-Z19C は内蔵された電球の駆動のために定期的に大きな電流を吸い込みます。これが LCD の動作に悪影響を与えないよう、配線の引き回しやコンデンサの配置に注意する必要があります。 227 | 228 | - MH-Z19C と LCD の電源ラインは根元から別々に配線してください。 229 | - MH-Z19C と LCD それぞれの直近にセラミックコンデンサを実装してください。 230 | - これでも LCD の表示に問題が生じた場合は: 231 | - MH-Z19C の直近に電解コンデンサ 47uF を追加してみてください。 232 | - USB 電源の電圧が低すぎないか確認してみてください。 233 | 234 | ### ケースの STL について 235 | 236 | [ケース用の STL のサンプル](stl/) を公開しています。2.54mm ピッチのユニバーサル基板を 24穴 x 17穴 にカットし、以下のようにレイアウトする前提の形状になっています。 基板を搭載する部分の内寸は 66 x 48mm です。 237 | 238 | ![](img/layout.png) 239 | 240 | ---- 241 | 242 | ## プログラム 243 | 244 | コンパイル済みバイナリをダウンロードしてそのまま書き込むか、またはソースコードからビルドしてください。 245 | 246 | ### コンパイル済みバイナリ 247 | 248 | 1. [Releases](https://github.com/shapoco/pico-env-mon/releases) から `pico_env_mon_x.x.zip` をダウンロードし、展開します。 249 | 2. Raspberry Pi Pico の `BOOT SEL`ボタンを押しながら USBケーブルを接続し、書き込みモードにします (USBストレージとして認識されます)。 250 | 3. `pico_env_mon.uf2` を Raspberry Pi Pico に書き込みます。 251 | 252 | ### ソースコードからのビルド 253 | 254 | ビルドするには Raspberry Pi Pico SDK が必要です。インストール方法はググってください。Windows の場合は WSL2上にインストールすることをお勧めします。 255 | 256 | 1. 本リポジトリを clone し、リポジトリのディレクトリ内でビルドを実行します。 257 | 258 | ```sh 259 | git clone https://github.com/shapoco/pico-env-mon.git 260 | cd pico-env-mon 261 | mkdir build 262 | cd build 263 | cmake .. 264 | make 265 | ``` 266 | 267 | 3. Raspberry Pi Pico の `BOOT SEL`ボタンを押しながら USBケーブルを接続し、書き込みモードにします (USBストレージとして認識されます)。 268 | 4. `pico-env-mon/build/pico_env_mon.uf2` を Raspberry Pi Pico に書き込みます。 269 | 270 | ---- 271 | 272 | ## 調整 273 | 274 | ### BME280 の温度 275 | 276 | BME280 で取得できる温度の値は湿度と気圧の補正のためのもので、実際の気温より若干高くなります。 277 | 278 | > The integrated temperature sensor has been optimized for lowest noise and highest resolution. Its output is used for temperature compensation of the pressure and humidity sensors and can also be used for estimation of the ambient temperature. 279 | 280 | > Temperature measured by the internal temperature sensor. This temperature value depends on the PCB temperature, sensor element self-heating and ambient temperature and is typically above ambient temperature. 281 | 282 | 実際の気温とどの程度乖離するかはセンサの使用条件によります。ケースに入れたり、BME280 を CO2センサに近接させたりすると乖離が大きくなります。本プロジェクトではこれらを考慮し、デフォルトでは取得した温度から 1.5℃ 減算した値を表示します。 283 | 284 | 補正値を変更するには、`src/pico_env_mon.cpp` の次の箇所を変更してビルドし直してください。 285 | 286 | ```c++ 287 | static const float TEMPERATURE_OFFSET = -1.5f; 288 | ``` 289 | 290 | ### MH-Z19C のキャリブレーション 291 | 292 | MH-Z19C は初期状態では正しい値を示さないケースがあるようです。 293 | 294 | 屋外に放置して値が 400ppm 付近まで下がらない場合や、人がいる室内なのに 400ppm 付近に張り付いてしまう場合はキャリブレーションを実施してください。 295 | 296 | 参考: [Home Assistant: ESP32+MH-Z19CでCO2モニター構築(2) [キャリブレーション編] - Sympapaのスマートホーム日記](https://sympapa.hatenablog.com/entry/2022/06/05/091621) 297 | 298 | 1. 本機を屋外や誰もいない風通しの良い場所に 20分間放置します。 299 | 2. 吐息がかからないように息を止めて、ゼロ点補正 (Zero Point Calibration) スイッチを 7秒間押下します。 300 | 301 | なお、本プロジェクトではオートキャリブレーションは無効化されています。 302 | 303 | ### 時間軸のスケール変更 304 | 305 | デフォルトでは 24時間分のグラフが表示されます。 306 | 307 | スケールを変更するには、`src/pico_env_mon.cpp` の次の箇所を変更してビルドし直してください。 308 | 309 | ```c++ 310 | static const int GRAPH_TIME_RANGE_H = 24; 311 | ``` 312 | 313 | ---- 314 | -------------------------------------------------------------------------------- /src/images.cpp: -------------------------------------------------------------------------------- 1 | #include "msb1stimage.hpp" 2 | 3 | const uint8_t img_digit16_data[] = { 4 | 116, 0, 16, 0, 5 | 0xe1, 0xfc, 0x7f, 0x3f, 0xcf, 0xc7, 0xe0, 0x1c, 0x7c, 0x00, 0xf3, 0xfc, 0xff, 0xff, 0x80, 0xc0, 6 | 0xf8, 0x7c, 0x0f, 0x03, 0xc7, 0xe0, 0x1c, 0x7c, 0x00, 0xc0, 0xf0, 0x3f, 0xff, 0x80, 0x80, 0x70, 7 | 0x78, 0x06, 0x01, 0x87, 0xe0, 0x1c, 0x7c, 0x00, 0x80, 0x60, 0x1f, 0xff, 0x80, 0x8c, 0x70, 0x70, 8 | 0xc0, 0x30, 0x8f, 0xe3, 0xf8, 0xfc, 0x78, 0x0c, 0x23, 0x0f, 0xff, 0x80, 0x0c, 0x34, 0x79, 0xe2, 9 | 0x78, 0x8f, 0xc7, 0xf8, 0xfc, 0x70, 0x1e, 0x07, 0x8f, 0xff, 0x10, 0x1e, 0x3c, 0x7f, 0xe3, 0xf0, 10 | 0x08, 0xc4, 0xf8, 0x3f, 0xf1, 0x0c, 0x07, 0x8f, 0xff, 0x10, 0x1e, 0x3c, 0x7f, 0xc3, 0x81, 0x18, 11 | 0xc0, 0x38, 0x0f, 0xf1, 0x80, 0x47, 0x8f, 0xff, 0x10, 0x1e, 0x3c, 0x7f, 0x87, 0x83, 0x18, 0xc0, 12 | 0x10, 0x07, 0xe1, 0xc0, 0xe3, 0x0f, 0x03, 0x10, 0x1e, 0x3c, 0x7f, 0x0f, 0x81, 0x18, 0xc7, 0x00, 13 | 0xc7, 0xe3, 0x80, 0x60, 0x0f, 0x02, 0x30, 0x1e, 0x3c, 0x7e, 0x1f, 0xf0, 0x00, 0x3f, 0x81, 0xe3, 14 | 0xe3, 0x0c, 0x30, 0x1f, 0x02, 0x30, 0x1e, 0x3c, 0x7c, 0x3f, 0xf8, 0x00, 0x3f, 0x81, 0xe3, 0xe3, 15 | 0x1e, 0x3c, 0x1f, 0xfe, 0x30, 0x0c, 0x3c, 0x78, 0x7e, 0x78, 0x00, 0x27, 0x81, 0xe3, 0xc3, 0x1e, 16 | 0x3f, 0x1f, 0xfe, 0x30, 0x8c, 0x7c, 0x78, 0xfc, 0x30, 0xf8, 0xc3, 0x10, 0xc7, 0xc7, 0x0c, 0x3f, 17 | 0x19, 0xfc, 0x70, 0x80, 0x7c, 0x70, 0x02, 0x01, 0xf8, 0xe0, 0x18, 0x07, 0xc7, 0x80, 0x7e, 0x30, 18 | 0xfc, 0x70, 0xc0, 0xfc, 0x70, 0x03, 0x03, 0xf8, 0xf0, 0x3c, 0x0f, 0xc7, 0xc0, 0xfe, 0x30, 0xfc, 19 | 0x70, 0xe1, 0xfc, 0x70, 0x03, 0xcf, 0xf8, 0xfc, 0xff, 0x3f, 0xc7, 0xf3, 0xfe, 0x39, 0xfc, 0x70, 20 | }; 21 | 22 | Msb1stImage img_digit16((uint8_t*)img_digit16_data); 23 | 24 | const uint8_t img_digit32_data[] = { 25 | 220, 0, 32, 0, 26 | 0xfc, 0x03, 0xff, 0xf0, 0x7f, 0xf8, 0x03, 0xff, 0x80, 0x3f, 0xfc, 0x0f, 0xfc, 0x00, 0x03, 0xf8, 27 | 0x1f, 0xf0, 0x00, 0x00, 0xfc, 0x03, 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xf0, 0xf0, 0x00, 0xff, 0xf0, 28 | 0x7f, 0xe0, 0x00, 0xfe, 0x00, 0x0f, 0xfc, 0x1f, 0xfc, 0x00, 0x03, 0xf8, 0x3f, 0xf0, 0x00, 0x00, 29 | 0xf0, 0x00, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xf0, 0xe0, 0x00, 0x7f, 0xe0, 0x7f, 0xc0, 0x00, 0x7c, 30 | 0x00, 0x07, 0xf8, 0x1f, 0xfc, 0x00, 0x03, 0xf0, 0x3f, 0xf0, 0x00, 0x00, 0xe0, 0x00, 0x7e, 0x00, 31 | 0x07, 0xff, 0xff, 0xf0, 0xc0, 0x00, 0x3f, 0xc0, 0x7f, 0x80, 0x00, 0x38, 0x00, 0x03, 0xf8, 0x1f, 32 | 0xfc, 0x00, 0x03, 0xf0, 0x3f, 0xf0, 0x00, 0x00, 0xc0, 0x00, 0x3c, 0x00, 0x03, 0xff, 0xff, 0xf0, 33 | 0xc0, 0x00, 0x3e, 0x00, 0x7f, 0x00, 0x00, 0x10, 0x00, 0x01, 0xf8, 0x3f, 0xfc, 0x00, 0x03, 0xf0, 34 | 0x7f, 0xf0, 0x00, 0x00, 0x80, 0x00, 0x18, 0x00, 0x01, 0xff, 0xff, 0xf0, 0x80, 0x60, 0x1e, 0x00, 35 | 0x7f, 0x00, 0xe0, 0x10, 0x0e, 0x01, 0xf0, 0x3f, 0xf8, 0x1f, 0xff, 0xe0, 0x7f, 0xf0, 0x7f, 0xc1, 36 | 0x80, 0x60, 0x18, 0x06, 0x01, 0xff, 0xff, 0xf0, 0x81, 0xf8, 0x1e, 0x00, 0x7f, 0x83, 0xf8, 0x08, 37 | 0x3f, 0x80, 0xf0, 0x3f, 0xf8, 0x1f, 0xff, 0xe0, 0x7f, 0xf0, 0x7f, 0xc1, 0x01, 0xf8, 0x00, 0x1f, 38 | 0x80, 0xff, 0xff, 0xf0, 0x81, 0xf8, 0x1e, 0x00, 0x7f, 0xc7, 0xfc, 0x0c, 0x7f, 0xc0, 0xf0, 0x7f, 39 | 0xf8, 0x1f, 0xff, 0xe0, 0xff, 0xf0, 0x7f, 0x81, 0x03, 0xfc, 0x00, 0x3f, 0xc0, 0xff, 0xff, 0xf0, 40 | 0x03, 0xfc, 0x0e, 0x10, 0x7f, 0xef, 0xfc, 0x0e, 0xff, 0xc0, 0xe0, 0x7f, 0xf8, 0x1f, 0xff, 0xc0, 41 | 0xff, 0xff, 0xff, 0x81, 0x03, 0xfc, 0x00, 0x3f, 0xc0, 0xff, 0xff, 0xf0, 0x03, 0xfc, 0x0f, 0xf0, 42 | 0x7f, 0xff, 0xfe, 0x0f, 0xff, 0xc0, 0xe0, 0x7f, 0xf8, 0x1f, 0xff, 0xc0, 0xff, 0xff, 0xff, 0x83, 43 | 0x03, 0xfc, 0x00, 0x7f, 0xe0, 0xff, 0xff, 0xf0, 0x03, 0xfc, 0x0f, 0xf0, 0x7f, 0xff, 0xfc, 0x0f, 44 | 0xff, 0xc0, 0xe0, 0xe0, 0xf8, 0x1f, 0xff, 0xc1, 0xff, 0xff, 0xff, 0x83, 0x03, 0xfc, 0x00, 0x7f, 45 | 0xe0, 0xff, 0xff, 0xf0, 0x07, 0xfe, 0x0f, 0xf0, 0x7f, 0xff, 0xfc, 0x0f, 0xff, 0x80, 0xc0, 0xe0, 46 | 0xf0, 0x3f, 0xff, 0x81, 0xff, 0xff, 0xff, 0x03, 0x01, 0xf8, 0x00, 0x3f, 0xc0, 0xff, 0xff, 0xf0, 47 | 0x07, 0xfe, 0x0f, 0xf0, 0x7f, 0xff, 0xf8, 0x0f, 0xfe, 0x01, 0xc0, 0xe0, 0xf0, 0x20, 0x3f, 0x82, 48 | 0x03, 0xff, 0xff, 0x03, 0x80, 0x60, 0x10, 0x3f, 0xc0, 0xff, 0xff, 0xf0, 0x07, 0xfe, 0x0f, 0xf0, 49 | 0x7f, 0xff, 0xf0, 0x1f, 0x80, 0x01, 0xc1, 0xe0, 0xf0, 0x00, 0x0f, 0x80, 0x00, 0xff, 0xff, 0x07, 50 | 0x80, 0x00, 0x10, 0x1f, 0x80, 0xff, 0xff, 0xf0, 0x07, 0xfe, 0x0f, 0xf0, 0x7f, 0xff, 0xc0, 0x1f, 51 | 0x80, 0x03, 0x81, 0xe0, 0xf0, 0x00, 0x07, 0x80, 0x00, 0x7f, 0xff, 0x07, 0xc0, 0x00, 0x38, 0x06, 52 | 0x00, 0xff, 0xff, 0xf0, 0x07, 0xfe, 0x0f, 0xf0, 0x7f, 0xff, 0x00, 0x3f, 0x80, 0x07, 0x81, 0xe0, 53 | 0xf0, 0x00, 0x03, 0x00, 0x00, 0x3f, 0xfe, 0x07, 0xe0, 0x00, 0x78, 0x00, 0x00, 0xff, 0x00, 0x00, 54 | 0x07, 0xfe, 0x0f, 0xf0, 0x7f, 0xfc, 0x00, 0x7f, 0x80, 0x03, 0x83, 0xe0, 0xf0, 0x00, 0x01, 0x00, 55 | 0x00, 0x1f, 0xfe, 0x07, 0xc0, 0x00, 0x3c, 0x00, 0x00, 0xff, 0x00, 0x00, 0x07, 0xfe, 0x0f, 0xf0, 56 | 0x7f, 0xf8, 0x00, 0xff, 0x80, 0x01, 0x03, 0xe0, 0xf0, 0x3e, 0x01, 0x00, 0x60, 0x1f, 0xfe, 0x0f, 57 | 0x80, 0x00, 0x1e, 0x00, 0x01, 0xff, 0x00, 0x00, 0x07, 0xfe, 0x0f, 0xf0, 0x7f, 0xf0, 0x03, 0xff, 58 | 0xfe, 0x01, 0x00, 0x00, 0x0f, 0xff, 0x80, 0x01, 0xf8, 0x0f, 0xfe, 0x0f, 0x80, 0x60, 0x1f, 0x00, 59 | 0x01, 0xff, 0x00, 0x00, 0x07, 0xfe, 0x0f, 0xf0, 0x7f, 0xe0, 0x0f, 0xff, 0xff, 0x80, 0x00, 0x00, 60 | 0x0f, 0xff, 0xc0, 0x03, 0xfc, 0x0f, 0xfc, 0x0f, 0x01, 0xf8, 0x0f, 0xc0, 0x41, 0xff, 0x00, 0x00, 61 | 0x07, 0xfe, 0x0f, 0xf0, 0x7f, 0xc0, 0x3f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xc0, 0x03, 62 | 0xfc, 0x0f, 0xfc, 0x0f, 0x03, 0xfc, 0x0f, 0xff, 0x81, 0xff, 0xff, 0xf0, 0x03, 0xfc, 0x0f, 0xf0, 63 | 0x7f, 0x80, 0x7f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xe0, 0x07, 0xfe, 0x0f, 0xfc, 0x1f, 64 | 0x03, 0xfc, 0x0f, 0xff, 0x83, 0xff, 0xff, 0xf0, 0x03, 0xfc, 0x0f, 0xf0, 0x7f, 0x80, 0xff, 0xff, 65 | 0xff, 0xe0, 0x00, 0x00, 0x0f, 0xff, 0xe0, 0x07, 0xfe, 0x0f, 0xfc, 0x1f, 0x07, 0xfe, 0x0f, 0xff, 66 | 0x03, 0xff, 0xff, 0xf0, 0x03, 0xfc, 0x0f, 0xf0, 0x7f, 0x01, 0xff, 0xfe, 0xff, 0xc0, 0xff, 0xe0, 67 | 0xfe, 0xff, 0xc0, 0x03, 0xfc, 0x0f, 0xf8, 0x1f, 0x03, 0xfc, 0x0f, 0xff, 0x03, 0xff, 0xff, 0xf0, 68 | 0x81, 0xf8, 0x1f, 0xf0, 0x7f, 0x01, 0xff, 0xfc, 0x7f, 0xc0, 0xff, 0xe0, 0xfc, 0x7f, 0xc0, 0x03, 69 | 0xfc, 0x0f, 0xf8, 0x1f, 0x03, 0xfc, 0x0f, 0xff, 0x07, 0xc3, 0xff, 0xf0, 0x81, 0xf8, 0x1f, 0xf0, 70 | 0x7f, 0x03, 0xff, 0xf8, 0x3f, 0x80, 0xff, 0xe0, 0xf8, 0x3f, 0x80, 0x01, 0xf8, 0x0f, 0xf8, 0x3f, 71 | 0x01, 0xf8, 0x0f, 0xfe, 0x07, 0x81, 0xff, 0xf0, 0x80, 0x60, 0x1f, 0xf0, 0x7f, 0x03, 0xff, 0xf0, 72 | 0x0e, 0x01, 0xff, 0xe0, 0xf0, 0x0e, 0x01, 0x80, 0x60, 0x1f, 0xf8, 0x3f, 0x80, 0x60, 0x1f, 0xfe, 73 | 0x07, 0x00, 0xff, 0xf0, 0xc0, 0x00, 0x3f, 0xf0, 0x7f, 0x00, 0x00, 0x10, 0x00, 0x01, 0xff, 0xe0, 74 | 0xf0, 0x00, 0x01, 0x80, 0x00, 0x1f, 0xf0, 0x3f, 0x80, 0x00, 0x1f, 0xfe, 0x0f, 0x00, 0xff, 0xf0, 75 | 0xc0, 0x00, 0x3f, 0xf0, 0x7f, 0x00, 0x00, 0x18, 0x00, 0x03, 0xff, 0xe0, 0xf8, 0x00, 0x03, 0xc0, 76 | 0x00, 0x3f, 0xf0, 0x3f, 0xc0, 0x00, 0x3f, 0xfc, 0x0f, 0x00, 0xff, 0xf0, 0xe0, 0x00, 0x7f, 0xf0, 77 | 0x7f, 0x00, 0x00, 0x1c, 0x00, 0x07, 0xff, 0xe0, 0xfc, 0x00, 0x07, 0xe0, 0x00, 0x7f, 0xf0, 0x7f, 78 | 0xe0, 0x00, 0x7f, 0xfc, 0x0f, 0x00, 0xff, 0xf0, 0xf0, 0x00, 0xff, 0xf0, 0x7f, 0x00, 0x00, 0x1e, 79 | 0x00, 0x0f, 0xff, 0xe0, 0xfe, 0x00, 0x0f, 0xf0, 0x00, 0xff, 0xe0, 0x7f, 0xf0, 0x00, 0xff, 0xfc, 80 | 0x1f, 0x81, 0xff, 0xf0, 0xfc, 0x03, 0xff, 0xf0, 0x7f, 0x00, 0x00, 0x1f, 0x80, 0x3f, 0xff, 0xe0, 81 | 0xff, 0x80, 0x3f, 0xfc, 0x03, 0xff, 0xe0, 0x7f, 0xfc, 0x03, 0xff, 0xf8, 0x1f, 0xc3, 0xff, 0xf0, 82 | }; 83 | 84 | Msb1stImage img_digit32((uint8_t*)img_digit32_data); 85 | 86 | const uint8_t img_degc_data[] = { 87 | 22, 0, 20, 0, 88 | 0xc3, 0xf0, 0x7c, 0x81, 0xc0, 0x1c, 0x00, 0x80, 0x0c, 0x18, 0x00, 0x04, 0x18, 0x07, 0x00, 0x00, 89 | 0x0f, 0x84, 0x80, 0x1f, 0xcc, 0xc2, 0x1f, 0xfc, 0xfe, 0x1f, 0xfc, 0xfe, 0x1f, 0xfc, 0xfe, 0x1f, 90 | 0xfc, 0xfe, 0x1f, 0xfc, 0xfe, 0x1f, 0xfc, 0xfe, 0x1f, 0xcc, 0xfe, 0x0f, 0x84, 0xff, 0x07, 0x00, 91 | 0xff, 0x00, 0x04, 0xff, 0x80, 0x0c, 0xff, 0xc0, 0x1c, 0xff, 0xf0, 0x7c, 92 | }; 93 | 94 | Msb1stImage img_degc((uint8_t*)img_degc_data); 95 | 96 | const uint8_t img_percent_data[] = { 97 | 21, 0, 20, 0, 98 | 0xc1, 0xf8, 0x38, 0x80, 0xf8, 0x78, 0x00, 0x70, 0x78, 0x1c, 0x70, 0xf8, 0x1c, 0x60, 0xf8, 0x1c, 99 | 0x61, 0xf8, 0x00, 0x41, 0xf8, 0x80, 0xc3, 0xf8, 0xc1, 0x83, 0xf8, 0xff, 0x07, 0xf8, 0xff, 0x07, 100 | 0xf8, 0xfe, 0x0c, 0x18, 0xfe, 0x18, 0x08, 0xfc, 0x10, 0x00, 0xfc, 0x31, 0xc0, 0xf8, 0x31, 0xc0, 101 | 0xf8, 0x71, 0xc0, 0xf0, 0x70, 0x00, 0xf0, 0xf8, 0x08, 0xe0, 0xfc, 0x18, 102 | }; 103 | 104 | Msb1stImage img_percent((uint8_t*)img_percent_data); 105 | 106 | const uint8_t img_hpa_data[] = { 107 | 43, 0, 20, 0, 108 | 0x0f, 0xfe, 0x00, 0x3f, 0xff, 0xe0, 0x0f, 0xfe, 0x00, 0x0f, 0xff, 0xe0, 0x0f, 0xfe, 0x00, 0x0f, 109 | 0xff, 0xe0, 0x0f, 0xfe, 0x1f, 0x07, 0xff, 0xe0, 0x0f, 0xfe, 0x1f, 0x87, 0xff, 0xe0, 0x0f, 0xfe, 110 | 0x1f, 0x87, 0xff, 0xe0, 0x08, 0x3e, 0x1f, 0x87, 0x00, 0xe0, 0x00, 0x0e, 0x1f, 0x86, 0x00, 0x20, 111 | 0x00, 0x0e, 0x1f, 0x86, 0x00, 0x20, 0x07, 0x06, 0x1f, 0x06, 0xfc, 0x00, 0x0f, 0x86, 0x00, 0x0f, 112 | 0xfe, 0x00, 0x0f, 0x86, 0x00, 0x0f, 0xc0, 0x00, 0x0f, 0x86, 0x00, 0x3f, 0x00, 0x00, 0x0f, 0x86, 113 | 0x1f, 0xfe, 0x00, 0x00, 0x0f, 0x86, 0x1f, 0xfc, 0x1e, 0x00, 0x0f, 0x86, 0x1f, 0xfc, 0x3e, 0x00, 114 | 0x0f, 0x86, 0x1f, 0xfc, 0x3c, 0x00, 0x0f, 0x86, 0x1f, 0xfc, 0x00, 0x00, 0x0f, 0x86, 0x1f, 0xfe, 115 | 0x00, 0x00, 0x0f, 0x86, 0x1f, 0xff, 0x01, 0x00, 116 | }; 117 | 118 | Msb1stImage img_hpa((uint8_t*)img_hpa_data); 119 | 120 | const uint8_t img_ppm_data[] = { 121 | 47, 0, 24, 0, 122 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 123 | 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 124 | 0xff, 0xff, 0xff, 0xfe, 0x08, 0x3e, 0x10, 0xf8, 0x47, 0x8e, 0x00, 0x0e, 0x00, 0x38, 0x01, 0x02, 125 | 0x00, 0x0e, 0x00, 0x38, 0x00, 0x02, 0x07, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x0f, 0x86, 0x1e, 0x18, 126 | 0x70, 0xe0, 0x0f, 0x86, 0x1e, 0x18, 0x70, 0xe0, 0x0f, 0x86, 0x1e, 0x18, 0x70, 0xe0, 0x0f, 0x86, 127 | 0x1e, 0x18, 0x70, 0xe0, 0x0f, 0x86, 0x1e, 0x18, 0x70, 0xe0, 0x0f, 0x86, 0x1e, 0x18, 0x70, 0xe0, 128 | 0x07, 0x06, 0x0c, 0x18, 0x70, 0xe0, 0x00, 0x0e, 0x00, 0x38, 0x70, 0xe0, 0x00, 0x0e, 0x00, 0x38, 129 | 0x70, 0xe0, 0x08, 0x3e, 0x10, 0xf8, 0x70, 0xe0, 0x0f, 0xfe, 0x1f, 0xff, 0xff, 0xfe, 0x0f, 0xfe, 130 | 0x1f, 0xff, 0xff, 0xfe, 0x0f, 0xfe, 0x1f, 0xff, 0xff, 0xfe, 0x0f, 0xfe, 0x1f, 0xff, 0xff, 0xfe, 131 | }; 132 | 133 | Msb1stImage img_ppm((uint8_t*)img_ppm_data); 134 | 135 | const uint8_t img_step_data[] = { 136 | 9, 0, 16, 0, 137 | 0x55, 0x00, 0x55, 0x00, 0xf7, 0x80, 0xe3, 0x80, 0xc1, 0x80, 0x80, 0x80, 0xe3, 0x80, 0xe3, 0x80, 138 | 0xe3, 0x80, 0xe3, 0x80, 0x80, 0x80, 0xc1, 0x80, 0xe3, 0x80, 0xf7, 0x80, 0x55, 0x00, 0x55, 0x00, 139 | }; 140 | 141 | Msb1stImage img_step((uint8_t*)img_step_data); 142 | --------------------------------------------------------------------------------