M5stack に変な?漢数字スクロール時計を表示させてみた

M5Stack

スケッチの入力

では、スケッチを入力していきます。

本当は for ループなどを使って一気に設定したかったのですが、他の人でも分かりやすくするためにソースコードが長くなっています。
それによって無駄が多いかも知れません。
因みに素人コードです。

※現在、WiFi.begin関数が使えない状態になっています。
WiFiにつながらない場合、こちらの記事の9番を参照して、108行目の_EWG.EWG_AP_Connect関数を WiFi.disconnect(true);と WifiMulti関数に替えてみてください。

 

配列要素数を超える書込みによると思われるエラーにより、再起動してしまうので、345行を修正しました。
(2018/06/04)

 

【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

#include "ESP32_LCD_ILI9341_SPI_V2.h" //今回作成した新しいライブラリ
#include "ESP32_Button_Switch.h"
#include "ESP32_SD_ShinonomeFNT.h" //beta ver 1.22-
#include "ESP32_SD_UTF8toSJIS.h" //beta ver 1.22-
#include "ESP32_WebGet.h" //beta ver 1.12-
#include "TimeLib.h" //Use Arduino time library ver1.5-

const int8_t sck = 18; // SPI clock pin
const int8_t miso = -1; // MISO(master input slave output) don't using
const int8_t mosi = 23; // MOSI(master output slave input) pin
const int8_t cs = 14; // Chip Select pin
const int8_t dc = 27; // Data/Command pin
const int8_t rst = 33; // Reset pin
const int8_t LCD_LEDpin = 32;

const uint8_t CS_SD = 4; //SD card CS ( Chip Select )

const char* ssid = "xxxxxxxx"; //ご自分のルーターのSSIDに書き換えてください
const char* password = "xxxxxxxx"; //ご自分のルーターのパスワードに書き換えてください

const char* UTF8SJIS_file = "/font/Utf8Sjis.tbl"; //UTF8 Shift_JIS 変換テーブルファイル名を記載しておく
const char* Shino_Zen_Font_file = "/font/shnmk16.bdf"; //オリジナル東雲全角フォントファイル
const char* Shino_Half_Font_file = "/font/shnm8x16.bdf"; //半角フォントファイル名を定義
//const char* Shino_Zen_Font_file = "/font/MYshnmk16.bdf"; //自作改変全角東雲フォントファイル

const uint8_t MAX_TXT_NUM = 6; //スクロール表示最大数

ESP32_LCD_ILI9341_SPI_V2 LCD; //今回作成した新しいライブラリクラス
ESP32_SD_ShinonomeFNT SFR(CS_SD, 40000000);
ESP32_Button_Switch BTN;
ESP32_WebGet _EWG;
FONT_PARAM font[ MAX_TXT_NUM ]; //フォントパラメーター入力用クラス
ScrolleSET_PARAM scl[ MAX_TXT_NUM ]; //スクロール設定入力用クラス

//------時刻文字表示系 引数初期化------------
uint8_t num = 0; //文字列番号
uint8_t time_sj_txt[ MAX_TXT_NUM ][ 20 ] = {}; //Shift_JIS文字コード格納
uint16_t time_sj_length[ MAX_TXT_NUM ] = {}; //文字列のShift_JISコード長
uint8_t time_font_buf[ MAX_TXT_NUM ][2][16] = {}; //16x16フォント全角1文字格納バッファ
uint16_t Fnt_Cnt[MAX_TXT_NUM] = {}; //半角フォント1文字スクロールカウント

//時刻数値を各桁に分割した変数定義
int8_t prev_hour1 = -1, prev_hour2 = -1;
int8_t now_hour1 = -1, now_hour2 = -1;
int8_t prev_min1 = -1, prev_min2 = -1;
int8_t now_min1 = -1, now_min2 = -1;
int8_t prev_sec1 = -1, prev_sec2 = -1;
int8_t now_sec1 = -1, now_sec2 = -1;
int32_t LastTime = 0;
bool startScrolle[ MAX_TXT_NUM ] = {};

