Sanity Check of a program written by AI

GAP

Senior Member
Following on from this thread https://www.picaxeforum.co.uk/threads/tying-c-5-high-instead-of-low.33515/

I have asked AI to write me a program to add a calibration method for the servo settings. could someone please look at it and make any comments please.

Thank You in advance

From the AI chat

Assumptions this code uses
  • C.2 = servo
  • C.3 = point lever input
  • C.1 = UP from calibrator
  • C.4 = DOWN from calibrator
  • C.5 = calibration enable input
  • C.0 = ESP-01 status output
  • C.5 is LOW normally, HIGH when calibrator ON
  • calibration saves automatically when calibrator turns OFF
  • lever selects which end is being adjusted:
    • lever normal = adjust normalPos
    • lever thrown = adjust thrownPos

      How this code works

      Normal mode​

      • calibrator OFF​
      • lever controls the servo normally​
      • ESP-01 status output still works​

      Calibration mode​

      • calibrator ON​
      • lever chooses which end you are trimming​
      • UP/DOWN move the servo​
      • when calibrator turns OFF, the current value is saved automatically​

      Important note​

      This line in the code:

      do<br>loop while calibEn = 1<br>
      Picaxe

      means the board waits at startup until the calibrator is OFF.

      That matches the forum advice about keeping C.5 safe at power-up.

      One thing I should mention​

      The line:

      symbol tempPos = b9<br>
      Picaxe

      may need checking because on an 08M2 the number of available scratch variables depends on how PICAXE maps them.
      The program;​

' Railway Point Controller - Servo Calibration with Plug-in Calibrator
' PICAXE 08M2
#picaxe 08m2
#no_data
setfreq m4

' -----------------------------
' Pin Definitions
' -----------------------------
symbol switchPin = pinC.3 ' Point lever input
symbol upBtn = pinC.1 ' Calibrator UP button
symbol downBtn = pinC.4 ' Calibrator DOWN button
symbol calibEn = pinC.5 ' Calibrator enable input
symbol servoPin = C.2 ' Servo output
symbol esp01Pin = C.0 ' Status to ESP-01

' -----------------------------
' Variables
' -----------------------------
symbol servoposition = b4
symbol normalPos = b5
symbol thrownPos = b6
symbol calibMode = b7
symbol lastCalibMode = b8
symbol tempPos = b9

' -----------------------------
' EEPROM addresses
' -----------------------------
symbol EEPROM_NORMAL = 0
symbol EEPROM_THROWN = 1

' -----------------------------
' Startup
' -----------------------------
init:
' Load saved positions
read EEPROM_NORMAL, normalPos
read EEPROM_THROWN, thrownPos

' First-run defaults
if normalPos = 0 then
normalPos = 75
write EEPROM_NORMAL, normalPos
endif

if thrownPos = 0 then
thrownPos = 175
write EEPROM_THROWN, thrownPos
endif

' Start servo
servo servoPin, 150
pause 1000

' Safety: wait until calibrator is OFF at boot
do
loop while calibEn = 1

lastCalibMode = 0

' -----------------------------
' Main loop
' -----------------------------
main:
if calibEn = 1 then
calibMode = 1
else
calibMode = 0
endif

' --------
' Normal mode
' --------
if calibMode = 0 then
if switchPin = 1 then
servoposition = thrownPos
high esp01Pin
else
servoposition = normalPos
low esp01Pin
endif

servopos servoPin, servoposition
pause 50
goto main
endif

' --------
' Calibration mode
' --------
if calibMode = 1 then

' If we just entered calibration, give servo a moment
if lastCalibMode = 0 then
pause 300
endif

' Decide which end is being calibrated from the lever position
if switchPin = 1 then
tempPos = thrownPos
else
tempPos = normalPos
endif

' Allow button trimming while calibration is enabled
do
' Re-check calibration enable continuously
if calibEn = 0 then
' Save the adjusted value before leaving
if switchPin = 1 then
thrownPos = tempPos
write EEPROM_THROWN, thrownPos
else
normalPos = tempPos
write EEPROM_NORMAL, normalPos
endif
pause 200
lastCalibMode = 0
goto main
endif

' UP button
if upBtn = 0 then
if tempPos < 255 then
tempPos = tempPos + 1
endif
servopos servoPin, tempPos
pause 100
endif

' DOWN button
if downBtn = 0 then
if tempPos > 0 then
tempPos = tempPos - 1
endif
servopos servoPin, tempPos
pause 100
endif

' Keep servo at current trim position
servopos servoPin, tempPos
pause 20

loop

endif

lastCalibMode = calibMode
goto main
 
I've tinkered with your code. My version compiles and to my eye is working.

What's unknown is the mechanical hookup of the servo to the track and the distance to the point flag switch. You will need some input conditioning to the MCU.

Code:
#rem
    
    Railway Point Controller using a Servo
    
    Features:
    
        - One Potentiometer Calibration
        - Write only to EEPROM when changes are made
        - Reassigning the pins, C.5 is left completely empty for programming.
        - Smooth Servo Movement
        - Silence the servo annoying buzzing sound, high current draw and heat buildup.
          For Longer lifespan for the motor.
        - Map 0-255 raw pot value to a safe 75-225 servo range
                
    How Calibration Works:
    
        Since you only have one dial, the code uses your existing physical rail lever (switchPin)
        to determine which limit you are adjusting:

        * Set Calibrator enable input to on
        * Flip the rail lever UP → Turning the dial sets the thrownPos.
        * Flip the rail lever DOWN → Turning the dial sets the normalPos.
        
        Everytime you flip the rail lever the program writes to the EEPROM saving the set value.
        
    PICAXE 08M2 - AI Gemini rhb.20260806 radioSPARKS (GPL v3) 213 BYTES
    
#endrem

#picaxe 08m2
#no_data
setfreq m4

' -----------------------------
' Pin Definitions
' -----------------------------
symbol esp01Pin      = C.0    ' Status to ESP-01
symbol potPin        = C.1    ' Used as an analog input for the potentiometer
symbol servoPin      = C.2    ' Servo output
symbol switchPin     = pinC.3 ' Point lever input
symbol calibEn       = pinC.4 ' Calibrator enable input

' -----------------------------
' Variables
' -----------------------------
symbol targetPos     = b4  ' The position the servo wants to go to
symbol normalPos     = b5  ' Saved normal/straight position
symbol thrownPos     = b6  ' Saved thrown/diverging position
symbol calibMode     = b7  ' Calibration mode flag
symbol lastCalibMode = b8  ' Previous calibration mode flag
symbol tempPos       = b9  ' Active trimming position variable
symbol currentPos    = b10 ' Track the actual live position for smooth sweeps
symbol activeLever   = b11 ' Tracks lever state inside calibration
symbol potVal        = b12 ' Stores the raw 0-255 reading from the pot

' -----------------------------
' EEPROM addresses
' -----------------------------
symbol EEPROM_NORMAL = 0
symbol EEPROM_THROWN = 1

' -----------------------------
' Startup
' -----------------------------

init:
    ' Load saved positions
    read EEPROM_NORMAL, normalPos
    read EEPROM_THROWN, thrownPos

    ' First-run defaults
    if normalPos = 0 then
        normalPos = 75
        write EEPROM_NORMAL, normalPos
    endif

    if thrownPos = 0 then
        thrownPos = 175
        write EEPROM_THROWN, thrownPos
    endif

    ' Move safely to normal position on startup
    currentPos = normalPos
    servo servoPin, currentPos
    pause 1000

    ' Safety: wait until calibrator is OFF at boot
    do
    loop while calibEn = 1

    lastCalibMode = 0

' -----------------------------
' Main loop
' -----------------------------

