SlackCell

Current status: Rework case and PCB

Participate in the project: Gitlab Repo

While developing my PrintLock (a 3d printed weblock for rodeolines) I realized that a loadcell or access to one is essential. Just guessing under which load something breaks is not satisfactory. Sadly load cells are quite expensive so I decided to build one on my one for approximately 60€. I went for a 1t version because the loadcell was cheap, small and I didn't know if it would ever work. 1t is enough for new school sag is swag lines and breaking my printed weblocks (at least for now) while still having a reasonable price. But this instructable should also work with higher rated load cells. Here you can see my process of building a loadcell using Arduino, some sensors and android.

In case you're not into tinkering or looking for a low error and high breaking strength load cell take look at the lineScale 2 that inspired this project.

Materials

  • Arduino Nano
  • Loadcell (DYLY-103 S type, 1t)
  • Loadcell amplifier (HX711)
  • OLED 128x64 pixel (SH1106)
  • Bluetooth module (HC-05)
  • 1k and 2k Ohm resistors
  • Battery
  • 5V Step Up Module (S09)
  • USB Charging Module (TC4056)
  • On/Off Switch
  • Wires
  • Android phone
  • Eye bolts (M16)

Tools

  • Soldering Iron
  • Optional: crimping tool
  • Fritzing
  • 3d printer
  • Arduino IDE
  • Android Studio

Hardware

Wiring

I started with a breadboard for easy prototyping. So I soldered pins to my Arduino and modules and connected them with jumper wires. In the tables you can see to which pin you have to connect them. You can choose the digital pins for the HX711 and HC-05 freely but have to adjust the accordingly. I power the arduino by a powerbank or my laptop connected to the mini USB port.

Please use a voltage divider at the RXD pin of your Bluetooth module if you don't want to fry it. For this put a 1k Ohm resistor in the RXD to Arduino line and after it a 2k Ohm resistor that is connected to ground.

Breadboard Schematic
Loadcell HX711
Red E+
Black E-
White A-
Green A+
HX711 Arduino
GND GND
DT D2
SCK D3
VCC 5V
Bluetooth Arduino
VCC 5V
GND GND
TXD D5
RXD D4
OLED Arduino
GND GND
SDA A4
SCK A5
VCC 5V
Breadboard prototype during first tests

After a few successful tries I tested the SlackCell in the field on a 65m longline. Sadly the cables disconnected while tensioning and I didn't get any data. So I soldered all connections and taped it thoroughly. I also removed the long cable of the loadcell. Instead of using a bag with all the prototype equipment I had a handy device. The whole thing was a bit delicate and I had to resolder multiple times. Everything worked like a charm but I was too scared to really use the SlackCell.

Hand soldered and taped SlackCell

PCB

To make the device sturdier and to skip all the hard work of soldering the cables I decided to design my first PCB. I tried multiple programs and ended up using Fritzing because I already used it to create the breadboard schematic and it seemed to be quite easy.

Sadly the parts I used for the breadboard view didn't have the right dimensions and I didn't want to create new parts. In the end I 3d modelled the electronics in Inventor. Gladly almost everything was available on GrabCAD. Then I spaced everything with the ruler tool in Fritzing accordingly using custom images and generic headers. So I didn't use anything of the breadboard view. Next time I'll either learn how to create parts or try Eagle, I'm using Autodesk anyway.

Finished PCB Layout

Soldering the components on the PCB is straight forward. Be careful with the OLED. Sometimes the VDD and GND pin are switched around. I used some thick cables between the battery and charging module because otherwise they got hot. Furthermore I crimped a connector so I can remove the battery.

The PCB is quite good for my first try but I have a few minor mistakes. First the holes for the HX711 are a bit too far apart. It's still possible to solder it but the pins are tilted. Second I wanted to use the powerbank controller and solder the pins of the USB-A port to the PCB. Sadly they were too flimsy and broke immediately. So I soldered wires on it but one solder pad went off after a short time. Luckily I had a S09 boost module, a USB-C charging controller and a switch from another project. So the power connection is hand wired and not on the board.