//時刻コロンフォント表示用変数
FONT_PARAM colon1_fnt, colon2_fnt;
uint8_t colon1_buf[ 2 ][ 16 ] = {}, colon2_buf[ 2 ][ 16 ] = {};
uint16_t colon1_sj_len = 0, colon2_sj_len = 0;
uint32_t colon_LastTime = 0;
bool on_ColonDisp = false;

//-----NTPサーバー時刻取得 引数初期化------------
uint32_t NTPGetLastTime = 0;
bool NTP_first_get = true;

//-----ボタンスイッチ 引数初期化-----------
const uint8_t buttonA_GPIO = 39;
const uint8_t buttonB_GPIO = 38;
const uint8_t buttonC_GPIO = 37;
uint8_t btn_stateA = _Release;
uint8_t btn_stateB = _Release;
uint8_t btn_stateC = _Release;
//-----文字チェンジ用変数定義---------------
uint32_t change_font_LastTime = 0;
bool change_font = false;

//***********セットアップ****************************
void setup() {
  delay(1000);
  Serial.begin(115200);
  pinMode(buttonA_GPIO, INPUT); //GPIO #39 は内部プルアップ無し
  pinMode(buttonB_GPIO, INPUT); //GPIO #38 は内部プルアップ無し
  pinMode(buttonC_GPIO, INPUT); //GPIO #37 は内部プルアップ無し

  SFR.SD_Shinonome_Init3F(UTF8SJIS_file, Shino_Half_Font_file, Shino_Zen_Font_file); //ライブラリ初期化。3ファイル同時に開く

  LCD.ILI9341_Init(sck, miso, mosi, cs, dc, rst, LCD_LEDpin, 40000000, false); //microSDを使う場合、必ず false にする
  LCD.Display_Clear();
  LCD.Brightness(255); //LCD LED Full brightness

  num = 0; //時、十の位の文字列をフォント変換
  TimeStrFontConv( num, " 拾二三四五六七八九" );
  num = 1; //時、一の位
  TimeStrFontConv( num, "零壱弐参四伍六七八九" );
  num = 2; //分、十の位
  TimeStrFontConv( num, "零拾二三四五六七八九" );
  num = 3; //分、一の位
  TimeStrFontConv( num, "零壱弐参四伍六七八九" );
  num = 4; //秒、十の位
  TimeStrFontConv( num, "零拾二三四五六七八九" );
  num = 5; //秒、一の位
  TimeStrFontConv( num, "零壱弐参四伍六七八九" );
  //コロン文字フォント変換
  colon1_sj_len = SFR.StrDirect_ShinoFNT_readALL("時", colon1_buf);
  colon2_sj_len = SFR.StrDirect_ShinoFNT_readALL("分", colon2_buf);

  //メッセージウィンドウ表示
  Message(" WiFi Connecting...");

  //--------Wi-Fiアクセスポイント接続---------
  _EWG.EWG_AP_Connect(ssid, password); //Wi-Fi ルーターと接続
  delay(1000);
  _EWG.EWG_NTP_TimeLib_init(9, "time.windows.com"); //NTPサーバー取得初期化
  _EWG.NTP_OtherServerSelect(9); //NTPサーバーと接続できなかった場合、他のNTPサーバーと接続できるか試す関数

  Font_Setup(); //フォントセットアップ
}
//***********メインループ****************************
void loop() {
  NTP_Get(600000); //10分毎に時刻取得補正

  if( change_font == false && LastTime != now() ){
    on_ColonDisp = true; //コロン表示開始フラグ

    YMDW_disp(); //年月日、曜日表示

    //時刻文字をフォント変換
    now_hour1 = hour() / 10; //時、十の位
    if( now_hour1 != prev_hour1 ){
      num = 0;
      Fnt_Cnt[num] = now_hour1 * 2; //全角は8x16フォント2つ分なので、フォントカウントを2倍進める
      prev_hour1 = now_hour1;
      startScrolle[num] = true;
    }
    now_hour2 = hour() % 10; //時、一の位
    if( now_hour2 != prev_hour2 ){
      num = 1;
      Fnt_Cnt[num] = now_hour2 * 2;
      prev_hour2 = now_hour2;
      startScrolle[num] = true;
    }
    now_min1 = minute() / 10; //分、十の位
    if( now_min1 != prev_min1 ){
      num = 2;
      Fnt_Cnt[num] = now_min1 * 2;
      prev_min1 = now_min1;
      startScrolle[num] = true;
    }
    now_min2 = minute() % 10; //分、一の位
    if( now_min2 != prev_min2 ){
      num = 3;
      Fnt_Cnt[num] = now_min2 * 2;
      prev_min2 = now_min2;
      startScrolle[num] = true;
    }
    now_sec1 = second() / 10; //秒、十の位
    if( now_sec1 != prev_sec1 ){
      num = 4;
      Fnt_Cnt[num] = now_sec1 * 2;
      prev_sec1 = now_sec1;
      startScrolle[num] = true;
    }
    now_sec2 = second() % 10; //秒、一の位
    if( now_sec2 != prev_sec2 ){
      num = 5;
      Fnt_Cnt[num] = now_sec2 * 2;
      prev_sec2 = now_sec2;
      startScrolle[num] = true;
    }
    for( num = 0; num < MAX_TXT_NUM; num++ ){ //フォント変換
      scl[num].Zen_or_Han = SFR.Sjis_inc_FntRead(time_sj_txt[num], time_sj_length[num], &Fnt_Cnt[num], time_font_buf[num]);
    }
    LastTime = now();
  }else if( change_font == true ){
    if( now() - change_font_LastTime > 2 ){
      change_font = false;
      Message(" Change Font OK!");
    }
  }

  num = 0; //時、十の位文字スクロール
  if( startScrolle[num] ){
    if( LCD.Rev_Scrolle_Inc_8x16_Font( font[num], scl[num], time_sj_length[num], time_font_buf[num] ) ){
      startScrolle[num] = false; //一文字スクロール後、スクロールストップ
    }
  }
  num = 1; //時、一の位文字スクロール
  if( startScrolle[num] ){
    if( LCD.Y_Scrolle_Inc_8x16_Font( font[num], scl[num], time_sj_length[num], time_font_buf[num] ) ){
      startScrolle[num] = false;
    }
  }
  num = 2; //分、十の位文字スクロール
  if( startScrolle[num] ){
    if( LCD.Scrolle_Inc_8x16_Font( font[num], scl[num], time_sj_length[num], time_font_buf[num] ) ){
      startScrolle[num] = false;
    }
  }
  num = 3; //分、一の位文字スクロール
  if( startScrolle[num] ){
    if( LCD.Ydown_Scrolle_Inc_8x16_Font( font[num], scl[num], time_sj_length[num], time_font_buf[num] ) ){
      startScrolle[num] = false;
    }
  }
  num = 4; //秒、十の位文字スクロール
  if( startScrolle[num] ){
    if( LCD.Rev_Scrolle_Inc_8x16_Font( font[num], scl[num], time_sj_length[num], time_font_buf[num] ) ){
      startScrolle[num] = false;
    }
  }
  num = 5; //秒、一の位文字スクロール
  if( startScrolle[num] ){
    if( LCD.Y_Scrolle_Inc_8x16_Font( font[num], scl[num], time_sj_length[num], time_font_buf[num] ) ){
      startScrolle[num] = false;
    }
  }

  Colon_Disp(); //時刻コロン表示
  button_action(); //※ボタン操作でヒープ領域の配列を超えてしまう場合があるので、同じタスクに置くこと
}
//************ 時刻コロン表示 ********************************
void Colon_Disp(){
  if( on_ColonDisp ){
    LCD.XYsizeUp_8x16_Font_DisplayOut(colon1_fnt, colon1_sj_len, colon1_buf);
    LCD.XYsizeUp_8x16_Font_DisplayOut(colon2_fnt, colon2_sj_len, colon2_buf);
    on_ColonDisp = false;
    colon_LastTime = millis();
  }else if( millis() - colon_LastTime > 500 ){
    LCD.Display_Clear(colon1_fnt.x0, colon1_fnt.y0, colon1_fnt.x0 + colon1_fnt.Xsize * 16, colon1_fnt.y0 + colon1_fnt.Ysize * 16 );
    LCD.Display_Clear(colon2_fnt.x0, colon2_fnt.y0, colon2_fnt.x0 + colon2_fnt.Xsize * 16, colon2_fnt.y0 + colon2_fnt.Ysize * 16 );
    colon_LastTime = millis();
  }
}
//******** ニュース記事取得中、待機メッセージ表示*******
void Message(String str){
  uint8_t f_buf[ 20 ][ 16 ] = {};
  uint16_t len = SFR.StrDirect_ShinoFNT_readALL(str, f_buf);
  FONT_PARAM fnt_msg;
  delay(10);
  fnt_msg.Xsize = 2, fnt_msg.Ysize = 2;
  fnt_msg.red = 31, fnt_msg.green = 63, fnt_msg.blue = 31;
  fnt_msg.bg_red = 0, fnt_msg.bg_green = 0, fnt_msg.bg_blue = 0;
  fnt_msg.x0 = 1, fnt_msg.y0 = 4 * 48 + 1;
  LCD.Display_Clear(1, fnt_msg.y0 + 1, 318, 238);
  fnt_msg.y0 = fnt_msg.y0 + 10;
  LCD.XYsizeUp_8x16_Font_DisplayOut(fnt_msg, len, f_buf);
  LCD.Draw_Rectangle_Line(0, 4 * 48, 319, 239, 31, 63, 31);
}
//*******************************************************
void Font_Setup(){
  uint8_t X_max_txt = 2; //横スクロール時刻文字、各桁全角1文字表示(半角2文字分)
  uint8_t Y_max_txt = 1; //縦スクロール時刻文字、各桁全角1文字表示(半角2文字分)
  for( num = 0; num < MAX_TXT_NUM; num++ ){
    LCD.Array2_Delete( scl[num] ); //ヒープ領域メモリ、配列消去
  }
  for( num = 0; num < MAX_TXT_NUM; num++ ){
    font[num].Xsize = 3, font[num].Ysize = 6;
    font[num].y0 = 0; //各文字開始位置
  }

  num = 0; //時表示十の位
  scl[num].SclSpeed = 50;
  font[num].x0 = 0; //各文字開始位置
  font[num].red = 0, font[num].green = 0, font[num].blue = 0;
  font[num].bg_red = 0, font[num].bg_green = 50, font[num].bg_blue = 0;
  //水平方向スクロール文字フォント配列生成、および初期化
  LCD.X_Scrolle_Font_2Array_Init( font[num], scl[num], X_max_txt, font[num].Xsize, font[num].Ysize);

  num = 1; //時表示一の位
  scl[num].SclSpeed = 50;
  font[num].x0 = 48;
  font[num].red = 0, font[num].green = 0, font[num].blue = 0;
  font[num].bg_red = 29, font[num].bg_green = 58, font[num].bg_blue = 0;
  //垂直方向スクロール文字フォント配列生成、および初期化
  LCD.Y_Scrolle_Font_2Array_Init( font[num], scl[num], Y_max_txt, font[num].Xsize, font[num].Ysize );

  num = 2; //分表示十の位
  scl[num].SclSpeed = 50;
  font[num].x0 = 112;
  font[num].red = 31, font[num].green = 63, font[num].blue = 31;
  font[num].bg_red = 29, font[num].bg_green = 0, font[num].bg_blue = 0;
  //水平方向スクロール文字フォント配列生成、および初期化
  LCD.X_Scrolle_Font_2Array_Init( font[num], scl[num], X_max_txt, font[num].Xsize, font[num].Ysize );

  num = 3; //分表示一の位
  scl[num].SclSpeed = 50;
  font[num].x0 = 160;
  font[num].red = 0, font[num].green = 0, font[num].blue = 0;
  font[num].bg_red = 31, font[num].bg_green = 63, font[num].bg_blue = 31;
  //垂直方向スクロール文字フォント配列生成、および初期化
  LCD.Y_Scrolle_Font_2Array_Init( font[num], scl[num], Y_max_txt, font[num].Xsize, font[num].Ysize );  LCD.Scrolle_Font_SetUp( font[num], scl[num] );

  num = 4; //秒表示十の位
  scl[num].SclSpeed = 30;
  font[num].x0 = 224;
  font[num].red = 31, font[num].green = 63, font[num].blue = 31;
  font[num].bg_red = 20, font[num].bg_green = 0, font[num].bg_blue = 20;
  //水平方向スクロール文字フォント配列生成、および初期化
  LCD.X_Scrolle_Font_2Array_Init( font[num], scl[num], X_max_txt, font[num].Xsize, font[num].Ysize );

  num = 5; //秒表示一の位
  scl[num].SclSpeed = 24;
  font[num].x0 = 272;
  font[num].red = 31, font[num].green = 63, font[num].blue = 31;
  font[num].bg_red = 0, font[num].bg_green = 31, font[num].bg_blue = 0;
  //垂直方向スクロール文字フォント配列生成、および初期化
  LCD.Y_Scrolle_Font_2Array_Init( font[num], scl[num], Y_max_txt, font[num].Xsize, font[num].Ysize );

  for( num = 0; num < MAX_TXT_NUM; num++ ){
    LCD.Scrolle_Font_SetUp( font[num], scl[num] );
  }

  //コロンフォント初期化
  colon1_fnt.x0 = 96, colon1_fnt.y0 = 32;
  colon1_fnt.Xsize = 1, colon1_fnt.Ysize = 4;
  colon1_fnt.red = 31, colon1_fnt.green = 63, colon1_fnt.blue = 31;
  colon1_fnt.bg_red = 0, colon1_fnt.bg_green = 0, colon1_fnt.bg_blue = 0;

  colon2_fnt.x0 = 208, colon2_fnt.y0 = 32;
  colon2_fnt.Xsize = 1, colon2_fnt.Ysize = 4;
  colon2_fnt.red = 31, colon2_fnt.green = 63, colon2_fnt.blue = 31;
  colon2_fnt.bg_red = 0, colon2_fnt.bg_green = 0, colon2_fnt.bg_blue = 0;

  Serial.printf("Free Heap Size = %d\r\n", esp_get_free_heap_size());
}
//*************************************
void NTP_Get(uint32_t get_interval){
  if( (NTP_first_get == true) || ((millis() - NTPGetLastTime) > get_interval) ){
    setTime(EWG_Get_Ntp_Time());
    Message(" NTP Getting Wait");
    NTP_first_get = false;
    Message( " NTP Get OK! " + String( hour() ) + ":" + String( minute() ) );
    NTPGetLastTime = millis();
  }
}
//********** 時刻文字列フォント変換 *****************
void TimeStrFontConv(uint8_t num, String str){
  Fnt_Cnt[num] = 0;
  time_sj_length[num] = SFR.UTF8toSJIS_convert( str, time_sj_txt[num]);
  scl[num].Zen_or_Han = SFR.Sjis_inc_FntRead(time_sj_txt[num], time_sj_length[num], &Fnt_Cnt[num], time_font_buf[num]);
}
//********** 年月日曜日表示 *************************
void YMDW_disp(){
  //Timeライブラリ weekday()関数は1~7の範囲数値を返す
  char week_c[7][4] = { "日", "月", "火", "水", "木", "金", "土" };
  String ymd_str = "";
  char month_chr[ 3 ] = {}, day_chr[ 3 ] = {};
  uint8_t ymd_buf[40][16] = {};
  FONT_PARAM ymd_fnt;

  sprintf( month_chr, "%2d", month() );
  sprintf( day_chr, "%02d", day() );
  ymd_str = String( year()) + "年";
  ymd_str += String( month_chr) + "月";
  ymd_str += String( day_chr) + '(';
  ymd_str += String( week_c[ weekday() - 1 ] ) + "曜日)";

  uint16_t ymd_len = SFR.StrDirect_ShinoFNT_readALL( ymd_str, ymd_buf );
  ymd_fnt.x0 = 0;
  ymd_fnt.y0 = 106;
  ymd_fnt.Xsize = 2, ymd_fnt.Ysize = 2;
  ymd_fnt.red = 31, ymd_fnt.green = 63, ymd_fnt.blue = 31;
  ymd_fnt.bg_red = 0, ymd_fnt.bg_green = 0, ymd_fnt.bg_blue = 0;
  LCD.XYsizeUp_8x16_Font_DisplayOut(ymd_fnt, ymd_len, ymd_buf);
}
//****************************************
void Time_Disp_Reset(){
  Message(" Change Font Wait...");
  prev_hour1 = -1, prev_hour2 = -1;
  now_hour1 = -1, now_hour2 = -1;
  prev_min1 = -1, prev_min2 = -1;
  now_min1 = -1, now_min2 = -1;
  prev_sec1 = -1, prev_sec2 = -1;
  now_sec1 = -1, now_sec2 = -1;
  change_font = true;
  LastTime = 0;
  change_font_LastTime = now();
}
//****************************************
void button_action(){
  btn_stateA = BTN.Button(0, buttonA_GPIO, true, 10, 300);
  switch( btn_stateA ){
    case _MomentPress:
      Serial.println("Button A Moment Press");
      for( num = 0; num < MAX_TXT_NUM; num++ ){
        TimeStrFontConv( num, "0123456789" );
      }
      colon1_sj_len = SFR.StrDirect_ShinoFNT_readALL(":", colon1_buf);
      colon2_sj_len = SFR.StrDirect_ShinoFNT_readALL(":", colon2_buf);
      Time_Disp_Reset();
      break;
    case _ContPress:
      Serial.println("-------------Button A Cont Press");
      for( num = 0; num < MAX_TXT_NUM; num++ ){
        TimeStrFontConv( num, "れひふみよいむなやこ" );
      }
      colon1_sj_len = SFR.StrDirect_ShinoFNT_readALL("じ", colon1_buf);
      colon2_sj_len = SFR.StrDirect_ShinoFNT_readALL("ふ", colon2_buf);
      Time_Disp_Reset();
      break;
    default:
      break;
  }

  btn_stateB = BTN.Button(1, buttonB_GPIO, true, 10, 300);
  switch( btn_stateB ){
    case _MomentPress:
      Serial.println("Button B Moment Press");
      for( num = 0; num < MAX_TXT_NUM; num++ ){
        TimeStrFontConv( num, "〇一二三四五六七八九" );
      }
      colon1_sj_len = SFR.StrDirect_ShinoFNT_readALL("時", colon1_buf);
      colon2_sj_len = SFR.StrDirect_ShinoFNT_readALL("分", colon2_buf);
      Time_Disp_Reset();
      break;
    case _ContPress:
      for( num = 0; num < MAX_TXT_NUM; num++ ){
        TimeStrFontConv( num, "零壱弐参四伍六七八九" );
      }
      colon1_sj_len = SFR.StrDirect_ShinoFNT_readALL("時", colon1_buf);
      colon2_sj_len = SFR.StrDirect_ShinoFNT_readALL("分", colon2_buf);
      Time_Disp_Reset();
      break;
    default:
      break;
  }

  btn_stateC = BTN.Button(2, buttonC_GPIO, true, 10, 300);
  switch( btn_stateC ){
    case _MomentPress:
      Serial.println("Button C Moment Press");
      for( num = 0; num < 4; num++ ){
        font[num].red = 0, font[num].green = 63, font[num].blue = 31;
        font[num].bg_red = 0, font[num].bg_green = 0, font[num].bg_blue = 0;
        LCD.Scrolle_Font_Color_Set( font[num] );
      }
      for( num = 4; num < 6; num++ ){
        font[num].red = 31, font[num].green = 10, font[num].blue = 25;
        font[num].bg_red = 0, font[num].bg_green = 0, font[num].bg_blue = 0;
        LCD.Scrolle_Font_Color_Set( font[num] );
      }
      Time_Disp_Reset();
      break;
    case _ContPress:
      Serial.println("-------------Button C Cont Press");
      Font_Setup();
      Time_Disp_Reset();
      break;
    default:
      break;
  }
}

