Micro:Bit read bluetooth message - c++

I want to send messages from my Micro:Bit to a linked device over bluetooth. I have the following code for Micro:Bit:
#include "MicroBit.h"
#include "MicroBitUARTService.h"
MicroBitUARTService *uart;
MicroBit uBit;
uint8_t connected = 0;
void onConnect(MicroBitEvent)
{
connected = 1;
uBit.display.print("C");
}
void onDisconnect(MicroBitEvent)
{
connected = 0;
uBit.display.print("D");
}
void onButtonA(MicroBitEvent e)
{
if (connected == 0) {
uBit.display.print("X");
return;
}
uart->send("Button A");
uBit.display.print("A");
}
void onButtonB(MicroBitEvent e)
{
if (connected == 0) {
uBit.display.print("X");
return;
}
uart->send("Button B");
uBit.display.print("B");
}
int main()
{
// Initialise the micro:bit runtime.
uBit.init();
uBit.messageBus.listen(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_CONNECTED, onConnect);
uBit.messageBus.listen(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_DISCONNECTED, onDisconnect);
uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, onButtonA);
uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonB);
uart = new MicroBitUARTService(*uBit.ble, 32, 32);
uBit.display.print("S");
release_fiber();
}
I'm able to pair it with my macbook using the following tool:
Once paired, I don't know how to read the messages sent over uart bluetooth.

Don't let the term UART in the characteristic name confuse you, it's just a standard characteristic and has nothing to do with actual UART.
according to the documentation, indications are used with the UART TX characteristic so look at how to use Indications from your API .
https://lancaster-university.github.io/microbit-docs/resources/bluetooth/bluetooth_profile.html
And
https://lancaster-university.github.io/microbit-docs/ble/uart-service/#example-microbit-application-animal-vegetable-mineral-game for an Android example.
Martin
More....
Per the profile documentation for which I gave the link above, you can write to the RX characteristic but must subscribe to Indications to the TX characteristic. You cannot read it directly.
On a Raspberry Pi I would use the Noble node.hs module:
https://github.com/sandeepmistry/noble
For indications use
characteristic.subscribe([callback(error)]);
and
characteristic.on('data', callback(data, isNotification));
For writing use
characteristic.write(data, withoutResponse[, callback(error)]); // data is a buffer, withoutResponse is true|false
I know you are not interested in phones but the principle is exactly the same, whichever platform you are coding for and whichever API you use. You just need to know what operations each characteristic supports and then use your API accordingly.

Related

QT -- QTLowEnergyService->discoverDetails() does not discover non standard characteristics

I am writing a QT application for BLE on windows 10. The windows application is a BLE central, while the peripheral is running on an iOS device (tablet or phone). I pretty much followed the low energy scanner example and I can find the iOS device with the UUID of interest. The problem is that when the service is discovered and after issuing the discoverDetails() to get a list of the characteristics, the QT state machine goes from DiscoveryRequired, DiscoveringServices, Invalid Service, and then it disconnects.
I know this is a QT problem because
I can connect and interact with the peripheral using other applications
https://apps.apple.com/us/app/lightblue/id557428110
https://www.microsoft.com/en-us/p/ble-scanner/9nblggh0j7m0#activetab=pivot:overviewtab
The BLE scanner app (written in C#) from Microsoft, when compiled and run on the same machine as QT, also is able to connect and interact with the iOS peripheral.
I have noticed that other people are had/have the same problem, but I don't see where/if a resolution/workaround was eventually found.
Qt - Cannot read descriptor of the characteristic - BLE
Here is my handler for the QLowEnergyService::ServiceState signal
void BleClient::serviceStateChanged(QLowEnergyService::ServiceState newState)
{
switch (newState)
{
case QLowEnergyService::DiscoveringServices) :
{
// Nothing to do here, just note that we got into this state
qDebug() << "Discovering services";
break;
}
case QLowEnergyService::ServiceDiscovered:
{
qDebug() << "Service discovered";
const QList<QLowEnergyCharacteristic> chars = m_currentService->characteristics();
for (const QLowEnergyCharacteristic& ch : chars) {
auto cInfo = new CharacteristicInfo(ch);
m_characteristics.append(cInfo);
}
if (!chars.isValid()) {
setMessage("Value for characteristic not found.");
break;
}
m_notificationDesc = chars.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
if (m_notificationDesc.isValid()) {
m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0100"));
setMessage("Characteristic enabled");
m_start = QDateTime::currentDateTime();
}
break;
}
default:
qDebug() << "Unhandled state received : (" << newState << ")";
}
}
Ok, figured it out. Added more debug code and saw that a very descriptive error was being logged:
QLowEnergyService error: UnknownError
Looking further, seems like , the QLowEnergyController needs a queued connection callback for the serviceDiscovered event to work as expected. So I changed
connect(controller, &QLowEnergyController::discoveryFinished, this, &BleClient::serviceScanDone);
to
connect(controller, &QLowEnergyController::discoveryFinished, this, &BleClient::serviceScanDone, Qt::QueuedConnection);
..and lo and behold, now I see all services with all the characteristics.

