HOME | Raspberry Pi | ビジネス書籍紹介 | 2026-01-04 (Sun) Today's Access : 324 Total : 1261022. Since 10 Sep. 2019

疑似コンソール (C言語編)
2023.12.01

YouTube でも紹介しています。画像をクリックすると再生できます。

以前、pythonを用いてリングバッファ擬きの疑似コンソールを作成しました。

疑似コンソール

今回は、C言語によりリングバッファを実装し、TFT LCDを疑似コンソール化してみます。



●リングバッファ


File:Ring.buffer.svg - Wikimedia Commons
リングバッファは、予め記憶領域を確保して、読み出し位置と書き込み位置をずらしながら処理を行い、領域の終端に到達したら、領域の先頭に繋げ無限ループ的に利用する仕組みです。

ここでは、疑似コンソール・クラス Console を作成して、inoファイルで使用する例を用いて説明します。

#include "Console.h"

Console console;

void setup()
{
	console.print("ABC",    TFT_GREEN);
	console.print("DEFGHI", TFT_YELLOW);
	console.print("JKL"   , TFT_GREEN);
	console.print("MNOP",   TFT_YELLOW);
}

void loop() {}



リングバッファのサイズは下記の通りです、
(色情報(COLOR565)2バイト+桁数+終端コード1バイト)×行数

2行5桁のLCDの場合、必要となるリングバッファのサイズは
(2+5+1)×2=16バイト

