//****************************************************** 
//  FILE        WIFI時計 MAX7219-DOT-LED 
//  DATE        :Tue, Jan 10, 2025      
//  DESCRIPTION : タイマー割り込みにて 制御。
//  CPU TYPE    : ESP32                      
//                                    by Chiyabo
//  This file is generated by Renesas Project Generator.
//****************************************************** 
//
#include < WiFi.h > //For WiFi Network Connection
#include < time.h > //For Time Based Application 
#include < MD_Parola.h > //For MAX7219 Matrix LED
#include < MD_MAX72xx.h > //For MAX7219 SPI LED Driver
#include < SPI.h > //For SPI Communication
#include < WiFiUdp.h >  //For WiFi UDP
#include " F3x8p.h"     // 3x8 font
#include "GF4x8p.h"     // 4x8 font

//MAX7219
  #define MAX_DEVICES 4 // four modules
  #define CLK_PIN   18
  #define data_PIN  23
  #define CS_PIN    5

//このライブラリは複数の種類のMAX7219製品に対応しています。
//そのため使う製品によってハードウェアの構成を変える必要があります。
//下記の4つの設定のうち、自分のモジュールで正常に表示される設定一つだけを有効にする。
//#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
//#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW
//#define HARDWARE_TYPE MD_MAX72XX::ICSTATION_HW
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW

#define SPEED_TIME  25  //Small numbers are faster. Zero is the fastest.

// ハードウェア SPI 接続を持つ MD_Parola クラスの新しいインスタンスを作成します。
MD_Parola Display = MD_Parola( HARDWARE_TYPE, CS_PIN, MAX_DEVICES ) ;
// ソフトウェア SPI のセットアップ:
//MD_Parola myDisplay = MD_Parola( HARDWARE_TYPE、data1_PIN、CLK_PIN、CS_PIN、MAX_DEVICES ) ;

// 家のwifiのssidとパスワードを入力
const char* ssid = "YOUR_SSID" ;
const char* password = "YOUR_PASSWORD" ;

// NTPサーバー設定
const char* ntpServer = "ntp.nict.jp" ;  // 日本標準時(JST)
const long gmtOffset_sec = 9 * 3600 ;    // GMT+9(日本時間)
const int daylightOffset_sec = 0 ;       // サマータイム(日本は使用しない)

//timer 初期化
hw_timer_t * timer = NULL ; 
char TimeInEnCnt = 0 ; //WiFi_Time Read時間 時間*TimeInEnCnt = 時間間隔
char TimeInEn = true ; 
char Int_Cnt = 2 ;
char Int_1Sec = false ;
char ErrFlg = false ;
char Sec ;        // 秒
char Min ;        // 分
char Hor ;        // 時
//char Week ;       // 曜日

char DisplayTime = true ;
int Sec_Change ;        
int Min_Change ;     
int Hor_Change ;     

// アナログ入力関連
int analogPin = 33 ;  // potentiometer wiper (middle terminal)
                      // connected to analog pin 33
                      // outside leads to ground and +3.3V
int val = 0 ;         // variable to store the value read
char SetBrightNoOld ;

char bufS[ 3 ], bufT[ 6 ] ; // bufS = Sec, 'ss' + 1 = 3,  bufT = HourMinute, 'hh:mm" + 1 = 6
char WeekText[ ][ 5 ] = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" } ;
//char WeekText[ ][ 8 ] = { "Sunday", "Monday", "Tuesday", "Wednes", "Thurs", "Friday", "Satur" } ;
static char DspBuf1[ 8 ] ;
static char DspBuf2[ 8 ] ;
unsigned char SelMessage = 0 ;
unsigned char DotMessage = false ;
unsigned char MsgNo = 0 ;
// 割込みサービスルーチン 0.5秒
void IRAM_ATTR LED_Blink( ){
  if( --Int_Cnt == 0 ){
    Int_Cnt = 2 ;
    Int_1Sec = true ;
  }
}

void setup( ){
  //Serial.begin( 115200 ) ;
  Display.begin( 2 ) ; // オブジェクトを初期化します: 2 zones
  Display.setZone( 0, 1, 3 ) ;  //00011111 <- Zone 0  for HM Display1
  Display.setZone( 1, 0, 0 ) ;  //11100000 <- Zone 1  for Sec Display1 
  Display.setFont( 0,GF4x8p ) ;
  Display.setFont( 1, GF3x8p ) ;
  Display.setIntensity( 0 ) ;  // ディスプレイの明るさを設定(0〜15)。明るさMax:15
  // 周波数を直接指定(ここでは1MHz = 1µsごとにカウントアップ)
  timer = timerBegin( 1000000 ) ;
  // 割り込み関数を登録
  timerAttachInterrupt( timer, &LED_Blink ) ;
  // アラーム設定: 500000カウントごと(=0.5Sec)、繰り返し
  timerAlarm( timer, 500000, true, 0 ) ; // 第3引数をtrue指定で、繰り返し。その際は第4引数は無
}

void loop( ){
  char SetBrightNoNew ;
  
  if( ErrFlg == true ){
    WiFiTime( ) ;
  }
  // 現在時刻を表示
  if( Int_1Sec == true ){
    Int_1Sec = false ;
    //if( Display.displayAnimate( ) ){// 何故か2秒間隔で表示?
    Display.displayAnimate( ) ;
    DotLed( ) ;
    //}
  }
  
  // 現在時刻を取得
  if( TimeInEn == true ){
    TimeInEn = false ;
    WiFiTime( ) ;    
  }

  // DisPlay Bright Adjust
  if( DisplayTime == true ){
    val = analogRead( analogPin ) ;  // read the input pin 
    if( val <= 1490 ){
      SetBrightNoNew = 3 ;
    }
    else if( val <= 2980 ){
      SetBrightNoNew = 2 ;
    }
    else{
      SetBrightNoNew = 1 ;
    }
    
    if( SetBrightNoOld != SetBrightNoNew ){
      SetBrightNoOld = SetBrightNoNew ;
      if( SetBrightNoNew >= 3 ){
        Display.setIntensity( 1 ) ;  // ディスプレイの明るさを設定(0〜15)。明るさMax:15
      }
      else{
        Display.setIntensity( 0 ) ;  // ディスプレイの明るさを設定(0〜15)。明るさMax:15
      }
    }
    DisplayTime = false ;     
  }
}

void connectToWiFi( ){
  char RtyErr = 0 ;
  
  // WiFiに接続
  WiFi.begin( ssid, password ) ;
  // WiFi接続出来るまで繰り返す
  while( WiFi.status( ) != WL_CONNECTED ){
    delay( 1000 ) ;
    if( RtyErr >= 3 ){
      ErrFlg = true ;
      WiFi.disconnect( true ) ;
      return;
    }
  }
  // WiFi接続成功!
}

bool getLocalTime( ){
  struct tm timeinfo ;
  return getLocalTime( &timeinfo ) ;
}

