Custom 3D Printer Firmware for Raspberry Pi Pico with Quadrature Encoders and Advanced Motion Control
This 3D Printer Firmware implements precise multi-axis motion control for my custom built 3D printer using Raspberry Pi Pico/RP2040, featuring quadrature encoder feedback, Bresenham-based path planning, cascaded PID control, and robust configuration storage using LittleFS. Designed for PlatformIO with modular architecture and real-time performance.
- Installation Guide
- Building the Firmware
- Flashing to Raspberry Pi Pico
- Hardware Configuration
- Core System Architecture
- Advanced Configuration
- Troubleshooting
- Customization for Other Boards
- PlatformIO Core (VSCode extension or standalone)
- Python 3.7+
- Git for version control
git clone https://github.com/Manish-git-tech/3d_printer_firmware_2.git
cd 3d_printer_firmware_2
- Install dependencies:
pio pkg install
- Configure board in
platformio.ini
:
[env:pico]
platform = raspberrypi
board = pico
framework = arduino
src/
├── comms/ # G-code parsing and serial communication
│ ├── comms.cpp # Command state machine
│ └── comms_parser.h # G-code syntax definitions
├── motion/ # Motor control and feedback
│ ├── encoder.cpp # Quadrature decoding
│ └── pid_control.cpp # Cascaded PID implementation
├── path_planning/ # Motion algorithms
│ └── bresenham.cpp # Coordinated movement
├── config/ # Hardware parameters
│ ├── pins.h # GPIO mappings
│ └── motion_params.h # Kinematic settings
└── main.cpp # Firmware entry point
# Build for Raspberry Pi Pico
pio run -e pico
# Build for ESP8266 (WiFi module)
pio run -e esp8266
# Clean build artifacts
pio run -t clean
Environment Variable | Description |
---|---|
ENABLE_DEBUG_LOG |
Verbose serial output |
FORCE_EEPROM_RESET |
Clear stored settings |
pio run -e pico -t upload
- Hold BOOTSEL button while connecting USB
- Copy
.pio/build/pico/firmware.uf2
to RPI-RP2 drive - Wait for automatic reboot
- Raspberry Pi Pico (Main Controller)
- ESP8266 (WiFi Communication)
- MX1508 Motor Drivers (2 per axis)
- DC Motors with Quadrature Encoders
- 24V Power Supply
// X-axis Configuration
#define X_ENC_A 2 // Encoder Phase A
#define X_ENC_B 3 // Encoder Phase B
#define X_MOT_IN1 4 // MX1508 Direction 1
#define X_MOT_IN2 5 // MX1508 Direction 2
Axis | Slits/mm | CPR | Maximum RPM |
---|---|---|---|
X | 120 | 480 | 300 |
Y | 150 | 600 | 250 |
Z | 200 | 800 | 150 |
Implementation: motion/encoder.cpp
- Uses pin change interrupts for real-time decoding
- 4x counting mode with state transition table:
const int8_t ENCODER_STATES[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
- Velocity calculation via position differentiation:
float getVelocity() { return (current_pos - last_pos) / (micros() - last_time) * 1e6; }
Implementation: path_planning/bresenham.cpp
- Coordinated multi-axis movement
- Error accumulation for step synchronization:
void BresenhamPlanner::planMove(int32_t target[]) { steps[X] = abs(target[X] - current[X]); steps[Y] = abs(target[Y] - current[Y]); err = steps[X] - steps[Y]; while(current != target) { if(err * 2 >= -steps[Y]) { err -= steps[Y]; current[X] += x_dir; } if(err * 2
// X-axis Settings
constexpr uint16_t X_STEPS_PER_MM = 80;
constexpr float X_MAX_ACCEL = 1500.0f; // mm/s²
constexpr float X_JERK = 20.0f; // mm/s
// PID Defaults
constexpr float DEFAULT_KP = 10.0f;
constexpr float DEFAULT_KI = 0.05f;
Command | Parameters | Description |
---|---|---|
G0 | X Y Z F | Linear move |
G1 | X Y Z E F | Linear move with extrusion |
M2000 | Pid values | change and save pid values in EEPROM |
M2001 | -- | Load pid values |
Symptom | Likely Cause | Solution |
---|---|---|
Position drift | Encoder noise | Add 0.1μF capacitors to encoder inputs |
Motor stalling | PWM below 47% | Increase minimum power in motor_control.cpp |
EEPROM corruption | Improper shutdown | Implement atomic writes with checksums |
# Report encoder counts
M119
# Dump EEPROM contents
M503
# Test axis movement
G0 X100 F500
- Modify
platformio.ini
:[env:esp32] platform = espressif32 board = esp32dev framework = arduino
- Update pin mappings in
config/pins.h
- Implement encoder interface:
class CustomEncoder : public EncoderBase { public: int32_t read() override; void init() override; };
- Register in
motion/motion.cpp
:Encoder* x_encoder = new CustomEncoder(X_ENC_A, X_ENC_B);
MIT License - Free for personal and commercial use
Special Thanks:
- Marlin Firmware Team for motion control foundations
- PlatformIO for build system
- Raspberry Pi Foundation for Pico SDK
Note: Always verify electrical connections before powering on the system. Incorrect wiring may damage components.