Advertisement
Guest User

app_main.c

a guest
Mar 20th, 2018
761
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 44.92 KB | None | 0 0
  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. //     http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14.  
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <byteswap.h>
  19.  
  20. #include "freertos/FreeRTOS.h"
  21. #include "freertos/task.h"
  22. #include "freertos/event_groups.h"
  23. #include "esp_heap_alloc_caps.h"
  24. #include "esp_system.h"
  25. #include "esp_wifi.h"
  26. #include "esp_event_loop.h"
  27. #include "esp_log.h"
  28. #include "nvs_flash.h"
  29. #include "driver/gpio.h"
  30. #include "driver/spi_master.h"
  31. #include "soc/spi_reg.h"
  32. #include "driver/hspi.h"
  33. #include "soc/gpio_reg.h"
  34. #include "esp_attr.h"
  35.  
  36. #include "soc/gpio_struct.h"
  37. #include "freertos/semphr.h"
  38. #include "esp_err.h"
  39. #include "camera.h"
  40.  
  41. #include "lwip/sys.h"
  42. #include "lwip/netdb.h"
  43. #include "lwip/api.h"
  44. #include "bitmap.h"
  45.  
  46. #include "telnet.h"
  47.  
  48. static const char* TAG = "ESPILICAM";
  49.  
  50. /*
  51.  This code displays some fancy graphics on the ILI9341-based 320x240 LCD on an ESP-WROVER_KIT board.
  52.  It is not very fast, even when the SPI transfer itself happens at 8MHz and with DMA, because
  53.  the rest of the code is not very optimized. Especially calculating the image line-by-line
  54.  is inefficient; it would be quicker to send an entire screenful at once. This example does, however,
  55.  demonstrate the use of both spi_device_transmit as well as spi_device_queue_trans/spi_device_get_trans_result
  56.  as well as pre-transmit callbacks.
  57.  
  58.  Some info about the ILI9341: It has an C/D line, which is connected to a GPIO here. It expects this
  59.  line to be low for a command and high for data. We use a pre-transmit callback here to control that
  60.  line: every transaction has as the user-definable argument the needed state of the D/C line and just
  61.  before the transaction is sent, the callback will set this line to the correct state.
  62. */
  63.  
  64. #define PIN_NUM_MISO CONFIG_HW_LCD_MISO_GPIO
  65. #define PIN_NUM_MOSI CONFIG_HW_LCD_MOSI_GPIO
  66. #define PIN_NUM_CLK  CONFIG_HW_LCD_CLK_GPIO
  67. #define PIN_NUM_CS   CONFIG_HW_LCD_CS_GPIO
  68. #define PIN_NUM_DC   CONFIG_HW_LCD_DC_GPIO
  69. #define PIN_NUM_RST  CONFIG_HW_LCD_RESET_GPIO
  70. #define PIN_NUM_BCKL CONFIG_HW_LCD_BL_GPIO
  71.  
  72. SemaphoreHandle_t dispSem = NULL;
  73. SemaphoreHandle_t dispDoneSem = NULL;
  74.  
  75. // TODO: replace display pause logic with task notify...
  76. static int tft_offset = 0;
  77. // don't start rendering framebuffer until we have a picture to display..
  78. static bool PAUSE_DISPLAY=true;
  79.  
  80. /*
  81.  The ILI9341 needs a bunch of command/argument values to be initialized. They are stored in this struct.
  82. */
  83. typedef struct {
  84.     uint8_t cmd;
  85.     uint8_t data[16];
  86.     uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
  87. } ili_init_cmd_t;
  88.  
  89. static spi_device_handle_t spi;
  90.  
  91. //Place data into DRAM. Constant data gets placed into DROM by default, which is not accessible by DMA.
  92. /*
  93. DRAM_ATTR static const ili_init_cmd_t ili_init_cmds[]={
  94.     {0xCF, {0x00, 0x83, 0X30}, 3},
  95.     {0xED, {0x64, 0x03, 0X12, 0X81}, 4},
  96.     {0xE8, {0x85, 0x01, 0x79}, 3},
  97.     {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5},
  98.     {0xF7, {0x20}, 1},
  99.     {0xEA, {0x00, 0x00}, 2},
  100.     {0xC0, {0x26}, 1},
  101.     {0xC1, {0x11}, 1},
  102.     {0xC5, {0x35, 0x3E}, 2},
  103.     {0xC7, {0xBE}, 1},
  104.     {0x36, {0x28}, 1},
  105.     {0x3A, {0x55}, 1},
  106.     {0xB1, {0x00, 0x1B}, 2},
  107.     {0xF2, {0x08}, 1},
  108.     {0x26, {0x01}, 1},
  109.     {0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15},
  110.     {0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15},
  111.     {0x2A, {0x00, 0x00, 0x00, 0xEF}, 4},
  112.     {0x2B, {0x00, 0x00, 0x01, 0x3f}, 4},
  113.     {0x2C, {0}, 0},
  114.     {0xB7, {0x07}, 1},
  115.     {0xB6, {0x0A, 0x82, 0x27, 0x00}, 4},
  116.     {0x11, {0}, 0x80},
  117.     {0x29, {0}, 0x80},
  118.     {0, {0}, 0xff},
  119. };*/
  120. DRAM_ATTR static const ili_init_cmd_t ili_init_cmds[]={
  121.     {0x36, {0}, 1},
  122.     {0x3A, {0x55}, 1},
  123.     {0xB2, {0x0C,0x0C,0x00,0x33,0x33}, 5},
  124.     {0xB7, {0x35}, 1},
  125.     {0xBB, {0x2B}, 1},
  126.     {0xC0, {0x2C}, 1},
  127.     {0xC2, {0x01,0xFF}, 2},
  128.     {0xC3, {0x11}, 1},
  129.     {0xC4, {0x20}, 1},
  130.     {0xC6, {0x0F}, 1},
  131.     {0xD0, {0xA4,0xA1}, 2},
  132.     {0xE0, {0xD0,0x00,0x05,0x0E,0x15,0x0D,0x37,0x43,0x47,0x09,0x15,0x12,0x16,0x19}, 14},
  133.     {0xE1, {0xD0,0x00,0x05,0x0D,0x0C,0x06,0x2D,0x44,0x40,0x0E,0x1C,0x18,0x16,0x19}, 14},
  134.     {0x11, {0}, 0x80}, // optional
  135.     {0x29, {0}, 0x80}, // optional
  136.     {0, {0}, 0xff},
  137. };
  138.  
  139. //Send a command to the ILI9341. Uses spi_device_transmit, which waits until the transfer is complete.
  140. void ili_cmd(spi_device_handle_t spi, const uint8_t cmd)
  141. {
  142.     esp_err_t ret;
  143.     spi_transaction_t t;
  144.     memset(&t, 0, sizeof(t));       //Zero out the transaction
  145.     t.length=8;                     //Command is 8 bits
  146.     t.tx_buffer=&cmd;               //The data is the cmd itself
  147.     t.user=(void*)0;                //D/C needs to be set to 0
  148.     ret=spi_device_transmit(spi, &t);  //Transmit!
  149.     assert(ret==ESP_OK);            //Should have had no issues.
  150. }
  151.  
  152. //Send data to the ILI9341. Uses spi_device_transmit, which waits until the transfer is complete.
  153. void ili_data(spi_device_handle_t spi, const uint8_t *data, int len)
  154. {
  155.     esp_err_t ret;
  156.     spi_transaction_t t;
  157.     if (len==0) return;             //no need to send anything
  158.     memset(&t, 0, sizeof(t));       //Zero out the transaction
  159.     t.length=len*8;                 //Len is in bytes, transaction length is in bits.
  160.     t.tx_buffer=data;               //Data
  161.     t.user=(void*)1;                //D/C needs to be set to 1
  162.     ret=spi_device_transmit(spi, &t);  //Transmit!
  163.     assert(ret==ESP_OK);            //Should have had no issues.
  164. }
  165.  
  166. //This function is called (in irq context!) just before a transmission starts. It will
  167. //set the D/C line to the value indicated in the user field.
  168. void ili_spi_pre_transfer_callback(spi_transaction_t *t)
  169. {
  170.     int dc=(int)t->user;
  171.     gpio_set_level(PIN_NUM_DC, dc);
  172. }
  173.  
  174. //Initialize the display
  175. void ili_init(spi_device_handle_t spi)
  176. {
  177.     int cmd=0;
  178.     //Initialize non-SPI GPIOs
  179.     gpio_set_direction(PIN_NUM_DC, GPIO_MODE_OUTPUT);
  180.     gpio_set_direction(PIN_NUM_RST, GPIO_MODE_OUTPUT);
  181.     gpio_set_direction(PIN_NUM_BCKL, GPIO_MODE_OUTPUT);
  182.     //Reset the display
  183.     gpio_set_level(PIN_NUM_RST, 0);
  184.     vTaskDelay(100 / portTICK_RATE_MS);
  185.     gpio_set_level(PIN_NUM_RST, 1);
  186.     vTaskDelay(100 / portTICK_RATE_MS);
  187.     //Send all the commands
  188.     while (ili_init_cmds[cmd].databytes!=0xff) {
  189.         ili_cmd(spi, ili_init_cmds[cmd].cmd);
  190.         ili_data(spi, ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes&0x1F);
  191.         if (ili_init_cmds[cmd].databytes&0x80) {
  192.             vTaskDelay(100 / portTICK_RATE_MS);
  193.         }
  194.         cmd++;
  195.     }
  196.     ///Enable backlight
  197.     gpio_set_level(PIN_NUM_BCKL, 1);
  198. }
  199.  
  200.  
  201. //To send a line we have to send a command, 2 data bytes, another command, 2 more data bytes and another command
  202. //before sending the line data itself; a total of 6 transactions. (We can't put all of this in just one transaction
  203. //because the D/C line needs to be toggled in the middle.)
  204. //This routine queues these commands up so they get sent as quickly as possible.
  205. static void send_line(spi_device_handle_t spi, int ypos, uint16_t *line)
  206. {
  207.     esp_err_t ret;
  208.     int x;
  209.     //Transaction descriptors. Declared static so they're not allocated on the stack; we need this memory even when this
  210.     //function is finished because the SPI driver needs access to it even while we're already calculating the next line.
  211.     static spi_transaction_t trans[6];
  212.  
  213.     //In theory, it's better to initialize trans and data only once and hang on to the initialized
  214.     //variables. We allocate them on the stack, so we need to re-init them each call.
  215.     for (x=0; x<6; x++) {
  216.         memset(&trans[x], 0, sizeof(spi_transaction_t));
  217.         if ((x&1)==0) {
  218.             //Even transfers are commands
  219.             trans[x].length=8;
  220.             trans[x].user=(void*)0;
  221.         } else {
  222.             //Odd transfers are data
  223.             trans[x].length=8*4;
  224.             trans[x].user=(void*)1;
  225.         }
  226.         trans[x].flags=SPI_TRANS_USE_TXDATA;
  227.     }
  228.     trans[0].tx_data[0]=0x2A;           //Column Address Set
  229.     trans[1].tx_data[0]=0;              //Start Col High
  230.     trans[1].tx_data[1]=0;              //Start Col Low
  231.     trans[1].tx_data[2]=(320)>>8;       //End Col High
  232.     trans[1].tx_data[3]=(320)&0xff;     //End Col Low
  233.     trans[2].tx_data[0]=0x2B;           //Page address set
  234.     trans[3].tx_data[0]=ypos>>8;        //Start page high
  235.     trans[3].tx_data[1]=ypos&0xff;      //start page low
  236.     trans[3].tx_data[2]=(ypos+1)>>8;    //end page high
  237.     trans[3].tx_data[3]=(ypos+1)&0xff;  //end page low
  238.     trans[4].tx_data[0]=0x2C;           //memory write
  239.     trans[5].tx_buffer=line;            //finally send the line data
  240.     trans[5].length=320*2*8;            //Data length, in bits
  241.     trans[5].flags=0; //undo SPI_TRANS_USE_TXDATA flag
  242.  
  243.     //Queue all transactions.
  244.     for (x=0; x<6; x++) {
  245.         ret=spi_device_queue_trans(spi, &trans[x], portMAX_DELAY);
  246.         assert(ret==ESP_OK);
  247.     }
  248.  
  249.     //When we are here, the SPI driver is busy (in the background) getting the transactions sent. That happens
  250.     //mostly using DMA, so the CPU doesn't have much to do here. We're not going to wait for the transaction to
  251.     //finish because we may as well spend the time calculating the next line. When that is done, we can call
  252.     //send_line_finish, which will wait for the transfers to be done and check their status.
  253. }
  254.  
  255.  
  256. static void send_line_finish(spi_device_handle_t spi)
  257. {
  258.     spi_transaction_t *rtrans;
  259.     esp_err_t ret;
  260.     //Wait for all 6 transactions to be done and get back the results.
  261.     for (int x=0; x<6; x++) {
  262.         ret=spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY);
  263.         assert(ret==ESP_OK);
  264.         //We could inspect rtrans now if we received any info back. The LCD is treated as write-only, though.
  265.     }
  266. }
  267.  
  268. // camera code
  269.  
  270. const static char http_hdr[] = "HTTP/1.1 200 OK\r\n";
  271. const static char http_stream_hdr[] =
  272.         "Content-type: multipart/x-mixed-replace; boundary=123456789000000000000987654321\r\n\r\n";
  273. const static char http_jpg_hdr[] =
  274.         "Content-type: image/jpg\r\n\r\n";
  275. const static char http_pgm_hdr[] =
  276.         "Content-type: image/x-portable-graymap\r\n\r\n";
  277. const static char http_stream_boundary[] = "--123456789000000000000987654321\r\n";
  278. const static char http_bitmap_hdr[] =
  279.         "Content-type: image/bitmap\r\n\r\n";
  280. const static char http_yuv422_hdr[] =
  281.         "Content-Disposition: attachment; Content-type: application/octet-stream\r\n\r\n";
  282.  
  283. static EventGroupHandle_t wifi_event_group;
  284. const int CONNECTED_BIT = BIT0;
  285. static ip4_addr_t s_ip_addr;
  286.  
  287. static camera_pixelformat_t s_pixel_format;
  288. static camera_config_t config = {
  289.     .ledc_channel = LEDC_CHANNEL_0,
  290.     .ledc_timer = LEDC_TIMER_0,
  291.     .pin_d0 = CONFIG_D0,
  292.     .pin_d1 = CONFIG_D1,
  293.     .pin_d2 = CONFIG_D2,
  294.     .pin_d3 = CONFIG_D3,
  295.     .pin_d4 = CONFIG_D4,
  296.     .pin_d5 = CONFIG_D5,
  297.     .pin_d6 = CONFIG_D6,
  298.     .pin_d7 = CONFIG_D7,
  299.     .pin_xclk = CONFIG_XCLK,
  300.     .pin_pclk = CONFIG_PCLK,
  301.     .pin_vsync = CONFIG_VSYNC,
  302.     .pin_href = CONFIG_HREF,
  303.     .pin_sscb_sda = CONFIG_SDA,
  304.     .pin_sscb_scl = CONFIG_SCL,
  305.     .pin_reset = CONFIG_RESET,
  306.     .xclk_freq_hz = CONFIG_XCLK_FREQ,
  307.     .test_pattern_enabled = false // CONFIG_ENABLE_TEST_PATTERN,
  308.     };
  309.  
  310. static camera_model_t camera_model;
  311.  
  312. //#define CAMERA_PIXEL_FORMAT CAMERA_PF_RGB565
  313. #define CAMERA_PIXEL_FORMAT CAMERA_PF_YUV422
  314. #define CAMERA_FRAME_SIZE CAMERA_FS_QVGA
  315.  
  316. // command parser...
  317. #include "smallargs.h"
  318.  
  319. #define UNUSED(x) ((void)x)
  320. #define RESPONSE_BUFFER_LEN 256
  321. #define CMD_BUFFER_LEN 128
  322.  
  323. static sarg_root root;
  324. static char telnet_cmd_response_buff[RESPONSE_BUFFER_LEN];
  325. static char telnet_cmd_buffer[CMD_BUFFER_LEN];
  326.  
  327. static void handle_camera_config_chg(bool reinit_reqd) {
  328.   if (reinit_reqd) {
  329.               ESP_LOGD(TAG, "Reconfiguring camera...");
  330.               PAUSE_DISPLAY = true;
  331.               esp_err_t err;
  332.               vTaskDelay(100 / portTICK_RATE_MS);
  333.               err = reset_pixformat();
  334.               config.pixel_format = s_pixel_format;
  335.               err = camera_init(&config);
  336.               if (err != ESP_OK) {
  337.                   ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
  338.                   //return;
  339.               }
  340.               return;
  341.               vTaskDelay(100 / portTICK_RATE_MS);
  342.               PAUSE_DISPLAY = false;
  343.     }
  344. }
  345.  
  346. static int help_cb(const sarg_result *res)
  347. {
  348.     UNUSED(res);
  349.     char *buf;
  350.     int ret;
  351.  
  352.     ret = sarg_help_text(&root, &buf);
  353.     if(ret != SARG_ERR_SUCCESS)
  354.         return ret;
  355. //    we can't spare much memory!!
  356. //    int length = 0;
  357. //    length += sprintf(telnet_cmd_response_buff+length, "%s\n", buf);
  358. //    ESP_LOGD(TAG," help_cb: %s",telnet_cmd_response_buff);
  359. //    telnet_esp32_sendData((uint8_t *)telnet_cmd_response_buff, strlen(telnet_cmd_response_buff));
  360.     telnet_esp32_sendData((uint8_t *)buf, strlen(buf));
  361.     free(buf);
  362.     return 0;
  363. }
  364.  
  365. static int sys_stats_cb(const sarg_result *res)
  366. {
  367.     int level = 0;
  368.     size_t free8start, free32start, free8, free32;
  369.     level = res->int_val;
  370.     int length = 0;
  371. //    if (level == 0) {
  372.       free8start = xPortGetMinimumEverFreeHeapSizeCaps(MALLOC_CAP_8BIT);
  373.       free32start = xPortGetMinimumEverFreeHeapSizeCaps(MALLOC_CAP_32BIT);
  374.       free8=xPortGetFreeHeapSizeCaps(MALLOC_CAP_8BIT);
  375.       free32=xPortGetFreeHeapSizeCaps(MALLOC_CAP_32BIT);
  376.       length += sprintf(telnet_cmd_response_buff+length, "Free 8-bit=%db, free 32-bit=%db, min 8-bit=%db, min 32-bit=%db",free8,free32, free8start, free32start);
  377. /*
  378.     } else {  //if (level == 1) {
  379.             //vTaskList(telnet_cmd_response_buff);
  380.       length += sprintf(telnet_cmd_response_buff+length, "vTaskList not implemented..");
  381.  
  382.     }
  383. */
  384.     telnet_esp32_sendData((uint8_t *)telnet_cmd_response_buff, strlen(telnet_cmd_response_buff));
  385.     return SARG_ERR_SUCCESS;
  386. }
  387.  
  388. static int  ov7670_xclck_cb(const sarg_result *res) {
  389.       int speed = 0;
  390.       speed = res->int_val;
  391.       ESP_LOGD(TAG, "Switch XCLCK to %dMHZ",speed);
  392.       speed = speed * 1000000;
  393.       config.xclk_freq_hz = speed;
  394.       reset_xclk(&config);
  395.       handle_camera_config_chg(true);
  396.       return SARG_ERR_SUCCESS;
  397. }
  398.  
  399. static int  ov7670_pixformat_cb(const sarg_result *res) {
  400.   if (strcmp("yuv422", res->str_val) == 0) {
  401.     //
  402.     ESP_LOGD(TAG, "Switch pixel format to YUV422");
  403.     s_pixel_format = CAMERA_PF_YUV422;
  404.     handle_camera_config_chg(true);
  405.   } else if (strcmp("rgb565", res->str_val) == 0) {
  406.     //
  407.     ESP_LOGD(TAG, "Switch pixel format to RGB565");
  408.     s_pixel_format = CAMERA_PF_RGB565;
  409.     handle_camera_config_chg(true);
  410.   }
  411.   return SARG_ERR_SUCCESS;
  412. }
  413.  
  414. static int  ov7670_framerate_cb(const sarg_result *res) {
  415.   int framerate = 0;
  416.   framerate = res->int_val;
  417.   ESP_LOGD(TAG, "Switch framerate to %dFPS",framerate);
  418.   sensor_t* s_sensor = get_cam_sensor();
  419.   if (s_sensor != NULL) {
  420.     // this framerate parameter is a hack, openmv params are different
  421.     if (framerate == 14) s_sensor->set_framerate(s_sensor,0);
  422.     if (framerate == 15) s_sensor->set_framerate(s_sensor,1);
  423.     if (framerate == 25) s_sensor->set_framerate(s_sensor,2);
  424.     if (framerate == 30) s_sensor->set_framerate(s_sensor,3);
  425.   //  handle_camera_config_chg(true);
  426.   }
  427.   return SARG_ERR_SUCCESS;
  428. }
  429.  
  430. static int  ov7670_colorbar_cb(const sarg_result *res) {
  431.  
  432.   bool onoff = false;
  433.   if (res->int_val == 1) onoff = true;
  434.   config.test_pattern_enabled = onoff;
  435.   sensor_t* s_sensor = get_cam_sensor();
  436.   if (s_sensor != NULL) {
  437.       ESP_LOGD(TAG, "Set Colorbar (Test Pattern) %d",config.test_pattern_enabled);
  438.       s_sensor->set_colorbar(s_sensor, config.test_pattern_enabled);
  439.   }
  440.   handle_camera_config_chg(false);
  441.   return SARG_ERR_SUCCESS;
  442. }
  443.  
  444.  
  445. static int  ov7670_saturation_cb(const sarg_result *res) {
  446.   int level = 0;
  447.   level = res->int_val;
  448.   ESP_LOGD(TAG, "Switch saturation (0-256) to %d",level);
  449.   sensor_t* s_sensor = get_cam_sensor();
  450.   if (s_sensor != NULL) {
  451.     s_sensor->set_saturation(s_sensor,level);
  452.   }
  453.   return SARG_ERR_SUCCESS;
  454. }
  455.  
  456. static int  ov7670_hue_cb(const sarg_result *res) {
  457.   int level = 0;
  458.   level = res->int_val;
  459.   ESP_LOGD(TAG, "Switch saturation (-180 to 180) to %d",level);
  460.   sensor_t* s_sensor = get_cam_sensor();
  461.   if (s_sensor != NULL) {
  462.     s_sensor->set_hue(s_sensor,level);
  463.   }
  464.   return SARG_ERR_SUCCESS;
  465. }
  466.  
  467.  
  468. static int  ov7670_brightness_cb(const sarg_result *res) {
  469.   int level = 0;
  470.   level = res->int_val;
  471.   ESP_LOGD(TAG, "Switch brightness (-4 to 4) to %d",level);
  472.   sensor_t* s_sensor = get_cam_sensor();
  473.   if (s_sensor != NULL) {
  474.     s_sensor->set_brightness(s_sensor,level);
  475.   }
  476.   return SARG_ERR_SUCCESS;
  477. }
  478.  
  479. static int  ov7670_contrast_cb(const sarg_result *res) {
  480.   int level = 0;
  481.   level = res->int_val;
  482.   ESP_LOGD(TAG, "Switch contrast (-4 to 4) to %d",level);
  483.   sensor_t* s_sensor = get_cam_sensor();
  484.   if (s_sensor != NULL) {
  485.     s_sensor->set_contrast(s_sensor,level);
  486.   }
  487.   return SARG_ERR_SUCCESS;
  488. }
  489.  
  490. static int  ov7670_hflip_cb(const sarg_result *res) {
  491.   //set_vflip
  492.   bool onoff = false;
  493.   if (res->int_val == 1) onoff = true;
  494.   sensor_t* s_sensor = get_cam_sensor();
  495.   if (s_sensor != NULL) {
  496.       ESP_LOGD(TAG, "Set hflip = %d",onoff);
  497.       s_sensor->set_hmirror(s_sensor, onoff);
  498.   }
  499.   //handle_camera_config_chg(false);
  500.   return SARG_ERR_SUCCESS;
  501. }
  502.  
  503. static int  ov7670_vflip_cb(const sarg_result *res) {
  504.   bool onoff = false;
  505.   if (res->int_val == 1) onoff = true;
  506.   sensor_t* s_sensor = get_cam_sensor();
  507.   if (s_sensor != NULL) {
  508.       ESP_LOGD(TAG, "Set vflip = %d",onoff);
  509.       s_sensor->set_vflip(s_sensor, onoff);
  510.   }
  511.   return SARG_ERR_SUCCESS;
  512. }
  513.  
  514.  
  515. const static sarg_opt my_opts[] = {
  516.     {"h", "help", "show help text", BOOL, help_cb},
  517.     {"v", "stats", "system stats (0=mem,1=tasks)", INT, sys_stats_cb},
  518.     {NULL, "clock", "set camera xclock frequency", INT, ov7670_xclck_cb},
  519.     {NULL, "pixformat", "set pixel format (yuv422, rgb565)", STRING, ov7670_pixformat_cb},
  520.     {NULL, "framerate", "set framerate (14,15,25,30)", INT, ov7670_framerate_cb},
  521.     {NULL, "colorbar", "set test pattern (0=off/1=on)", INT, ov7670_colorbar_cb},
  522.     {NULL, "saturation", "set saturation (1-256)", INT, ov7670_saturation_cb},
  523.     {NULL, "hue", "set hue (-180 to 180)", INT, ov7670_hue_cb},
  524.     {NULL, "brightness", "set brightness (-4 to 4)", INT, ov7670_brightness_cb},
  525.     {NULL, "contrast", "set contrast (-4 to 4)", INT, ov7670_contrast_cb},
  526.     {NULL, "hflip", "flip horizontal (0=off/1=on)", INT, ov7670_hflip_cb},
  527.     {NULL, "vflip", "flip vertical (0=off/1=on)", INT, ov7670_vflip_cb},
  528.     {NULL, NULL, NULL, INT, NULL}
  529. };
  530.  
  531.  
  532. static int handle_command(uint8_t *cmdLine, size_t len)
  533. {
  534.     int ret = sarg_init(&root, my_opts, "ESPILICAM");
  535.     assert(ret == SARG_ERR_SUCCESS);
  536.     // lots of redundant code here! //strcpy or memcpy would suffice
  537.     size_t cmd_len = len;
  538.     if (len > CMD_BUFFER_LEN) cmd_len = CMD_BUFFER_LEN;
  539.     for (int i = 0; i < cmd_len; i++)
  540.       telnet_cmd_buffer[i] = *(cmdLine+i);
  541.     telnet_cmd_buffer[cmd_len-1] = '\0';
  542.     ESP_LOGD(TAG, "Processing telnet_cmd_buffer len=%d - contents=%s",cmd_len,(char*)telnet_cmd_buffer);
  543.     if(telnet_cmd_buffer != NULL) {
  544.         // next command will call sarg_parse and call callbacks as needed...
  545.         ret = sarg_parse_command_buffer(&root, telnet_cmd_buffer, cmd_len);
  546.         if(ret != SARG_ERR_SUCCESS) {
  547.             ESP_LOGE(TAG, "Command parsing failed");
  548.             sarg_destroy(&root);
  549.             return -1;
  550.         }
  551.         // command has been parsed and executed!
  552.         ESP_LOGD(TAG, "Command parser completed...");
  553.     }
  554.     sarg_destroy(&root);
  555.     return 0;
  556. }
  557.  
  558. static esp_err_t event_handler(void *ctx, system_event_t *event)
  559. {
  560.     switch (event->event_id) {
  561.         case SYSTEM_EVENT_STA_START:
  562.             esp_wifi_connect();
  563.             break;
  564.         case SYSTEM_EVENT_STA_GOT_IP:
  565.             xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
  566.             s_ip_addr = event->event_info.got_ip.ip_info.ip;
  567.             break;
  568.         case SYSTEM_EVENT_STA_DISCONNECTED:
  569.             /* This is a workaround as ESP32 WiFi libs don't currently
  570.              auto-reassociate. */
  571.             esp_wifi_connect();
  572.             xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
  573.             break;
  574.         default:
  575.             break;
  576.     }
  577.     return ESP_OK;
  578. }
  579.  
  580. static void initialise_wifi(void)
  581. {
  582.     tcpip_adapter_init();
  583.     wifi_event_group = xEventGroupCreate();
  584.     ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
  585.  
  586.     wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  587.     ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
  588.     ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
  589.     wifi_config_t wifi_config = {
  590.         .sta = {
  591.             .ssid = CONFIG_WIFI_SSID,
  592.             .password = CONFIG_WIFI_PASSWORD,
  593.         },
  594.     };
  595.     ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
  596.     ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
  597.     ESP_ERROR_CHECK( esp_wifi_start() );
  598.     ESP_ERROR_CHECK( esp_wifi_set_ps(WIFI_PS_NONE) );
  599.     ESP_LOGI(TAG, "Connecting to \"%s\"", wifi_config.sta.ssid);
  600.     xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);
  601.     ESP_LOGI(TAG, "Connected");
  602. }
  603.  
  604.  
  605. // COMMAND PARSER
  606. uint8_t getHexVal(char c)
  607. {
  608.    if(c >= '0' && c <= '9')
  609.      return (uint8_t)(c - '0');
  610.    else
  611.      return (uint8_t)(c-'A'+10);
  612. }
  613.  
  614. static void recvData(uint8_t *buffer, size_t size) {
  615.   char cmdRecptMessage[100];
  616.   int length = 0;
  617.   ESP_LOGD(TAG, "We received: %.*s", size, buffer);
  618.   handle_command(buffer, size);
  619.   // have to wait for callback for actual response.. echo recpt for now
  620.   length += sprintf(cmdRecptMessage, "%s","#: ");
  621.   if (strlen(telnet_cmd_response_buff) > 0)
  622.     sprintf(cmdRecptMessage+length, "%s\n", telnet_cmd_response_buff);
  623.   telnet_esp32_sendData((uint8_t *)cmdRecptMessage, strlen(cmdRecptMessage));
  624. }
  625.  
  626. static void telnetTask(void *data) {
  627.   ESP_LOGD(TAG, ">> telnetTask");
  628.   telnet_esp32_listenForClients(recvData);
  629.   ESP_LOGD(TAG, "<< telnetTask");
  630.   vTaskDelete(NULL);
  631. }
  632.  
  633.  
  634. // DISPLAY LOGIC
  635. static inline uint8_t clamp(int n)
  636. {
  637.     n = n>255 ? 255 : n;
  638.     return n<0 ? 0 : n;
  639. }
  640.  
  641. // Pass 8-bit (each) R,G,B, get back 16-bit packed color
  642. static inline uint16_t ILI9341_color565(uint8_t r, uint8_t g, uint8_t b) {
  643.   return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
  644. }
  645.  
  646. uint16_t get_grayscale_pixel_as_565(uint8_t pix) {
  647.     // R = (img[n]&248)<<8; // 5 bit cao cua Y
  648.     // G = (img[n]&252)<<3; // 6 bit cao cua Y
  649.     // B = (img[n]&248)>>3; // 5 bit cao cua Y
  650.     uint16_t graypixel=((pix&248)<<8)|((pix&252)<<3)|((pix&248)>>3);
  651.     return graypixel;
  652.  
  653. }
  654.  
  655. // integers instead of floating point...
  656. static inline uint16_t fast_yuv_to_rgb565(int y, int u, int v) {
  657. int a0 = 1192 * (y - 16);
  658. int a1 = 1634 * (v - 128);
  659. int a2 = 832 * (v - 128);
  660. int a3 = 400 * (u - 128);
  661. int a4 = 2066 * (u - 128);
  662. int r = (a0 + a1) >> 10;
  663. int g = (a0 - a2 - a3) >> 10;
  664. int b = (a0 + a4) >> 10;
  665. return ILI9341_color565(clamp(r),clamp(g),clamp(b));
  666.  
  667. }
  668.  
  669. // fast but uses floating points...
  670. static inline uint16_t fast_pascal_to_565(int Y, int U, int V) {
  671.   uint8_t r, g, b;
  672.   r = clamp(1.164*(Y-16) + 1.596*(V-128));
  673.   g = clamp(1.164*(Y-16) - 0.392*(U-128) - 0.813*(V-128));
  674.   b = clamp(1.164*(Y-16) + 2.017*(U-128));
  675.   return ILI9341_color565(r,g,b);
  676. }
  677.  
  678. //Warning: This gets squeezed into IRAM.
  679. volatile static uint32_t *currFbPtr __attribute__ ((aligned(4))) = NULL;
  680.  
  681. inline uint8_t unpack(int byteNumber, uint32_t value) {
  682.     return (value >> (byteNumber * 8));
  683. }
  684.  
  685. // 8-bit logic - ref.
  686. /*
  687. static void convert_yuv_line_to_565(uint8_t *srcline, uint8_t *destline, int byte_len) {
  688.  
  689.   uint16_t pixel565 = 0;
  690.   uint16_t pixel565_2 = 0;
  691.   for (int current_byte_pos = 0; current_byte_pos < byte_len; current_byte_pos += 4)
  692.   {
  693.        uint8_t y1, y2, u, v;
  694.        y1 = srcline[current_byte_pos+3];
  695.        v = srcline[current_byte_pos+2];
  696.        y2 = srcline[current_byte_pos+1];
  697.        u = srcline[current_byte_pos];
  698.        pixel565 = fast_yuv_to_rgb565(y1,u,v);
  699.        //pixel565 = __bswap_16(pixel565);
  700.        pixel565_2 = fast_yuv_to_rgb565(y2,u,v);
  701.        //pixel565 = __bswap_16(pixel565_2);
  702.        destline[0] = pixel565 & 0xFF00 >> 8;
  703.        destline[1] = pixel565 & 0xFF;
  704.        //memcpy(destline, &pixel565, sizeof(uint16_t));
  705.        destline +=2;
  706.        destline[0] = pixel565_2 & 0xFF00 >> 8;
  707.        destline[1] = pixel565_2 & 0xFF;
  708.        //memcpy(destline, &pixel565_2, sizeof(uint16_t));
  709.        destline +=2;
  710.   }
  711. }
  712. */
  713.  
  714. static void convert_fb32bit_line_to_bmp565(uint32_t *srcline, uint8_t *destline, const camera_pixelformat_t format) {
  715.  
  716.   uint16_t pixel565 = 0;
  717.   uint16_t pixel565_2 = 0;
  718.   uint32_t long2px = 0;
  719.   uint16_t *sptr;
  720.   int current_src_pos=0, current_dest_pos=0;
  721.   for (int current_pixel_pos = 0; current_pixel_pos < 320; current_pixel_pos += 2)
  722.   {
  723.     current_src_pos = current_pixel_pos/2;
  724.     long2px = srcline[current_src_pos];
  725.     if (format == CAMERA_PF_YUV422) {
  726.         uint8_t y1, y2, u, v;
  727.         y1 = unpack(0,long2px);
  728.         v = unpack(1,long2px);;
  729.         y2 = unpack(2,long2px);
  730.         u = unpack(3,long2px);
  731.  
  732.         pixel565 = fast_yuv_to_rgb565(y1,u,v);
  733.         pixel565_2 = fast_yuv_to_rgb565(y2,u,v);
  734.  
  735.         sptr = &destline[current_dest_pos];
  736.         *sptr = pixel565;
  737.         sptr = &destline[current_dest_pos+2];
  738.         *sptr = pixel565_2;
  739.         current_dest_pos += 4;
  740.  
  741.     } else if (format == CAMERA_PF_RGB565) {
  742.       pixel565 =  (unpack(2,long2px) << 8) | unpack(3,long2px);
  743.       pixel565_2 = (unpack(0,long2px) << 8) | unpack(1,long2px);
  744.  
  745.       sptr = &destline[current_dest_pos];
  746.       *sptr = pixel565;
  747.       sptr = &destline[current_dest_pos+2];
  748.       *sptr = pixel565_2;
  749.       current_dest_pos += 4;
  750.     }
  751.   }
  752. }
  753.  
  754. void spi_lcd_wait_finish() {
  755.   xSemaphoreTake(dispDoneSem, portMAX_DELAY);
  756. }
  757.  
  758. void spi_lcd_send() {
  759.   xSemaphoreGive(dispSem);
  760. }
  761.  
  762. #define ILI_WIDTH 320
  763. #define ILI_HEIGHT 240
  764.  
  765. static void push_framebuffer_to_tft(void *pvParameters) {
  766.   uint16_t line[2][320];
  767.   int x, y; //, frame=0;
  768.   //Indexes of the line currently being sent to the LCD and the line we're calculating.
  769.   int sending_line=-1;
  770.   int calc_line=0;
  771.  
  772. // 32-bit aligned access (uint32 instead of byte array)
  773.   uint32_t* fbl = camera_get_fb();
  774.   uint32_t long2px = 0;
  775.  
  776.   int width = camera_get_fb_width();
  777.   int height =  camera_get_fb_height();
  778.   int max_fb_pos = width * height;
  779.   uint16_t pixel565 = 0;
  780.   uint16_t pixel565_2 = 0;
  781.   int current_byte_pos = 0, current_fb_pixel_pos = 0;
  782.  
  783.   xSemaphoreGive(dispDoneSem);
  784.  
  785.   while(1) {
  786.   //   frame++;
  787.      xSemaphoreTake(dispSem, portMAX_DELAY);
  788.  //   printf("Display task: frame.\n");
  789.      bool reset_loop = false;
  790.      for (y=0; y<ILI_HEIGHT; y++) {
  791.         //Calculate a line, operate on 2 pixels at a time...
  792.         for (x=0; x<ILI_WIDTH; x+=2) {
  793.             // TODO: display pause logic cleanup
  794.             if (PAUSE_DISPLAY) {
  795.               while (PAUSE_DISPLAY) { vTaskDelay(30 / portTICK_RATE_MS); }
  796.               // reset FB, cam may have re-init'ed
  797.               fbl = camera_get_fb();
  798.               reset_loop = true;
  799.             }
  800.             if (reset_loop) break;
  801.             // wrap pixels around...
  802.             current_fb_pixel_pos = ((y*width)+x+tft_offset) % max_fb_pos;
  803.             // note, the next line is done to enable shifting the offset
  804.             // TODO: remove test mod %
  805.             current_byte_pos = current_fb_pixel_pos/2+(tft_offset % 4);
  806.             if (fbl != NULL) {
  807.               if (s_pixel_format == CAMERA_PF_YUV422) {
  808.                 uint8_t y1, y2, u, v;
  809.                 long2px = fbl[current_byte_pos];
  810.                 y1 = unpack(0,long2px);
  811.                 v = unpack(1,long2px);;
  812.                 y2 = unpack(2,long2px);
  813.                 u = unpack(3,long2px);
  814.                 // UYVY (Reverse order)
  815.                 /*
  816.                 y1 = fb[current_byte_pos+3];
  817.                 v = fb[current_byte_pos+2];
  818.                 y2 = fb[current_byte_pos+1];
  819.                 u = fb[current_byte_pos];
  820.                 */
  821.                 pixel565 = fast_yuv_to_rgb565(y1,u,v);
  822.                 pixel565_2 = fast_yuv_to_rgb565(y2,u,v);
  823.                 // swapped bytes for ILI compared to BMP format
  824.                 line[calc_line][x]= __bswap_16(pixel565);
  825.                 line[calc_line][x+1]= __bswap_16(pixel565_2);
  826.               } else {
  827.                 // rgb565 direct from OV7670 to ILI9341
  828.                 // best to swap bytes here instead of bswap
  829.                 long2px = fbl[current_byte_pos];
  830.                 pixel565 =  (unpack(3,long2px) << 8) | unpack(2,long2px);
  831.                 pixel565_2 = (unpack(1,long2px) << 8) | unpack(0,long2px);
  832.                 /*
  833.                 pixel565 =  (fb[current_byte_pos] << 8) |  fb[current_byte_pos+1]; //(fb[currBytePos] & 0xFF00 >> 8) | (p565 = fb[currBytePos+1] & 0x00FF);
  834.                 pixel565_2 = (fb[current_byte_pos+2] << 8) |  fb[current_byte_pos+3];
  835.                 */
  836.                 line[calc_line][x]= pixel565;
  837.                 line[calc_line][x+1]= pixel565_2;
  838.               }
  839.             }
  840.         }
  841.         //Finish up the sending process of the previous line, if any
  842.         if (sending_line!=-1) send_line_finish(spi);
  843.         //Swap sending_line and calc_line
  844.         sending_line=calc_line;
  845.         calc_line=(calc_line==1)?0:1;
  846.         //Send the line we currently calculated.
  847.         send_line(spi, y, line[sending_line]);
  848.         //The line is queued up for sending now; the actual sending happens in the
  849.         //background. We can go on to calculate the next line as long as we do not
  850.         //touch line[sending_line]; the SPI sending process is still reading from that.
  851.       } // end for (y=0; y<ili_height; y++)
  852.       // TODO: check that line is actually sent before giving semaphore!
  853.       vTaskDelay(30 / portTICK_RATE_MS);
  854.       xSemaphoreGive(dispDoneSem);
  855.   } // end while(1)
  856. }
  857.  
  858. static void http_server_netconn_serve(struct netconn *conn)
  859. {
  860.     struct netbuf *inbuf;
  861.     char *buf;
  862.     u16_t buflen;
  863.     err_t err;
  864.     /* Read the data from the port, blocking if nothing yet there.
  865.      We assume the request (the part we care about) is in one netbuf */
  866.     err = netconn_recv(conn, &inbuf);
  867.     if (err == ERR_OK) {
  868.         netbuf_data(inbuf, (void**) &buf, &buflen);
  869.  
  870.         /* Is this an HTTP GET command? (only check the first 5 chars, since
  871.          there are other formats for GET, and we're keeping it very simple )*/
  872.         if (buflen >= 5 && buf[0] == 'G' && buf[1] == 'E' && buf[2] == 'T'
  873.                 && buf[3] == ' ' && buf[4] == '/') {
  874.           /* Send the HTTP header
  875.              * subtract 1 from the size, since we dont send the \0 in the string
  876.              * NETCONN_NOCOPY: our data is const static, so no need to copy it
  877.              */
  878.           netconn_write(conn, http_hdr, sizeof(http_hdr) - 1,
  879.                     NETCONN_NOCOPY);
  880.  
  881.            //check if a stream is requested.
  882.            if (buf[5] == 's') {
  883.                 //Send mjpeg stream header
  884.                 err = netconn_write(conn, http_stream_hdr, sizeof(http_stream_hdr) - 1,
  885.                     NETCONN_NOCOPY);
  886.                 ESP_LOGD(TAG, "Stream started.");
  887.  
  888.                 //Run while everyhting is ok and connection open.
  889.                 while(err == ERR_OK) {
  890.                     ESP_LOGD(TAG, "Capture frame");
  891.  
  892.                     PAUSE_DISPLAY = true;
  893.                     err = camera_run();
  894.                     PAUSE_DISPLAY = false;
  895.  
  896.                     spi_lcd_send();
  897.                     spi_lcd_wait_finish();
  898.  
  899.                     if (err != ESP_OK) {
  900.                         ESP_LOGD(TAG, "Camera capture failed with error = %d", err);
  901.                     } else {
  902.                         ESP_LOGD(TAG, "Done");
  903.                         //stream an image..
  904.                         if((s_pixel_format == CAMERA_PF_RGB565) || (s_pixel_format == CAMERA_PF_YUV422)) {
  905.                             // write mime boundary start
  906.                             err = netconn_write(conn, http_bitmap_hdr, sizeof(http_bitmap_hdr) - 1,
  907.                                 NETCONN_NOCOPY);
  908.                             // write bitmap header
  909.                             char *bmp = bmp_create_header565(camera_get_fb_width(), camera_get_fb_height());
  910.                             err = netconn_write(conn, bmp, sizeof(bitmap565), NETCONN_NOCOPY);
  911.                             free(bmp);
  912.                             // convert framebuffer on the fly...
  913.                             // only rgb and yuv...
  914.                             uint8_t s_line[320*2];
  915.                             uint32_t *fbl;
  916.                             for (int i = 0; i < 240; i++) {
  917.                               fbl = &currFbPtr[(i*320)/2];  //(i*(320*2)/4); // 4 bytes for each 2 pixel / 2 byte read..
  918.                               convert_fb32bit_line_to_bmp565(fbl, s_line,s_pixel_format);
  919.                               err = netconn_write(conn, s_line, 320*2,
  920.                                             NETCONN_COPY);
  921.                             }
  922.                         }
  923.                         else { // stream jpeg
  924.                             err = netconn_write(conn, http_jpg_hdr, sizeof(http_jpg_hdr) - 1,
  925.                                 NETCONN_NOCOPY);
  926.                             if(err == ERR_OK)
  927.                               err = netconn_write(conn, camera_get_fb(), camera_get_data_size(),
  928.                                               NETCONN_COPY);
  929.                         }
  930.                         if(err == ERR_OK)
  931.                         {
  932.                             //Send boundary to next jpeg
  933.                             err = netconn_write(conn, http_stream_boundary,
  934.                                     sizeof(http_stream_boundary) -1, NETCONN_NOCOPY);
  935.                         }
  936.                         vTaskDelay(200 / portTICK_RATE_MS);
  937.                     }
  938.                 }
  939.                 ESP_LOGD(TAG, "Stream ended.");
  940.             } else {
  941.                 if (s_pixel_format == CAMERA_PF_JPEG) {
  942.                     netconn_write(conn, http_jpg_hdr, sizeof(http_jpg_hdr) - 1, NETCONN_NOCOPY);
  943.                 } else if (s_pixel_format == CAMERA_PF_GRAYSCALE) {
  944.                     netconn_write(conn, http_pgm_hdr, sizeof(http_pgm_hdr) - 1, NETCONN_NOCOPY);
  945.                     if (memcmp(&buf[5], "pgm", 3) == 0) {
  946.                         char pgm_header[32];
  947.                         snprintf(pgm_header, sizeof(pgm_header), "P5 %d %d %d\n", camera_get_fb_width(), camera_get_fb_height(), 255);
  948.                         netconn_write(conn, pgm_header, strlen(pgm_header), NETCONN_COPY);
  949.                     }
  950.                     else {
  951.                       char outstr[120];
  952.                       get_image_mime_info_str(outstr);
  953.                       netconn_write(conn, outstr, sizeof(outstr) - 1, NETCONN_NOCOPY);
  954.                       //netconn_write(conn, http_yuv422_hdr, sizeof(http_yuv422_hdr) - 1, NETCONN_NOCOPY);
  955.                     }
  956.                 } else
  957.                  if (s_pixel_format == CAMERA_PF_RGB565) {
  958.                     netconn_write(conn, http_bitmap_hdr, sizeof(http_bitmap_hdr) - 1, NETCONN_NOCOPY);
  959.                     if (memcmp(&buf[5], "bmp", 3) == 0) {
  960.                         char *bmp = bmp_create_header565(camera_get_fb_width(), camera_get_fb_height());
  961.                         err = netconn_write(conn, bmp, sizeof(bitmap565), NETCONN_COPY);
  962.                         free(bmp);
  963.                     }
  964.                     else {
  965.                       char outstr[120];
  966.                       get_image_mime_info_str(outstr);
  967.                       netconn_write(conn, outstr, sizeof(outstr) - 1, NETCONN_NOCOPY);
  968.                       //netconn_write(conn, http_yuv422_hdr, sizeof(http_yuv422_hdr) - 1, NETCONN_NOCOPY);
  969.                     }
  970.                 } else if (s_pixel_format == CAMERA_PF_YUV422) {
  971.                   if (memcmp(&buf[5], "bmp", 3) == 0) {
  972.                       //PAUSE_DISPLAY = true;
  973.                       // send YUV converted to 565 2bpp for now...
  974.                       netconn_write(conn, http_bitmap_hdr, sizeof(http_bitmap_hdr) - 1, NETCONN_NOCOPY);
  975.                       char *bmp = bmp_create_header565(camera_get_fb_width(), camera_get_fb_height());
  976.                       err = netconn_write(conn, bmp, sizeof(bitmap565), NETCONN_COPY);
  977.                       free(bmp);
  978.                   } else {
  979.                     char outstr[120];
  980.                     get_image_mime_info_str(outstr);
  981.                     netconn_write(conn, outstr, sizeof(outstr) - 1, NETCONN_NOCOPY);
  982.                     //netconn_write(conn, http_yuv422_hdr, sizeof(http_yuv422_hdr) - 1, NETCONN_NOCOPY);
  983.                   }
  984.                 } else {
  985.                   char outstr[120];
  986.                   get_image_mime_info_str(outstr);
  987.                   netconn_write(conn, outstr, sizeof(outstr) - 1, NETCONN_NOCOPY);
  988.                 }
  989.                 // handle non streaming images (http../get and http:../bmp )
  990.  
  991.                   ESP_LOGD(TAG, "Image requested.");
  992.                   PAUSE_DISPLAY = true;
  993.                   err = camera_run();
  994.                   PAUSE_DISPLAY = false;
  995.                   spi_lcd_send();
  996.                   spi_lcd_wait_finish();
  997.  
  998.                   if (err != ESP_OK) {
  999.                       ESP_LOGD(TAG, "Camera capture failed with error = %d", err);
  1000.                   } else {
  1001.                       ESP_LOGD(TAG, "Done");
  1002.                       //Send jpeg
  1003.                       if ((s_pixel_format == CAMERA_PF_RGB565) || (s_pixel_format == CAMERA_PF_YUV422)) {
  1004.                         ESP_LOGD(TAG, "Converting framebuffer to RGB565 requested, sending...");
  1005.                         uint8_t s_line[320*2];
  1006.                         uint32_t *fbl;
  1007.                         for (int i = 0; i < 240; i++) {
  1008.                           fbl = &currFbPtr[(i*320)/2];  //(i*(320*2)/4); // 4 bytes for each 2 pixel / 2 byte read..
  1009.                           convert_fb32bit_line_to_bmp565(fbl, s_line,s_pixel_format);
  1010.                           err = netconn_write(conn, s_line, 320*2,
  1011.                                         NETCONN_COPY);
  1012.                         }
  1013.                       } else
  1014.                         err = netconn_write(conn, camera_get_fb(), camera_get_data_size(),
  1015.                           NETCONN_NOCOPY);
  1016.                   } // handle .bmp and std gets...
  1017.  
  1018.             }
  1019.         }
  1020.     }
  1021.     /* Close the connection (server closes in HTTP) */
  1022.     netconn_close(conn);
  1023.     /* Delete the buffer (netconn_recv gives us ownership,
  1024.      so we have to make sure to deallocate the buffer) */
  1025.     netbuf_delete(inbuf);
  1026. }
  1027.  
  1028. static void http_server(void *pvParameters)
  1029. {
  1030.     struct netconn *conn, *newconn;
  1031.     err_t err;
  1032.     conn = netconn_new(NETCONN_TCP);
  1033.     netconn_bind(conn, NULL, 80);
  1034.     netconn_listen(conn);
  1035.     do {
  1036.         err = netconn_accept(conn, &newconn);
  1037.         if (err == ERR_OK) {
  1038.             http_server_netconn_serve(newconn);
  1039.             netconn_delete(newconn);
  1040.         }
  1041.     } while (err == ERR_OK);
  1042.     netconn_close(conn);
  1043.     netconn_delete(conn);
  1044. }
  1045.  
  1046. void app_main()
  1047. {
  1048.     size_t free8start, free32start, free8, free32;
  1049.  
  1050.     esp_log_level_set("wifi", ESP_LOG_WARN);
  1051.     esp_log_level_set("gpio", ESP_LOG_WARN);
  1052.  
  1053.     nvs_flash_init();
  1054.  
  1055.     ESP_LOGI(TAG,"Starting ESPILICAM");
  1056.  
  1057.     // report memory at startup
  1058.     ESP_LOGI(TAG,"Starting Memory Allocation for Display");
  1059.  
  1060.     free8=xPortGetFreeHeapSizeCaps(MALLOC_CAP_8BIT);
  1061.     free32=xPortGetFreeHeapSizeCaps(MALLOC_CAP_32BIT);
  1062.     ESP_LOGI(TAG, "Free 8bit-capable memory: %dK, 32-bit capable memory %dK\n", free8, free32);
  1063.  
  1064.  
  1065.     ESP_LOGD(TAG, "Allocating Frame Buffer memory...");
  1066.     currFbPtr=pvPortMallocCaps(320*240*2, MALLOC_CAP_32BIT);
  1067.  
  1068.     ESP_LOGI(TAG,"Setup ILI9341");
  1069.  
  1070.     esp_err_t ret;
  1071.     spi_bus_config_t buscfg={
  1072.         .miso_io_num=PIN_NUM_MISO,
  1073.         .mosi_io_num=PIN_NUM_MOSI,
  1074.         .sclk_io_num=PIN_NUM_CLK,
  1075.         .quadwp_io_num=-1,
  1076.         .quadhd_io_num=-1
  1077.     };
  1078.     spi_device_interface_config_t devcfg={
  1079.         .clock_speed_hz=10000000,               //Clock out at 10 MHz
  1080.         //.clock_speed_hz=26000000,               //Clock out at 26 MHz. Yes, that's heavily overclocked.
  1081.         .mode=0,                                //SPI mode 0
  1082.         .spics_io_num=PIN_NUM_CS,               //CS pin
  1083.         .queue_size=7,                          //We want to be able to queue 7 transactions at a time
  1084.         .pre_cb=ili_spi_pre_transfer_callback,  //Specify pre-transfer callback to handle D/C line
  1085.     };
  1086.     //Initialize the SPI bus
  1087.     ESP_LOGI(TAG, "Call spi_bus_initialize");
  1088.     ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1);
  1089.     assert(ret==ESP_OK);
  1090.     //Attach the LCD to the SPI bus
  1091.     ESP_LOGI(TAG, "Call spi_bus_add_device");
  1092.     ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
  1093.     assert(ret==ESP_OK);
  1094.     //Initialize the LCD
  1095.     ESP_LOGI(TAG, "Call ili_init");
  1096.     ili_init(spi);
  1097.  
  1098.     dispSem=xSemaphoreCreateBinary();
  1099.     dispDoneSem=xSemaphoreCreateBinary();
  1100.  
  1101.  
  1102.     // camera init
  1103.  
  1104.     esp_err_t err = camera_probe(&config, &camera_model);
  1105.     if (err != ESP_OK) {
  1106.         ESP_LOGE(TAG, "Camera probe failed with error 0x%x", err);
  1107.         return;
  1108.     }
  1109.     if (camera_model == CAMERA_OV7725) {
  1110.         ESP_LOGI(TAG, "Detected OV7725 camera, using grayscale bitmap format");
  1111.         s_pixel_format = CAMERA_PIXEL_FORMAT;
  1112.         config.frame_size = CAMERA_FRAME_SIZE;
  1113.     } else if (camera_model == CAMERA_OV7670) {
  1114.         ESP_LOGI(TAG, "Detected OV7670 camera, using grayscale bitmap format");
  1115.         s_pixel_format = CAMERA_PIXEL_FORMAT;
  1116.         config.frame_size = CAMERA_FRAME_SIZE;
  1117.     } else if (camera_model == CAMERA_OV2640) {
  1118.         ESP_LOGI(TAG, "Detected OV2640 camera, using JPEG format");
  1119.         s_pixel_format = CAMERA_PF_JPEG;
  1120.         config.frame_size = CAMERA_FS_VGA;
  1121.         config.jpeg_quality = 15;
  1122.     } else {
  1123.         ESP_LOGE(TAG, "Camera not supported");
  1124.         return;
  1125.     }
  1126.  
  1127.     free8=xPortGetFreeHeapSizeCaps(MALLOC_CAP_8BIT);
  1128.     free32=xPortGetFreeHeapSizeCaps(MALLOC_CAP_32BIT);
  1129.     ESP_LOGI(TAG, "Free 8bit-capable memory: %dK, 32-bit capable memory %dK\n", free8, free32);
  1130.  
  1131.     initialise_wifi();
  1132.  
  1133.     ESP_LOGD(TAG, "Wifi Initialized...");
  1134.     free8=xPortGetFreeHeapSizeCaps(MALLOC_CAP_8BIT);
  1135.     free32=xPortGetFreeHeapSizeCaps(MALLOC_CAP_32BIT);
  1136.     ESP_LOGI(TAG, "Free 8bit-capable memory: %dK, 32-bit capable memory %dK\n", free8, free32);
  1137.  
  1138.     //delay(10000);
  1139.     // VERY UNSTABLE without this delay after init'ing wifi...
  1140.     vTaskDelay(2000 / portTICK_RATE_MS);
  1141.  
  1142.     config.displayBuffer = currFbPtr;
  1143.     config.pixel_format = s_pixel_format;
  1144.     err = camera_init(&config);
  1145.     if (err != ESP_OK) {
  1146.         ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
  1147.         return;
  1148.     }
  1149.  
  1150.     vTaskDelay(1000 / portTICK_RATE_MS);
  1151.  
  1152.     ESP_LOGI(TAG, "Free heap: %u", xPortGetFreeHeapSize());
  1153.     ESP_LOGI(TAG, "Camera demo ready");
  1154.     if (s_pixel_format == CAMERA_PF_GRAYSCALE) {
  1155.         ESP_LOGI(TAG, "open http://" IPSTR "/get for a single grayscale bitmap (no headers)", IP2STR(&s_ip_addr));
  1156.         ESP_LOGI(TAG, "open http://" IPSTR "/pgm for a single image/x-portable-graymap image", IP2STR(&s_ip_addr));
  1157.     }
  1158.     if (s_pixel_format == CAMERA_PF_RGB565) {
  1159.         ESP_LOGI(TAG, "open http://" IPSTR "/bmp for single image/bitmap image", IP2STR(&s_ip_addr));
  1160.         ESP_LOGI(TAG, "open http://" IPSTR "/stream for multipart/x-mixed-replace stream of bitmaps", IP2STR(&s_ip_addr));
  1161.     }
  1162.     if (s_pixel_format == CAMERA_PF_JPEG) {
  1163.         ESP_LOGI(TAG, "open http://" IPSTR "/jpg for single image/jpg image", IP2STR(&s_ip_addr));
  1164.         ESP_LOGI(TAG, "open http://" IPSTR "/stream for multipart/x-mixed-replace stream of JPEGs", IP2STR(&s_ip_addr));
  1165.     }
  1166.  
  1167.     ESP_LOGD(TAG, "Starting http_server task...");
  1168. //    xTaskCreate(&http_server, "http_server", 4096, NULL, 5, NULL);
  1169.     xTaskCreatePinnedToCore(&http_server, "http_server", 8048, NULL, 5, NULL,1);
  1170.  
  1171.     ESP_LOGD(TAG, "Starting ILI9341 display task...");
  1172.     xSemaphoreGive(dispDoneSem);
  1173. //    xTaskCreate(&push_framebuffer_to_tft, "push_framebuffer_to_tft", 4096, NULL, 5, NULL);
  1174.     xTaskCreatePinnedToCore(&push_framebuffer_to_tft, "push_framebuffer_to_tft", 4096, NULL, 5, NULL,1);
  1175.  
  1176.     // start telnet task after IP is assigned...
  1177.     ESP_LOGD(TAG, "Starting Telnetd task...");
  1178.     xTaskCreatePinnedToCore(&telnetTask, "telnetTask", 8048, NULL, 5, NULL, 1);
  1179. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement