diff --git a/ESP32_BadApple.ino b/ESP32_BadApple.ino index 3c34271..3308af7 100644 --- a/ESP32_BadApple.ino +++ b/ESP32_BadApple.ino @@ -4,12 +4,43 @@ #include "SSD1306.h" #include "heatshrink_decoder.h" -// Hints: +// Board Boot SW GPIO 0 +#define BOOT_SW 0 + +// Frame counter +#define ENABLE_FRAME_COUNTER + +// Disable heatshrink error checking +#define DISABLE_HS_ERROR + +// Hints: // * Adjust the display pins below // * After uploading to ESP32, also do "ESP32 Sketch Data Upload" from Arduino -SSD1306 display (0x3c, 4, 15); // For Heltec -//SSD1306 display (0x3c, 5, 4); +// SSD1306 display I2C bus +// For Heltec +// #define I2C_SCL 15 +// #define I2C_SDA 4 +// #define RESET_OLED 16 + +// For Wemos Lolin32 ESP32 +#define I2C_SCL 4 +#define I2C_SDA 5 + +#define OLED_BRIGHTNESS 16 + +// MAX freq for SCL is 4 MHz, However, Actual measured value is 892 kHz . (ESP32-D0WDQ6 (revision 1)) +// see Inter-Integrated Circuit (I2C) +// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/i2c.html +#define I2C_SCLK_FREQ 4000000 +#ifdef I2C_SCLK_FREQ +SSD1306 display (0x3c, I2C_SDA, I2C_SCL, GEOMETRY_128_64, I2C_ONE, I2C_SCLK_FREQ); +#else +SSD1306 display (0x3c, I2C_SDA, I2C_SCL); +#endif + +// Enable I2C Clock up 892kHz to 1.31MHz (It Actual measured value with ESP32-D0WDQ6 (revision 1)) +// #define ENABLE_EXTRA_I2C_CLOCK_UP #if HEATSHRINK_DYNAMIC_ALLOC #error HEATSHRINK_DYNAMIC_ALLOC must be false for static allocation test suite. @@ -17,9 +48,8 @@ SSD1306 display (0x3c, 4, 15); // For Heltec static heatshrink_decoder hsd; -// global storage for putPixels -int16_t curr_x = 0; -int16_t curr_y = 0; +// global storage for putPixels +int32_t curr_xy = 0; // global storage for decodeRLE int32_t runlength = -1; @@ -56,38 +86,104 @@ void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ } } -uint32_t lastRefresh = 0; +volatile unsigned long lastRefresh; +// 30 fps target rate = 33.333us +#define FRAME_DERAY_US 33333UL +#ifdef ENABLE_FRAME_COUNTER +int32_t frame = 0; +#endif +uint32_t* pImage; +uint32_t b = 0x01; + +volatile bool isButtonPressing = false; + +void ARDUINO_ISR_ATTR isr() { + lastRefresh = micros(); + isButtonPressing = (digitalRead(BOOT_SW) == LOW); +} + +void putPixels(uint32_t c, int32_t len) { + uint32_t d1; + uint32_t d2; -void putPixels(uint8_t c, int32_t len) { - uint8_t b = 0; while(len--) { - b = 128; - for(int i=0; i<8; i++) { - if(c & b) { - display.setColor(WHITE); + // Direct Draw OLED buffer + // OLED Buffer Image Rotate 90 Convert X-Y and Byte structure + { + // 4 dot(Direct Access 4 byte, 32 bit) + d1 = 0; + d2 = 0; + if (c == 0xff) { + d1 = b; d1 <<= 8; + d1 |= b; d1 <<= 8; + d1 |= b; d1 <<= 8; + d1 |= b; + + d2 = d1; + } else if (c != 0x00) { + // if ((c & 0xF0) != 0x00) { + if (c & 0x10) d1 = b<<24; + if (c & 0x20) d1 |= b<<16; + if (c & 0x40) d1 |= b<< 8; + if (c & 0x80) d1 |= b; + // } + + // if ((c & 0x0F) != 0x00) { + if (c & 0x01) d2 = b<<24; + if (c & 0x02) d2 |= b<<16; + if (c & 0x04) d2 |= b<< 8; + if (c & 0x08) d2 |= b; + // } + } + + if (b == 0x01) { + *pImage++ = d1; + *pImage = d2; + } else if (c != 0x00) { + *pImage++ |= d1; + *pImage |= d2; } else { - display.setColor(BLACK); + pImage++; } - b >>= 1; - display.setPixel(curr_x, curr_y); - curr_x++; - if(curr_x >= 128) { - curr_x = 0; - curr_y++; - if(curr_y >= 64) { - curr_y = 0; + pImage++; + } + + // oyy_ybbb_xxxx X=0-15(4 bit), Bit=0-7(3 bit),Y=0-7(3 bit) + curr_xy++; + if((curr_xy & 0x0f) == 0) { + pImage -= 128/4; + + b <<= 1; + if(b == 0x100) { + // Next Page + pImage += 128/4; + b = 0x01; + + // oyy_ybbb_xxxx X=0-15(4 bit), Bit=0-7(3 bit),Y=0-7(3 bit) + // Check Overflow bit, It equivalent if((curr_xy & 0x400) != 0) + if((curr_xy & 0x3ff) == 0) { + pImage = (uint32_t*)display.buffer; + + // Update Display frame display.display(); //display.clear(); - // 30 fps target rate - if(digitalRead(0)) while((millis() - lastRefresh) < 33) ; - lastRefresh = millis(); + + if(!isButtonPressing) { + // 30 fps target rate = 33.333us + lastRefresh += FRAME_DERAY_US; +#ifdef ENABLE_FRAME_COUNTER + // Adjust 33.334us every 3 frame + if ((++frame % 3) == 0) lastRefresh++; +#endif + while(micros() < lastRefresh) ; + } } } } } } -void decodeRLE(uint8_t c) { +void decodeRLE(uint32_t c) { if(c_to_dup == -1) { if((c == 0x55) || (c == 0xaa)) { c_to_dup = c; @@ -116,8 +212,8 @@ void decodeRLE(uint8_t c) { } else { putPixels(255, runlength); } - c_to_dup = -1; - runlength = -1; + c_to_dup = -1; + runlength = -1; } } } @@ -126,13 +222,13 @@ void decodeRLE(uint8_t c) { #define READBUFSIZE 2048 void readFile(fs::FS &fs, const char * path){ static uint8_t rle_buf[RLEBUFSIZE]; - size_t rle_bufhead = 0; - size_t rle_size = 0; - - size_t filelen = 0; + uint8_t* p_rle_buf; + size_t rle_size; + + size_t filelen; size_t filesize; static uint8_t compbuf[READBUFSIZE]; - + Serial.printf("Reading file: %s\n", path); File file = fs.open(path); if(!file || file.isDirectory()){ @@ -145,99 +241,132 @@ void readFile(fs::FS &fs, const char * path){ Serial.printf("File size: %d\n", filelen); // init display, putPixels and decodeRLE - display.clear(); - display.display(); - curr_x = 0; - curr_y = 0; - runlength = -1; - c_to_dup = -1; - lastRefresh = millis(); + display.resetDisplay(); + // curr_xy = 0; + pImage = (uint32_t*)display.buffer; + // runlength = -1; + // c_to_dup = -1; // init decoder heatshrink_decoder_reset(&hsd); size_t count = 0; uint32_t sunk = 0; - size_t toRead; size_t toSink = 0; uint32_t sinkHead = 0; - + + lastRefresh = micros(); // Go through file... while(filelen) { if(toSink == 0) { - toRead = filelen; - if(toRead > READBUFSIZE) toRead = READBUFSIZE; - file.read(compbuf, toRead); - filelen -= toRead; - toSink = toRead; + toSink = file.read(compbuf, READBUFSIZE); + filelen -= toSink; sinkHead = 0; } // uncompress buffer - HSD_sink_res sres; - sres = heatshrink_decoder_sink(&hsd, &compbuf[sinkHead], toSink, &count); + heatshrink_decoder_sink(&hsd, &compbuf[sinkHead], toSink, &count); //Serial.print("^^ sinked "); - //Serial.println(count); + //Serial.println(count); toSink -= count; - sinkHead = count; + sinkHead = count; sunk += count; if (sunk == filesize) { heatshrink_decoder_finish(&hsd); } - + HSD_poll_res pres; do { - rle_size = 0; + // rle_size = 0; pres = heatshrink_decoder_poll(&hsd, rle_buf, RLEBUFSIZE, &rle_size); //Serial.print("^^ polled "); //Serial.println(rle_size); +#ifndef DISABLE_HS_ERROR if(pres < 0) { Serial.print("POLL ERR! "); Serial.println(pres); return; } +#endif - rle_bufhead = 0; + p_rle_buf = rle_buf; while(rle_size) { rle_size--; +#ifndef DISABLE_HS_ERROR if(rle_bufhead >= RLEBUFSIZE) { Serial.println("RLE_SIZE ERR!"); return; } - decodeRLE(rle_buf[rle_bufhead++]); +#endif + decodeRLE(*(p_rle_buf++)); } } while (pres == HSDR_POLL_MORE); } file.close(); +#ifdef ENABLE_FRAME_COUNTER + Serial.print("Done. "); + Serial.println(frame); +#else Serial.println("Done."); +#endif + + // 10 sec + delay(10000); + display.resetDisplay(); + display.drawStringMaxWidth(0, 0, 128, "Optimize OLED Draw Performance version. modded By FREE WING"); + display.drawStringMaxWidth(0, 40, 128, "http://www.neko.ne.jp/~freewing/"); display.display(); + delay(10000); + // Reset to Infinite Loop Demo ! + ESP.restart(); } void setup(){ Serial.begin(115200); +#ifdef RESET_OLED // Reset for some displays - pinMode(16,OUTPUT); digitalWrite(16, LOW); delay(50); digitalWrite(16, HIGH); + pinMode(RESET_OLED, OUTPUT); digitalWrite(RESET_OLED, LOW); delay(50); digitalWrite(RESET_OLED, HIGH); +#endif display.init(); +#ifdef OLED_BRIGHTNESS + display.setBrightness(OLED_BRIGHTNESS); +#endif display.flipScreenVertically (); display.clear(); display.setTextAlignment (TEXT_ALIGN_LEFT); display.setFont(ArialMT_Plain_10); display.setColor(WHITE); display.drawString(0, 0, "Mounting SPIFFS... "); - display.display(); + display.display(); if(!SPIFFS.begin()){ Serial.println("SPIFFS mount failed"); display.drawStringMaxWidth(0, 10, 128, "SPIFFS mount failed. Upload video.hs using ESP32 Sketch Upload."); display.display(); return; } - pinMode(0, INPUT_PULLUP); + pinMode(BOOT_SW, INPUT_PULLUP); + attachInterrupt(BOOT_SW, isr, CHANGE); Serial.print("totalBytes(): "); Serial.println(SPIFFS.totalBytes()); Serial.print("usedBytes(): "); Serial.println(SPIFFS.usedBytes()); listDir(SPIFFS, "/", 0); + +#ifdef ENABLE_EXTRA_I2C_CLOCK_UP + // Direct Access I2C SCL frequency setting value + // It Tested ESP32-D0WDQ6 (revision 1) + uint32_t* ptr; + ptr = (uint32_t*)0x3FF53000; // I2C_SCL_LOW_PERIOD_REG + // *ptr = 30; // Don't work + // *ptr = 31; // Sometime Stop Frame drawing + // *ptr = 32; // Works + *ptr = 35; // Safety value + ptr = (uint32_t*)0x3FF53038; // I2C_SCL_HIGH_PERIOD_REG + // *ptr = 0; // Works + *ptr = 2; // Safety value +#endif + readFile(SPIFFS, "/video.hs"); //Serial.print("Format SPIFSS? (enter y for yes): "); @@ -252,4 +381,5 @@ void setup(){ void loop(){ -} +} +