stevieb9
Posts: 15
Joined: Fri Jan 13, 2017 2:40 pm

wiringPiI2CWriteReg16() and Arduino discrepancies

Fri Jun 23, 2017 12:59 am

Hi all,

Long time lurker, first question.

I am the author of WiringPi::API Perl wrapper for Gordon's wiringPi (amongst a bunch of other RPi related software on the CPAN).

Due to writing and building an automated CI test platform for some of my code, I needed to integrate with an Arduino via I2C for quick one-off testing of analog to digital inputs instead of wiring up one of my ADCs to my Pi every time.

I'm noticing an odd quirk with *wiringPiI2CWriteReg16()* and although I'm going to spend a lot more time tracking it down, I thought I'd ask here to look for suggestions on what I may be missing.

Here's a minimalistic Arduino sketch I've pared down for this example. It runs equally flawlessly on an Uno R3 and Pro Trinket:

Code: Select all

#include <Wire.h>

#define SLAVE_ADDR 0x04

void receive_data (int num_bytes){
  Serial.print("bytes in: ");
  Serial.println(num_bytes);
  
  while(Wire.available()){
    int data = Wire.read(); // tried char, uint8_t etc
    Serial.println(data);
  }
  Serial.print("\n");
}

void setup() {
  Serial.begin(9600);
  Wire.begin(SLAVE_ADDR);
  Wire.onReceive(receive_data);
}

void loop() {
  delay(500);
}
I'm bypassing my Perl routines here in an attempt to get to the bottom of the issue. This is my minimalistic repeatable C code to replicate the issue:

Code: Select all

// word.c

#include <wiringPiI2C.h>

void main (){
    int fd = wiringPiI2CSetup(0x04);
    wiringPiI2CWriteReg16(fd, 0x00, 255);
    wiringPiI2CWriteReg16(fd, 0x01, 256);
}
I compile it like this:

Code: Select all

gcc -o word word.c -lwiringPi
Then:

Code: Select all

./word
The serial console on my Arduino:

Code: Select all

bytes in: 3
0
255
0

bytes in: 3
1
0
1
I specifically set the register to *0x01* in the second case, to see where that bit fell, which according to the output from the Arduino, is byte 1 (byte 0, but I digress for the purposes here). In the first call to *wiringPiI2CWriteReg16()*, the register (line 1) is 0x0 and the second byte is 255. All expected.

In the second call, I send in 256, expecting that to break apart into the 2nd and 3rd bytes, but that doesn't happen properly. The first byte (register) is set correctly per my call, the second byte is empty, yet the third byte contains the proper remainder.

Same situation occurs if I put 511 as the data value to the call, byte 2 is 0 and byte 3 is still 1. I would expect something more than 0 on line 2 with the remainder on line 3.

I'm hoping this is a case of Rubber Duck Debugging and that the answer will come to me as soon as I preview and then hit Submit, but in case its not, my questions are...

Can anyone spot out any issues in my code or see anything obvious? Is this an issue with the Arduino or the way I'm coding it, or is there a possible problem with wiringPi that I haven't tracked down yet?

Thanks,

Steve

ps. In all cases I'm testing, I'm using wiringPi 2.36+

User avatar
DougieLawson
Posts: 35840
Joined: Sun Jun 16, 2013 11:19 pm
Location: Basingstoke, UK
Contact: Website Twitter

Re: wiringPiI2CWriteReg16() and Arduino discrepancies

Fri Jun 23, 2017 9:16 am

It's time to upgrade to WiringPi 2.44 and test again.
Note: Having anything humorous in your signature is completely banned on this forum. Wear a tin-foil hat and you'll get a ban.

Any DMs sent on Twitter will be answered next month.

This is a doctor free zone.

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: wiringPiI2CWriteReg16() and Arduino discrepancies

Fri Jun 23, 2017 10:41 am

stevieb9 wrote:Hi all,

Long time lurker, first question.

I am the author of WiringPi::API Perl wrapper for Gordon's wiringPi (amongst a bunch of other RPi related software on the CPAN).

Due to writing and building an automated CI test platform for some of my code, I needed to integrate with an Arduino via I2C for quick one-off testing of analog to digital inputs instead of wiring up one of my ADCs to my Pi every time.

