3

As you may know, DS18B20 is not the quickest sensor and answers after around 750ms delay but, afaik, 1-wire is a relatively quick bus. I have multiple sensors connected to the board, which means if I iterate over them , I waste around 4-5 seconds just to read the values. Is there a way to read multiple values at once. I can do this via std::thread/pthread, but it means that the memory consumption increases with each connected sensor (I believe each thread allocates around 1mb of memory) which seems pointless (and stupid) for such a simple task.

I guess I am looking for a way to handle multiple open connections (with the hardware) at once. Some people recommended a select with fd_set approach, but I am not sure if they make sense as I am not completely understand if the read actually blocks and/or notifies the system if there is any data available.

I am not sure if is allowed to link my own post, but the original question, which pointed me to this was https://stackoverflow.com/questions/47779500/controlling-different-hardware-multithreading-or-something-different

Edit (clarification):

Right now I read sensor values 1-wire sequentially in a loop (simplified pseudocode)

while (is_not_shutdown) {
    foreach(w1_sensor : w1_sensors) {
        w1_sensor.updateValue(); //reads from /sys/bus/w1/28-*/value
    }
}

Similar code handles GPIO updates, I2C sensors, SPI sensors and so on. Some sensors are fast to respond, some are quite slow. I am trying to implement a code in which each following sensor update would not depend on the delay from a communication with a previous sensor.

I could do it like this:

while (is_not_shutdown) {
    foreach (sensor : sensors) {
        sensor.init_background_update_thread(); //launches a thread
    }
}

And then in main thread:

while (is_not_shutdown) {
    foreach(sensor : sensors) {
        value = sensor.readValue(); //returns latest value.
    }
}

Obviously this means a lot of locking/unlocking and other multithreading drawbacks. I believe this multithreaded approach is not very efficient because each additional sensor means spawning an additional thread.

The initial idea was that the task of reading sensor values simultaneously was solved long before the development of multithreading in its current form (a simple user space API). In addition, this is somehow solved by the kernel drivers, for which the thread as a concept does not exist.

7
  • This seems to be a classic XY problem. What are you trying to achieve?
    – joan
    Dec 13, 2017 at 16:02
  • I am trying to archive a fast communication with multiple hardware connected via different buses. Right now I've stumbled on delays from read executions. Dec 13, 2017 at 16:20
  • I am trying to create a meaningful code, which implement an effective solution to the problem. I see multiple ways of implementation but I am unable to figure out the right one. Somehow It became hard to make a decision based on pros and cons. Each question raises even more questions and, combined with the approaching deadline, makes it hard to answer them all. Dec 13, 2017 at 16:29
  • Multiple hardware? Multiple buses? All I have seen mentioned is DS18B20 and the 1-wire bus.
    – joan
    Dec 13, 2017 at 17:02
  • I've modified the question to provide an additional information. Dec 13, 2017 at 18:35

1 Answer 1

3

For asynchronous or polling access, the better approach is to serialize your hardware access into a single thread for a given bus. This thread should run at relatively high priority and establish the "time-base" for your application control loop

In pseudo-code

while(running)
  write_long_delay_device
  write_dev1_cmd
  read_dev1_data
  write_dev2_cmd
  read_dev2_data
  fixed_sleep
  read_long_delay_device
  sleep_os_periodic

If your devices are synchronous, i.e. provide an interrupt or ready signal (typical for ADC), then it is preferable to have an IRQ per device. You start the devices running with some time offset to guarantee that you don't have too many interrupts-during-interrupt.

dev1_isr:
   read_data 
   cmd_next_read

dev2_isr:
   read_data
   cmd_next_read

start_sensors:
   start_dev1
   fixed_sleep 
   start_dev2

If you have completely isolated or very low priority (diagnostic) inputs, you can probably get away with the naive multi-thread approach, but your application should be robust enough to handle jitter and missed signals


Details

Underlying your communication scheme is a single physical bus device that is embedded in the CPU. This device cannot be simultaneously used by multiple threads at the same time to put bits on the wire.

If the kernel driver you are using locks that device, either due to real timing requirements (e.g. fixed delay for bus/device) or due to implementation inefficiency well then you are out of luck. Your user space code will block ioctl calls to the driver until the previous interaction has finished.

In a polling based approach, using multiple threads is problematic, you have no timing or scheduling guarantees, so it is quite likely that your threads will end up blocking each other anyway, using much more time than manually sequencing it . Using locks in time sensitive code is a recipe for always missing your timing requirements.

Locks are used for protection, if you are constantly thrashing a lock then your system is very poorly controlled, using locks for resource scheduling will not give you the resolution in time that you want.


Additional Responses

but it means that the memory consumption increases with each connected sensor (I believe each thread allocates around 1mb of memory) which seems pointless (and stupid) for such a simple task.

Yes, that is part of the trade-off. Is it "stupid" and "pointless"? Well it depends on your requirements...

A thread adds much more than just memory usage. Every time you have a context switch from one thread to another, that may be up to several milliseconds of wasted CPU time in the kernel to handle the context switch. In time sensitive applications this is much more crucial than memory.

In any case, this 1MB usage is likely not true , I suspect you are misinterpreting shared memory or caching reporting. An empty pthread should much smaller than that. In either case this isn't that much, these threads make up your primary control/poll loop, they should be respected more...

Obviously this means a lot of locking/unlocking and other multithreading drawbacks. I believe this multithreaded approach is not very efficient because each additional sensor means spawning an additional thread.

True, however the downside is not the extra thread, but that you cannot use the same bus at the same time in two places, regardless of how many threads you have.

Your multithreaded approach is basically out of laziness. You do not wish to design a robust polling loop so you are asking the kernel, to do its best to schedule your threads and you use locks to resolve resource conflicts when the scheduler screw up. Of course this is less efficient than a serial approach.

However, it is simpler (easier), but can cause unintended side effects, the scheduler/kernel is not always doing what you think it is doing.

The initial idea was that the task of reading sensor values simultaneously was solved long before the development of multithreading in its current form (a simple user space API).

In a sense yes, and you are already taking advantage of this solution. The peripherals that access the bus are integrated on die, they are also usually DMA Enabled and individually clocked , so they do not require CPU cycles to perform the task (the kernel is not bit-banging).

However, there is no general solution to simultaneously access multiple external devices in the way you describe. Most of this is handled by the bus protocol, which may allow multiplexing, messaging/mailbox and other high level protocols for more convenient sequencing and multiplxing of data.

With a simple serial bus like One-Wire, the solution is either a carefully crafted poll loop, using dedicated interrupts, or some other specific scheme that can be applied to a given system.

In addition, this is somehow solved by the kernel drivers, for which the thread as a concept does not exist.

It is "solved" in the sense that the kernel drivers will try their best to respect what you ask, however you cannot send two different voltage levels on the same bus at the same time, so the "solution" is just heavy handed concurency control which breaks your timing requirements.

Also, kernel absolutely has internal threads and a concept of a thread, it may not be a posix thread as you know it, but in terms of multiple control/execution flows it exists. For example, register an ISR with an interrupt in a driver and the IRQ has its own kernel thread/task associated with it.

3
  • Thank you for your detailed response. It mainly points me to right direction - investigate a bus/hardware implementation and decide accordingly. My original idea was that if devices are being represented as files in *nix, I could simultaneously read from them, thus establishing multiple communications at the same time, similar to sockets. I understand now that it is pointless, considering my bus is serial. Dec 13, 2017 at 22:31
  • @Akexey There are higher level buses that do have that concept, for example - CAN is a good candidate exactly because it has a notion of "connection" and can be used to provide data from multiple devices over the same bus using higher level logic. Part of that is that devices have a method of resolving bus conflicts as part of the protocol specification
    – crasic
    Dec 13, 2017 at 23:29
  • @Alexey In any case, multithread sampling/polling is actually quite difficult. With many caveats. I would single thread and sequence your poll commands/reads until you have enough insight into your system to see how its possible to multiplex and improve down the road.
    – crasic
    Dec 13, 2017 at 23:41

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.