syouwa-taroのアイコン画像
syouwa-taro 2024年11月11日作成 (2024年11月13日更新) © CC BY 4+
製作品 製作品 閲覧数 583
syouwa-taro 2024年11月11日作成 (2024年11月13日更新) © CC BY 4+ 製作品 製作品 閲覧数 583

太陽追尾第四弾 鏡で太陽追尾して反射光が同じ場所を照らし続けるようにしてみた

太陽追尾第四弾 鏡で太陽追尾して反射光が同じ場所を照らし続けるようにしてみた

概要
太陽光を反射させる鏡で、JSTデータから太陽の位置を計算し、更に反射光が設定した場所を照らし続ける計算を加え常に反射光が同じ位置を照らすよう太陽の動きに合わせ鏡を動かしてゆく。二つのサーボによるシンプルな構造で実現した。
BOXを日当たりの良いところに設置することにより、日陰にある植物や洗濯もの等に年間通じ太陽反射光を充てることが出来、日のあたらない暗い寒い部屋にも一日中太陽をあてればECOとしても期待できる。

使い方
①日当たりの良い場所にBOXを東にむけ設置し、鏡の向きを目標の日陰に日が差すよう設定キー(UP,DOWN,EAST,WEST)で調節する。以上で設定完了し動作開始。これだけで鏡で照らされた目標の日陰は年間、終日太陽が移動しても常に太陽光を照らし続ける。

鏡の設定はWiFiでスマホからもでき、BOXが手の届かない日当たりの良い高所などにあっても設定出来る。スマホ画面のIPアドレスはESP32モニタ画面で読み取る
キャプションを入力できます

ここに動画が表示されます

動作
①赤道儀に取り付けスマホから方向コントロールできるMirrorがあり、太陽のあたる場所に設置し太陽反射光を当てたい日陰の場所(ex 家庭菜園 洗濯もの 日陰の部屋 ・・・・)に、スマホでMirrorの方向を調整し日陰に反射光を照らす
②太陽方位角と高度を追尾する2つのサーボにより太陽追尾を行い更に反射光が常に同じ場所に留まるよう計算しMirrorの角度を1分毎に設定する。
③NTPには年月日現時刻情報があり、年間の太陽追尾計算ができ一度反射光の向きを設定し鏡が常に日の当たるところにあれば、年間を通して常に日陰の所に日を当てることが可能となり いろいろな応用が考えられそうです。

動作原理
太陽光をMirrorで反射させたとき、太陽の動きにより反射光も動き同じところを照らすことは出来ないが、Mirrorを太陽の動きを追尾し更に同じ場所に留まる様に角度調整することにより反射光を同じ位置に停止させることが出来る。
Mirrorの動きをどのようにするか考察し、以下は太陽方位角が⊿Φ回転移動した時(S0➤S1)Mirrorを半分の⊿Φ/2傾けることで反射光は同じ場所にとどまることを”エイヤー”で作図してみました。(このような記事はネットでも見つからず間違いがあるかもしれず、図も見ずらくご了承ください)
ただし二次元では図の通り反射光がとどまるが、実際には3次元空間での動きはかなり複雑である
キャプションを入力できます

構造
リアルタイムで太陽の高度・方位をJST(日本標準時)から計算し、太陽が⊿Φ(ex 2°)回転移動するとMirrorを半分の⊿Φ/2(ex 1°)同じ方向に高度・方位のサーボでMirrorを回転移動させ常に同じ位置に反射光を照らす。
180度の2つの高度・方位サーボでは180度以内しか反射光を反射させることが出来ないが、両面Mirrorを使い表面と裏面を使い事により360度の方向に反射光を当てることが出来る。

反射光を常に同じ位置に反射する、太陽の方位角運動に対するMirrorの方位角動き

ここに動画が表示されます

回路システム
①ESP32のWifiによりNTPサーバーからのJST(日本標準時)を読み取る
②装置を設置する場所の緯度・経度をプログラムに入力しJSTとその位置情報からリアルタイムの太陽の軌道を計算
③スマホとESP32をWifiで通信し、スマホから鏡をコントロールする高度サーバーと方位サーバーで太陽光を照らしたい所に鏡の角度を設定する。(ESP32からのキー入力からも設定は行える)
④太陽の軌道と鏡の設定角度から常に反射光が同じところを照らすように鏡の角度をリアルタイムで計算し高度 方位のサーボに計算結果の角度を送り鏡をコントロールする。
以上で設定は完了でそのまま太陽反射光は同じ位置を照らし続ける。(1分毎の太陽の動きと鏡の角度はシリアルモニタで確認出来る。)

キャプションを入力できます

ESP32 シリアルモニタ

部品 備考
ESP32-WROOM-32E 秋月
MG995 方位用サーボ/180° 鏡は重いのでSG90では厳しい
SG90 高度用サーボ/180° (/360°購入したら360°回るモーターでした)
ミニスタンドミラー 鏡が両面にあり回転する ダイソー¥200
反射フィルム 大きい軽量反射板を作る ダイソー¥100

キャプションを入力できます

キャプションを入力できます

反射光を大きくする為大きい反射板(反射フィルムで作成)が鏡の上に付くようにしている。
キャプションを入力できます

SolarTracke4