main:

    if calibEn = 1 then
        calibMode = 1
    else
        calibMode = 0
    endif

    ' --------
    ' Normal Operating Mode (With Smooth Sweep & Auto-Silence)
    ' --------
    
    if calibMode = 0 then
        
        ' Determine target based on lever
        if switchPin = 1 then
            targetPos = thrownPos
            high esp01Pin
        else
            targetPos = normalPos
            low esp01Pin
        endif

        ' Move one step closer to target position if not there yet
        if currentPos < targetPos then
            inc currentPos
            ' Re-initialize background timer if it was shut off
            servo servoPin, currentPos
            servopos servoPin, currentPos
            pause 15 ' Increase this number for slower movement, decrease for faster
        elseif currentPos > targetPos then
            dec currentPos
            ' Re-initialize background timer if it was shut off
            servo servoPin, currentPos
            servopos servoPin, currentPos
            pause 15 ' Increase this number for slower movement, decrease for faster
        else
            ' THE SERVO HAS ARRIVED AT ITS DESTINATION
            ' Give it a brief moment to fully settle into place...
            pause 250
            ' Shut down the background pulses to silence the motor completely
            low servoPin
            
            ' Long rest delay to prevent slamming the main loop
            pause 100
        endif

        goto main
    endif

    ' --------
    ' Calibration Mode (Potentiometer Tuning)
    ' --------

    if calibMode = 1 then

        if lastCalibMode = 0 then
            pause 300
            lastCalibMode = 1
        endif

        activeLever = switchPin

        ' Continuous loop while calibration unit is active
        do
            ' 1. Check if user flipped the rail lever mid-calibration
            if switchPin <> activeLever then
                ' Save what we just finished dialing in
                if activeLever = 1 then
                    thrownPos = tempPos
                    write EEPROM_THROWN, thrownPos
                else
                    normalPos = tempPos
                    write EEPROM_NORMAL, normalPos
                endif
                
                ' Switch focus to the other position
                activeLever = switchPin
                pause 300
            endif

            ' 2. Check if calibration unit was disabled / unplugged
            if calibEn = 0 then
                if activeLever = 1 then
                    thrownPos = tempPos
                    write EEPROM_THROWN, thrownPos
                else
                    normalPos = tempPos
                    write EEPROM_NORMAL, normalPos
                endif
                pause 200
                lastCalibMode = 0
                goto main
            endif

            ' 3. Read Potentiometer and Map to Safe Servo Ranges
            readadc potPin, potVal
            
            ' Math: Map 0-255 raw pot value to a safe 75-225 servo range
            ' formula: (potVal * 150 / 255) + 75 -> simplified for PICAXE integer math:
            tempPos = potVal * 10 / 17 + 75

            ' Update live position variable and move servo instantly to match dial
            currentPos = tempPos
            servopos servoPin, currentPos
            
            pause 30

        loop
    endif

    lastCalibMode = calibMode
    
goto main
 
The mechanical element is a bike spoke less than 20mm between the servo and the point actuator.
The servo is in a device (called a point motor) that changes the rotary action of a gear on the servo to a linear action via a rack and pinion that pushes and pulls the actuator. This is mounted beside the track.
The point lever just activates a microswitch; the furthest distance to the picaxe board is less than 400mm.
At present I have a program that is working fine but the part that used the 10K pots to fine tune the blades didn't have enough travel hence this modification.
There are 9 picaxe running all the points on the layout.
The picaxe reports to a raspberry pi via an ESP01 over MQTT to change a display that shows the blade position on a screen.

What did you mean by input conditioning?
 
What did you mean by input conditioning?
AI response to question...

In PICAXE hardware, input conditioning for a microswitch ensures the microcontroller reads a clean, single electrical transition (high or low) instead of erratic electronic noise.

Because mechanical switches are physically imperfect, connecting them directly to a digital input pin without conditioning causes two major problems: floating inputs and switch bounce.

Why Microswitches Need ConditioningRC_debouncer_circuit.jpg

  • Mechanical Bounce: When a microswitch closes, its internal metal contacts literally bounce against each other for a few milliseconds. The PICAXE chip runs fast enough to misinterpret these rapid bounces as dozens of separate button presses.​
  • Floating Pins: When a microswitch is open (disconnected), the PICAXE input pin is left connected to nothing. It acts like a small antenna, picking up static electricity and randomly flipping between 1 and 0. [1]​

The Two Steps of Input Conditioning

To solve these issues, you must apply both hardware and software conditioning.

1. Hardware Conditioning (Pull-Up / Pull-Down Resistors)active_low_high_debounce copy.jpg

You must tie the PICAXE input pin to a known voltage state when the switch is open.
  • Pull-Down Configuration: Connect a 10 kΩ resistor from the PICAXE input pin to 0V (Ground), and wire the microswitch between the input pin and +V (Power). When the switch is open, the resistor forces the pin to a solid 0. When pressed, it cleanly reads 1. [2, 3]
  • Pull-Up Configuration: Connect the resistor from the pin to +V, and the switch to 0V. The pin reads a solid 1 until pressed, which drops it to 0. [4, 5]

