From 2ab9beb946521a918e8cb69d802e732afa7cb9d8 Mon Sep 17 00:00:00 2001 From: "Ruslan V. Uss" Date: Wed, 26 Oct 2016 19:41:08 +0600 Subject: [PATCH 01/13] Extras/DHT improvements: makefile fix, replace DHT_TYPE macro by param (#252) --- examples/dht_sensor/Makefile | 2 +- examples/dht_sensor/dht_sensor.c | 5 ++-- extras/dht/component.mk | 8 +++---- extras/dht/dht.c | 40 +++++++++++++++----------------- extras/dht/dht.h | 19 ++++++++------- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/examples/dht_sensor/Makefile b/examples/dht_sensor/Makefile index cfdde69..79b8d3a 100644 --- a/examples/dht_sensor/Makefile +++ b/examples/dht_sensor/Makefile @@ -1,4 +1,4 @@ -PROGRAM=dht_sensor +PROGRAM = dht_sensor EXTRA_COMPONENTS = extras/dht include ../../common.mk diff --git a/examples/dht_sensor/dht_sensor.c b/examples/dht_sensor/dht_sensor.c index b031672..32988b1 100644 --- a/examples/dht_sensor/dht_sensor.c +++ b/examples/dht_sensor/dht_sensor.c @@ -8,7 +8,7 @@ #include "esp/uart.h" #include "FreeRTOS.h" #include "task.h" -#include "dht.h" +#include #include "esp8266.h" /* An example using the ubiquitous DHT** humidity sensors @@ -16,6 +16,7 @@ * from a sensor attached to GPIO pin 4. */ uint8_t const dht_gpio = 4; +const dht_sensor_type_t sensor_type = DHT_TYPE_DHT22; void dhtMeasurementTask(void *pvParameters) { @@ -28,7 +29,7 @@ void dhtMeasurementTask(void *pvParameters) gpio_set_pullup(dht_gpio, false, false); while(1) { - if (dht_read_data(dht_gpio, &humidity, &temperature)) { + if (dht_read_data(sensor_type, dht_gpio, &humidity, &temperature)) { printf("Humidity: %d%% Temp: %dC\n", humidity / 10, temperature / 10); diff --git a/extras/dht/component.mk b/extras/dht/component.mk index 0948e4d..f7ec19e 100644 --- a/extras/dht/component.mk +++ b/extras/dht/component.mk @@ -1,10 +1,10 @@ # Component makefile for extras/dht -INC_DIRS += $(ROOT)extras/dht +# include it as 'dht/dht.h' +INC_DIRS += $(dht_ROOT).. # args for passing into compile rule generation -extras/dht_INC_DIR = $(ROOT)extras/dht -extras/dht_SRC_DIR = $(ROOT)extras/dht +dht_SRC_DIR = $(dht_ROOT) -$(eval $(call component_compile_rules,extras/dht)) +$(eval $(call component_compile_rules,dht)) diff --git a/extras/dht/dht.c b/extras/dht/dht.c index 3930735..dee04f2 100644 --- a/extras/dht/dht.c +++ b/extras/dht/dht.c @@ -14,11 +14,10 @@ #include // sdk_os_delay_us // DHT timer precision in microseconds -#define DHT_TIMER_INTERVAL 2 -#define DHT_DATA_BITS 40 +#define DHT_TIMER_INTERVAL 2 +#define DHT_DATA_BITS 40 // #define DEBUG_DHT - #ifdef DEBUG_DHT #define debug(fmt, ...) printf("%s" fmt "\n", "dht: ", ## __VA_ARGS__); #else @@ -116,7 +115,7 @@ static inline bool dht_fetch_data(uint8_t pin, bool bits[DHT_DATA_BITS]) debug("LOW bit timeout\n"); return false; } - if (!dht_await_pin_state(pin, 75, false, &high_duration)){ + if (!dht_await_pin_state(pin, 75, false, &high_duration)) { debug("HIGHT bit timeout\n"); return false; } @@ -128,27 +127,26 @@ static inline bool dht_fetch_data(uint8_t pin, bool bits[DHT_DATA_BITS]) /** * Pack two data bytes into single value and take into account sign bit. */ -static inline int16_t dht_convert_data(uint8_t msb, uint8_t lsb) +static inline int16_t dht_convert_data(dht_sensor_type_t sensor_type, uint8_t msb, uint8_t lsb) { int16_t data; -#if DHT_TYPE == DHT22 - data = msb & 0x7F; - data <<= 8; - data |= lsb; - if (msb & BIT(7)) { - data = 0 - data; // convert it to negative + if (sensor_type == DHT_TYPE_DHT22) { + data = msb & 0x7F; + data <<= 8; + data |= lsb; + if (msb & BIT(7)) { + data = 0 - data; // convert it to negative + } + } + else { + data = msb * 10; } -#elif DHT_TYPE == DHT11 - data = msb * 10; -#else -#error "Unsupported DHT type" -#endif return data; } -bool dht_read_data(uint8_t pin, int16_t *humidity, int16_t *temperature) +bool dht_read_data(dht_sensor_type_t sensor_type, uint8_t pin, int16_t *humidity, int16_t *temperature) { bool bits[DHT_DATA_BITS]; uint8_t data[DHT_DATA_BITS/8] = {0}; @@ -175,19 +173,19 @@ bool dht_read_data(uint8_t pin, int16_t *humidity, int16_t *temperature) return false; } - *humidity = dht_convert_data(data[0], data[1]); - *temperature = dht_convert_data(data[2], data[3]); + *humidity = dht_convert_data(sensor_type, data[0], data[1]); + *temperature = dht_convert_data(sensor_type, data[2], data[3]); debug("Sensor data: humidity=%d, temp=%d\n", *humidity, *temperature); return true; } -bool dht_read_float_data(uint8_t pin, float *humidity, float *temperature) +bool dht_read_float_data(dht_sensor_type_t sensor_type, uint8_t pin, float *humidity, float *temperature) { int16_t i_humidity, i_temp; - if (dht_read_data(pin, &i_humidity, &i_temp)) { + if (dht_read_data(sensor_type, pin, &i_humidity, &i_temp)) { *humidity = (float)i_humidity / 10; *temperature = (float)i_temp / 10; return true; diff --git a/extras/dht/dht.h b/extras/dht/dht.h index ab22269..664e95c 100644 --- a/extras/dht/dht.h +++ b/extras/dht/dht.h @@ -11,16 +11,19 @@ #include #include -#define DHT11 11 -#define DHT22 22 - -// Type of sensor to use -#define DHT_TYPE DHT22 - #ifdef __cplusplus extern "C" { #endif +/** + * Sensor type + */ +typedef enum +{ + DHT_TYPE_DHT11 = 0, //!< DHT11 + DHT_TYPE_DHT22 //!< DHT22 +} dht_sensor_type_t; + /** * Read data from sensor on specified pin. * @@ -29,7 +32,7 @@ extern "C" { * temperature=24.4 is 24.4 degrees Celsius * */ -bool dht_read_data(uint8_t pin, int16_t *humidity, int16_t *temperature); +bool dht_read_data(dht_sensor_type_t sensor_type, uint8_t pin, int16_t *humidity, int16_t *temperature); /** @@ -37,7 +40,7 @@ bool dht_read_data(uint8_t pin, int16_t *humidity, int16_t *temperature); * * Return values as floating point values. */ -bool dht_read_float_data(uint8_t pin, float *humidity, float *temperature); +bool dht_read_float_data(dht_sensor_type_t sensor_type, uint8_t pin, float *humidity, float *temperature); #ifdef __cplusplus } From e71919410ddb2ef1e81d61fb9a6b17e22d8044cf Mon Sep 17 00:00:00 2001 From: "Ruslan V. Uss" Date: Thu, 27 Oct 2016 14:12:27 +0600 Subject: [PATCH 02/13] Add mkspiffs binary to .gitignore (#256) --- extras/spiffs/mkspiffs/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 extras/spiffs/mkspiffs/.gitignore diff --git a/extras/spiffs/mkspiffs/.gitignore b/extras/spiffs/mkspiffs/.gitignore new file mode 100644 index 0000000..55864af --- /dev/null +++ b/extras/spiffs/mkspiffs/.gitignore @@ -0,0 +1,2 @@ +/mkspiffs +/*.o From 5b12ba54dc7b8e76ff3dbc99f282aa271f78aedf Mon Sep 17 00:00:00 2001 From: urx Date: Fri, 28 Oct 2016 16:08:37 +0400 Subject: [PATCH 03/13] Driver and example for SSD1306 128x64 I2C display (#254) --- examples/ssd1306_i2c/Makefile | 3 + examples/ssd1306_i2c/README.md | 3 + examples/ssd1306_i2c/image.xbm | 89 +++++++ examples/ssd1306_i2c/ssd1306_i2c.c | 65 +++++ extras/ssd1306/LICENSE | 22 ++ extras/ssd1306/README.md | 27 +++ extras/ssd1306/component.mk | 9 + extras/ssd1306/ssd1306.c | 376 +++++++++++++++++++++++++++++ extras/ssd1306/ssd1306.h | 50 ++++ 9 files changed, 644 insertions(+) create mode 100644 examples/ssd1306_i2c/Makefile create mode 100644 examples/ssd1306_i2c/README.md create mode 100644 examples/ssd1306_i2c/image.xbm create mode 100644 examples/ssd1306_i2c/ssd1306_i2c.c create mode 100644 extras/ssd1306/LICENSE create mode 100644 extras/ssd1306/README.md create mode 100644 extras/ssd1306/component.mk create mode 100644 extras/ssd1306/ssd1306.c create mode 100644 extras/ssd1306/ssd1306.h diff --git a/examples/ssd1306_i2c/Makefile b/examples/ssd1306_i2c/Makefile new file mode 100644 index 0000000..805a3bd --- /dev/null +++ b/examples/ssd1306_i2c/Makefile @@ -0,0 +1,3 @@ +PROGRAM=SSD1306_Example +EXTRA_COMPONENTS = extras/ssd1306 extras/i2c +include ../../common.mk diff --git a/examples/ssd1306_i2c/README.md b/examples/ssd1306_i2c/README.md new file mode 100644 index 0000000..9c75bb3 --- /dev/null +++ b/examples/ssd1306_i2c/README.md @@ -0,0 +1,3 @@ +# I2C / SSD1306 OLED LCD Example + +To run this example connect the SSD1306 OLED LCD and configure SDA/SCL pins in ssd1306_i2c.c file. diff --git a/examples/ssd1306_i2c/image.xbm b/examples/ssd1306_i2c/image.xbm new file mode 100644 index 0000000..1bd888c --- /dev/null +++ b/examples/ssd1306_i2c/image.xbm @@ -0,0 +1,89 @@ +#define image_width 128 +#define image_height 64 +static unsigned char image_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0xc3, 0xc7, 0x0f, 0x00, 0x1e, 0xfc, 0xf0, 0xe7, 0x30, 0xc0, 0x9f, + 0x7f, 0xf0, 0x80, 0x0f, 0x18, 0x30, 0xc4, 0x18, 0x80, 0x61, 0x8c, 0x31, + 0xe0, 0x30, 0xc0, 0x38, 0x0c, 0x0c, 0x63, 0x08, 0x18, 0x30, 0xc0, 0x30, + 0x80, 0x61, 0x0c, 0x33, 0xe0, 0x31, 0xc0, 0x30, 0x0c, 0x0c, 0x63, 0x00, + 0x18, 0xf0, 0xc0, 0x30, 0xc0, 0xc0, 0x0c, 0x33, 0x60, 0x31, 0xc0, 0x30, + 0x0c, 0x06, 0xe6, 0x01, 0xf8, 0xe3, 0xc7, 0x30, 0xc0, 0xc0, 0x0c, 0xf3, + 0x67, 0x33, 0xc0, 0x38, 0x0c, 0x06, 0xc6, 0x0f, 0x18, 0x80, 0xcf, 0x18, + 0xcf, 0xc0, 0x8c, 0x31, 0x60, 0x36, 0xcf, 0x1f, 0x0c, 0x06, 0x06, 0x1f, + 0x18, 0x00, 0xce, 0x0f, 0xcf, 0xc0, 0xfc, 0x30, 0x60, 0x36, 0xcf, 0x18, + 0x0c, 0x06, 0x06, 0x1c, 0x18, 0x00, 0xcc, 0x00, 0x80, 0x61, 0x0c, 0x30, + 0x60, 0x3c, 0xc0, 0x30, 0x0c, 0x0c, 0x03, 0x18, 0x18, 0x10, 0xcc, 0x00, + 0x80, 0x61, 0x0c, 0x30, 0x60, 0x38, 0xc0, 0x30, 0x0c, 0x0c, 0x23, 0x18, + 0xf8, 0xf3, 0xc3, 0x00, 0x00, 0x1e, 0x0c, 0xf0, 0x67, 0x38, 0xc0, 0x60, + 0x0c, 0xf0, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x1f, 0xfc, 0x01, 0x1c, 0xf8, 0x81, + 0x0f, 0x78, 0x00, 0x0c, 0x80, 0x1f, 0xfe, 0x00, 0xf0, 0x87, 0x3f, 0xfc, + 0x07, 0x1f, 0xfc, 0xc3, 0x1f, 0xfc, 0x00, 0x0c, 0xe0, 0x3f, 0xfe, 0x03, + 0x38, 0xc4, 0x21, 0x0c, 0x0e, 0x1b, 0x04, 0xc7, 0x18, 0x8e, 0x00, 0x0c, + 0x70, 0x20, 0x06, 0x07, 0x18, 0xc0, 0x00, 0x0c, 0x0c, 0x18, 0x00, 0x66, + 0x30, 0x07, 0x00, 0x0c, 0x30, 0x00, 0x06, 0x06, 0x18, 0xc0, 0x00, 0x0c, + 0x18, 0x18, 0x00, 0x67, 0x30, 0x03, 0x00, 0x0c, 0x18, 0x00, 0x06, 0x0c, + 0xf0, 0x81, 0x0f, 0x0c, 0x18, 0x18, 0xf0, 0x61, 0x30, 0x7b, 0x00, 0x0c, + 0x18, 0x00, 0x06, 0x0c, 0xe0, 0x07, 0x3f, 0x0c, 0x18, 0x18, 0xf0, 0x63, + 0x30, 0xff, 0x00, 0x0c, 0x18, 0x00, 0x06, 0x0c, 0x00, 0x0e, 0x70, 0x0c, + 0x18, 0x18, 0x00, 0x67, 0x30, 0xc7, 0x01, 0x0c, 0x18, 0x00, 0x06, 0x0c, + 0x00, 0x0c, 0x60, 0x0c, 0x18, 0x18, 0x00, 0x66, 0x30, 0x83, 0x01, 0x0c, + 0x18, 0x00, 0x06, 0x0c, 0x00, 0x0c, 0x60, 0x0c, 0x0c, 0x18, 0x00, 0x66, + 0x30, 0x83, 0x01, 0x0c, 0x30, 0x00, 0x06, 0x06, 0x08, 0x4e, 0x70, 0x0c, + 0x0e, 0x18, 0x04, 0xc7, 0x18, 0xc6, 0x01, 0x0c, 0x70, 0x20, 0x06, 0x07, + 0xf8, 0xc7, 0x3f, 0xfc, 0x07, 0xff, 0xfc, 0xc3, 0x1f, 0xfe, 0x00, 0xfc, + 0xe3, 0x3f, 0xfe, 0x03, 0xf0, 0x83, 0x1f, 0xfc, 0x01, 0xff, 0xf8, 0x81, + 0x0f, 0x78, 0x00, 0xfc, 0x83, 0x1f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x18, + 0x18, 0x7e, 0xc0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x18, 0x18, 0xfe, 0x80, 0x61, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x18, 0x18, 0xc6, 0x81, 0x33, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3d, 0x06, 0x06, 0x18, + 0x18, 0x86, 0x01, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x7f, 0x06, 0x06, 0x18, 0x18, 0x86, 0x01, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xe3, 0x0c, 0x03, 0x18, 0x18, 0xc6, 0x01, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc1, 0x0c, 0x03, 0x18, + 0x18, 0xfe, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xc1, 0x98, 0x01, 0x18, 0x18, 0x7e, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc1, 0x98, 0x01, 0x18, 0x18, 0xc6, 0x00, 0x1b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc1, 0xf0, 0x00, 0x18, + 0x18, 0x86, 0x81, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xe3, 0xf0, 0x00, 0x30, 0x0c, 0x86, 0x81, 0x71, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x7f, 0x60, 0x00, 0xf0, 0x0f, 0x06, 0xc3, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3d, 0x60, 0x00, 0xc0, + 0x03, 0x06, 0xe7, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; diff --git a/examples/ssd1306_i2c/ssd1306_i2c.c b/examples/ssd1306_i2c/ssd1306_i2c.c new file mode 100644 index 0000000..8ee64ba --- /dev/null +++ b/examples/ssd1306_i2c/ssd1306_i2c.c @@ -0,0 +1,65 @@ +#include "espressif/esp_common.h" +#include "esp/uart.h" + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#include + +#include "i2c/i2c.h" +#include "ssd1306/ssd1306.h" + +#include "image.xbm" + +/* Change this according to you schematics */ +#define SCL_PIN GPIO_ID_PIN((14)) +#define SDA_PIN GPIO_ID_PIN((12)) + +/* Local frame buffer */ +static uint8_t buffer[SSD1306_ROWS * SSD1306_COLS / 8]; + +static void ssd1306_task(void *pvParameters) +{ + printf("%s: Started user interface task\n", __FUNCTION__); + vTaskDelay(1000/portTICK_RATE_MS); + + + if (ssd1306_load_xbm(image_bits, buffer)) + goto error_loop; + + ssd1306_set_whole_display_lighting(false); + while (1) { + vTaskDelay(2000 / portTICK_RATE_MS); + printf("%s: steel alive\n", __FUNCTION__); + } + +error_loop: + printf("%s: error while loading framebuffer into SSD1306\n", __func__); + for(;;){ + vTaskDelay(2000 / portTICK_RATE_MS); + printf("%s: error loop\n", __FUNCTION__); + } +} + +void user_init(void) +{ + // Setup HW + uart_set_baud(0, 115200); + + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + i2c_init(SCL_PIN, SDA_PIN); + + if (ssd1306_init()){ + for (;;) { + printf("%s: failed to init SSD1306 lcd\n", __func__); + vTaskDelay(1000/portTICK_RATE_MS); + } + } + + ssd1306_set_whole_display_lighting(true); + vTaskDelay(1000/portTICK_RATE_MS); + // Create user interface task + xTaskCreate(ssd1306_task, "ssd1306_task", 256, NULL, 2, NULL); +} diff --git a/extras/ssd1306/LICENSE b/extras/ssd1306/LICENSE new file mode 100644 index 0000000..1f44f09 --- /dev/null +++ b/extras/ssd1306/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Frank Bargstedt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/extras/ssd1306/README.md b/extras/ssd1306/README.md new file mode 100644 index 0000000..cb283b9 --- /dev/null +++ b/extras/ssd1306/README.md @@ -0,0 +1,27 @@ +# Driver for I2C SSD1306 128x64 OLED LCD + +This driver is written for usage with the ESP8266 and FreeRTOS ([esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos)). + +### Usage + +Before using the SSD1306 LCD module, the function `i2c_init(SCL_PIN, SDA_PIN)` needs to be called to setup the I2C interface and then you must call ssd1306_init(). + +#### Example + +``` +#define SCL_PIN GPIO_ID_PIN(0) +#define SDA_PIN GPIO_ID_PIN(2) +... + +i2c_init(SCL_PIN, SDA_PIN); + +if (ssd1306_init()) { +// An error occured, while performing SSD1306 init init (E.g device not found etc.) +} + +// rest of the code +``` + + + + diff --git a/extras/ssd1306/component.mk b/extras/ssd1306/component.mk new file mode 100644 index 0000000..7f93338 --- /dev/null +++ b/extras/ssd1306/component.mk @@ -0,0 +1,9 @@ +# Component makefile for extras/ssd1306 + +# expected anyone using ssd1306 driver includes it as 'ssd1306/ssd1306.h' +INC_DIRS += $(ssd1306_ROOT).. + +# args for passing into compile rule generation +ssd1306_SRC_DIR = $(ssd1306_ROOT) + +$(eval $(call component_compile_rules,ssd1306)) diff --git a/extras/ssd1306/ssd1306.c b/extras/ssd1306/ssd1306.c new file mode 100644 index 0000000..85f641f --- /dev/null +++ b/extras/ssd1306/ssd1306.c @@ -0,0 +1,376 @@ +#include +#include +#include +#include +#include "ssd1306.h" + +/* SSD1306 commands */ +#define SSD1306_SET_MEM_ADDR_MODE (0x20) +#define SSD1306_ADDR_MODE_HORIZ (0x0) +#define SSD1306_ADDR_MODE_VERT (0x1) +#define SSD1306_ADDR_MODE_PAGE (0x2) + +#define SSD1306_SET_COL_ADDR (0x21) +#define SSD1306_SET_PAGE_ADDR (0x22) +#define SSD1306_SET_DISP_START_LINE (0x40) +#define SSD1306_SET_CONTRAST (0x81) +#define SSD1306_SET_SEGMENT_REMAP0 (0xA0) +#define SSD1306_SET_SEGMENT_REMAP1 (0xA1) +#define SSD1306_SET_ENTIRE_DISP_ON (0xA5) +#define SSD1306_SET_ENTIRE_DISP_OFF (0xA4) +#define SSD1306_SET_INVERSION_OFF (0xA6) +#define SSD1306_SET_INVERSION_ON (0xA7) + +#define SSD1306_SET_MUX_RATIO (0xA8) +#define SSD1306_MUX_RATIO_MASK (0x3F) +#define SSD1306_SET_DISPLAY_OFF (0xAE) +#define SSD1306_SET_DISPLAY_ON (0xAF) +#define SSD1306_SET_SCAN_DIR_FWD (0xC0) +#define SSD1306_SET_SCAN_DIR_BWD (0xC8) +#define SSD1306_SET_DISPLAY_OFFSET (0xD3) +#define SSD1306_SET_OSC_FREQ (0xD5) +#define SSD1306_SET_PRE_CHRG_PER (0xD9) + +#define SSD1306_SET_COM_PINS_HW_CFG (0xDA) +#define SSD1306_COM_PINS_HW_CFG_MASK (0x32) +#define SSD1306_SEQ_COM_PINS_CFG (0x02) +#define SSD1306_ALT_COM_PINS_CFG (0x12) +#define SSD1306_COM_LR_REMAP_OFF (0x02) +#define SSD1306_COM_LR_REMAP_ON (0x22) + +#define SSD1306_SET_DESEL_LVL (0xDB) +#define SSD1306_SET_NOP (0xE3) + +#define SSD1306_SET_CHARGE_PUMP (0x8D) +#define SSD1306_CHARGE_PUMP_EN (0x14) +#define SSD1306_CHARGE_PUMP_DIS (0x10) + +#ifdef SSD1306_DEBUG +#define debug(fmt, ...) printf("%s" fmt "\n", "SSD1306", ## __VA_ARGS__); +#else +#define debug(fmt, ...) +#endif + +/* Issue a command to SSD1306 device + * format such follows: + * |S|Slave Address|W|ACK|0x00|Command|Ack|P| + * + * in case of two-bytes command here will be Data byte + * right after command byte. + */ +int ssd1306_command(uint8_t cmd) +{ + i2c_start(); + if (!i2c_write(SSD1306_I2C_ADDR << 1)) { + debug("Error while xmitting I2C slave address\n"); + i2c_stop(); + return -EIO; + } + if (!i2c_write(0x00)) { + debug("Error while xmitting transmission type\n"); + i2c_stop(); + return -EIO; + } + + if (!i2c_write(cmd)) { + debug("Error while xmitting command: 0x%02X\n", cmd); + i2c_stop(); + return -EIO; + } + + i2c_stop(); + + return 0; +} + +/* Perform default init routine according +* to SSD1306 datasheet from adafruit.com */ +int ssd1306_init() +{ + if (!ssd1306_display_on(false) && + !ssd1306_set_osc_freq(0x80) && + !ssd1306_set_mux_ratio(SSD1306_ROWS-1) && + !ssd1306_set_display_offset(0x0) && + !ssd1306_set_display_start_line(0x0) && + !ssd1306_set_charge_pump_enabled(true) && + !ssd1306_set_mem_addr_mode(SSD1306_ADDR_MODE_HORIZ) && + !ssd1306_set_segment_remapping_enabled(false) && + !ssd1306_set_scan_direction_fwd(true) && + !ssd1306_set_com_pin_hw_config(SSD1306_ALT_COM_PINS_CFG) && + !ssd1306_set_contrast(0x9f) && + !ssd1306_set_precharge_period(0xf1) && + !ssd1306_set_deseltct_lvl(0x40) && + !ssd1306_set_whole_display_lighting(true) && + !ssd1306_set_inversion(false) && + !ssd1306_display_on(true)) { + return 0; + } + + return -EIO; +} + +/* + * frame buffer of SSD1306 consists of 8 pages of 128 bits each + * +*/ +int ssd1306_load_frame_buffer(uint8_t buf[], uint16_t len) +{ + uint16_t i; + uint8_t j; + + ssd1306_set_column_addr(0, 127); + ssd1306_set_page_addr(0, 7); + + for (i=0; i= 0x3) + return -EINVAL; + + int err = 0; + if ((err = ssd1306_command(SSD1306_SET_MEM_ADDR_MODE))) + return err; + + return ssd1306_command(mode); +} + +int ssd1306_set_segment_remapping_enabled(bool on) +{ + if (on) + return ssd1306_command(SSD1306_SET_SEGMENT_REMAP1); + + return ssd1306_command(SSD1306_SET_SEGMENT_REMAP0); +} + +int ssd1306_set_scan_direction_fwd(bool fwd) +{ + if (fwd) + return ssd1306_command(SSD1306_SET_SCAN_DIR_FWD); + + return ssd1306_command(SSD1306_SET_SCAN_DIR_BWD); +} + +int ssd1306_set_com_pin_hw_config(uint8_t config) +{ + int err = 0; + if ((err = ssd1306_command(SSD1306_SET_COM_PINS_HW_CFG))) + return err; + + return ssd1306_command(config & SSD1306_COM_PINS_HW_CFG_MASK); +} + +int ssd1306_set_contrast(uint8_t contrast) +{ + int err = 0; + if ((err = ssd1306_command(SSD1306_SET_CONTRAST))) + return err; + + return ssd1306_command(contrast); +} + +int ssd1306_set_inversion(bool on) +{ + if (on) + return ssd1306_command(SSD1306_SET_INVERSION_ON); + + return ssd1306_command(SSD1306_SET_INVERSION_OFF); +} + +int ssd1306_set_osc_freq(uint8_t osc_freq) +{ + int err = 0; + if ((err = ssd1306_command(SSD1306_SET_OSC_FREQ))) + return err; + + return ssd1306_command(osc_freq); +} + +int ssd1306_set_mux_ratio(uint8_t ratio) +{ + if (ratio < 15) + return -EINVAL; + + int err = 0; + if ((err = ssd1306_command(SSD1306_SET_MUX_RATIO))) + return err; + + return ssd1306_command(ratio); +} + +int ssd1306_set_column_addr(uint8_t start, uint8_t stop) +{ + int err = 0; + if ((err = ssd1306_command(SSD1306_SET_COL_ADDR))) + return err; + + if ((err = ssd1306_command(start))) + return err; + + return ssd1306_command(stop); +} + +int ssd1306_set_page_addr(uint8_t start, uint8_t stop) +{ + int err = 0; + if ((err = ssd1306_command(SSD1306_SET_PAGE_ADDR))) + return err; + + if ((err = ssd1306_command(start))) + return err; + + return ssd1306_command(stop); +} + +int ssd1306_set_precharge_period(uint8_t prchrg) +{ + int err = 0; + if ((err = ssd1306_command(SSD1306_SET_PRE_CHRG_PER))) + return err; + + return ssd1306_command(prchrg); +} + +int ssd1306_set_deseltct_lvl(uint8_t lvl) +{ + int err = 0; + if ((err = ssd1306_command(SSD1306_SET_DESEL_LVL))) + return err; + + return ssd1306_command(lvl); +} + +int ssd1306_set_whole_display_lighting(bool light) +{ + if (light) + return ssd1306_command(SSD1306_SET_ENTIRE_DISP_ON); + + return ssd1306_command(SSD1306_SET_ENTIRE_DISP_OFF); +} + + +/* one byte of xbm - 8 dots in line of picture source + * one byte of fb - 8 rows for 1 column of screen + */ +int ssd1306_load_xbm(uint8_t *xbm, uint8_t *fb) +{ + uint8_t bit = 0; + + int row = 0; + int column = 0; + for (row = 0; row < SSD1306_ROWS; row ++) { + for (column = 0; column < SSD1306_COLS/8; column++) { + uint16_t xbm_offset = row * 16 + column; + for (bit = 0; bit < 8; bit++) { + if (*(xbm + xbm_offset) & 1 << bit) { + *(fb + SSD1306_COLS*(row/8)+column*8+bit) |= 1 << row%8; + } + } + } + } + + return ssd1306_load_frame_buffer(fb, SSD1306_ROWS*SSD1306_COLS/8); +} + + diff --git a/extras/ssd1306/ssd1306.h b/extras/ssd1306/ssd1306.h new file mode 100644 index 0000000..933ceae --- /dev/null +++ b/extras/ssd1306/ssd1306.h @@ -0,0 +1,50 @@ +#ifndef _SSD1306__H_ +#define _SSD1306__H_ + +#include +#include +#include + +// shifted +#define SSD1306_I2C_ADDR (0x3C) + +#define SSD1306_ROWS (64) +#define SSD1306_COLS (128) + +/* Issue single command on SSD1306 */ +int ssd1306_command(uint8_t cmd); + +/* Default init for SSD1306 */ +int ssd1306_init(); + +/* Load picture in xbm format into SSD1306 RAM + * xbm - pointer to xbm picture array + * fb - pointer fo local buffer for storing converted xbm image + */ +int ssd1306_load_xbm(uint8_t *xbm, uint8_t *fb); + +/* Load local framebuffer into SSD1306 RAM */ +int ssd1306_load_frame_buffer(uint8_t buf[], uint16_t len); + +/* Clears SSD1306 ram */ +int ssd1306_clear_screen(); + +int ssd1306_display_on(bool on); +int ssd1306_set_display_start_line(uint8_t start); +int ssd1306_set_display_offset(uint8_t offset); +int ssd1306_set_charge_pump_enabled(bool enabled); +int ssd1306_set_mem_addr_mode(uint8_t mode); +int ssd1306_set_segment_remapping_enabled(bool on); +int ssd1306_set_scan_direction_fwd(bool fwd); +int ssd1306_set_com_pin_hw_config(uint8_t config); +int ssd1306_set_contrast(uint8_t contrast); +int ssd1306_set_inversion(bool on); +int ssd1306_set_osc_freq(uint8_t osc_freq); +int ssd1306_set_mux_ratio(uint8_t ratio); +int ssd1306_set_column_addr(uint8_t start, uint8_t stop); +int ssd1306_set_page_addr(uint8_t start, uint8_t stop); +int ssd1306_set_precharge_period(uint8_t prchrg); +int ssd1306_set_deseltct_lvl(uint8_t lvl); +int ssd1306_set_whole_display_lighting(bool light); + +#endif // _SSD1306__H_ From 5a14ab31e49efa5acbc234acd3962bd1af01586e Mon Sep 17 00:00:00 2001 From: "Ruslan V. Uss" Date: Fri, 28 Oct 2016 18:29:47 +0600 Subject: [PATCH 04/13] DS18x20: DS18S20 support, bugfixes (#255) --- extras/ds18b20/component.mk | 6 +++++ extras/ds18b20/ds18b20.c | 46 ++++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/extras/ds18b20/component.mk b/extras/ds18b20/component.mk index 866371c..2bffda5 100644 --- a/extras/ds18b20/component.mk +++ b/extras/ds18b20/component.mk @@ -6,4 +6,10 @@ INC_DIRS += $(ds18b20_ROOT).. # args for passing into compile rule generation ds18b20_SRC_DIR = $(ds18b20_ROOT) +# users can override this setting and get console debug output +DS18B20_DEBUG ?= 0 +ifeq ($(DS18B20_DEBUG),1) + ds18b20_CFLAGS = $(CFLAGS) -DDS18B20_DEBUG +endif + $(eval $(call component_compile_rules,ds18b20)) diff --git a/extras/ds18b20/ds18b20.c b/extras/ds18b20/ds18b20.c index c965b04..466422e 100644 --- a/extras/ds18b20/ds18b20.c +++ b/extras/ds18b20/ds18b20.c @@ -18,6 +18,15 @@ #define os_sleep_ms(x) vTaskDelay(((x) + portTICK_RATE_MS - 1) / portTICK_RATE_MS) +#define DS18B20_FAMILY_ID 0x28 +#define DS18S20_FAMILY_ID 0x10 + +#ifdef DS18B20_DEBUG +#define debug(fmt, ...) printf("%s" fmt "\n", "DS18B20: ", ## __VA_ARGS__); +#else +#define debug(fmt, ...) +#endif + uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) { onewire_addr_t addr; onewire_search_t search; @@ -28,7 +37,7 @@ uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) { while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) { uint8_t crc = onewire_crc8((uint8_t *)&addr, 7); if (crc != (addr >> 56)){ - printf("CRC check failed: %02X %02X\n", (unsigned)(addr >> 56), crc); + debug("CRC check failed: %02X %02X\n", (unsigned)(addr >> 56), crc); return 0; } @@ -49,11 +58,11 @@ uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) { get[k]=onewire_read(pin); } - //printf("\n ScratchPAD DATA = %X %X %X %X %X %X %X %X %X\n",get[8],get[7],get[6],get[5],get[4],get[3],get[2],get[1],get[0]); + //debug("\n ScratchPAD DATA = %X %X %X %X %X %X %X %X %X\n",get[8],get[7],get[6],get[5],get[4],get[3],get[2],get[1],get[0]); crc = onewire_crc8(get, 8); if (crc != get[8]){ - printf("CRC check failed: %02X %02X\n", get[8], crc); + debug("CRC check failed: %02X %02X\n", get[8], crc); return 0; } @@ -64,7 +73,7 @@ uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) { float temperature; temperature = (temp * 625.0)/10000; - //printf("Got a DS18B20 Reading: %d.%02d\n", (int)temperature, (int)(temperature - (int)temperature) * 100); + //debug("Got a DS18B20 Reading: %d.%02d\n", (int)temperature, (int)(temperature - (int)temperature) * 100); result[sensor_id].id = sensor_id; result[sensor_id].value = temperature; sensor_id++; @@ -91,11 +100,11 @@ float ds18b20_read_single(uint8_t pin) { get[k]=onewire_read(pin); } - //printf("\n ScratchPAD DATA = %X %X %X %X %X %X %X %X %X\n",get[8],get[7],get[6],get[5],get[4],get[3],get[2],get[1],get[0]); + //debug("\n ScratchPAD DATA = %X %X %X %X %X %X %X %X %X\n",get[8],get[7],get[6],get[5],get[4],get[3],get[2],get[1],get[0]); uint8_t crc = onewire_crc8(get, 8); if (crc != get[8]){ - printf("CRC check failed: %02X %02X", get[8], crc); + debug("CRC check failed: %02X %02X", get[8], crc); return 0; } @@ -108,7 +117,7 @@ float ds18b20_read_single(uint8_t pin) { temperature = (temp * 625.0)/10000; return temperature; - //printf("Got a DS18B20 Reading: %d.%02d\n", (int)temperature, (int)(temperature - (int)temperature) * 100); + //debug("Got a DS18B20 Reading: %d.%02d\n", (int)temperature, (int)(temperature - (int)temperature) * 100); } bool ds18b20_measure(int pin, ds18b20_addr_t addr, bool wait) { @@ -156,7 +165,7 @@ bool ds18b20_read_scratchpad(int pin, ds18b20_addr_t addr, uint8_t *buffer) { expected_crc = onewire_crc8(buffer, 8); if (crc != expected_crc) { - printf("CRC check failed reading scratchpad: %02x %02x %02x %02x %02x %02x %02x %02x : %02x (expected %02x)\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], crc, expected_crc); + debug("CRC check failed reading scratchpad: %02x %02x %02x %02x %02x %02x %02x %02x : %02x (expected %02x)\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], crc, expected_crc); return false; } @@ -172,8 +181,16 @@ float ds18b20_read_temperature(int pin, ds18b20_addr_t addr) { } temp = scratchpad[1] << 8 | scratchpad[0]; - - return ((float)temp * 625.0)/10000; + + float res; + if ((uint8_t)addr == DS18B20_FAMILY_ID) { + res = ((float)temp * 625.0)/10000; + } + else { + temp = ((temp & 0xfffe) << 3) + (16 - scratchpad[6]) - 4; + res = ((float)temp * 625.0)/10000 - 0.25; + } + return res; } float ds18b20_measure_and_read(int pin, ds18b20_addr_t addr) { @@ -200,10 +217,13 @@ int ds18b20_scan_devices(int pin, ds18b20_addr_t *addr_list, int addr_count) { onewire_search_start(&search); while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) { - if (found < addr_count) { - addr_list[found] = addr; + uint8_t family_id = (uint8_t)addr; + if (family_id == DS18B20_FAMILY_ID || family_id == DS18S20_FAMILY_ID) { + if (found < addr_count) { + addr_list[found] = addr; + } + found++; } - found++; } return found; } From 8ef476c71ffa89c2991cdd715ab47b3d21736929 Mon Sep 17 00:00:00 2001 From: DanielCerejo Date: Sun, 30 Oct 2016 09:26:29 +0000 Subject: [PATCH 05/13] initialize dhcpserver_task_handle = NULL (#257) corrert error printf "OTA TFTP" to "DHCP Server Error" --- extras/dhcpserver/dhcpserver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/dhcpserver/dhcpserver.c b/extras/dhcpserver/dhcpserver.c index 82ccfd1..f1e8f66 100644 --- a/extras/dhcpserver/dhcpserver.c +++ b/extras/dhcpserver/dhcpserver.c @@ -49,7 +49,7 @@ typedef struct { /* Only one DHCP server task can run at once, so we have global state for it. */ -static xTaskHandle dhcpserver_task_handle; +static xTaskHandle dhcpserver_task_handle=NULL; static server_state_t *state; /* Handlers for various kinds of incoming DHCP messages */ @@ -108,7 +108,7 @@ static void dhcpserver_task(void *pxParameter) state->nc = netconn_new (NETCONN_UDP); if(!state->nc) { - printf("OTA TFTP: Failed to allocate socket.\r\n"); + printf("DHCP Server Error: Failed to allocate socket.\r\n"); return; } From 98de5e573afbda4b8346b19afdd4ff5cb47c058a Mon Sep 17 00:00:00 2001 From: "Ruslan V. Uss" Date: Tue, 1 Nov 2016 15:40:19 +0600 Subject: [PATCH 06/13] RTC drivers fix (#259) --- examples/ds1307/main.c | 27 ++++++++++++++------------- extras/ds1307/ds1307.c | 4 ++-- extras/ds3231/ds3231.c | 2 +- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/ds1307/main.c b/examples/ds1307/main.c index 2f22447..a1b847e 100644 --- a/examples/ds1307/main.c +++ b/examples/ds1307/main.c @@ -14,32 +14,33 @@ #define SCL_PIN 5 #define SDA_PIN 4 -void user_init (void) +void user_init(void) { - uart_set_baud (0, 115200); - printf ("SDK version:%s\n", sdk_system_get_sdk_version ()); + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); - i2c_init (SCL_PIN, SDA_PIN); - ds1307_start (true); + i2c_init(SCL_PIN, SDA_PIN); + ds1307_start(true); // setup datetime: 2016-10-09 13:50:10 struct tm time = { .tm_year = 2016, - .tm_mon = 10, + .tm_mon = 9, // 0-based .tm_mday = 9, .tm_hour = 13, - .tm_min = 50, - .tm_sec = 10 + .tm_min = 50, + .tm_sec = 10 }; - ds1307_set_time (&time); + ds1307_set_time(&time); while (true) { - ds1307_get_time (&time); + ds1307_get_time(&time); - printf ("%04d-%02d-%02d %02d:%02d:%02d\n", time.tm_year, time.tm_mon, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); + printf("%04d-%02d-%02d %02d:%02d:%02d\n", time.tm_year, time.tm_mon + 1, + time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); - for (uint32_t i = 0; i < 1000; i ++) - sdk_os_delay_us (500); + for (uint32_t i = 0; i < 1000; i++) + sdk_os_delay_us(500); } } diff --git a/extras/ds1307/ds1307.c b/extras/ds1307/ds1307.c index cd4472f..90abf9d 100644 --- a/extras/ds1307/ds1307.c +++ b/extras/ds1307/ds1307.c @@ -78,14 +78,14 @@ void ds1307_get_time(struct tm *time) if (buf[2] & HOUR12_BIT) { // RTC in 12-hour mode - time->tm_hour = bcd2dec(buf[2] & HOUR12_MASK); + time->tm_hour = bcd2dec(buf[2] & HOUR12_MASK) - 1; if (buf[2] & PM_BIT) time->tm_hour += 12; } else time->tm_hour = bcd2dec(buf[2] & HOUR24_MASK); time->tm_wday = bcd2dec(buf[3]) - 1; time->tm_mday = bcd2dec(buf[4]); - time->tm_mon = bcd2dec(buf[5]); + time->tm_mon = bcd2dec(buf[5]) - 1; time->tm_year = bcd2dec(buf[6]) + 2000; } diff --git a/extras/ds3231/ds3231.c b/extras/ds3231/ds3231.c index 87ce805..543474e 100644 --- a/extras/ds3231/ds3231.c +++ b/extras/ds3231/ds3231.c @@ -270,7 +270,7 @@ bool ds3231_getTime(struct tm *time) time->tm_min = bcdToDec(data[1]); if (data[2] & DS3231_12HOUR_FLAG) { /* 12H */ - time->tm_hour = bcdToDec(data[2] & DS3231_12HOUR_MASK); + time->tm_hour = bcdToDec(data[2] & DS3231_12HOUR_MASK) - 1; /* AM/PM? */ if (data[2] & DS3231_PM_FLAG) time->tm_hour += 12; } else { From e2e6f352889a6829b537f1b64ee3182bd27d31a1 Mon Sep 17 00:00:00 2001 From: sheinz Date: Tue, 1 Nov 2016 17:14:34 +0200 Subject: [PATCH 07/13] Fix spiff and stdin_uart_interrupt overiding the same read function (#249) * Fix spiff and stdin_uart_interrupt overiding the same read function * Make strong function defninition replace a weak one --- common.mk | 12 +++++++++--- core/newlib_syscalls.c | 16 ++++++++++------ extras/spiffs/esp_spiffs.c | 13 ++++--------- extras/stdin_uart_interrupt/component.mk | 10 ++++++---- .../stdin_uart_interrupt/stdin_uart_interrupt.c | 4 ++-- parameters.mk | 3 +++ 6 files changed, 34 insertions(+), 24 deletions(-) diff --git a/common.mk b/common.mk index c3923c8..41a218f 100644 --- a/common.mk +++ b/common.mk @@ -134,9 +134,15 @@ $$($(1)_OBJ_DIR)%.o: $$($(1)_REAL_ROOT)%.S $$($(1)_MAKEFILE) $(wildcard $(ROOT)* $$($(1)_CC_BASE) -c $$< -o $$@ $$($(1)_CC_BASE) -MM -MT $$@ -MF $$(@:.o=.d) $$< -# the component is shown to depend on both obj and source files so we get a meaningful error message -# for missing explicitly named source files -$$($(1)_AR): $$($(1)_OBJ_FILES) $$($(1)_SRC_FILES) +$(1)_AR_IN_FILES = $$($(1)_OBJ_FILES) + +# the component is shown to depend on both obj and source files so we get +# a meaningful error message for missing explicitly named source files +ifeq ($(INCLUDE_SRC_IN_AR),1) + $(1)_AR_IN_FILES += $$($(1)_SRC_FILES) +endif + +$$($(1)_AR): $$($(1)_AR_IN_FILES) $(vecho) "AR $$@" $(Q) mkdir -p $$(dir $$@) $(Q) $(AR) cru $$@ $$^ diff --git a/core/newlib_syscalls.c b/core/newlib_syscalls.c index 577ecda..f526bea 100644 --- a/core/newlib_syscalls.c +++ b/core/newlib_syscalls.c @@ -59,14 +59,9 @@ __attribute__((weak)) long _write_r(struct _reent *r, int fd, const char *ptr, i } /* syscall implementation for stdio read from UART */ -__attribute__((weak)) long _read_r( struct _reent *r, int fd, char *ptr, int len ) +__attribute__((weak)) long _read_stdin_r(struct _reent *r, int fd, char *ptr, int len) { int ch, i; - - if(fd != r->_stdin->_file) { - r->_errno = EBADF; - return -1; - } uart_rxfifo_wait(0, 1); for(i = 0; i < len; i++) { ch = uart_getc_nowait(0); @@ -76,6 +71,15 @@ __attribute__((weak)) long _read_r( struct _reent *r, int fd, char *ptr, int len return i; } +__attribute__((weak)) long _read_r( struct _reent *r, int fd, char *ptr, int len ) +{ + if(fd != r->_stdin->_file) { + r->_errno = EBADF; + return -1; + } + return _read_stdin_r(r, fd, ptr, len); +} + /* Stub syscall implementations follow, to allow compiling newlib functions that pull these in via various codepaths */ diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index c052c2f..96ec743 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -141,21 +141,16 @@ long _write_r(struct _reent *r, int fd, const char *ptr, int len ) return len; } +// This function is weakly defined in core/newlib_syscalls.c +long _read_stdin_r(struct _reent *r, int fd, char *ptr, int len); + // This implementation replaces implementation in core/newlib_syscals.c long _read_r( struct _reent *r, int fd, char *ptr, int len ) { - int ch, i; - if(fd != r->_stdin->_file) { return SPIFFS_read(&fs, (spiffs_file)fd, ptr, len); } - uart_rxfifo_wait(0, 1); - for(i = 0; i < len; i++) { - ch = uart_getc_nowait(0); - if (ch < 0) break; - ptr[i] = ch; - } - return i; + return _read_stdin_r(r, fd, ptr, len); } int _open_r(struct _reent *r, const char *pathname, int flags, int mode) diff --git a/extras/stdin_uart_interrupt/component.mk b/extras/stdin_uart_interrupt/component.mk index e00cf81..ba16484 100644 --- a/extras/stdin_uart_interrupt/component.mk +++ b/extras/stdin_uart_interrupt/component.mk @@ -4,10 +4,12 @@ # for 'usage' as this module is a drop-in replacement for the original polled # version of reading from the UART. -INC_DIRS += $(ROOT)extras/stdin_uart_interrupt +INC_DIRS += $(stdin_uart_interrupt_ROOT) # args for passing into compile rule generation -extras/stdin_uart_interrupt_INC_DIR = $(ROOT)extras/stdin_uart_interrupt -extras/stdin_uart_interrupt_SRC_DIR = $(ROOT)extras/stdin_uart_interrupt +stdin_uart_interrupt_SRC_DIR = $(stdin_uart_interrupt_ROOT) -$(eval $(call component_compile_rules,extras/stdin_uart_interrupt)) +INCLUDE_SRC_IN_AR = 0 +EXTRA_LDFLAGS = -Wl,--whole-archive $(stdin_uart_interrupt_AR) -Wl,--no-whole-archive + +$(eval $(call component_compile_rules,stdin_uart_interrupt)) diff --git a/extras/stdin_uart_interrupt/stdin_uart_interrupt.c b/extras/stdin_uart_interrupt/stdin_uart_interrupt.c index e85c913..c836651 100644 --- a/extras/stdin_uart_interrupt/stdin_uart_interrupt.c +++ b/extras/stdin_uart_interrupt/stdin_uart_interrupt.c @@ -75,9 +75,9 @@ uint32_t uart0_num_char(void) return count; } -// _read_r in core/newlib_syscalls.c will be skipped by the linker in favour +// _read_stdin_r in core/newlib_syscalls.c will be skipped by the linker in favour // of this function -long _read_r(struct _reent *r, int fd, char *ptr, int len) +long _read_stdin_r(struct _reent *r, int fd, char *ptr, int len) { if (!inited) uart0_rx_init(); for(int i = 0; i < len; i++) { diff --git a/parameters.mk b/parameters.mk index 8c6a8a3..ddf2580 100644 --- a/parameters.mk +++ b/parameters.mk @@ -42,6 +42,9 @@ PRINTF_SCANF_FLOAT_SUPPORT ?= 1 FLAVOR ?= release # or debug +# Include source files into a static library. It improves error messages. +INCLUDE_SRC_IN_AR ?= 1 + # Compiler names, etc. assume gdb CROSS ?= xtensa-lx106-elf- From 4c84b645669c51f62855141a457d6cdc781a3633 Mon Sep 17 00:00:00 2001 From: "Ruslan V. Uss" Date: Thu, 3 Nov 2016 16:01:18 +0600 Subject: [PATCH 08/13] HD44780 documentation (#262) --- extras/hd44780/README.md | 119 ++++++++++++++++++++++++++++++++++++ extras/hd44780/img/0801.png | Bin 0 -> 5413 bytes extras/hd44780/img/1601.png | Bin 0 -> 14612 bytes extras/hd44780/img/1602.png | Bin 0 -> 13625 bytes extras/hd44780/img/1604.png | Bin 0 -> 24946 bytes 5 files changed, 119 insertions(+) create mode 100644 extras/hd44780/README.md create mode 100644 extras/hd44780/img/0801.png create mode 100644 extras/hd44780/img/1601.png create mode 100644 extras/hd44780/img/1602.png create mode 100644 extras/hd44780/img/1604.png diff --git a/extras/hd44780/README.md b/extras/hd44780/README.md new file mode 100644 index 0000000..519bc92 --- /dev/null +++ b/extras/hd44780/README.md @@ -0,0 +1,119 @@ +# HD44780 LCD display driver + +## Connection type + +Driver supports GPIO connections to module and I2C GPIO expanders as well. +Define `HD44780_I2C = 0` in application makefile for direct GPIO connection. + +See `examples/i2c_lcd_test` and `examples/hd44780_test` . + +## Display types + +### 8x1 + +#### Memory layout + +![0801 display](img/0801.png) + +#### Example + +```C +hd44780_t lcd = { + .addr = ADDR, + .font = HD44780_FONT_5X8, + .lines = 1, + .pins = { + .rs = 0, + .e = 2, + .d4 = 4, + .d5 = 5, + .d6 = 6, + .d7 = 7, + .bl = 3 + }, + .backlight = true +}; +``` + +### 16x1 + +#### Memory layout + +![1601 display](img/1601.png) + +#### Example + +```C +hd44780_t lcd = { + .addr = ADDR, + .font = HD44780_FONT_5X8, + .lines = 2, + .pins = { + .rs = 0, + .e = 2, + .d4 = 4, + .d5 = 5, + .d6 = 6, + .d7 = 7, + .bl = 3 + }, + .backlight = true +}; +hd44780_init(&lcd); +hd44780_gotoxy(&lcd, 0, 0); +hd44780_puts(&lcd, "Hello wo"); +hd44780_gotoxy(&lcd, 0, 1); +hd44780_puts(&lcd, "rld!"); +``` + +### 16x2, 20x2 + +#### Memory layout + +![1602 display](img/1602.png) + +#### Example + +```C +hd44780_t lcd = { + .addr = ADDR, + .font = HD44780_FONT_5X8, + .lines = 2, + .pins = { + .rs = 0, + .e = 2, + .d4 = 4, + .d5 = 5, + .d6 = 6, + .d7 = 7, + .bl = 3 + }, + .backlight = true +}; +``` + +### 16x4, 20x4 + +#### Memory layout + +![1604 display](img/1604.png) + +#### Example + +```C +hd44780_t lcd = { + .addr = ADDR, + .font = HD44780_FONT_5X8, + .lines = 2, + .pins = { + .rs = 0, + .e = 2, + .d4 = 4, + .d5 = 5, + .d6 = 6, + .d7 = 7, + .bl = 3 + }, + .backlight = true +}; +``` diff --git a/extras/hd44780/img/0801.png b/extras/hd44780/img/0801.png new file mode 100644 index 0000000000000000000000000000000000000000..0a81c09e589bada151cae3ea9aea20d520cb3e4b GIT binary patch literal 5413 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 literal 0 HcmV?d00001 diff --git a/extras/hd44780/img/1601.png b/extras/hd44780/img/1601.png new file mode 100644 index 0000000000000000000000000000000000000000..429bdf60bf49a0a346810cbce19d00b19ef0bb76 GIT binary patch literal 14612 zcmdUW2UL^Ww(hU81w;WwfvpsybOBM3UL+!jG?6C31O!2)cSxiu9i%rQ6a^6!0RaIa zG-;uVNC_Aq(o2BQdwDB+?{m&Q_ue<&Ib*zW-*_2=LH=a@Yt4VHx#pbTx4wkj)lxli z?CdcJf=;O2R=x*8G)53~D2nbd_+tH1Rd|9C9wXfRqE&SR*RT74x&Mnu%%g59r^L0&2iA|1BhZ3eGhGvepmcSU;In54YoP2e zP!c$Kus*wZpItx2O}`@DN4kx@V_REWq=P+@&!Qi99Qax&pXJJ zg8iMEgB^nRJfW2_=Bu{Pc6V4!+qv_QX@XS`yk_}Qjgz;x0taIoV@yY+&+Zua z?k`%OWqzM~Zc)eW?CkuR}$czmlx{^;yd3xB$toSa_%``j-WJs0)LiV7jw7tHXF z1hQ6kw~Yko~;(i(~7r(5lg8ZOI9z1llJFH`37?v!_Tzobn@EoOOw;kY-W|-T1He6+4qjfuiR+Pt6E3->VthA`T znzB>v^Xpue53Y{w!b+|uo`YdY?lRqr4!m)mL7u^--T6>t@Nu6_q11g&=gSSQL;h@& zcD9_Si7xac%jY``P_53#TvLVgGUB{H|M8=Ki!4S7*UyGKRjMZkJi=rm@+7z-)jNej zPp}44vD>I$6V#UljN;J@2P>c59g?3j@Ajq*VTcYX-b#KI{acRJ9u{lRfaAcb`&uPVm+4XS|OAB^c_ za)D{GXhH9XFc0}0FUOov8%GT8;D|NsdZA&ie(}I*xHwjGivvApZPe#fK3LK2e^Kmu zQes<{=O+TWWvV%I*niC+xA*2T>z7FF+xY=;jrjy}g2f4mCpZjnIwH}yoKe+q#k{*)!!?;scZyL>D?lvP^rPTku z^pAfz3G?93&CB%bCm1P6oei%uAG~P=vnZtXd(NEEJ)>+Q5NV338t!XckU6ZIVAK?r zW{S!)Se|}Nm=Ff@l+*R(bXx6jNxwhhw#D4Id8=kt0oU>itc6imyb?PS7Y;5U1 z-5XooHh$R%7ST5bxvn^5A>~~dM^bmgw~7i(nj{CK`Atk{IC5^*akE20=$dwRcVc3q zcMZv^q%PoiP`w+j>VarUcvZ{B9{6y^WxN6@9UzWOX1v3Sl$4b0nrb!<98ZU!Z`+W~LU5q3!d%wtO)3hA9w&gh@Z%&lf^ ziK>xj1DKclfydtJY!6p4Cuj;AW$jwVnm^@w~y)FhmqCFk3Fpr=ZxDeNKxUP+}*8w#dHJ`P#$1=cTbJ| zV%eH*a}{FqR5=qv&}3VUit22((e31^hPkseS;Edezkclm+zH_f6+vIa+9lOral+Y| zNk3ZaKPRheX~f@l8VbWv@RTo%eP2
}4@6ZnyG+Hv(rR+W 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 literal 0 HcmV?d00001 diff --git a/extras/hd44780/img/1604.png b/extras/hd44780/img/1604.png new file mode 100644 index 0000000000000000000000000000000000000000..ac17ba1280e3c1829dc22b8988ee0cadf9fa5ada GIT binary patch literal 24946 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 literal 0 HcmV?d00001 From a5cc728079bb4bb9d0f31891117f129bb8abdeb3 Mon Sep 17 00:00:00 2001 From: Our Air Quality Date: Sat, 5 Nov 2016 21:04:03 +1100 Subject: [PATCH 09/13] FreeRTOS type updates. (#261) --- FreeRTOS/Source/include/FreeRTOSConfig.h | 6 ++++- FreeRTOS/Source/portable/esp8266/port.c | 4 ++-- FreeRTOS/Source/portable/esp8266/portmacro.h | 5 ++--- core/app_main.c | 6 ++--- core/sysparam.c | 2 +- examples/access_point/access_point.c | 2 +- examples/aws_iot/aws_iot.c | 16 +++++++------- examples/aws_iot/ssl_connection.c | 2 +- examples/blink/blink.c | 8 +++---- examples/bmp180_i2c/bmp180_i2c.c | 12 +++++----- examples/bmp280/bmp280_example.c | 8 +++---- examples/button/button.c | 10 ++++----- examples/dht_sensor/dht_sensor.c | 2 +- .../ds18b20_broadcaster/ds18b20_broadcaster.c | 4 ++-- examples/ds18b20_onewire/ds18b20_onewire.c | 2 +- examples/experiments/timers/timers.c | 2 +- .../unaligned_load/unaligned_load.c | 4 ++-- examples/fatfs_rtc/main.c | 2 +- examples/http_get/http_get.c | 10 ++++----- examples/http_get_mbedtls/http_get_mbedtls.c | 4 ++-- examples/i2s_audio/i2s_audio_example.c | 4 ++-- examples/mqtt_client/mqtt_client.c | 12 +++++----- examples/ota_basic/ota_basic.c | 4 ++-- examples/posix_fs/posix_fs_example.c | 4 ++-- examples/simple/simple.c | 6 ++--- examples/sntp/sntp_example.c | 2 +- examples/spiffs/spiffs_example.c | 2 +- examples/ssd1306_i2c/ssd1306_i2c.c | 10 ++++----- examples/terminal/terminal.c | 2 +- examples/tsl2561/tsl2561_example.c | 2 +- examples/ws2812_i2s/ws2812_i2s_colour_loop.c | 2 +- examples/ws2812_rainbow/ws2812_rainbow.c | 2 +- extras/bmp180/README.md | 2 +- extras/bmp180/bmp180.c | 20 ++++++++--------- extras/bmp180/bmp180.h | 8 +++---- extras/bmp280/README.md | 4 ++-- extras/cpp_support/include/countdown.hpp | 4 ++-- extras/cpp_support/include/mutex.hpp | 4 ++-- extras/cpp_support/include/queue.hpp | 6 ++--- extras/cpp_support/include/task.hpp | 4 ++-- extras/dhcpserver/dhcpserver.c | 2 +- extras/ds18b20/ds18b20.c | 6 ++--- extras/fatfs/ffconf.h | 2 +- extras/fatfs/syscall.c | 8 +++---- extras/paho_mqtt_c/MQTTESP8266.c | 14 ++++++------ extras/paho_mqtt_c/MQTTESP8266.h | 2 +- .../stdin_uart_interrupt.c | 2 +- extras/tsl2561/tsl2561.c | 6 ++--- include/etstimer.h | 2 +- lwip/include/arch/sys_arch.h | 12 +++++----- lwip/sys_arch.c | 22 +++++++++---------- open_esplibs/libmain/os_cpu_a.c | 4 ++-- open_esplibs/libmain/timers.c | 2 +- 53 files changed, 151 insertions(+), 148 deletions(-) diff --git a/FreeRTOS/Source/include/FreeRTOSConfig.h b/FreeRTOS/Source/include/FreeRTOSConfig.h index fc3b283..7df92bf 100644 --- a/FreeRTOS/Source/include/FreeRTOSConfig.h +++ b/FreeRTOS/Source/include/FreeRTOSConfig.h @@ -44,7 +44,7 @@ #define configCPU_CLOCK_HZ ( ( unsigned long ) 80000000 ) #endif #ifndef configTICK_RATE_HZ -#define configTICK_RATE_HZ ( ( portTickType ) 100 ) +#define configTICK_RATE_HZ ( ( TickType_t ) 100 ) #endif #ifndef configMAX_PRIORITIES #define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 15 ) @@ -140,5 +140,9 @@ to exclude the API function. */ #define INCLUDE_uxTaskGetStackHighWaterMark 1 #endif +#ifndef configENABLE_BACKWARD_COMPATIBILITY +#define configENABLE_BACKWARD_COMPATIBILITY 0 +#endif + #endif /* __DEFAULT_FREERTOS_CONFIG_H */ diff --git a/FreeRTOS/Source/portable/esp8266/port.c b/FreeRTOS/Source/portable/esp8266/port.c index b5d272f..87d846f 100644 --- a/FreeRTOS/Source/portable/esp8266/port.c +++ b/FreeRTOS/Source/portable/esp8266/port.c @@ -94,7 +94,7 @@ void *xPortSupervisorStackPointer; /* * Stack initialization */ -portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ) +portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) { #define SET_STKREG(r,v) sp[(r) >> 2] = (portSTACK_TYPE)(v) portSTACK_TYPE *sp, *tp; @@ -259,7 +259,7 @@ void IRAM vPortExitCritical( void ) } /* Backward compatibility with libmain.a and libpp.a and can remove when these are open. */ -signed portBASE_TYPE xTaskGenericCreate( pdTASK_CODE pxTaskCode, const signed char * const pcName, unsigned short usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pxCreatedTask, portSTACK_TYPE *puxStackBuffer, const xMemoryRegion * const xRegions ) +signed portBASE_TYPE xTaskGenericCreate( TaskFunction_t pxTaskCode, const signed char * const pcName, unsigned short usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, TaskHandle_t *pxCreatedTask, portSTACK_TYPE *puxStackBuffer, const MemoryRegion_t * const xRegions ) { (void)puxStackBuffer; (void)xRegions; return xTaskCreate( pxTaskCode, (const char * const)pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask); diff --git a/FreeRTOS/Source/portable/esp8266/portmacro.h b/FreeRTOS/Source/portable/esp8266/portmacro.h index 645f5c4..19c9248 100644 --- a/FreeRTOS/Source/portable/esp8266/portmacro.h +++ b/FreeRTOS/Source/portable/esp8266/portmacro.h @@ -100,13 +100,12 @@ typedef portSTACK_TYPE StackType_t; typedef portBASE_TYPE BaseType_t; typedef unsigned portBASE_TYPE UBaseType_t; -typedef uint32_t portTickType; typedef uint32_t TickType_t; -#define portMAX_DELAY ( portTickType ) 0xffffffff +#define portMAX_DELAY ( TickType_t ) 0xffffffff /* Architecture specifics. */ #define portSTACK_GROWTH ( -1 ) -#define portTICK_PERIOD_MS ( ( portTickType ) 1000 / configTICK_RATE_HZ ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) #define portBYTE_ALIGNMENT 8 /*-----------------------------------------------------------*/ diff --git a/core/app_main.c b/core/app_main.c index 6662301..63142cd 100644 --- a/core/app_main.c +++ b/core/app_main.c @@ -52,10 +52,10 @@ uint8_t sdk_user_init_flag; struct sdk_info_st sdk_info; // xUserTaskHandle -- .bss+0x28 -xTaskHandle sdk_xUserTaskHandle; +TaskHandle_t sdk_xUserTaskHandle; // xWatchDogTaskHandle -- .bss+0x2c -xTaskHandle sdk_xWatchDogTaskHandle; +TaskHandle_t sdk_xWatchDogTaskHandle; /* Static function prototypes */ @@ -227,7 +227,7 @@ void IRAM sdk_user_start(void) { } // .text+0x3a8 -void IRAM vApplicationStackOverflowHook(xTaskHandle task, char *task_name) { +void IRAM vApplicationStackOverflowHook(TaskHandle_t task, char *task_name) { printf("Task stack overflow (high water mark=%lu name=\"%s\")\n", uxTaskGetStackHighWaterMark(task), task_name); } diff --git a/core/sysparam.c b/core/sysparam.c index 290ddb3..f00f161 100644 --- a/core/sysparam.c +++ b/core/sysparam.c @@ -114,7 +114,7 @@ static struct { uint32_t end_addr; size_t region_size; bool force_compact; - xSemaphoreHandle sem; + SemaphoreHandle_t sem; } _sysparam_info; /***************************** Internal routines *****************************/ diff --git a/examples/access_point/access_point.c b/examples/access_point/access_point.c index 05e344a..f8fcd50 100644 --- a/examples/access_point/access_point.c +++ b/examples/access_point/access_point.c @@ -84,7 +84,7 @@ static void telnetTask(void *pvParameters) char buf[80]; snprintf(buf, sizeof(buf), "Uptime %d seconds\r\n", - xTaskGetTickCount()*portTICK_RATE_MS/1000); + xTaskGetTickCount()*portTICK_PERIOD_MS/1000); netconn_write(client, buf, strlen(buf), NETCONN_COPY); snprintf(buf, sizeof(buf), "Free heap %d bytes\r\n", (int)xPortGetFreeHeapSize()); netconn_write(client, buf, strlen(buf), NETCONN_COPY); diff --git a/examples/aws_iot/aws_iot.c b/examples/aws_iot/aws_iot.c index 8b26b99..08ce9f4 100644 --- a/examples/aws_iot/aws_iot.c +++ b/examples/aws_iot/aws_iot.c @@ -31,7 +31,7 @@ extern char *ca_cert, *client_endpoint, *client_cert, *client_key; static int wifi_alive = 0; static int ssl_reset; static SSLConnection *ssl_conn; -static xQueueHandle publish_queue; +static QueueHandle_t publish_queue; static void beat_task(void *pvParameters) { char msg[16]; @@ -39,7 +39,7 @@ static void beat_task(void *pvParameters) { while (1) { if (!wifi_alive) { - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); continue; } @@ -50,7 +50,7 @@ static void beat_task(void *pvParameters) { printf("Publish queue overflow\r\n"); } - vTaskDelay(10000 / portTICK_RATE_MS); + vTaskDelay(10000 / portTICK_PERIOD_MS); } } @@ -142,7 +142,7 @@ static void mqtt_task(void *pvParameters) { ssl_conn = (SSLConnection *) malloc(sizeof(SSLConnection)); while (1) { if (!wifi_alive) { - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); continue; } @@ -191,7 +191,7 @@ static void mqtt_task(void *pvParameters) { while (wifi_alive && !ssl_reset) { char msg[64]; while (xQueueReceive(publish_queue, (void *) msg, 0) == pdTRUE) { - portTickType task_tick = xTaskGetTickCount(); + TickType_t task_tick = xTaskGetTickCount(); uint32_t free_heap = xPortGetFreeHeapSize(); uint32_t free_stack = uxTaskGetStackHighWaterMark(NULL); snprintf(msg, sizeof(msg), "%u: free heap %u, free stack %u", @@ -246,7 +246,7 @@ static void wifi_task(void *pvParameters) { printf("WiFi: connection failed\r\n"); break; } - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); --retries; } @@ -256,12 +256,12 @@ static void wifi_task(void *pvParameters) { printf("WiFi: Connected\n\r"); wifi_alive = 1; } - vTaskDelay(500 / portTICK_RATE_MS); + vTaskDelay(500 / portTICK_PERIOD_MS); } wifi_alive = 0; printf("WiFi: disconnected\n\r"); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } } diff --git a/examples/aws_iot/ssl_connection.c b/examples/aws_iot/ssl_connection.c index be5bb23..24dafb0 100644 --- a/examples/aws_iot/ssl_connection.c +++ b/examples/aws_iot/ssl_connection.c @@ -133,7 +133,7 @@ int ssl_connect(SSLConnection* conn, const char* host, int port) { } handle_error(ret); - vTaskDelay(5000 / portTICK_RATE_MS); + vTaskDelay(5000 / portTICK_PERIOD_MS); } mbedtls_ssl_get_record_expansion(&conn->ssl_ctx); diff --git a/examples/blink/blink.c b/examples/blink/blink.c index 341ac08..9de0f20 100644 --- a/examples/blink/blink.c +++ b/examples/blink/blink.c @@ -19,9 +19,9 @@ void blinkenTask(void *pvParameters) gpio_enable(gpio, GPIO_OUTPUT); while(1) { gpio_write(gpio, 1); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); gpio_write(gpio, 0); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } } @@ -45,9 +45,9 @@ void blinkenRegisterTask(void *pvParameters) IOMUX_GPIO2 = IOMUX_GPIO2_FUNC_GPIO | IOMUX_PIN_OUTPUT_ENABLE; /* change this line if you change 'gpio' */ while(1) { GPIO.OUT_SET = BIT(gpio); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); GPIO.OUT_CLEAR = BIT(gpio); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } } diff --git a/examples/bmp180_i2c/bmp180_i2c.c b/examples/bmp180_i2c/bmp180_i2c.c index d05aca8..1e188bc 100644 --- a/examples/bmp180_i2c/bmp180_i2c.c +++ b/examples/bmp180_i2c/bmp180_i2c.c @@ -26,11 +26,11 @@ typedef struct } my_event_t; // Communication Queue -static xQueueHandle mainqueue; -static xTimerHandle timerHandle; +static QueueHandle_t mainqueue; +static TimerHandle_t timerHandle; // Own BMP180 User Inform Implementation -bool bmp180_i2c_informUser(const xQueueHandle* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure) +bool bmp180_i2c_informUser(const QueueHandle_t* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure) { my_event_t ev; @@ -43,7 +43,7 @@ bool bmp180_i2c_informUser(const xQueueHandle* resultQueue, uint8_t cmd, bmp180_ } // Timer call back -static void bmp180_i2c_timer_cb(xTimerHandle xTimer) +static void bmp180_i2c_timer_cb(TimerHandle_t xTimer) { my_event_t ev; ev.event_type = MY_EVT_TIMER; @@ -55,7 +55,7 @@ static void bmp180_i2c_timer_cb(xTimerHandle xTimer) void bmp180_task(void *pvParameters) { // Received pvParameters is communication queue - xQueueHandle *com_queue = (xQueueHandle *)pvParameters; + QueueHandle_t *com_queue = (QueueHandle_t *)pvParameters; printf("%s: Started user interface task\n", __FUNCTION__); @@ -116,7 +116,7 @@ void user_init(void) xTaskCreate(bmp180_task, "bmp180_task", 256, &mainqueue, 2, NULL); // Create Timer (Trigger a measurement every second) - timerHandle = xTimerCreate("BMP180 Trigger", 1000/portTICK_RATE_MS, pdTRUE, NULL, bmp180_i2c_timer_cb); + timerHandle = xTimerCreate("BMP180 Trigger", 1000/portTICK_PERIOD_MS, pdTRUE, NULL, bmp180_i2c_timer_cb); if (timerHandle != NULL) { diff --git a/examples/bmp280/bmp280_example.c b/examples/bmp280/bmp280_example.c index e80d6b0..c4c7c11 100644 --- a/examples/bmp280/bmp280_example.c +++ b/examples/bmp280/bmp280_example.c @@ -31,14 +31,14 @@ static void bmp280_task_forced(void *pvParameters) while (1) { while (!bmp280_init(&bmp280_dev, ¶ms)) { printf("BMP280 initialization failed\n"); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } bool bme280p = bmp280_dev.id == BME280_CHIP_ID; printf("BMP280: found %s\n", bme280p ? "BME280" : "BMP280"); while(1) { - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); if (!bmp280_force_measurement(&bmp280_dev)) { printf("Failed initiating measurement\n"); break; @@ -72,14 +72,14 @@ static void bmp280_task_normal(void *pvParameters) while (1) { while (!bmp280_init(&bmp280_dev, ¶ms)) { printf("BMP280 initialization failed\n"); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } bool bme280p = bmp280_dev.id == BME280_CHIP_ID; printf("BMP280: found %s\n", bme280p ? "BME280" : "BMP280"); while(1) { - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); if (!bmp280_read_float(&bmp280_dev, &temperature, &pressure, &humidity)) { printf("Temperature/pressure reading failed\n"); break; diff --git a/examples/button/button.c b/examples/button/button.c index 7bebf81..a371f0d 100644 --- a/examples/button/button.c +++ b/examples/button/button.c @@ -34,8 +34,8 @@ void buttonPollTask(void *pvParameters) { taskYIELD(); } - printf("Polled for button press at %dms\r\n", xTaskGetTickCount()*portTICK_RATE_MS); - vTaskDelay(200 / portTICK_RATE_MS); + printf("Polled for button press at %dms\r\n", xTaskGetTickCount()*portTICK_PERIOD_MS); + vTaskDelay(200 / portTICK_PERIOD_MS); } } @@ -50,14 +50,14 @@ void buttonPollTask(void *pvParameters) void buttonIntTask(void *pvParameters) { printf("Waiting for button press interrupt on gpio %d...\r\n", gpio); - xQueueHandle *tsqueue = (xQueueHandle *)pvParameters; + QueueHandle_t *tsqueue = (QueueHandle_t *)pvParameters; gpio_set_interrupt(gpio, int_type); uint32_t last = 0; while(1) { uint32_t button_ts; xQueueReceive(*tsqueue, &button_ts, portMAX_DELAY); - button_ts *= portTICK_RATE_MS; + button_ts *= portTICK_PERIOD_MS; if(last < button_ts-200) { printf("Button interrupt fired at %dms\r\n", button_ts); last = button_ts; @@ -65,7 +65,7 @@ void buttonIntTask(void *pvParameters) } } -static xQueueHandle tsqueue; +static QueueHandle_t tsqueue; void GPIO_HANDLER(void) { diff --git a/examples/dht_sensor/dht_sensor.c b/examples/dht_sensor/dht_sensor.c index 32988b1..22f110e 100644 --- a/examples/dht_sensor/dht_sensor.c +++ b/examples/dht_sensor/dht_sensor.c @@ -38,7 +38,7 @@ void dhtMeasurementTask(void *pvParameters) } // Three second delay... - vTaskDelay(3000 / portTICK_RATE_MS); + vTaskDelay(3000 / portTICK_PERIOD_MS); } } diff --git a/examples/ds18b20_broadcaster/ds18b20_broadcaster.c b/examples/ds18b20_broadcaster/ds18b20_broadcaster.c index e5bcfea..5b7f1dc 100644 --- a/examples/ds18b20_broadcaster/ds18b20_broadcaster.c +++ b/examples/ds18b20_broadcaster/ds18b20_broadcaster.c @@ -88,7 +88,7 @@ void broadcast_temperature(void *pvParameters) } netbuf_delete(buf); // De-allocate packet buffer } - vTaskDelay(1000/portTICK_RATE_MS); + vTaskDelay(1000/portTICK_PERIOD_MS); } err = netconn_disconnect(conn); @@ -97,7 +97,7 @@ void broadcast_temperature(void *pvParameters) err = netconn_delete(conn); printf("%s : Deleted connection (%s)\n", __FUNCTION__, lwip_strerr(err)); - vTaskDelay(1000/portTICK_RATE_MS); + vTaskDelay(1000/portTICK_PERIOD_MS); } } diff --git a/examples/ds18b20_onewire/ds18b20_onewire.c b/examples/ds18b20_onewire/ds18b20_onewire.c index 098f59f..73ff0b7 100644 --- a/examples/ds18b20_onewire/ds18b20_onewire.c +++ b/examples/ds18b20_onewire/ds18b20_onewire.c @@ -64,7 +64,7 @@ void print_temperature(void *pvParameters) { // Wait for a little bit between each sample (note that the // ds18b20_measure_and_read_multi operation already takes at // least 750ms to run, so this is on top of that delay). - vTaskDelay(LOOP_DELAY_MS / portTICK_RATE_MS); + vTaskDelay(LOOP_DELAY_MS / portTICK_PERIOD_MS); } } } diff --git a/examples/experiments/timers/timers.c b/examples/experiments/timers/timers.c index bc3aa2b..a2e5e66 100644 --- a/examples/experiments/timers/timers.c +++ b/examples/experiments/timers/timers.c @@ -93,7 +93,7 @@ void timerRegTask(void *pvParameters) printf("frc2 handler called %d times, last value 0x%08x\r\n", frc2_handler_call_count, frc2_last_count_val); - vTaskDelay(500 / portTICK_RATE_MS); + vTaskDelay(500 / portTICK_PERIOD_MS); } } diff --git a/examples/experiments/unaligned_load/unaligned_load.c b/examples/experiments/unaligned_load/unaligned_load.c index 984a566..f577ded 100644 --- a/examples/experiments/unaligned_load/unaligned_load.c +++ b/examples/experiments/unaligned_load/unaligned_load.c @@ -229,7 +229,7 @@ void user_init(void) test_isr(); test_sign_extension(); - xTaskHandle taskHandle; + TaskHandle_t taskHandle; xTaskCreate(test_system_interaction, "interactionTask", 256, &taskHandle, 2, NULL); } @@ -304,7 +304,7 @@ static void test_system_interaction() */ } uint32_t ticks = xTaskGetTickCount() - start; - printf("Timer interaction test PASSED after %dms.\n", ticks*portTICK_RATE_MS); + printf("Timer interaction test PASSED after %dms.\n", ticks*portTICK_PERIOD_MS); abort(); } diff --git a/examples/fatfs_rtc/main.c b/examples/fatfs_rtc/main.c index 0c818d0..613eb6f 100644 --- a/examples/fatfs_rtc/main.c +++ b/examples/fatfs_rtc/main.c @@ -118,7 +118,7 @@ void rewrite_file_task(void *p) } while (false); - vTaskDelay(DELAY_MS / portTICK_RATE_MS); + vTaskDelay(DELAY_MS / portTICK_PERIOD_MS); } } diff --git a/examples/http_get/http_get.c b/examples/http_get/http_get.c index 408d5b1..0fe0ae1 100644 --- a/examples/http_get/http_get.c +++ b/examples/http_get/http_get.c @@ -43,7 +43,7 @@ void http_get_task(void *pvParameters) printf("DNS lookup failed err=%d res=%p\r\n", err, res); if(res) freeaddrinfo(res); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); failures++; continue; } @@ -55,7 +55,7 @@ void http_get_task(void *pvParameters) if(s < 0) { printf("... Failed to allocate socket.\r\n"); freeaddrinfo(res); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); failures++; continue; } @@ -66,7 +66,7 @@ void http_get_task(void *pvParameters) close(s); freeaddrinfo(res); printf("... socket connect failed.\r\n"); - vTaskDelay(4000 / portTICK_RATE_MS); + vTaskDelay(4000 / portTICK_PERIOD_MS); failures++; continue; } @@ -81,7 +81,7 @@ void http_get_task(void *pvParameters) if (write(s, req, strlen(req)) < 0) { printf("... socket send failed\r\n"); close(s); - vTaskDelay(4000 / portTICK_RATE_MS); + vTaskDelay(4000 / portTICK_PERIOD_MS); failures++; continue; } @@ -106,7 +106,7 @@ void http_get_task(void *pvParameters) printf("successes = %d failures = %d\r\n", successes, failures); for(int countdown = 10; countdown >= 0; countdown--) { printf("%d... ", countdown); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } printf("\r\nStarting again!\r\n"); } diff --git a/examples/http_get_mbedtls/http_get_mbedtls.c b/examples/http_get_mbedtls/http_get_mbedtls.c index fa7c114..596a1f7 100644 --- a/examples/http_get_mbedtls/http_get_mbedtls.c +++ b/examples/http_get_mbedtls/http_get_mbedtls.c @@ -181,7 +181,7 @@ void http_get_task(void *pvParameters) err_t dns_err; ip_addr_t host_ip; do { - vTaskDelay(500 / portTICK_RATE_MS); + vTaskDelay(500 / portTICK_PERIOD_MS); dns_err = netconn_gethostbyname(WEB_SERVER, &host_ip); } while(dns_err != ERR_OK); printf("done.\n"); @@ -313,7 +313,7 @@ void http_get_task(void *pvParameters) printf("\n\nsuccesses = %d failures = %d\n", successes, failures); for(int countdown = successes ? 10 : 5; countdown >= 0; countdown--) { printf("%d... ", countdown); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } printf("\nStarting again!\n"); } diff --git a/examples/i2s_audio/i2s_audio_example.c b/examples/i2s_audio/i2s_audio_example.c index ddb1387..1972692 100644 --- a/examples/i2s_audio/i2s_audio_example.c +++ b/examples/i2s_audio/i2s_audio_example.c @@ -57,7 +57,7 @@ static dma_descriptor_t dma_block_list[DMA_QUEUE_SIZE]; static uint8_t dma_buffer[DMA_QUEUE_SIZE][DMA_BUFFER_SIZE]; // Queue of empty DMA blocks -static xQueueHandle dma_queue; +static QueueHandle_t dma_queue; /** * Create a circular list of DMA descriptors @@ -183,7 +183,7 @@ void play_task(void *pvParameters) printf("underrun counter: %d\n", underrun_counter); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } close(fd); diff --git a/examples/mqtt_client/mqtt_client.c b/examples/mqtt_client/mqtt_client.c index 2b9e764..6efbd2d 100644 --- a/examples/mqtt_client/mqtt_client.c +++ b/examples/mqtt_client/mqtt_client.c @@ -24,18 +24,18 @@ #define MQTT_USER NULL #define MQTT_PASS NULL -xSemaphoreHandle wifi_alive; -xQueueHandle publish_queue; +SemaphoreHandle_t wifi_alive; +QueueHandle_t publish_queue; #define PUB_MSG_LEN 16 static void beat_task(void *pvParameters) { - portTickType xLastWakeTime = xTaskGetTickCount(); + TickType_t xLastWakeTime = xTaskGetTickCount(); char msg[PUB_MSG_LEN]; int count = 0; while (1) { - vTaskDelayUntil(&xLastWakeTime, 10000 / portTICK_RATE_MS); + vTaskDelayUntil(&xLastWakeTime, 10000 / portTICK_PERIOD_MS); printf("beat\r\n"); snprintf(msg, PUB_MSG_LEN, "Beat %d\r\n", count++); if (xQueueSend(publish_queue, (void *)msg, 0) == pdFALSE) { @@ -190,7 +190,7 @@ static void wifi_task(void *pvParameters) printf("WiFi: connection failed\r\n"); break; } - vTaskDelay( 1000 / portTICK_RATE_MS ); + vTaskDelay( 1000 / portTICK_PERIOD_MS ); --retries; } if (status == STATION_GOT_IP) { @@ -205,7 +205,7 @@ static void wifi_task(void *pvParameters) } printf("WiFi: disconnected\n\r"); sdk_wifi_station_disconnect(); - vTaskDelay( 1000 / portTICK_RATE_MS ); + vTaskDelay( 1000 / portTICK_PERIOD_MS ); } } diff --git a/examples/ota_basic/ota_basic.c b/examples/ota_basic/ota_basic.c index 5083539..23102dd 100644 --- a/examples/ota_basic/ota_basic.c +++ b/examples/ota_basic/ota_basic.c @@ -109,10 +109,10 @@ void tftp_client_task(void *pvParameters) */ while(1) { tftpclient_download_and_verify_file1(slot, &conf); - vTaskDelay(5000 / portTICK_RATE_MS); + vTaskDelay(5000 / portTICK_PERIOD_MS); tftpclient_download_file2(slot); - vTaskDelay(5000 / portTICK_RATE_MS); + vTaskDelay(5000 / portTICK_PERIOD_MS); } } diff --git a/examples/posix_fs/posix_fs_example.c b/examples/posix_fs/posix_fs_example.c index 22877c4..610f58d 100644 --- a/examples/posix_fs/posix_fs_example.c +++ b/examples/posix_fs/posix_fs_example.c @@ -29,14 +29,14 @@ void test_task(void *pvParameters) esp_spiffs_mount(); while (1) { - vTaskDelay(5000 / portTICK_RATE_MS); + vTaskDelay(5000 / portTICK_PERIOD_MS); if (fs_load_test_run(100)) { printf("PASS\n"); } else { printf("FAIL\n"); } - vTaskDelay(5000 / portTICK_RATE_MS); + vTaskDelay(5000 / portTICK_PERIOD_MS); float write_rate, read_rate; if (fs_speed_test_run(get_current_time, &write_rate, &read_rate)) { printf("Read speed: %.0f bytes/s\n", read_rate * 1000); diff --git a/examples/simple/simple.c b/examples/simple/simple.c index 8134bb9..2f29df1 100644 --- a/examples/simple/simple.c +++ b/examples/simple/simple.c @@ -8,7 +8,7 @@ void task1(void *pvParameters) { - xQueueHandle *queue = (xQueueHandle *)pvParameters; + QueueHandle_t *queue = (QueueHandle_t *)pvParameters; printf("Hello from task1!\r\n"); uint32_t count = 0; while(1) { @@ -21,7 +21,7 @@ void task1(void *pvParameters) void task2(void *pvParameters) { printf("Hello from task 2!\r\n"); - xQueueHandle *queue = (xQueueHandle *)pvParameters; + QueueHandle_t *queue = (QueueHandle_t *)pvParameters; while(1) { uint32_t count; if(xQueueReceive(*queue, &count, 1000)) { @@ -32,7 +32,7 @@ void task2(void *pvParameters) } } -static xQueueHandle mainqueue; +static QueueHandle_t mainqueue; void user_init(void) { diff --git a/examples/sntp/sntp_example.c b/examples/sntp/sntp_example.c index a68365a..0a933a2 100644 --- a/examples/sntp/sntp_example.c +++ b/examples/sntp/sntp_example.c @@ -27,7 +27,7 @@ #define SNTP_SERVERS "0.pool.ntp.org", "1.pool.ntp.org", \ "2.pool.ntp.org", "3.pool.ntp.org" -#define vTaskDelayMs(ms) vTaskDelay((ms)/portTICK_RATE_MS) +#define vTaskDelayMs(ms) vTaskDelay((ms)/portTICK_PERIOD_MS) #define UNUSED_ARG(x) (void)x void sntp_tsk(void *pvParameters) diff --git a/examples/spiffs/spiffs_example.c b/examples/spiffs/spiffs_example.c index 5252a92..2d0dcbf 100644 --- a/examples/spiffs/spiffs_example.c +++ b/examples/spiffs/spiffs_example.c @@ -96,7 +96,7 @@ void test_task(void *pvParameters) } while (1) { - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); example_write_file(); diff --git a/examples/ssd1306_i2c/ssd1306_i2c.c b/examples/ssd1306_i2c/ssd1306_i2c.c index 8ee64ba..7dadfb4 100644 --- a/examples/ssd1306_i2c/ssd1306_i2c.c +++ b/examples/ssd1306_i2c/ssd1306_i2c.c @@ -22,7 +22,7 @@ static uint8_t buffer[SSD1306_ROWS * SSD1306_COLS / 8]; static void ssd1306_task(void *pvParameters) { printf("%s: Started user interface task\n", __FUNCTION__); - vTaskDelay(1000/portTICK_RATE_MS); + vTaskDelay(1000/portTICK_PERIOD_MS); if (ssd1306_load_xbm(image_bits, buffer)) @@ -30,14 +30,14 @@ static void ssd1306_task(void *pvParameters) ssd1306_set_whole_display_lighting(false); while (1) { - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); printf("%s: steel alive\n", __FUNCTION__); } error_loop: printf("%s: error while loading framebuffer into SSD1306\n", __func__); for(;;){ - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); printf("%s: error loop\n", __FUNCTION__); } } @@ -54,12 +54,12 @@ void user_init(void) if (ssd1306_init()){ for (;;) { printf("%s: failed to init SSD1306 lcd\n", __func__); - vTaskDelay(1000/portTICK_RATE_MS); + vTaskDelay(1000/portTICK_PERIOD_MS); } } ssd1306_set_whole_display_lighting(true); - vTaskDelay(1000/portTICK_RATE_MS); + vTaskDelay(1000/portTICK_PERIOD_MS); // Create user interface task xTaskCreate(ssd1306_task, "ssd1306_task", 256, NULL, 2, NULL); } diff --git a/examples/terminal/terminal.c b/examples/terminal/terminal.c index 83ea734..6f8b940 100644 --- a/examples/terminal/terminal.c +++ b/examples/terminal/terminal.c @@ -59,7 +59,7 @@ static void cmd_help(uint32_t argc, char *argv[]) static void cmd_sleep(uint32_t argc, char *argv[]) { printf("Type away while I take a 2 second nap (ie. let you test the UART HW FIFO\n"); - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); } static void handle_command(char *cmd) diff --git a/examples/tsl2561/tsl2561_example.c b/examples/tsl2561/tsl2561_example.c index 447f669..b97804b 100644 --- a/examples/tsl2561/tsl2561_example.c +++ b/examples/tsl2561/tsl2561_example.c @@ -56,7 +56,7 @@ void tsl2561MeasurementTask(void *pvParameters) } // 0.1 second delay - vTaskDelay(100 / portTICK_RATE_MS); + vTaskDelay(100 / portTICK_PERIOD_MS); } } diff --git a/examples/ws2812_i2s/ws2812_i2s_colour_loop.c b/examples/ws2812_i2s/ws2812_i2s_colour_loop.c index 7644494..05f47c4 100644 --- a/examples/ws2812_i2s/ws2812_i2s_colour_loop.c +++ b/examples/ws2812_i2s/ws2812_i2s_colour_loop.c @@ -70,7 +70,7 @@ static void demo(void *pvParameters) sizeof(ws2812_pixel_t)); ws2812_i2s_update(pixels); - vTaskDelay(20 / portTICK_RATE_MS); + vTaskDelay(20 / portTICK_PERIOD_MS); } } } diff --git a/examples/ws2812_rainbow/ws2812_rainbow.c b/examples/ws2812_rainbow/ws2812_rainbow.c index 034b099..1f67775 100644 --- a/examples/ws2812_rainbow/ws2812_rainbow.c +++ b/examples/ws2812_rainbow/ws2812_rainbow.c @@ -18,7 +18,7 @@ #include "ws2812.h" -#define delay_ms(ms) vTaskDelay((ms) / portTICK_RATE_MS) +#define delay_ms(ms) vTaskDelay((ms) / portTICK_PERIOD_MS) /** GPIO number used to control the RGBs */ diff --git a/extras/bmp180/README.md b/extras/bmp180/README.md index a8c41f5..9da464b 100644 --- a/extras/bmp180/README.md +++ b/extras/bmp180/README.md @@ -34,7 +34,7 @@ As all data aqquired from the BMP180/BMP085 is provided to the `bmp180_informUse ``` // Own BMP180 User Inform Implementation -bool my_informUser(const xQueueHandle* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure) { +bool my_informUser(const QueueHandle_t* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure) { my_event_t ev; ev.event_type = MY_EVT_BMP180; diff --git a/extras/bmp180/bmp180.c b/extras/bmp180/bmp180.c index 805a620..c3c8afd 100644 --- a/extras/bmp180/bmp180.c +++ b/extras/bmp180/bmp180.c @@ -214,20 +214,20 @@ bool bmp180_measure(bmp180_constants_t *c, int32_t *temperature, typedef struct { uint8_t cmd; - const xQueueHandle* resultQueue; + const QueueHandle_t* resultQueue; } bmp180_command_t; -// Just works due to the fact that xQueueHandle is a "void *" -static xQueueHandle bmp180_rx_queue = NULL; -static xTaskHandle bmp180_task_handle = NULL; +// Just works due to the fact that QueueHandle_t is a "void *" +static QueueHandle_t bmp180_rx_queue = NULL; +static TaskHandle_t bmp180_task_handle = NULL; // // Forward declarations // -static bool bmp180_informUser_Impl(const xQueueHandle* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure); +static bool bmp180_informUser_Impl(const QueueHandle_t* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure); // Set default implementation .. User gets result as bmp180_result_t event -bool (*bmp180_informUser)(const xQueueHandle* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure) = bmp180_informUser_Impl; +bool (*bmp180_informUser)(const QueueHandle_t* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure) = bmp180_informUser_Impl; // I2C Driver Task static void bmp180_driver_task(void *pvParameters) @@ -295,7 +295,7 @@ static bool bmp180_createTask() } // Default user inform implementation -static bool bmp180_informUser_Impl(const xQueueHandle* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure) +static bool bmp180_informUser_Impl(const QueueHandle_t* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure) { bmp180_result_t result; @@ -328,7 +328,7 @@ bool bmp180_init(uint8_t scl, uint8_t sda) return result; } -void bmp180_trigger_measurement(const xQueueHandle* resultQueue) +void bmp180_trigger_measurement(const QueueHandle_t* resultQueue) { bmp180_command_t c; @@ -339,7 +339,7 @@ void bmp180_trigger_measurement(const xQueueHandle* resultQueue) } -void bmp180_trigger_pressure_measurement(const xQueueHandle* resultQueue) +void bmp180_trigger_pressure_measurement(const QueueHandle_t* resultQueue) { bmp180_command_t c; @@ -349,7 +349,7 @@ void bmp180_trigger_pressure_measurement(const xQueueHandle* resultQueue) xQueueSend(bmp180_rx_queue, &c, 0); } -void bmp180_trigger_temperature_measurement(const xQueueHandle* resultQueue) +void bmp180_trigger_temperature_measurement(const QueueHandle_t* resultQueue) { bmp180_command_t c; diff --git a/extras/bmp180/bmp180.h b/extras/bmp180/bmp180.h index e7fc470..2f46150 100644 --- a/extras/bmp180/bmp180.h +++ b/extras/bmp180/bmp180.h @@ -45,16 +45,16 @@ typedef struct bool bmp180_init(uint8_t scl, uint8_t sda); // Trigger a "complete" measurement (temperature and pressure will be valid when given to "bmp180_informUser) -void bmp180_trigger_measurement(const xQueueHandle* resultQueue); +void bmp180_trigger_measurement(const QueueHandle_t* resultQueue); // Trigger a "temperature only" measurement (only temperature will be valid when given to "bmp180_informUser) -void bmp180_trigger_temperature_measurement(const xQueueHandle* resultQueue); +void bmp180_trigger_temperature_measurement(const QueueHandle_t* resultQueue); // Trigger a "pressure only" measurement (only pressure will be valid when given to "bmp180_informUser) -void bmp180_trigger_pressure_measurement(const xQueueHandle* resultQueue); +void bmp180_trigger_pressure_measurement(const QueueHandle_t* resultQueue); // Give the user the chance to create it's own handler -extern bool (*bmp180_informUser)(const xQueueHandle* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure); +extern bool (*bmp180_informUser)(const QueueHandle_t* resultQueue, uint8_t cmd, bmp180_temp_t temperature, bmp180_press_t pressure); // Calibration constants typedef struct diff --git a/extras/bmp280/README.md b/extras/bmp280/README.md index 9faed88..09e0115 100644 --- a/extras/bmp280/README.md +++ b/extras/bmp280/README.md @@ -72,7 +72,7 @@ while(1) { printf("Pressure: %.2f Pa, Temperature: %.2f C", pressure, temperature); if (bme280p) printf(", Humidity: %.2f\n", humidity); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } ``` @@ -96,7 +96,7 @@ while(1) { printf(", Humidity: %.2f\n", humidity); else printf("\n"); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } ``` diff --git a/extras/cpp_support/include/countdown.hpp b/extras/cpp_support/include/countdown.hpp index 87c8f3f..81f3853 100644 --- a/extras/cpp_support/include/countdown.hpp +++ b/extras/cpp_support/include/countdown.hpp @@ -35,7 +35,7 @@ namespace esp_open_rtos { namespace timer { -#define __millis() (xTaskGetTickCount() * portTICK_RATE_MS) +#define __millis() (xTaskGetTickCount() * portTICK_PERIOD_MS) /****************************************************************************************************************** * countdown_t @@ -93,7 +93,7 @@ public: } private: - portTickType interval_end_ms; + TickType_t interval_end_ms; }; } // namespace timer { diff --git a/extras/cpp_support/include/mutex.hpp b/extras/cpp_support/include/mutex.hpp index aaf29db..62b490c 100644 --- a/extras/cpp_support/include/mutex.hpp +++ b/extras/cpp_support/include/mutex.hpp @@ -86,7 +86,7 @@ public: */ inline int try_lock(unsigned long ms) { - return (xSemaphoreTake(mutex, ms / portTICK_RATE_MS) == pdTRUE) ? 0 : -1; + return (xSemaphoreTake(mutex, ms / portTICK_PERIOD_MS) == pdTRUE) ? 0 : -1; } /** * @@ -98,7 +98,7 @@ public: } private: - xSemaphoreHandle mutex; + SemaphoreHandle_t mutex; // Disable copy construction and assignment. mutex_t (const mutex_t&); diff --git a/extras/cpp_support/include/queue.hpp b/extras/cpp_support/include/queue.hpp index 047ce29..38a0493 100644 --- a/extras/cpp_support/include/queue.hpp +++ b/extras/cpp_support/include/queue.hpp @@ -83,7 +83,7 @@ public: */ inline int post(const Data& data, unsigned long ms = 0) { - return (xQueueSend(queue, &data, ms / portTICK_RATE_MS) == pdTRUE) ? 0 : -1; + return (xQueueSend(queue, &data, ms / portTICK_PERIOD_MS) == pdTRUE) ? 0 : -1; } /** * @@ -93,7 +93,7 @@ public: */ inline int receive(Data& data, unsigned long ms = 0) { - return (xQueueReceive(queue, &data, ms / portTICK_RATE_MS) == pdTRUE) ? 0 : -1; + return (xQueueReceive(queue, &data, ms / portTICK_PERIOD_MS) == pdTRUE) ? 0 : -1; } /** * @@ -110,7 +110,7 @@ public: } private: - xQueueHandle queue; + QueueHandle_t queue; // Disable copy construction. queue_t (const queue_t&); diff --git a/extras/cpp_support/include/task.hpp b/extras/cpp_support/include/task.hpp index e87e32a..75a716d 100644 --- a/extras/cpp_support/include/task.hpp +++ b/extras/cpp_support/include/task.hpp @@ -66,7 +66,7 @@ protected: */ void sleep(unsigned long ms) { - vTaskDelay(ms / portTICK_RATE_MS); + vTaskDelay(ms / portTICK_PERIOD_MS); } /** * @@ -74,7 +74,7 @@ protected: */ inline unsigned long millis() { - return xTaskGetTickCount() * portTICK_RATE_MS; + return xTaskGetTickCount() * portTICK_PERIOD_MS; } private: diff --git a/extras/dhcpserver/dhcpserver.c b/extras/dhcpserver/dhcpserver.c index f1e8f66..d87daab 100644 --- a/extras/dhcpserver/dhcpserver.c +++ b/extras/dhcpserver/dhcpserver.c @@ -49,7 +49,7 @@ typedef struct { /* Only one DHCP server task can run at once, so we have global state for it. */ -static xTaskHandle dhcpserver_task_handle=NULL; +static TaskHandle_t dhcpserver_task_handle = NULL; static server_state_t *state; /* Handlers for various kinds of incoming DHCP messages */ diff --git a/extras/ds18b20/ds18b20.c b/extras/ds18b20/ds18b20.c index 466422e..36449d7 100644 --- a/extras/ds18b20/ds18b20.c +++ b/extras/ds18b20/ds18b20.c @@ -16,7 +16,7 @@ #define DS18B20_ALARMSEARCH 0xEC #define DS18B20_CONVERT_T 0x44 -#define os_sleep_ms(x) vTaskDelay(((x) + portTICK_RATE_MS - 1) / portTICK_RATE_MS) +#define os_sleep_ms(x) vTaskDelay(((x) + portTICK_PERIOD_MS - 1) / portTICK_PERIOD_MS) #define DS18B20_FAMILY_ID 0x28 #define DS18S20_FAMILY_ID 0x10 @@ -46,7 +46,7 @@ uint8_t ds18b20_read_all(uint8_t pin, ds_sensor_t *result) { onewire_write(pin, DS18B20_CONVERT_T); onewire_power(pin); - vTaskDelay(750 / portTICK_RATE_MS); + vTaskDelay(750 / portTICK_PERIOD_MS); onewire_reset(pin); onewire_select(pin, addr); @@ -88,7 +88,7 @@ float ds18b20_read_single(uint8_t pin) { onewire_write(pin, DS18B20_CONVERT_T); onewire_power(pin); - vTaskDelay(750 / portTICK_RATE_MS); + vTaskDelay(750 / portTICK_PERIOD_MS); onewire_reset(pin); onewire_skip_rom(pin); diff --git a/extras/fatfs/ffconf.h b/extras/fatfs/ffconf.h index 00b79a4..7962e2a 100644 --- a/extras/fatfs/ffconf.h +++ b/extras/fatfs/ffconf.h @@ -275,7 +275,7 @@ #ifndef _FS_TIMEOUT #define _FS_TIMEOUT 1000 #endif -#define _SYNC_t xSemaphoreHandle +#define _SYNC_t SemaphoreHandle_t /* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs / module itself. Note that regardless of this option, file access to different / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() diff --git a/extras/fatfs/syscall.c b/extras/fatfs/syscall.c index 0fff89a..eb54f37 100644 --- a/extras/fatfs/syscall.c +++ b/extras/fatfs/syscall.c @@ -13,7 +13,7 @@ * synchronization object, such as semaphore and mutex. When a 0 is returned, * the f_mount() function fails with FR_INT_ERR. */ -int ff_cre_syncobj(BYTE vol, xSemaphoreHandle *sobj) +int ff_cre_syncobj(BYTE vol, SemaphoreHandle_t *sobj) { int ret; @@ -29,7 +29,7 @@ int ff_cre_syncobj(BYTE vol, xSemaphoreHandle *sobj) * object that created with ff_cre_syncobj() function. When a 0 is returned, * the f_mount() function fails with FR_INT_ERR. */ -int ff_del_syncobj(xSemaphoreHandle sobj) +int ff_del_syncobj(SemaphoreHandle_t sobj) { vSemaphoreDelete(sobj); return 1; @@ -40,7 +40,7 @@ int ff_del_syncobj(xSemaphoreHandle sobj) * This function is called on entering file functions to lock the volume. * When a 0 is returned, the file function fails with FR_TIMEOUT. */ -int ff_req_grant(xSemaphoreHandle sobj) +int ff_req_grant(SemaphoreHandle_t sobj) { return (int)(xSemaphoreTake(sobj, _FS_TIMEOUT) == pdTRUE); } @@ -49,7 +49,7 @@ int ff_req_grant(xSemaphoreHandle sobj) * Release Grant to Access the Volume * This function is called on leaving file functions to unlock the volume. */ -void ff_rel_grant(xSemaphoreHandle sobj) +void ff_rel_grant(SemaphoreHandle_t sobj) { xSemaphoreGive(sobj); } diff --git a/extras/paho_mqtt_c/MQTTESP8266.c b/extras/paho_mqtt_c/MQTTESP8266.c index 83e257f..e7a14d9 100644 --- a/extras/paho_mqtt_c/MQTTESP8266.c +++ b/extras/paho_mqtt_c/MQTTESP8266.c @@ -30,7 +30,7 @@ char mqtt_timer_expired(mqtt_timer_t* timer) { - portTickType now = xTaskGetTickCount(); + TickType_t now = xTaskGetTickCount(); int32_t left = timer->end_time - now; return (left < 0); } @@ -38,8 +38,8 @@ char mqtt_timer_expired(mqtt_timer_t* timer) void mqtt_timer_countdown_ms(mqtt_timer_t* timer, unsigned int timeout) { - portTickType now = xTaskGetTickCount(); - timer->end_time = now + timeout / portTICK_RATE_MS; + TickType_t now = xTaskGetTickCount(); + timer->end_time = now + timeout / portTICK_PERIOD_MS; } @@ -51,9 +51,9 @@ void mqtt_timer_countdown(mqtt_timer_t* timer, unsigned int timeout) int mqtt_timer_left_ms(mqtt_timer_t* timer) { - portTickType now = xTaskGetTickCount(); + TickType_t now = xTaskGetTickCount(); int32_t left = timer->end_time - now; - return (left < 0) ? 0 : left / portTICK_RATE_MS; + return (left < 0) ? 0 : left / portTICK_PERIOD_MS; } @@ -73,7 +73,7 @@ int mqtt_esp_read(mqtt_network_t* n, unsigned char* buffer, int len, int timeou FD_ZERO(&fdset); FD_SET(n->my_socket, &fdset); // It seems tv_sec actually means FreeRTOS tick - tv.tv_sec = timeout_ms / portTICK_RATE_MS; + tv.tv_sec = timeout_ms / portTICK_PERIOD_MS; tv.tv_usec = 0; rc = select(n->my_socket + 1, &fdset, 0, 0, &tv); if ((rc > 0) && (FD_ISSET(n->my_socket, &fdset))) @@ -98,7 +98,7 @@ int mqtt_esp_write(mqtt_network_t* n, unsigned char* buffer, int len, int timeo FD_ZERO(&fdset); FD_SET(n->my_socket, &fdset); // It seems tv_sec actually means FreeRTOS tick - tv.tv_sec = timeout_ms / portTICK_RATE_MS; + tv.tv_sec = timeout_ms / portTICK_PERIOD_MS; tv.tv_usec = 0; rc = select(n->my_socket + 1, 0, &fdset, 0, &tv); if ((rc > 0) && (FD_ISSET(n->my_socket, &fdset))) diff --git a/extras/paho_mqtt_c/MQTTESP8266.h b/extras/paho_mqtt_c/MQTTESP8266.h index 18e9cdf..05380e8 100644 --- a/extras/paho_mqtt_c/MQTTESP8266.h +++ b/extras/paho_mqtt_c/MQTTESP8266.h @@ -28,7 +28,7 @@ typedef struct mqtt_timer mqtt_timer_t; struct mqtt_timer { - portTickType end_time; + TickType_t end_time; }; typedef struct mqtt_network mqtt_network_t; diff --git a/extras/stdin_uart_interrupt/stdin_uart_interrupt.c b/extras/stdin_uart_interrupt/stdin_uart_interrupt.c index c836651..3319588 100644 --- a/extras/stdin_uart_interrupt/stdin_uart_interrupt.c +++ b/extras/stdin_uart_interrupt/stdin_uart_interrupt.c @@ -40,7 +40,7 @@ #define UART0_RX_SIZE (128) // ESP8266 UART HW FIFO size -static xSemaphoreHandle uart0_sem = NULL; +static SemaphoreHandle_t uart0_sem = NULL; static bool inited = false; static void uart0_rx_init(void); diff --git a/extras/tsl2561/tsl2561.c b/extras/tsl2561/tsl2561.c index 6a8ac74..36c52e9 100644 --- a/extras/tsl2561/tsl2561.c +++ b/extras/tsl2561/tsl2561.c @@ -195,13 +195,13 @@ static void get_channel_data(tsl2561_t *device, uint16_t *channel0, uint16_t *ch switch (device->integration_time) { case TSL2561_INTEGRATION_13MS: - vTaskDelay(TSL2561_INTEGRATION_TIME_13MS / portTICK_RATE_MS); + vTaskDelay(TSL2561_INTEGRATION_TIME_13MS / portTICK_PERIOD_MS); break; case TSL2561_INTEGRATION_101MS: - vTaskDelay(TSL2561_INTEGRATION_TIME_101MS / portTICK_RATE_MS); + vTaskDelay(TSL2561_INTEGRATION_TIME_101MS / portTICK_PERIOD_MS); break; default: - vTaskDelay(TSL2561_INTEGRATION_TIME_402MS / portTICK_RATE_MS); + vTaskDelay(TSL2561_INTEGRATION_TIME_402MS / portTICK_PERIOD_MS); break; } diff --git a/include/etstimer.h b/include/etstimer.h index 1187ab9..36e1a59 100644 --- a/include/etstimer.h +++ b/include/etstimer.h @@ -28,7 +28,7 @@ typedef void ETSTimerFunc(void *); typedef struct ETSTimer_st { struct ETSTimer_st *timer_next; - xTimerHandle timer_handle; + TimerHandle_t timer_handle; uint32_t _unknown; uint32_t timer_ms; ETSTimerFunc *timer_func; diff --git a/lwip/include/arch/sys_arch.h b/lwip/include/arch/sys_arch.h index ac616c9..0dfa482 100644 --- a/lwip/include/arch/sys_arch.h +++ b/lwip/include/arch/sys_arch.h @@ -39,14 +39,14 @@ /* MBOX primitives */ -#define SYS_MBOX_NULL ( ( xQueueHandle ) NULL ) -#define SYS_SEM_NULL ( ( xSemaphoreHandle ) NULL ) +#define SYS_MBOX_NULL ( ( QueueHandle_t ) NULL ) +#define SYS_SEM_NULL ( ( SemaphoreHandle_t ) NULL ) #define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE -typedef xSemaphoreHandle sys_sem_t; -typedef xSemaphoreHandle sys_mutex_t; -typedef xQueueHandle sys_mbox_t; -typedef xTaskHandle sys_thread_t; +typedef SemaphoreHandle_t sys_sem_t; +typedef SemaphoreHandle_t sys_mutex_t; +typedef QueueHandle_t sys_mbox_t; +typedef TaskHandle_t sys_thread_t; #define sys_mbox_valid( x ) ( ( ( *x ) == NULL) ? pdFALSE : pdTRUE ) #define sys_mbox_set_invalid( x ) ( ( *x ) = NULL ) diff --git a/lwip/sys_arch.c b/lwip/sys_arch.c index 51c29e0..87d8450 100644 --- a/lwip/sys_arch.c +++ b/lwip/sys_arch.c @@ -159,7 +159,7 @@ portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; } else { - xReturn = xQueueSend( *pxMailBox, &pxMessageToPost, ( portTickType ) 0 ); + xReturn = xQueueSend( *pxMailBox, &pxMessageToPost, ( TickType_t ) 0 ); } if( xReturn == pdPASS ) @@ -204,7 +204,7 @@ portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; u32_t sys_arch_mbox_fetch( sys_mbox_t *pxMailBox, void **ppvBuffer, u32_t ulTimeOut ) { void *pvDummy; -portTickType xStartTime, xEndTime, xElapsed; +TickType_t xStartTime, xEndTime, xElapsed; unsigned long ulReturn; xStartTime = xTaskGetTickCount(); @@ -218,10 +218,10 @@ unsigned long ulReturn; { configASSERT( is_inside_isr() == ( portBASE_TYPE ) 0 ); - if( pdTRUE == xQueueReceive( *pxMailBox, &( *ppvBuffer ), ulTimeOut/ portTICK_RATE_MS ) ) + if( pdTRUE == xQueueReceive( *pxMailBox, &( *ppvBuffer ), ulTimeOut/ portTICK_PERIOD_MS ) ) { xEndTime = xTaskGetTickCount(); - xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS; + xElapsed = ( xEndTime - xStartTime ) * portTICK_PERIOD_MS; ulReturn = xElapsed; } @@ -236,7 +236,7 @@ unsigned long ulReturn; { while( pdTRUE != xQueueReceive( *pxMailBox, &( *ppvBuffer ), portMAX_DELAY ) ); xEndTime = xTaskGetTickCount(); - xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS; + xElapsed = ( xEndTime - xStartTime ) * portTICK_PERIOD_MS; if( xElapsed == 0UL ) { @@ -358,17 +358,17 @@ err_t xReturn = ERR_MEM; *---------------------------------------------------------------------------*/ u32_t sys_arch_sem_wait( sys_sem_t *pxSemaphore, u32_t ulTimeout ) { -portTickType xStartTime, xEndTime, xElapsed; +TickType_t xStartTime, xEndTime, xElapsed; unsigned long ulReturn; xStartTime = xTaskGetTickCount(); if( ulTimeout != 0UL ) { - if( xSemaphoreTake( *pxSemaphore, ulTimeout / portTICK_RATE_MS ) == pdTRUE ) + if( xSemaphoreTake( *pxSemaphore, ulTimeout / portTICK_PERIOD_MS ) == pdTRUE ) { xEndTime = xTaskGetTickCount(); - xElapsed = (xEndTime - xStartTime) * portTICK_RATE_MS; + xElapsed = (xEndTime - xStartTime) * portTICK_PERIOD_MS; ulReturn = xElapsed; } else @@ -380,7 +380,7 @@ unsigned long ulReturn; { while( xSemaphoreTake( *pxSemaphore, portMAX_DELAY ) != pdTRUE ); xEndTime = xTaskGetTickCount(); - xElapsed = ( xEndTime - xStartTime ) * portTICK_RATE_MS; + xElapsed = ( xEndTime - xStartTime ) * portTICK_PERIOD_MS; if( xElapsed == 0UL ) { @@ -487,7 +487,7 @@ void sys_init(void) u32_t sys_now(void) { - return xTaskGetTickCount() * portTICK_RATE_MS; + return xTaskGetTickCount() * portTICK_PERIOD_MS; } /*---------------------------------------------------------------------------* @@ -510,7 +510,7 @@ u32_t sys_now(void) *---------------------------------------------------------------------------*/ sys_thread_t sys_thread_new( const char *pcName, void( *pxThread )( void *pvParameters ), void *pvArg, int iStackSize, int iPriority ) { -xTaskHandle xCreatedTask; +TaskHandle_t xCreatedTask; portBASE_TYPE xResult; sys_thread_t xReturn; diff --git a/open_esplibs/libmain/os_cpu_a.c b/open_esplibs/libmain/os_cpu_a.c index 9ace3a8..7fd3719 100644 --- a/open_esplibs/libmain/os_cpu_a.c +++ b/open_esplibs/libmain/os_cpu_a.c @@ -72,7 +72,7 @@ void IRAM sdk__xt_int_exit(void) { void IRAM sdk__xt_timer_int(void) { uint32_t trigger_ccount; uint32_t current_ccount; - uint32_t ccount_interval = portTICK_RATE_MS * sdk_os_get_cpu_frequency() * 1000; + uint32_t ccount_interval = portTICK_PERIOD_MS * sdk_os_get_cpu_frequency() * 1000; do { RSR(trigger_ccount, ccompare0); @@ -93,7 +93,7 @@ void IRAM sdk__xt_timer_int1(void) { void IRAM sdk__xt_tick_timer_init(void) { uint32_t ints_enabled; uint32_t current_ccount; - uint32_t ccount_interval = portTICK_RATE_MS * sdk_os_get_cpu_frequency() * 1000; + uint32_t ccount_interval = portTICK_PERIOD_MS * sdk_os_get_cpu_frequency() * 1000; RSR(current_ccount, ccount); WSR(current_ccount + ccount_interval, ccompare0); diff --git a/open_esplibs/libmain/timers.c b/open_esplibs/libmain/timers.c index b30f6e3..42a6767 100644 --- a/open_esplibs/libmain/timers.c +++ b/open_esplibs/libmain/timers.c @@ -60,7 +60,7 @@ void sdk_os_timer_setfn(ETSTimer *ptimer, ETSTimerFunc *pfunction, void *parg) { *tailptr = new_entry; } -static void timer_tramp(xTimerHandle xTimer) +static void timer_tramp(TimerHandle_t xTimer) { ETSTimer *ptimer = pvTimerGetTimerID(xTimer); ptimer->timer_func(ptimer->timer_arg); From 4f7ddd09f826e685359c182cc0617a187d1d67e8 Mon Sep 17 00:00:00 2001 From: "Ruslan V. Uss" Date: Sat, 5 Nov 2016 16:12:16 +0600 Subject: [PATCH 10/13] DS1302 RTC driver (#258) --- examples/ds1302_test/Makefile | 4 + examples/ds1302_test/main.c | 48 +++++++ extras/ds1302/component.mk | 9 ++ extras/ds1302/ds1302.c | 232 ++++++++++++++++++++++++++++++++++ extras/ds1302/ds1302.h | 84 ++++++++++++ 5 files changed, 377 insertions(+) create mode 100644 examples/ds1302_test/Makefile create mode 100644 examples/ds1302_test/main.c create mode 100644 extras/ds1302/component.mk create mode 100644 extras/ds1302/ds1302.c create mode 100644 extras/ds1302/ds1302.h diff --git a/examples/ds1302_test/Makefile b/examples/ds1302_test/Makefile new file mode 100644 index 0000000..2ba09a7 --- /dev/null +++ b/examples/ds1302_test/Makefile @@ -0,0 +1,4 @@ +PROGRAM = ds1302_test +EXTRA_COMPONENTS = extras/ds1302 +#ESPBAUD = 460800 +include ../../common.mk diff --git a/examples/ds1302_test/main.c b/examples/ds1302_test/main.c new file mode 100644 index 0000000..57bb848 --- /dev/null +++ b/examples/ds1302_test/main.c @@ -0,0 +1,48 @@ +/* + * Example of using DS1302 RTC driver + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss + * Pavel Merzlyakov + * BSD Licensed as described in the file LICENSE + */ +#include +#include +#include +#include + +#define CE_PIN 5 +#define IO_PIN 4 +#define SCLK_PIN 0 + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + struct tm time = { + .tm_year = 2016, + .tm_mon = 9, + .tm_mday = 31, + .tm_hour = 21, + .tm_min = 54, + .tm_sec = 10 + }; + + ds1302_init(CE_PIN, IO_PIN, SCLK_PIN); + ds1302_set_write_protect(false); + + ds1302_set_time(&time); + ds1302_start(true); + + while (true) + { + ds1302_get_time(&time); + + printf("%04d-%02d-%02d %02d:%02d:%02d\n", time.tm_year, time.tm_mon + 1, + time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); + + for (uint32_t i = 0; i < 1000; i++) + sdk_os_delay_us(500); + } +} diff --git a/extras/ds1302/component.mk b/extras/ds1302/component.mk new file mode 100644 index 0000000..7e5ea70 --- /dev/null +++ b/extras/ds1302/component.mk @@ -0,0 +1,9 @@ +# Component makefile for extras/ds1302 + +# expected anyone using RTC driver includes it as 'ds1302/ds1302.h' +INC_DIRS += $(ds1302_ROOT).. + +# args for passing into compile rule generation +ds1302_SRC_DIR = $(ds1302_ROOT) + +$(eval $(call component_compile_rules,ds1302)) diff --git a/extras/ds1302/ds1302.c b/extras/ds1302/ds1302.c new file mode 100644 index 0000000..2ff80df --- /dev/null +++ b/extras/ds1302/ds1302.c @@ -0,0 +1,232 @@ +/* + * Driver for DS1302 RTC + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss , + * Pavel Merzlyakov + * BSD Licensed as described in the file LICENSE + */ +#include "ds1302.h" +#include +#include + +#define CH_REG 0x80 +#define WP_REG 0x8e + +#define CH_BIT (1 << 7) +#define WP_BIT (1 << 7) +#define HOUR12_BIT (1 << 7) +#define PM_BIT (1 << 5) + +#define CH_MASK ((uint8_t)(~CH_BIT)) +#define WP_MASK ((uint8_t)(~WP_BIT)) + +#define CLOCK_BURST 0xbe +#define RAM_BURST 0xfe + +#define SECONDS_MASK 0x7f +#define HOUR12_MASK 0x1f +#define HOUR24_MASK 0x3f + +static uint8_t _ce_pin; +static uint8_t _io_pin; +static uint8_t _sclk_pin; +static bool _wp; +static uint8_t _ch; + +static uint8_t bcd2dec(uint8_t val) +{ + return (val >> 4) * 10 + (val & 0x0f); +} + +static uint8_t dec2bcd(uint8_t val) +{ + return ((val / 10) << 4) + (val % 10); +} + +inline static void chip_enable() +{ + gpio_write(_ce_pin, true); + sdk_os_delay_us(4); +} + +inline static void chip_disable() +{ + gpio_write(_ce_pin, false); +} + +inline static void prepare(gpio_direction_t dir) +{ + gpio_enable(_io_pin, dir); + gpio_write(_sclk_pin, false); + chip_enable(); +} + +inline static void toggle_clock() +{ + gpio_write(_sclk_pin, true); + sdk_os_delay_us(1); + gpio_write(_sclk_pin, false); + sdk_os_delay_us(1); +} + +static void write_byte(uint8_t b) +{ + for (uint8_t i = 0; i < 8; i++) + { + gpio_write(_io_pin, (b >> i) & 1); + toggle_clock(); + } +} + +static uint8_t read_byte() +{ + uint8_t b = 0; + for (uint8_t i = 0; i < 8; i++) + { + b |= gpio_read(_io_pin) << i; + toggle_clock(); + } + return b; +} + +static uint8_t read_register(uint8_t reg) +{ + prepare(GPIO_OUTPUT); + write_byte(reg | 0x01); + prepare(GPIO_INPUT); + uint8_t res = read_byte(); + chip_disable(); + return res; +} + +static void write_register(uint8_t reg, uint8_t val) +{ + prepare(GPIO_OUTPUT); + write_byte(reg); + write_byte(val); + chip_disable(); +} + +static void burst_read(uint8_t reg, uint8_t *dst, uint8_t len) +{ + prepare(GPIO_OUTPUT); + write_byte(reg | 0x01); + prepare(GPIO_INPUT); + for (uint8_t i = 0; i < len; i++, dst++) + *dst = read_byte(); + chip_disable(); +} + +static void burst_write(uint8_t reg, uint8_t *src, uint8_t len) +{ + prepare(GPIO_OUTPUT); + write_byte(reg); + for (uint8_t i = 0; i < len; i++, src++) + write_byte(*src); + chip_disable(); +} + +inline static void update_register(uint8_t reg, uint8_t mask, uint8_t val) +{ + write_register(reg, (read_register(reg) & mask) | val); +} + +void ds1302_init(uint8_t ce_pin, uint8_t io_pin, uint8_t sclk_pin) +{ + _ce_pin = ce_pin; + _io_pin = io_pin; + _sclk_pin = sclk_pin; + + gpio_enable(_ce_pin, GPIO_OUTPUT); + gpio_enable(_sclk_pin, GPIO_OUTPUT); + + _wp = ds1302_get_write_protect(); + _ch = read_register(CH_REG) & CH_BIT; +} + +bool ds1302_start(bool start) +{ + if (_wp) return false; + + _ch = start ? 0 : CH_BIT; + update_register(CH_REG, CH_MASK, _ch); + + return true; +} + +bool ds1302_is_running() +{ + return !(read_register(CH_REG) & CH_BIT); +} + +void ds1302_set_write_protect(bool protect) +{ + update_register(WP_REG, WP_MASK, protect ? WP_BIT : 0); + _wp = protect; +} + +bool ds1302_get_write_protect() +{ + return (read_register(WP_REG) & WP_BIT) != 0; +} + +void ds1302_get_time(struct tm *time) +{ + uint8_t buf[7]; + burst_read(CLOCK_BURST, buf, 7); + + time->tm_sec = bcd2dec(buf[0] & SECONDS_MASK); + time->tm_min = bcd2dec(buf[1]); + if (buf[2] & HOUR12_BIT) + { + // RTC in 12-hour mode + time->tm_hour = bcd2dec(buf[2] & HOUR12_MASK) - 1; + if (buf[2] & PM_BIT) + time->tm_hour += 12; + } + else time->tm_hour = bcd2dec(buf[2] & HOUR24_MASK); + time->tm_mday = bcd2dec(buf[3]); + time->tm_mon = bcd2dec(buf[4]) - 1; + time->tm_wday = bcd2dec(buf[5]) - 1; + time->tm_year = bcd2dec(buf[6]) + 2000; +} + +bool ds1302_set_time(const struct tm *time) +{ + if (_wp) return false; + + uint8_t buf[8] = { + dec2bcd(time->tm_sec) | _ch, + dec2bcd(time->tm_min), + dec2bcd(time->tm_hour), + dec2bcd(time->tm_mday), + dec2bcd(time->tm_mon + 1), + dec2bcd(time->tm_wday + 1), + dec2bcd(time->tm_year - 2000), + 0 + }; + burst_write(CLOCK_BURST, buf, 8); + + return true; +} + +bool ds1302_read_sram(uint8_t offset, void *buf, uint8_t len) +{ + if (offset + len > DS1302_RAM_SIZE) + return false; + + burst_read(RAM_BURST, (uint8_t *)buf, len); + + return true; +} + +bool ds1302_write_sram(uint8_t offset, void *buf, uint8_t len) +{ + if (offset + len > DS1302_RAM_SIZE) + return false; + + burst_write(RAM_BURST, (uint8_t *)buf, len); + + return true; +} diff --git a/extras/ds1302/ds1302.h b/extras/ds1302/ds1302.h new file mode 100644 index 0000000..4b3c1ba --- /dev/null +++ b/extras/ds1302/ds1302.h @@ -0,0 +1,84 @@ +/* + * Driver for DS1302 RTC + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss , + * Pavel Merlyakov + * BSD Licensed as described in the file LICENSE + */ +#ifndef EXTRAS_DS1302_H_ +#define EXTRAS_DS1302_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DS1302_RAM_SIZE 31 + +void ds1302_init(uint8_t ce_pin, uint8_t io_pin, uint8_t sclk_pin); + +/** + * \brief Start/stop clock + * \param start Start clock if true + * \return False if RTC is write-protected + */ +bool ds1302_start(bool start); + +/** + * \brief Get current clock state + * \return true if clock running + */ +bool ds1302_is_running(); + +/** + * \brief Enable/disable write protection + * \param protect Set RTC write-protected if true + */ +void ds1302_set_write_protect(bool protect); + +/** + * \brief Get write protection status + * \return true if RTC write-protected + */ +bool ds1302_get_write_protect(); + +/** + * \brief Get current time + * \param time Pointer to the time struct to fill + */ +void ds1302_get_time(struct tm *time); + +/** + * \brief Set time to RTC + * \param time Pointer to the time struct + * \return False if RTC is write-protected + */ +bool ds1302_set_time(const struct tm *time); + +/** + * \brief Read RAM contents into the buffer + * \param offset Start byte, 0..55 + * \param buf Buffer + * \param len Bytes to read, 1..56 + * \return false if error occured (invalid offset or buffer too big) + */ +bool ds1302_read_sram(uint8_t offset, void *buf, uint8_t len); + +/** + * \brief Write buffer to RTC RAM + * \param offset Start byte, 0..55 + * \param buf Buffer + * \param len Bytes to write, 1..56 + * \return false if error occured (invalid offset or buffer too big) + */ +bool ds1302_write_sram(uint8_t offset, void *buf, uint8_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* EXTRAS_DS1302_H_ */ From 99628cf3146c2b030d8d0aeb57750cb5575dd284 Mon Sep 17 00:00:00 2001 From: "Ruslan V. Uss" Date: Sat, 5 Nov 2016 16:12:47 +0600 Subject: [PATCH 11/13] DS1307 set month & weekday fix (#264) --- extras/ds1307/ds1307.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/ds1307/ds1307.c b/extras/ds1307/ds1307.c index 90abf9d..e5306a8 100644 --- a/extras/ds1307/ds1307.c +++ b/extras/ds1307/ds1307.c @@ -96,9 +96,9 @@ void ds1307_set_time(const struct tm *time) buf[1] = dec2bcd(time->tm_sec); buf[2] = dec2bcd(time->tm_min); buf[3] = dec2bcd(time->tm_hour); - buf[4] = dec2bcd(time->tm_wday); + buf[4] = dec2bcd(time->tm_wday + 1); buf[5] = dec2bcd(time->tm_mday); - buf[6] = dec2bcd(time->tm_mon); + buf[6] = dec2bcd(time->tm_mon + 1); buf[7] = dec2bcd(time->tm_year - 2000); i2c_slave_write(ADDR, buf, 8); From dda384f3a1539c035741e931fa9e1f4a2a76914e Mon Sep 17 00:00:00 2001 From: "Ruslan V. Uss" Date: Sat, 5 Nov 2016 16:19:36 +0600 Subject: [PATCH 12/13] WiFi scan example (#265) --- examples/wifi_scan/Makefile | 4 +++ examples/wifi_scan/main.c | 72 +++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 examples/wifi_scan/Makefile create mode 100644 examples/wifi_scan/main.c diff --git a/examples/wifi_scan/Makefile b/examples/wifi_scan/Makefile new file mode 100644 index 0000000..a9e5a71 --- /dev/null +++ b/examples/wifi_scan/Makefile @@ -0,0 +1,4 @@ +PROGRAM = wifi_scan +#ESPBAUD = 460800 + +include ../../common.mk diff --git a/examples/wifi_scan/main.c b/examples/wifi_scan/main.c new file mode 100644 index 0000000..a180a21 --- /dev/null +++ b/examples/wifi_scan/main.c @@ -0,0 +1,72 @@ +/* + * WiFi scan + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#include +#include +#include +#include +#include +#include + +static const char * const auth_modes [] = { + [AUTH_OPEN] = "Open", + [AUTH_WEP] = "WEP", + [AUTH_WPA_PSK] = "WPA/PSK", + [AUTH_WPA2_PSK] = "WPA2/PSK", + [AUTH_WPA_WPA2_PSK] = "WPA/WPA2/PSK" +}; + +static void scan_done_cb(void *arg, sdk_scan_status_t status) +{ + char ssid[33]; // max SSID length + zero byte + + if (status != SCAN_OK) + { + printf("Error: WiFi scan failed\n"); + return; + } + + struct sdk_bss_info *bss = (struct sdk_bss_info *)arg; + // first one is invalid + bss = bss->next.stqe_next; + + printf("\n----------------------------------------------------------------------------------\n"); + printf(" Wi-Fi networks\n"); + printf("----------------------------------------------------------------------------------\n"); + + while (NULL != bss) + { + size_t len = strlen((const char *)bss->ssid); + memcpy(ssid, bss->ssid, len); + ssid[len] = 0; + + printf("%32s (" MACSTR ") RSSI: %02d, security: %s\n", ssid, + MAC2STR(bss->bssid), bss->rssi, auth_modes[bss->authmode]); + + bss = bss->next.stqe_next; + } +} + +static void scan_task(void *arg) +{ + while (true) + { + sdk_wifi_station_scan(NULL, scan_done_cb); + vTaskDelay(5000 / portTICK_PERIOD_MS); + } +} + +void user_init() +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n\n", sdk_system_get_sdk_version()); + + // We can scan only in station mode + sdk_wifi_set_opmode(STATION_MODE); + + xTaskCreate(scan_task, "scan", 512, NULL, 2, NULL); +} From 7c702d7f09f4f810144a17ae3d0ebd110ee77599 Mon Sep 17 00:00:00 2001 From: sheinz Date: Sat, 5 Nov 2016 22:12:14 +0200 Subject: [PATCH 13/13] Unit testing for esp-open-rtos (#253) * Get testing system by projectgus working with master HEAD * Fix running dual test. Add basic wifi test * Moved spiff test to a test case. Reset retries in test runner * Add timers test case * test_runner: List test cases and run individual test cases * Add README for tests * Update README.md * Code clean-up * Python3.4 support. README.md update --- .gitignore | 1 + .gitmodules | 7 +- common.mk | 2 +- examples/posix_fs/Makefile | 11 - examples/posix_fs/README.md | 10 - examples/posix_fs/fs-test | 1 - tests/Makefile | 27 + tests/README.md | 66 +++ tests/cases/01_scheduler.c | 71 +++ tests/cases/02_heap.c | 56 ++ tests/cases/03_byte_load_flash.c | 481 ++++++++++++++++++ tests/cases/04_wifi_basic.c | 181 +++++++ .../cases/05_spiffs.c | 35 +- tests/cases/06_timers.c | 86 ++++ tests/fs-test | 1 + tests/include/testcase.h | 59 +++ tests/test_main.c | 122 +++++ tests/test_runner.py | 389 ++++++++++++++ tests/unity | 1 + 19 files changed, 1562 insertions(+), 45 deletions(-) delete mode 100644 examples/posix_fs/Makefile delete mode 100644 examples/posix_fs/README.md delete mode 160000 examples/posix_fs/fs-test create mode 100644 tests/Makefile create mode 100644 tests/README.md create mode 100644 tests/cases/01_scheduler.c create mode 100644 tests/cases/02_heap.c create mode 100644 tests/cases/03_byte_load_flash.c create mode 100644 tests/cases/04_wifi_basic.c rename examples/posix_fs/posix_fs_example.c => tests/cases/05_spiffs.c (50%) create mode 100644 tests/cases/06_timers.c create mode 160000 tests/fs-test create mode 100644 tests/include/testcase.h create mode 100644 tests/test_main.c create mode 100755 tests/test_runner.py create mode 160000 tests/unity diff --git a/.gitignore b/.gitignore index 4703478..c0df48e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ firmware local.mk local.h screenlog.* +*.swp diff --git a/.gitmodules b/.gitmodules index 5dddd18..d9d5e4c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,6 +13,9 @@ [submodule "extras/spiffs/spiffs"] path = extras/spiffs/spiffs url = https://github.com/pellepl/spiffs.git -[submodule "examples/posix_fs/fs-test"] - path = examples/posix_fs/fs-test +[submodule "tests/unity"] + path = tests/unity + url = https://github.com/ThrowTheSwitch/Unity.git +[submodule "tests/fs-test"] + path = tests/fs-test url = https://github.com/sheinz/fs-test diff --git a/common.mk b/common.mk index 41a218f..bdec87a 100644 --- a/common.mk +++ b/common.mk @@ -136,7 +136,7 @@ $$($(1)_OBJ_DIR)%.o: $$($(1)_REAL_ROOT)%.S $$($(1)_MAKEFILE) $(wildcard $(ROOT)* $(1)_AR_IN_FILES = $$($(1)_OBJ_FILES) -# the component is shown to depend on both obj and source files so we get +# the component is shown to depend on both obj and source files so we get # a meaningful error message for missing explicitly named source files ifeq ($(INCLUDE_SRC_IN_AR),1) $(1)_AR_IN_FILES += $$($(1)_SRC_FILES) diff --git a/examples/posix_fs/Makefile b/examples/posix_fs/Makefile deleted file mode 100644 index bf45ed7..0000000 --- a/examples/posix_fs/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -PROGRAM=posix_fs_example -PROGRAM_EXTRA_SRC_FILES=./fs-test/fs_test.c - -EXTRA_COMPONENTS = extras/spiffs -FLASH_SIZE = 32 - -# spiffs configuration -SPIFFS_BASE_ADDR = 0x200000 -SPIFFS_SIZE = 0x100000 - -include ../../common.mk diff --git a/examples/posix_fs/README.md b/examples/posix_fs/README.md deleted file mode 100644 index 7f05bd4..0000000 --- a/examples/posix_fs/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# POSIX file access example - -This example runs several file system tests on ESP8266. -It uses fs-test library to perform file operations test. fs-test library uses -only POSIX file functions so can be run on host system as well. - -Currently included tests: - * File system load test. Perform multiple file operations in random order. - * File system speed test. Measures files read/write speed. - diff --git a/examples/posix_fs/fs-test b/examples/posix_fs/fs-test deleted file mode 160000 index 2ad547a..0000000 --- a/examples/posix_fs/fs-test +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2ad547adc5f725594b3c6752f036ff4401b221fc diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..599253c --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,27 @@ +PROGRAM=tests + +EXTRA_COMPONENTS=extras/dhcpserver extras/spiffs + +PROGRAM_SRC_DIR = . ./cases + +FLASH_SIZE = 32 + +# spiffs configuration +SPIFFS_BASE_ADDR = 0x200000 +SPIFFS_SIZE = 0x100000 + +# Add unity test framework headers & core source file +PROGRAM_INC_DIR = ./unity/src ./fs-test +PROGRAM_EXTRA_SRC_FILES = ./unity/src/unity.c ./fs-test/fs_test.c + +TESTCASE_SRC_FILES = $(wildcard $(PROGRAM_DIR)cases/*.c) + +# Do not include source files into a static library because when adding this +# library with '--whole-archive' linker gives error that archive contains +# unknown objects (source files) +INCLUDE_SRC_IN_AR = 0 + +# Link every object in the 'program' archive, to pick up constructor functions for test cases +EXTRA_LDFLAGS = -Wl,--whole-archive $(BUILD_DIR)program.a -Wl,--no-whole-archive + +include ../common.mk diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..90b08a8 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,66 @@ +# esp-open-rtos tests + +Testing is based on [Unity](https://github.com/ThrowTheSwitch/Unity) +C testing framework. + +## Features + +* Single device test case. +* Dual devices test cases. Run test case on two ESP8266 modules simultaneously. +* Run only specified test cases. +* List available test cases on a device. + +## Usage + +There's a test runner script `test_runner.py` written in Python3 that runs +test cases on ESP8266 connected to a host. + +### Requirements and dependencies + +* Python3 version > 3.4 `sudo apt-get install python3 python3-pip` +* pyserial `sudo pip3 install pyserial` +* ESP8266 board with reset to boot mode support +* Two ESP8266 for dual mode test cases + +Test runner is heavily relying on device reset using DTR and RTS signals. +Popular ESP8266 boards such as **NodeMcu** and **Wemos D1** support device +reset into flash mode. + +### Options + +`--type` or `-t` - Type of test case to run. Can be 'solo' or 'dual'. +If not specified 'solo' test will be run. + +`--aport` or `-a` - Serial port for device A. +If not specified device `/dev/ttyUSB0` is used. + +`--bport` or `-b` - Serial port for device B. +If not specified device `/dev/ttyUSB1` is used. + +`--no-flash` or `-n` - Do not flash the test firmware before running tests. + +`--list` or `-l` - Display list of the available test cases on the device. + +### Example + +Build test firmware, flash it using serial device `/dev/tty.wchusbserial1410` +and run only *solo* test cases: + +`./test_runner.py -a /dev/tty.wchusbserial1410` + +Build test firmware. Flash both devices as `-t dual` is specified. And run both +*solo* and *dual* test cases: + +`./test_runner.py -a /dev/tty.wchusbserial1410 -b /dev/tty.wchusbserial1420 -t dual` + +Do not flash the firmware, only display available test cases on the device: + +`./test_runner.py -a /dev/tty.wchusbserial1410 -n -l` + +Do not flash the firmware and run only 2 and 4 test cases: + +`./test_runner.py -a /dev/tty.wchusbserial1410 -n 2 4` + +## References + +[Unity](https://github.com/ThrowTheSwitch/Unity) - Simple Unit Testing for C diff --git a/tests/cases/01_scheduler.c b/tests/cases/01_scheduler.c new file mode 100644 index 0000000..aa9b1c0 --- /dev/null +++ b/tests/cases/01_scheduler.c @@ -0,0 +1,71 @@ +#include "testcase.h" +#include +#include +#include + +/* Basic test cases to validate the FreeRTOS scheduler works */ + +DEFINE_SOLO_TESTCASE(01_scheduler_basic) +DEFINE_SOLO_TESTCASE(01_scheduler_priorities) + +void set_variable(void *pvParameters) +{ + bool *as_bool = (bool *)pvParameters; + *as_bool = true; + /* deliberately a busywait at the end, not vTaskSuspend, to test priorities */ + while(1) { } +} + +/* Really simple - do created tasks run? */ +static void a_01_scheduler_basic() +{ + volatile bool a = false, b = false, c = false; + printf("top of scheduler...\n"); + uart_flush_txfifo(0); + + xTaskCreate(set_variable, "set_a", 128, (void *)&a, tskIDLE_PRIORITY, NULL); + xTaskCreate(set_variable, "set_b", 128, (void *)&b, tskIDLE_PRIORITY, NULL); + xTaskCreate(set_variable, "set_c", 128, (void *)&c, tskIDLE_PRIORITY, NULL); + + TEST_ASSERT_FALSE_MESSAGE(a, "task set_a shouldn't run yet"); + TEST_ASSERT_FALSE_MESSAGE(b, "task set_b shouldn't run yet"); + TEST_ASSERT_FALSE_MESSAGE(c, "task set_c shouldn't run yet"); + + vTaskDelay(5); + + TEST_ASSERT_TRUE_MESSAGE(a, "task set_a should have run"); + TEST_ASSERT_TRUE_MESSAGE(b, "task set_b should have run"); + TEST_ASSERT_TRUE_MESSAGE(c, "task set_c should have run"); + TEST_PASS(); +} + +/* Verify that a high-priority task will starve a lower priority task */ +static void a_01_scheduler_priorities() +{ + /* Increase priority of the init task to make sure it always takes priority */ + vTaskPrioritySet(xTaskGetCurrentTaskHandle(), tskIDLE_PRIORITY+4); + + bool lower = false, higher = false; + TaskHandle_t task_lower, task_higher; + + xTaskCreate(set_variable, "high_prio", 128, (void *)&higher, tskIDLE_PRIORITY+1, &task_higher); + xTaskCreate(set_variable, "low_prio", 128, (void *)&lower, tskIDLE_PRIORITY, &task_lower); + + TEST_ASSERT_FALSE_MESSAGE(higher, "higher prio task should not have run yet"); + TEST_ASSERT_FALSE_MESSAGE(lower, "lower prio task should not have run yet"); + + vTaskDelay(2); + + TEST_ASSERT_TRUE_MESSAGE(higher, "higher prio task should have run"); + TEST_ASSERT_FALSE_MESSAGE(lower, "lower prio task should not have run"); + + /* Bump lower priority task over higher priority task */ + vTaskPrioritySet(task_lower, tskIDLE_PRIORITY+2); + + TEST_ASSERT_FALSE_MESSAGE(lower, "lower prio task should still not have run yet"); + + vTaskDelay(1); + + TEST_ASSERT_TRUE_MESSAGE(lower, "lower prio task should have run"); + TEST_PASS(); +} diff --git a/tests/cases/02_heap.c b/tests/cases/02_heap.c new file mode 100644 index 0000000..54bb5e3 --- /dev/null +++ b/tests/cases/02_heap.c @@ -0,0 +1,56 @@ +#include "testcase.h" +#include +#include +#include + +DEFINE_SOLO_TESTCASE(02_heap_simple) +DEFINE_SOLO_TESTCASE(02_heap_full) + +/* Simple heap accounting tests */ +static void a_02_heap_simple() +{ + struct mallinfo info = mallinfo(); + printf("'arena' allocation size %d bytes\n", info.arena); + /* This is really a sanity check, if the "arena" size shrinks then + this is a good thing and we can update the test. If it grows + then we can also update the test, but we need a good reason. */ + TEST_ASSERT_INT_WITHIN_MESSAGE(1000, 15000, info.arena, "Initial allocated heap should be approximately 15kB. SEE COMMENT."); + + uint32_t freeheap = xPortGetFreeHeapSize(); + printf("xPortGetFreeHeapSize = %d bytes\n", freeheap); + TEST_ASSERT_TRUE_MESSAGE(freeheap > 20000, "Should be at least 20kB free."); + + uint8_t *buf = malloc(8192); + /* <-- have to do something with buf or gcc helpfully optimises it out! */ + memset(buf, 0xEE, 8192); + uint32_t after = xPortGetFreeHeapSize(); + struct mallinfo after_info = mallinfo(); + printf("after arena size = %d bytes\n", after_info.arena); + printf("after xPortGetFreeHeapSize = %d bytes\n", after); + TEST_ASSERT_UINT32_WITHIN_MESSAGE(100, info.arena+8192, after_info.arena, "Allocated heap 'after' size should be 8kB more than before"); + TEST_ASSERT_UINT32_WITHIN_MESSAGE(100, freeheap-8192, after, "Free heap size should be 8kB less than before"); + + free(buf); + after = xPortGetFreeHeapSize(); + printf("after freeing xPortGetFreeHeapSize = %d bytes\n", after); + TEST_ASSERT_UINT32_WITHIN_MESSAGE(100, freeheap, after, "Free heap size after freeing buffer should be close to initial"); + TEST_PASS(); +} + +/* Ensure malloc behaves when out of memory */ +static void a_02_heap_full() +{ + void *x = malloc(65536); + TEST_ASSERT_NULL_MESSAGE(x, "Allocating 64kB should fail and return null"); + + void *y = malloc(32768); + TEST_ASSERT_NOT_NULL_MESSAGE(y, "Allocating 32kB should succeed"); + + void *z = malloc(32768); + TEST_ASSERT_NULL_MESSAGE(z, "Allocating second 32kB should fail"); + + free(y); + z = malloc(32768); + TEST_ASSERT_NOT_NULL_MESSAGE(z, "Allocating 32kB should succeed after first block freed"); + TEST_PASS(); +} diff --git a/tests/cases/03_byte_load_flash.c b/tests/cases/03_byte_load_flash.c new file mode 100644 index 0000000..b8dcb8e --- /dev/null +++ b/tests/cases/03_byte_load_flash.c @@ -0,0 +1,481 @@ +/** + * Unit tests to verify the "unaligned load handler" in core/exception_vectors.S + * that allows us to complete byte loads from unaligned memory, etc. + * + * Adapted from a test program in 'experiments' that did this. + */ +#include "testcase.h" +#include "esp/rom.h" +#include "esp/timer.h" +#include "esp/uart.h" +#include "espressif/esp_common.h" +#include "xtensa_ops.h" +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#include "string.h" +#include "strings.h" + +#include + +#define TESTSTRING "O hai there! %d %d %d" + +static char dramtest[] = TESTSTRING; + +static const __attribute__((section(".iram1.notrodata"))) + char iramtest[] = TESTSTRING; + +static const __attribute__((section(".text.notrodata"))) + char iromtest[] = TESTSTRING; + +static const volatile __attribute__((section(".iram1.notliterals"))) + int16_t unsigned_shorts[] = { -3, -4, -5, -32767, 44 }; + +static const __attribute__((section(".iram1.notrodata"))) + char sanity_test_data[] = { + 0x01, 0x55, 0x7e, 0x2a, 0x81, 0xd5, 0xfe, 0xaa + }; + +DEFINE_SOLO_TESTCASE(03_byte_load_verify_sections) + +#define PTR_IN_REGION(PTR, START, LEN) \ + ((START <= (intptr_t)(PTR)) && ((intptr_t)(PTR) < (START+LEN))) + +/* Sanity check, ensure the addresses of the various test strings + * are in the correct address space regions. */ +static void a_03_byte_load_verify_sections() +{ + printf("dramtest addr %p\n", dramtest); + TEST_ASSERT_MESSAGE(PTR_IN_REGION(dramtest, 0x3FFE8000, 0x14000), + "dramtest should be in DRAM region"); + + printf("iramtest addr %p\n", iramtest); + TEST_ASSERT_MESSAGE(PTR_IN_REGION(iramtest, 0x40100000, 0x8000), + "iramtest should be in IRAM region"); + + printf("iromtest addr %p\n", iromtest); + TEST_ASSERT_MESSAGE(PTR_IN_REGION(iromtest, 0x40202010, (0x100000 - 0x2010)), + "iromtest sohuld be in IROM region"); + + printf("unsigned_shorts addr %p\n", unsigned_shorts); + TEST_ASSERT_MESSAGE(PTR_IN_REGION(unsigned_shorts, 0x40100000, 0x8000), + "unsigned_shorts should be in IRAM region"); + + printf("sanity_test_data addr %p\n", sanity_test_data); + TEST_ASSERT_MESSAGE(PTR_IN_REGION(sanity_test_data, 0x40100000, 0x8000), + "sanity_test_data should be in IRAM region"); + + TEST_PASS(); +} + + +/* test utility functions used for '03_byte_load_test_strings' + + returns the expected string result */ +typedef const char *(* test_with_fn_t)(const char *string); + +static char buf[64]; + +static const char * test_memcpy_aligned(const char *string) +{ + memcpy(buf, string, 16); + return "O hai there! %d "; +} + +static const char * test_memcpy_unaligned(const char *string) +{ + memcpy(buf, string, 15); + return "O hai there! %d"; +} + + +static const char * test_memcpy_unaligned2(const char *string) +{ + memcpy(buf, string+1, 15); + return " hai there! %d "; +} + +static const char * test_strcpy(const char *string) +{ + strcpy(buf, string); + return dramtest; +} + +static const char * test_sprintf(const char *string) +{ + sprintf(buf, string, 1, 2, 3); + return "O hai there! 1 2 3"; +} + +static const char * test_sprintf_arg(const char *string) +{ + sprintf(buf, "%s", string); + return dramtest; +} + +static const char * test_naive_strcpy(const char *string) +{ + char *to = buf; + while((*to++ = *string++)) + ; + return dramtest; +} + +static const char * test_naive_strcpy_a0(const char *string) +{ + asm volatile ( +" mov a8, %0 \n" +" mov a9, %1 \n" +"tns_loop%=: l8ui a0, a9, 0 \n" +" addi.n a9, a9, 1 \n" +" s8i a0, a8, 0 \n" +" addi.n a8, a8, 1 \n" +" bnez a0, tns_loop%=\n" + : : "r" (buf), "r" (string) : "a0", "a8", "a9"); + return dramtest; +} + +static const char * test_naive_strcpy_a2(const char *string) +{ + asm volatile ( +" mov a8, %0 \n" +" mov a9, %1 \n" +"tns_loop%=: l8ui a2, a9, 0 \n" +" addi.n a9, a9, 1 \n" +" s8i a2, a8, 0 \n" +" addi.n a8, a8, 1 \n" +" bnez a2, tns_loop%=\n" + : : "r" (buf), "r" (string) : "a2", "a8", "a9"); + return dramtest; +} + +static const char * test_naive_strcpy_a3(const char *string) +{ + asm volatile ( +" mov a8, %0 \n" +" mov a9, %1 \n" +"tns_loop%=: l8ui a3, a9, 0 \n" +" addi.n a9, a9, 1 \n" +" s8i a3, a8, 0 \n" +" addi.n a8, a8, 1 \n" +" bnez a3, tns_loop%=\n" + : : "r" (buf), "r" (string) : "a3", "a8", "a9"); + return TESTSTRING; +} + +static const char * test_naive_strcpy_a4(const char *string) +{ + asm volatile ( +" mov a8, %0 \n" +" mov a9, %1 \n" +"tns_loop%=: l8ui a4, a9, 0 \n" +" addi.n a9, a9, 1 \n" +" s8i a4, a8, 0 \n" +" addi.n a8, a8, 1 \n" +" bnez a4, tns_loop%=\n" + : : "r" (buf), "r" (string) : "a4", "a8", "a9"); + return TESTSTRING; +} + +static const char * test_naive_strcpy_a5(const char *string) +{ + asm volatile ( +" mov a8, %0 \n" +" mov a9, %1 \n" +"tns_loop%=: l8ui a5, a9, 0 \n" +" addi.n a9, a9, 1 \n" +" s8i a5, a8, 0 \n" +" addi.n a8, a8, 1 \n" +" bnez a5, tns_loop%=\n" + : : "r" (buf), "r" (string) : "a5", "a8", "a9"); + return TESTSTRING; +} + +static const char * test_naive_strcpy_a6(const char *string) +{ + asm volatile ( +" mov a8, %0 \n" +" mov a9, %1 \n" +"tns_loop%=: l8ui a6, a9, 0 \n" +" addi.n a9, a9, 1 \n" +" s8i a6, a8, 0 \n" +" addi.n a8, a8, 1 \n" +" bnez a6, tns_loop%=\n" + : : "r" (buf), "r" (string) : "a6", "a8", "a9"); + return TESTSTRING; +} + +static const char * test_noop(const char *string) +{ + buf[0] = 0; + return ""; +} + +static uint32_t IRAM inner_string_test(const char *string, test_with_fn_t testfn, const char *testfn_label, uint32_t nullvalue, bool evict_cache) +{ + printf(" .. against %30s: ", testfn_label); + vPortEnterCritical(); + uint32_t before; + RSR(before, CCOUNT); + const int TEST_REPEATS = 1000; + for(int i = 0; i < TEST_REPEATS; i++) { + memset(buf, 0, sizeof(buf)); + const char *expected = testfn(string); + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, buf, testfn_label); + if(evict_cache) { + Cache_Read_Disable(); + Cache_Read_Enable(0,0,1); + } + } + uint32_t after; + RSR(after, CCOUNT); + vPortExitCritical(); + uint32_t instructions = (after-before)/TEST_REPEATS - nullvalue; + printf("%5d instructions\r\n", instructions); + return instructions; +} + +static void string_test(const char *string, char *label, bool evict_cache) +{ + printf("Testing %s (%p) '%s'\r\n", label, string, string); + printf("Formats as: '"); + printf(string, 1, 2, 3); + printf("'\r\n"); + uint32_t nullvalue = inner_string_test(string, test_noop, "null op", 0, evict_cache); + inner_string_test(string, test_memcpy_aligned, "memcpy - aligned len", nullvalue, evict_cache); + inner_string_test(string, test_memcpy_unaligned, "memcpy - unaligned len", nullvalue, evict_cache); + inner_string_test(string, test_memcpy_unaligned2, "memcpy - unaligned start&len", nullvalue, evict_cache); + inner_string_test(string, test_strcpy, "strcpy", nullvalue, evict_cache); + inner_string_test(string, test_naive_strcpy, "naive strcpy", nullvalue, evict_cache); + inner_string_test(string, test_naive_strcpy_a0, "naive strcpy (a0)", nullvalue, evict_cache); + inner_string_test(string, test_naive_strcpy_a2, "naive strcpy (a2)", nullvalue, evict_cache); + inner_string_test(string, test_naive_strcpy_a3, "naive strcpy (a3)", nullvalue, evict_cache); + inner_string_test(string, test_naive_strcpy_a4, "naive strcpy (a4)", nullvalue, evict_cache); + inner_string_test(string, test_naive_strcpy_a5, "naive strcpy (a5)", nullvalue, evict_cache); + inner_string_test(string, test_naive_strcpy_a6, "naive strcpy (a6)", nullvalue, evict_cache); + inner_string_test(string, test_sprintf, "sprintf", nullvalue, evict_cache); + inner_string_test(string, test_sprintf_arg, "sprintf format arg", nullvalue, evict_cache); +} + +DEFINE_SOLO_TESTCASE(03_byte_load_test_strings) + +/* Test various operations on strings in various regions */ +static void a_03_byte_load_test_strings() +{ + string_test(dramtest, "DRAM", 0); + string_test(iramtest, "IRAM", 0); + string_test(iromtest, "Cached flash", 0); + string_test(iromtest, "'Uncached' flash", 1); + TEST_PASS(); +} + +static volatile bool frc1_ran; +static volatile bool frc1_finished; +static volatile char frc1_buf[80]; + +DEFINE_SOLO_TESTCASE(03_byte_load_test_isr) + +static void frc1_interrupt_handler(void) +{ + frc1_ran = true; + timer_set_run(FRC1, false); + strcpy((char *)frc1_buf, iramtest); + frc1_finished = true; +} + +/* Verify that the unaligned loader can run inside an ISR */ +static void a_03_byte_load_test_isr() +{ + printf("Testing behaviour inside ISRs...\r\n"); + timer_set_interrupts(FRC1, false); + timer_set_run(FRC1, false); + _xt_isr_attach(INUM_TIMER_FRC1, frc1_interrupt_handler); + timer_set_frequency(FRC1, 1000); + timer_set_interrupts(FRC1, true); + timer_set_run(FRC1, true); + sdk_os_delay_us(2000); + + if(!frc1_ran) + TEST_FAIL_MESSAGE("ERROR: FRC1 timer exception never fired.\r\n"); + else if(!frc1_finished) + TEST_FAIL_MESSAGE("ERROR: FRC1 timer exception never finished.\r\n"); + else if(strcmp((char *)frc1_buf, iramtest)) + TEST_FAIL_MESSAGE("ERROR: FRC1 strcpy from IRAM failed.\r\n"); + else + TEST_PASS(); +} + +DEFINE_SOLO_TESTCASE(03_byte_load_test_sign_extension) + +static void a_03_byte_load_test_sign_extension() +{ + /* this step seems to be necessary so the compiler will actually generate l16si */ + int16_t *shorts_p = (int16_t *)unsigned_shorts; + if(shorts_p[0] == -3 && shorts_p[1] == -4 && shorts_p[2] == -5 && shorts_p[3] == -32767 && shorts_p[4] == 44) + { + TEST_PASS(); + } else { + sprintf(buf, "l16si sign extension failed. Got values %d %d %d %d %d\r\n", shorts_p[0], shorts_p[1], shorts_p[2], shorts_p[3], shorts_p[4]); + TEST_FAIL_MESSAGE(buf); + } +} + + +/* test that running unaligned loads in a running FreeRTOS system doesn't break things + + The following tests run inside a FreeRTOS task, after everything else. +*/ +DEFINE_SOLO_TESTCASE(03_byte_load_test_system_interaction); + +static void task_load_test_system_interaction() +{ + uint32_t start = xTaskGetTickCount(); + printf("Starting system/timer interaction test (takes approx 1 second)...\n"); + for(int i = 0; i < 5000; i++) { + test_naive_strcpy_a0(iromtest); + test_naive_strcpy_a2(iromtest); + test_naive_strcpy_a3(iromtest); + test_naive_strcpy_a4(iromtest); + test_naive_strcpy_a5(iromtest); + test_naive_strcpy_a6(iromtest); + /* + const volatile char *string = iromtest; + volatile char *to = dest; + while((*to++ = *string++)) + ; + */ + } + uint32_t ticks = xTaskGetTickCount() - start; + printf("Timer interaction test PASSED after %d ticks.\n", ticks); + TEST_PASS(); +} + +static void a_03_byte_load_test_system_interaction() +{ + xTaskCreate(task_load_test_system_interaction, "interactionTask", 256, NULL, 2, NULL); + while(1) { + vTaskDelay(100); + } +} + +/* The following "sanity tests" are designed to try to execute every code path + * of the LoadStoreError handler, with a variety of offsets and data values + * designed to catch any mask/shift errors, sign-extension bugs, etc */ +DEFINE_SOLO_TESTCASE(03_byte_load_test_sanity) + +/* (Contrary to expectations, 'mov a15, a15' in Xtensa is not technically a + * no-op, but is officially "undefined and reserved for future use", so we need + * a special case in the case where reg == "a15" so we don't end up generating + * those opcodes. GCC is smart enough to optimize away the whole conditional + * and just insert the correct asm block, since `reg` is a static argument.) */ +#define LOAD_VIA_REG(op, reg, addr, var) \ + if (strcmp(reg, "a15")) { \ + asm volatile ( \ + "mov a15, " reg "\n\t" \ + op " " reg ", %1, 0\n\t" \ + "mov %0, " reg "\n\t" \ + "mov " reg ", a15\n\t" \ + : "=r" (var) : "r" (addr) : "a15" ); \ + } else { \ + asm volatile ( \ + op " " reg ", %1, 0\n\t" \ + "mov %0, " reg "\n\t" \ + : "=r" (var) : "r" (addr) : "a15" ); \ + } + +#define TEST_LOAD(op, reg, addr, value) \ + { \ + int32_t result; \ + LOAD_VIA_REG(op, reg, addr, result); \ + if (result != value) sanity_test_failed(op, reg, addr, value, result); \ + } + +static void sanity_test_failed(const char *testname, const char *reg, const void *addr, int32_t value, int32_t result) { + uint32_t actual_data = *(uint32_t *)((uint32_t)addr & 0xfffffffc); + sprintf(buf, "%s %s from %p (32-bit value: 0x%x): Expected 0x%08x (%d), got 0x%08x (%d)\n", testname, reg, addr, actual_data, value, value, result, result); + TEST_FAIL_MESSAGE(buf); +} + +static void sanity_test_l8ui(const void *addr, int32_t value) { + TEST_LOAD("l8ui", "a0", addr, value); + TEST_LOAD("l8ui", "a1", addr, value); + TEST_LOAD("l8ui", "a2", addr, value); + TEST_LOAD("l8ui", "a3", addr, value); + TEST_LOAD("l8ui", "a4", addr, value); + TEST_LOAD("l8ui", "a5", addr, value); + TEST_LOAD("l8ui", "a6", addr, value); + TEST_LOAD("l8ui", "a7", addr, value); + TEST_LOAD("l8ui", "a8", addr, value); + TEST_LOAD("l8ui", "a9", addr, value); + TEST_LOAD("l8ui", "a10", addr, value); + TEST_LOAD("l8ui", "a11", addr, value); + TEST_LOAD("l8ui", "a12", addr, value); + TEST_LOAD("l8ui", "a13", addr, value); + TEST_LOAD("l8ui", "a14", addr, value); + TEST_LOAD("l8ui", "a15", addr, value); +} + +static void sanity_test_l16ui(const void *addr, int32_t value) { + TEST_LOAD("l16ui", "a0", addr, value); + TEST_LOAD("l16ui", "a1", addr, value); + TEST_LOAD("l16ui", "a2", addr, value); + TEST_LOAD("l16ui", "a3", addr, value); + TEST_LOAD("l16ui", "a4", addr, value); + TEST_LOAD("l16ui", "a5", addr, value); + TEST_LOAD("l16ui", "a6", addr, value); + TEST_LOAD("l16ui", "a7", addr, value); + TEST_LOAD("l16ui", "a8", addr, value); + TEST_LOAD("l16ui", "a9", addr, value); + TEST_LOAD("l16ui", "a10", addr, value); + TEST_LOAD("l16ui", "a11", addr, value); + TEST_LOAD("l16ui", "a12", addr, value); + TEST_LOAD("l16ui", "a13", addr, value); + TEST_LOAD("l16ui", "a14", addr, value); + TEST_LOAD("l16ui", "a15", addr, value); +} + +static void sanity_test_l16si(const void *addr, int32_t value) { + TEST_LOAD("l16si", "a0", addr, value); + TEST_LOAD("l16si", "a1", addr, value); + TEST_LOAD("l16si", "a2", addr, value); + TEST_LOAD("l16si", "a3", addr, value); + TEST_LOAD("l16si", "a4", addr, value); + TEST_LOAD("l16si", "a5", addr, value); + TEST_LOAD("l16si", "a6", addr, value); + TEST_LOAD("l16si", "a7", addr, value); + TEST_LOAD("l16si", "a8", addr, value); + TEST_LOAD("l16si", "a9", addr, value); + TEST_LOAD("l16si", "a10", addr, value); + TEST_LOAD("l16si", "a11", addr, value); + TEST_LOAD("l16si", "a12", addr, value); + TEST_LOAD("l16si", "a13", addr, value); + TEST_LOAD("l16si", "a14", addr, value); + TEST_LOAD("l16si", "a15", addr, value); +} + +static void a_03_byte_load_test_sanity(void) { + printf("== Performing sanity tests (sanity_test_data @ %p)...\n", sanity_test_data); + + sanity_test_l8ui(sanity_test_data + 0, 0x01); + sanity_test_l8ui(sanity_test_data + 1, 0x55); + sanity_test_l8ui(sanity_test_data + 2, 0x7e); + sanity_test_l8ui(sanity_test_data + 3, 0x2a); + sanity_test_l8ui(sanity_test_data + 4, 0x81); + sanity_test_l8ui(sanity_test_data + 5, 0xd5); + sanity_test_l8ui(sanity_test_data + 6, 0xfe); + sanity_test_l8ui(sanity_test_data + 7, 0xaa); + + sanity_test_l16ui(sanity_test_data + 0, 0x5501); + sanity_test_l16ui(sanity_test_data + 2, 0x2a7e); + sanity_test_l16ui(sanity_test_data + 4, 0xd581); + sanity_test_l16ui(sanity_test_data + 6, 0xaafe); + + sanity_test_l16si(sanity_test_data + 0, 0x5501); + sanity_test_l16si(sanity_test_data + 2, 0x2a7e); + sanity_test_l16si(sanity_test_data + 4, -10879); + sanity_test_l16si(sanity_test_data + 6, -21762); + + printf("== Sanity tests completed.\n"); + TEST_PASS(); +} diff --git a/tests/cases/04_wifi_basic.c b/tests/cases/04_wifi_basic.c new file mode 100644 index 0000000..129ddd0 --- /dev/null +++ b/tests/cases/04_wifi_basic.c @@ -0,0 +1,181 @@ +/** + * This test verifies basic WiFi communication. + * + * Device A creates a WiFi access point and listens on port 23 for incomming + * connection. When incomming connection occurs it sends a string and waits + * for the response. + * + * Device B connects to a WiFi access point and opens TCP connection to + * device A on port 23. Then it receives a string and sends it back. + */ +#include + +#include +#include + +#include +#include "sdk_internal.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "testcase.h" + +#define AP_SSID "esp-open-rtos-ap" +#define AP_PSK "esp-open-rtos" +#define SERVER "172.16.0.1" +#define PORT 23 +#define BUF_SIZE 128 + +DEFINE_TESTCASE(04_wifi_basic, DUAL) + +/********************************************************* + * WiFi AP part + *********************************************************/ + +static void server_task(void *pvParameters) +{ + char buf[BUF_SIZE]; + struct netconn *nc = netconn_new(NETCONN_TCP); + TEST_ASSERT_TRUE_MESSAGE(nc != 0, "Failed to allocate socket"); + + netconn_bind(nc, IP_ADDR_ANY, PORT); + netconn_listen(nc); + + struct netconn *client = NULL; + err_t err = netconn_accept(nc, &client); + TEST_ASSERT_TRUE_MESSAGE(err == ERR_OK, "Error accepting connection"); + + ip_addr_t client_addr; + uint16_t port_ignore; + netconn_peer(client, &client_addr, &port_ignore); + + snprintf(buf, sizeof(buf), "test ping\r\n"); + printf("Device A: send data: %s\n", buf); + netconn_write(client, buf, strlen(buf), NETCONN_COPY); + + struct pbuf *pb; + for (int i = 0; i < 10; i++) { + TEST_ASSERT_EQUAL_INT_MESSAGE(ERR_OK, netconn_recv_tcp_pbuf(client, &pb), + "Failed to receive data"); + if (pb->len > 0) { + memcpy(buf, pb->payload, pb->len); + buf[pb->len] = 0; + break; + } + vTaskDelay(100 / portTICK_PERIOD_MS); + } + TEST_ASSERT_TRUE_MESSAGE(pb->len > 0, "No data received"); + printf("Device A: received data: %s\n", buf); + TEST_ASSERT_FALSE_MESSAGE(strcmp((const char*)buf, "test pong\r\n"), + "Received wrong data"); + + netconn_delete(client); + + TEST_PASS(); +} + +static void a_04_wifi_basic(void) +{ + printf("Device A started\n"); + sdk_wifi_set_opmode(SOFTAP_MODE); + + struct ip_info ap_ip; + IP4_ADDR(&ap_ip.ip, 172, 16, 0, 1); + IP4_ADDR(&ap_ip.gw, 0, 0, 0, 0); + IP4_ADDR(&ap_ip.netmask, 255, 255, 0, 0); + sdk_wifi_set_ip_info(1, &ap_ip); + + struct sdk_softap_config ap_config = { + .ssid = AP_SSID, + .ssid_hidden = 0, + .channel = 3, + .ssid_len = strlen(AP_SSID), + .authmode = AUTH_WPA_WPA2_PSK, + .password = AP_PSK, + .max_connection = 3, + .beacon_interval = 100, + }; + sdk_wifi_softap_set_config(&ap_config); + + ip_addr_t first_client_ip; + IP4_ADDR(&first_client_ip, 172, 16, 0, 2); + dhcpserver_start(&first_client_ip, 4); + + xTaskCreate(server_task, "setver_task", 1024, NULL, 2, NULL); +} + + +/********************************************************* + * WiFi client part + *********************************************************/ + +static void connect_task(void *pvParameters) +{ + struct sockaddr_in serv_addr; + char buf[BUF_SIZE]; + + // wait for wifi connection + while (sdk_wifi_station_get_connect_status() != STATION_GOT_IP) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Waiting for connection to AP\n"); + } + + int s = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE_MESSAGE(s >= 0, "Failed to allocate a socket"); + + bzero(&serv_addr, sizeof(serv_addr)); + serv_addr.sin_port = htons(PORT); + serv_addr.sin_family = AF_INET; + + TEST_ASSERT_TRUE_MESSAGE(inet_aton(SERVER, &serv_addr.sin_addr.s_addr), + "Failed to set IP address"); + + TEST_ASSERT_TRUE_MESSAGE( + connect(s, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == 0, + "Socket connection failed"); + + bzero(buf, BUF_SIZE); + + int r = 0; + for (int i = 0; i < 10; i++) { + r = read(s, buf, BUF_SIZE); + if (r > 0) { + break; + } + vTaskDelay(100 / portTICK_PERIOD_MS); + } + TEST_ASSERT_TRUE_MESSAGE(r > 0, "No data received"); + + printf("Device B: received data: %s\n", buf); + TEST_ASSERT_FALSE_MESSAGE(strcmp((const char*)buf, "test ping\r\n"), + "Received wrong data"); + + snprintf(buf, sizeof(buf), "test pong\r\n"); + printf("Device B: send data: %s\n", buf); + TEST_ASSERT_EQUAL_INT_MESSAGE(strlen(buf), write(s, buf, strlen(buf)), + "Error socket writing"); + + close(s); + + TEST_PASS(); +} + +static void b_04_wifi_basic(void) +{ + printf("Device B started\n"); + struct sdk_station_config config = { + .ssid = AP_SSID, + .password = AP_PSK, + }; + + sdk_wifi_set_opmode(STATION_MODE); + sdk_wifi_station_set_config(&config); + + xTaskCreate(&connect_task, "connect_task", 1024, NULL, 2, NULL); +} diff --git a/examples/posix_fs/posix_fs_example.c b/tests/cases/05_spiffs.c similarity index 50% rename from examples/posix_fs/posix_fs_example.c rename to tests/cases/05_spiffs.c index 610f58d..c3bf30b 100644 --- a/examples/posix_fs/posix_fs_example.c +++ b/tests/cases/05_spiffs.c @@ -9,14 +9,18 @@ #include "esp_spiffs.h" #include "spiffs.h" -#include "fs-test/fs_test.h" +#include "fs_test.h" + +#include "testcase.h" + +DEFINE_SOLO_TESTCASE(05_spiffs) static fs_time_t get_current_time() { return timer_get_count(FRC2) / 5000; // to get roughly 1ms resolution } -void test_task(void *pvParameters) +static void test_task(void *pvParameters) { esp_spiffs_init(); esp_spiffs_mount(); @@ -28,28 +32,19 @@ void test_task(void *pvParameters) } esp_spiffs_mount(); - while (1) { - vTaskDelay(5000 / portTICK_PERIOD_MS); - if (fs_load_test_run(100)) { - printf("PASS\n"); - } else { - printf("FAIL\n"); - } + TEST_ASSERT_TRUE_MESSAGE(fs_load_test_run(100), "Load test failed"); - vTaskDelay(5000 / portTICK_PERIOD_MS); - float write_rate, read_rate; - if (fs_speed_test_run(get_current_time, &write_rate, &read_rate)) { - printf("Read speed: %.0f bytes/s\n", read_rate * 1000); - printf("Write speed: %.0f bytes/s\n", write_rate * 1000); - } else { - printf("FAIL\n"); - } + float write_rate, read_rate; + if (fs_speed_test_run(get_current_time, &write_rate, &read_rate)) { + printf("Read speed: %.0f bytes/s\n", read_rate * 1000); + printf("Write speed: %.0f bytes/s\n", write_rate * 1000); + } else { + TEST_FAIL(); } + TEST_PASS(); } -void user_init(void) +static void a_05_spiffs(void) { - uart_set_baud(0, 115200); - xTaskCreate(test_task, "test_task", 1024, NULL, 2, NULL); } diff --git a/tests/cases/06_timers.c b/tests/cases/06_timers.c new file mode 100644 index 0000000..91efb00 --- /dev/null +++ b/tests/cases/06_timers.c @@ -0,0 +1,86 @@ +#include +#include +#include "FreeRTOS.h" +#include "task.h" +#include "etstimer.h" + +#include "testcase.h" + +DEFINE_SOLO_TESTCASE(06_ets_timers); + +typedef struct { + ETSTimer handle; + uint32_t start_time; + uint32_t fire_count; +} test_timer_t; + +#define TEST_TIMERS_NUMBER 2 +static test_timer_t timers[TEST_TIMERS_NUMBER]; + +static uint32_t get_current_time() +{ + return timer_get_count(FRC2) / 5000; // to get roughly 1ms resolution +} + +static void timer_0_cb(void *arg) +{ + uint32_t v = (uint32_t)arg; + uint32_t delay = get_current_time() - timers[0].start_time; + timers[0].fire_count++; + + TEST_ASSERT_EQUAL_UINT32_MESSAGE(0xAA, v, "Timer 0 argument invalid"); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, timers[0].fire_count, "Timer 0 repeat error"); + + printf("Timer 0 delay: %d\n", delay); + // Timer should fire in 100ms + TEST_ASSERT_INT_WITHIN_MESSAGE(5, 100, delay, "Timer 0 time wrong"); +} + +static void timer_1_cb(void *arg) +{ + uint32_t v = (uint32_t)arg; + uint32_t delay = get_current_time() - timers[1].start_time; + + timers[1].start_time = get_current_time(); + timers[1].fire_count++; + + TEST_ASSERT_EQUAL_UINT32_MESSAGE(0xBB, v, "Timer 1 argument invalid"); + TEST_ASSERT_TRUE_MESSAGE(timers[1].fire_count < 6, + "Timer 1 repeats after disarming"); + + printf("Timer 1 delay: %d\n", delay); + // Timer should fire in 100ms + TEST_ASSERT_INT_WITHIN_MESSAGE(5, 50, delay, "Timer 1 time wrong"); + + if (timers[1].fire_count == 5) { + sdk_ets_timer_disarm(&timers[1].handle); + } +} + +static void test_task(void *pvParameters) +{ + sdk_ets_timer_disarm(&timers[0].handle); + sdk_ets_timer_setfn(&timers[0].handle, timer_0_cb, (void*)0xAA); + timers[0].start_time = get_current_time(); + sdk_ets_timer_arm(&timers[0].handle, 100, false); + + sdk_ets_timer_disarm(&timers[1].handle); + sdk_ets_timer_setfn(&timers[1].handle, timer_1_cb, (void*)0xBB); + timers[1].start_time = get_current_time(); + sdk_ets_timer_arm(&timers[1].handle, 50, true); // repeating timer + + vTaskDelay(500 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL_INT_MESSAGE(1, timers[0].fire_count, + "Timer hasn't fired"); + + TEST_ASSERT_EQUAL_INT_MESSAGE(5, timers[1].fire_count, + "Timer fire count isn't correct"); + + TEST_PASS(); +} + +static void a_06_ets_timers(void) +{ + xTaskCreate(test_task, "test_task", 256, NULL, 2, NULL); +} diff --git a/tests/fs-test b/tests/fs-test new file mode 160000 index 0000000..e531bc0 --- /dev/null +++ b/tests/fs-test @@ -0,0 +1 @@ +Subproject commit e531bc0d4f75887e5f0e081aae3efbf4a50e2f54 diff --git a/tests/include/testcase.h b/tests/include/testcase.h new file mode 100644 index 0000000..11f59a4 --- /dev/null +++ b/tests/include/testcase.h @@ -0,0 +1,59 @@ +#ifndef _TESTCASE_H +#define _TESTCASE_H +#include +#include +#include "esp/uart.h" + +/* Unity is the framework with test assertions, etc. */ +#include "unity.h" + +/* Need to explicitly flag once a test has completed successfully. */ +#undef TEST_PASS +#define TEST_PASS() do { UnityConcludeTest(); while(1) { } } while (0) + +/* Types of test, defined by hardware requirements */ +typedef enum { + SOLO, /* Test require "ESP A" only, no other connections */ + DUAL, /* Test requires "ESP A" and "ESP "B", basic interconnections between them */ + EYORE_TEST, /* Test requires an eyore-test board with onboard STM32F0 */ +} testcase_type_t; + +typedef void (testcase_fn_t)(void); + +typedef struct { + const char *name; + const char *file; + int line; + testcase_type_t type; + testcase_fn_t *a_fn; + testcase_fn_t *b_fn; +} testcase_t; + +void testcase_register(const testcase_t *testcase); + +/* Register a test case using these macros. Use DEFINE_SOLO_TESTCASE for single-MCU tests, + and DEFINE_TESTCASE for all other test types. +*/ +#define DEFINE_SOLO_TESTCASE(NAME) \ + static testcase_fn_t a_##NAME; \ + _DEFINE_TESTCASE_COMMON(NAME, SOLO, a_##NAME, 0) + +#define DEFINE_TESTCASE(NAME, TYPE) \ + static testcase_fn_t a_##NAME; \ + static testcase_fn_t b_##NAME; \ + _DEFINE_TESTCASE_COMMON(NAME, TYPE, a_##NAME, b_##NAME) + + +#define _DEFINE_TESTCASE_COMMON(NAME, TYPE, A_FN, B_FN) \ + void __attribute__((constructor)) testcase_ctor_##NAME() { \ + const testcase_t testcase = { .name = #NAME, \ + .file = __FILE__, \ + .line = __LINE__, \ + .type = TYPE, \ + .a_fn = A_FN, \ + .b_fn = B_FN, \ + }; \ + testcase_register(&testcase); \ + } + +#endif diff --git a/tests/test_main.c b/tests/test_main.c new file mode 100644 index 0000000..efc61b1 --- /dev/null +++ b/tests/test_main.c @@ -0,0 +1,122 @@ +#include "testcase.h" +#include +#include +#include +#include +#include +#include + +/* Convert requirement enum to a string we can print */ +static const char *get_requirements_name(const testcase_type_t arg) { + switch(arg) { + case SOLO: + return "SOLO"; + case DUAL: + return "DUAL"; + case EYORE_TEST: + return "EYORE_TEST"; + default: + return "UNKNOWN"; + } +} + +static testcase_t *testcases; +static uint32_t testcases_count; +static uint32_t testcases_alloc; + +void testcase_register(const testcase_t *testcase) +{ + /* Grow the testcases buffer to fit the new test case, + this buffer will be freed before the test runs. + */ + if(testcases_count == testcases_alloc) { + testcases_alloc += 1; + testcases = realloc(testcases, testcases_alloc * sizeof(testcase_t)); + if(!testcases) { + printf("Failed to reallocate test case register length %d\n", + testcases_alloc); + testcases_count = 0; + testcases_alloc = 0; + } + } + memcpy(&testcases[testcases_count++], testcase, sizeof(testcase_t)); +} + +void user_init(void) +{ + uart_set_baud(0, 115200); + sdk_wifi_set_opmode(NULL_MODE); + printf("esp-open-rtos test runner.\n"); + printf("%d test cases are defined:\n\n", testcases_count); + for(int i = 0; i < testcases_count; i++) { + printf("CASE %d = %s %s\n", i, testcases[i].name, + get_requirements_name(testcases[i].type)); + } + + printf("Enter A or B then number of test case to run, ie A0.\n"); + int case_idx = -1; + char type; + do { + printf("> "); + uart_rxfifo_wait(0,1); + type = uart_getc(0); + if(type != 'a' && type != 'A' && type != 'b' && type != 'B') { + printf("Type must be A or B.\n"); + continue; + } + + char idx_buf[6]; + for(int c = 0; c < sizeof(idx_buf); c++) { + uart_rxfifo_wait(0,1); + idx_buf[c] = uart_getc(0); + if(idx_buf[c] == ' ') { /* Ignore spaces */ + c--; + continue; + } + if(idx_buf[c] == '\r' || idx_buf[c] == '\n') { + idx_buf[c] = 0; + case_idx = atoi(idx_buf); + break; + } + else if(idx_buf[c] < '0' || idx_buf[c] > '9') { + break; + } + } + + if(case_idx == -1) { + printf("Invalid case index"); + } + else if(case_idx < 0 || case_idx >= testcases_count) { + printf("Test case index out of range.\n"); + } + else if((type == 'b' || type =='B') && testcases[case_idx].type == SOLO) { + printf("No ESP type B for 'SOLO' test cases.\n"); + } else { + break; + } + } while(1); + if(type =='a') + type = 'A'; + else if (type == 'b') + type = 'B'; + testcase_t testcase; + memcpy(&testcase, &testcases[case_idx], sizeof(testcase_t)); + /* Free the register of test cases now we have the one we're running */ + free(testcases); + testcases_alloc = 0; + testcases_count = 0; + + printf("\nRunning test case %d (%s %s) as instance %c " + "\nDefinition at %s:%d\n***\n", case_idx, + testcase.name, get_requirements_name(testcase.type), type, + testcase.file, testcase.line); + + Unity.CurrentTestName = testcase.name; + Unity.TestFile = testcase.file; + Unity.CurrentTestLineNumber = testcase.line; + Unity.NumberOfTests = 1; + if(type=='A') + testcase.a_fn(); + else + testcase.b_fn(); +} diff --git a/tests/test_runner.py b/tests/test_runner.py new file mode 100755 index 0000000..347fd9b --- /dev/null +++ b/tests/test_runner.py @@ -0,0 +1,389 @@ +#!/usr/bin/env python3 +import sys +import argparse +import subprocess +import os +import serial +import threading +import re +import time + + +SHORT_OUTPUT_TIMEOUT = 0.25 # timeout for resetting and/or waiting for more lines of output +TESTCASE_TIMEOUT = 60 +TESTRUNNER_BANNER = "esp-open-rtos test runner." +RESET_RETRIES = 10 # retries to receive test runner banner after reset + + +def run(env_a, env_b, cases): + counts = dict((status, 0) for status in TestResult.STATUS_NAMES.keys()) + failures = False + for test in cases: + if test.case_type == 'dual': + if env_b is None: + res = TestResult(TestResult.SKIPPED, 'Dual test case skipped') + else: + res = test.run(env_a, env_b) + else: + res = test.run(env_a) + counts[res.status] += 1 + failures = failures or res.is_failure() + + print("%20s: %d" % ("Total tests", sum(c for c in counts.values()))) + print() + # print status counts for tests + for c in sorted(counts.keys()): + print("%20s: %d" % (TestResult.STATUS_NAMES[c], counts[c])) + + return failures == 0 + + +def main(): + global verbose + args = parse_args() + verbose = args.verbose + + if not args.no_flash: + flash_image(args.aport) + if args.type != 'solo': + flash_image(args.bport) + + env = TestEnvironment(args.aport, TestEnvironment.A) + env_b = None + cases = env.get_testlist() + if args.type != 'solo': + env_b = TestEnvironment(args.bport, TestEnvironment.B) + cases_b = env_b.get_testlist() + if cases != cases_b: + raise TestRunnerError("Test cases on units A & B don't match") + + if args.list: # if list option is specified, do not run test cases + print("List of test cases:") + for test in cases: + print(test) + sys.exit(0) + + if args.testcases: # if testcases is specified run only those cases + cases = [c for c in cases if str(c.index) in args.testcases] + + sys.exit(0 if run(env, env_b, cases) else 1) + + +class TestCase(object): + def __init__(self, index, name, case_type): + self.name = name + self.index = index + self.case_type = case_type + + def __repr__(self): + return "#%d: %s (%s)" % (self.index, self.name, self.case_type) + + def __eq__(self, other): + return (self.index == other.index and + self.name == other.name and + self.case_type == other.case_type) + + def run(self, env_a, env_b=None): + """ + Run the test represented by this instance, against the environment(s) passed in. + + Returns a TestResult + """ + sys.stdout.write("Running test case '%s'...%s" % (self.name, "\n" if verbose else " "*(40-len(self.name)))) + mon_a = env_a.start_testcase(self) + mon_b = env_b.start_testcase(self) if env_b else None + while True: + if mon_a.get_result() and (mon_b is None or mon_b.get_result()): + break # all running test environments have finished + + # or, in the case both are running, stop as soon as either environemnt shows a failure + try: + if mon_a.get_result().is_failure(): + mon_b.cancel() + break + except AttributeError: + pass + try: + if mon_b.get_result().is_failure(): + mon_a.cancel() + break + except AttributeError: + pass + time.sleep(0.1) + + if mon_b is not None: + # return whichever result is more severe + res = max(mon_a.get_result(), mon_b.get_result()) + else: + res = mon_a.get_result() + if not verbose: # finish the line after the ... + print(TestResult.STATUS_NAMES[res.status]) + if res.is_failure(): + message = res.message + if "/" in res.message: # cut anything before the file name in the failure + message = message[message.index("/"):] + print("FAILURE MESSAGE:\n%s\n" % message) + return res + + +class TestResult(object): + """ Class to wrap a test result code and a message """ + # Test status flags, higher = more severe + CANCELLED = 0 + SKIPPED = 1 + PASSED = 2 + FAILED = 3 + ERROR = 4 + + STATUS_NAMES = { + CANCELLED: "Cancelled", + SKIPPED: "Skipped", + PASSED: "Passed", + FAILED: "Failed", + ERROR: "Error" + } + + def __init__(self, status, message): + self.status = status + self.message = message + + def is_failure(self): + return self.status >= TestResult.FAILED + + def __qe__(self, other): + if other is None: + return False + else: + return self.status == other.status + + def __lt__(self, other): + if other is None: + return False + else: + return self.status < other.status + + +class TestMonitor(object): + """ Class to monitor a running test case in a separate thread, defer reporting of the result until it's done. + + Can poll for completion by calling is_done(), read a TestResult via .get_result() + """ + def __init__(self, port, instance): + super(TestMonitor, self).__init__() + self._thread = threading.Thread(target=self._monitorThread) + self._port = port + self._instance = instance + self._result = None + self._cancelled = False + self.output = "" + self._thread.start() + + def cancel(self): + self._cancelled = True + + def is_done(self): + return self._result is not None + + def get_result(self): + return self._result + + def _monitorThread(self): + self.output = "" + start_time = time.time() + self._port.timeout = SHORT_OUTPUT_TIMEOUT + try: + while not self._cancelled and time.time() < start_time + TESTCASE_TIMEOUT: + line = self._port.readline().decode("utf-8", "ignore") + if line == "": + continue # timed out + self.output += "%s+%4.2fs %s" % (self._instance, time.time()-start_time, line) + verbose_print(line.strip()) + if line.endswith(":PASS\r\n"): + self._result = TestResult(TestResult.PASSED, "Test passed.") + return + elif ":FAIL:" in line: + self._result = TestResult(TestResult.FAILED, line) + return + elif line == TESTRUNNER_BANNER: + self._result = TestResult(TestResult.ERROR, "Test caused crash and reset.") + return + if not self._cancelled: + self._result = TestResult(TestResult.CANCELLED, "Cancelled") + else: + self._result = TestResult(TestResult.ERROR, "Test timed out") + + finally: + self._port.timeout = None + + +class TestEnvironment(object): + A = "A" + B = "B" + + def __init__(self, port, instance): + self._name = port + self._port = TestSerialPort(port, baudrate=115200) + self._instance = instance + + def reset(self): + """ Resets the test board, and waits for the test runner program to start up """ + for i in range(RESET_RETRIES): + self._port.setDTR(False) + self._port.setRTS(True) + time.sleep(0.05) + self._port.flushInput() + self._port.setRTS(False) + verbose_print("Waiting for test runner startup...") + if self._port.wait_line(lambda line: line == TESTRUNNER_BANNER): + return + else: + verbose_print("Retrying to reset the test board, attempt=%d" % + (i + 1)) + continue + raise TestRunnerError("Port %s failed to start test runner" % self._port) + + def get_testlist(self): + """ Resets the test board and returns the enumerated list of all supported tests """ + self.reset() + tests = [] + verbose_print("Enumerating tests...") + + def collect_testcases(line): + if line.startswith(">"): + return True # prompt means list of test cases is done, success + m = re.match(r"CASE (\d+) = (.+?) ([A-Z]+)", line) + if m is not None: + t = TestCase(int(m.group(1)), m.group(2), m.group(3).lower()) + verbose_print(t) + tests.append(t) + if not self._port.wait_line(collect_testcases): + raise TestRunnerError("Port %s failed to read test list" % self._port) + verbose_print("Port %s found %d test cases" % (self._name, len(tests))) + return tests + + def start_testcase(self, case): + """ Starts the specified test instance and returns a TestMonitor reader thread instance + to monitor the output + """ + # synchronously start the test case + self.reset() + if not self._port.wait_line(lambda line: line.startswith(">")): + raise TestRunnerError("Failed to read test runnner prompt") + command = "%s%d\r\n" % (self._instance, case.index) + self._port.write(command.encode("utf-8")) + return TestMonitor(self._port, self._instance) + + +def get_testdir(): + """ + Return the 'tests' directory in the source tree + (assuming the test_runner.py script is in that directory. + """ + res = os.path.dirname(__name__) + return "." if res == "" else res + + +def flash_image(serial_port): + # Bit hacky: rather than calling esptool directly, + # just use the Makefile flash target with the correct ESPPORT argument + env = dict(os.environ) + env["ESPPORT"] = serial_port + verbose_print("Building and flashing test image to %s..." % serial_port) + try: + stdout = sys.stdout if verbose else None + subprocess.check_call(["make", "flash"], cwd=get_testdir(), + stdout=stdout, stderr=subprocess.STDOUT, env=env) + except subprocess.CalledProcessError as e: + raise TestRunnerError("'make flash EPPORT=%s' failed with exit code %d" % + (serial_port, e.returncode)) + verbose_print("Flashing successful.") + + +def parse_args(): + parser = argparse.ArgumentParser(description='esp-open-rtos testrunner', prog='test_runner') + + parser.add_argument( + '--type', '-t', + help='Type of test hardware attached to serial ports A & (optionally) B', + choices=['solo', 'dual', 'eyore_test'], default='solo') + + parser.add_argument( + '--aport', '-a', + help='Serial port for device A', + default='/dev/ttyUSB0') + + parser.add_argument( + '--bport', '-b', + help='Serial port for device B (ignored if type is \'solo\')', + default='/dev/ttyUSB1') + + parser.add_argument( + '--no-flash', '-n', + help='Don\'t flash the test binary image before running tests', + action='store_true', + default=False) + + parser.add_argument( + '--list', '-l', + help='Display list of available test cases on a device', + action='store_true', + default=False) + + parser.add_argument( + '--verbose', '-v', + help='Verbose test runner debugging output', + action='store_true', + default=False) + + parser.add_argument('testcases', nargs='*', + help='Optional list of test case numbers to run. ' + 'By default, all tests are run.') + + return parser.parse_args() + + +class TestRunnerError(RuntimeError): + def __init__(self, message): + RuntimeError.__init__(self, message) + + +class TestSerialPort(serial.Serial): + def __init__(self, *args, **kwargs): + super(TestSerialPort, self).__init__(*args, **kwargs) + + def wait_line(self, callback, timeout=SHORT_OUTPUT_TIMEOUT): + """ Wait for the port to output a particular piece of line content, as judged by callback + + Callback called as 'callback(line)' and returns not-True if non-match otherwise can return any value. + + Returns first non-False result from the callback, or None if it timed out waiting for a new line. + + Note that a serial port spewing legitimate lines of output may block this function forever, if callback + doesn't detect this is happening. + """ + self.timeout = timeout + try: + res = None + while not res: + line = self.readline() + if line == b"": + break # timed out + line = line.decode("utf-8", "ignore").rstrip() + res = callback(line) + return res + finally: + self.timeout = None + +verbose = False + + +def verbose_print(msg): + if verbose: + print(msg) + +if __name__ == '__main__': + try: + main() + except TestRunnerError as e: + print(e) + sys.exit(2) diff --git a/tests/unity b/tests/unity new file mode 160000 index 0000000..bbf2fe3 --- /dev/null +++ b/tests/unity @@ -0,0 +1 @@ +Subproject commit bbf2fe3a934f96cd00693841247a689e57a17b0d