NO_POLL websocket server on esp8266 rtos sdk - sdk

I'm trying to use the nopoll library provided with the RTOS sdk for the esp8266, to build a websockets server.
I've followed the example provided on the no_poll website namely:
noPollConn * listener = nopoll_listener_new (ctx, "0.0.0.0", "1234");
if (! nopoll_conn_is_ok (listener)) {
// some error handling here
}
nopoll_ctx_set_on_msg (ctx, listener_on_message, NULL);
nopoll_loop_wait (ctx, 0);
with an appropriate callback:
void listener_on_message (noPollCtx * ctx, noPollConn * conn, noPollMsg * msg, noPollPtr user_data) {
printf ("Listener received (size: %d, ctx refs: %d): (first %d bytes, fragment: %d) '%s'\n",
nopoll_msg_get_payload_size (msg),
nopoll_ctx_ref_count (ctx), shown, nopoll_msg_is_fragment (msg), example);
nopoll_conn_send_text (conn, "Message received", 16);
return;
}
But the process does nothing!
Has anyone else been able to make this work?
Or is it just broken?
In the examples directory for the RTOS, a websockets send example is provided, which is a cut down version of the test suite for the standard NO_POLL library. I noticed that the tests which are removed include the
tests for server creation.
Any help which you can provide would be welcomed!
BR

Related

Dart TCP socket concatenates all 'write' sync calls as a single packet

I'm trying to send multiple packets at once to a server, but the socket keeps "merging" all sync calls to write as a single call, I did a minimal reproducible example:
import 'dart:io';
void main() async {
// <Server-side> Create server in the local network at port <any available port>.
final ServerSocket server =
await ServerSocket.bind(InternetAddress.anyIPv4, 0);
server.listen((Socket client) {
int i = 1;
client.map(String.fromCharCodes).listen((String message) {
print('Got a new message (${i++}): $message');
});
});
// <Client-side> Connects to the server.
final Socket socket = await Socket.connect('localhost', server.port);
socket.write('Hi World');
socket.write('Hello World');
}
The result is:
> dart example.dart
> Got a new message (1): Hi WorldHello World
What I expect is:
> dart example.dart
> Got a new message (1): Hi World
> Got a new message (2): Hello World
Unfortunately dart.dev doesn't support dart:io library, so you need to run in your machine to see it working.
But in summary:
It creates a new tcp server at a random port.
Then creates a socket that connects to the previous created server.
The socket makes 2 synchronous calls to the write method.
The server only receives 1 call, which is the 2 messages concatenated.
Do we have some way to receive each synchronous write call in the server as separated packets instead buffering all sync calls into a single packet?
What I've already tried:
Using socket.setOption(SocketOption.tcpNoDelay, true); right after Socket.connect instantiation, this does modify the result:
final Socket socket = await Socket.connect('localhost', server.port);
socket.setOption(SocketOption.tcpNoDelay, true);
// ...
Using socket.add('Hi World'.codeUnits); instead of socket.write(...), also does not modify the result as expected, because write(...) seems to be just a short version add(...):
socket.add('Hi World'.codeUnits);
socket.add('Hello World'.codeUnits);
Side note:
Adding an async delay to avoid calling write synchronously:
socket.add('Hi World'.codeUnits);
await Future<void>.delayed(const Duration(milliseconds: 100));
socket.add('Hello World'.codeUnits);
make it works, but I am pretty sure this is not the right solution, and this isn't what I wanted.
Environment:
Dart SDK version: 2.18.4 (stable) (Tue Nov 1 15:15:07 2022 +0000) on "windows_x64"
This is a Dart-only environment, there is no Flutter attached to the workspace.
As Jeremy said:
Programmers coding directly to the TCP API have to implement this logic themselves (e.g. by prepending a fixed-length message-byte-count field to each of their application-level messages, and adding logic to the receiving program to parse these byte-count fields, read in that many additional bytes, and then present those bytes together to the next level of logic).
So I chose to:
Prefix each message with a - and suffix with ..
Use base64 to encode the real message to avoid conflict between the message and the previously defined separators.
And using this approach, I got this implementation:
// Send packets:
socket.write('-${base64Encode("Hi World".codeUnits)}.');
socket.write('-${base64Encode("Hello World".codeUnits)}.');
And to parse the packets:
// Cache the previous parsed packet data.
String parsed = '';
void _handleCompletePacket(String rawPacket) {
// Decode the original message from base64 using [base64Decode].
// And convert the [List<int>] to [String].
final String message = String.fromCharCodes(base64Decode(rawPacket));
print(message);
}
void _handleServerPacket(List<int> rawPacket) {
final String packet = String.fromCharCodes(rawPacket);
final String next = parsed + packet;
final List<String> items = <String>[];
final List<String> tokens = next.split('');
for (int i = 0; i < tokens.length; i++) {
final String char = tokens[i];
if (char == '-') {
if (items.isNotEmpty) {
// malformatted packet.
items.clear();
continue;
}
items.add('');
continue;
} else if (char == '.') {
if (items.isEmpty) {
// malformatted packet.
items.clear();
continue;
}
_handleCompletePacket(items.removeLast());
continue;
} else {
if (items.isEmpty) {
// malformatted packet.
items.clear();
continue;
}
items.last = items.last + char;
continue;
}
}
if (items.isNotEmpty) {
// the last data of this packet was left incomplete.
// cache it to complete with the next packet.
parsed = items.last;
}
}
client.listen(_handleServerPacket);
There are certainly more optimized solutions/approaches, but I got this just for chatting messages within [100-500] characters, so that's fine for now.

