Read and save weight data from the M5Stack Unit Mini Scales (U177, I²C 0x26) to daily CSV files.
- GND (black) → Pi GND (e.g. pin 6, 9, 14, …)
- 5V (red) → Pi 5V (pin 2 or 4)
- SDA (white) → Pi SDA1 (pin 3, GPIO2)
- SCL (yellow) → Pi SCL1 (pin 5, GPIO3)
If
i2cdetectdoes not show the device at 0x26, swap SDA/SCL (it's easy to mix those).
The U177 blue LED briefly lights at power-up.
- Enable I2C
sudo raspi-config # Interface Options → I2C → Enable
sudo reboot- Install I2C tools (optional, for debugging) and Python deps
sudo apt update
sudo apt install -y i2c-tools python3-pip
pip3 install --upgrade smbus2- Ensure your user can access I2C
sudo usermod -aG i2c $USER
# log out/in or reboot for group change to take effect- Verify the device
i2cdetect -y 1
# Expect to see "26" in the table (address 0x26)- Get the code
git clone https://github.com/BioroboticsLab/bb_mini_scales
cd bb_mini_scales- Configure (optional)
Create a config.json (CLI flags override config values):
{
"data_dir": "/home/pi/scale_data",
"bus": 1,
"addr": "0x26",
"interval": 1.0,
"name": "scaleA",
"print": false,
"tare_on_start": true,
"gap": null,
"set_filters": false,
"lp_filter_enabled": 1,
"avg_filter_level": 10,
"ema_filter_alpha": 10,
"sign": 1.0
}Basic
python3 mini_scale_logger.pyWith config file
python3 mini_scale_logger.py -c config.jsonOverride config via CLI
python3 mini_scale_logger.py -c config.json --name scaleB --interval 1.0The U177 has two independent adjustments:
- Tare (offset reset): Zeroes out the current reading so an unloaded scale reads ~0 g. Volatile -- lost on power cycle. You need to tare every time the device powers up.
- GAP (gain calibration): The ratio of ADC counts per gram, stored as a float in the device’s non-volatile memory (register 0x40). Survives power cycles. Only needs to be set once per load cell.
In practice: calibrate GAP once with a known weight, then tare whenever you power-cycle or reposition the scale.
- Remove all weight from the scale.
- Run the calibration tool:
python3 test_and_calibrate_scale.py
- The script prints device info (firmware, address, current GAP, filter settings), then tares automatically.
- When prompted
Calibrate GAP with known weight? [y/N], answery. - The script reads the raw ADC at 0 g.
- Place a known weight (e.g. a 200 g calibration mass) on the scale. Wait a few seconds for it to stabilize, then press Enter.
- The script reads the raw ADC under load, then asks you to type the known weight in grams.
- It computes GAP = (adc_0g - adc_weight) / weight_grams, writes it to the device, and reads it back for confirmation.
- The script then streams 30 samples so you can verify the displayed weight matches the known mass.
After calibrating, place one or two known weights and confirm weight_f32 matches within a few tenths of a gram. If readings drift over time, re-tare (not re-calibrate) -- GAP rarely needs to change unless the load cell is swapped.
The logger (mini_scale_logger.py) always polls the physical button on the U177. Pressing it triggers scale.tare() immediately, with debounce. This is always active regardless of the tare_on_start config setting.
Useful for service mode: set tare_on_start: false so the service does not auto-tare on every restart, and use the physical button to tare when the scale is unloaded.
The config fields (in config.json, all overridable via CLI flags):
| Field | Type | Default | Description |
|---|---|---|---|
data_dir |
string | "data" |
Directory for CSV output files |
bus |
int | 1 |
I2C bus number (1 on Raspberry Pi) |
addr |
string/int | "0x26" |
I2C address of the scale |
interval |
float | 1.0 |
Seconds between samples |
name |
string | "" |
Tag inserted into CSV filename (e.g. weight_data_scaleA_...) |
print |
bool | false |
Print readings to stdout (useful for debugging, omit in production) |
tare_on_start |
bool | true |
Tare when the logger starts. Set false for service mode |
gap |
float/null | null |
If set, writes this GAP to the device on startup |
set_filters |
bool | false |
If true, applies the filter settings below on startup |
lp_filter_enabled |
0/1 | 1 |
Enable device low-pass filter |
avg_filter_level |
0-50 | 10 |
Number of samples to average (higher = smoother, slower) |
ema_filter_alpha |
0-99 | 10 |
EMA smoothing factor (higher = more smoothing) |
sign |
float | 1.0 |
Multiply weight by this value. Use -1.0 if readings are inverted |
The U177 has three on-device digital filters (register 0x80):
lp_filter_enabled(0 or 1): Built-in low-pass filter. Recommended to leave enabled.avg_filter_level(0--50): Moving average over N samples. Higher values give smoother but slower-responding readings. For static weights (e.g. beehive monitoring), 10--20 works well.ema_filter_alpha(0--99): Exponential moving average smoothing. Higher = more smoothing.
To apply filters, set "set_filters": true in config.json along with the three filter values. They are written to the device on logger startup. For safety, set them on every startup rather than relying on persistence.
Each U177 ships at address 0x26. To run multiple scales on the same I2C bus, change the address of additional units using set_i2c_address() in the driver. Alternatively, use separate I2C buses.
- Use
--name scaleA,--name scaleBto differentiate CSV filenames. - Run one logger instance per scale with separate config files (and separate systemd service files if using services).
Daily CSV files are written to data/ by default (or data_dir from config):
weight_data_[name_]YYYY-MM-DD.csv
Columns:
| Column | Description |
|---|---|
Time |
ISO 8601 timestamp with microseconds |
Weight_g |
Float weight in grams (register 0x10). Primary measurement column. |
Weight_x100_g |
Integer weight (register 0x60, value/100). Cross-check against Weight_g; if they diverge, investigate. |
RawADC |
Raw 32-bit ADC count (register 0x00). Useful for debugging calibration or hardware issues. |
A new file is created automatically at midnight.
i2cdetectdoes not show 0x26: Swap SDA/SCL wires. Verify 5V power (the unit needs 5V, not 3.3V). Check that I2C is enabled inraspi-config.- Readings are always 0.0 g: GAP may not be calibrated. Run
test_and_calibrate_scale.py. - Readings have wrong sign: Set
"sign": -1.0in config.json. - Noisy/jumping readings: Increase
avg_filter_level(e.g. 20) orema_filter_alpha(e.g. 30). Ensure the scale is on a stable surface. OSError: [Errno 121] Remote I/O error: Device not responding. Check wiring, check that nothing else is using the bus.- Service keeps restarting: Check
journalctl -u mini_scale_logger.service -f. Common causes: wrong paths in the service file, I2C permission issue (user not ini2cgroup). - Weight drifts after power cycle: Expected -- tare is volatile. Press the button or set
tare_on_start: true(only if the scale is guaranteed unloaded at boot).
A sample unit file is included in the repo as mini_scale_logger.service:
# mini_scale_logger.service (example)
[Unit]
Description=MiniScale weight logger
After=network.target
[Service]
Type=simple
User=pi
Group=pi
WorkingDirectory=/home/pi/bb_mini_scales
ExecStart=/usr/bin/python3 /home/pi/bb_mini_scales/mini_scale_logger.py -c /home/pi/bb_mini_scales/config.json
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target
Steps:
-
Edit the file in the repo to match your paths, user, and Python:
- User= / Group= (e.g., pi)
- WorkingDirectory= (e.g., /home/pi/bb_mini_scales)
- ExecStart= (ensure full paths to Python, script, and config)
-
Install the service:
From the repo directory where mini_scale_logger.service lives:
sudo cp mini_scale_logger.service /etc/systemd/system/mini_scale_logger.service
sudo systemctl daemon-reload- Enable on boot & start now:
sudo systemctl enable mini_scale_logger.service
sudo systemctl start mini_scale_logger.service- Manage / inspect:
sudo systemctl status mini_scale_logger.service
sudo journalctl -u mini_scale_logger.service -f
sudo systemctl restart mini_scale_logger.service
sudo systemctl stop mini_scale_logger.service
sudo systemctl disable mini_scale_logger.serviceImportant: If you run the logger as a service and don’t want it to re-tare on every restart, set "tare_on_start": false in config.json. You can still tare manually (e.g., with test_and_calibrate_scale.py, or by pressing the unit’s button if your logger watches it).