BLE BlueZ return fixed pin for connection

I have bluez 5.48 running on embedded device and I am able to connect Apple and Windows devices over the bluetooth. I have also been able to get Bluetooth pairing working using DisplayOnly custom agent which generates random pin/pass for pairing.
The embedded device has no Input/Output peripherals so I need to return fixed pin for all connections but for some reason I am not finding the right way to do it. So far I have created custom agent, registered it on dbus, which receives the calls RequestPinCode and DisplayPasskey (but they are set to return auto generated pins.)
here is code snippet from my set up
static void bluez_agent_method_call(GDBusConnection *con,
const gchar *sender,
const gchar *path,
const gchar *interface,
const gchar *method,
GVariant *params,
GDBusMethodInvocation *invocation,
void *userdata)
{
int pass;
int entered;
char *opath;
GVariant *p= g_dbus_method_invocation_get_parameters(invocation);
g_print("Agent method call: %s.%s()\n", interface, method);
if(!strcmp(method, "RequestPinCode")) {
;
}
else if(!strcmp(method, "DisplayPinCode")) {
;
}
else if(!strcmp(method, "RequestPasskey")) {
g_print("Getting the Pin from user: ");
fscanf(stdin, "%d", &pass);
g_print("\n");
g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", pass));
}
else if(!strcmp(method, "DisplayPasskey")) {
g_variant_get(params, "(ouq)", &opath, &pass, &entered);
cout << "== pass = " << pass << endl;
pass=1234; // Changing value here does not change the actual Pin for some reason.
cout << "== pass = " << pass << "opath = " << opath << endl;
g_dbus_method_invocation_return_value(invocation, NULL);
}
else if(!strcmp(method, "RequestConfirmation")) {
g_variant_get(params, "(ou)", &opath, &pass);
g_dbus_method_invocation_return_value(invocation, NULL);
}
else if(!strcmp(method, "RequestAuthorization")) {
;
}
else if(!strcmp(method, "AuthorizeService")) {
;
}
else if(!strcmp(method, "Cancel")) {
;
}
else
g_print("We should not come here, unknown method\n");
}
I tried changing the pass variable in DisplayPasskey function to set new pin but bluetooth still connects with the auto generated pin only.
I found this stack overflow question which is exactly what I need How to setup Bluez 5 to ask pin code during pairing and from the comments, there seems to be solution to return the fixed pins.
It would be great if somebody can provide me with some examples to return fix pin in DisplayPasskey and RequestPinCode functions.
The Bluetooth standard does not contain a fixed key association model. The standard does not use a PAKE (https://en.m.wikipedia.org/wiki/Password-authenticated_key_agreement) but a custom ad-hoc weaker protocol. The custom protocol used during passkey pairing is only secure for one time passkeys (in particular, a passive eavesdropper learns the passkey used after a successful pairing attempt and can also be brute forced in at most 20 pairing attempts).
BlueZ follows the Bluetooth standard, which says the passkey should be randomly generated. Therefore you cannot set your own fixed passkey. If you don't have the required I/O capabilities, you shall use the "Just Works" association model instead (which unfortunately does not give you MITM protection). If you want higher security by using a fixed passkey for MITM protection, you must implement your own security layer on top of the (insecure) Application layer. This is for example what Apple's Homekit does.
Please also see my post at https://stackoverflow.com/a/59282315.
This article is also worth reading that explains why a static passkey is insecure: https://insinuator.net/2021/10/change-your-ble-passkey-like-you-change-your-underwear/.

How to set low latency in serial port open by QSerialPort

I have already developed an application and it performs serial communication with sensors.
For an unknown reason, my received data is slow with QT C++ Framework.
I have tested the sample code (https://www.pjrc.com/tmp/host_software/receive_test.c). I received data in the appropriate time with this sample code.
Here i found that low latency mode is set ASYNC_LOW_LATENCY.
I have read (https://doc.qt.io/qt-5/qserialport.html) but didn't get any idea about how to set low latency with QSerialPort
Q1: Please give a sample code in qt c++ with QSerialPort on how to set low latency.
Please don't suggest writing c code inside qt c++ this is not the right approach to solve this probleam.
QSerialPort *pUsbSerialPort;
pUsbSerialPort = new QSerialPort();
if(IS_VALID_OBJ(pUsbSerialPort))
{
pUsbSerialPort->setPortName(sSerialPort);
pUsbSerialPort->setBaudRate(BaudRate);
pUsbSerialPort->setDataBits(QSerialPort::Data8);
pUsbSerialPort->setParity(QSerialPort::NoParity);
pUsbSerialPort->setStopBits(QSerialPort::OneStop);
pUsbSerialPort->setFlowControl(QSerialPort::NoFlowControl);
if(pUsbSerialPort->open(QIODevice::ReadWrite))
{
connect(pUsbSerialPort, &QSerialPort::readyRead,this , &Laser::LaserReadyRead);
PRINT_INFO("Serial port successfully initialized" + sSerialPort);
bIsServeropen = true;
}
else
{
PRINT_INFO("Serial port initialization failed" + sSerialPort);
return;
}
}
else
{
PRINT_INFO(" Failed to assign memory to pUsbSerialPort" + sSerialPort);
return;
}
The descriptor is the Handle of the QSerialPort:
#include <sys/ioctl.h>
#include <linux/serial.h>
// ...
pUsbSerialPort->open(QIODevice::ReadOnly);
int fd = pUsbSerialPort->handle();
struct serial_struct kernel_serial_settings;
::ioctl(fd, TIOCGSERIAL, &kernel_serial_settings);
kernel_serial_settings.flags |= ASYNC_LOW_LATENCY;
::ioctl(fd, TIOCSSERIAL, &kernel_serial_settings);

How to pass the(serverClients[i].read() to byte array on ESP8266

In a sketch of Arduino there is an example WiFi Telnet To Serial with ESP8266. There is a piece of code that is used to receive data from a client:
//check clients for data
for(i = 0; i < MAX_SRV_CLIENTS; i++){
if (serverClients[i] && serverClients[i].connected()){
if(serverClients[i].available()){
//get data from the telnet client and push it to the UART
while(serverClients[i].available())
Serial.write(serverClients[i].read());
}
}
}
This data is sent to the console or the serial port, but I need to capture that data and store it in a byte array:
byte bufferMSGfromCliente[1024]
How to do it?
Its not c++ but you might be able to use a processing sketch. The syntax is very close to arduino so it should be familiar.
import processing.serial.*;
Serial myPort; // The serial port
void setup() {
// List all the available serial ports
printArray(Serial.list());
// Open the port you are using at the rate you want:
myPort = new Serial(this, Serial.list()[0], 9600);
}
void draw() {
int i = 0;
byte[] bufferMSGfromCliente = new byte[1024];
while (myPort.available() > 0) {
int inByte = myPort.read();
if(i > 1024)
{
i = 0;
}
bufferMSGfromCliente[i] = inByte;
i++;
}
}
Did you try to write it to another Serial port? Here you write it on the same Serial port as your Serial Monitor.
Best would be to store serverClients[i].read() into a Byte and then Serial.println(Byte) to see what the information is.
After that, ask yourself:
1. What Serial port will this information be sent to, what is the destination?
2. How can I confirm that information is sent succesfully and has the ability to be debugged.
Also, use Serial.flush() to make sure the Serial.write function completes. Eventhough it says ''flush'', the function in Arduino IDE waits for the Serial write function to be completed.

Using UDP transport data to Non-ROS computer over UDP

I want to transport the data from a ROS computer to the Non-ROS computer over the UDP.
To specify my works now :
I have a LiDAR(Sick TiM561) and I can launch it by the ROS computer successfully, and I can use the "Subscriber" to get the data of the LiDAR now using roscpp which is the c++ code in the ROS. Now,I need to transport the LiDAR's data to the microbox which is a MATLAB-based computer (this PC that you can build your program in it by MATLAB). But I'm not familiar in the c++ code, could someone can suggest me or direct me how to modify the code as below and add it into my roscpp code? Thanks in advance!
For the structure, the Micro box is the client, ROS PC is server.
This is the code of my roscpp:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include "sensor_msgs/LaserScan.h"
void laser_msg_Callback(const sensor_msgs::LaserScan::ConstPtr& scan)
{
for (int x=0;x< scan->ranges.size();x++)
{
ROS_INFO("I heard: [%f]", scan->ranges[x]);
}
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "sick_listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("scan", 811, laser_msg_Callback);
ros::spin();
return 0;
}
And this code maybe is what I need:
UdpClient client;
public IPAddress serverIP = IPAddress.Parse("140.124.35.1");
public Form1()
{
InitializeComponent();
client = new UdpClient();
}
public void SendData()
{
client.Connect(serverIP, 3000);
byte[] data = Encoding.ASCII.GetBytes("Hi, I'm new client.");
client.Send(data, data.Length);
DoListening();
}
public void DoListening()
{
IPEndPoint adress = new IPEndPoint(serverIP, 3000);
byte[] receivedbytes = client.Receive(ref adress);
string recieved = Encoding.ASCII.GetString(receivedbytes);
MessageBox.Show("Recieved: " + recieved);
}
private void button1_Click(object sender, EventArgs e)
{
SendData();
}
I'd think a possible way to go for you is to check out the rossereal implementations. I'm not familiar with development for microbox, probably none of the rosserial supported platforms works with it out of the box, but I thought it's what the rosserial for: to provide a ros-compliant protocol for communicating a ros server with non-ros devices. For that your ros-nodes' code don't even need to know if the client's publishers and/or subscribers are regular ros nodes running on a ros-running computer or they are mimicked by a rosserial client on a non-ros device.
When I used it, the efficiency of rosserial_windows was good for me. I was able to control a rather high resolution camera attached to a windows computer connected to a network with several ros running computers to transfer image messages from the camera with a satisfiable rate.
If none of the available rosserial implementations are compatible with microbox then you may want to check out the rosserial's "Adding Support for New Hardware" and the existing code for rosserial_embeddedlinux to implement the rosserial-like connection thru udp.