From a43d176238f6bb71745d0960e2632f1c60000ed6 Mon Sep 17 00:00:00 2001 From: Alexander Begoon Date: Fri, 26 Apr 2024 12:08:27 +0200 Subject: [PATCH 1/9] WIP --- .gitignore | 1 + checksum.h | 3 + icons/Intro_128x64/frame_00.png | Bin 0 -> 1368 bytes icons/Intro_128x64/frame_01.png | Bin 0 -> 1632 bytes icons/Intro_128x64/frame_02.png | Bin 0 -> 1812 bytes icons/Intro_128x64/frame_03.png | Bin 0 -> 1860 bytes icons/Intro_128x64/frame_04.png | Bin 0 -> 1700 bytes icons/Intro_128x64/frame_05.png | Bin 0 -> 1725 bytes icons/Intro_128x64/frame_06.png | Bin 0 -> 1684 bytes icons/Intro_128x64/frame_07.png | Bin 0 -> 1590 bytes icons/Intro_128x64/frame_08.png | Bin 0 -> 1598 bytes icons/Intro_128x64/frame_09.png | Bin 0 -> 1604 bytes icons/Intro_128x64/frame_10.png | Bin 0 -> 1612 bytes icons/Intro_128x64/frame_rate | 1 + icons/Meshimi/Meshimi_128x64.png | Bin 0 -> 4546 bytes meshimi.c | 138 +++++++++++ meshimi.h | 41 ++++ meshimi_types.h | 8 + proto/spi.pb.c | 48 ++++ proto/spi.pb.h | 389 +++++++++++++++++++++++++++++++ scenes/meshimi_scene.c | 31 +++ scenes/meshimi_scene.h | 30 +++ scenes/meshimi_scene_about.c | 54 +++++ scenes/meshimi_scene_config.h | 5 + scenes/meshimi_scene_connect.c | 29 +++ scenes/meshimi_scene_settings.c | 38 +++ scenes/meshimi_scene_start.c | 68 ++++++ scenes/meshimi_scene_test.c | 161 +++++++++++++ 28 files changed, 1045 insertions(+) create mode 100644 .gitignore create mode 100644 checksum.h create mode 100644 icons/Intro_128x64/frame_00.png create mode 100644 icons/Intro_128x64/frame_01.png create mode 100644 icons/Intro_128x64/frame_02.png create mode 100644 icons/Intro_128x64/frame_03.png create mode 100644 icons/Intro_128x64/frame_04.png create mode 100644 icons/Intro_128x64/frame_05.png create mode 100644 icons/Intro_128x64/frame_06.png create mode 100644 icons/Intro_128x64/frame_07.png create mode 100644 icons/Intro_128x64/frame_08.png create mode 100644 icons/Intro_128x64/frame_09.png create mode 100644 icons/Intro_128x64/frame_10.png create mode 100644 icons/Intro_128x64/frame_rate create mode 100644 icons/Meshimi/Meshimi_128x64.png create mode 100644 meshimi.c create mode 100644 meshimi.h create mode 100644 meshimi_types.h create mode 100644 proto/spi.pb.c create mode 100644 proto/spi.pb.h create mode 100644 scenes/meshimi_scene.c create mode 100644 scenes/meshimi_scene.h create mode 100644 scenes/meshimi_scene_about.c create mode 100644 scenes/meshimi_scene_config.h create mode 100644 scenes/meshimi_scene_connect.c create mode 100644 scenes/meshimi_scene_settings.c create mode 100644 scenes/meshimi_scene_start.c create mode 100644 scenes/meshimi_scene_test.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/checksum.h b/checksum.h new file mode 100644 index 0000000..ad13698 --- /dev/null +++ b/checksum.h @@ -0,0 +1,3 @@ +#pragma once + +#include "lib/libcrc/include/checksum.h" \ No newline at end of file diff --git a/icons/Intro_128x64/frame_00.png b/icons/Intro_128x64/frame_00.png new file mode 100644 index 0000000000000000000000000000000000000000..77b531076df19b7a04056d7ec88284c29ee33421 GIT binary patch literal 1368 zcmaJ>e@q*76hAD#$1f9?fm_&lB#eb!?^nyUCza7&8Fec(r5V)($M^2Or4{b3UJqLc zZb;M#aT|#yBN`n{mY8g!G25IP1I8?7@sF8kV&-&dVsviKFwG<;1najzaet6q?(TiR z@80LV&wJnZZamPjue|ipQVhe&eeGfp$x-y|Ew-TdFU2SPNYt3#u-T2u6bpsTu>XJ{>#7xGZLBFZj?fs^+>(j|xgVOi1p8u|i&*?@ zp1@;@i|DrdX@A@Uqp|j319lB}gyi9V*{KjMPvFfd0Xa~i3GkFUpe2Nqi&*t5pmkPF z5%{W!+3zCONQM0Y+@l*1=dBDW(=?6qPAgOZD{&DZ@4;D`Whe&SJjw6^%L$AVU%voPA5gP6w8vxf=mo)CPtWPK1Wy zkP5qKPqAm}rP&0j^i$GtjN=y+P07?J=iInXO zL$c5&lR%1aq~xGw;81|f@s6CI&G2?xYpdJMb3EJTU>L8*>2-@F(V=&NW zW3-@#=E*>VGq;QbG<1c510gSxnM~%)wNpP~SYFsCwuVwa+?l#w@g-j7`8kuh_wejG&>WbN^GdD&mUcXv1T{-jDQ_Ucsz|W3J%eAv7 zUio*a)_%DEcG1HNOEo$sR^gpTJT0T?$qVC{R`SSPLp^r&^O=$HylFDMeEof&fAXs< zooye~7dZBu;^x2pbVn4cSWI$li}GQK5L_Kipn zbp{91D*^TB%iEV~LglS5lzy1LSk*UFd24@B$s1Uc%d)iCMQ`gpUut&T$d(ZxN%2swiG*YNp{(1MF m{QbNoef)R=n>%@V-)J7T*jVI!`&-GA{nz=t9paq3ckDkK+0xqp literal 0 HcmV?d00001 diff --git a/icons/Intro_128x64/frame_01.png b/icons/Intro_128x64/frame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..b53437265966c6367911b3c54d172284822e39c5 GIT binary patch literal 1632 zcmaJ>dr%W+5I;yntOYgyG!7bD-jbCAVLr!BdJp4%iRS|NG>D?iBKvc z(~_a%V{|I@NvE~GT5DT;*HK5OVzo-^qoPGeDivxi{-KtNN-sdHe>mRU-G1NR{&s)6 z-|qQxveP5O;=&*ZiZo{Eb46JzK664P;=3z!l}Qv61%pM%<86WmxL8Qb@KzQ!+Ced! z%K~O;`587Df+Y2v*&SrpCNWx#JE^V*#v8iJBjyiP!uvI1;ni#dk|={<4`fjLHl zRIs~I$gNR>b498F^z*+$S zR+LV}ag<<{GzzRX1!`4cG*B^sR*Yjn5;;Go$+Y)*_lj7@qamP&Q8K;T_wo-YeDLQV-U@a_`c2@?t#1}Efm4uISK5$a zJo7^FhWb?0h)FmDTqq1@ZHY9NgtDwsjnbqNN39BsKy82|6$%VjiNO%LNKx+Zn-}^h zpc0$YzkEIz;^z5iu?}&bT;kv)|AvH$Ll=^hWj2U%U|?Wd`_>K!3NAA0wPx?NzbrwH zdMu)GV4%l1vZZo^E;am@X;jZC++Ke1YW9*xU-30}7u|gPb$GA-#KxcNo(Az<(${A6 zY5u-@*_}|_oKg68ea*@Kbkp%?pQL>8;MuCCw)C*-+mc4fI4CUYhn8c@cEqjx@_O%f z-{P&+T@C%0W`;gel|OlUJ(sAPvSi)l+Q{pQO}AHf^vOmxwǘR?xI&JHpJ@9)bR zC=9#b5S$>1$?R%X=%k>wXJt=jYkl?VwkK;%_Lj%*_wJmIKpuBz%)`}37M3>WpMKW3 zsKJ#zcJ0%sv{6N1f8>X4%+c3tyDh)3*sS}!#H5}enc!O^jZsw2Y8u(rQ1h^`A$&tH zyuR?8>IMJqoiYZRcIE7~Q_Yd(HMNp4r%uO572Ut1?eJ}hZPe~Q+jLRelsVgdBP0A! zUYXB__pV8~75(eCmn0=Eb2{IA??=)jyL&@=W=sEgZ{|R^_fY)USX^ECaQle0Ymy3z zL+cis{#g-UP_-Od-bXWy#^h}=Co8dp`jBgv57{c3w@Z&k6yz^B{6_fqCmv6#A+6!_mmw`DBJSs`kGG6H@+k;izOVnn=yUTI-J4#wJL2FU z+`^Ufd{gG7H-$xcQ|AU@S9}|ZS(|rw>hQ^%Vx#wT9{4Fex;ASjhkSf~mq(7hHKI9l z-?_-RgWZ2(Nu#0jDLc0AEr-?xO)}rQy7QwFyr6zb0v#RO!cpxvgW5Fh4+y4pOvrbW z#&>r{g`XS;6;01SEa^C{gBF@&A*fmkUC895(u2S&; literal 0 HcmV?d00001 diff --git a/icons/Intro_128x64/frame_02.png b/icons/Intro_128x64/frame_02.png new file mode 100644 index 0000000000000000000000000000000000000000..9623af7d87422a4d538f5b2359bf5a59d7651444 GIT binary patch literal 1812 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 literal 0 HcmV?d00001 diff --git a/icons/Intro_128x64/frame_04.png b/icons/Intro_128x64/frame_04.png new file mode 100644 index 0000000000000000000000000000000000000000..677a3367e8d4ec943c88097d48abf95fcd29510f GIT binary patch literal 1700 zcmaJ?e^AqQ6fYG4QDCSjaHyd=C)2b^T1b;?q4WpPjUr-I1azcn3KiOvHeE~c2QqZZ z6#O+Ox|^anCo1zimP;@qYdox16qa-vf=C&FMUQ0E_>xg_uVeR-evKJR_s zyR1!5OAZQ*4CHV)LFyD`1}jV1M;0)Geg7U%rC~(`qsnCF(gwysSSe1Tp4L%-+C=10 z85E%}E^4DB9L|Vxqc)St)TH7hZQ>Ii8@|(IVbL6pB*AGR$O4K1bX11VI3b@hLrl=!6D>oCP2d0t;aFMnM?G5g`s^!0^Rm z16uV4JVTi@9FE;ddHD=u!36?`!@+k5`Ls1p0Am;?fDi$KfUE^*D>gHP6ExeVdOav9 z8)-FK7$a>4JRS)hZD*uBmh4a#CW}V%!m-&lTr0L}0w-Y+z%Pw6-xB)c-VI zX>HRMTPQ&WWuxs@l06c`R4*rN?41*L1mY9elNn#jQDKM2>sZzxW zV==i3ktaf4tlDg22s24}V;kAn!&vOKSX^PH2!^(5X}Zwc2cs>QGQb5^VCQUI-Ef0>%)r z0fI#a-Y{POQt^iRl-00FSiD?t2&N3NBoqfyln4V!R0M-MA%uVi0!4*F2o|%!cmkH9 zz|%J`^^sS_Hm7I#Vlddvi_xOY>^xc7!Remy^-^}|{L)jkDpn2*4D5C9{Ds5e)~c0> zT4(3|h1r$gK><+@R^0o!PTfvI8)~~|FWTQV{i~Hee)mT%SA_ITxv^!S|M~hG$HI~x zcT14SGeG~8lwUttitUUFY5pi;yW9FizQy^E+;w~G{K(DIc68jC+%YlmOaE2DSr?D5 zY&BJXQId8!yS)0%K;6EqWm^vCwk+C%GRGQfKMXx&xw^5(y>(vKu8h)_it`gE#INQ4 zwj_7D-%m}MOUU zdRcY)y;C!bi$?vc<(4&m+*h|75SW9^ilbZ4ge_awy(x^B=lFgS={puEgyvLt&j`Ol z_pV4GDih*tXiWO(MI z;Lf%U0XY!?KBm-$1I{t$Lf81eu|3eS#7EJ(qoX6ZtSCXGYJKmmgQG*QuacC@7W-DX z`X$`IuC-6mS&R?5Co%rnzMRg`)j$4V4|w+PxvQ0Je;=$WTfDQjnz<2D(Zu~cKXpS8 zx?=w3ngREy{T1s{e6H_k7~L8-xv#TnT*=ejO+||#*F8+;8m*jJDmfh-u%NlB|5n*! z-SRbbH1upt^R1BN(g)!xS(fNH)!h<^6|AVwE6wY)AWaoM=1a+`*{vmGN~3(19TK&1 fou7qQCiwVqK7AOxBD>a-==pD`RcXq4d2ZQ%RqlP+ literal 0 HcmV?d00001 diff --git a/icons/Intro_128x64/frame_05.png b/icons/Intro_128x64/frame_05.png new file mode 100644 index 0000000000000000000000000000000000000000..fb58fed1e0fbbe7bed41ff7ab0e676ae7067edaa GIT binary patch literal 1725 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 literal 0 HcmV?d00001 diff --git a/icons/Intro_128x64/frame_07.png b/icons/Intro_128x64/frame_07.png new file mode 100644 index 0000000000000000000000000000000000000000..4f3dfc8c4a875a8b9eda2c87337cb46d984a93b9 GIT binary patch literal 1590 zcmaJ>eNYou5Pt+eKt#b>N3r60Vv*|Q?vlVIXCj0oV5-3sV~uvKUhXcCf!xL9AOW=? z#j!Hh@3AOV)M~Y@1E{q_>lfN~uyrh08B1-8jOZ|m)~eY0Rp|>5>mQCccen5D?Qi$D z`*v@&H7hfENZb$rK(u*=F&oNK_@qUI!}qm_brvYbNG7{9M|4Vl#>)deCpvh<>}GQK zY@XqYmi)kL0SGU5+3b?tGK*$Kx10&u$OCQKL_IvvplXz0MrOAHcl=LQnW%zlNzM|LSaBJ z=cKcZ8U5klPKV}7l844|zuzzSE9Ig$2PZWe4NfRXn6F5Xm;fgDCWbs1c^~U_ zc_f!8AVH6eLoASVC?wmLh1+AXJaa7g`fCNNh6flAPRa?~?GC2aYweS=`TuFW(AsA! z^6+>z?-L8WEIbnD#1I&k@8yAlhL8 zU~Do;VhUcxVvNJ7#2jjZWz-zQDk*iy&x`Pe6eFcJ7*Z%jhMFWz28}7*XfmZJlQrok zMY^5{VabaO%(G(jbP0cCDE5FdJu(DP9T%# zxdcx1`;vlZ)R(f6_qqysF2gIjk=`QHt``t+vMLot@hXg=92BNv8JIl9DKW;$kQ#;B zNsuZh+K=a+D_&opLJds96XHS>B=1aSi4=_IRT_+?R3zq55(>=8P?SXxtnYKZb62{rk^X?YV!p^-x&gm!oa>Yy8WPJy1M398q^5547C4 z(=`pGR&3LMcXjcZs!O%&TUU0T9UD9OwB@P3et@<7$a@poKcu7XkC(Omybc7eJ={95 zeVAT`oY3Wg%ij#!8CQz7e+E?1k2v+d-@09!H^lt)TH2jsceBLCC1C47eZ~&3zv!t} z_&4Eh`Hhd>SvsNsSpR8Dyuz-ccFIjNa2a%(+3mscgKUD)(#(t zw8@O~>&LHHfBxd!(XU?lRI{OFd+U~6#H8u9DWd+|)cGYU`CDfXm+#1=%jz6d{CZe-=ON2ixnVKdliTcNt1Fg*&2?Ebx{I5+ zgpH|Do0=|$7h61qiQNx3j(i0l9JO7py?yRc`IK?OnrzMDL2(Y~0!7l1CXt2ypeH8>Q% zb;N+`8eso=**r(niYY6c;q3Ck&G#}t!8=PTXR2$!mp9!Qduq?dd+#O#l~Z%5`#E*!qQWH533d`rguo=FX$ X{(3~&sLLIZ!9Tg#lx3_*UtIPdOIt_2 literal 0 HcmV?d00001 diff --git a/icons/Intro_128x64/frame_08.png b/icons/Intro_128x64/frame_08.png new file mode 100644 index 0000000000000000000000000000000000000000..3a5a28805a856668c4d05c0824cb8253f9dcc732 GIT binary patch literal 1598 zcmaJ>eNYou5Wghyr80hi)uN+k)Pm?GcbC8=R|142NRZG>Oe<&|xm;c(1G$U2qlst{ zg&DPek2;Oj7O=G++G*`bty2`y>Qt~++WJMO&QPpcuo}l1Ypr1V0>t`<?RQM*LO_6fcXW5^(|V68(%1AbKb70I1o+ zECV*cI9Duh2U!Rrt9RQ=#1cyp&GH@v6R}YQJYI-KkgS}5mtiY_h&sSBH>Z<_T29DO zw^Juym|?*!UIQq17gYPeg6d*BTV26wo${P)G%G+u2Oc0YXuwm+33NawAM{JZbwsU{ zqk|@5g-$+1s>EVN4ZIJal!Cxm9LG^gs{l@hbf{bmI~OH!l28)xrZ9q{Nfk|K(cw!D z1NxjU+Gflj4hMHSdATThX{FNd_bdD=1@Bv?B(z$s5+{`;i9riYSiy-*0ON!iQ4dBS zus*j}bn_e<@yIy%Dp4ngWQVfwcrBKvjyYktR3S8;&L{b~H7DOBPpT={o z1^WswP}+dNSNT|YB(51zFf8AT14RrW8~Qb$8=ebOY2?`|58yoD8d?G*Lg#!y7VMjOFe&WpTI&U;Z(rNGg&g>KHt z`-Rz&Ga5?S2z>6hfHU96d(gom)9&XGaItDNMFBO&P!0-HvkXk0a;h-K#SmIjZJp~YUy*edrd-mqm19gXFNgFzmz6J_Ojw54`LgeQ8`Hz=h&riUQ zeIhwqg&da#&Zi~VGJlX*XT9H*b9};YNgd5E)&668*zwGD!Iw&Nb^`))|R<#+{L8v+j=E&C@}2K>3MYP zQJci7HLb)h)i>V%>Q4XeCl>w8&D}+&ZVp*J>9$aIPMkaK?O+EV)5%;v7y4=4f|^g0 zuYNwpAC?@P+AAFEq4q78xgNA1UN;J>QS@Gz)7Dwv)W7P~rh6y%CVaTqJ1w`!8v8ZU zxW#!kRO%8(m#yUN-8H3gmGa15eNuDEJ;zw{z^vL*;@!H=V*U13t)x^_c(eaaF)>%S z>vYqP`&xy$dt$8N^u577^I~_n3L6H@PwofvTf(UldZS7gKDvW{W&J$i@ZVL3T1QMm YrcO_Iys{zVX5^1AGk|J(*OVf literal 0 HcmV?d00001 diff --git a/icons/Intro_128x64/frame_09.png b/icons/Intro_128x64/frame_09.png new file mode 100644 index 0000000000000000000000000000000000000000..76267a2a8d6689a04241f432e55501ae91231b2d GIT binary patch literal 1604 zcmaJ=eNYou5Wggq5*0tH)fA~7hYsp+$=xMzNhC_h1%V-m5eKBT<#M^00LjJNX#%w6 zL~*EJwAC^|tx&1ejx|$T99yvpezdgJ4q98Pt$;)Ahg#IBR2-es7a-O@9B=M!-`lsp z-QVupeLW|8an#u8u@D4BWu#M9AVu({M9RSbYGjQCi01@zzOamQ2!7hfLPk4hW8n-p zy^6K6w7qoo*K9Hb$u_uh^M!m%mY(6rz^e8$1W?&&SlDq8J!(jv=B}X=z#+_E6{MjUE<;O0RuAZR}a=9 zwF-fUO@v|tGD0ffk^`GK9}AO89Az*JgGrr|wbO)6?Vya^sAMeZ2xZLP!R+QEKt7GBhUfV!^ed^ zP%O{*TwcM&dEk&o+QzLB3Wo?u9O)IA)ET^#~c10RbmUyUVWE z(yWc9X&cHU;y6mM8V03p4mE1iVhpXd(~O$bhW$JaZ%U#_t;u92i8L*an@u`%DrGh& zsS|anW+K&yg|QhPUZ6b;8;tJI2etFBv}nglQt67Ff>S>w5w6tLE}0? z>%ef00~y8JA1U5Qp8^d?!W-tI$8gq>$Y4n*%W8BeLuzo;rp5@=L6f9fjp14l3{e4! zs!-oN(nnzxXwJ~`;b4Hx!_i_rV4i$naBABQE(Svvk&~5c26AXNb1T^#gXkl)z)r?=GDGjbhag<;X+I^Ym6=HYV95f`oAcqZx2a(kL`jA zr>s0$L{0pyV0JGRm@8{^D*hcDh-o7WYI0u`+Yr<;XkD>Bc%qXNp|hDh*D`Ll^weakq7Hv%jfw#W(x{ z=>21dVzb(Jb&K-P`lF*|_1(Sw2F1rMeMe`;O7=9r1CxGOXl`#sJ>e>%0zNo z{oWJK{^YpC8Xi()+wY(6oPTMVVdBL7FY3A`96tB%M(4hThr65ka}VURX5_fG22Zb! zcq^M47(6xZcJrzFd!+pM4pEviX5EQh*}FXl4(^5Isfc=M7VK`2DB^yND1>?ggrqn2 zLQJ|$tT-#tMy-|?>SV$AowIgI^S@8AXPU*T`EN9Lbx!TG47NX={%ZU{f_Tqb{pHHo z*@Xqy$CN3mFIgKZtNdrl?MA6~-iAGQ?Z}O496dg+|aOkG? j&5o)3_*?4sMo0#=U6ZG6`PBUh=qxD1oJ|E&3oHKv!xl}j literal 0 HcmV?d00001 diff --git a/icons/Intro_128x64/frame_10.png b/icons/Intro_128x64/frame_10.png new file mode 100644 index 0000000000000000000000000000000000000000..bda1bf44ce0a0cab9a03d15fd284b709590f3b4d GIT binary patch literal 1612 zcmaJ>c~BE)6yI=2qGGYkSZLK*mQuBJv%5)PlP!USBxuxN3{-Kf%4YYAte9-vT}r@q z07osJIOy08Whx#_J*u_UX%#K!sJ-lXRA;0Y?dWI~j}}`UmC`RjtbaJ}?0)C_z4v?9 zu5ss2jUV~qNCZLRozv_dI9J2ZqK<~+6?L5pPRWX+NGT9~N;xY5#LA0afI9u`V&DNR zU$N|KkcA-8D}}-$rN}jt;Y7cd4a;al{vbpnNLEfL$a1AXLA_wH5HMr6kF{Z_z?-qT z87|Tlw1E;~TBQUED)S4u%2Lk6V>#JqR)~QL{6JyRkbh}FWh1MvVxuL4wkfgx?=_ZBSZPJm7yCPnDJnD}sRV z09h=PIG7S&Y6J|!_iRI9LCA)gB?&Mu>{7ePmH9zHaoWuoywUOk&lp+YWm(pXa~Twc z>wtm7S+7rzdyOQ=8hMt})5eINr{PV8JcrSdLDG3NNl^}))#|Y4*^MTf&2BY19QFv- z8ITn=z=4Qu0ooqI8uFfrWo!~)6;Ub_#ifx#aF>XRD3^#qRHxUH=;XOVfEUZ&-y0-Xp(xG*FId>I^>i34CT;T&zCaIc=!;XamzW=P6tFwhtQDH7qjd7_RYN3b}< z%g2oYH;-Ej1Ykc&uyN|D@^`|f8{wW==z#OUz(Dh*Z9gMO)J&({S{Ul?Ez)f4BGoGg zsw$gzeSWkf(6}URzWLmo_{1b%5c!_D@$n%zjjp3!yE2Vq!)Av$d{+5rR!L=3i)*VDMSr|DmPI(qrLg% zbd@mTSlqeMhu^*06+@(@T$sIAq>$o6h4Jku-hL#%!+du?8SVZfHz_x!IFxHl**QD) z*8UXDnjVvFEPf(;!irny#qOiFo`Qs!-{=jea{qdC=h#V^+PKH|ilMG$>;t4_OUIp4 z3xrJvKDw@L>-?tmBEEAT61+5t-gh=H?ZbhBG) d`S;9kt5=&x)v>U2xoy;SQCYg{7Bs{9t z=T@b#1wFK4eId5it*CXS6^|m?)B4tRi@NJ;wTc|D)ZJRKTKAiP2&X-#$8)y-k(|kV zk9&Xj-rv37J^9|Um?sa78W{ya&`@KhAsf8sgC{it{6OFDg*Sp%Q?b=1W>bFH;}tmG z$--ihhlSYy&p}Y&yvx!wEObo7{hW*`mefPu#+rAH;p0V|(Y~ZnE=f%geRz-e1~&XrHI>O zc`&F7P58vHabwd|%@(&IZ=$%6qZiNEG-I;1`0SX%Sz)X8cC0Nq)v?BSY0N?MUlvT- z;2e5VJLF7N{iNDOug1^d-YBb$?%KV1P$2Z$`P^9KLq$9Ug(UFl=@w&p`lDb#2sLXe zwohrc#4g!)W&`rs2NAzpI^^R)!;W6d9=>2*3_a+hocC&NS@z8AWy0mvt<7Rg*xIJt zrgIM`g@^A9TI1gZ|*`1>t1o&4&8HIN{LPRbBF90@@-|{4^tAF zxt6Jw#=BQ}_s%%Svp4Ftoj>q(bxx9VN!Ex;%c+%ITiliU7YoKmG;AJQHSS=WDKRHM zVZztfyl~rcarKf$^O_suSy}n^#;gOQKTI;kU!S+HCHZ)pX%4+AJj1g4y7jN8A}&5p zvCy0o_2Pw!U3Y(Q4ON=;|2d(V8!~pgWufUi>vT{P0UqSuX38RI!KI)W!OkiIE{{|k zP*O_3L(y|s5w^1q-mOQz*|QIUc}9=?G0}vXJn3v6pIPi>bBfJYx_Az)V~~{OsH6Z1 z09>p{!2y@k?IQzvB#29bv9ydLa8N{?qepBe3!E-^Sy-h|DKL3PfG;GFi7E85cLQG_TfkVxjZIQFTC5=!vf#~4Nx8wS74~ih4$6(i5Z0eq{pG3)$m!t z5kj+BpHSeX*^ENgEhhAZVCY_bPl4AN42Pjn*2%hns1I1h`xDhem$IxP-HOrkQ8)v0ki!}WnOx_u($rdbIJfGc=_!{IbX;2M=YQ3Y^R zG@+AIjFy#aaDw4DHLb>#+CC6dy*#K&%GuW|35o$wIE`UyhSSJ3l!lY5H~?j*DFQ&* zStX7W8avJgp%|K+BzRpENGI>294zW_JAxY$;bfY{s7D9|_ISnOq(lxV=#ebmT@ZM@ zVdY(Hjz~#t;u>6qsdZ{Cu#~7)5s!slWxYO7i4rD`DU|Bqj+7P>1Oterq&fuv!F3P| zneJsNQSe#?!Kp_imtaY=ciRMx6GMrVff88&iV-RjQ;|fXmB2_sOJd5GFoMMT$O{b5 z75$R5ba>#T?v!WpK48BnxYT{3a@hRtS@+b*2aggA2M+~F(cK~Vs6v(r`U$YQx9B{| z?O;Lo=qcAn>-^7_0)x?%2AA74Dy3Y-Y7^yJ9jlh(1g2D}wOAs9+54mW1Wxo*UN+4E zcm!O5@(gkX%Yum--(T9F$4XfMgvl|L{HKJWJprRq&v+DZ68aNPl7a$#nhenEUIWbw z^g^_^8TN1{wVhw_)02x|(E@-TF!DtF4$w6~*Ap@DM8X5rH9*%BG4Mpf1J(6^qbut1 zb&7R^e?fk5St>hGdIVgwp0d9@*#JRX$`S}Li}YmX`XDI!8R-du-m4h_7Q;oODI>f! z?764oLf?M-4h=54e>WP^ta%qZa{pA`3=e7k*Mo=q+Dr2It}S)iMQybm)N3C_uD)Cy zv%hwpWv+H`$c5JAC?e#)Xv0cqq(v*s3&Sp_u@x?;m%k!Zts8$H~g2MUg*N zKeMNFz9OWq>h!keh-lg95D$O3X0NcQX6BEjk{+s8YiW@tEZ4={zP~u4qSDp*`HRPt+qtG)_YZDOosUPy4(&AF{^#M_;bT8v z-noq2lc_0RQ3fpwJ70*&5di82lBkgyLj}7&%#zPu@A{WO=cBJSMussk+>ZpUa_o@%^3S1>NA zvW+;q)j=)%_ZP`u?EL=sA?^$N3WyE1uzRbZ%GoQ{gk9ZQ-4Uv|x&9LTTtnDz*WgL9 z6UL0xnz_cN>cgW-l9{XR`9HkzLQ1x|>|x6~*_hPLf1{^w$Q^uc>;5{~Vdv@cD{Rs0 z72i6scene_manager, event); +} + +bool meshimi_back_event_callback(void *context) { + furi_assert(context); + Meshimi *meshimi = context; + return scene_manager_handle_back_event(meshimi->scene_manager); +} + +void meshimi_tick_event_callback(void *context) { + furi_assert(context); + Meshimi *meshimi = context; + scene_manager_handle_tick_event(meshimi->scene_manager); +} + +Meshimi *meshimi_alloc() { + Meshimi *meshimi = malloc(sizeof(Meshimi)); + + // GUI + meshimi->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + meshimi->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(meshimi->view_dispatcher); + + meshimi->scene_manager = scene_manager_alloc(&meshimi_scene_handlers, meshimi); + view_dispatcher_set_event_callback_context(meshimi->view_dispatcher, meshimi); + view_dispatcher_set_custom_event_callback( + meshimi->view_dispatcher, meshimi_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + meshimi->view_dispatcher, meshimi_back_event_callback); + view_dispatcher_set_tick_event_callback( + meshimi->view_dispatcher, meshimi_tick_event_callback, 100); + + // Open Notification record + meshimi->notifications = furi_record_open(RECORD_NOTIFICATION); + + // SubMenu + meshimi->submenu = submenu_alloc(); + view_dispatcher_add_view( + meshimi->view_dispatcher, MeshimiViewIdMenu, submenu_get_view(meshimi->submenu)); + + // Popup + meshimi->popup = popup_alloc(); + view_dispatcher_add_view( + meshimi->view_dispatcher, MeshimiViewIdPopup, popup_get_view(meshimi->popup)); + + // Text Input + meshimi->text_input = text_input_alloc(); + view_dispatcher_add_view( + meshimi->view_dispatcher, MeshimiViewIdTextInput, text_input_get_view(meshimi->text_input)); + + // Custom Widget + meshimi->widget = widget_alloc(); + view_dispatcher_add_view( + meshimi->view_dispatcher, MeshimiViewIdWidget, widget_get_view(meshimi->widget)); + + //Dialog + meshimi->dialogs = furi_record_open(RECORD_DIALOGS); + + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_external); + + return meshimi; +} + +void meshimi_free(Meshimi *meshimi) { + furi_assert(meshimi); + + // TextInput + view_dispatcher_remove_view(meshimi->view_dispatcher, MeshimiViewIdTextInput); + text_input_free(meshimi->text_input); + + // Custom Widget + view_dispatcher_remove_view(meshimi->view_dispatcher, MeshimiViewIdWidget); + widget_free(meshimi->widget); + + //Dialog + furi_record_close(RECORD_DIALOGS); + + // Submenu + view_dispatcher_remove_view(meshimi->view_dispatcher, MeshimiViewIdMenu); + submenu_free(meshimi->submenu); + + // Popup + view_dispatcher_remove_view(meshimi->view_dispatcher, MeshimiViewIdPopup); + popup_free(meshimi->popup); + + // Scene manager + scene_manager_free(meshimi->scene_manager); + + // View Dispatcher + view_dispatcher_free(meshimi->view_dispatcher); + + // GUI + furi_record_close(RECORD_GUI); + meshimi->gui = NULL; + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + meshimi->notifications = NULL; + + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_external); + + // The rest + free(meshimi); +} + +/** + * @brief Main function for Meshimi application. + * @details This function is the entry point for the skeleton application. It should be defined in + * application.fam as the entry_point setting. + * @param p + * @return 0 - Success + */ +int32_t meshimi_app(void *p) { + UNUSED(p); + + Meshimi *meshimi = meshimi_alloc(); + + view_dispatcher_attach_to_gui( + meshimi->view_dispatcher, meshimi->gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneStart); + + furi_hal_power_suppress_charge_enter(); + + view_dispatcher_run(meshimi->view_dispatcher); + + furi_hal_power_suppress_charge_exit(); + + meshimi_free(meshimi); + + return 0; +} \ No newline at end of file diff --git a/meshimi.h b/meshimi.h new file mode 100644 index 0000000..05d3a72 --- /dev/null +++ b/meshimi.h @@ -0,0 +1,41 @@ +#pragma once + +#include "meshimi_types.h" +#include "applications_user/meshimi/scenes/meshimi_scene.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MESHIMI_VERSION "1.0" +#define MESHIMI_GITHUB "https://github.com/BegoonLab/meshimi" + +typedef struct Meshimi Meshimi; + +struct Meshimi { + Gui* gui; + NotificationApp* notifications; + + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + + Submenu* submenu; + Popup* popup; + TextInput* text_input; + Widget* widget; + DialogsApp* dialogs; +}; + +Meshimi* meshimi_alloc(); +void meshimi_free(Meshimi* meshimi); \ No newline at end of file diff --git a/meshimi_types.h b/meshimi_types.h new file mode 100644 index 0000000..f51c2dc --- /dev/null +++ b/meshimi_types.h @@ -0,0 +1,8 @@ +#pragma once + +typedef enum { + MeshimiViewIdMenu, + MeshimiViewIdPopup, + MeshimiViewIdTextInput, + MeshimiViewIdWidget, +} MeshimiViewId; \ No newline at end of file diff --git a/proto/spi.pb.c b/proto/spi.pb.c new file mode 100644 index 0000000..8be2d38 --- /dev/null +++ b/proto/spi.pb.c @@ -0,0 +1,48 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.7 */ + +#include "spi.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(SpiPacket, SpiPacket, 2) + + +PB_BIND(SpiHeader, SpiHeader, AUTO) + + +PB_BIND(Request, Request, AUTO) + + +PB_BIND(Request_ConnectToNetwork, Request_ConnectToNetwork, AUTO) + + +PB_BIND(Response, Response, 2) + + +PB_BIND(Response_ConnectToNetwork, Response_ConnectToNetwork, AUTO) + + +PB_BIND(Response_LoRaMessageReceived, Response_LoRaMessageReceived, 2) + + +PB_BIND(Response_Status, Response_Status, AUTO) + + +PB_BIND(LoRaMessage, LoRaMessage, 2) + + +PB_BIND(LoRaModulationParams, LoRaModulationParams, AUTO) + + +PB_BIND(LoRaPacketParams, LoRaPacketParams, AUTO) + + + + + + + + + diff --git a/proto/spi.pb.h b/proto/spi.pb.h new file mode 100644 index 0000000..f1efbeb --- /dev/null +++ b/proto/spi.pb.h @@ -0,0 +1,389 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.7 */ + +#ifndef PB_SPI_PB_H_INCLUDED +#define PB_SPI_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +/* LoRa Spreading Factor */ +typedef enum _LoRaSpreadingFactor { + LoRaSpreadingFactor_SF5 = 0, + LoRaSpreadingFactor_SF6 = 1, + LoRaSpreadingFactor_SF7 = 2, + LoRaSpreadingFactor_SF8 = 3, + LoRaSpreadingFactor_SF9 = 4, + LoRaSpreadingFactor_SF10 = 5, + LoRaSpreadingFactor_SF11 = 6, + LoRaSpreadingFactor_SF12 = 7 +} LoRaSpreadingFactor; + +/* LoRa Coding Rate */ +typedef enum _LoRaCodingRate { + LoRaCodingRate_CR_4_5 = 0, + LoRaCodingRate_CR_4_6 = 1, + LoRaCodingRate_CR_4_7 = 2, + LoRaCodingRate_CR_4_8 = 3 +} LoRaCodingRate; + +/* LoRa Bandwidth */ +typedef enum _LoRaBandwidth { + LoRaBandwidth_BW_007 = 0, + LoRaBandwidth_BW_010 = 1, + LoRaBandwidth_BW_015 = 2, + LoRaBandwidth_BW_020 = 3, + LoRaBandwidth_BW_031 = 4, + LoRaBandwidth_BW_041 = 5, + LoRaBandwidth_BW_062 = 6, + LoRaBandwidth_BW_125 = 7, + LoRaBandwidth_BW_250 = 8, + LoRaBandwidth_BW_500 = 9 +} LoRaBandwidth; + +/* LoRa packet length enumeration */ +typedef enum _LoRaPacketLengthMode { + LoRaPacketLengthMode_EXPLICIT = 0, + LoRaPacketLengthMode_IMPLICIT = 1 +} LoRaPacketLengthMode; + +typedef enum _SpiHeader_Status { + SpiHeader_Status_UNKNOWN = 0, + SpiHeader_Status_STAND_BY = 1, + SpiHeader_Status_TX_READY = 2, + SpiHeader_Status_RX_READY = 3, + SpiHeader_Status_BUSY = 4, + SpiHeader_Status_ERROR = 5 +} SpiHeader_Status; + +typedef enum _Response_Status_Enum { + Response_Status_Enum_OK = 0, + Response_Status_Enum_ERROR = 1 +} Response_Status_Enum; + +/* Struct definitions */ +typedef PB_BYTES_ARRAY_T(512) SpiPacket_buffer_t; +typedef struct _SpiPacket { + SpiPacket_buffer_t buffer; /* Last 4 bytes for CRC */ + uint32_t length; +} SpiPacket; + +typedef struct _SpiHeader { + SpiHeader_Status status; + uint32_t length; /* Buffer length for upcoming TX/RX packet. */ +} SpiHeader; + +typedef struct _Response_Status { + Response_Status_Enum status; + pb_callback_t error; +} Response_Status; + +typedef PB_BYTES_ARRAY_T(255) LoRaMessage_data_t; +typedef struct _LoRaMessage { + LoRaMessage_data_t data; /* Received data */ + uint8_t size; /* The number of bytes from the Rx radio buffer */ + int8_t rssi; /* RSSI of the packet */ + int8_t snr; /* SNR of the packet */ +} LoRaMessage; + +typedef struct _Response_LoRaMessageReceived { + bool has_loraMsg; + LoRaMessage loraMsg; +} Response_LoRaMessageReceived; + +/* LoRa modulation parameters */ +typedef struct _LoRaModulationParams { + LoRaSpreadingFactor sf; /* LoRa Spreading Factor */ + LoRaCodingRate cr; /* LoRa Coding Rate */ + LoRaBandwidth bw; /* LoRa Bandwidth */ + uint8_t ldro; /* Low DataRate Optimization configuration */ +} LoRaModulationParams; + +/* LoRa packet parameters */ +typedef struct _LoRaPacketParams { + uint16_t preambleLenInSymbols; /* Preamble length in symbols */ + LoRaPacketLengthMode headerType; /* Header type */ + uint32_t pldLenInBytes; /* Payload length in bytes */ + bool crcIsOn; /* CRC activation */ + bool invertIqIsOn; /* IQ polarity setup */ +} LoRaPacketParams; + +typedef struct _Request_ConnectToNetwork { + bool private; + uint32_t LoRaFrequency; /* The RF frequency for future radio operations */ + bool has_modParams; + LoRaModulationParams modParams; + bool has_pktParams; + LoRaPacketParams pktParams; +} Request_ConnectToNetwork; + +typedef struct _Request { + uint32_t version; + pb_size_t which_request; + union { + Request_ConnectToNetwork connectToNetworkRequest; + } request; +} Request; + +typedef struct _Response_ConnectToNetwork { + bool private; + uint32_t LoRaFrequency; + bool has_modParams; + LoRaModulationParams modParams; + bool has_pktParams; + LoRaPacketParams pktParams; +} Response_ConnectToNetwork; + +typedef struct _Response { + uint32_t version; + bool has_status; + Response_Status status; + pb_size_t which_response; + union { + Response_ConnectToNetwork connectToNetwork; + Response_LoRaMessageReceived loRaMessageReceived; + } response; +} Response; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _LoRaSpreadingFactor_MIN LoRaSpreadingFactor_SF5 +#define _LoRaSpreadingFactor_MAX LoRaSpreadingFactor_SF12 +#define _LoRaSpreadingFactor_ARRAYSIZE ((LoRaSpreadingFactor)(LoRaSpreadingFactor_SF12+1)) + +#define _LoRaCodingRate_MIN LoRaCodingRate_CR_4_5 +#define _LoRaCodingRate_MAX LoRaCodingRate_CR_4_8 +#define _LoRaCodingRate_ARRAYSIZE ((LoRaCodingRate)(LoRaCodingRate_CR_4_8+1)) + +#define _LoRaBandwidth_MIN LoRaBandwidth_BW_007 +#define _LoRaBandwidth_MAX LoRaBandwidth_BW_500 +#define _LoRaBandwidth_ARRAYSIZE ((LoRaBandwidth)(LoRaBandwidth_BW_500+1)) + +#define _LoRaPacketLengthMode_MIN LoRaPacketLengthMode_EXPLICIT +#define _LoRaPacketLengthMode_MAX LoRaPacketLengthMode_IMPLICIT +#define _LoRaPacketLengthMode_ARRAYSIZE ((LoRaPacketLengthMode)(LoRaPacketLengthMode_IMPLICIT+1)) + +#define _SpiHeader_Status_MIN SpiHeader_Status_UNKNOWN +#define _SpiHeader_Status_MAX SpiHeader_Status_ERROR +#define _SpiHeader_Status_ARRAYSIZE ((SpiHeader_Status)(SpiHeader_Status_ERROR+1)) + +#define _Response_Status_Enum_MIN Response_Status_Enum_OK +#define _Response_Status_Enum_MAX Response_Status_Enum_ERROR +#define _Response_Status_Enum_ARRAYSIZE ((Response_Status_Enum)(Response_Status_Enum_ERROR+1)) + + +#define SpiHeader_status_ENUMTYPE SpiHeader_Status + + + + + + +#define Response_Status_status_ENUMTYPE Response_Status_Enum + + +#define LoRaModulationParams_sf_ENUMTYPE LoRaSpreadingFactor +#define LoRaModulationParams_cr_ENUMTYPE LoRaCodingRate +#define LoRaModulationParams_bw_ENUMTYPE LoRaBandwidth + +#define LoRaPacketParams_headerType_ENUMTYPE LoRaPacketLengthMode + + +/* Initializer values for message structs */ +#define SpiPacket_init_default {{0, {0}}, 0} +#define SpiHeader_init_default {_SpiHeader_Status_MIN, 0} +#define Request_init_default {0, 0, {Request_ConnectToNetwork_init_default}} +#define Request_ConnectToNetwork_init_default {0, 0, false, LoRaModulationParams_init_default, false, LoRaPacketParams_init_default} +#define Response_init_default {0, false, Response_Status_init_default, 0, {Response_ConnectToNetwork_init_default}} +#define Response_ConnectToNetwork_init_default {0, 0, false, LoRaModulationParams_init_default, false, LoRaPacketParams_init_default} +#define Response_LoRaMessageReceived_init_default {false, LoRaMessage_init_default} +#define Response_Status_init_default {_Response_Status_Enum_MIN, {{NULL}, NULL}} +#define LoRaMessage_init_default {{0, {0}}, 0, 0, 0} +#define LoRaModulationParams_init_default {_LoRaSpreadingFactor_MIN, _LoRaCodingRate_MIN, _LoRaBandwidth_MIN, 0} +#define LoRaPacketParams_init_default {0, _LoRaPacketLengthMode_MIN, 0, 0, 0} +#define SpiPacket_init_zero {{0, {0}}, 0} +#define SpiHeader_init_zero {_SpiHeader_Status_MIN, 0} +#define Request_init_zero {0, 0, {Request_ConnectToNetwork_init_zero}} +#define Request_ConnectToNetwork_init_zero {0, 0, false, LoRaModulationParams_init_zero, false, LoRaPacketParams_init_zero} +#define Response_init_zero {0, false, Response_Status_init_zero, 0, {Response_ConnectToNetwork_init_zero}} +#define Response_ConnectToNetwork_init_zero {0, 0, false, LoRaModulationParams_init_zero, false, LoRaPacketParams_init_zero} +#define Response_LoRaMessageReceived_init_zero {false, LoRaMessage_init_zero} +#define Response_Status_init_zero {_Response_Status_Enum_MIN, {{NULL}, NULL}} +#define LoRaMessage_init_zero {{0, {0}}, 0, 0, 0} +#define LoRaModulationParams_init_zero {_LoRaSpreadingFactor_MIN, _LoRaCodingRate_MIN, _LoRaBandwidth_MIN, 0} +#define LoRaPacketParams_init_zero {0, _LoRaPacketLengthMode_MIN, 0, 0, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define SpiPacket_buffer_tag 1 +#define SpiPacket_length_tag 2 +#define SpiHeader_status_tag 1 +#define SpiHeader_length_tag 2 +#define Response_Status_status_tag 1 +#define Response_Status_error_tag 2 +#define LoRaMessage_data_tag 1 +#define LoRaMessage_size_tag 2 +#define LoRaMessage_rssi_tag 3 +#define LoRaMessage_snr_tag 4 +#define Response_LoRaMessageReceived_loraMsg_tag 1 +#define LoRaModulationParams_sf_tag 1 +#define LoRaModulationParams_cr_tag 2 +#define LoRaModulationParams_bw_tag 3 +#define LoRaModulationParams_ldro_tag 4 +#define LoRaPacketParams_preambleLenInSymbols_tag 1 +#define LoRaPacketParams_headerType_tag 2 +#define LoRaPacketParams_pldLenInBytes_tag 3 +#define LoRaPacketParams_crcIsOn_tag 4 +#define LoRaPacketParams_invertIqIsOn_tag 5 +#define Request_ConnectToNetwork_private_tag 1 +#define Request_ConnectToNetwork_LoRaFrequency_tag 2 +#define Request_ConnectToNetwork_modParams_tag 3 +#define Request_ConnectToNetwork_pktParams_tag 4 +#define Request_version_tag 1 +#define Request_connectToNetworkRequest_tag 2 +#define Response_ConnectToNetwork_private_tag 1 +#define Response_ConnectToNetwork_LoRaFrequency_tag 2 +#define Response_ConnectToNetwork_modParams_tag 3 +#define Response_ConnectToNetwork_pktParams_tag 4 +#define Response_version_tag 1 +#define Response_status_tag 2 +#define Response_connectToNetwork_tag 3 +#define Response_loRaMessageReceived_tag 4 + +/* Struct field encoding specification for nanopb */ +#define SpiPacket_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, buffer, 1) \ +X(a, STATIC, SINGULAR, UINT32, length, 2) +#define SpiPacket_CALLBACK NULL +#define SpiPacket_DEFAULT NULL + +#define SpiHeader_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, status, 1) \ +X(a, STATIC, SINGULAR, UINT32, length, 2) +#define SpiHeader_CALLBACK NULL +#define SpiHeader_DEFAULT NULL + +#define Request_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, version, 1) \ +X(a, STATIC, ONEOF, MESSAGE, (request,connectToNetworkRequest,request.connectToNetworkRequest), 2) +#define Request_CALLBACK NULL +#define Request_DEFAULT NULL +#define Request_request_connectToNetworkRequest_MSGTYPE Request_ConnectToNetwork + +#define Request_ConnectToNetwork_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, private, 1) \ +X(a, STATIC, SINGULAR, UINT32, LoRaFrequency, 2) \ +X(a, STATIC, OPTIONAL, MESSAGE, modParams, 3) \ +X(a, STATIC, OPTIONAL, MESSAGE, pktParams, 4) +#define Request_ConnectToNetwork_CALLBACK NULL +#define Request_ConnectToNetwork_DEFAULT NULL +#define Request_ConnectToNetwork_modParams_MSGTYPE LoRaModulationParams +#define Request_ConnectToNetwork_pktParams_MSGTYPE LoRaPacketParams + +#define Response_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, version, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, status, 2) \ +X(a, STATIC, ONEOF, MESSAGE, (response,connectToNetwork,response.connectToNetwork), 3) \ +X(a, STATIC, ONEOF, MESSAGE, (response,loRaMessageReceived,response.loRaMessageReceived), 4) +#define Response_CALLBACK NULL +#define Response_DEFAULT NULL +#define Response_status_MSGTYPE Response_Status +#define Response_response_connectToNetwork_MSGTYPE Response_ConnectToNetwork +#define Response_response_loRaMessageReceived_MSGTYPE Response_LoRaMessageReceived + +#define Response_ConnectToNetwork_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, private, 1) \ +X(a, STATIC, SINGULAR, UINT32, LoRaFrequency, 2) \ +X(a, STATIC, OPTIONAL, MESSAGE, modParams, 3) \ +X(a, STATIC, OPTIONAL, MESSAGE, pktParams, 4) +#define Response_ConnectToNetwork_CALLBACK NULL +#define Response_ConnectToNetwork_DEFAULT NULL +#define Response_ConnectToNetwork_modParams_MSGTYPE LoRaModulationParams +#define Response_ConnectToNetwork_pktParams_MSGTYPE LoRaPacketParams + +#define Response_LoRaMessageReceived_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, loraMsg, 1) +#define Response_LoRaMessageReceived_CALLBACK NULL +#define Response_LoRaMessageReceived_DEFAULT NULL +#define Response_LoRaMessageReceived_loraMsg_MSGTYPE LoRaMessage + +#define Response_Status_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, status, 1) \ +X(a, CALLBACK, OPTIONAL, STRING, error, 2) +#define Response_Status_CALLBACK pb_default_field_callback +#define Response_Status_DEFAULT NULL + +#define LoRaMessage_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) \ +X(a, STATIC, SINGULAR, UINT32, size, 2) \ +X(a, STATIC, SINGULAR, INT32, rssi, 3) \ +X(a, STATIC, SINGULAR, INT32, snr, 4) +#define LoRaMessage_CALLBACK NULL +#define LoRaMessage_DEFAULT NULL + +#define LoRaModulationParams_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, sf, 1) \ +X(a, STATIC, SINGULAR, UENUM, cr, 2) \ +X(a, STATIC, SINGULAR, UENUM, bw, 3) \ +X(a, STATIC, SINGULAR, UINT32, ldro, 4) +#define LoRaModulationParams_CALLBACK NULL +#define LoRaModulationParams_DEFAULT NULL + +#define LoRaPacketParams_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, preambleLenInSymbols, 1) \ +X(a, STATIC, SINGULAR, UENUM, headerType, 2) \ +X(a, STATIC, SINGULAR, UINT32, pldLenInBytes, 3) \ +X(a, STATIC, SINGULAR, BOOL, crcIsOn, 4) \ +X(a, STATIC, SINGULAR, BOOL, invertIqIsOn, 5) +#define LoRaPacketParams_CALLBACK NULL +#define LoRaPacketParams_DEFAULT NULL + +extern const pb_msgdesc_t SpiPacket_msg; +extern const pb_msgdesc_t SpiHeader_msg; +extern const pb_msgdesc_t Request_msg; +extern const pb_msgdesc_t Request_ConnectToNetwork_msg; +extern const pb_msgdesc_t Response_msg; +extern const pb_msgdesc_t Response_ConnectToNetwork_msg; +extern const pb_msgdesc_t Response_LoRaMessageReceived_msg; +extern const pb_msgdesc_t Response_Status_msg; +extern const pb_msgdesc_t LoRaMessage_msg; +extern const pb_msgdesc_t LoRaModulationParams_msg; +extern const pb_msgdesc_t LoRaPacketParams_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define SpiPacket_fields &SpiPacket_msg +#define SpiHeader_fields &SpiHeader_msg +#define Request_fields &Request_msg +#define Request_ConnectToNetwork_fields &Request_ConnectToNetwork_msg +#define Response_fields &Response_msg +#define Response_ConnectToNetwork_fields &Response_ConnectToNetwork_msg +#define Response_LoRaMessageReceived_fields &Response_LoRaMessageReceived_msg +#define Response_Status_fields &Response_Status_msg +#define LoRaMessage_fields &LoRaMessage_msg +#define LoRaModulationParams_fields &LoRaModulationParams_msg +#define LoRaPacketParams_fields &LoRaPacketParams_msg + +/* Maximum encoded size of messages (where known) */ +/* Response_size depends on runtime parameters */ +/* Response_Status_size depends on runtime parameters */ +#define LoRaMessage_size 283 +#define LoRaModulationParams_size 9 +#define LoRaPacketParams_size 16 +#define Request_ConnectToNetwork_size 37 +#define Request_size 45 +#define Response_ConnectToNetwork_size 37 +#define Response_LoRaMessageReceived_size 286 +#define SpiHeader_size 8 +#define SpiPacket_size 521 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/scenes/meshimi_scene.c b/scenes/meshimi_scene.c new file mode 100644 index 0000000..50ad620 --- /dev/null +++ b/scenes/meshimi_scene.c @@ -0,0 +1,31 @@ +#include "meshimi_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const meshimi_on_enter_handlers[])(void*) = { +#include "meshimi_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const meshimi_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "meshimi_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const meshimi_on_exit_handlers[])(void* context) = { +#include "meshimi_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers meshimi_scene_handlers = { + .on_enter_handlers = meshimi_on_enter_handlers, + .on_event_handlers = meshimi_on_event_handlers, + .on_exit_handlers = meshimi_on_exit_handlers, + .scene_num = MeshimiSceneNum, +}; + diff --git a/scenes/meshimi_scene.h b/scenes/meshimi_scene.h new file mode 100644 index 0000000..4b0527a --- /dev/null +++ b/scenes/meshimi_scene.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) MeshimiScene##id, +typedef enum { +#include "meshimi_scene_config.h" + + MeshimiSceneNum, +} MeshimiScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers meshimi_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "meshimi_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "meshimi_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "meshimi_scene_config.h" +#undef ADD_SCENE diff --git a/scenes/meshimi_scene_about.c b/scenes/meshimi_scene_about.c new file mode 100644 index 0000000..7e624d9 --- /dev/null +++ b/scenes/meshimi_scene_about.c @@ -0,0 +1,54 @@ +#include "../meshimi.h" + +void meshimi_scene_about_on_enter(void* context) { + Meshimi* meshimi = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", MESHIMI_VERSION); + furi_string_cat_printf(temp_str, "Developed by: %s\n", "todo"); + furi_string_cat_printf(temp_str, "Github: %s\n\n", MESHIMI_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf( + temp_str, + "This application is designed\nto test the functionality of the\nbuilt-in CC1101 module.\n\n"); + + widget_add_text_box_element( + meshimi->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + meshimi->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! Meshimi \e!\n", + false); + widget_add_text_scroll_element(meshimi->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(meshimi->view_dispatcher, MeshimiViewIdWidget); +} + +bool meshimi_scene_about_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void meshimi_scene_about_on_exit(void* context) { + Meshimi* meshimi = context; + widget_reset(meshimi->widget); +} \ No newline at end of file diff --git a/scenes/meshimi_scene_config.h b/scenes/meshimi_scene_config.h new file mode 100644 index 0000000..219f493 --- /dev/null +++ b/scenes/meshimi_scene_config.h @@ -0,0 +1,5 @@ +ADD_SCENE(meshimi, start, Start) +ADD_SCENE(meshimi, connect, Connect) +ADD_SCENE(meshimi, test, Test) +ADD_SCENE(meshimi, settings, Settings) +ADD_SCENE(meshimi, about, About) diff --git a/scenes/meshimi_scene_connect.c b/scenes/meshimi_scene_connect.c new file mode 100644 index 0000000..ec155d3 --- /dev/null +++ b/scenes/meshimi_scene_connect.c @@ -0,0 +1,29 @@ +#include "../meshimi.h" +#include "begoonlab_meshimi_icons.h" +#include + +void meshimi_scene_connect_on_enter(void* context) { + Meshimi* meshimi = context; + + IconAnimation *icon = icon_animation_alloc(&A_Intro_128x64); +// view_tie_icon_animation(meshimi->view_dispatcher, icon); + icon_animation_start(icon); + //one_shot_view_start_animation(meshimi->one_shot_view, &A_Levelup1_128x64); + //canvas_draw_icon(meshimi->widget, 0, 0, &A_Intro_128x64); +// widget_add_icon_element(meshimi->widget, 0, 0, &A_Intro_128x64); + //widget_add_icon_element(meshimi->widget, 0, 0, &I_Meshimi_128x64); +// widget_reset(meshimi->widget); + + view_dispatcher_switch_to_view(meshimi->view_dispatcher, MeshimiViewIdWidget); +} + +bool meshimi_scene_connect_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void meshimi_scene_connect_on_exit(void* context) { + Meshimi* meshimi = context; + widget_reset(meshimi->widget); +} \ No newline at end of file diff --git a/scenes/meshimi_scene_settings.c b/scenes/meshimi_scene_settings.c new file mode 100644 index 0000000..04b6a6e --- /dev/null +++ b/scenes/meshimi_scene_settings.c @@ -0,0 +1,38 @@ +#include "../meshimi.h" + +#include + +void meshimi_scene_settings_on_enter(void *context) { + Meshimi *meshimi = context; + const FuriHalRegion *const region = furi_hal_region_get(); + FuriString *buffer = furi_string_alloc(); + if (region) { + furi_string_cat_printf(buffer, "Region: %s, bands:\n", region->country_code); + for (uint16_t i = 0; i < region->bands_count; ++i) { + furi_string_cat_printf( + buffer, + " %lu-%lu kHz\n", + region->bands[i].start / 1000, + region->bands[i].end / 1000); + } + } else { + furi_string_cat_printf(buffer, "Region: N/A\n"); + } + + widget_add_string_multiline_element( + meshimi->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(buffer)); + + furi_string_free(buffer); + view_dispatcher_switch_to_view(meshimi->view_dispatcher, MeshimiViewIdWidget); +} + +bool meshimi_scene_settings_on_event(void *context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void meshimi_scene_settings_on_exit(void *context) { + Meshimi *meshimi = context; + widget_reset(meshimi->widget); +} \ No newline at end of file diff --git a/scenes/meshimi_scene_start.c b/scenes/meshimi_scene_start.c new file mode 100644 index 0000000..75c36ac --- /dev/null +++ b/scenes/meshimi_scene_start.c @@ -0,0 +1,68 @@ +#include "../meshimi.h" + +enum SubmenuIndex { + SubmenuIndexConnect, + SubmenuIndexTest, + SubmenuIndexSettings, + SubmenuIndexAbout, +}; + +void meshimi_scene_start_submenu_callback(void *context, uint32_t index) { + Meshimi *meshimi = context; + view_dispatcher_send_custom_event(meshimi->view_dispatcher, index); +} + +void meshimi_scene_start_on_enter(void *context) { + Meshimi *meshimi = context; + + submenu_add_item( + meshimi->submenu, "Connect", SubmenuIndexConnect, meshimi_scene_start_submenu_callback, meshimi); + submenu_add_item( + meshimi->submenu, "Test", SubmenuIndexTest, meshimi_scene_start_submenu_callback, meshimi); + submenu_add_item( + meshimi->submenu, "Settings", SubmenuIndexSettings, meshimi_scene_start_submenu_callback, meshimi); + submenu_add_item( + meshimi->submenu, "About", SubmenuIndexAbout, meshimi_scene_start_submenu_callback, meshimi); + submenu_set_selected_item( + meshimi->submenu, scene_manager_get_scene_state(meshimi->scene_manager, MeshimiSceneStart)); + + view_dispatcher_switch_to_view(meshimi->view_dispatcher, MeshimiViewIdMenu); +} + +bool meshimi_scene_start_on_event(void *context, SceneManagerEvent event) { + Meshimi *meshimi = context; + if (event.type == SceneManagerEventTypeBack) { + //exit app + scene_manager_stop(meshimi->scene_manager); + view_dispatcher_stop(meshimi->view_dispatcher); + return true; + } else if (event.type == SceneManagerEventTypeCustom) { + if (event.event == SubmenuIndexConnect) { + scene_manager_set_scene_state( + meshimi->scene_manager, MeshimiSceneStart, SubmenuIndexConnect); + scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneConnect); + return true; + } else if (event.event == SubmenuIndexTest) { + scene_manager_set_scene_state( + meshimi->scene_manager, MeshimiSceneStart, SubmenuIndexTest); + scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneTest); + return true; + } else if (event.event == SubmenuIndexSettings) { + scene_manager_set_scene_state( + meshimi->scene_manager, MeshimiSceneStart, SubmenuIndexSettings); + scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneSettings); + return true; + } else if (event.event == SubmenuIndexAbout) { + scene_manager_set_scene_state( + meshimi->scene_manager, MeshimiSceneStart, SubmenuIndexAbout); + scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneAbout); + return true; + } + } + return false; +} + +void meshimi_scene_start_on_exit(void *context) { + Meshimi *meshimi = context; + submenu_reset(meshimi->submenu); +} diff --git a/scenes/meshimi_scene_test.c b/scenes/meshimi_scene_test.c new file mode 100644 index 0000000..ee977fb --- /dev/null +++ b/scenes/meshimi_scene_test.c @@ -0,0 +1,161 @@ +#include "../meshimi.h" + +#include +#include +#include +#include +#include "../proto/spi.pb.h" +#include "../checksum.h" +#include "pb_encode.h" + +const NotificationSequence sequence_test_wait = { + &message_display_backlight_enforce_on, + &message_green_255, + &message_red_255, + +// &message_vibro_on, + &message_note_c6, + &message_delay_50, + &message_note_d6, + &message_delay_100, + &message_note_c6, + &message_delay_50, + &message_note_e6, + &message_delay_50, + &message_sound_off, +// &message_vibro_off, + + &message_delay_500, + NULL, +}; + +const NotificationSequence sequence_test_error = { + &message_display_backlight_enforce_on, + &message_red_255, + &message_note_e6, + &message_delay_50, + &message_sound_off, + + &message_delay_1000, + NULL, +}; + +void meshimi_scene_test_on_enter(void *context) { + Meshimi *meshimi = context; + + +// FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(LoraTestEvent)); + NotificationApp *notifications = furi_record_open(RECORD_NOTIFICATION); +// LoraTestEvent event; + FuriString *buffer = furi_string_alloc(); +// FuriTimer* timer = furi_timer_alloc(lora_scene_test_update, FuriTimerTypePeriodic, event_queue); +// furi_timer_start(timer, furi_kernel_get_tick_frequency()); + furi_string_cat_printf(buffer, "Waiting for Meshimi module...\n"); + + widget_add_string_multiline_element( + meshimi->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(buffer)); + + view_dispatcher_switch_to_view(meshimi->view_dispatcher, MeshimiViewIdWidget); + + notification_message(notifications, &sequence_test_wait); + + furi_string_cat_printf(buffer, "SPI receiving the data\n"); + widget_add_string_multiline_element( + meshimi->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(buffer)); + + SpiHeader txHeader = SpiHeader_init_zero; + uint8_t spiBuffer[256] = {0}; + txHeader.status = SpiHeader_Status_STAND_BY; + txHeader.length = 0; + size_t headerSize = 7; // TODO: Make it dynamically + for (int i = 0; i < 100; ++i) { + txHeader.length = 0; + txHeader.status = SpiHeader_Status_STAND_BY; + memset(spiBuffer, 0, sizeof(spiBuffer)); + pb_ostream_t stream = pb_ostream_from_buffer(spiBuffer, headerSize - 4); + + bool status = pb_encode_ex(&stream, SpiHeader_fields, &txHeader, PB_ENCODE_DELIMITED); + size_t msglen = stream.bytes_written; + + if (!status) { + // TODO: Handle error + } + + uint32_t crc = crc_32(spiBuffer, stream.bytes_written); + spiBuffer[stream.bytes_written] = (uint8_t) (crc >> 24); + spiBuffer[stream.bytes_written + 1] = (uint8_t) (crc >> 16); + spiBuffer[stream.bytes_written + 2] = (uint8_t) (crc >> 8); + spiBuffer[stream.bytes_written + 3] = (uint8_t) (crc); + msglen += 4; + + furi_hal_gpio_write(furi_hal_spi_bus_handle_external.cs, false); + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_external); + furi_hal_spi_bus_tx( + &furi_hal_spi_bus_handle_external, spiBuffer, msglen, 200); + furi_hal_spi_release(&furi_hal_spi_bus_handle_external); + furi_hal_gpio_write(furi_hal_spi_bus_handle_external.cs, true); + furi_delay_ms(100); + + furi_hal_gpio_write(furi_hal_spi_bus_handle_external.cs, false); + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_external); + furi_hal_spi_bus_rx( + &furi_hal_spi_bus_handle_external, spiBuffer, msglen, 200); + furi_hal_spi_release(&furi_hal_spi_bus_handle_external); + furi_hal_gpio_write(furi_hal_spi_bus_handle_external.cs, true); + + + furi_delay_ms(100); + } + + // TODO: Implement SPI + // Example: https://github.com/skotopes/flipperzero_max31855/blob/dev/max31855.c + + furi_delay_ms(5000); + widget_reset(meshimi->widget); + + widget_add_string_multiline_element( + meshimi->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, "Meshimi module disconnected\n"); + + notification_message(notifications, &sequence_test_error); + + furi_delay_ms(3000); + +// popup_reset(meshimi->popup); +// popup_set_header(meshimi->popup, "New Message!", 63, 3, AlignCenter, AlignTop); +// popup_set_text(meshimi->popup, "Here is some message text. And one more sentence.", 0, 17, AlignLeft, AlignTop); +// +// popup_set_timeout(meshimi->popup, 1500); +// popup_set_context(meshimi->popup, lora); +//// popup_set_callback(meshimi->popup, subghz_test_scene_show_only_rx_popup_callback); +// popup_enable_timeout(meshimi->popup); +// view_dispatcher_switch_to_view(meshimi->view_dispatcher, LoRaViewIdPopup); +// furi_delay_ms(3000); +// view_dispatcher_switch_to_view(meshimi->view_dispatcher, LoRaViewIdWidget); +// popup_reset(meshimi->popup); +// while(1) { +// furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); +// if(event.type == LoraTestEventTypeInput) { +// if((event.input.type == InputTypeShort) && (event.input.key == InputKeyBack)) { +// break; +// } +// } +// if((event.input.type == InputTypeShort) && (event.input.key == InputKeyBack)) { +// break; +// } else { +// +// } +// } + + furi_string_free(buffer); +} + +bool meshimi_scene_test_on_event(void *context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void meshimi_scene_test_on_exit(void *context) { + Meshimi *meshimi = context; + widget_reset(meshimi->widget); +} \ No newline at end of file From 2a7cfbcc100aa3b1621672cddc438241d265277e Mon Sep 17 00:00:00 2001 From: Alexander Begoon Date: Fri, 26 Apr 2024 12:19:00 +0200 Subject: [PATCH 2/9] Add libcrc --- .gitmodules | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..96c21db --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/libcrc"] + path = lib/libcrc + url = https://github.com/lammertb/libcrc From 21589f6a4400c3f7ed73f1996aaa454fffb27f4c Mon Sep 17 00:00:00 2001 From: Alexander Begoon Date: Fri, 26 Apr 2024 12:19:07 +0200 Subject: [PATCH 3/9] Add libcrc --- lib/libcrc | 1 + 1 file changed, 1 insertion(+) create mode 160000 lib/libcrc diff --git a/lib/libcrc b/lib/libcrc new file mode 160000 index 0000000..7719e21 --- /dev/null +++ b/lib/libcrc @@ -0,0 +1 @@ +Subproject commit 7719e2112a9a960b1bba130d02bebdf58e8701f1 From 1547821500562ccba9c0c30715199f7ddbe99eac Mon Sep 17 00:00:00 2001 From: Alexander Begoon Date: Sat, 27 Apr 2024 21:39:47 +0200 Subject: [PATCH 4/9] WIP --- helpers/meshimi_config.c | 27 +++++++ helpers/meshimi_config.h | 43 +++++++++++ icons/Intro_128x64/frame_00.png | Bin 1368 -> 0 bytes icons/Intro_128x64/frame_01.png | Bin 1632 -> 0 bytes icons/Intro_128x64/frame_02.png | Bin 1812 -> 0 bytes icons/Intro_128x64/frame_03.png | Bin 1860 -> 0 bytes icons/Intro_128x64/frame_04.png | Bin 1700 -> 0 bytes icons/Intro_128x64/frame_05.png | Bin 1725 -> 0 bytes icons/Intro_128x64/frame_06.png | Bin 1684 -> 0 bytes icons/Intro_128x64/frame_07.png | Bin 1590 -> 0 bytes icons/Intro_128x64/frame_08.png | Bin 1598 -> 0 bytes icons/Intro_128x64/frame_09.png | Bin 1604 -> 0 bytes icons/Intro_128x64/frame_10.png | Bin 1612 -> 0 bytes icons/Intro_128x64/frame_rate | 1 - meshimi.c | 82 +++++++++++++++------ meshimi.h | 11 ++- meshimi_types.h | 2 + scenes/meshimi_scene_about.c | 13 ++-- scenes/meshimi_scene_config.h | 2 +- scenes/meshimi_scene_configuration.c | 105 +++++++++++++++++++++++++++ scenes/meshimi_scene_connect.c | 4 +- scenes/meshimi_scene_settings.c | 38 ---------- scenes/meshimi_scene_start.c | 12 ++- scenes/meshimi_scene_test.c | 7 +- views/meshimi_view_mode.c | 61 ++++++++++++++++ views/meshimi_view_mode.h | 13 ++++ 26 files changed, 338 insertions(+), 83 deletions(-) create mode 100644 helpers/meshimi_config.c create mode 100644 helpers/meshimi_config.h delete mode 100644 icons/Intro_128x64/frame_00.png delete mode 100644 icons/Intro_128x64/frame_01.png delete mode 100644 icons/Intro_128x64/frame_02.png delete mode 100644 icons/Intro_128x64/frame_03.png delete mode 100644 icons/Intro_128x64/frame_04.png delete mode 100644 icons/Intro_128x64/frame_05.png delete mode 100644 icons/Intro_128x64/frame_06.png delete mode 100644 icons/Intro_128x64/frame_07.png delete mode 100644 icons/Intro_128x64/frame_08.png delete mode 100644 icons/Intro_128x64/frame_09.png delete mode 100644 icons/Intro_128x64/frame_10.png delete mode 100644 icons/Intro_128x64/frame_rate create mode 100644 scenes/meshimi_scene_configuration.c delete mode 100644 scenes/meshimi_scene_settings.c create mode 100644 views/meshimi_view_mode.c create mode 100644 views/meshimi_view_mode.h diff --git a/helpers/meshimi_config.c b/helpers/meshimi_config.c new file mode 100644 index 0000000..6d5b573 --- /dev/null +++ b/helpers/meshimi_config.c @@ -0,0 +1,27 @@ +#include "meshimi_config.h" +#include "subghz/helpers/subghz_threshold_rssi.h" + +struct MeshimiConfig { + MeshimiConfigMode mode; +}; + +MeshimiConfig* meshimi_config_alloc(void) { + MeshimiConfig* instance = malloc(sizeof(MeshimiConfig)); + instance->mode = ModeSimpleRX; + return instance; +} + +void meshimi_config_free(MeshimiConfig* instance) { + furi_assert(instance); + free(instance); +} + +void meshimi_mode_set(MeshimiConfig* instance, MeshimiConfigMode mode) { + furi_assert(instance); + instance->mode = mode; +} + +MeshimiConfigMode meshimi_mode_get(MeshimiConfig* instance) { + furi_assert(instance); + return instance->mode; +} diff --git a/helpers/meshimi_config.h b/helpers/meshimi_config.h new file mode 100644 index 0000000..1c1b26a --- /dev/null +++ b/helpers/meshimi_config.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +typedef enum MeshimiConfigMode { + ModeSimpleRX = 0, + ModeSimpleTX = 1, + ModeMeshtastic = 2, + ModeMeshimi = 3, + ModeLoRaWAN = 4, +} MeshimiConfigMode; + +typedef struct { + MeshimiConfigMode mode; +} MeshimiConfigData; + +typedef struct MeshimiConfig MeshimiConfig; + +/** Allocate MeshimiConfig + * + * @return MeshimiConfig* + */ +MeshimiConfig* meshimi_config_alloc(void); + +/** Free MeshimiConfig + * + * @param instance Pointer to a MeshimiConfig + */ +void meshimi_config_free(MeshimiConfig* instance); + +/** Set Mode + * + * @param instance Pointer to a MeshimiConfig + * @param MeshimiConfigMode Mode + */ +void meshimi_mode_set(MeshimiConfig* instance, MeshimiConfigMode mode); + +/** Get Mode + * + * @param instance Pointer to a MeshimiConfig + * @return MeshimiConfigMode Mode + */ +MeshimiConfigMode meshimi_mode_get(MeshimiConfig* instance); diff --git a/icons/Intro_128x64/frame_00.png b/icons/Intro_128x64/frame_00.png deleted file mode 100644 index 77b531076df19b7a04056d7ec88284c29ee33421..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1368 zcmaJ>e@q*76hAD#$1f9?fm_&lB#eb!?^nyUCza7&8Fec(r5V)($M^2Or4{b3UJqLc zZb;M#aT|#yBN`n{mY8g!G25IP1I8?7@sF8kV&-&dVsviKFwG<;1najzaet6q?(TiR z@80LV&wJnZZamPjue|ipQVhe&eeGfp$x-y|Ew-TdFU2SPNYt3#u-T2u6bpsTu>XJ{>#7xGZLBFZj?fs^+>(j|xgVOi1p8u|i&*?@ zp1@;@i|DrdX@A@Uqp|j319lB}gyi9V*{KjMPvFfd0Xa~i3GkFUpe2Nqi&*t5pmkPF z5%{W!+3zCONQM0Y+@l*1=dBDW(=?6qPAgOZD{&DZ@4;D`Whe&SJjw6^%L$AVU%voPA5gP6w8vxf=mo)CPtWPK1Wy zkP5qKPqAm}rP&0j^i$GtjN=y+P07?J=iInXO zL$c5&lR%1aq~xGw;81|f@s6CI&G2?xYpdJMb3EJTU>L8*>2-@F(V=&NW zW3-@#=E*>VGq;QbG<1c510gSxnM~%)wNpP~SYFsCwuVwa+?l#w@g-j7`8kuh_wejG&>WbN^GdD&mUcXv1T{-jDQ_Ucsz|W3J%eAv7 zUio*a)_%DEcG1HNOEo$sR^gpTJT0T?$qVC{R`SSPLp^r&^O=$HylFDMeEof&fAXs< zooye~7dZBu;^x2pbVn4cSWI$li}GQK5L_Kipn zbp{91D*^TB%iEV~LglS5lzy1LSk*UFd24@B$s1Uc%d)iCMQ`gpUut&T$d(ZxN%2swiG*YNp{(1MF m{QbNoef)R=n>%@V-)J7T*jVI!`&-GA{nz=t9paq3ckDkK+0xqp diff --git a/icons/Intro_128x64/frame_01.png b/icons/Intro_128x64/frame_01.png deleted file mode 100644 index b53437265966c6367911b3c54d172284822e39c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1632 zcmaJ>dr%W+5I;yntOYgyG!7bD-jbCAVLr!BdJp4%iRS|NG>D?iBKvc z(~_a%V{|I@NvE~GT5DT;*HK5OVzo-^qoPGeDivxi{-KtNN-sdHe>mRU-G1NR{&s)6 z-|qQxveP5O;=&*ZiZo{Eb46JzK664P;=3z!l}Qv61%pM%<86WmxL8Qb@KzQ!+Ced! z%K~O;`587Df+Y2v*&SrpCNWx#JE^V*#v8iJBjyiP!uvI1;ni#dk|={<4`fjLHl zRIs~I$gNR>b498F^z*+$S zR+LV}ag<<{GzzRX1!`4cG*B^sR*Yjn5;;Go$+Y)*_lj7@qamP&Q8K;T_wo-YeDLQV-U@a_`c2@?t#1}Efm4uISK5$a zJo7^FhWb?0h)FmDTqq1@ZHY9NgtDwsjnbqNN39BsKy82|6$%VjiNO%LNKx+Zn-}^h zpc0$YzkEIz;^z5iu?}&bT;kv)|AvH$Ll=^hWj2U%U|?Wd`_>K!3NAA0wPx?NzbrwH zdMu)GV4%l1vZZo^E;am@X;jZC++Ke1YW9*xU-30}7u|gPb$GA-#KxcNo(Az<(${A6 zY5u-@*_}|_oKg68ea*@Kbkp%?pQL>8;MuCCw)C*-+mc4fI4CUYhn8c@cEqjx@_O%f z-{P&+T@C%0W`;gel|OlUJ(sAPvSi)l+Q{pQO}AHf^vOmxwǘR?xI&JHpJ@9)bR zC=9#b5S$>1$?R%X=%k>wXJt=jYkl?VwkK;%_Lj%*_wJmIKpuBz%)`}37M3>WpMKW3 zsKJ#zcJ0%sv{6N1f8>X4%+c3tyDh)3*sS}!#H5}enc!O^jZsw2Y8u(rQ1h^`A$&tH zyuR?8>IMJqoiYZRcIE7~Q_Yd(HMNp4r%uO572Ut1?eJ}hZPe~Q+jLRelsVgdBP0A! zUYXB__pV8~75(eCmn0=Eb2{IA??=)jyL&@=W=sEgZ{|R^_fY)USX^ECaQle0Ymy3z zL+cis{#g-UP_-Od-bXWy#^h}=Co8dp`jBgv57{c3w@Z&k6yz^B{6_fqCmv6#A+6!_mmw`DBJSs`kGG6H@+k;izOVnn=yUTI-J4#wJL2FU z+`^Ufd{gG7H-$xcQ|AU@S9}|ZS(|rw>hQ^%Vx#wT9{4Fex;ASjhkSf~mq(7hHKI9l z-?_-RgWZ2(Nu#0jDLc0AEr-?xO)}rQy7QwFyr6zb0v#RO!cpxvgW5Fh4+y4pOvrbW z#&>r{g`XS;6;01SEa^C{gBF@&A*fmkUC895(u2S&; diff --git a/icons/Intro_128x64/frame_02.png b/icons/Intro_128x64/frame_02.png deleted file mode 100644 index 9623af7d87422a4d538f5b2359bf5a59d7651444..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1812 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 diff --git a/icons/Intro_128x64/frame_04.png b/icons/Intro_128x64/frame_04.png deleted file mode 100644 index 677a3367e8d4ec943c88097d48abf95fcd29510f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1700 zcmaJ?e^AqQ6fYG4QDCSjaHyd=C)2b^T1b;?q4WpPjUr-I1azcn3KiOvHeE~c2QqZZ z6#O+Ox|^anCo1zimP;@qYdox16qa-vf=C&FMUQ0E_>xg_uVeR-evKJR_s zyR1!5OAZQ*4CHV)LFyD`1}jV1M;0)Geg7U%rC~(`qsnCF(gwysSSe1Tp4L%-+C=10 z85E%}E^4DB9L|Vxqc)St)TH7hZQ>Ii8@|(IVbL6pB*AGR$O4K1bX11VI3b@hLrl=!6D>oCP2d0t;aFMnM?G5g`s^!0^Rm z16uV4JVTi@9FE;ddHD=u!36?`!@+k5`Ls1p0Am;?fDi$KfUE^*D>gHP6ExeVdOav9 z8)-FK7$a>4JRS)hZD*uBmh4a#CW}V%!m-&lTr0L}0w-Y+z%Pw6-xB)c-VI zX>HRMTPQ&WWuxs@l06c`R4*rN?41*L1mY9elNn#jQDKM2>sZzxW zV==i3ktaf4tlDg22s24}V;kAn!&vOKSX^PH2!^(5X}Zwc2cs>QGQb5^VCQUI-Ef0>%)r z0fI#a-Y{POQt^iRl-00FSiD?t2&N3NBoqfyln4V!R0M-MA%uVi0!4*F2o|%!cmkH9 zz|%J`^^sS_Hm7I#Vlddvi_xOY>^xc7!Remy^-^}|{L)jkDpn2*4D5C9{Ds5e)~c0> zT4(3|h1r$gK><+@R^0o!PTfvI8)~~|FWTQV{i~Hee)mT%SA_ITxv^!S|M~hG$HI~x zcT14SGeG~8lwUttitUUFY5pi;yW9FizQy^E+;w~G{K(DIc68jC+%YlmOaE2DSr?D5 zY&BJXQId8!yS)0%K;6EqWm^vCwk+C%GRGQfKMXx&xw^5(y>(vKu8h)_it`gE#INQ4 zwj_7D-%m}MOUU zdRcY)y;C!bi$?vc<(4&m+*h|75SW9^ilbZ4ge_awy(x^B=lFgS={puEgyvLt&j`Ol z_pV4GDih*tXiWO(MI z;Lf%U0XY!?KBm-$1I{t$Lf81eu|3eS#7EJ(qoX6ZtSCXGYJKmmgQG*QuacC@7W-DX z`X$`IuC-6mS&R?5Co%rnzMRg`)j$4V4|w+PxvQ0Je;=$WTfDQjnz<2D(Zu~cKXpS8 zx?=w3ngREy{T1s{e6H_k7~L8-xv#TnT*=ejO+||#*F8+;8m*jJDmfh-u%NlB|5n*! z-SRbbH1upt^R1BN(g)!xS(fNH)!h<^6|AVwE6wY)AWaoM=1a+`*{vmGN~3(19TK&1 fou7qQCiwVqK7AOxBD>a-==pD`RcXq4d2ZQ%RqlP+ diff --git a/icons/Intro_128x64/frame_05.png b/icons/Intro_128x64/frame_05.png deleted file mode 100644 index fb58fed1e0fbbe7bed41ff7ab0e676ae7067edaa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1725 zcmaJ?dsNeA6t6=8J)kfhAX660j^Z?J(iYQZbU+_KCmM_C@_wktHkhy00-j0Y?!_g5JpfA7lj4D*u|s+ z>J>^fSmHAlj@}8GDvHvfY_`#8WEr_EQXj#F1p)yZ;;=a!khTB~v04fxCaSg z!1Zb!r6#q2*&`+=V<;h$COeviMi&t9*0I(wRx7${Y!jwq!z_rc(U?;ku{Kb_#Q!wj zYi$UL)e-Dq!a&C8ar#J;;EHl*30OY~~`T(Bq!iN|ONEhUo(ne-QyLajjg z7$L_nOb+7iFbr}C9u8u1B^Q+QAspi?Fr16<$NjtyFOo>4A|50`I6izBmWl;Zkwhw8 z#B~>lq#ThqG>(;N4HTxuiSgKKI`$Y={6Q=#))N>->O)8}YP=DGR3t?jRHP2za9I%G zwnD8{kVeCN^BIk%EFtvjb%er4PilaXBBSc}5K!Vg9)b`&5JTh$$ip!@c|^elF(n2I zID91p^OVdnyyBhWjrJ+6p_9;!b3q}PP`cyLB9I_>0uV=dFev9j98ifN2$u`Nd^#AD zO;co>`{tcK8duTHXpy(?e$&C5ejKj>0%^41W4 zaNi?~{`5^vK-lo<=uod-M>}ox5|Itr{;xVSec>$cj5hn*Kc?P1|666t^X!jnH*S6L zvd1PX?4rD?O8Xceuxg#k6I`OJ?e?-%*cCV7(XL+{e67D+UhtfE*6T@B zaE)YpY1Phi&Xb>4Dr-1d>HG0I2ZQ6Uwc<0(w!LLdzlZy+yi`-NV~vkzaX*TFcA~s4 z5z_UAWn)1+e-5=;4~`Q?SHQysVL=~h*2+f?WO^=ZC?zaLt?uFf)d&*U^W zhRL?q$s%ojp(!}->NkCJ48BrmwxMs@XF;%U~qSHjU9STG?cya zx_?6^=~&hM%*D<=FFc?9;^u>+-t#*H9Wx^?&-nweDyVnJnO1UcAm{$2^BsNVLw8T~ z*O%{XgY1WMcO2SWw%sz!(fRLMv^AaVZk%3_T$%{n^Ry=o(Z`Cmx5wKT9{)#XIm_zK zu8PjoZmXxIGqN0S$!C)N9cQ>DYcCWz9a?{}_;SFQwn(k$w2HMIr)oi{3W_sOkWLjCQAa$%3oGb2Xu9FB{^7W@yYKtnzTbPl_rCXL zx27b;xVz1B0|3B1E>@Y!Nh|k+^PRZ&P5#bAPQ1^mGTAgn%N7tO8i=464GqTWiEKKR zCaA&<=jkv2a4OTKXR?`z2{Mw=3kkc8(4seTXaERPSd0XjOS7Pc&ej>^f`^SQ0#HZE z1sS1VoWrM3sHzf5Cp`fLYg8ljYvz7As~ieD2#G94xzXV6Uk61ICTlQ zfF?>SOI1csh2wT|K@Q6rWiVV&P#`Q22^mv1j7p_a7{Oo+gE$MwTxehk3uG`ap7x-m z&7?_ZWOa-Iw0k5pOdcy2aAYU5&>IsIUpY3Ir)tGj4Ym+Q7!@M0UT;rr!rIKH(*M(V zqqRA`&`86nw3*2>k=&7J7f*w^^1VHf-H>A=OE&4ab0O9%88S~#8`wCdT)=%1QaVZ| zA!rRj5E_UKMNtT&#Uw;%v?53&K}bSE5u^y0O#68gK2oVvMT$`r$D$=Ds)~}TB9$uD zQc3&< z7?hziP)vb2*nUQnDJyA{E}y2NO^hC#D6&lV1_D}AEXHwK3=y~nhr}eoC67}gh|m(K z6q9HXRIC+D;i=b(H`%A0hD*XY%|(Wwv^JDPmO?ZwmO>;hMj?#|!5}Sx<027)O1NMG zn4<{W`{uPinpSbmXf5WgJsD;se6<5LgJDYcRQ*!lRSlMnKK**GwqwJ0R3 zV(b%Jnf3bFhtanV`n`1M;fH-$*!jC{)3K9hU7D0sv0w#%Q?oFwsKnE)$zxC6+BWs+ zmTjlD+g3S`GkpH0G@sH_eZjw8D+ymwdNtnNA-N%bZc9ZACaDiH`y=~)_}rT9Yh9xL zz&(TCd|~YP?ueLcBV85N^3l@hQ~%tlDQ~4mwyTj{U)1G{Kwr57$1-M&f^{Li;mW<UqdC0>13=e&o^oPT6SCDuArU{aSk`@3ZM*Y%J+-X{i& zpQLa3Y;Zeo{B-roExS6Z-qH61zI$_V*_OavgMk6k{2e!%R*e2#-Lc49+tdBcvHSRk zepMln6^FgTlj~-A4BmbL@HZN^0i8(w2vADaW{hUt4G#C^ANuRYNZsYI6sLZ#kW0%d zkeP7q8R+2X@VKc#QBoO`=l?i=ZQI?@g&%c=2-{y@A zDz|KnZQGwaXXkuzGu3u5$m$$#@ddD8#@ diff --git a/icons/Intro_128x64/frame_07.png b/icons/Intro_128x64/frame_07.png deleted file mode 100644 index 4f3dfc8c4a875a8b9eda2c87337cb46d984a93b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1590 zcmaJ>eNYou5Pt+eKt#b>N3r60Vv*|Q?vlVIXCj0oV5-3sV~uvKUhXcCf!xL9AOW=? z#j!Hh@3AOV)M~Y@1E{q_>lfN~uyrh08B1-8jOZ|m)~eY0Rp|>5>mQCccen5D?Qi$D z`*v@&H7hfENZb$rK(u*=F&oNK_@qUI!}qm_brvYbNG7{9M|4Vl#>)deCpvh<>}GQK zY@XqYmi)kL0SGU5+3b?tGK*$Kx10&u$OCQKL_IvvplXz0MrOAHcl=LQnW%zlNzM|LSaBJ z=cKcZ8U5klPKV}7l844|zuzzSE9Ig$2PZWe4NfRXn6F5Xm;fgDCWbs1c^~U_ zc_f!8AVH6eLoASVC?wmLh1+AXJaa7g`fCNNh6flAPRa?~?GC2aYweS=`TuFW(AsA! z^6+>z?-L8WEIbnD#1I&k@8yAlhL8 zU~Do;VhUcxVvNJ7#2jjZWz-zQDk*iy&x`Pe6eFcJ7*Z%jhMFWz28}7*XfmZJlQrok zMY^5{VabaO%(G(jbP0cCDE5FdJu(DP9T%# zxdcx1`;vlZ)R(f6_qqysF2gIjk=`QHt``t+vMLot@hXg=92BNv8JIl9DKW;$kQ#;B zNsuZh+K=a+D_&opLJds96XHS>B=1aSi4=_IRT_+?R3zq55(>=8P?SXxtnYKZb62{rk^X?YV!p^-x&gm!oa>Yy8WPJy1M398q^5547C4 z(=`pGR&3LMcXjcZs!O%&TUU0T9UD9OwB@P3et@<7$a@poKcu7XkC(Omybc7eJ={95 zeVAT`oY3Wg%ij#!8CQz7e+E?1k2v+d-@09!H^lt)TH2jsceBLCC1C47eZ~&3zv!t} z_&4Eh`Hhd>SvsNsSpR8Dyuz-ccFIjNa2a%(+3mscgKUD)(#(t zw8@O~>&LHHfBxd!(XU?lRI{OFd+U~6#H8u9DWd+|)cGYU`CDfXm+#1=%jz6d{CZe-=ON2ixnVKdliTcNt1Fg*&2?Ebx{I5+ zgpH|Do0=|$7h61qiQNx3j(i0l9JO7py?yRc`IK?OnrzMDL2(Y~0!7l1CXt2ypeH8>Q% zb;N+`8eso=**r(niYY6c;q3Ck&G#}t!8=PTXR2$!mp9!Qduq?dd+#O#l~Z%5`#E*!qQWH533d`rguo=FX$ X{(3~&sLLIZ!9Tg#lx3_*UtIPdOIt_2 diff --git a/icons/Intro_128x64/frame_08.png b/icons/Intro_128x64/frame_08.png deleted file mode 100644 index 3a5a28805a856668c4d05c0824cb8253f9dcc732..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1598 zcmaJ>eNYou5Wghyr80hi)uN+k)Pm?GcbC8=R|142NRZG>Oe<&|xm;c(1G$U2qlst{ zg&DPek2;Oj7O=G++G*`bty2`y>Qt~++WJMO&QPpcuo}l1Ypr1V0>t`<?RQM*LO_6fcXW5^(|V68(%1AbKb70I1o+ zECV*cI9Duh2U!Rrt9RQ=#1cyp&GH@v6R}YQJYI-KkgS}5mtiY_h&sSBH>Z<_T29DO zw^Juym|?*!UIQq17gYPeg6d*BTV26wo${P)G%G+u2Oc0YXuwm+33NawAM{JZbwsU{ zqk|@5g-$+1s>EVN4ZIJal!Cxm9LG^gs{l@hbf{bmI~OH!l28)xrZ9q{Nfk|K(cw!D z1NxjU+Gflj4hMHSdATThX{FNd_bdD=1@Bv?B(z$s5+{`;i9riYSiy-*0ON!iQ4dBS zus*j}bn_e<@yIy%Dp4ngWQVfwcrBKvjyYktR3S8;&L{b~H7DOBPpT={o z1^WswP}+dNSNT|YB(51zFf8AT14RrW8~Qb$8=ebOY2?`|58yoD8d?G*Lg#!y7VMjOFe&WpTI&U;Z(rNGg&g>KHt z`-Rz&Ga5?S2z>6hfHU96d(gom)9&XGaItDNMFBO&P!0-HvkXk0a;h-K#SmIjZJp~YUy*edrd-mqm19gXFNgFzmz6J_Ojw54`LgeQ8`Hz=h&riUQ zeIhwqg&da#&Zi~VGJlX*XT9H*b9};YNgd5E)&668*zwGD!Iw&Nb^`))|R<#+{L8v+j=E&C@}2K>3MYP zQJci7HLb)h)i>V%>Q4XeCl>w8&D}+&ZVp*J>9$aIPMkaK?O+EV)5%;v7y4=4f|^g0 zuYNwpAC?@P+AAFEq4q78xgNA1UN;J>QS@Gz)7Dwv)W7P~rh6y%CVaTqJ1w`!8v8ZU zxW#!kRO%8(m#yUN-8H3gmGa15eNuDEJ;zw{z^vL*;@!H=V*U13t)x^_c(eaaF)>%S z>vYqP`&xy$dt$8N^u577^I~_n3L6H@PwofvTf(UldZS7gKDvW{W&J$i@ZVL3T1QMm YrcO_Iys{zVX5^1AGk|J(*OVf diff --git a/icons/Intro_128x64/frame_09.png b/icons/Intro_128x64/frame_09.png deleted file mode 100644 index 76267a2a8d6689a04241f432e55501ae91231b2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1604 zcmaJ=eNYou5Wggq5*0tH)fA~7hYsp+$=xMzNhC_h1%V-m5eKBT<#M^00LjJNX#%w6 zL~*EJwAC^|tx&1ejx|$T99yvpezdgJ4q98Pt$;)Ahg#IBR2-es7a-O@9B=M!-`lsp z-QVupeLW|8an#u8u@D4BWu#M9AVu({M9RSbYGjQCi01@zzOamQ2!7hfLPk4hW8n-p zy^6K6w7qoo*K9Hb$u_uh^M!m%mY(6rz^e8$1W?&&SlDq8J!(jv=B}X=z#+_E6{MjUE<;O0RuAZR}a=9 zwF-fUO@v|tGD0ffk^`GK9}AO89Az*JgGrr|wbO)6?Vya^sAMeZ2xZLP!R+QEKt7GBhUfV!^ed^ zP%O{*TwcM&dEk&o+QzLB3Wo?u9O)IA)ET^#~c10RbmUyUVWE z(yWc9X&cHU;y6mM8V03p4mE1iVhpXd(~O$bhW$JaZ%U#_t;u92i8L*an@u`%DrGh& zsS|anW+K&yg|QhPUZ6b;8;tJI2etFBv}nglQt67Ff>S>w5w6tLE}0? z>%ef00~y8JA1U5Qp8^d?!W-tI$8gq>$Y4n*%W8BeLuzo;rp5@=L6f9fjp14l3{e4! zs!-oN(nnzxXwJ~`;b4Hx!_i_rV4i$naBABQE(Svvk&~5c26AXNb1T^#gXkl)z)r?=GDGjbhag<;X+I^Ym6=HYV95f`oAcqZx2a(kL`jA zr>s0$L{0pyV0JGRm@8{^D*hcDh-o7WYI0u`+Yr<;XkD>Bc%qXNp|hDh*D`Ll^weakq7Hv%jfw#W(x{ z=>21dVzb(Jb&K-P`lF*|_1(Sw2F1rMeMe`;O7=9r1CxGOXl`#sJ>e>%0zNo z{oWJK{^YpC8Xi()+wY(6oPTMVVdBL7FY3A`96tB%M(4hThr65ka}VURX5_fG22Zb! zcq^M47(6xZcJrzFd!+pM4pEviX5EQh*}FXl4(^5Isfc=M7VK`2DB^yND1>?ggrqn2 zLQJ|$tT-#tMy-|?>SV$AowIgI^S@8AXPU*T`EN9Lbx!TG47NX={%ZU{f_Tqb{pHHo z*@Xqy$CN3mFIgKZtNdrl?MA6~-iAGQ?Z}O496dg+|aOkG? j&5o)3_*?4sMo0#=U6ZG6`PBUh=qxD1oJ|E&3oHKv!xl}j diff --git a/icons/Intro_128x64/frame_10.png b/icons/Intro_128x64/frame_10.png deleted file mode 100644 index bda1bf44ce0a0cab9a03d15fd284b709590f3b4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1612 zcmaJ>c~BE)6yI=2qGGYkSZLK*mQuBJv%5)PlP!USBxuxN3{-Kf%4YYAte9-vT}r@q z07osJIOy08Whx#_J*u_UX%#K!sJ-lXRA;0Y?dWI~j}}`UmC`RjtbaJ}?0)C_z4v?9 zu5ss2jUV~qNCZLRozv_dI9J2ZqK<~+6?L5pPRWX+NGT9~N;xY5#LA0afI9u`V&DNR zU$N|KkcA-8D}}-$rN}jt;Y7cd4a;al{vbpnNLEfL$a1AXLA_wH5HMr6kF{Z_z?-qT z87|Tlw1E;~TBQUED)S4u%2Lk6V>#JqR)~QL{6JyRkbh}FWh1MvVxuL4wkfgx?=_ZBSZPJm7yCPnDJnD}sRV z09h=PIG7S&Y6J|!_iRI9LCA)gB?&Mu>{7ePmH9zHaoWuoywUOk&lp+YWm(pXa~Twc z>wtm7S+7rzdyOQ=8hMt})5eINr{PV8JcrSdLDG3NNl^}))#|Y4*^MTf&2BY19QFv- z8ITn=z=4Qu0ooqI8uFfrWo!~)6;Ub_#ifx#aF>XRD3^#qRHxUH=;XOVfEUZ&-y0-Xp(xG*FId>I^>i34CT;T&zCaIc=!;XamzW=P6tFwhtQDH7qjd7_RYN3b}< z%g2oYH;-Ej1Ykc&uyN|D@^`|f8{wW==z#OUz(Dh*Z9gMO)J&({S{Ul?Ez)f4BGoGg zsw$gzeSWkf(6}URzWLmo_{1b%5c!_D@$n%zjjp3!yE2Vq!)Av$d{+5rR!L=3i)*VDMSr|DmPI(qrLg% zbd@mTSlqeMhu^*06+@(@T$sIAq>$o6h4Jku-hL#%!+du?8SVZfHz_x!IFxHl**QD) z*8UXDnjVvFEPf(;!irny#qOiFo`Qs!-{=jea{qdC=h#V^+PKH|ilMG$>;t4_OUIp4 z3xrJvKDw@L>-?tmBEEAT61+5t-gh=H?ZbhBG) d`scene_manager, event); } -bool meshimi_back_event_callback(void *context) { +bool meshimi_back_event_callback(void* context) { furi_assert(context); - Meshimi *meshimi = context; + Meshimi* meshimi = context; return scene_manager_handle_back_event(meshimi->scene_manager); } -void meshimi_tick_event_callback(void *context) { +void meshimi_tick_event_callback(void* context) { furi_assert(context); - Meshimi *meshimi = context; + Meshimi* meshimi = context; scene_manager_handle_tick_event(meshimi->scene_manager); } -Meshimi *meshimi_alloc() { - Meshimi *meshimi = malloc(sizeof(Meshimi)); +Meshimi* meshimi_alloc() { + Meshimi* meshimi = malloc(sizeof(Meshimi)); // GUI meshimi->gui = furi_record_open(RECORD_GUI); + // Bt + meshimi->bt = furi_record_open(RECORD_BT); + // View Dispatcher meshimi->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(meshimi->view_dispatcher); @@ -31,11 +34,11 @@ Meshimi *meshimi_alloc() { meshimi->scene_manager = scene_manager_alloc(&meshimi_scene_handlers, meshimi); view_dispatcher_set_event_callback_context(meshimi->view_dispatcher, meshimi); view_dispatcher_set_custom_event_callback( - meshimi->view_dispatcher, meshimi_custom_event_callback); + meshimi->view_dispatcher, meshimi_custom_event_callback); view_dispatcher_set_navigation_event_callback( - meshimi->view_dispatcher, meshimi_back_event_callback); + meshimi->view_dispatcher, meshimi_back_event_callback); view_dispatcher_set_tick_event_callback( - meshimi->view_dispatcher, meshimi_tick_event_callback, 100); + meshimi->view_dispatcher, meshimi_tick_event_callback, 100); // Open Notification record meshimi->notifications = furi_record_open(RECORD_NOTIFICATION); @@ -43,32 +46,44 @@ Meshimi *meshimi_alloc() { // SubMenu meshimi->submenu = submenu_alloc(); view_dispatcher_add_view( - meshimi->view_dispatcher, MeshimiViewIdMenu, submenu_get_view(meshimi->submenu)); + meshimi->view_dispatcher, MeshimiViewIdMenu, submenu_get_view(meshimi->submenu)); // Popup meshimi->popup = popup_alloc(); view_dispatcher_add_view( - meshimi->view_dispatcher, MeshimiViewIdPopup, popup_get_view(meshimi->popup)); + meshimi->view_dispatcher, MeshimiViewIdPopup, popup_get_view(meshimi->popup)); // Text Input meshimi->text_input = text_input_alloc(); view_dispatcher_add_view( - meshimi->view_dispatcher, MeshimiViewIdTextInput, text_input_get_view(meshimi->text_input)); + meshimi->view_dispatcher, + MeshimiViewIdTextInput, + text_input_get_view(meshimi->text_input)); // Custom Widget meshimi->widget = widget_alloc(); view_dispatcher_add_view( - meshimi->view_dispatcher, MeshimiViewIdWidget, widget_get_view(meshimi->widget)); + meshimi->view_dispatcher, MeshimiViewIdWidget, widget_get_view(meshimi->widget)); - //Dialog + // Dialog meshimi->dialogs = furi_record_open(RECORD_DIALOGS); + // Variable Item List + meshimi->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + meshimi->view_dispatcher, + MeshimiViewIdVariableItemList, + variable_item_list_get_view(meshimi->variable_item_list)); + + // Meshimi Configuration + meshimi->config = meshimi_config_alloc(); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_external); return meshimi; } -void meshimi_free(Meshimi *meshimi) { +void meshimi_free(Meshimi* meshimi) { furi_assert(meshimi); // TextInput @@ -79,9 +94,17 @@ void meshimi_free(Meshimi *meshimi) { view_dispatcher_remove_view(meshimi->view_dispatcher, MeshimiViewIdWidget); widget_free(meshimi->widget); - //Dialog + // Dialog furi_record_close(RECORD_DIALOGS); + // Bt + furi_record_close(RECORD_BT); + meshimi->bt = NULL; + + // Variable Item List + view_dispatcher_remove_view(meshimi->view_dispatcher, MeshimiViewIdVariableItemList); + variable_item_list_free(meshimi->variable_item_list); + // Submenu view_dispatcher_remove_view(meshimi->view_dispatcher, MeshimiViewIdMenu); submenu_free(meshimi->submenu); @@ -104,6 +127,9 @@ void meshimi_free(Meshimi *meshimi) { furi_record_close(RECORD_NOTIFICATION); meshimi->notifications = NULL; + // Meshimi Configuration + meshimi_config_free(meshimi->config); + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_external); // The rest @@ -112,26 +138,36 @@ void meshimi_free(Meshimi *meshimi) { /** * @brief Main function for Meshimi application. - * @details This function is the entry point for the skeleton application. It should be defined in - * application.fam as the entry_point setting. + * @details This function is the entry point for the Meshimi application. * @param p * @return 0 - Success */ -int32_t meshimi_app(void *p) { +int32_t meshimi_app(void* p) { UNUSED(p); + FURI_LOG_I(LOG_TAG, "Start app"); + + Meshimi* meshimi = meshimi_alloc(); - Meshimi *meshimi = meshimi_alloc(); +#ifdef BACKLIGHT_ALWAYS_ON + notification_message(meshimi->notifications, &sequence_display_backlight_enforce_on); +#endif view_dispatcher_attach_to_gui( - meshimi->view_dispatcher, meshimi->gui, ViewDispatcherTypeFullscreen); + meshimi->view_dispatcher, meshimi->gui, ViewDispatcherTypeFullscreen); scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneStart); furi_hal_power_suppress_charge_enter(); view_dispatcher_run(meshimi->view_dispatcher); + notification_message(meshimi->notifications, &sequence_blink_green_100); + furi_hal_power_suppress_charge_exit(); +#ifdef BACKLIGHT_ALWAYS_ON + notification_message(meshimi->notifications, &sequence_display_backlight_enforce_auto); +#endif + meshimi_free(meshimi); return 0; diff --git a/meshimi.h b/meshimi.h index 05d3a72..0746c1c 100644 --- a/meshimi.h +++ b/meshimi.h @@ -2,6 +2,8 @@ #include "meshimi_types.h" #include "applications_user/meshimi/scenes/meshimi_scene.h" +#include "helpers/meshimi_config.h" +#include "bt/bt_service/bt.h" #include #include @@ -19,11 +21,17 @@ #include #define MESHIMI_VERSION "1.0" -#define MESHIMI_GITHUB "https://github.com/BegoonLab/meshimi" +#define MESHIMI_GITHUB "github.com/BegoonLab/meshimi" +#define MESHIMI_GET_MODULE "begoonlab.tech/meshimi" +#define BACKLIGHT_ALWAYS_ON +#define LOG_TAG "Meshimi" typedef struct Meshimi Meshimi; struct Meshimi { + MeshimiConfig* config; + + Bt* bt; Gui* gui; NotificationApp* notifications; @@ -35,6 +43,7 @@ struct Meshimi { TextInput* text_input; Widget* widget; DialogsApp* dialogs; + VariableItemList* variable_item_list; }; Meshimi* meshimi_alloc(); diff --git a/meshimi_types.h b/meshimi_types.h index f51c2dc..149499b 100644 --- a/meshimi_types.h +++ b/meshimi_types.h @@ -5,4 +5,6 @@ typedef enum { MeshimiViewIdPopup, MeshimiViewIdTextInput, MeshimiViewIdWidget, + MeshimiViewIdVariableItemList, + MeshimiViewIdMode, } MeshimiViewId; \ No newline at end of file diff --git a/scenes/meshimi_scene_about.c b/scenes/meshimi_scene_about.c index 7e624d9..1405773 100644 --- a/scenes/meshimi_scene_about.c +++ b/scenes/meshimi_scene_about.c @@ -5,16 +5,19 @@ void meshimi_scene_about_on_enter(void* context) { FuriString* temp_str; temp_str = furi_string_alloc(); - furi_string_printf(temp_str, "\e#%s\n", "Information"); + furi_string_printf(temp_str, "\e#%s\n", "Buy module"); + + furi_string_cat_printf(temp_str, "%s\n\n", MESHIMI_GET_MODULE); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Information"); furi_string_cat_printf(temp_str, "Version: %s\n", MESHIMI_VERSION); - furi_string_cat_printf(temp_str, "Developed by: %s\n", "todo"); - furi_string_cat_printf(temp_str, "Github: %s\n\n", MESHIMI_GITHUB); + furi_string_cat_printf(temp_str, "%s\n\n", MESHIMI_GITHUB); furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); furi_string_cat_printf( temp_str, - "This application is designed\nto test the functionality of the\nbuilt-in CC1101 module.\n\n"); + "Meshimi empowers your\nFlipper Zero with advanced\nmesh networking capabilities,\nutilizing Meshtastic,\nLoRaWAN, and a proprietary\nsecure mesh protocol\nto deliver superior\nconnectivity\nand robust security.\n\n"); widget_add_text_box_element( meshimi->widget, @@ -34,7 +37,7 @@ void meshimi_scene_about_on_enter(void* context) { 14, AlignCenter, AlignBottom, - "\e#\e! Meshimi \e!\n", + "\e#\e! Meshimi \e!\n", false); widget_add_text_scroll_element(meshimi->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); furi_string_free(temp_str); diff --git a/scenes/meshimi_scene_config.h b/scenes/meshimi_scene_config.h index 219f493..b02ff56 100644 --- a/scenes/meshimi_scene_config.h +++ b/scenes/meshimi_scene_config.h @@ -1,5 +1,5 @@ ADD_SCENE(meshimi, start, Start) ADD_SCENE(meshimi, connect, Connect) ADD_SCENE(meshimi, test, Test) -ADD_SCENE(meshimi, settings, Settings) +ADD_SCENE(meshimi, configuration, Configuration) ADD_SCENE(meshimi, about, About) diff --git a/scenes/meshimi_scene_configuration.c b/scenes/meshimi_scene_configuration.c new file mode 100644 index 0000000..30f192b --- /dev/null +++ b/scenes/meshimi_scene_configuration.c @@ -0,0 +1,105 @@ +#include "../meshimi.h" + +#include + +#define MODE_COUNT 5 +const char* const mode_text[MODE_COUNT] = { + "Simple RX", + "Simple TX", + "LoRaWAN", + "Meshtastic", + "Meshimi", +}; +const MeshimiConfigMode mode_value[MODE_COUNT] = { + ModeSimpleRX, + ModeSimpleTX, + ModeLoRaWAN, + ModeMeshtastic, + ModeMeshimi, +}; + +static void meshimi_scene_configuration_set_mode(VariableItem* item) { + Meshimi* meshimi = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, mode_text[index]); + meshimi_mode_set(meshimi->config, mode_value[index]); +} + +uint8_t meshimi_scene_configuration_mode_value_index( + MeshimiConfigMode value, + const MeshimiConfigMode values[], + uint8_t values_count, + void* context) { + furi_assert(context); + UNUSED(values_count); + UNUSED(values); + UNUSED(context); + + switch(value) { + case ModeSimpleRX: + return 0; + case ModeSimpleTX: + return 1; + case ModeMeshtastic: + return 3; + case ModeMeshimi: + return 4; + case ModeLoRaWAN: + return 2; + default: + return 0; + } +} + +void meshimi_scene_configuration_on_enter(void* context) { + Meshimi* meshimi = context; + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + meshimi->variable_item_list, + "Mode:", + MODE_COUNT, + meshimi_scene_configuration_set_mode, + meshimi); + + value_index = meshimi_scene_configuration_mode_value_index( + meshimi_mode_get(meshimi->config), mode_value, MODE_COUNT, meshimi); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, mode_text[value_index]); + + // const FuriHalRegion* const region = furi_hal_region_get(); + // FuriString* buffer = furi_string_alloc(); + // if(region) { + // furi_string_cat_printf(buffer, "Region: %s, bands:\n", region->country_code); + // for(uint16_t i = 0; i < region->bands_count; ++i) { + // furi_string_cat_printf( + // buffer, + // " %lu-%lu kHz\n", + // region->bands[i].start / 1000, + // region->bands[i].end / 1000); + // } + // } else { + // furi_string_cat_printf(buffer, "Region: N/A\n"); + // } + // + // widget_add_string_multiline_element( + // meshimi->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(buffer)); + // + // furi_string_free(buffer); + view_dispatcher_switch_to_view(meshimi->view_dispatcher, MeshimiViewIdVariableItemList); +} + +bool meshimi_scene_configuration_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void meshimi_scene_configuration_on_exit(void* context) { + Meshimi* meshimi = context; + variable_item_list_set_selected_item(meshimi->variable_item_list, 0); + variable_item_list_reset(meshimi->variable_item_list); + widget_reset(meshimi->widget); +} diff --git a/scenes/meshimi_scene_connect.c b/scenes/meshimi_scene_connect.c index ec155d3..c362136 100644 --- a/scenes/meshimi_scene_connect.c +++ b/scenes/meshimi_scene_connect.c @@ -5,9 +5,9 @@ void meshimi_scene_connect_on_enter(void* context) { Meshimi* meshimi = context; - IconAnimation *icon = icon_animation_alloc(&A_Intro_128x64); + //IconAnimation *icon = icon_animation_alloc(&A_Intro_128x64); // view_tie_icon_animation(meshimi->view_dispatcher, icon); - icon_animation_start(icon); + //icon_animation_start(icon); //one_shot_view_start_animation(meshimi->one_shot_view, &A_Levelup1_128x64); //canvas_draw_icon(meshimi->widget, 0, 0, &A_Intro_128x64); // widget_add_icon_element(meshimi->widget, 0, 0, &A_Intro_128x64); diff --git a/scenes/meshimi_scene_settings.c b/scenes/meshimi_scene_settings.c deleted file mode 100644 index 04b6a6e..0000000 --- a/scenes/meshimi_scene_settings.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "../meshimi.h" - -#include - -void meshimi_scene_settings_on_enter(void *context) { - Meshimi *meshimi = context; - const FuriHalRegion *const region = furi_hal_region_get(); - FuriString *buffer = furi_string_alloc(); - if (region) { - furi_string_cat_printf(buffer, "Region: %s, bands:\n", region->country_code); - for (uint16_t i = 0; i < region->bands_count; ++i) { - furi_string_cat_printf( - buffer, - " %lu-%lu kHz\n", - region->bands[i].start / 1000, - region->bands[i].end / 1000); - } - } else { - furi_string_cat_printf(buffer, "Region: N/A\n"); - } - - widget_add_string_multiline_element( - meshimi->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(buffer)); - - furi_string_free(buffer); - view_dispatcher_switch_to_view(meshimi->view_dispatcher, MeshimiViewIdWidget); -} - -bool meshimi_scene_settings_on_event(void *context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void meshimi_scene_settings_on_exit(void *context) { - Meshimi *meshimi = context; - widget_reset(meshimi->widget); -} \ No newline at end of file diff --git a/scenes/meshimi_scene_start.c b/scenes/meshimi_scene_start.c index 75c36ac..a307c4e 100644 --- a/scenes/meshimi_scene_start.c +++ b/scenes/meshimi_scene_start.c @@ -3,7 +3,7 @@ enum SubmenuIndex { SubmenuIndexConnect, SubmenuIndexTest, - SubmenuIndexSettings, + SubmenuIndexConfiguration, SubmenuIndexAbout, }; @@ -18,9 +18,7 @@ void meshimi_scene_start_on_enter(void *context) { submenu_add_item( meshimi->submenu, "Connect", SubmenuIndexConnect, meshimi_scene_start_submenu_callback, meshimi); submenu_add_item( - meshimi->submenu, "Test", SubmenuIndexTest, meshimi_scene_start_submenu_callback, meshimi); - submenu_add_item( - meshimi->submenu, "Settings", SubmenuIndexSettings, meshimi_scene_start_submenu_callback, meshimi); + meshimi->submenu, "Config", SubmenuIndexConfiguration, meshimi_scene_start_submenu_callback, meshimi); submenu_add_item( meshimi->submenu, "About", SubmenuIndexAbout, meshimi_scene_start_submenu_callback, meshimi); submenu_set_selected_item( @@ -47,10 +45,10 @@ bool meshimi_scene_start_on_event(void *context, SceneManagerEvent event) { meshimi->scene_manager, MeshimiSceneStart, SubmenuIndexTest); scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneTest); return true; - } else if (event.event == SubmenuIndexSettings) { + } else if (event.event == SubmenuIndexConfiguration) { scene_manager_set_scene_state( - meshimi->scene_manager, MeshimiSceneStart, SubmenuIndexSettings); - scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneSettings); + meshimi->scene_manager, MeshimiSceneStart, SubmenuIndexConfiguration); + scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneConfiguration); return true; } else if (event.event == SubmenuIndexAbout) { scene_manager_set_scene_state( diff --git a/scenes/meshimi_scene_test.c b/scenes/meshimi_scene_test.c index ee977fb..9fbbc21 100644 --- a/scenes/meshimi_scene_test.c +++ b/scenes/meshimi_scene_test.c @@ -9,7 +9,6 @@ #include "pb_encode.h" const NotificationSequence sequence_test_wait = { - &message_display_backlight_enforce_on, &message_green_255, &message_red_255, @@ -30,7 +29,6 @@ const NotificationSequence sequence_test_wait = { }; const NotificationSequence sequence_test_error = { - &message_display_backlight_enforce_on, &message_red_255, &message_note_e6, &message_delay_50, @@ -45,7 +43,6 @@ void meshimi_scene_test_on_enter(void *context) { // FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(LoraTestEvent)); - NotificationApp *notifications = furi_record_open(RECORD_NOTIFICATION); // LoraTestEvent event; FuriString *buffer = furi_string_alloc(); // FuriTimer* timer = furi_timer_alloc(lora_scene_test_update, FuriTimerTypePeriodic, event_queue); @@ -57,7 +54,7 @@ void meshimi_scene_test_on_enter(void *context) { view_dispatcher_switch_to_view(meshimi->view_dispatcher, MeshimiViewIdWidget); - notification_message(notifications, &sequence_test_wait); + notification_message(meshimi->notifications, &sequence_test_wait); furi_string_cat_printf(buffer, "SPI receiving the data\n"); widget_add_string_multiline_element( @@ -116,7 +113,7 @@ void meshimi_scene_test_on_enter(void *context) { widget_add_string_multiline_element( meshimi->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, "Meshimi module disconnected\n"); - notification_message(notifications, &sequence_test_error); + notification_message(meshimi->notifications, &sequence_test_error); furi_delay_ms(3000); diff --git a/views/meshimi_view_mode.c b/views/meshimi_view_mode.c new file mode 100644 index 0000000..e67859d --- /dev/null +++ b/views/meshimi_view_mode.c @@ -0,0 +1,61 @@ +#include "meshimi_view_mode.h" + + +struct MeshimiViewMode { + View* view; + Meshimi* meshimi; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool left_mouse_pressed; + bool left_mouse_held; + bool right_mouse_pressed; + bool connected; +} MeshimiViewModeModel; + +static void meshimi_view_mode_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + MeshimiViewModeModel* model = context; + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Select Mode"); + canvas_set_font(canvas, FontSecondary); +} + +static bool meshimi_view_mode_input_callback(InputEvent* event, void* context) { + furi_assert(context); + MeshimiViewMode* meshimi_view_mode = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + with_view_model( + meshimi_view_mode->view, + MeshimiViewModeModel * model, + { + model->left_mouse_held = false; + model->left_mouse_pressed = false; + }, + false); + } else { + // TODO + consumed = true; + } + + return consumed; +} + +MeshimiViewMode* meshimi_view_mode_alloc(Meshimi* meshimi) { + MeshimiViewMode* meshimi_view_mode = malloc(sizeof(MeshimiViewMode)); + meshimi_view_mode->view = view_alloc(); + meshimi_view_mode->meshimi = meshimi; + view_set_context(meshimi_view_mode->view, meshimi_view_mode); + view_allocate_model( + meshimi_view_mode->view, ViewModelTypeLocking, sizeof(MeshimiViewModeModel)); + view_set_draw_callback(meshimi_view_mode->view, meshimi_view_mode_draw_callback); + view_set_input_callback(meshimi_view_mode->view, meshimi_view_mode_input_callback); + return meshimi_view_mode; +} diff --git a/views/meshimi_view_mode.h b/views/meshimi_view_mode.h new file mode 100644 index 0000000..cdc70e2 --- /dev/null +++ b/views/meshimi_view_mode.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +typedef struct Meshimi Meshimi; +typedef struct MeshimiViewMode MeshimiViewMode; + +MeshimiViewMode* meshimi_view_mode_alloc(Meshimi* meshimi); + +void meshimi_view_mode_free(MeshimiViewMode* meshimi_mode); + +View* meshimi_view_mode_get_view(MeshimiViewMode* meshimi_mode); \ No newline at end of file From 752559ef2df48c89f1e4ddc955f18119fe859f2d Mon Sep 17 00:00:00 2001 From: Alexander Begoon Date: Sun, 28 Apr 2024 14:42:12 +0200 Subject: [PATCH 5/9] WIP --- helpers/meshimi_config.c | 24 ++++++ helpers/meshimi_config.h | 15 +++- meshimi.c | 17 +++- meshimi.h | 2 + meshimi_custom_event.h | 5 ++ scenes/meshimi_scene_config.h | 1 + scenes/meshimi_scene_configuration.c | 124 ++++++++++----------------- scenes/meshimi_scene_mode.c | 35 ++++++++ views/meshimi_view_mode.c | 118 +++++++++++++++++++------ views/meshimi_view_mode.h | 13 ++- 10 files changed, 243 insertions(+), 111 deletions(-) create mode 100644 meshimi_custom_event.h create mode 100644 scenes/meshimi_scene_mode.c diff --git a/helpers/meshimi_config.c b/helpers/meshimi_config.c index 6d5b573..a52d7e0 100644 --- a/helpers/meshimi_config.c +++ b/helpers/meshimi_config.c @@ -1,6 +1,22 @@ #include "meshimi_config.h" #include "subghz/helpers/subghz_threshold_rssi.h" +// The order of items below is important +const char* const mode_text[MODE_COUNT] = { + "Simple RX", + "Simple TX", + "Meshtastic", + "LoRaWAN", + "Meshimi", +}; +const MeshimiConfigMode mode_value[MODE_COUNT] = { + ModeSimpleRX, + ModeSimpleTX, + ModeMeshtastic, + ModeLoRaWAN, + ModeMeshimi, +}; + struct MeshimiConfig { MeshimiConfigMode mode; }; @@ -25,3 +41,11 @@ MeshimiConfigMode meshimi_mode_get(MeshimiConfig* instance) { furi_assert(instance); return instance->mode; } + +const char* meshimi_mode_get_text(MeshimiConfigMode mode) { + return mode_text[mode]; +} + +const enum MeshimiConfigMode* meshimi_mode_get_value() { + return mode_value; +} \ No newline at end of file diff --git a/helpers/meshimi_config.h b/helpers/meshimi_config.h index 1c1b26a..415d351 100644 --- a/helpers/meshimi_config.h +++ b/helpers/meshimi_config.h @@ -2,12 +2,13 @@ #include +#define MODE_COUNT 5 typedef enum MeshimiConfigMode { ModeSimpleRX = 0, ModeSimpleTX = 1, ModeMeshtastic = 2, - ModeMeshimi = 3, - ModeLoRaWAN = 4, + ModeLoRaWAN = 3, + ModeMeshimi = 4, } MeshimiConfigMode; typedef struct { @@ -41,3 +42,13 @@ void meshimi_mode_set(MeshimiConfig* instance, MeshimiConfigMode mode); * @return MeshimiConfigMode Mode */ MeshimiConfigMode meshimi_mode_get(MeshimiConfig* instance); + +/** + * Get text representation of the mode + * + * @param MeshimiConfigMode mode + * @return + */ +const char* meshimi_mode_get_text(MeshimiConfigMode mode); + +const enum MeshimiConfigMode* meshimi_mode_get_value(); \ No newline at end of file diff --git a/meshimi.c b/meshimi.c index 0228985..3c5c698 100644 --- a/meshimi.c +++ b/meshimi.c @@ -21,6 +21,9 @@ void meshimi_tick_event_callback(void* context) { Meshimi* meshimi_alloc() { Meshimi* meshimi = malloc(sizeof(Meshimi)); + // Meshimi Configuration + meshimi->config = meshimi_config_alloc(); + // GUI meshimi->gui = furi_record_open(RECORD_GUI); @@ -68,6 +71,13 @@ Meshimi* meshimi_alloc() { // Dialog meshimi->dialogs = furi_record_open(RECORD_DIALOGS); + // Views + meshimi->meshimi_view_mode = meshimi_view_mode_alloc(meshimi->config); + view_dispatcher_add_view( + meshimi->view_dispatcher, + MeshimiViewIdMode, + meshimi_view_mode_get_view(meshimi->meshimi_view_mode)); + // Variable Item List meshimi->variable_item_list = variable_item_list_alloc(); view_dispatcher_add_view( @@ -75,8 +85,7 @@ Meshimi* meshimi_alloc() { MeshimiViewIdVariableItemList, variable_item_list_get_view(meshimi->variable_item_list)); - // Meshimi Configuration - meshimi->config = meshimi_config_alloc(); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_external); @@ -113,6 +122,10 @@ void meshimi_free(Meshimi* meshimi) { view_dispatcher_remove_view(meshimi->view_dispatcher, MeshimiViewIdPopup); popup_free(meshimi->popup); + // Views + view_dispatcher_remove_view(meshimi->view_dispatcher, MeshimiViewIdMode); + meshimi_view_mode_free(meshimi->meshimi_view_mode); + // Scene manager scene_manager_free(meshimi->scene_manager); diff --git a/meshimi.h b/meshimi.h index 0746c1c..5dca6d4 100644 --- a/meshimi.h +++ b/meshimi.h @@ -4,6 +4,7 @@ #include "applications_user/meshimi/scenes/meshimi_scene.h" #include "helpers/meshimi_config.h" #include "bt/bt_service/bt.h" +#include "views/meshimi_view_mode.h" #include #include @@ -44,6 +45,7 @@ struct Meshimi { Widget* widget; DialogsApp* dialogs; VariableItemList* variable_item_list; + MeshimiViewMode* meshimi_view_mode; }; Meshimi* meshimi_alloc(); diff --git a/meshimi_custom_event.h b/meshimi_custom_event.h new file mode 100644 index 0000000..542e346 --- /dev/null +++ b/meshimi_custom_event.h @@ -0,0 +1,5 @@ +#pragma once + +typedef enum { + MeshimiEventMode = 0, +} MeshimiCustomEvent; \ No newline at end of file diff --git a/scenes/meshimi_scene_config.h b/scenes/meshimi_scene_config.h index b02ff56..dbfc297 100644 --- a/scenes/meshimi_scene_config.h +++ b/scenes/meshimi_scene_config.h @@ -3,3 +3,4 @@ ADD_SCENE(meshimi, connect, Connect) ADD_SCENE(meshimi, test, Test) ADD_SCENE(meshimi, configuration, Configuration) ADD_SCENE(meshimi, about, About) +ADD_SCENE(meshimi, mode, Mode) diff --git a/scenes/meshimi_scene_configuration.c b/scenes/meshimi_scene_configuration.c index 30f192b..1aeda8b 100644 --- a/scenes/meshimi_scene_configuration.c +++ b/scenes/meshimi_scene_configuration.c @@ -1,100 +1,66 @@ #include "../meshimi.h" - +#include "meshimi_custom_event.h" #include -#define MODE_COUNT 5 -const char* const mode_text[MODE_COUNT] = { - "Simple RX", - "Simple TX", - "LoRaWAN", - "Meshtastic", - "Meshimi", -}; -const MeshimiConfigMode mode_value[MODE_COUNT] = { - ModeSimpleRX, - ModeSimpleTX, - ModeLoRaWAN, - ModeMeshtastic, - ModeMeshimi, +enum MeshimiItem { + MeshimiMode, }; -static void meshimi_scene_configuration_set_mode(VariableItem* item) { - Meshimi* meshimi = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, mode_text[index]); - meshimi_mode_set(meshimi->config, mode_value[index]); -} - -uint8_t meshimi_scene_configuration_mode_value_index( - MeshimiConfigMode value, - const MeshimiConfigMode values[], - uint8_t values_count, - void* context) { +static void meshimi_scene_configuration_var_list_enter_callback(void* context, uint32_t index) { furi_assert(context); - UNUSED(values_count); - UNUSED(values); - UNUSED(context); - - switch(value) { - case ModeSimpleRX: - return 0; - case ModeSimpleTX: - return 1; - case ModeMeshtastic: - return 3; - case ModeMeshimi: - return 4; - case ModeLoRaWAN: - return 2; - default: - return 0; + Meshimi* meshimi = context; + if(index == MeshimiMode) { + view_dispatcher_send_custom_event(meshimi->view_dispatcher, MeshimiEventMode); } } void meshimi_scene_configuration_on_enter(void* context) { Meshimi* meshimi = context; - VariableItem* item; - uint8_t value_index; + MeshimiConfig *config = meshimi->config; + MeshimiConfigMode mode = meshimi_mode_get(config); - item = variable_item_list_add( - meshimi->variable_item_list, - "Mode:", - MODE_COUNT, - meshimi_scene_configuration_set_mode, - meshimi); + variable_item_list_set_enter_callback( + meshimi->variable_item_list, meshimi_scene_configuration_var_list_enter_callback, meshimi); - value_index = meshimi_scene_configuration_mode_value_index( - meshimi_mode_get(meshimi->config), mode_value, MODE_COUNT, meshimi); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, mode_text[value_index]); + if (mode == ModeSimpleRX) { + variable_item_list_add( + meshimi->variable_item_list, "Mode: Simple RX", 0, NULL, NULL); + + } else if (mode == ModeSimpleTX) { + variable_item_list_add( + meshimi->variable_item_list, "Mode: Simple TX", 0, NULL, NULL); + } else if (mode == ModeMeshtastic) { + variable_item_list_add( + meshimi->variable_item_list, "Mode: Meshtastic", 0, NULL, NULL); + } else if (mode == ModeMeshimi) { + variable_item_list_add( + meshimi->variable_item_list, "Mode: Meshimi", 0, NULL, NULL); + } else if (mode == ModeLoRaWAN) { + variable_item_list_add( + meshimi->variable_item_list, "Mode: LoRaWAN", 0, NULL, NULL); + } - // const FuriHalRegion* const region = furi_hal_region_get(); - // FuriString* buffer = furi_string_alloc(); - // if(region) { - // furi_string_cat_printf(buffer, "Region: %s, bands:\n", region->country_code); - // for(uint16_t i = 0; i < region->bands_count; ++i) { - // furi_string_cat_printf( - // buffer, - // " %lu-%lu kHz\n", - // region->bands[i].start / 1000, - // region->bands[i].end / 1000); - // } - // } else { - // furi_string_cat_printf(buffer, "Region: N/A\n"); - // } - // - // widget_add_string_multiline_element( - // meshimi->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(buffer)); - // - // furi_string_free(buffer); view_dispatcher_switch_to_view(meshimi->view_dispatcher, MeshimiViewIdVariableItemList); } bool meshimi_scene_configuration_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; + Meshimi* meshimi = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == MeshimiEventMode) { + scene_manager_set_scene_state(meshimi->scene_manager, MeshimiSceneStart, MeshimiMode); + scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneMode); + } + consumed = true; + } + + if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneStart); + consumed = true; + } + + return consumed; } void meshimi_scene_configuration_on_exit(void* context) { diff --git a/scenes/meshimi_scene_mode.c b/scenes/meshimi_scene_mode.c new file mode 100644 index 0000000..4763575 --- /dev/null +++ b/scenes/meshimi_scene_mode.c @@ -0,0 +1,35 @@ +#include "../meshimi.h" + +void meshimi_view_mode_ok_callback(InputType type, void* context) { + furi_assert(context); + Meshimi* meshimi = context; + + if(type == InputTypePress) { + notification_message(meshimi->notifications, &sequence_set_green_255); + } else if(type == InputTypeRelease) { + notification_message(meshimi->notifications, &sequence_reset_green); + } + + scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneConfiguration); +} + +void meshimi_scene_mode_on_exit(void* context) { + UNUSED(context); +} + +void meshimi_scene_mode_on_enter(void* context) { + furi_assert(context); + Meshimi* meshimi = context; + view_dispatcher_switch_to_view(meshimi->view_dispatcher, MeshimiViewIdMode); +} + +bool meshimi_scene_mode_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + Meshimi* meshimi = context; + UNUSED(event); + + meshimi_view_mode_set_ok_callback( + meshimi->meshimi_view_mode, meshimi_view_mode_ok_callback, meshimi); + + return false; +} \ No newline at end of file diff --git a/views/meshimi_view_mode.c b/views/meshimi_view_mode.c index e67859d..88c0dae 100644 --- a/views/meshimi_view_mode.c +++ b/views/meshimi_view_mode.c @@ -1,20 +1,14 @@ #include "meshimi_view_mode.h" - struct MeshimiViewMode { View* view; - Meshimi* meshimi; + MeshimiConfig * meshimi_config; + MeshimiViewModeOkCallback callback; + void* context; }; typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool left_mouse_pressed; - bool left_mouse_held; - bool right_mouse_pressed; - bool connected; + MeshimiConfigMode mode; } MeshimiViewModeModel; static void meshimi_view_mode_draw_callback(Canvas* canvas, void* context) { @@ -22,8 +16,60 @@ static void meshimi_view_mode_draw_callback(Canvas* canvas, void* context) { MeshimiViewModeModel* model = context; canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Select Mode"); + elements_multiline_text_aligned(canvas, 35, 3, AlignLeft, AlignTop, "Select Mode"); canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned( + canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change mode"); + + elements_multiline_text_aligned( + canvas, 64, 32, AlignCenter, AlignTop, meshimi_mode_get_text(model->mode)); +} + +static bool meshimi_view_mode_process_right(MeshimiViewMode* meshimi_view_mode) { + with_view_model( + meshimi_view_mode->view, + MeshimiViewModeModel * model, + { + if(model->mode < MODE_COUNT - 1) { + model->mode = meshimi_mode_get_value()[model->mode + 1]; + } + }, + true); + return true; +} + +static bool meshimi_view_mode_process_left(MeshimiViewMode* meshimi_view_mode) { + with_view_model( + meshimi_view_mode->view, + MeshimiViewModeModel * model, + { + if(model->mode) { + model->mode = meshimi_mode_get_value()[model->mode - 1]; + } + }, + true); + return true; +} + +static bool meshimi_view_mode_process_ok(MeshimiViewMode* meshimi_view_mode, InputEvent* event) { + bool consumed = false; + + with_view_model( + meshimi_view_mode->view, + MeshimiViewModeModel * model, + { + if(event->type == InputTypePress) { + meshimi_mode_set(meshimi_view_mode->meshimi_config, model->mode); + consumed = true; + } else if(event->type == InputTypeRelease) { + meshimi_mode_set(meshimi_view_mode->meshimi_config, model->mode); + consumed = true; + } + meshimi_view_mode->callback(event->type, meshimi_view_mode->context); + }, + true); + + return consumed; } static bool meshimi_view_mode_input_callback(InputEvent* event, void* context) { @@ -31,31 +77,53 @@ static bool meshimi_view_mode_input_callback(InputEvent* event, void* context) { MeshimiViewMode* meshimi_view_mode = context; bool consumed = false; - if(event->type == InputTypeLong && event->key == InputKeyBack) { - with_view_model( - meshimi_view_mode->view, - MeshimiViewModeModel * model, - { - model->left_mouse_held = false; - model->left_mouse_pressed = false; - }, - false); - } else { - // TODO - consumed = true; + if(event->type == InputTypeShort) { + if(event->key == InputKeyRight) { + consumed = meshimi_view_mode_process_right(meshimi_view_mode); + } else if(event->key == InputKeyLeft) { + consumed = meshimi_view_mode_process_left(meshimi_view_mode); + } + } else if(event->key == InputKeyOk) { + consumed = meshimi_view_mode_process_ok(meshimi_view_mode, event); } return consumed; } -MeshimiViewMode* meshimi_view_mode_alloc(Meshimi* meshimi) { +MeshimiViewMode* meshimi_view_mode_alloc(MeshimiConfig * meshimi_config) { MeshimiViewMode* meshimi_view_mode = malloc(sizeof(MeshimiViewMode)); meshimi_view_mode->view = view_alloc(); - meshimi_view_mode->meshimi = meshimi; + meshimi_view_mode->meshimi_config = meshimi_config; view_set_context(meshimi_view_mode->view, meshimi_view_mode); view_allocate_model( meshimi_view_mode->view, ViewModelTypeLocking, sizeof(MeshimiViewModeModel)); view_set_draw_callback(meshimi_view_mode->view, meshimi_view_mode_draw_callback); view_set_input_callback(meshimi_view_mode->view, meshimi_view_mode_input_callback); + return meshimi_view_mode; } + +void meshimi_view_mode_free(MeshimiViewMode* meshimi_view_mode) { + furi_assert(meshimi_view_mode); + view_free(meshimi_view_mode->view); + free(meshimi_view_mode); +} + +View* meshimi_view_mode_get_view(MeshimiViewMode* meshimi_view_mode) { + furi_assert(meshimi_view_mode); + return meshimi_view_mode->view; +} + +void meshimi_view_mode_set_ok_callback(MeshimiViewMode* meshimi_view_mode, MeshimiViewModeOkCallback callback, void* context) { + furi_assert(meshimi_view_mode); + furi_assert(callback); + with_view_model( + meshimi_view_mode->view, + MeshimiViewModeModel * model, + { + UNUSED(model); + meshimi_view_mode->callback = callback; + meshimi_view_mode->context = context; + }, + false); +} \ No newline at end of file diff --git a/views/meshimi_view_mode.h b/views/meshimi_view_mode.h index cdc70e2..b3ace6a 100644 --- a/views/meshimi_view_mode.h +++ b/views/meshimi_view_mode.h @@ -2,12 +2,19 @@ #include #include +#include "helpers/meshimi_config.h" typedef struct Meshimi Meshimi; typedef struct MeshimiViewMode MeshimiViewMode; +typedef void (*MeshimiViewModeOkCallback)(InputType type, void* context); -MeshimiViewMode* meshimi_view_mode_alloc(Meshimi* meshimi); +MeshimiViewMode* meshimi_view_mode_alloc(MeshimiConfig * meshimi_config); -void meshimi_view_mode_free(MeshimiViewMode* meshimi_mode); +void meshimi_view_mode_free(MeshimiViewMode* meshimi_view_mode); -View* meshimi_view_mode_get_view(MeshimiViewMode* meshimi_mode); \ No newline at end of file +View* meshimi_view_mode_get_view(MeshimiViewMode* meshimi_view_mode); + +void meshimi_view_mode_set_ok_callback( + MeshimiViewMode* meshimi_view_mode, + MeshimiViewModeOkCallback callback, + void* context); \ No newline at end of file From 9cdbacb7f4cb0cbe76ad505a3c8f1d8d03025c02 Mon Sep 17 00:00:00 2001 From: Alexander Begoon Date: Mon, 29 Apr 2024 21:16:15 +0200 Subject: [PATCH 6/9] Add LoRa config menu --- helpers/meshimi_config.c | 104 +++++++++++++++++++++- helpers/meshimi_config.h | 120 +++++++++++++++++++++++-- meshimi_custom_event.h | 4 + proto/spi.pb.c | 1 + proto/spi.pb.h | 27 ++++-- scenes/meshimi_scene_configuration.c | 125 +++++++++++++++++++++++---- 6 files changed, 350 insertions(+), 31 deletions(-) diff --git a/helpers/meshimi_config.c b/helpers/meshimi_config.c index a52d7e0..2444352 100644 --- a/helpers/meshimi_config.c +++ b/helpers/meshimi_config.c @@ -17,13 +17,59 @@ const MeshimiConfigMode mode_value[MODE_COUNT] = { ModeMeshimi, }; +const char* const spreading_factor_text[SPREADING_FACTOR_COUNT] = { + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", +}; + +const char* const bandwidth_text[BANDWIDTH_COUNT] = { + "7.8kHz", + "10.4kHz", + "15.6kHz", + "20.8kHz", + "31.2kHz", + "41.7kHz", + "62.5kHz", + "125kHz", + "250kHz", + "500kHz", +}; + +const char* const coding_rate_text[CODING_RATE_COUNT] = { + "4_5", + "4_6", + "4_7", + "4_8", +}; + +const char* const ldro_text[LDRO_COUNT] = { + "OFF", + "ON", +}; + struct MeshimiConfig { MeshimiConfigMode mode; + LoRaSpreadingFactor lora_spreading_factor; + LoRaBandwidth lora_bandwidth; + LoRaCodingRate lora_coding_rate; + LoRaLowDataRateOptimization lora_ldro; }; MeshimiConfig* meshimi_config_alloc(void) { MeshimiConfig* instance = malloc(sizeof(MeshimiConfig)); + + // TODO: Instantiate it from conf file stored at SD Card instance->mode = ModeSimpleRX; + instance->lora_spreading_factor = LoRaSpreadingFactor_SF7; + instance->lora_bandwidth = LoRaBandwidth_BW_125; + instance->lora_coding_rate = LoRaCodingRate_CR_4_7; + instance->lora_ldro = LoRaLowDataRateOptimization_LDRO_OFF; return instance; } @@ -48,4 +94,60 @@ const char* meshimi_mode_get_text(MeshimiConfigMode mode) { const enum MeshimiConfigMode* meshimi_mode_get_value() { return mode_value; -} \ No newline at end of file +} + +void meshimi_spreading_factor_set(MeshimiConfig* instance, LoRaSpreadingFactor spreading_factor) { + furi_assert(instance); + instance->lora_spreading_factor = spreading_factor; +} + +LoRaSpreadingFactor meshimi_spreading_factor_get(MeshimiConfig* instance) { + furi_assert(instance); + return instance->lora_spreading_factor; +} + +const char* meshimi_spreading_factor_get_text(LoRaSpreadingFactor spreading_factor) { + return spreading_factor_text[spreading_factor]; +} + +void meshimi_bandwidth_set(MeshimiConfig* instance, LoRaBandwidth bandwidth) { + furi_assert(instance); + instance->lora_bandwidth = bandwidth; +} + +LoRaBandwidth meshimi_bandwidth_get(MeshimiConfig* instance) { + furi_assert(instance); + return instance->lora_bandwidth; +} + +const char* meshimi_bandwidth_get_text(LoRaBandwidth bandwidth) { + return bandwidth_text[bandwidth]; +} + +void meshimi_coding_rate_set(MeshimiConfig* instance, LoRaCodingRate coding_rate) { + furi_assert(instance); + instance->lora_coding_rate = coding_rate; +} + +LoRaCodingRate meshimi_coding_rate_get(MeshimiConfig* instance) { + furi_assert(instance); + return instance->lora_coding_rate; +} + +const char* meshimi_coding_rate_get_text(LoRaCodingRate coding_rate) { + return coding_rate_text[coding_rate]; +} + +void meshimi_ldro_set(MeshimiConfig* instance, LoRaLowDataRateOptimization ldro) { + furi_assert(instance); + instance->lora_ldro = ldro; +} + +LoRaLowDataRateOptimization meshimi_ldro_get(MeshimiConfig* instance) { + furi_assert(instance); + return instance->lora_ldro; +} + +const char* meshimi_ldro_get_text(LoRaLowDataRateOptimization ldro) { + return ldro_text[ldro]; +} diff --git a/helpers/meshimi_config.h b/helpers/meshimi_config.h index 415d351..a582447 100644 --- a/helpers/meshimi_config.h +++ b/helpers/meshimi_config.h @@ -1,6 +1,7 @@ #pragma once #include +#include "proto/spi.pb.h" #define MODE_COUNT 5 typedef enum MeshimiConfigMode { @@ -11,32 +12,45 @@ typedef enum MeshimiConfigMode { ModeMeshimi = 4, } MeshimiConfigMode; +#define SPREADING_FACTOR_COUNT 8 +#define BANDWIDTH_COUNT 10 +#define CODING_RATE_COUNT 4 +#define LDRO_COUNT 2 + typedef struct { MeshimiConfigMode mode; + LoRaSpreadingFactor lora_spreading_factor; + LoRaBandwidth lora_bandwidth; + LoRaCodingRate lora_coding_rate; + LoRaLowDataRateOptimization lora_ldro; } MeshimiConfigData; typedef struct MeshimiConfig MeshimiConfig; -/** Allocate MeshimiConfig +/** + * Allocate MeshimiConfig * * @return MeshimiConfig* */ MeshimiConfig* meshimi_config_alloc(void); -/** Free MeshimiConfig +/** + * Free MeshimiConfig * * @param instance Pointer to a MeshimiConfig */ void meshimi_config_free(MeshimiConfig* instance); -/** Set Mode +/** + * Set Mode * * @param instance Pointer to a MeshimiConfig * @param MeshimiConfigMode Mode */ void meshimi_mode_set(MeshimiConfig* instance, MeshimiConfigMode mode); -/** Get Mode +/** + * Get Mode * * @param instance Pointer to a MeshimiConfig * @return MeshimiConfigMode Mode @@ -51,4 +65,100 @@ MeshimiConfigMode meshimi_mode_get(MeshimiConfig* instance); */ const char* meshimi_mode_get_text(MeshimiConfigMode mode); -const enum MeshimiConfigMode* meshimi_mode_get_value(); \ No newline at end of file +const enum MeshimiConfigMode* meshimi_mode_get_value(); + +/** + * Set Spreading Factor + * + * @param instance Pointer to a MeshimiConfig + * @param LoRaSpreadingFactor Spreading Factor + */ +void meshimi_spreading_factor_set(MeshimiConfig* instance, LoRaSpreadingFactor spreading_factor); + +/** + * Get Spreading Factor + * + * @param instance Pointer to a MeshimiConfig + * @return LoRaSpreadingFactor + */ +LoRaSpreadingFactor meshimi_spreading_factor_get(MeshimiConfig* instance); + +/** + * Get text representation of the spreading_factor + * + * @param LoRaSpreadingFactor spreading_factor + * @return + */ +const char* meshimi_spreading_factor_get_text(LoRaSpreadingFactor spreading_factor); + +/** + * Set Bandwidth + * + * @param instance Pointer to a MeshimiConfig + * @param LoRaBandwidth bandwidth + */ +void meshimi_bandwidth_set(MeshimiConfig* instance, LoRaBandwidth bandwidth); + +/** + * Get Bandwidth + * + * @param instance Pointer to a MeshimiConfig + * @return LoRaBandwidth + */ +LoRaBandwidth meshimi_bandwidth_get(MeshimiConfig* instance); + +/** + * Get text representation of the bandwidth + * + * @param LoRaSpreadingFactor bandwidth + * @return + */ +const char* meshimi_bandwidth_get_text(LoRaBandwidth bandwidth); + +/** + * Set Coding Rate + * + * @param instance Pointer to a MeshimiConfig + * @param LoRaCodingRate coding_rate + */ +void meshimi_coding_rate_set(MeshimiConfig* instance, LoRaCodingRate coding_rate); + +/** + * Get Coding Rate + * + * @param instance Pointer to a MeshimiConfig + * @return LoRaCodingRate + */ +LoRaCodingRate meshimi_coding_rate_get(MeshimiConfig* instance); + +/** + * Get text representation of the coding rate + * + * @param LoRaCodingRate coding_rate + * @return + */ +const char* meshimi_coding_rate_get_text(LoRaCodingRate coding_rate); + +/** + * Set Low DataRate Optimization + * + * @param instance Pointer to a MeshimiConfig + * @param LoRaCodingRate ldro + */ +void meshimi_ldro_set(MeshimiConfig* instance, LoRaLowDataRateOptimization ldro); + +/** + * Get Low DataRate Optimization + * + * @param instance Pointer to a MeshimiConfig + * @return LoRaLowDataRateOptimization + */ +LoRaLowDataRateOptimization meshimi_ldro_get(MeshimiConfig* instance); + +/** + * Get text representation of the low dataRate optimization + * + * @param LoRaLowDataRateOptimization ldro + * @return + */ +const char* meshimi_ldro_get_text(LoRaLowDataRateOptimization ldro); \ No newline at end of file diff --git a/meshimi_custom_event.h b/meshimi_custom_event.h index 542e346..d89bcc6 100644 --- a/meshimi_custom_event.h +++ b/meshimi_custom_event.h @@ -2,4 +2,8 @@ typedef enum { MeshimiEventMode = 0, + MeshimiEventSpreadingFactor, + MeshimiEventBandwidth, + MeshimiEventCodingRate, + MeshimiEventLDRO, } MeshimiCustomEvent; \ No newline at end of file diff --git a/proto/spi.pb.c b/proto/spi.pb.c index 8be2d38..9675c56 100644 --- a/proto/spi.pb.c +++ b/proto/spi.pb.c @@ -46,3 +46,4 @@ PB_BIND(LoRaPacketParams, LoRaPacketParams, AUTO) + diff --git a/proto/spi.pb.h b/proto/spi.pb.h index f1efbeb..3f6526a 100644 --- a/proto/spi.pb.h +++ b/proto/spi.pb.h @@ -44,6 +44,12 @@ typedef enum _LoRaBandwidth { LoRaBandwidth_BW_500 = 9 } LoRaBandwidth; +/* LoRa Low DataRate Optimization */ +typedef enum _LoRaLowDataRateOptimization { + LoRaLowDataRateOptimization_LDRO_OFF = 0, + LoRaLowDataRateOptimization_LDRO_ON = 1 +} LoRaLowDataRateOptimization; + /* LoRa packet length enumeration */ typedef enum _LoRaPacketLengthMode { LoRaPacketLengthMode_EXPLICIT = 0, @@ -99,7 +105,7 @@ typedef struct _LoRaModulationParams { LoRaSpreadingFactor sf; /* LoRa Spreading Factor */ LoRaCodingRate cr; /* LoRa Coding Rate */ LoRaBandwidth bw; /* LoRa Bandwidth */ - uint8_t ldro; /* Low DataRate Optimization configuration */ + LoRaLowDataRateOptimization ldro; /* Low DataRate Optimization configuration */ } LoRaModulationParams; /* LoRa packet parameters */ @@ -166,6 +172,10 @@ extern "C" { #define _LoRaBandwidth_MAX LoRaBandwidth_BW_500 #define _LoRaBandwidth_ARRAYSIZE ((LoRaBandwidth)(LoRaBandwidth_BW_500+1)) +#define _LoRaLowDataRateOptimization_MIN LoRaLowDataRateOptimization_LDRO_OFF +#define _LoRaLowDataRateOptimization_MAX LoRaLowDataRateOptimization_LDRO_ON +#define _LoRaLowDataRateOptimization_ARRAYSIZE ((LoRaLowDataRateOptimization)(LoRaLowDataRateOptimization_LDRO_ON+1)) + #define _LoRaPacketLengthMode_MIN LoRaPacketLengthMode_EXPLICIT #define _LoRaPacketLengthMode_MAX LoRaPacketLengthMode_IMPLICIT #define _LoRaPacketLengthMode_ARRAYSIZE ((LoRaPacketLengthMode)(LoRaPacketLengthMode_IMPLICIT+1)) @@ -192,6 +202,7 @@ extern "C" { #define LoRaModulationParams_sf_ENUMTYPE LoRaSpreadingFactor #define LoRaModulationParams_cr_ENUMTYPE LoRaCodingRate #define LoRaModulationParams_bw_ENUMTYPE LoRaBandwidth +#define LoRaModulationParams_ldro_ENUMTYPE LoRaLowDataRateOptimization #define LoRaPacketParams_headerType_ENUMTYPE LoRaPacketLengthMode @@ -206,7 +217,7 @@ extern "C" { #define Response_LoRaMessageReceived_init_default {false, LoRaMessage_init_default} #define Response_Status_init_default {_Response_Status_Enum_MIN, {{NULL}, NULL}} #define LoRaMessage_init_default {{0, {0}}, 0, 0, 0} -#define LoRaModulationParams_init_default {_LoRaSpreadingFactor_MIN, _LoRaCodingRate_MIN, _LoRaBandwidth_MIN, 0} +#define LoRaModulationParams_init_default {_LoRaSpreadingFactor_MIN, _LoRaCodingRate_MIN, _LoRaBandwidth_MIN, _LoRaLowDataRateOptimization_MIN} #define LoRaPacketParams_init_default {0, _LoRaPacketLengthMode_MIN, 0, 0, 0} #define SpiPacket_init_zero {{0, {0}}, 0} #define SpiHeader_init_zero {_SpiHeader_Status_MIN, 0} @@ -217,7 +228,7 @@ extern "C" { #define Response_LoRaMessageReceived_init_zero {false, LoRaMessage_init_zero} #define Response_Status_init_zero {_Response_Status_Enum_MIN, {{NULL}, NULL}} #define LoRaMessage_init_zero {{0, {0}}, 0, 0, 0} -#define LoRaModulationParams_init_zero {_LoRaSpreadingFactor_MIN, _LoRaCodingRate_MIN, _LoRaBandwidth_MIN, 0} +#define LoRaModulationParams_init_zero {_LoRaSpreadingFactor_MIN, _LoRaCodingRate_MIN, _LoRaBandwidth_MIN, _LoRaLowDataRateOptimization_MIN} #define LoRaPacketParams_init_zero {0, _LoRaPacketLengthMode_MIN, 0, 0, 0} /* Field tags (for use in manual encoding/decoding) */ @@ -331,7 +342,7 @@ X(a, STATIC, SINGULAR, INT32, snr, 4) X(a, STATIC, SINGULAR, UENUM, sf, 1) \ X(a, STATIC, SINGULAR, UENUM, cr, 2) \ X(a, STATIC, SINGULAR, UENUM, bw, 3) \ -X(a, STATIC, SINGULAR, UINT32, ldro, 4) +X(a, STATIC, SINGULAR, UENUM, ldro, 4) #define LoRaModulationParams_CALLBACK NULL #define LoRaModulationParams_DEFAULT NULL @@ -373,11 +384,11 @@ extern const pb_msgdesc_t LoRaPacketParams_msg; /* Response_size depends on runtime parameters */ /* Response_Status_size depends on runtime parameters */ #define LoRaMessage_size 283 -#define LoRaModulationParams_size 9 +#define LoRaModulationParams_size 8 #define LoRaPacketParams_size 16 -#define Request_ConnectToNetwork_size 37 -#define Request_size 45 -#define Response_ConnectToNetwork_size 37 +#define Request_ConnectToNetwork_size 36 +#define Request_size 44 +#define Response_ConnectToNetwork_size 36 #define Response_LoRaMessageReceived_size 286 #define SpiHeader_size 8 #define SpiPacket_size 521 diff --git a/scenes/meshimi_scene_configuration.c b/scenes/meshimi_scene_configuration.c index 1aeda8b..d128ee6 100644 --- a/scenes/meshimi_scene_configuration.c +++ b/scenes/meshimi_scene_configuration.c @@ -14,30 +14,121 @@ static void meshimi_scene_configuration_var_list_enter_callback(void* context, u } } +void meshimi_scene_configuration_spreading_factor_callback(VariableItem* item) { + Meshimi* meshimi = variable_item_get_context(item); + furi_assert(meshimi); + MeshimiConfig* config = meshimi->config; + furi_assert(config); + + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, meshimi_spreading_factor_get_text(index)); + meshimi_spreading_factor_set(config, index); + + view_dispatcher_send_custom_event(meshimi->view_dispatcher, MeshimiEventSpreadingFactor); +} + +void meshimi_scene_configuration_bandwidth_callback(VariableItem* item) { + Meshimi* meshimi = variable_item_get_context(item); + furi_assert(meshimi); + MeshimiConfig* config = meshimi->config; + furi_assert(config); + + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, meshimi_bandwidth_get_text(index)); + meshimi_bandwidth_set(config, index); + + view_dispatcher_send_custom_event(meshimi->view_dispatcher, MeshimiEventBandwidth); +} + +void meshimi_scene_configuration_coding_rate_callback(VariableItem* item) { + Meshimi* meshimi = variable_item_get_context(item); + furi_assert(meshimi); + MeshimiConfig* config = meshimi->config; + furi_assert(config); + + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, meshimi_coding_rate_get_text(index)); + meshimi_coding_rate_set(config, index); + + view_dispatcher_send_custom_event(meshimi->view_dispatcher, MeshimiEventCodingRate); +} + +void meshimi_scene_configuration_ldro_callback(VariableItem* item) { + Meshimi* meshimi = variable_item_get_context(item); + furi_assert(meshimi); + MeshimiConfig* config = meshimi->config; + furi_assert(config); + + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, meshimi_ldro_get_text(index)); + meshimi_ldro_set(config, index); + + view_dispatcher_send_custom_event(meshimi->view_dispatcher, MeshimiEventLDRO); +} + void meshimi_scene_configuration_on_enter(void* context) { Meshimi* meshimi = context; - MeshimiConfig *config = meshimi->config; + MeshimiConfig* config = meshimi->config; MeshimiConfigMode mode = meshimi_mode_get(config); + VariableItem* item; variable_item_list_set_enter_callback( meshimi->variable_item_list, meshimi_scene_configuration_var_list_enter_callback, meshimi); - if (mode == ModeSimpleRX) { - variable_item_list_add( - meshimi->variable_item_list, "Mode: Simple RX", 0, NULL, NULL); - - } else if (mode == ModeSimpleTX) { - variable_item_list_add( - meshimi->variable_item_list, "Mode: Simple TX", 0, NULL, NULL); - } else if (mode == ModeMeshtastic) { - variable_item_list_add( - meshimi->variable_item_list, "Mode: Meshtastic", 0, NULL, NULL); - } else if (mode == ModeMeshimi) { - variable_item_list_add( - meshimi->variable_item_list, "Mode: Meshimi", 0, NULL, NULL); - } else if (mode == ModeLoRaWAN) { - variable_item_list_add( - meshimi->variable_item_list, "Mode: LoRaWAN", 0, NULL, NULL); + if(mode == ModeSimpleRX) { + variable_item_list_add(meshimi->variable_item_list, "Mode: Simple RX", 0, NULL, NULL); + item = variable_item_list_add( + meshimi->variable_item_list, + "Spread. Factor:", + SPREADING_FACTOR_COUNT, + meshimi_scene_configuration_spreading_factor_callback, + meshimi); + variable_item_set_current_value_index(item, meshimi_spreading_factor_get(config)); + variable_item_set_current_value_text( + item, meshimi_spreading_factor_get_text(meshimi_spreading_factor_get(config))); + + item = variable_item_list_add( + meshimi->variable_item_list, + "Bandwidth:", + BANDWIDTH_COUNT, + meshimi_scene_configuration_bandwidth_callback, + meshimi); + variable_item_set_current_value_index(item, meshimi_bandwidth_get(config)); + variable_item_set_current_value_text( + item, meshimi_bandwidth_get_text(meshimi_bandwidth_get(config))); + + item = variable_item_list_add( + meshimi->variable_item_list, + "Coding Rate:", + CODING_RATE_COUNT, + meshimi_scene_configuration_coding_rate_callback, + meshimi); + variable_item_set_current_value_index(item, meshimi_coding_rate_get(config)); + variable_item_set_current_value_text( + item, meshimi_coding_rate_get_text(meshimi_coding_rate_get(config))); + + item = variable_item_list_add( + meshimi->variable_item_list, + "LDRO:", + LDRO_COUNT, + meshimi_scene_configuration_ldro_callback, + meshimi); + variable_item_set_current_value_index(item, meshimi_ldro_get(config)); + variable_item_set_current_value_text( + item, meshimi_ldro_get_text(meshimi_ldro_get(config))); + + } else if(mode == ModeSimpleTX) { + variable_item_list_add(meshimi->variable_item_list, "Mode: Simple TX", 0, NULL, NULL); + } else if(mode == ModeMeshtastic) { + variable_item_list_add(meshimi->variable_item_list, "Mode: Meshtastic", 0, NULL, NULL); + } else if(mode == ModeMeshimi) { + variable_item_list_add(meshimi->variable_item_list, "Mode: Meshimi", 0, NULL, NULL); + } else if(mode == ModeLoRaWAN) { + variable_item_list_add(meshimi->variable_item_list, "Mode: LoRaWAN", 0, NULL, NULL); } view_dispatcher_switch_to_view(meshimi->view_dispatcher, MeshimiViewIdVariableItemList); From 3bc9e290e3fc0e7a796471f07aee999ca3ed5542 Mon Sep 17 00:00:00 2001 From: Alexander Begoon Date: Tue, 30 Apr 2024 21:51:48 +0200 Subject: [PATCH 7/9] Add LoRa Frequency configuration --- helpers/meshimi_config.c | 12 +++ helpers/meshimi_config.h | 28 ++++++- meshimi.h | 2 + meshimi_custom_event.h | 1 + proto/spi.pb.h | 4 +- scenes/meshimi_scene_config.h | 1 + scenes/meshimi_scene_configuration.c | 17 ++++- scenes/meshimi_scene_frequency.c | 106 +++++++++++++++++++++++++++ 8 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 scenes/meshimi_scene_frequency.c diff --git a/helpers/meshimi_config.c b/helpers/meshimi_config.c index 2444352..2530b4d 100644 --- a/helpers/meshimi_config.c +++ b/helpers/meshimi_config.c @@ -55,6 +55,7 @@ const char* const ldro_text[LDRO_COUNT] = { struct MeshimiConfig { MeshimiConfigMode mode; + uint32_t lora_frequency; LoRaSpreadingFactor lora_spreading_factor; LoRaBandwidth lora_bandwidth; LoRaCodingRate lora_coding_rate; @@ -66,6 +67,7 @@ MeshimiConfig* meshimi_config_alloc(void) { // TODO: Instantiate it from conf file stored at SD Card instance->mode = ModeSimpleRX; + instance->lora_frequency = 868000000U; instance->lora_spreading_factor = LoRaSpreadingFactor_SF7; instance->lora_bandwidth = LoRaBandwidth_BW_125; instance->lora_coding_rate = LoRaCodingRate_CR_4_7; @@ -151,3 +153,13 @@ LoRaLowDataRateOptimization meshimi_ldro_get(MeshimiConfig* instance) { const char* meshimi_ldro_get_text(LoRaLowDataRateOptimization ldro) { return ldro_text[ldro]; } + +void meshimi_frequency_set(MeshimiConfig* instance, uint32_t frequency) { + furi_assert(instance); + instance->lora_frequency = frequency; +} + +uint32_t meshimi_frequency_get(MeshimiConfig* instance) { + furi_assert(instance); + return instance->lora_frequency; +} diff --git a/helpers/meshimi_config.h b/helpers/meshimi_config.h index a582447..5ae0612 100644 --- a/helpers/meshimi_config.h +++ b/helpers/meshimi_config.h @@ -16,9 +16,11 @@ typedef enum MeshimiConfigMode { #define BANDWIDTH_COUNT 10 #define CODING_RATE_COUNT 4 #define LDRO_COUNT 2 +#define FREQUENCY_TEXT_LEN 10 typedef struct { MeshimiConfigMode mode; + uint32_t lora_frequency; LoRaSpreadingFactor lora_spreading_factor; LoRaBandwidth lora_bandwidth; LoRaCodingRate lora_coding_rate; @@ -161,4 +163,28 @@ LoRaLowDataRateOptimization meshimi_ldro_get(MeshimiConfig* instance); * @param LoRaLowDataRateOptimization ldro * @return */ -const char* meshimi_ldro_get_text(LoRaLowDataRateOptimization ldro); \ No newline at end of file +const char* meshimi_ldro_get_text(LoRaLowDataRateOptimization ldro); + +/** + * Set Frequency + * + * @param instance Pointer to a MeshimiConfig + * @param uint32_t frequency + */ +void meshimi_frequency_set(MeshimiConfig* instance, uint32_t frequency); + +/** + * Get Frequency + * + * @param instance Pointer to a MeshimiConfig + * @return uint32_t + */ +uint32_t meshimi_frequency_get(MeshimiConfig* instance); + +/** + * Get text representation of the frequency + * + * @param uint32_t frequency + * @return + */ +const char* meshimi_frequency_get_text(uint32_t frequency); \ No newline at end of file diff --git a/meshimi.h b/meshimi.h index 5dca6d4..0c02dd2 100644 --- a/meshimi.h +++ b/meshimi.h @@ -26,6 +26,7 @@ #define MESHIMI_GET_MODULE "begoonlab.tech/meshimi" #define BACKLIGHT_ALWAYS_ON #define LOG_TAG "Meshimi" +#define TEXT_STORE_SIZE 64U typedef struct Meshimi Meshimi; @@ -39,6 +40,7 @@ struct Meshimi { SceneManager* scene_manager; ViewDispatcher* view_dispatcher; + char text_store[TEXT_STORE_SIZE]; Submenu* submenu; Popup* popup; TextInput* text_input; diff --git a/meshimi_custom_event.h b/meshimi_custom_event.h index d89bcc6..96930f8 100644 --- a/meshimi_custom_event.h +++ b/meshimi_custom_event.h @@ -6,4 +6,5 @@ typedef enum { MeshimiEventBandwidth, MeshimiEventCodingRate, MeshimiEventLDRO, + MeshimiEventFrequency, } MeshimiCustomEvent; \ No newline at end of file diff --git a/proto/spi.pb.h b/proto/spi.pb.h index 3f6526a..d171a91 100644 --- a/proto/spi.pb.h +++ b/proto/spi.pb.h @@ -52,8 +52,8 @@ typedef enum _LoRaLowDataRateOptimization { /* LoRa packet length enumeration */ typedef enum _LoRaPacketLengthMode { - LoRaPacketLengthMode_EXPLICIT = 0, - LoRaPacketLengthMode_IMPLICIT = 1 + LoRaPacketLengthMode_EXPLICIT = 0, /* Header included in the packet */ + LoRaPacketLengthMode_IMPLICIT = 1 /* Header not included in the packet */ } LoRaPacketLengthMode; typedef enum _SpiHeader_Status { diff --git a/scenes/meshimi_scene_config.h b/scenes/meshimi_scene_config.h index dbfc297..bca0dfd 100644 --- a/scenes/meshimi_scene_config.h +++ b/scenes/meshimi_scene_config.h @@ -4,3 +4,4 @@ ADD_SCENE(meshimi, test, Test) ADD_SCENE(meshimi, configuration, Configuration) ADD_SCENE(meshimi, about, About) ADD_SCENE(meshimi, mode, Mode) +ADD_SCENE(meshimi, frequency, Frequency) diff --git a/scenes/meshimi_scene_configuration.c b/scenes/meshimi_scene_configuration.c index d128ee6..55161a0 100644 --- a/scenes/meshimi_scene_configuration.c +++ b/scenes/meshimi_scene_configuration.c @@ -2,8 +2,9 @@ #include "meshimi_custom_event.h" #include -enum MeshimiItem { +enum MeshimiConfigurationItem { MeshimiMode, + MeshimiFrequency, }; static void meshimi_scene_configuration_var_list_enter_callback(void* context, uint32_t index) { @@ -11,6 +12,8 @@ static void meshimi_scene_configuration_var_list_enter_callback(void* context, u Meshimi* meshimi = context; if(index == MeshimiMode) { view_dispatcher_send_custom_event(meshimi->view_dispatcher, MeshimiEventMode); + } else if(index == MeshimiFrequency) { + view_dispatcher_send_custom_event(meshimi->view_dispatcher, MeshimiEventFrequency); } } @@ -80,7 +83,10 @@ void meshimi_scene_configuration_on_enter(void* context) { meshimi->variable_item_list, meshimi_scene_configuration_var_list_enter_callback, meshimi); if(mode == ModeSimpleRX) { - variable_item_list_add(meshimi->variable_item_list, "Mode: Simple RX", 0, NULL, NULL); + variable_item_list_add( + meshimi->variable_item_list, "Mode: Simple RX", 0, NULL, NULL); + variable_item_list_add( + meshimi->variable_item_list, "Freq: 868,000,000Hz", 0, NULL, NULL); item = variable_item_list_add( meshimi->variable_item_list, "Spread. Factor:", @@ -142,8 +148,13 @@ bool meshimi_scene_configuration_on_event(void* context, SceneManagerEvent event if(event.event == MeshimiEventMode) { scene_manager_set_scene_state(meshimi->scene_manager, MeshimiSceneStart, MeshimiMode); scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneMode); + consumed = true; + } else if(event.event == MeshimiEventFrequency) { + scene_manager_set_scene_state( + meshimi->scene_manager, MeshimiSceneStart, MeshimiFrequency); + scene_manager_next_scene(meshimi->scene_manager, MeshimiSceneFrequency); + consumed = true; } - consumed = true; } if(event.type == SceneManagerEventTypeBack) { diff --git a/scenes/meshimi_scene_frequency.c b/scenes/meshimi_scene_frequency.c new file mode 100644 index 0000000..fabb04b --- /dev/null +++ b/scenes/meshimi_scene_frequency.c @@ -0,0 +1,106 @@ +#include "../meshimi.h" +#include "meshimi_custom_event.h" +#include +#include +#include + +void meshimi_scene_frequency_input_callback(void* context) { + furi_assert(context); + Meshimi* meshimi = context; + view_dispatcher_send_custom_event(meshimi->view_dispatcher, MeshimiEventFrequency); +} + +bool validator_lora_frequency_callback(const char* text, FuriString* error, void* context) { + furi_check(context); + + // Check if the text length is exactly 9 digits + if(strlen(text) != 9) { + furi_string_printf(error, "Frequency\nmust be\nexactly 9\ndigits."); + return false; + } + + // Check if all characters are digits + for(int i = 0; i < 9; i++) { + if(!isdigit((unsigned char)text[i])) { + furi_string_printf(error, "Frequency\nmust contain\nonly digits."); + return false; + } + } + + char* endptr; + long frequency = strtol(text, &endptr, 10); + + // Check if the entire string was successfully converted + if(*endptr != '\0') { + furi_string_printf(error, "Frequency\nis invalid."); + return false; + } + + const FuriHalRegion* const region = furi_hal_region_get(); + + // Check regional RF allowance + if(region) { + for(uint16_t i = 0; i < region->bands_count; ++i) { + if((unsigned long)frequency < region->bands[i].start || + (unsigned long)frequency > region->bands[i].end) { + furi_string_printf(error, "Frequency\nis out of\nallowed\nrange"); + return false; + } + } + } else { + // Check LoRa modem absolute limits + if(frequency < 150000000 || frequency > 960000000) { + furi_string_printf(error, "Frequency\nis out of\nallowed\nrange"); + return false; + } + } + + return true; +} + +void meshimi_scene_frequency_on_enter(void* context) { + Meshimi* meshimi = context; + MeshimiConfig* config = meshimi->config; + FuriString* frequency_text = furi_string_alloc(); + furi_string_printf(frequency_text, "%lu", meshimi_frequency_get(config)); + + strncpy(meshimi->text_store, furi_string_get_cstr(frequency_text), FREQUENCY_TEXT_LEN); + text_input_set_header_text(meshimi->text_input, "LoRa Frequency:"); + text_input_set_result_callback( + meshimi->text_input, + meshimi_scene_frequency_input_callback, + meshimi, + meshimi->text_store, + FREQUENCY_TEXT_LEN, + NULL); + + text_input_set_validator(meshimi->text_input, validator_lora_frequency_callback, meshimi); + + furi_string_free(frequency_text); + + view_dispatcher_switch_to_view(meshimi->view_dispatcher, MeshimiViewIdTextInput); +} + +bool meshimi_scene_frequency_on_event(void* context, SceneManagerEvent event) { + Meshimi* meshimi = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == MeshimiEventFrequency) { + scene_manager_previous_scene(meshimi->scene_manager); + consumed = true; + } + } + + return consumed; +} + +void meshimi_scene_frequency_on_exit(void* context) { + Meshimi* meshimi = context; + + // Clear validator + text_input_set_validator(meshimi->text_input, NULL, NULL); + + // Clear view + text_input_reset(meshimi->text_input); +} \ No newline at end of file From c36171c51424ba1207f72d8232ff31e999bbb2c8 Mon Sep 17 00:00:00 2001 From: Alexander Begoon Date: Tue, 30 Apr 2024 21:59:24 +0200 Subject: [PATCH 8/9] Fix regional RF validation --- scenes/meshimi_scene_frequency.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/scenes/meshimi_scene_frequency.c b/scenes/meshimi_scene_frequency.c index fabb04b..f352e30 100644 --- a/scenes/meshimi_scene_frequency.c +++ b/scenes/meshimi_scene_frequency.c @@ -40,13 +40,18 @@ bool validator_lora_frequency_callback(const char* text, FuriString* error, void // Check regional RF allowance if(region) { + bool is_within_range = false; for(uint16_t i = 0; i < region->bands_count; ++i) { - if((unsigned long)frequency < region->bands[i].start || - (unsigned long)frequency > region->bands[i].end) { - furi_string_printf(error, "Frequency\nis out of\nallowed\nrange"); - return false; + if((unsigned long)frequency >= region->bands[i].start && + (unsigned long)frequency <= region->bands[i].end) { + is_within_range = true; + break; } } + if (!is_within_range) { + furi_string_printf(error, "Frequency\nis out of\nallowed\nrange"); + return false; + } } else { // Check LoRa modem absolute limits if(frequency < 150000000 || frequency > 960000000) { From 86df50e2eb830d505245fe0459144046fc729df0 Mon Sep 17 00:00:00 2001 From: Alexander Begoon Date: Tue, 30 Apr 2024 23:02:46 +0200 Subject: [PATCH 9/9] Add LoRa Frequency configuration --- scenes/meshimi_scene_frequency.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/scenes/meshimi_scene_frequency.c b/scenes/meshimi_scene_frequency.c index f352e30..82542fe 100644 --- a/scenes/meshimi_scene_frequency.c +++ b/scenes/meshimi_scene_frequency.c @@ -48,7 +48,7 @@ bool validator_lora_frequency_callback(const char* text, FuriString* error, void break; } } - if (!is_within_range) { + if(!is_within_range) { furi_string_printf(error, "Frequency\nis out of\nallowed\nrange"); return false; } @@ -88,10 +88,21 @@ void meshimi_scene_frequency_on_enter(void* context) { bool meshimi_scene_frequency_on_event(void* context, SceneManagerEvent event) { Meshimi* meshimi = context; + MeshimiConfig* config = meshimi->config; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == MeshimiEventFrequency) { + char* endptr; + long frequency = strtol(meshimi->text_store, &endptr, 10); + + // Check if the entire string was successfully converted + if(*endptr != '\0') { + furi_crash(); + } + + meshimi_frequency_set(config, frequency); + scene_manager_previous_scene(meshimi->scene_manager); consumed = true; }