Mbed3 diving into network
mbed-example-network
After a couple of days struggling and fixing things (I sent another pull request fixing a #warning in mbed-hal-ksdk-mcu), today I can look at the actual mbed3 examples without build errors (Mbed / me not setting target) or missing libraries (Fedora).
So the first thing was leave mbed-client-examples, which is aimed at their cloud protocol / servers, and checkout the git version of mbed-example-network, which is aimed at generic network activity.
He builds four examples, the most interesting for me of which is "helloworld-tcpclient.bin". This does one fixed thing using "by hand" http directly on the socket. The one fixed thing is reach out to (the old...) server and fetch some text... surprisingly enough "Hello world!".
TCP client IP Address is 192.168.2.205
Starting DNS lookup for developer.mbed.org
DNS Response Received:
developer.mbed.org: 217.140.101.30
Connecting to 217.140.101.30:80
Connected to 217.140.101.30:80
Sending HTTP Get Request...
HTTP Response received.
HTTP: Received 473 chars from server
HTTP: Received 200 OK status ... [OK]
HTTP: Received 'Hello world!' status ... [OK]
HTTP: Received message:
HTTP/1.1 200 OK
Server: nginx/1.7.10
Date: Sun, 01 Nov 2015 00:42:37 GMT
Content-Type: text/plain
Content-Length: 14
Connection: keep-alive
Last-Modified: Fri, 27 Jul 2012 13:30:34 GMT
Accept-Ranges: bytes
Cache-Control: max-age=36000
Expires: Sun, 01 Nov 2015 10:42:37 GMT
X-Upstream-L3: 172.17.42.1:8080
X-Upstream-L2: developer-sjc-indigo-1-nginx
X-Upstream-L1-next-hop: 217.140.101.34:8001
X-Upstream-L1: developer-sjc-indigo-border-nginx
Hello world!
That worked great. But not until you read the source and saw that unlike mbed-client-examples, this code, I guess ported from earlier mbed, sets the serial port for 115200.
void app_start(int argc, char *argv[]) {
(void) argc;
(void) argv;
static Serial pc(USBTX, USBRX);
pc.baud(115200);
...
The other app defaults to 9600, at least on the one recommended supported board, K64F. So you will see nothing switching between these apps even though they are both official example apps.
I made a bug about it on the github project and offered to send a fix
https://github.com/ARMmbed/mbed-client-examples/issues/32
Three notable things
1) It's C++
Although you can write mainly in C, you have no choice but to frame it inside C++, because mbed3 apis themselves are in C++
2) The callbacks are sophisticated
In app_start(), which is the mbed3 equivalent of main(), he instantiates some things and then immediately goes back to the scheduler (minar) after scheduling a callback to start the test. It's what you would do in a generic event loop, set the state for the next thing you want and then return to the event loop.
The callback also has a specific object instantiation associated with it. They also have some ghetto varargs where you can fix how many args the callback wants and have that delivered at callback time.
mbed::util::FunctionPointer1<void, const char*> fp(hello, &HelloHTTP::startTest);
minar::Scheduler::postCallback(fp.bind(HTTP_PATH));
So it's basically doing schedule_the_callback(callback_t cb, void *context, ...); in two steps. Since it's mandatory to be defining callbacks a lot, I guess this will be very handy once you get used to it. But it's a C++ believer's way (C would have a state enum, and a switch() to do the callback work).
3) The class hierarchy for Socket is cool, send is unposixy
There is a TCPStream class which understands TCP connection state, and fires events as they change (bear in mind though, all events are serialized and never preempt other event handlers). TCPStream has a few carefully-chosen apis
- connect(), which points to your callback it handle it actually connects
- setOnDisconnect(), which... yes the same idea
He also inherits from the Socket class (this is nicely done)
setOnReadable(), again you can tell it what your handler is if some data arrived and can be read, like posix poll() POLLIN
setOnSent(), I guess this has the same semantic as posix poll() POLLOUT... the implication is if the last thing got sent, you may try to send() something else. It looks like it's the caller's problem to hold the buffer between send() and OnSent() coming... in contrast posix assumes the kernel will buffer it and on return from send(), you don't need to keep it around.
resolve(), again you tell the stream the handler to call after name resolution completes
So particularly the send flow has a critical deviation from posix... it's not a ding, these SoCs have way less resources and a different OS architecture than assumed in posix. But for example even though libwebsockets is otherwise quite compatible with mbed3, being singlethreaded and nonblocking, he assumes in many places he can do things like make a buffer on the stack, "send" it, and exit the function, as you can do in posix. So that requires some thought.
What we learned this time
mbed3 needs a little more consistency about what baud rate the official example apps will use
HTTP test app works great
Your code can be C-flavoured C++, but C++ it must be
class hierarchy seems to be used very wisely and neatly in Socket and friends
the callbacks carry with them object context and varargs, varargs needing some help by hand in the code
send() expects you to hold the buffer until it was sent, and you get a callback to tell you that, which is very reasonable under the circumstances. But we're not in Kansas any more posix-wise.