]> Devi Nivas Git - smartwatch.git/commitdiff
Complete SD Card Support
authorAdvaith Menon <noreply-git@bp4k.net>
Tue, 25 Nov 2025 08:57:12 +0000 (03:57 -0500)
committerAdvaith Menon <noreply-git@bp4k.net>
Tue, 25 Nov 2025 08:57:12 +0000 (03:57 -0500)
* Add bmp reading library
* Modify BLIT to match that in MBED code

main/CMakeLists.txt
main/bmp_reader.c [new file with mode: 0644]
main/bmp_reader.h [new file with mode: 0644]
main/goldeloxSerial.c
main/goldeloxSerial.h
main/hello_world_main.c
main/nvram_settings.h [new file with mode: 0644]
main/pins.h
main/sdcardspi.c

index 492e8c4b6b42d5226b637121e874cc3f469cb0f7..2e4a83a1c9d1889418dd9279ee4d3dd122f8a6e8 100644 (file)
@@ -3,6 +3,7 @@ idf_component_register(SRCS "hello_world_main.c"
                             "task_lcd.c"
                             "task_button.c"
                             "sdcardspi.c"
+                            "bmp_reader.c"
                        INCLUDE_DIRS "."
                        PRIV_REQUIRES esp_driver_gpio
                                      esp_driver_uart
diff --git a/main/bmp_reader.c b/main/bmp_reader.c
new file mode 100644 (file)
index 0000000..89349de
--- /dev/null
@@ -0,0 +1,268 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "esp_log.h"
+#include "goldeloxSerial.h"
+
+#include "bmp_reader.h"
+
+#define BMP_BLIT_CHUNK_SIZE 64
+
+
+static const char *TAG = "bmp_reader";
+
+int blit_chunk_size = BMP_BLIT_CHUNK_SIZE;
+
+/* Private functions */
+
+void _read_bgr_data(FILE *, int, int, int *);
+void _read_bgr_data_reverse(FILE *, int, int, int *);
+void _read_bgr_data_reverse_flip(FILE *, int, int, int, int *);
+void _read_bgr_data_reverse_flip_16bcol(FILE *, int, int, int, uint16_t *);
+uint16_t convert_24b_to_16b(uint32_t c);
+
+void _read_bgr_data(FILE *fp, int data_offset, int size, int *result) {
+    if (!fp)
+        return;
+    if (data_offset)
+        fseek(fp, data_offset, SEEK_SET);
+    // printf("all good!\n");
+    int i;
+    for (i = 0; i < size; i += 1) {
+        // printf("i=%d\n", i);
+        // result[i] = 0xFFFFFF;
+        if(!fread(&result[i], 3, 1, fp)) {
+            break;
+        }
+    }
+}
+
+void _read_bgr_data_reverse(FILE *fp, int data_offset, int size, int *result) {
+    if (!fp)
+        return;
+    if (data_offset)
+        fseek(fp, data_offset, SEEK_SET);
+    // printf("all good!\n");
+    int i;
+    for (i = 0; i < size; i += 1) {
+        // printf("i=%d\n", i);
+        // result[i] = 0xFFFFFF;
+        if(!fread(&result[size - i - 1], 3, 1, fp)) {
+            break;
+        }
+    }
+}
+
+void _read_bgr_data_reverse_flip(FILE *fp, int data_offset, int w, int h, int *result) {
+    if (!fp)
+        return;
+    if (data_offset)
+        fseek(fp, data_offset, SEEK_SET);
+    // printf("all good!\n");
+    int i, j;
+    for (i = h - 1; i >= 0; i -= 1) {
+        // printf("i=%d\n", i);
+        // result[i] = 0xFFFFFF;
+        for (j = 0; j < w; ++j) {
+            if(!fread(&result[i * w + j], 3, 1, fp)) {
+                fprintf(stderr, "bmp_reader: Ran out of bytes to read :(\n");
+                break;
+            }
+        }
+
+    }
+    // printf("read: %d, %d (%d) given h=%d w=%d\n", i, j, i*j, h, w);
+}
+
+void _read_bgr_data_reverse_flip_16bcol(FILE *fp, int data_offset, int w, int h,
+        uint16_t *result) {
+    if (!fp)
+        return;
+    if (data_offset)
+        fseek(fp, data_offset, SEEK_SET);
+    // printf("all good!\n");
+    int i, j;
+    for (i = h - 1; i >= 0; i -= 1) {
+        // printf("i=%d\n", i);
+        // result[i] = 0xFFFFFF;
+        for (j = 0; j < w; ++j) {
+            uint32_t rgb;
+            if(!fread(&rgb, 3, 1, fp)) {
+                fprintf(stderr, "bmp_reader: Ran out of bytes to read :(\n");
+                break;
+            }
+            result[i * w + j] = convert_24b_to_16b(rgb);
+        }
+
+    }
+    // printf("read: %d, %d (%d) given h=%d w=%d\n", i, j, i*j, h, w);
+}
+
+/* end private functions */
+
+void bmp_read_header(FILE *fp, struct bmp_header_t *header) {
+    if (!fp)
+        return;
+    // fseek(fp, 0, SEEK_SET);
+    // (void)fread(header, sizeof(bmp_header_t), 1, fp);
+    fread(&header->signature, sizeof(header->signature), 1, fp);
+    fread(&header->size, sizeof(header->size), 1, fp);
+    fread(&header->_slack, sizeof(header->_slack), 1, fp);
+    fread(&header->offset, sizeof(header->offset), 1, fp);
+}
+
+void bmp_read_info_header(FILE *fp, struct bmp_info_header_t *header) {
+    if (!fp)
+        return;
+    (void)fread(header, sizeof(struct bmp_info_header_t), 1, fp);
+}
+
+int bmp_procure_info(FILE * fp, struct bmp_header_t *head,
+        struct bmp_info_header_t *ih) {
+    if (!fp) {
+        ESP_LOGE(TAG, "file is NULL!");
+        return 1;
+    }
+
+    if (!head || !ih) {
+        ESP_LOGE(TAG, "head or ih is null!");
+        return 1;
+    }
+    bmp_read_header(fp, head);
+    if (head->signature[0] != 'B' || head->signature[1] != 'M') {
+        ESP_LOGE(TAG, "File is not a BMP! (%#2x%#2x)\n", head->signature[0],
+                head->signature[1]);
+        return 1;
+    }
+    bmp_read_info_header(fp, ih);
+    if (ih->width > 128 || ih->height > 128) {
+        ESP_LOGE(TAG,
+                "image too wide or long. ULCD is 128x128 (target is "
+                "%dx%d)",
+                ih->width, ih->height);
+        return 1;
+    }
+    return 0;
+}
+/*
+   int blit_bmp(char *file, int x, int y) {
+   FILE *fd = fopen(file, "rb");
+   if (errno)
+   return errno;
+   bmp_header_t h;
+   bmp_info_header_t ih;
+   if (bmp_procure_info(fd, &h, &ih)) 
+   return 1;
+
+   int *image_data = (int *) malloc(sizeof(int) * ih.width * 10);
+
+   int total = ih.width * ih.height;
+   for (int i = 0; i < ih.height; i += 10) {
+   _read_bgr_data(fd, 0, ih.width * 10, image_data);
+   int ht = 10;
+   if (i + 10 > ih.height)  {
+   ht = ih.height - i;
+   }
+   uLCD.BLIT(x, y + i, ih.width, ht, image_data);
+   }
+   return 0;
+   } */
+
+int bmp_blit(char *file, gl_display_t *disp, uint16_t x, uint16_t y) {
+    ESP_LOGI(TAG, "file name: %s", file);
+    FILE *fd = fopen(file, "r");
+    if (!fd) {
+        perror("fopen");
+        return errno;
+    }
+    struct bmp_header_t h;
+    struct bmp_info_header_t ih;
+    if (bmp_procure_info(fd, &h, &ih))
+        return 1;
+
+    ESP_LOGI(TAG, "Image of size: %dx%d", ih.width, ih.height);
+
+alloc_int:
+    uint16_t *image_data = malloc(sizeof(uint16_t) * ih.width * blit_chunk_size);
+
+    if (!image_data) {
+        ESP_LOGE(TAG, "not enough memory for chunk of size %d, halving",
+                blit_chunk_size);
+        if (blit_chunk_size < 4) {
+            ESP_LOGE(TAG, "ran out of memory");
+            return 1;
+        }
+        blit_chunk_size /= 2;
+        goto alloc_int;
+    }
+
+    int total = ih.width * ih.height;
+    for (int i = 0; i < ih.height; i += blit_chunk_size) {
+        int ht = blit_chunk_size;
+        int off = y + ih.height - i - blit_chunk_size;
+        if (i + blit_chunk_size > ih.height)  {
+            ht = ih.height - i;
+            off = y;
+        }
+        _read_bgr_data_reverse_flip_16bcol(fd, 0, ih.width, ht, image_data);
+
+        // printf("with y-offset %d\n", off);
+        gl_blitComtoDisplay(disp, x, y + i, ih.width, ht,
+                (uint8_t *) image_data);
+    }
+    fclose(fd);
+    free(image_data);
+    return 0;
+}
+
+void bmp_read_bgr_data(FILE *fp, int *data) {
+    if (!fp) {
+        ESP_LOGE(TAG, "file is NULL!");
+        return;
+    }
+
+    struct bmp_header_t head;
+    struct bmp_info_header_t ih;
+    bmp_read_header(fp, &head);
+    if (head.signature[0] != 'B' || head.signature[1] != 'M') {
+        ESP_LOGE(TAG, "File is not a BMP! (%#2x%#2x)",
+                head.signature[0], head.signature[1]);
+        return;
+    }
+    bmp_read_info_header(fp, &ih);
+    if (ih.width > 128 || ih.height > 128) {
+        ESP_LOGE(TAG,
+                "image too wide or long. ULCD is 128x128 (target is "
+                "%dx%d",
+                ih.width, ih.height);
+        return;
+    }
+
+    if (ih.compression) {
+        ESP_LOGE(TAG, "compressed images not supported :(");
+        return;
+    }
+
+    if (ih.bits_per_pixel != 24) {
+        ESP_LOGE(TAG, "only 24 bit color supported");
+        return;
+    }
+
+    _read_bgr_data(fp, head.offset, ih.width * ih.height, data);
+}
+
+
+uint16_t convert_24b_to_16b(uint32_t color) {
+    uint8_t blue = color & 0xFF;
+    uint8_t green = (color >> 8) & 0xFF;
+    uint8_t red = (color >> 16) & 0xFF;
+
+    uint8_t new_blue = (blue >> 3) & 0x1F;
+    uint8_t new_red = (red >> 3) & 0x1F;
+    uint8_t new_green = (green >> 2) & 0x3F;
+
+    return (new_red << 11) | (new_green << 5) | new_blue;
+}
diff --git a/main/bmp_reader.h b/main/bmp_reader.h
new file mode 100644 (file)
index 0000000..3647f2b
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * @file bmp_reader.h
+ * @brief Uncompressed BMP reader
+ * @author Advaith Menon
+ */
+
+/* code adapted from 2035le's BMP loader */
+#ifndef __BMP_READER_H__
+#define __BMP_READER_H__
+
+#ifdef __cplusplus
+// extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "goldeloxSerial.h"
+
+struct bmp_header_t {
+    char signature[2];
+    int32_t size;
+    int32_t _slack;
+    int32_t offset;
+};
+
+struct bmp_info_header_t {
+    int32_t header_size;
+    uint32_t width;
+    uint32_t height;
+    int16_t planes;
+    int16_t bits_per_pixel;
+    int32_t compression;
+    int32_t size;
+    int32_t x_pixels_per_m;
+    int32_t y_pixels_per_m;
+    int32_t used_colors;
+    int32_t important_colors;
+};
+
+/* color table is skipped because we support only 24 bit BMP*/
+
+/* only use for fine grain control */
+void bmp_read_header(FILE *, struct bmp_header_t *);
+void bmp_read_info_header(FILE *, struct bmp_info_header_t *);
+int bmp_procure_info(FILE *, struct bmp_header_t *, struct bmp_info_header_t *);
+void bmp_read_bgr_data(FILE *, int *);
+int bmp_blit(char *file, gl_display_t *, uint16_t, uint16_t);
+
+#ifdef __cplusplus
+// }
+#endif
+#endif
+
index 79f8f7c7725cd7994dc7ee35f59ece1ebe42e3fe..7e39b94aab1975510dc9582d51a1fab35e37d98a 100644 (file)
@@ -13,6 +13,8 @@
 // #include <fcntl.h>
 // #include <termios.h>
 
+#include "freertos/FreeRTOS.h"
+
 #include "Goldelox_Types4D.h"                  // defines data types used by the 4D Routines
 #include "Goldelox_const4D.h"                  // defines for 4dgl constants, generated by conversion of 4DGL constants to target language
 #include "gl_error.h"
@@ -23,6 +25,7 @@
 
 
 // 4D Global variables
+char *Error4DText[] = {"OK", "Timeout", "NAK", "Length", "Invalid"} ;
 int    cPort;                                   // comp port handle, used by Intrinsic routines
 int    Error4D ;                               // Error indicator,  used and set by Intrinsic routines
 unsigned char Error4D_Inv ;                     // Error byte returned from com port, onl set if error = Err_Invalid
@@ -1059,6 +1062,7 @@ void gl_blitComtoDisplay(gl_display_t *display, gl_word_t  X, gl_word_t  Y, gl_w
   towrite[8]= Height >> 8 ;
   towrite[9]= Height ;
   WriteBytes(display->serif, towrite, 10) ;
+  vTaskDelay(pdMS_TO_TICKS(1));
   WriteBytes(display->serif, Pixels, Width*Height*2) ;
   GetAck(display->serif) ;
 }
index 7407696b96cfdcedad54f123c93014990a046f3a..77b1b4c9b2ccd189091093059c95fd318d640964 100644 (file)
@@ -17,7 +17,6 @@
 #define   Err4D_Timeout 1
 #define   Err4D_NAK            2 // other than ACK received
 
-char *Error4DText[] = {"OK", "Timeout", "NAK", "Length", "Invalid"} ;
 // 4D Global variables
 extern int cPort ;                              // comp port handle, used by Intrinsic routines
 extern int Error4D ;                           // Error indicator,  used and set by Intrinsic routines
index 17725dbd8f18e413f9ce5d13afb9a0e8fb429c98..70f30f73fdf0ba194bc02b7985a52d3e7897beca 100644 (file)
@@ -16,7 +16,7 @@
 #include "task_button.h"
 #include "sdcardspi.h"
 
-
+int gt_global_sdsupport;
 
 void app_main(void)
 {
@@ -47,12 +47,10 @@ void app_main(void)
     ESP_ERROR_CHECK(gpio_set_drive_capability(PIN_VIBRATOR, GPIO_DRIVE_CAP_3));
 
 
-    gpio_dump_io_configuration(stdout, (1ULL << PIN_VIBRATOR) | (1ULL << PIN_BUTTON_RED));
-
     /* initialize the buttons */
     gt_btn_setup();
 
-    task_sdcard();
+    gt_global_sdsupport = task_sdcard();
     /* start the main gui thread  which starts all else */
     lcd_task();
 
diff --git a/main/nvram_settings.h b/main/nvram_settings.h
new file mode 100644 (file)
index 0000000..ef05f18
--- /dev/null
@@ -0,0 +1,11 @@
+#pragma once
+
+#ifndef __NVRAM_SETTINGS_H__
+#define __NVRAM_SETTINGS_H__
+
+void task_settings(void);
+
+
+void settings_set_pcpc(char *, char *);
+
+#endif
index c3f1046b69f986d2e8cd60bb717615187ad06ed0..b401d36500f16abfd1b219c6aa52a364d49d223a 100644 (file)
@@ -24,4 +24,6 @@
 #define SD_MOUNT_PATH "/mnt/sdcard0"
 #define SD_MAX_FILE_HANDLES 5
 
+extern int gt_global_sdsupport;
+
 #endif
index 9284f72c543358012101b13e0d92996a8ab243a0..1ea0920a9adec3083bef0eff10c9f6a4fdc01211 100644 (file)
@@ -17,7 +17,7 @@
 /* code largely adapted from ESP-IDF examples: sd_card/sdspi */
 
 /* constants */
-const char *TAG = "sdcardspi";
+static const char *TAG = "sdcardspi";
 /* the sdcard handle itself */
 sdmmc_card_t *sdmmc;
 /* the sd card mount path */