#include "Adafruit_NeoPixel.h"
#include "W600InnerFlash.h"
// tls_os_task_create
extern "C" {
#include "wm_osal.h"
}
// neopixel
#define PIN_NP PA_1
#define NP_NUM_X 3
#define NP_NUM_Y (NP_NUM_X)
#define NP_NUM_Z (NP_NUM_X)
#define NP_LED (NP_NUM_X * NP_NUM_Y * NP_NUM_Z)
Adafruit_NeoPixel np(NP_LED, PIN_NP, NEO_GRB + NEO_KHZ800);
const uint8_t brightness[] = { 0x07, 0x0f, 0x2f, 0x4f, 0x7f, 0xff };
#define NUM_BRIGHT (sizeof(brightness) / sizeof(brightness[0]))
// wdt
extern "C" {
#include "wm_watchdog.h"
}
#define WDT_SEC 5
#define PIN_PWM PA_0
// button
#define PIN_BTN_1 PB_18
#define PIN_BTN_2 PB_9 // PB_10
#define PIN_BTN_GND PB_7
#define BTN_TASK_STK_SIZE 512
#define BTN_TASK_PRIO (32 + 1)
static uint32_t btn_task_stack[BTN_TASK_STK_SIZE];
#define INPUT_PULLUP 0x02
#define INPUT_PULLDOWN 0x03
// flash
typedef enum { BRIGHT, MODE, _CONF_MAX } confDate_t;
uint8_t confData[_CONF_MAX];
#define FLASH_SECTOR 240 // 240-251
extern "C" {
#include "random.h"
}
class ledCube {
public:
ledCube(uint32_t nums_X, uint32_t nums_Y, uint32_t nums_Z) : numsX(nums_X), numsY(nums_Y), numsZ(nums_Z), numsXY(nums_X * nums_Y) {
cubeValue = (uint32_t *)malloc(sizeof(uint32_t) * nums_X * nums_Y * nums_Z);
randomize();
}
~ledCube() {
free(cubeValue);
}
uint32_t speedTimer = 5000;
uint32_t minInterval = 3;
uint32_t maxInterval = minInterval << 5;
uint32_t cubeMaxValue = 100;
int32_t minMag = 4 << 7;
int32_t maxMag = 11 << 7;
int32_t minDir = 2;
int32_t maxDir = 16;
const uint32_t numsX = 3;
const uint32_t numsY = 3;
const uint32_t numsZ = 3;
const uint32_t numsXY = numsX * numsY;
uint32_t *cubeValue = nullptr;
int32_t pos[3] = { 0, 0, 0 };
int32_t dir[3] = { 6, 6, 6 };
int32_t mag = maxMag;
int32_t dirMag = (maxMag - minMag) / 20;
uint32_t interval = maxInterval;
int32_t dirInterval = -1;
uint32_t lastChgPos = millis();
uint32_t lastChgSpd = millis();
uint32_t mode = 0;
uint16_t hue = 0;
void changePos() {
switch (mode) {
case 1:
case 2:
case 3:
case 4:
if (millis() - lastChgPos < interval) return; else lastChgPos = millis();
for (uint32_t z = 0; z < numsZ; z++) {
for (uint32_t y = 0; y < numsY; y++) {
for (uint32_t x = 0; x < numsX; x++) {
uint32_t dx = (x << 7) - pos[0];
uint32_t dy = (y << 7) - pos[1];
uint32_t dz = (z << 7) - pos[2];
uint32_t dist = ((dx * dx + dy * dy + dz * dz) * mag >> 14) * mag >> 14;
cubeValue[numsXY * z + numsX * y + x] = cubeMaxValue / (dist ? dist : 1);
}
}
}
for (uint32_t i = 0; i < 3; i++) {
if ((pos[i] += dir[i]) > (int32_t)((i ? i == 1 ? numsY : numsZ : numsX) - 1 << 7)) {
pos[i] = (i ? i == 1 ? numsY : numsZ : numsX) - 1 << 7;
dir[i] = -random(minDir, maxDir);
} else if (pos[i] < 0) {
pos[i] = 0;
dir[i] = random(minDir, maxDir);
}
}
if ((mag += dirMag) > maxMag) {
mag = maxMag;
dirMag = -random(minDir, maxDir);
} else if (mag < minMag) {
mag = minMag;
dirMag = random(minDir, maxDir);
}
break;
case 5: // rainbow
break;
case 6:
if (millis() - lastChgPos < interval << 4) return; else lastChgPos = millis();
clearCube();
for (uint32_t i = 0; i < numsXY * numsZ; i++) {
if (random() % 6) continue;
cubeValue[i] = 1 + random() % 100;
}
break;
default: // 0
clearCube();
cubeValue[0] = 10;
}
}
void changeSpd() {
if (mode != 1 && mode != 5 && mode != 6) return;
if (millis() - lastChgSpd < speedTimer) return; else lastChgSpd = millis();
if (dirInterval > 0 && (interval <<= 1) >= maxInterval) {
interval = maxInterval;
dirInterval = -1;
} else if (dirInterval < 0 && (interval >>= 1) <= minInterval) {
interval = minInterval;
dirInterval = 1;
}
}
void changeHue() {
hue += mode != 5 ? 0x0020 : interval << 2;
}
void clearCube() {
memset(cubeValue, 0, sizeof(uint32_t) * numsXY * numsZ);
}
void randomize() {
uint32_t buf = analogRead(0);
random_add_randomness((void *)&buf, sizeof(buf));
}
uint32_t random(uint32_t min = 0, uint32_t max = 0xffffffff) {
uint32_t buf;
random_get_bytes((void *)&buf, sizeof(buf));
return buf % (max - min) + min;
}
};
#define NUM_MODE 7
ledCube *cube;
void setup() {
tls_watchdog_init(WDT_SEC * 1000 * 1000);
pinMode(PIN_PWM, OUTPUT);
pinMode(PIN_BTN_1, INPUT_PULLUP);
pinMode(PIN_BTN_2, INPUT_PULLUP);
pinMode(PIN_BTN_GND, OUTPUT);
digitalWrite(PIN_BTN_GND, LOW);
InnerFlash.begin();
InnerFlash.flashRead(FLASH_SECTOR * 4096, &confData[0], sizeof(confData) / sizeof(confData[0])); // sector-size: 4096
np.begin();
setBrightness();
cube = new ledCube(NP_NUM_X, NP_NUM_Y, NP_NUM_Z);
cube->mode = confData[MODE];
setInterval();
tls_os_task_create(NULL, "btn", btn_task, (void *)0, (uint8_t *)&btn_task_stack, BTN_TASK_STK_SIZE * sizeof(uint32_t), BTN_TASK_PRIO, 0);
}
void loop() {
analogWrite(PIN_PWM, millis() & 0x0200 ? 255 : 252);
if (cube->mode == 5) np.rainbow(cube->hue, 1, 0x9f);
else {
for (uint32_t i = 0; i < cube->numsXY * cube->numsZ; i++) {
np.setPixelColor(i % cube->numsXY / cube->numsX % 2 ? cube->numsX * (i / cube->numsX + 1) - i % cube->numsX - 1 : i,
np.ColorHSV(cube->hue, 255, cube->cubeValue[i] * 0xff / cube->cubeMaxValue));
}
}
np.show();
cube->changePos();
cube->changeSpd();
cube->changeHue();
tls_watchdog_clr();
delay(1);
}
void setBrightness() { np.setBrightness(brightness[confData[BRIGHT] < NUM_BRIGHT ? confData[BRIGHT] : 0]); }
void setInterval() {
uint32_t blen = 0;
switch (confData[MODE]) {
case 2:
cube->interval = cube->maxInterval;
break;
case 3:
for (uint32_t i = cube->maxInterval; i > cube->minInterval; i >>= 1) blen++;
cube->interval = cube->minInterval << (blen >> 1);
break;
case 4:
cube->interval = cube->minInterval;
break;
}
}
void saveToFlash() { InnerFlash.flashEraseSector(FLASH_SECTOR); InnerFlash.flashWrite(FLASH_SECTOR * 4096, &confData[0], sizeof(confData) / sizeof(confData[0])); }
void btn_task(void *data) {
uint16_t btn1Count = 0;
uint16_t btn2Count = 0;
while (true) {
if (!digitalRead(PIN_BTN_1)) btn1Count++;
if (!digitalRead(PIN_BTN_2)) btn2Count++;
if (btn1Count && digitalRead(PIN_BTN_1)) {
if (++confData[BRIGHT] >= NUM_BRIGHT) confData[BRIGHT] = confData[MODE] < 5 ? 3 : 0;
saveToFlash();
setBrightness();
btn1Count = 0;
}
if (btn2Count && digitalRead(PIN_BTN_2)) {
if (++confData[MODE] >= NUM_MODE) confData[MODE] = 0;
if (confData[MODE] < 5 && confData[BRIGHT] < 3) {
confData[BRIGHT] = 3;
setBrightness();
}
saveToFlash();
cube->mode = confData[MODE];
setInterval();
btn2Count = 0;
}
tls_os_time_delay(HZ / 100);
}
}
本日届いたパーツの検品光景です、お収めください。「いぬにゃん おじょうさまちゃん(何も決まってない)」字はぴろ瀬先生(skeb)です、かわいい。8x8のneopixelこれで3枚になりました。縦8ドットでも読める文字を表示できるので満足です。w600ボードと合わせてusb給電で動作。今日もお疲れ様でした! pic.twitter.com/Nom1VYZIjL
— いぬにゃん (@inunyann) May 21, 2023
残りのw600とneopixel 8x8 LEDが届いたので光らせました、かわいい。名前(いぬにゃん)をスクロール表示させるのやってみたかった。世界広しといえど、w600でneopixel(ws2812)光らせたのは私だけでしょう(念のため検索したら私が出てきました)この8x8 LEDかわいいからもう1個買おうと思います。 pic.twitter.com/yashD3HVob
— いぬにゃん (@inunyann) May 11, 2023
#include
#include "Adafruit_NeoPixel.h"
#include "img2.h"
// neopixel
#define PIN_NP PB_12
#define NP_LED 64
Adafruit_NeoPixel np(NP_LED, PIN_NP, NEO_RGB + NEO_KHZ800);
#define PIN_NP2 PB_11
#define PIN_NP3 PB_10
Adafruit_NeoPixel np2(NP_LED, PIN_NP2, NEO_RGB + NEO_KHZ800);
Adafruit_NeoPixel np3(NP_LED, PIN_NP3, NEO_RGB + NEO_KHZ800);
uint16_t hue = 1 << 4;
uint8_t spd = 4;
uint16_t rainbowInterval = 8 * 1000;
uint32_t rainbowStart = 0;
uint16_t col = 0;
void setup() {
np.begin();
np.setBrightness(0x0f);
np2.begin();
np3.begin();
np2.setBrightness(0x0f);
np3.setBrightness(0x0f);
}
void loop() {
showLed();
}
void showLed() {
analogWrite(PIN_PWM, brightness);
if ((brightness += fadeAmount) < 5) fadeAmount = 0xff - fadeAmount - (fadeAmount < 0x80 ? (brightness -= fadeAmount) && 0 : 1);
if (!fadeAmount) fadeAmount = 5;
if (col == 0xffff) {
//np.rainbow(hue += spd << 5, 1, 0x9f);
rainbow2(&np, 1, 3, hue += spd << 5, 0x9f);
rainbow2(&np2, 2, 3, hue, 0x9f);
rainbow2(&np3, 3, 3, hue, 0x9f);
if (millis() - rainbowStart > rainbowInterval) col = 0;
} else {
for (uint8_t i = 0; i < 8 * 8; i++) {
np.setPixelColor(i, (img[ col * 8 + i] & 0x00ffffff));
np2.setPixelColor(i, (img[(col + 8) * 8 + i] & 0x00ffffff));
np3.setPixelColor(i, (img[(col + 16) * 8 + i] & 0x00ffffff));
}
if (++col > img_width - (8 * 3 + 1)) {
col = 0xffff;
rainbowStart = millis();
}
}
//np.rainbow(hue += spd << 5, 1, 0x9f);
np.show();
np2.show();
np3.show();
delay(col == 0xffff ? 10 : col == 0 ? 500 : 50);
//delay(10);
}
void rainbow2(Adafruit_NeoPixel *np, uint8_t n, uint8_t nMax, uint16_t first_hue, uint8_t saturation) {
for (uint16_t i = 0; i < np->numPixels(); i++) np->setPixelColor(i, np->gamma32(np->ColorHSV(first_hue + (((uint32_t)i + np->numPixels() * (n - 1)) * 65536 / nMax) / (np->numPixels() * nMax), saturation, 255)));
}
void Adafruit_NeoPixel::show(void) {
if (!pixels) return;
while (!canShow());
uint8_t *ptr, *end, p, bitMask;
uint32_t pinMask;
ptr = pixels;
end = ptr + numBytes;
p = *ptr++;
bitMask = 0x80;
//volatile uint32_t *GPIOA_DATA = (volatile uint32_t *) 0x40010C00; // HR_GPIOA_DATA
//volatile uint32_t *GPIOB_DATA = (volatile uint32_t *) 0x40011200; // HR_GPIOB_DATA
volatile uint32_t *GPIO_DATA = (volatile uint32_t *) (pin < 16 ? HR_GPIOA_DATA : HR_GPIOB_DATA);
pinMask = 1 << (pin < 16 ? pin : pin - 16);
uint32_t cpu_sr = tls_os_set_critical();
#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled
if (is800KHz) {
#endif
for (;;) {
if (p & bitMask) { // ONE
// High 800ns
*GPIO_DATA |= pinMask; // 300ns?
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;"); // 1nop = 12.5ns?
// Low 450ns
*GPIO_DATA &= ~pinMask;
asm("nop; nop; nop;");
} else { // ZERO
// High 400ns
*GPIO_DATA |= pinMask;
asm("nop; nop; nop; nop; nop; nop;");
// Low 850ns
*GPIO_DATA &= ~pinMask;
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;");
}
if (bitMask >>= 1) {
//asm("nop;");
asm("nop; nop; nop; nop; nop;");
} else {
if (ptr >= end) break;
p = *ptr++;
bitMask = 0x80;
}
}
#if defined(NEO_KHZ400)
} else { // 400 KHz bitstream
// ToDo!
}
#endif
tls_os_release_critical(cpu_sr);
endTime = micros(); // Save EOD time for latch on next call
}
#define HAS_SERVICE_REGISTRATION 1 // disabling saves about 1.25 kilobytes
#define HAS_NAME_BROWSING 1 // disable together with above, additionally saves about 4.3 kilobytes
#include
#include
#include
#include
extern "C" {
#include "EthernetUtil.h"
}
#include "MDNS.h"
#include
#define MDNS_DEFAULT_NAME "arduino"
#define MDNS_TLD ".local"
#define DNS_SD_SERVICE "_services._dns-sd._udp.local"
#define MDNS_SERVER_PORT (5353)
#define MDNS_NQUERY_RESEND_TIME (1000) // 1 second, name query resend timeout
#define MDNS_SQUERY_RESEND_TIME (10000) // 10 seconds, service query resend timeout
#define MDNS_RESPONSE_TTL (120) // two minutes (in seconds)
#define MDNS_MAX_SERVICES_PER_PACKET (6)
//#define _BROKEN_MALLOC_ 1
#undef _USE_MALLOC_
static uint8_t mdnsMulticastIPAddr[] = { 224, 0, 0, 251 };
//static uint8_t mdnsHWAddr[] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0xfb };
typedef enum _MDNSPacketType_t {
MDNSPacketTypeMyIPAnswer,
MDNSPacketTypeNoIPv6AddrAvailable,
MDNSPacketTypeServiceRecord,
MDNSPacketTypeServiceRecordRelease,
MDNSPacketTypeNameQuery,
MDNSPacketTypeServiceQuery,
} MDNSPacketType_t;
typedef struct _DNSHeader_t {
uint16_t xid;
uint8_t recursionDesired:1;
uint8_t truncated:1;
uint8_t authoritiveAnswer:1;
uint8_t opCode:4;
uint8_t queryResponse:1;
uint8_t responseCode:4;
uint8_t checkingDisabled:1;
uint8_t authenticatedData:1;
uint8_t zReserved:1;
uint8_t recursionAvailable:1;
uint16_t queryCount;
uint16_t answerCount;
uint16_t authorityCount;
uint16_t additionalCount;
} __attribute__((__packed__)) DNSHeader_t;
typedef enum _DNSOpCode_t {
DNSOpQuery = 0,
DNSOpIQuery = 1,
DNSOpStatus = 2,
DNSOpNotify = 4,
DNSOpUpdate = 5
} DNSOpCode_t;
// for some reason, I get data corruption issues with normal malloc() on arduino 0017
void* my_malloc(unsigned s)
{
#if defined(_BROKEN_MALLOC_)
char* b = (char*)malloc(s+2);
if (b)
b++;
return (void*)b;
#else
return malloc(s);
#endif
}
void my_free(void* ptr)
{
#if defined(_BROKEN_MALLOC_)
char* b = (char*)ptr;
if (b)
b--;
free(b);
#else
free(ptr);
#endif
}
MDNS::MDNS(UDP& udp)
{
memset(&this->_mdnsData, 0, sizeof(MDNSDataInternal_t));
memset(&this->_serviceRecords, 0, sizeof(this->_serviceRecords));
this->_udp = &udp;
this->_state = MDNSStateIdle;
// this->_sock = -1;
this->_name = NULL;
this->_resolveNames[0] = NULL;
this->_resolveNames[1] = NULL;
this->_lastAnnounceMillis = 0;
}
MDNS::~MDNS()
{
this->_udp->stop();
}
// return values:
// 1 on success
// 0 otherwise
int MDNS::begin(const IPAddress& ip, const char* name)
{
// if we were called very soon after the board was booted, we need to give the
// EthernetShield (WIZnet) some time to come up. Hence, we delay until millis() is at
// least 3000. This is necessary, so that if we need to add a service record directly
// after begin, the announce packet does not get lost in the bowels of the WIZnet chip.
while (millis() < 3000) delay(100);
_ipAddress = ip;
int statusCode = 0;
statusCode = this->setName(name);
//if (statusCode)
//statusCode = this->_udp->beginMulticast(mdnsMulticastIPAddr, MDNS_SERVER_PORT);
if (statusCode) {
ip4_addr_t mdns;
IP4_ADDR(&mdns, mdnsMulticastIPAddr[0], mdnsMulticastIPAddr[1], mdnsMulticastIPAddr[2], mdnsMulticastIPAddr[3]);
statusCode = igmp_joingroup(IP4_ADDR_ANY4, &mdns) == ERR_OK;
if (statusCode) this->_udp->begin(MDNS_SERVER_PORT);
}
return statusCode;
}
// return values:
// 1 on success
// 0 otherwise
int MDNS::begin(const IPAddress& ip)
{
return this->begin(ip, MDNS_DEFAULT_NAME);
}
// return values:
// 1 on success
// 0 otherwise
int MDNS::_initQuery(uint8_t idx, const char* name, unsigned long timeout)
{
int statusCode = 0;
if (NULL == this->_resolveNames[idx] && NULL != ((0==idx) ? (void*)this->_nameFoundCallback :
(void*)this->_serviceFoundCallback)) {
this->_resolveNames[idx] = (uint8_t*)name;
if (timeout)
this->_resolveTimeouts[idx] = millis() + timeout;
else
this->_resolveTimeouts[idx] = 0;
statusCode = (MDNSSuccess == this->_sendMDNSMessage(0,
0,
(idx == 0) ? MDNSPacketTypeNameQuery :
MDNSPacketTypeServiceQuery,
0));
} else
my_free((void*)name);
return statusCode;
}
void MDNS::_cancelQuery(uint8_t idx)
{
if (NULL != this->_resolveNames[idx]) {
my_free(this->_resolveNames[idx]);
this->_resolveNames[idx] = NULL;
}
}
// return values:
// 1 on success
// 0 otherwise
int MDNS::resolveName(const char* name, unsigned long timeout)
{
this->cancelResolveName();
char* n = (char*)my_malloc(strlen(name) + 7);
if (NULL == n)
return 0;
strcpy(n, name);
strcat(n, MDNS_TLD);
return this->_initQuery(0, n, timeout);
}
void MDNS::setNameResolvedCallback(MDNSNameFoundCallback newCallback)
{
this->_nameFoundCallback = newCallback;
}
void MDNS::cancelResolveName()
{
this->_cancelQuery(0);
}
int MDNS::isResolvingName()
{
return (NULL != this->_resolveNames[0]);
}
void MDNS::setServiceFoundCallback(MDNSServiceFoundCallback newCallback)
{
this->_serviceFoundCallback = newCallback;
}
// return values:
// 1 on success
// 0 otherwise
int MDNS::startDiscoveringService(const char* serviceName,
MDNSServiceProtocol_t proto,
unsigned long timeout)
{
this->stopDiscoveringService();
char* n = (char*)my_malloc(strlen(serviceName) + 13);
if (NULL == n)
return 0;
strcpy(n, serviceName);
const uint8_t* srv_type = this->_postfixForProtocol(proto);
if (srv_type)
strcat(n, (const char*)srv_type);
this->_resolveServiceProto = proto;
return this->_initQuery(1, n, timeout);
}
void MDNS::stopDiscoveringService()
{
this->_cancelQuery(1);
}
int MDNS::isDiscoveringService()
{
return (NULL != this->_resolveNames[1]);
}
// return value:
// A DNSError_t (DNSSuccess on success, something else otherwise)
// in "int" mode: positive on success, negative on error
MDNSError_t MDNS::_sendMDNSMessage(uint32_t /*peerAddress*/, uint32_t xid, int type,
int serviceRecord)
{
MDNSError_t statusCode = MDNSSuccess;
uint16_t ptr = 0;
#if defined(_USE_MALLOC_)
DNSHeader_t* dnsHeader = NULL;
#else
DNSHeader_t dnsHeaderBuf;
DNSHeader_t* dnsHeader = &dnsHeaderBuf;
#endif
uint8_t* buf;
#if defined(_USE_MALLOC_)
dnsHeader = (DNSHeader_t*)my_malloc(sizeof(DNSHeader_t));
if (NULL == dnsHeader) {
statusCode = MDNSOutOfMemory;
goto errorReturn;
}
#endif
memset(dnsHeader, 0, sizeof(DNSHeader_t));
dnsHeader->xid = ethutil_htons(xid);
dnsHeader->opCode = DNSOpQuery;
switch (type) {
case MDNSPacketTypeServiceRecordRelease:
case MDNSPacketTypeMyIPAnswer:
dnsHeader->answerCount = ethutil_htons(1);
dnsHeader->queryResponse = 1;
dnsHeader->authoritiveAnswer = 1;
break;
case MDNSPacketTypeServiceRecord:
dnsHeader->answerCount = ethutil_htons(4);
dnsHeader->additionalCount = ethutil_htons(1);
dnsHeader->queryResponse = 1;
dnsHeader->authoritiveAnswer = 1;
break;
case MDNSPacketTypeNameQuery:
case MDNSPacketTypeServiceQuery:
dnsHeader->queryCount = ethutil_htons(1);
break;
case MDNSPacketTypeNoIPv6AddrAvailable:
dnsHeader->queryCount = ethutil_htons(1);
dnsHeader->additionalCount = ethutil_htons(1);
dnsHeader->responseCode = 0x03;
dnsHeader->authoritiveAnswer = 1;
dnsHeader->queryResponse = 1;
break;
}
this->_udp->beginPacket(mdnsMulticastIPAddr,MDNS_SERVER_PORT);
this->_udp->write((uint8_t*)dnsHeader,sizeof(DNSHeader_t));
ptr += sizeof(DNSHeader_t);
buf = (uint8_t*)dnsHeader;
// construct the answer section
switch (type) {
case MDNSPacketTypeMyIPAnswer: {
this->_writeMyIPAnswerRecord(&ptr, buf, sizeof(DNSHeader_t));
break;
}
#if defined(HAS_SERVICE_REGISTRATION) && HAS_SERVICE_REGISTRATION
case MDNSPacketTypeServiceRecord: {
// SRV location record
this->_writeServiceRecordName(serviceRecord, &ptr, buf, sizeof(DNSHeader_t), 0);
buf[0] = 0x00;
buf[1] = 0x21; // SRV record
buf[2] = 0x80; // cache flush
buf[3] = 0x01; // class IN
// ttl
*((uint32_t*)&buf[4]) = ethutil_htonl(MDNS_RESPONSE_TTL);
// data length
*((uint16_t*)&buf[8]) = ethutil_htons(8 + strlen((char*)this->_name));
this->_udp->write((uint8_t*)buf,10);
ptr += 10;
// priority and weight
buf[0] = buf[1] = buf[2] = buf[3] = 0;
// port
*((uint16_t*)&buf[4]) = ethutil_htons(this->_serviceRecords[serviceRecord]->port);
this->_udp->write((uint8_t*)buf,6);
ptr += 6;
// target
this->_writeDNSName(this->_name, &ptr, buf, sizeof(DNSHeader_t), 1);
// TXT record
this->_writeServiceRecordName(serviceRecord, &ptr, buf, sizeof(DNSHeader_t), 0);
buf[0] = 0x00;
buf[1] = 0x10; // TXT record
buf[2] = 0x80; // cache flush
buf[3] = 0x01; // class IN
// ttl
*((uint32_t*)&buf[4]) = ethutil_htonl(MDNS_RESPONSE_TTL);
this->_udp->write((uint8_t*)buf,8);
ptr += 8;
// data length && text
if (NULL == this->_serviceRecords[serviceRecord]->textContent) {
buf[0] = 0x00;
buf[1] = 0x01;
buf[2] = 0x00;
this->_udp->write((uint8_t*)buf,3);
ptr += 3;
} else {
int slen = strlen((char*)this->_serviceRecords[serviceRecord]->textContent);
*((uint16_t*)buf) = ethutil_htons(slen);
this->_udp->write((uint8_t*)buf,2);
ptr += 2;
this->_udp->write((uint8_t*)this->_serviceRecords[serviceRecord]->textContent,slen);
ptr += slen;
}
// PTR record (for the DNS-SD service in general)
this->_writeDNSName((const uint8_t*)DNS_SD_SERVICE, &ptr, buf,
sizeof(DNSHeader_t), 1);
buf[0] = 0x00;
buf[1] = 0x0c; // PTR record
buf[2] = 0x00; // no cache flush
buf[3] = 0x01; // class IN
// ttl
*((uint32_t*)&buf[4]) = ethutil_htonl(MDNS_RESPONSE_TTL);
// data length.
uint16_t dlen = strlen((char*)this->_serviceRecords[serviceRecord]->servName) + 2;
*((uint16_t*)&buf[8]) = ethutil_htons(dlen);
this->_udp->write((uint8_t*)buf, 10);
ptr += 10;
this->_writeServiceRecordName(serviceRecord, &ptr, buf, sizeof(DNSHeader_t), 1);
// PTR record (our service)
this->_writeServiceRecordPTR(serviceRecord, &ptr, buf, sizeof(DNSHeader_t),
MDNS_RESPONSE_TTL);
// finally, our IP address as additional record
this->_writeMyIPAnswerRecord(&ptr, buf, sizeof(DNSHeader_t));
break;
}
case MDNSPacketTypeServiceRecordRelease: {
// just send our service PTR with a TTL of zero
this->_writeServiceRecordPTR(serviceRecord, &ptr, buf, sizeof(DNSHeader_t), 0);
break;
}
#endif // defined(HAS_SERVICE_REGISTRATION) && HAS_SERVICE_REGISTRATION
#if defined(HAS_NAME_BROWSING) && HAS_NAME_BROWSING
case MDNSPacketTypeNameQuery:
case MDNSPacketTypeServiceQuery:
{
// construct a query for the currently set _resolveNames[0]
this->_writeDNSName(
(type == MDNSPacketTypeServiceQuery) ? this->_resolveNames[1] :
this->_resolveNames[0],
&ptr, buf, sizeof(DNSHeader_t), 1);
buf[0] = buf[2] = 0x0;
buf[1] = (type == MDNSPacketTypeServiceQuery) ? 0x0c : 0x01;
buf[3] = 0x1;
this->_udp->write((uint8_t*)buf, sizeof(DNSHeader_t));
ptr += sizeof(DNSHeader_t);
this->_resolveLastSendMillis[(type == MDNSPacketTypeServiceQuery) ? 1 : 0] = millis();
break;
}
#endif // defined(HAS_NAME_BROWSING) && HAS_NAME_BROWSING
case MDNSPacketTypeNoIPv6AddrAvailable: {
// since the WIZnet doesn't have IPv6, we will respond with a Not Found message
this->_writeDNSName(this->_name, &ptr, buf, sizeof(DNSHeader_t), 1);
buf[0] = buf[2] = 0x0;
buf[1] = 0x1c; // AAAA record
buf[3] = 0x01;
this->_udp->write((uint8_t*)buf, 4);
ptr += 4;
// send our IPv4 address record as additional record, in case the peer wants it.
this->_writeMyIPAnswerRecord(&ptr, buf, sizeof(DNSHeader_t));
break;
}
}
this->_udp->endPacket();
#if defined(_USE_MALLOC_)
errorReturn:
if (NULL != dnsHeader)
my_free(dnsHeader);
#endif
return statusCode;
}
// return value:
// A DNSError_t (DNSSuccess on success, something else otherwise)
// in "int" mode: positive on success, negative on error
MDNSError_t MDNS::_processMDNSQuery()
{
MDNSError_t statusCode = MDNSSuccess;
#if defined(_USE_MALLOC_)
DNSHeader_t* dnsHeader = NULL;
#else
DNSHeader_t dnsHeaderBuf;
DNSHeader_t* dnsHeader = &dnsHeaderBuf;
#endif
unsigned int i, j;
uint8_t* buf;
uint32_t xid = 0;
uint16_t udp_len, qCnt, aCnt, aaCnt, addCnt;
uint8_t recordsAskedFor[NumMDNSServiceRecords+2];
uint8_t recordsFound[2];
uint8_t wantsIPv6Addr = 0;
uint8_t * udpBuffer = NULL;
uintptr_t ptr;
memset(recordsAskedFor, 0, sizeof(uint8_t)*(NumMDNSServiceRecords+2));
memset(recordsFound, 0, sizeof(uint8_t)*2);
udp_len = this->_udp->parsePacket();
if (0 == udp_len) {
statusCode = MDNSTryLater;
goto errorReturn;
}
//Serial.println("mdns len ok");
udpBuffer = (uint8_t*) my_malloc(udp_len); //allocate memory to hold _remaining UDP packet
if (NULL == udpBuffer) {
this->_udp->flush();
statusCode = MDNSOutOfMemory;
goto errorReturn;
}
this->_udp->read((uint8_t*)udpBuffer, udp_len);//read _remaining UDP packet from W5100/W5200 into memory
ptr = (uintptr_t)udpBuffer;
#if defined(_USE_MALLOC_)
dnsHeader = (DNSHeader_t*)my_malloc(sizeof(DNSHeader_t));
if (NULL == dnsHeader) {
statusCode = MDNSOutOfMemory;
goto errorReturn;
}
#endif
buf = (uint8_t*)dnsHeader;
memcpy((uint8_t*)buf, (uint16_t*)ptr ,sizeof(DNSHeader_t));
xid = ethutil_ntohs(dnsHeader->xid);
qCnt = ethutil_ntohs(dnsHeader->queryCount);
aCnt = ethutil_ntohs(dnsHeader->answerCount);
aaCnt = ethutil_ntohs(dnsHeader->authorityCount);
addCnt = ethutil_ntohs(dnsHeader->additionalCount);
if (0 == dnsHeader->queryResponse &&
DNSOpQuery == dnsHeader->opCode &&
MDNS_SERVER_PORT == this->_udp->remotePort())
{
// process an MDNS query
int offset = sizeof(DNSHeader_t);
uint8_t* buf = (uint8_t*)dnsHeader;
int rLen = 0, tLen = 0;
// read over the query section
for (i=0; i_name;
servNamePos[0] = 0;
servLens[0] = strlen((char*)this->_name);
servMatches[0] = 1;
// second entry is our own the general DNS-SD service
servNames[1] = (const uint8_t*)DNS_SD_SERVICE;
servNamePos[1] = 0;
servLens[1] = strlen((char*)DNS_SD_SERVICE);
servMatches[1] = 1;
for (j=2; j_serviceRecords[j-2] && NULL != this->_serviceRecords[j-2]->servName) {
servNames[j] = this->_serviceRecords[j-2]->servName;
servLens[j] = strlen((char*)servNames[j]);
servMatches[j] = 1;
servNamePos[j] = 0;
} else {
servNames[j] = NULL;
servLens[j] = 0;
servMatches[j] = 0;
servNamePos[j] = 0;
}
tLen = 0;
do {
memcpy((uint8_t*)buf, (uint16_t*)(ptr+offset) ,1);
offset += 1;
rLen = buf[0];
tLen += 1;
if (rLen > 128) {// handle DNS name compression, kinda, sorta
memcpy((uint8_t*)buf, (uint16_t*)(ptr+offset) ,1);
offset += 1;
for (j=0; j 0) {
int tr = rLen, ir;
while (tr > 0) {
ir = (tr > (int)sizeof(DNSHeader_t)) ? sizeof(DNSHeader_t) : tr;
memcpy((uint8_t*)buf, (uint16_t*)(ptr+offset) ,ir);
offset += ir;
tr -= ir;
for (j=0; j_matchStringPart(&servNames[j], &servLens[j], buf,
ir);
}
}
tLen += rLen;
}
} while (rLen > 0 && rLen <= 128);
// if this matched a name of ours (and there are no characters left), then
// check whether this is an A record query (for our own name) or a PTR record query
// (for one of our services).
// if so, we'll note to send a record
memcpy((uint8_t*)buf, (uint16_t*)(ptr+offset) ,4);
offset += 4;
for (j=0; jqueryResponse &&
DNSOpQuery == dnsHeader->opCode &&
MDNS_SERVER_PORT == this->_udp->remotePort() &&
(NULL != this->_resolveNames[0] || NULL != this->_resolveNames[1]))
{
int offset = sizeof(DNSHeader_t);
uint8_t* buf = (uint8_t*)dnsHeader;
int rLen = 0, tLen = 0;
uint8_t* ptrNames[MDNS_MAX_SERVICES_PER_PACKET];
uint16_t ptrOffsets[MDNS_MAX_SERVICES_PER_PACKET];
uint16_t ptrPorts[MDNS_MAX_SERVICES_PER_PACKET];
uint8_t ptrIPs[MDNS_MAX_SERVICES_PER_PACKET];
uint8_t servIPs[MDNS_MAX_SERVICES_PER_PACKET][5];
uint8_t* servTxt[MDNS_MAX_SERVICES_PER_PACKET];
memset(servIPs, 0, sizeof(uint8_t)*MDNS_MAX_SERVICES_PER_PACKET*5);
memset(servTxt, 0, sizeof(uint8_t*)*MDNS_MAX_SERVICES_PER_PACKET);
const uint8_t* ptrNamesCmp[MDNS_MAX_SERVICES_PER_PACKET];
int ptrLensCmp[MDNS_MAX_SERVICES_PER_PACKET];
uint8_t ptrNamesMatches[MDNS_MAX_SERVICES_PER_PACKET];
uint8_t checkAARecords = 0;
memset(ptrNames, 0, sizeof(uint8_t*)*MDNS_MAX_SERVICES_PER_PACKET);
const uint8_t* servNames[2];
uint8_t servNamePos[2];
int servLens[2];
uint8_t servMatches[2];
uint8_t firstNamePtrByte = 0;
uint8_t partMatched[2];
uint8_t lastWasCompressed[2];
uint8_t servWasCompressed[2];
servNamePos[0] = servNamePos[1] = 0;
for (i=0; i<(unsigned int)(qCnt+aCnt+aaCnt+addCnt); i++) {
for (j=0; j<2; j++) {
if (NULL != this->_resolveNames[j]) {
servNames[j] = this->_resolveNames[j];
servLens[j] = strlen((const char*)this->_resolveNames[j]);
servMatches[j] = 1;
} else {
servNames[j] = NULL;
servLens[j] = servMatches[j] = 0;
}
}
for (j=0; j 128) { // handle DNS name compression, kinda, sorta...
memcpy((uint8_t*)buf, (uint16_t*)(ptr+offset) ,1);
offset += 1;
for (j=0; j<2; j++) {
if (servNamePos[j] && servNamePos[j] != buf[0])
servMatches[j] = 0;
else
servWasCompressed[j] = 1;
lastWasCompressed[j] = 1;
}
tLen += 1;
if (0 == firstNamePtrByte)
firstNamePtrByte = buf[0];
} else if (rLen > 0) {
if (i < qCnt)
offset += rLen;
else {
int tr = rLen, ir;
if (0 == firstNamePtrByte)
firstNamePtrByte = offset-1; // -1, since we already read length (1 byte)
while (tr > 0) {
ir = (tr > (int)sizeof(DNSHeader_t)) ? sizeof(DNSHeader_t) : tr;
memcpy((uint8_t*)buf, (uint16_t*)(ptr+offset) ,ir);
offset += ir;
tr -= ir;
for (j=0; j<2; j++) {
if (!recordsFound[j] && servMatches[j] && servNames[j])
servMatches[j] &= this->_matchStringPart(&servNames[j], &servLens[j],
buf, ir);
if (!partMatched[j])
partMatched[j] = servMatches[j];
lastWasCompressed[j] = 0;
}
for (j=0; j= ir)
ptrNamesMatches[j] &= this->_matchStringPart(&ptrNamesCmp[j],
&ptrLensCmp[j], buf, ir);
}
}
}
tLen += rLen;
}
}
} while (rLen > 0 && rLen <= 128);
// if this matched a name of ours (and there are no characters left), then
// check whether this is an A record query (for our own name) or a PTR record query
// (for one of our services).
// if so, we'll note to send a record
if (i < qCnt)
offset += 4;
else if (i >= qCnt) {
if (i >= qCnt + aCnt && !checkAARecords)
break;
uint8_t packetHandled = 0;
memcpy((uint8_t*)buf, (uint16_t*)(ptr+offset) ,4);
offset += 4;
if (i < qCnt+aCnt) {
for (j=0; j<2; j++) {
if (0 == servNamePos[j])
servNamePos[j] = offset - 4 - tLen;
if (servNames[j] &&
((servMatches[j] && 0 == servLens[j]) ||
(partMatched[j] && lastWasCompressed[j]) ||
(servWasCompressed[j] && servMatches[j]))) { // somewhat handle compression by guessing
if (buf[0] == 0 && buf[1] == ((0 == j) ? 0x01 : 0x0c) &&
(buf[2] == 0x00 || buf[2] == 0x80) && buf[3] == 0x01) {
recordsFound[j] = 1;
// this is an A or PTR type response. Parse it as such.
memcpy((uint8_t*)buf, (uint16_t*)(ptr+offset) ,6);
offset += 6;
//uint32_t ttl = ethutil_ntohl(*(uint32_t*)buf);
uint16_t dataLen = ethutil_ntohs(*(uint16_t*)&buf[4]);
if (0 == j && 4 == dataLen) {
// ok, this is the IP address. report it via callback.
memcpy((uint8_t*)buf, (uint16_t*)(ptr+offset) ,4);
this->_finishedResolvingName((char*)this->_resolveNames[0],
(const byte*)buf);
} else if (1 == j) {
uint8_t k;
for (k=0; k= (unsigned int)(qCnt+aCnt+aaCnt)) {
// check whether we find a service description
if (buf[1] == 0x21) {
for (j=0; j= 8) {
memcpy((uint8_t*)buf, (uint16_t*)(ptr+offset) ,8);
ptrPorts[j] = ethutil_ntohs(*(uint16_t*)&buf[4]);
if (buf[6] > 128) { // target is a compressed name
ptrIPs[j] = buf[7];
} else { // target is uncompressed
ptrIPs[j] = offset+6;
}
}
offset += dataLen;
packetHandled = 1;
break;
}
}
} else if (buf[1] == 0x10) { // txt record
for (j=0; j 1 && NULL == servTxt[j]) {
servTxt[j] = (uint8_t*)my_malloc(dataLen+1);
if (NULL != servTxt[j]) {
memcpy((uint8_t*)servTxt[j], (uint16_t*)(ptr+offset) ,dataLen);
// zero-terminate
servTxt[j][dataLen] = '\0';
}
}
offset += dataLen;
packetHandled = 1;
break;
}
}
} else if (buf[1] == 0x01) { // A record (IPv4 address)
for (j=0; j_resolveNames[1]) {
char* typeName = (char*)this->_resolveNames[1];
char* p = (char*)this->_resolveNames[1];
while(*p && *p != '.')
p++;
*p = '\0';
for (i=0; i_serviceFoundCallback) {
this->_serviceFoundCallback(typeName,
this->_resolveServiceProto,
(const char*)ptrNames[i],
IPAddress((const byte*)ipAddr),
(unsigned short)ptrPorts[i],
(const char*)servTxt[i]);
}
}
*p = '.';
}
uint8_t k;
for (k=0; k_sendMDNSMessage(this->_udp->remoteIP(), xid, (int)MDNSPacketTypeMyIPAnswer, 0);
else if (1 == j) {
uint8_t k = 2;
for (k=0; k_serviceRecords[j-2])
(void)this->_sendMDNSMessage(this->_udp->remoteIP(), xid, (int)MDNSPacketTypeServiceRecord, j-2);
}
}
// if we were asked for our IPv6 address, say that we don't have any
if (wantsIPv6Addr)
(void)this->_sendMDNSMessage(this->_udp->remoteIP(), xid, (int)MDNSPacketTypeNoIPv6AddrAvailable, 0);
return statusCode;
}
void MDNS::run()
{
uint8_t i;
unsigned long now = millis();
// first, look for mDNS queries to handle
(void)_processMDNSQuery();
// are we querying a name or service? if so, should we resend the packet or time out?
for (i=0; i<2; i++) {
if (NULL != this->_resolveNames[i]) {
// Hint: _resolveLastSendMillis is updated in _sendMDNSMessage
if (now - this->_resolveLastSendMillis[i] > ((i == 0) ? (uint32_t)MDNS_NQUERY_RESEND_TIME :
(uint32_t)MDNS_SQUERY_RESEND_TIME))
(void)this->_sendMDNSMessage(0,
0,
(0 == i) ? MDNSPacketTypeNameQuery :
MDNSPacketTypeServiceQuery,
0);
if (this->_resolveTimeouts[i] > 0 && now > this->_resolveTimeouts[i]) {
if (i == 0)
this->_finishedResolvingName((char*)this->_resolveNames[0], NULL);
else if (i == 1) {
if (this->_serviceFoundCallback) {
char* typeName = (char*)this->_resolveNames[1];
char* p = (char*)this->_resolveNames[1];
while(*p && *p != '.')
p++;
*p = '\0';
this->_serviceFoundCallback(typeName,
this->_resolveServiceProto,
NULL,
IPAddress(),
0,
NULL);
}
}
if (NULL != this->_resolveNames[i]) {
my_free(this->_resolveNames[i]);
this->_resolveNames[i] = NULL;
}
}
}
}
// now, should we re-announce our services again?
unsigned long announceTimeOut = (((uint32_t)MDNS_RESPONSE_TTL/2)+((uint32_t)MDNS_RESPONSE_TTL/4));
if ((now - this->_lastAnnounceMillis) > 1000*announceTimeOut) {
for (i=0; i_serviceRecords[i])
(void)this->_sendMDNSMessage(0, 0, (int)MDNSPacketTypeServiceRecord, i);
}
this->_lastAnnounceMillis = now;
}
}
// return values:
// 1 on success
// 0 otherwise
int MDNS::setName(const char* name)
{
if (NULL == name)
return 0;
if (this->_name != NULL)
my_free(this->_name);
this->_name = (uint8_t*)my_malloc(strlen(name) + 7);
if (NULL == this->_name)
return 0;
strcpy((char*)this->_name, name);
strcpy((char*)this->_name+strlen(name), MDNS_TLD);
return 1;
}
// return values:
// 1 on success
// 0 otherwise
int MDNS::addServiceRecord(const char* name, uint16_t port,
MDNSServiceProtocol_t proto)
{
#if defined(__MK20DX128__) || defined(__MK20DX256__)
return this->addServiceRecord(name, port, proto, NULL); //works for Teensy 3 (32-bit Arm Cortex)
#else
return this->addServiceRecord(name, port, proto, ""); //works for Teensy 2 (8-bit Atmel)
#endif
}
// return values:
// 1 on success
// 0 otherwise
int MDNS::addServiceRecord(const char* name, uint16_t port,
MDNSServiceProtocol_t proto, const char* textContent)
{
int i, status = 0;
MDNSServiceRecord_t* record = NULL;
if (NULL != name && 0 != port) {
for (i=0; i < NumMDNSServiceRecords; i++) {
if (NULL == this->_serviceRecords[i]) {
record = (MDNSServiceRecord_t*)my_malloc(sizeof(MDNSServiceRecord_t));
if (NULL != record) {
record->name = record->textContent = NULL;
record->name = (uint8_t*)my_malloc(strlen((char*)name));
if (NULL == record->name)
goto errorReturn;
if (NULL != textContent) {
record->textContent = (uint8_t*)my_malloc(strlen((char*)textContent));
if (NULL == record->textContent)
goto errorReturn;
strcpy((char*)record->textContent, textContent);
}
record->port = port;
record->proto = proto;
strcpy((char*)record->name, name);
uint8_t* s = this->_findFirstDotFromRight(record->name);
record->servName = (uint8_t*)my_malloc(strlen((char*)s) + 12);
if (record->servName) {
strcpy((char*)record->servName, (const char*)s);
const uint8_t* srv_type = this->_postfixForProtocol(proto);
if (srv_type)
strcat((char*)record->servName, (const char*)srv_type);
}
this->_serviceRecords[i] = record;
status = (MDNSSuccess ==
this->_sendMDNSMessage(0, 0, (int)MDNSPacketTypeServiceRecord, i));
break;
}
}
}
}
return status;
errorReturn:
if (NULL != record) {
if (NULL != record->name)
my_free(record->name);
if (NULL != record->servName)
my_free(record->servName);
if (NULL != record->textContent)
my_free(record->textContent);
my_free(record);
}
return 0;
}
void MDNS::_removeServiceRecord(int idx)
{
if (NULL != this->_serviceRecords[idx]) {
(void)this->_sendMDNSMessage(0, 0, (int)MDNSPacketTypeServiceRecordRelease, idx);
if (NULL != this->_serviceRecords[idx]->textContent)
my_free(this->_serviceRecords[idx]->textContent);
if (NULL != this->_serviceRecords[idx]->servName)
my_free(this->_serviceRecords[idx]->servName);
my_free(this->_serviceRecords[idx]->name);
my_free(this->_serviceRecords[idx]);
this->_serviceRecords[idx] = NULL;
}
}
void MDNS::removeServiceRecord(uint16_t port, MDNSServiceProtocol_t proto)
{
this->removeServiceRecord(NULL, port, proto);
}
void MDNS::removeServiceRecord(const char* name, uint16_t port,
MDNSServiceProtocol_t proto)
{
int i;
for (i=0; i_serviceRecords[i]->port &&
proto == this->_serviceRecords[i]->proto &&
(NULL == name || 0 == strcmp((char*)this->_serviceRecords[i]->name, name))) {
this->_removeServiceRecord(i);
break;
}
}
void MDNS::removeAllServiceRecords()
{
int i;
for (i=0; i_removeServiceRecord(i);
}
void MDNS::_writeDNSName(const uint8_t* name, uint16_t* pPtr,
uint8_t* buf, int bufSize, int zeroTerminate)
{
uint16_t ptr = *pPtr;
uint8_t* p1 = (uint8_t*)name, *p2, *p3;
int i, c, len;
while(*p1) {
c = 1;
p2 = p1;
while (0 != *p2 && '.' != *p2) { p2++; c++; };
p3 = buf;
i = c;
len = bufSize-1;
*p3++ = (uint8_t)--i;
while (i-- > 0) {
*p3++ = *p1++;
if (--len <= 0) {
this->_udp->write((uint8_t*)buf, bufSize);
ptr += bufSize;
len = bufSize;
p3 = buf;
}
}
while ('.' == *p1)
++p1;
if (len != bufSize) {
this->_udp->write((uint8_t*)buf, bufSize-len);
ptr += bufSize-len;
}
}
if (zeroTerminate) {
buf[0] = 0;
this->_udp->write((uint8_t*)buf, 1);
ptr += 1;
}
*pPtr = ptr;
}
void MDNS::_writeMyIPAnswerRecord(uint16_t* pPtr, uint8_t* buf, int bufSize)
{
uint16_t ptr = *pPtr;
this->_writeDNSName(this->_name, &ptr, buf, bufSize, 1);
buf[0] = 0x00;
buf[1] = 0x01;
buf[2] = 0x80; // cache flush: true
buf[3] = 0x01;
this->_udp->write((uint8_t*)buf, 4);
ptr += 4;
*((uint32_t*)buf) = ethutil_htonl(MDNS_RESPONSE_TTL);
*((uint16_t*)&buf[4]) = ethutil_htons(4); // data length
uint8_t myIp[4];
myIp[0] = _ipAddress [0];
myIp[1] = _ipAddress [1];
myIp[2] = _ipAddress [2];
myIp[3] = _ipAddress [3];
memcpy(&buf[6], &myIp, 4); // our IP address
this->_udp->write((uint8_t*)buf, 10);
ptr += 10;
*pPtr = ptr;
}
void MDNS::_writeServiceRecordName(int recordIndex, uint16_t* pPtr, uint8_t* buf,
int bufSize, int tld)
{
uint16_t ptr = *pPtr;
uint8_t* name = tld ? this->_serviceRecords[recordIndex]->servName :
this->_serviceRecords[recordIndex]->name;
this->_writeDNSName(name, &ptr, buf, bufSize, tld);
if (0 == tld) {
const uint8_t* srv_type =
this->_postfixForProtocol(this->_serviceRecords[recordIndex]->proto);
if (NULL != srv_type) {
srv_type++; // eat the dot at the beginning
this->_writeDNSName(srv_type, &ptr, buf, bufSize, 1);
}
}
*pPtr = ptr;
}
void MDNS::_writeServiceRecordPTR(int recordIndex, uint16_t* pPtr, uint8_t* buf,
int bufSize, uint32_t ttl)
{
uint16_t ptr = *pPtr;
this->_writeServiceRecordName(recordIndex, &ptr, buf, bufSize, 1);
buf[0] = 0x00;
buf[1] = 0x0c; // PTR record
buf[2] = 0x00; // no cache flush
buf[3] = 0x01; // class IN
// ttl
*((uint32_t*)&buf[4]) = ethutil_htonl(ttl);
// data length (+13 = "._tcp.local" or "._udp.local" + 1 byte zero termination)
*((uint16_t*)&buf[8]) =
ethutil_htons(strlen((char*)this->_serviceRecords[recordIndex]->name) + 13);
this->_udp->write((uint8_t*)buf, 10);
ptr += 10;
this->_writeServiceRecordName(recordIndex, &ptr, buf, bufSize, 0);
*pPtr = ptr;
}
uint8_t* MDNS::_findFirstDotFromRight(const uint8_t* str)
{
const uint8_t* p = str + strlen((char*)str);
while (p > str && '.' != *p--);
return (uint8_t*)&p[2];
}
int MDNS::_matchStringPart(const uint8_t** pCmpStr, int* pCmpLen, const uint8_t* buf,
int dataLen)
{
int matches = 1;
if (*pCmpLen >= dataLen)
matches &= (0 == memcmp(*pCmpStr, buf, dataLen));
else
matches = 0;
*pCmpStr += dataLen;
*pCmpLen -= dataLen;
if ('.' == **pCmpStr)
(*pCmpStr)++, (*pCmpLen)--;
return matches;
}
const uint8_t* MDNS::_postfixForProtocol(MDNSServiceProtocol_t proto)
{
const uint8_t* srv_type = NULL;
switch(proto) {
case MDNSServiceTCP:
srv_type = (uint8_t*)"._tcp" MDNS_TLD;
break;
case MDNSServiceUDP:
srv_type = (uint8_t*)"._udp" MDNS_TLD;
break;
}
return srv_type;
}
void MDNS::_finishedResolvingName(char* name, const byte ipAddr[4])
{
if (NULL != this->_nameFoundCallback) {
if (NULL != name) {
uint8_t* n = this->_findFirstDotFromRight((const uint8_t*)name);
*(n-1) = '\0';
}
this->_nameFoundCallback((const char*)name, IPAddress(ipAddr));
}
my_free(this->_resolveNames[0]);
this->_resolveNames[0] = NULL;
}
#if !defined(__MDNS_H__)
#define __MDNS_H__ 1
extern "C" {
#include
}
#include
typedef uint8_t byte;
typedef enum _MDNSState_t {
MDNSStateIdle,
MDNSStateQuerySent
} MDNSState_t;
typedef enum _MDNSError_t {
MDNSTryLater = 3,
MDNSNothingToDo = 2,
MDNSSuccess = 1,
MDNSInvalidArgument = -1,
MDNSOutOfMemory = -2,
MDNSSocketError = -3,
MDNSAlreadyProcessingQuery = -4,
MDNSNotFound = -5,
MDNSServerError = -6,
MDNSTimedOut = -7
} MDNSError_t;
typedef struct _MDNSDataInternal_t {
uint32_t xid;
uint32_t lastQueryFirstXid;
} MDNSDataInternal_t;
typedef enum _MDNSServiceProtocol_t {
MDNSServiceTCP,
MDNSServiceUDP
} MDNSServiceProtocol_t;
typedef MDNSServiceProtocol_t MDNSServiceProtocol;
typedef struct _MDNSServiceRecord_t {
uint16_t port;
MDNSServiceProtocol_t proto;
uint8_t* name;
uint8_t* servName;
uint8_t* textContent;
} MDNSServiceRecord_t;
typedef void (*MDNSNameFoundCallback)(const char*, IPAddress);
typedef void (*MDNSServiceFoundCallback)(const char*, MDNSServiceProtocol_t, const char*, IPAddress, unsigned short, const char*);
#define NumMDNSServiceRecords (8)
//class MDNS
class MDNS {
private:
UDP* _udp;
IPAddress _ipAddress;
MDNSDataInternal_t _mdnsData;
MDNSState_t _state;
uint8_t* _name;
MDNSServiceRecord_t* _serviceRecords[NumMDNSServiceRecords];
unsigned long _lastAnnounceMillis;
uint8_t* _resolveNames[2];
unsigned long _resolveLastSendMillis[2];
unsigned long _resolveTimeouts[2];
MDNSServiceProtocol_t _resolveServiceProto;
MDNSNameFoundCallback _nameFoundCallback;
MDNSServiceFoundCallback _serviceFoundCallback;
MDNSError_t _processMDNSQuery();
MDNSError_t _sendMDNSMessage(uint32_t peerAddress, uint32_t xid, int type, int serviceRecord);
void _writeDNSName(const uint8_t* name, uint16_t* pPtr, uint8_t* buf, int bufSize, int zeroTerminate);
void _writeMyIPAnswerRecord(uint16_t* pPtr, uint8_t* buf, int bufSize);
void _writeServiceRecordName(int recordIndex, uint16_t* pPtr, uint8_t* buf, int bufSize, int tld);
void _writeServiceRecordPTR(int recordIndex, uint16_t* pPtr, uint8_t* buf, int bufSize, uint32_t ttl);
int _initQuery(uint8_t idx, const char* name, unsigned long timeout);
void _cancelQuery(uint8_t idx);
uint8_t* _findFirstDotFromRight(const uint8_t* str);
void _removeServiceRecord(int idx);
int _matchStringPart(const uint8_t** pCmpStr, int* pCmpLen, const uint8_t* buf, int dataLen);
const uint8_t* _postfixForProtocol(MDNSServiceProtocol_t proto);
void _finishedResolvingName(char* name, const byte ipAddr[4]);
public:
MDNS(UDP& udp);
~MDNS();
int begin(const IPAddress& ip);
int begin(const IPAddress& ip, const char* name);
void run();
int setName(const char* name);
int addServiceRecord(const char* name, uint16_t port, MDNSServiceProtocol_t proto);
int addServiceRecord(const char* name, uint16_t port, MDNSServiceProtocol_t proto, const char* textContent);
void removeServiceRecord(uint16_t port, MDNSServiceProtocol_t proto);
void removeServiceRecord(const char* name, uint16_t port, MDNSServiceProtocol_t proto);
void removeAllServiceRecords();
void setNameResolvedCallback(MDNSNameFoundCallback newCallback);
int resolveName(const char* name, unsigned long timeout);
void cancelResolveName();
int isResolvingName();
void setServiceFoundCallback(MDNSServiceFoundCallback newCallback);
int startDiscoveringService(const char* serviceName, MDNSServiceProtocol_t proto, unsigned long timeout);
void stopDiscoveringService();
int isDiscoveringService();
};
#endif // __MDNS_H__
#include "NetBIOS.h"
#include
#define NBNS_PORT 137
#define NBNS_MAX_HOSTNAME_LEN 32
typedef struct {
uint16_t id;
uint8_t flags1;
uint8_t flags2;
uint16_t qcount;
uint16_t acount;
uint16_t nscount;
uint16_t adcount;
uint8_t name_len;
char name[NBNS_MAX_HOSTNAME_LEN + 1];
uint16_t type;
uint16_t clas;
} __attribute__((packed)) nbns_question_t;
typedef struct {
uint16_t id;
uint8_t flags1;
uint8_t flags2;
uint16_t qcount;
uint16_t acount;
uint16_t nscount;
uint16_t adcount;
uint8_t name_len;
char name[NBNS_MAX_HOSTNAME_LEN + 1];
uint16_t type;
uint16_t clas;
uint32_t ttl;
uint16_t data_len;
uint16_t flags;
uint32_t addr;
} __attribute__((packed)) nbns_answer_t;
#undef _USE_MALLOC_
// for some reason, I get data corruption issues with normal malloc() on arduino 0017
void *sf_malloc(unsigned s) {
#if defined(_BROKEN_MALLOC_)
char* b = (char *)malloc(s + 2);
if (b) b++;
return (void *)b;
#else
return malloc(s);
#endif
}
void sf_free(void *ptr) {
#if defined(_BROKEN_MALLOC_)
char *b = (char *)ptr;
if (b) b--;
free(b);
#else
free(ptr);
#endif
}
static void _getnbname(const char *nbname, char *name, uint8_t maxlen) {
uint8_t b;
uint8_t c = 0;
while ((*nbname) && (c < maxlen)) {
b = (*nbname++ - 'A') << 4;
c++;
if (*nbname) {
b |= *nbname++ - 'A';
c++;
}
if(!b || b == ' ') break;
*name++ = b;
}
*name = 0;
}
static void append_16(void * dst, uint16_t value) {
uint8_t * d = (uint8_t *)dst;
*d++ = (value >> 8) & 0xFF;
*d++ = value & 0xFF;
}
static void append_32(void * dst, uint32_t value) {
uint8_t * d = (uint8_t *)dst;
*d++ = (value >> 24) & 0xFF;
*d++ = (value >> 16) & 0xFF;
*d++ = (value >> 8) & 0xFF;
*d++ = value & 0xFF;
}
void NetBIOS::_processUdpQuery() {
uint16_t udp_len;
uint8_t *udpBuffer;
if (!(udp_len = _udp.parsePacket())) return;
if (!(udpBuffer = (uint8_t *)sf_malloc(udp_len))) {
_udp.flush();
return;
}
_udp.read(udpBuffer, udp_len);
if (udp_len >= sizeof(nbns_question_t)) {
nbns_question_t *question = (nbns_question_t *)udpBuffer;
if (0 == (question->flags1 & 0x80)) {
char name[NBNS_MAX_HOSTNAME_LEN + 1];
_getnbname(&question->name[0], (char *)&name, question->name_len);
if (_name.equals(name)) {
nbns_answer_t nbnsa;
nbnsa.id = question->id;
nbnsa.flags1 = 0x85;
nbnsa.flags2 = 0;
append_16((void *)&nbnsa.qcount, 0);
append_16((void *)&nbnsa.acount, 1);
append_16((void *)&nbnsa.nscount, 0);
append_16((void *)&nbnsa.adcount, 0);
nbnsa.name_len = question->name_len;
memcpy(&nbnsa.name[0], &question->name[0], question->name_len + 1);
append_16((void *)&nbnsa.type, 0x20);
append_16((void *)&nbnsa.clas, 1);
//append_32((void *)&nbnsa.ttl, 300000);
append_32((void *)&nbnsa.ttl, 120);
append_16((void *)&nbnsa.data_len, 6);
append_16((void *)&nbnsa.flags, 0);
nbnsa.addr = WiFi.localIP();
_udp.beginPacket(_udp.remoteIP(), NBNS_PORT);
_udp.write((uint8_t *)&nbnsa, sizeof(nbnsa));
_udp.endPacket();
}
}
}
sf_free(udpBuffer);
}
NetBIOS::NetBIOS() {}
NetBIOS::~NetBIOS() {
end();
}
bool NetBIOS::begin(const char *name){
_name = name;
_name.toUpperCase();
return _udp.begin(NBNS_PORT);
}
void NetBIOS::run(){
_processUdpQuery();
}
void NetBIOS::end(){
_udp.stopAll();
}
#ifndef __NBNS_h__
#define __NBNS_h__
#include
#include
class NetBIOS {
protected:
WiFiUDP _udp;
String _name;
void _processUdpQuery();
public:
NetBIOS();
~NetBIOS();
bool begin(const char *name);
void run();
void end();
};
#endif
#include "Ticker.h"
Ticker::Ticker() :
_timer(WM_TIMER_ID_INVALID) {}
Ticker::~Ticker() {
detach();
}
void Ticker::_attach_ms(uint32_t milliseconds, bool repeat, tls_timer_irq_callback callback, uint32_t arg) {
tls_timer_cfg _timerConfig;
_timerConfig.unit = TLS_TIMER_UNIT_MS; // or TLS_TIMER_UNIT_US
_timerConfig.timeout = milliseconds;
_timerConfig.is_repeat = repeat;
_timerConfig.callback = callback;
_timerConfig.arg = reinterpret_cast(arg);
if (_timer != WM_TIMER_ID_INVALID) {
tls_timer_stop(_timer);
tls_timer_destroy(_timer);
}
_timer = tls_timer_create(&_timerConfig);
tls_timer_start(_timer);
}
void Ticker::detach() {
if (_timer != WM_TIMER_ID_INVALID) {
tls_timer_stop(_timer);
tls_timer_destroy(_timer);
_timer = WM_TIMER_ID_INVALID;
}
}
bool Ticker::active() {
if (_timer == WM_TIMER_ID_INVALID) return false;
//return esp_timer_is_active(_timer);
return true;
}
#ifndef TICKER_H
#define TICKER_H
#include
extern "C" {
#include "wm_timer.h"
}
class Ticker {
public:
Ticker();
~Ticker();
typedef void (*callback_t)(void);
typedef void (*tls_timer_irq_callback)(void*);
void attach(float seconds, callback_t callback) {
_attach_ms(seconds * 1000, true, reinterpret_cast(callback), 0);
}
void attach_ms(uint32_t milliseconds, callback_t callback) {
_attach_ms(milliseconds, true, reinterpret_cast(callback), 0);
}
template
void attach(float seconds, void (*callback)(TArg), TArg arg) {
static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach() callback argument size must be <= 4 bytes");
uint32_t arg32 = (uint32_t)arg;
_attach_ms(seconds * 1000, true, reinterpret_cast(callback), arg32);
}
template
void attach_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg) {
static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach_ms() callback argument size must be <= 4 bytes");
uint32_t arg32 = (uint32_t)arg;
_attach_ms(milliseconds, true, reinterpret_cast(callback), arg32);
}
void once(float seconds, callback_t callback) {
_attach_ms(seconds * 1000, false, reinterpret_cast(callback), 0);
}
void once_ms(uint32_t milliseconds, callback_t callback) {
_attach_ms(milliseconds, false, reinterpret_cast(callback), 0);
}
template
void once(float seconds, void (*callback)(TArg), TArg arg) {
static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach() callback argument size must be <= 4 bytes");
uint32_t arg32 = (uint32_t)(arg);
_attach_ms(seconds * 1000, false, reinterpret_cast(callback), arg32);
}
template
void once_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg) {
static_assert(sizeof(TArg) <= sizeof(uint32_t), "attach_ms() callback argument size must be <= 4 bytes");
uint32_t arg32 = (uint32_t)(arg);
_attach_ms(milliseconds, false, reinterpret_cast(callback), arg32);
}
void detach();
bool active();
protected:
void _attach_ms(uint32_t milliseconds, bool repeat, tls_timer_irq_callback callback, uint32_t arg);
protected:
uint8_t _timer;
};
#endif // TICKER_H
struct tm {
uint8_t tm_sec;
uint8_t tm_min;
uint8_t tm_hour;
uint8_t tm_mday;
uint8_t tm_mon;
uint8_t tm_year;
uint8_t tm_wday;
uint16_t tm_yday;
uint8_t tm_isdst;
} tm;
struct tm *localtime(time_t *t) {
uint32_t t2 = *t - 946684800 - 2208988800; // - (2000 - 1970) - (1970 - 1900)
tm.tm_sec = t2 % 60;
tm.tm_min = (t2 /= 60) % 60;
tm.tm_hour = (t2 /= 60) % 24;
t2 = t2 / 24;
tm.tm_year = 0;
while (t2 > (tm.tm_year % 4 ? 364 : 365)) t2 -= (tm.tm_year++ % 4 ? 365 : 366);
tm.tm_yday = t2;
uint8_t days[12] = { 31, (uint8_t)(tm.tm_year % 4 ? 28 : 29), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
tm.tm_mon = 0;
while (t2 >= days[tm.tm_mon]) t2 -= days[tm.tm_mon++];
tm.tm_mday = t2 + 1;
tm.tm_wday = ((uint16_t)tm.tm_year + (tm.tm_year - 1) / 4 + tm.tm_yday) % 7;
tm.tm_year += 100;
tm.tm_isdst = 0;
return &tm;
}