Hi There,
I have 2 Ardunio's set up in a master and slave configuration, connected via I2C (Analogue pins 4 and 5) using the example code from the Wire library. I am trying to send the reading from an analogue temperature sensor connected to the slave Arduino and get the value to be printed on the serial interface of the master Arduino. When I try to compile my edited slave code it comes up with the error "call of overloaded 'write(float&)' is ambiguous".
Can anyone help me out to get it to send the temperature sensor values? Thank you.
Slave Code
#include <Wire.h>
int sensor1 = A1;
void setup() {
Wire.begin(8); // join i2c bus with address #8
Wire.onRequest(requestEvent); // register event
}
float value1;
void loop() {
delay(100);
}
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
// Wire.write("hello "); // respond with message of 6 bytes
// as expected by master
value1 = analogRead(sensor1) * 5 / 1024.0;
value1 = value1 - 0.5;
value1 = value1 / 0.01;
Wire.write(value1);
}
Master Code
#include <Wire.h>
void setup() {
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output
}
void loop() {
Wire.requestFrom(8, 6); // request 6 bytes from slave device #8
while (Wire.available()) { // slave may send less than requested
char c = Wire.read(); // receive a byte as character
Serial.print(c); // print the character
}
delay(500);
}
You're trying to write a float.
'Wire.write()' comes in two flavours:-
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *, size_t);
You could possibly try this:-
Wire.write((uint8_t*)&value1, 4); // Untested.
Then put it together at the other end.
Or you could send the analogRead() value in two bytes, (in a similar manner), then put it back together and do the math to create the float at the receiving end.
Or you could try Nick Gammon's "I2C_Anything" library.
It might be simpler for the moment to just send the analogRead() to avoid over complicating things. I have tried implementing this like this.#
Wire.write(analogRead(sensor1))
Which is probably not correct because when I run the master code it comes up with this
ÿÿÿÿ¿ÿÿÿÿÿ¿ÿÿÿÿÿ¿ÿÿÿÿÿ¿ÿÿÿÿÿ¿ÿÿÿÿÿ¿ÿÿÿÿÿ
What am I doing wrong?
Stevie-G:
It might be simpler for the moment to just send the analogRead() to avoid over complicating things. I have tried implementing this like this.#
Wire.write(analogRead(sensor1))
Which is probably not correct because when I run the master code it comes up with this
ÿÿÿÿ¿ÿÿÿÿÿ¿ÿÿÿÿÿ¿ÿÿÿÿÿ¿ÿÿÿÿÿ¿ÿÿÿÿÿ¿ÿÿÿÿÿ
What am I doing wrong?
'analogRead()' returns a value larger than a 'uint8_t', probably 'int' or 'unsigned int'. And declare it as such, not as a 'float', if you only want to send the analogue reading.
You still need to break it up into two individual bytes and send them one at a time using this version of 'write()':-
virtual size_t write(const uint8_t *, size_t);
The first parameter is a pointer to the data, (must be a 'uint8_t*'), and the second is the number of bytes.
You can probably store the result of the 'analogRead()' in an 'int', then cast it's address to a 'uint8_t' pointer, and use 2 as the second parameter to indicate that two bytes are being sent.
Then, the master would need to request 2 bytes. (Currently, your master requests 6 bytes.)
And the master will be receiving 'byte' variables, which will need re-assembling into an 'int'.
It's late here, almost 11.30pm, and I'm too tired to help further right now. You need to do a little more reading, then make the code at both ends compatible. ie If the slave sends two bytes, the master needs to request two bytes.
Perhaps you should check this out:- Send and receive any data type
It's well worth taking the time to carefully read the whole page, then you could download and install Nick's "I2C_Anything" library to make things easy for yourself.
Thanks for your help. I will look into that library and its use.
I had a bit of a play this morning, and wrote a couple of small examples, (not using "I2C_Anything"), one to send/receive an 'int' variable via I2C, and the other to send/receive a 'float'.
I used a Mega2560 for the master, and an UNO for the slave, but two UNOs, two Megas or whatever will work the same. (As long as both have hardware I2C support.)
I'll post the 'int' example below in this reply, then the 'float' example in the next reply.
The 'slave' code to send an 'int':-
// Libraries:-
#include <Wire.h>
// Defines:-
#define SLAVE_ADDRESS 0x08
// Constants:-
const byte ledPin = 13;
// Global variables:-
int iTestVal;
void setup()
{
pinMode(ledPin, OUTPUT);
Wire.begin(SLAVE_ADDRESS); // Join I2C bus with address.
Wire.onRequest(I2CrequestHandler); // Register request handler.
}
void loop()
{
static unsigned long prevMillis = 0;
static bool ledPinState = 0;
unsigned long currentMillis = millis(); // Get the current time.
if (currentMillis - prevMillis >= 1000) // Toggle the LED once per second
{
prevMillis = currentMillis; // Record the current time.
ledPinState ^= 1; // Toggle the LED.
digitalWrite(ledPin, ledPinState); // " " "
}
iTestVal = 1067; // 'int' value to be sent via I2C.
}
void I2CrequestHandler()
{
Wire.write((byte*)&iTestVal, 2); // Transmit the 'int', one byte at a time.
}
The 'master' code to receive it:-
// Libraries:-
#include <Wire.h>
// Defines:-
#define SLAVE_ADDRESS 0x08
// Constants:-
const byte ledPin = 13;
void setup()
{
pinMode(ledPin, OUTPUT);
Serial.begin(115200);
Wire.begin(); // Join I2C bus.
}
void loop()
{
static unsigned long prevMillis = 0;
static bool ledPinState = 0;
unsigned long currentMillis = millis(); // Get the current time.
if (currentMillis - prevMillis >= 1000) // I2C updates once per second
{
prevMillis = currentMillis; // Record the current time.
ledPinState ^= 1; // Toggle the LED
digitalWrite(ledPin, ledPinState); // " " "
Wire.requestFrom(SLAVE_ADDRESS, 2); // Request 2 bytes from the slave device.
}
// Receive the 'int' from I2C and print it:-
if (Wire.available() >= 2) // Make sure there are two bytes.
{
int iRXVal;
for (int i = 0; i < 2; i++) // Receive and rebuild the 'int'.
iRXVal += Wire.read() << (i * 8); // " " " " "
Serial.println(iRXVal); // Print the result.
}
}
This is the 'float' example. It's a bit more complicated at the receiving end, but not much. It uses a 'union' to convert the raw data back into a 'float'.
The 'slave' code to send a 'float':-
// Libraries:-
#include <Wire.h>
// Defines:-
#define SLAVE_ADDRESS 0x08
// Constants:-
const byte ledPin = 13;
// Global variables:-
float dTestVal;
void setup()
{
pinMode(ledPin, OUTPUT);
Wire.begin(SLAVE_ADDRESS); // Join I2C bus with address.
Wire.onRequest(I2CrequestHandler); // Register request handler.
}
void loop()
{
static unsigned long prevMillis = 0;
static bool ledPinState = 0;
unsigned long currentMillis = millis(); // Get the current time.
if (currentMillis - prevMillis >= 1000) // Toggle the LED once per second
{
prevMillis = currentMillis; // Record the current time.
ledPinState ^= 1; // Toggle the LED.
digitalWrite(ledPin, ledPinState); // " " "
}
dTestVal = 123.456; // 'float' value to be sent via I2C.
}
void I2CrequestHandler()
{
Wire.write((byte*)&dTestVal, 4); // Transmit the 'float', one byte at a time.
}
The 'master' code to receive it:-
// Libraries:-
#include <Wire.h>
// Defines:-
#define SLAVE_ADDRESS 0x08
// Constants:-
const byte ledPin = 13;
// Global variables:-
union // Union used to convert raw data to 'float'.
{
float dRXVal;
byte bRawData[4];
}floatData;
void setup()
{
pinMode(ledPin, OUTPUT);
Serial.begin(115200);
Wire.begin(); // Join I2C bus.
}
void loop()
{
static unsigned long prevMillis = 0;
static bool ledPinState = 0;
unsigned long currentMillis = millis(); // Get the current time.
if (currentMillis - prevMillis >= 1000) // I2C updates once per second
{
prevMillis = currentMillis; // Record the current time.
ledPinState ^= 1; // Toggle the LED
digitalWrite(ledPin, ledPinState); // " " "
Wire.requestFrom(SLAVE_ADDRESS, 4); // Request 4 bytes from the slave device.
}
// Receive the 'float' from I2C and print it:-
if (Wire.available() >= 4) // Make sure there are four bytes.
{
for (int i = 0; i < 4; i++) // Receive the raw 'float' data.
floatData.bRawData[i] = Wire.read(); // " " " " "
Serial.println(floatData.dRXVal,3); // Print the result.
}
}
OldSteve:
I had a bit of a play this morning, and wrote a couple of small examples, (not using "I2C_Anything"), one to send/receive an 'int' variable via I2C, and the other to send/receive a 'float'.
I used a Mega2560 for the master, and an UNO for the slave, but two UNOs, two Megas or whatever will work the same. (As long as both have hardware I2C support.)
I'll post the 'int' example below in this reply, then the 'float' example in the next reply.
The 'slave' code to send an 'int':
Thank you very much! I will have a look when I get into work in a few hours!!!
Stevie-G:
Thank you very much! I will have a look when I get into work in a few hours!!!
No problem. And both are fully tested and work fine, (not just blindly typed
).
[/quote]
OldSteve:
No problem. And both are fully tested and work fine, (not just blindly typed
).
Thank you very much! I have tried the int version and that works great thank you! Colleague is now saying it would be good to transmit the data in an array. Sorry to keep moving the goal posts but is this possible easily?
Sorry!
Stevie-G:
Thank you very much! I have tried the int version and that works great thank you! Colleague is now saying it would be good to transmit the data in an array. Sorry to keep moving the goal posts but is this possible easily?
Sorry!
What data? Just the int? Or the float? An array of float values?
You indicated originally that you wanted to read an analogue input, convert that value to a float, then transmit that. My second example will do that, and it is effectively sending a byte array. It splits the float up into 4 bytes and sends them individually.
You'll need to explain the requirement more clearly.
(Does your colleague know what he/she is talking about?)
OldSteve:
(Does your colleague know what he/she is talking about?)
He is a bit more knowledgeable than me and I'm very green to programming. We have a multiplexer which can put 32 sensor inputs into 1 analogue pin of the arduino. My colleague reckons that sending this data as an array will help make recalling individual sensor data easier in the future. We are planning to hopefully connect this to the cloud (Adafruit IO) using a GSM module in the near future. Sorry to be such a pain and I have the int version running at the moment thank you!
You can base it on the example I showed for sending an int.
But you want to send 64 bytes at a time, and the "Wire" library buffer is only 32 bytes in size, so the first thing you'll have to do is modify the library to increase the buffer size.
Then you create and fill an array with the analogue read values.
int tempReadingBuffer[64];
When the master requests the data, it will have to request 64 bytes.
The slave will send it like this:-
Wire.write((uint8_t*)tempReadingBuffer, 64);
Then, you'll have to put the int values back together at the other end using a similar method to the one that I used, by building an int from each consecutive pair of values that are received.
If your colleague is more knowledgeable about programming as you say, perhaps he/she should be writing the code. I'm not going to write it for you - you'll have to do some reading/studying and learn a bit more about things, then give it a shot.
You should have mentioned right from the get-go exactly what you wanted to achieve.
Maybe it's time to take a good look at the "I2C_Anything" library, as I've suggested a couple of times, but even then you'll still need to modify the "Wire" library before you can do what you want.
Thank you for your help. I will try and follow your instructions. Trying to learn by throwing myself in at the deep end so your help has been greatly appreciated!
Just thinking. On the receiving end, you could store the received bytes in a byte array, then cast it to an int array, and the int values should be correct without further conversion.
(You still need to increase the "Wire" buffer size though.)
ie
byte rawData[64];
// Store the incoming bytes, starting at element 0:-
for(int i=0;i<64;i++)
rawData[i]=Wire.read();
Then, cast to int:-int* pValues = (int*)rawData;
And you could access the individual int values like so:-float value0 = pValues[0] * 5 / 1024.0;
Or even use a float array for the individual converted values:-
float temperatureList[32];
for(int i=0;i<32;i++)
temperatureList[i] = pValues[i] * 5 / 1024.0;
Just off the top of my head, but should work.
Thank you. I will give that a try. Just working on increasing the buffer size of the wire library from 32 to 64 bits. Think i've got that part working now...
Stevie-G:
Thank you. I will give that a try. Just working on increasing the buffer size of the wire library from 32 to 64 bits. Think i've got that part working now...
bytes, not bits. 
And yeah, with luck you only need to modify the define for the size.
Write a short test program to test it. Create a 64-byte array, initialise it and transmit it, then see if you get the same values at the other end.
You could easily initialise the array at the slave end with something like:-
for(int i=0;i<64;i++)
byteArray[i]=i*2;
or similar. Better than setting all of the values manually. 
Make sure that you stipulate 64 bytes in the slave's 'write()', and that the master requests 64 bytes.
I'll be interested to hear how you go.
Edit: I just quickly tested it myself. You need to change the buffer size in both "Wire.h" and "twi.h" from 32 to 64, and then it works fine. (Just changing one of them doesn't work.)