This repository has been archived by the owner on Sep 20, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 36
coap-chat: a simple chat application using gCoAP #44
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# name of your application | ||
APPLICATION = coap_chat | ||
|
||
# If no BOARD is found in the environment, use this default: | ||
BOARD ?= native | ||
|
||
# This has to be the absolute path to the RIOT base directory: | ||
RIOTBASE ?= $(CURDIR)/../../RIOT | ||
|
||
USEMODULE += gnrc_netdev_default | ||
USEMODULE += auto_init_gnrc_netif | ||
# Specify the mandatory networking modules | ||
USEMODULE += gnrc_ipv6_default | ||
USEMODULE += gcoap | ||
# Additional networking modules that can be dropped if not needed | ||
USEMODULE += gnrc_icmpv6_echo | ||
# Add also the shell, some shell commands | ||
USEMODULE += shell | ||
USEMODULE += shell_commands | ||
|
||
# Comment this out to disable code in RIOT that does safety checking | ||
# which is not needed in a production environment but helps in the | ||
# development process: | ||
DEVELHELP ?= 0 | ||
|
||
# Change this to 0 show compiler invocation lines by default: | ||
QUIET ?= 1 | ||
|
||
include $(RIOTBASE)/Makefile.include |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
## About | ||
|
||
This application provides a simple command line chat using (g)CoAP as transport. | ||
It allows to send (short) messages to any (IPv6) address reachable by the node. | ||
All messages are sent to and received for a CoAP resource with path `/chat`. | ||
|
||
## Usage | ||
|
||
To send messages use the `chat` shell command, it can be invoked as follows: | ||
|
||
``` | ||
chat <destination> <nickname> <message> | ||
``` | ||
|
||
- **destination:** a reachable IPv6 address, may even be multicast `ff02::1` | ||
- **nickname:** your chat nickname, anything is allowed, but keep it short | ||
- **message:** what ever text you want to share, be brief, size is limited | ||
|
||
The message format is plain text and will be send as *nickname: message*, the | ||
maximum message size is 63 chars - so keep *nickname* and *message* short :) | ||
Please be aware that all CoAP messages are sent as non-confirmable, hence there | ||
is no retransmission in case of packet loss. | ||
|
||
## Notes | ||
|
||
The code base of this application aims for simplicity, thus it only provides | ||
a minimalistic set of functions. An advanced CoAP application can be found in | ||
the [RIOT examples](https://github.com/RIOT-OS/RIOT/tree/master/examples/gcoap). | ||
|
||
The application also ships with a number of standard shell commands, such as | ||
`ifconfig` to allow for network interface configuration. Just type `help` in | ||
the shell to see a full list and description of all available commands. | ||
|
||
By default this application is configured to run as an IPv6 node, i.e. it does | ||
not include any routing or relaying functionality - such needs to be handled | ||
by other nodes. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/* | ||
* Copyright (c) 2018 HAW Hamburg | ||
* | ||
* This file is subject to the terms and conditions of the GNU Lesser | ||
* General Public License v2.1. See the file LICENSE in the top level | ||
* directory for more details. | ||
*/ | ||
|
||
/** | ||
* @ingroup applications | ||
* @{ | ||
* | ||
* @file | ||
* @brief coap-chat - CoAP helper functions | ||
* | ||
* @author Sebastian Meiling <[email protected]> | ||
* | ||
* @} | ||
*/ | ||
|
||
#include <stdint.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#include "net/gcoap.h" | ||
|
||
#define ENABLE_DEBUG (0) | ||
#include "debug.h" | ||
|
||
#define COAP_CHAT_PATH "/chat" | ||
|
||
static ssize_t _chat_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx); | ||
|
||
/* CoAP resources */ | ||
static const coap_resource_t _resources[] = { | ||
{ COAP_CHAT_PATH, COAP_POST, _chat_handler, NULL }, | ||
}; | ||
|
||
static gcoap_listener_t _listener = { | ||
&_resources[0], | ||
sizeof(_resources) / sizeof(_resources[0]), | ||
NULL | ||
}; | ||
|
||
static ssize_t _chat_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx) | ||
{ | ||
(void)ctx; | ||
|
||
if (pdu->payload_len > 0) { | ||
printf("\n[ CHAT ] %.*s\n\n", pdu->payload_len, (char *)pdu->payload); | ||
return gcoap_response(pdu, buf, len, COAP_CODE_CHANGED); | ||
} | ||
return gcoap_response(pdu, buf, len, COAP_CODE_BAD_REQUEST); | ||
} | ||
|
||
static size_t _send(uint8_t *buf, size_t len, char *addr_str) | ||
{ | ||
ipv6_addr_t addr; | ||
sock_udp_ep_t remote; | ||
|
||
remote.family = AF_INET6; | ||
|
||
/* parse for interface */ | ||
int iface = ipv6_addr_split_iface(addr_str); | ||
if (iface == -1) { | ||
if (gnrc_netif_numof() == 1) { | ||
/* assign the single interface found in gnrc_netif_numof() */ | ||
remote.netif = (uint16_t)gnrc_netif_iter(NULL)->pid; | ||
} | ||
else { | ||
remote.netif = SOCK_ADDR_ANY_NETIF; | ||
} | ||
} | ||
else { | ||
if (gnrc_netif_get_by_pid(iface) == NULL) { | ||
DEBUG("[CoAP] interface not valid"); | ||
return 0; | ||
} | ||
remote.netif = iface; | ||
} | ||
|
||
/* parse destination address */ | ||
if (ipv6_addr_from_str(&addr, addr_str) == NULL) { | ||
DEBUG("[CoAP] unable to parse destination address"); | ||
return 0; | ||
} | ||
if ((remote.netif == SOCK_ADDR_ANY_NETIF) && ipv6_addr_is_link_local(&addr)) { | ||
DEBUG("[CoAP] must specify interface for link local target"); | ||
return 0; | ||
} | ||
memcpy(&remote.addr.ipv6[0], &addr.u8[0], sizeof(addr.u8)); | ||
|
||
/* parse port */ | ||
remote.port = GCOAP_PORT; | ||
|
||
return gcoap_req_send2(buf, len, &remote, NULL); | ||
kb2ma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
int coap_post(char *addr, char *msgbuf, size_t msglen) | ||
{ | ||
assert(msglen < GCOAP_PDU_BUF_SIZE); | ||
|
||
coap_pkt_t pdu; | ||
uint8_t buf[GCOAP_PDU_BUF_SIZE]; | ||
size_t len = 0; | ||
|
||
gcoap_req_init(&pdu, buf, GCOAP_PDU_BUF_SIZE, COAP_POST, COAP_CHAT_PATH); | ||
|
||
memcpy(pdu.payload, msgbuf, msglen); | ||
|
||
len = gcoap_finish(&pdu, msglen, COAP_FORMAT_TEXT); | ||
|
||
DEBUG("[CoAP] coap_post: sending msg ID %u, %u bytes\n", | ||
coap_get_id(&pdu), (unsigned) len); | ||
|
||
if (!_send(buf, len, addr)) { | ||
puts("[CoAP] coap_post: msg send failed"); | ||
return -1; | ||
} | ||
return len; | ||
} | ||
|
||
void coap_init(void) | ||
{ | ||
gcoap_register_listener(&_listener); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/* | ||
* Copyright (c) 2018 HAW Hamburg | ||
* | ||
* This file is subject to the terms and conditions of the GNU Lesser | ||
* General Public License v2.1. See the file LICENSE in the top level | ||
* directory for more details. | ||
*/ | ||
|
||
/** | ||
* @ingroup applications | ||
* @{ | ||
* | ||
* @file | ||
* @brief coap-chat - Main loop and shell handlers | ||
* | ||
* @author Sebastian Meiling <[email protected]> | ||
* | ||
* @} | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include "msg.h" | ||
|
||
#include "net/gcoap.h" | ||
#include "kernel_types.h" | ||
#include "shell.h" | ||
|
||
#define BUFLEN (64U) | ||
#define MAIN_QUEUE_SIZE (4) | ||
|
||
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; | ||
|
||
extern int coap_post(const char *addr, const char *buf, size_t buflen); | ||
extern void coap_init(void); | ||
|
||
int chat(int argc, char **argv) | ||
{ | ||
if (argc < 4) { | ||
puts("usage: chat <addr> <nick> <msg>"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could note that you can specify the interface with <addr>. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll put something in the README |
||
return 1; | ||
} | ||
|
||
char buf[BUFLEN] = { 0 }; | ||
size_t len = snprintf(buf, BUFLEN, "%s: %s", argv[2], argv[3]); | ||
for (int i = 4; i < argc; ++i) { | ||
len += snprintf(buf + len, BUFLEN - len, " %s", argv[i]); | ||
} | ||
coap_post(argv[1], buf, len); | ||
|
||
return 0; | ||
} | ||
|
||
static const shell_command_t shell_commands[] = { | ||
{ "chat", "CoAP chat", chat }, | ||
{ NULL, NULL, NULL } | ||
}; | ||
|
||
int main(void) | ||
{ | ||
/* for the thread running the shell */ | ||
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); | ||
/* init coap listener */ | ||
coap_init(); | ||
/* start shell */ | ||
puts("All up, running the shell now"); | ||
char line_buf[SHELL_DEFAULT_BUFSIZE]; | ||
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); | ||
|
||
/* should never be reached */ | ||
return 0; | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mh... I know this takes a little away from the simplicity at least implementation-wise, but maybe do a
connect
command that takes the<destination> <nickname>
parameters (and stores them) and just keepchat <message>
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to keep it as simple as possible, introducing another command would complicate the work flow. Also
connect
might imply or suggest functionality that is not there. Also you typically want to chat with different user (that is here destination addresses) and not only one specific, okay you might simply useff02::1
...But again, I aimed for simplicity - for a real chat application you might want to have chat rooms, user discovery and lists and more, but that would also blow up the application.
This whole thing is a side-product of a small school project, where pupils (with no C experience) were introduced to IoT and RIOT - that's why the main goal also was to keep the code simple and with minimal LOCs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, thanks for the clarification. Then I'm for now fine with the PR as is, but maybe you want to introduce DNS, so the pupils don't need to type (rather non-descript) IPv6 addresses? For the application this would only mean maybe 5 more lines (using
sock_dns
), but of course a little bit more configuration in the backend (configuring the nodes' names, setting RAs to distribute the DNS server, etc).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I'd rather not - as you already state it requires infrastructure (DNS Server) and setup.
During the school project we only had a couple of RIOT nodes (at first) and they send messages via link-local addresses and link-local multicast (
ff02::1
) - which might not be very IoT'ish but at least they had fun 😉Later we added a Raspberry Pi (with crosscoap) which forwarded all multicast messages to a webserver backend where all message were put into log and could be access via a webpage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(and in the end you still can use IPv6 addresses as a fallback)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. Just a suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(gnarf, why does GitHub not live-update a page -.-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the suggestion in the documentation to use ff02::1, which simplifies testing when there are only two nodes.