Initial commit
This commit is contained in:
parent
3ea4b5d959
commit
45f7ef7973
|
@ -0,0 +1,5 @@
|
|||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
28
README.md
28
README.md
|
@ -1,2 +1,28 @@
|
|||
# esp8266_NeoPixel
|
||||
MQTT NeoPixel control using a esp8266 based board
|
||||
|
||||
## About
|
||||
MQTT NeoPixel control using a esp8266 based board (ESP-01S). Platformio project file for convenience.
|
||||
|
||||
## Requirements
|
||||
+ ESP-01S board
|
||||
+ ESP-01 ESP-01S RGB LED Controller Adapter
|
||||
+ WS2812 WS2812B n Bits Light Ring (I used 16 Bits)
|
||||
+ 3-5V supply or battery
|
||||
|
||||
![HW](HW.jpg)
|
||||
|
||||
+ [Visual Studio Code](https://code.visualstudio.com/) (or Codium) with:
|
||||
+ [PlatformIO extension](https://platformio.org/) with platform Espressif 8266 installed (configuration for the board in [platformio.ini](platformio.ini))
|
||||
|
||||
## Demo
|
||||
(Tested with MQTT_Dashboard)
|
||||
|
||||
![Demo](Demo.gif)
|
||||
|
||||
## References and links
|
||||
+ Libraries and code based on examples from the Unoficial Develpment Kit for Espressif ESP8266, available at https://programs74.ru/udkew-en.html
|
||||
+ Adafruit NeoPixel Arduino Library available at: https://github.com/adafruit/Adafruit_NeoPixel
|
||||
|
||||
## TODO
|
||||
+ Implement initial SSID and password configuration through serial port
|
||||
+ Standalone light effects
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
|
@ -0,0 +1,498 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* Martin d'Allens <martin.dallens@gmail.com> wrote this file. As long as you retain
|
||||
* this notice you can do whatever you want with this stuff. If we meet some day,
|
||||
* and you think this stuff is worth it, you can buy me a beer in return.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
// FIXME: sprintf->snprintf everywhere.
|
||||
// FIXME: support null characters in responses.
|
||||
|
||||
#include "osapi.h"
|
||||
#include "user_interface.h"
|
||||
#include "espconn.h"
|
||||
#include "mem.h"
|
||||
#include "limits.h"
|
||||
#include "httpclient.h"
|
||||
|
||||
|
||||
// Debug output.
|
||||
#ifdef HTTP_DEBUG
|
||||
#undef HTTP_DEBUG
|
||||
#define HTTP_DEBUG(...) os_printf(__VA_ARGS__);
|
||||
#else
|
||||
#define HTTP_DEBUG(...)
|
||||
#endif
|
||||
|
||||
// Internal state.
|
||||
typedef struct {
|
||||
char * path;
|
||||
int port;
|
||||
char * post_data;
|
||||
char * headers;
|
||||
char * hostname;
|
||||
char * buffer;
|
||||
int buffer_size;
|
||||
bool secure;
|
||||
http_callback user_callback;
|
||||
} request_args;
|
||||
|
||||
static char * ICACHE_FLASH_ATTR esp_strdup(const char * str)
|
||||
{
|
||||
if (str == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
char * new_str = (char *)os_malloc(os_strlen(str) + 1); // 1 for null character
|
||||
if (new_str == NULL) {
|
||||
os_printf("esp_strdup: malloc error");
|
||||
return NULL;
|
||||
}
|
||||
os_strcpy(new_str, str);
|
||||
return new_str;
|
||||
}
|
||||
|
||||
static int ICACHE_FLASH_ATTR
|
||||
esp_isupper(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z');
|
||||
}
|
||||
|
||||
static int ICACHE_FLASH_ATTR
|
||||
esp_isalpha(char c)
|
||||
{
|
||||
return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
|
||||
}
|
||||
|
||||
|
||||
static int ICACHE_FLASH_ATTR
|
||||
esp_isspace(char c)
|
||||
{
|
||||
return (c == ' ' || c == '\t' || c == '\n' || c == '\12');
|
||||
}
|
||||
|
||||
static int ICACHE_FLASH_ATTR
|
||||
esp_isdigit(char c)
|
||||
{
|
||||
return (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a string to a long integer.
|
||||
*
|
||||
* Ignores `locale' stuff. Assumes that the upper and lower case
|
||||
* alphabets and digits are each contiguous.
|
||||
*/
|
||||
long ICACHE_FLASH_ATTR
|
||||
esp_strtol(nptr, endptr, base)
|
||||
const char *nptr;
|
||||
char **endptr;
|
||||
int base;
|
||||
{
|
||||
const char *s = nptr;
|
||||
unsigned long acc;
|
||||
int c;
|
||||
unsigned long cutoff;
|
||||
int neg = 0, any, cutlim;
|
||||
|
||||
/*
|
||||
* Skip white space and pick up leading +/- sign if any.
|
||||
* If base is 0, allow 0x for hex and 0 for octal, else
|
||||
* assume decimal; if base is already 16, allow 0x.
|
||||
*/
|
||||
do {
|
||||
c = *s++;
|
||||
} while (esp_isspace(c));
|
||||
if (c == '-') {
|
||||
neg = 1;
|
||||
c = *s++;
|
||||
} else if (c == '+')
|
||||
c = *s++;
|
||||
if ((base == 0 || base == 16) &&
|
||||
c == '0' && (*s == 'x' || *s == 'X')) {
|
||||
c = s[1];
|
||||
s += 2;
|
||||
base = 16;
|
||||
} else if ((base == 0 || base == 2) &&
|
||||
c == '0' && (*s == 'b' || *s == 'B')) {
|
||||
c = s[1];
|
||||
s += 2;
|
||||
base = 2;
|
||||
}
|
||||
if (base == 0)
|
||||
base = c == '0' ? 8 : 10;
|
||||
|
||||
/*
|
||||
* Compute the cutoff value between legal numbers and illegal
|
||||
* numbers. That is the largest legal value, divided by the
|
||||
* base. An input number that is greater than this value, if
|
||||
* followed by a legal input character, is too big. One that
|
||||
* is equal to this value may be valid or not; the limit
|
||||
* between valid and invalid numbers is then based on the last
|
||||
* digit. For instance, if the range for longs is
|
||||
* [-2147483648..2147483647] and the input base is 10,
|
||||
* cutoff will be set to 214748364 and cutlim to either
|
||||
* 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
|
||||
* a value > 214748364, or equal but the next digit is > 7 (or 8),
|
||||
* the number is too big, and we will return a range error.
|
||||
*
|
||||
* Set any if any `digits' consumed; make it negative to indicate
|
||||
* overflow.
|
||||
*/
|
||||
cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
|
||||
cutlim = cutoff % (unsigned long)base;
|
||||
cutoff /= (unsigned long)base;
|
||||
for (acc = 0, any = 0;; c = *s++) {
|
||||
if (esp_isdigit(c))
|
||||
c -= '0';
|
||||
else if (esp_isalpha(c))
|
||||
c -= esp_isupper(c) ? 'A' - 10 : 'a' - 10;
|
||||
else
|
||||
break;
|
||||
if (c >= base)
|
||||
break;
|
||||
if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
|
||||
any = -1;
|
||||
else {
|
||||
any = 1;
|
||||
acc *= base;
|
||||
acc += c;
|
||||
}
|
||||
}
|
||||
if (any < 0) {
|
||||
acc = neg ? LONG_MIN : LONG_MAX;
|
||||
// errno = ERANGE;
|
||||
} else if (neg)
|
||||
acc = -acc;
|
||||
if (endptr != 0)
|
||||
*endptr = (char *)(any ? s - 1 : nptr);
|
||||
return (acc);
|
||||
}
|
||||
|
||||
static int ICACHE_FLASH_ATTR chunked_decode(const char * chunked, char * decode)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
int decode_size = 0;
|
||||
char *str = (char *)chunked;
|
||||
do
|
||||
{
|
||||
char * endstr = NULL;
|
||||
//[chunk-size]
|
||||
i = esp_strtol(str + j, endstr, 16);
|
||||
HTTP_DEBUG("Chunk Size:%d\r\n", i);
|
||||
if (i <= 0)
|
||||
break;
|
||||
//[chunk-size-end-ptr]
|
||||
endstr = (char *)os_strstr(str + j, "\r\n");
|
||||
//[chunk-ext]
|
||||
j += endstr - (str + j);
|
||||
//[CRLF]
|
||||
j += 2;
|
||||
//[chunk-data]
|
||||
decode_size += i;
|
||||
os_memcpy((char *)&decode[decode_size - i], (char *)str + j, i);
|
||||
j += i;
|
||||
//[CRLF]
|
||||
j += 2;
|
||||
} while(true);
|
||||
|
||||
//
|
||||
//footer CRLF
|
||||
//
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR receive_callback(void * arg, char * buf, unsigned short len)
|
||||
{
|
||||
struct espconn * conn = (struct espconn *)arg;
|
||||
request_args * req = (request_args *)conn->reverse;
|
||||
|
||||
if (req->buffer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's do the equivalent of a realloc().
|
||||
const int new_size = req->buffer_size + len;
|
||||
char * new_buffer;
|
||||
if (new_size > BUFFER_SIZE_MAX || NULL == (new_buffer = (char *)os_malloc(new_size))) {
|
||||
os_printf("Response too long (%d)\n", new_size);
|
||||
req->buffer[0] = '\0'; // Discard the buffer to avoid using an incomplete response.
|
||||
if (req->secure)
|
||||
espconn_secure_disconnect(conn);
|
||||
else
|
||||
espconn_disconnect(conn);
|
||||
return; // The disconnect callback will be called.
|
||||
}
|
||||
|
||||
os_memcpy(new_buffer, req->buffer, req->buffer_size);
|
||||
os_memcpy(new_buffer + req->buffer_size - 1 /*overwrite the null character*/, buf, len); // Append new data.
|
||||
new_buffer[new_size - 1] = '\0'; // Make sure there is an end of string.
|
||||
|
||||
os_free(req->buffer);
|
||||
req->buffer = new_buffer;
|
||||
req->buffer_size = new_size;
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR sent_callback(void * arg)
|
||||
{
|
||||
struct espconn * conn = (struct espconn *)arg;
|
||||
request_args * req = (request_args *)conn->reverse;
|
||||
|
||||
if (req->post_data == NULL) {
|
||||
HTTP_DEBUG("All sent\n");
|
||||
}
|
||||
else {
|
||||
// The headers were sent, now send the contents.
|
||||
HTTP_DEBUG("Sending request body\n");
|
||||
if (req->secure)
|
||||
espconn_secure_sent(conn, (uint8_t *)req->post_data, strlen(req->post_data));
|
||||
else
|
||||
espconn_sent(conn, (uint8_t *)req->post_data, strlen(req->post_data));
|
||||
os_free(req->post_data);
|
||||
req->post_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR connect_callback(void * arg)
|
||||
{
|
||||
HTTP_DEBUG("Connected\n");
|
||||
struct espconn * conn = (struct espconn *)arg;
|
||||
request_args * req = (request_args *)conn->reverse;
|
||||
|
||||
espconn_regist_recvcb(conn, receive_callback);
|
||||
espconn_regist_sentcb(conn, sent_callback);
|
||||
|
||||
const char * method = "GET";
|
||||
char post_headers[32] = "";
|
||||
|
||||
if (req->post_data != NULL) { // If there is data this is a POST request.
|
||||
method = "POST";
|
||||
os_sprintf(post_headers, "Content-Length: %d\r\n", strlen(req->post_data));
|
||||
}
|
||||
|
||||
char buf[69 + strlen(method) + strlen(req->path) + strlen(req->hostname) +
|
||||
strlen(req->headers) + strlen(post_headers)];
|
||||
int len = os_sprintf(buf,
|
||||
"%s %s HTTP/1.1\r\n"
|
||||
"Host: %s:%d\r\n"
|
||||
"Connection: close\r\n"
|
||||
"User-Agent: ESP8266\r\n"
|
||||
"%s"
|
||||
"%s"
|
||||
"\r\n",
|
||||
method, req->path, req->hostname, req->port, req->headers, post_headers);
|
||||
|
||||
if (req->secure)
|
||||
espconn_secure_sent(conn, (uint8_t *)buf, len);
|
||||
else
|
||||
espconn_sent(conn, (uint8_t *)buf, len);
|
||||
os_free(req->headers);
|
||||
req->headers = NULL;
|
||||
HTTP_DEBUG("Sending request header\n");
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR disconnect_callback(void * arg)
|
||||
{
|
||||
HTTP_DEBUG("Disconnected\n");
|
||||
struct espconn *conn = (struct espconn *)arg;
|
||||
|
||||
if(conn == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(conn->reverse != NULL) {
|
||||
request_args * req = (request_args *)conn->reverse;
|
||||
int http_status = -1;
|
||||
char * body = "";
|
||||
if (req->buffer == NULL) {
|
||||
os_printf("Buffer shouldn't be NULL\n");
|
||||
}
|
||||
else if (req->buffer[0] != '\0') {
|
||||
// FIXME: make sure this is not a partial response, using the Content-Length header.
|
||||
|
||||
const char * version = "HTTP/1.1 ";
|
||||
if (os_strncmp(req->buffer, version, strlen(version)) != 0) {
|
||||
os_printf("Invalid version in %s\n", req->buffer);
|
||||
}
|
||||
else {
|
||||
http_status = atoi(req->buffer + strlen(version));
|
||||
body = (char *)os_strstr(req->buffer, "\r\n\r\n") + 4;
|
||||
if(os_strstr(req->buffer, "Transfer-Encoding: chunked"))
|
||||
{
|
||||
int body_size = req->buffer_size - (body - req->buffer);
|
||||
char chunked_decode_buffer[body_size];
|
||||
os_memset(chunked_decode_buffer, 0, body_size);
|
||||
// Chunked data
|
||||
chunked_decode(body, chunked_decode_buffer);
|
||||
os_memcpy(body, chunked_decode_buffer, body_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (req->user_callback != NULL) { // Callback is optional.
|
||||
req->user_callback(body, http_status, req->buffer);
|
||||
}
|
||||
|
||||
os_free(req->buffer);
|
||||
os_free(req->hostname);
|
||||
os_free(req->path);
|
||||
os_free(req);
|
||||
}
|
||||
espconn_delete(conn);
|
||||
if(conn->proto.tcp != NULL) {
|
||||
os_free(conn->proto.tcp);
|
||||
}
|
||||
os_free(conn);
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR error_callback(void *arg, sint8 errType)
|
||||
{
|
||||
HTTP_DEBUG("Disconnected with error\n");
|
||||
disconnect_callback(arg);
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR dns_callback(const char * hostname, ip_addr_t * addr, void * arg)
|
||||
{
|
||||
request_args * req = (request_args *)arg;
|
||||
|
||||
if (addr == NULL) {
|
||||
os_printf("DNS failed for %s\n", hostname);
|
||||
if (req->user_callback != NULL) {
|
||||
req->user_callback("", -1, "");
|
||||
}
|
||||
os_free(req);
|
||||
}
|
||||
else {
|
||||
HTTP_DEBUG("DNS found %s " IPSTR "\n", hostname, IP2STR(addr));
|
||||
|
||||
struct espconn * conn = (struct espconn *)os_malloc(sizeof(struct espconn));
|
||||
conn->type = ESPCONN_TCP;
|
||||
conn->state = ESPCONN_NONE;
|
||||
conn->proto.tcp = (esp_tcp *)os_malloc(sizeof(esp_tcp));
|
||||
conn->proto.tcp->local_port = espconn_port();
|
||||
conn->proto.tcp->remote_port = req->port;
|
||||
conn->reverse = req;
|
||||
|
||||
os_memcpy(conn->proto.tcp->remote_ip, addr, 4);
|
||||
|
||||
espconn_regist_connectcb(conn, connect_callback);
|
||||
espconn_regist_disconcb(conn, disconnect_callback);
|
||||
espconn_regist_reconcb(conn, error_callback);
|
||||
|
||||
if (req->secure) {
|
||||
espconn_secure_set_size(ESPCONN_CLIENT,5120); // set SSL buffer size
|
||||
espconn_secure_connect(conn);
|
||||
} else {
|
||||
espconn_connect(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR http_raw_request(const char * hostname, int port, bool secure, const char * path, const char * post_data, const char * headers, http_callback user_callback)
|
||||
{
|
||||
HTTP_DEBUG("DNS request\n");
|
||||
|
||||
request_args * req = (request_args *)os_malloc(sizeof(request_args));
|
||||
req->hostname = esp_strdup(hostname);
|
||||
req->path = esp_strdup(path);
|
||||
req->port = port;
|
||||
req->secure = secure;
|
||||
req->headers = esp_strdup(headers);
|
||||
req->post_data = esp_strdup(post_data);
|
||||
req->buffer_size = 1;
|
||||
req->buffer = (char *)os_malloc(1);
|
||||
req->buffer[0] = '\0'; // Empty string.
|
||||
req->user_callback = user_callback;
|
||||
|
||||
ip_addr_t addr;
|
||||
err_t error = espconn_gethostbyname((struct espconn *)req, // It seems we don't need a real espconn pointer here.
|
||||
hostname, &addr, dns_callback);
|
||||
|
||||
if (error == ESPCONN_INPROGRESS) {
|
||||
HTTP_DEBUG("DNS pending\n");
|
||||
}
|
||||
else if (error == ESPCONN_OK) {
|
||||
// Already in the local names table (or hostname was an IP address), execute the callback ourselves.
|
||||
dns_callback(hostname, &addr, req);
|
||||
}
|
||||
else {
|
||||
if (error == ESPCONN_ARG) {
|
||||
os_printf("DNS arg error %s\n", hostname);
|
||||
}
|
||||
else {
|
||||
os_printf("DNS error code %d\n", error);
|
||||
}
|
||||
dns_callback(hostname, NULL, req); // Handle all DNS errors the same way.
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse an URL of the form http://host:port/path
|
||||
* <host> can be a hostname or an IP address
|
||||
* <port> is optional
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR http_post(const char * url, const char * post_data, const char * headers, http_callback user_callback)
|
||||
{
|
||||
// FIXME: handle HTTP auth with http://user:pass@host/
|
||||
// FIXME: get rid of the #anchor part if present.
|
||||
|
||||
char hostname[128] = "";
|
||||
int port = 80;
|
||||
bool secure = false;
|
||||
|
||||
bool is_http = os_strncmp(url, "http://", strlen("http://")) == 0;
|
||||
bool is_https = os_strncmp(url, "https://", strlen("https://")) == 0;
|
||||
|
||||
if (is_http)
|
||||
url += strlen("http://"); // Get rid of the protocol.
|
||||
else if (is_https) {
|
||||
port = 443;
|
||||
secure = true;
|
||||
url += strlen("https://"); // Get rid of the protocol.
|
||||
} else {
|
||||
os_printf("URL is not HTTP or HTTPS %s\n", url);
|
||||
return;
|
||||
}
|
||||
|
||||
char * path = os_strchr(url, '/');
|
||||
if (path == NULL) {
|
||||
path = os_strchr(url, '\0'); // Pointer to end of string.
|
||||
}
|
||||
|
||||
char * colon = os_strchr(url, ':');
|
||||
if (colon > path) {
|
||||
colon = NULL; // Limit the search to characters before the path.
|
||||
}
|
||||
|
||||
if (colon == NULL) { // The port is not present.
|
||||
os_memcpy(hostname, url, path - url);
|
||||
hostname[path - url] = '\0';
|
||||
}
|
||||
else {
|
||||
port = atoi(colon + 1);
|
||||
if (port == 0) {
|
||||
os_printf("Port error %s\n", url);
|
||||
return;
|
||||
}
|
||||
|
||||
os_memcpy(hostname, url, colon - url);
|
||||
hostname[colon - url] = '\0';
|
||||
}
|
||||
|
||||
|
||||
if (path[0] == '\0') { // Empty path is not allowed.
|
||||
path = "/";
|
||||
}
|
||||
|
||||
HTTP_DEBUG("hostname=%s\n", hostname);
|
||||
HTTP_DEBUG("port=%d\n", port);
|
||||
HTTP_DEBUG("path=%s\n", path);
|
||||
http_raw_request(hostname, port, secure, path, post_data, headers, user_callback);
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR http_get(const char * url, const char * headers, http_callback user_callback)
|
||||
{
|
||||
http_post(url, NULL, headers, user_callback);
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* Martin d'Allens <martin.dallens@gmail.com> wrote this file. As long as you retain
|
||||
* this notice you can do whatever you want with this stuff. If we meet some day,
|
||||
* and you think this stuff is worth it, you can buy me a beer in return.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef HTTPCLIENT_H
|
||||
#define HTTPCLIENT_H
|
||||
|
||||
#define HTTP_STATUS_GENERIC_ERROR -1 // In case of TCP or DNS error the callback is called with this status.
|
||||
#define BUFFER_SIZE_MAX 5000 // Size of http responses that will cause an error.
|
||||
|
||||
/*
|
||||
* "full_response" is a string containing all response headers and the response body.
|
||||
* "response_body and "http_status" are extracted from "full_response" for convenience.
|
||||
*
|
||||
* A successful request corresponds to an HTTP status code of 200 (OK).
|
||||
* More info at http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
||||
*/
|
||||
typedef void (* http_callback)(char * response_body, int http_status, char * full_response);
|
||||
|
||||
/*
|
||||
* Download a web page from its URL.
|
||||
* Try:
|
||||
* http_get("http://wtfismyip.com/text", http_callback_example);
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR http_get(const char * url, const char * headers, http_callback user_callback);
|
||||
|
||||
/*
|
||||
* Post data to a web form.
|
||||
* The data should be encoded as application/x-www-form-urlencoded.
|
||||
* Try:
|
||||
* http_post("http://httpbin.org/post", "first_word=hello&second_word=world", http_callback_example);
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR http_post(const char * url, const char * post_data, const char * headers, http_callback user_callback);
|
||||
|
||||
/*
|
||||
* Call this function to skip URL parsing if the arguments are already in separate variables.
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR http_raw_request(const char * hostname, int port, bool secure, const char * path, const char * post_data, const char * headers, http_callback user_callback);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* debug.h
|
||||
*
|
||||
* Created on: Dec 4, 2014
|
||||
* Author: Minh
|
||||
*/
|
||||
|
||||
#ifndef USER_DEBUG_H_
|
||||
#define USER_DEBUG_H_
|
||||
|
||||
#ifndef INFO
|
||||
#define INFO os_printf
|
||||
#endif
|
||||
|
||||
#endif /* USER_DEBUG_H_ */
|
|
@ -0,0 +1,653 @@
|
|||
/* mqtt.c
|
||||
* Protocol: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
|
||||
*
|
||||
* Copyright (c) 2014-2015, Tuan PM <tuanpm at live dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "user_interface.h"
|
||||
#include "osapi.h"
|
||||
#include "espconn.h"
|
||||
#include "os_type.h"
|
||||
#include "mem.h"
|
||||
#include "mqtt_msg.h"
|
||||
#include "debug.h"
|
||||
#include "user_config.h"
|
||||
#include "mqtt.h"
|
||||
#include "queue.h"
|
||||
|
||||
#define MQTT_TASK_PRIO 0
|
||||
#define MQTT_TASK_QUEUE_SIZE 1
|
||||
#define MQTT_SEND_TIMOUT 5
|
||||
|
||||
#ifndef QUEUE_BUFFER_SIZE
|
||||
#define QUEUE_BUFFER_SIZE 2048
|
||||
#endif
|
||||
|
||||
unsigned char *default_certificate;
|
||||
unsigned int default_certificate_len = 0;
|
||||
unsigned char *default_private_key;
|
||||
unsigned int default_private_key_len = 0;
|
||||
|
||||
os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE];
|
||||
|
||||
LOCAL void ICACHE_FLASH_ATTR
|
||||
mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
|
||||
{
|
||||
struct espconn *pConn = (struct espconn *)arg;
|
||||
MQTT_Client* client = (MQTT_Client *)pConn->reverse;
|
||||
|
||||
|
||||
if(ipaddr == NULL)
|
||||
{
|
||||
INFO("DNS: Found, but got no ip, try to reconnect\r\n");
|
||||
client->connState = TCP_RECONNECT_REQ;
|
||||
return;
|
||||
}
|
||||
|
||||
INFO("DNS: found ip %d.%d.%d.%d\n",
|
||||
*((uint8 *) &ipaddr->addr),
|
||||
*((uint8 *) &ipaddr->addr + 1),
|
||||
*((uint8 *) &ipaddr->addr + 2),
|
||||
*((uint8 *) &ipaddr->addr + 3));
|
||||
|
||||
if(client->ip.addr == 0 && ipaddr->addr != 0)
|
||||
{
|
||||
os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4);
|
||||
if(client->security){
|
||||
espconn_secure_connect(client->pCon);
|
||||
}
|
||||
else {
|
||||
espconn_connect(client->pCon);
|
||||
}
|
||||
|
||||
client->connState = TCP_CONNECTING;
|
||||
INFO("TCP: connecting...\r\n");
|
||||
}
|
||||
|
||||
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
|
||||
}
|
||||
|
||||
|
||||
|
||||
LOCAL void ICACHE_FLASH_ATTR
|
||||
deliver_publish(MQTT_Client* client, uint8_t* message, int length)
|
||||
{
|
||||
mqtt_event_data_t event_data;
|
||||
|
||||
event_data.topic_length = length;
|
||||
event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length);
|
||||
event_data.data_length = length;
|
||||
event_data.data = mqtt_get_publish_data(message, &event_data.data_length);
|
||||
|
||||
if(client->dataCb)
|
||||
client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Client received callback function.
|
||||
* @param arg: contain the ip link information
|
||||
* @param pdata: received data
|
||||
* @param len: the lenght of received data
|
||||
* @retval None
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
mqtt_tcpclient_recv(void *arg, char *pdata, unsigned short len)
|
||||
{
|
||||
uint8_t msg_type;
|
||||
uint8_t msg_qos;
|
||||
uint16_t msg_id;
|
||||
|
||||
struct espconn *pCon = (struct espconn*)arg;
|
||||
MQTT_Client *client = (MQTT_Client *)pCon->reverse;
|
||||
|
||||
READPACKET:
|
||||
INFO("TCP: data received %d bytes\r\n", len);
|
||||
if(len < MQTT_BUF_SIZE && len > 0){
|
||||
os_memcpy(client->mqtt_state.in_buffer, pdata, len);
|
||||
|
||||
msg_type = mqtt_get_type(client->mqtt_state.in_buffer);
|
||||
msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer);
|
||||
msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length);
|
||||
switch(client->connState){
|
||||
case MQTT_CONNECT_SENDING:
|
||||
if(msg_type == MQTT_MSG_TYPE_CONNACK){
|
||||
if(client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT){
|
||||
INFO("MQTT: Invalid packet\r\n");
|
||||
if(client->security){
|
||||
espconn_secure_disconnect(client->pCon);
|
||||
}
|
||||
else {
|
||||
espconn_disconnect(client->pCon);
|
||||
}
|
||||
} else {
|
||||
INFO("MQTT: Connected to %s:%d\r\n", client->host, client->port);
|
||||
client->connState = MQTT_DATA;
|
||||
if(client->connectedCb)
|
||||
client->connectedCb((uint32_t*)client);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case MQTT_DATA:
|
||||
client->mqtt_state.message_length_read = len;
|
||||
client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read);
|
||||
|
||||
|
||||
switch(msg_type)
|
||||
{
|
||||
|
||||
case MQTT_MSG_TYPE_SUBACK:
|
||||
if(client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id)
|
||||
INFO("MQTT: Subscribe successful\r\n");
|
||||
break;
|
||||
case MQTT_MSG_TYPE_UNSUBACK:
|
||||
if(client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id)
|
||||
INFO("MQTT: UnSubscribe successful\r\n");
|
||||
break;
|
||||
case MQTT_MSG_TYPE_PUBLISH:
|
||||
if(msg_qos == 1)
|
||||
client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id);
|
||||
else if(msg_qos == 2)
|
||||
client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id);
|
||||
if(msg_qos == 1 || msg_qos == 2){
|
||||
INFO("MQTT: Queue response QoS: %d\r\n", msg_qos);
|
||||
if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){
|
||||
INFO("MQTT: Queue full\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read);
|
||||
break;
|
||||
case MQTT_MSG_TYPE_PUBACK:
|
||||
if(client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id){
|
||||
INFO("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\r\n");
|
||||
}
|
||||
|
||||
break;
|
||||
case MQTT_MSG_TYPE_PUBREC:
|
||||
client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id);
|
||||
if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){
|
||||
INFO("MQTT: Queue full\r\n");
|
||||
}
|
||||
break;
|
||||
case MQTT_MSG_TYPE_PUBREL:
|
||||
client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id);
|
||||
if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){
|
||||
INFO("MQTT: Queue full\r\n");
|
||||
}
|
||||
break;
|
||||
case MQTT_MSG_TYPE_PUBCOMP:
|
||||
if(client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id){
|
||||
INFO("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\r\n");
|
||||
}
|
||||
break;
|
||||
case MQTT_MSG_TYPE_PINGREQ:
|
||||
client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection);
|
||||
if(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){
|
||||
INFO("MQTT: Queue full\r\n");
|
||||
}
|
||||
break;
|
||||
case MQTT_MSG_TYPE_PINGRESP:
|
||||
// Ignore
|
||||
break;
|
||||
}
|
||||
// NOTE: this is done down here and not in the switch case above
|
||||
// because the PSOCK_READBUF_LEN() won't work inside a switch
|
||||
// statement due to the way protothreads resume.
|
||||
if(msg_type == MQTT_MSG_TYPE_PUBLISH)
|
||||
{
|
||||
len = client->mqtt_state.message_length_read;
|
||||
|
||||
if(client->mqtt_state.message_length < client->mqtt_state.message_length_read)
|
||||
{
|
||||
//client->connState = MQTT_PUBLISH_RECV;
|
||||
//Not Implement yet
|
||||
len -= client->mqtt_state.message_length;
|
||||
pdata += client->mqtt_state.message_length;
|
||||
|
||||
INFO("Get another published message\r\n");
|
||||
goto READPACKET;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
INFO("ERROR: Message too long\r\n");
|
||||
}
|
||||
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Client send over callback function.
|
||||
* @param arg: contain the ip link information
|
||||
* @retval None
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
mqtt_tcpclient_sent_cb(void *arg)
|
||||
{
|
||||
struct espconn *pCon = (struct espconn *)arg;
|
||||
MQTT_Client* client = (MQTT_Client *)pCon->reverse;
|
||||
INFO("TCP: Sent\r\n");
|
||||
client->sendTimeout = 0;
|
||||
if(client->connState == MQTT_DATA && client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH){
|
||||
if(client->publishedCb)
|
||||
client->publishedCb((uint32_t*)client);
|
||||
}
|
||||
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR mqtt_timer(void *arg)
|
||||
{
|
||||
MQTT_Client* client = (MQTT_Client*)arg;
|
||||
|
||||
if(client->connState == MQTT_DATA){
|
||||
client->keepAliveTick ++;
|
||||
if(client->keepAliveTick > client->mqtt_state.connect_info->keepalive){
|
||||
|
||||
INFO("\r\nMQTT: Send keepalive packet to %s:%d!\r\n", client->host, client->port);
|
||||
client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection);
|
||||
client->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PINGREQ;
|
||||
client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data);
|
||||
client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);
|
||||
|
||||
|
||||
client->sendTimeout = MQTT_SEND_TIMOUT;
|
||||
INFO("MQTT: Sending, type: %d, id: %04X\r\n",client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id);
|
||||
if(client->security){
|
||||
espconn_secure_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);
|
||||
}
|
||||
else{
|
||||
espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);
|
||||
}
|
||||
|
||||
client->mqtt_state.outbound_message = NULL;
|
||||
|
||||
client->keepAliveTick = 0;
|
||||
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
|
||||
}
|
||||
|
||||
} else if(client->connState == TCP_RECONNECT_REQ){
|
||||
client->reconnectTick ++;
|
||||
if(client->reconnectTick > MQTT_RECONNECT_TIMEOUT) {
|
||||
client->reconnectTick = 0;
|
||||
client->connState = TCP_RECONNECT;
|
||||
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
|
||||
}
|
||||
}
|
||||
if(client->sendTimeout > 0)
|
||||
client->sendTimeout --;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
mqtt_tcpclient_discon_cb(void *arg)
|
||||
{
|
||||
|
||||
struct espconn *pespconn = (struct espconn *)arg;
|
||||
MQTT_Client* client = (MQTT_Client *)pespconn->reverse;
|
||||
INFO("TCP: Disconnected callback\r\n");
|
||||
client->connState = TCP_RECONNECT_REQ;
|
||||
if(client->disconnectedCb)
|
||||
client->disconnectedCb((uint32_t*)client);
|
||||
|
||||
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Tcp client connect success callback function.
|
||||
* @param arg: contain the ip link information
|
||||
* @retval None
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
mqtt_tcpclient_connect_cb(void *arg)
|
||||
{
|
||||
struct espconn *pCon = (struct espconn *)arg;
|
||||
MQTT_Client* client = (MQTT_Client *)pCon->reverse;
|
||||
|
||||
espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb);
|
||||
espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv);////////
|
||||
espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb);///////
|
||||
INFO("MQTT: Connected to broker %s:%d\r\n", client->host, client->port);
|
||||
|
||||
mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length);
|
||||
client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info);
|
||||
client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data);
|
||||
client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);
|
||||
|
||||
|
||||
client->sendTimeout = MQTT_SEND_TIMOUT;
|
||||
INFO("MQTT: Sending, type: %d, id: %04X\r\n",client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id);
|
||||
if(client->security){
|
||||
espconn_secure_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);
|
||||
}
|
||||
else{
|
||||
espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);
|
||||
}
|
||||
|
||||
client->mqtt_state.outbound_message = NULL;
|
||||
client->connState = MQTT_CONNECT_SENDING;
|
||||
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tcp client connect repeat callback function.
|
||||
* @param arg: contain the ip link information
|
||||
* @retval None
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
mqtt_tcpclient_recon_cb(void *arg, sint8 errType)
|
||||
{
|
||||
struct espconn *pCon = (struct espconn *)arg;
|
||||
MQTT_Client* client = (MQTT_Client *)pCon->reverse;
|
||||
|
||||
INFO("TCP: Reconnect to %s:%d\r\n", client->host, client->port);
|
||||
|
||||
client->connState = TCP_RECONNECT_REQ;
|
||||
|
||||
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MQTT publish function.
|
||||
* @param client: MQTT_Client reference
|
||||
* @param topic: string topic will publish to
|
||||
* @param data: buffer data send point to
|
||||
* @param data_length: length of data
|
||||
* @param qos: qos
|
||||
* @param retain: retain
|
||||
* @retval TRUE if success queue
|
||||
*/
|
||||
BOOL ICACHE_FLASH_ATTR
|
||||
MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain)
|
||||
{
|
||||
uint8_t dataBuffer[MQTT_BUF_SIZE];
|
||||
uint16_t dataLen;
|
||||
client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection,
|
||||
topic, data, data_length,
|
||||
qos, retain,
|
||||
&client->mqtt_state.pending_msg_id);
|
||||
if(client->mqtt_state.outbound_message->length == 0){
|
||||
INFO("MQTT: Queuing publish failed\r\n");
|
||||
return FALSE;
|
||||
}
|
||||
INFO("MQTT: queuing publish, length: %d, queue size(%d/%d)\r\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size);
|
||||
while(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){
|
||||
INFO("MQTT: Queue full\r\n");
|
||||
if(QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) {
|
||||
INFO("MQTT: Serious buffer error\r\n");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MQTT subscibe function.
|
||||
* @param client: MQTT_Client reference
|
||||
* @param topic: string topic will subscribe
|
||||
* @param qos: qos
|
||||
* @retval TRUE if success queue
|
||||
*/
|
||||
BOOL ICACHE_FLASH_ATTR
|
||||
MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos)
|
||||
{
|
||||
uint8_t dataBuffer[MQTT_BUF_SIZE];
|
||||
uint16_t dataLen;
|
||||
|
||||
client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection,
|
||||
topic, 0,
|
||||
&client->mqtt_state.pending_msg_id);
|
||||
INFO("MQTT: queue subscribe, topic\"%s\", id: %d\r\n",topic, client->mqtt_state.pending_msg_id);
|
||||
while(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){
|
||||
INFO("MQTT: Queue full\r\n");
|
||||
if(QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) {
|
||||
INFO("MQTT: Serious buffer error\r\n");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
MQTT_Task(os_event_t *e)
|
||||
{
|
||||
MQTT_Client* client = (MQTT_Client*)e->par;
|
||||
uint8_t dataBuffer[MQTT_BUF_SIZE];
|
||||
uint16_t dataLen;
|
||||
if(e->par == 0)
|
||||
return;
|
||||
switch(client->connState){
|
||||
|
||||
case TCP_RECONNECT_REQ:
|
||||
break;
|
||||
case TCP_RECONNECT:
|
||||
MQTT_Connect(client);
|
||||
INFO("TCP: Reconnect to: %s:%d\r\n", client->host, client->port);
|
||||
client->connState = TCP_CONNECTING;
|
||||
break;
|
||||
case MQTT_DATA:
|
||||
if(QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) {
|
||||
break;
|
||||
}
|
||||
if(QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0){
|
||||
client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer);
|
||||
client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen);
|
||||
|
||||
|
||||
client->sendTimeout = MQTT_SEND_TIMOUT;
|
||||
INFO("MQTT: Sending, type: %d, id: %04X\r\n",client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id);
|
||||
if(client->security){
|
||||
espconn_secure_sent(client->pCon, dataBuffer, dataLen);
|
||||
}
|
||||
else{
|
||||
espconn_sent(client->pCon, dataBuffer, dataLen);
|
||||
}
|
||||
|
||||
client->mqtt_state.outbound_message = NULL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MQTT initialization connection function
|
||||
* @param client: MQTT_Client reference
|
||||
* @param host: Domain or IP string
|
||||
* @param port: Port to connect
|
||||
* @param security: 1 for ssl, 0 for none
|
||||
* @retval None
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t security)
|
||||
{
|
||||
uint32_t temp;
|
||||
INFO("MQTT_InitConnection\r\n");
|
||||
os_memset(mqttClient, 0, sizeof(MQTT_Client));
|
||||
temp = os_strlen(host);
|
||||
mqttClient->host = (uint8_t*)os_zalloc(temp + 1);
|
||||
os_strcpy(mqttClient->host, host);
|
||||
mqttClient->host[temp] = 0;
|
||||
mqttClient->port = port;
|
||||
mqttClient->security = security;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MQTT initialization mqtt client function
|
||||
* @param client: MQTT_Client reference
|
||||
* @param clientid: MQTT client id
|
||||
* @param client_user:MQTT client user
|
||||
* @param client_pass:MQTT client password
|
||||
* @param client_pass:MQTT keep alive timer, in second
|
||||
* @retval None
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession)
|
||||
{
|
||||
uint32_t temp;
|
||||
INFO("MQTT_InitClient\r\n");
|
||||
|
||||
os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t));
|
||||
|
||||
temp = os_strlen(client_id);
|
||||
mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(temp + 1);
|
||||
os_strcpy(mqttClient->connect_info.client_id, client_id);
|
||||
mqttClient->connect_info.client_id[temp] = 0;
|
||||
|
||||
temp = os_strlen(client_user);
|
||||
mqttClient->connect_info.username = (uint8_t*)os_zalloc(temp + 1);
|
||||
os_strcpy(mqttClient->connect_info.username, client_user);
|
||||
mqttClient->connect_info.username[temp] = 0;
|
||||
|
||||
temp = os_strlen(client_pass);
|
||||
mqttClient->connect_info.password = (uint8_t*)os_zalloc(temp + 1);
|
||||
os_strcpy(mqttClient->connect_info.password, client_pass);
|
||||
mqttClient->connect_info.password[temp] = 0;
|
||||
|
||||
|
||||
mqttClient->connect_info.keepalive = keepAliveTime;
|
||||
mqttClient->connect_info.clean_session = cleanSession;
|
||||
|
||||
mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE);
|
||||
mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE;
|
||||
mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE);
|
||||
mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE;
|
||||
mqttClient->mqtt_state.connect_info = &mqttClient->connect_info;
|
||||
|
||||
mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length);
|
||||
|
||||
QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE);
|
||||
|
||||
system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE);
|
||||
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient);
|
||||
}
|
||||
void ICACHE_FLASH_ATTR
|
||||
MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain)
|
||||
{
|
||||
uint32_t temp;
|
||||
temp = os_strlen(will_topic);
|
||||
mqttClient->connect_info.will_topic = (uint8_t*)os_zalloc(temp + 1);
|
||||
os_strcpy(mqttClient->connect_info.will_topic, will_topic);
|
||||
mqttClient->connect_info.will_topic[temp] = 0;
|
||||
|
||||
temp = os_strlen(will_msg);
|
||||
mqttClient->connect_info.will_message = (uint8_t*)os_zalloc(temp + 1);
|
||||
os_strcpy(mqttClient->connect_info.will_message, will_msg);
|
||||
mqttClient->connect_info.will_message[temp] = 0;
|
||||
|
||||
|
||||
mqttClient->connect_info.will_qos = will_qos;
|
||||
mqttClient->connect_info.will_retain = will_retain;
|
||||
}
|
||||
/**
|
||||
* @brief Begin connect to MQTT broker
|
||||
* @param client: MQTT_Client reference
|
||||
* @retval None
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR
|
||||
MQTT_Connect(MQTT_Client *mqttClient)
|
||||
{
|
||||
MQTT_Disconnect(mqttClient);
|
||||
mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn));
|
||||
mqttClient->pCon->type = ESPCONN_TCP;
|
||||
mqttClient->pCon->state = ESPCONN_NONE;
|
||||
mqttClient->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));
|
||||
mqttClient->pCon->proto.tcp->local_port = espconn_port();
|
||||
mqttClient->pCon->proto.tcp->remote_port = mqttClient->port;
|
||||
mqttClient->pCon->reverse = mqttClient;
|
||||
espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb);
|
||||
espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb);
|
||||
|
||||
mqttClient->keepAliveTick = 0;
|
||||
mqttClient->reconnectTick = 0;
|
||||
|
||||
|
||||
os_timer_disarm(&mqttClient->mqttTimer);
|
||||
os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient);
|
||||
os_timer_arm(&mqttClient->mqttTimer, 1000, 1);
|
||||
|
||||
if(UTILS_StrToIP(mqttClient->host, &mqttClient->pCon->proto.tcp->remote_ip)) {
|
||||
INFO("TCP: Connect to ip %s:%d\r\n", mqttClient->host, mqttClient->port);
|
||||
if(mqttClient->security){
|
||||
espconn_secure_connect(mqttClient->pCon);
|
||||
}
|
||||
else {
|
||||
espconn_connect(mqttClient->pCon);
|
||||
}
|
||||
}
|
||||
else {
|
||||
INFO("TCP: Connect to domain %s:%d\r\n", mqttClient->host, mqttClient->port);
|
||||
espconn_gethostbyname(mqttClient->pCon, mqttClient->host, &mqttClient->ip, mqtt_dns_found);
|
||||
}
|
||||
mqttClient->connState = TCP_CONNECTING;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
MQTT_Disconnect(MQTT_Client *mqttClient)
|
||||
{
|
||||
if(mqttClient->pCon){
|
||||
INFO("Free memory\r\n");
|
||||
if(mqttClient->pCon->proto.tcp)
|
||||
os_free(mqttClient->pCon->proto.tcp);
|
||||
os_free(mqttClient->pCon);
|
||||
mqttClient->pCon = NULL;
|
||||
}
|
||||
|
||||
os_timer_disarm(&mqttClient->mqttTimer);
|
||||
}
|
||||
void ICACHE_FLASH_ATTR
|
||||
MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb)
|
||||
{
|
||||
mqttClient->connectedCb = connectedCb;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb)
|
||||
{
|
||||
mqttClient->disconnectedCb = disconnectedCb;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb)
|
||||
{
|
||||
mqttClient->dataCb = dataCb;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb)
|
||||
{
|
||||
mqttClient->publishedCb = publishedCb;
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/* mqtt.h
|
||||
*
|
||||
* Copyright (c) 2014-2015, Tuan PM <tuanpm at live dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef USER_AT_MQTT_H_
|
||||
#define USER_AT_MQTT_H_
|
||||
#include "mqtt_msg.h"
|
||||
#include "user_interface.h"
|
||||
|
||||
#include "queue.h"
|
||||
typedef struct mqtt_event_data_t
|
||||
{
|
||||
uint8_t type;
|
||||
const char* topic;
|
||||
const char* data;
|
||||
uint16_t topic_length;
|
||||
uint16_t data_length;
|
||||
uint16_t data_offset;
|
||||
} mqtt_event_data_t;
|
||||
|
||||
typedef struct mqtt_state_t
|
||||
{
|
||||
uint16_t port;
|
||||
int auto_reconnect;
|
||||
mqtt_connect_info_t* connect_info;
|
||||
uint8_t* in_buffer;
|
||||
uint8_t* out_buffer;
|
||||
int in_buffer_length;
|
||||
int out_buffer_length;
|
||||
uint16_t message_length;
|
||||
uint16_t message_length_read;
|
||||
mqtt_message_t* outbound_message;
|
||||
mqtt_connection_t mqtt_connection;
|
||||
uint16_t pending_msg_id;
|
||||
int pending_msg_type;
|
||||
int pending_publish_qos;
|
||||
} mqtt_state_t;
|
||||
|
||||
typedef enum {
|
||||
WIFI_INIT,
|
||||
WIFI_CONNECTING,
|
||||
WIFI_CONNECTING_ERROR,
|
||||
WIFI_CONNECTED,
|
||||
DNS_RESOLVE,
|
||||
TCP_DISCONNECTED,
|
||||
TCP_RECONNECT_REQ,
|
||||
TCP_RECONNECT,
|
||||
TCP_CONNECTING,
|
||||
TCP_CONNECTING_ERROR,
|
||||
TCP_CONNECTED,
|
||||
MQTT_CONNECT_SEND,
|
||||
MQTT_CONNECT_SENDING,
|
||||
MQTT_SUBSCIBE_SEND,
|
||||
MQTT_SUBSCIBE_SENDING,
|
||||
MQTT_DATA,
|
||||
MQTT_PUBLISH_RECV,
|
||||
MQTT_PUBLISHING
|
||||
} tConnState;
|
||||
|
||||
typedef void (*MqttCallback)(uint32_t *args);
|
||||
typedef void (*MqttDataCallback)(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t lengh);
|
||||
|
||||
typedef struct {
|
||||
struct espconn *pCon;
|
||||
uint8_t security;
|
||||
uint8_t* host;
|
||||
uint32_t port;
|
||||
ip_addr_t ip;
|
||||
mqtt_state_t mqtt_state;
|
||||
mqtt_connect_info_t connect_info;
|
||||
MqttCallback connectedCb;
|
||||
MqttCallback disconnectedCb;
|
||||
MqttCallback publishedCb;
|
||||
MqttDataCallback dataCb;
|
||||
ETSTimer mqttTimer;
|
||||
uint32_t keepAliveTick;
|
||||
uint32_t reconnectTick;
|
||||
uint32_t sendTimeout;
|
||||
tConnState connState;
|
||||
QUEUE msgQueue;
|
||||
void* user_data;
|
||||
} MQTT_Client;
|
||||
|
||||
#define SEC_NONSSL 0
|
||||
#define SEC_SSL 1
|
||||
|
||||
#define MQTT_FLAG_CONNECTED 1
|
||||
#define MQTT_FLAG_READY 2
|
||||
#define MQTT_FLAG_EXIT 4
|
||||
|
||||
#define MQTT_EVENT_TYPE_NONE 0
|
||||
#define MQTT_EVENT_TYPE_CONNECTED 1
|
||||
#define MQTT_EVENT_TYPE_DISCONNECTED 2
|
||||
#define MQTT_EVENT_TYPE_SUBSCRIBED 3
|
||||
#define MQTT_EVENT_TYPE_UNSUBSCRIBED 4
|
||||
#define MQTT_EVENT_TYPE_PUBLISH 5
|
||||
#define MQTT_EVENT_TYPE_PUBLISHED 6
|
||||
#define MQTT_EVENT_TYPE_EXITED 7
|
||||
#define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8
|
||||
|
||||
void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t security);
|
||||
void ICACHE_FLASH_ATTR MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession);
|
||||
void ICACHE_FLASH_ATTR MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain);
|
||||
void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb);
|
||||
void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb);
|
||||
void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb);
|
||||
void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb);
|
||||
BOOL ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos);
|
||||
void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client *mqttClient);
|
||||
void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client *mqttClient);
|
||||
BOOL ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain);
|
||||
|
||||
#endif /* USER_AT_MQTT_H_ */
|
|
@ -0,0 +1,472 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Stephen Robinson
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "mqtt_msg.h"
|
||||
#include "user_config.h"
|
||||
#define MQTT_MAX_FIXED_HEADER_SIZE 3
|
||||
|
||||
enum mqtt_connect_flag
|
||||
{
|
||||
MQTT_CONNECT_FLAG_USERNAME = 1 << 7,
|
||||
MQTT_CONNECT_FLAG_PASSWORD = 1 << 6,
|
||||
MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5,
|
||||
MQTT_CONNECT_FLAG_WILL = 1 << 2,
|
||||
MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1
|
||||
};
|
||||
|
||||
struct __attribute((__packed__)) mqtt_connect_variable_header
|
||||
{
|
||||
uint8_t lengthMsb;
|
||||
uint8_t lengthLsb;
|
||||
#if defined(PROTOCOL_NAMEv31)
|
||||
uint8_t magic[6];
|
||||
#elif defined(PROTOCOL_NAMEv311)
|
||||
uint8_t magic[4];
|
||||
#else
|
||||
#error "Please define protocol name"
|
||||
#endif
|
||||
uint8_t version;
|
||||
uint8_t flags;
|
||||
uint8_t keepaliveMsb;
|
||||
uint8_t keepaliveLsb;
|
||||
};
|
||||
|
||||
static int ICACHE_FLASH_ATTR append_string(mqtt_connection_t* connection, const char* string, int len)
|
||||
{
|
||||
if(connection->message.length + len + 2 > connection->buffer_length)
|
||||
return -1;
|
||||
|
||||
connection->buffer[connection->message.length++] = len >> 8;
|
||||
connection->buffer[connection->message.length++] = len & 0xff;
|
||||
memcpy(connection->buffer + connection->message.length, string, len);
|
||||
connection->message.length += len;
|
||||
|
||||
return len + 2;
|
||||
}
|
||||
|
||||
static uint16_t ICACHE_FLASH_ATTR append_message_id(mqtt_connection_t* connection, uint16_t message_id)
|
||||
{
|
||||
// If message_id is zero then we should assign one, otherwise
|
||||
// we'll use the one supplied by the caller
|
||||
while(message_id == 0)
|
||||
message_id = ++connection->message_id;
|
||||
|
||||
if(connection->message.length + 2 > connection->buffer_length)
|
||||
return 0;
|
||||
|
||||
connection->buffer[connection->message.length++] = message_id >> 8;
|
||||
connection->buffer[connection->message.length++] = message_id & 0xff;
|
||||
|
||||
return message_id;
|
||||
}
|
||||
|
||||
static int ICACHE_FLASH_ATTR init_message(mqtt_connection_t* connection)
|
||||
{
|
||||
connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
return MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
}
|
||||
|
||||
static mqtt_message_t* ICACHE_FLASH_ATTR fail_message(mqtt_connection_t* connection)
|
||||
{
|
||||
connection->message.data = connection->buffer;
|
||||
connection->message.length = 0;
|
||||
return &connection->message;
|
||||
}
|
||||
|
||||
static mqtt_message_t* ICACHE_FLASH_ATTR fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain)
|
||||
{
|
||||
int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
|
||||
if(remaining_length > 127)
|
||||
{
|
||||
connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1);
|
||||
connection->buffer[1] = 0x80 | (remaining_length % 128);
|
||||
connection->buffer[2] = remaining_length / 128;
|
||||
connection->message.length = remaining_length + 3;
|
||||
connection->message.data = connection->buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1);
|
||||
connection->buffer[2] = remaining_length;
|
||||
connection->message.length = remaining_length + 2;
|
||||
connection->message.data = connection->buffer + 1;
|
||||
}
|
||||
|
||||
return &connection->message;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length)
|
||||
{
|
||||
memset(connection, 0, sizeof(connection));
|
||||
connection->buffer = buffer;
|
||||
connection->buffer_length = buffer_length;
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length)
|
||||
{
|
||||
int i;
|
||||
int totlen = 0;
|
||||
|
||||
for(i = 1; i < length; ++i)
|
||||
{
|
||||
totlen += (buffer[i] & 0x7f) << (7 * (i - 1));
|
||||
if((buffer[i] & 0x80) == 0)
|
||||
{
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
totlen += i;
|
||||
|
||||
return totlen;
|
||||
}
|
||||
|
||||
const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length)
|
||||
{
|
||||
int i;
|
||||
int totlen = 0;
|
||||
int topiclen;
|
||||
|
||||
for(i = 1; i < *length; ++i)
|
||||
{
|
||||
totlen += (buffer[i] & 0x7f) << (7 * (i -1));
|
||||
if((buffer[i] & 0x80) == 0)
|
||||
{
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
totlen += i;
|
||||
|
||||
if(i + 2 >= *length)
|
||||
return NULL;
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
|
||||
if(i + topiclen > *length)
|
||||
return NULL;
|
||||
|
||||
*length = topiclen;
|
||||
return (const char*)(buffer + i);
|
||||
}
|
||||
|
||||
const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length)
|
||||
{
|
||||
int i;
|
||||
int totlen = 0;
|
||||
int topiclen;
|
||||
|
||||
for(i = 1; i < *length; ++i)
|
||||
{
|
||||
totlen += (buffer[i] & 0x7f) << (7 * (i - 1));
|
||||
if((buffer[i] & 0x80) == 0)
|
||||
{
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
totlen += i;
|
||||
|
||||
if(i + 2 >= *length)
|
||||
return NULL;
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
|
||||
if(i + topiclen >= *length){
|
||||
*length = 0;
|
||||
return NULL;
|
||||
}
|
||||
i += topiclen;
|
||||
|
||||
if(mqtt_get_qos(buffer) > 0)
|
||||
{
|
||||
if(i + 2 >= *length)
|
||||
return NULL;
|
||||
i += 2;
|
||||
}
|
||||
|
||||
if(totlen < i)
|
||||
return NULL;
|
||||
|
||||
if(totlen <= *length)
|
||||
*length = totlen - i;
|
||||
else
|
||||
*length = *length - i;
|
||||
return (const char*)(buffer + i);
|
||||
}
|
||||
|
||||
uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length)
|
||||
{
|
||||
if(length < 1)
|
||||
return 0;
|
||||
|
||||
switch(mqtt_get_type(buffer))
|
||||
{
|
||||
case MQTT_MSG_TYPE_PUBLISH:
|
||||
{
|
||||
int i;
|
||||
int topiclen;
|
||||
|
||||
for(i = 1; i < length; ++i)
|
||||
{
|
||||
if((buffer[i] & 0x80) == 0)
|
||||
{
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i + 2 >= length)
|
||||
return 0;
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
|
||||
if(i + topiclen >= length)
|
||||
return 0;
|
||||
i += topiclen;
|
||||
|
||||
if(mqtt_get_qos(buffer) > 0)
|
||||
{
|
||||
if(i + 2 >= length)
|
||||
return 0;
|
||||
//i += 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (buffer[i] << 8) | buffer[i + 1];
|
||||
}
|
||||
case MQTT_MSG_TYPE_PUBACK:
|
||||
case MQTT_MSG_TYPE_PUBREC:
|
||||
case MQTT_MSG_TYPE_PUBREL:
|
||||
case MQTT_MSG_TYPE_PUBCOMP:
|
||||
case MQTT_MSG_TYPE_SUBACK:
|
||||
case MQTT_MSG_TYPE_UNSUBACK:
|
||||
case MQTT_MSG_TYPE_SUBSCRIBE:
|
||||
{
|
||||
// This requires the remaining length to be encoded in 1 byte,
|
||||
// which it should be.
|
||||
if(length >= 4 && (buffer[1] & 0x80) == 0)
|
||||
return (buffer[2] << 8) | buffer[3];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info)
|
||||
{
|
||||
struct mqtt_connect_variable_header* variable_header;
|
||||
|
||||
init_message(connection);
|
||||
|
||||
if(connection->message.length + sizeof(*variable_header) > connection->buffer_length)
|
||||
return fail_message(connection);
|
||||
variable_header = (void*)(connection->buffer + connection->message.length);
|
||||
connection->message.length += sizeof(*variable_header);
|
||||
|
||||
variable_header->lengthMsb = 0;
|
||||
#if defined(PROTOCOL_NAMEv31)
|
||||
variable_header->lengthLsb = 6;
|
||||
memcpy(variable_header->magic, "MQIsdp", 6);
|
||||
variable_header->version = 3;
|
||||
#elif defined(PROTOCOL_NAMEv311)
|
||||
variable_header->lengthLsb = 4;
|
||||
memcpy(variable_header->magic, "MQTT", 4);
|
||||
variable_header->version = 4;
|
||||
#else
|
||||
#error "Please define protocol name"
|
||||
#endif
|
||||
|
||||
variable_header->flags = 0;
|
||||
variable_header->keepaliveMsb = info->keepalive >> 8;
|
||||
variable_header->keepaliveLsb = info->keepalive & 0xff;
|
||||
|
||||
if(info->clean_session)
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
|
||||
|
||||
if(info->client_id != NULL && info->client_id[0] != '\0')
|
||||
{
|
||||
if(append_string(connection, info->client_id, strlen(info->client_id)) < 0)
|
||||
return fail_message(connection);
|
||||
}
|
||||
else
|
||||
return fail_message(connection);
|
||||
|
||||
if(info->will_topic != NULL && info->will_topic[0] != '\0')
|
||||
{
|
||||
if(append_string(connection, info->will_topic, strlen(info->will_topic)) < 0)
|
||||
return fail_message(connection);
|
||||
|
||||
if(append_string(connection, info->will_message, strlen(info->will_message)) < 0)
|
||||
return fail_message(connection);
|
||||
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_WILL;
|
||||
if(info->will_retain)
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
|
||||
variable_header->flags |= (info->will_qos & 3) << 3;
|
||||
}
|
||||
|
||||
if(info->username != NULL && info->username[0] != '\0')
|
||||
{
|
||||
if(append_string(connection, info->username, strlen(info->username)) < 0)
|
||||
return fail_message(connection);
|
||||
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME;
|
||||
}
|
||||
|
||||
if(info->password != NULL && info->password[0] != '\0')
|
||||
{
|
||||
if(append_string(connection, info->password, strlen(info->password)) < 0)
|
||||
return fail_message(connection);
|
||||
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD;
|
||||
}
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id)
|
||||
{
|
||||
init_message(connection);
|
||||
|
||||
if(topic == NULL || topic[0] == '\0')
|
||||
return fail_message(connection);
|
||||
|
||||
if(append_string(connection, topic, strlen(topic)) < 0)
|
||||
return fail_message(connection);
|
||||
|
||||
if(qos > 0)
|
||||
{
|
||||
if((*message_id = append_message_id(connection, 0)) == 0)
|
||||
return fail_message(connection);
|
||||
}
|
||||
else
|
||||
*message_id = 0;
|
||||
|
||||
if(connection->message.length + data_length > connection->buffer_length)
|
||||
return fail_message(connection);
|
||||
memcpy(connection->buffer + connection->message.length, data, data_length);
|
||||
connection->message.length += data_length;
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain);
|
||||
}
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id)
|
||||
{
|
||||
init_message(connection);
|
||||
if(append_message_id(connection, message_id) == 0)
|
||||
return fail_message(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id)
|
||||
{
|
||||
init_message(connection);
|
||||
if(append_message_id(connection, message_id) == 0)
|
||||
return fail_message(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id)
|
||||
{
|
||||
init_message(connection);
|
||||
if(append_message_id(connection, message_id) == 0)
|
||||
return fail_message(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id)
|
||||
{
|
||||
init_message(connection);
|
||||
if(append_message_id(connection, message_id) == 0)
|
||||
return fail_message(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id)
|
||||
{
|
||||
init_message(connection);
|
||||
|
||||
if(topic == NULL || topic[0] == '\0')
|
||||
return fail_message(connection);
|
||||
|
||||
if((*message_id = append_message_id(connection, 0)) == 0)
|
||||
return fail_message(connection);
|
||||
|
||||
if(append_string(connection, topic, strlen(topic)) < 0)
|
||||
return fail_message(connection);
|
||||
|
||||
if(connection->message.length + 1 > connection->buffer_length)
|
||||
return fail_message(connection);
|
||||
connection->buffer[connection->message.length++] = qos;
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id)
|
||||
{
|
||||
init_message(connection);
|
||||
|
||||
if(topic == NULL || topic[0] == '\0')
|
||||
return fail_message(connection);
|
||||
|
||||
if((*message_id = append_message_id(connection, 0)) == 0)
|
||||
return fail_message(connection);
|
||||
|
||||
if(append_string(connection, topic, strlen(topic)) < 0)
|
||||
return fail_message(connection);
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection)
|
||||
{
|
||||
init_message(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection)
|
||||
{
|
||||
init_message(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection)
|
||||
{
|
||||
init_message(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0);
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* File: mqtt_msg.h
|
||||
* Author: Minh Tuan
|
||||
*
|
||||
* Created on July 12, 2014, 1:05 PM
|
||||
*/
|
||||
|
||||
#ifndef MQTT_MSG_H
|
||||
#define MQTT_MSG_H
|
||||
#include "c_types.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, Stephen Robinson
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/* 7 6 5 4 3 2 1 0*/
|
||||
/*| --- Message Type---- | DUP Flag | QoS Level | Retain |
|
||||
/* Remaining Length */
|
||||
|
||||
|
||||
enum mqtt_message_type
|
||||
{
|
||||
MQTT_MSG_TYPE_CONNECT = 1,
|
||||
MQTT_MSG_TYPE_CONNACK = 2,
|
||||
MQTT_MSG_TYPE_PUBLISH = 3,
|
||||
MQTT_MSG_TYPE_PUBACK = 4,
|
||||
MQTT_MSG_TYPE_PUBREC = 5,
|
||||
MQTT_MSG_TYPE_PUBREL = 6,
|
||||
MQTT_MSG_TYPE_PUBCOMP = 7,
|
||||
MQTT_MSG_TYPE_SUBSCRIBE = 8,
|
||||
MQTT_MSG_TYPE_SUBACK = 9,
|
||||
MQTT_MSG_TYPE_UNSUBSCRIBE = 10,
|
||||
MQTT_MSG_TYPE_UNSUBACK = 11,
|
||||
MQTT_MSG_TYPE_PINGREQ = 12,
|
||||
MQTT_MSG_TYPE_PINGRESP = 13,
|
||||
MQTT_MSG_TYPE_DISCONNECT = 14
|
||||
};
|
||||
|
||||
typedef struct mqtt_message
|
||||
{
|
||||
uint8_t* data;
|
||||
uint16_t length;
|
||||
|
||||
} mqtt_message_t;
|
||||
|
||||
typedef struct mqtt_connection
|
||||
{
|
||||
mqtt_message_t message;
|
||||
|
||||
uint16_t message_id;
|
||||
uint8_t* buffer;
|
||||
uint16_t buffer_length;
|
||||
|
||||
} mqtt_connection_t;
|
||||
|
||||
typedef struct mqtt_connect_info
|
||||
{
|
||||
char* client_id;
|
||||
char* username;
|
||||
char* password;
|
||||
char* will_topic;
|
||||
char* will_message;
|
||||
int keepalive;
|
||||
int will_qos;
|
||||
int will_retain;
|
||||
int clean_session;
|
||||
|
||||
} mqtt_connect_info_t;
|
||||
|
||||
|
||||
static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; }
|
||||
static inline int ICACHE_FLASH_ATTR mqtt_get_dup(uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; }
|
||||
static inline int ICACHE_FLASH_ATTR mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; }
|
||||
static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); }
|
||||
|
||||
void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length);
|
||||
int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length);
|
||||
const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length);
|
||||
const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length);
|
||||
uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length);
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info);
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id);
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id);
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id);
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id);
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id);
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id);
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id);
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection);
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection);
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MQTT_MSG_H */
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
#include "proto.h"
|
||||
#include "ringbuf.h"
|
||||
I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize)
|
||||
{
|
||||
parser->buf = buf;
|
||||
parser->bufSize = bufSize;
|
||||
parser->dataLen = 0;
|
||||
parser->callback = completeCallback;
|
||||
parser->isEsc = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value)
|
||||
{
|
||||
switch(value){
|
||||
case 0x7D:
|
||||
parser->isEsc = 1;
|
||||
break;
|
||||
|
||||
case 0x7E:
|
||||
parser->dataLen = 0;
|
||||
parser->isEsc = 0;
|
||||
parser->isBegin = 1;
|
||||
break;
|
||||
|
||||
case 0x7F:
|
||||
if (parser->callback != NULL)
|
||||
parser->callback();
|
||||
parser->isBegin = 0;
|
||||
return 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
if(parser->isBegin == 0) break;
|
||||
|
||||
if(parser->isEsc){
|
||||
value ^= 0x20;
|
||||
parser->isEsc = 0;
|
||||
}
|
||||
|
||||
if(parser->dataLen < parser->bufSize)
|
||||
parser->buf[parser->dataLen++] = value;
|
||||
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len)
|
||||
{
|
||||
while(len--)
|
||||
PROTO_ParseByte(parser, *buf++);
|
||||
|
||||
return 0;
|
||||
}
|
||||
I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF* rb, U8 *bufOut, U16* len, U16 maxBufLen)
|
||||
{
|
||||
U8 c;
|
||||
|
||||
PROTO_PARSER proto;
|
||||
PROTO_Init(&proto, NULL, bufOut, maxBufLen);
|
||||
while(RINGBUF_Get(rb, &c) == 0){
|
||||
if(PROTO_ParseByte(&proto, c) == 0){
|
||||
*len = proto.dataLen;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize)
|
||||
{
|
||||
U16 i = 2;
|
||||
U16 len = *(U16*) packet;
|
||||
|
||||
if (bufSize < 1) return -1;
|
||||
|
||||
*buf++ = 0x7E;
|
||||
bufSize--;
|
||||
|
||||
while (len--) {
|
||||
switch (*packet) {
|
||||
case 0x7D:
|
||||
case 0x7E:
|
||||
case 0x7F:
|
||||
if (bufSize < 2) return -1;
|
||||
*buf++ = 0x7D;
|
||||
*buf++ = *packet++ ^ 0x20;
|
||||
i += 2;
|
||||
bufSize -= 2;
|
||||
break;
|
||||
default:
|
||||
if (bufSize < 1) return -1;
|
||||
*buf++ = *packet++;
|
||||
i++;
|
||||
bufSize--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bufSize < 1) return -1;
|
||||
*buf++ = 0x7F;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len)
|
||||
{
|
||||
U16 i = 2;
|
||||
if(RINGBUF_Put(rb, 0x7E) == -1) return -1;
|
||||
while (len--) {
|
||||
switch (*packet) {
|
||||
case 0x7D:
|
||||
case 0x7E:
|
||||
case 0x7F:
|
||||
if(RINGBUF_Put(rb, 0x7D) == -1) return -1;
|
||||
if(RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1;
|
||||
i += 2;
|
||||
break;
|
||||
default:
|
||||
if(RINGBUF_Put(rb, *packet++) == -1) return -1;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(RINGBUF_Put(rb, 0x7F) == -1) return -1;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* File: proto.h
|
||||
* Author: ThuHien
|
||||
*
|
||||
* Created on November 23, 2012, 8:57 AM
|
||||
*/
|
||||
|
||||
#ifndef _PROTO_H_
|
||||
#define _PROTO_H_
|
||||
#include <stdlib.h>
|
||||
#include "typedef.h"
|
||||
#include "ringbuf.h"
|
||||
|
||||
typedef void(PROTO_PARSE_CALLBACK)();
|
||||
|
||||
typedef struct{
|
||||
U8 *buf;
|
||||
U16 bufSize;
|
||||
U16 dataLen;
|
||||
U8 isEsc;
|
||||
U8 isBegin;
|
||||
PROTO_PARSE_CALLBACK* callback;
|
||||
}PROTO_PARSER;
|
||||
|
||||
I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize);
|
||||
I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len);
|
||||
I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize);
|
||||
I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len);
|
||||
I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value);
|
||||
I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF *rb, U8 *bufOut, U16* len, U16 maxBufLen);
|
||||
#endif
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/* str_queue.c
|
||||
*
|
||||
* Copyright (c) 2014-2015, Tuan PM <tuanpm at live dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "queue.h"
|
||||
|
||||
#include "user_interface.h"
|
||||
#include "osapi.h"
|
||||
#include "os_type.h"
|
||||
#include "mem.h"
|
||||
#include "proto.h"
|
||||
void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize)
|
||||
{
|
||||
queue->buf = (uint8_t*)os_zalloc(bufferSize);
|
||||
RINGBUF_Init(&queue->rb, queue->buf, bufferSize);
|
||||
}
|
||||
int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len)
|
||||
{
|
||||
return PROTO_AddRb(&queue->rb, buffer, len);
|
||||
}
|
||||
int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen)
|
||||
{
|
||||
|
||||
return PROTO_ParseRb(&queue->rb, buffer, len, maxLen);
|
||||
}
|
||||
|
||||
BOOL ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue)
|
||||
{
|
||||
if(queue->rb.fill_cnt<=0)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/* str_queue.h --
|
||||
*
|
||||
* Copyright (c) 2014-2015, Tuan PM <tuanpm at live dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef USER_QUEUE_H_
|
||||
#define USER_QUEUE_H_
|
||||
#include "os_type.h"
|
||||
#include "ringbuf.h"
|
||||
typedef struct {
|
||||
uint8_t *buf;
|
||||
RINGBUF rb;
|
||||
} QUEUE;
|
||||
|
||||
void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize);
|
||||
int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len);
|
||||
int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen);
|
||||
BOOL ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue);
|
||||
#endif /* USER_QUEUE_H_ */
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* \file
|
||||
* Ring Buffer library
|
||||
*/
|
||||
|
||||
#include "ringbuf.h"
|
||||
|
||||
|
||||
/**
|
||||
* \brief init a RINGBUF object
|
||||
* \param r pointer to a RINGBUF object
|
||||
* \param buf pointer to a byte array
|
||||
* \param size size of buf
|
||||
* \return 0 if successfull, otherwise failed
|
||||
*/
|
||||
I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size)
|
||||
{
|
||||
if(r == NULL || buf == NULL || size < 2) return -1;
|
||||
|
||||
r->p_o = r->p_r = r->p_w = buf;
|
||||
r->fill_cnt = 0;
|
||||
r->size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* \brief put a character into ring buffer
|
||||
* \param r pointer to a ringbuf object
|
||||
* \param c character to be put
|
||||
* \return 0 if successfull, otherwise failed
|
||||
*/
|
||||
I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c)
|
||||
{
|
||||
if(r->fill_cnt>=r->size)return -1; // ring buffer is full, this should be atomic operation
|
||||
|
||||
|
||||
r->fill_cnt++; // increase filled slots count, this should be atomic operation
|
||||
|
||||
|
||||
*r->p_w++ = c; // put character into buffer
|
||||
|
||||
if(r->p_w >= r->p_o + r->size) // rollback if write pointer go pass
|
||||
r->p_w = r->p_o; // the physical boundary
|
||||
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* \brief get a character from ring buffer
|
||||
* \param r pointer to a ringbuf object
|
||||
* \param c read character
|
||||
* \return 0 if successfull, otherwise failed
|
||||
*/
|
||||
I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c)
|
||||
{
|
||||
if(r->fill_cnt<=0)return -1; // ring buffer is empty, this should be atomic operation
|
||||
|
||||
|
||||
r->fill_cnt--; // decrease filled slots count
|
||||
|
||||
|
||||
*c = *r->p_r++; // get the character out
|
||||
|
||||
if(r->p_r >= r->p_o + r->size) // rollback if write pointer go pass
|
||||
r->p_r = r->p_o; // the physical boundary
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef _RING_BUF_H_
|
||||
#define _RING_BUF_H_
|
||||
|
||||
#include <os_type.h>
|
||||
#include <stdlib.h>
|
||||
#include "typedef.h"
|
||||
|
||||
typedef struct{
|
||||
U8* p_o; /**< Original pointer */
|
||||
U8* volatile p_r; /**< Read pointer */
|
||||
U8* volatile p_w; /**< Write pointer */
|
||||
volatile I32 fill_cnt; /**< Number of filled slots */
|
||||
I32 size; /**< Buffer size */
|
||||
}RINGBUF;
|
||||
|
||||
I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size);
|
||||
I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c);
|
||||
I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c);
|
||||
#endif
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* \file
|
||||
* Standard Types definition
|
||||
*/
|
||||
|
||||
#ifndef _TYPE_DEF_H_
|
||||
#define _TYPE_DEF_H_
|
||||
|
||||
typedef char I8;
|
||||
typedef unsigned char U8;
|
||||
typedef short I16;
|
||||
typedef unsigned short U16;
|
||||
typedef long I32;
|
||||
typedef unsigned long U32;
|
||||
typedef unsigned long long U64;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Tuan PM
|
||||
* Email: tuanpm@live.com
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str)
|
||||
{
|
||||
uint8_t segs = 0; /* Segment count. */
|
||||
uint8_t chcnt = 0; /* Character count within segment. */
|
||||
uint8_t accum = 0; /* Accumulator for segment. */
|
||||
/* Catch NULL pointer. */
|
||||
if (str == 0)
|
||||
return 0;
|
||||
/* Process every character in string. */
|
||||
|
||||
while (*str != '\0') {
|
||||
/* Segment changeover. */
|
||||
|
||||
if (*str == '.') {
|
||||
/* Must have some digits in segment. */
|
||||
if (chcnt == 0)
|
||||
return 0;
|
||||
/* Limit number of segments. */
|
||||
if (++segs == 4)
|
||||
return 0;
|
||||
/* Reset segment values and restart loop. */
|
||||
chcnt = accum = 0;
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check numeric. */
|
||||
if ((*str < '0') || (*str > '9'))
|
||||
return 0;
|
||||
|
||||
/* Accumulate and check segment. */
|
||||
|
||||
if ((accum = accum * 10 + *str - '0') > 255)
|
||||
return 0;
|
||||
/* Advance other segment specific stuff and continue loop. */
|
||||
|
||||
chcnt++;
|
||||
str++;
|
||||
}
|
||||
|
||||
/* Check enough segments and enough characters in last segment. */
|
||||
|
||||
if (segs != 3)
|
||||
return 0;
|
||||
if (chcnt == 0)
|
||||
return 0;
|
||||
/* Address okay. */
|
||||
|
||||
return 1;
|
||||
}
|
||||
uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip)
|
||||
{
|
||||
|
||||
/* The count of the number of bytes processed. */
|
||||
int i;
|
||||
/* A pointer to the next digit to process. */
|
||||
const char * start;
|
||||
|
||||
start = str;
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* The digit being processed. */
|
||||
char c;
|
||||
/* The value of this byte. */
|
||||
int n = 0;
|
||||
while (1) {
|
||||
c = * start;
|
||||
start++;
|
||||
if (c >= '0' && c <= '9') {
|
||||
n *= 10;
|
||||
n += c - '0';
|
||||
}
|
||||
/* We insist on stopping at "." if we are still parsing
|
||||
the first, second, or third numbers. If we have reached
|
||||
the end of the numbers, we will allow any character. */
|
||||
else if ((i < 3 && c == '.') || i == 3) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (n >= 256) {
|
||||
return 0;
|
||||
}
|
||||
((uint8_t*)ip)[i] = n;
|
||||
}
|
||||
return 1;
|
||||
|
||||
}
|
||||
uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s)
|
||||
{
|
||||
uint32_t value = 0, digit;
|
||||
int8_t c;
|
||||
|
||||
while((c = *s++)){
|
||||
if('0' <= c && c <= '9')
|
||||
digit = c - '0';
|
||||
else if('A' <= c && c <= 'F')
|
||||
digit = c - 'A' + 10;
|
||||
else if('a' <= c && c<= 'f')
|
||||
digit = c - 'a' + 10;
|
||||
else break;
|
||||
|
||||
value = (value << 4) | digit;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef _UTILS_H_
|
||||
#define _UTILS_H_
|
||||
|
||||
#include "c_types.h"
|
||||
|
||||
uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s);
|
||||
uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip);
|
||||
uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str);
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:esp01_1m]
|
||||
platform = espressif8266
|
||||
board = esp01_1m
|
||||
framework = esp8266-nonos-sdk
|
||||
build_flags = -I src/
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef _USER_CONFIG_H_
|
||||
#define _USER_CONFIG_H_
|
||||
|
||||
#define INFO_MSG true
|
||||
|
||||
#define WIFI_CLIENTSSID "<your_wifi_client_SSID>"
|
||||
#define WIFI_CLIENTPASSWORD "<your_wifi_client_password>"
|
||||
|
||||
#define WIFI_CHECK_DELAY 3000 /* milliseconds */ //TODO:change to check every 5 minutes?
|
||||
#define FADE_DELAY 2000
|
||||
|
||||
/*MQTT stuff*/
|
||||
#define MQTT_HOST "<MQTT_broker_host IP>" //Host IP address, Raspberry Pi or "mqtt.yourdomain.com"
|
||||
#define MQTT_PORT 1883
|
||||
#define MQTT_BUF_SIZE 1024
|
||||
#define MQTT_KEEPALIVE 120 /*second*/
|
||||
|
||||
#define MQTT_CLIENT_ID "ESP_NeoPixel" //Name for your client, no duplicates
|
||||
#define MQTT_USER "<MQTT_broker_user>"
|
||||
#define MQTT_PASS "<MQTT_broker_password>"
|
||||
#define MQTT_RECONNECT_TIMEOUT 5 /*second*/
|
||||
#define DEFAULT_SECURITY 0
|
||||
#define QUEUE_BUFFER_SIZE 2048
|
||||
#define PROTOCOL_NAMEv31 /*MQTT version 3.1 compatible with Mosquitto v0.15*/
|
||||
//PROTOCOL_NAMEv311 /*MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/*/
|
||||
#define CLIENT_SSL_ENABLE
|
||||
|
||||
//Adafruit_NeoPixel Stuff
|
||||
#define PIN 2
|
||||
#define NUMPIXELS 16
|
||||
#define BRIGHTNESS 50
|
||||
// RGB NeoPixel permutations; white and red offsets are always same
|
||||
// Offset: W R G B
|
||||
#define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2))
|
||||
#define _BV(b) (1UL << (b))
|
||||
#endif
|
|
@ -0,0 +1,699 @@
|
|||
#include <user_interface.h>
|
||||
#include <osapi.h>
|
||||
#include <c_types.h>
|
||||
#include <mem.h>
|
||||
#include <os_type.h>
|
||||
#include "driver/uart.h"
|
||||
#include "user_config.h"
|
||||
#include "mqtt.h"
|
||||
#include <string.h>
|
||||
#include <eagle_soc.h>
|
||||
|
||||
//typedef void (*WifiCallback)(uint8_t);
|
||||
|
||||
extern int ets_uart_printf(const char *fmt, ...);
|
||||
int (*console_printf)(const char *fmt, ...) = ets_uart_printf;
|
||||
|
||||
// Debug output.
|
||||
#ifdef INFO_MSG
|
||||
#undef INFO_MSG
|
||||
#define INFO_MSG(...) console_printf(__VA_ARGS__);
|
||||
#else
|
||||
#define INFO_MSG(...)
|
||||
#endif
|
||||
|
||||
typedef uint8_t neoPixelType;
|
||||
|
||||
static uint32_t _getCycleCount(void) __attribute__((always_inline));
|
||||
static inline uint32_t _getCycleCount(void) {
|
||||
uint32_t ccount;
|
||||
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
|
||||
return ccount;
|
||||
}
|
||||
|
||||
struct Adafruit_NeoPixel{
|
||||
bool
|
||||
begun; // true if begin() previously called
|
||||
uint16_t
|
||||
numLEDs, // Number of RGB LEDs in strip
|
||||
numBytes; // Size of 'pixels' buffer below (3 or 4 bytes/pixel)
|
||||
int8_t
|
||||
pin; // Output pin number (-1 if not yet set)
|
||||
uint8_t
|
||||
brightness,
|
||||
*pixels, // Holds LED color values (3 or 4 bytes each)
|
||||
rOffset, // Index of red byte within each 3- or 4-byte pixel
|
||||
gOffset, // Index of green byte
|
||||
bOffset, // Index of blue byte
|
||||
wOffset; // Index of white byte (same as rOffset if no white)
|
||||
uint32_t
|
||||
endTime;
|
||||
};
|
||||
|
||||
bool neopixel_canShow(struct Adafruit_NeoPixel *an){
|
||||
return (system_get_time() - an->endTime) >= 300L;
|
||||
}
|
||||
|
||||
LOCAL void espShow(uint8_t pin, uint8_t *pixels, uint32_t numBytes) {
|
||||
|
||||
#define CYCLES_800_T0H (APB_CLK_FREQ / 2500000) // 0.4us //F_CPU changed for the equivalent in eagle_soc.h : CPU_CLK_FREQ
|
||||
#define CYCLES_800_T1H (APB_CLK_FREQ / 1250000) // 0.8us
|
||||
#define CYCLES_800 (APB_CLK_FREQ / 800000) // 1.25us per bit
|
||||
#define CYCLES_400_T0H (APB_CLK_FREQ / 2000000) // 0.5uS
|
||||
#define CYCLES_400_T1H (APB_CLK_FREQ / 833333) // 1.2us
|
||||
#define CYCLES_400 (APB_CLK_FREQ / 400000) // 2.5us per bit
|
||||
|
||||
uint8_t *p, *end, pix, mask;
|
||||
uint32_t t, time0, time1, period, c, startTime, pinMask;
|
||||
|
||||
pinMask = _BV(pin); //_BV is in arduino library had to include the define here
|
||||
p = pixels;
|
||||
end = p + numBytes;
|
||||
pix = *p++;
|
||||
mask = 0x80;
|
||||
startTime = 0;
|
||||
|
||||
time0 = CYCLES_800_T0H;
|
||||
time1 = CYCLES_800_T1H;
|
||||
period = CYCLES_800;
|
||||
|
||||
for(t = time0;; t = time0) {
|
||||
if(pix & mask) t = time1; // Bit high duration
|
||||
while(((c = _getCycleCount()) - startTime) < period); // Wait for bit start
|
||||
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinMask); // Set high
|
||||
startTime = c; // Save start time
|
||||
while(((c = _getCycleCount()) - startTime) < t); // Wait high duration
|
||||
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinMask); // Set low
|
||||
if(!(mask >>= 1)) { // Next bit/byte
|
||||
if(p >= end) break;
|
||||
pix = *p++;
|
||||
mask = 0x80;
|
||||
}
|
||||
}
|
||||
while((_getCycleCount() - startTime) < period); // Wait for last bit
|
||||
}
|
||||
|
||||
void begin(struct Adafruit_NeoPixel *an) {
|
||||
if(an->pin >= 0) { //these functions depend on Arduino, declared using equivalents from <eagle_soc.h>
|
||||
// pinMode(pin, OUTPUT);
|
||||
// digitalWrite(pin, LOW);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U,FUNC_GPIO2); //hardcoded GPIO 2
|
||||
GPIO_OUTPUT_SET(GPIO_ID_PIN(an->pin),0); // assuming GPIO2 = pin2 and the only argument passed is pin 2, otherwise this won't work
|
||||
}
|
||||
an->begun = true;
|
||||
}
|
||||
|
||||
void setPin(struct Adafruit_NeoPixel *an, uint8_t p){
|
||||
if(an->begun && (an->pin >= 0)) PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U,FUNC_GPIO2); //pinMode(pin, INPUT); //again hardcoded, this function may be useless
|
||||
an->pin = p;
|
||||
if(an->begun) {
|
||||
//pinMode(p, OUTPUT);
|
||||
//digitalWrite(p, LOW);
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U,FUNC_GPIO2); //hardcoded GPIO 2
|
||||
GPIO_OUTPUT_SET(GPIO_ID_PIN(an->pin),0);
|
||||
}
|
||||
}
|
||||
|
||||
void updateLength(struct Adafruit_NeoPixel *an, uint16_t n){
|
||||
if(an->pixels){ os_free(an->pixels);} // Free existing data (if any)
|
||||
// Allocate new data -- note: ALL PIXELS ARE CLEARED
|
||||
an->numBytes = n * ((an->wOffset == an->rOffset) ? 3 : 4);
|
||||
if((an->pixels = (uint8_t *)os_malloc(an->numBytes))) {
|
||||
os_memset(an->pixels, 0, an->numBytes);
|
||||
an->numLEDs = n;
|
||||
} else {
|
||||
an->numLEDs = an->numBytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void updateType(struct Adafruit_NeoPixel *an, neoPixelType t){
|
||||
bool oldThreeBytesPerPixel = (an->wOffset == an->rOffset); // false if RGBW
|
||||
an->wOffset = (t >> 6) & 0b11; // See notes in header file
|
||||
an->rOffset = (t >> 4) & 0b11; // regarding R/G/B/W offsets
|
||||
an->gOffset = (t >> 2) & 0b11;
|
||||
an->bOffset = t & 0b11;
|
||||
|
||||
// If bytes-per-pixel has changed (and pixel data was previously
|
||||
// allocated), re-allocate to new size. Will clear any data.
|
||||
if(an->pixels) {
|
||||
bool newThreeBytesPerPixel = (an->wOffset == an->rOffset);
|
||||
if(newThreeBytesPerPixel != oldThreeBytesPerPixel) updateLength(an ,an->numLEDs);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t color_rgb(uint8_t r, uint8_t g, uint8_t b){
|
||||
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
||||
}
|
||||
|
||||
//Set pixel color from 'packed' 32-bit RGB color:
|
||||
void setPixelColor(struct Adafruit_NeoPixel *an, uint16_t n, uint32_t c){
|
||||
if(n < an->numLEDs) {
|
||||
uint8_t *p,
|
||||
r = (uint8_t)(c >> 16),
|
||||
g = (uint8_t)(c >> 8),
|
||||
b = (uint8_t)c;
|
||||
if(an->brightness) { // See notes in setBrightness()
|
||||
r = (r * an->brightness) >> 8;
|
||||
g = (g * an->brightness) >> 8;
|
||||
b = (b * an->brightness) >> 8;
|
||||
}
|
||||
if(an->wOffset == an->rOffset) {
|
||||
p = &(an->pixels[n * 3]);
|
||||
} else {
|
||||
p = &(an->pixels[n * 4]);
|
||||
uint8_t w = (uint8_t)(c >> 24);
|
||||
p[an->wOffset] = an->brightness ? ((w * an->brightness) >> 8) : w;
|
||||
}
|
||||
p[an->rOffset] = r;
|
||||
p[an->gOffset] = g;
|
||||
p[an->bOffset] = b;
|
||||
}
|
||||
}
|
||||
|
||||
void show(struct Adafruit_NeoPixel *an) {
|
||||
if(!an->pixels) return;
|
||||
while(!neopixel_canShow(an));
|
||||
espShow(an->pin, an->pixels, an->numBytes);
|
||||
an->endTime = system_get_time();
|
||||
}
|
||||
|
||||
void constructor (struct Adafruit_NeoPixel *an, uint16_t n, uint8_t p, neoPixelType t){
|
||||
an->begun = false;
|
||||
an->brightness = 50;
|
||||
an->pixels = NULL;
|
||||
an->endTime = 0;
|
||||
|
||||
updateType(an, t);
|
||||
updateLength(an, n);
|
||||
setPin(an, p);
|
||||
}
|
||||
|
||||
struct Adafruit_NeoPixel ledstrip;
|
||||
|
||||
MQTT_Client mqttClient;
|
||||
LOCAL bool ICACHE_FLASH_ATTR setup_wifi_st_mode();
|
||||
static void ICACHE_FLASH_ATTR wifi_check_ip(void *arg);
|
||||
static struct ip_info ipConfig;
|
||||
static ETSTimer WiFiLinker;
|
||||
//static ETSTimer fadeTimer;
|
||||
//static void ICACHE_FLASH_ATTR fadeFunction(void *arg);
|
||||
static tConnState connState = WIFI_CONNECTING;
|
||||
bool firstTime = true;
|
||||
uint8_t received_red;
|
||||
uint8_t received_green;
|
||||
uint8_t received_blue;
|
||||
bool fade = false;
|
||||
bool solid = false;
|
||||
|
||||
/*void setStripColor(uint8_t red, uint8_t green, uint8_t blue){
|
||||
uint8_t i = 0;
|
||||
for(i=0;i<NUMPIXELS;i++){
|
||||
setPixelColor(&ledstrip, i, color_rgb(red,green,blue));
|
||||
system_soft_wdt_feed();
|
||||
}
|
||||
show(&ledstrip);
|
||||
}*/
|
||||
|
||||
typedef enum{
|
||||
initialToRed,
|
||||
redPlusGreen,
|
||||
greenMinusRed,
|
||||
greenPlusBlue,
|
||||
blueMinusGreen,
|
||||
bluePlusRed,
|
||||
redMinusBlue,
|
||||
redToInitial,
|
||||
}FADE_STATE;
|
||||
|
||||
/*const char *FlashSizeMap[] =
|
||||
{
|
||||
"512 KB (256 KB + 256 KB)", // 0x00
|
||||
"256 KB", // 0x01
|
||||
"1024 KB (512 KB + 512 KB)", // 0x02
|
||||
"2048 KB (512 KB + 512 KB)" // 0x03
|
||||
"4096 KB (512 KB + 512 KB)" // 0x04
|
||||
"2048 KB (1024 KB + 1024 KB)" // 0x05
|
||||
"4096 KB (1024 KB + 1024 KB)" // 0x06
|
||||
};*/
|
||||
|
||||
/*const char *WiFiMode[] =
|
||||
{
|
||||
"NULL", // 0x00
|
||||
"STATION", // 0x01
|
||||
"SOFTAP", // 0x02
|
||||
"STATIONAP" // 0x03
|
||||
};*/
|
||||
|
||||
/*const char *WiFiStatus[] =
|
||||
{
|
||||
"STATION_IDLE", // 0x00
|
||||
"STATION_CONNECTING", // 0x01
|
||||
"STATION_WRONG_PASSWORD", // 0x02
|
||||
"STATION_NO_AP_FOUND", // 0x03
|
||||
"STATION_CONNECT_FAIL", // 0x04
|
||||
"STATION_GOT_IP" // 0x05
|
||||
};*/
|
||||
|
||||
const char *statusMapping[] =
|
||||
{
|
||||
"OK",
|
||||
"FAIL",
|
||||
"PENDING",
|
||||
"BUSY",
|
||||
"CANCEL"
|
||||
};
|
||||
|
||||
const char *authMapping[]=
|
||||
{
|
||||
"AUTH_OPEN",
|
||||
"AUTH_WEP",
|
||||
"AUTH_WPA_PSK",
|
||||
"AUTH_WPA2_PSK",
|
||||
"AUTH_WPA_WPA2_PSK",
|
||||
"AUTH_MAX"
|
||||
};
|
||||
|
||||
/*const char *MQTT_state[]=
|
||||
{
|
||||
"WIFI_INIT",
|
||||
"WIFI_CONNECTING",
|
||||
"WIFI_CONNECTING_ERROR",
|
||||
"WIFI_CONNECTED",
|
||||
"DNS_RESOLVE",
|
||||
"TCP_DISCONNECTED",
|
||||
"TCP_RECONNECT_REQ",
|
||||
"TCP_RECONNECT",
|
||||
"TCP_CONNECTING",
|
||||
"TCP_CONNECTING_ERROR",
|
||||
"TCP_CONNECTED",
|
||||
"MQTT_CONNECT_SEND",
|
||||
"MQTT_CONNECT_SENDING",
|
||||
"MQTT_SUBSCIBE_SEND",
|
||||
"MQTT_SUBSCIBE_SENDING",
|
||||
"MQTT_DATA",
|
||||
"MQTT_PUBLISH_RECV",
|
||||
"MQTT_PUBLISHING"
|
||||
};*/
|
||||
|
||||
void mqttConnectedCb(uint32_t *args)
|
||||
{
|
||||
MQTT_Client* client = (MQTT_Client*)args;
|
||||
INFO_MSG("MQTT: Connected\r\n");
|
||||
MQTT_Subscribe(client, "/mqtt/lights/red", 2);
|
||||
MQTT_Subscribe(client, "/mqtt/lights/green", 2);
|
||||
MQTT_Subscribe(client, "/mqtt/lights/blue", 2);
|
||||
MQTT_Subscribe(client, "/mqtt/lights/color", 2);
|
||||
MQTT_Subscribe(client, "/mqtt/lights/fade", 2);
|
||||
MQTT_Subscribe(client, "/mqtt/lights/solid", 2);
|
||||
//system_soft_wdt_feed();
|
||||
//INFO_MSG("MQTT: Publishing initial values\r\n");
|
||||
//MQTT_Publish(client,"/mqtt/lights/red",0,1,2,0);
|
||||
//MQTT_Publish(client,"/mqtt/lights/green",0,1,2,0);
|
||||
//MQTT_Publish(client,"/mqtt/lights/blue",0,1,2,0);
|
||||
//MQTT_Publish(client,"/mqtt/lights/fade",0,1,2,0);
|
||||
//MQTT_Publish(client,"/mqtt/lights/solid",0,1,2,0);
|
||||
//system_soft_wdt_feed();
|
||||
}
|
||||
|
||||
void mqttDisconnectedCb(uint32_t *args)
|
||||
{
|
||||
MQTT_Client* client = (MQTT_Client*)args;
|
||||
INFO_MSG("MQTT: Disconnected\r\n");
|
||||
}
|
||||
|
||||
void mqttPublishedCb(uint32_t *args)
|
||||
{
|
||||
MQTT_Client* client = (MQTT_Client*)args;
|
||||
INFO_MSG("MQTT: Published\r\n");
|
||||
}
|
||||
|
||||
void mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len)
|
||||
{
|
||||
char *topicBuf = (char*)os_zalloc(topic_len+1),
|
||||
*dataBuf = (char*)os_zalloc(data_len+1);
|
||||
|
||||
MQTT_Client* client = (MQTT_Client*)args;
|
||||
|
||||
os_memcpy(topicBuf, topic, topic_len);
|
||||
topicBuf[topic_len] = 0;
|
||||
|
||||
os_memcpy(dataBuf, data, data_len);
|
||||
dataBuf[data_len] = 0;
|
||||
|
||||
INFO_MSG("Receive topic: %s, data: %s length: %d \r\n", topicBuf, dataBuf, data_len);
|
||||
int isRed = strcmp(topicBuf, "/mqtt/lights/red");
|
||||
int isGreen = strcmp(topicBuf, "/mqtt/lights/green");
|
||||
int isBlue = strcmp(topicBuf, "/mqtt/lights/blue");
|
||||
int isFade = strcmp(topicBuf, "/mqtt/lights/fade");
|
||||
int isSolid = strcmp(topicBuf, "/mqtt/lights/solid");
|
||||
INFO_MSG("topic comparison result: R %d, G %d, B %d, fade %d, solid%d\r\n",isRed,isGreen,isBlue,isFade,isSolid);
|
||||
if (isRed == 0){
|
||||
received_red = dataBuf[0];
|
||||
}else if (isGreen == 0){
|
||||
received_green = dataBuf[0];
|
||||
}else if (isBlue == 0){
|
||||
received_blue = dataBuf[0];
|
||||
}
|
||||
else if (isFade == 0){
|
||||
fade = dataBuf[0];
|
||||
solid = false;
|
||||
}
|
||||
else if (isSolid == 0)
|
||||
{
|
||||
solid = dataBuf[0];
|
||||
fade = false;
|
||||
}
|
||||
|
||||
os_free(topicBuf);
|
||||
os_free(dataBuf);
|
||||
|
||||
if (solid){
|
||||
//os_timer_disarm(&fadeTimer);
|
||||
|
||||
INFO_MSG("Updated color is R:%d G:%d B:%d \r\n", received_red, received_green, received_blue);
|
||||
//setStripColor(received_red, received_green, received_blue);
|
||||
//MQTT_Publish(client,"/mqtt/lights/fade",0,1,2,0);
|
||||
int i;
|
||||
for(i=0;i<NUMPIXELS;i++){
|
||||
//Color takes RGB values, from 0,0,0 up to 255,255,255
|
||||
setPixelColor(&ledstrip, i, color_rgb(received_red,received_green,received_blue));
|
||||
system_soft_wdt_feed();
|
||||
}
|
||||
show(&ledstrip);
|
||||
}else if (fade){
|
||||
//MQTT_Publish(client,"/mqtt/lights/solid",0,1,2,0);
|
||||
int i;
|
||||
for(i=0;i<NUMPIXELS;i++){
|
||||
//Color takes RGB values, from 0,0,0 up to 255,255,255
|
||||
setPixelColor(&ledstrip, i, color_rgb(255,255,255));
|
||||
system_soft_wdt_feed();
|
||||
}
|
||||
show(&ledstrip);
|
||||
//os_timer_setfn(&fadeTimer, (os_timer_func_t *)fadeFunction, NULL);
|
||||
//os_timer_arm(&fadeTimer, FADE_DELAY, 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR wifi_check_ip(void *arg)
|
||||
{
|
||||
os_timer_disarm(&WiFiLinker);
|
||||
switch(wifi_station_get_connect_status())
|
||||
{
|
||||
case STATION_GOT_IP:
|
||||
wifi_get_ip_info(STATION_IF, &ipConfig);
|
||||
if(ipConfig.ip.addr != 0) {
|
||||
connState = WIFI_CONNECTED;
|
||||
INFO_MSG("WiFi connected, wait MQTT message...\r\n");
|
||||
if (firstTime){
|
||||
MQTT_Connect(&mqttClient);
|
||||
firstTime = false;
|
||||
}
|
||||
} else {
|
||||
connState = WIFI_CONNECTING_ERROR;
|
||||
INFO_MSG("WiFi connected, ip.addr is null\r\n");
|
||||
}
|
||||
break;
|
||||
case STATION_WRONG_PASSWORD:
|
||||
connState = WIFI_CONNECTING_ERROR;
|
||||
INFO_MSG("WiFi connecting error, wrong password\r\n");
|
||||
break;
|
||||
case STATION_NO_AP_FOUND:
|
||||
connState = WIFI_CONNECTING_ERROR;
|
||||
INFO_MSG("WiFi connecting error, ap not found\r\n");
|
||||
break;
|
||||
case STATION_CONNECT_FAIL:
|
||||
connState = WIFI_CONNECTING_ERROR;
|
||||
INFO_MSG("WiFi connecting fail\r\n");
|
||||
break;
|
||||
default:
|
||||
connState = WIFI_CONNECTING;
|
||||
INFO_MSG("WiFi connecting...\r\n");
|
||||
}
|
||||
os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL);
|
||||
os_timer_arm(&WiFiLinker, WIFI_CHECK_DELAY, 0);
|
||||
}
|
||||
|
||||
/*static void ICACHE_FLASH_ATTR fadeFunction(void *arg){
|
||||
os_timer_disarm(&fadeTimer);
|
||||
static FADE_STATE fade_state = initialToRed;
|
||||
uint8_t i = 0;
|
||||
static uint8_t red_fade = 0;
|
||||
static uint8_t green_fade = 0;
|
||||
static uint8_t blue_fade = 0;
|
||||
if(fade_state == initialToRed){
|
||||
red_fade+=5;
|
||||
if (red_fade == 255){
|
||||
fade_state = redPlusGreen;
|
||||
}
|
||||
setStripColor(red_fade, green_fade, blue_fade);
|
||||
os_timer_setfn(&fadeTimer, (os_timer_func_t *)fadeFunction, NULL);
|
||||
os_timer_arm(&fadeTimer, FADE_DELAY, 0);
|
||||
return;
|
||||
}
|
||||
else if (fade_state == redPlusGreen){
|
||||
green_fade+=5;
|
||||
if (green_fade == 255){
|
||||
fade_state = greenMinusRed;
|
||||
}
|
||||
setStripColor(red_fade, green_fade, blue_fade);
|
||||
os_timer_setfn(&fadeTimer, (os_timer_func_t *)fadeFunction, NULL);
|
||||
os_timer_arm(&fadeTimer, FADE_DELAY, 0);
|
||||
return;
|
||||
}
|
||||
else if (fade_state == greenMinusRed){
|
||||
red_fade-=5;
|
||||
if (red_fade == 0){
|
||||
fade_state = greenPlusBlue;
|
||||
}
|
||||
setStripColor(red_fade, green_fade, blue_fade);
|
||||
os_timer_setfn(&fadeTimer, (os_timer_func_t *)fadeFunction, NULL);
|
||||
os_timer_arm(&fadeTimer, FADE_DELAY, 0);
|
||||
return;
|
||||
}
|
||||
else if (fade_state == greenPlusBlue){
|
||||
blue_fade+=5;
|
||||
if (blue_fade == 255){
|
||||
fade_state = blueMinusGreen;
|
||||
}
|
||||
setStripColor(red_fade, green_fade, blue_fade);
|
||||
os_timer_setfn(&fadeTimer, (os_timer_func_t *)fadeFunction, NULL);
|
||||
os_timer_arm(&fadeTimer, FADE_DELAY, 0);
|
||||
return;
|
||||
}
|
||||
else if (fade_state == blueMinusGreen){
|
||||
green_fade-=5;
|
||||
if (green_fade == 0){
|
||||
fade_state = bluePlusRed;
|
||||
}
|
||||
setStripColor(red_fade, green_fade, blue_fade);
|
||||
os_timer_setfn(&fadeTimer, (os_timer_func_t *)fadeFunction, NULL);
|
||||
os_timer_arm(&fadeTimer, FADE_DELAY, 0);
|
||||
return;
|
||||
}
|
||||
else if (fade_state == bluePlusRed){
|
||||
red_fade+=5;
|
||||
if (red_fade == 0){
|
||||
fade_state = redMinusBlue;
|
||||
}
|
||||
setStripColor(red_fade, green_fade, blue_fade);
|
||||
os_timer_setfn(&fadeTimer, (os_timer_func_t *)fadeFunction, NULL);
|
||||
os_timer_arm(&fadeTimer, FADE_DELAY, 0);
|
||||
return;
|
||||
}
|
||||
else if (fade_state == redMinusBlue){
|
||||
blue_fade-=5;
|
||||
if (blue_fade == 0){
|
||||
fade_state = redToInitial;
|
||||
}
|
||||
setStripColor(red_fade, green_fade, blue_fade);
|
||||
os_timer_setfn(&fadeTimer, (os_timer_func_t *)fadeFunction, NULL);
|
||||
os_timer_arm(&fadeTimer, FADE_DELAY, 0);
|
||||
return;
|
||||
}
|
||||
else if (fade_state == redToInitial){
|
||||
red_fade-=5;
|
||||
if (red_fade == 0){
|
||||
fade_state = initialToRed;
|
||||
}
|
||||
setStripColor(red_fade, green_fade, blue_fade);
|
||||
os_timer_setfn(&fadeTimer, (os_timer_func_t *)fadeFunction, NULL);
|
||||
os_timer_arm(&fadeTimer, FADE_DELAY, 0);
|
||||
return;
|
||||
}
|
||||
//TODO: test
|
||||
}*/
|
||||
|
||||
LOCAL void ICACHE_FLASH_ATTR wifi_show_scan_info(void *arg, STATUS status){
|
||||
INFO_MSG("\n==== Avaliable Networks: ====\n");
|
||||
if (status == OK){
|
||||
struct bss_info *bssInfo;
|
||||
bssInfo = (struct bss_info *)arg;
|
||||
//skip first in chain as it is invalid
|
||||
bssInfo = STAILQ_NEXT(bssInfo, next);
|
||||
while (bssInfo != NULL){
|
||||
INFO_MSG("SSID: %s\r\n", bssInfo->ssid);
|
||||
INFO_MSG("SECURITY: %s\r\n", authMapping[bssInfo->authmode]);
|
||||
INFO_MSG("RSSI: %d dB\r\n\n", bssInfo->rssi);
|
||||
bssInfo = STAILQ_NEXT(bssInfo, next);
|
||||
}
|
||||
|
||||
INFO_MSG("Scan done, setting WiFi check timer...\r\n");
|
||||
// Wait for Wi-Fi connection
|
||||
os_timer_disarm(&WiFiLinker);
|
||||
os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL);
|
||||
os_timer_arm(&WiFiLinker, WIFI_CHECK_DELAY, 0);
|
||||
}else{
|
||||
INFO_MSG("There is a problem scanning nearby networks \r\n");
|
||||
INFO_MSG("Status is: %s \r\n", statusMapping[status]);
|
||||
}
|
||||
}
|
||||
|
||||
LOCAL void ICACHE_FLASH_ATTR to_scan(void) { wifi_station_scan(NULL,wifi_show_scan_info); }
|
||||
|
||||
LOCAL bool ICACHE_FLASH_ATTR setup_wifi_st_mode()
|
||||
{
|
||||
struct station_config stconfig;
|
||||
wifi_station_disconnect();
|
||||
wifi_station_dhcpc_stop();
|
||||
if(wifi_station_get_config(&stconfig))
|
||||
{
|
||||
os_memset(stconfig.ssid, 0, sizeof(stconfig.ssid));
|
||||
os_memset(stconfig.password, 0, sizeof(stconfig.password));
|
||||
os_sprintf(stconfig.ssid, "%s", WIFI_CLIENTSSID);
|
||||
os_sprintf(stconfig.password, "%s", WIFI_CLIENTPASSWORD);
|
||||
if(!wifi_station_set_config(&stconfig))
|
||||
{
|
||||
INFO_MSG("ESP8266 not set station config!\r\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
wifi_station_connect();
|
||||
wifi_station_dhcpc_start();
|
||||
INFO_MSG("ESP8266 in STA mode configured.\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : user_rf_cal_sector_set
|
||||
* Description : SDK just reversed 4 sectors, used for rf init data and paramters.
|
||||
* We add this function to force users to set rf cal sector, since
|
||||
* we don't know which sector is free in user's application.
|
||||
* sector map for last several sectors : ABBBCDDD
|
||||
* A : rf cal
|
||||
* B : at parameters
|
||||
* C : rf init data
|
||||
* D : sdk parameters
|
||||
* Parameters : none
|
||||
* Returns : rf cal sector
|
||||
*******************************************************************************/
|
||||
uint32 ICACHE_FLASH_ATTR user_rf_cal_sector_set(void)
|
||||
{
|
||||
enum flash_size_map size_map = system_get_flash_size_map();
|
||||
uint32 rf_cal_sec = 0;
|
||||
|
||||
switch (size_map) {
|
||||
case FLASH_SIZE_4M_MAP_256_256:
|
||||
rf_cal_sec = 128 - 8;
|
||||
break;
|
||||
|
||||
case FLASH_SIZE_8M_MAP_512_512:
|
||||
rf_cal_sec = 256 - 5;
|
||||
break;
|
||||
|
||||
case FLASH_SIZE_16M_MAP_512_512:
|
||||
case FLASH_SIZE_16M_MAP_1024_1024:
|
||||
rf_cal_sec = 512 - 5;
|
||||
break;
|
||||
|
||||
case FLASH_SIZE_32M_MAP_512_512:
|
||||
case FLASH_SIZE_32M_MAP_1024_1024:
|
||||
rf_cal_sec = 1024 - 5;
|
||||
break;
|
||||
|
||||
default:
|
||||
rf_cal_sec = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return rf_cal_sec;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR user_rf_pre_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR user_init(void)
|
||||
{
|
||||
// Configure the UART
|
||||
uart_init(BIT_RATE_115200, BIT_RATE_115200);
|
||||
// Enable system messages
|
||||
system_set_os_print(1);
|
||||
|
||||
//To print available networks
|
||||
wifi_set_opmode(STATION_MODE);
|
||||
system_init_done_cb(to_scan);
|
||||
|
||||
INFO_MSG("\n==== System info: ====\n");
|
||||
INFO_MSG("SDK version:%s rom %d\n", system_get_sdk_version(), system_upgrade_userbin_check());
|
||||
INFO_MSG("Time = %ld\n", system_get_time());
|
||||
INFO_MSG("Chip id = 0x%x\n", system_get_chip_id());
|
||||
INFO_MSG("CPU freq = %d MHz\n", system_get_cpu_freq());
|
||||
//INFO_MSG("Flash size map = %s\n", system_get_flash_size_map()); //doesn't work for some reason
|
||||
INFO_MSG("Free heap size = %d\n", system_get_free_heap_size());
|
||||
INFO_MSG("==== End System info ====\n");
|
||||
INFO_MSG("==== MQTT client setup ====\n");
|
||||
MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, DEFAULT_SECURITY);
|
||||
INFO_MSG("MQTT settings:\r\n Host: %s\r\n Port: %d\r\n Security: %d\r\n", MQTT_HOST, MQTT_PORT, DEFAULT_SECURITY);
|
||||
MQTT_InitClient(&mqttClient, MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE, 1);
|
||||
INFO_MSG("MQTT client settings:\r\n Device ID: %s\r\n MQTT_User: %s\r\n MQTT_Password: %s\r\n MQTT_Keepalive: %d\r\n Uses clean session\r\n", MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE);
|
||||
MQTT_InitLWT(&mqttClient, "lwt", "offline", 0, 0); //last will topic
|
||||
MQTT_OnConnected(&mqttClient, mqttConnectedCb);
|
||||
MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb);
|
||||
MQTT_OnPublished(&mqttClient, mqttPublishedCb);
|
||||
MQTT_OnData(&mqttClient, mqttDataCb);
|
||||
INFO_MSG("==== End MQTT client setup ====\n");
|
||||
os_delay_us(10000);
|
||||
INFO_MSG("System init...\r\n");
|
||||
if(setup_wifi_st_mode()){
|
||||
if(wifi_get_phy_mode() != PHY_MODE_11N)
|
||||
wifi_set_phy_mode(PHY_MODE_11N);
|
||||
if(wifi_station_get_auto_connect() == 0)
|
||||
wifi_station_set_auto_connect(1);
|
||||
wifi_station_set_reconnect_policy(TRUE);
|
||||
}
|
||||
// Init NeoPixel ledstrip
|
||||
INFO_MSG("Initializing NeoPixel led Strip ...\r\n");
|
||||
constructor(&ledstrip, NUMPIXELS, PIN, NEO_GRB);
|
||||
INFO_MSG("Updated NeoPixel led Strip struct...\r\n");
|
||||
os_delay_us(50000);
|
||||
begin(&ledstrip); //pin initialization
|
||||
int i;
|
||||
for(i=0;i<NUMPIXELS;i++){
|
||||
//Color takes RGB values, from 0,0,0 up to 255,255,255
|
||||
setPixelColor(&ledstrip, i, color_rgb(255,255,255)); // Moderately bright green color.
|
||||
uint8_t delayCounter;
|
||||
os_delay_us(40000); // Delay
|
||||
os_delay_us(40000); // Delay
|
||||
os_delay_us(40000); // Delay
|
||||
os_delay_us(40000); // Delay
|
||||
os_delay_us(40000); // Delay
|
||||
system_soft_wdt_feed();
|
||||
show(&ledstrip);
|
||||
}
|
||||
for(i=0;i<NUMPIXELS;i++){
|
||||
//Color takes RGB values, from 0,0,0 up to 255,255,255
|
||||
setPixelColor(&ledstrip, i, color_rgb(0,0,0)); // Moderately bright green color.
|
||||
uint8_t delayCounter;
|
||||
os_delay_us(40000);// Delay
|
||||
os_delay_us(40000); // Delay
|
||||
os_delay_us(40000); // Delay
|
||||
os_delay_us(40000); // Delay
|
||||
os_delay_us(40000); // Delay
|
||||
system_soft_wdt_feed();
|
||||
show(&ledstrip);
|
||||
}
|
||||
//show(&ledstrip); // This sends the updated pixel color to the hardware.
|
||||
|
||||
INFO_MSG("System init done.\n");
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
This directory is intended for PIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
Loading…
Reference in New Issue