Git Product home page Git Product logo

esp_8_bit_composite's Introduction

ESP_8_BIT Color Composite Video Out Library

Status: Currently BROKEN

Espressif made breaking changes to ESP32 Arduino Core between v2.x and v3.x which cause compilation errors in this library. The first compiler error (but not the most serious one) is:

ESP_8_BIT_composite.cpp:45:55: error: invalid conversion from 'const volatile void*' to 'volatile void*' [-fpermissive]

I am tracking the problem as issue #56 in this repository. Current workaround is to downgrade Espressif ESP32 Arduino core to v2, this library was last verified to work with v2.0.14.

Espressif esp32 arduino core version select

Purpose

The composite video generation code from SEGA emulator of ESP_8_BIT extracted and packaged into a standalone Arduino library so everyone can write Arduino sketches that output a color composite video signal. NTSC and PAL are both supported.

Huge thanks to Peter Barrett / rossumur for ESP_8_BIT, without which this library would not have been possible.

For more behind-the-scenes information on how this library came to be, see the development diary which has all the details anyone would ever want plus even more that nobody ever asked for.

Hardware requirement

  • 'Newer' ESP32 (see below)
  • Composite video connector to ESP32 GPIO25 video signal pin.
  • Display device with composite video input port. (Usually an old-school tube TV.)

ESP32 Details

This composite video generation code is an extremely clever hack that used several ESP32 peripherals in ways they were not originally designed for. See the original author's blog documentation for details. It also means older versions of ESP32 could not run this code. I don't know exactly which Espressif errata is relevant. [UPDATE: sysytwl believes it is 3.7 Audio PLL frequency range is limited.]

Here are some data points:

  • Known to work
    • ESP32-D0WD (revision 1) (mine)
    • ESP32-D0WDQ6 (revision 1) (thanks todbot)
    • ESP32-PICO-D4 (revision 1) (thanks alex1115alex)
  • Known to NOT work
    • ESP32-D0WDQ6 (revision 0) (thanks todbot)

Chip identification obtained from ESPTool with the command esptool chip_id

Pushing hardware limits in this manner may restrict this library to a subset of the ESP32 family. This library was developed and tested against the original suffix-free ESP32. Compatiblity with variants (ESP32-S2, ESP32-S3, etc.) are untested and unknown.

Arduino requirement

  • Adafruit GFX Library available from Arduino IDE Library Manager. (Last verified to work with v1.11.9)
  • Espressif Arduino Core for ESP32, follow installation directions at that link. (Last verified to work with v2.0.14)
  • (Optional) AnimatedGIF, for displaying animated GIF files. (Last verified to work with v1.4.7)
  • Arduino IDE of course. (Last verified to work with v2.2.1)

Here's an Arduino IDE screenshot of my ESP32 configuration:

Arduino IDE board/esp32/ESP32 Dev Module

Screenshot 2023-12-03 142653

Not Compatible with "Arduino ESP32 Boards by Arduino"

arduino board manager esp32

