Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OnRequest handler #1

Closed
lunanigra opened this issue Oct 25, 2012 · 6 comments
Closed

OnRequest handler #1

lunanigra opened this issue Oct 25, 2012 · 6 comments
Assignees
Labels

Comments

@lunanigra
Copy link

Hello, it's more a question...

Trying to set up communication between an Arduino and an ATTiny 85. I've implemented an OnRequest handler and it works fine if just sending back one single byte.

If send back more bytes (e.g. calling TinyWireS.send twice) the whole I2C communication crashes.

Not sure if my approach is wrong or how to set up communication between Arduino and ATTiny 85 beyond exchanging single bytes.

Thanks a lot, JC

@rambo
Copy link
Owner

rambo commented Oct 25, 2012

You can send only one byte per read request from the master, this is I2C level constraint/requirement. So send multiple bytes you need to track what bytes have been sent and send the next byte whenever a read-request comes in.

So first try https://github.com/rambo/TinyWire/blob/master/TinyWireS/examples/attiny85_i2c_slave/attiny85_i2c_slave.ino on the tiny and https://github.com/rambo/I2C/blob/master/examples/i2crepl/i2crepl.ino on the Arduino (the tiny sketch has the test transactions I have used)

@ghost ghost assigned rambo Oct 25, 2012
@rambo
Copy link
Owner

rambo commented Oct 25, 2012

I doublechecked the code, the send() method will put stuff to the send-buffer and return, but will block if the buffer is full, waiting for free space, hitting this block inside OnRequest handler will lead to bad results (as the handler will never return and the buffer will never get emptied. calling send() twice in the handler will soon lead to this situation...

OTOH when not using the event-oriented approach it's nicer that it will simply block untill buffer has free space, as this is the way the Wire library works as well.

@lunanigra
Copy link
Author

Thanks for checking again. I will try to see if your sample code does what I am looking for. Right now I'm little confused as it seems the I2C implementations on Arduino, ATTiny and Raspberry PI are quite different. I've seen you have created an improved I2C impl. for Arduino.

I will short describe what I try to build... I want to connect different ATTiny modules to a Raspberry PI (or first Arduino). Each ATTiny holds a data array. The I2C master can request this array. It should be also possible that the I2C master sends two commands to the ATTiny.

  1. A new bus address to be stored in EEPROM.
  2. An update for the data record.

The event-oriented approach sounds quite elegant. But maybe I should try a simpler way first if I otherwise run in this blocking situation.

@rambo
Copy link
Owner

rambo commented Oct 25, 2012

You probably need to use event-oriented to be able to write new bus addresses reliably, I'm not 100% sure I have actually tested this feature but I did code it here: https://github.com/HelsinkiHacklab/aircores/blob/master/driver/AirCore_ATTiny/AirCore_ATTiny.ino (currenly we're focused on the arduino shields capable of running 8 cores at the same time and it works mostly fine, sometimes the I2C gets stuck [the core positions are updated 4 times a second and there are 33 cores driven by 5 boards, and there's plenty of other traffic on the bus as well], check out: https://github.com/HelsinkiHacklab/reactor/)

Anyway I suggest you have a defined register structure with the data-array starting from the first index and automatic index incementing, like in the example sketch, then after the data have the address and any other calibration bit you wish to write to EEPROM and copy the idea from the aircore driver sketch.

Then to read the data array you would do (using the repl semantics and 0x4 as the 7-bit address): [ 8 0 [ 9 r r ]
supposing two value array, add as many reads as needed to get full array. in longer form what is does is issue START then the write address for the slave, write 0 for the first index of the register then issue RSTART and the read-address for the slave and then do as many reads as needed and then STOP. The slave code will auto-increment the register index at each read.

Updating the data would be even simpler: [ 8 0 be ef ]
this enters write mode, sets first index and then writes the data

In the aircore example setting the slave address to 0x5 (7-bit address) would be: [ 8 2 5 1 ]
The new address should take effect in next reboot of the chip (which is surprisingly hard to do via software, using ASM and jumping to 0 is not the same thing as proper reboot)

The I2C master implementation is not by me, as stated in the description, but I have made some improvements and the upstream is not in any sensible repository so I maintain my fork... Anyways it's a lot better than Wire in so many ways. I have a raspberryPI but haven't yet had time to play with it much so can't help with implementing the I2C master on it.

@lunanigra
Copy link
Author

Your multi-core Arduino concept sounds quite interesting :-)

Unluckily I'm missing some basics... Don't really get your REPL semantics.

So, I've tried a simpler approach first. And it doesn't look too bad...
https://github.com/haddorp/playground/blob/master/ATTiny/i2c_slave.ino

The Arduino (later Raspberry PI) send a single byte as command identifier. For writing data further bytes will follow. Otherwise the ATTiny send back array data and appends a check sum. My first test has been success and no system crash.

But I assume my code isn't optimal and that there are some points to enhance.

@rambo
Copy link
Owner

rambo commented Oct 26, 2012

cores as in air-core, which is a kind of a motor (very low torque but incredibly fast and can be driven to an absolute position without any external feedback, good for dials & gauges [often used when building flight-simulator cockpits])

The semantics are the same as with bus-pirate (a very handy tool but the I2C on it chokes if slave stretches the clock): [=START, hex-numbers for bytes to send, ]=STOP, r=READ, see "in longer form" in the post above for explanation.

Resetting via watchdog does not disable the watchdog, this can lead to a reset-loop if setup() does not disable the watchdog quickly enough (at least this is what I read somewhere when looking at strategies for resetting via sw).

The "usual" way to communicate with I2C devices is to first write the number of the register (or "command byte") you wish to read or write and then either just continue writing or issue a repeated-start and send the slave read-address and start reading. This is all by convention since the I2C protocol only specifies if the request is read or write, very simple devices for example can have only one register that is read-only.

Anyway, if your code works for you then all is good, it doesn't have to be "elegant"

@rambo rambo closed this as completed Oct 29, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants