added scripting to the master branch

pull/16/head
Martin Ger 2017-07-21 16:35:45 +02:00
rodzic 7c15a93399
commit 1fc6ae24c6
30 zmienionych plików z 4781 dodań i 3335 usunięć

Wyświetl plik

@ -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
Wyświetl plik

@ -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.

343
easygpio/easygpio.c 100644
Wyświetl plik

@ -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);
}
}

114
easygpio/easygpio.h 100644
Wyświetl plik

@ -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.

Plik diff jest za duży Load Diff

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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;
}
}
}
}

Plik diff jest za duży Load Diff

Wyświetl plik

@ -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;
}
}
}
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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 */
}

Wyświetl plik

@ -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
Wyświetl plik

@ -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));
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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

49
user/demo_script 100644
Wyświetl plik

@ -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"

800
user/lang.c 100644
Wyświetl plik

@ -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(&timestamps, 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;
}

29
user/lang.h 100644
Wyświetl plik

@ -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);

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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

Plik diff jest za duży Load Diff

Wyświetl plik

@ -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