Hello @horace and @pylon,
Thank you for your interest in this issue. The Forum Administrator just bumped me up to Basic membership, so I hope that I can include several pictures per posting, as compared to just one previously!
The picture below shows the set up used: Adafruit Feather ESP32 V2 + 3 VL53L1X from Pololu. I also used the ROBOTIS software tool named TASK V2 as my PC interface for sending UDLR123456 Button signals to the Arduino Sketch on the ESP32 which in turn can send text strings back to this software tool.
Each button pushed by the user is represented by a 16-bit number which is then split up into a Low-Byte (Data_L) and a Hi-Byte (Data_H) which are then stuffed into a 6-byte packet (as per RC-100 protocol from ROBOTIS). Eventually motorized wheels will be attached to the above setup thus requiring remote control features for a more lengthy code, but a shorter basic sketch is listed below for the current purpose:
// Setting up SerialBT
#include <BluetoothSerial.h>
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#endif
BluetoothSerial SerialBT;
// Setting the 3 ToF sensors
#include <Wire.h>
#include <VL53L1X.h> // Pololu Library
const uint8_t sensorCount = 3;
int sensorRead = 0; // via Keyboard Input with Serial.read()
const uint8_t xshutPins[sensorCount] = { 13, 14, 15 }; // Digital Ports 13/14/15 used on Feather ESP32 V2
VL53L1X sensors[sensorCount]; // instantiate Sensor Objects
int L_ToF = 0;
int C_ToF = 0;
int R_ToF = 0;
////////// define RC-100 button key value ////////////////
#define RC100_BTN_U (1)
#define RC100_BTN_D (2)
#define RC100_BTN_L (4)
#define RC100_BTN_R (8)
#define RC100_BTN_1 (16)
#define RC100_BTN_2 (32)
#define RC100_BTN_3 (64)
#define RC100_BTN_4 (128)
#define RC100_BTN_5 (256)
#define RC100_BTN_6 (512)
uint8_t state2 = 0; // Init for RC-100 packet parameters
uint8_t rc_index2 = 0;
bool received2 = false;
uint16_t Data2 = 0;
uint16_t DataReceived2 = 0;
// Function Prototypes for RC-100 packet
bool rc100_Update2(uint8_t data2);
bool rc100_Available2(void);
uint16_t rc100_readData2(void);
/******END OF GLOBAL SETTINGS ***************************/
void setup()
{
Serial.begin(115200);
while (!Serial) {} // waiting for user to open Serial Monitor
delay(1000);
SerialBT.begin("ESP32_Blue"); //Bluetooth device name & set to 115.2 Kbps
delay(1000);
Serial.println("ESP32's BT device started");
// ToF setup
Serial.println("Triple VL53L1Xs ToF Sensor Example");
delay(1000);
Wire.begin(); // have to use TwoWire for OpenRB-150
Wire.setClock(400000); // use 400 kHz I2C
// Disable/reset all sensors by driving their XSHUT pins low.
for (uint8_t i = 0; i < sensorCount; i++)
{
pinMode(xshutPins[i], OUTPUT);
digitalWrite(xshutPins[i], LOW);
}
// Enable, initialize, and start each sensor, one by one.
for (uint8_t i = 0; i < sensorCount; i++)
{
pinMode(xshutPins[i], INPUT);
delay(10); // wait for sensor to start up
sensors[i].setTimeout(500);
if (!sensors[i].init())
{
Serial.print("Failed to detect and initialize sensor ");
Serial.println(i);
while (1);
}
sensors[i].setAddress(0x2A + i);
sensors[i].startContinuous(15); // default=50 ms, 15 is about lowest value used without TIMEOUT error
} // end of big FOR Loop
} // end of setup()
void loop()
{
// ToF code
// read_sensors_nb();
read_sensors_b();
// Processing RC-100 Buttons
DataReceived2 = 0;
if (rc100_Available2())
{
DataReceived2 = rc100_readData2();
Serial.println("DataReceived2 = " + String(DataReceived2));
// SerialBT.println("DataReceived2 = " + String(DataReceived2));
if (DataReceived2 == 0) // All Buttons Released
{
SerialBT.println("All Buttons Released\r\n\n");
}
else
{
if (DataReceived2 == RC100_BTN_U) // Up Button Detected
{
SerialBT.println("Forward");
}
else if (DataReceived2 == RC100_BTN_D) // Down Button Detected
{
SerialBT.println("Backward");
}
else if (DataReceived2 == RC100_BTN_L) // Down Button Detected
{
SerialBT.println("Left");
}
else if (DataReceived2 == RC100_BTN_R) // Down Button Detected
{
SerialBT.println("Right");
}
} // end of big else
delay(50); // a small delay for SerialBT packets
} // end of receiving RC-100 data
} // end of loop()
void read_sensors_nb() {
// ToF Sensors Read in Non-Blocking Mode to keep SerialBT AVAILABLE
if (sensors[0].dataReady())
{
L_ToF = sensors[0].read(false); // non-blocking mode
}
if (sensors[1].dataReady())
{
C_ToF = sensors[1].read(false); // non-blocking mode
}
if (sensors[2].dataReady())
{
R_ToF = sensors[2].read(false); // non-blocking mode
}
Serial.println(String(L_ToF) + "\t" + String(C_ToF) + "\t" + String(R_ToF));
// SerialBT.println(String(L_ToF) + "\t" + String(C_ToF) + "\t" + String(R_ToF));
} // end of read_sensors_nb()
void read_sensors_b() // ToFs read in Blocking mode
{
L_ToF = sensors[0].read(); // in Blocking Mode
C_ToF = sensors[1].read();
R_ToF = sensors[2].read();
Serial.println(String(L_ToF) + "\t" + String(C_ToF) + "\t" + String(R_ToF));
// SerialBT.println(String(L_ToF) + "\t" + String(C_ToF) + "\t" + String(R_ToF));
} // end of read_sensors_b()
bool rc100_Update2(uint8_t data2)
{
bool ret2 = false;
static uint8_t save_data2;
static uint8_t inv_data2;
static uint32_t time_t2;
inv_data2 = ~data2;
if (millis()-time_t2 > 100)
{
state2 = 0;
}
switch(state2)
{
case 0:
if (data2 == 0xFF)
{
state2 = 1;
time_t2 = millis();
}
break;
case 1:
if (data2 == 0x55)
{
state2 = 2;
received2 = false;
data2 = 0;
}
else
{
state2 = 0;
}
break;
case 2:
Data2 = data2; // Low Byte of Data
save_data2 = data2;
state2 = 3;
break;
case 3:
if (save_data2 == inv_data2)
{
state2 = 4;
}
else
{
state2 = 0;
}
break;
case 4:
Data2 |= data2<<8; // High Byte of Data
save_data2 = data2;
state2 = 5;
break;
case 5:
if (save_data2 == inv_data2)
{
received2 = true;
ret2 = true; // once per valid packet read in
}
state2 = 0;
break;
default:
state2 = 0;
break;
}
return ret2;
}
bool rc100_Available2(void)
{
// Serial.println("Inside rc100_Available2(void)");
if(SerialBT.available())
{
// Serial.println("Inside SerialBT.available()");
return rc100_Update2(SerialBT.read());
}
}
uint16_t rc100_readData2(void)
{
return Data2;
}
The Global Definitions section has 3 code sections for various devices:
- SerialBT() – classic BT communications found on the ESP32
- 3 VL53L1Xs used as a group of I2C devices connected to Pins D13, D14 and D15 on the ESP32’s header.
- Constants and Functions used for RC-100 communications between ESP32 and PC via SerialBT().
Function setup() initializes SerialBT() which is set to 115.2 Kbps via Device Manager on the PC, and also the 3 VL53L1Xs used.
Function loop() really just has 2 tasks:
- Read in latest values from the ToF sensors either in Non-Blocking mode or in Blocking mode. I actually started out using Blocking mode but got into trouble right away (explained in a later section). After consulting with Pololu Tech Support who suggested to try Non-Blocking mode instead, this mode became my “work-around” solution. See picture below for details of Functions read_sensors_nb() and read_sensors_b().
- The remaining code in setup() was for processing and acting on the RC-100 packets sent over from the PC via SerialBT. The RC-100 packet Capture and Processing tasks are performed by 3 functions: rc100_Update2(), rc100_Available2(), rc100_readData2().
An overview of these RC-100 processes can be summarized as follows:
For each iteration of Function loop():
- Parameter DataReceived2 is reset to zero.
- Each time that Function rc100_Available2() is invoked, it uses Function rc100_Update2() to read in 1 byte from the SerialBT() buffer and compare to the “expected” order of bytes FF, 55, Data_L, ~Data_L, Data_H, and ~Data_H for a proper and uncorrupted RC-100 packet. Thus the RC-100 protocol has built-in error-checking.
- In summary, if a “good” packet was received by SerialBT(), Function rc100_Available2() would evaluate FALSE the first 5 times going through Function loop(), and on the 6th time Function rc100_Available2() would evaluate TRUE. Then Parameter DataReceived2 gets assigned a numerical value corresponding to the Button pressed by the operator on the TASK V2 interface. For example, Button UP yields 1, while Button DOWN yields 2, and Button RIGHT yields 8, and so forth…
- Also, the net result is that 6 sets of ToF sensor data are collected for every RC-100 packet received and processed.
The picture below is a run-time screen capture of the Arduino IDE window, the Arduino Serial Monitor window, and the TASK V2 window, for a NON-BLOCKING typical run. And you can see that every task/feature is working out as it should – as I pushed on Button R (i.e. numerical value 8):
The picture below shows a run-time screen capture when ToF sensors are read in BLOCKING mode, whereas Sensors Data streamed in fine, but NO information about the Button that I pushed would show up on the Serial Monitor, unlike in the previous NON-BLOCKING case.
My interpretation is that somehow read_sensors_b() prevents rc100Available2() from ever evaluating to TRUE (by messing around with SerialBT() data buffer during the time waiting for the ToF sensors to be ready?). This is what I meant by “I2C disabling/interfering with Serial Communications” in the title of this post. As I had encountered the same issue back in June 2022 when I was using a MKR ZERO, so I think that this is an “Arduino Feature”! It was good that Pololu Library gave me a work-around solution, but I am very much interested in understanding what was really going on inside the Arduino Core between I2C and UART (and probably SPI too). Does anyone know of relevant documentations that I may have missed?
One last troubleshooting step that I did was to enable “text printing” from ESP32 to PC via SerialBT() inside Function read_sensors_b(), and this last picture shows that there was no problem with that task.
So the issue seemed to be how to program ESP32 to handle more appropriately two data streams coming in at the same time, one via I2C and the other via SerialBT(). Any suggestions that anyone may have? Besides making the I2C process NON-BLOCKING? Thank you in advance!