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.

342 satır
9.6KB

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