├── pc_monitor ├── pc_host_app │ ├── dependencies │ ├── cursor.png │ ├── include │ │ ├── process_capture │ │ ├── generate_eink_framebuffer.h │ │ ├── rle_compression.h │ │ ├── utils.h │ │ ├── 2bitLUTforC_inv.h │ │ ├── 2bitLUTforC.h │ │ ├── 64bitLUTc.h │ │ └── fill_adj_v2_inv.h │ ├── example_display.conf │ ├── display0.conf │ ├── display1.conf │ ├── display2.conf │ ├── display3.conf │ ├── Dither.h │ ├── ftdi245.cpp │ ├── utils.cpp │ ├── screen_capture.py │ ├── generate_eink_framebuffer.cpp │ ├── rle_compression.cpp │ ├── invert_.cpp │ ├── dither_.cpp │ └── draw_cursor.py ├── main │ ├── component.mk │ ├── CMakeLists.txt │ ├── pc_monitor.c │ └── main.c ├── CMakeLists.txt ├── changelog.md ├── include │ └── pc_monitor.h └── README.md └── README.md /pc_monitor/pc_host_app/dependencies: -------------------------------------------------------------------------------- 1 | Python: 2 | pip install mss 3 | pip install pyautogui 4 | pip install numpy 5 | 6 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amadeok/Epdiy-Eink-PC-monitor/HEAD/pc_monitor/pc_host_app/cursor.png -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/include/process_capture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amadeok/Epdiy-Eink-PC-monitor/HEAD/pc_monitor/pc_host_app/include/process_capture -------------------------------------------------------------------------------- /pc_monitor/main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # "main" pseudo-component makefile. 3 | # 4 | # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) 5 | -------------------------------------------------------------------------------- /pc_monitor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.0) 2 | set(EXTRA_COMPONENT_DIRS "../../components/" "$ENV{IDF_PATH}/components/nvs_flash") 3 | 4 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 5 | project(firmware) 6 | -------------------------------------------------------------------------------- /pc_monitor/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(app_sources "main.c" "pc_monitor.c") 2 | 3 | idf_component_register(SRCS ${app_sources} INCLUDE_DIRS "../include" "../../../components/epd_driver" "../../../components/epd_driver" REQUIRES epd_driver nvs_flash) 4 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/example_display.conf: -------------------------------------------------------------------------------- 1 | ip_address: 192.168.9.165 2 | id: 0 3 | width: 1200 4 | height: 825 5 | x_offset: 0 6 | y_offset: 0 7 | rotation: 0 8 | grey_monochrome_threshold: 200 9 | sleep_time: 50 10 | refresh_every_x_frames: 100 11 | framebuffer_cycles: 4 12 | rmt_high_time: 250:100 13 | enable_skipping: 0 14 | epd_skip_threshold: 75 15 | epd_skip_mouse_only: 1 16 | framebuffer_cycles_2: 4 17 | framebuffer_cycles_2_threshold: 76 18 | mode: 0 19 | color: 1.0 20 | contrast: 2.0 21 | brightness: 1.0 22 | sharpness: 1.0 23 | invert: 130 24 | selective_invert: 0 25 | polarize: 2.0,130,off 26 | enhance_before_greyscale: 0 27 | selective_compression: 90 28 | nb_chunks: 1 29 | do_full_refresh: 30 30 | draw_white_first: 0 31 | esp32_multithread: 0 32 | invert_draw_times: 0 33 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/display0.conf: -------------------------------------------------------------------------------- 1 | ip_address: 192.168.43.143 2 | id: 0 3 | width: 1200 4 | height: 825 5 | x_offset: 1920 6 | y_offset: 0 7 | rotation: 180 8 | grey_monochrome_threshold: 200 9 | sleep_time: 50 10 | refresh_every_x_frames: 100 11 | framebuffer_cycles: 4 12 | rmt_high_time: 250:100 13 | enable_skipping: 0 14 | epd_skip_threshold: 75 15 | epd_skip_mouse_only: 0 16 | framebuffer_cycles_2: 4 17 | framebuffer_cycles_2_threshold: 76 18 | mode: Bayer2 19 | color: 1.0 20 | contrast: 2.0 21 | brightness: 1.0 22 | sharpness: 1.0 23 | invert: 130 24 | selective_invert: 0 25 | polarize: 2.0,130,off 26 | enhance_before_greyscale: 0 27 | selective_compression: 90 28 | nb_chunks: 5 29 | do_full_refresh: 30 30 | draw_white_first: 1 31 | esp32_multithread: 0 32 | invert_draw_times: 1 33 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/display1.conf: -------------------------------------------------------------------------------- 1 | ip_address: 192.168.43.198 2 | id: 1 3 | width: 1200 4 | height: 825 5 | x_offset: 3120 6 | y_offset: 0 7 | rotation: 180 8 | grey_monochrome_threshold: 200 9 | sleep_time: 50 10 | refresh_every_x_frames: 100 11 | framebuffer_cycles: 4 12 | rmt_high_time: 250:100 13 | enable_skipping: 0 14 | epd_skip_threshold: 75 15 | epd_skip_mouse_only: 0 16 | framebuffer_cycles_2: 2 17 | framebuffer_cycles_2_threshold: 76 18 | mode: Bayer2 19 | color: 1.0 20 | contrast: 2.0 21 | brightness: 1.0 22 | sharpness: 1.0 23 | invert: 130 24 | selective_invert: 0 25 | polarize: 2.0,130,off 26 | enhance_before_greyscale: 0 27 | selective_compression: 90 28 | nb_chunks: 5 29 | do_full_refresh: 30 30 | draw_white_first: 1 31 | esp32_multithread: 0 32 | invert_draw_times: 1 33 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/display2.conf: -------------------------------------------------------------------------------- 1 | ip_address: 192.168.43.226 2 | id: 2 3 | width: 1200 4 | height: 825 5 | x_offset: 1920 6 | y_offset: 825 7 | rotation: 0 8 | grey_monochrome_threshold: 200 9 | sleep_time: 100 10 | refresh_every_x_frames: 100 11 | framebuffer_cycles: 4 12 | rmt_high_time: 250:100 13 | enable_skipping: 0 14 | epd_skip_threshold: 75 15 | epd_skip_mouse_only: 0 16 | framebuffer_cycles_2: 2 17 | framebuffer_cycles_2_threshold: 76 18 | mode: Bayer2 19 | color: 1.0 20 | contrast: 2.0 21 | brightness: 1.0 22 | sharpness: 1.0 23 | invert: 130 24 | selective_invert: 0 25 | polarize: 2.0,130,off 26 | enhance_before_greyscale: 0 27 | selective_compression: 90 28 | nb_chunks: 5 29 | do_full_refresh: 30 30 | draw_white_first: 1 31 | esp32_multithread: 0 32 | invert_draw_times: 1 33 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/display3.conf: -------------------------------------------------------------------------------- 1 | ip_address: 192.168.43.174 2 | id: 3 3 | width: 1200 4 | height: 825 5 | x_offset: 3120 6 | y_offset: 825 7 | rotation: 0 8 | grey_monochrome_threshold: 200 9 | sleep_time: 50 10 | refresh_every_x_frames: 100 11 | framebuffer_cycles: 4 12 | rmt_high_time: 250:100 13 | enable_skipping: 0 14 | epd_skip_threshold: 75 15 | epd_skip_mouse_only: 0 16 | framebuffer_cycles_2: 2 17 | framebuffer_cycles_2_threshold: 76 18 | mode: Bayer2 19 | color: 1.0 20 | contrast: 2.0 21 | brightness: 1.0 22 | sharpness: 1.0 23 | invert: 130 24 | selective_invert: 0 25 | polarize: 2.0,130,off 26 | enhance_before_greyscale: 0 27 | selective_compression: 90 28 | nb_chunks: 5 29 | do_full_refresh: 30 30 | draw_white_first: 1 31 | esp32_multithread: 0 32 | invert_draw_times: 1 33 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/include/generate_eink_framebuffer.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //Generate eink framebuffer from 1bpp monochrome captured image 4 | void *generate_eink_framebuffer_v1(unsigned char *source_1bpp, char *padded_2bpp_framebuffer_current, char *padded_2bpp_framebuffer_previous, char *eink_framebuffer); 5 | 6 | //Generate eink framebuffer from 8bpp monochrome captured image 7 | void generate_eink_framebuffer_v2(char *source_8bpp_current, char *source_8bpp_previous, char *source_8bpp_modified_previous, char **eink_framebuffer, int mode); 8 | 9 | 10 | void quantize(char *source_8bpp_current, char *source_8bpp_modified_current, int size); 11 | 12 | 13 | // Generate eink framebuffer and attempt to reduce ghosting (experimental) 14 | void generate_eink_framebuffer_v2_with_ghost(char *source_8bpp_current, char *source_8bpp_previous, char *source_8bpp_modified_previous, char **eink_framebuffer, int nb_pixels_to_change); 15 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/include/rle_compression.h: -------------------------------------------------------------------------------- 1 | 2 | // Compress a Eink framebuffer or a chunk of a Eink framebuffer 3 | int rle_compress(char *array_to_compress, char *compression_temporary_array, int nb_chunks, char *compressed_eink_framebuffer, const int total_nb_pixels, const int chunk_size); 4 | 5 | // Replaces unnecessary bytes with 0s to improve rle compression, not needed if using generate_eink_framebuffer_v2() 6 | void optimize_rle(char *eink_framebuffer); 7 | 8 | // Simpler extraction for debugging 9 | void rle_extract2(int compressed_size, unsigned char *decompressed_p, unsigned char *compressed, int k); 10 | 11 | // Extract a compressed framebuffer (for debugging) 12 | void rle_extract1(char *decompressed, int nb_chunks, char *eink_framebuffer_swapped, const int eink_framebuffer_size, const int chunk_size); 13 | 14 | int rle_compress_v2(char *array_to_compress, char *tmp_array, int nb_chunks, uint16_t **added_compression_arr, const int chunk_size); 15 | -------------------------------------------------------------------------------- /pc_monitor/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.06] - 05-06-2021 4 | - added draw white pixels first option 5 | - added selective inversion option 6 | - added smart invert option 7 | - reduced cpu usage when screen is not updating 8 | - added option to run download and draw on seperate task on the board 9 | 10 | ## [0.05] 11 | - added 8 dither modes 12 | 13 | ## [0.0421] - 25-04-2021 14 | -added option to do full refresh with epd_clear() instead of a quick one 15 | 16 | ## [0.042] - 09-04-2021 17 | -Added option to invert image 18 | -Added feature to choose wether hotkeys presses affect all instances of the running applications or only the one on which they are used 19 | -Fixed detecting mouse movement not working properly 20 | 21 | 22 | ## [0.041] - 26-03-2021 23 | -Fixed enable_skipping option not working 24 | 25 | ## [0.04] - 25-03-2021 26 | -Added option to apply Pillow color, contrast, brightness and sharpness enhancements to the image before it is converted to 1bpp 27 | 28 | ## [0.03] - 21-03-2021 29 | 30 | 31 | - Added support for pseudo greyscale mode 32 | - Added feature to choose minimum eficiency for the rle compression 33 | 34 | ## [0.02] - 19-03-2021- 35 | 36 | - Support for using multiple displays at the same time 37 | - Added feature to refresh screen after a certain number of frame draws 38 | - Fixed heap corruption on ESP32 application 39 | 40 | ## [0.01] 41 | 42 | - Initial release 43 | 44 | 45 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/Dither.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __DITHER_H__ 3 | #define __DITHER_H__ 4 | 5 | /***************************************************************************** 6 | FILE : Dither.h 7 | Author : Svetoslav Chekanov 8 | Description: Collection of dithering algorithms 9 | 10 | Copyright (c) 2014 Brosix 11 | *****************************************************************************/ 12 | /* # Revisions # */ 13 | 14 | ///////////////////////////////////////////////////////////////////////////// 15 | // Ordered dither using matrix 16 | ///////////////////////////////////////////////////////////////////////////// 17 | 18 | void makeDitherBayer16( unsigned char* pixels, int width, int height ) noexcept; 19 | void makeDitherBayer8 ( unsigned char* pixels, int width, int height ) noexcept; 20 | void makeDitherBayer4 ( unsigned char* pixels, int width, int height ) noexcept; 21 | void makeDitherBayer3 ( unsigned char* pixels, int width, int height ) noexcept; 22 | void makeDitherBayer2 ( unsigned char* pixels, int width, int height ) noexcept; 23 | 24 | ///////////////////////////////////////////////////////////////////////////// 25 | // Floyd-Steinberg dither 26 | ///////////////////////////////////////////////////////////////////////////// 27 | 28 | void makeDitherFS ( unsigned char* pixels, int width, int height ) noexcept; 29 | 30 | void makeDitherSierraLite ( unsigned char* pixels, int width, int height ) noexcept; 31 | void makeDitherSierra ( unsigned char* pixels, int width, int height ) noexcept; 32 | 33 | ///////////////////////////////////////////////////////////////////////////// 34 | 35 | #endif // !__DITHER_H__ 36 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/include/utils.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | uint32_t getTick(); 4 | 5 | #if !defined(_WIN32) 6 | #define HANDLE int 7 | #define DWORD unsigned long 8 | #define LPDWORD *DWORD. 9 | #define SOCKET int32_t 10 | #include "unistd.h" 11 | #define cwd getcwd 12 | #define cd chdir 13 | #endif 14 | #ifdef _WIN32 15 | #include 16 | #include 17 | #define cwd _getcwd 18 | #define cd _chdir 19 | #endif 20 | 21 | //Write an array to a file 22 | void array_to_file(void *array, int nb_bytes_to_write, const char *path, const char *filename, int k); 23 | 24 | //Read a file into an array in memory 25 | void file_to_array(char array[], int array_size, int file_size, const char *path, const char *filename, int k); 26 | 27 | //Swaps the bytes in the framebuffer to get them in the order that the board needs 28 | void swap_bytes(char *eink_framebuffer, char *eink_framebuffer_swapped, int eink_framebuffer_size, int source_image_bit_depth); 29 | 30 | // if a line didn't actually change in the original 8bpp capture, set the corresponding line to 0s 31 | // so that the dithering doesn't spoil the rest of the image 32 | void improve_dither_compression(unsigned char *eink_framebuffer, int eink_framebuffer_size, unsigned char *line_changed, int width, int height); 33 | 34 | //Compare a framebuffer received from the board to the one sent (for debugging) 35 | int extract_and_compare(unsigned char *eink_framebuffer_swapped, int g); 36 | 37 | DWORD pipe_read(HANDLE handle, void *buffer, DWORD nNumberOfBytesToRead, DWORD lpNumberOfBytesRead); 38 | DWORD pipe_write(HANDLE handle, void *buffer, DWORD nNumberOfBytesToWrite, DWORD lpNumberOfBytesWritten); 39 | 40 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/ftdi245.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ftd2xx.h" 10 | 11 | FT_HANDLE init_ft245_mode() 12 | { 13 | 14 | FT_HANDLE handle; 15 | 16 | // check how many FTDI devices are attached to this PC 17 | uint32_t deviceCount = 0; 18 | if (FT_CreateDeviceInfoList(&deviceCount) != FT_OK) 19 | { 20 | printf("Unable to query devices. Exiting.\r\n"); 21 | exit(EXIT_FAILURE); 22 | } 23 | 24 | // get a list of information about each FTDI device 25 | FT_DEVICE_LIST_INFO_NODE *deviceInfo = (FT_DEVICE_LIST_INFO_NODE *)malloc(sizeof(FT_DEVICE_LIST_INFO_NODE) * deviceCount); 26 | if (FT_GetDeviceInfoList(deviceInfo, &deviceCount) != FT_OK) 27 | { 28 | printf("Unable to get the list of info. Exiting.\r\n"); 29 | exit(EXIT_FAILURE); 30 | } 31 | 32 | // print the list of information 33 | for (unsigned long i = 0; i < deviceCount; i++) 34 | { 35 | 36 | printf("Device = %d\r\n", i); 37 | printf("Flags = 0x%X\r\n", deviceInfo[i].Flags); 38 | printf("Type = 0x%X\r\n", deviceInfo[i].Type); 39 | printf("ID = 0x%X\r\n", deviceInfo[i].ID); 40 | printf("LocId = 0x%X\r\n", deviceInfo[i].LocId); 41 | printf("SN = %s\r\n", deviceInfo[i].SerialNumber); 42 | printf("Description = %s\r\n", deviceInfo[i].Description); 43 | printf("Handle = 0x%X\r\n", deviceInfo[i].ftHandle); 44 | printf("\r\n"); 45 | 46 | if (FT_OpenEx(deviceInfo[i].SerialNumber, FT_OPEN_BY_SERIAL_NUMBER, &handle) == FT_OK && 47 | FT_SetBitMode(handle, 0x0, 0x40) == FT_OK && 48 | FT_SetLatencyTimer(handle, 2) == FT_OK && 49 | FT_SetUSBParameters(handle, 65536, 65536) == FT_OK && 50 | FT_SetFlowControl(handle, FT_FLOW_RTS_CTS, 0, 0) == FT_OK && 51 | FT_Purge(handle, FT_PURGE_RX | FT_PURGE_TX) == FT_OK && 52 | FT_SetTimeouts(handle, 10000, 10000) == FT_OK) 53 | { 54 | return deviceInfo[i].ftHandle; 55 | } 56 | else 57 | printf("error getting handle for ft232h\n"); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pc_monitor/include/pc_monitor.h: -------------------------------------------------------------------------------- 1 | #include "esp_attr.h" 2 | #include 3 | #include 4 | #include "freertos/FreeRTOS.h" 5 | #include "freertos/queue.h" 6 | #include "freertos/semphr.h" 7 | #include "freertos/task.h" 8 | #include "xtensa/core-macros.h" 9 | 10 | #define DEBUG_MSGs 0 11 | 12 | volatile int renderer_chunk_counter, downloader_chunk_counter; 13 | volatile unsigned long renderer_frame_counter, downloader_frame_counter; 14 | volatile int current_buffer; 15 | volatile int renderer_busy, downloader_busy; 16 | volatile int stop, clearing; 17 | volatile unsigned long tr0, tr1, td0, td1; 18 | 19 | volatile uint8_t mouse_moved; 20 | int download_size; 21 | int per_frame_wifi_settings_size; 22 | uint16_t *settings; 23 | 24 | int width_resolution, height_resolution; 25 | 26 | int total_nb_pixels, eink_framebuffer_size, chunk_size, nb_chunks, nb_rows_per_chunk; 27 | 28 | int framebuffer_cycles; // sets the number of times to write the current framebuffer to the screen 29 | int rmt_high_time; // defined in rmt_pulse.h, a higher value makes blacks blacker and whites whiter 30 | int framebuffer_cycles_2, framebuffer_cycles_2_threshold; 31 | int enable_skipping, epd_skip_threshold, epd_skip_mouse_only; 32 | int draw_white_first; 33 | int esp32_multithread; 34 | int selective_compression; 35 | int extra_bytes; 36 | int nb_draws; 37 | int nb_rmt_times; 38 | int mode; 39 | int frame_counter; 40 | uint8_t need_to_extract; 41 | 42 | uint8_t *compressed_chunk; 43 | uint8_t *chunk_lenghts; 44 | int32_t *chunk_lenghts_int; 45 | uint8_t *line_changed; 46 | int16_t *total_lines_changed; 47 | uint8_t *array_with_zeros; 48 | uint8_t *draw_black_bytes; 49 | uint8_t *draw_white_bytes; 50 | uint8_t **framebuffer_chunks; 51 | uint8_t *second_framebuffer; 52 | uint8_t *compressed_chunk; 53 | uint8_t *where_to_download; 54 | uint16_t *draw_rmt_times; 55 | uint8_t *per_frame_wifi_settings; 56 | uint8_t *fc0, *fc1, *fc2, *fc3, *fc4, *fc5, *fc6, *fc7, *fc8, *fc9; 57 | uint8_t ready0[6]; 58 | volatile uint8_t clear[2]; 59 | 60 | 61 | SemaphoreHandle_t begin; 62 | 63 | /** 64 | * Write the decompressed buffers to the display 65 | */ 66 | void IRAM_ATTR pc_monitor_feed_display(int total_lines_changed); 67 | 68 | void IRAM_ATTR pc_monitor_feed_display_with_skip(int total_lines_changed); 69 | 70 | uint8_t *get_current_chunk_ptr(int chunk_number); 71 | 72 | /** 73 | * Write the decompressed buffers to the display while the next one is being downloaded and extracted. Experimental, for testing only. 74 | */ 75 | void IRAM_ATTR pc_monitor_feed_display_multithreaded_v1(); 76 | 77 | void IRAM_ATTR pc_monitor_feed_display_multithreaded_v2(); 78 | 79 | void IRAM_ATTR signal_245_fifo(const int sock); 80 | 81 | void IRAM_ATTR pc_monitor_feed_display_multithreaded_v1_one_chunk(); 82 | int IRAM_ATTR switch_framebuffer_n(int n); 83 | void IRAM_ATTR switch_framebuffer(); 84 | int back_buffer(); 85 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #ifdef _WIN32 12 | #include 13 | 14 | #endif 15 | 16 | extern unsigned char *compressed_eink_framebuffer_ptrs[8]; 17 | extern unsigned char *decompressed_received; 18 | 19 | extern int compressed_chunk_lengths[8]; 20 | extern int chunk_size; 21 | 22 | //struct timeb start, end; 23 | 24 | uint32_t getTick() 25 | { 26 | #ifdef __linux__ 27 | struct timespec ts; 28 | unsigned theTick = 0U; 29 | clock_gettime(CLOCK_REALTIME, &ts); 30 | theTick = ts.tv_nsec / 1000000; 31 | theTick += ts.tv_sec * 1000; 32 | #elif _WIN32 33 | ULONGLONG theTick = GetTickCount(); 34 | // ftime(&start); 35 | 36 | #endif 37 | return theTick; 38 | } 39 | void array_to_file(void *array, int nb_bytes_to_write, const char *path, const char *filename, int k) 40 | { 41 | char buffer[250]; 42 | sprintf(buffer, "%s/%s%d", path, filename, k); 43 | FILE *f3 = fopen(buffer, "wb"); 44 | fwrite(array, sizeof(char), nb_bytes_to_write, f3); 45 | fflush(f3); 46 | fclose(f3); 47 | } 48 | void file_to_array(char array[], int array_size, int file_size, const char *path, const char *filename, int k) 49 | { 50 | char buffer[250]; 51 | sprintf(buffer, "%s%s", path, filename); 52 | //array = (char*)malloc( array_size * sizeof(char)); 53 | std::string inFileName = buffer; 54 | std::ifstream inFile(inFileName, std::ios::binary); 55 | int begin = inFile.tellg(); 56 | inFile.seekg(0, std::ios::end); 57 | int end = inFile.tellg(); 58 | inFile.seekg(0, std::ios::beg); 59 | inFile.read(array, file_size); 60 | inFile.close(); 61 | } 62 | 63 | void swap_bytes(char *eink_framebuffer, char *eink_framebuffer_swapped, int eink_framebuffer_size, int source_image_bit_depth) 64 | { //swapping bytes is necessary to get them in the order that the board needs 65 | // long t = getTick(); 66 | 67 | for (int h = 0; h < eink_framebuffer_size; h += 4) 68 | { 69 | // memcpy(temp_arr, eink_framebuffer + h, 4); 70 | eink_framebuffer_swapped[h] = eink_framebuffer[h + 2]; 71 | eink_framebuffer_swapped[h + 1] = eink_framebuffer[h + 3]; 72 | eink_framebuffer_swapped[h + 2] = eink_framebuffer[h + 0]; 73 | eink_framebuffer_swapped[h + 3] = eink_framebuffer[h + 1]; 74 | } 75 | 76 | // printf("swapping took: %d\n", getTick() - t); 77 | } 78 | 79 | void improve_dither_compression(unsigned char *eink_framebuffer, int eink_framebuffer_size, unsigned char *line_changed, int width, int height) 80 | { 81 | int line_size = width / 4; 82 | for (int h = 0; h < height; h++) 83 | { 84 | if (line_changed[h] == 0) 85 | memset(eink_framebuffer + h * line_size, 0, line_size); 86 | } 87 | } 88 | 89 | int extract_and_compare(unsigned char *eink_framebuffer_swapped, int g) 90 | { 91 | rle_extract2(compressed_chunk_lengths[g], decompressed_received, compressed_eink_framebuffer_ptrs[g], g); 92 | 93 | for (int h = 0; h < chunk_size - 2; h++) 94 | { 95 | if (decompressed_received[h] != eink_framebuffer_swapped[(g * chunk_size) + h]) 96 | { 97 | printf("arrays are different %d %d \n", decompressed_received[h], eink_framebuffer_swapped[(g * chunk_size) + h]); 98 | sleep(10); 99 | return h; 100 | } 101 | } 102 | } 103 | 104 | DWORD pipe_read(HANDLE handle, void *buffer, DWORD nNumberOfBytesToRead, DWORD lpNumberOfBytesRead) 105 | { 106 | DWORD ret; 107 | #ifdef __linux__ 108 | lpNumberOfBytesRead = read(handle, buffer, nNumberOfBytesToRead * sizeof(unsigned char)); 109 | #elif _WIN32 110 | ReadFile(handle, buffer, sizeof(unsigned char) * nNumberOfBytesToRead, &lpNumberOfBytesRead, NULL); 111 | #endif 112 | return lpNumberOfBytesRead; 113 | } 114 | 115 | DWORD pipe_write(HANDLE handle, void *buffer, DWORD nNumberOfBytesToWrite, DWORD lpNumberOfBytesWritten) 116 | 117 | { 118 | #ifdef __linux__ 119 | lpNumberOfBytesWritten = write(handle, buffer, nNumberOfBytesToWrite); 120 | #elif _WIN32 121 | WriteFile(handle, buffer, nNumberOfBytesToWrite, &lpNumberOfBytesWritten, NULL); 122 | #endif 123 | return lpNumberOfBytesWritten; 124 | } 125 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/include/2bitLUTforC_inv.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint8_t two_bit_LUT[512] = { 3 | 0x0, 0x0, // 0 4 | 0x0, 0x40, // 1 5 | 0x0, 0x10, // 2 6 | 0x0, 0x50, // 3 7 | 0x0, 0x4, // 4 8 | 0x0, 0x44, // 5 9 | 0x0, 0x14, // 6 10 | 0x0, 0x54, // 7 11 | 0x0, 0x1, // 8 12 | 0x0, 0x41, // 9 13 | 0x0, 0x11, // 10 14 | 0x0, 0x51, // 11 15 | 0x0, 0x5, // 12 16 | 0x0, 0x45, // 13 17 | 0x0, 0x15, // 14 18 | 0x0, 0x55, // 15 19 | 0x40, 0x0, // 16 20 | 0x40, 0x40, // 17 21 | 0x40, 0x10, // 18 22 | 0x40, 0x50, // 19 23 | 0x40, 0x4, // 20 24 | 0x40, 0x44, // 21 25 | 0x40, 0x14, // 22 26 | 0x40, 0x54, // 23 27 | 0x40, 0x1, // 24 28 | 0x40, 0x41, // 25 29 | 0x40, 0x11, // 26 30 | 0x40, 0x51, // 27 31 | 0x40, 0x5, // 28 32 | 0x40, 0x45, // 29 33 | 0x40, 0x15, // 30 34 | 0x40, 0x55, // 31 35 | 0x10, 0x0, // 32 36 | 0x10, 0x40, // 33 37 | 0x10, 0x10, // 34 38 | 0x10, 0x50, // 35 39 | 0x10, 0x4, // 36 40 | 0x10, 0x44, // 37 41 | 0x10, 0x14, // 38 42 | 0x10, 0x54, // 39 43 | 0x10, 0x1, // 40 44 | 0x10, 0x41, // 41 45 | 0x10, 0x11, // 42 46 | 0x10, 0x51, // 43 47 | 0x10, 0x5, // 44 48 | 0x10, 0x45, // 45 49 | 0x10, 0x15, // 46 50 | 0x10, 0x55, // 47 51 | 0x50, 0x0, // 48 52 | 0x50, 0x40, // 49 53 | 0x50, 0x10, // 50 54 | 0x50, 0x50, // 51 55 | 0x50, 0x4, // 52 56 | 0x50, 0x44, // 53 57 | 0x50, 0x14, // 54 58 | 0x50, 0x54, // 55 59 | 0x50, 0x1, // 56 60 | 0x50, 0x41, // 57 61 | 0x50, 0x11, // 58 62 | 0x50, 0x51, // 59 63 | 0x50, 0x5, // 60 64 | 0x50, 0x45, // 61 65 | 0x50, 0x15, // 62 66 | 0x50, 0x55, // 63 67 | 0x4, 0x0, // 64 68 | 0x4, 0x40, // 65 69 | 0x4, 0x10, // 66 70 | 0x4, 0x50, // 67 71 | 0x4, 0x4, // 68 72 | 0x4, 0x44, // 69 73 | 0x4, 0x14, // 70 74 | 0x4, 0x54, // 71 75 | 0x4, 0x1, // 72 76 | 0x4, 0x41, // 73 77 | 0x4, 0x11, // 74 78 | 0x4, 0x51, // 75 79 | 0x4, 0x5, // 76 80 | 0x4, 0x45, // 77 81 | 0x4, 0x15, // 78 82 | 0x4, 0x55, // 79 83 | 0x44, 0x0, // 80 84 | 0x44, 0x40, // 81 85 | 0x44, 0x10, // 82 86 | 0x44, 0x50, // 83 87 | 0x44, 0x4, // 84 88 | 0x44, 0x44, // 85 89 | 0x44, 0x14, // 86 90 | 0x44, 0x54, // 87 91 | 0x44, 0x1, // 88 92 | 0x44, 0x41, // 89 93 | 0x44, 0x11, // 90 94 | 0x44, 0x51, // 91 95 | 0x44, 0x5, // 92 96 | 0x44, 0x45, // 93 97 | 0x44, 0x15, // 94 98 | 0x44, 0x55, // 95 99 | 0x14, 0x0, // 96 100 | 0x14, 0x40, // 97 101 | 0x14, 0x10, // 98 102 | 0x14, 0x50, // 99 103 | 0x14, 0x4, // 100 104 | 0x14, 0x44, // 101 105 | 0x14, 0x14, // 102 106 | 0x14, 0x54, // 103 107 | 0x14, 0x1, // 104 108 | 0x14, 0x41, // 105 109 | 0x14, 0x11, // 106 110 | 0x14, 0x51, // 107 111 | 0x14, 0x5, // 108 112 | 0x14, 0x45, // 109 113 | 0x14, 0x15, // 110 114 | 0x14, 0x55, // 111 115 | 0x54, 0x0, // 112 116 | 0x54, 0x40, // 113 117 | 0x54, 0x10, // 114 118 | 0x54, 0x50, // 115 119 | 0x54, 0x4, // 116 120 | 0x54, 0x44, // 117 121 | 0x54, 0x14, // 118 122 | 0x54, 0x54, // 119 123 | 0x54, 0x1, // 120 124 | 0x54, 0x41, // 121 125 | 0x54, 0x11, // 122 126 | 0x54, 0x51, // 123 127 | 0x54, 0x5, // 124 128 | 0x54, 0x45, // 125 129 | 0x54, 0x15, // 126 130 | 0x54, 0x55, // 127 131 | 0x1, 0x0, // 128 132 | 0x1, 0x40, // 129 133 | 0x1, 0x10, // 130 134 | 0x1, 0x50, // 131 135 | 0x1, 0x4, // 132 136 | 0x1, 0x44, // 133 137 | 0x1, 0x14, // 134 138 | 0x1, 0x54, // 135 139 | 0x1, 0x1, // 136 140 | 0x1, 0x41, // 137 141 | 0x1, 0x11, // 138 142 | 0x1, 0x51, // 139 143 | 0x1, 0x5, // 140 144 | 0x1, 0x45, // 141 145 | 0x1, 0x15, // 142 146 | 0x1, 0x55, // 143 147 | 0x41, 0x0, // 144 148 | 0x41, 0x40, // 145 149 | 0x41, 0x10, // 146 150 | 0x41, 0x50, // 147 151 | 0x41, 0x4, // 148 152 | 0x41, 0x44, // 149 153 | 0x41, 0x14, // 150 154 | 0x41, 0x54, // 151 155 | 0x41, 0x1, // 152 156 | 0x41, 0x41, // 153 157 | 0x41, 0x11, // 154 158 | 0x41, 0x51, // 155 159 | 0x41, 0x5, // 156 160 | 0x41, 0x45, // 157 161 | 0x41, 0x15, // 158 162 | 0x41, 0x55, // 159 163 | 0x11, 0x0, // 160 164 | 0x11, 0x40, // 161 165 | 0x11, 0x10, // 162 166 | 0x11, 0x50, // 163 167 | 0x11, 0x4, // 164 168 | 0x11, 0x44, // 165 169 | 0x11, 0x14, // 166 170 | 0x11, 0x54, // 167 171 | 0x11, 0x1, // 168 172 | 0x11, 0x41, // 169 173 | 0x11, 0x11, // 170 174 | 0x11, 0x51, // 171 175 | 0x11, 0x5, // 172 176 | 0x11, 0x45, // 173 177 | 0x11, 0x15, // 174 178 | 0x11, 0x55, // 175 179 | 0x51, 0x0, // 176 180 | 0x51, 0x40, // 177 181 | 0x51, 0x10, // 178 182 | 0x51, 0x50, // 179 183 | 0x51, 0x4, // 180 184 | 0x51, 0x44, // 181 185 | 0x51, 0x14, // 182 186 | 0x51, 0x54, // 183 187 | 0x51, 0x1, // 184 188 | 0x51, 0x41, // 185 189 | 0x51, 0x11, // 186 190 | 0x51, 0x51, // 187 191 | 0x51, 0x5, // 188 192 | 0x51, 0x45, // 189 193 | 0x51, 0x15, // 190 194 | 0x51, 0x55, // 191 195 | 0x5, 0x0, // 192 196 | 0x5, 0x40, // 193 197 | 0x5, 0x10, // 194 198 | 0x5, 0x50, // 195 199 | 0x5, 0x4, // 196 200 | 0x5, 0x44, // 197 201 | 0x5, 0x14, // 198 202 | 0x5, 0x54, // 199 203 | 0x5, 0x1, // 200 204 | 0x5, 0x41, // 201 205 | 0x5, 0x11, // 202 206 | 0x5, 0x51, // 203 207 | 0x5, 0x5, // 204 208 | 0x5, 0x45, // 205 209 | 0x5, 0x15, // 206 210 | 0x5, 0x55, // 207 211 | 0x45, 0x0, // 208 212 | 0x45, 0x40, // 209 213 | 0x45, 0x10, // 210 214 | 0x45, 0x50, // 211 215 | 0x45, 0x4, // 212 216 | 0x45, 0x44, // 213 217 | 0x45, 0x14, // 214 218 | 0x45, 0x54, // 215 219 | 0x45, 0x1, // 216 220 | 0x45, 0x41, // 217 221 | 0x45, 0x11, // 218 222 | 0x45, 0x51, // 219 223 | 0x45, 0x5, // 220 224 | 0x45, 0x45, // 221 225 | 0x45, 0x15, // 222 226 | 0x45, 0x55, // 223 227 | 0x15, 0x0, // 224 228 | 0x15, 0x40, // 225 229 | 0x15, 0x10, // 226 230 | 0x15, 0x50, // 227 231 | 0x15, 0x4, // 228 232 | 0x15, 0x44, // 229 233 | 0x15, 0x14, // 230 234 | 0x15, 0x54, // 231 235 | 0x15, 0x1, // 232 236 | 0x15, 0x41, // 233 237 | 0x15, 0x11, // 234 238 | 0x15, 0x51, // 235 239 | 0x15, 0x5, // 236 240 | 0x15, 0x45, // 237 241 | 0x15, 0x15, // 238 242 | 0x15, 0x55, // 239 243 | 0x55, 0x0, // 240 244 | 0x55, 0x40, // 241 245 | 0x55, 0x10, // 242 246 | 0x55, 0x50, // 243 247 | 0x55, 0x4, // 244 248 | 0x55, 0x44, // 245 249 | 0x55, 0x14, // 246 250 | 0x55, 0x54, // 247 251 | 0x55, 0x1, // 248 252 | 0x55, 0x41, // 249 253 | 0x55, 0x11, // 250 254 | 0x55, 0x51, // 251 255 | 0x55, 0x5, // 252 256 | 0x55, 0x45, // 253 257 | 0x55, 0x15, // 254 258 | 0x55, 0x55, // 255 259 | }; 260 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/include/2bitLUTforC.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const unsigned char two_bit_LUT[512] = { 4 | 0x0, 0x0, // 0 5 | 0x0, 0x1, // 1 6 | 0x0, 0x4, // 2 7 | 0x0, 0x5, // 3 8 | 0x0, 0x10, // 4 9 | 0x0, 0x11, // 5 10 | 0x0, 0x14, // 6 11 | 0x0, 0x15, // 7 12 | 0x0, 0x40, // 8 13 | 0x0, 0x41, // 9 14 | 0x0, 0x44, // 10 15 | 0x0, 0x45, // 11 16 | 0x0, 0x50, // 12 17 | 0x0, 0x51, // 13 18 | 0x0, 0x54, // 14 19 | 0x0, 0x55, // 15 20 | 0x1, 0x0, // 16 21 | 0x1, 0x1, // 17 22 | 0x1, 0x4, // 18 23 | 0x1, 0x5, // 19 24 | 0x1, 0x10, // 20 25 | 0x1, 0x11, // 21 26 | 0x1, 0x14, // 22 27 | 0x1, 0x15, // 23 28 | 0x1, 0x40, // 24 29 | 0x1, 0x41, // 25 30 | 0x1, 0x44, // 26 31 | 0x1, 0x45, // 27 32 | 0x1, 0x50, // 28 33 | 0x1, 0x51, // 29 34 | 0x1, 0x54, // 30 35 | 0x1, 0x55, // 31 36 | 0x4, 0x0, // 32 37 | 0x4, 0x1, // 33 38 | 0x4, 0x4, // 34 39 | 0x4, 0x5, // 35 40 | 0x4, 0x10, // 36 41 | 0x4, 0x11, // 37 42 | 0x4, 0x14, // 38 43 | 0x4, 0x15, // 39 44 | 0x4, 0x40, // 40 45 | 0x4, 0x41, // 41 46 | 0x4, 0x44, // 42 47 | 0x4, 0x45, // 43 48 | 0x4, 0x50, // 44 49 | 0x4, 0x51, // 45 50 | 0x4, 0x54, // 46 51 | 0x4, 0x55, // 47 52 | 0x5, 0x0, // 48 53 | 0x5, 0x1, // 49 54 | 0x5, 0x4, // 50 55 | 0x5, 0x5, // 51 56 | 0x5, 0x10, // 52 57 | 0x5, 0x11, // 53 58 | 0x5, 0x14, // 54 59 | 0x5, 0x15, // 55 60 | 0x5, 0x40, // 56 61 | 0x5, 0x41, // 57 62 | 0x5, 0x44, // 58 63 | 0x5, 0x45, // 59 64 | 0x5, 0x50, // 60 65 | 0x5, 0x51, // 61 66 | 0x5, 0x54, // 62 67 | 0x5, 0x55, // 63 68 | 0x10, 0x0, // 64 69 | 0x10, 0x1, // 65 70 | 0x10, 0x4, // 66 71 | 0x10, 0x5, // 67 72 | 0x10, 0x10, // 68 73 | 0x10, 0x11, // 69 74 | 0x10, 0x14, // 70 75 | 0x10, 0x15, // 71 76 | 0x10, 0x40, // 72 77 | 0x10, 0x41, // 73 78 | 0x10, 0x44, // 74 79 | 0x10, 0x45, // 75 80 | 0x10, 0x50, // 76 81 | 0x10, 0x51, // 77 82 | 0x10, 0x54, // 78 83 | 0x10, 0x55, // 79 84 | 0x11, 0x0, // 80 85 | 0x11, 0x1, // 81 86 | 0x11, 0x4, // 82 87 | 0x11, 0x5, // 83 88 | 0x11, 0x10, // 84 89 | 0x11, 0x11, // 85 90 | 0x11, 0x14, // 86 91 | 0x11, 0x15, // 87 92 | 0x11, 0x40, // 88 93 | 0x11, 0x41, // 89 94 | 0x11, 0x44, // 90 95 | 0x11, 0x45, // 91 96 | 0x11, 0x50, // 92 97 | 0x11, 0x51, // 93 98 | 0x11, 0x54, // 94 99 | 0x11, 0x55, // 95 100 | 0x14, 0x0, // 96 101 | 0x14, 0x1, // 97 102 | 0x14, 0x4, // 98 103 | 0x14, 0x5, // 99 104 | 0x14, 0x10, // 100 105 | 0x14, 0x11, // 101 106 | 0x14, 0x14, // 102 107 | 0x14, 0x15, // 103 108 | 0x14, 0x40, // 104 109 | 0x14, 0x41, // 105 110 | 0x14, 0x44, // 106 111 | 0x14, 0x45, // 107 112 | 0x14, 0x50, // 108 113 | 0x14, 0x51, // 109 114 | 0x14, 0x54, // 110 115 | 0x14, 0x55, // 111 116 | 0x15, 0x0, // 112 117 | 0x15, 0x1, // 113 118 | 0x15, 0x4, // 114 119 | 0x15, 0x5, // 115 120 | 0x15, 0x10, // 116 121 | 0x15, 0x11, // 117 122 | 0x15, 0x14, // 118 123 | 0x15, 0x15, // 119 124 | 0x15, 0x40, // 120 125 | 0x15, 0x41, // 121 126 | 0x15, 0x44, // 122 127 | 0x15, 0x45, // 123 128 | 0x15, 0x50, // 124 129 | 0x15, 0x51, // 125 130 | 0x15, 0x54, // 126 131 | 0x15, 0x55, // 127 132 | 0x40, 0x0, // 128 133 | 0x40, 0x1, // 129 134 | 0x40, 0x4, // 130 135 | 0x40, 0x5, // 131 136 | 0x40, 0x10, // 132 137 | 0x40, 0x11, // 133 138 | 0x40, 0x14, // 134 139 | 0x40, 0x15, // 135 140 | 0x40, 0x40, // 136 141 | 0x40, 0x41, // 137 142 | 0x40, 0x44, // 138 143 | 0x40, 0x45, // 139 144 | 0x40, 0x50, // 140 145 | 0x40, 0x51, // 141 146 | 0x40, 0x54, // 142 147 | 0x40, 0x55, // 143 148 | 0x41, 0x0, // 144 149 | 0x41, 0x1, // 145 150 | 0x41, 0x4, // 146 151 | 0x41, 0x5, // 147 152 | 0x41, 0x10, // 148 153 | 0x41, 0x11, // 149 154 | 0x41, 0x14, // 150 155 | 0x41, 0x15, // 151 156 | 0x41, 0x40, // 152 157 | 0x41, 0x41, // 153 158 | 0x41, 0x44, // 154 159 | 0x41, 0x45, // 155 160 | 0x41, 0x50, // 156 161 | 0x41, 0x51, // 157 162 | 0x41, 0x54, // 158 163 | 0x41, 0x55, // 159 164 | 0x44, 0x0, // 160 165 | 0x44, 0x1, // 161 166 | 0x44, 0x4, // 162 167 | 0x44, 0x5, // 163 168 | 0x44, 0x10, // 164 169 | 0x44, 0x11, // 165 170 | 0x44, 0x14, // 166 171 | 0x44, 0x15, // 167 172 | 0x44, 0x40, // 168 173 | 0x44, 0x41, // 169 174 | 0x44, 0x44, // 170 175 | 0x44, 0x45, // 171 176 | 0x44, 0x50, // 172 177 | 0x44, 0x51, // 173 178 | 0x44, 0x54, // 174 179 | 0x44, 0x55, // 175 180 | 0x45, 0x0, // 176 181 | 0x45, 0x1, // 177 182 | 0x45, 0x4, // 178 183 | 0x45, 0x5, // 179 184 | 0x45, 0x10, // 180 185 | 0x45, 0x11, // 181 186 | 0x45, 0x14, // 182 187 | 0x45, 0x15, // 183 188 | 0x45, 0x40, // 184 189 | 0x45, 0x41, // 185 190 | 0x45, 0x44, // 186 191 | 0x45, 0x45, // 187 192 | 0x45, 0x50, // 188 193 | 0x45, 0x51, // 189 194 | 0x45, 0x54, // 190 195 | 0x45, 0x55, // 191 196 | 0x50, 0x0, // 192 197 | 0x50, 0x1, // 193 198 | 0x50, 0x4, // 194 199 | 0x50, 0x5, // 195 200 | 0x50, 0x10, // 196 201 | 0x50, 0x11, // 197 202 | 0x50, 0x14, // 198 203 | 0x50, 0x15, // 199 204 | 0x50, 0x40, // 200 205 | 0x50, 0x41, // 201 206 | 0x50, 0x44, // 202 207 | 0x50, 0x45, // 203 208 | 0x50, 0x50, // 204 209 | 0x50, 0x51, // 205 210 | 0x50, 0x54, // 206 211 | 0x50, 0x55, // 207 212 | 0x51, 0x0, // 208 213 | 0x51, 0x1, // 209 214 | 0x51, 0x4, // 210 215 | 0x51, 0x5, // 211 216 | 0x51, 0x10, // 212 217 | 0x51, 0x11, // 213 218 | 0x51, 0x14, // 214 219 | 0x51, 0x15, // 215 220 | 0x51, 0x40, // 216 221 | 0x51, 0x41, // 217 222 | 0x51, 0x44, // 218 223 | 0x51, 0x45, // 219 224 | 0x51, 0x50, // 220 225 | 0x51, 0x51, // 221 226 | 0x51, 0x54, // 222 227 | 0x51, 0x55, // 223 228 | 0x54, 0x0, // 224 229 | 0x54, 0x1, // 225 230 | 0x54, 0x4, // 226 231 | 0x54, 0x5, // 227 232 | 0x54, 0x10, // 228 233 | 0x54, 0x11, // 229 234 | 0x54, 0x14, // 230 235 | 0x54, 0x15, // 231 236 | 0x54, 0x40, // 232 237 | 0x54, 0x41, // 233 238 | 0x54, 0x44, // 234 239 | 0x54, 0x45, // 235 240 | 0x54, 0x50, // 236 241 | 0x54, 0x51, // 237 242 | 0x54, 0x54, // 238 243 | 0x54, 0x55, // 239 244 | 0x55, 0x0, // 240 245 | 0x55, 0x1, // 241 246 | 0x55, 0x4, // 242 247 | 0x55, 0x5, // 243 248 | 0x55, 0x10, // 244 249 | 0x55, 0x11, // 245 250 | 0x55, 0x14, // 246 251 | 0x55, 0x15, // 247 252 | 0x55, 0x40, // 248 253 | 0x55, 0x41, // 249 254 | 0x55, 0x44, // 250 255 | 0x55, 0x45, // 251 256 | 0x55, 0x50, // 252 257 | 0x55, 0x51, // 253 258 | 0x55, 0x54, // 254 259 | 0x55, 0x55, // 255 260 | }; 261 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/screen_capture.py: -------------------------------------------------------------------------------- 1 | 2 | from utils import * 3 | import mss 4 | import os, sys 5 | import pyautogui 6 | from PIL import Image, ImageEnhance, ImageOps 7 | 8 | import time 9 | import subprocess 10 | import io, struct 11 | from random import randint 12 | import numpy as np 13 | import threading, cv2 14 | from draw_cursor import generate_cursor, draw_cursor, draw_cursor_1bpp, paste_cursor, did_mouse_move 15 | from multiprocessing import shared_memory, resource_tracker, Value 16 | from collections import namedtuple 17 | 18 | 19 | work_dir = f'{working_dir}' 20 | if os.path.isdir(work_dir): 21 | os.chdir(work_dir) 22 | 23 | PID_list = [] 24 | pid0 = os.getpid() 25 | PID_list.append(pid0) 26 | 27 | 28 | for u in range(nb_displays-1): 29 | if ctx.a.disable_logging: 30 | P = subprocess.Popen([f'python', 'screen_capture.py', f'{sys.argv[u+2]}', '-silent', 'child']) 31 | else: 32 | P = subprocess.Popen([f'python3', 'screen_capture.py', sys.argv[u+2], 'child']) 33 | PID_list.append(P.pid) 34 | time.sleep(0.5) 35 | 36 | 37 | if ctx.a.child_process == 0: 38 | 39 | if pipe_output and ctx.a.start_cpp_process : 40 | ctx.shared_buffer[0:100] = bytearray(100) 41 | if windows: 42 | binary = "process_capture.exe" 43 | elif linux: 44 | binary = "process_capture" 45 | for x in range(nb_displays): 46 | time.sleep(0.5) 47 | 48 | R = subprocess.Popen([f'{working_dir}/{binary}', 49 | f'{display_list[x].ip_address}', 50 | f'{display_list[x].id}', 51 | f'{display_list[x].width}', 52 | f'{display_list[x].height}', 53 | f'{display_list[x].refresh_every_x_frames}', 54 | f'{display_list[x].framebuffer_cycles}', 55 | f'{display_list[x].rmt_high_time}', 56 | f'{display_list[x].enable_skipping}', 57 | f'{display_list[x].epd_skip_threshold}', 58 | f'{display_list[x].esp32_multithread}', 59 | # f'{display_list[x].epd_skip_mouse_only}', 60 | f'{display_list[x].framebuffer_cycles_2}', 61 | f'{display_list[x].framebuffer_cycles_2_threshold}', 62 | f'{modes[display_list[x].mode]}', 63 | f'{display_list[x].selective_compression}', 64 | f'{display_list[x].nb_chunks}', 65 | f'{display_list[x].nb_draws}', 66 | f'{display_list[x].draw_white_first}', 67 | f'{modes[display_list[x].mode]}', 68 | 69 | f'{display_list[x].do_full_refresh}', 70 | 71 | f'{display_list[x].a.disable_logging}', 72 | f'{ctx.wifi_on}']) 73 | 74 | ctx.has_childs = 1 75 | 76 | PID_list.append(R.pid) 77 | else: 78 | pid1 = None 79 | 80 | 81 | if ctx.a.child_process == 0: 82 | thread1 = threading.Thread(target=check_key_presses, args=(PID_list, ctx.offsets)) 83 | thread1.start() 84 | 85 | def main_task(ctx): 86 | if ctx.a.child_process == 0: 87 | w_shm(ctx.offsets.mode, modes.get(ctx.mode), 'a') 88 | w_shm(ctx.offsets.selective_invert, ctx.selective_invert, 'a') 89 | w_shm(ctx.offsets.settings_changed, 2, 'a') 90 | 91 | if ctx.invert > 1: 92 | w_shm(ctx.offsets.invert_threshold, ctx.invert, 'a') 93 | w_shm(ctx.offsets.invert, ctx.invert, 'a') 94 | 95 | w_shm(ctx.offsets.fb1_rmt, ctx.draw_rmt_times[0], 'a') 96 | w_shm(ctx.offsets.fb2_rmt, ctx.draw_rmt_times[1], 'a') 97 | w_shm(ctx.offsets.invert_draw_times, ctx.invert_draw_times, 'a') 98 | 99 | if pipe_output: 100 | fd1, fd0 = open_pipes(ctx) 101 | 102 | dith.alloc_memory_() 103 | 104 | n = 0 105 | with mss.mss() as sct: 106 | capture_list = [sct.grab(ctx.monitor).raw, sct.grab(ctx.monitor).raw] 107 | while 1: 108 | t0 = time.time() 109 | 110 | if ctx.switcher == 0: 111 | ctx.switcher = 1; 112 | else: 113 | ctx.switcher = 0; 114 | 115 | sct_img = sct.grab(ctx.monitor) #capture screen 116 | 117 | capture_list[ctx.switcher] = sct_img.raw 118 | 119 | screen_changed = check_for_difference_esp_fun(capture_list, True) 120 | mouse_moved = did_mouse_move(ctx) 121 | 122 | if mouse_moved: 123 | pass 124 | elif screen_changed == 0 and r_shm(ctx.offsets.settings_changed, 'i') == 0: 125 | time.sleep(ctx.sleep_time/1000) 126 | check_and_exit(fd0,fd1) 127 | #print_settings() 128 | continue 129 | 130 | if pipe_output: 131 | if linux: ready = os.read(fd1, 1) 132 | elif windows: ret2 = win32file.ReadFile(fd1, 1) 133 | 134 | image_file = Image.frombytes('RGB', (ctx.width, ctx.height), sct_img.rgb) 135 | # image_file.save("correct_colors.png") 136 | 137 | # image_file = Image.frombytes("RGB", sct_img.size, sct_img.bgra, "raw", "BGRX") # Image.frombytes('RGB', (ctx.width, ctx.height), rgb) 138 | # image_file.save("wrong_colors.png") 139 | 140 | if ctx.pipe_bit_depth == 8: 141 | ctx.mouse_moved = paste_cursor(ctx, image_file) 142 | # if ctx.mouse_moved: 143 | # print("8 moved") 144 | # else: print("not") 145 | 146 | mode = r_shm(ctx.offsets.mode, 'i') 147 | ctx.mode_code = mode 148 | 149 | if mode == 10 or ctx.draw_white_first: ctx.pipe_bit_depth = 8 150 | else: ctx.pipe_bit_depth = 1 151 | if mode == 9: #PIL dithering 152 | 153 | image_file = convert_to_greyscale_and_enhance(image_file, ctx) 154 | 155 | image_file = image_file.convert('1') 156 | 157 | elif mode == 0: #Monochrome 158 | 159 | 160 | image_file = convert_to_greyscale_and_enhance(image_file, ctx) 161 | th = ctx.grey_monochrome_threshold+r_shm(ctx.offsets.grey_to_monochrome_threshold, 'i') 162 | 163 | def fn(x): return 255 if x > th else 0 164 | 165 | image_file = image_file.point(fn, mode='1') 166 | 167 | elif mode > 0 and mode < 9: #other dithering 168 | image_file = convert_to_greyscale_and_enhance(image_file, ctx) 169 | 170 | mode = get_mode(modes, r_shm(ctx.offsets.mode, 'i')) 171 | 172 | 173 | if dith.apply(ctx.np_arr, mode) == -1: 174 | # print("error dither type") 175 | byte_frag = pipe_output_f(raw_data, None, ctx.mouse_moved, fd1, fd0) # 1bpp->raw_files[0] 176 | continue 177 | 178 | image_file = Image.frombytes('RGB', sct_img.size, ctx.np_arr) 179 | #t0 = t() 180 | 181 | image_file = image_file.convert('L') 182 | 183 | invert = r_shm(ctx.offsets.invert, 'i') 184 | 185 | if invert > 0: 186 | if invert == 1: image_file = ImageOps.invert(image_file) 187 | else: image_file = smart_invert(image_file) 188 | 189 | 190 | def fn(x): return x 191 | 192 | image_file = image_file.point(fn, mode='1') 193 | 194 | #print(t()-t0) 195 | 196 | elif mode == 10: # 4 shades grayscale mode 197 | 198 | image_file = convert_to_greyscale_and_enhance(image_file, ctx) 199 | 200 | # dith.quantize_(np_arr, np_arr, ctx.width*ctx.height) 201 | #image_file = Image.frombytes('L', sct_img.size, np_arr) 202 | 203 | else: 204 | print("error?") 205 | 206 | update_rmt_times(ctx, image_file) 207 | 208 | if mode != 10 and not ctx.draw_white_first: 209 | image_file = image_file.transpose(Image.FLIP_TOP_BOTTOM) #flip the image so that the first bytes contain the pixel data of the first lines 210 | if ctx.rotation != 0: 211 | image_file = image_file.rotate(ctx.rotation, expand=True) 212 | 213 | if enable_raw_output: 214 | raw_data = get_raw_pixels( 215 | image_file, raw_output_file, save_raw_file, ctx.switcher) #remove bitmap pad bytes 216 | if save_bmp: 217 | save_bmp_fun(image_file, mode) 218 | 219 | if pipe_output: # and dif_list_sum 220 | 221 | if ctx.pipe_bit_depth == 1: 222 | ctx.mouse_moved = draw_cursor_1bpp(display_list[0], raw_data[0]) 223 | # if ctx.mouse_moved: 224 | # print("1 moved") 225 | # else: print("not") 226 | pipe_output_f(raw_data, ctx.eight_bpp, ctx.mouse_moved, fd1, fd0) # 1bpp->raw_data[0] 227 | elif ctx.pipe_bit_depth == 8: 228 | pipe_output_f(raw_data, None, ctx.mouse_moved, fd1, fd0) 229 | 230 | if ctx.a.disable_logging == 0: 231 | took = int(((time.time() - t0)*1000)) 232 | 233 | print(f"Display ID: {ctx.id}, capture took {took}ms") 234 | 235 | time.sleep(ctx.sleep_time/1000) 236 | 237 | main_task(ctx) 238 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/include/64bitLUTc.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint64_t sixtyfour_bit_LUT[256] = { 3 | 0x0, // 0// 0 4 | 0x100000000000000, // 1// 72057594037927936 5 | 0x1000000000000, // 2// 281474976710656 6 | 0x101000000000000, // 3// 72339069014638592 7 | 0x10000000000, // 4// 1099511627776 8 | 0x100010000000000, // 5// 72058693549555712 9 | 0x1010000000000, // 6// 282574488338432 10 | 0x101010000000000, // 7// 72340168526266368 11 | 0x100000000, // 8// 4294967296 12 | 0x100000100000000, // 9// 72057598332895232 13 | 0x1000100000000, // 10// 281479271677952 14 | 0x101000100000000, // 11// 72339073309605888 15 | 0x10100000000, // 12// 1103806595072 16 | 0x100010100000000, // 13// 72058697844523008 17 | 0x1010100000000, // 14// 282578783305728 18 | 0x101010100000000, // 15// 72340172821233664 19 | 0x1000000, // 16// 16777216 20 | 0x100000001000000, // 17// 72057594054705152 21 | 0x1000001000000, // 18// 281474993487872 22 | 0x101000001000000, // 19// 72339069031415808 23 | 0x10001000000, // 20// 1099528404992 24 | 0x100010001000000, // 21// 72058693566332928 25 | 0x1010001000000, // 22// 282574505115648 26 | 0x101010001000000, // 23// 72340168543043584 27 | 0x101000000, // 24// 4311744512 28 | 0x100000101000000, // 25// 72057598349672448 29 | 0x1000101000000, // 26// 281479288455168 30 | 0x101000101000000, // 27// 72339073326383104 31 | 0x10101000000, // 28// 1103823372288 32 | 0x100010101000000, // 29// 72058697861300224 33 | 0x1010101000000, // 30// 282578800082944 34 | 0x101010101000000, // 31// 72340172838010880 35 | 0x10000, // 32// 65536 36 | 0x100000000010000, // 33// 72057594037993472 37 | 0x1000000010000, // 34// 281474976776192 38 | 0x101000000010000, // 35// 72339069014704128 39 | 0x10000010000, // 36// 1099511693312 40 | 0x100010000010000, // 37// 72058693549621248 41 | 0x1010000010000, // 38// 282574488403968 42 | 0x101010000010000, // 39// 72340168526331904 43 | 0x100010000, // 40// 4295032832 44 | 0x100000100010000, // 41// 72057598332960768 45 | 0x1000100010000, // 42// 281479271743488 46 | 0x101000100010000, // 43// 72339073309671424 47 | 0x10100010000, // 44// 1103806660608 48 | 0x100010100010000, // 45// 72058697844588544 49 | 0x1010100010000, // 46// 282578783371264 50 | 0x101010100010000, // 47// 72340172821299200 51 | 0x1010000, // 48// 16842752 52 | 0x100000001010000, // 49// 72057594054770688 53 | 0x1000001010000, // 50// 281474993553408 54 | 0x101000001010000, // 51// 72339069031481344 55 | 0x10001010000, // 52// 1099528470528 56 | 0x100010001010000, // 53// 72058693566398464 57 | 0x1010001010000, // 54// 282574505181184 58 | 0x101010001010000, // 55// 72340168543109120 59 | 0x101010000, // 56// 4311810048 60 | 0x100000101010000, // 57// 72057598349737984 61 | 0x1000101010000, // 58// 281479288520704 62 | 0x101000101010000, // 59// 72339073326448640 63 | 0x10101010000, // 60// 1103823437824 64 | 0x100010101010000, // 61// 72058697861365760 65 | 0x1010101010000, // 62// 282578800148480 66 | 0x101010101010000, // 63// 72340172838076416 67 | 0x100, // 64// 256 68 | 0x100000000000100, // 65// 72057594037928192 69 | 0x1000000000100, // 66// 281474976710912 70 | 0x101000000000100, // 67// 72339069014638848 71 | 0x10000000100, // 68// 1099511628032 72 | 0x100010000000100, // 69// 72058693549555968 73 | 0x1010000000100, // 70// 282574488338688 74 | 0x101010000000100, // 71// 72340168526266624 75 | 0x100000100, // 72// 4294967552 76 | 0x100000100000100, // 73// 72057598332895488 77 | 0x1000100000100, // 74// 281479271678208 78 | 0x101000100000100, // 75// 72339073309606144 79 | 0x10100000100, // 76// 1103806595328 80 | 0x100010100000100, // 77// 72058697844523264 81 | 0x1010100000100, // 78// 282578783305984 82 | 0x101010100000100, // 79// 72340172821233920 83 | 0x1000100, // 80// 16777472 84 | 0x100000001000100, // 81// 72057594054705408 85 | 0x1000001000100, // 82// 281474993488128 86 | 0x101000001000100, // 83// 72339069031416064 87 | 0x10001000100, // 84// 1099528405248 88 | 0x100010001000100, // 85// 72058693566333184 89 | 0x1010001000100, // 86// 282574505115904 90 | 0x101010001000100, // 87// 72340168543043840 91 | 0x101000100, // 88// 4311744768 92 | 0x100000101000100, // 89// 72057598349672704 93 | 0x1000101000100, // 90// 281479288455424 94 | 0x101000101000100, // 91// 72339073326383360 95 | 0x10101000100, // 92// 1103823372544 96 | 0x100010101000100, // 93// 72058697861300480 97 | 0x1010101000100, // 94// 282578800083200 98 | 0x101010101000100, // 95// 72340172838011136 99 | 0x10100, // 96// 65792 100 | 0x100000000010100, // 97// 72057594037993728 101 | 0x1000000010100, // 98// 281474976776448 102 | 0x101000000010100, // 99// 72339069014704384 103 | 0x10000010100, // 100// 1099511693568 104 | 0x100010000010100, // 101// 72058693549621504 105 | 0x1010000010100, // 102// 282574488404224 106 | 0x101010000010100, // 103// 72340168526332160 107 | 0x100010100, // 104// 4295033088 108 | 0x100000100010100, // 105// 72057598332961024 109 | 0x1000100010100, // 106// 281479271743744 110 | 0x101000100010100, // 107// 72339073309671680 111 | 0x10100010100, // 108// 1103806660864 112 | 0x100010100010100, // 109// 72058697844588800 113 | 0x1010100010100, // 110// 282578783371520 114 | 0x101010100010100, // 111// 72340172821299456 115 | 0x1010100, // 112// 16843008 116 | 0x100000001010100, // 113// 72057594054770944 117 | 0x1000001010100, // 114// 281474993553664 118 | 0x101000001010100, // 115// 72339069031481600 119 | 0x10001010100, // 116// 1099528470784 120 | 0x100010001010100, // 117// 72058693566398720 121 | 0x1010001010100, // 118// 282574505181440 122 | 0x101010001010100, // 119// 72340168543109376 123 | 0x101010100, // 120// 4311810304 124 | 0x100000101010100, // 121// 72057598349738240 125 | 0x1000101010100, // 122// 281479288520960 126 | 0x101000101010100, // 123// 72339073326448896 127 | 0x10101010100, // 124// 1103823438080 128 | 0x100010101010100, // 125// 72058697861366016 129 | 0x1010101010100, // 126// 282578800148736 130 | 0x101010101010100, // 127// 72340172838076672 131 | 0x1, // 128// 1 132 | 0x100000000000001, // 129// 72057594037927937 133 | 0x1000000000001, // 130// 281474976710657 134 | 0x101000000000001, // 131// 72339069014638593 135 | 0x10000000001, // 132// 1099511627777 136 | 0x100010000000001, // 133// 72058693549555713 137 | 0x1010000000001, // 134// 282574488338433 138 | 0x101010000000001, // 135// 72340168526266369 139 | 0x100000001, // 136// 4294967297 140 | 0x100000100000001, // 137// 72057598332895233 141 | 0x1000100000001, // 138// 281479271677953 142 | 0x101000100000001, // 139// 72339073309605889 143 | 0x10100000001, // 140// 1103806595073 144 | 0x100010100000001, // 141// 72058697844523009 145 | 0x1010100000001, // 142// 282578783305729 146 | 0x101010100000001, // 143// 72340172821233665 147 | 0x1000001, // 144// 16777217 148 | 0x100000001000001, // 145// 72057594054705153 149 | 0x1000001000001, // 146// 281474993487873 150 | 0x101000001000001, // 147// 72339069031415809 151 | 0x10001000001, // 148// 1099528404993 152 | 0x100010001000001, // 149// 72058693566332929 153 | 0x1010001000001, // 150// 282574505115649 154 | 0x101010001000001, // 151// 72340168543043585 155 | 0x101000001, // 152// 4311744513 156 | 0x100000101000001, // 153// 72057598349672449 157 | 0x1000101000001, // 154// 281479288455169 158 | 0x101000101000001, // 155// 72339073326383105 159 | 0x10101000001, // 156// 1103823372289 160 | 0x100010101000001, // 157// 72058697861300225 161 | 0x1010101000001, // 158// 282578800082945 162 | 0x101010101000001, // 159// 72340172838010881 163 | 0x10001, // 160// 65537 164 | 0x100000000010001, // 161// 72057594037993473 165 | 0x1000000010001, // 162// 281474976776193 166 | 0x101000000010001, // 163// 72339069014704129 167 | 0x10000010001, // 164// 1099511693313 168 | 0x100010000010001, // 165// 72058693549621249 169 | 0x1010000010001, // 166// 282574488403969 170 | 0x101010000010001, // 167// 72340168526331905 171 | 0x100010001, // 168// 4295032833 172 | 0x100000100010001, // 169// 72057598332960769 173 | 0x1000100010001, // 170// 281479271743489 174 | 0x101000100010001, // 171// 72339073309671425 175 | 0x10100010001, // 172// 1103806660609 176 | 0x100010100010001, // 173// 72058697844588545 177 | 0x1010100010001, // 174// 282578783371265 178 | 0x101010100010001, // 175// 72340172821299201 179 | 0x1010001, // 176// 16842753 180 | 0x100000001010001, // 177// 72057594054770689 181 | 0x1000001010001, // 178// 281474993553409 182 | 0x101000001010001, // 179// 72339069031481345 183 | 0x10001010001, // 180// 1099528470529 184 | 0x100010001010001, // 181// 72058693566398465 185 | 0x1010001010001, // 182// 282574505181185 186 | 0x101010001010001, // 183// 72340168543109121 187 | 0x101010001, // 184// 4311810049 188 | 0x100000101010001, // 185// 72057598349737985 189 | 0x1000101010001, // 186// 281479288520705 190 | 0x101000101010001, // 187// 72339073326448641 191 | 0x10101010001, // 188// 1103823437825 192 | 0x100010101010001, // 189// 72058697861365761 193 | 0x1010101010001, // 190// 282578800148481 194 | 0x101010101010001, // 191// 72340172838076417 195 | 0x101, // 192// 257 196 | 0x100000000000101, // 193// 72057594037928193 197 | 0x1000000000101, // 194// 281474976710913 198 | 0x101000000000101, // 195// 72339069014638849 199 | 0x10000000101, // 196// 1099511628033 200 | 0x100010000000101, // 197// 72058693549555969 201 | 0x1010000000101, // 198// 282574488338689 202 | 0x101010000000101, // 199// 72340168526266625 203 | 0x100000101, // 200// 4294967553 204 | 0x100000100000101, // 201// 72057598332895489 205 | 0x1000100000101, // 202// 281479271678209 206 | 0x101000100000101, // 203// 72339073309606145 207 | 0x10100000101, // 204// 1103806595329 208 | 0x100010100000101, // 205// 72058697844523265 209 | 0x1010100000101, // 206// 282578783305985 210 | 0x101010100000101, // 207// 72340172821233921 211 | 0x1000101, // 208// 16777473 212 | 0x100000001000101, // 209// 72057594054705409 213 | 0x1000001000101, // 210// 281474993488129 214 | 0x101000001000101, // 211// 72339069031416065 215 | 0x10001000101, // 212// 1099528405249 216 | 0x100010001000101, // 213// 72058693566333185 217 | 0x1010001000101, // 214// 282574505115905 218 | 0x101010001000101, // 215// 72340168543043841 219 | 0x101000101, // 216// 4311744769 220 | 0x100000101000101, // 217// 72057598349672705 221 | 0x1000101000101, // 218// 281479288455425 222 | 0x101000101000101, // 219// 72339073326383361 223 | 0x10101000101, // 220// 1103823372545 224 | 0x100010101000101, // 221// 72058697861300481 225 | 0x1010101000101, // 222// 282578800083201 226 | 0x101010101000101, // 223// 72340172838011137 227 | 0x10101, // 224// 65793 228 | 0x100000000010101, // 225// 72057594037993729 229 | 0x1000000010101, // 226// 281474976776449 230 | 0x101000000010101, // 227// 72339069014704385 231 | 0x10000010101, // 228// 1099511693569 232 | 0x100010000010101, // 229// 72058693549621505 233 | 0x1010000010101, // 230// 282574488404225 234 | 0x101010000010101, // 231// 72340168526332161 235 | 0x100010101, // 232// 4295033089 236 | 0x100000100010101, // 233// 72057598332961025 237 | 0x1000100010101, // 234// 281479271743745 238 | 0x101000100010101, // 235// 72339073309671681 239 | 0x10100010101, // 236// 1103806660865 240 | 0x100010100010101, // 237// 72058697844588801 241 | 0x1010100010101, // 238// 282578783371521 242 | 0x101010100010101, // 239// 72340172821299457 243 | 0x1010101, // 240// 16843009 244 | 0x100000001010101, // 241// 72057594054770945 245 | 0x1000001010101, // 242// 281474993553665 246 | 0x101000001010101, // 243// 72339069031481601 247 | 0x10001010101, // 244// 1099528470785 248 | 0x100010001010101, // 245// 72058693566398721 249 | 0x1010001010101, // 246// 282574505181441 250 | 0x101010001010101, // 247// 72340168543109377 251 | 0x101010101, // 248// 4311810305 252 | 0x100000101010101, // 249// 72057598349738241 253 | 0x1000101010101, // 250// 281479288520961 254 | 0x101000101010101, // 251// 72339073326448897 255 | 0x10101010101, // 252// 1103823438081 256 | 0x100010101010101, // 253// 72058697861366017 257 | 0x1010101010101, // 254// 282578800148737 258 | 0x101010101010101, // 255// 72340172838076673 259 | }; 260 | -------------------------------------------------------------------------------- /pc_monitor/main/pc_monitor.c: -------------------------------------------------------------------------------- 1 | #include "epd_driver.h" 2 | #include "pc_monitor.h" 3 | #include "i2s_data_bus.h" 4 | #include "rmt_pulse.h" 5 | 6 | //#include "display_ops.h" 7 | #include "ed097oc4.h" 8 | #include "esp_assert.h" 9 | #include "esp_heap_caps.h" 10 | #include "esp_types.h" 11 | #include "freertos/FreeRTOS.h" 12 | #include "freertos/task.h" 13 | #include "xtensa/core-macros.h" 14 | #include 15 | #include "esp_wifi.h" 16 | #include "esp_event.h" 17 | #include "esp_netif.h" 18 | 19 | #include "lwip/err.h" 20 | #include "lwip/sockets.h" 21 | #include "lwip/sys.h" 22 | #include 23 | 24 | #define EPD_LINE_BYTES EPD_WIDTH / 4 25 | #define MULTITASK 0 26 | 27 | extern uint8_t ready1[6]; 28 | uint8_t *current_chunk; 29 | //uint8_t *dma_buffer; 30 | 31 | //extern uint8_t *fc0, *fc1 , *fc2 , *fc3 , *fc4 , *fc5 , *fc6 , *fc7 , *fc8 , *fc9 ; 32 | 33 | uint8_t *get_current_chunk_ptr(int chunk_number) 34 | { 35 | switch (chunk_number) 36 | { 37 | case 0: 38 | return fc0; 39 | case 1: 40 | return fc1; 41 | case 2: 42 | return fc2; 43 | case 3: 44 | return fc3; 45 | case 4: 46 | return fc4; 47 | case 5: 48 | return fc5; 49 | case 6: 50 | return fc6; 51 | case 7: 52 | return fc7; 53 | case 8: 54 | return fc8; 55 | case 9: 56 | return fc9; 57 | } 58 | return fc0; 59 | } 60 | 61 | int IRAM_ATTR switch_framebuffer_n(int n) 62 | { 63 | if (n == 0) 64 | n = 1; 65 | else if (n == 1) 66 | n = 0; 67 | return n; 68 | } 69 | void IRAM_ATTR switch_framebuffer() 70 | { 71 | if (current_buffer == 0) 72 | current_buffer = 1; 73 | else if (current_buffer == 1) 74 | current_buffer = 0; 75 | return current_buffer; 76 | } 77 | int back_buffer() 78 | { 79 | int n = 0; 80 | if (current_buffer == 1) 81 | n = 0; 82 | else if (current_buffer == 0) 83 | n = 1; 84 | return n; 85 | } 86 | 87 | void IRAM_ATTR pc_monitor_feed_display_with_skip(int total_lines_changed) 88 | { 89 | int rmt_time; 90 | 91 | long time2 = xTaskGetTickCount(); 92 | int framebuffer_cycles_final; 93 | if (mouse_moved == 1 && total_lines_changed < framebuffer_cycles_2_threshold) 94 | framebuffer_cycles_final = framebuffer_cycles_2; 95 | else 96 | framebuffer_cycles_final = framebuffer_cycles; 97 | for (int i = 0; i < framebuffer_cycles_final; i++) 98 | { 99 | 100 | if (mode == 10 || draw_white_first == 1) 101 | rmt_time = draw_rmt_times[frame_counter]; 102 | else 103 | rmt_time = draw_rmt_times[i]; 104 | 105 | #if DEBUG_MSGs == 1 106 | printf("#framebuffer_cycle: %d, frame_counter: %d , rmt timing: %d , mode %d #\n", i, frame_counter, rmt_time, mode); 107 | #endif 108 | 109 | epd_start_frame(); 110 | 111 | for (int h = 0; h < nb_chunks; h++) 112 | { 113 | current_chunk = get_current_chunk_ptr(h); 114 | int offset = (h * nb_rows_per_chunk); 115 | if (enable_skipping == 1 && total_lines_changed < epd_skip_threshold) //only skip the rows that haven't changed if they are less in number than the specified threshold 116 | { 117 | for (int g = 0; g < nb_rows_per_chunk; g++) 118 | { 119 | switch (line_changed[offset + g]) 120 | { 121 | case 1: 122 | memcpy(epd_get_current_buffer(), current_chunk + (g * EPD_LINE_BYTES), EPD_LINE_BYTES); 123 | 124 | epd_output_row(rmt_time); 125 | break; 126 | 127 | case 0: 128 | epd_skip(); 129 | // memcpy(epd_get_current_buffer(), current_chunk + (g * EPD_LINE_BYTES), EPD_LINE_BYTES); 130 | // epd_output_row(rmt_high_time); 131 | break; 132 | } 133 | } 134 | } 135 | else 136 | { 137 | for (int g = 0; g < nb_rows_per_chunk; g++) 138 | { 139 | memcpy(epd_get_current_buffer(), current_chunk + (g * EPD_LINE_BYTES), EPD_LINE_BYTES); 140 | epd_output_row(rmt_time); 141 | } 142 | } 143 | } 144 | epd_end_frame(); 145 | 146 | // frame_counter++; 147 | } 148 | printf("Draw time: %lu\n", xTaskGetTickCount() - time2); 149 | } 150 | 151 | void IRAM_ATTR pc_monitor_feed_display(int total_lines_changed) 152 | { 153 | long time2 = xTaskGetTickCount(); 154 | int framebuffer_cycles_final; 155 | if (mouse_moved == 1 && total_lines_changed < framebuffer_cycles_2_threshold) 156 | framebuffer_cycles_final = framebuffer_cycles_2; 157 | else 158 | framebuffer_cycles_final = framebuffer_cycles; 159 | 160 | for (int i = 0; i < framebuffer_cycles_final; i++) 161 | { 162 | 163 | epd_start_frame(); 164 | 165 | for (int h = 0; h < nb_chunks; h++) 166 | { 167 | current_chunk = get_current_chunk_ptr(h); 168 | int offset = (h * nb_rows_per_chunk); 169 | 170 | for (int g = 0; g < nb_rows_per_chunk; g++) 171 | { 172 | memcpy(epd_get_current_buffer(), current_chunk + (g * EPD_LINE_BYTES), EPD_LINE_BYTES); 173 | epd_output_row(rmt_high_time); 174 | } 175 | } 176 | epd_end_frame(); 177 | 178 | // frame_counter++; 179 | } 180 | printf("draw time: %lu\n", xTaskGetTickCount() - time2); 181 | } 182 | 183 | void IRAM_ATTR pc_monitor_feed_display_multithreaded_v1() 184 | { 185 | printf("pc_monitor_feed_display_multithreaded \n"); 186 | rmt_high_time = 150; 187 | int sleep_time = 1; 188 | 189 | xSemaphoreTake(begin, 9999999); 190 | vTaskDelay(1000 / portTICK_PERIOD_MS); 191 | 192 | while (1) 193 | { 194 | 195 | renderer_chunk_counter = 0; 196 | while (renderer_chunk_counter == downloader_chunk_counter) 197 | { //vTaskDelay(sleep_time / portTICK_PERIOD_MS); 198 | }; 199 | long time2 = xTaskGetTickCount(); 200 | 201 | epd_start_frame(); 202 | 203 | for (int b = 0; b < nb_rows_per_chunk; b++) 204 | { 205 | memcpy(epd_get_current_buffer(), framebuffer_chunks[0] + (b * EPD_LINE_BYTES), EPD_LINE_BYTES); 206 | epd_output_row(rmt_high_time); 207 | // output_row(rmt_high_time, 1, framebuffer_chunks[0] + b * EPD_LINE_BYTES); 208 | } 209 | renderer_chunk_counter++; 210 | // printf("rend cc %d \n", downloader_chunk_counter); 211 | 212 | for (int h = 0; h < nb_chunks - 1; h++) 213 | { 214 | 215 | while (renderer_chunk_counter == downloader_chunk_counter) 216 | { //vTaskDelay(sleep_time / portTICK_PERIOD_MS); 217 | }; 218 | 219 | for (int g = 0; g < nb_rows_per_chunk * (nb_chunks - 1); g++) 220 | epd_skip(); 221 | for (int g = 0; g < nb_rows_per_chunk; g++) 222 | { 223 | memcpy(epd_get_current_buffer(), framebuffer_chunks[h + 1] + (g * EPD_LINE_BYTES), EPD_LINE_BYTES); 224 | epd_output_row(rmt_high_time); 225 | // output_row(rmt_high_time, 1, framebuffer_chunks[h + 1] + (g * EPD_LINE_BYTES)); 226 | } 227 | 228 | renderer_chunk_counter++; 229 | // printf("rend cc %d \n", downloader_chunk_counter); 230 | } 231 | epd_end_frame(); 232 | 233 | renderer_chunk_counter = 0; 234 | 235 | int time3 = (xTaskGetTickCount() - time2); 236 | 237 | printf("draw time: %d\n", time3); 238 | // printf("r: %d lp = %d \n", time3, frame_counter); 239 | 240 | // frame_counter++; 241 | } 242 | } 243 | 244 | void IRAM_ATTR pc_monitor_feed_display_multithreaded_v1_one_chunk() 245 | { 246 | printf("pc_monitor_feed_display_multithreaded_v1_onechunk \n"); 247 | int rmt_time; 248 | 249 | renderer_frame_counter = 0; 250 | 251 | xSemaphoreTake(begin, 9999999); 252 | vTaskDelay(1000 / portTICK_PERIOD_MS); 253 | 254 | while (1) 255 | { 256 | #if DEBUG_MSGs == 2 257 | printf("r0 render loop \n"); 258 | #endif 259 | renderer_chunk_counter = 0; 260 | uint8_t *ptr = NULL; 261 | uint8_t *ptr_b = NULL; 262 | 263 | while (renderer_frame_counter == downloader_frame_counter || clearing ) 264 | { 265 | if (stop == 1) 266 | { 267 | printf("terminating render task \n"); 268 | vTaskDelete(NULL); 269 | } 270 | vTaskDelay(1 / portTICK_PERIOD_MS); 271 | }; 272 | 273 | tr0 = xTaskGetTickCount(); 274 | 275 | ptr = get_current_chunk_ptr(current_buffer); 276 | 277 | for (int y = 0; y < framebuffer_cycles; y++) 278 | { 279 | renderer_busy = 1; 280 | if (mode == 10) 281 | rmt_time = draw_rmt_times[frame_counter]; 282 | else 283 | rmt_time = draw_rmt_times[y]; 284 | 285 | epd_start_frame(); 286 | 287 | for (int b = 0; b < nb_rows_per_chunk; b++) 288 | { 289 | memcpy(epd_get_current_buffer(), ptr + (b * EPD_LINE_BYTES), EPD_LINE_BYTES); 290 | epd_output_row(rmt_time); 291 | // output_row(rmt_high_time, 1, framebuffer_chunks[0] + b * EPD_LINE_BYTES); 292 | } 293 | } 294 | epd_end_frame(); 295 | switch_framebuffer(); 296 | renderer_busy = 0; 297 | 298 | tr1 = xTaskGetTickCount(); 299 | 300 | #if DEBUG_MSGs == 2 301 | printf("r2 fc %lu \n", renderer_frame_counter); 302 | printf("r3 draw time: %lu | tr1, tr0: %lu, %lu \n", tr1 - tr0, tr0, tr1); 303 | #else 304 | printf("draw time: %lu\n", tr1 - tr0); 305 | #endif 306 | renderer_frame_counter++; 307 | 308 | // printf("r: %d lp = %d \n", time3, frame_counter); 309 | if (renderer_frame_counter == 4294967290) 310 | renderer_frame_counter = 0; 311 | 312 | // frame_counter++; 313 | } 314 | } 315 | 316 | // void IRAM_ATTR signal_245_fifo(const int sock) 317 | // { 318 | // send(sock, "ready0", 6, 0); 319 | 320 | // recv(sock, ready1, 6, 0); 321 | // epd_start_frame(); 322 | 323 | // for (int h = 0; h > 100; h++) 324 | // { 325 | // send(sock, "ready0", 6, 0); 326 | // vTaskDelay(100 / portTICK_PERIOD_MS); 327 | // // output_row_245(150); 328 | // } 329 | // epd_end_frame(); 330 | // } 331 | 332 | void IRAM_ATTR pc_monitor_feed_display_multithreaded_v2() 333 | { 334 | printf("pc_monitor_feed_display_multithreaded \n"); 335 | rmt_high_time = 150; 336 | 337 | xSemaphoreTake(begin, 9999999); 338 | vTaskDelay(1000 / portTICK_PERIOD_MS); 339 | 340 | while (1) 341 | { 342 | 343 | renderer_chunk_counter = 0; 344 | while (renderer_chunk_counter == downloader_chunk_counter) 345 | { //vTaskDelay(sleep_time / portTICK_PERIOD_MS); 346 | }; 347 | long time2 = xTaskGetTickCount(); 348 | for (int j = 0; j < 3; j++) 349 | { 350 | epd_start_frame(); 351 | 352 | for (int b = 0; b < nb_rows_per_chunk; b++) 353 | { 354 | memcpy(epd_get_current_buffer(), framebuffer_chunks[0] + (b * EPD_LINE_BYTES), EPD_LINE_BYTES); 355 | epd_output_row(rmt_high_time); 356 | // output_row(rmt_high_time, 1, framebuffer_chunks[0] + b * EPD_LINE_BYTES); 357 | } 358 | epd_end_frame(); 359 | } 360 | renderer_chunk_counter++; 361 | // printf("rend cc %d \n", downloader_chunk_counter); 362 | 363 | for (int h = 0; h < nb_chunks - 1; h++) 364 | { 365 | 366 | while (renderer_chunk_counter == downloader_chunk_counter) 367 | { //vTaskDelay(sleep_time / portTICK_PERIOD_MS); 368 | }; 369 | for (int j = 0; j < 3; j++) 370 | { 371 | epd_start_frame(); 372 | 373 | for (int g = 0; g < nb_rows_per_chunk * (h + 1); g++) 374 | epd_skip(); 375 | for (int g = 0; g < nb_rows_per_chunk; g++) 376 | { 377 | memcpy(epd_get_current_buffer(), framebuffer_chunks[h + 1] + (g * EPD_LINE_BYTES), EPD_LINE_BYTES); 378 | epd_output_row(rmt_high_time); 379 | // output_row(rmt_high_time, 1, framebuffer_chunks[h + 1] + (g * EPD_LINE_BYTES)); 380 | } 381 | epd_end_frame(); 382 | } 383 | renderer_chunk_counter++; 384 | // printf("rend cc %d \n", downloader_chunk_counter); 385 | } 386 | 387 | renderer_chunk_counter = 0; 388 | 389 | int time3 = (xTaskGetTickCount() - time2); 390 | 391 | printf("draw time: %d\n", time3); 392 | // printf("r: %d lp = %d \n", time3, frame_counter); 393 | 394 | // frame_counter++; 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Epdiy Eink Pc Monitor 4 | #### Description: 5 | This repository contains the source code for a client a host application that allow a Epdiy eink display controller board to mirror the image of a monitor, allowing the eink display to be used as a pc monitor. 6 | Video: 7 | 8 | [![IMAGE ALT TEXT](http://img.youtube.com/vi/bzk12na2mWg/0.jpg)](http://www.youtube.com/watch?v=bzk12na2mWg "Video Title") 9 | 10 | ------------ 11 | 12 | 13 | #### Supported platforms: 14 | It has been tested on Ubuntu 20.04 and Windows 10 version 20H2 15 | 16 | ------------ 17 | 18 | 19 | #### How it works: 20 | The Python module mss is used to capture the screen and Pillow is used to convert the capture to black and white. The black and white image is piped to a C++ program that generates the eink framebuffer, compresses it with RLE compression and sends it wirelessly to the Epdiy board using TCP protocol.The ESP32 application for the board receives the compressed framebuffer, extracts it and uses the Epdiy driver to write it to the display. 21 | 22 | ------------ 23 | 24 | 25 | #### Notes on development: 26 | - Framerate and draw time depend on settings, on how much screen has changed and speed of wifi, with default settings full frame draw time including the wifi transfer can be between 60ms to 160ms. 27 | - Ghosting can be a problem in particular when drawing white letters on black background. Is recommended to use black letters on white background. 28 | - It is necessary to have at least 1 real monitor plugged in to a port of the pc, and the application will mirror an area of that monitor, a dummy hdmi plug could be used to bypass this limitation. 29 | - Because the mss module does not capture the mouse cursor, it is added manually 30 | 31 | ------------ 32 | #### Dependencies: 33 | This project requires a Epdiy controller board, to get one is necessary to order a pcb from a pcb manufacturers and solder the components. Some pcb manufacturers also offer to solder them. For more info go: https://github.com/vroland/epdiy 34 | 35 | Required Python modules: 36 | ```bash 37 | pip install mss 38 | pip install pyautogui 39 | pip install numpy 40 | ``` 41 | For Windows 10: 42 | ```bash 43 | pip install pywin32 44 | 45 | ``` 46 | 47 | 48 | #### Installation: 49 | 50 | 1) Once you have the board, flash the demo example on the example folder of the Epdiy repository to verify that it is working correctly. 51 | 52 | 3) Get the repositories: 53 | On linux: 54 | ```bash 55 | cd 56 | git clone -b v5 https://github.com/vroland/epdiy 57 | 58 | git clone https://github.com/amadeok/Epdiy-Eink-PC-monitor 59 | cp -R ~/Epdiy-Eink-PC-monitor/pc_monitor ~/epdiy/examples 60 | 61 | ``` 62 | On windows' command prompt: 63 | ```bash 64 | cd c:\ 65 | git clone -b v5 https://github.com/vroland/epdiy 66 | git clone https://github.com/amadeok/Epdiy-Eink-PC-monitor 67 | Xcopy /E /I C:\Epdiy-Eink-PC-monitor C:\epdiy\examples 68 | 69 | ``` 70 | 71 | 72 | 4) The computer and board should connect to the same wifi network. Go to *~/epdiy/examples/pc-monitor/main/*, open *main.c* , go to line 31 and insert the SSID of the wifi network you are going to use in the *WIFI_SSID* variable and the password of the wifi in the *WIFI_PASS* variable. 73 | 74 | 5) Build and flash the client application for the board: 75 | On linux: 76 | ```bash 77 | cd ~/epdiy/examples/pc_monitor 78 | idf.py build && idf.py flash -b 921600 && idf.py monitor 79 | ``` 80 | On windows: 81 | ```bash 82 | cd c:\epdiy\examples\pc_monitor 83 | idf.py build && idf.py flash -b 921600 && idf.py monitor 84 | ``` 85 | 86 | 6)After the flashing finishes, the terminal will start displaying some messages, look for the message: 87 | ```bash 88 | IP Address: xxx.xxx.xxx.xxx 89 | ``` 90 | 91 | (where xxx.xxx.xxx.xxx is an ip address) 92 | 93 | Note it down. 94 | 95 | 7) Add the ip address to a display configuration file: 96 | 97 | Open *example_display.conf* in *~/epdiy/examples/pc_monitor/pc_host_app/* with a text editor and change the IP address in the first line to the one you noted down before, and, if needed, change the values *width* and *height* to the witdh and height resolution of your display 98 | 99 | 100 | 8) Build the pc-host application and the C shared library: 101 | On a new terminal: 102 | On Linux: 103 | ```bash 104 | cd ~/epdiy/examples/pc_monitor/pc_host_app/ 105 | g++ main.cpp generate_eink_framebuffer.cpp rle_compression.cpp utils.cpp -o process_capture -I include 106 | g++ dither_.cpp invert_.cpp -o dither_.so -shared -fPIC 107 | ``` 108 | 109 | On Windows(requires MinGW installed and on PATH): 110 | ```bash 111 | cd C:\epdiy\examples\pc_monitor\pc_host_app\ 112 | 113 | g++ main.cpp generate_eink_framebuffer.cpp rle_compression.cpp utils.cpp -static -o process_capture.exe -I "C:\Epdiy-Eink-PC-monitor\pc_monitor\pc_host_app\include" -lws2_32 114 | g++ dither_.cpp invert_.cpp -o dither_.dll -shared -fPIC 115 | 116 | ``` 117 | 9) Now the pc is ready to start the mirroring. If the board is ready to start mirroring it will display a message saying "Socket listening ". 118 | To start the mirroring execute: 119 | ```bash 120 | python screen_capture.py example_display.conf 121 | ``` 122 | If you want to hide the terminal output run it like this: 123 | ```bash 124 | python screen_capture.py example_display.conf -silent 125 | ``` 126 | Important: always exit the pc host application by pressing the letter **q** 127 | 128 | 129 | 130 | #### Creating configuration files and settings: 131 | The pc host application reads the settings from a *.conf* file passed as an argument when running *screen_capture.py*. Inside a display configuration file there are the following settings: 132 | 133 | - *ip_address* : the IP address of the ESP32 module to connect to 134 | 135 | - *id*: Number to identify each display. Has to be different for each display 136 | 137 | - *width* : the resolution width of the display 138 | 139 | - *height*: the resolution height of the display 140 | 141 | - *x_offset* and *y_offset* 142 | They set how many pixels away for the top left corner the screen will be captured. ie. if the resolution is 1200x825, setting both to zero will capture 1200x825 pixels from the extreme top left corner of the screen. setting them to 100 and 50 will capture 1200x825 pixels 100 pixels to the right and 50 pixels from the top left corner of the screen. If you have 2 monitors of 1920x1080 pixels and you want to capture from the top left corner of the monitor on the right, *x_offset* should be 1920 and *y_offset* should be 0. More info on mss: https://python-mss.readthedocs.io/examples.html 143 | 144 | - *rotation*: can be set to 180 to if using a display upside down 145 | 146 | - *grey_monochrome_threshold*: 147 | When converting the capture from 256 greyscale shades to black and white monochrome a threshold is used to determine which shades will become plain black and which plain white. The default is 200 and it improves drawing black letters on white background. Minimum value is 0 and maximum is 255 148 | 149 | - *sleep_time*: Amount of time in miliseconds to pause the program after capturing a frame. Can be used to reduce the framerate artificially. Set it to 50 or less for fast refresh rate 150 | 151 | - *refresh_every_x_frames*: Refreshes the display every the specified number of frame draws. A frame draw is not counted if less than 85 lines have changed (such as when moving the mouse) 152 | 153 | - *framebuffer_cycles*: Defines the number of times to write the current eink framebuffer to the display. Because of the way that eink displays work, writing the same framebuffer to the display has the effect of getting deeper blacks and whites. Increases draw time. 154 | 155 | - *rmt_high_time*: Defines the high tick time of the CKV signal. A higher value makes blacks blacker and whites whiter. Increases draw time. It is defined in *rmt_pulse.h* 156 | 157 | - *mode*: there are 10 modes: *monochrome*, *PIL_dither*(Floyd-Steinberg dither) and 8 more dithering modes: *Bayer16*, *Bayer8* *Bayer4*, *Bayer3*, *Bayer2*, *FS*, *SierraLite*, *Sierra* which are defined in *Dither.h* 158 | 159 | - *invert*: if set to -1 invert is always off, if set to 0 it's always on, if it's greater than zero it acts as a threshold for *smart invert*, which activates. *smart invert* inverts the whole image only when most of it is black, otherwise it doesn't invert at all. maximum value is 255 160 | 161 | - *selective_invert* if set to 1 detects black regions on the screen and inverts them (can create some artifacts). *smart invert* and *selective_invert* can help improve overall usability for text based office work by forcing almost always black letters on white background 162 | 163 | - *color, contrast, brightness, sharpness*: the Pillow module has the option to apply these enhancements to the capture. They are applied before converting the image to 1 bit per pixel. more info: https://pillow.readthedocs.io/en/stable/reference/ImageEnhance.html 164 | (experimental) 165 | 166 | - *draw_white_first* makes the program draw to the display first the white pixels of the image and then the black ones 167 | 168 | - *enhance_before_greyscale*: choose wether to apply the Pillow enhancements before or after converting to 8bpp greyscale 169 | - *do_full_refresh*: calls epd_clear() to refresh screen 170 | 171 | Advanced settings: 172 | - *selective_compression* sets a maximum percentage of eficiency for the rle compression. if that percentage is not met, the framebuffer will be sent without compression to the board 173 | 174 | - *esp32_multithread* starts downloading next frame while the current is being drawn 175 | - 176 | - *enable_skipping*: makes the driver call *epd_skip()* instead of *epd_output_row()* with an empty buffer. It can reduce draw time significantly but may introduce some artifacts. To avoid the artifacts but mantain low draw time when moving the cursor *epd_skip_threshold* can be set to 75. 177 | 178 | - *epd_skip_threshold*: if less than *epd_skip_threshold* rows have changed skipping is enabled for the current frame draw. 179 | 180 | - *framebuffer_cycles_2* and *framebuffer_cycles_2_threshold*: If less than *framebuffer_cycles_2_threshold* number of rows have changed, the current framebuffer will be written *framebuffer_cycles_2* times instead of the value set by *framebuffer_cycles*. Can be used to reduce the draw time when just a few lines have changed such as when moving the cursor. This is only active is the mouse is moving 181 | 182 | #### Hotkeys: 183 | It is possible to change the following settings while the application is running by pressing their hotkey on the terminal: 184 | -*1* decreases *color* by 0.1 185 | 186 | -*2* increases *color* by 0.1 187 | 188 | -*3* decreases *contrast* by 0.1 189 | 190 | -*4* increases *contrast* by 0.1 191 | 192 | -*5* decreases *brightness* by 0.1 193 | 194 | -*6* increases *brightness* by 0.1 195 | 196 | -*7* decreases *sharpness* by 0.1 197 | 198 | -*8* increases *sharpness* by 0.1 199 | 200 | -*9* increases *grey_monochrome_threshold* by 10 201 | 202 | -*0* decreases *grey_monochrome_threshold* by 10 203 | 204 | -*b* toggles *enhance_before_greyscale* 205 | 206 | -*i* toggles *invert* 207 | 208 | -*u* increases *smart invert* threshold by 10 and activates it 209 | 210 | -*y* decreases *smart invert* threshold by 10 and activates it 211 | 212 | -*s* toggles *selective_invert* 213 | 214 | -*d* switches to the mode specified in the configuration file 215 | 216 | -*m* switches to monochrome mode 217 | 218 | 219 | #### Using multiple displays at the same time 220 | 221 | The application supports using multiple displays at the same time. For each display it is required to create a configuration file. 222 | Once the configuration files are created run them as 223 | 224 | ```bash 225 | python3 screen_capture.py display0.conf 226 | ``` 227 | ```bash 228 | python3 screen_capture.py display1.conf 229 | ``` 230 | ```bash 231 | python3 screen_capture.py display2.conf 232 | ``` 233 | Each from a different terminal. If you want that a hotkey press has an effect on all the running instances, append *-common* to each command: 234 | ```bash 235 | python3 screen_capture.py display0.conf -common 236 | ``` 237 | 238 | ------------ 239 | #### Support for other displays 240 | 241 | - The project has been tested using a board revision 4 and a ED097TC2 display. Check for the list of other displays supported by the Epdiy board on https://github.com/vroland/epdiy . Those displays should work but are untested. If the display you want to try has the same resolution of the ED097TC2, that is 1200x825, it should be enough to select the correct display type in the menuconfig Epdiy section. 242 | If the display you want to try has a different resolution, other than selecting it in the menuconfig before flashing the board, you should also specify its resolution on its display configuration file. 243 | ------------ 244 | 245 | 246 | #### To do list: 247 | - Organize the code better 248 | - Improve ghosting 249 | - Optimize data transfer between pc and board 250 | 251 | 252 | 253 | 254 | 255 | 256 | -------------------------------------------------------------------------------- /pc_monitor/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Epdiy Eink Pc Monitor 4 | (work in progress) 5 | #### Description: 6 | This repository contains the source code for a client a host application that allow a Epdiy eink display controller board to mirror the image of a monitor, allowing the eink display to be used as a pc monitor. 7 | Video: 8 | 9 | [![IMAGE ALT TEXT](http://img.youtube.com/vi/bzk12na2mWg/0.jpg)](http://www.youtube.com/watch?v=bzk12na2mWg "Video Title") 10 | 11 | ------------ 12 | 13 | 14 | #### Supported platforms: 15 | It has been tested on Ubuntu 20.04 and Windows 10 version 20H2 16 | 17 | ------------ 18 | 19 | 20 | #### How it works: 21 | The Python module mss is used to capture the screen and Pillow is used to convert the capture to black and white. The black and white image is piped to a C++ program that generates the eink framebuffer, compresses it with RLE compression and sends it wirelessly to the Epdiy board using TCP protocol.The ESP32 application for the board receives the compressed framebuffer, extracts it and uses the Epdiy driver to write it to the display. 22 | 23 | ------------ 24 | 25 | 26 | #### Notes on development: 27 | - Framerate and draw time depend on settings, on how much screen has changed and speed of wifi, with default settings full frame draw time including the wifi transfer can be between 60ms to 160ms. 28 | - Ghosting can be a problem in particular when drawing white letters on black background. Is recommended to use black letters on white background. 29 | - It is necessary to have at least 1 real monitor plugged in to a port of the pc, and the application will mirror an area of that monitor, a dummy hdmi plug could be used to bypass this limitation. 30 | - Because the mss module does not capture the mouse cursor, it is added manually 31 | 32 | ------------ 33 | #### Dependencies: 34 | This project requires a Epdiy controller board, to get one is necessary to order a pcb from a pcb manufacturers and solder the components. Some pcb manufacturers also offer to solder them. For more info go: https://github.com/vroland/epdiy 35 | 36 | Required Python modules: 37 | ```bash 38 | pip install mss 39 | pip install pyautogui 40 | pip install numpy 41 | ``` 42 | For Windows 10: 43 | ```bash 44 | pip install pywin32 45 | 46 | ``` 47 | 48 | 49 | #### Installation: 50 | 51 | 1) Once you have the board, flash the demo example on the example folder of the Epdiy repository to verify that it is working correctly. 52 | 53 | 3) Get the repositories: 54 | On linux: 55 | ```bash 56 | cd 57 | git clone https://github.com/vroland/epdiy 58 | 59 | git clone https://github.com/amadeok/Epdiy-Eink-PC-monitor 60 | cp -R ~/Epdiy-Eink-PC-monitor/pc_monitor ~/epdiy/examples 61 | 62 | ``` 63 | On windows' command prompt: 64 | ```bash 65 | cd c:\ 66 | git clone https://github.com/vroland/epdiy 67 | git clone https://github.com/amadeok/Epdiy-Eink-PC-monitor 68 | Xcopy /E /I C:\Epdiy-Eink-PC-monitor C:\epdiy\examples 69 | 70 | ``` 71 | 72 | 73 | 4) The computer and board should connect to the same wifi network. Go to *~/epdiy/examples/pc-monitor/main/*, open *main.c* , go to line 31 and insert the SSID of the wifi network you are going to use in the *WIFI_SSID* variable and the password of the wifi in the *WIFI_PASS* variable. 74 | 75 | 5) Build and flash the client application for the board: 76 | On linux: 77 | ```bash 78 | cd ~/epdiy/examples/pc_monitor 79 | idf.py build && idf.py flash -b 921600 && idf.py monitor 80 | ``` 81 | On windows: 82 | ```bash 83 | cd c:\epdiy\examples\pc_monitor 84 | idf.py build && idf.py flash -b 921600 && idf.py monitor 85 | ``` 86 | 87 | 6)After the flashing finishes, the terminal will start displaying some messages, look for the message: 88 | ```bash 89 | IP Address: xxx.xxx.xxx.xxx 90 | ``` 91 | 92 | (where xxx.xxx.xxx.xxx is an ip address) 93 | 94 | Note it down. 95 | 96 | 7) Add the ip address to a display configuration file: 97 | 98 | Open *example_display.conf* in *~/epdiy/examples/pc_monitor/pc_host_app/* with a text editor and change the IP address in the first line to the one you noted down before, and, if needed, change the values *width* and *height* to the witdh and height resolution of your display 99 | 100 | 101 | 8) Build the pc-host application and the C shared library: 102 | On a new terminal: 103 | On Linux: 104 | ```bash 105 | cd ~/epdiy/examples/pc_monitor/pc_host_app/ 106 | g++ main.cpp generate_eink_framebuffer.cpp rle_compression.cpp utils.cpp -o process_capture -I include 107 | g++ dither_.cpp invert_.cpp -o dither_.so -shared -fPIC 108 | ``` 109 | 110 | On Windows(requires MinGW installed and on PATH): 111 | ```bash 112 | cd C:\epdiy\examples\pc_monitor\pc_host_app\ 113 | 114 | g++ main.cpp generate_eink_framebuffer.cpp rle_compression.cpp utils.cpp -static -o process_capture.exe -I "C:\Epdiy-Eink-PC-monitor\pc_monitor\pc_host_app\include" -lws2_32 115 | g++ dither_.cpp invert_.cpp -o dither_.dll -shared -fPIC 116 | 117 | ``` 118 | 9) Now the pc is ready to start the mirroring. If the board is ready to start mirroring it will display a message saying "Socket listening ". 119 | To start the mirroring execute: 120 | ```bash 121 | python screen_capture.py example_display.conf 122 | ``` 123 | If you want to hide the terminal output run it like this: 124 | ```bash 125 | python screen_capture.py example_display.conf -silent 126 | ``` 127 | Important: always exit the pc host application by pressing the letter **q** 128 | 129 | 130 | 131 | #### Creating configuration files and settings: 132 | The pc host application reads the settings from a *.conf* file passed as an argument when running *screen_capture.py*. Inside a display configuration file there are the following settings: 133 | 134 | - *ip_address* : the IP address of the ESP32 module to connect to 135 | 136 | - *id*: Number to identify each display. Has to be different for each display 137 | 138 | - *width* : the resolution width of the display 139 | 140 | - *height*: the resolution height of the display 141 | 142 | - *x_offset* and *y_offset* 143 | They set how many pixels away for the top left corner the screen will be captured. ie. if the resolution is 1200x825, setting both to zero will capture 1200x825 pixels from the extreme top left corner of the screen. setting them to 100 and 50 will capture 1200x825 pixels 100 pixels to the right and 50 pixels from the top left corner of the screen. If you have 2 monitors of 1920x1080 pixels and you want to capture from the top left corner of the monitor on the right, *x_offset* should be 1920 and *y_offset* should be 0. More info on mss: https://python-mss.readthedocs.io/examples.html 144 | 145 | - *rotation*: can be set to 180 to if using a display upside down 146 | 147 | - *grey_monochrome_threshold*: 148 | When converting the capture from 256 greyscale shades to black and white monochrome a threshold is used to determine which shades will become plain black and which plain white. The default is 200 and it improves drawing black letters on white background. Minimum value is 0 and maximum is 255 149 | 150 | - *sleep_time*: Amount of time in miliseconds to pause the program after capturing a frame. Can be used to reduce the framerate artificially. Set it to 50 or less for fast refresh rate 151 | 152 | - *refresh_every_x_frames*: Refreshes the display every the specified number of frame draws. A frame draw is not counted if less than 85 lines have changed (such as when moving the mouse) 153 | 154 | - *framebuffer_cycles*: Defines the number of times to write the current eink framebuffer to the display. Because of the way that eink displays work, writing the same framebuffer to the display has the effect of getting deeper blacks and whites. Increases draw time. 155 | 156 | - *rmt_high_time*: Defines the high tick time of the CKV signal. A higher value makes blacks blacker and whites whiter. Increases draw time. It is defined in *rmt_pulse.h* 157 | 158 | - *mode*: there are 10 modes: *monochrome*, *PIL_dither*(Floyd-Steinberg dither) and 8 more dithering modes: *Bayer16*, *Bayer8* *Bayer4*, *Bayer3*, *Bayer2*, *FS*, *SierraLite*, *Sierra* which are defined in *Dither.h* 159 | 160 | - *invert*: if set to -1 invert is always off, if set to 0 it's always on, if it's greater than zero it acts as a threshold for *smart invert*, which activates. *smart invert* inverts the whole image only when most of it is black, otherwise it doesn't invert at all. maximum value is 255 161 | 162 | - *selective_invert* if set to 1 detects black regions on the screen and inverts them (can create some artifacts). *smart invert* and *selective_invert* can help improve overall usability for text based office work by forcing almost always black letters on white background 163 | 164 | - *color, contrast, brightness, sharpness*: the Pillow module has the option to apply these enhancements to the capture. They are applied before converting the image to 1 bit per pixel. more info: https://pillow.readthedocs.io/en/stable/reference/ImageEnhance.html 165 | (experimental) 166 | 167 | - *draw_white_first* makes the program draw to the display first the white pixels of the image and then the black ones 168 | 169 | - *enhance_before_greyscale*: choose wether to apply the Pillow enhancements before or after converting to 8bpp greyscale 170 | - *do_full_refresh*: calls epd_clear() to refresh screen 171 | 172 | Advanced settings: 173 | - *selective_compression* sets a maximum percentage of eficiency for the rle compression. if that percentage is not met, the framebuffer will be sent without compression to the board 174 | 175 | - *esp32_multithread* starts downloading next frame while the current is being drawn 176 | - 177 | - *enable_skipping*: makes the driver call *epd_skip()* instead of *epd_output_row()* with an empty buffer. It can reduce draw time significantly but may introduce some artifacts. To avoid the artifacts but mantain low draw time when moving the cursor *epd_skip_threshold* can be set to 75. 178 | 179 | - *epd_skip_threshold*: if less than *epd_skip_threshold* rows have changed skipping is enabled for the current frame draw. 180 | 181 | - *framebuffer_cycles_2* and *framebuffer_cycles_2_threshold*: If less than *framebuffer_cycles_2_threshold* number of rows have changed, the current framebuffer will be written *framebuffer_cycles_2* times instead of the value set by *framebuffer_cycles*. Can be used to reduce the draw time when just a few lines have changed such as when moving the cursor. This is only active is the mouse is moving 182 | 183 | #### Hotkeys: 184 | It is possible to change the following settings while the application is running by pressing their hotkey on the terminal: 185 | 186 | -*1* decreases *color* by 0.1 187 | 188 | -*2* increases *color* by 0.1 189 | 190 | -*3* decreases *contrast* by 0.1 191 | 192 | -*4* increases *contrast* by 0.1 193 | 194 | -*5* decreases *brightness* by 0.1 195 | 196 | -*6* increases *brightness* by 0.1 197 | 198 | -*7* decreases *sharpness* by 0.1 199 | 200 | -*8* increases *sharpness* by 0.1 201 | 202 | -*9* increases *grey_monochrome_threshold* by 10 203 | 204 | -*0* decreases *grey_monochrome_threshold* by 10 205 | 206 | -*b* toggles *enhance_before_greyscale* 207 | 208 | -*i* toggles *invert* 209 | 210 | -*u* increases *smart invert* threshold by 10 and activates it 211 | 212 | -*y* decreases *smart invert* threshold by 10 and activates it 213 | 214 | -*s* toggles *selective_invert* 215 | 216 | -*d* switches to the mode specified in the configuration file 217 | 218 | -*m* switches to monochrome mode 219 | 220 | 221 | #### Using multiple displays at the same time 222 | 223 | The application supports using multiple displays at the same time. For each display it is required to create a configuration file. 224 | Once the configuration files are created run them as 225 | 226 | ```bash 227 | python3 screen_capture.py display0.conf 228 | ``` 229 | ```bash 230 | python3 screen_capture.py display1.conf 231 | ``` 232 | ```bash 233 | python3 screen_capture.py display2.conf 234 | ``` 235 | Each from a different terminal. If you want that a hotkey press has an effect on all the running instances, append *-common* to each command: 236 | ```bash 237 | python3 screen_capture.py display0.conf -common 238 | ``` 239 | 240 | ------------ 241 | #### Support for other displays 242 | 243 | - The project has been tested using a board revision 4 and a ED097TC2 display. Check for the list of other displays supported by the Epdiy board on https://github.com/vroland/epdiy . Those displays should work but are untested. If the display you want to try has the same resolution of the ED097TC2, that is 1200x825, it should be enough to select the correct display type in the menuconfig Epdiy section. 244 | If the display you want to try has a different resolution, other than selecting it in the menuconfig before flashing the board, you should also specify its resolution on its display configuration file. 245 | ------------ 246 | 247 | 248 | #### To do list: 249 | - Organize the code better 250 | - Improve ghosting 251 | - Optimize data transfer between pc and board 252 | 253 | 254 | 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/generate_eink_framebuffer.cpp: -------------------------------------------------------------------------------- 1 | #include <2bitLUTforC_inv.h> 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern int total_nb_pixels, refres_every_x_frames, nb_draws, draw_white_first; 12 | extern char working_dir[256]; 13 | int debug_grayscale = 1; 14 | 15 | int loop_counter0 = 1, loop_counter1 = 0; 16 | 17 | void *generate_eink_framebuffer_v1(unsigned char *source_1bpp, char *padded_2bpp_framebuffer_current, char *padded_2bpp_framebuffer_previous, char *eink_framebuffer) 18 | { //generate eink framebuffer from 1bpp monochrome capture 19 | int counter = 0; 20 | 21 | // long t = getTick(); 22 | for (int g = 0; g < total_nb_pixels / 4; g++) 23 | { 24 | memcpy(padded_2bpp_framebuffer_current + counter, two_bit_LUT + (source_1bpp[g] * 2), 2); 25 | counter += 2; 26 | eink_framebuffer[g] = padded_2bpp_framebuffer_previous[g] ^= (padded_2bpp_framebuffer_current[g] << 1); 27 | } 28 | //array_to_file(eink_framebuffer, total_nb_pixels / 8, working_dir, "eink_framebuffer", 0); 29 | // printf("generate_eink_framebuffer_v1 took: %d\n", getTick() - t); 30 | // array_to_file(padded_2bpp_framebuffer_current, 10000, working_dir, "padded_2bpp_framebuffer_current", 0); 31 | } 32 | 33 | void generate_eink_framebuffer_v2(char *source_8bpp_current, char *source_8bpp_previous, char *source_8bpp_modified_previous, char **eink_framebuffer, int mode) 34 | { //generate eink framebuffer from 8bpp monochrome capture, slower than v1 35 | if (mode != 10 || draw_white_first) 36 | { 37 | unsigned char temp_masks[50]; 38 | int cur = 0, prev = 0, draws; 39 | 40 | for (int x = 0; x < nb_draws; x++) 41 | memset(eink_framebuffer[x], 0, total_nb_pixels / 4); 42 | if (draw_white_first == 1) 43 | draws = nb_draws / 2; 44 | else 45 | draws = 0; 46 | 47 | //memset(source_8bpp_previous, 255, total_nb_pixels); 48 | // array_to_file(source_8bpp_current, total_nb_pixels, working_dir, "source_8bpp_current", 0); 49 | //array_to_file(source_8bpp_previous, total_nb_pixels, working_dir, "source_8bpp_previous", 0); 50 | int dif_counter = 0, delta_counter = 0, delta_nb; 51 | int n = 0; 52 | for (int counter = 0; counter < total_nb_pixels; counter += 4) 53 | { 54 | for (int k = 0; k < nb_draws; k++) 55 | temp_masks[k] = 0; 56 | for (int y = 0; y < 4; y++) 57 | { 58 | 59 | cur = (uint8_t)source_8bpp_current[counter + y]; 60 | prev = (uint8_t)source_8bpp_previous[counter + y]; 61 | 62 | //n = (uint8_t)source_8bpp_previous[counter + y] - (uint8_t)source_8bpp_current[counter + y]; 63 | switch (cur) 64 | { 65 | case 0: 66 | switch (prev) 67 | { 68 | case 0: 69 | break; 70 | case 1: 71 | temp_masks[1] |= 1 << y * 2; // make pixel blacker 72 | break; 73 | } 74 | break; 75 | 76 | case 1: //cur 77 | switch (prev) 78 | { 79 | case 0: 80 | temp_masks[0] |= 2 << y * 2; // make pixel whiter 81 | break; 82 | case 1: 83 | break; 84 | } 85 | break; 86 | } 87 | } 88 | delta_counter++; 89 | 90 | for (int k = 0; k < nb_draws; k++) 91 | { 92 | eink_framebuffer[k][delta_counter] |= temp_masks[k]; 93 | } 94 | } 95 | // for (int k = 0; k < nb_draws; k++) 96 | // { 97 | // array_to_file(eink_framebuffer[k], 230400, working_dir, "eink_framebuffer", k); 98 | // } 99 | // std::string filename = "/home/amadeok/epdiy-working/examples/pc_monitor/pc_host_app/rebuild_fb_rgb.py"; 100 | // std::string command = "python3 "; 101 | // command += filename; 102 | // system(command.c_str()); 103 | } 104 | else 105 | { 106 | unsigned char temp_masks[50]; 107 | int cur = 0, prev = 0, draws; 108 | 109 | for (int x = 0; x < nb_draws; x++) 110 | memset(eink_framebuffer[x], 0, total_nb_pixels / 4); 111 | if (draw_white_first == 1) 112 | draws = nb_draws / 2; 113 | else 114 | draws = 0; 115 | 116 | //memset(source_8bpp_previous, 255, total_nb_pixels); 117 | // array_to_file(source_8bpp_current, total_nb_pixels, working_dir, "source_8bpp_current", 0); 118 | //array_to_file(source_8bpp_previous, total_nb_pixels, working_dir, "source_8bpp_previous", 0); 119 | 120 | int dif_counter = 0, delta_counter = 0, delta_nb; 121 | int n = 0; 122 | for (int counter = 0; counter < total_nb_pixels; counter += 4) 123 | { 124 | for (int k = 0; k < nb_draws; k++) 125 | temp_masks[k] = 0; 126 | for (int y = 0; y < 4; y++) 127 | { 128 | 129 | cur = (uint8_t)source_8bpp_current[counter + y]; 130 | prev = (uint8_t)source_8bpp_previous[counter + y]; 131 | 132 | //n = (uint8_t)source_8bpp_previous[counter + y] - (uint8_t)source_8bpp_current[counter + y]; 133 | switch (cur) 134 | { 135 | case 0: 136 | switch (prev) 137 | { 138 | case 0: 139 | break; 140 | case 85: 141 | temp_masks[0 + draws] |= 1 << y * 2; // make pixel blacker 142 | break; 143 | case 170: 144 | for (int q = 0 + draws; q < 2 + draws; q++) // make pixel blacker 145 | temp_masks[q] |= 1 << y * 2; 146 | break; 147 | 148 | case 255: 149 | for (int q = 0 + draws; q < 3 + draws; q++) // make pixel blacker 150 | temp_masks[q] |= 1 << y * 2; 151 | break; 152 | } 153 | break; 154 | 155 | case 85: //cur 156 | switch (prev) 157 | { 158 | case 0: 159 | if (draw_white_first == 0) 160 | temp_masks[0] |= 2 << y * 2; // make pixel whiter 161 | break; 162 | case 85: 163 | break; 164 | case 170: 165 | temp_masks[0 + draws] |= 1 << y * 2; // make pixel blacker 166 | break; 167 | 168 | case 255: 169 | for (int q = 0 + draws; q < 2 + draws; q++) 170 | temp_masks[q] |= 1 << y * 2; // make pixel blacker 171 | break; 172 | } 173 | break; 174 | 175 | case 170: //cur 176 | switch (prev) 177 | { 178 | case 0: 179 | for (int q = 0; q < 1; q++) 180 | { 181 | temp_masks[q] |= 2 << y * 2; // make pixel whiter 182 | if (draw_white_first == 0) 183 | temp_masks[1] |= 2 << y * 2; // make pixel whiter 184 | } 185 | 186 | break; 187 | case 85: 188 | if (draw_white_first == 0) 189 | temp_masks[0] |= 2 << y * 2; // make pixel whiter 190 | break; 191 | case 170: 192 | break; 193 | 194 | case 255: 195 | temp_masks[draws] |= 1 << y * 2; // make pixel blacker 196 | break; 197 | } 198 | break; 199 | 200 | case 255: //cur 201 | switch (prev) 202 | { 203 | case 0: 204 | for (int q = 0; q < 3; q++) 205 | temp_masks[q] |= 2 << y * 2; // make pixel whiter 206 | break; 207 | case 85: 208 | for (int q = 0; q < 2; q++) 209 | temp_masks[q] |= 2 << y * 2; // make pixel whiter 210 | break; 211 | case 170: 212 | temp_masks[0] |= 2 << y * 2; // make pixel whiter 213 | break; 214 | case 255: 215 | break; 216 | } 217 | break; 218 | } 219 | } 220 | delta_counter++; 221 | 222 | for (int k = 0; k < nb_draws; k++) 223 | { 224 | eink_framebuffer[k][delta_counter] |= temp_masks[k]; 225 | } 226 | } 227 | // for (int k = 0; k < nb_draws; k++) 228 | // { 229 | // array_to_file(eink_framebuffer[k], 230400, working_dir, "eink_framebuffer", k); 230 | // } 231 | // std::string filename = "/home/amadeok/epdiy-working/examples/pc_monitor/pc_host_app/rebuild_fb_rgb.py"; 232 | // std::string command = "python3 "; 233 | // command += filename; 234 | // system(command.c_str()); 235 | } 236 | } 237 | 238 | void quantize(char *source_8bpp_current, char *source_8bpp_modified_current, int size) 239 | { 240 | int val; 241 | 242 | for (int h = 0; h < size; h++) 243 | { // int v = source_8bpp_current[h]; 244 | val = (uint8_t)source_8bpp_current[h] / 64; 245 | switch (val) 246 | { 247 | case 0: 248 | source_8bpp_modified_current[h] = 0; 249 | break; 250 | case 1: 251 | source_8bpp_modified_current[h] = 85; 252 | break; 253 | case 2: 254 | source_8bpp_modified_current[h] = 170; 255 | break; 256 | case 3: 257 | source_8bpp_modified_current[h] = 255; 258 | break; 259 | } 260 | } 261 | } 262 | 263 | void generate_eink_framebuffer_v2_with_ghost(char *source_8bpp_current, char *source_8bpp_previous, char *source_8bpp_modified_previous, char **eink_framebuffer, int nb_pixels_to_change) 264 | { // generate eink framebuffer and attempt to reduce ghosting (experimental) 265 | memset(eink_framebuffer, 0, total_nb_pixels / 4); 266 | unsigned char temp_mask = 0; 267 | int dif_counter = 0, delta_counter = 1, delta_nb; 268 | memcpy(source_8bpp_modified_previous, source_8bpp_previous, total_nb_pixels); 269 | 270 | for (int counter = nb_pixels_to_change; counter < total_nb_pixels; counter += 1) 271 | if (source_8bpp_previous[counter] == 1 && source_8bpp_current[counter] == 0) 272 | { 273 | for (int u = 1; u < nb_pixels_to_change; u++) 274 | { 275 | if (source_8bpp_current[counter - u] == 0) 276 | source_8bpp_modified_previous[counter - u] = 1; 277 | if (source_8bpp_current[counter + u] == 0) 278 | source_8bpp_modified_previous[counter + u] = 1; 279 | } 280 | counter += nb_pixels_to_change * 2; 281 | } 282 | 283 | if (loop_counter1 == 1) 284 | { 285 | loop_counter1 = 0; 286 | memset(source_8bpp_modified_previous, 1, total_nb_pixels); 287 | memset(source_8bpp_current, 0, total_nb_pixels); 288 | } 289 | if (loop_counter0 % 81 == 0) 290 | { 291 | memset(source_8bpp_modified_previous, 0, total_nb_pixels); 292 | memset(source_8bpp_current, 1, total_nb_pixels); 293 | 294 | loop_counter1 = 1; 295 | } 296 | for (int counter = 4; counter < total_nb_pixels; counter += 4) 297 | { 298 | 299 | temp_mask = 0; 300 | for (int y = 0; y < 4; y++) 301 | { 302 | 303 | if (source_8bpp_modified_previous[counter + y] == 1 && source_8bpp_current[counter + y] == 0) 304 | { 305 | 306 | temp_mask |= 1 << y * 2; // make blacker 307 | } 308 | else if (source_8bpp_modified_previous[counter + y] == 0 && source_8bpp_current[counter + y] == 1) 309 | { 310 | 311 | temp_mask |= 2 << y * 2; // make whiter 312 | } 313 | } 314 | eink_framebuffer[0][delta_counter] |= temp_mask; 315 | delta_counter++; 316 | // array_to_file(eink_framebuffer, 230400, working_dir, "eink_framebuffer", 0); 317 | } 318 | } -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/rle_compression.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | extern char *compressed_eink_framebuffer_ptrs[8]; 8 | extern unsigned char *array_with_zeros, *draw_white_bytes, *draw_black_bytes; 9 | extern int compressed_chunk_lengths[8]; 10 | extern int eink_framebuffer_size, chunk_size; 11 | extern char working_dir[256]; 12 | 13 | void rle_extract1(char *decompressed, int nb_chunks, char *eink_framebuffer_swapped, const int eink_framebuffer_size, int compressed_size) 14 | { 15 | int counter = 0, counter2 = 0, id = 0, i = 0, j = 0, offset = 0, k; 16 | // int plus10 = 0, plus50 = 0, plus30 = 0, plus70 = 0, plus90 = 0, minus = 0; //for testing 17 | for (k = 0; k < nb_chunks; k++) 18 | { 19 | compressed_size = compressed_chunk_lengths[k]; 20 | counter2 = 0, counter = 0; 21 | while (counter < compressed_size) 22 | { 23 | 24 | i = (char)compressed_eink_framebuffer_ptrs[k][counter++]; 25 | j = compressed_eink_framebuffer_ptrs[k][counter++] & 0xff; 26 | if (i < 0) 27 | { 28 | offset = (i + 130); 29 | switch (j) 30 | { 31 | case 0: 32 | memcpy(decompressed + counter2, array_with_zeros, offset); 33 | counter2 += offset; 34 | break; 35 | case 85: 36 | memcpy(decompressed + counter2, draw_black_bytes, offset); 37 | counter2 += offset; 38 | break; 39 | case 170: 40 | memcpy(decompressed + counter2, draw_white_bytes, offset); 41 | counter2 += offset; 42 | break; 43 | default: 44 | for (int f = 0; f < i + 130; f++) 45 | { 46 | decompressed[counter2] = j; 47 | counter2++; 48 | } 49 | break; 50 | } 51 | } 52 | else if (i >= 0) 53 | { 54 | for (int f = 0; f < i; f++) 55 | { 56 | decompressed[counter2] = j; 57 | j = compressed_eink_framebuffer_ptrs[k][counter++] & 0xff; 58 | counter2++; 59 | } 60 | decompressed[counter2] = j; 61 | counter2++; 62 | } 63 | } 64 | //for testing and debugging: 65 | // if (i < 0) 66 | // minus++; 67 | // if (i > 10) 68 | // { 69 | // plus10++; 70 | // if (i > 30) 71 | // { 72 | // plus30++; 73 | // if (i > 50) 74 | // { 75 | // plus50++; 76 | // if (i > 70) 77 | // { 78 | // plus70++; 79 | // if (i > 90) 80 | // plus90++; 81 | // } 82 | // } 83 | // } 84 | // } 85 | // array_to_file(decompressed, counter2, working_dir, "decompressed", 0); 86 | for (int h = 0; h < chunk_size; h++) // for debugging 87 | { 88 | if (decompressed[h] != eink_framebuffer_swapped[k * chunk_size + h]) 89 | printf(" d "); 90 | } 91 | } 92 | //printf("10, 30, 50, 70, 90, is %d, %d, %d, %d, %d, minus: %d \n", plus10, plus30, plus50, plus70, plus90, minus); 93 | } 94 | 95 | int rle_compress(char *array_to_compress, char *tmp_array, int nb_chunks, char *compressed_eink_framebuffer, const int total_nb_pixels, int chunk_size) 96 | { 97 | 98 | uint8_t t[256]; 99 | int source_size = total_nb_pixels; 100 | 101 | int i = 0, keep, end_of_file = 0, counter = 0, counter2; 102 | long t1 = getTick(); 103 | // array_to_file(array_to_compress, total_nb_pixels, working_dir, "array_to_compress", switcher); 104 | // array_to_file(eink_framebuffer_swapped, total_nb_pixels / 2, working_dir, "eink_framebuffer_swapped", 0); 105 | int k; 106 | for (k = 0; k < nb_chunks; k++) 107 | { 108 | memcpy(tmp_array, array_to_compress + (k * chunk_size), chunk_size); 109 | // array_to_file(source, chunk_size, working_dir, "source", k); 110 | // array_to_file(array_to_compress + (k * chunk_size), chunk_size, working_dir, "atc", k); 111 | end_of_file = 0; 112 | counter = 0, counter2 = 0; 113 | if (nb_chunks == 2) 114 | { 115 | if (k == 0) 116 | chunk_size = 123600; 117 | else 118 | chunk_size = 123900; 119 | } 120 | t[0] = tmp_array[counter++]; 121 | while (counter < chunk_size) 122 | { 123 | if (end_of_file == 1) 124 | break; 125 | t[1] = tmp_array[counter++]; 126 | if (t[0] != t[1]) // uncompressible sequence 127 | { 128 | i = 1; 129 | if (counter < chunk_size + 1) 130 | do 131 | { 132 | t[++i] = tmp_array[counter++]; 133 | if (counter >= chunk_size) 134 | { 135 | end_of_file = 1; 136 | break; 137 | } 138 | } while (counter < chunk_size && i < 128 && t[i] != t[i - 1]); 139 | if ((keep = t[i] == t[i - 1])) 140 | --i; 141 | 142 | compressed_eink_framebuffer_ptrs[k][counter2++] = i - 1; 143 | memcpy(compressed_eink_framebuffer_ptrs[k] + counter2, t, i); 144 | counter2 += i; 145 | 146 | t[0] = t[i]; 147 | if (!keep) 148 | continue; // size too large or EOF 149 | } 150 | // compressible sequence 151 | i = 2; 152 | do 153 | { 154 | t[1] = tmp_array[counter++]; 155 | if (counter >= chunk_size) 156 | { 157 | end_of_file = 1; 158 | break; 159 | } 160 | } while (++i < 130 && t[0] == t[1]); 161 | 162 | compressed_eink_framebuffer_ptrs[k][counter2++] = i + 125; 163 | compressed_eink_framebuffer_ptrs[k][counter2++] = t[0]; 164 | 165 | t[0] = t[1]; 166 | } 167 | compressed_chunk_lengths[k] = counter2; 168 | // array_to_file(compressed_eink_framebuffer_ptrs[k], counter2, working_dir, "cefv1", k); 169 | 170 | // printf("counter2 is %d \n", counter2); 171 | } 172 | 173 | //printf("compressing took %d \n", getTick() - t1); 174 | return counter2; 175 | } 176 | 177 | int rle_compress_v2(unsigned char *array_to_compress, unsigned char tmp_array[], int nb_chunks, uint16_t **added_compression_arr, const int chunk_size) 178 | { 179 | 180 | uint8_t t[256]; 181 | int i = 0, keep, end_of_file = 0, counter = 0, counter2, counter3 = 0; 182 | long t1 = getTick(); 183 | array_to_file(array_to_compress, chunk_size / 100, working_dir, "array_to_compress", 0); 184 | //array_to_file(eink_framebuffer_swapped, total_nb_pixels / 2, working_dir, "eink_framebuffer_swapped", 0); 185 | int k; 186 | for (k = 0; k < nb_chunks; k++) 187 | { 188 | memcpy(tmp_array, array_to_compress + (k * chunk_size), chunk_size); 189 | // array_to_file(source, chunk_size, working_dir, "source", k); 190 | end_of_file = 0; 191 | counter = 0, counter2 = 0; 192 | 193 | t[0] = tmp_array[counter++]; 194 | while (counter < chunk_size) 195 | { 196 | if (end_of_file == 1) 197 | break; 198 | t[1] = tmp_array[counter++]; 199 | if (t[0] != t[1]) // uncompressible sequence 200 | { 201 | i = 1; 202 | if (counter < chunk_size + 1) 203 | do 204 | { 205 | t[++i] = tmp_array[counter++]; 206 | if (counter >= chunk_size) 207 | { 208 | end_of_file = 1; 209 | break; 210 | } 211 | } while (counter < chunk_size && i < 128 && t[i] != t[i - 1]); 212 | 213 | if ((keep = t[i] == t[i - 1])) 214 | --i; 215 | int i2 = i; 216 | 217 | if (i % 2 != 0) 218 | { 219 | t[++i] = tmp_array[counter++]; 220 | // i2--; 221 | } 222 | compressed_eink_framebuffer_ptrs[k][counter2++] = i - 1; 223 | 224 | memcpy(added_compression_arr[k] + counter3 / 2, t, i); 225 | memcpy(compressed_eink_framebuffer_ptrs[k] + counter2, array_with_zeros, i2); 226 | 227 | counter3 += i; 228 | counter2 += i2; 229 | 230 | t[0] = t[i]; 231 | if (!keep) 232 | continue; // size too large or EOF 233 | } 234 | // compressible sequence 235 | i = 2; 236 | do 237 | { 238 | t[1] = tmp_array[counter++]; 239 | if (counter >= chunk_size) 240 | { 241 | end_of_file = 1; 242 | break; 243 | } 244 | } while (++i < 130 && t[0] == t[1]); 245 | 246 | compressed_eink_framebuffer_ptrs[k][counter2++] = i + 125; 247 | compressed_eink_framebuffer_ptrs[k][counter2++] = t[0]; 248 | 249 | t[0] = t[1]; 250 | array_to_file(compressed_eink_framebuffer_ptrs[k], counter2, working_dir, "cef", k); 251 | array_to_file(added_compression_arr[k], counter3, working_dir, "aca", k); 252 | } 253 | compressed_chunk_lengths[k] = counter2; 254 | // array_to_file(compressed_eink_framebuffer_ptrs[k], counter2, working_dir, "cef", k); 255 | 256 | // printf("counter2 is %d \n", counter2); 257 | } 258 | 259 | //printf("compressing took %d \n", getTick() - t1); 260 | return counter2; 261 | } 262 | 263 | void optimize_rle(char *eink_framebuffer) 264 | { 265 | for (int y = 0; y < eink_framebuffer_size; y++) 266 | { 267 | int currentent_nb = eink_framebuffer[y]; 268 | switch (currentent_nb) 269 | { 270 | case 255: 271 | eink_framebuffer[y] = 0; 272 | break; 273 | case 192: 274 | eink_framebuffer[y] = 0; 275 | break; 276 | case 48: 277 | eink_framebuffer[y] = 0; 278 | break; 279 | case 12: 280 | eink_framebuffer[y] = 0; 281 | break; 282 | case 3: 283 | eink_framebuffer[y] = 0; 284 | break; 285 | case 240: 286 | eink_framebuffer[y] = 0; 287 | break; 288 | case 15: 289 | eink_framebuffer[y] = 0; 290 | break; 291 | case 252: 292 | eink_framebuffer[y] = 0; 293 | break; 294 | case 63: 295 | eink_framebuffer[y] = 0; 296 | break; 297 | case 60: 298 | eink_framebuffer[y] = 0; 299 | break; 300 | case 195: 301 | eink_framebuffer[y] = 0; 302 | break; 303 | case 204: 304 | eink_framebuffer[y] = 0; 305 | break; 306 | case 51: 307 | eink_framebuffer[y] = 0; 308 | break; 309 | case 207: 310 | eink_framebuffer[y] = 0; 311 | break; 312 | case 243: 313 | eink_framebuffer[y] = 0; 314 | break; 315 | } 316 | } 317 | } 318 | 319 | void rle_extract2(int compressed_size, unsigned char *decompressed_p, unsigned char *compressed, int k) 320 | { 321 | int counter, counter2, j = 0; 322 | 323 | counter2 = 0, counter = 0; 324 | int offset = 0; 325 | int8_t i = 0; 326 | while (counter < compressed_size) 327 | { 328 | i = compressed[counter]; 329 | counter++; 330 | j = compressed[counter] & 0xff; 331 | counter++; 332 | 333 | if (i < 0) 334 | { 335 | offset = (i + 130); 336 | switch (j) 337 | { 338 | case 0: 339 | memcpy(decompressed_p + counter2, array_with_zeros, offset); 340 | counter2 += offset; 341 | 342 | break; 343 | default: 344 | for (int f = 0; f < i + 130; f++) 345 | { 346 | decompressed_p[counter2] = j; 347 | counter2++; 348 | } 349 | break; 350 | } 351 | } 352 | else if (i >= 0) 353 | { 354 | for (int f = 0; f < i; f++) 355 | { 356 | decompressed_p[counter2] = j; 357 | 358 | j = compressed[counter++] & 0xff; 359 | counter2++; 360 | } 361 | decompressed_p[counter2] = j; 362 | counter2++; 363 | } 364 | } 365 | } -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/invert_.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | uint8_t *pixels_ori; 8 | uint8_t *pixels_inv_b; 9 | uint8_t *pixels_inv_g; 10 | uint8_t *chunk_arr_b; 11 | uint8_t *chunk_arr_g; 12 | uint8_t *regions[10000]; 13 | int counter = 0; 14 | int counter2_ = 0; 15 | int nb_regions = 0; 16 | int width_, height_; 17 | int chunk_w_, chunk_h_; 18 | int width_rows_, height_rows_; 19 | int first_time = 1; 20 | int n = 0; 21 | 22 | extern "C" 23 | { 24 | void selective_invert(unsigned char *pixels, unsigned char *pixels_inv, int width, int height, int chunk_w, int chunk_h, int thres) 25 | { 26 | // int chunk = 30; 27 | // int thres = 15; //chunk /2; good chunk 30 thres 15 28 | int size = width * height; 29 | int nb_times_chunk_h = height / chunk_h; 30 | int width_rows = width / chunk_w; 31 | int height_rows = height / chunk_h; 32 | 33 | for (int y = 0; y < height_rows; y++) 34 | { 35 | for (int x = 0; x < width_rows; x++) 36 | { 37 | int b_counter = 0; 38 | int w_counter = 0; 39 | for (int n = 0; n < chunk_h; n++) 40 | { 41 | int start = (x * chunk_w) + (y * width * chunk_h) + (n * width); 42 | int end = start + chunk_w; 43 | 44 | for (int j = start; j < end; j++) 45 | switch (pixels[j]) 46 | { 47 | case 0: 48 | pixels_inv[j] = 255; 49 | b_counter += 1; 50 | break; 51 | case 255: 52 | pixels_inv[j] = 0; 53 | //w_counter += 1; 54 | break; 55 | } 56 | } 57 | 58 | if (b_counter > thres) 59 | for (int n = 0; n < chunk_h; n++) 60 | memcpy(pixels + (x * chunk_w) + (y * width * chunk_h) + (n * width), pixels_inv + (x * chunk_w) + (y * width * chunk_h) + (n * width), chunk_w); 61 | } 62 | } 63 | } 64 | } 65 | extern "C" 66 | { 67 | void quantize(char *source_8bpp_current, char *source_8bpp_modified_current, int size) 68 | { 69 | int val; 70 | 71 | for (int h = 0; h < size; h++) 72 | { // int v = source_8bpp_current[h]; 73 | val = (uint8_t)source_8bpp_current[h] / 64; 74 | switch (val) 75 | { 76 | case 0: 77 | source_8bpp_modified_current[h] = 0; 78 | break; 79 | case 1: 80 | source_8bpp_modified_current[h] = 85; 81 | break; 82 | case 2: 83 | source_8bpp_modified_current[h] = 170; 84 | break; 85 | case 3: 86 | source_8bpp_modified_current[h] = 255; 87 | break; 88 | } 89 | } 90 | } 91 | } 92 | 93 | extern "C" 94 | { 95 | void fill_regions(uint8_t *dest, uint8_t *from, uint8_t *matrix, int thres) 96 | { 97 | // thres = 5; 98 | int x, y; 99 | for (int b = 0; b < counter2_; b++) 100 | { 101 | int nn = regions[b][width_rows_ * 2 + 1]; 102 | if (nn < thres * 2) 103 | for (int a = 0; a < nn; a += 2) 104 | { 105 | x = regions[b][a]; 106 | y = regions[b][a + 1]; 107 | for (int n = 0; n < chunk_h_; n++) 108 | memcpy(dest + (x * chunk_w_) + (y * width_ * chunk_h_) + (n * width_), from + (x * chunk_w_) + (y * width_ * chunk_h_) + (n * width_), chunk_w_); 109 | // matrix[x + y * width_rows_] = n; 110 | } 111 | } 112 | } 113 | } 114 | extern "C" 115 | { 116 | void find_regions(int to_find, uint8_t *matrix) 117 | { 118 | counter = 0; 119 | int y = 0, x = 0; 120 | int curr = 0, prev = 0; 121 | counter2_ = 0; 122 | for (int y = 0; y < height_rows_; y++) 123 | { 124 | for (int x = 0; x < width_rows_; x++) 125 | { 126 | prev = curr; 127 | curr = matrix[x + y * width_rows_]; 128 | if (curr != prev || x % width_rows_ == 0) 129 | { 130 | if (counter > width_rows_ * 2 + 1) 131 | printf("Warning counter overflow \n"); 132 | regions[counter2_][width_rows_ * 2 + 1] = counter; //store counter number 133 | counter = 0; 134 | 135 | counter2_++; 136 | } 137 | if (matrix[x + y * width_rows_] == to_find) 138 | { 139 | regions[counter2_][counter] = x; 140 | regions[counter2_][counter + 1] = y; 141 | counter += 2; 142 | } 143 | 144 | //printf(" [%3d %3d %3d] ", x, y, matrix[x + y * width]); 145 | // printf("%d", matrix[x + y * width_rows_]); 146 | } 147 | // printf(" \n"); 148 | } 149 | if (counter2_ > 10000) 150 | printf("Warning counter 2 overflow \n"); 151 | } 152 | } 153 | extern "C" 154 | { 155 | void polarize(uint8_t *pixels, float factor, int pivot, int size) 156 | { 157 | int ori, val, fin; 158 | for (int a = 0; a < size; a++) 159 | { 160 | // pixels[a] = pixels[a]; 161 | if (pixels[a] >= pivot) 162 | { 163 | // val = ; 164 | pixels[a] = pixels[a] + (255 - pixels[a]) / factor; 165 | } 166 | else 167 | { 168 | pixels[a] = pixels[a] - pixels[a] / factor; 169 | } 170 | } 171 | } 172 | } 173 | extern "C" 174 | { 175 | void polarize_24bit(uint8_t *pixels_, uint8_t *pixels_24, float factor_, int pivot_, int size_) 176 | { 177 | int ori, val, fin; 178 | // factor_ = 2; 179 | // pivot_ = 130; 180 | // size_ = 1200*825; 181 | printf("info %f \n", factor_); 182 | printf("info %d \n", pivot_); 183 | printf("info %d \n", size_); 184 | 185 | for (int a = 0; a < size_; a++) 186 | { 187 | // if (a < 500) 188 | if (pixels_[a] >= pivot_) 189 | { 190 | val = pixels_[a] + (255 - pixels_[a]) / factor_; 191 | // val = 170; 192 | pixels_24[a * 3] = val; 193 | pixels_24[a * 3 + 1] = val; 194 | pixels_24[a * 3 + 2] = val; 195 | // if (a < 20) 196 | // printf("%d %d %d \n", pixels_24[a], pixels_24[a + 1], pixels_24[a + 2]); 197 | } 198 | else 199 | { 200 | val = pixels_[a] - pixels_[a] / factor_; 201 | // val = 170; 202 | pixels_24[a * 3] = val; 203 | pixels_24[a * 3 + 1] = val; 204 | pixels_24[a * 3 + 2] = val; 205 | // if (a < 20) 206 | // printf("%d %d %d \n", pixels_24[a], pixels_24[a + 1], pixels_24[a + 2]); 207 | } 208 | } 209 | printf("%d \n", pixels_24[24 * 3]); 210 | } 211 | } 212 | extern "C" 213 | { 214 | int polarize_int(int n, int pivot, int factor) 215 | { 216 | if (n >= pivot) 217 | { 218 | // val = ; 219 | return n + (255 - n) / factor; 220 | } 221 | else 222 | { 223 | return n - n / factor; 224 | } 225 | } 226 | } 227 | extern "C" 228 | { 229 | void alloc_memory(int size) 230 | { 231 | pixels_inv_b = (uint8_t *)calloc(size * 2, sizeof(unsigned char)); 232 | pixels_inv_g = (uint8_t *)calloc(size * 2, sizeof(unsigned char)); 233 | 234 | chunk_arr_b = (uint8_t *)calloc(size * 2, sizeof(unsigned char)); 235 | chunk_arr_g = (uint8_t *)calloc(size * 2, sizeof(unsigned char)); 236 | 237 | pixels_ori = (uint8_t *)calloc(size * 2, sizeof(unsigned char)); 238 | } 239 | } 240 | extern "C" 241 | { 242 | void selective_invert_v2(uint8_t *pixels, int width, int height, int chunk_w, int chunk_h, int thres_perc, int b_thres, int fill_thres) 243 | { 244 | 245 | n = 0; 246 | // int chunk = 30; 247 | // int thres = 15; //chunk /2; good chunk 30 thres 15 248 | int area = chunk_w * chunk_h; 249 | float quotient = (float)thres_perc / 100; 250 | int thres = int(quotient * area); 251 | 252 | int prev_width_rows = width_rows_; 253 | 254 | if (prev_width_rows != width / chunk_w) 255 | for (int x = 0; x < 10000; x++) 256 | { 257 | free(regions[x]); 258 | regions[x] = (uint8_t *)calloc((width / chunk_w) * 2 + 5, 1); 259 | } 260 | 261 | int size = width * height; 262 | int nb_times_chunk_h = height / chunk_h; 263 | int width_rows = width / chunk_w; 264 | int height_rows = height / chunk_h; 265 | width_ = width; 266 | height_ = height; 267 | width_rows_ = width / chunk_w; 268 | height_rows_ = height / chunk_h; 269 | chunk_w_ = chunk_w, chunk_h_ = chunk_h; 270 | // if (first_time == 1){ 271 | // alloc_memory(size); 272 | // first_time = 0; 273 | // } 274 | // printf("%d \n", n++); 275 | memset(chunk_arr_b, 0, width_rows * height_rows * 2); 276 | memset(chunk_arr_g, 0, width_rows * height_rows * 2); 277 | memset(pixels_inv_b, 0, size); 278 | memset(pixels_inv_g, 0, size); 279 | memcpy(pixels_ori, pixels, size); 280 | 281 | // memcpy(pixels_inv_b, pixels, size); 282 | int primary_thres = 127; 283 | for (int y = 0; y < height_rows; y++) 284 | { 285 | for (int x = 0; x < width_rows; x++) 286 | { 287 | int b_counter = 0; 288 | int w_counter = 0; 289 | int g_counter = 0; 290 | for (int n = 0; n < chunk_h; n++) 291 | { 292 | int start = (x * chunk_w) + (y * width * chunk_h) + (n * width); 293 | int end = start + chunk_w; 294 | 295 | for (int j = start; j < end; j++) 296 | { 297 | // pixels[j] = polarize_int(pixels[j], 160, 2); 298 | 299 | if (pixels[j] < primary_thres) 300 | { 301 | if (pixels[j] < 80) 302 | 303 | pixels_inv_b[j] = 255; 304 | 305 | b_counter += 1; 306 | if (pixels[j] > b_thres) 307 | { 308 | pixels_inv_g[j] = 0; 309 | } 310 | } 311 | else if (pixels[j] >= primary_thres) 312 | { 313 | g_counter += 1; 314 | pixels_inv_b[j] = 0; 315 | 316 | pixels_inv_g[j] = 255; 317 | } 318 | } 319 | } 320 | 321 | if (b_counter > thres) 322 | { 323 | chunk_arr_b[x + y * width_rows_] = 1; 324 | for (int n = 0; n < chunk_h; n++) 325 | memcpy(pixels + (x * chunk_w) + (y * width * chunk_h) + (n * width), pixels_inv_b + (x * chunk_w) + (y * width * chunk_h) + (n * width), chunk_w); 326 | } 327 | if (g_counter > thres) 328 | { 329 | chunk_arr_g[x + y * width_rows_] = 1; 330 | for (int n = 0; n < chunk_h; n++) 331 | memcpy(pixels + (x * chunk_w) + (y * width * chunk_h) + (n * width), pixels_inv_g + (x * chunk_w) + (y * width * chunk_h) + (n * width), chunk_w); 332 | } 333 | } 334 | } 335 | find_regions(1, chunk_arr_b); 336 | fill_regions(pixels, pixels_ori, chunk_arr_b, fill_thres); 337 | find_regions(0, chunk_arr_g); 338 | fill_regions(pixels, pixels_inv_g, chunk_arr_g, fill_thres); 339 | 340 | // printf("nb_regions %d \n\n", nb_regions); 341 | 342 | // for (int y = 0; y < height_rows; y++) 343 | // { 344 | // for (int x = 0; x < width_rows; x++) 345 | // { 346 | 347 | // //printf(" [%3d %3d %3d] ", x, y, chunk_arr_b[x + y * width]); 348 | // printf("%d", chunk_arr_b[x + y * width_rows_]); 349 | // } 350 | // printf(" \n"); 351 | // } 352 | } 353 | } 354 | 355 | extern "C" 356 | { 357 | void invert_task(uint8_t *pixels, int width, int height, int chunk_w, int chunk_h, int thres_perc, int b_thres, int fill_thres, float pole_factor, int pivot) 358 | { 359 | int size = width * height; 360 | if (pole_factor != 0) 361 | polarize(pixels, pole_factor, pivot, size); 362 | selective_invert_v2(pixels, width, height, chunk_w, chunk_h, thres_perc, b_thres, fill_thres); 363 | } 364 | } -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/dither_.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // Ordered dither matrices 8 | ///////////////////////////////////////////////////////////////////////////// 9 | 10 | const int BAYER_PATTERN_2X2[2][2] = { // 2x2 Bayer Dithering Matrix. Color levels: 5 11 | { 51, 206 }, 12 | { 153, 102 } 13 | }; 14 | 15 | const int BAYER_PATTERN_3X3[3][3] = { // 3x3 Bayer Dithering Matrix. Color levels: 10 16 | { 181, 231, 131 }, 17 | { 50, 25, 100 }, 18 | { 156, 75, 206 } 19 | }; 20 | 21 | const int BAYER_PATTERN_4X4[4][4] = { // 4x4 Bayer Dithering Matrix. Color levels: 17 22 | { 15, 195, 60, 240 }, 23 | { 135, 75, 180, 120 }, 24 | { 45, 225, 30, 210 }, 25 | { 165, 105, 150, 90 } 26 | 27 | }; 28 | 29 | 30 | 31 | const int BAYER_PATTERN_8X8[8][8] = { // 8x8 Bayer Dithering Matrix. Color levels: 65 32 | { 0, 128, 32, 160, 8, 136, 40, 168 }, 33 | { 192, 64, 224, 96, 200, 72, 232, 104 }, 34 | { 48, 176, 16, 144, 56, 184, 24, 152 }, 35 | { 240, 112, 208, 80, 248, 120, 216, 88 }, 36 | { 12, 140, 44, 172, 4, 132, 36, 164 }, 37 | { 204, 76, 236, 108, 196, 68, 228, 100 }, 38 | { 60, 188, 28, 156, 52, 180, 20, 148 }, 39 | { 252, 124, 220, 92, 244, 116, 212, 84 } 40 | }; 41 | 42 | const int BAYER_PATTERN_16X16[16][16] = { // 16x16 Bayer Dithering Matrix. Color levels: 256 43 | { 0, 191, 48, 239, 12, 203, 60, 251, 3, 194, 51, 242, 15, 206, 63, 254 }, 44 | { 127, 64, 175, 112, 139, 76, 187, 124, 130, 67, 178, 115, 142, 79, 190, 127 }, 45 | { 32, 223, 16, 207, 44, 235, 28, 219, 35, 226, 19, 210, 47, 238, 31, 222 }, 46 | { 159, 96, 143, 80, 171, 108, 155, 92, 162, 99, 146, 83, 174, 111, 158, 95 }, 47 | { 8, 199, 56, 247, 4, 195, 52, 243, 11, 202, 59, 250, 7, 198, 55, 246 }, 48 | { 135, 72, 183, 120, 131, 68, 179, 116, 138, 75, 186, 123, 134, 71, 182, 119 }, 49 | { 40, 231, 24, 215, 36, 227, 20, 211, 43, 234, 27, 218, 39, 230, 23, 214 }, 50 | { 167, 104, 151, 88, 163, 100, 147, 84, 170, 107, 154, 91, 166, 103, 150, 87 }, 51 | { 2, 193, 50, 241, 14, 205, 62, 253, 1, 192, 49, 240, 13, 204, 61, 252 }, 52 | { 129, 66, 177, 114, 141, 78, 189, 126, 128, 65, 176, 113, 140, 77, 188, 125 }, 53 | { 34, 225, 18, 209, 46, 237, 30, 221, 33, 224, 17, 208, 45, 236, 29, 220 }, 54 | { 161, 98, 145, 82, 173, 110, 157, 94, 160, 97, 144, 81, 172, 109, 156, 93 }, 55 | { 10, 201, 58, 249, 6, 197, 54, 245, 9, 200, 57, 248, 5, 196, 53, 244 }, 56 | { 137, 74, 185, 122, 133, 70, 181, 118, 136, 73, 184, 121, 132, 69, 180, 117 }, 57 | { 42, 233, 26, 217, 38, 229, 22, 213, 41, 232, 25, 216, 37, 228, 21, 212 }, 58 | { 169, 106, 153, 90, 165, 102, 149, 86, 168, 105, 152, 89, 164, 101, 148, 85 } 59 | }; 60 | 61 | 62 | 63 | 64 | extern "C" { 65 | 66 | unsigned char makeDitherBayer16( unsigned char* pixels, int width, int height )noexcept 67 | { 68 | int col = 0; 69 | int row = 0; 70 | 71 | for( int y = 0; y < height; y++ ) 72 | { 73 | row = y & 15; // y % 16 74 | 75 | for( int x = 0; x < width; x++ ) 76 | { 77 | col = x & 15; // x % 16 78 | 79 | const int blue = pixels[x * 3 + 0]; 80 | const int green = pixels[x * 3 + 1]; 81 | const int red = pixels[x * 3 + 2]; 82 | 83 | int color = ((red + green + blue)/3 < BAYER_PATTERN_16X16[col][row] ? 0 : 255); 84 | 85 | pixels[x * 3 + 0] = color; // blue 86 | pixels[x * 3 + 1] = color; // green 87 | pixels[x * 3 + 2] = color; // red 88 | } 89 | 90 | pixels += width * 3; 91 | } 92 | return pixels[0]; 93 | }} 94 | 95 | 96 | extern "C" { 97 | 98 | void makeDitherBayer8( unsigned char* pixels, int width, int height ) noexcept 99 | { 100 | int col = 0; 101 | int row = 0; 102 | 103 | for( int y = 0; y < height; y++ ) 104 | { 105 | row = y & 7; // % 8; 106 | 107 | for( int x = 0; x < width; x++ ) 108 | { 109 | col = x & 7; // % 8; 110 | 111 | const int blue = pixels[x * 3 + 0]; 112 | const int green = pixels[x * 3 + 1]; 113 | const int red = pixels[x * 3 + 2]; 114 | 115 | int color = ((red + green + blue)/3 < BAYER_PATTERN_8X8[col][row] ? 0 : 255); 116 | 117 | pixels[x * 3 + 0] = color; // blue 118 | pixels[x * 3 + 1] = color; // green 119 | pixels[x * 3 + 2] = color; // red 120 | } 121 | 122 | pixels += width * 3; 123 | } 124 | }} 125 | ///////////////////////////////////////////////////////////////////////////// 126 | extern "C" { 127 | 128 | void makeDitherBayer4( unsigned char * pixels, int width, int height ) noexcept 129 | { 130 | int col = 0; 131 | int row = 0; 132 | 133 | for( int y = 0; y < height; y++ ) 134 | { 135 | row = y & 3; // % 4 136 | 137 | for( int x = 0; x < width; x++ ) 138 | { 139 | col = x & 3; // % 4 140 | 141 | const int blue = pixels[x * 3 + 0]; 142 | const int green = pixels[x * 3 + 1]; 143 | const int red = pixels[x * 3 + 2]; 144 | 145 | int color = ((red + green + blue)/3 < BAYER_PATTERN_4X4[col][row] ? 0 : 255); 146 | 147 | pixels[x * 3 + 0] = color; // blue 148 | pixels[x * 3 + 1] = color; // green 149 | pixels[x * 3 + 2] = color; // red 150 | } 151 | 152 | pixels += width * 3; 153 | } 154 | }} 155 | 156 | ///////////////////////////////////////////////////////////////////////////// 157 | extern "C" { 158 | void makeDitherBayer3( unsigned char* pixels, int width, int height ) noexcept 159 | { 160 | int col = 0; 161 | int row = 0; 162 | 163 | for( int y = 0; y < height; y++ ) 164 | { 165 | row = y % 3; 166 | 167 | for( int x = 0; x < width; x++ ) 168 | { 169 | col = x % 3; 170 | 171 | const int blue = pixels[x * 3 + 0]; 172 | const int green = pixels[x * 3 + 1]; 173 | const int red = pixels[x * 3 + 2]; 174 | 175 | int color = ((red + green + blue)/3 < BAYER_PATTERN_3X3[col][row] ? 0 : 255); 176 | 177 | pixels[x * 3 + 0] = color; // blue 178 | pixels[x * 3 + 1] = color; // green 179 | pixels[x * 3 + 2] = color; // red 180 | } 181 | 182 | pixels += width * 3; 183 | } 184 | }} 185 | ///////////////////////////////////////////////////////////////////////////// 186 | extern "C" { 187 | void makeDitherBayer2( unsigned char* pixels, int width, int height ) noexcept 188 | { 189 | int col = 0; 190 | int row = 0; 191 | 192 | for( int y = 0; y < height; y++ ) 193 | { 194 | row = y & 1; // y % 2 195 | 196 | for( int x = 0; x < width; x++ ) 197 | { 198 | col = x & 1; // x % 2 199 | 200 | const int blue = pixels[x * 3 + 0]; 201 | const int green = pixels[x * 3 + 1]; 202 | const int red = pixels[x * 3 + 2]; 203 | 204 | int color = ((red + green + blue)/3 < BAYER_PATTERN_2X2[col][row] ? 0 : 255); 205 | 206 | pixels[x * 3 + 0] = color; // blue 207 | pixels[x * 3 + 1] = color; // green 208 | pixels[x * 3 + 2] = color; // red 209 | } 210 | 211 | pixels += width * 3; 212 | } 213 | }} 214 | 215 | #define f7_16 112 //const int f7 = (7 << 8) / 16; 216 | #define f5_16 80 //const int f5 = (5 << 8) / 16; 217 | #define f3_16 48 //const int f3 = (3 << 8) / 16; 218 | #define f1_16 16 //const int f1 = (1 << 8) / 16; 219 | 220 | #define FS_COEF( v, err ) (((err) * ((v) << 8)) >> 12) 221 | 222 | // Back-white Floyd-Steinberg dither 223 | extern "C" { 224 | 225 | void makeDitherFS( unsigned char* pixels, int width, int height ) noexcept 226 | { 227 | const int size = width * height; 228 | 229 | int* error = (int*)malloc( size * sizeof(int) ); 230 | 231 | // Clear the errors buffer. 232 | memset( error, 0, size * sizeof(int) ); 233 | 234 | //~~~~~~~~ 235 | 236 | int i = 0; 237 | 238 | for( int y = 0; y < height; y++ ) 239 | { 240 | unsigned char* prow = pixels + ( y * width * 3 ); 241 | 242 | for( int x = 0; x < width; x++,i++ ) 243 | { 244 | const int blue = prow[x * 3 + 0]; 245 | const int green = prow[x * 3 + 1]; 246 | const int red = prow[x * 3 + 2]; 247 | 248 | // Get the pixel gray value. 249 | int newVal = (red+green+blue)/3 + (error[i] >> 8); // PixelGray + error correction 250 | 251 | int newc = (newVal < 128 ? 0 : 255); 252 | prow[x * 3 + 0] = newc; // blue 253 | prow[x * 3 + 1] = newc; // green 254 | prow[x * 3 + 2] = newc; // red 255 | 256 | // Correction - the new error 257 | const int cerror = newVal - newc; 258 | 259 | int idx = i+1; 260 | if( x+1 < width ) 261 | error[idx] += (cerror * f7_16); 262 | 263 | idx += width - 2; 264 | if( x-1 > 0 && y+1 < height ) 265 | error[idx] += (cerror * f3_16); 266 | 267 | idx++; 268 | if( y+1 < height ) 269 | error[idx] += (cerror * f5_16); 270 | 271 | idx++; 272 | if( x+1 < width && y+1 < height ) 273 | error[idx] += (cerror * f1_16); 274 | } 275 | } 276 | 277 | free( error ); 278 | }} 279 | 280 | 281 | #define SIERRA_LITE_COEF( v, err ) ((( (err) * ((v) << 8)) >> 2) >> 8) 282 | 283 | // Black-white Sierra Lite dithering (variation of Floyd-Steinberg with less computational cost) 284 | extern "C" { 285 | 286 | void makeDitherSierraLite( unsigned char* pixels, int width, int height ) noexcept 287 | { 288 | // To avoid real number calculations, I will raise the level of INT arythmetics by shifting with 8 bits to the left ( << 8 ) 289 | // Later, when it is necessary will return to te normal level by shifting back 8 bits to the right ( >> 8 ) 290 | // X 2 291 | // 1 1 292 | // (1/4) 293 | 294 | //~~~~~~~~ 295 | 296 | const int size = width * height; 297 | 298 | int* error = (int*)malloc( size * sizeof(int) ); 299 | 300 | // Clear the errors buffer. 301 | memset( error, 0, size * sizeof(int) ); 302 | 303 | //~~~~~~~~ 304 | 305 | int i = 0; 306 | 307 | for( int y = 0; y < height; y++ ) 308 | { 309 | for( int x = 0; x < width; x++,i++ ) 310 | { 311 | const int blue = pixels[x * 3 + 0]; 312 | const int green = pixels[x * 3 + 1]; 313 | const int red = pixels[x * 3 + 2]; 314 | 315 | // Get the pixel gray value. 316 | int newVal = (red + green + blue) / 3 + error[i]; // PixelGray + error correction 317 | int newc = (newVal < 128 ? 0 : 255); 318 | 319 | pixels[x * 3 + 0] = newc; 320 | pixels[x * 3 + 1] = newc; 321 | pixels[x * 3 + 2] = newc; 322 | 323 | // Correction - the new error 324 | const int cerror = newVal - newc; 325 | 326 | int idx = i; 327 | if( x + 1 < width ) 328 | error[idx+1] += SIERRA_LITE_COEF( 2, cerror ); 329 | 330 | idx += width; 331 | if( y + 1 < height ) 332 | { 333 | if( x-1 >= 0 ) 334 | error[idx-1] += SIERRA_LITE_COEF( 1, cerror ); 335 | 336 | error[idx] += SIERRA_LITE_COEF( 1, cerror ); 337 | } 338 | } 339 | 340 | pixels += width*3; 341 | } 342 | 343 | free( error ); 344 | }} 345 | 346 | 347 | #define SIERRA_COEF( v, err ) ((( (err) * ((v) << 8)) >> 5) >> 8) 348 | 349 | extern "C" { 350 | 351 | void makeDitherSierra( unsigned char* pixels, int width, int height ) noexcept 352 | { 353 | // To avoid real number calculations, I will raise the level of INT arythmetics by shifting with 8 bits to the left ( << 8 ) 354 | // Later, when it is necessary will return to te normal level by shifting back 8 bits to the right ( >> 8 ) 355 | // X 5 3 356 | // 2 4 5 4 2 357 | // 2 3 2 358 | // (1/32) 359 | 360 | //~~~~~~~~ 361 | 362 | const int size = width * height; 363 | 364 | int* error = (int*)malloc( size * sizeof(int) ); 365 | 366 | // Clear the errors buffer. 367 | memset( error, 0, size * sizeof(int) ); 368 | 369 | //~~~~~~~~ 370 | 371 | int i = 0; 372 | 373 | for( int y = 0; y < height; y++ ) 374 | { 375 | for( int x = 0; x < width; x++,i++ ) 376 | { 377 | const int blue = pixels[x * 3 + 0]; 378 | const int green = pixels[x * 3 + 1]; 379 | const int red = pixels[x * 3 + 2]; 380 | 381 | // Get the pixel gray value. 382 | int newVal = (red + green + blue) / 3 + error[i]; // PixelGray + error correction 383 | int newc = (newVal < 128 ? 0 : 255); 384 | 385 | pixels[x * 3 + 0] = newc; 386 | pixels[x * 3 + 1] = newc; 387 | pixels[x * 3 + 2] = newc; 388 | 389 | // Correction - the new error 390 | const int cerror = newVal - newc; 391 | 392 | int idx = i; 393 | if( x + 1 < width ) 394 | error[idx+1] += SIERRA_COEF( 5, cerror ); 395 | 396 | if( x + 2 < width ) 397 | error[idx+2] += SIERRA_COEF( 3, cerror ); 398 | 399 | if( y + 1 < height ) 400 | { 401 | idx += width; 402 | if( x-2 >= 0 ) 403 | error[idx-2] += SIERRA_COEF( 2, cerror ); 404 | 405 | if( x-1 >= 0 ) 406 | error[idx-1] += SIERRA_COEF( 4, cerror ); 407 | 408 | error[idx] += SIERRA_COEF( 5, cerror ); 409 | 410 | if( x+1 < width ) 411 | error[idx+1] += SIERRA_COEF( 4, cerror ); 412 | 413 | if( x+2 < width ) 414 | error[idx+2] += SIERRA_COEF( 2, cerror ); 415 | } 416 | 417 | if( y + 2 < height ) 418 | { 419 | idx += width; 420 | if( x-1 >= 0 ) 421 | error[idx-1] += SIERRA_COEF( 2, cerror ); 422 | 423 | error[idx] += SIERRA_COEF( 3, cerror ); 424 | 425 | if( x+1 < width ) 426 | error[idx+1] += SIERRA_COEF( 2, cerror ); 427 | } 428 | } 429 | 430 | pixels += width*3; 431 | } 432 | 433 | free( error ); 434 | }} 435 | ///////////////////////////////////////////////////////////////////////////// 436 | extern "C" { 437 | 438 | int main(){ 439 | return 0; 440 | }} -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/include/fill_adj_v2_inv.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | extern char *dir; 8 | const void fill_adj(unsigned char *delta_array, unsigned char *delta_array_modded) 9 | { 10 | unsigned char ori_val = 0, modded_val = 0; 11 | int counter = 0; 12 | long t = getTick(); 13 | bool overlaps = false; 14 | memcpy(delta_array_modded, delta_array, 990000 / 4); 15 | for (int g = 0; g < 990000 / 4; g++) 16 | { 17 | modded_val = 0; 18 | ori_val = delta_array[g]; 19 | if (overlaps == true && ori_val << 6 == 0) 20 | { 21 | delta_array_modded[g] += 1; 22 | } 23 | overlaps = false; 24 | switch (ori_val) 25 | { 26 | case 1: // 00000001 27 | delta_array_modded[g] |= 5; // 00000101 28 | 29 | break; 30 | case 4: // 00000100 31 | delta_array_modded[g] |= 21; // 00010101 32 | 33 | break; 34 | case 5: // 00000101 35 | delta_array_modded[g] |= 21; // 00010101 36 | 37 | break; 38 | case 16: // 00010000 39 | delta_array_modded[g] |= 84; // 01010100 40 | 41 | break; 42 | case 17: // 00010001 43 | delta_array_modded[g] |= 85; // 01010101 44 | 45 | break; 46 | case 18: // 00010010 47 | delta_array_modded[g] |= 86; // 01010110 48 | 49 | break; 50 | case 19: // 00010011 51 | delta_array_modded[g] |= 87; // 01010111 52 | 53 | break; 54 | case 21: // 00010101 55 | delta_array_modded[g] |= 85; // 01010101 56 | 57 | break; 58 | case 25: // 00011001 59 | delta_array_modded[g] |= 89; // 01011001 60 | 61 | break; 62 | case 29: // 00011101 63 | delta_array_modded[g] |= 93; // 01011101 64 | 65 | break; 66 | case 33: // 00100001 67 | delta_array_modded[g] |= 37; // 00100101 68 | 69 | break; 70 | case 49: // 00110001 71 | delta_array_modded[g] |= 53; // 00110101 72 | 73 | break; 74 | case 64: // 01000000 75 | delta_array_modded[g] |= 80; // 01010000 76 | overlaps = true; 77 | break; 78 | case 65: // 01000001 79 | delta_array_modded[g] |= 85; // 01010101 80 | overlaps = true; 81 | break; 82 | case 66: // 01000010 83 | delta_array_modded[g] |= 82; // 01010010 84 | overlaps = true; 85 | break; 86 | case 67: // 01000011 87 | delta_array_modded[g] |= 83; // 01010011 88 | overlaps = true; 89 | break; 90 | case 68: // 01000100 91 | delta_array_modded[g] |= 85; // 01010101 92 | overlaps = true; 93 | break; 94 | case 69: // 01000101 95 | delta_array_modded[g] |= 85; // 01010101 96 | overlaps = true; 97 | break; 98 | case 70: // 01000110 99 | delta_array_modded[g] |= 86; // 01010110 100 | overlaps = true; 101 | break; 102 | case 71: // 01000111 103 | delta_array_modded[g] |= 87; // 01010111 104 | overlaps = true; 105 | break; 106 | case 72: // 01001000 107 | delta_array_modded[g] |= 88; // 01011000 108 | overlaps = true; 109 | break; 110 | case 73: // 01001001 111 | delta_array_modded[g] |= 89; // 01011001 112 | overlaps = true; 113 | break; 114 | case 74: // 01001010 115 | delta_array_modded[g] |= 90; // 01011010 116 | overlaps = true; 117 | break; 118 | case 75: // 01001011 119 | delta_array_modded[g] |= 91; // 01011011 120 | overlaps = true; 121 | break; 122 | case 76: // 01001100 123 | delta_array_modded[g] |= 92; // 01011100 124 | overlaps = true; 125 | break; 126 | case 77: // 01001101 127 | delta_array_modded[g] |= 93; // 01011101 128 | overlaps = true; 129 | break; 130 | case 78: // 01001110 131 | delta_array_modded[g] |= 94; // 01011110 132 | overlaps = true; 133 | break; 134 | case 79: // 01001111 135 | delta_array_modded[g] |= 95; // 01011111 136 | overlaps = true; 137 | break; 138 | case 80: // 01010000 139 | delta_array_modded[g] |= 84; // 01010100 140 | overlaps = true; 141 | break; 142 | case 81: // 01010001 143 | delta_array_modded[g] |= 85; // 01010101 144 | overlaps = true; 145 | break; 146 | case 82: // 01010010 147 | delta_array_modded[g] |= 86; // 01010110 148 | overlaps = true; 149 | break; 150 | case 83: // 01010011 151 | delta_array_modded[g] |= 87; // 01010111 152 | overlaps = true; 153 | break; 154 | case 84: // 01010100 155 | delta_array_modded[g] |= 85; // 01010101 156 | overlaps = true; 157 | break; 158 | case 97: // 01100001 159 | delta_array_modded[g] |= 101; // 01100101 160 | overlaps = true; 161 | break; 162 | case 100: // 01100100 163 | delta_array_modded[g] |= 101; // 01100101 164 | overlaps = true; 165 | break; 166 | case 113: // 01110001 167 | delta_array_modded[g] |= 117; // 01110101 168 | overlaps = true; 169 | break; 170 | case 116: // 01110100 171 | delta_array_modded[g] |= 117; // 01110101 172 | overlaps = true; 173 | break; 174 | case 129: // 10000001 175 | delta_array_modded[g] |= 133; // 10000101 176 | 177 | break; 178 | case 132: // 10000100 179 | delta_array_modded[g] |= 149; // 10010101 180 | 181 | break; 182 | case 133: // 10000101 183 | delta_array_modded[g] |= 149; // 10010101 184 | 185 | break; 186 | case 145: // 10010001 187 | delta_array_modded[g] |= 149; // 10010101 188 | 189 | break; 190 | case 161: // 10100001 191 | delta_array_modded[g] |= 165; // 10100101 192 | 193 | break; 194 | case 177: // 10110001 195 | delta_array_modded[g] |= 181; // 10110101 196 | 197 | break; 198 | case 193: // 11000001 199 | delta_array_modded[g] |= 197; // 11000101 200 | 201 | break; 202 | case 196: // 11000100 203 | delta_array_modded[g] |= 213; // 11010101 204 | 205 | break; 206 | case 197: // 11000101 207 | delta_array_modded[g] |= 213; // 11010101 208 | 209 | break; 210 | case 209: // 11010001 211 | delta_array_modded[g] |= 213; // 11010101 212 | 213 | break; 214 | case 225: // 11100001 215 | delta_array_modded[g] |= 229; // 11100101 216 | 217 | break; 218 | case 96: // 01100000 219 | //modded_val |= delta_array[g]; 220 | overlaps = true; 221 | break; 222 | case 112: // 01110000 223 | //modded_val |= delta_array[g]; 224 | overlaps = true; 225 | break; 226 | case 88: // 01011000 227 | //modded_val |= delta_array[g]; 228 | overlaps = true; 229 | break; 230 | case 104: // 01101000 231 | //modded_val |= delta_array[g]; 232 | overlaps = true; 233 | break; 234 | case 120: // 01111000 235 | //modded_val |= delta_array[g]; 236 | overlaps = true; 237 | break; 238 | case 92: // 01011100 239 | //modded_val |= delta_array[g]; 240 | overlaps = true; 241 | break; 242 | case 108: // 01101100 243 | //modded_val |= delta_array[g]; 244 | overlaps = true; 245 | break; 246 | case 124: // 01111100 247 | //modded_val |= delta_array[g]; 248 | overlaps = true; 249 | break; 250 | case 85: // 01010101 251 | //modded_val |= delta_array[g]; 252 | overlaps = true; 253 | break; 254 | case 101: // 01100101 255 | //modded_val |= delta_array[g]; 256 | overlaps = true; 257 | break; 258 | case 117: // 01110101 259 | //modded_val |= delta_array[g]; 260 | overlaps = true; 261 | break; 262 | case 89: // 01011001 263 | //modded_val |= delta_array[g]; 264 | overlaps = true; 265 | break; 266 | case 105: // 01101001 267 | //modded_val |= delta_array[g]; 268 | overlaps = true; 269 | break; 270 | case 121: // 01111001 271 | //modded_val |= delta_array[g]; 272 | overlaps = true; 273 | break; 274 | case 93: // 01011101 275 | //modded_val |= delta_array[g]; 276 | overlaps = true; 277 | break; 278 | case 109: // 01101101 279 | //modded_val |= delta_array[g]; 280 | overlaps = true; 281 | break; 282 | case 125: // 01111101 283 | //modded_val |= delta_array[g]; 284 | overlaps = true; 285 | break; 286 | case 98: // 01100010 287 | //modded_val |= delta_array[g]; 288 | overlaps = true; 289 | break; 290 | case 114: // 01110010 291 | //modded_val |= delta_array[g]; 292 | overlaps = true; 293 | break; 294 | case 86: // 01010110 295 | //modded_val |= delta_array[g]; 296 | overlaps = true; 297 | break; 298 | case 102: // 01100110 299 | //modded_val |= delta_array[g]; 300 | overlaps = true; 301 | break; 302 | case 118: // 01110110 303 | //modded_val |= delta_array[g]; 304 | overlaps = true; 305 | break; 306 | case 90: // 01011010 307 | //modded_val |= delta_array[g]; 308 | overlaps = true; 309 | break; 310 | case 106: // 01101010 311 | //modded_val |= delta_array[g]; 312 | overlaps = true; 313 | break; 314 | case 122: // 01111010 315 | //modded_val |= delta_array[g]; 316 | overlaps = true; 317 | break; 318 | case 94: // 01011110 319 | //modded_val |= delta_array[g]; 320 | overlaps = true; 321 | break; 322 | case 110: // 01101110 323 | //modded_val |= delta_array[g]; 324 | overlaps = true; 325 | break; 326 | case 126: // 01111110 327 | //modded_val |= delta_array[g]; 328 | overlaps = true; 329 | break; 330 | case 99: // 01100011 331 | //modded_val |= delta_array[g]; 332 | overlaps = true; 333 | break; 334 | case 87: // 01010111 335 | //modded_val |= delta_array[g]; 336 | overlaps = true; 337 | break; 338 | case 103: // 01100111 339 | //modded_val |= delta_array[g]; 340 | overlaps = true; 341 | break; 342 | case 119: // 01110111 343 | //modded_val |= delta_array[g]; 344 | overlaps = true; 345 | break; 346 | case 91: // 01011011 347 | //modded_val |= delta_array[g]; 348 | overlaps = true; 349 | break; 350 | case 107: // 01101011 351 | //modded_val |= delta_array[g]; 352 | overlaps = true; 353 | break; 354 | case 123: // 01111011 355 | //modded_val |= delta_array[g]; 356 | overlaps = true; 357 | break; 358 | case 95: // 01011111 359 | //modded_val |= delta_array[g]; 360 | overlaps = true; 361 | break; 362 | case 111: // 01101111 363 | //modded_val |= delta_array[g]; 364 | overlaps = true; 365 | break; 366 | case 127: // 01111111 367 | //modded_val |= delta_array[g]; 368 | overlaps = true; 369 | break; 370 | //default: 371 | //modded_val |= delta_array[g]; 372 | //break; } 373 | //delta_array_modded[g] = modded_val; 374 | } 375 | // array_to_file(delta_array_modded, g, dir, "delta_array_modded", 0); 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /pc_monitor/pc_host_app/draw_cursor.py: -------------------------------------------------------------------------------- 1 | import pyautogui, time, io, platform 2 | from PIL import Image 3 | 4 | windows= None; linux = None 5 | if platform.system() == 'Linux': linux = True; 6 | elif platform.system() == 'Windows': windows = True; 7 | 8 | class cursor_coor: 9 | def __init__(self): 10 | self.x = 0 11 | self.y = 0 12 | curr_coor = cursor_coor() 13 | prev_coor = cursor_coor() 14 | 15 | if windows: 16 | import win32gui 17 | pos = win32gui.GetCursorPos() 18 | prev_coor.x = pos[0]; prev_coor.y = pos[1] 19 | 20 | elif linux: 21 | prev_coor = pyautogui.position() 22 | 23 | def process_string(current_byte_string): 24 | inverted_output0= '' 25 | inverted_output1= '' 26 | current_byte_string_0= ''.join(current_byte_string[0:16]) 27 | current_byte_string_1= ''.join(current_byte_string[16:32]) 28 | 29 | current_byte_string = ''.join(current_byte_string) 30 | # for x in range(16, 0, -1): 31 | # pixel_pos = (x-1) 32 | # inverted_output0 += current_byte_string_0[pixel_pos:pixel_pos+1] 33 | # inverted_output1 += current_byte_string_1[pixel_pos:pixel_pos+1] 34 | 35 | integer_rep0 = int(current_byte_string[0:16], 2) 36 | integer_rep1 = int(current_byte_string[16:32], 2) 37 | #integer_rep0 = int(inverted_output0, 2) 38 | #integer_rep1 = int(inverted_output1, 2) 39 | target_byte0 = integer_rep0.to_bytes(2, 'big') 40 | target_byte1 = integer_rep1.to_bytes(2, 'big') 41 | fin = target_byte0 + target_byte1 42 | return fin 43 | 44 | #def init_cursor(conf): 45 | 46 | 47 | 48 | def draw_cursor_1bpp(conf, byte_string_raw): 49 | 50 | x_offset = conf.x_offset 51 | y_offset = conf.y_offset 52 | 53 | width_res = conf.width 54 | height_res = conf.height 55 | width_res2 = conf.width_res2 56 | height_res2 = conf.height_res2 57 | 58 | global curr_coor; global prev_coor 59 | 60 | if linux: 61 | prev_coor = curr_coor 62 | curr_coor = pyautogui.position() 63 | elif windows: 64 | prev_coor.x = curr_coor.x 65 | prev_coor.y = curr_coor.y 66 | pos = win32gui.GetCursorPos() 67 | curr_coor.x = pos[0]; curr_coor.y = pos[1] 68 | 69 | if curr_coor.x >= x_offset and curr_coor.x <= width_res2 and curr_coor.y >= y_offset and curr_coor.y <= height_res2-22: 70 | t0 = time.time() 71 | x = int((curr_coor.x - x_offset) / 8) 72 | y = int((curr_coor.y - y_offset +1)) 73 | #print(f"inside : curr_coor.x {curr_coor.x } curr_coor.y {curr_coor.y }, y {y}, x {x}, ") 74 | 75 | 76 | if conf.rotation == 180: 77 | x = width_res-x 78 | y = height_res-y 79 | xrem = 7- (curr_coor.x % 8) 80 | y-=8 81 | x-=4 82 | 83 | else: 84 | xrem = curr_coor.x % 8 85 | y+=16 86 | 87 | yrem = (curr_coor.y - y_offset) % 8 88 | 89 | line_coor = (y*width_res//8) + x #+ 62 90 | #print(f"inv x {x}, line_coor {line_coor}") 91 | if conf.rotation == 0: 92 | for j in range(16): 93 | oribyte = int.from_bytes(byte_string_raw[line_coor+(j*-width_res//8):line_coor+4+(j*-width_res//8)], 'big') 94 | current_byte_string = list(format(oribyte, "b").zfill(32)) 95 | 96 | current_byte_string[xrem] = '0' 97 | try: current_byte_string[xrem+1] = '0' 98 | except: pass 99 | try: 100 | current_byte_string[xrem+(16-j)] = '0' 101 | current_byte_string[xrem+(16-j+1)] = '0' 102 | except: pass 103 | #if j > 2: 104 | for x in range(16-j-2): 105 | try:current_byte_string[xrem+2+x] = '1' 106 | except: pass 107 | 108 | fin = process_string(current_byte_string) 109 | #current_byte_string = ''.join(current_byte_string) 110 | #integer_rep1 = int(current_byte_string, 2) 111 | #integer_rep0 = int(inverted_output0, 2) 112 | #integer_rep1 = int(inverted_output1, 2) 113 | #fin = integer_rep1.to_bytes(2, 'big') 114 | byte_string_raw[line_coor+(j*-width_res//8):line_coor+4+(j*-width_res//8)] = fin 115 | 116 | j-=17 117 | oribyte = int.from_bytes(byte_string_raw[line_coor+(j*-width_res//8):line_coor+4+(j*-width_res//8)], 'big') 118 | current_byte_string = list(format(oribyte, "b").zfill(32)) 119 | for x in range(16): 120 | try: current_byte_string[xrem+x] = '0' 121 | except:pass 122 | 123 | fin = process_string(current_byte_string) 124 | byte_string_raw[line_coor+(j*-width_res//8):line_coor+4+(j*-width_res//8)] = fin 125 | j+=1 126 | byte_string_raw[line_coor+(j*-width_res//8):line_coor+4+(j*-width_res//8)] = fin 127 | 128 | 129 | else: 130 | for j in range(16): 131 | oribyte = int.from_bytes(byte_string_raw[line_coor+(j*-width_res//8):line_coor+4+(j*-width_res//8)], 'big') 132 | current_byte_string = list(format(oribyte, "b").zfill(32)) 133 | 134 | current_byte_string[xrem-8] = '0' 135 | if xrem+1 < 8: 136 | try: current_byte_string[xrem+1-8] = '0' 137 | except: pass 138 | 139 | 140 | 141 | if j > 2: 142 | for x in range(j-2): 143 | try:current_byte_string[-7+xrem-2-x] = '1' 144 | except: pass 145 | 146 | try: 147 | current_byte_string[xrem+(-8-j)] = '0' 148 | current_byte_string[xrem+(-8-j+1)] = '0' 149 | except: current_byte_string[xrem+(-8-j-1)] = '0' 150 | fin = process_string(current_byte_string) 151 | 152 | #current_byte_string = ''.join(current_byte_string) 153 | #integer_rep1 = int(current_byte_string, 2) 154 | 155 | #fin = integer_rep1.to_bytes(2, 'big') 156 | byte_string_raw[line_coor+(j*-width_res//8):line_coor+4+(j*-width_res//8)] = fin 157 | 158 | j-=1 159 | oribyte = int.from_bytes(byte_string_raw[line_coor+(j*-width_res//8):line_coor+4+(j*-width_res//8)], 'big') 160 | current_byte_string = list(format(oribyte, "b").zfill(32)) 161 | for x in range(12): 162 | try: current_byte_string[xrem+x+12] = '0' 163 | except:pass 164 | 165 | fin = process_string(current_byte_string) 166 | byte_string_raw[line_coor+(j*-width_res//8):line_coor+4+(j*-width_res//8)] = fin 167 | j+=1 168 | byte_string_raw[line_coor+(j*-width_res//8):line_coor+4+(j*-width_res//8)] = fin 169 | 170 | if prev_coor.x == curr_coor.x and prev_coor.y == curr_coor.y: 171 | #print(f" not moved px {prev_coor.x}, cx {curr_coor.x}, py {prev_coor.y}, cy {curr_coor.y}") 172 | return 0 173 | else: 174 | #print(f" moved :: px {prev_coor.x}, cx {curr_coor.x}, py {prev_coor.y}, cy {curr_coor.y}") 175 | return 1 176 | else: 177 | #print(f"outside : curr_coor.x {curr_coor.x } curr_coor.y {curr_coor.y }") 178 | return 0 179 | 180 | def did_mouse_move(ctx): 181 | global curr_coor; global prev_coor 182 | 183 | if linux: 184 | prev_coor = curr_coor 185 | curr_coor = pyautogui.position() 186 | elif windows: 187 | prev_coor.x = curr_coor.x 188 | prev_coor.y = curr_coor.y 189 | pos = win32gui.GetCursorPos() 190 | curr_coor.x = pos[0]; curr_coor.y = pos[1] 191 | 192 | if curr_coor.x >= ctx.x_offset and curr_coor.x <= ctx.width_res2 and curr_coor.y >= ctx.y_offset and curr_coor.y <= ctx.height_res2-22: 193 | if prev_coor.x == curr_coor.x and prev_coor.y == curr_coor.y: 194 | # print(f" not moved px {prev_coor.x}, cx {curr_coor.x}, py {prev_coor.y}, cy {curr_coor.y}") 195 | return 0 196 | else: 197 | # print(f" moved :: px {prev_coor.x}, cx {curr_coor.x}, py {prev_coor.y}, cy {curr_coor.y}") 198 | return 1 199 | else: 200 | # print(f"outside : curr_coor.x {curr_coor.x } curr_coor.y {curr_coor.y }") 201 | return 0 202 | 203 | def generate_cursor(): 204 | wp = b'\xff\xff\xff\xff' 205 | bp = b'\x00\x00\x00\xff' 206 | current_line = [] 207 | current_line_bytes = [] 208 | global byte_array_cursor 209 | byte_array_cursor = [] 210 | cursor = [[bp]] 211 | for y in range(12): 212 | current_line.append(bp) 213 | current_line.append(bp) 214 | 215 | current_line_bytes = bp 216 | current_line_bytes += bp 217 | 218 | for l in range(y-2): 219 | current_line.append(wp) 220 | current_line_bytes += wp 221 | current_line.append(bp) 222 | current_line.append(bp) 223 | 224 | current_line_bytes += bp 225 | current_line_bytes += bp 226 | 227 | cursor.append(current_line[:]) 228 | current_line.clear() 229 | 230 | current_line = [bp, bp, wp, wp, wp, wp, wp, bp, bp, bp, bp, bp, bp] 231 | cursor.append(current_line[:]) 232 | current_line.clear() 233 | current_line = [bp, bp, wp, wp, bp, wp, bp, bp] 234 | cursor.append(current_line[:]) 235 | current_line.clear() 236 | current_line = [bp, bp, wp, bp, wp, bp, wp, bp, bp] 237 | cursor.append(current_line[:]) 238 | current_line.clear() 239 | current_line = [bp, bp, bp, wp, wp, bp, wp, bp, bp] 240 | cursor.append(current_line[:]) 241 | current_line.clear() 242 | current_line = [bp, bp, wp, wp, wp, wp, bp, wp, bp, bp] 243 | cursor.append(current_line[:]) 244 | current_line.clear() 245 | current_line = [bp, wp, wp, bp] # 6 spaces before , list number 18 246 | cursor.append(current_line[:]) 247 | current_line.clear() 248 | current_line = [bp, bp] # 7 spaces before, list number 19 249 | cursor.append(current_line[:]) 250 | current_line.clear() 251 | for k in range(20): 252 | current_line2 = b"".join(cursor[k]) 253 | byte_array_cursor.append(current_line2[:]) 254 | 255 | 256 | def draw_cursor(conf, sct_img): 257 | 258 | 259 | x_offset = conf.x_offset 260 | y_offset = conf.y_offset 261 | 262 | width_res = conf.width 263 | height_res = conf.height 264 | width_res2 = conf.width_res2 265 | height_res2 = conf.height_res2 266 | 267 | global curr_coor; global prev_coor 268 | 269 | if linux: 270 | prev_coor = curr_coor 271 | curr_coor = pyautogui.position() 272 | elif windows: 273 | prev_coor.x = curr_coor.x 274 | prev_coor.y = curr_coor.y 275 | pos = win32gui.GetCursorPos() 276 | curr_coor.x = pos[0]; curr_coor.y = pos[1] 277 | 278 | if curr_coor.x >= x_offset and curr_coor.x <= width_res2 and curr_coor.y >= y_offset and curr_coor.y <= height_res2: 279 | x_cursor = curr_coor.x - x_offset 280 | y_cursor = curr_coor.y - y_offset 281 | linear_coor = (y_cursor*width_res*4) + x_cursor*4 282 | for h in range(15): 283 | sct_img.raw[linear_coor+(h*width_res*4):linear_coor+( 284 | h*width_res*4)+len(byte_array_cursor[h])] = byte_array_cursor[h] 285 | # print(sct_img.raw[linear_coor+(h*width_res*4):linear_coor+(h*width_res*4)+len(byte_array_cursor[h])]) 286 | h += 1 287 | sct_img.raw[linear_coor+(h*width_res*4):linear_coor + 288 | (h*width_res*4)+16] = byte_array_cursor[h][0:16] 289 | offset = 16 + 4 290 | sct_img.raw[linear_coor+(h*width_res*4)+offset:linear_coor + 291 | (h*width_res*4)+offset+16] = byte_array_cursor[h][20:36] 292 | h += 1 293 | offset = 12 + 8 294 | sct_img.raw[linear_coor+(h*width_res*4):linear_coor + 295 | (h*width_res*4)+3*4] = byte_array_cursor[h][0:3*4] 296 | sct_img.raw[linear_coor+(h*width_res*4)+offset:linear_coor + 297 | (h*width_res*4)+offset+16] = byte_array_cursor[h][20:36] 298 | h += 1 299 | offset = 8+16 300 | sct_img.raw[linear_coor+(h*width_res*4):linear_coor + 301 | (h*width_res*4)+2*4] = byte_array_cursor[h][0:2*4] 302 | sct_img.raw[linear_coor+(h*width_res*4)+offset:linear_coor + 303 | (h*width_res*4)+offset+16] = byte_array_cursor[h][24:40] 304 | # h+=1 305 | sct_img.raw[linear_coor+(h*width_res*4)+offset:linear_coor + 306 | (h*width_res*4)+offset+16] = byte_array_cursor[18] 307 | offset += 4 308 | h += 1 309 | sct_img.raw[linear_coor+(h*width_res*4)+offset:linear_coor + 310 | (h*width_res*4)+offset+8] = byte_array_cursor[19] 311 | # print("inside") 312 | if prev_coor.x == curr_coor.x and prev_coor.y == curr_coor.y: 313 | #print(f" not moved px {prev_coor.x}, cx {curr_coor.x}, py {prev_coor.y}, cy {curr_coor.y}") 314 | return 0 315 | else: 316 | #print(f" moved :: px {prev_coor.x}, cx {curr_coor.x}, py {prev_coor.y}, cy {curr_coor.y}") 317 | return 1 318 | else: 319 | #print(f"outside : curr_coor.x {curr_coor.x } curr_coor.y {curr_coor.y }") 320 | return 0 321 | def paste_cursor(ctx, image_file): 322 | 323 | 324 | global curr_coor; global prev_coor; global cursor 325 | if linux: 326 | prev_coor = curr_coor 327 | curr_coor = pyautogui.position() 328 | elif windows: 329 | prev_coor.x = curr_coor.x 330 | prev_coor.y = curr_coor.y 331 | pos = win32gui.GetCursorPos() 332 | curr_coor.x = pos[0]; curr_coor.y = pos[1] 333 | 334 | if curr_coor.x >= ctx.x_offset and curr_coor.x <= ctx.width_res2 and curr_coor.y >= ctx.y_offset and curr_coor.y <= ctx.height_res2-22: 335 | image_file.paste(ctx.cursor,box=(curr_coor.x-ctx.x_offset,curr_coor.y-ctx.y_offset),mask=ctx.cursor) 336 | if prev_coor.x == curr_coor.x and prev_coor.y == curr_coor.y: 337 | #print(f" not moved px {prev_coor.x}, cx {curr_coor.x}, py {prev_coor.y}, cy {curr_coor.y}") 338 | return 0 339 | else: 340 | #print(f" moved :: px {prev_coor.x}, cx {curr_coor.x}, py {prev_coor.y}, cy {curr_coor.y}") 341 | return 1 342 | else: 343 | #print(f"outside : curr_coor.x {curr_coor.x } curr_coor.y {curr_coor.y }") 344 | return 0 345 | 346 | -------------------------------------------------------------------------------- /pc_monitor/main/main.c: -------------------------------------------------------------------------------- 1 | /* Pc monitor application for Epdiy controller board. (work in progress) 2 | * More info at https://github.com/amadeok/Epdiy-PC-screen-monitor 3 | */ 4 | 5 | #include "esp_heap_caps.h" 6 | #include "esp_log.h" 7 | #include "esp_timer.h" 8 | #include "esp_types.h" 9 | #include "freertos/FreeRTOS.h" 10 | #include "freertos/task.h" 11 | #include "sdkconfig.h" 12 | #include 13 | #include 14 | #include "esp_system.h" 15 | #include "esp_wifi.h" 16 | #include "esp_event.h" 17 | #include "esp_netif.h" 18 | 19 | #include "lwip/err.h" 20 | #include "lwip/sockets.h" 21 | #include "lwip/sys.h" 22 | #include 23 | 24 | #include "freertos/event_groups.h" 25 | #include "esp_event_loop.h" 26 | #include "nvs_flash.h" 27 | #include "pc_monitor.h" 28 | #include "epd_driver.h" 29 | //#include "display_ops.h" 30 | 31 | #define WIFI_SSID "wifi_ssid" 32 | #define WIFI_PASS "wifi_password" 33 | 34 | #define FT245MODE 0 35 | 36 | #define PORT 3333 37 | int buf_size; 38 | int sock = 0; 39 | 40 | // Event group 41 | static EventGroupHandle_t wifi_event_group; 42 | const int CONNECTED_BIT = BIT0; 43 | static const char *TAG = "pc_monitor"; 44 | 45 | // Wifi event handler 46 | static esp_err_t event_handler(void *ctx, system_event_t *event) 47 | { 48 | switch (event->event_id) 49 | { 50 | 51 | case SYSTEM_EVENT_STA_START: 52 | esp_wifi_connect(); 53 | break; 54 | 55 | case SYSTEM_EVENT_STA_GOT_IP: 56 | xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); 57 | break; 58 | 59 | case SYSTEM_EVENT_STA_DISCONNECTED: 60 | xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); 61 | ESP_LOGI(TAG, "Wi-Fi disconnected, trying to reconnect..."); 62 | esp_err_t err = esp_wifi_connect(); 63 | if (err == ESP_ERR_WIFI_NOT_STARTED) 64 | { 65 | return; 66 | } 67 | ESP_ERROR_CHECK(err); 68 | break; 69 | 70 | default: 71 | break; 72 | } 73 | 74 | return ESP_OK; 75 | } 76 | 77 | void init_memory() 78 | { 79 | printf("sizes %d \n", chunk_size + extra_bytes); 80 | if (nb_chunks > 0) 81 | fc0 = (uint8_t *)heap_caps_malloc((chunk_size + extra_bytes) * sizeof(uint8_t), MALLOC_CAP_SPIRAM); 82 | if (nb_chunks > 1 || esp32_multithread >= 1) 83 | fc1 = (uint8_t *)heap_caps_malloc((chunk_size + extra_bytes) * sizeof(uint8_t), MALLOC_CAP_SPIRAM); 84 | if (nb_chunks > 2) 85 | fc2 = (uint8_t *)heap_caps_malloc((chunk_size + extra_bytes) * sizeof(uint8_t), MALLOC_CAP_SPIRAM); 86 | if (nb_chunks > 3) 87 | fc3 = (uint8_t *)heap_caps_malloc((chunk_size + extra_bytes) * sizeof(uint8_t), MALLOC_CAP_SPIRAM); 88 | if (nb_chunks > 4) 89 | fc4 = (uint8_t *)heap_caps_malloc((chunk_size + extra_bytes) * sizeof(uint8_t), MALLOC_CAP_SPIRAM); 90 | if (nb_chunks > 5) 91 | fc5 = (uint8_t *)heap_caps_malloc((chunk_size + extra_bytes) * sizeof(uint8_t), MALLOC_CAP_SPIRAM); 92 | if (nb_chunks > 6) 93 | fc6 = (uint8_t *)heap_caps_malloc((chunk_size + extra_bytes) * sizeof(uint8_t), MALLOC_CAP_SPIRAM); 94 | if (nb_chunks > 7) 95 | fc7 = (uint8_t *)heap_caps_malloc((chunk_size + extra_bytes) * sizeof(uint8_t), MALLOC_CAP_SPIRAM); 96 | if (nb_chunks > 8) 97 | fc8 = (uint8_t *)heap_caps_malloc((chunk_size + extra_bytes) * sizeof(uint8_t), MALLOC_CAP_SPIRAM); 98 | if (nb_chunks > 9) 99 | fc9 = (uint8_t *)heap_caps_malloc((chunk_size + extra_bytes) * sizeof(uint8_t), MALLOC_CAP_SPIRAM); 100 | } 101 | void free_memory() 102 | { 103 | if (nb_chunks > 0) 104 | heap_caps_free(fc0); 105 | if (nb_chunks > 1) 106 | heap_caps_free(fc1); 107 | if (nb_chunks > 2) 108 | heap_caps_free(fc2); 109 | if (nb_chunks > 3) 110 | heap_caps_free(fc3); 111 | if (nb_chunks > 4) 112 | heap_caps_free(fc4); 113 | if (nb_chunks > 5) 114 | heap_caps_free(fc5); 115 | if (nb_chunks > 6) 116 | heap_caps_free(fc6); 117 | if (nb_chunks > 7) 118 | heap_caps_free(fc7); 119 | if (nb_chunks > 8) 120 | heap_caps_free(fc8); 121 | if (nb_chunks > 9) 122 | heap_caps_free(fc9); 123 | 124 | heap_caps_free(compressed_chunk); 125 | heap_caps_free(chunk_lenghts); 126 | heap_caps_free(chunk_lenghts_int); 127 | heap_caps_free(line_changed); 128 | heap_caps_free(total_lines_changed); 129 | heap_caps_free(draw_rmt_times); 130 | heap_caps_free(per_frame_wifi_settings); 131 | } 132 | 133 | void check_conc() 134 | { 135 | while (1) 136 | { 137 | if (downloader_busy == 1 && renderer_busy == 1) 138 | printf("### busys %d, %d ###\n ", downloader_busy, renderer_busy); 139 | vTaskDelay(2 / portTICK_PERIOD_MS); 140 | } 141 | } 142 | 143 | int end_session() 144 | { 145 | printf("Powering off Epdiy board \n "); 146 | free_memory(); 147 | epd_poweroff(); 148 | stop = 1; 149 | clearing = 0; 150 | memset(clear, 0, 2); 151 | return -1; 152 | } 153 | 154 | // Main task 155 | tcpip_adapter_ip_info_t wifi_task(void *pvParameter) 156 | { 157 | if (heap_caps_check_integrity_all(true) == 1) 158 | ESP_LOGI(TAG, "Checking heap integrity: OK "); 159 | else 160 | ESP_LOGI(TAG, "Heap is corrupted"); 161 | // wait for connection 162 | printf("Main task: waiting for connection to the wifi network... \n"); 163 | xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, 50000000); 164 | printf("connected!\n"); 165 | 166 | // print the local IP address 167 | tcpip_adapter_ip_info_t ip_info; 168 | ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info)); 169 | printf("IP Address: %s\n", ip4addr_ntoa(&ip_info.ip)); 170 | printf("Subnet mask: %s\n", ip4addr_ntoa(&ip_info.netmask)); 171 | printf("Gateway: %s\n", ip4addr_ntoa(&ip_info.gw)); 172 | return ip_info; 173 | // while (1) 174 | // vTaskDelay(4000 / portTICK_PERIOD_MS); 175 | } 176 | void rle_extract1(int compressed_size, uint8_t *decompressed_ptr, uint8_t *compressed) 177 | { 178 | if (compressed == NULL) 179 | printf("compress NULL\n"); 180 | if (decompressed_ptr == NULL) 181 | printf("decompressed_ptr NULL\n"); 182 | 183 | int counter, counter2, j = 0; 184 | 185 | counter2 = 0, counter = 0, buf_size = 4096; 186 | int offset = 0; 187 | int8_t i = 0; 188 | while (counter < compressed_size) 189 | { 190 | // if (counter2 > 45000) 191 | // vTaskDelay(200 / portTICK_PERIOD_MS); 192 | 193 | i = compressed[counter]; 194 | // printf("counter %d, CAC: %hhx \n", counter, compressed[counter]); 195 | counter++; 196 | j = compressed[counter] & 0xff; 197 | // printf("counter %d, CAC: %hhx \n", counter, compressed[counter]); 198 | counter++; 199 | 200 | if (i < 0) 201 | { 202 | offset = (i + 130); 203 | // if (counter2 > 45000) 204 | //printf("offset %*d \n\n", space, offset); 205 | switch (j) 206 | { 207 | case 0: 208 | memcpy(decompressed_ptr + counter2, array_with_zeros, offset); 209 | counter2 += offset; 210 | break; 211 | // case 85: 212 | // memcpy(decompressed_ptr + counter2, draw_black_bytes, offset); 213 | // counter2 += offset; 214 | // break; 215 | // case 170: 216 | // memcpy(decompressed_ptr + counter2, draw_white_bytes, offset); 217 | // counter2 += offset; 218 | break; 219 | default: 220 | for (int f = 0; f < i + 130; f++) 221 | { 222 | decompressed_ptr[counter2] = j; 223 | counter2++; 224 | } 225 | break; 226 | } 227 | } 228 | else if (i >= 0) 229 | { 230 | for (int f = 0; f < i; f++) 231 | { 232 | decompressed_ptr[counter2] = j; 233 | // if (counter2 > 45000) 234 | // printf(" %*d ", space, j); 235 | j = compressed[counter++] & 0xff; 236 | counter2++; 237 | } 238 | decompressed_ptr[counter2] = j; 239 | 240 | counter2++; 241 | } 242 | } 243 | } 244 | 245 | void power_on_driver() 246 | { 247 | printf("Powering on Epdiy board \n "); 248 | epd_poweron(); 249 | printf("epd_poweron \n"); 250 | 251 | volatile uint32_t t1 = xTaskGetTickCount(); 252 | epd_clear(); 253 | volatile uint32_t t2 = xTaskGetTickCount(); 254 | printf("EPD clear took %dms.\n", t2 - t1); 255 | vTaskDelay(300 / portTICK_PERIOD_MS); 256 | } 257 | 258 | int send_compressed(int compressed_size) // for debugging 259 | { 260 | 261 | int buf_size = 4096 * 5; 262 | if (compressed_size < buf_size) 263 | buf_size = compressed_size; 264 | int tott = 0, rett = 0, tot_times_t = compressed_size / buf_size; 265 | for (int g = 0; g < tot_times_t; g++) // g< tot_times 266 | { 267 | rett = send(sock, compressed_chunk + tott, buf_size, 0); 268 | tott += rett; 269 | } 270 | rett = send(sock, compressed_chunk + tott, compressed_size - tott, 0); 271 | return tott; 272 | } 273 | 274 | int send_decompressed(uint8_t *decompressed_chunck) // for debugging 275 | { 276 | int buf_size2 = 4096 * 5, tot = 0, len = 0; 277 | do 278 | { 279 | len = send(sock, decompressed_chunck + tot, buf_size2, 0); 280 | // printf("len %d, tot %d\n", len, tot); 281 | tot += len; 282 | if (chunk_size - tot < 4096 * 6) 283 | { 284 | buf_size2 = chunk_size - tot; 285 | } 286 | } while (tot < chunk_size); 287 | return tot; 288 | } 289 | 290 | void print_values(int tot) // for debugging 291 | { 292 | for (int h = 0; h < 10; h++) 293 | printf("%03d", compressed_chunk[tot + h]); 294 | printf("\n"); 295 | 296 | for (int h = 0; h < 10; h++) 297 | printf(" %d ", (tot + h)); 298 | printf("\n"); 299 | } 300 | 301 | int set_download_pointer(int chunk_number) 302 | { 303 | int buf = chunk_number; 304 | long t0, t1; 305 | if (esp32_multithread == 2) 306 | { 307 | // t0 = xTaskGetTickCount(); 308 | while (downloader_frame_counter - renderer_frame_counter > 1 || clearing == 1 || renderer_busy == 1) 309 | { 310 | vTaskDelay(3 / portTICK_PERIOD_MS); 311 | // printf("downloader waiting %d, \n", busy[current_buffer]); 312 | } 313 | // t1 = xTaskGetTickCount(); 314 | // printf("d waited : %lu | td1 td0: %lu, %lu \n", t1 - t0, t0, t1); 315 | 316 | buf = back_buffer(); 317 | } 318 | else 319 | buf = chunk_number; 320 | 321 | if (chunk_lenghts_int[chunk_number] > chunk_size / 100 * selective_compression && selective_compression != 0) 322 | { 323 | where_to_download = get_current_chunk_ptr(buf); 324 | download_size = chunk_size; 325 | #if DEBUG_MSGs == 1 326 | printf("receving uncompressed framebuffer %d\n", chunk_lenghts_int[chunk_number]); 327 | #endif 328 | need_to_extract = 0; 329 | } 330 | else 331 | { 332 | where_to_download = compressed_chunk; 333 | download_size = chunk_lenghts_int[chunk_number]; 334 | #if DEBUG_MSGs == 1 335 | printf("receving compressed framebuffer %d\n", download_size); 336 | #endif 337 | need_to_extract = 1; 338 | } 339 | 340 | return need_to_extract; 341 | // printf("where_to_download %p, download_size %d\n", where_to_download, download_size); 342 | } 343 | int N = 0; 344 | 345 | void ch() 346 | { 347 | if (heap_caps_check_integrity_all(true) == 1) 348 | ESP_LOGI(TAG, "Checking heap integrity: OK fun %d", N); 349 | else 350 | ESP_LOGI(TAG, "Heap is corrupted fun %d", N); 351 | N++; 352 | } 353 | 354 | static void download_and_extract(const int sock) 355 | 356 | { 357 | uint8_t *ptr_m; 358 | 359 | downloader_frame_counter = 0; 360 | stop = 0; 361 | if (esp32_multithread == 2) 362 | xSemaphoreGive(begin); 363 | 364 | while (1) 365 | { 366 | #if DEBUG_MSGs == 2 367 | printf("d0 download_and_extract loop \n"); 368 | #endif 369 | 370 | int len = 0, tot = 0, compressed_size, buf_size = 4096 * 5; 371 | int delta = 0; 372 | downloader_chunk_counter = 0; 373 | send(sock, "ready0", 6, 0); 374 | recv(sock, per_frame_wifi_settings, per_frame_wifi_settings_size, 0); 375 | send(sock, per_frame_wifi_settings, per_frame_wifi_settings_size, 0); 376 | memcpy(draw_rmt_times, per_frame_wifi_settings + 6, nb_rmt_times * sizeof(int16_t)); 377 | 378 | mode = per_frame_wifi_settings[1]; 379 | 380 | #if DEBUG_MSGs == 1 381 | printf("mode: %d\n", mode); 382 | printf("rmt high times: "); 383 | for (int l = 0; l < nb_rmt_times; l++) 384 | printf(" %d ", draw_rmt_times[l]); 385 | printf("\n"); 386 | #endif 387 | 388 | if (per_frame_wifi_settings[0] == 'm') 389 | mouse_moved = 1; 390 | else 391 | mouse_moved = 0; 392 | 393 | //printf("per_frame_wifi_settings 2 \n"); 394 | 395 | recv(sock, chunk_lenghts, nb_chunks * 4, 0); 396 | for (int a = 0; a < nb_chunks; a++) 397 | { 398 | memcpy(chunk_lenghts_int + (a * 1), chunk_lenghts + a * 4, 4 * sizeof(uint8_t)); 399 | #if DEBUG_MSGs == 1 400 | printf(" %d ", chunk_lenghts_int[a]); 401 | if (a == nb_chunks) 402 | printf("\n"); 403 | #endif 404 | } 405 | 406 | recv(sock, line_changed, height_resolution + 2, 0); 407 | 408 | memcpy(total_lines_changed, line_changed + height_resolution, 2); 409 | 410 | need_to_extract = set_download_pointer(0); 411 | td0 = xTaskGetTickCount(); 412 | 413 | if (per_frame_wifi_settings[2] != 0) 414 | { 415 | int delay = per_frame_wifi_settings[2]; 416 | printf("d clearing with delay %d\n", delay); 417 | if (esp32_multithread == 0 || 1) 418 | { 419 | clearing = 1; 420 | epd_clear(); 421 | vTaskDelay(delay / portTICK_PERIOD_MS); 422 | clearing = 0; 423 | } 424 | else 425 | clear[current_buffer] = per_frame_wifi_settings[2]; 426 | } 427 | 428 | if (download_size < buf_size) 429 | buf_size = download_size; 430 | 431 | downloader_busy = 1; 432 | 433 | do 434 | { 435 | len = recv(sock, where_to_download + tot, buf_size, 0); 436 | #if DEBUG_MSGs == 1 437 | printf("len %d, tot %d\n", len, tot); 438 | #endif 439 | // print_values(tot); 440 | tot += len; 441 | if (len < 0) 442 | break; 443 | 444 | if (download_size - tot < 4096 * 6) 445 | { 446 | buf_size = download_size - tot; 447 | } 448 | } while (tot < download_size); 449 | //printf("per_frame_wifi_settings 8\n"); 450 | if (len < 0) 451 | if (end_session() == -1) 452 | break; 453 | 454 | #if DEBUG_MSGs == 1 455 | printf("tot %d \n", tot); 456 | #endif 457 | 458 | if (esp32_multithread == 0) 459 | { 460 | if (need_to_extract == 1) 461 | rle_extract1(download_size, get_current_chunk_ptr(0), where_to_download); 462 | } 463 | else 464 | { 465 | if (need_to_extract == 1) 466 | rle_extract1(download_size, get_current_chunk_ptr(current_buffer), where_to_download); 467 | 468 | // ptr_m = get_current_chunk_ptr(back_buffer()); 469 | // printf("ptr_m %p, \n", ptr_m); 470 | 471 | // if (per_frame_wifi_settings[2] != 0) 472 | // ptr_m[0] = per_frame_wifi_settings[2]; 473 | // else 474 | // ptr_m[0] = 0; 475 | } 476 | 477 | // delta = xTaskGetTickCount() - time2; 478 | // printf("extracting took : %d ", delta); 479 | downloader_chunk_counter++; 480 | downloader_frame_counter++; 481 | downloader_busy = 0; 482 | 483 | #if DEBUG_MSGs == 2 484 | printf("d1 cc %d, fc %lu \n", downloader_chunk_counter, downloader_frame_counter); 485 | #endif 486 | 487 | for (int h = 0; h < nb_chunks - 1; h++) 488 | { 489 | // printf("D renderer %d downloader %d\n", renderer_chunk_counter, downloader_chunk_counter); 490 | tot = 0; 491 | len = 0; 492 | buf_size = 4096 * 5; 493 | need_to_extract = set_download_pointer(h + 1); 494 | 495 | if (download_size < buf_size) 496 | buf_size = download_size; 497 | 498 | do 499 | { 500 | len = recv(sock, where_to_download + tot, buf_size, 0); 501 | #if DEBUG_MSGs == 1 502 | printf("len %d, tot %d\n", len, tot); 503 | #endif 504 | // print_values(tot); 505 | tot += len; 506 | if (len < 0) 507 | break; 508 | 509 | if (download_size - tot < 4096 * 6) 510 | { 511 | buf_size = download_size - tot; 512 | } 513 | } while (tot < download_size); 514 | #if DEBUG_MSGs == 1 515 | printf("tot %d \n", tot); 516 | #endif 517 | if (len < 0) 518 | if (end_session() == -1) 519 | break; 520 | 521 | if (need_to_extract == 1) 522 | rle_extract1(download_size, get_current_chunk_ptr(h + 1), where_to_download); 523 | downloader_chunk_counter++; 524 | #if DEBUG_MSGs == 2 525 | printf("down cc %d, fc %lu \n", downloader_chunk_counter, downloader_frame_counter); 526 | #endif 527 | } 528 | 529 | //printf("d2 Download and extract took : %lu\n", xTaskGetTickCount() - time1); 530 | td1 = xTaskGetTickCount(); 531 | 532 | #if DEBUG_MSGs == 2 533 | printf("d2 Download and extract took : %lu | td1 td0: %lu, %lu \n", td1 - td0, td0, td1); 534 | #else 535 | printf("Download and extract took : %lu\n", td1 - td0); 536 | #endif 537 | 538 | if (esp32_multithread == 0) 539 | pc_monitor_feed_display_with_skip(total_lines_changed[0]); 540 | 541 | frame_counter++; 542 | if (downloader_frame_counter == 4294967290) 543 | downloader_frame_counter = 0; 544 | if (frame_counter == nb_draws) 545 | frame_counter = 0; 546 | } 547 | } 548 | void receive_settings(const int sock) 549 | { 550 | printf("Receiving settings.. \n"); 551 | int8_t settings_size[1]; 552 | 553 | recv(sock, settings_size, 1, 0); 554 | printf("settings_size %d \n", settings_size[0]); 555 | 556 | settings = (uint16_t *)calloc(settings_size[0], sizeof(uint16_t)); 557 | 558 | int ret = recv(sock, settings, settings_size[0], 0); 559 | printf("### Settings ### %d \n", ret); 560 | printf("framebuffer_cycles %d \n", framebuffer_cycles = settings[0]); 561 | // printf("rmt_high_time %d \n", rmt_high_time = settings[1]); 562 | printf("enable_skipping %d \n", enable_skipping = settings[2]); 563 | printf("epd_skip_threshold %d \n", epd_skip_threshold = settings[3]); 564 | printf("esp32_multithread %d \n", esp32_multithread = settings[4]); 565 | 566 | printf("framebuffer_cycles_2 %d \n", framebuffer_cycles_2 = settings[5]); 567 | printf("framebuffer_cycles_2_threshold %d \n", framebuffer_cycles_2_threshold = settings[6]); 568 | printf("draw_white_first %d \n", draw_white_first = settings[7]); 569 | printf("selective_compression %d \n", selective_compression = settings[8]); 570 | printf("nb_chunks %d \n", nb_chunks = settings[9]); 571 | printf("nb_draws %d \n", nb_draws = settings[10]); 572 | printf("per_frame_wifi_settings_size %d \n", per_frame_wifi_settings_size = settings[11]); 573 | 574 | if (nb_draws > framebuffer_cycles) 575 | nb_rmt_times = nb_draws; 576 | else 577 | nb_rmt_times = framebuffer_cycles; 578 | 579 | printf("nb_rmt_times %d \n", nb_rmt_times); 580 | printf("################# \n"); 581 | // already_got_settings = true; 582 | width_resolution = EPD_WIDTH; 583 | height_resolution = EPD_HEIGHT; 584 | 585 | total_nb_pixels = width_resolution * height_resolution; 586 | eink_framebuffer_size = total_nb_pixels / 4; 587 | chunk_size = (eink_framebuffer_size / nb_chunks); 588 | nb_rows_per_chunk = height_resolution / nb_chunks; 589 | extra_bytes = 200000 / nb_chunks; 590 | // int free_mem = esp_get_free_heap_size(); 591 | // ESP_LOGI(TAG, "free memory %d ", free_mem); 592 | 593 | 594 | compressed_chunk = (uint8_t *)heap_caps_malloc(chunk_size, MALLOC_CAP_SPIRAM); 595 | chunk_lenghts = (uint8_t *)heap_caps_malloc(64, MALLOC_CAP_SPIRAM); 596 | chunk_lenghts_int = (int32_t *)heap_caps_malloc(nb_chunks * 64, MALLOC_CAP_SPIRAM); 597 | line_changed = (uint8_t *)heap_caps_malloc(height_resolution + 2, MALLOC_CAP_SPIRAM); 598 | total_lines_changed = (int16_t *)heap_caps_malloc(2, MALLOC_CAP_SPIRAM); 599 | draw_rmt_times = (uint16_t *)heap_caps_malloc(nb_rmt_times * sizeof(uint16_t), MALLOC_CAP_SPIRAM); 600 | per_frame_wifi_settings = (uint8_t *)heap_caps_malloc(per_frame_wifi_settings_size, MALLOC_CAP_SPIRAM); 601 | 602 | 603 | 604 | init_memory(); 605 | 606 | 607 | 608 | for (int g = 0; g < nb_chunks; g++) 609 | { 610 | if (get_current_chunk_ptr(g) == NULL) 611 | ESP_LOGI(TAG, "ptr %d is null ", g); 612 | } 613 | 614 | //free_mem = esp_get_free_heap_size(); 615 | //ESP_LOGI(TAG, "free memory %d ", free_mem); 616 | 617 | 618 | ESP_LOGI(TAG, "nb_chunks %d, nb_rows_chunks %d, chunk_size %d, eink_framebuffer_size %d, chunk_size+extra_bytes %d", nb_chunks, nb_rows_per_chunk, chunk_size, eink_framebuffer_size, chunk_size + extra_bytes); 619 | memset(line_changed, 0, height_resolution + 2); 620 | 621 | //xTaskCreatePinnedToCore(&check_conc, "check_conc", 10000, NULL, 5, NULL, 0); 622 | 623 | if (esp32_multithread == 2) 624 | { 625 | printf("fc0 %p, \n", fc0); 626 | printf("fc1 %p, \n", fc1); 627 | 628 | begin = xSemaphoreCreateBinary(); 629 | 630 | xTaskCreatePinnedToCore(&pc_monitor_feed_display_multithreaded_v1_one_chunk, "feed_display_task", 10000, NULL, 5, NULL, 0); 631 | second_framebuffer = (uint8_t *)heap_caps_malloc(chunk_size + extra_bytes, MALLOC_CAP_SPIRAM); 632 | } 633 | } 634 | 635 | static void tcp_server_task(void *pvParameter) 636 | { 637 | char addr_str[128]; 638 | int addr_family; 639 | int ip_protocol; 640 | ESP_LOGI(TAG, "tcp_server_task"); 641 | 642 | #ifdef CONFIG_EXAMPLE_IPV4 643 | struct sockaddr_in dest_addr; 644 | dest_addr.sin_addr.s_addr = htonl(INADDR_ANY); 645 | dest_addr.sin_family = AF_INET; 646 | dest_addr.sin_port = htons(PORT); 647 | addr_family = AF_INET; 648 | ip_protocol = IPPROTO_IP; 649 | inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1); 650 | #else // IPV6 651 | struct sockaddr_in6 dest_addr; 652 | bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un)); 653 | dest_addr.sin6_family = AF_INET6; 654 | dest_addr.sin6_port = htons(PORT); 655 | addr_family = AF_INET6; 656 | ip_protocol = IPPROTO_IPV6; 657 | inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1); 658 | #endif 659 | 660 | int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol); 661 | int yes = 0; 662 | int result = setsockopt(listen_sock, 663 | IPPROTO_TCP, 664 | TCP_NODELAY, 665 | (char *)&yes, 666 | sizeof(int)); // 1 - on, 0 - off 667 | if (result < 0) 668 | printf("error setting tcp socket options\n"); 669 | if (listen_sock < 0) 670 | { 671 | ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); 672 | goto CLEAN_UP; 673 | vTaskDelete(NULL); 674 | return; 675 | } 676 | ESP_LOGI(TAG, "Socket created"); 677 | 678 | int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); 679 | if (err != 0) 680 | { 681 | ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); 682 | goto CLEAN_UP; 683 | } 684 | ESP_LOGI(TAG, "Socket bound, port %d", PORT); 685 | 686 | err = listen(listen_sock, 1); 687 | if (err != 0) 688 | { 689 | ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno); 690 | goto CLEAN_UP; 691 | } 692 | while (1) 693 | { 694 | 695 | ESP_LOGI(TAG, "Socket listening"); 696 | struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6 697 | uint addr_len = sizeof(source_addr); 698 | sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len); 699 | result = setsockopt(sock, 700 | IPPROTO_TCP, 701 | TCP_NODELAY, 702 | (char *)&yes, 703 | sizeof(int)); // 1 - on, 0 - off 704 | if (result < 0) 705 | printf("error setting tcp socket options\n"); 706 | if (sock < 0) 707 | { 708 | ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno); 709 | goto CLEAN_UP; 710 | break; 711 | } 712 | 713 | // Convert ip address to string 714 | if (source_addr.sin6_family == PF_INET) 715 | { 716 | inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1); 717 | } 718 | else if (source_addr.sin6_family == PF_INET6) 719 | { 720 | inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1); 721 | } 722 | ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str); 723 | // if (already_got_settings == false) 724 | 725 | power_on_driver(); 726 | receive_settings(sock); 727 | 728 | // dma_buffer = epd_get_current_buffer(); 729 | #if FT245MODE == 0 730 | download_and_extract(sock); 731 | #else 732 | signal_245_fifo(sock); 733 | #endif 734 | } 735 | 736 | CLEAN_UP: 737 | ESP_LOGI(TAG, "Restarting the board in 2 seconds.."); 738 | close(listen_sock); 739 | vTaskDelay(2000 / portTICK_PERIOD_MS); 740 | esp_restart(); 741 | //vTaskDelete(NULL); 742 | //wifi_task(NULL); 743 | // xTaskCreatePinnedToCore(&tcp_server_task, "tcp_server_task", 10000, NULL, 5, NULL, 1); 744 | } 745 | 746 | void app_main() 747 | { 748 | 749 | // frame_counter = 0; 750 | width_resolution = EPD_WIDTH; 751 | height_resolution = EPD_HEIGHT; 752 | current_buffer = 0; 753 | memset(clear, 0, 2); 754 | 755 | printf("w %d %d, h %d %d, \n", width_resolution, height_resolution, EPD_WIDTH, EPD_HEIGHT); 756 | 757 | esp_log_level_set("wifi", ESP_LOG_NONE); 758 | 759 | heap_caps_print_heap_info(MALLOC_CAP_INTERNAL); 760 | heap_caps_print_heap_info(MALLOC_CAP_SPIRAM); 761 | // initialize NVS 762 | ESP_ERROR_CHECK(nvs_flash_init()); 763 | 764 | // create the event group to handle wifi events 765 | wifi_event_group = xEventGroupCreate(); 766 | 767 | // initialize the tcp stack 768 | tcpip_adapter_init(); 769 | 770 | if (heap_caps_check_integrity_all(true) == 1) 771 | ESP_LOGI(TAG, "Checking heap integrity: OK "); 772 | else 773 | ESP_LOGI(TAG, "Heap is corrupted"); 774 | 775 | // initialize the wifi event handler 776 | ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); 777 | 778 | // initialize the wifi stack in STAtion mode with config in RAM 779 | wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); 780 | 781 | ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config)); 782 | 783 | ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); 784 | 785 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); 786 | 787 | // configure the wifi connection and start the interface 788 | wifi_config_t wifi_config = { 789 | .sta = { 790 | .ssid = WIFI_SSID, 791 | .password = WIFI_PASS, 792 | }, 793 | }; 794 | 795 | ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); 796 | ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); 797 | 798 | ESP_ERROR_CHECK(esp_wifi_start()); 799 | 800 | wifi_task(NULL); 801 | 802 | array_with_zeros = (uint8_t *)heap_caps_malloc(129, MALLOC_CAP_SPIRAM); 803 | draw_black_bytes = (uint8_t *)heap_caps_malloc(129, MALLOC_CAP_SPIRAM); 804 | draw_white_bytes = (uint8_t *)heap_caps_malloc(129, MALLOC_CAP_SPIRAM); 805 | // array_with_zeros = array_with_zeros; 806 | // draw_black_bytes = draw_black_bytes; 807 | // draw_white_bytes = draw_white_bytes; 808 | memset(array_with_zeros, 0, 129); 809 | memset(draw_black_bytes, 85, 129); 810 | memset(draw_white_bytes, 170, 129); 811 | 812 | //epd_base_init(EPD_WIDTH); 813 | epd_init(); 814 | xTaskCreatePinnedToCore(&tcp_server_task, "tcp_server_task", 10000, NULL, 5, NULL, 1); 815 | } 816 | --------------------------------------------------------------------------------