diff --git a/Makefile b/Makefile index 5171629..63c0d6b 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # project subdirectory. # -PROJECT_NAME := esp32_ota_https +PROJECT_NAME := simple_ota EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common diff --git a/components/lcd/.vscode/c_cpp_properties.json b/components/lcd/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..88f7c97 --- /dev/null +++ b/components/lcd/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/clang", + "cStandard": "c11", + "cppStandard": "c++14", + "intelliSenseMode": "clang-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/components/lcd/CMakeLists.txt b/components/lcd/CMakeLists.txt new file mode 100644 index 0000000..41ec384 --- /dev/null +++ b/components/lcd/CMakeLists.txt @@ -0,0 +1,6 @@ +set(srcs "src/LCD.c" + "src/Driver.c") + +idf_component_register(SRCS "${lcd_srcs}" + INCLUDE_DIRS "include") + diff --git a/components/lcd/component.mk b/components/lcd/component.mk new file mode 100644 index 0000000..24cab8b --- /dev/null +++ b/components/lcd/component.mk @@ -0,0 +1,3 @@ +COMPONENT_ADD_INCLUDEDIRS := include + +COMPONENT_SRCDIRS := src diff --git a/components/lcd/include/Driver.h b/components/lcd/include/Driver.h new file mode 100644 index 0000000..85596fb --- /dev/null +++ b/components/lcd/include/Driver.h @@ -0,0 +1,58 @@ +/*! @file Driver.h + + @brief + @author Hendrik Schutter + @version V1.0 + @date 03.11.2020 +*/ + +#ifndef __DRIVER_H +#define __DRIVER_H + +#include +#include +#include +#include "driver/spi_master.h" +#include "esp_system.h" +#include "driver/gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#define LCD_HOST HSPI_HOST +#define DMA_CHAN 2 + +#define PIN_NUM_MISO 25 +#define PIN_NUM_MOSI 23 +#define PIN_NUM_CLK 19 +#define PIN_NUM_CS 22 + +#define PIN_NUM_DC 21 +#define PIN_NUM_RST 18 +#define PIN_NUM_BCKL 5 + +#define LCD_WIDTH 320 +#define LCD_HIGH 240 + +//To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many. More means more memory use, +//but less overhead for setting up / finishing transfers. Make sure 240 is dividable by this. +#define PARALLEL_LINES 16 + +/* + The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. +*/ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} lcd_init_cmd_t; + +typedef enum { + LCD_TYPE_ILI = 1, + LCD_TYPE_ST, + LCD_TYPE_MAX, +} type_lcd_t; + +esp_err_t vDriver_init(void); +esp_err_t iDriver_writeFramebuffer(uint16_t ***pu16Framebuffer); + +#endif /* __DRIVER_H */ diff --git a/components/lcd/include/LCD.h b/components/lcd/include/LCD.h new file mode 100644 index 0000000..b90d8e2 --- /dev/null +++ b/components/lcd/include/LCD.h @@ -0,0 +1,29 @@ +/*! @file LCD.h + + @brief + @author Hendrik Schutter + @version V1.0 + @date 03.11.2020 +*/ + +#ifndef __LCD_H +#define __LCD_H + +#include +#include +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include +#include "esp_system.h" + +#define COLOR_RED 0xF800 +#define COLOR_GREEN 0x07E0 +#define COLOR_BLUE 0x001F +#define COLOR_WHITE 0xFFFF +#define COLOR_BLACK 0x0000 + +esp_err_t iLCD_init(void); +esp_err_t iLCD_clearFramebuffer(uint16_t u16Color); +esp_err_t iLCD_writeString(uint16_t u16xPos, uint16_t u16yPos, char *pcText, uint16_t u16ColorFont, uint16_t u16ColorBackground); + +#endif /* __LCD_H */ diff --git a/components/lcd/src/Driver.c b/components/lcd/src/Driver.c new file mode 100644 index 0000000..64a4e3a --- /dev/null +++ b/components/lcd/src/Driver.c @@ -0,0 +1,468 @@ +/*! @file Driver.c + + @brief write the Framebuffer + @author Hendrik Schutter + @version V1.0 + @date 03.11.2020 + + This code used SPI to write data to the LCD driver like ILI9341 or ST7789V. +*/ + +#include "Driver.h" + +spi_device_handle_t spi; //SPI + +uint16_t *lines[2]; +int16_t sending_line=-1; +int16_t calc_line=0; + +uint32_t vDriver_GetId(void); +static esp_err_t iDriver_sendLineFinish(void); +void vDriver_spiPreTransferCallback(spi_transaction_t *t); +esp_err_t iDriver_sendCmd(const uint8_t u8Cmd); +static esp_err_t iDriver_sendLines(uint16_t u16Ypos, uint16_t *pu16Linedata); +esp_err_t iDriver_SendData(const uint8_t *pu8Data, uint16_t u16Len); +void vDriver_getFramenufferPerLine(uint16_t *pu16Dest, uint16_t u16Line, uint16_t u16Linect, uint16_t ***pu16Framebuffer); + +//Place data into DRAM. Constant data gets placed into DROM by default, which is not accessible by DMA. +DRAM_ATTR static const lcd_init_cmd_t st_init_cmds[]= { + /* Memory Data Access Control, MX=MV=1, MY=ML=MH=0, RGB=0 */ + {0x36, {(1<<5)|(1<<6)}, 1}, + /* Interface Pixel Format, 16bits/pixel for RGB/MCU interface */ + {0x3A, {0x55}, 1}, + /* Porch Setting */ + {0xB2, {0x0c, 0x0c, 0x00, 0x33, 0x33}, 5}, + /* Gate Control, Vgh=13.65V, Vgl=-10.43V */ + {0xB7, {0x45}, 1}, + /* VCOM Setting, VCOM=1.175V */ + {0xBB, {0x2B}, 1}, + /* LCM Control, XOR: BGR, MX, MH */ + {0xC0, {0x2C}, 1}, + /* VDV and VRH Command Enable, enable=1 */ + {0xC2, {0x01, 0xff}, 2}, + /* VRH Set, Vap=4.4+... */ + {0xC3, {0x11}, 1}, + /* VDV Set, VDV=0 */ + {0xC4, {0x20}, 1}, + /* Frame Rate Control, 60Hz, inversion=0 */ + {0xC6, {0x0f}, 1}, + /* Power Control 1, AVDD=6.8V, AVCL=-4.8V, VDDS=2.3V */ + {0xD0, {0xA4, 0xA1}, 1}, + /* Positive Voltage Gamma Control */ + {0xE0, {0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19}, 14}, + /* Negative Voltage Gamma Control */ + {0xE1, {0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19}, 14}, + /* Sleep Out */ + {0x11, {0}, 0x80}, + /* Display On */ + {0x29, {0}, 0x80}, + {0, {0}, 0xff} +}; + +DRAM_ATTR static const lcd_init_cmd_t ili_init_cmds[]= { + /* Power contorl B, power control = 0, DC_ENA = 1 */ + {0xCF, {0x00, 0x83, 0X30}, 3}, + /* Power on sequence control, + * cp1 keeps 1 frame, 1st frame enable + * vcl = 0, ddvdh=3, vgh=1, vgl=2 + * DDVDH_ENH=1 + */ + {0xED, {0x64, 0x03, 0X12, 0X81}, 4}, + /* Driver timing control A, + * non-overlap=default +1 + * EQ=default - 1, CR=default + * pre-charge=default - 1 + */ + {0xE8, {0x85, 0x01, 0x79}, 3}, + /* Power control A, Vcore=1.6V, DDVDH=5.6V */ + {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5}, + /* Pump ratio control, DDVDH=2xVCl */ + {0xF7, {0x20}, 1}, + /* Driver timing control, all=0 unit */ + {0xEA, {0x00, 0x00}, 2}, + /* Power control 1, GVDD=4.75V */ + {0xC0, {0x26}, 1}, + /* Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 */ + {0xC1, {0x11}, 1}, + /* VCOM control 1, VCOMH=4.025V, VCOML=-0.950V */ + {0xC5, {0x35, 0x3E}, 2}, + /* VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 */ + {0xC7, {0xBE}, 1}, + /* Memory access contorl, MX=MY=0, MV=1, ML=0, BGR=1, MH=0 */ + {0x36, {0x28}, 1}, + /* Pixel format, 16bits/pixel for RGB/MCU interface */ + {0x3A, {0x55}, 1}, + /* Frame rate control, f=fosc, 70Hz fps */ + {0xB1, {0x00, 0x1B}, 2}, + /* Enable 3G, disabled */ + {0xF2, {0x08}, 1}, + /* Gamma set, curve 1 */ + {0x26, {0x01}, 1}, + /* Positive gamma correction */ + {0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15}, + /* Negative gamma correction */ + {0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15}, + /* Column address set, SC=0, EC=0xEF */ + {0x2A, {0x00, 0x00, 0x00, 0xEF}, 4}, + /* Page address set, SP=0, EP=0x013F */ + {0x2B, {0x00, 0x00, 0x01, 0x3f}, 4}, + /* Memory write */ + {0x2C, {0}, 0}, + /* Entry mode set, Low vol detect disabled, normal display */ + {0xB7, {0x07}, 1}, + /* Display function control */ + {0xB6, {0x0A, 0x82, 0x27, 0x00}, 4}, + /* Sleep out */ + {0x11, {0}, 0x80}, + /* Display on */ + {0x29, {0}, 0x80}, + {0, {0}, 0xff}, +}; + +/** + * @fn esp_err_t vDriver_init(void) + * @brief Initialize the driver + * @param void + * @return esp error code + * @author Hendrik Schutter + * @date 3.11.2020 + */ +esp_err_t vDriver_init(void) +{ + esp_err_t ret; //store esp error code + + spi_bus_config_t buscfg= { + .miso_io_num=PIN_NUM_MISO, + .mosi_io_num=PIN_NUM_MOSI, + .sclk_io_num=PIN_NUM_CLK, + .quadwp_io_num=-1, + .quadhd_io_num=-1, + .max_transfer_sz=PARALLEL_LINES*320*2+8 + }; + + spi_device_interface_config_t devcfg= { +#ifdef CONFIG_LCD_OVERCLOCK + .clock_speed_hz=26*1000*1000, //Clock out at 26 MHz +#else + .clock_speed_hz=10*1000*1000, //Clock out at 10 MHz +#endif + .mode=0, //SPI mode 0 + .spics_io_num=PIN_NUM_CS, //CS pin + .queue_size=7, //We want to be able to queue 7 transactions at a time + .pre_cb=vDriver_spiPreTransferCallback, //Specify pre-transfer callback to handle D/C line + }; + + //Initialize the SPI bus + ret=spi_bus_initialize(LCD_HOST, &buscfg, DMA_CHAN); + ESP_ERROR_CHECK(ret); + //Attach the LCD to the SPI bus + ret=spi_bus_add_device(LCD_HOST, &devcfg, &spi); + ESP_ERROR_CHECK(ret); + + int cmd=0; + const lcd_init_cmd_t* lcd_init_cmds; + + //Initialize non-SPI GPIOs + gpio_set_direction(PIN_NUM_DC, GPIO_MODE_OUTPUT); + gpio_set_direction(PIN_NUM_RST, GPIO_MODE_OUTPUT); + gpio_set_direction(PIN_NUM_BCKL, GPIO_MODE_OUTPUT); + + //Reset the display + gpio_set_level(PIN_NUM_RST, 0); + vTaskDelay(100 / portTICK_RATE_MS); + gpio_set_level(PIN_NUM_RST, 1); + vTaskDelay(100 / portTICK_RATE_MS); + + //detect LCD type + uint32_t lcd_id = vDriver_GetId(); + int lcd_detected_type = 0; + int lcd_type; + + printf("LCD ID: %08X\n", lcd_id); + if ( lcd_id == 0 ) { + //zero, ili + lcd_detected_type = LCD_TYPE_ILI; + printf("ILI9341 detected.\n"); + } else { + // none-zero, ST + lcd_detected_type = LCD_TYPE_ST; + printf("ST7789V detected.\n"); + } + +#ifdef CONFIG_LCD_TYPE_AUTO + lcd_type = lcd_detected_type; +#elif defined( CONFIG_LCD_TYPE_ST7789V ) + printf("kconfig: force CONFIG_LCD_TYPE_ST7789V.\n"); + lcd_type = LCD_TYPE_ST; +#elif defined( CONFIG_LCD_TYPE_ILI9341 ) + printf("kconfig: force CONFIG_LCD_TYPE_ILI9341.\n"); + lcd_type = LCD_TYPE_ILI; +#endif + if ( lcd_type == LCD_TYPE_ST ) { + printf("LCD ST7789V initialization.\n"); + lcd_init_cmds = st_init_cmds; + } else { + printf("LCD ILI9341 initialization.\n"); + lcd_init_cmds = ili_init_cmds; + } + + //Send all the commands + while (lcd_init_cmds[cmd].databytes!=0xff) { + ret = iDriver_sendCmd(lcd_init_cmds[cmd].cmd); + assert(ret==ESP_OK); + ret = iDriver_SendData(lcd_init_cmds[cmd].data, lcd_init_cmds[cmd].databytes&0x1F); + if (lcd_init_cmds[cmd].databytes&0x80) { + vTaskDelay(100 / portTICK_RATE_MS); + } + cmd++; + } + + //Allocate memory for the line buffers + for (int i=0; i<2; i++) { + lines[i]=heap_caps_malloc(320*PARALLEL_LINES*sizeof(uint16_t), MALLOC_CAP_DMA); + assert(lines[i]!=NULL); + } + + ///Enable backlight + gpio_set_level(PIN_NUM_BCKL, 0); + + return ret; +} + +/** + * @fn void iDriver_sendCmd(const uint8_t u8Cmd) + * @brief Send a command to the LCD. + * @param const uint8_t u8Cmd + * @return esp error code + * @author Hendrik Schutter + * @date 3.11.2020 + * + * Send a command to the LCD. Uses spi_device_polling_transmit, which waits + * until the transfer is complete. + * + * Since command transactions are usually small, they are handled in polling + * mode for higher speed. The overhead of interrupt transactions is more than + * just waiting for the transaction to complete. + */ +esp_err_t iDriver_sendCmd(const uint8_t u8Cmd) +{ + esp_err_t ret; + spi_transaction_t t; + memset(&t, 0, sizeof(t)); //Zero out the transaction + t.length=8; //Command is 8 bits + t.tx_buffer=&u8Cmd; //The data is the cmd itself + t.user=(void*)0; //D/C needs to be set to 0 + ret=spi_device_polling_transmit(spi, &t); //Transmit! + assert(ret==ESP_OK); //Should have had no issues. + return ret; +} + +/** + * @fn esp_err_t iDriver_SendData(const uint8_t *pu8Data, uint16_t u16Len) + * @brief Send data to the LCD. + * @param const uint8_t *pu8Data + * @param uint16_t u16Len + * @return esp error code + * @author Hendrik Schutter + * @date 3.11.2020 + * + * Send data to the LCD. Uses spi_device_polling_transmit, which waits until the + * transfer is complete. + * + * Since data transactions are usually small, they are handled in polling + * mode for higher speed. The overhead of interrupt transactions is more than + * just waiting for the transaction to complete. + */ +esp_err_t iDriver_SendData(const uint8_t *pu8Data, uint16_t u16Len) +{ + esp_err_t ret; + spi_transaction_t t; + if (u16Len==0) return ESP_FAIL; //no need to send anything + memset(&t, 0, sizeof(t)); //Zero out the transaction + t.length=u16Len*8; //Len is in bytes, transaction length is in bits. + t.tx_buffer=pu8Data; //Data + t.user=(void*)1; //D/C needs to be set to 1 + ret=spi_device_polling_transmit(spi, &t); //Transmit! + return ret; +} + +/** + * @fn void vDriver_spiPreTransferCallback(spi_transaction_t *t) + * @brief Pre Transfer Callback + * @param spi_transaction_t *t + * @return void + * @author Hendrik Schutter + * @date 3.11.2020 + * + * This function is called (in irq context!) just before a transmission starts. It will + * set the D/C line to the value indicated in the user field. + */ +void vDriver_spiPreTransferCallback(spi_transaction_t *t) +{ + int dc=(int)t->user; + gpio_set_level(PIN_NUM_DC, dc); +} + +/** + * @fn uint32_t vDriver_GetId(void) + * @brief get LCD driver chip id + * @param void + * @return uint32_t + * @author Hendrik Schutter + * @date 3.11.2020 + */ +uint32_t vDriver_GetId(void) +{ + esp_err_t ret; + //get_id cmd + ret = iDriver_sendCmd(0x04); + assert(ret==ESP_OK); + + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length=8*3; + t.flags = SPI_TRANS_USE_RXDATA; + t.user = (void*)1; + + ret = spi_device_polling_transmit(spi, &t); + assert( ret == ESP_OK ); + + return *(uint32_t*)t.rx_data; +} + +/** + * @fn esp_err_t iDriver_writeFramebuffer(uint16_t ***pu16Framebuffer) + * @brief write framebuffer to LCD via SPI + * @param uint16_t ***pu16Framebuffer + * @return esp error code + * @author Hendrik Schutter + * @date 3.11.2020 + */ +esp_err_t iDriver_writeFramebuffer(uint16_t ***pu16Framebuffer) +{ + esp_err_t ret; + for (int y=0; y<240; y+=PARALLEL_LINES) { + vDriver_getFramenufferPerLine(lines[calc_line], y, PARALLEL_LINES, pu16Framebuffer); + //Finish up the sending process of the previous line, if any + if (sending_line!=-1) + { + ret = iDriver_sendLineFinish(); + } + //Swap sending_line and calc_line + sending_line=calc_line; + calc_line=(calc_line==1)?0:1; + //Send the line we currently calculated. + iDriver_sendLines(y, lines[sending_line]); + //The line set is queued up for sending now; the actual sending happens in the + //background. We can go on to calculate the next line set as long as we do not + //touch line[sending_line]; the SPI sending process is still reading from that. + } + return ret; +} + +/** + * @fn void vDriver_getFramenufferPerLine(uint16_t *pu16Dest, uint16_t u16Line, uint16_t u16Linect, uint16_t ***pu16Framebuffer) + * @brief Get the pixel data for a set of lines (with implied line size of 320) + * @param uint16_t *pu16Dest + * @param uint16_t u16Line + * @param uint16_t u16Linect + * @param uint16_t ***pu16Framebuffer + * @return void + * @author Hendrik Schutter + * @date 3.11.2020 + */ +void vDriver_getFramenufferPerLine(uint16_t *pu16Dest, uint16_t u16Line, uint16_t u16Linect, uint16_t ***pu16Framebuffer) +{ + for (uint16_t y=u16Line; y>8; //End Col High + trans[1].tx_data[3]=(320)&0xff; //End Col Low + trans[2].tx_data[0]=0x2B; //Page address set + trans[3].tx_data[0]=u16Ypos>>8; //Start page high + trans[3].tx_data[1]=u16Ypos&0xff; //start page low + trans[3].tx_data[2]=(u16Ypos+PARALLEL_LINES)>>8; //end page high + trans[3].tx_data[3]=(u16Ypos+PARALLEL_LINES)&0xff; //end page low + trans[4].tx_data[0]=0x2C; //memory write + trans[5].tx_buffer=pu16Linedata; //finally send the line data + trans[5].length=320*2*8*PARALLEL_LINES; //Data length, in bits + trans[5].flags=0; //undo SPI_TRANS_USE_TXDATA flag + + //Queue all transactions. + for (uint8_t x=0; x<6; x++) { + ret=spi_device_queue_trans(spi, &trans[x], portMAX_DELAY); + assert(ret==ESP_OK); + } + + //When we are here, the SPI driver is busy (in the background) getting the transactions sent. That happens + //mostly using DMA, so the CPU doesn't have much to do here. We're not going to wait for the transaction to + //finish because we may as well spend the time calculating the next line. When that is done, we can call + //send_line_finish, which will wait for the transfers to be done and check their status. + return ret; +} + +/** + * @fn static esp_err_t iDriver_sendLineFinish(void) + * @brief send finish after line + * @param void + * @return esp error code + * @author Hendrik Schutter + * @date 3.11.2020 + */ +static esp_err_t iDriver_sendLineFinish(void) +{ + spi_transaction_t *rtrans; + esp_err_t ret; + //Wait for all 6 transactions to be done and get back the results. + for (uint8_t x=0; x<6; x++) { + ret=spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY); + assert(ret==ESP_OK); + //We could inspect rtrans now if we received any info back. The LCD is treated as write-only, though. + } + return ret; +} \ No newline at end of file diff --git a/components/lcd/src/LCD.c b/components/lcd/src/LCD.c new file mode 100644 index 0000000..f406326 --- /dev/null +++ b/components/lcd/src/LCD.c @@ -0,0 +1,251 @@ +/*! @file LCD.c + + @brief sets and writes framebuffer + @author Hendrik Schutter + @version V1.0 + @date 03.11.2020 + + This code displays graphics on the 320x240 LCD on an ESP-WROVER_KIT board. + + Some info about the ILI9341/ST7789V: It has an C/D line, which is connected to a GPIO here. It expects this + line to be low for a command and high for data. We use a pre-transmit callback here to control that + line: every transaction has as the user-definable argument the needed state of the D/C line and just + before the transaction is sent, the callback will set this line to the correct state. + +*/ + +#include "LCD.h" +#include "Driver.h" + +esp_err_t iLCD_allocateFramebuffer(uint16_t ***pData); +void vLCD_writeChar(uint16_t u16xPos, uint16_t u16yPos, char cCharacter,uint16_t u16ColorFont,uint16_t u16ColorBackground); + +uint16_t **pu16Framebuffer; //framebuffer + +//ASCII Font 6x8 +static const uint8_t font6_8[][6] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, //sp0 + { 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00 }, // !1 + { 0x00, 0x00, 0x07, 0x00, 0x07, 0x00 }, // "2 + { 0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14 }, // #3 + { 0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12 }, // $4 + { 0x00, 0x62, 0x64, 0x08, 0x13, 0x23 }, // %5 + { 0x00, 0x36, 0x49, 0x55, 0x22, 0x50 }, // &6 + { 0x00, 0x00, 0x05, 0x03, 0x00, 0x00 }, // '7 + { 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00 }, // (8 + { 0x00, 0x00, 0x41, 0x22, 0x1c, 0x00 }, // )9 + { 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14 }, // *10 + { 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08 }, // +11 + { 0x00, 0x00, 0x00, 0xA0, 0x60, 0x00 }, // ,12 + { 0x00, 0x08, 0x08, 0x08, 0x08, 0x08 }, // -13 + { 0x00, 0x00, 0x60, 0x60, 0x00, 0x00 }, // .14 + { 0x00, 0x20, 0x10, 0x08, 0x04, 0x02 }, // /15 + { 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E }, // 016 + { 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00 }, // 117 + { 0x00, 0x42, 0x61, 0x51, 0x49, 0x46 }, // 218 + { 0x00, 0x21, 0x41, 0x45, 0x4B, 0x31 }, // 319 + { 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10 }, // 420 + { 0x00, 0x27, 0x45, 0x45, 0x45, 0x39 }, // 521 + { 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30 }, // 622 + { 0x00, 0x01, 0x71, 0x09, 0x05, 0x03 }, // 723 + { 0x00, 0x36, 0x49, 0x49, 0x49, 0x36 }, // 824 + { 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E }, // 925 + { 0x00, 0x00, 0x36, 0x36, 0x00, 0x00 }, // :26 + { 0x00, 0x00, 0x56, 0x36, 0x00, 0x00 }, // ;27 + { 0x00, 0x08, 0x14, 0x22, 0x41, 0x00 }, // <28 + { 0x00, 0x14, 0x14, 0x14, 0x14, 0x14 }, // =29 + { 0x00, 0x00, 0x41, 0x22, 0x14, 0x08 }, // >30 + { 0x00, 0x02, 0x01, 0x51, 0x09, 0x06 }, // ?31 + { 0x00, 0x32, 0x49, 0x59, 0x51, 0x3E }, // @32 + { 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C }, // A33 + { 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36 }, // B34 + { 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22 }, // C35 + { 0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C }, // D36 + { 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41 }, // E37 + { 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01 }, // F38 + { 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A }, // G39 + { 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F }, // H40 + { 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00 }, // I41 + { 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01 }, // J42 + { 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41 }, // K43 + { 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40 }, // L44 + { 0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F }, // M45 + { 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F }, // N46 + { 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E }, // O47 + { 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06 }, // P48 + { 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E }, // Q49 + { 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46 }, // R50 + { 0x00, 0x46, 0x49, 0x49, 0x49, 0x31 }, // S51 + { 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01 }, // T52 + { 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F }, // U53 + { 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F }, // V54 + { 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F }, // W55 + { 0x00, 0x63, 0x14, 0x08, 0x14, 0x63 }, // X56 + { 0x00, 0x07, 0x08, 0x70, 0x08, 0x07 }, // Y57 + { 0x00, 0x61, 0x51, 0x49, 0x45, 0x43 }, // Z58 + { 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00 }, // [59 + { 0x00, 0x02, 0x04, 0x08, 0x10, 0x20 }, // \60 + { 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00 }, // ]61 + { 0x00, 0x04, 0x02, 0x01, 0x02, 0x04 }, // ^62 + { 0x00, 0x40, 0x40, 0x40, 0x40, 0x40 }, // _63 + { 0x00, 0x00, 0x01, 0x02, 0x04, 0x00 }, // '64 + { 0x00, 0x20, 0x54, 0x54, 0x54, 0x78 }, // a65 + { 0x00, 0x7F, 0x48, 0x44, 0x44, 0x38 }, // b66 + { 0x00, 0x38, 0x44, 0x44, 0x44, 0x20 }, // c67 + { 0x00, 0x38, 0x44, 0x44, 0x48, 0x7F }, // d68 + { 0x00, 0x38, 0x54, 0x54, 0x54, 0x18 }, // e69 + { 0x00, 0x08, 0x7E, 0x09, 0x01, 0x02 }, // f70 + { 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C }, // g71 + { 0x00, 0x7F, 0x08, 0x04, 0x04, 0x78 }, // h72 + { 0x00, 0x00, 0x44, 0x7D, 0x40, 0x00 }, // i73 + { 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00 }, // j74 + { 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00 }, // k75 + { 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00 }, // l76 + { 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78 }, // m77 + { 0x00, 0x7C, 0x08, 0x04, 0x04, 0x78 }, // n78 + { 0x00, 0x38, 0x44, 0x44, 0x44, 0x38 }, // o79 + { 0x00, 0xFC, 0x24, 0x24, 0x24, 0x18 }, // p80 + { 0x00, 0x18, 0x24, 0x24, 0x18, 0xFC }, // q81 + { 0x00, 0x7C, 0x08, 0x04, 0x04, 0x08 }, // r82 + { 0x00, 0x48, 0x54, 0x54, 0x54, 0x20 }, // s83 + { 0x00, 0x04, 0x3F, 0x44, 0x40, 0x20 }, // t84 + { 0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C }, // u85 + { 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C }, // v86 + { 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C }, // w87 + { 0x00, 0x44, 0x28, 0x10, 0x28, 0x44 }, // x88 + { 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C }, // y89 + { 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44 }, // z90 + { 0x14, 0x14, 0x14, 0x14, 0x14, 0x14 } // horiz lines91 +}; + +/** + * @fn esp_err_t iLCD_init(void) + * @brief init + * @param void + * @return int + * @author Hendrik Schutter + * @date 3.11.2020 + */ +esp_err_t iLCD_init(void) { + esp_err_t ret; + + //Initialize the Driver + vDriver_init(); + + //Initialize the framebuffer + ret= iLCD_allocateFramebuffer(&pu16Framebuffer); + ESP_ERROR_CHECK(ret); + + return ret; +} + +/** + * @fn esp_err_t iLCD_allocateFramebuffer(uint16_t ***pData) + * @brief allocate framebuffer + * @param pointer of framebuffer two dimensional array + * @return esp error code + * @author Hendrik Schutter + * @date 3.11.2020 + */ +esp_err_t iLCD_allocateFramebuffer(uint16_t ***pData) +{ + *pData = NULL; + esp_err_t ret = ESP_OK; + //Alocate pixel memory. Each line is an array of IMAGE_W 16-bit pixels; the `*pixels` array itself contains pointers to these lines. + *pData = calloc(240, sizeof(uint16_t *)); + if (*pData == NULL) { + printf("Error allocating memory for lines"); + ret = ESP_ERR_NO_MEM; + } + for (int i = 0; i < 240; i++) { + (*pData)[i] = malloc(320 * sizeof(uint16_t)); + if ((*pData)[i] == NULL) { + printf("Error allocating memory for line %d", i); + ret = ESP_ERR_NO_MEM; + } + } + return ret; +} + +/** + * @fn esp_err_t iLCD_clearFramebuffer(uint16_t u16Color) + * @brief clear complete framebuffer with a color + * @param RGB586 color code + * @return esp error code + * @author Hendrik Schutter + * @date 3.11.2020 + */ +esp_err_t iLCD_clearFramebuffer(uint16_t u16Color) { + for (int y = 0; y < 240; y++) { + for (int x = 0; x < 320; x++) { + //The LCD wants the 16-bit value in big-endian, so swap bytes + u16Color = (u16Color >> 8) | (u16Color << 8); + pu16Framebuffer[y][x] = u16Color; + } + } + return iDriver_writeFramebuffer(&pu16Framebuffer); +} + +/** + * @fn esp_err_t iLCD_writeString(uint16_t u16xPos, uint16_t u16yPos, char *pcText, uint16_t u16ColorFont, uint16_t u16ColorBackground) + * @brief write string/text into framebuffer + * @param uint16_t u16xPos + * @param uint16_t u16yPos + * @param char *pcText + * @param RGB586 color code for font uint16_t u16ColorFont + * @param RGB586 color code for background uint16_t u16ColorBackground + * @return esp error code + * @author Hendrik Schutter + * @date 4.11.2020 + */ +esp_err_t iLCD_writeString(uint16_t u16xPos, uint16_t u16yPos, char *pcText, uint16_t u16ColorFont, uint16_t u16ColorBackground) +{ + while(*pcText != '\0') + { + if(u16xPos > LCD_WIDTH-6) + { + u16xPos = 0; + u16yPos++; + } + if(u16yPos > LCD_HIGH-8) + { + u16xPos = u16yPos = 0; + } + + vLCD_writeChar(u16xPos,u16yPos,*pcText,u16ColorFont,u16ColorBackground); + u16xPos+=6; + pcText++; + } + return iDriver_writeFramebuffer(&pu16Framebuffer); +} + +/** + * @fn void vLCD_writeChar(uint16_t u16xPos, uint16_t u16yPos, char cCharacter,uint16_t u16ColorFont,uint16_t u16ColorBackground) + * @brief write string/text into framebuffer + * @param uint16_t u16xPos + * @param uint16_t u16yPos + * @param char cCharacter + * @param RGB586 color code for font uint16_t u16ColorFont + * @param RGB586 color code for background uint16_t u16ColorBackground + * @return esp error code + * @author Hendrik Schutter + * @date 4.11.2020 + */ +void vLCD_writeChar(uint16_t u16xPos, uint16_t u16yPos, char cCharacter, uint16_t u16ColorFont,uint16_t u16ColorBackground) +{ + uint8_t u8FontIndex = (cCharacter-32); + + for (uint16_t y = u16yPos; y <= (u16yPos+8); y++) { + for (uint16_t x = u16xPos; x <= (u16xPos+6); x++) { + + if(font6_8[u8FontIndex] [(x-u16xPos)] & (1<<(y-u16yPos))) + { + pu16Framebuffer[y][x] = (u16ColorFont >> 8) | (u16ColorFont << 8); + } else + { + pu16Framebuffer[y][x] = (u16ColorBackground >> 8) | (u16ColorBackground << 8); + } + } + } +} \ No newline at end of file diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 00135f4..f8c5c18 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,5 +1,5 @@ # Embed the server root certificate into the final binary idf_build_get_property(project_dir PROJECT_DIR) -idf_component_register(SRCS "esp32_ota_https.c" +idf_component_register(SRCS "simple_ota_example.c" INCLUDE_DIRS "." EMBED_TXTFILES ${project_dir}/server_certs/ca_cert.pem) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 232b91e..b18d8cf 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -7,10 +7,38 @@ menu "Example Configuration" URL of server which hosts the firmware image. + config EXAMPLE_FIRMWARE_UPGRADE_URL_FROM_STDIN + bool + default y if EXAMPLE_FIRMWARE_UPGRADE_URL = "FROM_STDIN" config EXAMPLE_SKIP_COMMON_NAME_CHECK bool "Skip server certificate CN fieldcheck" default n help This allows you to skip the validation of OTA server certificate CN field. + + + choice LCD_TYPE + prompt "LCD module type" + default LCD_TYPE_AUTO + help + The type of LCD on the evaluation board. + + config LCD_TYPE_AUTO + bool "Auto detect" + config LCD_TYPE_ST7789V + bool "ST7789V (WROVER Kit v2 or v3)" + config LCD_TYPE_ILI9341 + bool "ILI9341 (WROVER Kit v1 or DevKitJ v1)" + endchoice + + config LCD_OVERCLOCK + bool + prompt "Run LCD at higher clock speed than allowed" + default "n" + help + The ILI9341 and ST7789 specify that the maximum clock speed for the SPI interface is 10MHz. However, + in practice the driver chips work fine with a higher clock rate, and using that gives a better framerate. + Select this to try using the out-of-spec clock rate. + endmenu diff --git a/main/simple_ota_example.c b/main/simple_ota_example.c index fb64c7f..ba29822 100644 --- a/main/simple_ota_example.c +++ b/main/simple_ota_example.c @@ -25,6 +25,8 @@ #include "esp_wifi.h" #endif +#include "LCD.h" + static const char *TAG = "simple_ota_example"; extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start"); extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end"); @@ -69,6 +71,20 @@ void simple_ota_example_task(void *pvParameter) .event_handler = _http_event_handler, }; +#ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL_FROM_STDIN + char url_buf[OTA_URL_SIZE]; + if (strcmp(config.url, "FROM_STDIN") == 0) { + example_configure_stdin_stdout(); + fgets(url_buf, OTA_URL_SIZE, stdin); + int len = strlen(url_buf); + url_buf[len - 1] = '\0'; + config.url = url_buf; + } else { + ESP_LOGE(TAG, "Configuration mismatch: wrong firmware upgrade image url"); + abort(); + } +#endif + #ifdef CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK config.skip_cert_common_name_check = true; #endif @@ -86,6 +102,7 @@ void simple_ota_example_task(void *pvParameter) void app_main(void) { + // Initialize NVS. esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -113,6 +130,21 @@ void app_main(void) */ esp_wifi_set_ps(WIFI_PS_NONE); #endif // CONFIG_EXAMPLE_CONNECT_WIFI + + + /* + iLCD_init(); //init lcd screen + + iLCD_clearFramebuffer(COLOR_BLACK); //set complete screen to desired color + + vTaskDelay(1000 / portTICK_RATE_MS); //wait one sec + + iLCD_writeString(42,42,"Hello World!",COLOR_WHITE,COLOR_BLACK); //Write text to screen + + iLCD_writeString(42,50,"ESP32",COLOR_WHITE,COLOR_GREEN); //Write text to screen +*/ + + xTaskCreate(&simple_ota_example_task, "ota_example_task", 8192, NULL, 5, NULL); } diff --git a/sdkconfig b/sdkconfig index 39deee1..a4d320a 100644 --- a/sdkconfig +++ b/sdkconfig @@ -122,8 +122,12 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 # # Example Configuration # -CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL="https://192.168.0.3:8070/hello-world.bin" +CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL="https://ota.hendrikschutter.com/hello-world.bin" CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK=y +CONFIG_LCD_TYPE_AUTO=y +# CONFIG_LCD_TYPE_ST7789V is not set +# CONFIG_LCD_TYPE_ILI9341 is not set +# CONFIG_LCD_OVERCLOCK is not set # end of Example Configuration # @@ -138,6 +142,20 @@ CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_PARTITION_TABLE_MD5=y # end of Partition Table +# +# Example Connection Configuration +# +CONFIG_EXAMPLE_CONNECT_WIFI=y +CONFIG_EXAMPLE_WIFI_SSID="nicht verbunden" +CONFIG_EXAMPLE_WIFI_PASSWORD="flamingo" +# CONFIG_EXAMPLE_CONNECT_ETHERNET is not set +CONFIG_EXAMPLE_CONNECT_IPV6=y +CONFIG_EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK=y +# CONFIG_EXAMPLE_CONNECT_IPV6_PREF_GLOBAL is not set +# CONFIG_EXAMPLE_CONNECT_IPV6_PREF_SITE_LOCAL is not set +# CONFIG_EXAMPLE_CONNECT_IPV6_PREF_UNIQUE_LOCAL is not set +# end of Example Connection Configuration + # # Compiler options # diff --git a/sdkconfig.ci b/sdkconfig.ci new file mode 100644 index 0000000..8367dac --- /dev/null +++ b/sdkconfig.ci @@ -0,0 +1,2 @@ +CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL="FROM_STDIN" +CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK=y diff --git a/sdkconfig.defaults b/sdkconfig.defaults new file mode 100644 index 0000000..2289a82 --- /dev/null +++ b/sdkconfig.defaults @@ -0,0 +1,4 @@ +# Default sdkconfig parameters to use the OTA +# partition table layout, with a 4MB flash size +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_TWO_OTA=y