Load Cell
Load cells measure force/weight to detect object presence, evaluate structural loading, or monitor mechanical stress. The system supports a dual load cell configuration.configuration, each read through its own HX711 24-bit ADC using GPIO bit-banging. This is the most complete sensor driver on the board: it talks to real hardware with no compile gate.
Hardware Specifications
Parameter | Value |
|---|---|
Sensor Count | 2 (independent) |
ADC | HX711 24-bit, one per load cell |
Interface |
|
Gain | Channel A, gain 128 (LOAD_CELL_GAIN_PULSES = 1) |
Measurement | Force (Newtons) / Mass (grams) after calibration; raw counts always available |
|
|
Supply | HX711 VCC 2.7 to 5.5 V, GND common with the STM32 |
Wiring and Pin Map (from firmware.ioc)
Unit | HX711 DOUT (data, low = ready) | HX711 SCK (clock) |
|---|---|---|
0 | PA5 (WEIGHT_INPUT_1) | PC7 (WEIGHT_CLOCK_1) |
1 | PA6 (WEIGHT_INPUT_2) | PB5 (WEIGHT_CLOCK_2) |
Data Structure
typedef struct {
// Raw Reading
int32_t raw_counts; /* 24-bit two's complement reading */ ADC value from sensor
// Converted Measurements
float force_newtons;
// Force in Newtons (N)
float mass_grams; // Equivalent mass in grams (g)
// Calibration Parameters
float scale_newtons_per_count; //* Conversiondefault factor1.0 (N/count)passthrough) */
int32_t tare_offset_counts; // Zero-load ADC offset
// Status
bool is_calibrated; /* true once load_cell_set_scale() called */
Calibrationbool validread_ok; flag/* true if the last poll succeeded */
/* HX711 hardware binding (set by load_cell_sensor_init_hw) */
GPIO_TypeDef *dout_port;
uint16_t dout_pin;
GPIO_TypeDef *sck_port;
uint16_t sck_pin;
uint8_t gain_pulses;
} load_cell_data_t;
Initialization
Initialize Load Cells (as in main.c)
load_cell_data_t load_cell_data[2];
//* SupportUnit 20: sensors
for (size_t iDOUT = 0;PA5, iSCK <= 2;PC7. i++)Unit {1: DOUT = PA6, SCK = PB5. */
load_cell_sensor_init_hw(&load_cell_data[0], GPIOA, GPIO_PIN_5, GPIOC, GPIO_PIN_7);
load_cell_sensor_init_hw(&load_cell_data[1], GPIOA, GPIO_PIN_6, GPIOB, GPIO_PIN_5);
/* init_hw powers up the HX711 and auto-tares */
/* Alternative: load_cell_sensor_init(&load_cell_data[i]data) zero-initialises WITHOUT a
* hardware binding; poll(); }then returns RESULT_ERR_UNIMPLEMENTED */
Poll Load Cell Sensor
result_t lc_result = poll_load_cell_sensor(&load_cell_data[0]i]);
if (lc_result == RESULT_OK) {
float force = load_cell_data[0].force_newtons;
float mass = load_cell_data[0].mass_grams;
int32_t raw = load_cell_data[0].raw_counts;
}
Data Access Functions
//float Getforce, force in Newtons
result_t load_cell_get_force_newtons(
const load_cell_data_t *data,scale;
float *force_newtons
);
// Get mass in grams (estimated)
result_t load_cell_get_mass_grams(
const load_cell_data_t *data,
float *mass_grams
);
// Get raw ADC counts
result_t load_cell_get_raw_counts(
const load_cell_data_t *data,mass;
int32_t *raw_countscounts, );
// Get calibration parameters
result_t load_cell_get_calibration(
const load_cell_data_t *data,
float *scale_newtons_per_count,
int32_t *tare_offset_counts
);
// Verify sensor validity
result_t load_cell_sensor_is_valid(
const load_cell_data_t *data,tare;
bool *is_validvalid;
)load_cell_get_force_newtons(&cell, &force);
load_cell_get_mass_grams(&cell, &mass);
load_cell_get_raw_counts(&cell, &counts);
load_cell_get_calibration(&cell, &scale, &tare);
load_cell_sensor_is_valid(&cell, &valid);
Calibration Procedure
Two-Step Calibration
Step 1: Tare (Zero Load)
1./* RemoveWith allnothing loadon fromthe sensorcell: 2.average MeasureN ADCraw value:reads ADC_zeroand 3.store Setas tare_offset_countszero =offset.
ADC_zero* init_hw already does this automatically at startup. */
load_cell_tare(&cell, 10);
Step 2: Span (Known Weight)
1./* Place a known weightmass, onread sensorraw 2.counts, Measurethen:
ADC value: ADC_loaded
3. Know reference force: F_ref (Newtons)
4. Calculate scale:* scale = (F_ref - 0.0)known_force_newtons / (ADC_loadedraw_counts - ADC_zero)tare_offset_counts) 5.*/
Setload_cell_set_scale(&cell, scale_newtons_per_countnewtons_per_count); /* sets is_calibrated = scaletrue */
Measurement Formulas
// Raw force calculation
Forceforce_newtons = (raw_counts - tare_offset_counts) × scale_newtons_per_count
// Mass conversion (approximate)
Mass_gramsmass_grams = (Force_newtonsforce_newtons / 9.81)81 × 1000
≈ Force_newtons × 102.04
Dual
Note: Sensorbefore Managementcalibration
Configurationthe Example
default //scale Initializeis both sensors
for1.0 (size_tpassthrough): iraw_counts =is 0;trustworthy ibut <force_newtons 2;and i++)mass_grams {are load_cell_sensor_init(&load_cell_data[i]);not }physical //units Poll both in sequence
poll_load_cell_sensor(&load_cell_data[0]);
poll_load_cell_sensor(&load_cell_data[1]);
// Access by index
float load_0 = load_cell_data[0].force_newtons;
float load_1 = load_cell_data[1].force_newtons;yet.
Protobuf Message Format
message SensorBoardLoadCellInfo {
uint32 sensor_index; //* 0 or 1 */
float force_newtons;
float mass_grams;
int32 raw_counts;
float scale_newtons_per_count;
int32 tare_offset_counts;
bool is_calibrated;
SensorState state;
LoadCellErrorCode error_code; /* NO_ERROR, COMMUNICATION_FAILURE, INVALID_DATA */
}
Unit Conversions
From | To | Factor |
|---|---|---|
Newtons | kilograms-force (kgf) | ÷ 9.81 |
Newtons | pounds-force (lbf) | ÷ 4.448 |
grams | kilograms | ÷ 1000 |
Manual Unit Conversion Example
// Convert to pounds-force
float force_lbf = load_cell_data[0].force_newtons / 4.448f;
// Convert to kilograms
float mass_kg = load_cell_data[0].mass_grams / 1000.0f;Typical Specifications
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
Integration Notes
Supports up to 2Two independentloadunitscellpolledsensorsHardware-specificeveryADCmainimplementationForceloopconversioniteration;viaeachlinearisscalingtransmittedmodelTareinoffsetitscorrectsownforenvelopesensorwithmechanicalitszeroReal-time monitoring of structural loadssensor_index- Each sensor maintains separate calibration
Independent(tare + scale) and independent error reporting- HX711 gain/channel is selected by extra SCK pulses after the 24 data bits (1 = channel A gain 128, 2 = channel B gain 32, 3 = channel A gain 64)
- The blocking wait for data-ready can take up to 200 ms per
sensorcell per poll; keep this in mind when reducing the loop interval - A failed read maps to state ERROR with LOAD_CELL_COMMUNICATION_FAILURE; a read with implausible data maps to LOAD_CELL_INVALID_DATA