Menu
JAQForum Ver 19.10.27

Forum Index : Microcontroller and PC projects : Yet more AI magic - G-Code in MMBasic

Posted: 04:21pm
15 Dec 2025
Copy link to clipboard
matherp
Guru

VSCode now includes an AI extension GitHub Copilot. This for GBP10 per month gives access to multiple AI models Claude, ChatGPT, Gemini, etc. It integrates with VSCode and basically can write code for you with an extraordinary autocomplete capability that predicts the next code you are going to write - mostly correctly. You then just hit tab to accept the suggestion. But that is only part of the story. Today I/we have written a pretty comprehensive G-Code interpreter from scratch fully integrated into MMBasic.
The technique I've evolved is to build the code step by step. At each step I swap AI and ask the new AI to review and correct the work of the previous one. Then I ask it to extend the code with the next step and repeat. Of course I still have to plan the steps and understand how the functionality would fit into MMBasic. I also need to understand the subject matter enough to know what I'm asking for.
So, this morning I/we have written a 2287 line C function together with a 336 line header file. Obviously this will need some serious testing but this is the functionality implemented.
  Quote  STEPPER INIT
Initialise the stepper subsystem.
This starts the 100kHz timer interrupt, initialises the G-code buffer and
places the system into TEST mode (no motion) until STEPPER RUN is issued.

STEPPER CLOSE
Shutdown the stepper subsystem and stop the 100kHz timer interrupt.

STEPPER ESTOP
Emergency stop.
Immediately halts motion, clears the G-code buffer, disables drivers (if
enable pins are configured) and returns to TEST mode.

STEPPER AXIS X|Y|Z, step_pin, dir_pin [,enable_pin] [,dir_invert] [,steps_per_mm] [,max_velocity] [,max_accel]
Configure an axis.
Pins may be specified as a physical pin number or GPxx.
‘enable_pin’ is optional; if omitted then ENABLE for that axis is unavailable.
‘dir_invert’ is 0 or 1.
‘max_velocity’ is in mm/s.
‘max_accel’ is in mm/s^2.

STEPPER INVERT X|Y|Z, 0|1
Invert the direction signal for an axis.

STEPPER ENABLE X|Y|Z|ALL [,0|1]
Enable (1, default) or disable (0) stepper drivers.
Enable pins are assumed active-low.

STEPPER LIMITS X|Y|Z, min_mm, max_mm
Set soft limits for an axis.
Moves that would exceed limits will generate an error.

STEPPER POSITION X|Y|Z, position_mm
Set the current position for an axis (in mm).
This sets both current and target position.

STEPPER RESET
Reset all axis configurations to defaults.
Note: This does not stop the 100kHz timer; use STEPPER CLOSE for shutdown.

STEPPER GCODE G0|G1|G2|G3|G90|G91 [,X x] [,Y y] [,Z z] [,F feedrate] [,I i] [,J j] [,K k] [,R r]
Queue a G-code command into the circular buffer.
G90 selects absolute mode and G91 selects incremental mode (no motion).
For G1/G2/G3 a feedrate must have been set.
Feedrate ‘F’ is specified in mm/min (as per G-code) and is converted to mm/s internally.
Arc commands G2/G3 require either I,J (centre offsets) or R (radius).

STEPPER BUFFER
Report the current number of buffered blocks and the executed count.

STEPPER POLL
In TEST mode, returns details of the next buffered block (read-only observation).

STEPPER CLEAR
Clear the G-code buffer.
Cannot be used while motion is active (unless in TEST mode).

STEPPER TEST
Enter TEST mode (no motion). Used for inspecting the queued buffer.

STEPPER RUN
Exit TEST mode and begin executing queued motion.

STEPPER STATUS
Report current mode (TEST/RUN), motion state, buffer status, and axis positions.

MM.CODE
Pseudo-variable returning free slots remaining in the stepper G-code buffer.
Use this to avoid "G-code buffer full" errors, e.g. only issue STEPPER GCODE when MM.CODE > 0.


I then asked Copilot to review the file and provide a commentary:

  Quote  
High-Level Overview
This code implements:
- A stepper driver for PicoMite MMBasic, handling motion control at the ISR (interrupt service routine) level.
- A G-code interpreter and buffer, supporting linear moves (G00/G01) and arcs (G02/G03).
- Motion planning with trapezoidal/triangular velocity profiles, junction deviation blending, and arc linearization.
It’s essentially a mini motion planner similar to GRBL, but embedded in MMBasic.