lwIP mqtt connection error on stm32f4 discovery

I am trying to use lwIP for a client, which sends data to mosquitto broker on stm32f407 discovery.
Mqtt application is implemented at lwIP. I just use them like that at main after initializing.
mqtt_client_t static_client;
Afterwards, with USART interrupt, I call
example_do_connect(&static_client); example_publish(&static_client,0);
Which calls those functions:
{
struct mqtt_connect_client_info_t ci;
err_t err;
/* Setup an empty client info structure */
memset(&ci, 0, sizeof(ci));
/* Minimal amount of information required is client identifier, so set it here */
ci.client_id = "lwip_test";
ci.client_user = NULL;
ci.client_pass = NULL;
/* Initiate client and connect to server, if this fails immediately an error code is returned
otherwise mqtt_connection_cb will be called with connection result after attempting
to establish a connection with the server.
For now MQTT version 3.1.1 is always used */
err = mqtt_client_connect(client, &serverIp, MQTT_PORT, mqtt_connection_cb, 0, &ci);
/* For now just print the result code if something goes wrong*/
if(err != ERR_OK) {
}
}
and
void example_publish(mqtt_client_t *client, void *arg)
{
const char *pub_payload= "stm32_test";
err_t err;
u8_t qos = 2; /* 0 1 or 2, see MQTT specification */
u8_t retain = 0; /* No don't retain such crappy payload... */
err = mqtt_publish(client, "pub_topic", pub_payload, strlen(pub_payload), qos, retain, mqtt_pub_request_cb, arg);
if(err != ERR_OK) {
// printf("Publish err: %d\n", err);
err = ERR_OK;
}
}
/* Called when publish is complete either with sucess or failure */
static void mqtt_pub_request_cb(void *arg, err_t result)
{
if(result != ERR_OK) {
// printf("Publish result: %d\n", result);
}
}
I am able to ping board, my IP adress has been assigned in main by using IP_ADDR4(&serverIp, 192,168,2,97);
I've used all needed functions like MX_LWIP_Init(), MX_LWIP_Process() and actually i am even able to implement a TCP client, which is working nice. So internet connection is well, but I guess, there is a point that i missed in mqttclient. Callbacks is also have done by Erik Anderssen's guide.
When i try to subscribe to board's IP by using mosquitto, Error: no connection could be made because the target actively refused it. If you notice some point that i have missed or have an idea, please let me know.
Any help will appreciated, thanks in advance.
I had a similar problem that the server refused the connection when QoS (quality of service) was set to 2, but the server needed it to be 0. Try changing the parameter qos in the line in the connection callback to either 0 or 1:
err = mqtt_subscribe(mqtt.client, "topic", qos, MqttApp_SubscribeRequestCallback, arg);
Same applies to the parameter qos in the publish function:
change u8_t qos = 2; to u8_t qos = 0; (or 1 - whatever your server requires)
Hope it helps. Cheers.

Is there a way to make separate DNS requests for IPv4 and IPv6 in swift or ObjC

