technoblogy / ulisp-esp

A version of the Lisp programming language for ESP32-based boards.
MIT License
110 stars 37 forks source link

`(with-i2c)` acting weird between ESP32 and ATtiny85 #75

Closed dragoncoder047 closed 1 year ago

dragoncoder047 commented 1 year ago

I've been playing around with the I2C bus on an ESP32 and connecting it to ATtiny85's emulating I2C slave devices. However, I'm having trouble figuring out how exactly to communicate with the I2C slave devices.

If it's relevant, I'm using this library to emulate an I2C slave, using the requestEvent() and receiveEvent() exactly as provided in the examples.

I was able to read the registers write to them, and then read back the updated values using the raw Wire library commands in a non-uLisp sketch at the same time the Atiny was doing stuff with the value of the registers (the details are in this blog post of mine).

I forgot about what I did there for a few weeks and uploaded a different Atiny sketch that didn't do anything at all but allow the master to read and write registers at will. I also figured out that the example code didn't do any bounds checking when receiving the target register address (specifically, this line), and could easily clobber over out-of-bounds memory, so I fixed it (by adding % reg_size) to the end of that line).

Unfortunately, when I tried to read and write again with uLisp, it didn't work. The I2C port scanner got a successful ACK from the Atiny (on both the set address and address 0), but I only ever got 255's back when reading.

I don't have the code on hand right now, and I'm not sure what I did to fix the problem, but I eventually was able to read all the registers individually (i.e. write first register address, request+read 1 byte, write 2nd register address, request+read 1 byte, etc.), but not as one transaction (i.e. write register address, then request+read 4 bytes all at once) -- it sent the first register correctly but then just sent 255 for the rest of them.

I have a hunch that the Arduino Wire library, when you call requestFrom, it actually sends a byte to the slave device telling it that the master wants N bytes, rather than just restarting the transaction in read mode and reserving N bytes in the buffer -- which ends up confusing my Atiny which doesn't want to (or need to) know how many bytes will be requested in advance.

I'll have to try again reading it with my Arduino Uno (where uLisp uses direct register manipulation, not the Wire library) and see if I get anything different.

I'm not sure how to get around this problem -- what are your thoughts?

technoblogy commented 1 year ago

The way that Arduino Wire is implemented is very peculiar; it buffers up all the calls to Wire.write(), and then sends them all when you call Wire.endTransmission(). I've written my own library which I believe does things the way I2C was intended to work:

Tiny I2C Routines for all AVR Microcontrollers

However, it only does I2C Master.

In uLisp I have used my routines for AVR microcontrollers, as you mentioned, and the Arduino routines for all the others.

The only time I've implemented an I2C Slave device is here:

I2C SD-Card Module

and that was using the ATtiny1614 which is a processor that has an I2C Slave peripheral. It was hard work figuring out how to get it to work; you might find that article useful. I don't think I'd trust an ATtiny85 to be able to do it properly.

dragoncoder047 commented 1 year ago

IMHO, I think that the Arduino Wire library is at fault here, not TinyWireS. I did get bytes to be read, and was able to write bytes, they just ended up in unpredictable positions depending on how many bytes I wrote at once. If you want to have a go at sidestepping the Arduino library on an ESP32 (and going straight to the ESP-IDF level functions) I think this page would be helpful: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/i2c.html

technoblogy commented 1 year ago

Thanks, but I don't think I'm going to start rewriting the ESP32 I2C code. If it works don't fix it!

dragoncoder047 commented 1 year ago

Well, I was able to try out the I2C routines on an ATmega328P (Arduino Uno) and they have the same lack of success as the ESP32.

The ATtiny85 ACK's at the usual addresses using the port scanner, as usual. But upon reading things, it's even weirder:

uLisp 4.4b
319> (with-i2c (str 5) (write-byte 0 str) (restart-i2c str) (dotimes (_ 4) (print (read-byte str))))

10 
10 
10 
10 
nil

