STM32 LwIP Delay in netconn_write - tcp

I implemented a small tcp client on STM32F7 with freeRtos and LwIP and netconn api.
I can establish a connection with the server and send some data to the network. My problem is a huge delay between the command and when I can actually see the ethernet data on the network (seconds..). Seems like the data is buffered before sending it in one go.
I'm aware of the TCP_NODELAY flag and I set it (with tcp_nagle_disable(conn->pcb.tcp)), but it doesn't make a difference. Ethernet payload is around 50 bytes, TCP_MSS is 1460.
The netconn api sees the data as sent (netbuffer structure is updated, tcp_write() and tcp_output() are called with no errors), but I have the impression that after low_level_output() is called and the data buffer is passed to the DMA (with HAL_ETH_TransmitFrame()) it stays there until something happened and 3 or 4 ethernet packets are sent in a go.
I don't want to wait forever for a reply and I set a timeout on netconn_recv(), enabling LWIP_SO_RCVTIMEO and calling netconn_set_recvtimeout(). I set up the server to answer with an echo but even with a timeout of 500ms I loose most of the replies.
Here some code:
conn = netconn_new(NETCONN_TCP);
if (conn != NULL)
{
err = netconn_bind(conn, IP_ADDR_ANY, 0);
if (err == ERR_OK)
{
connect_error = netconn_connect(conn, &dest_addr, SRV_PORT);
if (connect_error == ERR_OK)
{
// set a timeout to avoid waiting forever
netconn_set_recvtimeout(conn, 500);
//TCP_NODELAY
tcp_nagle_disable(conn->pcb.tcp);
osSemaphoreRelease(tcpsem); // signal tcpsend
if (netconn_recv(conn, &buf) == ERR_OK)
{
//Parse message
do
{
// do stuff..
}
while (netbuf_next(buf) >0);
netbuf_delete(buf);
} else {
// TIMEOUT
}
}
/* Close connection */
netconn_close(conn);
}
netconn_delete(conn);
}
vTaskDelete(tcpsendTaskHandle);
vTaskDelete(NULL);
tcpsend
void tcpsend (void *data, size_t len)
{
// send the data to the connected connection
netconn_write(conn, data, len, NETCONN_NOFLAG);
}
tcpsend
static void tcpsend_thread (void *arg)
{
for (;;)
{
// semaphore must be taken before accessing the tcpsend function
osSemaphoreAcquire(tcpsem, portMAX_DELAY);
// build msg
if (ethPrepare(&ethMsg) == ERR_OK)
{
// send the data to the server
tcpsend(&ethMsg, ethMsg.ncSize);
}
vTaskDelay(100);
}
}

Found the problem:
I forgot to set also the TxBuffer memory as not cacheable bufferable..
Once I added the memory configuration on the loader script and in ethernetif.c also for the tx buffer (I had it only for he rx) I could see the ethernet packets right away.

Related

esp32 BLE client application - connect to device name

I've hacked apart the ESP32 BLE arduino sketches to do what I want. The server side is easy. Please see code below:
if (con == 0){
digitalWrite(LED, LOW);
}
if (con == 1){
digitalWrite(LED, HIGH);
delay(1000);
digitalWrite(LED, LOW);
delay(1000);
}
if (deviceConnected) {
pCharacteristic->setValue((uint8_t*)&value, 4);
pCharacteristic->notify();
value++;
delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
con = 1;
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
Serial.println("start advertising");
oldDeviceConnected = deviceConnected;
con = 0;
}
This works exactly how I want. It simply sits idle doing nothing, when a device connects to the BLE server then it will flash an LED.
No problems there, even though I suspect my code isn't 'that pretty.
What i'm having trouble doing however is creating an ESP32 client to connect to the BLE device.
The client has the name set as
BLEDevice::init("BOX_A1");
The example code seems to want UID for both the service and characteristic. Is there any way to just connect to the short advertised name? No data is being shared, it's just simply acting as a beacon to identify a box when connected to.
Thanks
Andrew
You can't connect to a device using the advertised name, not directly at least.
If you want to use the advertised name you have to scan for all BLE devices around you and select the one matching your name. The BLE scan example shows you how this is done. The code scans for a scanTime of 5 seconds, waits 2 seconds and starts scanning again:
void loop() {
// put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
Serial.print("Devices found: ");
Serial.println(foundDevices.getCount());
Serial.println("Scan done!");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
delay(2000);
}
The example just prints the amount of found devices, you want to search through them and look for the correct name. The BLEScan returns an object of type BLEScanResults. You can access the found devices using getDevice with an index. Something like this might work to print the names of all found devices:
BLEAdvertisedDevice device;
for (int i = 0; i < foundDevices.getCount(); ++i) {
device = foundDevices.getDevice(i);
Serial.println(device.getName().c_str());
}
Now you can compare the names and work with the correct device.
To my understanding,
You want the client to connect to the server with given advertised name.
After connection is success, server turns on led.
You do have notification service running on server, but your client isn't interested.
Below is the client code which only connects to server with name "BOX_A1".
First Set our callback function that checks if device name matches when scanner discovers the devices:
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks
{
void onResult(BLEAdvertisedDevice advertisedDevice)
{
if (advertisedDevice.getName() == "BOX_A1")
{
advertisedDevice.getScan()->stop(); //Scan can be stopped, we found what we are looking for
foundDevice = new BLEAdvertisedDevice(advertisedDevice);
deviceFound = true;
Serial.print("Device found: ");
Serial.println(advertisedDevice.toString().c_str());
}
}
};
Use the BLEAdvertisedDevice object i.e. foundDevice object to connect to this server.
BLEClient* connectToServer(BLEAdvertisedDevice* device) {
BLEClient* pClient = BLEDevice::createClient();
if (pClient->connect(device)){ // Connect to the remote BLE Server.
Serial.println(" - Connected to server");
return pClient;
}
else{
Serial.println("Failed to Connect to device");
return NULL;
}
}
Use following line to call this connect function, it return the client object which can be used to disconnect from server.
if(deviceFound==true){
BLEClient* myDevice = connectToServer(device);
if(myDevice!=NULL){
Serial.print("Connected to the BLE Server: ");
Serial.println(device->getName().c_str());//print name of server
//DO SOME STUFF
//disconnect the device
myDevice->disconnect();
Serial.println("Device disconnected.");
}
}
This was the client side.
At server side to set the connection status flag use the following:
//Setup callbacks onConnect and onDisconnect
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
Serial.println("Client Connected");
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
Serial.println("Client Disconnected");
deviceConnected = false;
}
};

