macsbug

Make M5Stack Servo Module

leave a comment »

M5Stack Servo Module を製作しました。            2019.08.10

Servo Module は DC Motor 又は Stepper Motor を ドライブします。
Driver tip は DRV8833 (90円) を使用し 費用は 400円です。

調査チップは 左上から L9110, DRV8833, MX1508, DRV8835 の4種類です。


Drive IC と Module 表:

Name price Size mm Store Transport Operation
 L9110     83円  29×23   yang-sell584  22 day X
 DRV8833     90円  19×16   shoptongs  15 day
 MX1508   267円  25×21   sweetsale07  16 day
 DRV8835   300円  15×10   秋月電子通商  –

_ 評価:価格, 輸送期間, サイズ, 工作容易, 動作 調査の結果 DRV8833 を採用。
_   仕様:Output Current=1.5A, e Power Supply Voltage Range=2.7 to 10.8 V

_ L9110:基板に組む為のコネクターの取り外しが手間と動作が不安定で非採用。
_ DRV8833:ebay の shoptongs Store は 2個180円と安く、輸送期間は15日で短い。
_      小型の為、複数個の搭載が可能。
_ DRV8833国内価格:スイッチサイエンスは 733円で8倍の高価格。購入は非採用。
_ MX1508 は L298N と同等品の様です。低価格重視の為に非採用。
_ DRV8835:超小型で組み込み便利。優秀な基板です。低価格重視の為に非採用。
_       入力電源と出力電源は別系統の設計になっています。


部品準備:

No Nomen Purchase Price
1  DRV8833  ebay;shoptongs    90円
2  ピンヘッダー ( male, H=7.7mm )  秋月電子通商:

  低オス 2 x 40

   90円
3  ピンヘッダー 1×6 (6P)  秋月電子通商:ピンヘッダ    20円
4  M5Stack PROTO Board  tomorrow氏設計製作 基板  100円
5  M2 5mm ナベTapping Screw(4ea)  西川電子部品株式会社      6円
6  ケース:H=8.3mm  自作:材料費 FDM
  Low cost PROTO module
  of M5STACK Part 2
   14円
7  ショットキーバリアダイオード
. RSX101VAM-30, 30V1A
 秋月電子通商:
  RSX101VAM-30
     9円
=  ====================  ================ ======
費用総計 329円

 


配線:
_ M5Stack 配線例:配線は 10本です。
_ DRV8833 の 入力(GPIO)電源はVCC、出力 (Motor)電源は ULT(VM) です。
_  電源は 共に M-BUS の 3.3Vを使用します。
_  ULT – VCC間のショットキーバリアダイオードは VCC から ULT へ供給します。
_   補足:補助バッテリ未接続時に ULT へ電源を供給します。
_  外部もしくは補助バッテリーを使用する時は ULT(VM) へ接続します。
_ DRV8833 の IN 1,2,3,4 に M5Stack の GPIO を接続します。
_ DRV8833 の OUT 1,2,3,4 に DC Motor を接続します。
_ メモ : IN, OUT は基板上の名前、IC の名前は AIN, BIN, AOUT,BOUT です。
_ メモ:MOTORの回転方向:+, – 表示と回転方向は MOTORにより異なります。

 M5Stack 3V3 GND GPIO 5 GPIO 17 GPIO 13 GPIO 0
 DRV8833 VCC GND IN1 IN2 IN3 IN4
 DRV8833 ULT GND OUT1 OUT2 OUT3 OUT4
 DC Motor L Motor – L Motor + R Motor + R Motor –

.
_ 各端子と動作表: 1つの Motor の動作を示します。

INx IN1 IN2 DRV8833 Input
GPIO 5 7 M5Stack M-BUS
Motor RIGHT – RIGHT + DC Motor
1 PWM 0 Foward fast
2 1 PWM Foward slow
3 0 PWM Reverse fast
4 PWM 1 Reverse slow

.
_ スケッチの基本:

ledc Channel 1 2 3 4
    M-BUS GPIO 5 17 13 0
    DRV8833 IN 1 2 3 4
    DRV8833 OUT 1 2 3 4
    Motor Right – Right + Left  + Left  –

 

ledWrite ( ch , PWM or Phase );
ledWrite ( 1 , PWM);  // PWM   : Foward slow
ledWrite ( 1 , 1 );   // Phase : Foward slow

_ 基板レイアウトと回路図、配線。

.
_ DRV8833 Module Schematic

.
Servo Module Layout:

.
_ Solder side wiring

メモ:
_ Motorの電源は M5Stack USB 又は 2400mA Battery の 3.3v を使用します。
_ Motorの電源は 外部バッテリーを使用する事も可能です。
_ 配線面の右上のコネクターは 外部バッテリー接続時の予備のコネクターです。
_ 綺麗なデザインにする為に 接続コネクターは横から出さず 縦に接続します。


資料:
L9110:L9110 Datasheet
DRV8833:DRV8833 Dual H-Bridge Motor Driver
MX1508:Using the MX1508 Brushed DC Motor Driver , pdf
DRV8835:DRV8835 Dual Low-Voltage H-Bridge IC
H-BRIDGE:どの Module (tip) も H-BRIDGE 接続 ( Motor の正逆転) が可能です。
_ H-Bridge DC Motor Applications
Adafruit DRV8833 DC/Stepper Motor Driver Breakout Board
My Tech notes:DRV8833 motor driver module:回路動作と回路図。
Pololu:DRV8833 Dual Motor Driver Carrier:基板と動作の詳細。
HACKADAY.IO:Low level DC motor driver:H-bridge circuit の説明。
コーヒーとチョコレート:ESP32でローバーの基板を作ったお話:ESP32+DRV8833


感想:
今回の記事は DC Motor を低価格で動作させる為の Servo Drive です。
格安で動作する Servo Module を調査 及び 動作試験を実施しました。
Module を使用するとCR部品も装備されている為、便利です。
結果、DRV8833 は 格安で輸送期間も短くリーズナブルです。

次回は DC Motor Module の製作記事を予定です。


M5Stack , Servo , H-bridge , DRV883 3, DRV8835 , MAX1508 , L9110 , L298N ,

Written by macsbug

8月 10, 2019 at 12:41 pm

カテゴリー: M5STACK

M5StickC Start Adapter

leave a comment »

M5StickC Start Adapter を製作しました。         2019.07.09

M5StickC は 条件によって起動しない時があります。故障ではありません。
Start Adapter を GPIO コネクターに接続する事により起動が可能です。
費用は 50円程度です。
原因は 詳細を省きますが バッテリー電圧の低下と電源管理ICとの兼ね合いです。


.
準備:
1. 2.54mm 4Pin header
2. 680Ω ( 690Ω = 470Ω + 220Ω ) tip resistor
_ 簡単には 1KΩでも可能です。
上記により 余った部品でもできるかと思います。


.
製作:
1. 4Pin header の 3V3 と G0 間の BAT端子を外します。
2. Pin header の先端を平らに切ります。
3. M5StickC GPIO 端子の 3V3 と G0 間に 470Ω と 220Ω を接続します。
_ 合計 1KΩでも可能です。
4. M5StickC に接続する時は 向きに注意してください。
5. 面倒な場合:簡単な方法は 3V3 と G0 間に 通常(1/4W)の1KΩを接続します。


.
感想:
初回購入時より 時々 起動せず 分解して調べましたが異常なし。
バッテリ電圧が低下すると 電源管理ICの動作で停止する様になっています。
電源管理チップの設定の件もありますが、抵抗接続で出来るので簡単です。


M5StickC, 起動しない、680Ω、

Written by macsbug

7月 9, 2019 at 6:25 pm

カテゴリー: M5STACK

M5Stack Nixie tube Clock

leave a comment »

M5Stack で ニキシー管時計を製作しました。                 2019.06.16

M5Stack に 130円の リアルタイムクロックボード(RTC)を追加し 表示は ニキシー管画像 を使用しました。
ネット接続ができない場所でも RTCボード があると動作します。
RTCボード が無くても ネット接続 ( NTP ) できると動作します。

.

機能:Cボタン スイッチで 表示を切り換えます。
_  時計機能:130円の リアルタイムクロックボード(RTC)を追加しました。
_        RTCが無くとも動作します。
_  NTP設定:起動時に ネット接続が可能であれば RTCにNTPを保存します。
_  時間調整:ネット接続が可能であれば 決められた時間にNTP時間を調整(保存)します。

動作:ネットのNTPへ接続し 日付と時間のデーターを入手します。
_  日付と時間を RTC へ保存します。
_  ネット環境が無いか 接続出来ない場合は 上記のNTPをパスします。
_  RTC から 日付と時間を取得し ディスプレーのニキシー管に表示します。
_  RTC が無い場合は NTP の時間を使用します。
_  時間の更新は ESP32内の Timer を使用します。( 理由は 製作メモ を参照ください) 

DS3231 ボード:M5Stackは 時計機能が無い為、DS3231 リアルタイムクロック (RTC) を追加します。
_ 使用する DS3231 は ラズベリーパイ用の RTC Module で 130円と低価格で小型です。
_ Raspberry Pi RTC Module With Battery:回路図参照の事。
_ DS3231 の使用事例、価格、販売店 は 「DS3231 RTC in ESP8266」を参照ください。
_ 日本国内は高いですので ebay か Aliexpress で入手します。
_ 


.
部品の準備:DS3231 Precision RTC Clock Memory Module For Arduino Raspberry Pi
販売:ebay の worldchips Store は 製品管理は良いです。輸送期間は8日〜16日を経験しています。 
注意:Aliexpress や ebay は 販売画像と異なる物が来る事があります。
_  DS3231 Module は 電池付きであるかを確かめてください。
_  以下の worldchips Store を再調査すると Battery 付きではありませんでした。2019.06.29
_  最近、中◯からのバッテリの輸送は出来ない話を聞いています。2019.07.07
_  CR1025:3V 10.0 x 2.5mm。2019.07.07
_  ボタン電池一覧表で 電圧とサイズを確認して購入してください。2019.07.07

Store price memo    
 ebay : worldchips  127円  1個    
 ebay : worldchips  118円  10個 1184円    
 ebay : lincx  116円  1個    
 ebay : 3c_topstore  214円  1個 調査済(追記)  ⭕ 

.
追記:2019.07.23。3c_topstore Store の調査結果:
_  価格:1個 215円 ( 10個 $19.9 )。バッテリー付。輸送期間=19日。
追記:2019.07.04。DS3231 価格:
_  その後の調査で スイッチサイエンスの DS3231 は 1個 2104円です。約17倍の価格。
.


.
M5Stack 改造:DS3231 BOARD の取り付け。

1. DS3231 BOARD の BATTERY が 3.0V ある事を確認します。
2. BATTERY の位置を移動します。移動とハンダ付け時に向きに注意してください。
_ 
3. DS3231 BOARD をカプトンテープで保護します。
4. ハンダ付け時に周囲を溶かさない様にテープ等でケースを保護します。
5. M5Stackの基板にある小さな部品に接触や負担を与えない配置を考えます。
6. 回路図に従い 配線します。
7. M-BUS 3.3V と GROVE PIN にハンダ付けする時は 短時間(0.5秒)に実施します。
8, 注意:M5Stackには 小さな部品があり 接触, 衝撃, 熱 を加えないでください。


.
ニキシー管 画像のHex データー製作:元画像 70×134 pixel ( png ):

_ 12枚の画像 70×134 から vfd_70x134.c を作成します。

1.  vfd_18x34.c と vfd_35x67.c は 「M5StickC Nixie tube Clock」から入手します。

2.  vfd_70x134_0, , , , vfd_70x134_9, vfd_70x134_n.png, vfd_70x134_q.png
_ 以下の画像を DL ( ドラッグ アンド ドロップ ) し
_ 「M5StickC Nixie tube Clock」を参照し
_ 「lcd-image-converter 20180211-beta」で vfd_70x134.c を作成します。
_ 少し手間かもしれませんが マスターすると 綺麗な作品を自由に作る事が可能になります。

3. apple_35x41.png:Apple icon が好きな方へ。 apple_35x41.c は リストに記載しました。
_ 

4. vfd_70x134_0 〜 vfd_70x134_9, vfd_70x134_n.png, vfd_70x134_q.png

.

5. スケッチ:
_ 下の様に スケッチを用意します。
_ ライブラリー:RTClib : スケッチ内に入れるか ライブラリーにいれます。
_ フォルダーは以下の様になります。
_  


.
製作メモ:Production note
1. 時間の更新は ESP32内の Timer を使用します:
_ RTC の データーを 1秒毎に取得すると なぜか 飛び飛びの値しか取得出来ません。
_ いろいろ試しましたが 技術力が無い為か 1秒毎に取得する事はできませんでした。
_ 対策:その為に 1秒毎の動作は ESP32 の Timer を使用しています。
_ ネットでのサンプルを見ると 3秒毎に取得する例が殆どで 1秒単位の表示ができません。
_ M5Stack のサンプルは Timer を使用しています。
_ Timer を使用する場合は 適切な 単位の宣言(unsigned long)が必要です。
2. RTC は CS が無きため GPIO は D(SDA)=5, C(SCK)=13 としました。