解説しなくても、ソースコードを見ればわかるようにできるだけ工夫してみました。

今回のポイントは、91行目にあるように、カウンター数字を0~9までの文字列として代入するようにしたことです。
これを自分の好きな文字に変えることによって、オリジナルの「変な時計」を作ることができます。

これはオリジナルの東雲フォントを使う設定ですので、カスタムフォントを使う場合は22行目をコメントアウトして、24行目のコメントを解除してカスタムフォントファイル名に書き換えてください。

これでもプログラムがえらい長くて、引数が多過ぎると思うかもしれません。
でも、私の自作した従来のライブラリ関数よりも劇的に仮引数を減らすことができました。

引数専用のクラスを作ったことで、順不同で関数の外で代入出来て、とても便利になりました。
377行以降のボタン操作関数でも、好きなところにフォント設定が出来るようになりました。

それに、クラスのオブジェクト配列というものを利用することにより、6種類のスクロール文字列に別々にデータ保持することができるようになりました。
この方法は最近知ったのですが、とっても便利です。
そう言えば、String クラスも同じようなことができますね。

また、232行目のメッセージウィンドウ用のフォント設定は、クラスオブジェクトを別途関数内で定義することにより、その関数を抜けるとそのメモリを解放できて、超便利です。
今までライブラリを沢山作って来ましたが、こういう使い方ができるとは目から鱗です。