2. Software Conditioning (Debouncing)

To stop the PICAXE from registering the physical "bouncing" of the contacts, you introduce a tiny time delay in your code to let the signal stabilize. [6]

PICAXE Basic Code Example:

Code:
main:
    if pinC.1 = 1 then switch_pressed ; Check if microswitch is hit
    goto main

switch_pressed:
    pause 20                          ; HARDWARE CONDITIONING: 20ms debounce delay
    if pinC.1 = 0 then main           ; Verify it wasn't just a brief noise spike
  
    ; [Your actual project action code goes here]
  
wait_release:
    if pinC.1 = 1 then wait_release   ; Wait for user to let go of the switch
    goto main

✅ Summary

Input conditioning for your PICAXE microswitch means using a 10 kΩ resistor to prevent a floating pin, paired with a pause 20 command in your code to ignore mechanical switch bounce.

References:
[1] https://picaxe.com
[2] https://picaxe.com
[3] https://www.youtube.com
[4] https://www.gie.com.my
[5] https://mtruhl.com
[6] https://github.com
 
Last edited:
But, of course, you need to isolate the 'hardware conditioning' circuit if you need to reprogram the PICAXE. One option is to reprogram the PICAXE on a separate board.
 
But, of course, you need to isolate the 'hardware conditioning' circuit if you need to reprogram the PICAXE. One option is to reprogram the PICAXE on a separate board.
"One option is to reprogram the PICAXE on a separate board."
I do this all the time I never program insitu all my picaxe are in sockets for ease of removal.
 
AI response to question...

In PICAXE hardware, input conditioning for a microswitch ensures the microcontroller reads a clean, single electrical transition (high or low) instead of erratic electronic noise.

Because mechanical switches are physically imperfect, connecting them directly to a digital input pin without conditioning causes two major problems: floating inputs and switch bounce.

Why Microswitches Need ConditioningView attachment 27151

  • Mechanical Bounce: When a microswitch closes, its internal metal contacts literally bounce against each other for a few milliseconds. The PICAXE chip runs fast enough to misinterpret these rapid bounces as dozens of separate button presses.​
  • Floating Pins: When a microswitch is open (disconnected), the PICAXE input pin is left connected to nothing. It acts like a small antenna, picking up static electricity and randomly flipping between 1 and 0. [1]​

The Two Steps of Input Conditioning

To solve these issues, you must apply both hardware and software conditioning.

1. Hardware Conditioning (Pull-Up / Pull-Down Resistors)View attachment 27152

You must tie the PICAXE input pin to a known voltage state when the switch is open.
  • Pull-Down Configuration: Connect a 10 kΩ resistor from the PICAXE input pin to 0V (Ground), and wire the microswitch between the input pin and +V (Power). When the switch is open, the resistor forces the pin to a solid 0. When pressed, it cleanly reads 1. [2, 3]
  • Pull-Up Configuration: Connect the resistor from the pin to +V, and the switch to 0V. The pin reads a solid 1 until pressed, which drops it to 0. [4, 5]

2. Software Conditioning (Debouncing)

To stop the PICAXE from registering the physical "bouncing" of the contacts, you introduce a tiny time delay in your code to let the signal stabilize. [6]

PICAXE Basic Code Example:

Code:
main:
    if pinC.1 = 1 then switch_pressed ; Check if microswitch is hit
    goto main

switch_pressed:
    pause 20                          ; HARDWARE CONDITIONING: 20ms debounce delay
    if pinC.1 = 0 then main           ; Verify it wasn't just a brief noise spike
 
    ; [Your actual project action code goes here]
 
wait_release:
    if pinC.1 = 1 then wait_release   ; Wait for user to let go of the switch
    goto main

✅ Summary

Input conditioning for your PICAXE microswitch means using a 10 kΩ resistor to prevent a floating pin, paired with a pause 20 command in your code to ignore mechanical switch bounce.

References:
[1] https://picaxe.com
[2] https://picaxe.com
[3] https://www.youtube.com
[4] https://www.gie.com.my
[5] https://mtruhl.com
[6] https://github.com
Could I get away with just using software and not the RC network? It would be much cleaner than adding hardware. I am trying to reduce modifying my circuit to the barest minimum so far I only have to run one wire from C.5 to a "calibrator box" to enable calibration.
 
