0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

M5Stack Tab5 の初期ファームウェアをカスタマイズする

Last updated at Posted at 2026-01-12

1. はじめに

初期ファームウェア とは、M5Stack Tab5 に出荷時にインストールされている「M5Stack User Demo」のことです。

今年になり、RTCの日付設定カレンダーに 2026 が無いことに気付きました。RTCは 2026年と正しいのですが、カレンダーの年ドロップダウンリストに 2026年が無いため、1901年が表示されていると思われます。
before_year.jpeg

LVGLのカレンダー部品(デフォルトの年リスト)が、1901〜2025となっていることが直接要因

そこで、「M5Stack User Demo」に年リストの設定処理を追加します。

もう一点、WiFi が APモードになっているので、これを STAモード(ステーションモード)に変更して自宅のWiFiルータに接続するように変更します。
before_wifi.jpeg


ちなみに、初期ファームウェア に戻す方法は、次のURLで案内されています。

Tab5 ファームウェアの初期化:
https://docs.m5stack.com/ja/guide/restore_factory/m5tab5


Windows向け:Easyloader: Tab5 Factory Firmware
https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1132/C145_Tab5_User_Demo.exe

M5Stack Tab5 初期ファームウェア:
https://github.com/m5stack/M5Tab5-UserDemo/releases/download/V0.2/C145-Tab5-UserDemo-V0.2-20250909_0x0.bin

2. 環境準備

「ESP-IDF v5.4.2」の環境構築 と 「M5Stack User Demo」のビルドを行います。次のURLで説明されているので、参考にしてください。


以下のコマンドを順に実行します。
# esp-idf v5.4.2 クローン
$ cd ~
$ git clone -b v5.4.2 --recursive https://github.com/espressif/esp-idf.git

# esp-idf インストール
$ cd esp-idf
$ ./install.sh
$ . ./export.sh


# M5Stack User Demo クローン
$ cd ~
$ git clone https://github.com/m5stack/M5Tab5-UserDemo.git
$ cd M5Tab5-UserDemo
$ python ./fetch_repos.py

# M5Stack User Demo ビルド
$ cd platforms/tab5
$ idf.py build

コンパイルエラーが無ければ、次のメッセージが出力されます。

Successfully created esp32p4 image.
Generated /home/USERNAME/M5Tab5-UserDemo/platforms/tab5/build/m5stack_tab5.bin
(以下省略)

多くのワーニングが出力されますが無視してOKです。

3.カスタマイズ

先にも書きましたが、次の2点を変更します。

  1. カレンダーに 独自の年リストを追加します
  2. WiFi を STAモードに変更し、自IPアドレスを表示するように変更します

(以降の変更箇所は、コピペしてタイプミスを防止することをお勧めします)

なお、2つのカスタマイズ内容は、互いに独立しています。よって、どちらか一方のカスタマイズだけを適用することも可能です。

3.1 年リストの追加

次の2ファイルを変更します。

  • dependencies/smooth_ui_toolkit/src/lvgl/lvgl_cpp/calendar.h
  • app/apps/app_launcher/view/panel_rtc.cpp

(1) calendar.h

calendar.hの56行目あたりに、次のheaderDropdownSetYearList関数を追加します。

calendar.h
    lv_obj_t* headerDropdownCreate()
    {
        return lv_calendar_header_dropdown_create(this->raw_ptr());
    }

+   void headerDropdownSetYearList(const char* year_list)
+   {
+       lv_calendar_header_dropdown_set_year_list(this->raw_ptr(), year_list);
+   }

(2) panel_rtc.cpp

panel_rtc.cppの61行目あたりに、次の2行を追加します。

panel_rtc.cpp
    _calendar->headerDropdownCreate();
+   const char *year_list = "2029\n2028\n2027\n2026\n2025\n2024\n2023\n2022\n2021\n2020";
+   _calendar->headerDropdownSetYearList(year_list);
    _calendar->setTodayDate(local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday);

