├── .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 |
21 |
25 |
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 |
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 |
20 |
24 |
25 |
26 |
27 |
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 += "edit \n";
125 | tableHTML += "run \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 |
21 |
25 |
26 |
27 |
28 |
29 | WiFi
30 | Restart the device to apply new settings.
31 |
32 |
33 | SSID:
34 |
35 | edit
36 |
37 |
38 | Password:
39 |
40 | edit
41 |
42 |
43 | Channel:
44 |
45 | edit
46 |
47 |
48 | Autorun Script:
49 |
50 | disable
51 |
52 |
53 | Reset settings
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 |
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 |
21 |
25 |
26 |
27 |
28 |
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 |
--------------------------------------------------------------------------------