Saturday, 20 December 2025

Designing an FPGA development board support plate with Freecad for 3D printing

I have been playing with my FPGA development boards again.  I favour the open source programming environment based upon Yosys called Icestudio.  The website is below:

Icestudio

I am a member of their forum and one of the other members wanted a support for their FPGA development board called the Alhambra II:


I already 3D printed one which was very well designed by one of the other members - Fernando Brea.

If anyone wants to buy an Alhambra II to learn FPGA development (I recommend them) they are available here:

Buy the Alhambra 2

The request came up on the forum to make a support for the board without text.  

Fernando very kindly has shared his original design on a github page:

https://github.com/fernandobrea/Alhambra_II_3D_Support

I will state that Fernando's design skills are excellent.  The function of the support without the requirement for screws and cut outs for the buttons is inspired.  I rarely do so well with mechanical design - my primary skill is with electronic design and test and not mechanical design - we cannot be great at everything...but that won't stop me trying😉

There is a version available with text and without text.  The version without text does not have the base extension which is what I believe is what the request is for - the branding on the version with text may not be to everyone's taste.

So to that end I will attempt to create a Freecad design with the base present but without text.  I'm going to share how I achieved this in the hope that others might learn from the experience.  If one is following along with this tutorial they will need to download and install Freecad:

https://wiki.freecad.org/Download

Fernando's Freecad skills far outmatch my own so in an effort to learn something I am going to reverse engineer his design and hopefully reproduce something I can work with.

The Freecad feature tree alone is a testament to the work flow:

This means I can use the information to recreate the design of the base and merge it with the support section providing what is required.  There is in my opinion no right or wrong way to do things as long as the job works and gets completed without taking too much time and money.  Too much of anything is subjective so other people may disagree...

Here is the work flow:

Load up Freecad - I'm using the Windows version but Linux and Mac are also supported.

Start a new design in Freecad in the part design workbench and call it Alhambra II 3D support AL or whatever you would like.

Next create a 'Body' - a container to 'keep' all of the design sections in one place.

Next create a new sketch on the X-Y Plane so that whatever we model is from the bottom up:


Next we need to draw the base plate which goes under the Alhambra 2 printed circuit board:

I looked at the original design and found the dimension of the base portion of the design:

The base is a rectangular shape measuring 82 mm x 75.2 mm.  It has 4 mm radiused corners and a central cut-out feature.

Lets sketch those features:


Starting with the 'Centered Rectangle Tool' - lets create the main part of the base by creating a rectangle measuring 82 mm x 75.2 mm.

The screen should look like this:


 Next we are going to add the corner radii (fillets) - Click on the create fillet tool and select each horizontal and vertical line near the corner.  Do not worry about accuracy:


Do not worry that the sketch is under constrained...we will resolve that later on.  Next we need to make all of the fillets equal.  Hold down the CTRL key and select all four fillets and then click on the 'Constrain Equal' tool or press 'E':


Now lets set a dimension to the fillets by clicking on a fillet and clicking of the smart dimension tool:


Set the fillet radius to 4 mm.  The screen should now look like above and the drawing should be green denoting that the design is fully constrained.  It won't be for long however as we are going to add the central cut out.  This next step isn't strictly necessary but it will save on plastic when we come to 3D print the design.

Click on the 'Centered Rectangle Tool' and create another rectangle within the centre of the sketch.  The rectangle dimensions are: 68.8 mm x 53 mm.


Next we are going to add another fillet to the inner rectangle on the bottom left corner as this was present on the original.  Click on the 'create fillet' tool and click on the inner horizontal and vertical lines where they intersect in the bottom left corner - do not worry about accuracy:


When you have created the fillet it should look like this:


Next we need to dimension the fillet we just created - click on the dimension tool and then click on the fillet and set it to 3.17 mm (the same dimension that Fernando used in his implementation).


In the original design there were two small holes in the top right and bottom left corners - Lets add them.  

Click on the toggle construction geometry tool:


This allows one to add reference features without actually affecting the design.  We are going to use it to place markers where the holes need to be placed.  Fernando did this slightly differently in his implementation - either way is correct.  I like construction geometry.  The holes are 10 mm vertically and horizontally offset from the outer lines of the base.  So if we create centred rectangle which measures 72 mm x 65.2 mm in construction geometry the corner points will be exactly where required in each corner. This was calculated: (82 mm - 10 mm = 72 mm) and (75.2 mm - 10 mm = 65.2 mm)

Lets create the construction rectangle:


We can now add two circles to the top right and bottom left corners of the blue dashed rectangle (construction geometry) which will position the holes where we need them.

Click on the construction geometry tool to turn it off and and then click on the create a circle by centre tool:


Then click on the bottom left vertex of the blue dashed square and create a circle - the size isn't important, repeat the same process for the top right vertex of the blue dashed square:


Next select hold down the CTRL key and select both circles and then click on the 'Constrain Equal' tool or press 'E':


