Sending floats w/NRF8001 - arduino

Using Blend Micro (basically Arduino Uno + Nordic nRF8001) to gather and send sensor data via BLE. Can't figure out how to send floats.
Here's a snip of code:
if ((lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_TEMPERATURE_MEASUREMENT_TX)) && (lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_RELATIVE_HUMIDITY_TX)) && (lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_CARBON_MONOXIDE_LEVEL_TX)))
{
// Read DHT & MQ7 sensors
h = dht.readHumidity(); // Read humidity from DHT
t = dht.readTemperature(); // Read temperature as Celsius from DHT
f = dht.readTemperature(true); // Read temperature as Fahrenheit from DHT
int VoutAn = analogRead(MQ7PIN); //Read Vout in analog from MQ7
// Compute heat index
hi = dht.computeHeatIndex(f, h);
// Compute CO in PPM
float Vout = (5.0/1023)*VoutAn;
co = 100.468*(pow(((5/Vout)-1),-1.43));
lib_aci_send_data(PIPE_AIR_QUALITY_SENSOR_TEMPERATURE_MEASUREMENT_TX, &f, sizeof(f));
lib_aci_send_data(PIPE_AIR_QUALITY_SENSOR_RELATIVE_HUMIDITY_TX, &h, sizeof(h));
lib_aci_send_data(PIPE_AIR_QUALITY_SENSOR_CARBON_MONOXIDE_LEVEL_TX, &co, sizeof(co));
}
And here's the compile error:
Arduino: 1.0.6 (Mac OS X), Board: "Blend Micro 3.3V/16MHz (overclock)"
BLE-DHT-MQ7-Test.ino: In function 'void loop()':
BLE-DHT-MQ7-Test.ino:287: error: cannot convert 'float*' to 'uint8_t*'
for argument '2' to 'bool lib_aci_send_data(uint8_t, uint8_t*,
uint8_t)'
BLE-DHT-MQ7-Test.ino:288: error: cannot convert 'float*' to 'uint8_t*'
for argument '2' to 'bool lib_aci_send_data(uint8_t, uint8_t*,
uint8_t)'
BLE-DHT-MQ7-Test.ino:289: error: cannot convert 'float*' to 'uint8_t*'
for argument '2' to 'bool lib_aci_send_data(uint8_t, uint8_t*,
uint8_t)'
My understanding of the aci_send_data function is that argument 2 is a uint8_t variable that points to the data in memory to be sent, and that this data can be in any form. But it only seems to work if the data is in a uint8_t variable - e.g. this compiles if I convert my variables to uint8_t - but I need floats...
So my questions:
Am I understanding the aci_send_data function correctly?
If so, what am I doing wrong?
If not, how can I send floats?
Thanks!

I am not sure about the parameters needed by lib_aci_send_data function however, according to this code it seems that the first parameter accepts the TX pin, the second one is the reference of the first byte (in a byte array) to be interpreted as a number, and the third is the length or number of bytes to consider.
The data type float is a 4-byte variable. If we have an array of bytes representing the float number, we can pass the reference of the first byte in the second parameter of the function, and pass 4 as the length (or number of bytes to consider) in the third parameter.
To convert float to byte array, see this thread. In your case, it would look like this:
typedef union _data {
float f;
char s[4];//since f takes 4 bytes
} myData;
myData q;
void setup(){
Serial.begin(9600);
}
void loop(){
q.f = 1.234;
//you can access the char array by using q.s
//q.s[0] q.s[1] ...
//take note that q.s is a reference to the first element
//so we can pass that immediately to the 2nd parameter
}
Now you can use the function in this way: lib_aci_send_data(PIPE_AIR_QUALITY_SENSOR_RELATIVE_HUMIDITY_TX, q.s, 4);
I cannot test this as of now since I'm on a different machine, please let me know if this was successful, I'm willing to add edits if necessary.

Thanks, it works! Seems a little cumbersome, but here's the code for getting all 3 variables I need to send as floats:
if ((lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_TEMPERATURE_MEASUREMENT_TX)) && (lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_RELATIVE_HUMIDITY_TX)) && (lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_CARBON_MONOXIDE_LEVEL_TX)))
{
typedef union _dataF
{
float f;
uint8_t fInt[4];
} myDataF;
myDataF F;
typedef union _dataH
{
float h;
uint8_t hInt[4];
} myDataH;
myDataH H;
typedef union _dataCO
{
float co;
uint8_t coInt[4];
} myDataCO;
myDataCO CO;
// Read DHT & MQ7 sensors
h = dht.readHumidity(); // Read humidity from DHT
t = dht.readTemperature(); // Read temperature as Celsius from DHT
f = dht.readTemperature(true); // Read temperature as Fahrenheit from DHT
int VoutAn = analogRead(MQ7PIN); //Read Vout in analog from MQ7
// Compute heat index
hi = dht.computeHeatIndex(f, h);
// Compute CO in PPM
float Vout = (5.0/1023)*VoutAn;
co = 100.468*(pow(((5/Vout)-1),-1.43));
F.f = f;
H.h = h;
CO.co = co;
lib_aci_send_data(PIPE_AIR_QUALITY_SENSOR_TEMPERATURE_MEASUREMENT_TX, F.fInt, 4);
lib_aci_send_data(PIPE_AIR_QUALITY_SENSOR_RELATIVE_HUMIDITY_TX, H.hInt, 4);
lib_aci_send_data(PIPE_AIR_QUALITY_SENSOR_CARBON_MONOXIDE_LEVEL_TX, CO.coInt, 4);
}
I put the sensor data into wider scope floats as I draw on them elsewhere in the program.

Related

ESP32 ADXL345 doesn't get the negative values

Hi I can run this code without any problem on Arduino UNO. My output is like : 0.05 0.10 1.01
But when I run this code on ESP32, my output is : 255.89 255.81 0.99 and I don't see any minus value. What is the problem?
ESP32's X,Y,Z raw values like : 6500 0 , 65000 , 1 . ESP32 can't get the negative values. I have to do something with wire.h and I need to change uint8_t or uint16_t something but I really don't understand how should I solve this problem.
#include <Wire.h> // Wire library - used for I2C communication
int ADXL345 = 0x53; // The ADXL345 sensor I2C address
float X_out, Y_out, Z_out; // Outputs
float X, Y, Z ;
void setup() {
Serial.begin(115200); // Initiate serial communication for printing the results on the Serial monitor
Wire.begin(); // Initiate the Wire library
// Set ADXL345 in measuring mode
Wire.beginTransmission(ADXL345); // Start communicating with the device
Wire.write(0x31);
Wire.write(0x0B);
Wire.endTransmission();
Wire.beginTransmission(ADXL345);
Wire.write(0x20); // Z-axis offset register
Wire.write(-7);
Wire.endTransmission();
Wire.beginTransmission(ADXL345);
Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
// Enable measurement
Wire.write(0x08); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable
Wire.endTransmission();
Wire.beginTransmission(ADXL345);
Wire.write(0x2C);
// Enable measurement
Wire.write(0x09); //For low power 000x x pin set to 1 /1001 determine Hz
Wire.endTransmission();
delay(10);
}
void loop() {
unsigned long Time = millis();
// === Read acceleromter data === //
Wire.beginTransmission(ADXL345);
Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
X_out = ( Wire.read() | Wire.read() << 8); // X-axis value
X = X_out /256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
Y_out = ( Wire.read() | Wire.read() << 8); // Y-axis value
Y = Y_out /256;
Z_out = ( Wire.read() | Wire.read() << 8); // Z-axis value
Z = Z_out /256;
delay(20);
Serial.print(X);
Serial.print(" ");
Serial.print(Y);
Serial.print(" ");
Serial.print(Z);
Serial.println(" ");
}
On the Arduino Uno there are 16-bit integers. The ADXL345 seems to receive this data format. Therefore everything plays out nicely. You are using 32-bit integers on the ESP32. Therefore you have to choose the right data type explicitly. Otherwise the negative numbers appear in the positive region.
The execution order of the Wire.read()-calls is not defined in your code. Therefore the compiler may join the bytes 0xAA and 0xBB to 0xAABB or 0xBBAA. You should add a sequence point to make sure that the code does what's intended.
I have not tested this code. That should be the correct order, in case I got the datasheet description right:
int16_t readInt16()
{
int lsb = Wire.read();
int msb = Wire.read();
return int16_t(lsb | msb << 8);
}
X_out = readInt16() / 256.f;
Y_out = readInt16() / 256.f;
Z_out = readInt16() / 256.f;

