Search code examples
pythonserial-portputty

Getting b'\x08'b'\x9e'b'\x1d' or b'\xe0'b'\xe0'b' errors from a serial connection


I have an USB connection to a device that emitts serial information. When I run my Python script below (in a Jupyter Notebook) I gett weird information out of it.

import serial

ser = serial.Serial(port='COM3', baudrate=115200, bytesize=serial.EIGHTBITS, 
    parity=serial.PARITY_NONE, timeout=1)
ser.flushInput()

while True:
    print(ser.read())

When I set the baudrate to 115200 then I get the following information: b'\xe0'b'\xe0'b'\x00'b'\x00'b'\xe0'b'\x00'b'\x00'b'\xe0' ...

At 9200 I get this: b'\x08'b'\x9e'b'\x1d'b'\xca'b'L'b'k'b'\x84'b'\xff'b'\x90'b'\x8c'b'G'b'\x9b'

Does anyone know how to get real data? I should be getting information about a solar chargecontroller like

V: 12

A: 1

etc.

Edit: I have the following right now which is not giving any prints: 1st cell:

import os, serial, argparse

class vedirect:

    def __init__(self, serialport, timeout):
        self.serialport = serialport
        self.ser = serial.Serial(serialport, 19200, timeout=timeout)
        self.header1 = '\r'
        self.header2 = '\n'
        self.hexmarker = ':'
        self.delimiter = '\t'
        self.key = ''
        self.value = ''
        self.bytes_sum = 0;
        self.state = self.WAIT_HEADER
        self.dict = {}


    (HEX, WAIT_HEADER, IN_KEY, IN_VALUE, IN_CHECKSUM) = range(5)

    def input(self, byte):
        if byte == self.hexmarker and self.state != self.IN_CHECKSUM:
            self.state = self.HEX


        if self.state == self.WAIT_HEADER:
            self.bytes_sum += ord(byte)
            if byte == self.header1:
                self.state = self.WAIT_HEADER
            elif byte == self.header2:
                self.state = self.IN_KEY

            return None
        elif self.state == self.IN_KEY:
            self.bytes_sum += ord(byte)
            if byte == self.delimiter:
                if (self.key == 'Checksum'):
                    self.state = self.IN_CHECKSUM
                else:
                    self.state = self.IN_VALUE
            else:
                self.key += byte
            return None
        elif self.state == self.IN_VALUE:
            self.bytes_sum += ord(byte)
            if byte == self.header1:
                self.state = self.WAIT_HEADER
                self.dict[self.key] = self.value;
                self.key = '';
                self.value = '';
            else:
                self.value += byte
            return None
        elif self.state == self.IN_CHECKSUM:
            self.bytes_sum += ord(byte)
            self.key = ''
            self.value = ''
            self.state = self.WAIT_HEADER
            if (self.bytes_sum % 256 == 0):
                self.bytes_sum = 0
                return self.dict
            else:
                print ('Malformed packet')
                self.bytes_sum = 0
        elif self.state == self.HEX:
            self.bytes_sum = 0
            if byte == self.header2:
                self.state = self.WAIT_HEADER
        else:
            raise AssertionError()

    def read_data(self):
        while True:
            byte = self.ser.read(1)
            packet = self.input(byte)

    def read_data_single(self):
        while True:
            byte = self.ser.read(1)
            packet = self.input(byte)
            if (packet != None):
                return packet


    def read_data_callback(self, callbackFunction):
        while True:
            byte = self.ser.read(1)
            if byte:
                packet = self.input(byte)
                if (packet != None):
                    callbackFunction(packet)
            else:
                break


def print_data_callback(data):
    print (data)


#print(ve.read_data_single())

2nd:

ve = vedirect("COM3", 1)

Thrid which STILL runs [*] forever:

print(ve.read_data_single())

Meanwhile I got this

When I message[0] I get \t but there are no more values in it


Solution

  • If you're trying to use the VE-Direct protocol, according to the manual:

    On power up, a VE.Direct interface will always be in Text-mode, and continuously transmits all runtime fields. As soon as it receives a valid HEX-message, it will switch to HEX-mode. It will stay in HEXmode as long as HEX-messages are frequently received. After a product has not received any valid HEX-messages for several seconds, it will switch back to Text-mode and start to auto transmit the run-time fields periodically again. Some products will send Asynchronous HEX-messages, starting with “:A” and ending with a newline ‘\n’, on their own. These messages can interrupt a regular Text-mode frame.

    It seems that your problem is you are not setting the baudrate correctly, which by default is 19200.

    If you want to work on HEX mode you can do that with Python 3.x, just decode the HEX message:

    received_hex=ser.read()
    received_utf=received_hex.decode()
    print(received_utf)
    

    You can find some good pointers on this particular problem in other questions. Take a look at this, as an example.

    You might need to check page 4 on the manual and the firmware version of your device to see if you have support for the VE-Direct protocol

    If you're not sure about settings (baudrates stop bits, etc.) it might make more sense to start connecting your device using a terminal program like putty (which you seem to be using already). If you're on Windows I think RealTerm is easier to use and you can easily switch from HEX to ASCII.

    EDIT: As it turns out, there is quite a lot of useful stuff done for this particular protocol. This script looks very promising.

    If you want to run this script open a DOS terminal (make sure your Python folder C:\Python3.x\bin is in your path, move to the folder where you stored the script (C:\Example) and type:

    C:\Example\python vedirect.py --port COM3
    

    Looking at the comments below, it seems you already have your script working but it still needs some work to make it usable. Right now you can see the output of your device if you just change your loop a bit:

    message=""
    while True:
        message += ser.read()
        print(message)
    

    The output is obviously quite ugly, but with a bit of work you can make it look like the script above. There are also some good pointers on how to read data for these devices on the manual. Take a look and see if you can put it together. Now, depending on your needs you might prefer to work on your own script or start from the other one. If you're learning, try to do it yourself before you look at the solution, if you need to have a solution ASAP then I think the github script is the place to go.