I'm implement task handler for GUI task by using LVGL and FreeRTOS in ESP32
Whenever I get queue message, I read message as 0 in handler.
When I send message, It is fine.
LOG:
I (105690) wifi_app: WIFI_APP_MSG_STA_DISCONNECTED: USER REQUESTED DISCONNECTION
I (105700) LVGL: SEND MESSAGE
I (105700) LVGL: SENDING msgID 4 // when send
I (105710) LVGL: RECEVING msgID: 0 // read message in handler
I (105710) LVGL: Get NTU from sensor
I (105720) http_server: HTTP_MSG_WIFI_USER_DISCONNECT
Handler: Parsing message Id by using switch statement
static void cmd_parsing(void *arg)
{
lvgl_queue_message_t lvglMsg;
for (;;)
{
if (xQueueReceive(lvgl_queue_handle, &lvglMsg, portMAX_DELAY))
{
ESP_LOGI(TAG, "Recieving msgID: %d", lvglMsg.msgID);
switch (lvglMsg.msgID)
{
case LVGL_MSG_SEND_NTU:
ESP_LOGI(TAG, "Get NTU from sensor");
// lv_label_set_text(label_ntu, evt.string1);
lv_label_set_text_fmt(label_ntu, "%0.3f NTU", lvglMsg.valueFloat);
break;
case LVGL_MSG_WIFI_CONNECT_INIT:
ESP_LOGI(TAG, "Create spinner for connecting Wi-Fi");
create_spinner();
xSemaphoreGive(xGuiSemaphore);
break;
case LVGL_MSG_WIFI_USER_DISCONNECT:
ESP_LOGI(TAG, "WIFI_USER_DISCONNECT");
lv_obj_clean(settings_tab);
create_list_settings();
break;
default:
break;
}
}
}
Send: Sending message Id in WIFI task
BaseType_t lvgl_send_message(lvgl_message_e msgID)
{
ESP_LOGI(TAG, "SEND MESSAGE");
ESP_LOGI(TAG, "Sending msgID %d", msgID);
lvgl_queue_message_t lvglMsg;
lvglMsg.msgID = msgID;
return xQueueSend(lvgl_queue_handle, &lvglMsg, portMAX_DELAY);
}
Related
I am trying to create an UDP application where the packet generation rate and packet send interval can be controlled separately.
My code is in https://github.com/11187162/udpApp
With the above code, I am not getting the expected outcome and getting the following runtime error:
scheduleAt(): Message (omnetpp::cMessage)sendTimer is currently
scheduled, use cancelEvent() before rescheduling -- in module
(inet::UdpOwnApp) SensorNetworkShowcaseA.sensor3.app[0] (id=176), at
t=0.058384669093s, event #10
The code for handleMessageWhenUp() is given below.
void UdpOwnApp::handleMessageWhenUp(cMessage *msg)
{
if (msg->isSelfMessage()) {
ASSERT(msg == selfMsg);
switch (selfMsg->getKind()) {
case START:
processStart();
break;
case GENERATE:
generatePacket();
break;
case SEND:
processSend();
break;
case STOP:
processStop();
break;
default:
throw cRuntimeError("Invalid kind %d in self message", (int)selfMsg->getKind());
}
}
else
socket.processMessage(msg);
}
Would anyone please help me?
Thank you
You have written that "generation rate and packet send interval can be controlled separately" but you use the same self-message for control generation of packets as well as for sending of the packets. When a self-message is scheduled it cannot be scheduled again.
Consider adding a new self-message for generation of packets.
By the way: numGenerate is set to zero and it is never changed.
EDIT
Assuming that selfMsg1 is used for generating packets only the following code may be used:
void UdpOwnApp::handleMessageWhenUp(cMessage *msg) {
if (msg->isSelfMessage()) {
if (msg == selfMsg) {
switch (selfMsg->getKind()) {
case START:
processStart();
break;
case SEND:
processSend();
break;
case STOP:
processStop();
break;
default:
throw cRuntimeError("Invalid kind %d in self message", (int)selfMsg->getKind());
}
} else if (msg == selfMsg1) {
if (selfMsg1->getKind() == GENERATE) {
generatePacket();
}
}
}
else
socket.processMessage(msg);
}
And in initialize() you should create an instance of selfMsg1.
I am unable to use cancellation tokens to stop a TCP Listener. The first code extract is an example where I can successfully stop a test while loop in a method from another class. So I don't understand why I cant apply this similar logic to the TCP Listener Class. Spent many days reading convoluted answers on this topic and cannot find a suitable solution.
My software application requires that the TCP Listener must give the user the ability to stop it from the server end, not the client. If a user wants to re-configure the port number for this listener then they would currently have to shutdown the software in order for Windows to close the underlying socket, this is no good as would affect the other services running in my app.
This first extract of code is just an example where I am able to stop a while loop from running, this works OK but is not that relevant other than the faat I would expect this to work for my TCP Listener:
public void Cancel(CancellationToken cancelToken) // EXAMPLE WHICH IS WORKING
{
Task.Run(async () =>
{
while (!cancelToken.IsCancellationRequested)
{
await Task.Delay(500);
log.Info("Test Message!");
}
}, cancelToken);
}
Now below is the actual TCP Listener code I am struggling with
public void TcpServerIN(string inboundEncodingType, string inboundIpAddress, string inboundLocalPortNumber, CancellationToken cancelToken)
{
TcpListener listener = null;
Task.Run(() =>
{
while (!cancelToken.IsCancellationRequested)
{
try
{
IPAddress localAddr = IPAddress.Parse(inboundIpAddress);
int port = int.Parse(inboundLocalPortNumber);
listener = new TcpListener(localAddr, port);
// Start listening for client requests.
listener.Start();
log.Info("TcpListenerIN listener started");
// Buffer for reading data
Byte[] bytes = new Byte[1024];
String data = null;
// Enter the listening loop.
while (true)
{
// Perform a blocking call to accept client requests.
TcpClient client = listener.AcceptTcpClient();
// Once each client has connected, start a new task with included parameters.
var task = Task.Run(() =>
{
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
data = null;
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Select Encoding format set by string inboundEncodingType parameter.
if (inboundEncodingType == "UTF8") { data = Encoding.UTF8.GetString(bytes, 0, i); }
if (inboundEncodingType == "ASCII") { data = Encoding.ASCII.GetString(bytes, 0, i); }
// Use this if you want to echo each message directly back to TCP Client
//stream.Write(msg, 0, msg.Length);
// If any TCP Clients are connected then pass the appended string through
// the rules engine for processing, if not don't send.
if ((listConnectedClients != null) && (listConnectedClients.Any()))
{
// Pass the appended message string through the SSSCRulesEngine
SendMessageToAllClients(data);
}
}
// When the remote client disconnetcs, close/release the socket on the TCP Server.
client.Close();
});
}
}
catch (SocketException ex)
{
log.Error(ex);
}
finally
{
// If statement is required to prevent an en exception thrown caused by the user
// entering an invalid IP Address or Port number.
if (listener != null)
{
// Stop listening for new clients.
listener.Stop();
}
}
}
MessageBox.Show("CancellationRequested");
log.Info("TCP Server IN CancellationRequested");
}, cancelToken);
}
Interesting to see that no one had come back with any solutions, admittedly it took me a long while to figure out a solution. The key to stopping the TCP Listener when using a synchronous blocking mode like the example below is to register the Cancellation Token with the TCP Listener itself, as well the TCP Client that may have already been connected at the time the Cancellation Token was fired. (see comments that are marked as IMPORTANT)
The example code may differ slightly in your own environment and I have extracted some code bloat that is unique to my project, but you'll get the idea in what we're doing here. In my project this TCP Server is started as a background service using NET Core 5.0 IHosted Services. My code below was adapted from the notes on MS Docs: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcplistener?view=net-5.0
The main difference between the MS Docs and my example below is I wanted to allow multiple TCP Clients to connect hence the reason why I start up a new inner Task each time a new TCP Client connects.
/// <summary>
/// </summary>
/// <param name="server"></param>
/// <param name="port"></param>
/// <param name="logger"></param>
/// <param name="cancelToken"></param>
public void TcpServerRun(
int pluginId,
string pluginName,
string encoding,
int bufferForReadingData,
string ipAddress,
int port,
bool logEvents,
IServiceScopeFactory _scopeFactory,
CancellationToken cancelToken)
{
IPAddress localAddrIN = IPAddress.Parse(ipAddress);
TcpListener listener = new TcpListener(localAddrIN, port);
Task.Run(() =>
{
// Dispose the DbContext instance when the task has completed. 'using' = dispose when finished...
using var scope = _scopeFactory.CreateScope();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TcpServer>>();
try
{
listener.Start();
cancelToken.Register(listener.Stop); // THIS IS IMPORTANT!
string logData = "TCP Server with name [" + pluginName + "] started Succesfully";
// Custom Logger - you would use your own logging method here...
WriteLogEvent("Information", "TCP Servers", "Started", pluginName, logData, null, _scopeFactory);
while (!cancelToken.IsCancellationRequested)
{
TcpClient client = listener.AcceptTcpClient();
logData = "A TCP Client with IP Address [" + client.Client.RemoteEndPoint.ToString() + "] connected to the TCP Server with name: [" + pluginName + "]";
// Custom Logger - you would use your own logging method here...
WriteLogEvent("Information", "TCP Servers", "Connected", pluginName, logData, null, _scopeFactory);
// Once each client has connected, start a new task with included parameters.
var task = Task.Run(async () =>
{
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
// Buffer for reading data
Byte[] bytes = new Byte[bufferForReadingData]; // Bytes variable
String data = null;
int i;
cancelToken.Register(client.Close); // THIS IS IMPORTANT!
// Checks CanRead to verify that the NetworkStream is readable.
if (stream.CanRead)
{
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0 & !cancelToken.IsCancellationRequested)
{
data = Encoding.ASCII.GetString(bytes, 0, i);
logData = "TCP Server with name [" + pluginName + "] received data [" + data + "] from a TCP Client with IP Address [" + client.Client.RemoteEndPoint.ToString() + "]";
// Custom Logger - you would use your own logging method here...
WriteLogEvent("Information", "TCP Servers", "Receive", pluginName, logData, null, _scopeFactory);
}
// Shutdown and end connection
client.Close();
logData = "A TCP Client disconnected from the TCP Server with name: [" + pluginName + "]";
// Custom Logger - you would use your own logging method here...
WriteLogEvent("Information", "TCP Servers", "Disconnected", pluginName, logData, null, _scopeFactory);
}
}, cancelToken);
}
}
catch (SocketException ex)
{
// When the cancellation token is called, we will always encounter
// a socket exception for the listener.AcceptTcpClient(); blocking
// call in the while loop thread. We want to catch this particular exception
// and mark the exception as an accepted event without logging it as an error.
// A cancellation token is passed usually when the running thread is manually stopped
// by the user from the UI, or will occur when the IHosted service Stop Method
// is called during a system shutdown.
// For all other unexpected socket exceptions we provide en error log underneath
// in the else statement block.
if (ex.SocketErrorCode == SocketError.Interrupted)
{
string logData = "TCP Server with name [" + pluginName + "] was stopped due to a CancellationTokenSource cancellation. This event is triggered when the SMTP Server is manually stopped from the UI by the user or during a system shutdown.";
WriteLogEvent("Information", "TCP Servers", "Stopped", pluginName, logData, null, _scopeFactory);
}
else
{
string logData = "TCP Server with name [" + pluginName + "] encountered a socket exception error and exited the running thread.";
WriteLogEvent("Error", "TCP Servers", "Socket Exception", pluginName, logData, ex, _scopeFactory);
}
}
finally
{
// Call the Stop method to close the TcpListener.
// Closing the listener does not close any exisiting connections,
// simply stops listening for new connections, you are responsible
// closing the existing connections which we achieve by registering
// the cancel token with the listener.
listener.Stop();
}
});
}
Looking at this code, I'm wondering what triggers onNotificationGCM? It is triggered when the app is registered but when does it get triggered again, say, when when I want to push a message to the user? I have a chat app that I'd like to push a message when chats come in. So I understand that I register the device but then this code needs to run again, I assume, with the new event. I just need understand part flow and part code.
// handle GCM notifications for Android
$window.onNotificationGCM = function (event) {
switch (event.event) {
case 'registered':
if (event.regid.length > 0) {
// Your GCM push server needs to know the regID before it can push to this device
// here is where you might want to send it the regID for later use.
var device_token = event.regid;
RequestsService.register(device_token).then(function(response){
alert('registered!');
});
//send device reg id to server
}
break;
case 'message':
// if this flag is set, this notification happened while we were in the foreground.
// you might want to play a sound to get the user's attention, throw up a dialog, etc.
if (event.foreground) {
console.log('INLINE NOTIFICATION');
var my_media = new Media("/android_asset/www/" + event.soundname);
my_media.play();
} else {
if (event.coldstart) {
console.log('COLDSTART NOTIFICATION');
} else {
console.log('BACKGROUND NOTIFICATION');
}
}
navigator.notification.alert(event.payload.message);
console.log('MESSAGE -> MSG: ' + event.payload.message);
//Only works for GCM
console.log('MESSAGE -> MSGCNT: ' + event.payload.msgcnt);
//Only works on Amazon Fire OS
console.log('MESSAGE -> TIME: ' + event.payload.timeStamp);
break;
case 'error':
console.log('ERROR -> MSG:' + event.msg);
break;
default:
console.log('EVENT -> Unknown, an event was received and we do not know what it is');
break;
}
};
Have a look at this example:
case 'message':
/*
if (e.foreground) {
window.alert("Message recieved");
}
else {
if (e.coldstart) {
window.alert("Coldstart Notification");
} else {
window.alert("Background Notification");
}
}
window.alert("Notification message: " + e.payload.message
+ "\n\n Time: " + e.payload.conversation);
*/
var data = e.payload;
if (data.conversation){
window.history.replaceState(null, '', '#/chats/');
ProjectName.conversation.load(data.conversation);
}
if (data.product){
window.history.replaceState(null, '', '#/product/' + data.product);
}
break;
case 'error':
// window.alert("Notification error: " + e.msg);
break;
default:
// window.alert("Notification - Unknown event");
break;
}
The e.payload contains all data that you send to your application, including message. You can access your other variables in case 'message'
I am using Arduino GSM Shield receiving SMS from an Android app. And the content of this SMS will control a LED. If the content of this SMS is not "off", the LED will be on and the content will be printed in the serial monitor. But if it is "off", the LED will be off immediately. Besides, the LED will keep being on until the "off" message coming. For now, I used the code from the example of the software. But I cannot use the content of this SMS to control the status of LED. With the code below, the LED could not be turned on and the content could not be displayed on the monitor. I think it was because the sketch failed to get the whole content of this SMS. Could anybody tell me how to solve this problem? Thanks.
#include <GSM.h>
GSM gsmAccess;
GSM_SMS sms;
char senderNumber[20];
int led=13;
void setup()
{
Serial.begin(9600);
pinMode(led,OUTPUT);
digitalWrite(led,LOW);
while (!Serial) {
;
}
Serial.println("SMS Messages Receiver");
boolean notConnected = true;
while(notConnected)
{
if(gsmAccess.begin("6442")==GSM_READY)
notConnected = false;
else
{
Serial.println("Not connected");
delay(1000);
}
}
Serial.println("GSM initialized");
Serial.println("Waiting for messages");
}
void loop()
{
char c;
int val=0;
val=digitalRead(led);
if (val==HIGH){
digitalWrite(led,HIGH);
}
if (sms.available())
{
Serial.println("Message received from:");
sms.remoteNumber(senderNumber, 20);
Serial.println(senderNumber);
if(sms.peek()=='#')
{
Serial.println("Discarded SMS");
sms.flush();
}
while(c=sms.read())
if(c='off'){
digitalWrite(led,LOW);
}else{
digitalWrite(led,HIGH);
Serial.print(c);
}
Serial.println("\nEND OF MESSAGE");
sms.flush();
Serial.println("MESSAGE DELETED");
}
delay(1000);
}
With this line
if(c='off'){
you are setting the value of c to "off". I guess you want to compare the value of c to the string "off" instead. Use == instead of =.
Also, what happens if someone sends "OFF" instead of "off"......? you need to handle that case as well. Try converting the SMS to lower characters before you do the compare.
I am writing a kind of chat server app where a message received from one websocket client is sent out to all other websocket clients. To do this, I keep the connected clients in a list. When a client disconnects, I need to remove it from the list (so that future "sends" do not fail).
However, sometimes when a client disconnects, the server just gets an exception "connection reset by peer", and the code does not get chance to remove from the client list. Is there a way to guarantee a "nice" notification that the connection has been reset?
My code is:
void WsRequestHandler::handleRequest(HTTPServerRequest &req, HTTPServerResponse &resp)
{
int n;
Poco::Timespan timeOut(5,0);
try
{
req.set("Connection","Upgrade"); // knock out any extra tokens firefox may send such as "keep-alive"
ws = new WebSocket(req, resp);
ws->setKeepAlive(false);
connectedSockets->push_back(this);
do
{
flags = 0;
if (!ws->poll(timeOut,Poco::Net::Socket::SELECT_READ || Poco::Net::Socket::SELECT_ERROR))
{
// cout << ".";
}
else
{
n = ws->receiveFrame(buffer, sizeof(buffer), flags);
if (n > 0)
{
if ((flags & WebSocket::FRAME_OP_BITMASK) == WebSocket::FRAME_OP_BINARY)
{
// process and send out to all other clients
DoReceived(ws, buffer, n);
}
}
}
}
while ((flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE);
// client has closed, so remove from list
for (vector<WsRequestHandler *>::iterator it = connectedSockets->begin() ; it != connectedSockets->end(); ++it)
{
if (*it == this)
{
connectedSockets->erase(it);
logger->information("Connection closed %s", ws->peerAddress().toString());
break;
}
}
delete(ws);
ws = NULL;
}
catch (WebSocketException& exc)
{
//never gets called
}
}
See receiveFrame() documentation:
Returns the number of bytes received. A return value of 0 means that the peer has shut down or closed the connection.
So if receiveFrame() call returns zero, you can act acordingly.
I do not know if this is an answer to the question, but the implementation you have done does not deal with PING frames. This is currently (as of my POCO version: 1.7.5) not done automatically by the POCO framework. I put up a question about that recently. According to the RFC (6465), the ping and pong frames are used (among others) as a keep-alive function. This may therefore be critical to get right in order to get your connection stable over time. Much of this is guess-work from my side as I am experimenting with this now myself.
#Alex, you are a main developer of POCO I believe, a comment on my answer would be much appreciated.
I extended the catch, to do some exception handling for "Connection reset by peer".
catch (Poco::Net::WebSocketException& exc)
{
// Do something
}
catch (Poco::Exception& e)
{
// This is where the "Connection reset by peer" lands
}
A bit late to the party here... but I am using Poco and Websockets as well - and properly handling disconnects was tricky.
I ended up implementing a simple ping functionality myself where the client side sends an ACK message for every WS Frame it receives. A separate thread on the server side tries to read the ACK messages - and it will now detect when the client has disconnected by looking at flags | WebSocket::FRAME_OP_CLOSE.
//Serverside - POCO. Start thread for receiving ACK packages. Needed in order to detect when websocket is closed!
thread t0([&]()->void{
while((!KillFlag && ws!= nullptr && flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE && machineConnection != nullptr){
try{
if(ws == nullptr){
return;
}
if(ws->available() > 0){
int len = ws->receiveFrame(buffer, sizeof(buffer), flags);
}
else{
Util::Sleep(10);
}
}
catch(Poco::Exception &pex){
flags = flags | WebSocket::FRAME_OP_CLOSE;
return;
}
catch(...){
//log::info(string("Unknown exception in ACK Thread drained"));
return;
}
}
log::debug("OperatorWebHandler::HttpRequestHandler() Websocket Acking thread DONE");
});
on the client side I just send a dummy "ACK" message back to the server (JS) every time I receive a WS frame from the server (POCO).
websocket.onmessage = (evt) => {
_this.receivedData = JSON.parse(evt.data);
websocket.send("ACK");
};
It is not about disconnect handling, rather about the stability of the connection.
Had some issues with POCO Websocket server in StreamSocket mode and C# client. Sometimes the client sends Pong messages with zero length payload and disconnect occurs so I added Ping and Pong handling code.
int WebSocketImpl::receiveBytes(void* buffer, int length, int)
{
char mask[4];
bool useMask;
_frameFlags = 0;
for (;;) {
int payloadLength = receiveHeader(mask, useMask);
int frameOp = _frameFlags & WebSocket::FRAME_OP_BITMASK;
if (frameOp == WebSocket::FRAME_OP_PONG || frameOp ==
WebSocket::FRAME_OP_PING) {
std::vector<char> tmp(payloadLength);
if (payloadLength != 0) {
receivePayload(tmp.data(), payloadLength, mask, useMask);
}
if (frameOp == WebSocket::FRAME_OP_PING) {
sendBytes(tmp.data(), payloadLength, WebSocket::FRAME_OP_PONG);
}
continue;
}
if (payloadLength <= 0)
return payloadLength;
if (payloadLength > length)
throw WebSocketException(Poco::format("Insufficient buffer for
payload size %d", payloadLength),
WebSocket::WS_ERR_PAYLOAD_TOO_BIG);
return receivePayload(reinterpret_cast<char*>(buffer), payloadLength,
mask, useMask);
}
}