├── LICENSE ├── README.md ├── atmega_duck ├── atmega_duck.ino ├── com.cpp ├── com.h ├── config.h ├── debug.h ├── duckparser.cpp ├── duckparser.h ├── keyboard.cpp ├── keyboard.h ├── led.cpp ├── led.h ├── locale_be.h ├── locale_bg.h ├── locale_cafr.h ├── locale_chde.h ├── locale_chfr.h ├── locale_cz.h ├── locale_de.h ├── locale_dk.h ├── locale_es.h ├── locale_fr.h ├── locale_gb.h ├── locale_hu.h ├── locale_it.h ├── locale_pt.h ├── locale_ru.h ├── locale_si.h ├── locale_sk.h ├── locale_types.h ├── locale_us.h ├── locales.h ├── parser.c ├── parser.h ├── serial_bridge.cpp ├── serial_bridge.h └── usb_hid_keys.h ├── esp8266Programmer └── esp8266Programmer.ino ├── esp_duck ├── cli.cpp ├── cli.h ├── com.cpp ├── com.h ├── config.h ├── debug.h ├── duckscript.cpp ├── duckscript.h ├── eeprom.cpp ├── eeprom.h ├── esp_duck.ino ├── esp_duck_generic.bin ├── settings.cpp ├── settings.h ├── spiffs.cpp ├── spiffs.h ├── webfiles.h ├── webserver.cpp └── webserver.h ├── img ├── diy_example.jpg ├── dstike_atmega.jpg ├── dstike_esp8266.jpg ├── dstike_normal.jpg ├── dstikeboard.jpg ├── logo.png ├── malw.jpg ├── ota.jpg ├── pcbs.jpg ├── pcbs_soldered.jpg ├── showcase.gif └── thumbnail.jpg ├── test.script ├── web ├── credits.html ├── error404.html ├── index.html ├── index.js ├── script.js ├── settings.html ├── settings.js ├── style.css ├── terminal.html └── terminal.js └── webconverter.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Spacehuhn Technologies 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /atmega_duck/atmega_duck.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #include "config.h" 7 | #include "debug.h" 8 | 9 | #include "keyboard.h" 10 | #include "led.h" 11 | #include "com.h" 12 | #include "duckparser.h" 13 | #include "serial_bridge.h" 14 | 15 | // ===== SETUP ====== // 16 | void setup() { 17 | debug_init(); 18 | 19 | serial_bridge::begin(); 20 | keyboard::begin(); 21 | com::begin(); 22 | 23 | debugs("Started! "); 24 | debugln(VERSION); 25 | } 26 | 27 | // ===== LOOOP ===== // 28 | void loop() { 29 | com::update(); 30 | if (com::hasData()) { 31 | const buffer_t& buffer = com::getBuffer(); 32 | 33 | debugs("Interpreting: "); 34 | 35 | for (size_t i = 0; i // Arduino i2c 11 | 12 | #include "debug.h" 13 | #include "duckparser.h" 14 | 15 | // ! Communication request codes 16 | #define REQ_SOT 0x01 // !< Start of transmission 17 | #define REQ_EOT 0x04 // !< End of transmission 18 | #define REQ_VERSION 0x02 // !< Request current version 19 | 20 | #define COM_VERSION 4 21 | 22 | typedef struct status_t { 23 | unsigned int version : 8; 24 | unsigned int wait : 16; 25 | unsigned int repeat : 8; 26 | } status_t; 27 | 28 | namespace com { 29 | // =========== PRIVATE ========= // 30 | buffer_t receive_buf; 31 | buffer_t data_buf; 32 | 33 | bool start_parser = false; 34 | bool ongoing_transmission = false; 35 | 36 | status_t status; 37 | 38 | void update_status() { 39 | status.wait = (uint16_t)receive_buf.len 40 | + (uint16_t)data_buf.len 41 | + (uint16_t)duckparser::getDelayTime(); 42 | status.repeat = (uint8_t)(duckparser::getRepeats() > 255 ? 255 : duckparser::getRepeats()); 43 | } 44 | 45 | // ========== PRIVATE I2C ========== // 46 | #ifdef ENABLE_I2C 47 | 48 | // time sensetive! 49 | void i2c_request() { 50 | update_status(); 51 | Wire.write((uint8_t*)&status, sizeof(status_t)); 52 | } 53 | 54 | // time sensetive! 55 | void i2c_receive(int len) { 56 | if (receive_buf.len + (unsigned int)len <= BUFFER_SIZE) { 57 | Wire.readBytes(&receive_buf.data[receive_buf.len], len); 58 | receive_buf.len += len; 59 | } 60 | } 61 | 62 | void i2c_begin() { 63 | debugsln("ENABLED I2C"); 64 | Wire.begin(I2C_ADDR); 65 | Wire.onRequest(i2c_request); 66 | Wire.onReceive(i2c_receive); 67 | 68 | data_buf.len = 0; 69 | receive_buf.len = 0; 70 | } 71 | 72 | #else // ifdef ENABLE_I2C 73 | void i2c_begin() {} 74 | 75 | #endif // ifdef ENABLE_I2C 76 | 77 | // ========== PRIVATE SERIAL ========== // 78 | #ifdef ENABLE_SERIAL 79 | void serial_begin() { 80 | debugsln("ENABLED SERIAL"); 81 | SERIAL_COM.begin(SERIAL_BAUD); 82 | } 83 | 84 | void serial_send_status() { 85 | update_status(); 86 | #ifdef ENABLE_DEBUG 87 | debugs("Replying with status {"); 88 | debugs("wait: "); 89 | debug(status.wait); 90 | debugs(",repeat: "); 91 | debug(status.repeat); 92 | debugs("} ["); 93 | 94 | for (int i = 0; i 0) && (receive_buf.len+len <= BUFFER_SIZE)) { 113 | SERIAL_COM.readBytes(&receive_buf.data[receive_buf.len], len); 114 | receive_buf.len += len; 115 | } 116 | } 117 | 118 | #else // ifdef ENABLE_SERIAL 119 | void serial_begin() {} 120 | 121 | void serial_send_status() {} 122 | 123 | void serial_update() {} 124 | 125 | #endif // ifdef ENABLE_SERIAL 126 | 127 | // ========== PUBLIC ========== // 128 | void begin() { 129 | status.version = COM_VERSION; 130 | i2c_begin(); 131 | serial_begin(); 132 | } 133 | 134 | void update() { 135 | serial_update(); 136 | 137 | if (!start_parser && (receive_buf.len > 0) && (data_buf.len < BUFFER_SIZE)) { 138 | unsigned int i = 0; 139 | 140 | debugs("RECEIVED "); 141 | 142 | // ! Skip bytes until start of transmission 143 | while (i < receive_buf.len && !ongoing_transmission) { 144 | if (receive_buf.data[i] == REQ_SOT) { 145 | ongoing_transmission = true; 146 | debugs("[SOT] "); 147 | } 148 | ++i; 149 | } 150 | 151 | debugs("'"); 152 | 153 | while (i < receive_buf.len && ongoing_transmission) { 154 | char c = receive_buf.data[i]; 155 | 156 | if (c == REQ_EOT) { 157 | start_parser = true; 158 | ongoing_transmission = false; 159 | } else { 160 | debug(c, BIN); 161 | debug(" "); 162 | 163 | data_buf.data[data_buf.len] = c; 164 | ++data_buf.len; 165 | } 166 | 167 | if (data_buf.len == BUFFER_SIZE) { 168 | start_parser = true; 169 | ongoing_transmission = false; 170 | } 171 | 172 | ++i; 173 | } 174 | 175 | debugs("' "); 176 | 177 | if (start_parser && !ongoing_transmission) { 178 | debugs("[EOT]"); 179 | } else if (!start_parser && ongoing_transmission) { 180 | debugs("..."); 181 | } else if (!start_parser && !ongoing_transmission) { 182 | debugs("DROPPED"); 183 | } 184 | 185 | debugln(); 186 | 187 | receive_buf.len = 0; 188 | } 189 | } 190 | 191 | bool hasData() { 192 | return data_buf.len > 0 && start_parser; 193 | } 194 | 195 | const buffer_t& getBuffer() { 196 | return data_buf; 197 | } 198 | 199 | void sendDone() { 200 | data_buf.len = 0; 201 | start_parser = false; 202 | serial_send_status(); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /atmega_duck/com.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file atmega_duck/com.h 3 | \brief Communication Module header 4 | \author Stefan Kremser 5 | \copyright MIT License 6 | */ 7 | 8 | #pragma once 9 | 10 | #include // size_t 11 | 12 | #include "config.h" // BUFFER_SIZE 13 | 14 | /*! \typedef buffer_t 15 | * \brief A structure to buffer data and simplify access for the communication 16 | */ 17 | typedef struct buffer_t { 18 | char data[BUFFER_SIZE]; // !< Array to buffer incoming bytes 19 | size_t len; // !< How many bytes are currently in the buffer 20 | } buffer_t; 21 | 22 | /*! \namespace com 23 | * \brief Communication module 24 | */ 25 | namespace com { 26 | /*! Initializes the communication module */ 27 | void begin(); 28 | 29 | /*! Updates the communication module */ 30 | void update(); 31 | 32 | /*! Returns whether or not there's data to be processed */ 33 | bool hasData(); 34 | 35 | /*! Returns reference to buffer */ 36 | const buffer_t& getBuffer(); 37 | 38 | /*! Sends acknowledgement that data was parsed and executed */ 39 | void sendDone(); 40 | } -------------------------------------------------------------------------------- /atmega_duck/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #define VERSION "1.2.0" 9 | 10 | /* ===== Serial Bridge ===== */ 11 | #define BRIDGE_ENABLE 12 | #define BRIDGE_PORT Serial1 13 | #define BRIDGE_SWITCH 13 14 | #define BRIDGE_RST 0 15 | #define BRIDGE_0 12 16 | // #define BRIDGE_0_INVERTED 17 | // #define BRIDGE_SAFE 18 | 19 | /*! DEBUG Settings */ 20 | // #define ENABLE_DEBUG 21 | // #define DEBUG_PORT Serial 22 | // #define DEBUG_BAUD 115200 23 | 24 | /*! ===== Communication Settings ===== */ 25 | #define ENABLE_SERIAL 26 | #define SERIAL_COM Serial1 27 | #define SERIAL_BAUD 9600 28 | 29 | // #define ENABLE_I2C 30 | #define I2C_ADDR 0x31 31 | 32 | #define BUFFER_SIZE 256 33 | #define PACKET_SIZE 32 34 | 35 | /*! ===== LED Settings ===== */ 36 | // #define NEOPIXEL 37 | // #define NEOPIXEL_NUM 1 38 | // #define LED_PIN 7 39 | 40 | // #define DOTSTAR 41 | // #define DOTSTAR_NUM 1 42 | // #define DOTSTAR_DI 7 43 | // #define DOTSTAR_CI 8 44 | 45 | // #define LED_RGB 46 | // #define LED_ANODE 47 | // #define LED_R 5 48 | // #define LED_G 6 49 | // #define LED_B 9 50 | 51 | // *! ===== Color Modes ===== */ 52 | #define COLOR_ESP_UNFLASHED 0, 0, 255 53 | 54 | /*! ===== Parser Settings ===== */ 55 | #define CASE_SENSETIVE false 56 | #define DEFAULT_SLEEP 5 57 | 58 | /*! ========== Safety Checks ========= */ 59 | #if !defined(ENABLE_I2C) && !defined(ENABLE_SERIAL) 60 | #define ENABLE_I2C 61 | #endif /* if !defined(ENABLE_I2C) && !defined(ENABLE_SERIAL) */ 62 | 63 | #if defined(BRIDGE_ENABLE) && !defined(ENABLE_SERIAL) 64 | #warning Serial bridge enabled, but serial communication disabled. Enabling serial again... 65 | #define ENABLE_SERIAL 66 | #endif /* if defined(BRIDGE_ENABLE) */ 67 | 68 | #if defined(BRIDGE_ENABLE) 69 | 70 | #if !defined(BRIDGE_PORT) 71 | #error Serial bridge port not defined! 72 | #endif /* if !defined(BRIDGE_PORT) */ 73 | 74 | #if !defined(BRIDGE_SWITCH) 75 | #error Serial bridge button not defined! 76 | #endif /* if !defined(BRIDGE_SWITCH) */ 77 | 78 | #if !defined(BRIDGE_RST) 79 | #error Serial bridge reset not defined! 80 | #endif /* if !defined(BRIDGE_RST) */ 81 | 82 | #if !defined(BRIDGE_0) 83 | #error Serial bridge GPIO-0 not defined! 84 | #endif /* if !defined(BRIDGE_0) */ 85 | 86 | #endif /* if defined(BRIDGE_ENABLE) */ 87 | 88 | #if defined(NEOPIXEL) 89 | 90 | #if defined(ENABLE_I2C) && (LED_PIN==2 || LED_PIN==3) 91 | #error Neopixel pin overlaps with I2C pins, disable I2C or change the LED pin! 92 | #endif /* if defined(ENABLE_I2C) && (LED_PIN==2 || LED_PIN==3) */ 93 | 94 | #if defined(ENABLE_SERIAL) && (LED_PIN==0 || LED_PIN==1) 95 | #error Neopixel pin overlaps with serial pins, disable serial or change the LED pin! 96 | #endif /* if defined(ENABLE_SERIAL) && (LED_PIN==0 || LED_PIN==1) */ 97 | 98 | #if defined(BRIDGE_ENABLE) && (LED_PIN==BRIDGE_RST || LED_PIN==BRIDGE_0 || LED_PIN==BRIDGE_SWITCH) 99 | #error Neopixel pin overlaps with serial bridge pins, disable serial bridge or change the LED pin! 100 | #endif /* if defined(BRIDGE_ENABLE) && (LED_PIN==BRIDGE_RST || LED_PIN==BRIDGE_0) */ 101 | 102 | #if defined(NEOPIXEL) && !defined(NEOPIXEL_NUM) 103 | #define NEOPIXEL_NUM 1 104 | #endif /* if defined(NEOPIXEL) && !defined(NEOPIXEL_NUM) */ 105 | 106 | #elif defined(DOTSTAR) 107 | 108 | #if !defined(DOTSTAR_DI) 109 | #error Dotstar DI pin not set! 110 | #endif /* if !defined(DOTSTAR_DI) */ 111 | 112 | #if !defined(DOTSTAR_CI) 113 | #error Dotstar CI pin not set! 114 | #endif /* if !defined(DOTSTAR_CI) */ 115 | 116 | // DI 117 | #if defined(ENABLE_I2C) && (DOTSTAR_DI==2 || DOTSTAR_DI==3) 118 | #error Dotstar DI pin overlaps with I2C pins, disable I2C or change the LED pin! 119 | #endif /* if defined(ENABLE_I2C) && (DOTSTAR_DI==2 || DOTSTAR_DI==3) */ 120 | 121 | #if defined(ENABLE_SERIAL) && (DOTSTAR_DI==0 || DOTSTAR_DI==1) 122 | #error Dotstar DI pin overlaps with serial pins, disable serial or change the LED pin! 123 | #endif /* if defined(ENABLE_SERIAL) && (DOTSTAR_DI==0 || DOTSTAR_DI==1) */ 124 | 125 | #if defined(BRIDGE_ENABLE) && (DOTSTAR_DI==BRIDGE_RST || DOTSTAR_DI==BRIDGE_0 || DOTSTAR_DI==BRIDGE_SWITCH) 126 | #error Dotstar DI pin overlaps with serial bridge pins, disable serial bridge or change the LED pin! 127 | #endif /* if defined(BRIDGE_ENABLE) && (DOTSTAR_DI==BRIDGE_RST || DOTSTAR_DI==BRIDGE_0 || DOTSTAR_DI==BRIDGE_SWITCH) */ 128 | 129 | // CI 130 | #if defined(ENABLE_I2C) && (DOTSTAR_CI==2 || DOTSTAR_CI==3) 131 | #error Dotstar CI pin overlaps with I2C pins, disable I2C or change the LED pin! 132 | #endif /* if defined(ENABLE_I2C) && (DOTSTAR_CI==2 || DOTSTAR_CI==3) */ 133 | 134 | #if defined(ENABLE_SERIAL) && (DOTSTAR_CI==0 || DOTSTAR_CI==1) 135 | #error Dotstar CI pin overlaps with serial pins, disable serial or change the LED pin! 136 | #endif /* if defined(ENABLE_SERIAL) && (DOTSTAR_CI==0 || DOTSTAR_CI==1) */ 137 | 138 | #if defined(BRIDGE_ENABLE) && (DOTSTAR_CI==BRIDGE_RST || DOTSTAR_CI==BRIDGE_0 || DOTSTAR_CI==BRIDGE_SWITCH) 139 | #error Dotstar CI pin overlaps with serial bridge pins, disable serial bridge or change the LED pin! 140 | #endif /* if defined(BRIDGE_ENABLE) && (DOTSTAR_CI==BRIDGE_RST || DOTSTAR_CI==BRIDGE_0 || DOTSTAR_CI==BRIDGE_SWITCH) */ 141 | 142 | #if defined(DOTSTAR) && !defined(DOTSTAR_NUM) 143 | #define DOTSTAR_NUM 1 144 | #endif /* if defined(DOTSTAR) && !defined(DOTSTAR_NUM) */ 145 | 146 | #endif /* if defined(NEOPIXEL) */ 147 | -------------------------------------------------------------------------------- /atmega_duck/debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #ifdef ENABLE_DEBUG 11 | 12 | #define debug_init() DEBUG_PORT.begin(DEBUG_BAUD); 13 | 14 | #define debugs(...) if (DEBUG_PORT) DEBUG_PORT.print(F(__VA_ARGS__)) 15 | #define debugsln(...) if (DEBUG_PORT) DEBUG_PORT.println(F(__VA_ARGS__)) 16 | 17 | #define debug(...) if (DEBUG_PORT) DEBUG_PORT.print(__VA_ARGS__) 18 | #define debugln(...) if (DEBUG_PORT) DEBUG_PORT.println(__VA_ARGS__) 19 | #define debugf(...) if (DEBUG_PORT) DEBUG_PORT.printf(__VA_ARGS__) 20 | 21 | #else /* ifdef ENABLE_DEBUG */ 22 | 23 | #define debug_init() 0 24 | 25 | #define debugs(...) 0 26 | #define debugsln(...) 0 27 | 28 | #define debug(...) 0 29 | #define debugln(...) 0 30 | #define debugf(...) 0 31 | 32 | #endif /* ifdef ENABLE_DEBUG */ -------------------------------------------------------------------------------- /atmega_duck/duckparser.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Stefan Kremser 3 | This software is licensed under the MIT License. See the license file for details. 4 | Source: github.com/spacehuhn/SimpleCLI 5 | */ 6 | 7 | #pragma once 8 | 9 | #include // size_t 10 | 11 | namespace duckparser { 12 | void parse(const char* str, size_t len); 13 | int getRepeats(); 14 | unsigned int getDelayTime(); 15 | }; -------------------------------------------------------------------------------- /atmega_duck/keyboard.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #include "keyboard.h" 7 | #include "debug.h" 8 | 9 | namespace keyboard { 10 | // ====== PRIVATE ====== // 11 | hid_locale_t* locale { &locale_us }; 12 | report prev_report = report { KEY_NONE, KEY_NONE, { KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE } }; 13 | 14 | const uint8_t keyboardDescriptor[] PROGMEM { 15 | // Keyboard 16 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) // 47 17 | 0x09, 0x06, // USAGE (Keyboard) 18 | 0xa1, 0x01, // COLLECTION (Application) 19 | 0x85, 0x02, // REPORT_ID (2) 20 | 0x05, 0x07, // USAGE_PAGE (Keyboard) 21 | 22 | 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) 23 | 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) 24 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 25 | 0x25, 0x01, // LOGICAL_MAXIMUM (1) 26 | 0x75, 0x01, // REPORT_SIZE (1) 27 | 28 | 0x95, 0x08, // REPORT_COUNT (8) 29 | 0x81, 0x02, // INPUT (Data,Var,Abs) 30 | 0x95, 0x01, // REPORT_COUNT (1) 31 | 0x75, 0x08, // REPORT_SIZE (8) 32 | 0x81, 0x03, // INPUT (Cnst,Var,Abs) 33 | 34 | 0x95, 0x06, // REPORT_COUNT (6) 35 | 0x75, 0x08, // REPORT_SIZE (8) 36 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 37 | 0x25, 0x73, // LOGICAL_MAXIMUM (115) 38 | 0x05, 0x07, // USAGE_PAGE (Keyboard) 39 | 40 | 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) 41 | 0x29, 0x73, // USAGE_MAXIMUM (Keyboard Application) 42 | 0x81, 0x00, // INPUT (Data,Ary,Abs) 43 | 0xc0, // END_COLLECTION 44 | }; 45 | 46 | report makeReport(uint8_t modifiers = 0, uint8_t key1 = 0, uint8_t key2 = 0, uint8_t key3 = 0, uint8_t key4 = 0, uint8_t key5 = 0, uint8_t key6 = 0); 47 | 48 | report makeReport(uint8_t modifiers, uint8_t key1, uint8_t key2, uint8_t key3, uint8_t key4, uint8_t key5, uint8_t key6) { 49 | report k; 50 | 51 | k.modifiers = modifiers; 52 | 53 | k.reserved = 0x00; 54 | 55 | k.keys[0] = key1; 56 | k.keys[1] = key2; 57 | k.keys[2] = key3; 58 | k.keys[3] = key4; 59 | k.keys[4] = key5; 60 | k.keys[5] = key6; 61 | 62 | return k; 63 | } 64 | 65 | // ====== PUBLIC ====== // 66 | void begin() { 67 | static HIDSubDescriptor node(keyboardDescriptor, sizeof(keyboardDescriptor)); 68 | 69 | HID().AppendDescriptor(&node); 70 | } 71 | 72 | void setLocale(hid_locale_t* locale) { 73 | keyboard::locale = locale; 74 | } 75 | 76 | void send(report* k) { 77 | #ifdef ENABLE_DEBUG 78 | debug("Sending Report ["); 79 | for (uint8_t i = 0; i<6; ++i) { 80 | debug(String(prev_report.keys[i], HEX)); 81 | debug(","); 82 | } 83 | debug("#"); 84 | debug(String(prev_report.modifiers, HEX)); 85 | debugln("]"); 86 | #endif // ENABLE_DEBUG 87 | HID().SendReport(2, (uint8_t*)k, sizeof(report)); 88 | } 89 | 90 | void release() { 91 | prev_report = makeReport(); 92 | send(&prev_report); 93 | } 94 | 95 | void pressKey(uint8_t key, uint8_t modifiers) { 96 | for (uint8_t i = 0; i<6; ++i) { 97 | if (prev_report.keys[i] == KEY_NONE) { 98 | prev_report.modifiers |= modifiers; 99 | prev_report.keys[i] = key; 100 | send(&prev_report); 101 | return; 102 | } 103 | } 104 | } 105 | 106 | void pressModifier(uint8_t key) { 107 | prev_report.modifiers |= key; 108 | 109 | send(&prev_report); 110 | } 111 | 112 | uint8_t press(const char* strPtr) { 113 | // Convert string pointer into a byte pointer 114 | uint8_t* b = (uint8_t*)strPtr; 115 | 116 | // Key combinations (accent keys) 117 | // We have to check them first, because sometimes ASCII keys are in here 118 | for (uint8_t i = 0; icombinations_len; ++i) { 119 | uint8_t res = 0; 120 | 121 | // Read utf8 code and match it with the given data 122 | for (uint8_t j = 0; j<4; ++j) { 123 | uint8_t key_code = pgm_read_byte(locale->combinations + (i * 8) + j); 124 | 125 | if (key_code == 0) { 126 | break; 127 | } 128 | 129 | if (key_code == b[j]) { 130 | ++res; 131 | } else { 132 | res = 0; 133 | break; 134 | } 135 | } 136 | 137 | // If a match was found, read out the data and type it 138 | if (res > 0) { 139 | uint8_t comboModifiers = pgm_read_byte(locale->combinations + (i * 8) + 4); 140 | uint8_t comboKey = pgm_read_byte(locale->combinations + (i * 8) + 5); 141 | 142 | uint8_t modifiers = pgm_read_byte(locale->combinations + (i * 8) + 6); 143 | uint8_t key = pgm_read_byte(locale->combinations + (i * 8) + 7); 144 | 145 | pressKey(comboKey, comboModifiers); 146 | release(); 147 | pressKey(key, modifiers); 148 | release(); 149 | 150 | // Return the number of extra bytes we used from the string pointer 151 | return res-1; 152 | } 153 | } 154 | 155 | // ASCII 156 | if (b[0] < locale->ascii_len) { 157 | uint8_t modifiers = pgm_read_byte(locale->ascii + (b[0] * 2) + 0); 158 | uint8_t key = pgm_read_byte(locale->ascii + (b[0] * 2) + 1); 159 | 160 | pressKey(key, modifiers); 161 | 162 | return 0; 163 | } 164 | 165 | // UTF8 166 | for (size_t i = 0; iutf8_len; ++i) { 167 | uint8_t res = 0; 168 | 169 | // Read utf8 code and match it with the given data 170 | for (uint8_t j = 0; j<4; ++j) { 171 | uint8_t key_code = pgm_read_byte(locale->utf8 + (i * 6) + j); 172 | 173 | if (key_code == 0) { 174 | break; 175 | } 176 | 177 | if (key_code == b[j]) { 178 | ++res; 179 | } else { 180 | res = 0; 181 | break; 182 | } 183 | } 184 | 185 | // If a match was found, read out the data and type it 186 | if (res > 0) { 187 | uint8_t modifiers = pgm_read_byte(locale->utf8 + (i * 6) + 4); 188 | uint8_t key = pgm_read_byte(locale->utf8 + (i * 6) + 5); 189 | 190 | pressKey(key, modifiers); 191 | 192 | // Return the number of extra bytes we used from the string pointer 193 | return res-1; 194 | } 195 | } 196 | 197 | return 0; 198 | } 199 | 200 | uint8_t write(const char* c) { 201 | uint8_t res = press(c); 202 | 203 | release(); 204 | 205 | return res; 206 | } 207 | 208 | void write(const char* str, size_t len) { 209 | for (size_t i = 0; i Board 10 | #include 11 | #include "locales.h" 12 | 13 | namespace keyboard { 14 | typedef struct report { 15 | uint8_t modifiers; 16 | uint8_t reserved; 17 | uint8_t keys[6]; 18 | } report; 19 | 20 | void begin(); 21 | 22 | void setLocale(hid_locale_t* locale); 23 | 24 | void send(report* k); 25 | void release(); 26 | 27 | void pressKey(uint8_t key, uint8_t modifiers = KEY_NONE); 28 | void pressModifier(uint8_t key); 29 | 30 | uint8_t press(const char* strPtr); 31 | 32 | uint8_t write(const char* c); 33 | void write(const char* str, size_t len); 34 | } -------------------------------------------------------------------------------- /atmega_duck/led.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #include "led.h" 7 | 8 | #include "config.h" 9 | 10 | #if defined(NEOPIXEL) 11 | 12 | #include "NeoPixel.h" 13 | 14 | namespace led { 15 | NeoPixel led { NEOPIXEL_NUM, LED_PIN, NEO_GRB + NEO_KHZ800 }; 16 | 17 | void begin() { 18 | led.begin(); 19 | led.show(); 20 | } 21 | 22 | void setColor(int r, int g, int b) { 23 | for (size_t i = 0; i 56 | 57 | void begin() { 58 | pinMode(LED_R, OUTPUT); 59 | pinMode(LED_G, OUTPUT); 60 | pinMode(LED_B, OUTPUT); 61 | } 62 | 63 | void setColor(int r, int g, int b) { 64 | #ifdef LED_ANODE 65 | r = 255 - r; 66 | g = 255 - g; 67 | b = 255 - b; 68 | #endif 69 | 70 | analogWrite(LED_R, r); 71 | analogWrite(LED_G, g); 72 | analogWrite(LED_B, b); 73 | } 74 | } 75 | 76 | #else // if defined(NEOPIXEL) 77 | 78 | namespace led { 79 | void begin() {} 80 | 81 | void setColor(int r, int g, int b) {} 82 | } 83 | 84 | #endif // if defined(NEOPIXEL) -------------------------------------------------------------------------------- /atmega_duck/led.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | namespace led { 9 | void begin(); 10 | void setColor(int r, int g, int b); 11 | } -------------------------------------------------------------------------------- /atmega_duck/locale_de.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "usb_hid_keys.h" 9 | 10 | // Modifier(s), Key 11 | const uint8_t ascii_de[] PROGMEM = { 12 | KEY_NONE, KEY_NONE, // NUL 13 | KEY_NONE, KEY_NONE, // SOH 14 | KEY_NONE, KEY_NONE, // STX 15 | KEY_NONE, KEY_NONE, // ETX 16 | KEY_NONE, KEY_NONE, // EOT 17 | KEY_NONE, KEY_NONE, // ENQ 18 | KEY_NONE, KEY_NONE, // ACK 19 | KEY_NONE, KEY_NONE, // BEL 20 | 21 | // 8, 0x08 22 | KEY_NONE, KEY_BACKSPACE, // BS Backspace 23 | KEY_NONE, KEY_TAB, // TAB Tab 24 | KEY_NONE, KEY_ENTER, // LF Enter 25 | 26 | KEY_NONE, KEY_NONE, // VT 27 | KEY_NONE, KEY_NONE, // FF 28 | KEY_NONE, KEY_NONE, // CR 29 | KEY_NONE, KEY_NONE, // SO 30 | KEY_NONE, KEY_NONE, // SI 31 | KEY_NONE, KEY_NONE, // DEL 32 | KEY_NONE, KEY_NONE, // DC1 33 | KEY_NONE, KEY_NONE, // DC2 34 | KEY_NONE, KEY_NONE, // DC3 35 | KEY_NONE, KEY_NONE, // DC4 36 | KEY_NONE, KEY_NONE, // NAK 37 | KEY_NONE, KEY_NONE, // SYN 38 | KEY_NONE, KEY_NONE, // ETB 39 | KEY_NONE, KEY_NONE, // CAN 40 | KEY_NONE, KEY_NONE, // EM 41 | KEY_NONE, KEY_NONE, // SUB 42 | KEY_NONE, KEY_NONE, // ESC 43 | KEY_NONE, KEY_NONE, // FS 44 | KEY_NONE, KEY_NONE, // GS 45 | KEY_NONE, KEY_NONE, // RS 46 | KEY_NONE, KEY_NONE, // US 47 | 48 | // 32, 0x20 49 | KEY_NONE, KEY_SPACE, // ' ' 50 | KEY_MOD_LSHIFT, KEY_1, // ! 51 | KEY_MOD_LSHIFT, KEY_2, // " 52 | KEY_NONE, KEY_BACKSLASH, // # 53 | 54 | // 36, 0x24 55 | KEY_MOD_LSHIFT, KEY_4, // $ 56 | KEY_MOD_LSHIFT, KEY_5, // % 57 | KEY_MOD_LSHIFT, KEY_6, // & 58 | KEY_MOD_LSHIFT, KEY_BACKSLASH, // ' 59 | 60 | // 40, 0x28 61 | KEY_MOD_LSHIFT, KEY_8, // ( 62 | KEY_MOD_LSHIFT, KEY_9, // ) 63 | KEY_MOD_LSHIFT, KEY_RIGHTBRACE, // * 64 | KEY_NONE, KEY_RIGHTBRACE, // + 65 | 66 | // 44, 0x2c 67 | KEY_NONE, KEY_COMMA, // , 68 | KEY_NONE, KEY_SLASH, // - 69 | KEY_NONE, KEY_DOT, // . 70 | KEY_MOD_LSHIFT, KEY_7, // / 71 | 72 | // 48, 0x30 73 | KEY_NONE, KEY_0, // 0 74 | KEY_NONE, KEY_1, // 1 75 | KEY_NONE, KEY_2, // 2 76 | KEY_NONE, KEY_3, // 3 77 | 78 | // 52, 0x34 79 | KEY_NONE, KEY_4, // 4 80 | KEY_NONE, KEY_5, // 5 81 | KEY_NONE, KEY_6, // 6 82 | KEY_NONE, KEY_7, // 7 83 | 84 | // 56, 0x38 85 | KEY_NONE, KEY_8, // 8 86 | KEY_NONE, KEY_9, // 9 87 | KEY_MOD_LSHIFT, KEY_DOT, // : 88 | KEY_MOD_LSHIFT, KEY_COMMA, // ; 89 | 90 | // 60, 0x3c 91 | KEY_NONE, KEY_102ND, // < 92 | KEY_MOD_LSHIFT, KEY_0, // = 93 | KEY_MOD_LSHIFT, KEY_102ND, // > 94 | KEY_MOD_LSHIFT, KEY_MINUS, // ? 95 | 96 | // 64, 0x40 97 | KEY_MOD_RALT, KEY_Q, // @ 98 | KEY_MOD_LSHIFT, KEY_A, // A 99 | KEY_MOD_LSHIFT, KEY_B, // B 100 | KEY_MOD_LSHIFT, KEY_C, // C 101 | 102 | // 68, 0x44 103 | KEY_MOD_LSHIFT, KEY_D, // D 104 | KEY_MOD_LSHIFT, KEY_E, // E 105 | KEY_MOD_LSHIFT, KEY_F, // F 106 | KEY_MOD_LSHIFT, KEY_G, // G 107 | 108 | // 72, 0x48 109 | KEY_MOD_LSHIFT, KEY_H, // H 110 | KEY_MOD_LSHIFT, KEY_I, // I 111 | KEY_MOD_LSHIFT, KEY_J, // J 112 | KEY_MOD_LSHIFT, KEY_K, // K 113 | 114 | // 76, 0x4c 115 | KEY_MOD_LSHIFT, KEY_L, // L 116 | KEY_MOD_LSHIFT, KEY_M, // M 117 | KEY_MOD_LSHIFT, KEY_N, // N 118 | KEY_MOD_LSHIFT, KEY_O, // O 119 | 120 | // 80, 0x50 121 | KEY_MOD_LSHIFT, KEY_P, // P 122 | KEY_MOD_LSHIFT, KEY_Q, // Q 123 | KEY_MOD_LSHIFT, KEY_R, // R 124 | KEY_MOD_LSHIFT, KEY_S, // S 125 | 126 | // 84, 0x54 127 | KEY_MOD_LSHIFT, KEY_T, // T 128 | KEY_MOD_LSHIFT, KEY_U, // U 129 | KEY_MOD_LSHIFT, KEY_V, // V 130 | KEY_MOD_LSHIFT, KEY_W, // W 131 | 132 | // 88, 0x58 133 | KEY_MOD_LSHIFT, KEY_X, // X 134 | KEY_MOD_LSHIFT, KEY_Z, // Y 135 | KEY_MOD_LSHIFT, KEY_Y, // Z 136 | KEY_MOD_RALT, KEY_8, // [ 137 | 138 | // 92, 0x5c 139 | KEY_MOD_RALT, KEY_MINUS, // bslash 140 | KEY_MOD_RALT, KEY_9, // ] 141 | KEY_NONE, KEY_SPACE, // ^ 142 | KEY_MOD_LSHIFT, KEY_SLASH, // _ 143 | 144 | // 96, 0x60 145 | KEY_NONE, KEY_SPACE, // ` 146 | KEY_NONE, KEY_A, // a 147 | KEY_NONE, KEY_B, // b 148 | KEY_NONE, KEY_C, // c 149 | 150 | // 100, 0x64 151 | KEY_NONE, KEY_D, // d 152 | KEY_NONE, KEY_E, // e 153 | KEY_NONE, KEY_F, // f 154 | KEY_NONE, KEY_G, // g 155 | 156 | // 104, 0x68 157 | KEY_NONE, KEY_H, // h 158 | KEY_NONE, KEY_I, // i 159 | KEY_NONE, KEY_J, // j 160 | KEY_NONE, KEY_K, // k 161 | 162 | // 108, 0x6c 163 | KEY_NONE, KEY_L, // l 164 | KEY_NONE, KEY_M, // m 165 | KEY_NONE, KEY_N, // n 166 | KEY_NONE, KEY_O, // o 167 | 168 | // 112, 0x70 169 | KEY_NONE, KEY_P, // p 170 | KEY_NONE, KEY_Q, // q 171 | KEY_NONE, KEY_R, // r 172 | KEY_NONE, KEY_S, // s 173 | 174 | // 116, 0x74 175 | KEY_NONE, KEY_T, // t 176 | KEY_NONE, KEY_U, // u 177 | KEY_NONE, KEY_V, // v 178 | KEY_NONE, KEY_W, // w 179 | 180 | // 120, 0x78 181 | KEY_NONE, KEY_X, // x 182 | KEY_NONE, KEY_Z, // y 183 | KEY_NONE, KEY_Y, // z 184 | KEY_MOD_RALT, KEY_7, // { 185 | 186 | // 124, 0x7c 187 | KEY_MOD_RALT, KEY_102ND, // | 188 | KEY_MOD_RALT, KEY_0, // } 189 | KEY_MOD_RALT, KEY_RIGHTBRACE, // ~ 190 | KEY_NONE, KEY_DELETE // DEL 191 | }; 192 | 193 | const uint8_t utf8_de[] PROGMEM = { 194 | 0xC2, 0xA7, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_3, // § 195 | 0xC2, 0xB0, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_GRAVE, // ° 196 | 0xC2, 0xB2, 0x00, 0x00, KEY_MOD_RALT, KEY_2, // ² 197 | 0xC2, 0xB3, 0x00, 0x00, KEY_MOD_RALT, KEY_3, // ³ 198 | 0xC3, 0x84, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_APOSTROPHE, // Ä 199 | 0xC3, 0x96, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_SEMICOLON, // Ö 200 | 0xC3, 0x9C, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, // Ü 201 | 0xC3, 0x9F, 0x00, 0x00, KEY_NONE, KEY_MINUS, // ß 202 | 0xC3, 0xA4, 0x00, 0x00, KEY_NONE, KEY_APOSTROPHE, // ä 203 | 0xC3, 0xB6, 0x00, 0x00, KEY_NONE, KEY_SEMICOLON, // ö 204 | 0xC3, 0xBC, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, // ü 205 | 0xE2, 0x82, 0xAC, 0x00, KEY_MOD_RALT, KEY_E, // € 206 | }; 207 | 208 | const uint8_t combinations_de[] PROGMEM = { 209 | 0x5E, 0x00, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_NONE, KEY_SPACE, // ^ 210 | 0x60, 0x00, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_EQUAL, KEY_NONE, KEY_SPACE, // ` 211 | 0xC2, 0xB4, 0x00, 0x00, KEY_NONE, KEY_EQUAL, KEY_NONE, KEY_SPACE, // ´ 212 | 0xC3, 0x88, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_EQUAL, KEY_MOD_LSHIFT, KEY_E, // È 213 | 0xC3, 0x89, 0x00, 0x00, KEY_NONE, KEY_EQUAL, KEY_MOD_LSHIFT, KEY_E, // É 214 | 0xC3, 0x8A, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_MOD_LSHIFT, KEY_E, // Ê 215 | 0xC3, 0x8C, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_EQUAL, KEY_MOD_LSHIFT, KEY_I, // Ì 216 | 0xC3, 0x8D, 0x00, 0x00, KEY_NONE, KEY_EQUAL, KEY_MOD_LSHIFT, KEY_I, // Í 217 | 0xC3, 0x8E, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_MOD_LSHIFT, KEY_I, // Î 218 | 0xC3, 0x92, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_EQUAL, KEY_MOD_LSHIFT, KEY_O, // Ò 219 | 0xC3, 0x93, 0x00, 0x00, KEY_NONE, KEY_EQUAL, KEY_MOD_LSHIFT, KEY_O, // Ó 220 | 0xC3, 0x94, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_MOD_LSHIFT, KEY_O, // Ô 221 | 0xC3, 0x99, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_EQUAL, KEY_MOD_LSHIFT, KEY_U, // Ù 222 | 0xC3, 0x9A, 0x00, 0x00, KEY_NONE, KEY_EQUAL, KEY_MOD_LSHIFT, KEY_U, // Ú 223 | 0xC3, 0x9B, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_MOD_LSHIFT, KEY_U, // Û 224 | 0xC3, 0x9D, 0x00, 0x00, KEY_NONE, KEY_EQUAL, KEY_MOD_LSHIFT, KEY_Z, // Ý 225 | 0xC3, 0xA8, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_EQUAL, KEY_NONE, KEY_E, // è 226 | 0xC3, 0xA9, 0x00, 0x00, KEY_NONE, KEY_EQUAL, KEY_NONE, KEY_E, // é 227 | 0xC3, 0xAA, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_NONE, KEY_E, // ê 228 | 0xC3, 0xAC, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_EQUAL, KEY_NONE, KEY_I, // ì 229 | 0xC3, 0xAD, 0x00, 0x00, KEY_NONE, KEY_EQUAL, KEY_NONE, KEY_I, // í 230 | 0xC3, 0xAE, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_NONE, KEY_I, // î 231 | 0xC3, 0xB2, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_EQUAL, KEY_NONE, KEY_O, // ò 232 | 0xC3, 0xB3, 0x00, 0x00, KEY_NONE, KEY_EQUAL, KEY_NONE, KEY_O, // ó 233 | 0xC3, 0xB4, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_NONE, KEY_O, // ô 234 | 0xC3, 0xB9, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_EQUAL, KEY_NONE, KEY_U, // ù 235 | 0xC3, 0xBA, 0x00, 0x00, KEY_NONE, KEY_EQUAL, KEY_NONE, KEY_U, // ú 236 | 0xC3, 0xBB, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_NONE, KEY_U, // û 237 | 0xC3, 0xBD, 0x00, 0x00, KEY_NONE, KEY_EQUAL, KEY_NONE, KEY_Z, // ý 238 | }; 239 | 240 | static hid_locale_t locale_de { 241 | (uint8_t*)ascii_de, 128, 242 | (uint8_t*)utf8_de, sizeof(utf8_de) / 6, 243 | (uint8_t*)combinations_de, sizeof(combinations_de) / 8, 244 | }; -------------------------------------------------------------------------------- /atmega_duck/locale_fr.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "usb_hid_keys.h" 9 | 10 | // Modifier(s), Key 11 | const uint8_t ascii_fr[] PROGMEM = { 12 | KEY_NONE, KEY_NONE, // NUL 13 | KEY_NONE, KEY_NONE, // SOH 14 | KEY_NONE, KEY_NONE, // STX 15 | KEY_NONE, KEY_NONE, // ETX 16 | KEY_NONE, KEY_NONE, // EOT 17 | KEY_NONE, KEY_NONE, // ENQ 18 | KEY_NONE, KEY_NONE, // ACK 19 | KEY_NONE, KEY_NONE, // BEL 20 | 21 | // 8, 0x08 22 | KEY_NONE, KEY_BACKSPACE, // BS Backspace 23 | KEY_NONE, KEY_TAB, // TAB Tab 24 | KEY_NONE, KEY_ENTER, // LF Enter 25 | 26 | KEY_NONE, KEY_NONE, // VT 27 | KEY_NONE, KEY_NONE, // FF 28 | KEY_NONE, KEY_NONE, // CR 29 | KEY_NONE, KEY_NONE, // SO 30 | KEY_NONE, KEY_NONE, // SI 31 | KEY_NONE, KEY_NONE, // DEL 32 | KEY_NONE, KEY_NONE, // DC1 33 | KEY_NONE, KEY_NONE, // DC2 34 | KEY_NONE, KEY_NONE, // DC3 35 | KEY_NONE, KEY_NONE, // DC4 36 | KEY_NONE, KEY_NONE, // NAK 37 | KEY_NONE, KEY_NONE, // SYN 38 | KEY_NONE, KEY_NONE, // ETB 39 | KEY_NONE, KEY_NONE, // CAN 40 | KEY_NONE, KEY_NONE, // EM 41 | KEY_NONE, KEY_NONE, // SUB 42 | KEY_NONE, KEY_NONE, // ESC 43 | KEY_NONE, KEY_NONE, // FS 44 | KEY_NONE, KEY_NONE, // GS 45 | KEY_NONE, KEY_NONE, // RS 46 | KEY_NONE, KEY_NONE, // US 47 | 48 | // 32, 0x20 49 | KEY_NONE, KEY_SPACE, // ' ' 50 | KEY_NONE, KEY_SLASH, // ! 51 | KEY_NONE, KEY_3, // " 52 | KEY_MOD_RALT, KEY_3, // # 53 | 54 | // 36, 0x24 55 | KEY_NONE, KEY_RIGHTBRACE, // $ 56 | KEY_MOD_LSHIFT, KEY_APOSTROPHE, // % 57 | KEY_NONE, KEY_1, // & 58 | KEY_NONE, KEY_4, // ' 59 | 60 | // 40, 0x28 61 | KEY_NONE, KEY_5, // ( 62 | KEY_NONE, KEY_MINUS, // ) 63 | KEY_NONE, KEY_BACKSLASH, // * 64 | KEY_MOD_LSHIFT, KEY_EQUAL, // + 65 | 66 | // 44, 0x2c 67 | KEY_NONE, KEY_M, // , 68 | KEY_NONE, KEY_6, // - 69 | KEY_MOD_LSHIFT, KEY_COMMA, // . 70 | KEY_MOD_LSHIFT, KEY_DOT, // / 71 | 72 | // 48, 0x30 73 | KEY_MOD_LSHIFT, KEY_0, // 0 74 | KEY_MOD_LSHIFT, KEY_1, // 1 75 | KEY_MOD_LSHIFT, KEY_2, // 2 76 | KEY_MOD_LSHIFT, KEY_3, // 3 77 | 78 | // 52, 0x34 79 | KEY_MOD_LSHIFT, KEY_4, // 4 80 | KEY_MOD_LSHIFT, KEY_5, // 5 81 | KEY_MOD_LSHIFT, KEY_6, // 6 82 | KEY_MOD_LSHIFT, KEY_7, // 7 83 | 84 | // 56, 0x38 85 | KEY_MOD_LSHIFT, KEY_8, // 8 86 | KEY_MOD_LSHIFT, KEY_9, // 9 87 | KEY_NONE, KEY_DOT, // : 88 | KEY_NONE, KEY_COMMA, // ; 89 | 90 | // 60, 0x3c 91 | KEY_NONE, KEY_102ND, // < 92 | KEY_NONE, KEY_EQUAL, // = 93 | KEY_MOD_LSHIFT, KEY_102ND, // > 94 | KEY_MOD_LSHIFT, KEY_M, // ? 95 | 96 | // 64, 0x40 97 | KEY_MOD_RALT, KEY_0, // @ 98 | KEY_MOD_LSHIFT, KEY_Q, // A 99 | KEY_MOD_LSHIFT, KEY_B, // B 100 | KEY_MOD_LSHIFT, KEY_C, // C 101 | 102 | // 68, 0x44 103 | KEY_MOD_LSHIFT, KEY_D, // D 104 | KEY_MOD_LSHIFT, KEY_E, // E 105 | KEY_MOD_LSHIFT, KEY_F, // F 106 | KEY_MOD_LSHIFT, KEY_G, // G 107 | 108 | // 72, 0x48 109 | KEY_MOD_LSHIFT, KEY_H, // H 110 | KEY_MOD_LSHIFT, KEY_I, // I 111 | KEY_MOD_LSHIFT, KEY_J, // J 112 | KEY_MOD_LSHIFT, KEY_K, // K 113 | 114 | // 76, 0x4c 115 | KEY_MOD_LSHIFT, KEY_L, // L 116 | KEY_MOD_LSHIFT, KEY_SEMICOLON, // M 117 | KEY_MOD_LSHIFT, KEY_N, // N 118 | KEY_MOD_LSHIFT, KEY_O, // O 119 | 120 | // 80, 0x50 121 | KEY_MOD_LSHIFT, KEY_P, // P 122 | KEY_MOD_LSHIFT, KEY_A, // Q 123 | KEY_MOD_LSHIFT, KEY_R, // R 124 | KEY_MOD_LSHIFT, KEY_S, // S 125 | 126 | // 84, 0x54 127 | KEY_MOD_LSHIFT, KEY_T, // T 128 | KEY_MOD_LSHIFT, KEY_U, // U 129 | KEY_MOD_LSHIFT, KEY_V, // V 130 | KEY_MOD_LSHIFT, KEY_Z, // W 131 | 132 | // 88, 0x58 133 | KEY_MOD_LSHIFT, KEY_X, // X 134 | KEY_MOD_LSHIFT, KEY_Y, // Y 135 | KEY_MOD_LSHIFT, KEY_W, // Z 136 | KEY_MOD_RALT, KEY_5, // [ 137 | 138 | // 92, 0x5c 139 | KEY_MOD_RALT, KEY_8, // bslash 140 | KEY_MOD_RALT, KEY_MINUS, // ] 141 | KEY_MOD_RALT, KEY_9, // ^ 142 | KEY_NONE, KEY_8, // _ 143 | 144 | // 96, 0x60 145 | KEY_NONE, KEY_SPACE, // ` 146 | KEY_NONE, KEY_Q, // a 147 | KEY_NONE, KEY_B, // b 148 | KEY_NONE, KEY_C, // c 149 | 150 | // 100, 0x64 151 | KEY_NONE, KEY_D, // d 152 | KEY_NONE, KEY_E, // e 153 | KEY_NONE, KEY_F, // f 154 | KEY_NONE, KEY_G, // g 155 | 156 | // 104, 0x68 157 | KEY_NONE, KEY_H, // h 158 | KEY_NONE, KEY_I, // i 159 | KEY_NONE, KEY_J, // j 160 | KEY_NONE, KEY_K, // k 161 | 162 | // 108, 0x6c 163 | KEY_NONE, KEY_L, // l 164 | KEY_NONE, KEY_SEMICOLON, // m 165 | KEY_NONE, KEY_N, // n 166 | KEY_NONE, KEY_O, // o 167 | 168 | // 112, 0x70 169 | KEY_NONE, KEY_P, // p 170 | KEY_NONE, KEY_A, // q 171 | KEY_NONE, KEY_R, // r 172 | KEY_NONE, KEY_S, // s 173 | 174 | // 116, 0x74 175 | KEY_NONE, KEY_T, // t 176 | KEY_NONE, KEY_U, // u 177 | KEY_NONE, KEY_V, // v 178 | KEY_NONE, KEY_Z, // w 179 | 180 | // 120, 0x78 181 | KEY_NONE, KEY_X, // x 182 | KEY_NONE, KEY_Y, // y 183 | KEY_NONE, KEY_W, // z 184 | KEY_MOD_RALT, KEY_4, // { 185 | 186 | // 124, 0x7c 187 | KEY_MOD_RALT, KEY_6, // | 188 | KEY_MOD_RALT, KEY_EQUAL, // } 189 | KEY_NONE, KEY_SPACE, // ~ 190 | KEY_NONE, KEY_DELETE // DEL 191 | }; 192 | 193 | const uint8_t utf8_fr[] PROGMEM = { 194 | 0xC2, 0xA3, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_RIGHTBRACE, // £ 195 | 0xC2, 0xA4, 0x00, 0x00, KEY_MOD_RALT, KEY_RIGHTBRACE, // ¤ 196 | 0xC2, 0xA7, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_SLASH, // § 197 | 0xC2, 0xB0, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_MINUS, // ° 198 | 0xC2, 0xB2, 0x00, 0x00, KEY_NONE, KEY_GRAVE, // ² 199 | 0xC2, 0xB5, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_BACKSLASH, // µ 200 | 0xC3, 0xA0, 0x00, 0x00, KEY_NONE, KEY_0, // à 201 | 0xC3, 0xA7, 0x00, 0x00, KEY_NONE, KEY_9, // ç 202 | 0xC3, 0xA8, 0x00, 0x00, KEY_NONE, KEY_7, // è 203 | 0xC3, 0xA9, 0x00, 0x00, KEY_NONE, KEY_2, // é 204 | 0xC3, 0xB9, 0x00, 0x00, KEY_NONE, KEY_APOSTROPHE, // ù 205 | 0xE2, 0x82, 0xAC, 0x00, KEY_MOD_RALT, KEY_E, // € 206 | }; 207 | 208 | const uint8_t combinations_fr[] PROGMEM = { 209 | 0x60, 0x00, 0x00, 0x00, KEY_MOD_RALT, KEY_7, KEY_NONE, KEY_SPACE, // ` 210 | 0x7E, 0x00, 0x00, 0x00, KEY_MOD_RALT, KEY_2, KEY_NONE, KEY_SPACE, // ~ 211 | 0xC2, 0xA8, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, KEY_NONE, KEY_SPACE, // ¨ 212 | 0xC3, 0x80, 0x00, 0x00, KEY_MOD_RALT, KEY_7, KEY_MOD_LSHIFT, KEY_Q, // À 213 | 0xC3, 0x82, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, KEY_MOD_LSHIFT, KEY_Q, //  214 | 0xC3, 0x83, 0x00, 0x00, KEY_MOD_RALT, KEY_2, KEY_MOD_LSHIFT, KEY_Q, // à 215 | 0xC3, 0x84, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, KEY_MOD_LSHIFT, KEY_Q, // Ä 216 | 0xC3, 0x88, 0x00, 0x00, KEY_MOD_RALT, KEY_7, KEY_MOD_LSHIFT, KEY_E, // È 217 | 0xC3, 0x8A, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, KEY_MOD_LSHIFT, KEY_E, // Ê 218 | 0xC3, 0x8B, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, KEY_MOD_LSHIFT, KEY_E, // Ë 219 | 0xC3, 0x8C, 0x00, 0x00, KEY_MOD_RALT, KEY_7, KEY_MOD_LSHIFT, KEY_I, // Ì 220 | 0xC3, 0x8E, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, KEY_MOD_LSHIFT, KEY_I, // Î 221 | 0xC3, 0x8F, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, KEY_MOD_LSHIFT, KEY_I, // Ï 222 | 0xC3, 0x91, 0x00, 0x00, KEY_MOD_RALT, KEY_2, KEY_MOD_LSHIFT, KEY_N, // Ñ 223 | 0xC3, 0x92, 0x00, 0x00, KEY_MOD_RALT, KEY_7, KEY_MOD_LSHIFT, KEY_O, // Ò 224 | 0xC3, 0x94, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, KEY_MOD_LSHIFT, KEY_O, // Ô 225 | 0xC3, 0x95, 0x00, 0x00, KEY_MOD_RALT, KEY_2, KEY_MOD_LSHIFT, KEY_O, // Õ 226 | 0xC3, 0x96, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, KEY_MOD_LSHIFT, KEY_O, // Ö 227 | 0xC3, 0x99, 0x00, 0x00, KEY_MOD_RALT, KEY_7, KEY_MOD_LSHIFT, KEY_U, // Ù 228 | 0xC3, 0x9B, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, KEY_MOD_LSHIFT, KEY_U, // Û 229 | 0xC3, 0x9C, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, KEY_MOD_LSHIFT, KEY_U, // Ü 230 | 0xC3, 0xA2, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, KEY_NONE, KEY_Q, // â 231 | 0xC3, 0xA3, 0x00, 0x00, KEY_MOD_RALT, KEY_2, KEY_NONE, KEY_Q, // ã 232 | 0xC3, 0xA4, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, KEY_NONE, KEY_Q, // ä 233 | 0xC3, 0xAA, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, KEY_NONE, KEY_E, // ê 234 | 0xC3, 0xAB, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, KEY_NONE, KEY_E, // ë 235 | 0xC3, 0xAC, 0x00, 0x00, KEY_MOD_RALT, KEY_7, KEY_NONE, KEY_I, // ì 236 | 0xC3, 0xAE, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, KEY_NONE, KEY_I, // î 237 | 0xC3, 0xAF, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, KEY_NONE, KEY_I, // ï 238 | 0xC3, 0xB1, 0x00, 0x00, KEY_MOD_RALT, KEY_2, KEY_NONE, KEY_N, // ñ 239 | 0xC3, 0xB2, 0x00, 0x00, KEY_MOD_RALT, KEY_7, KEY_NONE, KEY_O, // ò 240 | 0xC3, 0xB4, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, KEY_NONE, KEY_O, // ô 241 | 0xC3, 0xB5, 0x00, 0x00, KEY_MOD_RALT, KEY_2, KEY_NONE, KEY_O, // õ 242 | 0xC3, 0xB6, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, KEY_NONE, KEY_O, // ö 243 | 0xC3, 0xBB, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, KEY_NONE, KEY_U, // û 244 | 0xC3, 0xBC, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, KEY_NONE, KEY_U, // ü 245 | }; 246 | 247 | static hid_locale_t locale_fr { 248 | (uint8_t*)ascii_fr, 128, 249 | (uint8_t*)utf8_fr, sizeof(utf8_fr) / 6, 250 | (uint8_t*)combinations_fr, sizeof(combinations_fr) / 8, 251 | }; -------------------------------------------------------------------------------- /atmega_duck/locale_gb.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "usb_hid_keys.h" 9 | 10 | // Modifier(s), Key 11 | const uint8_t ascii_gb[] PROGMEM = { 12 | KEY_NONE, KEY_NONE, // NUL 13 | KEY_NONE, KEY_NONE, // SOH 14 | KEY_NONE, KEY_NONE, // STX 15 | KEY_NONE, KEY_NONE, // ETX 16 | KEY_NONE, KEY_NONE, // EOT 17 | KEY_NONE, KEY_NONE, // ENQ 18 | KEY_NONE, KEY_NONE, // ACK 19 | KEY_NONE, KEY_NONE, // BEL 20 | 21 | // 8, 0x08 22 | KEY_NONE, KEY_BACKSPACE, // BS Backspace 23 | KEY_NONE, KEY_TAB, // TAB Tab 24 | KEY_NONE, KEY_ENTER, // LF Enter 25 | 26 | KEY_NONE, KEY_NONE, // VT 27 | KEY_NONE, KEY_NONE, // FF 28 | KEY_NONE, KEY_NONE, // CR 29 | KEY_NONE, KEY_NONE, // SO 30 | KEY_NONE, KEY_NONE, // SI 31 | KEY_NONE, KEY_NONE, // DEL 32 | KEY_NONE, KEY_NONE, // DC1 33 | KEY_NONE, KEY_NONE, // DC2 34 | KEY_NONE, KEY_NONE, // DC3 35 | KEY_NONE, KEY_NONE, // DC4 36 | KEY_NONE, KEY_NONE, // NAK 37 | KEY_NONE, KEY_NONE, // SYN 38 | KEY_NONE, KEY_NONE, // ETB 39 | KEY_NONE, KEY_NONE, // CAN 40 | KEY_NONE, KEY_NONE, // EM 41 | KEY_NONE, KEY_NONE, // SUB 42 | KEY_NONE, KEY_NONE, // ESC 43 | KEY_NONE, KEY_NONE, // FS 44 | KEY_NONE, KEY_NONE, // GS 45 | KEY_NONE, KEY_NONE, // RS 46 | KEY_NONE, KEY_NONE, // US 47 | 48 | // 32, 0x20 49 | KEY_NONE, KEY_SPACE, // ' ' 50 | KEY_MOD_LSHIFT, KEY_1, // ! 51 | KEY_MOD_LSHIFT, KEY_2, // " 52 | KEY_NONE, KEY_BACKSLASH, // # 53 | 54 | // 36, 0x24 55 | KEY_MOD_LSHIFT, KEY_4, // $ 56 | KEY_MOD_LSHIFT, KEY_5, // % 57 | KEY_MOD_LSHIFT, KEY_7, // & 58 | KEY_NONE, KEY_APOSTROPHE, // ' 59 | 60 | // 40, 0x28 61 | KEY_MOD_LSHIFT, KEY_9, // ( 62 | KEY_MOD_LSHIFT, KEY_0, // ) 63 | KEY_MOD_LSHIFT, KEY_8, // * 64 | KEY_MOD_LSHIFT, KEY_EQUAL, // + 65 | 66 | // 44, 0x2c 67 | KEY_NONE, KEY_COMMA, // , 68 | KEY_NONE, KEY_MINUS, // - 69 | KEY_NONE, KEY_DOT, // . 70 | KEY_NONE, KEY_SLASH, // / 71 | 72 | // 48, 0x30 73 | KEY_NONE, KEY_0, // 0 74 | KEY_NONE, KEY_1, // 1 75 | KEY_NONE, KEY_2, // 2 76 | KEY_NONE, KEY_3, // 3 77 | 78 | // 52, 0x34 79 | KEY_NONE, KEY_4, // 4 80 | KEY_NONE, KEY_5, // 5 81 | KEY_NONE, KEY_6, // 6 82 | KEY_NONE, KEY_7, // 7 83 | 84 | // 56, 0x38 85 | KEY_NONE, KEY_8, // 8 86 | KEY_NONE, KEY_9, // 9 87 | KEY_MOD_LSHIFT, KEY_SEMICOLON, // : 88 | KEY_NONE, KEY_SEMICOLON, // ; 89 | 90 | // 60, 0x3c 91 | KEY_MOD_LSHIFT, KEY_COMMA, // < 92 | KEY_NONE, KEY_EQUAL, // = 93 | KEY_MOD_LSHIFT, KEY_DOT, // > 94 | KEY_MOD_LSHIFT, KEY_SLASH, // ? 95 | 96 | // 64, 0x40 97 | KEY_MOD_LSHIFT, KEY_APOSTROPHE, // @ 98 | KEY_MOD_LSHIFT, KEY_A, // A 99 | KEY_MOD_LSHIFT, KEY_B, // B 100 | KEY_MOD_LSHIFT, KEY_C, // C 101 | 102 | // 68, 0x44 103 | KEY_MOD_LSHIFT, KEY_D, // D 104 | KEY_MOD_LSHIFT, KEY_E, // E 105 | KEY_MOD_LSHIFT, KEY_F, // F 106 | KEY_MOD_LSHIFT, KEY_G, // G 107 | 108 | // 72, 0x48 109 | KEY_MOD_LSHIFT, KEY_H, // H 110 | KEY_MOD_LSHIFT, KEY_I, // I 111 | KEY_MOD_LSHIFT, KEY_J, // J 112 | KEY_MOD_LSHIFT, KEY_K, // K 113 | 114 | // 76, 0x4c 115 | KEY_MOD_LSHIFT, KEY_L, // L 116 | KEY_MOD_LSHIFT, KEY_M, // M 117 | KEY_MOD_LSHIFT, KEY_N, // N 118 | KEY_MOD_LSHIFT, KEY_O, // O 119 | 120 | // 80, 0x50 121 | KEY_MOD_LSHIFT, KEY_P, // P 122 | KEY_MOD_LSHIFT, KEY_Q, // Q 123 | KEY_MOD_LSHIFT, KEY_R, // R 124 | KEY_MOD_LSHIFT, KEY_S, // S 125 | 126 | // 84, 0x54 127 | KEY_MOD_LSHIFT, KEY_T, // T 128 | KEY_MOD_LSHIFT, KEY_U, // U 129 | KEY_MOD_LSHIFT, KEY_V, // V 130 | KEY_MOD_LSHIFT, KEY_W, // W 131 | 132 | // 88, 0x58 133 | KEY_MOD_LSHIFT, KEY_X, // X 134 | KEY_MOD_LSHIFT, KEY_Y, // Y 135 | KEY_MOD_LSHIFT, KEY_Z, // Z 136 | KEY_NONE, KEY_LEFTBRACE, // [ 137 | 138 | // 92, 0x5c 139 | KEY_NONE, KEY_102ND, // bslash 140 | KEY_NONE, KEY_RIGHTBRACE, // ] 141 | KEY_MOD_LSHIFT, KEY_6, // ^ 142 | KEY_MOD_LSHIFT, KEY_MINUS, // _ 143 | 144 | // 96, 0x60 145 | KEY_NONE, KEY_GRAVE, // ` 146 | KEY_NONE, KEY_A, // a 147 | KEY_NONE, KEY_B, // b 148 | KEY_NONE, KEY_C, // c 149 | 150 | // 100, 0x64 151 | KEY_NONE, KEY_D, // d 152 | KEY_NONE, KEY_E, // e 153 | KEY_NONE, KEY_F, // f 154 | KEY_NONE, KEY_G, // g 155 | 156 | // 104, 0x68 157 | KEY_NONE, KEY_H, // h 158 | KEY_NONE, KEY_I, // i 159 | KEY_NONE, KEY_J, // j 160 | KEY_NONE, KEY_K, // k 161 | 162 | // 108, 0x6c 163 | KEY_NONE, KEY_L, // l 164 | KEY_NONE, KEY_M, // m 165 | KEY_NONE, KEY_N, // n 166 | KEY_NONE, KEY_O, // o 167 | 168 | // 112, 0x70 169 | KEY_NONE, KEY_P, // p 170 | KEY_NONE, KEY_Q, // q 171 | KEY_NONE, KEY_R, // r 172 | KEY_NONE, KEY_S, // s 173 | 174 | // 116, 0x74 175 | KEY_NONE, KEY_T, // t 176 | KEY_NONE, KEY_U, // u 177 | KEY_NONE, KEY_V, // v 178 | KEY_NONE, KEY_W, // w 179 | 180 | // 120, 0x78 181 | KEY_NONE, KEY_X, // x 182 | KEY_NONE, KEY_Y, // y 183 | KEY_NONE, KEY_Z, // z 184 | KEY_MOD_LSHIFT, KEY_LEFTBRACE, // { 185 | 186 | // 124, 0x7c 187 | KEY_MOD_LSHIFT, KEY_102ND, // | 188 | KEY_MOD_LSHIFT, KEY_RIGHTBRACE, // } 189 | KEY_MOD_LSHIFT, KEY_BACKSLASH, // ~ 190 | KEY_NONE, KEY_DELETE // DEL 191 | }; 192 | 193 | const uint8_t utf8_gb[] PROGMEM = { 194 | 0xC2, 0xA3, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_3, // £ 195 | 0xC2, 0xA6, 0x00, 0x00, KEY_MOD_RALT, KEY_GRAVE, // ¦ 196 | 0xC2, 0xAC, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_GRAVE, // ¬ 197 | 0xC3, 0x81, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_A, // Á 198 | 0xC3, 0x89, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_E, // É 199 | 0xC3, 0x8D, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_I, // Í 200 | 0xC3, 0x93, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_O, // Ó 201 | 0xC3, 0x9A, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_U, // Ú 202 | 0xC3, 0xA1, 0x00, 0x00, KEY_MOD_RALT, KEY_A, // á 203 | 0xC3, 0xA9, 0x00, 0x00, KEY_MOD_RALT, KEY_E, // é 204 | 0xC3, 0xAD, 0x00, 0x00, KEY_MOD_RALT, KEY_I, // í 205 | 0xC3, 0xB3, 0x00, 0x00, KEY_MOD_RALT, KEY_O, // ó 206 | 0xC3, 0xBA, 0x00, 0x00, KEY_MOD_RALT, KEY_U, // ú 207 | 0xE2, 0x82, 0xAC, 0x00, KEY_MOD_RALT, KEY_4, // € 208 | }; 209 | 210 | const uint8_t combinations_gb[] PROGMEM = { 211 | }; 212 | 213 | static hid_locale_t locale_gb { 214 | (uint8_t*)ascii_gb, 128, 215 | (uint8_t*)utf8_gb, sizeof(utf8_gb) / 6, 216 | (uint8_t*)combinations_gb, sizeof(combinations_gb) / 8, 217 | }; -------------------------------------------------------------------------------- /atmega_duck/locale_it.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "usb_hid_keys.h" 9 | 10 | // Modifier(s), Key 11 | const uint8_t ascii_it[] PROGMEM = { 12 | KEY_NONE, KEY_NONE, // NUL 13 | KEY_NONE, KEY_NONE, // SOH 14 | KEY_NONE, KEY_NONE, // STX 15 | KEY_NONE, KEY_NONE, // ETX 16 | KEY_NONE, KEY_NONE, // EOT 17 | KEY_NONE, KEY_NONE, // ENQ 18 | KEY_NONE, KEY_NONE, // ACK 19 | KEY_NONE, KEY_NONE, // BEL 20 | 21 | // 8, 0x08 22 | KEY_NONE, KEY_BACKSPACE, // BS Backspace 23 | KEY_NONE, KEY_TAB, // TAB Tab 24 | KEY_NONE, KEY_ENTER, // LF Enter 25 | 26 | KEY_NONE, KEY_NONE, // VT 27 | KEY_NONE, KEY_NONE, // FF 28 | KEY_NONE, KEY_NONE, // CR 29 | KEY_NONE, KEY_NONE, // SO 30 | KEY_NONE, KEY_NONE, // SI 31 | KEY_NONE, KEY_NONE, // DEL 32 | KEY_NONE, KEY_NONE, // DC1 33 | KEY_NONE, KEY_NONE, // DC2 34 | KEY_NONE, KEY_NONE, // DC3 35 | KEY_NONE, KEY_NONE, // DC4 36 | KEY_NONE, KEY_NONE, // NAK 37 | KEY_NONE, KEY_NONE, // SYN 38 | KEY_NONE, KEY_NONE, // ETB 39 | KEY_NONE, KEY_NONE, // CAN 40 | KEY_NONE, KEY_NONE, // EM 41 | KEY_NONE, KEY_NONE, // SUB 42 | KEY_NONE, KEY_NONE, // ESC 43 | KEY_NONE, KEY_NONE, // FS 44 | KEY_NONE, KEY_NONE, // GS 45 | KEY_NONE, KEY_NONE, // RS 46 | KEY_NONE, KEY_NONE, // US 47 | 48 | // 32, 0x20 49 | KEY_NONE, KEY_SPACE, // ' ' 50 | KEY_MOD_LSHIFT, KEY_1, // ! 51 | KEY_MOD_LSHIFT, KEY_2, // " 52 | KEY_MOD_RALT, KEY_APOSTROPHE, // # 53 | 54 | // 36, 0x24 55 | KEY_MOD_LSHIFT, KEY_4, // $ 56 | KEY_MOD_LSHIFT, KEY_5, // % 57 | KEY_MOD_LSHIFT, KEY_6, // & 58 | KEY_NONE, KEY_MINUS, // ' 59 | 60 | // 40, 0x28 61 | KEY_MOD_LSHIFT, KEY_8, // ( 62 | KEY_MOD_LSHIFT, KEY_9, // ) 63 | KEY_MOD_LSHIFT, KEY_RIGHTBRACE, // * 64 | KEY_NONE, KEY_RIGHTBRACE, // + 65 | 66 | // 44, 0x2c 67 | KEY_NONE, KEY_COMMA, // , 68 | KEY_NONE, KEY_SLASH, // - 69 | KEY_NONE, KEY_DOT, // . 70 | KEY_MOD_LSHIFT, KEY_7, // / 71 | 72 | // 48, 0x30 73 | KEY_NONE, KEY_0, // 0 74 | KEY_NONE, KEY_1, // 1 75 | KEY_NONE, KEY_2, // 2 76 | KEY_NONE, KEY_3, // 3 77 | 78 | // 52, 0x34 79 | KEY_NONE, KEY_4, // 4 80 | KEY_NONE, KEY_5, // 5 81 | KEY_NONE, KEY_6, // 6 82 | KEY_NONE, KEY_7, // 7 83 | 84 | // 56, 0x38 85 | KEY_NONE, KEY_8, // 8 86 | KEY_NONE, KEY_9, // 9 87 | KEY_MOD_LSHIFT, KEY_DOT, // : 88 | KEY_MOD_LSHIFT, KEY_COMMA, // ; 89 | 90 | // 60, 0x3c 91 | KEY_NONE, KEY_102ND, // < 92 | KEY_MOD_LSHIFT, KEY_0, // = 93 | KEY_MOD_LSHIFT, KEY_102ND, // > 94 | KEY_MOD_LSHIFT, KEY_MINUS, // ? 95 | 96 | // 64, 0x40 97 | KEY_MOD_RALT, KEY_SEMICOLON, // @ 98 | KEY_MOD_LSHIFT, KEY_A, // A 99 | KEY_MOD_LSHIFT, KEY_B, // B 100 | KEY_MOD_LSHIFT, KEY_C, // C 101 | 102 | // 68, 0x44 103 | KEY_MOD_LSHIFT, KEY_D, // D 104 | KEY_MOD_LSHIFT, KEY_E, // E 105 | KEY_MOD_LSHIFT, KEY_F, // F 106 | KEY_MOD_LSHIFT, KEY_G, // G 107 | 108 | // 72, 0x48 109 | KEY_MOD_LSHIFT, KEY_H, // H 110 | KEY_MOD_LSHIFT, KEY_I, // I 111 | KEY_MOD_LSHIFT, KEY_J, // J 112 | KEY_MOD_LSHIFT, KEY_K, // K 113 | 114 | // 76, 0x4c 115 | KEY_MOD_LSHIFT, KEY_L, // L 116 | KEY_MOD_LSHIFT, KEY_M, // M 117 | KEY_MOD_LSHIFT, KEY_N, // N 118 | KEY_MOD_LSHIFT, KEY_O, // O 119 | 120 | // 80, 0x50 121 | KEY_MOD_LSHIFT, KEY_P, // P 122 | KEY_MOD_LSHIFT, KEY_Q, // Q 123 | KEY_MOD_LSHIFT, KEY_R, // R 124 | KEY_MOD_LSHIFT, KEY_S, // S 125 | 126 | // 84, 0x54 127 | KEY_MOD_LSHIFT, KEY_T, // T 128 | KEY_MOD_LSHIFT, KEY_U, // U 129 | KEY_MOD_LSHIFT, KEY_V, // V 130 | KEY_MOD_LSHIFT, KEY_W, // W 131 | 132 | // 88, 0x58 133 | KEY_MOD_LSHIFT, KEY_X, // X 134 | KEY_MOD_LSHIFT, KEY_Y, // Y 135 | KEY_MOD_LSHIFT, KEY_Z, // Z 136 | KEY_MOD_RALT, KEY_LEFTBRACE, // [ 137 | 138 | // 92, 0x5c 139 | KEY_NONE, KEY_GRAVE, // bslash 140 | KEY_MOD_RALT, KEY_RIGHTBRACE, // ] 141 | KEY_MOD_LSHIFT, KEY_EQUAL, // ^ 142 | KEY_MOD_LSHIFT, KEY_SLASH, // _ 143 | 144 | // 96, 0x60 145 | KEY_NONE, KEY_NONE, // ` 146 | KEY_NONE, KEY_A, // a 147 | KEY_NONE, KEY_B, // b 148 | KEY_NONE, KEY_C, // c 149 | 150 | // 100, 0x64 151 | KEY_NONE, KEY_D, // d 152 | KEY_NONE, KEY_E, // e 153 | KEY_NONE, KEY_F, // f 154 | KEY_NONE, KEY_G, // g 155 | 156 | // 104, 0x68 157 | KEY_NONE, KEY_H, // h 158 | KEY_NONE, KEY_I, // i 159 | KEY_NONE, KEY_J, // j 160 | KEY_NONE, KEY_K, // k 161 | 162 | // 108, 0x6c 163 | KEY_NONE, KEY_L, // l 164 | KEY_NONE, KEY_M, // m 165 | KEY_NONE, KEY_N, // n 166 | KEY_NONE, KEY_O, // o 167 | 168 | // 112, 0x70 169 | KEY_NONE, KEY_P, // p 170 | KEY_NONE, KEY_Q, // q 171 | KEY_NONE, KEY_R, // r 172 | KEY_NONE, KEY_S, // s 173 | 174 | // 116, 0x74 175 | KEY_NONE, KEY_T, // t 176 | KEY_NONE, KEY_U, // u 177 | KEY_NONE, KEY_V, // v 178 | KEY_NONE, KEY_W, // w 179 | 180 | // 120, 0x78 181 | KEY_NONE, KEY_X, // x 182 | KEY_NONE, KEY_Y, // y 183 | KEY_NONE, KEY_Z, // z 184 | (KEY_MOD_RALT|KEY_MOD_LSHIFT),KEY_LEFTBRACE,// { 185 | 186 | // 124, 0x7c 187 | KEY_MOD_LSHIFT, KEY_GRAVE, // | 188 | (KEY_MOD_RALT|KEY_MOD_LSHIFT),KEY_RIGHTBRACE,// } 189 | KEY_NONE, KEY_NONE, // ~ 190 | KEY_NONE, KEY_DELETE // DEL 191 | }; 192 | 193 | const uint8_t utf8_it[] PROGMEM = { 194 | 0xC2, 0xA3, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_3, // £ 195 | 0xC2, 0xA7, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_BACKSLASH, // § 196 | 0xC2, 0xB0, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_APOSTROPHE, // ° 197 | 0xC3, 0xA0, 0x00, 0x00, KEY_NONE, KEY_APOSTROPHE, // à 198 | 0xC3, 0xA7, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_SEMICOLON, // ç 199 | 0xC3, 0xA8, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, // è 200 | 0xC3, 0xA9, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, // é 201 | 0xC3, 0xAC, 0x00, 0x00, KEY_NONE, KEY_EQUAL, // ì 202 | 0xC3, 0xB2, 0x00, 0x00, KEY_NONE, KEY_SEMICOLON, // ò 203 | 0xC3, 0xB9, 0x00, 0x00, KEY_NONE, KEY_BACKSLASH, // ù 204 | 0xE2, 0x82, 0xAC, 0x00, KEY_MOD_RALT, KEY_5, // € 205 | }; 206 | 207 | const uint8_t combinations_it[] PROGMEM = { 208 | }; 209 | 210 | static hid_locale_t locale_it { 211 | (uint8_t*)ascii_it, 128, 212 | (uint8_t*)utf8_it, sizeof(utf8_it) / 6, 213 | (uint8_t*)combinations_it, sizeof(combinations_it) / 8, 214 | }; -------------------------------------------------------------------------------- /atmega_duck/locale_ru.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "usb_hid_keys.h" 9 | 10 | // Modifier(s), Key 11 | const uint8_t ascii_ru[] PROGMEM = { 12 | KEY_NONE, KEY_NONE, // NUL 13 | KEY_NONE, KEY_NONE, // SOH 14 | KEY_NONE, KEY_NONE, // STX 15 | KEY_NONE, KEY_NONE, // ETX 16 | KEY_NONE, KEY_NONE, // EOT 17 | KEY_NONE, KEY_NONE, // ENQ 18 | KEY_NONE, KEY_NONE, // ACK 19 | KEY_NONE, KEY_NONE, // BEL 20 | 21 | // 8, 0x08 22 | KEY_NONE, KEY_BACKSPACE, // BS Backspace 23 | KEY_NONE, KEY_TAB, // TAB Tab 24 | KEY_NONE, KEY_ENTER, // LF Enter 25 | 26 | KEY_NONE, KEY_NONE, // VT 27 | KEY_NONE, KEY_NONE, // FF 28 | KEY_NONE, KEY_NONE, // CR 29 | KEY_NONE, KEY_NONE, // SO 30 | KEY_NONE, KEY_NONE, // SI 31 | KEY_NONE, KEY_NONE, // DEL 32 | KEY_NONE, KEY_NONE, // DC1 33 | KEY_NONE, KEY_NONE, // DC2 34 | KEY_NONE, KEY_NONE, // DC3 35 | KEY_NONE, KEY_NONE, // DC4 36 | KEY_NONE, KEY_NONE, // NAK 37 | KEY_NONE, KEY_NONE, // SYN 38 | KEY_NONE, KEY_NONE, // ETB 39 | KEY_NONE, KEY_NONE, // CAN 40 | KEY_NONE, KEY_NONE, // EM 41 | KEY_NONE, KEY_NONE, // SUB 42 | KEY_NONE, KEY_NONE, // ESC 43 | KEY_NONE, KEY_NONE, // FS 44 | KEY_NONE, KEY_NONE, // GS 45 | KEY_NONE, KEY_NONE, // RS 46 | KEY_NONE, KEY_NONE, // US 47 | 48 | // 32, 0x20 49 | KEY_NONE, KEY_SPACE, // ' ' 50 | KEY_MOD_LSHIFT, KEY_1, // ! 51 | KEY_MOD_LSHIFT, KEY_2, // " 52 | KEY_NONE, KEY_NONE, // # 53 | 54 | // 36, 0x24 55 | KEY_NONE, KEY_NONE, // $ 56 | KEY_MOD_LSHIFT, KEY_5, // % 57 | KEY_NONE, KEY_NONE, // & 58 | KEY_NONE, KEY_NONE, // ' 59 | 60 | // 40, 0x28 61 | KEY_MOD_LSHIFT, KEY_9, // ( 62 | KEY_MOD_LSHIFT, KEY_0, // ) 63 | KEY_MOD_LSHIFT, KEY_8, // * 64 | KEY_MOD_LSHIFT, KEY_EQUAL, // + 65 | 66 | // 44, 0x2c 67 | KEY_MOD_LSHIFT, KEY_SLASH, // , 68 | KEY_NONE, KEY_MINUS, // - 69 | KEY_NONE, KEY_SLASH, // . 70 | KEY_MOD_LSHIFT, KEY_BACKSLASH, // / 71 | 72 | // 48, 0x30 73 | KEY_NONE, KEY_0, // 0 74 | KEY_NONE, KEY_1, // 1 75 | KEY_NONE, KEY_2, // 2 76 | KEY_NONE, KEY_3, // 3 77 | 78 | // 52, 0x34 79 | KEY_NONE, KEY_4, // 4 80 | KEY_NONE, KEY_5, // 5 81 | KEY_NONE, KEY_6, // 6 82 | KEY_NONE, KEY_7, // 7 83 | 84 | // 56, 0x38 85 | KEY_NONE, KEY_8, // 8 86 | KEY_NONE, KEY_9, // 9 87 | KEY_MOD_LSHIFT, KEY_6, // : 88 | KEY_MOD_LSHIFT, KEY_4, // ; 89 | 90 | // 60, 0x3c 91 | KEY_NONE, KEY_NONE, // < 92 | KEY_NONE, KEY_EQUAL, // = 93 | KEY_NONE, KEY_NONE, // > 94 | KEY_MOD_LSHIFT, KEY_7, // ? 95 | 96 | // 64, 0x40 97 | KEY_NONE, KEY_NONE, // @ 98 | KEY_NONE, KEY_NONE, // A 99 | KEY_NONE, KEY_NONE, // B 100 | KEY_NONE, KEY_NONE, // C 101 | 102 | // 68, 0x44 103 | KEY_NONE, KEY_NONE, // D 104 | KEY_NONE, KEY_NONE, // E 105 | KEY_NONE, KEY_NONE, // F 106 | KEY_NONE, KEY_NONE, // G 107 | 108 | // 72, 0x48 109 | KEY_NONE, KEY_NONE, // H 110 | KEY_NONE, KEY_NONE, // I 111 | KEY_NONE, KEY_NONE, // J 112 | KEY_NONE, KEY_NONE, // K 113 | 114 | // 76, 0x4c 115 | KEY_NONE, KEY_NONE, // L 116 | KEY_NONE, KEY_NONE, // M 117 | KEY_NONE, KEY_NONE, // N 118 | KEY_NONE, KEY_NONE, // O 119 | 120 | // 80, 0x50 121 | KEY_NONE, KEY_NONE, // P 122 | KEY_NONE, KEY_NONE, // Q 123 | KEY_NONE, KEY_NONE, // R 124 | KEY_NONE, KEY_NONE, // S 125 | 126 | // 84, 0x54 127 | KEY_NONE, KEY_NONE, // T 128 | KEY_NONE, KEY_NONE, // U 129 | KEY_NONE, KEY_NONE, // V 130 | KEY_NONE, KEY_NONE, // W 131 | 132 | // 88, 0x58 133 | KEY_NONE, KEY_NONE, // X 134 | KEY_NONE, KEY_NONE, // Y 135 | KEY_NONE, KEY_NONE, // Z 136 | KEY_NONE, KEY_NONE, // [ 137 | 138 | // 92, 0x5c 139 | KEY_NONE, KEY_BACKSLASH, // bslash 140 | KEY_NONE, KEY_NONE, // ] 141 | KEY_NONE, KEY_NONE, // ^ 142 | KEY_MOD_LSHIFT, KEY_MINUS, // _ 143 | 144 | // 96, 0x60 145 | KEY_NONE, KEY_NONE, // ` 146 | KEY_NONE, KEY_NONE, // a 147 | KEY_NONE, KEY_NONE, // b 148 | KEY_NONE, KEY_NONE, // c 149 | 150 | // 100, 0x64 151 | KEY_NONE, KEY_NONE, // d 152 | KEY_NONE, KEY_NONE, // e 153 | KEY_NONE, KEY_NONE, // f 154 | KEY_NONE, KEY_NONE, // g 155 | 156 | // 104, 0x68 157 | KEY_NONE, KEY_NONE, // h 158 | KEY_NONE, KEY_NONE, // i 159 | KEY_NONE, KEY_NONE, // j 160 | KEY_NONE, KEY_NONE, // k 161 | 162 | // 108, 0x6c 163 | KEY_NONE, KEY_NONE, // l 164 | KEY_NONE, KEY_NONE, // m 165 | KEY_NONE, KEY_NONE, // n 166 | KEY_NONE, KEY_NONE, // o 167 | 168 | // 112, 0x70 169 | KEY_NONE, KEY_NONE, // p 170 | KEY_NONE, KEY_NONE, // q 171 | KEY_NONE, KEY_NONE, // r 172 | KEY_NONE, KEY_NONE, // s 173 | 174 | // 116, 0x74 175 | KEY_NONE, KEY_NONE, // t 176 | KEY_NONE, KEY_NONE, // u 177 | KEY_NONE, KEY_NONE, // v 178 | KEY_NONE, KEY_NONE, // w 179 | 180 | // 120, 0x78 181 | KEY_NONE, KEY_NONE, // x 182 | KEY_NONE, KEY_NONE, // y 183 | KEY_NONE, KEY_NONE, // z 184 | KEY_NONE, KEY_NONE, // { 185 | 186 | // 124, 0x7c 187 | KEY_NONE, KEY_NONE, // | 188 | KEY_NONE, KEY_NONE, // } 189 | KEY_NONE, KEY_NONE, // ~ 190 | KEY_NONE, KEY_DELETE // DEL 191 | }; 192 | 193 | const uint8_t utf8_ru[] PROGMEM = { 194 | 0xD0, 0x81, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_GRAVE, // Ё 195 | 0xD0, 0x90, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_F, // А 196 | 0xD0, 0x91, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_COMMA, // Б 197 | 0xD0, 0x92, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_D, // В 198 | 0xD0, 0x93, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_U, // Г 199 | 0xD0, 0x94, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_L, // Д 200 | 0xD0, 0x95, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_T, // Е 201 | 0xD0, 0x96, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_SEMICOLON, // Ж 202 | 0xD0, 0x97, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_P, // З 203 | 0xD0, 0x98, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_B, // И 204 | 0xD0, 0x99, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_Q, // Й 205 | 0xD0, 0x9A, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_R, // К 206 | 0xD0, 0x9B, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_K, // Л 207 | 0xD0, 0x9C, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_V, // М 208 | 0xD0, 0x9D, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_Y, // Н 209 | 0xD0, 0x9E, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_J, // О 210 | 0xD0, 0x9F, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_G, // П 211 | 0xD0, 0xA0, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_H, // Р 212 | 0xD0, 0xA1, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_C, // С 213 | 0xD0, 0xA2, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_N, // Т 214 | 0xD0, 0xA3, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_E, // У 215 | 0xD0, 0xA4, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_A, // Ф 216 | 0xD0, 0xA5, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_LEFTBRACE, // Х 217 | 0xD0, 0xA6, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_W, // Ц 218 | 0xD0, 0xA7, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_X, // Ч 219 | 0xD0, 0xA8, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_I, // Ш 220 | 0xD0, 0xA9, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_O, // Щ 221 | 0xD0, 0xAA, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_RIGHTBRACE, // Ъ 222 | 0xD0, 0xAB, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_S, // Ы 223 | 0xD0, 0xAC, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_M, // Ь 224 | 0xD0, 0xAD, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_APOSTROPHE, // Э 225 | 0xD0, 0xAE, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_DOT, // Ю 226 | 0xD0, 0xAF, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_Z, // Я 227 | 0xD0, 0xB0, 0x00, 0x00, KEY_NONE, KEY_F, // а 228 | 0xD0, 0xB1, 0x00, 0x00, KEY_NONE, KEY_COMMA, // б 229 | 0xD0, 0xB2, 0x00, 0x00, KEY_NONE, KEY_D, // в 230 | 0xD0, 0xB3, 0x00, 0x00, KEY_NONE, KEY_U, // г 231 | 0xD0, 0xB4, 0x00, 0x00, KEY_NONE, KEY_L, // д 232 | 0xD0, 0xB5, 0x00, 0x00, KEY_NONE, KEY_T, // е 233 | 0xD0, 0xB6, 0x00, 0x00, KEY_NONE, KEY_SEMICOLON, // ж 234 | 0xD0, 0xB7, 0x00, 0x00, KEY_NONE, KEY_P, // з 235 | 0xD0, 0xB8, 0x00, 0x00, KEY_NONE, KEY_B, // и 236 | 0xD0, 0xB9, 0x00, 0x00, KEY_NONE, KEY_Q, // й 237 | 0xD0, 0xBA, 0x00, 0x00, KEY_NONE, KEY_R, // к 238 | 0xD0, 0xBB, 0x00, 0x00, KEY_NONE, KEY_K, // л 239 | 0xD0, 0xBC, 0x00, 0x00, KEY_NONE, KEY_V, // м 240 | 0xD0, 0xBD, 0x00, 0x00, KEY_NONE, KEY_Y, // н 241 | 0xD0, 0xBE, 0x00, 0x00, KEY_NONE, KEY_J, // о 242 | 0xD0, 0xBF, 0x00, 0x00, KEY_NONE, KEY_G, // п 243 | 0xD1, 0x80, 0x00, 0x00, KEY_NONE, KEY_H, // р 244 | 0xD1, 0x81, 0x00, 0x00, KEY_NONE, KEY_C, // с 245 | 0xD1, 0x82, 0x00, 0x00, KEY_NONE, KEY_N, // т 246 | 0xD1, 0x83, 0x00, 0x00, KEY_NONE, KEY_E, // у 247 | 0xD1, 0x84, 0x00, 0x00, KEY_NONE, KEY_A, // ф 248 | 0xD1, 0x85, 0x00, 0x00, KEY_NONE, KEY_LEFTBRACE, // х 249 | 0xD1, 0x86, 0x00, 0x00, KEY_NONE, KEY_W, // ц 250 | 0xD1, 0x87, 0x00, 0x00, KEY_NONE, KEY_X, // ч 251 | 0xD1, 0x88, 0x00, 0x00, KEY_NONE, KEY_I, // ш 252 | 0xD1, 0x89, 0x00, 0x00, KEY_NONE, KEY_O, // щ 253 | 0xD1, 0x8A, 0x00, 0x00, KEY_NONE, KEY_RIGHTBRACE, // ъ 254 | 0xD1, 0x8B, 0x00, 0x00, KEY_NONE, KEY_S, // ы 255 | 0xD1, 0x8C, 0x00, 0x00, KEY_NONE, KEY_M, // ь 256 | 0xD1, 0x8D, 0x00, 0x00, KEY_NONE, KEY_APOSTROPHE, // э 257 | 0xD1, 0x8E, 0x00, 0x00, KEY_NONE, KEY_DOT, // ю 258 | 0xD1, 0x8F, 0x00, 0x00, KEY_NONE, KEY_Z, // я 259 | 0xD1, 0x91, 0x00, 0x00, KEY_NONE, KEY_GRAVE, // ё 260 | 0xE2, 0x82, 0xBD, 0x00, KEY_MOD_RALT, KEY_8, // ₽ 261 | 0xE2, 0x84, 0x96, 0x00, KEY_MOD_LSHIFT, KEY_3, // № 262 | }; 263 | 264 | const uint8_t combinations_ru[] PROGMEM = { 265 | }; 266 | 267 | static hid_locale_t locale_ru { 268 | (uint8_t*)ascii_ru, 128, 269 | (uint8_t*)utf8_ru, sizeof(utf8_ru) / 6, 270 | (uint8_t*)combinations_ru, sizeof(combinations_ru) / 8, 271 | }; -------------------------------------------------------------------------------- /atmega_duck/locale_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | typedef struct hid_locale_t { 9 | uint8_t* ascii; 10 | uint8_t ascii_len; 11 | 12 | uint8_t* utf8; 13 | size_t utf8_len; 14 | 15 | uint8_t* combinations; 16 | size_t combinations_len; 17 | } hid_locale_t; -------------------------------------------------------------------------------- /atmega_duck/locale_us.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "usb_hid_keys.h" 9 | 10 | // Modifier(s), Key 11 | const uint8_t ascii_us[] PROGMEM = { 12 | KEY_NONE, KEY_NONE, // NUL 13 | KEY_NONE, KEY_NONE, // SOH 14 | KEY_NONE, KEY_NONE, // STX 15 | KEY_NONE, KEY_NONE, // ETX 16 | KEY_NONE, KEY_NONE, // EOT 17 | KEY_NONE, KEY_NONE, // ENQ 18 | KEY_NONE, KEY_NONE, // ACK 19 | KEY_NONE, KEY_NONE, // BEL 20 | 21 | // 8, 0x08 22 | KEY_NONE, KEY_BACKSPACE, // BS Backspace 23 | KEY_NONE, KEY_TAB, // TAB Tab 24 | KEY_NONE, KEY_ENTER, // LF Enter 25 | 26 | KEY_NONE, KEY_NONE, // VT 27 | KEY_NONE, KEY_NONE, // FF 28 | KEY_NONE, KEY_NONE, // CR 29 | KEY_NONE, KEY_NONE, // SO 30 | KEY_NONE, KEY_NONE, // SI 31 | KEY_NONE, KEY_NONE, // DEL 32 | KEY_NONE, KEY_NONE, // DC1 33 | KEY_NONE, KEY_NONE, // DC2 34 | KEY_NONE, KEY_NONE, // DC3 35 | KEY_NONE, KEY_NONE, // DC4 36 | KEY_NONE, KEY_NONE, // NAK 37 | KEY_NONE, KEY_NONE, // SYN 38 | KEY_NONE, KEY_NONE, // ETB 39 | KEY_NONE, KEY_NONE, // CAN 40 | KEY_NONE, KEY_NONE, // EM 41 | KEY_NONE, KEY_NONE, // SUB 42 | KEY_NONE, KEY_NONE, // ESC 43 | KEY_NONE, KEY_NONE, // FS 44 | KEY_NONE, KEY_NONE, // GS 45 | KEY_NONE, KEY_NONE, // RS 46 | KEY_NONE, KEY_NONE, // US 47 | 48 | // 32, 0x20 49 | KEY_NONE, KEY_SPACE, // ' ' 50 | KEY_MOD_LSHIFT, KEY_1, // ! 51 | KEY_MOD_LSHIFT, KEY_APOSTROPHE, // " 52 | KEY_MOD_LSHIFT, KEY_3, // # 53 | 54 | // 36, 0x24 55 | KEY_MOD_LSHIFT, KEY_4, // $ 56 | KEY_MOD_LSHIFT, KEY_5, // % 57 | KEY_MOD_LSHIFT, KEY_7, // & 58 | KEY_NONE, KEY_APOSTROPHE, // ' 59 | 60 | // 40, 0x28 61 | KEY_MOD_LSHIFT, KEY_9, // ( 62 | KEY_MOD_LSHIFT, KEY_0, // ) 63 | KEY_MOD_LSHIFT, KEY_8, // * 64 | KEY_MOD_LSHIFT, KEY_EQUAL, // + 65 | 66 | // 44, 0x2c 67 | KEY_NONE, KEY_COMMA, // , 68 | KEY_NONE, KEY_MINUS, // - 69 | KEY_NONE, KEY_DOT, // . 70 | KEY_NONE, KEY_SLASH, // / 71 | 72 | // 48, 0x30 73 | KEY_NONE, KEY_0, // 0 74 | KEY_NONE, KEY_1, // 1 75 | KEY_NONE, KEY_2, // 2 76 | KEY_NONE, KEY_3, // 3 77 | 78 | // 52, 0x34 79 | KEY_NONE, KEY_4, // 4 80 | KEY_NONE, KEY_5, // 5 81 | KEY_NONE, KEY_6, // 6 82 | KEY_NONE, KEY_7, // 7 83 | 84 | // 56, 0x38 85 | KEY_NONE, KEY_8, // 8 86 | KEY_NONE, KEY_9, // 9 87 | KEY_MOD_LSHIFT, KEY_SEMICOLON, // : 88 | KEY_NONE, KEY_SEMICOLON, // ; 89 | 90 | // 60, 0x3c 91 | KEY_MOD_LSHIFT, KEY_COMMA, // < 92 | KEY_NONE, KEY_EQUAL, // = 93 | KEY_MOD_LSHIFT, KEY_DOT, // > 94 | KEY_MOD_LSHIFT, KEY_SLASH, // ? 95 | 96 | // 64, 0x40 97 | KEY_MOD_LSHIFT, KEY_2, // @ 98 | KEY_MOD_LSHIFT, KEY_A, // A 99 | KEY_MOD_LSHIFT, KEY_B, // B 100 | KEY_MOD_LSHIFT, KEY_C, // C 101 | 102 | // 68, 0x44 103 | KEY_MOD_LSHIFT, KEY_D, // D 104 | KEY_MOD_LSHIFT, KEY_E, // E 105 | KEY_MOD_LSHIFT, KEY_F, // F 106 | KEY_MOD_LSHIFT, KEY_G, // G 107 | 108 | // 72, 0x48 109 | KEY_MOD_LSHIFT, KEY_H, // H 110 | KEY_MOD_LSHIFT, KEY_I, // I 111 | KEY_MOD_LSHIFT, KEY_J, // J 112 | KEY_MOD_LSHIFT, KEY_K, // K 113 | 114 | // 76, 0x4c 115 | KEY_MOD_LSHIFT, KEY_L, // L 116 | KEY_MOD_LSHIFT, KEY_M, // M 117 | KEY_MOD_LSHIFT, KEY_N, // N 118 | KEY_MOD_LSHIFT, KEY_O, // O 119 | 120 | // 80, 0x50 121 | KEY_MOD_LSHIFT, KEY_P, // P 122 | KEY_MOD_LSHIFT, KEY_Q, // Q 123 | KEY_MOD_LSHIFT, KEY_R, // R 124 | KEY_MOD_LSHIFT, KEY_S, // S 125 | 126 | // 84, 0x54 127 | KEY_MOD_LSHIFT, KEY_T, // T 128 | KEY_MOD_LSHIFT, KEY_U, // U 129 | KEY_MOD_LSHIFT, KEY_V, // V 130 | KEY_MOD_LSHIFT, KEY_W, // W 131 | 132 | // 88, 0x58 133 | KEY_MOD_LSHIFT, KEY_X, // X 134 | KEY_MOD_LSHIFT, KEY_Y, // Y 135 | KEY_MOD_LSHIFT, KEY_Z, // Z 136 | KEY_NONE, KEY_LEFTBRACE, // [ 137 | 138 | // 92, 0x5c 139 | KEY_NONE, KEY_BACKSLASH, // bslash 140 | KEY_NONE, KEY_RIGHTBRACE, // ] 141 | KEY_MOD_LSHIFT, KEY_6, // ^ 142 | KEY_MOD_LSHIFT, KEY_MINUS, // _ 143 | 144 | // 96, 0x60 145 | KEY_NONE, KEY_GRAVE, // ` 146 | KEY_NONE, KEY_A, // a 147 | KEY_NONE, KEY_B, // b 148 | KEY_NONE, KEY_C, // c 149 | 150 | // 100, 0x64 151 | KEY_NONE, KEY_D, // d 152 | KEY_NONE, KEY_E, // e 153 | KEY_NONE, KEY_F, // f 154 | KEY_NONE, KEY_G, // g 155 | 156 | // 104, 0x68 157 | KEY_NONE, KEY_H, // h 158 | KEY_NONE, KEY_I, // i 159 | KEY_NONE, KEY_J, // j 160 | KEY_NONE, KEY_K, // k 161 | 162 | // 108, 0x6c 163 | KEY_NONE, KEY_L, // l 164 | KEY_NONE, KEY_M, // m 165 | KEY_NONE, KEY_N, // n 166 | KEY_NONE, KEY_O, // o 167 | 168 | // 112, 0x70 169 | KEY_NONE, KEY_P, // p 170 | KEY_NONE, KEY_Q, // q 171 | KEY_NONE, KEY_R, // r 172 | KEY_NONE, KEY_S, // s 173 | 174 | // 116, 0x74 175 | KEY_NONE, KEY_T, // t 176 | KEY_NONE, KEY_U, // u 177 | KEY_NONE, KEY_V, // v 178 | KEY_NONE, KEY_W, // w 179 | 180 | // 120, 0x78 181 | KEY_NONE, KEY_X, // x 182 | KEY_NONE, KEY_Y, // y 183 | KEY_NONE, KEY_Z, // z 184 | KEY_MOD_LSHIFT, KEY_LEFTBRACE, // { 185 | 186 | // 124, 0x7c 187 | KEY_MOD_LSHIFT, KEY_BACKSLASH, // | 188 | KEY_MOD_LSHIFT, KEY_RIGHTBRACE, // } 189 | KEY_MOD_LSHIFT, KEY_GRAVE, // ~ 190 | KEY_NONE, KEY_DELETE // DEL 191 | }; 192 | 193 | const uint8_t utf8_us[] PROGMEM = { 194 | }; 195 | 196 | const uint8_t combinations_us[] PROGMEM = { 197 | }; 198 | 199 | static hid_locale_t locale_us { 200 | (uint8_t*)ascii_us, 128, 201 | (uint8_t*)utf8_us, sizeof(utf8_us) / 6, 202 | (uint8_t*)combinations_us, sizeof(combinations_us) / 8, 203 | }; -------------------------------------------------------------------------------- /atmega_duck/locales.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "usb_hid_keys.h" 9 | #include "locale_types.h" 10 | 11 | #include "locale_us.h" 12 | #include "locale_de.h" 13 | 14 | #include "locale_gb.h" 15 | #include "locale_es.h" 16 | #include "locale_fr.h" 17 | #include "locale_ru.h" 18 | #include "locale_dk.h" 19 | #include "locale_be.h" 20 | #include "locale_pt.h" 21 | #include "locale_it.h" 22 | #include "locale_sk.h" 23 | #include "locale_cz.h" 24 | #include "locale_si.h" 25 | #include "locale_bg.h" 26 | #include "locale_cafr.h" 27 | #include "locale_chde.h" 28 | #include "locale_chfr.h" 29 | #include "locale_hu.h" 30 | -------------------------------------------------------------------------------- /atmega_duck/parser.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Stefan Kremser 3 | This software is licensed under the MIT License. See the license file for details. 4 | Source: github.com/spacehuhn/SimpleCLI 5 | */ 6 | 7 | #include "parser.h" 8 | 9 | #include // malloc 10 | #include // strlen 11 | #include // bool 12 | 13 | // My own implementation, because the default one in ctype.h make problems on older ESP8266 SDKs 14 | char to_lower(char c) { 15 | if ((c >= 65) && (c <= 90)) { 16 | return (char)(c + 32); 17 | } 18 | return c; 19 | } 20 | 21 | int compare(const char* user_str, size_t user_str_len, const char* templ_str, int case_sensetive) { 22 | if (user_str == templ_str) return COMPARE_EQUAL; 23 | 24 | // null check string pointers 25 | if (!user_str || !templ_str) return COMPARE_UNEQUAL; 26 | 27 | // string lengths 28 | size_t str_len = user_str_len; // strlen(user_str); 29 | size_t key_len = strlen(templ_str); 30 | 31 | // when same length, it there is no need to check for slashes or commas 32 | if (str_len == key_len) { 33 | for (size_t i = 0; i < key_len; i++) { 34 | if (case_sensetive == COMPARE_CASE_SENSETIVE) { 35 | if (user_str[i] != templ_str[i]) return COMPARE_UNEQUAL; 36 | } else { 37 | if (to_lower(user_str[i]) != to_lower(templ_str[i])) return COMPARE_UNEQUAL; 38 | } 39 | } 40 | return COMPARE_EQUAL; 41 | } 42 | 43 | // string can't be longer than templ_str (but can be smaller because of '/' and ',') 44 | if (str_len > key_len) return COMPARE_UNEQUAL; 45 | 46 | unsigned int res_i = 0; 47 | unsigned int a = 0; 48 | unsigned int b = 0; 49 | unsigned int res = 1; 50 | 51 | while (a < str_len && b < key_len) { 52 | if (templ_str[b] == '/') { 53 | // skip slash in templ_str 54 | ++b; 55 | } else if (templ_str[b] == ',') { 56 | // on comma increment res_i and reset str-index 57 | ++b; 58 | a = 0; 59 | ++res_i; 60 | } 61 | 62 | // compare character 63 | if (case_sensetive == COMPARE_CASE_SENSETIVE) { 64 | if (user_str[a] != templ_str[b]) res = 0; 65 | } else { 66 | if (to_lower(user_str[a]) != to_lower(templ_str[b])) res = 0; 67 | } 68 | 69 | // comparison incorrect or string checked until the end and templ_str not checked until the end 70 | if (!res || ((a == str_len - 1) && 71 | (templ_str[b + 1] != ',') && 72 | (templ_str[b + 1] != '/') && 73 | (templ_str[b + 1] != '\0'))) { 74 | // fast forward to next comma 75 | while (b < key_len && templ_str[b] != ',') b++; 76 | res = 1; 77 | } else { 78 | // otherwise icrement indices 79 | ++a; 80 | ++b; 81 | } 82 | } 83 | 84 | // comparison correct AND string checked until the end AND templ_str checked until the end 85 | if (res && (a == str_len) && 86 | ((templ_str[b] == ',') || 87 | (templ_str[b] == '/') || 88 | (templ_str[b] == '\0'))) return COMPARE_EQUAL; // res_i 89 | 90 | return COMPARE_UNEQUAL; 91 | } 92 | 93 | // ===== Word Node ===== // 94 | word_node* word_node_create(const char* str, size_t len) { 95 | word_node* n = (word_node*)malloc(sizeof(word_node)); 96 | 97 | n->str = str; 98 | n->len = len; 99 | n->next = NULL; 100 | return n; 101 | } 102 | 103 | word_node* word_node_destroy(word_node* n) { 104 | if (n) { 105 | free(n); 106 | } 107 | return NULL; 108 | } 109 | 110 | word_node* word_node_destroy_rec(word_node* n) { 111 | if (n) { 112 | word_node_destroy_rec(n->next); 113 | word_node_destroy(n); 114 | } 115 | return NULL; 116 | } 117 | 118 | // ===== Word List ===== // 119 | word_list* word_list_create() { 120 | word_list* l = (word_list*)malloc(sizeof(word_list)); 121 | 122 | l->first = NULL; 123 | l->last = NULL; 124 | l->size = 0; 125 | return l; 126 | } 127 | 128 | word_list* word_list_destroy(word_list* l) { 129 | if (l) { 130 | word_node_destroy_rec(l->first); 131 | free(l); 132 | } 133 | return NULL; 134 | } 135 | 136 | void word_list_push(word_list* l, word_node* n) { 137 | if (l && n) { 138 | if (l->last) { 139 | l->last->next = n; 140 | } else { 141 | l->first = n; 142 | } 143 | 144 | l->last = n; 145 | ++l->size; 146 | } 147 | } 148 | 149 | word_node* word_list_get(word_list* l, size_t i) { 150 | if (!l) return NULL; 151 | 152 | size_t j; 153 | word_node* h = l->first; 154 | 155 | for (j = 0; j < i && h; ++j) { 156 | h = h->next; 157 | } 158 | 159 | return h; 160 | } 161 | 162 | // ===== Line Node ==== // 163 | line_node* line_node_create(const char* str, size_t len) { 164 | line_node* n = (line_node*)malloc(sizeof(line_node)); 165 | 166 | n->str = str; 167 | n->len = len; 168 | n->words = NULL; 169 | n->next = NULL; 170 | 171 | return n; 172 | } 173 | 174 | word_node* line_node_destroy(line_node* n) { 175 | if (n) { 176 | word_list_destroy(n->words); 177 | free(n); 178 | } 179 | return NULL; 180 | } 181 | 182 | word_node* line_node_destroy_rec(line_node* n) { 183 | if (n) { 184 | line_node_destroy_rec(n->next); 185 | line_node_destroy(n); 186 | } 187 | return NULL; 188 | } 189 | 190 | // ===== Line List ===== // 191 | line_list* line_list_create() { 192 | line_list* l = (line_list*)malloc(sizeof(line_list)); 193 | 194 | l->first = NULL; 195 | l->last = NULL; 196 | l->size = 0; 197 | 198 | return l; 199 | } 200 | 201 | line_list* line_list_destroy(line_list* l) { 202 | if (l) { 203 | line_node_destroy_rec(l->first); 204 | free(l); 205 | } 206 | return NULL; 207 | } 208 | 209 | void line_list_push(line_list* l, line_node* n) { 210 | if (l && n) { 211 | if (l->last) { 212 | l->last->next = n; 213 | } else { 214 | l->first = n; 215 | } 216 | 217 | l->last = n; 218 | ++l->size; 219 | } 220 | } 221 | 222 | line_node* line_list_get(line_list* l, size_t i) { 223 | if (!l) return NULL; 224 | 225 | size_t j; 226 | line_node* h = l->first; 227 | 228 | for (j = 0; j < i && h; ++j) { 229 | h = h->next; 230 | } 231 | 232 | return h; 233 | } 234 | 235 | // ===== Parser ===== // 236 | word_list* parse_words(const char* str, size_t len) { 237 | word_list* l = word_list_create(); 238 | 239 | if (len == 0) return l; 240 | 241 | // Go through string and look for space to split it into words 242 | word_node* n = NULL; 243 | 244 | size_t i = 0; // current index 245 | size_t j = 0; // start index of word 246 | 247 | int escaped = 0; 248 | int ignore_space = 0; 249 | 250 | for (i = 0; i <= len; ++i) { 251 | if ((str[i] == '\\') && (escaped == 0)) { 252 | escaped = 1; 253 | } else if ((str[i] == '"') && (escaped == 0)) { 254 | ignore_space = !ignore_space; 255 | } else if ((i == len) || ((str[i] == ' ') && (ignore_space == 0) && (escaped == 0))) { 256 | size_t k = i - j; // length of word 257 | 258 | // for every word, add to list 259 | if (k > 0) { 260 | n = word_node_create(&str[j], k); 261 | word_list_push(l, n); 262 | } 263 | 264 | j = i + 1; // reset start index of word 265 | } else if (escaped == 1) { 266 | escaped = 0; 267 | } 268 | } 269 | 270 | return l; 271 | } 272 | 273 | line_list* parse_lines(const char* str, size_t len) { 274 | line_list* l = line_list_create(); 275 | 276 | if (len == 0) return l; 277 | 278 | // Go through string and look for \r and \n to split it into lines 279 | line_node* n = NULL; 280 | 281 | size_t stri = 0; // current index 282 | size_t ls = 0; // start index of line 283 | 284 | bool escaped = false; 285 | bool in_quotes = false; 286 | bool delimiter = false; 287 | bool linebreak = false; 288 | bool endofline = false; 289 | 290 | for (stri = 0; stri <= len; ++stri) { 291 | char prev = stri > 0 ? str[stri-1] : 0; 292 | char curr = str[stri]; 293 | char next = str[stri+1]; 294 | 295 | escaped = prev == '\\'; 296 | 297 | // disabled because ducky script isn't using quotes 298 | // in_quotes = (curr == '"' && !escaped) ? !in_quotes : in_quotes; 299 | // delimiter = !in_quotes && !escaped && curr == ';' && next == ';'; 300 | 301 | linebreak = !in_quotes && (curr == '\r' || curr == '\n'); 302 | 303 | endofline = stri == len || curr == '\0'; 304 | 305 | if (linebreak || endofline || delimiter) { 306 | size_t llen = stri - ls; // length of line 307 | 308 | // for every line, parse_words and add to list 309 | if (llen > 0) { 310 | n = line_node_create(&str[ls], llen); 311 | n->words = parse_words(&str[ls], llen); 312 | line_list_push(l, n); 313 | } 314 | 315 | if (delimiter) ++stri; 316 | 317 | ls = stri+1; // reset start index of line 318 | } 319 | } 320 | 321 | return l; 322 | } -------------------------------------------------------------------------------- /atmega_duck/parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Stefan Kremser 3 | This software is licensed under the MIT License. See the license file for details. 4 | Source: github.com/spacehuhn/SimpleCLI 5 | */ 6 | 7 | #pragma once 8 | 9 | #include // size_t 10 | 11 | #define COMPARE_UNEQUAL 0 12 | #define COMPARE_EQUAL 1 13 | 14 | #define COMPARE_CASE_INSENSETIVE 0 15 | #define COMPARE_CASE_SENSETIVE 1 16 | 17 | int compare(const char* user_str, size_t user_str_len, const char* templ_str, int case_sensetive); 18 | 19 | typedef struct word_node { 20 | const char * str; 21 | size_t len; 22 | struct word_node* next; 23 | } word_node; 24 | 25 | typedef struct word_list { 26 | struct word_node* first; 27 | struct word_node* last; 28 | size_t size; 29 | } word_list; 30 | 31 | typedef struct line_node { 32 | const char * str; 33 | size_t len; 34 | struct word_list* words; 35 | struct line_node* next; 36 | } line_node; 37 | 38 | typedef struct line_list { 39 | struct line_node* first; 40 | struct line_node* last; 41 | size_t size; 42 | } line_list; 43 | 44 | // ===== Word Node ===== // 45 | word_node* word_node_create(const char* str, size_t len); 46 | word_node* word_node_destroy(word_node* n); 47 | word_node* word_node_destroy_rec(word_node* n); 48 | 49 | // ===== Word List ===== // 50 | word_list* word_list_create(); 51 | word_list* word_list_destroy(word_list* l); 52 | 53 | void word_list_push(word_list* l, word_node* n); 54 | word_node* word_list_get(word_list* l, size_t i); 55 | 56 | // ===== Line Node ==== // 57 | line_node* line_node_create(const char* str, size_t len); 58 | word_node* line_node_destroy(line_node* n); 59 | word_node* line_node_destroy_rec(line_node* n); 60 | 61 | // ===== Line List ===== // 62 | line_list* line_list_create(); 63 | line_list* line_list_destroy(line_list* l); 64 | 65 | void line_list_push(line_list* l, line_node* n); 66 | line_node* line_list_get(line_list* l, size_t i); 67 | 68 | // ===== Parser ===== // 69 | word_list* parse_words(const char* str, size_t len); 70 | line_list* parse_lines(const char* str, size_t len); -------------------------------------------------------------------------------- /atmega_duck/serial_bridge.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "serial_bridge.h" 9 | 10 | #include "config.h" 11 | #include "led.h" 12 | 13 | #include // pinMode, digitalWrite, ... 14 | 15 | #ifdef BRIDGE_SAFE 16 | #define BRIDGE_SAFE_NUM 123 17 | #include 18 | #endif 19 | 20 | namespace serial_bridge { 21 | #ifdef BRIDGE_SAFE 22 | FlashStorage(esp_was_flashed, int); 23 | #endif 24 | 25 | #ifdef BRIDGE_ENABLE 26 | bool enabled = false; 27 | 28 | unsigned long baud = 38400; 29 | 30 | int rts = -1; 31 | int dtr = -1; 32 | 33 | void begin() { 34 | BRIDGE_PORT.begin(38400); 35 | 36 | pinMode(BRIDGE_SWITCH, INPUT_PULLUP); 37 | 38 | pinMode(BRIDGE_0, OUTPUT); 39 | pinMode(BRIDGE_RST, OUTPUT); 40 | 41 | #ifdef BRIDGE_0_INVERTED 42 | digitalWrite(BRIDGE_0, LOW); 43 | #else // ifdef BRIDGE_0_INVERTED 44 | digitalWrite(BRIDGE_0, HIGH); 45 | #endif // ifdef BRIDGE_0_INVERTED 46 | digitalWrite(BRIDGE_RST, HIGH); 47 | 48 | if ((digitalRead(BRIDGE_SWITCH) == LOW) 49 | #ifdef BRIDGE_SAFE 50 | || (esp_was_flashed.read() != BRIDGE_SAFE_NUM) 51 | #endif 52 | ) { 53 | enabled = true; 54 | led::setColor(COLOR_ESP_UNFLASHED); 55 | 56 | // Wait until user releases button 57 | while (digitalRead(BRIDGE_SWITCH) == LOW) {} 58 | 59 | // Go into serial bridge mode until user presses button again 60 | while (digitalRead(BRIDGE_SWITCH) == HIGH) { 61 | update(); 62 | } 63 | 64 | stop(); 65 | } 66 | } 67 | 68 | void stop() { 69 | #ifdef BRIDGE_SAFE 70 | if(esp_was_flashed.read() != BRIDGE_SAFE_NUM) { 71 | esp_was_flashed.write(BRIDGE_SAFE_NUM); 72 | } 73 | #endif 74 | enabled = false; 75 | led::setColor(0,0,0); 76 | } 77 | 78 | void update() { 79 | if (enabled) { 80 | if (rts != Serial.rts()) { 81 | digitalWrite(BRIDGE_RST, !Serial.rts()); 82 | rts = Serial.rts(); 83 | #ifdef BRIDGE_SAFE 84 | if(esp_was_flashed.read() != BRIDGE_SAFE_NUM) { 85 | esp_was_flashed.write(BRIDGE_SAFE_NUM); 86 | } 87 | #endif 88 | } 89 | 90 | if (dtr != Serial.dtr()) { 91 | #ifdef BRIDGE_0_INVERTED 92 | digitalWrite(BRIDGE_0, Serial.dtr()); 93 | #else // ifdef BRIDGE_0_INVERTED 94 | digitalWrite(BRIDGE_0, !Serial.dtr()); 95 | #endif // ifdef BRIDGE_0_INVERTED 96 | 97 | dtr = Serial.dtr(); 98 | } 99 | 100 | if (Serial.available()) { 101 | BRIDGE_PORT.write(Serial.read()); 102 | } 103 | 104 | if (BRIDGE_PORT.available()) { 105 | Serial.write(BRIDGE_PORT.read()); 106 | } 107 | 108 | if (Serial.baud() != baud) { 109 | rts = -1; 110 | dtr = -1; 111 | 112 | baud = Serial.baud(); 113 | BRIDGE_PORT.begin(38400); 114 | } 115 | } 116 | } 117 | 118 | #else /* ifdef SERIAL_BRIDGE_ENABLE */ 119 | void begin() {} 120 | 121 | void stop() {} 122 | 123 | void update() {} 124 | 125 | #endif /* ifdef SERIAL_BRIDGE_ENABLE */ 126 | } 127 | -------------------------------------------------------------------------------- /atmega_duck/serial_bridge.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | namespace serial_bridge { 9 | void begin(); 10 | void stop(); 11 | void update(); 12 | } -------------------------------------------------------------------------------- /atmega_duck/usb_hid_keys.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Source: https://gist.github.com/MightyPork/6da26e382a7ad91b5496ee55fdc73db2 3 | * 4 | * USB HID Keyboard scan codes as per USB spec 1.11 5 | * plus some additional codes 6 | * 7 | * Created by MightyPork, 2016 8 | * Public domain 9 | * 10 | * Adapted from: 11 | * https://source.android.com/devices/input/keyboard-devices.html 12 | */ 13 | 14 | #ifndef USB_HID_KEYS 15 | #define USB_HID_KEYS 16 | 17 | /** 18 | * Modifier masks - used for the first byte in the HID report. 19 | * NOTE: The second byte in the report is reserved, 0x00 20 | */ 21 | #define KEY_MOD_LCTRL 0x01 22 | #define KEY_MOD_LSHIFT 0x02 23 | #define KEY_MOD_LALT 0x04 24 | #define KEY_MOD_LMETA 0x08 25 | #define KEY_MOD_RCTRL 0x10 26 | #define KEY_MOD_RSHIFT 0x20 27 | #define KEY_MOD_RALT 0x40 28 | #define KEY_MOD_RMETA 0x80 29 | 30 | /** 31 | * Scan codes - last N slots in the HID report (usually 6). 32 | * 0x00 if no key pressed. 33 | * 34 | * If more than N keys are pressed, the HID reports 35 | * KEY_ERR_OVF in all slots to indicate this condition. 36 | */ 37 | 38 | #define KEY_NONE 0x00 // No key pressed 39 | #define KEY_ERR_OVF 0x01 // Keyboard Error Roll Over - used for all slots if too many keys are pressed ("Phantom key") 40 | // 0x02 // Keyboard POST Fail 41 | // 0x03 // Keyboard Error Undefined 42 | #define KEY_A 0x04 // Keyboard a and A 43 | #define KEY_B 0x05 // Keyboard b and B 44 | #define KEY_C 0x06 // Keyboard c and C 45 | #define KEY_D 0x07 // Keyboard d and D 46 | #define KEY_E 0x08 // Keyboard e and E 47 | #define KEY_F 0x09 // Keyboard f and F 48 | #define KEY_G 0x0a // Keyboard g and G 49 | #define KEY_H 0x0b // Keyboard h and H 50 | #define KEY_I 0x0c // Keyboard i and I 51 | #define KEY_J 0x0d // Keyboard j and J 52 | #define KEY_K 0x0e // Keyboard k and K 53 | #define KEY_L 0x0f // Keyboard l and L 54 | #define KEY_M 0x10 // Keyboard m and M 55 | #define KEY_N 0x11 // Keyboard n and N 56 | #define KEY_O 0x12 // Keyboard o and O 57 | #define KEY_P 0x13 // Keyboard p and P 58 | #define KEY_Q 0x14 // Keyboard q and Q 59 | #define KEY_R 0x15 // Keyboard r and R 60 | #define KEY_S 0x16 // Keyboard s and S 61 | #define KEY_T 0x17 // Keyboard t and T 62 | #define KEY_U 0x18 // Keyboard u and U 63 | #define KEY_V 0x19 // Keyboard v and V 64 | #define KEY_W 0x1a // Keyboard w and W 65 | #define KEY_X 0x1b // Keyboard x and X 66 | #define KEY_Y 0x1c // Keyboard y and Y 67 | #define KEY_Z 0x1d // Keyboard z and Z 68 | 69 | #define KEY_1 0x1e // Keyboard 1 and ! 70 | #define KEY_2 0x1f // Keyboard 2 and @ 71 | #define KEY_3 0x20 // Keyboard 3 and # 72 | #define KEY_4 0x21 // Keyboard 4 and $ 73 | #define KEY_5 0x22 // Keyboard 5 and % 74 | #define KEY_6 0x23 // Keyboard 6 and ^ 75 | #define KEY_7 0x24 // Keyboard 7 and & 76 | #define KEY_8 0x25 // Keyboard 8 and * 77 | #define KEY_9 0x26 // Keyboard 9 and ( 78 | #define KEY_0 0x27 // Keyboard 0 and ) 79 | 80 | #define KEY_ENTER 0x28 // Keyboard Return (ENTER) 81 | #define KEY_ESC 0x29 // Keyboard ESCAPE 82 | #define KEY_BACKSPACE 0x2a // Keyboard DELETE (Backspace) 83 | #define KEY_TAB 0x2b // Keyboard Tab 84 | #define KEY_SPACE 0x2c // Keyboard Spacebar 85 | #define KEY_MINUS 0x2d // Keyboard - and _ 86 | #define KEY_EQUAL 0x2e // Keyboard = and + 87 | #define KEY_LEFTBRACE 0x2f // Keyboard [ and { 88 | #define KEY_RIGHTBRACE 0x30 // Keyboard ] and } 89 | #define KEY_BACKSLASH 0x31 // Keyboard \ and | 90 | #define KEY_HASHTILDE 0x32 // Keyboard Non-US # and ~ 91 | #define KEY_SEMICOLON 0x33 // Keyboard ; and : 92 | #define KEY_APOSTROPHE 0x34 // Keyboard ' and " 93 | #define KEY_GRAVE 0x35 // Keyboard ` and ~ 94 | #define KEY_COMMA 0x36 // Keyboard , and < 95 | #define KEY_DOT 0x37 // Keyboard . and > 96 | #define KEY_SLASH 0x38 // Keyboard / and ? 97 | #define KEY_CAPSLOCK 0x39 // Keyboard Caps Lock 98 | 99 | #define KEY_F1 0x3a // Keyboard F1 100 | #define KEY_F2 0x3b // Keyboard F2 101 | #define KEY_F3 0x3c // Keyboard F3 102 | #define KEY_F4 0x3d // Keyboard F4 103 | #define KEY_F5 0x3e // Keyboard F5 104 | #define KEY_F6 0x3f // Keyboard F6 105 | #define KEY_F7 0x40 // Keyboard F7 106 | #define KEY_F8 0x41 // Keyboard F8 107 | #define KEY_F9 0x42 // Keyboard F9 108 | #define KEY_F10 0x43 // Keyboard F10 109 | #define KEY_F11 0x44 // Keyboard F11 110 | #define KEY_F12 0x45 // Keyboard F12 111 | 112 | #define KEY_SYSRQ 0x46 // Keyboard Print Screen 113 | #define KEY_SCROLLLOCK 0x47 // Keyboard Scroll Lock 114 | #define KEY_PAUSE 0x48 // Keyboard Pause 115 | #define KEY_INSERT 0x49 // Keyboard Insert 116 | #define KEY_HOME 0x4a // Keyboard Home 117 | #define KEY_PAGEUP 0x4b // Keyboard Page Up 118 | #define KEY_DELETE 0x4c // Keyboard Delete Forward 119 | #define KEY_END 0x4d // Keyboard End 120 | #define KEY_PAGEDOWN 0x4e // Keyboard Page Down 121 | #define KEY_RIGHT 0x4f // Keyboard Right Arrow 122 | #define KEY_LEFT 0x50 // Keyboard Left Arrow 123 | #define KEY_DOWN 0x51 // Keyboard Down Arrow 124 | #define KEY_UP 0x52 // Keyboard Up Arrow 125 | 126 | #define KEY_NUMLOCK 0x53 // Keyboard Num Lock and Clear 127 | #define KEY_KPSLASH 0x54 // Keypad / 128 | #define KEY_KPASTERISK 0x55 // Keypad * 129 | #define KEY_KPMINUS 0x56 // Keypad - 130 | #define KEY_KPPLUS 0x57 // Keypad + 131 | #define KEY_KPENTER 0x58 // Keypad ENTER 132 | #define KEY_KP1 0x59 // Keypad 1 and End 133 | #define KEY_KP2 0x5a // Keypad 2 and Down Arrow 134 | #define KEY_KP3 0x5b // Keypad 3 and PageDn 135 | #define KEY_KP4 0x5c // Keypad 4 and Left Arrow 136 | #define KEY_KP5 0x5d // Keypad 5 137 | #define KEY_KP6 0x5e // Keypad 6 and Right Arrow 138 | #define KEY_KP7 0x5f // Keypad 7 and Home 139 | #define KEY_KP8 0x60 // Keypad 8 and Up Arrow 140 | #define KEY_KP9 0x61 // Keypad 9 and Page Up 141 | #define KEY_KP0 0x62 // Keypad 0 and Insert 142 | #define KEY_KPDOT 0x63 // Keypad . and Delete 143 | 144 | #define KEY_102ND 0x64 // Keyboard Non-US \ and | 145 | #define KEY_COMPOSE 0x65 // Keyboard Application 146 | #define KEY_POWER 0x66 // Keyboard Power 147 | #define KEY_KPEQUAL 0x67 // Keypad = 148 | 149 | #define KEY_F13 0x68 // Keyboard F13 150 | #define KEY_F14 0x69 // Keyboard F14 151 | #define KEY_F15 0x6a // Keyboard F15 152 | #define KEY_F16 0x6b // Keyboard F16 153 | #define KEY_F17 0x6c // Keyboard F17 154 | #define KEY_F18 0x6d // Keyboard F18 155 | #define KEY_F19 0x6e // Keyboard F19 156 | #define KEY_F20 0x6f // Keyboard F20 157 | #define KEY_F21 0x70 // Keyboard F21 158 | #define KEY_F22 0x71 // Keyboard F22 159 | #define KEY_F23 0x72 // Keyboard F23 160 | #define KEY_F24 0x73 // Keyboard F24 161 | 162 | #define KEY_OPEN 0x74 // Keyboard Execute 163 | #define KEY_HELP 0x75 // Keyboard Help 164 | #define KEY_PROPS 0x76 // Keyboard Menu 165 | #define KEY_FRONT 0x77 // Keyboard Select 166 | #define KEY_STOP 0x78 // Keyboard Stop 167 | #define KEY_AGAIN 0x79 // Keyboard Again 168 | #define KEY_UNDO 0x7a // Keyboard Undo 169 | #define KEY_CUT 0x7b // Keyboard Cut 170 | #define KEY_COPY 0x7c // Keyboard Copy 171 | #define KEY_PASTE 0x7d // Keyboard Paste 172 | #define KEY_FIND 0x7e // Keyboard Find 173 | #define KEY_MUTE 0x7f // Keyboard Mute 174 | #define KEY_VOLUMEUP 0x80 // Keyboard Volume Up 175 | #define KEY_VOLUMEDOWN 0x81 // Keyboard Volume Down 176 | // 0x82 Keyboard Locking Caps Lock 177 | // 0x83 Keyboard Locking Num Lock 178 | // 0x84 Keyboard Locking Scroll Lock 179 | #define KEY_KPCOMMA 0x85 // Keypad Comma 180 | // 0x86 Keypad Equal Sign 181 | #define KEY_RO 0x87 // Keyboard International1 182 | #define KEY_KATAKANAHIRAGANA 0x88 // Keyboard International2 183 | #define KEY_YEN 0x89 // Keyboard International3 184 | #define KEY_HENKAN 0x8a // Keyboard International4 185 | #define KEY_MUHENKAN 0x8b // Keyboard International5 186 | #define KEY_KPJPCOMMA 0x8c // Keyboard International6 187 | // 0x8d Keyboard International7 188 | // 0x8e Keyboard International8 189 | // 0x8f Keyboard International9 190 | #define KEY_HANGEUL 0x90 // Keyboard LANG1 191 | #define KEY_HANJA 0x91 // Keyboard LANG2 192 | #define KEY_KATAKANA 0x92 // Keyboard LANG3 193 | #define KEY_HIRAGANA 0x93 // Keyboard LANG4 194 | #define KEY_ZENKAKUHANKAKU 0x94 // Keyboard LANG5 195 | // 0x95 Keyboard LANG6 196 | // 0x96 Keyboard LANG7 197 | // 0x97 Keyboard LANG8 198 | // 0x98 Keyboard LANG9 199 | // 0x99 Keyboard Alternate Erase 200 | // 0x9a Keyboard SysReq/Attention 201 | // 0x9b Keyboard Cancel 202 | // 0x9c Keyboard Clear 203 | // 0x9d Keyboard Prior 204 | // 0x9e Keyboard Return 205 | // 0x9f Keyboard Separator 206 | // 0xa0 Keyboard Out 207 | // 0xa1 Keyboard Oper 208 | // 0xa2 Keyboard Clear/Again 209 | // 0xa3 Keyboard CrSel/Props 210 | // 0xa4 Keyboard ExSel 211 | 212 | // 0xb0 Keypad 00 213 | // 0xb1 Keypad 000 214 | // 0xb2 Thousands Separator 215 | // 0xb3 Decimal Separator 216 | // 0xb4 Currency Unit 217 | // 0xb5 Currency Sub-unit 218 | #define KEY_KPLEFTPAREN 0xb6 // Keypad ( 219 | #define KEY_KPRIGHTPAREN 0xb7 // Keypad ) 220 | // 0xb8 Keypad { 221 | // 0xb9 Keypad } 222 | // 0xba Keypad Tab 223 | // 0xbb Keypad Backspace 224 | // 0xbc Keypad A 225 | // 0xbd Keypad B 226 | // 0xbe Keypad C 227 | // 0xbf Keypad D 228 | // 0xc0 Keypad E 229 | // 0xc1 Keypad F 230 | // 0xc2 Keypad XOR 231 | // 0xc3 Keypad ^ 232 | // 0xc4 Keypad % 233 | // 0xc5 Keypad < 234 | // 0xc6 Keypad > 235 | // 0xc7 Keypad & 236 | // 0xc8 Keypad && 237 | // 0xc9 Keypad | 238 | // 0xca Keypad || 239 | // 0xcb Keypad : 240 | // 0xcc Keypad # 241 | // 0xcd Keypad Space 242 | // 0xce Keypad @ 243 | // 0xcf Keypad ! 244 | // 0xd0 Keypad Memory Store 245 | // 0xd1 Keypad Memory Recall 246 | // 0xd2 Keypad Memory Clear 247 | // 0xd3 Keypad Memory Add 248 | // 0xd4 Keypad Memory Subtract 249 | // 0xd5 Keypad Memory Multiply 250 | // 0xd6 Keypad Memory Divide 251 | // 0xd7 Keypad +/- 252 | // 0xd8 Keypad Clear 253 | // 0xd9 Keypad Clear Entry 254 | // 0xda Keypad Binary 255 | // 0xdb Keypad Octal 256 | // 0xdc Keypad Decimal 257 | // 0xdd Keypad Hexadecimal 258 | 259 | #define KEY_LEFTCTRL 0xe0 // Keyboard Left Control 260 | #define KEY_LEFTSHIFT 0xe1 // Keyboard Left Shift 261 | #define KEY_LEFTALT 0xe2 // Keyboard Left Alt 262 | #define KEY_LEFTMETA 0xe3 // Keyboard Left GUI 263 | #define KEY_RIGHTCTRL 0xe4 // Keyboard Right Control 264 | #define KEY_RIGHTSHIFT 0xe5 // Keyboard Right Shift 265 | #define KEY_RIGHTALT 0xe6 // Keyboard Right Alt 266 | #define KEY_RIGHTMETA 0xe7 // Keyboard Right GUI 267 | 268 | #define KEY_MEDIA_PLAYPAUSE 0xe8 269 | #define KEY_MEDIA_STOPCD 0xe9 270 | #define KEY_MEDIA_PREVIOUSSONG 0xea 271 | #define KEY_MEDIA_NEXTSONG 0xeb 272 | #define KEY_MEDIA_EJECTCD 0xec 273 | #define KEY_MEDIA_VOLUMEUP 0xed 274 | #define KEY_MEDIA_VOLUMEDOWN 0xee 275 | #define KEY_MEDIA_MUTE 0xef 276 | #define KEY_MEDIA_WWW 0xf0 277 | #define KEY_MEDIA_BACK 0xf1 278 | #define KEY_MEDIA_FORWARD 0xf2 279 | #define KEY_MEDIA_STOP 0xf3 280 | #define KEY_MEDIA_FIND 0xf4 281 | #define KEY_MEDIA_SCROLLUP 0xf5 282 | #define KEY_MEDIA_SCROLLDOWN 0xf6 283 | #define KEY_MEDIA_EDIT 0xf7 284 | #define KEY_MEDIA_SLEEP 0xf8 285 | #define KEY_MEDIA_COFFEE 0xf9 286 | #define KEY_MEDIA_REFRESH 0xfa 287 | #define KEY_MEDIA_CALC 0xfb 288 | 289 | #endif // USB_HID_KEYS -------------------------------------------------------------------------------- /esp8266Programmer/esp8266Programmer.ino: -------------------------------------------------------------------------------- 1 | /* From https://github.com/AprilBrother/cactus-micro-r2/tree/master/libraries/CactusMicro/examples/esp8266Programmer */ 2 | 3 | /************************************************** 4 | * Simple pass-through serial flash programmer 5 | * programming the ESP8266 by an Arduino 6 | * 7 | * This resembles the comfortable DTS controlled 8 | * programming mode one have with an FTDI or similiar 9 | * serial connection cable, where no manual reset of 10 | * the ESP is needed to upload code and run it. 11 | * Unfortunately there is no RTS/DTS control on the 12 | * Arduino Serial library, so we solely rely on timing. 13 | * 14 | * If the esptool does not wait or retry long enough, 15 | * you have to press the reset button 16 | * 17 | * Transmissions from the ESP are passed without any 18 | * modification. 19 | * 20 | * 21 | ***************************************************/ 22 | 23 | 24 | /* 25 | * connection table: 26 | * ESP8266 Cactus Micro Rev2 27 | * GPIO0 12 28 | * ENABLE 13 29 | * RX TX 30 | * TX RX 31 | * GND GND 32 | * 33 | */ 34 | 35 | int program_pin = 12; 36 | int enable_pin = 13; 37 | 38 | void setup() 39 | { 40 | Serial1.begin(115000); 41 | Serial.begin(115000); 42 | pinMode(enable_pin, OUTPUT); 43 | pinMode(program_pin, OUTPUT); 44 | digitalWrite(program_pin, LOW); 45 | digitalWrite(enable_pin,HIGH); 46 | 47 | //while(!Serial); 48 | 49 | Serial.println("ESP8266 programmer ready."); 50 | } 51 | 52 | void loop() 53 | { 54 | // pass data from ESP to host, if any 55 | while(Serial1.available()) 56 | { 57 | Serial.write((uint8_t)Serial1.read()); 58 | } 59 | 60 | // pass data from host to ESP, if any 61 | if(Serial.available()) 62 | { 63 | while(Serial.available()) 64 | { 65 | Serial1.write((uint8_t)Serial.read()); 66 | } 67 | } 68 | } 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /esp_duck/cli.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file esp_duck/cli.h 3 | \brief Command line interface header 4 | \author Stefan Kremser 5 | \copyright MIT License 6 | */ 7 | 8 | #pragma once 9 | 10 | #include // String, bool 11 | 12 | /*! \typedef PrintFunction 13 | * \brief A function that outputs a given string, for example to std::out. 14 | * \param s String to be printed 15 | */ 16 | typedef void (* PrintFunction)(const char* s); 17 | 18 | /*! \namespace CLI 19 | * \brief Command line interface module 20 | */ 21 | namespace cli { 22 | /*! Initializes the CLI module */ 23 | void begin(); 24 | 25 | /*! 26 | * \brief Processes user input as a command 27 | * 28 | * Analyzes the input string if it matches a command name and its arguments. 29 | * Resulting output, from a command or an error message, 30 | * will be passed to printfunc. 31 | * If echo is true, "# " will be passed to printfunc first. 32 | * 33 | * \param input String to be parsed 34 | * \param printfunc Function that prints the result 35 | * \param echo Flag to enable echo of input 36 | */ 37 | void parse(const char* input, PrintFunction printfunc, bool echo = true); 38 | } -------------------------------------------------------------------------------- /esp_duck/com.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file esp_duck/com.cpp 3 | \brief Communication Module source 4 | \author Stefan Kremser 5 | \copyright MIT License 6 | */ 7 | 8 | #include "com.h" 9 | 10 | #include // Arduino i2c 11 | 12 | #include "config.h" 13 | #include "debug.h" 14 | 15 | // ! Communication request codes 16 | #define REQ_SOT 0x01 // !< Start of transmission 17 | #define REQ_EOT 0x04 // !< End of transmission 18 | #define REQ_VERSION 0x02 // !< Request current version 19 | 20 | #define COM_VERSION 4 21 | 22 | typedef struct status_t { 23 | unsigned int version : 8; 24 | unsigned int wait : 16; 25 | unsigned int repeat : 8; 26 | } status_t; 27 | 28 | namespace com { 29 | // ========== PRIVATE ========== // 30 | bool connection = false; 31 | 32 | com_callback callback_done = NULL; 33 | com_callback callback_repeat = NULL; 34 | com_callback callback_error = NULL; 35 | 36 | bool react_on_status = false; 37 | bool new_transmission = false; 38 | 39 | status_t status; 40 | 41 | uint8_t transm_tries = 0; 42 | 43 | // ========= PRIVATE I2C ========= // 44 | 45 | #ifdef ENABLE_I2C 46 | unsigned long request_time = 0; 47 | 48 | void i2c_start_transmission() { 49 | Wire.beginTransmission(I2C_ADDR); 50 | debug("Transmitting '"); 51 | } 52 | 53 | void i2c_stop_transmission() { 54 | Wire.endTransmission(); 55 | debugln("' "); 56 | delay(1); 57 | } 58 | 59 | void i2c_transmit(char b) { 60 | Wire.write(b); 61 | } 62 | 63 | void i2c_request() { 64 | debug("I2C Request"); 65 | 66 | uint16_t prev_wait = status.wait; 67 | 68 | Wire.requestFrom(I2C_ADDR, sizeof(status_t)); 69 | 70 | if (Wire.available() == sizeof(status_t)) { 71 | status.version = Wire.read(); 72 | 73 | status.wait = Wire.read(); 74 | status.wait |= uint16_t(Wire.read()) << 8; 75 | 76 | status.repeat = Wire.read(); 77 | 78 | debugf(" %u", status.wait); 79 | } else { 80 | connection = false; 81 | debug(" ERROR"); 82 | } 83 | 84 | react_on_status = status.wait == 0 || 85 | status.repeat > 0 || 86 | ((prev_wait&1) ^ (status.wait&1)); 87 | 88 | debugln(); 89 | 90 | if (!react_on_status && (status.wait == prev_wait)) { 91 | debug("Last message was not processed"); 92 | 93 | if (transm_tries > 3) { 94 | connection = false; 95 | debugln("...LOOP ERROR"); 96 | } else { 97 | debugln("...repeating last line"); 98 | 99 | status.repeat = 1; 100 | 101 | react_on_status = true; 102 | 103 | ++transm_tries; 104 | } 105 | } else { 106 | transm_tries = 0; 107 | } 108 | 109 | request_time = millis(); 110 | } 111 | 112 | void i2c_begin() { 113 | unsigned long start_time = millis(); 114 | 115 | Wire.begin(I2C_SDA, I2C_SCL); 116 | Wire.setClock(I2C_CLOCK_SPEED); 117 | 118 | while (Wire.available()) Wire.read(); 119 | 120 | debugln("Connecting via i2c"); 121 | 122 | connection = true; 123 | 124 | send(MSG_CONNECTED); 125 | 126 | update(); 127 | 128 | debug("I2C Connection "); 129 | debugln(connection ? "OK" : "ERROR"); 130 | } 131 | 132 | void i2c_update() { 133 | if (!connection) return; 134 | 135 | bool processing = status.wait > 0; 136 | bool delay_over = request_time + status.wait < millis(); 137 | 138 | if (new_transmission || (processing && delay_over)) { 139 | new_transmission = false; 140 | i2c_request(); 141 | } 142 | } 143 | 144 | #else // ifdef ENABLE_I2C 145 | void i2c_start_transmission() {} 146 | 147 | void i2c_stop_transmission() {} 148 | 149 | void i2c_transmit(char b) {} 150 | 151 | void i2c_request() {} 152 | 153 | void i2c_begin() {} 154 | 155 | void i2c_update() {} 156 | 157 | #endif // ifdef ENABLE_I2C 158 | 159 | // ========= PRIVATE I2C ========= // 160 | 161 | #ifdef ENABLE_SERIAL 162 | bool ongoing_transmission = false; 163 | 164 | void serial_start_transmission() { 165 | debug("Transmitting '"); 166 | } 167 | 168 | void serial_stop_transmission() { 169 | SERIAL_PORT.flush(); 170 | debugln("' "); 171 | } 172 | 173 | void serial_transmit(char b) { 174 | SERIAL_PORT.write(b); 175 | } 176 | 177 | void serial_begin() { 178 | SERIAL_PORT.begin(SERIAL_BAUD); 179 | 180 | while (SERIAL_PORT.available()) SERIAL_PORT.read(); 181 | 182 | debug("Connecting via serial"); 183 | 184 | connection = true; 185 | 186 | send(MSG_CONNECTED); 187 | 188 | update(); 189 | 190 | debug("Serial Connection "); 191 | debugln(connection ? "OK" : "ERROR"); 192 | } 193 | 194 | void serial_update() { 195 | if (SERIAL_PORT.available() >= sizeof(status_t)+2) { 196 | if (SERIAL_PORT.read() == REQ_SOT) { 197 | uint16_t prev_wait = status.wait; 198 | 199 | status.version = SERIAL_PORT.read(); 200 | 201 | status.wait = SERIAL_PORT.read(); 202 | status.wait |= uint16_t(SERIAL_PORT.read()) << 8; 203 | 204 | status.repeat = SERIAL_PORT.read(); 205 | 206 | react_on_status = status.wait == 0 || 207 | status.repeat > 0 || 208 | ((prev_wait&1) ^ (status.wait&1)); 209 | 210 | while (SERIAL_PORT.available() && SERIAL_PORT.read() != REQ_EOT) {} 211 | } 212 | } 213 | } 214 | 215 | #else // ifdef ENABLE_SERIAL 216 | void serial_start_transmission() {} 217 | 218 | void serial_stop_transmission() {} 219 | 220 | void serial_transmit(char b) {} 221 | 222 | void serial_begin() {} 223 | 224 | void serial_update() {} 225 | 226 | #endif // ifdef ENABLE_SERIAL 227 | 228 | void start_transmission() { 229 | i2c_start_transmission(); 230 | serial_start_transmission(); 231 | } 232 | 233 | void stop_transmission() { 234 | i2c_stop_transmission(); 235 | serial_stop_transmission(); 236 | } 237 | 238 | void transmit(char b) { 239 | i2c_transmit(b); 240 | serial_transmit(b); 241 | } 242 | 243 | // ===== PUBLIC ===== // 244 | void begin() { 245 | status.version = 0; 246 | status.wait = 0; 247 | status.repeat = 0; 248 | 249 | i2c_begin(); 250 | serial_begin(); 251 | } 252 | 253 | void update() { 254 | i2c_update(); 255 | serial_update(); 256 | 257 | if (react_on_status) { 258 | react_on_status = false; 259 | 260 | debug("Com. status "); 261 | 262 | if (status.version != COM_VERSION) { 263 | debugf("ERROR %u\n", status.version); 264 | connection = false; 265 | if (callback_error) callback_error(); 266 | } else if (status.wait > 0) { 267 | debugf("PROCESSING %u\n", status.wait); 268 | } else if (status.repeat > 0) { 269 | debugf("REPEAT %u\n", status.repeat); 270 | if (callback_repeat) callback_repeat(); 271 | } else if ((status.wait == 0) && (status.repeat == 0)) { 272 | debugln("DONE"); 273 | if (callback_done) callback_done(); 274 | } else { 275 | debugln("idk"); 276 | } 277 | } 278 | } 279 | 280 | unsigned int send(char str) { 281 | return send(&str, 1); 282 | } 283 | 284 | unsigned int send(const char* str) { 285 | return send(str, strlen(str)); 286 | } 287 | 288 | unsigned int send(const char* str, size_t len) { 289 | // ! Truncate string to fit into buffer 290 | if (len > BUFFER_SIZE) len = BUFFER_SIZE; 291 | 292 | size_t sent = 0; // byte sent overall 293 | size_t i = 0; // index of string 294 | size_t j = 0; // byte sent for current packet 295 | 296 | start_transmission(); 297 | 298 | transmit(REQ_SOT); 299 | 300 | ++sent; 301 | ++j; 302 | 303 | while (i < len) { 304 | char b = str[i]; 305 | 306 | if ((b != '\n') && (b != '\n')) debug(b); 307 | transmit(b); 308 | 309 | ++i; 310 | ++j; 311 | ++sent; 312 | 313 | if (j == PACKET_SIZE/*sent % PACKET_SIZE == 0*/) { 314 | stop_transmission(); 315 | start_transmission(); 316 | j = 0; 317 | } 318 | } 319 | 320 | transmit(REQ_EOT); 321 | 322 | ++sent; 323 | 324 | stop_transmission(); 325 | 326 | new_transmission = true; 327 | 328 | // ! Return number of characters sent, minus 2 due to the signals 329 | return sent-2; 330 | } 331 | 332 | void onDone(com_callback c) { 333 | callback_done = c; 334 | } 335 | 336 | void onRepeat(com_callback c) { 337 | callback_repeat = c; 338 | } 339 | 340 | void onError(com_callback c) { 341 | callback_error = c; 342 | } 343 | 344 | bool connected() { 345 | return connection; 346 | } 347 | 348 | int getVersion() { 349 | return status.version; 350 | } 351 | } -------------------------------------------------------------------------------- /esp_duck/com.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file esp_duck/com.h 3 | \brief Communication Module header 4 | \author Stefan Kremser 5 | \copyright MIT License 6 | */ 7 | 8 | #pragma once 9 | 10 | /*! \typedef com_callback 11 | * \brief Callback function to react on different responses 12 | */ 13 | typedef void (* com_callback)(); 14 | 15 | /*! \namespace com 16 | * \brief Communication module 17 | */ 18 | namespace com { 19 | /*! Initializes the communication module */ 20 | void begin(); 21 | 22 | /*! Updates the communication module */ 23 | void update(); 24 | 25 | /*! Transmits string */ 26 | unsigned int send(char str); 27 | unsigned int send(const char* str); 28 | unsigned int send(const char* str, unsigned int len); 29 | 30 | /*! Sets callback for status done */ 31 | void onDone(com_callback c); 32 | 33 | /*! Sets callback for status error */ 34 | void onError(com_callback c); 35 | 36 | /*! Sets callback for status repeat */ 37 | void onRepeat(com_callback c); 38 | 39 | /*! Returns state of connection */ 40 | bool connected(); 41 | 42 | int getVersion(); 43 | } -------------------------------------------------------------------------------- /esp_duck/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #define VERSION "1.2.0" 9 | 10 | /*! ===== DEBUG Settings ===== */ 11 | // #define ENABLE_DEBUG 12 | // #define DEBUG_PORT Serial 13 | // #define DEBUG_BAUD 115200 14 | 15 | /*! ===== Communication Settings ===== */ 16 | #define ENABLE_SERIAL 17 | #define SERIAL_PORT Serial 18 | #define SERIAL_BAUD 9600 19 | 20 | // #define ENABLE_I2C 21 | #define I2C_ADDR 0x31 22 | // #define I2C_SDA 4 23 | // #define I2C_SCL 5 24 | #define I2C_CLOCK_SPEED 100000L 25 | 26 | #define BUFFER_SIZE 256 27 | #define PACKET_SIZE 32 28 | 29 | #define MSG_CONNECTED "LED 0 0 25\n" 30 | #define MSG_STARTED "LED 0 25 0\n" 31 | 32 | /*! ======EEPROM Settings ===== */ 33 | #define EEPROM_SIZE 4095 34 | #define EEPROM_BOOT_ADDR 3210 35 | #define BOOT_MAGIC_NUM 1234567890 36 | 37 | /*! ===== WiFi Settings ===== */ 38 | #define WIFI_SSID "wifiduck" 39 | #define WIFI_PASSWORD "wifiduck" 40 | #define WIFI_CHANNEL "1" 41 | 42 | #define HOSTNAME "wifiduck" 43 | #define URL "wifi.duck" 44 | 45 | /*! ========== Safty checks ========== */ 46 | #if !defined(ENABLE_I2C) && !defined(ENABLE_SERIAL) 47 | #define ENABLE_I2C 48 | #define I2C_SDA 4 49 | #define I2C_SCL 5 50 | #endif /* if !defined(ENABLE_I2C) || !defined(ENABLE_SERIAL) */ 51 | 52 | #if !defined(ESP8266) 53 | #error You are compiling for the wrong board, mate! Select something with an ESP8266. 54 | #endif /* ifdef DUCKMCU && DUCKMCU!="ATMEGA32U4" */ 55 | 56 | #if defined(ENABLE_DEBUG) && defined(ENABLE_SERIAL) && DEBUG_PORT == SERIAL_PORT 57 | #error Using same serial port for debugging and Communication!\ 58 | Use I2C instead or disable debug. 59 | #endif /* if DEBUG_PORT == SERIAL_PORT */ 60 | 61 | #if defined(ENABLE_I2C) && I2C_SDA==I2C_SCL 62 | #error SDA pin equals to SCL pin 63 | #endif /* if !defined(ENABLE_I2C) && !defined(ENABLE_I2C) */ 64 | 65 | #if defined(ENABLE_I2C) && defined(ENABLE_SERIAL) && (I2C_SDA==1 || I2C_SDA==3 || I2C_SCL==1 || I2C_SCL==3) 66 | #error I2C pins overlap with RX and TX pins. Disable serial debugging or change the I2C pins. 67 | #endif /* if !defined(ENABLE_I2C) && !defined(ENABLE_I2C) */ 68 | -------------------------------------------------------------------------------- /esp_duck/debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #ifdef ENABLE_DEBUG 11 | 12 | #define debug_init() DEBUG_PORT.begin(DEBUG_BAUD);\ 13 | DEBUG_PORT.setTimeout(200); 14 | 15 | #define debug(...) DEBUG_PORT.print(__VA_ARGS__) 16 | #define debugln(...) DEBUG_PORT.println(__VA_ARGS__) 17 | #define debugf(...) DEBUG_PORT.printf(__VA_ARGS__) 18 | 19 | #define debug_update()\ 20 | if (Serial.available()) {\ 21 | String input = Serial.readStringUntil('\n');\ 22 | cli::parse(input.c_str(), [] (const char* str) {\ 23 | Serial.println(str);\ 24 | });\ 25 | } 26 | 27 | #else /* ifdef ENABLE_DEBUG */ 28 | 29 | #define debug_init() 0 30 | 31 | #define debug(...) 0 32 | #define debugln(...) 0 33 | #define debugf(...) 0 34 | 35 | #define debug_update() 0 36 | 37 | #endif /* ifdef ENABLE_DEBUG */ -------------------------------------------------------------------------------- /esp_duck/duckscript.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #include "duckscript.h" 7 | 8 | #include "config.h" 9 | #include "debug.h" 10 | 11 | #include "com.h" 12 | #include "spiffs.h" 13 | 14 | namespace duckscript { 15 | // ===== PRIVATE ===== // 16 | File f; 17 | 18 | char * prevMessage { NULL }; 19 | size_t prevMessageLen { 0 }; 20 | 21 | bool running { false }; 22 | 23 | // ===== PUBLIC ===== // 24 | void run(String fileName) { 25 | if (fileName.length() > 0) { 26 | debugf("Run file %s\n", fileName.c_str()); 27 | f = spiffs::open(fileName); 28 | running = true; 29 | nextLine(); 30 | } 31 | } 32 | 33 | void nextLine() { 34 | if (!running) return; 35 | 36 | if (!f) { 37 | debugln("File error"); 38 | stopAll(); 39 | return; 40 | } 41 | 42 | if (!f.available()) { 43 | debugln("Reached end of file"); 44 | stopAll(); 45 | return; 46 | } 47 | 48 | char buf[BUFFER_SIZE]; 49 | unsigned int buf_i = 0; 50 | bool eol = false; // End of line 51 | 52 | while (f.available() && !eol && buf_i < BUFFER_SIZE) { 53 | uint8_t b = f.peek(); 54 | 55 | //utf8 56 | if((b & 0x80) == 0x80) { 57 | uint8_t extra_chars = 0; 58 | 59 | if((b & 0xC0) == 0xC0) { 60 | extra_chars = 2; 61 | } else if((b & 0xE0) == 0xC0) { 62 | extra_chars = 3; 63 | } else if((b & 0xF0) == 0xC0) { 64 | extra_chars = 4; 65 | } 66 | 67 | // utf8 char doesn't fit into buffer 68 | if ((buf_i + extra_chars) > BUFFER_SIZE) break; 69 | } 70 | 71 | eol = (b == '\n'); 72 | buf[buf_i] = f.read(); 73 | ++buf_i; 74 | // debug(char(b)); 75 | } 76 | 77 | if (!eol) debugln(); 78 | 79 | if (strncmp((char*)buf, "REPEAT", _min(buf_i, 6)) != 0) { 80 | if (prevMessage) free(prevMessage); 81 | prevMessageLen = buf_i; 82 | prevMessage = (char*)malloc(prevMessageLen + 1); 83 | memcpy(prevMessage, buf, buf_i); 84 | prevMessage[buf_i] = '\0'; 85 | } 86 | 87 | com::send(buf, buf_i); 88 | 89 | if (strncmp((char*)buf, "REPEAT", _min(buf_i, 6)) != 0) { 90 | if (prevMessage) free(prevMessage); 91 | prevMessageLen = buf_i; 92 | prevMessage = (char*)malloc(prevMessageLen + 1); 93 | memcpy(prevMessage, buf, buf_i); 94 | prevMessage[buf_i] = '\0'; 95 | } 96 | } 97 | 98 | void repeat() { 99 | if (!prevMessage) { 100 | stopAll(); 101 | } else { 102 | debugln("Repeating last message"); 103 | com::send(prevMessage, prevMessageLen); 104 | } 105 | } 106 | 107 | void stopAll() { 108 | if (running) { 109 | if (f) f.close(); 110 | running = false; 111 | debugln("Stopped script"); 112 | } 113 | } 114 | 115 | void stop(String fileName) { 116 | if (fileName.length() == 0) stopAll(); 117 | else { 118 | if (running && f && (fileName == currentScript())) { 119 | f.close(); 120 | running = false; 121 | debugln("Stopped script"); 122 | } 123 | } 124 | } 125 | 126 | bool isRunning() { 127 | return running; 128 | } 129 | 130 | String currentScript() { 131 | if (!running) return String(); 132 | return String(f.name()); 133 | } 134 | } -------------------------------------------------------------------------------- /esp_duck/duckscript.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #include // String 9 | 10 | namespace duckscript { 11 | void runTest(); 12 | void run(String fileName); 13 | 14 | void nextLine(); 15 | void repeat(); 16 | void stopAll(); 17 | void stop(String fileName); 18 | 19 | bool isRunning(); 20 | String currentScript(); 21 | }; -------------------------------------------------------------------------------- /esp_duck/eeprom.cpp: -------------------------------------------------------------------------------- 1 | #include "eeprom.h" 2 | 3 | #include "config.h" 4 | 5 | // Used to verify memory 6 | typedef struct boot { 7 | unsigned int magic_num : 32; 8 | unsigned int boot_num : 8; 9 | } boot; 10 | 11 | namespace eeprom { 12 | void begin() { 13 | EEPROM.begin(EEPROM_SIZE); 14 | } 15 | 16 | void end() { 17 | EEPROM.end(); 18 | } 19 | 20 | bool checkBootNum() { 21 | boot b; 22 | 23 | EEPROM.get(EEPROM_BOOT_ADDR, b); 24 | 25 | if ((b.magic_num == BOOT_MAGIC_NUM) && (b.boot_num < 3)) { 26 | saveObject(EEPROM_BOOT_ADDR, boot{ BOOT_MAGIC_NUM, ++b.boot_num }); 27 | return true; 28 | } 29 | 30 | return false; 31 | } 32 | 33 | void resetBootNum() { 34 | saveObject(EEPROM_BOOT_ADDR, boot{ BOOT_MAGIC_NUM, 1 }); 35 | } 36 | }; -------------------------------------------------------------------------------- /esp_duck/eeprom.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace eeprom { 6 | void begin(); 7 | void end(); 8 | 9 | bool checkBootNum(); 10 | void resetBootNum(); 11 | 12 | template 13 | void saveObject(const int address, const T& t) { 14 | EEPROM.put(address, t); 15 | 16 | EEPROM.commit(); 17 | } 18 | 19 | template 20 | void getObject(const int address, const T& t) { 21 | EEPROM.get(address, t); 22 | } 23 | }; -------------------------------------------------------------------------------- /esp_duck/esp_duck.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #include "config.h" 7 | #include "debug.h" 8 | 9 | #include "com.h" 10 | #include "duckscript.h" 11 | #include "webserver.h" 12 | #include "spiffs.h" 13 | #include "settings.h" 14 | #include "cli.h" 15 | 16 | void setup() { 17 | debug_init(); 18 | 19 | delay(200); 20 | 21 | com::begin(); 22 | 23 | spiffs::begin(); 24 | settings::begin(); 25 | cli::begin(); 26 | webserver::begin(); 27 | 28 | com::onDone(duckscript::nextLine); 29 | com::onError(duckscript::stopAll); 30 | com::onRepeat(duckscript::repeat); 31 | 32 | if (spiffs::freeBytes() > 0) com::send(MSG_STARTED); 33 | 34 | delay(10); 35 | com::update(); 36 | 37 | debug("\n[~~~ WiFi Duck v"); 38 | debug(VERSION); 39 | debugln(" Started! ~~~]"); 40 | debugln(" __"); 41 | debugln("___( o)>"); 42 | debugln("\\ <_. )"); 43 | debugln(" `---' hjw\n"); 44 | 45 | duckscript::run(settings::getAutorun()); 46 | } 47 | 48 | void loop() { 49 | com::update(); 50 | webserver::update(); 51 | 52 | debug_update(); 53 | } -------------------------------------------------------------------------------- /esp_duck/esp_duck_generic.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/esp_duck/esp_duck_generic.bin -------------------------------------------------------------------------------- /esp_duck/settings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #include "settings.h" 7 | 8 | #include "spiffs.h" 9 | #include "debug.h" 10 | #include "config.h" 11 | #include "eeprom.h" 12 | 13 | #define SETTINGS_ADDRES 1 14 | #define SETTINGS_MAGIC_NUM 1234567891 15 | 16 | namespace settings { 17 | // ===== PRIVATE ===== // 18 | typedef struct settings_t { 19 | uint32_t magic_num; 20 | 21 | char ssid[33]; 22 | char password[65]; 23 | char channel[5]; 24 | char autorun[65]; 25 | } settings_t; 26 | 27 | settings_t data; 28 | 29 | // ===== PUBLIC ====== // 30 | void begin() { 31 | eeprom::begin(); 32 | load(); 33 | } 34 | 35 | void load() { 36 | eeprom::getObject(SETTINGS_ADDRES, data); 37 | if (data.magic_num != SETTINGS_MAGIC_NUM) reset(); 38 | 39 | if (data.ssid[32] != 0) setSSID(WIFI_SSID); 40 | if (data.password[64] != 0) setPassword(WIFI_PASSWORD); 41 | if (data.channel[4] != 0) setChannel(WIFI_CHANNEL); 42 | if (data.autorun[64] != 0) setAutorun(""); 43 | } 44 | 45 | void reset() { 46 | debugln("Resetting Settings"); 47 | data.magic_num = SETTINGS_MAGIC_NUM; 48 | setSSID(WIFI_SSID); 49 | setPassword(WIFI_PASSWORD); 50 | setChannel(WIFI_CHANNEL); 51 | } 52 | 53 | void save() { 54 | debugln("Saving Settings"); 55 | eeprom::saveObject(SETTINGS_ADDRES, data); 56 | } 57 | 58 | String toString() { 59 | String s; 60 | 61 | s += "ssid="; 62 | s += getSSID(); 63 | s += "\n"; 64 | s += "password="; 65 | s += getPassword(); 66 | s += "\n"; 67 | s += "channel="; 68 | s += getChannel(); 69 | s += "\n"; 70 | s += "autorun="; 71 | s += getAutorun(); 72 | s += "\n"; 73 | 74 | return s; 75 | } 76 | 77 | const char* getSSID() { 78 | return data.ssid; 79 | } 80 | 81 | const char* getPassword() { 82 | return data.password; 83 | } 84 | 85 | const char* getChannel() { 86 | return data.channel; 87 | } 88 | 89 | int getChannelNum() { 90 | if (strcmp(data.channel, "auto") != 0) return atoi(data.channel); 91 | return 1; 92 | } 93 | 94 | const char* getAutorun() { 95 | return data.autorun; 96 | } 97 | 98 | void set(const char* name, const char* value) { 99 | if (strcmp(name, "ssid") == 0) { 100 | setSSID(value); 101 | } else if (strcmp(name, "password") == 0) { 102 | setPassword(value); 103 | } else if (strcmp(name, "channel") == 0) { 104 | setChannel(value); 105 | } else if (strcmp(name, "autorun") == 0) { 106 | setAutorun(value); 107 | } 108 | } 109 | 110 | void setSSID(const char* ssid) { 111 | if (ssid) { 112 | memset(data.ssid, 0, 33); 113 | strncpy(data.ssid, ssid, 32); 114 | 115 | save(); 116 | } 117 | } 118 | 119 | void setPassword(const char* password) { 120 | if (password && (strlen(password) >= 8)) { 121 | memset(data.password, 0, 65); 122 | strncpy(data.password, password, 64); 123 | 124 | save(); 125 | } 126 | } 127 | 128 | void setChannel(const char* channel) { 129 | if (channel && ((strcmp(channel, "auto") == 0) || ((atoi(channel) >= 1) && (atoi(channel) <= 13)))) { 130 | memset(data.channel, 0, 5); 131 | strncpy(data.channel, channel, 4); 132 | 133 | save(); 134 | } 135 | } 136 | 137 | void setAutorun(const char* autorun) { 138 | if (autorun) { 139 | memset(data.autorun, 0, 65); 140 | strncpy(data.autorun, autorun, 64); 141 | 142 | save(); 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /esp_duck/settings.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | #include // String 9 | 10 | namespace settings { 11 | void begin(); 12 | void load(); 13 | 14 | void reset(); 15 | void save(); 16 | 17 | String toString(); 18 | 19 | const char* getSSID(); 20 | const char* getPassword(); 21 | const char* getChannel(); 22 | const char* getAutorun(); 23 | 24 | int getChannelNum(); 25 | 26 | void set(const char* name, const char* value); 27 | 28 | void setSSID(const char* ssid); 29 | void setPassword(const char* password); 30 | void setChannel(const char* channel); 31 | void setAutorun(const char* autorun); 32 | } -------------------------------------------------------------------------------- /esp_duck/spiffs.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #include "spiffs.h" 7 | 8 | #include "config.h" 9 | #include "debug.h" 10 | 11 | namespace spiffs { 12 | File streamFile; 13 | 14 | // ===== PRIVATE ===== // 15 | void fixPath(String& path) { 16 | if (!path.startsWith("/")) { 17 | path = "/" + path; 18 | } 19 | } 20 | 21 | // ===== PUBLIC ====== // 22 | void begin() { 23 | debug("Initializing SPIFFS..."); 24 | SPIFFS.begin(); 25 | debugln("OK"); 26 | 27 | String FILE_NAME = "/startup_spiffs_test"; 28 | 29 | remove(FILE_NAME); 30 | create(FILE_NAME); 31 | File f = open(FILE_NAME); 32 | if (!f) { 33 | format(); 34 | } else { 35 | f.close(); 36 | remove(FILE_NAME); 37 | } 38 | } 39 | 40 | void format() { 41 | debug("Formatting SPIFFS..."); 42 | SPIFFS.format(); 43 | debugln("OK"); 44 | } 45 | 46 | size_t size() { 47 | FSInfo fs_info; 48 | 49 | SPIFFS.info(fs_info); 50 | return fs_info.totalBytes; 51 | } 52 | 53 | size_t usedBytes() { 54 | FSInfo fs_info; 55 | 56 | SPIFFS.info(fs_info); 57 | return fs_info.usedBytes; 58 | } 59 | 60 | size_t freeBytes() { 61 | FSInfo fs_info; 62 | 63 | SPIFFS.info(fs_info); 64 | return fs_info.totalBytes - fs_info.usedBytes; 65 | } 66 | 67 | size_t size(String fileName) { 68 | fixPath(fileName); 69 | 70 | File f = SPIFFS.open(fileName, "r"); 71 | 72 | return f.size(); 73 | } 74 | 75 | bool exists(String fileName) { 76 | return SPIFFS.exists(fileName); 77 | } 78 | 79 | File open(String fileName) { 80 | fixPath(fileName); 81 | 82 | return SPIFFS.open(fileName, "a+"); 83 | } 84 | 85 | void create(String fileName) { 86 | fixPath(fileName); 87 | 88 | File f = SPIFFS.open(fileName, "a+"); 89 | 90 | f.close(); 91 | } 92 | 93 | void remove(String fileName) { 94 | fixPath(fileName); 95 | 96 | SPIFFS.remove(fileName); 97 | } 98 | 99 | void rename(String oldName, String newName) { 100 | fixPath(oldName); 101 | fixPath(newName); 102 | 103 | SPIFFS.rename(oldName, newName); 104 | } 105 | 106 | void write(String fileName, const char* str) { 107 | File f = open(fileName); 108 | 109 | if (f) { 110 | f.println(str); 111 | f.close(); 112 | debugln("Wrote file"); 113 | } else { 114 | debugln("File error"); 115 | } 116 | } 117 | 118 | void write(String fileName, const uint8_t* buf, size_t len) { 119 | File f = open(fileName); 120 | 121 | if (f) { 122 | f.write(buf, len); 123 | f.close(); 124 | debugln("Wrote file"); 125 | } else { 126 | debugln("File error"); 127 | } 128 | } 129 | 130 | String listDir(String dirName) { 131 | String res; 132 | 133 | fixPath(dirName); 134 | 135 | Dir dir = SPIFFS.openDir(dirName); 136 | 137 | while (dir.next()) { 138 | res += dir.fileName(); 139 | res += ' '; 140 | res += size(dir.fileName()); 141 | res += '\n'; 142 | } 143 | 144 | if (res.length() == 0) { 145 | res += "\n"; 146 | } 147 | 148 | return res; 149 | } 150 | 151 | void streamOpen(String fileName) { 152 | streamClose(); 153 | streamFile = open(fileName); 154 | if (!streamFile) debugln("ERROR: No stream file open"); 155 | } 156 | 157 | void streamWrite(const char* buf, size_t len) { 158 | if (streamFile) streamFile.write((uint8_t*)buf, len); 159 | else debugln("ERROR: No stream file open"); 160 | } 161 | 162 | size_t streamRead(char* buf, size_t len) { 163 | if (streamFile) { 164 | size_t i; 165 | 166 | for (i = 0; i // String 9 | #include // File 10 | 11 | namespace spiffs { 12 | void begin(); 13 | void format(); 14 | 15 | size_t size(); 16 | size_t usedBytes(); 17 | size_t freeBytes(); 18 | 19 | size_t size(String fileName); 20 | bool exists(String fileName); 21 | 22 | File open(String fileName); 23 | void create(String fileName); 24 | 25 | void remove(String fileName); 26 | void rename(String oldName, String newName); 27 | void write(String fileName, const char* str); 28 | void write(String fileName, const uint8_t* buf, size_t len); 29 | 30 | String listDir(String dirName); 31 | 32 | void streamOpen(String fileName); 33 | void streamWrite(const char* buf, size_t len); 34 | size_t streamRead(char* buf, size_t len); 35 | size_t streamReadUntil(char* buf, char delimiter, size_t max_len); 36 | void streamClose(); 37 | bool streaming(); 38 | size_t streamAvailable(); 39 | } -------------------------------------------------------------------------------- /esp_duck/webserver.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #include "webserver.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "config.h" 16 | #include "debug.h" 17 | #include "cli.h" 18 | #include "spiffs.h" 19 | #include "settings.h" 20 | 21 | #include "webfiles.h" 22 | 23 | void reply(AsyncWebServerRequest* request, int code, const char* type, const uint8_t* data, size_t len) { 24 | AsyncWebServerResponse* response = 25 | request->beginResponse_P(code, type, data, len); 26 | 27 | response->addHeader("Content-Encoding", "gzip"); 28 | request->send(response); 29 | } 30 | 31 | namespace webserver { 32 | // ===== PRIVATE ===== // 33 | AsyncWebServer server(80); 34 | AsyncWebSocket ws("/ws"); 35 | AsyncEventSource events("/events"); 36 | 37 | AsyncWebSocketClient* currentClient { nullptr }; 38 | 39 | DNSServer dnsServer; 40 | 41 | bool reboot = false; 42 | IPAddress apIP(192, 168, 4, 1); 43 | 44 | void wsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) { 45 | if (type == WS_EVT_CONNECT) { 46 | debugf("WS Client connected %u\n", client->id()); 47 | } 48 | 49 | else if (type == WS_EVT_DISCONNECT) { 50 | debugf("WS Client disconnected %u\n", client->id()); 51 | } 52 | 53 | else if (type == WS_EVT_ERROR) { 54 | debugf("WS Client %u error(%u): %s\n", client->id(), *((uint16_t*)arg), (char*)data); 55 | } 56 | 57 | else if (type == WS_EVT_PONG) { 58 | debugf("PONG %u\n", client->id()); 59 | } 60 | 61 | else if (type == WS_EVT_DATA) { 62 | AwsFrameInfo* info = (AwsFrameInfo*)arg; 63 | 64 | if (info->opcode == WS_TEXT) { 65 | char* msg = (char*)data; 66 | msg[len] = 0; 67 | 68 | debugf("Message from %u [%llu byte]=%s", client->id(), info->len, msg); 69 | 70 | currentClient = client; 71 | cli::parse(msg, [](const char* str) { 72 | webserver::send(str); 73 | debugf("%s\n", str); 74 | }, false); 75 | currentClient = nullptr; 76 | } 77 | } 78 | } 79 | 80 | // ===== PUBLIC ===== // 81 | void begin() { 82 | // Access Point 83 | WiFi.hostname(HOSTNAME); 84 | 85 | // WiFi.mode(WIFI_AP_STA); 86 | WiFi.softAP(settings::getSSID(), settings::getPassword(), settings::getChannelNum()); 87 | WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); 88 | debugf("Started Access Point \"%s\":\"%s\"\n", settings::getSSID(), settings::getPassword()); 89 | 90 | // Webserver 91 | server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { 92 | request->redirect("/index.html"); 93 | }); 94 | 95 | server.onNotFound([](AsyncWebServerRequest* request) { 96 | request->redirect("/error404.html"); 97 | }); 98 | 99 | server.on("/run", [](AsyncWebServerRequest* request) { 100 | String message; 101 | 102 | if (request->hasParam("cmd")) { 103 | message = request->getParam("cmd")->value(); 104 | } 105 | 106 | request->send(200, "text/plain", "Run: " + message); 107 | 108 | cli::parse(message.c_str(), [](const char* str) { 109 | debugf("%s\n", str); 110 | }, false); 111 | }); 112 | 113 | WEBSERVER_CALLBACK; 114 | 115 | // Arduino OTA Update 116 | ArduinoOTA.onStart([]() { 117 | events.send("Update Start", "ota"); 118 | }); 119 | ArduinoOTA.onEnd([]() { 120 | events.send("Update End", "ota"); 121 | }); 122 | ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { 123 | char p[32]; 124 | sprintf(p, "Progress: %u%%\n", (progress/(total/100))); 125 | events.send(p, "ota"); 126 | }); 127 | ArduinoOTA.onError([](ota_error_t error) { 128 | if (error == OTA_AUTH_ERROR) events.send("Auth Failed", "ota"); 129 | else if (error == OTA_BEGIN_ERROR) events.send("Begin Failed", "ota"); 130 | else if (error == OTA_CONNECT_ERROR) events.send("Connect Failed", "ota"); 131 | else if (error == OTA_RECEIVE_ERROR) events.send("Recieve Failed", "ota"); 132 | else if (error == OTA_END_ERROR) events.send("End Failed", "ota"); 133 | }); 134 | ArduinoOTA.setHostname(HOSTNAME); 135 | ArduinoOTA.begin(); 136 | 137 | events.onConnect([](AsyncEventSourceClient* client) { 138 | client->send("hello!", NULL, millis(), 1000); 139 | }); 140 | server.addHandler(&events); 141 | 142 | // Web OTA 143 | server.on("/update", HTTP_POST, [](AsyncWebServerRequest* request) { 144 | reboot = !Update.hasError(); 145 | 146 | AsyncWebServerResponse* response; 147 | response = request->beginResponse(200, "text/plain", reboot ? "OK" : "FAIL"); 148 | response->addHeader("Connection", "close"); 149 | 150 | request->send(response); 151 | }, [](AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final) { 152 | if (!index) { 153 | debugf("Update Start: %s\n", filename.c_str()); 154 | Update.runAsync(true); 155 | if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) { 156 | Update.printError(Serial); 157 | } 158 | } 159 | if (!Update.hasError()) { 160 | if (Update.write(data, len) != len) { 161 | Update.printError(Serial); 162 | } 163 | } 164 | if (final) { 165 | if (Update.end(true)) { 166 | debugf("Update Success: %uB\n", index+len); 167 | } else { 168 | Update.printError(Serial); 169 | } 170 | } 171 | }); 172 | 173 | dnsServer.setTTL(300); 174 | dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure); 175 | dnsServer.start(53, URL, apIP); 176 | 177 | MDNS.addService("http", "tcp", 80); 178 | 179 | // Websocket 180 | ws.onEvent(wsEvent); 181 | server.addHandler(&ws); 182 | 183 | // Start Server 184 | server.begin(); 185 | debugln("Started Webserver"); 186 | } 187 | 188 | void update() { 189 | ArduinoOTA.handle(); 190 | if (reboot) ESP.restart(); 191 | dnsServer.processNextRequest(); 192 | } 193 | 194 | void send(const char* str) { 195 | if (currentClient) currentClient->text(str); 196 | } 197 | } -------------------------------------------------------------------------------- /esp_duck/webserver.h: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | #pragma once 7 | 8 | namespace webserver { 9 | void begin(); 10 | void update(); 11 | void send(const char* str); 12 | } -------------------------------------------------------------------------------- /img/diy_example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/img/diy_example.jpg -------------------------------------------------------------------------------- /img/dstike_atmega.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/img/dstike_atmega.jpg -------------------------------------------------------------------------------- /img/dstike_esp8266.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/img/dstike_esp8266.jpg -------------------------------------------------------------------------------- /img/dstike_normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/img/dstike_normal.jpg -------------------------------------------------------------------------------- /img/dstikeboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/img/dstikeboard.jpg -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/img/logo.png -------------------------------------------------------------------------------- /img/malw.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/img/malw.jpg -------------------------------------------------------------------------------- /img/ota.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/img/ota.jpg -------------------------------------------------------------------------------- /img/pcbs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/img/pcbs.jpg -------------------------------------------------------------------------------- /img/pcbs_soldered.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/img/pcbs_soldered.jpg -------------------------------------------------------------------------------- /img/showcase.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/img/showcase.gif -------------------------------------------------------------------------------- /img/thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamff-dev/WiFiDuck-whid/fc6945df3b1b001cead307c035d0631c1986832a/img/thumbnail.jpg -------------------------------------------------------------------------------- /test.script: -------------------------------------------------------------------------------- 1 | REM default delay 2 | DEFAULTDELAY 200 3 | 4 | REM LED Test 5 | LED 0 100 0 6 | LED 255 0 0 7 | DELAY 1000 8 | LED 0 255 0 9 | DELAY 1000 10 | LED 0 0 255 11 | 12 | REM open notepad 13 | GUI r 14 | STRING notepad 15 | ENTER 16 | 17 | REM hello world 18 | STRING Hello World! 19 | ENTER 20 | 21 | REM delay test 22 | DELAY 1000 23 | . 24 | DELAY 3000 25 | . 26 | DELAY 5000 27 | . 28 | ENTER 29 | 30 | REM repeat test 31 | STRING Hello World! 32 | REPEAT 2 33 | ENTER 34 | 35 | REM us char test 36 | LOCALE US 37 | STRING !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~` 38 | ENTER 39 | 40 | REM de char test 41 | LOCALE DE 42 | STRING !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~²³äöüÄÖÜ߀°§` 43 | ENTER 44 | 45 | REM gb char test 46 | LOCALE GB 47 | STRING !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~£¦¬éíúóÉÍÚÓ€ 48 | ENTER 49 | 50 | REM es char test 51 | LOCALE ES 52 | STRING !"#$%&'()*+,-./0123456789: =>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~¿¡ñÑçǺª€·¨` 53 | ENTER 54 | 55 | REM dk char test 56 | LOCALE DK 57 | STRING !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}£¤§ÅÆØåæø 58 | ENTER 59 | 60 | REM ru char test 61 | LOCALE RU 62 | STRING ёЁйЙцЦуУкКеЕнНгГшШщЩзЗхХъЪФФыЫвВаАпПрРоОлЛдДжЖэЭяЯчЧсСмМиИтТьЬбБюЮ 63 | ENTER 64 | 65 | REM overflow test 66 | STRING 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 01234567890 67 | ENTER 68 | 69 | REM keycode test 70 | A 71 | KEYCODE 0x02 0x04 72 | KEYCODE 2 4 73 | 74 | REM close notepad 75 | DELAY 5000 76 | ALT F4 77 | RIGHT 78 | ENTER 79 | -------------------------------------------------------------------------------- /web/credits.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | WiFi Duck | About 15 | 16 | 17 | 18 | 19 | 20 | 28 |
29 |
30 |
31 |

