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.
Related
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;
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");
Following this tutorial:
Arduino LED Dimmer Example
I added the Serial.println(brightness); at the bottom to see what gets written to the brightness variable and only get back 48 no matter the value sent by serial to the arduino.
As you can see the value of brightness goes from 50 to 48 where it settles. The sent data is in pink and the black data is the response from the arduino.
What else could I try.
Thanks
Using the Hercules utility to establish a serial link to the arduino.
Hercules Utility Showing communication with Arduino
The oscilloscope sees this:
PWM signal from oscilloscope
const int ledPin = 9; // the pin that the LED is attached to
void setup() {
// initialize the serial communication:
Serial.begin(9600);
// initialize the ledPin as an output:
pinMode(ledPin, OUTPUT);
}
void loop() {
byte brightness;
// check if data has been sent from the computer:
if (Serial.available()) {
// read the most recent byte (which will be from 0 to 255):
brightness = Serial.read();
// set the brightness of the LED:
analogWrite(ledPin, brightness);
Serial.println(brightness);
}
}
your problem is obvious in the image you provided , for example in the image it shows
when send 100 , the arduino responded with 49 , 48 , 48 which is the ascii representation of the 100 , refer to ascii table where
'1' is equivalent to 49
'0' is equivalent to 48
which means that you send to the arduino the number 100 not as a number but as a string where your terminal sends 1 first in its ascii representation which is 49 then it sends 0 in its ascii representation which is 48 then it sends another 0 in its ascii representation which is 48.
to solve this problem:
in your pc side , try to change that in your terminal settings
or
in your arduino code , instead of brightness = Serial.read(); write brightness = Serial.parseInt(); , refer to Serial.parseInt() docs where it says that :
Looks for the next valid integer in the incoming serial. The function terminates if it times out
meaning that it will convert "100" (string) into 100 (number) in your arduino side .
also change byte brightness; into int brightness; as Serial.parseInt() returns the next valid int
Thank you abdo salm, your suggestion educated me and resolved my problem at the same time. Answer updated for anyone who stumbles across the same rookie issue (see snippet below).
So in conclusion, the example code expected a number in the range of
00000000 to 11111111 (0 to 255 as a single binary byte)
I was instead sending multiple bytes that represented One, Zero and Zero on the ASCII table
11000100 (49)
11000000 (48)
11000000 (48)
erroneously thinking I was sending 100 which is 110010000 in binary.
I altered the code with brightness = Serial.parseInt(); as abdo salm suggested.
Here is the Hercules (or similar) friendly version of the Arduino Dimmer Example.
const int ledPin = 9; // the pin that the LED is attached to
int brightness;
void setup() {
// initialize the serial communication:
Serial.begin(9600);
// initialize the ledPin as an output:
pinMode(ledPin, OUTPUT);
}
void loop() {
// check if data has been sent from the computer:
if (Serial.available()) {
// read the most recent byte (which will be from 0 to 255):
brightness = Serial.parseInt();
// set the brightness of the LED:
analogWrite(ledPin, brightness);
Serial.println(brightness);
}
}
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.
I need to use a sparkfun breakout board ADXL345 to detect when my motor system has stopped vibrating. I am also using a Sparkfun RedBoard (Arduino uno).
Things I am doing to configure for this behavior:
enable INACTIVITY event
route INACTIVITY events to INT 1 (pin 2 on the RedBoard)
raise INACTIVITY interrupt without delay
set low threshold for INACTIVITY (rule out too high of a setting)
INACTIVITY considers all axes
clear interrupt data register
Having done all these things I do not receive interrupts after going from shaking the devise to setting it down.
//Add the SPI library so we can communicate with the ADXL345 sensor
#include <SPI.h>
//Assign the Chip Select signal to pin 10.
int CS=10;
//This is a list of some of the registers available on the ADXL345.
//To learn more about these and the rest of the registers on the ADXL345, read the datasheet!
char POWER_CTL = 0x2D; //Power Control Register
char DATA_FORMAT = 0x31;
char DATAX0 = 0x32; //X-Axis Data 0
char DATAX1 = 0x33; //X-Axis Data 1
char DATAY0 = 0x34; //Y-Axis Data 0
char DATAY1 = 0x35; //Y-Axis Data 1
char DATAZ0 = 0x36; //Z-Axis Data 0
char DATAZ1 = 0x37; //Z-Axis Data 1
char THRESH_ACT = 0x24; // Activity threshold
char THRESH_INACT = 0x38; // Inactivity threshold to 3g
char TIME_INACT = 0x26; // time before raising interrupt
char INT_ENABLE = 0x2E; // Enabling the interrupt lines
char INT_MAP = 0x2F;
char ACT_INACT_CTL = 0x27; // mask byte for controlling
char INT_SOURCE = 0x30;
//This buffer will hold values read from the ADXL345 registers.
char values[10];
//These variables will be used to hold the x,y and z axis accelerometer values.
int x,y,z;
void setup(){
//Initiate an SPI communication instance.
SPI.begin();
//Configure the SPI connection for the ADXL345.
SPI.setDataMode(SPI_MODE3);
//Create a serial connection to display the data on the terminal.
Serial.begin(9600);
//Set up the Chip Select pin to be an output from the Arduino.
pinMode(CS, OUTPUT);
//Before communication starts, the Chip Select pin needs to be set high.
digitalWrite(CS, HIGH);
// Create an interrupt that will trigger when inactivity is detected
attachInterrupt(0, interruptHandler, RISING);
//Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
writeRegister(DATA_FORMAT, 0x01);
//Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
writeRegister(POWER_CTL, 0x08); //Measurement mode
// Send the inactivity && activity to PIN 1
// 0xF7 && 0xEF
writeRegister(INT_MAP,0xF7 && 0xEF);
// Set the inactivity threshold to 3g (0x38)
// writeRegister(THRESH_INACT,0x38);
writeRegister(THRESH_INACT,1);
// Raise the inact interrupt immediately after going below threshold
writeRegister(TIME_INACT,0);
// Map INACT event (only) to PIN 1
writeRegister(ACT_INACT_CTL, 0x0F);
// Enab le inactivity to generate interrupts
writeRegister(INT_ENABLE, 0x08);
readRegister(INT_SOURCE, 1, values); // Clear the INT_SOURCE register
Serial.println("Waiting for interrupt!");
}
void interruptHandler(){
// readRegister(INT_SOURCE, 1, values); // Clear the INT_SOURCE register
Serial.println("something raise an interrupt!");
}
void loop(){
//Reading 6 bytes of data starting at register DATAX0 will retrieve the x,y and z acceleration values from the ADXL345.
//The results of the read operation will get stored to the values[] buffer.
readRegister(DATAX0, 6, values);
//The ADXL345 gives 10-bit acceleration values, but they are stored as bytes (8-bits). To get the full value, two bytes must be combined for each axis.
//The X value is stored in values[0] and values[1].
x = ((int)values[1]<<8)|(int)values[0];
//The Y value is stored in values[2] and values[3].
y = ((int)values[3]<<8)|(int)values[2];
//The Z value is stored in values[4] and values[5].
z = ((int)values[5]<<8)|(int)values[4];
//Print the results to the terminal.
Serial.print(x, DEC);
Serial.print(',');
Serial.print(y, DEC);
Serial.print(',');
Serial.println(z, DEC);
delay(500);
}
//This function will write a value to a register on the ADXL345.
//Parameters:
// char registerAddress - The register to write a value to
// char value - The value to be written to the specified register.
void writeRegister(char registerAddress, char value){
//Set Chip Select pin low to signal the beginning of an SPI packet.
digitalWrite(CS, LOW);
//Transfer the register address over SPI.
SPI.transfer(registerAddress);
//Transfer the desired register value over SPI.
SPI.transfer(value);
//Set the Chip Select pin high to signal the end of an SPI packet.
digitalWrite(CS, HIGH);
}
//This function will read a certain number of registers starting from a specified address and store their values in a buffer.
//Parameters:
// char registerAddress - The register addresse to start the read sequence from.
// int numBytes - The number of registers that should be read.
// char * values - A pointer to a buffer where the results of the operation should be stored.
void readRegister(char registerAddress, int numBytes, char * values){
//Since we're performing a read operation, the most significant bit of the register address should be set.
char address = 0x80 | registerAddress;
//If we're doing a multi-byte read, bit 6 needs to be set as well.
if(numBytes > 1)address = address | 0x40;
//Set the Chip select pin low to start an SPI packet.
digitalWrite(CS, LOW);
//Transfer the starting register address that needs to be read.
SPI.transfer(address);
//Continue to read registers until we've read the number specified, storing the results to the input buffer.
for(int i=0; i<numBytes; i++){
values[i] = SPI.transfer(0x00);
}
//Set the Chips Select pin high to end the SPI packet.
digitalWrite(CS, HIGH);
}
Here is a tutorial, arduino library and example sketch. If you haven't run through something like this, might be worth a try starting with someone else's code that is working (maybe you've already done that).
In the example sketch from above, they are enabling interrupts in the code, they just don't seem to tie them into the Arduino's external interrupt system. Once you verify that the example code is working, you can call attachInterrupt() and abandon the polling approach (as you are doing in your example).