Qt bluetooth: get Battery level value notifications - qt

I am developing a program to read some characteristics from a smart watch (heart rate, battery level, humidity and temperature).
To doing this, I am following the heart rate game example. From this example I am able to get notifications about heart rate from a simulator on my smartphone, but for the other parameters I am not able to get the values.
For example, for heart rate I'm doing this for stateChanged: (connect(m_service, &QLowEnergyService::stateChanged, this, &DeviceHandler::serviceStateChanged);)
if (uuid == QBluetoothUuid(QBluetoothUuid::HeartRate)) {
const QLowEnergyCharacteristic hrChar = m_service->characteristic(QBluetoothUuid(QBluetoothUuid::HeartRateMeasurement));
m_notificationDesc = hrChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0100"));
And this for updateValue: (connect(m_service, &QLowEnergyService::characteristicChanged, this, &DeviceHandler::updateValue);)
auto data = reinterpret_cast<const quint8 *>(value.constData());
quint8 flags = *data;
if (flags & 0x1) // HR 16 bit? otherwise 8 bit
hrvalue = static_cast<int>(qFromLittleEndian<quint16>(data[1]));
else
hrvalue = static_cast<int>(data[1]);
qCDebug(BLELog) << hrvalue;
If i run my program, it finds the device and connect to it and I get correctly the new heart rate value everytime I push "notify" button on the simulator.
For the other services I don't have the equivalent of HeartRateMeasurment.
For the battery I tried to use BatteryService and then BatteryLevel as characteristic but my program does not read the values while standing still (but not blocked).
Which services and characteristics should I use to get the same result I get with heart rate?
Thank you very much for all the help.

Related

How to transfer an Integer from an Arduino to another Arduino?

So I have to write a code that recognizes colour using a colour-detector.
The because the colourpalette is limited to just 6 colours I can be saved as an Integer (Red as 0, Green as 1 aso.).
Now I have problems transfering the Integer from the Arduino that handles the detector to the Arduino I have to write code on.
I tried using the analog (A0) pin but with that I only ended up with a 19-20 whenever I tried to transfer anything at all.
Is there a solution to transfer an Integer from 0-5?
Thanks in advance
Using the Analog is not a good solution. You should use the "Serial Connection". It only requires two cables - no other electronics - and the code is very simple (see below).
If you just want to transfer values in the range 0-255 or smaller (as in your case: 0-5), you can use the "byte"-type variable. One or multiple bytes can easily be transferred using a serial connection, using the "TX" (transmit) and "RX" (receive) pins of the Arduinos. You just connect the TX pin from Arduino #1 to the "RX" pin of Arduino #2 - and: connect the GND pins of the two.
In the setup code for both, you need to start the serial connection with the same baud rate, e.g.
void setup(){
Serial.begin(9600);
}
Arduino #1 is sending a byte using the following command:
byte color = 0; // declare the variable "color"
color = 3; // set the variable to any value in the range [0-255]
Serial.write(color); // transmit the byte-variable "color"
Arduino #2 is receiving the byte. Therefore it needs to continuously check for new serial data.
void loop() {
byte data = 0;
// - check for new serial data - and respond accordingly
if (Serial.available() > 0) {
int x = Serial.read(); // The "Serial.read" command returns integer-type
data = x; //
// - now: do something with "data"
if (data == 0) {
// "red" was received ... do something ...
} else if (data == 1) {
// "green" was received ... do something ...
} else if (data == 2) {
// ... and so on, and so on ...
}
}
// ... do other tasks
}
Make sure that in the "other tasks" you are not using the command "delay", as this would prevent your code from checking the serial for new data in a timely manner.
In exactly the same way, your Arduino #2 could also send data back to Arduino #1. In that case, you add one more cable connecting "TX" from #2 to "RX" of #1, and use the same code as above, on the respective other Arduino.
Serial is most universal, as you can test/simulate/use it by a broad variety of partners.
Here, you might code your possible values as human readable characters { '0' .. '5' } or
{'R', 'G', 'B', 'C', 'M', 'Y'} or whatever you like.
Eventually, I2C is a better way of serial communication.
Of course, an analog signal can be divided into 6 clearly distinguishable areas, if it's more about states than about events. You need electronics (low pass filter) to turn analogWrite PWM into analog constant values. And eventually, you have to handle the state transitions separately.
(Huh, sounds pretty complicated, doesn't it :) )

What is the unit of measure for the pressure data I collect using an FSR sesnor with an Arduino

I have written code on the Arduino to record the pressure applied to a FSR sensor connected to pin A0. Here is my code
int pressureAnalogPin = 0; //pin where our pressure pad is located.
int pressureReading; //variable for storing our reading
bool active = false; //boolean to check whether arduino should be sending pressure values
void setup()
{
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}
void loop()
{
if (Serial.available()) //checks if data is coming in
{
char read = Serial.read(); //Set varaiable read to data read from mobile
if (read == 'g') //if read is equal to character 'g' set boolean active to true
{
active = true;
}
if (read == 'x') //if read is equal to character 'x' set boolean active to false
{
active = false;
}
}
if (active == true) //Only send data to phone when boolean active is set to true
{
pressureReading = analogRead(pressureAnalogPin); // Set varaible pressureReading to the pressure value recorded by FSR
Serial.print(pressureReading); //Send pressure value to mobile phone
}
delay(100);// a delay of 100ms in loop
}
I receive results from 0 to 1023. I have conducted an experiment, by incrementing weights on top of the pressure sensor.
Excel Experiment Results
Above is an excel chart showing the increase in weight and the pressure recorded.
Can someone let me know what is the unit is for these pressure readings?
As you gave already measured and calibrated the sensor with weights 50 - 800 gram according to your Excel, you have the option to use two methods (provided you use the same setup as in your calibration.
Oprion one programatically with map
map(value, fromLow, fromHigh, toLow, toHigh)
for each interval
map(measuredValue, 974, 978, 351, 400)
which in your case will produce very imprecise measurements as you have to have if and than map.
Or you calculate the whole range with EXCEL interpolate function than you get for every of the 1024 data points a gram value which you store in an array and retrieve with:
gramValue = interpolatedArray[measuredValue];
BUT for real use you will probably need an additional circuit, the manufacturers calibration data and a stable power supply. For playing around and learning this shoot and hope its a hit method is ok.
AND this is the sellers recommendation:
These sensors are simple to set up and great for sensing pressure, but
they aren't incredibly accurate. Use them to sense if it's being
squeezed, but you may not want to use it as a scale
Datasheet for the sensor including all the calibration circuits

STM32F4 Encoder count is changing when it should not

I am currently using the STM32F4 with the STM32F429ZI Nucleo-144 Board. I am looking to use this microcontroller to evaluate the position of a rotary encoder via a quadrature encoder interface. Looking at the documentation, this is done with the timers. I have the A/B encoder outputs hooked up to PA6 and PC7 on the micro, but I have noticed that the counts appear to be drifting.
During the debugging, I noticed that if I disconnect one of the encoder outputs to the microcontroller and I move the motor, the counts still increment/decrement even though only one of the encoder lines are connected. Since I am counting on both the TI1 and TI2 edges, this should not be happening. If I am reading the below diagram correctly, since one of my lines is held high using the internal pull-up, clock pulses on the other input should be going up/down/up/down and really just cycling between two different counts. However, if I am rotating the encoder, the counts keep incrementing or decrementing depending on the direction.
Why is the encoder count changing with only one encoder input connected? I also have the scope trace attached to prove that only one count is is active, as well as the code.
EDIT: I have also tried changing the polarity from BOTH EDGE to RISING EDGE, with no perceived benefit.
#include "stm32f4xx_hal.h"
#include "encoder_test.h"
GPIO_InitTypeDef GPIO_InitStruct;
TIM_HandleTypeDef Timer_InitStruct;
TIM_Encoder_InitTypeDef Encoder_InitStruct;
void EncoderTest_Init()
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_TIM3_CLK_ENABLE();
/**TIM3 GPIO Configuration
PA6 ------> TIM3_CH1
PC7 ------> TIM3_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
Timer_InitStruct.Instance = TIM3;
Timer_InitStruct.Init.Period = 0xFFFF;
Timer_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
Timer_InitStruct.Init.Prescaler = 1;
Timer_InitStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
Encoder_InitStruct.EncoderMode = TIM_ENCODERMODE_TI12;
Encoder_InitStruct.IC1Filter = 0x00;
Encoder_InitStruct.IC1Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
Encoder_InitStruct.IC1Prescaler = TIM_ICPSC_DIV1;
Encoder_InitStruct.IC1Selection = TIM_ICSELECTION_DIRECTTI;
Encoder_InitStruct.IC2Filter = 0x00;
Encoder_InitStruct.IC2Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
Encoder_InitStruct.IC2Prescaler = TIM_ICPSC_DIV1;
Encoder_InitStruct.IC2Selection = TIM_ICSELECTION_DIRECTTI;
if (HAL_TIM_Encoder_Init(&Timer_InitStruct, &Encoder_InitStruct) != HAL_OK)
{
while (1);
}
if (HAL_TIM_Encoder_Start_IT(&Timer_InitStruct, TIM_CHANNEL_1) != HAL_OK)
{
while (1);
}
}
void TIM3_IRQHandler()
{
HAL_TIM_IRQHandler(&Timer_InitStruct);
}
Upon further investigation, it appears that the issue is due to the prescaler. The prescaler does not work in encoder mode when you provide even values. Since prescaler is the entered value + 1, using the STM32F4 HAL, the entered prescaler must be even.
I found confirmation that I am not the only person with this issue at this forum post. There is some discussion at the post that prescalers may not be compatible with encoder mode, but this has not yet been confirmed. I have sent an email to ST to get to the bottom of it. It is safe to enter a prescaler value of 0 if it is not supported.
Here is the working code below:
#include "stm32f4xx_hal.h"
#include "encoder_test.h"
GPIO_InitTypeDef GPIO_InitStruct;
TIM_HandleTypeDef Timer3_InitStruct;
TIM_Encoder_InitTypeDef EncoderTim3_InitStruct;
void EncoderTest_Init_Tim3()
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_TIM3_CLK_ENABLE();
/**TIM3 GPIO Configuration
PA6 ------> TIM3_CH1
PC7 ------> TIM3_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
Timer3_InitStruct.Instance = TIM3;
Timer3_InitStruct.Init.Period = 0xFFFF;
Timer3_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
Timer3_InitStruct.Init.Prescaler = 10;
Timer3_InitStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
EncoderTim3_InitStruct.EncoderMode = TIM_ENCODERMODE_TI12;
EncoderTim3_InitStruct.IC1Filter = 0x00;
EncoderTim3_InitStruct.IC1Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
EncoderTim3_InitStruct.IC1Prescaler = TIM_ICPSC_DIV4;
EncoderTim3_InitStruct.IC1Selection = TIM_ICSELECTION_DIRECTTI;
EncoderTim3_InitStruct.IC2Filter = 0x00;
EncoderTim3_InitStruct.IC2Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
EncoderTim3_InitStruct.IC2Prescaler = TIM_ICPSC_DIV4;
EncoderTim3_InitStruct.IC2Selection = TIM_ICSELECTION_DIRECTTI;
if (HAL_TIM_Encoder_Init(&Timer3_InitStruct, &EncoderTim3_InitStruct) != HAL_OK)
{
while (1);
}
if (HAL_TIM_Encoder_Start_IT(&Timer3_InitStruct, TIM_CHANNEL_1) != HAL_OK)
{
while (1);
}
}
void TIM3_IRQHandler()
{
HAL_TIM_IRQHandler(&Timer3_InitStruct);
}
EDIT:
After speaking with ST tech support, the encoder interface was not intended to be used with a prescaler value, even OR odd. I have pasted their response below, but even with using a prescaler value that appears to work, it seems possible that the encoder counts drift over time.
Another solution is to use no prescalers, but instead extend the 16 bit value into the 32 bit space using the approach suggested here. I have reprinted the approach here in case the link goes dead:
From user goosen.kobus.001 on 11/19/2013 on ST's forum:
In my experience using an overflow interrupt to scale up an encoder is not reliable, especially when you have high resolution encoders: it happens from time to time that the encoder value will change sign in the instant you enter the interrupt, causing the system to increment the upper word when it should have decremented etc. This is especially true if the encoder is supposed to be stalled at 0, like a servo motor commanded to go to encoder position 0.
The best approach I have found is to do it manually. this is my procedure:
Ensure that the control loop that reads the encoder value is run often, (i.e. that if your encoder is rotating at full speed, the encoder value is still read at least 10-20 times between overflows. For my servo motor application a 1ms loop interval was sufficient.
keep track of the last read encoder value.
divide the current and last encoder value into quadrants (the most significant 2 bits). i.e. pos_now &= 0xC000; pos_last &= 0xC000;
check to see if the encoder has moved from quadrant 0 to quadrant 3 or 3 to 0 in the last step:
4.1 if(pos_now == 0 && pos_last == 0xC000) upper_word++;
4.2 if(pos_now == 0xC000 && pos_last == 0) upper_word--;
this is why I say the encoder read loop needs to be run often; you need to be sure that the value is read often enough that it is impossible to go from quadrant 0->1->2->3 in between reads.
It should also be possible to put this logic in another timer interrupt that runs at say 10kHz. that way you have an encoder value which is always up to date.
ST RESPONSE:
Hi,
I have got the feedback from the design and the architect of the timers.
The encoder interface has been designed to work without prescaler in order to not downgrade the resolution of the encoder.
As you observed, they have confirmed that it cannot work with even prescaler value but only the odd ones.
We have a sub-counter for the prescaler which is mono-directional, so not affected by the counter direction and incremented on every rising edge of the timer clock (without the prescaler).
The counter direction is updated on every rising edge of the timer clock (without the prescaler) but the counter is incremented only while the sub-counter of the prescaler reach the programmed value and according to the value in the direction bit.
So, in one case, the behavior is the same as without prescaler, because the counter is updated with a different direction (every odd clock cycles number), but in the other case, the direction is always the same when the counter is updated and the encoder interface doesn't work correctly.
So you can use the prescaler but with an odd value.
The recommended use case is without prescaler.
Best regards
ST MCU Tech Support

My Qt app does not recieve all the data sent by arduino

I'll go right to the point. My arduino reads values from the adc port and send them via serial port(values from 0 to 255). I save them in a byte type vector. After sending an specific signal to arduino, it starts to send to Qt app the data saved in the vector. Everything is working except that the arduino should send 800 values and the app receives less values than that. If I set the serial baud rate to 9600, I get 220 values. If, instead, I set the baud rate to 115200, I get only 20 values. Can you guys help me to fix this? I would like to use 115200 baud rate, because I need a good trasmision speed at this project(Real time linear CCD). I'll leave some code below:
Arduino code:
void sendData(void)
{
int x;
for (x = 0; x < 800; ++x)
{
Serial.print(buffer[x]);
}
}
This is the function that sends the values. I think is enough information, so I summarized it. If you need more code, please let me know.
Qt serial port setting code:
...
// QDialog windows private variables and constants
QSerialPort serial;
QSerialPortInfo serialInfo;
QList<QSerialPortInfo> listaPuertos;
bool estadoPuerto;
bool dataAvailable;
const QSerialPort::BaudRate BAUDRATE = QSerialPort::Baud9600;
const QSerialPort::DataBits DATABITS = QSerialPort::Data8;
const QSerialPort::Parity PARITY = QSerialPort::NoParity;
const QSerialPort::StopBits STOPBITS = QSerialPort::OneStop;
const QSerialPort::FlowControl FLOWCONTROL = QSerialPort::NoFlowControl;
const int pixels = 800;
QVector<double> data;
unsigned int dataIndex;
QByteArray values;
double maximo;
...
// Signal and slot connection.
QObject::connect(&serial, SIGNAL(readyRead()), this,SLOT(fillDataBuffer()));
...
// Method called when there's data available to read at the serial port.
void Ventana::fillDataBuffer()
{
dataIndex++;
data.append(QString::fromStdString(serial.readAll().toStdString()).toDouble());
if(data.at(dataIndex-1) > maximo) maximo = data.at(dataIndex-1);
/* The following qDebug is the one I use to test the recieved values,
* where I check that values. */
qDebug() << data.at(dataIndex-1);
}
Thanks and sorry if it's not so clear, it has been an exhausting day :P
Ok... I see two probelms here:
Arduino side: you send your data in a decimal form (so x = 100 will be sent as 3 characters - 1, 0 and 0. You have no delimiter between your data, so how your receiver will know that it received value 100 not three values 1, 0 and 0? Please see my answer here for further explanation on how to send ADC data from Arduino.
QT side: There is no guarantee on the moment when readyRead() signal will be triggered. It may be immediately after first sample arrives, but it may be raised after there are already couple of samples inside the serial port buffer. If that happens, your method fillDataBuffer() may process string 12303402 instead of four separate strings 123, 0, 340 and 2, because between two buffer reads four samples arrived. The bigger the baudrate, the more samples will arrive between the reads, which is why you observe less values with a bigger baud rate.
Solution for both of your problems is to append some delimiting byte for your data, and split the string in the buffer on that delimiting byte. If you don't want to have maximum data throughput, you can just do
Serial.print(buffer[x]);
Serial.print('\n');
and then, split incoming string on \n character.
Thank you very much! I did what you said about my arduino program and after solving that, I was still not getting the entire amount of data. So the the problem was in Qt. How you perfectly explain, the serial buffer was accumulating the values too fast, so the slot function "fillDataBuffer()" was too slow to process the arriving data. I simplified that function:
void Ventana::fillDataBuffer()
{
dataIndex++;
buffer.append(serial.readAll());
}
After saving all the values in the QByteArray buffer, I process the data separately.
Thanks again man. Your answer was really helpful.

BLE GATT server data format

I'm playing on this example:
https://doc-snapshots.qt.io/qt5-dev/qtbluetooth-heartrate-server-example.html
to better understand how to configure a GATT server.
The example fakes a HeartRate profile. In detail it creates a characteristic with this client descriptor:
const QLowEnergyDescriptorData clientConfig(QBluetoothUuid::ClientCharacteristicConfiguration, QByteArray(2, 0));
from here:
https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorViewer.aspx?u=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
I understand it has both notifications and indications disabled by default (in fact I need to enable them from a client application in order to be notified).
What I really don't understand is this code:
quint8 currentHeartRate = 60;
const auto heartbeatProvider = [&service, &currentHeartRate, &valueChange]() {
QByteArray value;
value.append(char(0)); // Flags that specify the format of the value.
value.append(char(currentHeartRate)); // Actual value.
QLowEnergyCharacteristic characteristic = service->characteristic(QBluetoothUuid::HeartRateMeasurement);
service->writeCharacteristic(characteristic, value); // Potentially causes notification.
...
Well, it appends two bytes to the characteristic's value because it was defined above:
QLowEnergyCharacteristicData charData;
charData.setUuid(QBluetoothUuid::HeartRateMeasurement);
charData.setValue(QByteArray(2, 0));
but what does the first one mean?
value.append(char(0)); // Flags that specify the format of the value.
I cannot find any documentation about this "format".
The first byte is the flags field specified in the Heart Rate Service (HRS)here. In this example, flags field indicates that the heart rate measurement value is in uint8 format.

Resources