diff --git a/Makefile b/Makefile index 414366a..a90e5d4 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ ESPPORT ?= /dev/ttyUSB0 TARGET = app # which modules (subdirectories) of the project to include in compiling -MODULES = driver user mqtt ntp easygpio pwm httpclient +MODULES = driver user mqtt ntp easygpio pwm httpclient adc #EXTRA_INCDIR = $(BUILD_AREA)/esp-open-sdk/esp-open-lwip/include include EXTRA_INCDIR = include diff --git a/README.md b/README.md index dd84019..0954067 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # esp_uMQTT_broker An MQTT Broker/Client with scripting support on the ESP8266 -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. Also it can parse JSON structures, write to local GPIO pins, react on timers and GPIO interrupts, drive GPIO pins with PWM, and do basic HTTP GET requests. +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. Also it can parse JSON structures, send basic HTTP GET requests and do basic I/O: i.e. read and write to local GPIO pins, react on timers and GPIO interrupts, drive GPIO pins with PWM, and read the ADC. Find a video that explains the ideas and the architecture of the project at: https://www.youtube.com/watch?v=0K9q4IuB_oA @@ -227,6 +227,11 @@ If an *MqttAuthCallback* function is registered with MQTT_server_onAuth(), it is The *MqttConnectCallback* function does a similar check for the connection, but it is called right after the connect request before any internal status is allocated. This is done in order to reject requests from unautorized clients in an early stage. +# Limitations on the number of clients +To adjust memory consumption of one MQTT connection and thus the max number of concurrent connections you can redefine MQTT_BUF_SIZE and QUEUE_BUFFER_SIZE in "user_config.h". MQTT_BUF_SIZE is the max. size of pending inbound messages for one connection (and thus also the max. size of a single MQTT message) and QUEUE_BUFFER_SIZE is the max. size of all pending outbound messages for one connection. Currently these parameters are set to 1024 resp. 2048 bytes, resulting in a memory consumption of about 4 KB per connection and a max number of connections of about 8-9 (depending on the memory usage of the rest of the program). When you reduce buffer sizes, e.g. to 512 and 1024 bytes, a single connection requires only about 2.5 KB resulting in up to 13 possible concurrent MQTT connections. In any case you have to increase the number of TCP connections (default 5) first by calling "espconn_tcp_set_max_con(n)" with n, the max. number of concurrent TCP connections, less or equal to 15. + +Also there is a hard limitation on the number of STAs connected to the SoftAP, which is 8. I.e. when using the esp_uMQTT_broker only with clients via the SoftAP interface, even with reduced memory consumtion, the limit of different client nodes is still 8, as it is imposed by the binary WiFi driver. Only when used via the STA interface and an external AP you can connect more than 8 MQTT clients. + # Thanks - pfalcon for esp_open_sdk (https://github.com/martin-ger/esp-open-sdk) - tuanpmt for esp_mqtt (https://github.com/tuanpmt/esp_mqtt ) diff --git a/SCRIPTING.md b/SCRIPTING.md index d45a26d..c0d8768 100644 --- a/SCRIPTING.md +++ b/SCRIPTING.md @@ -1,4 +1,5 @@ -# Scripting Language +Scripting Language +================== The scripting language of the esp_uMQTT_broker is stricly event based. It mainly consists of "on _event_ do _action_" clauses. An event can be: - the reception of an MQTT item, @@ -56,7 +57,7 @@ In general, scripts conform to the following BNF: := '=' | '>' | gte | str_ge | str_gte | '+' | '-' | '*' | '|' | div := | | # | $[any ASCII]* | @ | - gpio_in() | $this_item | $this_data | $this_gpio | + gpio_in() | $adc | $this_item | $this_data | $this_gpio | $this_http_code | $this_http_body | $timestamp | $weekday := "[any ASCII]*" | [any ASCII]* @@ -223,13 +224,14 @@ gpio_in() Reads the current boolean input value of the given GPIO pin. This pin has to be defined as input before using the "gpio_pinmode" action. ``` -$this_item | $this_data | $this_gpio | $timestamp | $weekday | $this_http_body | $this_http_code +$adc | $this_item | $this_data | $this_gpio | $timestamp | $weekday | $this_http_body | $this_http_code ``` Special variables: -$this_topic and $this_data are only defined in "on topic" clauses and contain the current topic and its data. -$this_gpio contains the state of the GPIO in an "on gpio_interrupt" clause. -$timestamp contains the current time of day in "hh:mm:ss" format. If no NTP sync happened the time will be reported as "99:99:99". $weekday returns the day of week as three letters ("Mon","Tue",...). -$this_http_body and $this_http_code are only defined inside the "on http_response" clause and contain the body of an HTTP response and the HTTP return code. +- $adc gives you the current value of the ADC (analog to digital input pin) +- $this_topic and $this_data are only defined in "on topic" clauses and contain the current topic and its data. +- $this_gpio contains the state of the GPIO in an "on gpio_interrupt" clause. +- $timestamp contains the current time of day in "hh:mm:ss" format. If no NTP sync happened the time will be reported as "99:99:99". $weekday returns the day of week as three letters ("Mon","Tue",...). +- $this_http_body and $this_http_code are only defined inside the "on http_response" clause and contain the body of an HTTP response and the HTTP return code. # Operators Operators are used to combine values and expressions. @@ -242,7 +244,7 @@ These operators result in boolean values and are typically used in comparisons i ``` '+' | '-' | '*' | div ``` -These operators are the arithmetical operations. CAUTION: arithmetical preceedence does not (yet) apply automatically, all expressions are evaluated from left to right. I.e. "2+3*4" evaluates to 20 instead of 14. However, the preceedence can be fully controlled by brackets. Write "2+(3*4)" instead. +These operators are the arithmetical operations. CAUTION: arithmetical preceedence does not (yet) apply automatically, all expressions are evaluated from left to right. I.e. "2+3\*4" evaluates to 20 instead of 14. However, the preceedence can be fully controlled by brackets. Write "2+(3\*4)" instead. ``` '|' @@ -376,3 +378,4 @@ do publish local $command_topic "off" ``` + diff --git a/adc/adc.c b/adc/adc.c new file mode 100644 index 0000000..a70a3f3 --- /dev/null +++ b/adc/adc.c @@ -0,0 +1,75 @@ +#include "ets_sys.h" +#include "osapi.h" + +#include "adc.h" + +#define i2c_bbpll 0x67 +#define i2c_bbpll_en_audio_clock_out 4 +#define i2c_bbpll_en_audio_clock_out_msb 7 +#define i2c_bbpll_en_audio_clock_out_lsb 7 +#define i2c_bbpll_hostid 4 +#define i2c_saradc 0x6C +#define i2c_saradc_hostid 2 + +#define i2c_saradc_en_test 0 +#define i2c_saradc_en_test_msb 5 +#define i2c_saradc_en_test_lsb 5 + +#define i2c_writeReg_Mask(block, host_id, reg_add, Msb, Lsb, indata) \ + rom_i2c_writeReg_Mask(block, host_id, reg_add, Msb, Lsb, indata) + +#define i2c_readReg_Mask(block, host_id, reg_add, Msb, Lsb) \ + rom_i2c_readReg_Mask_(block, host_id, reg_add, Msb, Lsb) + +#define i2c_writeReg_Mask_def(block, reg_add, indata) \ + i2c_writeReg_Mask(block, block##_hostid, reg_add, reg_add##_msb, reg_add##_lsb, indata) + +#define i2c_readReg_Mask_def(block, reg_add) \ + i2c_readReg_Mask(block, block##_hostid, reg_add, reg_add##_msb, reg_add##_lsb) + +#ifdef ADC_DEBUG +#define ADC_DBG os_printf +#else +#define ADC_DBG +#endif + +uint16 ICACHE_FLASH_ATTR adc_read(void) +{ + uint8 i; + uint16 sar_dout, tout, sardata[8]; + + i2c_writeReg_Mask_def(i2c_saradc, i2c_saradc_en_test, 1); //select test mux + + //PWDET_CAL_EN=0, PKDET_CAL_EN=0 + SET_PERI_REG_MASK(0x60000D5C, 0x200000); + + while (GET_PERI_REG_BITS(0x60000D50, 26, 24) > 0); //wait r_state == 0 + + sar_dout = 0; + CLEAR_PERI_REG_MASK(0x60000D50, 0x02); //force_en=0 + SET_PERI_REG_MASK(0x60000D50, 0x02); //force_en=1 + + os_delay_us(2); + + while (GET_PERI_REG_BITS(0x60000D50, 26, 24) > 0); //wait r_state == 0 + + read_sar_dout(sardata); + + for (i = 0; i < 8; i++) { + sar_dout += sardata[i]; + ADC_DBG("%d, ", sardata[i]); + } + + tout = (sar_dout + 8) >> 4; //tout is 10 bits fraction + + i2c_writeReg_Mask_def(i2c_saradc, i2c_saradc_en_test, 1); //select test mux + + while (GET_PERI_REG_BITS(0x60000D50, 26, 24) > 0); //wait r_state == 0 + + CLEAR_PERI_REG_MASK(0x60000D5C, 0x200000); + SET_PERI_REG_MASK(0x60000D60, 0x1); //force_en=1 + CLEAR_PERI_REG_MASK(0x60000D60, 0x1); //force_en=1 + + return tout; //tout is 10 bits fraction +} + diff --git a/adc/adc.h b/adc/adc.h new file mode 100644 index 0000000..36265f4 --- /dev/null +++ b/adc/adc.h @@ -0,0 +1,6 @@ +#ifndef __ADC_H__ +#define __ADC_H__ + +uint16 adc_read(void); + +#endif diff --git a/firmware/0x00000.bin b/firmware/0x00000.bin index 082c65c..e7c19a2 100644 Binary files a/firmware/0x00000.bin and b/firmware/0x00000.bin differ diff --git a/firmware/0x10000.bin b/firmware/0x10000.bin index 0075144..e3e0214 100644 Binary files a/firmware/0x10000.bin and b/firmware/0x10000.bin differ diff --git a/firmware/libmqtt.a b/firmware/libmqtt.a index 2cdb653..0fd4e30 100644 Binary files a/firmware/libmqtt.a and b/firmware/libmqtt.a differ diff --git a/firmware/sha1sums b/firmware/sha1sums index b5ebb9a..3b6c114 100644 --- a/firmware/sha1sums +++ b/firmware/sha1sums @@ -1,2 +1,2 @@ -8c0436ab218b8b4165f57501775797e09e818ae0 0x00000.bin -b02f819746fe72bb7692e9c6bb2c37d175f58b24 0x10000.bin +25afd95372b46210eedc30515d63589985c2d281 0x00000.bin +3ef293f39d57c0379b8c9ec4795fb38360d2b214 0x10000.bin diff --git a/mqtt/mqtt.c b/mqtt/mqtt.c index 44ab360..aa5154f 100644 --- a/mqtt/mqtt.c +++ b/mqtt/mqtt.c @@ -278,7 +278,7 @@ void ICACHE_FLASH_ATTR mqtt_tcpclient_recv(void *arg, char *pdata, unsigned shor struct espconn *pCon = (struct espconn *)arg; MQTT_Client *client = (MQTT_Client *) pCon->reverse; - client->keepAliveTick = 0; + //client->keepAliveTick = 0; READPACKET: MQTT_INFO("TCP: data received %d bytes\r\n", len); // MQTT_INFO("STATE: %d\r\n", client->connState); @@ -722,6 +722,7 @@ void ICACHE_FLASH_ATTR MQTT_Task(os_event_t * e) { client->sendTimeout = MQTT_SEND_TIMOUT; MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); + client->keepAliveTick = 0; if (client->security) { #ifdef MQTT_SSL_ENABLE espconn_secure_send(client->pCon, dataBuffer, dataLen); diff --git a/mqtt/mqtt_server.c b/mqtt/mqtt_server.c index 0000f26..dbe07f5 100644 --- a/mqtt/mqtt_server.c +++ b/mqtt/mqtt_server.c @@ -22,10 +22,6 @@ return _v; #define os_malloc(x) my_os_zalloc(x, __LINE__) */ -#ifndef QUEUE_BUFFER_SIZE -#define QUEUE_BUFFER_SIZE 2048 -#endif - #define MAX_SUBS_PER_REQ 16 #define MQTT_SERVER_TASK_PRIO 1 @@ -125,6 +121,8 @@ bool ICACHE_FLASH_ATTR activate_next_client() { return true; } +static uint8_t shared_out_buffer[MQTT_BUF_SIZE]; + bool ICACHE_FLASH_ATTR MQTT_InitClientCon(MQTT_ClientCon * mqttClientCon) { uint32_t temp; MQTT_INFO("MQTT:InitClientCon\r\n"); @@ -138,7 +136,8 @@ bool ICACHE_FLASH_ATTR MQTT_InitClientCon(MQTT_ClientCon * mqttClientCon) { mqttClientCon->mqtt_state.in_buffer = (uint8_t *) os_zalloc(MQTT_BUF_SIZE); mqttClientCon->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; - mqttClientCon->mqtt_state.out_buffer = (uint8_t *) os_zalloc(MQTT_BUF_SIZE); + // mqttClientCon->mqtt_state.out_buffer = (uint8_t *) os_zalloc(MQTT_BUF_SIZE); + mqttClientCon->mqtt_state.out_buffer = shared_out_buffer; mqttClientCon->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; mqttClientCon->mqtt_state.connect_info = &mqttClientCon->connect_info; @@ -182,18 +181,20 @@ bool ICACHE_FLASH_ATTR MQTT_DeleteClientCon(MQTT_ClientCon * mqttClientCon) { mqttClientCon->mqtt_state.in_buffer = NULL; } - if (mqttClientCon->mqtt_state.out_buffer != NULL) { - os_free(mqttClientCon->mqtt_state.out_buffer); - mqttClientCon->mqtt_state.out_buffer = NULL; - } +/* We use one static buffer for all connections +// if (mqttClientCon->mqtt_state.out_buffer != NULL) { +// os_free(mqttClientCon->mqtt_state.out_buffer); +// mqttClientCon->mqtt_state.out_buffer = NULL; +// } if (mqttClientCon->mqtt_state.outbound_message != NULL) { if (mqttClientCon->mqtt_state.outbound_message->data != NULL) { - os_free(mqttClientCon->mqtt_state.outbound_message->data); + // Don't think, this is has ever been allocated separately + // os_free(mqttClientCon->mqtt_state.outbound_message->data); mqttClientCon->mqtt_state.outbound_message->data = NULL; } } - +*/ if (mqttClientCon->mqtt_state.mqtt_connection.buffer != NULL) { // Already freed but not NULL mqttClientCon->mqtt_state.mqtt_connection.buffer = NULL; diff --git a/scripts/script.adc b/scripts/script.adc new file mode 100644 index 0000000..5c8b28e --- /dev/null +++ b/scripts/script.adc @@ -0,0 +1,15 @@ +% Config params, overwrite any previous settings from the commandline +% Nothing here + +% Now the events, checked whenever something happens + +on init +do + settimer 1 1000 + +on timer 1 +do + println "ADC value: " | $adc + settimer 1 1000 + + diff --git a/user/lang.c b/user/lang.c index d60d504..7702879 100644 --- a/user/lang.c +++ b/user/lang.c @@ -16,6 +16,10 @@ #endif #endif +#ifdef ADC +#include "adc.h" +#endif + #ifdef JSON_PARSE #include "json_path.h" #endif @@ -1340,6 +1344,18 @@ int ICACHE_FLASH_ATTR parse_value(int next_token, char **data, int *data_len, Va *data_type = STRING_T; return next_token + 1; } +#endif +#ifdef ADC + else if (is_token(next_token, "$adc")) { + static char adcbuf[8]; + lang_debug("val $adc\r\n"); + + os_sprintf(adcbuf, "%d", adc_read()); + *data = adcbuf; + *data_len = os_strlen(*data); + *data_type = STRING_T; + return next_token + 1; + } #endif else if (my_token[next_token][0] == '$' && my_token[next_token][1] != '\0') { lang_debug("val var %s\r\n", &(my_token[next_token][1])); diff --git a/user/user_config.h b/user/user_config.h index 534a329..df132a1 100644 --- a/user/user_config.h +++ b/user/user_config.h @@ -20,7 +20,14 @@ #define MQTT_CLIENT 1 //#define MQTT_SSL_ENABLE 1 +// +// Change this to adjust memory consuption of one MQTT connection +// MQTT_BUF_SIZE is the max. size of pending inbound messages for one connection +// QUEUE_BUFFER_SIZE is the max. size of all pending outbound messages for one connection +// #define MQTT_BUF_SIZE 1024 +#define QUEUE_BUFFER_SIZE 2048 + #define MQTT_KEEPALIVE 120 /*seconds*/ #define MQTT_RECONNECT_TIMEOUT 5 /*seconds*/ //#define PROTOCOL_NAMEv31 /*MQTT version 3.1 compatible with Mosquitto v0.15*/ @@ -47,21 +54,18 @@ // // Define this if you want to have GPIO OUT support in scripts. +// Define GPIO_PWM if you want to have additionally GPIO PWM support in scripts. // #define GPIO 1 - -// -// Define this if you want to have additionally GPIO PWM support in scripts. -// #define GPIO_PWM 1 // -// Define this if you want to have NTP support. +// Define this if you want to have ADC support. // -#define NTP 1 +#define ADC 1 // -// Define this if you want to have HTTP client support. +// Define this if you want to have HTTP client support in scripts. // Define HTTPCS if you want to have additional HTTPS support. // #define HTTPC 1 @@ -72,6 +76,11 @@ // #define JSON_PARSE 1 +// +// Define this if you want to have NTP support. +// +#define NTP 1 + // // Define this if you want to have mDNS support. // diff --git a/user/user_main.c b/user/user_main.c index 7688a9d..4f9926a 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -1626,7 +1626,7 @@ void user_init() { // Start the broker only if it accessible if (config.mqtt_broker_access != 0) { - espconn_tcp_set_max_con(10); + espconn_tcp_set_max_con(15); os_printf("Max number of TCP clients: %d\r\n", espconn_tcp_get_max_con()); MQTT_server_onData(MQTT_local_DataCallback);