Now lets dimension the circles we have just created to bring the sketch back to fully constrained - click on the dimension tool and select constrain diameter and then click on one of the circles:


Set the diameter to be 1.4 mm which gives the same as that which Fernando used in the original design.

Your sketch should now look something like this:


As the sketch is fully constrained we can exit the sketch although after all of that work I would sincerely suggest we save our work so that we don't lose everything we have created so far:


Lets exit the sketch by clicking on the close button or the leave sketch button and return to the part design work bench:


The screen should now look like this:


We are now going to create the 3D shape by extruding our design to create the base in 3D.  

Click on the 'Pad tool' and set the amount of extrusion to 3 mm:


The shape should now look like this:


If we look at the design from an isometric view point it looks like this:


I actually think this is the best bit about 3D modelling and design - seeing ones idea become reality even virtually is very rewarding.  I believe Nicola Tesla said it best:

"I do not think there is any deeper fulfilment that can capture a human heart than the feeling of an inventor seeing his ideas materialized"

Anyway...lets get on with creating the support...

At this point we can continue to model the original design from scratch or we could copy the files Fernando has shared and merge them with our base plate creating what we require.

I quite like this approach as to re-model all the features from scratch will be quite time consuming and for a beginner difficult to achieve.  I may do this myself as I have moments where I want to practice my Freecad modelling skills and this is an opportunity to do that.  However I know that me documenting those steps will make it very hard to follow for a novice so I have decided to document the short cut version.

If you haven't already please download Fernando's Freecad file for the plate design from here:

https://github.com/fernandobrea/Alhambra_II_3D_Support/blob/master/Alhambra_II_3D_support.fcstd

Open the file and navigate to this body called 'Final Without Text':


Right click and select copy:


Select all and Click 'OK'

We are going to paste this Body into our design and then merge the two things together so create one whole part.

Navigate to our recent design and right click paste next to our body:


The screen should look like this:


We now have the central support section, button and switch cut outs and stand offs which we can join with the base to create the final part.

If we look at the models in isometric we can see that there is a slight issue:


The body we have imported is sitting below the base plate.  Lets resolve that.

Click on the Body icon in the tree so that is is highlighted and then right click and select 'Transform':


Then click on the y transformation arrow and move the base down until the base of our two parts match:


Click Ok once you are happy with the positioning.  

Now...at this point I have to admit my intention was to join these two bodies together using a Boolean Fuse Operation.  That unfortunately did not work.  So I'm going to do something sneaky...

Select everything! Click on the edit menu and choose select all:


Next Click on the file menu and select Export:


Choose to export an STL file and give the file a suitable name:


Click Save.

We have now created and STL file with both bodies and can 3D print the support for our FPGA:

I loaded up the file in Cura however any slicer program will do:


I'm printing mine in PLA but any plastic would probably work as the design is not overly complex.

I will share the results once I have printed it.  Two hours and forty minutes isn't too bad.

There are other ways I could have achieved this result.  I could have exported the central section as a step file and then re-imported it into Freecad and merged the two bodies together.  The proper thing would be to model all of the features like Fernando did so that we have a clean file to begin with.  Fernando's version appears to have issues with the version of Freecad I'm using.

I sincerely doubt any of this will matter as once printed this will work perfectly well.

If people would like the files we created for their own nefarious purposes they are here:

Alhambra 2 Stand - Google Drive Link

That is all for now.... 

Take care always - Langster!




 

Monday, 15 December 2025

Updating a 25 Year old Test Rig!

I recently (about six months ago) had to update a high power test rig.  The original test rig had been in service for over 25 years and was showing its age.  It wasn't working properly due to interference from external sources which was causing the control of the test equipment to malfunction.

The rig was used to control a power converter product during temperature and vibration stress testing.  The power converter itself would receive electrical energy from an external source and then use this electricity to generate a very stable 28 V dc which was then used to power sub systems.  I cannot provide the specific function as it is proprietary.

I won't share a photo of the units under test as that is also proprietary.  I can however discuss how the test rig was originally implemented using some custom hardware and various pieces of off the shelf instrumentation to achieve the various test signals and routines required as this is fairly standard for any test rig.

The device under test is essentially a very high power switch mode buck boost converter which takes in a range of voltage inputs and provides a steady output at a range of power requirements as needed.  Various outputs are also monitored to ensure that the device under test does not change state whilst under test.

The test regime required several different test conditions to be satisfied but essentially relied on the control of two HP 6681A power supplies to provide the simulated electrical supply from the external source. The unit under test was powered by these supplies and combined with a switch box which would satisfy the input signals needed. A large external load provided the standard operating conditions the unit would be exposed to and the outputs present would be monitored by a data-logger.

The original test rig is below:

The image shows several pieces of test equipment - 5x lab power supplies which were used to power and control the test rig, the 2x high power power supplies used to provide the power to the devices under test and 2x high power resistive loads.  A Keysight data-logger is used to monitor the device under test outputs and the data is displayed on the fly on the computer monitor on the top shelf.

The critical part of the system was the device which provided the voltage profile to control the 2x large high power supplies which we shall call the profile generator.

