ESP32 Bluetooth Connection disconnects when finished with reading buffer - c

I'm programming an ESP32 to accept Bluetooth commands and send Bluetooth Data back to my phone using the Serial profile. For this I'm using the Arduino Espressif Bluetooth Serial Library. Whenever I send something to the ESP32 it processes it and then suddenly closes the Bluetooth Connection.
Up to know I have already tried various delays because I thought that maybe the processor was not keeping up with other stuff due to which it crashed.
However when monitoring using the Serial Connection via USB it still keeps sending status updates.
Other than that I could not really find a solution (also on the Internet).
As I'm pretty much a beginner I did not want to try and build my own Serial Bluetooth Library.
The ESP does not crash when sending it the data. It also keeps processing the data which was sent. I can see that as it sends the chars I sent it via Bluetooth using the Serial interface after having collected them.
The Connection cannot be rebuild after this incident, no matter how long I wait.
My Main function, containing the function call as well as a buffer to write the result to as I thought that maybe I was misusing that.
void loop() {
if (ESP_BT.available() > 0)
{
char *buffer = (char*) malloc(InputSize);
getCurrentMessage(ESP_BT, buffer, InputSize);
Serial.println(buffer);
strncpy(currentMessage, buffer, InputSize);
free(buffer);
}
if (millis() %2000 == 0){
Serial.println("Debug");
delay(1);
}
}
The function which gets called which should read the inputBuffer of the BluetoothSerial into my Buffer.
void getCurrentMessage(BluetoothSerial ESP_BT, char* receivedChars, int InputSize)
{
Serial.println("DEBUG: getCurrentMessageInit");
static byte ndx = 0;
char rc;
while (ESP_BT.available() > 0){
ESP_BT.println("DEBUG: Message Available");
Serial.println("DEBUG: Message Available");
rc = ESP_BT.read();
receivedChars[ndx] = rc;
ndx++;
delay(100);
if (ndx >= InputSize){
while(ESP_BT.available() > 0){
ESP_BT.read();
}
}
}
}
I would expect the Bluetooth Connection to keep working. This it does not do.
I also got the error Code "queue.c:1442 (xQueueGenericReceive)- assert failed!" When not using the delays and the ESP then rebooted.
This it does not do after I included the delays.

The problem was that I did not call my Bluetooth Object by reference.
Instead of giving my function the Bluetooth Object I should point to it:
void getCurrentMessage(BluetoothSerial* ESP_BT, char* receivedChars, int InputSize)
{
Serial.println("DEBUG: getCurrentMessageInit");
static byte ndx = 0;
char rc;
while (ESP_BT->available() > 0){
ESP_BT->println("DEBUG: Message Available");
Serial.println("DEBUG: Message Available");
rc = ESP_BT.read();
receivedChars[ndx] = rc;
ndx++;
delay(100);
if (ndx >= InputSize){
while(ESP_BT->available() > 0){
ESP_BT->read();
}
}
}
}

Related

Is there a size limit of write() for a socket fd?

