Smbus2 to TinyWireS

I'm trying to interface a Pi with smbus2 to an ATTiny85 with TinyWireS.
I have two questions:

  1. Why does the smbus write not work?
  2. Why does the read i2c_block_data return only 0xff where the single byte read succeed?

Here is the output from the python script:
Test started
Results from smbus2 single byte reads:
Register 0 = 0x20
Register 1 = 0x21
Register 2 = 0x22
Register 3 = 0x23
Register 4 = 0x24
Register 5 = 0x25
Register 6 = 0x26
Register 7 = 0x27

Results from smbus2 block read:
Register 0 255
Register 1 255
Register 2 255
Register 3 255
Register 4 255
Register 5 255
Register 6 255
Register 7 255

The AtTiny85 code comes from the example here and looks like this:

 #include <TinyWireS.h>

#ifndef TWI_RX_BUFFER_SIZE
#define TWI_RX_BUFFER_SIZE ( 8 )
#endif

const byte I2C_SLAVE_ADDRESS = 4;

volatile byte i2c_regs[] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27};

// Tracks the current register pointer position
volatile byte reg_position;
const byte reg_size = sizeof(i2c_regs);

void requestEvent()
{  
    TinyWireS.send(i2c_regs[reg_position]);
    // Increment the reg position on each read, and loop back to zero
    reg_position++;
    if (reg_position >= reg_size)
    {
        reg_position = 0;
    }
}


void receiveEvent(uint8_t howMany)
{
    if (howMany < 1)
    {
        // Sanity-check
        return;
    }

    if (howMany > TWI_RX_BUFFER_SIZE)
    {
        // Also insane number
        return;
    }


    reg_position = TinyWireS.receive();
    howMany--;

    if (!howMany)
    {
        // This write was only to set the buffer for next read
        return;
    }
    while(howMany--)
    {
        i2c_regs[reg_position] = TinyWireS.receive();  // write data into reg
        reg_position++;
        if (reg_position >= reg_size)
        {
            reg_position = 0;
        }
    }
}

void setup()
{
  TinyWireS.begin(I2C_SLAVE_ADDRESS);
  TinyWireS.onReceive(receiveEvent);  // receive data from Pi to write into regs
  TinyWireS.onRequest(requestEvent);  // request to send data (regs) back to Pi
}  

void loop()
{
  TinyWireS_stop_check();
}

The python code on the Pi looks like this:

#!/usr/bin/python3

import smbus2
import time

bus = smbus2.SMBus(1)     # Pi uses i2c bus 1 
address = 4              # I2C slave Address  
upsRegister = list((0,0,0,0,0,0,0,0))
upsRegLen = len(upsRegister)


# -------------------------------------------

def writeReg(offset, data):
    try:
        bus.write_i2c_block_data(address, offset, data)
        return True
    except OSError as oserr:
        message = "writeRegisters  " + str(oserr)
        print(message)
        return False

def readReg1():  # using loop with single byte reads
    try:
        bus.write_byte(address, 0)  # start from reg 0
        for reg in range (8):
            upsRegister[reg] = bus.read_byte(address)
        return True
    except OSError as oserr:
        print("readRegisters  " + str(oserr))
        return False

def readReg2():  # using block read
    try:
        rxblock = bus.read_i2c_block_data(address, 0, 8)
        return True, rxblock
    except OSError as oserr:
        print("readRegisters  " + str(oserr))
        return False, 0


def main():
    print("Test started")

    writeReg(0, [0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68])
    

# ----- Using single byte reads
    print("Results from smbus2 single byte reads:")
    if (readReg1()):
        for i in range (8):
            print("Register", i, "=", hex(upsRegister[i]))
    print()

# ----- Using block read

    print("Results from smbus2 block read:")
    success, rxblock = readReg2()
    for i in range (8):
        print("Register", i, rxblock[i])

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        sys.exit(0)

You define TWI_RX_BUFFER_SIZE as 8 . You transfer the register number and 8 bytes, so 9 bytes so howMany is 9 and receiveEvent() returns without doing anything.

The read_i2c_block_data() does the complete read in one I2C transaction (from start condition to stop condition with one repeated start condition). But you provide only one byte so the smbus block read will fail as the slave returns NAK (SDA passively pulled to high). Additonally I'm not sure if TinyWireS can handle a repeated start condition.

SMbus is a subset of I2C and you should read the documentation of it to know what exactly happens with each allowed operation.

@pylon Thank you for the reply.
OK, I've increased the TWI_RX_BUFFER_SIZE and the write now works.
I've searched for detailed doco on TinyWireS and smbus but haven't been able to find any. For TinyWireS just the example code in github. Where is the explanation of onReceive and onRequest and the parameters passed with them?
Also TinyWireS_stop_check(). I've seen some examples where it is included and others where it is not.

The USI hardware doesn't handle I2C stop conditions by an interrupt, you have to poll for it. That's why you have to call that function quite often to handle the slave's reads.

I think for TinyWireS there isn't much more documentation than the example code and the source of the library. I checked the source code and I was wrong too, with this library you must not use the send() method more than once per onRequest call.

The problem is most probably in the repeated start condition, thie library doesn't handle that and I'm not sure if the hardware can handle it correctly. So simply don't use the SMbus block read on the master, the slave doesn't know how to answer.

Thanks again for for your information.
I can live with byte-at-a-time transactions so I'll just run with that.
it's a pity the code isn't documented more thoroughly. It wouldn't be a huge job for someone who knows how it all works and it could save a lot of people a lot of time and frustration in the long run.

Most users of TinyWireS are experts who know how the hardware works and what the software does. The Tiny series isn't a good choice for beginners.
At least there are a few important comments so you don't overlook some important details.
But of course you're welcome to write some documentation.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.