From e7c0343339e7cb17334937f31803b0a619ce4619 Mon Sep 17 00:00:00 2001 From: Andrew Dunai Date: Sat, 20 Apr 2024 23:54:34 +0300 Subject: [PATCH 1/2] keira: allow to enable/disable WiFi keira: fix clock service stealing all CPU time when network is not available keira: use keira nvs namespace for network stuff keira: update battery icons keira: add telnet nvs commands sdk: add colors to log levels sdk: do not deinit WiFi on startup since it's unnecessary --- firmware/keira/src/apps/icons/battery.h | 96 +-- firmware/keira/src/apps/icons/battery.png | Bin 5290 -> 5515 bytes .../keira/src/apps/icons/battery_absent.h | 96 +-- .../keira/src/apps/icons/battery_absent.png | Bin 5351 -> 5570 bytes .../keira/src/apps/icons/battery_danger.h | 96 +-- .../keira/src/apps/icons/battery_danger.png | Bin 5284 -> 5515 bytes firmware/keira/src/apps/icons/music.h | 6 +- firmware/keira/src/apps/icons/wifi_disabled.h | 584 ++++++++++++++++++ .../keira/src/apps/icons/wifi_disabled.png | Bin 0 -> 4630 bytes firmware/keira/src/apps/icons/wifi_offline.h | 428 ++++++------- .../keira/src/apps/icons/wifi_offline.png | Bin 4667 -> 4754 bytes firmware/keira/src/apps/launcher.cpp | 35 +- firmware/keira/src/apps/statusbar.cpp | 5 +- firmware/keira/src/services/clock.cpp | 2 + firmware/keira/src/services/network.cpp | 125 ++-- firmware/keira/src/services/network.h | 4 + firmware/keira/src/services/telnet.cpp | 194 +++++- sdk/lib/lilka/src/lilka.cpp | 1 - sdk/lib/lilka/src/lilka/serial.cpp | 7 +- 19 files changed, 1235 insertions(+), 444 deletions(-) create mode 100644 firmware/keira/src/apps/icons/wifi_disabled.h create mode 100644 firmware/keira/src/apps/icons/wifi_disabled.png diff --git a/firmware/keira/src/apps/icons/battery.h b/firmware/keira/src/apps/icons/battery.h index d5fafea5..470b8ff2 100644 --- a/firmware/keira/src/apps/icons/battery.h +++ b/firmware/keira/src/apps/icons/battery.h @@ -87,23 +87,22 @@ const uint16_t battery_img[] = { 0xf81f, 0xf81f, 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -112,14 +111,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -128,14 +127,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -144,14 +143,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -160,14 +159,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -176,14 +175,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -192,14 +191,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -208,14 +207,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -224,14 +223,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -240,14 +239,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -256,14 +255,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -272,14 +271,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -288,14 +287,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -304,14 +303,14 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, 0x0000, 0x0000, 0x0000, @@ -320,23 +319,24 @@ const uint16_t battery_img[] = { 0x0000, 0x0000, 0x0000, - 0xffff, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, 0xf81f, 0xf81f, 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, 0xffff, 0xf81f, 0xf81f, diff --git a/firmware/keira/src/apps/icons/battery.png b/firmware/keira/src/apps/icons/battery.png index 5892d729e5d7cbae43fe3b9903160ffcc83cde21..5d7510fce8c3a1dbe160bca1eafb3789598c00d9 100644 GIT binary patch delta 826 zcmZ3b*{waHpOJau1ZUnqnhXrl3=9lldg23dUK1-L0|g^vD+6;YBa6u_jF$D^+k8(m zFfg`cIy(n=Iy);A6y>L7=A<$(RLrTJXzOvjy6c8<36RP3V z8lb)9RYK8Ad6P`fpcom}>rKt}-J$H_>9Mjgtd1YpkK8=ydC}GDkDlh1j*oqh)_Dwp+ZOYL$I|@_vI!=3&!z`en0t>LhU*sjOJB zxTl^g=SYUR$HocEKDYGO-}rj^NzlnqHs#|?t1c|uQTM_A$KB}F2Bt4nU4kJY{j-zI-?bMKa8WWIgiXsLVf_lDE52`^SIo6)7s zn#tM9^r6n}{uE}m{KkEz>+|0h&#_~Wd=>hN&BB7^m~_{az3)E1yZ3J2-{{Nl?(_Hm zD%uqJ{_%bW2Epf>uP}8nsTidunIY62)B&8V{m?tHf zSxjzXUCwK4W&%W(#ulb#rUsME*z)W^u^j@7ZEXW!v>PaKDJUpZ0xjHJ$eqn$_u$#pSHMKbmgMd3!eGPT!(beJ=(iV8gtNdS zvY3HEPZ@+6E0)@q0R`DhJbhi+U$BUXu*+%(EcrTFN<-g`4%}`QWaak o!1{x=AvI+2XRiE-9~7L}?f(eqn{S)>3uqyOr>mdKI;Vst0MG(J1poj5 delta 740 zcmeCyUZpvqpOI z>=t8YQYxGzxy*Nuqs`__={es!lh-J%KJxsCuv+Yd9}{199nYV0T94btIPKY{&qoZC zHr_ex#lN|0rJ%p?@lfC8HHz;yo~rEL9G1P|ob6BU!>+ZdGhdsyPduhHO~J9HiEY)2 z`h#vwVRJ>eBX9Zt{NWz6-$k!ACe)xvX8|AYrq}HMoA2L^TDsCxUvm!AuXvf~$vhly zncgmYTqAGo_nU>ojyb&QdVD_P=J`v^cSrr<=*VLhzaHy*lUc94;i0;-S!y`fB@r*x z2Y-9^tz_W2%ha45mp`|5jva&LE3IN?85yR>sZyunzE^&C$j|>j@8y^8%*j$k3(o%J ze9pii$g=q=Qx}tpxw)xPs)eC}uAzmIv93v)sim%kiKT^Zs(G53foW=zp+TDQ1E(HaJirfM#-~5!!v`Ux6l2ltI zBLgE7D+6@Zn;)~yV%eO)lfWT%_o3W*U@Bru@^*It(m?Rjd3XKfYa;TT%sjlRf*bXw zCQkk*qLC5o>Eak-ar*5PLp}xt4ra}d|Kr!~?r@NZd7-f|#*0NHNGd2ZYvL7=A<$(RLrTJXzOvjy6c8<36RP3V z8lb)9RYK8Ad6P`fpcom}>rKt}-J$H_>9Mjgtd1YpkK8=ydC}GDkDlh1j*oqh)_Dwp+ZOYL$I|@_vI!=3&!z`en0t>LhU*sjOJB zxTl^g=SYUR$HocEKDYGO-}rj^NzlnqHs#|?t1c|uQTM_A$KB}F2Bt4nU4kJY{j-zI-?bMKa8WWIgiXsLVf_lDE52`^SIo6)7s zn#tM9^r6n}{uE}m{KkEz>+|0h&#_~Wd=>hN&BB7^m~_{az3)E1yZ3J2-{{Nl?(_Hm zD%uqJ{_%bW2Epf>uP}8pDJPj*S{fxK>82(n8ta-Q8k*`_n3x&prlloXnx-ZjSR`2* zPi|se&TDLD0z@X3hDN5wMw89h^6Ws79RiDNZ3AGe8z^xpC@56q7FhY_r(~v8x+IpQ z+A0|t7@1fZpsU{eh;0_jMVQCL5VsnwXfF z8JVY~PA=f^5HQh2wQzGGcQ%Jz>w@HFU>amg@^*J&uwn3FFpfU-+Y2bdS>O>_%)p?h z48n{ROYO^mg6t)pzOL*qSVTnFC8DhYswPW`XwaV>$hs&K>|dl)=;0&t;ucLK6TC0a^n9 delta 801 zcmX@4{akZGKO@t`3C_Gaj0_Aw!~muzJ`m?MwlXwQFf_F?Hn%c0oy@{$S z>=t8YQYxGzxy*Nuqs`__={es!lh-J%KJxsCuv+Yd9}{199nYV0T94btIPKY{&qoZC zHr_ex#lN|0rJ%p?@lfC8HHz;yo~rEL9G1P|ob6BU!>+ZdGhdsyPduhHO~J9HiEY)2 z`h#vwVRJ>eBX9Zt{NWz6-$k!ACe)xvX8|AYrq}HMoA2L^TDsCxUvm!AuXvf~$vhly zncgmYTqAGo_nU>ojyb&QdVD_P=J`v^cSrr<=*VLhzaHy*lUc94;i0;-S!y`fB@r*x z2Y-9^tz_W2%ha45mp`|5jva&LE3IN?85yR>sZyunzE^&C$j|>j@8y^8%*j$k3(o%J ze9pii$g=q=Q#X^cNt&strGbU6d9s;-u1TVqnQoFna+=hLg?N^6Wry9RiDMZ381K0|O;41qFqQ+yX1#{FKbJN|(fvR9ht@ z10xeF19a7!AG6J3*_^L+-*IEGl9J{sc5cR+!|<&yp><&P7;r8sLYaFgDtX&~9A8oEMMj3@D;-ub{C zd+yErWWhZ5&0j_am6eU+OMEvSyqO~V`(i`Kp_H_Hw_E=*%t|`nK8-C*!Fcws8LAlq z`j&HCIE6}|?bkE8HBU{kVVxyYWtn5&^6WnsF+hb(bnUzgGAf?Wx8D|0SRFsGAGvwZ^P;QQA3e=29UuE1%ia5Z zZ~E@{3=1O@cZqf}?@FBgGR5iCv#_Q0^TJycF7YHgC5GktO$+`Y8~>f-!(Y8)oBzE2 zC-XPzUXp`@Qs3rTnZo%3=W}kEp8I`7Nn2CuqvgjPp5iKXPjnHHAqWL zGo0MSx}4kC%mj$cjZ91}C!4Sp*n#3Y1QyrY2Ea%+P~uWhP^ic)u=34M$xN$sNi0dV zRWdR#GO;p1SH1ZW+bovJGr3|^QVlFq)6$I7bPWxXQh*LHHPyASFfr0KF|n{PG%zU#t=0fgl4m%OK&v$`IkS)pE-G#x1!H2;(`p|DLpa^GyM`SSr zgPt-7Ggd6MF9Qm)mw5WRvcF&v5n-1;WXyPAvXqEMYJjJUV~EA+w^I&sF(`02$Nu=g ze@~uZ=Ms*K2k(YdVCR?;CK=BIK zTB{i`zgT^xCW(l@w`ocFZ;-Fd;l zZZT#irNT*)%Y64Z+HB60p7Xskd5zNQBhQZrtHn{^>T^RxUG!RGLJf*^7Vzd7`TpIgr7J!4HRmw>ikEqw%){}P z>Fu(|HS*Sezgal!n8T~C$LBL{p1;I=chnz_jyz`Z>#@E!nf1yW9;!Q=rG|4|67f=f z@V95*N(P?0OwHMG`EzUM*fChX(kf<_kzsnADs?LEd*yeB{QUp(UViz`oGewe;OtM% z=L`&jESs+~bup=!rzIzwCMTQdCZ-q|>6#>4nCT{^n49V*TUwf!CmE$0nwlC+Zf0H1 zZD3?-U}k1uVq$JO*_5ro4iwcPu&CBHFtRc*P~uWhP^ic)u=34M$xN$sNi0dVRWdR# zGO;p1SH1Z$+bovN2|NiLVsW$TmIBidTavfC3y=napU%7MCtnkh=Va#LRn>T>Et@&{ zpNK}1ucwP+h{fr*lMnJS7_c~p{`s$8{(WYTXW65+71Ox_JThCnjCJx7Pe&>`Y -const uint16_t music_width = 24; -const uint16_t music_height = 24; -const uint16_t music[] = { +const uint16_t music_img_width = 24; +const uint16_t music_img_height = 24; +const uint16_t music_img[] = { 0x0000, 0x0000, 0x0000, diff --git a/firmware/keira/src/apps/icons/wifi_disabled.h b/firmware/keira/src/apps/icons/wifi_disabled.h new file mode 100644 index 00000000..bf40cab0 --- /dev/null +++ b/firmware/keira/src/apps/icons/wifi_disabled.h @@ -0,0 +1,584 @@ +// This is a generated file, do not edit. +// clang-format off +#include +const uint16_t wifi_disabled_img_width = 24; +const uint16_t wifi_disabled_img_height = 24; +const uint16_t wifi_disabled_img[] = { + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0861, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x52aa, + 0x52aa, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, +}; +// clang-format on diff --git a/firmware/keira/src/apps/icons/wifi_disabled.png b/firmware/keira/src/apps/icons/wifi_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..f1033b5d469ab6bb54cb164db43e00d19a703aa1 GIT binary patch literal 4630 zcmeHLYj6|S6<*l}3m1FxFi9Y8S=I>;+pB#_T5T-ANCwHVjBCr-khHLmD|=(>fwV@l z4T&8{Ft0QO2g;)aD3m6E83+U?W^B?J+O$awp)E~j;*yXt4I#~sOal|sOz*Db*EBO7 zcbfjPG*|cDbI<+mIo~<=NUIH{C5zJ2rllbWlJ0gn%V7V2){GP3=hdkFHta5fD!0$= z&OwZD0yPaOh(XhV%`jq|WJpGmq3l`M?tx=XM{Cmz6ZC#F96y*Cx54r3gpRg{G9)4C zu-^k44ab?Ve+4#Wef+F0IM!Z9{jC|53fo}6$n7abX%j`77z^A_(kxH1JWZlB&C?W5 zTH*PIeZv-MNDo{)w+Y&}PH{L&-3|vD3I}~k9YB!So;6$TuD9lAonL;rs4(Y;y9#<{ zIOz3`WNd4*(QS<)u2Rpd5_6e!;?mLoUdhZRsBkk#{sjaWB-F9G8 z+wJay7qJU#uRVFT^HxVwYUtRm155WmwC}Umc6tu4TP;)=yRnoXQBEp%&UR|n#;LcT zJKmJ?m&P3{mVSHv+pM)uTqQR(;gI-5PkGPEipz6e`a{!Ex=%d%r)fpk_uhPF>w&J5 z@2I=7UDDl;-Zf+X!sLoSuE;JpQnK-xye|%~zVlZ5@`rX5x94Pr^Y&$=zwmkLz}mll zwCvE^lZZJlBz^PKtm{&y@ynzyesyj2^hZhtQjBwxD;uvY4;q-44q7{x-7&TXlJCFr z6Hi~VDcZb$mVjDuz zX2*0~9*#9JfuXvJT5HFuU>-SwVSutG)C1|De$s6q@6M6xxAIEE8Mq_7fFm0$qXFaSst}NX1_i-Q3dAu38^>6DB2JPv0cUB}g4=u?hg&H#YvV*S z%SpZw5R1bK%u1ndq*WS}454U&5m{QW;3ANrH7q6L0z(V9*(O00ATU-Rr$fmSzc3i~ z3ox9DU#JE|C{V30XoT|xrEWV$o5*2FX`P_@;08NZtOV*~!wX);56V?RW0SH{jD;mR z3rA55oD3^10pSSDL=97~o%)Iv79KhSi50Xwg#fx7`ocTHKv08WZ!lPA$A+rz(6R?! zCs|Mhr=S7|I#h?fgLOEUWOy=ez6R$B%8IYyKWS^12hE52Fyt;J0?%*IMe!R|4pzoz z@oAl+UnLaPF9k11aTg-ODj@52Lag|bR3ikc0jwU0bRCo{-;oL=Ns%1IaJazvtT<~U zMVvGH7@YN4NtzTyA8oUYMvnx2YE%e=f@;VkjL{21J%qO(6I2hD?;@`p^R~S)8AvJLstk1CA6GQbr!5Oso`+OzR?fX0eMaK^r z6Tjnhjng$I2F9d3o?YW~jfsIVDUWB@|BSA*;U`8AfIsx2@Of}?>&~y?v*bka(M3)q zA1Owd$9^+V1SOL~uB8$9C&(4885Ydm9fQJD)$J)v{aZ@ry;F>jSZ2DQ=%U+M;C21> z{E~u<9@OYOx#+3>xdiVp*NPt+GbWdxYH4lDc&_*N#&anc)`d)noxXHq z`i*-UT8_1}wPrSNzqP!2{ppFRF|kN4Ufwo zzI*hR(BJ<-)|AeIyh(S?IFA$?YK$M}C0DFjU(lO$&h<`o>Zf}=Prmt8<%!6lHBLf8^BP%0AD+5CYMh3jDZxxoCC3O6aUKA%N&ir?B%K@ zJInEiqI2n{?yipDwN8RsEl~?Y**{;@&$u*U$qoyyb*$|6VypS|&h4_=!PWGDy<>Cp zAKJQKI z#6|lyohg~6?2#neMYrOQc9=0Uz(A*X+NW z@7LdrT6&UGUvm=EuQ-|K$s!!@ncgmYTqAGo_nU>ojyb&QdVD^k@%$y`yQBVabmTFM zUyt>@$E;W0uu$FENHv`6(i1P$2Y-9^tzMn zF*iuowMaBD(Y3TRNKQ62v$U`@Pn~Sfx}4Y8%*4RL%*fEv%*@1W@@LjO0Z=@Lz~Wik zz<6>Yn;eGV=J{-VjFV5X#VV&5SX!7S8R;6C8YStPB$_4bCK;O<=~`H%npz~8C0SUc z8BF$K_Yg4AMHp)YGtquw--=^v%n*=n1O-! zItVj5Y0Rzw3bL1Y`ns~eU=a}(WxDAnC`FOfGhIky`dTp;BOP~n*hx_O9M6_gE zCxtCN6rCX=wDpAzm*dv8OwNlhX6|9?by8l@!XkJxrggW`%r|fDMApAnQ7B*{ZR=Ax$*<#0N=HwF}PT;-Udxb5l!q=(#Vf&PlRPyKpSYDRxuRlIB~V zxSm@a<(B&(^w^lwBb-z&=b464|x2Q*GyP4@xHrk;*g;_*+q*u)+!XCLZ}s5buhW~51KS2DK3tJYr(;v#j1mgv#t)V zf*|+<;_Tq0=prS4mlRsWcyMyvHi%EzVl4!8-TkFANv; zm1VBe977t5Sb_u*3L2=Q1{-nOby6&3={)7*A9ei_xfF6$!N{?IIyA_xAN&t~_kY$Z zO;34A;S|vS;y54UKxh|eHy!8u*m2q?K=2v3(!2h83z+>Rz1h`bN5IfFaB3H8N!_H#amfEi^VZHZ3?YHZ(0b zWH2%`F)}nUGd47nKLw`^FfueaIWsjfH#aymHIwrNZV)p%F)~y!H99ajIyE&bFfx;I z1|ASIIx#X-F*Q0cI65^oD=;#%p9T&Avycd22o_$lp)&vg00v@9M??Vs0RI60puMM) zlLiqVe*+8+2sc9sN^<}J0oX}IK~zY`wbiR{R6!I5@ZYkf2`Paf3M35<$}6bWu&N4C zgII#7#epfLt-(S<0D<980Cz0{RaG1g2(*qgp%^HS5H?vZ=Gzb^r?8KnGL*Skz6d;cTHFq9i zxoHHI!8-aq2)d@8KaF(tv2U-x#H$hhQf6GNSEAr!d+qyu+{3NR^M#})y`~Z5QB>d4 wS(ServiceManager::getInstance()->getService("network")); while (1) { while (!menu.isFinished()) { + lilka::MenuItem wifiItem; + menu.getItem(0, &wifiItem); + wifiItem.postfix = networkService->getEnabled() ? "ON" : "OFF"; + menu.setItem(0, wifiItem.title, wifiItem.icon, wifiItem.color, wifiItem.postfix); menu.update(); menu.draw(canvas); queueDraw(); @@ -331,14 +337,17 @@ void LauncherApp::settingsMenu() { return; } if (index == 0) { - AppManager::getInstance()->runApp(new WiFiConfigApp()); + networkService->setEnabled(!networkService->getEnabled()); } else if (index == 1) { - alert("Keira OS", "by Андерсон & friends"); + if (!networkService->getEnabled()) { + alert("Помилка", "Спочатку увімкніть WiFi-адаптер"); + continue; + } + AppManager::getInstance()->runApp(new WiFiConfigApp()); } else if (index == 2) { + alert("Keira OS", "by Андерсон & friends"); + } else if (index == 3) { char buf[256]; - NetworkService* networkService = - static_cast(ServiceManager::getInstance()->getService("network")); - // TODO: use dynamic_cast and assert networkService != nullptr sprintf( buf, "Модель: %s\n" @@ -355,7 +364,7 @@ void LauncherApp::settingsMenu() { networkService->getIpAddr().c_str() ); alert("Інфо про пристрій", buf); - } else if (index == 3) { + } else if (index == 4) { String labels[16]; int labelCount = lilka::sys.get_partition_labels(labels); labels[labelCount++] = "<< Назад"; @@ -380,7 +389,7 @@ void LauncherApp::settingsMenu() { "Розмір: 0x" + String(lilka::sys.get_partition_size(labels[partitionIndex].c_str()), HEX) ); } - } else if (index == 4) { + } else if (index == 5) { lilka::Alert confirm( "Форматування", "УВАГА: Це очистить ВСІ дані з SD-карти!\n" @@ -423,13 +432,13 @@ void LauncherApp::settingsMenu() { "Систему буде перезавантажено." ); esp_restart(); - } else if (index == 5) { + } else if (index == 6) { lilka::board.enablePowerSavingMode(); esp_light_sleep_start(); - } else if (index == 6) { + } else if (index == 7) { lilka::board.enablePowerSavingMode(); esp_deep_sleep_start(); - } else if (index == 7) { + } else if (index == 8) { esp_restart(); } } diff --git a/firmware/keira/src/apps/statusbar.cpp b/firmware/keira/src/apps/statusbar.cpp index b4a62fc8..726bcf4a 100644 --- a/firmware/keira/src/apps/statusbar.cpp +++ b/firmware/keira/src/apps/statusbar.cpp @@ -2,6 +2,7 @@ #include "icons/battery.h" #include "icons/battery_danger.h" #include "icons/battery_absent.h" +#include "icons/wifi_disabled.h" #include "icons/wifi_offline.h" #include "icons/wifi_connecting.h" #include "icons/wifi_0.h" @@ -63,7 +64,9 @@ int16_t StatusBarApp::drawIcons(lilka::Canvas* iconCanvas) { // Draw WiFi signal strength if (networkService != NULL) { - if (networkService->getNetworkState() == NETWORK_STATE_OFFLINE) { + if (networkService->getNetworkState() == NETWORK_STATE_DISABLED) { + iconCanvas->draw16bitRGBBitmapWithTranColor(xOffset, 0, wifi_disabled_img, 0, 24, 24); + } else if (networkService->getNetworkState() == NETWORK_STATE_OFFLINE) { iconCanvas->draw16bitRGBBitmapWithTranColor(xOffset, 0, wifi_offline_img, 0, 24, 24); } else if (networkService->getNetworkState() == NETWORK_STATE_CONNECTING) { iconCanvas->draw16bitRGBBitmapWithTranColor(xOffset, 0, wifi_connecting_img, 0, 24, 24); diff --git a/firmware/keira/src/services/clock.cpp b/firmware/keira/src/services/clock.cpp index 5e534fde..0ea535b8 100644 --- a/firmware/keira/src/services/clock.cpp +++ b/firmware/keira/src/services/clock.cpp @@ -17,6 +17,8 @@ void ClockService::run() { configTzTime(MYTZ, "ua.pool.ntp.org", "pool.ntp.org"); // Delay for 12 hours vTaskDelay(1000 * 60 * 60 * 12 / portTICK_PERIOD_MS); + } else { + vTaskDelay(1000 / portTICK_PERIOD_MS); } } } diff --git a/firmware/keira/src/services/network.cpp b/firmware/keira/src/services/network.cpp index c6910c7c..a6346b30 100644 --- a/firmware/keira/src/services/network.cpp +++ b/firmware/keira/src/services/network.cpp @@ -12,8 +12,8 @@ #define LILKA_HOSTNAME_PREFIX "LilkaV" // EEPROM preferences used: -// - network.last_ssid - last connected SSID -// - network.[SSID_hash]_pw - password of known network with a given SSID +// - keira.last_ssid - last connected SSID +// - keira.[SSID_hash]_pw - password of known network with a given SSID NetworkService::NetworkService() : Service("network"), @@ -29,47 +29,23 @@ NetworkService::NetworkService() : void NetworkService::run() { Preferences prefs; - // Setting Lilka hostname - // Take LILKA_HOSTNAME_PREFIX as a prefix - // and append MAC to it - // This value should be guaranted random enough - // to avoid potential conflicts - uint8_t mac[6]; - WiFi.macAddress(mac); - char cstrMac[50]; - sprintf(cstrMac, LILKA_HOSTNAME_PREFIX STR(LILKA_VERSION) "_%06X", mac); - WiFi.setHostname(cstrMac); - WiFi.mode(WIFI_STA); - - // Check if there is a known network to connect to - prefs.begin("network", true); - if (!prefs.isKey("last_ssid")) { - lilka::serial_log("NetworkService: no last SSID found, skipping startup connection"); - } else { - String currentSSID = prefs.getString("last_ssid"); - lilka::serial_log("NetworkService: last SSID found: %s", currentSSID.c_str()); - lastPassword = getPassword(currentSSID); - if (lastPassword == "") { - lilka::serial_log("NetworkService: no password found for last SSID, skipping startup connection"); - } else { - connect(currentSSID, lastPassword); - } - } + prefs.begin("keira", true); + bool enabled = prefs.isKey("enabled") ? prefs.getBool("enabled") : false; prefs.end(); WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { switch (event) { case ARDUINO_EVENT_WIFI_STA_START: { - lilka::serial_log("NetworkService: connecting to WiFi"); + lilka::serial_log("NetworkService: got event: connecting to WiFi"); setNetworkState(NETWORK_STATE_CONNECTING); break; } case ARDUINO_EVENT_WIFI_STA_CONNECTED: { - lilka::serial_log("NetworkService: connected to WiFi"); + lilka::serial_log("NetworkService: got event: connected to WiFi"); setNetworkState(NETWORK_STATE_ONLINE); Preferences prefs; String connectedSSID = String(info.wifi_sta_connected.ssid, info.wifi_sta_connected.ssid_len); - prefs.begin("network", false); + prefs.begin("keira", false); if (!prefs.isKey("last_ssid") || !String(prefs.getString("last_ssid")).equals(connectedSSID)) { // Set current SSID as last connected prefs.putString("last_ssid", String(connectedSSID)); @@ -78,7 +54,7 @@ void NetworkService::run() { prefs.end(); String ssidHash = hash(connectedSSID); String savedPassword = getPassword(connectedSSID); - prefs.begin("network", false); + prefs.begin("keira", false); if (savedPassword != lastPassword) { // Save password for the connected network prefs.putString(String(ssidHash + "_pw").c_str(), lastPassword); @@ -89,7 +65,7 @@ void NetworkService::run() { } case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: { lilka::serial_log( - "NetworkService: disconnected from WiFi, reason: %d", info.wifi_sta_disconnected.reason + "NetworkService: got event: disconnected from WiFi, reason: %d", info.wifi_sta_disconnected.reason ); setNetworkState(NETWORK_STATE_OFFLINE); reason = info.wifi_sta_disconnected.reason; @@ -99,21 +75,38 @@ void NetworkService::run() { case ARDUINO_EVENT_WIFI_STA_GOT_IP6: { IPAddress ip = WiFi.localIP(); ipAddr = ip.toString(); - lilka::serial_log("NetworkService: got IP address: %s", ipAddr.c_str()); + lilka::serial_log("NetworkService: got event: got IP address: %s", ipAddr.c_str()); setNetworkState(NETWORK_STATE_ONLINE); break; } case ARDUINO_EVENT_WIFI_STA_LOST_IP: { - lilka::serial_log("NetworkService: lost IP address"); + lilka::serial_log("NetworkService: got event: lost IP address"); ipAddr = ""; setNetworkState(NETWORK_STATE_OFFLINE); break; } + case ARDUINO_EVENT_WIFI_STA_STOP: { + lilka::serial_log("NetworkService: got event: WiFi stopped"); + setNetworkState(NETWORK_STATE_DISABLED); + break; + } default: break; } }); + if (enabled) { + lilka::serial_log("NetworkService: WiFi is enabled, starting auto connection"); + setNetworkState(NETWORK_STATE_OFFLINE); + WiFi.mode(WIFI_STA); + autoConnect(); + } else { + lilka::serial_log("NetworkService: WiFi is disabled, not starting auto connection"); + setNetworkState(NETWORK_STATE_DISABLED); + WiFi.disconnect(true, true); + WiFi.mode(WIFI_OFF); + } + while (1) { // Check if WiFi is deallocated wifi_mode_t mode; @@ -149,16 +142,34 @@ void NetworkService::run() { } } -// bool NetworkService::connect(String ssid, String password) { -// ssid = ssid; -// password = password; -// Preferences prefs; -// prefs.begin("network", false); -// prefs.putString("ssid", ssid); -// prefs.putString("password", password); -// prefs.end(); -// connect(); -// } +void NetworkService::autoConnect() { + // Setting Lilka hostname + // Take LILKA_HOSTNAME_PREFIX as a prefix and append MAC to it + // This value should be random enough to avoid potential conflicts + uint8_t mac[6]; + WiFi.macAddress(mac); + char cstrMac[50]; + sprintf(cstrMac, LILKA_HOSTNAME_PREFIX STR(LILKA_VERSION) "_%02X%02X%02X", mac[3], mac[4], mac[5]); + WiFi.setHostname(cstrMac); + WiFi.mode(WIFI_STA); + + // Check if there is a known network to connect to + Preferences prefs; + prefs.begin("keira", true); + if (!prefs.isKey("last_ssid")) { + lilka::serial_log("NetworkService: no last SSID found, skipping auto connection"); + } else { + String currentSSID = prefs.getString("last_ssid"); + lilka::serial_log("NetworkService: last SSID found: %s", currentSSID.c_str()); + lastPassword = getPassword(currentSSID); + if (lastPassword == "") { + lilka::serial_log("NetworkService: no password found for last SSID, skipping auto connection"); + } else { + connect(currentSSID, lastPassword); + } + } + prefs.end(); +} NetworkState NetworkService::getNetworkState() { return state; @@ -189,9 +200,31 @@ void NetworkService::connect(String ssid, String password) { WiFi.begin(ssid.c_str(), password.c_str()); } +bool NetworkService::getEnabled() { + return state != NETWORK_STATE_DISABLED; +} + +void NetworkService::setEnabled(bool enabled) { + Preferences prefs; + prefs.begin("keira", false); + prefs.putBool("enabled", enabled); + prefs.end(); + + lilka::serial_log("NetworkService: WiFi set to %s", enabled ? "enabled" : "disabled"); + + if (enabled) { + setNetworkState(NETWORK_STATE_OFFLINE); + WiFi.mode(WIFI_STA); + autoConnect(); + } else { + WiFi.disconnect(true, true); + WiFi.mode(WIFI_OFF); + } +} + String NetworkService::getPassword(String ssid) { Preferences prefs; - prefs.begin("network", true); + prefs.begin("keira", true); String ssidHash = hash(ssid); String result; if (!prefs.isKey(String(ssidHash + "_pw").c_str())) { diff --git a/firmware/keira/src/services/network.h b/firmware/keira/src/services/network.h index 38616593..2a927633 100644 --- a/firmware/keira/src/services/network.h +++ b/firmware/keira/src/services/network.h @@ -4,6 +4,7 @@ #include "service.h" enum NetworkState { + NETWORK_STATE_DISABLED, NETWORK_STATE_OFFLINE, NETWORK_STATE_CONNECTING, NETWORK_STATE_ONLINE, @@ -17,12 +18,15 @@ class NetworkService : public Service { int getSignalStrength(); bool connect(String ssid); void connect(String ssid, String password); + bool getEnabled(); + void setEnabled(bool enabled); String getPassword(String ssid); String getIpAddr(); private: void run() override; + void autoConnect(); String hash(String input); void setNetworkState(NetworkState state); diff --git a/firmware/keira/src/services/telnet.cpp b/firmware/keira/src/services/telnet.cpp index 9a68bc78..289dd085 100644 --- a/firmware/keira/src/services/telnet.cpp +++ b/firmware/keira/src/services/telnet.cpp @@ -1,6 +1,7 @@ #include #include "telnet.h" +#include "Preferences.h" #include "servicemanager.h" #include "network.h" @@ -18,26 +19,29 @@ TelnetService::~TelnetService() { typedef struct { const char* command; - void (*function)(String); + void (*function)(std::vector); } Command; Command commands[] = { { "help", - [](String args) { + [](std::vector args) { telnet.println("Доступні команди:"); - telnet.println(" help - показати цей список команд"); - telnet.println(" reboot - перезавантажити пристрій"); - telnet.println(" uptime - показати час роботи пристрою"); - telnet.println(" free - показати стан пам'яті"); - telnet.println(" ls [DIR] - показати список файлів на SD-картці"); - telnet.println(" find [TEXT] - знайти файли на SD-картці, які містять TEXT в назві"); - telnet.println(" exit - розірвати з'єднання"); + telnet.println(" help - показати цей список команд"); + telnet.println(" reboot - перезавантажити пристрій"); + telnet.println(" uptime - показати час роботи пристрою"); + telnet.println(" free - показати стан пам'яті"); + telnet.println(" ls [DIR] - показати список файлів на SD-картці"); + telnet.println(" find [TEXT] - знайти файли на SD-картці, які містять TEXT в назві"); + telnet.println(" nvs get [NS] [KEY] - отримати значення ключа з NVS"); + telnet.println(" nvs rm [NS] [KEY] - видалити ключ з NVS"); + telnet.println(" nvs rm [NS] - видалити всі ключі з namespace в NVS"); + telnet.println(" exit - розірвати з'єднання"); }, }, { "reboot", - [](String args) { + [](std::vector args) { telnet.println("Система перезавантажується..."); telnet.disconnectClient(); esp_restart(); @@ -45,22 +49,23 @@ Command commands[] = { }, { "uptime", - [](String args) { telnet.println("Uptime: " + String(millis() / 1000) + " seconds"); }, + [](std::vector args) { telnet.println("Uptime: " + String(millis() / 1000) + " seconds"); }, }, { "free", - [](String args) { + [](std::vector args) { telnet.println("Heap: " + String(ESP.getFreeHeap()) + " / " + String(ESP.getHeapSize()) + " bytes free"); telnet.println("PSRAM: " + String(ESP.getFreePsram()) + " / " + String(ESP.getPsramSize()) + " bytes free"); }, }, { "ls", - [](String args) { - File dir = SD.open(args.isEmpty() ? "/" : ("/" + args)); + [](std::vector args) { + String dirName = args.empty() ? "/" : ("/" + args[0]); + File dir = SD.open(dirName); int count = 0; if (!dir) { - telnet.println("Не вдалося відкрити директорію: " + args); + telnet.println("Не вдалося відкрити директорію: " + dirName); return; } while (File file = dir.openNextFile()) { @@ -79,7 +84,7 @@ Command commands[] = { }, { "find", - [](String args) { + [](std::vector args) { std::vector dirs; dirs.push_back("/"); int count = 0; @@ -95,7 +100,7 @@ Command commands[] = { String fullPath = (!dir.equals("/") ? dir : "") + "/" + file.name(); if (file.isDirectory()) { dirs.push_back(fullPath); - } else if (args.isEmpty() || String(file.name()).indexOf(args) != -1) { + } else if (args.empty() || String(file.name()).indexOf(args[0]) != -1) { count++; telnet.print("FILE "); telnet.printf("%8s ", lilka::fileutils.getHumanFriendlySize(file.size()).c_str()); @@ -108,9 +113,135 @@ Command commands[] = { telnet.println("Знайдено " + String(count) + " файлів"); }, }, + { + "nvs", + [](std::vector args) { + if (args.size() == 0) { + telnet.println("Помилка: не вказано підкоманду"); + return; + } + + String subcommand = args[0]; + subcommand.toLowerCase(); + + if (subcommand == "get") { + if (args.size() != 3) { + telnet.println("Помилка: невірна кількість параметрів"); + return; + } + String ns = args[1]; + String key = args[2]; + Preferences prefs; + prefs.begin(ns.c_str(), true); + if (!prefs.isKey(key.c_str())) { + telnet.println("Помилка: ключ не знайдено"); + } else { + PreferenceType type = prefs.getType(key.c_str()); + String typeStr; + String valueStr; + // Ah sh1t, here we go again + switch (type) { + case PreferenceType::PT_I8: { + typeStr = "int8"; + valueStr = String(prefs.getChar(key.c_str())); + break; + } + case PreferenceType::PT_U8: { + typeStr = "uint8"; + valueStr = String(prefs.getUChar(key.c_str())); + break; + } + case PreferenceType::PT_I16: { + typeStr = "int16"; + valueStr = String(prefs.getShort(key.c_str())); + break; + } + case PreferenceType::PT_U16: { + typeStr = "uint16"; + valueStr = String(prefs.getUShort(key.c_str())); + break; + } + case PreferenceType::PT_I32: { + typeStr = "int32"; + valueStr = String(prefs.getInt(key.c_str())); + break; + } + case PreferenceType::PT_U32: { + typeStr = "uint32"; + valueStr = String(prefs.getUInt(key.c_str())); + break; + } + case PreferenceType::PT_I64: { + typeStr = "int64"; + valueStr = String(prefs.getLong(key.c_str())); + break; + } + case PreferenceType::PT_U64: { + typeStr = "uint64"; + valueStr = String(prefs.getULong(key.c_str())); + break; + } + case PreferenceType::PT_STR: { + typeStr = "string"; + valueStr = prefs.getString(key.c_str()); + break; + } + case PreferenceType::PT_BLOB: { + typeStr = "blob"; + size_t size = prefs.getBytesLength(key.c_str()); + uint8_t* value = new uint8_t[size]; + std::unique_ptr valuePtr(value); + prefs.getBytes(key.c_str(), value, size); + for (size_t i = 0; i < size; i++) { + if (i > 0) { + valueStr += " "; + } + valueStr += String(value[i], HEX); + } + valueStr = "???"; + break; + } + case PreferenceType::PT_INVALID: { + typeStr = "invalid"; + valueStr = "???"; + break; + } + } + telnet.printf("Тип: %s, значення: %s", typeStr.c_str(), valueStr.c_str()); + telnet.println(); + } + prefs.end(); + } else if (subcommand == "rm") { + if (args.size() != 2 && args.size() != 3) { + telnet.println("Помилка: невірна кількість параметрів"); + return; + } + String ns = args[1]; + Preferences prefs; + prefs.begin(ns.c_str(), false); + if (args.size() == 2) { + if (prefs.clear()) { + telnet.println("Всі ключі в namespace " + ns + " видалено"); + } else { + telnet.println("Помилка: не вдалося видалити namespace"); + } + } else { + String key = args[2]; + if (prefs.remove(key.c_str())) { + telnet.println("Ключ " + key + " видалено"); + } else { + telnet.println("Помилка: не вдалося видалити ключ"); + } + } + prefs.end(); + } else { + telnet.println("Помилка: невідома підкоманда"); + } + }, + }, { "exit", - [](String args) { telnet.disconnectClient(); }, + [](std::vector args) { telnet.disconnectClient(); }, }, }; @@ -127,6 +258,7 @@ void TelnetService::run() { telnet.println(ansi.setBG(ANSI_BLUE) + " " + ansi.reset() + " Keira OS @ Lilka v" STR(LILKA_VERSION)); telnet.println(ansi.setBG(ANSI_YELLOW) + " " + ansi.reset() + " Слава Україні! "); telnet.println(); + telnet.println("Введіть 'help' для отримання списку команд"); telnet.print("> "); }); telnet.onReconnect([](String ip) { lilka::serial_log("TelnetService: %s reconnected", ip.c_str()); }); @@ -142,8 +274,29 @@ void TelnetService::run() { String command = input.substring(0, spaceIndex); command.toLowerCase(); command.trim(); - String args = input.substring(spaceIndex + 1); - args.trim(); + String argsString = input.substring(spaceIndex + 1); + argsString.trim(); + + std::vector args; + // Split args by space + while (argsString.length()) { + spaceIndex = argsString.indexOf(' '); + if (spaceIndex == -1) { + String param = argsString; + param.trim(); + if (!param.isEmpty()) { + args.push_back(param); + } + break; + } + String param = argsString.substring(0, spaceIndex); + param.trim(); + if (!param.isEmpty()) { + args.push_back(param); + } + argsString = argsString.substring(spaceIndex + 1); + } + Command* cmd = &commands[0]; for (int i = 0; i < sizeof(commands) / sizeof(Command); i++) { if (command.equals(commands[i].command)) { @@ -174,6 +327,7 @@ void TelnetService::run() { } if (isOnline) { telnet.loop(); + taskYIELD(); } else { vTaskDelay(500 / portTICK_PERIOD_MS); } diff --git a/sdk/lib/lilka/src/lilka.cpp b/sdk/lib/lilka/src/lilka.cpp index a08f0f10..aa52a7c3 100644 --- a/sdk/lib/lilka/src/lilka.cpp +++ b/sdk/lib/lilka/src/lilka.cpp @@ -18,7 +18,6 @@ void begin() { battery.begin(); // TODO: I2S - esp_wifi_deinit(); // TODO: Delete Task Watchdog Timer - we'll be running long tasks // TODO: Maybe keep it? TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0); diff --git a/sdk/lib/lilka/src/lilka/serial.cpp b/sdk/lib/lilka/src/lilka/serial.cpp index cc8a75b5..9ccb7092 100644 --- a/sdk/lib/lilka/src/lilka/serial.cpp +++ b/sdk/lib/lilka/src/lilka/serial.cpp @@ -2,6 +2,9 @@ #include "serial.h" +#define ANSI_COLOR(x) "\033[1;" #x "m" +#define ANSI_RESET "\033[0m" + namespace lilka { void serial_begin() { @@ -16,7 +19,7 @@ void serial_log(const char* format, ...) { va_start(args, format); vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); - printf("[lilka] INFO: %s\n", buffer); + printf("[lilka] " ANSI_COLOR(32) "INFO:" ANSI_RESET " %s\n", buffer); } void serial_err(const char* format, ...) { // TODO: printf cannot be called from an ISR @@ -25,7 +28,7 @@ void serial_err(const char* format, ...) { va_start(args, format); vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); - printf("[lilka] ERROR: %s\n", buffer); + printf("[lilka] " ANSI_COLOR(31) "ERROR:" ANSI_RESET " %s\n", buffer); } } // namespace lilka From 9bcf9b841b761a2b4dd68a9cfb7e018b23fa644d Mon Sep 17 00:00:00 2001 From: Andrew Dunai Date: Sun, 21 Apr 2024 00:03:34 +0300 Subject: [PATCH 2/2] keira: use keira namespace for weather & ftp config --- firmware/keira/src/apps/ftp/ftp_server.cpp | 4 ++-- firmware/keira/src/apps/weather/weather.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware/keira/src/apps/ftp/ftp_server.cpp b/firmware/keira/src/apps/ftp/ftp_server.cpp index 0fd8cf42..bee3bb35 100644 --- a/firmware/keira/src/apps/ftp/ftp_server.cpp +++ b/firmware/keira/src/apps/ftp/ftp_server.cpp @@ -70,7 +70,7 @@ String FTPServerApp::createPassword() { password[pwdLen] = '\0'; Preferences prefs; - prefs.begin("ftp", false); + prefs.begin("keira", false); prefs.putString("password", password); prefs.end(); @@ -79,7 +79,7 @@ String FTPServerApp::createPassword() { String FTPServerApp::getPassword() { Preferences prefs; - prefs.begin("ftp", true); + prefs.begin("keira", true); String password; if (!prefs.isKey("password")) { password = ""; diff --git a/firmware/keira/src/apps/weather/weather.cpp b/firmware/keira/src/apps/weather/weather.cpp index 22af3849..b36b0eff 100644 --- a/firmware/keira/src/apps/weather/weather.cpp +++ b/firmware/keira/src/apps/weather/weather.cpp @@ -310,7 +310,7 @@ bool WeatherApp::runSettings() { } if (saveSettings) { Preferences prefs; - prefs.begin("weather", false); + prefs.begin("keira", false); prefs.putFloat("lat", settings.lat); prefs.putFloat("lon", settings.lon); prefs.end(); @@ -322,7 +322,7 @@ bool WeatherApp::runSettings() { settings_t WeatherApp::getSettings() { Preferences prefs; settings_t settings = {false, 0, 0}; - prefs.begin("weather", false); + prefs.begin("keira", false); if (prefs.isKey("lat") && prefs.isKey("lon")) { settings.lat = prefs.getFloat("lat", settings.lat); settings.lon = prefs.getFloat("lon", settings.lon);