今回は年リストを、2020〜2029 としましたが、範囲はご自由に設定してください。

ちなみに、LVGLのカレンダー部品の`年リスト`は、`dependencies/lvgl/src/widgets/calendar/lv_calendar_header_dropdown.c`の46行目で、1901〜2025と定義されています。
lv_calendar_header_dropdown.c
static const char * year_list = {
    "2025\n2024\n2023\n2022\n2021\n"
    "2020\n2019\n2018\n2017\n2016\n2015\n2014\n2013\n2012\n2011\n2010\n2009\n2008\n2007\n2006\n2005\n2004\n2003\n2002\n2001\n"
    "2000\n1999\n1998\n1997\n1996\n1995\n1994\n1993\n1992\n1991\n1990\n1989\n1988\n1987\n1986\n1985\n1984\n1983\n1982\n1981\n"
    "1980\n1979\n1978\n1977\n1976\n1975\n1974\n1973\n1972\n1971\n1970\n1969\n1968\n1967\n1966\n1965\n1964\n1963\n1962\n1961\n"
    "1960\n1959\n1958\n1957\n1956\n1955\n1954\n1953\n1952\n1951\n1950\n1949\n1948\n1947\n1946\n1945\n1944\n1943\n1942\n1941\n"
    "1940\n1939\n1938\n1937\n1936\n1935\n1934\n1933\n1932\n1931\n1930\n1929\n1928\n1927\n1926\n1925\n1924\n1923\n1922\n1921\n"
    "1920\n1919\n1918\n1917\n1916\n1915\n1914\n1913\n1912\n1911\n1910\n1909\n1908\n1907\n1906\n1905\n1904\n1903\n1902\n1901"
};

いずれ、2026が追加されるものと思います。

3.2 WiFi STAモードへの変更

次の4ファイルを変更します。

  • app/hal/hal.h
  • platforms/tab5/main/hal/hal_esp32.h
  • platforms/tab5/main/hal/components/hal_wifi.cpp
  • app/apps/app_launcher/view/panel_switches.cpp

(1) hal.h

hal.hの260行目あたりに、次の2行を追加します。

hal.h
    virtual void startWifiAp()
    {
    }
+   virtual std::string getWifiSSID(){ return ""; }
+   virtual std::string getWifiIPv4(){ return ""; }

(2) hal_esp32.h

hal_esp32.hの77行目あたりに、次の2行を追加します。

hal_esp32.h
    void startWifiAp() override;
+   std::string getWifiSSID() override; // get SSID
+   std::string getWifiIPv4() override; // get My IP Address (v4)

(3) hal_wifi.cpp

  1. hal_wifi.cppの24行目あたりから、次のように変更します
hal_wifi.cpp
- #define WIFI_SSID    "M5Tab5-UserDemo-WiFi"
- #define WIFI_PASS    ""
+ #define WIFI_SSID    "YOUR_WIFI_SSID"
+ #define WIFI_PASS    "YOUR_WIFI_PASS"

(自分のWi-Fi環境に合わせて、SSIDとパスワードを変更)

2. hal_wifi.cppの94行目あたりから、次のように追加・変更します

hal_wifi.cpp
void wifi_init_softap()
{
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

-   esp_netif_create_default_wifi_ap();
+   esp_netif_create_default_wifi_sta();    //STAモード

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    wifi_config_t wifi_config = {};
-   std::strncpy(reinterpret_cast<char*>(wifi_config.ap.ssid), WIFI_SSID, sizeof(wifi_config.ap.ssid));
-   std::strncpy(reinterpret_cast<char*>(wifi_config.ap.password), WIFI_PASS, sizeof(wifi_config.ap.password));
-   wifi_config.ap.ssid_len       = std::strlen(WIFI_SSID);
-   wifi_config.ap.max_connection = MAX_STA_CONN;
-   wifi_config.ap.authmode       = WIFI_AUTH_OPEN;
+   std::strncpy(reinterpret_cast<char*>(wifi_config.sta.ssid), WIFI_SSID, sizeof(wifi_config.sta.ssid));
+   std::strncpy(reinterpret_cast<char*>(wifi_config.sta.password), WIFI_PASS, sizeof(wifi_config.sta.password));
+   wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
+   wifi_config.sta.pmf_cfg.capable  = true;
+   wifi_config.sta.pmf_cfg.required = false;

-   ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
-   ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
+   ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));  //STAモード
+   ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
+   ESP_ERROR_CHECK(esp_wifi_connect()); //WiFi Connecting...
    
