From 6ce76071987d1894451349bb4d9eca4ef21dba82 Mon Sep 17 00:00:00 2001 From: 0r0chic0 Date: Sat, 7 Mar 2026 13:46:34 -0800 Subject: [PATCH 1/3] Server-client test for sensor data transfer --- .../index/main.cpp.621DB008C2D6724D.idx | Bin 0 -> 24602 bytes .../index/test.cpp.4729B44F633614F3.idx | Bin 0 -> 29104 bytes firmware/panorama/.clangd | 9 ++ firmware/panorama/src/main.cpp | 106 --------------- firmware/panorama/src/test.cpp | 127 ++++++++++++++++++ firmware/panorama/src/test_server.py | 54 ++++++++ 6 files changed, 190 insertions(+), 106 deletions(-) create mode 100644 firmware/panorama/.cache/clangd/index/main.cpp.621DB008C2D6724D.idx create mode 100644 firmware/panorama/.cache/clangd/index/test.cpp.4729B44F633614F3.idx create mode 100644 firmware/panorama/.clangd delete mode 100644 firmware/panorama/src/main.cpp create mode 100644 firmware/panorama/src/test.cpp create mode 100644 firmware/panorama/src/test_server.py diff --git a/firmware/panorama/.cache/clangd/index/main.cpp.621DB008C2D6724D.idx b/firmware/panorama/.cache/clangd/index/main.cpp.621DB008C2D6724D.idx new file mode 100644 index 0000000000000000000000000000000000000000..1d973c8e0cf5c728d824dc4e727d183de627503d GIT binary patch literal 24602 zcmd5^XLK9K5ym7=cben$rZ7Fqks>MEvJxbrKv05B5;9n1Cl{X&cYDAJcekhAJ%Dt2 z@4ffldymtc-h1y(uTC#ce&oH(9tuH9RDMJ^AfJQ-7Ki(0XJ=<;c4wwmnVUP~l0u>6 zSJon?-O~z%!kPGAX~{3R7~hyb%ff%Y^Oer4h%5K;-%i6mZgrA0NZ0HHucg}2ib*Sy|Ol`Tc zT&XTPJ`3D5)I;B`&eSS1i_66^rjqF?^Q2O>R>lV$SUKp-msiX4#Yt@J(}Y*&@chj3 zT(!1<&2vua@JexN@1E}8JT@JgSuH!I>f&7GpmPX6SMa9Qa;-Sl-aEc`{2a^YQm-4F zg#+c;<@u%ly>pfMa&auGGhc^l0;zF45eK|6-ZVG&F)P*L*?T4@&&8&d#me$UPGzw) zzcRZC@knL)kaM`YRJjoEat@c4eWuWREAIs{Mb|uD_vCs)qByq1$$$TlePrHowbWG2}&0phqbs?$D zF)tEc%PuXL*L*yJKKDG~CN}yY*_D8a29C(QM88srFi(Zlb@<67um_j_D6t?_nNf`1 zE)^O9j!G;)r0CElAlqk}dAJwze{Vc&_3C>pGx9goF`GyEF2h^kLU)}>y52>uZW5vy z8{WB82s+3(ro%E4MToYZFx;8U84$M>^GC?$93oknpOLe4Oix)J?ng=&z1bLTJKPtF zDf~*D=o`>zB~dm*(3wMyJTS-6d3q_s-QB>%QyAXHGdzbzKk!)b zMpIL*iaep%j5h)tdQ>X5i=>$h@SHbikqNohAq%L6D->uCI-ZtPMq$h}4~W!w(p>MV z7D7ClNu5MQU6L-3BezAe!EPqCI$8JwU+@|6G zb`>B#t<_ivwS-uf?&j42sn{m+l~W#)C9ELFmhv?wTKM4?HI8^hr^?E za$0HhyNO(@?PxLwdbaD2c#`y~Aad4E%8Zd}?TisoO62~Yln+S7P8be#j3fm6B+E!P zCJGNBMlBhVukDDb+M{Fi(qRbM34^i|CamFwp#xWx5gQyhUwXvs3<(+e)TPVmIEfbP z6A@8u9Bj9^ba?EGj>5oI%=a>O*+^&bR%)E4D!r zb5yl>p7Cv6n0$lWyBO^t&%nT-?lb}lV=$16;Wl4AHEGI&w`7S|22*N5u|92uG6y&q zvgdPc0w8moXD0{F*lt)rK|wazyF1AQW=bQ$MDaQ<^_Y{}yX3#7G%)}ZN0ub$(_qo` zs9#pVAyU^(hM6__ca8gjL*(FXh06W>mNUlo?U|Z9YcByKkE)2mYCNiNDtqTGRTp#A zRtCgruoy$sF$vU86ReHG+$e9v#FcpdJt{Z?89{4O37@Bt$fx;RAJvd8Ya-1C*VD@69g+NA32eWmKY8}rycRZr)N-xjs zuwL1s%nl9Z zY_m@6IJ-=lrJ}C@@>FqMII7Ir`iyoLnhsZBAY{QHD4DOzAu zsg+eg>b=czJ<9OR4n@Cjz4#vd94tb`UTqeAnXe2H$NxlIv3aZGxc5Zxce z962eo7;CzWNvS=@6tJP_oARTG!YacE&)i++0S19g7_9ttBUVWyTU!t-AjxdeURIQ` ztBMGih;lP>8Q7NS{E*})V$#A-8`5uhmkqAM5I?R<$h)LTaW++fN z_a@HVL8GJ1O{oMT%*4$SRxvxl#X@A}sKNw1^giaVw>q2g$pMM{#EzQ6QpzYzevT8p zciuv^nQ_XGciLD`7=>`7Nb!a=Wcp-bVYXmZ(Ih<*3yVo9WYK3B#O`4*vv70S2pT*y*PTNwye_8fjn@0K8qJ@fMue6{&kS)FAIkMvB8MfYw@&W)`s zy~os0H0ZsD;%+Y%LBaI|TRW0#W|<1yaRYycS=s?s0d|qD3>TXMw&t;<*N31_(C`iT zEpCz+5~CfBNCrfAFj3}dLhmStQV z1Q$phwn*+Fg9L`DV0Brm&UL_9=;PfDEGJR0qEN&Q%riBWYVd3wJ1jHAuFE-+I_wLEb!Z@A_;(;I!eVc`>A%sad znV!ND)40DFVM^qQyYHMZrk zv!WB5TE)sTSUk4&Fa8F;Ut- zk(CplFy+K22EIjTU6%M4ydVS$;jLo1*d()1JH6&^1@QOP!8a_%~b; zUe5nxUv_1|i~LYQp>P#k1^hbO?(;HmILcoIAro(@lePW!=^J{%tMpoc!}(eMa(Bs>-# a2akeO$zzX!we{!0&2RcW_#42#pzvP@v&s1Y literal 0 HcmV?d00001 diff --git a/firmware/panorama/.cache/clangd/index/test.cpp.4729B44F633614F3.idx b/firmware/panorama/.cache/clangd/index/test.cpp.4729B44F633614F3.idx new file mode 100644 index 0000000000000000000000000000000000000000..d40dc5a727617fa217c41fc84eda2764298e846d GIT binary patch literal 29104 zcmd5^d3+RAwys-r0$DnN5CSn^0x}3x|-pzH_T{I~5f2$FpwWr@`36Lq;$(mi2K zlq%Y%PGvkbmCZG6zJ|t%f@x(1g?%(v*<3xBt8Hm2vL{wG0(-@TS|g%Mh9L*@EQY)q zsWWeZCPo!$f-MK@H?F33ku9yMvbojQ>T7J^L@CHe71Q?HqRM7pWkYMl1j&dL72=$# z8=Gq(gQ~VUTz&1L+WLwDNF*}BU#_XXvbCyHXan((26@|sqV*lzPX>R4BTCTdW zq0Tpln+voLm=@JGS4`+E&M(d{^@y@=c4M3cRkby(^(}1(VgnO0Cx4oPhdzSbq2uc5lWt)?I1VqfcAuBoxbcQ$Zwa~j&Xmd3W` z>RL=$$y{@7Wle1}x47BY3V-SwtLL{c9$)ozf14rchTpfKvBjSsQFt?`>0w#(M|h!w zZzQ0K!^m!@}kcwLOjss?h#Dy68n2*bx)cFUlIX%81B*SrwG1C^-Ppo3o#fL^xCD1&Il#P1H8XCRIlFa>`^*MhPl;a1`gDA8bC zib0XUd6ViZy=G7u1kKy)N~j;IqNxpTJGLf zCMdF`4qAm1AqLg@dxe0ZEzCmbm_kT1Ox$rwqOQpz-EO?s$MlYHo%c9GK+|ar$AqvZ zboi?m;HNV)0-`-Z<0F2UgIIqlC=tU^8eYP@($Ewq9a3zm&B>~oA{@)7^$Ij||Rfg@)RxrODcD5Tn22SC-C7oXB!n_%x zh=#}T+!{@z2dS#`u&_l@MC*=7urlc|AS^37k6^VqK=N4g42P|wsL&(8dZf<7Y*vM_ z9jptfH0G7i(EFh2bv_uCj+f?mQQ!ogQlCI?a*St5qRh9eFg%w9N~Xd|W{6U?Nu6}& zdRUL`V!$w?f!H9=S;rE`qB0hWqpdb2)!-d2S_r0xbcz)+P1sYSwH$y=#6qMt?Iwr@ zrG<~yh?u;WIp%W8WdO3OfWElj2F!8mjb z!-hnqWQGuu@L4TN#e*T44F1Yc_)dvaWVM49Znt4dwCd1^fmtJlNHn0xFlpDq5#Hf7 zRnCs<{7}%8VTp%Q^>s-BC{TxF(w!)E$y83*fZxPt^u%a5LPwv+qQ!Q&Xw0i)hY~ps zitG{L5EJYHgYz-Sxhg6?_V_ZWopkhnJPAaB#)$yX{c#1*OgLgjX;^+dA%ej{)+K`^a^uwSjeC^U3zyqPT<9e5Mf#i2gmDNx@1+L<5+_Ky$%WggOgUG>BBWokJ>ev^llx&75~btQLAVxcz|B#kLw53fTMWk8L+&w* z&Y+VA1A_ssJxF6P7}O;RmiYovd`&t2O6}qm#?(!XM*nmy6yCsrBYUE3;scNlwzCrh zTWZHDpdo?P*kdcn6ox775x6KGfUX|ywl}4DxsBxtMO<9=N(%9gjx56M?yQIPq zb+`oTk47Loio}PVMvU+}?0*jy90ePJ(WIe?ax{!@==HwANXbSy-{AX@u_L2!Mo-b& zX{feKj=M{^w;FM>Ggpqq@*AodajTEK<4B6kcUo!4o{zi}PAkr7m!w{`g>!0wSDk zHY0x3`csIz3gQ5qba62_S~t7ZPJ@FrKnN(~ygs!E+>SA5%o-wao*D;3a=uJ(CdJg? zRH?^`fYkfyV1H!c;S~x}w%+qeR3WY9LD}r43JQ_XR0F$ECmUbH48d@l#sP{|x&RlV zNi904N!>bQP4C8}ZVDU@U_+y9h8Pah7?r^ZPsgXsaSRGJfw7$TjSwY~THgX#K`J*} zOu-7u7;{Awln8h-UaI9Ia7l;)UQ~%s<{WfNQ5Cum8l|ytVrb58jKG!%h4OSsyv`|b zXAI#@h!PxP z7vMCo=n*AB;bA2=w#nAV5>+88l<0wdA&()M9t#8_d)jm9WeG-A0e}88KlT60inHv2 zEj@h3BE}7ub5)OZy$8oo#OS@5!fG!BL4p1RQoD57lsq^Hx7rTh!7c3|L;>bOE(;fu zg3@Y1iu9HtNCdJoK+Y@ShKR@Hp%C~BNYG6ag3sbNSilB=F4+`9APtGS0>9~!rbC!q zPXKO;cHqz847JeV!BZRn*FZW*(PaS?q`@&2h%W01$YxNM6cM=FA)Ex`2`ie~4l!L6 z9I6I3*P--UR2L*1G`pgIcM$e2wWZ{LjYL#xaM45L~15 z#O&S^Yi0pi_1hCXk`nYl6!m;*wRB9jHCmU7gB2V8wyJ7RBTUenwSZ7N)7Oxs_?DZD?t1=6p5et)X&3Ez{Bh z39Zc#C>?VP(>s}1cnSu`9-i*?daI{)0&q=B=?>uBDvR!NRDHv7ZrLd zV*$(aL%&$=U=^_l{XAd*ooAoB6@w$V$xuIq;Kk+`8qqZD|LxPETbnwB(qTFbnd)Pr`BEn>(Id zxMyL3n>lLbF)Dr3m>w2C3C9E5_C@Q@f4s!a{Ah8wQe7Y^eiDuy_uO>b;tO6Vbu-^v z9Es_PBYRl#gU(opS+&E z5+z)Tl9?ogn9JD>JdK)=Gs%IZu{ao&%bKd~+{{{wBPlrv z^oO5>_{VI+#2C4yA8ru%Z4qBvtcr0*%D929bhJIxuyA!+PIg@sSHDGToAg#~kbH|OfhgB!3UXsbaGhFj$ zZrhQBfGw3tO(ykv&*X|nuh@)I5a`<+ZH^=Sr0ub%^lx#%7S0Y&A#9u1J%sK&aC$lm z>4j`z5-~s^i`b$#`L{X3m<8SyIYS_QdCKzPVPufHLdJ*U~@x@|I}>un}j5HDGsPpaEkd448UdN5Rp{ zhA*u@R4_np#PQ);u)90Rtu(DPu67{Hr<8k0i6%@ua%S@*pO@g0F7qraAfR1)r@i?@ z-KJ8U-kIH*OVWw+#4_7mCS;qMk(x!UePf1x(>sR_l_D3^+KAtjBNJRfR>Jm@{z#G2`d|Ud(=1 zv%h8-eC}nv!-ytP!L+WW$Jt975jh|MfcW590$^hi=B5Ok6gQOV}lG zlnY-Qwe^Erp9&0Mb%$nLaQ-f~F*pF*nz4CPhu%C!#%xU~O<OAXlWUh?~chP1_gU z@bZtc3(|AgoHU|yAamJVJ2{>mZzqe{;xwX{f>*b%-~F$DuEZX=D6=S==x6$s)mn{v*>GK^)%jG6yashc4q6L zMQaBr0ztzTf(IPr^OUc4?tJwxcU_D?D%?Ahs-O4B6(8?7{NSb7)a$%;C8TybQVtKF zcF{AJVz}~(@+zWXAX}!kOe1Xfc04ojrH`K2jHShmoxr`mCD-+tnS43URv06kPQ?1^ zraN~1ap4PF2Pg&cCAbya$3cKHgPmb-XfxTFc5+GflDNOx`_jT6cNT8B6W3-5TarkM z0Awj!YA4G^l#L?4ev+=aW*@}%-~k`82jO8p2@aIA-vmN@xgt+Y9FZS{Rv z4<)H3@!{kjDoaK)4`)1p^txStDm<+t>t`VD~wZ#7MmP z1G7F>pFlDJ(k?lMu#I7Wv^#Sk?On#+8xGa`&r8{jtXB6M9<@k%6P=tNMmedG+;=A`G0CJh$I-1J;KIX->7oh(c)wD-Bi>BXao zNWZ)1xifl3Uhx`+t{YvKM{4Bg2C+K-=`-HIin2QmGHAyQ!9G+68RlfpnLs`Vvc9ao z&PJ|0W#wd2gKwSI+u0Al+gSC%0C2PG+Lx}r z&h-V(wsdHzn|!{ey>##MJF5SU)5{CXr<3#pldmd$>!HI(aC*tml6dXwEyfsq|MG8f zXP29uJC3*}ASZez77+66F=yWU_~qNbbAjfUCN51UN(0h9Z2{6g^#Ia7n|M0(){IM@ zO8g#IANE(oe{ma9i!)oE{T|EaP4dPq@W`)kUs|5~`cZ7>%d(fn+ZvF8(SbZ71CXIf zq4@klmlPh04eJudWc;!LCUWH8`o8xe;ETnqNqeduUdL`=*Rw)cR61e4djuUpU!x&k zqOZ_lbO8MueSkhjZ=)~Jhvv81+oHv1-;C};ccXjIz34u4KYAWLi=IO_ zqV4D=6l|B~u2eee{(;^=C1(Z1veK84HlLgKSHz#QG&}qddJsK;PWdl1?lJTfTF_7* zS+x(nhW?IjMa4N|$9qrB&C8#3`s68R{OZi9p3^2ynD#gH5-KPv+>IVZRn>DEUqDR@ zn_HIo_n;@ypV5DyU1-HKNLGK3u0*e(tI$?-HM$o45#54rL${+l(N6R*dK^80{(@da zucH4%zNgWW9q119ClvY}5^g|y(d%e=`RvLW6|-he|8;FmbQ8K5U5>V(OVFk0GIRyn wj8=D@ckTt~x4$`mE$Tt*(0X(c+K4uwPSemYMBQtyL1VK18}#}w#D0VR56^a;MF0Q* literal 0 HcmV?d00001 diff --git a/firmware/panorama/.clangd b/firmware/panorama/.clangd new file mode 100644 index 00000000..e3c57092 --- /dev/null +++ b/firmware/panorama/.clangd @@ -0,0 +1,9 @@ +# Strip ESP32/toolchain flags that clang (x86 host) doesn't support when used for IDE diagnostics. +# Use headers from the compiler in compile_commands.json so stdlib.h and other toolchain headers are found. +CompileFlags: + BuiltinHeaders: QueryDriver + Remove: + - -mlongcalls + - -mlong-calls + - -fstrict-volatile-bitfields + - -fno-tree-switch-conversion diff --git a/firmware/panorama/src/main.cpp b/firmware/panorama/src/main.cpp deleted file mode 100644 index c227be3c..00000000 --- a/firmware/panorama/src/main.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include -#include - -// ===== Wi-Fi Access Point (ESP32 hosts itself) ===== -const char* AP_SSID = "ESP32-JSON"; -const char* AP_PASS = "esp32json"; // must be 8+ chars - -// ===== TCP server ===== -const uint16_t SERVER_PORT = 9000; -WiFiServer server(SERVER_PORT); - -// ===== State ===== -WiFiClient client; // single client for simplicity -unsigned long lastSendMs = 0; // pacing JSON sends -const uint32_t SEND_PERIOD_MS = 1000; - -// make a tiny JSON line without ArduinoJson -String makeJson() { - // dummy data — tweak as you like - static int seq = 0; - float temp = 20.0 + sin(millis() / 1000.0) * 2.5; - int humidity = 40 + (millis()/1000) % 20; - - // ISO-ish time in ms since boot for now - String s = "{"; - s += "\"seq\":" + String(seq++) + ","; - s += "\"ts_ms\":" + String(millis()) + ","; - s += "\"temp_c\":" + String(temp, 2) + ","; - s += "\"humidity\":" + String(humidity) + ","; - s += "\"status\":\"ok\""; - s += "}\n"; // newline-delimited JSON (NDJSON) - return s; -} - -void setup() { - Serial.begin(115200); - delay(200); - - // Start AP (self-hosted) - Serial.println("[WiFi] Starting AP…"); - bool ok = WiFi.softAP(AP_SSID, AP_PASS); - if (!ok) { - Serial.println("[WiFi] AP start failed!"); - } - IPAddress ip = WiFi.softAPIP(); // usually 192.168.4.1 - Serial.print("[WiFi] AP SSID: "); Serial.println(AP_SSID); - Serial.print("[WiFi] AP PASS: "); Serial.println(AP_PASS); - Serial.print("[WiFi] AP IP: "); Serial.println(ip); - - // Start TCP server - server.begin(); - server.setNoDelay(true); - Serial.print("[TCP] Listening on port "); Serial.println(SERVER_PORT); -} - -void handleNewClient() { - WiFiClient incoming = server.available(); - if (!incoming) return; - - // If we already have a client, drop the older one - if (client && client.connected()) { - client.stop(); - } - - client = incoming; - client.setTimeout(50); - Serial.print("[TCP] Client connected from "); - Serial.println(client.remoteIP()); - - // optional greeting / one-shot JSON blob - client.print("{\"hello\":\"welcome\",\"port\":"); - client.print(SERVER_PORT); - client.print(",\"hint\":\"I will stream one JSON per second. Each line is a JSON object.\"}\n"); -} - -void streamJsonIfTime() { - if (!client || !client.connected()) return; - - // read & ignore any input (you could add commands here) - while (client.available()) { - (void)client.read(); // drain input - } - - unsigned long now = millis(); - if (now - lastSendMs >= SEND_PERIOD_MS) { - lastSendMs = now; - String line = makeJson(); - client.print(line); // send one JSON line - Serial.print("[TX] "); // mirror to serial - Serial.print(line); - } -} - -void loop() { - // accept new client connections - handleNewClient(); - - // send JSON periodically if a client is connected - streamJsonIfTime(); - - // clean up if disconnected - if (client && !client.connected()) { - Serial.println("[TCP] Client disconnected"); - client.stop(); - } -} diff --git a/firmware/panorama/src/test.cpp b/firmware/panorama/src/test.cpp new file mode 100644 index 00000000..68dd8184 --- /dev/null +++ b/firmware/panorama/src/test.cpp @@ -0,0 +1,127 @@ +#include +#include + +// Wi-Fi Access Point credentials +const char* SSID = "ESP32-Interface"; +const char* PASS = "12345678"; +const uint16_t PORT = 9000; + +// HC-SR04 pins (adjust to match your wiring) +const int TRIG_PIN = 5; +const int ECHO_PIN = 18; + +WiFiServer server(PORT); +WiFiClient client; + +bool sendEnabled = false; +unsigned long sampleInterval = 1000; // ms +unsigned long lastSend = 0; +unsigned long startTime = 0; + +uint32_t seq = 0; +const uint16_t SENSOR_ID = 1; +const char* SENSOR_NAME = "ultrasonic_distance_cm"; + +// Return distance in cm, or -1.0 if no echo / timeout +float readUltrasonicCm() { + // ensure trigger is low + digitalWrite(TRIG_PIN, LOW); + delayMicroseconds(2); + + // 10 µs HIGH pulse to trigger measurement + digitalWrite(TRIG_PIN, HIGH); + delayMicroseconds(10); + digitalWrite(TRIG_PIN, LOW); + + // measure echo time (timeout 30 ms ≈ 5 m) + unsigned long duration = pulseIn(ECHO_PIN, HIGH, 30000); + if (duration == 0) { + return -1.0f; // no echo + } + + // speed of sound ≈ 0.0343 cm/µs, divide by 2 (out and back) + float distanceCm = (duration * 0.0343f) / 2.0f; + return distanceCm; +} + +void setup() { + Serial.begin(115200); + + // ultrasonic sensor pins + pinMode(TRIG_PIN, OUTPUT); + pinMode(ECHO_PIN, INPUT); + digitalWrite(TRIG_PIN, LOW); + + WiFi.mode(WIFI_AP); + WiFi.softAP(SSID, PASS); + IPAddress ip = WiFi.softAPIP(); + Serial.printf("AP started: %s (%s)\n", SSID, ip.toString().c_str()); + + server.begin(); + server.setNoDelay(true); +} + +void handleCommand(String cmd) { + cmd.trim(); + cmd.toUpperCase(); + + if (cmd.startsWith("START")) { + int spaceIdx = cmd.indexOf(' '); + if (spaceIdx > 0) { + float freq = cmd.substring(spaceIdx + 1).toFloat(); + if (freq > 0) sampleInterval = 1000.0 / freq; + } + startTime = millis(); + seq = 0; // reset sequence on start + sendEnabled = true; + Serial.printf("Signal ON, freq=%.1f Hz\n", 1000.0 / sampleInterval); + + } else if (cmd.startsWith("STOP")) { + sendEnabled = false; + Serial.println("Signal OFF"); + + } else { + Serial.printf("Unknown cmd: %s\n", cmd.c_str()); + } +} + +void loop() { + // accept new client + WiFiClient newClient = server.available(); + if (newClient) { + if (client && client.connected()) client.stop(); + client = newClient; + client.print(F("{\"type\":\"status\",\"msg\":\"connected\"}\n")); + Serial.println("Backend connected"); + } + + // read commands + if (client && client.connected() && client.available()) { + String cmd = client.readStringUntil('\n'); + handleCommand(cmd); + } + + // send JSON data packets + if (sendEnabled && client && client.connected()) { + unsigned long now = millis(); + if (now - lastSend >= sampleInterval) { + lastSend = now; + + float distanceCm = readUltrasonicCm(); + unsigned long timestamp = now - startTime; + + String json = + "{" + "\"sensor\":\"" + String(SENSOR_NAME) + "\"," + "\"sensor_id\":" + String(SENSOR_ID) + "," + "\"seq\":" + String(seq++) + "," + "\"timestamp_ms\":" + String(timestamp) + "," + "\"value\":" + String(distanceCm, 2) + + "}\n"; + + client.print(json); + Serial.print("Sent: "); + Serial.print(json); + } + } +} \ No newline at end of file diff --git a/firmware/panorama/src/test_server.py b/firmware/panorama/src/test_server.py new file mode 100644 index 00000000..20267c2b --- /dev/null +++ b/firmware/panorama/src/test_server.py @@ -0,0 +1,54 @@ +import socket +import time +import json + +ESP_IP = "192.168.4.1" # ESP32 softAP IP (printed in Serial Monitor) +PORT = 9000 +RUN_SECONDS = 30 # how long to receive data + + +def main(): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((ESP_IP, PORT)) + print(f"Connected to {ESP_IP}:{PORT}") + + # read optional status line from ESP32 + try: + status = s.recv(1024).decode().strip() + if status: + print("Status from ESP32:", status) + except OSError: + pass + + # start JSON streaming at 5 Hz + s.sendall(b"START 5\n") + start = time.time() + + try: + while time.time() - start < RUN_SECONDS: + data = s.recv(1024) + if not data: + print("Connection closed by ESP32") + break + + # ESP32 sends one JSON object per line; ignore debug text (e.g. "Sensor detected at GPIO 5") + for line in data.decode().splitlines(): + line = line.strip() + if not line or not line.startswith("{"): + continue # skip non-JSON lines + try: + msg = json.loads(line) + print("JSON:", msg) + except json.JSONDecodeError: + print("Bad JSON:", line) + finally: + # tell ESP32 to stop streaming + try: + s.sendall(b"STOP\n") + except OSError: + pass + print("Sent STOP") + + +if __name__ == "__main__": + main() \ No newline at end of file From 9fab878c7d1812ffa20bc9bf1693dac867836403 Mon Sep 17 00:00:00 2001 From: 0r0chic0 Date: Sat, 14 Mar 2026 15:31:28 -0700 Subject: [PATCH 2/3] Message control for server --- firmware/panorama/src/test_server.py | 74 ++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/firmware/panorama/src/test_server.py b/firmware/panorama/src/test_server.py index 20267c2b..e77bfa14 100644 --- a/firmware/panorama/src/test_server.py +++ b/firmware/panorama/src/test_server.py @@ -1,54 +1,98 @@ import socket -import time +import sys import json +import threading ESP_IP = "192.168.4.1" # ESP32 softAP IP (printed in Serial Monitor) PORT = 9000 -RUN_SECONDS = 30 # how long to receive data + +# Commands the ESP32 understands (sent as one line, newline-terminated): +# START [freq] - start streaming at freq Hz (default 5); e.g. "START 10" +# STOP - stop streaming +# Type QUIT or EXIT to close the connection and exit. + + +def read_commands(sock, stop_event): + """Read lines from stdin and send them as commands to the ESP32.""" + try: + while not stop_event.is_set(): + line = sys.stdin.readline() + if not line: + break + line = line.strip() + if not line: + continue + if line.upper() in ("QUIT", "EXIT", "Q"): + stop_event.set() + break + # Send command to ESP32 (one line, newline-terminated) + try: + sock.sendall((line + "\n").encode()) + print(f"[sent] {line}") + except OSError as e: + print(f"[error] send failed: {e}", file=sys.stderr) + stop_event.set() + break + except (KeyboardInterrupt, EOFError): + stop_event.set() def main(): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((ESP_IP, PORT)) print(f"Connected to {ESP_IP}:{PORT}") + print("Send commands: START [freq], STOP. Type QUIT to exit.\n") - # read optional status line from ESP32 + # Read optional status line from ESP32 + s.settimeout(0.5) try: status = s.recv(1024).decode().strip() if status: print("Status from ESP32:", status) + except socket.timeout: + pass except OSError: pass + s.settimeout(None) - # start JSON streaming at 5 Hz - s.sendall(b"START 5\n") - start = time.time() + stop_event = threading.Event() + cmd_thread = threading.Thread(target=read_commands, args=(s, stop_event), daemon=True) + cmd_thread.start() try: - while time.time() - start < RUN_SECONDS: - data = s.recv(1024) + buffer = "" + while not stop_event.is_set(): + try: + data = s.recv(1024) + except (OSError, socket.timeout): + continue if not data: print("Connection closed by ESP32") break - # ESP32 sends one JSON object per line; ignore debug text (e.g. "Sensor detected at GPIO 5") - for line in data.decode().splitlines(): + buffer += data.decode(errors="replace") + while "\n" in buffer: + line, buffer = buffer.split("\n", 1) line = line.strip() - if not line or not line.startswith("{"): - continue # skip non-JSON lines + if not line: + continue + if not line.startswith("{"): + # Status or debug line + print("ESP32:", line) + continue try: msg = json.loads(line) print("JSON:", msg) except json.JSONDecodeError: print("Bad JSON:", line) finally: - # tell ESP32 to stop streaming + stop_event.set() try: s.sendall(b"STOP\n") except OSError: pass - print("Sent STOP") + print("Sent STOP; exiting.") if __name__ == "__main__": - main() \ No newline at end of file + main() From 0be9cbd7d91fafd3275c5c37c327c675520fe367 Mon Sep 17 00:00:00 2001 From: Alex Zhou Date: Sat, 14 Mar 2026 15:42:48 -0700 Subject: [PATCH 3/3] Removed firmware cache files and updated gitignore --- .gitignore | 6 +++++- .../clangd/index/main.cpp.621DB008C2D6724D.idx | Bin 24602 -> 0 bytes .../clangd/index/test.cpp.4729B44F633614F3.idx | Bin 29104 -> 0 bytes 3 files changed, 5 insertions(+), 1 deletion(-) delete mode 100644 firmware/panorama/.cache/clangd/index/main.cpp.621DB008C2D6724D.idx delete mode 100644 firmware/panorama/.cache/clangd/index/test.cpp.4729B44F633614F3.idx diff --git a/.gitignore b/.gitignore index 9aa39412..903c3c3a 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,8 @@ desktop.ini node_modules/ __pycache__/ -*.pyc \ No newline at end of file +*.pyc + + +# Firmware related: +firmware/panorama/.cache/ \ No newline at end of file diff --git a/firmware/panorama/.cache/clangd/index/main.cpp.621DB008C2D6724D.idx b/firmware/panorama/.cache/clangd/index/main.cpp.621DB008C2D6724D.idx deleted file mode 100644 index 1d973c8e0cf5c728d824dc4e727d183de627503d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24602 zcmd5^XLK9K5ym7=cben$rZ7Fqks>MEvJxbrKv05B5;9n1Cl{X&cYDAJcekhAJ%Dt2 z@4ffldymtc-h1y(uTC#ce&oH(9tuH9RDMJ^AfJQ-7Ki(0XJ=<;c4wwmnVUP~l0u>6 zSJon?-O~z%!kPGAX~{3R7~hyb%ff%Y^Oer4h%5K;-%i6mZgrA0NZ0HHucg}2ib*Sy|Ol`Tc zT&XTPJ`3D5)I;B`&eSS1i_66^rjqF?^Q2O>R>lV$SUKp-msiX4#Yt@J(}Y*&@chj3 zT(!1<&2vua@JexN@1E}8JT@JgSuH!I>f&7GpmPX6SMa9Qa;-Sl-aEc`{2a^YQm-4F zg#+c;<@u%ly>pfMa&auGGhc^l0;zF45eK|6-ZVG&F)P*L*?T4@&&8&d#me$UPGzw) zzcRZC@knL)kaM`YRJjoEat@c4eWuWREAIs{Mb|uD_vCs)qByq1$$$TlePrHowbWG2}&0phqbs?$D zF)tEc%PuXL*L*yJKKDG~CN}yY*_D8a29C(QM88srFi(Zlb@<67um_j_D6t?_nNf`1 zE)^O9j!G;)r0CElAlqk}dAJwze{Vc&_3C>pGx9goF`GyEF2h^kLU)}>y52>uZW5vy z8{WB82s+3(ro%E4MToYZFx;8U84$M>^GC?$93oknpOLe4Oix)J?ng=&z1bLTJKPtF zDf~*D=o`>zB~dm*(3wMyJTS-6d3q_s-QB>%QyAXHGdzbzKk!)b zMpIL*iaep%j5h)tdQ>X5i=>$h@SHbikqNohAq%L6D->uCI-ZtPMq$h}4~W!w(p>MV z7D7ClNu5MQU6L-3BezAe!EPqCI$8JwU+@|6G zb`>B#t<_ivwS-uf?&j42sn{m+l~W#)C9ELFmhv?wTKM4?HI8^hr^?E za$0HhyNO(@?PxLwdbaD2c#`y~Aad4E%8Zd}?TisoO62~Yln+S7P8be#j3fm6B+E!P zCJGNBMlBhVukDDb+M{Fi(qRbM34^i|CamFwp#xWx5gQyhUwXvs3<(+e)TPVmIEfbP z6A@8u9Bj9^ba?EGj>5oI%=a>O*+^&bR%)E4D!r zb5yl>p7Cv6n0$lWyBO^t&%nT-?lb}lV=$16;Wl4AHEGI&w`7S|22*N5u|92uG6y&q zvgdPc0w8moXD0{F*lt)rK|wazyF1AQW=bQ$MDaQ<^_Y{}yX3#7G%)}ZN0ub$(_qo` zs9#pVAyU^(hM6__ca8gjL*(FXh06W>mNUlo?U|Z9YcByKkE)2mYCNiNDtqTGRTp#A zRtCgruoy$sF$vU86ReHG+$e9v#FcpdJt{Z?89{4O37@Bt$fx;RAJvd8Ya-1C*VD@69g+NA32eWmKY8}rycRZr)N-xjs zuwL1s%nl9Z zY_m@6IJ-=lrJ}C@@>FqMII7Ir`iyoLnhsZBAY{QHD4DOzAu zsg+eg>b=czJ<9OR4n@Cjz4#vd94tb`UTqeAnXe2H$NxlIv3aZGxc5Zxce z962eo7;CzWNvS=@6tJP_oARTG!YacE&)i++0S19g7_9ttBUVWyTU!t-AjxdeURIQ` ztBMGih;lP>8Q7NS{E*})V$#A-8`5uhmkqAM5I?R<$h)LTaW++fN z_a@HVL8GJ1O{oMT%*4$SRxvxl#X@A}sKNw1^giaVw>q2g$pMM{#EzQ6QpzYzevT8p zciuv^nQ_XGciLD`7=>`7Nb!a=Wcp-bVYXmZ(Ih<*3yVo9WYK3B#O`4*vv70S2pT*y*PTNwye_8fjn@0K8qJ@fMue6{&kS)FAIkMvB8MfYw@&W)`s zy~os0H0ZsD;%+Y%LBaI|TRW0#W|<1yaRYycS=s?s0d|qD3>TXMw&t;<*N31_(C`iT zEpCz+5~CfBNCrfAFj3}dLhmStQV z1Q$phwn*+Fg9L`DV0Brm&UL_9=;PfDEGJR0qEN&Q%riBWYVd3wJ1jHAuFE-+I_wLEb!Z@A_;(;I!eVc`>A%sad znV!ND)40DFVM^qQyYHMZrk zv!WB5TE)sTSUk4&Fa8F;Ut- zk(CplFy+K22EIjTU6%M4ydVS$;jLo1*d()1JH6&^1@QOP!8a_%~b; zUe5nxUv_1|i~LYQp>P#k1^hbO?(;HmILcoIAro(@lePW!=^J{%tMpoc!}(eMa(Bs>-# a2akeO$zzX!we{!0&2RcW_#42#pzvP@v&s1Y diff --git a/firmware/panorama/.cache/clangd/index/test.cpp.4729B44F633614F3.idx b/firmware/panorama/.cache/clangd/index/test.cpp.4729B44F633614F3.idx deleted file mode 100644 index d40dc5a727617fa217c41fc84eda2764298e846d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29104 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