/ SHARP_BadApple / SHARP_BadApple.ino
SHARP_BadApple.ino
  1  // SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries
  2  //
  3  // SPDX-License-Identifier: MIT
  4  
  5  // Bad Apple for ESP32 with OLED SSD1306 | 2018 by Hackerspace-FFM.de | MIT-License.
  6  // Adapted for Sharp Memory display + Itsy Bitsy M4 - put video.hs on QSPI storage using CircuitPython
  7  #include "heatshrink_decoder.h"
  8  
  9  #include "SdFat.h"
 10  #include "Adafruit_SPIFlash.h"
 11  #include <Adafruit_SharpMem.h>
 12  #define BLACK 0
 13  #define WHITE 1
 14  
 15  #define SCALE 3
 16  
 17  
 18  #if HEATSHRINK_DYNAMIC_ALLOC
 19  #error HEATSHRINK_DYNAMIC_ALLOC must be false for static allocation test suite.
 20  #endif
 21  
 22  static heatshrink_decoder hsd;
 23  
 24  // global storage for putPixels 
 25  int16_t curr_x = 0;
 26  int16_t curr_y = 0;
 27  
 28  // global storage for decodeRLE
 29  int32_t runlength = -1;
 30  int32_t c_to_dup = -1;
 31  
 32  uint32_t lastRefresh = 0;
 33  
 34  #define SHARP_SS   A5
 35  Adafruit_SharpMem display(&SPI, SHARP_SS, 400, 240, 3000000);
 36  #define X_OFFSET (400 - SCALE*128) / 2
 37  #define Y_OFFSET (240 - SCALE*64) / 2
 38    
 39  Adafruit_FlashTransport_QSPI flashTransport;
 40  Adafruit_SPIFlash flash(&flashTransport);
 41  FatFileSystem fatfs;
 42  
 43  
 44  void putPixels(uint8_t c, int32_t len) {
 45    static uint8_t color;
 46    uint8_t b = 0;
 47    while(len--) {
 48      b = 128;
 49      for (int i=0; i<8; i++) {
 50        if (c & b) {
 51          color = WHITE;  
 52        } else {
 53          color = BLACK; 
 54        }
 55        b >>= 1;
 56        if (color == BLACK) {
 57          // we clear the buffer each frame so only black pixels need to be drawn
 58          display.fillRect(X_OFFSET+curr_x*SCALE, Y_OFFSET+curr_y*SCALE, SCALE, SCALE, color);
 59        }
 60        curr_x++;
 61        if(curr_x >= 128) {
 62          curr_x = 0;
 63          curr_y++;
 64          if(curr_y >= 64) {
 65            curr_y = 0;
 66            display.refresh();
 67            display.clearDisplayBuffer();
 68            // 30 fps target rate
 69            //if(digitalRead(0)) while((millis() - lastRefresh) < 33) ;
 70            //lastRefresh = millis();
 71          }
 72        }
 73      }
 74    }
 75  }
 76  
 77  void decodeRLE(uint8_t c) {
 78      if(c_to_dup == -1) {
 79        if((c == 0x55) || (c == 0xaa)) {
 80          c_to_dup = c;
 81        } else {
 82          putPixels(c, 1);
 83        }
 84      } else {
 85        if(runlength == -1) {
 86          if(c == 0) {
 87            putPixels(c_to_dup & 0xff, 1);
 88            c_to_dup = -1;
 89          } else if((c & 0x80) == 0) {
 90            if(c_to_dup == 0x55) {
 91              putPixels(0, c);
 92            } else {
 93              putPixels(255, c);
 94            }
 95            c_to_dup = -1;
 96          } else {
 97            runlength = c & 0x7f;
 98          }
 99        } else {
100          runlength = runlength | (c << 7);
101            if(c_to_dup == 0x55) {
102              putPixels(0, runlength);
103            } else {
104              putPixels(255, runlength);
105            }
106            c_to_dup = -1;  
107            runlength = -1;        
108        }
109      }
110  }
111  
112  #define RLEBUFSIZE 4096
113  #define READBUFSIZE 2048
114  void readFile(const char * path){
115      static uint8_t rle_buf[RLEBUFSIZE];
116      size_t rle_bufhead = 0;
117      size_t rle_size = 0; 
118    
119      size_t filelen = 0;
120      size_t filesize;
121      static uint8_t compbuf[READBUFSIZE];
122      
123      Serial.printf("Reading file: %s\n", path);
124      File file = fatfs.open(path);
125      if(!file || file.isDirectory()){
126          Serial.println("Failed to open file for reading");
127          display.println("File open error. Upload video.hs using CircuitPython"); 
128          display.refresh();
129          return;
130      }
131      filelen = file.size();
132      filesize = filelen;
133      Serial.printf("File size: %d\n", filelen);
134  
135      // init display, putPixels and decodeRLE
136      display.clearDisplay();
137      display.refresh();
138      curr_x = 0;
139      curr_y = 0;
140      runlength = -1;
141      c_to_dup = -1;   
142      lastRefresh = millis(); 
143  
144      // init decoder
145      heatshrink_decoder_reset(&hsd);
146      size_t   count  = 0;
147      uint32_t sunk   = 0;
148      size_t toRead;
149      size_t toSink = 0;
150      uint32_t sinkHead = 0;
151      
152  
153      // Go through file...
154      while(filelen) {
155        if(toSink == 0) {
156          toRead = filelen;
157          if(toRead > READBUFSIZE) toRead = READBUFSIZE;
158          file.read(compbuf, toRead);
159          filelen -= toRead;
160          toSink = toRead;
161          sinkHead = 0;
162        }
163  
164        // uncompress buffer
165        HSD_sink_res sres;
166        sres = heatshrink_decoder_sink(&hsd, &compbuf[sinkHead], toSink, &count);
167        //Serial.print("^^ sinked ");
168        //Serial.println(count);      
169        toSink -= count;
170        sinkHead = count;        
171        sunk += count;
172        if (sunk == filesize) {
173          heatshrink_decoder_finish(&hsd);
174        }
175          
176        HSD_poll_res pres;
177        do {
178            rle_size = 0;
179            pres = heatshrink_decoder_poll(&hsd, rle_buf, RLEBUFSIZE, &rle_size);
180            //Serial.print("^^ polled ");
181            //Serial.println(rle_size);
182            if(pres < 0) {
183              Serial.print("POLL ERR! ");
184              Serial.println(pres);
185              return;
186            }
187  
188            rle_bufhead = 0;
189            while(rle_size) {
190              rle_size--;
191              if(rle_bufhead >= RLEBUFSIZE) {
192                Serial.println("RLE_SIZE ERR!");
193                return;
194              }
195              decodeRLE(rle_buf[rle_bufhead++]);
196            }
197        } while (pres == HSDR_POLL_MORE);
198      }
199      file.close();
200      Serial.println("Done.");
201  }
202  
203  
204  
205  void setup(){
206      Serial.begin(115200);
207      //while (!Serial) delay(10);
208      Serial.println("Bad apple");
209  
210      flash.begin();
211      // Init file system on the flash
212      fatfs.begin(&flash);
213    
214      display.begin();
215      display.clearDisplay();
216      display.setTextColor(BLACK, WHITE);
217      display.setTextSize(2);
218      display.println("Scaled Bad Apple For SHARP Memory");
219      display.refresh();       
220  
221      readFile("/video.hs");
222  }
223  
224  void loop(){
225  
226  }