-   ESP_LOGI(TAG, "Wi-Fi AP started. SSID:%s password:%s", WIFI_SSID, WIFI_PASS);
+   ESP_LOGI(TAG, "Wi-Fi Station started. SSID:%s password:%s", WIFI_SSID, WIFI_PASS);
}

esp_wifi_connect()は 非同期処理のため、本来はイベント処理を追加して接続完了を監視すべきですが、(SSIDとパスワードに誤りが無ければ)いずれ接続されるため、今回は良しとしました。


3. hal_wifi.cppの165行目あたりに、次の2つの関数を追加します

hal_wifi.cpp
  void HalEsp32::startWifiAp()
  {
      wifi_init();
  }
  
+ std::string HalEsp32::getWifiSSID() {
+     return std::string(WIFI_SSID);
+ }
  
+ std::string HalEsp32::getWifiIPv4() {
+     char ip[64];
+     esp_netif_ip_info_t ip_info;
+     esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); // STAモードのインターフェースを取得
+     if (netif != NULL) {
+         esp_netif_get_ip_info(netif, &ip_info);
+         return std::string(esp_ip4addr_ntoa(&ip_info.ip, ip, sizeof(ip))); // IPアドレスv4
+     }
+     return "NO IP Address";
+ }

(4) panel_switches.cpp

  1. panel_switches.cppの75行目あたりに、次のように変更(削除)します
panel_switches.cpp
    _panel_ssid = std::make_unique<Container>(_window->get());
    _panel_ssid->align(LV_ALIGN_CENTER, 87, -80);
    _panel_ssid->setSize(379, 51);
    _panel_ssid->setRadius(22);
    _panel_ssid->setBorderWidth(0);
    _panel_ssid->setBgColor(lv_color_hex(0x725151));

-   _label_ssid = std::make_unique<Label>(_panel_ssid->get());
-   _label_ssid->align(LV_ALIGN_CENTER, 0, 0);
-   _label_ssid->setTextFont(&lv_font_montserrat_24);
-   _label_ssid->setTextColor(lv_color_hex(0xFFFFFF));
-   _label_ssid->setText("M5Tab5-UserDemo-WiFi");

    _panel_url = std::make_unique<Container>(_window->get());
    _panel_url->align(LV_ALIGN_CENTER, 105, 0);
    _panel_url->setSize(336, 51);
    _panel_url->setRadius(22);
    _panel_url->setBorderWidth(0);
    _panel_url->setBgColor(lv_color_hex(0x725151));

-   _label_url = std::make_unique<Label>(_panel_url->get());
-   _label_url->align(LV_ALIGN_CENTER, 0, 0);
-   _label_url->setTextFont(&lv_font_montserrat_24);
-   _label_url->setTextColor(lv_color_hex(0xFFFFFF));
-   _label_url->setText("http://192.168.4.1");

//をつけてコメント化を推奨

2. panel_switches.cppの169行目あたりに、次のように追加します

panel_switches.cpp
        } else {
            _label_msg_a = std::make_unique<Label>(_panel_msg->get());
            _label_msg_a->align(LV_ALIGN_CENTER, 0, 0);
            _label_msg_a->setTextFont(&lv_font_montserrat_22);
            _label_msg_a->setTextColor(lv_color_hex(0xFFFFFF));
            _label_msg_a->setText("Using internal antenna.");

            _label_msg_b.reset();
        }

