This project started because I was laid up, unable to surf. It was something I’d been wanting to do for a long time - I wanted to see if I could get a deeper understanding of my surfboard fin collection and why some ‘feel’ better than others. The final results showed that rear fins are a bit more flexible than side fins!
The project uses a workshop vice to clamp a fin in place, and then a load cell with a screw thread applies a lateral force to the tip of the fin, while a digital dial indicator reports the displacement of the fin tip. These two devices are inputs to an Arduino, which is used as a data logger interface to the serial terminal on the Mac.
Each fin was tested, and a force vs displacement plot was made for it. The fin was also photographed for documentation and area calculation using shareware.
The project spanned two weeks, a few hours, most days.


Bill of Materials
- Sparkfun RedBoard (Arduino)
- 10kg Beam Load Cell
- Makerverse Load Cell Amplifier (HX710)
- Digital Dial Gauge (Just a generic eBay unit; you will be using a Dremel on this)
- Workshop Vice (Bunnings)
- Bracketry - 50mm angle aluminium scrap (anything rigid really)
- Computer & Serial Program (I Used a MacBook and CoolTerm)
- A selection of analog components


Loadcell
I soldered the 4-pin header to the HX710c loadcell amplifier and connected wires to jump pins to go into the Arduino. I kept black and red together, and data (green) and clock(white) together.
I compared the data sheets for the loadcell HX710 and HX711. They have the same serial output protocol: the data line goes low, signalling it has a measurement ready, and the Arduino monitors this, and when it sees the low, it strobes the clock pin 24 times to shift the data over to the Arduino.
Connecting to the board was easy for the loadcell, I/O pin 2 was clk and I/O pin 3 was data, however getting the Mac to talk to the arduino was a beast - the problem was a driver - the sparkfun recommended CH340 (check the chip type near the connector) driver wasn't seen and the serial 14101 didn't seem to cut the mustard. A couple of attempts at installing the CH340 driver were done, but it wasn’t till I pulled out the Bluetooth serial USB key for the mouse that I could get connectivity! Let that be a lesson for ya….
I then ran the load cell calibration program, which set the zero point, and you could put in a calibration factor to get the reading to represent a mass value. The code was written for imperial pounds, so the value in code needed to be tweaked a bit - I tried dividing the 7050 value by 2.204 but that gave silly numbers, I was using a cup of water that weighed 834 grams as a reference weight, and the program only had a single decimal point, which for kilograms was a poor resolution. So I also tweaked the function call as I saw it was passing a variable to give 3 decimals
Serial.print(scale.get_units(), 3);
This gave a better level of resolution, still stable, but I realised I also had to use a large calibration_factor to get kilograms with 3 decimal places - so a scale factor of -225600 was a good value. I might tweak it with a more accurate mass and fixture, but it was repeatable, just only a tenth of the full scale of the 10 kg loadcell, so there will probably be some inaccuracies there at this early stage. But it's working!