A rough diagram of the test rig is below:

The profile generator is the key part of the system as it is that which controls the power supply to the device under test. The signal required was a slowly changing ramp signal which began at 24.5 V dc and reduced to 12 V dc in a second and then slowly increased over 18 seconds back up to 24.5 V dc simulating a specific start condition.  The unit under test had to maintain its 28 V dc output  under load during this ramp without any of the outputs being affected.  

The original profile generator was in a very sorry state.  It had been constructed on strip board and was showing its age.  The circuitry used was also very out-dated and contained 555 timers (one of my favourite devices), 741 Operational amplifiers and an TTL logic counters used to create the time period needed to produce the ramp.  The profile of the ramp itself was hard coded with a UV erasable EEPROM, the output of which was passed to a DAC08 digital to analogue converter.  I should state that I have no issue with this implementation as it had worked for many many years without issue.  It was only recently found to be affected by external interference due to the test rig being moved from one part of the building to another.  A photo of the original profile generator is below:

There was unfortunately not a great deal of documentation for the profile generator and it had been repaired on several occasions.  The operational amplifier circuitry required positive and negative supplies which had been mis-connected a few times which destroyed the linear regulators and often took out the 555 timer and the op-amps as well. 

For weeks I attempted to find out what was causing the issue with the test rig.  The profile generator output was a negative voltage signal between 0 and -5 V dc.  This signal was passed to the voltage control input of the HP 6681A which in turn controlled the output between 12 V dc and 24.5 V dc. Various probing of points within the system with an oscilloscope showed some spurious signals which no amount of filtering or cable screening could remove. A photo of an oscilloscope trace showing the spurious signal is below:


Once I realised that I couldn't fix the current rig I decided to record the output of the original profile generator and reproduce it somehow.  I disconnected the profile generator from the test rig and recorded the output with an oscilloscope and a data-logger which allowed me to obtain the profile in comma separated values.  The traces recorded by the oscilloscope are below, the yellow trace is the output from the power supply (12 V dc to 24.5 V dc), the blue trace is the voltage control signal from the profile generator (0 V dc to -5 V dc).


From this recording I tried to program an arbitrary function generator to produce the required waveform.  Unfortunately this was also affected by the same spurious interference signal which plagued the original profile generator.  

At this point I had a bit of an epiphany and decided to use the recording of the signal to program a microcontroller with a built in digital analogue converter (DAC).  As I had one available, I used an Arduino R4.  This development board is normally used by hobbyists and students but is a perfectly functional microcontroller and is very well supported and fairly easy to program.  

The specifications of the arduino R4 are here: Arduino R4 Datasheet

The Arduino R4 had a 10 bit DAC and an op-amp which I could use to buffer the signal if needed.  It was helpfully a 5 V dc device as well which meant I could interface it with the HP Power supply without any extra circuitry or voltage manipulation required.

The first thing I did was write some code to create the voltage profile required. I had to convert the recorded 5 V dc CSV data to 1023 bit values to be able to produce the profile required but this was easily achieved with a spreadsheet formula. The microcontroller firmware worked like this:

1. Check if a trigger signal has been received.

2. If trigger is present output the voltage ramp over 18 seconds creating the ramp from hard coded bit values calculated from comma separated values recorded.

3. Light an LED to show that the profile generator is active to provide visual feedback to an operator.

4. Loop back to number 1.

I then tested the system and found it worked perfectly.  I then re-read all of the test requirements and specifications to ensure I had missed something critically required.  Luckily there didn't appear to be anything insurmountable and I proceeded to write the code:

Click here to view the Arduino Code

    
//Profile Generator Mk2
//Alex Lang

// constants won't change. They're used here to set pin numbers:

const int outputPin = A0; // DAC output pin on R4 (Analog pin A0)
const int dataSize = 51;
const unsigned long totalRampTime = 48000;
const int buttonPin = 3;  // the number of the pushbutton pin
const int ledPin = 13;    // the number of the LED pin

int buttonState = 0;  // variable for reading the pushbutton status

//Voltage data (scaled to 10 bits: 0-1023)
uint16_t voltageData[dataSize] = {
  352,773,749,722,696,667,640,614,587,561,534,507,481,452,426,399,
  372,346,319,293,293,293,293,293,293,293,293,293,293,293,293,293,
  293,293,293,293,293,293,293,293,293,293,293,293,293,293,293,293,293,352
};

void setup() {
  analogWriteResolution(10); // Set analogWrite resolution to 10 bits  
  pinMode(ledPin, OUTPUT); // initialize the LED pin as an output
  pinMode(buttonPin, INPUT); // initialize the pushbutton pin as an input
}

void loop() {
analogWrite(outputPin, 352); // Output 10-bit value directly: equates to -1.71 V dc
read_Trigger();


}

void read_Trigger(){
  
  buttonState = digitalRead(buttonPin); // read the state of the pushbutton value
  
  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == HIGH) {
    // turn LED on:
    digitalWrite(ledPin, HIGH);
    Profile_start();
  } else {
    // turn LED off:
    digitalWrite(ledPin, LOW);
  }
}

