Analog read value (int) to byte or char (Wire library)

Hi everybody,

Sorry for this question which as been post so much time but I'm lost...

I want to send an analog value (0-1024) through I2C from one arduino to another.
I use Wire.h library but I can only send "byte" or "char" ( with Wire.send() )

Thanks to nice example I succed to send byte or char (So my wiring between both arduino is correct)

But I dont now how to send an analog value,

  • Sould I convert it to char or byte ?
  • How to do that ?
  • How to convert back to int ?

PS: The best help (for me and I suppose for all the newbies like me :slight_smile: ) would be to add an example in the wire library because there is only digital data I2C sending example

Thank you by advance for you precious help

  • Sould I convert it to char or byte ?

Obviously.

  • How to do that ?

Use the highByte() and lowByte() functions to extract two bytes.

  • How to convert back to int ?

int val = hiByte << 8 + loByte;

PS: The best help (for me and I suppose for all the newbies like me :slight_smile: ) would be to add an example in the wire library because there is only digital data I2C sending example

What you will remember most, and be able to apply in other situations, is what you figure out on your own. So, I'm sorry, but I disagree that providing an example should be done, or would be best.

Thanks for this quick answer PaulS,

When I said "Sould I convert it to char or byte ?", I was thinking "what is the best ? convert "int to char" or convert "int to byte" ? (It's maybe a newbie question... according to your answer I think int to byte is a good choice )

Anyway, as you said it's better to try than follow an example so I tried that to understand the fonctionning of HigByte and lowByte but I dont understand why it doesn't work :

void loop()
{

readVal = analogRead(potPin);

Serial.println("read value");
Serial.println(readVal);

Serial.println("convert value to 16 bit word"); // I tried also without that conversion 
word wordVal = word(readVal);
Serial.println(wordVal);

Serial.println("cut value to 8 first bits and 8 last bits");
byte hiByte = highByte(wordVal);
byte loByte = lowByte(wordVal);
Serial.println(hiByte,BYTE);
Serial.println(loByte,BYTE);

Serial.println("get back the initial read value");
int val = hiByte << 8 + loByte;
Serial.println(val);
delay(1000);
}

Thanks for your help

Try:

byte hiByte = highByte(wordVal);
byte loByte = lowByte(wordVal);
Serial.println(hiByte, HEX);
Serial.println(loByte,HEX);

Saying "I dont understand why it doesn't work " doesn't help us; you have some idea of what "works" is, and why what you observe differs from that, but you haven't explained either.
It may simply be that what you expect is incorrect.

How is readVal declared?

You have a whole lot of debugging prints there. How about displaying what they show? And what you expect them to show?

The type "word" is unsigned int, so converting back to int will fail for some values.

You mention the Wire library in your subject line, but none of your code uses that.

Sorry AWOL,

I am quite stressed because I spend so much time to find on the net how to just send an analog value trough I2C and as I'm not programmer I'm probably not patient enought :slight_smile: and I assume that I dont take time enough to explain what is working and what is not :

So as you notice, first I didn't reach to display the loByte and HighByte value. I was expecting a value between 0-255 (corresponding to a decimal conversion of the lowByte and highByte) but when I wrote that it was displaying nothing or strange letters :

Serial.println(hiByte, BYTE);

Now thanks to your help I can display the lowByte and highByte in HEX or BIN or DEC
I fixed the input value to 659 and I have :
highByte=10
lowByte=10010011
So it's correct because 10 10010011(BIN) = 659 (DEC)

But now I dont understand why I can't get back to the initial decimal value, I suppose that this syntax assemble the two byte but I dont understand why it doesn't return the correct decimal value? :

int val = hiByte << 8 + loByte;

Thanks Nick, your were faster than me to answer,

Sorry I just copy and past the main loop, and I made this sample program just to test the conversion "int to byte" and "byte to int". I proceed step by step because I'm really a newbie with all that programming stuff.
Ok for the type word, it's seems to be useless for my problem (I just saw that on another post and I was thinking it was need for this conversion) but it's seems to works without this conversion..

