How to read a weight scale over RS-232/serial in Python

Based on contributions by Glenn, ThijsvanStrien.

Many lab weight scales expose an RS-232 (or USB-to-serial) interface that streams the current reading as text. This guide covers connecting to one from Python, plus a design pattern for accurately weighing a single part that’s normally attached to a robot’s end effector.

What you need

  • A scale with a serial or RS-232-to-USB interface
  • Python 3.x with pyserial: python -m pip install pyserial
  • Linux or Windows (steps are the same, only the port name differs)

Steps

1. Connect the scale and find its port

Plug the scale into a USB port (directly, or via an RS-232-to-USB cable). Install pyserial:

python -m pip install pyserial

Then import it in your script:

import serial

On Linux the port will be something like /dev/ttyUSB0; on Windows it will be a COM port, e.g. COM3.

# Linux
ser = serial.Serial('/dev/ttyUSB0')

# Windows
ser = serial.Serial('COM3')

2. Fix serial port permissions (Linux only)

On Linux you typically don’t have permission to access the USB serial device by default. Check with:

ls -l /dev/ttyUSB*

You can grant access immediately with:

sudo chmod 777 /dev/ttyUSB*

This resets every time you disconnect the USB device. For a permanent fix, see this guide on permanent USB device permissions.

3. Read from the scale

with serial.Serial('/dev/ttyUSB0', 19200, timeout=1) as ser:
    byte = ser.read()        # read one byte
    chunk = ser.read(10)     # read up to 10 bytes (respecting the timeout)
    line = ser.readline()    # read one '\n'-terminated line

:warning: Check: 19200 baud is only an example from the source — set it to match your scale’s actual configuration (check its manual or DIP switches).

Notes on the two main read methods:

  • readline() reads up to one line, including the trailing \n. Always set a timeout when opening the port — without one, readline() can block forever if no newline is ever received. If the returned data has no trailing \n, it means the read timed out before a full line arrived.
  • readlines() tries to read all currently available lines. Because “all” isn’t well defined for a port that stays open, it relies on the configured timeout to decide when to stop (treating a timeout as end-of-file). It raises an exception if the port isn’t open. Returned lines don’t include the \n.

Both methods call read() internally, and the port’s timeout applies there — so the effective timeout for readlines() in particular can end up much larger than you might expect.

4. Wrap the connection in a class

This example continuously reads lines from the scale and extracts the first floating-point number it finds, so you get a clean weight value back:

import serial
import re


class Scale:
    def __init__(self, port='/dev/ttyUSB0'):
        self.ser = serial.Serial(port)  # open serial port
        print(f"Listening on port: {self.ser.name}")

    def check_status(self):
        return self.ser.is_open

    def write_to(self, message):
        self.ser.write(message)

    def read_line(self):
        """Read lines until one contains a float, then return it."""
        while True:
            data = self.ser.readline()
            if data:
                try:
                    match = re.search(r'(\d+\.\d+)', data.decode('utf-8'))
                    if match:
                        weight = float(match.group(1))
                        print(f"Scanned weight: {weight}")
                        return weight
                except (UnicodeDecodeError, ValueError) as e:
                    print(f"Failed to parse line: {data}, error: {e}")

Use it like this:

scale = Scale()
weight = scale.read_line()
print(weight)

Weighing a part that’s normally mounted on the end effector

If the part you need to weigh is permanently attached to a robot’s end effector (rather than a loose item you can just place on a scale), you need a way to isolate it and place it on the scale consistently:

  1. Design a way to remove the part from the end effector. One approach: use a pneumatic gripper to pick the part up and set it down. Because external forces act on the end effector during normal use, the part needs to sit tightly enough in the gripper’s hole that it doesn’t shift, while still being removable when the gripper releases it. A T-shaped hole that matches the gripper is one way to achieve both.
  2. Design a holding station for the removed part, positioned so the part always lands in the same orientation regardless of which way it happens to be facing when picked up — for example, a station the part can rotate in so its heaviest side always settles facing down, with the gripper’s mating hole always facing up.
  3. Place the holding station on a scale. In this project a scale with 0.1g accuracy was used.
  4. Read the weight from Python using the Scale class above, once the part is settled in the holding station.

:warning: Check: the original source describing this design pattern stopped short of the actual communication code between the scale and the robot controller/software — only the mechanical design (gripper hole, holding station) was documented. Use the general serial-reading approach above to complete that part; if your scale isn’t RS-232, check its manual for the correct connection method.

Related


Rewritten and consolidated (Sept 2026) from the original student how-to’s: Connect a weight scale (RS-232 to USB), How to do accurate weighing of an fixed end effector part.<.