void printLocalTime( ){
  struct tm timeinfo ;
  if ( !getLocalTime( &timeinfo ) ){
    // 時刻取得エラー
    ErrFlg = true ;
    WiFi.disconnect( true ) ;
    return ;
  }
  ErrFlg = false ;
  // 時刻を整形して表示
  //Serial.printf( "現在時刻: %04d/%02d/%02d/%d %02d:%02d:%02d\n ",
  //              timeinfo.tm_year + 1900,
  //              timeinfo.tm_mon + 1,
  //              timeinfo.tm_mday,
  //              timeinfo.tm_wday,    // 曜日 
  //              timeinfo.tm_hour,
  //              timeinfo.tm_min,
  //              timeinfo.tm_sec ) ;
  
  sprintf( DspBuf1, "%04d", timeinfo.tm_year + 1900 ) ;
  sprintf( DspBuf2, "%02d/%02d %d", timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_wday) ;
  SelMessage = ( DspBuf2[ 6 ] -( 16 * 3 ) ) ; 
  DspBuf2[ 6 ] = NULL ;
  DotMessage = true ;
  //Serial.println( DspBuf1 ) ;
  //Serial.println( DspBuf2 ) ;
  //Serial.println( SelMessage, DEC ) ;
    
  Sec = ( char )timeinfo.tm_sec ;        // 秒
  Min = ( char )timeinfo.tm_min ;        // 分
  Hor = ( char )timeinfo.tm_hour ;       // 時
  //Week = ( char )timeinfo.tm_wday ;      // 曜日 
  //Serial.println( Sec, DEC ) ;
  //Serial.println( Min, DEC ) ;
  //Serial.println( Week, DEC ) ;
  //Serial.println( WeekText[ Week ] ) ;
  //ESP32のWi-Fi切断
  WiFi.disconnect( true ) ;
}

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 
//	FunctionName : WiFi時刻取得									                     
//		Prototype: void WiFiTime( void )                   				    
//		Function : WiFi時刻設定       								                 
//		Input    : NONE								                                 
//		Output   : NONE	    									                         
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
void WiFiTime( void ){
  
  connectToWiFi( ) ;
  
  // NTP時刻設定
  configTime( gmtOffset_sec, daylightOffset_sec, ntpServer ) ;
  // 時刻同期を待つ
  printLocalTime( ) ;
}

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 
//	FunctionName : Led Write Task									                   
//		Prototype: void LED( void )                   				         
//		Function : LED data1 SET	& WRITE								                 
//		Input    : NONE								                                 
//		Output   : NONE	    									                         
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
void DotLed( void ){
  char Test ;
  
  if( ++Sec >= 60 ){
    Sec = 0 ;
    DisplayTime = true ;
    if( ++Min >= 60 ){ // 分
      Min = 0 ;
      if( ++Hor >= 24 ){
        Hor = 0 ;              
        TimeInEnCnt = 0 ;
        TimeInEn = true ;
      }
      if( ++TimeInEnCnt >= 6 ){  // 6時間経過 WiFiTime( )
        TimeInEnCnt = 0 ;
        TimeInEn = true ;
      }
    }
  }
  else if( Sec == 1 ){
    DotMessage = true ;
    MsgNo = 0 ;
  } 
  //Serial.println( Sec,DEC ) ;

  //時計表示切替
  Test = HexChgDec( Hor ) ;
  bufT[ 0 ] = ( ( Test >> 4 ) + 0x30 ) ;
  bufT[ 1 ] = ( ( Test & 0x0f ) + 0x30 ) ;
  bufT[ 2 ] = ':' ;
  Test = HexChgDec( Min ) ;
  bufT[ 3 ] = ( ( Test >> 4 ) + 0x30 ) ;
  bufT[ 4 ] = ( ( Test & 0x0f ) + 0x30 ) ;
   
  Test = HexChgDec( Sec ) ;
  bufS[ 0 ] = ( ( Test >> 4 ) + 0x30 ) ;
  bufS[ 1 ] = ( ( Test & 0x0f ) + 0x30 ) ; 
  
  //時計表示切替
  //Zone 0 Time
  if( DotMessage == true ){
    if( Display.displayAnimate( ) ){
      DotLed1( MsgNo ) ;
      if( ++MsgNo == 4 ){ ;
        DotMessage = false ;
      }
    }
  }
  else{
    //if( Display.getZoneStatus( 0 ) ){ //何故か1秒遅れて変化?
      Display.displayZoneText( 0, bufT, PA_CENTER, SPEED_TIME, 0, PA_PRINT, PA_NO_EFFECT ) ;
      Display.displayReset( 0 ) ;
    //}
  }
  //Zone 1 Time
  //if( Display.getZoneStatus( 1 ) ){ //何故か2秒単位で変化?
    Display.displayZoneText( 1, bufS, PA_RIGHT, SPEED_TIME, 0, PA_PRINT, PA_NO_EFFECT ) ;
    Display.displayReset( 1 ) ;
  //}
}

void DotLed1( unsigned char MsgNo ){
  if( MsgNo == 0 ){
    Display.displayZoneText( 0, DspBuf1, PA_CENTER , SPEED_TIME, 0,  PA_PRINT, PA_NO_EFFECT ) ;
    Display.displayReset( ) ;
  }
  else if( MsgNo == 1 ){
    Display.displayZoneText( 0, DspBuf2, PA_CENTER , SPEED_TIME, 0,  PA_PRINT, PA_NO_EFFECT ) ;
    Display.displayReset( ) ;
  }
  else{ // 2 3
    Display.displayZoneText( 0, WeekText[ SelMessage ], PA_CENTER , SPEED_TIME, 0,  PA_PRINT, PA_NO_EFFECT ) ;
    Display.displayReset( ) ;
  }
}

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 
//	FunctionName : HexChgDec								
//		Prototype: int HexChgDec(unsigned char HexByte)
//		Function : HEXをDEC値に変換する								 
//		Input    : 00h-->ffh										
//    Output   : 変換結果											 
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
int HexChgDec( unsigned char HexByte ){
	unsigned char i ;										
	int Value , Work ;									
  
	i = 0 ;
	Value = Work = 0 ;
	for( Work = 0 ; HexByte >= 0x0a ; Work++ ){
		HexByte = ( HexByte - 0x0a ) ;
	}																
	if( Work >= 0x0a ){							
		for( i = 0 ;  Work >= 0x0a ; i++ ){
			Work = ( Work - 0x0a ) ;						
		}															
	}																
	Value = ( ( i * 0x100 ) + ( Work * 0x10 ) + ( int )HexByte ) ;
	return Value;										
}