Raspi I2C communication with Arduino issues with wiringPi - arduino

I'm experiencing unexpected results using wiringPi's wiringPiI2CWriteReg16() function, and I'm not sure if it's due to incorrect usage, or something else. This is the declaration for the function:
extern int wiringPiI2CWriteReg16 (int fd, int reg, int data);
There are notes within the wiringPiI2C.c file that state it resembles Linux's SMBus code, if that helps.
On my Arduino (both an Uno R3 and a Trinket Pro), I am running this pared-down sketch:
#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 would think that Wire.read() would break things apart at the byte boundary, but that's not occurring in my case. Perhaps this is my issue... a misunderstanding.
Nonetheless, I have this C code (requires wiringPi v2.36+ to be installed):
// word.c
#include <wiringPiI2C.h>
void main (){
int fd = wiringPiI2CSetup(0x04);
wiringPiI2CWriteReg16(fd, 0x00, 255);
wiringPiI2CWriteReg16(fd, 0x01, 256);
}
Compiled like this:
gcc -o word word.c -lwiringPi
When run, ./word, I receive the following on my Arduino's serial output:
bytes in: 3
0
255
0
bytes in: 3
1
0
1
In the first call to wiringPiI2CWriteReg16(), I expect the first byte in the output to be zero (0x00) as that's the register address I'm requesting. The second byte (255) is also correct. The third byte (0) is meaningless from what I can tell (as I'm only sending in one byte as data).
However, in the second call to that function, I do get the correct output for the register (first byte as 0x01 == 1), but the second byte is zero, and the third byte has what appears to be the correct remainder (255 == one byte, + 1). The problem is, is that the second byte is 0.
The exact same effect happens if I pass in 511 or for that matter, any number as the data in the call.
My question is whether I'm missing something glaringly obvious (I'm relatively new to C and Arduino), and/or if I can get some pointers on how to troubleshoot this more thoroughly.

I found that the problem was in my Arduino code. In the official Raspi forums, 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 loop code in my Arduino sketch to this:
while(Wire.available()){
Wire.read(); // throw away register byte
int16_t data = Wire.read(); // LSB
data += Wire.read() << 8; // MSB
Serial.print("data: ");
Serial.println(data);
}
...everything works. In effect, that's at least one way to read two-byte values over I2C on the Arduino and put the bytes back together.

Related

I2C Data Conversion -- Arduino

Im trying to read the pressure from a sensor using I2C. Per the datasheet, I am writing a start command (0xAA) and then reading the data in the pressure registers as bytes. (Reference picture below):
Registers of Pressure Data
Here is the code that I am using:
#include <Adafruit_I2CDevice.h>
#define I2C_ADDRESS 0x29
Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(I2C_ADDRESS);
void setup() {
while (!Serial) { delay(10); }
Serial.begin(115200);
Serial.println("I2C device read and write test");
if (!i2c_dev.begin()) {
Serial.print("Did not find device at 0x");
Serial.println(i2c_dev.address(), HEX);
while (1);
}
Serial.print("Device found on address 0x");
Serial.println(i2c_dev.address(), HEX);
}
void loop(){
uint8_t buffer[7];
buffer[0] = 0xAA;
i2c_dev.write_then_read(buffer, 1, buffer, 2, false);
Serial.println();
Serial.print(buffer[0], HEX);
Serial.print(" ");
Serial.print(buffer[1]);
Serial.print(" ");
Serial.print(buffer[2]);
Serial.print(" ");
Serial.print(buffer[3]);
Serial.println();
delay(500);
}
The first Register (buffer[0]) is the status register, which is supposed to return 0x40 if the operation succeeded without any errors. However, I am getting 0x60 when I read the first register. Is my code incorrect?
Here is the datasheet for the pressure sensor: https://www.farnell.com/datasheets/3208001.pdf
0x40 is completed reading without error.
In datasheet you can read that "When the Busy bit in the Status byte is zero, this indicate that valid data is ready, and a full Data Read of all 7 bytes may be performed"
So i think you just need to wait some time (and poll status bit) and then read data.
Hi Stephen are you aware the 0x40 is written out in binary 01000000 and 0x60 01100000?
If you look at table 3 you can find the relevant bits in that return byte. The first 0 is bit 7. The first 1 bit 6 etc to bit 0 (the utmost right 0)
Bit 5 (the second 1 from the left) is 1, so that means the device is busy.
Sometimes i use a binary to hex calculator.
You could read out the value per x time and check if it will become 0x40 and then process the other data bytes.

Wire.read() returns only 7-bit bytes