maffucci氏の M5Stack Fire Robottillo:Stack Case を製作しました。
ロボチックな感じがなかなか良いです。

3. トラブルシュート:
_ シリアルモニター:「RTC is NOT running!」が表示される:DS3231 の配線を確認します。
_ ディスプレーの表示:「2165/165/165 165:165:85」になる:DS3231 の配線を確認します。
_ ディスプレーの表示:「2010 ,,,, 」になる:DS3231 のバッテリー不足です。交換します。
_ バッテリー交換:電圧をチェックしてください。


.
追記:青いニキシー管:2019.07.04
_ ニキシー管イメージは 微妙で 数字周りのニキシー管イメージが少ない感じです。
_ 


.
参考:
M5StickC Nixie tube Clock:2019.06.15
DS3231 RTC in ESP8266:2016.10.07:
ESP8266 + RTC + NTP デジタル時計:2015.12.05:
+DEVICE PLUS:ESP32とRTCで「二度寝防止アラーム時計」を作ろう!
Raspberry Pi RTC Module With Battery:回路図があります。


.
感想:
_ 前回 M5StickC Nixie tube Clock:これに基づき M5Stack も製作しました。
_ 小型化:PROTO Board か Expansion Board は大きくなりますので本体に収めました。
_ 本体未改造方法:Expansion Board か Expansion Adapter で行なう方法があります。
_ Nixie 画像 70×134 pixel :M5Stack の画面に合わせ作成しました。
_ DS3231のバッテリー:ハンダ付けです。電圧が無い場合は交換する必要があります。
_ DS3231 モジュールが無い場合:NTPやPC TIME を使用し電源を切らなければ使用できます。
_ 表示:ニキシー数値のサイズや位置を変更すると 好みのデザインができます。

_ 追記:2019.06.30。
_  facebook:関氏の情報:「電池は省略し本体のBATT端子に接続しました。」
_   配線は使い慣れた0.2mmのホルマル線を使用。
_   GROVEのGNDは付けにくいのでUSBフレームのGNDに強引にはんだ付け。


.
スケッチ:M5Stack nixie tube clock : 2019.06.16 macsbug

// M5Stack nixie tube clock : 2019.06.16 macsbug
// https://macsbug.wordpress.com/2019/06/16/m5stack-nixie-tube-clock/
// RTC DS3231 : https://wiki.52pi.com/index.php/Raspberry_Pi_Super_Capacitor_RTC(English)
// RTClib : https://www.arduinolibraries.info/libraries/rt-clib
#include <M5Stack.h>
#include "M5StackUpdater.h"
#include <WiFi.h>
#include <SPI.h> 
#include "RTClib.h"
RTC_DS3231 rtc;
#include "vfd_18x34.c"   // font 18px34
#include "vfd_35x67.c"   // font 35x67
#include "vfd_70x134.c"  // font 70px134
#include "apple_35x41.c" // icon 35px41 
const uint8_t rtc_sda = 5, rtc_sck = 13;
const char *ssid = "your ssid";
const char *pass = "your password";
uint32_t targetTime = 0; // for next 1 second timeout
const uint8_t*n[] = { // vfd font 18x34
  vfd_18x34_0,vfd_18x34_1,vfd_18x34_2,vfd_18x34_3,vfd_18x34_4,
  vfd_18x34_5,vfd_18x34_6,vfd_18x34_7,vfd_18x34_8,vfd_18x34_9 };
const uint8_t*m[] = { // vfd font 35x67
  vfd_35x67_0,vfd_35x67_1,vfd_35x67_2,vfd_35x67_3,vfd_35x67_4,
  vfd_35x67_5,vfd_35x67_6,vfd_35x67_7,vfd_35x67_8,vfd_35x67_9,
  vfd_35x67_q,vfd_35x67_n };
  const uint8_t*b[] = { // vfd font 70x134
  vfd_70x134_0,vfd_70x134_1,vfd_70x134_2,vfd_70x134_3,vfd_70x134_4,
  vfd_70x134_5,vfd_70x134_6,vfd_70x134_7,vfd_70x134_8,vfd_70x134_9,
  vfd_70x134_q,vfd_70x134_n };
const char *monthName[12] = {
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
uint16_t yy;
uint8_t mn, dd, hh, mm, ss;
uint8_t md = 2; // mode 1(yyyy_mmdd_hhmmss),2(mmdd_hh_mmss),3(mmdd_ss_hhmm)
 
void setup() { 
  M5.begin();
  Wire.begin(rtc_sda,rtc_sck);delay(10); //GLOVE A : SDA,SCL
  if(digitalRead(39)==0){updateFromFS(SD);ESP.restart();}
  rtc.begin(); 
  M5.Lcd.setBrightness(255);
  M5.Lcd.setRotation(1);
  M5.Lcd.fillRect(0, 0, 319, 239, ORANGE);
  M5.Lcd.fillRect(1, 1, 317, 236, BLACK);
  M5.Lcd.setTextSize(2);
  esp_timer_init();
  wifi_setup(); // wifi conection, NTP read, Save NTP to RTC
  rtc_setup();  // Read the value of RTC
}

void loop() {
  M5.update();
  if (targetTime < esp_timer_get_time()/1000 ){
    targetTime = esp_timer_get_time()/1000 + 1000;
    ss++; if (ss == 60) { ss = 0; mm++;
           if (mm == 60) { mm = 0; hh++;
            if (hh == 24) { hh = 0; dd++; //
   }}}}
  if(M5.BtnC.wasPressed()){            // mode change
   if (md == 3){md = 1;M5.Lcd.fillRect(1,1,317,236,BLACK);return;}
   if (md == 2){md = 3;M5.Lcd.fillRect(1,1,317,236,BLACK);return;}
   if (md == 1){md = 2;M5.Lcd.fillRect(1,1,317,236,BLACK);return;}
  }
  if ( md == 3 ){ hhmm();}             // yyyy,mm,dd,ss,hh,mm
  if ( md == 2 ){ yyyy_mmdd_hhmmss();} // yyyy,mm,dd,hh,mm,ss
  if ( md == 1 ){ mmss();}             // mm,ss
  periodic_ntp();                      // Set the time by periodic NTP
  delay(500);
}

void wifi_setup(){
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  M5.Lcd.setCursor(20,40);M5.Lcd.setTextColor(WHITE);
  M5.Lcd.print("Connecting to wifi");
  WiFi.begin(ssid,pass);
  int wifi_check = 10; // WiFi count
  M5.Lcd.setCursor(20,80);M5.Lcd.setTextColor(YELLOW);
  M5.Lcd.print("wifi check in progress");
  while (WiFi.status() != WL_CONNECTED){ // WiFI Status
    delay(500); wifi_check--; if (wifi_check < 0){break;}
  }
  if (wifi_check > 0){  // when there is wifi
    M5.Lcd.setCursor(20,120);M5.Lcd.setTextColor(GREEN);
    M5.Lcd.print("wifi connection complete");delay(2000);
    struct tm t;
    configTime(9*3600L,0,"ntp.nict.jp",
      "time.google.com","ntp.jst.mfeed.ad.jp");
    getLocalTime(&t);
    yy = t.tm_year + 1900; mn = t.tm_mon + 1; dd = t.tm_mday;
    hh = t.tm_hour; mm = t.tm_min; ss = t.tm_sec;
    //Serial.printf("%d %d %d %d %d %d\n",yy,mn,dd,hh,mm,ss);
    rtc.adjust(DateTime(yy,mn,dd,hh,mm,ss));//save ntp to rtc
    M5.Lcd.setCursor(20,160);M5.Lcd.setTextColor(GREEN);
    M5.Lcd.print("SET UP NTP");delay(2000); 
  }
  if (wifi_check < 0){   // When there is no wifi
    M5.Lcd.setCursor(20,120);M5.Lcd.setTextColor(ORANGE);
    M5.Lcd.print("without wifi connection");delay(2000);
  }
}

void rtc_setup(){
  DateTime now = rtc.now(); // time geting from RTC
  if (now.year() == 2165){  // rtc check
    M5.Lcd.setCursor(20,200);M5.Lcd.setTextColor(RED);
    M5.Lcd.print("RTC FAIL");delay(2000);
    M5.Lcd.fillRect(1, 1, 317, 236, BLACK);
    }else{
    yy = now.year(); mn = now.month(); dd = now.day();
    hh = now.hour(); mm = now.minute(); ss = now.second();
    //Serial.printf("%d %d %d %d %d %d\n",yy,mn,dd,hh,mm,ss);
    M5.Lcd.setCursor(20,200);M5.Lcd.setTextColor(BLUE);
    M5.Lcd.print("SET UP RTC");delay(2000);
    M5.Lcd.fillRect(1, 1, 317, 236, BLACK);
  }
}

void yyyy_mmdd_hhmmss(){
  int y1 = (yy / 1000) % 10; int y2 = (yy / 100) % 10;
  int y3 = (yy /   10) % 10; int y4 = yy % 10;
  int ma = (mn /   10) % 10; int mb = mn % 10;
  int d1 = (dd /   10) % 10; int d2 = dd % 10;
  int h1 = (hh /   10) % 10; int h2 = hh % 10;
  int m1 = (mm /   10) % 10; int m2 = mm % 10;
  int s1 = (ss /   10) % 10; int s2 = ss % 10;

  //int p0 = 8; int x0 = 40; int t0 = 22; // icon
  //M5.Lcd.pushImage( p0 + 0*x0, t0, 35,41, (uint16_t *)apple_35x41);

  int p1 = 80; int px1 = 40; int py1 = 5;
  M5.Lcd.pushImage( p1 + 0*px1, py1, 35,67, (uint16_t *)m[y1]); 
  M5.Lcd.pushImage( p1 + 1*px1, py1, 35,67, (uint16_t *)m[y2]);
  M5.Lcd.pushImage( p1 + 2*px1, py1, 35,67, (uint16_t *)m[y3]);
  M5.Lcd.pushImage( p1 + 3*px1, py1, 35,67, (uint16_t *)m[y4]);
  
  int p2 = 80; int px2 = 40; int py2 = 76;
  M5.Lcd.pushImage( p2 + 0*px2, py2, 35,67, (uint16_t *)m[ma]);
  M5.Lcd.pushImage( p2 + 1*px2, py2, 35,67, (uint16_t *)m[mb]);
  //M5.Lcd.drawPixel(118,13, ORANGE); M5.Lcd.drawPixel(119,23,ORANGE);
  M5.Lcd.pushImage( p2 + 2*px2, py2, 35,67, (uint16_t *)m[d1]);
  M5.Lcd.pushImage( p2 + 3*px2, py2, 35,67, (uint16_t *)m[d2]);

  int p3 = 2; int px3 = 40; int py3 = 150;
  M5.Lcd.pushImage( p3 + 0*px3, py3, 35,67, (uint16_t *)m[h1]);
  M5.Lcd.pushImage( p3 + 1*px3, py3, 35,67, (uint16_t *)m[h2]);
  M5.Lcd.pushImage( p3 + 2*px3, py3, 35,67, (uint16_t *)m[10]);
  M5.Lcd.pushImage( p3 + 3*px3, py3, 35,67, (uint16_t *)m[m1]);
  M5.Lcd.pushImage( p3 + 4*px3, py3, 35,67, (uint16_t *)m[m2]);
  M5.Lcd.pushImage( p3 + 5*px3, py3, 35,67, (uint16_t *)m[10]);
  M5.Lcd.pushImage( p3 + 6*px3, py3, 35,67, (uint16_t *)m[s1]);
  M5.Lcd.pushImage( p3 + 7*px3, py3, 35,67, (uint16_t *)m[s2]);
  
  if ( s1 == 0 && s2 == 0 ){ fade1();}
}

void mmss(){
  int ma = (mn / 10) % 10; int mb = mn % 10;
  int d1 = (dd / 10) % 10; int d2 = dd % 10;
  int h1 = (hh / 10) % 10; int h2 = hh % 10;
  int m1 = (mm / 10) % 10; int m2 = mm % 10;
  int s1 = (ss / 10) % 10; int s2 = ss % 10;

  int p0 = 8; int x0 = 40; int t0 = 22; // icon
  M5.Lcd.pushImage( p0 + 0*x0, t0, 35,41, (uint16_t *)apple_35x41);

  int p2 = 65; int px2 = 40; int py2 = 10;
  M5.Lcd.pushImage( p2 + 0*px2, py2, 35,67, (uint16_t *)m[ma]);
  M5.Lcd.pushImage( p2 + 1*px2, py2, 35,67, (uint16_t *)m[mb]);
  //M5.Lcd.drawPixel(118,13, ORANGE); M5.Lcd.drawPixel(119,23,ORANGE);
  M5.Lcd.pushImage( p2 + 2*px2, py2, 35,67, (uint16_t *)m[d1]);
  M5.Lcd.pushImage( p2 + 3*px2, py2, 35,67, (uint16_t *)m[d2]);
  
  int p3 = 240; int px3 = 40; int py3 = 10;
  M5.Lcd.pushImage( p3 + 0*px3, py3, 35,67, (uint16_t *)m[h1]);
  M5.Lcd.pushImage( p3 + 1*px3, py3, 35,67, (uint16_t *)m[h2]);
  
  int p4 = 2; int px4 = 80; int py4 = 100;
  M5.Lcd.pushImage( p4 + 0*px4   , py4, 70,134, (uint16_t *)b[m1]);
  M5.Lcd.pushImage( p4 + 1*px4 -4, py4, 70,134, (uint16_t *)b[m2]);
  //M5.Lcd.drawPixel(155,150, ORANGE); M5.Lcd.drawPixel(155,190,ORANGE);
  M5.Lcd.fillCircle(156,151,3,ORANGE);M5.Lcd.fillCircle(156,191,3,ORANGE);
  M5.Lcd.fillCircle(156,151,1,YELLOW);M5.Lcd.fillCircle(156,191,1,YELLOW);
  M5.Lcd.pushImage( p4 + 2*px4 +4, py4, 70,134, (uint16_t *)b[s1]);
  M5.Lcd.pushImage( p4 + 3*px4   , py4, 70,134, (uint16_t *)b[s2]);

  if ( m1 == 0 && m2 == 0 ){ fade2();}
}

void hhmm(){
  int ma = (mn / 10) % 10; int mb = mn % 10;
  int d1 = (dd / 10) % 10; int d2 = dd % 10;
  int h1 = (hh / 10) % 10; int h2 = hh % 10;
  int m1 = (mm / 10) % 10; int m2 = mm % 10;
  int s1 = (ss / 10) % 10; int s2 = ss % 10;

  //int p0 = 8; int x0 = 40; int t0 = 22; // icon
  //M5.Lcd.pushImage( p0 + 0*x0, t0, 35,41, (uint16_t *)apple_35x41);

  int p2 = 65; int px2 = 40; int py2 = 10;
  M5.Lcd.pushImage( p2 + 0*px2, py2, 35,67, (uint16_t *)m[ma]);
  M5.Lcd.pushImage( p2 + 1*px2, py2, 35,67, (uint16_t *)m[mb]);
  //M5.Lcd.drawPixel(118,13, ORANGE); M5.Lcd.drawPixel(119,23,ORANGE);
  M5.Lcd.pushImage( p2 + 2*px2, py2, 35,67, (uint16_t *)m[d1]);
  M5.Lcd.pushImage( p2 + 3*px2, py2, 35,67, (uint16_t *)m[d2]);
  
  int p3 = 240; int px3 = 40; int py3 = 10;
  M5.Lcd.pushImage( p3 + 0*px3, py3, 35,67, (uint16_t *)m[s1]);
  M5.Lcd.pushImage( p3 + 1*px3, py3, 35,67, (uint16_t *)m[s2]);
  
  int p4 = 2; int px4 = 80; int py4 = 100;
  M5.Lcd.pushImage( p4 + 0*px4   , py4, 70,134, (uint16_t *)b[h1]);
  M5.Lcd.pushImage( p4 + 1*px4 -4, py4, 70,134, (uint16_t *)b[h2]);
  //M5.Lcd.drawPixel( 155,150, ORANGE); M5.Lcd.drawPixel(155,190,ORANGE);
  M5.Lcd.fillCircle(156,151,3,ORANGE);M5.Lcd.fillCircle(156,191,3,ORANGE);
  M5.Lcd.fillCircle(156,151,1,YELLOW);M5.Lcd.fillCircle(156,191,1,YELLOW);
  M5.Lcd.pushImage( p4 + 2*px4 +4, py4, 70,134, (uint16_t *)b[m1]);
  M5.Lcd.pushImage( p4 + 3*px4   , py4, 70,134, (uint16_t *)b[m2]);

  if ( h1 == 0 && h2 == 0 ){ fade2();}
}

void fade1(){
  int p3 = 2; int px3 = 40; int py3 = 150;
  for ( int i = 0; i < 2; i++ ){
  M5.Lcd.pushImage( p3 + 2*px3, py3, 35,67, (uint16_t *)m[11]);
  M5.Lcd.pushImage( p3 + 5*px3, py3, 35,67, (uint16_t *)m[11]);
  delay(25);
  M5.Lcd.pushImage( p3 + 2*px3, py3, 35,67, (uint16_t *)m[10]);
  M5.Lcd.pushImage( p3 + 5*px3, py3, 35,67, (uint16_t *)m[10]);
  delay(25);
  }
}

void fade2(){
  int p3 = 2; int px3 = 40; int py3 = 150;
  for ( int i = 0; i < 2; i++ ){
  M5.Lcd.fillCircle(156,151,3,BLACK);M5.Lcd.fillCircle(156,191,3,BLACK);
  delay(25);
  M5.Lcd.fillCircle(156,151,3,ORANGE);M5.Lcd.fillCircle(156,191,3,ORANGE);
  delay(25);
  }
}

void periodic_ntp(){ // Adjust to ntp at hh mm ss
  if ( (hh == 1) & (mm == 0) & (ss == 0)){ // AM 1:00:00
    wifi_setup();
    rtc_setup();
  }
}

.
apple_35x41.c
.

/*******************************************************************************
* generated by lcd-image-converter rev.c6da9ce from 2018-02-11 00:03:45 +0500
* image
* filename: unsaved
* name: apple_35x41
* macsbug
* preset name: Color R5G6B5
* data block size: 8 bit(s), uint8_t
* RLE compression enabled: no
* conversion type: Color, not_used not_used
* split to rows: yes
* bits per pixel: 16
*
* preprocess:
*  main scan direction: top_to_bottom
*  line scan direction: forward
*  inverse: no
*******************************************************************************/

/*
 typedef struct {
     const uint8_t *data;
     uint16_t width;
     uint16_t height;
     uint8_t dataSize;
     } tImage;
*/
#include <stdint.h>

static const uint8_t apple_35x41[2870] = {
    // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒∙∙∙∙∙∙∙∙∙∙
    // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓∙∙∙∙∙∙∙∙∙∙
    // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓▒▒▓∙∙∙∙∙∙∙∙∙∙
    // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▒▒▒▒▒∙∙∙∙∙∙∙∙∙∙
    // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▒▒▒▒▓░∙∙∙∙∙∙∙∙∙∙
    // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▓▒▒▒▒▓∙∙∙∙∙∙∙∙∙∙∙
    // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▓░∙∙∙∙∙∙∙∙∙∙∙
    // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▒▒▒▓▒∙∙∙∙∙∙∙∙∙∙∙∙
    // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▒▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙
    // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
    // ∙∙∙∙∙∙░▒▓▓▓▓▓▒░∙∙∙∙∙∙∙▒▓▓▓▓▓▓▒∙∙∙∙∙
    // ∙∙∙∙∙▓▓▒▒▒▒▒▒▒▓▓▒░░░▒▓▓▒▒▒▒▒▒▓▓░∙∙∙
    // ∙∙∙∙▓▓▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒▒▒▒▒▒▓░∙∙
    // ∙∙∙▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓░∙
    // ∙∙▒▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒∙∙
    // ∙∙█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█▒∙∙∙
    // ∙▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙
    // ∙█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█∙∙∙∙∙
    // ░█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙
    // ▒█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█░∙∙∙∙∙
    // ▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓∙∙∙∙∙∙
    // ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙
    // ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙
    // ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░∙∙∙∙∙
    // ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙
    // ░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒∙∙∙∙∙
    // ∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒∙∙∙∙
    // ∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░∙∙∙
    // ∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░∙∙
    // ∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒∙
    // ∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
    // ∙∙░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒∙
    // ∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒∙
    // ∙∙∙░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒∙∙
    // ∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒∙∙
    // ∙∙∙∙░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒∙∙∙
    // ∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓░∙∙∙
    // ∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░∙∙∙∙
    // ∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒∙∙∙∙∙
    // ∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒░∙∙∙∙∙▒▒▒▒▒▒▒░∙∙∙∙∙∙
    // ∙∙∙∙∙∙∙∙∙∙░▒░∙∙∙∙∙∙∙∙∙∙░░▒░∙∙∙∙∙∙∙∙
    0x21, 0x24, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x21, 0x64, 0x43, 0x87, 0x6d, 0xc9, 0x19, 0x03, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x21, 0x24, 
    0x08, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xc9, 0x7f, 0xaa, 0x6e, 0xe9, 0x87, 0xea, 0x10, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x61, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x62, 0x87, 0xeb, 0x6e, 0xc9, 0x65, 0xe8, 0x65, 0xe8, 0x77, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xe1, 0x87, 0xeb, 0x66, 0x08, 0x65, 0xe8, 0x65, 0xe8, 0x66, 0x08, 0x6e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x0a, 0x66, 0x48, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x77, 0x49, 0x44, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x45, 0x77, 0x6a, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x7f, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x49, 0x66, 0x28, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x87, 0xea, 0x32, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x77, 0x2a, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x87, 0xea, 0x4c, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xe1, 0x7f, 0x6a, 0x66, 0x08, 0x6f, 0x09, 0x87, 0xcb, 0x33, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xc1, 0x08, 0xe1, 0x08, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xe1, 0x8f, 0xec, 0x76, 0xea, 0x44, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xc1, 0x21, 0xe3, 0x19, 0xa2, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x04, 0x66, 0x29, 0x77, 0x0a, 0x7f, 0x4a, 0x7f, 0x4a, 0x7f, 0x2a, 0x76, 0xc9, 0x66, 0x29, 0x44, 0x26, 0x11, 0x42, 0x00, 0x00, 0x08, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xc2, 0x5d, 0x68, 0x76, 0xea, 0x7f, 0x4a, 0x77, 0x4a, 0x77, 0x4a, 0x7f, 0x4a, 0x6e, 0xaa, 0x4c, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x8a, 0x7f, 0x8a, 0x66, 0x48, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x66, 0x28, 0x6f, 0x09, 0x7f, 0x6a, 0x5d, 0xc8, 0x3b, 0x45, 0x2a, 0x83, 0x44, 0x26, 0x66, 0x49, 0x7f, 0x6a, 0x66, 0xa9, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x66, 0x08, 0x6f, 0x29, 0x7f, 0x8b, 0x2a, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x87, 0xab, 0x66, 0xa9, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x66, 0x29, 0x6e, 0xe9, 0x6f, 0x29, 0x6e, 0xc9, 0x66, 0x08, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x65, 0xe8, 0x66, 0x08, 0x87, 0xea, 0x3b, 0x85, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x67, 0x2b, 0x5e, 0x89, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x55, 0xe8, 0x97, 0xec, 0x3b, 0x45, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x5c, 0x86, 0x87, 0x48, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0x6d, 0xc8, 0xa7, 0x89, 0x4c, 0x66, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x21, 0xff, 0x85, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xff, 0x85, 0xab, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0xc4, 0x63, 0xfe, 0xc5, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfe, 0xc5, 0xc4, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0xff, 0x05, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x49, 0xe4, 0xff, 0x65, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfd, 0xc4, 0xfe, 0xa5, 0xc4, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x93, 0xa5, 0xfe, 0xe5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xfd, 0xc5, 0xff, 0x25, 0x51, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0xb3, 0x45, 0xfc, 0x64, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xf3, 0xe4, 0xfc, 0xc4, 0x28, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0xdb, 0xe5, 0xfc, 0x24, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xfc, 0x04, 0xfc, 0xe4, 0x20, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0xdb, 0xe5, 0xfc, 0x24, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xf4, 0x04, 0xfd, 0x04, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0xcb, 0xc6, 0xfc, 0x44, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xf4, 0x24, 0xfc, 0xa4, 0x9a, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x92, 0xe5, 0xfd, 0x24, 0xf4, 0x64, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x84, 0xf4, 0x64, 0xfc, 0x84, 0xfc, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x61, 0x25, 0xfa, 0x28, 0xe1, 0xa7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xfa, 0x68, 0x28, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x30, 0xe4, 0xf9, 0xe8, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xd9, 0xc7, 0xe9, 0xc7, 0xf9, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0xa2, 0xfa, 0x08, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xfa, 0x08, 0x99, 0x45, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0xe1, 0xc7, 0xf1, 0xe7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xe1, 0xc7, 0xfa, 0x08, 0x89, 0x45, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x58, 0xc3, 0xfa, 0x07, 0xe9, 0xc4, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xe9, 0xc5, 0xfa, 0x07, 0xd9, 0x84, 0x30, 0xe2, 
    0x10, 0x82, 0x00, 0x00, 0xd2, 0x34, 0xa9, 0xf0, 0xa1, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa9, 0xf0, 0xa1, 0xef, 0xe2, 0x34, 0xa1, 0xed, 
    0x10, 0x82, 0x00, 0x00, 0x71, 0xb1, 0xa2, 0x16, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x91, 0xf3, 0x89, 0xf3, 0xb2, 0x59, 0x18, 0xe5, 
    0x10, 0x82, 0x00, 0x00, 0x08, 0x20, 0xc2, 0x58, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0xa2, 0x13, 0xa2, 0x13, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x81, 0xaf, 0xaa, 0x15, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0x91, 0xf2, 0xc2, 0x58, 0x20, 0x64, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x58, 0xb9, 0x71, 0xb9, 0x51, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x71, 0xb9, 0x51, 0xb1, 0x51, 0xc9, 0xb3, 0x9a, 0x12, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x0d, 0x4c, 0xff, 0x3b, 0x97, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x3b, 0xb8, 0x54, 0xff, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xff, 0x05, 0xff, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x5c, 0x05, 0x3c, 0x07, 0x3f, 0x03, 0x50, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x3f, 0x05, 0x7e, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x05, 0x1c, 0x05, 0x1c, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x06, 0x5f, 0x04, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x10, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x5f, 0x05, 0xbf, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x05, 0x1c, 0x05, 0xff, 0x06, 0x1f, 0x05, 0xbf, 0x05, 0xbf, 0x05, 0xff, 0x06, 0x1f, 0x05, 0x7f, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x04, 0xfb, 0x05, 0x1b, 0x06, 0x9f, 0x04, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 
    0x08, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1c, 0x06, 0x9f, 0x05, 0x9f, 0x05, 0x3d, 0x05, 0x7f, 0x06, 0x5f, 0x05, 0xbf, 0x02, 0xcf, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa8, 0x04, 0x99, 0x06, 0x5f, 0x06, 0x1f, 0x05, 0x5e, 0x05, 0x3d, 0x06, 0x1f, 0x06, 0x9f, 0x02, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x61, 
    0x21, 0x24, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x11, 0xa8, 0x1b, 0xd4, 0x14, 0x98, 0x13, 0xd4, 0x11, 0xc9, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0xa3, 0x12, 0xad, 0x1c, 0x15, 0x1c, 0x77, 0x1a, 0xef, 0x10, 0xc3, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, 0x21, 0x24
};

.
M5Stack nixie tube clock , DS3231 , RTC , VFD , Nixie tube , ニキシー管 , Hex File

Written by macsbug

6月 16, 2019 at 11:00 am

カテゴリー: M5STACK

M5StickC Nixie tube Clock

with 2 comments

M5StickC で ニキシー管時計を製作しました。    2019.06.06, rev 2019.06.15

ニキシー管は 高電圧が必要である事や 高価である為 製作する機会がありません。
見たい気持ちは大きく ソフトで製作しました。

費用:M5StickC以外の材料費は0円です。
外観:M5StickC の IPS Display は 80×160 で表示サイズは小さいですが
_  明るく綺麗で見やすいです。
機能:M5のスイッチで YYYY,MM,DD, HH,MM,SS 表示と
_  MM,SS 表示に切り換える事ができます。

追記:2019.06.15
Carlos Orts に スケッチがパクられました。
McOrts/M5StickC_Nixie_tube_Clock: Jun 14, 2019
原作者(macsbug) や URLリンク が削除されています。
M5Stack Community / PROJECTS にMcOrts/M5StickC_Nixie_tube_Clockを記載しました。
その中で 3箇所に 原作者(macsbug) や URLリンク削除されている事を記載しました。


.
準備:以下の画像をドラック&ドロップし 画像を入手します。

ニキシー管 画像の製作:元画像 18×34 pixel

.

ニキシー管 画像の製作:元画像 35×67 pixel

.
参考:画像から Hex.c の作成方法は M5Stack CoinMarketCap:2019.05.30 を参照下さい。

⭕ HEX File 変換の準備:
_ png画像を「LCD image Converter 」アプリで hex ( .c ) に変換します。
_ 「LCD image Converter 」は Windowsアプリです。
_ 「lcd-image-converter 20180211-beta」を使用します。
_  メモ:投稿時 最新の20190317 は エラーで動作しません。
_ Mac では「Wine.app」を使用しWindows アプリケーションを動かすことができます。
_ Wine.app Downloads:例 Wine 2.0 を 選択し Download を押します。
_ 約5sec後に「Continue」が表示されますので「Continue」を選択します。
_ 約4sec後に 右上(のアイコン) を選択します。Wine_2.0.dmg 175.6MB が DLされます。
_ 解凍すると Wine.app ができます。
_

⭕ HEX File 変換方法:
1. Wine.app を起動し LCD image Converter を動作させます。
_ 事前に Wine.app や LCD image Converter の動かし方や 配置を把握しておきます。
_ 初めての場合は Winの配置等に慣れない為に時間を要するかと思います。

2. LCD image Converter:
_ File Menu から png 又は Jpg を読み込みます。読み込まれると画像が表示されます。
_ Option Menu を選択します。一番上の Preset: は「Color R5G6B5」を選択します。
_ Image を選択し Block size: は「8 bit」を選択します。「OK」を押します。
_ File Menu にある 「Convert… 」 で 例「vfd_35x67_8.c」が出力されます。
_  事前に読み込み先 ong や 出力先を把握しておきます。
_  全ての画像を読み込み 「Convert… all」で 全部の出力もできます。
_ 

3. .c の編集:そのままではエラーが出ますので ご使用のエディターで修正します。
_ 上位にある 「static const uint8_t image_data_vfd_35x67[4690] = {」は
_  解りやすくする為に名前を編集します。
_ ⭕ 「image_data_ 」を削除し「static const uint8_t vfd_35x67_8[4690] = {」にします。
_ ⭕最下位にある「const tImage vfd_35x67 = { image_data_vfd_35x67, 35, 67, 8 };」は
_   削除します。
_ 例「vfd_35x67_8」を保存します。
_ 同様に 0 から 9 まで実施し 保存します。
_ ⭕ 0 から 9 までのファイルは1つにした方が便利です。「0」の行末の後に
_   1 から 9 までの名前とデーター部を全て「0」の下に追加し 1つのファイルにします。

4. スケッチの作成。
_ 最下位にあるスケッチをコピーして 以下の画像の様に準備し
_ 画像フォント「vfd_18x34.c」 と 「vfd_35x67.c」 もフォルダーに入れます。
_ 
_ 以上で完了です。


.
M5StickC 開発環境:
Arduino IDE 1.8.9

_ 1. Upload Speed : “115200”
_ 1. シリアルモニター:115200 baud:OPEN 又は CLOSE で可能。

_ 2. Upload Speed : “500000”
_ 2. シリアルモニター:500000 baud:OPEN である事。

_ 書込み時に「A fatal error occurred: Timed out waiting for packet header」
_ が発生する場合があります。
_ 速度を速くしたく board.txt を書き換える方法もありますが、
_ 手順の簡単な 115200 baud が無難かと思います。


.
参考:
RTC: I2C BM8563:RTC機能を実現する。アドレス:0x15。
_  M5.Rtc.GetBm8563Time(); : ASCII形式で Rtc.Hour / Rtc.Minute / Rtc.Second
m5stack/m5-docs / system_m5stickc:M5stickC システム。
m5stack/m5-docs / axp192_m5stickc:AXP192 電源管理IC。
m5stack/m5-docs / lcd_m5stickc: IPS Display。
m5stack/m5-docs / sh200q_m5stickc:SH200Q 6 axis inertial sensor。
Virtual nixie tube (digitron) display – DHTML component library:nixie tube の画像データーがあります。
Long-ship:M5Stick-CのRTCをNTPサーバーからセットする:NTPが可能です。


.
感想:
画像の製作:画像処理により飽和したり綺麗でない場合がありますので
_ 手間ですが綺麗な表示になるように編集します。
_ 発光文字部分とニキシー管の背景とのバランスが重要です。
_ ニキシー管の背景が無いと雰囲気出ませんし強調すると全体が白く飽和します。
_ hex.c の編集が面倒でしたら「M5Stack CoinMarketCap」での
_ microSD 方式で 画像を使用する方法も良いかとも思います。
時刻:書込み時に PC の時刻を読み込み ESP内部の RTCに設定しています。
_  NTPは使用していません。
ニキシー管:ニキシー管をなんとか見る事ができて ほっとしています。
_     私もそうですが ニキシー管を好きな人が多い様です。
_     光る数値は前後に配置され数値の部分が重なる所があります。
_     細かく見るとフィラメントが重なる部分は綺麗に表示されないのですね。
github:使用していませんので大きなデータやリストを載せられず申し訳有りません。
M5StickC 価格:M5Stack Official Store = 1799円。$16.64。輸送期間 8日。
_ スイッチサイエンス = 2444円。
_ 10個購入する場合 M5Stack Official Store は 国内販売よりも 6450円お得になります。
M5Stack版:同様に M5Stack FIRE でも表示可能です。
_ M5Stack FIRE Nixie tube Clock:


.
スケッチ:M5StickC Nixie tube Clock: 2019.06.06 macsbug

// M5StickC Nixie tube Clock: 2019.06.06 macsbug
// https://macsbug.wordpress.com/2019/06/06/m5stickc-nixie-tube-clock/
#include <M5StickC.h>
#include "vfd_18x34.c"
#include "vfd_35x67.c"
RTC_TimeTypeDef RTC_TimeStruct;
RTC_DateTypeDef RTC_DateStruct;
int mode_ = 2; // 2: 2line, 1:1line
const uint8_t*n[] = { // vfd font 18x34
  vfd_18x34_0,vfd_18x34_1,vfd_18x34_2,vfd_18x34_3,vfd_18x34_4,
  vfd_18x34_5,vfd_18x34_6,vfd_18x34_7,vfd_18x34_8,vfd_18x34_9
  };
const uint8_t*m[] = { // vfd font 35x67
  vfd_35x67_0,vfd_35x67_1,vfd_35x67_2,vfd_35x67_3,vfd_35x67_4,
  vfd_35x67_5,vfd_35x67_6,vfd_35x67_7,vfd_35x67_8,vfd_35x67_9
  };
const char *monthName[12] = {
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

void setup(void){ 
  M5.begin();
  pinMode(M5_BUTTON_HOME, INPUT);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setRotation(1);
  M5.Axp.ScreenBreath(10);             // 7-15
  // rtc setup start ---------------------------------------------
  String pt = (__DATE__ " " __TIME__); // PC DATE TIME READ
  //000000000011111111112  Read data
  //012345678901234567890
  //Jun  6 2019 07:20:41
  char m1[3]; int  m2; // Month conversion ( Jun to 6 )
  (pt.substring(0,3)).toCharArray(m1,4);
  for (int mx = 0; mx < 12; mx ++) {
    if (strcmp(m1, monthName[mx]) == 0){m2 = mx + 1; break;}
  }
  RTC_DateTypeDef DateStruct;         // Month, Date, Year 
  DateStruct.Month   = m2;
  DateStruct.Date    = (pt.substring(4, 6)).toInt();
  DateStruct.Year    = (pt.substring(7,11)).toInt();
  M5.Rtc.SetData(&DateStruct);
  RTC_TimeTypeDef TimeStruct;         // Hours, Minutes, Seconds 
  TimeStruct.Hours   = (pt.substring(12,14)).toInt();
  TimeStruct.Minutes = (pt.substring(15,17)).toInt();
  TimeStruct.Seconds = (pt.substring(18,20)).toInt();
  M5.Rtc.SetTime(&TimeStruct);
  // rtc setup end -----------------------------------------------
}

void loop(void){ 
  if(digitalRead(M5_BUTTON_HOME) == LOW){
    if (mode_ == 2){mode_ = 1;M5.Lcd.fillScreen(BLACK);return;}
    if (mode_ == 1){mode_ = 2;M5.Lcd.fillScreen(BLACK);return;}
  }
  if ( mode_ == 2 ){ vfd_2_line();}   // yyyy,mm,dd,hh,mm,ss
  if ( mode_ == 1 ){ vfd_1_line();}   // mm,ss
  delay(500);
}

void vfd_2_line(){
  M5.Rtc.GetTime(&RTC_TimeStruct);
  M5.Rtc.GetData(&RTC_DateStruct);
  //Serial.printf("Data: %04d-%02d-%02d\n",RTC_DateStruct.Year,RTC_DateStruct.Month,RTC_DateStruct.Date);
  //Serial.printf("Week: %d\n",RTC_DateStruct.WeekDay);
  //Serial.printf("Time: %02d : %02d : %02d\n",RTC_TimeStruct.Hours,RTC_TimeStruct.Minutes,RTC_TimeStruct.Seconds);
  // Data: 2019-06-06
  // Week: 0
  // Time: 09 : 55 : 26
  int y1 = int(RTC_DateStruct.Year    / 1000 );
  int y2 = int((RTC_DateStruct.Year   - y1*1000 ) / 100 );
  int y3 = int((RTC_DateStruct.Year   - y1*1000 - y2*100 ) / 10 );
  int y4 = int(RTC_DateStruct.Year    - y1*1000 - y2*100 - y3*10 );
  int j1 = int(RTC_DateStruct.Month   / 10);
  int j2 = int(RTC_DateStruct.Month   - j1*10 );
  int d1 = int(RTC_DateStruct.Date    / 10 );
  int d2 = int(RTC_DateStruct.Date    - d1*10 );
  int h1 = int(RTC_TimeStruct.Hours   / 10) ;
  int h2 = int(RTC_TimeStruct.Hours   - h1*10 );
  int i1 = int(RTC_TimeStruct.Minutes / 10 );
  int i2 = int(RTC_TimeStruct.Minutes - i1*10 );
  int s1 = int(RTC_TimeStruct.Seconds / 10 );
  int s2 = int(RTC_TimeStruct.Seconds - s1*10 );
  
  M5.Lcd.pushImage(  0, 0,18,34, (uint16_t *)n[y1]); 
  M5.Lcd.pushImage( 19, 0,18,34, (uint16_t *)n[y2]);
  M5.Lcd.pushImage( 38, 0,18,34, (uint16_t *)n[y3]);
  M5.Lcd.pushImage( 57, 0,18,34, (uint16_t *)n[y4]);
  M5.Lcd.drawPixel( 77,13, ORANGE); M5.Lcd.drawPixel( 77,23,ORANGE);
  M5.Lcd.pushImage( 80, 0,18,34, (uint16_t *)n[j1]);
  M5.Lcd.pushImage( 99, 0,18,34, (uint16_t *)n[j2]);
  M5.Lcd.drawPixel(118,13, ORANGE); M5.Lcd.drawPixel(119,23,ORANGE);
  M5.Lcd.pushImage(120, 0,18,34, (uint16_t *)n[d1]);
  M5.Lcd.pushImage(140, 0,18,34, (uint16_t *)n[d2]);
                                                   
  M5.Lcd.pushImage( 00,40,18,34, (uint16_t *)n[h1]);
  M5.Lcd.pushImage( 20,40,18,34, (uint16_t *)n[h2]);
  M5.Lcd.drawPixel( 48,54, ORANGE); M5.Lcd.drawPixel( 48,64,ORANGE); 
  M5.Lcd.pushImage( 60,40,18,34, (uint16_t *)n[i1]);
  M5.Lcd.pushImage( 80,40,18,34, (uint16_t *)n[i2]);
  M5.Lcd.drawPixel(108,54, ORANGE); M5.Lcd.drawPixel(108,64,ORANGE);
  M5.Lcd.pushImage(120,40,18,34, (uint16_t *)n[s1]);
  M5.Lcd.pushImage(140,40,18,34, (uint16_t *)n[s2]);

  if ( i1 == 0 && i2 == 0 ){ fade();}
}

void vfd_1_line(){
  M5.Rtc.GetTime(&RTC_TimeStruct);
  M5.Rtc.GetData(&RTC_DateStruct);
  int i1 = int(RTC_TimeStruct.Minutes / 10 );
  int i2 = int(RTC_TimeStruct.Minutes - i1*10 );
  int s1 = int(RTC_TimeStruct.Seconds / 10 );
  int s2 = int(RTC_TimeStruct.Seconds - s1*10 );
 
  //M5.Lcd.drawPixel( 48,54, ORANGE); M5.Lcd.drawPixel( 48,64,ORANGE); 
  M5.Lcd.pushImage(  2,6,35,67, (uint16_t *)m[i1]);
  M5.Lcd.pushImage( 41,6,35,67, (uint16_t *)m[i2]);
  //M5.Lcd.drawPixel(108,54, ORANGE); M5.Lcd.drawPixel(108,64,ORANGE);
  M5.Lcd.pushImage( 83,6,35,67, (uint16_t *)m[s1]);
  M5.Lcd.pushImage(121,6,35,67, (uint16_t *)m[s2]);

  if ( s1 == 0 && s2 == 0 ){ fade();}
}

void fade(){
  for (int i=7;i<16;i++){M5.Axp.ScreenBreath(i);delay(25);}
  for (int i=15;i>7;i--){M5.Axp.ScreenBreath(i);delay(25);}
  M5.Axp.ScreenBreath(12);
}

.
M5StickC , VFD , Nixie tube , ニキシー管 , IPS , clock , M5Stack Official Store , Hex File

Written by macsbug

6月 6, 2019 at 5:00 pm

カテゴリー: M5STACK

M5StickC Audio Spectrum Display

leave a comment »

M5StickC で Audio Spectrum Display を製作しました。 2019.06.01


_ 注:gif 画像にて音はでません。


.
準備:
_ 1. Arduino IDE 1.8.9
_ 2. M5StickC:販売:M5Stack Official Store:$11.90 + $4.74 shipping。
_   輸送期間:8日。
_ 3. Audio spectrum:Audio Spectrum Display with M5STACK
_ 4. m5Stack / M5StickC:github
_ 5. M5StickC exsample:Microphone


.
作成:
_ Audio Spectrum Display with M5STACK に M5StickC の Microphone を移植。

_ M5StickC の マイクは SPM1423 I2C を使用しています。
_ 使用 PORT は D : G34 , C : G0 です。
_ マイクは M5 ボタンの下にあります。
_ 特性:iPad の Signal Generator で周波数特性を確認し大凡の表示をしています。
_    雰囲気を感じる程度です。

_ 

_ 


.
補足:
_ tobozo/ESP32-Audio-Spectrum-Waveform-Display を基に移植しました。
_ 今回は ESP32_Spectrum_Display_03.ino 以前のバージョンで制作しています。
_ 周波数領域:8オクターブ
_ バンド:125Hz 250Hz 500Hz 1KHz 2KHz 4KHz 8KHz 16kHz の8つ。
_ マイクと感度:最適な結果を得る為には マイクや感度の考慮が重要です。
_  tobozo氏の例では “DCオフセットなしで約50mV〜100mV pk-pkの
_    Audioを直接ADCポートに供給することです。” と書かれています。
_ 調整:eqBand で設定します。
_    eqBand は freqname, amplitude, peak, lastpeak, lastval, lastmeasured
_    で構成されています。これは オクターブ毎にあります。
_    例:1KHz: { “1KHz”, 200, 0, 0, 0, 0} // 200 は ゲインです。
_      M5StcickCのマイクの特性に合わせて調整してください。

eqBand audiospectrum[8] = {
  // Adjust the amplitude values to fit your microphone
  // freqname,amplitude,peak,lastpeak,lastval,lastmeasured
  { ".1", 900, 0, 0, 0, 0}, 
  { ".2", 500, 0, 0, 0, 0}, 
  { ".5", 200, 0, 0, 0, 0},
  { "1" , 200, 0, 0, 0, 0},
  { "2" , 200, 0, 0, 0, 0},
  { "4" , 100, 0, 0, 0, 0},
  { "8" , 100, 0, 0, 0, 0},
  { "16",  50, 0, 0, 0, 0}
};

_ 最新版の tobozo/ESP32_Spectrum_Display_03.ino では
_    eqBand は freqname, amplitude, bandWidth, peak, lastpeak, curval,
_     lastval, lastmeasured で 6つから8つになっています。
_    例:1KHz:{ “1KHz”, 250, 7, 0, 0, 0, 0, 0}


.
参照:
_ M5StickC docs:概要(電源操作、サポート ボーレート、ピンマップ)
_ github:M5StickC Library:M5StickC ライブラリー
_ M5StickC Audio Spectrum Display : 2019.06.01 : macsbug
_ Audio Spectrum Display with M5STACK : 2017.12.31 : macsbug
_ tobozo/ESP32-Audio-Spectrum-Waveform-Display:ESP32-8-Octave-Audio-Spectrum-Display
_ G6EJD/ESP32-8266-Audio-Spectrum-Display:ESP32-8266-Audio-Spectrum-Display
_ kosme/arduinoFFT
_ M5StickC 使用チップ:ESP32-PICO-D4
_ SPM1423 I2C:仕様。
_ M5StickC exsample:Microphone:M5StickC exsample。
_ hacster.oi:Audio Spectrum Display with M5Stack:2019.04.17 紹介記事。
_ ElectroMagus/M5-FFT:MAX9814 マイクロフォンの例。


.
感想:
M5StickC IPS Display の表示は綺麗です。
M5StickC が 2台有るとステレオで表示する事が出来ます。
M5StickCの注意点:時折 動作しなくなる時があります。故障ではなく慌てない事です。
_ 電源IC AXP192 の設定による物と想像します。
とりあえず表示したく移植し 縦軸と横軸は深い検討をしていません。
輸送期間:M5Stack Official Store は これまでの最短の 8日で到着。
_    $4.74 shipping で Air Mail を使用しています。例:5/8 に注文し 5/16 に到着。
_    輸送力の向上は 販売側と購入者の双方に大きなメリットがあります。
I2C SPM1423 Mic:example の Microphone は 複雑で使用し難いです。
_ Arduino や ESP8266 では analogRead の1行で済みますが I2C Microphone は
_  かなりの行数を使用しています。1行で可能なライブラリーを期待しています。
価格:追記 2019.06.04。 M5Stack Official Store = 1799円。$16.64。SS = 2444円。


.
スケッチ:M5StickC Audio Spectrum Display : 2019.06.01 : macsbug

/* ESP8266/32 Audio Spectrum Analyser on an SSD1306/SH1106 Display
 * The MIT License (MIT) Copyright (c) 2017 by David Bird. 
 * The formulation and display of an AUdio Spectrum using an ESp8266 or ESP32 and SSD1306 or SH1106 OLED Display using a Fast Fourier Transform
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
 * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
 * publish, distribute, but not to use it commercially for profit making or to sub-license and/or to sell copies of the Software or to 
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:  
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 * See more at http://dsbird.org.uk 
*/
// M5StickC Audio Spectrum             : 2019.06.01 : macsbug
//  https://macsbug.wordpress.com/2019/06/01/
// Audio Spectrum Display with M5STACK : 2017.12.31 : macsbug
//  https://macsbug.wordpress.com/2017/12/31/audio-spectrum-display-with-m5stack/
// https://github.com/tobozo/ESP32-8-Octave-Audio-Spectrum-Display/tree/wrover-kit
// https://github.com/G6EJD/ESP32-8266-Audio-Spectrum-Display
// https://github.com/kosme/arduinoFFT

#include "arduinoFFT.h"
arduinoFFT FFT = arduinoFFT();  
#include <M5StickC.h>
#include <driver/i2s.h>
#pragma GCC optimize ("O3")
#define PIN_CLK  0
#define PIN_DATA 34
#define READ_LEN (2 * 1024)
uint8_t BUFFER[READ_LEN] = {0};
uint16_t oldx[160];
uint16_t oldy[160];
uint16_t *adcBuffer = NULL; // uint16_t *adcBuffer = NULL;
#define SAMPLES 512         // Must be a power of 2
#define SAMPLING_FREQUENCY 40000
struct eqBand {
  const char *freqname;
  uint16_t amplitude;
  int peak;
  int lastpeak;
  uint16_t lastval;
  unsigned long lastmeasured;
};

eqBand audiospectrum[8] = {
  // Adjust the amplitude values to fit your microphone
  // freqname,amplitude,peak,lastpeak,lastval,lastmeasured
  {  ".1",1000, 0, 0, 0, 0},
  {  ".2", 500, 0, 0, 0, 0},
  {  ".5", 300, 0, 0, 0, 0},
  {  "1" , 250, 0, 0, 0, 0},
  {  "2" , 200, 0, 0, 0, 0},
  {  "4" , 100, 0, 0, 0, 0},
  {  "8" ,  50, 0, 0, 0, 0},
  { "16" ,  50, 0, 0, 0, 0}
};

unsigned int sampling_period_us;
unsigned long microseconds;
double vReal[SAMPLES];
double vImag[SAMPLES];
unsigned long newTime, oldTime;
uint16_t tft_width  = 160;
uint16_t tft_height =  80;
uint8_t bands = 8;
uint8_t bands_width = floor( tft_width / bands );
uint8_t bands_pad = bands_width - 10;
uint16_t colormap[255];//color palette for the band meter(pre-fill in setup)

void i2sInit(){
   i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
    .sample_rate =  44100,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, //is fixed at 12bit,stereo,MSB
    .channel_format = I2S_CHANNEL_FMT_ALL_RIGHT,
    .communication_format = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 2,
    .dma_buf_len = 128,
   };
   i2s_pin_config_t pin_config;
   pin_config.bck_io_num   = I2S_PIN_NO_CHANGE;
   pin_config.ws_io_num    = PIN_CLK;
   pin_config.data_out_num = I2S_PIN_NO_CHANGE;
   pin_config.data_in_num  = PIN_DATA; 
   i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
   i2s_set_pin(I2S_NUM_0, &pin_config);
   i2s_set_clk(I2S_NUM_0, 44100,I2S_BITS_PER_SAMPLE_16BIT,I2S_CHANNEL_MONO);
}

void mic_record_task (void* arg){      
  while(1){
    i2s_read_bytes(I2S_NUM_0,(char*)BUFFER,READ_LEN,(100/portTICK_RATE_MS));
    adcBuffer = (uint16_t *)BUFFER;
    showSignal();
    vTaskDelay(100 / portTICK_RATE_MS);
  }
}

void setup() {
  M5.begin();
  M5.Lcd.setRotation(1);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(BLUE);
  M5.Lcd.setTextSize(1);
  
  i2sInit();
  xTaskCreatePinnedToCore(mic_record_task,"mic_record_task",2048,NULL,1,NULL,1);

  sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
  delay(2000);
  for(uint8_t i=0;i<tft_height;i++) {
  //colormap[i] = M5.Lcd.color565(tft_height-i*.5,i*1.1,0); //RGB 
    colormap[i] = M5.Lcd.color565(tft_height-i*4.4,i*2.5,0);//RGB:rev macsbug
  }
  for (byte band = 0; band <= 7; band++) {
    M5.Lcd.setCursor(bands_width*band + 2, 0);
    M5.Lcd.print(audiospectrum[band].freqname);
  }
}

void showSignal(){
  for (int i = 0; i < SAMPLES; i++) {
    newTime  = micros() - oldTime;
    oldTime  = newTime;
    vReal[i] = adcBuffer[i];
    vImag[i] = 0;
    while (micros() < (newTime + sampling_period_us)){//do nothing to wait
    }
  }
  
  FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
  FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);

  for (int i = 2; i < (SAMPLES/2); i++){ 
    // Don't use sample 0 and only first SAMPLES/2 are usable. 
    // Each array eleement represents a frequency and its value the amplitude.
    if (vReal[i] > 1500) { // Add a crude noise filter, 10 x amplitude or more
      byte bandNum = getBand(i);
      if(bandNum!=8) {
        displayBand(bandNum, (int)vReal[i]/audiospectrum[bandNum].amplitude);
      }
    }
  }
  long vnow = millis();
  for (byte band = 0; band <= 7; band++) {
    // auto decay every 50ms on low activity bands
    if(vnow - audiospectrum[band].lastmeasured > 50) {
      displayBand(band, audiospectrum[band].lastval>4 ? audiospectrum[band].lastval-4 : 0);
    }
    if (audiospectrum[band].peak > 0) {
      audiospectrum[band].peak -= 2;
      if(audiospectrum[band].peak<=0) {
        audiospectrum[band].peak = 0;
      }
    }
    // only draw if peak changed
    if(audiospectrum[band].lastpeak != audiospectrum[band].peak) {
      // delete last peak
     M5.Lcd.drawFastHLine(bands_width*band,tft_height-audiospectrum[band].lastpeak,bands_pad,BLACK);
     audiospectrum[band].lastpeak = audiospectrum[band].peak;
     M5.Lcd.drawFastHLine(bands_width*band, tft_height-audiospectrum[band].peak,
                          bands_pad, colormap[tft_height-audiospectrum[band].peak]);
    }
  } 
}

void displayBand(int band, int dsize){
  uint16_t hpos = bands_width*band;
  int dmax = 200;
  if(dsize>tft_height-10) {
    dsize = tft_height-10; // leave some hspace for text
  }
  if(dsize < audiospectrum[band].lastval) {
    // lower value, delete some lines
    M5.Lcd.fillRect(hpos, tft_height-audiospectrum[band].lastval,
                    bands_pad, audiospectrum[band].lastval - dsize,BLACK);
  }
  if (dsize > dmax) dsize = dmax;
  for (int s = 0; s <= dsize; s=s+4){
    M5.Lcd.drawFastHLine(hpos, tft_height-s, bands_pad, colormap[tft_height-s]);
  }
  if (dsize > audiospectrum[band].peak){audiospectrum[band].peak = dsize;}
  audiospectrum[band].lastval = dsize;
  audiospectrum[band].lastmeasured = millis();
}

byte getBand(int i) {
  if (i<=2 )             return 0;  // 125Hz
  if (i >3   && i<=5 )   return 1;  // 250Hz
  if (i >5   && i<=7 )   return 2;  // 500Hz
  if (i >7   && i<=15 )  return 3;  // 1000Hz
  if (i >15  && i<=30 )  return 4;  // 2000Hz
  if (i >30  && i<=53 )  return 5;  // 4000Hz
  if (i >53  && i<=200 ) return 6;  // 8000Hz
  if (i >200           ) return 7;  // 16000Hz
  return 8;
}

void loop() {}

.
M5StickC , Audio , Spectrum , M5Stack Official Store , ESP32-PICO-D4 , SPM1423 ,

Written by macsbug

6月 1, 2019 at 10:00 am

カテゴリー: M5STACK

M5Stack CoinMarketCap

leave a comment »

M5Stack で 仮想通貨の時価総額 ( CoinMarketCap ) を表示しました。 ORG:2019.05.26, rev:2019.05.30

.
CoinMarketCap ( 仮想通貨の時価総額 ) は リアルタイムに 数値とグラフを見る事ができます。
bitcoin, cardano, ethereum, litecoin, xrp(ripple) の5つを M5Stack に表示します。
オリジナルは mnett0氏の ESP32+ILI9341「CryptoMoneyTicker」です。mnett0氏に感謝!
この「CryptoMoneyTicker」を TTGO が TTGO-T4-DEMO 「T4_9341_NEW」に移植。
そして「TTGO-T4-DEMO」を基に M5Stack用に移植致しました。


.
手順:
1. 開発環境:Arduino IDE。
2. ArduinoJson を Arduino IDE の Library へ Install します。
_ version は 5 を使用します。ArduinoJson-5.13.5。注:version 6 では 動作しません。
3. 最下位にある スケッチ「M5Stack_CoinMarketCap」をコピーし
_ 「M5Stack_CoinMarketCap.ino」Folder を 作ります。
4. 以下の8つの表示画像を 用意し、SD File か HEX File か どちらかで行ないます。
_ 
_ 画像表示の為に 画像を microSD に入れて使用するか、HEX File に変換し
_ 「M5Stack_CoinMarketCap.ino」 Folder に入れます。
_ Free_Fonts.h は M5Stack/examples/Advanced/Display/Free_Font_Demo/
_ にありますので DL してください。
 
_ ⭕ SD File の場合:
_  
_  microSDに 「coin_image」 Folder を作り 以下の表示画像( jpg ) を入れます。
.
表示画像:

bitcoin_.jpg cardano_.jpg ethereum_.jpg litecoin_.jpg xrp_.jpg

.

wifi_1.jpg wifi_2.jpg wifi_3.jpg

.
_ ⭕ HEX Fileの場合:
_  
_  1. 8つの表示画像( jpg ) を 変換し DL(ダウンロード)します。事例:bitcoin_.jpg
_  2. ブラウザーで「PicturetoC_Hex_converter」に接続し jpg を hex に変換します。
_  3. your の選択ボタンで DL した File を選択します。
_  4. width は 「64」, Height は 「64」 を入力。
_  5. User for は 「256 color byte/pixel:RRRGGGBB, for EDM1」を選択します。
_  6. 「Get C String」ボタンを押すと Image data: が作成されます。
_  7. Image data (0xff 等) をコピーし 「bitcoin_.c」File を作ります。
_   ( 左下の「Download data」を選択し DL する方法は DL した bin を変換しますが
_    この方法は 変換方法が解らず 説明は省きます。)
_  8. 8つの Hex File を 「M5Stack_CoinMarketCap.ino」 Folder に入れます。
_

4. Arduino IDE で M5Stackに書き込みます。


.
追記:2019.05.30
⭕ HEX File 変換:その2:「LCD image Converter 」アプリで変換する。
_ 「LCD image Converter 」は Windowsアプリです。
_ 「lcd-image-converter 20180211-beta」を使用します。
_  メモ:投稿時 最新の20190317 は エラーで動作しません。
_ Mac では「Wine.app」を使用しWindows アプリケーションを動かすことができます。
_ Wine.app Downloads:例 Wine 2.0 を 選択し Download を押します。
_ 約5sec後に「Continue」が表示されますので「Continue」を選択します。
_ 約4sec後に 右上(のアイコン) を選択します。Wine_2.0.dmg 175.6MB が DLされます。
_ 解凍すると Wine.app ができます。
_

HEX File 変換方法:
1. Wine.app を起動し LCD image Converter を動作させます。
_ 事前に Wine.app や LCD image Converter の動かし方や 配置を把握しておきます。
_ 初めての場合は Winの配置等に慣れない為に時間を要するかと思います。
2. LCD image Converter:
_ File Menu から png 又は Jpg を読み込みます。読み込まれると画像が表示されます。
_ Option Menu を選択します。一番上の Preset: は「Color R5G6B5」を選択します。
_ Image を選択し Block size: は「8 bit」を選択します。「OK」を押します。
_ File Menu にある 「Convert… 」 で 例「bitcoin_.c」が出力されます。
_  事前に読み込み先 ong や 出力先を把握しておきます。
3. .c の編集:そのままではエラーが出ますので修正します。
_ 最下位にある 「const tImage ethereum_ = ,,, }; 」は コメントアウト「//)」します。
_ 上にある 例「static const uint8_t image_data_bitcoin_[8192] = {」は
_  名前の「image_data_」を削除すると短く見やすくなります。
_  「static const uint8_t bitcoin_[8192] = {」に変更します。
_ これにより 例「bitcoin_.c」を作成する事ができます。
_ 


.
移植メモ:
1. オリジナルの 「CryptoMoneyTicker」:
_ ログを CSV File で SDに保存できる。
_ ボタン操作で 表示を選択できる。
_ 表示の為の画像(bitmap.c) は そのままでは M5Stack に使用できない。

2. TTGO の TTGO-T4-DEMO 「T4_9341_NEW」:
_ ログを CSV File で SDに保存できない。
_ ボタン操作は 無し。
_ 表示の為の画像(bitmap.c) は そのままでは M5Stack に使用できない。

3. 移植の課題と対策:
_ 画像の問題:
_ 「CryptoMoneyTicker」や「T4_9341_NEW」にある画像 bitmap.c は
_  M5Stack では使用できません。さらに これまでの Arduino等にある HEX画像も
_  そのままでは使用できません。
_  理由は Display の表示に関し RGB の配置変更(BGR)や鏡面反転している為です。
_  素直に RGB 配置にすれば これまでの資産がスムーズに使用できますが
_  配置変更をした為 都度 画像の変換や 新規作成が必要となります。
_  これは 手間と時間を要し M5Stackの発展を妨げます。
_  参照:TFT_eSPI Library supports M5STACK
_  PC環境やOSにより 画像フォーマットが変化し 動作しない場合が考えられます。
_  OS環境により 不可視ファイルが出来る場合があります。

_ 画像の対策:
_ 1. M5Stack で使用可能な Jpg File を microSDに保存して使用する。
_  スケッチ例:M5.Lcd.drawJpgFile(SD,”/coin_image/bitcoin_.jpg”,0,0,64,64);
_ 2. ブラウザーで使用可能な png to hex 又は jpg to hex を使用し hex を作成する。
_  png to hex が可能で 「256 color byte/pixel:RRRGGGBB, for EDM1」の選択が
_  必要となります。この変換データーは大きくなりますが使用できます。
_  他に可能な方法があるかと思いますが 現在知り得る方法です。
_  スケッチ例:M5.Lcd.pushImage(0, 0, 64, 64, (uint8_t *)bitcoin_);
_  それにしても 簡単な変換方法を探していますが なかなか思うように行きません。

_ 今回の機能:
_ 1. 画像は SD と hex 両方が使用出来る記述にしました。
_  必要に応じて 不要な場所は 削除や変更をしてください。
_ 2. ログの SD保存 は 特に必要なく追加しませんでした。
_  グラフに興味がある場合は SD へ CSV 保存すると便利になるかと思います。
_ 3. ボタンスイッチによる 表示の切り替えは無しにしました。
_  繰り返し表示の時間設定は 18行目の interval = 60000; です。
_  必要に応じて 数値を変更してください。
_  マニュアルで画面切り替えが 便利な場合は スイッチ操作を加えると
_  便利になるかと思います。


.
参照:
M5StickC docs:概要(電源操作、サポート ボーレート、ピンマップ)
github:M5StickC Library:M5StickC ライブラリー
Market Capitalization:データーのソース基。
CryptoMoneyTicker:オリジナル版 mnett0氏のページ。
TTGO-T40DEMO:TTGOのデモ
png to hex:Hex変換。
png to jpg:jpg変換。


.
感想:
仮想通貨は使用していませんので必要ありませんが、TTGO のサンプルの
表示が綺麗でなく M5Stack では どうなるか試してみました。

画像処理:M5Stack用の画像準備には時間がかかります。
_ もう少し簡単に出来る方法は無いかと模索しましたが 私の力量では
_ この位かと思います。
_ png は 透明処理が可能ですが M5Stack上ではマスター出来ませんでした。
_ よって 左上のアイコン画像は 背景部を透明にして試しましたが 表示
_ できない為に 背景を白で仕上げました。

当初 TTGO製のスケッチかと思いましたが mnett0氏の作品である事が解りました。
_ CryptoMoneyTicker_LITE ( ESP8266 + OLED ) もあり 小型でケースも出来ています。

勉強:
M5Stack の Github には Graphic のサンプルがあり 基本的な使用方法が あります。
今回の移植後に気づき、サンプルを先にマスターしておくべきだったかと 思っています。
 1. drawXBitmap/xbm.h:モノクロ画像を扱う。
 2. TFT_Flash_Bitmap:カラー画像を扱う。


.
スケッチ:M5Stack_CoinMarketCap : 2019.05.26 Transplant by macsbug

/*
The MIT License (MIT)

Copyright © 2018 Médéric NETTO

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// M5Stack_CoinMarketCap : 2019.05.26 Transplant by macsbug
// https://macsbug.wordpress.com/2019/05/26/m5stack-coinmarketcap/
// https://coinmarketcap.com // Market Capitalization
// https://github.com/mnett0/CryptoMoneyTicker // CryptoMoneyTicker
// https://github.com/LilyGO/TTGO-T4-DEMO // TTGO-T40DEMO
// https://www.digole.com/tools/PicturetoC_Hex_converter.php // png to hex
// https://onlinejpgtools.com/convert-png-to-jpg // png to jpg
#include <M5Stack.h>
#include "M5StackUpdater.h"
#include "Free_Fonts.h"
#include <driver/dac.h>
#include <ArduinoJson.h> // with ArduinoJson 5.xx
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <TimeLib.h>
#define SD_ // with SD File
#if defined(SD_)
#else
  #include "bitcoin_.c"
  #include "cardano_.c"
  #include "ethereum_.c"
  #include "litecoin_.c"
  #include "xrp_.c" // ripple
  #include "wifi_1.c"
  #include "wifi_2.c"
  #include "wifi_3.c"
#endif
const char* ssid = "your wifi ssid";
const char* password = "your wifi password";
const char host[] = "api.coinmarketcap.com";
int COLOR;
#define CUSTOM_DARK 0x4228 // Background color
unsigned long previousMillis = 0;
long interval = 0;
int coin = 0;
String crypto[] = {"bitcoin", "cardano", "ethereum", "litecoin", "ripple"};
String oldPrice[5];

void setup() {
  M5.begin();
  Wire.begin();if(digitalRead(39)==0){updateFromFS(SD);ESP.restart();}
  dac_output_disable(DAC_CHANNEL_1); // DAC OFF
  M5.Lcd.setRotation(0);
  M5.Lcd.fillScreen(CUSTOM_DARK); 
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextWrap(true);
  M5.Lcd.setCursor(0, 170);
  M5.Lcd.setFreeFont(FM9); 
  M5.Lcd.println(">>> Connecting to: ");
  M5.Lcd.println(" ");
  M5.Lcd.println(ssid);
  Serial.println("start");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    #if defined(SD_)
      M5.Lcd.drawJpgFile(SD,"/coin_image/wifi_3.jpg",56,64,128,128);delay(250);
      M5.Lcd.drawJpgFile(SD,"/coin_image/wifi_2.jpg",56,64,128,128);delay(250);
      M5.Lcd.drawJpgFile(SD,"/coin_image/wifi_1.jpg",56,64,128,128);delay(250);
    #else
      M5.Lcd.pushImage(56, 64, 128, 128, (uint8_t *)wifi_3);delay(250);
      M5.Lcd.pushImage(56, 64, 128, 128, (uint8_t *)wifi_2);delay(250);
      M5.Lcd.pushImage(56, 64, 128, 128, (uint8_t *)wifi_1);delay(250);
      M5.Lcd.fillRect (56, 64, 128, 128, CUSTOM_DARK);
    #endif
  }
  //Serial.println("start");
  M5.Lcd.println(" ");
  M5.Lcd.print(">>> WiFi connected");
  //M5.Lcd.print("IP address: ");
  //M5.Lcd.println(WiFi.localIP());

  delay(1500);
  M5.Lcd.fillScreen(CUSTOM_DARK);
  M5.Lcd.setTextColor(YELLOW);
  M5.Lcd.setCursor(0, 150);
  M5.Lcd.setFreeFont(FM9);
  M5.Lcd.println("CryptoMoneyTicker_v1");
  M5.Lcd.println("porting : 2019.05.26");
  M5.Lcd.drawLine(0,130,240,130, WHITE);
  M5.Lcd.drawLine(0,185,240,185, WHITE);

  M5.Lcd.setFreeFont(FM9);
  M5.Lcd.setCursor(5, 307);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.println("Data from: CoinMarketCap.com");
}

void loop() {
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setCursor(1, 10);
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    interval = 60000; //  <<<--------------------  
    if(coin == 5 ){ coin = 0; }
  //Serial.print(">>> Connecting to ");
  //Serial.println(host);
  WiFiClientSecure client;
  const int httpsPort = 443;
  if (!client.connect(host, httpsPort)) {
    M5.Lcd.fillScreen(CUSTOM_DARK);
    M5.Lcd.println(">>> Connection failed");
    return;
  }
  //Serial.print("Requesting URL: ");
  //Serial.println("Connected to server!");
  client.println("GET /v1/ticker/" + crypto[coin] + "/ HTTP/1.1");
  client.println("Host: api.coinmarketcap.com");
  client.println("Connection: close");
  client.println();

  unsigned long timeout = millis();
  while (client.available() == 0) {
    if (millis() - timeout > 5000) {
      M5.Lcd.fillScreen(CUSTOM_DARK);
      M5.Lcd.println(">>> Client Timeout!");
      client.stop();
      return;
    }
  }

  String data;
  while(client.available()) {
    data = client.readStringUntil('\r');
    //Serial.println(data);
  }
  data.replace('[', ' ');
  data.replace(']', ' ');
  char buffer[data.length() + 1];
  data.toCharArray(buffer, sizeof(buffer));
  buffer[data.length() + 1] = '\0';
  const size_t bufferSize = JSON_OBJECT_SIZE(15) + 110;
  DynamicJsonBuffer jsonBuffer(bufferSize);
  JsonObject& root = jsonBuffer.parseObject(buffer);
  if (!root.success()) {
    M5.Lcd.println("parseObject() failed");
    return;
  }

  String name = root["name"];                          // "Bitcoin"
  String symbol = root["symbol"];                      // "BTC"
  String price_usd = root["price_usd"];                // "573.137"
  String percent_change_1h = root["percent_change_1h"];// "0.04"
  String last_updated = root["last_updated"];          // "1472762067"<--Unix Time Stamp
  String error = root["error"];                        // id not found

  printTransition();

  switch (coin) {
  case 0 : // Bitcoin
  #if defined(SD_)
    M5.Lcd.drawJpgFile(SD,"/coin_image/bitcoin_.jpg",0,0,64,64);
  #else
    M5.Lcd.pushImage(0, 0, 64, 64, (uint8_t *)bitcoin_);
  #endif
  printName(name, symbol);
  printPrice(price_usd);
  printChange(percent_change_1h);
  printTime(last_updated);
  printPagination();
  printError(error);
  M5.Lcd.fillCircle(82, 300, 8, WHITE);
  break;

  case 1 : // cardano
  #if defined(SD_)
    M5.Lcd.drawJpgFile(SD,"/coin_image/cardano_.jpg",0,0,64,64);
  #else
    M5.Lcd.pushImage(0, 0, 64, 64, (uint8_t *)cardano_);
  #endif
  printName(name, symbol);
  printPrice(price_usd);
  printChange(percent_change_1h);
  printTime(last_updated);
  printPagination();
  printError(error);
  M5.Lcd.fillCircle(100, 300, 8, WHITE);
  break;

  case 2 : // ethereum
  #if defined(SD_)
    M5.Lcd.drawJpgFile(SD,"/coin_image/ethereum_.jpg",0,0,64,64);
  #else
    M5.Lcd.pushImage(0, 0, 64, 64, (uint8_t *)ethereum_);
  #endif
  printName(name, symbol);
  printPrice(price_usd);
  printChange(percent_change_1h);
  printTime(last_updated);
  printPagination();
  printError(error);
  M5.Lcd.fillCircle(118, 300, 8, WHITE);
  break;

  case 3 : // litecoin
  #if defined(SD_)
    M5.Lcd.drawJpgFile(SD,"/coin_image/litecoin_.jpg",0,0,64,64);
  #else
    M5.Lcd.pushImage(0, 0, 64, 64, (uint8_t *)litecoin_);
  #endif
  printName(name, symbol);
  printPrice(price_usd);
  printChange(percent_change_1h);
  printTime(last_updated);
  printPagination();
  printError(error);
  M5.Lcd.fillCircle(136, 300, 8, WHITE);
  break;

  case 4 : // ripple ( xrp )
  #if defined(SD_)
    M5.Lcd.drawJpgFile(SD,"/coin_image/xrp_.jpg",0,0,64,64);
  #else
    M5.Lcd.pushImage(0, 0, 64, 64, (uint8_t *)ripple_);
  #endif
  printName(name, symbol);
  printPrice(price_usd);
  printChange(percent_change_1h);
  printTime(last_updated);
  printPagination();
  printError(error);
  M5.Lcd.fillCircle(154, 300, 8, WHITE);
  break;
  }
  oldPrice[coin] = price_usd;
  coin++;
  }
}

void printName(String name, String symbol) {
  M5.Lcd.setFreeFont(FM12);
  M5.Lcd.setCursor(75, 15);
  M5.Lcd.println(name);
  M5.Lcd.setFreeFont(FM12);
  M5.Lcd.setCursor(75, 42);
  M5.Lcd.print(symbol);
  M5.Lcd.drawLine(75, 54, 240, 54, WHITE);
}

void printPrice(String price_usd) {
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setFreeFont(FM12);
  M5.Lcd.setCursor(5, 85);
  M5.Lcd.print("Price:");
  if(price_usd != oldPrice[coin]){
    if(price_usd > oldPrice[coin]){
    COLOR = GREENYELLOW;
    }else{
    COLOR = RED;
    }
  }
  M5.Lcd.setFreeFont(FMB18);
  M5.Lcd.setTextColor(COLOR);
  M5.Lcd.setCursor(20, 126);
  M5.Lcd.print("$");
  M5.Lcd.println(price_usd.substring(0,8));
}

void printChange(String percent_change_1h) {
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setFreeFont(FM12);
  M5.Lcd.setCursor(5, 160);
  M5.Lcd.print("Change(1h):");
  if(percent_change_1h >= "0"){
    M5.Lcd.setTextColor(GREENYELLOW);
    }else{
    M5.Lcd.setTextColor(RED);
  }
  M5.Lcd.setFreeFont(FMB18);
  M5.Lcd.setCursor(20, 199);
  M5.Lcd.print(percent_change_1h);
  M5.Lcd.print("%");
}

void printTime(String last_updated) {
  long int timeData = last_updated.toInt();
  time_t t = timeData;
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setFreeFont(FM12);
  M5.Lcd.setCursor(5, 230);
  M5.Lcd.print("Last Updated:");
  M5.Lcd.setFreeFont(FMB18);
  M5.Lcd.setCursor(20, 266);
  /*
  M5.Lcd.setFreeFont(FM9);
  printDigits(day(t));  M5.Lcd.print("/");
  printDigits(month(t));M5.Lcd.print("/");
  M5.Lcd.print(year(t));M5.Lcd.print(" ");
  */
  printDigits(hour(t) + 1); // +1 for the French time
  M5.Lcd.print(":");
  printDigits(minute(t));
  //M5.Lcd.print(":");
  //printDigits(second(t));
}

void printPagination() {
  M5.Lcd.drawCircle( 82, 300, 8, WHITE);
  M5.Lcd.drawCircle(100, 300, 8, WHITE);
  M5.Lcd.drawCircle(118, 300, 8, WHITE);
  M5.Lcd.drawCircle(136, 300, 8, WHITE);
  M5.Lcd.drawCircle(154, 300, 8, WHITE);
}

void printError(String error) {
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setFreeFont(FM9);
  M5.Lcd.setCursor(65, 22);
  M5.Lcd.println(error);
}

void printTransition(){
  M5.Lcd.fillScreen(CUSTOM_DARK);
  M5.Lcd.fillScreen(RED);
  M5.Lcd.fillScreen(GREEN);
  M5.Lcd.fillScreen(BLUE);
  M5.Lcd.fillScreen(CUSTOM_DARK);
}

void printDigits(int digits) {
 if (digits < 10)  // prints preceding colon and leading 0
 M5.Lcd.print('0');
 M5.Lcd.print(digits);
}

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
M5Stack , CoinMarketCap , RGB , GBR png to hex , png to jpg , macsbug , TTGO ,
HEX , HEX File 変換 , LCD image Converter , Wine , Wine_2.0 ,

Written by macsbug

5月 26, 2019 at 12:00 pm

カテゴリー: M5STACK

M5StickC 3D COLOR CUBE

leave a comment »

M5StickC に 3D COLOR CUBE を表示しました。      2019.05.20

M5StickC の表示は 0.96″ 80×160 IPS (In Plane Switching) SPI ST7735S Display です。
色は 65K フルカラー ( 16bit color )です。
上下左右178度の広視野角、どの位置で見ても色の変化は ほとんどありません。

斜めの視野:明るく綺麗で見やすいです。


.
作成:
記事の最下位にある スケッチを Arduino IDE で ボード選択 M5StcicK-C で書き込みます。
_ スケッチ詳細:
_ 1. CUBE のサイズ:9行目の #define U 70 // size of cube で 数値を変更します。
_ 2. 回転速度:251 行目の delay(50); // Rotational speed で 50msecです。
_   回転速度の変更:delay の数値を変更してください。
_ 3. SOLID表示と WIREFRAME表示:選択は 5行目の #define WIREFRAME を
_   コメント(//) を入れるか 入れないかで 表示が変わります。
_ 4. M5Stack 用に 使用する時は // In the case of M5Stack と書かれているコメントを
_   削除してください。#include M5StickC.h は コメントアウトします。
_   237行目は SD_UPDATE 用です。修正は 合計 4箇所です。


.
参考:
M5StickC docs:概要(電源操作、サポート ボーレート、ピンマップ)
github:M5StickC Library:M5StickC ライブラリー
blog:3D CUBE with ESP8266 and OLED:2016.01.16:モノクロの立方体がグイグリ回ります。
blog:I2C OLED 128×64 SSD1306 in ESP8266:2016.03.20:ESP8266 と SSD1306。
blog:Using the WeMos I2C OLED 64×48:2016.04.01:WeMos I2C OLED 64×48 の使用例。
blog:Part 2 of the FM radio in the ESP8266:2016.04.06:cubeをradio displayに使用。
blog:Using the TFT LCD display in the ESP8266:2016.04.16:ESP8266 + TFT SPI ILI9341
blog:ESP32 Adapter Board and TFT Display:2017.02.11:ESP32 + 1.44″ TFT v2.1
LilyGO/TTGO-TS: TTGO-TS/ESP32_1.44/ESP32_1.44.ino:2018.08.11 確認:TTGOに使用される。
blog:Run IPS SPI display on M5Stack:2019.03.24:65K フルカラーです。
Adafruit:Adafruit Mini TFT – 0.96″ 160×80


.
M5Stack Basic:M5Stack Basic用に記載し表示しました。


.
感想:
IPSカラーディスプレーに CUBE を表示したく 作成しました。
M5StickCの動作確認やデモに 良いかと思います。
0.96 inch 128 x64 OLED のデモと同じく CUBE を表示しました。
立方体を2次元表示する為に 3つの四辺形で構成されます。
四辺形は 三角形を3つ抽画しています。

メモ:
_ チラツキ:スケッチに工夫が必要かと思います。
_  CUBE 抽画に M5.Lcd.drawTriangle を 9回(3×3)使用し時間がかかっています。
_  252行目の M5.Lcd.fillRect(x_min,y_min,x_max-x_min+3,y_max-y_min+3,BLACK);
_  は CUBE 全体を消し 時間がかかっています。
_ M5StickC:動作しなくなる時がある。
_  1. 突然動作せず、画面が黒いままになる。
_  2. USBは認識し書込みも出来るが、動作しない。set_up が動作していない。
_   起動ボタンを押したり USB CABLE を抜き差ししたり すると動作する事がある。
_   原因不明ですので 壊れたとか あわてない事です。
_  3. 電源管理チップ AXP192 の設定かと思われます。
_   jimmy Lai氏の情報:
_    There is problem from the setting for axp192, need to set a higher volt level for shutdown.
_    The early version test firmware’s bug.


.
スケッチ:M5StickC 3D COLOR CUBE : 2019.05.20 macsbug

// M5StickC 3D COLOR CUBE : 2019.05.20 macsbug
#include <M5StickC.h>          // In the case of M5StickC
// #include <M5Stack.h>        // In the case of M5Stack
// #include "M5StackUpdater.h" // In the case of M5Stack
//#define WIREFRAME   // WIREFRAME or SOLID Display
#pragma GCC optimize ("O3")
struct p3d{ int16_t x, y, z;};
struct surface{ uint8_t p[4]; int16_t z;};
struct p2d{ int16_t x, y; unsigned is_visible;};
#define U  70         // size of cube
#define ZS U          // eye to screen distance (fixed)

struct p3d cube[8] ={ // cube edge length is 2*U
  { -U, -U,  U },
  {  U, -U,  U },
  {  U, -U, -U },
  { -U, -U, -U },
  { -U,  U,  U },
  {  U,  U,  U },
  {  U,  U, -U },
  { -U,  U, -U },
};

struct surface s[6] = {// define the surfaces
  { {0, 1, 2, 3}, 0 }, // bottom
  { {4, 5, 6, 7}, 0 }, // top
  { {0, 1, 5, 4}, 0 }, // back
  { {3, 7, 6, 2}, 0 }, // front
  { {1, 2, 6, 5}, 0 }, // right
  { {0, 3, 7, 4}, 0 }, // left
};

struct p3d cube2[8];   // Define the structure of the cube,
struct p2d cube_[8];   // calculation will be done there

int16_t x_min, x_max;  // will contain a rectangle border
int16_t y_min, y_max;  //  of the box projection into 2D plane.

const int16_t sin_tbl[65] = {
       0,  1606,  3196,  4756,  6270,  7723,  9102, 10394, 
   11585, 12665, 13623, 14449, 15137, 15679, 16069, 16305,
   16384, 16305, 16069, 15679, 15137, 14449, 13623, 12665,
   11585, 10394,  9102,  7723,  6270,  4756,  3196,  1606, 
       0, -1605, -3195, -4755, -6269, -7722, -9101,-10393,
  -11584,-12664,-13622,-14448,-15136,-15678,-16068,-16304,
  -16383,-16304,-16068,-15678,-15136,-14448,-13622,-12664,
  -11584,-10393, -9101, -7722, -6269, -4755, -3195, -1605, 
       0
};

const int16_t cos_tbl[65] = {
   16384, 16305, 16069, 15679, 15137, 14449, 13623, 12665, 
   11585, 10394,  9102,  7723,  6270,  4756,  3196,  1606,
       0, -1605, -3195, -4755, -6269, -7722, -9101,-10393,
  -11584,-12664,-13622,-14448,-15136,-15678,-16068,-16304,
  -16383,-16304,-16068,-15678,-15136,-14448,-13622,-12664,
  -11584,-10393, -9101, -7722, -6269, -4755, -3195, -1605,
       0,  1606,  3196,  4756,  6270,  7723,  9102, 10394,
   11585, 12665, 13623, 14449, 15137, 15679, 16069, 16305,
   16384
};

void copy_cube(void){
  uint8_t i;
  for (i = 0; i < 8; i++){
    cube2[i] = cube[i];
  }
}

void rotate_cube_x(uint16_t w){
  uint8_t i;
  int16_t y, z;
  for (i = 0; i < 8; i++){
    y = ( (int32_t)cube2[i].y * (int32_t)cos_tbl[w] + 
          (int32_t)cube2[i].z * (int32_t)sin_tbl[w]) >> 14;
    z = (-(int32_t)cube2[i].y * (int32_t)sin_tbl[w] + 
          (int32_t)cube2[i].z * (int32_t)cos_tbl[w]) >> 14;
    cube2[i].y = y;
    cube2[i].z = z;
  }
}

void rotate_cube_y(uint16_t w){
  uint8_t i;
  int16_t x, z;
  for (i = 0; i < 8; i++){
    x = ( (int32_t)cube2[i].x * (int32_t)cos_tbl[w] +
          (int32_t)cube2[i].z * (int32_t)sin_tbl[w]) >> 14;
    z = (-(int32_t)cube2[i].x * (int32_t)sin_tbl[w] + 
          (int32_t)cube2[i].z * (int32_t)cos_tbl[w]) >> 14;
    //printf("%d: %d %d --> %d %d\n",i,cube2[i].x,cube2[i].z,x,z);
    cube2[i].x = x;
    cube2[i].z = z;
  }
}

void trans_cube(uint16_t z){
  uint8_t i;
  for (i = 0; i < 8; i++){
    cube2[i].z += z;
  }
}

void reset_min_max(void){
  x_min =  0x07fff;
  y_min =  0x07fff;
  x_max = -0x07fff;
  y_max = -0x07fff;
}

// calculate xs and ys from a 3d value
void convert_3d_to_2d(struct p3d *p3, struct p2d *p2){
  int ws = M5.Lcd.width();
  int hs = M5.Lcd.height();
  int32_t t;
  p2->is_visible = 1;
  if (p3->z >= ZS){
    t = ZS;
    t *= p3->x;
    t <<= 1;
    t /= p3->z;
    if (t >= -(ws/2) && t <= (ws/2) - 1){
      t += (ws/2);
      p2->x = t;
      if (x_min > t) x_min = t;
      if (x_max < t) x_max = t;
      t = ZS;
      t *= p3->y;
      t <<= 1;
      t /= p3->z;
      if (t >= -(hs/2) && t <= (hs/2) - 1){
        t += (hs/2);
        p2->y = t;
        if (y_min > t) y_min = t;
        if (y_max < t) y_max = t;
      }else{
        p2->is_visible = 0;
      }
    }else{
      p2->is_visible = 0;
    }
  }else{
    p2->is_visible = 0;
  }
}

void convert_cube(void){
  uint8_t i;
  reset_min_max();
  for (i = 0; i < 8; i++){
    convert_3d_to_2d(cube2 + i, cube_ + i);
  }
}

void calculate_z(void){
  uint8_t  i, j;
  uint16_t z;
  for (i = 0; i < 6; i++){
    z = 0;
    for (j = 0; j < 4; j++){
      z += cube2[s[i].p[j]].z;
    }
    z /= 4;
    s[i].z = z;
    //printf("%d: z=%d\n", i, z);
  }
}

void draw_cube(void){
  uint8_t i, ii;
  uint8_t skip_cnt = 3; // first 3 surfaces are invisible
  int16_t z, z_upper;
  uint16_t color;
  z_upper = 32767;
  for (;;){
    ii = 6;
    z = -32767;
    for (i = 0; i < 6; i++){
      if (s[i].z <= z_upper){
        if (z < s[i].z){
          z = s[i].z;
          ii = i;
        }
      }
    }
    if (ii >= 6) break;
    z_upper = s[ii].z;
    s[ii].z++;
    if (skip_cnt > 0){
      skip_cnt--;
    }else{
      color = M5.Lcd.color565((uint8_t)((( ii + 1) &  1)      * 255),
                              (uint8_t)((((ii + 1) >> 1) & 1) * 255),
                              (uint8_t)((((ii + 1) >> 2) & 1) * 255));
      #if defined(WIREFRAME) // WIREFRAME
        M5.Lcd.drawTriangle(cube_[s[ii].p[0]].x, cube_[s[ii].p[0]].y,
                            cube_[s[ii].p[1]].x, cube_[s[ii].p[1]].y,
                            cube_[s[ii].p[2]].x, cube_[s[ii].p[2]].y,
                            color);
        M5.Lcd.drawTriangle(cube_[s[ii].p[2]].x, cube_[s[ii].p[2]].y,
                            cube_[s[ii].p[3]].x, cube_[s[ii].p[3]].y,
                            cube_[s[ii].p[0]].x, cube_[s[ii].p[0]].y,
                            color);
        M5.Lcd.drawTriangle(cube_[s[ii].p[1]].x, cube_[s[ii].p[1]].y,
                            cube_[s[ii].p[2]].x, cube_[s[ii].p[2]].y,
                            cube_[s[ii].p[3]].x, cube_[s[ii].p[3]].y,
                            color);
      #else // SOLID
        M5.Lcd.fillTriangle(cube_[s[ii].p[0]].x, cube_[s[ii].p[0]].y,
                            cube_[s[ii].p[1]].x, cube_[s[ii].p[1]].y,
                            cube_[s[ii].p[2]].x, cube_[s[ii].p[2]].y,
                            color);
        M5.Lcd.fillTriangle(cube_[s[ii].p[2]].x, cube_[s[ii].p[2]].y,
                            cube_[s[ii].p[3]].x, cube_[s[ii].p[3]].y,
                            cube_[s[ii].p[0]].x, cube_[s[ii].p[0]].y,
                            color);
        M5.Lcd.fillTriangle(cube_[s[ii].p[1]].x, cube_[s[ii].p[1]].y,
                            cube_[s[ii].p[2]].x, cube_[s[ii].p[2]].y,
                            cube_[s[ii].p[3]].x, cube_[s[ii].p[3]].y,
                            color);
      #endif
    }
  }
}

void calc_and_draw(int16_t w, int16_t v){
  copy_cube();
  rotate_cube_y(w);
  rotate_cube_x(v);
  trans_cube(U * 8);
  convert_cube();
  calculate_z();
  draw_cube();
}

void setup(void){ 
  M5.begin();
//Wire.begin();if(digitalRead(39)==0){updateFromFS(SD);ESP.restart();}//SD UPdate
  M5.Lcd.fillScreen(TFT_BLACK);
  M5.Lcd.setRotation(3);
}

int16_t w = 0;
int16_t v = 0;

void loop(void){
  calc_and_draw(w, v >> 3);
  v += 3;
  v &= 511;
  w++;
  w &= 63;
  delay(50); // Rotational speed
  M5.Lcd.fillRect(x_min,y_min,x_max-x_min+3,y_max-y_min+3,BLACK);
}

———————————————————————————————–
M5StickC , 3D , COLOR , CUBE , IPS ,

Written by macsbug

5月 20, 2019 at 10:30 pm

カテゴリー: M5STACK