├── Board ├── Board.cop └── Board.pdf ├── Firmware ├── src │ ├── defs.h │ ├── OccupationProcess.h │ ├── Process.h │ ├── ScanProcess.h │ ├── images.h │ ├── ConnectProcess.h │ ├── OccupationProcess.cpp │ ├── ScanHostsProcess.h │ ├── ScanProcess.cpp │ ├── main.cpp │ ├── ConnectProcess.cpp │ └── ScanHostsProcess.cpp ├── images │ ├── iWiFi.xcf │ ├── sdcard.xcf │ ├── battery_full.xcf │ ├── iWiFi.h │ ├── sdcard.h │ ├── battery_03.h │ ├── battery_13.h │ ├── battery_23.h │ └── battery_33.h ├── lib │ ├── InputOutput │ │ ├── Input.cpp │ │ ├── Input.h │ │ ├── Output.h │ │ ├── Output.cpp │ │ ├── AnalogInput.h │ │ ├── AnalogInput.cpp │ │ ├── Button.h │ │ └── Button.cpp │ ├── Screens │ │ ├── MessageScreen.h │ │ ├── LoadingScreen.h │ │ ├── BarGraphScreen.h │ │ ├── MessageScreen.cpp │ │ ├── LoadingScreen.cpp │ │ ├── Screen.h │ │ ├── TextInputScreen.cpp │ │ ├── ListScreen.h │ │ ├── TextInputScreen.h │ │ ├── BarGraphScreen.cpp │ │ └── ListScreen.cpp │ ├── CredentialCache │ │ ├── CredentialCache.h │ │ └── CredentialCache.cpp │ ├── readme.txt │ ├── ESPPing │ │ ├── Pinger.h │ │ └── Pinger.cpp │ └── LinkedList │ │ └── LinkedList.h ├── platformio.ini └── LICENCE ├── .gitignore └── README.md /Board/Board.cop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangrie158/ESPTool/HEAD/Board/Board.cop -------------------------------------------------------------------------------- /Board/Board.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangrie158/ESPTool/HEAD/Board/Board.pdf -------------------------------------------------------------------------------- /Firmware/src/defs.h: -------------------------------------------------------------------------------- 1 | #define VBAT_PIN A0 2 | 3 | #define ADC_MAX 1024 4 | #define VBAT_MAX 5.9f 5 | -------------------------------------------------------------------------------- /Firmware/images/iWiFi.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangrie158/ESPTool/HEAD/Firmware/images/iWiFi.xcf -------------------------------------------------------------------------------- /Firmware/images/sdcard.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangrie158/ESPTool/HEAD/Firmware/images/sdcard.xcf -------------------------------------------------------------------------------- /Firmware/images/battery_full.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dangrie158/ESPTool/HEAD/Firmware/images/battery_full.xcf -------------------------------------------------------------------------------- /Firmware/images/iWiFi.h: -------------------------------------------------------------------------------- 1 | #define iWiFi_width 8 2 | #define iWiFi_height 8 3 | static unsigned char iWiFi_bits[] = { 4 | 0x3c, 0x42, 0x81, 0x3c, 0x42, 0x00, 0x18, 0x00 }; 5 | -------------------------------------------------------------------------------- /Firmware/images/sdcard.h: -------------------------------------------------------------------------------- 1 | #define sdcard_width 8 2 | #define sdcard_height 8 3 | static unsigned char sdcard_bits[] = { 4 | 0x7f, 0x55, 0x55, 0x81, 0x81, 0x81, 0x81, 0xff }; 5 | -------------------------------------------------------------------------------- /Firmware/images/battery_03.h: -------------------------------------------------------------------------------- 1 | #define battery_03_width 16 2 | #define battery_03_height 8 3 | static unsigned char battery_03_bits[] = { 4 | 0xfe, 0xff, 0x02, 0x80, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x03, 0x80, 5 | 0x02, 0x80, 0xfe, 0xff }; 6 | -------------------------------------------------------------------------------- /Firmware/images/battery_13.h: -------------------------------------------------------------------------------- 1 | #define battery_13_width 16 2 | #define battery_13_height 8 3 | static unsigned char battery_13_bits[] = { 4 | 0xfe, 0xff, 0x02, 0x80, 0x03, 0xb8, 0x01, 0xb8, 0x01, 0xb8, 0x03, 0xb8, 5 | 0x02, 0x80, 0xfe, 0xff }; 6 | -------------------------------------------------------------------------------- /Firmware/images/battery_23.h: -------------------------------------------------------------------------------- 1 | #define battery_23_width 16 2 | #define battery_23_height 8 3 | static unsigned char battery_23_bits[] = { 4 | 0xfe, 0xff, 0x02, 0x80, 0x83, 0xbb, 0x81, 0xbb, 0x81, 0xbb, 0x83, 0xbb, 5 | 0x02, 0x80, 0xfe, 0xff }; 6 | -------------------------------------------------------------------------------- /Firmware/images/battery_33.h: -------------------------------------------------------------------------------- 1 | #define battery_33_width 16 2 | #define battery_33_height 8 3 | static unsigned char battery_33_bits[] = { 4 | 0xfe, 0xff, 0x02, 0x80, 0xbb, 0xbb, 0xb9, 0xbb, 0xb9, 0xbb, 0xbb, 0xbb, 5 | 0x02, 0x80, 0xfe, 0xff }; 6 | -------------------------------------------------------------------------------- /Firmware/lib/InputOutput/Input.cpp: -------------------------------------------------------------------------------- 1 | #include "Input.h" 2 | 3 | Input::Input(uint8_t pinNumber, bool internalPullupEnabled, bool activeLow) 4 | : mPin(pinNumber), mStatus(false), mActiveLow(activeLow) { 5 | pinMode(this->mPin, internalPullupEnabled ? INPUT_PULLUP : INPUT); 6 | digitalWrite(this->mPin, mActiveLow); 7 | } 8 | 9 | uint16_t Input::read() { 10 | bool state = digitalRead(mPin) ? !mActiveLow : mActiveLow; 11 | return state; 12 | } 13 | -------------------------------------------------------------------------------- /Firmware/lib/InputOutput/Input.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifndef INPUT_H_ 6 | #define INPUT_H_ 7 | 8 | class Input { 9 | protected: 10 | uint8_t mPin; 11 | bool mStatus; 12 | bool mActiveLow; 13 | 14 | public: 15 | Input(uint8_t pinNumber, bool internalPullupEnabled = false, 16 | bool activeLow = false); 17 | 18 | virtual uint16_t read(); 19 | }; 20 | 21 | #endif // INPUT_H_ 22 | -------------------------------------------------------------------------------- /Firmware/lib/InputOutput/Output.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifndef OUPUT_H_ 6 | #define OUPUT_H_ 7 | 8 | class Output { 9 | private: 10 | uint8_t mPin; 11 | bool mStatus; 12 | bool mActiveLow; 13 | 14 | public: 15 | Output(uint8_t pinNumber, bool activeLow = false); 16 | 17 | void switchOn(); 18 | void switchOff(); 19 | void toggle(); 20 | bool getStatus(); 21 | }; 22 | 23 | #endif // OUPUT_H_ 24 | -------------------------------------------------------------------------------- /Firmware/src/OccupationProcess.h: -------------------------------------------------------------------------------- 1 | #ifndef OCCUPATION_PROCESS_H_ 2 | #define OCCUPATION_PROCESS_H_ 3 | 4 | #include "ScanProcess.h" 5 | 6 | // Name of the Process used in all Screens 7 | #define OCCUPATION_PROCESS_NAME "Channel Occupation" 8 | 9 | #define WLAN_NUM_CHANNELS 13 10 | 11 | class OccupationProcess : public ScanProcess { 12 | 13 | public: 14 | OccupationProcess(SSD1306 *display); 15 | 16 | void handleInput(button_t button, status_t action); 17 | void showResult(int numNetworksDiscovered); 18 | }; 19 | 20 | #endif // OCCUPATION_PROCESS_H_ 21 | -------------------------------------------------------------------------------- /Firmware/lib/Screens/MessageScreen.h: -------------------------------------------------------------------------------- 1 | #ifndef _MESSAGESCREEN_H_ 2 | #define _MESSAGESCREEN_H_ 3 | 4 | #include "SSD1306.h" 5 | #include "Screen.h" 6 | #include 7 | 8 | class MessageScreen : public Screen { 9 | private: 10 | String mMessage; 11 | const String mAckMessage; 12 | 13 | public: 14 | MessageScreen(SSD1306 *display, const String message, 15 | const String ackMessage); 16 | ~MessageScreen(); 17 | void draw(); 18 | inline void setMessage(String &message) { mMessage = message; } 19 | }; 20 | 21 | #endif //_MESSAGESCREEN_H_ 22 | -------------------------------------------------------------------------------- /Firmware/platformio.ini: -------------------------------------------------------------------------------- 1 | # 2 | # Project Configuration File 3 | # 4 | # A detailed documentation with the EXAMPLES is located here: 5 | # http://docs.platformio.org/en/latest/projectconf.html 6 | # 7 | 8 | # A sign `#` at the beginning of the line indicates a comment 9 | # Comment lines are ignored. 10 | 11 | # Simple and base environment 12 | # [env:mybaseenv] 13 | # platform = %INSTALLED_PLATFORM_NAME_HERE% 14 | # framework = 15 | # board = 16 | # 17 | # Automatic targets - enable auto-uploading 18 | # targets = upload 19 | 20 | [env:esp12e] 21 | platform = espressif 22 | framework = arduino 23 | board = nodemcuv2 24 | -------------------------------------------------------------------------------- /Firmware/lib/Screens/LoadingScreen.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOADINGSCREEN_H_ 2 | #define _LOADINGSCREEN_H_ 3 | 4 | #include "SSD1306.h" 5 | #include "Screen.h" 6 | #include 7 | 8 | #define INDETERMINATE -1 9 | 10 | class LoadingScreen : public Screen { 11 | private: 12 | int mProgress; 13 | const String mMessage; 14 | long mLastUpdateTime; 15 | int mLastIndeterminateProgress; 16 | 17 | public: 18 | LoadingScreen(SSD1306 *display, const String message); 19 | ~LoadingScreen(); 20 | void draw(); 21 | inline void setProgress(int newProgress) { mProgress = newProgress; } 22 | }; 23 | 24 | #endif //_LOADINGSCREEN_H_ 25 | -------------------------------------------------------------------------------- /Firmware/lib/InputOutput/Output.cpp: -------------------------------------------------------------------------------- 1 | #include "Output.h" 2 | 3 | Output::Output(uint8_t pinNumber, bool activeLow) 4 | : mPin(pinNumber), mStatus(false), mActiveLow(activeLow) { 5 | pinMode(this->mPin, OUTPUT); 6 | digitalWrite(this->mPin, mActiveLow); 7 | } 8 | 9 | void Output::switchOn() { 10 | digitalWrite(this->mPin, !mActiveLow); 11 | this->mStatus = true; 12 | } 13 | 14 | void Output::switchOff() { 15 | digitalWrite(this->mPin, mActiveLow); 16 | this->mStatus = false; 17 | } 18 | 19 | void Output::toggle() { 20 | digitalWrite(this->mPin, this->mStatus ? mActiveLow : !mActiveLow); 21 | this->mStatus = !this->mStatus; 22 | } 23 | 24 | bool Output::getStatus() { return this->mStatus; } 25 | -------------------------------------------------------------------------------- /Firmware/lib/CredentialCache/CredentialCache.h: -------------------------------------------------------------------------------- 1 | #ifndef _CREDENTIALCACHE_H_ 2 | #define _CREDENTIALCACHE_H_ 3 | 4 | #include "Arduino.h" 5 | 6 | #define CREDENTIAL_FOLDER "/cc" 7 | #define FORMAT_MAGIC_FILE "/cc/.magic" 8 | #define FS_FILE_SEPERATOR "/" 9 | #define FILE_TERMINATOR '\r' 10 | 11 | class CredentialCache { 12 | private: 13 | static String getFileName(String ssid); 14 | 15 | public: 16 | static void begin(); 17 | static bool isFormatted(); 18 | static void format(); 19 | static bool hasPassphrase(const String &ssid); 20 | static String getPassphrase(const String &ssid); 21 | static void savePassphrase(const String &ssid, const String &passphrase); 22 | }; 23 | 24 | #endif //_CREDENTIALCACHE_H_ 25 | -------------------------------------------------------------------------------- /Firmware/lib/InputOutput/AnalogInput.h: -------------------------------------------------------------------------------- 1 | #include "Input.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef ANALOG_INPUT_H_ 7 | #define ANALOG_INPUT_H_ 8 | 9 | // 10 bit ADC 10 | #define ADC_MAX 1024 11 | 12 | // default scaling factor is to map the 0-1024 values in a range of 0-1 13 | #define DEFAULT_CONVERSION 1.0f / ADC_MAX 14 | 15 | class AnalogInput : Input { 16 | private: 17 | float mConversionFactor; 18 | uint8_t mNumSamples; 19 | 20 | public: 21 | AnalogInput(uint8_t pinNumber, float conversionFactor = DEFAULT_CONVERSION, 22 | uint8_t numSamples = 1); 23 | 24 | uint16_t read(); 25 | float readConverted(); 26 | }; 27 | 28 | #endif // ANALOG_INPUT_H_ 29 | -------------------------------------------------------------------------------- /Firmware/lib/Screens/BarGraphScreen.h: -------------------------------------------------------------------------------- 1 | #ifndef _BARGRAPHSCREEN_H_ 2 | #define _BARGRAPHSCREEN_H_ 3 | 4 | #include "SSD1306.h" 5 | #include "Screen.h" 6 | #include 7 | 8 | class BarGraphScreen : public Screen { 9 | private: 10 | int mNumBars; 11 | int *mBarValues; 12 | String *mBarTitles; 13 | String mMessage; 14 | 15 | public: 16 | BarGraphScreen(SSD1306 *display, const String message, int numBars); 17 | ~BarGraphScreen(); 18 | void draw(); 19 | inline void setBarValue(int bar, int value) { mBarValues[bar] = value; } 20 | inline void setBarLabel(int bar, String title) { mBarTitles[bar] = title; } 21 | inline int getBarValue(int bar) { return mBarValues[bar]; } 22 | }; 23 | 24 | #endif //_BARGRAPHSCREEN_H_ 25 | -------------------------------------------------------------------------------- /Firmware/lib/InputOutput/AnalogInput.cpp: -------------------------------------------------------------------------------- 1 | #include "AnalogInput.h" 2 | 3 | AnalogInput::AnalogInput(uint8_t pinNumber, float conversionFactor, 4 | uint8_t numSamples) 5 | : Input(pinNumber, false, false), mConversionFactor(conversionFactor), 6 | mNumSamples(numSamples) { 7 | pinMode(this->mPin, INPUT); 8 | } 9 | 10 | uint16_t AnalogInput::read() { 11 | uint32_t sum = 0; 12 | 13 | for (uint8_t i = 0; i < this->mNumSamples; ++i) { 14 | sum += analogRead(this->mPin); 15 | } 16 | 17 | return (uint16_t)(sum / this->mNumSamples); 18 | } 19 | 20 | float AnalogInput::readConverted() { 21 | uint16_t value = this->read(); 22 | float convertedValue = value * this->mConversionFactor; 23 | return convertedValue; 24 | } 25 | -------------------------------------------------------------------------------- /Firmware/lib/InputOutput/Button.h: -------------------------------------------------------------------------------- 1 | #include "Input.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef BUTTON_H_ 7 | #define BUTTON_H_ 8 | 9 | typedef enum { 10 | Open = 0, 11 | Closed, 12 | Held, 13 | Clicked, 14 | Released, 15 | DoubleClicked 16 | } status_t; 17 | 18 | class Button : Input { 19 | private: 20 | volatile status_t mCurrentStatus; 21 | 22 | uint16_t mKeyDownTicks; 23 | uint16_t mDoubleClickTicks; 24 | unsigned long mLastButtonCheck; 25 | bool mDoubleClickEnabled; 26 | 27 | public: 28 | Button(uint8_t pinNumber, bool internalPullupEnabled = false, 29 | bool activeLow = false, bool doubleClickEnabled = false); 30 | 31 | status_t getStatus(); 32 | 33 | void update(); 34 | }; 35 | 36 | #endif // BUTTON_H_ 37 | -------------------------------------------------------------------------------- /Firmware/src/Process.h: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_H_ 2 | #define PROCESS_H_ 3 | 4 | #include "Button.h" 5 | #include "Screen.h" 6 | #include 7 | 8 | typedef enum { Up = 0, Down, Left } button_t; 9 | 10 | class Process { 11 | protected: 12 | Screen *mScreen; 13 | const String mName; 14 | void (*mCallback)(int); 15 | 16 | public: 17 | Process(const String name, Screen *screen) : mName(name), mScreen(screen){}; 18 | virtual ~Process() { delete mScreen; } 19 | virtual void initialize() {} 20 | virtual Process *update() {} 21 | virtual void handleInput(button_t button, status_t Action) {} 22 | inline void setCallback(void (*callback)(int)) { mCallback = callback; } 23 | inline void setScreen(Screen *newScreen) { mScreen = newScreen; } 24 | inline const String getName() { return mName; } 25 | }; 26 | 27 | #endif // PROCESS_H_ 28 | -------------------------------------------------------------------------------- /Firmware/src/ScanProcess.h: -------------------------------------------------------------------------------- 1 | #ifndef SCAN_PROCESS_H_ 2 | #define SCAN_PROCESS_H_ 3 | 4 | #include "Process.h" 5 | 6 | // Name of the Process used in all Screens 7 | #define SCAN_PROCESS_NAME "Scan for Networks" 8 | 9 | // message used on the loading screen 10 | #define SCAN_MESSAGE "scanning..." 11 | 12 | // Identifier for the ListItem to rescan for networks 13 | #define RESCAN "-1" 14 | 15 | class ScanProcess : public Process { 16 | protected: 17 | bool mScanComplete; 18 | SSD1306 *mDisplay; 19 | 20 | void fillList(int); 21 | 22 | public: 23 | ScanProcess(SSD1306 *display); 24 | ~ScanProcess(); 25 | void initialize(); 26 | Process *update(); 27 | virtual void handleInput(button_t button, status_t action); 28 | void startScan(); 29 | virtual void showResult(int numNetworksDiscovered); 30 | }; 31 | 32 | #endif // SCAN_PROCESS_H_ 33 | -------------------------------------------------------------------------------- /Firmware/lib/Screens/MessageScreen.cpp: -------------------------------------------------------------------------------- 1 | #include "MessageScreen.h" 2 | 3 | MessageScreen::MessageScreen(SSD1306 *display, const String message, 4 | const String ackMessage) 5 | : Screen(display), mMessage(message), mAckMessage(ackMessage) {} 6 | 7 | MessageScreen::~MessageScreen() {} 8 | 9 | void MessageScreen::draw() { 10 | this->clear(); 11 | this->mDisplay->setColor(WHITE); 12 | this->mDisplay->setFont(ArialMT_Plain_10); 13 | this->mDisplay->setTextAlignment(TEXT_ALIGN_CENTER_BOTH); 14 | 15 | this->mDisplay->drawString(SCREEN_WIDTH / 2, (DISPLAY_HEIGHT / 2) - 12, 16 | mMessage); 17 | 18 | this->mDisplay->setTextAlignment(TEXT_ALIGN_CENTER); 19 | this->mDisplay->drawString( 20 | SCREEN_WIDTH / 2, (SCREEN_START_Y + SCREEN_HEIGHT) - 12, mAckMessage); 21 | 22 | this->mDisplay->setColor(INVERSE); 23 | this->mDisplay->fillRect( 24 | SCREEN_START_X, (SCREEN_START_Y + SCREEN_HEIGHT) - 12, SCREEN_WIDTH, 12); 25 | } 26 | -------------------------------------------------------------------------------- /Firmware/lib/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for the project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link to executable file. 4 | 5 | The source code of each library should be placed in separate directory, like 6 | "lib/private_lib/[here are source files]". 7 | 8 | For example, see how can be organized `Foo` and `Bar` libraries: 9 | 10 | |--lib 11 | | |--Bar 12 | | | |--docs 13 | | | |--examples 14 | | | |--src 15 | | | |- Bar.c 16 | | | |- Bar.h 17 | | |--Foo 18 | | | |- Foo.c 19 | | | |- Foo.h 20 | | |- readme.txt --> THIS FILE 21 | |- platformio.ini 22 | |--src 23 | |- main.c 24 | 25 | Then in `src/main.c` you should use: 26 | 27 | #include 28 | #include 29 | 30 | // rest H/C/CPP code 31 | 32 | PlatformIO will find your libraries automatically, configure preprocessor's 33 | include paths and build them. 34 | 35 | See additional options for PlatformIO Library Dependency Finder `lib_*`: 36 | 37 | http://docs.platformio.org/en/latest/projectconf.html#lib-install 38 | 39 | -------------------------------------------------------------------------------- /Firmware/LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Daniel Grießhaber 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pioenvs/ 2 | 3 | # Backup files 4 | *.s#? 5 | *.b#? 6 | *.l#? 7 | *.DS_Store 8 | # Eagle project file 9 | # It contains a serial number and references to the file structure 10 | # on your computer. 11 | # comment the following line if you want to have your project file included. 12 | eagle.epf 13 | 14 | #AutoCAD Files 15 | *.dxf 16 | *.bak 17 | 18 | # CAM files 19 | *.$$$ 20 | *.cmp 21 | *.ly2 22 | *.l15 23 | *.sol 24 | *.plc 25 | *.stc 26 | *.sts 27 | *.crc 28 | *.crs 29 | 30 | *.dri 31 | *.drl 32 | *.gpi 33 | *.pls 34 | 35 | *.drd 36 | *.drd.* 37 | 38 | *.info 39 | 40 | *.eps 41 | 42 | 43 | build/ 44 | mkespfsimage/*.o 45 | mkespfsimage/mkespfsimage 46 | webpages.espfs 47 | espfstest/*.o 48 | espfstest/espfstest 49 | html/ 50 | # Object files 51 | *.o 52 | *.ko 53 | *.obj 54 | *.elf 55 | 56 | # Precompiled Headers 57 | *.gch 58 | *.pch 59 | 60 | # Libraries 61 | *.lib 62 | *.a 63 | *.la 64 | *.lo 65 | 66 | # Shared objects (inc. Windows DLLs) 67 | *.dll 68 | *.so 69 | *.so.* 70 | *.dylib 71 | 72 | # Executables 73 | *.exe 74 | *.out 75 | *.app 76 | *.i*86 77 | *.x86_64 78 | *.hex 79 | -------------------------------------------------------------------------------- /Firmware/lib/Screens/LoadingScreen.cpp: -------------------------------------------------------------------------------- 1 | #include "LoadingScreen.h" 2 | 3 | LoadingScreen::LoadingScreen(SSD1306 *display, const String message) 4 | : Screen(display), mMessage(message), mProgress(0), mLastUpdateTime(0), 5 | mLastIndeterminateProgress(0) {} 6 | 7 | LoadingScreen::~LoadingScreen() {} 8 | 9 | void LoadingScreen::draw() { 10 | this->clear(); 11 | this->mDisplay->setColor(WHITE); 12 | this->mDisplay->setFont(ArialMT_Plain_10); 13 | this->mDisplay->setTextAlignment(TEXT_ALIGN_CENTER); 14 | 15 | int progress = mProgress; 16 | 17 | if (mProgress == INDETERMINATE) { 18 | long updateTime = millis(); 19 | if (updateTime - mLastUpdateTime >= 10) { 20 | mLastIndeterminateProgress += 1; 21 | if (mLastIndeterminateProgress > 100) { 22 | mLastIndeterminateProgress = 0; 23 | } 24 | mLastUpdateTime = updateTime; 25 | } 26 | progress = mLastIndeterminateProgress; 27 | } 28 | 29 | this->mDisplay->drawProgressBar(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH - 1, 8, 30 | progress); 31 | this->mDisplay->drawString(SCREEN_WIDTH / 2, (SCREEN_HEIGHT / 2) + 10, 32 | mMessage); 33 | } 34 | -------------------------------------------------------------------------------- /Firmware/lib/Screens/Screen.h: -------------------------------------------------------------------------------- 1 | #ifndef _SCREEN_H_ 2 | #define _SCREEN_H_ 3 | 4 | #include "SSD1306.h" 5 | #include 6 | 7 | #define SCREEN_HEIGHT 50 8 | #define SCREEN_WIDTH 128 9 | #define SCREEN_START_X 0 10 | #define SCREEN_START_Y ((DISPLAY_HEIGHT) - (SCREEN_HEIGHT)) 11 | 12 | class Screen { 13 | private: 14 | protected: 15 | SSD1306 *mDisplay; 16 | 17 | public: 18 | Screen(SSD1306 *display) : mDisplay(display) {} 19 | virtual ~Screen(){}; 20 | virtual void draw() = 0; 21 | inline SSD1306 *getDisplay() { return mDisplay; } 22 | 23 | void clear() { 24 | mDisplay->setColor(BLACK); 25 | mDisplay->fillRect(SCREEN_START_X, SCREEN_START_Y, SCREEN_WIDTH, 26 | SCREEN_HEIGHT); 27 | mDisplay->setColor(WHITE); 28 | } 29 | 30 | inline String trimStringToLength(String input, int maxLength) { 31 | String empty = ""; 32 | if (maxLength <= 0) { 33 | return empty; 34 | } 35 | 36 | while (mDisplay->getStringWidth(input) > maxLength) { 37 | int charsLeft = input.length(); 38 | if (charsLeft <= 4) { 39 | return empty; 40 | } 41 | input.remove(charsLeft - 4); 42 | input += "..."; 43 | } 44 | 45 | return input; 46 | } 47 | }; 48 | 49 | #endif //_SCREEN_H_ 50 | -------------------------------------------------------------------------------- /Firmware/src/images.h: -------------------------------------------------------------------------------- 1 | #define ICON_BATTERY_WIDTH 16 2 | #define ICON_BATTERY_HEIGHT 8 3 | 4 | const char icon_battery_33[] PROGMEM = {0xfe, 0xff, 0x02, 0x80, 0xbb, 0xbb, 5 | 0xb9, 0xbb, 0xb9, 0xbb, 0xbb, 0xbb, 6 | 0x02, 0x80, 0xfe, 0xff}; 7 | const char icon_battery_23[] PROGMEM = {0xfe, 0xff, 0x02, 0x80, 0x83, 0xbb, 8 | 0x81, 0xbb, 0x81, 0xbb, 0x83, 0xbb, 9 | 0x02, 0x80, 0xfe, 0xff}; 10 | const char icon_battery_13[] PROGMEM = {0xfe, 0xff, 0x02, 0x80, 0x03, 0xb8, 11 | 0x01, 0xb8, 0x01, 0xb8, 0x03, 0xb8, 12 | 0x02, 0x80, 0xfe, 0xff}; 13 | const char icon_battery_03[] PROGMEM = {0xfe, 0xff, 0x02, 0x80, 0x03, 0x80, 14 | 0x01, 0x80, 0x01, 0x80, 0x03, 0x80, 15 | 0x02, 0x80, 0xfe, 0xff}; 16 | 17 | #define ICON_SDCARD_WIDTH 8 18 | #define ICON_SDCARD_HEIGHT 8 19 | const char icon_sdcard[] PROGMEM = {0x7f, 0x55, 0x55, 0x81, 20 | 0x81, 0x81, 0x81, 0xff}; 21 | 22 | #define ICON_WIFI_WIDTH 8 23 | #define ICON_WIFI_HEIGHT 8 24 | const char icon_wifi[] PROGMEM = {0x3c, 0x42, 0x81, 0x3c, 25 | 0x42, 0x00, 0x18, 0x00}; 26 | -------------------------------------------------------------------------------- /Firmware/src/ConnectProcess.h: -------------------------------------------------------------------------------- 1 | #ifndef CONNECT_PROCESS_H_ 2 | #define CONNECT_PROCESS_H_ 3 | 4 | #include "ESP8266WiFi.h" 5 | #include "Process.h" 6 | 7 | // Name of the Process used in all Screens 8 | #define CONNECT_PROCESS_NAME "Connect to ..." 9 | 10 | // message used on the loading screen 11 | #define PROMPT_MESSAGE "Enter Key for" 12 | 13 | // message displayed while connecting (the ssid is appended at runtime) 14 | #define CONNECTING_MESSAGE "connecting to" 15 | 16 | typedef enum { 17 | UNKNOWN, 18 | PROMPTING, 19 | CONNECTING, 20 | CONNECTED, 21 | FAILED 22 | } connect_status_t; 23 | 24 | class ConnectProcess : public Process { 25 | private: 26 | bool mDidDelete; 27 | int mWlanNumberFromScan; 28 | connect_status_t mCurrentStatus; 29 | SSD1306 *mDisplay; 30 | 31 | void promptForPassword(); 32 | void connectToWiFi(const String &passphrase); 33 | void showConnectedSummary(); 34 | void showConnectionError(); 35 | 36 | public: 37 | ConnectProcess(SSD1306 *display, int wlanNumberFromScan); 38 | ~ConnectProcess(); 39 | void initialize(); 40 | Process *update(); 41 | void handleInput(button_t button, status_t action); 42 | inline String ipToString(const IPAddress &address) { 43 | return String(address[0]) + "." + String(address[1]) + "." + 44 | String(address[2]) + "." + String(address[3]); 45 | } 46 | }; 47 | 48 | #endif // CONNECT_PROCESS_H_ 49 | -------------------------------------------------------------------------------- /Firmware/lib/CredentialCache/CredentialCache.cpp: -------------------------------------------------------------------------------- 1 | #include "CredentialCache.h" 2 | #include "FS.h" 3 | void CredentialCache::begin() { SPIFFS.begin(); } 4 | 5 | bool CredentialCache::isFormatted() { return SPIFFS.exists(FORMAT_MAGIC_FILE); } 6 | 7 | void CredentialCache::format() { 8 | SPIFFS.format(); 9 | File magic = SPIFFS.open(FORMAT_MAGIC_FILE, "w+"); 10 | magic.print(FORMAT_MAGIC_FILE); 11 | magic.close(); 12 | } 13 | 14 | String CredentialCache::getFileName(String ssid) { 15 | String filePath = String(CREDENTIAL_FOLDER); 16 | filePath += FS_FILE_SEPERATOR; 17 | filePath += ssid; 18 | filePath.replace(" ", ""); 19 | return filePath; 20 | } 21 | 22 | bool CredentialCache::hasPassphrase(const String &ssid) { 23 | String filePath = getFileName(ssid); 24 | return SPIFFS.exists(filePath); 25 | } 26 | 27 | String CredentialCache::getPassphrase(const String &ssid) { 28 | String filePath = getFileName(ssid); 29 | File passFile = SPIFFS.open(filePath, "r"); 30 | 31 | String passPhrase = passFile.readStringUntil(FILE_TERMINATOR); 32 | 33 | passFile.close(); 34 | 35 | return passPhrase; 36 | } 37 | 38 | void CredentialCache::savePassphrase(const String &ssid, 39 | const String &passphrase) { 40 | String filePath = getFileName(ssid); 41 | 42 | File passFile = SPIFFS.open(filePath, "w+"); 43 | 44 | passFile.println(passphrase); 45 | 46 | passFile.close(); 47 | } 48 | -------------------------------------------------------------------------------- /Firmware/src/OccupationProcess.cpp: -------------------------------------------------------------------------------- 1 | #include "OccupationProcess.h" 2 | #include "BarGraphScreen.h" 3 | #include "ESP8266WiFi.h" 4 | 5 | OccupationProcess::OccupationProcess(SSD1306 *display) : ScanProcess(display) {} 6 | 7 | void OccupationProcess::showResult(int numNetworksDiscovered) { 8 | // switch to a new ListScreen to show the result 9 | Screen *oldScreen = this->mScreen; 10 | BarGraphScreen *screen = new BarGraphScreen( 11 | this->mDisplay, OCCUPATION_PROCESS_NAME, WLAN_NUM_CHANNELS); 12 | this->mScreen = screen; 13 | delete oldScreen; 14 | 15 | // fill the list 16 | for (int i = 0; i < numNetworksDiscovered; i++) { 17 | int channel = WiFi.channel(i); 18 | int lastBarValue = screen->getBarValue(channel - 1); 19 | screen->setBarValue(channel - 1, lastBarValue + 1); 20 | } 21 | 22 | // fill in the labels 23 | for (int i = 0; i < WLAN_NUM_CHANNELS; i++) { 24 | screen->setBarLabel(i, String(i + 1)); 25 | } 26 | } 27 | 28 | void OccupationProcess::handleInput(button_t button, status_t action) { 29 | // make sure we're displaying a ListScreen before casting 30 | if (mScanComplete) { 31 | BarGraphScreen *screen = static_cast(this->mScreen); 32 | if (action == Clicked) { 33 | switch (button) { 34 | case Up: 35 | 36 | break; 37 | case Down: 38 | 39 | break; 40 | case Left: 41 | 42 | break; 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Firmware/src/ScanHostsProcess.h: -------------------------------------------------------------------------------- 1 | #ifndef SCAN_HOSTS_PROCESS_H_ 2 | #define SCAN_HOSTS_PROCESS_H_ 3 | 4 | #include "IPAddress.h" 5 | #include "Pinger.h" 6 | #include "Process.h" 7 | #include "ScanProcess.h" 8 | 9 | // Name of the Process used in all Screens 10 | #define SCAN_HOST_PROCESS_NAME "Scan for Hosts" 11 | 12 | // Identifier for the ListItem to rescan for networks 13 | #define CANCEL "-2" 14 | 15 | typedef enum { NOT_STARTED, RUNNING, FINISHED } host_scan_status_t; 16 | 17 | class ScanHostsProcess : public Process { 18 | private: 19 | host_scan_status_t mScanStatus; 20 | ip_addr_t mNetAddress; 21 | ip_addr_t mStartAddress; 22 | ip_addr_t mEndAddress; 23 | ip_addr_t mNetMask; 24 | ip_addr_t mHost; 25 | ip_addr_t mCurrentHost; 26 | SSD1306 *mDisplay; 27 | Pinger *mPinger; 28 | 29 | inline ip_addr_t IPAddress2ip_addr_t(IPAddress ip) { 30 | ip_addr_t retVal; 31 | IP4_ADDR(&retVal, ip[0], ip[1], ip[2], ip[3]); 32 | return retVal; 33 | }; 34 | static void pingerCallback(ip_addr_t ip, u16_t seqNum, bool didRespond, 35 | u32_t responseTime, void *arg); 36 | 37 | public: 38 | ScanHostsProcess(SSD1306 *display); 39 | ~ScanHostsProcess(); 40 | void initialize(); 41 | Process *update(); 42 | virtual void handleInput(button_t button, status_t action); 43 | void startScan(); 44 | void stopScan(); 45 | void showStartScreen(); 46 | void addHost(ip_addr_t ip, u32_t responseTime); 47 | }; 48 | 49 | #endif // SCAN_HOSTS_PROCESS_H_ 50 | -------------------------------------------------------------------------------- /Firmware/lib/Screens/TextInputScreen.cpp: -------------------------------------------------------------------------------- 1 | #include "TextInputScreen.h" 2 | 3 | TextInputScreen::TextInputScreen(SSD1306 *display, const String message) 4 | : Screen(display), mMessage(message), mInput((char)START_CHAR), 5 | mCurrentCharPos(0) {} 6 | 7 | TextInputScreen::~TextInputScreen() {} 8 | 9 | void TextInputScreen::draw() { 10 | this->clear(); 11 | this->mDisplay->setColor(WHITE); 12 | this->mDisplay->setFont(ArialMT_Plain_10); 13 | this->mDisplay->setTextAlignment(TEXT_ALIGN_CENTER); 14 | 15 | this->mDisplay->drawString(SCREEN_WIDTH / 2, SCREEN_START_Y + 20, mMessage); 16 | 17 | // use a progressbar with 0 progress to easily draw a nice frame 18 | this->mDisplay->drawProgressBar(0, SCREEN_START_Y, SCREEN_WIDTH - 1, 16, 0); 19 | 20 | // draw the current input 21 | this->mDisplay->setTextAlignment(TEXT_ALIGN_LEFT); 22 | int inputStartX = 8; 23 | 24 | this->mDisplay->drawString(inputStartX, SCREEN_START_Y + 1, mInput); 25 | 26 | // draw the (blinking) cursor (0.5Hz) 27 | if ((millis() % 1000) > 500) { 28 | this->mDisplay->setColor(INVERSE); 29 | String inputToStartCurrentChar = mInput.substring(0, mCurrentCharPos); 30 | String inputToEndCurrentChar = mInput.substring(0, mCurrentCharPos + 1); 31 | int cursorStartX = 32 | inputStartX + this->mDisplay->getStringWidth(inputToStartCurrentChar); 33 | int cursorEndX = 34 | inputStartX + this->mDisplay->getStringWidth(inputToEndCurrentChar); 35 | this->mDisplay->fillRect(cursorStartX, SCREEN_START_Y + 3, 36 | cursorEndX - cursorStartX, 12); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Firmware/lib/ESPPing/Pinger.h: -------------------------------------------------------------------------------- 1 | #ifndef __PING_H__ 2 | #define __PING_H__ 3 | 4 | #include "lwip/opt.h" 5 | 6 | #include "lwip/icmp.h" 7 | #include "lwip/inet_chksum.h" 8 | #include "lwip/mem.h" 9 | #include "lwip/netif.h" 10 | #include "lwip/raw.h" 11 | #include "lwip/sys.h" 12 | #include "lwip/timers.h" 13 | 14 | #include "LinkedList.h" 15 | 16 | // the timeout for a single ping request 17 | #define PING_DELAY 1000 18 | 19 | #define PING_DATA_SIZE 32 20 | 21 | #define PING_ID 0xABAB 22 | 23 | class Pinger; 24 | 25 | typedef struct { 26 | ip_addr_t ip; 27 | u16_t id; 28 | u16_t seqNum; 29 | Pinger *pinger; 30 | u32_t pingTime; 31 | } ping_id_t; 32 | 33 | class Pinger { 34 | private: 35 | struct raw_pcb *mPingPcb; 36 | u16_t mPingSeqNum; 37 | u16_t mCurrentId; 38 | void (*mCallback)(ip_addr_t ip, u16_t seqNum, bool didRespond, 39 | u32_t responseTime, void *arg); 40 | LinkedList *mCurrentPings; 41 | u16_t mMaxSimPings; 42 | static sys_mutex_t mMutex; 43 | void *mCallbackArg; 44 | 45 | static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, 46 | ip_addr_t *addr); 47 | static void ping_prepare_echo(struct icmp_echo_hdr *iecho, u16_t len, 48 | u16_t seqNo); 49 | static void ping_timeout(void *arg); 50 | 51 | public: 52 | Pinger(u16_t maxSimultaneousPings = 1); 53 | ~Pinger(); 54 | err_t initialize(); 55 | u16_t send(ip_addr_t ip); 56 | ping_id_t *getPingBySeqNo(u16_t seqNo); 57 | ping_id_t *getPingById(u16_t id); 58 | inline void setCallback(void (*cb)(ip_addr_t, u16_t, bool, u32_t, void *), 59 | void *arg) { 60 | mCallbackArg = arg; 61 | mCallback = cb; 62 | } 63 | }; 64 | 65 | #endif /* __PING_H__ */ 66 | -------------------------------------------------------------------------------- /Firmware/lib/Screens/ListScreen.h: -------------------------------------------------------------------------------- 1 | #ifndef _LISTSCREEN_H_ 2 | #define _LISTSCREEN_H_ 3 | 4 | #include "LinkedList.h" 5 | #include "SSD1306.h" 6 | #include "Screen.h" 7 | #include 8 | 9 | #define ITEM_HEIGHT 11 10 | #define ITEMS_ON_SCREEN ((SCREEN_HEIGHT) / (ITEM_HEIGHT)) 11 | #define INDICATOR_WIDTH 12 12 | #define ELEMENT_INDICATOR ">" 13 | #define PADDING 5 14 | #define LIST_OFFSET 3 15 | 16 | class ListItem { 17 | private: 18 | const String mName; 19 | const String mReference; 20 | const bool mClickable; 21 | const String mExtra; 22 | 23 | public: 24 | inline ListItem(String name, String reference, bool clickable = false, 25 | String extra = "") 26 | : mName(name), mReference(reference), mClickable(clickable), 27 | mExtra(extra) {} 28 | 29 | inline const String getName() { return mName; } 30 | inline const String getReference() { return mReference; } 31 | inline const bool isClickable() { return mClickable; } 32 | inline const String getExtra() { return mExtra; } 33 | }; 34 | 35 | class ListScreen : public Screen { 36 | private: 37 | LinkedList *mItems; 38 | int mScrollOffset; 39 | int mSelectedElementOnScreen; 40 | bool mSelectable; 41 | 42 | public: 43 | ListScreen(SSD1306 *display, bool elementsAreSelectable = true); 44 | ~ListScreen(); 45 | void draw(); 46 | inline void addItem(ListItem *item) { mItems->push_back(item); } 47 | 48 | bool cursorDown(int steps = 1); 49 | bool cursorUp(int steps = 1); 50 | 51 | inline ListItem *getSelectedItem() { 52 | return mItems->at(mScrollOffset + mSelectedElementOnScreen); 53 | } 54 | 55 | inline LinkedList *items() { return mItems; } 56 | 57 | inline void setSelectable(bool isSelectable) { mSelectable = isSelectable; } 58 | }; 59 | 60 | #endif //_LISTSCREEN_H_ 61 | -------------------------------------------------------------------------------- /Firmware/lib/Screens/TextInputScreen.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEXTINPUTSCREEN_H_ 2 | #define _TEXTINPUTSCREEN_H_ 3 | 4 | #include "SSD1306.h" 5 | #include "Screen.h" 6 | #include 7 | 8 | // the first char in ascii that is not a control char (space) 9 | #define FIRST_CHAR 0x20 10 | 11 | // the last char in ascii that is a valid character 12 | #define LAST_CHAR 0x7E 13 | #define START_CHAR 0x41 14 | 15 | class TextInputScreen : public Screen { 16 | private: 17 | const String mMessage; 18 | String mInput; 19 | int mCurrentCharPos; 20 | 21 | public: 22 | TextInputScreen(SSD1306 *display, const String message); 23 | ~TextInputScreen(); 24 | void draw(); 25 | 26 | inline String getInputString() { return mInput; } 27 | inline String setInputString(const String &text) { 28 | mInput = text; 29 | mCurrentCharPos = text.length() - 1; 30 | } 31 | 32 | inline void nextChar() { 33 | // duplicate the last char, that makes the input easier 34 | // because if you enter a long numeric password 35 | // or a sequence of special chars you're already 36 | // in the right region of the ascii table 37 | char lastChar = mInput.charAt(mCurrentCharPos); 38 | mInput += lastChar; 39 | mCurrentCharPos++; 40 | } 41 | 42 | inline void prevChar() { 43 | if (mInput.length() > 1) { 44 | mInput.remove(mInput.length() - 1); 45 | mCurrentCharPos--; 46 | } 47 | } 48 | 49 | inline void charUp() { 50 | char current = mInput.charAt(mCurrentCharPos); 51 | char newChar = current + 1 > LAST_CHAR ? FIRST_CHAR : current + 1; 52 | mInput.setCharAt(mCurrentCharPos, newChar); 53 | } 54 | 55 | inline void charDown() { 56 | char current = mInput.charAt(mCurrentCharPos); 57 | char newChar = current - 1 < FIRST_CHAR ? LAST_CHAR : current - 1; 58 | mInput.setCharAt(mCurrentCharPos, newChar); 59 | } 60 | }; 61 | 62 | #endif //_TEXTINPUTSCREEN_H_ 63 | -------------------------------------------------------------------------------- /Firmware/lib/InputOutput/Button.cpp: -------------------------------------------------------------------------------- 1 | #include "Button.h" 2 | 3 | // ---------------------------------------------------------------------------- 4 | // Button configuration (values for 1ms timer service calls) 5 | // 6 | #define ENC_BUTTONINTERVAL 10 // debounce time 7 | #define ENC_DOUBLECLICKTIME 400 // second click within 600ms 8 | #define ENC_HOLDTIME 1200 // report held button after 1.2s 9 | 10 | Button::Button(uint8_t pinNumber, bool internalPullupEnabled, bool activeLow, 11 | bool doubleClickEnabled) 12 | : Input(pinNumber, activeLow, internalPullupEnabled), mCurrentStatus(Open), 13 | mKeyDownTicks(0), mLastButtonCheck(0), 14 | mDoubleClickEnabled(doubleClickEnabled) {} 15 | 16 | status_t Button::getStatus() { 17 | status_t status = mCurrentStatus; 18 | // only fire click event once 19 | if (status == Clicked || status == Released || status == DoubleClicked) { 20 | mCurrentStatus = Open; 21 | } 22 | return status; 23 | } 24 | 25 | void Button::update() { 26 | unsigned long now = millis(); 27 | 28 | mLastButtonCheck = now; 29 | 30 | if (this->read()) { // key is down 31 | mKeyDownTicks++; 32 | if (mKeyDownTicks > (ENC_HOLDTIME / ENC_BUTTONINTERVAL)) { 33 | this->mCurrentStatus = Held; 34 | } else { 35 | this->mCurrentStatus = Closed; 36 | } 37 | } else { // key is now up 38 | if (mKeyDownTicks) { 39 | if (this->mCurrentStatus == Held) { 40 | this->mCurrentStatus = Released; 41 | mDoubleClickTicks = 0; 42 | } else { 43 | if (mDoubleClickTicks > 1) { 44 | if (mDoubleClickTicks < (ENC_DOUBLECLICKTIME / ENC_BUTTONINTERVAL)) { 45 | this->mCurrentStatus = DoubleClicked; 46 | mDoubleClickTicks = 0; 47 | } 48 | } else { 49 | mDoubleClickTicks = (mDoubleClickEnabled) 50 | ? (ENC_DOUBLECLICKTIME / ENC_BUTTONINTERVAL) 51 | : 1; 52 | } 53 | } 54 | } 55 | mKeyDownTicks = 0; 56 | } 57 | if (mDoubleClickTicks > 0) { 58 | mDoubleClickTicks--; 59 | if (mDoubleClickTicks == 0) { 60 | this->mCurrentStatus = Clicked; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESPTool 2 | ## A WiFi Security Swiss Army Knife 3 | 4 | The ESPTool is a convenient Tool to test your WiFi Security against attackers. 5 | It can demonstrate how easy it is to crack a WiFi Password or jam your WiFi so you cant access the internet. 6 | This Tool is designed for education and self tests. 7 | 8 | **Be aware that it is illegal to use this device on Networks other than your own.** 9 | 10 | _**Disclaimer** The authors of this reposity are not accountable for any action users may perform with this tool_ 11 | 12 | ## Hardware 13 | 14 | The Hardware is designed to be a relative generic and reusable Module around the ESP8266 WiFi Plattform. The Schematic is loosley based on the [NodeMCU](http://nodemcu.com) Project, however instead of breaking out all the free pins, all pins are used for a bunch of devices. 15 | 16 | The Device has the following Hardware 17 | 18 | * SSD1306 based OLED Display, connected via I2C 19 | * microSD Card Socket connected over the SPI interface 20 | * 3 general purpose buttons 21 | * ESP8266-12E Module 22 | * TPS63031 Buck-/Boost-Converter with an input range from 1.8V - 5.5V 23 | * MCP73831 Single-Cell LiPo Charger Chip 24 | * CP2012 USB to UART converter Chip 25 | 26 | ## Software 27 | The software is of course the part where all the security features are implemented. 28 | 29 | It still is under heavy development. A List of planned features are listed here: 30 | 31 | * Layer 1: 32 | * *Since the ESPs Radio is not really configurable it is not possible to create a WiFi Jammer that works by emitting broadband noise or any other Layer 1 attacks* 33 | * Layer 2: 34 | * [Deauthentification attack](https://en.wikipedia.org/wiki/Wi-Fi_deauthentication_attack) 35 | * collecting authentification frames and save the keys to SD card for later decryption (using a wordlist on a computer, the ESP neither has enough memory nor enough processing power to crack them on the system) 36 | * arp spoofing and session hijacking when connected (needs investigation) 37 | * Layer 3 (after connecting to an AP): 38 | * Host discovery (IP Scanner) 39 | * Ping flooding (ICMP Pings) 40 | * Layer 4 (after connecting to an AP): 41 | * Port Scan on Host 42 | 43 | ### Incognito Features (TBD): 44 | * Random MAC-Address on Power Up 45 | * Random Mac Address while performing critical Tasks 46 | -------------------------------------------------------------------------------- /Firmware/lib/LinkedList/LinkedList.h: -------------------------------------------------------------------------------- 1 | #ifndef _LINKEDLIST_H_ 2 | #define _LINKEDLIST_H_ 3 | 4 | #ifndef NULL 5 | #include "cstddef" 6 | #endif 7 | 8 | template class Node { 9 | public: 10 | T value; 11 | Node *next; 12 | 13 | Node(T data) : value(data), next(NULL) {} 14 | }; 15 | 16 | template class LinkedList { 17 | private: 18 | Node *mRoot; 19 | 20 | public: 21 | LinkedList() : mRoot(NULL) {} 22 | void push_back(T data) { 23 | if (this->mRoot == NULL) { 24 | this->mRoot = new Node(data); 25 | return; 26 | } 27 | 28 | Node *temp = this->mRoot; 29 | 30 | while (temp->next != NULL) { 31 | temp = temp->next; 32 | } 33 | 34 | temp->next = new Node(data); 35 | } 36 | 37 | T pop() { 38 | if (this->mRoot == NULL) { 39 | return NULL; 40 | } 41 | 42 | Node *temp = this->mRoot; 43 | Node *prev = NULL; 44 | 45 | while (temp->next != NULL) { 46 | prev = temp; 47 | temp = temp->next; 48 | } 49 | 50 | T data = temp->value; 51 | delete temp; 52 | if (prev != NULL) { 53 | prev->next = NULL; 54 | } else { 55 | mRoot = NULL; 56 | } 57 | return data; 58 | } 59 | 60 | int size() { 61 | int counter = 0; 62 | Node *temp = mRoot; 63 | while (temp != 0) { 64 | temp = temp->next; 65 | counter++; 66 | } 67 | return counter; 68 | } 69 | 70 | T at(int index) { 71 | Node *temp = this->mRoot; 72 | if (temp == NULL) { 73 | return NULL; 74 | } 75 | while (index-- > 0) { 76 | if (temp->next != NULL) { 77 | temp = temp->next; 78 | } else { 79 | return NULL; 80 | } 81 | } 82 | return temp->value; 83 | } 84 | 85 | void clear() { 86 | Node *temp = mRoot; 87 | while (temp != NULL) { 88 | Node *next = temp->next; 89 | delete temp; 90 | temp = next; 91 | } 92 | } 93 | 94 | void remove(T data) { 95 | Node *temp = this->mRoot; 96 | if (temp == NULL) { 97 | return; 98 | } 99 | 100 | Node *prev = NULL; 101 | 102 | while (temp != NULL) { 103 | if (temp->value == data) { 104 | if (prev == NULL) { 105 | Node *next = temp->next; 106 | delete mRoot; 107 | mRoot = next; 108 | } else { 109 | prev->next = temp->next; 110 | delete temp; 111 | } 112 | return; 113 | } 114 | prev = temp; 115 | temp = temp->next; 116 | } 117 | } 118 | }; 119 | #endif // _LINKEDLIST_H_ 120 | -------------------------------------------------------------------------------- /Firmware/lib/Screens/BarGraphScreen.cpp: -------------------------------------------------------------------------------- 1 | #include "BarGraphScreen.h" 2 | 3 | BarGraphScreen::BarGraphScreen(SSD1306 *display, const String message, 4 | int numBars) 5 | : Screen(display), mNumBars(numBars), mBarValues(new int[numBars]), 6 | mBarTitles(new String[numBars]), mMessage(message) { 7 | for (int i = 0; i < numBars; i++) { 8 | mBarValues[i] = 0; 9 | mBarTitles[i] = ""; 10 | } 11 | } 12 | 13 | BarGraphScreen::~BarGraphScreen() { 14 | delete[] mBarTitles; 15 | delete[] mBarValues; 16 | } 17 | 18 | void BarGraphScreen::draw() { 19 | this->clear(); 20 | mDisplay->setFont(ArialMT_Plain_10); 21 | mDisplay->setColor(WHITE); 22 | mDisplay->setTextAlignment(TEXT_ALIGN_CENTER); 23 | 24 | int screenHeightLeft = SCREEN_HEIGHT; 25 | // draw the title if any 26 | if (mMessage.length() > 0) { 27 | mDisplay->drawString(SCREEN_WIDTH / 2, SCREEN_START_Y, mMessage); 28 | screenHeightLeft -= 12; 29 | } 30 | 31 | // while iterating over the labels, use the chance to find the biggest bar 32 | // value 33 | int maxBarValue = 0; 34 | 35 | // draw the bar labels 36 | float barWidth = (SCREEN_WIDTH / (float)mNumBars); 37 | int labelStartY = (SCREEN_START_Y + SCREEN_HEIGHT) - 10; 38 | for (int i = 0; i < mNumBars; i++) { 39 | int labelCenterX = (barWidth * i) + (barWidth / 2); 40 | mDisplay->drawString(labelCenterX, labelStartY, mBarTitles[i]); 41 | 42 | // while iterating over the labels, use the chance to find the biggest bar 43 | // value, otherwise we would need to iterate one more time just for this 44 | if (mBarValues[i] > maxBarValue) { 45 | maxBarValue = mBarValues[i]; 46 | } 47 | } 48 | screenHeightLeft -= 12; 49 | 50 | // draw the bars 51 | 52 | // the height of a bar with value 1 53 | // use flot or we could accumulate the error and end up with an error of 54 | // maxBarValue / 2 pixels 55 | float unitHeight = screenHeightLeft / (float)maxBarValue; 56 | for (int i = 0; i < mNumBars; i++) { 57 | mDisplay->setColor(WHITE); 58 | int barValue = mBarValues[i]; 59 | int barHeight = max(1, unitHeight * barValue); 60 | int barStartX = (barWidth * i) + 1; 61 | int barStartY = (SCREEN_START_Y + SCREEN_HEIGHT - 10) - barHeight; 62 | mDisplay->fillRect(barStartX, barStartY, barWidth - 2, barHeight); 63 | 64 | // draw the labels inside of the bars 65 | if (barValue > 0) { 66 | mDisplay->setColor(INVERSE); 67 | int labelCenterX = (barWidth * i) + (barWidth / 2); 68 | int labelStartY = min(barStartY, barStartY + barHeight - 10); 69 | mDisplay->drawString(labelCenterX, labelStartY, String(barValue)); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Firmware/src/ScanProcess.cpp: -------------------------------------------------------------------------------- 1 | #include "ScanProcess.h" 2 | #include "ESP8266WiFi.h" 3 | #include "ListScreen.h" 4 | #include "LoadingScreen.h" 5 | #include "Process.h" 6 | 7 | ScanProcess::ScanProcess(SSD1306 *display) 8 | : Process(SCAN_PROCESS_NAME, NULL), mDisplay(display) {} 9 | 10 | ScanProcess::~ScanProcess() { delete mScreen; } 11 | 12 | void ScanProcess::initialize() { startScan(); } 13 | 14 | void ScanProcess::startScan() { 15 | Screen *tempScreen = this->mScreen; 16 | LoadingScreen *screen = new LoadingScreen(mDisplay, SCAN_MESSAGE); 17 | this->mScreen = screen; 18 | if (tempScreen != NULL) { 19 | delete tempScreen; 20 | } 21 | 22 | mScanComplete = false; 23 | WiFi.mode(WIFI_STA); 24 | delay(100); 25 | 26 | WiFi.scanNetworks(true, true); 27 | screen->setProgress(INDETERMINATE); 28 | } 29 | 30 | void ScanProcess::showResult(int numNetworksDiscovered) { 31 | // switch to a new ListScreen to show the result 32 | Screen *oldScreen = this->mScreen; 33 | this->mScreen = new ListScreen(oldScreen->getDisplay()); 34 | delete oldScreen; 35 | 36 | // fill the list 37 | fillList(numNetworksDiscovered); 38 | } 39 | 40 | Process *ScanProcess::update() { 41 | int scanStatus = WiFi.scanComplete(); 42 | if (scanStatus >= 0 && !mScanComplete) { 43 | showResult(scanStatus); 44 | mScanComplete = true; 45 | } else if (scanStatus == WIFI_SCAN_FAILED) { 46 | 47 | } else if (scanStatus == WIFI_SCAN_RUNNING) { 48 | } 49 | 50 | mScreen->draw(); 51 | } 52 | 53 | void ScanProcess::fillList(int numberOfNetworks) { 54 | ListScreen *screen = static_cast(this->mScreen); 55 | for (int i = 0; i < numberOfNetworks; i++) { 56 | String ssid = WiFi.SSID(i); 57 | String encryptionScheme = 58 | WiFi.encryptionType(i) == ENC_TYPE_NONE ? " " : " *"; 59 | String rssi = String(WiFi.RSSI(i)); 60 | ListItem *item = 61 | new ListItem(ssid, String(i), true, rssi + encryptionScheme); 62 | screen->addItem(item); 63 | } 64 | ListItem *rescan = 65 | new ListItem(PSTR(" ---- Scan again ----"), RESCAN, true); 66 | screen->addItem(rescan); 67 | } 68 | 69 | void ScanProcess::handleInput(button_t button, status_t action) { 70 | // make sure we're displaying a ListScreen before casting 71 | if (mScanComplete) { 72 | ListScreen *screen = static_cast(this->mScreen); 73 | if (action == Clicked) { 74 | switch (button) { 75 | case Up: 76 | screen->cursorUp(); 77 | break; 78 | case Down: 79 | screen->cursorDown(); 80 | break; 81 | case Left: 82 | ListItem *selectedItem = screen->getSelectedItem(); 83 | if (selectedItem->getReference() == RESCAN) { 84 | this->initialize(); 85 | } else { 86 | if (mCallback != NULL) { 87 | mCallback(selectedItem->getReference().toInt()); 88 | } 89 | } 90 | break; 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Firmware/lib/Screens/ListScreen.cpp: -------------------------------------------------------------------------------- 1 | #include "ListScreen.h" 2 | 3 | ListScreen::ListScreen(SSD1306 *display, bool elementsAreSelectable) 4 | : Screen(display), mItems(new LinkedList()), mScrollOffset(0), 5 | mSelectedElementOnScreen(0), mSelectable(elementsAreSelectable) { 6 | // invisibly "preselect" the last item on the screen if there is no cursor 7 | // otherwise the list will scroll to far on non selectable lists 8 | if (!mSelectable) { 9 | mSelectedElementOnScreen = ITEMS_ON_SCREEN - 1; 10 | } 11 | } 12 | 13 | ListScreen::~ListScreen() { 14 | mItems->clear(); 15 | delete mItems; 16 | } 17 | 18 | void ListScreen::draw() { 19 | this->clear(); 20 | mDisplay->setFont(ArialMT_Plain_10); 21 | mDisplay->setColor(WHITE); 22 | for (int i = 0; i < ITEMS_ON_SCREEN; i++) { 23 | ListItem *item = mItems->at(i + mScrollOffset); 24 | if (item != NULL) { 25 | 26 | // the extra always should just be a short slug 27 | // so it always is in full length and the name gets the rest of the place 28 | int extraLength = mDisplay->getStringWidth(item->getExtra()) + 29 | INDICATOR_WIDTH + PADDING; 30 | 31 | String shortenedName = 32 | this->trimStringToLength(item->getName(), SCREEN_WIDTH - extraLength); 33 | 34 | int elementYPos = SCREEN_START_Y + ((i * ITEM_HEIGHT) - 1) + LIST_OFFSET; 35 | int nameXPos = SCREEN_START_X; 36 | int extraXPos = SCREEN_WIDTH - INDICATOR_WIDTH; 37 | int indicatorXPos = SCREEN_WIDTH; 38 | 39 | // draw the indicator 40 | if (item->isClickable()) { 41 | mDisplay->setTextAlignment(TEXT_ALIGN_RIGHT); 42 | mDisplay->drawString(indicatorXPos, elementYPos, ELEMENT_INDICATOR); 43 | } 44 | 45 | // draw the extra 46 | mDisplay->setTextAlignment(TEXT_ALIGN_RIGHT); 47 | mDisplay->drawString(extraXPos, elementYPos, item->getExtra()); 48 | 49 | // draw the shortened name 50 | mDisplay->setTextAlignment(TEXT_ALIGN_LEFT); 51 | mDisplay->drawString(nameXPos, elementYPos, shortenedName); 52 | } 53 | } 54 | 55 | // draw the cursor only if the items are selectable 56 | if (mSelectable) { 57 | mDisplay->setColor(INVERSE); 58 | mDisplay->fillRect( 59 | SCREEN_START_X, 60 | SCREEN_START_Y + (mSelectedElementOnScreen * ITEM_HEIGHT) + LIST_OFFSET, 61 | SCREEN_WIDTH, ITEM_HEIGHT); 62 | } 63 | } 64 | 65 | bool ListScreen::cursorDown(int steps) { 66 | if (mSelectable) { 67 | mSelectedElementOnScreen += steps; 68 | if (mSelectedElementOnScreen >= ITEMS_ON_SCREEN) { 69 | mScrollOffset += mSelectedElementOnScreen - (ITEMS_ON_SCREEN - 1); 70 | mSelectedElementOnScreen = ITEMS_ON_SCREEN - 1; 71 | } 72 | } else { 73 | mScrollOffset += steps; 74 | } 75 | 76 | if ((mScrollOffset + mSelectedElementOnScreen) > mItems->size()) { 77 | mSelectedElementOnScreen = ITEMS_ON_SCREEN - 1; 78 | if (mSelectable) { 79 | mScrollOffset = mItems->size() - mSelectedElementOnScreen; 80 | } 81 | } 82 | } 83 | 84 | bool ListScreen::cursorUp(int steps) { 85 | if (mSelectable) { 86 | mSelectedElementOnScreen -= steps; 87 | if (mSelectedElementOnScreen < 0) { 88 | mScrollOffset += mSelectedElementOnScreen; 89 | mSelectedElementOnScreen = 0; 90 | } 91 | } else { 92 | mScrollOffset -= steps; 93 | } 94 | 95 | if (mScrollOffset < 0) { 96 | mScrollOffset = 0; 97 | if (mSelectable) { 98 | mSelectedElementOnScreen = 0; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Firmware/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "AnalogInput.h" 6 | #include "Button.h" 7 | #include "ConnectProcess.h" 8 | #include "ESP8266WiFi.h" 9 | #include "OccupationProcess.h" 10 | #include "SSD1306.h" 11 | #include "ScanHostsProcess.h" 12 | #include "ScanProcess.h" 13 | 14 | // Include custom images 15 | #include "images.h" 16 | 17 | #include "Pinger.h" 18 | // include pin definitions 19 | #include "defs.h" 20 | 21 | SSD1306 display(0x3C, 4, 5); 22 | AnalogInput batteryVoltage(VBAT_PIN, VBAT_MAX / ADC_MAX, 10); 23 | Button down(12, true, true); 24 | Button up(13, true, true); 25 | Button left(14, true, true, true); 26 | Ticker buttonUpdater; 27 | 28 | ScanProcess scanner(&display); 29 | Process *currentProcess = &scanner; 30 | 31 | void updateButton() { 32 | down.update(); 33 | up.update(); 34 | left.update(); 35 | } 36 | 37 | void drawStatusBar() { 38 | // draw the battery indicator and voltage 39 | float currentVoltage = batteryVoltage.readConverted(); 40 | int screenWidthLeft = DISPLAY_WIDTH; 41 | 42 | const char *currentVoltageImage = NULL; 43 | if (currentVoltage >= 3.6) { 44 | currentVoltageImage = icon_battery_33; 45 | } else if (currentVoltage >= 3.4) { 46 | currentVoltageImage = icon_battery_23; 47 | } else if (currentVoltage >= 3.2) { 48 | currentVoltageImage = icon_battery_13; 49 | } else { 50 | currentVoltageImage = icon_battery_03; 51 | } 52 | 53 | display.drawXbm(DISPLAY_WIDTH - ICON_BATTERY_WIDTH, 2, ICON_BATTERY_WIDTH, 54 | ICON_BATTERY_HEIGHT, currentVoltageImage); 55 | 56 | screenWidthLeft -= ICON_BATTERY_WIDTH + 2; 57 | 58 | display.setTextAlignment(TEXT_ALIGN_RIGHT); 59 | display.setFont(ArialMT_Plain_10); 60 | String voltageText = String(currentVoltage, 2) + "V"; 61 | display.drawString(screenWidthLeft, 0, voltageText); 62 | display.drawLine(0, 11, DISPLAY_WIDTH, 11); 63 | 64 | screenWidthLeft -= display.getStringWidth(voltageText) + 4; 65 | 66 | // draw the sdcard icon if a card is present 67 | display.drawXbm(screenWidthLeft - ICON_SDCARD_WIDTH, 2, ICON_SDCARD_WIDTH, 68 | ICON_SDCARD_HEIGHT, icon_sdcard); 69 | 70 | screenWidthLeft -= ICON_SDCARD_WIDTH + 4; 71 | 72 | // draw the wifi icon if connected 73 | if (WiFi.status() == WL_CONNECTED) { 74 | display.drawXbm(screenWidthLeft - ICON_WIFI_WIDTH, 2, ICON_WIFI_WIDTH, 75 | ICON_WIFI_HEIGHT, icon_wifi); 76 | 77 | screenWidthLeft -= ICON_WIFI_WIDTH + 4; 78 | } 79 | 80 | // draw the current menu location 81 | String currentScreenName = currentProcess->getName(); 82 | while (display.getStringWidth(currentScreenName) > screenWidthLeft) { 83 | currentScreenName.remove(currentScreenName.length() - 4); 84 | currentScreenName += "..."; 85 | } 86 | 87 | display.setTextAlignment(TEXT_ALIGN_LEFT); 88 | display.drawString(0, 0, currentScreenName); 89 | } 90 | 91 | void setup() { 92 | display.init(); 93 | 94 | display.flipScreenVertically(); 95 | 96 | pinMode(VBAT_PIN, INPUT); 97 | 98 | Serial.begin(115200); 99 | Serial.println("lalalalala test"); 100 | 101 | buttonUpdater.attach_ms(10, updateButton); 102 | 103 | currentProcess->initialize(); 104 | 105 | scanner.setCallback([currentProcess](int selectedLan) { 106 | currentProcess = new ConnectProcess(&display, selectedLan); 107 | currentProcess->setCallback([currentProcess](int status) { 108 | if (status == 0) { 109 | currentProcess = new ScanHostsProcess(&display); 110 | currentProcess->initialize(); 111 | } else { 112 | currentProcess = &scanner; 113 | } 114 | }); 115 | }); 116 | } 117 | 118 | void handleInput() { 119 | status_t status = up.getStatus(); 120 | currentProcess->handleInput(Up, status); 121 | 122 | status = down.getStatus(); 123 | currentProcess->handleInput(Down, status); 124 | 125 | status = left.getStatus(); 126 | currentProcess->handleInput(Left, status); 127 | } 128 | 129 | void loop() { 130 | display.clear(); 131 | drawStatusBar(); 132 | 133 | currentProcess->update(); 134 | 135 | handleInput(); 136 | 137 | display.display(); 138 | } 139 | -------------------------------------------------------------------------------- /Firmware/src/ConnectProcess.cpp: -------------------------------------------------------------------------------- 1 | #include "ConnectProcess.h" 2 | #include "CredentialCache.h" 3 | #include "ESP8266WiFi.h" 4 | #include "ListScreen.h" 5 | #include "LoadingScreen.h" 6 | #include "MessageScreen.h" 7 | #include "Process.h" 8 | #include "TextInputScreen.h" 9 | 10 | ConnectProcess::ConnectProcess(SSD1306 *display, int wlanNumberFromScan) 11 | : Process(CONNECT_PROCESS_NAME, NULL), 12 | mWlanNumberFromScan(wlanNumberFromScan), mCurrentStatus(UNKNOWN), 13 | mDisplay(display) { 14 | this->initialize(); 15 | } 16 | 17 | void ConnectProcess::initialize() { 18 | CredentialCache::begin(); 19 | if (!CredentialCache::isFormatted()) { 20 | CredentialCache::format(); 21 | } 22 | String ssid = WiFi.SSID(mWlanNumberFromScan); 23 | if (WiFi.encryptionType(mWlanNumberFromScan) != ENC_TYPE_NONE) { 24 | if (CredentialCache::hasPassphrase(ssid)) { 25 | String passphrase = CredentialCache::getPassphrase(ssid); 26 | connectToWiFi(passphrase); 27 | } else { 28 | promptForPassword(); 29 | } 30 | } else { 31 | connectToWiFi(""); 32 | } 33 | } 34 | 35 | void ConnectProcess::promptForPassword() { 36 | String ssid = WiFi.SSID(mWlanNumberFromScan); 37 | String screenPrompt = PROMPT_MESSAGE; 38 | screenPrompt += String('\n'); 39 | screenPrompt += ssid; 40 | 41 | Screen *tempScreen = this->mScreen; 42 | TextInputScreen *txtInScreen = new TextInputScreen(mDisplay, screenPrompt); 43 | 44 | // set the password we have saved 45 | if (CredentialCache::hasPassphrase(ssid)) { 46 | String passphrase = CredentialCache::getPassphrase(ssid); 47 | txtInScreen->setInputString(passphrase); 48 | } 49 | this->setScreen(txtInScreen); 50 | 51 | if (tempScreen != NULL) { 52 | delete tempScreen; 53 | } 54 | 55 | mCurrentStatus = PROMPTING; 56 | } 57 | 58 | void ConnectProcess::connectToWiFi(const String &passphrase) { 59 | String screenPrompt = CONNECTING_MESSAGE; 60 | String ssid = WiFi.SSID(mWlanNumberFromScan); 61 | 62 | CredentialCache::savePassphrase(ssid, passphrase); 63 | 64 | screenPrompt += String('\n'); 65 | screenPrompt += ssid; 66 | 67 | Screen *tempScreen = this->mScreen; 68 | LoadingScreen *connectingScreen = new LoadingScreen(mDisplay, screenPrompt); 69 | connectingScreen->setProgress(INDETERMINATE); 70 | this->setScreen(connectingScreen); 71 | 72 | if (tempScreen != NULL) { 73 | delete tempScreen; 74 | } 75 | Serial.println(ssid.c_str()); 76 | Serial.println(passphrase.c_str()); 77 | WiFi.begin(ssid.c_str(), passphrase.c_str()); 78 | mCurrentStatus = CONNECTING; 79 | } 80 | 81 | void ConnectProcess::showConnectedSummary() { 82 | String ssid = WiFi.SSID(mWlanNumberFromScan); 83 | String localIp = ipToString(WiFi.localIP()); 84 | String gatewayIp = ipToString(WiFi.gatewayIP()); 85 | String subnetMask = ipToString(WiFi.subnetMask()); 86 | String macAddress = WiFi.macAddress(); 87 | 88 | Screen *tempScreen = this->mScreen; 89 | ListScreen *connectSummaryScreen = new ListScreen(mDisplay, false); 90 | 91 | connectSummaryScreen->addItem(new ListItem("Connected", "", false, ssid)); 92 | connectSummaryScreen->addItem(new ListItem("IP:", "", false, localIp)); 93 | connectSummaryScreen->addItem(new ListItem("Gateway:", "", false, gatewayIp)); 94 | connectSummaryScreen->addItem(new ListItem("Subnet:", "", false, subnetMask)); 95 | connectSummaryScreen->addItem(new ListItem("Mac:", "", false, macAddress)); 96 | 97 | this->setScreen(connectSummaryScreen); 98 | 99 | if (tempScreen != NULL) { 100 | delete tempScreen; 101 | } 102 | mCurrentStatus = CONNECTED; 103 | } 104 | 105 | void ConnectProcess::showConnectionError() { 106 | Screen *tempScreen = this->mScreen; 107 | MessageScreen *messageScreen = 108 | new MessageScreen(mDisplay, "failed to connect", "back"); 109 | 110 | this->setScreen(messageScreen); 111 | 112 | if (tempScreen != NULL) { 113 | delete tempScreen; 114 | } 115 | 116 | mCurrentStatus = FAILED; 117 | } 118 | 119 | ConnectProcess::~ConnectProcess() { delete mScreen; } 120 | 121 | Process *ConnectProcess::update() { 122 | if (mCurrentStatus == CONNECTING && WiFi.status() == WL_CONNECTED) { 123 | // we are connected now, show the 124 | showConnectedSummary(); 125 | } else if (mCurrentStatus == CONNECTING && 126 | WiFi.status() == WL_CONNECT_FAILED) { 127 | showConnectionError(); 128 | } 129 | mScreen->draw(); 130 | } 131 | 132 | void ConnectProcess::handleInput(button_t button, status_t action) { 133 | 134 | if (action == Released || action == Open) { 135 | mDidDelete = false; 136 | return; 137 | } 138 | if (mCurrentStatus == PROMPTING) { 139 | TextInputScreen *screen = static_cast(this->mScreen); 140 | if (action == Clicked) { 141 | switch (button) { 142 | case Up: 143 | screen->charUp(); 144 | break; 145 | case Down: 146 | screen->charDown(); 147 | break; 148 | case Left: 149 | screen->nextChar(); 150 | break; 151 | } 152 | } 153 | 154 | if (action == Held) { 155 | switch (button) { 156 | case Up: 157 | screen->charUp(); 158 | break; 159 | case Down: 160 | screen->charDown(); 161 | break; 162 | case Left: 163 | String passphrase = screen->getInputString(); 164 | connectToWiFi(passphrase); 165 | break; 166 | } 167 | } 168 | 169 | if (action == DoubleClicked && button == Left) { 170 | if (!mDidDelete) { 171 | screen->prevChar(); 172 | mDidDelete = true; 173 | } 174 | } 175 | } else if (mCurrentStatus == CONNECTED) { 176 | ListScreen *screen = static_cast(this->mScreen); 177 | if (action == Clicked) { 178 | switch (button) { 179 | case Up: 180 | screen->cursorUp(1); 181 | break; 182 | case Down: 183 | screen->cursorDown(1); 184 | break; 185 | case Left: 186 | if (mCallback != NULL) { 187 | mCallback(0); 188 | } 189 | break; 190 | } 191 | } 192 | } else if (mCurrentStatus == FAILED) { 193 | if (action == Clicked && button == Left) { 194 | if (WiFi.encryptionType(mWlanNumberFromScan) != ENC_TYPE_NONE) { 195 | promptForPassword(); 196 | } else { 197 | if (mCallback != NULL) { 198 | mCallback(-1); 199 | } 200 | } 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /Firmware/src/ScanHostsProcess.cpp: -------------------------------------------------------------------------------- 1 | #include "ScanHostsProcess.h" 2 | #include "ESP8266WiFi.h" 3 | #include "ListScreen.h" 4 | #include "MessageScreen.h" 5 | #include "Process.h" 6 | 7 | ScanHostsProcess::ScanHostsProcess(SSD1306 *display) 8 | : Process(SCAN_HOST_PROCESS_NAME, NULL), mDisplay(display), mPinger(NULL) {} 9 | 10 | ScanHostsProcess::~ScanHostsProcess() { 11 | if (mPinger != NULL) { 12 | delete mPinger; 13 | } 14 | delete mScreen; 15 | } 16 | 17 | void ScanHostsProcess::initialize() { 18 | IPAddress netMask = WiFi.subnetMask(); 19 | IPAddress localIp = WiFi.localIP(); 20 | 21 | mHost = IPAddress2ip_addr_t(localIp); 22 | mNetMask = IPAddress2ip_addr_t(netMask); 23 | mNetAddress.addr = mHost.addr & mNetMask.addr; 24 | 25 | showStartScreen(); 26 | } 27 | 28 | void ScanHostsProcess::showStartScreen() { 29 | mStartAddress.addr = mNetAddress.addr + (1 << 24); 30 | mEndAddress.addr = (mNetAddress.addr | ~mNetMask.addr) - (1 << 24); 31 | 32 | char IpStrBuffer[16]; 33 | String message = "Network: "; 34 | sprintf(IpStrBuffer, IPSTR, IP2STR(&mNetAddress)); 35 | message += IpStrBuffer; 36 | message += "\n"; 37 | sprintf(IpStrBuffer, IPSTR, IP2STR(&mStartAddress)); 38 | message += IpStrBuffer; 39 | message += " -\n"; 40 | sprintf(IpStrBuffer, IPSTR, IP2STR(&mEndAddress)); 41 | message += IpStrBuffer; 42 | 43 | Screen *tempScreen = this->mScreen; 44 | MessageScreen *screen = new MessageScreen(mDisplay, message, "start scan"); 45 | this->mScreen = screen; 46 | if (tempScreen != NULL) { 47 | delete tempScreen; 48 | } 49 | 50 | mScanStatus = NOT_STARTED; 51 | } 52 | 53 | void ScanHostsProcess::startScan() { 54 | Screen *tempScreen = this->mScreen; 55 | ListScreen *screen = new ListScreen(mDisplay, true); 56 | 57 | mCurrentHost.addr = mStartAddress.addr; 58 | 59 | // add the cancel item 60 | ListItem *cancelItem = new ListItem("cancel scan", CANCEL, true, "0%"); 61 | screen->addItem(cancelItem); 62 | 63 | // change the screen 64 | this->mScreen = screen; 65 | 66 | // delete the old screen 67 | if (tempScreen != NULL) { 68 | delete tempScreen; 69 | } 70 | 71 | if (mPinger == NULL) { 72 | mPinger = new Pinger(10); 73 | mPinger->initialize(); 74 | } 75 | 76 | mPinger->setCallback(&ScanHostsProcess::pingerCallback, this); 77 | mScanStatus = RUNNING; 78 | } 79 | 80 | void ScanHostsProcess::stopScan() { 81 | if (mPinger != NULL) { 82 | // delete the old pinger, we create a new one for each scan 83 | delete mPinger; 84 | mPinger = NULL; 85 | } 86 | 87 | ListScreen *screen = static_cast(mScreen); 88 | 89 | // remove the cancel item 90 | ListItem *cancelItem = screen->items()->pop(); 91 | if (cancelItem != NULL) { 92 | delete cancelItem; 93 | cancelItem = NULL; 94 | } 95 | 96 | // add the rescan item 97 | ListItem *rescanItem = new ListItem("restart scan", RESCAN, true); 98 | screen->addItem(rescanItem); 99 | 100 | mScanStatus = FINISHED; 101 | } 102 | 103 | void ScanHostsProcess::pingerCallback(ip_addr_t ip, u16_t seqNum, 104 | bool didRespond, u32_t responseTime, 105 | void *arg) { 106 | ScanHostsProcess *scanner = static_cast(arg); 107 | 108 | if (didRespond) { 109 | scanner->addHost(ip, responseTime); 110 | } 111 | } 112 | 113 | Process *ScanHostsProcess::update() { 114 | if (mScanStatus == RUNNING) { 115 | ListScreen *screen = static_cast(this->mScreen); 116 | u16_t status = mPinger->send(mCurrentHost); 117 | if (status != 65535) { 118 | ip_addr_t nextIp; 119 | nextIp.addr = mCurrentHost.addr; 120 | // get the next ip that is not our own 121 | do { 122 | nextIp.addr = htonl(ntohl(nextIp.addr) + 1); 123 | } while (nextIp.addr == mHost.addr); 124 | 125 | if (ntohl(nextIp.addr) > ntohl(mEndAddress.addr)) { 126 | this->stopScan(); 127 | } 128 | 129 | mCurrentHost = nextIp; 130 | char str[16]; 131 | sprintf(str, IPSTR, IP2STR(&mCurrentHost)); 132 | Serial.println(str); 133 | 134 | // calculate the new progress and show it as extra on the cancel item 135 | ListItem *cancelItem = screen->items()->pop(); 136 | 137 | int hostToScan = ntohl(mEndAddress.addr) - ntohl(mStartAddress.addr); 138 | int hostsScanned = ntohl(mCurrentHost.addr) - ntohl(mStartAddress.addr); 139 | int scanProgress = (hostsScanned * 100) / hostToScan; 140 | String scanProgressString = String(scanProgress) + "%"; 141 | 142 | if (cancelItem != NULL) { 143 | ListItem *newCancelItem = 144 | new ListItem(cancelItem->getName(), cancelItem->getReference(), 145 | cancelItem->isClickable(), scanProgressString); 146 | screen->addItem(newCancelItem); 147 | delete cancelItem; 148 | } 149 | } 150 | } 151 | 152 | mScreen->draw(); 153 | } 154 | 155 | void ScanHostsProcess::addHost(ip_addr_t ip, u32_t responseTime) { 156 | 157 | ListScreen *screen = static_cast(this->mScreen); 158 | 159 | ListItem *selectedItem = screen->getSelectedItem(); 160 | 161 | char ipStr[16]; 162 | sprintf(ipStr, IPSTR, IP2STR(&ip)); 163 | String responseTimeStr = String(responseTime) + "ms"; 164 | ListItem *item = new ListItem(ipStr, ipStr, true, responseTimeStr); 165 | 166 | ListItem *cancelItem = screen->items()->pop(); 167 | screen->addItem(item); 168 | screen->addItem(cancelItem); 169 | 170 | bool autoScroll = selectedItem == cancelItem; 171 | if (autoScroll) { 172 | screen->cursorDown(1); 173 | } 174 | } 175 | 176 | void ScanHostsProcess::handleInput(button_t button, status_t action) { 177 | // make sure we're displaying a ListScreen before casting 178 | if (mScanStatus == NOT_STARTED) { 179 | if (action == Clicked && button == Left) { 180 | startScan(); 181 | } 182 | } else if (mScanStatus == RUNNING || mScanStatus == FINISHED) { 183 | ListScreen *screen = static_cast(this->mScreen); 184 | if (action == Clicked) { 185 | switch (button) { 186 | case Up: 187 | screen->cursorUp(); 188 | break; 189 | case Down: 190 | screen->cursorDown(); 191 | break; 192 | case Left: 193 | ListItem *selectedItem = screen->getSelectedItem(); 194 | if (selectedItem->getReference() == CANCEL) { 195 | this->stopScan(); 196 | } else if (selectedItem->getReference() == RESCAN) { 197 | this->startScan(); 198 | } else { 199 | if (mCallback != NULL) { 200 | mCallback(selectedItem->getReference().toInt()); 201 | } 202 | } 203 | break; 204 | } 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /Firmware/lib/ESPPing/Pinger.cpp: -------------------------------------------------------------------------------- 1 | #include "Pinger.h" 2 | Pinger::Pinger(u16_t maxSimultaneousPings) 3 | : mPingSeqNum(0), mCallback(NULL), mMaxSimPings(maxSimultaneousPings), 4 | mCurrentPings(new LinkedList), mCurrentId(0), 5 | mCallbackArg(NULL) {} 6 | 7 | Pinger::~Pinger() { 8 | if (mPingPcb != NULL) { 9 | raw_remove(mPingPcb); 10 | } 11 | 12 | int i = mCurrentPings->size(); 13 | while (i--) { 14 | ping_id_t *ping = mCurrentPings->pop(); 15 | sys_untimeout(Pinger::ping_timeout, ping); 16 | delete ping; 17 | } 18 | 19 | // delete mCurrentPings; 20 | sys_mutex_free(&mMutex); 21 | } 22 | 23 | err_t Pinger::initialize() { 24 | err_t error = ERR_OK; 25 | 26 | // create a new raw ip control block 27 | mPingPcb = raw_new(IP_PROTO_ICMP); 28 | 29 | // set the receive listener for the block 30 | raw_recv(mPingPcb, Pinger::ping_recv, this); 31 | // bin the pcb to all ip addresses 32 | error = raw_bind(mPingPcb, IP_ADDR_ANY); 33 | 34 | // create a new mutex to avoid race-conditions 35 | // while checking if a response was received in time 36 | sys_mutex_new(&mMutex); 37 | return error; 38 | } 39 | 40 | u8_t Pinger::ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, 41 | ip_addr_t *addr) { 42 | struct icmp_echo_hdr *iecho; 43 | u32_t recvTime = sys_now(); 44 | 45 | // the Pinger is set as recv_arg while registering the receive callback 46 | Pinger *self = static_cast(pcb->recv_arg); 47 | 48 | LWIP_UNUSED_ARG(pcb); 49 | LWIP_UNUSED_ARG(addr); 50 | 51 | if (pbuf_header(p, -PBUF_IP_HLEN) == 0) { 52 | // get the echo response payload 53 | iecho = (struct icmp_echo_hdr *)p->payload; 54 | 55 | // get the ID 56 | u16_t echoId = ntohs(iecho->id); 57 | 58 | // get the sequence number 59 | u16_t seqNo = ntohs(iecho->seqno); 60 | 61 | // lock the mutex 62 | sys_mutex_lock(&mMutex); 63 | 64 | // check if we have an outstanding response for this sequence number 65 | ping_id_t *ping = self->getPingBySeqNo(seqNo); 66 | if (ping != NULL) { 67 | 68 | ip_addr_t ip = ping->ip; 69 | u32_t responseTime = recvTime - ping->pingTime; 70 | 71 | // call the callback 72 | if (self->mCallback != NULL) { 73 | self->mCallback(ip, seqNo, true, responseTime, self->mCallbackArg); 74 | } 75 | 76 | // clear the timeout 77 | sys_untimeout(Pinger::ping_timeout, ping); 78 | // clean up for this ping 79 | self->mCurrentPings->remove(ping); 80 | delete ping; 81 | } 82 | // unock the mutex 83 | sys_mutex_unlock(&mMutex); 84 | // free the packet buffer 85 | pbuf_free(p); 86 | 87 | // eat the packet 88 | return 1; 89 | } 90 | 91 | // don't eat the packet 92 | return 0; 93 | } 94 | 95 | ping_id_t *Pinger::getPingBySeqNo(u16_t seqNo) { 96 | u16_t i = 0; 97 | ping_id_t *currentPing = this->mCurrentPings->at(i); 98 | 99 | while (currentPing != NULL) { 100 | if (currentPing->seqNum == seqNo) { 101 | return currentPing; 102 | } 103 | currentPing = this->mCurrentPings->at(++i); 104 | } 105 | return NULL; 106 | } 107 | 108 | ping_id_t *Pinger::getPingById(u16_t id) { 109 | u16_t i = 0; 110 | ping_id_t *currentPing = this->mCurrentPings->at(i); 111 | 112 | while (currentPing != NULL) { 113 | if (currentPing->id == id) { 114 | return currentPing; 115 | } 116 | currentPing = this->mCurrentPings->at(++i); 117 | } 118 | return NULL; 119 | } 120 | 121 | void Pinger::ping_prepare_echo(struct icmp_echo_hdr *iecho, u16_t len, 122 | u16_t seqNo) { 123 | size_t i; 124 | size_t data_len = len - sizeof(struct icmp_echo_hdr); 125 | 126 | // set all the required fields 127 | ICMPH_TYPE_SET(iecho, ICMP_ECHO); 128 | ICMPH_CODE_SET(iecho, 0); 129 | iecho->chksum = 0; 130 | iecho->id = PING_ID; 131 | iecho->seqno = htons(seqNo); 132 | 133 | // fill the additional data buffer with some data 134 | for (i = 0; i < data_len; i++) { 135 | ((char *)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i; 136 | } 137 | 138 | // calculate and set the checksum 139 | iecho->chksum = inet_chksum(iecho, len); 140 | } 141 | 142 | u16_t Pinger::send(ip_addr_t ip) { 143 | struct icmp_echo_hdr *iecho; 144 | size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE; 145 | 146 | u16_t seqNo = -1; 147 | 148 | // calculate the next sequence number 149 | u16_t nextSeqNo = this->mPingSeqNum + 1; 150 | 151 | u16_t nextId = this->mCurrentId; 152 | int freeSlots = this->mMaxSimPings; 153 | ping_id_t *ping = NULL; 154 | do { 155 | nextId += 1; 156 | nextId %= mMaxSimPings; 157 | ping = this->getPingById(nextId); 158 | } while (ping != NULL && --freeSlots > 0); 159 | 160 | // if the sequence number is currently in use, we have no more free pings 161 | // available, another ping has to respond or timeout first. 162 | if (ping != NULL) { 163 | return -1; 164 | } 165 | 166 | sys_mutex_lock(&mMutex); 167 | 168 | // allocate a new packet buffer 169 | struct pbuf *p; 170 | p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM); 171 | if (!p) { 172 | pbuf_free(p); 173 | return -1; 174 | } 175 | if ((p->len == p->tot_len) && (p->next == NULL)) { 176 | 177 | // create a new ping_id_t for the timeout callback to pass 178 | ping_id_t *id = new ping_id_t; 179 | id->pinger = this; 180 | id->id = nextId; 181 | id->seqNum = nextSeqNo; 182 | id->ip = ip; 183 | this->mCurrentId = nextId; 184 | 185 | // mark the current sequence number as not available 186 | mCurrentPings->push_back(id); 187 | 188 | sys_mutex_unlock(&mMutex); 189 | 190 | // increment the sequence number 191 | this->mPingSeqNum = nextSeqNo; 192 | 193 | // get the data field of the ip packet 194 | iecho = (struct icmp_echo_hdr *)p->payload; 195 | 196 | // prepare the ICMP Echo Request 197 | Pinger::ping_prepare_echo(iecho, (u16_t)ping_size, nextSeqNo); 198 | 199 | // register the timeout 200 | sys_timeout(PING_DELAY, ping_timeout, id); 201 | 202 | // send the request 203 | id->pingTime = sys_now(); 204 | raw_sendto(mPingPcb, p, &ip); 205 | 206 | // set the return value; 207 | seqNo = this->mPingSeqNum; 208 | 209 | // free the packet buffer 210 | } 211 | pbuf_free(p); 212 | 213 | return seqNo; 214 | } 215 | 216 | void Pinger::ping_timeout(void *arg) { 217 | // get the ping_id_t that was specified as argument 218 | ping_id_t *id = static_cast(arg); 219 | 220 | Pinger *pinger = id->pinger; 221 | if (pinger == NULL) { 222 | // delete id; 223 | return; 224 | } 225 | u16_t seqNo = id->seqNum; 226 | ip_addr_t ip = id->ip; 227 | 228 | // lock the mutex before checking the state 229 | sys_mutex_lock(&mMutex); 230 | ping_id_t *ping = pinger->getPingBySeqNo(seqNo); 231 | if (ping != NULL) { 232 | 233 | // call the callback 234 | if (pinger->mCallback != NULL) { 235 | pinger->mCallback(ip, seqNo, false, -1, pinger->mCallbackArg); 236 | } 237 | 238 | // free the state of the current sequence number 239 | pinger->mCurrentPings->remove(ping); 240 | delete ping; 241 | } 242 | 243 | // free the mutex again 244 | sys_mutex_unlock(&mMutex); 245 | } 246 | --------------------------------------------------------------------------------