I am working on a project, where 2 load cells (outputting 1 weight value) and an IMU must communicate data to the Arduino Serial Monitor via bluetooth HC-05. I was having an issues where I could only get one of the values to be accurate, either the angle values from the IMU or the load cell weight value. I believe the issue is sending multiple signals to bluetooth, so I have made a change where I use the SoftwareSerial library to print the outputs, but now I am getting no output. Below is my code, as well as a picture of the Arduino and sensor setup. Any tips would be appreciated, and let me know if you have any other questions!
#include <Wire.h>
#include <MPU6050.h>
#include "SoftwareSerial.h"
#include <HX711_ADC.h>
#define PI 3.1415926535
MPU6050 mpu; //mpu6050 is for angle library
// Timers
float timeStep = 0.01;
unsigned long t = 0;
// Pitch, Roll and Yaw values
int angle_threshold=20;// Change this threshold for angle
int load_threshold=8;// Change this threshold for angle
float pitch_angle = 0;
float roll_angle = 0;
float yaw_angle = 0;
// Initialize Cell Values
float LC1data=0;
char val; //Store received variables
// Using software serial port, can speak digital port analog into serial port
SoftwareSerial BT(0, 1); //New object, receive foot 2, send foot 3
//pins:
const int HX711_dout1 = 4; //mcu > HX711 dout pin
const int HX711_sck1 = 5; //mcu > HX711 sck pin
//HX711 constructor:
HX711_ADC LoadCell1(HX711_dout1, HX711_sck1);
void setup()
{
//pinMode(LED_BUILTIN,OUTPUT);
Serial.begin(9600);
Wire.begin();
// Initialize Bluetooth
Serial.println(F("The bluetooth gates are open.\n Connect to HC-05 from any other bluetooth device with 1234 as pairing key!."));
BT.begin(9600); //set baud rate
delay(3000); //delay 3000ms
BT.print("AT\r\n"); //Send AT.
// Set threshold sensivty. Default 3.
// mpu.setThreshold(3);
//Load Cell 1 Setup
LoadCell1.begin();
//LoadCell1.setReverseOutput(); //uncomment to turn a negative output value to positive
unsigned long stabilizingtime = 2000; // preciscion right after power-up can be improved by adding a few seconds of stabilizing time
boolean _tare = true; //set this to false if you don't want tare to be performed in the next step
LoadCell1.start(stabilizingtime, _tare);
if (LoadCell1.getTareTimeoutFlag() ) {
Serial.println(F("Tare Timeout, check MCU>HX711 wiring and pin designations"));
while (1);
}
else if (LoadCell1.getSignalTimeoutFlag()) {
Serial.println(F("Signal Timeout, check MCU>HX711 wiring and pin designations"));
while (1);
}
else {
LoadCell1.setCalFactor(1.0); // user set calibration value (float), initial value 1.0 may be used for this sketch
Serial.println(F("Startup is complete"));
}
while (!LoadCell1.update());
calibrate1(); //start calibration procedure
// Initialize MPU6050 for the angles.
while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G))
{
Serial.println("Could not find a valid MPU6050 sensor, check wiring!");
delay(500);
}
// Calibrate gyroscope for the angles. The calibration must be at rest.
mpu.calibrateGyro();
}
void loop()
{
// Calculate Pitch, Roll and Yaw
// Read normalized values for
Vector norm = mpu.readNormalizeGyro();
//pitch and yaw are flopped from the original code bc of the direction of board on
// pitch_angle = pitch_angle + norm.YAxis * timeStep *180 /PI;
// yaw_angle = yaw_angle + norm.XAxis * timeStep *180 /PI;
// roll_angle = roll_angle + norm.ZAxis * timeStep *180 /PI;
pitch_angle =norm.YAxis * timeStep *180 /PI;
yaw_angle =norm.XAxis * timeStep *180 /PI;
roll_angle =norm.ZAxis * timeStep *180 /PI;
// Output raw
// Serial.print(" Pitch = ");
// Serial.println(pitch_angle);
// Serial.print(" Roll = ");
// Serial.println(roll_angle);
// Serial.print(" Yaw = ");
// Serial.println(yaw_angle);
BT.print(" Pitch = ");
BT.println(pitch_angle);
BT.print(" Roll = ");
BT.println(roll_angle);
BT.print(" Yaw = ");
BT.println(yaw_angle);
// Alert patient if moving too extreme
if(roll_angle>angle_threshold || pitch_angle>angle_threshold || yaw_angle>angle_threshold|| roll_angle<-angle_threshold || pitch_angle<-angle_threshold || yaw_angle<-angle_threshold ) {
BT.println(F("You are moving too much. Limit head movements. "));
//Serial.println(F("You are moving too much. Limit head movements. "));
}
//Load Cell 1 Readings
static boolean newDataReady = 0;
const int serialPrintInterval = 0; //increase value to slow down serial print activity
// check for new data/start next conversion:
if (LoadCell1.update()) newDataReady = true;
// get smoothed value from the dataset:
if (newDataReady) {
if (millis() > t + serialPrintInterval) {
LC1data = LoadCell1.getData();
// Serial.print(F("Load_cell 1 output val: "));
// Serial.println(LC1data);
BT.print(F("Load_cell 1 output val: "));
BT.println(LC1data);
newDataReady = 0;
t = millis();
}
}
// // receive command from serial terminal
// if (Serial.available() > 0) {
// char inByte = Serial.read();
// if (inByte == 't') LoadCell1.tareNoDelay(); //tare
// else if (inByte == 'r1') calibrate1(); //calibrate
// else if (inByte == 'c1') changeSavedCalFactor1(); //edit calibration value manually
// }
// check if last tare operation is complete
if (LoadCell1.getTareStatus() == true) {
//Serial.println(F("Tare complete"));
BT.println(F("Tare complete"));
}
// Alarm if weight is above 10lbs total
if ((LC1data)>load_threshold) {
//Serial.println(F("You are lifting too much weight!"));
BT.println(F("You are lifting too much weight!"));
}
// //If the serial port receives the data, it outputs it to the Bluetooth serial port.
// if (Serial.available()) {
// val = Serial.read();
// BT.print(val);
// }
// //If the Bluetooth module data is received, it is output to the screen.
// if (BT.available()) {
// val = BT.read();
// Serial.print(val);
// if (val=='1')
// {
// digitalWrite(LED_BUILTIN, HIGH);
// }
// else if (val=='0')
// {
// digitalWrite(LED_BUILTIN, LOW);
// }
// }
delay(500);
}
// Calibration 1
void calibrate1() {
Serial.println(F("***"));
Serial.println(F("Start calibration:"));
Serial.println(F("Place the load cell an a level stable surface."));
Serial.println(F("Remove any load applied to the load cell."));
Serial.println(F("Send 't' from serial monitor to set the tare offset."));
boolean _resume = false;
while (_resume == false) {
LoadCell1.update();
if (Serial.available() > 0) {
if (Serial.available() > 0) {
char inByte = Serial.read();
if (inByte == 't') LoadCell1.tareNoDelay();
}
}
if (LoadCell1.getTareStatus() == true) {
Serial.println(F("Tare complete"));
_resume = true;
}
}
Serial.println(F("Now, place your known mass on the loadcell."));
Serial.println(F("Then send the weight of this mass (i.e. 100.0) from serial monitor."));
float known_mass = 0;
_resume = false;
while (_resume == false) {
LoadCell1.update();
if (Serial.available() > 0) {
known_mass = Serial.parseFloat();
if (known_mass != 0) {
Serial.print(F("Known mass is: "));
Serial.println(known_mass);
_resume = true;
}
}
}
LoadCell1.refreshDataSet(); //refresh the dataset to be sure that the known mass is measured correct
float newCalibrationValue = LoadCell1.getNewCalibration(known_mass); //get the new calibration value
Serial.print(F("New calibration value has been set to: "));
Serial.print(newCalibrationValue);
Serial.println(F(", use this as calibration value (calFactor) in your project sketch."));
Serial.println(F("End calibration"));
Serial.println(F("***"));
Serial.println(F("To re-calibrate, send 'r1' from serial monitor."));
Serial.println(F("For manual edit of the calibration value, send 'c1' from serial monitor."));
Serial.println(F("***"));
}
void changeSavedCalFactor1() {
float oldCalibrationValue = LoadCell1.getCalFactor();
boolean _resume = false;
Serial.println(F("***"));
Serial.print(F("Current value is: "));
Serial.println(oldCalibrationValue);
Serial.println(F("Now, send the new value from serial monitor, i.e. 696.0"));
float newCalibrationValue;
while (_resume == false) {
if (Serial.available() > 0) {
newCalibrationValue = Serial.parseFloat();
if (newCalibrationValue != 0) {
Serial.print(F("New calibration value is: "));
Serial.println(newCalibrationValue);
LoadCell1.setCalFactor(newCalibrationValue);
_resume = true;
}
}
}
Serial.println(F("End change calibration value"));
Serial.println(F("***"));
}
In that case, all AT commands in your programme are redundant.
You cannot send data to serial monitor via Bluetooth. Use a proper terminal programme on PC, like RealTerm, or phone, like that by Morisch
This is misleading nonsense - and irrelevant. Pairing and connecting are two separate operations, and you only need to pair once.
You should not expect reliable service from that battery. Since your problem appears to be intermittent data, it may be giving you grief already. Use a wall wart, or PC USB while addressing your coding issues.
In regards to not being able to send data to the serial monitor, I am currently seeing data on the serial monitor, just not correct data for both signals simultaneously. Do you know why that might be?
I can delete the print statement. Agree its a bit unclear, it just came with the sample code for the library and I never deleted it. But it should not affect the functionality of the bluetooth.
I will try plugging into an outlet! Thanks for the tip.
This depends entirely on what you want to do. There are times when you are obliged to use software serial on a Uno, but it is usually best avoided.
In your case, you appear to want to send raw data via Bluetooth, presumably for a remote datafile, AND a raft of stuff about calibration (that I know nothing about) to the serial monitor, presumably just for your own edification. Hence two comms channels, serial for monitor, software serial for Bluetooth. However it seems that the main job is just a datastreaming loop, and you may deem it OK to send that other stuff out to Bluetooth during Setup, and you may eventually find you don't need it anyway. In that event, you could leave Bluetooth on pins 0,1 and
serial.print
everything, thereby dispensing with software serial altogether. The time when you really need software serial on a Uno is when you need the hardware serial for keyboard input. I believe a Uno can use any pins other than 0,1 for software serial - even the analogue pins.
Every serial.print command in the loop should result in data on the screen. I can't see any serial.print command. I suggest you comment out all superfluous serial.prints and ensure that all relevant ones are in the loop, rather than off in some subroutine somewhere.
Update: I have changed the software serial port to 2 and 3 so it does not interfere with the TX and RX pins (0 and 1). To my understanding the TX and RX pins are used to process information typed into the Serial Monitor bar. (I use this during calibration). Pins 2 and 3 are used to process data from the sensors. I then use the Serial. print command to display the data collected by the sensors as well as alerts based on sensor values. Here is the code with those changes (I omitted the calibration functions for simplicity). I haven't changed any wiring.
The pitch roll yaw angles are correct, and change accurately based on movements in real time. Whereas the load cell weight is obviously incorrect. Because, I can see real time changes in IMU values I want to assume the bluetooth connection is fine. Do you think its correct to assume that the bluetooth is working but something is off with the load cell, whether is be wire connections or code? Or is it possible the bluetooth is throwing off only the load cell values, and not the IMU angle values?
#include <Wire.h>
#include <MPU6050.h>
#include "SoftwareSerial.h"
#include <HX711_ADC.h>
#define PI 3.1415926535
MPU6050 mpu; //mpu6050 is for angle library
// Timers
float timeStep = 0.01;
unsigned long t = 0;
// Pitch, Roll and Yaw values
int angle_threshold=20;// Change this threshold for angle
int load_threshold=8;// Change this threshold for angle
float pitch_angle = 0;
float roll_angle = 0;
float yaw_angle = 0;
// Initialize Cell Values
float LC1data=0;
char val; //Store received variables
// Using software serial port, can speak digital port analog into serial port
SoftwareSerial BT(2, 3); //New object, receive foot 2, send foot 3
//pins:
const int HX711_dout1 = 4; //mcu > HX711 dout pin
const int HX711_sck1 = 5; //mcu > HX711 sck pin
//HX711 constructor:
HX711_ADC LoadCell1(HX711_dout1, HX711_sck1);
void setup()
{
//pinMode(LED_BUILTIN,OUTPUT);
Serial.begin(9600);
Wire.begin();
// Initialize Bluetooth
Serial.println(F("Ready to connect. Pairing Key is 1234 ."));
BT.begin(9600); //set baud rate
delay(3000); //delay 3000ms
//Load Cell 1 Setup
LoadCell1.begin();
unsigned long stabilizingtime = 2000; // preciscion right after power-up can be improved by adding a few seconds of stabilizing time
boolean _tare = true; //set this to false if you don't want tare to be performed in the next step
LoadCell1.start(stabilizingtime, _tare);
if (LoadCell1.getTareTimeoutFlag() ) {
Serial.println(F("Tare Timeout, check MCU>HX711 wiring and pin designations"));
while (1);
}
else if (LoadCell1.getSignalTimeoutFlag()) {
Serial.println(F("Signal Timeout, check MCU>HX711 wiring and pin designations"));
while (1);
}
else {
LoadCell1.setCalFactor(1.0); // user set calibration value (float), initial value 1.0 may be used for this sketch
Serial.println(F("Startup is complete"));
}
while (!LoadCell1.update());
calibrate1(); //start calibration procedure
// Initialize MPU6050 for the angles.
while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G))
{
Serial.println("Could not find a valid MPU6050 sensor, check wiring!");
delay(500);
}
// Calibrate gyroscope for the angles. The calibration must be at rest.
mpu.calibrateGyro();
}
void loop()
{
// Calculate Pitch, Roll and Yaw
// Read normalized values for
Vector norm = mpu.readNormalizeGyro();
pitch_angle =norm.YAxis * timeStep *180 /PI;
yaw_angle =norm.XAxis * timeStep *180 /PI;
roll_angle =norm.ZAxis * timeStep *180 /PI;
// Output raw
Serial.print(" Pitch = ");
Serial.println(pitch_angle);
Serial.print(" Roll = ");
Serial.println(roll_angle);
Serial.print(" Yaw = ");
Serial.println(yaw_angle);
// BT.print(" Pitch = ");
// BT.println(pitch_angle);
// BT.print(" Roll = ");
// BT.println(roll_angle);
// BT.print(" Yaw = ");
// BT.println(yaw_angle);
// Alert patient if moving too extreme
if(roll_angle>angle_threshold || pitch_angle>angle_threshold || yaw_angle>angle_threshold|| roll_angle<-angle_threshold || pitch_angle<-angle_threshold || yaw_angle<-angle_threshold ) {
// BT.println(F("You are moving too much. Limit head movements. "));
Serial.println(F("You are moving too much. Limit head movements. "));
}
//Load Cell 1 Readings
static boolean newDataReady = 0;
const int serialPrintInterval = 0; //increase value to slow down serial print activity
// check for new data/start next conversion:
if (LoadCell1.update()) newDataReady = true;
// get smoothed value from the dataset:
if (newDataReady) {
if (millis() > t + serialPrintInterval) {
LC1data = LoadCell1.getData();
Serial.print(F("Load_cell 1 output val: "));
Serial.println(LC1data);
// BT.print(F("Load_cell 1 output val: "));
// BT.println(LC1data);
newDataReady = 0;
t = millis();
}
}
// receive command from serial terminal
if (Serial.available() > 0) {
char inByte = Serial.read();
if (inByte == 't') LoadCell1.tareNoDelay(); //tare
else if (inByte == 'r1') calibrate1(); //calibrate
else if (inByte == 'c1') changeSavedCalFactor1(); //edit calibration value manually
}
// check if last tare operation is complete
if (LoadCell1.getTareStatus() == true) {
Serial.println(F("Tare complete"));
// BT.println(F("Tare complete"));
}
// Alarm if weight is above 10lbs total
if ((LC1data)>load_threshold) {
Serial.println(F("You are lifting too much weight!"));
// BT.println(F("You are lifting too much weight!"));
}
// //If the serial port receives the data, it outputs it to the Bluetooth serial port.
// if (Serial.available()) {
// val = Serial.read();
// BT.print(val);
// }
// //If the Bluetooth module data is received, it is output to the screen.
// if (BT.available()) {
// val = BT.read();
// Serial.print(val);
// }
delay(500);
}
With that line of thinking you might have a hard time explaining the Tx pin. Further, the signal from the keyboard comes down the USB cable from the PC with the keyboard attached, so no.
A Uno, and those of its ilk, have one hardware UART. It is shared by pins 0,1, hence their clearly being labelled Rx,Tx, and the USB socket, hence the serial.printing to the PC's monitor. Since you are using the keyboard, and I assume you will always need to do this every time you start up, this is not debugging, it is a required procedure, and you are right to use software serial for Bluetooth. As I said before, this is the time for it but, contrary to common rumour, you do not need to have your peripherals on software serial simply so you can debug your serial operations, indeed it is a dumb idea and purveyors of that claptrap might have a hard time explaining why Arduino carefully labels pins 0,1 as Rx.Tx. I might point out that Arduino neither knows nor cares about what's going on in that arena, it simply serial.prints to the UART.
No, pardon me for being so pedantic: Arduino processes the data, and pins 2,3 are simply used to send it to Bluetooth.
OK, that's BT.prints for Bluetooth and serial.prints for monitor. This is reasonable since the BT. data and serial. data are different. I assume you see everything you expect to see on the screen but nothing from Bluetooth because all your BT.prints are commented out.
Update: The issue was uploading the code with my mac. When my teammate with a pc uploads the code to the arduino it works as expected. When I upload the same code from my mac it does not!