I2C waitReady with timeout, I2C ping and async-processing

58 views
Skip to first unread message

Et Dev

unread,
Oct 4, 2017, 7:55:47 PM10/4/17
to ioio-users
Hello Ytai,

Great product - just love it!

I am not sure if this is the right group to send this request, if not please guide. I am hoping to make a positive contribution to this community.

I have used IOIO in past and have started using it for a pretty big project using some intricate hardware. I ran into problems for effectively using I2C devices and most of these were attributed to the fact that ioio.lib.api.TwiMaster.Result has a waitReady that blocks forever.  This is problematic esp. when the I2C slave's behavior is not always completely determistic - as is the case with many complex hardwares. The thread blocks forever and recovery logic becomes more complex than original operation esp. when monitoring multiple such devices. For now I had to resort to some pretty ugly work-arounds. However, a waitReady in Result that times out - would make an application developer's task a lot easier. So I have the following suggestion, which I would appreciate if you can please consider to make something similar to this available in your next API release.

In ioio.lib.impl.ResourceLifeCycle, add the following method:

protected synchronized void safeWait(long millis) throws ConnectionLostException, InterruptedException {
   if (state_ == State.CLOSED) {
throw new InterruptedException("Resource closed");
}
else if (state_ == State.DISCONNECTED) {
throw new ConnectionLostException();
}
wait(
millis);
}

In ioio.lib.api.TwiMaster.Result interface, add the following method:
public boolean waitReady(long millis) throws ConnectionLostException,
      InterruptedException;

In ioio.lib.impl.TwiMasterImpl.TwiResult class, add the following method:
@Override
public synchronized boolean waitReady(long millis) throws ConnectionLostException,
InterruptedException {
checkState();
while (!ready_) {
safeWait(
millis);
}
return success_;
}

Another issue for working with I2C devices, I was not able to come up with a reliable solution to "ping" a I2C slave with IOIO master to determine if slave is indeed present. For example, connecting my I2C device to a bus-pirate, master can write an address of the slave on the bus and obtain an ACK and this would be an indication that a slave is indeed present at that address. My logic analyzer shows the same with this bus-pirate setup. However, I did not have any such luck making it work with IOIO's TwiMaster.writeRead. I tried writing a 0-byte buffer (with read null buffer) and reading 0-byte buffer (with write null buffer) but seems I dont get any results - calls simply block. I would appreciate any help in this regards. 

It would also help to obtain information on what was NACKed when writing+reading using TwiMaster.writeRead or TwiMaster.writeReadAsync returns false as currently, we have to split write and read into two separate invocations to know what failed esp. when error recovery depends on what failed.

I was able to handle most trivial apps fairly easily with the current APIs and I like the synchronous API in its elegance and simplicity. I recognize this is a subjective viewpoint - however, when time-critical processing, reliability and performance are critical, it becomes harder to overcome the absence of asynchronous event handling paradigm in API esp for devices such as I2C, UART. User ends up dedicating a thread per item to operate and for complex apps, thread synchronization across many threads is typically harder, error-prone and sometime lags quite a bit - when recovery depends on state of device(s) across many such threads. At IOIOProtocol level, we are getting the notifications from the board (asynchronous processing). I wonder if it is worthwhile to explore allowing users to use an asynchronous event handling paradigm - where user can register listeners for handling ingress traffic notifications rather than blocking for it in individual threads. Problem of a potential event-handler hogging the IOIOProtocol handling thread can be mitigated by user handling events using a thread-pool/queue. Please share your thoughts.


With regards,
-VB



Ytai Ben-Tsvi