いやぁ~、それにしても、C++ のクラスっていうものはホントに便利です。
先人の技術者達がもっと使い易くしようと、C言語を改良した努力の功績が満載です。
Arduino IDE でこれを使えることには意味がありますね。
手軽にテストできて、アマチュアにとっては有り難いことです。

そして、264行目の関数では、ヒープ(heap)領域に配列を確保するようにしました。
文字を拡大したりすると、配列が大きくなります。
任意に拡大縮小する度に配列サイズを変えられるようにできます。
ただ、先に作ったヒープ(heap)メモリを251行の関数で削除しないと、延々とヒープ(heap)メモリ食いつぶすので注意が必要です。

以前の私の自作ライブラリでは、スケッチの初めに使用する最大の配列を定義してメモリを確保していました。
それではやはり効率が悪いし、これに Web と SSL 通信しようものならば、たちまちメモリオーバーしてしまいます。
関数内で、自由に配列を定義したり削除したりできるのがヒープ(heap)メモリの利点ですね。
Arduino 系では SRAM メモリが少ないので、今までヒープ(heap)メモリを使うことは避けていました。
でも、ESP32 を使うようになり、SRAMに余裕が出てきたので、そろそろ使ってみようと思った次第です。

その他、ボタンライブラリの関数の使い方は以下の記事を参照してください。

