i2c multi-byte read for ESP8266? (e.g. RTC1307)

All ESP8266 boards running MicroPython.
Official boards are the Adafruit Huzzah and Feather boards.
Target audience: MicroPython users with an ESP8266 board.
gojimmypi
Posts: 36
Joined: Wed Mar 02, 2016 8:01 pm
Contact:

i2c multi-byte read for ESP8266? (e.g. RTC1307)

Post by gojimmypi » Sun Mar 27, 2016 12:44 am

edit: update subject with specific (e.g. RTC1307)

I was wondering if anyone has some working sample code for multi-byte reads from the I2C bus?

I'm using the mp-esp8266-firmware-v02.bin

Code: Select all

#4 ets_task(40100278, 3, 3fff4658, 4)
MicroPython v1.6-336-g6f5af76-dirty on 2016-03-16; ESP module with ESP8266
Type "help()" for more information.
I have an RTC1307 that allows for reading multiple bytes from device 0x68 (ya, I know the ESP8266 is supposed to have a built-in clock, this is just something for me to learn I2C on MicroPython).

The time is set properly via an RPi (hwclock -w), and with a battery in place, I have confirmed it is working properly when moving from the RPi to the ESP8266 and back.

When I try read all the by registers at once in MicroPython, only the first byte appears to have valid data (and seems to properly increment with seconds). Note that I write a zero to register 0 before reading. Without it, I seem to get garbage data. (Is that really needed?)

Code: Select all

>>> from machine import Pin, I2C
>>> i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
>>> buf=array.array('H')
>>> buf.append(0)
>>> print(buf)
array('H', [0])
>>> i2c.writeto(0x68,buf)
>>> print(i2c.readfrom(0x68, 7) )
b'\x14\xff\xff\xff\xff\xff\xff'
>>> print(i2c.readfrom(0x68, 7) )
b'\x01\xff\xff\xff\xff\xff\xff'
>>> print(i2c.readfrom(0x68, 7) )
b'\x03\xff\xff\xff\xff\xff\xff'
>>> print(i2c.readfrom(0x68, 7) )
b'\x93\xff\xff\xff\xff\xff\xff'
>>> print(i2c.readfrom(0x68, 7) )
b'\xfa\xff\xff\xff\xff\xff\xff'
>>> print(i2c.readfrom(0x68, 7) )
For reference, the datasheet is here: http://datasheets.maximintegrated.com/en/ds/DS1307.pdf

I was able to get something somewhat working, by reading a single byte at a time with this hack:

Code: Select all

class DS1307():
    def __init__(self,  addr=0x68):
        self._addr = addr
        self._i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)

    def _readbyte(self):
        return self._i2c.readfrom(self._addr, 1)

    def _readbyte_nostop(self):
        return self._i2c.readfrom(self._addr, 1, stop=False)

    def readbuf_nostop(self, numbytes):
        # it seems only one byte is actually returned! (the remainders are 0xff)
        # we will assume this is an incomplete implementation

        self._write(0,0) # we need to send a zero to start reading
        buf = [0x0] * numbytes # char array
        for i in range(0, numbytes-2):
            buf[i] = self._readbyte_nostop()
            print(i, hex(ord((buf[i]))))
        buf[i + 1] = self._readbyte() # final read includes stop
        print(i + 1,hex(ord((buf[i+1]))))
        return (buf)
this seems to almost work: the first byte read does seem to increment seconds. But the second byte appears to be the hours, not the minutes. (well, the second byte does not increment when the seconds pass 59, and it is not 0xff)

has anyone had any luck with multi-byte I2C? or perhaps can spot what I'm doing wrong? Thanks
Last edited by gojimmypi on Sun Mar 27, 2016 10:00 am, edited 1 time in total.

User avatar
marfis
Posts: 215
Joined: Fri Oct 31, 2014 10:29 am
Location: Zurich / Switzerland

Re: i2c multi-byte read for ESP8266?

Post by marfis » Sun Mar 27, 2016 9:04 am

for any memory mapped I2C devices you should use readfrom_mem or mem_read.

This handles the correct repeated start transfer conditions after the write of address.

something like
i2c.mem_read(0x68,0,7)

gojimmypi
Posts: 36
Joined: Wed Mar 02, 2016 8:01 pm
Contact:

Re: i2c multi-byte read for ESP8266? (e.g. RTC1307)

Post by gojimmypi » Sun Mar 27, 2016 9:47 am

thanks for your reply. well, that sounded fairly promising, but does not seem to work on the V02 alpha that I have (shipped March 16 - is that the most recent?)

Code: Select all

>>> from machine import Pin, I2C
>>> i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
>>> print(i2c.mem_read(0x68,0,7))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'I2C' object has no attribute 'mem_read'
pressing [tab] after the dot seems to confirm:

Code: Select all

>>> i2c.
init            readfrom        writeto
I was hoping the docs had simply not yet been updated:
http://micropython.org/resources/docs/e ... ml#i2c-bus
perhaps there's a newer alpha that did not arrive in my inbox? address is in my contacts, and nothing found in spam.