How to have multiple connections to a Arduino webserver?

I have an Arduino running a webserver that is receiving data every 2 seconds. I CAN connect to its IP address by typing into the browser. I also created a web app that pulls data from this IP address every time new data comes in. The issue is that I need to access the IP address with the web app while another program is accessing it. Currently only one program can access at a time. I would like to have a Python script pulling data from the IP address constantly and still allow the web app to connect to view it live. How can I achieve this?
Arduino code with a lot of other stuff removed...
WiFiServer server(80); //server socket
WiFiClient client_1 = server.available();
void setup() {
Serial.begin(9600);
enable_WiFi(); // function to enable wifi
connect_WiFi(); // function to connect to wifi
server.begin();
}
void loop() {
client_1 = server.available();
if (client_1) {
printWEB(client_1); // this posts the data as text to the web IP address
}
delay(2000);
}
void printWEB(WiFiClient client) {
if (client) { // if you get a client,
Serial.println("new client"); // print a message out the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Access-Control-Allow-Origin: *");
client.println("Access-Control-Allow-Methods: GET");
client.println("Content-type: application/json");
client.println();
moistureReading = analogRead(A1);
tmpString = dataPosted;
tmpString.replace("%moistureData%", String(moistureReading) );
client.flush();
client.print( tmpString );
// The HTTP response ends with another blank line:
client.println();
// break out of the while loop:
break;
}
else { // if you got a newline, then clear currentLine:
currentLine = "";
}
}
else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// close the connection:
client.stop();
Serial.println("client disconnected");
}
}
The solution here was to take the delay off of the arduino code by simply removing the sleep function (Turns out this is not required). I then had to create a delay in all of my fetching codes. The React.js needed a sleep function and the python script needed a sleep function. This allows the two platforms to collect data from the same IP Address simultaneously. I needed a minimum of about 5-10 second sleep for both apps in order for this to work. I initially tried 1 second for each and it didn't work.
The webserver as programmed on your Arduino can only serve one request from a client at a time, not two simultaneously.
If two clients are doing requests in a loop, and are doing that fairly quickly, there will be situations where one client is blocking access for the other.
Try making the two clients much slower, i.e. retrieve information at a much lower frequency, just to see if the Arduino can keep up then.
Also, make the webserver on the Arduino as fast as possible, and call it as often as possible.
So, don't put delay()s in the loop, or anywhere else, and try to do the analogread() and the return string preparation outside of the webserver code and in the loop() every x seconds using millis(), so as little time as possible is spent in the webserver code.

ESP32 TCP client