319> ;; Expected output would have been "55 220 128 0 nil" here,
319> ;; as that is what I initially had the ATtiny85's registers set to.
319> (with-i2c (str 5) (write-byte 0 str) (dolist (x '(#xDE #xAD #xBE #xEF)) (write-byte x str)))
nil

319> (with-i2c (str 5) (write-byte 0 str) (restart-i2c str) (dotimes (_ 4) (print (read-byte str))))

10 
10 
10 
10 
nil

319> 

I suspect one of two things now: either a) the ATtiny85 is not responding correctly, or b) it is, but both the Uno and the ESP32 don't take too kindly to clock-stretching. (I'm not a hardware expert; please correct me if you know for certain they do implement clock-stretching properly.) I'll have a go at setting the ESP32's SCL frequency to something super low like 10KHz and see if that makes any difference.

dragoncoder047 commented 1 year ago

To test the Wire library I made a simple I2C REPL and put it in this gist.

Input: a5[w0]r4 produces this output (the ATtiny85 is listening at address 0x05):

Target address 0x5
Begin transmission at target address 0x5
Writing byte 0
End transmission ACK
Requesting 4 bytes from address 0x5:
0 255 255 255 

a5[w0]r1r1r1r1 has a little better luck:

Target address 0x5
Begin transmission at target address 0x5
Writing byte 0
End transmission ACK
Requesting 1 bytes from address 0x5:
0 
Requesting 1 bytes from address 0x5:
220 
Requesting 1 bytes from address 0x5:
128 
Requesting 1 bytes from address 0x5:
0

Just to see if the REPL program is acting OK, I tested this with the MAX17048 which comes on the dev board.

Input: a54[w8]r2

Output:

Target address 0x36
Begin transmission at target address 0x36
Writing byte 0x8
End transmission ACK
Requesting 2 bytes from address 0x36:
0 18

Seems to be working. I guess the ATtiny85's clock-stretching is confusing the ESP32.

dragoncoder047 commented 1 year ago

Off-topic but I don't know where else to get ahold of Mr. AVR Expert:

I'm trying to modify the ATtiny85 program to flash an LED when it receives stuff, but now avrdude is giving me this error:

         Using Port                    : usb
         Using Programmer              : usbtiny
avrdude: usbdev_open(): Found USBtinyISP, bus:device: 001:054
avrdude: Warning: cannot open USB device: Permission denied
avrdude: usbdev_open(): Found USBtinyISP, bus:device: 001:060
avrdude: Warning: cannot open USB device: Permission denied
avrdude: Error: Could not find USBtiny device (0x1781/0xc9f)

I'm just using a sparkfun usb ATtiny85 programmer and the default settings. I was previously able to upload programs, but now something has changed. Any idea what is going on?

technoblogy commented 1 year ago

What core are you using?

With the SparkFun programmer you don't select a USB port from the Port menu - I sometimes forget that and wonder why the USB port isn't showing up.

Also, sometimes things connected to the ATtiny85 circuit can interfere with the upload; try disconnecting something while you upload.

dragoncoder047 commented 1 year ago

I'm using David A. Mellis's core, and the ATtiny85 is plugged directly into the socket in the programmer. I remove it from the breadboard.

For the heck of it I hit "upload" with the programmer unplugged, and I got this:

         Using Port                    : usb
         Using Programmer              : usbtiny
avrdude: usbdev_open(): Found USBtinyISP, bus:device: 001:054
avrdude: Warning: cannot open USB device: Permission denied
avrdude: Error: Could not find USBtiny device (0x1781/0xc9f)

I actually think it might be a sudo issue now, because I previously had to run the Arduino IDE with sudo for it to be able to access the ESP32's linker for some reason, but now I updated Arduino and I don't need sudo for that anymore.

I don't want to run Arduino with sudo because then it changes the owner of all the files to root when I hit save and that is annoying. Do you know of any kind of rules I can add to my /etc/sudoers that will allow avrdude to access USB ports?

technoblogy commented 1 year ago