This library is NOT compatible with the "Arduino ESP32 Boards by Arduino" board library. (thanks JLBCS for #44) Compilation will fail with the following error:

ESP_8_BIT_composite.h:48:10: fatal error: driver/dac.h: No such file or directory
 #include "driver/dac.h"
          ^~~~~~~~~~~~~~

Installation

This library can now be installed from within the Arduino desktop IDE via the Library Manager. Listed as "ESP_8_BIT Color Composite Video Library"

It can also be installed from this GitHub repository if desired:

  1. Download into a folder named "ESP_8_BIT_composite" under your Arduino IDE's libraries folder.
  2. Restart Arduino IDE.

Classes

  1. ESP_8_BIT_GFX offers high-level drawing commands via the Adafruit GFX API. Easy to use, but not the highest performance.
  2. ESP_8_BIT_composite exposes low-level frame buffer for those who prefer to manipulate bytes directly. Maximum performance, but not very easy to use.

Examples

  1. GFX_HelloWorld draws animated rectangles and text, both in changing colors, using the Adafruit GFX API exposed by ESP_8_BIT_GFX.
  2. RGB332_Colors draws all 256 available colors directly to frame buffer allocated by ESP_8_BIT_composite. Draws once, no updates.
  3. RGB332_PulseB draws 64 blocks of colors (8x8) representing different combinations of red (vertical axis) and green (horizontal axis). Uses the frame buffer of ESP_8_BIT_composite directly. Every second, the entire screen is redrawn with one of four possible values of blue in a pulsing cycle.
  4. GFX_Screen_Fillers demonstrates several of the common ways to put graphics on screen. Includes the following APIS: fillRect, fillCircle, drawFastVLine, and drawFastHLine.
  5. AnimatedGIF demonstrates how to use this video out library with the AnimatedGIF decoder library by Larry Bank. Art used in this example is Cat and Galactic Squid by Emily Velasco (CC BY-SA 4.0)
  6. GFX_RotatedText demonstrates support for Adafruit_GFX::setRotation() by rendering text in one of four orientations and one of three text sizes.
  7. GFX_RotatedRect demonstrates support for Adafruit_GFX::setRotation() by drawing four rectangles - one in each supported orientation - on every frame. Cycles through one of four animated pameters (X/Y/Width/Height) every second.

Screen Size

  • Inherited from SEGA emulator of ESP_8_BIT, the addressible screen size is 256 pixels wide and 240 pixels tall. This means valid X values of 0 to 255 inclusive, and valid Y values of 0 to 239 inclusive.
    • When displayed on a standard analog TV with 4:3 aspect ratio, these pixels are not square. So drawCircle() will look like a squat wide oval on screen and not a perfect circle. This is inherent to the system and not considered a bug.
    • When displayed on a standard analog TV, the visible image will be slightly cropped due to overscan. This is inherent to analog televisions and not considered a bug.
  • The developer-friendly ESP_8_BIT_GFX class checks for valid coordinates and will only draw within the valid range. So if X is too large (say, 300) drawPixel() will ignore the command and silently do nothing.
  • The raw ESP_8_BIT_composite class gives max performance power, but with great power comes great responsibility. Caller is responsible for making sure X and Y stay within bounds when manipulating frame buffer bytes via getFrameBufferLines()[Y][X]. Any bugs that use out of range array index may garble the image, or trigger a memory access violation and cause your ESP32 to reset, or other general memory corruption nastiness including the potential for security vulnerabilities.

8-Bit Color

Inherited from ESP_8_BIT is a fixed 8-bit color palette in RGB332 format. The underlying composite video out code always works with this set of colors. (See Examples.)

  • The developer-friendly ESP_8_BIT_GFX class constructor can be initialized in either 8-bit (native) or 16-bit (compatibility) color mode.
    • Adafruit GFX was written for 16-bit color in RGB565 format. ESP_8_BIT_GFX in 16-bit mode is compatible with existing Adafruit GFX code by automatically downconverting color while drawing. The resulting colors will be approximate, but they should closely resemble the original. Using RGB332 color values while in this mode will result in wrong colors on screen due to interpretation as RGB565 colors.
    • In 8-bit mode, color values given to GFX APIs will be treated as native 8-bit RGB332 values. This is faster because it skips the color conversion process. Using RGB565 color values while in this mode will result in wrong colors on screen due to the higher 8 bits being ignored.
  • The raw ESP_8_BIT_composite class always works in 8-bit RGB332 color.

Sample colors in 8-bit RGB332 format:

Name RGB332 (binary) RGB332 (hexadecimal)
Black 0b00000000 0x00
Blue 0b00000011 0x03
Green 0b00011100 0x1C
Cyan 0b00011111 0x1F
Red 0b11100000 0xE0
Magenta 0b11100011 0xE3
Yellow 0b11111100 0xFC
White 0b11111111 0xFF

8-bit RGB332 Color Picker Utility

CLICK HERE for an interactive color picker web app. It shows all 256 possible 8-bit RGB332 colors in either a HSV (hue/saturation/value) color cylinder or a RGB (red/green/blue) color cube.

Alternatives

The intent of this library is to be easy to use, with minimum complexity for beginners. Advanced users are expected to fork this repository to add their desired features, or go to the source and fork ESP_8_BIT directly.

If this library is not a good fit for your project, please consider another library for ESP32 composite video:

  • Bitluni's Lab: one of the earliest ESP32 composite video generators that informed development of many that followed.
  • FabGL: This powerful graphics library didn't have composite video output when I started my project, but it has since been added. See the author's show-and-tell thread for details. The author has a Tektronix VM700 to ensure PAL video output signal is accurate.
  • LovyanGFX: Another very nice graphics library that has preliminary composite video support in a development branch. I believe this library is unique in support for NTSC-J, and documentation is in both Japanese and English. (Code comments are in Japanese.)

Questions?

Please post to discussions and see if anyone knows the answer. Note there's no guarantee of an answer.

Bugs?

Please open an issue to see if it can be fixed. Note there's no guarantee of support.

Tip jar

Just like its predecessor ESP_8_BIT, this project is shared freely with the world. Under the MIT license, you don't owe me anything.

But if you want to toss a few coins my way, you can do so by using my Amazon Associates link to buy your ESP32 development boards or composite video cables. You'll pay the same price, but I get a small percentage. As an Amazon Associate I earn from qualifying purchases.

esp_8_bit_composite's People

Contributors

roger-random avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

esp_8_bit_composite's Issues

Strange behaviour

Hello,
I have developed a program which work on an ARDUINO Uno, an Arduino nanoesp32 and an ESP32 WROOM 32 board.
The output is an LCD 20x4 which work correctly.
I need to add a large display on a TV screen, so I sniffed
ESP_8_Bit_TV
ESP_8_bit_all
into ESP_8_Bit;
I have been able to run 'Hello World' on an ESP32 WROOM 32 board. I did not change any connection
I started copying ESPP_8_Bit statements into my code and this turn to a disaster!
Progressively I removed the ESP_8_Bit specific statements to end up with only the begin() statement.

On the attached picture, you can see the full TV screen and part of the screen with the PC screen and the LCD. You can read all you like!
I also added to Hello world all the include that are in my code. Does not re-create the problem.

Bluetooth classic stops working after running .begin()

I'm trying to use Bluetooth (classic, not BLE) and display composite video at the same time, but it seems as if Bluetooth stops working once the video output starts.

Has anyone run into a similar issue? If so, how did you work around this limitation?

video_isr and rtc_clk_apll_enable broken

C:\Users\Joshu\Documents\Arduino\libraries\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.cpp: In function 'void i2s_intr_handler_video(void*)':
C:\Users\Joshu\Documents\Arduino\libraries\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.cpp:45:55: error: invalid conversion from 'const volatile void*' to 'volatile void*' [-fpermissive]
45 | video_isr(((lldesc_t*)I2S0.out_eof_des_addr)->buf); // get the next line of video
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
| |
| const volatile void*
C:\Users\Joshu\Documents\Arduino\libraries\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.cpp:39:41: note: initializing argument 1 of 'void video_isr(volatile void*)'
39 | void IRAM_ATTR video_isr(volatile void* buf);
| ~~~~~~~~~~~~~~~^~~
C:\Users\Joshu\Documents\Arduino\libraries\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.cpp: In function 'esp_err_t start_dma(int, int, int)':
C:\Users\Joshu\Documents\Arduino\libraries\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.cpp:102:40: error: too many arguments to function 'void rtc_clk_apll_enable(bool)'
102 | case 3: rtc_clk_apll_enable(1,0x46,0x97,0x4,2); break; // 10.7386363636 3x NTSC (10.7386398315mhz)
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
In file included from C:\Users\Joshu\Documents\Arduino\libraries\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.h:57,
from C:\Users\Joshu\Documents\Arduino\libraries\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.cpp:22:
C:\Users\Joshu\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-442a798083/esp32/include/soc/esp32/include/soc/rtc.h:242:6: note: declared here
242 | void rtc_clk_apll_enable(bool enable);
| ^~~~~~~~~~~~~~~~~~~
C:\Users\Joshu\Documents\Arduino\libraries\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.cpp:103:40: error: too many arguments to function 'void rtc_clk_apll_enable(bool)'
103 | case 4: rtc_clk_apll_enable(1,0x46,0x97,0x4,1); break; // 14.3181818182 4x NTSC (14.3181864421mhz)
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
C:\Users\Joshu\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-442a798083/esp32/include/soc/esp32/include/soc/rtc.h:242:6: note: declared here
242 | void rtc_clk_apll_enable(bool enable);
| ^~~~~~~~~~~~~~~~~~~
C:\Users\Joshu\Documents\Arduino\libraries\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.cpp:106:28: error: too many arguments to function 'void rtc_clk_apll_enable(bool)'
106 | rtc_clk_apll_enable(1,0x04,0xA4,0x6,1); // 17.734476mhz ~4x PAL
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
C:\Users\Joshu\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-442a798083/esp32/include/soc/esp32/include/soc/rtc.h:242:6: note: declared here
242 | void rtc_clk_apll_enable(bool enable);
| ^~~~~~~~~~~~~~~~~~~
C:\Users\Joshu\Documents\Arduino\libraries\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.cpp: In destructor 'ESP_8_BIT_composite::~ESP_8_BIT_composite()':
C:\Users\Joshu\Documents\Arduino\libraries\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.cpp:686:28: error: too many arguments to function 'void rtc_clk_apll_enable(bool)'
686 | rtc_clk_apll_enable(false,0x46,0x97,0x4,1);
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Joshu\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-442a798083/esp32/include/soc/esp32/include/soc/rtc.h:242:6: note: declared here
242 | void rtc_clk_apll_enable(bool enable);
| ^~~~~~~~~~~~~~~~~~~
C:\Users\Joshu\Documents\Arduino\libraries\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.cpp:688:28: error: too many arguments to function 'void rtc_clk_apll_enable(bool)'
688 | rtc_clk_apll_enable(false,0x04,0xA4,0x6,1);
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Joshu\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-442a798083/esp32/include/soc/esp32/include/soc/rtc.h:242:6: note: declared here
242 | void rtc_clk_apll_enable(bool enable);
| ^~~~~~~~~~~~~~~~~~~

exit status 1

Compilation error: exit status 1

Update frame buffer pointer upon completion of rendering a frame

When video_isr() renders beyond _active_lines it should be safe for us to swap out the _lines frame buffer pointer for another. This mechanism will allow functionality like double-buffering, which completely eliminates video tearing, compared to waitForFrame() which could only reduce it.

Software design questions for this advanced scenario:

  • How will the developer submit a new frame buffer pointer in preparation for this swap?
  • How will the developer know the swap is complete? Once complete they can safely start rendering to the previous buffer.
  • What is a fast but ISR-safe way for video_isr() to send this notification?

Related:

  • Double (or more) buffering will require a lot more memory, and the developer needs control over memory. This will either require #3 or a superset of that functionality.

How switch off the display

HI Roger thank you for you great job, I need to switch off the display after some time, there are some function for do that ?

Negative X or Y coordinates cause core panic

Bug in version 1.0.0 found by @emilyvelasco

My code originally used unsigned integers, where negative numbers were impossible by definition. So for bounds checking it only checked the high end, that X did not exceed 255 and Y did not exceed 239.

When it was adapted to use signed integers in conformance to Adafruit GFX declarations, negative numbers became possible, but there were no checks to ensure X and Y were positive numbers.

Using negative coordinates would then step outside of allocated frame buffer memory, an access violation causing core panic.

What version of the Arduino ESP32 core to use?

Hi,
I've tried this on an Espressif ESP32 Dev Module and it seems to hang in loop().
I've tried versions 2.0.2, 2.0.1, 1.0.6, and 1.0.4 of the ESP32 Arduino core.
All on Arduino IDE 1.8.19.

Also, what settings for things like CPU frequency, core, flash speed, etc. are you using?

Screen Shot 2022-02-28 at 1 47 23p

Shifted display

I developed on an ESP32-Wroom, a clock based on GPS. I need to display the time on two devices, an LCD and a TV screen.
I prepare 4 records containing my output. When time come I shoot my records on the LCD and on the TV screen.
I use <hd44780.h> and <hd44780ioClass/hd44780_I2Cexp.h> to manage the LCD and <ESP_8_BIT_GFX.h> to manage the TV part, inspired by hello world example.
Both displays are updated every second with exactly the same data.

Code is about 700 lines and requires a GPS module and both display
Everything look fine except the the TV display is one second after the TFT display.
I swapped the display so TV is first but that did not do any change.
When I compare the time on my PC, my phone, DC77, and LCD, all of them are in line.
Any clue ?
I ran some more tests to clearly identify the problem.
assume my code display the time every second. I observed that the LCD time is one second ahead of the TV time so I distorted the TV time and display it every 10 seconds.

    lcd.setCursor(0, 0);
    lcd.print(rec_0);

    lcd.setCursor(0, 1); 
    lcd.print(rec_1);
 
  v_ctr = v_ctr + 1;
  if (v_ctr <10) return;
  v_ctr = 0;
  // TV for the next frame to minimize chance of visible tearing
  videoOut.waitForFrame();
    // Clear screen
  videoOut.fillScreen(0);
  videoOut.setTextColor(color);
  videoOut.setTextSize(2);

  videoOut.setCursor(5, 90);
  videoOut.print(rec_0); 
  videoOut.setCursor(5, 130);
  videoOut.print(rec_1);

TV display time is 10 seconds late. When LCD time switch to 42, TV time switches to 32.

I also ran another test.
I added a statement videoOut.waitForFrame(); at the end . Both times on LCD and on TV where identical but the image was tearing.
This issue is not visible in the example Hello world as the code is continuously looping.

Override Adafruit_GFX::drawFastHLine() and related methods for better performance.

An implementation of Adafruit_GFX is absolutely required to implement drawPixel() by overriding that pure virtual method. Everything beyond that point is optional, as they all offer fallback implementations that eventually end up at drawPixel().

For v1.0.0, the only other override was fillScreen() so there are a few promising venues for performance enhancements. Top of the list is drawFastHLine(). ESP_8_BIT (from which this project was derived) set up the frame buffer as an array of pointers to horizontal lines, so drawFastHLine() can be optimized into a single memset() call.

In contrast, drawFastVLine() is less likely to offer significant improvements, because we have to step through the line array anyway, but it might be worth a shot.

Once drawFastHLine() has been implemented, it should be tested to verify it is indeed faster and not counterintuitively slower for some surprising reason. (Requires #4 ) Once confirmed, it might be worth overriding a few other methods because their default implementation calls into drawFastVLine() instead of drawFastHLine(). (Example: fillRect()) Switching them to drawFastHLine() should be faster.

Compilation Error

Hi...

I've tried the library and got this error:

SP_8_BIT_composite.cpp:178:33: error: pal_yuyv causes a section type conflict with _sync_type
const static DRAM_ATTR uint32_t pal_yuyv[] = {

Any advise, please....?

Regards,
Jhon R Putra

Frame buffer memory allocation needs to be tolerant of fragmented memory space

The default code path begin() will allocate 61KB of memory.

  • 60KB for the frame buffer itself
  • Another ~1KB (240 32-bit pointers) for the lines array.

This is fine for small simple sketches, but some developers may want control over this memory.

  • It is conceivable to have 61KB of memory free but not contiguous, which would cause memory allocation call to fail. A developer could allocate memory in smaller chunks and assemble them via the lines array.
  • The developer may prefer to statically allocate an array at compile time.
  • The developer may prefer to control memory allocation for any other reason.

To support this scenario, add an alternate begin() that accepts a pointer to caller-allocated memory. The caller would be responsible for freeing this memory as well.

There was an early draft of this feature, but it was removed before v1.0.0 release in an effort to keep API surface area simple in accordance with Arduino API Style Guide.

This feature can be reintroduced if there is demand for it.

hd44780 and hd44780ioClass

I am working on a big project mixing up GPS, LCD 20x4, softwareserial,Wire ans Streaming. All work fine on Arduino Uno. A museum is interested in this development with a larger screen. TVOUT is out of age, so I looked at ESP_8_bit with the GFX library.
Problem number 1 ESP_8_bit requires an ESP processor, so I moved my code toe ESP-Wroom.;; and hit the second problem.
There seem to have incompatibility(ies) between all my #include.
I then decided to split the application
One processor to crunch the data, manage the LCD and transmit the data to an ESP processor capable of managing the TV screen.
Now I have to decide of the protocol to use for the transfer. Ideally I2C is already in my code by mean of HD44780 if we simply broadcast to all the participants, LCD and the ESP processor.
Question: when used in conjunction with STREAMING, is HD44780 broadcasting or addressing a single device? A physical test show that 2 LCDs can display the same data without problem.
Second question/suggestion I need help (or working example) to receive the data on the ESP for large TV display
Thankyou

Compiler warning: format '%s' expects argument of type 'char*', but argument 2 has type 'int' [-Wformat=] [73, 46]

Hi,

I was trying to use this library for testing, and my compiler warns about this:

format '%s' expects argument of type 'char*', but argument 2 has type 'int' [-Wformat=] [73, 46]

int n = line_width*2*ch;
if (n >= 4092) {
printf("DMA chunk too big:%s\n",n);
return -1;
}

The compiler could be right, as far as I know you can't use %s on an int.

Compiler: xtensa-esp32-elf-gcc (crosstool-NG crosstool-ng-1.22.0-97-gc752ad5) 5.2.0 (platformio-ide on vscode, reasonably up-to-date)

Regards, Alex.

Reduce importance level of X/Y clamp logs

Design change from version 1.0.0 based on feedback from @emilyvelasco

While investigating #8 it became obvious that clamping X/Y coordinate to fit on screen is not an error condition. Clamping can occur as part of normal operation, such as drawing a circle close to the edge of the screen.

However, there are still occasions when it is useful to know when clamping has occurred, because it still represent unnecessary work that is wasted.

Compromise: Drop "value has been clamped" logging messages from "Error" to "Verbose" level of importance.

drawRect() does not function correctly when setRotation() is set to "1"

When using drawRect() and setting setRotation() to 1, the vertical and horizontal bars of drawn rectangles become misaligned.

I did not attempt to recreate the behavior with setRotation set to 2 or 3. fillRect seems to work correctly, however.

Code included below:

#include "Keypad.h"
#define BUZZER_PIN 4 // ESP32 GIOP21 pin connected to Buzzer's pin
/*
Example for ESP_8_BIT color composite video generator library on ESP32.
Connect GPIO25 to signal line, usually the center of composite video plug.
GFX Hello World
This demonstrates using the ESP_8_BIT_GFX class, which inherits from the
Adafruit GFX base class to deliver an easy to use graphics API. Draws two
rectangles that cycle around the border of the screen. The amount of corners
cut off from these rectangle show the amount of overscan on a particular
screen. In the middle of two rectangles are a bit of text drawn using
Adafruit GFX print() API.
Copyright (c) Roger Cheng
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <ESP_8_BIT_GFX.h>

// A list of 8-bit color values that work well in a cycle.
uint8_t colorCycle[] = {
  0xFF, // White
  0xFE, // Lowering blue
  0xFD,
  0xFC, // No blue
  0xFD, // Raising blue
  0xFE,
  0xFF, // White
  0xF3, // Lowering green
  0xE7,
  0xE3, // No green
  0xE7, // Raising green
  0xF3,
  0xFF, // White
  0x9F, // Lowering red
  0x5F,
  0x1F, // No red
  0x5F, // Raising red
  0x9F,
  0xFF
};

// Create an instance of the graphics library
ESP_8_BIT_GFX videoOut(true /* = NTSC */, 8 /* = RGB332 color */);

char * words[12] = {
  "one", "two", "three", "four" , "five" , "six" , "seven" , "eight" , "nine" , "asterisk" , "zero" , "pound"};

 
const byte ROWS = 4; // number of rows
const byte COLS = 3; // number of columns
char keys[ROWS][COLS] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'#','0','*'}
};