Disclaimer

32 |

33 | This tool is intended to be used for testing, training, and educational purposes only.
34 | Never use it to do harm or create damage!
35 |
36 | The continuation of this project counts on you! 37 |

38 |
39 |
40 |

License

41 |

42 | MIT License
43 | Copyright (c) 2021 Spacehuhn Technologies
44 |
45 | Permission is hereby granted, free of charge, to any person obtaining a copy 46 | of this software and associated documentation files (the "Software"), to deal 47 | in the Software without restriction, including without limitation the rights 48 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 49 | copies of the Software, and to permit persons to whom the Software is 50 | furnished to do so, subject to the following conditions:
51 |
52 | The above copyright notice and this permission notice shall be included in all 53 | copies or substantial portions of the Software.
54 |
55 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 56 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 57 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 58 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 59 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 60 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 61 | SOFTWARE. 62 |

63 |
64 |
65 |

Credits

66 |

67 | Other software used for this project:
68 | - Arduino
69 | - Neopixel Library
70 | - Dotstar Library
71 | - AVR, ESP8266 & SAMD Arduino Core
72 | - ESPAsyncTCP
73 | - ESPAsyncWebServer
74 | - SimpleCLI 75 |

76 |
77 |
78 |

Thanks

79 |

80 | Thanks to everyone that helped making this project reality, especially:
81 | - deantonious for helping to design and improve the user experience
82 | - Seytonic for educating people about the topic
83 | - Travis Lin for creating, selling and financially supporting us with custom made hardware
84 | - and YOU for using it! 85 |