I want to set up TCP server on windows and TCP client on ESP32. Main idea is to send String to ESP32 change it and send it back to server, but I'm really new with all of this stuff and got stuck on setting up TCP client on ESP32. Examples or references would be really helpful.
int create_ipv4_socket()
{
struct addrinfo hints;
struct addrinfo *res;
struct in_addr *addr;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
int err = getaddrinfo(UDP_IPV4_ADDR, TCP_PORT, &hints, &res);
if(err != 0 || res == NULL) {
printf("DNS lookup failed err=%d res=%p\n", err, res);
return -1;
}
/* Code to print the resolved IP.
Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */
addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
printf("DNS lookup succeeded. IP=%s\n", inet_ntoa(*addr));
l_sock = socket(res->ai_family, res->ai_socktype, 0);
if(l_sock < 0) {
printf("... Failed to allocate socket.\n");
freeaddrinfo(res);
return -1;
}
struct timeval to;
to.tv_sec = 2;
to.tv_usec = 0;
setsockopt(l_sock,SOL_SOCKET,SO_SNDTIMEO,&to,sizeof(to));
if(connect(l_sock, res->ai_addr, res->ai_addrlen) != 0) {
printf("... socket connect failed errno=%d\n", errno);
close(l_sock);
freeaddrinfo(res);
return -1;
}
printf("... connected\n");
freeaddrinfo(res);
// All set, socket is configured for sending and receiving
return l_sock;
}
From this forum https://www.esp32.com/viewtopic.php?t=5965
How do you communicate with your ESP? if you communicate through UART, just send him AT command he need by writing on the UART port:
"AT+CIPSTATUS\r\n"
and then wait for his response.
If you are connected to your ESP32 directly with your computer, just use putty and directly send AT command to it.
A non exhaustive list of AT's command can be found here:
https://www.espressif.com/sites/default/files/documentation/esp32_at_instruction_set_and_examples_en.pdf

QUdpSocket broadcast till answered

I'm implementing a simple UDP server-client app. The main logic is: server starts sending broadcast -> clients responses -> server stops sending broadcast and performs some work.
I'm stuck with broadcasting loop:
sUDP::sUDP(QObject *parent) : QObject(parent)
{
serverSocket = new QUdpSocket(this);
serverIp = new QHostAddress("192.168.1.2");
pickUpThePhone = false;
if(serverSocket->state() != serverSocket->BoundState){
if (!serverSocket->bind(*serverIp, 4321)) {
qFatal("Error binding server");
}
}
connect(serverSocket,SIGNAL(readyRead()), SLOT(readSocket()));
while(!pickUpThePhone){
QByteArray Data;
Data.append("server");
serverSocket->writeDatagram(Data, Data.size(), QHostAddress("192.168.255.255"), 1234);
}
}
actually signal readyRead() never emitted and so broadcasting never stops:
void sUDP::readSocket()
{
qDebug() << "read";
while(serverSocket->hasPendingDatagrams()){
QByteArray buffer;
buffer.resize(serverSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
serverSocket->readDatagram(buffer.data(), buffer.size(), &sender, &senderPort);
if(strcmp(buffer.data(),"stop") == 0){
pickUpThePhone = true;
}
}
}
The client works as it should - it responses to the datagram with "server" with "stop" message, but it looks like the while-loop is never interrupted.
Any help will be useful, thank you.
It doesn't work because your program is busy sending broadcasts. Your while loop will not let the Qt event-loop perform its work and therefore your program can't do anything else.
You're also sending broadcasts constantly, your computer will do nothing else than spam the network.
The solution to both problems is a timer. Create a repeating timer that once every second (ore more or less) sends out the broadcast. Stop or pause the timer when you get a response.

Microchip TCPIP keep alive not works

i'm trying to build a server with a PIC24F.
This is a piece of code i'm isuing:
switch(TCPServerState) {
case SM_HOME:
// Allocate a socket for this server to listen and accept connections on
socket.Socket = TCPOpen(0, TCP_OPEN_SERVER, SERVER_PORT, TCP_PURPOSE_GENERIC_TCP_SERVER);
if(socket.Socket != INVALID_SOCKET) {
TCPServerState = SM_LISTENING;
}
break;
case SM_LISTENING:
// See if anyone is connected to us
//if(TCPIsConnected(socket.Socket)) {
if(!TCPWasReset(socket.Socket)){
if(socket.Connected == 0) {
socket.Connected = 1;
printf("Socket is CONNECTED: %d\n", socket.Socket);
}
uint16_t avaible = TCPIsGetReady(socket.Socket);
// Some stuff
}
else if(socket.Connected == 1){
printf("Socket RESET: %d\n", socket.Socket);
TCPServerState = SM_CLOSING;
}
break;
case SM_CLOSING:
// Close the socket connection.
socket.Connected = 0;
TCPClose(socket.Socket);
TCPServerState = SM_HOME;
printf("Socket is CLOSED: %d\n", socket.Socket);
break;
}
All works fine if i close my client socket properly, but if i disconnect ethernet cable i am not able to detect disconnection and my code does not close the socket because TCPWasReset still FALSE(or TCPIsConnected still TRUE).
So how can i detect the disconnection of network cable(without add a software keep_alive implementation) ?
Thanks
Check a few items:
Call TickInit(); before StackInit();
Select the correct TIMER1 clock source for your application - internal clock or external clock (T1CON.TCS)
Otherwise, just use a debugger on the keepalive logic in TCP.C, which should default to 10 seconds in the latest Microchip Library for Applications TCP/IP stack 5.42.08.

Resources