Reading multiple bytes in Arduino sent using Pyserial

I'm constantly sending structs of int64 via Pyserial with:
with serial.Serial(port='COM4', baudrate=115200, timeout=.1) as arduino:
value = write_read(struct.pack(">q", int_array[1][i])) #this sends signed int.64 in bytes
print(value)
the struct.pack has this shape, for example:
b'\xff\xff\xff\xff\xff\xff\xff\xef'
and the function write_read consists of:
def write_read(x):
arduino.write((x))
data = arduino.readline()
#the idea is to receive an ACK from the Arduino after 8 bytes (the full
#number)
return data
The code I'm trying to develop in arduino is the following:
void loop() {
// send data only when you receive data:
if (Serial.available() \> 0) {
// read the incoming byte:
incomingByte = Serial.read();
//read 8 bytes and create the result
r= function_to_read_8_last_bytes // or similar
// say what you got:
Serial.print("I received: ");
Serial.printlesultn(r, DEC);
Serial.write("ACK");
}
}
I'm very curious how I could do a robust "read 8 bytes" function.
Should I add some especial character in the Python part to indentify when it ends one value?
Thanks! I'll appreciate any help :)
Given the discussion in the comments, it's hard to receive a stream of bytes and be sure that the receiver is completely synchronized. However let's make some assumptions to ease the problem:
The serial buffer is empty when you connect your laptop to Arduino. This ensures you won't receive spurious data with no meaning. I had this problem happens a lot when the serial connection was ended abruptly by any cause.
You are not constantly sending bytes, Arduino has time to process them until the start of the new sequence.
You only send this data, so there is no need to create a higher level protocol on top of it. Bare in mind that the serial communication is almost just an hardware stack, you receive bytes with no headers.
For assumption 1 you can write a simple piece of code to consume all the spurious bytes in the serial buffer as soon as your main starts from Arudino, so this will be done everytime you connect the serial (as this is also where the power supply comes from). Something like this:
void serialFlush(){
while(Serial.available() > 0) {
char t = Serial.read();
}
}
You can send a "READY" signal back to the Python interface, so that the program knows you are ready to receive data.
Going on with the solution you can implement an easy CRC in python, an additional byte which contains a XOR of all the previous bytes, and you check that in Arduino upon reception complete.
def xor_reduce_long_int(li):
res = 0;
for i in range(8):
mask = (0xFF)<<(i*8)
print(hex(mask))
masked = (li&mask)>>(i*8)
res ^= masked
return res
with serial.Serial(port='COM4', baudrate=115200, timeout=.1) as arduino:
crc=xor_reduce_long_int(int_array[1][i])
value = write_read(struct.pack(">qc", int_array[1][i],crc)) #this sends signed int.64 in bytes
print(value)
And with Arduino I would read 8 bytes when they are available and put them into an unsigned char buffer. I would then define a union that alias such buffer to interpret it as long long int.
typedef struct long_int_CRC
{
union
{
unsigned char bytes[8];
long int data;
};
unsigned char CRC;
}data_T;
// .. Later in main
data_T = received_data;
int received_bytes=0
unsigned char my_CRC = 0;
unsigned char rec_byte= 0;
while( received_bytes < 8 )
{
if(Serial.available() )
{
// Get the byte
rec_byte = Serial.read()
// Store the byte and calc CRC
received_data.bytes[received_bytes] = rec_byte;
my_CRC ^= rec_byte;
// Increment counter for next byte
received_bytes++;
}
}
// Reception complete, check CRC
unsigned char rec_CRC;
if(Serial.available() )
{
rec_CRC = Serial.read()
}
if( my_CRC != rec_CRC )
{
// Something was wrong!
}
// Now access your data as a long int
Serial.print("I received: ");
Serial.printlesultn(received_data.data, DEC);
Serial.write("ACK");

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.

