Community project

Real-Time Cargo Visualization

Photo of Real-Time Cargo Visualization

Pablo Gonzalez

Last updated November 11, 2026

Real-Time Cargo Visualization monitors weight distribution across seven independent cargo zones using load cells and an ESP32 microcontroller. Each zone has its own HX711 amplifier module connected to a load cell, allowing simultaneous measurement of cargo weight on different platforms or compartments.

This guide provides a complete wiring diagram showing how to connect all seven load cells to their respective HX711 modules and the ESP32, a full parts list, and step-by-step assembly instructions. The included firmware enables real-time web-based visualization of cargo weight in each zone, with calibration and data logging capabilities.

Wiring diagram

Wiring diagram for Real-Time Cargo Visualization
Loading diagram…

Gather all the parts

QtyComponent
1

HX711 Load Cell Amplifier

24-bit ADC front-end for strain-gauge load cells and weigh scales. DT/DOUT and SCK are MCU-facing pins; E+/E-/A+/A- connect to the load cell bridge.

1

Load Cell

Four-wire strain-gauge straight-bar load cell, common 5 kg hobby scale sensor. It produces a small bridge signal and should be read through a load-cell ADC/amplifier such as HX711 or NAU7802 rather than directly by a microcontroller GPIO.

1

HX711 Load Cell Amplifier

24-bit ADC front-end for strain-gauge load cells and weigh scales. DT/DOUT and SCK are MCU-facing pins; E+/E-/A+/A- connect to the load cell bridge.

1

Load Cell

Four-wire strain-gauge straight-bar load cell, common 5 kg hobby scale sensor. It produces a small bridge signal and should be read through a load-cell ADC/amplifier such as HX711 or NAU7802 rather than directly by a microcontroller GPIO.

1

HX711 Load Cell Amplifier

24-bit ADC front-end for strain-gauge load cells and weigh scales. DT/DOUT and SCK are MCU-facing pins; E+/E-/A+/A- connect to the load cell bridge.

1

Load Cell

Four-wire strain-gauge straight-bar load cell, common 5 kg hobby scale sensor. It produces a small bridge signal and should be read through a load-cell ADC/amplifier such as HX711 or NAU7802 rather than directly by a microcontroller GPIO.

1

HX711 Load Cell Amplifier

24-bit ADC front-end for strain-gauge load cells and weigh scales. DT/DOUT and SCK are MCU-facing pins; E+/E-/A+/A- connect to the load cell bridge.

1

Load Cell

Four-wire strain-gauge straight-bar load cell, common 5 kg hobby scale sensor. It produces a small bridge signal and should be read through a load-cell ADC/amplifier such as HX711 or NAU7802 rather than directly by a microcontroller GPIO.

1

HX711 Load Cell Amplifier

24-bit ADC front-end for strain-gauge load cells and weigh scales. DT/DOUT and SCK are MCU-facing pins; E+/E-/A+/A- connect to the load cell bridge.

1

Load Cell

Four-wire strain-gauge straight-bar load cell, common 5 kg hobby scale sensor. It produces a small bridge signal and should be read through a load-cell ADC/amplifier such as HX711 or NAU7802 rather than directly by a microcontroller GPIO.

1

HX711 Load Cell Amplifier

24-bit ADC front-end for strain-gauge load cells and weigh scales. DT/DOUT and SCK are MCU-facing pins; E+/E-/A+/A- connect to the load cell bridge.

1

Load Cell

Four-wire strain-gauge straight-bar load cell, common 5 kg hobby scale sensor. It produces a small bridge signal and should be read through a load-cell ADC/amplifier such as HX711 or NAU7802 rather than directly by a microcontroller GPIO.

1

HX711 Load Cell Amplifier

24-bit ADC front-end for strain-gauge load cells and weigh scales. DT/DOUT and SCK are MCU-facing pins; E+/E-/A+/A- connect to the load cell bridge.

1

Load Cell

Four-wire strain-gauge straight-bar load cell, common 5 kg hobby scale sensor. It produces a small bridge signal and should be read through a load-cell ADC/amplifier such as HX711 or NAU7802 rather than directly by a microcontroller GPIO.

