wifi_scanner.c 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  1. #include <furi.h>
  2. #include <furi_hal_gpio.h>
  3. #include <furi_hal_power.h>
  4. #include <furi_hal_serial_control.h>
  5. #include <furi_hal_serial.h>
  6. #include <gui/canvas_i.h>
  7. #include <gui/gui.h>
  8. #include <input/input.h>
  9. #include <math.h>
  10. #include <notification/notification.h>
  11. #include <notification/notification_messages.h>
  12. #include <stdlib.h>
  13. #include <expansion/expansion.h>
  14. #include "FlipperZeroWiFiModuleDefines.h"
  15. #define WIFI_APP_DEBUG 0
  16. #if WIFI_APP_DEBUG
  17. #define APP_NAME_TAG "WiFi_Scanner"
  18. #define WIFI_APP_LOG_I(format, ...) FURI_LOG_I(APP_NAME_TAG, format, ##__VA_ARGS__)
  19. #define WIFI_APP_LOG_D(format, ...) FURI_LOG_D(APP_NAME_TAG, format, ##__VA_ARGS__)
  20. #define WIFI_APP_LOG_E(format, ...) FURI_LOG_E(APP_NAME_TAG, format, ##__VA_ARGS__)
  21. #else
  22. #define WIFI_APP_LOG_I(format, ...)
  23. #define WIFI_APP_LOG_D(format, ...)
  24. #define WIFI_APP_LOG_E(format, ...)
  25. #endif // WIFI_APP_DEBUG
  26. #define ENABLE_MODULE_POWER 1
  27. #define ENABLE_MODULE_DETECTION 1
  28. #define ANIMATION_TIME 350
  29. typedef enum EChunkArrayData {
  30. EChunkArrayData_Context = 0,
  31. EChunkArrayData_SSID,
  32. EChunkArrayData_EncryptionType,
  33. EChunkArrayData_RSSI,
  34. EChunkArrayData_BSSID,
  35. EChunkArrayData_Channel,
  36. EChunkArrayData_IsHidden,
  37. EChunkArrayData_CurrentAPIndex,
  38. EChunkArrayData_TotalAps,
  39. EChunkArrayData_ENUM_MAX
  40. } EChunkArrayData;
  41. typedef enum EEventType // app internally defined event types
  42. {
  43. EventTypeKey // flipper input.h type
  44. } EEventType;
  45. typedef struct SPluginEvent {
  46. EEventType m_type;
  47. InputEvent m_input;
  48. } SPluginEvent;
  49. typedef struct EAccessPointDesc {
  50. FuriString* m_accessPointName;
  51. int16_t m_rssi;
  52. FuriString* m_secType;
  53. FuriString* m_bssid;
  54. unsigned short m_channel;
  55. bool m_isHidden;
  56. } EAccessPointDesc;
  57. typedef enum EAppContext {
  58. Undefined,
  59. WaitingForModule,
  60. Initializing,
  61. ScanMode,
  62. MonitorMode,
  63. ScanAnimation,
  64. MonitorAnimation
  65. } EAppContext;
  66. typedef enum EWorkerEventFlags {
  67. WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
  68. WorkerEventStop = (1 << 1),
  69. WorkerEventRx = (1 << 2),
  70. } EWorkerEventFlags;
  71. typedef struct SWiFiScannerApp {
  72. FuriMutex* mutex;
  73. Gui* m_gui;
  74. FuriThread* m_worker_thread;
  75. NotificationApp* m_notification;
  76. FuriStreamBuffer* m_rx_stream;
  77. FuriHalSerialHandle* serial_handle;
  78. bool m_wifiModuleInitialized;
  79. bool m_wifiModuleAttached;
  80. EAppContext m_context;
  81. EAccessPointDesc m_currentAccesspointDescription;
  82. unsigned short m_totalAccessPoints;
  83. unsigned short m_currentIndexAccessPoint;
  84. uint32_t m_prevAnimationTime;
  85. uint32_t m_animationTime;
  86. uint8_t m_animtaionCounter;
  87. } SWiFiScannerApp;
  88. /*
  89. Fontname: -FreeType-Inconsolata LGC-Bold-R-Normal--36-360-72-72-P-176-ISO10646-1
  90. Copyright: Original Roman version created by Raph Levien using his own tools and FontForge. Copyright 2006 Raph Levien. Hellenisation of the Roman font, by Dimosthenis Kaponis, using FontForge. Hellenic glyphs Copyright 2010-2012 Dimosthenis Kaponis. Released under the SIL Open Font License, http://scripts.sil.org/OFL. Cyrillic glyphs added by MihailJP, using FontForge. Cyrillic glyphs Copyright 2012 MihailJP. Released under the SIL Open Font License, http://scripts.sil.org/OFL. Emboldened by MihailJP. Some glyphs modified by Greg Omelaenko, using FontForge.
  91. Glyphs: 95/658
  92. BBX Build Mode: 2
  93. */
  94. const uint8_t u8g2_font_inb27_mr[4414] =
  95. "_\2\5\4\5\6\1\4\6\26%\0\370\33\371\35\371\5e\13\313\21! \13\266\14\366\207\377\377\377"
  96. "\20\0!\30\266\14\366\7`\310\22\353E\42\351\177r|(\220h\240-\222\17\37\42%\266\14v\37"
  97. "d,\62\310\42\203,\62\310\42\203,\62\310\42\203,\62\310\42\203,\62\310\42\203|\370\377\307\1#"
  98. "M\266\14\366\7u\220\261\310 \213\14\262\310 \213\14\262\310 k\20b\36 \343\1\62\36 \3;"
  99. "d\220E\6Yd\220\65\10Y\203\220\365\306\3c<@\306\3\344\220A\26\31d\221A\326 d\15"
  100. "\62\330 \203\15\62\26!\343\303\63\0$A\266\14\366\7\200H\352\251\325\320;$\220P\14\21d\210"
  101. "C\4\31\1\21AX\11\244e.\271\325\64F\202YD\224E\6Yd\20\23\10\31\244\214AD!"
  102. "&\220P\314Cm\251G$\365\341\42\0%I\266\14\366\7\261\34RN!%\21B\226 \205\14\42"
  103. "H!\203\4b\310H\346!\227N \253\204\22I,\221\222d\10F\302Q\15\221\220N\32\304\220@"
  104. "\6)%\220A\12\21d\20R\304\42\204$R\312)\344\224\17/\3&F\266\14\366\7\264\274\304\326"
  105. "b\251\214\222H!\211\24\222H!\212\14\262\310 \214\4\322\220;\16\65E\302!\202\220A\250A\221"
  106. "\22\310 &\15r\16!\250\224bL)\5\225\7Fa\241\224\64\310\61F|x\30\0'\17\266\14"
  107. "\366\7aH\372\357\303\377\177\1\0((\266\14v\14sD\363\216\63\317@\22\13,\221\304\22\211$"
  108. "\261D\42\351\223ER\262\304\42K,\262\212v\261L\241\1)$\266\14\266\26\263D\233\254\305\42K"
  109. ",\222\310\42\351_$\222\304\22I,\260\304\2\313\263\301\22\307\7\7\0*/\266\14\366\207\207\210\244"
  110. "\261PH\11\205\14\62F\61a\10C\36\30\306\261\2\317C\215\10\262\310(\251\20\202\212!h\234\261"
  111. "\2\12\37\376,\0+\33\266\14\366\207\207\210\244\267\36\30\344\201A\36\30\344\201\261\210\244\337\207\277\14"
  112. "\0,\30\266\14\366\207\377\377\300\220%\32hI\42\307\34rH\42\7\15\15\0-\26\266\14\366\207\177"
  113. "\352\201`\36\10\346\201`\36\10\37\376\277\4\0.\21\266\14\366\207\377\377\0\211\6\332\42\371p\22\0"
  114. "/.\266\14\366\33\223\304\22I,\221H\22\211$\221H\22\211$\221H\22K$\261D\22K$\261"
  115. "D\42I$\222D\42\211\34\65|x\17\0\60I\266\14\366\7\266<\324\324b\251\220\202\210!\247\230"
  116. "b\310)\206\30SJA\204\24\22\10!e\10B\10\31\203\20\62(B\306 \224\30\205\20\22\206!"
  117. "\4\31R\214)\245\34b\312!\247\224r\12)\211-\325\20,\37^\7\61\26\266\14\366\7\226@\363"
  118. "NKL\265!\210\244\377\377\367\341y\0\62'\266\14\366\7\325\70\265Xr\247\24s\306)($\42"
  119. "i\261D\22\13\254\277H\311g\36\10\346\201`\36\10\37\36\6\63-\266\14\366\7\324\70\265Xri"
  120. "\230\222\2\42\222\26\313\63,\265\363RS\261\310\42i) \202\304)\207\224r\36jj\265\363\341y"
  121. "\0\64\65\266\14\366\7\232H\22\13\264\336q\310\215@\32\11\204\21A\330\30d\221A\24!$\221B"
  122. "\22)\4\21C\316\3\203<\60\310\3\203<\60\36\221\364}x\32\0\65*\266\14\366\7\361\235\337!"
  123. "\222N\20\306TK\16\25b\316\70%\5T$\275#P)\4\21cJ\61\17\265\245\234\371\360:\0"
  124. "\66>\266\14\366\7\327\70\265Xj\250\224\221\310\11\211H\22K$\243(\22\22z\347\201`L)\246"
  125. "\234R\10*\205$RH\42\205$R\12\42\206\234b\212!\250\220\202\232b+=\363\341q\0\67-"
  126. "\266\14\366\7\360\201`\36\10\346\201`\236$\261D\22K$\222\304\22I,\221H\22K$\222\304\22"
  127. "I\254\42\211U$\261|\370\4\0\70;\266\14\366\7\325\70\265Xj\250\224r\212!\207\356\24C\20"
  128. ")$\225Q\324b\251)U\204I\244\224C\31\202J!\211\24\222H!\211\224r\212)\245\234\207\232"
  129. "Z\355|x\35\0\71\71\266\14\366\7\265\274\304\326b\251\220\202\210!\247\230b\10\42\206 b\10\42"
  130. "\206 b\312\241L\71\205\30\344\251\23\10\33\203H\42\207$*\34\222D)\251%\266\322+\37>:"
  131. "\27\266\14\366\207\77A\242\201\266H>|\210D\3m\221|\70\11\0;\34\266\14\366\207\77A\242\201"
  132. "\266H>|\210D\3\255X$\221C\222HM\361\1\1< \266\14\366\207\271\60G,\357\264\304\360"
  133. "\265\363J<\20\301\23/\210\301\3\215$T|\370\25\0= \266\14\366\207\317=\60\310\3\203<\60"
  134. "\310\3\343\303\352\3\203<\60\310\3\203<\60>\374g\1>\36\266\14\366\207\266\240D\32\210\37<\261"
  135. "\274\323\20\303\332\325P;\257\304\61\305\207\277\6\0\77%\266\14\266\67N\251\206\236\61\245\34r\312\21"
  136. "\211H*\226Hb\201u\221H\352C\226D\3m\221|x\36\0@G\266\14\366\7\266\274\264\32r"
  137. "\247\230b\10\42\205(B\306Ad\24\65\6ac\20\66\306(\204\214\61H!c\14R\310\30\203\24"
  138. "\62\306\250\306\30m\220\301\310 \213\20R\2!d\222YNH.\265\245\234\371\360\70\0A<\266\14"
  139. "\366\7\67PA\307\34\262\304\2\15<\357\70\22H#\201\264!\10#\203,\62\310\32e(RHj"
  140. "\311\235\357\220S\12I\244\220T\6Yd\220E\6Y%\220F><\13\0BD\266\14\366\7\217)"
  141. "\207\36\10\346\201`\310\61\205\240RH\42\205$RH\42\205\240R\310)\346\201`\336\371@\60\4\225"
  142. "BR!D\21B\24!D\21B\24!$\25B\220!\17\210\362@\60\357\264\17\217\3C/\266\14"
  143. "\366\7\327\264\245\32z\306\24S\312)\244$B\210\22\206\304\22\211\244'K,\222\310\22\213\12\247\240"
  144. "a\214)\346\241\246V\63\37\36\7DC\266\14\366\7o\255\226\336y\207\30c\10*\205\240RH\42"
  145. "\205\244B\210\42\204(B\210\42\204(B\210\42\204(B\210\42\204(B\210\42\204\244BH\42\205\240"
  146. "R\10\42\206\30c\336q\250\245\365\341y\0E'\266\14\366\7\357\1Q\36\20\345\1Q\36\20\205H"
  147. "\372\344;\277C$\375\344\3\242< \312\3\242< ><\14\0F\35\266\14\366\7\360\201`\36\10"
  148. "\346\201`\36\10\206H\372\244C>D$\375\357\303W\0G:\266\14\366\7\326\70\245\32z\306\24S"
  149. "\312)\244$a\210$\261D\42i&\15b\322 &\15b\322(\212\20\242\10!\212\220\222H)\210"
  150. "\24c\212y \34\227\30;\37\36\7HM\266\14\366\7\217$RH\42\205$RH\42\205$RH"
  151. "\42\205$RH\42\205$RH\42\205$R\36\20\345\1Q\36\20\345\1QH\42\205$RH\42\205"
  152. "$RH\42\205$RH\42\205$RH\42\205$RH\42\205$\362\341a\0I\24\266\14\366\7\320"
  153. "!_#\222\376\377k\357\374>\274\14\0J\33\266\14\366\7\264\245\236#\222\376\377\222\60%\215BR"
  154. "S\213\245W>|\1\0KI\266\14\366\7\257\244\62H*\204\240R\310)\206\230rH)\210\24\222"
  155. "\10!\212\14\262\210(\213\204\302RKM\261\22\312\42\203,\62\212\42\244$RH\42\245 b\312!"
  156. "\247\30r\212!\250\24\222\12!\251\20\242\312\207g\1L\32\266\14\366\7\260D\42\351\377\377\311\7D"
  157. "y@\224\7Dy@|x\27\0MU\266\14\366\7\217,\62\310\42\243\244\62J*\303\34\63\314\61"
  158. "\343\224\63N\71\3\33$\214\61\2\31$\320\6\21#\14A\6\21#\14A\6\31eP\243\14\32!"
  159. "\203\220A\310 \213\14\262\310 \213\14\262\310 \213\14\262\310 \213\14\262\310 \213\14\262\310\207g\1"
  160. "NS\266\14\366\7\217(BJ\42\244$B\14\42\304 B\316!\344\34B\220!\204\4R\10!\201"
  161. "\24B\210 \204\22\204P\203\42dP\204\22\204P\202\20RH \204\230\21\10!\6\21r\16!\347"
  162. "\20\202\14!\310\20\222\12!\251\20\242\10!\212|x\27\0OC\266\14\366\7\325\70\265Xr\247\224"
  163. "r\310)\245 RH*\204(\62\212\42\203,\62\310\42\203,\62\310\42\203,\62\310\42\203,\62\212"
  164. "\42\204(BH*\244 b\310)\246\224\202\134bK\71\363\341u\0P\60\266\14\366\7\257\245w\36"
  165. "\10\346\1Q\10*\205\244B\210\42\204(B\210\42\204(BH*\204\240R\36\20\345\201`\34b\212"
  166. "H\372\357\303g\0QJ\266\14\366\7\325\70\265Xr\247\224r\310)\245 RH*\204\244\62\212\42"
  167. "\203,\62\310\42\203,\62\310\42\203,\62\310\42\203,\62\212\42\204\244BH*\244 b\310)\246\224"
  168. "\202\134b+\271\23\211$R\265\344\20\64\37@\0RC\266\14\366\7\217)\207\336y \30rL!"
  169. "\250\24\222H!\211\24\222H!\211\24\202J!\247\230\7\202y\307!\246H!\211\24\222H)\210\30"
  170. "\202\210)\207v\212!\210\30\202J!\211\24\222\312\207w\1S\61\266\14\366\7\365\264\245\32z\207\230"
  171. "\12\211C\222\70DV\321D\364\262\207\242\221ER&,b\302\42e\244B\214)\345\1a^b\354"
  172. "|x\35\0T\27\266\14\366\7\356\1\62\36 \343\1\62\36 \212H\372\377\377>|UH\266\14\366"
  173. "\7\257$B\210\42\204(B\210\42\204(B\210\42\204(B\210\42\204(B\210\42\204(B\210\42\204"
  174. "(B\210\42\204(B\210\42\204(B\210\42\204(B\210\42\244\240B\12*\245\230b\36rJ\71\363"
  175. "\341u\0V>\266\14\366\7\216\60\42\10#\203(B\210\42\204(R\10\42\206 b\10\42\207\30\202"
  176. "\210!\210\30\222H\31\212\20\242\10!\213\214\301\210 \214\10\322H\30\16{\6Z\221H\42\7\25\37"
  177. ">W\134\266\14\366\7n\22\303\15A\212(D\220\62\10\21\244\14B\4!\204\214A\10!\23)c"
  178. "\20\62\312\30\204\10\63\6!\302\10B\210\70\201\20\42N\30f\204!F\30\346\210\21\206\71\342\230#"
  179. "\216\61\304\30C\12*\244\240B\12\42\206 b\10\42\206\240q\206\22H(\201\304\207\247\1XB\266"
  180. "\14\366\7\217(BH\42\206 b\212!\210\224\202\12!\212\214\242\212 \214\204\302\222;\320\300\22K"
  181. "\64\357<\324\24#\202,\62\212\42\204$R\12\42\206\34rJ)\210\24\222\312(\252|x\26\0Y"
  182. "\60\266\14\366\7\216\254\42\212\42\204\244B\12\42\206 b\212!\210\30\202\12!\212\20\242\212 \214\10"
  183. "\322H\30\16\275\363L,\221H\372\357\303\363\0Z\63\266\14\366\7\360\1Q\36\20\345\1Q\36\10\222"
  184. "D\22K\244b\211$\226H\305\22I,\260D\22\13,\261\300\22\37\20\344\201A\36\30\344\201\361\341"
  185. "]\0[\23\266\14\366n\251\227\210\244\377\377\77\331R\357C\10\0\134+\266\14\66\21\262H\42\213$"
  186. "\262H\42\213$\262Hj\22I&\221d\22Id\221D\26Id\221D\26IM\42\311\14\37\36\1"
  187. "]\20\266\14vn\251'\351\377\377_\352\373\220\2^!\266\14\366\7WL\42I\64\357<\324\210 "
  188. "\213\14\262\10!\211\24\222\210!I\34\361\341\377\17\2_\26\266\14\366\207\377\377\247\36 \343\1\62\36"
  189. " \343\1\362!\3\0`\33\266\14\266\11UPA\307$\222\310\22\213,\261\310\42\251\71Q\361\341\377"
  190. "g\0a\60\266\14\366\207\217\236\266\222C.\15S$\325Rj\347\231\7\202)\207\224\202H!\211\24"
  191. "\202J)\345\230\7\202y \34\25H\62\203|x\30\0b;\266\14\66.\221H\372\215\242HH\350"
  192. "\235\7\202\61\305\224rJ!\251\20\222\12!\212\20\242\10!\212\20\242\10!\212\20\222\12)\210\24c"
  193. "Jy \230w\206Hh\220\362\341u\0c(\266\14\366\207\257\32\247\224;\17\4S\225\202\206!I"
  194. "\230\22\211\244\311\42\211,H\240b\310y \34\247\224\63\37\36\7d;\266\14\366\7\207H\372\251\62"
  195. "\10J\201\234g\36\10\245\30S\10*\244\240BH*\204(B\210\42\204(B\210\42\244\240B\12*"
  196. "\245\234R,\363@\70\17\245@T\31\344\303\273\0e,\266\14\366\207\217\236\266TC\317T\206 b"
  197. "H\42\344\201A\36\30\344\201A\36\30\204H\42\253\24\216\61\344<\344\322r\346\303\343\0f\35\266\14"
  198. "\366\7\302\70\265Xj\250\230\201\312\11\211H\312\71\344cD\322\377\367\341\23\0g=\266\14\366\207o"
  199. "\26R\314\3\241< \10!j\224R\16}\247\224r\314(\251\251\265\324\42a\64\42[z \234\7"
  200. "\202y@\20\222\312 \213\14\262\310(\251\214\7Fy \34\267\220\7h<\266\14v.\221H\372\215"
  201. "\242\210@\210\4u\336\71\244\30c\210)\207\30\202\210!\210\30\202\210!\210\30\202\210!\210\30\202\210"
  202. "!\210\30\202\210!\210\30\202\210!\210\30\202\310\207\207\1i\30\266\14\366\7`\310\22+\71>$S"
  203. "\313I\372\277\326R\357\303\323\0j\37\266\14\366\7d\310\22+\71>\364\230\342I\372\377\227\204)\210"
  204. "\220\202\134bK\71\363A\0k\71\266\14v.\221H\372\241R\310)\206\230rH)\210\220\222\310("
  205. "\212\210\262H(Lck\25Q\24!%\221R\20)\5\21S\16\71\305\20T\12A\206\220d>\274"
  206. "\12\0l\21\266\14vVL'\351\377\377\330;\277\17/\3m@\266\14\366\207\257\221PF!+\234"
  207. "\361\0\31\17\24QF\21\325 \204\10B(A\10%\10\241\4!\224 \204\22\204P\202\20J\20B"
  208. "\11B(A\10%\10\241\4!\224 \204\22\204P\37\236\5n:\266\14\366\207\357\221Q\24\21\10\275"
  209. "\363\316!\305\224CL\71\304\20D\14A\304\20D\14A\304\20D\14A\304\20D\14A\304\20D\14"
  210. "A\304\20D\14A\304\20D><\14\0o\62\266\14\366\207\257\32\247TC\357\224bJ\71\245\220T"
  211. "FIe\220E\6Yd\220E\6Yd\24E\10I\244\224SL)\306<\324\324r\346\303\343\0p"
  212. "=\266\14\366\207\317\221Q\24\11\11=\20\314\3\301\230bJ\71\245\220T\10I\205\20E\10Q\204\20"
  213. "E\10Q\204\20E\10I\205\24D\212\61\245<\20\314;$$DFQD\322\367\301\1q\71\266\14"
  214. "\366\207\217\32AP\12\344<\363@(\305\230BP!\5\25B\24!D\21B\24!D\21B\24!"
  215. "%\221BP)\345\224b\231\7\302y(\5\242\312 \222~\30\0r\37\266\14\366\207\17\22R\22\21"
  216. "\351<\20\314\3\301\34#\216\71\342\224X\42\221\364\277\17\37\1s,\266\14\366\207\217\232\306\222C\317"
  217. "\24C\16A\343\24\24R\211\351)\227\340\221\305\10E\214P\244\224S\312\3\341\270\304\332\371\360:\0"
  218. "t\35\266\14\366\207\234\220\364\332;\277E$\375\223\305\4U\212XL\261\245\232\371\360\66\0u;\266"
  219. "\14\366\207\317\221D\12I\244\220D\12I\244\220D\12I\244\220D\12I\244\220D\12I\244\220D\12"
  220. "I\244\220DJ\71\245\224SL)\306<\20\316C)\20U\6\371\360\60\0v\63\266\14\366\207\257\25"
  221. "E\10Q\204\20E\12I\303\20DL\71\3\21C\20\61$\221\62\24!D\21\62\30\21\204\21A\32"
  222. "\11\303!w\240\201E\22\71>|wH\266\14\366\207\217Q\202\60\42H\31\204\214Q\6!c\224A"
  223. "\310 \243\14j\224A\215\62F\31\303\210Q\206\30a\210QH\230\304($Lb\24\22\204@\306\14"
  224. "s\314\60\307\14sJ)\247\224\202H!\211\24\362\341i\0x\63\266\14\366\207\317\221DJ\71\305\24"
  225. "C\20)D\221QT\21\204%\207\334\201%\232w\36j$\224U\4Q\205\220DJ\71\305\224R\20"
  226. ")$\225\17\357\2y<\266\14\366\207\317\225T\10I\244\220DJA\304\220CC\304\20D\12I\205"
  227. "\20E\310Xd\20F\4aD\14\67\2q\347\35h`\221T$,\24\262\4!L-\325\320+\37"
  228. "\24\0z&\266\14\366\207\357=\20\314\3\301<\20\314\213$\26X\213$\26X\27\13|@\220\7\6"
  229. "y`\220\7\306\207w\1{$\266\14\366\7\342\264\304\324Z\253\300\22\211\244_$\357<\3\15<\262"
  230. "H\372'K,r\255\305\224C\32\0|\16\266\14\366\7\200H\372\377\377\377\357\3}#\266\14\266\66"
  231. "\20\271\324\224,\222\376d\221d\36h\336q\310\221H$\375\213e)\246Xj\347\3\1\0~\31\266"
  232. "\14\366\207/\232\23\214\42d<\60\306\3\303\210\202`\371\360\377\303\0\0\0\0\4\377\377\0";
  233. /*
  234. Fontname: open_iconic_arrow_2x
  235. Copyright: https://github.com/iconic/open-iconic, SIL OPEN FONT LICENSE
  236. Glyphs: 28/28
  237. BBX Build Mode: 0
  238. */
  239. const uint8_t u8g2_font_open_iconic_arrow_2x_t[644] =
  240. "\34\0\4\4\5\5\4\4\6\20\20\0\0\16\0\16\0\0\17\0\0\2g@\17\352i\302$P\376\221"
  241. "\12\64\246\310\11\2A\22O%\303\24Z\360X\242\17^\20\36-<(\0B\22O!\303\32\134\364"
  242. "`\22\17\236\222\35,:\20\0C\17\352)\302$\216\224\31\24\212\4\312\77\2D\37\20\42\302eP"
  243. "\25!\62\205\212\24*a\210#\304E\17\222 F\244P\231\42\245\24\232\2E \20\42\302ePU"
  244. "\11\63E\216\220\71Ah-a\302e\15\241 s\244\310\231\22\246\24\232\2F\37\20\42\302eP\225"
  245. "\211\62G\212\234!\201\310laj\27\221\70C\344H\31\23\245\24\232\2G\37\20\42\302ePU\221"
  246. "\62\205\212\20#A\360haD\34\231(T\244P\31B\244\24\232\2H\21\12.\302C\214\376\215\212"
  247. "$\207\212\15\14\4\0I\23P\341\302\25\134\364`\27\17\36\204xBz\270P\0J\23P\341\302*"
  248. "|\64\221\27\17\36\204p<Zx(\0K\21\12.\302\24pX\241#)\324\20\243\177\3\0L\16"
  249. "\356d\303\340\202\215*t&\311\12\3M\24\307u\302\26J\320\30\42%\36\224\60R\206\320(a\1"
  250. "N\25\307q\302\20L\324 \62EL<(Q\204\314 Q\301\0O\15\356$\303&\226\244\71Tj"
  251. "X\70P\24.%\303!P\4\261D%\212\224Q\205\316$Ya\0Q\20\311m\302&\214P\231:"
  252. "*UW\304D\0R\17\311m\302!\214T=*SG\304\204\1S\24.%\303&\226\244\71Tj"
  253. "\212\224(\224\214\204@\21\0T\23\20\42\302F\230^\252Ci\226\264x\230>x \0U\23\20\42"
  254. "\302\360\201x\230\212&k\22\235J\302\364\14\0V\34\320\241\302\34\36\200\30\27/\204\12\21\32\36\302"
  255. "\240B\204\212x\341F<\200\300\0W\32\220\241\302&\230\354hq\202\304\221\21\306L\14\71A\342D"
  256. "\217%,\14\0X\37\20\42\302\302\210\221@A\2\205K$\314\210\70\42\344\204\30\23$\134BA\2"
  257. "\5q\2\0Y$\17\42\302\34\134\304\270TF\306\214\20\64bH\260\262\203\211\226\34\42$\320\230\21"
  258. "\42H\245+,:\10\0Z\30\220!\303\32\36\200\360\321\4S\61\351\206\324 q\202\4\206\12\17\0"
  259. "[\34\320\241\302`\324X\30q&D-J!&\211\230`a\204\313P\220@A\34\1\0\0\0\4"
  260. "\377\377\0";
  261. /*
  262. Fontname: -Adobe-Courier-Bold-R-Normal--11-80-100-100-M-60-ISO10646-1
  263. Copyright: Copyright (c) 1984, 1987 Adobe Systems Incorporated. All Rights Reserved. Copyright (c) 1988, 1991 Digital Equipment Corporation. All Rights Reserved.
  264. Glyphs: 18/873
  265. BBX Build Mode: 0
  266. */
  267. const uint8_t u8g2_font_courB08_tn[199] =
  268. "\22\0\3\3\3\4\2\3\4\6\11\0\376\6\376\7\377\0\0\0\0\0\252 \5\0\353\0*\11$\357"
  269. "\212P$\241\0+\12-\353\12\206J\301\20\0,\7\233\345\221$\1-\5\15\357(.\5\212\351\20"
  270. "/\14\304\347K\212\205b\241X\14\0\60\12=\351\231Hx\221L\0\61\10>\351\22\21u\62\62\11"
  271. "=\351\231\250\211\264\34\63\14=\351\231\250I\206\24\311\4\0\64\14>\351\223\215\42ZlB\11\0\65"
  272. "\12=\351\270Q\324F\26\0\66\14=\351\231Hh\24\11E\62\1\67\13=\351\270\310D\62\221L\4"
  273. "\70\15=\351\231H(\222\211\204\42\231\0\71\14=\351\231H(\22\32E\62\1:\6\242\351\20\12\0"
  274. "\0\0\4\377\377\0";
  275. /*
  276. Fontname: -Misc-Fixed-Bold-R-Normal--13-120-75-75-C-70-ISO10646-1
  277. Copyright: Public domain font. Share and enjoy.
  278. Glyphs: 95/1003
  279. BBX Build Mode: 0
  280. */
  281. const uint8_t u8g2_font_7x13B_tr[1083] =
  282. "_\0\3\3\3\4\3\5\4\6\15\0\376\11\376\11\0\1}\2\330\4\36 \5\0\356\7!\7J\303"
  283. "\307\241D\42\10\235\332\207\204E\0#\20\315\302OR$r\230\244\34&I\221\10\0$\17N\302\227"
  284. "\214\22\321F\223\250Dh\42\0%\17N\302\307H\22\251\4e\212\221JD\64&\17N\302\317H\242"
  285. "\247\221$\62\251\210&\1'\7\42\327\307!\0(\14\314\302\227D$\21\251\211d\2)\15\314\302\207"
  286. "L$\23\251\210$\42\0*\15\66\306O(&:\224d\241\10\0+\13\66\306\227Pt(\11E\0"
  287. ",\10\244\276\317\212\22\0-\6\16\316\207\1.\10\234\276\217\204\42\1/\14N\302\247\232P\246(\23"
  288. "\12\1\60\16N\302\227,\24\21\361$\11\305D\0\61\13N\302\227l\24\21\352\311\0\62\16N\302\17"
  289. "ED\22\212F\62\241\320\0\63\15N\302\207Q\246F\25\222$\24\0\64\15N\302\247lD\221\220H"
  290. "\207\240\2\65\16N\302\307!(\254\210\204B\222\204\2\66\16N\302\17ED\24VDL\22\12\0\67"
  291. "\15N\302\207QM(\23\312\204\62\0\70\16N\302\17E\304$\241\210\230$\24\0\71\16N\302\17E"
  292. "\304$)\12I\22\12\0:\14\304\276\217\204\42\207I(\22\0;\13\304\276\217\204\42\236L\224\0<"
  293. "\10N\302\247LW\35=\7&\312\207\35j>\11N\302\207T\67\35\1\77\16N\302\17ED\22\212"
  294. "fr\230P\4@\17N\302\17%\266R\211L\252\61\11\5\0A\13N\302\17E\304t\30q\22B"
  295. "\14N\302GE\304t\21\61]\0C\13N\302\17ED\324\223\204\2D\12N\302GE\304O\27\0"
  296. "E\13N\302\307!\250X\21*\32F\13N\302\307!\250X\21j\4G\14N\302\17EDT)\61"
  297. "I\12H\13N\302\207\210\323a\304I\0I\11N\302\207I\250O\6J\12N\302\247>\222$\24\0"
  298. "K\17N\302\207lD\221\220f$\211\22-\0L\10N\302\207P\77\32M\13N\302Gpt\70\210"
  299. "x\22N\15N\302\207\210T\251\34&M$\1O\13N\302\17E\304O\22\12\0P\13N\302GE"
  300. "\304t\21j\4Q\14V\276\17E\304S\205\62\241\12R\15N\302GE\304t!I\224h\1S\16"
  301. "N\302\17ED\224R\205$\11\5\0T\11N\302\207I\250\237\0U\12N\302\207\210\77I(\0V"
  302. "\15N\302\207\210I\22\312D\23*\1W\13N\302\207\210\247\303A\64\14X\17N\302Gp$\11\205"
  303. "h\62R\212h\30Y\14N\302\207\210$!\321\204:\1Z\12N\302\207QMG\241\1[\10\314\302"
  304. "\207I\237\10\134\14N\302\207P*\224*J\205\2]\10\314\302\7I\237\14^\11&\326\227\214\42\32"
  305. "\6_\7\26\276\307\241\0`\7\234\336\207L\1a\12\66\302\17Ur\42I\12b\13N\302\207P\261"
  306. "\42\342t\1c\13\66\302\17EDT\222P\0d\12N\302\247\226\23'I\1e\14\66\302\17Et"
  307. "\30\212$\24\0f\14N\302\327H\242(\243\11\265\1g\16F\272\317\22IB\221RD\22\12\0h"
  308. "\13N\302\207P\261\42\342I\0i\12N\302\227P\16\32\352dj\14^\272\247:L\250#IB\1"
  309. "k\15N\302\207P\23EB\42I\224\4l\10N\302\317P\77\31m\12\66\302\207Dr\70\61\11n"
  310. "\11\66\302GE\304\223\0o\12\66\302\17E\304IB\1p\13F\272GE\304t\21*\2q\12F"
  311. "\272\317\211IR\324\0r\11\66\302GED\324\10s\15\66\302\17E$\21KD\22\12\0t\14F"
  312. "\302\217PV\22j\21M\0u\11\66\302\207\210'I\1v\13\66\302\207\210IB\242\211\0w\13\66"
  313. "\302\207\210\351p\11E\0x\14\66\302\207\210$!QD$\1y\14F\272\207\210\223\244H\222P\0"
  314. "z\12\66\302\207Q&\222\11\15{\14\314\302OI&\221ID\262\1|\7J\303\307\3\1}\15\314"
  315. "\302\307L$\221Id\242\12\0~\11\36\332\217\350\20\222\0\0\0\0\4\377\377\0";
  316. /////// INIT STATE ///////
  317. static void wifi_scanner_app_init(SWiFiScannerApp* const app) {
  318. app->m_context = Undefined;
  319. app->m_totalAccessPoints = 0;
  320. app->m_currentIndexAccessPoint = 0;
  321. app->m_currentAccesspointDescription.m_accessPointName = furi_string_alloc();
  322. furi_string_set(app->m_currentAccesspointDescription.m_accessPointName, "N/A\n");
  323. app->m_currentAccesspointDescription.m_channel = 0;
  324. app->m_currentAccesspointDescription.m_bssid = furi_string_alloc();
  325. furi_string_set(app->m_currentAccesspointDescription.m_bssid, "N/A\n");
  326. app->m_currentAccesspointDescription.m_secType = furi_string_alloc();
  327. furi_string_set(app->m_currentAccesspointDescription.m_secType, "N/A\n");
  328. app->m_currentAccesspointDescription.m_rssi = 0;
  329. app->m_currentAccesspointDescription.m_isHidden = false;
  330. app->m_prevAnimationTime = 0;
  331. app->m_animationTime = ANIMATION_TIME;
  332. app->m_animtaionCounter = 0;
  333. app->m_wifiModuleInitialized = false;
  334. #if ENABLE_MODULE_DETECTION
  335. app->m_wifiModuleAttached = false;
  336. #else
  337. app->m_wifiModuleAttached = true;
  338. #endif
  339. }
  340. int16_t dBmtoPercentage(int16_t dBm) {
  341. const int16_t RSSI_MAX = -50; // define maximum strength of signal in dBm
  342. const int16_t RSSI_MIN = -100; // define minimum strength of signal in dBm
  343. int16_t quality;
  344. if(dBm <= RSSI_MIN) {
  345. quality = 0;
  346. } else if(dBm >= RSSI_MAX) {
  347. quality = 100;
  348. } else {
  349. quality = 2 * (dBm + 100);
  350. }
  351. return quality;
  352. }
  353. void DrawSignalStrengthBar(Canvas* canvas, int rssi, int x, int y, int width, int height) {
  354. int16_t percents = dBmtoPercentage(rssi);
  355. //u8g2_DrawHLine(&canvas->fb, x, y, width);
  356. //u8g2_DrawHLine(&canvas->fb, x, y + height, width);
  357. if(rssi != NA && height > 0) {
  358. uint8_t barHeight = floor((height / 100.f) * percents);
  359. canvas_draw_box(canvas, x, y + height - barHeight, width, barHeight);
  360. }
  361. }
  362. static void wifi_module_render_callback(Canvas* const canvas, void* ctx) {
  363. furi_assert(ctx);
  364. SWiFiScannerApp* app = ctx;
  365. furi_mutex_acquire(app->mutex, FuriWaitForever);
  366. canvas_clear(canvas);
  367. {
  368. switch(app->m_context) {
  369. case Undefined: {
  370. canvas_set_font(canvas, FontPrimary);
  371. const char* strError = "Something wrong";
  372. canvas_draw_str(
  373. canvas,
  374. (128 / 2) - (canvas_string_width(canvas, strError) / 2),
  375. (64 / 2) /* - (canvas_current_font_height(canvas) / 2)*/,
  376. strError);
  377. } break;
  378. case WaitingForModule:
  379. #if ENABLE_MODULE_DETECTION
  380. furi_assert(!app->m_wifiModuleAttached);
  381. if(!app->m_wifiModuleAttached) {
  382. canvas_set_font(canvas, FontSecondary);
  383. const char* strConnectModule = "Attach WiFi scanner module";
  384. canvas_draw_str(
  385. canvas,
  386. (128 / 2) - (canvas_string_width(canvas, strConnectModule) / 2),
  387. (64 / 2) /* - (canvas_current_font_height(canvas) / 2)*/,
  388. strConnectModule);
  389. }
  390. #endif
  391. break;
  392. case Initializing: {
  393. furi_assert(!app->m_wifiModuleInitialized);
  394. if(!app->m_wifiModuleInitialized) {
  395. canvas_set_font(canvas, FontPrimary);
  396. const char* strInitializing = "Initializing...";
  397. canvas_draw_str(
  398. canvas,
  399. (128 / 2) - (canvas_string_width(canvas, strInitializing) / 2),
  400. (64 / 2) - (canvas_current_font_height(canvas) / 2),
  401. strInitializing);
  402. }
  403. } break;
  404. case ScanMode: {
  405. uint8_t offsetY = 0;
  406. uint8_t offsetX = 0;
  407. canvas_draw_frame(canvas, 0, 0, 128, 64);
  408. //canvas_set_font(canvas, FontPrimary);
  409. canvas_set_custom_u8g2_font(canvas, u8g2_font_7x13B_tr);
  410. uint8_t fontHeight = canvas_current_font_height(canvas);
  411. offsetX += 5;
  412. offsetY += fontHeight;
  413. canvas_draw_str(
  414. canvas,
  415. offsetX,
  416. offsetY,
  417. app->m_currentAccesspointDescription.m_isHidden ?
  418. "(Hidden SSID)" :
  419. furi_string_get_cstr(app->m_currentAccesspointDescription.m_accessPointName));
  420. offsetY += fontHeight;
  421. canvas_draw_str(
  422. canvas,
  423. offsetX,
  424. offsetY,
  425. furi_string_get_cstr(app->m_currentAccesspointDescription.m_bssid));
  426. canvas_set_font(canvas, FontSecondary);
  427. //canvas_set_custom_u8g2_font(canvas, u8g2_font_tinytim_tf);
  428. fontHeight = canvas_current_font_height(canvas);
  429. offsetY += fontHeight + 1;
  430. char string[15];
  431. snprintf(
  432. string, sizeof(string), "RSSI: %d", app->m_currentAccesspointDescription.m_rssi);
  433. canvas_draw_str(canvas, offsetX, offsetY, string);
  434. offsetY += fontHeight + 1;
  435. snprintf(
  436. string, sizeof(string), "CHNL: %d", app->m_currentAccesspointDescription.m_channel);
  437. canvas_draw_str(canvas, offsetX, offsetY, string);
  438. offsetY += fontHeight + 1;
  439. snprintf(
  440. string,
  441. sizeof(string),
  442. "ENCR: %s",
  443. furi_string_get_cstr(app->m_currentAccesspointDescription.m_secType));
  444. canvas_draw_str(canvas, offsetX, offsetY, string);
  445. offsetY += fontHeight;
  446. offsetY -= fontHeight;
  447. canvas_set_custom_u8g2_font(canvas, u8g2_font_courB08_tn);
  448. snprintf(
  449. string,
  450. sizeof(string),
  451. "%d/%d",
  452. app->m_currentIndexAccessPoint,
  453. app->m_totalAccessPoints);
  454. offsetX = 128 - canvas_string_width(canvas, string) - 5;
  455. canvas_draw_str(canvas, offsetX, offsetY, string);
  456. canvas_draw_frame(
  457. canvas, offsetX - 6, offsetY - canvas_current_font_height(canvas) - 3, 128, 64);
  458. canvas_set_custom_u8g2_font(canvas, u8g2_font_open_iconic_arrow_2x_t);
  459. if(app->m_currentIndexAccessPoint != app->m_totalAccessPoints) {
  460. //canvas_draw_triangle(canvas, offsetX - 5 - 20, offsetY + 5, 4, 4, CanvasDirectionBottomToTop);
  461. canvas_draw_str(canvas, offsetX - 0 - 35, offsetY + 5, "\x4C");
  462. }
  463. if(app->m_currentIndexAccessPoint != 1) {
  464. //canvas_draw_triangle(canvas, offsetX - 6 - 35, offsetY + 5, 4, 4, CanvasDirectionTopToBottom);
  465. canvas_draw_str(canvas, offsetX - 4 - 20, offsetY + 5, "\x4F");
  466. }
  467. } break;
  468. case MonitorMode: {
  469. uint8_t offsetY = 0;
  470. uint8_t offsetX = 0;
  471. canvas_draw_frame(canvas, 0, 0, 128, 64);
  472. //canvas_set_font(canvas, FontBigNumbers);
  473. //canvas_set_custom_u8g2_font(canvas, u8g2_font_inb27_mr);
  474. canvas_set_custom_u8g2_font(canvas, u8g2_font_inb27_mr);
  475. uint8_t fontHeight = canvas_current_font_height(canvas);
  476. uint8_t fontWidth = canvas_current_font_width(canvas);
  477. if(app->m_currentAccesspointDescription.m_rssi == NA) {
  478. offsetX += floor(128 / 2) - fontWidth - 10;
  479. offsetY += fontHeight - 5;
  480. canvas_draw_str(canvas, offsetX, offsetY, "N/A");
  481. } else {
  482. offsetX += floor(128 / 2) - 2 * fontWidth;
  483. offsetY += fontHeight - 5;
  484. char rssi[8];
  485. snprintf(rssi, sizeof(rssi), "%d", app->m_currentAccesspointDescription.m_rssi);
  486. canvas_draw_str(canvas, offsetX, offsetY, rssi);
  487. }
  488. //canvas_set_font(canvas, FontPrimary);
  489. canvas_set_custom_u8g2_font(canvas, u8g2_font_7x13B_tr);
  490. fontHeight = canvas_current_font_height(canvas);
  491. fontWidth = canvas_current_font_width(canvas);
  492. offsetX = 5;
  493. offsetY = 64 - 7 - fontHeight;
  494. canvas_draw_str(
  495. canvas,
  496. offsetX,
  497. offsetY,
  498. furi_string_get_cstr(app->m_currentAccesspointDescription.m_accessPointName));
  499. offsetY += fontHeight + 2;
  500. canvas_draw_str(
  501. canvas,
  502. offsetX,
  503. offsetY,
  504. furi_string_get_cstr(app->m_currentAccesspointDescription.m_bssid));
  505. DrawSignalStrengthBar(
  506. canvas, app->m_currentAccesspointDescription.m_rssi, 5, 5, 12, 25);
  507. DrawSignalStrengthBar(
  508. canvas, app->m_currentAccesspointDescription.m_rssi, 128 - 5 - 12, 5, 12, 25);
  509. } break;
  510. case ScanAnimation: {
  511. uint32_t currentTime = furi_get_tick();
  512. if(currentTime - app->m_prevAnimationTime > app->m_animationTime) {
  513. app->m_prevAnimationTime = currentTime;
  514. app->m_animtaionCounter += 1;
  515. app->m_animtaionCounter = app->m_animtaionCounter % 3;
  516. }
  517. uint8_t offsetX = 10;
  518. uint8_t mutliplier = 2;
  519. for(uint8_t i = 0; i < 3; ++i) {
  520. canvas_draw_disc(
  521. canvas,
  522. offsetX + 30 + 25 * i,
  523. 64 / 2 - 7,
  524. 5 * (app->m_animtaionCounter == i ? mutliplier : 1));
  525. }
  526. canvas_set_custom_u8g2_font(canvas, u8g2_font_7x13B_tr);
  527. //canvas_set_font(canvas, FontPrimary);
  528. const char* message = "Scanning";
  529. canvas_draw_str(
  530. canvas, 128 / 2 - canvas_string_width(canvas, message) / 2, 55, message);
  531. } break;
  532. case MonitorAnimation: {
  533. uint32_t currentTime = furi_get_tick();
  534. if(currentTime - app->m_prevAnimationTime > app->m_animationTime) {
  535. app->m_prevAnimationTime = currentTime;
  536. app->m_animtaionCounter += 1;
  537. app->m_animtaionCounter = app->m_animtaionCounter % 2;
  538. }
  539. uint8_t offsetX = 10;
  540. uint8_t mutliplier = 2;
  541. canvas_draw_disc(
  542. canvas,
  543. offsetX + 30,
  544. 64 / 2 - 7,
  545. 5 * (app->m_animtaionCounter == 0 ? mutliplier : 1));
  546. canvas_draw_disc(
  547. canvas,
  548. offsetX + 55,
  549. 64 / 2 - 7,
  550. 5 * (app->m_animtaionCounter == 1 ? mutliplier : 1));
  551. canvas_draw_disc(
  552. canvas,
  553. offsetX + 80,
  554. 64 / 2 - 7,
  555. 5 * (app->m_animtaionCounter == 0 ? mutliplier : 1));
  556. canvas_set_custom_u8g2_font(canvas, u8g2_font_7x13B_tr);
  557. //canvas_set_font(canvas, FontPrimary);
  558. const char* message = "Monitor Mode";
  559. canvas_draw_str(
  560. canvas, 128 / 2 - canvas_string_width(canvas, message) / 2, 55, message);
  561. } break;
  562. default:
  563. break;
  564. }
  565. }
  566. furi_mutex_release(app->mutex);
  567. }
  568. static void wifi_module_input_callback(InputEvent* input_event, void* ctx) {
  569. furi_assert(ctx);
  570. FuriMessageQueue* event_queue = ctx;
  571. SPluginEvent event = {.m_type = EventTypeKey, .m_input = *input_event};
  572. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  573. }
  574. static void
  575. uart_on_irq_cb(FuriHalSerialHandle* handle, FuriHalSerialRxEvent event, void* context) {
  576. furi_assert(context);
  577. SWiFiScannerApp* app = context;
  578. WIFI_APP_LOG_I("uart_echo_on_irq_cb");
  579. if(event == FuriHalSerialRxEventData) {
  580. uint8_t data = furi_hal_serial_async_rx(handle);
  581. WIFI_APP_LOG_I("ev == UartIrqEventRXNE");
  582. furi_stream_buffer_send(app->m_rx_stream, &data, 1, 0);
  583. furi_thread_flags_set(furi_thread_get_id(app->m_worker_thread), WorkerEventRx);
  584. }
  585. }
  586. static int32_t uart_worker(void* context) {
  587. furi_assert(context);
  588. SWiFiScannerApp* app = context;
  589. furi_mutex_acquire(app->mutex, FuriWaitForever);
  590. if(app == NULL) {
  591. return 1;
  592. }
  593. FuriStreamBuffer* rx_stream = app->m_rx_stream;
  594. furi_mutex_release(app->mutex);
  595. while(true) {
  596. uint32_t events = furi_thread_flags_wait(
  597. WorkerEventStop | WorkerEventRx, FuriFlagWaitAny, FuriWaitForever);
  598. furi_check((events & FuriFlagError) == 0);
  599. if(events & WorkerEventStop) break;
  600. if(events & WorkerEventRx) {
  601. size_t length = 0;
  602. FuriString* receivedString;
  603. receivedString = furi_string_alloc();
  604. do {
  605. uint8_t data[64];
  606. length = furi_stream_buffer_receive(rx_stream, data, 64, 25);
  607. if(length > 0) {
  608. WIFI_APP_LOG_I("Received Data - length: %i", length);
  609. for(uint16_t i = 0; i < length; i++) {
  610. furi_string_push_back(receivedString, data[i]);
  611. }
  612. //notification_message(app->notification, &sequence_set_only_red_255);
  613. }
  614. } while(length > 0);
  615. if(furi_string_size(receivedString) > 0) {
  616. FuriString* chunk;
  617. chunk = furi_string_alloc();
  618. size_t begin = 0;
  619. size_t end = 0;
  620. size_t stringSize = furi_string_size(receivedString);
  621. WIFI_APP_LOG_I("Received string: %s", furi_string_get_cstr(receivedString));
  622. FuriString* chunksArray[EChunkArrayData_ENUM_MAX];
  623. for(uint8_t i = 0; i < EChunkArrayData_ENUM_MAX; ++i) {
  624. chunksArray[i] = furi_string_alloc();
  625. }
  626. uint8_t index = 0;
  627. do {
  628. end = furi_string_search_char(receivedString, '+', begin);
  629. if(end == FURI_STRING_FAILURE) {
  630. end = stringSize;
  631. }
  632. WIFI_APP_LOG_I("size: %i, begin: %i, end: %i", stringSize, begin, end);
  633. furi_string_set_strn(
  634. chunk, &furi_string_get_cstr(receivedString)[begin], end - begin);
  635. WIFI_APP_LOG_I("String chunk: %s", furi_string_get_cstr(chunk));
  636. furi_string_set(chunksArray[index++], chunk);
  637. begin = end + 1;
  638. } while(end < stringSize);
  639. furi_string_free(chunk);
  640. app = context;
  641. furi_mutex_acquire(app->mutex, FuriWaitForever);
  642. if(app == NULL) {
  643. return 1;
  644. }
  645. if(!app->m_wifiModuleInitialized) {
  646. if(furi_string_cmp_str(
  647. chunksArray[EChunkArrayData_Context], MODULE_CONTEXT_INITIALIZATION) ==
  648. 0) {
  649. app->m_wifiModuleInitialized = true;
  650. app->m_context = ScanAnimation;
  651. }
  652. } else {
  653. if(furi_string_cmp_str(
  654. chunksArray[EChunkArrayData_Context], MODULE_CONTEXT_MONITOR) == 0) {
  655. app->m_context = MonitorMode;
  656. } else if(
  657. furi_string_cmp_str(
  658. chunksArray[EChunkArrayData_Context], MODULE_CONTEXT_SCAN) == 0) {
  659. app->m_context = ScanMode;
  660. } else if(
  661. furi_string_cmp_str(
  662. chunksArray[EChunkArrayData_Context], MODULE_CONTEXT_SCAN_ANIMATION) ==
  663. 0) {
  664. app->m_context = ScanAnimation;
  665. } else if(
  666. furi_string_cmp_str(
  667. chunksArray[EChunkArrayData_Context],
  668. MODULE_CONTEXT_MONITOR_ANIMATION) == 0) {
  669. app->m_context = MonitorAnimation;
  670. }
  671. if(app->m_context == MonitorMode || app->m_context == ScanMode) {
  672. furi_string_set(
  673. app->m_currentAccesspointDescription.m_accessPointName,
  674. chunksArray[EChunkArrayData_SSID]);
  675. furi_string_set(
  676. app->m_currentAccesspointDescription.m_secType,
  677. chunksArray[EChunkArrayData_EncryptionType]);
  678. app->m_currentAccesspointDescription.m_rssi =
  679. atoi(furi_string_get_cstr(chunksArray[EChunkArrayData_RSSI]));
  680. furi_string_set(
  681. app->m_currentAccesspointDescription.m_bssid,
  682. chunksArray[EChunkArrayData_BSSID]);
  683. app->m_currentAccesspointDescription.m_channel =
  684. atoi(furi_string_get_cstr(chunksArray[EChunkArrayData_Channel]));
  685. app->m_currentAccesspointDescription.m_isHidden =
  686. atoi(furi_string_get_cstr(chunksArray[EChunkArrayData_IsHidden]));
  687. app->m_currentIndexAccessPoint = atoi(
  688. furi_string_get_cstr(chunksArray[EChunkArrayData_CurrentAPIndex]));
  689. app->m_totalAccessPoints =
  690. atoi(furi_string_get_cstr(chunksArray[EChunkArrayData_TotalAps]));
  691. }
  692. }
  693. furi_mutex_release(app->mutex);
  694. // Clear string array
  695. for(index = 0; index < EChunkArrayData_ENUM_MAX; ++index) {
  696. furi_string_free(chunksArray[index]);
  697. }
  698. }
  699. furi_string_free(receivedString);
  700. }
  701. }
  702. return 0;
  703. }
  704. typedef enum ESerialCommand {
  705. ESerialCommand_Next,
  706. ESerialCommand_Previous,
  707. ESerialCommand_Scan,
  708. ESerialCommand_MonitorMode,
  709. ESerialCommand_Restart
  710. } ESerialCommand;
  711. void send_serial_command(SWiFiScannerApp* app, ESerialCommand command) {
  712. uint8_t data[1] = {0};
  713. switch(command) {
  714. case ESerialCommand_Next:
  715. data[0] = MODULE_CONTROL_COMMAND_NEXT;
  716. break;
  717. case ESerialCommand_Previous:
  718. data[0] = MODULE_CONTROL_COMMAND_PREVIOUS;
  719. break;
  720. case ESerialCommand_Scan:
  721. data[0] = MODULE_CONTROL_COMMAND_SCAN;
  722. break;
  723. case ESerialCommand_MonitorMode:
  724. data[0] = MODULE_CONTROL_COMMAND_MONITOR;
  725. break;
  726. case ESerialCommand_Restart:
  727. data[0] = MODULE_CONTROL_COMMAND_RESTART;
  728. break;
  729. default:
  730. return;
  731. };
  732. furi_hal_serial_tx(app->serial_handle, data, 1);
  733. }
  734. int32_t wifi_scanner_app(void* p) {
  735. UNUSED(p);
  736. // Disable expansion protocol to avoid interference with UART Handle
  737. Expansion* expansion = furi_record_open(RECORD_EXPANSION);
  738. expansion_disable(expansion);
  739. WIFI_APP_LOG_I("Init");
  740. // FuriTimer* timer = furi_timer_alloc(blink_test_update, FuriTimerTypePeriodic, event_queue);
  741. // furi_timer_start(timer, furi_kernel_get_tick_frequency());
  742. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SPluginEvent));
  743. SWiFiScannerApp* app = malloc(sizeof(SWiFiScannerApp));
  744. wifi_scanner_app_init(app);
  745. #if ENABLE_MODULE_DETECTION
  746. furi_hal_gpio_init(
  747. &gpio_ext_pc0,
  748. GpioModeInput,
  749. GpioPullUp,
  750. GpioSpeedLow); // Connect to the Flipper's ground just to be sure
  751. //furi_hal_gpio_add_int_callback(pinD0, input_isr_d0, this);
  752. app->m_context = WaitingForModule;
  753. #else
  754. app->m_context = Initializing;
  755. #if ENABLE_MODULE_POWER
  756. uint8_t attempts = 0;
  757. while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
  758. furi_hal_power_enable_otg();
  759. furi_delay_ms(10);
  760. }
  761. furi_delay_ms(200);
  762. #endif // ENABLE_MODULE_POWER
  763. #endif // ENABLE_MODULE_DETECTION
  764. app->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  765. if(!app->mutex) {
  766. WIFI_APP_LOG_E("cannot create mutex\r\n");
  767. free(app);
  768. // Return previous state of expansion
  769. expansion_enable(expansion);
  770. furi_record_close(RECORD_EXPANSION);
  771. return 255;
  772. }
  773. WIFI_APP_LOG_I("Mutex created");
  774. app->m_notification = furi_record_open(RECORD_NOTIFICATION);
  775. ViewPort* view_port = view_port_alloc();
  776. view_port_draw_callback_set(view_port, wifi_module_render_callback, app);
  777. view_port_input_callback_set(view_port, wifi_module_input_callback, event_queue);
  778. // Open GUI and register view_port
  779. app->m_gui = furi_record_open(RECORD_GUI);
  780. gui_add_view_port(app->m_gui, view_port, GuiLayerFullscreen);
  781. //notification_message(app->notification, &sequence_set_only_blue_255);
  782. app->m_rx_stream = furi_stream_buffer_alloc(1 * 1024, 1);
  783. app->m_worker_thread = furi_thread_alloc();
  784. furi_thread_set_name(app->m_worker_thread, "WiFiModuleUARTWorker");
  785. furi_thread_set_stack_size(app->m_worker_thread, 1024);
  786. furi_thread_set_context(app->m_worker_thread, app);
  787. furi_thread_set_callback(app->m_worker_thread, uart_worker);
  788. furi_thread_start(app->m_worker_thread);
  789. WIFI_APP_LOG_I("UART thread allocated");
  790. // Enable uart listener
  791. app->serial_handle = furi_hal_serial_control_acquire(UART_CH);
  792. furi_check(app->serial_handle);
  793. furi_hal_serial_init(app->serial_handle, FLIPPERZERO_SERIAL_BAUD);
  794. furi_hal_serial_async_rx_start(app->serial_handle, uart_on_irq_cb, app, false);
  795. WIFI_APP_LOG_I("UART Listener created");
  796. // Because we assume that module was on before we launched the app. We need to ensure that module will be in initial state on app start
  797. send_serial_command(app, ESerialCommand_Restart);
  798. SPluginEvent event;
  799. for(bool processing = true; processing;) {
  800. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  801. furi_mutex_acquire(app->mutex, FuriWaitForever);
  802. #if ENABLE_MODULE_DETECTION
  803. if(!app->m_wifiModuleAttached) {
  804. if(furi_hal_gpio_read(&gpio_ext_pc0) == false) {
  805. WIFI_APP_LOG_I("Module Attached");
  806. app->m_wifiModuleAttached = true;
  807. app->m_context = Initializing;
  808. #if ENABLE_MODULE_POWER
  809. uint8_t attempts2 = 0;
  810. while(!furi_hal_power_is_otg_enabled() && attempts2++ < 3) {
  811. furi_hal_power_enable_otg();
  812. furi_delay_ms(10);
  813. }
  814. #endif
  815. }
  816. }
  817. #endif // ENABLE_MODULE_DETECTION
  818. if(event_status == FuriStatusOk) {
  819. if(event.m_type == EventTypeKey) {
  820. if(app->m_wifiModuleInitialized) {
  821. if(app->m_context == ScanMode) {
  822. switch(event.m_input.key) {
  823. case InputKeyUp:
  824. case InputKeyLeft:
  825. if(event.m_input.type == InputTypeShort) {
  826. WIFI_APP_LOG_I("Previous");
  827. send_serial_command(app, ESerialCommand_Previous);
  828. } else if(event.m_input.type == InputTypeRepeat) {
  829. WIFI_APP_LOG_I("Previous Repeat");
  830. send_serial_command(app, ESerialCommand_Previous);
  831. }
  832. break;
  833. case InputKeyDown:
  834. case InputKeyRight:
  835. if(event.m_input.type == InputTypeShort) {
  836. WIFI_APP_LOG_I("Next");
  837. send_serial_command(app, ESerialCommand_Next);
  838. } else if(event.m_input.type == InputTypeRepeat) {
  839. WIFI_APP_LOG_I("Next Repeat");
  840. send_serial_command(app, ESerialCommand_Next);
  841. }
  842. break;
  843. default:
  844. break;
  845. }
  846. }
  847. switch(event.m_input.key) {
  848. case InputKeyOk:
  849. if(event.m_input.type == InputTypeShort) {
  850. if(app->m_context == ScanMode) {
  851. WIFI_APP_LOG_I("Monitor Mode");
  852. send_serial_command(app, ESerialCommand_MonitorMode);
  853. }
  854. } else if(event.m_input.type == InputTypeLong) {
  855. WIFI_APP_LOG_I("Scan");
  856. send_serial_command(app, ESerialCommand_Scan);
  857. }
  858. break;
  859. case InputKeyBack:
  860. if(event.m_input.type == InputTypeShort) {
  861. switch(app->m_context) {
  862. case MonitorMode:
  863. send_serial_command(app, ESerialCommand_Scan);
  864. break;
  865. case ScanMode:
  866. processing = false;
  867. break;
  868. default:
  869. break;
  870. }
  871. } else if(event.m_input.type == InputTypeLong) {
  872. processing = false;
  873. }
  874. break;
  875. default:
  876. break;
  877. }
  878. } else {
  879. if(event.m_input.key == InputKeyBack) {
  880. if(event.m_input.type == InputTypeShort ||
  881. event.m_input.type == InputTypeLong) {
  882. processing = false;
  883. }
  884. }
  885. }
  886. }
  887. }
  888. #if ENABLE_MODULE_DETECTION
  889. if(app->m_wifiModuleAttached && furi_hal_gpio_read(&gpio_ext_pc0) == true) {
  890. WIFI_APP_LOG_D("Module Disconnected - Exit");
  891. processing = false;
  892. app->m_wifiModuleAttached = false;
  893. app->m_wifiModuleInitialized = false;
  894. }
  895. #endif
  896. furi_mutex_release(app->mutex);
  897. view_port_update(view_port);
  898. }
  899. WIFI_APP_LOG_I("Start exit app");
  900. furi_hal_serial_async_rx_stop(app->serial_handle);
  901. furi_hal_serial_deinit(app->serial_handle);
  902. furi_hal_serial_control_release(app->serial_handle);
  903. furi_thread_flags_set(furi_thread_get_id(app->m_worker_thread), WorkerEventStop);
  904. furi_thread_join(app->m_worker_thread);
  905. furi_thread_free(app->m_worker_thread);
  906. WIFI_APP_LOG_I("Thread Deleted");
  907. // Reset GPIO pins to default state
  908. furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  909. view_port_enabled_set(view_port, false);
  910. gui_remove_view_port(app->m_gui, view_port);
  911. // Close gui record
  912. furi_record_close(RECORD_GUI);
  913. furi_record_close(RECORD_NOTIFICATION);
  914. app->m_gui = NULL;
  915. view_port_free(view_port);
  916. furi_message_queue_free(event_queue);
  917. furi_stream_buffer_free(app->m_rx_stream);
  918. furi_mutex_free(app->mutex);
  919. // Free rest
  920. free(app);
  921. WIFI_APP_LOG_I("App freed");
  922. #if ENABLE_MODULE_POWER
  923. if(furi_hal_power_is_otg_enabled()) {
  924. furi_hal_power_disable_otg();
  925. }
  926. #endif
  927. // Return previous state of expansion
  928. expansion_enable(expansion);
  929. furi_record_close(RECORD_EXPANSION);
  930. return 0;
  931. }