unread,
Oct 4, 2017, 8:12:37 PM10/4/17
to ioio-...@googlegroups.com
There are a few issues here, I hope I didn't forget anything.
  • Timeout: the approach I took with all blocking operations is that they all handle Thread.interrupt() correctly. This way, you can implement your timeout externally without having to change the IOIO library internals. Simply set a timer that will interrupt the current thread on your deadline and remember to cancel this timer if the operation succeeds.
  • Async API: can be implemented on top of the sync API by creating a thread per call or using a pool, etc. The risk you mentioned with a native async API (blocking the internal threads of the IOIO) is one of the reasons why I chose this route. The other is that synchronous code is generally a lot more straightforward. There's obviously a trade-off.
  • I don't know why writeRead with empty in/out buffers doesn't work. I agree that this is the preferred way to just check whether a device is on the bus or not. Do you happen to have a logic analyzer capture of what happens on the bus in this case?
  • As a side-note, I haven't been actively maintaining the code-base in a couple of years now and have no strong desire to do so. I'm happy to answer questions or engage in discussions, but just wanted to set your expectations as far as making changes per your request.
Cheers.

--
You received this message because you are subscribed to the Google Groups "ioio-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ioio-users+unsubscribe@googlegroups.com.
To post to this group, send email to ioio-...@googlegroups.com.
Visit this group at https://groups.google.com/group/ioio-users.
For more options, visit https://groups.google.com/d/optout.

Et Dev

unread,
Oct 30, 2017, 3:03:01 PM10/30/17
to ioio-users
Hello Ytai,

Sorry for the delay as I was trying to get hold of another reliable logic analyzer to confirm my findings. The logic analyzer capture indicates that address is written by the master followed by an ACK from the slave. Do you need a trace with any other specific scenario?

I understand that managing the project is quite intensive and time consuming - I didn't mean to impose additional burden on you.  I believe timeout change is pretty innocent and could help esp. when dealing with unpredictable I2C slaves - and hence my suggestion. A complex I2C interaction with a timer at each step was quite difficult to program and hence I came up with my workaround. If you find it worthwhile please try to incorporate it in a future release - if you do come up with one in the future.

Once again, thank you for your help and making a fantastic board available to the community.

With regards,
-VB
To unsubscribe from this group and stop receiving emails from it, send an email to ioio-users+...@googlegroups.com.

Ytai Ben-Tsvi

unread,
Nov 3, 2017, 8:24:55 PM11/3/17
to ioio-...@googlegroups.com
On Mon, Oct 30, 2017 at 12:03 PM, Et Dev <etde...@gmail.com> wrote:
Hello Ytai,

Sorry for the delay as I was trying to get hold of another reliable logic analyzer to confirm my findings. The logic analyzer capture indicates that address is written by the master followed by an ACK from the slave. Do you need a trace with any other specific scenario?

Can you send a capture? Specifically, I'm curious about whether the transaction ends (i.e. has a stop symbol).
 

I understand that managing the project is quite intensive and time consuming - I didn't mean to impose additional burden on you.  I believe timeout change is pretty innocent and could help esp. when dealing with unpredictable I2C slaves - and hence my suggestion.

Actually, I don't think this is as simple as you're describing it. Since the master is the one driving the clock, all transactions are predictable under normal conditions. Under abnormal conditions, a slave may hold SCL low ("clock stretching") or SDA low indefinitely. Under these circumstances, the bus is hung and even if the Java side gives up waiting, the firmware is still stuck and the bus is physically unusable. This is a serious limitation of the I2C specification, regardless of implementation. For that reason, some chip manufacturers offer I2C "buffers", which have some logic to "unstuck" slaves that have timed out and detach them from the bus.

However, this doesn't explain or justify why the 0-length transactions don't work. We can try to at least figure this out, so you could at least enumerate what's on the bus.
 
A complex I2C interaction with a timer at each step was quite difficult to program and hence I came up with my workaround.

In what way was it difficult? And in either case, once it is done, you never need to do it again, right? Simply a matter of wrapping the standard writeRead() with your own writeReadWithTimeout() that you can call instead. This can be done externally, i.e. without changing the internals of IOIOLib.
 
To unsubscribe from this group and stop receiving emails from it, send an email to ioio-users+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages