Asked — Edited
Resolved Resolved by ptp!

I2c Response Not Recognized

I am trying to connect my IoTiny to an Arduino Pro Mini via I2C. I am able to successfully write for the IoTiny to the Arduino but when I try to request a response from the Arduino the data see corrupt.

I've tried very basic ez-script:

print(i2cRead( 0, 0xA,1 ))

And Arduino code:


  String outtoezb;

  outtoezb = "R";
  
  char  tempout[outtoezb.length() + 1];
  outtoezb.toCharArray(tempout,outtoezb.length() + 1); 

  Wire.write(tempout); 
  Serial.print("Response Sent: ");
  Serial.println(outtoezb);

When executed I get the follow on the IoTiny:


Start
> 
Done (00:00:00.0230307)

And I see that the request get through to the arduion:


My i2c address: 10
Response Sent: R

Any thoughts?


ARC Pro

Upgrade to ARC Pro

Become a Synthiam ARC Pro subscriber to unleash the power of easy and powerful robot programming

#9  

Well interesting.... I replaced my code with yours and I get a "H" as the first letter with 10 special characters (see attached) and the wire hangs. I have to disconnect and reconnect IoTiny to resume. If I only request one byte then the "H" is returned, the wire is closed and all is good. Not sure why requesting multiple bytes is freezing things up confused I'll continue to review the links and look into getting an analyzer.

User-inserted image

*** UPDATE ***

Okay much closer! We both had logic issues with our code but here is what I have so far....


#include <Wire.h> 

int _myi2c = 10;
int _pos = 0;
String _toSend = "Hello World";

void setup() {

  Serial.begin(57600); // Debug Console
  
  Serial.print("My i2c address: " );
  Serial.println(_myi2c);
  Wire.begin(_myi2c);   
  Wire.onRequest(receivei2cEvent); 
}

void receivei2cEvent() {
 //_pos = Wire.available();

  while (Wire.available()) { 
    char c = Wire.read();          // Read the bytes from Master (these show up as special characters on the wire if you skip this step)
  }
  for( int i =0; i <  _toSend.length(); i++)
     {  Wire.write(_toSend[i]);   // Loop through to send each character in the msg
      }
  Serial.println("event processed");
}

void loop() {
   
}


Now I get the full msg returned BUT the wire still remains open until I disconnect/reconnect the IoTiny.

PRO
USA
#10  

@msriddle68,

I have a working code from a previous integration, I'll post in 5 minutes.

PRO
USA
#11  

I tested the code with an Iotiny and arduino mini.

Note: Please use the code and do not change anything, and let me know the results.

Arduino code:


#include <Wire.h>

#define CMD_SAVE_BYTES                21
#define CMD_READ_BYTES                22

#define  SLAVE_MEMORY_MAX_READ_SIZE   16
#define  SLAVE_MEMORY_SIZE            256
uint8_t  slave_memory[SLAVE_MEMORY_SIZE]; 
volatile int last_cmd, last_cmd_addr, last_cmd_size;
//#define DEBUG

void setup() 
{
  //slave address
  Wire.begin(8);
  
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent); 
  Serial.begin(115200);
  Serial.println("Ready" ) ;

  memset(&slave_memory, 0, sizeof(slave_memory));
}

void loop() 
{
  delay(2000);
}

void requestEvent() 
{
  switch(last_cmd)
  {
    case CMD_READ_BYTES:
    {
      #ifdef DEBUG
      Serial.print("Request/Read addr=" ) ;
      Serial.print(last_cmd_addr ) ;
      Serial.print(" size=" ) ;
      Serial.print(last_cmd_size ) ;
      #endif
      if (last_cmd_size<=SLAVE_MEMORY_MAX_READ_SIZE && last_cmd_addr+last_cmd_size<SLAVE_MEMORY_SIZE)
      {
        Wire.write(&slave_memory[last_cmd_addr], last_cmd_size);
      }
      else
      {
        #ifdef DEBUG
        Serial.print("**INVALID ARGS**");        
        #endif
      }
      #ifdef DEBUG
      Serial.println();
      #endif
      break;
    }
    default:
    {
      #ifdef DEBUG
      Serial.print("Invalid Request last_cmd=" );
      Serial.print(last_cmd);
      Serial.println();
      #endif
      break;
    }
  }
}