I am writing a little web server which involves epoll and multithread. For small and short http/1.1 requests and responses, it works as expected. But when working with large size file downloads, it is always interrupted by the timer I devised. I expire the timers with a fixed timeout value, but I also have a if statement to check if the response was sent successfully.
static void
_expire_timers(list_t *timers, long timeout)
{
httpconn_t *conn;
int sockfd;
node_t *timer;
long cur_time;
long stamp;
timer = list_first(timers);
if (timer) {
cur_time = mstime();
do {
stamp = list_node_stamp(timer);
conn = (httpconn_t *)list_node_data(timer);
if ((cur_time - stamp >= timeout) && httpconn_close(conn)) {
sockfd = httpconn_sockfd(conn);
DEBSI("[CONN] socket closed, server disconnected", sockfd);
close(sockfd);
list_del(timers, stamp);
}
timer = list_next(timers);
} while (timer);
}
}
I realized that in a non-blocking environment, the write() function might be interrupted during the request-response communication. I wonder how long write() can hold or how much data write() can send, so I can tweek the timout setting in my code.
This is the code which involves write(),
void
http_rep_get(int clifd, void *cache, char *path, void *req)
{
httpmsg_t *rep;
int len_msg;
char *bytes;
rep = _get_rep_msg((list_t *)cache, path, req);
bytes = msg_create_rep(rep, &len_msg);
/* send msg */
DEBSI("[REP] Sending reply msg...", clifd);
write(clifd, bytes, len_msg);
/* send body */
DEBSI("[REP] Sending body...", clifd);
write(clifd, msg_body_start(rep), msg_body_len(rep));
free(bytes);
msg_destroy(rep, 0);
}
And the following is the epoll loop I use to process the incoming requests,
do {
nevents = epoll_wait(epfd, events, MAXEVENTS, HTTP_KEEPALIVE_TIME);
if (nevents == -1) perror("epoll_wait()");
/* expire the timers */
_expire_timers(timers, HTTP_KEEPALIVE_TIME);
/* loop through events */
for (i = 0; i < nevents; i++) {
conn = (httpconn_t *)events[i].data.ptr;
sockfd = httpconn_sockfd(conn);
/* error case */
if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) ||
(!(events[i].events & EPOLLIN))) {
perror("EPOLL ERR|HUP");
list_update(timers, conn, mstime());
break;
}
else if (sockfd == srvfd) {
_receive_conn(srvfd, epfd, cache, timers);
}
else {
/* client socket; read client data and process it */
thpool_add_task(taskpool, httpconn_task, conn);
}
}
} while (svc_running);
The http_rep_get() is executed by the threadpool handler httpconn_task(), HTTP_KEEPALIVE_TIME is the fixed timeout. The handler httpconn_task() will add a timer to the timers once a request arrives. Since the write() is executed in http_rep_get(), I think it might be interrupted by the timers. I guess I can change the way to write to the clients, but I need to make sure how much the write() can do.
If you are interested, you may browser my project to help me with this.
https://github.com/grassroot72/Maestro
Cheers,
Edward
Is there a size limit of write() for a socket fd?
It depends on what you mean by a limit.
As the comments explain, a write call may write fewer bytes than you ask it to. Furthermore, this is expected behavior if you perform a large write to a socket. However, there is no reliable way to determine (or predict) how many bytes will be written before you call write.
The correct way to deal with this is to check how many bytes were actually written each time, and use a loop for ensure that all bytes are written (or until you get a failure).

Passive Monitoring Serial Port in Windows using C

