-1

Here's my situation: I have four FDC1004 devices that I want to have connected to an array of 64 electrodes through four ADG1206 16:1 multiplexers. Each device is slaved to a pi4 using separate I2C buses, each set to 400 kHz. This is done due to the devices having the same address. This is the general scheme of my data acquisition process:

  1. Initiate repeat mode for each FDC1004 device at 400 Hz
  2. Wait until each device's 0x0C register "ready" bit is 1 (they do not have an interrupt pin)
  3. Read off the 24 bit measurement from each device
  4. Convert 24 bit measurements to floats and store measurements in array
  5. Set each device into standby mode
  6. Switch to next set of 4 electrodes using the ADG1206s
  7. Repeat until all 64 data points have been collected

Trying to do this with loops and device polling has resulting in a linear increase in data acquisition time with the number of devices i.e. 1 device=45ms, 2 = 90ms, etc. I would like to see if multiprocessing speeds up the data acquisition but I am unsure how to implement it in this case. Here is my non-optimized python script set for 2 FDC devices:

from smbus2 import SMBus, i2c_msg
import RPi.GPIO as GPIO
import time
#ADG1206 binary addresses
addresses = [
    [0, 0, 0, 0], [1, 0, 0, 0], [0, 1, 0, 0], [1, 1, 0, 0],
    [0, 0, 1, 0], [1, 0, 1, 0], [0, 1, 1, 0], [1, 1, 1, 0],
    [0, 0, 0, 1], [1, 0, 0, 1], [0, 1, 0, 1], [1, 1, 0, 1],
    [0, 0, 1, 1], [1, 0, 1, 1], [0, 1, 1, 1], [1, 1, 1, 1]
]
GPIO_pins = [10,9,11,25,8]
GPIO.setmode(GPIO.BCM)

for pin in GPIO_pins:
    GPIO.setup(pin,GPIO.OUT)

def switch_elec(elec):

    GPIO.output(GPIO_pins[0],GPIO.HIGH)
    for i in range(4):
        GPIO.output(GPIO_pins[i+1],addresses[elec][i])

class FDC1004(object):
    def __init__(self, bus, address):
        self.bus = bus
        self.address = address

    def write_register(self, reg, data):
    # Function to write a 16-bit value to a register
        msg = i2c_msg.write(self.address, [reg, data >> 8, data & 0xFF])
        self.bus.i2c_rdwr(msg)

    def read_register_16_bit(self, reg):
    # Function to read a 16-bit value from a register
        msg_write = i2c_msg.write(self.address, [reg])
        msg_read = i2c_msg.read(self.address, 2)
        self.bus.i2c_rdwr(msg_write, msg_read)
        value = int.from_bytes(msg_read, byteorder='big', signed=False)
        return value

    def read_measurement_registers(self):
    # Function to read the MSB and LSB measurement registers and combine into a 24-bit value
        msb = self.read_register_16_bit(0x00)
        lsb = self.read_register_16_bit(0x01)
        return (msb << 8) | lsb

    def convert_to_float(self, rawdata):
    # Function to convert the 24-bit rawdata into a float value (capacitance in pF)
        capacitance = rawdata / 524288.0  # 2^19 = 524288.0 (scaling factor)
        return capacitance

if __name__ == "__main__":
    bus_nums=[3,4]
    num_dev=2
    addr=0x50
    num_elec=16
    dev_count=0
    fdc_data=[[0.0 for _ in range(num_elec)] for _ in range(num_dev)]
    fdcs = [FDC1004(SMBus(fdc), addr) for fdc in bus_nums]

    #Reset FDC devices
    for fdc in fdcs:
        fdc.write_register(0x0C, 0x800)
        while not (fdc.read_register_16_bit(0x0C) & 0x0800)==0x0800:
            print ("ready")
        #Set measurement channel and set to standy mode
        fdc.write_register(0x0C,0xD00)
        fdc.write_register(0x08,0x1C00)

    start_time=time.perf_counter()
    #loop through 16x4 electrodes
    for i in range(num_elec):
        #set FDCs in standby mode before switching electrode
        if i>0:
            for fdc in fdcs:
                fdc.write_register(0x0C,0xD00)
        #switch ADG1206 multiplexer to next electrode
        switch_elec(i)
        #set devices to repeat mode at 400 Hz
        for fdc in fdcs:
            fdc.write_register(0x0C,0xD80)
        #Wait until each device is ready before storing measurement in array
        while dev_count<num_dev:
            for j, fdc in enumerate(fdcs):
                if fdc.read_register_16_bit(0x0C) ^ 0xD88==0:
                    raw_measurement_data = fdc.read_measurement_registers()
                    converted_data = fdc.convert_to_float(raw_measurement_data)
                    fdc_data[j][i]=converted_data
                    dev_count+=1
        dev_count=0
        #print out values
        if i==num_elec-1:
            for j in range(num_dev):
                for k in range(num_elec):
                    print (fdc_data[j][k])
            print (time.perf_counter()-start_time,"seconds")

I am not sure which parts, if any would be faster using multiprocessing. I am also not sure if Python is too slow compared to C/C++ for multiprocessing to make a noticeable difference. I chose Python because it's what I know, but if C or C++ would be much faster than I will just try that. If multiprocessing would significantly speed up the data acquisition, what would that look like? -Not asking for someone to write the code, but just places to start or directions to take with it.

3
  • I know that this is changing your hardware architecture, but have you considered to use C++ microcontrollers in parallel to do the low level job, gather the data and send it once to the PI using different I2C addresses and leave the high level work to the PI? Commented Jul 28, 2023 at 23:11
  • @LucaSpuntoni I have already tried using one Arduino microcontroller with an I2C multiplexer and had the same results. Doing what you suggested-Having multiple microcontrollers- is what I will try next if I cannot figure this out. I switched to an Pi4 due to the possibility of multiprocessing, and because I have several lying around. Commented Jul 28, 2023 at 23:19
  • Thanks for your answer, what I meant is having say 4 Esp32 configured with different I2C addresses gathering the data and send those data to a single PI, in this way you are collecting the data 4 time faster, leaving the PI the high level work. Commented Jul 28, 2023 at 23:30

1 Answer 1

0

Your question is basically a programming question, so off topic here. Secondly you seem to want someone to write your multiprocessing code.

To comment on the Pi related issues you raised.

Your task is limited by the I²C bus speed and performs little processing so neither c or multiprocessing would make significant difference.

You could increase speed by running separate programs (or processes) for each I²C bus. (The Pi4 has an extra 4.)
Offloading the I²C logging to separate microcontrollers e.g. Pi Pico would be a better solution.

2
  • By separate programs do you mean separate threads? Commented Jul 29, 2023 at 0:30
  • No. Threading in python is problematic. Multiprocessing would be a better option (provided each process uses a separate I²C bus - it is unclear from your question what your actual configuration is) but I don't have the python expertise to recommend. If I was doing this I would just write separate programs.
    – Milliways
    Commented Jul 29, 2023 at 0:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.