PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

335 lines
9.5KB

  1. /***************************************************
  2. ** DemoSauce! **
  3. State-of-the-art graphics for your beautiful TFT display.
  4. Greetz to the Portland Dorkbot Crew!!!!
  5. Programmed by Zach Archer (@zkarcher) :: http://controlzinc.com/
  6. Using hardware by Thomas Hudson (@hydronics) :: http://thomashudson.org/
  7. Usage:
  8. * Connect a microphone to MIC_PIN.
  9. * Connect TFT backlight to BACKLIGHT_PIN.
  10. MIT license, all text above must be included in any redistribution
  11. ****************************************************/
  12. // https://github.com/zkarcher/demosauce
  13. #include "SPI.h"
  14. #include "ILI9488_t3.h"
  15. #include "ILI9488_t3_font_Arial.h"
  16. #include "FrameParams.h"
  17. // Animations
  18. #include "Checkerboard.h"
  19. #include "Cube3D.h"
  20. #include "Leaves.h"
  21. #include "MagentaSquares.h"
  22. //#include "MicCheck.h"
  23. #include "PlasmaCloud.h"
  24. #include "PlasmaYellow.h"
  25. #include "Sphere3D.h"
  26. #include "TriangleWeb.h"
  27. #include "TwistyText.h"
  28. #include "Waveform.h"
  29. // Transitions
  30. #include "TransitionDither.h"
  31. #include "TransitionHalftone.h"
  32. #include "TransitionScroll.h"
  33. #include "TransitionSquares.h"
  34. const boolean DO_BENCHMARKS = true;
  35. const uint32_t SERIAL_BAUD_RATE = 9600;
  36. const boolean DEBUG_ANIM = false; // dev: for hacking on one animation.
  37. const uint_fast8_t DEBUG_ANIM_INDEX = 0;
  38. const boolean DEBUG_TRANSITION = false; // dev: set to true for short animation durations
  39. const int_fast8_t DEBUG_TRANSITION_INDEX = -1; // Supports -1: chooses a transition at random
  40. const int_fast16_t DEFAULT_ANIM_TIME = 10.0f * 1000.0f; // ms
  41. // TFT pins
  42. const uint8_t TFT_DC = 9;
  43. const uint8_t TFT_CS = 10;
  44. // Use hardware SPI (#13, #12, #11) and the above for CS/DC
  45. ILI9488_t3 tft = ILI9488_t3(&SPI, TFT_CS, TFT_DC,23);
  46. FrameParams frameParams;
  47. long previousMillis = 0;
  48. Checkerboard * _checkerboard = new Checkerboard();
  49. Cube3D * _cube3D = new Cube3D();
  50. Leaves * _leaves = new Leaves();
  51. MagentaSquares * _magentaSquares = new MagentaSquares();
  52. PlasmaCloud * _plasmaCloud = new PlasmaCloud();
  53. PlasmaYellow * _plasmaYellow = new PlasmaYellow();
  54. Sphere3D * _sphere3D = new Sphere3D();
  55. TriangleWeb * _triangleWeb = new TriangleWeb();
  56. TwistyText * _twistyText = new TwistyText();
  57. Waveform * _waveform = new Waveform();
  58. TransitionDither * _transDither = new TransitionDither();
  59. TransitionHalftone * _transHalftone = new TransitionHalftone();
  60. TransitionScroll * _transScroll = new TransitionScroll();
  61. TransitionSquares * _transSquares = new TransitionSquares();
  62. BaseAnimation **anims; // Array of pointers to BaseAnimation's. Initialized in setup() below.
  63. int_fast8_t animCount;
  64. BaseAnimation *activeAnim = 0;
  65. int_fast16_t animTimeLeft = 0;
  66. BaseAnimation *nextAnim;
  67. BaseTransition **transitions;
  68. int_fast8_t transCount;
  69. boolean isTransition = true;
  70. BaseTransition *activeTransition = 0;
  71. // Benchmarks
  72. uint32_t frameCount;
  73. // Search the anims[] aray for the activeAnim pointer. If found, return the array index.
  74. int_fast8_t getActiveAnimIndex() {
  75. for( int_fast8_t i=0; i<animCount; i++ ) {
  76. if( anims[i] == activeAnim ) return i;
  77. }
  78. return -1; // not found
  79. }
  80. void setup() {
  81. tft.begin();
  82. tft.setRotation( 3 );
  83. tft.fillScreen(ILI9488_BLACK);
  84. // Serial
  85. if( DO_BENCHMARKS ) {
  86. Serial.begin( SERIAL_BAUD_RATE );
  87. tft.setTextColor(ILI9488_YELLOW);
  88. tft.setFont(Arial_18);
  89. tft.setCursor(98, 42);
  90. tft.print("waiting for");
  91. tft.setFont(Arial_24);
  92. tft.setCursor(100, 80);
  93. tft.print("Arduino");
  94. tft.setCursor(60, 120);
  95. tft.print("Serial Monitor");
  96. tft.setTextColor(ILI9488_GREEN);
  97. tft.setFont(Arial_18);
  98. while (!Serial && millis() < 8000) { // wait for Arduino Serial Monitor
  99. tft.fillRect(118, 182, 42, 18, ILI9488_BLACK);
  100. tft.setCursor(118, 182);
  101. tft.print((8000.0 - (float)millis()) / 1000.0, 1);
  102. tft.print(" sec");
  103. delay(100);
  104. }
  105. }
  106. previousMillis = millis();
  107. // Clear
  108. uint16_t w = tft.width();
  109. uint16_t h = tft.height();
  110. tft.fillRect( 0, 0, w, h, 0x0 );
  111. tft.setRotation( 3 ); // ribbon cable on left
  112. tft.setScroll( 0 );
  113. // Populate anims in the order you want them to display.
  114. BaseAnimation* ANIMS_TEMP[] = {
  115. _twistyText,
  116. _plasmaCloud,
  117. _waveform,
  118. _magentaSquares,
  119. _sphere3D,
  120. _checkerboard,
  121. _leaves,
  122. _cube3D,
  123. _plasmaYellow,
  124. _triangleWeb
  125. };
  126. animCount = sizeof( ANIMS_TEMP ) / sizeof( BaseAnimation* );
  127. // Retain ANIMS_TEMP objects permanently
  128. anims = (BaseAnimation**)malloc( animCount * sizeof(BaseAnimation*) );
  129. for( int_fast8_t i=0; i<animCount; i++ ) {
  130. anims[i] = ANIMS_TEMP[i];
  131. anims[i]->init( tft ); // Initalize all animations
  132. }
  133. BaseTransition* TRANS_TEMP[] = {
  134. _transDither,
  135. _transHalftone,
  136. _transScroll,
  137. _transSquares
  138. };
  139. transCount = sizeof( TRANS_TEMP ) / sizeof( BaseTransition* );
  140. // Retain TRANS_TEMP objects permanently
  141. transitions = (BaseTransition**)malloc( transCount * sizeof(BaseTransition*) );
  142. for( int_fast8_t i=0; i<transCount; i++ ) {
  143. transitions[i] = TRANS_TEMP[i];
  144. transitions[i]->init( tft );
  145. }
  146. // Start!
  147. if( !activeAnim ) {
  148. if( DEBUG_ANIM ) {
  149. startAnimation( anims[DEBUG_ANIM_INDEX] );
  150. } else {
  151. startAnimation( anims[0] );
  152. }
  153. }
  154. }
  155. void startAnimation( BaseAnimation *newAnim ) {
  156. isTransition = false;
  157. activeAnim = newAnim;
  158. tft.fillScreen( activeAnim->bgColor() );
  159. tft.setScroll( 0 );
  160. activeAnim->reset( tft );
  161. animTimeLeft = DEFAULT_ANIM_TIME;
  162. if( DEBUG_TRANSITION ) animTimeLeft = 2000;
  163. if( DO_BENCHMARKS ) {
  164. Serial.println("---");
  165. Serial.print( activeAnim->title() );
  166. if( activeAnim->willForceTransition() ) {
  167. // TwistyText does not obey DEFAULT_ANIM_TIME
  168. Serial.println("");
  169. } else {
  170. Serial.print(" [");
  171. Serial.print( (uint8_t)(animTimeLeft / 1000.0f) );
  172. Serial.println(" secs]");
  173. }
  174. frameCount = 0;
  175. }
  176. }
  177. void loop() {
  178. // Frame multiplier
  179. long newMillis = millis();
  180. uint_fast8_t elapsed = newMillis - previousMillis;
  181. previousMillis = newMillis;
  182. frameParams.timeMult = elapsed * (60.0f / 1000); // 1.0==exactly 60fps. 4.0==15fps, 4x slower
  183. // Get some audio input
  184. const uint_fast8_t SAMPLES_PER_FRAME = 1;
  185. frameParams.audioPeak = 0;
  186. uint_fast16_t sum = 0;
  187. for( uint_fast8_t s=0; s<SAMPLES_PER_FRAME; s++ ) {
  188. uint_fast16_t sample = abs( random(0,1023) - 511 );
  189. frameParams.audioPeak = max( frameParams.audioPeak, sample );
  190. sum += sample;
  191. }
  192. frameParams.audioMean = sum * (1.0 / (512*SAMPLES_PER_FRAME)); // Range: 0..1
  193. frameParams.audioPeak = min( (uint_fast16_t)frameParams.audioPeak, (uint_fast16_t)511 );
  194. if( !isTransition ) {
  195. activeAnim->perFrame( tft, frameParams );
  196. animTimeLeft -= elapsed;
  197. if( DO_BENCHMARKS ) frameCount++;
  198. }
  199. // Has this animation expired?
  200. boolean willForceTransition = activeAnim->willForceTransition();
  201. boolean forceTransitionNow = activeAnim->forceTransitionNow();
  202. // Debugging transitions: Ignore animations hogging the screen
  203. if( DEBUG_TRANSITION ) willForceTransition = false;
  204. if( !DEBUG_ANIM ) {
  205. if( (!willForceTransition && (animTimeLeft <= 0)) || forceTransitionNow ) {
  206. // If the transition has not started yet, then start it.
  207. if( !isTransition ) {
  208. isTransition = true;
  209. nextAnim = anims[ (getActiveAnimIndex() + 1) % animCount ];
  210. /*
  211. // Print some debug stuff
  212. if( DO_BENCHMARKS ) {
  213. Serial.println("---------- Testing sinf() vs sin() performance");
  214. unsigned long then = micros();
  215. float z = 0;
  216. for( uint32_t i=0; i<50000; i++ ) {
  217. z += sinf( (float)i );
  218. }
  219. unsigned long now = micros();
  220. Serial.print("sinf:");
  221. Serial.println( now - then );
  222. Serial.print(" z:");
  223. Serial.println( z );
  224. then = micros();
  225. z = 0;
  226. for( uint32_t i=0; i<50000; i++ ) {
  227. z += sin( (float)i );
  228. }
  229. now = micros();
  230. Serial.print(" sin:");
  231. Serial.println( now - then );
  232. Serial.print(" z:");
  233. Serial.println( z );
  234. }
  235. */
  236. // When we loop back to the first animation, shuffle the other ones for variety.
  237. if( nextAnim == anims[0] ) {
  238. for( int_fast8_t i=1; i<animCount-1; i++ ) {
  239. uint_fast8_t shuffleIdx = random( i, animCount );
  240. BaseAnimation* temp = anims[i];
  241. anims[i] = anims[shuffleIdx];
  242. anims[shuffleIdx] = temp;
  243. }
  244. }
  245. // Choose a random transition
  246. activeTransition = transitions[ random(transCount) ];
  247. if( DEBUG_TRANSITION && (DEBUG_TRANSITION_INDEX >= 0) ) {
  248. activeTransition = transitions[ DEBUG_TRANSITION_INDEX ];
  249. }
  250. activeTransition->restart( tft, nextAnim->bgColor() );
  251. // Benchmark: show how many frames the animation completed while alive.
  252. if( DO_BENCHMARKS ) {
  253. Serial.print("Frame count (more is better): ");
  254. Serial.print( frameCount );
  255. if( !activeAnim->willForceTransition() ) {
  256. Serial.print( " (" );
  257. Serial.print( (float)frameCount / (DEFAULT_ANIM_TIME / 1000.0f) );
  258. Serial.println(" FPS)");
  259. } else {
  260. Serial.println("");
  261. }
  262. }
  263. }
  264. // After the transition ends, advance to the next animation
  265. activeTransition->perFrame( tft, frameParams );
  266. if( activeTransition->isComplete() ) {
  267. startAnimation( nextAnim );
  268. }
  269. }
  270. }
  271. }