Parsing char array to integer, atoi() returns squares

I'm using an android app to send values to control servos.
Code:
char inputData[4];
char buffer[3];
void loop()
{
if(Serial.available() > 3) {
for (int i = 0; i < 4; i++){
inputData[i] = Serial.read();
}
char buffer[4];
buffer[0] = inputData[1];
buffer[1] = inputData[2];
buffer[2] = inputData[3];
buffer[3] = '\0';
int angle = atoi(buffer);
Serial.write(angle);
}
}
Issue: I'm getting the values + A-F letters to address each servo - A10, A180, B30 etc. Now the trouble is turning this to an actual integer. As you can see I've declared a character array to store the integers in and as suggested in a post on the arduino forum, I added a \0 at the end of the array. Currently, the Atoi returns random characters, mostly squares and some random numbers. I've tried even assigning them to a string and then .toInt() but same issue there, mostly squares.
Any ideas?
Thanks!
Use print or println to see the number as text. write sends it as byte and Serial Monitor shows a symbol with that ASCII code.

arduino global variable using I2C and LCD

I'm using Arduino Uno in my project : One is a Master and the other is a Slave. I send Data from the Master to the Slave using I2C. I need to send float, but because I2C can only send char, so I'm obligated to transform float into String, then send character by character, and assemble them in the Slave.
The problem I had is, I'm declaring the variable (in the Slave) that contain the Float receiving and assumbling from the Master like a global variable, and I need to use it in my code in the slave, but my problem is that it is always printed as 0, and it dosn't give the right value of it.
The code I'm using is :
#include <LCD16x2.h>
#include <Wire.h>
LCD16x2 lcd;
int buttons;
int sensorPin = A0; // select the input pin for the potentiometer
int sensorValue = 0; // variable to store the value coming from the sensor
float numOut=0; // The Global Variable
int comp=1 ;
String wordd = "";
void setup()
{
Wire.begin(8); // join i2c bus with address #8
Wire.onReceive(receiveEvent); // register event
Serial.begin(9600); // start serial for output
lcd.lcdGoToXY(1,1);
lcd.lcdClear();
lcd.lcdWrite("EG ");
lcd.lcdGoToXY(7,1);
lcd.lcdWrite(numOut,3);
}
void loop()
{
}
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
wordd = "";
int x = Wire.read();
for (int i=0; i<=x; i++)
{
char c = Wire.read();
wordd += c;
}
numOut = wordd.toFloat();
Serial.println(numOut,3); // print the integer
}
I need to know how to have the result of the global variable "numOut" to use it in my code.
Thank you in advance.!!
Did you actually check if the data received is correct ? I don't know about the Arduino's strings but if your toFloat() fails it might return 0.
Check through your serial port if your wordd variable is correct, and as already mentioned global variables should be declared as volatile.
EDIT:
volatile is necessary here to ensure your data is synchronized between your different function calls (otherwise your program might keep the value inside a register although the "real" value has been updated through an interrupt).
However as someone said volatile is NOT necessary to every global variable, but you won't be using semaphores/mutex... on an Arduino so stick witch volatile for the problem you posted.
EDIT:
https://www.arduino.cc/en/Reference/StringToFloat
"If no valid conversion could be performed because the string doesn't start with a digit, a zero is returned."

Resources