here is my code :

// Déclaration des variables et librairies I2C
  #include <Wire.h>
  #include <stdlib.h>

#define potPin 0  // 
int readVal;

///////////////////////////////////////////////////////////////////////////////////////////////

void setup()
{
// Communication I2C
//  Wire.begin(2);                
//  Wire.onRequest(requestEvent); // register event
  
  Serial.begin(9600);
}

///////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{

readVal = analogRead(potPin);

Serial.println("read value");
Serial.println(readVal);

Serial.println("cut value to 8 first bits and 8 last bits");
byte hiByte = highByte(readVal);
byte loByte = lowByte(readVal);
Serial.println(hiByte,BIN);
Serial.println(loByte,BIN);

Serial.println("get back the initial read value");
int val = hiByte << 8 + loByte;
Serial.println(val);
delay(1000);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void requestEvent()
{
 // respond with message of 2 bytes
 // Wire.send(highByte(readVal)); 
 // Wire.send(lowByte(readVal)); 
}

My debugging prints now display :
read value
560
cut value to 8 first bits and 8 last bits
10
110000
get back the initial read value
0


Thanks for your help, I hope I'm clear now and hope that your are not fed up with my simple question...

int val = (hiByte << 8) + loByte;

Thank you very much ! I dont close the post because I will now apply this code for I2C...

I proceed step by step because I'm really a newbie with all that programming stuff.

This is a most excellent approach. Keep it up, and you WILL become a good programmer.

I think I have some way to do before beeing a good programmer and here is the proof ...

So I tried to apply the conversion to the I2C communication, my logic is :

  • The master ask two bytes to the slave (adresse #2).
  • The slave send two bytes back (first highByte, then lowByte)
  • Then master put the first byte in the first variable, then the second byte...
    (It's seems to work byte by byte as I saw on some other post on the forum)
  • Finally the two variable are assemble and printed on the serial port..

Master code :

// Déclaration des variables et librairies I2C
  #include <Wire.h>


///////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(115200);  // start serial for output
}

void loop()
{
  Wire.requestFrom(2, 2);    // request 2 bytes from slave device #2
 
  Serial.println("potentiometer value"); 
 
  byte loByte;
  byte hiByte;
  
  while(Wire.available())    // slave may send less than requested
  { 
   // char c = Wire.receive(); // test with char
   // Serial.print(c);     

    hiByte = Wire.receive(); 
    loByte = Wire.receive();
  }
  Serial.println(hiByte,BIN); 
  Serial.println(loByte,BIN);
  int val = (hiByte << 8) + loByte;
  Serial.print(val);
    
  Serial.println("");
  delay(500);
}

Slave code :

// Déclaration des variables et librairies I2C
  #include <Wire.h>
  #include <stdlib.h>

#define potPin 0  // 
int readVal;

///////////////////////////////////////////////////////////////////////////////////////////////

void setup()
{
// Communication I2C
  Wire.begin(2);                
  Wire.onRequest(requestEvent); // register event
  
  Serial.begin(9600);
}

void loop()
{

readVal = analogRead(potPin);

}
/////////////////////////////////////////////////////////////////////////////////////////////////
void requestEvent()
{
// respond with message of 2 bytes
  Wire.send(highByte(readVal)); 
  Wire.send(lowByte(readVal)); 
// Wire.send("hello"); //test with char
}

Here is what I obtain on the serial port :

101111 - hiByte (BIN)
101111 - loByte (BIN)
12287 - val (assemble value, should be 1023)

I tried with a char example, my communication works well between the two arduino. I tried with different type of variable for hiByte and loByte but without success.

thanks again for your help.

Here is what I obtain on the serial port :

101111 - hiByte (BIN)
101111 - loByte (BIN)
12287 - val (assemble value, should be 1023)

I think you mean that the value should be less than or equal 1023. The fact that both hiByte and loByte print the same value leads me to suspect a problem.

Several things to try. First, all Serial.println() statements need a Serial.print() statement before them.

  Serial.println(hiByte,BIN);

should be

  Serial.print("hiByte = ");
  Serial.println(hiByte,BIN);

This way, there is no guessing what a number means.

Second, try storing the high and low bytes in separate variables before sending them.

  byte hi = highByte(readVal);
  byte lo = lowByte(readVal);
  Wire.send(hi); 
  Wire.send(lo);

Third, the master has some issues.

  while(Wire.available())    // slave may send less than requested
  { 
   // char c = Wire.receive(); // test with char
   // Serial.print(c);     

    hiByte = Wire.receive(); 
    loByte = Wire.receive();
  }

If you request 2, and the slave sends 1, what happens? What does Wire.receive() return if there is nothing on the wire?

I think the while() should be replaced with an if:

if(Wire.available() >= 2)
{
  // Read the two bytes...
}

Thanks for your advices,

So I modify the code as you said :

For master :

// Déclaration des variables et librairies I2C
  #include <Wire.h>


///////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(115200);  // start serial for output
}