What I have so far is:
void startQueryIPv4(const char *hostName){
printf("startQueryIPv4");
DNSServiceRef serviceRef;
DNSServiceGetAddrInfo(&serviceRef, kDNSServiceFlagsForceMulticast, 0, kDNSServiceProtocol_IPv4, hostName, queryIPv4Callback, NULL);
DNSServiceProcessResult(serviceRef);
DNSServiceRefDeallocate(serviceRef);
}
static void queryIPv4Callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context){
printf("queryIPv4Callback");
if (errorCode == kDNSServiceErr_NoError) {
printf("no error");
char *theAddress = NULL;
switch(address->sa_family) {
case AF_INET: {
struct sockaddr_in *addr_in = (struct sockaddr_in *)address;
theAddress = malloc(INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(addr_in->sin_addr), theAddress, INET_ADDRSTRLEN);
break;
}
case AF_INET6: {
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)address;
theAddress = malloc(INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &(addr_in6->sin6_addr), theAddress, INET6_ADDRSTRLEN);
break;
}
default:
break;
}
printf("IP address: %s\n", theAddress);
free(theAddress);
} else {
printf("%d", errorCode);
}
}
But the callback is never called.
In the console I get this error: TIC Read Status [9:0x0]: 1:57
ObjectiveC is not my power but I had to mess with it. Any help will be appreciated.
Inasmuch as Objective-C is a true superset of C and Darwin is certified compatible with SUS 3, your Objective-C program for iOS should be able to use the C interface to the system's name resolver: getaddrinfo(). You can use the third argument to this function to specify that you want only IPv4 results (or only IPv6 results).
Things of which you should be aware:
this is of course a synchronous interface; if you want asynchronous then you'll need to arrange for that yourself.
getaddrinfo() allocates and returns a linked list of addresses, so
in principle, you might need to check more than one
you need to free the list after you're done with it via freeaddrinfo()
The reason you aren't getting a callback is that you aren't using a dispatch queue. The DNSServiceGetAddrInfo API is asynchronous. If you are doing this on an Apple device, you want something more like this:
void startQueryIPv4(const char *hostName) {
printf("startQueryIPv4");
DNSServiceRef serviceRef;
DNSServiceGetAddrInfo(&serviceRef, kDNSServiceFlagsForceMulticast, 0, kDNSServiceProtocol_IPv4, hostName, queryIPv4Callback, NULL);
main_queue = dispatch_get_main_queue();
DNSServiceSetDispatchQueue(sdref, main_queue);
dispatch_main();
}
Note that dispatch_main() is the main event loop for libdispatch: if you want to do other stuff, you need to schedule it in the dispatch loop, because dispatch_main() will not return.
In your original code you called DNSServiceRefDeallocate(), but you can't do that until you want to stop the query. If you call it right after you start the query, it will cancel the query. So e.g. you could call it from the callback.
However, a better flow would be to do a long-lived query (kDNSServiceFlagsLongLivedQuery) so that you get an update whenever the information changes. Of course you'd then need to change the host you're connecting to, so only do this if your application will be connected for an extended period.
Additionally, you may get more than one answer. If you do, it may be that some answers work to connect, and others don't. So you might like to accumulate answers and try each one, rather than giving up if the first answer you get doesn't work. The callback will include the kDNSServiceFlagsMoreComing flag if there is more data coming immediately. Each time the callback is called, it will get one answer (or an indication that the query has failed in some way).
Of course, this is a fairly low-level API. If you want to make your life a bit easier, you should use Network Framework. Network Framework does the "happy eyeballs" part for you—trying each response until it gets a connection, and returning you the connection it gets, canceling the others.
But you didn't ask about that, so I won't go into details here.

Http client mode with Contiki?

I want to make a webAPI call from a sensor using http, is it possible to do http requests using Contiki OS?
As far as I've searched I found only coap client examples.
Check the examples/http-socket example, it shows how to use CRUD methods such as PUT, GET, etc.
Here's the link to the example (working with the latest master commit)
This example relies on IP64, but can be changed to work with IPv6, basically you need to include the http-socket library. Here are the more relevant parts of the example:
#include "contiki-net.h"
#include "http-socket.h"
#include "ip64-addr.h"
#include <stdio.h>
static struct http_socket s;
static int bytes_received = 0;
static void
callback(struct http_socket *s, void *ptr,
http_socket_event_t e,
const uint8_t *data, uint16_t datalen)
{
if(e == HTTP_SOCKET_ERR) {
printf("HTTP socket error\n");
} else if(e == HTTP_SOCKET_DATA) {
bytes_received += datalen;
printf("HTTP socket received %d bytes of data\n", datalen);
}
}
PROCESS_THREAD(http_example_process, ev, data)
{
PROCESS_BEGIN();
/* Initializes the socket */
http_socket_init(&s);
/* GET request */
http_socket_get(&s, "http://www.contiki-os.org/", 0, 0,
callback, NULL);
/* Waits forever for the HTTP callback */
while(1) {
PROCESS_WAIT_EVENT_UNTIL(0);
}
PROCESS_END();
}
Yes you can do that:
What I understand is that you are looking for Websense Example in Contiki OS.it uses HTTP protocl.
A: so find this file.
~/contiki/examples/zolertia/z1/ipv6/z1-websense/z1-websense.c
Burn it on Sender Mote.
Burn border-router.c file located in /home/superuser/contiki/examples/ipv6/rpl-border-router/
Connect Border Router with tunnelslip with command make connect-router.
use the HTTP IPV6 url shown by tunnelslip on connection.
this url in browser will give you address of motes connected to it.
use that sender mote address in web browser and see the mote output.
B: or from contiki/cooja simulator:
launch this project file. this is working demo for the websense.
~contiki/examples/zolertia/z1/ipv6/z1-websense/example-z1-websense.csc
and repeat from step 3.
for further you can ask me.

Arduino WiFi shield post with header problems

I'm trying to do a post from the arduino wifi shield to my java servlet. The servlet functions with url get, and jquery post, but I can't sort the headers out in my arduino code. Any help will be greatly appreciated!
The server returns 200, but I'm not getting the payload "content" as value. I'm not exactly sure what I'm doing wrong but I'm pretty sure it's in how my headers are setup. I've spent the last two days trying to get it.
#include <SPI.h>
#include <WiFi.h>
char ssid[] = "jesussavesforjust19.95"; // your network SSID (name)
char pass[] = "********"; // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0; // your network key Index number (needed only for WEP)
int status = WL_IDLE_STATUS;
IPAddress server(192,168,10,149); // numeric IP for Google (no DNS)
WiFiClient client;
void setup() {
Serial.begin(9600);
// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.println("Attempting to connect to SSID: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
Serial.println("Connected to wifi");
printWifiStatus();
sendData("0600890876");
}
void loop() {
// if there's incoming data from the net connection.
// send it out the serial port. This is for debugging
// purposes only:
if (client.available()) {
char c = client.read();
Serial.println(c);
}
//String dataString = "060088765";
// if you're not connected, and ten seconds have passed since
// your last connection, then connect again and send data:
if(!client.connected())
{
Serial.println();
Serial.println("disconnecting.");
client.stop();
//sendData(dataString);
for(;;)
;
}
}
// this method makes a HTTP connection to the server:
void sendData(String thisData) {
// if there's a successful connection:
Serial.println("send data");
if (client.connect(server, 8080)) {
String content = "value=0600887654";
Serial.println(content);
Serial.println("connected");
client.println("POST /hos HTTP/1.1");
client.println("Host:localhost");
client.println("Connection:Keep-Alive");
client.println("Cache-Control:max-age=0");
client.println("Content-Type: application/x-www-form-urlencoded\n");
client.println("Content-Length: ");
client.println(content.length());
client.println("\n\n");
client.println(content);
}
else {
// if you couldn't make a connection:
Serial.println("form connection failed");
Serial.println();
Serial.println("disconnecting.");
client.stop();
}
}
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.println("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.println("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.println("signal strength (RSSI):");
Serial.println(rssi);
Serial.println(" dBm");
}
Perhaps, some of your "Serial.println" and "client.println" commands should be "Serial.print" and "client.print" instead. For example:
client.print("Content-Length: ");
client.println(content.length());
would avoid adding a line break between the text and the number.
This is maybe more advice on an approach than an answer.
If I was doing something like this I would not start on the Arduino. The endless compile, download, run, look at print()'s would drive me crazy. I would fully prototype the client/server interaction in whatever you have at your fingertips, preferably something with a debugger. (Java, Python, PHP, VB, whatever you know that you can slap together)
Second, I would run Wireshark on the server so that I could see exactly what was being sent and responded.
Then I would port the same interaction over to the Arduino. Again inspect with Wireshark to confirm you are getting what you expected. If you send the same bytes, you should get the same response.
Even if you choose to implement straight on Arduino, consider having Wireshark to capture the actual network traffic.
With Wireshark, you might see that the Arduino println() is not sending the correct line end for the server.
Also, there is no guarantee that last println() is actually sent. The network stack implementation is free to buffer as it sees fit. You might need a flush(). A packet trace would show this.
With a packet capture you might find that time matters. In theory TCP is a stream and you should be able to send that POST data 1 character at a time in 1 packet and everything would work. But the Arduino might be so slow executing those println()'s by the server's standards that it times out. In such case you would see the server respond before the Arduino even finished sending.

Resources