byte rowPins[ROWS] = {23, 22, 3, 21}; 
byte colPins[COLS] = {19, 18, 5};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
 
void setup()
{
  videoOut.begin();
  videoOut.setRotation(1);
  //Serial.begin(9600);
  pinMode(BUZZER_PIN, OUTPUT);
}

void buzzy(int beepLength, int pitch){
int starttime = millis();
int endtime = starttime;

//int endtime = starttime+beepLength;
while ((endtime - starttime) <=beepLength){
  digitalWrite(BUZZER_PIN, HIGH); delay(pitch);
  digitalWrite(BUZZER_PIN, LOW); delay(pitch);
  endtime = millis();
 }
     
}

void fillButton(int buttonX int buttonY int buttonW int buttonH int buttonColor int buttonTime){
  videoOut.setRotation(1);
  videoOut.fillRect(buttonX, buttonY, buttonW, buttonH, buttonColor);
  delay(buttonTime);
  videoOut.fillScreen(0);
}



void printKey( char key) {
  switch(key) {
   case '0': Serial.println("column 2, row 4"); buzzy(50, 1); videoOut.fillRect(132, 122, 48, 28, 0xFF); break;
   case '1': Serial.println("column 1, row 6"); buzzy(50, 1); videoOut.fillRect(78, 186, 48, 28, 0xFF); break;
   case '2': Serial.println("column 2, row 1"); buzzy(50, 1); videoOut.fillRect(132, 26, 48, 28, 0xFF); break;
   case '3': Serial.println("column 1, row 1"); buzzy(50, 1); videoOut.fillRect(78, 26, 48, 28, 0xFF); break;
   case '4': Serial.println("column 2, row 6"); buzzy(50, 1); videoOut.fillRect(132, 186, 48, 28, 0xFF); break;
   case '5': Serial.println("column 2, row 2"); buzzy(50, 1); videoOut.fillRect(132, 58, 48, 28, 0xFF); break;
   case '6': Serial.println("column 1, row 2"); buzzy(50, 1); videoOut.fillRect(78, 58, 48, 28, 0xFF); break;
   case '7': Serial.println("column 1, row 5"); buzzy(50, 1); videoOut.fillRect(78, 154, 48, 28, 0xFF); break;
   case '8': Serial.println("column 2, row 3"); buzzy(50, 1); videoOut.fillRect(132, 90, 48, 28, 0xFF); break;
   case '9': Serial.println("column 1, row 3"); buzzy(50, 1); videoOut.fillRect(78, 90, 48, 28, 0xFF); break;    
   case '*': Serial.println("column 1, row 4"); buzzy(50, 1); videoOut.fillRect(78, 122, 48, 28, 0xFF); break;
   case '#': Serial.println("column 2, row 5"); buzzy(50, 1); videoOut.fillRect(132, 186, 48, 28, 0xFF); break;    
  }
  
}

 
void loop()
{
    videoOut.waitForFrame();
    videoOut.setRotation(3);

  // Clear screen
  videoOut.fillScreen(0);

  // Left column
  videoOut.drawRect(78, 26, 48, 28, 0xFF);
  videoOut.drawRect(78, 58, 48, 28, 0xFF);
  videoOut.drawRect(78, 90, 48, 28, 0xFF);
  videoOut.drawRect(78, 122, 48, 28, 0xFF);
  videoOut.drawRect(78, 154, 48, 28, 0xFF);
  videoOut.drawRect(78, 186, 48, 28, 0xFF);
  
  //Right column
  videoOut.drawRect(132, 26, 48, 28, 0xFF);
  videoOut.drawRect(132, 58, 48, 28, 0xFF);
  videoOut.drawRect(132, 90, 48, 28, 0xFF);
  videoOut.drawRect(132, 122, 48, 28, 0xFF);
  videoOut.drawRect(132, 154, 48, 28, 0xFF);
  videoOut.drawRect(132, 186, 48, 28, 0xFF);
  videoOut.drawRect(132, 186, 48, 28, 0xFF);
  char key = keypad.getKey(); 
  if (key != NO_KEY){
  
    printKey(key);
 
  }
}