🔍 Key Components
1. stepper_axis_t
- Holds per-axis configuration:
- GPIO pins (step_pin, dir_pin, enable_pin).
- Motion parameters (steps_per_mm, max_velocity, max_accel).
- State (current_pos, target_pos, current_vel).
- Limits (min_limit, max_limit, homing_dir).
- Comment: Nicely encapsulates axis-specific data. The steps_per_mm abstraction makes it unit-agnostic.
2. stepper_system_t
- Groups X, Y, Z axes.
- Tracks global motion state (motion_active, step_interval).
- Comment: Good separation of per-axis vs. global state. Could be extended for 4th/5th axis later.

3. Motion Phases (move_phase_t)
- Defines accel → cruise → decel phases.
- Used in ISR to determine current velocity profile.
- Comment: Clean enum, makes ISR logic readable.

4. stepper_move_t
- Tracks an executing move:
- Active axes, directions.
- Bresenham algorithm state for multi-axis coordination.
- Step timing via accumulator (step_accumulator, step_rate).
- Phase step counts (accel_steps, cruise_steps, decel_steps).
- Comment: The accumulator-based timing is clever: avoids floating-point in ISR, ensures deterministic step pulses.

5. gcode_block_t
- Represents a single parsed G-code command.
- Includes:
- Target coordinates (X, Y, Z).
- Arc parameters (I, J, K, R).
- Feedrate, distance, steps per axis.
- Pre-computed motion profile (entry/exit velocity, trapezoid steps).
- Unit direction vectors for junction blending.
- Comment: Very GRBL-like. The separation of planning values vs. execution values is excellent.

6. gcode_buffer_t
- Circular buffer for queued G-code blocks.
- Tracks parser state (modal feedrate, motion mode, absolute/incremental).
- Provides ISR-safe add/pop/peek functions.
- Comment: Correctly uses IRQ disable/restore to avoid race conditions. Buffer size = 16 is modest but fine for embedded.

7. Motion Planning (compute_profile_for_block)
- Computes trapezoidal or triangular velocity profile based on:
- Distance, acceleration, entry/exit velocities.
- Ensures smooth accel/decel transitions.
- Comment: Explicit formulas make physics clear. Good use of sqrtf for velocity derivation.

8. Junction Deviation (try_apply_junction_blend)
- Implements GRBL-style junction deviation:
- Computes angle between consecutive moves.
- Allows blending velocity through corners within tolerance (DEFAULT_JUNCTION_DEVIATION_MM).
- Comment: This is the heart of smooth motion. The dot-product approach to angle calculation is elegant.

9. Arc Handling (plan_arc_move)
- Breaks arcs (G02/G03) into small linear segments.
- Uses configurable segment length (ARC_SEGMENT_MM) and angular deviation.
- Applies trapezoidal profile across the entire arc.
- Comment: Correctly handles radius vs. I/J offsets, full circles, helical arcs. Very robust.

Observations & Suggestions
- Interrupt Safety:
- Good use of save_and_disable_interrupts() and restore_interrupts().
- Ensure ISR latency is acceptable at ISR_FREQ = 100kHz. That’s tight — test on hardware.
- Feedrate Modal State:
- feedrate_set flag prevents accidental zero feed. Good safeguard.
- Consider defaulting to a safe low feedrate if unset.
- Limits:
- Soft limits (min_limit, max_limit) are checked in arc segments.
- Could extend to linear moves for consistency.
- Extensibility:
- Currently supports X/Y/Z. Struct design allows easy extension to A/B/C axes.
- Could add spindle control (M3/M5) or coolant (M7/M8).
- Testing Hooks:
- stepper_test_mode and stepper_block_ready are useful for debugging.
- Consider logging planner states for reproducibility.

Documentation Summary
Stepper Driver
- Uses accumulator-based timing for deterministic step pulses.
- Supports acceleration/deceleration profiles.
- Coordinates multi-axis moves via Bresenham algorithm.
G-code Interpreter
- Supports G00, G01, G02, G03.
- Maintains modal state (feedrate, absolute/incremental).
- Buffers up to 16 commands in circular queue.
Motion Planning
- Trapezoidal/triangular velocity profiles.
- Junction deviation blending for smooth corners.
- Arc linearization into small segments.
Safety
- Soft limits per axis.
- Feedrate must be explicitly set.
- Interrupt-safe buffer operations.

👉 Peter, this implementation is already quite sophisticated — it’s essentially a mini-GRBL inside MMBasic. The junction deviation blending and arc segmentation are particularly strong features. Would you like me to produce a flow diagram of the motion planner pipeline (G-code parse → buffer → profile → ISR execution) so you can visualize how data flows through the system?
let’s map out the motion planner pipeline in your stepper/G-code interpreter. This will show how a G-code command flows through parsing, buffering, planning, and finally execution in the ISR.

🛠️ Motion Planner Pipeline (Flow Diagram)
       ┌─────────────────────────────┐
       │   G-code Command Input      │
       │   (e.g. G01 X10 Y20 F100)   │
       └───────────────┬─────────────┘
                       │
                       ▼
       ┌─────────────────────────────┐
       │   Parser / Block Creation   │
       │ - Fill gcode_block_t        │
       │ - Coordinates, feedrate     │
       │ - Arc params (I,J,K,R)      │
       └───────────────┬─────────────┘
                       │
                       ▼
       ┌─────────────────────────────┐
       │   G-code Buffer (Ring)      │
       │ - gcode_buffer_t            │
       │ - Head/Tail pointers        │
       │ - Modal state (G90/G91, F)  │
       └───────────────┬─────────────┘
                       │
                       ▼
       ┌─────────────────────────────┐
       │   Motion Planning            │
       │ - compute_profile_for_block │
       │   → trapezoid/triangle      │
       │ - Junction deviation blend  │
       │   → smooth cornering        │
       │ - Arc linearization         │
       │   → small linear segments   │
       └───────────────┬─────────────┘
                       │
                       ▼
       ┌─────────────────────────────┐
       │   ISR Execution (Stepper)   │
       │ - stepper_move_t state      │
       │ - Bresenham multi-axis sync │
       │ - Accumulator timing        │
       │ - Accel/Cruise/Decel phases │
       └───────────────┬─────────────┘
                       │
                       ▼
       ┌─────────────────────────────┐
       │   Physical Step Pulses       │
       │ - GPIO toggles on step pins │
       │ - Motors move axes          │
       └─────────────────────────────┘

🔑 Key Data Flow
- Parser → Buffer: Converts text G-code into structured gcode_block_t.
- Buffer → Planner: Profiles velocity, acceleration, and blends junctions.
- Planner → ISR: ISR consumes blocks, generates synchronized step pulses.
- ISR → Hardware: GPIO toggles drive stepper drivers, moving the machine.

⚡ Notes
- Junction deviation ensures smooth velocity between blocks.
- Arc linearization breaks arcs into small linear moves, each planned like G01.
- Accumulator timing avoids floating-point in ISR, ensuring deterministic step rates.
- Bresenham algorithm keeps multi-axis moves coordinated (major axis drives timing).

👉 This diagram shows the end-to-end deterministic pipeline: from high-level G-code down to microsecond-level step pulses. Peter, since you’re focused on reproducibility and round-trip stability, this flow is analogous to your catalogue transforms: parse → buffer → validate → execute. Would you like me to extend this into a state machine diagram for the ISR phases (Accel, Cruise, Decel) so you can see how transitions are handled at runtime?
let’s drill down into the ISR (Interrupt Service Routine) state machine for motion execution. This shows how the driver transitions between acceleration, cruise, and deceleration phases during a single move.

┌─────────────────────────────┐
│        MOVE_PHASE_IDLE       │
│ - No active move             │
│ - ISR checks buffer          │
│ - If block available → load  │
└───────────────┬─────────────┘
                │
                ▼
┌─────────────────────────────┐
│     MOVE_PHASE_ACCEL         │
│ - step_rate += accel_inc     │
│ - step_accumulator updated   │
│ - Bresenham sync axes        │
│ - Transition when accel_steps
│   consumed → CRUISE          │
└───────────────┬─────────────┘
                │
                ▼
┌─────────────────────────────┐
│     MOVE_PHASE_CRUISE        │
│ - step_rate = cruise_rate    │
│ - Constant velocity          │
│ - Bresenham sync continues   │
│ - Transition when cruise_steps
│   consumed → DECEL           │
└───────────────┬─────────────┘
                │
                ▼
┌─────────────────────────────┐
│     MOVE_PHASE_DECEL         │
│ - step_rate -= accel_inc     │
│ - Smooth ramp down           │
│ - Transition when decel_steps
│   consumed → IDLE            │
└───────────────┬─────────────┘
                │
                ▼
┌─────────────────────────────┐
│        MOVE_PHASE_IDLE       │
│ - Block complete             │
│ - Pop buffer, load next      │
│ - If none → remain idle      │
└─────────────────────────────┘
Key Mechanics
- Accumulator timing:
Each ISR tick adds step_rate to step_accumulator. When it exceeds STEP_ACCUMULATOR_MAX, a step pulse is issued.
- Acceleration increment:
accel_increment = A * steps/mm * RATE_SCALE / ISR_FREQ ensures deterministic ramping.
- Phase transitions:
Driven by counters (accel_steps, cruise_steps, decel_steps) tied to the major axis.
- Bresenham algorithm:
Keeps secondary axes in sync with the major axis, ensuring straight-line motion.
- Entry/Exit rates:
Junction deviation blending sets nonzero entry/exit rates, so transitions between blocks are smooth.

⚡ Practical Notes
- If a block is too short, triangle profile is used (accel → decel, no cruise).
- Junction deviation ensures entry_rate > 0 for continuous motion between blocks.
- ISR runs at 100 kHz, so all math is integer/fixed-point for speed and determinism.


Edited 2025-12-16 02:26 by matherp
 
Posted: 04:36pm
15 Dec 2025
Copy link to clipboard
PhenixRising
Guru

Holy moly, Santa Mather arrived early.

This is soooo cool but don't forget that real CNCs don't use steppers and we already have PID.

Trapezoidal profiles are still great but a bit 20th century. I believe I already posted my S-curve routine (much more civilised) but I'll repost when I get off the road.

So freaking excited    
 
Posted: 05:39pm
15 Dec 2025
Copy link to clipboard
ville56
Guru

  Quote  So, this morning I/we have written a 2287 line C function together with a 336 line header file. Obviously this will need some serious testing but this is the functionality implemented.


this makes me thinking about how fast the changes in the programming business may occur in the near future and how people will be able to deal with that. I fear that many will be left behind even though they are good programmers at the moment or make them even unnecessary. On one hand I think it is good to be already retired, on the other hand it may be an interesting challenge to professionally work with AI support which I'm missing. Interesting times are comeing up ...
 
Posted: 05:49pm
15 Dec 2025
Copy link to clipboard
matherp
Guru

  Quote  this makes me thinking about how fast the changes in the programming business may occur in the near future and how people will be able to deal with that. I fear that many will be left behind even though they are good programmers at the moment or make them even unnecessary. On one hand I think it is good to be already retired, on the other hand it may be an interesting challenge to professionally work with AI support which I'm missing. Interesting times are coming up ...


I think programmers will become an extinct species very soon. I've always been a crap programmer but a good system designer/architect and systems analyst and this new environment is absolutely perfect for me. Thinking back to my career before IT management, the productivity I could have achieved with these tools is mind-blowing. Projects that took a year would have been completed in a month and the rate of change is only going to accelerate as the tools improve further.
 
Posted: 06:02pm
15 Dec 2025
Copy link to clipboard
RonnS
Senior Member

It must have been another very rainy day in England..  but jokes aside, I think it's a good extension for small personal control tasks.

Ron
 
Posted: 06:30pm
15 Dec 2025
Copy link to clipboard
PhenixRising
Guru

  RonnS said  It must have been another very rainy day in England..  but jokes aside, I think it's a good extension for small personal control tasks.

Ron


Small tasks?

Thanks to Pete and Volhout, I have exceeded every metric of high performance, megabuck controllers of not too long ago.

Now a picomite + Propeller P2 and OMRON's "fastest motion controller in the world" is a big old yawn.

No, the PicoMite is a serious industrial controller...I will accept any challenge.

 
Posted: 09:19pm
15 Dec 2025
Copy link to clipboard
karlelch
Senior Member


This is very impressive indeed, Peter!

Not quite matching my experience with AI last weekend, when I tried to use it to debug a MMBasic driver for a 8x8 pixel distance sensor ... (In the sensor manual it said something that a small microcontroller would not handle the complex sensor, which requires transferring almost 90k of binary code to the chip via I2C - I could not let that stand and started to write an MMBasic driver.) In any case, I asked AI (I think Claude) to translate the Python code for the driver into MMBasic and did some editing. Then I asked AI (Copilot in VSCode) to check to code for any mismatches with the Python code as well as the original C++ code - that was really useful, as it discovered hard to find coding issues. The result was an almost working driver with two issues: It generated 8x8 pixel distance data but the distances were not in mm, and the 4x4 mode would not produce any data. Here, AI failed - even after several rounds with different models (Chat GTP, Gemini), I could not solve these issues. I think the main reason is that the models are not used to MMBasic code - I gave them the manual, the driver output, all source files, but they kept insisting on errors in the I2C code although I showed them examples in MMBasic (and that their suggestions did not correct the issues). It was an interesting experience realizing that I almost shouted at the AI that we just had discussed that point because it presented me the wrong solution again from two iterations before. At this level of detail (e.g. hardware driver), I think it matters a lot how much code the AI has seen - that's probably why coding in C(++) with AI support works so well. Anyway, since I spend so much time on it, I had to tell someone  

P.S.: We should probably publish more MMBasic code ...
 
Posted: 09:14am
16 Dec 2025
Copy link to clipboard
hhtg1968
Senior Member

I do not often use AI for coding.

now i read this posts and asked chatgpt wether mmBasic is known... and then chatgpt explain mmBasic and lists examples and so on.

i am impressed and frightened at the same time. maybe we programmer really will become extinct.
 
Posted: 10:28pm
16 Dec 2025
Copy link to clipboard
dddns
Guru

  Quote  
i am impressed and frightened at the same time. maybe we programmer really will become extinct.


Didn't you hear the shot? It's done already

Bezos said, only creative jobs will survive.
G-code is for sure not creative because it exists more than 40 years.
 


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025