void loop()
{
  Wire.requestFrom(2, 2);    // request 2 bytes from slave device #2
 
  Serial.println("potentiometer value"); 
 
  byte loByte;
  byte hiByte;
  
if(Wire.available() >= 2)    // slave may send less than requested
  { 
   // char c = Wire.receive(); // test with char
   // Serial.print(c);     

    hiByte = Wire.receive();
    loByte = Wire.receive();
  }
  Serial.print("hiByte = ");
  Serial.println(hiByte,BIN);
  Serial.print("loByte = "); 
  Serial.println(loByte,BIN);
  int val = (hiByte << 8) + loByte;
  Serial.print(val);
    
  Serial.println("");
  delay(500);
}

for salve :

// Déclaration des variables et librairies I2C
  #include <Wire.h>
  #include <stdlib.h>

#define potPin 0  // 
int readVal;
byte hi;
byte lo;
///////////////////////////////////////////////////////////////////////////////////////////////

void setup()
{
// Communication I2C
  Wire.begin(2);                
  Wire.onRequest(requestEvent); // register event
  
  Serial.begin(9600);
}

void loop()
{

readVal = analogRead(potPin);
hi = highByte(readVal);
lo = lowByte(readVal);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void requestEvent()
{
// respond with message of 2 bytes
  Wire.send(hi); 
  Wire.send(lo); 
// Wire.send("hello"); //test with char
}

With that program I made some test as you advice me :

I read that on the serial port (with the analog input connected to 5V):
potentiometer value
hiByte = 11111111
loByte = 11111111
-1
(That is why I said this value sould be 1023 or 1024 because I connect the analog input 5V)

I read that on the serial port (with the analog input connected to 3V):
potentiometer value
hiByte = 10111100
loByte = 11111111
-17153
or:
potentiometer value
hiByte = 10111101
loByte = 11111111
-16897
or:
potentiometer value
hiByte = 11000001
loByte = 11111111
-15873

I read that on the serial port (with the analog input connected to 0V):
potentiometer value
hiByte = 0
loByte = 11111111
255

Then I try to send 1 with the slave and ask 2 with the master as you said, so I just comment this line (Wire.send(lo)) in the slave code :

void requestEvent()
{
// respond with message of 2 bytes
  Wire.send(hi); 
 // Wire.send(lo); 
// Wire.send("hello"); //test with char
}

I read that on the serial port (with the analog input connected to 5V):
potentiometer value
hiByte = 11
loByte = 11111111
1023

I read that on the serial port (with the analog input connected to 3V):
potentiometer value
hiByte = 10
loByte = 11111111
767

I read that on the serial port (with the analog input connected to GND):
potentiometer value
hiByte = 0
loByte = 11111111
255

I also try to comment the other line (of course hiByte now mean display loByte on the serial port):

void requestEvent()
{
// respond with message of 2 bytes
 // Wire.send(hi); 
  Wire.send(lo); 
// Wire.send("hello"); //test with char
}

I read that on the serial port (with the analog input connected to 5V):
potentiometer value
hiByte = 11111111
loByte = 11111111
-1
or:
potentiometer value
hiByte = 11111110
loByte = 11111111
-257

I read that on the serial port (with the analog input connected to 3V):
potentiometer value
hiByte = 10111011
loByte = 11111111
-17409
or:
potentiometer value
hiByte = 10111101
loByte = 11111111
-16897
or
potentiometer value
hiByte = 10111010
loByte = 11111111
-17665

I read that on the serial port (with the analog input connected to 0V):
potentiometer value
hiByte = 0
loByte = 11111111
255

In the two last test were I send 1 and ask for 2 it's seems to correspond to what I expect but I didn't manage to hab both lo and hi correct in the same time, I tried to ask 3 bytes with the master and send hi and lo but it doesn't like the first test...
I hope these test will help ..

The only problem I see now is this:

if(Wire.available() >= 2)    // slave may send less than requested
  { 
   // char c = Wire.receive(); // test with char
   // Serial.print(c);     

    hiByte = Wire.receive();
    loByte = Wire.receive();
  }
  Serial.print("hiByte = ");
  Serial.println(hiByte,BIN);
  Serial.print("loByte = "); 
  Serial.println(loByte,BIN);
  int val = (hiByte << 8) + loByte;
  Serial.print(val);

You are printing the hiByte and loByte values even if you do not get a response, or a response of the proper length, from the slave. So, I suspect that multiple requests are made before enough information is received to satisfy the if test and to get valid data to print.

A couple of things to try. Since you need to get two bytes back (you, of course, need to actually send two bytes), put in a while loop, after the request:

while(Wire.available() < 2)
{
   Serial.println("Waiting for complete reply...");
   delay(250);
}

This will cause the master to spin its wheels until two bytes come back.

Then, print the response data in the if(Wire.available() >= 2) block, so nothing is printed unless it was just read.

rogerlette:
Thanks for your advices,

So I modify the code as you said :
...

for salve :

...

/////////////////////////////////////////////////////////////////////////////////////////////////
void requestEvent()
{
// respond with message of 2 bytes
 Wire.send(hi);
 Wire.send(lo);
// Wire.send("hello"); //test with char
}

See my page here:

In the middle of that is code for master/slave stuff. Note the comment:

Warning - because of the way the Wire library is written, the requestEvent handler can only (successfully) do a single send. The reason is that each attempt to send a reply resets the internal buffer back to the start. Thus in your requestEvent, if you need to send multiple bytes, you should assemble them into a temporary buffer, and then send that buffer using a single Wire.send.

You are doing two Wire.send () - thus it won't work.

Hi PaulS and Nick Gammon,

Thanks you very very much both of you,

It was the multiple wire.send ... it's now working well ! So many tips to now with i2C communication, thanks for your help.

So here is the final code :

For master :

 #include <Wire.h>

///////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(115200);  // start serial for output
}