ใ€Qใ€‘ Do I need to modify this library to use it on my Japanese NTSC-J standard TV?

Hello. @Roger-random
I am grateful to be able to use your library. Thank you.๐Ÿ‘

I wrote your library into lolin32 and tweaked it into the composite input of a Japanese SHARP digital TV.
When I did so, I was able to display the text and graphics, but the text and graphics oscillated up and down periodically. I am not sure, but I am wondering if there is something wrong with the vertical sync signal.
Is this library not compatible with "NTSC-J"?
How can I correct the vertical oscillation of the display?

Thank you very much for your help.

PS
Maybe it's just noise coming from the ESP32 board.

Library does not work with "Arduino ESP32 Boards"

I am the stupid user.
I just discover theis app after failure to migrate TVout from arduino uno to arduino nano ESP32.
So I downloaded and install ESP_8_bit like indicated and had a try with Hello world.
Beautiful error message ResolveLibrary(driver/dac.h)
I cannot belive it
What is this mess ?

Need to verify PAL functionality

When I extracted the video signal generation code from ESP_8_BIT, I tried to preserve PAL support. Since I only have a NTSC TV, I couldn't verify that PAL support is still functional. I would appreciate one of the following:

  1. Someone to test PAL and verify it works.
  2. Someone to fix any PAL problems and create a pull request.

