Hi, I'm using a board that shares lcd touch and RTC pins ( this is the board ).
Since it is a pretty big project I decided to split the program in different tasks in order to split the work load on both cores ( I'm using an esp32s3 and the UI is pretty demanding on a single core ) .
Now I have all the RTC functions in a task and touch functions inside the lcd task. I don't think I can create 2 wire instances with the same sda and scl pins, right?
This is the issue: how do I read the time from RTC and touch information without errors and in the quickest possible way using wire/twowire?
I'm mostly concerned about connection instability and the possible delay or stutters in touch readings.
A Baton like system could work?
Merging the touch task with the RTC? That would be a pretty ugly implementation since all the touch functions are inside the "LCD" task that also contains all lvgl stuffs. However it would be the easiest: sequentially running touch and then the rtc (adding maybe a timer since I don't need rtc checks at each loop) would probably solve this.
I could create a task called "WireDevices" and pass Touch coordinates to the lcd tasks with a queue with all the other data from other i2c devices.
The ESP32 contains an internal RTC and has APIs supporting standard POSIX time functions. So, I'd do all time readings in the code from the internal RTC and just read the external RTC once at startup and then every couple of hours to true up the internal one.
If you have access to the internet through WiFi, you can get the time from the internet using NTP, there wouldn't be any need to use an RTC (internal or external).
The NTP system and APIs on ESP32 operate directly on the Internal RTC keep it updated. The ESP32's POSIX time APIs work directly with the Internal RTC to get time, make time zone adjustments, etc. So, you obviously need the Internal RTC.
That was my first idea. However I can see several downsides using this method. One of which is the behavior of the touch in case a swipe is happening ( I'm using lvgl and many pages require swipes to navigate through the UI) and at the same time a new time update request is happening. If the mutex is busy, the other device would behave strangely.
A walkaround would be to never update the rtc if at least x second passed since the last touch.
But yes, this would be my preferred method. Defining the wire/twowire object in the main loop and then pass it to the function of each task that requires it. All of this using mutex.
See Post #4. You don't need to read the External (I2C) RTC more often than every few hours. Any requirement to know the time should be satisfied by reading the Internal RTC. The timing of the I2C RTC reading isn't critical since it's only used to true up the internal one, it can wait until the UI takes a break. Conversely, reading the I2C RTC is quick. So the UI won't have to wait long if the mutex is already held (which will only happen once every couple of hours anyway).
That's true, and I will probably follow this route, however it's impossible to predict if both device will require an i2c call since touch is constantly checking for a new input.
However those are all programming walkarounds to avoid a simultaneous i2c request.
The end of the story is that there's no real way to have 2 device with the same pins (and the same wire instance) and not use mutex, right?
Again it doesn't matter as reading the I2C RTC is not time-critical, it can wait. And, the UI won't have to wait very long even if the I2C bus is busy with the RTC. The slow, human user using the UI will never notice the difference.
This will all be taken care of by the way you structure the tasks and their interaction. But, it's all hand-waving so far since you haven't provided and real details or code.
Suppose you could do that. How do you imagine it working when the two independent instances try to communicate over the same wires at the same time to two different devices with two different messages?
You can have lots of I2C devices on a single pair of SCL/SDA pins, only one Wire instance needed. They all must be at different addresses of course and you can run the sample sketch called something like I2C scan to make sure. If there is a conflict many devices have a way to re-assign the address, you may also need to specify that address in the init/begin.
I suspected the NTP API may use the internal RTC, but I wasn't at all sure. Thank you for explaining. My comment about there not being any need to use an RTC was intended to indicate that the TS wouldn't need to write any code to directly interact with any RTC. I could have worded it better with hindsight.
Right, once you setup the NTP and update options it's all auto-magic. The user simply gets the time from the RTC using the provided APIs and NTP updates the RTC in the background.
Yes, I know that I2C is pretty robust and that you can add many devices. The problem is different. Since they are all connected together there's no way to split them in order to give different tasks the ability to read them independently...
...and this leads me to this. I haven't gone very deeply into I2C protocol but I was pretty sure that there wasn't an easy way to do this other than using mutex. That's why I was asking, maybe I was missing something. Short answer is no...I was pretty sure about that but I tried.
So, in this case, as I thought, the simplest solution is to use a mutex logic to give access to the I2C bus giving priority to the touch reducing RTC update frequency. Or just merging all the I2C devices in one task and then send the data where is needed.
However I would like to ask you something: in case I had 2 sensor connected together, both of them are reading data that are pretty time-critical, for example temperature and distance ( or any other sensor that share the same I2C bus at different addresses), the mutex system would still be the best option?
Obviously thousand of possibility may occur but let's say 2 tasks runs very comparable functions in terms of load to cpu or execution time. Both task needs access to their sensor ( each task has to read/write to 1 sensor), mutex is still the best option? Or there are other techniques or workaround?
I do not think that is correct. Each I2C device is individually addressed, there is no other concept of 'splitting' them.
If I am overlooking something, you can have multiple Wire instances if that somehow helps.
Sorry, not really clear what your problem is.
Bottom line is that you can only communicate with one device on the I2C bus at a time. Your program must prevent any attempt to access multiple devices simultaneously. How you choose to do that is more a question of preference, coding style, and experience. Choose what's "best" within that parameter space.
Yes, each device has it's own address that the instance of twowire uses to comunicate with the devices. However, in my case, is like having 2 masters (or more in case I want to add more i2c devices in different tasks) that want to comunicate with 2 slaves.
Like gfvalvo was saying:
It's like having a video call where 2 person are talking to other 2 person at the same time: none will understand or worst they would understand very strange messages and execute doubtful commands.
Have one task in charge of ALL the I2C devices, and each task that needs an I2C service sends a message to the I2C manager who then performs whatever action is needed and sends a message back. I used that type of design in several places of the ticker plant.
Exactly, the one you described here is going to be my plan b in case I find the mutex method too complicated to maintain in the long run. I tried to describe it in the previus posts but you described better than me. Doing so would make the code modular and easier to maintain in case I'm adding more i2c devices to the same pins