Hi all,
I've got a problem where my device shares the one UART device with both
the serial console and polling some Modbus/RTU energy meters, and this
is proving a little tricky to debug.
My usual method would be to have the client write hexdumps of all
traffic to the console, but this doesn't work when the serial console is
tied up talking to the said meter.
Whilst pondering this, it occurred to me the Linux kernel has a feature
called `netconsole` wherein it sends console traffic over UDP to `nc`
where it can be monitored. My thinking was, I could set up `nc` on my
workstation and once the Thread device was on the network, I could tell
it the IP and port to stream logs to over 6LoWPAN to the workstation.
Long term, it'd be nice to have the devices be able to stream logs to a
syslog daemon running on the border router, but that requires more
set-up and overhead than plain `netconsole`.
On my workstation, I run `nc -6 -u -l -p 1234`, `netstat` informs me
that `nc` is listening on all IPv6 interfaces. Indeed, if I try sending
from another IPv6 host:
> RC=0 stuartl@atomos ~ $ fortune | nc -u 2001:44b8:21ac:7053:3c67:3bff:fe75:3e4a 1234
> nc: using datagram socket
> ^C
I see this:
> stuartl@vk4msl-ws:~$ nc -6 -u -l -p 1234
> This fortune is encrypted -- get your decoder rings ready!
>
So that end works.
I have a CoAP endpoint, which I can hit via Copper with a POST,
containing the IP address and port number of the `nc` instance. The
code for the actual CoAP handler is pretty simple: on receipt of a POST,
it looks for a comma in the message body, replaces that with a NULL and
passes a pointer to the next character along to `strtoul`.
With that, I'm able to parse an IP/port in the form:
2001:db8:1111:2222::abcd,1234
In my code, I have:
> static void serial_console_udp_receive(
> void *context, otMessage *msg,
> const otMessageInfo *msg_info) {
> /*
> * Extract the message content and inject that into the console buffer
> */
> (void)context;
> (void)msg_info;
>
> int length = otMessageGetLength(msg);
> uint8_t buffer[length];
> length = otMessageRead(msg, otMessageGetOffset(msg),
> buffer, length);
>
> otPlatUartReceived(buffer, length);
> }
>
> otError serial_console_init(otInstance* thread_instance,
> const char* target_ip, uint16_t target_port) {
> otError err;
> otSockAddr addr;
>
> err = otIp6AddressFromString(target_ip, &serial_console_target.addr);
> if (err)
> return err;
>
> serial_console_target.thread_instance = thread_instance;
> serial_console_target.port = target_port;
>
> memcpy(&(addr.mAddress), &serial_console_target.addr,
> sizeof(otIp6Address));
> addr.mPort = target_port;
> addr.mScopeId = 0;
>
> err = otUdpOpen(thread_instance,
> &serial_console_target.socket,
> serial_console_udp_receive, NULL);
> if (err) {
> memset(&serial_console_target, 0,
> sizeof(struct serial_console_target_t));
> return err;
> }
>
> err = otUdpConnect(&serial_console_target.socket, &addr);
> if (err) {
> otUdpClose(&serial_console_target.socket);
> memset(&serial_console_target, 0,
> sizeof(struct serial_console_target_t));
> }
> return err;
> }
and in my `_write_r` newlib stub (my OpenThread platform code calls
`write(0, …)`; so `iprintf` et all work too), I have:
> /* If there's a network console target, send a message */
> if (serial_console_target.port) {
> otError err = OT_ERROR_NONE;
> otMessageInfo msg_info;
> otMessage* msg = otUdpNewMessage(
> serial_console_target.thread_instance, true);
> if (!msg)
> err = OT_ERROR_NO_BUFS;
>
> if (err == OT_ERROR_NONE) {
> memset(&msg_info, 0, sizeof(otMessageInfo));
> memcpy(&(msg_info.mPeerAddr),
> &serial_console_target.addr,
> sizeof(otIp6Address));
> msg_info.mPeerPort = serial_console_target.port;
> msg_info.mHopLimit = 1;
>
> err = otMessageAppend(msg, (const uint8_t*)buf,
> cnt);
> }
>
> if (err == OT_ERROR_NONE)
> err = otUdpSend(&serial_console_target.socket, msg, &msg_info);
>
> if (err != OT_ERROR_NONE)
> otMessageFree(msg);
> }
In my browser, I send POST "fdde:ad00:beef:0:37ff:93ed:d6e9:26aa,1234"
(IP address is that of the `wlan0` interface created by `wpantund`) to
the /console endpoint, that calls `serial_console_init(thread_instance,
"fdde:…", 1234);`. This returns back `OT_ERROR_NONE`.
If I set a breakpoint within the above `if` block, typing a key on the
console breaks right there, and single-stepping, I see it call
`otUdpSend`, which again, returns `OT_ERROR_NONE`.
The device is acting as a "leader", with an NCP joined. I can ping it
and otherwise communicate with the device… it tells me it is sending
these UDP messages, but I don't seem to receive them. `ip6tables-save`
shows a clear firewall ruleset too.
The NCP shows no traffic in the `wpantund` logs.
Is there any other steps needed in order to send UDP datagrams to an
arbitrary IP/port?
Regards,
--
_ ___ Stuart Longland - Systems Engineer
\ /|_) | T:
+61 7 3535 9619
\/ | \ | 38b Douglas Street F:
+61 7 3535 9699
SYSTEMS Milton QLD 4064
http://www.vrt.com.au