Note that I can't do anything about "PAL doesn't work" absent a fix, as I have no way to diagnose, debug, or verify fixes without a PAL TV.

setRotation support

Would it be possible to support the setRotation function present in the Adafruit GFX library in this library? There might be occasions where someone would need to display an image on a composite video monitor rotated to 90, 180 or 270 degrees.

Add some way to see performance metrics

The typical pattern is to call waitForFrame() before drawing, in order to minimize visible tearing as we draw the next frame. But as the drawing tasks get more complex, eventually they'll take too long and spill over into rendering time for the next frame.

When writing a sketch, right now there's no way to tell how close we are to this limit.

Enhancement: track a few numbers from a high resolution timing mechanism, so we can calculate how much processing time was consumed in the drawing routine as compared to time available between frame renders.

Precedent: the ESP_8_BIT project (from which this project was derived) had performance metric mechanisms using numbers from xthal_get_ccount(). But that didn't translate easily to something generalized yet conforms to Arduino API Style Guide. Thus it did not make the transition for v1.0.0, but the topic is worth revisiting.

Make better Documentation and improve collision and bitmap support.

Roger, i think that better documentation on this library, will make it better for beginners like me, specially in the bitmap and collision departament.

I genuinely think that your library can be better than fabgl and esp32lib, because of the lower hardware requirements(No external DAC).