I'm noticing an odd quirk with *wiringPiI2CWriteReg16()* and although I'm going to spend a lot more time tracking it down, I thought I'd ask here to look for suggestions on what I may be missing.
Emailing the wiringPi author directly is also recommended...

Here's a minimalistic Arduino sketch I've pared down for this example. It runs equally flawlessly on an Uno R3 and Pro Trinket:

Code: Select all

// word.c

#include <wiringPiI2C.h>

void main (){
    int fd = wiringPiI2CSetup(0x04);
    wiringPiI2CWriteReg16(fd, 0x00, 255);
    wiringPiI2CWriteReg16(fd, 0x01, 256);
}
I compile it like this:

Code: Select all

gcc -o word word.c -lwiringPi
Then:

Code: Select all

./word
The serial console on my Arduino:

Code: Select all

bytes in: 3
0
255
0

bytes in: 3
1
0
1
So what's the problem? It's working correctly as far as I can tell (although I don't use the Arduino 'Wire' library myself)

In the first call, you write 255 to register 0 - so on the 'wire', there are 4 bytes - the first is the address (which is hidden), then the register - 1, correct, then 255 encoded as a 16-bit value, in least significant byte first, so; 255, 0.

The 2nd is also correct, 1 (register), then 256 which is again sent as a 16-bit value, LSByte first; 0, 1, or 0x0100 when reassembled which is 256 in decimal.

No idea about your 511 case though, but bear in-mind that the wiringPi I2C stuff is just dumbed-down wrappers round the bog-standard Linux SMBus interface - doesn't mean my wrappers are correct, but this is the first time anyone has reported issues like this.
ps. In all cases I'm testing, I'm using wiringPi 2.36+
Since kernel 4.9 has been a thing, I've had over 1000 emails so-far from people using other peoples libraries with old versions of wiringPi statically linked into various projects and wrappers.

Stop using OLD versions of wiringPi - perl wrappers with wiringPi < 2.44 FAIL on 4.9 kernels. I fixed wiringPi for 4.9 back in December, please make sure anything you release with a statically compiled version of wiringPi has the latest. (Better still, don't statically link wiringPi, use the packaged ones).

-Gordon
--
Gordons projects: https://projects.drogon.net/

stevieb9
Posts: 15
Joined: Fri Jan 13, 2017 2:40 pm

Re: wiringPiI2CWriteReg16() and Arduino discrepancies

Fri Jun 23, 2017 12:45 pm

First off, thanks for the responses.

Second, I have to apologize. I should have actually checked the version as opposed to simply stating "v2.36+", as I am actually on 2.44:

Code: Select all

$ gpio -v
gpio version: 2.44
Copyright (c) 2012-2017 Gordon Henderson
This is free software with ABSOLUTELY NO WARRANTY.
For details type: gpio -warranty
The problem was in my Arduino code. Gordon tipped me off that the bytes are read in separately, LSB first. Throughout all of my searching, I hadn't come across that, and really didn't quite understand what was happening. After changing my I2C read code in my Arduino sketch to this:

Code: Select all

  while(Wire.available()){
    Wire.read(); // throw away register byte
    
    int16_t data = Wire.read();
    data += Wire.read() << 8;
    
    Serial.print("data: ");
    Serial.println(data);
  }
...everything works.

Thanks again!

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: wiringPiI2CWriteReg16() and Arduino discrepancies

Fri Jun 23, 2017 1:19 pm

stevieb9 wrote: The problem was in my Arduino code. Gordon tipped me off that the bytes are read in separately, LSB first. Throughout all of my searching, I hadn't come across that, and really didn't quite understand what was happening.
To be pedantically correct; they are sent LSB first. Who knows what's on the other end...

You can use

Code: Select all

#include <byteswap.h>
....
  val = __bswap_16 (val) ;
If you want to do it at the Pi end. See my ads1115 driver for an example.

Also - Pi to Arduino via I2C... Icky. Technically needs level shifters if it's a 5v device, although you can get away with it without them - just don't enable the internal pull-ups at the ATmega end. Using USB serial for data exchange is far easier and faster and powers the Arduino for free...

-Gordon
--
Gordons projects: https://projects.drogon.net/

Return to “C/C++”