Hi,

A "genuine" micro switch has a snap action which won't bounce for long (perhaps a few ms) and not at all on opening, so a short PAUSE could be adequate. You can use the PICaxe's internal Weak Pullup resistor to avoid an external component, if the switch is connected as "Active Low" (to Earth).

The BUTTON command is primarily intended for an Auto-Repeat facility, so IMHO not particularly useful here. Personally I always fit a "Programming Interface", at the least for its debugging capability; it need be no more than a 3-pin/pad "Legacy" header, with an input pin bridge if you don't want to allow space for even the one or two resistors on the PCB.

Cheers, Alan. (Sent from my phone)
 
As I am using momentary switches on the 2 inputs in question I went with the belt and braces approach. Instead of trying to shoehorn 2 more components onto my already crowded board I put the 2 resistors and a capacitor onto their own board which will mount close to the main board. Thanks to Radiosparks for the picture.
I have also included switch de-bounce (well AI put it in on my instructions).
Thanks for all the advice and could I ask for another sanity check of this program that I have given instruction to AI to write.

' Railway Point Controller - Servo Calibration with Plug-in Calibrator
' PICAXE 08M2
#picaxe 08m2
#no_data
setfreq m4

' -----------------------------
' Pin Definitions
' -----------------------------
symbol switchPin = pinC.3 ' Point lever switch
symbol upBtn = pinC.1 ' Conditioned UP input
symbol downBtn = pinC.4 ' Conditioned DOWN input
symbol calibEn = pinC.5 ' Calibration enable
symbol servoPin = C.2 ' Servo output
symbol esp01Pin = C.0 ' Status to ESP-01

' -----------------------------
' Variables
' -----------------------------
symbol servoposition = b4
symbol normalPos = b5
symbol thrownPos = b6
symbol calibMode = b7

' -----------------------------
' EEPROM addresses
' -----------------------------
symbol EEPROM_NORMAL = 0
symbol EEPROM_THROWN = 1

' -----------------------------
' Startup
' -----------------------------
init:
read EEPROM_NORMAL, normalPos
read EEPROM_THROWN, thrownPos

if normalPos = 0 then
normalPos = 75
write EEPROM_NORMAL, normalPos
endif

if thrownPos = 0 then
thrownPos = 175
write EEPROM_THROWN, thrownPos
endif

servo servoPin, 150
pause 1000

' Wait until calibrator is OFF at boot
do
loop while calibEn = 1

main:
if calibEn = 1 then
calibMode = 1
else
calibMode = 0
endif

if calibMode = 0 then
' -------------------------
' Normal mode
' -------------------------
if switchPin = 1 then
servoposition = thrownPos
high esp01Pin
else
servoposition = normalPos
low esp01Pin
endif

servopos servoPin, servoposition
pause 50
goto main
endif

' -------------------------
' Calibration mode
' -------------------------
if switchPin = 1 then
servoposition = thrownPos
else
servoposition = normalPos
endif

servopos servoPin, servoposition
pause 100

do
' Exit calibration when enable turns OFF
if calibEn = 0 then
if switchPin = 1 then
thrownPos = servoposition
write EEPROM_THROWN, thrownPos
else
normalPos = servoposition
write EEPROM_NORMAL, normalPos
endif
pause 200
goto main
endif

' UP button - active LOW
if upBtn = 0 then
pause 20
if upBtn = 0 then
if servoposition < 255 then
servoposition = servoposition + 1
endif
servopos servoPin, servoposition
do
pause 10
loop while upBtn = 0
endif
endif

' DOWN button - active LOW
if downBtn = 0 then
pause 20
if downBtn = 0 then
if servoposition > 0 then
servoposition = servoposition - 1
endif
servopos servoPin, servoposition
do
pause 10
loop while downBtn = 0
endif
endif

servopos servoPin, servoposition
pause 20

loop


AI Notes
  • UP and DOWN are treated as active-low inputs, which matches your conditioning board.
  • calibEn is read as HIGH = calibration ON.
  • When calibration turns OFF, the currently selected end position is written to EEPROM automatically.
  • The lever switch still selects whether you are adjusting normal or thrown.

Thank you in advance with the help
 
Back
Top