commit
c2e87940f2
@ -0,0 +1,4 @@ |
||||
.idea/ |
||||
build |
||||
cmake-build-* |
||||
*.old |
@ -0,0 +1,6 @@ |
||||
# The following lines of boilerplate have to be in your project's |
||||
# CMakeLists in this exact order for cmake to work correctly |
||||
cmake_minimum_required(VERSION 3.5) |
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake) |
||||
project(espnode) |
@ -0,0 +1,8 @@ |
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := hello-world
|
||||
|
||||
include $(IDF_PATH)/make/project.mk |
@ -0,0 +1,8 @@ |
||||
set(COMPONENT_ADD_INCLUDEDIRS include) |
||||
|
||||
set(COMPONENT_SRCDIRS |
||||
"src") |
||||
|
||||
#set(COMPONENT_REQUIRES) |
||||
|
||||
register_component() |
@ -0,0 +1,2 @@ |
||||
General purpose, mostly platofrm-idependent utilities |
||||
that may be used by other components. |
@ -0,0 +1,3 @@ |
||||
|
||||
COMPONENT_SRCDIRS := src
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
@ -0,0 +1,75 @@ |
||||
/*
|
||||
* Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, but |
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||||
*/ |
||||
|
||||
#ifndef BASE16_H_ |
||||
#define BASE16_H_ |
||||
|
||||
#include <stdint.h> |
||||
#include <string.h> |
||||
|
||||
/**
|
||||
* Calculate length of base16-encoded data |
||||
* @param raw_len Raw data length |
||||
* @return Encoded string length (excluding NUL) |
||||
*/ |
||||
static inline size_t base16_encoded_len(size_t raw_len) { |
||||
return (2 * raw_len); |
||||
} |
||||
|
||||
/**
|
||||
* Calculate maximum length of base16-decoded string |
||||
* @param encoded Encoded string |
||||
* @return Maximum length of raw data |
||||
*/ |
||||
static inline size_t base16_decoded_max_len(const char *encoded) { |
||||
return ((strlen(encoded) + 1) / 2); |
||||
} |
||||
|
||||
/**
|
||||
* Base16-encode data |
||||
* |
||||
* The buffer must be the correct length for the encoded string. Use |
||||
* something like |
||||
* |
||||
* char buf[ base16_encoded_len ( len ) + 1 ]; |
||||
* |
||||
* (the +1 is for the terminating NUL) to provide a buffer of the |
||||
* correct size. |
||||
* |
||||
* @param raw Raw data |
||||
* @param len Length of raw data |
||||
* @param encoded Buffer for encoded string |
||||
*/ |
||||
extern void base16_encode(uint8_t *raw, size_t len, char *encoded); |
||||
|
||||
/**
|
||||
* Base16-decode data |
||||
* |
||||
* The buffer must be large enough to contain the decoded data. Use |
||||
* something like |
||||
* |
||||
* char buf[ base16_decoded_max_len ( encoded ) ]; |
||||
* |
||||
* to provide a buffer of the correct size. |
||||
* @param encoded Encoded string |
||||
* @param raw Raw data |
||||
* @return Length of raw data, or negative error |
||||
*/ |
||||
extern int base16_decode(const char *encoded, uint8_t *raw); |
||||
|
||||
#endif /* BASE16_H_ */ |
@ -0,0 +1,131 @@ |
||||
/**
|
||||
* TODO file description |
||||
*
|
||||
* Created on 2019/09/13. |
||||
*/ |
||||
|
||||
#ifndef CSPEMU_DATETIME_H |
||||
#define CSPEMU_DATETIME_H |
||||
|
||||
#include <stdbool.h> |
||||
#include <stdint.h> |
||||
|
||||
enum weekday { |
||||
MONDAY = 1, |
||||
TUESDAY, |
||||
WEDNESDAY, |
||||
THURSDAY, |
||||
FRIDAY, |
||||
SATURDAY, |
||||
SUNDAY |
||||
}; |
||||
_Static_assert(MONDAY==1, "enum weekday numbering Mon"); |
||||
_Static_assert(SUNDAY==7, "enum weekday numbering Sun"); |
||||
|
||||
enum month { |
||||
JANUARY = 1, |
||||
FEBRUARY, |
||||
MARCH, |
||||
APRIL, |
||||
MAY, |
||||
JUNE, |
||||
JULY, |
||||
AUGUST, |
||||
SEPTEMBER, |
||||
OCTOBER, |
||||
NOVEMBER, |
||||
DECEMBER |
||||
}; |
||||
_Static_assert(JANUARY==1, "enum month numbering Jan"); |
||||
_Static_assert(DECEMBER==12, "enum month numbering Dec"); |
||||
|
||||
/** Abbreviated weekday names */ |
||||
extern const char *DT_WKDAY_NAMES[]; |
||||
/** Full-length weekday names */ |
||||
extern const char *DT_WKDAY_NAMES_FULL[]; |
||||
/** Abbreviated month names */ |
||||
extern const char *DT_MONTH_NAMES[]; |
||||
/** Full-length month names */ |
||||
extern const char *DT_MONTH_NAMES_FULL[]; |
||||
|
||||
typedef struct datetime { |
||||
uint16_t year; |
||||
enum month month; |
||||
uint8_t day; |
||||
uint8_t hour; |
||||
uint8_t min; |
||||
uint8_t sec; |
||||
enum weekday wkday; // 1=monday
|
||||
} datetime_t; |
||||
|
||||
// Templates for printf
|
||||
#define DT_FORMAT_DATE "%d/%d/%d" |
||||
#define DT_SUBS_DATE(dt) (dt).year, (dt).month, (dt).day |
||||
|
||||
#define DT_FORMAT_TIME "%d:%02d:%02d" |
||||
#define DT_SUBS_TIME(dt) (dt).hour, (dt).min, (dt).sec |
||||
|
||||
#define DT_FORMAT_DATE_WK DT_FORMAT_WK " " DT_FORMAT_DATE |
||||
#define DT_SUBS_DATE_WK(dt) DT_SUBS_WK(dt), DT_SUBS_DATE(dt) |
||||
|
||||
#define DT_FORMAT_WK "%s" |
||||
#define DT_SUBS_WK(dt) DT_WKDAY_NAMES[(dt).wkday] |
||||
|
||||
#define DT_FORMAT_DATE_TIME DT_FORMAT_DATE " " DT_FORMAT_TIME |
||||
#define DT_SUBS_DATE_TIME(dt) DT_SUBS_DATE(dt), DT_SUBS_TIME(dt) |
||||
|
||||
#define DT_FORMAT DT_FORMAT_DATE_WK " " DT_FORMAT_TIME |
||||
#define DT_SUBS(dt) DT_SUBS_DATE_WK(dt), DT_SUBS_TIME(dt) |
||||
|
||||
// base century for two-digit year conversions
|
||||
#define DT_CENTURY 2000 |
||||
// start year for weekday computation
|
||||
#define DT_START_YEAR 2019 |
||||
// January 1st weekday of DT_START_YEAR
|
||||
#define DT_START_WKDAY TUESDAY |
||||
// max date supported by 2-digit year RTC counters (it can't check Y%400==0 with only two digits)
|
||||
#define DT_END_YEAR 2399 |
||||
|
||||
typedef union __attribute__((packed)) { |
||||
struct __attribute__((packed)) { |
||||
uint8_t ones : 4; |
||||
uint8_t tens : 4; |
||||
}; |
||||
uint8_t byte; |
||||
} bcd_t; |
||||
_Static_assert(sizeof(bcd_t) == 1, "Bad bcd_t len"); |
||||
|
||||
/** Check if a year is leap */ |
||||
static inline bool is_leap_year(int year) |
||||
{ |
||||
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0); |
||||
} |
||||
|
||||
/**
|
||||
* Check if a datetime could be valid (ignores leap years) |
||||
* |
||||
* @param[in] dt |
||||
* @return basic validations passed |
||||
*/ |
||||
bool datetime_is_valid(const datetime_t *dt); |
||||
|
||||
/**
|
||||
* Set weekday based on a date in a given datetime |
||||
* |
||||
* @param[in,out] dt |
||||
* @return success |
||||
*/ |
||||
bool datetime_set_weekday(datetime_t *dt); |
||||
|
||||
/**
|
||||
* Get weekday for given a date |
||||
* |
||||
* @param year - year number |
||||
* @param month - 1-based month number |
||||
* @param day - 1-based day number |
||||
* @return weekday |
||||
*/ |
||||
enum weekday date_weekday(uint16_t year, enum month month, uint8_t day); |
||||
|
||||
|
||||
#endif //CSPEMU_DATETIME_H
|
@ -0,0 +1,19 @@ |
||||
/**
|
||||
* @file |
||||
* @brief A simple way of dumping memory to a hex output |
||||
* |
||||
* \addtogroup Hexdump |
||||
* |
||||
* @{ |
||||
*/ |
||||
|
||||
|
||||
#include <stdio.h> |
||||
|
||||
#define HEX_DUMP_LINE_BUFF_SIZ 16 |
||||
|
||||
extern void hex_dump(FILE * fp,void *src, int len); |
||||
extern void hex_dump_buff_line(FILE *fp, int addr_size, unsigned pos, char *line, unsigned len); |
||||
/**
|
||||
* }@ |
||||
*/ |
@ -0,0 +1,80 @@ |
||||
/**
|
||||
* General purpose, platform agnostic, reusable utils |
||||
*/ |
||||
|
||||
#ifndef COMMON_UTILS_UTILS_H |
||||
#define COMMON_UTILS_UTILS_H |
||||
|
||||
#include <stdbool.h> |
||||
#include <stdint.h> |
||||
|
||||
#include "base16.h" |
||||
#include "datetime.h" |
||||
#include "hexdump.h" |
||||
|
||||
/** Convert a value to BCD struct */ |
||||
static inline bcd_t num2bcd(uint8_t value) |
||||
{ |
||||
return (bcd_t) {.ones=value % 10, .tens=value / 10}; |
||||
} |
||||
|
||||
/** Convert unpacked BCD to value */ |
||||
static inline uint8_t bcd2num(uint8_t tens, uint8_t ones) |
||||
{ |
||||
return tens * 10 + ones; |
||||
} |
||||
|
||||
/**
|
||||
* Append to a buffer. |
||||
* |
||||
* In case the buffer capacity is reached, it stays unchanged (up to the terminator) and NULL is returned. |
||||
* |
||||
* @param buf - buffer position to append at; if NULL is given, the function immediately returns NULL. |
||||
* @param appended - string to append |
||||
* @param pcap - pointer to a capacity variable |
||||
* @return the new end of the string (null byte); use as 'buf' for following appends |
||||
*/ |
||||
char *append(char *buf, const char *appended, size_t *pcap); |
||||
|
||||
/**
|
||||
* Test if a file descriptor is valid (e.g. when cleaning up after a failed select) |
||||
* |
||||
* @param fd - file descriptor number |
||||
* @return is valid |
||||
*/ |
||||
bool fd_is_valid(int fd); |
||||
|
||||
/**
|
||||
* parse user-provided string as boolean |
||||
* |
||||
* @param str |
||||
* @return 0 false, 1 true, -1 invalid |
||||
*/ |
||||
int parse_boolean_arg(const char *str); |
||||
|
||||
/** Check equality of two strings; returns bool */ |
||||
#define streq(a, b) (strcmp((const char*)(a), (const char*)(b)) == 0) |
||||
|
||||
/** Check prefix equality of two strings; returns bool */ |
||||
#define strneq(a, b, n) (strncmp((const char*)(a), (const char*)(b), (n)) == 0) |
||||
|
||||
/** Check if a string starts with a substring; returns bool */ |
||||
#define strstarts(a, b) strneq((a), (b), (int)strlen((b))) |
||||
|
||||
#ifndef MIN |
||||
/** Get min of two numbers */ |
||||
#define MIN(a, b) ((a) > (b) ? (b) : (a)) |
||||
#endif |
||||
|
||||
#ifndef MAX |
||||
/** Get max of two values */ |
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b)) |
||||
#endif |
||||
|
||||
#ifndef STR |
||||
#define STR_HELPER(x) #x |
||||
/** Stringify a token */ |
||||
#define STR(x) STR_HELPER(x) |
||||
#endif |
||||
|
||||
#endif //COMMON_UTILS_UTILS_H
|
@ -0,0 +1,62 @@ |
||||
/*
|
||||
* Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, but |
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||||
*/ |
||||
|
||||
#include <stdint.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <stdio.h> |
||||
#include <esp_log.h> |
||||
|
||||
static const char *TAG = "base16"; |
||||
|
||||
void base16_encode(uint8_t *raw, size_t len, char *encoded) { |
||||
uint8_t *raw_bytes = raw; |
||||
char *encoded_bytes = encoded; |
||||
size_t remaining = len; |
||||
|
||||
for (; remaining--; encoded_bytes += 2) |
||||
snprintf(encoded_bytes, 3, "%02X", *(raw_bytes++)); |
||||
|
||||
} |
||||
|
||||
int base16_decode(const char *encoded, uint8_t *raw) { |
||||
const char *encoded_bytes = encoded; |
||||
uint8_t *raw_bytes = raw; |
||||
char buf[3]; |
||||
char *endp; |
||||
size_t len; |
||||
|
||||
while (encoded_bytes[0]) { |
||||
if (!encoded_bytes[1]) { |
||||
ESP_LOGE(TAG, "Base16-encoded string \"%s\" has invalid length\n", |
||||
encoded); |
||||
return -22; |
||||
} |
||||
memcpy(buf, encoded_bytes, 2); |
||||
buf[2] = '\0'; |
||||
*(raw_bytes++) = strtoul(buf, &endp, 16); |
||||
if (*endp != '\0') { |
||||
ESP_LOGE(TAG,"Base16-encoded string \"%s\" has invalid byte \"%s\"\n", |
||||
encoded, buf); |
||||
return -22; |
||||
} |
||||
encoded_bytes += 2; |
||||
} |
||||
len = (raw_bytes - raw); |
||||
return (len); |
||||
} |
@ -0,0 +1,52 @@ |
||||
#include <stdint.h> |
||||
#include <string.h> |
||||
#include <stdbool.h> |
||||
#include <fcntl.h> |
||||
#include <errno.h> |
||||
|
||||
#include "common_utils/utils.h" |
||||
|
||||
char *append(char *buf, const char *appended, size_t *pcap) |
||||
{ |
||||
char c; |
||||
char *buf0 = buf; |
||||
size_t cap = *pcap; |
||||
|
||||
if (buf0 == NULL) return NULL; |
||||
if (appended == NULL || appended[0] == 0) return buf0; |
||||
|
||||
while (cap > 1 && 0 != (c = *appended++)) { |
||||
*buf++ = c; |
||||
cap--; |
||||
} |
||||
if (cap == 0) { |
||||
*buf0 = '\0'; |
||||
return NULL; |
||||
} |
||||
|
||||
*pcap = cap; |
||||
*buf = 0; |
||||
return buf; |
||||
} |
||||
|
||||
bool fd_is_valid(int fd) |
||||
{ |
||||
return fcntl(fd, F_GETFD) != -1 || errno != EBADF; |
||||
} |
||||
|
||||
int parse_boolean_arg(const char *str) |
||||
{ |
||||
if (0 == strcasecmp(str, "on")) return 1; |
||||
if (strstarts(str, "en")) return 1; |
||||
if (strstarts(str, "y")) return 1; |
||||
if (0 == strcmp(str, "1")) return 1; |
||||
if (0 == strcasecmp(str, "a")) return 1; |
||||
|
||||
if (0 == strcasecmp(str, "off")) return 0; |
||||
if (0 == strcmp(str, "0")) return 0; |
||||
if (strstarts(str, "dis")) return 0; |
||||
if (strstarts(str, "n")) return 0; |
||||
|
||||
return -1; |
||||
} |
||||
|
@ -0,0 +1,110 @@ |
||||
|
||||
#include <stdint.h> |
||||
#include <stdbool.h> |
||||
#include <stddef.h> |
||||
#include "common_utils/datetime.h" |
||||
|
||||
const char *DT_WKDAY_NAMES[] = { |
||||
[MONDAY] = "Mon", |
||||
[TUESDAY] = "Tue", |
||||
[WEDNESDAY] = "Wed", |
||||
[THURSDAY] = "Thu", |
||||
[FRIDAY] = "Fri", |
||||
[SATURDAY] = "Sat", |
||||
[SUNDAY] = "Sun" |
||||
}; |
||||
|
||||
const char *DT_WKDAY_NAMES_FULL[] = { |
||||
[MONDAY] = "Monday", |
||||
[TUESDAY] = "Tuesday", |
||||
[WEDNESDAY] = "Wednesday", |
||||
[THURSDAY] = "Thursday", |
||||
[FRIDAY] = "Friday", |
||||
[SATURDAY] = "Saturday", |
||||
[SUNDAY] = "Sunday" |
||||
}; |
||||
|
||||
const char *DT_MONTH_NAMES[] = { |
||||
[JANUARY] = "Jan", |
||||
[FEBRUARY] = "Feb", |
||||
[MARCH] = "Mar", |
||||
[APRIL] = "Apr", |
||||
[MAY] = "May", |
||||
[JUNE] = "Jun", |
||||
[JULY] = "Jul", |
||||
[AUGUST] = "Aug", |
||||
[SEPTEMBER] = "Sep", |
||||
[OCTOBER] = "Oct", |
||||
[NOVEMBER] = "Nov", |
||||
[DECEMBER] = "Dec" |
||||
}; |
||||
|
||||
const char *DT_MONTH_NAMES_FULL[] = { |
||||
[JANUARY] = "January", |
||||
[FEBRUARY] = "February", |
||||
[MARCH] = "March", |
||||
[APRIL] = "April", |
||||
[MAY] = "May", |
||||
[JUNE] = "June", |
||||
[JULY] = "July", |
||||
[AUGUST] = "August", |
||||
[SEPTEMBER] = "September", |
||||
[OCTOBER] = "October", |
||||
[NOVEMBER] = "November", |
||||
[DECEMBER] = "December" |
||||
}; |
||||
|
||||
static const uint16_t MONTH_LENGTHS[] = { /* 1-based, normal year */ |
||||
[JANUARY]=31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 |
||||
}; |
||||
_Static_assert(sizeof(MONTH_LENGTHS) / sizeof(uint16_t) == 13, "Months array length"); |
||||
|
||||
static const uint16_t MONTH_YEARDAYS[] = { /* 1-based */ |
||||
[JANUARY]=0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 // // days until 1st of month
|
||||
}; |
||||
_Static_assert(sizeof(MONTH_YEARDAYS) / sizeof(uint16_t) == 13, "Months array length"); |
||||
|
||||
_Static_assert(MONDAY < SUNDAY, "Weekday ordering"); |
||||
|
||||
bool datetime_is_valid(const datetime_t *dt) |
||||
{ |
||||
if (dt == NULL) return false; |
||||
|
||||
// check month first to avoid out-of-bounds read from the MONTH_LENGTHS table
|
||||
if (!(dt->month >= JANUARY && dt->month <= DECEMBER)) return false; |
||||
|
||||
int monthlen = MONTH_LENGTHS[dt->month]; |
||||
if (dt->month == FEBRUARY && is_leap_year(dt->year)) { |
||||
monthlen = 29; |
||||
} |
||||
|
||||
return dt->sec < 60 && |
||||
dt->min < 60 && |
||||
dt->hour < 24 && |
||||
dt->wkday >= MONDAY && |
||||
dt->wkday <= SUNDAY && |
||||
dt->year >= DT_START_YEAR && |
||||
dt->year <= DT_END_YEAR && |
||||
dt->day >= 1 && |
||||
dt->day <= monthlen; |
||||
} |
||||
|
||||
bool datetime_set_weekday(datetime_t *dt) |
||||
{ |
||||
dt->wkday = MONDAY; // prevent the validator func erroring out on invalid weekday
|
||||
if (!datetime_is_valid(dt)) return false; |
||||
dt->wkday = date_weekday(dt->year, dt->month, dt->day); |
||||
return true; |
||||
} |
||||
|
||||
enum weekday date_weekday(uint16_t year, enum month month, uint8_t day) |
||||
{ |
||||
uint16_t days = (DT_START_WKDAY - MONDAY) + (year - DT_START_YEAR) * 365 + MONTH_YEARDAYS[month] + (day - 1); |
||||
|
||||
for (uint16_t i = DT_START_YEAR; i <= year; i++) { |
||||
if (is_leap_year(i) && (i < year || month > FEBRUARY)) days++; |
||||
} |
||||
|
||||
return MONDAY + days % 7; |
||||
} |
||||
|
@ -0,0 +1,72 @@ |
||||
/*
|
||||
* util.c |
||||
* |
||||
* Created on: Aug 12, 2009 |
||||
* Author: johan |
||||
*/ |
||||
|
||||
// adapted from libgomspace
|
||||
|
||||
#include <string.h> |
||||
#include <stdio.h> |
||||
#include "common_utils/hexdump.h" |
||||
|
||||
|
||||
//! Dump memory to debugging output
|
||||
/**
|
||||
* Dumps a chunk of memory to the screen |
||||
*/ |
||||
void hex_dump(FILE * fp, void *src, int len) { |
||||
int i, j=0, k; |
||||
char text[17]; |
||||
|
||||
text[16] = '\0'; |
||||
//printf("Hex dump:\r\n");
|
||||
fprintf(fp, "%p : ", src); |
||||
for(i=0; i<len; i++) { |
||||
j++; |
||||
fprintf(fp, "%02X ", ((volatile unsigned char *)src)[i]); |
||||
if(j == 8) |
||||
fputc(' ', fp); |
||||
if(j == 16) { |
||||
j = 0; |
||||
memcpy(text, &((char *)src)[i-15], 16); |
||||
for(k=0; k<16; k++) { |
||||
if((text[k] < 32) || (text[k] > 126)) { |
||||
text[k] = '.'; |
||||
} |
||||
} |
||||
fprintf(fp, " |%s|\n\r", text); |
||||
if(i<len-1) { |
||||
fprintf(fp, "%p : ", src+i+1); |
||||
} |
||||
} |
||||
} |
||||
if (i % 16) |
||||
fprintf(fp, "\r\n"); |
||||
} |
||||
|
||||
void hex_dump_buff_line(FILE *fp, int addr_size, unsigned pos, char *line, unsigned len) |
||||
{ |
||||
unsigned i; |
||||
|
||||
fprintf(fp, "%0*x", addr_size, pos); |
||||
for (i = 0; i < HEX_DUMP_LINE_BUFF_SIZ; i++) |
||||
{ |
||||
if (!(i % 8)) |
||||
fputc(' ', fp); |
||||
if (i < len) |
||||
fprintf(fp, " %02x", (unsigned char)line[i]); |
||||
else |
||||
fputs(" ", fp); |
||||
} |
||||
fputs(" |", fp); |
||||
for (i = 0; i < HEX_DUMP_LINE_BUFF_SIZ && i < len; i++) |
||||
{ |
||||
if (line[i] >= 32 && line[i] <= 126) |
||||
fprintf(fp, "%c", (unsigned char)line[i]); |
||||
else |
||||
fputc('.', fp); |
||||
} |
||||
fputs("|\r\n", fp); |
||||
} |
@ -0,0 +1,9 @@ |
||||
set(COMPONENT_ADD_INCLUDEDIRS |
||||
"include") |
||||
|
||||
set(COMPONENT_SRCDIRS |
||||
"src") |
||||
|
||||
set(COMPONENT_REQUIRES ping tcpip_adapter) |
||||
|
||||
register_component() |
@ -0,0 +1,27 @@ |
||||
menu "DHCP watchdog" |
||||
|
||||
config DHCPWD_PERIOD_GW_PING_S |
||||
int "Connectivity test interval (s)" |
||||
default 60 |
||||
help |
||||
Time between two connectivity tests (gateway ping) |
||||
|
||||
config DHCPWD_GETIP_TIMEOUT_S |
||||
int "Timeout to get IP (s)" |
||||
default 10 |
||||
help |
||||
Timeout after establishing connection to get an IP address from the DHCP server. |
||||
|
||||
config DHCPWD_TASK_STACK_SIZE |
||||
int "Task stack size (bytes)" |
||||
default 4096 |
||||
help |
||||
DHCP watchdog task stack size |
||||
|
||||
config DHCPWD_TASK_PRIORITY |
||||
int "Task priority" |
||||
default 3 |
||||
help |
||||
DHCP watchdog task priority |
||||
|
||||
endmenu |
@ -0,0 +1,5 @@ |
||||
DHCP ping watchdog. |
||||
|
||||
ESP32 sometimes loses wireless connectivity (expiring lease that fails to renew, |
||||
AP rebooting and forgetting us, etc). This module periodically pings the gateway |
||||
and triggers reconnect if the ping fails. |
@ -0,0 +1,3 @@ |
||||
|
||||
COMPONENT_SRCDIRS := src
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
@ -0,0 +1,82 @@ |
||||
/**
|
||||
* DHCP watchdog |
||||
* |
||||
* This is a workaround for a rare case where we don't get |
||||
* any IP after connecting with STA. If it takes too long, |
||||
* try power-cycling the DHCP client. If that fails too, |
||||
* try cycling the WiFi stack too. |
||||
* |
||||
* This does not try to reboot, as there are valid cases when this |
||||
* can happen - e.g. no DHCP on the network + no static IP configured yet. |
||||
* |
||||
* The ping component is used as a dependency. |
||||
*/ |
||||
|
||||
#ifndef _DHCP_WD_H_ |
||||
#define _DHCP_WD_H_ |
||||
|
||||
#include "esp_netif.h" |
||||
#include "esp_event.h" |
||||
#include "freertos/FreeRTOS.h" |
||||
#include "freertos/task.h" |
||||
|
||||
typedef struct dhcp_wd_instance * dhcp_wd_handle_t; |
||||
|
||||
/**
|
||||
* Start the watchdog. Handle must remain valid until the task is deleted. |
||||
* |
||||
* @param[in] iface |
||||
* @param[out] handle - pointer to a handle variable (will be written to it) |
||||
* @return success |
||||
*/ |
||||
esp_err_t dhcp_watchdog_start(esp_netif_t * netif, bool is_wifi, dhcp_wd_handle_t *pHandle); |
||||
|
||||
/**
|
||||
* Check if a watchdog is running |
||||
* |
||||
* @param[in] handle |
||||
* @return is running |
||||
*/ |
||||
bool dhcp_watchdog_is_running(dhcp_wd_handle_t handle); |
||||
|
||||
/**
|
||||
* Stop the watchdog and free resources. |
||||
* The handle becomes invalid and is set to NULL. |
||||
* |
||||
* @param[in] handle |
||||
* @return success |
||||
*/ |
||||
esp_err_t dhcp_watchdog_stop(dhcp_wd_handle_t *pHandle); |
||||
|
||||
enum dhcp_wd_event { |
||||
DHCP_WD_NOTIFY_CONNECTED, |
||||
DHCP_WD_NOTIFY_DISCONNECTED, |
||||
DHCP_WD_NOTIFY_GOT_IP, |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Notify the watchdog task about a wifi state change |
||||
* |
||||
* Call this from the WiFi event handler. |
||||
* |
||||
* @param[in] handle |
||||
* @param[in] event - detected event |
||||
*/ |
||||
esp_err_t dhcp_watchdog_notify(dhcp_wd_handle_t handle, enum dhcp_wd_event); |
||||
|
||||
enum dhcp_wd_test_result { |
||||
DHCP_WD_RESULT_OK = 0, |
||||
DHCP_WD_RESULT_PING_LOST, |
||||
DHCP_WD_RESULT_NO_GATEWAY, |
||||
}; |
||||
|
||||
/**
|
||||
* Manually trigger a connection test by pinging the gateway. |
||||
* This is independent on any watchdog tasks and can be run without starting the watchdog. |
||||
* |
||||
* @param[in] iface - network interface, typically TCPIP_ADAPTER_IF_STA |
||||
* @return test result |
||||
*/ |
||||
enum dhcp_wd_test_result dhcp_wd_test_connection(esp_netif_t *iface); |
||||
|
||||
#endif //_DHCP_WD_H_
|
@ -0,0 +1,277 @@ |
||||
#include <string.h> |
||||
#include "esp_log.h" |
||||
#include "dhcp_wd.h" |
||||
#include "esp_wifi.h" |
||||
//#include "esp_eth.h"
|
||||
#include "ping.h" |
||||
|
||||
#define xstr(s) str(s) |
||||
#define str(s) #s |
||||
|
||||
#define PERIOD_GW_PING_S CONFIG_DHCPWD_PERIOD_GW_PING_S |
||||
#define GETIP_TIMEOUT_S CONFIG_DHCPWD_GETIP_TIMEOUT_S |
||||
#define TASK_STACK_SIZE CONFIG_DHCPWD_TASK_STACK_SIZE |
||||
#define TASK_PRIO CONFIG_DHCPWD_TASK_PRIORITY |
||||
|
||||
static const char *TAG = "dhcp_wd"; |
||||
|
||||
static void dhcp_watchdog_task(void *parm); |
||||
|
||||
struct dhcp_wd_instance { |
||||
TaskHandle_t task; |
||||
esp_netif_t * iface; |
||||
bool is_wifi; |
||||
bool running; |
||||
}; |
||||
|
||||
#define STATES_ENUM \ |
||||
X(DISCONECTED) \
|
||||
X(CONECTED_WAIT_IP) \
|
||||
X(CONECTED_WAIT_IP2) \
|
||||
X(CONECTED) |
||||
|
||||
enum dhcp_wd_state { |
||||
#undef X |
||||
#define X(s) STATE_##s, |
||||
STATES_ENUM |
||||
}; |
||||
|
||||
const char *state_names[] = { |
||||
#undef X |
||||
#define X(s) xstr(s), |
||||
STATES_ENUM |
||||
}; |
||||
|
||||
enum dhcp_wd_notify { |
||||
NOTIFY_CONNECTED = BIT0, |
||||
NOTIFY_DISCONNECTED = BIT1, |
||||
NOTIFY_GOT_IP = BIT2, |
||||
NOTIFY_SHUTDOWN = BIT3, // kills the task
|
||||
}; |
||||
|
||||
/** Send a notification to the watchdog task */ |
||||
esp_err_t dhcp_watchdog_notify(dhcp_wd_handle_t handle, const enum dhcp_wd_event event) |
||||
{ |
||||
assert(handle != NULL); |
||||
assert(handle->task != NULL); |
||||
|
||||
uint32_t flag = 0; |
||||
|
||||
switch (event) { |
||||
case DHCP_WD_NOTIFY_CONNECTED: |
||||
flag = NOTIFY_CONNECTED; |
||||
break; |
||||
|
||||
case DHCP_WD_NOTIFY_DISCONNECTED: |
||||
flag = NOTIFY_DISCONNECTED; |
||||
break; |
||||
|
||||
case DHCP_WD_NOTIFY_GOT_IP: |
||||
flag = NOTIFY_GOT_IP; |
||||
break; |
||||
|
||||
default: |
||||
break; |
||||
} |
||||
|
||||
BaseType_t ret = pdPASS; |
||||
if (flag != 0) { |
||||
ret = xTaskNotify(handle->task, flag, eSetBits); |
||||
} |
||||
|
||||
return (pdPASS == ret) ? ESP_OK : ESP_FAIL; |
||||
} |
||||
|
||||
/**
|
||||
* Start the watchdog |
||||
*/ |
||||
esp_err_t dhcp_watchdog_start(esp_netif_t * netif, bool is_wifi, dhcp_wd_handle_t *pHandle) |
||||
{ |
||||
assert(pHandle != NULL); |
||||
|
||||
dhcp_wd_handle_t handle = calloc(1, sizeof(struct dhcp_wd_instance)); |
||||
if (!handle) return ESP_ERR_NO_MEM; |
||||
*pHandle = handle; |
||||
|
||||
handle->iface = netif; |
||||
handle->is_wifi = is_wifi; |
||||
|
||||
BaseType_t ret = xTaskCreate(dhcp_watchdog_task, "dhcp-wd", TASK_STACK_SIZE, (void *)handle, TASK_PRIO, &handle->task); |
||||
handle->running = true; |
||||
|
||||
return (pdPASS == ret) ? ESP_OK : ESP_FAIL; |
||||
} |
||||
|
||||
/**
|
||||
* Check if a watchdog is still running |
||||
* |
||||
* @param handle |
||||
* @return is running |
||||
*/ |
||||
bool dhcp_watchdog_is_running(dhcp_wd_handle_t handle) |
||||
{ |
||||
return handle->running; |
||||
} |
||||
|
||||
/**
|
||||
* Stop the watchdog and free resources |
||||
*/ |
||||
esp_err_t dhcp_watchdog_stop(dhcp_wd_handle_t *pHandle) |
||||
{ |
||||
assert(pHandle != NULL); |
||||
assert(*pHandle != NULL); |
||||
xTaskNotify((*pHandle)->task, NOTIFY_SHUTDOWN, eSetBits); |
||||
*pHandle = NULL; |
||||
return ESP_OK; |
||||
} |
||||
|
||||
/**
|
||||
* @param parm - tcpip_adapter_if_t iface (cast to void *) - typically TCPIP_ADAPTER_IF_STA |
||||
*/ |
||||
static void dhcp_watchdog_task(void *parm) |
||||
{ |
||||
enum dhcp_wd_state state = STATE_DISCONECTED; |
||||
|
||||
dhcp_wd_handle_t handle = parm; |
||||
assert(handle != NULL); |
||||
assert(handle->iface != NULL); |
||||
|
||||
ESP_LOGI(TAG, "Watchdog started"); |
||||
|
||||
while (1) { |
||||
uint32_t flags = 0; |
||||
uint32_t wait_s; |
||||
TickType_t waittime; |
||||
|
||||
switch (state) { |
||||
case STATE_DISCONECTED: |
||||
wait_s = waittime = portMAX_DELAY; |
||||
break; |
||||
|
||||
case STATE_CONECTED_WAIT_IP: |
||||
case STATE_CONECTED_WAIT_IP2: |
||||
wait_s = GETIP_TIMEOUT_S; |
||||
waittime = (GETIP_TIMEOUT_S * 1000) / portTICK_PERIOD_MS; |
||||
break; |
||||
|
||||
case STATE_CONECTED: |
||||
wait_s = PERIOD_GW_PING_S; |
||||
waittime = (PERIOD_GW_PING_S * 1000) / portTICK_PERIOD_MS; |
||||
break; |
||||
|
||||
default: |
||||
assert(0); |
||||
} |
||||
|
||||
ESP_LOGD(TAG, "State %s, wait %d s", state_names[state], wait_s); |
||||
BaseType_t rv = xTaskNotifyWait( |
||||
/* no clear on entry */ pdFALSE, |
||||
/* clear all on exit */ ULONG_MAX, |
||||
&flags, waittime); |
||||
|
||||
if (rv == pdPASS) { |
||||
// the order here is important in case we get multiple events at once
|
||||
|
||||
if (flags & NOTIFY_DISCONNECTED) { |
||||
state = STATE_DISCONECTED; |
||||
} |
||||
|
||||
if (flags & NOTIFY_CONNECTED) { |
||||
state = STATE_CONECTED_WAIT_IP; |
||||
} |
||||
|
||||
if (flags & NOTIFY_GOT_IP) { |
||||
state = STATE_CONECTED; |
||||
} |
||||
|
||||
if (flags & NOTIFY_SHUTDOWN) { |
||||
// kill self
|
||||
handle->running = false; |
||||
free(handle); |
||||
vTaskDelete(NULL); |
||||
return; |
||||
} |
||||
} else { |
||||
// a timeout occurred
|
||||
|
||||
switch (state) { |
||||
case STATE_DISCONECTED: |
||||
// this shouldn't happen, we have infinite delay waiting for disconnected
|
||||
ESP_LOGW(TAG, "dhcp_wd double discon evt"); |
||||
break; |
||||
|
||||
case STATE_CONECTED_WAIT_IP: |
||||
ESP_LOGW(TAG, "Get IP timeout, restarting DHCP client"); |
||||
// this is a bit suspicious
|
||||
// try to restart the DHCPC client
|
||||
ESP_ERROR_CHECK(esp_netif_dhcpc_stop(handle->iface)); |
||||
ESP_ERROR_CHECK(esp_netif_dhcpc_start(handle->iface)); |
||||
state = STATE_CONECTED_WAIT_IP2; |
||||
break; |
||||
|
||||
case STATE_CONECTED_WAIT_IP2: |
||||
ESP_LOGW(TAG, "Get IP timeout 2, restarting network stack"); |
||||
// well now this is weird. try flipping the whole WiFi/Eth stack
|
||||
if (handle->is_wifi) { |
||||
ESP_ERROR_CHECK(esp_wifi_disconnect()); |
||||
} |
||||
// this will trigger the disconnected event and loop back into Disconnected
|
||||
// the disconnect event handler calls connect again
|
||||
state = STATE_DISCONECTED; |
||||
break; |
||||
|
||||
case STATE_CONECTED: { |
||||
// Ping gateway to check if we're still connected
|
||||
enum dhcp_wd_test_result result = dhcp_wd_test_connection(handle->iface); |
||||
|
||||
if (result == DHCP_WD_RESULT_PING_LOST) { |
||||
// looks like the gateway silently dropped us
|
||||
// try kicking the DHCP client, if it helps
|
||||
ESP_ERROR_CHECK(esp_netif_dhcpc_stop(handle->iface)); |
||||
ESP_ERROR_CHECK(esp_netif_dhcpc_start(handle->iface)); |
||||
state = STATE_CONECTED_WAIT_IP2; |
||||
// if not, it'll flip the whole wifi stack
|
||||
} else { |
||||
ESP_LOGD(TAG, "Gateway ping OK"); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
enum dhcp_wd_test_result dhcp_wd_test_connection(esp_netif_t *iface) |
||||
{ |
||||
ESP_LOGD(TAG, "Ping Gateway to check if IP is valid"); |
||||
|
||||
ping_opts_t opts = PING_CONFIG_DEFAULT(); |
||||
opts.count = 3; |
||||
opts.interval_ms = 0; |
||||
opts.timeout_ms = 1000; |
||||
|
||||
esp_netif_ip_info_t ip_info = {}; |
||||
ESP_ERROR_CHECK(esp_netif_get_ip_info(iface, &ip_info)); |
||||
|
||||
opts.ip_addr.addr = ip_info.gw.addr; |
||||
|
||||
ping_result_t result = {}; |
||||
|
||||
if (ip_info.gw.addr != 0) { |
||||
esp_err_t ret = ping(&opts, &result); |
||||
if (ret != ESP_OK) { |
||||
ESP_LOGW(TAG, "Ping error"); |
||||
return DHCP_WD_RESULT_PING_LOST; |
||||
} |
||||
ESP_LOGD(TAG, "Ping result: %d tx, %d rx", result.sent, result.received); |
||||
if (result.received == 0) { |
||||
ESP_LOGW(TAG, "Failed to ping GW"); |
||||
return DHCP_WD_RESULT_PING_LOST; |
||||
} else { |
||||
return DHCP_WD_RESULT_OK; |
||||
} |
||||
} else { |
||||
ESP_LOGW(TAG, "No GW IP to ping"); |
||||
return DHCP_WD_RESULT_NO_GATEWAY; |
||||
} |
||||
} |
@ -0,0 +1,16 @@ |
||||
set(COMPONENT_ADD_INCLUDEDIRS |
||||
"include" "files") |
||||
|
||||
set(COMPONENT_SRCDIRS |
||||
"src" "files") |
||||
|
||||
set(COMPONENT_REQUIRES tcpip_adapter esp_http_server httpd_utils common_utils) |
||||
|
||||
#begin staticfiles |
||||
# generated by rebuild_file_tables |
||||
set(COMPONENT_EMBED_FILES |
||||
"files/embed/favicon.ico" |
||||
"files/embed/index.html") |
||||
#end staticfiles |
||||
|
||||
register_component() |
@ -0,0 +1,2 @@ |
||||
File and template serving support for the http_server bundled with ESP-IDF. |
||||
|
@ -0,0 +1,3 @@ |
||||
|
||||
COMPONENT_SRCDIRS := src
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
After Width: | Height: | Size: 2.2 KiB |
@ -0,0 +1,106 @@ |
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<title>ESP node</title> |
||||
<style> |
||||
html { font-family:sans-serif } |
||||
table { border-collapse:collapse; } |
||||
td,th { padding: 4px 10px; min-width: 65px; } |
||||
td { min-width: 70px; } |
||||
|
||||
th { text-align:left; } |
||||
tbody th { text-align:center; } |
||||
tbody th {border-right: 1px solid black;} |
||||
thead th { text-align:center;padding-top:0; } |
||||
td { text-align:right; } |
||||
td[c],[c] td { text-align:center; } |
||||
td[l],[l] td { text-align:left; } |
||||
td[r],[r] td { text-align:right; } |
||||
td[ed] {cursor:pointer;} |
||||
td[ed]:hover {text-decoration:underline;} |
||||
table[thl] tbody th {text-align:left;} |
||||
figure { |
||||
border-top:3px solid black; |
||||
border-bottom:3px solid black; |
||||
display: inline-block; |
||||
padding: 5px 0; |
||||
margin: 0 0 15px 15px; |
||||
} |
||||
thead tr { border-bottom: 2px solid black; } |
||||
</style> |
||||
</head> |
||||
<body> |
||||
<h1>ESP node {version}</h1> |
||||
|
||||
<a href="/reboot">Restart node</a> |
||||
|
||||
<script> |
||||
var kv_re = /^(-?[0-9.-]+)\s+(.+)$/; |
||||
var Qi = function (x) { return document.getElementById(x) }; |
||||
var data = {}; |
||||
function update(data) { |
||||
if (data) { |
||||
var rows = data.split('\x1e'); |
||||
rows.forEach(function (v) { |
||||
var kv = v.split('\x1f'); |
||||
var el = Qi(kv[0]); |
||||
if (!el) return; |
||||
var suf = ''; |
||||
var res = kv_re.exec(el.textContent); |
||||
if (res) suf = ' ' + res[2]; |
||||
el.textContent = kv[1] + suf; |
||||
data[kv[0]] = kv[1]; |
||||
}); |
||||
} else { |
||||
var xhr=new XMLHttpRequest(); |
||||
xhr.onreadystatechange = function () { |
||||
if (xhr.readyState===4){ |
||||
if (xhr.status===200) { |
||||
update(xhr.responseText); |
||||
} |
||||
setTimeout(update, 1000); |
||||
} |
||||
}; |
||||
xhr.onerror = function () { |
||||
setTimeout(update, 1000); |
||||
}; |
||||
xhr.open('GET', '/data'); |
||||
xhr.send(); |
||||
} |
||||
} |
||||
|
||||
function mkXhr() { |
||||
var xhr=new XMLHttpRequest(); |
||||
xhr.onreadystatechange = function () { |
||||
if (xhr.readyState===4 && xhr.status!==200) { |
||||
alert(xhr.responseText || xhr.statusText || 'Request failed'); |
||||
} |
||||
}; |
||||
return xhr; |
||||
} |
||||
|
||||
function editOpt() { |
||||
var k = this.id; |
||||
var v = prompt(k+' =', data[k]||''); |
||||
if (v !== null) { |
||||
var xhr = mkXhr(); |
||||
xhr.open('POST', '/set'); |
||||
xhr.send("key="+k+'&value='+encodeURIComponent(v)); |
||||
} |
||||
} |
||||
|
||||
function toggleOpt() { |
||||
var xhr = mkXhr(); |
||||
xhr.open('POST', '/toggle'); |
||||
xhr.send("x="+this.id); |
||||
} |
||||
|
||||
/* |
||||
setTimeout(update, 500); |
||||
for(var i=1;i<8;i++) { |
||||
if(i<7) Qi('i'+i+'_max').addEventListener('click', editOpt); |
||||
Qi('ch'+i+'_state').addEventListener('click', toggleOpt); |
||||
} |
||||
*/ |
||||
</script> |
@ -0,0 +1,163 @@ |
||||
#!/usr/bin/env php |
||||
<?php |
||||
// This script rebuilds the static files enum, extern symbols pointing to the embedded byte buffers, |
||||
// and the look-up structs table. To add more files, simply add them in the 'files' directory. |
||||
|
||||
// Note that all files will be accessible by the webserver, unless you filter them in embedded_files.c. |
||||
|
||||
|
||||
// List all files |
||||
$files = scandir(__DIR__.'/embed'); |
||||
|
||||
$files = array_filter(array_map(function ($f) { |
||||
if (!is_file(__DIR__.'/embed/'.$f)) return null; |
||||
if (preg_match('/^\.|\.kate-swp|\.bak$|~$|\.sh$/', $f)) return null; |
||||
|
||||
echo "Found: $f\n"; |
||||
return $f; |
||||
}, $files)); |
||||
|
||||
sort($files); |
||||
|
||||
$formatted = array_filter(array_map(function ($f) { |
||||
return "\"files/embed/$f\""; |
||||
}, $files)); |
||||
|
||||
$cmake = file_get_contents(__DIR__.'/../CMakeLists.txt'); |
||||
|
||||
$cmake = preg_replace('/#begin staticfiles\n.*#end staticfiles/s', |
||||
"#begin staticfiles\n". |
||||
"# generated by rebuild_file_tables\n". |
||||
"set(COMPONENT_EMBED_FILES\n ". |
||||
implode("\n ", $formatted) . ")\n". |
||||
"#end staticfiles", |
||||
$cmake); |
||||
|
||||
file_put_contents(__DIR__.'/../CMakeLists.txt', $cmake); |
||||
|
||||
|
||||
// Generate a list of files |
||||
|
||||
$num = 0; |
||||
$enum_keys = array_map(function ($f) use(&$num) { |
||||
$a = preg_replace("/[^A-Z0-9_]+/", "_", strtoupper($f)); |
||||
return 'FILE_'. $a.' = '.($num++); |
||||
}, $files); |
||||
|
||||
$keylist = implode(",\n ", $enum_keys); |
||||
|
||||
$struct_array = []; |
||||
|
||||
$externs = array_map(function ($f) use (&$struct_array) { |
||||
$a = preg_replace("/[^A-Z0-9_]+/", "_", strtoupper($f)); |
||||
|
||||
$start = '_binary_'. strtolower($a).'_start'; |
||||
$end = '_binary_'. strtolower($a).'_end'; |
||||
|
||||
static $mimes = array( |
||||
'txt' => 'text/plain', |
||||
'htm' => 'text/html', |
||||
'html' => 'text/html', |
||||
'php' => 'text/html', |
||||
'css' => 'text/css', |
||||
'js' => 'application/javascript', |
||||
'json' => 'application/json', |
||||
'xml' => 'application/xml', |
||||
'swf' => 'application/x-shockwave-flash', |
||||
'flv' => 'video/x-flv', |
||||
|
||||
'pem' => 'application/x-pem-file', |
||||
|
||||
// images |
||||
'png' => 'image/png', |
||||
'jpe' => 'image/jpeg', |
||||
'jpeg' => 'image/jpeg', |
||||
'jpg' => 'image/jpeg', |
||||
'gif' => 'image/gif', |
||||
'bmp' => 'image/bmp', |
||||
'ico' => 'image/vnd.microsoft.icon', |
||||
'tiff' => 'image/tiff', |
||||
'tif' => 'image/tiff', |
||||
'svg' => 'image/svg+xml', |
||||
'svgz' => 'image/svg+xml', |
||||
|
||||
// archives |
||||
'zip' => 'application/zip', |
||||
'rar' => 'application/x-rar-compressed', |
||||
'exe' => 'application/x-msdownload', |
||||
'msi' => 'application/x-msdownload', |
||||
'cab' => 'application/vnd.ms-cab-compressed', |
||||
|
||||
// audio/video |
||||
'mp3' => 'audio/mpeg', |
||||
'qt' => 'video/quicktime', |
||||
'mov' => 'video/quicktime', |
||||
|
||||
// adobe |
||||
'pdf' => 'application/pdf', |
||||
'psd' => 'image/vnd.adobe.photoshop', |
||||
'ai' => 'application/postscript', |
||||
'eps' => 'application/postscript', |
||||
'ps' => 'application/postscript', |
||||
|
||||
// ms office |
||||
'doc' => 'application/msword', |
||||
'rtf' => 'application/rtf', |
||||
'xls' => 'application/vnd.ms-excel', |
||||
'ppt' => 'application/vnd.ms-powerpoint', |
||||
|
||||
// open office |
||||
'odt' => 'application/vnd.oasis.opendocument.text', |
||||
'ods' => 'application/vnd.oasis.opendocument.spreadsheet', |
||||
); |
||||
|
||||
$parts = explode('.', $f); |
||||
$suffix = end($parts); |
||||
$mime = $mimes[$suffix] ?? 'application/octet-stream'; |
||||
|
||||
$len = filesize('embed/'.$f); |
||||
|
||||
$struct_array[] = "[FILE_$a] = {{$start}, {$end}, \"{$f}\", \"{$mime}\"},"; |
||||
|
||||
return |
||||
'extern const uint8_t '.$start.'[];'."\n". |
||||
'extern const uint8_t '.$end.'[];'; |
||||
}, $files); |
||||
|
||||
$externlist = implode("\n", $externs); |
||||
$structlist = implode("\n ", $struct_array); |
||||
|
||||
|
||||
file_put_contents('www_files_enum.h', <<<FILE |
||||
// Generated by 'rebuild_file_tables' |
||||
|
||||
#ifndef _EMBEDDED_FILES_ENUM_H |
||||
#define _EMBEDDED_FILES_ENUM_H |
||||
|
||||
#include "fileserver/embedded_files.h" |
||||
|
||||
enum embedded_file_id { |
||||
$keylist |
||||
}; |
||||
|
||||
#endif // _EMBEDDED_FILES_ENUM_H |
||||
|
||||
FILE |
||||
); |
||||
|
||||
$files_count = count($struct_array); |
||||
file_put_contents("www_files_enum.c", <<<FILE |
||||
// Generated by 'rebuild_file_tables' |
||||
#include <stdint.h> |
||||
#include "www_files_enum.h" |
||||
|
||||
$externlist |
||||
|
||||
const struct embedded_file_info EMBEDDED_FILE_LOOKUP[] = { |
||||
$structlist |
||||
}; |
||||
|
||||
const size_t EMBEDDED_FILE_LOOKUP_LEN = $files_count; |
||||
|
||||
FILE |
||||
); |
@ -0,0 +1,15 @@ |
||||
// Generated by 'rebuild_file_tables'
|
||||
#include <stdint.h> |
||||
#include "www_files_enum.h" |
||||
|
||||
extern const uint8_t _binary_favicon_ico_start[]; |
||||
extern const uint8_t _binary_favicon_ico_end[]; |
||||
extern |