初回のconsole.print()関数呼び出しでは配列の先頭から、色情報2バイト、文字列、終端(NULLコード1バイトと埋めていきます。
LCD表示用データの範囲は配列の先頭から終端コードまでになります。

2回目の関数呼び出しでは、終端コードの次の位置からデータを埋めていきます。 ここで指定した文字列を表示するためには2行必要になるため、LCD表示用データの範囲もこれに合わせてシフトさせます。

3回目の関数呼び出しで単に終端コードに続けて埋めていってしまうとオーバーフローしてしまいます。 リングバッファとはいっても、円形に配列が繋がっているわけではありません。 配列の終端までデータが埋まった時点で、配列の先頭から続きのデータで上書きしていきます。
LCD表示データの開始位置は2回目に呼び出した際の文字列の途中からになるので、表示文字列の直前に色情報も付加します。

4回目の関数呼び出しも同様に処理していきます。

このような流れになるようにプログラム要件をまとめ、疑似コンソール・クラスにコードを記述しています。

ここにソースコードも記載しておきますが、まずはご自身で処理の流れを考えてコーディングしてみてください。 意外と面倒なことに気付くと思います。
下記のコードは、横240x縦320のディスプレイに表示フォントサイズ横12x縦16pxのフォントを使用した場合です。 実行環境に応じて、変更してください。

疑似コンソール・クラス Console
#include "Console.h"

Console::Console() {

	tft.init(240, 320);
	tft.setRotation(2);
	tft.setTextSize(2);   // 12px
	tft.setTextWrap(false);
	tft.fillScreen(ST77XX_BLACK);

	width  = tft.width();
	height = tft.height();
	cols   = width  / FONT_WIDTH;
	rows   = height / FONT_HEIGHT;
	lines  = 0;

	stack = (uint8_t*)malloc(STACK_SIZE);
	if (!stack) {
		tft.setCursor(0, 0);
		tft.setTextColor(TFT_RED);
		tft.println("memory allocate failed!");
		while(1);
	} else {
		from = stack;
		to   = stack;
		memset(stack,'\0',sizeof(char)*(cols+1)*rows);
	}
}

Console::~Console() {}

void Console::backlight(uint8_t pin, uint8_t val)
{
	pinMode(pin,OUTPUT);
	analogWrite(pin, 64); // 0 - 255
}

void Console::print(char *text, uint16_t color)
{
	int       skip, cnt, line;
	uint16_t  c;
	uint8_t   *pos;
	uint8_t   cH, cL;
	char      prn_buff[512];
	boolean   nullflag;

	if (*text == 0x0) return;

	// stack text to the ring buffer
	*to = (uint8_t)(color>>8);
	if ((to+1)<(stack+STACK_SIZE)) to++; else to = stack;
	*to = (uint8_t)(color&0xff);

	skip = 0;
	cnt  = 0;
	while (1) {
		if ((to+1)<(stack+STACK_SIZE)) to++; else to = stack;
		*to = *text;
		if (++cnt == cols) {
			skip++;
			cnt = 0;
		}
		if (*text == 0x0) {
			if (cnt > 1) skip++;
			break;
		}
		text++;
	}
	if ((to+1)<(stack+STACK_SIZE)) to++; else to = stack;

	lines += skip;
	if (lines > rows) {
		skip = lines - rows;
		lines = rows;
	} else {
		skip = 0;
	}

	// shift the display start position
	while (skip > 0) {
		cH = *from;
		if ((from+1)<(stack+STACK_SIZE)) from++; else from=stack;
		cL = *from;
		if ((from+1)<(stack+STACK_SIZE)) from++; else from=stack;
		nullflag = false;
		while (skip > 0) {
			if (nullflag) break;
			cnt = 1;
			while (1) {
				if (*from == 0x0) {
					if (cnt > 1) --skip;
					if ((from+1)<(stack+STACK_SIZE)) from++; else from=stack;
					nullflag = true;
					break;
				} else {
					if (cnt == cols) {
						if ((--skip) == 0) {
							*from = cL;
							if (from == stack) from = stack + STACK_SIZE - 1; else --from;
							*from = cH;
							break;
						}
						cnt = 0;
					}
				}
				if ((from+1)<(stack+STACK_SIZE)) from++; else from=stack;
				cnt++;
			}
		}
	}

	tft.fillScreen(ST77XX_BLACK);
	line = 0;
	pos  = from;
	while(1) {
		c = *pos;
		if ((pos+1)<(stack+STACK_SIZE)) pos++; else pos=stack;
		c = (c<<8)&0xff00 + *pos;
		if ((pos+1)<(stack+STACK_SIZE)) pos++; else pos=stack;
		tft.setTextColor(c);

		cnt = 1;
		while (1) {
			prn_buff[cnt-1] = *pos;
			if (*pos == 0x0) {
				if (cnt > 1) {
					tft.setCursor(0, line*FONT_HEIGHT);
					tft.print(prn_buff);
					line++;
				}
				if ((pos+1)<(stack+STACK_SIZE)) pos++; else pos=stack;
				break;
			}
			if (!(cnt%cols)) {
				prn_buff[cnt] = 0x0;
				tft.setCursor(0, line*FONT_HEIGHT);
				tft.print(prn_buff);
				line++;
				cnt = 0;
			}
			if ((pos+1)<(stack+STACK_SIZE)) pos++; else pos=stack;
			cnt++;
		}
		if (pos == to) break;
	}
}

疑似コンソール・ヘッダーファイル
#ifndef __CONSOLE_H
#define __CONSOLE_H

#include <Adafruit_GFX.h>    // Core graphics library
//#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>
#include "stdint.h"

#define TFT_CS         A10
#define TFT_RST        A11
#define TFT_DC         A12
#define TFT_LIT        A13

#define TFT_BLACK       0x0000      /*   0,   0,   0 */
#define TFT_NAVY        0x000F      /*   0,   0, 128 */
#define TFT_DARKGREEN   0x03E0      /*   0, 128,   0 */
#define TFT_DARKCYAN    0x03EF      /*   0, 128, 128 */
#define TFT_MAROON      0x7800      /* 128,   0,   0 */
#define TFT_PURPLE      0x780F      /* 128,   0, 128 */
#define TFT_OLIVE       0x7BE0      /* 128, 128,   0 */
#define TFT_LIGHTGREY   0xC618      /* 192, 192, 192 */
#define TFT_DARKGREY    0x7BEF      /* 128, 128, 128 */
#define TFT_BLUE        0x001F      /*   0,   0, 255 */
#define TFT_GREEN       0x07E0      /*   0, 255,   0 */
#define TFT_CYAN        0x07FF      /*   0, 255, 255 */
#define TFT_RED         0xF800      /* 255,   0,   0 */
#define TFT_MAGENTA     0xF81F      /* 255,   0, 255 */
#define TFT_WHITE       0xFFFF      /* 255, 255, 255 */
#define TFT_ORANGE      0xFDA0      /* 255, 180,   0 */
#define TFT_YELLOW      0xB7E0      /* 180, 255,   0 */
#define TFT_PINK        0xFC9F
#define TFT_AQUAMARINE  0x6675      /* 102,205,170 */
#define TFT_WHEAT       0xF756      /* 245,222,179 */

#define STACK_SIZE 512
#define FONT_WIDTH  12
#define FONT_HEIGHT 16

class Console
{
	private:
		uint8_t  cols, rows, lines;
		uint16_t width, height;
		uint8_t  *stack;
		uint8_t  *from, *to;
		char     prn_buff[256];

	public:
		Console();
		~Console();
		Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
    void print(char *text, uint16_t color);
    void backlight(uint8_t pin, uint8_t val);
};

#endif /*__CONSOLE_H*/

それでは、実際に動かしてみます。

●NodeMCU-32S / HiLetgo ESP-WROOM-32 ESP-32S WiFi + Bluetooth Dev Board
今回は激安で手に入れたNodeMCU-32Sを使いました。 使用するLCDの表示サイズに合わせて、メモリを確保できるマイコンボードを用意してください。

ESP32S-HiLetgo Dev Boad with Pinout Template

●2.0" 320x240 Color IPS TFT Display with microSD Card Breakoutd
表示に使用するフォントに横12ドット、縦16ドットを使用したので、このTFTを縦置きで使用した場合に、このTFTでは横20文字、縦20行になります。

ADA4311

 ST7789  -  NodeMCU-32S  -  Raspberry Pi 
  VIN   -  #1 3.3V        
  GND   -  #38 GND        
  SCK   -  #30 SCK        
  MISO  -  #31 MISO        
  MOSI  -  #37 MOSI        
  CS  -  #26 (GPIO4)        
  RESET  -  #25 (GPIO0)        
  D/C  -  #24 (GPIO2)        
 BACKLIGHT  -  #23 (GPIO15)       
       USB   -  USB 



ソースコードのビルドには、PlatformIOを使用しています。
Arduino開発環境構築 PlatformIO

$ pio boards NodeMCU-32S
Platform: espressif32
==================================================================
ID           MCU     Frequency Flash RAM   Name
------------ ------- --------- ----- ----- -----------------------
nodemcu-32s2 ESP32S2 240MHz    4MB   320KB Ai-Thinker NodeMCU-32S2
nodemcu-32s  ESP32   240MHz    4MB   320KB NodeMCU-32S
プロジェクトの作成
$ mkdir -p ~/NodeMCU-32S/console
$ cd ~/NodeMCU-32S/console
$ pio init -b nodemcu-32s
$ pio run -t upload

●活用例

今回の疑似コンソールを作成するにあたり、テキストに色情報を付加した理由は、 2つのシリアルポートを使って、双方向通信の情報を識別して監視するのが目的でした。

●応用
リングバッファのサイズを大きくすると、表示履歴を遡るようにすることも可能です。

Raspberry Pi(ラズベリー パイ)は、ARMプロセッサを搭載したシングルボードコンピュータ。イギリスのラズベリーパイ財団によって開発されている。
2019.12.13 モバイルバッテリーによる瞬間停電対策
2020.01.01 1280x800 HDMI MONITOR
2020.01.12 micro:bitをコマンドラインで使う
2020.02.04 サーマルプリンタを使う
2020.04.10 電卓を制御して数字を表示する
2020.08.03 Seeeduino XIAO
2020.08.09 LGT8F328P - Arduino clone
2020.09.18 電流計測モジュール INA219
2021.02.16 癒しの電子回路
2021.03.06 疑似コンソール
2021.08.08 電子ペーパー
2021.09.04 AVRマイコン・ATTiny85
2021.09.25 pH測定
2021.11.13 NTP時刻取得と活用
2021.11.27 GPS情報取得
2021.12.11 GR-KURUMI
2021.12.25 ATMEGA328P 3.3V/8MHz
2022.01.11 AS-289R2 プリンタシールド
2022.01.25 TM1637 & ATtiny85
2022.02.22 Raspberry Pi Zero 小道具
2022.03.01 ATTinyCore
2022.03.18 Adafruit QT Py + XIAO Expansion board
2022.07.31 サーマルプリンター番外編:通信筒
2023.01.01 FTP Server & SPI Flash SD
2023.02.01 LPC810(ARM Cortex-M0+)
2023.02.15 IchigoJam互換機
2023.03.01 Telnet
2023.04.26 USBメモリをUART接続で利用する
2023.05.14 焦電型赤外線モーションセンサー
2023.07.01 文字化けしないキーボード
2023.08.01 Bluetoothサーマルプリンター
2023.08.12 LattePanda 2G/32GB
2023.09.04 SI-3012KS
2023.12.01 疑似コンソール(C言語編)
2023.12.16 昭和レトロ・温度湿度時刻計
2023.12.25 二酸化炭素濃度監視
2024.01.23 なんちゃってmicro:bit
2024.02.07 オリジナル micro:bit
2024.02.23 ESP32 OTA
2024.03.08 TELNETサーマルプリンター
2024.05.08 ESP32 PROGRAM SELECTOR
2024.05.23 統合開発環境とQwiic
2025.01.24 赤外線リモコン
2025.03.25 QRCode SCANNER
2025.04.08 Keyestudio 328 WiFi Plus
2025.08.23 NanoPi NEO3
2025.09.24 I2C接続microSDモジュール
2025.10.08 UNO 3.3V@8MHz

たいていのことは100日あれば、うまくいく。長田英知著
「時間がなくて、なかなか自分のやりたいことができない」 「一念発起して何かを始めても、いつも三日坊主で終わってしまう」 「色んなことを先延ばしにしたまま、時間だけが過ぎていく」 そこで本書では、そんな著者が独自に開発した、 まったく新しい目標達成メソッド「100日デザイン」について、 その知識と技術を、余すところなくご紹介します。

まんがで納得ナポレオン・ヒル 思考は現実化する
OLとして雑務をこなす日々に飽き足らず、科学者だった父が残した薬品を商品化すべく、起業を決意した内山麻由(27)。彼女はセミナーで知り合った謎の女性からサポートを得ながら、彼女と二人三脚でナポレオン・ヒルの成功哲学を実践し、さまざまな問題を乗り越えていく。 ヒル博士の<ゴールデンルール>に従い、仕事に、恋に全力疾走する彼女の、成功への物語。

今日は人生最悪で最高の日 1秒で世界を変えるたったひとつの方法 ひすいこたろう著
偉人の伝記を読むと、最悪な日は、不幸な日ではなく、新しい自分が始まる日であることがわかります。最悪な出来事は、自分の人生が、想像を超えて面白くなる兆しなのです。偉人伝を読むことで、このときの不幸があったおかげで、未来にこういう幸せがくるのかと、人生を俯瞰する視線が立ち上がるのです。

ご飯は私を裏切らない heisoku著
辛い現実から目を背けて食べるご飯は、いつも美味しく幸せを届けてくれる。 29歳、中卒、恋人いない歴イコール年齢。バイト以外の職歴もなく、短期バイトを転々とする日々。ぐるぐると思索に耽るけど、ご飯を食べると幸せになれる。奇才の新鋭・heisokuが贈るリアル労働グルメ物語!

【最新版Gemini 3に対応!】できるGemini (できるシリーズ)
Geminiを「最強の知的生産パートナー」として使いこなすための、実践的なノウハウを凝縮した一冊です。 基本的な操作方法から、具体的なビジネスシーンでの活用、日々の業務を自動化するGoogle Workspaceとの連携、さらには自分だけのオリジナルAIを作成する方法まで余すところなく解説します。

Rustプログラミング完全ガイド 他言語との比較で違いが分かる!
Rustの各手法や考え方を幅広く解説! 500以上のサンプルを掲載。実行結果も確認。 全24章の包括的なチュートリアル。

ポチらせる文章術
販売サイト・ネット広告・メルマガ・ブログ・ホームページ・SNS… 全WEB媒体で効果バツグン! カリスマコピーライターが教える「見てもらう」「買ってもらう」「共感してもらう」すべてに効くネット文章術

小型で便利な Type-C アダプター USB C オス - USB3.1 オスアダプター
Type-C端子のマイコンボードをこのアダプタを介して直接Raspberry Piに挿すことができます。ケーブルなしで便利なツールです。

Divoom Ditoo Pro ワイヤレススピーカー
15W高音質重低音/青軸キーボード/Bluetooth5.3/ピクセルアート 専用アプリ/USB接続/microSDカード

電源供給USBケーブル スリム 【5本セット】
USB電源ケーブル 5V DC電源供給ケーブル スリム 【5本セット】 電源供給 バッテリー 修理 自作 DIY 電子工作 (100cm)

Copyright © 2011-2027 Sarako Tsukiyono All rights reserved®.