├── .clang-format ├── .gitattributes ├── .gitignore ├── .gitmodules ├── .husky ├── .gitignore ├── common.sh └── pre-commit ├── .prettierrc ├── .vscode ├── c_cpp_properties.json ├── launch.json ├── settings.json └── tasks.json ├── .yarn └── sdks │ ├── integrations.yml │ ├── prettier │ ├── index.js │ └── package.json │ └── typescript │ ├── bin │ ├── tsc │ └── tsserver │ ├── lib │ ├── tsc.js │ ├── tsserver.js │ ├── tsserverlibrary.js │ └── typescript.js │ └── package.json ├── .yarnrc.yml ├── LICENSE ├── README.md ├── Toolchain-Adafruit-nRF52480.cmake ├── WiredSketch ├── CoreCapability.hpp ├── Left-nRF52840 │ ├── CoreCapability.hpp │ └── Left-nRF52840.ino ├── Left-rp2040 │ └── Left-rp2040.ino ├── Right-nRF52840 │ ├── CoreCapability.hpp │ └── Right-nRF52840.ino └── Right-rp2040 │ └── Right-rp2040.ino ├── cmake-simple ├── CMakeLists.txt ├── Left-nRF52840 │ └── Left-nRF52840.ino └── Left-rp2040 │ └── Left-rp2040.ino ├── docs ├── 1mmMilling.jpg ├── AluminumIsHard.jpg ├── FinalCanvasXDA.jpg ├── FinalCasesSanded.jpg ├── FinalGraniteDSA.jpg ├── FinishedLeftHand.jpg ├── FirstAluminumPrototype.jpg ├── FirstCasePrototype.jpg ├── FreiKey Architecture.excalidraw ├── KarbonCanvasXDA.jpg ├── Key Position Helper.xlsx ├── LapType.jpg ├── MillingAluminum.jpg ├── PrototypeWithKeys.jpg ├── RightHandPCB.jpg ├── SandingAndEdges.jpg ├── battery-log.txt ├── info.txt ├── notes-teensy.txt └── notes-threepiece.txt ├── fix_compile_commands.js ├── package.json ├── src ├── CMakeLists.txt ├── Makefile ├── apple2.mk ├── ardfs.cpp ├── ardfs.h ├── ardfs_test.cpp ├── bigscreen.cpp ├── dbgcfg.cpp ├── include │ ├── action.h │ ├── adafruit_nrf52 │ │ └── mpu.h │ ├── bigscreen │ │ ├── boardio.h │ │ ├── hwconfig.h │ │ └── keymap.h │ ├── bitstuff.h │ ├── dbgcfg.h │ ├── debounce.h │ ├── enumhelpers.h │ ├── enumtypes.h │ ├── fs │ │ ├── dir.h │ │ └── path.h │ ├── generalstate.h │ ├── kbreporter.h │ ├── keyhelpers.h │ ├── keymatrix.h │ ├── keystate.h │ ├── laptype │ │ ├── boardio.h │ │ ├── hwconfig.h │ │ └── keymap.h │ ├── localscan │ │ └── scanner.h │ ├── mock.h │ ├── mock │ │ ├── boardio.h │ │ ├── hwconfig.h │ │ ├── keymap.h │ │ ├── mpu.h │ │ ├── mpusys.h │ │ └── scanner.h │ ├── moduleLoaderTest.cpp │ ├── modulekeyboard.h │ ├── newActionResult.h │ ├── newKeyState.h │ ├── newKeyboardInput.h │ ├── newModule.h │ ├── newSystemState.h │ ├── remotescan │ │ └── scanner.h │ ├── scanning.h │ ├── sysstuff.h │ ├── teensy │ │ ├── mpu.h │ │ └── mpusys.h │ ├── threepiece │ │ ├── boardio.h │ │ ├── hwconfig.h │ │ └── keymap.h │ └── usbenums.h ├── kbreporter.cpp ├── laptype.cpp ├── localscan.cpp ├── mainloop.cpp ├── make-3piece.mak ├── make-41.mak ├── make-bigscreen.mak ├── make-laptype.mak ├── make-mock.mak ├── make-sd.mak ├── make-test.mak ├── make_secrets.cpp ├── mock.cpp ├── modulekeyboard.cpp ├── modules │ ├── calculator │ │ ├── .gitignore │ │ ├── CMakeLists.txt │ │ ├── CalcContext.cpp │ │ ├── CalcExpr.cpp │ │ ├── CalcGrammar.yy │ │ ├── CalcHandler.cpp │ │ ├── CalcLexer.cpp │ │ ├── Calculator.cpp │ │ ├── Makefile │ │ ├── headers │ │ │ ├── CalcContext.h │ │ │ ├── CalcExpr.h │ │ │ └── CalcLexer.h │ │ ├── include.mk │ │ └── include │ │ │ └── Calculator.h │ ├── display │ │ ├── CMakeLists.txt │ │ ├── display.cpp │ │ ├── display.h │ │ └── include.mk │ ├── editline │ │ ├── CMakeLists.txt │ │ ├── Makefile │ │ ├── editline.cpp │ │ ├── editline.h │ │ └── include.mk │ ├── images │ │ ├── CMakeLists.txt │ │ ├── Makefile │ │ ├── bitmaps │ │ │ ├── amy-135-200.bin │ │ │ ├── amy.cpp │ │ │ ├── amy.h │ │ │ ├── apple-ii-240-86.raw │ │ │ ├── apple-ii.cpp │ │ │ ├── apple-ii.h │ │ │ ├── batman-102-58.bin │ │ │ ├── batman.cpp │ │ │ ├── batman.h │ │ │ ├── calc.bin │ │ │ ├── calc.cpp │ │ │ ├── calc.h │ │ │ ├── haha.bin │ │ │ ├── haha.cpp │ │ │ ├── haha.h │ │ │ ├── hug.bin │ │ │ ├── hug.cpp │ │ │ ├── hug.h │ │ │ ├── keyb.bin │ │ │ ├── keyb.cpp │ │ │ ├── keyb.h │ │ │ ├── like.bin │ │ │ ├── like.cpp │ │ │ ├── like.h │ │ │ ├── linux-79-96.bin │ │ │ ├── linux.cpp │ │ │ ├── linux.h │ │ │ ├── love.bin │ │ │ ├── love.cpp │ │ │ ├── love.h │ │ │ ├── mac-78-96.bin │ │ │ ├── mac.cpp │ │ │ ├── mac.h │ │ │ ├── mad.bin │ │ │ ├── mad.cpp │ │ │ ├── mad.h │ │ │ ├── sad.bin │ │ │ ├── sad.cpp │ │ │ ├── sad.h │ │ │ ├── win-100-100.bin │ │ │ ├── win.cpp │ │ │ ├── win.h │ │ │ ├── wow.bin │ │ │ ├── wow.cpp │ │ │ └── wow.h │ │ ├── image.cpp │ │ ├── image.h │ │ ├── img_cmdln.cpp │ │ ├── imgdec_pal.cpp │ │ ├── imgdec_prle.cpp │ │ ├── imgdec_rle.cpp │ │ ├── imgenc_pal.cpp │ │ ├── imgenc_prle.cpp │ │ ├── imgenc_rle.cpp │ │ ├── imgencoder.cpp │ │ ├── include.mk │ │ └── include │ │ │ ├── bitmap.h │ │ │ └── imgencoder.h │ ├── menu │ │ ├── CMakeLists.txt │ │ ├── include.mk │ │ ├── menu.cpp │ │ └── menu.h │ ├── snake │ │ ├── disp_stuff.cpp │ │ ├── makesnake.mk │ │ ├── snake.cpp │ │ ├── snake.h │ │ ├── snakeboard.cpp │ │ └── snakeboard.h │ ├── standalone.mk │ └── tetris │ │ ├── CMakeLists.txt │ │ ├── disp_stuff.cpp │ │ ├── maketetris.mk │ │ ├── tetris.cpp │ │ ├── tetris.h │ │ ├── tetrisboard.cpp │ │ └── tetrisboard.h ├── newLoop.cpp ├── oldstuff │ ├── battery.h │ ├── client-comm.cpp │ ├── client-comm.h │ ├── comm.h │ ├── dongle.cpp │ ├── dongle.h │ ├── hardware.cpp │ ├── hardware.h │ ├── host-comm.cpp │ ├── host-comm.h │ ├── kbclient.cpp │ ├── kbclient.h │ ├── led_states.cpp │ ├── led_states.h │ ├── leds.h │ ├── sleepstate.cpp │ ├── sleepstate.h │ ├── status_dump.cpp │ ├── status_dump.h │ ├── sync.cpp │ ├── sync.h │ ├── timesync.h │ └── usb-host.cpp ├── remotescan.cpp ├── scanning.cpp ├── test_41.cpp ├── test_gfx.cpp ├── test_sd.cpp ├── threepiece.cpp └── tools │ ├── adafruit-make-config.json │ ├── af_nrf52.mk │ ├── af_nrf52.win │ ├── gen-makefiles.mjs │ ├── libs │ ├── busio │ │ └── CMakeLists.txt │ ├── eeprom │ │ └── CMakeLists.txt │ ├── gfx │ │ └── CMakeLists.txt │ ├── spi │ │ └── CMakeLists.txt │ ├── st77xx │ │ └── CMakeLists.txt │ └── wire │ │ └── CMakeLists.txt │ ├── mock.mk │ ├── teensy-make-config.json │ ├── teensy.lin │ ├── teensy.mac │ ├── teensy.win │ ├── teensy │ └── teensy40 │ │ ├── CMakeLists.txt │ │ └── arduino_config.cmake │ └── toolchains │ └── Teensy40.cmake └── yarn.lock /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: -1 3 | AlignAfterOpenBracket: Align 4 | AlignEscapedNewlinesLeft: true 5 | AlignTrailingComments: false 6 | AllowAllParametersOfDeclarationOnNextLine: true 7 | AllowShortBlocksOnASingleLine: false 8 | AllowShortFunctionsOnASingleLine: Empty 9 | AllowShortIfStatementsOnASingleLine: false 10 | AllowShortLoopsOnASingleLine: false 11 | AlwaysBreakBeforeMultilineStrings: true 12 | AlwaysBreakTemplateDeclarations: true 13 | BinPackArguments: false 14 | BinPackParameters: false 15 | BreakBeforeBinaryOperators: false 16 | BreakBeforeBraces: Attach 17 | BreakBeforeTernaryOperators: true 18 | BreakConstructorInitializersBeforeComma: false 19 | ColumnLimit: 80 20 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 21 | ConstructorInitializerIndentWidth: 2 22 | ContinuationIndentWidth: 2 23 | Cpp11BracedListStyle: true 24 | DerivePointerAlignment: false 25 | IndentWrappedFunctionNames: false 26 | ExperimentalAutoDetectBinPacking: true 27 | IndentCaseLabels: true 28 | IndentFunctionDeclarationAfterType: false 29 | IndentWidth: 2 30 | JavaScriptQuotes: Single 31 | MaxEmptyLinesToKeep: 1 32 | NamespaceIndentation: None 33 | ObjCSpaceBeforeProtocolList: false 34 | PenaltyBreakBeforeFirstCallParameter: 10 35 | PenaltyBreakComment: 60 36 | PenaltyBreakFirstLessLess: 20 37 | PenaltyBreakString: 1000 38 | PenaltyExcessCharacter: 1000000 39 | PenaltyReturnTypeOnItsOwnLine: 200 40 | PointerAlignment: Left 41 | SpaceAfterCStyleCast: false 42 | SpaceAfterTemplateKeyword: true 43 | SpaceAfterControlStatementKeyword: true 44 | SpaceBeforeAssignmentOperators: true 45 | SpaceBeforeParens: ControlStatements 46 | SpaceInEmptyParentheses: false 47 | SpacesBeforeTrailingComments: 1 48 | SpacesInAngles: false 49 | SpacesInCStyleCastParentheses: false 50 | SpacesInParentheses: false 51 | SpacesInContainerLiterals: true 52 | Standard: Auto 53 | TabWidth: 2 54 | UseTab: Never 55 | ... 56 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | .yarn/**/* -diff 2 | yarn.lock -diff 3 | .pnp.* -diff 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | *.idb 10 | *.pdb 11 | 12 | # Precompiled Headers 13 | *.gch 14 | *.pch 15 | 16 | # Compiled Dynamic libraries 17 | *.so 18 | *.dylib 19 | *.dll 20 | 21 | # Fortran module files 22 | *.mod 23 | *.smod 24 | 25 | # Compiled Static libraries 26 | *.lai 27 | *.la 28 | *.a 29 | *.lib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | 36 | 37 | # Custom 38 | src/out-* 39 | src/Makefile 40 | src/out* 41 | src/.vs 42 | src/*.bin 43 | src/include/secrets.h 44 | 45 | # Cmake build directories 46 | **/build 47 | local_tools 48 | 49 | # Yarn stuff 50 | .yarn/* 51 | .pnp.* 52 | !.yarn/patches 53 | !.yarn/plugins 54 | !.yarn/releases 55 | !.yarn/sdks 56 | !.yarn/versions 57 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/libs/GFX"] 2 | path = src/libs/GFX 3 | url = https://github.com/adafruit/Adafruit-GFX-Library.git 4 | [submodule "src/libs/ArduinoCore"] 5 | path = src/libs/ArduinoCore 6 | url = https://github.com/arduino/ArduinoCore-avr.git 7 | [submodule "src/libs/BusIO"] 8 | path = src/libs/BusIO 9 | url = https://github.com/adafruit/Adafruit_BusIO.git 10 | [submodule "src/libs/ST77XX"] 11 | path = src/libs/ST77XX 12 | url = https://github.com/adafruit/Adafruit-ST7735-Library.git 13 | [submodule "src/libs/apple2"] 14 | path = src/libs/apple2 15 | url = https://github.com/kevinfrei/linapple.git 16 | [submodule "src/libs/T4_PXP"] 17 | path = src/libs/T4_PXP 18 | url = https://github.com/vjmuzik/T4_PXP.git 19 | [submodule "src/libs/GFX_Buffer"] 20 | path = src/libs/GFX_Buffer 21 | url = https://github.com/vjmuzik/Adafruit_GFX_Buffer.git 22 | [submodule "src/libs/AsyncDMA"] 23 | path = src/libs/AsyncDMA 24 | url = https://github.com/kevinfrei/AsyncDMA_ST77XX 25 | [submodule "src/libs/8875"] 26 | path = src/libs/8875 27 | url = https://github.com/adafruit/Adafruit_RA8875.git 28 | [submodule "SdAda"] 29 | path = src/libs/SdFat 30 | url = https://github.com/adafruit/SdFat.git 31 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/common.sh: -------------------------------------------------------------------------------- 1 | command_exists () { 2 | command -v "$1" >/dev/null 2>&1 3 | } 4 | 5 | # Workaround for Windows 10, Git Bash and Yarn 6 | if command_exists winpty && test -t 1; then 7 | exec < /dev/tty 8 | fi 9 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | . "$(dirname "$0")/common.sh" 4 | 5 | yarn format -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "proseWrap": "always", 6 | "tabWidth": 2, 7 | "useTabs": false, 8 | "overrides": [ 9 | { 10 | "files": ".prettierrc", 11 | "options": { "parser": "json" } 12 | }, 13 | { 14 | "files": "*.json", 15 | "options": { "parser": "json" } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "gccRoot": "/usr/local/opt/gcc-arm-none-eabi", 4 | "gccVer": "8.3.2", 5 | "srcRoot": "${workspaceFolder}", 6 | "variableReference": "https://code.visualstudio.com/docs/editor/variables-reference", 7 | "fileReference": "https://github.com/Microsoft/vscode-cpptools/blob/master/Documentation/LanguageServer/c_cpp_properties.json.md" 8 | }, 9 | "configurations": [ 10 | { 11 | "name": "Laptype-win", 12 | "intelliSenseMode": "windows-gcc-arm", 13 | "compileCommands": "${workspaceFolder}/src/out/laptype/compile_commands.json", 14 | "browse": { 15 | "path": ["${workspaceFolder}/src/out/laptype/"], 16 | "databaseFilename": "" 17 | } 18 | }, 19 | { 20 | "name": "Laptype-mac", 21 | "intelliSenseMode": "macos-gcc-arm", 22 | "compileCommands": "${workspaceFolder}/src/out/laptype/compile_commands.json", 23 | "browse": { 24 | "path": ["${workspaceFolder}/src/out/laptype/"], 25 | "databaseFilename": "" 26 | }, 27 | "configurationProvider": "ms-vscode.makefile-tools" 28 | }, 29 | { 30 | "name": "3piece-win", 31 | "intelliSenseMode": "windows-gcc-arm", 32 | "compileCommands": "${workspaceFolder}/src/out/threepiece/compile_commands.json", 33 | "browse": { 34 | "path": ["${workspaceFolder}/src/out/threepiece/"], 35 | "databaseFilename": "" 36 | } 37 | }, 38 | { 39 | "name": "3piece-mac", 40 | "intelliSenseMode": "macos-gcc-arm", 41 | "compileCommands": "${workspaceFolder}/src/out/threepiece/compile_commands.json", 42 | "browse": { 43 | "path": ["${workspaceFolder}/src/out/threepiece/"], 44 | "databaseFilename": "" 45 | }, 46 | "configurationProvider": "ms-vscode.makefile-tools" 47 | }, 48 | { 49 | "name": "Mock-mac", 50 | "intelliSenseMode": "macos-clang-arm64", 51 | "compileCommands": "${workspaceFolder}/src/out/mock-host/compile_commands.json", 52 | "browse": { 53 | "path": ["${workspaceFolder}/src/out/mock-host/"], 54 | "databaseFilename": "" 55 | } 56 | }, 57 | { 58 | "name": "Mock-win", 59 | "intelliSenseMode": "clang-x64", 60 | "compileCommands": "${workspaceFolder}/src/out/mock-host/compile_commands.json" 61 | }, 62 | { 63 | "name": "ImageComp (Win)", 64 | "includePath": ["${workspaceFolder}/src/include"], 65 | "defines": ["COMPRESSOR"], 66 | "windowsSdkVersion": "10.0.19041.0", 67 | "compilerPath": "C:\\Program Files\\LLVM\\bin\\clang.exe", 68 | "cStandard": "c17", 69 | "cppStandard": "c++17", 70 | "intelliSenseMode": "windows-clang-x64" 71 | }, 72 | { 73 | "name": "ImageComp (Mac)", 74 | "includePath": ["${workspaceFolder}/src/include"], 75 | "defines": [], 76 | "cStandard": "c17", 77 | "cppStandard": "c++17", 78 | "intelliSenseMode": "macos-clang-arm64" 79 | }, 80 | { 81 | "name": "Calc (Win)", 82 | "includePath": ["${workspaceFolder}/src/include"], 83 | "defines": ["CALC_TEST"], 84 | "windowsSdkVersion": "10.0.19041.0", 85 | "compilerPath": "C:\\Program Files\\LLVM\\bin\\clang.exe", 86 | "cStandard": "c11", 87 | "cppStandard": "c++20", 88 | "intelliSenseMode": "windows-clang-x64" 89 | }, 90 | { 91 | "name": "Calc (Mac)", 92 | "includePath": ["${workspaceFolder}/src/include"], 93 | "defines": [], 94 | "cStandard": "c11", 95 | "cppStandard": "c++20", 96 | "intelliSenseMode": "macos-clang-arm64" 97 | } 98 | ], 99 | "version": 4 100 | } 101 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Debug Tetris (win)", 9 | "type": "cppvsdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/src/out/tetris/tetris.exe", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${fileDirname}", 15 | "environment": [], 16 | "console": "externalTerminal" 17 | }, 18 | { 19 | "type": "lldb", 20 | "request": "launch", 21 | "name": "Debug ImageComp", 22 | "program": "${workspaceFolder}/src/out/imagecomp", 23 | "args": ["-w:174", "-h:174", "bitmaps/love.bin"], 24 | "cwd": "${workspaceFolder}" 25 | }, 26 | { 27 | "type": "lldb", 28 | "request": "launch", 29 | "name": "Debug Calculator", 30 | "program": "${workspaceFolder}/src/out/calculator/calculator", 31 | "cwd": "${workspaceFolder}" 32 | }, 33 | { 34 | "type": "node", 35 | "program": "node", 36 | "name": "Fix Compile Commands", 37 | "request": "launch", 38 | "runtimeArgs": [ 39 | "--inspect-brk", 40 | "${workspaceFolder}/fix_compile_commands.js" 41 | ], 42 | "console": "integratedTerminal", 43 | "internalConsoleOptions": "openOnSessionStart" 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "files.associations": { 4 | "*.cps": "javascript", 5 | "*.h": "cpp", 6 | "*.mak": "makefile", 7 | "__functional_03": "cpp", 8 | "functional": "cpp", 9 | "__bit_reference": "cpp", 10 | "__split_buffer": "cpp", 11 | "deque": "cpp", 12 | "initializer_list": "cpp", 13 | "iterator": "cpp", 14 | "list": "cpp", 15 | "string": "cpp", 16 | "string_view": "cpp", 17 | "vector": "cpp", 18 | "chrono": "cpp", 19 | "locale": "cpp", 20 | "system_error": "cpp", 21 | "queue": "cpp", 22 | "__locale": "cpp", 23 | "__config": "cpp", 24 | "__nullptr": "cpp", 25 | "cstddef": "cpp", 26 | "exception": "cpp", 27 | "stdexcept": "cpp", 28 | "type_traits": "cpp", 29 | "typeinfo": "cpp", 30 | "algorithm": "cpp", 31 | "ios": "cpp", 32 | "__functional_base": "cpp", 33 | "atomic": "cpp", 34 | "bitset": "cpp", 35 | "limits": "cpp", 36 | "memory": "cpp", 37 | "ratio": "cpp", 38 | "tuple": "cpp", 39 | "utility": "cpp", 40 | "map": "cpp", 41 | "__tree": "cpp", 42 | "__debug": "cpp", 43 | "__node_handle": "cpp", 44 | "__tuple": "cpp", 45 | "array": "cpp", 46 | "cstdint": "cpp", 47 | "cstdio": "cpp", 48 | "cstdlib": "cpp", 49 | "cstring": "cpp", 50 | "iosfwd": "cpp", 51 | "optional": "cpp", 52 | "set": "cpp", 53 | "*.tcc": "cpp", 54 | "cctype": "cpp", 55 | "clocale": "cpp", 56 | "cmath": "cpp", 57 | "cstdarg": "cpp", 58 | "cwchar": "cpp", 59 | "cwctype": "cpp", 60 | "unordered_map": "cpp", 61 | "fstream": "cpp", 62 | "istream": "cpp", 63 | "new": "cpp", 64 | "ostream": "cpp", 65 | "sstream": "cpp", 66 | "streambuf": "cpp", 67 | "cinttypes": "cpp", 68 | "random": "cpp", 69 | "xstring": "cpp", 70 | "codecvt": "cpp", 71 | "complex": "cpp", 72 | "condition_variable": "cpp", 73 | "ctime": "cpp", 74 | "iomanip": "cpp", 75 | "iostream": "cpp", 76 | "mutex": "cpp", 77 | "numeric": "cpp", 78 | "thread": "cpp", 79 | "__errc": "cpp", 80 | "__hash_table": "cpp", 81 | "__mutex_base": "cpp", 82 | "__string": "cpp", 83 | "__threading_support": "cpp", 84 | "bit": "cpp", 85 | "compare": "cpp", 86 | "concepts": "cpp", 87 | "numbers": "cpp", 88 | "semaphore": "cpp", 89 | "stack": "cpp", 90 | "memory_resource": "cpp", 91 | "stop_token": "cpp", 92 | "format": "cpp", 93 | "forward_list": "cpp", 94 | "xhash": "cpp", 95 | "xiosbase": "cpp", 96 | "xtree": "cpp", 97 | "xutility": "cpp", 98 | "unordered_set": "cpp", 99 | "xfacet": "cpp", 100 | "xlocale": "cpp", 101 | "xlocbuf": "cpp", 102 | "xlocinfo": "cpp", 103 | "xlocmes": "cpp", 104 | "xlocmon": "cpp", 105 | "xlocnum": "cpp", 106 | "xloctime": "cpp", 107 | "xmemory": "cpp", 108 | "__bits": "cpp", 109 | "variant": "cpp", 110 | "xtr1common": "cpp", 111 | "*.rh": "cpp", 112 | "__memory": "cpp", 113 | "filesystem": "cpp" 114 | }, 115 | "files.exclude": { 116 | "src/out": true, 117 | "**/.git": true, 118 | "**/.svn": true, 119 | "**/.hg": true, 120 | "**/CVS": true, 121 | "**/.DS_Store": true, 122 | "**/Thumbs.db": true 123 | }, 124 | "search.exclude": { 125 | "**/.yarn": true, 126 | "**/.pnp.*": true 127 | }, 128 | "prettier.prettierPath": ".yarn/sdks/prettier/index.js", 129 | "typescript.tsdk": ".yarn/sdks/typescript/lib", 130 | "typescript.enablePromptUseWorkspaceTsdk": true 131 | } 132 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Build", 8 | "type": "shell", 9 | "command": "yarn make", 10 | "problemMatcher": "$gcc", 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | }, 15 | "options": { "cwd": "${workspaceFolder}/src" } 16 | }, 17 | { 18 | "label": "Clean", 19 | "type": "shell", 20 | "command": "yarn clean", 21 | "problemMatcher": "$gcc", 22 | "group": "build" 23 | }, 24 | { 25 | "label": "Make 3-Piece", 26 | "type": "shell", 27 | "command": "yarn threepiece", 28 | "problemMatcher": "$gcc", 29 | "group": "build" 30 | }, 31 | { 32 | "label": "Make Laptype", 33 | "type": "shell", 34 | "command": "yarn laptype", 35 | "problemMatcher": "$gcc", 36 | "group": "build" 37 | }, 38 | { 39 | "label": "Make 41", 40 | "type": "shell", 41 | "command": "yarn 41", 42 | "problemMatcher": "$gcc", 43 | "group": "build" 44 | }, 45 | { 46 | "label": "Flash 3-Piece", 47 | "type": "shell", 48 | "command": "yarn 3pf", 49 | "problemMatcher": "$gcc", 50 | "group": "build" 51 | }, 52 | { 53 | "label": "Flash Laptype", 54 | "type": "shell", 55 | "command": "yarn ltf", 56 | "problemMatcher": "$gcc", 57 | "group": "build" 58 | }, 59 | { 60 | "label": "Flash 41", 61 | "type": "shell", 62 | "command": "yarn 41f", 63 | "problemMatcher": "$gcc", 64 | "group": "build" 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /.yarn/sdks/integrations.yml: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by @yarnpkg/sdks. 2 | # Manual changes might be lost! 3 | 4 | integrations: 5 | - vscode 6 | -------------------------------------------------------------------------------- /.yarn/sdks/prettier/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require prettier/index.js 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real prettier/index.js your application uses 20 | module.exports = absRequire(`prettier/index.js`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/prettier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prettier", 3 | "version": "2.8.8-sdk", 4 | "main": "./index.js", 5 | "type": "commonjs" 6 | } 7 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/bin/tsc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require typescript/bin/tsc 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real typescript/bin/tsc your application uses 20 | module.exports = absRequire(`typescript/bin/tsc`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/bin/tsserver: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require typescript/bin/tsserver 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real typescript/bin/tsserver your application uses 20 | module.exports = absRequire(`typescript/bin/tsserver`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/lib/tsc.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require typescript/lib/tsc.js 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real typescript/lib/tsc.js your application uses 20 | module.exports = absRequire(`typescript/lib/tsc.js`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/lib/typescript.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require typescript/lib/typescript.js 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real typescript/lib/typescript.js your application uses 20 | module.exports = absRequire(`typescript/lib/typescript.js`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript", 3 | "version": "5.0.2-sdk", 4 | "main": "./lib/typescript.js", 5 | "type": "commonjs" 6 | } 7 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | yarnPath: .yarn/releases/yarn-4.2.1.cjs 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Kevin Frei 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 | -------------------------------------------------------------------------------- /WiredSketch/CoreCapability.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | uint32_t last_change[COLS * ROWS] = {0}; 4 | bool pressed[COLS * ROWS] = {0}; 5 | 6 | uint32_t hsvToRgb(float h, float s, float v) { 7 | float r = 0, g = 0, b = 0; 8 | int i = int(h * 6); 9 | float f = h * 6 - i; 10 | float p = v * (1 - s); 11 | float q = v * (1 - f * s); 12 | float t = v * (1 - (1 - f) * s); 13 | switch (i % 6) { 14 | case 0: 15 | r = v; 16 | g = t; 17 | b = p; 18 | break; 19 | case 1: 20 | r = q; 21 | g = v; 22 | b = p; 23 | break; 24 | case 2: 25 | r = p; 26 | g = v; 27 | b = t; 28 | break; 29 | case 3: 30 | r = p; 31 | g = q; 32 | b = v; 33 | break; 34 | case 4: 35 | r = t; 36 | g = p; 37 | b = v; 38 | break; 39 | case 5: 40 | r = v; 41 | g = p; 42 | b = q; 43 | break; 44 | } 45 | return (int(r * 255) << 16) | (int(g * 255) << 8) | int(b * 255); 46 | } 47 | 48 | void setupComms(); 49 | void setupMatrix(); 50 | void setupIndicators(); 51 | // Called when starting a column (probably write low, with delay) 52 | void startColumn(uint8_t colIdx); 53 | // Called when ending a column (probably a write high, no delay?) 54 | void endColumn(uint8_t colIdx); 55 | // Returns true if the cur column + row is pressed (probably read == LOW) 56 | bool readRow(uint8_t rowIdx); 57 | // Called for every loop: Indicate the passage of time 58 | void timeIndication(uint32_t now); 59 | // This sends the data over the comms channel 60 | void sendData(uint8_t value); 61 | // You can indiate a key change in here: 62 | void indicateChange(uint8_t r, uint8_t c, uint8_t p, uint32_t now); 63 | 64 | uint32_t makeHSV(uint16_t hue, uint8_t sat, uint8_t val) { 65 | return hue << 16 | sat << 8 | val; 66 | } 67 | 68 | uint32_t getColor(uint32_t number) { 69 | float val = (float)(number & 0xFF) / 255.0f; 70 | float sat = (float)((number >> 8) & 0xFF) / 255.0f; 71 | float hue = ((number >> 16) % 360) / 360.0f; 72 | return hsvToRgb(hue, sat, val); 73 | } 74 | 75 | uint8_t index(uint8_t row, uint8_t col) { 76 | return row * 6 + col; 77 | } 78 | 79 | uint8_t encodeValue(uint8_t row, uint8_t col, bool pressed) { 80 | uint8_t code = index(row, col) + (pressed ? 0 : 36); 81 | // Now add some miniscule amount of error resilience: 82 | return code * 3 + code % 3 + 1; 83 | } 84 | 85 | void reportChange(uint8_t r, uint8_t c, uint8_t p) { 86 | sendData(encodeValue(r, c, p)); 87 | } 88 | 89 | bool debouncedChange(uint8_t r, uint8_t c, bool p, uint32_t now) { 90 | uint8_t idx = index(r, c); 91 | return p != pressed[idx] && last_change[idx] < now - debounce_time; 92 | } 93 | 94 | // Record the state & the last-change time 95 | void recordChange(uint8_t r, uint8_t c, bool p, uint32_t now) { 96 | uint8_t idx = index(r, c); 97 | pressed[idx] = p; 98 | last_change[idx] = now; 99 | indicateChange(r, c, p, now); 100 | } 101 | 102 | void setup() { 103 | setupComms(); 104 | setupMatrix(); 105 | setupIndicators(); 106 | } 107 | 108 | void loop() { 109 | uint32_t now = millis(); 110 | for (uint8_t c = 0; c < COLS; c++) { 111 | startColumn(c); 112 | for (uint8_t r = 0; r < ROWS; r++) { 113 | bool p = readRow(r); 114 | if (debouncedChange(r, c, p, now)) { 115 | // Report the change up the wire 116 | reportChange(r, c, p); 117 | recordChange(r, c, p, now); 118 | } 119 | } 120 | endColumn(c); 121 | } 122 | timeIndication(now); 123 | } 124 | -------------------------------------------------------------------------------- /WiredSketch/Left-nRF52840/CoreCapability.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | uint32_t last_change[COLS * ROWS] = {0}; 4 | bool pressed[COLS * ROWS] = {0}; 5 | 6 | uint32_t hsvToRgb(float h, float s, float v) { 7 | float r = 0, g = 0, b = 0; 8 | int i = int(h * 6); 9 | float f = h * 6 - i; 10 | float p = v * (1 - s); 11 | float q = v * (1 - f * s); 12 | float t = v * (1 - (1 - f) * s); 13 | switch (i % 6) { 14 | case 0: 15 | r = v; 16 | g = t; 17 | b = p; 18 | break; 19 | case 1: 20 | r = q; 21 | g = v; 22 | b = p; 23 | break; 24 | case 2: 25 | r = p; 26 | g = v; 27 | b = t; 28 | break; 29 | case 3: 30 | r = p; 31 | g = q; 32 | b = v; 33 | break; 34 | case 4: 35 | r = t; 36 | g = p; 37 | b = v; 38 | break; 39 | case 5: 40 | r = v; 41 | g = p; 42 | b = q; 43 | break; 44 | } 45 | return (int(r * 255) << 16) | (int(g * 255) << 8) | int(b * 255); 46 | } 47 | 48 | void setupComms(); 49 | void setupMatrix(); 50 | void setupIndicators(); 51 | // Called when starting a column (probably write low, with delay) 52 | void startColumn(uint8_t colIdx); 53 | // Called when ending a column (probably a write high, no delay?) 54 | void endColumn(uint8_t colIdx); 55 | // Returns true if the cur column + row is pressed (probably read == LOW) 56 | bool readRow(uint8_t rowIdx); 57 | // Called for every loop: Indicate the passage of time 58 | void timeIndication(uint32_t now); 59 | // This sends the data over the comms channel 60 | void sendData(uint8_t value); 61 | // You can indiate a key change in here: 62 | void indicateChange(uint8_t r, uint8_t c, uint8_t p, uint32_t now); 63 | 64 | uint32_t makeHSV(uint16_t hue, uint8_t sat, uint8_t val) { 65 | return hue << 16 | sat << 8 | val; 66 | } 67 | 68 | uint32_t getColor(uint32_t number) { 69 | float val = (float)(number & 0xFF) / 255.0f; 70 | float sat = (float)((number >> 8) & 0xFF) / 255.0f; 71 | float hue = ((number >> 16) % 360) / 360.0f; 72 | return hsvToRgb(hue, sat, val); 73 | } 74 | 75 | uint8_t index(uint8_t row, uint8_t col) { 76 | return row * 6 + col; 77 | } 78 | 79 | uint8_t encodeValue(uint8_t row, uint8_t col, bool pressed) { 80 | uint8_t code = index(row, col) + (pressed ? 0 : 36); 81 | // Now add some miniscule amount of error resilience: 82 | return code * 3 + code % 3 + 1; 83 | } 84 | 85 | // Record the state & the last-change time, and report it to the controller 86 | void reportChange(uint8_t r, uint8_t c, uint8_t p, uint32_t now) { 87 | uint8_t idx = index(r, c); 88 | pressed[idx] = p; 89 | last_change[idx] = now; 90 | sendData(encodeValue(r, c, p)); 91 | indicateChange(r, c, p, now); 92 | } 93 | 94 | bool debouncedChange(uint8_t r, uint8_t c, bool p, uint32_t now) { 95 | uint8_t idx = index(r, c); 96 | return p != pressed[idx] && last_change[idx] < now - debounce_time; 97 | } 98 | 99 | void setup() { 100 | setupComms(); 101 | setupMatrix(); 102 | setupIndicators(); 103 | } 104 | 105 | void loop() { 106 | for (uint8_t c = 0; c < COLS; c++) { 107 | startColumn(c); 108 | uint32_t now = millis(); 109 | for (uint8_t r = 0; r < ROWS; r++) { 110 | bool p = readRow(r); 111 | if (debouncedChange(r, c, p, now)) { 112 | // Report the change up the wire 113 | reportChange(r, c, p, now); 114 | } 115 | } 116 | endColumn(c); 117 | } 118 | timeIndication(millis()); 119 | } 120 | -------------------------------------------------------------------------------- /WiredSketch/Left-nRF52840/Left-nRF52840.ino: -------------------------------------------------------------------------------- 1 | // This include is to work around an issue with linking with some Adafruit 2 | // libraries 3 | // #include "Adafruit_TinyUSB.h" 4 | 5 | #include 6 | #include 7 | 8 | #include "Adafruit_DotStar.h" 9 | #include "Adafruit_NeoPixel.h" 10 | 11 | // This runs on Adafruit nRF52840 devices 12 | 13 | const uint8_t ROWS = 6; 14 | const uint8_t COLS = 6; 15 | const uint32_t debounce_time = 15; 16 | 17 | #include "CoreCapability.hpp" 18 | 19 | #if defined(ARDUINO_NRF52480_ITSYBITSY) 20 | uint8_t colPins[COLS] = {A4, MISO, 2, 7, 22, 21}; // ItsyBitsy 21 | uint8_t rowPins[ROWS] = {11, 12, 10, 25, A5, A3}; // ItsyBitsy 22 | const uint8_t BLUE_LED = 3; 23 | 24 | const uint8_t NumDotStarPixels = 1; 25 | const uint8_t DotStarData = 8; 26 | const uint8_t DotStarClock = 6; 27 | 28 | Adafruit_DotStar pixel(NumDotStarPixels, 29 | DotStarData, 30 | DotStarClock, 31 | DOTSTAR_GBR); 32 | 33 | #elif defined(ARDUINO_NRF52840_FEATHER) 34 | 35 | uint8_t colPins[COLS] = {12, 6, 5, A4, SCK, MOSI}; // Feather 36 | uint8_t rowPins[ROWS] = {A1, A0, A2, 10, 11, 13}; // Feather 37 | 38 | const uint8_t BLUE_LED = 4; 39 | 40 | Adafruit_NeoPixel pixel(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800); 41 | 42 | #define DataSerial Serial1 43 | 44 | #elif defined(ARDUINO_NRF52832_FEATHER) 45 | // A6 46 | uint8_t colPins[COLS] = {15, 30, 27, A4, SCK, MOSI}; // Feather 47 | uint8_t rowPins[ROWS] = {A1, A0, A2, 11, 7, 16}; // Feather 48 | 49 | const uint8_t BLUE_LED = 17; 50 | 51 | #define DataSerial Serial 52 | 53 | // Adafruit_NeoPixel pixel(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800); 54 | 55 | #else 56 | 57 | #error Sorry: unsupported hardware 58 | #endif 59 | 60 | void setupComms() { 61 | // If you don't use the debug serial port 62 | // you have to double-click the reset button to get the device 63 | // to a flashable state 64 | #if !defined(ARDUINO_NRF52832_FEATHER) 65 | Serial.begin(115200); 66 | #endif 67 | // Run at 1Mbps, which seems both plenty fast, and is also reliable 68 | DataSerial.begin(1 << 20); 69 | } 70 | 71 | void setupMatrix() { 72 | digitalWrite(BLUE_LED, HIGH); 73 | for (uint8_t r : rowPins) { 74 | pinMode(r, INPUT_PULLUP); 75 | } 76 | for (uint8_t c : colPins) { 77 | pinMode(c, OUTPUT); 78 | digitalWrite(c, HIGH); 79 | } 80 | } 81 | 82 | void setupIndicators() { 83 | pinMode(BLUE_LED, OUTPUT); 84 | digitalWrite(BLUE_LED, LOW); 85 | #if !defined(ARDUINO_NRF52832_FEATHER) 86 | pixel.begin(); 87 | #endif 88 | } 89 | 90 | void startColumn(uint8_t colIdx) { 91 | digitalWrite(colPins[colIdx], LOW); 92 | delay(2); 93 | } 94 | 95 | bool readRow(uint8_t rowIdx) { 96 | return digitalRead(rowPins[rowIdx]) == LOW; 97 | } 98 | 99 | void sendData(uint8_t val) { 100 | DataSerial.write(val); 101 | } 102 | 103 | void indicateChange(uint8_t r, uint8_t c, uint8_t p, uint32_t now) { 104 | analogWrite(BLUE_LED, p ? 10 : 0); 105 | } 106 | 107 | void endColumn(uint8_t colIdx) { 108 | digitalWrite(colPins[colIdx], HIGH); 109 | } 110 | 111 | uint32_t lastCol = 0; 112 | // Called for every loop: Indicate the passage of time 113 | void timeIndication(uint32_t now) { 114 | #if !defined(ARDUINO_NRF52832_FEATHER) 115 | now = now >> 5; 116 | if (now != lastCol) { 117 | lastCol = now; 118 | uint32_t hsv = makeHSV(now % 360, (now / 360) & 0xFF, 20); 119 | uint32_t col = getColor(hsv); 120 | pixel.setPixelColor(0, (col >> 16) & 0xFF, (col >> 8) & 0xFF, col & 0xFF); 121 | pixel.show(); 122 | } 123 | #endif 124 | } 125 | -------------------------------------------------------------------------------- /WiredSketch/Left-rp2040/Left-rp2040.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // This runs on Adafruit Feather RP2040 devices 7 | 8 | constexpr byte ROWS = 6; 9 | constexpr byte COLS = 6; 10 | 11 | // Compiled & uploaded to board on 2023-11-19 12 | 13 | /* 14 | 26 27 28 29 24 25 18 19 20 15 | R1 R0 R2 __ C3 __ C4 C5 __ 16 | |=============================| 17 | R5 C0 R4 R3 __ C1 C2 18 | 13 12 11 10 9 8 7 19 | */ 20 | 21 | // C0:12 C1: 8 C2: 7 C3:24 C4:18 C5:19 22 | // R0:27 R1:26 R2:28 R3:10 R4:11 R5:13 23 | 24 | uint8_t colPins[COLS] = {12, 8, 7, 24, 18, 19}; 25 | uint8_t rowPins[ROWS] = {27, 26, 28, 10, 11, 13}; 26 | 27 | constexpr uint32_t debounce_time = 25; 28 | 29 | uint32_t last_change[COLS * ROWS] = {0}; 30 | bool pressed[COLS * ROWS] = {0}; 31 | 32 | Adafruit_NeoPixel pixels(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800); 33 | 34 | void setupComms() { 35 | // Run at 1Mbps, which seems both plenty fast, and is also reliable 36 | Serial1.begin(1 << 20); 37 | Serial.begin(115200); 38 | } 39 | 40 | void setupMatrix() { 41 | // Configure the pins 42 | for (uint8_t r : rowPins) { 43 | pinMode(r, INPUT_PULLUP); 44 | } 45 | for (uint8_t c : colPins) { 46 | pinMode(c, OUTPUT); 47 | digitalWrite(c, HIGH); 48 | } 49 | } 50 | 51 | void setupIndicators() { 52 | // Configure the neopixel for silly debug info 53 | // pinMode(NEOPIXEL_POWER, OUTPUT); 54 | // digitalWrite(NEOPIXEL_POWER, HIGH); 55 | pixels.begin(); 56 | } 57 | 58 | uint8_t index(uint8_t row, uint8_t col) { 59 | return row * 6 + col; 60 | } 61 | 62 | uint8_t encodeValue(uint8_t row, uint8_t col, bool pressed) { 63 | uint8_t code = index(row, col) + (pressed ? 0 : 36); 64 | // Now add some miniscule amount of error resilience: 65 | return code * 3 + code % 3 + 1; 66 | } 67 | 68 | void indicateChange(uint8_t r, uint8_t c, uint8_t p) { 69 | pixels.clear(); 70 | pixels.setPixelColor( 71 | 0, p ? pixels.Color(16, 45 + r * 42, 45 + c * 42) : pixels.Color(0, 0, 0)); 72 | pixels.show(); 73 | } 74 | 75 | void reportChange(uint8_t r, uint8_t c, uint8_t p) { 76 | Serial1.write(encodeValue(r, c, p)); 77 | indicateChange(r, c, p); 78 | Serial.print("r:"); 79 | Serial.print(r); 80 | Serial.print(" c:"); 81 | Serial.print(c); 82 | Serial.print(" p:"); 83 | Serial.println(p ? "presseed" : "released"); 84 | } 85 | 86 | bool debouncedChange(uint8_t r, uint8_t c, bool p, uint32_t now) { 87 | uint8_t idx = index(r, c); 88 | return p != pressed[idx] && last_change[idx] < now - debounce_time; 89 | } 90 | 91 | // Record the state & the last-change time 92 | void recordChange(uint8_t r, uint8_t c, bool p, uint32_t now) { 93 | uint8_t idx = index(r, c); 94 | pressed[idx] = p; 95 | last_change[idx] = now; 96 | } 97 | 98 | void setup() { 99 | setupComms(); 100 | setupMatrix(); 101 | setupIndicators(); 102 | } 103 | 104 | void loop() { 105 | for (uint8_t c = 0; c < COLS; c++) { 106 | digitalWrite(colPins[c], LOW); 107 | // This looks arbitrary but it seems to take 108 | // the output change a little while to stabilize on the input pins 109 | delay(1); 110 | uint32_t now = millis(); 111 | for (uint8_t r = 0; r < ROWS; r++) { 112 | bool p = digitalRead(rowPins[r]) == LOW; 113 | if (debouncedChange(r, c, p, now)) { 114 | // Report the change up the wire 115 | reportChange(r, c, p); 116 | recordChange(r, c, p, now); 117 | } 118 | } 119 | digitalWrite(colPins[c], HIGH); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /WiredSketch/Right-nRF52840/CoreCapability.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | uint32_t last_change[COLS * ROWS] = {0}; 4 | bool pressed[COLS * ROWS] = {0}; 5 | 6 | uint32_t hsvToRgb(float h, float s, float v) { 7 | float r = 0, g = 0, b = 0; 8 | int i = int(h * 6); 9 | float f = h * 6 - i; 10 | float p = v * (1 - s); 11 | float q = v * (1 - f * s); 12 | float t = v * (1 - (1 - f) * s); 13 | switch (i % 6) { 14 | case 0: 15 | r = v; 16 | g = t; 17 | b = p; 18 | break; 19 | case 1: 20 | r = q; 21 | g = v; 22 | b = p; 23 | break; 24 | case 2: 25 | r = p; 26 | g = v; 27 | b = t; 28 | break; 29 | case 3: 30 | r = p; 31 | g = q; 32 | b = v; 33 | break; 34 | case 4: 35 | r = t; 36 | g = p; 37 | b = v; 38 | break; 39 | case 5: 40 | r = v; 41 | g = p; 42 | b = q; 43 | break; 44 | } 45 | return (int(r * 255) << 16) | (int(g * 255) << 8) | int(b * 255); 46 | } 47 | 48 | void setupComms(); 49 | void setupMatrix(); 50 | void setupIndicators(); 51 | // Called when starting a column (probably write low, with delay) 52 | void startColumn(uint8_t colIdx); 53 | // Called when ending a column (probably a write high, no delay?) 54 | void endColumn(uint8_t colIdx); 55 | // Returns true if the cur column + row is pressed (probably read == LOW) 56 | bool readRow(uint8_t rowIdx); 57 | // Called for every loop: Indicate the passage of time 58 | void timeIndication(uint32_t now); 59 | // This sends the data over the comms channel 60 | void sendData(uint8_t value); 61 | // You can indiate a key change in here: 62 | void indicateChange(uint8_t r, uint8_t c, uint8_t p, uint32_t now); 63 | 64 | uint32_t makeHSV(uint16_t hue, uint8_t sat, uint8_t val) { 65 | return hue << 16 | sat << 8 | val; 66 | } 67 | 68 | uint32_t getColor(uint32_t number) { 69 | float val = (float)(number & 0xFF) / 255.0f; 70 | float sat = (float)((number >> 8) & 0xFF) / 255.0f; 71 | float hue = ((number >> 16) % 360) / 360.0f; 72 | return hsvToRgb(hue, sat, val); 73 | } 74 | 75 | uint8_t index(uint8_t row, uint8_t col) { 76 | return row * 6 + col; 77 | } 78 | 79 | uint8_t encodeValue(uint8_t row, uint8_t col, bool pressed) { 80 | uint8_t code = index(row, col) + (pressed ? 0 : 36); 81 | // Now add some miniscule amount of error resilience: 82 | return code * 3 + code % 3 + 1; 83 | } 84 | 85 | void reportChange(uint8_t r, uint8_t c, uint8_t p) { 86 | sendData(encodeValue(r, c, p)); 87 | } 88 | 89 | bool debouncedChange(uint8_t r, uint8_t c, bool p, uint32_t now) { 90 | uint8_t idx = index(r, c); 91 | return p != pressed[idx] && last_change[idx] < now - debounce_time; 92 | } 93 | 94 | // Record the state & the last-change time 95 | void recordChange(uint8_t r, uint8_t c, bool p, uint32_t now) { 96 | uint8_t idx = index(r, c); 97 | pressed[idx] = p; 98 | last_change[idx] = now; 99 | indicateChange(r, c, p, now); 100 | } 101 | 102 | void setup() { 103 | setupComms(); 104 | setupMatrix(); 105 | setupIndicators(); 106 | } 107 | 108 | void loop() { 109 | uint32_t now = millis(); 110 | for (uint8_t c = 0; c < COLS; c++) { 111 | startColumn(c); 112 | for (uint8_t r = 0; r < ROWS; r++) { 113 | bool p = readRow(r); 114 | if (debouncedChange(r, c, p, now)) { 115 | // Report the change up the wire 116 | reportChange(r, c, p); 117 | recordChange(r, c, p, now); 118 | } 119 | } 120 | endColumn(c); 121 | } 122 | timeIndication(now); 123 | } 124 | -------------------------------------------------------------------------------- /WiredSketch/Right-nRF52840/Right-nRF52840.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Adafruit_DotStar.h" 5 | 6 | // This runs on Adafruit nRF52840 devices 7 | 8 | const uint8_t ROWS = 6; 9 | const uint8_t COLS = 6; 10 | const uint32_t debounce_time = 15; 11 | 12 | #include "CoreCapability.hpp" 13 | 14 | 15 | /* 16 | 13 12 11 10 9 7 21 22 17 | C5 R2 R0 R5 R4 R3 __ C4 C3 18 | |=============================| 19 | C0 R1 __ __ __ C2 C1 20 | 16 17 24 23 21 | */ 22 | 23 | // C0:16 C1:23 C2:24 C3:22 C4:21 C5:13 24 | // R0:11 R1:17 R2:12 R3: 7 R4: 9 R5:10 25 | 26 | uint8_t colPins[COLS] = {16, 23, 24, 22, 21, 13}; 27 | uint8_t rowPins[ROWS] = {11, 17, 12, 7, 9, 10}; 28 | 29 | const uint8_t BLUE_LED = 3; 30 | const uint8_t NumDotStarPixels = 1; 31 | const uint8_t DotStarData = 8; 32 | const uint8_t DotStarClock = 6; 33 | 34 | Adafruit_DotStar pixel(NumDotStarPixels, 35 | DotStarData, 36 | DotStarClock, 37 | DOTSTAR_GBR); 38 | 39 | void setupComms() { 40 | // If you don't use the debug serial port 41 | // you have to double-click the reset button to get the device 42 | // to a flashable state 43 | Serial.begin(9600); 44 | // Run at 1Mbps, which seems both plenty fast, and is also reliable 45 | Serial1.begin(1 << 20); 46 | } 47 | 48 | void setupMatrix() { 49 | digitalWrite(BLUE_LED, HIGH); 50 | for (uint8_t r : rowPins) { 51 | pinMode(r, INPUT_PULLUP); 52 | } 53 | for (uint8_t c : colPins) { 54 | pinMode(c, OUTPUT); 55 | digitalWrite(c, HIGH); 56 | } 57 | } 58 | 59 | void setupIndicators() { 60 | pinMode(BLUE_LED, OUTPUT); 61 | digitalWrite(BLUE_LED, LOW); 62 | pixel.begin(); 63 | pixel.setPixelColor(0, 0x10, 0x10, 0x10); 64 | pixel.show(); 65 | delay(50); 66 | pixel.setPixelColor(0, 0, 0, 0); 67 | pixel.show(); 68 | } 69 | 70 | void startColumn(uint8_t colIdx) { 71 | digitalWrite(colPins[colIdx], LOW); 72 | delay(1); 73 | } 74 | 75 | bool readRow(uint8_t rowIdx) { 76 | // delay(1); 77 | return digitalRead(rowPins[rowIdx]) == LOW; 78 | } 79 | 80 | void sendData(uint8_t val) { 81 | Serial1.write(val); 82 | // Serial.print("Sending data:"); 83 | // Serial.println(val); 84 | } 85 | 86 | void indicateChange(uint8_t r, uint8_t c, uint8_t p, uint32_t now) { 87 | analogWrite(BLUE_LED, p ? 10 : 0); 88 | /* 89 | Serial.print("Got row "); 90 | Serial.print(r); 91 | Serial.print(" col "); 92 | Serial.print(c); 93 | Serial.println(p ? " pressed": " released"); 94 | */ 95 | } 96 | 97 | void endColumn(uint8_t colIdx) { 98 | digitalWrite(colPins[colIdx], HIGH); 99 | // delay(1); 100 | } 101 | 102 | uint32_t lastCol = 0; 103 | // Called for every loop: Indicate the passage of time 104 | void timeIndication(uint32_t now) { 105 | now = now >> 7; 106 | if (now != lastCol) { 107 | lastCol = now; 108 | uint32_t hsv = makeHSV(now % 360, (now / 360) & 0xFF, 20); 109 | uint32_t col = getColor(hsv); 110 | pixel.setPixelColor(0, (col >> 16) & 0xFF, (col >> 8) & 0xFF, col & 0xFF); 111 | pixel.show(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /WiredSketch/Right-rp2040/Right-rp2040.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // This runs on Adafruit Feather RP2040 devices 7 | 8 | constexpr byte ROWS = 6; 9 | constexpr byte COLS = 6; 10 | 11 | // Compiled & uploaded to board on 2023-11-19 12 | 13 | /* 14 | 26 27 28 29 24 25 18 19 20 15 | C5 R2 R0 R5 R4 R3 C4 C3 __ 16 | |=============================| 17 | C0 R1 __ __ __ C2 C1 18 | 13 12 __ __ __ 8 7 19 | */ 20 | 21 | // C0:13 C1: 7 C2: 8 C3:19 C4:18 C5:26 22 | // R0:28 R1:12 R2:27 R3:25 R4:24 R5:29 23 | 24 | uint8_t colPins[COLS] = {13, 7, 8, 19, 18, 26}; 25 | uint8_t rowPins[ROWS] = {28, 12, 27, 25, 24, 29}; 26 | 27 | constexpr uint32_t debounce_time = 25; 28 | 29 | uint32_t last_change[COLS * ROWS] = {0}; 30 | bool pressed[COLS * ROWS] = {0}; 31 | 32 | Adafruit_NeoPixel pixels(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800); 33 | 34 | void setupComms() { 35 | // Run at 1Mbps, which seems both plenty fast, and is also reliable 36 | Serial1.begin(1 << 20); 37 | Serial.begin(115200); 38 | } 39 | 40 | void setupMatrix() { 41 | // Configure the pins 42 | for (uint8_t r : rowPins) { 43 | pinMode(r, INPUT_PULLUP); 44 | } 45 | for (uint8_t c : colPins) { 46 | pinMode(c, OUTPUT); 47 | digitalWrite(c, HIGH); 48 | } 49 | } 50 | 51 | void setupIndicators() { 52 | // Configure the neopixel for silly debug info 53 | // pinMode(NEOPIXEL_POWER, OUTPUT); 54 | // digitalWrite(NEOPIXEL_POWER, HIGH); 55 | pixels.begin(); 56 | } 57 | 58 | uint8_t index(uint8_t row, uint8_t col) { 59 | return row * 6 + col; 60 | } 61 | 62 | uint8_t encodeValue(uint8_t row, uint8_t col, bool pressed) { 63 | uint8_t code = index(row, col) + (pressed ? 0 : 36); 64 | // Now add some miniscule amount of error resilience: 65 | return code * 3 + code % 3 + 1; 66 | } 67 | 68 | void indicateChange(uint8_t r, uint8_t c, uint8_t p) { 69 | pixels.clear(); 70 | pixels.setPixelColor( 71 | 0, p ? pixels.Color(16, 45 + r * 42, 45 + c * 42) : pixels.Color(0, 0, 0)); 72 | pixels.show(); 73 | } 74 | 75 | void reportChange(uint8_t r, uint8_t c, uint8_t p) { 76 | Serial1.write(encodeValue(r, c, p)); 77 | indicateChange(r, c, p); 78 | Serial.print("r:"); 79 | Serial.print(r); 80 | Serial.print(" c:"); 81 | Serial.print(c); 82 | Serial.print(" p:"); 83 | Serial.println(p ? "presseed" : "released"); 84 | } 85 | 86 | bool debouncedChange(uint8_t r, uint8_t c, bool p, uint32_t now) { 87 | uint8_t idx = index(r, c); 88 | return p != pressed[idx] && last_change[idx] < now - debounce_time; 89 | } 90 | 91 | // Record the state & the last-change time 92 | void recordChange(uint8_t r, uint8_t c, bool p, uint32_t now) { 93 | uint8_t idx = index(r, c); 94 | pressed[idx] = p; 95 | last_change[idx] = now; 96 | } 97 | 98 | void setup() { 99 | setupComms(); 100 | setupMatrix(); 101 | setupIndicators(); 102 | } 103 | 104 | void loop() { 105 | for (uint8_t c = 0; c < COLS; c++) { 106 | digitalWrite(colPins[c], LOW); 107 | // This looks arbitrary but it seems to take 108 | // the output change a little while to stabilize on the input pins 109 | delay(1); 110 | uint32_t now = millis(); 111 | for (uint8_t r = 0; r < ROWS; r++) { 112 | bool p = digitalRead(rowPins[r]) == LOW; 113 | if (debouncedChange(r, c, p, now)) { 114 | // Report the change up the wire 115 | reportChange(r, c, p); 116 | recordChange(r, c, p, now); 117 | } 118 | } 119 | digitalWrite(colPins[c], HIGH); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /cmake-simple/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | set(CMAKE_CXX_STANDARD 17) 4 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 5 | 6 | # Select stuff before setting the toolchain file 7 | set(CMKARD_BOARD Teensy40) # The name of the board we're compiling 8 | # Stuff that's specific to Teensy: 9 | set(CMKARD_IN_USB keyboard) # how is the Teensy USB stack configured? 10 | set(CMKARD_IN_SPEED 600) # the frequency to clock the CPU at 11 | set(CMKARD_IN_OPT osstd) # compilation flags 12 | set(CMKARD_IN_KEYS en-us) # keyboard layout (for typing strings) 13 | set(CMKARD_EXTRA_TIME_LOCAL 0) # TODO: Make this dynamically calculated for build time? 14 | set(CMKARD_SERIAL_PORT COM18) # TODO: Can we look for it? 15 | set(CMKARD_SERIAL_PORT_LABEL ${CMKARD_SERIAL_PORT}) 16 | set(CMKARD_SERIAL_PORT_PROTOCOL serial) 17 | 18 | # Must be before the project command: 19 | set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../Toolchain-Adafruit-nRF52480.cmake) 20 | 21 | project(LeftKeyboardClientWire) 22 | 23 | set(MY_SOURCE_FILES WiredSketchLeft.ino) 24 | 25 | # It's annoying that I can't configure this in the toolchain file 26 | # This makes .ino files compile as C++ files 27 | set_source_files_properties(${MY_SOURCE_FILES} PROPERTIES LANGUAGE CXX) 28 | add_definitions("-x c++") 29 | 30 | add_executable(leftFirmware ${MY_SOURCE_FILES}) 31 | 32 | # This should let me create a Flash target :) 33 | # ninja Test (case sensitive!) echos "Hello-there" 34 | add_custom_target(Test echo Hello-there) -------------------------------------------------------------------------------- /cmake-simple/Left-nRF52840/Left-nRF52840.ino: -------------------------------------------------------------------------------- 1 | // This include is to work around an issue with linking with some Adafruit 2 | // libraries 3 | #include "Adafruit_DotStar.h" 4 | #include "Adafruit_TinyUSB.h" 5 | 6 | #include 7 | #include 8 | 9 | // This runs on Adafruit nRF52840 devices 10 | 11 | const uint8_t ROWS = 6; 12 | const uint8_t COLS = 6; 13 | 14 | // uint8_t colPins[COLS] = {12, 6, 5, A4, SCK, MOSI}; // Feather 15 | uint8_t colPins[COLS] = {A4, MISO, 2, 7, 22, 21}; // ItsyBitsy 16 | // uint8_t rowPins[ROWS] = {A1, A0, A2, 10, 11, 13}; // Feather 17 | uint8_t rowPins[ROWS] = {11, 12, 10, 25, A5, A3}; // ItsyBitsy 18 | 19 | uint32_t last_change[COLS * ROWS] = {0}; 20 | bool pressed[COLS * ROWS] = {0}; 21 | const uint32_t debounce_time = 15; 22 | const uint8_t BLUE_LED = 3; 23 | 24 | const uint8_t NumDotStarPixels = 1; 25 | const uint8_t DotStarData = 8; 26 | const uint8_t DotStarClock = 6; 27 | 28 | Adafruit_DotStar pixel(NumDotStarPixels, 29 | DotStarData, 30 | DotStarClock, 31 | DOTSTAR_GBR); 32 | 33 | void setup() { 34 | // If you don't use the debug serial port 35 | // you have to double-click the reset button to get the device 36 | // to a flashable state 37 | Serial.begin(9600); 38 | // Run at 1Mbps, which seems both plenty fast, and is also reliable 39 | Serial1.begin(1 << 20); 40 | pinMode(BLUE_LED, OUTPUT); 41 | digitalWrite(BLUE_LED, HIGH); 42 | for (uint8_t r : rowPins) { 43 | pinMode(r, INPUT_PULLUP); 44 | } 45 | for (uint8_t c : colPins) { 46 | pinMode(c, OUTPUT); 47 | digitalWrite(c, HIGH); 48 | } 49 | digitalWrite(BLUE_LED, LOW); 50 | pixel.begin(); 51 | pixel.setPixelColor(0, 0x10, 0x10, 0x10); 52 | pixel.show(); 53 | delay(50); 54 | pixel.setPixelColor(0, 0, 0, 0); 55 | pixel.show(); 56 | } 57 | 58 | void loop() { 59 | uint32_t now = millis(); 60 | for (uint8_t c = 0; c < COLS; c++) { 61 | digitalWrite(colPins[c], LOW); 62 | delay(1); 63 | for (uint8_t r = 0; r < ROWS; r++) { 64 | bool p = digitalRead(rowPins[r]) == LOW; 65 | if (p != pressed[r * 6 + c] && 66 | last_change[r * 6 + c] + debounce_time > now) { 67 | uint8_t val = r * 6 + c + (p ? 0 : 36); 68 | Serial1.write((unsigned char)(val * 3 + val % 3 + 1)); 69 | analogWrite(BLUE_LED, p ? 10 : 0); 70 | pressed[r * 6 + c] = p; 71 | last_change[r * 6 + c] = now; 72 | } 73 | } 74 | digitalWrite(colPins[c], HIGH); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /docs/1mmMilling.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/1mmMilling.jpg -------------------------------------------------------------------------------- /docs/AluminumIsHard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/AluminumIsHard.jpg -------------------------------------------------------------------------------- /docs/FinalCanvasXDA.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/FinalCanvasXDA.jpg -------------------------------------------------------------------------------- /docs/FinalCasesSanded.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/FinalCasesSanded.jpg -------------------------------------------------------------------------------- /docs/FinalGraniteDSA.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/FinalGraniteDSA.jpg -------------------------------------------------------------------------------- /docs/FinishedLeftHand.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/FinishedLeftHand.jpg -------------------------------------------------------------------------------- /docs/FirstAluminumPrototype.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/FirstAluminumPrototype.jpg -------------------------------------------------------------------------------- /docs/FirstCasePrototype.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/FirstCasePrototype.jpg -------------------------------------------------------------------------------- /docs/KarbonCanvasXDA.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/KarbonCanvasXDA.jpg -------------------------------------------------------------------------------- /docs/Key Position Helper.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/Key Position Helper.xlsx -------------------------------------------------------------------------------- /docs/LapType.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/LapType.jpg -------------------------------------------------------------------------------- /docs/MillingAluminum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/MillingAluminum.jpg -------------------------------------------------------------------------------- /docs/PrototypeWithKeys.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/PrototypeWithKeys.jpg -------------------------------------------------------------------------------- /docs/RightHandPCB.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/RightHandPCB.jpg -------------------------------------------------------------------------------- /docs/SandingAndEdges.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/docs/SandingAndEdges.jpg -------------------------------------------------------------------------------- /docs/info.txt: -------------------------------------------------------------------------------- 1 | Extensions: 2 | LED's 3 | Display(s) 4 | Storage 5 | 6 | Modules: 7 | Edit Line 8 | Images 9 | Menu 10 | Calculator 11 | Apple IIe/6502 Emulator 12 | Games: 13 | Tetris 14 | Snake 15 | Bubble Popper? 16 | Traffic? 17 | Sudoku? 18 | 19 | KeyMatrix/RemoteMatrix static stuff: 20 | uint8_t matrix_size; 21 | typedef std::bitset bits; 22 | void Configure(); 23 | bits Read(); 24 | 25 | BoardIO: 26 | void Configure(); 27 | void Changed(time, state) 28 | void Tick(time) 29 | KeyboardMode Mode(time, curMode) 30 | void Reset(State) 31 | 32 | 33 | -------------------------------------------------------------------------------- /docs/notes-threepiece.txt: -------------------------------------------------------------------------------- 1 | Right Hand: 2 | 1 2 3 4 5 6 7 8 3 | C3 C4 R3 R4 R5 R0 R2 C5 4 | 5 | 1 2 3 4 6 | C1 C2 R1 C0 7 | 8 | 9 | C0: 13 10 | C1: 5 11 | C2: 6 12 | C3: M0 13 | C4: SCK 14 | C5: A0 15 | 16 | R0: A2 17 | R1: 12 18 | R2: A1 19 | R3: A5 20 | R4: A4 21 | R5: A3 22 | 23 | 24 | Left Hand: 25 | 1 2 3 4 5 6 26 | R5 C6 R4 R3 C7 C8 27 | 28 | 1 2 3 4 5 6 29 | R1 R0 R2 C9 C10 C11 30 | 31 | For Wires coming from adapter plates, pin "1" is the one closet to the top 32 | then it counts 33 | 1 2 3 4 5 6 34 | 12 11 10 9 8 7 35 | 36 | 37 | So, 38 | 39 | Right hand: 40 | 1 2 3 4 5 6 7 8 9 10 11 12 41 | T5 T6 T7 T8 B3 B4 B2 B1 T1 T2 T3 T4 42 | R5 R0 R2 C5 R1 C0 C2 C1 C3 C4 R3 R4 43 | 44 | Left hand: 45 | 1 2 3 4 5 6 7 8 9 10 11 12 46 | T3 T1 T2 B2 B1 B3 B4 B5 B6 T6 T5 T4 47 | R4 R5 C6 R0 R1 R2 C9 C10 C11 C8 C7 R3 -------------------------------------------------------------------------------- /fix_compile_commands.js: -------------------------------------------------------------------------------- 1 | // For each file named "compile_commands.json" under the src/out/* directory 2 | // fix the directory location to work from the root of the repo instead of src 3 | // TODO: Maybe just make this also invoke Make, since that would add a lot of 4 | // flexibility to the system 5 | 6 | const path = require('path'); 7 | const fsp = require('fs').promises; 8 | 9 | const rootLoc = path.join('src', 'out'); 10 | const ccname = 'compile_commands.jsn'; 11 | // File all the compile_commands.json files underneath src/out 12 | async function findFiles() { 13 | const files = await fsp.readdir(rootLoc); 14 | const ccfiles = []; 15 | for (let subdir of files) { 16 | const fullDir = path.join(rootLoc, subdir); 17 | const allFiles = await fsp.readdir(fullDir); 18 | if (allFiles.some((fn) => fn === ccname)) { 19 | ccfiles.push(path.join(fullDir, ccname)); 20 | } 21 | } 22 | return ccfiles; 23 | } 24 | 25 | async function fixFile(filename) { 26 | const text = (await fsp.readFile(filename)) 27 | .toString() 28 | .split('\n') 29 | .map((v) => v.trim()); 30 | const contents = []; 31 | for (let i = 0; i < text.length - 2; i += 3) { 32 | const obj = {}; 33 | const ln1 = text[i]; 34 | let flag = 0; 35 | if (ln1.startsWith('dir#')) { 36 | const dir = ln1.substring(4); 37 | obj.directory = dir === '.' ? 'src' : dir; 38 | } 39 | flag += 1; 40 | const ln2 = text[i + 1]; 41 | if (ln2.startsWith('fil#')) { 42 | obj.file = ln2.substring(4); 43 | flag += 2; 44 | } 45 | const ln3 = text[i + 2]; 46 | if (ln3.startsWith('cmd#')) { 47 | obj.command = ln3.substring(4); 48 | flag += 4; 49 | } 50 | if (flag === 7) { 51 | contents.push(obj); 52 | } 53 | } 54 | const jsonCC = JSON.stringify(contents, null, ' '); 55 | await fsp.writeFile(filename.replace('.jsn', '.json'), jsonCC); 56 | } 57 | 58 | async function doit() { 59 | const files = await findFiles(); 60 | await Promise.all(files.map(fixFile)); 61 | } 62 | 63 | doit().catch(console.error); 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "freikey", 3 | "version": "1.0.0", 4 | "repository": "https://github.com/kevinfrei/FreiKey.git", 5 | "author": "Kevin Frei ", 6 | "license": "MIT", 7 | "scripts": { 8 | "41": "cd src && make 41 && yarn fixcc", 9 | "postinstall": "husky install", 10 | "___": "putting stuff in here means you can run it from anywhere in the repo :)", 11 | "format": "yarn ftool format", 12 | "fixcc": "yarn node fix_compile_commands.js", 13 | "__ ": "You can pass extra args in by doing 'yarn make -- ' - Actually, I don't think this works with the follow-on fixcc", 14 | "make": "cd src && make && yarn fixcc", 15 | "laptype": "cd src && make laptype && yarn fixcc", 16 | "lt": "cd src && make laptype && yarn fixcc", 17 | "ltf": "cd src && make laptypef && yarn fixcc", 18 | "threepiece": "cd src && make threepiece && yarn fixcc", 19 | "3pf": "cd src && make threepiecef && yarn fixcc", 20 | "f3p": "cd src && make threepiecef && yarn fixcc", 21 | "3p": "cd src && make threepiece && yarn fixcc", 22 | "big": "cd src && make big && yarn fixcc", 23 | "bigf": "cd src && make bigf && yarn fixcc", 24 | "calc": "cd src && make calc && yarn fixcc", 25 | "mock": "cd src && make mock && yarn fixcc", 26 | "img": "cd src && make img && yarn fixcc", 27 | "test": "cd src && make test && yarn fixcc", 28 | "sd": "cd src && make sd && yarn fixcc", 29 | "sdf": "cd src && make sdf && yarn fixcc", 30 | "41f": "cd src && make 41f && yarn fixcc", 31 | "clean": "cd src && make clean", 32 | "cmake": "rimraf build && cmake -S src -B build -G Ninja -DCMAKE_TOOLCHAIN_FILE=tools/toolchains/Teensy40.cmake", 33 | "build": "cd build && ninja", 34 | "cfg": "yarn node src/tools/gen-makefiles.mjs" 35 | }, 36 | "devDependencies": { 37 | "@freik/arduino2proj": "^0.7.6", 38 | "@freik/build-tools": "^3.6.10", 39 | "husky": "^9.0.11", 40 | "prettier": "^3.2.5", 41 | "rimraf": "^5.0.5", 42 | "typescript": "5.4.5" 43 | }, 44 | "packageManager": "yarn@4.2.1", 45 | "dependencies": { 46 | "@freik/typechk": "^0.5.11" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # roll-up make file. It doesn't do dependency checking for anything, 2 | # it's just, perhaps, better than a shell script 3 | 4 | # phony rules cus there are no dependencies 5 | .PHONY: clean laptype lt l ltf lf flaptype laptypef 3 3p three threepiece 3f 3pf threef threepiecef f3 f3p fthree fthreepiece img calc edit test testf ftest tf ft t sd sdf fsd 41 41f f41 6 | 7 | flags=-j 8 | THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST)))) 9 | 10 | # Now the default target 11 | all: laptype threepiece mock img calc edit test 12 | 13 | clean: 14 | -$(MAKE) -C modules/calculator ${flags} clean 15 | -$(MAKE) -C modules/images ${flags} clean 16 | ifeq ($(OS),Windows_NT) 17 | -cmd /c rd /s /q $(subst /,\,${THIS_DIR}out) 18 | -cmd /c mkdir $(subst /,\,${THIS_DIR}out) 19 | else 20 | -rm -rf out 21 | mkdir out 22 | endif 23 | 24 | laptype lt l: 25 | $(MAKE) ${flags} -f make-laptype.mak all compile_commands 26 | 27 | flaptype laptypef lf ltf: 28 | $(MAKE) ${flags} -f make-laptype.mak flash 29 | 30 | threepiece three 3 3p: 31 | $(MAKE) ${flags} -f make-3piece.mak all compile_commands 32 | 33 | fthreepiece fthree f3 f3p threepiecef threef 3f 3pf: 34 | $(MAKE) ${flags} -f make-3piece.mak flash 35 | 36 | test t: 37 | $(MAKE) ${flags} -f make-test.mak all 38 | 39 | testf tf ftest ft: 40 | $(MAKE) ${flags} -f make-test.mak flash 41 | 42 | big bigscreen: 43 | $(MAKE) ${flags} -f make-bigscreen.mak all 44 | 45 | bigf bigscreenf: 46 | $(MAKE) ${flags} -f make-bigscreen.mak flash 47 | 48 | sd: 49 | $(MAKE) ${flags} -f make-sd.mak all 50 | 51 | sdf fds: 52 | $(MAKE) ${flags} -f make-sd.mak flash 53 | 54 | 41: 55 | $(MAKE) ${flags} -f make-41.mak all 56 | 57 | 41f f41: 58 | $(MAKE) ${flags} -f make-41.mak flash 59 | 60 | mock m: 61 | $(MAKE) ${flags} -f make-mock.mak all compile_commands 62 | 63 | rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) 64 | 65 | format: $(call rwildcard,include,*.h) $(wildcard *.cpp) $(wildcard bitmaps/*.cpp) $(wildcard bitmaps/*.h) $(call rwildcard,modules,*.cpp) $(call rwildcard,modules,*.h) 66 | clang-format -i $^ 67 | 68 | img: 69 | $(MAKE) -C modules/images ${flags} 70 | 71 | calc: 72 | $(MAKE) -C modules/calculator ${flags} 73 | 74 | edit: 75 | $(MAKE) -C modules/editline ${flags} 76 | 77 | -------------------------------------------------------------------------------- /src/apple2.mk: -------------------------------------------------------------------------------- 1 | APPLE_MODULE_DIR = libs/apple2/ 2 | USER_INCLUDES += -I${APPLE_MODULE_DIR}inc 3 | USER_CPP_SRCS += $(patsubst ${APPLE_MODULE_DIR}src/%,%,$(wildcard ${APPLE_MODULE_DIR}src/*.cpp)) 4 | VPATH += ${APPLE_MODULE_DIR}src 5 | # By default, optimize the executable. 6 | # CFLAGS := -Wall -flto -Os -ansi -c -std=c++11 7 | # LFLAGS := -flto 8 | 9 | # ifdef DEBUG 10 | # CFLAGS := -O0 -ggdb -ansi -c -finstrument-functions -std=c++11 -Wno-write-strings -Werror 11 | # endif 12 | -------------------------------------------------------------------------------- /src/ardfs.cpp: -------------------------------------------------------------------------------- 1 | #include "ardfs.h" 2 | 3 | namespace ard { 4 | namespace filesystem { 5 | 6 | namespace { 7 | 8 | // File-local stuff 9 | 10 | path cur("/"); 11 | 12 | } // namespace 13 | 14 | path current_path() { 15 | return cur; 16 | } 17 | 18 | void current_path(const path& p) { 19 | cur = p; 20 | } 21 | 22 | bool remove(const ard::filesystem::path& p) { 23 | // TODO 24 | return false; 25 | } 26 | 27 | bool remove(const ard::filesystem::path& p, std::error_code& ec) noexcept { 28 | // TODO 29 | return false; 30 | } 31 | 32 | std::uintmax_t remove_all(const ard::filesystem::path& p) { 33 | return 0; 34 | } 35 | 36 | std::uintmax_t remove_all(const ard::filesystem::path& p, std::error_code& ec) { 37 | return 0; 38 | } 39 | 40 | } // namespace filesystem 41 | } // namespace ard 42 | -------------------------------------------------------------------------------- /src/ardfs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if !defined(__ARDUINO_CPPFILESYSTEM) 3 | #define __ARDUINO_CPPFILESYSTEM 4 | 5 | #include "fs/dir.h" 6 | #include "fs/path.h" 7 | 8 | namespace ard { 9 | namespace filesystem { 10 | 11 | path current_path(); 12 | void current_path(const ard::filesystem::path& p); 13 | 14 | bool remove(const ard::filesystem::path& p); 15 | bool remove(const ard::filesystem::path& p, std::error_code& ec) noexcept; 16 | 17 | std::uintmax_t remove_all(const ard::filesystem::path& p); 18 | std::uintmax_t remove_all(const ard::filesystem::path& p, std::error_code& ec); 19 | 20 | } // namespace filesystem 21 | } // namespace ard 22 | 23 | #endif // __ARDUINO_CPPFILESYSTEM 24 | -------------------------------------------------------------------------------- /src/ardfs_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ardfs.h" 5 | 6 | namespace afs = ard::filesystem; 7 | namespace sfs = std::filesystem; 8 | 9 | // These are *not* extensive, and I should make them actually test afs vs sfs 10 | 11 | int main(void) { 12 | std::string testStr = "a/test/string/"; 13 | 14 | afs::path a1{"/usr/lib"}; 15 | afs::path a2; 16 | afs::path a3{a1}; 17 | afs::path a4{"file"}; 18 | afs::path a5{a1 / a4}; 19 | afs::path a6{++testStr.cbegin(), --testStr.cend()}; 20 | afs::path a7{testStr.substr(1)}; 21 | 22 | sfs::path s1{"/usr/lib"}; 23 | sfs::path s2; 24 | sfs::path s3{s1}; 25 | sfs::path s4{"file"}; 26 | sfs::path s5{s1 / s4}; 27 | sfs::path s6{++testStr.cbegin(), --testStr.cend()}; 28 | sfs::path s7{testStr.substr(1)}; 29 | 30 | std::cout << "Construction:\n" 31 | << "a1 = " << a1 << " should be " << s1 << '\n' 32 | << "a2 = " << a2 << " should be " << s2 << '\n' 33 | << "a3 = " << a3 << " should be " << s3 << '\n' 34 | << "a4 = " << a4 << " should be " << s4 << '\n' 35 | << "a5 = " << a5 << " should be " << s5 << '\n' 36 | << "a6 = " << a6 << " should be " << s6 << '\n' 37 | << "a7 = " << a7 << " should be " << s7 << '\n'; 38 | 39 | a7 = a6; 40 | a6 = a1 / a4; 41 | a5 = testStr.substr(1); 42 | a4.assign(testStr.substr(1, testStr.size() - 2)); 43 | a3.assign(++(++testStr.cbegin()), --(--testStr.cend())); 44 | 45 | s7 = s6; 46 | s6 = s1 / s4; 47 | s5 = testStr.substr(1); 48 | s4.assign(testStr.substr(1, testStr.size() - 2)); 49 | s3.assign(++(++testStr.cbegin()), --(--testStr.cend())); 50 | 51 | std::cout << "operator= & assign:\n" 52 | << "a1 = " << a1 << " should be " << s1 << '\n' 53 | << "a2 = " << a2 << " should be " << s2 << '\n' 54 | << "a3 = " << a3 << " should be " << s3 << '\n' 55 | << "a4 = " << a4 << " should be " << s4 << '\n' 56 | << "a5 = " << a5 << " should be " << s5 << '\n' 57 | << "a6 = " << a6 << " should be " << s6 << '\n' 58 | << "a7 = " << a7 << " should be " << s7 << '\n'; 59 | 60 | a7 = a1; 61 | a7 += a4; 62 | a1 /= testStr; 63 | a2 += testStr; 64 | a4 += a3; 65 | a4 += ".suf"; 66 | 67 | s7 = s1; 68 | s7 += s4; 69 | s1 /= testStr; 70 | s2 += testStr; 71 | s4 += s3; 72 | s4 += ".suf"; 73 | 74 | std::cout << "operator+, /=, +, +=:\n" 75 | << "a1 = " << a1 << " should be " << s1 << '\n' 76 | << "a2 = " << a2 << " should be " << s2 << '\n' 77 | << "a4 = " << a4 << " should be " << s4 << '\n' 78 | << "a7 = " << a7 << " should be " << s7 << '\n'; 79 | 80 | std::cout << "Component tests:\n" 81 | << "Path considered: " << a4 << '\n' 82 | << "Root name: " << a4.root_name() << " should be " 83 | << s4.root_name() << '\n' 84 | << "Root dir: " << a4.root_directory() << " should be " 85 | << s4.root_directory() << '\n' 86 | << "Root path: " << a4.root_path() << " should be " 87 | << s4.root_path() << '\n' 88 | << "Relative path: " << a4.relative_path() << " should be " 89 | << s4.relative_path() << '\n' 90 | << "Parent path: " << a4.parent_path() << " should be " 91 | << s4.parent_path() << '\n' 92 | << "Filename: " << a4.filename() << " should be " 93 | << s4.filename() << '\n' 94 | << "Extension: " << a4.extension() << " should be " 95 | << s4.extension() << '\n' 96 | << "Stem: " << a4.stem() << " should be " << s4.stem() 97 | << '\n'; 98 | 99 | return 0; 100 | } -------------------------------------------------------------------------------- /src/dbgcfg.cpp: -------------------------------------------------------------------------------- 1 | #include "sysstuff.h" 2 | 3 | #include "dbgcfg.h" 4 | 5 | SerialStream Dbg(1); 6 | SerialStream Dbg2(2); 7 | -------------------------------------------------------------------------------- /src/include/adafruit_nrf52/mpu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | 5 | struct AdafruitNRF52 { 6 | static void configOutputPin(uint8_t pin) { 7 | pinMode(pin, OUTPUT); 8 | digitalWrite(pin, HIGH); 9 | } 10 | static void configInputPin(uint8_t pin) { 11 | pinMode(pin, INPUT_PULLUP); 12 | } 13 | static void prepPinForRead(uint8_t pin) { 14 | digitalWrite(pin, LOW); 15 | delayMicroseconds(750); 16 | } 17 | static void completePin(uint8_t pin) { 18 | digitalWrite(pin, HIGH); 19 | } 20 | #if defined(USE_INTERRUPT) 21 | static void prepForInterrupt(uint8_t pin) { 22 | digitalWrite(pin, LOW); 23 | } 24 | static void restoreFromInterrupt(uint8_t pin) { 25 | digitalWrite(pin, HIGH); 26 | } 27 | #endif 28 | }; 29 | -------------------------------------------------------------------------------- /src/include/bigscreen/boardio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "sysstuff.h" 3 | 4 | #include "enumtypes.h" 5 | #include "generalstate.h" 6 | #include "keystate.h" 7 | 8 | struct BoardIO { 9 | // These two are necessary for 3piece because it doesn't 10 | // inhereit from a Matrix 11 | static constexpr uint8_t matrix_size = 72; 12 | typedef uint8_t bits; 13 | 14 | static void Configure(); 15 | static void Changed(uint32_t now, GeneralState&); 16 | static void Tick(uint32_t now); 17 | static KeyboardMode Mode(uint32_t now, KeyboardMode mode); 18 | static void ReturnFromMode(); 19 | static void Reset(GeneralState&); 20 | 21 | static void ShowScanCode(uint16_t sc); 22 | static void SaveLayer(); 23 | }; 24 | 25 | using MatrixBits = BoardIO::bits; 26 | -------------------------------------------------------------------------------- /src/include/bigscreen/hwconfig.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // #define HAS_DISPLAY 1 4 | // #define DISPLAY_ST7789 1 5 | // #define DISPLAY_320_240 1 6 | #define NEEDS_SERIAL 1 7 | #define BIGSCREEN 1 8 | -------------------------------------------------------------------------------- /src/include/bitstuff.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | #include 5 | 6 | template 7 | uint8_t pull_a_bit(std::bitset& bits) { 8 | for (uint8_t i = 0; i < bits.size(); i++) { 9 | if (bits.test(i)) { 10 | bits.reset(i); 11 | return i; 12 | } 13 | } 14 | return 0xFF; 15 | } 16 | 17 | // Given a delta mask, get the scan code, update the delta mask and set pressed 18 | // while we're at it. 19 | template 20 | uint8_t getNextBitSet(std::bitset& delta, 21 | std::bitset& curState, 22 | bool& pressed) { 23 | uint8_t sc = pull_a_bit(delta); 24 | pressed = curState.test(sc); 25 | return sc; 26 | } -------------------------------------------------------------------------------- /src/include/debounce.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "bitstuff.h" 7 | #include "boardio.h" 8 | 9 | template 10 | class Debouncer { 11 | typedef std::bitset BITS; 12 | 13 | private: 14 | // The last set of switches we reported 15 | std::bitset last_reported_switches; 16 | // This is just the set of report times for switches 17 | std::array last_reported_time; 18 | // This is the # of msec to delay after reporting a change before reporting 19 | // another one. Rumor has it that Cherry claims a debounce period of 5ms, but 20 | // I still sometimes see a bounce or two, so I've increased it a bit. 21 | // 25ms translates to a typing speed of about 400 WPM, which seems plenty 22 | // fast... 23 | static const uint8_t debounce_delay = DELAY; 24 | 25 | public: 26 | Debouncer() : last_reported_switches{}, last_reported_time{} {} 27 | void reset() { 28 | last_reported_switches.reset(); 29 | last_reported_time.fill(0); 30 | } 31 | BITS update(BITS cur_switches, uint32_t now) { 32 | // If we've read the same thing we last reported, there's nothing to do 33 | if (last_reported_switches == cur_switches) 34 | return cur_switches; 35 | // This gets us a set of bits that are different between last report & 36 | // the current read 37 | BITS change = last_reported_switches ^ cur_switches; 38 | while (change.any()) { 39 | // pull_a_bit yoinks a bit number and clears it from the bit array 40 | uint8_t bit_num = pull_a_bit(change); 41 | if (bit_num >= cur_switches.size()) 42 | break; 43 | // For each change, check if we're in a debounce period for that switch 44 | if (now - last_reported_time[bit_num] < debounce_delay) { 45 | // Let's clear the change from cur_switches 46 | // If it's on, this will turn it off, if it's off, this will turn it on 47 | cur_switches.flip(bit_num); 48 | Dbg << "Bounce ignored " << bit_num << sfmt::endl; 49 | Dbg << "milleseconds since initial flip: " 50 | << now - last_reported_time[bit_num] << sfmt::endl; 51 | } else { 52 | // We're not in the debounce period: leave the change intact, and start 53 | // the timer 54 | last_reported_time[bit_num] = now; 55 | } 56 | } 57 | // Save off the things we're reporting 58 | last_reported_switches = cur_switches; 59 | return cur_switches; 60 | } 61 | }; 62 | 63 | MatrixBits debounce(MatrixBits cur_switches, uint32_t now); 64 | -------------------------------------------------------------------------------- /src/include/enumtypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | enum class layer_num : uint8_t { 5 | Base = 0, 6 | MacBase = 0, 7 | WinBase = 1, 8 | LinBase = 2, 9 | ValidSaves = 3, 10 | Func = 3, 11 | MacCap = 4, 12 | WinCap = 5, 13 | WinCtl = 6, 14 | LinCap = 7, 15 | Apple = 8, 16 | NumElems = 9 17 | }; 18 | 19 | // This needs to fit in 4 bits 20 | enum class KeyAction : uint8_t { 21 | KeyPress = 0, 22 | Modifier = 1, 23 | TapHold = 2, 24 | Consumer = 3, 25 | KeyAndMods = 4, 26 | LayerShift = 5, 27 | LayerToggle = 6, 28 | LayerSwitch = 7, 29 | Mode = 8, // "mode switch" 30 | Macro = 9, 31 | LayerRotate3 = 10, 32 | // LayerRotate4 (or more) requires 'moreData' in a single blob 33 | LayerRotate4 = 11, 34 | // Could make more LayerRotate actions 35 | // MaxActions = 15 << MUST FIT IN 4 BITS! 36 | }; 37 | 38 | // Layer changing stuff 39 | enum class layer_t : uint8_t { 40 | None = 0, 41 | Push = 1, 42 | Pop = 2, 43 | Toggle = 3, 44 | Switch = 4, 45 | Rotate3 = 5, 46 | Rotate4 = 6 47 | }; 48 | 49 | enum class KeyboardMode : uint8_t { 50 | Normal = 0, 51 | Menu, // To pick from other modes :D 52 | Calculator, // Done 53 | Tetris, // This is close enough 54 | Snake, // Definitely going to code this at some point 55 | BubbleBreaker, // Maybe 56 | Bejeweled, // Maybe 57 | MediaBrowse, // Sure, why not? 58 | Apple2, // Eventually :) 59 | Diagnostics, // Maybe? 60 | NumElems 61 | }; 62 | 63 | enum class ModuleId : uint8_t { 64 | // These are all the random modules that are available 65 | LineEditor, 66 | Display, // Maybe different displays? 67 | Calculator, 68 | Image, 69 | Menu, 70 | Snake, 71 | Tetris 72 | }; 73 | -------------------------------------------------------------------------------- /src/include/kbreporter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | #include "usbenums.h" 5 | 6 | class kb_reporter { 7 | // This is used by the scanner to build & send the key press signals 8 | uint8_t mods; 9 | uint8_t repsize; 10 | uint8_t report[6]; 11 | 12 | public: 13 | kb_reporter(); 14 | void set_modifier(Modifiers); 15 | void add_key_press(Keystroke); 16 | void consumer_press(Consumer); 17 | void consumer_release(Consumer); 18 | void send_keys(); 19 | }; 20 | -------------------------------------------------------------------------------- /src/include/keystate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "action.h" 4 | #include "dbgcfg.h" 5 | #include "enumtypes.h" 6 | 7 | using scancode_t = uint8_t; 8 | constexpr scancode_t null_scan_code = 0xff; 9 | constexpr uint16_t null_consumer_code = 0x8000; 10 | action_t resolveActionForScanCodeOnActiveLayer(uint8_t scanCode); 11 | 12 | struct keystate { 13 | // The time this keystate was changed (using millis()) 14 | uint32_t lastChange; 15 | // The action this key state is referring to. 16 | // This comes from the keymap when the key is pressed. 17 | action_t action = no_action; 18 | // The scan code of the key this action is about 19 | scancode_t scanCode; 20 | // Is this a press or release? 21 | bool down; 22 | 23 | layer_num get_layer() const { 24 | return action.getLayer(); 25 | } 26 | layer_num get_layer1() const { 27 | return action.getLayer1(); 28 | } 29 | layer_num get_layer2() const { 30 | return action.getLayer2(); 31 | } 32 | layer_num get_layer3() const { 33 | return action.getLayer3(); 34 | } 35 | layer_num get_layer4() const { 36 | return action.getLayer4(); 37 | } 38 | 39 | layer_t update(scancode_t sc, bool pressed, uint32_t now) { 40 | if (scanCode == sc) { 41 | // Update the transition time, if any 42 | if (down != pressed) { 43 | lastChange = now; 44 | down = pressed; 45 | if (pressed) { 46 | action = resolveActionForScanCodeOnActiveLayer(scanCode); 47 | } 48 | } 49 | Dbg2 << "Updated transition time for scancode " << sc << sfmt::endl; 50 | } else { 51 | // We claimed a new slot, so set the transition 52 | // time to the current time. 53 | down = pressed; 54 | scanCode = sc; 55 | lastChange = now; 56 | if (pressed) { 57 | action = resolveActionForScanCodeOnActiveLayer(scanCode); 58 | } else { 59 | action = no_action; 60 | } 61 | Dbg2 << "Set lastChange for new scancode " << sc << sfmt::endl; 62 | } 63 | Dbg2 << (pressed ? "(Pressed)" : "(Released)"); 64 | switch (action.getAction()) { 65 | case KeyAction::LayerShift: 66 | return down ? layer_t::Push : layer_t::Pop; 67 | case KeyAction::LayerToggle: 68 | return down ? layer_t::Toggle : layer_t::None; 69 | case KeyAction::LayerSwitch: 70 | return down ? layer_t::Switch : layer_t::None; 71 | case KeyAction::LayerRotate3: 72 | return down ? layer_t::Rotate3 : layer_t::None; 73 | case KeyAction::LayerRotate4: 74 | return down ? layer_t::Rotate4 : layer_t::None; 75 | default: 76 | return layer_t::None; 77 | } 78 | }; 79 | }; 80 | 81 | constexpr keystate empty_keystate = {0, no_action, null_scan_code, false}; 82 | 83 | inline SerialStream& operator<<(SerialStream& s, const keystate& ks) { 84 | s << "ScanCode=" << sfmt::hex << ks.scanCode << " down=" << ks.down 85 | << " lastChange=" << ks.lastChange << " action=" << ks.action << sfmt::endl; 86 | return s; 87 | } 88 | -------------------------------------------------------------------------------- /src/include/laptype/boardio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "enumtypes.h" 4 | #include "generalstate.h" 5 | #include "keymatrix.h" 6 | #include "keystate.h" 7 | #include "mpu.h" 8 | 9 | // clang-format off 10 | using LaptypeMatrix = KeyMatrix; 18 | // clang-format on 19 | 20 | struct BoardIO : public LaptypeMatrix { 21 | static void Configure(); 22 | static void Changed(uint32_t now, GeneralState& state); 23 | static void Tick(uint32_t now); 24 | static KeyboardMode Mode(uint32_t now, KeyboardMode mode); 25 | static void Reset(GeneralState&); 26 | static void ReturnFromMode(); 27 | 28 | static void ShowScanCode(uint16_t sc); 29 | static void SaveLayer(uint32_t now); 30 | }; 31 | 32 | using MatrixBits = LaptypeMatrix::bits; -------------------------------------------------------------------------------- /src/include/laptype/hwconfig.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define HAS_DISPLAY 1 4 | #define DISPLAY_ST7789 1 5 | #define DISPLAY_240_135 1 6 | #define LAPTYPE 1 7 | -------------------------------------------------------------------------------- /src/include/localscan/scanner.h: -------------------------------------------------------------------------------- 1 | #include "boardio.h" 2 | #include "debounce.h" 3 | 4 | class Scanner { 5 | MatrixBits before; 6 | MatrixBits after; 7 | MatrixBits delta; 8 | 9 | // Scanner-specific global state 10 | static MatrixBits prevBits; 11 | static Debouncer debouncer; 12 | // Helper functions 13 | static MatrixBits key_scan(uint32_t now); 14 | 15 | public: 16 | // This is the interface that needs filled in: 17 | 18 | // Called during setup, and maybe in response to something else? 19 | // It should reset all global state 20 | static void Reset(); 21 | 22 | // Constructs the scan-loop-invoke local state for this time through 23 | Scanner(uint32_t now); 24 | // Gets the next scan code to be (pre)processed 25 | scancode_t getNextCode(bool& pressed); 26 | // Called once we've completed processing the keys (prep for next loop) 27 | void Done(); 28 | }; -------------------------------------------------------------------------------- /src/include/mock/boardio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "enumtypes.h" 4 | #include "generalstate.h" 5 | #include "keymatrix.h" 6 | #include "keystate.h" 7 | #include "mpu.h" 8 | 9 | // clang-format off 10 | using MockMatrix = KeyMatrix; 18 | // clang-format on 19 | 20 | struct BoardIO : public MockMatrix { 21 | static void Configure(); 22 | static void Reset(GeneralState&); 23 | static void Changed(uint32_t now, GeneralState&); 24 | static void Tick(uint32_t now); 25 | static KeyboardMode Mode(uint32_t now, KeyboardMode mode); 26 | static void ReturnFromMode(); 27 | 28 | static void ShowScanCode(uint16_t sc); 29 | }; 30 | 31 | constexpr uint8_t BAD_PIN = 0xFF; 32 | constexpr uint8_t pinToCol[24] = { 33 | 0xFF, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 34 | 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 35 | 36 | constexpr uint8_t pinToRow[24] = { 37 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 38 | 0xFF, 0, 1, 2, 3, 4, 5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 39 | 40 | using MatrixBits = MockMatrix::bits; 41 | -------------------------------------------------------------------------------- /src/include/mock/hwconfig.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define MOCK 1 4 | -------------------------------------------------------------------------------- /src/include/mock/mpu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | 5 | struct MockMPU { 6 | // This configuration make sit so the Teensy LED (on pin 13) 7 | // doesn't stay lit 99.999% of the time... 8 | static void configOutputPin(uint8_t pin) { 9 | pinMode(pin, INPUT); 10 | } 11 | static void configInputPin(uint8_t pin) { 12 | pinMode(pin, INPUT_PULLUP); 13 | } 14 | static void prepPinForRead(uint8_t pin) { 15 | pinMode(pin, OUTPUT); 16 | digitalWrite(pin, LOW); 17 | delayMicroseconds(250); 18 | } 19 | static void completePin(uint8_t pin) { 20 | pinMode(pin, INPUT); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/include/mock/mpusys.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "mock.h" 6 | -------------------------------------------------------------------------------- /src/include/mock/scanner.h: -------------------------------------------------------------------------------- 1 | #include "keystate.h" 2 | 3 | class Scanner { 4 | 5 | public: 6 | // This is the interface that needs filled in: 7 | 8 | // Called during setup, and maybe in response to something else? 9 | // It should reset all global state 10 | static void Reset(); 11 | 12 | // Constructs the scan-loop-invoke local state for this time through 13 | Scanner(uint32_t now); 14 | // Gets the next scan code to be (pre)processed 15 | scancode_t getNextCode(bool& pressed); 16 | // Called once we've completed processing the keys (prep for next loop) 17 | void Done(); 18 | }; -------------------------------------------------------------------------------- /src/include/moduleLoaderTest.cpp: -------------------------------------------------------------------------------- 1 | #include "newModule.h" 2 | #include 3 | 4 | class FakeDisplay; 5 | class FakeCalculator; 6 | class FakeLineEditor; 7 | 8 | class FakeDisplay : public Registerable { 9 | bool ready; 10 | 11 | public: 12 | FakeDisplay() : ready(false), Registerable(ModuleId::Display, {}) { 13 | Constructed(); 14 | } 15 | virtual void RegisterDependency(Registerable* r) { 16 | // Never should be reached... 17 | std::cerr << "FAIL!" << std::endl; 18 | } 19 | virtual void AllDependenciesMet() { 20 | if (ready) { 21 | std::cerr << "Already ready!" << std::endl; 22 | } else { 23 | ready = true; 24 | } 25 | } 26 | }; 27 | 28 | class FakeLineEditor : public Registerable { 29 | FakeDisplay* disp; 30 | bool ready; 31 | 32 | public: 33 | FakeLineEditor() 34 | : disp(nullptr), 35 | ready(false), 36 | Registerable(ModuleId::LineEditor, {ModuleId::Display}) { 37 | Constructed(); 38 | } 39 | virtual void RegisterDependency(Registerable* r) { 40 | if (r->ID() != ModuleId::Display) { 41 | std::cerr << "Failed in dep registration: " << static_cast(r->ID()) 42 | << std::endl; 43 | } else if (disp != nullptr) { 44 | std::cerr << "Looks like the calculator dependency is already registered!" 45 | << std::endl; 46 | } else { 47 | disp = static_cast(r); 48 | } 49 | } 50 | virtual void AllDependenciesMet() { 51 | if (disp == nullptr) { 52 | std::cerr << "Display dependency not met, but claimed to have been." 53 | << std::endl; 54 | } else if (ready) { 55 | std::cerr << "Already dep-met, but triggered again..." << std::endl; 56 | } else { 57 | ready = true; 58 | } 59 | } 60 | }; 61 | 62 | class FakeCalculator : public Registerable { 63 | FakeLineEditor* edit; 64 | FakeDisplay* disp; 65 | bool ready; 66 | 67 | public: 68 | FakeCalculator() 69 | : edit(nullptr), 70 | disp(nullptr), 71 | ready(false), 72 | Registerable(ModuleId::Calculator, 73 | {ModuleId::Display, ModuleId::LineEditor}) { 74 | Constructed(); 75 | } 76 | virtual void RegisterDependency(Registerable* r) { 77 | switch (r->ID()) { 78 | case ModuleId::Display: 79 | if (disp != nullptr) { 80 | std::cerr 81 | << "Looks like the calculator dependency is already registered!" 82 | << std::endl; 83 | } else { 84 | disp = static_cast(r); 85 | } 86 | break; 87 | case ModuleId::LineEditor: 88 | if (edit != nullptr) { 89 | std::cerr 90 | << "Looks like the calculator dependency is already registered!" 91 | << std::endl; 92 | } else { 93 | edit = static_cast(r); 94 | } 95 | break; 96 | default: 97 | std::cerr << "Failed in dep registration: " << static_cast(r->ID()) 98 | << std::endl; 99 | } 100 | } 101 | virtual void AllDependenciesMet() { 102 | if (disp == nullptr) { 103 | std::cerr << "Display dependency not met, but claimed to have been." 104 | << std::endl; 105 | } else if (ready) { 106 | std::cerr << "Already dep-met, but triggered again..." << std::endl; 107 | } else { 108 | ready = true; 109 | } 110 | } 111 | }; 112 | 113 | FakeLineEditor lineEditor; 114 | FakeDisplay fakeDisplay; 115 | FakeCalculator fakeCalculator; 116 | -------------------------------------------------------------------------------- /src/include/modulekeyboard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "action.h" 3 | #include "enumtypes.h" 4 | #include "usbenums.h" 5 | #include 6 | 7 | typedef KeyboardMode (*KeystrokeHandler)(Keystroke ks, 8 | Modifiers m, 9 | bool pressed, 10 | uint32_t now); 11 | typedef KeyboardMode (*Spinner)(KeyboardMode mode, uint32_t now); 12 | KeyboardMode ModuleKeyboardHandler(KeyboardMode curMode, 13 | KeystrokeHandler handler, 14 | Spinner spin = nullptr); 15 | -------------------------------------------------------------------------------- /src/include/newActionResult.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if !defined(_GUARD_ACTIONRESULT_H) 3 | #define _GUARD_ACTIONRESULT_H 4 | 5 | #include "newKeyState.h" 6 | #include "newKeyboardInput.h" 7 | #include "newSystemState.h" 8 | 9 | struct ActionResult { 10 | KeyboardInput input; 11 | SystemState state; 12 | static ActionResult determineAction(const KeyState&, const SystemState&) { 13 | // TODO 14 | return ActionResult{}; 15 | } 16 | }; 17 | 18 | #endif -------------------------------------------------------------------------------- /src/include/newKeyState.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if !defined(_GUARD_KEYSTATE_H) 3 | #define _GUARD_KEYSTATE_H 4 | 5 | class KeyState { 6 | public: 7 | static KeyState acquire() { 8 | // TODO 9 | return KeyState{}; 10 | } 11 | }; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/include/newKeyboardInput.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if !defined(_GUARD_KEYBOARDINPUT_H) 3 | #define _GUARD_KEYBOARDINPUT_H 4 | 5 | class KeyboardInput { 6 | public: 7 | bool needsReported() const { 8 | // TODO 9 | return true; 10 | } 11 | }; 12 | 13 | #endif -------------------------------------------------------------------------------- /src/include/newModule.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if !defined(_GUARD_MODULE_H) 3 | #define _GUARD_MODULE_H 4 | 5 | #include 6 | #include 7 | 8 | #include "enumtypes.h" 9 | 10 | /* 11 | I want all built modules to be able to register what they depend on, 12 | and then get initialized *after* their dependencies are intialized... 13 | */ 14 | 15 | class Registerable { 16 | private: 17 | static std::map completedDepencies; 18 | static std::multimap waitingForDependency; 19 | uint8_t numUnmetDeps; 20 | const ModuleId name; 21 | const std::forward_list dependencies; 22 | 23 | // a recursive demand-based dependency linker, basically? 24 | void RegistrationComplete() { 25 | Registerable::completedDepencies.insert(std::make_pair(name, this)); 26 | for (auto deps = Registerable::waitingForDependency.find(name); 27 | deps != Registerable::waitingForDependency.end() && 28 | deps->first == name; 29 | deps++) { 30 | deps->second->RegisterDependency(this); 31 | deps->second->numUnmetDeps--; 32 | if (deps->second->numUnmetDeps == 0) { 33 | deps->second->RegistrationComplete(); 34 | } 35 | } 36 | Registerable::waitingForDependency.erase(name); 37 | AllDependenciesMet(); 38 | } 39 | 40 | protected: 41 | Registerable(ModuleId n, std::initializer_list i) 42 | : name(n), dependencies(i) { 43 | // You need to invoke the 'Constructed' method in the child constructor, 44 | // otherwise, registration *may* occur before your object is full 45 | // constructed 46 | } 47 | // Demand-based dependency walking, hopefully 48 | void Constructed() { 49 | uint8_t unmet = 0; 50 | for (ModuleId nextDep : dependencies) { 51 | auto dep = Registerable::completedDepencies.find(nextDep); 52 | if (dep == Registerable::completedDepencies.end() || 53 | dep->first != nextDep) { 54 | unmet++; 55 | Registerable::waitingForDependency.insert( 56 | std::make_pair(nextDep, this)); 57 | } else { 58 | RegisterDependency(dep->second); 59 | } 60 | } 61 | numUnmetDeps = unmet; 62 | if (unmet == 0) { 63 | RegistrationComplete(); 64 | } 65 | } 66 | // This is where you do stuff with your dependency 67 | // It *could* get invoked during "Constructed" 68 | // or it could be invoked much later 69 | virtual void RegisterDependency(Registerable* r) = 0; 70 | // This is only called once all the deps of your class are met 71 | virtual void AllDependenciesMet() = 0; 72 | 73 | public: 74 | ModuleId ID() const { 75 | return name; 76 | } 77 | }; 78 | 79 | #endif -------------------------------------------------------------------------------- /src/include/newSystemState.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if !defined(_GUARD_SYSTEMSTATE_H) 3 | #define _GUARD_SYSTEMSTATE_H 4 | 5 | #include "enumtypes.h" 6 | #include 7 | #include 8 | 9 | // TODO: Fix these, as they aren't correct 10 | constexpr uint8_t num_switches = 76; 11 | constexpr uint8_t max_layers = 16; 12 | 13 | using LayerStates = std::array; 14 | using SwitchStates = std::array; 15 | 16 | class SystemState { 17 | uint32_t curMillis; // Time, for lack of anything better I guess 18 | uint8_t capslock : 1; // LED's and what-not 19 | uint8_t numlock : 1; 20 | uint8_t scrollLock : 1; 21 | KeyboardMode mode; // Current 'mode' 22 | LayerStates layers; 23 | SwitchStates switches; 24 | 25 | public: 26 | static SystemState makeFreshState() { 27 | // TODO 28 | return SystemState{}; 29 | } 30 | SystemState executeMode() const { 31 | // TODO 32 | return *this; 33 | } 34 | bool isNormalMode() const { 35 | return mode == KeyboardMode::Normal; 36 | } 37 | }; 38 | 39 | #endif -------------------------------------------------------------------------------- /src/include/remotescan/scanner.h: -------------------------------------------------------------------------------- 1 | #include "keystate.h" 2 | 3 | class Scanner { 4 | 5 | public: 6 | // This is the interface that needs filled in: 7 | 8 | // Called during setup, and maybe in response to something else? 9 | // It should reset all global state 10 | static void Reset(); 11 | 12 | // Constructs the scan-loop-invoke local state for this time through 13 | Scanner(uint32_t now); 14 | // Gets the next scan code to be (pre)processed 15 | scancode_t getNextCode(bool& pressed); 16 | // Called once we've completed processing the keys (prep for next loop) 17 | void Done(); 18 | }; -------------------------------------------------------------------------------- /src/include/scanning.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | #include 5 | 6 | #include "enumtypes.h" 7 | #include "kbreporter.h" 8 | #include "keystate.h" 9 | 10 | // variable declarations 11 | constexpr uint8_t num_keystates = 10; 12 | extern keystate keyStates[num_keystates]; 13 | 14 | // Functions 15 | void layer_switch(layer_t layer); 16 | void layer_toggle(layer_t layer); 17 | void layer_pop(layer_t layer); 18 | void layer_push(layer_t layer); 19 | uint32_t getColorForCurrentLayer(); 20 | layer_num getCurrentLayer(); 21 | keystate* findStateSlot(scancode_t scanCode); 22 | void preprocessScanCode(scancode_t sc, bool pressed, uint32_t now); 23 | KeyboardMode ProcessKeys(uint32_t now, kb_reporter& rpt); 24 | -------------------------------------------------------------------------------- /src/include/sysstuff.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define _CRT_SECURE_NO_WARNINGS 3 | 4 | #include "hwconfig.h" 5 | #include "mpusys.h" 6 | -------------------------------------------------------------------------------- /src/include/teensy/mpu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | 5 | // Bit 1/1 == num lock, Bit 2/2 == caps lock, Bit 3/4 == scroll lock 6 | extern volatile uint8_t keyboard_leds; 7 | struct Teensy { 8 | // This configuration make sit so the Teensy LED (on pin 13) 9 | // doesn't stay lit 99.999% of the time... 10 | static void configOutputPin(uint8_t pin) { 11 | pinMode(pin, INPUT); 12 | } 13 | static void configInputPin(uint8_t pin) { 14 | pinMode(pin, INPUT_PULLUP); 15 | } 16 | static void prepPinForRead(uint8_t pin) { 17 | pinMode(pin, OUTPUT); 18 | digitalWrite(pin, LOW); 19 | delayMicroseconds(250); 20 | } 21 | static void completePin(uint8_t pin) { 22 | pinMode(pin, INPUT); 23 | } 24 | static bool isNumLocked() { 25 | return keyboard_leds & 1; 26 | } 27 | static bool isCapsLocked() { 28 | return keyboard_leds & 2; 29 | } 30 | static bool isScrollLocked() { 31 | return keyboard_leds & 4; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/include/teensy/mpusys.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "core_pins.h" 4 | #include "usb_keyboard.h" 5 | #include "usb_serial.h" 6 | #include 7 | 8 | #if defined(HAS_DISPLAY) 9 | #include 10 | #if defined(DISPLAY_OLED) 11 | #include 12 | #elif defined(DISPLAY_ST7789) 13 | #include 14 | #else 15 | #error Unrecognized display 16 | #endif 17 | #define NEEDS_SPI 18 | #endif 19 | 20 | #if defined(NEEDS_SPI) 21 | #include 22 | #include 23 | #endif 24 | 25 | #if defined(NEEDS_SERIAL) 26 | #include 27 | #endif 28 | 29 | #define TEENSY 1 30 | -------------------------------------------------------------------------------- /src/include/threepiece/boardio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "sysstuff.h" 3 | 4 | #include "enumtypes.h" 5 | #include "generalstate.h" 6 | #include "keystate.h" 7 | 8 | struct BoardIO { 9 | // These two are necessary for 3piece because it doesn't 10 | // inhereit from a Matrix 11 | static constexpr uint8_t matrix_size = 72; 12 | typedef uint8_t bits; 13 | 14 | static void Configure(); 15 | static void Changed(uint32_t now, GeneralState&); 16 | static void Tick(uint32_t now); 17 | static KeyboardMode Mode(uint32_t now, KeyboardMode mode); 18 | static void ReturnFromMode(); 19 | static void Reset(GeneralState&); 20 | 21 | static void ShowScanCode(uint16_t sc); 22 | static void SaveLayer(); 23 | }; 24 | 25 | using MatrixBits = BoardIO::bits; 26 | -------------------------------------------------------------------------------- /src/include/threepiece/hwconfig.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define HAS_DISPLAY 1 4 | #define DISPLAY_ST7789 1 5 | #define DISPLAY_320_240 1 6 | #define NEEDS_SERIAL 1 7 | #define THREEPIECE 1 8 | -------------------------------------------------------------------------------- /src/kbreporter.cpp: -------------------------------------------------------------------------------- 1 | #include "sysstuff.h" 2 | 3 | #include "kbreporter.h" 4 | 5 | #if defined(ADAFRUIT) 6 | 7 | #include "dongle.h" 8 | 9 | kb_reporter::kb_reporter() : mods(0), repsize(0) { 10 | std::fill(std::begin(report), std::end(report), 0); 11 | } 12 | 13 | void kb_reporter::set_modifier(uint8_t m) { 14 | mods = m; 15 | } 16 | 17 | void kb_reporter::add_key_press(uint8_t key) { 18 | if (repsize < 6) 19 | report[repsize++] = key; 20 | } 21 | 22 | void kb_reporter::consumer_press(uint16_t key) { 23 | Dongle::ConsumerPress(key); 24 | } 25 | 26 | void kb_reporter::consumer_release(uint16_t) { 27 | Dongle::ConsumerRelease(); 28 | } 29 | 30 | void kb_reporter::send_keys() { 31 | Dongle::ReportKeys(mods, &report[0]); 32 | } 33 | 34 | #elif defined(TEENSY) || defined(MOCK) 35 | 36 | kb_reporter::kb_reporter() : mods(0), repsize(0) { 37 | std::fill(std::begin(report), std::end(report), 0); 38 | } 39 | 40 | void kb_reporter::set_modifier(Modifiers mod) { 41 | mods = getUSBCode(mod); 42 | } 43 | 44 | void kb_reporter::add_key_press(Keystroke key) { 45 | if (repsize < 6) { 46 | report[repsize++] = getUSBCode(key); 47 | } 48 | } 49 | 50 | void kb_reporter::consumer_press(Consumer kc) { 51 | // Handle these things in real-time 52 | Keyboard.press(getConsumerUSBCode(kc)); 53 | } 54 | 55 | void kb_reporter::consumer_release(Consumer kc) { 56 | // Handle these things in real-time 57 | Keyboard.release(getConsumerUSBCode(kc)); 58 | } 59 | 60 | void kb_reporter::send_keys() { 61 | Keyboard.set_key1(report[0]); 62 | Keyboard.set_key2(report[1]); 63 | Keyboard.set_key3(report[2]); 64 | Keyboard.set_key4(report[3]); 65 | Keyboard.set_key5(report[4]); 66 | Keyboard.set_key6(report[5]); 67 | Keyboard.set_modifier(mods); 68 | Keyboard.send_now(); 69 | } 70 | #endif 71 | -------------------------------------------------------------------------------- /src/localscan.cpp: -------------------------------------------------------------------------------- 1 | #include "scanner.h" 2 | 3 | MatrixBits Scanner::prevBits{0}; 4 | Debouncer Scanner::debouncer{}; 5 | 6 | MatrixBits Scanner::key_scan(uint32_t now) { 7 | auto res = BoardIO::Read(); 8 | return Scanner::debouncer.update(res, now); 9 | } 10 | 11 | void Scanner::Reset() { 12 | Scanner::prevBits.reset(); 13 | Scanner::debouncer.reset(); 14 | } 15 | 16 | Scanner::Scanner(uint32_t tm) 17 | // Get the before & after of each side into a 64 bit value 18 | : before{Scanner::prevBits}, after{key_scan(tm)}, delta{0} { 19 | // Calculate the delta 20 | delta = before ^ after; 21 | } 22 | 23 | scancode_t Scanner::getNextCode(bool& pressed) { 24 | if (!delta.any()) { 25 | return null_scan_code; 26 | } 27 | return getNextBitSet(delta, after, pressed); 28 | } 29 | 30 | void Scanner::Done() { 31 | Scanner::prevBits = after; 32 | } 33 | -------------------------------------------------------------------------------- /src/mainloop.cpp: -------------------------------------------------------------------------------- 1 | #include "sysstuff.h" 2 | 3 | #include "boardio.h" 4 | #include "dbgcfg.h" 5 | #include "generalstate.h" 6 | #include "keystate.h" 7 | #include "scanner.h" 8 | #include "scanning.h" 9 | 10 | GeneralState curState{}; 11 | 12 | // This is called when the system is initialized. 13 | // The idea is that it should just wipe everything clean. 14 | void resetTheWorld() { 15 | Dbg2 << "Resetting the world!" << sfmt::endl; 16 | curState.reset(); 17 | std::fill(std::begin(keyStates), std::end(keyStates), empty_keystate); 18 | Scanner::Reset(); 19 | Dbg2 << "World reset!" << sfmt::endl; 20 | BoardIO::Reset(curState); 21 | } 22 | 23 | extern "C" void setup() { 24 | // Wait for 2 seconds in case we are trying to attach a serial monitor 25 | DBG(for (uint16_t iter = 0; !Serial && iter < 2000; iter++) delay(1);); 26 | DBG(Serial.begin(115200)); 27 | Dbg << "SETUP!" << sfmt::endl; 28 | BoardIO::Configure(); 29 | resetTheWorld(); 30 | } 31 | 32 | extern "C" void loop() { 33 | bool keysChanged = false, pressed = false; 34 | uint32_t now = millis(); 35 | Scanner scanner{now}; 36 | for (scancode_t sc = scanner.getNextCode(pressed); sc != 0xFF; 37 | sc = scanner.getNextCode(pressed)) { 38 | Dbg2 << "Got scan code 0x" << sfmt::hex << sc << sfmt::endl; 39 | preprocessScanCode(sc, pressed, now); 40 | keysChanged = true; 41 | } 42 | KeyboardMode mode = KeyboardMode::Normal; 43 | if (keysChanged) { 44 | kb_reporter rpt; 45 | mode = ProcessKeys(now, rpt); 46 | BoardIO::Changed(now, curState); 47 | } 48 | scanner.Done(); 49 | BoardIO::Tick(now); 50 | while (mode != KeyboardMode::Normal) { 51 | auto newmode = BoardIO::Mode(now, mode); 52 | Dbg2 << "Mode handler returned " << newmode; 53 | // If we're going back to normal mode 54 | // Just reset the world: it's easier this way... 55 | if (newmode != mode) { 56 | resetTheWorld(); 57 | BoardIO::ReturnFromMode(); 58 | } 59 | mode = newmode; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/make-3piece.mak: -------------------------------------------------------------------------------- 1 | # Some simple details 2 | ifeq ($(OS),Windows_NT) 3 | SUF=win 4 | ARD=${HOME}/AppData/Local 5 | SERIAL_PORT=COM15 6 | RUNTIME_HARDWARE_PATH=c:/PROGRA~2/Arduino/hardware/tools 7 | CMD_PATH=${RUNTIME_HARDWARE_PATH} 8 | BISON=bison 9 | else ifeq ($(shell uname -s), Darwin) 10 | SUF=mac 11 | ARD=/Applications/Teensyduino.app/Contents/Java/hardware 12 | SERIAL_PORT=$(wildcard /dev/cu.usb*401) 13 | TOOLS_PATH=${ARD}/tools 14 | RUNTIME_HARDWARE_PATH=${TOOLS_PATH} 15 | CMD_PATH=${TOOLS_PATH} 16 | ifeq ($(shell uname -p), i386) 17 | BISON=/usr/local/opt/bison/bin/bison 18 | else 19 | BISON=/opt/homebrew/opt/bison/bin/bison 20 | endif 21 | else ifeq ($(shell uname -s), Linux) 22 | SUF=lin 23 | ARD=${HOME}/Apps/arduino-1.8.19/hardware 24 | # grab the *last* serial port we see 25 | SERIAL_PORT=$(shell ls /dev/ttyACM* | tail -1) 26 | TOOLS_PATH=${ARD}/tools 27 | RUNTIME_HARDWARE_PATH=${TOOLS_PATH} 28 | CMD_PATH=${TOOLS_PATH} 29 | BISON=/bin/bison 30 | else 31 | $(error No idea what platform you're running on...) 32 | endif 33 | 34 | # I'm going to run an Apple II emulator on this "soon" so just run it at 600MHz 35 | # I could also pump it up to 720 or 816, but i don't want to slowly destroy the 36 | # cpu either. We'll see how well it runs at 600MHz first. 37 | IN_SPEED=600 38 | IN_USB=serialhid 39 | IN_OPT=osstd 40 | IN_KEYS=en-us 41 | EXTRA_TIME_LOCAL=0 42 | BOARD_NAME=teensy40 43 | SERIAL_PORT_LABEL=${SERIAL_PORT} 44 | SERIAL_PORT_PROTOCOL=serial 45 | PROJ_NAME=threepiece 46 | BUILD_PATH=out/threepiece 47 | 48 | # My custom flags 49 | COMPILER_CPP_EXTRA_FLAGS=-DDEBUG=2 50 | # This causes link errors now :'( 51 | # -flto 52 | 53 | # Libraries to use: 54 | LIB_BUSIO=1 55 | LIB_SPI=1 56 | LIB_GFX=1 57 | LIB_ST77XX=1 58 | LIB_WIRE=1 59 | LIB_EEPROM=1 60 | LIB_SDFAT=1 61 | LIB_T4_PXP=1 62 | LIB_GFX_BUFFER=1 63 | 64 | USER_INCLUDES=-Iinclude/threepiece -Iinclude/remotescan -Iinclude/teensy -Iinclude 65 | 66 | USER_CPP_SRCS=\ 67 | dbgcfg.cpp \ 68 | kbreporter.cpp \ 69 | mainloop.cpp \ 70 | scanning.cpp \ 71 | remotescan.cpp \ 72 | modulekeyboard.cpp \ 73 | threepiece.cpp 74 | 75 | include modules/display/include.mk 76 | include modules/calculator/include.mk 77 | include modules/images/include.mk 78 | include modules/editline/include.mk 79 | include modules/tetris/maketetris.mk 80 | include modules/menu/include.mk 81 | include apple2.mk 82 | 83 | include tools/teensy.${SUF} 84 | 85 | -------------------------------------------------------------------------------- /src/make-41.mak: -------------------------------------------------------------------------------- 1 | # Some simple details 2 | ifeq ($(OS),Windows_NT) 3 | SUF=win 4 | ARD=${HOME}/AppData/Local 5 | SERIAL_PORT=COM4 6 | RUNTIME_HARDWARE_PATH=c:/PROGRA~2/Arduino/hardware/tools 7 | CMD_PATH=${RUNTIME_HARDWARE_PATH} 8 | else ifeq ($(shell uname -s), Darwin) 9 | SUF=mac 10 | ARD=/Applications/Teensyduino.app/Contents/Java/hardware 11 | SERIAL_PORT=$(shell ls /dev/cu.usbmodem*401) 12 | TOOLS_PATH=${ARD}/tools 13 | RUNTIME_HARDWARE_PATH=${TOOLS_PATH} 14 | CMD_PATH=${TOOLS_PATH} 15 | else 16 | $(error No Linux support yet, but copying the Darwin stuff should be nearly all of the work) 17 | endif 18 | 19 | IN_SPEED=600 20 | IN_USB=serialhid 21 | IN_OPT=osstd 22 | IN_KEYS=en-us 23 | EXTRA_TIME_LOCAL=0 24 | BOARD_NAME=teensy41 25 | SERIAL_PORT_LABEL=${SERIAL_PORT} 26 | SERIAL_PORT_PROTOCOL=serial 27 | PROJ_NAME=sd 28 | BUILD_PATH=out/sd 29 | 30 | # My custom flags 31 | COMPILER_CPP_EXTRA_FLAGS= 32 | # This causes link errors now :'( 33 | # -flto 34 | 35 | # Libraries to use: 36 | LIB_BUSIO=1 37 | LIB_SPI=1 38 | LIB_GFX=1 39 | LIB_8875=1 40 | LIB_WIRE=1 41 | LIB_SD=1 42 | LIB_SDFAT=1 43 | 44 | USER_INCLUDES=-Iinclude 45 | 46 | USER_CPP_SRCS=\ 47 | test_41.cpp 48 | 49 | include tools/teensy.${SUF} 50 | -------------------------------------------------------------------------------- /src/make-bigscreen.mak: -------------------------------------------------------------------------------- 1 | # Some simple details 2 | ifeq ($(OS),Windows_NT) 3 | SUF=win 4 | ARD=${HOME}/AppData/Local 5 | SERIAL_PORT=COM4 6 | RUNTIME_HARDWARE_PATH=c:/PROGRA~2/Arduino/hardware/tools 7 | CMD_PATH=${RUNTIME_HARDWARE_PATH} 8 | BISON=bison 9 | else ifeq ($(shell uname -s), Darwin) 10 | SUF=mac 11 | ARD=/Applications/Teensyduino.app/Contents/Java/hardware 12 | SERIAL_PORT=$(wildcard /dev/cu.usb*401) 13 | TOOLS_PATH=${ARD}/tools 14 | RUNTIME_HARDWARE_PATH=${TOOLS_PATH} 15 | CMD_PATH=${TOOLS_PATH} 16 | ifeq ($(shell uname -p), i386) 17 | BISON=/usr/local/opt/bison/bin/bison 18 | else 19 | BISON=/opt/homebrew/opt/bison/bin/bison 20 | endif 21 | else ifeq ($(shell uname -s), Linux) 22 | SUF=lin 23 | ARD=${HOME}/Apps/arduino-1.8.19/hardware 24 | # grab the *last* serial port we see 25 | SERIAL_PORT=$(shell ls /dev/ttyACM* | tail -1) 26 | TOOLS_PATH=${ARD}/tools 27 | RUNTIME_HARDWARE_PATH=${TOOLS_PATH} 28 | CMD_PATH=${TOOLS_PATH} 29 | BISON=/bin/bison 30 | else 31 | $(error No idea what platform you're running on...) 32 | endif 33 | 34 | # I'm going to run an Apple II emulator on this "soon" so just run it at 600MHz 35 | # I could also pump it up to 720 or 816, but i don't want to slowly destroy the 36 | # cpu either. We'll see how well it runs at 600MHz first. 37 | IN_SPEED=600 38 | IN_USB=serialhid 39 | IN_OPT=osstd 40 | IN_KEYS=en-us 41 | EXTRA_TIME_LOCAL=0 42 | BOARD_NAME=teensy41 43 | SERIAL_PORT_LABEL=${SERIAL_PORT} 44 | SERIAL_PORT_PROTOCOL=serial 45 | PROJ_NAME=bigscreen 46 | BUILD_PATH=out/bigscreen 47 | 48 | # My custom flags 49 | COMPILER_CPP_EXTRA_FLAGS=-DDEBUG=2 50 | # This causes link errors now :'( 51 | # -flto 52 | 53 | # Libraries to use: 54 | LIB_BUSIO=1 55 | LIB_SPI=1 56 | LIB_GFX=1 57 | LIB_EEPROM=1 58 | LIB_8875=1 59 | LIB_SD=1 60 | LIB_SDFAT=1 61 | LIB_T4_PXP=1 62 | LIB_GFX_BUFFER=1 63 | LIB_WIRE=1 64 | 65 | USER_INCLUDES=-Iinclude/bigscreen -Iinclude/remotescan -Iinclude/teensy -Iinclude 66 | 67 | USER_CPP_SRCS=\ 68 | dbgcfg.cpp \ 69 | kbreporter.cpp \ 70 | mainloop.cpp \ 71 | scanning.cpp \ 72 | remotescan.cpp \ 73 | modulekeyboard.cpp \ 74 | bigscreen.cpp 75 | 76 | #include modules/display/include.mk 77 | #include modules/calculator/include.mk 78 | #include modules/images/include.mk 79 | #include modules/editline/include.mk 80 | #include modules/tetris/maketetris.mk 81 | #include modules/menu/include.mk 82 | #include apple2.mk 83 | 84 | include tools/teensy.${SUF} 85 | 86 | -------------------------------------------------------------------------------- /src/make-laptype.mak: -------------------------------------------------------------------------------- 1 | # Some simple details 2 | THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST)))) 3 | ifeq ($(OS),Windows_NT) 4 | SUF=win 5 | ARD=${USERPROFILE}/AppData/Local/Arduino15 6 | SERIAL_PORT=COM8 7 | # RUNTIME_HARDWARE_PATH=c:/PROGRA~2/Arduino/hardware/tools 8 | RUNTIME_TOOLS_TEENSY_TOOLS_PATH=${ARD}/packages/teensy/tools/teensy-tools/1.57.2 9 | # RUNTIME_TOOLS_TEENSY_COMPILE_PATH=${THIS_DIR}../local_tools/arm-gnu-toolchain-12.2.rel1-mingw-w64-i686-arm-none-eabi 10 | RUNTIME_TOOLS_TEENSY_COMPILE_PATH=${ARD}/packages/teensy/tools/teensy-compile/5.4.1 11 | CMD_PATH=${RUNTIME_HARDWARE_PATH} 12 | BISON=bison 13 | else ifeq ($(shell uname -s), Darwin) 14 | SUF=mac 15 | ARD=/Applications/Teensyduino.app/Contents/Java/hardware 16 | SERIAL_PORT=$(shell ls /dev/cu.usbmodem*501) 17 | TOOLS_PATH=${ARD}/tools 18 | RUNTIME_HARDWARE_PATH=${TOOLS_PATH} 19 | CMD_PATH=${TOOLS_PATH} 20 | ifeq ($(shell uname -p), i386) 21 | BISON=/usr/local/opt/bison/bin/bison 22 | else 23 | BISON=/opt/homebrew/opt/bison/bin/bison 24 | endif 25 | else ifeq ($(shell uname -s), Linux) 26 | SUF=lin 27 | ARD=${HOME}/Apps/arduino-1.8.19/hardware 28 | # grab the *first* serial port we see 29 | SERIAL_PORT=$(shell ls /dev/ttyACM* | head -1) 30 | TOOLS_PATH=${ARD}/tools 31 | RUNTIME_HARDWARE_PATH=${TOOLS_PATH} 32 | CMD_PATH=${TOOLS_PATH} 33 | BISON=/bin/bison 34 | else 35 | $(error No idea what platform you're running on...) 36 | endif 37 | 38 | # At only 24MHz, the screen is dramatically slower, but at 150MHz 39 | # things are buttery smooth, so not running at 600MHz helps overheating, right? 40 | IN_SPEED=150 41 | # For debug: 42 | # IN_USB=serialhid 43 | # For optimized: 44 | IN_USB=keyboard 45 | IN_OPT=osstd 46 | IN_KEYS=en-us 47 | EXTRA_TIME_LOCAL=0 48 | BOARD_NAME=teensy40 49 | SERIAL_PORT_LABEL=${SERIAL_PORT} 50 | SERIAL_PORT_PROTOCOL=serial 51 | PROJ_NAME=laptype 52 | BUILD_PATH=out/laptype 53 | 54 | # My custom flags 55 | COMPILER_CPP_EXTRA_FLAGS=-gsplit-dwarf 56 | # COMPILER_CPP_EXTRA_FLAGS=-flto -gsplit-dwarf 57 | # COMPILER_ELF_EXTRA_FLAGS=-Wl,--gdb-index 58 | # -DDEBUG=1 59 | 60 | # Libraries to use (Updated for Arduino2Make v0.6) 61 | LIB_ADAFRUIT_BUSIO=1 62 | LIB_SPI=1 63 | LIB_ADAFRUIT_GFX_LIBRARY=1 64 | LIB_ADAFRUIT_ST7735_AND_ST7789_LIBRARY=1 65 | LIB_WIRE=1 66 | LIB_EEPROM=1 67 | 68 | USER_INCLUDES=-Iinclude/laptype -Iinclude/localscan -Iinclude/teensy -Iinclude 69 | 70 | USER_CPP_SRCS=\ 71 | dbgcfg.cpp \ 72 | kbreporter.cpp \ 73 | mainloop.cpp \ 74 | scanning.cpp \ 75 | localscan.cpp \ 76 | modulekeyboard.cpp \ 77 | laptype.cpp 78 | 79 | include modules/display/include.mk 80 | include modules/calculator/include.mk 81 | include modules/images/include.mk 82 | include modules/editline/include.mk 83 | include modules/tetris/maketetris.mk 84 | include modules/menu/include.mk 85 | 86 | include tools/teensy.${SUF} 87 | 88 | -------------------------------------------------------------------------------- /src/make-mock.mak: -------------------------------------------------------------------------------- 1 | # This is for doing testing without hardware 2 | 3 | # Here's all the #define's that my source code cares about 4 | # hardware 'core' stuff: 5 | # TEENSY, ADAFRUIT, ARDUINO_NRF52_ADAFRUIT 6 | # hardware config stuff: 7 | # UART_CLIENT, UART_ONLY, BETTERFLY, USB_MASTER 8 | # hardware 'capabilities' stuff: 9 | # HAS_BATTERY, HAS_LED 10 | # software capabilities: 11 | # DEBUG, LOTSA_BLINKING, STATUS_DUMP, TEST_MASTER 12 | 13 | # Necessary configuration stuff 14 | BUILD_PATH=out/mock-host 15 | PROJ_NAME=mock-host 16 | 17 | # This is how to add new flags 18 | # -DDEBUG=1/2 19 | COMPILER_CPP_EXTRA_FLAGS=-DDEBUG=1 20 | 21 | USER_INCLUDES=-Iinclude/mock -Iinclude 22 | 23 | USER_CPP_SRCS=\ 24 | dbgcfg.cpp \ 25 | kbreporter.cpp \ 26 | mainloop.cpp \ 27 | scanning.cpp \ 28 | mock.cpp 29 | 30 | include tools/mock.mk 31 | -------------------------------------------------------------------------------- /src/make-sd.mak: -------------------------------------------------------------------------------- 1 | # Some simple details 2 | ifeq ($(OS),Windows_NT) 3 | SUF=win 4 | ARD=${HOME}/AppData/Local 5 | SERIAL_PORT=COM4 6 | RUNTIME_HARDWARE_PATH=c:/PROGRA~2/Arduino/hardware/tools 7 | CMD_PATH=${RUNTIME_HARDWARE_PATH} 8 | else ifeq ($(shell uname -s), Darwin) 9 | SUF=mac 10 | ARD=/Applications/Teensyduino.app/Contents/Java/hardware 11 | SERIAL_PORT=$(shell ls /dev/cu.usbmodem*401) 12 | TOOLS_PATH=${ARD}/tools 13 | RUNTIME_HARDWARE_PATH=${TOOLS_PATH} 14 | CMD_PATH=${TOOLS_PATH} 15 | else 16 | $(error No Linux support yet, but copying the Darwin stuff should be nearly all of the work) 17 | endif 18 | 19 | IN_SPEED=600 20 | IN_USB=serialhid 21 | IN_OPT=osstd 22 | IN_KEYS=en-us 23 | EXTRA_TIME_LOCAL=0 24 | BOARD_NAME=teensy41 25 | SERIAL_PORT_LABEL=${SERIAL_PORT} 26 | SERIAL_PORT_PROTOCOL=serial 27 | PROJ_NAME=sd 28 | BUILD_PATH=out/sd 29 | 30 | # My custom flags 31 | COMPILER_CPP_EXTRA_FLAGS= 32 | # This causes link errors now :'( 33 | # -flto 34 | 35 | # Libraries to use: 36 | LIB_BUSIO=1 37 | LIB_SPI=1 38 | LIB_GFX=1 39 | LIB_8875=1 40 | LIB_WIRE=1 41 | LIB_SD=1 42 | LIB_SDFAT=1 43 | 44 | USER_INCLUDES=-Iinclude 45 | 46 | USER_CPP_SRCS=\ 47 | test_sd.cpp 48 | 49 | include tools/teensy.${SUF} 50 | -------------------------------------------------------------------------------- /src/make-test.mak: -------------------------------------------------------------------------------- 1 | # Some simple details 2 | ifeq ($(OS),Windows_NT) 3 | SUF=win 4 | ARD=${HOME}/AppData/Local 5 | SERIAL_PORT=COM15 6 | RUNTIME_HARDWARE_PATH=c:/PROGRA~2/Arduino/hardware/tools 7 | CMD_PATH=${RUNTIME_HARDWARE_PATH} 8 | BISON=bison 9 | else ifeq ($(shell uname -s), Darwin) 10 | SUF=mac 11 | ARD=/Applications/Teensyduino.app/Contents/Java/hardware 12 | SERIAL_PORT=$(shell ls /dev/cu.usbmodem*401) 13 | TOOLS_PATH=${ARD}/tools 14 | RUNTIME_HARDWARE_PATH=${TOOLS_PATH} 15 | CMD_PATH=${TOOLS_PATH} 16 | ifeq ($(shell uname -p), i386) 17 | BISON=/usr/local/opt/bison/bin/bison 18 | else 19 | BISON=/opt/homebrew/opt/bison/bin/bison 20 | endif 21 | else 22 | $(error No Linux support yet, but copying the Darwin stuff should be nearly all of the work) 23 | endif 24 | 25 | # I'm going to run an Apple II emulator on this "soon" so just run it at 600MHz 26 | # I could also pump it up to 720 or 816, but i don't want to slowly destroy the 27 | # cpu either. We'll see how well it runs at 600MHz first. 28 | IN_SPEED=600 29 | IN_USB=serialhid 30 | IN_OPT=osstd 31 | IN_KEYS=en-us 32 | EXTRA_TIME_LOCAL=0 33 | BOARD_NAME=teensy40 34 | SERIAL_PORT_LABEL=${SERIAL_PORT} 35 | SERIAL_PORT_PROTOCOL=serial 36 | PROJ_NAME=tst 37 | BUILD_PATH=out/tst 38 | 39 | # My custom flags 40 | COMPILER_CPP_EXTRA_FLAGS= 41 | # This causes link errors now :'( 42 | # -flto 43 | 44 | # Libraries to use: 45 | LIB_BUSIO=1 46 | LIB_SPI=1 47 | LIB_GFX=1 48 | LIB_ST77XX=1 49 | LIB_WIRE=1 50 | LIB_EEPROM=1 51 | LIB_SDFAT=1 52 | LIB_T4_PXP=1 53 | LIB_GFX_BUFFER=1 54 | LIB_ASYNCDMA=1 55 | 56 | # USER_INCLUDES=-Iinclude/threepiece -Iinclude/remotescan -Iinclude/teensy -Iinclude 57 | 58 | USER_CPP_SRCS=\ 59 | test_gfx.cpp 60 | 61 | include tools/teensy.${SUF} 62 | -------------------------------------------------------------------------------- /src/make_secrets.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | uint8_t rotl(char c, int num) { 7 | if (num > 7 || num < 1) 8 | return 0; 9 | return (static_cast(c) << num) | 10 | (static_cast(c) >> (8 - num)); 11 | } 12 | char rotr(char c, int num) { 13 | if (num > 7 || num < 1) 14 | return 0; 15 | return static_cast((static_cast(c) >> num) | 16 | (static_cast(c) << (8 - num))); 17 | } 18 | 19 | int main(int argc, const char* argv[]) { 20 | do { 21 | std::string txt; 22 | std::cout << "Please enter the value for a secret:" << std::endl; 23 | std::getline(std::cin, txt); 24 | if (txt.empty()) 25 | break; 26 | std::cout << "Here's the value, modified: " << std::endl << "\""; 27 | int r = 1; 28 | std::vector vals; 29 | for (char v : txt) { 30 | uint8_t c = rotl(v, r); 31 | r = (r + v) % 6 + 1; 32 | if (c < 32 || c > 0x7e) { 33 | std::cout << "\\" << std::oct << std::setw(3) << std::setfill('0') 34 | << static_cast(c); 35 | } else if (c == '\\') { 36 | std::cout << "\\\\"; 37 | } else { 38 | std::cout << static_cast(c); 39 | } 40 | vals.push_back(c); 41 | } 42 | std::cout << "\"" << std::endl << "Validated: " << std::endl << "\""; 43 | // And now to validate it can be undone: 44 | r = 1; 45 | for (char v : vals) { 46 | char nc = rotr(v, r); 47 | r = (r + nc) % 6 + 1; 48 | std::cout << nc; 49 | } 50 | std::cout << "\"" << std::endl; 51 | } while (true); 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /src/modulekeyboard.cpp: -------------------------------------------------------------------------------- 1 | #include "dbgcfg.h" 2 | 3 | #include "action.h" 4 | #include "enumhelpers.h" 5 | #include "enumtypes.h" 6 | #include "keymap.h" 7 | #include "keystate.h" 8 | #include "modulekeyboard.h" 9 | #include "scanner.h" 10 | #include "usbenums.h" 11 | 12 | // This is used to let us not be a USB keyboard, but use the board "locally" 13 | // for something like a calculator or a game, etc... 14 | KeyboardMode ModuleKeyboardHandler(KeyboardMode curMode, 15 | KeystrokeHandler handler, 16 | Spinner spin) { 17 | Modifiers localMods = Modifiers::None; 18 | KeyboardMode mode = curMode; 19 | Dbg << "Entering Module Keyboard handler" << sfmt::endl; 20 | do { 21 | scancode_t sc; 22 | bool pressed = false; 23 | uint32_t now = millis(); 24 | Scanner scanner{now}; 25 | while ((sc = scanner.getNextCode(pressed)) != 0xFF) { 26 | // Get the keystroke & modifier based on the scan code: 27 | action_t a = moduleKeyMap[sc]; 28 | switch (a.getAction()) { 29 | case KeyAction::KeyPress: 30 | mode = handler(a.getKeystroke(), localMods, pressed, now); 31 | Dbg2 << "Mode handler KeyPress returned " << value_cast(mode) 32 | << sfmt::endl; 33 | break; 34 | case KeyAction::Modifier: 35 | if (pressed) { 36 | localMods |= a.getModifiers(); 37 | } else { 38 | localMods &= ~a.getModifiers(); 39 | } 40 | mode = handler(Keystroke::None, localMods, pressed, now); 41 | Dbg2 << "Mode handler Modifier returned " << value_cast(mode) 42 | << sfmt::endl; 43 | break; 44 | case KeyAction::Mode: 45 | if (pressed) { 46 | mode = a.getMode(); 47 | } 48 | Dbg2 << "Got raw Mode action " << value_cast(mode) << sfmt::endl; 49 | break; 50 | default: 51 | Dbg << "Unsupported action type for raw keyboard: " << a.getAction() 52 | << sfmt::endl; 53 | } 54 | } 55 | scanner.Done(); 56 | if (mode == curMode && spin != nullptr) { 57 | mode = spin(mode, now); 58 | } 59 | } while (mode == curMode); 60 | Dbg << "Exiting Module Keyboard Handler" << sfmt::endl; 61 | return mode; 62 | } 63 | /* 64 | if (a.getAction() == KeyAction::Modifier) { 65 | if (pressed) { 66 | menuMods |= a.getModifiers(); 67 | } else { 68 | // This should turn them off, right? 69 | menuMods &= ~a.getModifiers(); 70 | } 71 | } else if (a.getAction() == KeyAction::KeyPress) { 72 | // Send the keystroke to the Edit Line module 73 | // Then print the results of the Edit Line modules on the screen 74 | 75 | // If the user hits "enter" trigger the calculator 76 | auto ln = edit::readline(a.getKeystroke(), menuMods, pressed, millis()); 77 | if (a.getKeystroke() == Keystroke::Enter && pressed) { 78 | const char* val = calc::Parse(ln.buf); 79 | if (val) { 80 | edit::setline(val); 81 | ln = edit::readline(a.getKeystroke(), menuMods, pressed, millis()); 82 | } 83 | } 84 | DrawText(ln); 85 | if (a.getKeystroke() == Keystroke::Tab && pressed) { 86 | Keyboard.print(ln.buf); 87 | } 88 | // And if they hit whatever "paste" should be, type the calculator value 89 | } 90 | */ -------------------------------------------------------------------------------- /src/modules/calculator/.gitignore: -------------------------------------------------------------------------------- 1 | CalcParser.cpp 2 | include/CalcParser.h 3 | -------------------------------------------------------------------------------- /src/modules/calculator/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | if(CMAKE_HOST_APPLE) 4 | # On macOS, search Homebrew for keg-only versions of Bison (and Flex). 5 | # Xcode does not provide new enough versions for us to use. 6 | execute_process( 7 | COMMAND brew --prefix bison 8 | RESULT_VARIABLE BREW_BISON 9 | OUTPUT_VARIABLE BREW_BISON_PREFIX 10 | OUTPUT_STRIP_TRAILING_WHITESPACE 11 | ) 12 | 13 | if(BREW_BISON EQUAL 0 AND EXISTS "${BREW_BISON_PREFIX}") 14 | message(STATUS "Found Bison keg installed by Homebrew at ${BREW_BISON_PREFIX}") 15 | set(BISON_EXECUTABLE "${BREW_BISON_PREFIX}/bin/bison") 16 | endif() 17 | endif() 18 | 19 | find_package(BISON) 20 | BISON_TARGET( 21 | CalcParser 22 | CalcGrammar.yy 23 | ${CMAKE_CURRENT_BINARY_DIR}/CalcParser.cpp 24 | DEFINES_FILE 25 | ${CMAKE_CURRENT_BINARY_DIR}/CalcParser.h 26 | ) 27 | add_custom_module( 28 | calculator 29 | include 30 | calculator.cpp 31 | CalcLexer.cpp 32 | Calculator.cpp 33 | CalcExpr.cpp 34 | CalcContext.cpp 35 | CalcHandler.cpp 36 | ${BISON_CalcParser_OUTPUTS} 37 | ) 38 | target_include_directories( 39 | module_calculator 40 | PRIVATE 41 | ${CMAKE_CURRENT_BINARY_DIR} 42 | headers 43 | ) 44 | target_link_libraries(module_calculator PUBLIC Adafruit_ST77XX_lib module_display module_editline) 45 | -------------------------------------------------------------------------------- /src/modules/calculator/CalcGrammar.yy: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | 4 | // Using my own scanner, because flex is incredibly 'stdio.h-centric' which hates Arduino 5 | #include "CalcExpr.h" 6 | int yyerror(const char *s); 7 | int yylex(void); 8 | 9 | %} 10 | 11 | %require "3.7.4" 12 | 13 | %no-lines 14 | 15 | // CMake handles setting these things, which is very handy, honestly 16 | // %defines "include/CalcParser.h" 17 | // %output "CalcParser.cpp" 18 | 19 | %code requires { 20 | #include "CalcExpr.h" 21 | } 22 | 23 | %define api.value.type yystype 24 | 25 | %token EOL LPAREN RPAREN 26 | %token INT 27 | %token FLT 28 | %token VAR 29 | 30 | %nterm exp 31 | 32 | %nonassoc ASSIGN 33 | %left PLUS MINUS 34 | %left MULTIPLY DIVIDE MODULO 35 | %precedence UMINUS 36 | %precedence FACTORIAL 37 | %right EXPONENT 38 | 39 | %% 40 | 41 | lines : %empty 42 | | lines line 43 | ; 44 | 45 | line : EOL { yyerror("Read an empty line.\n"); } 46 | | exp EOL { $1.show(); } 47 | | VAR ASSIGN exp EOL { $1.assignVal($3); $3.show(); } 48 | | error EOL { yyerrok; } 49 | ; 50 | 51 | exp : INT { $$ = $1; } 52 | | FLT { $$ = $1; } 53 | | exp PLUS exp { $$ = $1 + $3; } 54 | | exp MINUS exp { $$ = $1 - $3; } 55 | | exp MULTIPLY exp { $$ = $1 * $3; } 56 | | exp DIVIDE exp { $$ = $1 / $3; } 57 | | exp MODULO exp { $$ = $1 % $3; } 58 | | exp EXPONENT exp { $$ = $1.power($3); } 59 | | MINUS exp %prec UMINUS { $$ = -$2; } 60 | | exp FACTORIAL { $$ = $1.factorial(); } 61 | | VAR LPAREN exp RPAREN { $$ = $1.invoke($3); } 62 | | LPAREN exp RPAREN { $$ = $2; } 63 | | VAR { $$ = $1.getVal(); } 64 | ; 65 | 66 | %% 67 | -------------------------------------------------------------------------------- /src/modules/calculator/CalcHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "Calculator.h" 2 | #include "display.h" 3 | #include "editline.h" 4 | #include "enumhelpers.h" 5 | #include "enumtypes.h" 6 | #include "keymap.h" 7 | #include "modulekeyboard.h" 8 | #include "scanning.h" 9 | 10 | namespace calc { 11 | 12 | disp::rect_t lastPos = {0, 0, 0, 0}; 13 | 14 | void DrawText(const edit::editline& ln) { 15 | // Add the 'cursor' 16 | char loc[129]; 17 | int after = 0; 18 | size_t s; 19 | Dbg << "Buffer:" << sfmt::endl << ln.buf << sfmt::endl; 20 | for (s = 0; ln.buf[s]; s++) { 21 | if (after == 0 && ln.pos == s) { 22 | loc[s] = '|'; 23 | after = 1; 24 | } 25 | loc[s + after] = ln.buf[s]; 26 | } 27 | if (!after) { 28 | loc[s] = '|'; 29 | after++; 30 | } 31 | loc[s + after] = 0; 32 | disp::DrawText(&loc[0], lastPos); 33 | } 34 | 35 | KeyboardMode Handler(Keystroke ks, Modifiers mods, bool pressed, uint32_t now) { 36 | // Send the keystroke to the Edit Line module 37 | // Then print the results of the Edit Line modules on the screen 38 | auto ln = edit::readline(ks, mods, pressed, now); 39 | if (pressed) { 40 | if (ks == Keystroke::Enter) { 41 | // If the user hit "enter" trigger the calculator 42 | const char* val = calc::Parse(ln.buf); 43 | if (val) { 44 | edit::setline(val); 45 | ln = edit::readline(ks, mods, pressed, now); 46 | } 47 | } 48 | } 49 | DrawText(ln); 50 | KeyboardMode result = 51 | (!pressed && ks == Keystroke::None && mods == Modifiers::ROpt) 52 | ? KeyboardMode::Normal 53 | : KeyboardMode::Calculator; 54 | if (result == KeyboardMode::Normal) { 55 | lastPos = {0, 0, 0, 0}; 56 | } 57 | if (ks == Keystroke::Tab && pressed) { 58 | Keyboard.print(ln.buf); 59 | } 60 | return result; 61 | } 62 | } // namespace calc -------------------------------------------------------------------------------- /src/modules/calculator/Calculator.cpp: -------------------------------------------------------------------------------- 1 | // MSVC, LKG6, bad memories. Very. Bad. Memories. 2 | #define _CRT_SECURE_NO_WARNINGS 1 3 | #if defined(STANDALONE) 4 | #include 5 | #include 6 | #else 7 | #include "Arduino.h" 8 | #endif 9 | 10 | #include "CalcContext.h" 11 | #include "CalcLexer.h" 12 | #include "CalcParser.h" 13 | #include "Calculator.h" 14 | 15 | // This stuff is all for the bison parser 16 | 17 | const char* errors = nullptr; 18 | calc::Lexer* scan; 19 | 20 | int yyerror(const char* msg) { 21 | #if defined(STANDALONE) 22 | std::cerr << "Error:" << msg << std::endl; 23 | #endif 24 | errors = msg; 25 | return 1; 26 | } 27 | 28 | int yylex() { 29 | return scan->lex(); 30 | } 31 | 32 | namespace calc { 33 | 34 | void Initialize() { 35 | std::fill(std::begin(showBuffer), std::end(showBuffer), 0); 36 | context.clear(); 37 | } 38 | 39 | const char* Parse(const char* str) { 40 | errors = nullptr; 41 | calc::Lexer scanner{str}; 42 | scan = &scanner; 43 | yyparse(); 44 | // TODO: Return the calculated expression 45 | return showBuffer; 46 | } 47 | 48 | } // namespace calc 49 | 50 | #if defined(STANDALONE) 51 | int main(int argc, const char* argv[]) { 52 | std::string input; 53 | std::cout.precision(10); 54 | do { 55 | std::cout << "Enter some stuff:" << std::endl; 56 | std::getline(std::cin, input); 57 | std::cout << "'" << input << "'" << std::endl; 58 | calc::Parse(input.c_str()); 59 | std::cout << "====" << std::endl; 60 | } while (!input.empty()); 61 | return 0; 62 | } 63 | #endif 64 | -------------------------------------------------------------------------------- /src/modules/calculator/Makefile: -------------------------------------------------------------------------------- 1 | MODNAME = calculator 2 | PARSE = CalcParser.cpp include/CalcParser.h 3 | CPP_SRC = CalcParser.cpp CalcLexer.cpp Calculator.cpp CalcExpr.cpp CalcContext.cpp 4 | MORE_CLEAN = ${PARSE} 5 | 6 | include ../standalone.mk 7 | 8 | ${PARSE}: CalcGrammar.yy 9 | ${BISON} $< -------------------------------------------------------------------------------- /src/modules/calculator/headers/CalcContext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // MSVC, LKG6, bad memories. Very. Bad. Memories. 4 | #if !defined(_CRT_SECURE_NO_WARNINGS) 5 | #define _CRT_SECURE_NO_WARNINGS 6 | #endif 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "CalcParser.h" 16 | 17 | namespace calc { 18 | 19 | struct StrCompare { 20 | bool operator()(const char* const str1, const char* const str2) const { 21 | return std::strcmp(str1, str2) < 0; 22 | } 23 | }; 24 | 25 | enum class IntMode : uint8_t { Decimal = 0, Hexadecimal, Binary }; 26 | 27 | class Context { 28 | // I need/want to intern strings 29 | std::map interned; 30 | // Then I need a function map 31 | std::map funcs; 32 | // And I need a variable map 33 | std::map vars; 34 | 35 | public: 36 | const char* intern(char*); 37 | void assign(const char*, const CalcExpr&); 38 | CalcExpr getVal(const char*, const CalcExpr&); 39 | CalcExpr invoke(const char*, const CalcExpr&) const; 40 | void clear(); 41 | IntMode curMode; 42 | }; 43 | 44 | extern Context context; 45 | 46 | } // namespace calc -------------------------------------------------------------------------------- /src/modules/calculator/headers/CalcExpr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #if defined(STANDALONE) 6 | #include 7 | #else 8 | #include "Arduino.h" 9 | #endif 10 | 11 | namespace calc { 12 | 13 | extern char showBuffer[128]; 14 | 15 | enum class ValType : uint8_t { Err, Int, Float, Text }; 16 | 17 | class CalcExpr { 18 | ValType type; 19 | union { 20 | double fVal; 21 | int64_t iVal; 22 | const char* txt; 23 | }; 24 | 25 | public: 26 | CalcExpr(double d) : type(ValType::Float), fVal(d) {} 27 | CalcExpr(int64_t i) : type(ValType::Int), iVal(i) {} 28 | CalcExpr(int i) : type(ValType::Int), iVal(i) {} 29 | CalcExpr(uint64_t i) : type(ValType::Int), iVal(i) {} 30 | CalcExpr(int8_t i) : type(ValType::Int), iVal(i) {} 31 | CalcExpr(uint8_t i) : type(ValType::Int), iVal(i) {} 32 | CalcExpr(const char* e = nullptr) : type(ValType::Err), txt(e) {} 33 | CalcExpr(int, const char* text) : type(ValType::Text), txt(text) {} 34 | CalcExpr(const CalcExpr& ce) : type(ce.type) { 35 | switch (type) { 36 | case ValType::Int: 37 | iVal = ce.iVal; 38 | break; 39 | case ValType::Float: 40 | fVal = ce.fVal; 41 | break; 42 | default: 43 | txt = ce.txt; 44 | break; 45 | } 46 | } 47 | CalcExpr& self() { 48 | return *this; 49 | } 50 | bool isInt() const { 51 | return type == ValType::Int; 52 | } 53 | bool isError() const { 54 | return type == ValType::Err; 55 | } 56 | bool isFloat() const { 57 | return type == ValType::Float; 58 | } 59 | bool isText() const { 60 | return type == ValType::Text; 61 | } 62 | double asFloat() const; 63 | int64_t asInt() const; 64 | const char* asError() const; 65 | const char* asText() const; 66 | CalcExpr operator-() const; 67 | CalcExpr operator+(const CalcExpr& v) const; 68 | CalcExpr operator-(const CalcExpr& v) const; 69 | CalcExpr operator*(const CalcExpr& v) const; 70 | CalcExpr operator/(const CalcExpr& v) const; 71 | CalcExpr operator%(const CalcExpr& v) const; 72 | CalcExpr operator&(const CalcExpr& v) const; 73 | CalcExpr operator|(const CalcExpr& v) const; 74 | CalcExpr operator~() const; 75 | CalcExpr factorial() const; 76 | CalcExpr power(const CalcExpr& v) const; 77 | CalcExpr getVal() const; 78 | CalcExpr invoke(const CalcExpr& v) const; 79 | void assignVal(const CalcExpr& v) const; 80 | void show() const; 81 | #if defined(STANDALONE) 82 | friend std::ostream& operator<<(std::ostream&, const CalcExpr&); 83 | #else 84 | void print(const char* header = nullptr) const; 85 | void println(const char* header = nullptr) const; 86 | #endif 87 | }; 88 | 89 | } // namespace calc 90 | 91 | // This is the thing I use in Bison: 92 | #define YYSTYPE calc::CalcExpr 93 | #define YYSTYPE_IS_DECLARED 94 | -------------------------------------------------------------------------------- /src/modules/calculator/headers/CalcLexer.h: -------------------------------------------------------------------------------- 1 | // MSVC, LKG6, bad memories. Very. Bad. Memories. 2 | #define _CRT_SECURE_NO_WARNINGS 1 3 | #include 4 | #include 5 | 6 | #include "CalcExpr.h" 7 | #include "CalcParser.h" 8 | 9 | namespace calc { 10 | 11 | class Token { 12 | public: 13 | Token(yytoken_kind_t t, uint16_t s, uint16_t e) : tok(t), start(s), end(e) {} 14 | yytoken_kind_t tok; 15 | uint16_t start, end; 16 | }; 17 | 18 | class Lexer { 19 | std::vector tokens; 20 | const char* str; 21 | size_t cur; 22 | uint16_t addToken(yytoken_kind_t tk, uint16_t s, uint16_t e); 23 | void Tokenize(const char* str); 24 | 25 | public: 26 | Lexer(const char* str) : str(str) { 27 | Tokenize(str); 28 | cur = 0; 29 | } 30 | // This is the (very generic) bison/yacc interface 31 | int lex() { 32 | // I still need to fill in the semantic_type 33 | auto& t = tokens[cur++]; 34 | yytokentype tok = t.tok; 35 | char* buf = nullptr; 36 | switch (tok) { 37 | case LPAREN: // LPAREN 38 | case RPAREN: // RPAREN 39 | case ASSIGN: // ASSIGN 40 | case PLUS: // PLUS 41 | case MINUS: // MINUS 42 | case MULTIPLY: // MULTIPLY 43 | case DIVIDE: // DIVIDE 44 | case MODULO: // MODULO 45 | case UMINUS: // UMINUS 46 | case FACTORIAL: // FACTORIAL 47 | case EXPONENT: // EXPONENT 48 | break; 49 | case INT: // INT 50 | buf = new char[t.end - t.start + 1]; 51 | strncpy(buf, &str[t.start], t.end - t.start); 52 | buf[t.end - t.start] = 0; 53 | yylval = CalcExpr{static_cast(atoll(buf))}; 54 | delete[] buf; 55 | break; 56 | case FLT: // FLT 57 | buf = new char[t.end - t.start + 1]; 58 | strncpy(buf, &str[t.start], t.end - t.start); 59 | buf[t.end - t.start] = 0; 60 | yylval = CalcExpr{atof(buf)}; 61 | delete[] buf; 62 | break; 63 | case VAR: // VAR 64 | buf = new char[t.end - t.start + 1]; 65 | strncpy(buf, &str[t.start], t.end - t.start); 66 | buf[t.end - t.start] = 0; 67 | yylval = CalcExpr{0, buf}; 68 | break; 69 | default: 70 | break; 71 | } 72 | return static_cast(tok); 73 | } 74 | }; 75 | 76 | using CalcFunction = CalcExpr (*)(const CalcExpr&); 77 | 78 | } // namespace calc 79 | -------------------------------------------------------------------------------- /src/modules/calculator/include.mk: -------------------------------------------------------------------------------- 1 | CALC_MODULE_DIR=modules/calculator/ 2 | 3 | CALC_PARSER_FILES=${CALC_MODULE_DIR}CalcParser.cpp ${CALC_MODULE_DIR}include/CalcParser.h 4 | 5 | USER_CLEAN = ${USER_CLEAN} ${CALC_PARSER_FILES} 6 | 7 | ${CALC_PARSER_FILES}: ${CALC_MODULE_DIR}CalcGrammar.yy 8 | cd ${CALC_MODULE_DIR} && ${BISON} --header=include/CalcParser.h --output=CalcParser.cpp CalcGrammar.yy 9 | 10 | # one minor dependency... 11 | ${CALC_MODULE_DIR}CalcLexer.cpp : ${CALC_MODULE_DIR}include/CalcParser.h 12 | 13 | USER_INCLUDES += -Imodules/calculator -Imodules/calculator/include -Imodules/calculator/headers 14 | USER_CPP_SRCS += $(addprefix ${CALC_MODULE_DIR}, CalcLexer.cpp CalcParser.cpp Calculator.cpp CalcExpr.cpp CalcContext.cpp CalcHandler.cpp) 15 | 16 | VPATH += ${CALC_MODULE_DIR} 17 | -------------------------------------------------------------------------------- /src/modules/calculator/include/Calculator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "enumtypes.h" 4 | #include "usbenums.h" 5 | #include 6 | 7 | namespace calc { 8 | 9 | void Initialize(); 10 | 11 | const char* Parse(const char*); 12 | 13 | KeyboardMode Handler(Keystroke ks, Modifiers mods, bool pressed, uint32_t now); 14 | 15 | } // namespace calc 16 | -------------------------------------------------------------------------------- /src/modules/display/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_custom_module(display . display.cpp) 4 | target_link_libraries(module_display PUBLIC Adafruit_ST77XX_lib) -------------------------------------------------------------------------------- /src/modules/display/display.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace disp { 6 | 7 | struct rect_t { 8 | int16_t x, y = 0; 9 | uint16_t w, h; 10 | }; 11 | 12 | enum class RelativeAlignment : uint8_t { Start, Middle, End }; 13 | enum class TextAlignment : uint8_t { 14 | LeftTop, 15 | LeftMiddle, 16 | LeftBottom, 17 | CenterTop, 18 | CenterMiddle, 19 | CenterBottom, 20 | RightTop, 21 | RightMiddle, 22 | RightBottom 23 | }; 24 | 25 | /*Adafruit_ST7789* */ 26 | void Init(uint16_t w, 27 | uint16_t h, 28 | int mhz, 29 | uint8_t rotation, 30 | uint8_t TFT_CS, 31 | uint8_t TFT_DC, 32 | uint8_t TFT_Reset, 33 | uint8_t TFT_Backlight, 34 | uint8_t SD_CS = 0, 35 | uint8_t SPKR = 0); 36 | void SetBacklight(bool turnOn, uint32_t now); 37 | void SetTimeout(uint16_t seconds); 38 | void Tick(uint32_t time); 39 | void DrawKeyboard(uint16_t scancode, uint16_t x, uint16_t y); 40 | void DrawText(const char* loc, 41 | rect_t& prv, 42 | TextAlignment align = TextAlignment::CenterMiddle, 43 | uint8_t padding = 2, 44 | uint16_t color = 0xFFFF, 45 | uint16_t bgColor = 0); 46 | void ClearScreen(uint16_t color); 47 | void DrawText( 48 | const char* str, uint8_t size, uint16_t color, int16_t x, int16_t y); 49 | void FillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); 50 | void FillRoundRect( 51 | int16_t x, int16_t y, int16_t w, int16_t h, uint16_t r, uint16_t color); 52 | void DrawLine(int16_t sx, int16_t sy, int16_t ex, int16_t ey, uint16_t color); 53 | void GetTextBounds(const char* str, 54 | int16_t x, 55 | int16_t y, 56 | int16_t& xp, 57 | int16_t& yp, 58 | uint16_t& w, 59 | uint16_t& h, 60 | uint8_t fontSize = 1); 61 | uint16_t GetWidth(); 62 | uint16_t GetHeight(); 63 | void Draw16BitBitmap( 64 | uint16_t* buffer, int16_t x, int16_t y, uint16_t w, uint16_t h); 65 | 66 | } // namespace disp -------------------------------------------------------------------------------- /src/modules/display/include.mk: -------------------------------------------------------------------------------- 1 | DISP_MODULE_DIR=modules/display/ 2 | 3 | USER_INCLUDES += -I${DISP_MODULE_DIR} 4 | USER_CPP_SRCS += $(addprefix ${DISP_MODULE_DIR}, display.cpp) 5 | 6 | VPATH += ${DISP_MODULE_DIR} 7 | -------------------------------------------------------------------------------- /src/modules/editline/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_custom_module(editline . editline.cpp) 4 | -------------------------------------------------------------------------------- /src/modules/editline/Makefile: -------------------------------------------------------------------------------- 1 | MODNAME = edit 2 | CPP_SRC = editline.cpp 3 | 4 | include ../standalone.mk 5 | -------------------------------------------------------------------------------- /src/modules/editline/editline.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "usbenums.h" 7 | 8 | namespace edit { 9 | struct editline { 10 | const char* buf; 11 | uint8_t pos; 12 | }; 13 | void Initialize(); 14 | const editline& readline(Keystroke k, Modifiers m, bool pressed, uint32_t now); 15 | void setline(const char* buf, int16_t pos = -1); 16 | char getChar(Keystroke k, Modifiers m); 17 | } // namespace edit -------------------------------------------------------------------------------- /src/modules/editline/include.mk: -------------------------------------------------------------------------------- 1 | EDIT_MODULE_DIR=modules/editline/ 2 | 3 | USER_INCLUDES += -I${EDIT_MODULE_DIR} -I${EDIT_MODULE_DIR}include 4 | USER_CPP_SRCS += $(addprefix ${EDIT_MODULE_DIR}, editline.cpp) 5 | 6 | VPATH += ${EDIT_MODULE_DIR} 7 | -------------------------------------------------------------------------------- /src/modules/images/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_custom_module( 4 | images 5 | . 6 | image.cpp 7 | imgdec_pal.cpp 8 | imgdec_prle.cpp 9 | imgdec_rle.cpp 10 | bitmaps/amy.cpp 11 | bitmaps/batman.cpp 12 | bitmaps/calc.cpp 13 | bitmaps/haha.cpp 14 | bitmaps/hug.cpp 15 | bitmaps/keyb.cpp 16 | bitmaps/like.cpp 17 | bitmaps/linux.cpp 18 | bitmaps/love.cpp 19 | bitmaps/mac.cpp 20 | bitmaps/mad.cpp 21 | bitmaps/sad.cpp 22 | bitmaps/win.cpp 23 | bitmaps/wow.cpp 24 | ) 25 | target_include_directories( 26 | module_images 27 | PUBLIC 28 | include 29 | ) 30 | target_link_libraries(module_images PUBLIC Adafruit_ST77XX_lib module_display) -------------------------------------------------------------------------------- /src/modules/images/Makefile: -------------------------------------------------------------------------------- 1 | MODNAME=image 2 | IMG_SRC = imgencoder.cpp \ 3 | img_cmdln.cpp \ 4 | imgenc_rle.cpp \ 5 | imgdec_rle.cpp \ 6 | imgenc_pal.cpp \ 7 | imgdec_pal.cpp \ 8 | imgenc_prle.cpp \ 9 | imgdec_prle.cpp 10 | BMP_SRC = $(notdir $(wildcard bitmaps/*.cpp)) 11 | CPP_SRC = ${IMG_SRC} ${BMP_SRC} 12 | VPATH += bitmaps 13 | 14 | include ../standalone.mk 15 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/amy-135-200.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/amy-135-200.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/amy.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_amy; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/apple-ii-240-86.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/apple-ii-240-86.raw -------------------------------------------------------------------------------- /src/modules/images/bitmaps/apple-ii.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_apple_ii; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/batman-102-58.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/batman-102-58.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/batman.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_batman; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/calc.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/calc.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/calc.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_calcpic; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/haha.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/haha.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/haha.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_haha; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/hug.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/hug.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/hug.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_hug; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/keyb.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/keyb.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/keyb.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_keyb; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/like.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/like.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/like.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_like; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/linux-79-96.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/linux-79-96.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/linux.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_linux; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/love.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/love.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/love.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_love; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/mac-78-96.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/mac-78-96.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/mac.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_mac; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/mad.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/mad.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/mad.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_mad; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/sad.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/sad.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/sad.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_sad; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/win-100-100.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/win-100-100.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/win.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_win; 2 | -------------------------------------------------------------------------------- /src/modules/images/bitmaps/wow.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinfrei/FreiKey/07d5bc92f102b5c550bd649103c9b098d86fc947/src/modules/images/bitmaps/wow.bin -------------------------------------------------------------------------------- /src/modules/images/bitmaps/wow.h: -------------------------------------------------------------------------------- 1 | extern const struct image_descriptor* gfx_wow; 2 | -------------------------------------------------------------------------------- /src/modules/images/image.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "bitmaps/amy.h" 5 | #include "bitmaps/apple-ii.h" 6 | #include "bitmaps/batman.h" 7 | #include "bitmaps/calc.h" 8 | #include "bitmaps/haha.h" 9 | #include "bitmaps/hug.h" 10 | #include "bitmaps/keyb.h" 11 | #include "bitmaps/like.h" 12 | #include "bitmaps/linux.h" 13 | #include "bitmaps/love.h" 14 | #include "bitmaps/mac.h" 15 | #include "bitmaps/mad.h" 16 | #include "bitmaps/sad.h" 17 | #include "bitmaps/win.h" 18 | #include "bitmaps/wow.h" 19 | 20 | struct image_descriptor; 21 | 22 | void ShowImage(const image_descriptor* img); -------------------------------------------------------------------------------- /src/modules/images/imgdec_pal.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "bitmap.h" 4 | 5 | // This file must compile both for Arduino and for Windows/Mac. 6 | // That means be careful with the C++, unfortunately... 7 | 8 | uint8_t log2ish(uint16_t n) { 9 | uint8_t count = 0; 10 | // This handles 2 ^ n exactly. 11 | uint8_t rounded = !!((n > 1) && !(n & (n - 1))); 12 | while (n != 0) { 13 | n >>= 1; 14 | count += 1; 15 | } 16 | 17 | return count - rounded; 18 | } 19 | 20 | bool rdbit(bytestream strm, uint32_t* ofs, uint8_t* bit) { 21 | uint8_t mask = 1 << *bit; 22 | bool res = (strm[*ofs] & mask) != 0; 23 | if (*bit == 7) { 24 | *bit = 0; 25 | (*ofs)++; 26 | } else { 27 | (*bit)++; 28 | } 29 | return res; 30 | } 31 | 32 | uint16_t readBits(uint8_t numBits, 33 | bytestream cmpStrm, 34 | uint32_t* i, 35 | uint8_t* curBit) { 36 | uint16_t res = 0; 37 | uint16_t val = 1; 38 | while (numBits--) { 39 | if (rdbit(cmpStrm, i, curBit)) { 40 | res |= val; 41 | } 42 | val <<= 1; 43 | } 44 | return res; 45 | } 46 | 47 | uint16_t read16b(bytestream stream, uint32_t* ofs) { 48 | *ofs += 2; 49 | return stream[*ofs - 2] + (stream[*ofs - 1] << 8); 50 | } 51 | 52 | // Palette encoded image 53 | 54 | // The first 2 bytes are the # of palette entries (must be less than 16 bits) 55 | // followed by the palette entries 56 | 57 | // Next is a stop-bit number of entries, and finally the list of palette indices 58 | // encoded in bit-little-endian order. The # of bits is the number required to 59 | // encode all palette locations, so if it's 488 palette entries, we'll have 9 60 | // bit palette entries: 61 | // clang-format off 62 | // 8765 4321 7654 3219 6543 2198 5432 1987 4321 9876.... << bit number 63 | // 1111 1111 2222 2221 3333 3322 4444 4333 5555 4444.... << digit number 64 | // 1101.0110 1010.1000 1101.0000 1011.1101 1111.0001 << values (MSB->LSB) 65 | // clang-format on 66 | // the first number is 0.1101.0110, next number is 0.0101.0100 67 | 68 | void decode_pal(bytestream cmpStrm, uint32_t strmLen, sender send) { 69 | const uint16_t bufSize = 1024; 70 | uint8_t buffer[1024]; 71 | uint32_t offs = 0; 72 | uint32_t i = 0; 73 | // Read the size of the palette 74 | uint16_t paletteSize = read16b(cmpStrm, &i); 75 | uint16_t* palette = new uint16_t[paletteSize]; 76 | // Read the palette 77 | for (uint16_t j = 0; j < paletteSize; j++) { 78 | palette[j] = read16b(cmpStrm, &i); 79 | if (i > strmLen) { 80 | // ARG! 81 | } 82 | } 83 | // Now read the pixel data 84 | uint8_t bit = 0; 85 | uint8_t numBits = log2ish(paletteSize); 86 | while (i < strmLen) { 87 | uint16_t paletteIndex = readBits(numBits, cmpStrm, &i, &bit); 88 | uint16_t color = palette[paletteIndex]; 89 | if (offs == bufSize) { 90 | send(buffer, offs); 91 | offs = 0; 92 | } 93 | // I need to byte-swap here 94 | buffer[offs++] = color >> 8; 95 | buffer[offs++] = color & 0xFF; 96 | } 97 | if (offs) { 98 | send(buffer, offs); 99 | } 100 | delete[] palette; 101 | } 102 | -------------------------------------------------------------------------------- /src/modules/images/imgdec_prle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "bitmap.h" 4 | 5 | // This file must compile both for Arduino and for Windows/Mac. 6 | // That means be careful with the C++, unfortunately... 7 | 8 | void decode_prle(bytestream cmpStrm, uint32_t strmLen, sender send) { 9 | const uint16_t bufSize = 1024; 10 | uint8_t buffer[1024]; 11 | int32_t offs = 0; 12 | uint32_t i = 0; 13 | // Read the size of the palette 14 | uint16_t paletteSize = read16b(cmpStrm, &i); 15 | uint16_t* palette = new uint16_t[paletteSize]; 16 | // Read the palette 17 | for (uint16_t j = 0; j < paletteSize; j++) { 18 | palette[j] = read16b(cmpStrm, &i); 19 | if (i > strmLen) { 20 | // ARG! 21 | } 22 | } 23 | // Now read the pixel data 24 | uint8_t bit = 0; 25 | uint8_t numBits = log2ish(paletteSize); 26 | while (i < strmLen) { 27 | uint32_t length = readStopBitNumber(cmpStrm, &i); 28 | bool repeat = length % 2 == 1; 29 | length /= 2; 30 | if (repeat) { 31 | // Read the value and repeat it 32 | uint16_t paletteIndex = readBits(numBits, cmpStrm, &i, &bit); 33 | uint16_t color = palette[paletteIndex]; 34 | for (uint32_t j = 0; j < length; j++) { 35 | if (offs + j * 2 == bufSize) { 36 | send(buffer, bufSize); 37 | offs = -(j * 2); 38 | } 39 | buffer[offs + j * 2] = (color >> 8) & 0xFF; 40 | buffer[offs + j * 2 + 1] = color & 0xFF; 41 | } 42 | offs += length * 2; 43 | } else { 44 | // Read the next 'length' palette entries into the buffer 45 | for (uint16_t j = 0; j < length; j++) { 46 | uint16_t paletteIndex = readBits(numBits, cmpStrm, &i, &bit); 47 | uint16_t color = palette[paletteIndex]; 48 | if (offs + j * 2 == bufSize) { 49 | send(buffer, bufSize); 50 | offs = -(j * 2); 51 | } 52 | // I need to byte-swap here, apparently 53 | buffer[offs + j * 2] = (color >> 8) & 0xFF; 54 | buffer[offs + j * 2 + 1] = color & 0xFF; 55 | } 56 | offs += length * 2; 57 | } 58 | // Increment i if we had any bits left to read in the current byte 59 | if (bit > 0) { 60 | i++; 61 | bit = 0; 62 | } 63 | } 64 | if (offs) { 65 | send(buffer, offs); 66 | } 67 | delete[] palette; 68 | } 69 | -------------------------------------------------------------------------------- /src/modules/images/imgdec_rle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "bitmap.h" 5 | 6 | // Not-Quite-Run-Length-Encoding 16 bit decoder, with byte swapping 7 | 8 | // This reads a unsigned integer value encoded with high "stop" bit 9 | // which allows you to encode 0-127 in 1 byte, 128-16383 in 2 bytes, and 10 | // 16384-2M in 3. For NQRLE, odd means "repeat" and even means "unique" 11 | // so we can encode runs of 63 bytes in 1 byte, and runs of 8191 bytes in 2, 12 | // which 13 | uint32_t readStopBitNumber(bytestream cmpStrm, uint32_t* i) { 14 | uint32_t val = 0; 15 | uint8_t curByte = 0; 16 | uint32_t shift = 0; 17 | do { 18 | curByte = cmpStrm[(*i)++]; 19 | val = val | ((curByte & 0x7f) << shift); 20 | shift += 7; 21 | } while ((curByte & 0x80) == 0); 22 | return val; 23 | } 24 | 25 | void decode_rle(bytestream cmpStrm, uint32_t strmLen, sender send) { 26 | const uint16_t bufSize = 1024; 27 | uint8_t buffer[1024]; 28 | int32_t offs = 0; 29 | for (uint32_t i = 0; i < strmLen;) { 30 | uint32_t length = readStopBitNumber(cmpStrm, &i); 31 | bool repeat = length % 2 == 1; 32 | length /= 2; 33 | if (repeat) { 34 | // repeat the next pair of bytes N times 35 | uint8_t byte1 = cmpStrm[i++]; 36 | uint8_t byte2 = cmpStrm[i++]; 37 | for (uint32_t j = 0; j < length; j++) { 38 | if (offs + j * 2 == bufSize) { 39 | send(buffer, offs + j * 2); 40 | offs = -(j * 2); 41 | } 42 | buffer[offs + j * 2 + 1] = byte1; 43 | buffer[offs + j * 2] = byte2; 44 | } 45 | offs += length * 2; 46 | } else { 47 | // copy the next N pair of bytes 48 | for (uint32_t j = 0; j < length; j++) { 49 | if (offs + j * 2 == bufSize) { 50 | send(buffer, offs + j * 2); 51 | offs = -(j * 2); 52 | } 53 | buffer[offs + j * 2 + 1] = cmpStrm[i++]; 54 | buffer[offs + j * 2] = cmpStrm[i++]; 55 | } 56 | offs += length * 2; 57 | } 58 | } 59 | if (offs) { 60 | send(buffer, offs); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/modules/images/imgenc_prle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "bitmap.h" 6 | #include "imgencoder.h" 7 | 8 | // it should work better for stuff that has strings of unique stuff along with 9 | // strings of repeated stuff 10 | bool encode_prle(bytestream data, uint32_t bytes, byte_printer print) { 11 | if (bytes & 1) { 12 | // It must be 16 bit values, yeah? 13 | return false; 14 | } 15 | // First, build the palette 16 | const uint16_t* colors = reinterpret_cast(data); 17 | uint32_t cbytes = bytes / 2; 18 | std::pair, std::vector> pal_and_rev = 19 | calculate_palette(colors, cbytes); 20 | 21 | // Okay, start encoding 22 | // First, the palette size 23 | const uint32_t paletteSize = static_cast(pal_and_rev.first.size()); 24 | print(paletteSize & 0xFF); 25 | print((paletteSize >> 8) & 0xFF); 26 | 27 | // Now emit the palette 28 | for (const uint16_t val : pal_and_rev.first) { 29 | print(val & 0xFF); 30 | print(val >> 8); 31 | } 32 | 33 | uint8_t numBits = log2ish(pal_and_rev.first.size()); 34 | 35 | bool success = true; 36 | uint16_t bitBuffer = 0; 37 | // This is used to print the RLE counts, so it shows up between 38 | // groups of palette encoded numbers 39 | byte_printer printheader = [&](uint8_t val) { 40 | flushBits(bitBuffer, print); 41 | bitBuffer = 0; 42 | print(val); 43 | }; 44 | // This is called to emit a palette encoded value 45 | // So, in here, we have to look up the value and emit just the bits 46 | // bitBuffer is updated to contain anything that hasn't been finished 47 | word_printer printblobs = [&](uint16_t val) { 48 | uint16_t palIndex = pal_and_rev.second[val]; 49 | if (palIndex > (1 << numBits)) { 50 | std::cerr << "Bad bad bad" << std::endl; 51 | success = false; 52 | } 53 | bitBuffer = writeBits(palIndex, numBits, bitBuffer, print); 54 | }; 55 | // Now we use the rle engine, but use bit emission instead of word emission 56 | // to try to buy back more savings 57 | success = dump_rle(data, bytes, printheader, printblobs) && success; 58 | // Pick up any final blob of data 59 | flushBits(bitBuffer, print); 60 | return success; 61 | } 62 | -------------------------------------------------------------------------------- /src/modules/images/imgenc_rle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "imgencoder.h" 6 | 7 | /* 8 | This should spit out the encoded source array 9 | */ 10 | 11 | void dumpRLECount(bool repeat, uint32_t count, byte_printer print) { 12 | if (count == 0 || count > 0x800000) { 13 | std::cerr << "Derp" << std::endl; 14 | return; 15 | } 16 | // A count instruction is a number encoded with a high stop bit 17 | // The low bit is true if it's a repeat 18 | count = count * 2 + !!repeat; 19 | while (count) { 20 | uint8_t byte = count & 0x7f; 21 | count = count >> 7; 22 | print(byte | (count ? 0 : 0x80)); 23 | } 24 | } 25 | 26 | // it should work better for stuff that has strings of unique stuff along with 27 | // strings of repeated stuff 28 | bool encode_rle(bytestream data, uint32_t bytes, byte_printer print) { 29 | std::function print2 = [&](uint16_t val) { 30 | print(val & 0xFF); 31 | print((val >> 8) & 0xFF); 32 | }; 33 | return dump_rle(data, bytes, print, print2); 34 | } 35 | 36 | bool dump_rle(bytestream data, 37 | uint32_t bytes, 38 | byte_printer print, 39 | word_printer print2) { 40 | if (bytes & 1) { 41 | return false; 42 | } 43 | const uint16_t* colors = reinterpret_cast(data); 44 | const uint32_t cbytes = bytes / 2; 45 | for (uint32_t pos = 0; pos < cbytes;) { 46 | // First, check to see if this is unique, or dupe: 47 | uint16_t val = colors[pos]; 48 | if (pos + 1 == bytes) { 49 | // Special case last unique word 50 | dumpRLECount(false, 1, print); 51 | print2(colors[pos]); 52 | break; 53 | } 54 | uint16_t nxt = colors[pos + 1]; 55 | if (val == nxt) { 56 | // Scan forward to see how long the repeat goes 57 | uint32_t count = 1; 58 | while (pos + count < cbytes) { 59 | nxt = colors[pos + count]; 60 | if (nxt != val) { 61 | break; 62 | } else { 63 | count++; 64 | } 65 | } 66 | // We've got the count, dump the sequence 67 | dumpRLECount(true, count, print); 68 | print2(colors[pos]); 69 | pos += count; 70 | } else { 71 | // Scan forward to see how long the uniqueness goes 72 | uint32_t count = 1; 73 | val = nxt; 74 | while (pos + count < cbytes) { 75 | nxt = colors[pos + count + 1]; 76 | if (nxt == val) { 77 | break; 78 | } 79 | count++; 80 | val = nxt; 81 | } 82 | dumpRLECount(false, count, print); 83 | while (count--) { 84 | print2(colors[pos++]); 85 | } 86 | } 87 | } 88 | return true; 89 | } 90 | -------------------------------------------------------------------------------- /src/modules/images/include.mk: -------------------------------------------------------------------------------- 1 | IMG_MODULE_DIR=modules/images/ 2 | 3 | USER_INCLUDES += -I${IMG_MODULE_DIR} -I${IMG_MODULE_DIR}include 4 | USER_CPP_SRCS += $(addprefix ${IMG_MODULE_DIR}, image.cpp imgdec_pal.cpp imgdec_prle.cpp imgdec_rle.cpp) 5 | USER_CPP_SRCS += $(wildcard ${IMG_MODULE_DIR}bitmaps/*.cpp) 6 | 7 | VPATH += ${IMG_MODULE_DIR} ${IMG_MODULE_DIR}bitmaps/ 8 | -------------------------------------------------------------------------------- /src/modules/images/include/bitmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /* 6 | NQRLE: 7 | A list of stop-bit encoded values (Thus 0 - 2M in range), followed by data: 8 | An odd value means N / 2 bytes to repeat the next single encoded value 9 | An even value means N / 2 bytes to read in as unique string of pixels 10 | 11 | PAL_RAW: (NYI) 12 | First two bytes: little endian palette size (always less than 32768, or it's 13 | dumb) (palette_size) Next palette_size * 2 bytes: the palette entries 14 | log(palette_size) * width * height bits: pixel data 15 | 16 | PAL_NQRLE: (NYI) 17 | Same as PAL_RAW, but the pixel data is encoded as NQRLE, 18 | where data is log(palette_size) bits, with the end trimmed 19 | */ 20 | 21 | enum class image_compression : uint8_t { 22 | RAW = 0, 23 | NQRLE, // 16 bit NQRLE encoding 24 | PAL_RAW, // Palette encoding 25 | PAL_NQRLE, // Palette encoded as NQRLE data 26 | #if defined(STANDALONE) 27 | FIND_BEST, 28 | INVALID 29 | #endif 30 | }; 31 | 32 | struct image_descriptor { 33 | uint16_t width, height; 34 | uint32_t byte_count : 24; 35 | image_compression compression : 8; 36 | const uint8_t* image_data; 37 | }; 38 | 39 | using bytestream = const uint8_t*; 40 | using sender = void (*)(bytestream, uint16_t); 41 | using decoder = void (*)(bytestream, uint32_t, sender); 42 | 43 | void decode_rle(bytestream cmpStrm, uint32_t len, sender send); 44 | void decode_pal(bytestream cmpStrm, uint32_t len, sender send); 45 | void decode_prle(bytestream cmpStrm, uint32_t len, sender send); 46 | 47 | // Helpers 48 | uint8_t log2ish(uint16_t n); 49 | uint16_t readBits(uint8_t numBits, 50 | bytestream cmpStrm, 51 | uint32_t* i, 52 | uint8_t* curBit); 53 | uint16_t read16b(bytestream stream, uint32_t* ofs); 54 | uint32_t readStopBitNumber(bytestream cmpStrm, uint32_t* i); -------------------------------------------------------------------------------- /src/modules/images/include/imgencoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if !defined(STANDALONE) 4 | #error This is only meant to be used in the stand-alone image encoder/decoder thing 5 | #endif 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "bitmap.h" 14 | 15 | using byte_printer = std::function; 16 | using word_printer = std::function; 17 | bool encode_rle(bytestream data, uint32_t bytes, byte_printer print); 18 | bool encode_prle(bytestream data, uint32_t bytes, byte_printer print); 19 | bool encode_pal(bytestream data, uint32_t bytes, byte_printer print); 20 | using encoder = bool (*)(bytestream, uint32_t, byte_printer); 21 | 22 | struct cmdLine { 23 | image_compression cmpType; 24 | uint8_t imageToDecode; 25 | std::string filename; 26 | uint16_t width; 27 | uint16_t height; 28 | }; 29 | 30 | uint16_t writeBits(uint16_t value, 31 | uint8_t numBits, 32 | uint16_t bitBuffer, 33 | byte_printer print); 34 | void flushBits(uint16_t bitBuffer, byte_printer print); 35 | void dumpRLECount(bool repeat, uint32_t count, byte_printer print); 36 | bool dump_rle(bytestream data, 37 | uint32_t bytes, 38 | byte_printer print, 39 | word_printer print2); 40 | std::pair, std::vector> calculate_palette( 41 | const uint16_t* colors, uint32_t cbytes); 42 | 43 | extern const uint32_t builtin_count; 44 | bool validateArgs(cmdLine& ln); 45 | int usage(const std::string& name); 46 | int parseArgs(int argc, const char* argv[], cmdLine* ln); 47 | -------------------------------------------------------------------------------- /src/modules/menu/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_custom_module(menu . menu.cpp) 4 | target_link_libraries(module_menu PUBLIC Adafruit_ST77XX_lib module_display) -------------------------------------------------------------------------------- /src/modules/menu/include.mk: -------------------------------------------------------------------------------- 1 | MENU_MODULE_DIR=modules/menu/ 2 | 3 | USER_INCLUDES += -I${MENU_MODULE_DIR} 4 | USER_CPP_SRCS += $(addprefix ${MENU_MODULE_DIR}, menu.cpp) 5 | 6 | VPATH += ${MENU_MODULE_DIR} 7 | -------------------------------------------------------------------------------- /src/modules/menu/menu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if !defined(MENU_MODULE_H) 4 | #define MENU_MODULE_H 5 | 6 | #include 7 | 8 | #include "enumtypes.h" 9 | #include "usbenums.h" 10 | 11 | namespace menu { 12 | 13 | namespace impl { 14 | 15 | extern std::vector modes; 16 | 17 | void SelectHelper(); 18 | 19 | template 20 | void SelectHelper(KM km, R... rest) { 21 | modes.push_back(km); 22 | SelectHelper(rest...); 23 | } 24 | 25 | } // namespace impl 26 | 27 | // This configures, then displays the initial menu 28 | template 29 | void SetupModeList(Modes... r) { 30 | // add k to the list 31 | impl::modes.clear(); 32 | impl::SelectHelper(r...); 33 | } 34 | 35 | // This runs until a selection is made... 36 | KeyboardMode Handler(Keystroke ks, Modifiers mods, bool pressed, uint32_t now); 37 | 38 | } // namespace menu 39 | 40 | #endif -------------------------------------------------------------------------------- /src/modules/snake/disp_stuff.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace tetris { 5 | 6 | uint8_t xW, xO, yW, yO; 7 | uint8_t xN, yN; 8 | 9 | void calcDisplayValues(uint16_t height, uint16_t width) { 10 | yW = (height - 1) / 24; 11 | xW = (width - 1) / 10; 12 | yW = std::min(xW, yW); 13 | xW = yW; 14 | xO = std::min(10, (width - xW * 10) / 2); 15 | yO = std::min(10, height - yW * 24); 16 | // This is only going to work for a landscape oriented display: 17 | xN = xO * 5 + (xW * 23) / 2; 18 | yN = (yW * 5) / 2; 19 | } 20 | 21 | uint16_t getDispX(uint8_t x) { 22 | return xO + xW * x; 23 | } 24 | 25 | uint16_t getDispY(uint8_t y) { 26 | return yO + yW * y; 27 | } 28 | 29 | uint16_t getDispW() { 30 | return xW; 31 | } 32 | 33 | uint16_t getDispH() { 34 | return yW; 35 | } 36 | 37 | uint16_t getPrevX(uint8_t x) { 38 | return xN + xW * x; 39 | } 40 | 41 | uint16_t getPrevY(uint8_t y) { 42 | return yN + yW * y; 43 | } 44 | 45 | uint16_t getPrevW() { 46 | return xW * 3; 47 | } 48 | 49 | uint16_t getPrevH() { 50 | return yW * 4; 51 | } 52 | 53 | uint16_t getColor(uint8_t blk) { 54 | switch (blk) { 55 | case 0: 56 | return 0x0000; // Black 57 | case 1: 58 | return 0xF800; // Red 59 | case 2: 60 | return 0x001F; // Blue 61 | case 3: 62 | return 0x07E0; // Green 63 | case 4: 64 | return 0xFFE0; // Yellow 65 | case 5: 66 | return 0x07FF; // Cyan 67 | case 6: 68 | return 0xF81F; // Magenta 69 | case 7: 70 | return 0xFC00; // Orange 71 | default: 72 | return 0xFFFF; // White 73 | } 74 | } 75 | 76 | } // namespace tetris -------------------------------------------------------------------------------- /src/modules/snake/makesnake.mk: -------------------------------------------------------------------------------- 1 | TETRIS_MODULE_DIR=modules/tetris/ 2 | 3 | USER_INCLUDES += -I${TETRIS_MODULE_DIR} 4 | USER_CPP_SRCS += $(addprefix ${TETRIS_MODULE_DIR}, tetris.cpp board.cpp disp_stuff.cpp) 5 | 6 | VPATH += ${TETRIS_MODULE_DIR} 7 | -------------------------------------------------------------------------------- /src/modules/snake/snake.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "enumtypes.h" 4 | #include "usbenums.h" 5 | 6 | namespace snake { 7 | 8 | void Initialize(); 9 | KeyboardMode Handler(Keystroke ks, Modifiers m, bool pressed, uint32_t now); 10 | KeyboardMode Spin(KeyboardMode curMode, uint32_t now); 11 | 12 | } // namespace snake -------------------------------------------------------------------------------- /src/modules/standalone.mk: -------------------------------------------------------------------------------- 1 | # Stuff to make the stand-alone module test beds easier 2 | 3 | ifeq ($(OS),Windows_NT) 4 | # Decide if we've got VC++ around, which on Windows is preferred 5 | ifeq ($(VisualStudioVersion),) 6 | CXX=clang++ 7 | else 8 | CXX=cl.exe 9 | endif 10 | SFX=.exe 11 | SEP=\\ 12 | # Chocolatey install winflexbison3 :) 13 | BISON=bison 14 | CLEANPATHS=$(subst /,${SEP},$1) 15 | MKDIRPATH=$(subst /,${SEP},$1) 16 | RM=del /s /q 17 | else 18 | SFX= 19 | SEP=/ 20 | ifeq ($(shell uname -p), i386) 21 | BISON=/usr/local/opt/bison/bin/bison 22 | else 23 | BISON=/opt/homebrew/opt/bison/bin/bison 24 | endif 25 | CLEANPATHS=$(subst *,@,$1) 26 | MKDIRPATH=-p $1 27 | rm=rm -rf 28 | endif 29 | 30 | # Size is larger than -O3 lame lame lame, clang 31 | # OPTFLAGS=-flto -Oz 32 | # Speed: 33 | # OPTFLAGS=-flto -O3 34 | # Debug: 35 | ifeq (${CXX},cl.exe) 36 | OBJFLAG=/FS /Fo 37 | IMGFLAG=/FS /Fe 38 | OPTFLAGS=/Od /Zi /EHsc 39 | STDNUM=/std:c++20 40 | OSUFFIX=obj 41 | else 42 | OBJFLAG=-o 43 | IMGFLAG=-o 44 | OPTFLAGS=-g 45 | STDNUM=-std=c++20 46 | OSUFFIX=o 47 | endif 48 | 49 | DESTDIR = ../../out/${MODNAME}/ 50 | IMAGE = ${DESTDIR}${MODNAME}${SFX} 51 | 52 | ${MODNAME}: out/${MODNAME} ${IMAGE} 53 | 54 | .PHONY: clean ${MODNAME} 55 | 56 | out/${MODNAME}: 57 | ifeq ($(OS),Windows_NT) 58 | @if not exist $(call MKDIRPATH, ${DESTDIR}) mkdir $(call MKDIRPATH,${DESTDIR}) 2> NUL 1> NUL 59 | else 60 | test -d ${DESTDIR} || mkdir -p ${DESTDIR} 2>&1 > /dev/null 61 | endif 62 | 63 | CPP_OBJS = $(addprefix ${DESTDIR}, $(patsubst %.cpp, %.${OSUFFIX}, ${CPP_SRC})) 64 | 65 | ${DESTDIR}%.${OSUFFIX} : %.cpp 66 | ${CXX} ${OPTFLAGS} -I. -Iinclude -I../../include -DSTANDALONE ${STDNUM} -c $< ${OBJFLAG}$@ 67 | 68 | clean: 69 | -${RM} $(call CLEANPATHS, ${CPP_OBJS} ${MORE_CLEAN} ${IMAGE}) 70 | 71 | ${IMAGE}: ${CPP_OBJS} 72 | ${CXX} ${OPTFLAGS} ${IMGFLAG}$@ $^ 73 | -------------------------------------------------------------------------------- /src/modules/tetris/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_custom_module(tetris . tetris.cpp tetrisboard.cpp disp_stuff.cpp) 4 | target_link_libraries(module_tetris PUBLIC module_display) -------------------------------------------------------------------------------- /src/modules/tetris/disp_stuff.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace tetris { 5 | 6 | uint8_t xW, xO, yW, yO; 7 | uint8_t xN, yN; 8 | 9 | void calcDisplayValues(uint16_t height, uint16_t width) { 10 | yW = (height - 1) / 24; 11 | xW = (width - 1) / 10; 12 | yW = std::min(xW, yW); 13 | xW = yW; 14 | xO = std::min(10, (width - xW * 10) / 2); 15 | yO = std::min(10, height - yW * 24); 16 | // This is only going to work for a landscape oriented display: 17 | xN = xO * 5 + (xW * 23) / 2; 18 | yN = (yW * 5) / 2; 19 | } 20 | 21 | uint16_t getDispX(uint8_t x) { 22 | return xO + xW * x; 23 | } 24 | 25 | uint16_t getDispY(uint8_t y) { 26 | return yO + yW * y; 27 | } 28 | 29 | uint16_t getDispW() { 30 | return xW; 31 | } 32 | 33 | uint16_t getDispH() { 34 | return yW; 35 | } 36 | 37 | uint16_t getPrevX(uint8_t x) { 38 | return xN + xW * x; 39 | } 40 | 41 | uint16_t getPrevY(uint8_t y) { 42 | return yN + yW * y; 43 | } 44 | 45 | uint16_t getPrevW() { 46 | return xW * 3; 47 | } 48 | 49 | uint16_t getPrevH() { 50 | return yW * 4; 51 | } 52 | 53 | uint16_t getColor(uint8_t blk) { 54 | switch (blk) { 55 | case 0: 56 | return 0x0000; // Black 57 | case 1: 58 | return 0xF800; // Red 59 | case 2: 60 | return 0x001F; // Blue 61 | case 3: 62 | return 0x07E0; // Green 63 | case 4: 64 | return 0xFFE0; // Yellow 65 | case 5: 66 | return 0x07FF; // Cyan 67 | case 6: 68 | return 0xF81F; // Magenta 69 | case 7: 70 | return 0xFC00; // Orange 71 | default: 72 | return 0xFFFF; // White 73 | } 74 | } 75 | 76 | } // namespace tetris -------------------------------------------------------------------------------- /src/modules/tetris/maketetris.mk: -------------------------------------------------------------------------------- 1 | TETRIS_MODULE_DIR=modules/tetris/ 2 | 3 | USER_INCLUDES += -I${TETRIS_MODULE_DIR} 4 | USER_CPP_SRCS += $(addprefix ${TETRIS_MODULE_DIR}, tetris.cpp tetrisboard.cpp disp_stuff.cpp) 5 | 6 | VPATH += ${TETRIS_MODULE_DIR} 7 | -------------------------------------------------------------------------------- /src/modules/tetris/tetris.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "enumtypes.h" 4 | #include "usbenums.h" 5 | 6 | namespace tetris { 7 | 8 | void Initialize(); 9 | KeyboardMode Handler(Keystroke ks, Modifiers m, bool pressed, uint32_t now); 10 | KeyboardMode Spin(KeyboardMode curMode, uint32_t now); 11 | 12 | } // namespace tetris -------------------------------------------------------------------------------- /src/modules/tetris/tetrisboard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if !defined(TETRIS_BOARD_H) 3 | #define TETRIS_BOARD_H 4 | 5 | #include "enumhelpers.h" 6 | #include 7 | #include 8 | 9 | namespace tetris { 10 | 11 | enum class PieceName : uint8_t { I, L, J, O, T, S, Z, NumElems, Empty }; 12 | 13 | class Board { 14 | // Constants 15 | static constexpr uint16_t White = 0xFFFF; 16 | static constexpr uint16_t Black = 0; 17 | 18 | static constexpr uint8_t numPieces = value_cast(PieceName::NumElems); 19 | static constexpr uint8_t numLocs = 6; 20 | 21 | static constexpr int8_t WIDTH = 10; 22 | static constexpr int8_t HEIGHT = 24; 23 | static constexpr int8_t TOP_BUFFER = 4; 24 | static constexpr uint8_t BOARD_LEFT_OFFSET = 20; 25 | static constexpr uint8_t BOARD_TOP_OFFSET = 0; 26 | 27 | uint16_t PieceWidth; 28 | uint16_t PieceHeight; 29 | 30 | // data 31 | std::array board; 32 | uint32_t score; 33 | uint32_t lastDrawnScore; 34 | 35 | PieceName curPiece, nextPiece; 36 | PieceName lastDrawNextPiece; 37 | 38 | uint8_t rot, x, y; 39 | uint32_t lastDropTime; 40 | uint32_t lastDraw; 41 | uint32_t dropSpeed; 42 | uint16_t totalRows; 43 | 44 | void addScore(uint8_t v); 45 | PieceName getSpot(uint8_t x, uint8_t y); 46 | void setSpot(uint8_t x, uint8_t y, PieceName piece); 47 | void clrSpot(uint8_t x, uint8_t y); 48 | void drawDot(int8_t x, int8_t y, PieceName piece); 49 | void drawBoard(); 50 | void drawNext(); 51 | void drawScore(); 52 | void newPiece(); 53 | bool checkLoc(int8_t xx, int8_t yy); 54 | bool intersects(); 55 | void placePiece(); 56 | void removeLines(); 57 | bool gameOver(); 58 | void endGame(); 59 | void clearBoard(); 60 | 61 | public: 62 | Board(uint8_t w, uint8_t h); 63 | bool active(); 64 | void splash(); 65 | void draw(uint32_t now); 66 | void drawPiece(PieceName piece, uint8_t rot, uint8_t xc, uint8_t yc); 67 | bool down(uint32_t now); 68 | void left(); 69 | void right(); 70 | void rotCW(); 71 | void rotCCW(); 72 | void start(uint32_t now); 73 | }; 74 | 75 | } // namespace tetris 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/newLoop.cpp: -------------------------------------------------------------------------------- 1 | #include "sysstuff.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "newActionResult.h" 7 | #include "newKeyInput.h" 8 | #include "newKeyState.h" 9 | #include "newSystemState.h" 10 | 11 | // New 'thing' 12 | 13 | void reportInput(const KeyboardInput&) { 14 | // TODO 15 | } 16 | 17 | void resumeNormalMode(const SystemState&) { 18 | // TODO 19 | } 20 | 21 | SystemState normalLoop(const SystemState& systemState) { 22 | KeyState keyState = KeyState::acquire(); 23 | ActionResult actionResult = 24 | ActionResult::determineAction(keyState, systemState); 25 | if (actionResult.input.needsReported()) { 26 | reportInput(actionResult.input); 27 | } 28 | return actionResult.state; 29 | } 30 | 31 | SystemState modeLoop(const SystemState& systemState) { 32 | SystemState res = systemState.executeMode(); 33 | if (res.isNormalMode()) { 34 | resumeNormalMode(res); 35 | } 36 | return res; 37 | } 38 | 39 | SystemState state; 40 | void newMasterLoop() { 41 | state = normalLoop(state); 42 | while (!state.isNormalMode()) { 43 | state = modeLoop(state); 44 | } 45 | } 46 | 47 | /* 48 | ActionResult determineAction(SystemState) { 49 | LayerState layerState = determineLayer(keyState, &systemState); 50 | ActionList actionList = calculateActions(keyState, layerState, &systemState); 51 | OutputActions outputActions = aggregateActions(actionList, &systemState); 52 | performLocalActions(outputActions.local, &systemState); 53 | reportKeyboardActions(outputActions.keyboard); 54 | return systemState; 55 | } 56 | */ -------------------------------------------------------------------------------- /src/oldstuff/battery.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | 5 | template 6 | struct Battery { 7 | // pin 31 on the 832, pin 30 on the 840, is available for sampling the battery 8 | static constexpr uint8_t VBAT_PIN = batPin; 9 | 10 | static void ConfigBattery() { 11 | analogReference(AR_INTERNAL); 12 | analogReadResolution(12); 13 | delay(1); 14 | } 15 | 16 | // I ran the Adafruit 500mah LiPo battery down, logging values until it died 17 | // this is a general linear approximation, cuz I'm not good enough at math 18 | // anymore to do anything fancier :/ 19 | static uint8_t getBatteryPercent() { 20 | uint32_t bat = analogRead(VBAT_PIN); 21 | if (bat > 3460) { 22 | return 100; 23 | } else if (bat > 3255) { 24 | return 70 + 30 * (bat - 3255) / 205; 25 | } else if (bat > 3175) { 26 | return 45 + 25 * (bat - 3175) / 80; 27 | } else if (bat > 3110) { 28 | return 30 + 15 * (bat - 3110) / 65; 29 | } else if (bat > 3050) { 30 | return 10 * (bat - 3050) / 20; 31 | } else { 32 | return 0; 33 | } 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/oldstuff/client-comm.cpp: -------------------------------------------------------------------------------- 1 | #include "sysstuff.h" 2 | 3 | #include "boardio.h" 4 | #include "client-comm.h" 5 | #include "kbclient.h" 6 | 7 | void comm::send::scan(BLEUart& uart, const MatrixBits& b) { 8 | send_packet(uart, b); 9 | } 10 | 11 | void comm::send::battery(BLEUart& uart, uint8_t pct) { 12 | send_packet(uart, pct); 13 | } 14 | 15 | void comm::send::time(BLEUart& uart, uint32_t time) { 16 | send_packet(uart, time); 17 | } 18 | 19 | void comm::recv::data(uint16_t handle) { 20 | comm::header h; 21 | uint8_t buf[15]; 22 | buf[0] = waitForByte(KBClient::bleuart); 23 | DBG2(dumpHex(buf[0], "r:")); 24 | memcpy(reinterpret_cast(&h), &buf, 1); 25 | for (uint8_t i = 0; i < h.size; i++) { 26 | buf[i] = waitForByte(KBClient::bleuart); 27 | DBG2(dumpHex(buf[i], "r:")); 28 | } 29 | switch (h.type) { 30 | case comm::types::SYNC: 31 | comm::recv::sync(); 32 | break; 33 | case comm::types::SETLED: 34 | comm::recv::set_led(buf[0]); 35 | break; 36 | case comm::types::SETRED: 37 | comm::recv::set_red(buf[0]); 38 | break; 39 | case comm::types::SETBLUE: 40 | comm::recv::set_blue(buf[0]); 41 | break; 42 | default: 43 | // TODO: ERROR! 44 | break; 45 | } 46 | } 47 | void comm::recv::sync() { 48 | comm::send::time(KBClient::bleuart, millis()); 49 | } 50 | void comm::recv::set_led(uint8_t brightness) { 51 | BoardIO::setLED(brightness); 52 | } 53 | void comm::recv::set_red(bool on) { 54 | BoardIO::setRed(on); 55 | } 56 | void comm::recv::set_blue(bool on) { 57 | BoardIO::setBlue(on); 58 | } 59 | -------------------------------------------------------------------------------- /src/oldstuff/client-comm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "comm.h" 4 | 5 | namespace comm { 6 | namespace send { 7 | void scan(BLEUart& uart, const MatrixBits&); 8 | void battery(BLEUart& uart, uint8_t pct); 9 | void time(BLEUart& uart, uint32_t time); 10 | } // namespace send 11 | 12 | namespace recv { 13 | void data(uint16_t handle); // BLEUart& uart); 14 | void sync(); 15 | void set_led(uint8_t brightness); 16 | void set_red(bool on); 17 | void set_blue(bool on); 18 | 19 | } // namespace recv 20 | 21 | } // namespace comm 22 | -------------------------------------------------------------------------------- /src/oldstuff/comm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | 5 | #include 6 | 7 | #include "boardio.h" 8 | 9 | namespace comm { 10 | 11 | constexpr uint8_t LEFT_SIDE = 0; 12 | constexpr uint8_t RIGHT_SIDE = 1; 13 | #if defined(LEFT) 14 | constexpr uint8_t WHICH_SIDE = LEFT_SIDE; 15 | #elif defined(RIGHT) 16 | constexpr uint8_t WHICH_SIDE = RIGHT_SIDE; 17 | #endif 18 | 19 | // Message types (only 3 bits, currently) 20 | enum class types : uint8_t { 21 | SCAN = 0, 22 | BATTERY = 1, 23 | TIME = 2, 24 | SYNC = 3, 25 | SETLED = 4, 26 | SETRED = 5, 27 | SETBLUE = 6, 28 | }; 29 | 30 | // Get the packet size of the different messages above 31 | constexpr uint8_t types_size(types t) { 32 | switch (t) { 33 | case types::SCAN: 34 | return MatrixBits::num_bytes; 35 | case types::BATTERY: 36 | case types::SETLED: 37 | case types::SETRED: 38 | case types::SETBLUE: 39 | return 1; 40 | case types::TIME: 41 | return sizeof(uint32_t); 42 | case types::SYNC: 43 | return 0; 44 | }; 45 | } 46 | 47 | #if defined(DEBUG) && DEBUG > 1 48 | // String name for diagnostics... 49 | constexpr const char* get_type_name(types t) { 50 | switch (t) { 51 | case types::SCAN: 52 | return "Scan"; 53 | case types::BATTERY: 54 | return "Battery"; 55 | case types::SETLED: 56 | return "SetLED"; 57 | case types::SETRED: 58 | return "SetRed"; 59 | case types::SETBLUE: 60 | return "SetBlue"; 61 | case types::TIME: 62 | return "Time"; 63 | case types::SYNC: 64 | return "Sync"; 65 | } 66 | return ""; 67 | } 68 | #endif 69 | 70 | struct header { 71 | uint8_t side : 1; 72 | types type : 3; 73 | uint8_t size : 4; 74 | }; 75 | 76 | } // namespace comm 77 | 78 | template 79 | uint8_t getSide(UART& uart) { 80 | #if defined(BTLE_HOST) 81 | return comm::LEFT_SIDE; // TODO: Fix this 82 | #else 83 | return comm::WHICH_SIDE; 84 | #endif 85 | } 86 | 87 | template 88 | void send_packet(UART& uart, const T& v) { 89 | comm::header h; 90 | char buffer[sizeof(h) + sizeof(T)]; 91 | h.side = getSide(uart); 92 | h.type = VAL; 93 | h.size = comm::types_size(VAL); 94 | memcpy(&buffer[0], reinterpret_cast(&h), sizeof(h)); 95 | memcpy(&buffer[sizeof(h)], reinterpret_cast(&v), sizeof(T)); 96 | #if defined(DEBUG) && DEBUG > 1 97 | for (int i = 0; i < sizeof(buffer); i++) { 98 | dumpHex((uint32_t)buffer[i], "S:"); 99 | } 100 | Serial.println("---"); 101 | #endif 102 | uart.write(buffer, sizeof(buffer)); 103 | }; 104 | 105 | template 106 | void send_packet(UART& uart) { 107 | comm::header h; 108 | h.side = getSide(uart); 109 | h.type = VAL; 110 | static_assert(comm::types_size(VAL) == 0); 111 | h.size = 0; 112 | DBG2(dumpHex((uint8_t)(*(reinterpret_cast(&h))), "SB:")); 113 | uart.write(*reinterpret_cast(&h)); 114 | }; 115 | 116 | template 117 | uint8_t waitForByte(UART& uart) { 118 | while (!uart.available()) { 119 | delayMicroseconds(5); 120 | } 121 | return uart.read(); 122 | } 123 | -------------------------------------------------------------------------------- /src/oldstuff/dongle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | 5 | #if defined(HAS_DISPLAY) 6 | #include 7 | #endif 8 | 9 | #include 10 | #include 11 | 12 | using MacroBits = std::bitset<6>; 13 | 14 | class Dongle { 15 | 16 | static const uint32_t ScreenWidth = 128; 17 | static const uint32_t ScreenHeight = 32; 18 | static const uint8_t OledResetPin = 13; 19 | 20 | static uint32_t connect_time; 21 | static bool black; 22 | static Adafruit_NeoPixel neopix; 23 | static Adafruit_USBD_HID usb_hid; 24 | static uint16_t leftHandle; 25 | static uint16_t rightHandle; 26 | 27 | public: 28 | #if defined(HAS_DISPLAY) 29 | static Adafruit_SSD1306 display; 30 | #endif 31 | static BLEClientUart leftUart; 32 | static BLEClientUart rightUart; 33 | 34 | static void blink(bool leaveOn = true); 35 | static void Configure(); 36 | static void StartListening(); 37 | static void Reset(); 38 | static bool Ready(); 39 | static bool BothSides(); 40 | static void ReportKeys(uint8_t mods, uint8_t* report); 41 | static void ConsumerPress(uint16_t keycode); 42 | static void ConsumerRelease(); 43 | 44 | static void setRGB(uint8_t r, uint8_t g, uint8_t b); 45 | static void setRGB(uint32_t rgb); 46 | static void setRed(bool on); 47 | static void setBlue(bool on); 48 | static void blinkRGB(uint8_t r, uint8_t g, uint8_t b, uint16_t length = 250); 49 | 50 | #if defined(MACRO_PAD) 51 | static MacroBits key_scan(uint32_t now); 52 | #endif 53 | static void updateClientStatus(uint32_t now, 54 | uint8_t batLeft, 55 | uint8_t batRight); 56 | 57 | // Callbacks from the Bluefruit runtime 58 | static void cent_connect(uint16_t conn_handle); 59 | static void cent_disconnect(uint16_t conn_handle, uint8_t reason); 60 | static void scan(ble_gap_evt_adv_report_t* report); 61 | static void hid_report_callback(uint8_t report_id, 62 | hid_report_type_t report_type, 63 | uint8_t const* buffer, 64 | uint16_t bufsize); 65 | static void receive_callback(BLEClientUart& uart_svc); 66 | }; 67 | -------------------------------------------------------------------------------- /src/oldstuff/hardware.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | #include 5 | 6 | #include "boardio.h" 7 | #include "dbgcfg.h" 8 | #include "debounce.h" 9 | 10 | #if !defined(TEENSY) 11 | constexpr const char* MANUFACTURER = "FreikyStuff"; 12 | 13 | #if defined(KARBON) 14 | constexpr const char* MODEL = "Karbon"; 15 | constexpr const char* BT_NAME = "Karbon"; 16 | constexpr const char* HW_REV = "0001"; 17 | constexpr const char* LHS_NAME = "Karbon"; 18 | constexpr const char* LTCL_NAME = "Karbon-Left"; 19 | constexpr const char* RTCL_NAME = "Karbon-Right"; 20 | #elif defined(FREIKEYS) 21 | constexpr const char* MODEL = "FreiKeyboard"; 22 | constexpr const char* BT_NAME = "FreiKeys"; 23 | constexpr const char* HW_REV = "0001"; 24 | constexpr const char* LHS_NAME = "FreiKeys-Slave"; 25 | constexpr const char* LTCL_NAME = "FreiKeys-LClient"; 26 | constexpr const char* RTCL_NAME = "FreiKeys-RClient"; 27 | #else 28 | #error You need to create a bluetooth name setup 29 | #endif 30 | 31 | #if defined(BTLE_CLIENT) 32 | #if defined(LEFT) 33 | constexpr const char* BTLE_CLIENT_NAME = LTCL_NAME; 34 | #elif defined(RIGHT) 35 | constexpr const char* BTLE_CLIENT_NAME = RTCL_NAME; 36 | #else 37 | #error For a client build, you need to pick left or right 38 | #endif 39 | #endif 40 | #endif 41 | 42 | namespace state { 43 | #if defined(BTLE_HOST) 44 | struct incoming { 45 | BLEClientUart* which; 46 | // TODO: Tag it with 'when' to synchronize the sides better 47 | struct hw* what; 48 | }; 49 | extern std::queue data_queue; 50 | #endif 51 | 52 | // This struct is to encapsulate the complete hardware state, including both 53 | // which switches are down, as well as the current battery level. 54 | struct hw { 55 | MatrixBits switches; 56 | #if !defined(TEENSY) 57 | uint8_t battery_level; 58 | #endif 59 | static constexpr std::size_t data_size = MatrixBits::num_bytes + 1; 60 | // This is just a dumb constructor 61 | hw(uint8_t bl = 0); 62 | 63 | #if defined(BTLE_HOST) 64 | // This is for reading the data from remote pieces over the UART 65 | hw(BLEClientUart& clientUart, const hw& prev); 66 | // Try to receive any relevant switch data from the wire. 67 | // Returns true if something was received 68 | bool receive(BLEClientUart& clientUart, const hw& prev); 69 | #else 70 | Debouncer debouncer; 71 | // This is for reading the data from the hardware 72 | hw(uint32_t now, const hw& prev); 73 | // Just reads the switches... 74 | void readSwitches(uint32_t now); 75 | #endif 76 | 77 | #if defined(BTLE_CLIENT) 78 | // Send the relevant data over the wire 79 | void send(BLEUart& bleuart, const hw& prev) const; 80 | #endif 81 | 82 | // Generic copy constructor... 83 | hw(const hw& c); 84 | 85 | bool operator==(const hw& o) const; 86 | bool operator!=(const hw& o) const; 87 | 88 | // A little helper for serial port dumping... 89 | #if defined(DEBUG) 90 | void dump() const; 91 | #endif 92 | }; 93 | } // namespace state 94 | -------------------------------------------------------------------------------- /src/oldstuff/host-comm.cpp: -------------------------------------------------------------------------------- 1 | #include "sysstuff.h" 2 | 3 | #include "boardio.h" 4 | #include "dongle.h" 5 | #include "general.h" 6 | #include "hardware.h" 7 | #include "host-comm.h" 8 | #include "sync.h" 9 | 10 | bool waiting; 11 | uint32_t locTime; 12 | GeneralState curState{}; 13 | 14 | void comm::send::sync(BLEClientUart& uart) { 15 | waiting = true; 16 | locTime = millis(); 17 | DBG2(dumpVal(locTime, "Local time at sync(1) send")); 18 | send_packet(uart); 19 | } 20 | 21 | void comm::send::set_led(BLEClientUart& uart, uint8_t brightness) { 22 | send_packet(uart, brightness); 23 | } 24 | 25 | void comm::send::set_red(BLEClientUart& uart, bool on) { 26 | send_packet(uart, on); 27 | } 28 | 29 | void comm::send::set_blue(BLEClientUart& uart, bool on) { 30 | send_packet(uart, on); 31 | } 32 | 33 | uint8_t whichOne(BLEClientUart& uart) { 34 | return (&uart == &Dongle::leftUart) ? comm::LEFT_SIDE : comm::RIGHT_SIDE; 35 | } 36 | 37 | void comm::recv::data(BLEClientUart& uart) { 38 | comm::header h; 39 | uint8_t buf[15]; 40 | buf[0] = waitForByte(uart); 41 | memcpy(reinterpret_cast(&h), &buf, 1); 42 | DBG2(Serial.printf("Received %02X (Side: %d Type: %s Size: %d)\nData: ", 43 | (int)buf[0], 44 | (int)h.side, 45 | comm::get_type_name(h.type), 46 | (int)h.size)); 47 | for (uint8_t i = 0; i < h.size; i++) { 48 | buf[i] = waitForByte(uart); 49 | DBG2(Serial.printf("%02X ", (int)buf[i])); 50 | } 51 | DBG2(Serial.println("")); 52 | switch (h.type) { 53 | case comm::types::SCAN: { 54 | MatrixBits b; 55 | b.write(buf); 56 | comm::recv::scan(whichOne(uart), b); 57 | } break; 58 | case comm::types::BATTERY: 59 | comm::recv::battery(whichOne(uart), buf[0]); 60 | break; 61 | case comm::types::TIME: { 62 | uint32_t time; 63 | memcpy(reinterpret_cast(&time), buf, sizeof(time)); 64 | comm::recv::time(whichOne(uart), time); 65 | } break; 66 | default: 67 | // TODO: ERROR! 68 | break; 69 | } 70 | } 71 | 72 | void comm::recv::scan(uint8_t which, const MatrixBits& b) { 73 | // Put the data in the queue 74 | state::hw* newData = new state::hw; 75 | bool isLeft = which == comm::LEFT_SIDE; 76 | DBG2(b.dumpHex(isLeft ? "Left Scan " : "Right Scan ")); 77 | newData->switches = b; 78 | newData->battery_level = 79 | isLeft ? curState.left.battery : curState.right.battery; 80 | state::data_queue.push( 81 | {isLeft ? &Dongle::leftUart : &Dongle::rightUart, newData}); 82 | } 83 | 84 | void comm::recv::battery(uint8_t which, uint8_t pct) { 85 | if (which == comm::LEFT_SIDE) { 86 | curState.left.battery = pct; 87 | } else { 88 | curState.right.battery = pct; 89 | } 90 | } 91 | 92 | void comm::recv::time(uint8_t which, uint32_t time) { 93 | waiting = false; 94 | timeSync.ReportSync(which == comm::LEFT_SIDE); 95 | uint32_t locUpdate = millis(); 96 | if (which == comm::LEFT_SIDE) { 97 | DBG2(dumpVal(time, "Left time ")); 98 | // digitalWrite(LED_BLUE, HIGH); 99 | } else { 100 | DBG2(dumpVal(time, "Right time ")); 101 | // digitalWrite(LED_RED, HIGH); 102 | } 103 | DBG2(dumpVal(locUpdate, "Local time ")); 104 | DBG2(dumpVal(locUpdate - locTime, "Latency: ")); 105 | } 106 | -------------------------------------------------------------------------------- /src/oldstuff/host-comm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "comm.h" 4 | 5 | namespace comm { 6 | namespace send { 7 | void sync(BLEClientUart& uart); 8 | void set_led(BLEClientUart& uart, uint8_t brightness); 9 | void set_red(BLEClientUart& uart, bool on); 10 | void set_blue(BLEClientUart& uart, bool on); 11 | } // namespace send 12 | 13 | namespace recv { 14 | void data(BLEClientUart& uart); 15 | void scan(uint8_t which, const MatrixBits&); 16 | void battery(uint8_t which, uint8_t pct); 17 | void time(uint8_t which, uint32_t time); 18 | } // namespace recv 19 | 20 | } // namespace comm 21 | -------------------------------------------------------------------------------- /src/oldstuff/kbclient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | 5 | #include "dbgcfg.h" 6 | #include "hardware.h" 7 | 8 | class KBClient { 9 | static BLEDis bledis; 10 | static uint32_t stateTime; 11 | static state::hw lastRead; 12 | static uint16_t noChanges; 13 | static constexpr uint16_t CHANGE_COUNT_BEFORE_SLEEP = 1000; 14 | 15 | static volatile bool interruptsEnabled; 16 | static volatile bool interruptTriggered; 17 | 18 | static void enableInterrupts(); 19 | static void disableInterrupts(); 20 | static void interruptHandler(); 21 | 22 | static void connect_callback(uint16_t conn_hdl); 23 | static void disconnect_callback(uint16_t conn_hdl, uint8_t reason); 24 | 25 | public: 26 | static BLEUart bleuart; 27 | 28 | static void setup(const char* name); 29 | static void loop(); 30 | }; 31 | -------------------------------------------------------------------------------- /src/oldstuff/led_states.cpp: -------------------------------------------------------------------------------- 1 | #include "led_states.h" 2 | #include "boardio.h" 3 | 4 | namespace state { 5 | 6 | uint32_t batteryFlasher(const state::hw& sw, uint32_t time_offset) { 7 | if (sw.battery_level > 15) { 8 | return abs(16 - (time_offset / 4) & 31); 9 | } else { 10 | return 10; 11 | } 12 | } 13 | 14 | // TODO: make some blinky states to indicate what mode the keyboard is in 15 | uint32_t macMode(const state::hw& sw, uint32_t time_offset) { 16 | return (time_offset & 0x40) ? 10 : 0; 17 | } 18 | 19 | // TODO: make some blinky states to indicate what mode the keyboard is in 20 | uint32_t winMode(const state::hw& sw, uint32_t time_offset) { 21 | return (time_offset & 0x20) ? 10 : 0; 22 | } 23 | 24 | const led key_states[] = { 25 | // These are the lower & outer 3 keys to get the battery status 26 | {BoardIO::bits{0x01, 0x04, 0x08, 0, 0, 0}, 27 | BoardIO::bits{0, 0x10, 0x10, 0x20, 0, 0}, 28 | batteryFlasher, 29 | 1000}}; 30 | 31 | const led* led::get(const state::hw& sw, uint8_t layer) { 32 | for (auto& st : key_states) { 33 | if (sw.switches == ((!layer) ? st.left_state : st.right_state)) { 34 | return &st; 35 | } 36 | } 37 | return nullptr; 38 | } 39 | 40 | } // namespace state 41 | -------------------------------------------------------------------------------- /src/oldstuff/led_states.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "boardio.h" 4 | #include "hardware.h" 5 | 6 | namespace state { 7 | 8 | struct led { 9 | MatrixBits left_state; 10 | MatrixBits right_state; 11 | uint32_t (*get_led_value)(const state::hw& switches, uint32_t time_offset); 12 | uint32_t time; 13 | static const led* get(const state::hw& switches, uint8_t layer = 0); 14 | }; 15 | 16 | } // namespace state 17 | -------------------------------------------------------------------------------- /src/oldstuff/leds.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | 5 | template 6 | struct Analog_LED { 7 | static void ConfigLED() { 8 | pinMode(pinLED, OUTPUT); 9 | delay(1); 10 | analogWrite(pinLED, 0); 11 | } 12 | static void setLED(uint32_t brightness) { 13 | analogWrite(pinLED, brightness); 14 | } 15 | }; 16 | 17 | struct No_Analog_LED { 18 | static void setLED(uint32_t brightness) { 19 | // Do nothing: Maybe in the future do something else? 20 | } 21 | }; 22 | 23 | template 24 | struct Digital_LEDs { 25 | static void ConfigLEDs() { 26 | pinMode(tRED, OUTPUT); 27 | pinMode(tBLUE, OUTPUT); 28 | } 29 | static void setRed(bool on) { 30 | digitalWrite(tRED, on ? HIGH : LOW); 31 | } 32 | static void setBlue(bool on) { 33 | digitalWrite(tBLUE, on ? HIGH : LOW); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/oldstuff/sleepstate.cpp: -------------------------------------------------------------------------------- 1 | #include "sleepstate.h" 2 | #include "boardio.h" 3 | #include "hardware.h" 4 | 5 | bool SleepState::CheckForSleeping(MatrixBits& switches, uint32_t time) { 6 | // If we're in forced sleep mode, don't wake up 7 | if (!forced) { 8 | // First, handle sleeping states 9 | if (switches.any()) { 10 | // We detected a keypress! 11 | if (sleeping) { 12 | // Turn off the LED if we were sleeping 13 | BoardIO::setLED(0); 14 | DBG(dumpVal(lastPressTime, "Exiting sleep from ")); 15 | } 16 | sleeping = false; 17 | lastPressTime = time; 18 | } else if (!sleeping && (time - lastPressTime > 300000)) { 19 | // 5 minutes before we sleep 20 | // Do other stuff to get into low power mode, here! 21 | sleeping = true; 22 | DBG(dumpVal(time, "Entering sleep from ")); 23 | } 24 | } 25 | if (sleeping || forced) { 26 | // Blink the LED a little bit 27 | uint8_t brightness = !((time >> 9) & 3); 28 | BoardIO::setLED(brightness); 29 | } 30 | return sleeping; 31 | } 32 | 33 | void SleepState::BeginForcedSleepMode() { 34 | DBG(dumpVal(lastPressTime, "Forced sleep at ")); 35 | forced = true; 36 | } 37 | 38 | void SleepState::EndForcedSleepMode() { 39 | DBG(dumpVal(lastPressTime, "Forced sleep stopped at ")); 40 | forced = false; 41 | sleeping = true; 42 | } 43 | -------------------------------------------------------------------------------- /src/oldstuff/sleepstate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "boardio.h" 4 | #include "hardware.h" 5 | 6 | struct SleepState { 7 | uint32_t lastPressTime; 8 | bool sleeping; 9 | bool forced; 10 | // This updates the SleepState object to reflect the current sleep state 11 | // It also sets (or clears) the LED to indicate sleep 12 | bool CheckForSleeping(MatrixBits& switches, uint32_t time); 13 | void BeginForcedSleepMode(); 14 | void EndForcedSleepMode(); 15 | }; 16 | -------------------------------------------------------------------------------- /src/oldstuff/status_dump.cpp: -------------------------------------------------------------------------------- 1 | #if defined(STATUS_DUMP) 2 | 3 | #include "sysstuff.h" 4 | #include 5 | 6 | #include "globals.h" 7 | #include "hardware.h" 8 | #include "keyhelpers.h" 9 | #include "status_dump.h" 10 | 11 | // TODO: Expose this stuff somehow. This is a disgusting hack to get at some 12 | // necessary state, and it makes me slightly queasy 13 | 14 | extern BLEHidAdafruit hid; 15 | extern layer_t* layer_stack; 16 | extern layer_t layer_pos; 17 | extern const char* layer_names[]; 18 | 19 | // If you hold this configuration down, it types out status 20 | // Lowest out, and the key directly above it 21 | constexpr uint64_t status_keys_left = 0x10200000000ULL; 22 | // Thumb low edge and right most bottom row 23 | constexpr uint64_t status_keys_right = 0x1020000000ULL; 24 | // If you hold this down, just on the right keyboard, it shows RHS status only 25 | // Sameas above, just add the rightmost key on the second to bottom row 26 | constexpr uint64_t just_right_stat = 0x1030000000ULL; 27 | 28 | // I used to implement this all myself, but then I discovered it was alread in 29 | // the hid class :) 30 | void type_string(const char* str) { 31 | hid.keySequence(str); 32 | } 33 | 34 | // Interview question ahead! 35 | void type_number(uint32_t val) { 36 | char buffer[25]; 37 | int curPos = sizeof(buffer) - 1; 38 | buffer[curPos] = 0; 39 | do { 40 | buffer[--curPos] = val % 10 + '0'; 41 | val = val / 10; 42 | } while (val && curPos); 43 | type_string(&buffer[curPos]); 44 | } 45 | const layer_t maxLayers = 5; 46 | bool status_dump_check(const state::hw& rightSide, const state::hw& leftSide) { 47 | bool justRight = rightSide.switches == just_right_stat; 48 | bool leftCheck = leftSide.switches == status_keys_left; 49 | bool rightCheck = rightSide.switches == status_keys_right; 50 | // Check for hardware request thingamajig: 51 | // This is hard coded, mostly because I'm just hacking 52 | if (justRight || (leftCheck && rightCheck)) { 53 | if (!justRight) { 54 | type_string("Lbat:"); 55 | type_number(leftSide.battery_level); 56 | type_string("% "); 57 | } 58 | type_string("Rbat: "); 59 | type_number(rightSide.battery_level); 60 | type_string("% Layer: "); 61 | for (uint8_t i = 0; i <= std::min(layer_pos, maxLayers); i++) { 62 | uint8_t layerLoc = std::min(layer_stack[i], maxLayers); 63 | type_string(layer_names[layerLoc]); 64 | type_string(" ("); 65 | type_number(i); 66 | type_string(i == layer_pos ? ")" : "), "); 67 | } 68 | DBG(Bluefruit.printInfo()); 69 | DBG(dumpHex(Bluefruit.connHandle(), "Connection handle: ")); 70 | DBG(dumpHex(Bluefruit.connPaired(), "Connection paired: ")); 71 | DBG(dumpHex(core_handle, "Core Connection handle: ")); 72 | return true; 73 | } 74 | return false; 75 | } 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/oldstuff/status_dump.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(STATUS_DUMP) 4 | #include "hardware.h" 5 | 6 | // Types the "str" string (with lots of limitations) 7 | void type_string(const char* str); 8 | 9 | // Types the number 10 | void type_number(uint32_t val); 11 | 12 | // Checks to see if the user is pushing magic keys. If so, do something with it. 13 | // return true to onot report the keys to the HID... 14 | bool status_dump_check(const state::hw& rightSide, const state::hw& leftSide); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/oldstuff/sync.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sysstuff.h" 4 | 5 | #include 6 | 7 | #include "hardware.h" 8 | 9 | class Sync { 10 | enum class State : uint8_t { 11 | BothNotConnected, 12 | BothSidesFirstConnected, 13 | StartFullSync, 14 | LeftSent, 15 | LeftReceived, 16 | RightSent, 17 | RightReceived, 18 | EndFullSync, 19 | BothSidesConnectedBoring, 20 | BothSidesConnectedPing, 21 | StartPing, 22 | LRSent, 23 | LRReceived, 24 | EndPing 25 | }; 26 | static constexpr size_t sampleSize = 10; 27 | // Circular buffers of recent latencies 28 | std::array lsamples; 29 | std::array rsamples; 30 | uint8_t lLoc, rLoc; 31 | State state; 32 | bool isLeft; 33 | bool leftPacketReceived, rightPacketReceived; 34 | uint32_t lastTapOrPing; 35 | uint32_t sendTime; 36 | 37 | bool dataWaiting; 38 | uint32_t dataTime; 39 | MatrixBits delayData; 40 | uint8_t DELAY; 41 | bool Buffer(uint32_t time, state::hw& left, state::hw& right); 42 | void Delay(uint32_t time, state::hw& down, state::hw& prev); 43 | 44 | public: 45 | Sync() 46 | : lLoc(0), 47 | rLoc(0), 48 | state(State::BothNotConnected), 49 | rightPacketReceived(false), 50 | leftPacketReceived(false), 51 | lastTapOrPing(0), 52 | DELAY(0), 53 | dataWaiting(false) {} 54 | void ReportSync(bool isLeft); 55 | void UpdateLatency(); 56 | void Process(uint32_t time, 57 | state::hw& left, 58 | state::hw& prevL, 59 | state::hw& right, 60 | state::hw& prevR); 61 | }; 62 | 63 | extern Sync timeSync; 64 | -------------------------------------------------------------------------------- /src/oldstuff/timesync.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct TimeSyncEntry { 5 | uint32_t ms; 6 | uint32_t us; 7 | }; 8 | 9 | constexpr size_t BufferSize = 8; 10 | 11 | class TimeSync { 12 | std::array left; 13 | std::array right; 14 | uint8_t leftCount; 15 | uint8_t rightCount; 16 | 17 | static void registerTime(const TimeSyncEntry& tse, 18 | std::array& times, 19 | uint8_t& count) { 20 | // TODO: Pick the right entry to remove 21 | uint8_t elem = (count == BufferSize) ? tse.us % BufferSize : count; 22 | times[elem] = tse; 23 | } 24 | 25 | public: 26 | TimeSync() : leftCount(0), rightCount(0) {} 27 | void RegisterTime(bool isLeft, const TimeSyncEntry& tse) { 28 | if (isLeft) { 29 | registerTime(tse, left, leftCount); 30 | } else { 31 | registerTime(tse, right, rightCount); 32 | } 33 | } 34 | }; -------------------------------------------------------------------------------- /src/remotescan.cpp: -------------------------------------------------------------------------------- 1 | #include "sysstuff.h" 2 | 3 | #include "dbgcfg.h" 4 | #include "keystate.h" 5 | #include "scanner.h" 6 | 7 | #define right Serial2 8 | #define left Serial4 9 | 10 | void Scanner::Reset() {} 11 | 12 | Scanner::Scanner(uint32_t now) {} 13 | 14 | scancode_t validate(uint8_t b, bool& pressed) { 15 | Dbg2 << "Validating Scan code 0x" << sfmt::hex << b << sfmt::endl; 16 | b--; 17 | uint8_t sc = b / 3; 18 | uint8_t chk = b % 3; 19 | if (sc % 3 != chk) { 20 | // Error! 21 | Dbg2 << "Invalid scan code received 0x" << sfmt::hex << b << sfmt::endl; 22 | return 0xFF; 23 | } else { 24 | pressed = (sc < 36); // TODO: Encode all this shit somewhere? 25 | Dbg2 << "Pressed: " << (pressed ? 1 : 0) << sfmt::endl; 26 | Dbg2 << "Raw scan code 0x" << sfmt::hex << sc << sfmt::endl; 27 | if (!pressed) { 28 | sc -= 36; 29 | } 30 | Dbg2 << "Validated scan code 0x" << sfmt::hex << sc << sfmt::endl; 31 | return sc; 32 | } 33 | } 34 | 35 | // Template specialization for remote modules 36 | scancode_t Scanner::getNextCode(bool& pressed) { 37 | scancode_t sc = 0xFF; 38 | if (left.available()) { 39 | sc = validate(left.read(), pressed); 40 | if (sc != 0xFF) { 41 | sc = (sc / 6) * 12 + 5 - sc % 6; 42 | } 43 | } else if (right.available()) { 44 | sc = validate(right.read(), pressed); 45 | if (sc != 0xFF) { 46 | sc = (sc / 6) * 12 + 11 - sc % 6; 47 | } 48 | } else { 49 | return 0xFF; 50 | } 51 | Dbg2 << "Pressed: " << (pressed ? 1 : 0) << sfmt::endl; 52 | return sc; 53 | } 54 | 55 | void Scanner::Done() {} -------------------------------------------------------------------------------- /src/tools/adafruit-make-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "transforms": [ 3 | { 4 | "defmatch": "_CPP_FLAGS", 5 | "text": "-std=gnu++11", 6 | "replace": "-std=gnu++17" 7 | } 8 | ], 9 | "filters": [{ "defmatch": "CPP_SYS_SRCS", "remove": "BLEMidi.cpp" }] 10 | } 11 | -------------------------------------------------------------------------------- /src/tools/gen-makefiles.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import fs from 'fs'; 3 | import os from 'os'; 4 | import path from 'path'; 5 | import { hasField } from '@freik/typechk'; 6 | import main from '@freik/arduino2proj/lib/main.js'; 7 | 8 | const teensyVersion = '1.58.1'; 9 | 10 | const data = { 11 | win32: { 12 | outputSuffix: 'win', 13 | // Updated for the Arduino IDE install location: 14 | platformPath: path.join( 15 | os.homedir(), 16 | 'AppData', 17 | 'Local', 18 | 'Arduino15', 19 | 'packages', 20 | ), 21 | }, 22 | darwin: { 23 | outputSuffix: 'mac', 24 | platformPath: '/Applications/Teensyduino.app/Contents/Java', 25 | }, 26 | linux: { 27 | outputSuffix: 'lin', 28 | platformPath: path.join(process.env.HOME || '', 'Apps/arduino-1.8.19'), 29 | }, 30 | }; 31 | 32 | const teensyLoc = ['teensy', 'hardware', 'avr', teensyVersion]; 33 | 34 | const key = os.platform(); 35 | if (!hasField(data, key)) { 36 | console.error( 37 | `Please configure this script (${process.argv[1]}) for the ${key} platform.`, 38 | ); 39 | process.exit(-1); 40 | } 41 | 42 | const { outputSuffix, platformPath } = data[key]; 43 | const plat = path.join(...[platformPath, ...teensyLoc]); 44 | if (!fs.existsSync(plat)) { 45 | console.error( 46 | `${plat} doesn't exist: Make sure you've got stuff configured properly`, 47 | ); 48 | process.exit(-2); 49 | } 50 | process.chdir('src'); 51 | if (process.argv.length < 3 || process.argv.includes('teensy')) { 52 | const args = [ 53 | '-o', 54 | 'tools/teensy.' + outputSuffix, 55 | '-c', 56 | 'tools/teensy-make-config.json', 57 | plat, 58 | 'libs/SdFat', 59 | 'libs/GFX', 60 | 'libs/ST77XX', 61 | 'libs/BusIO', 62 | 'libs/T4_PXP', 63 | 'libs/GFX_Buffer', 64 | 'libs/AsyncDMA', 65 | 'libs/8875', 66 | ]; 67 | console.log('Running with these args:'); 68 | console.log(args); 69 | main(...args); 70 | } 71 | /* 72 | if (process.argv.length < 3 || process.argv.includes('nrf52')) { 73 | main( 74 | '-o', 'tools/af_nrf52.' + outputSuffix, 75 | '-c','tools/adafruit-make-config.json', 76 | 'libs/nRF52_Adafruit', 77 | 'libs/BusIO', 78 | 'libs/GFX', 79 | 'libs/NeoPixel', 80 | 'libs/SSD1306', 81 | ); 82 | } 83 | */ 84 | -------------------------------------------------------------------------------- /src/tools/libs/busio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_arduino_library( 4 | Adafruit_BusIO 5 | ${A2CM_BusIO_LIB_PATH} 6 | ${A2CM_BusIO_LIB_PATH}/Adafruit_BusIO_Register.cpp 7 | ${A2CM_BusIO_LIB_PATH}/Adafruit_I2CDevice.cpp 8 | ${A2CM_BusIO_LIB_PATH}/Adafruit_SPIDevice.cpp 9 | ) 10 | target_link_libraries(Adafruit_BusIO_lib PUBLIC Wire_lib SPI_lib) -------------------------------------------------------------------------------- /src/tools/libs/eeprom/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_arduino_library( 4 | EEPROM 5 | ${A2CM_DEFAULT_LIB_PATH}/EEPROM 6 | ) -------------------------------------------------------------------------------- /src/tools/libs/gfx/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_arduino_library( 4 | Adafruit_GFX_Library 5 | # VERSION 6 | # 1.11.3 7 | ${A2CM_GFX_LIB_PATH}/ 8 | # SOURCES 9 | ${A2CM_GFX_LIB_PATH}/Adafruit_GFX.cpp 10 | ${A2CM_GFX_LIB_PATH}/Adafruit_GrayOLED.cpp 11 | ${A2CM_GFX_LIB_PATH}/Adafruit_SPITFT.cpp 12 | ${A2CM_GFX_LIB_PATH}/glcdfont.c 13 | ) 14 | # Add dependency on Adafruit's BusIO library 15 | # I think it's PUBLIC dependency, but object libraries, I'm not certain about... 16 | target_link_libraries(Adafruit_GFX_Library_lib PUBLIC Adafruit_BusIO_lib) 17 | -------------------------------------------------------------------------------- /src/tools/libs/spi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_arduino_library( 4 | SPI 5 | ${A2CM_DEFAULT_LIB_PATH}/SPI 6 | ${A2CM_DEFAULT_LIB_PATH}/SPI/SPI.cpp 7 | ) -------------------------------------------------------------------------------- /src/tools/libs/st77xx/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_arduino_library( 4 | Adafruit_ST77XX 5 | ${A2CM_ST77XX_LIB_PATH}/ 6 | ${A2CM_ST77XX_LIB_PATH}/Adafruit_ST7735.cpp 7 | ${A2CM_ST77XX_LIB_PATH}/Adafruit_ST7789.cpp 8 | ${A2CM_ST77XX_LIB_PATH}/Adafruit_ST77xx.cpp 9 | ) 10 | target_link_libraries(Adafruit_ST77XX_lib PUBLIC Adafruit_GFX_Library_lib) -------------------------------------------------------------------------------- /src/tools/libs/wire/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | add_arduino_library( 4 | Wire 5 | ${A2CM_DEFAULT_LIB_PATH}/Wire 6 | ${A2CM_DEFAULT_LIB_PATH}/Wire/Wire.cpp 7 | ${A2CM_DEFAULT_LIB_PATH}/Wire/WireIMXRT.cpp 8 | ${A2CM_DEFAULT_LIB_PATH}/Wire/WireKinetis.cpp 9 | ) -------------------------------------------------------------------------------- /src/tools/mock.mk: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) 2 | CXX=clang++ 3 | SFX=.exe 4 | SEP=\\ 5 | MKDIRPATH=$(subst /,${SEP},$1) 6 | else 7 | SFX= 8 | SEP=/ 9 | ifeq ($(shell uname -p), i386) 10 | BISON=/usr/local/opt/bison/bin/bison 11 | else 12 | BISON=/opt/homebrew/opt/bison/bin/bison 13 | endif 14 | MKDIRPATH=-p $1 15 | endif 16 | 17 | # Size is larger than -O3 lame lame lame, clang 18 | # OPTFLAGS=-flto -Oz 19 | # Speed: 20 | # OPTFLAGS=-flto -O3 21 | # Debug: 22 | ifeq (${CXX},cl.exe) 23 | OBJFLAG=/Fo: 24 | IMGFLAG=/Fe: 25 | OPTFLAGS=/Od /Zi /EHsc 26 | STDNUM=/std:c++20 27 | OSUFFIX=obj 28 | ONLYCOMPILE=/c 29 | else 30 | OBJFLAG=-o 31 | IMGFLAG=-o 32 | OPTFLAGS=-g 33 | STDNUM=-std=c++20 34 | OSUFFIX=o 35 | ONLYCOMPILE=-c 36 | endif 37 | 38 | BUILD_PROJECT_NAME=${PROJ_NAME}${APP_SUFFIX} 39 | # variables I might need: 40 | # COMPILER_PATH 41 | COMPILER_CPP_FLAGS=${STDNUM} -DMOCKING 42 | COMPILER_CPP_CMD=clang++ 43 | COMPILER_C_ELF_CMD=clang++ 44 | # COMPILER_CPP_EXTRA_FLAGS 45 | # COMPILER_LDFLAGS 46 | # COMPILER_PATH 47 | 48 | USER_SRC=${USER_C_SRCS} ${USER_CPP_SRCS} ${USER_S_SRCS} 49 | ALL_SRC=${USER_SRC} 50 | VPATH:=${VPATH}:${VPATH_MORE}:${VPATH_CORE}:${VPATH_VAR} 51 | 52 | USER_OBJS=\ 53 | $(addprefix ${BUILD_PATH}/, \ 54 | $(patsubst %.cpp, %.cpp.${OSUFFIX}, $(notdir ${USER_SRC}))) 55 | ALL_OBJS=${USER_OBJS} 56 | USER_JSON=\ 57 | $(addprefix ${BUILD_PATH}/, \ 58 | $(patsubst %.c, %.c.json, \ 59 | $(patsubst %.cpp, %.cpp.json, \ 60 | $(patsubst %.S, %.S.json, $(notdir ${USER_SRC}))))) 61 | 62 | # And now the build rules! 63 | 64 | # First, the phony rules that don't product things 65 | .PHONY: ${PROJ_NAME} clean allclean 66 | 67 | # Now the default target 68 | all: ${BUILD_PATH} ${PROJ_NAME} 69 | 70 | # Some house keeping 71 | clean: 72 | ifeq ($(OS),Windows_NT) 73 | -@del ${USER_OBJS} 74 | else 75 | -rm ${USER_OBJS} 76 | endif 77 | 78 | # Make us rebuild user code if the makefile(s) change: 79 | # Needs to be above the deps thing, I think 80 | ${USER_OBJS} : $(MAKEFILE_LIST) 81 | 82 | # Let's start using the generated .d files... 83 | -include $(ALL_OBJS:.o=.d) 84 | 85 | # Next, the project name shortcut, because it's easier 86 | ${PROJ_NAME}: ${BUILD_PATH}/${BUILD_PROJECT_NAME}${SFX} 87 | 88 | # And finally, create the director 89 | # TODO: This no worky on Windows fer sure 90 | ${BUILD_PATH}: 91 | ifeq ($(OS),Windows_NT) 92 | -@mkdir "$@" 93 | else 94 | @test -d "$@" || mkdir -p "$@" 95 | endif 96 | 97 | ${BUILD_PATH}/%.cpp.${OSUFFIX} : %.cpp 98 | "${CXX}" ${ONLYCOMPILE} ${COMPILER_CPP_FLAGS} ${SYS_INCLUDES} ${USER_INCLUDES} ${OPTFLAGS} ${COMPILER_CPP_EXTRA_FLAGS} "$<" ${OBJFLAG} "$@" 99 | 100 | ${BUILD_PATH}/${BUILD_PROJECT_NAME}${SFX} : ${USER_OBJS} 101 | "${CXX}" ${USER_OBJS} ${BUILD_FLAGS_LIBS} ${IMGFLAG} "$@" 102 | 103 | ${BUILD_PATH}/%.cpp.json : %.cpp 104 | ifeq ($(OS),Windows_NT) 105 | @echo { "directory":"$( $@ 106 | @echo "\"${CXX}\" -c ${COMPILER_CPP_FLAGS} ${SYS_INCLUDES} ${USER_INCLUDES} ${COMPILER_CPP_EXTRA_FLAGS} \"$<\" -o \"$@\"" >> $@ 107 | @echo }, >> $@ 108 | else 109 | @echo "{ \"directory\": \"$( $@ 110 | @echo "\"command\":\"\\\"${CXX}\\\" -c ${COMPILER_CPP_FLAGS} ${SYS_INCLUDES} ${USER_INCLUDES} ${COMPILER_CPP_EXTRA_FLAGS} \\\"$<\\\" -o \\\"$@\\\"\"}," >> $@ 111 | endif 112 | 113 | ${BUILD_PATH}/compile_commands.json: ${USER_JSON} 114 | ifeq ($(OS),Windows_NT) 115 | @echo [ > $@ 116 | @sed -e "s/ / /" $^ >> $@ 117 | @echo {}] >> $@ 118 | else 119 | @echo "[" > $@.tmp 120 | @cat $^ >> $@.tmp 121 | @echo "]" >> $@.tmp 122 | @sed -e ':a' -e 'N' -e '$$!ba' -e 's/},\n]/}]/g' $@.tmp > $@ 123 | endif 124 | 125 | compile_commands: ${BUILD_PATH} ${BUILD_PATH}/compile_commands.json 126 | -------------------------------------------------------------------------------- /src/tools/teensy-make-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "transforms": [ 3 | { 4 | "defmatch": "_FLAGS_CPP", 5 | "text": "-std=gnu++14", 6 | "replace": "-std=gnu++17" 7 | }, 8 | { 9 | "defmatch": "_FLAGS_CPP", 10 | "text": "-std=gnu++11", 11 | "replace": "-std=gnu++17" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/tools/toolchains/Teensy40.cmake: -------------------------------------------------------------------------------- 1 | # Target OS: Docs say to set this to "Generic" but I kinda want to call it "Arduino" ?? 2 | # Automatically sets CMAKE_CROSSCOMPILING to True (use that mostly, not this value?) 3 | set(CMAKE_SYSTEM_NAME Generic) 4 | 5 | # Docs say "anything I want" and is used to load a 6 | # ${CMAKE_SYSTEM_NAME}-COMPILER_ID-${CMAKE_SYSTEM_PROCESSOR}.cmake file 7 | # So I guess I could factor things nicely into pieces, enabling this set of stuff to be 8 | # generated & installed in a global (or user-global) location, right? 9 | set(CMAKE_SYSTEM_PROCESSOR Teensy40) 10 | 11 | # You can use CMAKE_HOST_* (SYSTEM/SYSTEM_NAME/SYST EM_VERSION/SYSTEM_PROCESSOR/WIN32/UNIX/APPLE) 12 | # for host dependent stuff in here if you want... 13 | if(CMAKE_HOST_WIN32) 14 | # message(NOTICE "Selecting Windows globally installed ARM EABI None toolchain") 15 | # set(TOOLCHAIN_ROOT_LOCATION "c:/program files (x86)/Arm GNU Toolchain arm-none-eabi/11.2 2022.02/bin") 16 | # message(NOTICE "Selecting Windows Arduino installed ARM EABI None toolchain") 17 | set(TOOLCHAIN_ROOT_LOCATION "c:/program files (x86)/Arduino/hardware/tools/arm/bin") 18 | set(HOST_EXE_SUFFIX .exe) 19 | set(A2CM_RUNTIME_PLATFORM_PATH "c:/program files (x86)/Arduino/hardware/teensy/avr") 20 | set(A2CM_CMD_PATH "c:/program files (x86)/Arduino/hardware/tools") 21 | elseif(CMAKE_HOST_APPLE) 22 | set(TOOLCHAIN_ROOT_LOCATION "$ENV{HOME}/Library/Arduino15/packages/adafruit/tools/arm-none-eabi-gcc/9-2019q4/bin") 23 | set(A2CM_RUNTIME_PLATFORM_PATH "/Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr") 24 | elseif(CMAKE_HOST_UNIX) 25 | # Definitely wrong currently 26 | set(TOOLCHAIN_ROOT_LOCATION "$ENV{HOME}/.arduino") 27 | else() 28 | message(FATAL_ERROR "Unknown host platform: Sorry!") 29 | endif() 30 | 31 | set(CMAKE_ASM_COMPILER "${TOOLCHAIN_ROOT_LOCATION}/arm-none-eabi-gcc${HOST_EXE_SUFFIX}") 32 | set(CMAKE_C_COMPILER "${TOOLCHAIN_ROOT_LOCATION}/arm-none-eabi-gcc${HOST_EXE_SUFFIX}") 33 | set(CMAKE_CXX_COMPILER "${TOOLCHAIN_ROOT_LOCATION}/arm-none-eabi-g++${HOST_EXE_SUFFIX}") 34 | set(CMAKE_AR "${TOOLCHAIN_ROOT_LOCATION}/arm-none-eabi-gcc-ar${HOST_EXE_SUFFIX}") 35 | 36 | # This prevents the compile test from trying to link 37 | set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 38 | 39 | # adjust the default behavior of the FIND_XXX() commands: 40 | # search programs in the host environment 41 | # KBF: I think this shouldn't be "NEVER" because a number of programs are host-only things 42 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 43 | 44 | # search headers and libraries in the target environment 45 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 46 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 47 | 48 | # And now, the initial compiler flags to be used 49 | set(A2CM_ASM_FLAGS 50 | -x assembler-with-cpp 51 | ) 52 | string(JOIN " " CMAKE_ASM_FLAGS_INIT ${A2CM_ASM_FLAGS}) 53 | set(A2CM_CXX_FLAGS 54 | -fno-exceptions 55 | -fpermissive 56 | -fno-rtti 57 | -fno-threadsafe-statics 58 | -felide-constructors 59 | -Wno-error=narrowing 60 | ) 61 | string(JOIN " " CMAKE_CXX_FLAGS_INIT ${A2CM_CXX_FLAGS}) 62 | set(A2CM_C_FLAGS 63 | -Wno-error=narrowing 64 | ) 65 | string(JOIN " " CMAKE_C_FLAGS_INIT ${A2CM_C_FLAGS}) 66 | #set(A2CM_EXE_LINKER_FLAGS 67 | # \"-T${A2CM_RUNTIME_PLATFORM_PATH}cores/teensy4/imxrt1062_mm.ld\" 68 | #) 69 | set(CMAKE_EXE_LINKER_FLAGS_INIT -Wl,--gc-sections,--relax) 70 | --------------------------------------------------------------------------------