Soldered PCB

Until then I always powered the Arduino via mini-USB. With the PCB it's over the 5V pin which results in voltage fluctuations and subsequently in noise for the force measurements. Most of the time it's around 5N but spikes sometimes up to 1kN. I added a 100µF capacitor to the E+ and E- pins of the HX711 module to eliminate the noise. Furthermore I soldered a jumper between the to pins on the top right of the HX711 chip to increase the read frequency of the HX711 from 10Hz to 80Hz. There are also some modules where you can do it without soldering.

HX711 with jumper and capacitor

All in all I'm happy with my first PCB but there is room for improvement. I will rework the PCB soon™.

Case

I used Autodesk Inventor to design the case and 3d printed it. To be honest I didn't expect it to fit good enough on the first try and didn't put too much thought into it and it craves for improvements which will come with a new PCB.

I had to file a bit of the case so the lid can clip which made the clip too weak and during the first test the lid and PCB fell off. I "reengineered" the case with zipties as reinforcement. Furthermore I glued the on/off switch to the side because I was too lazy to incorporate in the first design.

First version of the case

Software

Requirements

  1. Install Arduino IDE

  2. Install the HX711 library in Arduino IDE and upload the calibration sketch on your Arduino. If you get output in the console you successfully connected the loadcell to the Arduino.

  3. Install the u8g2 library for the OLED

Calibration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// Use this sketch to calibrate your loadcell.
// You need your arduino connected to a HX711 module and a loadcell
// and a computer to read the serial output of your arduino
//
// First use it with an unloaded loadcell. LOADCELL_OFFSET should roughly equal 
// the Raw value in output. 
//
// Next measure a calibration weight that is roughly in the magnitude of your 
// usecase (for slacklines you can use your bodyweight). Don't forget to include
// all the attachments for the loadcell
//
// With the LOADCELL_OFFSET configured correctly you can now get your LOADCELL_DIVIDER right. 
// Attach the calibration weight and change the LOADCELL_DIVIDER to the recommendedDivider
// in the output

#include <stdio.h>
#include "HX711.h"

//      D2  ||  SCK   HX711
//      D4  ||  DOUT  HX711

// HX711 circuit wiring
const int LOADCELL_DOUT_PIN = 4;
const int LOADCELL_SCK_PIN = 2;
const long LOADCELL_OFFSET = 9500;
// divider to get the force in the right units. I'm using logical units [N]
const long LOADCELL_DIVIDER_N = -445;
const long LOADCELL_DIVIDER_kg = LOADCELL_DIVIDER_N * 9.81;
const long LOADCELL_DIVIDER_lb = LOADCELL_DIVIDER_N * 4.448;

// Hang yourself on the scale and weight you with and everything 
// you need for hanging you there on a trustworthy scale
const double calibrationWeight = 81.4;

const double calibrationForce = calibrationWeight * 9.81;

long maxForce = 0;
long force = 0;
long prevForce = 0;
HX711 loadcell;

void setup() {
  Serial.begin(9600);
  Serial.println("Welcome to SlackCell Calibrating!");
  Serial.print("Sketch:   ");   Serial.println(__FILE__);
  Serial.print("Uploaded: ");   Serial.println(__DATE__);

  loadcell.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  loadcell.set_offset(LOADCELL_OFFSET);
  loadcell.set_scale(LOADCELL_DIVIDER_N);
  delay(1000);
}

void loop() {
  if (loadcell.is_ready()) {
    long value = loadcell.read_average(30);
    long valueWithOffset = value - LOADCELL_OFFSET;
    long withDivider =  (value - LOADCELL_OFFSET)/LOADCELL_DIVIDER_N;
    //LOADCELL_OFFSET should roughly equal Raw -> withOffset should be 0 without any force on the loadcell
    //withDivider should equal calibration weight
    Serial.println("     Raw  withOffset  withDivider  recommendedDivider");
    char string [60];
    // doubles with sprintf didn't work :(
    sprintf(string,"%8ld  %10ld  %11ld", value, value - LOADCELL_OFFSET, withDivider);
    Serial.print(string);
    Serial.print("         ");    
    Serial.println((value - LOADCELL_OFFSET)/ calibrationForce);
  }  
}

