SmittyHalibut / EleksTubeHAX

A custom firmware for the EleksTube IPS Clock
GNU General Public License v3.0
116 stars 49 forks source link

Photo album with static image #88

Open lorerave85 opened 1 month ago

lorerave85 commented 1 month ago

Hi,

has anyone tried to insert a logic to create a photo album?

thanks Lorenzo

aly-fly commented 1 month ago

Hi, ESP32 has very limited RAM and manipulating JPEG images would be tricky. Also Flash is completely full with clock faces. For a photo album there are a few variants.

  1. Add a code that will load BMP files from an external server and display them directly.
  2. Store just one clock font and use space for photos.

You also have to make a new function that will override the clock display and load your photos. I think this is pretty much out of scope for this "clock" project...

lorerave85 commented 1 month ago

ok clear, I started from the H401 model that had native management for photos in a static way and I would have liked to bring it back also in this version integrated in home assistant.

if it is outofscope, close this issue too.

aly-fly commented 1 month ago

As far as I know, H401 has flash larger Flash than other clock models, therefore enough space for additional custom photos. Then you need an external app that resizes, trims and uploads photos over wifi or serial, without connection to Platformio. Such function should only be compiled if clock has large flash size. If someone has enough time to make such addition, it is welcome of course.

lorerave85 commented 1 month ago

I did some tests and I confirm that the space is slightly larger, but it does not hold more than 20 photos. It would almost be a waste of resources considering the changes needed to include a feature like this.

I inserted a switch on home assistant as shown in the image that interrupts the clock cycle and starts the photo show.

Screenshot 2024-10-07 alle 11 23 32

I actually made a PoC to be able to download the photos live from my home assistant server. Also considering the lack of RAM resources I chose to download a photo in jpg format live, convert it and send it in blocks on the screen. In this way I do not allocate excessive space on the memory.

code

bool TFTs::LoadImageIntoBuffer_net(uint8_t file_index) {

  http.begin(client, "https://xxx.yyy.zzz.hhh:1111/local/images/100.jpg");
  httpCode = http.GET();      

  if (httpCode == HTTP_CODE_OK) { 
    totalLength = http.getSize();

    if (httpCode != 200) {
      Serial.print("Failed to retrieve file: ");
      Serial.println(httpCode);
      http.end();
      return false;
    }

    WiFiClient *stream = http.getStreamPtr();
    if (!stream) {
      Serial.println("Failed to get HTTP stream.");
      http.end();
      return false;
    }

    int contentLength = http.getSize();
    if (contentLength <= 0) {
      Serial.println("Content size is zero or could not be determined.");
      http.end();
      return false;
    }

    uint8_t* buffer = (uint8_t*)malloc(totalLength);
    if (buffer == nullptr) {
      Serial.println("Errore: Memoria insufficiente per allocare il buffer");
      http.end();
      return false;
    }

    int bytesRead = 0;
    while (http.connected() && bytesRead < totalLength) {
      int availableBytes = stream->available();
      if (availableBytes > 0) {
        int bytes = stream->readBytes(buffer + bytesRead, availableBytes);
        bytesRead += bytes;
      }
    }

    if (JpegDec.decodeArray(buffer, bytesRead)) {

      renderJPEG();  // Se la decodifica ha successo, visualizza l'immagine
    } else {
      Serial.println("Errore nella decodifica del JPEG");
    }

    free(buffer);  // Libera il buffer dalla memoria

  } else {
    Serial.printf("Errore HTTP: %d\n", httpCode);
  }

  http.end();  // Chiude la connessione HTTP
  return true;
}

render

void TFTs::renderJPEG() {
  uint16_t *pImg;
  int16_t mcu_w = JpegDec.MCUWidth;  
  int16_t mcu_h = JpegDec.MCUHeight; 
  int32_t max_x = JpegDec.width;    
  int32_t max_y = JpegDec.height; 

  while (JpegDec.read()) {
    uint16_t *pImg = JpegDec.pImage;

    int16_t mcu_x = JpegDec.MCUx * JpegDec.MCUWidth;
    int16_t mcu_y = JpegDec.MCUy * JpegDec.MCUHeight;

    int16_t adjusted_w = JpegDec.MCUWidth;
    int16_t adjusted_h = JpegDec.MCUHeight;

    if (mcu_x + JpegDec.MCUWidth > JpegDec.width) {
        adjusted_w = JpegDec.width - mcu_x;
    }
    if (mcu_y + JpegDec.MCUHeight > JpegDec.height) {
        adjusted_h = JpegDec.height - mcu_y;
    }

    bool oldSwapBytes = getSwapBytes();
    setSwapBytes(true);
    pushImage(mcu_x, mcu_y, adjusted_w, adjusted_h, (uint16_t *)pImg);
    setSwapBytes(oldSwapBytes);
}

I don't have the logic of "preloading" the image, but it goes directly to download it and renders it to blocks. these are all tests waiting to understand the best way for this type of hw