///////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
  Wire.requestFrom(2, 2);    // request 2 bytes from slave device #2
 
  Serial.println("potentiometer value"); 
 
  byte loByte;
  byte hiByte;
  
if(Wire.available() >= 2)    // slave may send less than requested
  { 

    hiByte = Wire.receive();
    loByte = Wire.receive();
  }
  Serial.print("hiByte = ");
  Serial.println(hiByte,BIN);
  Serial.print("loByte = "); 
  Serial.println(loByte,BIN);
  int val = (hiByte << 8) + loByte;
  Serial.print(val);
    
  Serial.println("");
  delay(500);
}

For slave :

#include <Wire.h>
#include <stdlib.h>
#define potPin 0  
int readVal;
byte hi;
byte lo;
///////////////////////////////////////////////////////////////////////////////////////////////

void setup()
{
// Communication I2C
  Wire.begin(2);                
  Wire.onRequest(requestEvent); // register event
  
  Serial.begin(9600);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{

readVal = analogRead(potPin);
hi = highByte(readVal);
lo = lowByte(readVal);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void requestEvent()
{

byte buf [2];

  buf [0] = hi;
  buf [1] = lo;
 
  Wire.send(buf, sizeof buf);  // send 2-byte response

}

Thanks again really It's makes me happy today !