Connect a weight scale (RS-232 to USB)

How to connect a weight scale to your python program?

Linux/Windows
For this How-To i am using Linux (Ubuntu), but windows should roughly be the same.

Instead of opening the serial port with ser = serial.Serial('/dev/ttyUSB0') for example, use ser = serial.Serial('COM3')

Permission error
With Linux it is highly likely that you dont have permission to use the USB port by default. You can check this by entering the following line in your terminal ls -l /dev/ttyUSB*.

You can change this manually by using the following line: sudo chmod 777 /dev/ttyUSB*, but this will reset after disconnecting the USB.
To make this change permanent follow this guide: Permanent permission

Step 1: Make sure the scale has some form of serial port or a direct USB connection. (I am using a RS-232 to USB cable, but it should work the same way)

Step 2: Connect to any USB-port on your device (Linux or Windows)

Step 3: To read out of a serial port using Python you can simply use the Serial package available via the CMD or terminal:
python -m pip install pyserial

Then import it in your python script using:
import serial

For more information about pyserial see: PySerial

Now with a couple of lines of code you can write/read via a serial connection:

Linux writing to:

import serial
ser = serial.Serial(β€˜/dev/ttyUSB0’) # open serial port (linux)
print(ser.name) # check which port was really used
ser.write(b’hello’) # write a string
ser.close() # close port

Linux Reading from

with serial.Serial(β€˜/dev/ttyS1’, 19200, timeout=1) as ser: x = ser.read() # read one byte
s = ser.read(10) # read up to ten bytes (timeout)
line = ser.readline() # read a β€˜\n’ terminated line

Readline and Readlines
readline() reads up to one line, including the \n at the end. Be careful when using readline(). Do specify a timeout when opening the serial port otherwise it could block forever if no newline character is received. If the \n is missing in the return value, it returned on timeout.

readlines() tries to read β€œall” lines which is not well defined for a serial port that is still open. Therefore readlines() depends on having a timeout on the port and interprets that as EOF (end of file). It raises an exception if the port is not opened correctly. The returned list of lines do not include the \n.

Both functions call read() to get their data and the serial port timeout is acting on this function. Therefore the effective timeout, especially for readlines(), can be much larger.

Simple code example
Here is a quick code example of how you can integrate a serial port in an β€˜Scale’ class. The code inreadLine() continuously reads a line from the serial port, then tries to filter that message using regular expression (re) so that only when you receive the data/message you want (in this case a float), it will stop reading and return the data/message.

import serial 
import re

class Scale:
    # Constructor, example port: '/dev/ttyUSB0'
    def __init__(self, port='/dev/ttyUSB0'):
        self.ser = serial.Serial(port)  # open serial port
        print(f"listening on port: {self.ser.name}")         # check which port was really used
    
    # return status of the port    
    def checkStatus(self):
        return self.ser.is_open
    
    # write a message to port
    def writeTo(self, message):
        self.ser.write(message)
    
    # read continuously until the expected/wanted data is received
    def readLine(self):
        extract = 0.0
        while extract == 0.0:
            data = self.ser.readline()
            if data:
                try:
                    match = re.search(r'(\d+\.\d+)', data.decode('utf-8'))
                    if match:
                        extract = float(match.group(1))
                        print(f"Scanned weight: {extract}")
                        return extract
                except (UnicodeDecodeError, ValueError) as e:
                    print(f"Failed to parse line: {data}, Error: {e}")

You can then use this class using the following code:

scale = Scale()
data = scale.readLine()
print(data)