#include <WiFi.h> #include <time.h> #define JST 3600* 9 #define PI 3.141592653589793 const char* ssid = "***********"; // Change this to your WiFi SSID const char* pass = "***********"; // Change this to your WiFi password const float NL = 35.43 ; // Change this to your North longitude default:KANAGAWA const float EL = 139.65 ; // Change this to your East latitude default:KANAGAWA WiFiServer server(80); int OneLoopMin ; //1loop/1minute flg int JSTIM; //日本標準時                  【単位:分】 int JSTIMrf; //mirror設定時刻                【単位:分】 float UDAGL ; //太陽高度 float EWAGL ; //太陽方位角 float EWservoAGLRF; //Mirror角度(E/Wサーボ 【単位:度】 float UDservoAGLRF; //Mirror角度(N/Sサーボ)【単位:度】 float EWAGLRF; //Mirror方位角 float UDAGLRF; //Mirror高度角 float eh; //均時差: e(天球上を一定な速さで動くと考えた平均太陽と、実際の太陽との移動の差、17分未満) [単位:時間] float Hd; //太陽計算高度 float Hdrf; //設定時UD高度 float Hdset; //反射角設定_高度変更値 float MKHK; //Mirror 高度補正係数 float Bd; //太陽計算方位 float Bdrf; //E/W設定時方位 float Bdset; //反射角設定_方位変更値 float MHHK; //Mirror 方位補正係数 float Bdsr; //日の出方位 float ttr; float t1h; //日の出時刻: [単位:時] float t2h; //日の入時刻: [単位:時] float tmh; //南中時刻 : [単位:時] int EWAGLsrt ; //Sun_Rise時刻:East/WestAngle_sunrise_time 【単位:分】 int EWAGLsst ; //Sun_Set時刻: East/WestAngle_sunset_time 【単位:分】 int EWAGLsmt ; //南中時刻: East/WestAngle_sunmid_time 【単位:分】 const int EWservo_PIN = 13; const int UDservo_PIN = 27;//GIOP12,14,15 OUTPUT PWMNG? GIOP27 OUTPUT PWMOK? const int BdsetInc_PIN = 12;//方位EAST 反時計回り const int BdsetDec_PIN = 14;//方位WEST 時計回り const int HdsetInc_PIN = 26;//高度UP const int HdsetDec_PIN = 25;//高度DOWN void setup() { Serial.begin(115200); delay(10); WiFi.begin(ssid, pass); while(WiFi.status() != WL_CONNECTED){ delay(500); } Serial.println("WiFi Connected"); configTime( JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp"); Serial.println(""); Serial.println("WiFi connected."); Serial.println("IP address: "); Serial.println(WiFi.localIP()); server.begin(); pinMode(EWservo_PIN,OUTPUT);//EWservo 制御pin pinMode(UDservo_PIN,OUTPUT);//UDservo 制御pin pinMode(BdsetInc_PIN,INPUT_PULLUP);//方位+ pinMode(BdsetDec_PIN,INPUT_PULLUP);//方位ー pinMode(HdsetInc_PIN,INPUT_PULLUP);//高度+ pinMode(HdsetDec_PIN,INPUT_PULLUP);//高度ー JSTIMrf=0; //Mirror設定時刻 未設定=0 【単位:分】  Hdset=0; //Mirror_Up/Down反射角   未設定=0 【単位:度】 Bdset=0; //Mirror_EAST/WEST反射角  未設定=0 【単位:度】 Bdsr=90; //Mirror 追尾基準方位 default90°:東【単位:度】 int OneLoopMin=0; //one loop/1min Flag   //TEST サーボ軸はめ込み角度確認========================================================= // EWmotor_servo(20);delay(1000); //PWM出力100ms // EWmotor_servo(10);delay(1000); //PWM出力100ms // EWmotor_servo(0); //PWM出力100ms // delay(3000); Serial.println("EWサー0°(方位270°西)TEST");// // EWmotor_servo(80);delay(1000); //PWM出力+100ms // EWmotor_servo(90);delay(1000); //PWM出力+100ms // delay(3000); Serial.println("EWサーボ90°(方位180°南)TEST");// EWmotor_servo(170); delay(1000);//PWM出力+100ms EWmotor_servo(175); delay(1000);//PWM出力+100ms EWmotor_servo(179); delay(1000);//PWM出力+100ms EWmotor_servo(180); delay(1000);//PWM出力+100ms Serial.println("EWサーボ180°(方位90°東)TEST:EWサーボ軸=垂直/天頂  Mirror正面は真東"); delay(3000); // // UDmotor_servo(90);delay(1000); //PWM出力100ms // UDmotor_servo(110);delay(1000); //PWM出力100ms // delay(1000); Serial.println("UDサーボ110°(高度70°)TEST"); // // for (int i=110; i>=80; i--) // { // UDmotor_servo(i);delay(100); //PWM出力100ms // } // delay(1000); Serial.println("UDサーボ110°➤80°(高度70°➤100°)TEST"); // // for (int i=80; i<=180; i++) // { // UDmotor_servo(i);delay(100); //PWM出力100ms // } UDmotor_servo(170);delay(1000); //PWM出力100ms UDmotor_servo(175);delay(1000); //PWM出力100ms UDmotor_servo(179);delay(1000); //PWM出力100ms UDmotor_servo(180);delay(1000); //PWM出力100ms Serial.println("UDサーボ180°(高度0°)TEST:UDサーボ軸=水平/真南  MIrror正面は真東:高度0°"); delay(3000); // Serial.println("TEST3終了"); //================================================================ } //set up END void loop() { time_t t; struct tm *tm; static const char *wd[7] = {"Sun","Mon","Tue","Wed","Thr","Fri","Sat"}; t = time(NULL); tm = localtime(&t); JSTIM = tm->tm_hour*60 + tm->tm_min ; // 現時刻【単位:分】時間をずらすことで時間のシュミレーションが出来る。(二時間前:-60X2=-120) //mirror設定キー入力check if (digitalRead(12) == LOW) {Bdset++;JSTIMrf=JSTIM;EWservoAGLRF=270-(Bdrf+Bdset+(Bd-Bdrf)/2);EWmotor_servo(EWservoAGLRF);} //時計回り WEST方向 if (digitalRead(14) == LOW) {Bdset--;JSTIMrf=JSTIM;EWservoAGLRF=270-(Bdrf+Bdset+(Bd-Bdrf)/2);EWmotor_servo(EWservoAGLRF);} //反時計回り EAST方向 if (digitalRead(26) == LOW) {Hdset++;JSTIMrf=JSTIM;UDservoAGLRF=180-(Hdrf+Hdset+(Hd-Hdrf)/2);UDmotor_servo(UDservoAGLRF);} //高度UP 90°超えた時注意 if (digitalRead(25) == LOW) {Hdset--;JSTIMrf=JSTIM;UDservoAGLRF=180-(Hdrf+Hdset+(Hd-Hdrf)/2);UDmotor_servo(UDservoAGLRF);} //高度DOWN 90°以内になった時注意 //*****1分周期のLOOP/0秒基準&& Mirror設定******************************************************************************************** //******************************************************************************************************************************** if ((tm->tm_sec == 0)&&(tm->tm_min != OneLoopMin)) //OneLoop/1分周期のLOOP/0秒基準 //============================================= { OneLoopMin = tm->tm_min; //Min更新でOneLoop/1Min Serial.printf(" %04d/%02d/%02d(%s) %02d:%02d:%02d\n", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, wd[tm->tm_wday], tm->tm_hour, tm->tm_min, tm->tm_sec); //************************************************データー計算*************************************************** //****************************太陽赤緯 均時差 太陽高度 太陽方位 日の出時刻 日の入り時刻******************************* //太陽赤緯計算  太陽赤緯:δ(太陽光線と地球の赤道面との角度、±23°27'の範囲で変化) [単位:度] float cald ; float month[12]={0,31,28,31,30,31,30,31,31,30,31,30}; //1月month[0]=0 ~ 11月month[11]=30 11ヶ月配列 float J = 0 ; //元日からの通算日数 + 0.5 for (int i=0; i<=tm->tm_mon; i++) //元日から前月までの合計日数 { J=J+month[i]; } J = J+tm->tm_mday+0.5 ; //元日から本日までの通算日数+0.5日 cald=0.33281-22.984*cos(PI*2/365*J)-0.34990*cos(2*PI*2/365*J)-0.13980*cos(3*PI*2/365*J)+3.7872*sin(PI*2/365*J) +0.03250*sin(2*PI*2/365*J)+0.07187*sin(3*PI*2/365*J); //均時差計算  均時差:e(天球上を一定な速さで動くと考えた平均太陽と、実際の太陽との移動の差、17分未満) [単位:時間] eh=0.0072*cos(PI*2/365*J )-0.0528*cos(2*PI*2/365*J )-0.0012*cos(3*PI*2/365*J )-0.1229*sin(PI*2/365*J ) -0.1565*sin(2*PI*2/365*J )-0.0041*sin(3*PI*2/365*J ); //太陽高度計算 float td ; //時角[度] float tr ; //時角[ラジアン] float Hr ; //計算高度[ラジアン] float Min=tm->tm_min; float Hou=tm->tm_hour; td=15*(Hou + Min/60 + (EL-135.00)/15+eh)-180; //時角【度】:東経139.65(神奈川) 0時:-180° 12時:0° 24時:+180° tr=td/(180/PI); //時角【度】を【ラジアン】に変換 Hr = asin(sin(NL/(180/PI))*sin(cald/(180/PI)) + cos(NL/(180/PI))*cos(cald/(180/PI))*cos(tr)); //高度計算【ラジアン】東経139.65 北緯35.43 時角t = 15T-180 Hd = Hr*(180/PI); //高度【ラジアン】を【度】に変換 //高度サーボ設定 if (JSTIMrf==0) Hdrf=0; //Mirror 反射位置未設定 日の出高度(Hd=0°)を追尾基準とする else { //Mirror反射位置設定:方位 高度 表裏で補正回転方向を場合分けする if (abs(Bd-EWAGLRF) < 10 ) MKHK=0 ; //|太陽方位ーMirror方位|角度が離れると高度補正角度を増やす(=UP補正) else if (abs(Bd-EWAGLRF) < 20 ) MKHK=0.020 ; else if (abs(Bd-EWAGLRF) < 30 ) MKHK=0.035 ; else if (abs(Bd-EWAGLRF) < 45 ) MKHK=0.060; else if (abs(Bd-EWAGLRF) < 60) MKHK=0.08; else if (abs(Bd-EWAGLRF) < 90) MKHK=0.12 ;//これ以上の方位角はmirrorの裏面に近ずき不安定 else if (abs(Bd-EWAGLRF) < 120) MKHK=0.170 ; else MKHK=0.070 ; Hdset = Hdset + MKHK ; //高度補正係数を加える } //Mirror高度計算 UDAGLRF = Hdrf+Hdset+(Hd-Hdrf)/2 ; UDservoAGLRF=180-UDAGLRF ; //constrain(UDAGLRF,0,180); //servoSpec 0°~180° の範囲にconstrain UDmotor_servo(UDservoAGLRF); //方位角計算 float X ; X = cos(cald/(180/PI))*sin(tr)/cos(Hr); //方位角計算X float Y ; Y = (sin(Hr)*sin(NL/(180/PI)) - sin(cald/(180/PI)))/cos(Hr)/cos(NL/(180/PI));//方位角計算Y float A ; A = atan2(X ,Y )+PI ; //計算方位角 【ラジアン】 Bd = A*(180/PI); //計算方位角 【度】 //方位サーボ設定 if (JSTIMrf==0) Bdrf=Bdsr; //Mirror 反射位置未設定 日の出方位を追尾方位基準とする else { //Mirror反射位置設定:方位 高度 表裏で補正回転方向を場合分けする |太陽方位ーMirror方位|角度が離れると方位補正角度を減らす(=EAST補正) if (abs(Bd-EWAGLRF) < 10 ) MHHK=0 ; else if (abs(Bd-EWAGLRF) < 20 ) MHHK=0; else if (abs(Bd-EWAGLRF) < 30 ) MHHK=-0.010; else if (abs(Bd-EWAGLRF) < 45 ) MHHK=-0.019; else if (abs(Bd-EWAGLRF) < 60 ) MHHK=-0.021; else if (abs(Bd-EWAGLRF) < 90 ) MHHK=-0.023;//これ以上はmirro反転裏側 else if (abs(Bd-EWAGLRF) < 120 ) MHHK=-0.03; else MHHK=-0.025; Bdset = Bdset + MHHK ;//方位補正係数を加える } EWAGLRF = Bdrf+Bdset+(Bd-Bdrf)/2 ;//方位計算 if (EWAGLRF < 90) {EWAGL=90; EWservoAGLRF=180; //現在の総合方位角が90°以下であればEWサーボのspec外で方位角90°(EWサーボ(180°))とする(北:0°~) } else if (EWAGLRF > 270) { EWAGL=270; EWservoAGLRF=0 ; //現在の総合方位角が270°以上はEWサーボspec外で方位角270°(EWサーボ(0°))とする } else {EWservoAGLRF=270-EWAGLRF;//サーボ回転方向反転180°-(計算方位角 - サーボ補正90°) } EWmotor_servo(EWservoAGLRF); //日の出 日の入り計算 ttr = acos(-tan(cald/(180/PI))*tan(NL/(180/PI))); t1h = (-ttr*(180/PI)+180)/15-(EL-135)/15-eh ; //Sun_Rise時刻【時】  t2h = (ttr*(180/PI)+180)/15-(EL-135)/15-eh ; //Sun_Set時刻 【時】 tmh = 12 - (EL - 135)/15 - eh ; //南中時刻 【時】 EWAGLsrt=t1h*60; //Sun_Rise時刻【分】 EWAGLsst=t2h*60; //Sun_Set時刻 【分】 EWAGLsmt=tmh*60; //南中時刻   【分】 if (JSTIM==EWAGLsrt) Bdsr=Bd ; //日の出方位設定 default90°(東) Serial.printf("日の出/現時刻/日の入り === %02d:%02d/%02d%:%02d/%02d:%02d 設置位置:北緯",EWAGLsrt/60,EWAGLsrt%60,JSTIM/60, JSTIM%60,EWAGLsst/60,EWAGLsst%60);Serial.print(NL);Serial.print("° 東経");Serial.print(EL);Serial.println("°"); Serial.print("太陽赤緯");Serial.print(cald);Serial.print("° 元日から通算日数+0.5=");Serial.print(J);Serial.print("日 均時差");Serial.print(eh);Serial.println("時"); Serial.print("太陽計算高度/方位=");Serial.print(Hd);Serial.print("°/");Serial.print(Bd);Serial.print("° 日の出方位");Serial.print(Bdsr);Serial.print("°"); Serial.print(" 計算南中高度:");Serial.print(90-NL+cald);Serial.printf("° 南中時刻 %02d:%02d \n",EWAGLsmt/60,EWAGLsmt%60); Serial.print("Mirror高度");Serial.print(UDAGLRF);Serial.print("°(servo");Serial.print(UDservoAGLRF);Serial.print("°) =追尾開始高度");Serial.print(Hdrf); Serial.print("°+U/D反射位置設定角");Serial.print(Hdset);Serial.print("°(補正係数");Serial.print(MKHK);Serial.print("°/分)+追尾移動角"); Serial.print(Hd-Hdrf);Serial.println("°/2"); Serial.print("Mirror方位");Serial.print(EWAGLRF);Serial.print("°(servo");Serial.print(EWservoAGLRF);Serial.print("°)=追尾開始方位");Serial.print(Bdrf); Serial.print("°+EW反射位置設定角");Serial.print(Bdset);Serial.print("°(補正係数");Serial.print(MHHK);Serial.print("°/分)+追尾移動角"); Serial.print(Bd-Bdrf);Serial.println("°/2");Serial.println(" "); //**************************************【Sun_Rise5分前➤Sun_Rise】************************************************************* //***************************************************************************************************************************** if (JSTIM < EWAGLsrt ) // WakeUp(Sun_Rise5分前)からSun_Rise直前 { //Sun_Riseを待つ Serial.printf("Sun_Riseまであと%d分\n\n", EWAGLsrt-JSTIM ); } //*****************************************【Sun_Rise ➤ Day_Time➤ Sun_Set ➤ deep sleep 】*********************************** else //Sun_Rise以降****************************************************************************************** { //**********************Sun_Rise**************************************************************************************** if (JSTIM == EWAGLsrt) { Serial.println(""); Serial.printf("🌅 Good morning!今日は%d月%d日 現在%d時%d分\n",tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min); } //**********************DayTime till SunSet********************************************************************************** if (JSTIM < EWAGLsst) { //1分後のサーバー設定までSLEEP // delay(200); 表示転送時間確保 // esp_sleep_enable_timer_wakeup(50*1000000); //方位を1°/4分 西にすすめる:servo確実に設定する為(50秒sleep+10秒servo設定)x4回try=方位角1°回転   // esp_light_sleep_start(); // ***deep sleepではごくまれにWifi connect missが発生でlight sleepが安心?*** } //Sun_RiseからSun_Set前まで1分毎間欠sleep終了 //*********************Sun_Set Sleep**************【Sun_Set➤sleep➤Sun_Rise5分前】**************************************** else //Sun_Set時刻になるとパネルをSun_Rise方位(東)に戻しSun_Rise5分前までDeepSleep。 { Serial.printf("🌄SunSet! %d時%d分パネルを日の出方位(東方)に戻し Sun_Rise%d時%d分の5分前までsleep \n\n", EWAGLsst/60,EWAGLsst%60,EWAGLsrt/60,EWAGLsrt%60); while (EWservoAGLRF < (270-Bdsr)) { //Sun_Set方位角 ➤ 翌日Sun_Rise方向:Bdsr (=サーボ:180°-(Bdsr-90°))に戻す EWservoAGLRF++; EWmotor_servo(EWservoAGLRF); Serial.println("Sun_Rise方向に戻す"); delay(100) ; //1°/100msecでSun_Rise角まで戻す } Serial.printf("Good Night(-_-)zzz till tomorrow SunRise for %d時間%d分 \n",((1440-JSTIM)+EWAGLsrt-5)/60,((1440-JSTIM)+EWAGLsrt-5)%60); delay(2000); //表示転送時間確保 int nightsleep=((1440-JSTIM)+EWAGLsrt-5)*60 ; // (深夜零時までの時間:60分X24時間ー現時刻)+明日のSun_Rise時刻 - 5分前)X60秒= Night_sleep時間 esp_sleep_enable_timer_wakeup(nightsleep*1000000ULL); // Sun_Set(現時刻)からSun_Rise5分前までTimer_SLEEP 【単位μsec】    esp_light_sleep_start() ; //(-_-)zzz sleep till tomorrow SunRise(before 5 minutes) } //Sun_Set sleep end } // Sun_Rise 以降end } //分周期 0秒同期 SolarTrackeLOOP END //*****************WIFI client MirrorAdjust***************************************************************************** WiFiClient client = server.available(); // listen for incoming clients if (client) { // if you get a client, Serial.println("New Client."); // print a message out the serial port String currentLine = ""; // make a String to hold incoming data from the client while (client.connected()) { // loop while the client's connected if (client.available()) { // if there's bytes to read from the client, char c = client.read(); // read a byte, then Serial.write(c); // print it out the serial monitor if (c == '\n') { // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. // that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0) { // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) // and a content-type so the client knows what's coming, then a blank line: client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println(); // the content of the HTTP response follows the header: client.print("<big>&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;Mirror_Angle_Adjust</big><br><br><br>"); client.print("<big>&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;click <a href=\"/U\">here</a> to UP </big><br><br><br>"); client.print("<big>Click <a href=\"/L\">here</a> to EAST </big>&emsp;&emsp;&emsp;"); client.print("<big>&emsp;&emsp;&emsp;Click <a href=\"/R\">here</a> to WEST </big><br><br><br>"); client.print("<big>&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;Click <a href=\"/D\">here</a> to DOWN </big><br><br><br>"); // client.print("<big>Click <a href=\"/E\">here</a> to END Adjust Mirror.</big><br><br><br>"); // The HTTP response ends with another blank line: client.println(); // break out of the while loop: break; } else { // if you got a newline, then clear currentLine: currentLine = ""; } } else if (c != '\r') { // if you got anything else but a carriage return character, currentLine += c; // add it to the end of the currentLine } // Check to see if the client request was "GET /H" or "GET /L": //WiFi setting if (currentLine.endsWith("GET /R")) { // GET /R RIGHT on 時計回りWEST方向 Bdset++ ; JSTIMrf=JSTIM ;// Bdrf=Bd;設定値は更新しない EWAGLRF=Bdrf+Bdset+(Bd-Bdrf)*0.5 ; //(Bdrf+(Bd-Bdrf)/2)+Bdset ; //BdsetTEST mainでは別サーボORメカ EWservoAGLRF=270-EWAGLRF; //servoAngle変換 逆回転 方位90°補正(東:EWservo=0⁰~) EWmotor_servo(EWservoAGLRF); } if (currentLine.endsWith("GET /U")) { // GET /U UP on 上向き高度設定 Hdset++ ; JSTIMrf=JSTIM ; //Hdrf=Hd ;設定値は更新しない UDAGLRF=Hdrf+Hdset+(Hd-Hdrf)*0.5 ; UDservoAGLRF=180-UDAGLRF ; //servo逆回転補正 高度0°=servo180° (Hdrf+(Hd-Hdrf)/2)+Hdset; UDmotor_servo(UDservoAGLRF); } if (currentLine.endsWith("GET /L")) { // GET /L LEFT on 反時計回りEAST方向  Bdset-- ; JSTIMrf=JST ; //Bdrf=Bd; 設定値は更新しない EWAGLRF=Bdrf+Bdset+(Bd-Bdrf)*0.5 ; //EWAGLRF=Bdrf/2 + Bd/2 +Bdset ; //(Bdrf+(Bd-Bdrf)/2)+Bdset;//BdsetTEST mainでは別サーボORメカ EWservoAGLRF=270-EWAGLRF; //servoAngle変換 逆回転 方位90°補正(東:EWservo=0⁰~) EWmotor_servo(EWservoAGLRF); } if (currentLine.endsWith("GET /D")) { // GET /D UP on 下向き高度設定 Hdset-- ; JSTIMrf=JSTIM ; //Hdrf=Hd; 設定値は更新しない UDAGLRF=Hdrf+Hdset+(Hd-Hdrf)*0.5 ; UDservoAGLRF=180-UDAGLRF ; //servo逆回転補正 高度0°=servo180°~高度90°=servo90°~  Hdrf+(Hd-Hdrf)/2+Hdset; UDmotor_servo(UDservoAGLRF); } //if (currentLine.endsWith("GET /E")) MRSET = 0 ;// GET /E } } // close the connection: client.stop(); Serial.println("Client Disconnected."); } //WiFi client end } //LOOP END void EWmotor_servo(int angle) { float pulse_width; float starttime; pulse_width = 500.00+angle*10.556; starttime = millis(); while(true) { if(millis()-starttime > 200) break; //PWM出力時間( 120ms/60°):MG995/1°>200ms SG90/1°>100ms digitalWrite(EWservo_PIN, HIGH); delayMicroseconds(pulse_width); digitalWrite(EWservo_PIN, LOW); delay(19); //周期 約20秒 19.5ms~21.4ms } } void UDmotor_servo(int angle) { float pulse_width; float starttime; pulse_width = 500.00+angle*10.556; starttime = millis(); while(true) { if(millis()-starttime > 100) break; //PWM出力時間100ms( 120ms/60°)SG90/1°>100ms digitalWrite(UDservo_PIN, HIGH); delayMicroseconds(pulse_width); digitalWrite(UDservo_PIN, LOW); delay(19); //周期 約20秒 19.5ms~21.4ms } }

今後の課題

  • 反射光が留まる為のMirrorの追尾角は筆者が2D平面で考察したもので実際の3Dの空間では誤差が出てくる。現在の脳みそ能力では3Dの計算式の考察が困難でとりあえず誤差を現物合わせするソフトでおよそ留まっているが、正確な3D計算式の作成が課題である。
    計算式の基準になるJSTはWifi環境が必要で電波時計で読み込む検証、スマホとの通信をWifiでなくBluetoothで行う検証(WiFiとBluetoothを両方使うとコンパイラが”出来ません”と出てきてしまいました)
  • 鏡の角度の精度が1度以上だと反射光は動きが目立ち、ホビーのサーボで1度以内の精度は難しくステップモーターなどで改良が課題となる。サーボの角度は180°ではミラーを回すとすぐ限度になり360°回せるものが望ましい。
    Mirrorを支えるメカはサーボと数度のバックラッシュがあり1度以内の精度にする事が必要
  • 鏡は重いので小さな鏡を使い反射光のスポットは小さい。反射光を照らす対象の大きさに合わせ軽量化 大小 凹面凸面と変え効率よく照らす。(反射光の大きさを鏡の表裏で変えられるようにするのも便利そうです。)
  • 太陽光は日陰だけでなく、日の当たるところにも反射させ、ソーラー発電パワーアップ等の応用も考えられ、太陽光の利用を増やす太陽反射装置の活用を考えてゆきたい。
  • ソーラーパネル・バッテリを搭載し配線不要にすればより活用しやすくなります。(sleepを使うと時計が遅くなりました、ESP32限界かもしれません)参考 https://www.youtube.com/watch?v=n7Zw0ZIGtAk&t=15s

感想
まだまだ課題の多いテーマですが、この記事を見て興味を持っていただき、独自に実用的な物を作られ世の中で使われるようになればうれしい限りです。太陽光の利用を広げることが出来、環境にも貢献できればと思います。

参考にさせて頂いた記事
ESP32で現在時刻を取得する
太陽の日周運動
太陽の高度と方位角
360°連続回転のサーボモータ「SG90-HV」を使ってみる
ESP-WROOM-32DをWi-Fiに接続する方法と手順の紹介。
太陽の明かりを室内に取り込めるライティングマシン「Lucy」

3
2
syouwa-taroのアイコン画像
昭和生まれ 谷中生まれ谷中育ちの江戸っ子 ヽ(^o^)丿
ログインしてコメントを投稿する