├── .github ├── CODE_OF_CONDUCT.md ├── FUNDING.yml ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── 1--error-report.yaml │ ├── 2--question.yaml │ ├── 3--feature-request.yaml │ ├── 4--bug-report.yaml │ └── 5--locale.yaml ├── config.yml └── stale.yml ├── .gitignore ├── LICENSE ├── README.md ├── atmega_duck ├── Adafruit_DotStar.cpp ├── Adafruit_DotStar.h ├── NeoPixel.cpp ├── NeoPixel.h ├── 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_ee.h ├── locale_es.h ├── locale_esmx.h ├── locale_fi.h ├── locale_fr.h ├── locale_gb.h ├── locale_gr.h ├── locale_hu.h ├── locale_ie.h ├── locale_in.h ├── locale_is.h ├── locale_it.h ├── locale_lt.h ├── locale_lv.h ├── locale_nl.h ├── locale_no.h ├── locale_pl.h ├── locale_pt.h ├── locale_ptbr.h ├── locale_ro.h ├── locale_ru.h ├── locale_se.h ├── locale_si.h ├── locale_sk.h ├── locale_tr.h ├── locale_types.h ├── locale_ua.h ├── locale_us.h ├── locales.h ├── parser.c ├── parser.h ├── serial_bridge.cpp ├── serial_bridge.h └── usb_hid_keys.h ├── 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 ├── settings.cpp ├── settings.h ├── spiffs.cpp ├── spiffs.h ├── webfiles.h ├── webserver.cpp └── webserver.h ├── reset_sketch └── reset_sketch.ino ├── 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 /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, religion, or sexual identity 11 | and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the 27 | overall community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or 32 | advances of any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email 36 | address, without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [INSERT CONTACT METHOD]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series 87 | of actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or 94 | permanent ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within 114 | the community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.0, available at 120 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 121 | 122 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 123 | enforcement ladder](https://github.com/mozilla/diversity). 124 | 125 | [homepage]: https://www.contributor-covenant.org 126 | 127 | For answers to common questions about this code of conduct, see the FAQ at 128 | https://www.contributor-covenant.org/faq. Translations are available at 129 | https://www.contributor-covenant.org/translations. 130 | 131 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: spacehuhntech 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: spacehuhn 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['spacehuhn.com/store'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | > Please search for existing (open and closed) issues first to avoid duplicates. 2 | Also have a look at the [Wiki](https://spacehuhn.wiki). 3 | 4 | ``` 5 | PASTE YOUR ERROR/COMPILE LOGS HERE 6 | ``` -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1--error-report.yaml: -------------------------------------------------------------------------------- 1 | name: Problem/Error report 2 | description: I encountered a problem and need help to solve it 3 | labels: ["help wanted"] 4 | body: 5 | - type: checkboxes 6 | attributes: 7 | label: Is there an existing issue for this? 8 | description: Please search to see if an issue already exists for the problem you encountered. 9 | options: 10 | - label: I have searched the existing issues 11 | required: true 12 | - type: input 13 | id: description 14 | attributes: 15 | label: Describe your problem 16 | description: A clear and short description of what the problem is. 17 | placeholder: Whenever I do X, it throws an error... 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: steps 22 | attributes: 23 | label: Steps to reproduce 24 | description: Tell us how we can reproduce the behavior 25 | placeholder: | 26 | 1. Go to ... 27 | 2. Click on .... 28 | 3. Scroll down to .... 29 | 4. See error 30 | value: # 31 | validations: 32 | required: true 33 | - type: input 34 | id: environment 35 | attributes: 36 | label: What hardware are you using? 37 | description: Is it a DIY built? If you bought it, how is the product called? 38 | placeholder: DIY-built (Pro Micro + D1 mini) 39 | validations: 40 | required: true 41 | - type: textarea 42 | id: context 43 | attributes: 44 | label: Anything else? 45 | description: Tell us more if needed. Add links, references, or anything that will give us more context. You can attach images by clicking this area to highlight it and then dragging files in 46 | placeholder: I think this might be caused by ... 47 | value: # 48 | validations: 49 | required: false 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2--question.yaml: -------------------------------------------------------------------------------- 1 | name: Question 2 | description: I have a question about this project 3 | labels: ["question"] 4 | body: 5 | - type: checkboxes 6 | attributes: 7 | label: Is there an existing issue for this? 8 | description: Please search to see if an issue already exists for question. 9 | options: 10 | - label: I have searched the existing issues 11 | required: true 12 | - type: input 13 | id: question 14 | attributes: 15 | label: Your Question 16 | description: A clear and short question. 17 | placeholder: How can I do X? 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: context 22 | attributes: 23 | label: Description 24 | description: Tell us more if needed. Add links, references, or anything that will give us more context. You can attach images by clicking this area to highlight it and then dragging files in 25 | placeholder: # 26 | value: # 27 | validations: 28 | required: false 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3--feature-request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: I have an idea for improving this project 3 | title: "[Feature Request]: " 4 | labels: ["feature request"] 5 | body: 6 | - type: checkboxes 7 | attributes: 8 | label: Is there an existing issue for this? 9 | description: Please search to see if an issue already exists for your feature. 10 | options: 11 | - label: I have searched the existing issues 12 | required: true 13 | - type: textarea 14 | id: problem 15 | attributes: 16 | label: Problem 17 | description: Is your feature request related to a problem? Please describe 18 | placeholder: I'm always frustrated when ... 19 | validations: 20 | required: false 21 | - type: textarea 22 | id: solution 23 | attributes: 24 | label: Solution 25 | description: What do you want to happen? 26 | placeholder: # 27 | value: # 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: alternatives 32 | attributes: 33 | label: Alternatives 34 | description: Are there alternative solutions or features you've considered? 35 | placeholder: # 36 | value: # 37 | validations: 38 | required: false 39 | - type: textarea 40 | id: context 41 | attributes: 42 | label: Anything else? 43 | description: Tell us more if needed. Add links, references, or anything that will give us more context. You can attach images by clicking this area to highlight it and then dragging files in 44 | placeholder: # 45 | value: # 46 | validations: 47 | required: false 48 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/4--bug-report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: I found a reproducible bug 3 | title: "[Bug]: " 4 | labels: ["bug"] 5 | body: 6 | - type: checkboxes 7 | attributes: 8 | label: Is there an existing issue for this? 9 | description: Please search to see if an issue already exists for the bug you encountered. 10 | options: 11 | - label: I have searched the existing issues 12 | required: true 13 | - type: input 14 | id: description 15 | attributes: 16 | label: Describe the bug 17 | description: A clear and short description of what the bug is. 18 | placeholder: Doing X leads to a crash when Y is enabled... 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: steps 23 | attributes: 24 | label: Steps to reproduce 25 | description: Tell us how we can reproduce the behavior 26 | placeholder: | 27 | 1. Go to ... 28 | 2. Click on .... 29 | 3. Scroll down to .... 30 | 4. See error 31 | value: # 32 | validations: 33 | required: true 34 | - type: input 35 | id: environment 36 | attributes: 37 | label: What hardware are you using? 38 | description: Is it a DIY built? If you bought it, how is the product called? 39 | placeholder: DIY-built (Pro Micro + D1 mini) 40 | validations: 41 | required: true 42 | - type: textarea 43 | id: context 44 | attributes: 45 | label: Anything else? 46 | description: Tell us more if needed. Add links, references, or anything that will give us more context. You can attach images by clicking this area to highlight it and then dragging files in 47 | placeholder: I think this might be caused by ... 48 | value: # 49 | validations: 50 | required: false 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/5--locale.yaml: -------------------------------------------------------------------------------- 1 | name: Keyboard Layout Translation 2 | description: I want to add support for a new keyboard layout and looking for help 3 | title: "[Locale]: " 4 | labels: ["locale"] 5 | body: 6 | - type: checkboxes 7 | attributes: 8 | label: Is there an existing issue for this? 9 | description: Please search to see if an issue already exists for your keyboard layout. 10 | options: 11 | - label: I have searched the existing issues 12 | required: true 13 | - type: input 14 | id: locale 15 | attributes: 16 | label: What layout are you translating? 17 | description: Name of the layout 18 | placeholder: DE 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: description 23 | attributes: 24 | label: Where do you need help? 25 | description: Tell us how far you got and where you need assistance. 26 | placeholder: # 27 | value: # 28 | validations: 29 | required: true 30 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | # Configuration for welcome - https://github.com/behaviorbot/welcome 2 | newIssueWelcomeComment: > 3 | Thanks for opening your first issue here! 🎉
4 | 👉 Be sure to:
5 | 1. 📖 Have a look at the [Wiki](https://spacehuhn.wiki) and [README](https://github.com/SpacehuhnTech/WiFiDuck/blob/master/README.md) for information
6 | 2. 🔍 Search for similar [issues (open and closed)](https://github.com/SpacehuhnTech/esp8266_deauther/issues?q=is%3Aissue+)
7 | 3. ✍️ Provide enough information to understand, recreate and help out with your problem
8 | 4. ℹ️ Let us know if you find a solution
9 | 5. 📕 Close the issue when your problem has been solved 10 | 11 | newPRWelcomeComment: 12 | 13 | firstPRMergeComment: -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 180 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - bug 9 | - translation 10 | - feature request 11 | - locale 12 | # Label to use when marking an issue as stale 13 | staleLabel: stale 14 | # Comment to post when marking an issue as stale. Set to `false` to disable 15 | markComment: > 16 | This issue has been automatically marked as stale because it has not had 17 | recent activity. It will be closed if no further activity occurs. Thank you 18 | for your contributions. 19 | # Comment to post when closing a stale issue. Set to `false` to disable 20 | closeComment: false 21 | only: issues -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Save on Command Plugin 35 | save-commands.json 36 | 37 | # Compiled binaries 38 | *.bin 39 | *.zip 40 | .DS_Store 41 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MalDuino W 2 | 3 |

4 | The code running on the Maltronics MalDuino W 5 |

6 | 7 |

8 | For documentation and support please see here 9 |

10 | 11 | ## Credits 12 | 13 | Other software used for this project: 14 | - [Arduino](https://www.arduino.cc) 15 | - [Neopixel Library](https://github.com/adafruit/Adafruit_NeoPixel) 16 | - [Dotstar Library](https://github.com/adafruit/Adafruit_DotStar) 17 | - [AVR, ESP8266 & SAMD Arduino Core](https://github.com/spacehuhn/hardware/tree/master/wifiduck) 18 | - [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) 19 | - [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) 20 | - [SimpleCLI](https://github.com/spacehuhn/SimpleCLI) 21 | -------------------------------------------------------------------------------- /atmega_duck/Adafruit_DotStar.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file Adafruit_DotStar.h 3 | * 4 | * This file is part of the Adafruit_DotStar library. 5 | * 6 | * Adafruit_DotStar is free software: you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * Adafruit_DotStar is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with DotStar. If not, see . 18 | * 19 | */ 20 | 21 | #ifndef _ADAFRUIT_DOT_STAR_H_ 22 | #define _ADAFRUIT_DOT_STAR_H_ 23 | 24 | #if (ARDUINO >= 100) 25 | #include 26 | #else 27 | #include 28 | #include 29 | #endif 30 | 31 | // Color-order flag for LED pixels (optional extra parameter to constructor): 32 | // Bits 0,1 = R index (0-2), bits 2,3 = G index, bits 4,5 = B index 33 | #define DOTSTAR_RGB (0 | (1 << 2) | (2 << 4)) ///< Transmit as R,G,B 34 | #define DOTSTAR_RBG (0 | (2 << 2) | (1 << 4)) ///< Transmit as R,B,G 35 | #define DOTSTAR_GRB (1 | (0 << 2) | (2 << 4)) ///< Transmit as G,R,B 36 | #define DOTSTAR_GBR (2 | (0 << 2) | (1 << 4)) ///< Transmit as G,B,R 37 | #define DOTSTAR_BRG (1 | (2 << 2) | (0 << 4)) ///< Transmit as B,R,G 38 | #define DOTSTAR_BGR (2 | (1 << 2) | (0 << 4)) ///< Transmit as B,G,R 39 | #define DOTSTAR_MONO 0 ///< Single-color strip WIP DO NOT USE, use RGB for now 40 | 41 | // These two tables are declared outside the Adafruit_DotStar class 42 | // because some boards may require oldschool compilers that don't 43 | // handle the C++11 constexpr keyword. 44 | 45 | /* A PROGMEM (flash mem) table containing 8-bit unsigned sine wave (0-255). 46 | Copy & paste this snippet into a Python REPL to regenerate: 47 | import math 48 | for x in range(256): 49 | print("{:3},".format(int((math.sin(x/128.0*math.pi)+1.0)*127.5+0.5))), 50 | if x&15 == 15: print 51 | */ 52 | static const uint8_t PROGMEM _DotStarSineTable[256] = { 53 | 128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173, 54 | 176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215, 55 | 218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244, 56 | 245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255, 57 | 255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246, 58 | 245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220, 59 | 218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179, 60 | 176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131, 61 | 128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82, 62 | 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, 63 | 37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 64 | 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 65 | 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 66 | 10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 67 | 37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, 68 | 79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124}; 69 | 70 | /* Similar to above, but for an 8-bit gamma-correction table. 71 | Copy & paste this snippet into a Python REPL to regenerate: 72 | import math 73 | gamma=2.6 74 | for x in range(256): 75 | print("{:3},".format(int(math.pow((x)/255.0,gamma)*255.0+0.5))), 76 | if x&15 == 15: print 77 | */ 78 | static const uint8_t PROGMEM _DotStarGammaTable[256] = { 79 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80 | 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 81 | 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 82 | 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 83 | 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 84 | 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 85 | 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, 86 | 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42, 87 | 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 88 | 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75, 89 | 76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 90 | 97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120, 91 | 122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148, 92 | 150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180, 93 | 182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215, 94 | 218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255}; 95 | 96 | /*! 97 | @brief Class that stores state and functions for interacting with 98 | Adafruit DotStars and compatible devices. 99 | */ 100 | class Adafruit_DotStar { 101 | 102 | public: 103 | 104 | Adafruit_DotStar(uint16_t n, uint8_t o=DOTSTAR_BRG); 105 | Adafruit_DotStar(uint16_t n, uint8_t d, uint8_t c, uint8_t o=DOTSTAR_BRG); 106 | ~Adafruit_DotStar(void); 107 | 108 | void begin(void); 109 | void show(void); 110 | void setPixelColor(uint16_t n, uint32_t c); 111 | void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); 112 | void fill(uint32_t c=0, uint16_t first=0, uint16_t count=0); 113 | void setBrightness(uint8_t); 114 | void clear(); 115 | void updateLength(uint16_t n); 116 | void updatePins(void); 117 | void updatePins(uint8_t d, uint8_t c); 118 | /*! 119 | @brief Get a pointer directly to the DotStar data buffer in RAM. 120 | Pixel data is stored in a device-native format (a la the 121 | DOTSTAR_* constants) and is not translated here. Applications 122 | that access this buffer will need to be aware of the specific 123 | data format and handle colors appropriately. 124 | @return Pointer to DotStar buffer (uint8_t* array). 125 | @note This is for high-performance applications where calling 126 | setPixelColor() on every single pixel would be too slow (e.g. 127 | POV or light-painting projects). There is no bounds checking 128 | on the array, creating tremendous potential for mayhem if one 129 | writes past the ends of the buffer. Great power, great 130 | responsibility and all that. 131 | */ 132 | uint8_t *getPixels(void) const { return pixels; }; 133 | uint8_t getBrightness(void) const; 134 | /*! 135 | @brief Return the number of pixels in an Adafruit_DotStar strip object. 136 | @return Pixel count (0 if not set). 137 | */ 138 | uint16_t numPixels(void) const { return numLEDs; }; 139 | uint32_t getPixelColor(uint16_t n) const; 140 | /*! 141 | @brief An 8-bit integer sine wave function, not directly compatible 142 | with standard trigonometric units like radians or degrees. 143 | @param x Input angle, 0-255; 256 would loop back to zero, completing 144 | the circle (equivalent to 360 degrees or 2 pi radians). 145 | One can therefore use an unsigned 8-bit variable and simply 146 | add or subtract, allowing it to overflow/underflow and it 147 | still does the expected contiguous thing. 148 | @return Sine result, 0 to 255, or -128 to +127 if type-converted to 149 | a signed int8_t, but you'll most likely want unsigned as this 150 | output is often used for pixel brightness in animation effects. 151 | */ 152 | static uint8_t sine8(uint8_t x) { 153 | return pgm_read_byte(&_DotStarSineTable[x]); // 0-255 in, 0-255 out 154 | } 155 | /*! 156 | @brief An 8-bit gamma-correction function for basic pixel brightness 157 | adjustment. Makes color transitions appear more perceptially 158 | correct. 159 | @param x Input brightness, 0 (minimum or off/black) to 255 (maximum). 160 | @return Gamma-adjusted brightness, can then be passed to one of the 161 | setPixelColor() functions. This uses a fixed gamma correction 162 | exponent of 2.6, which seems reasonably okay for average 163 | DotStars in average tasks. If you need finer control you'll 164 | need to provide your own gamma-correction function instead. 165 | */ 166 | static uint8_t gamma8(uint8_t x) { 167 | return pgm_read_byte(&_DotStarGammaTable[x]); // 0-255 in, 0-255 out 168 | } 169 | /*! 170 | @brief Convert separate red, green and blue values into a single 171 | "packed" 32-bit RGB color. 172 | @param r Red brightness, 0 to 255. 173 | @param g Green brightness, 0 to 255. 174 | @param b Blue brightness, 0 to 255. 175 | @return 32-bit packed RGB value, which can then be assigned to a 176 | variable for later use or passed to the setPixelColor() 177 | function. Packed RGB format is predictable, regardless of 178 | LED strand color order. 179 | */ 180 | static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) { 181 | return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; 182 | } 183 | static uint32_t ColorHSV(uint16_t hue, uint8_t sat=255, uint8_t val=255); 184 | static uint32_t gamma32(uint32_t x); 185 | 186 | private: 187 | 188 | uint16_t numLEDs; ///< Number of pixels 189 | uint8_t dataPin; ///< If soft SPI, data pin # 190 | uint8_t clockPin; ///< If soft SPI, clock pin # 191 | uint8_t brightness; ///< Global brightness setting 192 | uint8_t *pixels; ///< LED RGB values (3 bytes ea.) 193 | uint8_t rOffset; ///< Index of red in 3-byte pixel 194 | uint8_t gOffset; ///< Index of green byte 195 | uint8_t bOffset; ///< Index of blue byte 196 | #ifdef __AVR__ 197 | uint8_t dataPinMask; ///< If soft SPI, data pin bitmask 198 | uint8_t clockPinMask; ///< If soft SPI, clock pin bitmask 199 | volatile uint8_t *dataPort; ///< If soft SPI, data PORT 200 | volatile uint8_t *clockPort; ///< If soft SPI, clock PORT 201 | #endif 202 | void hw_spi_init(void); ///< Start hardware SPI 203 | void hw_spi_end(void); ///< Stop hardware SPI 204 | void sw_spi_init(void); ///< Start bitbang SPI 205 | void sw_spi_out(uint8_t n); ///< Bitbang SPI write 206 | void sw_spi_end(void); ///< Stop bitbang SPI 207 | }; 208 | 209 | #endif // _ADAFRUIT_DOT_STAR_H_ 210 | -------------------------------------------------------------------------------- /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 | led::begin(); 20 | serial_bridge::begin(); 21 | keyboard::begin(); 22 | com::begin(); 23 | 24 | debugs("Started! "); 25 | debugln(VERSION); 26 | } 27 | 28 | // ===== LOOOP ===== // 29 | void loop() { 30 | com::update(); 31 | if (com::hasData()) { 32 | const buffer_t& buffer = com::getBuffer(); 33 | 34 | debugs("Interpreting: "); 35 | 36 | 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 | } -------------------------------------------------------------------------------- /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.1.0" 9 | 10 | /* ===== Serial Bridge ===== */ 11 | // #define BRIDGE_ENABLE 12 | // #define BRIDGE_PORT Serial1 13 | // #define BRIDGE_SWITCH 6 14 | // #define BRIDGE_RST 4 15 | // #define BRIDGE_0 5 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 115200 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) */ -------------------------------------------------------------------------------- /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_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_ie.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_ie[] 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_MOD_RALT|KEY_MOD_LSHIFT),KEY_APOSTROPHE,// ` 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_ie[] 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_ie[] PROGMEM = { 211 | 0xC2, 0xB4, 0x00, 0x00, KEY_MOD_RALT, KEY_APOSTROPHE, KEY_NONE, KEY_SPACE, // ´ 212 | 0xC3, 0x80, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_MOD_LSHIFT, KEY_A, // À 213 | 0xC3, 0x88, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_MOD_LSHIFT, KEY_E, // È 214 | 0xC3, 0x8C, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_MOD_LSHIFT, KEY_I, // Ì 215 | 0xC3, 0x92, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_MOD_LSHIFT, KEY_O, // Ò 216 | 0xC3, 0x99, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_MOD_LSHIFT, KEY_U, // Ù 217 | 0xC3, 0x9D, 0x00, 0x00, KEY_MOD_RALT, KEY_APOSTROPHE, KEY_MOD_LSHIFT, KEY_Y, // Ý 218 | 0xC3, 0xA0, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_NONE, KEY_A, // à 219 | 0xC3, 0xA8, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_NONE, KEY_E, // è 220 | 0xC3, 0xAC, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_NONE, KEY_I, // ì 221 | 0xC3, 0xB2, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_NONE, KEY_O, // ò 222 | 0xC3, 0xB9, 0x00, 0x00, KEY_NONE, KEY_GRAVE, KEY_NONE, KEY_U, // ù 223 | 0xC3, 0xBD, 0x00, 0x00, KEY_MOD_RALT, KEY_APOSTROPHE, KEY_NONE, KEY_Y, // ý 224 | }; 225 | 226 | static hid_locale_t locale_ie { 227 | (uint8_t*)ascii_ie, 128, 228 | (uint8_t*)utf8_ie, sizeof(utf8_ie) / 6, 229 | (uint8_t*)combinations_ie, sizeof(combinations_ie) / 8, 230 | }; -------------------------------------------------------------------------------- /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_lt.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_lt[] 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_RALT|KEY_MOD_LSHIFT),KEY_1, // ! 51 | KEY_MOD_LSHIFT, KEY_APOSTROPHE, // " 52 | (KEY_MOD_RALT|KEY_MOD_LSHIFT),KEY_3, // # 53 | 54 | // 36, 0x24 55 | (KEY_MOD_RALT|KEY_MOD_LSHIFT),KEY_4, // $ 56 | (KEY_MOD_RALT|KEY_MOD_LSHIFT),KEY_5, // % 57 | (KEY_MOD_RALT|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_RALT|KEY_MOD_LSHIFT),KEY_8, // * 64 | (KEY_MOD_RALT|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_MOD_RALT, KEY_1, // 1 75 | KEY_MOD_RALT, KEY_2, // 2 76 | KEY_MOD_RALT, KEY_3, // 3 77 | 78 | // 52, 0x34 79 | KEY_MOD_RALT, KEY_4, // 4 80 | KEY_MOD_RALT, KEY_5, // 5 81 | KEY_MOD_RALT, KEY_6, // 6 82 | KEY_MOD_RALT, KEY_7, // 7 83 | 84 | // 56, 0x38 85 | KEY_MOD_RALT, 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_MOD_RALT, KEY_EQUAL, // = 93 | KEY_MOD_LSHIFT, KEY_DOT, // > 94 | KEY_MOD_LSHIFT, KEY_SLASH, // ? 95 | 96 | // 64, 0x40 97 | (KEY_MOD_RALT|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_RALT|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_lt[] PROGMEM = { 194 | 0xC4, 0x84, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_1, // Ą 195 | 0xC4, 0x85, 0x00, 0x00, KEY_NONE, KEY_1, // ą 196 | 0xC4, 0x8C, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_2, // Č 197 | 0xC4, 0x8D, 0x00, 0x00, KEY_NONE, KEY_2, // č 198 | 0xC4, 0x96, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_4, // Ė 199 | 0xC4, 0x97, 0x00, 0x00, KEY_NONE, KEY_4, // ė 200 | 0xC4, 0x98, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_3, // Ę 201 | 0xC4, 0x99, 0x00, 0x00, KEY_NONE, KEY_3, // ę 202 | 0xC4, 0xAE, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_5, // Į 203 | 0xC4, 0xAF, 0x00, 0x00, KEY_NONE, KEY_5, // į 204 | 0xC5, 0xA0, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_6, // Š 205 | 0xC5, 0xA1, 0x00, 0x00, KEY_NONE, KEY_6, // š 206 | 0xC5, 0xAA, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_8, // Ū 207 | 0xC5, 0xAB, 0x00, 0x00, KEY_NONE, KEY_8, // ū 208 | 0xC5, 0xB2, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_7, // Ų 209 | 0xC5, 0xB3, 0x00, 0x00, KEY_NONE, KEY_7, // ų 210 | 0xC5, 0xBD, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_EQUAL, // Ž 211 | 0xC5, 0xBE, 0x00, 0x00, KEY_NONE, KEY_EQUAL, // ž 212 | 0xE2, 0x82, 0xAC, 0x00, KEY_MOD_RALT, KEY_E, // € 213 | }; 214 | 215 | const uint8_t combinations_lt[] PROGMEM = { 216 | }; 217 | 218 | static hid_locale_t locale_lt { 219 | (uint8_t*)ascii_lt, 128, 220 | (uint8_t*)utf8_lt, sizeof(utf8_lt) / 6, 221 | (uint8_t*)combinations_lt, sizeof(combinations_lt) / 8, 222 | }; -------------------------------------------------------------------------------- /atmega_duck/locale_pl.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_pl[] 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_NONE, KEY_SPACE, // ~ 190 | KEY_NONE, KEY_DELETE // DEL 191 | }; 192 | 193 | const uint8_t utf8_pl[] PROGMEM = { 194 | 0xC3, 0x93, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_O, // Ó 195 | 0xC3, 0xB3, 0x00, 0x00, KEY_MOD_RALT, KEY_O, // ó 196 | 0xC4, 0x84, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_A, // Ą 197 | 0xC4, 0x85, 0x00, 0x00, KEY_MOD_RALT, KEY_A, // ą 198 | 0xC4, 0x86, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_C, // Ć 199 | 0xC4, 0x87, 0x00, 0x00, KEY_MOD_RALT, KEY_C, // ć 200 | 0xC4, 0x98, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_E, // Ę 201 | 0xC4, 0x99, 0x00, 0x00, KEY_MOD_RALT, KEY_E, // ę 202 | 0xC5, 0x81, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_L, // Ł 203 | 0xC5, 0x82, 0x00, 0x00, KEY_MOD_RALT, KEY_L, // ł 204 | 0xC5, 0x83, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_N, // Ń 205 | 0xC5, 0x84, 0x00, 0x00, KEY_MOD_RALT, KEY_N, // ń 206 | 0xC5, 0x9A, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_S, // Ś 207 | 0xC5, 0x9B, 0x00, 0x00, KEY_MOD_RALT, KEY_S, // ś 208 | 0xC5, 0xB9, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_X, // Ź 209 | 0xC5, 0xBA, 0x00, 0x00, KEY_MOD_RALT, KEY_X, // ź 210 | 0xC5, 0xBB, 0x00, 0x00, (KEY_MOD_RALT|KEY_MOD_LSHIFT), KEY_Z, // Ż 211 | 0xC5, 0xBC, 0x00, 0x00, KEY_MOD_RALT, KEY_Z, // ż 212 | 0xE2, 0x82, 0xAC, 0x00, KEY_MOD_RALT, KEY_U, // € 213 | }; 214 | 215 | const uint8_t combinations_pl[] PROGMEM = { 216 | 0x7E, 0x00, 0x00, 0x00, KEY_MOD_LSHIFT, KEY_GRAVE, KEY_NONE, KEY_SPACE, // ~ 217 | }; 218 | 219 | static hid_locale_t locale_pl { 220 | (uint8_t*)ascii_pl, 128, 221 | (uint8_t*)utf8_pl, sizeof(utf8_pl) / 6, 222 | (uint8_t*)combinations_pl, sizeof(combinations_pl) / 8, 223 | }; -------------------------------------------------------------------------------- /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_ee.h" 30 | #include "locale_esmx.h" 31 | #include "locale_fi.h" 32 | #include "locale_gr.h" 33 | #include "locale_hu.h" 34 | #include "locale_ie.h" 35 | #include "locale_in.h" 36 | #include "locale_is.h" 37 | #include "locale_lt.h" 38 | #include "locale_lv.h" 39 | #include "locale_nl.h" 40 | #include "locale_no.h" 41 | #include "locale_pl.h" 42 | #include "locale_ptbr.h" 43 | #include "locale_ro.h" 44 | #include "locale_se.h" 45 | #include "locale_tr.h" 46 | #include "locale_ua.h" 47 | -------------------------------------------------------------------------------- /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 = 115200; 29 | 30 | int rts = -1; 31 | int dtr = -1; 32 | 33 | void begin() { 34 | BRIDGE_PORT.begin(baud); 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(baud); 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 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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.1.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 115200 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 "malduinow" 39 | #define WIFI_PASSWORD "malduinow" 40 | #define WIFI_CHANNEL "7" 41 | 42 | #define HOSTNAME "malduinow" 43 | #define URL "malduinow.tools" 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) */ -------------------------------------------------------------------------------- /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/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 | } -------------------------------------------------------------------------------- /reset_sketch/reset_sketch.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | Upload this sketch to your ESP8266 to erase 6 | - all data in the EEPROM (Application settings) 7 | - WiFi credentials (SSID, password) 8 | 9 | Also overwrites the previous program with this one 10 | */ 11 | 12 | void setup() { 13 | Serial.begin(115200); 14 | 15 | Serial.println(); 16 | Serial.println("STARTING..."); 17 | 18 | EEPROM.begin(4096); 19 | Serial.println("EEPROM initialized"); 20 | 21 | for (int i = 0; i < 4096; ++i){ 22 | EEPROM.write(i,0x00); 23 | } 24 | 25 | EEPROM.commit(); 26 | 27 | Serial.println("EEPROM cleaned"); 28 | 29 | ESP.eraseConfig(); 30 | 31 | Serial.println("WiFi credentials erased"); 32 | 33 | Serial.println("DONE!"); 34 | } 35 | 36 | void loop() { 37 | 38 | } -------------------------------------------------------------------------------- /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 | Malduino W | About 15 | 16 | 17 | 18 | 19 | 20 | 26 |
27 |
28 |
29 |

Disclaimer

30 |

31 | By using this device you are agreeing to our terms of service available here: http://terms.maltronics.com 32 | If you intend to use our products on computer systems you do not have permission to interfere with then you are crossing an ethical line, this may also be a legal line. Please consult your country's laws. A tool can be used for good or for harm - only do good. 33 |

34 |
35 |
36 |

Credits

37 |

38 | Other software used for this project:
39 | - Arduino
40 | - Neopixel Library
41 | - Dotstar Library
42 | - AVR, ESP8266 & SAMD Arduino Core
43 | - ESPAsyncTCP
44 | - ESPAsyncWebServer
45 | - SimpleCLI
46 | - FlashStorage Library 47 |

48 |
49 |
50 |

Thanks

51 |

52 | Thanks to everyone that helped making this project reality, especially:
53 | - deantonious for helping to design and improve the user experience
54 | - Maltronics for creating, selling and financially supporting us with custom made hardware
55 | - and YOU for using it! 56 |

57 |
58 |
59 |

License

60 |

61 | In regards to the hardware design and implementation: 62 | 63 | Copyright 2020 Maltronics Limited © All rights reserved 64 | 65 | In regards to the bootloader: 66 | 67 | The MIT License (MIT) 68 | 69 | Copyright (c) Microsoft 70 | 71 | Permission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 72 | furnished to do so, subject to the following conditions: 73 | 74 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 75 | 76 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 77 | 78 | Third Party Programs: The software may include third party programs that Microsoft, not the third party, licenses to you under this agreement. Notices, if any, for the third party programs are included for your information only. 79 | 80 | ---------------------------------------------------------------------------- 81 | SAM Software Package License 82 | ---------------------------------------------------------------------------- 83 | Copyright (c) 2011-2014, Atmel Corporation 84 | 85 | All rights reserved. 86 | 87 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following condition is met: 88 | 89 | Redistributions of source code must retain the above copyright notice, this list of conditions and the disclaimer below. 90 | 91 | Atmel's name may not be used to endorse or promote products derived from this software without specific prior written permission. 92 | 93 | DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 94 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 95 | ---------------------------------------------------------------------------- 96 | 97 | In regards to the firmware: 98 | 99 | MIT License 100 | Copyright (c) 2020 Spacehuhn Technologies 101 | 102 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 103 | 104 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 105 | 106 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 107 |

108 |
109 |
110 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /web/error404.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Malduino W | Error 14 | 15 | 16 | 17 | 18 | 19 | 25 |
26 |
27 |
28 | 29 |

404

30 |

31 | Page not found :( 32 |

33 | Back to Homepage 34 |
35 |
36 | 42 | 43 | -------------------------------------------------------------------------------- /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 | Malduino W | Settings 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 |
27 |
28 |
29 |

WiFi

30 |

Restart the device to apply new settings.

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

Update

57 | 58 |

59 | Go to malwreleases.maltronics.com 60 | to check for updates.
Select a .bin file and press upload to update the device.
61 |

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

Terminal

30 |
31 | 32 | 33 | 34 | 35 |
36 |
37 |
38 |
39 |
40 |
41 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------