Merge pull request #988 from pjsg/mdns

Simple mdns implementation using the Espressif code
This commit is contained in:
Arnim Läuger 2016-02-24 22:54:39 +01:00
commit 78e9873d33
4 changed files with 200 additions and 0 deletions

View File

@ -29,6 +29,7 @@
#define LUA_USE_MODULES_HTTP
//#define LUA_USE_MODULES_HX711
#define LUA_USE_MODULES_I2C
//#define LUA_USE_MODULES_MDNS
#define LUA_USE_MODULES_MQTT
#define LUA_USE_MODULES_NET
#define LUA_USE_MODULES_NODE

160
app/modules/mdns.c Normal file
View File

@ -0,0 +1,160 @@
// Module for access to the espconn_mdns functions
#include "module.h"
#include "lauxlib.h"
#include "c_string.h"
#include "c_stdlib.h"
#include "c_types.h"
#include "mem.h"
#include "lwip/ip_addr.h"
#include "espconn.h"
#include "user_interface.h"
typedef struct wrapper {
struct mdns_info mdns_info;
char data;
} wrapper_t;
static wrapper_t *info;
typedef enum phase {
PHASE_CALCULATE_LENGTH,
PHASE_COPY_DATA
} phase_t;
static char *advance_over_string(char *s)
{
while (*s++) {
}
// s now points after the null
return s;
}
//
// mdns.close()
//
static int mdns_close(lua_State *L)
{
if (info) {
espconn_mdns_close();
c_free(info);
info = NULL;
}
return 0;
}
//
// this handles all the arguments. Two passes are necessary --
// one to calculate the size of the block, and the other to
// copy the data. It is vitally important that these two
// passes are kept in step.
//
static wrapper_t *process_args(lua_State *L, phase_t phase, size_t *sizep)
{
wrapper_t *result = NULL;
char *p = NULL;
if (phase == PHASE_COPY_DATA) {
result = (wrapper_t *) c_zalloc(sizeof(wrapper_t) + *sizep);
if (!result) {
return NULL;
}
p = &result->data;
}
if (phase == PHASE_CALCULATE_LENGTH) {
luaL_checktype(L, 1, LUA_TSTRING);
luaL_checktype(L, 2, LUA_TSTRING);
(void) luaL_checkinteger(L, 3);
*sizep += c_strlen(luaL_checkstring(L, 1)) + 1;
*sizep += c_strlen(luaL_checkstring(L, 2)) + 1;
} else {
c_strcpy(p, luaL_checkstring(L, 1));
result->mdns_info.host_name = p;
p = advance_over_string(p);
c_strcpy(p, luaL_checkstring(L, 2));
result->mdns_info.server_name = p;
p = advance_over_string(p);
result->mdns_info.server_port = luaL_checkinteger(L, 3);
}
if (lua_gettop(L) >= 4) {
luaL_checktype(L, 4, LUA_TTABLE);
lua_pushnil(L); // first key
int slot = 0;
while (lua_next(L, 4) != 0 && slot < sizeof(result->mdns_info.txt_data) / sizeof(result->mdns_info.txt_data[0])) {
if (phase == PHASE_CALCULATE_LENGTH) {
luaL_checktype(L, -2, LUA_TSTRING);
*sizep += c_strlen(luaL_checkstring(L, -2)) + 1;
*sizep += c_strlen(luaL_checkstring(L, -1)) + 1;
} else {
// put in the key
c_strcpy(p, luaL_checkstring(L, -2));
result->mdns_info.txt_data[slot] = p;
p = advance_over_string(p);
// now smash in the value
const char *value = luaL_checkstring(L, -1);
p[-1] = '=';
c_strcpy(p, value);
p = advance_over_string(p);
}
lua_pop(L, 1);
}
}
return result;
}
//
// mdns.register(hostname, servicename, port [, attributes])
//
static int mdns_register(lua_State *L)
{
size_t len = 0;
(void) process_args(L, PHASE_CALCULATE_LENGTH, &len);
struct ip_info ipconfig;
uint8_t mode = wifi_get_opmode();
if (!wifi_get_ip_info((mode == 2) ? SOFTAP_IF : STATION_IF, &ipconfig) || !ipconfig.ip.addr) {
return luaL_error(L, "No network connection");
}
wrapper_t *result = process_args(L, PHASE_COPY_DATA, &len);
if (!result) {
return luaL_error( L, "failed to allocate info block" );
}
result->mdns_info.ipAddr = ipconfig.ip.addr;
// Close up the old session (if any). This cannot fail
// so no chance of losing the memory in 'result'
mdns_close(L);
// Save the result as it appears that espconn_mdns_init needs
// to have the data valid while it is running.
info = result;
espconn_mdns_init(&(info->mdns_info));
return 0;
}
// Module function map
static const LUA_REG_TYPE mdns_map[] = {
{ LSTRKEY("register"), LFUNCVAL(mdns_register) },
{ LSTRKEY("close"), LFUNCVAL(mdns_close) },
{ LNILKEY, LNILVAL }
};
NODEMCU_MODULE(MDNS, "mdns", mdns_map, NULL);

38
docs/en/modules/mdns.md Normal file
View File

@ -0,0 +1,38 @@
# mdns Module
Multicast DNS is used as part of Bonjour / Zeroconf. This allows system to identify themselves and the services that they provide on a local area network. Clients are then
able to discover these systems and connect to them.
## mdns.register()
Register a hostname and start the mDns service. If the service is already running, then it
will be restarted with the new parameters.
#### Syntax
`mdns.register(hostname, servicename, port [, attributes])`
#### Parameters
- `hostname` The hostname for this device. Alphanumeric characters are best.
- `servicename` The servicename for this device. Alphanumeric characters are best. This will get prefixed with '_' and suffixed with ._tcp
- `port` The port number for the primary service.
- `attributes` A optional table of up to 10 attributes to be exposed. The keys must all be strings.
#### Returns
Nothing.
#### Errors
Various errors can be generated during argument validation. The nodemcu must have an IP address at the time of the call, otherwise an error is thrown.
#### Example
mdns.register("fishtank", "http", 80, { hardware='NodeMCU'})
Using dns-sd on OSX, you can see fishtank.local as providing the _http._tcp service. You can also browse directly to fishtank.local. In safari you can get all the mdns web pages as part of your bookmarks menu.
## mdns.close()
Shut down the mDns service. This is not normally needed.
#### Syntax
`mdns.close()`
#### Returns
Nothing.

View File

@ -49,6 +49,7 @@ pages:
- 'http': 'en/modules/http.md'
- 'hx711' : 'en/modules/hx711.md'
- 'i2c' : 'en/modules/i2c.md'
- 'mdns': 'en/modules/mdns.md'
- 'mqtt': 'en/modules/mqtt.md'
- 'net': 'en/modules/net.md'
- 'node': 'en/modules/node.md'