How to stop display initiation?

I have run into an issue where after init this library using "display.begin();" I am having the issue where the Bluetooth code I am running alongside this no longer functions properly. Is there a way I can undo the display.begin()? My plan is just to declare display.begin(); whenever I need the display and then otherwise have it deinitialized to enable the rest of my code to function properly. Thanks!

Code not compiling with the verified latest versions

For some unknown to me reason the code (whhich used to work perfectly fine) doesn't compile properly anymore. The code still loads onto the ESP32 board but the image on the screen seems not to sync horizontally or it generates fleshing horizontal segments in random positions. I do have PAL and I think I set it right. The board I'm using should work properly (It used to work with previous vesions of the code). Even trying to upload older versions of the code the problem persists.
I'm working on this with the "Hello world" example.

Merry Christmas to everyone an thank you for the help!

Warning when compiling.

ESP_8_BIT_composite-main\ESP_8_BIT_GFX.cpp: In member function 'uint8_t ESP_8_BIT_GFX::getColor8(uint16_t)':
ESP_8_BIT_composite-main\ESP_8_BIT_GFX.cpp:262:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^

This message is displayed whenever I launch the IDE to check the code. I imagine it is not desirable. So I'm bringing it for you to review.

Arduino IDE 1.8.19
Esp Board (Arduino IDE): 2.0.3 -RC1

