From 026fba1b3eb0ed52734d4b1408e5b893353257be Mon Sep 17 00:00:00 2001 From: Luc Date: Wed, 3 Jun 2015 20:44:21 +0800 Subject: [PATCH] Allow to change web port and data ports System configuration web page allows to change web port and data port --- Page2.png | Bin 37156 -> 39436 bytes esp8266/config.cpp | 8 +- esp8266/config.h | 86 ++++++------ esp8266/esp8266.ino | 35 +++-- esp8266/webinterface.cpp | 277 +++++++++++++++++++++++++-------------- esp8266/webinterface.h | 4 +- esp8266/wifi.cpp | 8 +- esp8266/wifi.h | 4 + 8 files changed, 265 insertions(+), 157 deletions(-) diff --git a/Page2.png b/Page2.png index 62a94d861369873df9660f1f21dfc0f81ff487bc..88df9ec1b75a036035bf917fd850ef35849535ac 100644 GIT binary patch literal 39436 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%0Dq9(-< z8)b@P)N7c3Y`{QO{$^%Kl}RTe!Lstue5j2#EsTD+e9qn~zVEdfVZh9`LP2xTgHh&k zx7uZtWo#F^AKPl8+a5NettKeVH&u>&hpC%8=#=bg_bff2E}$|#iT7^(*1sS6Z}I-y zkp6vS{$B%!*E0tU-C^<;aZb2fYKk6#UGS92PB!Ke8$V^n@xK5KMq1ys#ChJ-YMAaE z6yB6<+X^&ZY`NT${KuaITd!~kQOdfwvEKLru^DcvS9=oIdn34RTE8P`I?Og_%`R98 z?wJ@_KRe{^zR14th31`+%3UwCz|!p3 zsFC`u>19AHmex?=THIh9oB3eON5if%rDAl!rhK+>sSV?R?+MnKcYkwf^waJnnF(av z4wG4eaYJ{O{77xcpY6jTg{ztb1%fB*FGS|TP6f~Sppqu%R!{ia)?F)|lg>jzwM50u{iFE02?R^D_w z;&qIrkaF7gVaQ&tx#}goI_Z87>evTd(RRbp?a8ifgpX8z7kdqB#-AT=o#)hKl-rwn zTl6iv8Fm^gi@gu1T+(~NS2}zwm%eje(+_%Yxc{*t`|(W;H~M`)f;uLn6#C_U@vrdF zE*s3LnqsewO2z3v51?w|4johYB-p#ok+ z@76E%+0*B2_d5<$3+>UtU*f?3r94d{kN0JMR#U zG0~m-1PXYt!$fMS`O}j^82z}5XOHxvqu!T7!@ct!{`&qs)Mq@+A!)ttgOiq##Q3zQ z)Kyh5weVQT1X{cDNnuPl z>|-|{V}tGk$GsQ#KHEh&^Q`-S{~??#o>7ES=K`r4;|f?siwirj7aL;aM|>NV{f?@2 z-QKyh%Q3A`Lr~zNS$_HeBz=b(s|DXdfkw{^zuS0Lzn{9MUAZQh@$s_x@F899&J$=o{90eHW1KQkK-Xt3-~ypg*M^Y! zmKZ6kT)m-xt#5Znsdb5rqCtJgeE)%-o}-7R;bXp|Z97wX0jc3M0KgtlBDaX=QWSXi4lzm( zsm2FNxLu4B#cZVXOWTw;IMtqkE#L7{orV2n*jiEYh0H(ht}%@y;8_H%baYU__T zu|k|asjJ0!&%sTN%Mj){Ecjd)#M1w@t7pTM&Irh*s!$PApkG^{q|ntOnWsDyhQ|md z=WZsACcMx2y4Da%Zq?XWVH{`8DQ|Q<@VvSv_WsJfsm?WDQW!a5-gq`*^}0gGdB^y! zUBQ9D1E$v2)(#F10RdPE;YV-dmMzcT5eNhf22)yUdNSr6W=o5P)-f{QC^V2tIZY^% z*42V}D6|d$_nfmwGAiAg?>O1n!RG!zx-|6^9m9L{b{#|BHVJ;2K2fC`{{3sQg*+f0 zLip0evpsSRT=ETu)SRze2>c5aioW>G&7l2oKt?%BTSb|} zV1$g_di3{JE}ugRm)fHb-Z^+r%g_G{^wFO#ADj5&b?Z80-6lUfI^iA_Bj7aI!CQaa znlfnW?2LwfZEs&-Onn!N4r!1pW@jhQoi?;NzmbW~%l^7k;7N{pS7uf>#G=j}YFqBvS8H;t@lN)z$4x9iYm1AujjV8s5wiU3DlEdx~ zJPgfCmPWYLWP|S7>1m+xMbry#hS3A+h|y#9LDSQqIhSLwKH+>xe){YQuQ{JEBs?|K zxw5ph(tl&Qp+Qd)AP>}yR?9c)VM3X|mHvwTwIwEyEL!W*APc)7;(i{~*}-9cSpAit zoe!vtINldiT3)W=q|W`Y9A~V*^$CmnLzE~HYk(A{(74BIc9wL3rbI@QT7r&+MK`|Rbud;A#EQw%B4xQ}SL1yZ&QR0dC zu1sF2SaEfC5I5-xcaU0G*)3xSa&2z(Z;d~+M@~HiA~!4IGc{CHU`-Y-46M3_6DO?U z!UB5Zooa(@L=M7OW!_mG$3nWXaw~Hpo?`&{(QUkC%fce9+B`!&RPuF<8p&;%wG}m6(yP7t zfTlWU?riSwaE>!7V;62NgMQo2t}be0xvlNSp51rzwipkT(@Nm%vQ3braJf$+{0J6Q z!@&YtoJ7ndMKkjy#B%%RymSwH-3eALvEuH6QbN#2g+42TP2oVvFxH3|)>`Vp&2z5$ zes{iLpeVtzNrEfSai}ZaB$gOOc|6Z~CxF#7h1~+rmiyDHJQ7|D%$fuo60mpIIw%96 zzKjtY{|@$9KotT}<`K5D(bC}2qet^l;*CHg%LzFO?*huTd4_bhKbNxh5GDT?LRs-l z-AA+9&|08#=u7CZ%Wz#F1b#d2LqU^fRj%Xf%E0KOK|2H|qtw1(ckHpnIuq};JV{fr z^VB}2?RZ;{oK@T2u&}To76~9I5pVm>U+RJN#Qt(*`QtutNRvFNQ(9;2t0LJ+)Q%FG zY?ycC_}C34YfD$8lS(Co#QILav?q3!SmqL_gM(AeT$k1%#-R|q_62I6BEn+H47_sg z@Ik)BtPkjcaSrPFP|BJdTl};}Y~7A0{xuZ2Dm0T!>^Rh%XEn}hSYD%j3>-8kncs5e zmSaOzlt-W8>w~;aXJo2#U%@=A`|x9zqM%an=z_AE^)_*Jx$vUX(37}U7+he=nr~ot zd0HViN!8G?9sNh@klNa<(sD+FuE{Q&RSTo%r|0-I}q&A+JO=)cxc@MKQu|;_1$8h&f3sQ za^pD=c}#|XX>vJ){B+*`q?h(yb&cl!CwxWmEZ}brK?8e&2`0HG1{s z{L+<6HP)Wcz%KauxsYe;s*?;vBOQmhxo;$Gr8+410vhH>@b`CUYAgz&l)}gLR@!xy z$AvEDw3HYtLqg!UC^*z5AU7`{BbiiHC?`AEmMG6|vP+QDh~_ikb%D(2)w$btaFR1@ zGJsAXHi%r`(&yLO%a@O>{PR8Z zHP1fRP=C5jGn8pdu!TUJtS!9v<_!#p3*uW~2u&?g! zo#hnyoR^=rrcb7i=A{G`JZKqwtK&)m&_L0!>Pc}KQ55OAQnBMCU~l6ilWqG!hp`%G zT`QU_SF##-O2CIUjhf1hL8DDLCr5{<9<8fhb-UhEts09>n5Fb0XFJCziy4{JbDn%| z1kedvHUISG%6J50leWL9BbVO;y2s9QIu>gb%7UmVURdJsKmzB55SP}flpTkmxg&<~ zi8m7|JLu?B)V%14`QZx@Lu)j*yff<11NUozwuC3=(yYOiqEI+H#p4j=r0#A0(k8Lm z&hbr{J8kfMD;`&861;hBXoi@;GggEM{(5sB4^6tGK0CSN0OMoOvjGxBL-9hWachu% zyW#o{_VkkniCQ;OIt*@{WLQPXTb{V>2%(HEoU#pOXmtGNL)KKGA%> zdcJnRN@E0Pwf2Hn^zV^t36gK%`l7{O{NB6v;!aw$BO-J^ch{TonvqV=MO|^=UY|Ud z{>{;ssR^?>P})dMfb|MBv4;fDaU>P+hBfjw*eX5b=+bF4J}_cVe9|V?*Ke}i9fz0W zV-}!NGIvKccB#8I*4Nam7NXaP1<+LzdklTfD7YMo4lw#7;zQtzFeP=x#HxHpq^NS( zHJcan4gBl)+q&^6mc8h_E=9dmhdDIF{}OECTd?X|f%x`JE(rwY3^%+M^o|fLG%?ch z+N#d=4~NQZHjit37_IeVOf~|IHe&A5Z%AvU84qm6+3|jL&5Cm$ zYxJVS@93(4ps9bvt>Ng1g4J(%5rw6Tkr=1nYfQKVy6s_pj=S251 zPua$x@Q%*1cp7p=%DzO0tmnC47OB$TzUOpOW`pWVbosjhx|iIngFz)N4dTKDl|BC> zK@O}_yv}39k*D^^RwSurZ+;qVwP8oS9b1NRELU)yuyFP0H~YOeGD+frwHvB`gPuc; zH|6zNYbwTJa+cQF)x}u&>)9SrBNAvc36VT?wXvD5R;i6x@w5Z_Wfu6gR|m?@YBa5; zL>h2FYhJxMK0mAo?omgK`Ne(i%Ui(N>a7l=0`?8TP@5&BbX5n@aucLtgm@J ziq&OpMgH!8wPP@=1l7P#ga|I^-~aer8Zg#8fxZI}<^1TmFCq}AyhuKW(Gco){rYw1 zpz<}+Qz%)#%SQdKo0}U9wi35S97>ujR$TsL3T;sQrpMXN+8P+^k&N`0pSb$`RKLoe z5B_r{9L!wfoUh*Nv|GkuaR^DfRgFIN%U5v{H68Clj&2gjxHkEwEq+<4!q3Gn-7X6a zwMqn`W`PDhSf3#P!bV|>Z6-X#?xpdlPTusE*ra zuD(C1yN~*8r@eyF{@codA38ffYp%K@`_jZ__k&im(?}4|1K7Sy8qq|YEnm@L)Za~UD#b8eBBH@KPS0z}A z;=giIR@=}iYGOWLn|z5FwDNp%07bbI`hETRau zZGt^1?t$FGE4Y|Gx4ozx2?&`PF{>b$ZuuB1%Q6dyh}ihS6Vsi!ZCYAdoW+l^=z2qd zxfAHzbYTo^jKVo>{qSn;T+`?mi)&5qOtMaFlpc)wbBgJH(1l{zd5m#+8|G-PASTjX zI|N;U<4|s+9TB{*F|so9+KWm0oH+&we)~3Mi016k|IQR)tkkCgvjKO$WZ7aJwMr6o z#=|FjbePV7WzO_uGvMP=ps0$6Em!y%hah*Y2!89gi!)4|Sc>2NG>#EZL&Vc_l)|O5 z$ioB07A#;`=Bsf`vqZ#odZgw4c`si$9S@B~VV1i^4q94Eb*0e*p7rDfIoI_eKqX^! zkxoFHH(dbnRMqiLaX+&mqq-|*`Py}5+ryBdm2aP#ogwZ2vbB+Tlm<)1Nw#*r62rch>-`ma(0q8Jo(RB}nc1CP_ z+PIZU)nSf7Tsb@S4{MAQ9)7DaLi!qQ&Mj%6B8KxtnLF&SK9@-w;9WrwFI`D~{L?wg z)J@(uHQ6Zs$N{jf?u?1DiKHT;4_ByR?j`bZi8lwpZ=h9Ik zZM5ohyQ10{VuY|?bPlV?v>QA>);jldv?HT`N{s`D4< zZ+e}1+0X_bfJ4a|t>4QL{BrEl_xx(4fX)<`08sfO96+?Lz+^qe!VOOL2g8WKL@>^U z6gJtG==7T(6%!#~*Hvryxy=`rfg1w&)=udZMU|kf}^iF%)K3iK)rPO!cCo0d*_D;$Cw&XE}E{pfTcPck_NqKmf+*xKNwc;g9 zq5P>Tl-KtV#tgBKXq^`hg|3zKZ3mpuW#cONy;{Bxr5z1|mn$@G?+TLqOfG_PKLqQG zbOXGvOi$lR^@m|9D}$yx)1;-P4p+MjK9J>p~4Xw@H*vkjfZ^Kl_ywusHwfD*GbK-ibGPLb;?MseMPJ+ei zzRuYjhI0~ogV)1;_@>jzuk!rm1{3CQi@i-bx7AsBk}?|=J8T%r7E8@uLpV2eV&i+3 z>jAs^&V$H_7qQ z(BO>AGaip;T_Rovu)IAE%`Ys`_xnT&B+G{_U z$eG_0RxeagaW1S`n$h~#?RALpz7N3?|M4Y3#h5Q06>VkKJl$y_^6oqcFPKyg*rp1% z*i$jOXK%0kz06XkaK={@xbO5fRnXU~d?&_V>vI+=#1 z`DRiUc$B3czSf-y^Xlu2^wWNw@9$eCp^*vE0+75w6hkMjt+Ch$v5fGhRA!cRQXBV! zV-up5Q%YO8Kcip|)!JR*?jzddd3boN$*3~?ms>sJuk|VP zv%|^7Ci9H^#-zdFVG?&sXg4VIE^2YaHezyUp(o`bc9am{gmlSuKGS?hczOCw)S$^2 zqZQJ{p|)_6fsvM0WX9l- zg5TemaO+W8QNcU-%+)cL8K}8M=~XkpB%w zo`2(Vv-7|`B_pv}hkWibsyl5!{2;IIL;SUU>DQd)_TQ_8d9ibkAXB0=u_@*?l4wm6 zMS0M|j||$M9~2D2AI6LAeg+uPqWM0%O3Ci~ks#-^De=-+tj$J^D3+h(7BMqUHjlPn{rz%??-7mmp|{SxFIH?;A`DSP{C7Q*4VB28!rQ1Y{#tMN$$2+SC=BC z7P6Xk?n!JQ?*&Y|91)#tu#$)$Y}OQw(WFLSvg4GjLLSfPBjW30DNg-l(!|6A zT`Q{4XkHj`yTvRz>mVC;Qs+yh%PlDr2D~k$UkPAU&cxs$U?hyiTJXOf={7s}dpo6& z^NAZZtIrg1-~f>c{f=IW9!IZ$AULD?LJ;WcQtSCi)>emk8|L2VFU z*@gkei5OeVC(9OPy}^NxitP0Jqym0q`#+R0QIT@7asKo5uKGR1$4Qfy|5_O+)OXElUh#vx z7jB~bh%;SSiD1P9)C`G^_&#OYQQ^!SMT8IX`&>kF%D9bXF@kHhmbvbac+}^ zM=f_K`w1aW*)MuufrFc!+coB<{Z9eiNjBi*u$p2|!1=7q_Q9E7RBTn5Ws;2>4nt~` zA@}lb(KY;Am_rasp;qgFvUnmdk~2SyXZ9dB8`nnt(xg-vE8vG;Ka}H}5d#)FjE5hk zCEsNw-(w?I#tPBu^L@2%U-IImkKU^lt?TgZ&inKOQ_HyneEhUFU@LNp_iWV8RLXwv z1-wM$oYwF`{3@Tc{4M9*+29ArB`rf3Dj(ky8erEEX6kBrw>uFZRS*(xWoISaeZS8y zUaQyCRVN~hv6dpH;E&8SakaPFQrjWdE&WlV`SYzxdkA*9Uv4g}#+9)-M5pdT&=XhZ zD$HLf#NfQ%2D?f5bT2~zb!hq8t^M~skl{97+b{(5CkDMe0rDbj5DTfex6)3Tyi_F~ zniylA@Rz^Gb~S)|TwiTWvQzbzkllX%;aGM|0qJ8dS<oO=@1wa!>k(>y zACl97k7_k)Vd%n^r}V81c?+m@S)-4yaLYK1XNh(w@v0UCCuV6N!-wQ-qgS|PvWgfZXRO?-0d|>K zacWM^--l@eYQi4YaBXz&NHL6D8-dcI*S`a%lc51={t=u5nK!yVx6v(|kF2rMp8vR- zQJoxeyT?{Eb+d<(<)v(~uzq!n%)>gl=58!d3dK>xbj7hAb;ebVb5^~z7`G?%@|K== zzztD+67$EvKBdq^x^A~noQu_{$3r|M_cw(Y93A&fPT>XJ6}yY=}}yt;j$?!;0L9;}?tgsNCs z1qhNC1GZzzTCMYYofnCAFz|-L4}04EMxeaZnjl9OE_Yd%Scd+{s{d=kU)!Ve;4rC@ znbikmHIepdXeXXv^!~TI$8MVfFR*Qnj*hl?S7bK;l~eBa2|yvjODrPif9K)B0yv7l zoJ^GS}YlbU{NgUO*Ka%NRY)hPs}l+i1QMb z14B~mwt^bAZ+Ktq?%SgyZjUP`{~_W@2m%zcQdkZd5pW9DF~>LvO6Y}QI6$pp0OC5L z?DV5j!O<8%y5tx}PJOKC?Ccy{$kOr~Tc8J$o1!*?>mZg1mVFOeqs&dvihxZ7dKrB( z+^17|ZSmw*+t9;mtn;ve13<08HT>H%Ab--h*;x*7oFT;W)H=p}$hUd#5c^k)8ZeMN z27+KaRY;{r7qwy3k^YLNnEPi&{OKTkNbTla^2t~k1q48efFDJ^-GzG}9s$J*5_yMC z^K70mhkpAvT%pNg_a|DCIC0Kn%wikHW@9@h?f@zXhlKKgNU;RMX@~q>RDRVCs9`a+ zLQfpH@5hX8)wz;_f&v__mlxgallns!Zha_@M`LAX8Rx@3DL%keSC#{K!pyVF$_BD!d-h%&1Y>xN^77?INYy z3#hiuiuUrs;wAL5tBO9QcWrpDp^Pc&{pPDVjnwp!NE0^j8Hz*2-bWTz) zwQ_tBUDBoZBFo~PdXFU@i^Z1X%S*9o@!E{3y7u7;BJQ*vgCqVt@$F;#0y=;}V5ix+ zMgYzTgj}UeOiVabd$PxB%UxmCwAA{>ypwhBGe#>nH;2k?^9>^UQS=*Q%n$WJtyTt0 z?vdE~b+4c71Hj_H8x9^d0t4@mJ9n0WjQ(bXk(A7_5g;N3qS}-|La^K48AWr2to|88 zTLt}y<*Blva`ezJ{MhmOwWgNLUszw|$Zz~($r~1SD7UtkTxrE)x%;)UKVL6umE(VV z?lM|ic20-Ei`6k&pV1*8c`G0nWNZ6%$@B+_SOX*{8jhFnTCot3Pzos#e|#3$U}z&H z+u4C=Ait81>anUVh$%YqZEUFtV0AlUCkHtE@_#JvK$;Pd8W6c)ZggO!fV4!9cnDG|?J=!LK`E4j(l zoZ>r`Ww&HlPHjmFsglmOEQWnS`MVsIQ}t;8BLiTtPNX&3*4BnL+Vt+rGsFdUEkMys z=^qHQ#i;U!A1W?xv#KH@l)*d2S90}2eR&H*Km?~C^|R;quUt{{yQx*2O8}h(kY%7Q z&B$BR7*Isr7;1^Z;m7I%hZi$+L=U|q$nsR-Br{DiU?C4`2;{Ty3K-053n-ed?ls~N z{Xj1XDFb98fb^uQYvVc)k-$WMeFi*9HP9l`n!pSyhg!}2l-B4TnGwj`=>6<~HySJ~ zD_eLqc1-6BxKjZ$r!zt+rtHcr{3Ud1PRYM6N%V)G=4;D3uTD)uX($4giy7U8sCJ#@ ze9e{?nKpop%ymBBEhiKlkiRa!=shs^)Q!h0U*2I~VwDdC%D~KKHt@**5JN)=fUj7` zrUvmyWzzPi>zC@02(!}(pPgG66ICw*d~?U0mCIwx&Z+QJk2Hw?%pO$?093q?(!F{- zHz17>Tup>f;t~s_ZEQf%^(DfXWj+&yq7+=;!vmr?XK{-Zklab@5gU+Ry57-vtK9WP zy~vtuXq{eZfgQ+Zxer->$>!<7rI`NVH@BZuHg|(uO;- z;$(0S%$I-lcukuFgyp>k!Yv`FL4pn`;~*yQ6Mh zOfm5zjrC|O=r*6x%5767#bDPfsl3-K;M#_U&E@4<;45|jce=tP{%u5m{+^=`#}yTM zut%3SH}%YSzw-%sb>R5T>#_uQ5~gn2{Va}DoNE!F-gH-(B05i*>-RiKQ6&G@pG8?= z+goDN05<6Yf?tuzyx;i1t$g$GNjHZexs-&6%nKncZIly4i-u20;jJ1Da*2!^;C<11 z)ke!?0$OJ>E&arvvEi+4wxKaV$s|`F-au|l1xgHmi)hQUDPSU3o*FpP6lrH+VR7u} zl=NPMBVF-79W$DGsOc5b?*A#Z67zX-1x@M53G&HvlqH|;!IjWOcTLrfDzWydEMzWS zlgr6ri~DIdItk_k_1r^Iw6OX(u7Sc$IobWee8`A3Or^HAmK4TuB!#g7H)o4?U}v#b zizgljMHZ+XfgaBAchOT>uVOzuz>Ls)rLK5tiZd*A=Hn-$((8rph7YdiWrgVe*Ee4o ztg=roIqhOO)H9KSHTT*9At*p6^sS`&TVtM%i41C0ZKq02Gps|a>)l!X4lNl0vDxdr z1CK+Nj-P;ot<(|@Mfmnl-({$((;7l-zkATXoo-$UIqqU`^E0E*y6gYOqw|+bY+akf zW_w+Ly&LIzgD?MXJ2-bDZ|+HIwF~}rSgh@@?>~U(MEDw>lf7%Jc!2RG_P@>ZvT-RM477w-2z^s9HnG_OXy*{9PVXH$UiG(=-s$1Y5?u1N*8 zEV=35?6Y3c(YP>GTC41Lp8qnuQe^D>P&gvfzcMK7>G~zYm)+smZFthvAGKHR7?=>V zYM_N$(Y%`H7+G}~*7rh=4*ImC9nC&&Xw}1P?0!{3&HLYS`>f%~$?YIg3=5o|{F3np z)YjIv*2zE{k(DkwqDf80Z(Pr9(+`DSr?dJh8aS1fb@mqP7hLcxy>4H^uON?8*@cz4 zC2-H|s3GXm$efSkn%8H0lja$@(4+rHdtV+;_167=PDCZCgpf|%44G1q%3P8nB=bzj za55xAhGS?Fg)*xQnKB%Xlp!1xk)cePj;UlG4vyJ5oZtTF8m@c4zxzD*xzF?ZexFzV z`qcS+_Gj<4*IMuQT6^vN8T;~2d)ZzWZ_75BHN;XXArI(F~Bb>(bVJqBZnTOyYkBk#YmmHnFT>~x+}VYU!8vGQuA=$j@M85%Ve1= z`pmEQ4@S0f8vGpC&l&rvivNdg%u?K>ZlEqCvqDU(djGZ2&p6Jo9DZdrss(c~w$?8* zLll|GVc|oJ0!WQSw26VPFO|!s_N*t)nc?EqxX3Jp{Yh3nDaXGiTa2+M`cOaTEBAgd z@=(4`D!?aS__z||Yj3Um*Vz>mSx}Q=Cq^Sng(f_MdgpdNLEYeH88~*M`Fvl+)tRG6 zrzT4lSf4W_*OkuHJ`{|vmOrrR%(YSNIj=c<}G_zSW7icqa_ce-D7(1#JDCOq1TAY~=58Uj~RaOFCtAokVdw8Xjp zgs={LRT-XW>nc=ScMKD>t3M%o+I+L;QA;EeM6;x~AR(p%F#R&D9Vt1xt+t4f(({On z=jH>!?u!ekKEddDQokfQVP>95bnrU@0qzv(e`r98bsarvUVDJ7y%UzV2hQkud8tQf zsTAu!ficG!StxItFQ*1Yd5T2nVepF2Yt4Qxl?r7{co&!JG9&8^zywRiAO# zkMtcZLl)n&#l(@2XJ7+YC^cbc2QO`pH&SN#(HWX=`m*!Qw_&vjTQXpG>m&AFD2Wv! z1kiz(F}=t76EK_MG4i_%;ad^}k3GIGW6`rb+9P*e*?J9VHc5s3QoL!WoY~a_v=8e) zN=ksIbhpfkRQ_gQEQ|?H{(}Sa-mk6QterD;P_kjrUxUPcfIRoPe@Nm`nbNZtiGGO2 zvEjn@!lJxEuL)!C8N%y_H%u|^aU_(YYoeeNGjS*_E-IvA|G+y}^h&HNfz1u!{=U3omdjHKJ3{Wd z5fydRm=u$0HGx@>#PigVFY1jhA%-WGJWPWgTz8&%hg__Fa+{Ro>%K#RI2U6xmk`g3 zVV$_%OD2-QTs>^?I=ogPH?euH>a5gpWz9ooFa=kWWv^se7$Si$?bB91tS zY{Ja2it|q{Wa$x7W`jtRZ|UR6_ZD194o(Q zOGB((?rwJ7c@wW~1HyrJpF9H@70`YbMu(&h*sy^2qN8kNr*g;rK{6@KgTRFuv5k{q zeWRXbUe7D4nETErz+JS(YU!CQ6N+7>gT#M&QH=C`xTV=!LeQ90i=GYtSfxbPR?}zk zFPu(AofnHE58cGg?LsYehTJXKRzSd=2^dD!4Twtr)jt7^y=&>Ur$3Bg+DsoibLSih*tT-rS|>JkfoU z7s%JTWxI8q9mpe2h7%|IdghP#AufeUC@y8g_fXDn4LTrc&z>Ae&UciZCm(Qne4!Fc z^`O%?q-t>|L7qS%NA{?4*S~ntJb7SPdnfMvFqg%J zi`gdWoaCh$5?u_g`@$93sFP&_|8m!#3i z=)02@@?N||zZsk4%D4cJBDFrV$BPAlPdd+pQEA%A7)$QfLr6Z%mZY21Adwf(C-_6a z#K5#3n3AOR7z|NU-jhY6Jntk6X>LS%jCs4ZE{uGXU^GtV#T4S(7}*}>9&y1QE-z)h zcdo7Zq4q<4H=cP1PjrJd%fv{M4kj2CEq1^bQ+QL+N8pz+#51v(cQ#!vdWhLA@$5Rs zHS$&AUSg$`3K`w9ff_H!e!YaQgo!(YxbliFVb8NW$#*Xfopd4GS@d2oU<)2MC)@!N z&%P6V1n-S|^8{5mQ7o8Vvj;cNa5iYZqLF(>@brX$!*MXQXwvD3$;ph!=$ys)`eek@ z-(&UAWmmZRlEJ}hgyCZE6m1%QZlJ3Lv$L{qav}gDO&AmIcvys-j&8;jEHo(xjq;zt zb9|pOi+V+>#C~q*(#`C$Vrk@Z$F`JXMeO_aEyl_M++T7Nr>{<3Q8^z)N<7miooT~1 zkIrX3`I*dm<+R4bBZ=i=MX{y2dabYNvwCQ7jlCZZBjDzh+lgw zzL#^kSJW;V21ctc#PN+qL`3UG9zt|W7?bYI9K>t5PqQwbm*k~ucR$MMq1ZXX2$?f5Gi9+IVCSFJ^JZoTxd6 z&aZYmpVdgv<B$O^Vu%KiF)QYhb*Y4U#g zk3tJhNAO&Vg$O~yj^rNhI=aH@{`mgq;Xp$yG`fQ4iaaOcs5^T0KOw&hpF15voIE;K z)AlM(_Tm$|8`QSUzoAiGALjFjFD=M4@Ij~EtX6@yHzY`YdU7Ny3aOm$f5^$kewkuE zVO(Z{daahS*r;%5B+_U>Kn2rC7Ge{5-_?>(+9eFLH~76i{^XAT8Y%A9FLe7xV=#^} z#l2YN%|A$dYmIx;8rS_bHn@Q?ChO!K%fL1lV`pw+V2i84 zMKT&K<~Cf~mIu6TC%BXX zmz01~g2>Gix;zU|cMCLj&rRpReu6U1wq9%9 zE20>#eUF$0HUm}(`^(Lv6#vff!lfh?J7 zvv$Ak!^+0PTlgtD3Eqstpbk4tm@6w|i0qtgGq?_!*;Y01n%dsA*HCMIeqklA?|Mm+ zDz$lfRt9l9D>1j(Sl&`_KBVB}aN!_Q;!+`sXIn;l{ilYLnxiGB@A$^j$D!pcKbYoK z+pvN8qgd`2Xfsx?t)gp!xSwUT@F}OY+xsSd*mRH)Qomvj*%gJd7cRD&2~%jMD-H}O z_Vp0G9AcWFup}vTxae$dZKt(^`NbP#{p{Wn=ZV;&O&gvC%Gel%%j|wOI@t#7w^kf7 zgu7svJ3&bsZbq9N+gZ}z>z=^?=A#~>>OY2m+X=Gvn0&|@{(=dBmw>JP2Y~i^LGQZ_ zIhGh01W(JaUKZKrRbl^$_3=SdBlQC63K*T|0ls1aWc_=n{+{N64gy#5j~e&iIi$?@BqKvFJl{Ic#w`$$G5#PLbT#JYs@0^^X59ZYsa5Z< z<;1`seg$Xvn#R@(REtFdfyD%^-sTB!8s!i*X~{Hh%3yV=c$R7HVe+DYTfQqLfjJ~m z&Cb88Vfel*r^4Pq2E)NVqeJJXj^v@#Wj-fN*$aNV2TI^PS_38E)4xvAbvlNNEOOO!pwQSk}BPQ#;UyXb^)8o0Vl9`JM}Yd*ZdwlniQC zK9^!>)jwBF?m*v)a!npk^YZG6z0>4=O<%J8FSTFiejeW@vuBIpi z#a`#-E#_&S6xotN(B1kXTAf!JXd{HO1G@;X18bs1 z78uSx>wg=3gf2p(^$MN`zl}dF1mBG<%Y)z$w`QbPo*{L_8QDJ73y)N_QV{8#u3uZW z22641Ar(J|RFO)iN#eSYVhz!`K;aTNBI#vV<`@0Q!H}KKMNaY-h?sYDi1p ziS}>-`-9$^Y3u2Y_JclltXfW;8eHAk*;z#;DB2BoLP+R(du|<+x5W9?_I}P>rfu~@#X{)nDsmbIG`4M~Mij*kfO&@?) z0DS-)W(Asm*OYD{EcI&NxM-ry1bv8F>v2>uTW69?WBO%1;Tv;O8mvccRVx-mIvR2l zOmbE4EAa)fIrB2Ta2o3r-`)O2fE|*wr!}BPKlC$K46Z)CFi3Y%A_Qzsh&C;I$Zk7I&Blb9Ff>MAQhb+2uexG-)UEO)nHh;x;p|JzCrwBBm``Pxyxf!0 zFB#93ou-SPQ_ytoa_{|J_r{8M>+FVyUQ~4q-CkfkZcDT(?$=j%tLU+XCaf+WQ!IXoJrDMOMgp zuTzft{e~8e9xUqL7Y0kBpBuaw4524zY>B+m%Vo8pZx_rwIikxXUR~;x;y|>fN0)cz znl4B;OsljT>m>HOo4B^g9p2dZ!VYv9zo0B_a6mE3Da*dk=ZTlmlthIy^NVK}!_hvb z3lHzi^FR{(%^bPG)n}I}?3#dY!-l&z+;*Ti@6?9(ZOJ&Y0|L;2rZvMuKa~FMT*cB* zp!@o+NzT7Pnwz&|7~O%qvc2?*R-V(?tQis#0!k(<&CSOrl`x5koU@lwQ&SiB0l~4Z zlFA=e2ka`tNZJ{ml%C$V8RYrb-uxha8-z@?exTcb1va5s{ojFWf5MmTEj$~4KobMt z%ll7dzo}>NuVz(O6*3A+bKmrgGxs4Q!!&oR(WLKbCyQ@xVfYjx2o2_fSIJ4<@#2){ zf*DEX)r8QdZOmd=<## zp``c~O)tt|$*u^A8ms zY%6r3eWo#ZL{;LOqtjC2C+lZ=iFW=8x>jQlwaQral6owLr)e3g%*21Oam+)#PYgX( z97xGsaXf)Nr{W3^5hcky`*aT(AI%?P^RzbAeB}K-EBLdgM3+YI*@W(5a~auhW+|59X{#B0UNc+p{s z_wF<8-W=-4S5=GF8kJzTWgA!4BEx$++{S)Pv1x`R7*ki4Cs%nV)Zpp@rw`&quleO- zovv{x$s9^PMlUxd-R?c7$0?UOa+}a~vO2g&x_$8;l|R2#zjS+ByC@jfnYD2V6)%_^ zf3gSk`2DJNZm^ywvCFY>*tRvG0si}cjsYQ8^Zy~hAUXMVSXD#Arakvn0W8<7k_N17 zt;0I-yPCQ>9+#E+;>EifNi3)VY8Qm|OoGotdRng%=YbShYC%Cka&m=|#3(o<1?;RC z-~z)~hspQgp$LZiXI%@y0-nnK6&v`guI~@LXzq4+Q|O>m`;GGAz({u-&czVFn0_4f zFgiQVr7md_|JEK7LeUy_P3k^6#3@@t-%V(6zQXJU+dqIs0;OhL-{;6% z?G<_kwJ)*!OC4&~ zcID#{3~6b8qVo6YZl$0ndWL)`sFV!44v#gkm=;~jeQh#6zMD2j*P^(zk!#v%H?_o= z@QJ(*;lLcEpSJz0t?J!>jMKWH9$arQ{M#hm8)3`G42v!?Ixgn!S@=M@vZsZnW*15M z_uK>a?WW2Nq}0sj&QrAKI-YlU;#mfvaIta&=eg0kiEpv--GHAa(_X`hw)EYbC^Z#r zeviMoZt&1^s$la$J>AWK+b;`>&L$Pnhm#oIdr_SK6xJmXY;e%tb<<|egi0mca!9sD zt8Oq;@S%c27R$E?zEc7F-sGExWi?OWzbKg%k&`3psIe#2`Ftfy^(1L{FL^dX0m6E1 z4m()9pt_;Oe=p~=H}Y(3Zy(``b_82qS%t5UgMe{^-DGCkfsea^pWT-R8^OUnQ5Fsk zS<%3O{}##dA^^)l3x@?@jg5^3U%?G)VgZa7%AMuyT5mM9;N%K}I z#PV$=)(8-m0@-j^U*fgMaU0Didxe#DC$Mp#V)=M=G{E`|e?D-(Fng*MPYQ(%(u0R& zWJ(zKzj_yb%-Gj=Kev^+c&2SQRxmZ;YG-&pgrIpwnit=mQ(!S%#^Cqe-m1AJ`mDa-6)J=TE5AU}2 zrV-T9vU0rg2=1X1Mk>g+9yY-RyO5VGq>;!H!Yi!rdA{ozlqofL35-rdXfGLTS>?xb z^49qVDdFFDl=Lv|Vy)zA$2SRA+RKCh9DOqhI7YJOie?_iu( zV2>6`uFs-KdNMVB>$A*EP6&(p6|F+cl$aC0g=fa|4P(_aeGCqQZ0&&9icZB21M}{9 zCxKc3TtK#yi6f$MG^fk@T@ctmKK4k&#?#c90a8OL*y;p;=y9n?Vy}lue!-!tCra-v zt+)|wRqh7hBR@xp4rdgSGu@RkCX3GABC-=1-kW$PkQ1cE1w{`7<(AS}ZH0+SQzFy1 zlRIJTBf+NJ(#op!7_xK}+biAT{RwwaIpmsxF=sHlWO7H9Hp|tvfZ(vXNszesXHN9Z$3B~5cKp+1wxBAa1J;2A|slf(N zvaXk7t#|rLHOjADqtfFO7$$rT{&|XV0Dfjh+gMpxNc#pGpi{iggJ-4z*j`@k2}H-i zSuda*mqcmNxV#@zL=u4f=C!2RdstP#V{5krFaQuTtOT+QEQR1x(8a4{bxX!JS_5tX z$Lo0gz55tIKWx`VjXqnV0WtzmeDZ5XTK&a0ULYDMD*h2M25#1DG^O{Z1N&p)6tbV% zTa!Y{_&KzhC0?PmX>#{K7QNOD-B?(vbzPF^`A?FyIGm>Njbeb-X=z*+nh6prev+VN0dD|Zm8`uqz!xbInay6Br99J#$&*LP{*G2J&+;zRP~A> z-|&GJ@$(vMx;(sZk|$G#O$tpH*y0!mF+0?&@?SGr?D__#9R^!9j@)w~trPf;BHEy< z1&7OZU-StF@s@Cf4q|q92QFjO+IW&T@x^cc8>Z?u zEVXt#WWA`)c&eGt$*q^gzC=1R6;SLvOfdN=h>tg|CRgm$mW|AMHR<*z z30P=0{%=H*dY)MpXO+>jqle@w1Lp2!FXL0o5 zHS4vpdLEvq<7Z~B+%AknmbPrAaLxbV#qmsAcl3g@4;~K%iJ3aH+gXprIw^N4-|2C= z`d&9#vBe||Oudx;igBMyg2~@Zk(Fa*uNV@%NQn2c`75R zxrN|+8h`YoPQRv4)l<v%mPp`2!q9>uKI7IP3Zf*^d#@xDl;P}ciJsy>^fg?(Q!wFMuZJ_EL>f7 z4Q6`N2WzVHAjV|3`n(>ip^QtHL~ML}Cc3iznA3Ei(p7}$wP>q?p~S5 z4P@#;Fcp*)*y6Jm5dr)Bt1EyIHh?WlSdcVy7;XwV^r5}^BPxN^ynUl>f-{i7r>(J2 zQ1?UvY}>&j-+-`L5@a*h!N^)r_dhrT5KWV|43_-~K>QG5Y?-_L zNcwFh0F~=7t(0S1hPKYIx}*o0?ek&{CY{L6;CA+RE;|c^SjGL#C$qDK)1xJBjZg7& z31t4Rt%R)^DNKHO#@#NgHWBXSo`X6^(yto3?cUHF`2Hx>!YnwtiCN-na%R~>^jG>n z0I@P(nRUP#dj=c{H-2#gcB#S{>u>+lsQuR_WvAgr5|MYb*b*E9L?5pvEnSl5FiZlJ z@wYJW52_l(kH98$aP}(TN7n&x;CXwCt^pJGTr(#I2(@Ea*D;v?BY5!7vBm!u6#Q=) zHMHyme0cOPOXRN;gHUn&^3u@w4#51@m0SF`40i*|0%38}tHe-Q1HGjj$))M$Ldi+H z6SJ`Pl5sldh>p?q@yo)}oBCAu@n@-sC=DUYccvB^l44!rp2`@$**~}$H*2Q9I8J(g zd2n|Qcw!bbpXDJ1+B8S3pk>`XxVqwVd#$Igdzavl@uOC_%D-3|Qx6-SE|JyN;gz^0 zkkfgF`1Ka@=BK;QuM(GQk75LY5SHV<2lC8@3N1H>K7RX7n?>#YM$0m1+5b|4b+ccq zY~I6=<%uf%Rc1OEhOICCLl6?*6>XBm#l9sw^0py$0!DNNURGl)%*7>Y8U@2X{is!n z1U%r^=ks7lBR%=i7YE}QAGNmHne=leyV(|GO<(#@HjUyOZTZrU%P`YflDqlIzIpg< zz{qtAakm&HLT2mly2@x6G9s$-)*q;CC!`ejgGTrm%mDR zzj#*On;9|03!up$J;uBu(b9x%89a`2etp%6_rO_De%eQezecd7@|F2wB3>>JdM#IS z!(tO!BGo@jc%M7_Q~%c1dKX)RXu2LIDyQHOhCzYtm17KjX`1oV`W;XDRQTDJmSUjA zQW$LAumfSk*wfV06lvYQC`Mx6_p~&AhSP}F&g|~4pe(b76Yg^JUnWb>%Kfc1K0WMQn9Qj$FyJw>vWM`i_tQ&4s2iH+}(Cmn(*an zGbyumu`lS}UrtlqgEuuw&Km<&^=WIQBfn~vE}HoRjbAm7!t2b?197gfIDIWVVkda_ zx)ppiPX}~zFR?++ReWj~gYxL>wS;cle}AKO{I{n4Hjw1Mwdmk3IIuu5k^pilFgF}H z&IT0cg#q%t1?cB(TLE2gqQYfR%BNpGw;S9cfmMtY>D4EKMUW^_r_W)q9|e5xv9=c0 z*3fyw;LO?|<$*DonS@dwdeF*QNC|yiNA18tnox|f=RSypgY!$*=(8Gx6^iOvYvqE`=X5Ex%9J!xBnk% CaoLdo literal 37156 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 settings //default baud rate int baud_rate=0; + //check if EEPROM has value - if ( CONFIG::read_buffer(EP_BAUD_RATE, (byte *)&baud_rate , BAUD_LENGH)) + if ( CONFIG::read_buffer(EP_BAUD_RATE, (byte *)&baud_rate , INTEGER_LENGH)&&CONFIG::read_buffer(EP_WEB_PORT, (byte *)&(wifi_config.iweb_port) , INTEGER_LENGH)&&CONFIG::read_buffer(EP_DATA_PORT, (byte *)&(wifi_config.idata_port) , INTEGER_LENGH)) { //check if baud value is one of allowed ones if ( ! (baud_rate==9600 || baud_rate==19200 ||baud_rate==38400 ||baud_rate==57600 ||baud_rate==115200 ||baud_rate==230400) )breset_config=true;//baud rate is incorrect =>reset settings + if (wifi_config.iweb_port<1 ||wifi_config.iweb_port>65001 || wifi_config.idata_port <1 || wifi_config.idata_port >65001)breset_config=true; //out of range =>reset settings + } else breset_config=true;//cannot access to config settings=> reset settings + + //reset is requested if(breset_config) { //update EEPROM with default settings CONFIG::reset_config(); - //use default baud rate + //use default baud rate and ports baud_rate=DEFAULT_BAUD_RATE; + wifi_config.iweb_port=DEFAULT_WEB_PORT; + wifi_config.idata_port=DEFAULT_DATA_PORT; } //setup serial Serial.begin(baud_rate); @@ -77,9 +84,11 @@ void setup() { wifi_config.Setup(); delay(1000); //start interfaces - web_interface.WebServer.begin(); - server.begin(); - server.setNoDelay(true); + web_interface = new WEBINTERFACE_CLASS(wifi_config.iweb_port); + data_server = new WiFiServer (wifi_config.idata_port); + web_interface->WebServer.begin(); + data_server->begin(); + data_server->setNoDelay(true); } @@ -87,31 +96,31 @@ void setup() { void loop() { #ifdef MDNS_FEATURE // Check for any mDNS queries and send responses - wifi_config.mdns.update(); + wifi_config.Updatemdns(); #endif //web requests -web_interface.WebServer.handleClient(); +web_interface->WebServer.handleClient(); //TODO use a method to handle serial also in class and call it instead of this one uint8_t i; //check if there are any new clients - if (server.hasClient()){ + if (data_server->hasClient()){ for(i = 0; i < MAX_SRV_CLIENTS; i++){ //find free/disconnected spot if (!serverClients[i] || !serverClients[i].connected()){ if(serverClients[i]) serverClients[i].stop(); - serverClients[i] = server.available(); + serverClients[i] = data_server->available(); continue; } } //no free/disconnected spot so reject - WiFiClient serverClient = server.available(); + WiFiClient serverClient = data_server->available(); serverClient.stop(); } //check clients for data for(i = 0; i < MAX_SRV_CLIENTS; i++){ if (serverClients[i] && serverClients[i].connected()){ if(serverClients[i].available()){ - //get data from the telnet client and push it to the UART + //get data from the tpc client and push it to the UART while(serverClients[i].available()) Serial.write(serverClients[i].read()); } } @@ -121,7 +130,7 @@ uint8_t i; size_t len = Serial.available(); uint8_t sbuf[len]; Serial.readBytes(sbuf, len); - //push UART data to all connected telnet clients + //push UART data to all connected tcp clients for(i = 0; i < MAX_SRV_CLIENTS; i++){ if (serverClients[i] && serverClients[i].connected()){ serverClients[i].write(sbuf, len); diff --git a/esp8266/webinterface.cpp b/esp8266/webinterface.cpp index 30e11ecb..c5dc1cef 100644 --- a/esp8266/webinterface.cpp +++ b/esp8266/webinterface.cpp @@ -170,7 +170,6 @@ const char ERROR_QUERY[] PROGMEM = "Error in query!!"; const char ERROR_WRITING_CHANGES[] PROGMEM = "Error in writing changes!!"; const char SAVED_CHANGES[] PROGMEM = "Change saved, restarting module..."; const char SUBMIT_ID[] PROGMEM = "SUBMIT"; -const char SYS_1_ID[] PROGMEM = "SYS1"; const char BAUD_RATE_NAME[] PROGMEM = "Baud rate"; const char NETWORK_NAME[] PROGMEM = "Network"; const char SLEEP_MODE_NAME[] PROGMEM = "Sleep Mode"; @@ -180,8 +179,6 @@ const char VALUE_38400[] PROGMEM = "38400"; const char VALUE_57600[] PROGMEM = "57600"; const char VALUE_115200[] PROGMEM = "115200"; const char VALUE_230400[] PROGMEM = "230400"; -const char SYS_2_ID[] PROGMEM = "SYS2"; -const char SYS_3_ID[] PROGMEM = "SYS3"; const char VALUE_SELECTED[] PROGMEM = "selected"; const char AP_1_ID[] PROGMEM = "AP1"; const char AP_2_ID[] PROGMEM = "AP2"; @@ -211,12 +208,19 @@ const char GATEWAY_NAME[] PROGMEM = "Gateway"; const char SUBNET_NAME[] PROGMEM = "Subnet"; const char ERROR_INCORRECT_SSID[] PROGMEM = "Incorrect SSID :only char and digit, no space, limited to 33 char length"; const char ERROR_INCORRECT_PASSWORD[] PROGMEM = "Incorrect password : space not allowed, limited to 8~64 char length
"; +const char ERROR_INCORRECT_PORT[] PROGMEM = "Incorrect port : 1~65000 only
"; +const char ERROR_INCORRECT_PORT2[] PROGMEM = "Incorrect port : web port aannot be same as data port
"; const char ERROR_INCORRECT_IP_FORMAT[] PROGMEM = "Incorrect IP format, should be : xxx.xxx.xxx.xxx
"; const char SHOW_IP_BLOCK[] PROGMEM = "
"; const char HIDE_IP_BLOCK[] PROGMEM = "
"; const char AVAILABLE_APS[] PROGMEM = " AP(s) available"; const char RSSI_NAME[] PROGMEM = "Signal"; const char PROTECTED_NAME[] PROGMEM = "Protected"; +const char WEB_PORT_TITLE[] PROGMEM = "Web port:"; +const char DATA_PORT_TITLE[] PROGMEM = "Data port:"; +const char WEB_PORT_ID[] PROGMEM = "WEBPORT"; +const char DATA_PORT_ID[] PROGMEM = "DATAPORT"; +const char PORT_DESC[]PROGMEM = "1~65000"; #define MSG_SUCCESS(msg) buffer2send+=(PROGMEM2CHAR(ALERT_SUCCESS));buffer2send+=(msg);buffer2send+=(PROGMEM2CHAR(DIV_E)); #define MSG_ERROR(msg) buffer2send+=(PROGMEM2CHAR(ALERT_ERROR));buffer2send+=(msg);buffer2send+=(PROGMEM2CHAR(DIV_E)); @@ -315,10 +319,15 @@ void handle_web_interface_root() String sstatus; struct softap_config apconfig; struct ip_info info; - int istatus; + int istatus; uint8_t mac [WL_MAC_ADDR_LENGTH]; if (wifi_get_opmode()==WIFI_STA ) IP=wifi_config.ip2str(WiFi.localIP()); else IP=wifi_config.ip2str(WiFi.softAPIP()); + if (wifi_config.iweb_port!=80) + { + IP+=":"; + IP+=String(wifi_config.iweb_port); + } buffer2send+=(PROGMEM2CHAR(PAGE_HEAD_1)); buffer2send+=(PROGMEM2CHAR(PAGE_HEAD_2)); //top bar @@ -332,9 +341,12 @@ void handle_web_interface_root() LABEL_UNITS(PROGMEM2CHAR(FREE_MEM_TITLE),String(system_get_free_heap_size()).c_str(),PROGMEM2CHAR(UNIT_OCTET)) LABEL(PROGMEM2CHAR(SDK_VERSION_TITLE),system_get_sdk_version()) #ifdef MDNS_FEATURE - sstatus = PROGMEM2CHAR(HTTP_START); - sstatus+=PROGMEM2CHAR(LOCAL_NAME); - LABEL_UNITS(PROGMEM2CHAR(HTTP_MDNS_NAME),sstatus.c_str(),PROGMEM2CHAR(HTTP_END)) + if (wifi_get_opmode()==WIFI_STA ) + { + sstatus = PROGMEM2CHAR(HTTP_START); + sstatus+=PROGMEM2CHAR(LOCAL_NAME); + LABEL_UNITS(PROGMEM2CHAR(HTTP_MDNS_NAME),sstatus.c_str(),PROGMEM2CHAR(HTTP_END)) + } #endif istatus = wifi_get_phy_mode(); if (istatus==PHY_MODE_11B) sstatus=PROGMEM2CHAR(VALUE_11B); @@ -349,8 +361,14 @@ void handle_web_interface_root() //LABEL(sbuf,"Boot mode: ",String(system_get_boot_mode())) //no meaning so far LABEL(PROGMEM2CHAR(BOOT_VERSION_TITLE),String(system_get_boot_version()).c_str()) istatus=0; - if (!CONFIG::read_buffer(EP_BAUD_RATE, (byte *)&istatus , BAUD_LENGH))istatus=0; + if (!CONFIG::read_buffer(EP_BAUD_RATE, (byte *)&istatus , INTEGER_LENGH))istatus=0; LABEL(PROGMEM2CHAR(BAUD_RATE_TITLE),String(istatus).c_str()) + istatus=0; + if (!CONFIG::read_buffer(EP_WEB_PORT, (byte *)&istatus , INTEGER_LENGH))istatus=0; + LABEL(PROGMEM2CHAR(WEB_PORT_TITLE),String(istatus).c_str()) + istatus=0; + if (!CONFIG::read_buffer(EP_DATA_PORT, (byte *)&istatus , INTEGER_LENGH))istatus=0; + LABEL(PROGMEM2CHAR(DATA_PORT_TITLE),String(istatus).c_str()) buffer2send+=(PROGMEM2CHAR(PANEL_END)); //access point buffer2send+=(PROGMEM2CHAR(PANEL_TOP)); @@ -456,7 +474,7 @@ void handle_web_interface_root() } buffer2send+=(PROGMEM2CHAR(PANEL_END)); buffer2send+=(PROGMEM2CHAR(PAGE_BOTTOM)); - web_interface.WebServer.send(200, "text/html", buffer2send); + web_interface->WebServer.send(200, "text/html", buffer2send); } void handle_web_interface_configSys() @@ -465,20 +483,35 @@ void handle_web_interface_configSys() String buffer2send =""; int istatus=0; byte bflag=0; + char error_display[4]={0,0,0,0}; bool msg_alert_error=false; bool msg_alert_success=false; int ibaud=0; - byte bnetwork=0; + int iweb_port =0; + int idata_port =0; byte bsleepmode=0; //check is it is a submission or a display - if (web_interface.WebServer.hasArg(PROGMEM2CHAR(SUBMIT_ID))) + if (web_interface->WebServer.hasArg(PROGMEM2CHAR(SUBMIT_ID))) { //is there a correct list of values? - if (web_interface.WebServer.hasArg(PROGMEM2CHAR(BAUD_RATE_ID)) && web_interface.WebServer.hasArg(PROGMEM2CHAR(SLEEP_MODE_ID))) + if (web_interface->WebServer.hasArg(PROGMEM2CHAR(BAUD_RATE_ID)) && web_interface->WebServer.hasArg(PROGMEM2CHAR(SLEEP_MODE_ID))&& web_interface->WebServer.hasArg(PROGMEM2CHAR(WEB_PORT_ID))&& web_interface->WebServer.hasArg(PROGMEM2CHAR(DATA_PORT_ID))) { //is each value correct ? - ibaud = atoi(web_interface.WebServer.arg(PROGMEM2CHAR(BAUD_RATE_ID)).c_str()); - bsleepmode = atoi(web_interface.WebServer.arg(PROGMEM2CHAR(SLEEP_MODE_ID)).c_str()); + ibaud = atoi(web_interface->WebServer.arg(PROGMEM2CHAR(BAUD_RATE_ID)).c_str()); + iweb_port = atoi(web_interface->WebServer.arg(PROGMEM2CHAR(WEB_PORT_ID)).c_str()); + idata_port = atoi(web_interface->WebServer.arg(PROGMEM2CHAR(DATA_PORT_ID)).c_str()); + bsleepmode = atoi(web_interface->WebServer.arg(PROGMEM2CHAR(SLEEP_MODE_ID)).c_str()); + if (!(iweb_port>0 && iweb_port<65001) || + !(idata_port>0 && idata_port<65001)) + { + msg_alert_error=true; + smsg=PROGMEM2CHAR(ERROR_INCORRECT_PORT); + } + if (iweb_port== idata_port) + { + msg_alert_error=true; + smsg=PROGMEM2CHAR(ERROR_INCORRECT_PORT2); + } if (!(ibaud==9600 || ibaud==19200|| ibaud==38400|| ibaud==57600|| ibaud==115200|| ibaud==230400) || - !(bsleepmode==NONE_SLEEP_T ||bsleepmode==LIGHT_SLEEP_T ||bsleepmode==MODEM_SLEEP_T )) + !(bsleepmode==NONE_SLEEP_T ||bsleepmode==LIGHT_SLEEP_T ||bsleepmode==MODEM_SLEEP_T )) { msg_alert_error=true; smsg=PROGMEM2CHAR(ERROR_QUERY); @@ -492,7 +525,7 @@ void handle_web_interface_configSys() //if no error apply the changes if (msg_alert_error!=true) { - if(!CONFIG::write_buffer(EP_BAUD_RATE,(const byte *)&ibaud,BAUD_LENGH)||!CONFIG::write_byte(EP_SLEEP_MODE,bsleepmode)) + if(!CONFIG::write_buffer(EP_BAUD_RATE,(const byte *)&ibaud,INTEGER_LENGH)||!CONFIG::write_buffer(EP_WEB_PORT,(const byte *)&iweb_port,INTEGER_LENGH)||!CONFIG::write_buffer(EP_DATA_PORT,(const byte *)&idata_port,INTEGER_LENGH)||!CONFIG::write_byte(EP_SLEEP_MODE,bsleepmode)) { msg_alert_error=true; smsg=PROGMEM2CHAR(ERROR_WRITING_CHANGES); @@ -501,14 +534,27 @@ void handle_web_interface_configSys() if (!msg_alert_error) { msg_alert_success=true; + wifi_config.iweb_port=iweb_port; + wifi_config.idata_port=idata_port; smsg=PROGMEM2CHAR(SAVED_CHANGES); } } } - if (!CONFIG::read_buffer(EP_BAUD_RATE, (byte *)&istatus , BAUD_LENGH))istatus=0; + else + { + if (!CONFIG::read_buffer(EP_BAUD_RATE, (byte *)&ibaud , INTEGER_LENGH))ibaud=DEFAULT_BAUD_RATE; + if (!CONFIG::read_byte(EP_SLEEP_MODE, &bsleepmode ))bsleepmode=DEFAULT_SLEEP_MODE; + if (!CONFIG::read_buffer(EP_WEB_PORT, (byte *)&iweb_port , INTEGER_LENGH))ibaud=DEFAULT_WEB_PORT; + if (!CONFIG::read_buffer(EP_DATA_PORT, (byte *)&idata_port , INTEGER_LENGH))ibaud=DEFAULT_DATA_PORT; + } if (wifi_get_opmode()==WIFI_STA ) stmp=wifi_config.ip2str(WiFi.localIP()); else stmp=wifi_config.ip2str(WiFi.softAPIP()); + if (wifi_config.iweb_port!=80) + { + stmp+=":"; + stmp+=String(wifi_config.iweb_port); + } buffer2send+=(PROGMEM2CHAR(PAGE_HEAD_1)); buffer2send+=(PROGMEM2CHAR(PAGE_HEAD_2)); TOPBAR(stmp.c_str()) @@ -516,43 +562,61 @@ void handle_web_interface_configSys() buffer2send+=(PROGMEM2CHAR(TITLE_SYSTEM)); buffer2send+=(PROGMEM2CHAR(PANEL_START)); buffer2send+=(PROGMEM2CHAR(FORM_START)); - - SELECT_START(PROGMEM2CHAR(SYS_1_ID),PROGMEM2CHAR(BAUD_RATE_NAME),PROGMEM2CHAR(BAUD_RATE_ID)) - if (istatus==9600)stmp = PROGMEM2CHAR(VALUE_SELECTED); + //baud rate + SELECT_START(PROGMEM2CHAR(AP_1_ID),PROGMEM2CHAR(BAUD_RATE_NAME),PROGMEM2CHAR(BAUD_RATE_ID)) + if (ibaud==9600)stmp = PROGMEM2CHAR(VALUE_SELECTED); else stmp=""; OPTION(PROGMEM2CHAR(VALUE_9600), stmp.c_str(),PROGMEM2CHAR(VALUE_9600)) - if (istatus==19200)stmp = PROGMEM2CHAR(VALUE_SELECTED); + if (ibaud==19200)stmp = PROGMEM2CHAR(VALUE_SELECTED); else stmp=""; OPTION(PROGMEM2CHAR(VALUE_19200), stmp.c_str(),PROGMEM2CHAR(VALUE_19200)) - if (istatus==38400)stmp = PROGMEM2CHAR(VALUE_SELECTED); + if (ibaud==38400)stmp = PROGMEM2CHAR(VALUE_SELECTED); else stmp=""; OPTION(PROGMEM2CHAR(VALUE_38400), stmp.c_str(),PROGMEM2CHAR(VALUE_38400)) - if (istatus==57600)stmp = PROGMEM2CHAR(VALUE_SELECTED); + if (ibaud==57600)stmp = PROGMEM2CHAR(VALUE_SELECTED); else stmp=""; OPTION(PROGMEM2CHAR(VALUE_57600), stmp.c_str(),PROGMEM2CHAR(VALUE_57600)) - if (istatus==115200)stmp = PROGMEM2CHAR(VALUE_SELECTED); + if (ibaud==115200)stmp = PROGMEM2CHAR(VALUE_SELECTED); else stmp=""; OPTION(PROGMEM2CHAR(VALUE_115200), stmp.c_str(),PROGMEM2CHAR(VALUE_115200)) - if (istatus==230400)stmp = PROGMEM2CHAR(VALUE_SELECTED); + if (ibaud==230400)stmp = PROGMEM2CHAR(VALUE_SELECTED); else stmp=""; OPTION(PROGMEM2CHAR(VALUE_230400), stmp.c_str(),PROGMEM2CHAR(VALUE_230400)) SELECT_END // buffer2send+=(PROGMEM2CHAR(BR)); - if (!CONFIG::read_byte(EP_SLEEP_MODE, &bflag ))bflag=0; - SELECT_START(PROGMEM2CHAR(SYS_3_ID),PROGMEM2CHAR(SLEEP_MODE_NAME),PROGMEM2CHAR(SLEEP_MODE_ID)) - if (bflag==NONE_SLEEP_T)stmp = PROGMEM2CHAR(VALUE_SELECTED); + //sleep mode + SELECT_START(PROGMEM2CHAR(AP_2_ID),PROGMEM2CHAR(SLEEP_MODE_NAME),PROGMEM2CHAR(SLEEP_MODE_ID)) + if (bsleepmode==NONE_SLEEP_T)stmp = PROGMEM2CHAR(VALUE_SELECTED); else stmp=""; OPTION(String(NONE_SLEEP_T).c_str(), stmp.c_str(),PROGMEM2CHAR(VALUE_NONE)) - if (bflag==LIGHT_SLEEP_T)stmp = PROGMEM2CHAR(VALUE_SELECTED); + if (bsleepmode==LIGHT_SLEEP_T)stmp = PROGMEM2CHAR(VALUE_SELECTED); else stmp=""; OPTION(String(LIGHT_SLEEP_T).c_str(), stmp.c_str(),PROGMEM2CHAR(VALUE_LIGHT)) - if (bflag==MODEM_SLEEP_T)stmp = PROGMEM2CHAR(VALUE_SELECTED); + if (bsleepmode==MODEM_SLEEP_T)stmp = PROGMEM2CHAR(VALUE_SELECTED); else stmp=""; OPTION(String(MODEM_SLEEP_T).c_str(), stmp.c_str(),PROGMEM2CHAR(VALUE_MODEM)) SELECT_END + //web port + if(error_display[2]==0) + { + INPUT_TEXT( PROGMEM2CHAR(AP_3_ID),PROGMEM2CHAR(WEB_PORT_TITLE), PROGMEM2CHAR(WEB_PORT_ID),PROGMEM2CHAR(PORT_DESC),String(iweb_port).c_str()) + } + else + { + INPUT_TEXT_ERROR( PROGMEM2CHAR(AP_3_ID),PROGMEM2CHAR(WEB_PORT_TITLE), PROGMEM2CHAR(WEB_PORT_ID),PROGMEM2CHAR(PORT_DESC),String(iweb_port).c_str()) + } + //data port + if(error_display[3]==0) + { + INPUT_TEXT( PROGMEM2CHAR(AP_4_ID),PROGMEM2CHAR(DATA_PORT_TITLE), PROGMEM2CHAR(DATA_PORT_ID),PROGMEM2CHAR(PORT_DESC),String(idata_port).c_str()) + } + else + { + INPUT_TEXT_ERROR( PROGMEM2CHAR(AP_4_ID),PROGMEM2CHAR(DATA_PORT_TITLE), PROGMEM2CHAR(DATA_PORT_ID),PROGMEM2CHAR(PORT_DESC),String(idata_port).c_str()) + } if(msg_alert_error) { MSG_ERROR(smsg.c_str()) @@ -567,7 +631,7 @@ void handle_web_interface_configSys() buffer2send+=(PROGMEM2CHAR(FORM_END)); buffer2send+=(PROGMEM2CHAR(PANEL_END)); buffer2send+=(PROGMEM2CHAR(PAGE_BOTTOM)); - web_interface.WebServer.send(200, "text/html", buffer2send); + web_interface->WebServer.send(200, "text/html", buffer2send); if (msg_alert_success && !msg_alert_error) { system_restart(); @@ -599,46 +663,46 @@ void handle_web_interface_configAP() bool msg_alert_success=false; //check is it is a submission or a display smsg=""; - if (web_interface.WebServer.hasArg(PROGMEM2CHAR(SUBMIT_ID))) + if (web_interface->WebServer.hasArg(PROGMEM2CHAR(SUBMIT_ID))) { //is there a correct list of values? - if (web_interface.WebServer.hasArg(PROGMEM2CHAR(SSID_ID)) && web_interface.WebServer.hasArg(PROGMEM2CHAR(PASSWORD_ID))&& web_interface.WebServer.hasArg(PROGMEM2CHAR(NETWORK_ID))&& web_interface.WebServer.hasArg(PROGMEM2CHAR(AUTENTIFICATION_ID))&& web_interface.WebServer.hasArg(PROGMEM2CHAR(IP_NAME))&& web_interface.WebServer.hasArg(PROGMEM2CHAR(GATEWAY_ID))&& web_interface.WebServer.hasArg(PROGMEM2CHAR(SUBNET_ID))&& web_interface.WebServer.hasArg(PROGMEM2CHAR(CHANNEL_ID))) + if (web_interface->WebServer.hasArg(PROGMEM2CHAR(SSID_ID)) && web_interface->WebServer.hasArg(PROGMEM2CHAR(PASSWORD_ID))&& web_interface->WebServer.hasArg(PROGMEM2CHAR(NETWORK_ID))&& web_interface->WebServer.hasArg(PROGMEM2CHAR(AUTENTIFICATION_ID))&& web_interface->WebServer.hasArg(PROGMEM2CHAR(IP_NAME))&& web_interface->WebServer.hasArg(PROGMEM2CHAR(GATEWAY_ID))&& web_interface->WebServer.hasArg(PROGMEM2CHAR(SUBNET_ID))&& web_interface->WebServer.hasArg(PROGMEM2CHAR(CHANNEL_ID))) { //ssid - if (web_interface.WebServer.arg(PROGMEM2CHAR(SSID_ID)).length() > MAX_SSID_LENGH) + if (web_interface->WebServer.arg(PROGMEM2CHAR(SSID_ID)).length() > MAX_SSID_LENGH) { - stmp = web_interface.WebServer.arg(PROGMEM2CHAR(SSID_ID)).substring(0,MAX_SSID_LENGH); + stmp = web_interface->WebServer.arg(PROGMEM2CHAR(SSID_ID)).substring(0,MAX_SSID_LENGH); msg_alert_error=true; error_display[0]=1; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_SSID); - web_interface.urldecode(ssid_buf,stmp.c_str()); + web_interface->urldecode(ssid_buf,stmp.c_str()); } else - web_interface.urldecode(ssid_buf,web_interface.WebServer.arg(PROGMEM2CHAR(SSID_ID)).c_str()); - if (!web_interface.isSSIDValid(ssid_buf)) + web_interface->urldecode(ssid_buf,web_interface->WebServer.arg(PROGMEM2CHAR(SSID_ID)).c_str()); + if (!web_interface->isSSIDValid(ssid_buf)) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_SSID); error_display[0]=1; } - if (web_interface.WebServer.arg(PROGMEM2CHAR(PASSWORD_ID)).length() > MAX_PASSWORD_LENGH) + if (web_interface->WebServer.arg(PROGMEM2CHAR(PASSWORD_ID)).length() > MAX_PASSWORD_LENGH) { - stmp = web_interface.WebServer.arg(PROGMEM2CHAR(PASSWORD_ID)).substring(0,MAX_PASSWORD_LENGH); + stmp = web_interface->WebServer.arg(PROGMEM2CHAR(PASSWORD_ID)).substring(0,MAX_PASSWORD_LENGH); msg_alert_error=true; error_display[0]=2; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_PASSWORD); - web_interface.urldecode(password_buf,stmp.c_str()); + web_interface->urldecode(password_buf,stmp.c_str()); } else - web_interface.urldecode(password_buf,web_interface.WebServer.arg(PROGMEM2CHAR(PASSWORD_ID)).c_str()); + web_interface->urldecode(password_buf,web_interface->WebServer.arg(PROGMEM2CHAR(PASSWORD_ID)).c_str()); Serial.println(password_buf); - if (!web_interface.isPasswordValid(password_buf)) + if (!web_interface->isPasswordValid(password_buf)) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_PASSWORD); error_display[2]=1; } //ssid visible ? - if (web_interface.WebServer.hasArg(PROGMEM2CHAR(VISIBLE_NAME))) + if (web_interface->WebServer.hasArg(PROGMEM2CHAR(VISIBLE_NAME))) { visible_buf=1; } @@ -647,28 +711,28 @@ void handle_web_interface_configAP() visible_buf=0; } //phy mode - phy_mode_buf = atoi(web_interface.WebServer.arg(PROGMEM2CHAR(NETWORK_ID)).c_str()); + phy_mode_buf = atoi(web_interface->WebServer.arg(PROGMEM2CHAR(NETWORK_ID)).c_str()); if (!(phy_mode_buf==PHY_MODE_11B||phy_mode_buf==PHY_MODE_11G) ) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_QUERY); } //channel - channel_buf = atoi(web_interface.WebServer.arg(PROGMEM2CHAR(CHANNEL_ID)).c_str()); + channel_buf = atoi(web_interface->WebServer.arg(PROGMEM2CHAR(CHANNEL_ID)).c_str()); if (channel_buf< 1|| channel_buf>11) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_QUERY); } //authentification - auth_buf = atoi(web_interface.WebServer.arg(PROGMEM2CHAR(AUTENTIFICATION_ID)).c_str()); + auth_buf = atoi(web_interface->WebServer.arg(PROGMEM2CHAR(AUTENTIFICATION_ID)).c_str()); if (!(auth_buf==AUTH_OPEN||auth_buf==AUTH_WEP||auth_buf==AUTH_WPA_PSK||auth_buf==AUTH_WPA2_PSK||auth_buf==AUTH_WPA_WPA2_PSK||auth_buf==AUTH_MAX) ) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_QUERY); } //Static IP ? - if (web_interface.WebServer.hasArg(PROGMEM2CHAR(STATIC_IP_NAME))) + if (web_interface->WebServer.hasArg(PROGMEM2CHAR(STATIC_IP_NAME))) { static_ip_buf=STATIC_IP_MODE; } @@ -678,17 +742,17 @@ void handle_web_interface_configAP() } //IP - if (web_interface.WebServer.arg(PROGMEM2CHAR(IP_NAME)).length() > MAX_SSID_LENGH) + if (web_interface->WebServer.arg(PROGMEM2CHAR(IP_NAME)).length() > MAX_SSID_LENGH) { - stmp = web_interface.WebServer.arg(PROGMEM2CHAR(IP_NAME)).substring(0,MAX_SSID_LENGH); + stmp = web_interface->WebServer.arg(PROGMEM2CHAR(IP_NAME)).substring(0,MAX_SSID_LENGH); msg_alert_error=true; error_display[6]=1; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_IP_FORMAT); - web_interface.urldecode(ip_buf,stmp.c_str()); + web_interface->urldecode(ip_buf,stmp.c_str()); } else - web_interface.urldecode(ip_buf,web_interface.WebServer.arg(PROGMEM2CHAR(IP_NAME)).c_str()); - if (!web_interface.isIPValid(ip_buf)) + web_interface->urldecode(ip_buf,web_interface->WebServer.arg(PROGMEM2CHAR(IP_NAME)).c_str()); + if (!web_interface->isIPValid(ip_buf)) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_IP_FORMAT); @@ -696,34 +760,34 @@ void handle_web_interface_configAP() } //Gateway - if (web_interface.WebServer.arg(PROGMEM2CHAR(GATEWAY_ID)).length() > MAX_SSID_LENGH) + if (web_interface->WebServer.arg(PROGMEM2CHAR(GATEWAY_ID)).length() > MAX_SSID_LENGH) { - stmp = web_interface.WebServer.arg(PROGMEM2CHAR(GATEWAY_ID)).substring(0,MAX_SSID_LENGH); + stmp = web_interface->WebServer.arg(PROGMEM2CHAR(GATEWAY_ID)).substring(0,MAX_SSID_LENGH); msg_alert_error=true; error_display[7]=1; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_IP_FORMAT); - web_interface.urldecode(gw_buf,stmp.c_str()); + web_interface->urldecode(gw_buf,stmp.c_str()); } else - web_interface.urldecode(gw_buf,web_interface.WebServer.arg(PROGMEM2CHAR(GATEWAY_ID)).c_str()); - if (!web_interface.isIPValid(gw_buf)) + web_interface->urldecode(gw_buf,web_interface->WebServer.arg(PROGMEM2CHAR(GATEWAY_ID)).c_str()); + if (!web_interface->isIPValid(gw_buf)) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_IP_FORMAT); error_display[7]=1; } //subnet - if (web_interface.WebServer.arg(PROGMEM2CHAR(SUBNET_ID)).length() > MAX_SSID_LENGH) + if (web_interface->WebServer.arg(PROGMEM2CHAR(SUBNET_ID)).length() > MAX_SSID_LENGH) { - stmp = web_interface.WebServer.arg(PROGMEM2CHAR(SUBNET_ID)).substring(0,MAX_SSID_LENGH); + stmp = web_interface->WebServer.arg(PROGMEM2CHAR(SUBNET_ID)).substring(0,MAX_SSID_LENGH); msg_alert_error=true; error_display[8]=1; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_IP_FORMAT); - web_interface.urldecode(msk_buf,stmp.c_str()); + web_interface->urldecode(msk_buf,stmp.c_str()); } else - web_interface.urldecode(msk_buf,web_interface.WebServer.arg(PROGMEM2CHAR(SUBNET_ID)).c_str()); - if (!web_interface.isIPValid(msk_buf)) + web_interface->urldecode(msk_buf,web_interface->WebServer.arg(PROGMEM2CHAR(SUBNET_ID)).c_str()); + if (!web_interface->isIPValid(msk_buf)) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_IP_FORMAT); @@ -803,6 +867,11 @@ void handle_web_interface_configAP() //display page if (wifi_get_opmode()==WIFI_STA ) stmp=wifi_config.ip2str(WiFi.localIP()); else stmp=wifi_config.ip2str(WiFi.softAPIP()); + if (wifi_config.iweb_port!=80) + { + stmp+=":"; + stmp+=String(wifi_config.iweb_port); + } buffer2send+=(PROGMEM2CHAR(PAGE_HEAD_1)); buffer2send+=(PROGMEM2CHAR(PAGE_HEAD_2)); TOPBAR(stmp.c_str()) @@ -924,7 +993,7 @@ void handle_web_interface_configAP() buffer2send+=(PROGMEM2CHAR(FORM_END)); buffer2send+=(PROGMEM2CHAR(PANEL_END)); buffer2send+=(PROGMEM2CHAR(PAGE_BOTTOM)); - web_interface.WebServer.send(200, "text/html", buffer2send); + web_interface->WebServer.send(200, "text/html", buffer2send); if (msg_alert_success && !msg_alert_error) { system_restart(); @@ -958,53 +1027,53 @@ void handle_web_interface_configSTA() bool msg_alert_success=false; //check is it is a submission or a display smsg=""; - if (web_interface.WebServer.hasArg(PROGMEM2CHAR(SUBMIT_ID))) + if (web_interface->WebServer.hasArg(PROGMEM2CHAR(SUBMIT_ID))) { //is there a correct list of values? - if (web_interface.WebServer.hasArg(PROGMEM2CHAR(SSID_ID)) && web_interface.WebServer.hasArg(PROGMEM2CHAR(PASSWORD_ID))&& web_interface.WebServer.hasArg(PROGMEM2CHAR(NETWORK_ID))&& web_interface.WebServer.hasArg(PROGMEM2CHAR(IP_NAME))&& web_interface.WebServer.hasArg(PROGMEM2CHAR(GATEWAY_ID))&& web_interface.WebServer.hasArg(PROGMEM2CHAR(SUBNET_ID))) + if (web_interface->WebServer.hasArg(PROGMEM2CHAR(SSID_ID)) && web_interface->WebServer.hasArg(PROGMEM2CHAR(PASSWORD_ID))&& web_interface->WebServer.hasArg(PROGMEM2CHAR(NETWORK_ID))&& web_interface->WebServer.hasArg(PROGMEM2CHAR(IP_NAME))&& web_interface->WebServer.hasArg(PROGMEM2CHAR(GATEWAY_ID))&& web_interface->WebServer.hasArg(PROGMEM2CHAR(SUBNET_ID))) { //ssid - if (web_interface.WebServer.arg(PROGMEM2CHAR(SSID_ID)).length() > MAX_SSID_LENGH) + if (web_interface->WebServer.arg(PROGMEM2CHAR(SSID_ID)).length() > MAX_SSID_LENGH) { - stmp = web_interface.WebServer.arg(PROGMEM2CHAR(SSID_ID)).substring(0,MAX_SSID_LENGH); + stmp = web_interface->WebServer.arg(PROGMEM2CHAR(SSID_ID)).substring(0,MAX_SSID_LENGH); msg_alert_error=true; error_display[0]=1; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_SSID); - web_interface.urldecode(ssid_buf,stmp.c_str()); + web_interface->urldecode(ssid_buf,stmp.c_str()); } else - web_interface.urldecode(ssid_buf,web_interface.WebServer.arg(PROGMEM2CHAR(SSID_ID)).c_str()); - if (!web_interface.isSSIDValid(ssid_buf)) + web_interface->urldecode(ssid_buf,web_interface->WebServer.arg(PROGMEM2CHAR(SSID_ID)).c_str()); + if (!web_interface->isSSIDValid(ssid_buf)) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_SSID); error_display[0]=1; } - if (web_interface.WebServer.arg(PROGMEM2CHAR(PASSWORD_ID)).length() > MAX_PASSWORD_LENGH) + if (web_interface->WebServer.arg(PROGMEM2CHAR(PASSWORD_ID)).length() > MAX_PASSWORD_LENGH) { - stmp = web_interface.WebServer.arg(PROGMEM2CHAR(PASSWORD_ID)).substring(0,MAX_PASSWORD_LENGH); + stmp = web_interface->WebServer.arg(PROGMEM2CHAR(PASSWORD_ID)).substring(0,MAX_PASSWORD_LENGH); msg_alert_error=true; error_display[0]=2; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_PASSWORD); - web_interface.urldecode(password_buf,stmp.c_str()); + web_interface->urldecode(password_buf,stmp.c_str()); } else - web_interface.urldecode(password_buf,web_interface.WebServer.arg(PROGMEM2CHAR(PASSWORD_ID)).c_str()); + web_interface->urldecode(password_buf,web_interface->WebServer.arg(PROGMEM2CHAR(PASSWORD_ID)).c_str()); Serial.println(password_buf); - if (!web_interface.isPasswordValid(password_buf)) + if (!web_interface->isPasswordValid(password_buf)) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_PASSWORD); error_display[2]=1; } //phy mode - phy_mode_buf = atoi(web_interface.WebServer.arg(PROGMEM2CHAR(NETWORK_ID)).c_str()); + phy_mode_buf = atoi(web_interface->WebServer.arg(PROGMEM2CHAR(NETWORK_ID)).c_str()); if (!(phy_mode_buf==PHY_MODE_11B||phy_mode_buf==PHY_MODE_11G ||phy_mode_buf==PHY_MODE_11N)) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_QUERY); } //Static IP ? - if (web_interface.WebServer.hasArg(PROGMEM2CHAR(STATIC_IP_NAME))) + if (web_interface->WebServer.hasArg(PROGMEM2CHAR(STATIC_IP_NAME))) { static_ip_buf=STATIC_IP_MODE; } @@ -1014,17 +1083,17 @@ void handle_web_interface_configSTA() } //IP - if (web_interface.WebServer.arg(PROGMEM2CHAR(IP_NAME)).length() > MAX_SSID_LENGH) + if (web_interface->WebServer.arg(PROGMEM2CHAR(IP_NAME)).length() > MAX_SSID_LENGH) { - stmp = web_interface.WebServer.arg(PROGMEM2CHAR(IP_NAME)).substring(0,MAX_SSID_LENGH); + stmp = web_interface->WebServer.arg(PROGMEM2CHAR(IP_NAME)).substring(0,MAX_SSID_LENGH); msg_alert_error=true; error_display[6]=1; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_IP_FORMAT); - web_interface.urldecode(ip_buf,stmp.c_str()); + web_interface->urldecode(ip_buf,stmp.c_str()); } else - web_interface.urldecode(ip_buf,web_interface.WebServer.arg(PROGMEM2CHAR(IP_NAME)).c_str()); - if (!web_interface.isIPValid(ip_buf)) + web_interface->urldecode(ip_buf,web_interface->WebServer.arg(PROGMEM2CHAR(IP_NAME)).c_str()); + if (!web_interface->isIPValid(ip_buf)) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_IP_FORMAT); @@ -1032,34 +1101,34 @@ void handle_web_interface_configSTA() } //Gateway - if (web_interface.WebServer.arg(PROGMEM2CHAR(GATEWAY_ID)).length() > MAX_SSID_LENGH) + if (web_interface->WebServer.arg(PROGMEM2CHAR(GATEWAY_ID)).length() > MAX_SSID_LENGH) { - stmp = web_interface.WebServer.arg(PROGMEM2CHAR(GATEWAY_ID)).substring(0,MAX_SSID_LENGH); + stmp = web_interface->WebServer.arg(PROGMEM2CHAR(GATEWAY_ID)).substring(0,MAX_SSID_LENGH); msg_alert_error=true; error_display[7]=1; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_IP_FORMAT); - web_interface.urldecode(gw_buf,stmp.c_str()); + web_interface->urldecode(gw_buf,stmp.c_str()); } else - web_interface.urldecode(gw_buf,web_interface.WebServer.arg(PROGMEM2CHAR(GATEWAY_ID)).c_str()); - if (!web_interface.isIPValid(gw_buf)) + web_interface->urldecode(gw_buf,web_interface->WebServer.arg(PROGMEM2CHAR(GATEWAY_ID)).c_str()); + if (!web_interface->isIPValid(gw_buf)) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_IP_FORMAT); error_display[7]=1; } //subnet - if (web_interface.WebServer.arg(PROGMEM2CHAR(SUBNET_ID)).length() > MAX_SSID_LENGH) + if (web_interface->WebServer.arg(PROGMEM2CHAR(SUBNET_ID)).length() > MAX_SSID_LENGH) { - stmp = web_interface.WebServer.arg(PROGMEM2CHAR(SUBNET_ID)).substring(0,MAX_SSID_LENGH); + stmp = web_interface->WebServer.arg(PROGMEM2CHAR(SUBNET_ID)).substring(0,MAX_SSID_LENGH); msg_alert_error=true; error_display[8]=1; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_IP_FORMAT); - web_interface.urldecode(msk_buf,stmp.c_str()); + web_interface->urldecode(msk_buf,stmp.c_str()); } else - web_interface.urldecode(msk_buf,web_interface.WebServer.arg(PROGMEM2CHAR(SUBNET_ID)).c_str()); - if (!web_interface.isIPValid(msk_buf)) + web_interface->urldecode(msk_buf,web_interface->WebServer.arg(PROGMEM2CHAR(SUBNET_ID)).c_str()); + if (!web_interface->isIPValid(msk_buf)) { msg_alert_error=true; smsg+=PROGMEM2CHAR(ERROR_INCORRECT_IP_FORMAT); @@ -1131,6 +1200,11 @@ void handle_web_interface_configSTA() //display page if (wifi_get_opmode()==WIFI_STA ) stmp=wifi_config.ip2str(WiFi.localIP()); else stmp=wifi_config.ip2str(WiFi.softAPIP()); + if (wifi_config.iweb_port!=80) + { + stmp+=":"; + stmp+=String(wifi_config.iweb_port); + } buffer2send+=(PROGMEM2CHAR(PAGE_HEAD_1)); buffer2send+=(PROGMEM2CHAR(PAGE_HEAD_2)); TOPBAR(stmp.c_str()) @@ -1258,7 +1332,7 @@ void handle_web_interface_configSTA() buffer2send+=(PROGMEM2CHAR(FORM_END)); buffer2send+=(PROGMEM2CHAR(PANEL_END)); buffer2send+=(PROGMEM2CHAR(PAGE_BOTTOM)); - web_interface.WebServer.send(200, "text/html", buffer2send); + web_interface->WebServer.send(200, "text/html", buffer2send); if (msg_alert_success && !msg_alert_error) { system_restart(); @@ -1272,10 +1346,15 @@ void handle_not_found() String buffer2send =""; if (wifi_get_opmode()==WIFI_STA ) IP=wifi_config.ip2str(WiFi.localIP()); else IP=wifi_config.ip2str(WiFi.softAPIP()); - buffer2send+=(PROGMEM2CHAR(T404_PAGE)); - buffer2send+=(IP.c_str()); - buffer2send+=(PROGMEM2CHAR(T404_PAGE_2)); - web_interface.WebServer.send(200, "text/html", buffer2send); + if (wifi_config.iweb_port!=80) + { + IP+=":"; + IP+=String(wifi_config.iweb_port); + } + buffer2send+=(PROGMEM2CHAR(T404_PAGE)); + buffer2send+=(IP.c_str()); + buffer2send+=(PROGMEM2CHAR(T404_PAGE_2)); + web_interface->WebServer.send(200, "text/html", buffer2send); } //URI Decoding function @@ -1325,5 +1404,5 @@ WEBINTERFACE_CLASS::WEBINTERFACE_CLASS (int port):WebServer(port) WebServer.onNotFound( handle_not_found); } -WEBINTERFACE_CLASS web_interface(80); +WEBINTERFACE_CLASS * web_interface; diff --git a/esp8266/webinterface.h b/esp8266/webinterface.h index 80f10c0b..192dae57 100644 --- a/esp8266/webinterface.h +++ b/esp8266/webinterface.h @@ -28,7 +28,7 @@ class WEBINTERFACE_CLASS { public: - WEBINTERFACE_CLASS (int port = 82); + WEBINTERFACE_CLASS (int port = 80); ESP8266WebServer WebServer; void urldecode(char *dst, const char *src); bool isSSIDValid(const char * ssid); @@ -38,6 +38,6 @@ class WEBINTERFACE_CLASS }; -extern WEBINTERFACE_CLASS web_interface; +extern WEBINTERFACE_CLASS * web_interface; #endif diff --git a/esp8266/wifi.cpp b/esp8266/wifi.cpp index 3bb28bdc..d26898a0 100644 --- a/esp8266/wifi.cpp +++ b/esp8266/wifi.cpp @@ -83,6 +83,7 @@ bool WIFI_CONFIG::Setup() if (!CONFIG::read_byte(EP_WIFI_MODE, &bflag ) || !CONFIG::read_string(EP_SSID, sbuf , MAX_SSID_LENGH) ||!CONFIG::read_string(EP_PASSWORD, pwd , MAX_PASSWORD_LENGH)) return false; //disconnect if connected WiFi.disconnect(); + current_mode=bflag; //this is AP mode if (bflag==AP_MODE) { @@ -160,5 +161,10 @@ bool WIFI_CONFIG::Setup() #endif return true; } - +#ifdef MDNS_FEATURE +void WIFI_CONFIG::Updatemdns() +{ + if(current_mode==CLIENT_MODE)mdns.update(); +} +#endif WIFI_CONFIG wifi_config; diff --git a/esp8266/wifi.h b/esp8266/wifi.h index f34b23be..67d46e7e 100644 --- a/esp8266/wifi.h +++ b/esp8266/wifi.h @@ -35,7 +35,11 @@ class WIFI_CONFIG // multicast DNS responder feature #ifdef MDNS_FEATURE MDNSResponder mdns; + void Updatemdns(); #endif + byte current_mode; + int iweb_port; + int idata_port; bool Setup(); char * mac2str(uint8_t mac [WL_MAC_ADDR_LENGTH]); char * ip2str(IPAddress Ip );