Read MyCharNotify as a character instead of a HEX value - bluetooth-lowenergy

I am programming an STM32WB board using the following tutorial (https://www.youtube.com/watch?v=Zgw3wRpGSRQ&list=PLnMKNibPkDnG9JRe2fbOOpVpWY7E4WbJ-&index=18&ab_channel=STMicroelectronics)
I am able to send a hex value to the phone using the ST BLE Toolbox, however I would like to send a char to start, end goal would be to send a string. how could I go about displaying the hex value as a char?
Would CHAR_PROP_BROADCAST or CHAR_PROP_READ be more appropriate for this? I could not find any tutorials on this unfortunately.
enter image description here
followed this tutorial.
https://www.youtube.com/watch?v=Zgw3wRpGSRQ&list=PLnMKNibPkDnG9JRe2fbOOpVpWY7E4WbJ-&index=18&ab_channel=STMicroelectronics
the tutorial only sends one hex number, to send more you can change the "Value length" on CubeMX,
UpdateCharData[n] = some_data;

You need to understand how a character is stored in the microcontroller/PC memory. Read about ASCII table. For example:
#include <stdio.h>
int main() {
unsigned num = 0x41; // two byte variable
printf("%0x\n", num);// output: 41
printf("%d\n", num);// output: 65
printf("%c\n", num);// output: A
//because ASCII code 65 it's 'A' character
return 0;
}
As you can see, the same data are interpreted differently.
If you want to convert a integer to a character, just do the following ->
int a = 65;
char c = (char) a;
Or you can use itoa() function to convert the integer to a string.

Related

How do char* pointers work in C? How are they stored? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I'm confused in understanding how pointers to char work.
What is the difference between using char *s = "car"; and s = "India";?
And what about char **s?
How will these be stored?
String literals like "car" and "India" are stored in arrays of character type such that they are available over the lifetime of the program. Under most circumstances, when an expression of array type appears in the code, it is implicitly converted ("decays") to an expression of pointer type, and the value of the expression is the address of the first element of the array.
So suppose our "car" string is stored as an array of char starting at address 0x8000:
+---+
0x8000: |'c'|
+---+
0x8001: |'a'|
+---+
0x8002: |'r'|
+---+
0x8003: | 0 |
+---+
When you write
char *s = "car";
the expression "car" is converted from type "4-element array of char" to "pointer to char", and the value of the expression is the address of the first element - 0x8000. So, that 0x8000 address is written to s1:
+--------+
s: | 0x8000 |
+--------+
Later on, when you write
s = "India";
the address of the first element of the array that stores "India" is written to s.
Here's a practical example. I've written a small utility that prints out the addresses and contents of multiple variables. Here's the code:
#include <stdio.h>
#include "dumper.h"
int main( void )
{
char *s = "car";
char *names[] = { "s", "\"car\"", "\"India\"" };
void *addrs[] = { &s, "car", "India" };
size_t sizes[] = { sizeof s, sizeof "car", sizeof "India" };
dumper( names, addrs, sizes, 3, stdout );
s = "India";
dumper( names, addrs, sizes, 3, stdout );
return 0;
}
Here's how it looks on my system - first we look at the strings themselves:
"car" 0x104f2ff71 63 61 72 00 car.
"India" 0x104f2ff88 49 6e 64 69 Indi
0x104f2ff8c 61 00 00 00 a...
The string "car" is stored starting at address 0x104f2ff71, and the string "India" is stored starting at address 0x104f2ff88.
Now we look at s:
s 0x7ffeeacd0a10 71 ff f2 04 q...
0x7ffeeacd0a14 01 00 00 00 ....
s is stored starting at address 0x7ffeeacd0a10, and it's contents are the address of the string literal "car". Since x86 is little-endian, multi-byte values have to be read right-to-left.
After we write
s = "India";
our memory looks like this:
s 0x7ffeeacd0a10 88 ff f2 04 ....
0x7ffeeacd0a14 01 00 00 00 ....
Now s is storing the address of the array containing "India".
what is char **s? How it will be stored?
You can have pointers to pointers:
char **s2 = &s;
s2 just stores the address of the variable s. Pointers to pointers come up when you're dealing with arrays of pointers, or when you're passing a pointer to a function and you want the function to be able to write a new pointer value.
Different pointer types may have different representations - the only guarantees are:
char * and void * have the same representation and alignment;
pointers to qualified types have the same representation and alignment as pointers to the corresponding unqualified type (i.e., int * and const int * are stored the same way);
All struct pointer types have the same representation and alignment;
All union pointer types have the same representation and alignment;
So it's possible for a char ** object to be stored differently from a char * object. On most modern systems like x86, all pointer types are stored the same way, but there are oddballs out there where that's not the case.
In a declaration, the * operator is simply part of the type; it is not dereferencing anything. We're storing the value to s, not to what s points to.
1st case
char* s = "car";
In this case you declaring a char pointer and at the same time assigning it a string literal, in other words, making it point to the beginning of the string literal.
2nd case
char* s;
//some code;
s = "india";
This is the same as first, the difference is that you are first declaring the pointer, and later assigning it the string literal.
3rd case
char** s;
Is a pointer to a pointer to char or double pointer to char, take this sample:
#include <stdio.h>
int main()
{
char* ptr1 = "abc";
char** ptr2;
ptr2 = &ptr1;
printf("%s", *ptr2);
}
Thhis means that ptr2 is pointing to ptr1 pointer to char (so it's a pointer to pointer to char), through indirection ptr2 can be used to access the beggining of the string pointed by ptr1.
Output
abc
Where the string literal is stored varies deppending on the platform, this answer has some details, as does the link I provided for string literal documentation.
String literals are not modifiable (and in fact may be placed in read-only memory such as .rodata). If a program attempts to modify the static array formed by a string literal, the behavior is undefined.
char *s = "car"; and s = "India"; are the same thing except for the fact that char *s = "car"; defines (creates) a new s variable which didn't exist before, while s = "India"; reuses the existing s. In both cases s is a variable pointer to the first character of the string which is created in a non writable part of the data space. i.e. You can change s to another string (e.g. set it to "India" or increment it), but you cannot change the pointed memory (e.g. you can't do s[0]='e').
char **p; just defines a pointer to a pointer (to a string), so you could write p=&s; and if you then wrote *p="Terminator"; s would now point to a new "terminator" string.
There is another case that you should compare with the first two, which is char s[20] = "Hello"; In this case, s is not a variable but a constant pointer to a variable 20 bytes space, in which you have stored "Hello\0". In this case, you CANNOT point s to another string as in s="turlututu";, but you can change the content of the 20 bytes space, providing you do not overflow outside this space and keep the content properly terminated with a '\0' (not terminating your string is not a syntax error, but it is a dangerous practice).

Different result between Serial.print and Serial.printf in ESP32

I want to print my string variable using printf method:
id = 6415F1BF713C
Serial.printf("id: %s\n\n", id);
Serial.print(id);
The result that I got was:
id: ⸮⸮⸮?
6415F1BF713C
is there any thing that's wrong?
Thanks.
Update :
//get device id
String getDeviceID() {
uint64_t chipid = ESP.getEfuseMac(); // The chip ID is essentially its MAC address(length: 6 bytes).
uint16_t chip = (uint16_t)(chipid >> 32);
char devID[40];
snprintf(devID, 40, "%04X%08X", chip, (uint32_t)chipid);
return devID;
}
String id = getDeviceID();
Serial.printf("id: %s\n\n", id);
Serial.print(id);
You didn't offer enough code to properly debug this, but I'm guessing what you mean is
String id = "6415F1BF713C";
Serial.printf("id: %s\n\n", id);
Serial.print(id);
The %s format in the printf() method expects to take a C/C++ char *, not a String. When you passed it a String, it printed the memory address of the String object - four characters which would appear as garbage, as you saw.
C and C++ use char * (pointers to characters) and char [] (arrays of characters) to represent strings. These are different from the Arduino String class, and people often confuse them.
To use the printf() method with a String you need to get its C string pointer, like this:
Serial.printf("id: %s\n\n", id.c_str());
The reason that this:
Serial.print(id);
works is that the print() method has a form that specifically expects to take a String object as an argument.

DXL convert hex string to char

I am parsing an OLE object in doors.
The representation mixes ascii characters and objdata (hex value of ASCII chars) numbers:
{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang2057{\fonttbl{\f0\fnil\fcharset0 Tahoma;}}
{\*\generator Riched20 10.0.17134}\viewkind4\uc1
\pard\f0\fs20{\object\objemb{\*\objclass Package}\objw4620\objh810{\*\objdata
01050000
02000000
08000000
5061636b61676500
00000000
00000000
d5c40000
02005f30306132636633392d323936612d346263612d396539342d383039343437336133343035
I am able to detect where my file starts using regex and objdata field.
Since my file extension is *.ole, I am going to search for the ".ole" string at the beginning of the objdata field (the long line starting with 0200) and backwards search for the 0200 hex string that.
My question is:
How can I convert from hex representation back to ascii a string in DXL?
Are there functions to perform this task? a simple cast would be enough? Or shall I write my own function to perform this task?
I wasn't able to find any hint on the ref manual, also some keyword or "RTFM page" would be gladly appreciated.
K.R.
The only support that I know of that might help you are char charOf(int asciiCode) and int(char ch).
I have not been able to spot ".OLE" (2E 4F 4C 45) or ".ole" (2E 6F 6C 65) in the line you posted, but assuming that these characters (or a combination of upper and lower characters) exist, one approach would be to walk through the objdata character for character (using a Buffer and an integer variable which loops over every character, something like int i = 0; int high; int low; Char c; while (...) { high = int(buf[i]); low = int(buf[i+1]); c = calculate_character_from_integers(high, low); i+=2; if c = ... then ...} and with this approach, whenever you have a new line, have another integer variable that points to the beginning of the line, and when you have something like "current character is 45 or 65 and the character before is 4C or 6C and the one before is 4F or 6F and the one before is 2E, then concatenate the file name from the start of the line." Not sure whether there are any scripts or snippets out in the wild that help here, perhaps you find something in the IBM DeveloperWorks DXL forums (hurry, they will cease to exist in two weeks)
I had to write a function from scratch based on Mike's reply and an old thread on IBM forums by user Mathias Mamsch.
I have many doubts on the buffer handling, buts it works fine for my purposes:
This functions performs the Hex2ascii translations of the string.
string Hex2Ascii(string &stringIn){
Buffer buf = create
string hexval
while(length(stringIn)>0){
hexval = iterateOnString(stringIn)
//print(charOf(intOfHex))
//print hexval
//print charOf(intOfHex(hexval))
buf += charOf(intOfHex(hexval))
}
return stringOf buf
}
It invokes other two functions; iterateOnString returns two chars to form a byte to be converted and removes it from the original string:
string iterateOnString(string &stringIn){
string StringOut
int x = length(stringIn)
if(x<2){
return ""
}
StringOut = stringIn[0:1]
stringIn = stringIn[2:]
return StringOut
}
then intOfHex converts the hex to int value, then the result is passed to charOf()
int intOfHex( string s ) {
if( "0x" == s[0:1] ) {
return intOf( realOf( eval_ "return_ (" s ") \"\"" ) )
} else {
return intOf( realOf( eval_ "return_ (0x" s ") \"\"" ) )
}
}
Any hint, optimization proposal or critic is welcome.
K.R.

How can a 1 byte int conversion of a QByteArray fail?

So here is the thing, I'm receiving 1 byte from Bluetooth transmission. When using QDebug I get this message:
The array with error has "\x06"
The line that fails is this:
bool ok = true;
int v = value.toInt(&ok,0);
Because ok has false. But I'm trying to wrap my head around the fact that, How can the conversion fail in the first place if the data represented in that byte (as a sequence of zeros and ones) will always have a valid integer representation. (one byte can always be represented as a int between -127 and 128). So I'm left with the question, how can the conversion fail?
Reading the documentation does not provide many clues as it does not say how the byte array will be interpreted.
QByteArray::toInt converts a string representation in the default C locale to an integer. That means to successfully convert the value in your example, your byte array must contain the string "0x06", which consists of 4 bytes.
To convert a single byte to an int, just extract it:
int i = value[0];
Type promotion will widen the char to an int

Qt Check QString to see if it is a valid hex value

I'm working with Qt on an existing project. I'm trying to send a string over a serial cable to a thermostat to send it a command. I need to make sure the string only contains 0-9, a-f, and is no more or less than 6 characters long. I was trying to use QString.contains, but I'm am currently stuck. Any help would be appreciated.
You have two options:
Use QRegExp
Use the QRegExp class to create a regular expression that finds what you're looking for. In your case, something like the following might do the trick:
QRegExp hexMatcher("^[0-9A-F]{6}$", Qt::CaseInsensitive);
if (hexMatcher.exactMatch(someString))
{
// Found hex string of length 6.
}
Update
Qt 5 users should consider using QRegularExpression instead of QRegExp:
QRegularExpression hexMatcher("^[0-9A-F]{6}$",
QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatch match = hexMatcher.match(someString);
if (match.hasMatch())
{
// Found hex string of length 6.
}
Use QString Only
Check the length of the string and then check to see that you can convert it to an integer successfully (using a base 16 conversion):
bool conversionOk = false;
int value = myString.toInt(&conversionOk, 16);
if (conversionOk && myString.length() == 6)
{
// Found hex string of length 6.
}

Resources