void Profile_start(){
  
  unsigned long startTime = millis();
  unsigned long currentTime = startTime;
  unsigned long elapsedTime = 0;
  unsigned long delayTime = totalRampTime / dataSize;

  for (int i = 0; i < dataSize; i++) {
    analogWrite(outputPin, voltageData[i]); // Output 10-bit value directly
    elapsedTime = millis() - startTime;
    while(elapsedTime < (i+1) * delayTime){
      elapsedTime = millis() - startTime;
    }
  }
  delay(100);
}
  

  

The code worked well enough! The next stage involved checking all the product test requirements one more time with the design authority for the product as we needed to be certain that this new implementation of the profile generator wouldn't cause any issues for the reporting of the test results from the devices being tested - no issues were found.

I ended up modifying the code somewhat as it transpired that two profiles were required for the testing with two separate triggers:

Click here to view the full Arduino Code
    
//Test code for analogue voltage output via pin A0
// Author: Alexander Lang
// Date 14/05/2025
// Updated after changing PSU 1 as there appeared to be an issue.

// Variables

const int outputPin = A0; // DAC output pin on R4 (Analog pin A0).
const int apuTrigger = 5; // Trigger input from APU signal
const int pp3Trigger = 6; // Trigger input from PP3 Signal
const int dataSize = 40;  // Number of steps in APU Ramp.
const int ledPin = 13;    // Using built in LED to show when an APU trigger has been received. 
const unsigned long totalRampTime = 19000; // Time period for ramp in milli-seconds

float originalVoltageData[dataSize] = {
-2.75,-2.75,-2.7,-2.65,-2.60,-2.55,-2.50,-2.45,-2.40,-2.35,
-2.30,-2.25,-2.20,-2.15,-2.10,-2.05,-2.00,-1.95,-1.90,-1.85,
-1.80,-1.75,-1.70,-1.65,-1.60,-1.55,-1.50,-1.45,-1.40,-1.35,
-1.30,-1.25,-1.20,-1.15,-1.10,-1.10,-1.10,-1.10,-1.10,-1.10,
};

//Scaled 10-bit voltage data (0-1023)
uint16_t scaledVoltageData[dataSize];

// Setup function to set pin states

void setup() {
    Serial.begin(9600); // Set serial Monitor Running at 9600 baud
    //analogWriteResolution(12); // Set analogWrite resolution to 12 bits
    pinMode(apuTrigger, INPUT);  // Configure pin as an input
    pinMode(pp3Trigger, INPUT);  // Configure pin as an input
    pinMode(ledPin, OUTPUT);     // Configure pin as an output

    analogWriteResolution(10);   // Set analogWrite resolution to 10 bits

  //Scale the original voltage data to the 10-bit range (0-1023)
  float minValue = 0; // Find the minimum voltage
  float maxValue = -4.75; // Find the maximum voltage

  for (int i = 0; i < dataSize; i++) {
     //Use the map function to linearly scale the voltage to the 0-1023 range
    scaledVoltageData[i] = map(originalVoltageData[i] * 100, minValue * 100, maxValue * 100, 0, 1023);
     //We multiply by 100 to work with integers in the map function for better precision
  }
}

void loop() {

analogWrite(outputPin, 336); //Set output 336 to default to -1.564 V to ensure a 20.5 Volt output at the agilent PSU - CR16514
delay(100);                  //Delay to ensure no issues

readAPUTriggerState(); //Read the APU trigger state.  If a trigger has been received output the profile.
readPP3TriggerState(); //Read the APU trigger state.  If a trigger has been received output the profile.
}

void readAPUTriggerState(){
byte apuTriggerState = digitalRead(apuTrigger);

if (apuTriggerState == HIGH) {
      Serial.println("APU Trigger Received");
      digitalWrite(ledPin, HIGH);
      APU_start();
  }
  else {
      Serial.println("APU Trigger not received");
      digitalWrite(ledPin, LOW);
  }
  delay(100);
}

void readPP3TriggerState(){
byte pp3TriggerState = digitalRead(pp3Trigger);

if (pp3TriggerState == HIGH) {
      Serial.println("PP3 Trigger Received");
      digitalWrite(ledPin, HIGH);
      PP3_start();
  }
  else {
      Serial.println("PP3 Trigger not received");
      digitalWrite(ledPin, LOW);
  }
  delay(100);
}

void PP3_start(){
 unsigned long startTime = millis();
  unsigned long elapsedTime = 0;
  unsigned long delayTime = totalRampTime / dataSize;

  for (int i = 0; i < dataSize; i++) {
    Serial.print("PP3 in operation");
    Serial.println();
    analogWrite(outputPin, 200);  //Set profile generator to provide 0.93 V dc which equates to a 24.5 V dc output at the agilent PSU CR
    delay(300);
    elapsedTime = millis() - startTime;
    while (elapsedTime < (i + 1) * delayTime) {
      elapsedTime = millis() - startTime;
    }
  }
  delay(10); // Small delay at the end
}

