kopia lustrzana https://github.com/martin-ger/esp_mqtt
added scripting to the master branch
rodzic
7c15a93399
commit
1fc6ae24c6
4
Makefile
4
Makefile
|
@ -33,7 +33,7 @@ ESPPORT ?= /dev/ttyUSB0
|
|||
TARGET = app
|
||||
|
||||
# which modules (subdirectories) of the project to include in compiling
|
||||
MODULES = driver user mqtt ntp
|
||||
MODULES = driver user mqtt ntp easygpio
|
||||
#EXTRA_INCDIR = $(BUILD_AREA)/esp-open-sdk/esp-open-lwip/include include
|
||||
EXTRA_INCDIR = include
|
||||
|
||||
|
@ -43,7 +43,7 @@ LIB_MODULES = mqtt
|
|||
LIBS = c gcc hal pp phy net80211 lwip wpa main
|
||||
|
||||
# compiler flags using during compilation of source files
|
||||
CFLAGS = -Os -g -O2 -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH -DUSE_OPTIMIZE_PRINTF
|
||||
CFLAGS = -Os -g -O2 -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH -DUSE_OPTIMIZE_PRINTF -Desp8266
|
||||
#-DMQTT_DEBUG_ON
|
||||
|
||||
# linker flags used to generate the main object file
|
||||
|
|
255
README.md
255
README.md
|
@ -1,48 +1,11 @@
|
|||
# esp_uMQTT_broker
|
||||
A basic MQTT Broker on the ESP8266
|
||||
A basic MQTT Broker/Client with scripting support on the ESP8266
|
||||
|
||||
Thanks to Tuan PM for sharing his MQTT client library https://github.com/tuanpmt/esp_mqtt as a basis with us. The modified code still contains the complete client functionality from the original esp_mqtt lib, but it has been extended by the basic broker service.
|
||||
|
||||
The broker does support:
|
||||
- MQTT protocoll versions v3.1 and v3.1.1 simultaniously
|
||||
- a smaller number of clients (at least 8 have been tested, memory is the issue)
|
||||
- retained messages
|
||||
- LWT
|
||||
- QoS level 0
|
||||
- a subset of MQTT (CONNECT, DISCONNECT, SUBSCRIBE, UNSUBSCRIBE, PUBLISH, PING)
|
||||
|
||||
The broker does not yet support:
|
||||
- username, password authentication
|
||||
- QoS levels other than 0
|
||||
- many TCP(=MQTT) clients
|
||||
- non-clear sessions
|
||||
- TLS
|
||||
|
||||
The complete functionality is included in the mqtt directory. The broker is started by simply including:
|
||||
|
||||
```c
|
||||
#include "mqtt_server.h"
|
||||
|
||||
bool MQTT_server_start(uint16_t portno, uint16_t max_subscriptions, uint16_t max_retained_topics);
|
||||
```
|
||||
|
||||
in the user_init() function.
|
||||
|
||||
# Building and Flashing
|
||||
The code can be used in any project that is compiled using the NONOS_SDK or the esp-open-sdk. Also the sample code in the user directory can be build using the standard SDKs after adapting the variables in the Makefile.
|
||||
|
||||
If you don't need the full demo program, you can find a minimal demo in the directory "user_basic". Rename it to "user", adapt "user_config.h", and do the "make" to build a small demo that just starts an MQTT broker.
|
||||
|
||||
Build the esp_uMQTT_broker firmware with "make". "make flash" flashes it onto an esp8266.
|
||||
|
||||
If you want to use the precompiled binaries of the bigger demo (see below) from the firmware directory you can flash them on an ESP12 with
|
||||
|
||||
```bash
|
||||
esptool.py --port /dev/ttyUSB0 write_flash -fs 32m 0x00000 firmware/0x00000.bin 0x10000 firmware/0x10000.bin
|
||||
```
|
||||
This program enables the ESP8266 to become the central node in a small distributed IoT system. It implements an MQTT Broker and a simple scripted rule engine with event/action statements that links together the MQTT sensors and actors. It can act as STA, as AP, or as both and it can connect to another MQTT broker (i.e. in the cloud). Here it can act as bridge and forward and rewrite topics in both directions.
|
||||
|
||||
# Usage
|
||||
In the user directory there is a demo program that serves as a stand-alone MQTT broker and bridge. The program starts with the following default configuration:
|
||||
In the user directory there is the main program that serves as a stand-alone MQTT broker, client and bridge. The program starts with the following default configuration:
|
||||
|
||||
- ssid: ssid, password: password
|
||||
- ap_ssid: MyAP, ap_password: none, ap_on: 1, ap_open: 1
|
||||
- network: 192.168.4.0/24
|
||||
|
@ -50,6 +13,7 @@ In the user directory there is a demo program that serves as a stand-alone MQTT
|
|||
This means it connects to the internet via AP ssid,password and offers an open AP with ap_ssid MyAP. This default can be changed in the file user_config.h. The default can be overwritten and persistenly saved to flash by using a console interface. This console is available either via the serial port at 115200 baud or via tcp port 7777 (e.g. "telnet 192.168.4.1 7777" from a connected STA).
|
||||
|
||||
Use the following commands for an initial setup:
|
||||
|
||||
- set ssid your_home_router's_SSID
|
||||
- set password your_home_router's_password
|
||||
- set ap_ssid ESP's_ssid
|
||||
|
@ -63,19 +27,19 @@ After reboot it will connect to your home router and itself is ready for station
|
|||
The console understands the following commands:
|
||||
|
||||
Basic commands (enough to get it working in nearly all environments):
|
||||
|
||||
- help: prints a short help message
|
||||
- set [ssid|password] _value_: changes the settings for the uplink AP (WiFi config of your home-router)
|
||||
- set ap_on [0|1]: selects, whether the soft-AP is disabled (ap_on=0) or enabled (ap_on=1, default)
|
||||
- set [ap_ssid|ap_password] _value_: changes the settings for the soft-AP of the ESP (for your stations)
|
||||
- show [config|stats]: prints the current config or some status information and statistics
|
||||
- show mqtt_broker: shows the current status of the uMQTT broker
|
||||
- save [dhcp]: saves the current config parameters [+ the current DHCP leases] to flash
|
||||
- show [config|stats|script|mqtt]: prints the current config or some status information and statistics
|
||||
- save: saves the current config parameters to flash
|
||||
- reset [factory]: resets the esp, optionally resets WiFi params to default values
|
||||
- lock: locks the current config, changes are not allowed
|
||||
- unlock _password_: unlocks the config, requires password of the network AP
|
||||
- lock [_password_]: saves and locks the current config, changes are not allowed. Password can be left open if already set before
|
||||
- unlock _password_: unlocks the config, requires password from the lock command
|
||||
- quit: terminates a remote session
|
||||
|
||||
Advanced commands:
|
||||
(Most of the set-commands are effective only after save and reset)
|
||||
Advanced commands (most of the set-commands are effective only after save and reset):
|
||||
- set network _ip-addr_: sets the IP address of the internal network, network is always /24, router is always x.x.x.1
|
||||
- set dns _dns-addr_: sets a static DNS address
|
||||
- set dns dhcp: configures use of the dynamic DNS address from DHCP, default
|
||||
|
@ -84,17 +48,184 @@ Advanced commands:
|
|||
- set netmask _netmask_: sets a static netmask for the uplink network
|
||||
- set gw _gw-addr_: sets a static gateway address in the uplink network
|
||||
- scan: does a scan for APs
|
||||
- set ap_on [0|1]: selects, whether the soft-AP is disabled (ap_on=0) or enabled (ap_on=1, default)
|
||||
- set ap_open [0|1]: selects, whether the soft-AP uses WPA2 security (ap_open=0, automatic, if an ap_password is set) or open (ap_open=1)
|
||||
- set speed [80|160]: sets the CPU clock frequency (default 80 Mhz)
|
||||
- set ntp_server _IP_or_hostname_: sets the name or IP of an NTP server ("none" disables NTP, default)
|
||||
- set ntp_interval _interval_: sets the NTP sync interval in seconds (default 60)
|
||||
- set ntp_timezone _tz_: sets the timezone in hours offset
|
||||
- set config_port _portno_: sets the port number of the console login (default is 7777, 0 disables remote console config)
|
||||
- script [_portno_|delete]: opens port for upload of scripts or deletes the current script
|
||||
|
||||
While the user interface looks similar to my esp_wifi_repeater at https://github.com/martin-ger/esp_wifi_repeater this does NO NAT routing. AP and STA network are stricly separated and there is no routing in between. The only possible connection via both networks is the uMQTT broker that listens on both interfaces.
|
||||
|
||||
# MQTT client/bridging functionality
|
||||
The broker comes with a "local" and a "remote" client, which means, the broker itself can publish and subscribe topics. The "local" client is a client to the own broker (without the need of an additional TCP connection). This feature is meant to provide the basis for a local rule engine that can react on MQTT events, e.g. to switch GPIOs or send other messages (MQTT, HTTP,...). You can use this at source level with the functions:
|
||||
The broker comes with a "local" and a "remote" client, which means, the broker itself can publish and subscribe topics. The "local" client is a client to the own broker (without the need of an additional TCP connection).
|
||||
|
||||
By default the "remote" MQTT client is disabled. It can be enabled by setting the config parameter "mqtt_host" to a hostname different from "none". To configure the "remote" MQTT client you can set the following parameters:
|
||||
|
||||
- set mqtt_host _IP_or_hostname_: IP or hostname of the MQTT broker ("none" disables the MQTT client)
|
||||
- set mqtt_user _username_: Username for authentication ("none" if no authentication is required at the broker)
|
||||
- set mqtt_user _password_: Password for authentication
|
||||
- set mqtt_id _clientId_: Id of the client at the broker (default: "ESPRouter_xxxxxx" derived from the MAC address)
|
||||
|
||||
# Scripting
|
||||
The esp_uMQTT_broker comes with a build-in scripting engine. A script enables the ESP not just to act as a passive broker but to react on events (publications and timing events) and to send out its own items.
|
||||
|
||||
Here is a demo of a small script to give you an idea of the power of the scripting feature:
|
||||
```
|
||||
% Config params, overwrite any previous settings from the commandline
|
||||
config ap_ssid MyAP
|
||||
config ap_password stupidPassword
|
||||
config ntp_server 1.pool.ntp.org
|
||||
config mqtt_host 192.168.1.20
|
||||
|
||||
% Now the initialization, this is done once after booting
|
||||
on init
|
||||
do
|
||||
println "MQTT Script 1.0 starting"
|
||||
subscribe local /test/#
|
||||
settimer 1 1000 % once per second
|
||||
setvar $1 0
|
||||
setvar $2 0
|
||||
|
||||
% Now the events, checked whenever something happens
|
||||
|
||||
% Here a remote republish, of any local topic starting with "/test/"
|
||||
on topic local /test/#
|
||||
do
|
||||
publish remote $this_topic $this_data
|
||||
|
||||
% When timer 1 expires, do some stuff
|
||||
on timer 1
|
||||
do
|
||||
% publish a timestamp locally
|
||||
publish local /t/time $timestamp
|
||||
|
||||
% Let the LED on GPIO 2 blink
|
||||
gpio_out 2 $1
|
||||
setvar $1 not $1
|
||||
|
||||
% Count occurences in var $2
|
||||
setvar $2 $2 add 1
|
||||
|
||||
% And if we have reached 100, print that to the console
|
||||
if $2 gte 100 then
|
||||
print "We have reached "
|
||||
println $2
|
||||
setvar $2 0
|
||||
endif
|
||||
|
||||
% Reload the timer
|
||||
settimer 1 1000
|
||||
|
||||
% Here a local publication once each day at noon
|
||||
on clock 12:00:00
|
||||
do
|
||||
publish local /t/2 "High Noon"
|
||||
```
|
||||
|
||||
In general, scripts have the following BNF:
|
||||
|
||||
```
|
||||
<statement> ::= on <event> do <action> |
|
||||
config <param> <value> |
|
||||
<statement> <statement>
|
||||
|
||||
<event> ::= init | timer <num> | clock <timestamp> | topic (local|remote) <topic-id>
|
||||
|
||||
<action> ::= publish (local|remote) <topic-id> <val> [retained] |
|
||||
subscribe (local|remote) <topic-id> |
|
||||
unsubscribe (local|remote) <topic-id> |
|
||||
settimer <num> <num> |
|
||||
setvar $<num> <expr> |
|
||||
gpio_out <num> <expr> |
|
||||
if <expr> then <action> endif |
|
||||
print <expr> | println <expr>
|
||||
<action> <action>
|
||||
|
||||
<expr> ::= <val> <op> <expr> | not <expr>
|
||||
|
||||
<op> := eq | gt | gte | str_ge | str_gte | add | sub | mult | div
|
||||
|
||||
<val> := <string> | <const> | #<hex-string> | $<num> | $this_item | $this_data | $timestamp
|
||||
|
||||
<string> := "[any ASCII]*" | [any ASCII]*
|
||||
|
||||
<num> := [0-9]*
|
||||
|
||||
<timestamp> := hh:mm:ss
|
||||
```
|
||||
|
||||
Scripts with size up to 4KB are uploaded to the esp_uMQTT_broker using a network interface. Start the upload with "script <portno>" on the concole of the ESP, e.g.:
|
||||
```
|
||||
CMD>script 2000
|
||||
Waiting for script upload on port 2000
|
||||
CMD>
|
||||
|
||||
```
|
||||
Now the ESP listens on the given port for an incoming connection and stores anything it receives as new script. Upload a file using netcat, e.g.:
|
||||
```bash
|
||||
$ netcat 192.168.178.29 2000 < user/demo_script2
|
||||
```
|
||||
The ESP will store the file and immediatly checks the syntax of the script:
|
||||
```
|
||||
CMD>script 2000
|
||||
Waiting for script upload on port 2000
|
||||
Script upload completed (451 Bytes)
|
||||
Syntax okay
|
||||
CMD>
|
||||
```
|
||||
|
||||
You can examine the currently loaded script using the "show script" command. It only displays about 1KB of a script. If you need to see more, use "show script <line_no>" with a higher starting line. Newly loaded scripts are stored persistently in flash and will be executed after next reset if they contain no syntax errors. "script delete" stops script execution and deleted a script from flash.
|
||||
|
||||
# NTP Support
|
||||
NTP time is supported and timestamps are only available if the sync with an NTP server is done. By default the NTP client is enabled and set to "1.pool.ntp.org". It can be changed by setting the config parameter "ntp_server" to a hostname or an IP address. An ntp_server of "none" will disable the NTP client. Also you can set the "ntp_timezone" to an offset from GMT in hours. The system time will be synced with the NTP server every "ntp_interval" seconds (default ). Here it uses NOT the full NTP calculation and clock drift compensation. Instead it will just set the local time to the latest received time.
|
||||
|
||||
After NTP sync has been completed successfully once, the local time will be published every second under the topic "$SYS/broker/time" in the format "hh:mm:ss". You can also query the NTP time using the "time" command from the commandline.
|
||||
|
||||
- set ntp_server _IP_or_hostname_: sets the name or IP of an NTP server (default "1.pool.ntp.org", "none" disables NTP)
|
||||
- set ntp_interval _interval_: sets the NTP sync interval in seconds (default 300)
|
||||
- set ntp_timezone _tz_: sets the timezone in hours offset (default 0)
|
||||
- time: prints the current time as hh:mm:ss
|
||||
|
||||
# Building and Flashing
|
||||
The code can be used in any project that is compiled using the NONOS_SDK or the esp-open-sdk. Also the sample code in the user directory can be build using the standard SDKs after adapting the variables in the Makefile.
|
||||
|
||||
Build the esp_uMQTT_broker firmware with "make". "make flash" flashes it onto an esp8266.
|
||||
|
||||
If you want to use the precompiled binaries from the firmware directory you can flash them directly on an ESP8266, e.g. with
|
||||
|
||||
```bash
|
||||
$ esptool.py --port /dev/ttyUSB0 write_flash -fs 32m 0x00000 firmware/0x00000.bin 0x10000 firmware/0x10000.bin
|
||||
|
||||
```
|
||||
# The MQTT broker library
|
||||
Thanks to Tuan PM for sharing his MQTT client library https://github.com/tuanpmt/esp_mqtt as a basis with us. The modified code still contains the complete client functionality from the original esp_mqtt lib, but it has been extended by the basic broker service.
|
||||
|
||||
The broker does support:
|
||||
- MQTT protocoll versions v3.1 and v3.1.1 simultaniously
|
||||
- a smaller number of clients (at least 8 have been tested, memory is the issue)
|
||||
- retained messages
|
||||
- LWT
|
||||
- QoS level 0
|
||||
- a subset of MQTT (CONNECT, DISCONNECT, SUBSCRIBE, UNSUBSCRIBE, PUBLISH, PING)
|
||||
|
||||
The broker does not yet support:
|
||||
- username, password authentication
|
||||
- QoS levels other than 0
|
||||
- many TCP(=MQTT) clients
|
||||
- non-clear sessions
|
||||
- TLS
|
||||
|
||||
# Using the Source Code
|
||||
The complete functionality is included in the mqtt directory and can be integrated into any NONOS SDK program. The broker is started by simply including:
|
||||
|
||||
```c
|
||||
#include "mqtt_server.h"
|
||||
|
||||
bool MQTT_server_start(uint16_t portno, uint16_t max_subscriptions, uint16_t max_retained_topics);
|
||||
|
||||
```
|
||||
in the user_init() function. Now it is ready for MQTT connections on all activated interfaces (STA and/or AP). You can find a minimal demo in the directory "user_basic". Rename it to "user", adapt "user_config.h", and do the "make" to build a small demo that just starts an MQTT broker.
|
||||
|
||||
Your code can locally interact with the broker using the functions:
|
||||
|
||||
```c
|
||||
bool MQTT_local_publish(uint8_t* topic, uint8_t* data, uint16_t data_length, uint8_t qos, uint8_t retain);
|
||||
|
@ -103,21 +234,5 @@ bool MQTT_local_unsubscribe(uint8_t* topic);
|
|||
void MQTT_local_onData(MqttDataCallback dataCb);
|
||||
```
|
||||
|
||||
By default the "remote" MQTT client is disabled. It can be enabled by setting the config parameter "mqtt_host" to a hostname different from "none". To configure the "remote" MQTT client you can set the following parameters:
|
||||
- set mqtt_host _IP_or_hostname_: IP or hostname of the MQTT broker ("none" disables the MQTT client)
|
||||
- set mqtt_user _username_: Username for authentication ("none" if no authentication is required at the broker)
|
||||
- set mqtt_user _password_: Password for authentication
|
||||
- set mqtt_id _clientId_: Id of the client at the broker (default: "ESPRouter_xxxxxx" derived from the MAC address)
|
||||
With these functions you can publish and subscribe topics as a local client like you would with a remote MQTT broker.
|
||||
|
||||
You can test this with the commands:
|
||||
|
||||
- publish [local|remote] [topic] [data]: this publishes a topic
|
||||
- subscribe [local|remote] [topic]: subscribes to a topic, received topic will be printed to serial output
|
||||
- unsubscribe [local|remote] [topic]: unsubscribes from a topic
|
||||
|
||||
Currently the clients republish everything they receive (and they have subscribed) to the other client, thus it can act as something like an MQTT bridge. Up to now, the subscriptions are not persistently saved to config, so they have to be entered manually after each reboot - will work on this...
|
||||
|
||||
# NTP Support
|
||||
Remote NTP time servers are supported. By default the NTP client is disabled - and the is no immediate need for synced time. Nowever, it can be enabled by setting the config parameter "ntp_server" to a hostname or IP different from "none" ("1.pool.ntp.org" is a good choice). Also you can set the "ntp_timezone" to an offset from GMT. The system time will be synced with the NTP server every "ntp_interval" seconds. Here it uses NOT the full NTP calculation and clock drift compensation. Instead it will just set the local time to the latest received time.
|
||||
|
||||
After NTP sync has been completed successful once, the local time will be published every second under the topic "$SYS/broker/time" in the format "hh:mm:ss". You can also query the NTP time with the "time" command from the commandline.
|
||||
|
|
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* easygpio.c
|
||||
*
|
||||
* Copyright (c) 2015, eadf (https://github.com/eadf)
|
||||
* 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 "easygpio.h"
|
||||
#include "gpio.h"
|
||||
#include "osapi.h"
|
||||
#include "ets_sys.h"
|
||||
|
||||
#define EASYGPIO_USE_GPIO_INPUT_GET
|
||||
|
||||
static void ICACHE_FLASH_ATTR
|
||||
gpio16_output_conf(void) {
|
||||
WRITE_PERI_REG(PAD_XPD_DCDC_CONF,
|
||||
(READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xffffffbcUL) | 0x1UL); // mux configuration for XPD_DCDC to output rtc_gpio0
|
||||
|
||||
WRITE_PERI_REG(RTC_GPIO_CONF,
|
||||
(READ_PERI_REG(RTC_GPIO_CONF) & 0xfffffffeUL) | 0x0UL); //mux configuration for out enable
|
||||
|
||||
WRITE_PERI_REG(RTC_GPIO_ENABLE,
|
||||
(READ_PERI_REG(RTC_GPIO_ENABLE) & 0xfffffffeUL) | 0x1UL); //out enable
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR
|
||||
gpio16_input_conf(void) {
|
||||
WRITE_PERI_REG(PAD_XPD_DCDC_CONF,
|
||||
(READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xffffffbcUL) | 0x1UL); // mux configuration for XPD_DCDC and rtc_gpio0 connection
|
||||
|
||||
WRITE_PERI_REG(RTC_GPIO_CONF,
|
||||
(READ_PERI_REG(RTC_GPIO_CONF) & 0xfffffffeUL) | 0x0UL); //mux configuration for out enable
|
||||
|
||||
WRITE_PERI_REG(RTC_GPIO_ENABLE,
|
||||
READ_PERI_REG(RTC_GPIO_ENABLE) & 0xfffffffeUL); //out disable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of active pins in the gpioMask.
|
||||
*/
|
||||
uint8_t ICACHE_FLASH_ATTR
|
||||
easygpio_countBits(uint32_t gpioMask) {
|
||||
|
||||
uint8_t i=0;
|
||||
uint8_t numberOfPins=0;
|
||||
for (i=0; i<32; i++){
|
||||
numberOfPins += (gpioMask & BIT(i))?1:0;
|
||||
}
|
||||
return numberOfPins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the gpio name and func for a specific pin.
|
||||
*/
|
||||
bool ICACHE_FLASH_ATTR
|
||||
easygpio_getGPIONameFunc(uint8_t gpio_pin, uint32_t *gpio_name, uint8_t *gpio_func) {
|
||||
|
||||
if (gpio_pin == 6 || gpio_pin == 7 || gpio_pin == 8 || gpio_pin == 11 || gpio_pin >= 17) {
|
||||
os_printf("easygpio_getGPIONameFunc Error: There is no GPIO%d, check your code\n", gpio_pin);
|
||||
return false;
|
||||
}
|
||||
if (gpio_pin == 16) {
|
||||
os_printf("easygpio_getGPIONameFunc Error: GPIO16 does not have gpio_name and gpio_func\n");
|
||||
return false;
|
||||
}
|
||||
switch ( gpio_pin ) {
|
||||
case 0:
|
||||
*gpio_func = FUNC_GPIO0;
|
||||
*gpio_name = PERIPHS_IO_MUX_GPIO0_U;
|
||||
return true;
|
||||
case 1:
|
||||
*gpio_func = FUNC_GPIO1;
|
||||
*gpio_name = PERIPHS_IO_MUX_U0TXD_U;
|
||||
return true;
|
||||
case 2:
|
||||
*gpio_func = FUNC_GPIO2;
|
||||
*gpio_name = PERIPHS_IO_MUX_GPIO2_U;
|
||||
return true;
|
||||
case 3:
|
||||
*gpio_func = FUNC_GPIO3;
|
||||
*gpio_name = PERIPHS_IO_MUX_U0RXD_U;
|
||||
return true;
|
||||
case 4:
|
||||
*gpio_func = FUNC_GPIO4;
|
||||
*gpio_name = PERIPHS_IO_MUX_GPIO4_U;
|
||||
return true;
|
||||
case 5:
|
||||
*gpio_func = FUNC_GPIO5;
|
||||
*gpio_name = PERIPHS_IO_MUX_GPIO5_U;
|
||||
return true;
|
||||
case 9:
|
||||
*gpio_func = FUNC_GPIO9;
|
||||
*gpio_name = PERIPHS_IO_MUX_SD_DATA2_U;
|
||||
return true;
|
||||
case 10:
|
||||
*gpio_func = FUNC_GPIO10;
|
||||
*gpio_name = PERIPHS_IO_MUX_SD_DATA3_U;
|
||||
return true;
|
||||
case 12:
|
||||
*gpio_func = FUNC_GPIO12;
|
||||
*gpio_name = PERIPHS_IO_MUX_MTDI_U;
|
||||
return true;
|
||||
case 13:
|
||||
*gpio_func = FUNC_GPIO13;
|
||||
*gpio_name = PERIPHS_IO_MUX_MTCK_U;
|
||||
return true;
|
||||
case 14:
|
||||
*gpio_func = FUNC_GPIO14;
|
||||
*gpio_name = PERIPHS_IO_MUX_MTMS_U;
|
||||
return true;
|
||||
case 15:
|
||||
*gpio_func = FUNC_GPIO15;
|
||||
*gpio_name = PERIPHS_IO_MUX_MTDO_U;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pull up registers for a pin.
|
||||
*/
|
||||
static void ICACHE_FLASH_ATTR
|
||||
easygpio_setupPullsByName(uint32_t gpio_name, EasyGPIO_PullStatus pullStatus) {
|
||||
|
||||
if (EASYGPIO_PULLUP == pullStatus) {
|
||||
PIN_PULLUP_EN(gpio_name);
|
||||
} else {
|
||||
PIN_PULLUP_DIS(gpio_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pull registers for a pin.
|
||||
*/
|
||||
bool ICACHE_FLASH_ATTR
|
||||
easygpio_pullMode(uint8_t gpio_pin, EasyGPIO_PullStatus pullStatus) {
|
||||
uint32_t gpio_name;
|
||||
uint8_t gpio_func;
|
||||
|
||||
if (!easygpio_getGPIONameFunc(gpio_pin, &gpio_name, &gpio_func) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
easygpio_setupPullsByName(gpio_name, pullStatus);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the 'gpio_pin' pin as a GPIO and sets the pull register for that pin.
|
||||
* 'pullStatus' has no effect on output pins or GPIO16
|
||||
*/
|
||||
bool ICACHE_FLASH_ATTR
|
||||
easygpio_pinMode(uint8_t gpio_pin, EasyGPIO_PullStatus pullStatus, EasyGPIO_PinMode pinMode) {
|
||||
uint32_t gpio_name;
|
||||
uint8_t gpio_func;
|
||||
|
||||
if (16==gpio_pin) {
|
||||
// ignoring pull status on GPIO16 for now
|
||||
if (EASYGPIO_OUTPUT == pinMode) {
|
||||
gpio16_output_conf();
|
||||
} else {
|
||||
gpio16_input_conf();
|
||||
}
|
||||
return true;
|
||||
} else if (!easygpio_getGPIONameFunc(gpio_pin, &gpio_name, &gpio_func) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PIN_FUNC_SELECT(gpio_name, gpio_func);
|
||||
easygpio_setupPullsByName(gpio_name, pullStatus);
|
||||
|
||||
if (EASYGPIO_OUTPUT != pinMode) {
|
||||
GPIO_DIS_OUTPUT(GPIO_ID_PIN(gpio_pin));
|
||||
} else {
|
||||
// must enable the pin or else the WRITE_PERI_REG won't work
|
||||
gpio_output_set(0, 0, BIT(GPIO_ID_PIN(gpio_pin)),0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the 'gpio_pin' pin as a GPIO and sets the interrupt to trigger on that pin.
|
||||
* The 'interruptArg' is the function argument that will be sent to your interruptHandler
|
||||
*/
|
||||
bool ICACHE_FLASH_ATTR
|
||||
easygpio_attachInterrupt(uint8_t gpio_pin, EasyGPIO_PullStatus pullStatus, void (*interruptHandler)(void *arg), void *interruptArg) {
|
||||
uint32_t gpio_name;
|
||||
uint8_t gpio_func;
|
||||
|
||||
if (gpio_pin == 16) {
|
||||
os_printf("easygpio_setupInterrupt Error: GPIO16 does not have interrupts\n");
|
||||
return false;
|
||||
}
|
||||
if (!easygpio_getGPIONameFunc(gpio_pin, &gpio_name, &gpio_func) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ETS_GPIO_INTR_ATTACH(interruptHandler, interruptArg);
|
||||
ETS_GPIO_INTR_DISABLE();
|
||||
|
||||
PIN_FUNC_SELECT(gpio_name, gpio_func);
|
||||
|
||||
easygpio_setupPullsByName(gpio_name, pullStatus);
|
||||
|
||||
// disable output
|
||||
GPIO_DIS_OUTPUT(gpio_pin);
|
||||
|
||||
gpio_register_set(GPIO_PIN_ADDR(gpio_pin), GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE)
|
||||
| GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE)
|
||||
| GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE));
|
||||
|
||||
//clear gpio14 status
|
||||
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(gpio_pin));
|
||||
ETS_GPIO_INTR_ENABLE();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach the interrupt handler from the 'gpio_pin' pin.
|
||||
*/
|
||||
bool ICACHE_FLASH_ATTR
|
||||
easygpio_detachInterrupt(uint8_t gpio_pin) {
|
||||
|
||||
if (gpio_pin == 16) {
|
||||
os_printf("easygpio_setupInterrupt Error: GPIO16 does not have interrupts\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't know how to detach interrupt, yet.
|
||||
// Quick and dirty fix - just disable the interrupt
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(gpio_pin), GPIO_PIN_INTR_DISABLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uniform way of setting GPIO output value. Handles GPIO 0-16.
|
||||
*
|
||||
* You can not rely on that this function will switch the gpio to an output like GPIO_OUTPUT_SET does.
|
||||
* Use easygpio_outputEnable() to change an input gpio to output mode.
|
||||
*/
|
||||
void
|
||||
easygpio_outputSet(uint8_t gpio_pin, uint8_t value) {
|
||||
if (16==gpio_pin) {
|
||||
WRITE_PERI_REG(RTC_GPIO_OUT,
|
||||
(READ_PERI_REG(RTC_GPIO_OUT) & 0xfffffffeUL) | (0x1UL & value));
|
||||
} else {
|
||||
#ifdef EASYGPIO_USE_GPIO_OUTPUT_SET
|
||||
GPIO_OUTPUT_SET(GPIO_ID_PIN(gpio_pin), value);
|
||||
#else
|
||||
if (value&1){
|
||||
WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR, READ_PERI_REG(PERIPHS_GPIO_BASEADDR) | BIT(gpio_pin));
|
||||
} else {
|
||||
WRITE_PERI_REG( PERIPHS_GPIO_BASEADDR, READ_PERI_REG(PERIPHS_GPIO_BASEADDR) & ~BIT(gpio_pin));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uniform way of getting GPIO input value. Handles GPIO 0-16.
|
||||
* The pin must be initiated with easygpio_pinMode() so that the pin mux is setup as a gpio in the first place.
|
||||
* If you know that you won't be using GPIO16 then you'd better off by just using GPIO_INPUT_GET().
|
||||
*/
|
||||
uint8_t
|
||||
easygpio_inputGet(uint8_t gpio_pin) {
|
||||
if (16==gpio_pin) {
|
||||
return (READ_PERI_REG(RTC_GPIO_IN_DATA) & 1UL);
|
||||
} else {
|
||||
#ifdef EASYGPIO_USE_GPIO_INPUT_GET
|
||||
return GPIO_INPUT_GET(GPIO_ID_PIN(gpio_pin));
|
||||
#else
|
||||
// this does *not* work, maybe GPIO_IN_ADDRESS is the wrong address
|
||||
return ((GPIO_REG_READ(GPIO_IN_ADDRESS) > gpio_pin) & 1UL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uniform way of turning an output GPIO pin into input mode. Handles GPIO 0-16.
|
||||
* The pin must be initiated with easygpio_pinMode() so that the pin mux is setup as a gpio in the first place.
|
||||
* This function does the same thing as GPIO_DIS_OUTPUT, but works on GPIO16 too.
|
||||
*/
|
||||
void easygpio_outputDisable(uint8_t gpio_pin) {
|
||||
if (16==gpio_pin) {
|
||||
WRITE_PERI_REG(RTC_GPIO_ENABLE,
|
||||
READ_PERI_REG(RTC_GPIO_ENABLE) & 0xfffffffeUL); //out disable
|
||||
} else {
|
||||
GPIO_DIS_OUTPUT(GPIO_ID_PIN(gpio_pin));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uniform way of turning an input GPIO pin into output mode. Handles GPIO 0-16.
|
||||
* The pin must be initiated with easygpio_pinMode() so that the pin mux is setup as a gpio in the first place.
|
||||
*
|
||||
* This function:
|
||||
* - should only be used to convert a input pin into an output pin.
|
||||
* - is a little bit slower than easygpio_outputSet() so you should use that
|
||||
* function to just change output value.
|
||||
* - does the same thing as GPIO_OUTPUT_SET, but works on GPIO16 too.
|
||||
*/
|
||||
void easygpio_outputEnable(uint8_t gpio_pin, uint8_t value) {
|
||||
if (16==gpio_pin) {
|
||||
// write the value before flipping to output
|
||||
// - so we don't flash previous value for a few ns.
|
||||
WRITE_PERI_REG(RTC_GPIO_OUT,
|
||||
(READ_PERI_REG(RTC_GPIO_OUT) & 0xfffffffeUL) | (0x1UL & value));
|
||||
|
||||
WRITE_PERI_REG(RTC_GPIO_ENABLE,
|
||||
(READ_PERI_REG(RTC_GPIO_ENABLE) & 0xfffffffeUL) | 0x1UL); //out enable
|
||||
|
||||
} else {
|
||||
GPIO_OUTPUT_SET(GPIO_ID_PIN(gpio_pin), value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* easygpio.h
|
||||
*
|
||||
* Copyright (c) 2015, eadf (https://github.com/eadf)
|
||||
* 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 EASYGPIO_INCLUDE_EASYGPIO_EASYGPIO_H_
|
||||
#define EASYGPIO_INCLUDE_EASYGPIO_EASYGPIO_H_
|
||||
|
||||
#include "c_types.h"
|
||||
|
||||
typedef enum {
|
||||
EASYGPIO_INPUT=0,
|
||||
EASYGPIO_OUTPUT=1
|
||||
} EasyGPIO_PinMode;
|
||||
|
||||
typedef enum {
|
||||
EASYGPIO_PULLUP=3,
|
||||
EASYGPIO_NOPULL=4
|
||||
} EasyGPIO_PullStatus;
|
||||
|
||||
/**
|
||||
* Returns the gpio name and func for a specific pin.
|
||||
*/
|
||||
bool easygpio_getGPIONameFunc(uint8_t gpio_pin, uint32_t *gpio_name, uint8_t *gpio_func);
|
||||
|
||||
/**
|
||||
* Sets the 'gpio_pin' pin as a GPIO and sets the interrupt to trigger on that pin.
|
||||
* The 'interruptArg' is the function argument that will be sent to your interruptHandler
|
||||
* (this way you can several interrupts with one interruptHandler)
|
||||
*/
|
||||
bool easygpio_attachInterrupt(uint8_t gpio_pin, EasyGPIO_PullStatus pullStatus, void (*interruptHandler)(void *arg), void *interruptArg);
|
||||
|
||||
/**
|
||||
* Deatach the interrupt handler from the 'gpio_pin' pin.
|
||||
*/
|
||||
bool easygpio_detachInterrupt(uint8_t gpio_pin);
|
||||
|
||||
/**
|
||||
* Returns the number of active pins in the gpioMask.
|
||||
*/
|
||||
uint8_t easygpio_countBits(uint32_t gpioMask);
|
||||
|
||||
/**
|
||||
* Sets the 'gpio_pin' pin as a GPIO and enables/disables the pull-up on that pin.
|
||||
* 'pullStatus' has no effect on output pins or GPIO16
|
||||
*/
|
||||
bool easygpio_pinMode(uint8_t gpio_pin, EasyGPIO_PullStatus pullStatus, EasyGPIO_PinMode pinMode);
|
||||
|
||||
/**
|
||||
* Enable or disable the internal pull up for a pin.
|
||||
*/
|
||||
bool easygpio_pullMode(uint8_t gpio_pin, EasyGPIO_PullStatus pullStatus);
|
||||
|
||||
/**
|
||||
* Uniform way of getting GPIO input value. Handles GPIO 0-16.
|
||||
* The pin must be initiated with easygpio_pinMode() so that the pin mux is setup as a gpio in the first place.
|
||||
* If you know that you won't be using GPIO16 then you'd better off by just using GPIO_INPUT_GET().
|
||||
*/
|
||||
uint8_t easygpio_inputGet(uint8_t gpio_pin);
|
||||
|
||||
/**
|
||||
* Uniform way of setting GPIO output value. Handles GPIO 0-16.
|
||||
*
|
||||
* You can not rely on that this function will switch the gpio to an output like GPIO_OUTPUT_SET does.
|
||||
* Use easygpio_outputEnable() to change an input gpio to output mode.
|
||||
*/
|
||||
void easygpio_outputSet(uint8_t gpio_pin, uint8_t value);
|
||||
|
||||
/**
|
||||
* Uniform way of turning an output GPIO pin into input mode. Handles GPIO 0-16.
|
||||
* The pin must be initiated with easygpio_pinMode() so that the pin mux is setup as a gpio in the first place.
|
||||
* This function does the same thing as GPIO_DIS_OUTPUT, but works on GPIO16 too.
|
||||
*/
|
||||
void easygpio_outputDisable(uint8_t gpio_pin);
|
||||
|
||||
/**
|
||||
* Uniform way of turning an input GPIO pin into output mode. Handles GPIO 0-16.
|
||||
* The pin must be initiated with easygpio_pinMode() so that the pin mux is setup as a gpio in the first place.
|
||||
*
|
||||
* This function:
|
||||
* - should only be used to convert a input pin into an output pin.
|
||||
* - is a little bit slower than easygpio_outputSet() so you should use that
|
||||
* function to just change output value.
|
||||
* - does the same thing as GPIO_OUTPUT_SET, but works on GPIO16 too.
|
||||
*/
|
||||
void easygpio_outputEnable(uint8_t gpio_pin, uint8_t value);
|
||||
|
||||
#endif /* EASYGPIO_INCLUDE_EASYGPIO_EASYGPIO_H_ */
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
1327
mqtt/mqtt.c
1327
mqtt/mqtt.c
Plik diff jest za duży
Load Diff
712
mqtt/mqtt_msg.c
712
mqtt/mqtt_msg.c
|
@ -37,231 +37,206 @@
|
|||
#include "mqtt_msg.h"
|
||||
#include "user_config.h"
|
||||
|
||||
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;
|
||||
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;
|
||||
os_memcpy(connection->buffer + connection->message.length, string, len);
|
||||
connection->message.length += len;
|
||||
connection->buffer[connection->message.length++] = len >> 8;
|
||||
connection->buffer[connection->message.length++] = len & 0xff;
|
||||
os_memcpy(connection->buffer + connection->message.length, string, len);
|
||||
connection->message.length += len;
|
||||
|
||||
return len + 2;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
connection->buffer[connection->message.length++] = message_id >> 8;
|
||||
connection->buffer[connection->message.length++] = message_id & 0xff;
|
||||
|
||||
return message_id;
|
||||
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 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;
|
||||
static mqtt_message_t *ICACHE_FLASH_ATTR fail_message(mqtt_connection_t * connection) {
|
||||
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;
|
||||
connection->message.length = 0;
|
||||
return &connection->message;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length)
|
||||
{
|
||||
os_memset(connection, 0, sizeof(mqtt_connection_t));
|
||||
connection->buffer = buffer;
|
||||
connection->buffer_length = buffer_length;
|
||||
}
|
||||
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;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
totlen += i;
|
||||
|
||||
return totlen;
|
||||
return &connection->message;
|
||||
}
|
||||
|
||||
|
||||
char* ICACHE_FLASH_ATTR mqtt_get_str(uint8_t* buffer, uint16_t* length)
|
||||
{
|
||||
int i = 0;
|
||||
int topiclen;
|
||||
|
||||
if (i + 2 >= *length)
|
||||
return NULL;
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
|
||||
if (i + topiclen > *length)
|
||||
return NULL;
|
||||
|
||||
*length = topiclen;
|
||||
return buffer + i;
|
||||
void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t * connection, uint8_t * buffer, uint16_t buffer_length) {
|
||||
os_memset(connection, 0, sizeof(mqtt_connection_t));
|
||||
connection->buffer = buffer;
|
||||
connection->buffer_length = buffer_length;
|
||||
}
|
||||
|
||||
const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length)
|
||||
{
|
||||
int i;
|
||||
int totlen = 0;
|
||||
int topiclen;
|
||||
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;
|
||||
for (i = 1; i < length; ++i) {
|
||||
totlen += (buffer[i] & 0x7f) << (7 * (i - 1));
|
||||
if ((buffer[i] & 0x80) == 0) {
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
totlen += i;
|
||||
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);
|
||||
return totlen;
|
||||
}
|
||||
|
||||
const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length)
|
||||
{
|
||||
int i;
|
||||
int totlen = 0;
|
||||
int topiclen;
|
||||
int blength = *length;
|
||||
*length = 0;
|
||||
char *ICACHE_FLASH_ATTR mqtt_get_str(uint8_t * buffer, uint16_t * length) {
|
||||
int i = 0;
|
||||
int topiclen;
|
||||
|
||||
for (i = 1; i < blength; ++i)
|
||||
{
|
||||
totlen += (buffer[i] & 0x7f) << (7 * (i - 1));
|
||||
if ((buffer[i] & 0x80) == 0)
|
||||
{
|
||||
++i;
|
||||
break;
|
||||
if (i + 2 >= *length)
|
||||
return NULL;
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
|
||||
if (i + topiclen > *length)
|
||||
return NULL;
|
||||
|
||||
*length = topiclen;
|
||||
return buffer + i;
|
||||
}
|
||||
|
||||
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;
|
||||
totlen += i;
|
||||
|
||||
if (i + 2 >= blength)
|
||||
return NULL;
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
if (i + 2 >= *length)
|
||||
return NULL;
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
|
||||
if (i + topiclen >= blength)
|
||||
return NULL;
|
||||
if (i + topiclen > *length)
|
||||
return NULL;
|
||||
|
||||
i += topiclen;
|
||||
*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;
|
||||
int blength = *length;
|
||||
*length = 0;
|
||||
|
||||
for (i = 1; i < blength; ++i) {
|
||||
totlen += (buffer[i] & 0x7f) << (7 * (i - 1));
|
||||
if ((buffer[i] & 0x80) == 0) {
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
totlen += i;
|
||||
|
||||
if (mqtt_get_qos(buffer) > 0)
|
||||
{
|
||||
if (i + 2 >= blength)
|
||||
return NULL;
|
||||
i += 2;
|
||||
}
|
||||
return NULL;
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
|
||||
if (totlen < i)
|
||||
return NULL;
|
||||
if (i + topiclen >= blength)
|
||||
return NULL;
|
||||
|
||||
if (totlen <= blength)
|
||||
*length = totlen - i;
|
||||
else
|
||||
*length = blength - i;
|
||||
return (const char*)(buffer + i);
|
||||
i += topiclen;
|
||||
|
||||
if (mqtt_get_qos(buffer) > 0) {
|
||||
if (i + 2 >= blength)
|
||||
return NULL;
|
||||
i += 2;
|
||||
}
|
||||
|
||||
if (totlen < i)
|
||||
return NULL;
|
||||
|
||||
if (totlen <= blength)
|
||||
*length = totlen - i;
|
||||
else
|
||||
*length = blength - 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;
|
||||
uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t * buffer, uint16_t length) {
|
||||
if (length < 1)
|
||||
return 0;
|
||||
|
||||
switch (mqtt_get_type(buffer))
|
||||
{
|
||||
switch (mqtt_get_type(buffer)) {
|
||||
case MQTT_MSG_TYPE_PUBLISH:
|
||||
{
|
||||
int i;
|
||||
int topiclen;
|
||||
{
|
||||
int i;
|
||||
int topiclen;
|
||||
|
||||
for (i = 1; i < length; ++i)
|
||||
{
|
||||
if ((buffer[i] & 0x80) == 0)
|
||||
{
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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 + 2 >= length)
|
||||
return 0;
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
|
||||
if (i + topiclen >= length)
|
||||
return 0;
|
||||
i += topiclen;
|
||||
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;
|
||||
}
|
||||
if (mqtt_get_qos(buffer) > 0) {
|
||||
if (i + 2 >= length)
|
||||
return 0;
|
||||
//i += 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (buffer[i] << 8) | buffer[i + 1];
|
||||
}
|
||||
return (buffer[i] << 8) | buffer[i + 1];
|
||||
}
|
||||
case MQTT_MSG_TYPE_PUBACK:
|
||||
case MQTT_MSG_TYPE_PUBREC:
|
||||
case MQTT_MSG_TYPE_PUBREL:
|
||||
|
@ -270,249 +245,230 @@ uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length)
|
|||
case MQTT_MSG_TYPE_UNSUBACK:
|
||||
case MQTT_MSG_TYPE_SUBSCRIBE:
|
||||
case MQTT_MSG_TYPE_UNSUBSCRIBE:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
variable_header->lengthMsb = 0;
|
||||
#if defined(PROTOCOL_NAMEv31)
|
||||
variable_header->lengthLsb = 6;
|
||||
os_memcpy(variable_header->magic, "MQIsdp", 6);
|
||||
variable_header->version = 3;
|
||||
variable_header->lengthLsb = 6;
|
||||
os_memcpy(variable_header->magic, "MQIsdp", 6);
|
||||
variable_header->version = 3;
|
||||
#elif defined(PROTOCOL_NAMEv311)
|
||||
variable_header->lengthLsb = 4;
|
||||
os_memcpy(variable_header->magic, "MQTT", 4);
|
||||
variable_header->version = 4;
|
||||
variable_header->lengthLsb = 4;
|
||||
os_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;
|
||||
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->clean_session)
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
|
||||
|
||||
if (info->client_id == NULL)
|
||||
{
|
||||
/* Never allowed */
|
||||
return fail_message(connection);
|
||||
}
|
||||
else if (info->client_id[0] == '\0')
|
||||
{
|
||||
if (info->client_id == NULL) {
|
||||
/* Never allowed */
|
||||
return fail_message(connection);
|
||||
} else if (info->client_id[0] == '\0') {
|
||||
#ifdef PROTOCOL_NAMEv311
|
||||
/* Allowed. Format 0 Length ID */
|
||||
append_string(connection, info->client_id, 2) ;
|
||||
/* Allowed. Format 0 Length ID */
|
||||
append_string(connection, info->client_id, 2);
|
||||
#else
|
||||
/* 0 Length not allowed */
|
||||
return fail_message(connection);
|
||||
/* 0 Length not allowed */
|
||||
return fail_message(connection);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No 0 data and at least 1 long. Good to go. */
|
||||
if(append_string(connection, info->client_id, os_strlen(info->client_id)) < 0)
|
||||
return fail_message(connection);
|
||||
}
|
||||
} else {
|
||||
/* No 0 data and at least 1 long. Good to go. */
|
||||
if (append_string(connection, info->client_id, os_strlen(info->client_id)) < 0)
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
if (info->will_topic != NULL && info->will_topic[0] != '\0')
|
||||
{
|
||||
if (append_string(connection, info->will_topic, os_strlen(info->will_topic)) < 0)
|
||||
return fail_message(connection);
|
||||
if (info->will_topic != NULL && info->will_topic[0] != '\0') {
|
||||
if (append_string(connection, info->will_topic, os_strlen(info->will_topic)) < 0)
|
||||
return fail_message(connection);
|
||||
|
||||
if (append_string(connection, info->will_data, os_strlen(info->will_data)) < 0)
|
||||
return fail_message(connection);
|
||||
if (append_string(connection, info->will_data, os_strlen(info->will_data)) < 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;
|
||||
}
|
||||
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, os_strlen(info->username)) < 0)
|
||||
return fail_message(connection);
|
||||
if (info->username != NULL && info->username[0] != '\0') {
|
||||
if (append_string(connection, info->username, os_strlen(info->username)) < 0)
|
||||
return fail_message(connection);
|
||||
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME;
|
||||
}
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME;
|
||||
}
|
||||
|
||||
if (info->password != NULL && info->password[0] != '\0')
|
||||
{
|
||||
if (append_string(connection, info->password, os_strlen(info->password)) < 0)
|
||||
return fail_message(connection);
|
||||
if (info->password != NULL && info->password[0] != '\0') {
|
||||
if (append_string(connection, info->password, os_strlen(info->password)) < 0)
|
||||
return fail_message(connection);
|
||||
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD;
|
||||
}
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD;
|
||||
}
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connack(mqtt_connection_t* connection, enum mqtt_connect_return_code retcode)
|
||||
{
|
||||
init_message(connection);
|
||||
connection->buffer[connection->message.length++] = 0; // Connect Acknowledge Flags
|
||||
connection->buffer[connection->message.length++] = retcode; // Connect Return code
|
||||
return fini_message(connection, MQTT_MSG_TYPE_CONNACK, 0, 0, 0);
|
||||
mqtt_message_t *ICACHE_FLASH_ATTR mqtt_msg_connack(mqtt_connection_t * connection,
|
||||
enum mqtt_connect_return_code retcode) {
|
||||
init_message(connection);
|
||||
connection->buffer[connection->message.length++] = 0; // Connect Acknowledge Flags
|
||||
connection->buffer[connection->message.length++] = retcode; // Connect Return code
|
||||
return fini_message(connection, MQTT_MSG_TYPE_CONNACK, 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);
|
||||
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 (topic == NULL || topic[0] == '\0')
|
||||
return fail_message(connection);
|
||||
|
||||
if (append_string(connection, topic, os_strlen(topic)) < 0)
|
||||
return fail_message(connection);
|
||||
if (append_string(connection, topic, os_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);
|
||||
os_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 (qos > 0)
|
||||
{
|
||||
if ((*message_id = append_message_id(connection, 0)) == 0)
|
||||
return fail_message(connection);
|
||||
}
|
||||
else
|
||||
*message_id = 0;
|
||||
return fail_message(connection);
|
||||
|
||||
if (connection->message.length + data_length > connection->buffer_length)
|
||||
return fail_message(connection);
|
||||
os_memcpy(connection->buffer + connection->message.length, data, data_length);
|
||||
connection->message.length += data_length;
|
||||
if (append_string(connection, topic, os_strlen(topic)) < 0)
|
||||
return fail_message(connection);
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain);
|
||||
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_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_suback(mqtt_connection_t * connection, uint8_t * ret_codes,
|
||||
uint8_t ret_codes_len, uint16_t message_id) {
|
||||
uint8_t i;
|
||||
|
||||
init_message(connection);
|
||||
|
||||
if ((append_message_id(connection, message_id)) == 0)
|
||||
return fail_message(connection);
|
||||
|
||||
for (i = 0; i < ret_codes_len; i++)
|
||||
connection->buffer[connection->message.length++] = ret_codes[i];
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_SUBACK, 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_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, os_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_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_unsuback(mqtt_connection_t * connection, uint16_t message_id) {
|
||||
uint8_t i;
|
||||
|
||||
init_message(connection);
|
||||
|
||||
if ((append_message_id(connection, message_id)) == 0)
|
||||
return fail_message(connection);
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_UNSUBACK, 0, 0, 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_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_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, os_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_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_suback(mqtt_connection_t* connection, uint8_t *ret_codes, uint8_t ret_codes_len, uint16_t message_id)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
init_message(connection);
|
||||
|
||||
if ((append_message_id(connection, message_id)) == 0)
|
||||
return fail_message(connection);
|
||||
|
||||
for (i = 0; i < ret_codes_len; i++)
|
||||
connection->buffer[connection->message.length++] = ret_codes[i];
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_SUBACK, 0, 0, 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, os_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_unsuback(mqtt_connection_t* connection, uint16_t message_id)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
init_message(connection);
|
||||
|
||||
if ((append_message_id(connection, message_id)) == 0)
|
||||
return fail_message(connection);
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_UNSUBACK, 0, 0, 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);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -12,111 +12,110 @@
|
|||
static retained_entry *retained_list = NULL;
|
||||
static uint16_t max_entry;
|
||||
|
||||
bool ICACHE_FLASH_ATTR create_retainedlist(uint16_t num_entires)
|
||||
{
|
||||
max_entry = num_entires;
|
||||
retained_list = (retained_entry *)os_zalloc(num_entires * sizeof(retained_entry));
|
||||
return retained_list != NULL;
|
||||
bool ICACHE_FLASH_ATTR create_retainedlist(uint16_t num_entires) {
|
||||
max_entry = num_entires;
|
||||
retained_list = (retained_entry *) os_zalloc(num_entires * sizeof(retained_entry));
|
||||
return retained_list != NULL;
|
||||
}
|
||||
|
||||
bool update_retainedtopic(uint8_t *topic, uint8_t *data, uint16_t data_len, uint8_t qos)
|
||||
{
|
||||
uint16_t i;
|
||||
bool update_retainedtopic(uint8_t * topic, uint8_t * data, uint16_t data_len, uint8_t qos) {
|
||||
uint16_t i;
|
||||
|
||||
if (retained_list == NULL) return false;
|
||||
if (retained_list == NULL)
|
||||
return false;
|
||||
|
||||
// look for topic in list
|
||||
for (i=0; i<max_entry; i++) {
|
||||
if (retained_list[i].topic != NULL && os_strcmp(retained_list[i].topic, topic)==0)
|
||||
break;
|
||||
}
|
||||
|
||||
// not yet in list
|
||||
if (i>=max_entry) {
|
||||
|
||||
// if empty new data - no entry required
|
||||
if (data_len == 0) return true;
|
||||
|
||||
// find free
|
||||
for (i=0; i<max_entry; i++) {
|
||||
if (retained_list[i].topic == NULL)
|
||||
break;
|
||||
// look for topic in list
|
||||
for (i = 0; i < max_entry; i++) {
|
||||
if (retained_list[i].topic != NULL && os_strcmp(retained_list[i].topic, topic) == 0)
|
||||
break;
|
||||
}
|
||||
if (i>=max_entry) {
|
||||
// list full
|
||||
return false;
|
||||
|
||||
// not yet in list
|
||||
if (i >= max_entry) {
|
||||
|
||||
// if empty new data - no entry required
|
||||
if (data_len == 0)
|
||||
return true;
|
||||
|
||||
// find free
|
||||
for (i = 0; i < max_entry; i++) {
|
||||
if (retained_list[i].topic == NULL)
|
||||
break;
|
||||
}
|
||||
if (i >= max_entry) {
|
||||
// list full
|
||||
return false;
|
||||
}
|
||||
retained_list[i].topic = (uint8_t *) os_malloc(os_strlen(topic) + 1);
|
||||
if (retained_list[i].topic == NULL) {
|
||||
// out of mem
|
||||
return false;
|
||||
}
|
||||
os_strcpy(retained_list[i].topic, topic);
|
||||
}
|
||||
retained_list[i].topic = (uint8_t *)os_malloc(os_strlen(topic)+1);
|
||||
if (retained_list[i].topic == NULL) {
|
||||
// out of mem
|
||||
return false;
|
||||
// if empty new data - delete
|
||||
if (data_len == 0) {
|
||||
os_free(retained_list[i].topic);
|
||||
retained_list[i].topic = NULL;
|
||||
os_free(retained_list[i].data);
|
||||
retained_list[i].data = NULL;
|
||||
retained_list[i].data_len = 0;
|
||||
return true;
|
||||
}
|
||||
os_strcpy(retained_list[i].topic, topic);
|
||||
}
|
||||
|
||||
// if empty new data - delete
|
||||
if (data_len == 0) {
|
||||
os_free(retained_list[i].topic);
|
||||
retained_list[i].topic = NULL;
|
||||
os_free(retained_list[i].data);
|
||||
retained_list[i].data = NULL;
|
||||
retained_list[i].data_len = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (retained_list[i].data == NULL) {
|
||||
// no data till now, new memory allocation
|
||||
retained_list[i].data = (uint8_t *)os_malloc(data_len);
|
||||
} else {
|
||||
if (data_len != retained_list[i].data_len) {
|
||||
// not same size as before, new memory allocation
|
||||
os_free(retained_list[i].data);
|
||||
retained_list[i].data = (uint8_t *)os_malloc(data_len);
|
||||
if (retained_list[i].data == NULL) {
|
||||
// no data till now, new memory allocation
|
||||
retained_list[i].data = (uint8_t *) os_malloc(data_len);
|
||||
} else {
|
||||
if (data_len != retained_list[i].data_len) {
|
||||
// not same size as before, new memory allocation
|
||||
os_free(retained_list[i].data);
|
||||
retained_list[i].data = (uint8_t *) os_malloc(data_len);
|
||||
}
|
||||
}
|
||||
if (retained_list[i].data == NULL) {
|
||||
// out of mem
|
||||
os_free(retained_list[i].topic);
|
||||
retained_list[i].topic = NULL;
|
||||
retained_list[i].data_len = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (retained_list[i].data == NULL) {
|
||||
// out of mem
|
||||
os_free(retained_list[i].topic);
|
||||
retained_list[i].topic = NULL;
|
||||
retained_list[i].data_len = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
os_memcpy(retained_list[i].data, data, data_len);
|
||||
retained_list[i].data_len = data_len;
|
||||
retained_list[i].qos = qos;
|
||||
os_memcpy(retained_list[i].data, data, data_len);
|
||||
retained_list[i].data_len = data_len;
|
||||
retained_list[i].qos = qos;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ICACHE_FLASH_ATTR find_retainedtopic(uint8_t *topic, find_retainedtopic_cb cb, MQTT_ClientCon *clientcon)
|
||||
{
|
||||
uint16_t i;
|
||||
bool retval = false;
|
||||
bool ICACHE_FLASH_ATTR find_retainedtopic(uint8_t * topic, find_retainedtopic_cb cb, MQTT_ClientCon * clientcon) {
|
||||
uint16_t i;
|
||||
bool retval = false;
|
||||
|
||||
if (retained_list == NULL) return false;
|
||||
if (retained_list == NULL)
|
||||
return false;
|
||||
|
||||
for (i=0; i<max_entry; i++) {
|
||||
if (retained_list[i].topic != NULL) {
|
||||
if (Topics_matches(topic, 1, retained_list[i].topic)) {
|
||||
(*cb) (&retained_list[i], clientcon);
|
||||
retval = true;
|
||||
}
|
||||
for (i = 0; i < max_entry; i++) {
|
||||
if (retained_list[i].topic != NULL) {
|
||||
if (Topics_matches(topic, 1, retained_list[i].topic)) {
|
||||
(*cb) (&retained_list[i], clientcon);
|
||||
retval = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
return retval;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR iterate_retainedtopics(iterate_retainedtopic_cb cb, void *user_data)
|
||||
{
|
||||
uint16_t i;
|
||||
void ICACHE_FLASH_ATTR iterate_retainedtopics(iterate_retainedtopic_cb cb, void *user_data) {
|
||||
uint16_t i;
|
||||
|
||||
if (retained_list == NULL) return;
|
||||
|
||||
for (i=0; i<max_entry; i++) {
|
||||
if (retained_list[i].topic != NULL) {
|
||||
if ((*cb) (&retained_list[i], user_data) == true)
|
||||
if (retained_list == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < max_entry; i++) {
|
||||
if (retained_list[i].topic != NULL) {
|
||||
if ((*cb) (&retained_list[i], user_data) == true)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1161
mqtt/mqtt_server.c
1161
mqtt/mqtt_server.c
Plik diff jest za duży
Load Diff
|
@ -12,79 +12,80 @@
|
|||
static topic_entry *topic_list = NULL;
|
||||
static uint16_t max_entry;
|
||||
|
||||
bool ICACHE_FLASH_ATTR create_topiclist(uint16_t num_entires)
|
||||
{
|
||||
max_entry = num_entires;
|
||||
topic_list = (topic_entry *)os_zalloc(num_entires * sizeof(topic_entry));
|
||||
return topic_list != NULL;
|
||||
bool ICACHE_FLASH_ATTR create_topiclist(uint16_t num_entires) {
|
||||
max_entry = num_entires;
|
||||
topic_list = (topic_entry *) os_zalloc(num_entires * sizeof(topic_entry));
|
||||
return topic_list != NULL;
|
||||
}
|
||||
|
||||
bool ICACHE_FLASH_ATTR add_topic(MQTT_ClientCon *clientcon, uint8_t *topic, uint8_t qos)
|
||||
{
|
||||
uint16_t i;
|
||||
bool ICACHE_FLASH_ATTR add_topic(MQTT_ClientCon * clientcon, uint8_t * topic, uint8_t qos) {
|
||||
uint16_t i;
|
||||
|
||||
if (topic_list == NULL) return false;
|
||||
if (!Topics_isValidName(topic)) return false;
|
||||
if (topic_list == NULL)
|
||||
return false;
|
||||
if (!Topics_isValidName(topic))
|
||||
return false;
|
||||
|
||||
for (i=0; i<max_entry; i++) {
|
||||
if (topic_list[i].clientcon == NULL) {
|
||||
topic_list[i].topic = (uint8_t*)os_malloc(os_strlen(topic)+1);
|
||||
if (topic_list[i].topic == NULL)
|
||||
return false;
|
||||
os_strcpy(topic_list[i].topic, topic);
|
||||
topic_list[i].clientcon = clientcon;
|
||||
topic_list[i].qos = qos;
|
||||
return true;
|
||||
for (i = 0; i < max_entry; i++) {
|
||||
if (topic_list[i].clientcon == NULL) {
|
||||
topic_list[i].topic = (uint8_t *) os_malloc(os_strlen(topic) + 1);
|
||||
if (topic_list[i].topic == NULL)
|
||||
return false;
|
||||
os_strcpy(topic_list[i].topic, topic);
|
||||
topic_list[i].clientcon = clientcon;
|
||||
topic_list[i].qos = qos;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ICACHE_FLASH_ATTR delete_topic(MQTT_ClientCon *clientcon, uint8_t *topic){
|
||||
uint16_t i;
|
||||
bool ICACHE_FLASH_ATTR delete_topic(MQTT_ClientCon * clientcon, uint8_t * topic) {
|
||||
uint16_t i;
|
||||
|
||||
if (topic_list == NULL) return false;
|
||||
if (topic_list == NULL)
|
||||
return false;
|
||||
|
||||
for (i=0; i<max_entry; i++) {
|
||||
if (topic_list[i].clientcon != NULL && (clientcon == NULL || topic_list[i].clientcon == clientcon)) {
|
||||
if (topic == NULL || (topic_list[i].topic != NULL && strcmp(topic, topic_list[i].topic)==0)) {
|
||||
topic_list[i].clientcon = NULL;
|
||||
os_free(topic_list[i].topic);
|
||||
topic_list[i].qos = 0;
|
||||
}
|
||||
for (i = 0; i < max_entry; i++) {
|
||||
if (topic_list[i].clientcon != NULL && (clientcon == NULL || topic_list[i].clientcon == clientcon)) {
|
||||
if (topic == NULL || (topic_list[i].topic != NULL && strcmp(topic, topic_list[i].topic) == 0)) {
|
||||
topic_list[i].clientcon = NULL;
|
||||
os_free(topic_list[i].topic);
|
||||
topic_list[i].qos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ICACHE_FLASH_ATTR find_topic(uint8_t *topic, find_topic_cb cb, uint8_t *data, uint16_t data_len)
|
||||
{
|
||||
uint16_t i;
|
||||
bool retval = false;
|
||||
bool ICACHE_FLASH_ATTR find_topic(uint8_t * topic, find_topic_cb cb, uint8_t * data, uint16_t data_len) {
|
||||
uint16_t i;
|
||||
bool retval = false;
|
||||
|
||||
if (topic_list == NULL) return false;
|
||||
if (topic_list == NULL)
|
||||
return false;
|
||||
|
||||
for (i=0; i<max_entry; i++) {
|
||||
if (topic_list[i].clientcon != NULL) {
|
||||
if (Topics_matches(topic_list[i].topic, 1, topic)) {
|
||||
(*cb) (&topic_list[i], topic, data, data_len);
|
||||
retval = true;
|
||||
}
|
||||
for (i = 0; i < max_entry; i++) {
|
||||
if (topic_list[i].clientcon != NULL) {
|
||||
if (Topics_matches(topic_list[i].topic, 1, topic)) {
|
||||
(*cb) (&topic_list[i], topic, data, data_len);
|
||||
retval = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
return retval;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR iterate_topics(iterate_topic_cb cb, void *user_data)
|
||||
{
|
||||
uint16_t i;
|
||||
void ICACHE_FLASH_ATTR iterate_topics(iterate_topic_cb cb, void *user_data) {
|
||||
uint16_t i;
|
||||
|
||||
if (topic_list == NULL) return;
|
||||
|
||||
for (i=0; i<max_entry; i++) {
|
||||
if (topic_list[i].clientcon != NULL) {
|
||||
if ((*cb) (&topic_list[i], user_data) == true)
|
||||
if (topic_list == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < max_entry; i++) {
|
||||
if (topic_list[i].clientcon != NULL) {
|
||||
if ((*cb) (&topic_list[i], user_data) == true)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,214 +29,184 @@
|
|||
|
||||
char *_strtok_r(char *s, const char *delim, char **last);
|
||||
|
||||
char *_strchr(const char *s, int c)
|
||||
{
|
||||
char *_strchr(const char *s, int c) {
|
||||
while (*s != (char)c)
|
||||
if (!*s++)
|
||||
return 0;
|
||||
if (!*s++)
|
||||
return 0;
|
||||
return (char *)s;
|
||||
}
|
||||
|
||||
char *_strdup(char *src)
|
||||
{
|
||||
char *_strdup(char *src) {
|
||||
char *str;
|
||||
char *p;
|
||||
int len = 0;
|
||||
|
||||
while (src[len])
|
||||
len++;
|
||||
str = (char*)os_malloc(len + 1);
|
||||
len++;
|
||||
str = (char *)os_malloc(len + 1);
|
||||
p = str;
|
||||
while (*src)
|
||||
*p++ = *src++;
|
||||
*p++ = *src++;
|
||||
*p = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks that the syntax of a topic string is correct.
|
||||
* @param aName the topic name string
|
||||
* @return boolean value indicating whether the topic name is valid
|
||||
*/
|
||||
int Topics_isValidName(char* aName)
|
||||
{
|
||||
int rc = true;
|
||||
char *c = NULL;
|
||||
int length = os_strlen(aName);
|
||||
char* hashpos = _strchr(aName, '#'); /* '#' wildcard can be only at the beginning or the end of a topic */
|
||||
int Topics_isValidName(char *aName) {
|
||||
int rc = true;
|
||||
char *c = NULL;
|
||||
int length = os_strlen(aName);
|
||||
char *hashpos = _strchr(aName, '#'); /* '#' wildcard can be only at the beginning or the end of a topic */
|
||||
|
||||
if (hashpos != NULL)
|
||||
{
|
||||
char* second = _strchr(hashpos+1, '#');
|
||||
if ((hashpos != aName && hashpos != aName+(length-1)) || second != NULL)
|
||||
rc = false;
|
||||
if (hashpos != NULL) {
|
||||
char *second = _strchr(hashpos + 1, '#');
|
||||
if ((hashpos != aName && hashpos != aName + (length - 1)) || second != NULL)
|
||||
rc = false;
|
||||
}
|
||||
|
||||
/* '#' or '+' only next to a slash separator or end of name */
|
||||
for (c = "#+"; *c != '\0'; ++c) {
|
||||
char *pos = _strchr(aName, *c);
|
||||
while (pos != NULL) {
|
||||
if (pos > aName) { /* check previous char is '/' */
|
||||
if (*(pos - 1) != '/')
|
||||
rc = false;
|
||||
}
|
||||
if (*(pos + 1) != '\0') { /* check that subsequent char is '/' */
|
||||
if (*(pos + 1) != '/')
|
||||
rc = false;
|
||||
}
|
||||
pos = _strchr(pos + 1, *c);
|
||||
}
|
||||
}
|
||||
|
||||
/* '#' or '+' only next to a slash separator or end of name */
|
||||
for (c = "#+"; *c != '\0'; ++c)
|
||||
{
|
||||
char* pos = _strchr(aName, *c);
|
||||
while (pos != NULL)
|
||||
{
|
||||
if (pos > aName) /* check previous char is '/'*/
|
||||
{
|
||||
if (*(pos - 1) != '/')
|
||||
rc = false;
|
||||
}
|
||||
if (*(pos + 1) != '\0') /* check that subsequent char is '/'*/
|
||||
{
|
||||
if (*(pos + 1) != '/')
|
||||
rc = false;
|
||||
}
|
||||
pos = _strchr(pos + 1, *c);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reverse a string.
|
||||
* Linux utility function for Linux to enable Windows/Linux portability
|
||||
* @param astr the character string to reverse
|
||||
* @return pointer to the reversed string which was reversed in place
|
||||
*/
|
||||
char* _strrev(char* astr)
|
||||
{
|
||||
char* forwards = astr;
|
||||
int len = os_strlen(astr);
|
||||
if (len > 1)
|
||||
{
|
||||
char* backwards = astr + len - 1;
|
||||
while (forwards < backwards)
|
||||
{
|
||||
char temp = *forwards;
|
||||
*forwards++ = *backwards;
|
||||
*backwards-- = temp;
|
||||
}
|
||||
char *_strrev(char *astr) {
|
||||
char *forwards = astr;
|
||||
int len = os_strlen(astr);
|
||||
if (len > 1) {
|
||||
char *backwards = astr + len - 1;
|
||||
while (forwards < backwards) {
|
||||
char temp = *forwards;
|
||||
*forwards++ = *backwards;
|
||||
*backwards-- = temp;
|
||||
}
|
||||
return astr;
|
||||
}
|
||||
return astr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does a topic string contain wildcards?
|
||||
* @param topic the topic name string
|
||||
* @return boolean value indicating whether the topic contains a wildcard or not
|
||||
*/
|
||||
int Topics_hasWildcards(char* topic)
|
||||
{
|
||||
return (_strchr(topic, '+') != NULL) || (_strchr(topic, '#') != NULL);
|
||||
int Topics_hasWildcards(char *topic) {
|
||||
return (_strchr(topic, '+') != NULL) || (_strchr(topic, '#') != NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests whether one topic string matches another where one can contain wildcards.
|
||||
* @param wildTopic a topic name string that can contain wildcards
|
||||
* @param topic a topic name string that must not contain wildcards
|
||||
* @return boolean value indicating whether topic matches wildTopic
|
||||
*/
|
||||
int Topics_matches(char* wildTopic, int wildcards, char* topic)
|
||||
{
|
||||
int rc = false;
|
||||
char *last1 = NULL, *last2 = NULL;
|
||||
char *pwild = NULL, *pmatch = NULL;
|
||||
int Topics_matches(char *wildTopic, int wildcards, char *topic) {
|
||||
int rc = false;
|
||||
char *last1 = NULL, *last2 = NULL;
|
||||
char *pwild = NULL, *pmatch = NULL;
|
||||
|
||||
if (!wildcards)
|
||||
{
|
||||
rc = (os_strcmp(wildTopic, topic) == 0);
|
||||
goto exit;
|
||||
if (!wildcards) {
|
||||
rc = (os_strcmp(wildTopic, topic) == 0);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (Topics_hasWildcards(topic)) {
|
||||
//os_printf("Topics_matches: should not be wildcard in topic %s", topic);
|
||||
goto exit;
|
||||
}
|
||||
if (!Topics_isValidName(wildTopic)) {
|
||||
//os_printf("Topics_matches: invalid topic name %s", wildTopic);
|
||||
goto exit;
|
||||
}
|
||||
if (!Topics_isValidName(topic)) {
|
||||
//os_printf("Topics_matches: invalid topic name %s", topic);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strcmp(wildTopic, MULTI_LEVEL_WILDCARD) == 0 || /* Hash matches anything... */
|
||||
strcmp(wildTopic, topic) == 0) {
|
||||
rc = true;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strcmp(wildTopic, "/#") == 0) { /* Special case for /# matches anything starting with / */
|
||||
rc = (topic[0] == '/') ? true : false;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* because strtok will return bill when matching /bill/ or bill in a topic name for the first time,
|
||||
* we have to check whether the first character is / explicitly.
|
||||
*/
|
||||
if ((wildTopic[0] == TOPIC_LEVEL_SEPARATOR[0]) && (topic[0] != TOPIC_LEVEL_SEPARATOR[0]))
|
||||
goto exit;
|
||||
|
||||
if ((wildTopic[0] == SINGLE_LEVEL_WILDCARD[0]) && (topic[0] == TOPIC_LEVEL_SEPARATOR[0]))
|
||||
goto exit;
|
||||
|
||||
/* We only match hash-first topics in reverse, for speed */
|
||||
if (wildTopic[0] == MULTI_LEVEL_WILDCARD[0]) {
|
||||
wildTopic = (char *)_strrev(_strdup(wildTopic));
|
||||
topic = (char *)_strrev(_strdup(topic));
|
||||
} else {
|
||||
wildTopic = (char *)_strdup(wildTopic);
|
||||
topic = (char *)_strdup(topic);
|
||||
}
|
||||
|
||||
pwild = _strtok_r(wildTopic, TOPIC_LEVEL_SEPARATOR, &last1);
|
||||
pmatch = _strtok_r(topic, TOPIC_LEVEL_SEPARATOR, &last2);
|
||||
|
||||
/* Step through the subscription, level by level */
|
||||
while (pwild != NULL) {
|
||||
/* Have we got # - if so, it matches anything. */
|
||||
if (strcmp(pwild, MULTI_LEVEL_WILDCARD) == 0) {
|
||||
rc = true;
|
||||
break;
|
||||
}
|
||||
/* Nope - check for matches... */
|
||||
if (pmatch != NULL) {
|
||||
if (strcmp(pwild, SINGLE_LEVEL_WILDCARD) != 0 && strcmp(pwild, pmatch) != 0)
|
||||
/* The two levels simply don't match... */
|
||||
break;
|
||||
} else
|
||||
break; /* No more tokens to match against further tokens in the wildcard stream... */
|
||||
pwild = _strtok_r(NULL, TOPIC_LEVEL_SEPARATOR, &last1);
|
||||
pmatch = _strtok_r(NULL, TOPIC_LEVEL_SEPARATOR, &last2);
|
||||
}
|
||||
|
||||
if (Topics_hasWildcards(topic))
|
||||
{
|
||||
//os_printf("Topics_matches: should not be wildcard in topic %s", topic);
|
||||
goto exit;
|
||||
}
|
||||
if (!Topics_isValidName(wildTopic))
|
||||
{
|
||||
//os_printf("Topics_matches: invalid topic name %s", wildTopic);
|
||||
goto exit;
|
||||
}
|
||||
if (!Topics_isValidName(topic))
|
||||
{
|
||||
//os_printf("Topics_matches: invalid topic name %s", topic);
|
||||
goto exit;
|
||||
}
|
||||
/* All tokens up to here matched, and we didn't end in #. If there
|
||||
are any topic tokens remaining, the match is bad, otherwise it was
|
||||
a good match. */
|
||||
if (pmatch == NULL && pwild == NULL)
|
||||
rc = true;
|
||||
|
||||
if (strcmp(wildTopic, MULTI_LEVEL_WILDCARD) == 0 || /* Hash matches anything... */
|
||||
strcmp(wildTopic, topic) == 0)
|
||||
{
|
||||
rc = true;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strcmp(wildTopic, "/#") == 0) /* Special case for /# matches anything starting with / */
|
||||
{
|
||||
rc = (topic[0] == '/') ? true : false;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* because strtok will return bill when matching /bill/ or bill in a topic name for the first time,
|
||||
* we have to check whether the first character is / explicitly.
|
||||
*/
|
||||
if ((wildTopic[0] == TOPIC_LEVEL_SEPARATOR[0]) && (topic[0] != TOPIC_LEVEL_SEPARATOR[0]))
|
||||
goto exit;
|
||||
|
||||
if ((wildTopic[0] == SINGLE_LEVEL_WILDCARD[0]) && (topic[0] == TOPIC_LEVEL_SEPARATOR[0]))
|
||||
goto exit;
|
||||
|
||||
/* We only match hash-first topics in reverse, for speed */
|
||||
if (wildTopic[0] == MULTI_LEVEL_WILDCARD[0])
|
||||
{
|
||||
wildTopic = (char*)_strrev(_strdup(wildTopic));
|
||||
topic = (char*)_strrev(_strdup(topic));
|
||||
}
|
||||
else
|
||||
{
|
||||
wildTopic = (char*)_strdup(wildTopic);
|
||||
topic = (char*)_strdup(topic);
|
||||
}
|
||||
|
||||
pwild = _strtok_r(wildTopic, TOPIC_LEVEL_SEPARATOR, &last1);
|
||||
pmatch = _strtok_r(topic, TOPIC_LEVEL_SEPARATOR, &last2);
|
||||
|
||||
/* Step through the subscription, level by level */
|
||||
while (pwild != NULL)
|
||||
{
|
||||
/* Have we got # - if so, it matches anything. */
|
||||
if (strcmp(pwild, MULTI_LEVEL_WILDCARD) == 0)
|
||||
{
|
||||
rc = true;
|
||||
break;
|
||||
}
|
||||
/* Nope - check for matches... */
|
||||
if (pmatch != NULL)
|
||||
{
|
||||
if (strcmp(pwild, SINGLE_LEVEL_WILDCARD) != 0 && strcmp(pwild, pmatch) != 0)
|
||||
/* The two levels simply don't match... */
|
||||
break;
|
||||
}
|
||||
else
|
||||
break; /* No more tokens to match against further tokens in the wildcard stream... */
|
||||
pwild = _strtok_r(NULL, TOPIC_LEVEL_SEPARATOR, &last1);
|
||||
pmatch = _strtok_r(NULL, TOPIC_LEVEL_SEPARATOR, &last2);
|
||||
}
|
||||
|
||||
/* All tokens up to here matched, and we didn't end in #. If there
|
||||
are any topic tokens remaining, the match is bad, otherwise it was
|
||||
a good match. */
|
||||
if (pmatch == NULL && pwild == NULL)
|
||||
rc = true;
|
||||
|
||||
/* Now free the memory allocated in strdup() */
|
||||
os_free(wildTopic);
|
||||
os_free(topic);
|
||||
exit:
|
||||
return rc;
|
||||
} /* end matches*/
|
||||
/* Now free the memory allocated in strdup() */
|
||||
os_free(wildTopic);
|
||||
os_free(topic);
|
||||
exit:
|
||||
return rc;
|
||||
} /* end matches */
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#if !defined(ARRAY_SIZE)
|
||||
|
@ -246,73 +216,64 @@ exit:
|
|||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
#endif
|
||||
|
||||
int test()
|
||||
{
|
||||
int i;
|
||||
int test() {
|
||||
int i;
|
||||
|
||||
struct
|
||||
{
|
||||
char* str;
|
||||
} tests0[] = {
|
||||
"#", "jj",
|
||||
"+/a", "adkj/a",
|
||||
"+/a", "adsjk/adakjd/a", "a/+", "a/#", "#/a"
|
||||
};
|
||||
struct {
|
||||
char *str;
|
||||
} tests0[] = {
|
||||
"#", "jj", "+/a", "adkj/a", "+/a", "adsjk/adakjd/a", "a/+", "a/#", "#/a"};
|
||||
|
||||
for (i = 0; i < sizeof(tests0)/sizeof(char*); ++i)
|
||||
{
|
||||
os_printf("topic %s, isValidName %d\n", tests0[i].str, Topics_isValidName(tests0[i].str));
|
||||
//assert(Topics_isValidName(tests0[i].str) == 1);
|
||||
}
|
||||
for (i = 0; i < sizeof(tests0) / sizeof(char *); ++i) {
|
||||
os_printf("topic %s, isValidName %d\n", tests0[i].str, Topics_isValidName(tests0[i].str));
|
||||
//assert(Topics_isValidName(tests0[i].str) == 1);
|
||||
}
|
||||
|
||||
struct
|
||||
struct {
|
||||
char *wild;
|
||||
char *topic;
|
||||
int result;
|
||||
} tests1[] = {
|
||||
{
|
||||
char* wild;
|
||||
char* topic;
|
||||
int result;
|
||||
} tests1[] = {
|
||||
{ "#", "jj" , 1},
|
||||
{ "+/a", "adkj/a", 1},
|
||||
{ "+/a", "adsjk/adakjd/a", 0},
|
||||
{ "+/+/a", "adsjk/adakjd/a", 1},
|
||||
{ "#/a", "adsjk/adakjd/a", 1},
|
||||
{ "test/#", "test/1", 1},
|
||||
{ "test/+", "test/1", 1},
|
||||
{ "+", "test1", 1},
|
||||
{ "+", "test1/k", 0},
|
||||
{ "+", "/test1/k", 0},
|
||||
{ "/+", "test1/k", 0},
|
||||
{ "+", "/jkj", 0},
|
||||
{ "/+", "/test1", 1},
|
||||
{ "+/+", "/test1", 0},
|
||||
{ "+/+", "test1/k", 1},
|
||||
{ "/#", "/test1/k", 1},
|
||||
{ "/#", "test1/k", 0},
|
||||
};
|
||||
"#", "jj", 1}, {
|
||||
"+/a", "adkj/a", 1}, {
|
||||
"+/a", "adsjk/adakjd/a", 0}, {
|
||||
"+/+/a", "adsjk/adakjd/a", 1}, {
|
||||
"#/a", "adsjk/adakjd/a", 1}, {
|
||||
"test/#", "test/1", 1}, {
|
||||
"test/+", "test/1", 1}, {
|
||||
"+", "test1", 1}, {
|
||||
"+", "test1/k", 0}, {
|
||||
"+", "/test1/k", 0}, {
|
||||
"/+", "test1/k", 0}, {
|
||||
"+", "/jkj", 0}, {
|
||||
"/+", "/test1", 1}, {
|
||||
"+/+", "/test1", 0}, {
|
||||
"+/+", "test1/k", 1}, {
|
||||
"/#", "/test1/k", 1}, {
|
||||
"/#", "test1/k", 0},};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests1); ++i)
|
||||
{
|
||||
os_printf("wild: %s, topic %s, result %d %d (should)\n", tests1[i].wild, tests1[i].topic,
|
||||
Topics_matches(_strdup(tests1[i].wild), 1, _strdup(tests1[i].topic)), tests1[i].result);
|
||||
//assert(Topics_matches(_strdup(tests1[i].wild), _strdup(tests1[i].topic)) == tests1[i].result);
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(tests1); ++i) {
|
||||
os_printf("wild: %s, topic %s, result %d %d (should)\n", tests1[i].wild, tests1[i].topic,
|
||||
Topics_matches(_strdup(tests1[i].wild), 1, _strdup(tests1[i].topic)), tests1[i].result);
|
||||
//assert(Topics_matches(_strdup(tests1[i].wild), _strdup(tests1[i].topic)) == tests1[i].result);
|
||||
}
|
||||
|
||||
struct
|
||||
struct {
|
||||
char *str;
|
||||
char *result;
|
||||
} tests2[] = {
|
||||
{
|
||||
char* str;
|
||||
char* result;
|
||||
} tests2[] = {
|
||||
{ "#", "#" },
|
||||
{ "ab", "ba" },
|
||||
{ "abc", "cba" },
|
||||
{ "abcd", "dcba" },
|
||||
{ "abcde", "edcba" }
|
||||
};
|
||||
for (i = 0; i < 5; ++i)
|
||||
{
|
||||
os_printf("str: %s, _strrev %s\n", tests2[i].str, _strrev(_strdup(tests2[i].str)));
|
||||
//assert(strcmp(tests2[i].result, _strrev(_strdup(tests2[i].str))) == 0);
|
||||
}
|
||||
"#", "#"}, {
|
||||
"ab", "ba"}, {
|
||||
"abc", "cba"}, {
|
||||
"abcd", "dcba"}, {
|
||||
"abcde", "edcba"}
|
||||
};
|
||||
for (i = 0; i < 5; ++i) {
|
||||
os_printf("str: %s, _strrev %s\n", tests2[i].str, _strrev(_strdup(tests2[i].str)));
|
||||
//assert(strcmp(tests2[i].result, _strrev(_strdup(tests2[i].str))) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
209
mqtt/proto.c
209
mqtt/proto.c
|
@ -1,129 +1,132 @@
|
|||
#include "proto.h"
|
||||
#include "ringbuf_mqtt.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_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) {
|
||||
I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER * parser, U8 value) {
|
||||
switch (value) {
|
||||
case 0x7D:
|
||||
parser->isEsc = 1;
|
||||
break;
|
||||
parser->isEsc = 1;
|
||||
break;
|
||||
|
||||
case 0x7E:
|
||||
parser->dataLen = 0;
|
||||
parser->isEsc = 0;
|
||||
parser->isBegin = 1;
|
||||
break;
|
||||
parser->dataLen = 0;
|
||||
parser->isEsc = 0;
|
||||
parser->isBegin = 1;
|
||||
break;
|
||||
|
||||
case 0x7F:
|
||||
if (parser->callback != NULL)
|
||||
parser->callback();
|
||||
parser->isBegin = 0;
|
||||
return 0;
|
||||
break;
|
||||
if (parser->callback != NULL)
|
||||
parser->callback();
|
||||
parser->isBegin = 0;
|
||||
return 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (parser->isBegin == 0) break;
|
||||
if (parser->isBegin == 0)
|
||||
break;
|
||||
|
||||
if (parser->isEsc) {
|
||||
value ^= 0x20;
|
||||
parser->isEsc = 0;
|
||||
}
|
||||
if (parser->isEsc) {
|
||||
value ^= 0x20;
|
||||
parser->isEsc = 0;
|
||||
}
|
||||
|
||||
if (parser->dataLen < parser->bufSize)
|
||||
parser->buf[parser->dataLen++] = value;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
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;
|
||||
I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER * parser, U8 * buf, U16 len) {
|
||||
while (len--)
|
||||
PROTO_ParseByte(parser, *buf++);
|
||||
|
||||
*buf++ = 0x7E;
|
||||
bufSize--;
|
||||
return 0;
|
||||
}
|
||||
I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF * rb, U8 * bufOut, U16 * len, U16 maxBufLen) {
|
||||
U8 c;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bufSize < 1) return -1;
|
||||
*buf++ = 0x7F;
|
||||
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
I16 ICACHE_FLASH_ATTR PROTO_Add(U8 * buf, const U8 * packet, I16 bufSize) {
|
||||
U16 i = 2;
|
||||
U16 len = *(U16 *) packet;
|
||||
|
||||
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 (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 (RINGBUF_Put(rb, 0x7F) == -1) return -1;
|
||||
|
||||
return i;
|
||||
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;
|
||||
}
|
||||
|
|
26
mqtt/queue.c
26
mqtt/queue.c
|
@ -34,24 +34,20 @@
|
|||
#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);
|
||||
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_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)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
BOOL ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE * queue) {
|
||||
if (queue->rb.fill_cnt <= 0)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include "ringbuf_mqtt.h"
|
||||
|
||||
|
||||
/**
|
||||
* \brief init a RINGBUF object
|
||||
* \param r pointer to a RINGBUF object
|
||||
|
@ -13,55 +12,53 @@
|
|||
* \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;
|
||||
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;
|
||||
r->p_o = r->p_r = r->p_w = buf;
|
||||
r->fill_cnt = 0;
|
||||
r->size = size;
|
||||
|
||||
return 0;
|
||||
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
|
||||
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->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
|
||||
|
||||
*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;
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
*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;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -40,48 +40,46 @@
|
|||
#include "osapi.h"
|
||||
#include "mem.h"
|
||||
|
||||
char *
|
||||
_strtok_r(char *s, const char *delim, char **last)
|
||||
{
|
||||
char *spanp, *tok;
|
||||
int c, sc;
|
||||
char *_strtok_r(char *s, const char *delim, char **last) {
|
||||
char *spanp, *tok;
|
||||
int c, sc;
|
||||
|
||||
if (s == NULL && (s = *last) == NULL)
|
||||
return (NULL);
|
||||
if (s == NULL && (s = *last) == NULL)
|
||||
return (NULL);
|
||||
|
||||
/*
|
||||
* Skip (span) leading delimiters (s += strspn(s, delim), sort of).
|
||||
*/
|
||||
cont:
|
||||
/*
|
||||
* Skip (span) leading delimiters (s += strspn(s, delim), sort of).
|
||||
*/
|
||||
cont:
|
||||
c = *s++;
|
||||
for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
|
||||
if (c == sc)
|
||||
goto cont;
|
||||
}
|
||||
|
||||
if (c == 0) { /* no non-delimiter characters */
|
||||
*last = NULL;
|
||||
return (NULL);
|
||||
}
|
||||
tok = s - 1;
|
||||
|
||||
/*
|
||||
* Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
|
||||
* Note that delim must have one NUL; we stop if we see that, too.
|
||||
*/
|
||||
for (;;) {
|
||||
c = *s++;
|
||||
for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
|
||||
if (c == sc)
|
||||
goto cont;
|
||||
}
|
||||
|
||||
if (c == 0) { /* no non-delimiter characters */
|
||||
*last = NULL;
|
||||
return (NULL);
|
||||
}
|
||||
tok = s - 1;
|
||||
|
||||
/*
|
||||
* Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
|
||||
* Note that delim must have one NUL; we stop if we see that, too.
|
||||
*/
|
||||
for (;;) {
|
||||
c = *s++;
|
||||
spanp = (char *)delim;
|
||||
do {
|
||||
if ((sc = *spanp++) == c) {
|
||||
if (c == 0)
|
||||
s = NULL;
|
||||
else
|
||||
s[-1] = '\0';
|
||||
*last = s;
|
||||
return (tok);
|
||||
}
|
||||
} while (sc != 0);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
spanp = (char *)delim;
|
||||
do {
|
||||
if ((sc = *spanp++) == c) {
|
||||
if (c == 0)
|
||||
s = NULL;
|
||||
else
|
||||
s[-1] = '\0';
|
||||
*last = s;
|
||||
return (tok);
|
||||
}
|
||||
} while (sc != 0);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
|
187
mqtt/utils.c
187
mqtt/utils.c
|
@ -37,113 +37,108 @@
|
|||
#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. */
|
||||
|
||||
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. */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 numeric. */
|
||||
if ((*str < '0') || (*str > '9'))
|
||||
return 0;
|
||||
/* Check enough segments and enough characters in last segment. */
|
||||
|
||||
/* Accumulate and check segment. */
|
||||
if (segs != 3)
|
||||
return 0;
|
||||
if (chcnt == 0)
|
||||
return 0;
|
||||
/* Address okay. */
|
||||
|
||||
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;
|
||||
return 1;
|
||||
}
|
||||
uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip)
|
||||
{
|
||||
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;
|
||||
/* 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (n >= 256) {
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
((uint8_t*)ip)[i] = n;
|
||||
}
|
||||
return 1;
|
||||
|
||||
return value;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
213
ntp/ntp.c
213
ntp/ntp.c
|
@ -14,162 +14,161 @@
|
|||
|
||||
#define OFFSET 2208988800ULL
|
||||
|
||||
static ip_addr_t ntp_server_ip = {0};
|
||||
static ip_addr_t ntp_server_ip = { 0 };
|
||||
|
||||
static os_timer_t ntp_timeout;
|
||||
static struct espconn *pCon, pConDNS;
|
||||
|
||||
static struct timeval t_tv = {0,0};
|
||||
static struct timeval t_tv = { 0, 0 };
|
||||
|
||||
static uint64_t t_offset = 0;
|
||||
static int16_t ntp_timezone = 0;
|
||||
|
||||
void ICACHE_FLASH_ATTR get_cur_time(struct timeval *tv)
|
||||
{
|
||||
uint64_t t_curr = get_long_systime() - t_offset + t_tv.tv_usec;
|
||||
tv->tv_sec = t_tv.tv_sec + t_curr/1000000;
|
||||
tv->tv_usec = t_curr%1000000;
|
||||
void ICACHE_FLASH_ATTR get_cur_time(struct timeval *tv) {
|
||||
uint64_t t_curr = get_long_systime() - t_offset + t_tv.tv_usec;
|
||||
tv->tv_sec = t_tv.tv_sec + t_curr / 1000000;
|
||||
tv->tv_usec = t_curr % 1000000;
|
||||
}
|
||||
|
||||
|
||||
uint8_t* ICACHE_FLASH_ATTR get_timestr(int16_t timezone)
|
||||
{
|
||||
struct timeval tv;
|
||||
static uint8_t buf[10];
|
||||
|
||||
get_cur_time(&tv);
|
||||
tv.tv_sec += timezone * 3600;
|
||||
os_sprintf(buf, "%02d:%02d:%02d", (tv.tv_sec/3600)%24, (tv.tv_sec/60)%60, tv.tv_sec%60);
|
||||
return buf;
|
||||
void ICACHE_FLASH_ATTR set_timezone(int16_t timezone) {
|
||||
ntp_timezone = timezone;
|
||||
}
|
||||
|
||||
uint8_t *ICACHE_FLASH_ATTR get_timestr() {
|
||||
struct timeval tv;
|
||||
static uint8_t buf[10];
|
||||
|
||||
void ICACHE_FLASH_ATTR ntp_to_tv(uint8_t ntp[8], struct timeval *tv)
|
||||
{
|
||||
uint64_t aux = 0;
|
||||
|
||||
tv->tv_sec = ntohl(*(uint32_t*)ntp) - OFFSET;
|
||||
|
||||
aux = ntohl(*(uint32_t*)&ntp[4]);
|
||||
|
||||
// aux is the NTP fraction (0..2^32-1)
|
||||
aux *= 1000000; // multiply by 1e6
|
||||
aux >>= 32; // and divide by 2^32
|
||||
tv->tv_usec = (uint32_t)aux;
|
||||
get_cur_time(&tv);
|
||||
tv.tv_sec += ntp_timezone * 3600;
|
||||
os_sprintf(buf, "%02d:%02d:%02d", (tv.tv_sec / 3600) % 24, (tv.tv_sec / 60) % 60, tv.tv_sec % 60);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR ntp_to_tv(uint8_t ntp[8], struct timeval *tv) {
|
||||
uint64_t aux = 0;
|
||||
|
||||
LOCAL void ICACHE_FLASH_ATTR ntp_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) {
|
||||
struct espconn *pespconn = (struct espconn *)arg;
|
||||
tv->tv_sec = ntohl(*(uint32_t *) ntp) - OFFSET;
|
||||
|
||||
if (ipaddr != NULL) {
|
||||
os_printf("Got NTP server: %d.%d.%d.%d\r\n", IP2STR(ipaddr));
|
||||
// Call the NTP update
|
||||
ntp_server_ip.addr = ipaddr->addr;
|
||||
ntp_get_time();
|
||||
}
|
||||
aux = ntohl(*(uint32_t *) & ntp[4]);
|
||||
|
||||
// aux is the NTP fraction (0..2^32-1)
|
||||
aux *= 1000000; // multiply by 1e6
|
||||
aux >>= 32; // and divide by 2^32
|
||||
tv->tv_usec = (uint32_t) aux;
|
||||
}
|
||||
|
||||
LOCAL void ICACHE_FLASH_ATTR ntp_dns_found(const char *name, ip_addr_t * ipaddr, void *arg) {
|
||||
struct espconn *pespconn = (struct espconn *)arg;
|
||||
|
||||
if (ipaddr != NULL) {
|
||||
os_printf("Got NTP server: %d.%d.%d.%d\r\n", IP2STR(ipaddr));
|
||||
// Call the NTP update
|
||||
ntp_server_ip.addr = ipaddr->addr;
|
||||
ntp_get_time();
|
||||
}
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR ntp_udp_timeout(void *arg) {
|
||||
|
||||
os_timer_disarm(&ntp_timeout);
|
||||
os_printf("NTP timout\r\n");
|
||||
|
||||
// clean up connection
|
||||
if (pCon) {
|
||||
espconn_delete(pCon);
|
||||
os_free(pCon->proto.udp);
|
||||
os_free(pCon);
|
||||
pCon = 0;
|
||||
}
|
||||
os_timer_disarm(&ntp_timeout);
|
||||
os_printf("NTP timout\r\n");
|
||||
|
||||
// clean up connection
|
||||
if (pCon) {
|
||||
espconn_delete(pCon);
|
||||
os_free(pCon->proto.udp);
|
||||
os_free(pCon);
|
||||
pCon = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ICACHE_FLASH_ATTR ntp_udp_recv(void *arg, char *pdata, unsigned short len) {
|
||||
ntp_t *ntp;
|
||||
ntp_t *ntp;
|
||||
|
||||
struct timeval tv;
|
||||
int32_t hh, mm, ss;
|
||||
struct timeval tv;
|
||||
int32_t hh, mm, ss;
|
||||
|
||||
get_cur_time(&tv);
|
||||
if (!ntp_sync_done())
|
||||
os_printf("NTP synced\r\n");
|
||||
|
||||
// get the according sys_time;
|
||||
t_offset = get_long_systime();
|
||||
get_cur_time(&tv);
|
||||
|
||||
os_timer_disarm(&ntp_timeout);
|
||||
// get the according sys_time;
|
||||
t_offset = get_long_systime();
|
||||
|
||||
// extract ntp time
|
||||
ntp = (ntp_t*)pdata;
|
||||
os_timer_disarm(&ntp_timeout);
|
||||
|
||||
ntp_to_tv(ntp->trans_time, &t_tv);
|
||||
// extract ntp time
|
||||
ntp = (ntp_t *) pdata;
|
||||
|
||||
os_printf("NTP resync - diff: %d usecs\r\n", t_tv.tv_usec-tv.tv_usec);
|
||||
ntp_to_tv(ntp->trans_time, &t_tv);
|
||||
|
||||
// os_printf("NTP re-sync - diff: %d usecs\r\n", t_tv.tv_usec-tv.tv_usec);
|
||||
/*
|
||||
ss = t_tv.tv_sec%60;
|
||||
mm = (t_tv.tv_sec/60)%60;
|
||||
hh = (t_tv.tv_sec/3600)%24;
|
||||
os_printf("time: %2d:%02d:%02d\r\n", hh, mm, ss);
|
||||
*/
|
||||
// clean up connection
|
||||
if (pCon) {
|
||||
espconn_delete(pCon);
|
||||
os_free(pCon->proto.udp);
|
||||
os_free(pCon);
|
||||
pCon = 0;
|
||||
}
|
||||
// clean up connection
|
||||
if (pCon) {
|
||||
espconn_delete(pCon);
|
||||
os_free(pCon->proto.udp);
|
||||
os_free(pCon);
|
||||
pCon = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR ntp_set_server(uint8_t * ntp_server) {
|
||||
|
||||
void ICACHE_FLASH_ATTR ntp_set_server(uint8_t *ntp_server) {
|
||||
ntp_server_ip.addr = 0;
|
||||
|
||||
ntp_server_ip.addr = 0;
|
||||
// invalid arg?
|
||||
if (ntp_server == NULL)
|
||||
return;
|
||||
|
||||
// invalid arg?
|
||||
if (ntp_server == NULL)
|
||||
return;
|
||||
|
||||
// ip or DNS name?
|
||||
if (UTILS_IsIPV4(ntp_server)) {
|
||||
// read address
|
||||
UTILS_StrToIP(ntp_server, &ntp_server_ip);
|
||||
ntp_get_time();
|
||||
} else {
|
||||
// call DNS and wait for callback
|
||||
espconn_gethostbyname(&pConDNS, ntp_server, &ntp_server_ip, ntp_dns_found);
|
||||
}
|
||||
// ip or DNS name?
|
||||
if (UTILS_IsIPV4(ntp_server)) {
|
||||
// read address
|
||||
UTILS_StrToIP(ntp_server, &ntp_server_ip);
|
||||
ntp_get_time();
|
||||
} else {
|
||||
// call DNS and wait for callback
|
||||
espconn_gethostbyname(&pConDNS, ntp_server, &ntp_server_ip, ntp_dns_found);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ICACHE_FLASH_ATTR ntp_sync_done() {
|
||||
return t_offset!=0;
|
||||
return t_offset != 0;
|
||||
}
|
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR ntp_get_time() {
|
||||
ntp_t ntp;
|
||||
ntp_t ntp;
|
||||
|
||||
// either ongoing request or invalid ip?
|
||||
if (pCon != 0 || ntp_server_ip.addr == 0)
|
||||
return;
|
||||
// either ongoing request or invalid ip?
|
||||
if (pCon != 0 || ntp_server_ip.addr == 0)
|
||||
return;
|
||||
|
||||
// set up the udp "connection"
|
||||
pCon = (struct espconn*)os_zalloc(sizeof(struct espconn));
|
||||
pCon->type = ESPCONN_UDP;
|
||||
pCon->state = ESPCONN_NONE;
|
||||
pCon->proto.udp = (esp_udp*)os_zalloc(sizeof(esp_udp));
|
||||
pCon->proto.udp->local_port = espconn_port();
|
||||
pCon->proto.udp->remote_port = 123;
|
||||
os_memcpy(pCon->proto.udp->remote_ip, &ntp_server_ip, 4);
|
||||
// set up the udp "connection"
|
||||
pCon = (struct espconn *)os_zalloc(sizeof(struct espconn));
|
||||
pCon->type = ESPCONN_UDP;
|
||||
pCon->state = ESPCONN_NONE;
|
||||
pCon->proto.udp = (esp_udp *) os_zalloc(sizeof(esp_udp));
|
||||
pCon->proto.udp->local_port = espconn_port();
|
||||
pCon->proto.udp->remote_port = 123;
|
||||
os_memcpy(pCon->proto.udp->remote_ip, &ntp_server_ip, 4);
|
||||
|
||||
// create a really simple ntp request packet
|
||||
os_memset(&ntp, 0, sizeof(ntp_t));
|
||||
ntp.options = 0b00011011; // leap = 0, version = 3, mode = 3 (client)
|
||||
// create a really simple ntp request packet
|
||||
os_memset(&ntp, 0, sizeof(ntp_t));
|
||||
ntp.options = 0b00011011; // leap = 0, version = 3, mode = 3 (client)
|
||||
|
||||
// set timeout timer
|
||||
os_timer_disarm(&ntp_timeout);
|
||||
os_timer_setfn(&ntp_timeout, (os_timer_func_t*)ntp_udp_timeout, pCon);
|
||||
os_timer_arm(&ntp_timeout, NTP_TIMEOUT_MS, 0);
|
||||
// set timeout timer
|
||||
os_timer_disarm(&ntp_timeout);
|
||||
os_timer_setfn(&ntp_timeout, (os_timer_func_t *) ntp_udp_timeout, pCon);
|
||||
os_timer_arm(&ntp_timeout, NTP_TIMEOUT_MS, 0);
|
||||
|
||||
// send the ntp request
|
||||
espconn_create(pCon);
|
||||
espconn_regist_recvcb(pCon, ntp_udp_recv);
|
||||
espconn_sent(pCon, (uint8*)&ntp, sizeof(ntp_t));
|
||||
// send the ntp request
|
||||
espconn_create(pCon);
|
||||
espconn_regist_recvcb(pCon, ntp_udp_recv);
|
||||
espconn_sent(pCon, (uint8 *) & ntp, sizeof(ntp_t));
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ void ntp_set_server(uint8_t *ntp_server);
|
|||
bool ntp_sync_done();
|
||||
void ntp_get_time();
|
||||
void get_cur_time(struct timeval *tv);
|
||||
uint8_t *get_timestr(int16_t timezone);
|
||||
void set_timezone(int16_t timezone);
|
||||
uint8_t *get_timestr();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,108 +1,102 @@
|
|||
#include "lwip/ip.h"
|
||||
#include "config_flash.h"
|
||||
|
||||
|
||||
/* From the document 99A-SDK-Espressif IOT Flash RW Operation_v0.2 *
|
||||
* -------------------------------------------------------------------------*
|
||||
* Flash is erased sector by sector, which means it has to erase 4Kbytes one
|
||||
* time at least. When you want to change some data in flash, you have to
|
||||
* erase the whole sector, and then write it back with the new data.
|
||||
*--------------------------------------------------------------------------*/
|
||||
void config_load_default(sysconfig_p config)
|
||||
{
|
||||
uint8_t mac[6];
|
||||
void config_load_default(sysconfig_p config) {
|
||||
uint8_t mac[6];
|
||||
|
||||
os_memset(config, 0, sizeof(sysconfig_t));
|
||||
os_printf("Loading default configuration\r\n");
|
||||
config->magic_number = MAGIC_NUMBER;
|
||||
config->length = sizeof(sysconfig_t);
|
||||
os_sprintf(config->ssid,"%s", WIFI_SSID);
|
||||
os_sprintf(config->password,"%s", WIFI_PASSWORD);
|
||||
config->auto_connect = 1;
|
||||
os_sprintf(config->ap_ssid,"%s", WIFI_AP_SSID);
|
||||
os_sprintf(config->ap_password,"%s",WIFI_AP_PASSWORD);
|
||||
config->ap_open = 1;
|
||||
config->ap_on = 1;
|
||||
config->locked = 0;
|
||||
config->magic_number = MAGIC_NUMBER;
|
||||
config->length = sizeof(sysconfig_t);
|
||||
os_sprintf(config->ssid, "%s", WIFI_SSID);
|
||||
os_sprintf(config->password, "%s", WIFI_PASSWORD);
|
||||
config->auto_connect = 1;
|
||||
os_sprintf(config->ap_ssid, "%s", WIFI_AP_SSID);
|
||||
os_sprintf(config->ap_password, "%s", WIFI_AP_PASSWORD);
|
||||
config->ap_open = 1;
|
||||
config->ap_on = 1;
|
||||
|
||||
config->locked = 0;
|
||||
config->lock_password[0] = '\0';
|
||||
|
||||
IP4_ADDR(&config->network_addr, 192, 168, 4, 1);
|
||||
config->dns_addr.addr = 0; // use DHCP
|
||||
config->my_addr.addr = 0; // use DHCP
|
||||
config->my_netmask.addr = 0; // use DHCP
|
||||
config->my_gw.addr = 0; // use DHCP
|
||||
config->dns_addr.addr = 0; // use DHCP
|
||||
config->my_addr.addr = 0; // use DHCP
|
||||
config->my_netmask.addr = 0; // use DHCP
|
||||
config->my_gw.addr = 0; // use DHCP
|
||||
|
||||
config->clock_speed = 80;
|
||||
config->config_port = CONSOLE_SERVER_PORT;
|
||||
config->clock_speed = 80;
|
||||
config->config_port = CONSOLE_SERVER_PORT;
|
||||
|
||||
#ifdef MQTT_CLIENT
|
||||
os_sprintf(config->mqtt_host,"%s", "none");
|
||||
config->mqtt_port = 1883;
|
||||
os_sprintf(config->mqtt_user,"%s", "none");
|
||||
config->mqtt_password[0] = 0;
|
||||
os_sprintf(config->mqtt_host, "%s", "none");
|
||||
config->mqtt_port = 1883;
|
||||
os_sprintf(config->mqtt_user, "%s", "none");
|
||||
config->mqtt_password[0] = 0;
|
||||
wifi_get_macaddr(0, mac);
|
||||
os_sprintf(config->mqtt_id,"%s_%2x%2x%2x", MQTT_ID, mac[3], mac[4], mac[5]);
|
||||
os_sprintf(config->mqtt_id, "%s_%2x%2x%2x", MQTT_ID, mac[3], mac[4], mac[5]);
|
||||
#endif
|
||||
#ifdef NTP
|
||||
os_sprintf(config->ntp_server,"%s", "none");
|
||||
config->ntp_interval = 60000000;
|
||||
config->ntp_timezone = 0;
|
||||
os_sprintf(config->ntp_server, "%s", "1.pool.ntp.org");
|
||||
config->ntp_interval = 300000000;
|
||||
config->ntp_timezone = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int config_load(sysconfig_p config)
|
||||
{
|
||||
if (config == NULL) return -1;
|
||||
int config_load(sysconfig_p config) {
|
||||
if (config == NULL)
|
||||
return -1;
|
||||
uint16_t base_address = FLASH_BLOCK_NO;
|
||||
|
||||
spi_flash_read(base_address* SPI_FLASH_SEC_SIZE, &config->magic_number, 4);
|
||||
spi_flash_read(base_address * SPI_FLASH_SEC_SIZE, &config->magic_number, 4);
|
||||
|
||||
if((config->magic_number != MAGIC_NUMBER))
|
||||
{
|
||||
os_printf("\r\nNo config found, saving default in flash\r\n");
|
||||
config_load_default(config);
|
||||
config_save(config);
|
||||
return -1;
|
||||
if ((config->magic_number != MAGIC_NUMBER)) {
|
||||
os_printf("\r\nNo config found, saving default in flash\r\n");
|
||||
config_load_default(config);
|
||||
config_save(config);
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_printf("\r\nConfig found and loaded\r\n");
|
||||
spi_flash_read(base_address * SPI_FLASH_SEC_SIZE, (uint32 *) config, sizeof(sysconfig_t));
|
||||
if (config->length != sizeof(sysconfig_t))
|
||||
{
|
||||
os_printf("Length Mismatch, probably old version of config, loading defaults\r\n");
|
||||
config_load_default(config);
|
||||
config_save(config);
|
||||
if (config->length != sizeof(sysconfig_t)) {
|
||||
os_printf("Length Mismatch, probably old version of config, loading defaults\r\n");
|
||||
config_load_default(config);
|
||||
config_save(config);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void config_save(sysconfig_p config)
|
||||
{
|
||||
void config_save(sysconfig_p config) {
|
||||
uint16_t base_address = FLASH_BLOCK_NO;
|
||||
os_printf("Saving configuration\r\n");
|
||||
spi_flash_erase_sector(base_address);
|
||||
spi_flash_write(base_address * SPI_FLASH_SEC_SIZE, (uint32 *)config, sizeof(sysconfig_t));
|
||||
spi_flash_write(base_address * SPI_FLASH_SEC_SIZE, (uint32 *) config, sizeof(sysconfig_t));
|
||||
}
|
||||
|
||||
void blob_save(uint8_t blob_no, uint32_t *data, uint16_t len)
|
||||
{
|
||||
void blob_save(uint8_t blob_no, uint32_t * data, uint16_t len) {
|
||||
uint16_t base_address = FLASH_BLOCK_NO + 1 + blob_no;
|
||||
spi_flash_erase_sector(base_address);
|
||||
spi_flash_write(base_address * SPI_FLASH_SEC_SIZE, data, len);
|
||||
}
|
||||
|
||||
void blob_load(uint8_t blob_no, uint32_t *data, uint16_t len)
|
||||
{
|
||||
void blob_load(uint8_t blob_no, uint32_t * data, uint16_t len) {
|
||||
uint16_t base_address = FLASH_BLOCK_NO + 1 + blob_no;
|
||||
spi_flash_read(base_address * SPI_FLASH_SEC_SIZE, data, len);
|
||||
}
|
||||
|
||||
void blob_zero(uint8_t blob_no, uint16_t len)
|
||||
{
|
||||
int i;
|
||||
void blob_zero(uint8_t blob_no, uint16_t len) {
|
||||
int i;
|
||||
uint8_t z[len];
|
||||
os_memset(z, 0,len);
|
||||
os_memset(z, 0, len);
|
||||
uint16_t base_address = FLASH_BLOCK_NO + 1 + blob_no;
|
||||
spi_flash_erase_sector(base_address);
|
||||
spi_flash_write(base_address * SPI_FLASH_SEC_SIZE, (uint32_t *)z, len);
|
||||
spi_flash_write(base_address * SPI_FLASH_SEC_SIZE, (uint32_t *) z, len);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ typedef struct
|
|||
uint8_t ap_on; // AP enabled?
|
||||
|
||||
uint8_t locked; // Should we allow for config changes
|
||||
uint8_t lock_password[32]; // Password of config lock
|
||||
ip_addr_t network_addr; // Address of the internal network
|
||||
ip_addr_t dns_addr; // Optional: address of the dns server
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
% Config params, overwrite any previous settings from the commandline
|
||||
config ap_ssid MyAP
|
||||
config ap_password stupidPassword
|
||||
config ntp_server 1.pool.ntp.org
|
||||
config mqtt_host 192.168.1.20
|
||||
|
||||
% Now the initialization, this is done once after booting
|
||||
on init
|
||||
do
|
||||
println "MQTT Script 1.0 starting"
|
||||
subscribe local /test/#
|
||||
settimer 1 1000 % once per second
|
||||
setvar $1 0
|
||||
setvar $2 0
|
||||
|
||||
% Now the events, checked whenever something happens
|
||||
|
||||
% Here a remote republish, of any local topic starting with "/test/"
|
||||
on topic local /test/#
|
||||
do
|
||||
publish remote $this_topic $this_data
|
||||
|
||||
% When timer 1 expires, do some stuff
|
||||
on timer 1
|
||||
do
|
||||
% publish a timestamp locally
|
||||
publish local /t/time $timestamp
|
||||
|
||||
% Let the LED on GPIO 2 blink
|
||||
gpio_out 2 $1
|
||||
setvar $1 not $1
|
||||
|
||||
% Count occurences in var $2
|
||||
setvar $2 $2 add 1
|
||||
|
||||
% And if we have reached 10, print that to the console
|
||||
if $2 gte 10 then
|
||||
print "We have reached "
|
||||
println $2
|
||||
setvar $2 0
|
||||
endif
|
||||
|
||||
% Reload the timer
|
||||
settimer 1 1000
|
||||
|
||||
% Here a local publication once each day at noon
|
||||
on clock 12:00:00
|
||||
do
|
||||
publish local /t/2 "High Noon"
|
|
@ -0,0 +1,800 @@
|
|||
#include "c_types.h"
|
||||
#include "mem.h"
|
||||
#include "osapi.h"
|
||||
#include "lang.h"
|
||||
#include "user_config.h"
|
||||
#include "mqtt_topics.h"
|
||||
#include "ntp.h"
|
||||
|
||||
#include "easygpio.h"
|
||||
|
||||
#define lang_debug //os_printf
|
||||
#define lang_info //os_printf
|
||||
|
||||
extern void do_command(char *t1, char *t2, char *t3);
|
||||
extern void con_print(uint8_t *str);
|
||||
|
||||
#define len_check(x) \
|
||||
if (interpreter_status==SYNTAX_CHECK && next_token+(x) >= max_token) \
|
||||
return syntax_error(next_token+(x), "end of text")
|
||||
#define syn_chk (interpreter_status==SYNTAX_CHECK)
|
||||
|
||||
#define ON "\xf0"
|
||||
#define CONFIG "\xf1"
|
||||
|
||||
typedef struct _var_entry_t {
|
||||
uint8_t data[MAX_VAR_LEN];
|
||||
uint32_t data_len;
|
||||
Value_Type data_type;
|
||||
} var_entry_t;
|
||||
|
||||
typedef struct _timestamp_entry_t {
|
||||
uint8_t *ts;
|
||||
bool happened;
|
||||
} timestamp_entry_t;
|
||||
|
||||
char **my_token;
|
||||
int max_token;
|
||||
bool script_enabled = false;
|
||||
bool in_topic_statement;
|
||||
Interpreter_Status interpreter_status;
|
||||
char *interpreter_topic;
|
||||
char *interpreter_data;
|
||||
int interpreter_data_len;
|
||||
int interpreter_timer;
|
||||
char *interpreter_timestamp;
|
||||
int ts_counter;
|
||||
|
||||
static os_timer_t timers[MAX_TIMERS];
|
||||
static var_entry_t vars[MAX_VARS];
|
||||
static timestamp_entry_t timestamps[MAX_TIMESTAMPS];
|
||||
|
||||
static void ICACHE_FLASH_ATTR lang_timers_timeout(void *arg) {
|
||||
|
||||
interpreter_timer = (int)arg;
|
||||
os_timer_disarm(&timers[interpreter_timer]);
|
||||
if (!script_enabled)
|
||||
return;
|
||||
|
||||
lang_debug("timer %d expired\r\n", interpreter_timer + 1);
|
||||
|
||||
interpreter_topic = interpreter_data = "";
|
||||
interpreter_data_len = 0;
|
||||
interpreter_status = TIMER;
|
||||
parse_statement(0);
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR init_timestamps(uint8_t * curr_time) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ts_counter; i++) {
|
||||
if (os_strcmp(curr_time, timestamps[i].ts) >= 0) {
|
||||
timestamps[i].happened = true;
|
||||
} else {
|
||||
timestamps[i].happened = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR check_timestamps(uint8_t * curr_time) {
|
||||
int i;
|
||||
|
||||
if (!script_enabled)
|
||||
return;
|
||||
for (i = 0; i < ts_counter; i++) {
|
||||
if (os_strcmp(curr_time, timestamps[i].ts) >= 0) {
|
||||
if (timestamps[i].happened)
|
||||
continue;
|
||||
lang_info("timerstamp %s happened\r\n", timestamps[i].ts);
|
||||
|
||||
interpreter_topic = interpreter_data = "";
|
||||
interpreter_data_len = 0;
|
||||
interpreter_status = CLOCK;
|
||||
interpreter_timestamp = timestamps[i].ts;
|
||||
parse_statement(0);
|
||||
timestamps[i].happened = true;
|
||||
} else {
|
||||
timestamps[i].happened = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR test_tokens(void) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_token; i++) {
|
||||
lang_debug("<%s>", my_token[i]);
|
||||
}
|
||||
lang_debug("\r\n");
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR text_into_tokens(char *str) {
|
||||
char *p, *q;
|
||||
int token_count = 0;
|
||||
bool in_token = false;
|
||||
|
||||
// preprocessing
|
||||
lang_debug("lexxer preprocessing\r\n");
|
||||
|
||||
for (p = q = str; *p != 0; p++) {
|
||||
// special case "on" keyword - replace by special token ON (0xf0)
|
||||
if (!in_token && *p == 'o' && *(p + 1) == 'n' && *(p + 2) <= ' ') {
|
||||
*q++ = '\xf0';
|
||||
p += 1;
|
||||
continue;
|
||||
}
|
||||
// special case "config" keyword - replace by special token CONFIG (0xf1)
|
||||
if (!in_token && *p == 'c' && *(p + 1) == 'o' && *(p + 2) == 'n'
|
||||
&& *(p + 3) == 'f' && *(p + 4) == 'i' && *(p + 5) == 'g' && *(p + 6) <= ' ') {
|
||||
*q++ = '\xf1';
|
||||
p += 5;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*p == '\\') {
|
||||
// next char is quoted, copy that - skip this one
|
||||
if (*(p + 1) != 0)
|
||||
*q++ = *++p;
|
||||
} else if (*p == '\"') {
|
||||
// string quotation
|
||||
in_token = !in_token;
|
||||
*q++ = 1;
|
||||
} else if (*p == '%' && !in_token) {
|
||||
// comment till eol
|
||||
for (; *p != 0; p++)
|
||||
if (*p == '\n')
|
||||
break;
|
||||
} else if (*p <= ' ' && !in_token) {
|
||||
// mark this as whitespace
|
||||
*q++ = 1;
|
||||
} else {
|
||||
*q++ = *p;
|
||||
}
|
||||
}
|
||||
*q = 0;
|
||||
|
||||
// eliminate double whitespace and count tokens
|
||||
lang_debug("lexxer whitespaces\r\n");
|
||||
|
||||
in_token = false;
|
||||
for (p = q = str; *p != 0; p++) {
|
||||
if (*p == 1) {
|
||||
if (in_token) {
|
||||
*q++ = 1;
|
||||
in_token = false;
|
||||
}
|
||||
} else {
|
||||
if (!in_token) {
|
||||
token_count++;
|
||||
in_token = true;
|
||||
}
|
||||
*q++ = *p;
|
||||
}
|
||||
}
|
||||
*q = 0;
|
||||
|
||||
lang_debug("found %d tokens\r\n", token_count);
|
||||
my_token = (char **)os_malloc(token_count * sizeof(char *));
|
||||
if (my_token == 0)
|
||||
return 0;
|
||||
|
||||
// assign tokens
|
||||
lang_debug("lexxer tokenize\r\n");
|
||||
|
||||
in_token = false;
|
||||
token_count = 0;
|
||||
for (p = str; *p != 0; p++) {
|
||||
if (*p == 1) {
|
||||
*p = '\0';
|
||||
in_token = false;
|
||||
} else {
|
||||
if (!in_token) {
|
||||
my_token[token_count++] = p;
|
||||
in_token = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
max_token = token_count;
|
||||
return max_token;
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR free_tokens(void) {
|
||||
if (my_token != NULL)
|
||||
os_free((uint32_t *) my_token);
|
||||
my_token = NULL;
|
||||
}
|
||||
|
||||
bool ICACHE_FLASH_ATTR is_token(int i, char *s) {
|
||||
if (i >= max_token)
|
||||
return false;
|
||||
//os_printf("cmp: %s %s\r\n", s, my_token[i]);
|
||||
return os_strcmp(my_token[i], s) == 0;
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR search_token(int i, char *s) {
|
||||
for (; i < max_token; i++)
|
||||
if (is_token(i, s))
|
||||
return i;
|
||||
return max_token;
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR syntax_error(int i, char *message) {
|
||||
int j;
|
||||
|
||||
os_sprintf(syntax_error_buffer, "Error (%s) at >>", message);
|
||||
for (j = i; j < i + 5 && j < max_token; j++) {
|
||||
int pos = os_strlen(syntax_error_buffer);
|
||||
if (is_token(j, ON))
|
||||
my_token[j] = "on";
|
||||
if (is_token(j, CONFIG))
|
||||
my_token[j] = "config";
|
||||
if (sizeof(syntax_error_buffer) - pos - 2 > os_strlen(my_token[j])) {
|
||||
os_sprintf(syntax_error_buffer + pos, "%s ", my_token[j]);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR parse_statement(int next_token) {
|
||||
bool event_happened;
|
||||
int on_token;
|
||||
|
||||
while (next_token < max_token) {
|
||||
|
||||
in_topic_statement = false;
|
||||
|
||||
if (!syn_chk)
|
||||
next_token = search_token(next_token, ON);
|
||||
|
||||
if (is_token(next_token, ON)) {
|
||||
lang_debug("statement on\r\n");
|
||||
|
||||
if ((next_token = parse_event(next_token + 1, &event_happened)) == -1)
|
||||
return -1;
|
||||
if (!syn_chk && !event_happened)
|
||||
continue;
|
||||
|
||||
if (syn_chk && !is_token(next_token, "do"))
|
||||
return syntax_error(next_token, "'do' expected");
|
||||
if ((next_token = parse_action(next_token + 1, event_happened)) == -1)
|
||||
return -1;
|
||||
} else if (is_token(next_token, CONFIG)) {
|
||||
next_token += 3;
|
||||
} else {
|
||||
return syntax_error(next_token, "'on' or 'config' expected");
|
||||
}
|
||||
}
|
||||
return next_token;
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR parse_event(int next_token, bool * happend) {
|
||||
*happend = false;
|
||||
|
||||
if (is_token(next_token, "init")) {
|
||||
lang_debug("event init\r\n");
|
||||
|
||||
*happend = (interpreter_status == INIT || interpreter_status == RE_INIT);
|
||||
return next_token + 1;
|
||||
}
|
||||
|
||||
if (is_token(next_token, "topic")) {
|
||||
lang_debug("event topic\r\n");
|
||||
|
||||
in_topic_statement = true;
|
||||
len_check(2);
|
||||
if (is_token(next_token + 1, "remote")) {
|
||||
if (interpreter_status != TOPIC_REMOTE)
|
||||
return next_token + 3;
|
||||
} else if (is_token(next_token + 1, "local")) {
|
||||
if (interpreter_status != TOPIC_LOCAL)
|
||||
return next_token + 3;
|
||||
} else {
|
||||
return syntax_error(next_token + 1, "'local' or 'remote' expected");
|
||||
}
|
||||
|
||||
*happend = Topics_matches(my_token[next_token + 2], true, interpreter_topic);
|
||||
|
||||
if (*happend)
|
||||
lang_info("topic %s %s %s match\r\n", my_token[next_token + 1],
|
||||
my_token[next_token + 2], interpreter_topic);
|
||||
|
||||
return next_token + 3;
|
||||
}
|
||||
|
||||
if (is_token(next_token, "timer")) {
|
||||
lang_debug("event timer\r\n");
|
||||
|
||||
len_check(1);
|
||||
uint32_t timer_no = atoi(my_token[next_token + 1]);
|
||||
if (timer_no == 0 || timer_no > MAX_TIMERS)
|
||||
return syntax_error(next_token + 1, "invalid timer number");
|
||||
if (interpreter_status == TIMER && interpreter_timer == --timer_no) {
|
||||
lang_info("timer %s expired\r\n", my_token[next_token + 1]);
|
||||
*happend = true;
|
||||
}
|
||||
return next_token + 2;
|
||||
}
|
||||
|
||||
else if (is_token(next_token, "clock")) {
|
||||
lang_debug("event clock\r\n");
|
||||
|
||||
len_check(1);
|
||||
if (syn_chk && os_strlen(my_token[next_token + 1]) != 8)
|
||||
return syntax_error(next_token, "invalid timestamp");
|
||||
if (syn_chk) {
|
||||
if (ts_counter >= MAX_TIMESTAMPS)
|
||||
return syntax_error(next_token, "too many timestamps");
|
||||
timestamps[ts_counter++].ts = my_token[next_token + 1];
|
||||
}
|
||||
*happend = (interpreter_status == CLOCK && os_strcmp(interpreter_timestamp, my_token[next_token + 1]) == 0);
|
||||
return next_token + 2;
|
||||
}
|
||||
|
||||
return syntax_error(next_token, "'init', 'topic', 'clock', or 'timer' expected");
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR parse_action(int next_token, bool doit) {
|
||||
|
||||
while (next_token < max_token && !is_token(next_token, ON)
|
||||
&& !is_token(next_token, CONFIG) && !is_token(next_token, "endif")) {
|
||||
lang_debug("action %s %s\r\n", my_token[next_token], doit ? "do" : "ignore");
|
||||
|
||||
bool is_nl = false;
|
||||
if ((is_nl = is_token(next_token, "println")) || is_token(next_token, "print")) {
|
||||
char *p_char;
|
||||
int p_len;
|
||||
Value_Type p_type;
|
||||
|
||||
len_check(1);
|
||||
if ((next_token = parse_expression(next_token + 1, &p_char, &p_len, &p_type)) == -1)
|
||||
return -1;
|
||||
if (doit) {
|
||||
con_print(p_char);
|
||||
if (is_nl)
|
||||
con_print("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
else if (is_token(next_token, "publish")) {
|
||||
bool retained = false;
|
||||
char *data;
|
||||
int data_len;
|
||||
Value_Type data_type;
|
||||
char *topic;
|
||||
int topic_len;
|
||||
Value_Type topic_type;
|
||||
int lr_token = next_token + 1;
|
||||
|
||||
len_check(3);
|
||||
if ((next_token = parse_value(next_token + 2, &topic, &topic_len, &topic_type)) == -1)
|
||||
return -1;
|
||||
if ((next_token = parse_value(next_token, &data, &data_len, &data_type)) == -1)
|
||||
return -1;
|
||||
if (next_token < max_token && is_token(next_token, "retained")) {
|
||||
retained = true;
|
||||
next_token++;
|
||||
}
|
||||
|
||||
if (doit) {
|
||||
if (data_type == STRING_T && data_len > 0)
|
||||
data_len--;
|
||||
if (topic_type != STRING_T || Topics_hasWildcards(topic)) {
|
||||
os_printf("invalid topic string\r\n");
|
||||
return next_token;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MQTT_CLIENT
|
||||
if (is_token(lr_token, "remote")) {
|
||||
if (doit && mqtt_connected) {
|
||||
MQTT_Publish(&mqttClient, topic, data, data_len, 0, retained);
|
||||
lang_info("published remote %s len: %d\r\n", topic, data_len);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (is_token(lr_token, "local")) {
|
||||
if (doit && interpreter_status != RE_INIT) {
|
||||
MQTT_local_publish(topic, data, data_len, 0, retained);
|
||||
lang_info("published local %s len: %d\r\n", topic, data_len);
|
||||
}
|
||||
} else {
|
||||
return syntax_error(lr_token, "'local' or 'remote' expected");
|
||||
}
|
||||
}
|
||||
|
||||
else if (is_token(next_token, "subscribe")) {
|
||||
bool retval;
|
||||
|
||||
len_check(2);
|
||||
#ifdef MQTT_CLIENT
|
||||
if (is_token(next_token + 1, "remote")) {
|
||||
if (doit && mqtt_connected) {
|
||||
retval = MQTT_Subscribe(&mqttClient, my_token[next_token + 2], 0);
|
||||
lang_info("subsrcibe remote %s %s\r\n", my_token[next_token + 2], retval ? "success" : "failed");
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (is_token(next_token + 1, "local")) {
|
||||
if (doit && interpreter_status != RE_INIT) {
|
||||
retval = MQTT_local_subscribe(my_token[next_token + 2], 0);
|
||||
lang_info("subsrcibe local %s %s\r\n", my_token[next_token + 2], retval ? "success" : "failed");
|
||||
}
|
||||
} else {
|
||||
return syntax_error(next_token + 1, "'local' or 'remote' expected");
|
||||
}
|
||||
next_token += 3;
|
||||
}
|
||||
|
||||
else if (is_token(next_token, "unsubscribe")) {
|
||||
bool retval;
|
||||
|
||||
len_check(2);
|
||||
#ifdef MQTT_CLIENT
|
||||
if (is_token(next_token + 1, "remote")) {
|
||||
if (doit && mqtt_connected) {
|
||||
retval = MQTT_UnSubscribe(&mqttClient, my_token[next_token + 2]);
|
||||
lang_info("unsubsrcibe remote %s %s\r\n", my_token[next_token + 2], retval ? "success" : "failed");
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (is_token(next_token + 1, "local")) {
|
||||
if (doit && interpreter_status != RE_INIT) {
|
||||
retval = MQTT_local_unsubscribe(my_token[next_token + 2]);
|
||||
lang_info("unsubsrcibe local %s %s\r\n", my_token[next_token + 2], retval ? "success" : "failed");
|
||||
}
|
||||
} else {
|
||||
return syntax_error(next_token + 1, "'local' or 'remote' expected");
|
||||
}
|
||||
|
||||
next_token += 3;
|
||||
}
|
||||
|
||||
else if (is_token(next_token, "if")) {
|
||||
uint32_t if_val;
|
||||
char *if_char;
|
||||
int if_len;
|
||||
Value_Type if_type;
|
||||
|
||||
len_check(3);
|
||||
if ((next_token = parse_expression(next_token + 1, &if_char, &if_len, &if_type)) == -1)
|
||||
return -1;
|
||||
if (syn_chk && !is_token(next_token, "then"))
|
||||
return syntax_error(next_token, "'then' expected");
|
||||
|
||||
if (doit) {
|
||||
if_val = atoi(if_char);
|
||||
lang_info("if %s\r\n", if_val != 0 ? "done" : "not done");
|
||||
}
|
||||
if ((next_token = parse_action(next_token + 1, doit && if_val != 0)) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
else if (is_token(next_token, "settimer")) {
|
||||
len_check(2);
|
||||
uint32_t timer_no = atoi(my_token[next_token + 1]);
|
||||
if (timer_no == 0 || timer_no > MAX_TIMERS)
|
||||
return syntax_error(next_token + 1, "invalid timer number");
|
||||
|
||||
uint32_t timer_val;
|
||||
char *timer_char;
|
||||
int timer_len;
|
||||
Value_Type timer_type;
|
||||
if ((next_token = parse_value(next_token + 2, &timer_char, &timer_len, &timer_type)) == -1)
|
||||
return -1;
|
||||
|
||||
if (doit) {
|
||||
timer_val = atoi(timer_char);
|
||||
lang_info("settimer %d %d\r\n", timer_no, timer_val);
|
||||
timer_no--;
|
||||
|
||||
os_timer_disarm(&timers[timer_no]);
|
||||
if (timer_val != 0) {
|
||||
os_timer_setfn(&timers[timer_no], (os_timer_func_t *) lang_timers_timeout, timer_no);
|
||||
os_timer_arm(&timers[timer_no], timer_val, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (is_token(next_token, "setvar")) {
|
||||
len_check(2);
|
||||
if (my_token[next_token + 1][0] != '$')
|
||||
return syntax_error(next_token + 1, "invalid var identifier");
|
||||
uint32_t var_no = atoi(&(my_token[next_token + 1][1]));
|
||||
if (var_no == 0 || var_no > MAX_VARS)
|
||||
return syntax_error(next_token + 1, "invalid var number");
|
||||
|
||||
char *var_data;
|
||||
int var_len;
|
||||
Value_Type var_type;
|
||||
if ((next_token = parse_expression(next_token + 2, &var_data, &var_len, &var_type)) == -1)
|
||||
return -1;
|
||||
|
||||
if (doit) {
|
||||
lang_info("setvar $%d \r\n", var_no);
|
||||
if (var_len > MAX_VAR_LEN) {
|
||||
os_printf("Var $%d too long '%s'\r\n", var_no, var_data);
|
||||
return next_token;
|
||||
}
|
||||
var_no--;
|
||||
os_memcpy(vars[var_no].data, var_data, var_len);
|
||||
vars[var_no].data_len = var_len;
|
||||
vars[var_no].data_type = var_type;
|
||||
}
|
||||
}
|
||||
#ifdef GPIO
|
||||
else if (is_token(next_token, "gpio_out")) {
|
||||
len_check(2);
|
||||
|
||||
uint32_t gpio_no = atoi(my_token[next_token + 1]);
|
||||
if (gpio_no > 16)
|
||||
return syntax_error(next_token + 1, "invalid gpio number");
|
||||
|
||||
char *gpio_data;
|
||||
int gpio_len;
|
||||
Value_Type gpio_type;
|
||||
if ((next_token = parse_expression(next_token + 2, &gpio_data, &gpio_len, &gpio_type)) == -1)
|
||||
return -1;
|
||||
|
||||
if (doit) {
|
||||
lang_info("gpio_out %d %d\r\n", gpio_no, atoi(gpio_data) != 0);
|
||||
|
||||
if (easygpio_pinMode(gpio_no, EASYGPIO_NOPULL, EASYGPIO_OUTPUT))
|
||||
easygpio_outputSet(gpio_no, atoi(gpio_data) != 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
return syntax_error(next_token, "action command expected");
|
||||
|
||||
}
|
||||
if (is_token(next_token, "endif"))
|
||||
next_token++;
|
||||
return next_token;
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR parse_expression(int next_token, char **data, int *data_len, Value_Type * data_type) {
|
||||
|
||||
if (is_token(next_token, "not")) {
|
||||
len_check(1);
|
||||
lang_debug("expr not\r\n");
|
||||
|
||||
if ((next_token = parse_expression(next_token + 1, data, data_len, data_type)) == -1)
|
||||
return -1;
|
||||
*data = atoi(*data) ? "0" : "1";
|
||||
*data_len = 1;
|
||||
*data_type = STRING_T;
|
||||
}
|
||||
|
||||
else {
|
||||
if ((next_token = parse_value(next_token, data, data_len, data_type)) == -1)
|
||||
return -1;
|
||||
|
||||
// if it is not some kind of binary operation - finished
|
||||
if (!is_token(next_token, "eq")
|
||||
&& !is_token(next_token, "add")
|
||||
&& !is_token(next_token, "sub")
|
||||
&& !is_token(next_token, "mult")
|
||||
&& !is_token(next_token, "div")
|
||||
&& !is_token(next_token, "gt")
|
||||
&& !is_token(next_token, "gte")
|
||||
&& !is_token(next_token, "str_gt")
|
||||
&& !is_token(next_token, "str_gte"))
|
||||
return next_token;
|
||||
|
||||
// okay, it is an operation
|
||||
int op = next_token;
|
||||
|
||||
char *r_data;
|
||||
int r_data_len;
|
||||
Value_Type r_data_type;
|
||||
static char res_str[10];
|
||||
|
||||
// parse second operand
|
||||
if ((next_token = parse_expression(next_token + 1, &r_data, &r_data_len, &r_data_type)) == -1)
|
||||
return -1;
|
||||
//os_printf("l:%s(%d) r:%s(%d)\r\n", *data, *data_len, r_data, r_data_len);
|
||||
|
||||
*data_len = 1;
|
||||
*data_type = STRING_T;
|
||||
if (is_token(op, "eq")) {
|
||||
*data = os_strcmp(*data, r_data) ? "0" : "1";
|
||||
} else if (is_token(op, "gt")) {
|
||||
*data = atoi(*data) > atoi(r_data) ? "1" : "0";
|
||||
} else if (is_token(op, "gte")) {
|
||||
*data = atoi(*data) >= atoi(r_data) ? "1" : "0";
|
||||
} else if (is_token(op, "str_gt")) {
|
||||
*data = os_strcmp(*data, r_data) > 0 ? "1" : "0";
|
||||
} else if (is_token(op, "str_gte")) {
|
||||
*data = os_strcmp(*data, r_data) >= 0 ? "1" : "0";
|
||||
} else if (is_token(op, "add")) {
|
||||
os_sprintf(res_str, "%d", atoi(*data) + atoi(r_data));
|
||||
*data = res_str;
|
||||
*data_len = os_strlen(res_str);
|
||||
} else if (is_token(op, "sub")) {
|
||||
os_sprintf(res_str, "%d", atoi(*data) - atoi(r_data));
|
||||
*data = res_str;
|
||||
*data_len = os_strlen(res_str);
|
||||
} else if (is_token(op, "mult")) {
|
||||
os_sprintf(res_str, "%d", atoi(*data) * atoi(r_data));
|
||||
*data = res_str;
|
||||
*data_len = os_strlen(res_str);
|
||||
} else if (is_token(op, "div")) {
|
||||
os_sprintf(res_str, "%d", atoi(*data) / atoi(r_data));
|
||||
*data = res_str;
|
||||
*data_len = os_strlen(res_str);
|
||||
}
|
||||
}
|
||||
|
||||
return next_token;
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR parse_value(int next_token, char **data, int *data_len, Value_Type * data_type) {
|
||||
if (is_token(next_token, "$this_data")) {
|
||||
lang_debug("val $this_data\r\n");
|
||||
if (!in_topic_statement)
|
||||
return syntax_error(next_token, "undefined $this_data");
|
||||
*data = interpreter_data;
|
||||
*data_len = interpreter_data_len;
|
||||
*data_type = DATA_T;
|
||||
return next_token + 1;
|
||||
}
|
||||
|
||||
else if (is_token(next_token, "$this_topic")) {
|
||||
lang_debug("val $this_topic\r\n");
|
||||
if (!in_topic_statement)
|
||||
return syntax_error(next_token, "undefined $this_topic");
|
||||
*data = interpreter_topic;
|
||||
*data_len = os_strlen(interpreter_topic) + 1;
|
||||
*data_type = STRING_T;
|
||||
return next_token + 1;
|
||||
}
|
||||
#ifdef NTP
|
||||
else if (is_token(next_token, "$timestamp")) {
|
||||
lang_debug("val $timestamp\r\n");
|
||||
if (ntp_sync_done())
|
||||
*data = get_timestr();
|
||||
else
|
||||
*data = "invalid";
|
||||
*data_len = os_strlen(*data) + 1;
|
||||
*data_type = STRING_T;
|
||||
return next_token + 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* else if (my_token[next_token][0] == '\'') {
|
||||
char *p = &(my_token[next_token][1]);
|
||||
int *b = (int*)&val_buf[0];
|
||||
|
||||
*b = atoi(htonl(p));
|
||||
*data = val_buf;
|
||||
*data_len = sizeof(int);
|
||||
return next_token+1;
|
||||
}
|
||||
*/
|
||||
else if (my_token[next_token][0] == '$' && my_token[next_token][1] <= '9') {
|
||||
uint32_t var_no = atoi(&(my_token[next_token][1]));
|
||||
if (var_no == 0 || var_no > MAX_VARS)
|
||||
return syntax_error(next_token + 1, "invalid var number");
|
||||
var_no--;
|
||||
|
||||
*data = vars[var_no].data;
|
||||
*data_len = vars[var_no].data_len;
|
||||
*data_type = vars[var_no].data_type;
|
||||
return next_token + 1;
|
||||
}
|
||||
|
||||
else if (my_token[next_token][0] == '#') {
|
||||
|
||||
lang_debug("val hexbinary\r\n");
|
||||
|
||||
// Convert it in place to binary once during syntax check
|
||||
// Format: Byte 0: '#', Byte 1: len (max 255), Byte 2 to len: binary data, Byte len+1: 0
|
||||
if (syn_chk) {
|
||||
int i, j, len = os_strlen(my_token[next_token]);
|
||||
uint8_t a, *p = &(my_token[next_token][1]);
|
||||
|
||||
if (len < 3)
|
||||
return syntax_error(next_token, "hexbinary too short");
|
||||
if (len > 511)
|
||||
return syntax_error(next_token, "hexbinary too long");
|
||||
for (i = 0, j = 1; i < len - 1; i += 2, j++) {
|
||||
if (p[i] <= '9')
|
||||
a = p[i] - '0';
|
||||
else
|
||||
a = toupper(p[i]) - 'A' + 10;
|
||||
a <<= 4;
|
||||
if (p[i + 1] <= '9')
|
||||
a += p[i + 1] - '0';
|
||||
else
|
||||
a += toupper(p[i + 1]) - 'A' + 10;
|
||||
p[j] = a;
|
||||
}
|
||||
p[j] = '\0';
|
||||
p[0] = (uint8_t) j - 2;
|
||||
}
|
||||
|
||||
*data = &my_token[next_token][2];
|
||||
*data_len = my_token[next_token][1];
|
||||
*data_type = DATA_T;
|
||||
return next_token + 1;
|
||||
}
|
||||
|
||||
else {
|
||||
*data = my_token[next_token];
|
||||
*data_len = os_strlen(my_token[next_token]) + 1;
|
||||
*data_type = STRING_T;
|
||||
return next_token + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR interpreter_syntax_check() {
|
||||
lang_debug("interpreter_syntax_check\r\n");
|
||||
|
||||
os_sprintf(syntax_error_buffer, "Syntax okay");
|
||||
interpreter_status = SYNTAX_CHECK;
|
||||
interpreter_topic = interpreter_data = "";
|
||||
interpreter_data_len = 0;
|
||||
os_bzero(×tamps, sizeof(timestamps));
|
||||
ts_counter = 0;
|
||||
return parse_statement(0);
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR interpreter_config() {
|
||||
int next_token = 0;
|
||||
|
||||
while ((next_token = search_token(next_token, CONFIG)) < max_token) {
|
||||
lang_debug("statement config\r\n");
|
||||
|
||||
len_check(2);
|
||||
do_command("set", my_token[next_token + 1], my_token[next_token + 2]);
|
||||
next_token += 3;
|
||||
}
|
||||
return next_token;
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR interpreter_init() {
|
||||
if (!script_enabled)
|
||||
return -1;
|
||||
|
||||
lang_debug("interpreter_init\r\n");
|
||||
|
||||
interpreter_status = INIT;
|
||||
interpreter_topic = interpreter_data = "";
|
||||
interpreter_data_len = 0;
|
||||
return parse_statement(0);
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR interpreter_init_reconnect(void) {
|
||||
if (!script_enabled)
|
||||
return -1;
|
||||
|
||||
lang_debug("interpreter_init_reconnect\r\n");
|
||||
|
||||
interpreter_status = RE_INIT;
|
||||
interpreter_topic = interpreter_data = "";
|
||||
interpreter_data_len = 0;
|
||||
return parse_statement(0);
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR interpreter_topic_received(const char *topic, const char *data, int data_len, bool local) {
|
||||
if (!script_enabled)
|
||||
return -1;
|
||||
|
||||
lang_debug("interpreter_topic_received\r\n");
|
||||
|
||||
interpreter_status = (local) ? TOPIC_LOCAL : TOPIC_REMOTE;
|
||||
interpreter_topic = (char *)topic;
|
||||
interpreter_data_len = data_len;
|
||||
if ((interpreter_data = (uint8_t *) os_malloc(data_len + 1)) == 0) {
|
||||
os_printf("Out of Memory\r\n");
|
||||
return -1;
|
||||
}
|
||||
os_memcpy(interpreter_data, data, data_len);
|
||||
interpreter_data[data_len] = '\0';
|
||||
|
||||
int retval = parse_statement(0);
|
||||
|
||||
os_free(interpreter_data);
|
||||
return retval;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#include "mqtt_server.h"
|
||||
|
||||
extern MQTT_Client mqttClient;
|
||||
extern bool mqtt_enabled, mqtt_connected;
|
||||
uint8_t syntax_error_buffer[128];
|
||||
|
||||
typedef enum {SYNTAX_CHECK, CONFIG, INIT, RE_INIT, TOPIC_LOCAL, TOPIC_REMOTE, TIMER, CLOCK} Interpreter_Status;
|
||||
typedef enum {STRING_T, DATA_T} Value_Type;
|
||||
|
||||
int text_into_tokens(char *str);
|
||||
void free_tokens(void);
|
||||
bool is_token(int i, char *s);
|
||||
int search_token(int i, char *s);
|
||||
int syntax_error(int i, char *message);
|
||||
|
||||
int parse_statement(int next_token);
|
||||
int parse_event(int next_token, bool *happened);
|
||||
int parse_action(int next_token, bool doit);
|
||||
int parse_value(int next_token, char **data, int *data_len, Value_Type *data_type);
|
||||
|
||||
extern bool script_enabled;
|
||||
int interpreter_syntax_check();
|
||||
int interpreter_config();
|
||||
int interpreter_init();
|
||||
int interpreter_init_reconnect(void);
|
||||
int interpreter_topic_received(const char *topic, const char *data, int data_len, bool local);
|
||||
|
||||
void init_timestamps(uint8_t *curr_time);
|
||||
void check_timestamps(uint8_t *curr_time);
|
|
@ -25,52 +25,50 @@
|
|||
* Parameters : none
|
||||
* Returns : rf cal sector
|
||||
*******************************************************************************/
|
||||
uint32 ICACHE_FLASH_ATTR __attribute__((weak))
|
||||
user_rf_cal_sector_set(void)
|
||||
{
|
||||
enum flash_size_map size_map = system_get_flash_size_map();
|
||||
uint32 rf_cal_sec = 0;
|
||||
uint32 ICACHE_FLASH_ATTR __attribute__ ((weak))
|
||||
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) {
|
||||
switch (size_map) {
|
||||
case FLASH_SIZE_4M_MAP_256_256:
|
||||
rf_cal_sec = 128 - 5;
|
||||
break;
|
||||
rf_cal_sec = 128 - 5;
|
||||
break;
|
||||
|
||||
case FLASH_SIZE_8M_MAP_512_512:
|
||||
rf_cal_sec = 256 - 5;
|
||||
break;
|
||||
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;
|
||||
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;
|
||||
rf_cal_sec = 1024 - 5;
|
||||
break;
|
||||
|
||||
default:
|
||||
rf_cal_sec = 0;
|
||||
break;
|
||||
}
|
||||
rf_cal_sec = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return rf_cal_sec;
|
||||
return rf_cal_sec;
|
||||
}
|
||||
|
||||
void __attribute__((weak))
|
||||
user_rf_pre_init(void)
|
||||
{
|
||||
// Warning: IF YOU DON'T KNOW WHAT YOU ARE DOING, DON'T TOUCH THESE CODE
|
||||
void __attribute__ ((weak))
|
||||
user_rf_pre_init(void) {
|
||||
// Warning: IF YOU DON'T KNOW WHAT YOU ARE DOING, DON'T TOUCH THESE CODE
|
||||
|
||||
// Control RF_CAL by esp_init_data_default.bin(0~127byte) 108 byte when wakeup
|
||||
// Will low current
|
||||
// system_phy_set_rfoption(0);
|
||||
// Control RF_CAL by esp_init_data_default.bin(0~127byte) 108 byte when wakeup
|
||||
// Will low current
|
||||
// system_phy_set_rfoption(0);
|
||||
|
||||
// Process RF_CAL when wakeup.
|
||||
// Will high current
|
||||
system_phy_set_rfoption(1);
|
||||
// Process RF_CAL when wakeup.
|
||||
// Will high current
|
||||
system_phy_set_rfoption(1);
|
||||
|
||||
// Set Wi-Fi Tx Power, Unit: 0.25dBm, Range: [0, 82]
|
||||
system_phy_set_max_tpw(82);
|
||||
// Set Wi-Fi Tx Power, Unit: 0.25dBm, Range: [0, 82]
|
||||
system_phy_set_max_tpw(82);
|
||||
}
|
||||
|
|
166
user/ringbuf.c
166
user/ringbuf.c
|
@ -35,56 +35,45 @@
|
|||
* intended.
|
||||
*/
|
||||
|
||||
struct ringbuf_t
|
||||
{
|
||||
struct ringbuf_t {
|
||||
uint8_t *buf;
|
||||
uint8_t *head, *tail;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
ringbuf_t
|
||||
ringbuf_new(size_t capacity)
|
||||
{
|
||||
ringbuf_t rb = (ringbuf_t)os_malloc(sizeof(struct ringbuf_t));
|
||||
ringbuf_t ringbuf_new(size_t capacity) {
|
||||
ringbuf_t rb = (ringbuf_t) os_malloc(sizeof(struct ringbuf_t));
|
||||
if (rb) {
|
||||
|
||||
/* One byte is used for detecting the full condition. */
|
||||
rb->size = capacity + 1;
|
||||
rb->buf = (uint8_t *)os_malloc(rb->size);
|
||||
if (rb->buf)
|
||||
ringbuf_reset(rb);
|
||||
else {
|
||||
os_free(rb);
|
||||
return 0;
|
||||
}
|
||||
/* One byte is used for detecting the full condition. */
|
||||
rb->size = capacity + 1;
|
||||
rb->buf = (uint8_t *) os_malloc(rb->size);
|
||||
if (rb->buf)
|
||||
ringbuf_reset(rb);
|
||||
else {
|
||||
os_free(rb);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return rb;
|
||||
}
|
||||
|
||||
size_t
|
||||
ringbuf_buffer_size(const struct ringbuf_t *rb)
|
||||
{
|
||||
size_t ringbuf_buffer_size(const struct ringbuf_t * rb) {
|
||||
return rb->size;
|
||||
}
|
||||
|
||||
void
|
||||
ringbuf_reset(ringbuf_t rb)
|
||||
{
|
||||
void ringbuf_reset(ringbuf_t rb) {
|
||||
rb->head = rb->tail = rb->buf;
|
||||
}
|
||||
|
||||
void
|
||||
ringbuf_free(ringbuf_t *rb)
|
||||
{
|
||||
void ringbuf_free(ringbuf_t * rb) {
|
||||
assert(rb && *rb);
|
||||
os_free((*rb)->buf);
|
||||
os_free(*rb);
|
||||
*rb = 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
ringbuf_capacity(const struct ringbuf_t *rb)
|
||||
{
|
||||
size_t ringbuf_capacity(const struct ringbuf_t *rb) {
|
||||
return ringbuf_buffer_size(rb) - 1;
|
||||
}
|
||||
|
||||
|
@ -93,48 +82,34 @@ ringbuf_capacity(const struct ringbuf_t *rb)
|
|||
* contiguous buffer. You shouldn't normally need to use this function
|
||||
* unless you're writing a new ringbuf_* function.
|
||||
*/
|
||||
static const uint8_t *
|
||||
ringbuf_end(const struct ringbuf_t *rb)
|
||||
{
|
||||
static const uint8_t *ringbuf_end(const struct ringbuf_t *rb) {
|
||||
return rb->buf + ringbuf_buffer_size(rb);
|
||||
}
|
||||
|
||||
size_t
|
||||
ringbuf_bytes_free(const struct ringbuf_t *rb)
|
||||
{
|
||||
size_t ringbuf_bytes_free(const struct ringbuf_t * rb) {
|
||||
if (rb->head >= rb->tail)
|
||||
return ringbuf_capacity(rb) - (rb->head - rb->tail);
|
||||
return ringbuf_capacity(rb) - (rb->head - rb->tail);
|
||||
else
|
||||
return rb->tail - rb->head - 1;
|
||||
return rb->tail - rb->head - 1;
|
||||
}
|
||||
|
||||
size_t
|
||||
ringbuf_bytes_used(const struct ringbuf_t *rb)
|
||||
{
|
||||
size_t ringbuf_bytes_used(const struct ringbuf_t * rb) {
|
||||
return ringbuf_capacity(rb) - ringbuf_bytes_free(rb);
|
||||
}
|
||||
|
||||
int
|
||||
ringbuf_is_full(const struct ringbuf_t *rb)
|
||||
{
|
||||
int ringbuf_is_full(const struct ringbuf_t *rb) {
|
||||
return ringbuf_bytes_free(rb) == 0;
|
||||
}
|
||||
|
||||
int
|
||||
ringbuf_is_empty(const struct ringbuf_t *rb)
|
||||
{
|
||||
int ringbuf_is_empty(const struct ringbuf_t *rb) {
|
||||
return ringbuf_bytes_free(rb) == ringbuf_capacity(rb);
|
||||
}
|
||||
|
||||
const void *
|
||||
ringbuf_tail(const struct ringbuf_t *rb)
|
||||
{
|
||||
const void *ringbuf_tail(const struct ringbuf_t *rb) {
|
||||
return rb->tail;
|
||||
}
|
||||
|
||||
const void *
|
||||
ringbuf_head(const struct ringbuf_t *rb)
|
||||
{
|
||||
const void *ringbuf_head(const struct ringbuf_t *rb) {
|
||||
return rb->head;
|
||||
}
|
||||
|
||||
|
@ -143,9 +118,7 @@ ringbuf_head(const struct ringbuf_t *rb)
|
|||
* contiguous buffer, return the a pointer to the next logical
|
||||
* location in the ring buffer.
|
||||
*/
|
||||
static uint8_t *
|
||||
ringbuf_nextp(ringbuf_t rb, const uint8_t *p)
|
||||
{
|
||||
static uint8_t *ringbuf_nextp(ringbuf_t rb, const uint8_t * p) {
|
||||
/*
|
||||
* The assert guarantees the expression (++p - rb->buf) is
|
||||
* non-negative; therefore, the modulus operation is safe and
|
||||
|
@ -155,96 +128,89 @@ ringbuf_nextp(ringbuf_t rb, const uint8_t *p)
|
|||
return rb->buf + ((++p - rb->buf) % ringbuf_buffer_size(rb));
|
||||
}
|
||||
|
||||
void *
|
||||
ringbuf_memcpy_into(ringbuf_t dst, const void *src, size_t count)
|
||||
{
|
||||
void *ringbuf_memcpy_into(ringbuf_t dst, const void *src, size_t count) {
|
||||
const uint8_t *u8src = src;
|
||||
const uint8_t *bufend = ringbuf_end(dst);
|
||||
int overflow = count > ringbuf_bytes_free(dst);
|
||||
size_t nread = 0;
|
||||
|
||||
while (nread != count) {
|
||||
/* don't copy beyond the end of the buffer */
|
||||
assert(bufend > dst->head);
|
||||
size_t n = MIN(bufend - dst->head, count - nread);
|
||||
os_memcpy(dst->head, u8src + nread, n);
|
||||
dst->head += n;
|
||||
nread += n;
|
||||
/* don't copy beyond the end of the buffer */
|
||||
assert(bufend > dst->head);
|
||||
size_t n = MIN(bufend - dst->head, count - nread);
|
||||
os_memcpy(dst->head, u8src + nread, n);
|
||||
dst->head += n;
|
||||
nread += n;
|
||||
|
||||
/* wrap? */
|
||||
if (dst->head == bufend)
|
||||
dst->head = dst->buf;
|
||||
/* wrap? */
|
||||
if (dst->head == bufend)
|
||||
dst->head = dst->buf;
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
dst->tail = ringbuf_nextp(dst, dst->head);
|
||||
assert(ringbuf_is_full(dst));
|
||||
dst->tail = ringbuf_nextp(dst, dst->head);
|
||||
assert(ringbuf_is_full(dst));
|
||||
}
|
||||
|
||||
return dst->head;
|
||||
}
|
||||
|
||||
void *
|
||||
ringbuf_memcpy_from(void *dst, ringbuf_t src, size_t count)
|
||||
{
|
||||
void *ringbuf_memcpy_from(void *dst, ringbuf_t src, size_t count) {
|
||||
size_t bytes_used = ringbuf_bytes_used(src);
|
||||
if (count > bytes_used)
|
||||
return 0;
|
||||
return 0;
|
||||
|
||||
uint8_t *u8dst = dst;
|
||||
const uint8_t *bufend = ringbuf_end(src);
|
||||
size_t nwritten = 0;
|
||||
while (nwritten != count) {
|
||||
assert(bufend > src->tail);
|
||||
size_t n = MIN(bufend - src->tail, count - nwritten);
|
||||
os_memcpy(u8dst + nwritten, src->tail, n);
|
||||
src->tail += n;
|
||||
nwritten += n;
|
||||
assert(bufend > src->tail);
|
||||
size_t n = MIN(bufend - src->tail, count - nwritten);
|
||||
os_memcpy(u8dst + nwritten, src->tail, n);
|
||||
src->tail += n;
|
||||
nwritten += n;
|
||||
|
||||
/* wrap ? */
|
||||
if (src->tail == bufend)
|
||||
src->tail = src->buf;
|
||||
/* wrap ? */
|
||||
if (src->tail == bufend)
|
||||
src->tail = src->buf;
|
||||
}
|
||||
|
||||
assert(count + ringbuf_bytes_used(src) == bytes_used);
|
||||
return src->tail;
|
||||
}
|
||||
|
||||
void *
|
||||
ringbuf_copy(ringbuf_t dst, ringbuf_t src, size_t count)
|
||||
{
|
||||
void *ringbuf_copy(ringbuf_t dst, ringbuf_t src, size_t count) {
|
||||
size_t src_bytes_used = ringbuf_bytes_used(src);
|
||||
if (count > src_bytes_used)
|
||||
return 0;
|
||||
return 0;
|
||||
int overflow = count > ringbuf_bytes_free(dst);
|
||||
|
||||
const uint8_t *src_bufend = ringbuf_end(src);
|
||||
const uint8_t *dst_bufend = ringbuf_end(dst);
|
||||
size_t ncopied = 0;
|
||||
while (ncopied != count) {
|
||||
assert(src_bufend > src->tail);
|
||||
size_t nsrc = MIN(src_bufend - src->tail, count - ncopied);
|
||||
assert(dst_bufend > dst->head);
|
||||
size_t n = MIN(dst_bufend - dst->head, nsrc);
|
||||
os_memcpy(dst->head, src->tail, n);
|
||||
src->tail += n;
|
||||
dst->head += n;
|
||||
ncopied += n;
|
||||
assert(src_bufend > src->tail);
|
||||
size_t nsrc = MIN(src_bufend - src->tail, count - ncopied);
|
||||
assert(dst_bufend > dst->head);
|
||||
size_t n = MIN(dst_bufend - dst->head, nsrc);
|
||||
os_memcpy(dst->head, src->tail, n);
|
||||
src->tail += n;
|
||||
dst->head += n;
|
||||
ncopied += n;
|
||||
|
||||
/* wrap ? */
|
||||
if (src->tail == src_bufend)
|
||||
src->tail = src->buf;
|
||||
if (dst->head == dst_bufend)
|
||||
dst->head = dst->buf;
|
||||
/* wrap ? */
|
||||
if (src->tail == src_bufend)
|
||||
src->tail = src->buf;
|
||||
if (dst->head == dst_bufend)
|
||||
dst->head = dst->buf;
|
||||
}
|
||||
|
||||
assert(count + ringbuf_bytes_used(src) == src_bytes_used);
|
||||
|
||||
|
||||
if (overflow) {
|
||||
dst->tail = ringbuf_nextp(dst, dst->head);
|
||||
assert(ringbuf_is_full(dst));
|
||||
dst->tail = ringbuf_nextp(dst, dst->head);
|
||||
assert(ringbuf_is_full(dst));
|
||||
}
|
||||
|
||||
return dst->head;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,31 +2,29 @@
|
|||
#include "osapi.h"
|
||||
|
||||
typedef union _timer {
|
||||
uint32_t time_s[2];
|
||||
uint64_t time_l;
|
||||
uint32_t time_s[2];
|
||||
uint64_t time_l;
|
||||
} long_time_t;
|
||||
|
||||
static long_time_t time;
|
||||
static uint32_t old;
|
||||
|
||||
uint64_t ICACHE_FLASH_ATTR get_long_systime() {
|
||||
uint32_t now = system_get_time();
|
||||
if (now < old) {
|
||||
time.time_s[1]++;
|
||||
}
|
||||
old = now;
|
||||
time.time_s[0] = now;
|
||||
return time.time_l;
|
||||
uint32_t now = system_get_time();
|
||||
if (now < old) {
|
||||
time.time_s[1]++;
|
||||
}
|
||||
old = now;
|
||||
time.time_s[0] = now;
|
||||
return time.time_l;
|
||||
}
|
||||
|
||||
uint64_t ICACHE_FLASH_ATTR get_low_systime() {
|
||||
get_long_systime();
|
||||
return time.time_s[0];
|
||||
get_long_systime();
|
||||
return time.time_s[0];
|
||||
}
|
||||
|
||||
void init_long_systime() {
|
||||
old = system_get_time();
|
||||
time.time_l = (uint64_t)old;
|
||||
old = system_get_time();
|
||||
time.time_l = (uint64_t) old;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef _USER_CONFIG_
|
||||
#define _USER_CONFIG_
|
||||
|
||||
typedef enum {SIG_DO_NOTHING=0, SIG_START_SERVER=1, SIG_SEND_DATA, SIG_UART0, SIG_CONSOLE_RX, SIG_CONSOLE_TX, SIG_GPIO_INT} USER_SIGNALS;
|
||||
typedef enum {SIG_DO_NOTHING=0, SIG_START_SERVER=1, SIG_UART0, SIG_SCRIPT_LOADED, SIG_CONSOLE_TX_RAW, SIG_CONSOLE_TX, SIG_CONSOLE_RX} USER_SIGNALS;
|
||||
|
||||
#define WIFI_SSID "ssid"
|
||||
#define WIFI_PASSWORD "password"
|
||||
|
@ -26,6 +26,20 @@ typedef enum {SIG_DO_NOTHING=0, SIG_START_SERVER=1, SIG_SEND_DATA, SIG_UART0, SI
|
|||
|
||||
#define MQTT_ID "ESPBroker"
|
||||
|
||||
//
|
||||
// Define this if you want to have script support.
|
||||
//
|
||||
#define SCRIPTED 1
|
||||
// Define this if you want to have GPIO OUT support in scripts.
|
||||
#define GPIO 1
|
||||
// Some params for scripts
|
||||
|
||||
#define MAX_SCRIPT_SIZE 0x1000
|
||||
#define MAX_TIMERS 4
|
||||
#define MAX_VARS 6
|
||||
#define MAX_VAR_LEN 64
|
||||
#define MAX_TIMESTAMPS 6
|
||||
|
||||
//
|
||||
// Define this if you want to have NTP support.
|
||||
//
|
||||
|
@ -44,7 +58,7 @@ typedef enum {SIG_DO_NOTHING=0, SIG_START_SERVER=1, SIG_SEND_DATA, SIG_UART0, SI
|
|||
|
||||
//
|
||||
// Define this if you want to have access to the config console via TCP.
|
||||
// Ohterwise only local access via serial is possible
|
||||
// Otherwise only local access via serial is possible
|
||||
//
|
||||
#define REMOTE_CONFIG 1
|
||||
#define CONSOLE_SERVER_PORT 7777
|
||||
|
|
1496
user/user_main.c
1496
user/user_main.c
Plik diff jest za duży
Load Diff
|
@ -2,8 +2,7 @@
|
|||
#include "mqtt_server.h"
|
||||
#include "user_config.h"
|
||||
|
||||
void ICACHE_FLASH_ATTR user_init()
|
||||
{
|
||||
void ICACHE_FLASH_ATTR user_init() {
|
||||
struct station_config stationConf;
|
||||
|
||||
// Initialize the UART
|
||||
|
|
Ładowanie…
Reference in New Issue