I'm trying to use IOConnectCallStructMethod so send data in the input field to talk to my driver in DriverKit (iPadOS)
In this particular method I send an input and I expect getting an output. But ignore the output for now.
In my case IOConnectCallStructMethod is called with Swift:
var input:[UInt8] = Array(repeating: 0, count: 200)
for i in 0..<200 {
if (i % 2 == 0) {
input[i] = UInt8(i)
}
}
let arraySize:Int = input.count;
var outputSize = MemoryLayout<UInt8>.size * arraySize
var output:[UInt8] = Array(repeating: 0, count: arraySize)
let inputSize = MemoryLayout<UInt8>.size * input.count
let ret = IOConnectCallStructMethod(connection, Selector.mySelector.rawValue, &input, inputSize, &output, &outputSize)
This is the dispatch in the external method:
[ExternalMethodType_MyMethodRequest] =
{
.function = (IOUserClientMethodFunction) &Client::StaticHandleRequest,
.checkCompletionExists = false,
.checkScalarInputCount = 0,
.checkStructureInputSize = 200,
.checkScalarOutputCount = 0,
.checkStructureOutputSize = 200,
},
The ExternalMethod gets called and when I try to access to the input data with this:
char* input = nullptr;
size_t length = 0;
if (arguments->structureInput != nullptr)
{
input = (char*)arguments->structureInput->getBytesNoCopy();
length = arguments->structureInput->getLength();
Log("Input: %s", input);
Log("length: %zu", length);
}
If I check the variable input with a breakpoint the value is always "" and the length is 200
I tried adding the input in a struct, same result.
I tried using IOConnectCallMethod, same result.
The only alternative I can do it's making the input data extra big to force the system to use the descriptor instead of the OSData, but the input in this case would be always much smaller, so not sure if the best way to go.
Interestingly if I print the output of input with:
#define Log(fmt, ...) os_log(OS_LOG_DEFAULT, "NullDriver - " fmt "\n", ##__VA_ARGS__)
It will give me private:
NullDriver - Input: <private>
Maybe the data is actually there but I can't see it?
Further to discussion in comments:
There is no issue here with the way the data is being sent from app to dext, but rather with the way it is being logged/printed: a format string of %s is not a suitable way of printing the contents of a byte array. That treats it as a nul-terminated string (UTF-8, usually). It looks like you're sending arbitrary bytes, not a human readable string. In particular, your first byte is 0, which is how an empty C string is represented, and hence your empty output.
A format string such as "%02x %02x %02x %02x" with arguments input[0], input[1], input[2], input[3] would be more appropriate for printing the first 4 bytes; you'll have to do this in a loop, possibly writing to a buffer using snprintf before sending it to the system log, to print the entire array.
Separately: to fix the <private> issue when using %s, use %{public}s. (but read the os_log documentation on the privacy ramifications of this before you ship it)
I think the data is there but I can't see it.
If I do:
char realInput[200];
memcpy(realInput, input, 200);
When I loop printing in the debugger: realInput[i] I get my precious data
Related
I am doing this IoT based project on displaying data to connected display( I've used the MAX7219 module, in this case) with the help of nodeMCU. The idea here is that the string which is stored in my firebase database is to be display on the led display.
I've had no trouble in getting the value from the database to my nodeMCU but there is this little problem with converting that string to char array since the code i am using( Max72xx_Message_serial, which was available as an example with the max72xx library) has used char array but i can only fetch the stored data in string format. I've modified that code so as to connect with firebase but the main issue is to convert the string fetched from the database to char array.
I tried toCharArray() but it still shows conversion error.
void readfromfirebase(void)
{
static uint8_t putIndex = 0;
int n=1;
while (Firebase.available())
{
newMessage[putIndex] = (char)Firebase.getString("Submit Message"); // this line produces the error
if ((newMessage[putIndex] == '\n') || (putIndex >= BUF_SIZE-3)) // end of message character or full buffer
{
// put in a message separator and end the string
newMessage[putIndex++] = ' ';
newMessage[putIndex] = '\0';
// restart the index for next filling spree and flag we have a message waiting
putIndex = 0;
newMessageAvailable = true;
}
else if (newMessage[putIndex] != '\r')
// Just save the next char in next location
{putIndex++;}
n++;
}
}
I think you are confusing the types
getString returns a String object wich can be converted to a char[] using the methods of the String class.
I assume your newMessage is of type char[] or char*.
Then I would advise you to go for the String.c_str() method, because it returns a C style null-terminated string, meaning a char*.
See https://www.arduino.cc/reference/en/language/variables/data-types/string/functions/c_str/ for reference.
It also sets the last character of the string to 0. So methods like strlen, strcmp etc will work.
! be carefull not to modify the array returned by c_str(), if you want to modify it you chould copy the char[] or use string.toCharArray(buf, len).
Your Code might then look like the following.
String msg = Firebase.getString("Submit Message");
newMessage = msg.c_str();
// rest of your code
If newMessage is a buffer storing multiple messages, meaning char* newMessage[3].
String msg = Firebase.getString("Submit Message");
newMessage[putIndex] = msg.c_str();
// rest of your code
Be careful, because you are storing multiple characters in an array, so use strcmp to compare these arrays!
If you are new to C I would recommend reading.
https://www.cprogramming.com/tutorial/c/lesson9.html
https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/ (as pointed out by #gre_gor)
I am trying to send out Mat image by TCP. Firstly the Mat has been transferred into uchar and then into char format. The whole image in char format will be send out buffer by buffer whose size is 1024 byte. The following is my code.
Mat decodeImg = imdecode(Mat(bufferFrame), 1);
uchar *transferImg = decodeImg.data;
char* charImg = (char*) transferImg;
int length = strlen(charImg);
int offset = 0;
while (true)
{
bzero(bufferSend, BUFFER_SIZE);
if (offset + BUFFER_SIZE <= length)
{
for (int i = 0; i < BUFFER_SIZE; i++)
{
bufferSend[i] = charImg[i + offset];
}
// memcpy(charImg+offset, bufferSend,BUFFER_SIZE);
if (send(sockfd, bufferSend, sizeof(bufferSend), 0) < 0)
{
printf("Send FIle Failed,total length is%d,failed offset is%d\n",
length,
offset);
break;
}
}
else
{
for (int i = 0; i < length - offset; i++)
{
bufferSend[i] = charImg[i + offset];
}
if (send(sockfd, bufferSend, sizeof(bufferSend), 0) < 0)
{
printf("Send FIle Failed,total length is%d,failed offset is%d\n",
length,
offset);
break;
}
break;
}
offset += BUFFER_SIZE;
}
The output of the code shows : send file failed, total length is 251035, failed offset is 182272.
I am really appreciated on your help. Thank you in advance!
Pulling out the crystal ball here. This might be OP's problem, but if it isn't, this is certainly a problem that needs to be addressed.
Mat decodeImg = imdecode(Mat(bufferFrame), 1);
uchar *transferImg = decodeImg.data;
Get data. Not a bad idea if that's what you need to send.
char* charImg = (char*) transferImg;
Take the array of bytes from above and treat it as an array of characters.
int length = strlen(charImg);
And Boom. Matrix data is not ascii formated data, a string, so it should not be treated like a string.
strlen counts data until it reaches a null character, a character with the numerical value 0, which does not exist in the normal alpha numeric world and thus can be used as a canary value to signal the end of a string. The count is the number of characters before the first null character in the string.
In this case we don't have a string. We have a blob of binary numbers, any one of which could bee 0. There could be a null value anywhere. Could be right at the beginning. Could be a hundred bytes in. There might not be a null value in the until long after all of the valid image data has been read.
Anyway, strlen will almost certainly return the wrong value. Too few bytes and the receiver doesn't get all of the image data and I have no idea what it does. That code's not available to us. It probably gets upset and discards the result. Maybe it crashes. There's no way to know. If there is too much information, we also don't know what happens. Maybe it processes the file happily and ignores the extra crap that's sent. Maybe it crashes.
But what if it closes the TCP/IP connection when it has enough bytes? That leaves the sender trying to write a handful of unsent and unwanted bytes into a closed socket. send will fail and set the error code to socket closed.
Solution:
Get the right size of the data.
What I'm reading from the openCV documentation is inside a Mat is Mat::elemSize which will give you the size of each item in the matrix and Mat::size which returns a Size object containing the rows and columns. Multiply rows * columns * elemSize and you should have the number of bytes to send.
EDIT
This looks to be a better way to get the size.
I have my code which writes data into a text file. My problem is for some odd reason, it writes in Japanese! Obviously I cannot read Japanese, but how can I write it into a regular text format. Pretend the data[i].name and password have a char [10] value. And they already write in the file, the problem is the language it writes in.
// variables
FILE *streaming;
const int amount = 5;
profile data[5];
if (!(streaming = fopen("Profiles.txt", "r" )))
{
for (int i = 0; i < amount; i++)
{
data[i].name[0] = 0;
data[i].password[0] = 0;
}
streaming = fopen( "Profiles.txt", "wb" );
fwrite (data, sizeof(data), 1 , streaming);
fclose (streaming);
}
data[i].name[0] = 0; sets the first element of what I presume to be a character array to 0. It does absolutely nothing with the rest of the character array. Whatever random crap was in the rest of the character array is still there.
fwrite (data, sizeof(data), 1 , streaming); will mindlessly write the everything inside the array of profile structures, including all of the random crap after the first element of the name and password arrays inside each profile.
To completely clear the profiles, assuming there is nothing in profile that will respond badly to memset, try
memset(data, 0, sizeof(data));
Don't bother asking your doctor. Post your profile structure and we'll know for sure if memset is right for you.
We are trying to initialize a character array but it we get an error saying that we can not as we can not mix integers and chars, but we dont have have integers initialized.
thanks in advance
void setup()
{
Serial.begin(9600); //Set the serial monitor.
lcd.begin(16, 2); //Set the LCD
}
char line1 [5] = {0};
char line2 [] = {0};
void loop()
{
if (Serial.available() > 0) { //If the serial monitor is open it will read a value.
line1 = Serial.read();
delay(10);
Serial.print(line1);
lcd.print(line1);
}
}
I am not an arduino guy, but I did stay at a Holiday Inn last night with access to Google :)
http://arduino.cc/en/Serial/Read
Serial.read() returns a single byte (as an int). You're trying to assign that to a char array.
You can't do that. You can assign something to a specific element in an array:
line1[0] = 'c';
for example, but you can't assign to an array itself.
It seems like you're looking for Serial.readBytes()
http://arduino.cc/en/Serial/ReadBytes
Which would look like:
Serial.readBytes(line1, 5);
in your case where 5 is the length of your buffer (array). This would read (at most) 5 bytes into your line1 buffer.
Edit to add: That being said, it appears arduino's "C-Like" language is very much like C in that it expects "Strings" (char arrays) to be null terminated when passing them to Serial.print(). The advice above doesn't do that and in fact would cause problems.
What you would need to do is read up to one byte less than the length of your array, and then null terminate the "string" using the number of bytes actually read which is what bytesRead() returns to you (arrays are zero indexed):
int numBytesRead = 0;
...
numBytesRead = Serial.readBytes(line1, 4);
line1[numBytesRead] = '\0';
Serial.print(line1);
Option B is to do as I originally mentioned, but loop and print one byte at a time from your line1 array using the index:
int numBytesRead = 0;
numBytesRead = Serial.readBytes(line1, 5);
int i;
for (i = 0; i < numBytesRead; i++) {
Serial.print(line1[i]);
}
According to the docs Serial.print() automagically knows to send a single byte/char when that's all that's passed to it.
My goal is create an app client server, written in C++.
When the server read an input from the client, should process the string and give an output.
Basically, I have a simply echo server that send the same message.
But if the user types a special string (like "quit"), the program have to do something else.
My problem is that this one dont happend, because the comparison between strings is not working... I dunno why!
Here a simple code:
while(1) {
int num = recv(client,buffer,BUFSIZE,0);
if (num < 1) break;
send(client, ">> ", 3, 0);
send(client, buffer, num, 0);
char hello[6] ="hello";
if(strcmp(hello,buffer)==0) {
send(client, "hello dude! ", 12, 0);
}
buffer[num] = '\0';
if (buffer[num-1] == '\n')
buffer[num-1] = '\0';
std::cout << buffer;
strcpy(buffer, "");
}
Why the comparison is not working?
I have tried many solutions...but all failed :(
Your data in buf may not be NULL-terminated, because buf contains random data if not initialized. You only know the content of the first num bytes. Therefore you also have to check how much data you've received before comparing the strings:
const char hello[6] ="hello";
size_t hello_sz = sizeof hello - 1;
if(num == hello_sz && memcmp(hello, buffer, hello_sz) == 0) { ...
As a side note, this protocol will be fragile unless you delimit your messages, so in the event of fragmented reads (receive "hel" on first read, "lo" on the second) you can tell where one message starts and another one ends.
strcmp requires null terminated strings. The buffer you read to might have non-null characters after the received message.
Either right before the read do:
ZeroMemory(buffer, BUFSIZE); //or your compiler defined equivalent
Or right after the read
buffer[num] = '\0';
This will ensure that there is a terminating null at the end of the received message and the comparison should work.
A string is defined to be an array of chars upto and including the terminating \0 byte. Initially your buffer contains arbitrary bytes, and is not even guaranteed to contain a string. You have to set buffer[num] = '\0' to make it a string.
That of course means that recv should not read sizeof buffer bytes but one byte less.