Error of conversion

\ESP_8_BIT_Color_Composite_Video_Library\ESP_8_BIT_composite.cpp:45:55: error: invalid conversion from 'const volatile void*' to 'volatile void*' [-fpermissive]
45 | video_isr(((lldesc_t*)I2S0.out_eof_des_addr)->buf); // get the next line of video
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
| |
| const volatile void*

Need a way to keep double buffers in sync with each other

Bug introduced by double-buffering (v1.1.0)

Instead of clearing the screen and redrawing everything on every frame, some rendering libraries are optimized so that they only draw what is changed from one frame to the next. But with two buffers, the default behavior results in each frame being drawn on top of the image data from TWO frames ago. Such libraries require the two buffers be kept in sync so the image deltas are applied correctly.

NTSC mode color not-correct

I am currently working with the NTSC mode and have come across an issue with the color palette. Despite implementing the NTSC mode, the colors do not appear as expected. I have attached an image that showcases the results of the following code:

videoOut.fillRect(0, 0, 35, 180, 0xFFFF);
videoOut.fillRect(35, 0, 35, 180, 0x001F);
videoOut.fillRect(70, 0, 35, 180, 0xF800);
videoOut.fillRect(105, 0, 35, 180, 0x07E0);
videoOut.fillRect(140, 0, 35, 180, 0x07FF);
videoOut.fillRect(175, 0, 35, 180, 0xF81F);
videoOut.fillRect(210, 0, 35, 180, 0xFFE0);