I am a noob at serial programming. I am trying to code a passive monitor in C that displays to screen whatever is written to or read from a COM port. Most of the code, that I have seen actually reads from or writes to the COM Port.
I have tried to read from a COM port that is transmitting and receive Modbus traffic but I get no readings. I am using a com0com serial port emulator. Only time the code works is if I actually read from the other port that the COM port is paired with.
I am trying to mimic the Serial Port Monitor application. So far it is not working. Kindly assist.
Thanks.
Below is the code for the COM read:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
void setupPort(HANDLE * handle, char * portName);
void readFromPort(HANDLE * handle);
int main()
{
HANDLE first_port;
char * first_port_name = "COM3";
setupPort(&first_port, first_port_name);
readFromPort(&first_port);
return 0;
}
void setupPort(HANDLE * handle, char * portName)
{
BOOL status;
*handle = CreateFile(portName, //port name
GENERIC_READ | GENERIC_WRITE, //Read/Write
0, // No Sharing
NULL, // No Security
OPEN_EXISTING,// Open existing port only
0, // Non Overlapped I/O
NULL); // Null for Comm Devices
if (handle == INVALID_HANDLE_VALUE)
{
printf("\n%s could not be opened\n", portName);
}
else
{
printf("\n%s successfully opened.\n", portName);
}
DCB dcbSerialParams = { 0 }; // Initializing DCB structure
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
status = GetCommState(*handle, &dcbSerialParams); //retreives the current settings
if (status == FALSE)
printf("\n Error! in GetCommState()");
dcbSerialParams.BaudRate = CBR_9600; // Setting BaudRate = 9600
dcbSerialParams.ByteSize = 8; // Setting ByteSize = 8
dcbSerialParams.StopBits = ONESTOPBIT; // Setting StopBits = 1
dcbSerialParams.Parity = NOPARITY; // Setting Parity = None
status = SetCommState(*handle, &dcbSerialParams); //Configuring the port according to settings in DCB
if (status == FALSE)
{
printf("\n Error! in Setting DCB Structure");
}
else //If Successful display the contents of the DCB Structure
{
printf("\n\n Setting DCB Structure Successful\n");
printf("\n Baudrate = %d", dcbSerialParams.BaudRate);
printf("\n ByteSize = %d", dcbSerialParams.ByteSize);
printf("\n StopBits = %d", dcbSerialParams.StopBits);
printf("\n Parity = %d", dcbSerialParams.Parity);
}
/*------------------------------------ Setting Timeouts --------------------------------------------------*/
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (SetCommTimeouts(*handle, &timeouts) == FALSE)
printf("\n\n Error! in Setting Time Outs");
else
printf("\n\n Setting Serial Port Timeouts Successful");
/*------------------------------------ Setting Receive Mask ----------------------------------------------*/
status = SetCommMask(*handle, EV_RXCHAR); //Configure Windows to Monitor the serial device for Character Reception
if (status == FALSE)
printf("\n\n Error! in Setting CommMask");
else
printf("\n\n Setting CommMask successful");
}
void readFromPort(HANDLE * handle)
{
BOOL status;
DWORD dwEventMask; // Event mask to trigger
char TempChar; // Temporary Character
char SerialBuffer[256]; // Buffer Containing Rxed Data
DWORD NoBytesRead; // Bytes read by ReadFile()
int i = 0;
/*------------------------------------ Setting WaitComm() Event ----------------------------------------*/
while(TRUE)
{
printf("\n\n Waiting for Data Reception");
status = TRUE; //Wait for the character to be received
/*-------------------------- Program will Wait here till a Character is received ------------------------*/
if (status == FALSE)
{
printf("\n Error! in Setting WaitCommEvent()");
}
else //If WaitCommEvent()==True Read the RXed data using ReadFile();
{
printf("\n\n Characters Received\n");
do
{
status = ReadFile(*handle, &TempChar, sizeof(TempChar), &NoBytesRead, NULL);
SerialBuffer[i] = TempChar;
i++;
}
while (NoBytesRead > 0);
/*------------Printing the RXed String to Console----------------------*/
printf("\n\n ");
int j =0;
for (j = 0; j < i-1; j++) // j < i-1 to remove the dupliated last character
{
printf("%02X", (unsigned int)(unsigned char)SerialBuffer[j]);
}
i=0;
}
//CloseHandle(*handle);//Closing the Serial Port
printf("\n +==========================================+\n");
}
}
Your code should work fine (EDIT: as long as you intend to use it together with com0com). As the busybee suggested in the comment above I think you are mixing up your ports or misunderstanding how com0com is supposed to work.
You might have two different scenarios:
1)You are using your Windows PC as a sniffer to monitor the Modbus transactions in between two other parties. For instance a PLC and remote Modbus sensor. In this scenario, you need two real hardware serial ports and a couple of virtual ports provided by com0com.
2)If something in your computer is acting as one of the parties in the Modbus transaction then you only need a hardware serial port and a couple of virtual ports.
Since you mention passive I guess you are on scenario number 1. If so you just need to choose your ports correctly. I wrote a complete example on how to do this very same, coincidentally for Modbus too using Termite and com0com, take a look here. You might also want to take a look to SerialPCAP, which in combination with Wireshark can even decode your Modbus messages.
If you prefer to reinvent the wheel, I guess you can just drop com0com and share the port as somebody else suggested in the comments. There are some interesting questions you might want to read if you decide to follow on this road, see here.
EDIT: You say you do want to reinvent the wheel. That's fine but I think you need to consider some things before you jump into writing code. I'm no expert serial port developer; much less on Windows, and even much less on recent Windows versions. But I did some research on this topic way back so I can give you my view:
-Most of us non-wheelreinventors would be more than happy to monitor our serial ports with the virtual serial port techniques explained above (I will repeat myself once more: for Modbus RTU traffic monitoring, look at Wireshark/SerialPCAP and you'll forget about anything else). My first impression was you wanted to do that (that's why you were talking about com0com). Reading your comments, I guess that's not good enough for you (I can understand that, I tend to prefer clean solutions to dirty tricks).
-Now, having that clear, is there something you can do? From userspace, I don't think you can share a serial port nowadays. The trick on the comment to your question that mentions dwShareMode might have worked back in the 90s, but I'm afraid it won't work anymore. For more details see here.
-If you go to driverland, you might have some chances. Read here. Other useful links: 1,2.
My conclusion is: there is no fix for your code, what you want to do is more involved than what you have.

configfs USB gadget write() call hangs