86 |
87 |
88 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /web/error404.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | WiFi Duck | 404 14 | 15 | 16 | 17 | 18 | 19 | 27 |
28 |
29 |
30 | 31 |

404

32 |

33 | Page not found :( 34 |

35 | Back to Homepage 36 |
37 |
38 | 45 | 46 | -------------------------------------------------------------------------------- /web/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | 7 | // ========== Global Variables ========== // 8 | 9 | // ! List of files returned by "ls" command 10 | var file_list = ""; 11 | 12 | // ! Variable to save interval for updating status continously 13 | var status_interval = undefined; 14 | 15 | // ! Unsaved content in the editor 16 | var unsaved_changed = false; 17 | 18 | // ! Flag if editor has loaded a file yet 19 | var file_opened = false; 20 | 21 | // ========== Global Functions ========== // 22 | 23 | // ===== Value Getters ===== // 24 | function get_new_filename() { 25 | return E("newFile").value; 26 | } 27 | 28 | function get_editor_filename() { 29 | return E("editorFile").value; 30 | } 31 | 32 | function set_editor_filename(filename) { 33 | return E("editorFile").value = filename; 34 | } 35 | 36 | function get_editor_content() { 37 | var content = E("editor").value; 38 | 39 | if (!content.endsWith("\n")) 40 | content = content + "\n"; 41 | 42 | return content; 43 | } 44 | 45 | // ! Update status until it's no longer "running" 46 | function check_status() { 47 | if (current_status.includes("running") || current_status.includes("saving")) 48 | ws_update_status(); 49 | else 50 | stop_status_interval(); 51 | } 52 | 53 | // ! Start interval that checks and updates the status continously 54 | function start_status_interval() { 55 | if (status_interval) return; // !< Only continue if status_interval not set 56 | 57 | ws_update_status(); // !< Get current status 58 | status_interval = setInterval(check_status, 500); // !< Start interval 59 | } 60 | 61 | // ! Stop interval that checks and updates the status continously 62 | function stop_status_interval() { 63 | if (!status_interval) return; // !< Only continue if status_interval was set 64 | 65 | // ! Stop interval and unset variable 66 | clearInterval(status_interval); 67 | status_interval = undefined; 68 | } 69 | 70 | // ! Append string to script content 71 | function append(str) { 72 | E("editor").value += str; 73 | } 74 | 75 | // ! Updates file list and memory usage 76 | function update_file_list() { 77 | ws_send("mem", function(msg) { 78 | var lines = msg.split(/\n/); 79 | 80 | if(lines.length == 1) { 81 | console.error("Malformed response:"); 82 | console.error(msg); 83 | return; 84 | } 85 | 86 | var byte = lines[0].split(" ")[0]; 87 | var used = lines[1].split(" ")[0]; 88 | var free = lines[2].split(" ")[0]; 89 | 90 | var percent = Math.floor(byte / 100); 91 | var freepercent = Math.floor(free / percent); 92 | 93 | E("freeMemory").innerHTML = used + " byte used (" + freepercent + "% free)"; 94 | 95 | file_list = ""; 96 | 97 | ws_send("ls", function(csv) { 98 | file_list += csv; 99 | 100 | var lines = file_list.split(/\n/); 101 | var tableHTML = "\n"; 102 | 103 | tableHTML += "\n"; 104 | tableHTML += "File\n"; 105 | tableHTML += "Byte\n"; 106 | tableHTML += "Actions\n"; 107 | tableHTML += "\n"; 108 | tableHTML += "\n"; 109 | tableHTML += "\n"; 110 | 111 | for (var i = 0; i < lines.length; i++) { 112 | var data = lines[i].split(" "); 113 | var fileName = data[0]; 114 | var fileSize = data[1]; 115 | 116 | if (fileName.length > 0) { 117 | if (i == 0 && !file_opened) { 118 | read(fileName); 119 | } 120 | tableHTML += "\n"; 121 | tableHTML += "" + fileName + "\n"; 122 | tableHTML += "" + fileSize + "\n"; 123 | tableHTML += "\n"; 124 | tableHTML += "\n"; 125 | tableHTML += "\n"; 126 | tableHTML += "\n"; 127 | } 128 | } 129 | tableHTML += "\n"; 130 | 131 | E("scriptTable").innerHTML = tableHTML; 132 | }); 133 | }); 134 | } 135 | 136 | // ! Format SPIFFS 137 | function format() { 138 | if (confirm("Format SPIFFS? This will delete all scripts!")) { 139 | ws_send("format", log_ws); 140 | alert("Formatting will take a minute.\nYou have to reconnect afterwards."); 141 | } 142 | } 143 | 144 | // ! Run script 145 | function run(fileName) { 146 | ws_send("run \"" + fixFileName(fileName) + "\"", log_ws); 147 | start_status_interval(); 148 | } 149 | 150 | // ! Stop running specific script 151 | function stop(fileName) { 152 | ws_send("stop \"" + fixFileName(fileName) + "\"", log_ws, true); 153 | } 154 | 155 | // ! Stop running all scripts 156 | function stopAll() { 157 | ws_send("stop", log_ws, true); 158 | } 159 | 160 | // ! Recursive read from stream 161 | function read_stream() { 162 | ws_send("read", function(content) { 163 | if (content != "> END") { 164 | E("editor").value += content; 165 | read_stream(); 166 | status("reading..."); 167 | } else { 168 | ws_send("close", log_ws); 169 | ws_update_status(); 170 | } 171 | }); 172 | } 173 | 174 | // ! Open stream to a file 175 | function read(fileName) { 176 | stop(fileName); 177 | 178 | fileName = fixFileName(fileName); 179 | 180 | set_editor_filename(fileName); 181 | E("editor").value = ""; 182 | 183 | ws_send("stream \"" + fileName + "\"", log_ws); 184 | 185 | read_stream(); // !< Read file contents (recursively) 186 | 187 | file_opened = true; 188 | } 189 | 190 | // ! Create a new file 191 | function create(fileName) { 192 | stop(fileName); 193 | 194 | fileName = fixFileName(fileName); 195 | 196 | if (file_list.includes(fileName + " ")) { 197 | read(fileName); 198 | } else { 199 | set_editor_filename(fileName); 200 | E("editor").value = ""; 201 | 202 | ws_send("create \"" + fileName + "\"", log_ws); 203 | update_file_list(); 204 | } 205 | } 206 | 207 | // ! Delete a file 208 | function remove(fileName) { 209 | stop(fileName); 210 | ws_send("remove \"" + fixFileName(fileName) + "\"", log_ws); 211 | update_file_list(); 212 | unsaved_changed = true; 213 | } 214 | 215 | function autorun(fileName) { 216 | ws_send("set autorun \"" + fixFileName(fileName) + "\"", log_ws); 217 | } 218 | 219 | // ! Write content to file 220 | function write(fileName, content) { 221 | stop(fileName); 222 | 223 | fileName = fixFileName(fileName); 224 | 225 | ws_send("remove \"/temporary_script\"", log_ws); 226 | ws_send("create \"/temporary_script\"", log_ws); 227 | 228 | ws_send("stream \"/temporary_script\"", log_ws); 229 | 230 | var ws_send_log = function(msg) { 231 | status("saving..."); 232 | log_ws(msg); 233 | }; 234 | 235 | var pktsize = 1024; 236 | 237 | for (var i = 0; i < Math.ceil(content.length / pktsize); i++) { 238 | var begin = i * pktsize; 239 | var end = begin + pktsize; 240 | if (end > content.length) end = content.length; 241 | 242 | ws_send_raw(content.substring(begin, end), ws_send_log); 243 | } 244 | 245 | ws_send("close", log_ws); 246 | 247 | ws_send("remove \"" + fileName + "\"", log_ws); 248 | ws_send("rename \"/temporary_script\" \"" + fileName + "\"", log_ws); 249 | 250 | ws_update_status(); 251 | } 252 | 253 | // ! Save file that is currently open in the editor 254 | function save() { 255 | write(get_editor_filename(), get_editor_content()); 256 | unsaved_changed = false; 257 | E("editorinfo").innerHTML = "saved"; 258 | update_file_list(); 259 | } 260 | 261 | // ! Function that is called once the websocket connection was established 262 | function ws_connected() { 263 | update_file_list(); 264 | } 265 | 266 | // ========== Startup ========== // 267 | window.addEventListener("load", function() { 268 | E("reconnect").onclick = ws_init; 269 | E("scriptsReload").onclick = update_file_list; 270 | E("format").onclick = format; 271 | E("stop").onclick = stopAll; 272 | 273 | E("editorReload").onclick = function() { 274 | read(get_editor_filename()); 275 | }; 276 | 277 | E("editorSave").onclick = save; 278 | 279 | E("editorDelete").onclick = function() { 280 | if (confirm("Delete " + get_editor_filename() + "?")) { 281 | remove(get_editor_filename()); 282 | } 283 | }; 284 | 285 | E("editorDownload").onclick = function() { 286 | download_txt(get_editor_filename(), get_editor_content()); 287 | }; 288 | 289 | E("editorStop").onclick = function() { 290 | stop(get_editor_filename()); 291 | } 292 | 293 | E("editorRun").onclick = function() { 294 | if (unsaved_changed) { 295 | save(); 296 | } 297 | 298 | run(get_editor_filename()); 299 | }; 300 | 301 | E("editor").onkeyup = function() { 302 | unsaved_changed = true; 303 | E("editorinfo").innerHTML = "unsaved changes"; 304 | } 305 | 306 | E("editorAutorun").onclick = function() { 307 | if (confirm("Run this script automatically on startup?\nYou can disable it in the settings.")) 308 | autorun(get_editor_filename()); 309 | } 310 | 311 | // ! Make all s append to the editor when clicked 312 | var codes = document.querySelectorAll("code"); 313 | for (var i = 0; i < codes.length; i++) { 314 | codes[i].addEventListener("click", function() { 315 | append(this.innerHTML + " \n"); 316 | }); 317 | } 318 | 319 | ws_init(); 320 | }, false); -------------------------------------------------------------------------------- /web/script.js: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | 6 | // ===== Helper Functions ===== // 7 | function log(msg) { 8 | console.log(msg); 9 | } 10 | 11 | function E(id) { 12 | return document.getElementById(id); 13 | } 14 | 15 | function download_txt(fileName, fileContent) { 16 | var element = document.createElement('a'); 17 | element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(fileContent)); 18 | element.setAttribute('download', fileName); 19 | 20 | element.style.display = 'none'; 21 | document.body.appendChild(element); 22 | 23 | element.click(); 24 | 25 | document.body.removeChild(element); 26 | } 27 | 28 | function fixFileName(fileName) { 29 | if (fileName.length > 0) { 30 | if (fileName[0] != '/') { 31 | fileName = '/' + fileName; 32 | } 33 | 34 | fileName = fileName.replace(/ /g, '\-'); 35 | } 36 | return fileName; 37 | } 38 | 39 | // ===== DOM Manipulation ===== // 40 | function status(mode) { 41 | current_status = mode; 42 | 43 | if (mode == "connected") { 44 | E("status").style.backgroundColor = "#3c5"; 45 | } else if (mode == "disconnected") { 46 | E("status").style.backgroundColor = "#d33"; 47 | } else if (mode.includes("problem") || mode.includes("error")) { 48 | E("status").style.backgroundColor = "#ffc107"; 49 | } else /*if (mode == "connecting...")*/ { 50 | E("status").style.backgroundColor = "#0ae"; 51 | } 52 | 53 | E("status").innerHTML = mode; 54 | } 55 | 56 | // ===== Web Socket ===== // 57 | function log_ws(msg) { 58 | log("[WS] " + msg); 59 | } 60 | 61 | function set_version(str) { 62 | E("version").innerHTML = str; 63 | } 64 | 65 | var ws = null; // web socket instance 66 | var ws_callback = log_ws; // message receive callback 67 | var ws_msg_queue = []; // queue for outgoing messages 68 | var cts = false; // clear to send flag for message queue 69 | 70 | var current_status = ""; 71 | 72 | var ws_queue_interval = null; 73 | 74 | // ===== WebSocket Functions ===== // 75 | function ws_msg_queue_update() { 76 | if (cts && ws_msg_queue.length > 0) { 77 | 78 | var item = ws_msg_queue.shift(); 79 | 80 | var message = item.message; 81 | var callback = item.callback; 82 | 83 | ws.send(message); 84 | ws_callback = callback; 85 | 86 | console.debug("# " + message); 87 | cts = false; 88 | } 89 | } 90 | 91 | function ws_send(message, callback, force = false) { 92 | if (!message.endsWith('\n')) message += '\n'; 93 | 94 | ws_send_raw(message, callback, force); 95 | } 96 | 97 | function ws_send_raw(message, callback, force = false) { 98 | var obj = { 99 | "message": message, 100 | "callback": callback 101 | }; 102 | 103 | if (force) { 104 | ws_msg_queue.unshift(obj); 105 | } else { 106 | ws_msg_queue.push(obj); 107 | } 108 | } 109 | 110 | function ws_update_status() { 111 | ws_send("status", status); 112 | } 113 | 114 | function ws_init() { 115 | status("connecting..."); 116 | 117 | ws = new WebSocket("ws://192.168.4.1/ws"); 118 | 119 | ws.onopen = function(event) { 120 | log_ws("connected"); 121 | status("connected"); 122 | 123 | ws_send("close", log_ws, true); 124 | ws_send("version", set_version); 125 | 126 | ws_connected(); 127 | }; 128 | 129 | ws.onclose = function(event) { 130 | log_ws("disconnected"); 131 | status("disconnected"); 132 | }; 133 | 134 | ws.onmessage = function(event) { 135 | var msg = event.data; 136 | 137 | log_ws(msg); 138 | 139 | if (ws_callback && msg.length > 0) { 140 | ws_callback(msg); 141 | } 142 | 143 | cts = true; 144 | }; 145 | 146 | ws.onerror = function(event) { 147 | log_ws("error"); 148 | status("error"); 149 | 150 | console.error(event); 151 | }; 152 | 153 | cts = true; 154 | 155 | if (ws_queue_interval) clearInterval(ws_queue_interval); 156 | ws_queue_interval = setInterval(ws_msg_queue_update, 1); 157 | } -------------------------------------------------------------------------------- /web/settings.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | WiFi Duck | Settings 14 | 15 | 16 | 17 | 18 | 19 | 20 | 28 |
29 |
30 |
31 |

WiFi

32 |

Restart the device to apply new settings.

33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 |
SSID:
Password:
Channel:
Autorun Script:
55 | 56 |
57 |
58 |

Update

59 | 60 |

61 | Go to wifiduck.com/releases 62 | to check for updates.
Select a .bin file and press upload to update the device.
63 |

64 | 65 |
66 | 67 | 68 |
69 |
70 | 77 | 78 | -------------------------------------------------------------------------------- /web/settings.js: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | // ===== WebSocket Actions ===== // 6 | function load_settings() { 7 | ws_send("settings", function(msg) { 8 | var lines = msg.split(/\n/); 9 | 10 | var ssid = lines[0].split("=")[1]; 11 | var password = lines[1].split("=")[1]; 12 | var channel = lines[2].split("=")[1]; 13 | var autorun = lines[3].split("=")[1]; 14 | 15 | E("ssid").innerHTML = ssid; 16 | E("password").innerHTML = password; 17 | E("channel").innerHTML = channel; 18 | E("autorun").innerHTML = autorun; 19 | }); 20 | } 21 | 22 | function ws_connected() { 23 | load_settings(); 24 | } 25 | 26 | 27 | // ===== Startup ===== // 28 | window.addEventListener("load", function() { 29 | 30 | E("edit_ssid").onclick = function() { 31 | var newssid = prompt("SSID (1-32 chars)", E("ssid").innerHTML); 32 | 33 | if (newssid) { 34 | if (newssid.length >= 1 && newssid.length <= 32) { 35 | ws_send("set ssid \"" + newssid + "\"", function(msg) { 36 | load_settings(); 37 | }); 38 | } else { 39 | alert("ERROR: Invalid length"); 40 | } 41 | } 42 | }; 43 | 44 | E("edit_password").onclick = function() { 45 | var newpassword = prompt("Password (8-64 chars)", E("password").innerHTML); 46 | 47 | if (newpassword) { 48 | if (newpassword.length >= 8 && newpassword.length <= 64) { 49 | ws_send("set password \"" + newpassword + "\"", function(msg) { 50 | load_settings(); 51 | }); 52 | } else { 53 | alert("ERROR: Invalid length"); 54 | } 55 | } 56 | }; 57 | 58 | E("edit_channel").onclick = function() { 59 | var newchannel = prompt("Channel (1-14)", E("channel").innerHTML); 60 | 61 | if (newchannel) { 62 | if (parseInt(newchannel) >= 1 && parseInt(newchannel) <= 13) { 63 | ws_send("set channel " + newchannel, function(msg) { 64 | load_settings(); 65 | }); 66 | } else { 67 | alert("ERROR: Invalid channel number"); 68 | } 69 | } 70 | }; 71 | 72 | E("disable_autorun").onclick = function() { 73 | ws_send("set autorun \"\"", function(msg) { 74 | load_settings(); 75 | }); 76 | }; 77 | 78 | E("reset").onclick = function() { 79 | if (confirm("Reset all settings to default?")) { 80 | ws_send("reset", function(msg) { 81 | load_settings(); 82 | }); 83 | } 84 | }; 85 | 86 | ws_init(); 87 | }, false); -------------------------------------------------------------------------------- /web/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | /* Global */ 6 | 7 | body { 8 | background: #36393e; 9 | margin: 0; 10 | color: #bfbfbf; 11 | font-family: sans-serif; 12 | } 13 | 14 | main { 15 | width: 60vw; 16 | margin-left: auto; 17 | margin-right: auto; 18 | } 19 | 20 | section { 21 | margin-top: 50px; 22 | } 23 | 24 | footer { 25 | font-size: .95em; 26 | text-align: center; 27 | margin-top: 3em; 28 | margin-bottom: 3em; 29 | } 30 | 31 | h1 { 32 | font-size: 1.4rem; 33 | margin-top: 1rem; 34 | background: 35 | #2f3136; 36 | padding: 10px; 37 | border-radius: 4px; 38 | border-left: solid #20c20e 5px; 39 | font-weight: 100; 40 | letter-spacing: 5px; 41 | } 42 | 43 | h2 { 44 | font-size: 1.2rem; 45 | margin-top: 1rem; 46 | background: #2f3136; 47 | padding: 10px; 48 | border-radius: 4px; 49 | border-left: solid #20c20e 5px; 50 | font-weight: 100; 51 | letter-spacing: 5px; 52 | } 53 | 54 | h3 { 55 | font-size: 1rem; 56 | margin-top: 1rem; 57 | background: #2f3136; 58 | padding: 10px; 59 | border-radius: 4px; 60 | border-left: solid #20c20e 5px; 61 | font-weight: 100; 62 | letter-spacing: 5px; 63 | } 64 | 65 | label { 66 | line-height: 44px; 67 | } 68 | 69 | p { 70 | margin: .5em 0; 71 | } 72 | 73 | /* Navigation bar */ 74 | nav { 75 | display: block; 76 | background: #2f3136; 77 | font-weight: bold; 78 | } 79 | 80 | nav a { 81 | color: inherit; 82 | } 83 | 84 | .menu { 85 | width: 60vw; 86 | list-style-type: none; 87 | margin: 0; 88 | padding: 0; 89 | margin: 0 auto; 90 | display: flex; 91 | flex-direction: row; 92 | display:block; 93 | } 94 | 95 | .menu li { 96 | margin: 10px 5px 10px 0; 97 | display: inline-block; 98 | } 99 | 100 | .menu li:last-child { 101 | float: right; 102 | } 103 | 104 | .menu a { 105 | padding: .75em; 106 | border-radius: 4px; 107 | } 108 | 109 | .menu a:hover { 110 | background: #42444a; 111 | } 112 | 113 | code { 114 | background: #ccc; 115 | padding: 3px; 116 | border-radius: 3px; 117 | word-break: keep-all !important; 118 | color: #000; 119 | line-height: 24px; 120 | } 121 | 122 | .terminal { 123 | max-height: 100vh; 124 | background:#2f3136; 125 | height: 400px; 126 | border-radius: 4px; 127 | overflow-y: scroll; 128 | padding: 15px; 129 | } 130 | 131 | .white { 132 | color: #bfbfbf; 133 | } 134 | 135 | .warn { 136 | color: #ffc107; 137 | } 138 | 139 | .danger { 140 | color: #F04747; 141 | } 142 | 143 | .success { 144 | color: #43B581; 145 | } 146 | 147 | .primary { 148 | color: #0092db; 149 | } 150 | 151 | .select { 152 | width: 98px !important; 153 | padding: 0 !important; 154 | } 155 | 156 | .selected { 157 | background: #4974a9; 158 | } 159 | 160 | .clickable { 161 | cursor: pointer; 162 | } 163 | 164 | .code { 165 | font-family: "Courier New", Courier, monospace; 166 | } 167 | 168 | .reload { 169 | float: right; 170 | line-height: 1.1rem; 171 | cursor: pointer; 172 | font-size: 1.5rem; 173 | margin-top: 4px; 174 | } 175 | 176 | .reload:hover { 177 | text-decoration: none; 178 | } 179 | 180 | #status { 181 | text-align: center; 182 | text-transform: capitalize; 183 | padding: 5px; 184 | color: #fff; 185 | position: sticky; 186 | top: 0; 187 | z-index: 99; 188 | } 189 | 190 | .debugger { 191 | font-family: monospace; 192 | background: #2f3136; 193 | border-radius: 4px; 194 | padding: 10px; 195 | margin-bottom: 4px; 196 | } 197 | 198 | #editor-primary-buttons { 199 | float: right; 200 | } 201 | 202 | #editor-primary-buttons p { 203 | text-align: right; 204 | } 205 | 206 | /* ===== CHECKBOX ===== */ 207 | /* Customize the label (the container) */ 208 | .checkBoxContainer { 209 | display: block; 210 | position: relative; 211 | padding-left: 35px; 212 | margin-bottom: 12px; 213 | cursor: pointer; 214 | font-size: 22px; 215 | -webkit-user-select: none; 216 | -moz-user-select: none; 217 | -ms-user-select: none; 218 | user-select: none; 219 | height: 32px; 220 | } 221 | 222 | /* Hide the browser's default checkbox */ 223 | .checkBoxContainer input { 224 | position: absolute; 225 | opacity: 0; 226 | cursor: pointer; 227 | } 228 | 229 | /* Create a custom checkbox */ 230 | .checkmark { 231 | position: absolute; 232 | top: 8px; 233 | left: 0; 234 | height: 28px; 235 | width: 28px; 236 | background-color: #2F3136; 237 | border-radius: 4px; 238 | } 239 | 240 | /* Create the checkmark/indicator (hidden when not checked) */ 241 | .checkmark:after { 242 | content: ""; 243 | position: absolute; 244 | display: none; 245 | } 246 | 247 | /* Show the checkmark when checked */ 248 | .checkBoxContainer input:checked~.checkmark:after { 249 | display: block; 250 | } 251 | 252 | .checkBoxContainer .checkmark:after { 253 | left: 10px; 254 | top: 7px; 255 | width: 4px; 256 | height: 10px; 257 | border: solid white; 258 | border-width: 0 3px 3px 0; 259 | -webkit-transform: rotate(45deg); 260 | -ms-transform: rotate(45deg); 261 | transform: rotate(45deg); 262 | } 263 | 264 | /* ERROR */ 265 | .hide { 266 | display: none; 267 | } 268 | 269 | .show { 270 | display: block !important; 271 | animation-name: fadeIn; 272 | animation-duration: 1s; 273 | } 274 | 275 | @keyframes fadeIn { 276 | 0% { 277 | opacity: 0; 278 | } 279 | 280 | 100% { 281 | opacity: 1; 282 | } 283 | } 284 | 285 | hr { 286 | background: #3e4146; 287 | border-top: 1px dotted#fff; 288 | border-bottom: none; 289 | margin: 0; 290 | } 291 | 292 | a { 293 | color: #1ca8eb; 294 | text-decoration: none; 295 | } 296 | 297 | a:hover { 298 | color: #fff; 299 | text-decoration: underline dotted; 300 | } 301 | 302 | /* Meter */ 303 | .meter_background { 304 | background: #42464D; 305 | width: 100%; 306 | word-break: normal; 307 | min-width: 100px; 308 | } 309 | 310 | .meter_forground { 311 | color: #fff; 312 | padding: 4px 0; 313 | /* + one of the colors below 314 | (width will be set by the JS) */ 315 | } 316 | 317 | .meter_green { 318 | background: #43B581; 319 | } 320 | 321 | .meter_orange { 322 | background: #FAA61A; 323 | } 324 | 325 | .meter_red { 326 | background: #F04747; 327 | } 328 | 329 | .meter_value { 330 | padding-left: 8px; 331 | } 332 | 333 | /* Tables */ 334 | table { 335 | width: 100%; 336 | min-width: 400px; 337 | margin-bottom: 2em; 338 | border-collapse: collapse; 339 | } 340 | 341 | th { 342 | word-break: break-word; 343 | } 344 | 345 | th, td { 346 | padding: 10px 6px; 347 | text-align: left; 348 | border-bottom: 1px solid #5d5d5d; 349 | } 350 | 351 | #ducky-functions-table td { 352 | min-width: 180px; 353 | } 354 | 355 | @media only screen and (max-width: 768px), (min-device-width: 768px) and (max-device-width: 1024px) { 356 | 357 | .menu { 358 | width: 90vw; 359 | } 360 | 361 | main { 362 | width: 90vw !important; 363 | } 364 | 365 | /* Force table to not be like tables anymore */ 366 | #ducky-functions-table table, #ducky-functions-table thead, #ducky-functions-table tbody, #ducky-functions-table th, #ducky-functions-table td, #ducky-functions-table tr { 367 | display: block; 368 | } 369 | 370 | /* Hide table headers (but not display: none;, for accessibility) */ 371 | #ducky-functions-table thead tr { 372 | position: absolute; 373 | top: -9999px; 374 | left: -9999px; 375 | } 376 | 377 | #ducky-functions-table tr { 378 | border: none; 379 | } 380 | 381 | #ducky-functions-table tr:nth-child(odd) { 382 | background: #2f3136; 383 | border-radius: 4px; 384 | } 385 | 386 | #ducky-functions-table td { 387 | /* Behave like a "row" */ 388 | border: none; 389 | border-bottom: 2px solid transparent; 390 | position: relative; 391 | padding-left: 120px; 392 | line-height: 1.8rem; 393 | } 394 | 395 | #ducky-functions-table td:before { 396 | /* Now like a table header */ 397 | position: absolute; 398 | /* Top/left values mimic padding */ 399 | top: 6px; 400 | left: 6px; 401 | width: 120px; 402 | padding-right: 10px; 403 | white-space: nowrap; 404 | } 405 | 406 | /* 407 | Label the data 408 | */ 409 | #ducky-functions-table td:nth-of-type(1):before { 410 | content: "Command"; 411 | } 412 | 413 | #ducky-functions-table td:nth-of-type(2):before { 414 | content: "Example"; 415 | } 416 | 417 | #ducky-functions-table td:nth-of-type(3):before { 418 | content: "Description"; 419 | } 420 | } 421 | 422 | /* Inputs and buttons */ 423 | textarea { 424 | color: #bfbfbf; 425 | resize: vertical; 426 | width: 100%; 427 | height: 400px; 428 | padding: 15px; 429 | border: none; 430 | border-radius: 4px; 431 | background: #2f3136; 432 | font-family: "Courier New", Courier, monospace; 433 | box-sizing: border-box; 434 | -moz-box-sizing: border-box; 435 | -webkit-box-sizing: border-box; 436 | } 437 | 438 | input[type="file"] { 439 | padding: 7px 20px; 440 | } 441 | 442 | input[type="text"] { 443 | color: #bfbfbf; 444 | text-transform: none; 445 | width: 10em; 446 | } 447 | 448 | input[type="text"]:hover { 449 | cursor: text; 450 | } 451 | 452 | input, button { 453 | padding: 10px 20px; 454 | border: none; 455 | border-radius: 4px; 456 | background: #2f3136; 457 | letter-spacing: .1rem; 458 | text-transform: uppercase; 459 | margin: 2px 0; 460 | } 461 | 462 | input:hover, button:hover { 463 | background: #42444a; 464 | cursor: pointer; 465 | } 466 | 467 | .setting { 468 | width: 100% !important; 469 | max-width: 284px !important; 470 | } 471 | 472 | /* GRID SYSTEM */ 473 | .row { 474 | position: relative; 475 | width: 100%; 476 | margin-top: 10px; 477 | } 478 | 479 | .row [class^="col"] { 480 | float: left; 481 | } 482 | 483 | .col-1, 484 | .col-2, 485 | .col-3, 486 | .col-4, 487 | .col-5, 488 | .col-6, 489 | .col-7, 490 | .col-8, 491 | .col-9, 492 | .col-10, 493 | .col-11, 494 | .col-12 { 495 | width: 96%; 496 | } 497 | 498 | .row::after { 499 | content: ""; 500 | display: table; 501 | clear: both; 502 | } 503 | 504 | .hidden-sm { 505 | display: none; 506 | } 507 | 508 | @media only screen and (min-width: 24em) { 509 | .col-1 { 510 | width: 4.33%; 511 | } 512 | 513 | .col-2 { 514 | width: 12.66%; 515 | } 516 | 517 | .col-3 { 518 | width: 21%; 519 | } 520 | 521 | .col-4 { 522 | width: 29.33%; 523 | } 524 | 525 | .col-5 { 526 | width: 37.66%; 527 | } 528 | 529 | .col-6 { 530 | width: 46%; 531 | } 532 | 533 | .col-7 { 534 | width: 54.33%; 535 | } 536 | 537 | .col-8 { 538 | width: 62.66%; 539 | } 540 | 541 | .col-9 { 542 | width: 71%; 543 | } 544 | 545 | .col-10 { 546 | width: 79.33%; 547 | } 548 | 549 | .col-11 { 550 | width: 87.66%; 551 | } 552 | 553 | .col-12 { 554 | width: 96%; 555 | } 556 | 557 | .hidden-sm { 558 | display: block; 559 | } 560 | } 561 | -------------------------------------------------------------------------------- /web/terminal.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | WiFi Duck | Terminal 14 | 15 | 16 | 17 | 18 | 19 | 20 | 28 |
29 |
30 |
31 |

Terminal

32 |
33 | 34 | 35 | 36 | 37 |
38 |
39 |
40 |
41 |
42 |
43 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /web/terminal.js: -------------------------------------------------------------------------------- 1 | /* 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | */ 5 | // ===== WebSocket Actions ===== // 6 | function ws_connected() {} 7 | 8 | // ===== Startup ===== // 9 | window.addEventListener("load", function() { 10 | E("send").onclick = function() { 11 | var input = E("input").value; 12 | 13 | E("output").innerHTML += "# " + input + "
"; 14 | 15 | E("reconnect").onclick = ws_init; 16 | 17 | ws_send(input, function(msg) { 18 | log(msg); 19 | E("output").innerHTML += msg.replace(/\n/g, "
"); 20 | E("output").scrollTop = E("output").scrollHeight; 21 | }); 22 | }; 23 | 24 | E("clear").onclick = function() { 25 | E("output").innerHTML = ""; 26 | }; 27 | 28 | ws_init(); 29 | }, false); -------------------------------------------------------------------------------- /webconverter.py: -------------------------------------------------------------------------------- 1 | """ 2 | This software is licensed under the MIT License. See the license file for details. 3 | Source: https://github.com/spacehuhntech/WiFiDuck 4 | """ 5 | 6 | import os 7 | import binascii 8 | import gzip 9 | 10 | def get_file_content(path): 11 | file = open(path,"r") 12 | content = file.read().encode("utf-8") 13 | file.close(); 14 | 15 | gzip_content = gzip.compress(content) 16 | 17 | print(f"({len(content)} -> {len(gzip_content)} byte)...", end="") 18 | 19 | return gzip_content 20 | 21 | def get_varname(filename): 22 | return filename.replace(".","_").lower() 23 | 24 | def get_file_type(filename): 25 | file_ending = filename.split('.')[1] 26 | if file_ending == "js": 27 | return "application/javascript" 28 | elif file_ending == "css": 29 | return "text/css" 30 | elif file_ending == "html": 31 | return "text/html" 32 | else: 33 | return "text/plain" 34 | 35 | def get_response_code(filename): 36 | if filename == "error404.html": 37 | return 404 38 | else: 39 | return 200 40 | 41 | def build_hex_string(varname, content): 42 | hexstr = f"const uint8_t {varname}[] PROGMEM = {{ " 43 | 44 | for c in content: 45 | hexstr += f"{hex(c)}," 46 | 47 | hexstr = hexstr[:-1] 48 | hexstr += " };\n\n" 49 | 50 | return hexstr 51 | 52 | def write_server_callback(filename, output): 53 | varname = get_varname(filename) 54 | filetype = get_file_type(filename) 55 | response_code = get_response_code(filename) 56 | 57 | output.write(f"\\\nserver.on(\"/{filename}\", HTTP_GET, [](AsyncWebServerRequest* request) {{") 58 | output.write(f"\\\n\treply(request, {response_code}, \"{filetype}\", {varname}, sizeof({varname}));") 59 | output.write(f"\\\n}});") 60 | 61 | def write_hex_array(filename, output): 62 | print(f"Converting {filename}...", end="") 63 | 64 | path = f"web/{filename}" 65 | content = get_file_content(path) 66 | varname = get_varname(filename) 67 | 68 | hex_array = build_hex_string(varname, content) 69 | 70 | output.write(hex_array) 71 | 72 | print("OK") 73 | 74 | def write_callbacks(files, output): 75 | for filename in files: 76 | write_server_callback(filename, output); 77 | 78 | def write_arrays(files, output): 79 | for filename in files: 80 | write_hex_array(filename, output) 81 | 82 | def main(): 83 | web_files = os.listdir("web/") 84 | 85 | outputfile = open("esp_duck/webfiles.h", "w+") 86 | outputfile.write("#pragma once\n\n") 87 | 88 | outputfile.write(f"#define WEBSERVER_CALLBACK ") 89 | 90 | write_callbacks(web_files, outputfile) 91 | 92 | outputfile.write("\n\n") 93 | 94 | write_arrays(web_files, outputfile) 95 | 96 | outputfile.close() 97 | 98 | if __name__== "__main__": 99 | main() 100 | --------------------------------------------------------------------------------