I'm a newbie to both Arduino programming and I2C in general. I'm using an Arduino Micro to extract data from the capacitance sensor AD7150.
Now in trying to answer my question myself, I've seen the basic setup of the Wire I2C communication a lot, and I feel like I have it down. But for some reason, Wire.read() only ever returns 7-bit bytes to me when it should be 8 bits from all that I see and understand. Another problem is that even if I read out a register that should be stable, i.e. the Chip ID register, the output "wobbles" over time. Google hasn't helped so far. Maybe one of you has the magic answer?
#include <Wire.h>
#define AD 0x48
#define ID 0x17
byte data1;
byte data2;
uint16_t data_combined;
void setup() {
analogReference(EXTERNAL);
Wire.begin();
Serial.begin(9600);
}
void loop()
{
Wire.beginTransmission(AD);
Wire.write(ID);
Wire.endTransmission(); // I checked the output; is successful
delay(10);
Wire.requestFrom(0x48,2); // Unsure whether to ask for 1 or 2 bytes here; the ChipID, as far as I understand, should only be one byte, but I've been told that I2C transmissions always include at least 2 bytes
data1 = Wire.read(); // so I just save both bytes separately here.
data2 = Wire.read();
data_combined = word(data1,data2);
Serial.print("First byte from Chip ID register: ");
Serial.print(data1,BIN);
Serial.print(" | Second byte from Chip ID register: ");
Serial.print(data2,BIN);
Serial.print(" | Combined to make: ");
Serial.println(data_combined,BIN);
Serial.println("---");
delay( 200);
}

How do make code compatible to ESP32 board?

I'm trying to get a GY-US-42 ultrasonic sensor working on the ESP32. However, I keep getting an error while compiling. For and Arduino Board it is not a problem, but for the ESP32.
My code:
#include "Wire.h"
//The Arduino Wire library uses the 7-bit version of the address, so the code example uses 0x70 instead of the 8-bit 0xE0
#define SensorAddress byte(0x70)
//The sensors ranging command has a value of 0x51
#define RangeCommand byte(0x51)
//These are the two commands that need to be sent in sequence to change the sensor address
#define ChangeAddressCommand1 byte(0xAA)
#define ChangeAddressCommand2 byte(0xA5)
void setup() {
Serial.begin(115200); //Open serial connection at 9600 baud
Wire.begin();
// changeAddress(SensorAddress,0x40,0);
}
void loop(){
takeRangeReading(); //Tell the sensor to perform a ranging cycle
delay(50); //Wait for sensor to finish
word range = requestRange(); //Get the range from the sensor
Serial.print("Range: "); Serial.println(range); //Print to the user
}
//Commands the sensor to take a range reading
void takeRangeReading(){
Wire.beginTransmission(SensorAddress); //Start addressing
Wire.write(RangeCommand); //send range command
Wire.endTransmission(); //Stop and do something else now
}
//Returns the last range that the sensor determined in its last ranging cycle in centimeters. Returns 0 if there is no communication.
word requestRange(){
Wire.requestFrom(SensorAddress, byte(2));
if(Wire.available() >= 2){ //Sensor responded with the two bytes
byte HighByte = Wire.read(); //Read the high byte back
byte LowByte = Wire.read(); //Read the low byte back
word range = word(HighByte, LowByte); //Make a 16-bit word out of the two bytes for the range
return range;
}
else {
return word(0); //Else nothing was received, return 0
}
}
Error:
sketch/GY-US42_I2C.ino.cpp.o:(.literal._Z12requestRangev+0x0): undefined reference to `makeWord(unsigned short)'
sketch/GY-US42_I2C.ino.cpp.o: In function `requestRange()':
/Users/Arduino/GY-US42_I2C/GY-US42_I2C.ino:42: undefined reference to `makeWord(unsigned short)'
collect2: error: ld returned 1 exit status
The word() is for casting a variable or literal into a 16-bit word, it does not add two bytes into a 16-bit word as you do word(HighByte, LowByte), I'm actually surprise this even compiled in Arduino.
To get the range value, you could do:
int range = HighByte * 256 + LowByte;
or:
int range = ((int)HighByte) << 8 | LowByte; //cast HighByte to int, then shift left by 8 bits.
But since Wire.read() is returning an int instead of a byte(you can see its function prototype definition here), therefore you code can actually be written like this:
int reading = Wire.read(); //read the first data
reading = reading << 8; // shift reading left by 8 bits, equivalent to reading * 256
reading |= Wire.read(); // reading = reading | Wire.read()
By the way, when you use #define, you don't need to specifically cast the const value into specific data type, the compiler will take care of the optimization and the right data type, so:
#define SensorAddress byte(0x70)
would be just fine by defining like this:
#define SensorAddress 0x70
You also do not need to cast const value with byte(2) or return word(0). In the latter case, your function prototype already expect the return would be a data type of word.

Write an array of bytes to EEPROM from PROGMEM

I'm trying to store an array of LED patterns to EEPROM, to later write to an external flash module. Below is my header file containing the patterns.
#ifndef DefaultPatterns_h
#define DefaultPatterns_h
#define DEFAULT_SIZE 147
const byte defaultData[DEFAULT_SIZE] PROGMEM ={225,112,111,};
#define RAINBOWPIXEL_SIZE 972
const byte rainbowpixelData[RAINBOWPIXEL_SIZE] PROGMEM ={255,0,0,0,0,0,0};
#endif
I've cut some bytes out from the above patterns as they are too long for this post.
Below is my sketch that is taken from ESP8266 EEPROM example library and I'm trying to use it as a starting point. I've commented out some things I've tried. Remember the patterns are in a header file.
#include "EEPROM.h"
#include "defaultPatterns.h"
int addr = 0;// address of EEPROM to write to
void setup() {
EEPROM.begin(512);
}
void loop() {
Serial.begin(9600);
//const byte defaultData[] = PROGMEM(defaultData,DEFAULT_SIZE );
// write the value to the appropriate byte of the EEPROM.
// these values will remain there when the board is
// turned off.
// EEPROM.write(addr, val);
//EEPROM.write(addr,byte (defaultData)) sizeof(defaultData));
//EEPROM.write ( 0, (byte *)defaultData), sizeof(defaultData)));
const char pattern = defaultData ;
EEPROM.write(addr, pattern);
// advance to the next address. There are 512 bytes in
// the EEPROM, so go back to 0 when we hit 512.
// save all changes to the flash.
addr = addr + 1;
if (addr == 512) {
addr = 0;
EEPROM.commit();
}
delay(100);
}
I've tried all sorts of different ways, but I'm getting errors and syntax errors.
I've tried to do this myself but I'll be at it forever. Any help that would be great.
I'm trying to build a LED controller that can store lots of patterns in the form of the patterns in the header file and play them back via a WS2812B strip of LEDs on button press. I've a long way to go.
Why use flash you ask? Space, I need lots of space. I know flash wears out faster, but I can replace it for a few bucks.
Look at the definition of EEPROM.write() - it only writes one byte. You need to use a for loop or EEPROM.put().

Docklight and Arduino serial communication (sending hex through RS232)

I have the following code (I'm using Arduino IDE and a NodeMCU with a MAX3232):
#include <SoftwareSerial.h>
SoftwareSerial RS232Serial(D1, D2); // RX | TX
void setup() {
Serial.begin(9600);
while (!Serial);
RS232Serial.begin(9600);
while (!RS232Serial);
}
void loop() {
Serial.println("Sending msg...");
char msg[] = {0x7E, 0x00, 0x78, 0xF0, 0x7E};
RS232Serial.write(msg, sizeof(msg));
Serial.println("Waiting incomming message:");
while(RS232Serial.available() <= 0) {
delay(1);
}
while(RS232Serial.available() > 0) {
Serial.print(RS232Serial.read(), HEX);
}
Serial.println("");
Serial.println("------------ END -----------");
}
I'm using Docklight to test the communication. So far I'm able to receive the HEX that the NodeMCU boards sends first char msg[] = {0x7E, 0x00, 0x78, 0xF0, 0x7E};. But I'm unable to receive the data properly from Docklight.
The HEX I'm trying to send from Docklight is 7E 00 00 70 15 7E. Debugging with serial monitor, sometimes I receive just 7E and sometimes 0FF, never the complete message. Baud rate, start/stop bits and parity are set to default.
What's wrong with my code?
One reason for not working as expected is:
You are processing the answer as soon as one byte has appeared in the receive buffer.
There is a related example in the Docklight Application Note Arduino Serial Communication - check the code listing on page 5. It makes use of the Arduino SerialEvent() function to collect the incoming telegram byte-per-byte. Only after receiving the end of telegram mark (here: a line feed "\n" character), it sets a flag that tells the code in the main loop() to do something with the accumulated data telegram.
The SerialEvent approach used in the Docklight exampleis not a must. You can solve this inside loop() alone, too. But you certainly need to change the
while(RS232Serial.available() <= 0) {
...
while(RS232Serial.available() > 0) {
parts to something that will wait until a full telegram has been received (or a timeout has occurred).
Now what irritates me still is that you write you sometimes receive "0FF" even though you never send it from Docklight. This could indicate still a second problem on the actual RS232 communication, but it's hard to guess without extra information.
But re-working the telegram receiving part as described above should already make things much clearer and then the source of the "0FF" is probably more obvious.
while(RS232Serial.available() <= 0) {
delay(1);
}
You are only waiting for the serial buffer to have at least 1 byte, but you are not guaranteed that your full message (6 bytes) will be present, when you call RS232Serial.available().
You should wait for the serial buffer to accumulate at least 6 bytes, before you try to read them.
while(RS232Serial.available() < 6) {
delay(1);
}
They you can read the full buffer with:
while(RS232Serial.available() > 0) {
Serial.print(RS232Serial.read(), HEX);
}
or only 6 bytes with:
for (int i=0; i<6; i++) {
Serial.print(RS232Serial.read(), HEX);
}
I don't know about your 0FF, but Serial.print(RS232Serial.read(), HEX) doesn't zero pad the hex string and this could actually be 00 0F 0F or 00 FF.
You should print out the bytes with:
byte b = RS232Serial.read();
if (b < 16) Serial.print('0'); // zero pad
Serial.print(b, HEX);
Serial.print(' '); // add space after each byte

Resources