thanks for the idea though! I'm looking forward to getting this working :)

User avatar
marfis
Posts: 215
Joined: Fri Oct 31, 2014 10:29 am
Location: Zurich / Switzerland

Re: i2c multi-byte read for ESP8266? (e.g. RTC1307)

Post by marfis » Sun Mar 27, 2016 11:39 am

sorry i wrongly assumed it is a true memory mapping i2c sensor whereas it seems to use a register pointer that needs to be written first. So even if memread was present it wouldn't be correct.

Actually I think your code should work. did you check with a logic analyser?

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: i2c multi-byte read for ESP8266? (e.g. RTC1307)

Post by deshipu » Sun Mar 27, 2016 1:38 pm

There is an additional flag you can pass to readfrom and writeto to make it not end the transmission. You can use that to implement mem_read and mem_write, I think.

gojimmypi
Posts: 36
Joined: Wed Mar 02, 2016 8:01 pm
Contact:

Re: i2c multi-byte read for ESP8266? (e.g. RTC1307)

Post by gojimmypi » Sun Mar 27, 2016 3:51 pm

marfis wrote: did you check with a logic analyser?
I've been looking for a reason to try out my Rigol 1054Z decoding! The good news, is that the oscilloscope is really quite cool. The bad news is that the resultant data seems to have a problem (see attached picture). Note all the question marks between data elements.

From the manual
When the ACK (ACKnowledge Character) is not met, error marks "?" as shown in the figure below will be displayed.
The here's the code I used to continually trigger the I2C (I've not yet determined how to capture a one-shot on the DS1054)

Code: Select all

from machine import Pin, I2C
import array
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
buf=array.array('H')
buf.append(0)
i2c.writeto(0x68,buf)
while True:
  print(i2c.readfrom(0x68, 7))
deshipu wrote:There is an additional flag you can pass to readfrom and writeto to make it not end the transmission. You can use that to implement mem_read and mem_write, I think.
I tried both with and without the "stop=False" parameter. And without it, things are really terrible; the scope has a very difficult time in decoding. (and the question marks remain between data elements)

So, I'm thinking perhaps in this early alpha release, that multi-register reading from devices like this might simply not be fully implemented yet?
Attachments
Rigol_I2C.png
Rigol_I2C.png (62.61 KiB) Viewed 9239 times

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: i2c multi-byte read for ESP8266? (e.g. RTC1307)

Post by deshipu » Tue Mar 29, 2016 7:37 pm

I did a bit of experimenting with I2C on the ESP recently, and my conclusion is, while I can't really put a finger to what is wrong, it's wrong somehow. I tried both with some I2C sensors and displays, as well as with a Pro Mini acting as an I2C slave. Sending individual bytes seems to work, most of the time -- you will only sometimes receive an OSError. With longer payloads, you get the OSError more often, and sooner or later the error happens at such an unfortunate moment, that gets your slave stuck and unable to respond.

I tried pyboard with exactly the same setup, and everything works perfectly, so I'm sure that's just some glitch in how the bitbanging is implemented, and it will get worked out sooner or later. Just consider the I2C support a work in progress, and wait for proper implementation.

pfalcon
Posts: 1155
Joined: Fri Feb 28, 2014 2:05 pm

Re: i2c multi-byte read for ESP8266? (e.g. RTC1307)

Post by pfalcon » Wed Mar 30, 2016 6:47 am

deshipu, thanks for the feedback. Can you please provide more info: exact I2C devices you tried with, whether WiFi was enabled/connected (and then trying with WiFi disabled may be a good idea). Actually, this warrants a github ticket, so please feel free to provide this info in it.
Awesome MicroPython list
Pycopy - A better MicroPython https://github.com/pfalcon/micropython
MicroPython standard library for all ports and forks - https://github.com/pfalcon/micropython-lib
More up to date docs - http://pycopy.readthedocs.io/

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: i2c multi-byte read for ESP8266? (e.g. RTC1307)

Post by deshipu » Wed Mar 30, 2016 8:06 am

I you want, I will sit down this evening and try to analyze it properly and prepare osciloscope captures. I suspect it's just a missing delay somewhere.

pfalcon
Posts: 1155
Joined: Fri Feb 28, 2014 2:05 pm

Re: i2c multi-byte read for ESP8266? (e.g. RTC1307)

Post by pfalcon » Wed Mar 30, 2016 8:46 am

I'd say just capturing whatever you have on your hands/in mind is ok for now. Damien works on hardware stuff support and is busy with other devel topics now, but as he gets back to it, he may ask for further details, and then they certainly will be helpful.
Awesome MicroPython list
Pycopy - A better MicroPython https://github.com/pfalcon/micropython
MicroPython standard library for all ports and forks - https://github.com/pfalcon/micropython-lib
More up to date docs - http://pycopy.readthedocs.io/

Post Reply