M5Stack ボタンスイッチの、チャタリング、誤作動防止、感度調整、長押し設定を考えてみる

コンパイル書き込み実行

では、Arduino IDE でコンパイル書き込み実行してみてください。
先に紹介した動画のように表示されればOKです。

ボタンを押したり、長押ししたりしてみて下さい。
文字列が変わったり、フォント色が変わったりすると思います。
変更が確定されるためには2秒程かかります。
これは、スクロールの途中で変更ボタンが押された場合、スクロール開始位置を次の秒開始に合わせる為です。

スケッチ上の文字列を自分の好きなように変えたり、自分の好きなカスタムフォントに変えたりして、オリジナルの「変な時計」ができます。

編集後記

今回は、C++ を新たに勉強し始め、ライブラリをいろいろと修正することに時間がかかってしまいました。

「こんな便利な使い方があったんだ・・・」

という発見が沢山ありました。
やっぱり、独学には限界があって、新たな発見に至るまでは長い年月がかかりますね。
先生がいれば、もっと早く習得できたかもしれません。

でも、今回はプログラムをもっと作り易くするためのノウハウを体感できました。
これは大きいですね。
今までの自作プログラムは無駄が多いことが分かって来ました。
やはり先人のプログラマや技術者が作ってきたものを無視しないで、ちゃんと習得することは大切だなぁと思いました。

まだまだ、クラスの継承やクラス設計テンプレートなどは理解できておらず、勉強が足りません。
少しずつ習得していこうと思います。

ということで、今回はここまでです。

ではまた・・・。

コメント

  1. masayan より:

    いつも良いコードをありがとう御座います!!
    esp8266などで漢字の入力はできないでしょうか?
    シリアルモニタか入力装置から名前と誕生日を入力させて、その1週間前からLEDに通知したりリストを表示するのをコンパクトな関数で実現したいです。
    個人情報なのでじぶんで削除したりというような。

    • mgo-tec mgo-tec より:

      masayanさん

      記事をご覧いただき、ありがとうございます。

      随分前に書いた記事で、そんなの作ったっけ?、、、とすっかり忘れていました。
      今見ると、なかなかオモシロイ物をつくったな、、、と我ながら思いました。

      ところで、esp8266 については現在まったく使っておりません。
      なぜかというと、発売当初よりもフラッシュサイズが減らされてしまった為、大きなプログラミングができなくなったためです。
      その分、ESP32はメモリも大きく、漢字フォントも余裕で扱えます。
      私はESP32をお勧めします。

      また、シリアルモニタから漢字入力はやったことありません。
      思い付いたのは、入力はスマホかFirebaseなどが良いような気がします。
      Firebaseならデータベースできますし。
      以下の記事参照↓
      Firebase Realtime Database のデータ保存、取得、ストリーミング受信実験( ESP32 , M5Stack )
      Firebase Realtime Database をスマホで操作およびストリーミング受信する実験
      Firebase Realtime database を使った ESP32, M5Stack, スマートフォン相互通信してみる

      あとは、ESP32でNTPで時刻を取得して、カウントダウンするプログラムを組むとかでしょうか。
      今考えられるのはそんなところです。
      コンパクトな関数とはいきませんが、、、、

      因みに、Firebaseの記事は随分前に書いたものですから、今現在ちゃんと動くかどうかは分かりません。
      ご了承くださいませ。
      m(_ _)m

  2. masayan より:

    アドバイス有難う御座います!
    そういえばGoogleNotifyのでスマホからの文章(漢字)入力をそのまま発声させるのが有りました。あれをFireBaseで処理すれば良いですか。ダメなりに頑張ってみます

    esp8266はAliだとLolinて裏に書いてるやつが2GB掴まされましたが、それ以外は全て4GBでしたよ。安さは魅力です。ndemcuでも失敗して必ずしも最高速で書き込めないけど(汗
    あとGH関係は2.4.2でしか動かないのは何ででしょう?

    品質と低価格は永久ではないですね。
    でも悪くないな。
    ホットなやる気ある時にガンガン行きたいと思ってます。

    • mgo-tec mgo-tec より:

      masayanさん

      esp8266 のフラッシュ4MB版の方は、ESP32-WROOM-32と同じなのですが、SRAMが大幅に異なります。
      LCDに日本語漢字表示させたりする場合はESP32でないと厳しいと思います。
      ディスプレイに表示することがなければ、esp8266でも良いかも知れません。
      頑張ってみて下さい!
      (^^)

タイトルとURLをコピーしました