Assemble it in 3 steps

1. Montar siete plataformas independientes

Fija un extremo de cada célula de carga de 5 kg a la base y el otro a su plataforma, con separadores para que la barra pueda flexar. Cada zona debe apoyar únicamente en su propia célula, sin rozar paredes ni las otras plataformas.

  • No superes 5 kg por zona contando la plataforma. Evita golpes y cargas laterales.

2. Conectar las células a sus módulos

Con el USB desconectado, une E+ y E- de cada célula a E+ y E- de su HX711 (alimentación del puente), y A+ y A- a A+ y A- (señal de peso). Identifica los hilos con la ficha de tu célula; no te fíes solo de los colores. Usa un HX711 por célula.

3. Alimentar y conectar al ESP32

Une VCC de los siete HX711 a 3V3 y sus GND a GND. DT es la línea de datos y SCK la de reloj. Para las zonas 1 a 7 conecta DT/SCK respectivamente a GPIO13/14, GPIO16/17, GPIO18/19, GPIO21/22, GPIO23/25, GPIO26/27 y GPIO32/33. Mantén cortos los cables de las células y alejados de la antena del ESP32.

  • Usa módulos HX711 compatibles con alimentación de 3.3 V. No alimentes sus señales a 5 V porque puedes dañar el ESP32.

Review all connections

1. Connections between "hx1" and "ESP32"

Functionhx1ESP32
powerVCC3V3
groundGNDGND
digitalDTGPIO 13
digitalSCKGPIO 14

2. Connections between "cell1" and "ESP32"

Functioncell1ESP32
powerE+ → HX711 Load Cell Amplifier E+EXT
groundE- → HX711 Load Cell Amplifier E-EXT
analogA+ → HX711 Load Cell Amplifier A+EXT
analogA- → HX711 Load Cell Amplifier A-EXT

3. Connections between "hx2" and "ESP32"

Functionhx2ESP32
powerVCC3V3
groundGNDGND
digitalDTGPIO 16
digitalSCKGPIO 17

4. Connections between "cell2" and "ESP32"

Functioncell2ESP32
powerE+ → HX711 Load Cell Amplifier E+EXT
groundE- → HX711 Load Cell Amplifier E-EXT
analogA+ → HX711 Load Cell Amplifier A+EXT
analogA- → HX711 Load Cell Amplifier A-EXT

5. Connections between "hx3" and "ESP32"

Functionhx3ESP32
powerVCC3V3
groundGNDGND
digitalDTGPIO 18
digitalSCKGPIO 19

6. Connections between "cell3" and "ESP32"

Functioncell3ESP32
powerE+ → HX711 Load Cell Amplifier E+EXT
groundE- → HX711 Load Cell Amplifier E-EXT
analogA+ → HX711 Load Cell Amplifier A+EXT
analogA- → HX711 Load Cell Amplifier A-EXT

7. Connections between "hx4" and "ESP32"

Functionhx4ESP32
powerVCC3V3
groundGNDGND
digitalDTGPIO 21
digitalSCKGPIO 22

8. Connections between "cell4" and "ESP32"

Functioncell4ESP32
powerE+ → HX711 Load Cell Amplifier E+EXT
groundE- → HX711 Load Cell Amplifier E-EXT
analogA+ → HX711 Load Cell Amplifier A+EXT
analogA- → HX711 Load Cell Amplifier A-EXT

9. Connections between "hx5" and "ESP32"

Functionhx5ESP32
powerVCC3V3
groundGNDGND
digitalDTGPIO 23
digitalSCKGPIO 25

10. Connections between "cell5" and "ESP32"

Functioncell5ESP32
powerE+ → HX711 Load Cell Amplifier E+EXT
groundE- → HX711 Load Cell Amplifier E-EXT
analogA+ → HX711 Load Cell Amplifier A+EXT
analogA- → HX711 Load Cell Amplifier A-EXT

11. Connections between "hx6" and "ESP32"

Functionhx6ESP32
powerVCC3V3
groundGNDGND
digitalDTGPIO 26
digitalSCKGPIO 27

12. Connections between "cell6" and "ESP32"

Functioncell6ESP32
powerE+ → HX711 Load Cell Amplifier E+EXT
groundE- → HX711 Load Cell Amplifier E-EXT
analogA+ → HX711 Load Cell Amplifier A+EXT
analogA- → HX711 Load Cell Amplifier A-EXT

13. Connections between "hx7" and "ESP32"

Functionhx7ESP32
powerVCC3V3
groundGNDGND
digitalDTGPIO 32
digitalSCKGPIO 33

14. Connections between "cell7" and "ESP32"

Functioncell7ESP32
powerE+ → HX711 Load Cell Amplifier E+EXT
groundE- → HX711 Load Cell Amplifier E-EXT
analogA+ → HX711 Load Cell Amplifier A+EXT
analogA- → HX711 Load Cell Amplifier A-EXT

Deploy the firmware

#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <Preferences.h>
#include <HX711.h>
#include <math.h>
WebServer server(80);
Preferences prefs;
HX711 hx[7];
const uint8_t dt[7]={13,16,18,21,23,26,32}, ck[7]={14,17,19,22,25,27,33};
struct Zone { double raw=0,zero=0,scale=0; float grams=0; int count=0; bool seen=false,tared=false; uint32_t last=0; } z[7];
void save(int i){String k=String(i);prefs.putDouble((k+"z").c_str(),z[i].zero);prefs.putDouble((k+"s").c_str(),z[i].scale);prefs.putBool((k+"t").c_str(),z[i].tared);}
const char page[] PROGMEM=R"HTML(<!doctype html><html lang="es"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Bodega del barco</title><style>
body{font:16px system-ui;background:#10283c;color:#eef6ff;margin:20px}h1{font-size:25px}.deck{display:flex;gap:10px;overflow:auto;border:5px solid #678b9b;border-radius:25px;padding:20px;background:#203e50}.zone{min-width:120px;flex:1}.stack{height:340px;display:flex;flex-direction:column-reverse;gap:3px;overflow:auto}.box{height:17px;min-height:17px;border:1px solid #ffcf77;background:repeating-linear-gradient(90deg,#d78427 0 12px,#b86920 12px 14px);border-radius:3px;animation:arrive .4s ease}.val{min-height:55px}button,input{padding:8px;margin:4px 0;max-width:95px}button{cursor:pointer}.bad{color:#ffbd80}@keyframes arrive{from{opacity:0;transform:translateY(-20px)}to{opacity:1;transform:none}}
</style><h1>Bodega · 7 zonas de carga</h1><p id="status">Conectando…</p><p id="total"></p><div class="deck" id="deck"></div><h2>Ajuste inicial de cada zona</h2><p>1. Deja la plataforma vacía y estable. Pulsa «Cero». 2. Coloca una masa de peso conocido, espera 3 segundos, introduce sus gramos y pulsa «Calibrar». Se guardan los ajustes aunque desconectes el USB. No hagas cero con contenedores encima.</p><p>Cada contenedor equivale a 300 g. Una célula mide el total de su zona, no la posición exacta de cada contenedor. Máximo: 5 kg incluyendo la plataforma. La pila dibujada representa una zona, no una reconstrucción de posiciones reales.</p><p id="msg"></p><script>
const deck=document.querySelector('#deck');let last=Array(7).fill(-1);
for(let i=0;i<7;i++){let d=document.createElement('section');d.className='zone';d.innerHTML=`<h3>Zona ${i+1}</h3><div class="stack" id="s${i}"></div><p class="val" id="v${i}">Sin datos</p><button onclick="act(${i},'zero')">Cero</button><br><input id="m${i}" type="number" min="1" max="4500" value="300" aria-label="Masa conocida en gramos"><br><button onclick="act(${i},'cal')">Calibrar</button>`;deck.appendChild(d)}
async function act(i,op){if(!confirm(op==='zero'?'¿La plataforma está vacía y estable?':'¿La masa conocida está colocada y estable?'))return;try{let r=await fetch('/adjust',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:`i=${i}&op=${op}&mass=${document.querySelector('#m'+i).value}`});document.querySelector('#msg').textContent=await r.text()}catch(e){document.querySelector('#msg').textContent='Sin conexión'}}
async function poll(){try{let r=await fetch('/data',{cache:'no-store'});if(!r.ok)throw Error();let a=await r.json(),sum=0,good=0;a.forEach((v,i)=>{let n=v.ok&&v.cal?v.n:-1;let text=!v.ok?'Sensor sin respuesta':!v.cal?'Pendiente de cero y calibración':`${v.g.toFixed(0)} g · ${v.n} contenedores${v.g>4800?' · CERCA DEL LÍMITE':''}${v.g< -100?' · Revisar cero':''}`;let el=document.querySelector('#v'+i);if(el.textContent!==text)el.textContent=text;if(n!==last[i]){let st=document.querySelector('#s'+i);st.replaceChildren();for(let j=0;j<Math.min(17,Math.max(0,n));j++){let b=document.createElement('div');b.className='box';st.appendChild(b)}last[i]=n}if(n>=0){sum+=n;good++}});document.querySelector('#status').textContent='En directo · actualización cada 0,5 s';document.querySelector('#total').textContent=`Total: ${sum} contenedores en ${good}/7 zonas válidas`;}catch(e){document.querySelector('#status').textContent='Conexión perdida: los últimos datos NO son actuales'}finally{setTimeout(poll,500)}}poll();
</script></html>)HTML";
void setup(){
 Serial.begin(115200);prefs.begin("bodega",false);
 for(int i=0;i<7;i++){String k=String(i);z[i].zero=prefs.getDouble((k+"z").c_str(),0);z[i].scale=prefs.getDouble((k+"s").c_str(),0);z[i].tared=prefs.getBool((k+"t").c_str(),false);hx[i].begin(dt[i],ck[i]);}
 WiFi.mode(WIFI_AP);WiFi.softAP("Bodega-Barco","Bodega300g");Serial.println(WiFi.softAPIP());
 server.on("/",HTTP_GET,[]{server.send_P(200,"text/html; charset=utf-8",page);});
 server.on("/data",HTTP_GET,[]{String s="[";for(int i=0;i<7;i++){if(i)s+=",";bool ok=z[i].seen&&millis()-z[i].last<2000;s+="{\"ok\":"+String(ok?"true":"false")+",\"cal\":"+String(z[i].tared&&z[i].scale!=0?"true":"false")+",\"g\":"+String(z[i].grams,1)+",\"n\":"+String(z[i].count)+"}";}s+="]";server.send(200,"application/json",s);});
 server.on("/adjust",HTTP_POST,[]{
 if(!server.hasArg("i")||!server.hasArg("op")){server.send(400,"text/plain","Faltan datos");return;}
 int i=server.arg("i").toInt();if(i<0||i>=7||!z[i].seen||millis()-z[i].last>2000){server.send(400,"text/plain","Sensor no disponible");return;}
 if(server.arg("op")=="zero"){z[i].zero=z[i].raw;z[i].tared=true;z[i].count=0;}
 else if(server.arg("op")=="cal"){float m=server.arg("mass").toFloat();double delta=z[i].raw-z[i].zero;if(!z[i].tared||m<=0||m>4500||fabs(delta)<100){server.send(400,"text/plain; charset=utf-8","Haz cero primero y usa una masa conocida estable de 1 a 4500 g");return;}z[i].scale=delta/m;}
 else {server.send(400,"text/plain","Operacion desconocida");return;}
 save(i);server.send(200,"text/plain; charset=utf-8","Ajuste guardado");});server.begin();
}
void loop(){server.handleClient();for(int i=0;i<7;i++){if(hx[i].is_ready()){long r=hx[i].read();z[i].raw=z[i].seen?z[i].raw*.75+r*.25:r;z[i].seen=true;z[i].last=millis();if(z[i].tared&&z[i].scale!=0){z[i].grams=(z[i].raw-z[i].zero)/z[i].scale;float g=max(0.0f,z[i].grams);int n=max(0,(int)lroundf(g/300));if(fabsf(g-z[i].count*300)>180)z[i].count=n;}}}delay(1);}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace, diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik