From 4b2a838e6dd40933ec7a955cffee763f8c655c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem?= Date: Mon, 9 Feb 2026 16:56:11 -0800 Subject: [PATCH] first innit --- CAD/PCB_Adapter.stl | Bin 0 -> 35484 bytes CAD/motor_Adapter.stl | Bin 0 -> 68184 bytes Firmware/orreryDriver.ino | 283 ++++++++++++++++++++++++++++++++++++++ README.md | 24 ++++ 4 files changed, 307 insertions(+) create mode 100644 CAD/PCB_Adapter.stl create mode 100644 CAD/motor_Adapter.stl create mode 100644 Firmware/orreryDriver.ino diff --git a/CAD/PCB_Adapter.stl b/CAD/PCB_Adapter.stl new file mode 100644 index 0000000000000000000000000000000000000000..e5716a5f9d7c793c10b8cc4fd004e1e71c662818 GIT binary patch literal 35484 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 literal 0 HcmV?d00001 diff --git a/CAD/motor_Adapter.stl b/CAD/motor_Adapter.stl new file mode 100644 index 0000000000000000000000000000000000000000..cecebcb200c813a5f8d0eb965a1d51cac17f91b2 GIT binary patch literal 68184 zcmb822bdH^8uwca7zk!P#DJ1LOdw%Lwr3C(QAbRlIe`fk(};%%GOM5{=EIy&f-K5m zI1zSadsaL-1F{@?>M4fvjAuYOGk1RVR!vR)s=IeTzkMF1p83DOdaJsstKO>Vt~u)H zLx=2=JNm#~_U^aGK6~%6*B*QAvj5?S9)9HD88c@5y37Cn-;o;z!A8eV3J>aZ+u%U` zeQ?Kv*&tYx?c29L=hAh#O$!fX3u}JWpBM6$7M{hb2~CHg*=3bV7VcTgVmu_|Tyq;pn>D5t;q66V~*u_#iAwJ~Zkb5LfIJz!(BRgM>@hEhVx5Bg#_m-pP)j5vyV?uA;G!CuQ?SGGL~H5 zLFV&#%}LOTbBSLnDkL~F_yiRa9Or$43JH$dK0$>9$6!sEp+t=(R7l9k*LKAA?pmc1 zw32bG`O@DxqS~+GZD4yJ{rRP0UNYL*d|6t*=2S?ynJ7M%P$3~>WqQp?&`L(Q^nN9Z zt*wlSHr+a5^CI!bs|sZk!^P{tyl7Sa;`p+O$l}CFTkcx6R7eav;K8z4 z^`ZK2F=DACZRZU7^RE>Z65>03jv_%TxrXU8ry)!C#diaW=?t~9ND!quc%Ac z=h$O>f=W73SqVoH?aNR~!lmnTeos^@2~Egqn@Tz%KGgaql2uFh4?~Y|bbXHZqa&nL zRMH9Yq1I`UT|1L->G~Y|n@>G~XdrB6^vC&Y(Z2}gGAOv0t> zbAE3pK_#6KAF2+KT|1L->G~Y+8%Lm*#OUz#S*{&Qv(h2c#Om_O5E=T>ZhpeAn++Lq^Q8@SHglHVyJm=ldr_C0^ ztqpv_tt|Aly6&|(a%JNLtt4&cD`lH_4f%1u27w3Dt z^!cJkgrMSL`1%}6TgX2;Z{D06-&`?A2wJ)OF;4L6GA|M?>W|l)3JH!YehZSI6-PUt zV7?qnNc#ly;z&*3K0$?qTfM|q(`4QcJ!e_!mVWD*NOzKLPn*5mFR zJMK~;!3a=~?Q`tdPJ&i$?GvvD6%veO{eDG)R&KQv_dz92xH*bTudD~41^n6#{~gvh@TV|HtWFeNgjqC!IMX0szK#PU~OnSI)u^Yt7>g@lPEY(1pP8$YqH zj!Q_;O2$g(W8Ine&i-ZGC)x)U5;7CHdTeyayhj?&KU~+G3JIAX-L;ZZNxkLjQXwJH zhigG&og`=_IeA3UQ1Xq&b$;@_Ip_DeL)V-N2|u!wBB{Tkl#0Dw(zXqFr}@2|(UPR?$SI|@X(5X0yC&UJ zistH?+PZXItOYBClq_I-q!SY9AN=LG!K<&CBx}xM%`cm(fA>=D+q21~o6Cbvoff|J zhduS@j!@)GD(OUXxsbo-g+aAr%U6Wz-&rJFy19Jaz~=Dm5BgMmfappkoe&>055Bwh z-?>Xe^{+=by19JERhwkz4cWcog9MdyLVOH9e^u>WJw}J>->xKFy15*l(JOo7>>d># zAZAfXC&b4C12zoD-+Mx+{+&(2rJKu-4Lc~iXuI7iJ}hzms)M>wNhid|u%?~D+g}?N ze%a*$k8pHzc}3&M?Ah~ntoR^7C7lo-*L}Nh*mc`};U{CKb|c}^&E;24xgxvMCEHbe zSmL^GpYBQ}oe&@Oh2i0Sf9f387hduRM>m(7&YYS(^P0^oJ{&Q8HkEWjeC+VUiDBLM zZ9?_0M>x8<{Ga1*&7QN~CKVr+kUNS>Iw3x0)tnpd{lKfTN9GZZZZ7Y3?p@hCe+?== zEb-un@9ah;oe&>KZag+zlbc?<^oFLcBwV_=eCHMSXD_Y&CM);9Pf$rG#K%DcCWPOP z-m^A;Ro8ALT)Mg3^@#Mb<|Z`|X=(f2LL?)~5S+13eKO}%ANw*T4(QV3eHRNZcv zn_U%{(%PC!xrz}L5_YyrmWl+eo*X|jdqdN!u2e#&urp`U2MJoSFX(y{wF~8g z3JFQeHz`RUal%@;J|?|gDqww3VZKr)`3@=Rg9NSIy;AXET&Q|bAz>qcs*h@^NYKjN z^YMC6A>l@%I6;MkjaRCrs@8)9t=xzj_d$h(8|UK$6%uatsSwptQ6XV>kgD~lUR@Hj zaR8&Z~xjkMgDkSV)O|n!ZXysy*c&Vt6aFJ7-phCipm2rX!2{($yYfgoP-K(kA zTv|}INiiZptEbn$F>&wA*hge?dVO*E>5fv;wGX(!tMtp z>p_B66Z`iryEu`hqC(>HPQ%JB>SL*>kdU>Ae6OA?6$x6kzjajEMSUz46%w27J)!Jk zI1*Gyy#MOdvWv)A4=N<=?oP5EBxtqO(p$?eB4eqjkQlP+uCj}-SSl(c?0!(PR3vD% z@L%_rU3^6!R7ltzq@)iLwA%k)Gs-T$q7N!0?4DE7N1Tv7E3sPn;mooZ0aV-+BPz_- z?ouUvkf7Dj@8*`%BN-|rq-|`ZR*ij%a@UgZtTueSoF0)CppcMWDZ4nydXS*iBR?%F zCt^5VDk>z}JifHNbcb70d{7|~1kL4r9-ZtHfz+G|2^l41|0-Dz613W~w7R_WLxmKA z3W;I!zbL;t`gEVrrJ_PYMpW5{OO}cRtvWxju6*owNBV^JL4}0OJ_#QQf>!_Sw=SFR zg9?dWL%+zT*PIFoi3Sqwp-V-AR%?$~olUfdCa943;Eom9^j<=RghWn>-mZO+pw&MQ zT$)YvT}@CSkyzstmAU#WM)CMl_Qj-UFrM@8bLruBzLT^iIBHi3?IV6~+wp_ zvx@VJ%?ZgKOD(QsyP`iVTmHot7BwV^@ zlZyt$hb8*lx>GkQ>4f-DyMvMJC(7<1371xPR5h{`A0TE^NhidI+8vB4`-vXm=$=h3 zeiR=hsH79(L+uVmmHouoBwV^@lZ!aThb7d%v#F#L;zR8YMrJ>8xJNj;XOoLK#fK#V z^+zS05FcuHFp~YmIN|7?O)f?iA0()x6XHYd4n~#z#DIiL_iS=8s`#*k+E1KGC7lo- zYCkcm>?h75;nF>uT#PC{EU_lHbvG*Mg!oXqgOThfemiYhb*ZEi z;$!x}3DMZ$d+O&CNw{>+rp6CumJfMJcD(50r(32CphALCZ6TlQI5uj(_4L|<51#f6 z30iIZ_1yA;EoA48*XrPdHycQW1f$wQzG?Wm(N@R2T6_0;n=K+itKa>!pxkSlKUI9# zHb3*K5d*1^U{qVkKeg3K(SJWe(()aHB|;V-_8$C8hxr4kkYMDjdg5OFqle$<8}@wq zu|*_k_1YUN%6qJkm|A?;vE;>Y#Xu?~#L~rA&uqDK)M@&#F#Fo8i%8IF?6s@Qoj#M8 zT71|s@w-F68Ayc$W9>rzllwP}HaPi&aMFr3i%8Ha8uLYY_xTcqi;w#&vq9H;HyK2Q z1Y>Pg^HHxgp8oYYVd1Dv7L%aWZAY#v58m^rijVy&v)cO;I}M^jf>BfBf<|J~kKExG6l^dpy4!uyB~8m;G3g)} z*QA~Ws6>5})tE?v3JIIj>&1z!5VZQ{{%f_5k4~+N5maXGb&a;#B)U3Ts-lX(^~}jq zkvQ>S^PIz_O;ch%NYDyTM)-vCReffniNpv0tJ5Xi{_tykLR~gT(CWaquGGXy$4re8 zR9+ot{|e_Bq~ILW?Cg~Wruo}!85{XvDqS!WqS?nkf9i&RM1oaAoy2`VIPX$NG- z#0V-R7C$gb*W;_Yb7BM)62r%wt%>a?o)IIckQm(Nd`+x8{qz_?g+xR5i#2i3F{j1| zDkS!~|1wRif6*yE5l|tq@R6N0vFFzdW1}buTCG2!yCyz;>b@AkzPs{Ph_vS_Z9Yuu%BZHO+THW};0PW+A$5u7^ z1QilU=RBg=0+E}%()gIs)F$)^D$MuhTTRWEWS{YgfC>p*g;=Rb&}zwUrUn20<2G5Z zQ~?zdXw8@p613Xt2GbLdJ^t{R4=NG-1{2KE?g#>1wm=6-P`rz2LWtpijXuGLT z1XM_1PV|W)30l1}>K*N4$_ZQfL_mcEW^JD+lAzTQ>p!o3{Iqz77(s=Edv@5pS!`lz z60|CxGEe(htDZ~sdV4^H#FaB{*2K_rdig|=3W+J7ovn#0X6z9osF1jC%RM#m<1_oj z2r48l@AtX-)-;>_{*irS1QilL7e{O2p7RF82r48BLu>XEA3y&ZC#aBcPs&%?qiEMS zBxrS5{zA1<&SrNxqvE4VP$BX4R$pqO{oR$CR|zU4PM)w=R(v!(RJpoUf(nW6hmY1o zyQ?cLSS6^C`23JtH1YcWm0nUMsE~Mm{YN!X{$f|t9+oIlA+dVK^O_hwHGY3kA%XRk z?;{{VtG9oANBekm*Z6%-g#=b)F&`voHSxo>+Q+gNH}QL7K!pU>a4{bwXm!#38{~Aq z>J%S$sgS^`FXn>;tyYiepnZJy^QW<~oeBx86JtI|&}#XS=3C2&C&cGLDkQM7jQJoz ztLp}v739B9T@V)i1l4HD|G3EXoL|kjQnNps%i46~%mzpjG)Bd1oS< zy=wh|GmBAu*$WKTT|Q!`88@ONGS8 zd&s*>*{oV~#!5wkRvqu}uZdGn{&Os*rb0r+*P4)bnDjhYtp^EO-SYYXP5kwPpK5)A z3W?6|8e-eS-!tovst+n8Y+82R{Mk8;5UWQPnwl?IIzP6KqQZP}6=FU}&}#eBO$&A# zzD3zD6%`VPeqee@&2z(J1Qil!|5&L=&}y^6zt`Iq+;tKyS2=zSHkUEWM0HPJ6pW}Xm#Xq+vAk1V*8)-R`|#c@rVqOlqVDrn2wK_w88w!)LeNS^1nF1O zQ~iELg@oN_Qlnif1g-4uml{Q51Qojvr$%Qrj{KHVk+6G>_6e@yN!Xl{qYuWu9L7W`mAb;&Y&FskY$QX41jgW4%}LM-V}6XFg8Ab&mWqT; zCr2NaidNQla`d5M>!D`k-&iUV_PQm<5|)ZqX{Sb*JxeMiY|AD`9}=`e9OSo0kqTn1 z9CPw}A{7#d=X@Ul30fg$^ob%B#HGKnR3s4l`aZl;(aPOv@sXMe2}I;EA0%jnRYofW ztz1s=om46$?8qQvjNgLxTV#38%BJPHd4BWj6MVkFrholqMVU_#6!Ig~d-UDZbFXSZ z3EbPu&E;9@&FjDDH?LcIkN%u{KJ4d+o=qh9uC+SBOq~btm+bV~i|+jJ&s0eKciP-+ zZaqAcs!tHO=+065lAsme88779XU+|ex$o85annaVO@+h;>aF{;Pg5bWfqIYr2mKzs_^{_Q)ZO~wz8(Q9|E-i|mktkm z&+i;wchiSYQ(?ZlsyAOA?2Knp^~n_54(iaK1g-c+j5>klKl_F^Zrv};4Ct_s3W>LG zUy<$n7@kShCv420+q*vrTJb%dLjJ_1JB8o;ZCF^C(|aKm5)Y49on8Ado=MdwgB#1?(mWmkMJZ-z;m+jCfgwUhdjpcUWEDdg8X;_lj|*-OLvuO}^} zLgI%@Hp&&Rz%!}(1e)eOZ|YBiR(v<7kRSENpxOn|72$V%Zdyo%#Q)lN%nj-?q*4!i zE>6>#_w^@1E54gk$bUQF^1;*9`94pLx^E#B66!fp9m$w$rQXBQCjzxyIIlkmTG_FS zZwcwSwn!z7kkL-Y#A5X0<7IXBXHax+gEf~WZQj8$X}*0{B?3p7x4}r*H@FJE<%uK6J!SJ$lTfl1@m!y7J0iwQa^t2(P@d{$Ua>-CX9|hmP3k z-Bk}$Nhid|u)bUC)5|(#7f&PM(#>VQOXw3+(uqnv8l@hpRJ)RJ>E<%u4Ak||gu1!` zm2^UUsP<^Aw1-DHI^O0a*yibk_>c%(^(%FbR}e_6*`C1`bf5QZYKiFC6enCyRQb;l zKjOo^KiFJ$`8Jo`=i(!sFg{p9*LUSQN$stV<#IJxmM;J3#o_nCDZ06Uz>KHo4%`T^;_Pix2N?I z*Z#J*H%ykZiVb1jmNk8sKI~hxrah8`>ANJX@9KRXi4VDJZC);I#uE9x3DX{x`&<)p zZ(AjukZYyJT^W56grm)vD8Hd$d|1Mca8^ktD(#_apikfnWK^?+OPjIXelFKa{tO{w ztyR*ANuFQi8!qKMvZ9Dmd^f;@e6XseK zqlU@1sBzKE=eB3C1tslziCm`&p?ir{;)M0BM^V*p&DC}9hMPIV`g5OG#@!Uc#0-}3 zOC{Gyu9vi<mu@ceWGg)~R0%5Sgj_3WZ;2bk2ML#MF7sq7pP-UX*!Gb8<&T6* z8z1&8EPbEr9%2crq!Xw|V;W&-bFJKoS(Tb=LdtFx@sT9NN2NUygrl3wu{Jj?s0k|R zgt=CF+zq83(i`lI%ocQ?W894qJRi=K%Jv>RGKhy_=*|jb8*oKmDV3P3zhdZC^ghA1 z@qNSzJF9VZ?-Nv*FZ)=_1g*Hb_X#Q_c%@q=XvNjNPf#HtEofr6_|+vrE3WQiKCn|j zf@^i3phAKxZ=aw-g0XMQ1g*I8_6aH^xQ_M-DkK>7w@lEA>u8^#;t^_-F(e^tPHV+=wC{ro39gELf(i+)ajQg8?pls&yyrO*X+l}cpZkRhsUZn>@*nzc zF;37*dcK|Es-@CCsE}}H1;R&ypp`rM4}>lieb7q!v6;^+gtC@D_v;Z-a}w^XK=?=y zv~p(!#tAC!v_BI+c%{;1ikFInJNXYjigAKga<7<34TP=-6%y{`zt#v^xs(4u=u*)K ztr(a3y@U!0cUB;L=z5T#l{+giPEc`Yy)bT%6ZAoXv9DhbDkR*wFL56vXys0EX^o&2 zqh-HTR7ki}T;e`R(8`_3(i%Z47thBDDkR*QEUgi=awn|B2`cW46s|ww1WQH2oqp09 zK`XZsjuTW!xYJKsBWUH$GHH#V757g3UP6V0JIf^Qg9NR(cjEh?LSp~wDS~kyBxuFG zlcWzDf0E!yC@mAT;@*i*P$9vSP+BHv<@RdgHK#(tI|-$-2cU*THLH=JmGtTKSdmhRx0XK=VzqGqEJZi%n{!Q^P&|`5b+5rLfi91n3GSi9<-7fHT_yq zA)(UM_}cdoUv1fSG*>S^!5$}ROYpw&2~liZD+JU3?-))>VhP5m+DB2{8+r$XHDt~E zeAGeL{JYmbQ9BcID_Md{jL^NLN(iKVkWlHW)p1*FsC|Sz|7Uzu2`Vu{(^aBcDiSJP zwQAG&AGJ1UmtA(wCi?pi<)Tt5DltM`br4k_BviU;wQYH|_EG=FmN6exVuYrvMAZig zm9AQybogJikM)k&!T6{WRAPjtt3dwU3!E>>8^Fl^CJvDp4&J36-u| zbt~PXeQfjlUNIk3VuYrvMAZigm9AQKdvCP%G3T;9;y$3n2u)WB=Yzz1qpMb5mG{y< zCf>hK%m)=!5)wLHC8|D1sC3op$nU>UcW}Gx+`IRU`JfUb)F=U>>Vt$zSFN^PGDiF8 zeb#`O4=OQ2(^aDCgM>;~t)BUQ_#7!!5DZj4PT1=xJt{o;_~xOEKIXaU<}ybj`@OpP z{`{BMkBdq#{WRdCtv3#-kno==+UES@qETCnDjoCe#vv6F=e*QO`3{07{_}zwea5aj zDeBaHXlCQO^*n-BhmPD;6Q^~YuZRz(9ua+V&()dpw%jVD6@9Gey`?5PT>G9Pe(H5t zwDF)~X;A0ig;YpztSscGy)`tNzU@_+)9ZKh2wLrP+s4|*{r7#Zd<^K4jrxz6Sh~4S zuaF7}j+KS{TmR@Eb-28knbNDTN6_l^F6&h51;GYwH&QLQ*Bg6B-~DoVX}zukLn(eSaCm2UX>fRG9aj+KRc^N8)D zW&gT76HUx}1g)N}S+0F7XxLu)`1|`?L<66_q_q1fM}|~LaI7rk`~9+ERJY}2nS*va z!6Rt3!Mfv!CbPz3RzM$0hvXet9Bsf+U@{e8hN#m;amt^)h<_wRZ)p_sT zrG4a2?V)@;Gw_|px1T(>^v4~}4ylmfSgC&dY;sfMvwt6x*|yCXkD%4Z{?%UH#arD z_ulEH(#o+R73RyavXKA%JEIzpUwKAm!t~2Mf>v9dx{>yA#Ham~k6mv#tnn|sPcAjS zI6kC8f@5VNzqDbC#&+wUmbv+ii5@|#UjMvZe?PLvW-^MN{p=4*hVFk{>CBz345^Ud zSXsy)GI{)x-Ikox>!?zfmnMZ&NciLKy`3J} zZ}5s^G8=sB6SO+6V?RAMuY5#&+%UP@;P$HzD|J&osF2_|U&tRewRv#E=n5yo*4EUnTsb^5=*cTQf>w1e>{ga* zwce<{%16H&4y&!1KcuuuwK)|MoJ9-yP4>B_wyx~~nY-Hk!6RsO&od`$A8!@*R6c6I zxvBP<7xyc@sroJz5{w4a2)E&%YG2+o%xv@AMIJ$`iQ7%nK3+P$xAJjHpM|xrKGnbU zH+2tEA;GAokni}!YqbmB9+)|2^*KI)XsQ2MbF`1EcI>Kryf^Tj+AGi7tJJ8*5-QA> z(ODt?>o-5u_8GrVX8rR|^$5@E?y~kV^Tp1}$Cl>?;frf}l?JFWu>gexqs&77=I1+v z2YkGHX5GZ2J%U!ZO@2%Jn0xNl%E!;|ZxQ}!|J_Qbs{Kxn09R9F#x6HTQ zhIs_7cK+`g?c=o$n<*dPjoLNrb4KUVb83#FLV{6$A%Db~dxk@M?3DR(T#ZN2>ZUR4 zHwEf%`xgS`7NO{qrBwNyxO^-{=x*`_w^vu@kWpqqC02wKga&|draZvTI) znBfmyvf+Q*cPRZ;#SBzPa1~a_zjo3g;o}c<%!HG6@CaHxanH8e$LHs+Qa*ZUz=yZIz_{9|sZ69gt5w!B3Z~yk_qhzEn)?A^Z*J^pihuxVVUC6)l z#p&U|@vCaiTDGQ^rDDE=m&xf#L2%@w52|{sJY-aO(}>1~WjFu3mI?`;g;vN)dk0e?!PCG~2C5x`sd28w|2-UA19yesr{66D6f>!9E z%E!G&j|*?!_UV0R|7Bbw6%sseP5tWh)JwuYw0rvL*H+K;2wL&4Ex;CH0Yn*UiJuDVT@5eP8mKj zEIin_c+%NlH&P+N^VU=^Q6t>^mpjyqTeij{XoZna`I!FgvEjjgY}fG6giRtUBzWFh zA%D7>4f-FMsTp(jrXE2n%qz;rl6?;iUpZ-D!(JmhMN~-eyfyW!>}pnf;*_&$j-IrG zN6-rMqwwxwm5)#F{YUNE zjhh?Zns|0Zg#^!ZE9Cc4QTW2Y{j282)#rEwtq{*EA9EJ}xpu^J?>Ag9<-&*x37!I{ z#uBxfxVF!CHTykxkw?%9>k{ST_Q!6ly|Zym!@A(Ihzbdw8&}BhqE7(%x6%ssYu8<$ER(ES(-y}2Y$tyg9R#+!0A2rARXYkiy z+tSkAuZ*aW;MsJA{KaaOy6BV5GXo#>30h&jt9<l3uXx?TA=^5LFKPMfoJY3N@kMV^B5^LUz_irdw$!M$JanAvHHPtXec z56Z_uC;hl&>C2r;yLP@ZqC$e_-xcz!)$YeLAMKWT_Kb-hK`ZQ|C?BVsby(wFx9w7T z`{nTw6%sr#uaG}R?cxlb*eg>%{c?|>750OakBPHyYJB(SZl%xO85>a{!87&>`Q>Ui zDz|Wt%$Yk}>JhZUzLxT_=Fz7cr*E)(>C$5_jHr;{seFa}JhiK~;Fi9bA#KKZ1g)?? zr+l=z=IzFY1$&kbIPmO<3JIR$SIEDqb_Z{4H!ySjF=u!Lt+3CieC+t$uZ<(t^({TM z@Z^XJ37+Fu$mi8A=RbPwm$_)S6Fh=e*e_K+t~zCl=$Ow3l%_2@GNM9)=lH3ZLG9Mw z@oAX(bYk8kXoY=W<)hQzx?LYr6qC$e__!aUUt~(<7``(9S zrfs>EN6-r2aws1w2OJlDwBdoJP8~OisF2_}eueyn>N}Rj+Z~qKpl&^npcTGjQ9h<$ zGcvmJ*@H?OT=i8W6%sthPyP1U?6aax9?WMNy1nZWw3_x&&z!8hGV9-_d@MiZylBeS z2bZom@x?|eBzTUW`rhEH3!-Jq4$u7X>N6fetM&))p?%C-cb@XG`iV=TpW6>FZS?8G zjZ{eR24)%$24ubsA!@-eqCE_!?J@X|%EUe-v31W*1e z=H4?Q>iyC1Qd7U~jZ{eRlf=S+qru!6ayf z(nf{+3j-!avqv6W>hk7;gQ<|cpYxfa?SA$MTHzWhAMIb75Y=}bRtjcy zsii`KrwbPHjjGK*yWz0Ro0lB!5wt?@DXSK27#DS#dSGe6h)Zgzkl^Wpg?wApcQyYjm5&Gh zc3w2J?~u~Gnq{?ANbo$uLVkuCOX}Mnl!>~%>k+iVsHA*++;LR&>-eyA%h)e!sgU6J z(e`;C^ESDCrby1FlBaj%2_Z?l&*Nu{P$A*chLE?(v-19V^_?HUBLVVc(&UGi@B9-ans3#nE*+_qm*3QhBRU-ant| zlzPv~KFMW#SVC$}#nE*+_j&w;Ca2VUR`&Ta<3sXRmr8bp11gTL%el`h^^o@<69ld7 z6N1%xR0%5Sg!rhu2dRCKaOt|7eIl*e9`*zQ{WiI6+jPQQD^qK!xy#GsYx}PIywb1q z?zvCY>ynUQPZV2oe@I@|%04wGeb;?%d{hZtDw`Jxmlgu=J6GRvmuupz>T>p(TXU^Q zP)R4`T1k7`_aI5QbY0FqRcw5apps6YJ&IC)5-x3g$lHg;hwZnPuu3|CdgwO}#fpSU zn`0=oVyNk|tr&l~J_%zO%D3rLu(F!SX(ixmNZK>7W1f z*la3}uFJX4yMj(q^P~KE$#@ z6lJWnN;*-gN2An3FcL0pW*m2|D)WbWgW1eDR!JwsM`a#VZ%Lb3%@Q)>n6#POy%v;{ zLUgHQ?z2idVXl=wpWAj8+V(N#qvHz}!Rmdk2^D3`qLNO?wUYLhRy^~rwjSZ=x}1G? z(fF{0l!{6^VcSEjr{T0m_bZb<3>ndkhO1eIAo~0W_ zd1{@bNl3q1HR($2qi)RVCH7=ZIkVfOgFwUBMuvI=tP)QO&V16%kk9pm+k9jjDgd|Mb zSm9({<%0y3BoPGWZ1tt(lb1*j{qVs_AqkT2m(2iz8c9$ zlLRD8+F04o?K$*%t}T)fB}oK_{zEc3v^Ruu6hT zk_ZArU{;Gr*cnHx?EGkcFO>w9B%$tV{kzDRog)%<<`gSC&zs*%B|#-gsJ^Rztr>Ai zM8ZY@VrAnM^NY|Vs3eIXFa)Bdh=h$~#LC89=9g$lP)QO&Us6ZLF;CWK2yZNuz&A87NCN|H#6sY#f$vBGKN$_M&xNF_<6#ndEB+F0S# zbmas0U`QoNq{Y-EOxjrCTzBOIV@XIQNuhR17l)HB}t^k)Fe#WSmE4z z<%2Obl_ZfCQ1)yO(jXB#ndEB+F0S23zQGW z)Krp0T1-vCq>UARfkF9TOid+8q{Y-EOxjrCHzJe|#?(}jL|RNu!laEAeyu|JU`$OV zNuvup@)yYsax< zOid+8q{Y-E>D$37)%BG)YTFi0_%_JKD8*eRFcNJMEM{=B}wS^Py{1n5?Hb1u)c~BRFZ`5SBhYS zOad#f9M*|3f=ZIm_lF`FA(OxgGKckUjG&Sv^nI=fM#vBJ*hh&GRFZ_AKNP_TnFMxNa@Y@w5mb_do(C1d2$=+S zigMW3iV;+jgr3h8!3dcIcD!=fpNkPxl7x;+6u}6Y1a|JKzp1F=pcsK2$Vy2}zSvt- z1S4b;w8DOAjG&SvbeyONM#vt}wwE^VJ$6zjtgdS#(}r0r7+VQVgF+fMS^cRoQSN$3@lBDm{J!nUB~ zYx|q|g**~el7wFADIeT*CSm)o%3e-qBG$Fb`JusU+QZE9i?mCN-B=ioOA`q8&gpB}%w(*MjWl|DUl7!wVR6Y=2RrjeSFB{27 z+Qwbxw@gV;NfLU;Q~5yDSKX(Uylg}!X&Z4$9PATRl7!yhRRnjPZ4@qfVYk-%?Npz@ zu5${(nA%3jB(UpTp|ja!Ol?agN|MlP1Hb0ABy2r|#=0a%P)QPLF*OOSSaMijDIaL_ z>OQqvZBvzzQ;l^{(=PySBPdtyaIeB#{u-X)!ek>`dgakD`1q3a64J(qd{7 z*kQ?GKS=pt6iy{cq{Y-Euv3)7zLxUAD4a@?NQCSUBy zsZuefCP6FgGsXxiNkZ>gDuOXJ3G6`TuwNP@s3eKBn3@E3Qghh%RX!MnQ%MqOF*OP7 z2v>bbBwLNbi!xeULX;$t7E`AYLfiT!V`?haha=KrY7(~QlCN#2WK2yZNu^&&?+WS8lQ&UM2X)!ekJ2FVVb{tE_ z)Krp0T1-vCj&PE%9gmYSHI*ch7E_b3Bempf=cr^%O(jXB#ndG1j3fEl`7s$&Q%MqO zF*ON0*Gj&2o=?WqRFXtmOijW@0FtkbeUdRXl_ZfCQ;xec$<^`K%(QZ1p%$(72yb0>bRkBeWvTH2uM&*mhb>-Ny4azr-U5+SA4S67Il zluj%yX?@6N=5t*t(}H37{AXu6!YCx-rIK9D+ZKEa+Y;uw@!fE-w*=qxCSlW3I^E{2 z5@H44@{YUyOO#6PE`1e*!28@Wf(i*c!>R9XW2GWNE4S|(`4Q)7IB z3JE(Ws&9K^K1k4tr{DNKsF2|POv?nVc#4ltP$9v6rj`j>@iZczphCj>Gf@UdI1;qt zsY<>NDkSW6Gtm-#kf0S$hw^<;A;B}Ne1ZxI+ha_e$WoD@m6_Gd% zQ6YhOrR@74K`YD*F&|V&U?z(BAVDk4E3FW;!dwz76%`VgiHwh;jeSVa3Uf)!2Ne>S ziDEuT(8~5*HAlsKP$7YNCFX+!t?a#Go^(P_45*O6Ok_$`y;dY>W$$3~q!WEmA%S@% z=7R*S>_}vubfOO`Brp@je2}1(9fQ@k4zc#2LIN{U%m)ctVJ?aJph5!kitnRHf>zeI znxkSqsF1+C67xZVR`wdIIV$FZ3JJ_atq`=ryb>dDs$&yZcP$fCNZ9_SX0@0P60~Cd zyiv5sQsHb%uY?|Ea1DkN}TXRIDFo7uc*g;PXZA!vo?4q^lq5;zUD z6@pecqqP-+R(RqfMo=Mvvu0Z%XoV+7VgwZuI5W2uf>t&s`|P>2nOQqiA%U}hTkxUt za%Tru-siMWl8XoH!(J=>R5}QpVI1>8f>yRKm}k%Fg9-_p#T@fNf>!qFltg?LP$7Xc zrDHxw(8@lYVxB!`J*bet+1Rm2h6Jtb(<$cJbNZk{0%v^3e2}1(eLBTFdrluzNZ_pU zm=6-PvQMX&XV2+_3JILK9`iwhRyYaX5JlT}NzlqZ&tjfdXQ`-=z!~^4A0%jHpJz$N za8yX(EdQ7f611|DM9_R1FtR# zTH*6ti$snD((0ETTJS-Fzg!X9Po#I$qjHXheaeD9aIJDAaHX?;sYuWYEgP#j6%yz% zF&`vog&rF7L4^c*e#{36TH#KM`Jh4qcW}%H30h&4Xoa8^Mx|J(sF1*D7xO`aRv2Al zKB$nus2%e`f>xL%Vm_#l!0gisK`YF5F@g#S%%ZIjw8E?%BdCx-G|&n`D?}eLf(i*l zORW&JLfjQ2JjF#zIYeiz5VW#6*(W(<{PCkcQAi-lY{7>Rw6a&kJjuZkjtU7x%P}7$ zXk}a0Jjp>HR7fD|kNF@$E37P9A!voQPmG{K0xP6e2wGtk)(Sx@?q&M(2Ne=nS;l;j zpcPiHF~aT?$&RVrnUd!jm{ZH8B4PK=%#$2!bLK@Wy9a2VFCjt2?ncTJDa@(mQjxIx zrRG^2_$V?jTG`!N^MnwHfCQ~nERhpC(RHbc_O7Kug3lNE^&mkjKK0}iRQUWAbMgr) zBy3u(6Jxz3jSwsATdfm)qDaNoL*9>JPA!*;guQNRb?5u=zN4p=ZO!C&!hF|`@Aj!u z*?2eUqpFawIVHdOVP3T2^V@#Ssqjg1=HwGpNZ5!o`Hcw)T5;s_e5gKL-6^U(&2OF~ zvJq$EI~H{ZID%F-el*WtDKqtFW;H4{0+jKCIeDd0r1L?-Mrz5igaoZ@+-07i(}k*( zii(Z3Wc*-GEtiUfjiQoc3G<>AXFK1AZ8iCR*|xcS56qVJ2`VIPe@lMDOM+ImADbup zd985YRn8eOrv|8;)b*0Tloe!E!F^si2_bDaig{sNX~KBb!Hfv`;hBhRY=$|QT5|i2wGtl^@$=C z%+yWv?O$CgBoGbwKD>I+3K54Pij}dXNCi>NZ!8rF`^}abMO!Hqt@w?AuDLB$VDB9H z_675Yw7Dvo{H5F1@#${BR_geFB4Q|7@Y%$&dRT9L5rk=l2w^`I4dk6&{tBy9U9za=9 +#include +#include + +unsigned int oldVal; + +// Define variables for timing +unsigned long lastUpdate = 0; +const int updateInterval = 100; // Update display every 100ms + +const unsigned long rtcSyncInterval = 600000; // 172 seconds in milliseconds +unsigned long lastRtcSync = 0; + +unsigned long lastDebugTime = 0; +const int debugInterval = 500; // Only check RTC/Print every 500ms + +//motor values +uint32_t totalSecondsinYear = 31536000; +//uint32_t fullRotationMotorSteps = 194400; +uint32_t fullRotationMotorSteps = 183348; + +bool isCalibrated = false; +bool homingComplete = false; +bool realTime = false; + +int count=0; + +long targetPos; + +//inititate EEPROM with preferences library +Preferences preferences; + +// Instantiation and pins configurations +// Pin 2 - > DIO +// Pin 1 - > CLK +TM1637 tm(1, 2); +TM1637 tm2(5, 6); +int potValue = 0; + +RTC_DS3231 rtc; + +AccelStepper stepper1(1, stepPin, dirPin); + +//define state machine +enum {calibrationMode, manualMode, realTimeMode}; +unsigned char machineState; + +void setup() { + pinMode(buttonPin, INPUT_PULLDOWN); + pinMode(motorEnablePin,OUTPUT); + Serial.begin(115200); +//preferences.begin("persistent-mem", false); +//oldVal = preferences.getUInt("oldVal", 0); + tm.init(); + tm2.init(); + tm.setBrightnessPercent(5); + tm2.setBrightnessPercent(5); + rtc.begin(); +// rtc.adjust(DateTime(2026, 4, 1, 12, 0, 0)); +// rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); + + stepper1.setMaxSpeed(4000); + stepper1.setAcceleration(500); + stepper1.setSpeed(4000); +//enable motor + digitalWrite(motorEnablePin ,LOW); +} +void loop() +{ + +switch (machineState){ + case calibrationMode: + calibrateOrrery(); + break; + case realTimeMode: + if (digitalRead(buttonPin) == HIGH){ + tm.display("real"); + tm2.display("time"); + realTime=true; + + updateMotor(); + Serial.println("Real time switch"); + + } + + +if (realTime && (millis() - lastRtcSync >= rtcSyncInterval)) { + lastRtcSync = millis(); + updateMotor(); // Calculates new targetPos based on RTC + Serial.println("Periodic RTC Sync Triggered"); + } + +manualControl(isCalibrated); + break; + case manualMode: + break; + default: + Serial.println("Default Mode"); +// first check if theres state in memorty + if(oldVal == 0){ + machineState = calibrationMode; + }else{ + stepper1.setCurrentPosition(oldVal); + isCalibrated = true; + realTime=true; + updateMotor(); + machineState = realTimeMode; + } + } + + + + + +} + +void calibrateOrrery(){ + if (digitalRead(buttonPin) == HIGH && !homingComplete) { + tm.display("clbr"); + tm2.display("set"); + + stepper1.setCurrentPosition(1500); + + updateMotor(); + homingComplete = true; + + + } + else if(!homingComplete) { + tm.display("clbr"); + tm2.display("reqd"); + manualControl(isCalibrated); + + } + + else{ + + if (stepper1.distanceToGo() == 0){ + isCalibrated = true; + machineState = realTimeMode; + tm.display("done"); + realTime = true; + Serial.println("DONE"); + } + } + +} + +long currentMotorPos(){ + DateTime now = rtc.now(); + DateTime newYear(2026, 1, 1, 0, 0, 0); + uint32_t nowUnixTime = now.unixtime(); + uint32_t newYearUnixTime = newYear.unixtime(); + uint32_t secondsSinceNewYear = nowUnixTime - newYearUnixTime; + + uint32_t motorPosRT = (uint64_t) secondsSinceNewYear * fullRotationMotorSteps / totalSecondsinYear; +// preferences.putUInt("oldVal", motorPosRT); + return motorPosRT; + } + +void manualControl(bool calibrated){ + + potValue = analogRead(potPin); + potValue = map(potValue, 0, 4095, 3000, -3000); + +if(potValue > -900 && potValue<300){ + }else{ + stepper1.setSpeed(potValue); + stepper1.runSpeed(); + realTime=false; + } + + +if (calibrated && (millis() - lastUpdate >= updateInterval)) { + lastUpdate = millis(); + if (realTime) { + displayTime(rtc.now()); + } else { + displayTime(simulatedTime(stepper1.currentPosition())); + } + } + + +} + +void displayTime(DateTime timeObject) { + char timeStr[10]; // Buffer to hold the formatted string + int h = timeObject.hour(); + int m = timeObject.minute(); + // Determine if we show the dot (colon) + bool showDot = (millis() / 1000) % 2; + bool screenUpdate = (millis() / 500) % 2; +if(realTime){ + if (showDot) { + snprintf(timeStr, sizeof(timeStr), "%02d.%02d", h, m); + } else { + snprintf(timeStr, sizeof(timeStr), "%02d%02d", h, m); + } + + + if (screenUpdate) { + tm.display(String(timeStr)); + tm2.display(formatDate(timeObject.month(), timeObject.day())); + } + +} else { + tm.display((int)timeObject.year()); + tm2.display(formatDate(timeObject.month(), timeObject.day())); +//tm.display(timeStr); +} + + + +} + +DateTime simulatedTime(long motorPosition) { + uint32_t secondsSince2026 = (uint32_t)((uint64_t)totalSecondsinYear * motorPosition / fullRotationMotorSteps); + + return DateTime(DateTime(2026, 1, 1, 0, 0, 0).unixtime() + secondsSince2026); +} + +String formatDate(int month, int day) { + + String months[] = {"Ja", "Fe", "mr", "Al", "ma", "Jn","Jl", "Au", "Se", "Oc", "No", "De"}; + + String dayStr = (day < 10) ? "0" + String(day) : String(day); + + String monthStr = months[month - 1]; + + return monthStr + dayStr; +} + +int motorStep2seconds(int motorStep){ +// return motorStep*1460.0/9.0; + return motorStep*172; + } + +void updateMotor(){ + targetPos = currentMotorPos(); + stepper1.moveTo(targetPos); + + Serial.print("Target updated to: "); + Serial.println(targetPos); + Serial.print("Current Pos: "); + Serial.print(stepper1.currentPosition()); + Serial.print(" Distance to go: "); + Serial.println(stepper1.distanceToGo()); + Serial.println("RUNING!"); + stepper1.runToPosition(); + Serial.print("Current Pos: "); + Serial.print(stepper1.currentPosition()); + Serial.print(" Distance to go: "); + Serial.println(stepper1.distanceToGo()); + Serial.println("DONE!!"); + } + + void updateMotorManual(){ +manual + stepper1.moveTo(97200); + + Serial.print("Target updated to: "); + Serial.println(targetPos); + Serial.print("Current Pos: "); + Serial.print(stepper1.currentPosition()); + Serial.print(" Distance to go: "); + Serial.println(stepper1.distanceToGo()); + Serial.println("RUNING!"); + stepper1.runToPosition(); + Serial.print("Current Pos: "); + Serial.print(stepper1.currentPosition()); + Serial.print(" Distance to go: "); + Serial.println(stepper1.distanceToGo()); + Serial.println("DONE!!"); + + } \ No newline at end of file diff --git a/README.md b/README.md index ec47821..a83908b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,26 @@ # OrreryDriver +![Human made](https://img.shields.io/badge/human-coded-green?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1wZXJzb24tc3RhbmRpbmctaWNvbiBsdWNpZGUtcGVyc29uLXN0YW5kaW5nIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjUiIHI9IjEiLz48cGF0aCBkPSJtOSAyMCAzLTYgMyA2Ii8+PHBhdGggZD0ibTYgOCA2IDIgNi0yIi8+PHBhdGggZD0iTTEyIDEwdjQiLz48L3N2Zz4=) +[![License: CERN-OHL-S](https://img.shields.io/badge/Hardware%20License-CERN--OHL--S%20v2-blueviolet)](https://ohwr.org/cern_ohl_s_v2.pdf) +[![License: GNU General Public License v3.0](https://img.shields.io/badge/Firmware%20License-GNU%20GPL--v3.0-yellow)](https://www.gnu.org/licenses/gpl-3.0.en.html#license-text) +[![License: CC BY-SA 4.0](https://img.shields.io/badge/Documentation%20License-CC_BY--SA_4.0-lightgrey)](https://creativecommons.org/licenses/by-sa/4.0/) + +This repository contains all the files and documentation for the LEGO Orrery Driver Mod! +- [Visit the DIY guide](https://gorkem.cc/projects/LegoOrreryMod) for details. + + +File structure: ++ `Firmware` contains the code and necessary drivers. ++ `CAD` contains print ready .stl files. ++ `PCB` contains gerber files. + +Components list: +- ESP32-C3 Super mini +- DS3231 mini RTC Module +- 10K Panel Mount Pot with a center detent +- TMC 2208 Silent Stepper Driver Module +- 2.54 PCB mount DC Jack +- JST-EH 1x04 P2.50mm Horizontal Connector +- JST-EH 1x06 P2.50mm Horizontal Connector +- B3F-3152 Button +- Male header pins