void APU_start(){
  unsigned long startTime = millis();
  unsigned long elapsedTime = 0;
  unsigned long delayTime = totalRampTime / dataSize;

  for (int i = 0; i < dataSize; i++) {
    Serial.print(scaledVoltageData[i]);
    Serial.println();
    analogWrite(outputPin, scaledVoltageData[i]);
    delay(300);
    elapsedTime = millis() - startTime;
    while (elapsedTime < (i + 1) * delayTime) {
      elapsedTime = millis() - startTime;
    }
  }
  delay(10); // Small delay at the end
} 

I then switched to designing an enclosure for the new profile generator.  As time was of the essence and I didn't have access to machining services I couldn't use an off the shelf enclosure.  I designed something bespoke which could be 3D printed.  There are distinct benefits to doing this.  Everything fits first time without needing any machining or post processing and all the internal components should fit perfectly.

I used Freecad to design the enclosure as I didn't have access to a commercial cad package although I could have used Solidworks if more time had been available.  In previous projects I have created a parametric box design where I can set the required internal dimensions and a program then creates an enclosure and lid for me.  All that is then left to do is to create some internal support for the arduino R4 development board and holes for the various 4 mm banana connectors and LEDS present.


The 3D printer was then pressed into service and a case was created in less than a day.  Construction took less than 30 minutes and for once no further iterations were required...I will admit I often need more than one attempt at mechanical design...but that is the beauty of rapid prototyping, further iterations are cheap and easy to achieve assuming one has the time available.

The final result looks like this:

It has been in use ever since and I not heard of any issues or anything other than positive feedback from the production test department.

Because my profile generator used an off the shelf USB C power supply it was possible to remove 4x of the external lab power supplies from the test rig as these were being used to satisfy the positive and negative power requirements from the original profile generator.  These power supplies are being used in other areas within the test department.

I have been informed that the HP power supplies used originally in the test rig are to be replaced as they are 'long in the tooth'.  The new power supplies have been selected to work with the profile generator as it works exactly as intended - quite the endorsement!

That's all for now - Langster!

Wednesday, 19 November 2025

More work on the Robot Arm - EEZYBot Mk 2

 In March 2017 I 3D printed a Robot arm.  The original post is here:

https://langster1980.blogspot.com/2017/03/another-robot-arm.html

I saw the arm in amongst a pile of boxes covered in dust and looking rather sad.  In a moment of pure nostalgia...and possibly guilt I decided the robot arm needed a new lease of life and some purpose.  To be fair this is also true for me.  I rarely make time for personal projects and blog posts these days...this will hopefully change.

I also managed to find the boards I had made by Elecrow and I must have intended something as It was populated and working!  

There were various broken 3D printed parts and linkages on the Robot arm that I have 3D printed and replaced.  I also upgraded the base servo motor and the gripper.

Here is the new and improved robot arm😆


EEZYBot Mk2 - my implementation - design credits to Daguismo the original designer

The sense of satisfaction at getting this working reminded me of why I chose engineering as a profession and I really must make more time for this.

To that end I wrote up some new arduino code which I then decided to have Google Gemini assist me with.  It actually did an excellent job and provided some significant improvements.

The arduino code is here: Robot Arm Arduino Code

The code essential works as follows:

1. Prompt the user via the serial terminal as to whether they want Local potentiometer control or Serial Control

2. Handle the inputs from the serial terminal (and the Python front end)

3. Handle the inputs from the potentiometers

4. Drive the servo motors appropriately

The table below provides further information:

SectionPurposeKey Mechanism
State & ModeDefines the two control modes (M0 Serial, M1 Potentiometer).The global variable currentMode dictates which handler runs in the loop().
handleSerialControl()Reads all incoming commands (M0, S5, U90, ; comment) from Python.Uses Serial.readStringUntil('\n') to read entire commands and a switch statement to process them. It ignores lines starting with ;.
handlePotControl()Reads the analog values from the potentiometers.readSmoothAnalog() averages multiple samples to prevent noisy/jittery movement.
Target SettingUpdates the target angle (targetBase, targetUpper, etc.).setServoTarget() takes the desired angle (from serial or a pot) and updates the target variable, but does not move the servo yet.
handleRamping()Manages the speed and movement of the servos.Runs every 20ms (RAMPING\_INTERVAL\_MS). It moves the servo's current position (posBase) only a small step (maxSpeed) closer to its final target position (targetBase). This creates the smooth, speed-controlled motion.
reportCurrentPosition()Sends the arm's actual position back to the Python GUI.Sends a dedicated string (e.g., P:B85,U90,...) every 100ms to synchronize the GUI sliders with the physical arm.

I also...with Google Gemini's assistance wrote a Python front end which provides a graphical user interface to the serial terminal - very cool.  I am not a great python coder...with the AI assistance a lot was achieved.

The Python Code is here: Robot Arm Python GUI