Calibration sketch

Manual

Calibration right now is tedious. But you only have to do it once (in a while). You need to configure two variables for the loadcell LOADCELL_OFFSET and LOADCELL_DIVIDER. The Arduino must be connected to your computer.

  1. First use it with an unloaded loadcell. LOADCELL_OFFSET should roughly equal the Raw value in output.
  2. Next measure a calibration weight that is roughly in the magnitude of your use case (for slacklines you can use your bodyweight). Don't forget to include all the attachments for the loadcell.
  3. With the LOADCELL_OFFSET configured correctly you can now get your LOADCELL_DIVIDER right. Attach the calibration weight and change the LOADCELL_DIVIDER to the recommendedDivider in the output.

Tests

First calibration sitting in a spanset hanging from a pullup bar
More comfortable calibration option: Using a hang chair

I did a rough first calibration with my body weight scale as reference. The second calibration was done using a comfy hang chair and multiple calibration points. Actually I got the same calibration factor for both tests even though I was using different scales and during the first test I only had one reference value.

The results are surprisingly good. The measurements almost match perfectly with less than 1% deviation. But this calibration is still quite limited. It assumes that the cheap bodyweight scales are working properly. Furthermore the calibrated range is small and ends where the use case for slacklining roughly starts at around 1.5 kN.

Thanks to Lukas I had access to a LineScale 2 and the chance to calibrate the full range of my SlackCell. My loadcell was roughly 1.6% below the force measured with the LineScale. So with an adjusted value for the loadcell divider in the Arduino code I get a difference of less than 20N compared to the LineScale which is in the specified error margin of ±0.5% at 3kN-15kN of the LineScale.

So all in all I guess calibrating with a bodyweight scale can be a proper method for calibration if you don't have access to a loadcell. For most cases it won't matter if the SlackCell is a few percent of and if it would matter then you should be using professional equipment anyway. Furthermore the SlackCell yields high quality results over the full working load range with a proper calibration.

Arduino

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include "HX711.h"
#include "BT.h"
#include <U8x8lib.h>


#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif

#define OLED_RESET -1

//  ARDUINO ||  PIN   DEVICE
//      A4  ||  SDA   OLED
//      A5  ||  SCK   OLED

//      D10 ||  SCK   HX711
//      D12 ||  DOUT  HX711

//      D4  ||  RX    HC-0x
//      D5  ||  TX    HC-0x

#define DEBUG 0
#define BT_RX  4 //RX on bluetooth module
#define BT_TX  5 //TX on bluetooth module


//#define BT_RX  6 //RX on bluetooth module
//#define BT_TX  8 //TX on bluetooth module
const long baud = 38400;



// HX711 circuit wiring
const int LOADCELL_SCK_PIN = 3;
const int LOADCELL_DOUT_PIN = 2;
const long LOADCELL_OFFSET = 10900;
const long LOADCELL_DIVIDER_N = -438;
//const long LOADCELL_DIVIDER_kg = LOADCELL_DIVIDER_N * 9.81;
//const long LOADCELL_DIVIDER_lb = LOADCELL_DIVIDER_N * 4.448;

HX711 loadcell;
BT bt(BT_RX, BT_TX, baud);
//// if you get a noname product maybe you got SH1106 instead of SSD1306
U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);
////U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);
unsigned long timestamp = 0;
long maxForce = 0;
long force = 0;
long prevForce = 1;
bool recording = false;
char prev_cmd = '0';
float hue = 0;
cmd_t cmd;


