Example #1
0
def receive(conn):
    """ 
    Attempt to read messages from a serial connection
    """
    data = bytearray()
    while True:
        buf = conn.read(READ_BYTES)
        if buf:
            if DEBUG: print(">>> RAW RECEIVE:", buf)
            data.extend(buf)
        if (not buf) or len(buf) < READ_BYTES:
            break

    idx = 0
    while idx + 9 <= len(data):
        if data[idx] != STX:
            idx += 1
            continue
        stx, req, addr, size = struct.unpack('>BBBB', data[idx:idx + 4])
        if req not in (ENQ, ACK, NAK):
            print(
                "Bad req value: {:02x} (should be one of ENQ/ACK/NAK)".format(
                    req))
            idx += 1
            continue
        if idx + 4 + size >= len(data):
            print("Can't read %d bytes from buffer" % size)
            idx += 1
            continue
        msg, lsb, msb, etx = struct.unpack('>%dsBBB' % size,
                                           data[idx + 4:idx + 7 + size])
        if etx != ETX:
            print("Bad ETX value: {:02x}".format(etx))
            idx += 1
            continue
        crc_calc = crc16.calcData(data[idx + 1:idx + 4 + size])
        crc_msg = msb << 8 | lsb
        if crc_calc != crc_msg:
            print("Bad CRC check: %s <> %s" %
                  (binascii.hexlify(crc_calc), binascii.hexlify(crc_msg)))
            idx += 1
            continue

        if DEBUG:
            print(">>> RECV:", binascii.hexlify(data), "=>",
                  binascii.hexlify(msg))
        yield {
            "stx": stx,
            "req": req,
            "addr": addr,
            "size": size,
            "msg": msg,
            "lsb": lsb,
            "msb": msb,
            "etx": etx,
        }
        idx += 4 + size
Example #2
0
def send(conn, req, cmd, subcmd, data=b'', addr=1):
    """
    Send cmd/subcmd (e.g. 0x60/0x01) and optional data to the RS485 bus
    """
    assert req in (ENQ, ACK, NAK)  # req should be one of ENQ, ACK, NAK
    msg = struct.pack('BBBBB', req, addr, 2 + len(data), cmd, subcmd)
    if len(data) > 0:
        msg = struct.pack('5s%ds' % len(data), msg, data)
    crcval = crc16.calcData(msg)
    lsb = crcval & (0xff)
    msb = (crcval >> 8) & 0xff
    data = struct.pack('B%dsBBB' % len(msg), STX, msg, lsb, msb, ETX)
    if DEBUG: print(">>> SEND:", binascii.hexlify(msg), "=>", binascii.hexlify(data))
    conn.write(data)
    conn.flush()
Example #3
0
def send_request(connection, inv_id, cmd):
    """ Send command (e.g. b'\x60\x01') to the inverter with id inv_id """

    # Borrowed from DeltaPVOutput

    length = len(cmd)
    msgbody = struct.pack('BBB%ds' % length, 5, inv_id, length, cmd)
    crcval = crc16.calcData(msgbody)
    lsb = crcval & (0xff)
    msb = (crcval >> 8) & 0xff
    data = struct.pack('BBBB%dsBBB' % length, 2, 5, inv_id, length, cmd, lsb,
                       msb, 3)

    if debugging:
        print("Sending data query to inverter", inv_id)

    connection.write(data)
    connection.flush()
Example #4
0
def decode_response(data):
    """ 
    Try to decode an inverter-messages from serial data and return 
    a dictionary with message parameters (including length, inverter_id, 
    command, subcommand).
    Checks message validity, consistency and CRC, and returns None 
    if a message is not valid. Request-messages are parsed, but are 
    currently not returned. 
    """

    try:

        stx = data[0]
        enqack = data[1]

        if stx != 0x02:
            if verbose:
                print("Invalid message, STX =", stx)
            return None

        if enqack != 0x05 and enqack != 0x06:
            if verbose:
                print("Invalid message, ENQ/ACK =", enqack)
            return None

        inv_id = data[2]
        length = data[3]

        if (
                len(data)
        ) < length + 4 + 3:  # should be 4 bytes (STX + ACK + ID + LEN) + data length + 3 bytes (CRC16 + ETX)
            if verbose:
                print("Incomplete data block of", len(data),
                      "bytes, should be", length + 7, "bytes")
            return None

        cmd = data[4]  # Command ID
        subcmd = data[5]  # Subcommand ID

        data_offset = 6  # Start of data
        data_length = length - 2  # Length of data

        crc_lsb = data[
            4 +
            length]  # Least-significant byte of CRC-16 over preceding bytes after STX
        crc_msb = data[
            4 + length +
            1]  # Most-significant byte of CRC-16 over preceding bytes after STX

        etx = data[4 + length +
                   2]  # ETX-byte to signify end of message, should be 0x03

        rvals = {'enqack': enqack, 'inv_id': inv_id, 'length': length, 'cmd': cmd, 'subcmd': subcmd, \
                 'data_offset': data_offset, 'data_length': data_length}

        if etx != 0x03:  # ETX isn't 0x03, data probably isn't valid

            if verbose:
                print("ETX at", length + 2, "is", etx, "but should be 3")
                print(rvals)

            return None

        else:  # ETX is 0x03, we probably have a valid data block

            crc_calc = crc16.calcData(
                data[1:4 +
                     length])  # Calculate CRC-16 over message, excluding STX
            crc_msg = crc_msb << 8 | crc_lsb  # Compare with CRC transmitted at end of message

            if crc_calc != crc_msg:

                print("WARNING: CRC-16 is", hex(crc_calc), " but should be",
                      hex(crc_msg))
                return None

            else:

                if enqack == 0x05:  # ENQ, marks start of request message
                    if debugging:
                        print("Found request-message for inverter", inv_id,
                              "with length", length, "and CMD", cmd, "SUB",
                              subcmd)
                    return None  # Currently we do not return requests, only replies

                if debugging:
                    print("Found valid response:", rvals)

                return rvals

    except:

        print("Error decoding response:", str(sys.exc_info()[0]))
        return None