+       _label_ssid = std::make_unique<Label>(_panel_ssid->get());
+       _label_ssid->align(LV_ALIGN_CENTER, 0, 0);
+       _label_ssid->setTextFont(&lv_font_montserrat_24);
+       _label_ssid->setTextColor(lv_color_hex(0xFFFFFF));
+       _label_ssid->setText(GetHAL()->getWifiSSID());
+
+       _label_url = std::make_unique<Label>(_panel_url->get());
+       _label_url->align(LV_ALIGN_CENTER, 0, 0);
+       _label_url->setTextFont(&lv_font_montserrat_24);
+       _label_url->setTextColor(lv_color_hex(0xFFFFFF));
+       _label_url->setText("http://" + GetHAL()->getWifiIPv4());
    }

以上で、ソースコードの変更は終わりです。
各ファイルを保存してビルドします。

4. ビルド

次のコマンドにて ビルドします。変更ソースに関係するファイルだけ再コンパイルされるため、環境準備のときのビルドより短時間で済むはずです。

# esp-idf 環境設定(念のため)
$ cd ~/esp-idf
$ . ./export.sh

$ cd ~/M5Tab5-UserDemo/platforms/tab5
$ idf.py build

コンパイルエラーが無ければ、次のメッセージが出力されます。

Successfully created esp32p4 image.
Generated /home/USERNAME/M5Tab5-UserDemo/platforms/tab5/build/m5stack_tab5.bin
(以下省略)

コンパイルエラーになった場合は、変更したソースファイルを見直してください。

5. カスタマイズした「M5Stack User Demo」ファームウェアを書き込む

M5Stack Tab5 ダウンロードモードにします(デバイスのリセットボタンを長押し(約2秒)、内部の緑色LED が点滅したらボタンを離す)。

$ idf.py flash
or
$ idf.py -p PORT flash
or
$ python -m esptool --chip esp32p4 -b 460800 --before default_reset --after hard_reset write_flash --flash_mode dio --flash_size 16MB --flash_freq 80m 0x2000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/m5stack_tab5.bin
or 
$ python -m esptool --chip esp32p4 -b 460800 --before default_reset --after hard_reset write_flash "@flash_args"

しかし、自分のESP-IDF環境はDockerで構築しているので、直接PCのシリアルデバイスに書き込むことができません。そのため、次の3ステップにて書き込みます。

  1. 3本の.binを統合
docker環境で実行
$ cd ~/M5Tab5-UserDemo/platforms/tab5
$ esptool.py --chip esp32p4 merge_bin --output ~/m5-merged.bin \
0x2000 build/bootloader/bootloader.bin \
0x8000 build/partition_table/partition-table.bin \
0x10000 build/m5stack_tab5.bin


2. 統合したファームウェアをホストPCにコピー

ホストで実行
$ docker cp <コンテナID>:/home/USERNAME/m5-merged.bin .


3. コピーしたファームウェアを書き込む

ホストで実行
$ esptool --chip auto --port <シリアルポート> --baud 1500000 \
--after hard_reset write-flash 0 ./m5-merged.bin

リモートシリアルポートを使えば、docker環境から直接書き込むこともできるのですが、時間がかかるのでコピーしています。

docker コンテナからリモートシリアルポートを使って ホストに接続した シリアルデバイスにアクセスする:
https://qiita.com/nak435/items/76cbf08b25b614e3c80b

4. 結果確認

下記スクショのように、期待した通りに変更されました。

after_year.jpeg after_wifi.jpeg

WiFi接続完了前(もしくは、DHCPアドレス取得前)の状態の場合は、http://0.0.0.0と表示されます。その場合は、一旦閉じて、再度開くと表示されると思います。

PCのブラウザから http://192.168.0.9 にアクセスできました。

web.png

5. 終わりに

ソースコードの解析と、変更コードの作成・検証に半日ほどかかりましたが、無事に目的を達成できました。

何かの参考になれば幸いです。

以上 

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?