├── .clang-format ├── .git-blame-ignore-revs ├── .github └── workflows │ ├── changelog.yml │ ├── code_format.yml │ ├── platformio.yml │ └── platformio_unit_tests.yml ├── .gitignore ├── .mailmap ├── CODE_OF_CONDUCT.md ├── Changelog.md ├── Configuration.hpp ├── ConfigurationValidation.hpp ├── Configuration_adv.hpp ├── Constants.hpp ├── LICENSE ├── LocalConfiguration.hpp ├── OpenAstroTracker-Firmware.ino ├── README.md ├── Version.h ├── boards ├── AVR_MEGA2560 │ └── pins_MEGA2560.hpp ├── AVR_MKS_GEN_L_V1 │ └── pins_MKS_GEN_L_V1.h ├── AVR_MKS_GEN_L_V2 │ └── pins_MKS_GEN_L_V2.h ├── AVR_MKS_GEN_L_V21 │ └── pins_MKS_GEN_L_V21.h ├── ESP32_ESP32DEV │ └── pins_ESP32DEV.hpp └── RAMPS │ └── pins_RAMPS.hpp ├── matrix_build.py ├── matrix_build_parallel.py ├── platformio.ini ├── post_script_remove_patched_files.py ├── pre_script_patch_debug.py ├── requirements_matrix_build.txt ├── requirements_version_check.txt ├── scripts └── MeadeCommandParser.py ├── src ├── Core.cpp ├── DayTime.cpp ├── DayTime.hpp ├── Declination.cpp ├── Declination.hpp ├── EPROMStore.cpp ├── EPROMStore.hpp ├── EndSwitches.cpp ├── EndSwitches.hpp ├── Gyro.cpp ├── Gyro.hpp ├── HallSensorHoming.cpp ├── HallSensorHoming.hpp ├── InfoDisplayRender.hpp ├── InterruptAccelStepper.h ├── InterruptCallback.cpp ├── InterruptCallback.hpp ├── Latitude.cpp ├── Latitude.hpp ├── LcdButtons.cpp ├── LcdButtons.hpp ├── LcdMenu.cpp ├── LcdMenu.hpp ├── Longitude.cpp ├── Longitude.hpp ├── MeadeCommandProcessor.cpp ├── MeadeCommandProcessor.hpp ├── Mount.cpp ├── Mount.hpp ├── SSD1306_128x64_Display.hpp ├── Sidereal.cpp ├── Sidereal.hpp ├── StepperConfiguration.hpp ├── Types.hpp ├── Utility.cpp ├── Utility.hpp ├── WifiControl.cpp ├── WifiControl.hpp ├── a_inits.hpp ├── b_setup.hpp ├── c65_startup.hpp ├── c70_menuRA.hpp ├── c71_menuDEC.hpp ├── c722_menuPOI.hpp ├── c725_menuHOME.hpp ├── c72_menuHA.hpp ├── c72_menuHA_GPS.hpp ├── c75_menuCTRL.hpp ├── c76_menuCAL.hpp ├── c77_menuFOC.hpp ├── c78_menuINFO.hpp ├── c_buttons.hpp ├── f_serial.hpp ├── fonts128x64.h ├── inc │ ├── Globals.cpp │ └── Globals.hpp ├── libs │ ├── MappedDict │ │ └── MappedDict.hpp │ └── TimerInterrupt │ │ ├── LICENSE │ │ ├── TimerInterrupt.cpp │ │ └── TimerInterrupt.h ├── macros │ └── gcc │ │ └── Macros.hpp ├── testmenu.cpp ├── testmenu.hpp └── testmenudef.hpp ├── unit_tests ├── test_common │ └── test_MappedDict.cpp └── test_embedded │ ├── main.cpp │ └── test_sidereal.h └── version_check.py /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -2 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveMacros: true 6 | AlignConsecutiveAssignments: true 7 | AlignConsecutiveBitFields: true 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlines: Right 10 | AlignOperands: Align 11 | AlignTrailingComments: true 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortEnumsOnASingleLine: false 16 | AllowShortBlocksOnASingleLine: Never 17 | AllowShortCaseLabelsOnASingleLine: false 18 | AllowShortFunctionsOnASingleLine: None 19 | AllowShortLambdasOnASingleLine: All 20 | AllowShortIfStatementsOnASingleLine: Never 21 | AllowShortLoopsOnASingleLine: false 22 | AlwaysBreakAfterDefinitionReturnType: None 23 | AlwaysBreakAfterReturnType: None 24 | AlwaysBreakBeforeMultilineStrings: false 25 | AlwaysBreakTemplateDeclarations: MultiLine 26 | BinPackArguments: false 27 | BinPackParameters: false 28 | BraceWrapping: 29 | AfterCaseLabel: true 30 | AfterClass: true 31 | AfterControlStatement: Always 32 | AfterEnum: true 33 | AfterFunction: true 34 | AfterNamespace: true 35 | AfterObjCDeclaration: true 36 | AfterStruct: false 37 | AfterUnion: false 38 | AfterExternBlock: true 39 | BeforeCatch: true 40 | BeforeElse: true 41 | BeforeLambdaBody: false 42 | BeforeWhile: false 43 | IndentBraces: false 44 | SplitEmptyFunction: true 45 | SplitEmptyRecord: true 46 | SplitEmptyNamespace: true 47 | BreakBeforeBinaryOperators: All 48 | BreakBeforeBraces: Custom 49 | BreakBeforeInheritanceComma: false 50 | BreakInheritanceList: BeforeColon 51 | BreakBeforeTernaryOperators: true 52 | BreakConstructorInitializersBeforeComma: false 53 | BreakConstructorInitializers: BeforeColon 54 | BreakAfterJavaFieldAnnotations: false 55 | BreakStringLiterals: true 56 | ColumnLimit: 140 57 | CommentPragmas: '^ IWYU pragma:' 58 | CompactNamespaces: false 59 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 60 | ConstructorInitializerIndentWidth: 4 61 | ContinuationIndentWidth: 4 62 | Cpp11BracedListStyle: true 63 | DeriveLineEnding: true 64 | DerivePointerAlignment: false 65 | DisableFormat: false 66 | ExperimentalAutoDetectBinPacking: false 67 | FixNamespaceComments: true 68 | ForEachMacros: 69 | - foreach 70 | - Q_FOREACH 71 | - BOOST_FOREACH 72 | IncludeBlocks: Preserve 73 | IncludeCategories: 74 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 75 | Priority: 2 76 | SortPriority: 0 77 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 78 | Priority: 3 79 | SortPriority: 0 80 | - Regex: '.*' 81 | Priority: 1 82 | SortPriority: 0 83 | IncludeIsMainRegex: '(Test)?$' 84 | IncludeIsMainSourceRegex: '' 85 | IndentCaseLabels: true 86 | IndentCaseBlocks: true 87 | IndentGotoLabels: true 88 | IndentPPDirectives: BeforeHash 89 | IndentExternBlock: AfterExternBlock 90 | IndentWidth: 4 91 | IndentWrappedFunctionNames: false 92 | InsertTrailingCommas: Wrapped 93 | JavaScriptQuotes: Leave 94 | JavaScriptWrapImports: true 95 | KeepEmptyLinesAtTheStartOfBlocks: false 96 | MacroBlockBegin: '' 97 | MacroBlockEnd: '' 98 | MaxEmptyLinesToKeep: 1 99 | NamespaceIndentation: None 100 | ObjCBinPackProtocolList: Auto 101 | ObjCBlockIndentWidth: 2 102 | ObjCBreakBeforeNestedBlockParam: true 103 | ObjCSpaceAfterProperty: false 104 | ObjCSpaceBeforeProtocolList: true 105 | PenaltyBreakAssignment: 2 106 | PenaltyBreakBeforeFirstCallParameter: 19 107 | PenaltyBreakComment: 300 108 | PenaltyBreakFirstLessLess: 120 109 | PenaltyBreakString: 1000 110 | PenaltyBreakTemplateDeclaration: 10 111 | PenaltyExcessCharacter: 1000000 112 | PenaltyReturnTypeOnItsOwnLine: 1000 113 | PointerAlignment: Right 114 | ReflowComments: false 115 | SortIncludes: false 116 | SortUsingDeclarations: true 117 | SpaceAfterCStyleCast: true 118 | SpaceAfterLogicalNot: false 119 | SpaceAfterTemplateKeyword: true 120 | SpaceBeforeAssignmentOperators: true 121 | SpaceBeforeCpp11BracedList: true 122 | SpaceBeforeCtorInitializerColon: true 123 | SpaceBeforeInheritanceColon: true 124 | SpaceBeforeParens: ControlStatementsExceptForEachMacros 125 | SpaceBeforeRangeBasedForLoopColon: true 126 | SpaceInEmptyBlock: false 127 | SpaceInEmptyParentheses: false 128 | SpacesBeforeTrailingComments: 2 129 | SpacesInAngles: false 130 | SpacesInConditionalStatement: false 131 | SpacesInContainerLiterals: false 132 | SpacesInCStyleCastParentheses: false 133 | SpacesInParentheses: false 134 | SpacesInSquareBrackets: false 135 | SpaceBeforeSquareBrackets: false 136 | Standard: Latest 137 | StatementMacros: 138 | - Q_UNUSED 139 | - QT_REQUIRE_VERSION 140 | TabWidth: 4 141 | UseCRLF: false 142 | UseTab: Never 143 | WhitespaceSensitiveMacros: 144 | - STRINGIZE 145 | - PP_STRINGIZE 146 | - BOOST_PP_STRINGIZE 147 | ... 148 | 149 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Same formatting as git's fsck.skipList, see git-blame --ignore-revs-file 2 | 3 | # Reformat codebase according to .clang-format 4 | a25945b9984f8ca2ac582ecc7d634635d7ce9134 5 | 6 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: Check changelog and version are matching 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '*' 7 | 8 | jobs: 9 | check: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v1 14 | - name: Set up Python 15 | uses: actions/setup-python@v1 16 | - name: Install dependencies 17 | run: | 18 | python3 -m pip install --upgrade pip 19 | python3 -m pip install -r requirements_version_check.txt 20 | - name: Run version check script 21 | run: python3 version_check.py 22 | -------------------------------------------------------------------------------- /.github/workflows/code_format.yml: -------------------------------------------------------------------------------- 1 | name: Run clang-format 2 | 3 | on: 4 | pull_request_target: 5 | branches: 6 | - '*' 7 | 8 | jobs: 9 | check: 10 | runs-on: ubuntu-latest 11 | outputs: 12 | status: ${{ steps.early.conclusion }} 13 | steps: 14 | - uses: actions/checkout@v2 15 | with: 16 | ref: ${{ github.event.pull_request.head.ref }} 17 | repository: ${{ github.event.pull_request.head.repo.full_name }} 18 | - uses: DoozyX/clang-format-lint-action@v0.18.1 19 | with: 20 | source: '. ./src ./src/libs' 21 | exclude: './src/libs/TimerInterrupt ./scripts ./src/fonts128x64.h' 22 | extensions: 'c,cpp,h,hpp' 23 | clangFormatVersion: 12 24 | inplace: True 25 | - name: Check if diff 26 | id: needs_work 27 | run: if git diff --quiet --exit-code; then exit 0; else exit 1; fi 28 | continue-on-error: true 29 | - name: Create patch 30 | if: ${{ steps.needs_work.outcome != 'success' }} 31 | run: git diff > ./clang-format-diff.patch 32 | - uses: actions/upload-artifact@v4 33 | if: ${{ steps.needs_work.outcome != 'success' }} 34 | with: 35 | name: clang-format-diff.patch 36 | path: ./clang-format-diff.patch 37 | - uses: mshick/add-pr-comment@v1 38 | if: ${{ steps.needs_work.outcome != 'success' }} 39 | with: 40 | message: | 41 | Looks like your PR has code that needs to be changed in order to meet our coding standards! 42 | Here are your options: 43 | 1. Apply the patch that was generated by the job 44 | 1. Click `details` under the failing clang-format check 45 | 1. Click the `Artifacts` dropdown in the top right 46 | 1. Download + unzip the `clang-format-diff.patch` file into the `OpenAstroTracker-Firmware` repo 47 | 1. Run `git apply clang-format-diff.patch` to make the changes 48 | 1. Commit and push up the formatted code 49 | 1. Run `clang-format` locally 50 | 1. Run the command `bash -c 'shopt -s nullglob globstar;GLOBIGNORE=./src/libs/TimerInterrupt/*; for i in ./{.,src/**,unit_tests,boards/**}/*.{c,cpp,h,hpp}; do clang-format -i $i; done'` 51 | 1. Commit and push up the formatted code 52 | repo-token: ${{secrets.BOT_ACCESS_TOKEN}} 53 | repo-token-user-login: 'openastrotech-bot' 54 | allow-repeats: false 55 | - name: Fail if needs formatting 56 | if: ${{ steps.needs_work.outcome != 'success' }} 57 | run: if git diff --quiet --exit-code; then exit 0; else exit 1; fi 58 | -------------------------------------------------------------------------------- /.github/workflows/platformio.yml: -------------------------------------------------------------------------------- 1 | name: PlatformIO CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | build: 13 | name: BOARD=${{ matrix.board }} 14 | runs-on: ubuntu-latest 15 | continue-on-error: true 16 | strategy: 17 | matrix: 18 | board: [ mksgenlv21, 19 | mksgenlv2, 20 | mksgenlv1, 21 | esp32, 22 | ramps ] 23 | 24 | steps: 25 | - uses: actions/checkout@v1 26 | - name: Set up Python 27 | uses: actions/setup-python@v2 28 | - name: Install dependencies 29 | run: | 30 | python -m pip install --upgrade pip 31 | pip install wheel 32 | pip install platformio 33 | pip install -r requirements_matrix_build.txt 34 | - name: Run PlatformIO 35 | run: python matrix_build.py -b ${{ matrix.board }} 36 | -------------------------------------------------------------------------------- /.github/workflows/platformio_unit_tests.yml: -------------------------------------------------------------------------------- 1 | name: PlatformIO Unit Tests 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '*' 7 | 8 | jobs: 9 | native_unit_tests: 10 | runs-on: ubuntu-latest 11 | continue-on-error: true 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: Set up Python 16 | uses: actions/setup-python@v1 17 | - name: Install dependencies 18 | run: | 19 | python -m pip install --upgrade pip 20 | pip install platformio 21 | - name: Run Unit Tests 22 | run: pio test -e native -v 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode 3 | venv 4 | .idea 5 | cmake-* 6 | CMakeLists.txt 7 | CMakeListsPrivate.txt 8 | build_cache 9 | 10 | Configuration_local* 11 | MeadeToWikiOutput.txt 12 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | # Shown Name 2 | Julianne Swinoga 3 | Andre Stefanov 4 | Cameron Tetford 5 | Christian Kardach 6 | Clutchplate 7 | Fabian Uehleke 8 | Fabian Uehleke <61112622+OpenAstroTech@users.noreply.github.com> 9 | Fabian Uehleke 10 | jwellman80 11 | jwellman80 12 | 13 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | fabian@openastrotech.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Constants.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * This file contains constants that SHOULD NOT BE CHANGED by oat users! 5 | * If you are a developer and want to add new hardware support, add a 6 | * proper definition here with increased value. 7 | **/ 8 | 9 | /** 10 | * Supported boards. The name consists of the platform and the board name (model). 11 | **/ 12 | // AVR based boards 13 | #define BOARD_AVR_MEGA2560 0001 14 | #define BOARD_AVR_MKS_GEN_L_V21 0002 15 | #define BOARD_AVR_MKS_GEN_L_V2 0003 16 | #define BOARD_AVR_MKS_GEN_L_V1 0004 17 | #define BOARD_AVR_RAMPS 0005 18 | 19 | // ESP32 based boards 20 | #define BOARD_ESP32_ESP32DEV 1001 21 | 22 | /** 23 | * Supported keypad/display types. Use one of these values for DISPLAY_TYPE configuration matching your used display and keypad. 24 | * 25 | * DISPLAY_TYPE_NONE: No display. Use this if you don't use any display. 26 | * DISPLAY_TYPE_LCD_KEYPAD: 1602 LCD Keypad shield which can be mounted directly to an Arduino UNO / Mega boards. 27 | * Example: https://www.digikey.com/en/products/detail/dfrobot/DFR0009/7597118 28 | * DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23008: RGB LCD Keypad shield based on the MCP23008 I/O Expander. It can be mounted 29 | * directly to an Arduino UNO / Mega boards and controlled over I2C. 30 | * DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23017: RGB LCD Keypad shield based on the MCP23017 I/O Expander. It can be mounted 31 | * directly to an Arduino UNO / Mega boards and controlled over I2C. 32 | * DISPLAY_TYPE_LCD_JOY_I2C_SSD1306: I2C 32x128 OLED display module with SSD1306 controller, plus mini joystick 33 | * Display: https://www.banggood.com/Geekcreit-0_91-Inch-128x32-IIC-I2C-Blue-OLED-LCD-Display-DIY-Module-SSD1306-Driver-IC-DC-3_3V-5V-p-1140506.html 34 | * Joystick: https://www.banggood.com/3pcs-JoyStick-Module-Shield-2_54mm-5-pin-Biaxial-Buttons-Rocker-for-PS2-Joystick-Game-Controller-Sensor-p-1586026.html 35 | **/ 36 | #define DISPLAY_TYPE_NONE 0 37 | #define DISPLAY_TYPE_LCD_KEYPAD 1 38 | #define DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23008 2 39 | #define DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23017 3 40 | #define DISPLAY_TYPE_LCD_JOY_I2C_SSD1306 4 41 | 42 | /** 43 | * Supported info display types. Use one of these values for INF_DISPLAY_TYPE configuration matching your used display. 44 | * 45 | * INFO_DISPLAY_TYPE_NONE: No display. Use this if you don't use any display. 46 | * INFO_DISPLAY_TYPE_SSD1306_I2C_128x64: I2C 128x64 OLED display module with SSD1306 controller, attached via I2C 47 | * Amazon: https://www.amazon.com/dp/B06XRBTBTB?_encoding=UTF8&psc=1&ref_=cm_sw_r_cp_ud_dp_DQCWKZ7YB40X84RZSHJ0 48 | **/ 49 | #define INFO_DISPLAY_TYPE_NONE 0 50 | #define INFO_DISPLAY_TYPE_I2C_SSD1306_128x64 1 51 | 52 | // Supported stepper models 53 | #define STEPPER_TYPE_NONE -1 54 | #define STEPPER_TYPE_ENABLED 1 55 | 56 | // Supported stepper driver models 57 | #define DRIVER_TYPE_NONE -1 58 | #define DRIVER_TYPE_A4988_GENERIC 1 // Supports fixed microstepping 59 | #define DRIVER_TYPE_TMC2209_STANDALONE 2 // Supports fixed microstepping 60 | #define DRIVER_TYPE_TMC2209_UART 3 // Supports dynamic microstepping 61 | 62 | // USB serial port speed according to external controller 63 | #define SERIAL_BAUDRATE_ASCOM 19200 64 | 65 | // Wifi operating modes (ESP32 only) 66 | #define WIFI_MODE_INFRASTRUCTURE 0 // Infrastructure Only - OAT connects to an existing Router 67 | #define WIFI_MODE_AP_ONLY 1 // AP Mode Only - OAT acts as a local Router/Hotspot 68 | #define WIFI_MODE_ATTEMPT_INFRASTRUCTURE_FAIL_TO_AP \ 69 | 2 // Attempt Infrastructure, Fail over to AP Mode - Attempt infrastructure mode, with fail over to AP Mode. 70 | #define WIFI_MODE_DISABLED 3 // Wifi disabled, transceiver switched off 71 | 72 | // Debugging output control 73 | // Each bit in the debug level specifies a kind of debug to enable. Combine these into DEBUG_LEVEL for the required information e.g.: 74 | // #define DEBUG_LEVEL (DEBUG_STEPPERS|DEBUG_MOUNT) 75 | // #define DEBUG_LEVEL (DEBUG_INFO|DEBUG_MOUNT|DEBUG_GENERAL) 76 | // #define DEBUG_LEVEL (DEBUG_SERIAL|DEBUG_WIFI|DEBUG_INFO|DEBUG_MOUNT|DEBUG_GENERAL) 77 | // #define DEBUG_LEVEL (DEBUG_ANY) 78 | // #define DEBUG_LEVEL (DEBUG_INFO|DEBUG_MOUNT|DEBUG_GENERAL) 79 | #define DEBUG_NONE 0x0000 // No debug output (release build) 80 | #define DEBUG_INFO 0x0001 // General output, like startup variables and status 81 | #define DEBUG_SERIAL 0x0002 // Serial commands and replies 82 | #define DEBUG_WIFI 0x0004 // Wifi related output 83 | #define DEBUG_MOUNT 0x0008 // Mount processing output 84 | #define DEBUG_MOUNT_VERBOSE 0x0010 // Verbose mount processing (coordinates, etc) 85 | #define DEBUG_GENERAL 0x0020 // Other misc. output 86 | #define DEBUG_MEADE 0x0040 // Meade command handling output 87 | #define DEBUG_VERBOSE 0x0080 // High-rate mount activity, incl. stepper servicing 88 | #define DEBUG_STEPPERS 0x0100 // Stepper motor-related activity 89 | #define DEBUG_EEPROM 0x0200 // Storing and retrieval of non-volatile configuration data 90 | #define DEBUG_GYRO 0x0400 // Gyro activity (tilt/roll) calibration 91 | #define DEBUG_GPS 0x0800 // GPS activity 92 | #define DEBUG_FOCUS 0x1000 // Focuser activity 93 | #define DEBUG_COORD_CALC 0x2000 // Calculations of coordinates 94 | #define DEBUG_DISPLAY 0x4000 // Info display 95 | #define DEBUG_GUIDE 0x8000 // Guiding info 96 | #define DEBUG_ANY 0xFFFF // All debug output 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 OpenAstroTech 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 | -------------------------------------------------------------------------------- /LocalConfiguration.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Constants.hpp" 4 | 5 | #if defined(NO_LOCAL_CONFIG) 6 | // Don't load local configuration file 7 | #elif defined(MATRIX_LOCAL_CONFIG) 8 | #include "Configuration_local_matrix.hpp" 9 | #elif BOARD == BOARD_AVR_RAMPS && __has_include("Configuration_local_ramps.hpp") 10 | #include "Configuration_local_ramps.hpp" 11 | #elif BOARD == BOARD_AVR_MKS_GEN_L_V21 && __has_include("Configuration_local_mksgenlv21.hpp") 12 | #include "Configuration_local_mksgenlv21.hpp" 13 | #elif BOARD == BOARD_AVR_MKS_GEN_L_V2 && __has_include("Configuration_local_mksgenlv2.hpp") 14 | #include "Configuration_local_mksgenlv2.hpp" 15 | #elif BOARD == BOARD_AVR_MKS_GEN_L_V1 && __has_include("Configuration_local_mksgenlv1.hpp") 16 | #include "Configuration_local_mksgenlv1.hpp" 17 | #elif BOARD == BOARD_ESP32_ESP32DEV && __has_include("Configuration_local_esp32dev.hpp") 18 | #include "Configuration_local_esp32dev.hpp" 19 | #elif __has_include("Configuration_local.hpp") 20 | #include "Configuration_local.hpp" 21 | #endif -------------------------------------------------------------------------------- /OpenAstroTracker-Firmware.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ======================================================================================================================================= 3 | 4 | Version 1.8 5 | 6 | 1. Connect your Arduino, under tools choose the board you're using (e.g. Arduino Mega), set the right Port and set "Arduino ISP" as the Programmer. 7 | 2. Hit upload (Ctrl-U) 8 | 9 | You might need to download the "AccelStepper" library by Mike McCauley 10 | 11 | Authors: /u/intercipere 12 | /u/clutchplate 13 | /u/EorEquis 14 | 15 | ======================================================================================================================================= 16 | */ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenAstroTracker-Firmware 2 | Official firmware for the OpenAstroTracker. Other important resources can be found [here](https://wiki.openastrotech.com/en/Links). 3 | 4 | ## Change log 5 | See the [Changelog](Changelog.md) for details about what versions made what changes. 6 | 7 | ## Coding guidelines 8 | 9 | See `.clang-format` file. A GitHub action is run on every PR to make sure that the code complies with the formatting guidelines. 10 | 11 | ### Run clang-format locally 12 | * Install `clang-format` version 12. _Note: not all distributions default to version 12_ 13 | * Windows: Installers available from [LLVM Website](https://llvm.org/builds/) 14 | * Ubuntu: `sudo apt install clang-format-12` 15 | * ArchLinux: `sudo pacman -S clang` 16 | * Run the formatter: 17 | * VSCode Extension: [https://marketplace.visualstudio.com/items?itemName=xaver.clang-format](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format) 18 | * Shell: `bash -c 'shopt -s nullglob globstar;GLOBIGNORE=./src/libs/TimerInterrupt/*; for i in ./{.,src/**,unit_tests,boards/**}/*.{c,cpp,h,hpp}; do clang-format -i $i; done'` 19 | ## Contribution 20 | 21 | This is an open source project and everyone is welcome to contribute. We will be following these rules while reviewing your pull request: 22 | - The pull request consists **only** of the **changes related to its particular feature or bugfix**. If there are multiple unrelated changes which should be merged into this repository, you have to create a separate pull request for each of them. 23 | - The pull request **builds correctly**. If it doesn't, please fix the issues and push them to the source branch. You can use the matrix_build.py script to build all the important configurations locally (works similar to our CI). 24 | - The pull request can only be merged **after** all comments were resolved BY **OAT DEVELOPERS**. Please don't resolve the comments yourself since this can lead to missed issues. 25 | - If the pull request is not maintained by its author in a reasonably prompt manner after a review, the developers can decide to close it without merging since the accumulated merge conflicts and original code changes could lead to massive efforts. You can then still recreate your pull request after applying all the required changes on your fork branch. 26 | 27 | ## Development 28 | 29 | Even if Arduino IDE is supported, we highly recommend using VSCode with [PlatformIO](https://platformio.org/) for development. It allows automatic dependency management, powerful IDE, debugging, automatic build flags definition and more. 30 | 31 | ### Debugging 32 | 33 | #### ATmega2560-based 34 | 35 | > :warning: **Debugging is only supported on mega2560 platforms at the moment!** 36 | 37 | For this example we will be using the `ramps` environment, but you can use any derived environment as well 38 | 39 | > You may need to set `debug_port` in your `platformio.ini`, platformio says it will auto-detect the port but it doesn't seem to be working at the moment 40 | 41 | Start a gdb shell debugging the current firmware: 42 | ```shell 43 | pio run -e ramps -t clean # Clean the environment 44 | piodebuggdb -e ramps # Initialize a debug session. 45 | # This will build the firmware in debug mode, and then initialize a remote gdb session 46 | # You may have visual debug capabilities in your IDE if it has platformio integration as well 47 | ``` 48 | 49 | When using `avr-stub` as a debug interface, it requires 2 things: 50 | 1. Serial link 0 must not be used in the firmware 51 | - As such, all external interfaces using `Serial` are disabled in a debug build (`Serial1`, `Serial2` etc are ok) 52 | 2. Exclusive access to an interrupt vector 53 | - This requires hot-patching the arduino framework (specifically `WInterrupts.c`) to disable the ISR registration. The implementation of this is in `pre_script_patch_debug.py`, which should happen automagically 54 | 55 | > Note that while avr-stub is in RAM mode, the firmware will run very slowly and timing-related functions might not work correctly 56 | 57 | Debugging is still a bit flakey, so you may need to try multiple times in order to get a solid debugging session. 58 | 59 | More information is available in the [avr-stub documentation](https://github.com/jdolinay/avr_debug/tree/master/doc) 60 | 61 | ### Meade Command Documentation 62 | The Meade commands page on the Wiki is generated by running: 63 | ```shell 64 | python .\scripts\MeadeCommandParser.py 65 | ``` 66 | from the main directory. The page is generated in the scripts folder and needs to be copy pasted into the Wiki manually. Please add the version manually (for now) by pasting something like this: 67 | ``` 68 | > This documentation is current as of Firmware **V1.13.9** 69 | {.is-warning} 70 | ``` 71 | -------------------------------------------------------------------------------- /Version.h: -------------------------------------------------------------------------------- 1 | // Version number must have only two digits maximum at each of the three locations. 2 | // So 1.8.99 is ok, but 1.8.234 is not. Neither is 1.123.22 3 | // Also, numbers are interpreted as simple numbers. _ __ _ 4 | // So 1.8 is actually 1.08, meaning that 1.12 is a later version than 1.8. \_(..)_/ 5 | 6 | #define VERSION "V1.13.15" 7 | -------------------------------------------------------------------------------- /boards/AVR_MEGA2560/pins_MEGA2560.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief a pins configuration file for an MEGA2560 OAT. 3 | */ 4 | 5 | #pragma once 6 | 7 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 8 | #ifndef RA_STEP_PIN 9 | #define RA_STEP_PIN 22 // STEP 10 | #endif 11 | #ifndef RA_DIR_PIN 12 | #define RA_DIR_PIN 24 // DIR 13 | #endif 14 | #ifndef RA_EN_PIN 15 | #define RA_EN_PIN 26 // Enable 16 | #endif 17 | #ifndef RA_DIAG_PIN 18 | #define RA_DIAG_PIN 28 // only needed for autohome function 19 | #endif 20 | #ifndef RA_MS0_PIN 21 | #define RA_MS0_PIN 30 22 | #endif 23 | #ifndef RA_MS1_PIN 24 | #define RA_MS1_PIN 32 25 | #endif 26 | #ifndef RA_MS2_PIN 27 | #define RA_MS2_PIN 34 28 | #endif 29 | // DRIVER_TYPE_TMC2209_UART HardwareSerial port, can be shared across all drivers 30 | #ifndef RA_SERIAL_PORT 31 | #define RA_SERIAL_PORT Serial3 32 | #endif 33 | #ifndef RA_DRIVER_ADDRESS 34 | #define RA_DRIVER_ADDRESS 0b00 // Set by MS1/MS2. LOW/LOW in this case 35 | #endif 36 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 37 | #ifndef DEC_STEP_PIN 38 | #define DEC_STEP_PIN 23 // STEP 39 | #endif 40 | #ifndef DEC_DIR_PIN 41 | #define DEC_DIR_PIN 25 // DIR 42 | #endif 43 | #ifndef DEC_EN_PIN 44 | #define DEC_EN_PIN 27 // Enable 45 | #endif 46 | #ifndef DEC_DIAG_PIN 47 | #define DEC_DIAG_PIN 29 // only needed for autohome function 48 | #endif 49 | #ifndef DEC_MS0_PIN 50 | #define DEC_MS0_PIN 31 51 | #endif 52 | #ifndef DEC_MS1_PIN 53 | #define DEC_MS1_PIN 33 54 | #endif 55 | #ifndef DEC_MS2_PIN 56 | #define DEC_MS2_PIN 35 57 | #endif 58 | // DRIVER_TYPE_TMC2209_UART HardwareSerial port, can be shared across all drivers 59 | #ifndef DEC_SERIAL_PORT 60 | #define DEC_SERIAL_PORT Serial3 61 | #endif 62 | #ifndef DEC_DRIVER_ADDRESS 63 | #define DEC_DRIVER_ADDRESS 0b01 // Set by MS1/MS2 (MS1 HIGH, MS2 LOW) 64 | #endif 65 | 66 | #define SW_SERIAL_UART 0 67 | 68 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 69 | #ifndef FOCUS_STEP_PIN 70 | #define FOCUS_STEP_PIN 32 71 | #endif 72 | #ifndef FOCUS_DIR_PIN 73 | #define FOCUS_DIR_PIN 33 74 | #endif 75 | #ifndef FOCUS_EN_PIN 76 | #define FOCUS_EN_PIN 34 77 | #endif 78 | #ifndef FOCUS_SERIAL_PORT 79 | #define FOCUS_SERIAL_PORT Serial3 // SoftwareSerial TX port 80 | #endif 81 | 82 | #ifndef FOCUS_DRIVER_ADDRESS 83 | #define FOCUS_DRIVER_ADDRESS 0b10 // Set by MS1/MS2 (MS1 HIGH, MS2 LOW) 84 | #endif 85 | 86 | // RA Homing pin for Hall sensor 87 | #ifndef RA_HOMING_SENSOR_PIN 88 | #define RA_HOMING_SENSOR_PIN 40 89 | #endif 90 | 91 | // DEC Homing pin for Hall sensor 92 | #ifndef DEC_HOMING_SENSOR_PIN 93 | #define DEC_HOMING_SENSOR_PIN 41 94 | #endif 95 | 96 | //GPS pin configuration 97 | #ifndef GPS_SERIAL_PORT 98 | #define GPS_SERIAL_PORT Serial1 99 | #endif 100 | 101 | // DISPLAY_TYPE_LCD_KEYPAD requires 6 digital & 1 analog output in Arduino pin numbering 102 | #ifndef LCD_BRIGHTNESS_PIN 103 | #define LCD_BRIGHTNESS_PIN 10 // Brightness PWM control (#undef to disable) 104 | #endif 105 | #ifndef LCD_PIN4 106 | #define LCD_PIN4 4 // LCD DB4 pin 107 | #endif 108 | #ifndef LCD_PIN5 109 | #define LCD_PIN5 5 // LCD DB5 pin 110 | #endif 111 | #ifndef LCD_PIN6 112 | #define LCD_PIN6 6 // LCD DB6 pin 113 | #endif 114 | #ifndef LCD_PIN7 115 | #define LCD_PIN7 7 // LCD DB7 pin 116 | #endif 117 | #ifndef LCD_PIN8 118 | #define LCD_PIN8 8 // LCD RS pin 119 | #endif 120 | #ifndef LCD_PIN9 121 | #define LCD_PIN9 9 // LCD ENABLE pin 122 | #endif 123 | 124 | // DISPLAY_TYPE_LCD_KEYPAD requires 1 analog input in Arduino pin numbering 125 | #ifndef LCD_KEY_SENSE_PIN 126 | #define LCD_KEY_SENSE_PIN 0 // Analog input for shield keys (#undef to disable) 127 | #endif 128 | -------------------------------------------------------------------------------- /boards/AVR_MKS_GEN_L_V1/pins_MKS_GEN_L_V1.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief a pins configuration file for an MKS Gen L V2.1 OAT. 3 | * https://github.com/makerbase-mks/MKS-GEN_L/blob/master/hardware/MKS%20Gen_L%20V1.0_008/MKS%20Gen_L%20V1.0_008%20PIN.pdf 4 | */ 5 | 6 | #pragma once 7 | 8 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 9 | #ifndef RA_STEP_PIN 10 | #define RA_STEP_PIN 54 // STEP 11 | #endif 12 | #ifndef RA_DIR_PIN 13 | #define RA_DIR_PIN 55 // DIR 14 | #endif 15 | #ifndef RA_EN_PIN 16 | #define RA_EN_PIN 38 // Enable 17 | #endif 18 | #ifndef RA_DIAG_PIN 19 | #define RA_DIAG_PIN 3 // only needed for autohome function 20 | #endif 21 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 22 | #ifndef RA_SERIAL_PORT_TX 23 | #define RA_SERIAL_PORT_TX 4 // SoftwareSerial TX port 24 | #endif 25 | #ifndef RA_SERIAL_PORT_RX 26 | #define RA_SERIAL_PORT_RX 49 // SoftwareSerial RX port 27 | #endif 28 | #ifndef RA_DRIVER_ADDRESS 29 | #define RA_DRIVER_ADDRESS 0b00 30 | #endif 31 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 32 | #ifndef DEC_STEP_PIN 33 | #define DEC_STEP_PIN 60 // STEP 34 | #endif 35 | #ifndef DEC_DIR_PIN 36 | #define DEC_DIR_PIN 61 // DIR 37 | #endif 38 | #ifndef DEC_EN_PIN 39 | #define DEC_EN_PIN 56 // Enable 40 | #endif 41 | #ifndef DEC_DIAG_PIN 42 | #define DEC_DIAG_PIN 14 // only needed for autohome function 43 | #endif 44 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 45 | #ifndef DEC_SERIAL_PORT_TX 46 | #define DEC_SERIAL_PORT_TX 5 // SoftwareSerial TX port 47 | #endif 48 | #ifndef DEC_SERIAL_PORT_RX 49 | #define DEC_SERIAL_PORT_RX 50 // SoftwareSerial RX port 50 | #endif 51 | #ifndef DEC_DRIVER_ADDRESS 52 | #define DEC_DRIVER_ADDRESS 0b00 53 | #endif 54 | 55 | #define SW_SERIAL_UART 1 56 | 57 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 58 | #ifndef AZ_STEP_PIN 59 | #define AZ_STEP_PIN 46 // STEP 60 | #endif 61 | #ifndef AZ_DIR_PIN 62 | #define AZ_DIR_PIN 48 // DIR 63 | #endif 64 | #ifndef AZ_EN_PIN 65 | #define AZ_EN_PIN 62 // Enable 66 | #endif 67 | #ifndef AZ_DIAG_PIN 68 | #define AZ_DIAG_PIN 18 // only needed for autohome function 69 | #endif 70 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 71 | #ifndef AZ_SERIAL_PORT_TX 72 | #define AZ_SERIAL_PORT_TX 6 // SoftwareSerial TX port 73 | #endif 74 | #ifndef AZ_SERIAL_PORT_RX 75 | #define AZ_SERIAL_PORT_RX 51 // SoftwareSerial RX port 76 | #endif 77 | #ifndef AZ_DRIVER_ADDRESS 78 | #define AZ_DRIVER_ADDRESS 0b00 79 | #endif 80 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 81 | #ifndef ALT_STEP_PIN 82 | #define ALT_STEP_PIN 26 // STEP 83 | #endif 84 | #ifndef ALT_DIR_PIN 85 | #define ALT_DIR_PIN 28 // DIR 86 | #endif 87 | #ifndef ALT_EN_PIN 88 | #define ALT_EN_PIN 24 // Enable 89 | #endif 90 | #ifndef ALT_DIAG_PIN 91 | #define ALT_DIAG_PIN 2 // only needed for autohome function 92 | #endif 93 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 94 | #ifndef ALT_SERIAL_PORT_TX 95 | #define ALT_SERIAL_PORT_TX 11 // SoftwareSerial TX port 96 | #endif 97 | #ifndef ALT_SERIAL_PORT_RX 98 | #define ALT_SERIAL_PORT_RX 52 // SoftwareSerial RX port 99 | #endif 100 | #ifndef ALT_DRIVER_ADDRESS 101 | #define ALT_DRIVER_ADDRESS 0b00 102 | #endif 103 | 104 | //GPS pin configuration 105 | #ifndef GPS_SERIAL_PORT 106 | #define GPS_SERIAL_PORT Serial1 107 | #endif 108 | 109 | // DISPLAY_TYPE_LCD_KEYPAD requires 6 digital & 1 analog output in Arduino pin numbering 110 | #ifndef LCD_PIN4 111 | #define LCD_PIN4 17 112 | #endif 113 | #ifndef LCD_PIN5 114 | #define LCD_PIN5 16 115 | #endif 116 | #ifndef LCD_PIN6 117 | #define LCD_PIN6 23 118 | #endif 119 | #ifndef LCD_PIN7 120 | #define LCD_PIN7 25 121 | #endif 122 | #ifndef LCD_PIN8 123 | #define LCD_PIN8 27 124 | #endif 125 | #ifndef LCD_PIN9 126 | #define LCD_PIN9 29 127 | #endif 128 | 129 | // DISPLAY_TYPE_LCD_KEYPAD requires 1 analog input in Arduino pin numbering 130 | #ifndef LCD_KEY_SENSE_PIN 131 | #define LCD_KEY_SENSE_PIN 65 132 | #endif 133 | 134 | //Pin to turn on dew heater MOSFET 135 | #ifndef DEW_HEATER_1_PIN 136 | #define DEW_HEATER_1_PIN 10 137 | #endif 138 | #ifndef DEW_HEATER_2_PIN 139 | #define DEW_HEATER_2_PIN 7 140 | #endif 141 | 142 | //Serial port for external debugging 143 | #if DEBUG_SEPARATE_SERIAL == 1 144 | #ifndef DEBUG_SERIAL_PORT 145 | #define DEBUG_SERIAL_PORT Serial2 //D16 (LCD_RS) - TXD2 and D17 (LCD_EN) - RXD2 146 | #endif 147 | #else 148 | #ifndef DEBUG_SERIAL_PORT 149 | #define DEBUG_SERIAL_PORT Serial 150 | #endif 151 | #endif 152 | -------------------------------------------------------------------------------- /boards/AVR_MKS_GEN_L_V2/pins_MKS_GEN_L_V2.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief a pins configuration file for an MKS Gen L V2.1 OAT. 3 | * https://github.com/makerbase-mks/MKS-GEN_L/blob/master/hardware/MKS%20Gen_L%20V2.0_001/MKS%20Gen_L%20V2.0_001%20PIN.pdf 4 | */ 5 | 6 | #pragma once 7 | 8 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 9 | #ifndef RA_STEP_PIN 10 | #define RA_STEP_PIN 54 // STEP 11 | #endif 12 | #ifndef RA_DIR_PIN 13 | #define RA_DIR_PIN 55 // DIR 14 | #endif 15 | #ifndef RA_EN_PIN 16 | #define RA_EN_PIN 38 // Enable 17 | #endif 18 | #ifndef RA_DIAG_PIN 19 | #define RA_DIAG_PIN 3 // only needed for autohome function 20 | #endif 21 | #ifndef RA_MS0_PIN 22 | #define RA_MS0_PIN 51 23 | #endif 24 | #ifndef RA_MS1_PIN 25 | #define RA_MS1_PIN 52 26 | #endif 27 | #ifndef RA_MS2_PIN 28 | #define RA_MS2_PIN 63 29 | #endif 30 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 31 | #ifndef RA_SERIAL_PORT_TX 32 | #define RA_SERIAL_PORT_TX 40 // SoftwareSerial TX port 33 | #endif 34 | #ifndef RA_SERIAL_PORT_RX 35 | #define RA_SERIAL_PORT_RX 63 // SoftwareSerial RX port 36 | #endif 37 | #ifndef RA_DRIVER_ADDRESS 38 | #define RA_DRIVER_ADDRESS 0b00 39 | #endif 40 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 41 | #ifndef DEC_STEP_PIN 42 | #define DEC_STEP_PIN 60 // STEP 43 | #endif 44 | #ifndef DEC_DIR_PIN 45 | #define DEC_DIR_PIN 61 // DIR 46 | #endif 47 | #ifndef DEC_EN_PIN 48 | #define DEC_EN_PIN 56 // Enable 49 | #endif 50 | #ifndef DEC_DIAG_PIN 51 | #define DEC_DIAG_PIN 14 // only needed for autohome function 52 | #endif 53 | #ifndef DEC_MS0_PIN 54 | #define DEC_MS0_PIN 51 55 | #endif 56 | #ifndef DEC_MS1_PIN 57 | #define DEC_MS1_PIN 52 58 | #endif 59 | #ifndef DEC_MS2_PIN 60 | #define DEC_MS2_PIN 64 61 | #endif 62 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 63 | #ifndef DEC_SERIAL_PORT_TX 64 | #define DEC_SERIAL_PORT_TX 59 // SoftwareSerial TX port 65 | #endif 66 | #ifndef DEC_SERIAL_PORT_RX 67 | #define DEC_SERIAL_PORT_RX 64 // SoftwareSerial RX port 68 | #endif 69 | #ifndef DEC_DRIVER_ADDRESS 70 | #define DEC_DRIVER_ADDRESS 0b00 71 | #endif 72 | 73 | #define SW_SERIAL_UART 1 74 | 75 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 76 | #ifndef AZ_STEP_PIN 77 | #define AZ_STEP_PIN 46 // STEP 78 | #endif 79 | #ifndef AZ_DIR_PIN 80 | #define AZ_DIR_PIN 48 // DIR 81 | #endif 82 | #ifndef AZ_EN_PIN 83 | #define AZ_EN_PIN 62 // Enable 84 | #endif 85 | #ifndef AZ_DIAG_PIN 86 | #define AZ_DIAG_PIN 18 // only needed for autohome function 87 | #endif 88 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 89 | #ifndef AZ_SERIAL_PORT_TX 90 | #define AZ_SERIAL_PORT_TX 42 // SoftwareSerial TX port 91 | #endif 92 | #ifndef AZ_SERIAL_PORT_RX 93 | #define AZ_SERIAL_PORT_RX 65 // SoftwareSerial RX port 94 | #endif 95 | #ifndef AZ_DRIVER_ADDRESS 96 | #define AZ_DRIVER_ADDRESS 0b00 97 | #endif 98 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 99 | #ifndef ALT_STEP_PIN 100 | #define ALT_STEP_PIN 26 // STEP 101 | #endif 102 | #ifndef ALT_DIR_PIN 103 | #define ALT_DIR_PIN 28 // DIR 104 | #endif 105 | #ifndef ALT_EN_PIN 106 | #define ALT_EN_PIN 24 // Enable 107 | #endif 108 | #ifndef ALT_DIAG_PIN 109 | #define ALT_DIAG_PIN 2 // only needed for autohome function 110 | #endif 111 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 112 | #ifndef ALT_SERIAL_PORT_TX 113 | #define ALT_SERIAL_PORT_TX 44 // SoftwareSerial TX port 114 | #endif 115 | #ifndef ALT_SERIAL_PORT_RX 116 | #define ALT_SERIAL_PORT_RX 66 // SoftwareSerial RX port 117 | #endif 118 | #ifndef ALT_DRIVER_ADDRESS 119 | #define ALT_DRIVER_ADDRESS 0b00 120 | #endif 121 | 122 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering. This is the E1 port. 123 | #ifndef FOCUS_STEP_PIN 124 | #define FOCUS_STEP_PIN 36 // STEP 125 | #endif 126 | #ifndef FOCUS_DIR_PIN 127 | #define FOCUS_DIR_PIN 34 // DIR 128 | #endif 129 | #ifndef FOCUS_EN_PIN 130 | #define FOCUS_EN_PIN 30 // Enable 131 | #endif 132 | #ifndef FOCUS_DIAG_PIN 133 | #define FOCUS_DIAG_PIN 15 // only needed for autohome function 134 | #endif 135 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 136 | #ifndef FOCUS_SERIAL_PORT_TX 137 | #define FOCUS_SERIAL_PORT_TX 20 // SoftwareSerial TX port 138 | #endif 139 | #ifndef FOCUS_SERIAL_PORT_RX 140 | #define FOCUS_SERIAL_PORT_RX 21 // SoftwareSerial RX port 141 | #endif 142 | #ifndef FOCUS_DRIVER_ADDRESS 143 | #define FOCUS_DRIVER_ADDRESS 0b00 144 | #endif 145 | 146 | //GPS pin configuration 147 | #ifndef GPS_SERIAL_PORT 148 | #define GPS_SERIAL_PORT Serial1 149 | #endif 150 | 151 | // DISPLAY_TYPE_LCD_KEYPAD requires 6 digital & 1 analog output in Arduino pin numbering 152 | #ifndef LCD_PIN4 153 | #define LCD_PIN4 17 154 | #endif 155 | #ifndef LCD_PIN5 156 | #define LCD_PIN5 16 157 | #endif 158 | #ifndef LCD_PIN6 159 | #define LCD_PIN6 23 160 | #endif 161 | #ifndef LCD_PIN7 162 | #define LCD_PIN7 25 163 | #endif 164 | #ifndef LCD_PIN8 165 | #define LCD_PIN8 27 166 | #endif 167 | #ifndef LCD_PIN9 168 | #define LCD_PIN9 29 169 | #endif 170 | 171 | // DISPLAY_TYPE_LCD_KEYPAD requires 1 analog input in Arduino pin numbering 172 | #ifndef LCD_KEY_SENSE_PIN 173 | #define LCD_KEY_SENSE_PIN 58 174 | #endif 175 | 176 | //Pin to turn on dew heater MOSFET 177 | #ifndef DEW_HEATER_1_PIN 178 | #define DEW_HEATER_1_PIN 10 179 | #endif 180 | #ifndef DEW_HEATER_2_PIN 181 | #define DEW_HEATER_2_PIN 7 182 | #endif 183 | 184 | //Serial port for external debugging 185 | #if DEBUG_SEPARATE_SERIAL == 1 186 | #ifndef DEBUG_SERIAL_PORT 187 | #define DEBUG_SERIAL_PORT Serial2 //D16 (LCD_RS) - TXD2 and D17 (LCD_EN) - RXD2 188 | #endif 189 | #else 190 | #ifndef DEBUG_SERIAL_PORT 191 | #define DEBUG_SERIAL_PORT Serial 192 | #endif 193 | #endif 194 | -------------------------------------------------------------------------------- /boards/AVR_MKS_GEN_L_V21/pins_MKS_GEN_L_V21.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief a pins configuration file for an MKS Gen L V2.1 OAT. 3 | * https://github.com/makerbase-mks/MKS-GEN_L/blob/master/hardware/MKS%20Gen_L%20V2.1_001/MKS%20GEN_L%20V2.1_001%20PIN.pdf 4 | */ 5 | 6 | #pragma once 7 | 8 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 9 | #ifndef RA_STEP_PIN 10 | #define RA_STEP_PIN 54 // STEP 11 | #endif 12 | #ifndef RA_DIR_PIN 13 | #define RA_DIR_PIN 55 // DIR 14 | #endif 15 | #ifndef RA_EN_PIN 16 | #define RA_EN_PIN 38 // Enable 17 | #endif 18 | #ifndef RA_DIAG_PIN 19 | #define RA_DIAG_PIN 3 // only needed for autohome function 20 | #endif 21 | #ifndef RA_MS0_PIN 22 | #define RA_MS0_PIN 51 23 | #endif 24 | #ifndef RA_MS1_PIN 25 | #define RA_MS1_PIN 52 26 | #endif 27 | #ifndef RA_MS2_PIN 28 | #define RA_MS2_PIN 63 29 | #endif 30 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 31 | #ifndef RA_SERIAL_PORT_TX 32 | #define RA_SERIAL_PORT_TX 40 // SoftwareSerial TX port 33 | #endif 34 | #ifndef RA_SERIAL_PORT_RX 35 | #define RA_SERIAL_PORT_RX 63 // SoftwareSerial RX port 36 | #endif 37 | #ifndef RA_DRIVER_ADDRESS 38 | #define RA_DRIVER_ADDRESS 0b00 39 | #endif 40 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 41 | #ifndef DEC_STEP_PIN 42 | #define DEC_STEP_PIN 60 // STEP 43 | #endif 44 | #ifndef DEC_DIR_PIN 45 | #define DEC_DIR_PIN 61 // DIR 46 | #endif 47 | #ifndef DEC_EN_PIN 48 | #define DEC_EN_PIN 56 // Enable 49 | #endif 50 | #ifndef DEC_DIAG_PIN 51 | #define DEC_DIAG_PIN 14 // only needed for autohome function 52 | #endif 53 | #ifndef DEC_MS0_PIN 54 | #define DEC_MS0_PIN 51 55 | #endif 56 | #ifndef DEC_MS1_PIN 57 | #define DEC_MS1_PIN 52 58 | #endif 59 | #ifndef DEC_MS2_PIN 60 | #define DEC_MS2_PIN 64 61 | #endif 62 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 63 | #ifndef DEC_SERIAL_PORT_TX 64 | #define DEC_SERIAL_PORT_TX 59 // SoftwareSerial TX port 65 | #endif 66 | #ifndef DEC_SERIAL_PORT_RX 67 | #define DEC_SERIAL_PORT_RX 64 // SoftwareSerial RX port 68 | #endif 69 | #ifndef DEC_DRIVER_ADDRESS 70 | #define DEC_DRIVER_ADDRESS 0b00 71 | #endif 72 | 73 | #define SW_SERIAL_UART 1 74 | #ifndef UART_CONNECTION_TEST_TXRX 75 | #define UART_CONNECTION_TEST_TXRX 1 76 | #endif 77 | 78 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 79 | #ifndef AZ_STEP_PIN 80 | #define AZ_STEP_PIN 46 // STEP 81 | #endif 82 | #ifndef AZ_DIR_PIN 83 | #define AZ_DIR_PIN 48 // DIR 84 | #endif 85 | #ifndef AZ_EN_PIN 86 | #define AZ_EN_PIN 62 // Enable 87 | #endif 88 | #ifndef AZ_DIAG_PIN 89 | #define AZ_DIAG_PIN 18 // only needed for autohome function 90 | #endif 91 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 92 | #ifndef AZ_SERIAL_PORT_TX 93 | #define AZ_SERIAL_PORT_TX 42 // SoftwareSerial TX port 94 | #endif 95 | #ifndef AZ_SERIAL_PORT_RX 96 | #define AZ_SERIAL_PORT_RX 65 // SoftwareSerial RX port 97 | #endif 98 | #ifndef AZ_DRIVER_ADDRESS 99 | #define AZ_DRIVER_ADDRESS 0b00 100 | #endif 101 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 102 | #ifndef ALT_STEP_PIN 103 | #define ALT_STEP_PIN 26 // STEP 104 | #endif 105 | #ifndef ALT_DIR_PIN 106 | #define ALT_DIR_PIN 28 // DIR 107 | #endif 108 | #ifndef ALT_EN_PIN 109 | #define ALT_EN_PIN 24 // Enable 110 | #endif 111 | #ifndef ALT_DIAG_PIN 112 | #define ALT_DIAG_PIN 2 // only needed for autohome function 113 | #endif 114 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 115 | #ifndef ALT_SERIAL_PORT_TX 116 | #define ALT_SERIAL_PORT_TX 44 // SoftwareSerial TX port 117 | #endif 118 | #ifndef ALT_SERIAL_PORT_RX 119 | #define ALT_SERIAL_PORT_RX 66 // SoftwareSerial RX port 120 | #endif 121 | #ifndef ALT_DRIVER_ADDRESS 122 | #define ALT_DRIVER_ADDRESS 0b00 123 | #endif 124 | 125 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering. This is the E1 port. 126 | #ifndef FOCUS_STEP_PIN 127 | #define FOCUS_STEP_PIN 36 // STEP 128 | #endif 129 | #ifndef FOCUS_DIR_PIN 130 | #define FOCUS_DIR_PIN 34 // DIR 131 | #endif 132 | #ifndef FOCUS_EN_PIN 133 | #define FOCUS_EN_PIN 30 // Enable 134 | #endif 135 | #ifndef FOCUS_DIAG_PIN 136 | #define FOCUS_DIAG_PIN 15 // only needed for autohome function 137 | #endif 138 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 139 | #ifndef FOCUS_SERIAL_PORT_TX 140 | #define FOCUS_SERIAL_PORT_TX 20 // SoftwareSerial TX port 141 | #endif 142 | #ifndef FOCUS_SERIAL_PORT_RX 143 | #define FOCUS_SERIAL_PORT_RX 12 // SoftwareSerial RX port 144 | #endif 145 | #ifndef FOCUS_DRIVER_ADDRESS 146 | #define FOCUS_DRIVER_ADDRESS 0b00 147 | #endif 148 | 149 | // RA Homing pin for Hall sensor 150 | #ifndef RA_HOMING_SENSOR_PIN 151 | #define RA_HOMING_SENSOR_PIN 53 152 | #endif 153 | 154 | // DEC Homing pin for Hall sensor 155 | #ifndef DEC_HOMING_SENSOR_PIN 156 | #define DEC_HOMING_SENSOR_PIN 52 157 | #endif 158 | 159 | // RA End Switch East pin 160 | #ifndef RA_ENDSWITCH_EAST_SENSOR_PIN 161 | #define RA_ENDSWITCH_EAST_SENSOR_PIN 19 162 | #endif 163 | 164 | // RA End Switch West pin 165 | #ifndef RA_ENDSWITCH_WEST_SENSOR_PIN 166 | #define RA_ENDSWITCH_WEST_SENSOR_PIN 18 167 | #endif 168 | 169 | // DEC End Switch Up pin 170 | #ifndef DEC_ENDSWITCH_UP_SENSOR_PIN 171 | #define DEC_ENDSWITCH_UP_SENSOR_PIN 3 172 | #endif 173 | 174 | // DEC End Switch Down pin 175 | #ifndef DEC_ENDSWITCH_DOWN_SENSOR_PIN 176 | #define DEC_ENDSWITCH_DOWN_SENSOR_PIN 2 177 | #endif 178 | 179 | //GPS pin configuration 180 | #ifndef GPS_SERIAL_PORT 181 | #define GPS_SERIAL_PORT Serial1 182 | #endif 183 | 184 | // DISPLAY_TYPE_LCD_KEYPAD requires 6 digital & 1 analog output in Arduino pin numbering 185 | #ifndef LCD_PIN4 186 | #define LCD_PIN4 17 187 | #endif 188 | #ifndef LCD_PIN5 189 | #define LCD_PIN5 16 190 | #endif 191 | #ifndef LCD_PIN6 192 | #define LCD_PIN6 23 193 | #endif 194 | #ifndef LCD_PIN7 195 | #define LCD_PIN7 25 196 | #endif 197 | #ifndef LCD_PIN8 198 | #define LCD_PIN8 27 199 | #endif 200 | #ifndef LCD_PIN9 201 | #define LCD_PIN9 29 202 | #endif 203 | 204 | // DISPLAY_TYPE_LCD_KEYPAD requires 1 analog input in Arduino pin numbering 205 | #ifndef LCD_KEY_SENSE_PIN 206 | #define LCD_KEY_SENSE_PIN 58 207 | #endif 208 | 209 | //Pin to turn on dew heater MOSFET 210 | #ifndef DEW_HEATER_1_PIN 211 | #define DEW_HEATER_1_PIN 10 212 | #endif 213 | #ifndef DEW_HEATER_2_PIN 214 | #define DEW_HEATER_2_PIN 7 215 | #endif 216 | 217 | //Serial port for external debugging 218 | #if DEBUG_SEPARATE_SERIAL == 1 219 | #ifndef DEBUG_SERIAL_PORT 220 | #define DEBUG_SERIAL_PORT Serial2 //D16 (LCD_RS) - TXD2 and D17 (LCD_EN) - RXD2 221 | #endif 222 | #else 223 | #ifndef DEBUG_SERIAL_PORT 224 | #define DEBUG_SERIAL_PORT Serial 225 | #endif 226 | #endif 227 | -------------------------------------------------------------------------------- /boards/ESP32_ESP32DEV/pins_ESP32DEV.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief a pins configuration file for an ESP32-based OAT. 3 | */ 4 | 5 | #pragma once 6 | 7 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 8 | #ifndef RA_STEP_PIN 9 | #define RA_STEP_PIN 19 // STEP 10 | #endif 11 | #ifndef RA_DIR_PIN 12 | #define RA_DIR_PIN 21 // DIR 13 | #endif 14 | #ifndef RA_EN_PIN 15 | #define RA_EN_PIN 22 // Enable 16 | #endif 17 | #ifndef RA_DIAG_PIN 18 | #define RA_DIAG_PIN 23 // only needed for autohome function 19 | #endif 20 | #ifndef RA_MS0_PIN 21 | #define RA_MS0_PIN 4 22 | #endif 23 | #ifndef RA_MS1_PIN 24 | #define RA_MS1_PIN 0 25 | #endif 26 | #ifndef RA_MS2_PIN 27 | #define RA_MS2_PIN 2 28 | #endif 29 | // DRIVER_TYPE_TMC2209_UART HardwareSerial port, can be shared across all drivers 30 | #ifndef RA_SERIAL_PORT 31 | #define RA_SERIAL_PORT Serial2 32 | #endif 33 | #ifndef RA_DRIVER_ADDRESS 34 | #define RA_DRIVER_ADDRESS 0b00 // Set by MS1/MS2. LOW/LOW in this case 35 | #endif 36 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 37 | #ifndef DEC_STEP_PIN 38 | #define DEC_STEP_PIN 16 // STEP 39 | #endif 40 | #ifndef DEC_DIR_PIN 41 | #define DEC_DIR_PIN 17 // DIR 42 | #endif 43 | #ifndef DEC_EN_PIN 44 | #define DEC_EN_PIN 5 // Enable 45 | #endif 46 | #ifndef DEC_DIAG_PIN 47 | #define DEC_DIAG_PIN 18 // only needed for autohome function 48 | #endif 49 | #ifndef DEC_MS0_PIN 50 | #define DEC_MS0_PIN 15 51 | #endif 52 | #ifndef DEC_MS1_PIN 53 | #define DEC_MS1_PIN 8 54 | #endif 55 | #ifndef DEC_MS2_PIN 56 | #define DEC_MS2_PIN 7 57 | #endif 58 | // DRIVER_TYPE_TMC2209_UART HardwareSerial port, can be shared across all drivers 59 | #ifndef DEC_SERIAL_PORT 60 | #define DEC_SERIAL_PORT Serial2 // SoftwareSerial TX port 61 | #endif 62 | #ifndef DEC_DRIVER_ADDRESS 63 | #define DEC_DRIVER_ADDRESS 0b01 // Set by MS1/MS2 (MS1 HIGH, MS2 LOW) 64 | #endif 65 | 66 | #define SW_SERIAL_UART 0 67 | 68 | // DISPLAY_TYPE_LCD_JOY_I2C_SSD1306 requires 3 analog inputs in Arduino pin numbering 69 | #ifndef LCD_KEY_SENSE_X_PIN 70 | #define LCD_KEY_SENSE_X_PIN 34 71 | #endif 72 | #ifndef LCD_KEY_SENSE_Y_PIN 73 | #define LCD_KEY_SENSE_Y_PIN 39 74 | #endif 75 | #ifndef LCD_KEY_SENSE_PUSH_PIN 76 | #define LCD_KEY_SENSE_PUSH_PIN 36 77 | #endif 78 | 79 | //Serial port for external debugging 80 | #if DEBUG_SEPARATE_SERIAL == 1 81 | #ifndef DEBUG_SERIAL_PORT 82 | #error "There is no default separate serial port for ESP32, please define DEBUG_SERIAL_PORT" 83 | #endif 84 | #else 85 | #ifndef DEBUG_SERIAL_PORT 86 | #define DEBUG_SERIAL_PORT Serial 87 | #endif 88 | #endif 89 | -------------------------------------------------------------------------------- /boards/RAMPS/pins_RAMPS.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief a pins configuration file for a RAMPS 1.4 + Arduino Mega OAT. 3 | */ 4 | 5 | #pragma once 6 | 7 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 8 | #ifndef RA_STEP_PIN 9 | #define RA_STEP_PIN 54 // STEP 10 | #endif 11 | #ifndef RA_DIR_PIN 12 | #define RA_DIR_PIN 55 // DIR 13 | #endif 14 | #ifndef RA_EN_PIN 15 | #define RA_EN_PIN 38 // Enable 16 | #endif 17 | #ifndef RA_DIAG_PIN 18 | #define RA_DIAG_PIN 43 // only needed for autohome function 19 | #endif 20 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 21 | #ifndef RA_SERIAL_PORT_TX 22 | #define RA_SERIAL_PORT_TX 23 // SoftwareSerial TX port 23 | #endif 24 | #ifndef RA_SERIAL_PORT_RX 25 | #define RA_SERIAL_PORT_RX 25 // SoftwareSerial RX port 26 | #endif 27 | #ifndef RA_DRIVER_ADDRESS 28 | #define RA_DRIVER_ADDRESS 0b00 29 | #endif 30 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 31 | #ifndef DEC_STEP_PIN 32 | #define DEC_STEP_PIN 60 // STEP 33 | #endif 34 | #ifndef DEC_DIR_PIN 35 | #define DEC_DIR_PIN 61 // DIR 36 | #endif 37 | #ifndef DEC_EN_PIN 38 | #define DEC_EN_PIN 56 // Enable 39 | #endif 40 | #ifndef DEC_DIAG_PIN 41 | #define DEC_DIAG_PIN 45 // only needed for autohome function 42 | #endif 43 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 44 | #ifndef DEC_SERIAL_PORT_TX 45 | #define DEC_SERIAL_PORT_TX 27 // SoftwareSerial TX port 46 | #endif 47 | #ifndef DEC_SERIAL_PORT_RX 48 | #define DEC_SERIAL_PORT_RX 29 // SoftwareSerial RX port 49 | #endif 50 | #ifndef DEC_DRIVER_ADDRESS 51 | #define DEC_DRIVER_ADDRESS 0b00 52 | #endif 53 | 54 | #define SW_SERIAL_UART 1 55 | 56 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 57 | #ifndef AZ_STEP_PIN 58 | #define AZ_STEP_PIN 46 // STEP 59 | #endif 60 | #ifndef AZ_DIR_PIN 61 | #define AZ_DIR_PIN 48 // DIR 62 | #endif 63 | #ifndef AZ_EN_PIN 64 | #define AZ_EN_PIN 62 // Enable 65 | #endif 66 | #ifndef AZ_DIAG_PIN 67 | #define AZ_DIAG_PIN 47 // only needed for autohome function 68 | #endif 69 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 70 | #ifndef AZ_SERIAL_PORT_TX 71 | #define AZ_SERIAL_PORT_TX 31 // SoftwareSerial TX port 72 | #endif 73 | #ifndef AZ_SERIAL_PORT_RX 74 | #define AZ_SERIAL_PORT_RX 33 // SoftwareSerial RX port 75 | #endif 76 | #ifndef AZ_DRIVER_ADDRESS 77 | #define AZ_DRIVER_ADDRESS 0b00 78 | #endif 79 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering 80 | #ifndef ALT_STEP_PIN 81 | #define ALT_STEP_PIN 26 // STEP 82 | #endif 83 | #ifndef ALT_DIR_PIN 84 | #define ALT_DIR_PIN 28 // DIR 85 | #endif 86 | #ifndef ALT_EN_PIN 87 | #define ALT_EN_PIN 24 // Enable 88 | #endif 89 | #ifndef ALT_DIAG_PIN 90 | #define ALT_DIAG_PIN 32 // only needed for autohome function 91 | #endif 92 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 93 | #ifndef ALT_SERIAL_PORT_TX 94 | #define ALT_SERIAL_PORT_TX 35 // SoftwareSerial TX port 95 | #endif 96 | #ifndef ALT_SERIAL_PORT_RX 97 | #define ALT_SERIAL_PORT_RX 37 // SoftwareSerial RX port 98 | #endif 99 | #ifndef ALT_DRIVER_ADDRESS 100 | #define ALT_DRIVER_ADDRESS 0b00 101 | #endif 102 | 103 | // DRIVER_TYPE_TMC2209_UART requires 4 digital pins in Arduino pin numbering. This is the E1 port. 104 | #ifndef FOCUS_STEP_PIN 105 | #define FOCUS_STEP_PIN 36 // STEP 106 | #endif 107 | #ifndef FOCUS_DIR_PIN 108 | #define FOCUS_DIR_PIN 34 // DIR 109 | #endif 110 | #ifndef FOCUS_EN_PIN 111 | #define FOCUS_EN_PIN 30 // Enable 112 | #endif 113 | #ifndef FOCUS_DIAG_PIN 114 | #define FOCUS_DIAG_PIN 49 // only needed for autohome function 115 | #endif 116 | // DRIVER_TYPE_TMC2209_UART requires 2 additional digital pins for SoftwareSerial, can be shared across all drivers 117 | #ifndef FOCUS_SERIAL_PORT_TX 118 | #define FOCUS_SERIAL_PORT_TX 39 // SoftwareSerial TX port 119 | #endif 120 | #ifndef FOCUS_SERIAL_PORT_RX 121 | #define FOCUS_SERIAL_PORT_RX 41 // SoftwareSerial RX port 122 | #endif 123 | #ifndef FOCUS_DRIVER_ADDRESS 124 | #define FOCUS_DRIVER_ADDRESS 0b00 125 | #endif 126 | 127 | // RA Homing pin for Hall sensor 128 | #ifndef RA_HOMING_SENSOR_PIN 129 | #define RA_HOMING_SENSOR_PIN 53 130 | #endif 131 | 132 | // DEC Homing pin for Hall sensor 133 | #ifndef DEC_HOMING_SENSOR_PIN 134 | #define DEC_HOMING_SENSOR_PIN 52 135 | #endif 136 | 137 | //GPS pin configuration 138 | #ifndef GPS_SERIAL_PORT 139 | #define GPS_SERIAL_PORT Serial2 // Pins 16 and 17 140 | #endif 141 | 142 | // DISPLAY_TYPE_LCD_KEYPAD requires 6 digital & 1 analog output in Arduino pin numbering 143 | #ifndef LCD_PIN4 144 | #define LCD_PIN4 63 145 | #endif 146 | #ifndef LCD_PIN5 147 | #define LCD_PIN5 40 148 | #endif 149 | #ifndef LCD_PIN6 150 | #define LCD_PIN6 42 151 | #endif 152 | #ifndef LCD_PIN7 153 | #define LCD_PIN7 59 154 | #endif 155 | #ifndef LCD_PIN8 156 | #define LCD_PIN8 64 157 | #endif 158 | #ifndef LCD_PIN9 159 | #define LCD_PIN9 44 160 | #endif 161 | 162 | // DISPLAY_TYPE_LCD_KEYPAD requires 1 analog input in Arduino pin numbering 163 | #ifndef LCD_KEY_SENSE_PIN 164 | #define LCD_KEY_SENSE_PIN 65 165 | #endif 166 | 167 | //Pin to turn on dew heater MOSFET 168 | #ifndef DEW_HEATER_1_PIN 169 | #define DEW_HEATER_1_PIN 10 170 | #endif 171 | #ifndef DEW_HEATER_2_PIN 172 | #define DEW_HEATER_2_PIN 9 173 | #endif 174 | 175 | //Serial port for external debugging 176 | #if DEBUG_SEPARATE_SERIAL == 1 177 | #ifndef DEBUG_SERIAL_PORT 178 | #define DEBUG_SERIAL_PORT Serial2 //D16 - TX3 and D17 - RX3 179 | #endif 180 | #else 181 | #ifndef DEBUG_SERIAL_PORT 182 | #define DEBUG_SERIAL_PORT Serial 183 | #endif 184 | #endif 185 | -------------------------------------------------------------------------------- /matrix_build_parallel.py: -------------------------------------------------------------------------------- 1 | """ 2 | Module where all functionality that purely relates to how we parallelize matrix_build.py 3 | should live. It's not a perfect split of course, but it helps to separate the 'matrix' 4 | logic from the 'how we build' logic. 5 | """ 6 | import os 7 | import shutil 8 | import subprocess 9 | import tempfile 10 | import time 11 | from pathlib import Path 12 | from typing import Optional, List 13 | from dataclasses import dataclass 14 | 15 | 16 | @dataclass 17 | class Executor: 18 | """ 19 | Core data that defines a solution that is being built 20 | """ 21 | # The directory where we are building the solution 22 | proj_dir: Path 23 | # The solution dictionary 24 | solution: Optional[dict] = None 25 | # The process building the solution 26 | proc: Optional[subprocess.Popen] = None 27 | # Object that holds tempdir data, so that it can be cleaned up later 28 | tempdir_obj: Optional[tempfile.TemporaryDirectory] = None 29 | 30 | 31 | def generate_config_file(project_location: Path, flag_values: dict): 32 | content = "#pragma once\n\n" 33 | for key, value in flag_values.items(): 34 | content += "#define {} {}\n".format(key, value) 35 | 36 | with open(Path(project_location, "Configuration_local_matrix.hpp"), 'w') as f: 37 | f.write(content) 38 | f.flush() 39 | 40 | 41 | def execute(project_location: Path, board: str, flag_values: dict, jobs: int = 1, out_pipe=True) -> subprocess.Popen: 42 | """ 43 | Start up an executor that is building a solution 44 | :param project_location: The directory where to build the solution 45 | :param board: The board type (aka environment) 46 | :param flag_values: Dictionary of #defines to create a config file from 47 | :param jobs: How many jobs the build process should use 48 | :param out_pipe: If the executor's stdout/stderr should be pipes 49 | :return: Process object that is executing the solution 50 | """ 51 | build_env = dict(os.environ) 52 | build_env["PLATFORMIO_BUILD_FLAGS"] = "-DMATRIX_LOCAL_CONFIG=1" 53 | generate_config_file(project_location, flag_values) 54 | 55 | proc = subprocess.Popen( 56 | ['pio', 57 | 'run', 58 | f'--project-dir={str(project_location.resolve())}', 59 | f'--environment={board}', 60 | f'--jobs={jobs}', 61 | ], 62 | stdout=subprocess.PIPE if out_pipe else None, 63 | stderr=subprocess.PIPE if out_pipe else None, 64 | env=build_env, 65 | close_fds=True, 66 | ) 67 | return proc 68 | 69 | 70 | def get_available_executor_idx(e_list: List[Executor]) -> Optional[int]: 71 | """ 72 | Get the index of an idle executor 73 | :param e_list: List of executors 74 | :return: Idle executor index, else None if all are busy 75 | """ 76 | for i, executor in enumerate(e_list): 77 | if executor.proc is None: 78 | return i 79 | return None 80 | 81 | 82 | def get_finished_executor_idx(e_list: List[Executor]) -> Optional[int]: 83 | """ 84 | Get the index of a finished executor 85 | :param e_list: List of executors 86 | :return: Finished executor index, else None if all are busy 87 | """ 88 | for i, executor in enumerate(e_list): 89 | if executor.proc is not None and executor.proc.poll() is not None: 90 | return i 91 | return None 92 | 93 | 94 | def cleanup_tempdirs(e_list: List[Executor]): 95 | """ 96 | Delete all the temporary directories that executors were using 97 | :param e_list: List of executors 98 | """ 99 | for executor in e_list: 100 | if executor.tempdir_obj is not None: 101 | tempdir_path = executor.tempdir_obj.name 102 | print(f'Deleting {tempdir_path}') 103 | shutil.rmtree(tempdir_path, ignore_errors=True) 104 | 105 | 106 | def create_executors(num_executors: int, local_paths_to_link: List[Path]) -> List[Executor]: 107 | """ 108 | Create a number of executors and their associated temporary directories, then 109 | soft-link all needed project files 110 | :param num_executors: Number of executors to create 111 | :param local_paths_to_link: List of files to soft-link into the executor projects 112 | :return: List of executors 113 | """ 114 | executor_list: List[Executor] = [] 115 | print(f'Creating {num_executors} executors') 116 | for executor_idx in range(num_executors): 117 | tempdir = tempfile.TemporaryDirectory() 118 | temp_proj_path = Path(tempdir.name) 119 | for local_path in local_paths_to_link: 120 | temp_dst_path = Path(temp_proj_path, local_path).resolve() 121 | os.makedirs(temp_dst_path.parent, exist_ok=True) 122 | os.symlink(local_path.resolve(), temp_dst_path) 123 | executor_list.append(Executor(temp_proj_path, tempdir_obj=tempdir)) 124 | print(f'{executor_idx} ', end='') 125 | print() 126 | return executor_list 127 | 128 | 129 | def copy_caches_to_executors(src_proj_dir: Path, dst_executors: List[Executor]): 130 | """ 131 | Copy cache directories from a source directory to a number of executor project directories 132 | :param src_proj_dir: Directory to copy from 133 | :param dst_executors: List of executors to copy to 134 | """ 135 | print('Copying caches to other executors') 136 | dir_names_to_copy = ['.pio', 'build_cache'] 137 | for dir_name_to_copy in dir_names_to_copy: 138 | src_path = Path(src_proj_dir, dir_name_to_copy) 139 | for dst_executor in dst_executors: 140 | dst_path = Path(dst_executor.proj_dir, dir_name_to_copy) 141 | shutil.copytree(src_path, dst_path) 142 | 143 | 144 | def get_source_files_to_link() -> List[Path]: 145 | """ 146 | Create a list of the important files from the local project. I didn't want to 147 | use git here, since that might not pick up untracked (but needed) files. 148 | :return: List of source files that a project needs in order to compile 149 | """ 150 | local_proj_path = Path('.') 151 | venv_dirs = list(local_proj_path.glob('*venv*/')) 152 | # Don't link the .pio directory because the builds need to be independent 153 | pio_dirs = list(local_proj_path.glob('*.pio*/')) 154 | cmake_dirs = list(local_proj_path.glob('*cmake-build*/')) 155 | 156 | local_dirs_to_not_link = [Path('.git/'), Path('build_cache/')] + venv_dirs + pio_dirs + cmake_dirs 157 | local_filenames_to_not_link = [ 158 | Path('Configuration_local.hpp'), 159 | Path('Configuration_local_matrix.hpp'), 160 | ] 161 | 162 | local_paths_to_link = [] 163 | for local_dir_str, local_subdirs, local_files in os.walk(local_proj_path): 164 | local_dir_path = Path(local_dir_str) 165 | dir_shouldnt_be_linked = any(d == local_dir_path or d in local_dir_path.parents for d in local_dirs_to_not_link) 166 | if dir_shouldnt_be_linked: 167 | continue 168 | for local_file in local_files: 169 | local_file_full_path = Path(local_dir_path, local_file) 170 | file_shouldnt_be_linked = any(local_file_full_path == f for f in local_filenames_to_not_link) 171 | if file_shouldnt_be_linked: 172 | continue 173 | local_paths_to_link.append(local_file_full_path) 174 | return local_paths_to_link 175 | 176 | 177 | def wait_for_executor_to_finish(executor_list: List[Executor], timeout=0.1, poll_time=0.2): 178 | """ 179 | Block until an executor has finished building 180 | :param executor_list: List of executors 181 | :param timeout: Time to communicate() with the running process (kind of a hack) 182 | :param poll_time: Time to wait before checking all executors again 183 | """ 184 | while get_finished_executor_idx(executor_list) is None: 185 | for e in executor_list: 186 | if e.proc is not None and e.proc.poll() is None: 187 | # Communicate with the running processes to stop them from blocking 188 | # (i.e. they spew too much output) 189 | try: 190 | _ = e.proc.communicate(timeout=timeout) 191 | except subprocess.TimeoutExpired: 192 | pass # This is expected and what should happen 193 | time.sleep(poll_time) 194 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | [platformio] 2 | include_dir = . 3 | src_dir = ./src 4 | lib_dir = ./src/libs 5 | test_dir = ./unit_tests 6 | build_cache_dir = ./build_cache 7 | 8 | [common] 9 | lib_deps = 10 | mikalhart/TinyGPSPlus @ ^1.0.2 11 | teemuatlut/TMCStepper @ ^0.7.1 12 | waspinator/AccelStepper @ ^1.61 13 | arduino-libraries/LiquidCrystal @ ^1.0.7 14 | lincomatic/LiquidTWI2@^1.2.7 15 | olikraus/U8g2@^2.28.8 16 | https://github.com/ClutchplateDude/esp8266-oled-ssd1306#4.6.2 17 | 18 | [env] 19 | extra_scripts = 20 | pre:pre_script_patch_debug.py 21 | post:post_script_remove_patched_files.py 22 | build_unflags = 23 | -Os 24 | build_flags = 25 | ; Use non STL version of ETL 26 | -D ETL_NO_STL 27 | ; Can change to -Os if we get size constrained 28 | -include ./src/macros/gcc/Macros.hpp 29 | -O2 30 | debug_build_flags = 31 | ; Optimize for debugging 32 | -Og 33 | ; Include debug symbols 34 | -g2 35 | ; PP define that says we are compiling for debug 36 | -D OAT_DEBUG_BUILD 37 | 38 | [common_embedded] 39 | framework = arduino 40 | monitor_speed = 19200 41 | upload_speed = 115200 42 | test_ignore = test_native 43 | src_filter = 44 | +<*> -<../.git/> -<../test/> 45 | -<*/.pio/> -<*/.platformio/> - 46 | src_build_flags = 47 | ; Warnings to be enabled as codebase is refined and fixed up 48 | -Werror 49 | -Wall 50 | -Wextra 51 | ; -Wpedantic 52 | -Wno-unused-parameter 53 | ; -Wold-style-cast 54 | -Wlogical-op 55 | -Wuseless-cast 56 | ; Wdouble-promotion can't be enabled until GCC bug 55578 is fixed, since floats are 57 | ; implicitly converted to doubles when passed to a variadic function (i.e. a printf-like). 58 | ; Else we could disable Wdouble-promotion only for our logv function inside of the LOG macro 59 | ; -Wdouble-promotion 60 | ; -Wconversion 61 | ; -Wfloat-conversion 62 | ; -Wsign-conversion 63 | -Wshadow 64 | ; -Wsuggest-final-types 65 | -Wunknown-pragmas 66 | ; Have to use Wunknown-pragmas instead of Wundef because of a bug in the ESP32 GCC compiler 67 | ; See https://stackoverflow.com/questions/31509434/gcc-does-not-honor-pragma-gcc-diagnostic-to-silence-warnings 68 | ; platformio says that it will auto-discover the debug port if not set, but it just doesn't 69 | ;debug_port = /dev/ttyACM0 70 | ; Do not set an initial breakpoint 71 | debug_init_break = 72 | ; Always upload firmware when initializing a debug session 73 | debug_load_mode = always 74 | 75 | [env:ramps] 76 | extends = common_embedded 77 | ; atmelavr@5.0.0 is currently broken (can't upload, gets stk500v2_recv errors) 78 | ; but https://github.com/platformio/platform-atmelavr.git @ d9d2738b 79 | ; works, even though there are no commits since the 5.0.0 release?!?!?!?!?!?! 80 | platform = atmelavr@4.2.0 81 | board = ATmega2560 82 | upload_protocol = wiring 83 | build_flags = 84 | ${env.build_flags} 85 | -D BOARD=BOARD_AVR_RAMPS 86 | debug_tool = avr-stub 87 | debug_build_flags = 88 | ${env.debug_build_flags} 89 | ; 0: FLASH breakpoints with avr-stub bootloader 90 | ; 1: RAM only - RAM breakpoints and stepping 91 | ; 2: FLASH breakpoints with Optiboot bootloader (using do_spm() function) 92 | ; Note that only RAM breakpoints are supported on ATmega in version 1.2 of avr-debugger! 93 | -D AVR8_BREAKPOINT_MODE=1 94 | -D AVR8_SWINT_SOURCE=7 95 | lib_deps = 96 | ${common.lib_deps} 97 | jdolinay/avr-debugger @ 1.2 98 | https://github.com/andre-stefanov/avr-interrupt-stepper#0.0.4 99 | 100 | [env:mksgenlv21] 101 | extends = env:ramps 102 | build_flags = 103 | ${env.build_flags} 104 | -D BOARD=BOARD_AVR_MKS_GEN_L_V21 105 | 106 | [env:mksgenlv2] 107 | extends = env:ramps 108 | build_flags = 109 | ${env.build_flags} 110 | -D BOARD=BOARD_AVR_MKS_GEN_L_V2 111 | 112 | [env:mksgenlv1] 113 | extends = env:ramps 114 | build_flags = 115 | ${env.build_flags} 116 | -D BOARD=BOARD_AVR_MKS_GEN_L_V1 117 | 118 | [env:esp32] 119 | extends = common_embedded 120 | platform = espressif32 121 | board = esp32dev 122 | upload_speed = 460800 123 | monitor_filters = esp32_exception_decoder 124 | build_flags = 125 | ${env.build_flags} 126 | -D BOARD=BOARD_ESP32_ESP32DEV 127 | lib_deps = 128 | ${common.lib_deps} 129 | WiFi 130 | 131 | [env:native] 132 | platform = native 133 | test_ignore = test_embedded 134 | -------------------------------------------------------------------------------- /post_script_remove_patched_files.py: -------------------------------------------------------------------------------- 1 | Import("env", "projenv") 2 | 3 | import os 4 | import tempfile 5 | from pathlib import Path 6 | 7 | 8 | def cprint(*args, **kwargs): 9 | print(f'post_script_remove_patched_files.py:', *args, **kwargs) 10 | 11 | 12 | def clean_up_patched_files(*_, **__): 13 | """ 14 | Removes all temporary patched files created previously in the build process 15 | """ 16 | # patch_path_key needs to be kept in sync with pre_script_patch_debug.py 17 | # We put the current directory name in the key so that we only remove 18 | # patched files that we know were built by the current build process. 19 | # This is only useful in safeguarding against multiple builds being done in 20 | # different directories at the same time. (i.e. we don't want to remove another 21 | # processes' files while they are still in use) 22 | project_dir_name = Path.cwd().name 23 | patch_path_key = f'_{project_dir_name}_patched_' 24 | tempdir_path = tempfile.gettempdir() 25 | cprint(f'Temp file dir is {tempdir_path}') 26 | patched_filepaths = [] 27 | for filename in os.listdir(tempdir_path): 28 | full_filepath = os.path.join(tempdir_path, filename) 29 | if os.path.isfile(full_filepath) and patch_path_key in filename: 30 | patched_filepaths.append(full_filepath) 31 | for patched_filepath in patched_filepaths: 32 | cprint(f'Removing {patched_filepath}') 33 | try: 34 | os.remove(patched_filepath) 35 | pass 36 | except FileNotFoundError: 37 | cprint('Not found (deleted already?)') 38 | 39 | 40 | env.AddPostAction('buildprog', clean_up_patched_files) 41 | -------------------------------------------------------------------------------- /pre_script_patch_debug.py: -------------------------------------------------------------------------------- 1 | Import("env") 2 | 3 | import os 4 | import tempfile 5 | from pathlib import Path 6 | 7 | 8 | def cprint(*args, **kwargs): 9 | print(f'pre_script_patch_debug.py:', *args, **kwargs) 10 | 11 | 12 | def get_winterrupts_path(): 13 | winterrupts_path = None 14 | for pio_package in env.PioPlatform().dump_used_packages(): 15 | pio_dir = env.PioPlatform().get_package_dir(pio_package['name']) 16 | # TODO: This should change for non-mega cores! 17 | possible_path = os.path.join(pio_dir, 'cores', 'MegaCore', 'WInterrupts.c') 18 | if os.path.exists(possible_path): 19 | cprint(f'Found WInterrupts.c: {possible_path}') 20 | winterrupts_path = possible_path 21 | return winterrupts_path 22 | 23 | 24 | def patch_function_factory(src_path, output_suffix, replacement_list): 25 | """ 26 | Creates a function that will return a filepath to a patched source file 27 | :param src_path: The actual source path on disk, this is different than node.get_abspath() 28 | :param output_suffix: The suffix for the output temporary file 29 | :param replacement_list: List of 'in'/'out' pairs that should be replaced 30 | :return: Build Middleware function 31 | """ 32 | def out_func(node): 33 | # patch_path_key needs to be kept in sync with post_script_remove_patched_files.py 34 | # so that after a successful build the patched file can be removed 35 | project_dir_name = Path.cwd().name # See post_script_remove_patched_files.py on why this is needed 36 | patch_path_key = f'_{project_dir_name}_patched_' 37 | with tempfile.NamedTemporaryFile(mode='w', suffix=f'{patch_path_key}{output_suffix}', delete=False) as tf: 38 | patched_filepath = tf.name 39 | cprint(f'Patching {src_path}') 40 | cprint(f'Replacement path: {patched_filepath}') 41 | cprint(f'Build path: {node.get_abspath()}') 42 | with open(src_path, 'r') as wint_f: 43 | for wint_line in wint_f.readlines(): 44 | # Default is to just path the line through un-replaced 45 | out_line = wint_line 46 | # Now we check if line is in the replacements list 47 | for replacement in replacement_list: 48 | if replacement['in'] in wint_line: 49 | out_line = replacement['out'] 50 | break 51 | # Write the (possibly replaced) line to the output temporary file 52 | tf.write(out_line) 53 | return env.File(patched_filepath) 54 | return out_func 55 | 56 | 57 | source_patch_dict = { 58 | '*WInterrupts.c': { 59 | 'actual_src_path': get_winterrupts_path(), 60 | 'patches': [ 61 | { 62 | 'in': 'IMPLEMENT_ISR(INT7_vect, EXTERNAL_INT_7)', 63 | 'out': '''\ 64 | #if defined(OAT_DEBUG_BUILD) 65 | #pragma message "OAT_DEBUG_BUILD is defined, ISR 7 disabled in WInterrupts.c" 66 | #else 67 | IMPLEMENT_ISR(INT7_vect, EXTERNAL_INT_7) 68 | #endif 69 | ''' 70 | }, 71 | ] 72 | } 73 | } 74 | 75 | for filepath_glob, file_patch_info in source_patch_dict.items(): 76 | file_src_path = file_patch_info['actual_src_path'] 77 | if not file_src_path: 78 | cprint(f'Could not find {filepath_glob} to patch! Skipping...') 79 | continue 80 | 81 | env.AddBuildMiddleware( 82 | patch_function_factory(src_path=file_src_path, 83 | replacement_list=file_patch_info['patches'], 84 | output_suffix='WInterrupts.c'), 85 | filepath_glob 86 | ) 87 | -------------------------------------------------------------------------------- /requirements_matrix_build.txt: -------------------------------------------------------------------------------- 1 | tabulate~=0.8.7 2 | python-constraint~=1.4.0 3 | # matrix_build.py also requires click, but platformio also requires click so we just let them pick the version 4 | # use the latest version of platformio, same as what a user would install 5 | platformio 6 | -------------------------------------------------------------------------------- /requirements_version_check.txt: -------------------------------------------------------------------------------- 1 | mistune~=2.0.0rc1 2 | robotpy-cppheaderparser~=5.0.14 3 | semver~=2.13.0 4 | GitPython~=3.1.17 -------------------------------------------------------------------------------- /src/Core.cpp: -------------------------------------------------------------------------------- 1 | #include "inc/Globals.hpp" 2 | #include "../Configuration.hpp" 3 | #include "Utility.hpp" 4 | #include "Mount.hpp" 5 | 6 | #include "a_inits.hpp" 7 | #include "b_setup.hpp" 8 | #include "c65_startup.hpp" 9 | #include "c70_menuRA.hpp" 10 | #include "c71_menuDEC.hpp" 11 | #include "c722_menuPOI.hpp" 12 | #include "c725_menuHOME.hpp" 13 | #include "c72_menuHA.hpp" 14 | #include "c72_menuHA_GPS.hpp" 15 | #include "c75_menuCTRL.hpp" 16 | #include "c76_menuCAL.hpp" 17 | #include "c78_menuINFO.hpp" 18 | #include "c_buttons.hpp" 19 | #include "f_serial.hpp" 20 | 21 | #ifdef NEW_STEPPER_LIB 22 | #ifdef ARDUINO_ARCH_AVR 23 | ISR(TIMER1_OVF_vect) 24 | { 25 | IntervalInterrupt_AVR::handle_overflow(); 26 | } 27 | ISR(TIMER1_COMPA_vect) 28 | { 29 | IntervalInterrupt_AVR::handle_compare_match(); 30 | } 31 | ISR(TIMER3_OVF_vect) 32 | { 33 | IntervalInterrupt_AVR::handle_overflow(); 34 | } 35 | ISR(TIMER3_COMPA_vect) 36 | { 37 | IntervalInterrupt_AVR::handle_compare_match(); 38 | } 39 | ISR(TIMER4_OVF_vect) 40 | { 41 | IntervalInterrupt_AVR::handle_overflow(); 42 | } 43 | ISR(TIMER4_COMPA_vect) 44 | { 45 | IntervalInterrupt_AVR::handle_compare_match(); 46 | } 47 | ISR(TIMER5_OVF_vect) 48 | { 49 | IntervalInterrupt_AVR::handle_overflow(); 50 | } 51 | ISR(TIMER5_COMPA_vect) 52 | { 53 | IntervalInterrupt_AVR::handle_compare_match(); 54 | } 55 | #endif 56 | #endif -------------------------------------------------------------------------------- /src/DayTime.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // A class to handle hours, minutes, seconds in a unified manner, allowing 4 | // addition of hours, minutes, seconds, other times and conversion to string. 5 | 6 | // Forward declarations 7 | class String; 8 | 9 | // DayTime handles a 24-hour time. 10 | class DayTime 11 | { 12 | protected: 13 | long totalSeconds; 14 | 15 | public: 16 | DayTime(); 17 | 18 | DayTime(const DayTime &other); 19 | DayTime(int h, int m, int s); 20 | 21 | // From hours 22 | DayTime(float timeInHours); 23 | 24 | int getHours() const; 25 | int getMinutes() const; 26 | int getSeconds() const; 27 | float getTotalHours() const; 28 | float getTotalMinutes() const; 29 | long getTotalSeconds() const; 30 | 31 | void getTime(int &h, int &m, int &s) const; 32 | virtual void set(int h, int m, int s); 33 | void set(const DayTime &other); 34 | 35 | // Add hours, wrapping days (which are not tracked). Negative or positive. 36 | virtual void addHours(float deltaHours); 37 | 38 | // Add minutes, wrapping hours if needed 39 | void addMinutes(int deltaMins); 40 | 41 | // Add seconds, wrapping minutes and hours if needed 42 | void addSeconds(long deltaSecs); 43 | 44 | // Add time components, wrapping seconds, minutes and hours if needed 45 | void addTime(int deltaHours, int deltaMinutes, int deltaSeconds); 46 | 47 | // Add another time, wrapping seconds, minutes and hours if needed 48 | void addTime(const DayTime &other); 49 | // Subtract another time, wrapping seconds, minutes and hours if needed 50 | 51 | void subtractTime(const DayTime &other); 52 | 53 | // Convert to a standard string (like 14:45:06) 54 | virtual const char *ToString() const; 55 | virtual const char *formatString(char *targetBuffer, const char *format, long *pSeconds = nullptr) const; 56 | 57 | //protected: 58 | virtual void checkHours(); 59 | 60 | static DayTime ParseFromMeade(String const &s); 61 | 62 | protected: 63 | const char *formatStringImpl(char *targetBuffer, const char *format, char sgn, long degs, long mins, long secs) const; 64 | void printTwoDigits(char *achDegs, int num) const; 65 | 66 | private: 67 | static long const secondsPerDay = 24L * 3600L; /// Real seconds (not sidereal) 68 | }; 69 | -------------------------------------------------------------------------------- /src/Declination.cpp: -------------------------------------------------------------------------------- 1 | #include "./inc/Globals.hpp" 2 | #include "../Configuration.hpp" 3 | #include "Utility.hpp" 4 | #include "Declination.hpp" 5 | 6 | ////////////////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Declination 9 | // 10 | // A class to handle degrees, minutes, seconds of a declination 11 | 12 | // Parses the RA or DEC from a string that has an optional sign, a two digit degree, a seperator, a two digit minute, a seperator and a two digit second. 13 | // For example: -45*32:11 or 23:44:22 14 | 15 | // In the NORTHERN hemisphere, 0 is north pole, 180 and -180 is south pole 16 | //------------------ S---------------------------------------- N ---------------------------------- S 17 | // Celestial -90 -60 -30 0 30 60 90 60 30 0 -30 -60 -90 18 | // Celestial = 90 - abs(Declination) 19 | // Declination -180 -150 -120 -90 -60 -30 0 30 60 90 120 150 180 20 | 21 | // In the SOUTHERN hemisphere, 0 is south pole, 180 and -180 is north pole 22 | //------------------ N---------------------------------------- S ---------------------------------- N 23 | // Celestial 90 60 30 0 -30 -60 -90 -60 -30 0 30 60 90 24 | // Celestial = -90 + abs(Declination) 25 | // Declination -180 -150 -120 -90 -60 -30 0 30 60 90 120 150 180 26 | Declination::Declination() : DayTime() 27 | { 28 | } 29 | 30 | Declination::Declination(const Declination &other) : DayTime(other) 31 | { 32 | } 33 | 34 | Declination::Declination(int h, int m, int s) : DayTime(h, m, s) 35 | { 36 | } 37 | 38 | Declination::Declination(float inDegrees) : DayTime(inDegrees) 39 | { 40 | } 41 | 42 | void Declination::set(int h, int m, int s) 43 | { 44 | Declination dt(h, m, s); 45 | totalSeconds = dt.totalSeconds; 46 | checkHours(); 47 | } 48 | 49 | void Declination::addDegrees(int deltaDegrees) 50 | { 51 | addHours(deltaDegrees); 52 | } 53 | 54 | float Declination::getTotalDegrees() const 55 | { 56 | return getTotalHours(); 57 | } 58 | 59 | void Declination::checkHours() 60 | { 61 | if (totalSeconds > arcSecondsPerHemisphere) 62 | { 63 | LOG(DEBUG_GENERAL, "[DECLINATION]: CheckHours: Degrees is more than 180, clamping"); 64 | totalSeconds = arcSecondsPerHemisphere; 65 | } 66 | if (totalSeconds < -arcSecondsPerHemisphere) 67 | { 68 | LOG(DEBUG_GENERAL, "[DECLINATION]: CheckHours: Degrees is less than -180, clamping"); 69 | totalSeconds = -arcSecondsPerHemisphere; 70 | } 71 | } 72 | 73 | char achBufDeg[32]; 74 | 75 | // Convert to a standard string (like 14:45:06), specifying separators if needed 76 | const char *Declination::ToDisplayString(char sep1, char sep2) const 77 | { 78 | char achFormat[16]; 79 | sprintf(achFormat, "{d}%c{m}%c{s}", sep1, sep2); 80 | return formatString(achBufDeg, achFormat); 81 | } 82 | 83 | const char *Declination::ToString() const 84 | { 85 | ToDisplayString('*', ':'); 86 | 87 | char *p = achBufDeg + strlen(achBufDeg); 88 | 89 | *p++ = ' '; 90 | *p++ = '('; 91 | strcpy(p, String(inNorthernHemisphere ? 90 - fabsf(getTotalHours()) : -90 + fabsf(getTotalHours()), 4).c_str()); 92 | strcat(p, ", "); 93 | strcat(p, String(getTotalHours(), 4).c_str()); 94 | strcat(p, ")"); 95 | 96 | return achBufDeg; 97 | } 98 | 99 | Declination Declination::ParseFromMeade(String const &s) 100 | { 101 | Declination result; 102 | LOG(DEBUG_MEADE, "[DECLINATION]: Declination.Parse(%s) for %s Hemi", s.c_str(), inNorthernHemisphere ? "N" : "S"); 103 | 104 | // Use the DayTime code to parse it... 105 | DayTime dt = DayTime::ParseFromMeade(s); 106 | LOG(DEBUG_MEADE, "[DECLINATION]: Declination DayTime is %l secs", dt.getTotalSeconds()); 107 | 108 | // ...and then correct for hemisphere 109 | result.totalSeconds = inNorthernHemisphere ? (arcSecondsPerHemisphere / 2) - dt.getTotalSeconds() 110 | : -(arcSecondsPerHemisphere / 2) - dt.getTotalSeconds(); 111 | LOG(DEBUG_MEADE, "[DECLINATION]: Adjust for hemisphere. %s -> %s (%l secs)", s.c_str(), result.ToString(), result.totalSeconds); 112 | return result; 113 | } 114 | 115 | Declination Declination::FromSeconds(long seconds) 116 | { 117 | const auto secondsFloat = static_cast(seconds); 118 | const auto arcSecondsPerHemisphereFloat = static_cast(arcSecondsPerHemisphere); 119 | if (inNorthernHemisphere) 120 | { 121 | return Declination(((arcSecondsPerHemisphereFloat / 2.0f) - secondsFloat) / 3600.0f); 122 | } 123 | return Declination(((arcSecondsPerHemisphereFloat / 2.0f) + secondsFloat) / 3600.0f); 124 | } 125 | 126 | const char *Declination::formatString(char *targetBuffer, const char *format, long *) const 127 | { 128 | long secs 129 | = inNorthernHemisphere ? (arcSecondsPerHemisphere / 2) - labs(totalSeconds) : -(arcSecondsPerHemisphere / 2) + labs(totalSeconds); 130 | return DayTime::formatString(targetBuffer, format, &secs); 131 | } 132 | -------------------------------------------------------------------------------- /src/Declination.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DayTime.hpp" 4 | 5 | // Declination is used to store DEC coordinate. 6 | // The range of time is from -180 degrees to 0 degrees. 7 | // In the northern hemisphere, 0 is north pole, -180 is south pole 8 | // In the southern hemisphere, 0 is south pole, -180 is north pole 9 | class Declination : public DayTime 10 | { 11 | public: 12 | Declination(); 13 | Declination(const Declination &other); 14 | Declination(int h, int m, int s); 15 | Declination(float inDegrees); 16 | 17 | virtual void set(int h, int m, int s); 18 | 19 | // Add degrees, clamp to -180...0 20 | void addDegrees(int deltaDegrees); 21 | 22 | // Get total degrees (-180..0) 23 | float getTotalDegrees() const; 24 | 25 | // Convert to a standard string (like +54:45:06) 26 | virtual const char *ToString() const override; 27 | virtual const char *formatString(char *targetBuffer, const char *format, long *pSeconds = nullptr) const; 28 | 29 | const char *ToDisplayString(char sep1, char sep2) const; 30 | 31 | protected: 32 | virtual void checkHours() override; 33 | 34 | public: 35 | static Declination ParseFromMeade(String const &s); 36 | static Declination FromSeconds(long seconds); 37 | 38 | private: 39 | static long const arcSecondsPerHemisphere = 180L * 60L * 60L; // Arc-seconds in 180 degrees 40 | }; 41 | -------------------------------------------------------------------------------- /src/EndSwitches.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _ENDSWITCHES_HPP 2 | #define _ENDSWITCHES_HPP 3 | 4 | #include "Types.hpp" 5 | #include "Mount.hpp" 6 | 7 | #if (USE_RA_END_SWITCH == 1 || USE_DEC_END_SWITCH == 1) 8 | 9 | class Mount; 10 | 11 | enum EndSwitchState 12 | { 13 | SWITCH_NOT_ACTIVE, 14 | SWITCH_AT_MINIMUM, 15 | SWITCH_AT_MAXIMUM, 16 | WAIT_FOR_STOP_AT_MAXIMUM, 17 | WAIT_FOR_STOP_AT_MINIMUM, 18 | SWITCH_SLEWING_OFF_MINIMUM, 19 | SWITCH_SLEWING_OFF_MAXIMUM, 20 | }; 21 | 22 | ///////////////////////////////// 23 | // 24 | // class EndSwitchState 25 | // 26 | ///////////////////////////////// 27 | class EndSwitch 28 | { 29 | private: 30 | EndSwitchState _state; 31 | Mount *_pMount; 32 | StepperAxis _axis; 33 | long _posWhenTriggered; 34 | int _activeState; 35 | int _inactiveState; 36 | int _dir; 37 | int _minPin; 38 | int _maxPin; 39 | 40 | public: 41 | EndSwitch(Mount *mount, StepperAxis axis, int minPin, int maxPin, int activeState); 42 | void processEndSwitchState(); 43 | void checkSwitchState(); 44 | }; 45 | 46 | #endif 47 | #endif 48 | -------------------------------------------------------------------------------- /src/Gyro.cpp: -------------------------------------------------------------------------------- 1 | #include "../Configuration.hpp" 2 | #include "Utility.hpp" 3 | #include "Gyro.hpp" 4 | 5 | #if USE_GYRO_LEVEL == 1 6 | 7 | PUSH_NO_WARNINGS 8 | #include // I2C communication library 9 | POP_NO_WARNINGS 10 | 11 | /** 12 | * Tilt, roll, and temperature measurementusing the MPU-6050 MEMS gyro. 13 | * See: https://invensense.tdk.com/products/motion-tracking/6-axis/mpu-6050/ 14 | * Datasheet: https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Datasheet1.pdf 15 | * Register descriptions: https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf 16 | * */ 17 | 18 | bool Gyro::isPresent(false); 19 | 20 | void Gyro::startup() 21 | /* Starts up the MPU-6050 device. 22 | Reads the WHO_AM_I register to verify if the device is present. 23 | Wakes device from power-down. 24 | Sets accelerometers to minimum bandwidth to reduce measurement noise. 25 | */ 26 | { 27 | // Initialize interface to the MPU6050 28 | LOG(DEBUG_INFO, "[GYRO]:: Starting"); 29 | Wire.begin(); 30 | 31 | // Execute 1 byte read from MPU6050_REG_WHO_AM_I 32 | // This is a read-only register which should have the value 0x68 33 | Wire.beginTransmission(MPU6050_I2C_ADDR); 34 | Wire.write(MPU6050_REG_WHO_AM_I); 35 | Wire.endTransmission(true); 36 | Wire.requestFrom(MPU6050_I2C_ADDR, 1, 1); 37 | byte id = (Wire.read() >> 1) & 0x3F; 38 | isPresent = (id == 0x34); 39 | if (!isPresent) 40 | { 41 | LOG(DEBUG_INFO, "[GYRO]:: Not found!"); 42 | return; 43 | } 44 | 45 | // Execute 1 byte write to MPU6050_REG_PWR_MGMT_1 46 | Wire.beginTransmission(MPU6050_I2C_ADDR); 47 | Wire.write(MPU6050_REG_PWR_MGMT_1); 48 | Wire.write(0); // Disable sleep, 8 MHz clock 49 | Wire.endTransmission(true); 50 | 51 | // Execute 1 byte write to MPU6050_REG_PWR_MGMT_1 52 | Wire.beginTransmission(MPU6050_I2C_ADDR); 53 | Wire.write(MPU6050_REG_CONFIG); 54 | Wire.write(6); // 5Hz bandwidth (lowest) for smoothing 55 | Wire.endTransmission(true); 56 | 57 | LOG(DEBUG_INFO, "[GYRO]:: Started"); 58 | } 59 | 60 | void Gyro::shutdown() 61 | /* Shuts down the MPU-6050 device. 62 | Currently does nothing. 63 | */ 64 | { 65 | LOG(DEBUG_INFO, "[GYRO]: Shutdown"); 66 | // Nothing to do 67 | } 68 | 69 | angle_t Gyro::getCurrentAngles() 70 | /* Returns roll & tilt angles from MPU-6050 device in angle_t object in degrees. 71 | If MPU-6050 is not found then returns {0,0}. 72 | */ 73 | { 74 | const int windowSize = 16; 75 | // Read the accelerometer data 76 | struct angle_t result; 77 | result.pitchAngle = 0; 78 | result.rollAngle = 0; 79 | if (!isPresent) 80 | return result; // Gyro is not available 81 | 82 | for (int i = 0; i < windowSize; i++) 83 | { 84 | // Execute 6 byte read from MPU6050_REG_WHO_AM_I 85 | Wire.beginTransmission(MPU6050_I2C_ADDR); 86 | Wire.write(MPU6050_REG_ACCEL_XOUT_H); 87 | Wire.endTransmission(false); 88 | Wire.requestFrom(MPU6050_I2C_ADDR, 6, 1); // Read 6 registers total, each axis value is stored in 2 registers 89 | int16_t AcX = Wire.read() << 8 | Wire.read(); // X-axis value 90 | int16_t AcY = Wire.read() << 8 | Wire.read(); // Y-axis value 91 | int16_t AcZ = Wire.read() << 8 | Wire.read(); // Z-axis value 92 | 93 | // Calculating the Pitch angle (rotation around Y-axis) 94 | result.pitchAngle += ((atanf(-1 * AcX / sqrtf(powf(AcY, 2) + powf(AcZ, 2))) * 180.0f / static_cast(PI)) * 2.0f) / 2.0f; 95 | // Calculating the Roll angle (rotation around X-axis) 96 | result.rollAngle += ((atanf(-1 * AcY / sqrtf(powf(AcX, 2) + powf(AcZ, 2))) * 180.0f / static_cast(PI)) * 2.0f) / 2.0f; 97 | 98 | delay(10); // Decorrelate measurements 99 | } 100 | 101 | result.pitchAngle /= windowSize; 102 | result.rollAngle /= windowSize; 103 | #if GYRO_AXIS_SWAP == 1 104 | float temp = result.pitchAngle; 105 | result.pitchAngle = result.rollAngle; 106 | result.rollAngle = temp; 107 | #endif 108 | return result; 109 | } 110 | 111 | float Gyro::getCurrentTemperature() 112 | /* Returns MPU-6050 device temperature in degree C. 113 | If MPU-6050 is not found then returns 99 (C). 114 | */ 115 | { 116 | if (!isPresent) 117 | return 99.0f; // Gyro is not available 118 | 119 | // Execute 2 byte read from MPU6050_REG_TEMP_OUT_H 120 | Wire.beginTransmission(MPU6050_I2C_ADDR); 121 | Wire.write(MPU6050_REG_TEMP_OUT_H); 122 | Wire.endTransmission(false); 123 | Wire.requestFrom(MPU6050_I2C_ADDR, 2, 1); // Read 2 registers total, the temperature value is stored in 2 registers 124 | int16_t tempValue = Wire.read() << 8 | Wire.read(); // Raw Temperature value 125 | 126 | // Calculating the actual temperature value 127 | float result = static_cast(tempValue) / 340.0f + 36.53f; 128 | return result; 129 | } 130 | #endif 131 | -------------------------------------------------------------------------------- /src/Gyro.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if USE_GYRO_LEVEL == 1 4 | struct angle_t { 5 | float pitchAngle = 0; 6 | float rollAngle = 0; 7 | }; 8 | 9 | class Gyro 10 | { 11 | public: 12 | static void startup(); 13 | static void shutdown(); 14 | static angle_t getCurrentAngles(); 15 | static float getCurrentTemperature(); 16 | 17 | private: 18 | // MPU6050 constants 19 | enum 20 | { 21 | MPU6050_I2C_ADDR = 0x68, // I2C address of the MPU6050 accelerometer 22 | 23 | // Register addresses 24 | MPU6050_REG_CONFIG = 0x1A, 25 | MPU6050_REG_ACCEL_XOUT_H = 0x3B, 26 | MPU6050_REG_TEMP_OUT_H = 0x41, 27 | MPU6050_REG_PWR_MGMT_1 = 0x6B, 28 | MPU6050_REG_WHO_AM_I = 0x75 29 | }; 30 | 31 | static bool isPresent; // True if gyro correctly detected on startup 32 | }; 33 | #endif 34 | -------------------------------------------------------------------------------- /src/HallSensorHoming.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _HALLSENSORHOMING_HPP 2 | #define _HALLSENSORHOMING_HPP 3 | 4 | #include "Types.hpp" 5 | #include "Mount.hpp" 6 | 7 | #define HOMING_RESULT_SUCCEEDED 1 8 | #define HOMING_RESULT_HOMING_NEVER_RUN 0 9 | #define HOMING_RESULT_HOMING_IN_PROGRESS -1 10 | #define HOMING_RESULT_CANT_MOVE_OFF_SENSOR -2 11 | #define HOMING_RESULT_CANT_FIND_SENSOR_ON_REVERSE -3 12 | #define HOMING_RESULT_CANT_FIND_SENSOR_END -4 13 | 14 | class Mount; 15 | 16 | enum HomingState 17 | { 18 | HOMING_MOVE_OFF, 19 | HOMING_MOVING_OFF, 20 | HOMING_STOP_AT_TIME, 21 | HOMING_WAIT_FOR_STOP, 22 | HOMING_START_FIND_START, 23 | HOMING_FINDING_START, 24 | HOMING_FINDING_START_REVERSE, 25 | HOMING_FINDING_END, 26 | HOMING_RANGE_FOUND, 27 | HOMING_FAILED, 28 | HOMING_SUCCESSFUL, 29 | 30 | HOMING_NOT_ACTIVE 31 | }; 32 | 33 | struct HomingData { 34 | HomingState state; 35 | HomingState nextState; 36 | int pinState; 37 | int lastPinState; 38 | int pinChangeCount; 39 | int savedRate; 40 | int initialDir; 41 | long searchDistance; 42 | long position[2]; 43 | long offset; 44 | long startPos; 45 | unsigned long stopAt; 46 | }; 47 | 48 | ///////////////////////////////// 49 | // 50 | // class HallSensorHoming 51 | // 52 | ///////////////////////////////// 53 | class HallSensorHoming 54 | { 55 | private: 56 | HomingData _homingData; 57 | Mount *_pMount; 58 | StepperAxis _axis; 59 | long _stepsPerDegree; 60 | int _sensorPin; 61 | int _activeState; 62 | int _lastResult; 63 | bool _wasTracking; 64 | 65 | public: 66 | HallSensorHoming(Mount *mount, StepperAxis axis, long stepsPerDegree, int sensorPin, int activeState, int32_t offset) 67 | { 68 | _homingData.state = HomingState::HOMING_NOT_ACTIVE; 69 | _homingData.offset = offset; 70 | _homingData.pinChangeCount = 0; 71 | _pMount = mount; 72 | _axis = axis; 73 | _stepsPerDegree = stepsPerDegree; 74 | _sensorPin = sensorPin; 75 | _activeState = activeState; 76 | _lastResult = HOMING_RESULT_HOMING_NEVER_RUN; 77 | _wasTracking = mount->isSlewingTRK(); 78 | } 79 | 80 | bool findHomeByHallSensor(int initialDirection, int searchDistanceDegrees); 81 | void processHomingProgress(); 82 | String getHomingState(HomingState state) const; 83 | HomingState getHomingState() const; 84 | bool isIdleOrComplete() const; 85 | String getLastResult() const; 86 | }; 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/InfoDisplayRender.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class Mount; 5 | 6 | // Base class to implement a 7 | class InfoDisplayRender 8 | { 9 | public: 10 | InfoDisplayRender() {}; 11 | 12 | virtual void init() {}; 13 | virtual void render(Mount *mount) {}; 14 | virtual void setConsoleMode(bool active) {}; 15 | virtual int addConsoleText(String text, bool tinyFont = true); 16 | virtual void updateConsoleText(int line, String newText); 17 | }; 18 | -------------------------------------------------------------------------------- /src/InterruptAccelStepper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by andre on 12.11.22. 3 | // 4 | 5 | #ifndef AVR_INTERRUPT_STEPPER_INTERRUPTACCELSTEPPER_H 6 | #define AVR_INTERRUPT_STEPPER_INTERRUPTACCELSTEPPER_H 7 | 8 | #include 9 | #include 10 | 11 | #ifdef NEW_STEPPER_LIB 12 | 13 | #define SIGN(x) ((x >= 0) ? 1 : -1) 14 | 15 | // minimal stepping frequency (steps/s) based on cpu frequency, timer counter overflow and max amount of overflows 16 | #define MIN_STEPS_PER_SEC (static_cast(F_CPU) / (static_cast(UINT16_MAX) * static_cast(UINT8_MAX))) 17 | 18 | template class InterruptAccelStepper 19 | { 20 | private: 21 | float _max_speed; 22 | float _speed; 23 | long _target; 24 | 25 | public: 26 | InterruptAccelStepper(...) : _max_speed(0.0f), _target(0) 27 | { 28 | STEPPER::init(); 29 | } 30 | 31 | void moveTo(long absolute) 32 | { 33 | LOG(DEBUG_STEPPERS, "[IAS-%d] moveTo(%l)", STEPPER::TIMER_ID, absolute); 34 | LOG(DEBUG_STEPPERS, "[IAS-%d] relative=%l", STEPPER::TIMER_ID, absolute - STEPPER::getPosition()); 35 | 36 | _target = absolute; 37 | 38 | STEPPER::moveTo(_max_speed, _target); 39 | } 40 | 41 | void move(long relative) 42 | { 43 | LOG(DEBUG_STEPPERS, "[IAS-%d] move(%l)", STEPPER::TIMER_ID, relative); 44 | 45 | _target = STEPPER::getPosition() + relative; 46 | 47 | moveTo(_target); 48 | } 49 | 50 | void setMaxSpeed(float speed) 51 | { 52 | LOG(DEBUG_STEPPERS, "[IAS-%d] setMaxSpeed(%f)", STEPPER::TIMER_ID, speed); 53 | _max_speed = fabsf(speed); 54 | } 55 | 56 | void setAcceleration(float value) 57 | { 58 | // STUB 59 | } 60 | 61 | float maxSpeed() 62 | { 63 | return _max_speed; 64 | } 65 | 66 | void setSpeed(float speed) 67 | { 68 | LOG(DEBUG_STEPPERS, "[IAS-%d] setSpeed(%f)", STEPPER::TIMER_ID, speed); 69 | _speed = speed; 70 | 71 | if (fabsf(speed) < MIN_STEPS_PER_SEC) 72 | { 73 | LOG(DEBUG_STEPPERS, "[IAS-%d] setSpeed(%f) - speed is too low (%f). Stopping.", STEPPER::TIMER_ID, speed, MIN_STEPS_PER_SEC); 74 | STEPPER::stop(); 75 | } 76 | else 77 | { 78 | STEPPER::moveTo(_speed, INT32_MAX); 79 | } 80 | } 81 | 82 | float speed() 83 | { 84 | return _speed; 85 | } 86 | 87 | uint32_t distanceToGo() 88 | { 89 | return STEPPER::distanceToGo(); 90 | } 91 | 92 | long targetPosition() 93 | { 94 | return _target; 95 | } 96 | 97 | long currentPosition() 98 | { 99 | return STEPPER::getPosition(); 100 | } 101 | 102 | void setCurrentPosition(long position) 103 | { 104 | LOG(DEBUG_STEPPERS, "[IAS-%d] setCurrentPosition(%l)", STEPPER::TIMER_ID, position); 105 | STEPPER::setPosition(position); 106 | } 107 | 108 | void run() 109 | { 110 | // STUB 111 | } 112 | 113 | void runSpeed() 114 | { 115 | } 116 | 117 | void runToPosition() 118 | { 119 | LOG(DEBUG_STEPPERS, "[IAS-%d] runToPosition(%l)", STEPPER::TIMER_ID, _target); 120 | while (isRunning()) 121 | { 122 | yield(); 123 | } 124 | } 125 | 126 | void runToNewPosition(long position) 127 | { 128 | LOG(DEBUG_STEPPERS, "[IAS-%d] runToNewPosition(%l)", STEPPER::TIMER_ID, position); 129 | moveTo(position); 130 | runToPosition(); 131 | } 132 | 133 | void stop() 134 | { 135 | LOG(DEBUG_STEPPERS, "[IAS-%d] stop()", STEPPER::TIMER_ID); 136 | STEPPER::stop(); 137 | } 138 | 139 | void setPinsInverted(bool directionInvert = false, bool stepInvert = false, bool enableInvert = false) 140 | { 141 | STEPPER::setInverted(directionInvert); 142 | } 143 | 144 | bool isRunning() 145 | { 146 | return STEPPER::isRunning(); 147 | } 148 | }; 149 | #endif 150 | 151 | #endif //AVR_INTERRUPT_STEPPER_INTERRUPTACCELSTEPPER_H 152 | -------------------------------------------------------------------------------- /src/InterruptCallback.cpp: -------------------------------------------------------------------------------- 1 | #include "../Configuration.hpp" 2 | #include "Utility.hpp" 3 | #include "InterruptCallback.hpp" 4 | 5 | ////////////////////////////////////// 6 | // This is an hardware-independent abstraction layer over 7 | // whatever timer is used for the hardware being run 8 | ////////////////////////////////////// 9 | 10 | #ifndef NEW_STEPPER_LIB 11 | 12 | #if defined ESP32 13 | // We don't support ESP32 boards in interrupt mode 14 | #elif defined __AVR_ATmega2560__ // Arduino Mega 15 | #define USE_TIMER_1 true 16 | #define USE_TIMER_2 true 17 | #define USE_TIMER_3 false 18 | #define USE_TIMER_4 false 19 | #define USE_TIMER_5 false 20 | PUSH_NO_WARNINGS 21 | #include "libs/TimerInterrupt/TimerInterrupt.h" 22 | POP_NO_WARNINGS 23 | #else 24 | #error Unrecognized board selected. Either implement interrupt code or define the board here. 25 | #endif 26 | 27 | #if defined(ESP32) 28 | 29 | #elif defined __AVR_ATmega2560__ 30 | 31 | bool InterruptCallback::setInterval(float intervalMs, interrupt_callback_p callback, void *payload) 32 | { 33 | // We have requested to use Timer2 (see above) 34 | ITimer2.init(); 35 | 36 | // This timer supports the callback with payload 37 | return ITimer2.attachInterruptInterval(intervalMs, callback, payload, 0UL); 38 | } 39 | 40 | void InterruptCallback::stop() 41 | { 42 | ITimer2.stopTimer(); 43 | } 44 | 45 | void InterruptCallback::start() 46 | { 47 | ITimer2.restartTimer(); 48 | } 49 | 50 | #endif 51 | #endif -------------------------------------------------------------------------------- /src/InterruptCallback.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | ////////////////////////////////////// 4 | // This is an hardware-independent abstraction layer over 5 | // whatever timer is used for the hardware being run. 6 | ////////////////////////////////////// 7 | 8 | #ifndef NEW_STEPPER_LIB 9 | 10 | // The callback function signature 11 | typedef void (*interrupt_callback_p)(void *); 12 | 13 | // The static class managing the callbacks. 14 | class InterruptCallback 15 | { 16 | public: 17 | // Requests the hardware to call the given callback with the given payload at the given interval in milliseconds. 18 | // The interrupts should be started before returning. 19 | bool static setInterval(float intervalMs, interrupt_callback_p callback, void *payload); 20 | 21 | // Starts the timer interrupts (currently not called/used) 22 | void static start(); 23 | 24 | // Stops the timer interrupts (currently not called/used) 25 | void static stop(); 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/Latitude.cpp: -------------------------------------------------------------------------------- 1 | #include "../Configuration.hpp" 2 | #include "Utility.hpp" 3 | #include "Latitude.hpp" 4 | 5 | ////////////////////////////////////////////////////////////////////////////////////// 6 | // 7 | // 90 is north pole, -90 is south pole 8 | Latitude::Latitude(const Latitude &other) : DayTime(other) 9 | { 10 | } 11 | 12 | Latitude::Latitude(int h, int m, int s) : DayTime(h, m, s) 13 | { 14 | } 15 | 16 | Latitude::Latitude(float inDegrees) : DayTime(inDegrees) 17 | { 18 | } 19 | 20 | void Latitude::checkHours() 21 | { 22 | if (totalSeconds > 90L * 3600L) 23 | { 24 | LOG(DEBUG_GENERAL, "[LATITUDE]: CheckHours: Degrees is more than 90, clamping"); 25 | totalSeconds = 90L * 3600L; 26 | } 27 | if (totalSeconds < (-90L * 3600L)) 28 | { 29 | LOG(DEBUG_GENERAL, "[LATITUDE]: CheckHours: Degrees is less than -90, clamping"); 30 | totalSeconds = -90L * 3600L; 31 | } 32 | } 33 | 34 | Latitude Latitude::ParseFromMeade(String const &s) 35 | { 36 | Latitude result(0.0); 37 | 38 | LOG(DEBUG_MEADE, "[LATITUDE]: Latitude.Parse(%s)", s.c_str()); 39 | // Use the DayTime code to parse it. 40 | DayTime dt = DayTime::ParseFromMeade(s); 41 | result.totalSeconds = dt.getTotalSeconds(); 42 | result.checkHours(); 43 | LOG(DEBUG_MEADE, "[LATITUDE]: Latitude.Parse(%s) -> %s", s.c_str(), result.ToString()); 44 | return result; 45 | } 46 | -------------------------------------------------------------------------------- /src/Latitude.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DayTime.hpp" 4 | 5 | // 90 at north pole, -90 at south pole 6 | class Latitude : public DayTime 7 | { 8 | public: 9 | Latitude() : DayTime() 10 | { 11 | } 12 | Latitude(const Latitude &other); 13 | Latitude(int h, int m, int s); 14 | Latitude(float inDegrees); 15 | 16 | static Latitude ParseFromMeade(String const &s); 17 | 18 | protected: 19 | virtual void checkHours() override; 20 | }; 21 | -------------------------------------------------------------------------------- /src/LcdButtons.cpp: -------------------------------------------------------------------------------- 1 | #include "inc/Globals.hpp" 2 | #include "../Configuration.hpp" 3 | #include "LcdMenu.hpp" 4 | #include "LcdButtons.hpp" 5 | 6 | #if DISPLAY_TYPE != DISPLAY_TYPE_NONE 7 | 8 | LcdButtons::LcdButtons(byte pin, LcdMenu *lcdMenu) 9 | { 10 | _lcdMenu = lcdMenu; 11 | _analogPin = pin; 12 | _lastKeyChange = 0; 13 | 14 | _newKey = btnNONE; 15 | _lastNewKey = btnNONE; 16 | 17 | _currentKey = btnNONE; 18 | _lastKey = btnNONE; 19 | 20 | #if DISPLAY_TYPE == DISPLAY_TYPE_LCD_JOY_I2C_SSD1306 21 | // Initialize keypad 22 | pinMode(LCD_KEY_SENSE_X_PIN, INPUT); 23 | pinMode(LCD_KEY_SENSE_Y_PIN, INPUT); 24 | pinMode(LCD_KEY_SENSE_PUSH_PIN, INPUT); 25 | #endif 26 | } 27 | 28 | LcdButtons::LcdButtons(LcdMenu *lcdMenu) 29 | { 30 | _lcdMenu = lcdMenu; 31 | _lastKeyChange = 0; 32 | 33 | _newKey = btnNONE; 34 | _lastNewKey = btnINVALID; 35 | 36 | _currentKey = btnNONE; 37 | _lastKey = btnINVALID; 38 | } 39 | 40 | bool LcdButtons::keyChanged(lcdButton_t *pNewKey) 41 | { 42 | checkKey(); 43 | if (_newKey != _lastNewKey) 44 | { 45 | *pNewKey = _newKey; 46 | _lastNewKey = _newKey; 47 | return true; 48 | } 49 | return false; 50 | } 51 | 52 | int LcdButtons::currentAnalogState() 53 | { 54 | #if DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23008 || DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23017 55 | return 0; // No analog value for these displays 56 | #elif DISPLAY_TYPE == DISPLAY_TYPE_LCD_JOY_I2C_SSD1306 57 | return analogRead(LCD_KEY_SENSE_Y_PIN); 58 | #else 59 | return analogRead(_analogPin); 60 | #endif 61 | } 62 | 63 | void LcdButtons::checkKey() 64 | { 65 | #if DISPLAY_TYPE > 0 66 | #if DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23008 || DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23017 67 | uint8_t buttons = _lcdMenu->readButtons(); 68 | _currentKey = btnNONE; 69 | if (buttons) 70 | { 71 | if (buttons & BUTTON_UP) 72 | _currentKey = btnUP; 73 | if (buttons & BUTTON_DOWN) 74 | _currentKey = btnDOWN; 75 | if (buttons & BUTTON_LEFT) 76 | _currentKey = btnLEFT; 77 | if (buttons & BUTTON_RIGHT) 78 | _currentKey = btnRIGHT; 79 | if (buttons & BUTTON_SELECT) 80 | _currentKey = btnSELECT; 81 | } 82 | #elif DISPLAY_TYPE == DISPLAY_TYPE_LCD_JOY_I2C_SSD1306 83 | uint16_t x(analogRead(LCD_KEY_SENSE_X_PIN)); 84 | uint16_t y(analogRead(LCD_KEY_SENSE_Y_PIN)); 85 | uint16_t push(analogRead(LCD_KEY_SENSE_PUSH_PIN)); 86 | 87 | // Assumes analogReadResolution(12) (the default) 88 | int16_t const MIDSCALE = 4096 / 2; 89 | int16_t const DEADBAND = 500; 90 | 91 | _currentKey = btnNONE; 92 | if (x > (MIDSCALE + DEADBAND)) 93 | _currentKey = btnRIGHT; 94 | if (x < (MIDSCALE - DEADBAND)) 95 | _currentKey = btnLEFT; 96 | if (y > (MIDSCALE + DEADBAND)) 97 | _currentKey = btnDOWN; // Y appears reversed 98 | if (y < (MIDSCALE - DEADBAND)) 99 | _currentKey = btnUP; 100 | if (push < MIDSCALE) 101 | _currentKey = btnSELECT; // Active low 102 | #else 103 | const int analogKeyValue = currentAnalogState(); 104 | if (analogKeyValue > 1000) 105 | _currentKey = btnNONE; 106 | else if (analogKeyValue < 50) 107 | _currentKey = btnRIGHT; 108 | else if (analogKeyValue < 240) 109 | _currentKey = btnUP; 110 | else if (analogKeyValue < 400) 111 | _currentKey = btnDOWN; 112 | else if (analogKeyValue < 600) 113 | _currentKey = btnLEFT; 114 | else if (analogKeyValue < 920) 115 | _currentKey = btnSELECT; 116 | #endif 117 | 118 | if (_currentKey != _lastKey) 119 | { 120 | _lastKey = _currentKey; 121 | _lastKeyChange = millis(); 122 | } 123 | else 124 | { 125 | // If the keys haven't changed in 5ms, commit the change to the new keys. 126 | if (millis() - _lastKeyChange > 5) 127 | { 128 | _newKey = _currentKey; 129 | } 130 | } 131 | #endif 132 | } 133 | 134 | #else 135 | 136 | // Null implementation 137 | LcdButtons::LcdButtons(byte pin, LcdMenu *lcdMenu) 138 | : _lastKeyChange(0), _analogPin(pin), _lastKey(btnNONE), _newKey(btnNONE), _lastNewKey(btnNONE), _currentKey(btnNONE), _lcdMenu(lcdMenu) 139 | { 140 | } 141 | 142 | LcdButtons::LcdButtons(LcdMenu *lcdMenu) 143 | : _lastKeyChange(0), _analogPin(0), _lastKey(btnNONE), _newKey(btnNONE), _lastNewKey(btnNONE), _currentKey(btnNONE), _lcdMenu(lcdMenu) 144 | { 145 | } 146 | 147 | bool LcdButtons::keyChanged(lcdButton_t *pNewKey) 148 | { 149 | return false; 150 | } 151 | 152 | void LcdButtons::checkKey() 153 | { 154 | } 155 | 156 | #endif -------------------------------------------------------------------------------- /src/LcdButtons.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LCDBUTTONS_HPP_ 2 | #define LCDBUTTONS_HPP_ 3 | 4 | #include "inc/Globals.hpp" 5 | 6 | // LCD shield buttons 7 | enum lcdButton_t 8 | { 9 | btnRIGHT, 10 | btnUP, 11 | btnDOWN, 12 | btnLEFT, 13 | btnSELECT, 14 | btnNONE, 15 | btnINVALID, 16 | }; 17 | 18 | // Forward declaration 19 | class LcdMenu; 20 | 21 | class LcdButtons 22 | { 23 | public: 24 | LcdButtons(byte pin, LcdMenu *lcdMenu); 25 | LcdButtons(LcdMenu *lcdMenu); 26 | 27 | lcdButton_t currentKey() 28 | { 29 | checkKey(); 30 | return _newKey; 31 | } 32 | 33 | lcdButton_t currentState() 34 | { 35 | checkKey(); 36 | return _currentKey; 37 | } 38 | 39 | int currentAnalogState(); 40 | 41 | bool keyChanged(lcdButton_t *pNewKey); 42 | 43 | private: 44 | void checkKey(); 45 | 46 | private: 47 | unsigned long _lastKeyChange; 48 | byte _analogPin; 49 | lcdButton_t _lastKey; 50 | lcdButton_t _newKey; 51 | lcdButton_t _lastNewKey; 52 | lcdButton_t _currentKey; 53 | LcdMenu *_lcdMenu; 54 | }; 55 | 56 | #endif // LCDBUTTONS_HPP_ -------------------------------------------------------------------------------- /src/LcdMenu.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _LCDMENU_HPP_ 2 | #define _LCDMENU_HPP_ 3 | 4 | #if DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD 5 | #include 6 | #elif DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23008 || DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23017 7 | #include 8 | #elif DISPLAY_TYPE == DISPLAY_TYPE_LCD_JOY_I2C_SSD1306 9 | #include // https://github.com/olikraus/u8g2 10 | #endif 11 | 12 | // A single menu item (like RA, HEAT, POL, etc.) 13 | // The ID is just a number, it has no relevance for the order of the items 14 | // The display is what is shown on the menu. 15 | class MenuItem 16 | { 17 | const char *_display; // What to display on the screen 18 | byte _id; // The ID of the menu item 19 | public: 20 | MenuItem(const char *display, byte id) 21 | { 22 | _display = display; 23 | _id = id; 24 | } 25 | 26 | const char *display() 27 | { 28 | return _display; 29 | } 30 | 31 | byte id() 32 | { 33 | return _id; 34 | } 35 | }; 36 | 37 | // Class that drives the LCD screen with a menu 38 | // You add a string and an id item and this class handles the display and navigation 39 | class LcdMenu 40 | { 41 | public: 42 | // Create a new menu, using the given number of LCD display columns and rows 43 | LcdMenu(byte cols, byte rows, int maxItems); 44 | 45 | void startup(); 46 | 47 | #if DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD && defined(LCD_BRIGHTNESS_PIN) 48 | // Function to test LCD hardware, some units are shipped with defects 49 | static bool testIfLcdIsBad(); 50 | #endif 51 | 52 | // Find a menu item by its ID 53 | MenuItem *findById(byte id); 54 | 55 | // Add a new menu item to the list (order matters) 56 | void addItem(const char *disp, byte id); 57 | 58 | // Get the currently active item ID 59 | byte getActive(); 60 | 61 | // Set the active menu item 62 | void setActive(byte id); 63 | 64 | // Pass thru utility function 65 | void setCursor(byte col, byte row); 66 | 67 | // Set and get the brightness of the backlight 68 | void setBacklightBrightness(int level, bool persist = true); 69 | int getBacklightBrightness() const; 70 | void getBacklightBrightnessRange(int *minPtr, int *maxPtr) const; 71 | 72 | // Pass thru utility function 73 | void clear(); 74 | 75 | // Go to the next menu item from currently active one 76 | void setNextActive(); 77 | 78 | // Update the display of the LCD with the current menu settings 79 | // This iterates over the menu items, building a menu string by concatenating their display string. 80 | // It also places the selector arrows around the active one. 81 | // It then sends the string to the LCD, keeping the selector arrows centered in the same place. 82 | void updateDisplay(); 83 | 84 | // Print a string to the LCD at the current cursor position, substituting the special arrows and padding with spaces to the end 85 | void printMenu(String line); 86 | 87 | // Print a character at a specific position 88 | void printAt(int col, int row, char ch); 89 | 90 | #if DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23008 || DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23017 91 | uint8_t readButtons(); 92 | #endif 93 | 94 | private: 95 | // Print a single character at the current cursor location and advance cursor by one. Substitutes special chars. 96 | void printChar(char ch); 97 | 98 | private: 99 | #if DISPLAY_TYPE > 0 100 | 101 | #if DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD 102 | LiquidCrystal _lcd; // The LCD screen that we'll display the menu on 103 | #elif DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23008 || DISPLAY_TYPE == DISPLAY_TYPE_LCD_KEYPAD_I2C_MCP23017 104 | LiquidTWI2 _lcd; // The LCD screen that we'll display the menu on 105 | #elif DISPLAY_TYPE == DISPLAY_TYPE_LCD_JOY_I2C_SSD1306 106 | U8X8_SSD1306_128X32_UNIVISION_HW_I2C _lcd; 107 | #endif 108 | 109 | byte const _cols; 110 | byte const _rows; 111 | byte const _maxItems; 112 | byte const _charHeightRows; // Height of character in display native rows 113 | bool _lcdBadHw; 114 | 115 | MenuItem **_menuItems; // The first menu item (linked list) 116 | byte _numMenuItems; 117 | byte _activeMenuIndex; 118 | byte _longestDisplay; // The number of characters in the longest menu item 119 | byte _columns; // The number of columns in the LCD display 120 | byte _activeRow; // The row that the LCD cursor is on 121 | byte _activeCol; // The column that the LCD cursor is on 122 | String _lastDisplay[2]; // The last string that was displayed on each row 123 | byte _brightness; 124 | 125 | #if DISPLAY_TYPE != DISPLAY_TYPE_LCD_JOY_I2C_SSD1306 126 | enum specialChar_t : byte 127 | { 128 | _degrees, 129 | _minutes, 130 | _leftArrow, 131 | _rightArrow, 132 | _upArrow, 133 | _downArrow, 134 | _tracking, 135 | _noTracking, 136 | SPECIAL_CHAR_MAX, 137 | }; 138 | static_assert(SPECIAL_CHAR_MAX <= 8, "LCD only supports a maximum of 8 special characters"); 139 | 140 | // The special character bitmaps 141 | static byte RightArrowBitmap[8]; 142 | static byte LeftArrowBitmap[8]; 143 | static byte UpArrowBitmap[8]; 144 | static byte DownArrowBitmap[8]; 145 | static byte DegreesBitmap[8]; 146 | static byte MinutesBitmap[8]; 147 | static byte TrackingBitmap[8]; 148 | static byte NoTrackingBitmap[8]; 149 | #endif 150 | 151 | #endif 152 | }; 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /src/Longitude.cpp: -------------------------------------------------------------------------------- 1 | #include "../Configuration.hpp" 2 | #include "Utility.hpp" 3 | #include "Longitude.hpp" 4 | 5 | ////////////////////////////////////////////////////////////////////////////////////// 6 | // 7 | // -180..180 range, 0 is at the prime meridian (through Greenwich), negative going west, positive going east 8 | 9 | Longitude::Longitude(const Longitude &other) : DayTime(other) 10 | { 11 | } 12 | 13 | Longitude::Longitude(int h, int m, int s) : DayTime(h, m, s) 14 | { 15 | } 16 | 17 | Longitude::Longitude(float inDegrees) : DayTime(inDegrees) 18 | { 19 | } 20 | 21 | void Longitude::checkHours() 22 | { 23 | while (totalSeconds > 180L * 3600L) 24 | { 25 | LOG(DEBUG_GENERAL, "[LONGITUDE]: CheckHours: Degrees is more than 180, wrapping"); 26 | totalSeconds -= 360L * 3600L; 27 | } 28 | while (totalSeconds < (-180L * 3600L)) 29 | { 30 | LOG(DEBUG_GENERAL, "[LONGITUDE]: CheckHours: Degrees is less than -180, wrapping"); 31 | totalSeconds += 360L * 3600L; 32 | } 33 | } 34 | 35 | Longitude Longitude::ParseFromMeade(String const &s) 36 | { 37 | Longitude result(0.0); 38 | LOG(DEBUG_MEADE, "[LONGITUDE]: Parse(%s)", s.c_str()); 39 | 40 | // Use the DayTime code to parse it. 41 | DayTime dt = DayTime::ParseFromMeade(s); 42 | 43 | if ((s[0] == '-') || (s[0] == '+')) 44 | { 45 | // According to spec 2010.10, if a sign is present it is straight up longitude with east as negative. 46 | result.totalSeconds = -dt.getTotalSeconds(); 47 | } 48 | else 49 | { 50 | //from indilib driver: Meade defines longitude as 0 to 360 WESTWARD (https://github.com/indilib/indi/blob/1b2f462b9c9b0f75629b635d77dc626b9d4b74a3/drivers/telescope/lx200driver.cpp#L1019) 51 | result.totalSeconds = 180L * 3600L - dt.getTotalSeconds(); 52 | } 53 | result.checkHours(); 54 | 55 | LOG(DEBUG_MEADE, "[LONGITUDE]: Parse(%s) -> %s = %ls", s.c_str(), result.ToString(), result.getTotalSeconds()); 56 | return result; 57 | } 58 | 59 | char achBufLong[32]; 60 | 61 | const char *Longitude::ToString() const 62 | { 63 | long secs = totalSeconds; 64 | if (secs < 0) 65 | { 66 | secs += 360L * 3600L; 67 | } 68 | 69 | String totalDegs = String(1.0f * labs(totalSeconds) / 3600.0f, 2); 70 | String degs = String(1.0f * secs / 3600.0f, 2); 71 | strcpy(achBufLong, degs.c_str()); 72 | strcat(achBufLong, " ("); 73 | strcat(achBufLong, totalDegs.c_str()); 74 | strcat(achBufLong, (totalSeconds < 0) ? "W)" : "E)"); 75 | return achBufLong; 76 | } 77 | 78 | const char *Longitude::formatString(char *targetBuffer, const char *format, long *) const 79 | { 80 | long secs = labs(totalSeconds); 81 | 82 | long degs = secs / 3600; 83 | secs = secs - degs * 3600; 84 | long mins = secs / 60; 85 | secs = secs - mins * 60; 86 | if (totalSeconds < 0) 87 | { 88 | degs = -degs; 89 | } 90 | 91 | return formatStringImpl(targetBuffer, format, '\0', degs, mins, secs); 92 | } 93 | 94 | const char *Longitude::formatStringForMeade(char *targetBuffer) const 95 | { 96 | LOG(DEBUG_MEADE, "[LONGITUDE] Format %l for Meade", totalSeconds); 97 | long secs = labs(totalSeconds); 98 | 99 | long degs = secs / 3600; 100 | secs = secs - degs * 3600; 101 | long mins = secs / 60; 102 | secs = secs - mins * 60; 103 | LOG(DEBUG_MEADE, "[LONGITUDE] Degs is %l, Mins is %l", degs, mins); 104 | 105 | // Since internal storage is actual longitude, Meade is negated 106 | if (totalSeconds > 0) 107 | { 108 | // Since we already inverted it when it was negative (by using ABS a few 109 | // lines above here), we only invert it if it is positive. 110 | degs = -degs; 111 | } 112 | 113 | LOG(DEBUG_MEADE, "[LONGITUDE] Inverted Degs, now %l, Mins is %l", degs, mins); 114 | 115 | return formatStringImpl(targetBuffer, "{+}{d}*{m}", '\0', degs, mins, secs); 116 | } 117 | -------------------------------------------------------------------------------- /src/Longitude.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DayTime.hpp" 4 | 5 | // -180..180 range, 0 is at the prime meridian (through Greenwich), negative going west, positive going east 6 | class Longitude : public DayTime 7 | { 8 | public: 9 | Longitude() : DayTime() 10 | { 11 | } 12 | Longitude(const Longitude &other); 13 | Longitude(int h, int m, int s); 14 | Longitude(float inDegrees); 15 | 16 | virtual const char *formatString(char *targetBuffer, const char *format, long *pSeconds = nullptr) const; 17 | const char *formatStringForMeade(char *targetBuffer) const; 18 | virtual const char *ToString() const; 19 | 20 | static Longitude ParseFromMeade(String const &s); 21 | 22 | protected: 23 | virtual void checkHours() override; 24 | }; 25 | -------------------------------------------------------------------------------- /src/MeadeCommandProcessor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Forward declarations 4 | class Mount; 5 | class LcdMenu; 6 | 7 | class MeadeCommandProcessor 8 | { 9 | public: 10 | static MeadeCommandProcessor *createProcessor(Mount *mount, LcdMenu *lcdMenu); 11 | static MeadeCommandProcessor *instance(); 12 | String processCommand(String incmd); 13 | 14 | private: 15 | MeadeCommandProcessor(Mount *mount, LcdMenu *lcdMenu); 16 | String handleMeadeSetInfo(String inCmd); 17 | String handleMeadeMovement(String inCmd); 18 | String handleMeadeGetInfo(String inCmd); 19 | String handleMeadeGPSCommands(String inCmd); 20 | String handleMeadeSyncControl(String inCmd); 21 | String handleMeadeHome(String inCmd); 22 | String handleMeadeInit(String inCmd); 23 | String handleMeadeQuit(String inCmd); 24 | String handleMeadeDistance(String inCmd); 25 | String handleMeadeSetSlewRate(String inCmd); 26 | String handleMeadeExtraCommands(String inCmd); 27 | String handleMeadeFocusCommands(String inCmd); 28 | Mount *_mount; 29 | LcdMenu *_lcdMenu; 30 | static MeadeCommandProcessor *_instance; 31 | }; 32 | -------------------------------------------------------------------------------- /src/Sidereal.cpp: -------------------------------------------------------------------------------- 1 | #include "inc/Globals.hpp" 2 | #include "../Configuration.hpp" 3 | #include "Sidereal.hpp" 4 | 5 | // Constants for sidereal calculation 6 | // Source: http://www.stargazing.net/kepler/altaz.html 7 | const double C1 = 100.46; 8 | const double C2 = 0.985647; 9 | const double C3 = 15.0; 10 | const double C4 = -0.5125; 11 | const unsigned J2000 = 2000; 12 | 13 | #if USE_GPS == 1 14 | PUSH_NO_WARNINGS 15 | #include 16 | POP_NO_WARNINGS 17 | DayTime Sidereal::calculateByGPS(TinyGPSPlus *gps) 18 | { 19 | DayTime timeUTC = DayTime(gps->time.hour(), gps->time.minute(), gps->time.second()); 20 | int deltaJd = calculateDeltaJd(gps->date.year(), gps->date.month(), gps->date.day()); 21 | double deltaJ = static_cast(deltaJd) + (timeUTC.getTotalHours() / 24.0f); 22 | return DayTime(static_cast(calculateTheta(deltaJ, gps->location.lng(), timeUTC.getTotalHours()) / 15.0)); 23 | } 24 | #endif // USE_GPS 25 | 26 | DayTime Sidereal::calculateByDateAndTime(double longitude, int year, int month, int day, DayTime *timeUTC) 27 | { 28 | int deltaJd = calculateDeltaJd(year, month, day); 29 | double deltaJ = deltaJd + ((timeUTC->getTotalHours()) / 24.0f); 30 | return DayTime(static_cast(calculateTheta(deltaJ, longitude, timeUTC->getTotalHours()) / 15.0)); 31 | } 32 | 33 | double Sidereal::calculateTheta(double deltaJ, double longitude, float timeUTC) 34 | { 35 | double theta = C1; 36 | theta += C2 * deltaJ; 37 | theta += C3 * static_cast(timeUTC); 38 | theta += C4; 39 | theta += longitude; 40 | return fmod(theta, 360.0); 41 | } 42 | 43 | int Sidereal::calculateDeltaJd(int year, int month, int day) 44 | { 45 | const int daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 46 | 47 | // Calculating days without leapdays 48 | long int deltaJd = (year - J2000) * 365 + day; 49 | for (int i = 0; i < month - 1; i++) 50 | { 51 | deltaJd += daysInMonth[i]; 52 | } 53 | 54 | // Add leapdays 55 | if (month <= 2) 56 | { 57 | // Not counting current year if we have not passed february yet 58 | year--; 59 | } 60 | deltaJd += year / 4 - year / 100 + year / 400; 61 | deltaJd -= J2000 / 4 - J2000 / 100 + J2000 / 400; 62 | return deltaJd; 63 | } 64 | 65 | DayTime Sidereal::calculateHa(float lstTotalHours) 66 | { 67 | float lstDeg = lstTotalHours * 15; //to deg 68 | 69 | //subtract Poloars RA 70 | lstDeg -= ((POLARIS_RA_SECOND / 3600.0f + POLARIS_RA_MINUTE / 60.0f + POLARIS_RA_HOUR) * 15.0f); 71 | 72 | //ensure positive deg 73 | while (lstDeg < 0.0f) 74 | { 75 | lstDeg += 360.0f; 76 | } 77 | 78 | //update HA 79 | return DayTime(lstDeg / 15.0f); 80 | } -------------------------------------------------------------------------------- /src/Sidereal.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "DayTime.hpp" 5 | 6 | // Forward declaration 7 | class TinyGPSPlus; 8 | 9 | class Sidereal 10 | { 11 | public: 12 | static DayTime calculateByGPS(TinyGPSPlus *gps); 13 | 14 | static DayTime calculateByDateAndTime(double longitude, int year, int month, int day, DayTime *timeUTC); 15 | static DayTime calculateHa(float lstTotalHours); 16 | 17 | private: 18 | static double calculateTheta(double deltaJ, double longitude, float timeUTC); 19 | static int calculateDeltaJd(int year, int month, int day); 20 | }; 21 | -------------------------------------------------------------------------------- /src/StepperConfiguration.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../Configuration.hpp" 4 | 5 | #ifdef NEW_STEPPER_LIB 6 | 7 | #ifdef ARDUINO_AVR_ATmega2560 8 | #include "Pin.h" 9 | #include "IntervalInterrupt.h" 10 | #include "Driver.h" 11 | 12 | PUSH_NO_WARNINGS 13 | #include "Stepper.h" 14 | #include "InterruptAccelStepper.h" 15 | POP_NO_WARNINGS 16 | #endif 17 | 18 | #define UINT32(x) static_cast(x) 19 | 20 | namespace config 21 | { 22 | struct Ra { 23 | constexpr static uint32_t DRIVER_SPR_SLEW = static_cast(RA_STEPPER_SPR) * static_cast(RA_SLEW_MICROSTEPPING); 24 | constexpr static uint32_t DRIVER_SPR_TRK = static_cast(RA_STEPPER_SPR) * static_cast(RA_TRACKING_MICROSTEPPING); 25 | 26 | constexpr static float SPR_SLEW = DRIVER_SPR_SLEW * RA_TRANSMISSION; 27 | constexpr static float SPR_TRK = DRIVER_SPR_TRK * RA_TRANSMISSION; 28 | 29 | constexpr static float SPEED_TRK = SPR_TRK / SIDEREAL_SECONDS_PER_DAY; 30 | constexpr static float SPEED_SLEW = SPR_SLEW / 360.0f * RA_SLEWING_SPEED_DEG; 31 | constexpr static float ACCEL_SLEW = SPR_SLEW / 360.0f * RA_SLEWING_ACCELERATION_DEG; 32 | 33 | #ifdef ARDUINO_AVR_ATmega2560 34 | using pin_step = Pin; 35 | using pin_dir = Pin; 36 | 37 | using interrupt = IntervalInterrupt; 38 | using driver = Driver; 39 | 40 | using ramp_slew = AccelerationRamp<256, interrupt::FREQ, UINT32(SPEED_SLEW), UINT32(ACCEL_SLEW)>; 41 | using ramp_trk = ConstantRamp; 42 | 43 | using stepper_slew = Stepper; 44 | using stepper_trk = Stepper; 45 | 46 | constexpr static float SPEED_COMPENSATION = SPEED_SLEW; 47 | #else 48 | constexpr static float SPEED_COMPENSATION = SPEED_SLEW; 49 | #endif 50 | }; 51 | 52 | struct Dec { 53 | constexpr static uint32_t DRIVER_SPR_SLEW = static_cast(DEC_STEPPER_SPR) * static_cast(DEC_SLEW_MICROSTEPPING); 54 | constexpr static uint32_t DRIVER_SPR_TRK = static_cast(DEC_STEPPER_SPR) * static_cast(DEC_GUIDE_MICROSTEPPING); 55 | 56 | constexpr static float SPR_SLEW = DRIVER_SPR_SLEW * DEC_TRANSMISSION; 57 | constexpr static float SPR_TRK = DRIVER_SPR_TRK * DEC_TRANSMISSION; 58 | 59 | constexpr static float SPEED_TRK = 0; 60 | constexpr static float SPEED_SIDEREAL = SPR_TRK / SIDEREAL_SECONDS_PER_DAY; 61 | constexpr static float SPEED_SLEW = SPR_SLEW / 360.0f * DEC_SLEWING_SPEED_DEG; 62 | constexpr static float ACCEL_SLEW = SPR_SLEW / 360.0f * DEC_SLEWING_ACCELERATION_DEG; 63 | 64 | #ifdef ARDUINO_AVR_ATmega2560 65 | using pin_step = Pin; 66 | using pin_dir = Pin; 67 | 68 | using interrupt = IntervalInterrupt; 69 | using driver = Driver; 70 | 71 | using ramp_slew = AccelerationRamp<256, interrupt::FREQ, UINT32(SPEED_SLEW), UINT32(ACCEL_SLEW)>; 72 | using ramp_trk = ConstantRamp; 73 | 74 | using stepper_slew = Stepper; 75 | using stepper_trk = Stepper; 76 | #endif 77 | }; 78 | 79 | #if AZ_STEPPER_TYPE != STEPPER_TYPE_NONE 80 | struct Az { 81 | constexpr static float SPEED_SLEW = static_cast(AZ_STEPPER_SPEED); 82 | constexpr static float ACCEL_SLEW = static_cast(AZ_STEPPER_ACCELERATION); 83 | 84 | #ifdef ARDUINO_AVR_ATmega2560 85 | using pin_step = Pin; 86 | using pin_dir = Pin; 87 | 88 | using interrupt = IntervalInterrupt; 89 | using driver = Driver; 90 | 91 | using ramp_slew = AccelerationRamp<64, interrupt::FREQ, UINT32(SPEED_SLEW), UINT32(ACCEL_SLEW)>; 92 | 93 | using stepper_slew = Stepper; 94 | #endif 95 | }; 96 | #endif 97 | 98 | #if ALT_STEPPER_TYPE != STEPPER_TYPE_NONE 99 | struct Alt { 100 | constexpr static float SPEED_SLEW = static_cast(ALT_STEPPER_SPEED); 101 | constexpr static float ACCEL_SLEW = static_cast(ALT_STEPPER_ACCELERATION); 102 | 103 | #ifdef ARDUINO_AVR_ATmega2560 104 | using pin_step = Pin; 105 | using pin_dir = Pin; 106 | 107 | using interrupt = IntervalInterrupt; 108 | using driver = Driver; 109 | 110 | using ramp_slew = AccelerationRamp<64, interrupt::FREQ, UINT32(SPEED_SLEW), UINT32(ACCEL_SLEW)>; 111 | 112 | using stepper_slew = Stepper; 113 | #endif 114 | }; 115 | #endif 116 | 117 | #if FOCUS_STEPPER_TYPE != STEPPER_TYPE_NONE 118 | struct Focus { 119 | constexpr static float SPEED_SLEW = static_cast(FOCUS_STEPPER_SPEED); 120 | constexpr static float ACCEL_SLEW = static_cast(FOCUS_STEPPER_ACCELERATION); 121 | 122 | #ifdef ARDUINO_AVR_ATmega2560 123 | using pin_step = Pin; 124 | using pin_dir = Pin; 125 | 126 | using interrupt = IntervalInterrupt; 127 | using driver = Driver; 128 | 129 | using ramp_slew = AccelerationRamp<64, interrupt::FREQ, UINT32(SPEED_SLEW), UINT32(ACCEL_SLEW)>; 130 | 131 | using stepper_slew = Stepper; 132 | #endif 133 | }; 134 | #endif 135 | 136 | } // namespace config 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /src/Types.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _TYPES_HPP_ 2 | #define _TYPES_HPP_ 3 | 4 | enum StepperAxis 5 | { 6 | RA_STEPS, 7 | DEC_STEPS, 8 | RA_AND_DEC_STEPS, 9 | AZIMUTH_STEPS, 10 | ALTITUDE_STEPS, 11 | FOCUS_STEPS 12 | }; 13 | 14 | #endif -------------------------------------------------------------------------------- /src/Utility.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTILITY_HPP_ 2 | #define UTILITY_HPP_ 3 | 4 | #include "inc/Globals.hpp" 5 | 6 | #ifndef DEBUG_LEVEL 7 | #error Configuration.hpp must be included before Utility.hpp for correct debug configuration 8 | #endif 9 | 10 | String getLogBuffer(); 11 | int freeMemory(); 12 | 13 | #if DEBUG_LEVEL > 0 14 | #define LOG(level, format, ...) logv((level), (F(format)), ##__VA_ARGS__) 15 | 16 | // Realtime timer class using microseconds to time stuff 17 | class RealTime 18 | { 19 | static unsigned long _pausedTime; 20 | static unsigned long _suspendStart; 21 | static int _suspended; 22 | 23 | public: 24 | static void suspend() 25 | { 26 | if (_suspended == 0) 27 | { 28 | _suspendStart = micros(); 29 | } 30 | _suspended++; 31 | } 32 | 33 | static void resume() 34 | { 35 | _suspended--; 36 | if (_suspended == 0) 37 | { 38 | unsigned long now = micros(); 39 | _pausedTime += now - _suspendStart; 40 | } 41 | } 42 | 43 | static unsigned long currentTime() 44 | { 45 | if (_suspended != 0) 46 | { 47 | unsigned long now = micros(); 48 | unsigned long pausedUntilNow = now - _suspendStart; 49 | return now - pausedUntilNow; 50 | } 51 | else 52 | { 53 | return micros() - _pausedTime; 54 | } 55 | } 56 | }; 57 | 58 | // Performance measurement class 59 | class PerfMeasure 60 | { 61 | unsigned long _start; 62 | unsigned long _end; 63 | unsigned long _duration; 64 | int _indent; 65 | String _name; 66 | bool _running; 67 | bool _printed; 68 | 69 | public: 70 | PerfMeasure(int indent, String name) 71 | { 72 | _name = name; 73 | _running = true; 74 | _printed = false; 75 | _indent = indent; 76 | _start = RealTime::currentTime(); 77 | } 78 | 79 | ~PerfMeasure() 80 | { 81 | if (_running) 82 | stop(); 83 | print(); 84 | } 85 | 86 | void stop() 87 | { 88 | _end = RealTime::currentTime(); 89 | _duration = _end - _start; 90 | _running = false; 91 | } 92 | 93 | float durationMs() 94 | { 95 | return 0.001 * _duration; 96 | } 97 | 98 | void print() 99 | { 100 | if (_running) 101 | stop(); 102 | RealTime::suspend(); 103 | if (!_printed) 104 | { 105 | char buf[128]; 106 | memset(buf, ' ', 127); 107 | buf[127] = 0; 108 | 109 | String disp = String(durationMs(), 3); 110 | char *p = buf + (_indent * 1); 111 | memcpy(p, _name.c_str(), _name.length()); 112 | p = buf + 36 - disp.length(); 113 | memcpy(p, disp.c_str(), disp.length()); 114 | p = buf + 36; 115 | *p++ = 'm'; 116 | *p++ = 's'; 117 | *p++ = 0; 118 | Serial.println(String(buf)); 119 | _printed = true; 120 | } 121 | RealTime::resume(); 122 | } 123 | }; 124 | 125 | String formatArg(const char *input, va_list args); 126 | String format(const char *input, ...); 127 | // void log(const char* input); 128 | // void log(String input); 129 | void logv(int levelFlags, String input, ...); 130 | 131 | #else // DEBUG_LEVEL>0 132 | #define LOG(level, format, ...) 133 | #endif // DEBUG_LEVEL>0 134 | 135 | // For some reason arduino just defines all float functions to be as double 136 | #if defined(fabsf) 137 | #undef fabsf 138 | #endif 139 | #if defined(roundf) 140 | #undef roundf 141 | #endif 142 | #if defined(atanf) 143 | #undef atanf 144 | #endif 145 | float fabsf(float); 146 | float roundf(float); 147 | float atanf(float); 148 | 149 | // Adjust the given number by the given adjustment, wrap around the limits. 150 | // Limits are inclusive, so they represent the lowest and highest valid number. 151 | int adjustWrap(int current, int adjustBy, int minVal, int maxVal); 152 | 153 | // Adjust the given number by the given adjustment, clamping to the limits. 154 | // Limits are inclusive, so they represent the lowest and highest valid number. 155 | int adjustClamp(int current, int adjustBy, int minVal, int maxVal); 156 | 157 | // Clamp the given number to the limits. 158 | // Limits are inclusive, so they represent the lowest and highest valid number. 159 | long clamp(long current, long minVal, long maxVal); 160 | 161 | // Clamp the given number to the limits. 162 | // Limits are inclusive, so they represent the lowest and highest valid number. 163 | int clamp(int current, int minVal, int maxVal); 164 | 165 | // Clamp the given number to the limits. 166 | // Limits are inclusive, so they represent the lowest and highest valid number. 167 | float clamp(float current, float minVal, float maxVal); 168 | 169 | // Return -1 if the given number is less than zero, 1 if not. 170 | int sign(long num); 171 | 172 | // Return -1 if the given number is less than zero, 1 if not. 173 | int fsign(float num); 174 | 175 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 176 | #define CASERETURN(c, r) \ 177 | case c: \ 178 | return r 179 | 180 | // Return an array of String* with a nullptr sentinel 181 | String *splitStringBy(String str, char splitChar); 182 | 183 | #endif 184 | -------------------------------------------------------------------------------- /src/WifiControl.cpp: -------------------------------------------------------------------------------- 1 | #include "../Configuration.hpp" 2 | #include "Utility.hpp" 3 | #include "WifiControl.hpp" 4 | #include "MeadeCommandProcessor.hpp" 5 | #include "Mount.hpp" 6 | 7 | #if (WIFI_ENABLED == 1) 8 | 9 | WifiControl::WifiControl(Mount *mount, LcdMenu *lcdMenu) 10 | { 11 | _mount = mount; 12 | _lcdMenu = lcdMenu; 13 | } 14 | 15 | void WifiControl::setup() 16 | { 17 | LOG(DEBUG_WIFI, "[WIFI]: Starting up Wifi As Mode %d\n", WIFI_MODE); 18 | 19 | _cmdProcessor = MeadeCommandProcessor::instance(); 20 | 21 | switch (WIFI_MODE) 22 | { 23 | case WIFI_MODE_INFRASTRUCTURE: // startup Infrastructure Mode 24 | startInfrastructureMode(); 25 | break; 26 | case WIFI_MODE_AP_ONLY: // startup AP mode 27 | startAccessPointMode(); 28 | break; 29 | case WIFI_MODE_ATTEMPT_INFRASTRUCTURE_FAIL_TO_AP: // Attempt Infra, fail over to AP 30 | startInfrastructureMode(); 31 | _infraStart = millis(); 32 | break; 33 | case WIFI_MODE_DISABLED: // Disabled 34 | WiFi.mode(WIFI_OFF); 35 | btStop(); 36 | break; 37 | } 38 | } 39 | 40 | void WifiControl::startInfrastructureMode() 41 | { 42 | LOG(DEBUG_WIFI, "[WIFI]: Starting Infrastructure Mode Wifi"); 43 | LOG(DEBUG_WIFI, "[WIFI]: with host name: %s", String(WIFI_HOSTNAME).c_str()); 44 | LOG(DEBUG_WIFI, "[WIFI]: for SSID: %s", String(WIFI_INFRASTRUCTURE_MODE_SSID).c_str()); 45 | LOG(DEBUG_WIFI, "[WIFI]: and WPA key: %s", String(WIFI_INFRASTRUCTURE_MODE_WPAKEY).c_str()); 46 | 47 | #if defined(ESP32) 48 | WiFi.setHostname(WIFI_HOSTNAME); 49 | #endif 50 | WiFi.begin(WIFI_INFRASTRUCTURE_MODE_SSID, WIFI_INFRASTRUCTURE_MODE_WPAKEY); 51 | } 52 | 53 | void WifiControl::startAccessPointMode() 54 | { 55 | LOG(DEBUG_WIFI, "[WIFI]: Starting AP Mode Wifi"); 56 | IPAddress local_ip(192, 168, 1, 1); 57 | IPAddress gateway(192, 168, 1, 1); 58 | IPAddress subnet(255, 255, 255, 0); 59 | 60 | #if defined(ESP32) 61 | WiFi.setHostname(WIFI_HOSTNAME); 62 | #endif 63 | 64 | WiFi.softAP(WIFI_HOSTNAME, WIFI_AP_MODE_WPAKEY); 65 | WiFi.softAPConfig(local_ip, gateway, subnet); 66 | } 67 | 68 | String wifiStatus(int status) 69 | { 70 | if (status == WL_IDLE_STATUS) 71 | return "Idle."; 72 | if (status == WL_NO_SSID_AVAIL) 73 | return "No SSID available."; 74 | if (status == WL_SCAN_COMPLETED) 75 | return "Scan completed."; 76 | if (status == WL_CONNECTED) 77 | return "Connected!"; 78 | if (status == WL_CONNECT_FAILED) 79 | return "Connect failed."; 80 | if (status == WL_CONNECTION_LOST) 81 | return "Connection Lost."; 82 | if (status == WL_DISCONNECTED) 83 | return "Disconnected."; 84 | return "Status " + String(status); 85 | } 86 | 87 | String WifiControl::getStatus() 88 | { 89 | if (WIFI_MODE == WIFI_MODE_DISABLED) 90 | { 91 | return "0,"; 92 | } 93 | 94 | String result = "1,"; 95 | 96 | if (WIFI_MODE == WIFI_MODE_INFRASTRUCTURE) 97 | { 98 | result += "Infrastructure,"; 99 | } 100 | else if (WIFI_MODE == WIFI_MODE_AP_ONLY) 101 | { 102 | result += "Access Point,"; 103 | } 104 | else if (WIFI_MODE == WIFI_MODE_ATTEMPT_INFRASTRUCTURE_FAIL_TO_AP) 105 | { 106 | result += "Infra-Fail-To-AP,"; 107 | } 108 | 109 | result += wifiStatus(WiFi.status()) + ","; 110 | #if defined(ESP32) 111 | result += WiFi.getHostname(); 112 | #endif 113 | 114 | result += "," + WiFi.localIP().toString() + ":" + WIFI_PORT; 115 | result += "," + String(WIFI_INFRASTRUCTURE_MODE_SSID) + "," + String(WIFI_HOSTNAME); 116 | 117 | return result; 118 | } 119 | 120 | void WifiControl::loop() 121 | { 122 | if (WIFI_MODE == WIFI_MODE_DISABLED) 123 | { 124 | return; 125 | } 126 | if (_status != WiFi.status()) 127 | { 128 | _status = WiFi.status(); 129 | LOG(DEBUG_WIFI, "[WIFI]: Connected status changed to %s", wifiStatus(_status).c_str()); 130 | if (_status == WL_CONNECTED) 131 | { 132 | _tcpServer = new WiFiServer(WIFI_PORT); 133 | _tcpServer->begin(); 134 | _tcpServer->setNoDelay(true); 135 | 136 | _udp = new WiFiUDP(); 137 | _udp->begin(4031); 138 | 139 | LOG(DEBUG_WIFI, 140 | "[WIFI]: Connecting to SSID %s at %s:%d", 141 | WIFI_INFRASTRUCTURE_MODE_SSID, 142 | WiFi.localIP().toString().c_str(), 143 | WIFI_PORT); 144 | } 145 | } 146 | 147 | _mount->loop(); 148 | 149 | if (_status != WL_CONNECTED) 150 | { 151 | infraToAPFailover(); 152 | return; 153 | } 154 | 155 | tcpLoop(); 156 | udpLoop(); 157 | } 158 | 159 | void WifiControl::infraToAPFailover() 160 | { 161 | if (_infraStart != 0 && !WiFi.isConnected() && _infraStart + _infraWait < millis()) 162 | { 163 | WiFi.disconnect(); 164 | startAccessPointMode(); 165 | _infraStart = 0; 166 | 167 | LOG(DEBUG_WIFI, "[WIFI]: Could not connect to Infra, Starting AP."); 168 | } 169 | } 170 | 171 | void WifiControl::tcpLoop() 172 | { 173 | if (client && client.connected()) 174 | { 175 | while (client.available()) 176 | { 177 | LOG(DEBUG_WIFI, "[WIFITCP]: Available bytes %d. Peeking.", client.available()); 178 | 179 | // Peek first byte and check for ACK (0x06) handshake 180 | LOG(DEBUG_WIFI, "[WIFITCP]: First byte is %x", client.peek()); 181 | if (client.peek() == 0x06) 182 | { 183 | client.read(); 184 | LOG(DEBUG_WIFI, "[WIFITCP]: Query <-- Handshake request"); 185 | client.write("P"); 186 | LOG(DEBUG_WIFI, "[WIFITCP]: Reply --> P (polar mode)"); 187 | } 188 | else 189 | { 190 | String cmd = client.readStringUntil('#'); 191 | LOG(DEBUG_WIFI, "[WIFITCP]: Query <-- %s#", cmd.c_str()); 192 | String retVal = _cmdProcessor->processCommand(cmd); 193 | 194 | if (retVal != "") 195 | { 196 | client.write(retVal.c_str()); 197 | LOG(DEBUG_WIFI, "[WIFITCP]: Reply --> %s", retVal.c_str()); 198 | } 199 | else 200 | { 201 | LOG(DEBUG_WIFI, "[WIFITCP]: No Reply"); 202 | } 203 | } 204 | 205 | _mount->loop(); 206 | } 207 | } 208 | else 209 | { 210 | client = _tcpServer->available(); 211 | } 212 | } 213 | 214 | void WifiControl::udpLoop() 215 | { 216 | int packetSize = _udp->parsePacket(); 217 | if (packetSize) 218 | { 219 | String lookingFor = "skyfi:"; 220 | String reply = "skyfi:"; 221 | reply += WIFI_HOSTNAME; 222 | reply += "@"; 223 | reply += WiFi.localIP().toString(); 224 | LOG(DEBUG_WIFI, 225 | "[WIFIUDP]: Received %d bytes from %s, port %d", 226 | packetSize, 227 | _udp->remoteIP().toString().c_str(), 228 | _udp->remotePort()); 229 | char incomingPacket[255]; 230 | int len = _udp->read(incomingPacket, 255); 231 | incomingPacket[len] = 0; 232 | LOG(DEBUG_WIFI, "[WIFIUDP]: Received: %s", incomingPacket); 233 | 234 | incomingPacket[lookingFor.length()] = 0; 235 | if (lookingFor.equalsIgnoreCase(incomingPacket)) 236 | { 237 | _udp->beginPacket(_udp->remoteIP(), 4031); 238 | /*unsigned char bytes[255]; 239 | reply.getBytes(bytes, 255); 240 | _udp->write(bytes, reply.length());*/ 241 | 242 | #if defined(ESP32) 243 | _udp->print(reply.c_str()); 244 | #endif 245 | 246 | _udp->endPacket(); 247 | LOG(DEBUG_WIFI, "[WIFIUDP]: Replied: %s", reply.c_str()); 248 | } 249 | } 250 | } 251 | #endif 252 | -------------------------------------------------------------------------------- /src/WifiControl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if (WIFI_ENABLED == 1) 4 | #include "WiFiServer.h" 5 | #include "WiFiUdp.h" 6 | #include "WiFiClient.h" 7 | 8 | #ifdef ESP32 9 | #include 10 | #include 11 | #endif 12 | 13 | // Forward declarations 14 | class Mount; 15 | class LcdMenu; 16 | class MeadeCommandProcessor; 17 | 18 | class WifiControl 19 | { 20 | public: 21 | WifiControl(Mount *mount, LcdMenu *lcdMenu); 22 | void setup(); 23 | void loop(); 24 | String getStatus(); 25 | 26 | private: 27 | void startInfrastructureMode(); 28 | void startAccessPointMode(); 29 | void infraToAPFailover(); 30 | void tcpLoop(); 31 | void udpLoop(); 32 | wl_status_t _status; 33 | Mount *_mount; 34 | LcdMenu *_lcdMenu; 35 | MeadeCommandProcessor *_cmdProcessor; 36 | 37 | WiFiServer *_tcpServer; 38 | WiFiUDP *_udp; 39 | WiFiClient client; 40 | 41 | unsigned long _infraStart = 0; 42 | unsigned long _infraWait = 30000; // 30 second timeout for 43 | }; 44 | 45 | extern WifiControl wifiControl; 46 | 47 | #endif // WIFI_ENABLED 48 | -------------------------------------------------------------------------------- /src/a_inits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../Configuration.hpp" 4 | #include "inc/Globals.hpp" 5 | 6 | #ifndef NEW_STEPPER_LIB 7 | PUSH_NO_WARNINGS 8 | #include 9 | POP_NO_WARNINGS 10 | #endif 11 | 12 | #include "Utility.hpp" 13 | #include "DayTime.hpp" 14 | #include "Mount.hpp" 15 | #include "MeadeCommandProcessor.hpp" 16 | 17 | // TODO: we have to change driver type to DRIVER_TYPE_TMC2209 and add a new definition for the actual mode (e.g. DRIVER_MODE_UART) 18 | #if (RA_DRIVER_TYPE == DRIVER_TYPE_TMC2209_STANDALONE) || (RA_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART) \ 19 | || (DEC_DRIVER_TYPE == DRIVER_TYPE_TMC2209_STANDALONE) || (DEC_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART) \ 20 | || (AZ_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART) || (ALT_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART) \ 21 | || (FOCUS_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART) 22 | PUSH_NO_WARNINGS 23 | #include 24 | POP_NO_WARNINGS 25 | #endif 26 | 27 | #if USE_GPS == 1 28 | PUSH_NO_WARNINGS 29 | //#include 30 | #include 31 | POP_NO_WARNINGS 32 | 33 | //SoftwareSerial SoftSerial(GPS_SERIAL_RX_PIN, GPS_SERIAL_TX_PIN); // RX, TX 34 | TinyGPSPlus gps; 35 | #endif 36 | 37 | //////////////////////////////////// 38 | // Stepper definitions ///////////// 39 | #define RAmotorPin1 RA_STEP_PIN 40 | #define RAmotorPin2 RA_DIR_PIN 41 | 42 | // DEC Motor pins 43 | #define DECmotorPin1 DEC_STEP_PIN 44 | #define DECmotorPin2 DEC_DIR_PIN 45 | 46 | // AZ Motor pins 47 | #if (AZ_STEPPER_TYPE != STEPPER_TYPE_NONE) 48 | #define AZmotorPin1 AZ_STEP_PIN 49 | #define AZmotorPin2 AZ_DIR_PIN 50 | #endif 51 | 52 | // ALT Motor pins 53 | #if (ALT_STEPPER_TYPE != STEPPER_TYPE_NONE) 54 | #define ALTmotorPin1 ALT_STEP_PIN 55 | #define ALTmotorPin2 ALT_DIR_PIN 56 | #endif 57 | 58 | // Focus Motor pins 59 | #if (FOCUS_STEPPER_TYPE != STEPPER_TYPE_NONE) 60 | #define FOCUSmotorPin1 FOCUS_STEP_PIN 61 | #define FOCUSmotorPin2 FOCUS_DIR_PIN 62 | #endif 63 | 64 | // End Stepper Definitions ////////////// 65 | ///////////////////////////////////////// 66 | 67 | ///////////////////////////////////////// 68 | // Driver definitions /////////////////// 69 | #if (RA_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART) || (DEC_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART) \ 70 | || (AZ_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART) || (ALT_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART) \ 71 | || (FOCUS_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART) 72 | #define R_SENSE 0.11f // 0.11 for StepStick 73 | #endif 74 | // End Driver Definitions /////////////// 75 | ///////////////////////////////////////// 76 | 77 | // Menu IDs 78 | #define RA_Menu 1 79 | #define DEC_Menu 2 80 | #define HA_Menu 3 81 | #define Heat_Menu 4 82 | #define Calibration_Menu 5 83 | #define Focuser_Menu 6 84 | #define Control_Menu 7 85 | #define Home_Menu 8 86 | #define POI_Menu 9 87 | #define Status_Menu 10 88 | 89 | // How many menu items at most? 90 | #define MAXMENUITEMS 11 91 | 92 | #if SUPPORT_GUIDED_STARTUP == 1 93 | bool inStartup = true; // Start with a guided startup 94 | #else 95 | bool inStartup = false; // Start with a guided startup 96 | #endif 97 | 98 | // Serial control variables 99 | bool okToUpdateMenu = true; // Can be used to supress rendering the first line of the menu. 100 | bool quitSerialOnNextButtonRelease = false; // Used to detect SELECT button to quit Serial mode. 101 | 102 | // RA variables 103 | int RAselect; 104 | 105 | // DEC variables 106 | int DECselect; 107 | 108 | // HA variables 109 | int HAselect; 110 | -------------------------------------------------------------------------------- /src/c65_startup.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../Configuration.hpp" 3 | 4 | #include "Sidereal.hpp" 5 | 6 | #if USE_GYRO_LEVEL == 1 7 | #include "Gyro.hpp" 8 | #endif 9 | 10 | #if DISPLAY_TYPE > 0 11 | #if SUPPORT_GUIDED_STARTUP == 1 12 | 13 | ////////////////////////////////////////////////////////////// 14 | // This file contains the Starup 'wizard' that guides you through initial setup 15 | 16 | void setControlMode(bool); // In CTRL menu 17 | 18 | enum startupState_t 19 | { 20 | StartupIsInHomePosition, 21 | StartupSetRoll, 22 | StartupWaitForRollCompletion, 23 | StartupRollConfirmed, 24 | StartupSetHATime, 25 | StartupWaitForHACompletion, 26 | StartupHAConfirmed, 27 | StartupWaitForPoleCompletion, 28 | StartupPoleConfirmed, 29 | StartupCompleted, 30 | }; 31 | 32 | #define YES 1 33 | #define NO 2 34 | #define CANCEL 3 35 | 36 | startupState_t startupState = StartupIsInHomePosition; 37 | int isInHomePosition = NO; 38 | 39 | void startupIsCompleted() 40 | { 41 | LOG(DEBUG_INFO, "[STARTUP]: Completed!"); 42 | 43 | startupState = StartupCompleted; 44 | inStartup = false; 45 | okToUpdateMenu = true; 46 | 47 | #if TRACK_ON_BOOT == 1 48 | // Start tracking. 49 | LOG(DEBUG_ANY, "[STARTUP]: Start Tracking."); 50 | mount.startSlewing(TRACKING); 51 | #endif 52 | 53 | // Start on the RA menu 54 | lcdMenu.setActive(RA_Menu); 55 | lcdMenu.updateDisplay(); 56 | LOG(DEBUG_ANY, "[STARTUP]: Completed!"); 57 | } 58 | 59 | bool processStartupKeys() 60 | { 61 | lcdButton_t key; 62 | bool waitForRelease = false; 63 | switch (startupState) 64 | { 65 | case StartupIsInHomePosition: 66 | { 67 | if (lcdButtons.keyChanged(&key)) 68 | { 69 | waitForRelease = true; 70 | if (key == btnLEFT) 71 | { 72 | isInHomePosition = adjustWrap(isInHomePosition, 1, YES, CANCEL); 73 | } 74 | else if (key == btnSELECT) 75 | { 76 | if (isInHomePosition == YES) 77 | { 78 | #if USE_GYRO_LEVEL == 1 79 | startupState = StartupSetRoll; 80 | LOG(DEBUG_INFO, "[STARTUP]: State is set roll!"); 81 | #else 82 | startupState = StartupSetHATime; 83 | #endif 84 | } 85 | else if (isInHomePosition == NO) 86 | { 87 | startupState = StartupWaitForPoleCompletion; 88 | inStartup = false; 89 | okToUpdateMenu = false; 90 | lcdMenu.setCursor(0, 0); 91 | lcdMenu.printMenu("Home with ^~<>"); 92 | lcdMenu.setActive(Control_Menu); 93 | 94 | // Skip the 'Manual control' prompt 95 | setControlMode(true); 96 | } 97 | else if (isInHomePosition == CANCEL) 98 | { 99 | startupIsCompleted(); 100 | } 101 | } 102 | } 103 | } 104 | break; 105 | 106 | #if USE_GYRO_LEVEL == 1 107 | case StartupSetRoll: 108 | { 109 | inStartup = false; 110 | LOG(DEBUG_INFO, "[STARTUP]: Switching to CAL menu!"); 111 | 112 | lcdMenu.setCursor(0, 0); 113 | lcdMenu.printMenu("Level front"); 114 | lcdMenu.setActive(Calibration_Menu); 115 | 116 | startupState = StartupWaitForRollCompletion; 117 | } 118 | break; 119 | 120 | case StartupRollConfirmed: 121 | { 122 | LOG(DEBUG_INFO, "[STARTUP]: Roll confirmed!"); 123 | startupState = StartupSetHATime; 124 | } 125 | break; 126 | #endif 127 | 128 | case StartupSetHATime: 129 | { 130 | inStartup = false; 131 | LOG(DEBUG_INFO, "[STARTUP]: Switching to HA menu!"); 132 | 133 | #if USE_GPS == 0 134 | // Jump to the HA menu 135 | lcdMenu.setCursor(0, 0); 136 | lcdMenu.printMenu("Set current HA"); 137 | lcdMenu.setActive(HA_Menu); 138 | startupState = StartupWaitForHACompletion; 139 | #else 140 | lcdMenu.setCursor(0, 0); 141 | lcdMenu.printMenu("Finding GPS..."); 142 | lcdMenu.setActive(HA_Menu); 143 | startupState = StartupWaitForHACompletion; 144 | #endif 145 | } 146 | break; 147 | 148 | case StartupHAConfirmed: 149 | { 150 | mount.setHome(true); 151 | DayTime ha(mount.HA()); 152 | mount.setHA(ha); 153 | mount.targetRA() = mount.currentRA(); 154 | startupIsCompleted(); 155 | } 156 | break; 157 | 158 | case StartupPoleConfirmed: 159 | { 160 | isInHomePosition = YES; 161 | 162 | // Ask again to confirm 163 | startupState = StartupIsInHomePosition; 164 | } 165 | break; 166 | 167 | default: 168 | break; 169 | } 170 | 171 | return waitForRelease; 172 | } 173 | 174 | void printStartupMenu() 175 | { 176 | switch (startupState) 177 | { 178 | case StartupIsInHomePosition: 179 | { 180 | // 0123456789012345 181 | String choices(" Yes No Cancl "); 182 | if (isInHomePosition == YES) 183 | { 184 | choices.setCharAt(0, '>'); 185 | choices.setCharAt(4, '<'); 186 | } 187 | 188 | if (isInHomePosition == NO) 189 | { 190 | choices.setCharAt(5, '>'); 191 | choices.setCharAt(8, '<'); 192 | } 193 | 194 | if (isInHomePosition == CANCEL) 195 | { 196 | choices.setCharAt(9, '>'); 197 | choices.setCharAt(15, '<'); 198 | } 199 | 200 | lcdMenu.setCursor(0, 0); 201 | lcdMenu.printMenu("Home position?"); 202 | lcdMenu.setCursor(0, 1); 203 | lcdMenu.printMenu(choices); 204 | } 205 | break; 206 | 207 | default: 208 | break; 209 | } 210 | } 211 | #endif 212 | #endif 213 | -------------------------------------------------------------------------------- /src/c70_menuRA.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if DISPLAY_TYPE > 0 4 | bool showTargetRA = true; 5 | bool processRAKeys() 6 | { 7 | lcdButton_t key; 8 | bool waitForRelease = false; 9 | if (lcdButtons.currentState() == btnUP) 10 | { 11 | if (RAselect == 0) 12 | { 13 | mount.targetRA().addHours(1); 14 | showTargetRA = true; 15 | } 16 | if (RAselect == 1) 17 | { 18 | mount.targetRA().addMinutes(1); 19 | showTargetRA = true; 20 | } 21 | if (RAselect == 2) 22 | { 23 | mount.targetRA().addSeconds(1); 24 | showTargetRA = true; 25 | } 26 | if (RAselect == 3) 27 | { 28 | showTargetRA = !showTargetRA; 29 | waitForRelease = true; 30 | } 31 | 32 | // slow down key repetitions 33 | mount.delay(200); 34 | } 35 | else if (lcdButtons.currentState() == btnDOWN) 36 | { 37 | if (RAselect == 0) 38 | { 39 | mount.targetRA().addHours(-1); 40 | showTargetRA = true; 41 | } 42 | if (RAselect == 1) 43 | { 44 | mount.targetRA().addMinutes(-1); 45 | showTargetRA = true; 46 | } 47 | if (RAselect == 2) 48 | { 49 | mount.targetRA().addSeconds(-1); 50 | showTargetRA = true; 51 | } 52 | if (RAselect == 3) 53 | { 54 | showTargetRA = !showTargetRA; 55 | waitForRelease = true; 56 | } 57 | 58 | // slow down key repetitions 59 | mount.delay(200); 60 | } 61 | else if (lcdButtons.keyChanged(&key)) 62 | { 63 | waitForRelease = true; 64 | switch (key) 65 | { 66 | case btnLEFT: 67 | { 68 | RAselect = adjustWrap(RAselect, 1, 0, 3); 69 | } 70 | break; 71 | 72 | case btnSELECT: 73 | { 74 | if (mount.isSlewingRAorDEC()) 75 | { 76 | mount.stopSlewing(ALL_DIRECTIONS); 77 | mount.waitUntilStopped(ALL_DIRECTIONS); 78 | } 79 | 80 | mount.startSlewingToTarget(); 81 | } 82 | break; 83 | 84 | case btnRIGHT: 85 | { 86 | lcdMenu.setNextActive(); 87 | } 88 | break; 89 | 90 | default: 91 | break; 92 | } 93 | } 94 | 95 | return waitForRelease; 96 | } 97 | 98 | void printRASubmenu() 99 | { 100 | if (mount.isSlewingIdle()) 101 | { 102 | String ra = mount.RAString(LCDMENU_STRING | (showTargetRA ? TARGET_STRING : CURRENT_STRING), RAselect).substring(0, 12); 103 | ra += (RAselect == 3) ? ">" : " "; 104 | ra += showTargetRA ? "Ta" : "Cu"; 105 | lcdMenu.printMenu(ra); 106 | } 107 | } 108 | #endif 109 | -------------------------------------------------------------------------------- /src/c71_menuDEC.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if DISPLAY_TYPE > 0 4 | bool showTargetDEC = true; 5 | bool processDECKeys() 6 | { 7 | lcdButton_t key; 8 | bool waitForRelease = false; 9 | if (lcdButtons.currentState() == btnUP) 10 | { 11 | if (DECselect == 0) 12 | { 13 | mount.targetDEC().addDegrees(1); 14 | showTargetDEC = true; 15 | } 16 | if (DECselect == 1) 17 | { 18 | mount.targetDEC().addMinutes(1); 19 | showTargetDEC = true; 20 | } 21 | if (DECselect == 2) 22 | { 23 | mount.targetDEC().addSeconds(1); 24 | showTargetDEC = true; 25 | } 26 | if (DECselect == 3) 27 | { 28 | showTargetDEC = !showTargetDEC; 29 | waitForRelease = true; 30 | } 31 | // slow down key repetitions 32 | mount.delay(200); 33 | } 34 | else if (lcdButtons.currentState() == btnDOWN) 35 | { 36 | if (DECselect == 0) 37 | { 38 | mount.targetDEC().addDegrees(-1); 39 | showTargetDEC = true; 40 | } 41 | if (DECselect == 1) 42 | { 43 | mount.targetDEC().addMinutes(-1); 44 | showTargetDEC = true; 45 | } 46 | if (DECselect == 2) 47 | { 48 | mount.targetDEC().addSeconds(-1); 49 | showTargetDEC = true; 50 | } 51 | if (DECselect == 3) 52 | { 53 | showTargetDEC = !showTargetDEC; 54 | waitForRelease = true; 55 | } 56 | // slow down key repetitions 57 | mount.delay(200); 58 | } 59 | else if (lcdButtons.keyChanged(&key)) 60 | { 61 | waitForRelease = true; 62 | switch (key) 63 | { 64 | case btnLEFT: 65 | { 66 | DECselect = adjustWrap(DECselect, 1, 0, 3); 67 | } 68 | break; 69 | 70 | case btnSELECT: 71 | { 72 | if (mount.isSlewingRAorDEC()) 73 | { 74 | mount.stopSlewing(ALL_DIRECTIONS); 75 | } 76 | else 77 | { 78 | mount.startSlewingToTarget(); 79 | } 80 | } 81 | break; 82 | 83 | case btnRIGHT: 84 | { 85 | lcdMenu.setNextActive(); 86 | } 87 | break; 88 | 89 | default: 90 | break; 91 | } 92 | } 93 | 94 | return waitForRelease; 95 | } 96 | 97 | void printDECSubmenu() 98 | { 99 | if (mount.isSlewingIdle()) 100 | { 101 | String dec = mount.DECString(LCDMENU_STRING | (showTargetDEC ? TARGET_STRING : CURRENT_STRING), DECselect).substring(0, 13); 102 | dec += (DECselect == 3) ? ">" : " "; 103 | dec += showTargetDEC ? "Ta" : "Cu"; 104 | lcdMenu.printMenu(dec); 105 | } 106 | } 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /src/c722_menuPOI.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if DISPLAY_TYPE > 0 4 | #if SUPPORT_POINTS_OF_INTEREST == 1 5 | struct PointOfInterest { 6 | const char *pDisplay; 7 | byte hourRA; 8 | byte minRA; 9 | byte secRA; 10 | int degreeDEC; 11 | byte minDEC; 12 | byte secDEC; 13 | }; 14 | 15 | // Points of interest are sorted by DEC 16 | // Note that you cannot add any points of interest between 0 and -1 degree Declination (don't ask). 17 | PointOfInterest pointOfInterest[] = { 18 | // Name (15chars) RA (hms) DEC (dms) 19 | //0123456789012345 20 | {">Polaris", POLARIS_RA_HOUR, POLARIS_RA_MINUTE, POLARIS_RA_SECOND, 89, 21, 6}, 21 | {">Altair", 19, 51, 45, 8, 55, 15}, 22 | {">Arcturus", 14, 16, 37, 19, 5, 21}, 23 | {">Big Dipper", 12, 16, 26, 56, 55, 7}, 24 | {">Cederblad 214", 0, 5, 46, 67, 16, 45}, 25 | {">Deneb (Cygnus)", 20, 42, 7, 45, 21, 12}, 26 | {">Elephant Trunk", 21, 39, 44, 57, 35, 31}, 27 | {">Heart Nebula", 2, 34, 57, 61, 31, 17}, 28 | {">Lagoon Nebula", 18, 5, 2, -24, 22, 52}, 29 | {">M31 Andromeda", 0, 43, 52, 41, 22, 53}, 30 | {">M33 Triangulum", 1, 35, 02, 30, 46, 5}, 31 | {">M42 Orion Nbula", 5, 36, 18, -5, 22, 44}, 32 | {">M51 Whirlpool", 13, 30, 45, 47, 5, 21}, 33 | {">M63 Sunflower", 13, 16, 45, 41, 55, 14}, 34 | {">M81 Bodes Galxy", 9, 57, 13, 68, 58, 1}, 35 | {">M101 Pinwheel", 14, 3, 56, 54, 15, 0}, 36 | {">Navi", 0, 57, 57, 60, 49, 33}, 37 | {">Pleiades 7Sistr", 3, 48, 15, 24, 10, 54}, 38 | {">Soul Nebula", 2, 52, 47, 60, 30, 56}, 39 | {">Vega", 18, 37, 37, 38, 48, 7}, 40 | {">Veil Nebula", 20, 51, 28, 30, 59, 30}, 41 | 42 | // Add new items above here, not below. 43 | {">Home", 0, 0, 0, 90, 0, 0}, 44 | {">Unpark", 0, 0, 0, 90, 0, 0}, 45 | {">Park", 0, 0, 0, 90, 0, 0}, 46 | // And definitely don't add here. 47 | }; 48 | 49 | int currentPOI = 0; 50 | int parkPOI = sizeof(pointOfInterest) / sizeof(pointOfInterest[0]) - 1; 51 | int unparkPOI = sizeof(pointOfInterest) / sizeof(pointOfInterest[0]) - 2; 52 | byte homePOI = sizeof(pointOfInterest) / sizeof(pointOfInterest[0]) - 3; 53 | 54 | bool processPOIKeys() 55 | { 56 | lcdButton_t key; 57 | bool waitForRelease = false; 58 | if (lcdButtons.keyChanged(&key)) 59 | { 60 | waitForRelease = true; 61 | switch (key) 62 | { 63 | case btnSELECT: 64 | { 65 | mount.stopSlewing(ALL_DIRECTIONS); 66 | if (currentPOI == homePOI) 67 | { 68 | mount.startSlewingToHome(); 69 | } 70 | else if (currentPOI == parkPOI) 71 | { 72 | mount.park(); 73 | } 74 | else if (currentPOI == unparkPOI) 75 | { 76 | mount.startSlewing(TRACKING); 77 | } 78 | else 79 | { 80 | PointOfInterest *poi = &pointOfInterest[currentPOI]; 81 | LOG(DEBUG_INFO, "[POI]: Selected %s. RA: %d %d %d", poi->pDisplay, poi->hourRA, poi->minRA, poi->secRA); 82 | LOG(DEBUG_INFO, "[POI]: Selected %s. DEC: %d %d %d", poi->pDisplay, poi->degreeDEC, poi->minDEC, poi->secDEC); 83 | long targetSeconds = (60L * abs(poi->degreeDEC) + poi->minDEC) * 60L + poi->secDEC; 84 | targetSeconds *= (poi->degreeDEC < 0 ? -1 : 1); 85 | mount.targetRA().set(poi->hourRA, poi->minRA, poi->secRA); 86 | mount.targetDEC() = Declination::FromSeconds(targetSeconds); 87 | LOG(DEBUG_INFO, "[POI]: Target RA is %s. %ls", mount.targetRA().ToString(), targetSeconds); 88 | LOG(DEBUG_INFO, 89 | "[POI]: mount target DEC is %s. %ls", 90 | mount.targetDEC().ToString(), 91 | mount.targetDEC().getTotalSeconds()); 92 | mount.startSlewingToTarget(); 93 | } 94 | } 95 | break; 96 | 97 | case btnLEFT: 98 | case btnDOWN: 99 | { 100 | currentPOI = adjustWrap(currentPOI, 1, 0, parkPOI); 101 | } 102 | break; 103 | 104 | case btnUP: 105 | { 106 | currentPOI = adjustWrap(currentPOI, -1, 0, parkPOI); 107 | } 108 | break; 109 | 110 | case btnRIGHT: 111 | { 112 | lcdMenu.setNextActive(); 113 | } 114 | break; 115 | 116 | default: 117 | break; 118 | } 119 | } 120 | 121 | return waitForRelease; 122 | } 123 | 124 | void printPOISubmenu() 125 | { 126 | if (mount.isSlewingIdle()) 127 | { 128 | lcdMenu.printMenu(pointOfInterest[currentPOI].pDisplay); 129 | } 130 | } 131 | #endif 132 | #endif 133 | -------------------------------------------------------------------------------- /src/c725_menuHOME.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "b_setup.hpp" 4 | 5 | #if DISPLAY_TYPE > 0 6 | byte subGoIndex = 0; 7 | 8 | bool processHomeKeys() 9 | { 10 | lcdButton_t key; 11 | bool waitForRelease = false; 12 | 13 | if (lcdButtons.keyChanged(&key)) 14 | { 15 | waitForRelease = true; 16 | switch (key) 17 | { 18 | case btnSELECT: 19 | { 20 | if (subGoIndex == 0) 21 | { 22 | mount.startSlewingToHome(); 23 | } 24 | else if (mount.isSlewingTRK()) 25 | { 26 | mount.park(); 27 | } 28 | else 29 | { 30 | mount.startSlewing(TRACKING); 31 | } 32 | } 33 | break; 34 | 35 | case btnUP: 36 | case btnDOWN: 37 | case btnLEFT: 38 | { 39 | subGoIndex = 1 - subGoIndex; 40 | } 41 | break; 42 | 43 | case btnRIGHT: 44 | { 45 | lcdMenu.setNextActive(); 46 | } 47 | break; 48 | 49 | default: 50 | break; 51 | } 52 | } 53 | 54 | return waitForRelease; 55 | } 56 | 57 | void printHomeSubmenu() 58 | { 59 | char scratchBuffer[16]; 60 | if (mount.isSlewingTRK()) 61 | { 62 | strcpy(scratchBuffer, " Home Park"); 63 | } 64 | else 65 | { 66 | strcpy(scratchBuffer, " Home Unpark"); 67 | } 68 | scratchBuffer[subGoIndex * 6] = '>'; 69 | lcdMenu.printMenu(scratchBuffer); 70 | } 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src/c72_menuHA.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../Configuration.hpp" 4 | #include "EPROMStore.hpp" 5 | 6 | #if DISPLAY_TYPE > 0 7 | #if USE_GPS == 0 8 | 9 | bool processHAKeys() 10 | { 11 | lcdButton_t key; 12 | bool waitForRelease = false; 13 | if (lcdButtons.currentState() == btnUP) 14 | { 15 | DayTime ha(mount.HA()); 16 | if (HAselect == 0) 17 | ha.addHours(1); 18 | if (HAselect == 1) 19 | ha.addMinutes(1); 20 | mount.setHA(ha); 21 | 22 | // slow down key repetitions 23 | mount.delay(200); 24 | } 25 | else if (lcdButtons.currentState() == btnDOWN) 26 | { 27 | DayTime ha(mount.HA()); 28 | if (HAselect == 0) 29 | ha.addHours(-1); 30 | if (HAselect == 1) 31 | ha.addMinutes(-1); 32 | mount.setHA(ha); 33 | 34 | // slow down key repetitions 35 | mount.delay(200); 36 | } 37 | else if (lcdButtons.keyChanged(&key)) 38 | { 39 | waitForRelease = true; 40 | switch (key) 41 | { 42 | case btnLEFT: 43 | { 44 | HAselect = adjustWrap(HAselect, 1, 0, 1); 45 | } 46 | break; 47 | 48 | case btnSELECT: 49 | { 50 | EEPROMStore::storeHATime(mount.HA()); 51 | lcdMenu.printMenu("Stored."); 52 | mount.delay(500); 53 | 54 | #if SUPPORT_GUIDED_STARTUP == 1 55 | if (startupState == StartupWaitForHACompletion) 56 | { 57 | startupState = StartupHAConfirmed; 58 | inStartup = true; 59 | } 60 | #endif 61 | mount.startSlewing(TRACKING); 62 | } 63 | break; 64 | 65 | case btnRIGHT: 66 | { 67 | #if SUPPORT_GUIDED_STARTUP == 1 68 | if (startupState != StartupWaitForHACompletion) 69 | #endif 70 | { 71 | lcdMenu.setNextActive(); 72 | } 73 | } 74 | break; 75 | 76 | default: 77 | break; 78 | } 79 | } 80 | 81 | return waitForRelease; 82 | } 83 | 84 | void printHASubmenu() 85 | { 86 | char scratchBuffer[20]; 87 | sprintf(scratchBuffer, " %02dh %02dm", mount.HA().getHours(), mount.HA().getMinutes()); 88 | scratchBuffer[HAselect * 4] = '>'; 89 | lcdMenu.printMenu(scratchBuffer); 90 | } 91 | 92 | #endif 93 | #endif 94 | -------------------------------------------------------------------------------- /src/c72_menuHA_GPS.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../Configuration.hpp" 4 | #include "EPROMStore.hpp" 5 | 6 | #if USE_GPS == 1 7 | 8 | #if DEBUG_LEVEL & DEBUG_GPS 9 | char gpsBuf[256]; 10 | int gpsBufPos = 0; 11 | #endif 12 | 13 | long lastGPSUpdate = 0; 14 | bool gpsAqcuisitionComplete(int &indicator) 15 | { 16 | while (GPS_SERIAL_PORT.available()) 17 | { 18 | int gpsChar = GPS_SERIAL_PORT.read(); 19 | 20 | #if DEBUG_LEVEL & DEBUG_GPS 21 | if ((gpsBufPos < 254) && (gpsChar > 31)) 22 | { 23 | gpsBuf[gpsBufPos++] = gpsChar; 24 | } 25 | #endif 26 | if (gpsChar == 36) 27 | { 28 | // $ (ASCII 36) marks start of message, so we switch indicator every message 29 | if (millis() - lastGPSUpdate > 500) 30 | { 31 | indicator = adjustWrap(indicator, 1, 0, 3); 32 | lastGPSUpdate = millis(); 33 | } 34 | } 35 | if (gps.encode(gpsChar)) 36 | { 37 | #if DEBUG_LEVEL & DEBUG_GPS 38 | gpsBuf[gpsBufPos++] = 0; 39 | LOG(DEBUG_GPS, "[GPS]: Sentence: [%s]", gpsBuf); 40 | gpsBufPos = 0; 41 | #endif 42 | 43 | LOG(DEBUG_GPS, 44 | "[GPS]: Encoded. %l sats, Location is%svalid, age is %lms", 45 | gps.satellites.value(), 46 | (gps.location.isValid() ? " " : " NOT "), 47 | gps.location.age()); 48 | // Make sure we got a fix in the last 30 seconds 49 | if ((gps.location.lng() != 0) && (gps.location.age() < 30000UL)) 50 | { 51 | LOG(DEBUG_INFO, "[GPS]: Sync'd GPS location. Age is %d secs", gps.location.age() / 1000); 52 | LOG(DEBUG_INFO, "[GPS]: Location: %f %f", gps.location.lat(), gps.location.lng()); 53 | LOG(DEBUG_INFO, "[GPS]: UTC time is %dh%dm%ds", gps.time.hour(), gps.time.minute(), gps.time.second()); 54 | lcdMenu.printMenu("GPS sync'd...."); 55 | 56 | DayTime utcNow = DayTime(gps.time.hour(), gps.time.minute(), gps.time.second()); 57 | utcNow.addHours(mount.getLocalUtcOffset()); 58 | mount.setLocalStartTime(utcNow); 59 | mount.setLocalStartDate(gps.date.year(), gps.date.month(), gps.date.day()); 60 | mount.setLatitude(gps.location.lat()); 61 | mount.setLongitude(gps.location.lng()); 62 | 63 | mount.delay(500); 64 | 65 | return true; 66 | } 67 | } 68 | } 69 | return false; 70 | } 71 | 72 | #if DISPLAY_TYPE > 0 73 | 74 | // States that HA menu displays goes through 75 | enum haMenuState_t 76 | { 77 | SHOWING_HA_SYNC = 1, 78 | SHOWING_HA_SET, 79 | ENTER_HA_MANUALLY, 80 | STARTING_GPS, 81 | }; 82 | 83 | int indicator = 0; 84 | haMenuState_t haState = STARTING_GPS; 85 | 86 | bool processHAKeys() 87 | { 88 | lcdButton_t key; 89 | bool waitForRelease = false; 90 | 91 | if (haState == STARTING_GPS) 92 | { 93 | if (gpsAqcuisitionComplete(indicator)) 94 | { 95 | LOG(DEBUG_INFO, "[HA]: GPS acquired"); 96 | GPS_SERIAL_PORT.end(); 97 | haState = SHOWING_HA_SYNC; 98 | #if SUPPORT_GUIDED_STARTUP == 1 99 | if (startupState == StartupWaitForHACompletion) 100 | { 101 | LOG(DEBUG_INFO, "[HA]: We were in startup, so confirm HA"); 102 | startupState = StartupHAConfirmed; 103 | inStartup = true; 104 | } 105 | #endif 106 | } 107 | } 108 | 109 | if (lcdButtons.keyChanged(&key)) 110 | { 111 | waitForRelease = true; 112 | LOG(DEBUG_INFO, "[HA]: Key %d was pressed in state %d", key, haState); 113 | if (haState == SHOWING_HA_SYNC) 114 | { 115 | if (key == btnSELECT) 116 | { 117 | haState = STARTING_GPS; 118 | GPS_SERIAL_PORT.begin(GPS_BAUD_RATE); 119 | } 120 | else if ((key == btnUP) || (key == btnDOWN)) 121 | { 122 | haState = SHOWING_HA_SET; 123 | } 124 | } 125 | else if (haState == SHOWING_HA_SET) 126 | { 127 | if (key == btnSELECT) 128 | { 129 | haState = ENTER_HA_MANUALLY; 130 | } 131 | else if ((key == btnUP) || (key == btnDOWN)) 132 | { 133 | haState = SHOWING_HA_SYNC; 134 | } 135 | } 136 | else if (haState == ENTER_HA_MANUALLY) 137 | { 138 | if (key == btnSELECT) 139 | { 140 | DayTime ha(mount.HA()); 141 | EEPROMStore::storeHATime(mount.HA()); 142 | lcdMenu.printMenu("Stored."); 143 | mount.delay(500); 144 | haState = SHOWING_HA_SET; 145 | #if SUPPORT_GUIDED_STARTUP == 1 146 | if (startupState == StartupWaitForHACompletion) 147 | { 148 | startupState = StartupHAConfirmed; 149 | inStartup = true; 150 | } 151 | #endif 152 | } 153 | else if (key == btnUP) 154 | { 155 | DayTime ha(mount.HA()); 156 | if (HAselect == 0) 157 | ha.addHours(1); 158 | if (HAselect == 1) 159 | ha.addMinutes(1); 160 | mount.setHA(ha); 161 | } 162 | else if (key == btnDOWN) 163 | { 164 | DayTime ha(mount.HA()); 165 | if (HAselect == 0) 166 | ha.addHours(-1); 167 | if (HAselect == 1) 168 | ha.addMinutes(-1); 169 | mount.setHA(ha); 170 | } 171 | else if (key == btnLEFT) 172 | { 173 | HAselect = adjustWrap(HAselect, 1, 0, 1); 174 | } 175 | } 176 | 177 | if (key == btnRIGHT) 178 | { 179 | LOG(DEBUG_INFO, "[HA]: Right Key was pressed"); 180 | if (haState == STARTING_GPS) 181 | { 182 | LOG(DEBUG_INFO, "[HA]: In GPS Start mode, switching to manual"); 183 | GPS_SERIAL_PORT.end(); 184 | haState = SHOWING_HA_SYNC; 185 | } 186 | #if SUPPORT_GUIDED_STARTUP == 1 187 | else if (startupState == StartupWaitForHACompletion) 188 | { 189 | LOG(DEBUG_INFO, "[HA]: In Startup, not in GPS Start mode, leaving"); 190 | startupState = StartupHAConfirmed; 191 | inStartup = true; 192 | } 193 | #endif 194 | else 195 | { 196 | LOG(DEBUG_INFO, "[HA]: leaving HA"); 197 | lcdMenu.setNextActive(); 198 | } 199 | } 200 | } 201 | 202 | return waitForRelease; 203 | } 204 | 205 | void printHASubmenu() 206 | { 207 | const char *ind = "*+* "; 208 | char satBuffer[20]; 209 | if (haState == SHOWING_HA_SYNC) 210 | { 211 | sprintf(satBuffer, "%02dh %02dm >Sync", mount.HA().getHours(), mount.HA().getMinutes()); 212 | } 213 | else if (haState == SHOWING_HA_SET) 214 | { 215 | sprintf(satBuffer, "%02dh %02dm >Set", mount.HA().getHours(), mount.HA().getMinutes()); 216 | } 217 | else if (haState == STARTING_GPS) 218 | { 219 | sprintf(satBuffer, " Found %u sats", static_cast(gps.satellites.value())); 220 | satBuffer[0] = ind[indicator]; 221 | } 222 | else if (haState == ENTER_HA_MANUALLY) 223 | { 224 | sprintf(satBuffer, " %02dh %02dm", mount.HA().getHours(), mount.HA().getMinutes()); 225 | satBuffer[HAselect * 4] = '>'; 226 | } 227 | lcdMenu.printMenu(satBuffer); 228 | } 229 | 230 | #endif 231 | #endif 232 | -------------------------------------------------------------------------------- /src/c77_menuFOC.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if (DISPLAY_TYPE > 0) && (FOCUS_STEPPER_TYPE != STEPPER_TYPE_NONE) 4 | 5 | // HIGHLIGHT states allow you to pick one of the sub functions. 6 | enum FocusMenuItem 7 | { 8 | HIGHLIGHT_FOCUS_FIRST = 1, 9 | HIGHLIGHT_FOCUS_ADJUSTMENT = 1, 10 | 11 | HIGHLIGHT_FOCUS_LAST = HIGHLIGHT_FOCUS_ADJUSTMENT, 12 | 13 | FOCUS_ADJUSTMENT, 14 | }; 15 | 16 | FocusMenuItem focState = HIGHLIGHT_FOCUS_FIRST; 17 | byte rateIndex = 3; 18 | 19 | bool processFocuserKeys() 20 | { 21 | lcdButton_t key; 22 | bool waitForRelease = false; 23 | bool checkForKeyChange = true; 24 | 25 | lcdButton_t currentButtonState = lcdButtons.currentState(); 26 | 27 | if (focState == FOCUS_ADJUSTMENT) 28 | { 29 | if (currentButtonState == btnUP) 30 | { 31 | if (!mount.isRunningFocus()) 32 | { 33 | mount.focusContinuousMove(FOCUS_BACKWARD); 34 | } 35 | } 36 | else if (currentButtonState == btnDOWN) 37 | { 38 | if (!mount.isRunningFocus()) 39 | { 40 | mount.focusContinuousMove(FOCUS_FORWARD); 41 | } 42 | } 43 | } 44 | 45 | if (currentButtonState == btnNONE) 46 | { 47 | if (mount.isRunningFocus()) 48 | { 49 | mount.focusStop(); 50 | } 51 | } 52 | 53 | if (checkForKeyChange && lcdButtons.keyChanged(&key)) 54 | { 55 | waitForRelease = true; 56 | 57 | switch (focState) 58 | { 59 | case HIGHLIGHT_FOCUS_ADJUSTMENT: 60 | if (key == btnSELECT) 61 | { 62 | focState = FOCUS_ADJUSTMENT; 63 | } 64 | if (key == btnRIGHT) 65 | { 66 | lcdMenu.setNextActive(); 67 | } 68 | 69 | break; 70 | 71 | case FOCUS_ADJUSTMENT: 72 | { 73 | // UP and DOWN are handled above 74 | if (key == btnSELECT) 75 | { 76 | focState = HIGHLIGHT_FOCUS_ADJUSTMENT; 77 | } 78 | else if (key == btnRIGHT) 79 | { 80 | rateIndex = adjustClamp(rateIndex, 1, 0, 3); 81 | mount.focusSetSpeedByRate(rateIndex + 1); 82 | } 83 | else if (key == btnLEFT) 84 | { 85 | rateIndex = adjustClamp(rateIndex, -1, 0, 3); 86 | mount.focusSetSpeedByRate(rateIndex + 1); 87 | } 88 | } 89 | break; 90 | } 91 | } 92 | return waitForRelease; 93 | } 94 | 95 | void printFocusSubmenu() 96 | { 97 | char scratchBuffer[20]; 98 | if (focState == HIGHLIGHT_FOCUS_ADJUSTMENT) 99 | { 100 | lcdMenu.printMenu(">Focus Adjust"); 101 | } 102 | else if (focState == FOCUS_ADJUSTMENT) 103 | { 104 | strcpy(scratchBuffer, "Rate: 1 2 3 4 *"); 105 | scratchBuffer[6 + rateIndex * 2] = '>'; 106 | scratchBuffer[8 + rateIndex * 2] = '<'; 107 | if (!mount.isRunningFocus()) 108 | { 109 | scratchBuffer[15] = '-'; 110 | } 111 | else 112 | { 113 | scratchBuffer[15] = mount.getFocusSpeed() < 0 ? '~' : '^'; 114 | } 115 | 116 | lcdMenu.printMenu(scratchBuffer); 117 | } 118 | } 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /src/c_buttons.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "LcdButtons.hpp" 4 | #include "b_setup.hpp" 5 | #include "c65_startup.hpp" 6 | #include "c70_menuRA.hpp" 7 | #include "c71_menuDEC.hpp" 8 | #include "c722_menuPOI.hpp" 9 | #include "c72_menuHA.hpp" 10 | #include "c72_menuHA_GPS.hpp" 11 | #include "c75_menuCTRL.hpp" 12 | #include "c76_menuCAL.hpp" 13 | #include "c77_menuFOC.hpp" 14 | #include "c78_menuINFO.hpp" 15 | 16 | #if SUPPORT_SERIAL_CONTROL == 1 17 | #include "f_serial.hpp" 18 | #endif 19 | 20 | #if DISPLAY_TYPE > 0 21 | #if LCD_BUTTON_TEST == 1 22 | lcdButton_t lastKey = btnNONE; 23 | #endif 24 | 25 | lcdButton_t lcd_key; 26 | unsigned long lastTrackingStatusPrint = 0; 27 | 28 | void loop() 29 | { 30 | #if LCD_BUTTON_TEST == 1 31 | int adc_key_in; 32 | 33 | lcdMenu.setCursor(0, 0); 34 | lcdMenu.printMenu("Key Diagnostic"); 35 | lcd_key = lcdButtons.currentState(); 36 | int key = lcdButtons.currentKey(); 37 | bool changed = lcdButtons.keyChanged(&lastKey); 38 | 39 | adc_key_in = lcdButtons.currentAnalogState(); 40 | 41 | lcdMenu.setCursor(0, 1); 42 | char buf[128]; 43 | sprintf(buf, "A:%4d %d ", adc_key_in, key); 44 | String state = String(buf); 45 | switch (lcd_key) 46 | { 47 | case btnNONE: 48 | state += "None"; 49 | break; 50 | case btnSELECT: 51 | state += "Select"; 52 | break; 53 | case btnLEFT: 54 | state += "Left"; 55 | break; 56 | case btnRIGHT: 57 | state += "Right"; 58 | break; 59 | case btnUP: 60 | state += "Up"; 61 | break; 62 | case btnDOWN: 63 | state += "Down"; 64 | break; 65 | default: 66 | state += "Invalid"; 67 | break; 68 | } 69 | 70 | lcdMenu.printMenu(state); 71 | if (changed) 72 | { 73 | Serial.println(lastKey); 74 | } 75 | 76 | return; 77 | 78 | #endif 79 | 80 | // Give the mount a time slice to do its thing... 81 | mount.loop(); 82 | 83 | // Update the LCD display 84 | unsigned long now = millis(); 85 | if (!inSerialControl && okToUpdateMenu && !inStartup && !mount.isSlewingRAorDEC()) 86 | { 87 | // Main menu display 88 | lcdMenu.updateDisplay(); 89 | } 90 | 91 | // Tracking marker 92 | if ((mount.isBootComplete()) && (now - lastTrackingStatusPrint > 200)) 93 | { 94 | lcdMenu.printAt(15, 0, mount.isSlewingTRK() ? '&' : '`'); 95 | lastTrackingStatusPrint = now; 96 | } 97 | 98 | lcdMenu.setCursor(0, 1); 99 | 100 | #if SUPPORT_SERIAL_CONTROL == 1 101 | if (inSerialControl) 102 | { 103 | if (lcdButtons.keyChanged(&lcd_key)) 104 | { 105 | if (lcd_key == btnSELECT) 106 | { 107 | quitSerialOnNextButtonRelease = true; 108 | } 109 | else if ((lcd_key == btnNONE) && quitSerialOnNextButtonRelease) 110 | { 111 | MeadeCommandProcessor::instance()->processCommand(":Qq#"); 112 | quitSerialOnNextButtonRelease = false; 113 | } 114 | } 115 | serialLoop(); 116 | } 117 | else 118 | #endif 119 | { 120 | bool waitForButtonRelease = false; 121 | 122 | // Handle the keys 123 | #if SUPPORT_GUIDED_STARTUP == 1 124 | if (inStartup) 125 | { 126 | waitForButtonRelease = processStartupKeys(); 127 | } 128 | else 129 | #endif 130 | { 131 | switch (lcdMenu.getActive()) 132 | { 133 | case RA_Menu: 134 | waitForButtonRelease = processRAKeys(); 135 | break; 136 | case DEC_Menu: 137 | waitForButtonRelease = processDECKeys(); 138 | break; 139 | #if SUPPORT_POINTS_OF_INTEREST == 1 140 | case POI_Menu: 141 | waitForButtonRelease = processPOIKeys(); 142 | break; 143 | #else 144 | case Home_Menu: 145 | waitForButtonRelease = processHomeKeys(); 146 | break; 147 | #endif 148 | 149 | case HA_Menu: 150 | waitForButtonRelease = processHAKeys(); 151 | break; 152 | 153 | #if SUPPORT_CALIBRATION == 1 154 | case Calibration_Menu: 155 | waitForButtonRelease = processCalibrationKeys(); 156 | break; 157 | #endif 158 | 159 | #if FOCUS_STEPPER_TYPE != STEPPER_TYPE_NONE 160 | case Focuser_Menu: 161 | waitForButtonRelease = processFocuserKeys(); 162 | break; 163 | #endif 164 | 165 | #if SUPPORT_MANUAL_CONTROL == 1 166 | case Control_Menu: 167 | waitForButtonRelease = processControlKeys(); 168 | break; 169 | #endif 170 | 171 | #if SUPPORT_INFO_DISPLAY == 1 172 | case Status_Menu: 173 | waitForButtonRelease = processStatusKeys(); 174 | break; 175 | #endif 176 | } 177 | } 178 | 179 | if (waitForButtonRelease) 180 | { 181 | if (lcdButtons.currentState() != btnNONE) 182 | { 183 | do 184 | { 185 | lcdButton_t waitKey; 186 | if (lcdButtons.keyChanged(&waitKey)) 187 | { 188 | if (waitKey == btnNONE) 189 | { 190 | break; 191 | } 192 | } 193 | 194 | // Make sure tracker can still run while fiddling with menus.... 195 | mount.loop(); 196 | } while (true); 197 | } 198 | } 199 | 200 | // Input handled, do output 201 | lcdMenu.setCursor(0, 1); 202 | 203 | #if SUPPORT_GUIDED_STARTUP == 1 204 | if (inStartup) 205 | { 206 | printStartupMenu(); 207 | } 208 | else 209 | #endif 210 | { 211 | if (!inSerialControl) 212 | { 213 | // For some strange reason, a switch statement here causes a crash and reboot.... 214 | int activeMenu = lcdMenu.getActive(); 215 | if (activeMenu == RA_Menu) 216 | { 217 | printRASubmenu(); 218 | } 219 | else if (activeMenu == DEC_Menu) 220 | { 221 | printDECSubmenu(); 222 | } 223 | #if SUPPORT_POINTS_OF_INTEREST == 1 224 | else if (activeMenu == POI_Menu) 225 | { 226 | printPOISubmenu(); 227 | } 228 | #else 229 | else if (activeMenu == Home_Menu) 230 | { 231 | printHomeSubmenu(); 232 | } 233 | #endif 234 | else if (activeMenu == HA_Menu) 235 | { 236 | printHASubmenu(); 237 | } 238 | #if SUPPORT_MANUAL_CONTROL == 1 239 | else if (activeMenu == Control_Menu) 240 | { 241 | printControlSubmenu(); 242 | } 243 | #endif 244 | #if SUPPORT_CALIBRATION == 1 245 | else if (activeMenu == Calibration_Menu) 246 | { 247 | printCalibrationSubmenu(); 248 | } 249 | #endif 250 | 251 | #if (FOCUS_STEPPER_TYPE != STEPPER_TYPE_NONE) 252 | else if (activeMenu == Focuser_Menu) 253 | { 254 | printFocusSubmenu(); 255 | } 256 | #endif 257 | 258 | #if SUPPORT_INFO_DISPLAY == 1 259 | else if (activeMenu == Status_Menu) 260 | { 261 | printStatusSubmenu(); 262 | } 263 | #endif 264 | } 265 | } 266 | } 267 | } 268 | 269 | #else 270 | 271 | // No display present. 272 | void loop() 273 | { 274 | serialLoop(); 275 | } 276 | 277 | #endif 278 | -------------------------------------------------------------------------------- /src/f_serial.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if TEST_VERIFY_MODE == 1 4 | #include "testmenu.hpp" 5 | #include "testmenudef.hpp" 6 | #endif 7 | 8 | #include "b_setup.hpp" 9 | 10 | #if SUPPORT_SERIAL_CONTROL == 1 11 | #include "MeadeCommandProcessor.hpp" 12 | 13 | void processSerialData(); 14 | 15 | //////////////////////////////////////////////// 16 | // The main loop when under serial control 17 | void serialLoop() 18 | { 19 | mount.loop(); 20 | mount.displayStepperPositionThrottled(); 21 | 22 | #ifdef ESP32 23 | processSerialData(); 24 | #endif 25 | 26 | #if (WIFI_ENABLED == 1) 27 | wifiControl.loop(); 28 | #endif 29 | 30 | #if TEST_VERIFY_MODE == 1 31 | mainTestMenu.tick(); 32 | #endif 33 | } 34 | 35 | ////////////////////////////////////////////////// 36 | // Event that is triggered when the serial port receives data. 37 | #ifndef ESP32 38 | void serialEvent() 39 | { 40 | processSerialData(); 41 | } 42 | #endif 43 | 44 | #if TEST_VERIFY_MODE == 1 45 | 46 | void processTestState() 47 | { 48 | static char buffer[32]; 49 | static unsigned int index = 0; 50 | switch (TestMenu::getMenuState()) 51 | { 52 | case testMenuState_t::CLEAR: 53 | TestMenu::setMenuState(testMenuState_t::WAITING_ON_INPUT); 54 | break; 55 | 56 | case testMenuState_t::WAITING_ON_INPUT: 57 | while (Serial.available() > 0) 58 | { 59 | if (Serial.readBytes(buffer, 1) == 1) 60 | { 61 | if ((buffer[0] >= '0') && (buffer[0] <= '9')) 62 | { 63 | Serial.println(buffer[0]); 64 | int pressedKey = buffer[0] - '0'; 65 | TestMenu::getCurrentMenu()->onKeyPressed(pressedKey); 66 | } 67 | } 68 | } 69 | break; 70 | 71 | case testMenuState_t::WAITING_ON_COMMAND: 72 | while (Serial.available() > 0) 73 | { 74 | char ch; 75 | if (Serial.readBytes(&ch, 1) == 1) 76 | { 77 | if (isascii(ch)) 78 | { 79 | buffer[index] = ch; 80 | if (ch == '#') 81 | { 82 | buffer[index + 1] = '\0'; 83 | TestMenu::getCurrentMenu()->onCommandReceived(buffer); 84 | TestMenu::setMenuState(testMenuState_t::WAITING_ON_INPUT); 85 | TestMenu::getCurrentMenu()->display(); 86 | index = 0; 87 | } 88 | else 89 | { 90 | index++; 91 | if (index > ARRAY_SIZE(buffer) - 1) 92 | { 93 | Serial.println(F("Buffer overflow, too many chars received")); 94 | index = 0; 95 | } 96 | } 97 | } 98 | } 99 | } 100 | break; 101 | } 102 | } 103 | 104 | void processSerialData() 105 | { 106 | processTestState(); 107 | } 108 | #else 109 | // ESP needs to call this in a loop :_( 110 | void processSerialData() 111 | { 112 | static char buffer[20]; 113 | static unsigned int index = 0; 114 | while (Serial.available() > 0) 115 | { 116 | if (Serial.readBytes((buffer + index), 1) == 1) 117 | { 118 | if (buffer[index] == 0x06) 119 | { 120 | LOG(DEBUG_SERIAL, "[SERIAL]: Received: ACK request, replying P"); 121 | // When not debugging, print the result to the serial port . 122 | // When debugging, only print the result to Serial if we're on seperate ports. 123 | #if (DEBUG_LEVEL == DEBUG_NONE) || (DEBUG_SEPARATE_SERIAL == 1) 124 | Serial.print('P'); 125 | #endif 126 | index = 0; 127 | } 128 | else if (buffer[index] == '#') 129 | { 130 | // Ignoring trailing hash 131 | buffer[index] = '\0'; 132 | const String inCmd = String(buffer); 133 | LOG(DEBUG_SERIAL, "[SERIAL]: ReceivedCommand(%d chars): [%s]", inCmd.length(), inCmd.c_str()); 134 | 135 | const String retVal = MeadeCommandProcessor::instance()->processCommand(inCmd); 136 | if (retVal != "") 137 | { 138 | LOG(DEBUG_SERIAL, "[SERIAL]: RepliedWith: [%s]", retVal.c_str()); 139 | // When not debugging, print the result to the serial port . 140 | // When debugging, only print the result to Serial if we're on seperate ports. 141 | #if (DEBUG_LEVEL == DEBUG_NONE) || (DEBUG_SEPARATE_SERIAL == 1) 142 | Serial.print(retVal); 143 | #endif 144 | } 145 | // Wait for next command 146 | index = 0; 147 | } 148 | else if (buffer[index] >= ' ') 149 | { 150 | index++; 151 | if (index >= sizeof(buffer)) 152 | { 153 | LOG(DEBUG_SERIAL, "[SERIAL]: Command buffer overflow! Ignoring received data."); 154 | index = 0; 155 | } 156 | } 157 | } 158 | 159 | mount.loop(); 160 | } 161 | } 162 | 163 | #endif 164 | #endif 165 | -------------------------------------------------------------------------------- /src/inc/Globals.cpp: -------------------------------------------------------------------------------- 1 | #include "Globals.hpp" 2 | 3 | // TODO: Should move this somewhere else... 4 | bool inSerialControl = false; // True when the serial port is in control 5 | bool inNorthernHemisphere = true; // Default to northern hemisphere 6 | -------------------------------------------------------------------------------- /src/inc/Globals.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * These are only necessary for the Arduino IDE build, these are actually 5 | * defined in `pre_script_custom_defines.py` which is run by platformio. 6 | */ 7 | #if !defined(PUSH_NO_WARNINGS) 8 | #define PUSH_NO_WARNINGS ; 9 | #endif 10 | #if !defined(POP_NO_WARNINGS) 11 | #define POP_NO_WARNINGS ; 12 | #endif 13 | 14 | PUSH_NO_WARNINGS 15 | #include 16 | #include 17 | POP_NO_WARNINGS 18 | 19 | extern bool inSerialControl; // True when the serial port is in control 20 | extern bool inNorthernHemisphere; 21 | -------------------------------------------------------------------------------- /src/libs/MappedDict/MappedDict.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * @brief A simple class to deal with 1-1 value mapping, a la python dictionaries 5 | * @details Operations operate on pointers so that this class has no data storage, 6 | * data should be set up by the caller and passed in to the constructor. 7 | * @tparam KeyType Type that should be the 'key' 8 | * @tparam ValueType Type that should be the 'value' 9 | */ 10 | template class MappedDict 11 | { 12 | public: 13 | typedef struct { 14 | KeyType in; 15 | ValueType out; 16 | } DictEntry_t; 17 | 18 | MappedDict(DictEntry_t *dictPtr, size_t dictSize) : _dictPtr(dictPtr), _dictSize(dictSize) 19 | { 20 | } 21 | 22 | /** 23 | * Try to retrieve a value from the mapping 24 | * @param[in] query Key to look for in the dictionary 25 | * @param[out] rtnValPtr Pointer to store the return value in, if successful 26 | * @return true if the query was found in the dictionary, false otherwise 27 | */ 28 | bool tryGet(KeyType query, ValueType *rtnValPtr) 29 | { 30 | bool foundElement = false; 31 | for (unsigned i = 0; i < _dictSize; i++) 32 | { 33 | if (_dictPtr[i].in == query) 34 | { 35 | foundElement = true; 36 | *rtnValPtr = _dictPtr[i].out; 37 | break; 38 | } 39 | } 40 | return foundElement; 41 | } 42 | 43 | private: 44 | DictEntry_t *_dictPtr; ///< Pointer to dictionary storage 45 | size_t _dictSize; ///< Number of elements in the dictionary 46 | }; 47 | -------------------------------------------------------------------------------- /src/libs/TimerInterrupt/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Khoi Hoang 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 | -------------------------------------------------------------------------------- /src/macros/gcc/Macros.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MACROS_HPP 2 | #define MACROS_HPP 3 | 4 | #define DO_PRAGMA_(x) _Pragma(#x) 5 | #define DO_PRAGMA(x) DO_PRAGMA_(x) 6 | 7 | #define PUSH_NO_WARNINGS \ 8 | DO_PRAGMA(GCC diagnostic push); \ 9 | DO_PRAGMA(GCC diagnostic ignored "-Wconversion"); \ 10 | DO_PRAGMA(GCC diagnostic ignored "-Wdouble-promotion"); \ 11 | DO_PRAGMA(GCC diagnostic ignored "-Wshadow"); \ 12 | DO_PRAGMA(GCC diagnostic ignored "-Wsign-conversion"); \ 13 | DO_PRAGMA(GCC diagnostic ignored "-Wsign-compare"); \ 14 | DO_PRAGMA(GCC diagnostic ignored "-Wignored-qualifiers"); \ 15 | DO_PRAGMA(GCC diagnostic ignored "-Wuseless-cast"); \ 16 | DO_PRAGMA(GCC diagnostic ignored "-Wunknown-pragmas"); \ 17 | DO_PRAGMA(GCC diagnostic ignored "-Wall"); \ 18 | DO_PRAGMA(GCC diagnostic ignored "-Wextra"); \ 19 | DO_PRAGMA(GCC diagnostic ignored "-Wpedantic"); 20 | 21 | #define POP_NO_WARNINGS DO_PRAGMA(GCC diagnostic pop); 22 | 23 | #endif // MACROS_HPP -------------------------------------------------------------------------------- /src/testmenu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #if TEST_VERIFY_MODE == 1 5 | 6 | enum menuText_t 7 | { 8 | MENU_BACK, 9 | MENU_CONNECT_RA, 10 | MENU_CONNECT_DEC, 11 | MENU_CONNECT_ALT, 12 | MENU_CONNECT_AZ, 13 | MENU_CONNECT_FOC, 14 | MENU_PRIMARY_RA_CW, 15 | MENU_PRIMARY_RA_CCW, 16 | MENU_PRIMARY_DEC_UP, 17 | MENU_PRIMARY_DEC_DOWN, 18 | MENU_PRIMARY_SET_HOME, 19 | MENU_PRIMARY_GO_HOME, 20 | MENU_TOGGLE_TRK, 21 | MENU_SECONDARY_RATE_1, 22 | MENU_SECONDARY_RATE_2, 23 | MENU_SECONDARY_RATE_3, 24 | MENU_SECONDARY_RATE_4, 25 | MENU_SECONDARY_RATE_5, 26 | MENU_SECONDARY_ALT_UP, 27 | MENU_SECONDARY_ALT_DOWN, 28 | MENU_SECONDARY_AZ_LEFT, 29 | MENU_SECONDARY_AZ_RIGHT, 30 | MENU_FACTORY_RESET, 31 | MENU_PASSTHROUGH_COMMAND, 32 | MENU_MAIN_LIST_HARDWARE, 33 | MENU_MAIN_CONNECT_DRIVERS, 34 | MENU_MAIN_PRIMARY_AXIS_MOVES, 35 | MENU_MAIN_SECONDARY_AXIS_MOVES, 36 | }; 37 | 38 | enum testMenuState_t 39 | { 40 | CLEAR, 41 | WAITING_ON_INPUT, 42 | WAITING_ON_COMMAND, 43 | }; 44 | 45 | // Flag as to what to display to terminal 46 | enum testMenuInternalState 47 | { 48 | IDLE = 0, 49 | DISPLAY_RA = 1 << 0, 50 | DISPLAY_DEC = 1 << 1, 51 | DISPLAY_AZ = 1 << 2, 52 | DISPLAY_ALT = 1 << 3, 53 | }; 54 | 55 | class TestMenu; 56 | 57 | class TestMenuItem 58 | { 59 | int _key; 60 | String _label; 61 | String _action; 62 | TestMenu *_subMenu; 63 | bool _isSubMenu; 64 | 65 | public: 66 | TestMenuItem(menuText_t labelId, TestMenu *subMenu = nullptr); 67 | void display() const; 68 | int getKey() const; 69 | void setKey(int key); 70 | String getAction() const; 71 | TestMenu *getSubMenu() const; 72 | }; 73 | 74 | class TestMenu 75 | { 76 | int _level; 77 | unsigned long _lastTick; 78 | String _name; 79 | String _parent; 80 | TestMenuItem *_choices; 81 | int _numChoices; 82 | TestMenu *_parentMenu; 83 | float _secondaryDistance; 84 | 85 | static long _targetRA; 86 | static long _startRA; 87 | static long _targetDEC; 88 | static long _startDEC; 89 | static long _startAZ; 90 | static long _targetAZ; 91 | static long _startALT; 92 | static long _targetALT; 93 | 94 | static testMenuState_t _menuState; 95 | static testMenuInternalState _internalState; 96 | static TestMenu *_currentMenu; 97 | static TestMenuItem *_backItem; 98 | 99 | public: 100 | TestMenu(int level, String name, String parent, TestMenuItem *choices, int numChoices, TestMenu *parentMenu = nullptr); 101 | void onKeyPressed(int key); 102 | void onCommandReceived(String s); 103 | void display() const; 104 | void displayStepperPos() const; 105 | void setParentMenu(TestMenu *parentMenu); 106 | static TestMenu *getCurrentMenu(); 107 | static testMenuState_t getMenuState(); 108 | static void setMenuState(testMenuState_t state); 109 | 110 | void listHardware() const; 111 | void connectDriver(String axisStr); 112 | void tick(); 113 | }; 114 | #endif 115 | -------------------------------------------------------------------------------- /src/testmenudef.hpp: -------------------------------------------------------------------------------- 1 | #if TEST_VERIFY_MODE == 1 2 | #include "testmenu.hpp" 3 | 4 | TestMenuItem connectMenuItems[] = { 5 | #if RA_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART 6 | TestMenuItem(MENU_CONNECT_RA), 7 | #endif 8 | #if DEC_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART 9 | TestMenuItem(MENU_CONNECT_DEC), 10 | #endif 11 | #if ALT_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART 12 | TestMenuItem(MENU_CONNECT_ALT), 13 | #endif 14 | #if AZ_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART 15 | TestMenuItem(MENU_CONNECT_AZ), 16 | #endif 17 | #if FOCUS_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART 18 | TestMenuItem(MENU_CONNECT_FOC), 19 | #endif 20 | }; 21 | TestMenu connectDriversMenu(1, "ConnectDrivers", "Main menu", connectMenuItems, sizeof(connectMenuItems) / sizeof(connectMenuItems[0])); 22 | 23 | TestMenuItem primaryAxisMenuItems[] = { 24 | TestMenuItem(MENU_PRIMARY_RA_CW), 25 | TestMenuItem(MENU_PRIMARY_RA_CCW), 26 | TestMenuItem(MENU_PRIMARY_DEC_UP), 27 | TestMenuItem(MENU_PRIMARY_DEC_DOWN), 28 | TestMenuItem(MENU_PRIMARY_SET_HOME), 29 | TestMenuItem(MENU_PRIMARY_GO_HOME), 30 | TestMenuItem(MENU_TOGGLE_TRK), 31 | }; 32 | TestMenu primaryAxisMenu( 33 | 1, "PrimaryAxisMoves", "Move Primary Axes", primaryAxisMenuItems, sizeof(primaryAxisMenuItems) / sizeof(primaryAxisMenuItems[0])); 34 | 35 | TestMenuItem secondaryAxisMenuItems[] = { 36 | TestMenuItem(MENU_SECONDARY_RATE_1), 37 | TestMenuItem(MENU_SECONDARY_RATE_2), 38 | TestMenuItem(MENU_SECONDARY_RATE_3), 39 | TestMenuItem(MENU_SECONDARY_RATE_4), 40 | TestMenuItem(MENU_SECONDARY_RATE_5), 41 | #if ALT_STEPPER_TYPE != STEPPER_TYPE_NONE 42 | TestMenuItem(MENU_SECONDARY_ALT_UP), 43 | TestMenuItem(MENU_SECONDARY_ALT_DOWN), 44 | #endif 45 | #if AZ_STEPPER_TYPE != STEPPER_TYPE_NONE 46 | TestMenuItem(MENU_SECONDARY_AZ_LEFT), 47 | TestMenuItem(MENU_SECONDARY_AZ_RIGHT), 48 | #endif 49 | }; 50 | TestMenu secondaryAxisMenu(1, 51 | "SecondaryAxisMoves", 52 | "Move Secondary Axes", 53 | secondaryAxisMenuItems, 54 | sizeof(secondaryAxisMenuItems) / sizeof(secondaryAxisMenuItems[0])); 55 | 56 | TestMenuItem menuItems[] = { 57 | TestMenuItem(MENU_MAIN_LIST_HARDWARE), 58 | #if RA_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART || DEC_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART \ 59 | || AZ_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART || ALT_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART \ 60 | || FOCUS_DRIVER_TYPE == DRIVER_TYPE_TMC2209_UART 61 | TestMenuItem(MENU_MAIN_CONNECT_DRIVERS, &connectDriversMenu), 62 | #endif 63 | TestMenuItem(MENU_MAIN_PRIMARY_AXIS_MOVES, &primaryAxisMenu), 64 | #if (AZ_STEPPER_TYPE != STEPPER_TYPE_NONE) || (ALT_STEPPER_TYPE != STEPPER_TYPE_NONE) 65 | TestMenuItem(MENU_MAIN_SECONDARY_AXIS_MOVES, &secondaryAxisMenu), 66 | #endif 67 | TestMenuItem(MENU_PASSTHROUGH_COMMAND), 68 | TestMenuItem(MENU_FACTORY_RESET), 69 | }; 70 | 71 | TestMenu mainTestMenu(0, "OAT/OAM Testing menu", "", menuItems, sizeof(menuItems) / sizeof(menuItems[0])); 72 | #endif -------------------------------------------------------------------------------- /unit_tests/test_common/test_MappedDict.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "MappedDict.hpp" 4 | 5 | void test_function_mapped_dict_simple_lookups(void) 6 | { 7 | MappedDict::DictEntry_t lookupTable[] = { 8 | {'0', 0}, 9 | {'1', 1}, 10 | {'2', 2}, 11 | }; 12 | auto idxLookup = MappedDict(lookupTable, 3); 13 | int intIdx; 14 | (void) idxLookup.tryGet('0', &intIdx); 15 | TEST_ASSERT_EQUAL(0, intIdx); 16 | (void) idxLookup.tryGet('1', &intIdx); 17 | TEST_ASSERT_EQUAL(1, intIdx); 18 | (void) idxLookup.tryGet('2', &intIdx); 19 | TEST_ASSERT_EQUAL(2, intIdx); 20 | } 21 | 22 | void test_function_mapped_dict_bounds(void) 23 | { 24 | MappedDict::DictEntry_t lookupTable[] = { 25 | {'0', 0}, 26 | {'1', 1}, 27 | }; 28 | auto idxLookup = MappedDict(lookupTable, 2); 29 | int intIdx; 30 | bool found = idxLookup.tryGet('0', &intIdx); 31 | TEST_ASSERT_EQUAL(0, intIdx); 32 | TEST_ASSERT_TRUE(found); 33 | found = idxLookup.tryGet('2', &intIdx); 34 | TEST_ASSERT_NOT_EQUAL(2, intIdx); 35 | TEST_ASSERT_FALSE(found); 36 | } 37 | 38 | void test_function_mapped_dict_zero_size(void) 39 | { 40 | MappedDict::DictEntry_t lookupTable[] = {}; 41 | 42 | auto idxLookup = MappedDict(lookupTable, 0); 43 | int intIdx; 44 | bool found = idxLookup.tryGet('0', &intIdx); 45 | TEST_ASSERT_FALSE(found); 46 | } 47 | 48 | void test_function_mapped_dict_pointer_types(void) 49 | { 50 | MappedDict::DictEntry_t lookupTable[] = { 51 | {"One", 1}, 52 | {"Two", 2}, 53 | }; 54 | auto idxLookup = MappedDict(lookupTable, 2); 55 | int intIdx; 56 | bool found = idxLookup.tryGet("One", &intIdx); 57 | TEST_ASSERT_TRUE(found); 58 | TEST_ASSERT_EQUAL(1, intIdx); 59 | found = idxLookup.tryGet("Two", &intIdx); 60 | TEST_ASSERT_TRUE(found); 61 | TEST_ASSERT_EQUAL(2, intIdx); 62 | } 63 | 64 | void process() 65 | { 66 | UNITY_BEGIN(); 67 | RUN_TEST(test_function_mapped_dict_simple_lookups); 68 | RUN_TEST(test_function_mapped_dict_bounds); 69 | RUN_TEST(test_function_mapped_dict_zero_size); 70 | RUN_TEST(test_function_mapped_dict_pointer_types); 71 | UNITY_END(); 72 | } 73 | 74 | #if defined(ARDUINO) 75 | #include 76 | void setup() 77 | { 78 | delay(2000); // Just if board doesn't support software reset via Serial.DTR/RTS 79 | process(); 80 | } 81 | 82 | void loop() 83 | { 84 | digitalWrite(13, HIGH); 85 | delay(100); 86 | digitalWrite(13, LOW); 87 | delay(500); 88 | } 89 | #else 90 | int main(int argc, char **argv) 91 | { 92 | process(); 93 | return 0; 94 | } 95 | #endif 96 | -------------------------------------------------------------------------------- /unit_tests/test_embedded/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "test_sidereal.h" 4 | 5 | // void setup() 6 | // { 7 | 8 | // } 9 | 10 | // void loop() 11 | // { 12 | 13 | // } 14 | 15 | int main(int argc, char **argv) 16 | { 17 | UNITY_BEGIN(); 18 | 19 | test::sidereal::run(); 20 | 21 | UNITY_END(); 22 | 23 | return 0; 24 | } -------------------------------------------------------------------------------- /unit_tests/test_embedded/test_sidereal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "unity.h" 4 | #include "Sidereal.hpp" 5 | 6 | namespace test 7 | { 8 | namespace sidereal 9 | { 10 | void test_calculate_from_lst() 11 | { 12 | DayTime ha_zero = Sidereal::calculateHa(0.0); 13 | TEST_ASSERT_EQUAL_INT16(2, ha_zero.getHours()); 14 | TEST_ASSERT_EQUAL_INT16(47, ha_zero.getMinutes()); 15 | TEST_ASSERT_EQUAL_INT16(44, ha_zero.getSeconds()); 16 | } 17 | 18 | void run() 19 | { 20 | RUN_TEST(test_calculate_from_lst); 21 | } 22 | } // namespace sidereal 23 | } // namespace test -------------------------------------------------------------------------------- /version_check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import argparse 5 | import fnmatch 6 | from typing import Optional, Dict 7 | 8 | import mistune 9 | import CppHeaderParser 10 | import semver 11 | import git 12 | 13 | parser = argparse.ArgumentParser(description='''Check that the versioning of the repo has been correctly changed. 14 | Intended to run in a PR CI''') 15 | parser.add_argument('branchspec', default='origin/develop', nargs='?', 16 | help='Git branch specification for previous file versions (default: %(default)s)') 17 | 18 | 19 | def die(*args, error_code=1, **kwargs): 20 | print('ERROR: ', end='', file=sys.stderr) 21 | print(*args, **kwargs, file=sys.stderr) 22 | exit(error_code) 23 | 24 | 25 | class SemVerWithVPrefix(semver.VersionInfo): 26 | @classmethod 27 | def parse(cls, version): 28 | if version[0] != 'V': 29 | raise ValueError(f"{version}: not a valid semantic version tag. Must start with 'v' or 'V'") 30 | self = super(SemVerWithVPrefix, cls).parse(version[1:]) 31 | return self 32 | 33 | def __str__(self): 34 | return 'V' + super(SemVerWithVPrefix, self).__str__() 35 | 36 | 37 | def only_changed_non_fw_files(refspec: str) -> bool: 38 | print('Checking if the files changed impact the firmware...') 39 | repo = git.Repo('.') 40 | index = repo.index 41 | origin_develop_commit = repo.commit(refspec) 42 | 43 | # index vs working copy (unstaged files) 44 | changed_but_not_staged = [item.a_path for item in index.diff(None)] 45 | # index vs current HEAD tree (staged files) 46 | changed_and_staged = [item.a_path for item in index.diff('HEAD')] 47 | # origin vs current HEAD tree (files changed in the branch) 48 | branch_files_changed = [item.a_path for item in origin_develop_commit.diff('HEAD')] 49 | 50 | print(f'Unstaged changes: {changed_but_not_staged}') 51 | print(f'Staged changes: {changed_and_staged}') 52 | print(f'Changed in branch: {branch_files_changed}') 53 | 54 | all_changed_filenames = changed_but_not_staged + changed_and_staged + branch_files_changed 55 | print(f'Total changed files: {all_changed_filenames}') 56 | 57 | non_fw_globs = [ 58 | # non-build Python scripts (pre/post Platformio scripts still affect the build process) 59 | 'matrix_build*.py', 'version_check.py', 'scripts/MeadeCommandParser.py', 60 | # prose text files 61 | '*.md', 'LICENSE', '.mailmap', 62 | # build system 63 | '.github/*.yml', 'requirements_*.txt', 64 | # misc tooling files 65 | '.gitignore', '.git-blame-ignore-revs', 66 | ] 67 | for changed_filename in all_changed_filenames: 68 | if not any(fnmatch.fnmatch(changed_filename, non_fw_glob) for non_fw_glob in non_fw_globs): 69 | # If the changed file isn't a non-FW file (==> is a FW file) 70 | # then we need to do the full version check 71 | print(f"Changed file {changed_filename} will affect firmware ({non_fw_globs})") 72 | return False 73 | return True 74 | 75 | 76 | def get_header_defines(header_contents: str) -> Dict[str, str]: 77 | defines_detail = CppHeaderParser.CppHeader(header_contents, argType='string').defines_detail 78 | return dict(d['value'].split(' ', maxsplit=1) for d in defines_detail) 79 | 80 | 81 | def find_child_text(node: dict) -> Optional[str]: 82 | if 'children' in node.keys(): 83 | if node['children'] and isinstance(node['children'], list): 84 | return find_child_text(node['children'][0]) 85 | elif 'text' in node.keys(): 86 | return node['text'] 87 | return None 88 | 89 | 90 | def get_header_version(version_header_contents: str) -> semver.VersionInfo: 91 | version_defines = get_header_defines(version_header_contents) 92 | try: 93 | version_str = next(v.strip('"') for k, v in version_defines.items() if 'version' in k.lower()) 94 | except StopIteration: 95 | die(f'Could not find version define in\n{version_header_contents}: {version_defines}') 96 | try: 97 | version = SemVerWithVPrefix.parse(version_str) 98 | except ValueError as e: 99 | die(f'SemVer error: {e}') 100 | 101 | print(f'Version is {version}') 102 | return version 103 | 104 | 105 | def get_changelog_version(changelog_markdown_path: str) -> str: 106 | markdown = mistune.create_markdown(renderer=mistune.AstRenderer()) 107 | with open(changelog_markdown_path, 'r') as f: 108 | changelog_lines = f.read() 109 | changelog_ast = markdown(changelog_lines) 110 | 111 | first_heading_has_list = (changelog_ast[1]['type'] == 'list') 112 | if not first_heading_has_list: 113 | die(f'Latest changelog entry does not include a _list_ of what has changed') 114 | 115 | first_heading_str = find_child_text(changelog_ast[0]) 116 | if not first_heading_str: 117 | die(f'Could not find the latest changelog heading: {changelog_ast[0]}') 118 | print(f'Latest changelog heading: {first_heading_str}') 119 | 120 | return first_heading_str 121 | 122 | 123 | def get_previous_header_version(version_header_path: str, refspec: str) -> semver.VersionInfo: 124 | repo = git.Repo('.') 125 | previous_header_contents = repo.git.show(f'{refspec}:{version_header_path}') 126 | print(f'Checking previous header version from {refspec}:{version_header_path}') 127 | previous_version = get_header_version(previous_header_contents) 128 | print(f'Read previous header version: {previous_version}') 129 | return previous_version 130 | 131 | 132 | def main(): 133 | if only_changed_non_fw_files(args.branchspec): 134 | print('No FW files changed, not checking version.') 135 | return 136 | version_header_path = './Version.h' 137 | # Get the current version from the header 138 | print(f'Checking current header version from {version_header_path}') 139 | with open(version_header_path, 'r') as fp: 140 | version_header_contents = fp.read() 141 | header_version = get_header_version(version_header_contents) 142 | 143 | # Get the previous version (using git) 144 | previous_header_version = get_previous_header_version(version_header_path, args.branchspec) 145 | 146 | # Check that the version has been increased 147 | if header_version <= previous_header_version: 148 | die(f'Header version must be greater than the previous version {header_version} <= {previous_header_version}') 149 | 150 | changelog_markdown_path = './Changelog.md' 151 | # Get a string from the changelog, should have a version in the latest entry 152 | changelog_version = get_changelog_version(changelog_markdown_path) 153 | 154 | # Check that the header and the changelog match 155 | if str(header_version).lower() not in changelog_version.lower(): 156 | die(f'Header version ("{header_version}" in {version_header_path}) does \ 157 | not match changelog version ("{changelog_version}" in {changelog_markdown_path})') 158 | 159 | 160 | if __name__ == '__main__': 161 | args = parser.parse_args() 162 | main() 163 | print('Finished version check!') 164 | --------------------------------------------------------------------------------