I'm trying to emulate a HID device (a gamepad for PS3) from a Raspberry Pi Zero using configfs with kernel v4.19.40 (pretty much the same process as described here). Following the instructions in gadget_hid.txt, I was able to get a working prototype that is able to send button presses from the Pi Zero to a USB host (a PS3). However, when some state changes on the USB host (i.e. a certain game is started), the program freezes. strace says that it is stuck on a call to write() to the configfs HID file (/dev/hidgX, in my case /dev/hidg0). How could the USB host block the device from sending data? If the USB host is not ready to receive, won't it just ignore the data sent?
In debugging I found this patch which seemed to be related but I saw no change after applying it.
Here is a small code sample (the full version can be found in gadget_hid.txt) that produces the same problem:
int main(int argc, const char *argv[]) {
int fd = 0;
char report[8];
int to_send = 8;
fd_set rfds;
if ((fd = open("/dev/hidg0", O_RDWR, 0666)) == -1) {
perror("/dev/hidg0");
return 3;
}
while (1) {
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
// Generate some data to send
fill_report(&report);
// Gets stuck here
write(fd, report, to_send);
}
close(fd);
return 0;
}

File transfer with arduino

My final goal is to send a 30 KB file over XBEE to another arduino. But for now i am just trying to duplicate a 4KB file on SD connected to first arduino. First i tried to send the data one byte by one byte.it worked and file duplicated successfully . but i have to have a buffer and then send data on 64 byte packets to XBEE so i should be able to read and write file in 64 byte packets. this is what i have done:
#include <SD.h>
#include <SPI.h>
void setup() {
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
if (!SD.begin(4)) {
Serial.println("begin failed");
return;
}
File file = SD.open("student.jpg",FILE_READ);
File endFile = SD.open("cop.jpg",FILE_WRITE);
Serial.flush();
char buf[64];
if(file) {
while (file.position() < file.size())
{
while (file.read(buf, sizeof(buf)) == sizeof(buf)) // read chunk of 64bytes
{
Serial.println(((float)file.position()/(float)file.size())*100);//progress %
endFile.write(buf); // Send to xbee via serial
delay(50);
}
}
file.close();
}
}
void loop() {
}
It successfully finish its progress until 100% but when i open the SD on laptop the file is created but it shown as 0 KB file.
whats the problem?
You are not telling .write what's the length of your buffer, so it will think it's a null-terminated string (which it isn't).
Plus, the inner loop appears to be not only unnecessary but even harmful because it would skip the last chunk if it's less than 64 bytes.
Check this out:
while(file.position() < file.size()) {
// The docs tell me this should be file.readBytes... but then I wonder why file.read even compiled for you?
// So if readBytes doesn't work, go back to "read".
int bytesRead = file.readBytes(buf, sizeof(buf));
Serial.println(((float)file.position()/(float)file.size())*100);//progress %
// We have to specify the length! Otherwise it will stop when encountering a null byte...
endFile.write(buf, bytesRead); // Send to xbee via serial
delay(50);
}

Serial communication buffer data out of order

I'm trying to implement a simple SLAM project with Arduino and C on Linux Mint 15.
The Arduino project is sending data to notebook via bluetooth (serial). The data is read by a C program. In Arduino serial, the data is shown correctly, but in the notebook, the received data is wrong. (In image, white is Arduino data. The terminal shows the 'received' data.)
I'm sending d080x096y099z035 (for example) and receiving 99z0356y0999z035 (out of order?).
So, I have some questions:
What can I do to make the read()command in C, read the data in the correct order and length? (order: d000x000y000z000, length = 16)
In the Arduino sending function, are there length differences using Serial.print(char buffer[]) and Serial.println(char buffer[])? (Like adding a '\n' or something else at the end of buffer?)
Should I use the delay() function in the Arduino code or in the C code?
In Arduino:
...
int buffer_size = 17;
char buffer[17];
//void setup()
void loop(){
//create the string resp = "d000x111y222z333"
...
resp.toCharArray(buffer, buffersize);
bluetooth.print(buffer);
delay(200);
}
In C program:
...
int fd = open("/dev/rfcomm4", O_RDONLY | O_NOCTTY | O_NDELAY);
printf("fd code %d\n", fd);
if (fd == -1)
{
gchar *msg = "open_port: Unable to open /dev/rfcomm4";
gtk_label_set_text(GTK_LABEL(label), msg);
perror("error: ");
}
char buffer[17];
int n;
printf("entering in loop...\n");
while (1)
{
n = read(fd, buffer, sizeof(buffer));
printf("%s\n", buffer);
}
Sorry I'm not an expert but just a few ideas you might check concering your questions:
to 1) I guess it might be a problem with encoding, as Python AFAIK expects files to be unicode. So try open (.... ,encoding='ascii') or whatever encoding you use
Please also pay attention that you might block the GTK mainthread, that causes heavy delays in your UI. So I recommend creating a own thread for reading the serial port and filling an internal buffer, that get's rendered by the GTK mainthread, if you send an update request:
http://www.pardon-sleeuwaegen.be/antoon/python/page0.html

Resources