Page 1 of 1

ctypes into uctypes for crc communication

Posted: Sun Jan 19, 2020 5:03 am
by DavidRKnowles
Finally got some time to jump back onto micropython and finish a project I had for communicating with my offgrid inverter.

It is a MPP Solar unit and requires a CRC with the commands to ensure all is healthy with the data, the python project I am getting most of the ideas with regards to communication is https://github.com/jblance/mpp-solar

jblance uses the ctypes module in his code but this deviates from the micro python uctypes module and I am wondering if someone can help me translate the structures over as ctypes seems to respond with an actual class that contains a value attribute.

the crc high/low return method looks like this:

Code: Select all

import ctypes
def crc(cmd):
    """
    Calculates CRC for supplied text
    """
    log.info('Calculating CRC for %s', cmd)

    crc = 0
    da = 0
    crc_ta = [0x0000, 0x1021, 0x2042, 0x3063,
              0x4084, 0x50a5, 0x60c6, 0x70e7,
              0x8108, 0x9129, 0xa14a, 0xb16b,
              0xc18c, 0xd1ad, 0xe1ce, 0xf1ef]

    for c in cmd:
        t_da = ctypes.c_uint8(crc >> 8)
        da = t_da.value >> 4
        crc <<= 4
        index = da ^ (ord(c) >> 4)
        crc ^= crc_ta[index]
        t_da = ctypes.c_uint8(crc >> 8)
        da = t_da.value >> 4
        crc <<= 4
        index = da ^ (ord(c) & 0x0f)
        crc ^= crc_ta[index]

    crc_low = ctypes.c_uint8(crc).value
    crc_high = ctypes.c_uint8(crc >> 8).value

    if (crc_low == 0x28 or crc_low == 0x0d or crc_low == 0x0a):
        crc_low += 1
    if (crc_high == 0x28 or crc_high == 0x0d or crc_high == 0x0a):
        crc_high += 1

    crc = crc_high << 8
    crc += crc_low

    log.debug('Generated CRC %x %x %x', crc_high, crc_low, crc)
    return [crc_high, crc_low]
I was thinking of something like this, but when it loops through the command the index changes from what the ctypes can do due to the value of the class to being retained, and extends beyond the crc_ta scope.

Code: Select all

import uctypes
def crc(cmd):
    crc = 0
    da = 0
    crc_ta = [0x0000, 0x1021, 0x2042, 0x3063,
              0x4084, 0x50a5, 0x60c6, 0x70e7,
              0x8108, 0x9129, 0xa14a, 0xb16b,
              0xc18c, 0xd1ad, 0xe1ce, 0xf1ef]

    for c in cmd:
        t_da = crc >> 8 | uctypes.UINT8
        da = t_da >> 4
        crc <<= 4
        index = da ^ (ord(c) >> 4)
        crc ^= crc_ta[index]
        t_da = crc >> 8 | uctypes.UINT8
        da = t_da >> 4
        crc <<= 4
        index = da ^ (ord(c) & 0x0f)
        crc ^= crc_ta[index]

    crc_low = crc | uctypes.UINT8
    crc_high = crc >> 8 | uctypes.UINT8

    if (crc_low == 0x28 or crc_low == 0x0d or crc_low == 0x0a):
        crc_low += 1
    if (crc_high == 0x28 or crc_high == 0x0d or crc_high == 0x0a):
        crc_high += 1

    crc = crc_high << 8
    crc += crc_low

    print('Generated CRC {} {} {}'.format(crc_high, crc_low, crc))
    return [crc_high, crc_low]
Any help would be greatly appreciated as my experience is fairly limited in this area.

Re: ctypes into uctypes for crc communication

Posted: Sun Jan 19, 2020 10:31 am
by Christian Walther
I’m not sure why they’re using ctypes here, ctypes is for calling C functions and there are no C functions being called here. It seems all they want is to slice 8 bits out of an integer, which is easily done using the & operator, no ctypes needed.

The following seems to work for me in CPython, giving the same results as the original for what I’ve thrown at it, and I imagine it should work in MicroPython as well:

Code: Select all

def crc2(cmd):
    """
    Calculates CRC for supplied text
    """
    print('Calculating CRC for %s', cmd)

    crc = 0
    da = 0
    crc_ta = [0x0000, 0x1021, 0x2042, 0x3063,
              0x4084, 0x50a5, 0x60c6, 0x70e7,
              0x8108, 0x9129, 0xa14a, 0xb16b,
              0xc18c, 0xd1ad, 0xe1ce, 0xf1ef]

    for c in cmd:
        da = ((crc >> 12) & 0xF)
        crc <<= 4
        index = da ^ (ord(c) >> 4)
        crc ^= crc_ta[index]
        da = ((crc >> 12) & 0xF)
        crc <<= 4
        index = da ^ (ord(c) & 0x0f)
        crc ^= crc_ta[index]

    crc_low = (crc & 0xFF)
    crc_high = ((crc >> 8) & 0xFF)

    if (crc_low == 0x28 or crc_low == 0x0d or crc_low == 0x0a):
        crc_low += 1
    if (crc_high == 0x28 or crc_high == 0x0d or crc_high == 0x0a):
        crc_high += 1

    crc = crc_high << 8
    crc += crc_low

    print('Generated CRC %x %x %x', crc_high, crc_low, crc)
    return [crc_high, crc_low]

Re: ctypes into uctypes for crc communication

Posted: Sun Jan 19, 2020 11:36 am
by DavidRKnowles
Christian Walther wrote:
Sun Jan 19, 2020 10:31 am
I’m not sure why they’re using ctypes here, ctypes is for calling C functions and there are no C functions being called here. It seems all they want is to slice 8 bits out of an integer, which is easily done using the & operator, no ctypes needed.

The following seems to work for me in CPython, giving the same results as the original for what I’ve thrown at it, and I imagine it should work in MicroPython as well:
Dude, That worked a freaking charm, you are a legend, Cheers.

I have to train my self a bit more on the utilisation of bit slicing, I haven't gone too far down the rabbit hole of dealing with bits, bit arrays etc. in micropython. I will at some stage because I am sure I can be less memory intense on my firmware and other projects, especially this project as I have built a BMS to manage the Lithium batteries and that has quite a few different modules in it running all the time for 16bit analog reads of each cell and the main shunt resistor. memory is starting to get constrained, even on the esp32.