diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ecb3b9fa..0e6b50c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,8 @@ jobs: - mksgenlv1 - esp32 - ramps + - rp2040_jackw01 + - rp2040_skr_pico steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup-pio diff --git a/.gitignore b/.gitignore index 84f88302..b0686c2e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,10 +10,9 @@ CMakeListsPrivate.txt build_cache Configuration_local* -MeadeToWikiOutput.txt - !Configuration_local_examples/ !Configuration_local_examples/* +MeadeToWikiOutput.txt # macOS **/.DS_Store diff --git a/Configuration.hpp b/Configuration.hpp index 353208bd..b348dd68 100644 --- a/Configuration.hpp +++ b/Configuration.hpp @@ -64,10 +64,24 @@ #define INFO_DISPLAY_I2C_ADDRESS 0x3C #endif #ifndef INFO_DISPLAY_I2C_SDA_PIN - #define INFO_DISPLAY_I2C_SDA_PIN 5 + #if (BOARD == BOARD_RP2040_SKR_PICO) + // The SKR Pico has exactly one header-accessible I2C-capable GPIO pair: + // GP0/GP1 on the left-side header (shared with the default GPS wiring). + #define INFO_DISPLAY_I2C_SDA_PIN 0 + #elif (BOARD == BOARD_RP2040_JACKW01) + #define INFO_DISPLAY_I2C_SDA_PIN 20 + #else + #define INFO_DISPLAY_I2C_SDA_PIN 5 + #endif #endif #ifndef INFO_DISPLAY_I2C_SCL_PIN - #define INFO_DISPLAY_I2C_SCL_PIN 4 + #if (BOARD == BOARD_RP2040_SKR_PICO) + #define INFO_DISPLAY_I2C_SCL_PIN 1 + #elif (BOARD == BOARD_RP2040_JACKW01) + #define INFO_DISPLAY_I2C_SCL_PIN 21 + #else + #define INFO_DISPLAY_I2C_SCL_PIN 4 + #endif #endif #ifndef INFO_DISPLAY_UPSIDE_DOWN #define INFO_DISPLAY_UPSIDE_DOWN 0 @@ -164,7 +178,12 @@ * See Constants.hpp for predefined SERIAL_BAUDRATE options, or customize as required. */ #ifndef SERIAL_BAUDRATE - #define SERIAL_BAUDRATE SERIAL_BAUDRATE_ASCOM + #if defined(ARDUINO_ARCH_RP2040) + // The 19200 ASCOM default exists because the 8-bit AVR platforms cannot keep up. + #define SERIAL_BAUDRATE SERIAL_BAUDRATE_FAST + #else + #define SERIAL_BAUDRATE SERIAL_BAUDRATE_ASCOM + #endif #endif /** diff --git a/ConfigurationValidation.hpp b/ConfigurationValidation.hpp index 190d306e..386bb1cf 100644 --- a/ConfigurationValidation.hpp +++ b/ConfigurationValidation.hpp @@ -174,6 +174,17 @@ #error Unsupported GPS configuration. Use at own risk. #endif +// RP2040 SKR Pico: info display pin sharing. +#if defined(ARDUINO_ARCH_RP2040) && (BOARD == BOARD_RP2040_SKR_PICO) && (INFO_DISPLAY_TYPE != INFO_DISPLAY_TYPE_NONE) + #if (USE_GPS == 1) + #error On the SKR Pico the info display and GPS share GP0/GP1 on the left-side header. Enable only one, or remap the pins explicitly. + #endif + #if (INFO_DISPLAY_I2C_SDA_PIN == 20) || (INFO_DISPLAY_I2C_SCL_PIN == 21) || (INFO_DISPLAY_I2C_SDA_PIN == 21) \ + || (INFO_DISPLAY_I2C_SCL_PIN == 20) + #error GP20/GP21 on the SKR Pico only drive on-board MOSFETs (HB/FAN/Laser power outputs); they are not exposed as GPIO on any connector and cannot be used for I2C. Use GP0/GP1 on the left-side header. + #endif +#endif + #if (USE_GYRO_LEVEL == 0) // Baseline configuration without gyro is valid #elif defined(ESP32) || defined(__AVR_ATmega2560__) @@ -240,12 +251,6 @@ // Required pin assignments missing (ATmega uses SoftwareSerial for this driver) #error Missing pin assignments for configured AZ DRIVER_TYPE_TMC2209_UART driver #endif - #if !defined(AZ_DIAG_PIN) - // AZ_DIAG_PIN (TMC2209 DIAG output) is used only for stallGuard-based homing. - // It is optional: omit it if your hardware does not wire the DIAG output to a GPIO. - // Without it, stallGuard homing is unavailable but all other AZ motion works normally. - #pragma message "AZ_DIAG_PIN not defined. StallGuard homing unavailable for AZ axis." - #endif #if !((defined(AZ_SERIAL_PORT_TX) && defined(AZ_SERIAL_PORT_RX)) || defined(AZ_SERIAL_PORT)) // Required pin assignments missing for UART serial #error Missing pin assignments for configured AZ DRIVER_TYPE_TMC2209_UART driver serial connection @@ -264,12 +269,6 @@ // Required pin assignments missing (ATmega uses SoftwareSerial for this driver) #error Missing pin assignments for configured ALT DRIVER_TYPE_TMC2209_UART driver #endif - #if !defined(ALT_DIAG_PIN) - // ALT_DIAG_PIN (TMC2209 DIAG output) is used only for stallGuard-based homing. - // It is optional: omit it if your hardware does not wire the DIAG output to a GPIO. - // Without it, stallGuard homing is unavailable but all other ALT motion works normally. - #pragma message "ALT_DIAG_PIN not defined. StallGuard homing unavailable for ALT axis." - #endif #if !((defined(ALT_SERIAL_PORT_TX) && defined(ALT_SERIAL_PORT_RX)) || defined(ALT_SERIAL_PORT)) // Required pin assignments missing for UART serial #error Missing pin assignments for configured ALT DRIVER_TYPE_TMC2209_UART driver serial connection diff --git a/Configuration_adv.hpp b/Configuration_adv.hpp index 48d87f7f..07b480b0 100644 --- a/Configuration_adv.hpp +++ b/Configuration_adv.hpp @@ -35,6 +35,19 @@ // //////// // MOTOR & DRIVER SETTINGS //////// // //////// +////////////////////////////////////// +// +// Interval at which the stepper control timer/interrupt services the steppers. +// Shorter intervals mean finer step timing. AVR is capped at 2 kHz: higher rates +// there interfere with serial communications. The RP2040 has ample headroom. +#ifndef STEPPER_CONTROL_INTERVAL_MS + #if defined(ARDUINO_ARCH_RP2040) + #define STEPPER_CONTROL_INTERVAL_MS 0.2f // 5 kHz + #else + #define STEPPER_CONTROL_INTERVAL_MS 0.5f // 2 kHz + #endif +#endif + ////////////////////////////////////// // // This is how many steps your stepper needs for a full rotation. @@ -556,16 +569,15 @@ // Enable RA End Switches in your local configuration. Do not edit here! #ifndef USE_RA_END_SWITCH #define USE_RA_END_SWITCH 0 -#else - #ifndef RA_END_SWITCH_ACTIVE_STATE - #define RA_END_SWITCH_ACTIVE_STATE LOW - #endif - // You can define how many degrees to slew back after the end switch has triggered. - // Mechanical end switches might have a hysteresis behavior, meaning once signaled, - // it needs to move well back beyond the signal point to become un-signaled. - #ifndef RA_ENDSWITCH_BACKSLEW_DEG - #define RA_ENDSWITCH_BACKSLEW_DEG 0.5 - #endif +#endif +#ifndef RA_END_SWITCH_ACTIVE_STATE + #define RA_END_SWITCH_ACTIVE_STATE LOW +#endif +// You can define how many degrees to slew back after the end switch has triggered. +// Mechanical end switches might have a hysteresis behavior, meaning once signaled, +// it needs to move well back beyond the signal point to become un-signaled. +#ifndef RA_ENDSWITCH_BACKSLEW_DEG + #define RA_ENDSWITCH_BACKSLEW_DEG 0.5 #endif ////////////////////////////////////////// @@ -574,17 +586,15 @@ // Enable DEC End Switches in your local configuration. Do not edit here! #ifndef USE_DEC_END_SWITCH #define USE_DEC_END_SWITCH 0 -#else - #ifndef DEC_END_SWITCH_ACTIVE_STATE - #define DEC_END_SWITCH_ACTIVE_STATE LOW - #endif - // You can define how many degrees to slew back after the end switch has triggered. - // Mechanical end switches might have a hysteresis behavior, meaning once signaled, - // it needs to move well back beyond the signal point to become un-signaled. - #ifndef DEC_ENDSWITCH_BACKSLEW_DEG - #define DEC_ENDSWITCH_BACKSLEW_DEG 0.5 - #endif - +#endif +#ifndef DEC_END_SWITCH_ACTIVE_STATE + #define DEC_END_SWITCH_ACTIVE_STATE LOW +#endif +// You can define how many degrees to slew back after the end switch has triggered. +// Mechanical end switches might have a hysteresis behavior, meaning once signaled, +// it needs to move well back beyond the signal point to become un-signaled. +#ifndef DEC_ENDSWITCH_BACKSLEW_DEG + #define DEC_ENDSWITCH_BACKSLEW_DEG 0.5 #endif ////////////////////////////////////////// diff --git a/Configuration_local_examples/Configuration_local_rp2040_skr_pico.hpp b/Configuration_local_examples/Configuration_local_rp2040_skr_pico.hpp index a757cfe5..2fbe97ac 100644 --- a/Configuration_local_examples/Configuration_local_rp2040_skr_pico.hpp +++ b/Configuration_local_examples/Configuration_local_rp2040_skr_pico.hpp @@ -16,7 +16,7 @@ * Stepper slot : E0 -> ALT (TMC2209, UART addr 3) * Motor rating : 0.9 deg / step (400 SPR), 470 mA * Pulley : 16T aluminium GT2 - * RA homing : Hall sensor on GPIO4 (X-STOP / RA_DIAG_PIN) + * RA homing : Hall sensor on GPIO4 (X-STOP) * * All pin assignments are in boards/RP2040_SKR_PICO/pins_RP2040_SKR_PICO.hpp. * R_SENSE (0.11 ohm) is set globally in src/a_inits.hpp for all TMC2209_UART @@ -124,15 +124,25 @@ #endif // ── RA Hall sensor auto-home ────────────────────────────────────────────────── -// Hall sensor is wired to GPIO4 (X-STOP input / RA_DIAG_PIN on SKR Pico). +// Hall sensor is wired to GPIO4 (X-STOP input on SKR Pico). // Active state LOW: sensor pulls GPIO4 LOW when the magnet is present. // Search distance: 30° default (RA_HOMING_SENSOR_SEARCH_DEGREES in Configuration_adv.hpp). // GPIO4 is configured INPUT (no pull-up); add external 10kΩ to 3.3V if sensor // is open-drain, or change to INPUT_PULLUP in b_setup.hpp. #define USE_HALL_SENSOR_RA_AUTOHOME 1 -#define RA_HOMING_SENSOR_PIN 4 // GPIO4 = RA_DIAG_PIN (X-STOP) +#define RA_HOMING_SENSOR_PIN 4 // GPIO4 (X-STOP) #define RA_HOMING_SENSOR_ACTIVE_STATE LOW // sensor pulls LOW when active +// ── Info display (alternative to GPS) ───────────────────────────────────────── +// The SKR Pico has exactly one header-accessible I2C-capable GPIO pair: GP0/GP1 +// on the left-side header (IO0/IO1/GND/5V). This example uses it for GPS below. +// For an SSD1306 128x64 info display instead, set USE_GPS to 0 and enable: +// #define INFO_DISPLAY_TYPE INFO_DISPLAY_TYPE_I2C_SSD1306_128x64 +// (SDA/SCL then default to GP0/GP1: SDA->IO0, SCL->IO1, VCC->5V, GND->GND.) +// NOTE: GP20/GP21 must NOT be used for the display on this board - they only +// drive the HB/FAN/Laser MOSFET power outputs and never reach a connector as +// raw GPIO (wiring I2C there hangs the bus). Enforced by ConfigurationValidation. + // ── GPS ─────────────────────────────────────────────────────────────────────── // ATGM336H-5N wired to left-side header: GPS TX→IO1, GPS RX→IO0, VCC→5V, GND. // GPS_SERIAL_PORT, RP2040_GPS_TX_PIN, RP2040_GPS_RX_PIN defined in pins file. diff --git a/Constants.hpp b/Constants.hpp index 56ccb6ce..f0ef8b40 100644 --- a/Constants.hpp +++ b/Constants.hpp @@ -21,7 +21,9 @@ #define BOARD_ESP32_ESP32DEV 1001 #define BOARD_OAE_V1 1002 -// RP2040 based boards +// RP2040 based boards (2000-2999) +// 2101-2199: Pico family + JackW01 printer carrier board +// 2201-2299: BTT SKR Pico family #define BOARD_RP2040_JACKW01 2101 #define BOARD_RP2040_SKR_PICO 2201 @@ -67,6 +69,7 @@ // USB serial port speed according to external controller #define SERIAL_BAUDRATE_ASCOM 19200 +#define SERIAL_BAUDRATE_FAST 115200 // For platforms that keep up (RP2040 USB CDC) // Wifi operating modes (ESP32 only) #define WIFI_MODE_INFRASTRUCTURE 0 // Infrastructure Only - OAT connects to an existing Router diff --git a/Example_Configuration_local_rp2040_skr_pico.hpp b/Example_Configuration_local_rp2040_skr_pico.hpp new file mode 100644 index 00000000..edd4f73d --- /dev/null +++ b/Example_Configuration_local_rp2040_skr_pico.hpp @@ -0,0 +1,143 @@ +/** + * @brief Bench configuration for the BTT SKR Pico v1.0 RP2040 board. + * + * Personal local config (gitignored). Based on + * Configuration_local_examples/Configuration_local_rp2040_skr_pico.hpp, + * adapted for the bench hardware: + * - Hall homing sensors on RA (X-STOP GPIO4) AND DEC (Y-STOP GPIO3) + * - DEC endstops on Z-STOP (GPIO25, down) and E-STOP (GPIO16, up) + * - SSD1306 info display on I2C0, GP0/GP1 on the left-side header + * - GPS disabled for the bench + * + * All other pin assignments are in boards/RP2040_SKR_PICO/pins_RP2040_SKR_PICO.hpp. + */ +#pragma once + +// ── Board ───────────────────────────────────────────────────────────────────── +#define BOARD BOARD_RP2040_SKR_PICO +#define OAM +#define TEST_VERIFY_MODE 0 + +// ── Hemisphere ──────────────────────────────────────────────────────────────── +#define NORTHERN_HEMISPHERE 1 + +// ── Display ──────────────────────────────────────────────────────────────────── +// No LCD menu display (RP2040 phase 1); SSD1306 128x64 info display on I2C0. +// GP0/GP1 on the left-side header (IO0/IO1/GND/5V) - the only header-accessible +// I2C0 pair on the SKR Pico. Shares pins with the default GPS wiring, so +// USE_GPS must stay 0 while the display is enabled. +// (GP20/GP21 are MOSFET-gate-only nets on this board - HB/FAN/Laser outputs, +// never exposed as raw GPIO; wiring I2C there hangs the bus and the firmware.) +#define DISPLAY_TYPE DISPLAY_TYPE_NONE +#define INFO_DISPLAY_TYPE INFO_DISPLAY_TYPE_I2C_SSD1306_128x64 +#define INFO_DISPLAY_I2C_ADDRESS 0x3C +#define INFO_DISPLAY_I2C_SDA_PIN 0 // GP0 (I2C0 SDA, left header IO0) +#define INFO_DISPLAY_I2C_SCL_PIN 1 // GP1 (I2C0 SCL, left header IO1) +#define INFO_DISPLAY_UPSIDE_DOWN 1 +#define INFO_DISPLAY_MIRRORED 0 + +// ── Stepper control interval ───────────────────────────────────────────────── +// 5 kHz is the RP2040 default (see Configuration_adv.hpp) and the bench-verified +// rate. The RP2040 can also handle 10 kHz if you need faster step generation +// (set 0.1f), but that is not part of the verified baseline. +#define STEPPER_CONTROL_INTERVAL_MS 0.2f // 5 kHz + +// ── Stepper motor types ─────────────────────────────────────────────────────── +#define RA_STEPPER_TYPE STEPPER_TYPE_ENABLED +#define DEC_STEPPER_TYPE STEPPER_TYPE_ENABLED +#define AZ_STEPPER_TYPE STEPPER_TYPE_ENABLED +#define ALT_STEPPER_TYPE STEPPER_TYPE_ENABLED + +// ── Stepper driver types ────────────────────────────────────────────────────── +#define RA_DRIVER_TYPE DRIVER_TYPE_TMC2209_UART +#define DEC_DRIVER_TYPE DRIVER_TYPE_TMC2209_UART +#define AZ_DRIVER_TYPE DRIVER_TYPE_TMC2209_UART +#define ALT_DRIVER_TYPE DRIVER_TYPE_TMC2209_UART + +// ── RA Settings ─────────────────────────────────────────────────────────────── +#define RA_SLEW_MICROSTEPPING 16 // µsteps used while slewing RA +#define RA_TRACKING_MICROSTEPPING 256 // µsteps used for sidereal tracking +#define RA_STEPPER_SPEED 8000 // steps/s. If SPEED and ACCELERATION numbers are the same, the spool up to full speed takes one second. +#define RA_STEPPER_ACCELERATION 8000 // steps/s/s +#define RA_MOTOR_CURRENT_RATING 1000 // mA +#define RA_OPERATING_CURRENT_SETTING 75 // % +#define RA_WHEEL_CIRCUMFERENCE 1130.97f // 360mm diam +#define RA_PULLEY_TEETH 16 +#define RA_STEPPER_SPR (400 * 9) // change to (200 * 9) for 1.8° stepper +#define RA_LIMIT_LEFT 6.0f +#define RA_LIMIT_RIGHT 6.0f +#define RA_INVERT_DIR 1 + +// ── DEC settings ──────────────────────────────────────────────────────────── +#define DEC_SLEW_MICROSTEPPING 16 // µsteps used while slewing DEC +#define DEC_GUIDE_MICROSTEPPING 256 // µsteps used for guide pulses +#define DEC_STEPPER_SPEED 8000 // steps/s. If SPEED and ACCELERATION numbers are the same, the spool up to full speed takes one second. +#define DEC_STEPPER_ACCELERATION 8000 // steps/s/s +#define DEC_MOTOR_CURRENT_RATING 1000 // mA +#define DEC_OPERATING_CURRENT_SETTING 75 // % +#define DEC_WHEEL_CIRCUMFERENCE 816.814f +#define DEC_PULLEY_TEETH 16 +#define DEC_STEPPER_SPR (400 * 9) // change to (200 * 9) for 1.8° stepper +#define DEC_LIMIT_UP 130 +#define DEC_LIMIT_DOWN 130 +#define DEC_INVERT_DIR 1 + +// ── StealthChop (silent mode) ───────────────────────────────────────────────── +#define RA_UART_STEALTH_MODE 1 +#define DEC_UART_STEALTH_MODE 1 + +// ── AZIMUTH definitions ────────────────────────────────────────────────────── +#define AZ_MICROSTEPPING 16 +#define AZ_STEPPER_SPEED 2500 +#define AZ_STEPPER_ACCELERATION 1250 +#define AZ_MOTOR_CURRENT_RATING 2000 // mA +#define AZ_OPERATING_CURRENT_SETTING 80 // % +#define AZ_STEPPER_SPR 400 +#define AZ_ALWAYS_ON 1 +#define AZ_MOTOR_HOLD_SETTING 15 +#define AZ_PULLEY_TEETH 20 +#define AZ_ROD_PITCH 1.0f // mm per full rev of stepper +#define AZ_CIRCUMFERENCE (115 * 2 * 3.1415927) +#define AZIMUTH_STEPS_PER_REV (AZ_CIRCUMFERENCE / AZ_ROD_PITCH * AZ_STEPPER_SPR * AZ_MICROSTEPPING) // Steps needed to turn AZ 360deg +#define AZ_INVERT_DIR 1 + +// ── ALTITUDE definitions ───────────────────────────────────────────────────── +#define ALT_MICROSTEPPING 32 +#define ALT_STEPPER_SPEED 2500 +#define ALT_STEPPER_ACCELERATION 1250 +#define ALT_MOTOR_CURRENT_RATING 1000 // mA +#define ALT_OPERATING_CURRENT_SETTING 70 // % +#define ALT_STEPPER_SPR 400 +#define ALT_INVERT_DIR 0 +#define ALT_ALWAYS_ON 0 + +// ── Hall sensor auto-homing (RA and DEC) ────────────────────────────────────── +// RA sensor on X-STOP (GPIO4 = RA_DIAG_PIN), DEC sensor on Y-STOP (GPIO3 = DEC_DIAG_PIN). +// Active state LOW: sensor pulls the GPIO LOW when the magnet is present. +#define USE_HALL_SENSOR_RA_AUTOHOME 0 +#define RA_HOMING_SENSOR_PIN 4 // GPIO4 = X-STOP +#define RA_HOMING_SENSOR_ACTIVE_STATE LOW + +#define USE_HALL_SENSOR_DEC_AUTOHOME 0 +#define DEC_HOMING_SENSOR_PIN 3 // GPIO3 = Y-STOP +#define DEC_HOMING_SENSOR_ACTIVE_STATE LOW + +// ── DEC endstops ────────────────────────────────────────────────────────────── +// Wired to the two remaining endstop inputs on the SKR Pico. +#define USE_DEC_END_SWITCH 0 +#define DEC_ENDSWITCH_DOWN_SENSOR_PIN 25 // GPIO25 = Z-STOP +#define DEC_ENDSWITCH_UP_SENSOR_PIN 16 // GPIO16 = E-STOP +#define DEC_END_SWITCH_ACTIVE_STATE LOW + +// ── GPS ─────────────────────────────────────────────────────────────────────── +// Disabled for the bench (no module connected). NOTE: GPS shares GP0/GP1 with +// the info display on this board - only one of the two can be enabled. +#define USE_GPS 0 + +// ── FAN (disabled for now)─────────────────────────────────────────────────────────────────────── +// #define RP2040_FAN1_POWER_PERCENT 60 + +// Track immediately after boot +#define TRACK_ON_BOOT 0 + +#define DEBUG_LEVEL (DEBUG_NONE) diff --git a/docs/RP2040-WIRING.md b/docs/RP2040-WIRING.md new file mode 100644 index 00000000..f9c0ea23 --- /dev/null +++ b/docs/RP2040-WIRING.md @@ -0,0 +1,176 @@ +# RP2040 Wiring Guide + +Pin assignments for the two RP2040 boards supported by the OpenAstroTracker +firmware: + +| Board | Board ID | Pins file | +|---|---|---| +| BigTreeTech **SKR Pico v1.0** | `BOARD_RP2040_SKR_PICO` (2201) | `boards/RP2040_SKR_PICO/pins_RP2040_SKR_PICO.hpp` | +| **JackW01** (pi-pico-printer-board carrier) | `BOARD_RP2040_JACKW01` (2101) | `boards/RP2040_JACKW01/pins_RP2040_JACKW01.hpp` | + +Everything below reflects the **default** pin mapping compiled into the +firmware. Peripherals are enabled in your `Configuration_local_*.hpp` (see +`Configuration_local_examples/Configuration_local_rp2040_skr_pico.hpp` for a +complete annotated example). GPIO numbers refer to the `GPxx` labels on the +board, not to physical pin positions. + +All GPIO signals are **3.3 V logic**. + +--- + +## Power (SKR Pico) + +The board can be powered from USB, from the VIN terminals (12-24 V), or both - +but **never leave the USB-power jumper on while VIN is applied**. The jumper +ties the 5 V rail to USB VBUS; with a supply also feeding the rail, the +contention can hang the board during stepper driver initialization. Use: + +- **Jumper removed + VIN (12-24 V)** - normal operation. Motors and drivers are + powered, and the USB connector still works for flashing and serial (its data + lines are independent of VBUS). +- **USB only (jumper fitted)** - the firmware boots and serial works, but the + stepper drivers have no motor power; nothing moves. + +## Stepper motors + +Drivers must be `DRIVER_TYPE_TMC2209_UART` (the boards carry no A4988/standalone +wiring - MS pins are not routed). + +### SKR Pico (on-board TMC2209s) + +| Axis | Slot | STEP | DIR | EN | Endstop input | TMC2209 UART address | +|---|---|---|---|---|---|---| +| RA | X | GP11 | GP10 | GP12 | GP4 | 0 | +| DEC | Y | GP6 | GP5 | GP7 | GP3 | 2 | +| AZ | Z | GP19 | GP28 | GP2 | - | 1 | +| ALT | E0 | GP14 | GP13 | GP15 | - | 3 | + +Note the address order X=0, **Y=2, Z=1**, E0=3 - set by the MS1/MS2 strapping +on the SKR Pico PCB and the opposite of the JackW01 board. + +AZ and ALT are optional: set `AZ_DRIVER_TYPE`/`ALT_DRIVER_TYPE` to +`DRIVER_TYPE_NONE` if your mount has no vertical axis motors. + +### JackW01 (TMC2209 modules) + +| Axis | Slot | STEP | DIR | EN | Endstop input | TMC2209 UART address | +|---|---|---|---|---|---|---| +| RA | X | GP7 | GP6 | GP5 | GP4 | 0 | +| DEC | Y | GP11 | GP10 | GP5 | GP3 | 1 | +| AZ | Z | GP13 | GP12 | GP5 | - | 2 | +| ALT | E | GP15 | GP14 | GP5 | - | 3 | + +- **EN is shared**: all four enable lines are tied to GP5, so enable/disable + always affects every axis. Per-axis current control happens over UART + (IRUN/IHOLD) instead. +- Use BTT TMC2209 modules with the PDN_UART pad bridge (the on-board resistor + linking RESET/TX and MS3/RX pads to PDN_UART). The carrier routes UART1 + (GP8 TX, GP9 RX) to the driver sockets through R202/R201. + +## USB serial (control and flashing) + +- The USB port enumerates as a virtual COM port (USB CDC) and carries the + Meade/LX200 protocol used by OATControl, the ASCOM driver, and INDI. No + handshaking lines are required - hosts that open the port without asserting + DTR still receive replies. +- Flashing: the board resets into BOOTSEL mode when a serial port opens at + 1200 baud (the standard arduino-pico upload path), or hold BOOTSEL while + applying power. +- `:XGM#` reports `RP2040` as the hardware type. + +## Info display (SSD1306 128x64 OLED, I2C) + +Config keys (set in your local config; the pins default correctly per board): + +```cpp +#define INFO_DISPLAY_TYPE INFO_DISPLAY_TYPE_I2C_SSD1306_128x64 +#define INFO_DISPLAY_I2C_ADDRESS 0x3C // default +#define INFO_DISPLAY_UPSIDE_DOWN 0 // 1 = rotate 180 degrees +#define INFO_DISPLAY_MIRRORED 0 // 1 = flip horizontally +``` + +- **SKR Pico**: SDA = GP0, SCL = GP1 (I2C0) — the only header-accessible + I2C-capable GPIO pair on this board, and the firmware default when the + display is enabled. Wire SDA→IO0, SCL→IO1, VCC→5V, GND→GND, all on the + left-side header (bench-verified with a common 4-pin SSD1306 module on 5V). + **GP0/GP1 are shared with the default GPS wiring** — enable either the + display or GPS, not both (enforced at compile time). +- **JackW01**: defaults are SDA = GP20, SCL = GP21 (free on that carrier); + override with `INFO_DISPLAY_I2C_SDA_PIN`/`..._SCL_PIN` to use other pins. + +> **Important (SKR Pico):** do **not** use GP20/GP21 for the display on the SKR +> Pico. They only drive on-board MOSFETs (the HB/FAN/Laser power outputs, +> labelled + and − on the board) and are never exposed as raw GPIO on any +> connector. Wiring an I2C display to those pins hangs the bus and the +> firmware (enforced at compile time). + +## GPS (SKR Pico) + +Wired to the left-side 5-pin header (IO0, IO1, GND, 5V) using UART0: + +| GPS module pin | Connect to | RP2040 GPIO | +|---|---|---| +| RX (module input) | IO0 | GP0 (MCU TX - fixed mux, TX only) | +| TX (module output) | IO1 | GP1 (MCU RX - fixed mux, RX only) | +| VCC | 5V | - | +| GND | GND | - | + +Enable with `#define USE_GPS 1`. Tested with an ATGM336H-5N; any GPS module +with a 3.3 V UART output works (the RP2040 GPIO are not 5 V tolerant). +GP0/GP1 are shared with the info display's default pins on this board — +enable either GPS or the display, not both (enforced at compile time). + +The JackW01 board has no default GPS wiring; leave `USE_GPS` at 0. + +## Hall homing sensors (RA and DEC) + +Active-LOW inputs on the endstop headers: + +| Axis | Input | GPIO | Config | +|---|---|---|---| +| RA | X-STOP | GP4 | `USE_HALL_SENSOR_RA_AUTOHOME`, `RA_HOMING_SENSOR_PIN`, `RA_HOMING_SENSOR_ACTIVE_STATE LOW` | +| DEC | Y-STOP | GP3 | `USE_HALL_SENSOR_DEC_AUTOHOME`, `DEC_HOMING_SENSOR_PIN`, `DEC_HOMING_SENSOR_ACTIVE_STATE LOW` | + +The inputs are configured without pull-ups; open-drain Hall sensors need an +external pull-up (10 kOhm to 3.3 V). The search distance defaults to 30 degrees +(`RA_HOMING_SENSOR_SEARCH_DEGREES` etc. in `Configuration_adv.hpp`). + +## DEC endstops (SKR Pico) + +The two remaining endstop inputs can back up Hall homing with hardware limits: + +| Limit | Input | GPIO | Config key | +|---|---|---|---| +| DEC down | Z-STOP | GP25 | `DEC_ENDSWITCH_DOWN_SENSOR_PIN` | +| DEC up | E-STOP | GP16 | `DEC_ENDSWITCH_UP_SENSOR_PIN` | + +Both are active LOW (`DEC_END_SWITCH_ACTIVE_STATE`). RA endstops have no +default wiring on these boards - all four endstop inputs are allocated above. + +## Fan (SKR Pico) + +The FAN1 header is switched by a MOSFET on GP17 and carries the VIN rail +(12/24 V). Set the power percentage in your config: + +```cpp +#define RP2040_FAN1_POWER_PERCENT 60 // 0-100, default 100 +``` + +## Not available by default on RP2040 + +- **StallGuard driver autohome** - not implemented in the OAT firmware on any + platform; use Hall sensor homing instead (the DIAG outputs are unused) +- **LCD menu display** - `DISPLAY_TYPE` must be `DISPLAY_TYPE_NONE` + (enforced at compile time) +- **Gyro auto-leveling** - `USE_GYRO_LEVEL` must be 0 +- **Focuser stepper** - all four driver slots are used by RA/DEC/AZ/ALT +- **Dew heater output** - no pin is wired on these boards +- **WiFi/Bluetooth** - ESP32 builds only +- SKR Pico's NeoPixel (GP24) and BLTouch (GP22/GP29) pins exist on the board + but are unused by the OAT firmware + +--- + +Source of truth for any pin: the pins files linked at the top. If your wiring +needs differ, every pin macro is `#ifndef`-guarded and can be overridden in +your local configuration file. diff --git a/matrix_build.py b/matrix_build.py index dad16c54..56148c09 100644 --- a/matrix_build.py +++ b/matrix_build.py @@ -183,11 +183,25 @@ def update_dict(orig, patch): "rp2040_jackw01": update_dict(BUILD_FLAGS, { "USE_GPS": [0], "USE_GYRO_LEVEL": [0], + # No LCD menu display support on RP2040 (enforced by ConfigurationValidation) "DISPLAY_TYPE": [ "DISPLAY_TYPE_NONE" ], - "INFO_DISPLAY_TYPE": [ - "INFO_DISPLAY_TYPE_NONE" + # Both RP2040 boards carry on-board TMC2209s wired for UART only; + # A4988/standalone driver types need MS pins they do not route. + "RA_DRIVER_TYPE": [ + "DRIVER_TYPE_TMC2209_UART" + ], + "DEC_DRIVER_TYPE": [ + "DRIVER_TYPE_TMC2209_UART" + ], + "AZ_DRIVER_TYPE": [ + "DRIVER_TYPE_NONE", + "DRIVER_TYPE_TMC2209_UART" + ], + "ALT_DRIVER_TYPE": [ + "DRIVER_TYPE_NONE", + "DRIVER_TYPE_TMC2209_UART" ], "FOCUS_DRIVER_TYPE": [ "DRIVER_TYPE_NONE" @@ -197,13 +211,26 @@ def update_dict(orig, patch): ], }), "rp2040_skr_pico": update_dict(BUILD_FLAGS, { - "USE_GPS": [0], "USE_GYRO_LEVEL": [0], + # No LCD menu display support on RP2040 (enforced by ConfigurationValidation) "DISPLAY_TYPE": [ "DISPLAY_TYPE_NONE" ], - "INFO_DISPLAY_TYPE": [ - "INFO_DISPLAY_TYPE_NONE" + # Both RP2040 boards carry on-board TMC2209s wired for UART only; + # A4988/standalone driver types need MS pins they do not route. + "RA_DRIVER_TYPE": [ + "DRIVER_TYPE_TMC2209_UART" + ], + "DEC_DRIVER_TYPE": [ + "DRIVER_TYPE_TMC2209_UART" + ], + "AZ_DRIVER_TYPE": [ + "DRIVER_TYPE_NONE", + "DRIVER_TYPE_TMC2209_UART" + ], + "ALT_DRIVER_TYPE": [ + "DRIVER_TYPE_NONE", + "DRIVER_TYPE_TMC2209_UART" ], "FOCUS_DRIVER_TYPE": [ "DRIVER_TYPE_NONE" @@ -332,14 +359,23 @@ def set_ci_constraints(problem): problem.addConstraint(InSetConstraint({"DISPLAY_TYPE_NONE", "DISPLAY_TYPE_LCD_KEYPAD"}), ["DISPLAY_TYPE"]) # problem.addConstraint(InSetConstraint({"DRIVER_TYPE_ULN2003"}), ["ALT_DRIVER_TYPE"]) - # Restrict INFO_DISPLAY_TYPE_I2C_SSD1306_128x64 to mksgenlv21 and esp32 only + # Restrict INFO_DISPLAY_TYPE_I2C_SSD1306_128x64 to mksgenlv21, esp32 and rp2040_skr_pico only # (just to reduce compile times) def info_display_constraint(board, info_display): if info_display == "INFO_DISPLAY_TYPE_I2C_SSD1306_128x64": - return board in ["mksgenlv21", "esp32"] + return board in ["mksgenlv21", "esp32", "rp2040_skr_pico"] return True problem.addConstraint(info_display_constraint, ["BOARD", "INFO_DISPLAY_TYPE"]) + # SKR Pico: the info display (defaults to GP0/GP1) and GPS share the only + # free header I2C/UART pair; ConfigurationValidation rejects the combo. + def skr_pico_gps_display_constraint(board, gps, info_display): + if board == "rp2040_skr_pico" and gps == 1 and info_display == "INFO_DISPLAY_TYPE_I2C_SSD1306_128x64": + return False + return True + + problem.addConstraint(skr_pico_gps_display_constraint, ["BOARD", "USE_GPS", "INFO_DISPLAY_TYPE"]) + def print_solutions_matrix(solutions, short_strings=False): def get_value(vb, vk): diff --git a/platformio.ini b/platformio.ini index 7b68d651..fd850316 100644 --- a/platformio.ini +++ b/platformio.ini @@ -141,23 +141,19 @@ build_flags = [common_rp2xxx] extends = common_embedded -; Use maxgerhardt platform wrapper to access Earle Philhower arduino-pico core. -; The official platform-raspberrypi uses the Arduino-mbed core which has poorer +; The 19200 default exists for the 8-bit AVR platforms; RP2040 USB CDC keeps up at 115200. +monitor_speed = 115200 +; Use the maxgerhardt platform wrapper to access the Earle Philhower arduino-pico core. +; The official platform-raspberrypi uses the Arduino-mbed core, which has poorer ; library compatibility and no flexible UART pin remapping. platform = https://github.com/maxgerhardt/platform-raspberrypi.git board_build.core = earlephilhower -; SSD1306_128x64_Display.cpp includes SSD1306Wire.h unconditionally at the top of -; the file (not guarded by INFO_DISPLAY_TYPE), so it can't compile without that -; library. RP2040 boards don't support this display (ConfigurationValidation.hpp -; forces INFO_DISPLAY_TYPE_NONE), so exclude the file instead of pulling in an -; otherwise-unused, possibly RP2040-incompatible dependency. -src_filter = - ${common_embedded.src_filter} - - lib_deps = + mikalhart/TinyGPSPlus @ ^1.0.2 teemuatlut/TMCStepper @ ^0.7.1 waspinator/AccelStepper @ ^1.61 - mikalhart/TinyGPSPlus @ ^1.0.2 +; SSD1306 info display support (SSD1306_128x64_Display.cpp is always compiled) + https://github.com/ClutchplateDude/esp8266-oled-ssd1306#4.6.2 ; TimerInterrupt is AVR-only (uses OCR1A etc.); RP2040 uses hardware/timer.h directly lib_ignore = TimerInterrupt @@ -166,7 +162,11 @@ extends = common_rp2xxx board = rpipico build_flags = ${env.build_flags} - -Wno-deprecated-declarations ; arduino-pico core deprecates the B00000000-style Arduino::Binary.h macros used throughout the shared Mount code +; arduino-pico core deprecates the B00000000-style Arduino::Binary.h macros used throughout the shared Mount code + -Wno-deprecated-declarations +; The arduino-pico core headers and the SSD1306 OLED library trigger -Woverloaded-virtual +; (-Werror) when parsed together in the same TU; the warning never fires in OAT code itself. + -Wno-overloaded-virtual -D BOARD=BOARD_RP2040_JACKW01 [env:rp2040_skr_pico] @@ -174,7 +174,11 @@ extends = common_rp2xxx board = rpipico build_flags = ${env.build_flags} - -Wno-deprecated-declarations ; arduino-pico core deprecates the B00000000-style Arduino::Binary.h macros used throughout the shared Mount code +; arduino-pico core deprecates the B00000000-style Arduino::Binary.h macros used throughout the shared Mount code + -Wno-deprecated-declarations +; The arduino-pico core headers and the SSD1306 OLED library trigger -Woverloaded-virtual +; (-Werror) when parsed together in the same TU; the warning never fires in OAT code itself. + -Wno-overloaded-virtual -D BOARD=BOARD_RP2040_SKR_PICO [env:native] diff --git a/src/InfoDisplayRender.hpp b/src/InfoDisplayRender.hpp index 3f9569bd..79287764 100644 --- a/src/InfoDisplayRender.hpp +++ b/src/InfoDisplayRender.hpp @@ -18,6 +18,10 @@ class InfoDisplayRender int _curLine; public: + // Draws the console-mode header (logo and product name). + // Overridden by subclasses; the default draws nothing. + virtual void drawHeader() {}; + InfoDisplayRender() { _consoleMode = true; @@ -56,6 +60,12 @@ class InfoDisplayRender { drawContentFunction(this); } + else if (_consoleMode) + { + // Console-text updates arrive without a content callback; draw the + // header here so the boot console carries the logo on every frame. + drawHeader(); + } if (_consoleMode) { diff --git a/src/InterruptCallback.cpp b/src/InterruptCallback.cpp index 18ab6601..9a28c206 100644 --- a/src/InterruptCallback.cpp +++ b/src/InterruptCallback.cpp @@ -22,8 +22,9 @@ PUSH_NO_WARNINGS POP_NO_WARNINGS #elif defined(ARDUINO_ARCH_RP2040) // Earle Philhower arduino-pico core exposes the Pico SDK alarm API. - // add_repeating_timer_us() fires a hardware alarm callback at a precise - // interval without relying on FreeRTOS or a second core. + // add_repeating_timer_us() fires a hardware-alarm callback at a precise interval in + // interrupt context, like the AVR timers. (The ESP32 port instead steps from a FreeRTOS + // task; this core has no RTOS at all, and no second core needs to be dedicated.) #include #else #error Unrecognized board selected. Either implement interrupt code or define the board here. @@ -75,10 +76,10 @@ bool InterruptCallback::setInterval(float intervalMs, interrupt_callback_p callb _rp2040_callback = callback; _rp2040_payload = payload; - // Positive delay_us means the period is measured from callback-start to - // callback-start (fixed wall-clock rate), which is what the stepper - // controller needs for consistent timing. - int32_t intervalUs = static_cast(intervalMs * 1000.0f); + // Negative delay_us gives a fixed wall-clock period from callback start to + // callback start (matching the AVR TimerInterrupt semantics). A positive + // delay would add the callback's own runtime to every period. + int64_t intervalUs = -static_cast(intervalMs * 1000.0f); return add_repeating_timer_us(intervalUs, rp2040_timer_isr, nullptr, &_rp2040_timer); } @@ -90,8 +91,7 @@ void InterruptCallback::stop() void InterruptCallback::start() { // Re-arm with the same interval. The stored callback/payload are still set. - int32_t intervalUs = static_cast(_rp2040_timer.delay_us); - add_repeating_timer_us(intervalUs, rp2040_timer_isr, nullptr, &_rp2040_timer); + add_repeating_timer_us(_rp2040_timer.delay_us, rp2040_timer_isr, nullptr, &_rp2040_timer); } #endif diff --git a/src/Mount.cpp b/src/Mount.cpp index 67ac5b0a..db551328 100644 --- a/src/Mount.cpp +++ b/src/Mount.cpp @@ -498,9 +498,8 @@ void Mount::configureRAdriver(Stream *serial, float rsense, byte driveraddress, LOG(DEBUG_STEPPERS, "[MOUNT]: Requested RA motor rms_current: %d mA", rmscurrent); _driverRA->rms_current(rmscurrent, 1.0f); //holdMultiplier = 1 to set ihold = irun _driverRA->pdn_disable(1); - _driverRA->ihold(31); #if defined(ARDUINO_ARCH_RP2040) - _driverRA->toff(4); // toff(1) is too short for StealthChop to engage reliably on RP2040 + _driverRA->toff(4); // toff>=3 required for StealthChop slow-decay phase #else _driverRA->toff(1); #endif @@ -543,7 +542,7 @@ void Mount::configureRAdriver(uint16_t RA_SW_RX, uint16_t RA_SW_TX, float rsense LOG(DEBUG_STEPPERS, "[MOUNT]: Requested RA motor rms_current: %d mA", rmscurrent); _driverRA->rms_current(rmscurrent, 1.0f); //holdMultiplier = 1 to set ihold = irun #if defined(ARDUINO_ARCH_RP2040) - _driverRA->toff(4); // toff(1) is too short for StealthChop to engage reliably on RP2040 + _driverRA->toff(4); // toff>=3 required for StealthChop slow-decay phase #else _driverRA->toff(1); #endif @@ -591,11 +590,10 @@ void Mount::configureDECdriver(Stream *serial, float rsense, byte driveraddress, LOG(DEBUG_STEPPERS, "[MOUNT]: Requested DEC motor rms_current: %d mA", rmscurrent); _driverDEC->rms_current(rmscurrent, 1.0f); //holdMultiplier = 1 to set ihold = irun _driverDEC->pdn_disable(1); - _driverDEC->ihold(31); _driverDEC->iholddelay(15); _driverDEC->TPOWERDOWN(255); #if defined(ARDUINO_ARCH_RP2040) - _driverDEC->toff(4); // toff(1) is too short for StealthChop to engage reliably on RP2040 + _driverDEC->toff(4); // toff>=3 required for StealthChop slow-decay phase #else _driverDEC->toff(1); #endif @@ -638,7 +636,7 @@ void Mount::configureDECdriver(uint16_t DEC_SW_RX, uint16_t DEC_SW_TX, float rse LOG(DEBUG_STEPPERS, "[MOUNT]: Requested DEC motor rms_current: %d mA", rmscurrent); _driverDEC->rms_current(rmscurrent, 1.0f); //holdMultiplier = 1 to set ihold = irun #if defined(ARDUINO_ARCH_RP2040) - _driverDEC->toff(4); // toff(1) is too short for StealthChop to engage reliably on RP2040 + _driverDEC->toff(4); // toff>=3 required for StealthChop slow-decay phase #else _driverDEC->toff(1); #endif @@ -686,7 +684,7 @@ void Mount::configureAZdriver(Stream *serial, float rsense, byte driveraddress, LOG(DEBUG_STEPPERS, "[MOUNT]: Requested AZ motor rms_current: %d mA", rmscurrent); _driverAZ->rms_current(rmscurrent, AZ_MOTOR_HOLD_SETTING / 100.0); //holdMultiplier = 1 to set ihold = irun #if defined(ARDUINO_ARCH_RP2040) - _driverAZ->toff(4); // toff(1) is too short for StealthChop to engage reliably on RP2040 + _driverAZ->toff(4); // toff>=3 required for StealthChop slow-decay phase #else _driverAZ->toff(1); #endif @@ -728,7 +726,7 @@ void Mount::configureAZdriver(uint16_t AZ_SW_RX, uint16_t AZ_SW_TX, float rsense LOG(DEBUG_STEPPERS, "[MOUNT]: Requested AZ motor rms_current: %d mA", rmscurrent); _driverAZ->rms_current(rmscurrent, AZ_MOTOR_HOLD_SETTING / 100.0); //holdMultiplier = 1 to set ihold = irun #if defined(ARDUINO_ARCH_RP2040) - _driverAZ->toff(4); // toff(1) is too short for StealthChop to engage reliably on RP2040 + _driverAZ->toff(4); // toff>=3 required for StealthChop slow-decay phase #else _driverAZ->toff(1); #endif @@ -775,7 +773,7 @@ void Mount::configureALTdriver(Stream *serial, float rsense, byte driveraddress, LOG(DEBUG_STEPPERS, "[MOUNT]: Requested ALT motor rms_current: %d mA", rmscurrent); _driverALT->rms_current(rmscurrent, ALT_MOTOR_HOLD_SETTING / 100.0); //holdMultiplier = 1 to set ihold = irun #if defined(ARDUINO_ARCH_RP2040) - _driverALT->toff(4); // toff(1) is too short for StealthChop to engage reliably on RP2040 + _driverALT->toff(4); // toff>=3 required for StealthChop slow-decay phase #else _driverALT->toff(1); #endif @@ -817,7 +815,7 @@ void Mount::configureALTdriver(uint16_t ALT_SW_RX, uint16_t ALT_SW_TX, float rse LOG(DEBUG_STEPPERS, "[MOUNT]: Requested ALT motor rms_current: %d mA", rmscurrent); _driverALT->rms_current(rmscurrent, ALT_MOTOR_HOLD_SETTING / 100.0); //holdMultiplier = 1 to set ihold = irun #if defined(ARDUINO_ARCH_RP2040) - _driverALT->toff(4); // toff(1) is too short for StealthChop to engage reliably on RP2040 + _driverALT->toff(4); // toff>=3 required for StealthChop slow-decay phase #else _driverALT->toff(1); #endif @@ -866,7 +864,7 @@ void Mount::configureFocusDriver(Stream *serial, float rsense, byte driveraddres LOG(DEBUG_STEPPERS | DEBUG_FOCUS, "[FOCUS]: Requested Focus motor rms_current: %d mA", rmscurrent); _driverFocus->rms_current(rmscurrent, FOCUSER_MOTOR_HOLD_SETTING / 100.f); //holdMultiplier = 1 to set ihold = irun #if defined(ARDUINO_ARCH_RP2040) - _driverFocus->toff(4); // toff(1) is too short for StealthChop to engage reliably on RP2040 + _driverFocus->toff(4); // toff>=3 required for StealthChop slow-decay phase #else _driverFocus->toff(1); #endif @@ -916,7 +914,7 @@ void Mount::configureFocusDriver( LOG(DEBUG_STEPPERS | DEBUG_FOCUS, "[FOCUS]: Requested Focus motor rms_current: %d mA", rmscurrent); _driverFocus->rms_current(rmscurrent, FOCUSER_MOTOR_HOLD_SETTING / 100.f); //holdMultiplier = 1 to set ihold = irun #if defined(ARDUINO_ARCH_RP2040) - _driverFocus->toff(4); // toff(1) is too short for StealthChop to engage reliably on RP2040 + _driverFocus->toff(4); // toff>=3 required for StealthChop slow-decay phase #else _driverFocus->toff(1); #endif @@ -1169,6 +1167,8 @@ String Mount::getMountHardwareInfo() ret = F("ESP32,"); #elif defined(__AVR_ATmega2560__) ret = F("Mega,"); +#elif defined(ARDUINO_ARCH_RP2040) + ret = F("RP2040,"); #endif ret += F("NEMA|"); @@ -3405,7 +3405,11 @@ void Mount::updateInfoDisplay() // timeout, causing ASCOM errors. // We will update at 30Hz when idle, 5Hz when slewing one axis and skip updates when slewing both. - int refreshRateHz = 30; + int refreshRateHz = 30; + #if defined(ARDUINO_ARCH_RP2040) + const bool isSlewingAny = (slewStatus() & (SLEWING_DEC | SLEWING_RA)) != 0; + refreshRateHz = isSlewingAny ? 15 : 30; + #else const bool isSlewingRAandDEC = (slewStatus() & (SLEWING_DEC | SLEWING_RA)) == (SLEWING_DEC | SLEWING_RA); if (isSlewingRAandDEC) { @@ -3415,7 +3419,7 @@ void Mount::updateInfoDisplay() { refreshRateHz = 5; // Update the display slower } - + #endif const long now = millis(); if (now - _lastInfoUpdate > (1000 / refreshRateHz)) { @@ -3794,7 +3798,7 @@ void Mount::moveSteppersTo(float targetRASteps, float targetDECSteps, StepperAxi { if (_decUpperLimit != 0) { -#if DEBUG_LEVEL > 0 +#if DEBUG_LEVEL != 0 if (targetDECSteps > (float) _decUpperLimit) { LOG(DEBUG_STEPPERS, "[STEPPERS]: MoveSteppersTo: DEC Upper Limit enforced. To: %l", _decUpperLimit); @@ -3805,7 +3809,7 @@ void Mount::moveSteppersTo(float targetRASteps, float targetDECSteps, StepperAxi if (_decLowerLimit != 0) { -#if DEBUG_LEVEL > 0 +#if DEBUG_LEVEL != 0 if (targetDECSteps < (float) _decLowerLimit) { LOG(DEBUG_STEPPERS, "[STEPPERS]: MoveSteppersTo: DEC Lower Limit enforced. To: %l", _decLowerLimit); diff --git a/src/SSD1306_128x64_Display.cpp b/src/SSD1306_128x64_Display.cpp index bae3c33b..945d87f7 100644 --- a/src/SSD1306_128x64_Display.cpp +++ b/src/SSD1306_128x64_Display.cpp @@ -55,25 +55,30 @@ void SDD1306OLED128x64::renderCallback(void *context) display->drawScreen(); } -void SDD1306OLED128x64::drawScreen() +// Console-mode header, in the area 0,0 to 127,20. +// Logo on the left, product name on the right. +void SDD1306OLED128x64::drawHeader() { _display->setColor(WHITE); - if (_consoleMode) - { - // Draw header only in console mode, in the area 0,0 to 127,20 - // Logo on the left. Base class renders console output text. - _display->setFont(OATLogo); - _display->drawString(0, 0, "!"); + _display->setFont(OATLogo); + _display->drawString(0, 0, "!"); - // Name on the right - _display->setFont(Bitmap5x7); + // Name on the right + _display->setFont(Bitmap5x7); #ifdef OAE - _display->drawString(32, 6, F("OpenAstroExplorer")); + _display->drawString(32, 6, F("OpenAstroExplorer")); #elif defined(OAM) - _display->drawString(32, 6, F("OpenAstroMount")); + _display->drawString(32, 6, F("OpenAstroMount")); #else - _display->drawString(32, 6, F("OpenAstroTracker")); + _display->drawString(32, 6, F("OpenAstroTracker")); #endif +} + +void SDD1306OLED128x64::drawScreen() +{ + if (_consoleMode) + { + drawHeader(); } else { @@ -124,7 +129,11 @@ void SDD1306OLED128x64::drawIndicators(Mount *mount) _display->drawString(11, 59, F("L")); // Memory chip icon _display->setFont(Bitmap3x5); long availMem = freeMemory(); - if (availMem > 9999) + if (availMem > 999999) + { + _display->drawString(20, 59, String(availMem / 1024 / 1024, 1) + "M"); + } + else if (availMem > 9999) { _display->drawString(20, 59, String(availMem / 1024) + "K"); } @@ -159,7 +168,11 @@ void SDD1306OLED128x64::drawIndicators(Mount *mount) break; case 3: { - _display->drawString(55, 59, (String(F(" FW ")) + String(VERSION)).c_str()); + // Bitmap3x5 has no lowercase glyphs; uppercase so release-please's + // "v" version prefix renders as V instead of an out-of-range glyph. + String fw = String(F(" FW ")) + String(VERSION); + fw.toUpperCase(); + _display->drawString(55, 59, fw.c_str()); } break; case 4: diff --git a/src/SSD1306_128x64_Display.hpp b/src/SSD1306_128x64_Display.hpp index 32f14633..6e59daf6 100644 --- a/src/SSD1306_128x64_Display.hpp +++ b/src/SSD1306_128x64_Display.hpp @@ -1,6 +1,20 @@ #pragma once +// In PlatformIO builds the real pragma wrappers come from macros/gcc/Macros.hpp, +// which is force-included via the -include build flag. Arduino IDE builds have no +// such flag, and this header can be the first to use the macros (SSD1306_128x64_Display.cpp +// includes it directly), so provide the same no-op fallbacks as inc/Globals.hpp. +#if !defined(PUSH_NO_WARNINGS) + #define PUSH_NO_WARNINGS ; +#endif +#if !defined(POP_NO_WARNINGS) + #define POP_NO_WARNINGS ; +#endif +// The arduino-pico core headers (the Arduino.h chain) and the OLED library are not +// warning-clean under our strict flag set (-Wshadow). +PUSH_NO_WARNINGS #include #include "SSD1306Wire.h" +POP_NO_WARNINGS #include "Configuration.hpp" #include "Utility.hpp" #include "Version.h" @@ -55,6 +69,9 @@ class SDD1306OLED128x64 : public InfoDisplayRender void static renderCallback(void *context); void drawScreen(); + // Console-mode header: logo and product name + void drawHeader(); + // Build the display from the mount void renderScreen(void *mount); diff --git a/src/Utility.cpp b/src/Utility.cpp index 7937dda4..16248a1d 100644 --- a/src/Utility.cpp +++ b/src/Utility.cpp @@ -3,11 +3,11 @@ #include "../Configuration.hpp" #include "Utility.hpp" -#if DEBUG_LEVEL > 0 +#if DEBUG_LEVEL != 0 #include #endif -#if DEBUG_LEVEL > 0 +#if DEBUG_LEVEL != 0 unsigned long RealTime::_pausedTime = 0; unsigned long RealTime::_suspendStart = 0; int RealTime::_suspended = 0; @@ -293,7 +293,7 @@ int freeMemory() } #endif -#if DEBUG_LEVEL > 0 +#if DEBUG_LEVEL != 0 String formatArg(const char *input, va_list args) { @@ -410,15 +410,26 @@ void logv(int levelFlags, String input, ...) #if BUFFER_LOGS == true addToLogBuffer(formatArg(input.c_str(), argp)); #else - DEBUG_SERIAL_PORT.print("["); - DEBUG_SERIAL_PORT.print(String(now)); - DEBUG_SERIAL_PORT.print("]{"); - DEBUG_SERIAL_PORT.print(String(delta)); - DEBUG_SERIAL_PORT.print("}ms:"); - DEBUG_SERIAL_PORT.print(String(freeMemory())); - DEBUG_SERIAL_PORT.print("B: "); - DEBUG_SERIAL_PORT.println(formatArg(input.c_str(), argp)); + // Compose the whole line and write it with a single print() call. + // The arduino-pico USB CDC write path can wedge when many small + // writes per line race the TinyUSB FIFO (each write re-enters the + // USB mutex); one write per line minimizes the exposure. + String line = "["; + line += String(now); + line += "]{"; + line += String(delta); + line += "}ms:"; + line += String(freeMemory()); + line += "B: "; + line += formatArg(input.c_str(), argp); + line += "\n"; + DEBUG_SERIAL_PORT.print(line); + #if !defined(ARDUINO_ARCH_RP2040) + // UART outputs drain from a ring buffer; flush so the tail of the log is not + // lost if the firmware crashes right after logging (matters for AVR debugging). + // On RP2040 the port is USB CDC, where flush() can stall without a reader. DEBUG_SERIAL_PORT.flush(); + #endif #endif lastLog = now; va_end(argp); diff --git a/src/Utility.hpp b/src/Utility.hpp index d8801ebd..91847c6c 100644 --- a/src/Utility.hpp +++ b/src/Utility.hpp @@ -10,7 +10,7 @@ String getLogBuffer(); int freeMemory(); -#if DEBUG_LEVEL > 0 +#if DEBUG_LEVEL != 0 #define LOG(level, format, ...) logv((level), (F(format)), ##__VA_ARGS__) // Realtime timer class using microseconds to time stuff diff --git a/src/b_setup.hpp b/src/b_setup.hpp index 2b2c21c0..ac45478c 100644 --- a/src/b_setup.hpp +++ b/src/b_setup.hpp @@ -130,22 +130,27 @@ void setup() // OATControl (and other apps using C# SerialPort) open the port with DTR=false. // The Philhower USB CDC SerialUSB::write() gates TX on tud_cdc_connected() which // requires DTR=true, so all responses are silently dropped for those hosts. - // ignoreFlowControl(true) bypasses the DTR check and always sends TX data. + // ignoreFlowControl(true) bypasses the DTR check so replies always flow. + // Bounded by the core: with no (or a non-DTR) host, writes stop as soon as the + // ~257-byte CDC TX FIFO is full (tud_cdc_connected() false breaks the write + // loop immediately); a connected host that stops draining is dropped after + // 1 second without progress. Headless boots never stall on logging. Serial.ignoreFlowControl(true); #endif - #if DEBUG_LEVEL > 0 && DEBUG_SEPARATE_SERIAL == 1 + #if DEBUG_LEVEL != 0 && DEBUG_SEPARATE_SERIAL == 1 DEBUG_SERIAL_PORT.begin(DEBUG_SERIAL_BAUDRATE); #endif - // RP2040 USB CDC: wait up to 3 s for the host to open the port so that - // LOG() calls below are not dropped (or do not block indefinitely). - // This only matters when DEBUG_LEVEL > 0 — in release builds LOG() is - // a no-op, so the wait is compiled out. - #if defined(ARDUINO_ARCH_RP2040) && DEBUG_LEVEL > 0 - { - uint32_t _cdcWaitStart = millis(); - while (!Serial && (millis() - _cdcWaitStart) < 3000UL) - { /* spin */ - } + // RP2040 USB CDC: give a console up to 3 seconds to attach so the boot + // log stays visible. Bounded, never infinite: a mount powered externally + // in the field must boot with no USB host attached. With no host attached + // the CDC TX FIFO holds only ~250 bytes and further writes are dropped, so + // a headless debug build simply boots invisibly (reset with the console + // open to see the full log). Release builds (DEBUG_LEVEL == 0) skip this + // entirely. + #if defined(ARDUINO_ARCH_RP2040) && DEBUG_LEVEL != 0 + uint32_t consoleWaitStart = millis(); + while (!Serial && (millis() - consoleWaitStart < 3000UL)) + { /* spin until the console connects or the timeout expires */ } #endif #endif @@ -218,9 +223,6 @@ void setup() ///////////////////////////////// // RP2040: UART pin remapping ///////////////////////////////// - // The Philhower arduino-pico core requires setTX()/setRX() to be called - // before begin() to remap UART pins away from their defaults. This must - // happen before any Serial2.begin() calls in the driver setup below. // RP2040_UART1_TX_PIN / RP2040_UART1_RX_PIN are defined in the board's // pins file only for RP2040 targets that use a non-default UART1 mapping. #if defined(ARDUINO_ARCH_RP2040) && defined(RP2040_UART1_TX_PIN) && defined(RP2040_UART1_RX_PIN) @@ -256,10 +258,10 @@ void setup() #endif #ifdef RA_SERIAL_PORT - #if defined(ARDUINO_ARCH_RP2040) - RA_SERIAL_PORT.begin(115200); // TMCStepper requires 115200 baud on the Philhower RP2040 core - #elif defined(OAE) + #ifdef OAE RA_SERIAL_PORT.begin(57600, SERIAL_8N1, RA_RX_PIN, RA_TX_PIN); + #elif defined(ARDUINO_ARCH_RP2040) + RA_SERIAL_PORT.begin(115200); // TMCStepper requires 115200 baud on RP2040 #else RA_SERIAL_PORT.begin(57600); // Start HardwareSerial comms with driver #endif @@ -291,10 +293,10 @@ void setup() pinMode(DEC_DIAG_PIN, INPUT); #endif #ifdef DEC_SERIAL_PORT - #if defined(ARDUINO_ARCH_RP2040) - DEC_SERIAL_PORT.begin(115200); // TMCStepper requires 115200 baud on the Philhower RP2040 core - #elif defined(OAE) + #ifdef OAE DEC_SERIAL_PORT.begin(57600, SERIAL_8N1, DEC_RX_PIN, DEC_TX_PIN); + #elif defined(ARDUINO_ARCH_RP2040) + DEC_SERIAL_PORT.begin(115200); // TMCStepper requires 115200 baud on RP2040 #else DEC_SERIAL_PORT.begin(57600); // Start HardwareSerial comms with driver #endif @@ -315,7 +317,7 @@ void setup() #endif #ifdef AZ_SERIAL_PORT #if defined(ARDUINO_ARCH_RP2040) - AZ_SERIAL_PORT.begin(115200); // TMCStepper requires 115200 baud on the Philhower RP2040 core + AZ_SERIAL_PORT.begin(115200); // TMCStepper requires 115200 baud on RP2040 #else AZ_SERIAL_PORT.begin(57600); // Start HardwareSerial comms with driver #endif @@ -336,7 +338,7 @@ void setup() #endif #ifdef ALT_SERIAL_PORT #if defined(ARDUINO_ARCH_RP2040) - ALT_SERIAL_PORT.begin(115200); // TMCStepper requires 115200 baud on the Philhower RP2040 core + ALT_SERIAL_PORT.begin(115200); // TMCStepper requires 115200 baud on RP2040 #else ALT_SERIAL_PORT.begin(57600); // Start HardwareSerial comms with driver #endif @@ -355,7 +357,7 @@ void setup() #ifdef FOCUS_SERIAL_PORT LOG(DEBUG_FOCUS, "[FOCUS]: setup(): focus TMC2209U starting comms"); #if defined(ARDUINO_ARCH_RP2040) - FOCUS_SERIAL_PORT.begin(115200); // TMCStepper requires 115200 baud on the Philhower RP2040 core + FOCUS_SERIAL_PORT.begin(115200); // TMCStepper requires 115200 baud on RP2040 #else FOCUS_SERIAL_PORT.begin(57600); // Start HardwareSerial comms with driver #endif @@ -653,8 +655,9 @@ void setup() #else #ifndef NEW_STEPPER_LIB - // 2 kHz updates (higher frequency interferes with serial communications and complete messes up OATControl communications) - if (!InterruptCallback::setInterval(0.5f, stepperControlTimerCallback, &mount)) + // Stepper servicing rate: 2 kHz on AVR (higher rates there interfere with serial + // communications and mess up OATControl), 5 kHz on RP2040 (see STEPPER_CONTROL_INTERVAL_MS). + if (!InterruptCallback::setInterval(STEPPER_CONTROL_INTERVAL_MS, stepperControlTimerCallback, &mount)) { LOG(DEBUG_MOUNT, "[SYSTEM]: CANNOT setup interrupt timer!"); } diff --git a/src/f_serial.hpp b/src/f_serial.hpp index 7f8458a1..64d5644b 100644 --- a/src/f_serial.hpp +++ b/src/f_serial.hpp @@ -19,7 +19,7 @@ void serialLoop() mount.loop(); mount.displayStepperPositionThrottled(); - #ifdef ESP32 + #if defined(ESP32) processSerialData(); #endif diff --git a/src/testmenu.cpp b/src/testmenu.cpp index 2649ea7d..f13e7571 100644 --- a/src/testmenu.cpp +++ b/src/testmenu.cpp @@ -479,7 +479,7 @@ void TestMenu::onKeyPressed(int key) Serial.println(F("Mount reset, EEPROM erased.")); _currentMenu->display(); } - else if (action = F("PassthroughCmd")) + else if (action == F("PassthroughCmd")) { Serial.print(F("Enter LX200-OAT command to send to mount: ")); TestMenu::setMenuState(testMenuState_t::WAITING_ON_COMMAND);