void receiveEvent(int bytes_read) 
{
  if (Wire.available()<1)
  {
    return;
  }
  
  last_cmd = Wire.read();
  
  switch(last_cmd)
  {
    case CMD_SAVE_BYTES:
    {
      int addr = Wire.available()>0 ? Wire.read() : 0;
      #ifdef DEBUG
      Serial.print("Save bytes addr=" );
      Serial.print(addr);
      Serial.print(" size=" );
      Serial.print(bytes_read-2);
      Serial.print(" bytes=[" );
      #endif
      int ix=0;
      while (Wire.available()>0 && addr<SLAVE_MEMORY_SIZE) 
      { 
        int b = Wire.read();
        slave_memory[addr++] = b;
        #ifdef DEBUG
        if (ix++>0) 
        { 
          Serial.print(" "); 
        }
        Serial.print(b);
        #endif
      }
      #ifdef DEBUG
      Serial.println("]" );
      #endif
      break;
    }
    case CMD_READ_BYTES:
    {
      last_cmd_addr = Wire.available()>0 ? Wire.read() : -1;
      last_cmd_size = Wire.available()>0 ? Wire.read() : -1;
      #ifdef DEBUG
      Serial.print("Read bytes addr=" );
      Serial.print(last_cmd_addr);
      Serial.print(" size=");
      Serial.print(last_cmd_size);
      #endif
      if (last_cmd_size<=SLAVE_MEMORY_MAX_READ_SIZE && last_cmd_addr+last_cmd_size<SLAVE_MEMORY_SIZE)
      {
        #ifdef DEBUG
        Serial.print(" OK" );        
        #endif
      }
      else
      {
        #ifdef DEBUG
        Serial.print(" **INVALID ARGS**" );        
        #endif
      }
      #ifdef DEBUG
      Serial.println();
      #endif
      break;
    }
  }
}

EZ-Scripts

Script 1: Write to the slave:


# writes a msg to the slave:
# msg[0] = 21 = CMD_SAVE_BYTES
# msg[1] = Address position
# msg[2..n] = msg content
#
# Note: Some i2c implementations (Wire) have buffer size constrains.
# I'll break the msg content in 2 write operations

# --- part 1 ---
$text="The quick brown fox"
$text_len=length($text)
DefineArray($numeric_array, $text_len+2, 0)
$numeric_array[0]=21
$numeric_array[1]=0
repeat ($ix1, 0, $text_len-1, 1)
  $numeric_array[$ix1+2]=GetByteAt($text,$ix1)
endrepeat
print("Status: Sending addr="+$numeric_array[1]+" textSize="+$text_len)
I2CWriteBinary(0, 0x08, $numeric_array)

# --- part 2 ---
$text=" over the lazy dog"
$text_len=length($text)
DefineArray($numeric_array, $text_len+2, 0)
$numeric_array[0]=21
$numeric_array[1]=$ix1+1
repeat ($ix2, 0, $text_len-1, 1)
  $numeric_array[$ix2+2]=GetByteAt($text,$ix2)
endrepeat
print("Status: Sending addr="+$numeric_array[1]+" textSize="+$text_len)
I2CWriteBinary(0, 0x08, $numeric_array)

Script 2: Read from the slave:


I2CClockSpeed(0, 10000)
$result="" 

#send to slave addr=8 cmd=22(ReadBytes) Addr=29 length=4
I2CWrite(0, 0x08, 22, 29, 4) 
$result=i2cRead(0, 0x08, 4)
print("Read: [" + $result + "]") 

#send to slave addr=8 cmd=22(ReadBytes) Addr=16 length=8
I2CWrite(0, 0x08, 22, 16, 8) 
$result=i2cRead(0, 0x08, 8)
print("Read: [" + $result + "]") 

Steps:

  1. Flash the arduino code
  2. ARC: Run script1 then script2

expected results:

User-inserted image

PRO
Synthiam
#12  

Ah! Receive and request interrupt events with an event type variable. Now that's how it should be done! I didn't see any of that in the documentation for arduino when I googled. Super awesome it exists and you know about it - because I was wondering how it would magically exist with only one interrupt and no event type.

Thanks for helping! We'll have to turn this into a tutorial

PRO
USA
#13  

@DJ,

Correct, let's wait to see if it works for the OP. I would like to share more details.

Like you said before I2C is not a serial protocol and is very sensitive.

I2C protocol is inherently half-duplex, basically the master controls the clock and sends the first byte (7 addr bit) with 1 bit (read/write). So the slave obey promptly to the master and performs two different and isolated operations: receiving and sending data.

PRO
Synthiam
#14  

Why do I see people use delay() in the arduino loop when there's no code? I see it everywhere but I don't know why it's there - it creates extra processing, and the stack nests. So when the interrupts raise, the stack has to be backed up and stopping the demay() anyway. Which slows down the interrupt from starting.

PRO
USA
#15  

@DJ,

There is no need/excuse for a delay an empty loop function is enough for this particular example.

The original code before the cut, is a man-in-middle implementation to debug a micro-controller firmware. So i used a micro-controller with my code to simulate the same behavior and every 2 seconds sends a serial debug message. So the delay is left overs:)

BUT

I was lazy... the correct way is to measure time deltas and execute when the condition is true.

#16  

Awesome guys! This sample works and releases the wire correctly. I can break it down to figure out my implementation. I really appreciate all the support!:):):)