void setup() {
  Serial.begin(baud);
  Serial.println("Welcome to SlackCell!");
  Serial.print("Sketch:   ");   Serial.println(__FILE__);
  Serial.print("Uploaded: ");   Serial.println(__DATE__);
  bt.begin();
  //bt.setConfig();


  u8x8.begin();
  u8x8.setPowerSave(0);
  // fontlist: https://github.com/olikraus/u8g2/wiki/fntlist8x8
  // n: numeric, f: with chars
  u8x8.setFont(u8x8_font_profont29_2x3_n );
  //u8x8.setFont(u8x8_font_inb21_2x4_f );

  //u8x8.drawString(0,0,"Slack");
  //u8x8.drawString(0,4,"Cell");
  u8x8.drawString(0, 0, "11235813");
  u8x8.drawString(0, 4, "21345589");

  loadcell.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  loadcell.set_offset(LOADCELL_OFFSET);
  loadcell.set_scale(LOADCELL_DIVIDER_N);
  force = loadcell.get_units(30);
  if (force < 30) {
    loadcell.tare();
    force = 0;
  }
  //delay(1000);
  u8x8.clear();
}

void loop() {
  bt.getCommand();
  if (bt.commandAvailable()) {
    prev_cmd = cmd.cmd;
    cmd = bt.nextCommand();
    Serial.println(cmd.cmd);
    if (cmd.cmd == 'R') {
      Serial.println("Command Recording");
      recording = true;
      u8x8.clear();
      u8x8.drawString(0, 0, "1.618033");
      u8x8.drawString(0, 4, "98874989");
    }
    if (cmd.cmd == 'S') {
      Serial.println("Command Stop Recording");
      recording = false;
      displayForce(int(force), 0);
      displayForce(maxForce, 4);
    }
    Serial.println(cmd.cmd);
    cmd.cmd = '?';
  }
  if (loadcell.is_ready()) {
    force = loadcell.get_units(1);
    Serial.print("Force:");
    Serial.println(force);
    timestamp = millis();
    // filter spikes bigger than 1.1kN
    //if (abs(force - prevForce) < 1100){
      bt.sendForce(force, timestamp);
      // if new force value available/force changes
      if (force != prevForce && !recording) {
        //display current force
        displayForce(force, 0);
        prevForce = force;
        maxForce = max(abs(force), abs(maxForce));
        // update display with maxForce only if it changes to reduce flickering
        if (maxForce == abs(force))
          displayForce(maxForce, 4);
      }
    if(maxForce != 0){
      hue = force/(float)maxForce;
    }
    else{
      hue = 0.01;
    }
    Serial.print(force);
    Serial.print(", ");
    Serial.println(hue);
    //}
  }
}


void displayForce(long force, uint8_t line) {
  // Maximum number of letters the screen can display in one row
  uint8_t maxLength = 8;
  // Long range is +-2,147,483,648
  char bufferF[12] = {};
  ltoa(force, bufferF, 10);
  // align right by padding with spaces
  char bufferLCD[maxLength + 1] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'};
  for (int i = 0; i < strlen(bufferF); i++) {
    bufferLCD[maxLength - strlen(bufferF) + i] = bufferF[i];
  }
  u8x8.drawString(0, line, bufferLCD);
}

App

I used MIT App Inventor for developing the first app. It's aimed at beginners and therefore has many limitations. You're using blocks instead of normal code similar to scratch.

The second version of the app was developed in Kotlin and Android studio. Which felt way more familiar and powerful than playing Lego with the MIT blocks. Furthermore it offers the opportunity to build an iOS app. Right now the app displays actual force, maximum force and can record the forces in a csv file.

Results of the first tests.

Android App

LeuchteLine

The SlackCell has now the functionality of LEDs displaying the current tension of the line to create a LeuchteLine.

(Planned) Features

  • Recording forces: First successful tests conducted
  • Tare: The scales sets back to 0 on startup if force is below 30N
  • PCB: To ease soldering
  • Case: Package the delicate stuff
  • Calibration with linescale: full range calibration with something more trustworthy than a normal body weight balance
  • New kotlin app: MIT App Inventor is too limited
  • Fast reads of force
  • Calibrating/settings via app: calibration is laborious
  • Rework PCB
  • Rework case
  • Improve app

You can find further features and issues in the Gitlab repo