So I'm trying to adapt a CPP library for ESP-IDF/Arduino BLE control into ESP-IDF v5.0 but I'm getting some errors regarding Semaphore from FreeRTOS when waiting. This is most likerly due to some APIs changing between ESP-IDF 3.0 to 5.0 but since Im just learning FreeRTOS I got a bit lost with the error.
Some logs from the program before the crash:
I (2376) BLEClient: >> connect(a6:c0:82:01:17:72)
I (2386) : 119660
I (2386) BLEDevice: add conn_id: 0, GATT role: client
I (2386) FreeRTOS: Semaphore taking: name: RegEvt (0x3ffdb0d4), owner: <N/A> for connect
I (2396) FreeRTOS: Semaphore taken: name: RegEvt (0x3ffdb0d4), owner: connect
I (2406) : 119628
assert failed: xQueueGenericSend queue.c:837 (pxQueue->pcHead != ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle())
The traceback:
Backtrace: 0x40081d1e:0x3ffdacb0 0x40090699:0x3ffdacd0 0x4009687d:0x3ffdacf0 0x40090e62:0x3ffdae10 0x400dc597:0x3ffdae50 0x400d94d9:0x3ffdae90 0x400da145:0x3ffdaf10 0x400e0226:0x3ffdaf60 0x400e0922:0x3ffdaf80 0x400f73fd:0x3ffdafc0 0x400f95ab:0x3ffdafe0
0x40081d1e: panic_abort at C:/Espressif/frameworks/esp-idf-v5.0/components/esp_system/panic.c:412
0x40090699: esp_system_abort at C:/Espressif/frameworks/esp-idf-v5.0/components/esp_system/esp_system.c:135
0x4009687d: __assert_func at C:/Espressif/frameworks/esp-idf-v5.0/components/newlib/assert.c:78
0x40090e62: xQueueGenericSend at C:/Espressif/frameworks/esp-idf-v5.0/components/freertos/FreeRTOS-Kernel/queue.c:828 (discriminator 2)
0x400dc597: FreeRTOS::Semaphore::give() at C:/Users/amng8/Sync/PROJECTS/PillCPP/components/BLE/FreeRTOS.cpp:107
0x400d94d9: BLEClient::gattClientEventHandler(esp_gattc_cb_event_t, unsigned char, esp_ble_gattc_cb_param_t*) at C:/Users/amng8/Sync/PROJECTS/PillCPP/components/BLE/BLEClient.cpp:233
0x400da145: BLEDevice::gattClientEventHandler(esp_gattc_cb_event_t, unsigned char, esp_ble_gattc_cb_param_t*) at C:/Users/amng8/Sync/PROJECTS/PillCPP/components/BLE/BLEDevice.cpp:163
0x400e0226: btc_gattc_cb_to_app at C:/Espressif/frameworks/esp-idf-v5.0/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c:24
0x400e0922: btc_gattc_cb_handler at C:/Espressif/frameworks/esp-idf-v5.0/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c:1002
0x400f73fd: btc_thread_handler at C:/Espressif/frameworks/esp-idf-v5.0/components/bt/common/btc/core/btc_task.c:207
ELF file SHA256: b8542ed11e07aba6
0x400f95ab: osi_thread_run at C:/Espressif/frameworks/esp-idf-v5.0/components/bt/common/osi/thread.c:165
The code in question is from the file BLEClient.cpp function bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type):
/**
* #brief Connect to the partner (BLE Server).
* #param [in] address The address of the partner.
* #return True on success.
*/
bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type) {
ESP_LOGI(LOG_TAG, ">> connect(%s)", address.toString().c_str());
// We need the connection handle that we get from registering the application. We register the app
// and then block on its completion. When the event has arrived, we will have the handle.
m_appId = BLEDevice::m_appId++;
BLEDevice::addPeerDevice(this, true, m_appId);
m_semaphoreRegEvt.take("connect");
// clearServices(); // we dont need to delete services since every client is unique?
esp_err_t errRc = ::esp_ble_gattc_app_register(m_appId);
if (errRc != ESP_OK) {
ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return false;
}
m_semaphoreRegEvt.wait("connect");
m_peerAddress = address;
// Perform the open connection request against the target BLE Server.
m_semaphoreOpenEvt.take("connect");
errRc = ::esp_ble_gattc_open(
m_gattc_if,
*getPeerAddress().getNative(), // address
type, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature.
1 // direct connection <-- maybe needs to be changed in case of direct indirect connection???
);
if (errRc != ESP_OK) {
ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return false;
}
uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete.
ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK);
return rc == ESP_GATT_OK;
} // connect
and what seems to be triggering the crash is the .wait("connect") command.
Once again I'm new to FreeRTOS and Semaphore and not sure what's useful or not to get help so if you need any more information please let me know.
Related
I have tried to set up a server with a specific IP address and Port number by freeDiameter libraries. In other side, there is a client that sends a string message via freeDiameter libraries successfully; I checked it out in Wireshark.
So, here is the problem; when I start listening from the server side, it has no problem until a client sends a connection request; if you look at the below code, "fd_cnx_serv_accept(listener)" is listening to a socket named "listener" which is bind to a specific IP address and Port number.
uint8_t * rcv_buf;
size_t rcv_size;
struct cnxctx * listener_side = NULL;
int ret;
listener_side = fd_cnx_serv_accept(listener);
ret = fd_cnx_start_clear(listener_side, 0);
if(ret != 0){
std::cout<<side<<" is unable to accept connections."<<std::endl;
return -1;
}
ret = fd_cnx_receive(listener_side, NULL, &rcv_buf, &rcv_size);
if(ret == 0){
std::cout<<"Message received."<<std::endl;
listener_side = NULL;
free(rcv_buf);
}
The program works fine until it gets a client request; when "fd_cnx_serv_accept" accept the client side, the program tries to receive the message by "fd_cnx_receive"; but the program stops and shows the error "Segmentation fault (core dumped)". Then, I test the program by gdb and it shows that it is something wrong with the line 309 of "hook.c". Here is the image of the error which is shown by gdb.
Error Screenshot
transcript
multi-thre Thread 0x7fffff72a77 In: fd_hook_call
[New Thread 0x7fffff72a7700 (LWP 1612914)
Thread 2 "server" received signal SIGSEGEV, Segmentation Fault
[Switching to Thread 0x7fffff72a7700 (LWP 1612914)
--Type <RET> for more, q to quit, c to continue without paging --c
0x00007ffff7f2cefc in fd_hook_call (type=HOOD_DATA_RECEIVED, msg=0x0, peer=0x0, other=0x7ffff72a6be0, pmdl=0x7ffff0000ba8 at /root/projects/start_cnx_free/third-party/libfdcore/hooks.c:309
It is good to mention that I am trying to write a c++ program.
How can I solve this problem ?
I solved this problem myself. I forgot to use fd_hooks_init() at the begining of my program to initialize necessary libraries and objects.
Here is the modified code:
/* Initialize the library -- must come first since it initializes the debug facility */
int ret = fd_libproto_init();
if (ret != 0) {
fprintf(stderr, "Unable to initialize libfdproto: %s\n", strerror(ret));
return ret;
}
ret = fd_hooks_init();
if(ret != 0){
std::cout<<"Unable to initialize the hooks."<<std::endl;
return ret;
}
/* Initialize the config with default values */
memset(&g_conf, 0, sizeof(struct fd_config));
fd_g_config = &g_conf;
ret = fd_conf_init();
if (ret != 0) {
printf("Unable to initialize the config with default values.");
return ret;
}
/* Add definitions of the base protocol */
ret = fd_dict_base_protocol(fd_g_config->cnf_dict);
if (ret != 0) {
printf("Unable to add definitions of the base protocol.");
return ret;
}
I'm quite new in the opcua's world and I'm trying to monitor a server variable with a client in C++.
I'm on the 1.2.2 version of opcua
I have a boolean variable in the server at the node (1,6070) and when I run the following code I receive the LOG :
[2021-08-03 15:27:47.442 (UTC+0200)] info/session Connection 5 | SecureChannel 2 | Session ns=1;g=913a21de-f467-5bc9-ed9e-29b27b470490 | Subscription 2 | Created the Subscription with a publishing interval of 500.00 ms
But I've never reach the function 'handler_events_datachange' in which I only put an output for now. (I'm sure that the value in the node 6070 changed btw)
Thanks for helping!
int main(void) {
signal(SIGINT, stopHandler); /* catches ctrl-c */
UA_Client *client = UA_Client_new();
UA_ClientConfig *cc = UA_Client_getConfig(client);
UA_ClientConfig_setDefault(cc);
UA_Client_connect(client, "opc.tcp://localhost");
UA_MonitoredItemCreateResult result;
UA_CreateSubscriptionResponse response; // warning memory leak
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL);
UA_Int32 subId = response.subscriptionId;
if(response.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
{
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND , "subscription succed");
} else {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND , "subscription UNsucced");
}
UA_MonitoredItemCreateRequest monRequest = UA_MonitoredItemCreateRequest_default(UA_NODEID_NUMERIC(1, 6070));
result = UA_Client_MonitoredItems_createDataChange(client, subId, UA_TIMESTAMPSTORETURN_BOTH, monRequest, NULL, handler_events_datachange, NULL);
while(running) {
}
}
I finally found the error !
The problem comme from the fact no means of handling asynchronous
events automatically is provided. However, some synchronous function
calls will trigger handling, but to ensure this happens a client
should periodically call UA_Client_run_iterate explicitly.
So the solution is to add UA_Client_run_iterate(client,100) in the while().
I didn't fully understand what the timeout was about but I'll complete this answer if I can
I have this running on an esp8266 but I suspect it's a c/c++ issue, most likely with me not understanding something basic.
I'm trying to connect to a mqtt server set by a value read from a json config file. Debug of the connect() call doesn't see the value I've assigned to the variable. The connect call is failing as the server name to connect to is blank, but only inside the connect() call.
What's the proper way to do this type of call?
Code snippet:
Adafruit_MQTT_Client *mqtt;
const char* _MqttServer = NULL;
setup {
// ...
// read file, parse json, gets the correct value
_MqttServer = jObject["mqttserver"];
// port/username/key are setup as #define earlier
mqtt = new Adafruit_MQTT_Client(&client, _MqttServer, MQTT_SERVERPORT, MQTT_USERNAME, MQTT_KEY);
while ((ret = mqtt->connect()) != 0) { // connect will return 0 for connected
Serial.println(mqtt->connectErrorString(ret));
Serial.println(_MqttServer); // prints correct value
mqtt->disconnect();
delay(5000); // wait 5 seconds
}
// ...
}
The error printed is from
bool Adafruit_MQTT_Client::connectServer() {
// Grab server name from flash and copy to buffer for name resolution.
memset(buffer, 0, sizeof(buffer));
strcpy((char *)buffer, servername);
Serial.print(F("Connecting to: ")); Serial.print((char *)buffer);
The error I get is
Connection failed!
Connecting to:
Connect result: 0
Retrying MQTT connection in 5 seconds...
If I change the _MqttServer to a #define and remove it from assignment to the json it connects normally. I've done that in the meantime to get the device working. The issue I'm seeing is the passing or the assignment of the variable that is not being seen by the called function.
I use the following code to verify that a serial port name is valid on the computer:
typedef std::pair<StrAsc const, bool> port_pair_type;
typedef std::list<port_pair_type> port_pairs_type;
port_pairs_type pairs;
StrBin config_buffer;
config_buffer.fill(0,sizeof(COMMCONFIG));
while(!pairs.empty())
{
port_pair_type pair(pairs.front());
pairs.pop_front();
if(!pair.second)
{
// we need to get the default configuration for the port. This may
// require some fudging on the buffer size. That is why two calls
// are being made.
uint4 config_size = config_buffer.length();
StrUni temp(pair.first);
COMMCONFIG *config(reinterpret_cast<COMMCONFIG *>(config_buffer.getContents_writable()));
config->dwSize = sizeof(COMMCONFIG);
rcd = GetDefaultCommConfigW(
temp.c_str(), config, &config_size);
if(!rcd && config_buffer.length() < config_size)
{
config_buffer.fill(0, config_size);
config = reinterpret_cast<COMMCONFIG *>(config_buffer.getContents_writable());
config->dwSize = sizeof(COMMCONFIG);
rcd = GetDefaultCommConfigW(
temp.c_str(),
reinterpret_cast<COMMCONFIG *>(config_buffer.getContents_writable()),
&config_size);
}
// if the call succeeded, we can go ahead and look at the
// configuration structure.
if(rcd)
{
COMMCONFIG const *config = reinterpret_cast<COMMCONFIG const *>(
config_buffer.getContents());
if(config->dwProviderSubType == PST_RS232)
port_names.push_back(pair.first);
}
else
{
OsException error("GetDefaultCommConfig Failed");
trace("\"%s\"", error.what());
}
}
else
port_names.push_back(pair.first);
}
On windows 10, when trying to confirm a serial port that uses usbser.sys, the call to GetDefaultCommConfig() is failing and the error code returned by GetLastError() is 87 (invalid parameter). As I am aware, the usbser.sys driver has been rewritten on windows 10 and I suspect that this is a problem with that driver. Does anyone else have an idea of what might be going wrong?
This had been a bug in usbser.sys and was fixed with the Windows 10 Update KB3124262 from 27.01.2016.
The Microsoft employee explained:
The COM port name in the HKLM\HARDWARE\DEVICEMAP\SERIALCOMM registry is not NULL terminated.
Related discussion on MSDN
Because of Windows 10's update policies this issue should not appear in the future anymore.
When you call GetDefaultCommConfigW the second time you probably need to config->dwSize to the new size the structure. Eg:
config->dwSize = config_size;
I am trying to implement an authentication system using C++/QtTcpSocket for a personal project (A Multiplayer Chess Game).
My friend suggested a method for verifying a user but I wanted to ask if there was an easier or better way. Coming from a Python background and mostly doing this project to develop a deeper understanding of C++.
I will post the method my friend suggested and ask for maybe a better solution.
He built it in a kind of pseudo code fashion. The server is mostly built, I am now hoping to implement Authentication
*cheers
void process_packet(PACKET *pkt)
{
switch(pkt->PacketID)
{
case 0: // let's say packet id 0 is the logon packet; packet contents are username and password
{
//let's say packet size is 101 bytes; packet id was already received, so get the other 100 bytes
unsigned char BUFFER[101] = {0}; // i always add an extra byte to the end of the buffer to allow for off-by-one errors ^_^
int result = recv_packet(pkt->cSocket, 100, BUFFER);
if(result <= 0)
return; // connection error; no packet data was received
unsigned char *UserName = BUFFER+0; //+0 is not neccessary, but the username starts at the beginning. just getting the point across.
unsigned char *PassWord = BUFFER+50;
//side note: if we did "unsigned long *blah = BUFFER+4" or something, we would have to make sure the byte order is right. network byte order is BIG ENDIAN
// WINDOWS byte order is LITTLE ENDIAN
result = QueryDatabase("SELECT username, password FROM chess_players WHERE username = '%s'", FILTER_INVALID_CHARS(UserName));
// check result
unsigned char ServerResponse[2] = {0};
if(result['password'] == PassWord)
{
ServerResponse[0] = 1; // packet id will be 1. the next byte can be 1 or 0 to indicate logon success or failure.
ServerResponse[1] = true; // so packet 0x0101 mean logon success, packet 0x0100 means logon failure
send_packet(pkt->cSocket, ServerResponse, 2);
} else {
ServerResponse[0] = 1;
ServerResponse[1] = false;
send_packet(pkt->cSocket, ServerResponse, 2);
}
}
break;
default:
{
// received an unknown packet id; send a packet to the client that indicates an error_status_t
unsigned char *ServerResponse[2] = {0};
ServerResponse[0] = 2; // packet id 2 means server error
ServerResponse[1] = 0; // error code 0 means 'unknown packet id'
send_packet(pkt_cSocket, ServerResponse, 2);
}
break;
}
delete pkt; // must delete pkt, was created with 'new' in get_client_packets()
}
This seems rather C-stylish and not like the Qt way of doing things.
There is no general answer to your question but my suggestions are the following:
Listen to the newConnection() signal of the QTcpServer. Your handler has to call the nextPendingConnection() to get the next client waiting in the queue. The first thing you will do is probably your user authentication.
Once authenticated, you keep the QTcpSocket in your list of active connections.
Take a look at e.g. the fortune client/server examples how to actually write/read packets.
You might also want to look into the stream operators << to serialize your objects. This is much easier and less error prone than the low-level method you posted. ALso, QDataStream will take care of host and network byte orders automatically.
If you have followed the fortune client/server examples, you should have a QTcpServer (Rfserver) with a QThread subclass (Rfdevice, its instance variable is called thread in the following code) that contains a QTcpSocket (listenSocket).
Having said that, in your server class, listen for incoming connections, my setup looks like this:
void Rfserver::incomingConnection(int socketDescriptor){
if(thread){ //if thread exists, there is probably still an open connection
if(thread->listenSocket){//if thread exists and the listenSocket is filled, there is definately an open connection
if(thread->listenSocket->state() == QAbstractSocket::UnconnectedState){
//but alas, it could just be in the unconnected state, if so kill it.
this->disconnect();
thread->terminate();
thread=0;
connected=false;
}//otherwise, do nothing, because the software is happily connected to a device
}
}
if(!thread){ //if no thread exists, we are by no means connected
thread = new rfdevice(socketDescriptor, this); //set up a new thread
//this first connection communicates the string from your socket to the server parent...use it if you want.
connect( thread, SIGNAL(RemoteButton(QString)),this,SLOT(remoteButton(QString)),Qt::BlockingQueuedConnection);
connect( thread, SIGNAL(error(QTcpSocket::SocketError)),this,SLOT(tcpError(QTcpSocket::SocketError)),Qt::AutoConnection);
connect( thread, SIGNAL(finished()), this, SLOT(threadZero())); //I have a threadZero function that deletes all the data then schedules the socket for deletion.
thread->start();
connected=true;
QString *welcome = new QString("Enter your password:\r\n");
echoCommand(welcome); //this is a function you will implement that sends the welcome message to the pending device.
}
}
Okay, so now, when a device tries to connect to the server the device is presented with "Enter your password:\r\n". Your device will respond to this with a password and username perhaps. But the Qt side of things would look like this:
/*
FUNCTION:read
this is a polling runloop that listens for data as long as the socket is connected or connecting. If a
write is ever scheduled, it will be called from this runloop..
*/
void Rfdevice::read(void){
while((listenSocket->state() == QAbstractSocket::ConnectedState) || (listenSocket->state() == QAbstractSocket::ConnectingState)){
//if there is data available to send write it to the socket
if(dataToSend) this->write();
if(listenSocket->waitForReadyRead(50)) readBytes();
//wait for 50ms for data from the device
//if there is ever data available to be read, read it.
}
}
Your device responds with a username/password in the format username---password\r\n. Then the socket does this:
/*
FUNCTION:readBytes
this is like a callback function because it only gets called when there is data available for read.
It basically converts the data to a string.
*/
void Rfdevice::readBytes(void){
QByteArray newData;
newData = listenSocket->readAll();
QString *recieved = new QString(newData);
QStringList userAndPass = recieved.split("---");//this is your delimiter
QString username = userAndPass.at(0);
QString password = userAndPass.at(1);
//NOW, check the username and password vs your SQL or wherever it's saved.
}
The pseudo-code is pretty complete on the particulars. Hopefully you can put it all together! Let me know if you need more code.