Here is a screen shot of the GUI


Here is how the code roughly works:

FunctionPurposeConnection to Arduino
connect_serial()Establishes the connection and starts the arm.Creates the serial.Serial object. It includes a 2-second time.sleep(2) to wait for the Arduino to reboot.
initialize_arm_position()Runs the initial setup (M0, S2, B85...) to guarantee a safe, stable starting state after connection.Sends commands line-by-line using self.ser.write().
serial_reader()Runs in a separate thread to constantly listen for incoming data from the Arduino.Uses self.ser.readline() in a loop. It looks for lines starting with P:.
update_gui_position()Synchronizes the interface sliders with the physical arm position.Crucially, it ignores position updates if the GUI is set to Serial Mode (M0) to prevent the Arduino's periodic report from overriding a position you just selected on a slider.
run_sequence()Manages script execution.Runs in its own thread to prevent the GUI from freezing. It sends commands separated by the configurable Delay. It checks the self.stop_requested flag to allow immediate interruption.
record_all_positions()Creates movement script lines.Reads the current angle values from the synchronized sliders and formats them into the line-by-line command structure (e.g., U90\nL150\n).

Finally here is a video of the arm performing a pre-determined function <massive grin>


I intend doing a lot more with this Robot arm now that I have it working reasonably consistently.  I have always wanted to couple a robot arm with an AI assisted camera to perform sorting operations...so that's next.  I expect I will need to create a leader arm to assist with creating the AI learning model.

Take care as always - Langster!











Sunday, 14 September 2025

Create Wiring Harnesses using WireViz 0.41

I often need to create diagrams showing how to make wiring harnesses for test cables in work.  I have tried previous versions of WireViz and found it to be useful but needed a little more work.  I am not a coder by any stretch of the imagination and YAML (Yet Another Markup Language) syntax is new to me.

Here is the Hackaday article on WireViz:

https://hackaday.com/2020/06/23/an-open-source-tool-to-document-your-wiring/

Installing WireViz is fairly simple assuming one has Python installed properly and the system variables in the path.  Instructions for installing Python correctly here:

https://docs.python.org/3/using/windows.html#

Ensure you add the python path to the system variables...if this doesn't happen WireViz does not work.

Next grab the latest version of Wireviz....

Open a command prompt and type:

pip install wireviz

Once installed check things are working by typing:

wireviz --version

If things are working correctly the result should read:

WireViz 0.4.1

If that doesn't happen please check your python installation and system variables etc.

For reference here is the git repository for the WireViz Project - it contains useful information and example files:

https://github.com/wireviz/WireViz

If WireViz has correctly installed we can start to create a simple wiring harness diagram. Lets create a simple test cable - a BNC to screw terminals connector on one end and a red 4 mm banana connector and a black 4 mm banana connector at the other end with two red and black wires which are 24 AWG.  Lets make the cable 400 mm in length.

To describe this in YAML we create a text file.  I like to use notepad++ but one can use any text editor they wish providing it can save the file with a yml extension.  We can call it whatever we like but it is good practice to use a sensible filename.  I called mine BNC2Banana.yml.  Ensure you save it in a folder you can locate.

While we are at it lets create a folder called IMG within the same folder we are using to store the text file.  We will need this later...

Lets look at the example code provided for some context:

connectors:
  X1:
    type: D-Sub
    subtype: female
    pinlabels: [DCD, RX, TX, DTR, GND, DSR, RTS, CTS, RI]
  X2:
    type: Molex KK 254
    subtype: female
    pinlabels: [GND, RX, TX]

cables:
  W1:
    gauge: 0.25 mm2
    length: 0.2
    color_code: DIN
    wirecount: 3
    shield: true

connections:
  - - X1: [5, 2, 3]
    - W1: [1, 2, 3]
    - X2: [1, 3, 2]
  - - X1: 5
    - W1: s

Ok so the code is fairly easy to read...lets break things down:

The code creates a wiring harness with two connector types (X1 and X2) - a 9 pin D-type connector and a three pin molex kk connector with 2.54 mm pitch.  An RS232 serial cable has the following signals present: DCD, RX, TX, DTR, GND, DSR, RTS, CTS and RI.  The code makes connections to GND, RX and TX which will be present at the Molex Connector.  The wire type is set in the cables: section - the wire gauge used will be 0.25 mm squared cable and will use the DIN colour code and the cable itself will be 200 mm in length or 0.2 metres.

If we run this code the output looks like this:


We are going to edit this code for our own purposes to create our test cable:

Instead of calling the connectors X1 and X2 lets use variable names that make sense:  Lets call them what they actually are:

4mm_Red_Banana:
4mm_Black_Banana:
BNC_SCREW_Terminal:

Let us also update the type and pinlables variables to something more appropriate:

type:

Therefore our code should look like this:

connectors:
 
  4mm_Red_Banana: 4mm_Banana_Red # Red 4mm Banana
    type: 4 mm Red Banana Connector
    pinlabels: [+VE]
    
  4mm_Black_Banana: 4mm_Banana_Black # Black 4mm Banana
    type: 4 mm Black Banana Connector
    pinlabels: [GND]

  BNC_SCREW_Terminal: # Female BNC to Screw Terminals
    type: BNC Connector female - Screw Terminal
    pinlabels: [+VE, GND]

The next section of the code relates to the cable itself (cables: Section).  We want to use something sensible but we only need two wires and I would like to specify the insulation jacket colour and the length as well as the gauge.  To achieve that we can update the code as follows:

cables:
 
  C:
    colors: [RD, BK] # number of wires implicit in color list
    gauge: 0.25 mm2 # also accepts AWG as unit
    show_equiv: true # auto-calculate AWG equivalent from metric gauge
    length: 0.4 # length in m
    shield: false

I have chosen to call the cable C as opposed to W1. It makes more sense to me. Feel free to use your own variable names...

Finally we need to write the code which describes how the connectors and cables are connected which is denoted in the connectors: section...I like how simple this actually is to read...

The BNC connector pin 1 needs to connect to the cable C1 which will have a red insulation jacket and then on to the red 4 mm banana connector.  The BNC connector pin 2 needs to connect to Cable C2 which will have a black insulation jacket which in turn connects to the black 4 mm banana connector.

The code looks like this:

connections:
  -
    - BNC_SCREW_Terminal: [1]
    - C.C1: [1]
    - 4mm_Red_Banana: [1]
  -
    - BNC_SCREW_Terminal: [2]
    - C.C1: [2]
    - 4mm_Black_Banana: [1]

The entire code for reference looks like this:

connectors:
 
  4mm_Red_Banana: # Red 4mm Banana
    type: 4 mm Red Banana Connector
    pinlabels: [+VE]
    
  4mm_Black_Banana: # Black 4mm Banana
    type: 4 mm Black Banana Connector
    pinlabels: [GND]

  BNC_SCREW_Terminal: # Female BNC to Screw Terminals
    type: BNC Connector female - Screw Terminal
    pinlabels: [+VE, GND]
    
cables:
 
  C:
    colors: [RD, BK] # number of wires implicit in color list
    gauge: 0.25 mm2 # also accepts AWG as unit
    show_equiv: true # auto-calculate AWG equivalent from metric gauge
    length: 0.4 # length in m
    shield: false
 
connections:
  -
    - BNC_SCREW_Terminal: [1]
    - C.C1: [1]
    - 4mm_Red_Banana: [1]
  -
    - BNC_SCREW_Terminal: [2]
    - C.C1: [2]
    - 4mm_Black_Banana: [1]

Save the code - always a good idea!

Jump into a command prompt and navigate to the directory where you have saved your YAML code:


Type the following command:

WireViz BNC2BananaSimple.yml

If everything has gone according to plan you should see the following response:


A number of files should have been created in the folder:
  • An SVG file
  • An HTML file
  • A TSV file
  • A PNG file
If we look at the PNG file we can see our cable harness diagram:


The other files are copies of the above diagram except the TSV file which is a tab separated value file making up the bill of materials.

The above diagram should be more than enough information for a wiring person to create a wiring loom but due to the improvements in WireViz we can now add more code which in turn creates more information.

As 4 mm banana connectors are essentially all the same part apart from the number we can use some programming tricks to make the code slightly more easy to understand and we can add more information to make our diagram more comprehensive and the bill of materials more explicit:

connectors:
 
  4mm_Red_Banana: &4mm_Banana_t # https://uk.rs-online.com/web/p/banana-connectors/2080252
    pinlabels: [+VE]
    type: 4 mm Red Banana Connector
    manufacturer: RS Components
    mpn: 2080252
    supplier: uk.rs-online.com
    spn: 2080252
    subtype: male
    color: RD
    image:
      src: IMG/4mm.Red.Banana.png
 
  4mm_Black_Banana: # https://uk.rs-online.com/web/p/banana-connectors/2080251
    <<: *4mm_Banana_t
    pinlabels: [GND]
    type: 4 mm Black Banana Connector
    mpn: 2080251
    spn: 2080251
    color: BK
    image:
      src: IMG/4mm.Black.Banana.png
 
  BNC_SCREW_Terminal: # https://uk.rs-online.com/web/p/coaxial-adapters/1242521
    pinlabels: [+VE, GND]
    type: BNC Connector Male - Screw Terminal
    manufacturer: RS Components
    mpn: 1242521
    supplier: RS Components
    spn: 1242521
    subtype: female
    color: BK
    image:
      src: IMG/BNC-SCREW-TERM-MALE.png

The change in the connectors section is the line containing this:

&4mm_Banana_t

Essentially what this line of code does is create a template for the 4 mm banana connector type.   

It means that connectors of the same type but with slightly different attributes can be used without quite so much typing needed.  If we look at the next connector section it references the template with this line of code:

<<: *4mm_Banana_t

The rest of the code should be self explanatory.

We now need to obtain some images of our connectors.  I downloaded some stock images with white backgrounds:

The BNC to Screw Terminal Connector

The Red 4 mm Banana Connector

The Black 4 mm Banana Connector

Save these pictures (or find your own) into the IMG folder we created earlier.  Be sure to rename them to the same names used in the code:
  • 4mm.Red.Banana.png
  • 4mm.Black.Banana.png
  • BNC-SCREW-TERM-MALE.png
The rest of the code remains unchanged but for completeness here it is in case it is needed:
   
connectors:
 
  4mm_Red_Banana: &4mm_Banana_t # https://uk.rs-online.com/web/p/banana-connectors/2080252
    pinlabels: [+VE]
    type: 4 mm Red Banana Connector
    manufacturer: RS Components
    mpn: 2080252
    supplier: uk.rs-online.com
    spn: 2080252
    subtype: male
    color: RD
    image:
      src: IMG/4mm.Red.Banana.png
 
  4mm_Black_Banana: # https://uk.rs-online.com/web/p/banana-connectors/2080251
    <<: *4mm_Banana_t
    pinlabels: [GND]
    type: 4 mm Black Banana Connector
    mpn: 2080251
    spn: 2080251
    color: BK
    image:
      src: IMG/4mm.Black.Banana.png
 
  BNC_SCREW_Terminal: # https://uk.rs-online.com/web/p/coaxial-adapters/1242521
    pinlabels: [+VE, GND]
    type: BNC Connector Male - Screw Terminal
    manufacturer: RS Components
    mpn: 1242521
    supplier: RS Components
    spn: 1242521
    subtype: female
    color: BK
    image:
      src: IMG/BNC-SCREW-TERM-MALE.png
 
cables:
 
  C:
    colors: [RD, BK] # number of wires implicit in color list
    gauge: 0.25 mm2 # also accepts AWG as unit
    show_equiv: true # auto-calculate AWG equivalent from metric gauge
    length: 0.4 # length in m
    shield: false
    mpn: UL1007-24AWG
 
connections:
  -
    - BNC_SCREW_Terminal: [1]
    - C.C1: [1]
    - 4mm_Red_Banana: [1]
  -
    - BNC_SCREW_Terminal: [2]
    - C.C1: [2]
    - 4mm_Black_Banana: [1]

With that done we can now re-run the WireViz command and we get a different and in my opinion, a much improved diagram:


In my opinion that is a more than satisfactory diagram for a test cable harness.  There are ways to set the page size adding a header section to ensure that the code is more readable.  I would like to be able to add a template with a title block but at the moment this feature isn't working.

Here is the full code in case it might provide further insight:

metadata:
 
  title: BNC <-> 4 mm Banana
  pn: BNC2Banana01
 
  authors:
    Created: 2025-09-14
    name: Lang 
    date: 2025-09-14
    Approved:
      name: Lang 
      date: 2025-09-14
 
  revisions:
    A:
      name: Lang 
      date: 2025-09-14
      changelog: Initial commit
 
  template:
    name: din-6771
    sheetsize: A3
 
connectors:
 
  4mm_Red_Banana: &4mm_Banana_t # https://uk.rs-online.com/web/p/banana-connectors/2080252
    pinlabels: [+VE]
    type: 4 mm Red Banana Connector
    manufacturer: RS Components
    mpn: 2080252
    supplier: uk.rs-online.com
    spn: 2080252
    subtype: male
    color: RD
    image:
      src: IMG/4mm.Red.Banana.png
 
  4mm_Black_Banana: # https://uk.rs-online.com/web/p/banana-connectors/2080251
    <<: *4mm_Banana_t
    pinlabels: [GND]
    type: 4 mm Black Banana Connector
    mpn: 2080251
    spn: 2080251
    color: BK
    image:
      src: IMG/4mm.Black.Banana.png
 
  BNC_SCREW_Terminal: # https://uk.rs-online.com/web/p/coaxial-adapters/1242521
    pinlabels: [+VE, GND]
    type: BNC Connector Male - Screw Terminal
    manufacturer: RS Components
    mpn: 1242521
    supplier: RS Components
    spn: 1242521
    subtype: female
    color: BK
    image:
      src: IMG/BNC-SCREW-TERM-MALE.png
 
cables:
 
  C:
    colors: [RD, BK] # number of wires implicit in color list
    gauge: 0.25 mm2 # also accepts AWG as unit
    show_equiv: true # auto-calculate AWG equivalent from metric gauge
    length: 0.4 # length in m
    shield: false
    mpn: UL1007-24AWG
 
connections:
  -
    - BNC_SCREW_Terminal: [1]
    - C.C1: [1]
    - 4mm_Red_Banana: [1]
  -
    - BNC_SCREW_Terminal: [2]
    - C.C1: [2]
    - 4mm_Black_Banana: [1]

It is possible to use WireViz using Linux but I haven't had the time to try it.  I am confident it works just as well.

If blog readers need more examples on how to use WireViz please leave a comment below.

I think that's all for now - take care always - Langster!