The displayed colors do not seem to match the expected output based on the provided color values.

I don't have a PAL-TV, but if I put the code in PAL mode and watch it on an NTSC-TV, it displays the correct colors with some flickering.
So I think there is a problem specific to the NTSC color palette.

Switching between 8bit/16bit color depth has no particular meaning.

I would appreciate any guidance or assistance in resolving this issue.

Thank you.

image_ 2

Compilation failures after updating to v3.x ESP32 Arduino Core

Symptom:

After updating to v3.x ESP32 Arduino Core, the first compilation error will be:

ESP_8_BIT_composite.cpp:45:55: error: invalid conversion from 'const volatile void*' to 'volatile void*' [-fpermissive]

HOWEVER, this is not the core issue, it's just the first breaking change to cause a error. (Default compiler flags changed in v2.x to v3.x.)

Downstream are deeper problems with this library's dependency on deprecated ESP32 legacy DAC driver, which has been (or will soon be) removed. Replacement DAC driver does not expose the same functionality.

Reference:

Espressif's documentation on breaking changes: https://docs.espressif.com/projects/arduino-esp32/en/latest/migration_guides/2.x_to_3.0.html

Workaround:

The current workaround is to downgrade ESP32 Arduino Core to an earlier version in the v2.x lineage. I understand this is not a satisfactory long-term solution.

Espressif esp32 arduino core version select

Why did Espressif do this?

The following is my observation/hypothesis/guess and not from Espressif.

Espressif seized upon the fame of the successful "ESP32" name and has released many variants. Some are VERY different from the original chip, but they are all supported by ESP-IDF which ESP32 Arduino Core is built upon. ESP-IDF has the job of keeping things in sync across the entire ESP32 library, so software developers won't have to worry about details of hardware differences between variants.

Unfortunately, this means ESP-IDF is restricted to the subset supported by all ESP variations on a peripheral. Creative hacks like the ESP_8_BIT composite video code (which this library is built from) depended on features outside of that lowest-common-denominator subset. So while the original ESP32 (no suffix) DAC can do this, but newer ESP32-[???] variation DAC could not, so it was removed from ESP-IDF.

Need help interfacing sd card with esp32

Hi roger
I need sd card support for the animatedgif example.And im struggling to do that as my attempts lead to no sucess.
Can you send me the example but instead of using
.h file it opens a file /example.gif and also add the ability to cycle through gifs like the animatedgif library examples.
Thanks!!

Support for external DAC

It appears that the newer ESP32 boards (S2/S3 etc.) no longer have an on board DAC :(
Do you know if it would be possible to support an external DAC instead?
Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.