Dial Gauge
Next step is to get the dial gauge connected, and this will be exciting because it will use the electronoobs circuit with a 3V3 supply to a 1V5 device - cleverly doing a level shift by a ground offset with a 200-ohm resistor in series with the ground.
(SPOILER: 200 ohms isn't the right value: it’s BIGGER!! (100k!!))
Look at the dial gauge plug and see if any JST connectors can be sourced and used off a bit of printer or the like.
Hit the back button to get to the full article. Note: I ended up needing a 100k resistor where the 200R resistor is


In my junk box, I found a printer stepper motor connector that mated perfectly with a small JST connector, & I soldered the salvaged socket onto the dial gauge PCB and enlarged the existing hole in the case to allow it to fit in. I used a bit of hot glue to improve the anchoring of the connector.
NEXT: find 10uF cap, find LED, 200R, test out code.
Woah… that was close! I used a 200R for the circuit, and when powered with 3V3, the dial gauge was totally lit up - all digit segments going flat out and not responding! i measured the voltage drop across it, and it was ~18mV, which meant the gauge was seeing 3V3-0.018V=3V2, MUCH bigger than the 1V5 desired operating voltage. So the Resistor had to go up. trial and error in a logarithmic way. 560, 4k, 10k, 39k, & finally 100k which hit the sweet spot of 1v8 drop, which gave the dial gauge operation. I am not sure why there is such a large difference between the schematic and observed values, but anyway… GLAD THERE WAS NO SMOKE!
Tried to upload some code. Electronoob thanks. found the right code, and it worked - the display initially blanked, and I thought I had killed it, but a zero press seemed to bring things to operation. The serial data is being displayed on the screen.
Now the fun part: merge the two codes and see if they work together - there might be a problem because the dialgauge just spews data, whereas the loadcell goes low, and waits to be clocked. I’d like to maybe add a start stop button for recording, but not sure. There might be polling clashes?
Now the real fun begins - integrating the two pieces of software to work nicely!
Whelp, my mind is blown. I feel dumber. I talked with ChatGPT and showed it the two bits of code, and asked it to merge them, and it was done. Like that. Variable name duplicates were checked, and all the stuff was put in the right places. I tweaked the output format for on-screen readability. Then I asked chat GPT to help out with adding a momentary pushbutton to toggle the data stream on and off - I told it what pins were spare on the arduino and pow, "attach switch to pin 4 and ground the other side, use internal pullup resistors, and heres the code for it all to work” this saved me days. Software is called 'FinFlex3.ino'. The code is neat; it sets a switch state variable to monitor whether to read and spit data out, toggled by the switch action.






Back to the hardware. I decided to make a shield board to hold the switch and level shift electronics and provide headers for the load cell and dial gauge. This would make it a bit more robust. The other side of the equation is that the hardware needs to fit without interfering with the screw of the force applicator, so it seems that the dial gauge on one side of the vice/fin and the loadcell on the other will be a straightforward way to measure the fins without fiddling around too much.






I addressed the load cell first, deciding to use the 5mm holes as the end pushing against the fin, as I had a long 5mm screw. I made it smooth on the end, bored a 2mm hole 6mm deep, then made a Delrin plug to go in it so as to not damage the fin. It took a long time and was probably unnecessary, but it made me happy and reminded me how uncoordinated my machining is compared to Clickspring on YouTube.
The load cell will get fixed with M4s to a bit of angle iron I have yet to find, and that will be fixed to the vice head with an M8 and a slot for vertical and rotational adjustment. Will have to eyeball it to some degree here.
The Dial gauge will be mounted to the magnetic base I have, and the only limiting thing here is the branding cast onto the moving jaw of the vice, so the angle grinder is going to be making some noise






Made a mockup of a test, with a fin clamped in the vice, and experimented with what sort of angle bracket would be used to hold the load cell in place. 50mm angle seemed best - I used 3mm aluminium as that's what was lying around. I drilled and tapped M8 onto the side of the fixed jaw for the bracket to be fixed to. I experimented with how much space was needed to be accommodated and the extra offset because of the washer's diameter. The total height of the bracket was eyeballed with a facsimile loadcell and vice grips, to work out how much material to lop off, and still have the loadcell around the tip of the fin.
With that estimation complete, a mark on the Aluminium was made for later cut off - it is easier to make a slot with a big chunk of material clamped to a saw horse rather than a fiddly thing in a vice. The angle grinder was used to create a surface for the dial gauge; it was rough and wavy, so the magnetic base had a rock on the high spots. I used 80 grit to sand that to a smoother surface, and knock the high spots off the grinding effort. The vice was then cleaned with WD40, isopropyl, and taped/masked for painting with a fluoro green, as that was the only paint I had. The bracket was slotted, cut, and the loadcell dummy position was re-checked with a couple of fins of various sizes. There are limitations on the height of small fins, so I made a workaround by allowing the load cell to pivot, and just drilled a couple of extra holes that followed a radius line, giving a 20 and 40 degree (roughly) angle to the load cell. A small amount of material had to be removed to account for the strain gauge coatings with the load cell at the 40-degree position. The main problem with this bracket is that there looks like a bit of flex, so that might attenuate the result data, so I am not sure what I’ll do there yet. Either do a calibration number with the bracket included or something, perhaps see what kilogram flex is being lost for the same displacement with a luggage scale?


Testing Day!
Set up the apparatus in the garage and trialled a couple of fins, 5mm displacement ~~3kg, just to get a look at the data and see repeatability, problems, etc.
The thing worked fine, measures to two decimal places pretty well:
| Jordy large centre | 0.641 |
| Jordy large centre | 0.639 |
| Jordy small centre | 0.672 |
| Jordy small centre | 0.678 |
The numbers above are kilograms per mm of flex. The smaller the number, the more flexible the fin.




Flex/movement in the bracket. Star washer improved greatly, reducing the deflection to ~3mm only (not 5)
Subsequent Days of testing - The first complete set of data was discarded because the application of force should be done in relatively the same spot on the fins; a few mm will give a different result on the same fin, so we need to be consistent in testing. Photos taken, and ImageJ software used to apply scale and calculate fin areas.
| Index | Name | Set Type | Fin Position | Stiffness (kg/mm) | Fin Area mm² |
| 1 | Jordy_Lrg_Tri_Side | Tri | Side | 0.597 | 10446 |
| 2 | DHD_Tri_side | Tri | Side | 0.668 | 10535 |
| 3 | TOMO_Quad_Side | Quad | Side | 0.680 | 9770 |
| 4 | JJF_Tri_Side | Tri | Side | 0.472 | 10085 |
CONCLUSION: Rear fins (green circles) seem to be generally less stiff.




Code
#include "HX711.h" // Include the HX711 library
//DOT: This includes GPT code for button start stop logging.
//DOT: removed the units from the data to save data cleaning each time in Numbers
// Caliper pin definitions
#define CLOCK_PIN 12
#define DATA_PIN 11
// HX711 pin definitions
#define LOADCELL_DOUT_PIN 3
#define LOADCELL_SCK_PIN 2
// Pushbutton pin definition
#define BUTTON_PIN 4
// Calibration factor for the HX711
#define calibration_factor -225600 // This value is obtained using the SparkFun_HX711_Calibration sketch
HX711 scale; // Create an HX711 object
char buf[20];
unsigned long tmpTime;
int sign;
int inches;
long value;
float result;
bool mm = true; // Define mm to false if you want inches values
bool logging = false; // State variable for logging
bool lastButtonState = HIGH; // Previous state of the button
unsigned long lastDebounceTime = 0; // Last time the button state changed
unsigned long debounceDelay = 50; // Debounce time
void setup() {
Serial.begin(9600);
// Caliper setup
pinMode(CLOCK_PIN, INPUT);
pinMode(DATA_PIN, INPUT);
// HX711 setup
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
scale.set_scale(calibration_factor); // Set the calibration factor
scale.tare(); // Reset the scale to 0
// Pushbutton setup
pinMode(BUTTON_PIN, INPUT_PULLUP); // Use internal pull-up resistor
Serial.println("Setup complete. Readings:");
}
void loop() {
// Read the pushbutton state
int buttonState = digitalRead(BUTTON_PIN);
// Check for button press
if (buttonState == LOW && lastButtonState == HIGH && (millis() - lastDebounceTime) > debounceDelay) {
logging = !logging; // Toggle logging state
lastDebounceTime = millis(); // Update the debounce time
if (logging) {
Serial.println("Logging started.");
} else {
Serial.println("Logging stopped.");
}
}
lastButtonState = buttonState; // Save the current button state
// Only log data if logging is enabled
if (logging) {
// Read from the caliper
while(digitalRead(CLOCK_PIN) == LOW) {}
tmpTime = micros();
while(digitalRead(CLOCK_PIN) == HIGH) {}
if ((micros() - tmpTime) < 500) return;
readCaliper();
buf[0] = ' ';
dtostrf(result, 6, 3, buf + 1);
strcat(buf, " in ");
if (mm) {
Serial.print(result); Serial.print(", "); // Combined output - this is the millimeters and a space char
} else {
Serial.print(result); Serial.print(" in, ");
}
// Read from the load cell
Serial.print(scale.get_units(), 3); // Get the weight from the scale with 3 decimal places
Serial.print(""); // Display weight in kg -unit removed for ease of data transfer & processing.
Serial.println();
}
// Introduce a delay to manage timing
delay(100); // Adjust this delay as needed for your application
}
void readCaliper() {
sign = 1;
value = 0;
inches = 0;
for (int i = 0; i < 24; i++) {
while (digitalRead(CLOCK_PIN) == LOW) {}
while (digitalRead(CLOCK_PIN) == HIGH) {}
if (digitalRead(DATA_PIN) == HIGH) {
if (i < 20) value |= (1 << i);
if (i == 20) sign = -1;
if (i == 23) inches = 1;
}
}
if (mm) {
result = (value * sign) / 100.0;
} else {
result = (value * sign) / (inches ? 2000.0 : 100.0); // Map the values for inches
}
}