First of all I would recommend Spence Konde's ATTinyCore rather than the much older David Mellis core.

I don't have any experience of using the Arduino IDE on Linux, but on Mac I have occasionally had some cases SparkFun programmer where the USB port seems to become unavailable, and I have to restart the Mac to cure it, or switch to the other USB socket.

dragoncoder047 commented 1 year ago

or switch to the other USB socket.

Tried 'em all, none work.

Tried again with the Spence Konde core, same error.

technoblogy commented 1 year ago

You've tried restarting your computer?

Perhaps the ATtiny85 is damaged? Try another.

technoblogy commented 1 year ago

Re: AVR problems. I might be able to help if you give me a complete description of what you're doing.

A couple of other thoughts:

Both of these are documented on Spence Konde's README somewhere.

dragoncoder047 commented 1 year ago

Perhaps the ATtiny85 is damaged? Try another.

I doubt that's the problem, because a) I bought then new, and have only reprogrammed this particular one like 6 times, and b) in the past I have made the mistake of forgetting to move the ATtiny85 from the breadboard to the programmer and it gave a different error, like "failed to read chip signature registers", not "cannot open USB device".

Have you used Burn Bootloader to change the fuses to choose a different clock setting? If you chose a setting using an external crystal, subsequent uploads will only work if you have the crystal present.

No, I only have ever used the default 8Mhz internal clock. (The internal clock is part of the reason I chose the ATtiny85 anyway!)

I never really have to reboot my computer, but I'll try that when I get home from school.

dragoncoder047 commented 1 year ago

Unrelated to the ATtiny85 programmer issue:

I found this issue regarding the ESP32-C3 in particular timing out and getting confused by clock stretching. Maybe the plain ESP32 has the same bug.

The ESP32's Wire.cpp source says the timeout is 50 milliseconds which should be more than enough, but all the arduino documentation says the number is in microseconds -- I'm going to try increasing the timeout to 50000 and seeing if that changes anything...

dragoncoder047 commented 1 year ago

Update after rebooting computer:

technoblogy commented 1 year ago

ATtiny85 programmer still gives a "permission denied" error.

That's very odd. Can you think of anything else that might have changed since it last worked?

dragoncoder047 commented 1 year ago

That's very odd. Can you think of anything else that might have changed since it last worked?

The only thing that changed is that I updated my Arduino IDE, and I'm not running it with sudo anymore -- so I probably need to add avrdude to some permissions group or setting etc. because it isn't being run as root anymore. I don't have enough Linux experience to know what to do to fix that.

technoblogy commented 1 year ago

If that's the only thing that changed between it working and not working then that's probably the culprit.

Do you have an Arduino Uno (or equivalent)? Then you could try using that as an ISP programmer, as described here:

http://www.technoblogy.com/show?19OV#install

dragoncoder047 commented 1 year ago

Do you have an Arduino Uno (or equivalent)? Then you could try using that as an ISP programmer, as described here:

I have an Uno. I thought of that, but honestly I don't think the programmer is at fault, it's my operating system.

I found this, a similar problem:

https://stackoverflow.com/questions/26421803/run-my-executable-automatically-as-root-ubuntu

Going to try that on avrdude and see if that fixes it...

technoblogy commented 1 year ago

I think the Sparkfun Programmer is doing something clever with the USB port, whereas using the UNO is just using the UNO's standard USB port device.

dragoncoder047 commented 1 year ago

I found this, a similar problem:

https://stackoverflow.com/questions/26421803/run-my-executable-automatically-as-root-ubuntu

Going to try that on avrdude and see if that fixes it...

Sorry for the extremely late update... turns out it was a permissions problem. This worked!!

[/offtopic]

technoblogy commented 1 year ago

Good to hear!

dragoncoder047 commented 1 year ago

I'm just going to close this now because it turns out it was a problem with the ATtiny85 that multi-byte reads didn't work (see https://github.com/SpenceKonde/ATTinyCore/issues/776). Sorry for all the unnecessary bother.