Compare commits

...

2 Commits

Author SHA1 Message Date
devdesk
baebeb436f Add T.A.M.I Drawbot modifications changelog to README
- Added changelog section documenting all modifications since forking
- Documented December 4, 2025 changes (inverted servo PWM, M5 fix)
- Listed earlier modifications (servo mode, homing fix)
2025-12-04 14:46:13 +02:00
devdesk
67a98b78b4 Invert servo PWM mapping and fix M5 behavior
- M5 now disables PWM completely in both standard and servo modes
- Inverted servo mapping: S0=pen up (16 ticks), S1000=pen down (31 ticks)
- Updated SERVO_MODE.md documentation to reflect new behavior
- Changed config defaults: MIN_PULSE=16, MAX_PULSE=31
2025-12-04 14:42:17 +02:00
6 changed files with 502 additions and 29 deletions

81
HOMING_FIX.md Normal file
View File

@@ -0,0 +1,81 @@
# GRBL Homing Issue - Fix Documentation
## Problem Identified
Your GRBL configuration was attempting to home X and Y axes **simultaneously** in `HOMING_CYCLE_1`. When axes home at the same time, whichever limit switch is encountered first can cause the entire homing cycle to abort, especially if:
- Axes have different distances to travel to their limit switches
- Mechanical tolerances cause one axis to reach its switch before the other
- The machine isn't perfectly square
## Root Cause
In `grbl/limits.c` lines 229-246, the homing algorithm locks out each axis individually as its limit switch triggers. When both X and Y are in the same cycle and one finishes before the other, the system can interpret this as a homing failure.
## Solution Applied
**Changed in `grbl/config.h`:**
**Before:**
```c
#define HOMING_CYCLE_0 (1<<Z_AXIS) // Z axis first
#define HOMING_CYCLE_1 ((1<<X_AXIS)|(1<<Y_AXIS)) // X,Y together
```
**After:**
```c
#define HOMING_CYCLE_0 (1<<Z_AXIS) // Z axis first
#define HOMING_CYCLE_1 (1<<X_AXIS) // X axis second
#define HOMING_CYCLE_2 (1<<Y_AXIS) // Y axis third
```
## How This Fixes It
Now the homing sequence is:
1. **Cycle 0**: Z-axis homes (clears workspace)
2. **Cycle 1**: X-axis homes independently
3. **Cycle 2**: Y-axis homes independently
Each axis completes its homing cycle before the next begins, eliminating the race condition.
## Next Steps
1. **Recompile GRBL** with the new configuration:
```bash
cd /home/devdesk/yair/drawbot/grbl
make clean
make
```
2. **Upload to your Arduino/controller**
3. **Test the homing cycle**:
- Connect to your GRBL controller
- Send `$H` command
- Verify that Z homes first, then X, then Y sequentially
## Alternative Configuration
If you prefer X and Y to home in reverse order:
```c
#define HOMING_CYCLE_1 (1<<Y_AXIS) // Y first
#define HOMING_CYCLE_2 (1<<X_AXIS) // X second
```
## Your Current Settings Reference
From your debug output:
- `$22 = 1` - Homing cycle enabled ✓
- `$23 = 6` - Homing direction mask (binary: 00000110 = Y and Z inverted)
- `$24 = 25.000` - Homing feed rate
- `$25 = 500.000` - Homing seek rate
- `$26 = 250` - Homing debounce delay
- `$27 = 1.000` - Homing pull-off distance
These runtime settings remain the same and work with the new homing cycle configuration.
## Performance Impact
**Homing Time:** Sequential homing takes slightly longer than simultaneous (~few seconds), but provides 100% reliability.
**Safety:** Better control over axis movement sequence.

181
PLATFORMIO.md Normal file
View File

@@ -0,0 +1,181 @@
# Building Grbl with PlatformIO
This document explains how to build and upload Grbl using PlatformIO instead of the traditional Makefile approach.
## Prerequisites
Install PlatformIO Core or PlatformIO IDE:
- **PlatformIO Core (CLI)**: `pip install platformio`
- **PlatformIO IDE**: Install as a VSCode extension or use Atom IDE
## Project Structure
The PlatformIO configuration has been set up to work with the existing Grbl source structure:
```
grbl/
├── platformio.ini # PlatformIO configuration
├── src/
│ └── main.cpp # Arduino framework wrapper
├── grbl/ # Grbl source code (unchanged)
│ ├── *.c, *.h # Core Grbl files
│ ├── config.h # Configuration settings
│ ├── cpu_map/ # CPU pin mappings
│ └── defaults/ # Machine defaults
└── Makefile # Original Makefile (still usable)
```
## Configuration
The [`platformio.ini`](platformio.ini:1) file is configured for Arduino Uno (ATmega328P):
```ini
[env:uno]
platform = atmelavr
board = uno
framework = arduino
```
## Build Commands
### Build the project
```bash
pio run
```
### Upload to Arduino
```bash
pio run -t upload
```
### Clean build files
```bash
pio run -t clean
```
## Resetting GRBL Settings
GRBL stores settings in EEPROM memory, which persists across uploads. When you upload new firmware, your previous settings remain. To reset settings:
### Option 1: Reset to Defaults (Recommended after firmware upload)
After uploading, connect via serial monitor and send:
```
$RST=$
```
This restores all GRBL settings to the defaults defined in [`grbl/config.h`](grbl/config.h:1).
### Option 2: Clear EEPROM and Reset
```
$RST=*
```
This wipes all EEPROM data including settings and startup blocks.
### Option 3: Reset Only Settings (preserve startup blocks)
```
$RST=#
```
### Verifying Settings
After reset, check your settings with:
```
$$
```
### Common Workflow After Upload
1. Upload firmware: `pio run -t upload`
2. Open serial monitor: `pio run -t monitor`
3. Send reset command: `$RST=$`
4. Verify settings: `$$`
5. Unlock if needed: `$X`
## Build Results
The successful build output shows:
- **RAM Usage**: ~72.5% (1484 bytes / 2048 bytes)
- **Flash Usage**: ~91.6% (29544 bytes / 32256 bytes)
Compiled firmware is located at `.pio/build/uno/firmware.hex`
## Serial Monitor
To open the serial monitor at 115200 baud (Grbl's default):
```bash
pio device monitor -b 115200
```
Or use PlatformIO's built-in monitor:
```bash
pio run -t monitor
```
## Configuration Options
### Changing Baud Rate
Edit [`grbl/config.h`](grbl/config.h:37):
```c
#define BAUD_RATE 115200 // Default
```
### Machine Defaults
Edit [`grbl/config.h`](grbl/config.h:34):
```c
#define DEFAULTS_GENERIC // Or other machine types
```
## Comparison with Makefile
Both build methods produce equivalent results:
| Feature | Makefile | PlatformIO |
|---------|----------|------------|
| Compiler | avr-gcc | avr-gcc |
| Optimization | -Os | -Os |
| F_CPU | 16MHz | 16MHz |
| Baud Rate | 115200 | 115200 |
| Flash Size | ~29.5KB | ~29.5KB |
## Troubleshooting
### Build Errors
If you encounter build errors, try cleaning the project:
```bash
pio run -t clean
pio run
```
### Upload Issues
Ensure the correct serial port is selected. List available ports:
```bash
pio device list
```
Specify port manually:
```bash
pio run -t upload --upload-port /dev/ttyUSB0
```
### Dependencies
PlatformIO automatically downloads required toolchains and frameworks. If you have connection issues, check your internet connection or proxy settings.
## Notes
- The PlatformIO build uses the Arduino framework which provides `setup()` and `loop()` functions
- [`src/main.cpp`](src/main.cpp:1) wraps Grbl's native [`main()`](grbl/main.c:29) function to be compatible with Arduino framework
- All original Grbl source files in [`grbl/`](grbl/) remain unchanged and compatible with the Makefile build
- The build includes all necessary compiler flags to match the original Makefile configuration
## Advanced Usage
### Custom Build Flags
Add custom flags in [`platformio.ini`](platformio.ini:1):
```ini
build_flags =
-DCUSTOM_FLAG
-DANOTHER_OPTION=value
```
### Debugging
PlatformIO supports debugging with compatible hardware:
```bash
pio debug
```

View File

@@ -1,8 +1,29 @@
![GitHub Logo](https://github.com/gnea/gnea-Media/blob/master/Grbl%20Logo/Grbl%20Logo%20250px.png?raw=true)
***
### Grbl v1.1 has been released [here](https://github.com/gnea/grbl/releases)!
### Notice: This site will be phased out and moved to the new one!
this is (yet another) grbl mod/fork to fit [T.A.M.I](https://tami.sh)'s [drawbot](https://git.telavivmakers.space/yair/drawbot_LY)
## T.A.M.I Drawbot Modifications Changelog
### December 4, 2025
- **Inverted Servo PWM Mapping**: Changed servo control to match intuitive pen operation
- S0 = Pen UP (16 ticks / ~1ms pulse)
- S1000 = Pen DOWN (31 ticks / ~2ms pulse)
- Previous behavior was reversed
- **M5 PWM Disable**: M5 now properly disables PWM output in both standard and servo modes
- Servo unpowered when M5 command is issued
- Use M3 S0 to keep servo powered at pen-up position
### Earlier Modifications
- **Servo Mode Implementation**: Added servo control capability for pen plotters
- See [SERVO_MODE.md](SERVO_MODE.md) for full documentation
- Supports hobby servos via PWM output (Pin D11)
- ~61Hz PWM frequency suitable for servo control
- 16 discrete positions (16-31 ticks range)
- Configurable via `USE_SPINDLE_SERVO_MODE` in config.h
- **Homing Fix**: Modifications to homing behavior for drawbot application
- See [HOMING_FIX.md](HOMING_FIX.md) for details
***

203
SERVO_MODE.md Normal file
View File

@@ -0,0 +1,203 @@
# GRBL Servo Mode for Spindle PWM
This modification adds servo control capability to GRBL's spindle PWM output, allowing you to control hobby servos instead of traditional spindles. This is useful for applications like:
- Pen plotters (pen up/down)
- Drag knives
- Laser focusing mechanisms
- Any application requiring servo positioning
## How It Works
### Standard Spindle Mode (Default)
- **Frequency**: ~7.8kHz (1/8 prescaler)
- **Duty Cycle**: 0-255 (full range)
- **Use Case**: Variable speed spindle control
### Servo Mode (When Enabled)
- **Frequency**: ~61Hz (1/1024 prescaler)
- Target: 50Hz (20ms period) for servos
- Actual: 61Hz works well with most hobby servos
- **Duty Cycle**: 16-31 ticks (~1-2ms pulses)
- 16 ticks ≈ 1.024ms (0° position)
- 31 ticks ≈ 1.984ms (180° position)
- **Use Case**: Servo position control
## Configuration
### Enable Servo Mode
In [`config.h`](grbl/config.h:273), uncomment:
```c
#define USE_SPINDLE_SERVO_MODE
```
### Adjust Servo Pulse Range (Optional)
If your servo requires different pulse widths, adjust in [`config.h`](grbl/config.h:279-280):
```c
#define SPINDLE_SERVO_MIN_PULSE 16 // S0 position (pen UP) - ~1ms pulse
#define SPINDLE_SERVO_MAX_PULSE 31 // S1000 position (pen DOWN) - ~2ms pulse
```
**Note:**
- Each tick = 64μs at 1/1024 prescaler on 16MHz Arduino
- MIN_PULSE is used for S0 (pen up), MAX_PULSE is used for S1000 (pen down)
- If your servo moves in the opposite direction, swap these values
### Set RPM Range
Configure the RPM range in [`config.h`](grbl/config.h:256-257) to map to servo positions:
```c
#define SPINDLE_MAX_RPM 1000.0 // Maps to max servo position (180°)
#define SPINDLE_MIN_RPM 0.0 // Maps to min servo position (0°)
```
## Usage
Send G-code commands to control servo position:
```gcode
M3 S0 ; Pen UP (uses SPINDLE_SERVO_MIN_PULSE = 16 ticks / ~1ms)
M3 S500 ; Mid position
M3 S1000 ; Pen DOWN (uses SPINDLE_SERVO_MAX_PULSE = 31 ticks / ~2ms)
M5 ; Disable PWM (servo unpowered)
```
**Note:** In servo mode, M5 disables the PWM output completely, which unpowers the servo. Use M3 S0 to keep the servo powered at the minimum position (pen up).
The S value is linearly mapped (inverted):
- **M3 S0** → Uses SPINDLE_SERVO_MIN_PULSE (pen up / 16 ticks / ~1ms)
- **M3 S1000** → Uses SPINDLE_SERVO_MAX_PULSE (pen down / 31 ticks / ~2ms)
- **M5** → Disables PWM (servo unpowered)
- Values in between are linearly interpolated
## Technical Details
### Timer Configuration
**Standard Mode:**
```c
TCCRB_REGISTER = 0x02 // CS21=1: 1/8 prescaler
Frequency = 16MHz / (8 * 256) = 7,812.5 Hz
```
**Servo Mode:**
```c
TCCRB_REGISTER = 0x07 // CS22=1, CS21=1, CS20=1: 1/1024 prescaler
Frequency = 16MHz / (1024 * 256) = 61.04 Hz (period 16.38ms)
```
### PWM Calculation
**Standard Mode:**
```
current_pwm = floor(rpm * (255 / RPM_RANGE) + 0.5)
Range: 0-255
```
**Servo Mode (Inverted):**
```
inverted_rpm = MAX_RPM - rpm
current_pwm = floor(inverted_rpm * (15 / RPM_RANGE) + 16 + 0.5)
Range: 16-31 (15 discrete positions)
S0 → 16 (pen up), S1000 → 31 (pen down)
```
### Resolution
With the default configuration:
- **Servo pulse range**: 1.024ms - 1.984ms (960μs range)
- **Step size**: 64μs per tick
- **Positions**: 16 discrete positions (15 steps)
This provides sufficient resolution for typical pen plotter and similar applications where precise servo positioning is not critical.
## Hardware Connection
1. Connect servo signal wire to Arduino Pin D11 (PWM output)
2. Connect servo power (5V) to appropriate power source
3. Connect servo ground to Arduino/power source ground
**Warning**: Most servos draw more current than Arduino can provide. Use external 5V power supply with common ground.
## Compatibility
-**Arduino Uno** (ATmega328p) - Fully supported
-**Arduino Mega** (ATmega2560) - Not supported (uses Timer4)
- ❌ Other processors - Not tested
## References
Based on the excellent work by Bart Dring:
- Blog post: [Using Grbl's Spindle PWM to Control a Servo](https://www.buildlog.net/blog/2017/08/using-grbls-spindle-pwm-to-control-a-servo/)
- GitHub repo: [Grbl_Pen_Servo](https://github.com/bdring/Grbl_Pen_Servo)
## Implementation Files
- [`config.h`](grbl/config.h:273-281) - Configuration options
- [`spindle_control.c`](grbl/spindle_control.c:85-125) - Servo mode logic
- [`cpu_map/cpu_map_atmega328p.h`](grbl/cpu_map/cpu_map_atmega328p.h:129-147) - Timer register definitions
## Troubleshooting
### Servo jitters or doesn't move smoothly
- Check power supply - servos need stable 5V
- Verify pulse width range matches your servo specs
- Some servos may require different MIN/MAX pulse values
### Servo moves to wrong positions
- Adjust `SPINDLE_SERVO_MIN_PULSE` and `SPINDLE_SERVO_MAX_PULSE`
- Check that `SPINDLE_MIN_RPM` and `SPINDLE_MAX_RPM` are set correctly
- Verify S values in G-code are within MIN/MAX RPM range
### Servo doesn't respond at all
- Verify `VARIABLE_SPINDLE` is enabled
- Check `USE_SPINDLE_SERVO_MODE` is uncommented
- Confirm connection to Pin D11
- Test PWM output with logic analyzer or oscilloscope
## Example: Pen Plotter
```c
// config.h settings for pen plotter
#define VARIABLE_SPINDLE
#define USE_SPINDLE_SERVO_MODE
#define SPINDLE_MAX_RPM 1000.0
#define SPINDLE_MIN_RPM 0.0
#define SPINDLE_SERVO_MIN_PULSE 16 // S0 = Pen UP (~1ms pulse)
#define SPINDLE_SERVO_MAX_PULSE 31 // S1000 = Pen DOWN (~2ms pulse)
```
```gcode
; Pen up (keeping servo powered)
M3 S0
; Move to start position
G0 X10 Y10
; Pen down
M3 S1000
; Draw square
G1 X20 Y10 F1000
G1 X20 Y20
G1 X10 Y20
G1 X10 Y10
; Pen up (keeping servo powered)
M3 S0
```
**Pen Plotter Commands:**
- **M3 S0** = Pen UP (16 ticks / ~1ms / MIN_PULSE - servo powered)
- **M3 S1000** = Pen DOWN (31 ticks / ~2ms / MAX_PULSE - servo powered)
- **M5** = Disable PWM (servo unpowered)
**Servo Direction:**
- Default configuration: S0 = pen up (MIN_PULSE), S1000 = pen down (MAX_PULSE)
- If your servo moves backwards, swap the MIN and MAX pulse values
- Fine-tune the exact pulse values (16-31 range) to match your servo's physical limits

View File

@@ -253,7 +253,7 @@
// equally divided voltage bins between the maximum and minimum spindle speeds. So for a 5V pin, 1000
// max rpm, and 250 min rpm, the spindle output voltage would be set for the following "S" commands:
// "S1000" @ 5V, "S250" @ 0.02V, and "S625" @ 2.5V (mid-range). The pin outputs 0V when disabled.
#define SPINDLE_MAX_RPM 1200.0 // Max spindle RPM. This value is equal to 100% duty cycle on the PWM.
#define SPINDLE_MAX_RPM 1000.0 // Max spindle RPM. This value is equal to 100% duty cycle on the PWM.
#define SPINDLE_MIN_RPM 0.0 // Min spindle RPM. This value is equal to (1/256) duty cycle on the PWM.
// Used by variable spindle output only. This forces the PWM output to a minimum duty cycle when enabled.

View File

@@ -45,38 +45,22 @@ void spindle_init()
void spindle_stop()
{
#ifdef USE_SPINDLE_SERVO_MODE
// Servo mode: M5 moves servo to maximum position (e.g., pen up) instead of disabling PWM
// This keeps the servo powered and in position
TCCRA_REGISTER = (1<<COMB_BIT) | (1<<WAVE1_REGISTER) | (1<<WAVE0_REGISTER);
TCCRB_REGISTER = (TCCRB_REGISTER & 0b11111000) | 0x07; // 1/1024 prescaler
OCR_REGISTER = SPINDLE_SERVO_MAX_PULSE; // Set to maximum position
// Disable PWM output (both servo and standard modes)
#ifdef VARIABLE_SPINDLE
TCCRA_REGISTER &= ~(1<<COMB_BIT); // Disable PWM. Output voltage is zero.
#if defined(CPU_MAP_ATMEGA2560) || defined(USE_SPINDLE_DIR_AS_ENABLE_PIN)
#ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT);
#else
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT);
#endif
#endif
#else
// Standard spindle mode: Disable PWM output
#ifdef VARIABLE_SPINDLE
TCCRA_REGISTER &= ~(1<<COMB_BIT); // Disable PWM. Output voltage is zero.
#if defined(CPU_MAP_ATMEGA2560) || defined(USE_SPINDLE_DIR_AS_ENABLE_PIN)
#ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT); // Set pin to high
#else
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // Set pin to low
#endif
#endif
#else
#ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT); // Set pin to high
#else
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // Set pin to low
#endif
#endif
#else
#ifdef INVERT_SPINDLE_ENABLE_PIN
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT); // Set pin to high
#else
SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // Set pin to low
#endif
#endif
}
@@ -124,12 +108,15 @@ void spindle_set_state(uint8_t state, float rpm)
else {
// Servo mode: Map RPM range to servo pulse width (16-31 ticks = ~1-2ms pulses)
// Each tick at 1/1024 prescaler = 64μs, so 16 ticks ≈ 1.024ms, 31 ticks ≈ 1.984ms
// INVERTED: S1000 = pen down (max pulse), S0 = pen up (min pulse)
#define SPINDLE_SERVO_RANGE (SPINDLE_SERVO_MAX_PULSE - SPINDLE_SERVO_MIN_PULSE)
#define SPINDLE_RPM_RANGE (SPINDLE_MAX_RPM-SPINDLE_MIN_RPM)
if ( rpm < SPINDLE_MIN_RPM ) { rpm = SPINDLE_MIN_RPM; }
if ( rpm > SPINDLE_MAX_RPM ) { rpm = SPINDLE_MAX_RPM; }
// Invert RPM mapping: S1000 → max pulse (pen down), S0 → min pulse (pen up)
rpm = SPINDLE_MAX_RPM - rpm;
rpm -= SPINDLE_MIN_RPM;
// Map RPM to servo pulse range
// Map inverted RPM to servo pulse range
current_pwm = floor( rpm * (SPINDLE_SERVO_RANGE / SPINDLE_RPM_RANGE) + SPINDLE_SERVO_MIN_PULSE + 0.5);
OCR_REGISTER = current_pwm; // Set PWM pin output