Skip to content

Commit 4a6201f

Browse files
author
ladyada
committed
1 parent 7adec62 commit 4a6201f

File tree

1 file changed

+335
-0
lines changed

1 file changed

+335
-0
lines changed
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
// please read credits at the bottom of file
2+
3+
#include <Adafruit_Arcada.h>
4+
#include "GifDecoder.h"
5+
6+
/*************** Display setup */
7+
Adafruit_Arcada arcada;
8+
GifDecoder<ARCADA_TFT_WIDTH, ARCADA_TFT_HEIGHT, 12> decoder;
9+
10+
File file;
11+
int16_t gif_offset_x, gif_offset_y;
12+
#define GIF_DIRECTORY "/" // on SD or QSPI
13+
14+
float hotTemp = 24; // how warm it has to be to be considered hot
15+
float coldTemp = 20; // how cool it has to be to be considered cold
16+
17+
18+
#if defined(ARDUINO_NRF52840_CIRCUITPLAY)
19+
void setupTemperature() {
20+
}
21+
float readTemperature() {
22+
return CircuitPlayground.temperature();
23+
}
24+
#endif
25+
26+
#define BRIGHTER_BUTTON ARCADA_BUTTONMASK_A
27+
#define DIMMER_BUTTON ARCADA_BUTTONMASK_B
28+
29+
30+
// Setup method runs once, when the sketch starts
31+
void setup() {
32+
decoder.setScreenClearCallback(screenClearCallback);
33+
decoder.setUpdateScreenCallback(updateScreenCallback);
34+
decoder.setDrawPixelCallback(drawPixelCallback);
35+
decoder.setDrawLineCallback(drawLineCallback);
36+
37+
decoder.setFileSeekCallback(fileSeekCallback);
38+
decoder.setFilePositionCallback(filePositionCallback);
39+
decoder.setFileReadCallback(fileReadCallback);
40+
decoder.setFileReadBlockCallback(fileReadBlockCallback);
41+
42+
// Start arcada!
43+
if (!arcada.arcadaBegin()) {
44+
Serial.println("Couldn't start Arcada");
45+
while(1) yield();
46+
}
47+
// If we are using TinyUSB & QSPI we will have the filesystem show up!
48+
arcada.filesysBeginMSD();
49+
50+
//while (!Serial) delay(10);
51+
52+
Serial.begin(115200);
53+
Serial.println("Animated GIFs Demo");
54+
55+
arcada.displayBegin();
56+
arcada.display->fillScreen(ARCADA_BLUE);
57+
arcada.setBacklight(255);
58+
59+
if (arcada.filesysBegin()) {
60+
Serial.println("Found filesystem!");
61+
} else {
62+
arcada.haltBox("No filesystem found! For QSPI flash, load CircuitPython. For SD cards, format with FAT");
63+
}
64+
65+
if (! arcada.loadConfigurationFile()) {
66+
//arcada.infoBox("No configuration file found, using default 10 seconds per GIF");
67+
arcada.display->fillScreen(ARCADA_BLUE);
68+
} else {
69+
if (arcada.configJSON.containsKey("hot_temp")) {
70+
hotTemp = arcada.configJSON["hot_temp"];
71+
}
72+
if (arcada.configJSON.containsKey("cold_temp")) {
73+
coldTemp = arcada.configJSON["hot_temp"];
74+
}
75+
}
76+
}
77+
78+
uint32_t cycle_start = 0L;
79+
int8_t currGIF = 0, nextGIF = 0; // 0 for no change, +1 for hot gif, -1 for cool gif
80+
81+
void loop() {
82+
if (arcada.recentUSB()) {
83+
yield();
84+
return; // prioritize USB over GIF decoding
85+
}
86+
87+
// Check button presses
88+
arcada.readButtons();
89+
uint8_t buttons = arcada.justPressedButtons();
90+
if (buttons & BRIGHTER_BUTTON) {
91+
int16_t newbrightness = arcada.getBacklight(); // brightness up
92+
newbrightness = min(255, newbrightness+25); // about 10 levels
93+
Serial.printf("New brightness %d", newbrightness);
94+
arcada.setBacklight(newbrightness, true); // save to disk
95+
}
96+
if (buttons & DIMMER_BUTTON) {
97+
int16_t newbrightness = arcada.getBacklight(); // brightness down
98+
newbrightness = max(25, newbrightness-25); // about 10 levels
99+
Serial.printf("New brightness %d", newbrightness);
100+
arcada.setBacklight(newbrightness, true); // save to disk
101+
}
102+
103+
uint32_t now = millis();
104+
105+
// at least one 'cycle' elapsed, check if its time to change gifs
106+
if(decoder.getCycleNo() > 1) {
107+
// Print the stats for this GIF
108+
char buf[80];
109+
int32_t frames = decoder.getFrameCount();
110+
int32_t cycle_design = decoder.getCycleTime(); // Intended duration
111+
int32_t cycle_actual = now - cycle_start; // Actual duration
112+
int32_t percent = 100 * cycle_design / cycle_actual;
113+
sprintf(buf, "[%ld frames = %ldms] actual: %ldms speed: %ld%%",
114+
frames, cycle_design, cycle_actual, percent);
115+
Serial.println(buf);
116+
117+
float temp = readTemperature();
118+
Serial.printf("Temp = %0.1f C\n", temp);
119+
if (temp >= hotTemp) {
120+
file = arcada.open("hot.gif");
121+
} else if (temp <= coldTemp) {
122+
file = arcada.open("cold.gif");
123+
} else {
124+
file = arcada.open("neutral.gif");
125+
}
126+
127+
cycle_start = now;
128+
129+
if (!file) {
130+
Serial.println("Gif not found!");
131+
return;
132+
}
133+
134+
arcada.display->dmaWait();
135+
arcada.display->endWrite(); // End transaction from any prior callback
136+
arcada.display->fillScreen(ARCADA_BLACK);
137+
decoder.startDecoding();
138+
139+
// Center the GIF
140+
uint16_t w, h;
141+
decoder.getSize(&w, &h);
142+
Serial.print("Width: "); Serial.print(w); Serial.print(" height: "); Serial.println(h);
143+
if (w < arcada.display->width()) {
144+
gif_offset_x = (arcada.display->width() - w) / 2;
145+
} else {
146+
gif_offset_x = 0;
147+
}
148+
if (h < arcada.display->height()) {
149+
gif_offset_y = (arcada.display->height() - h) / 2;
150+
} else {
151+
gif_offset_y = 0;
152+
}
153+
}
154+
155+
decoder.decodeFrame();
156+
}
157+
158+
/******************************* Drawing functions */
159+
160+
void updateScreenCallback(void) { }
161+
162+
void screenClearCallback(void) { }
163+
164+
void drawPixelCallback(int16_t x, int16_t y, uint8_t red, uint8_t green, uint8_t blue) {
165+
arcada.display->drawPixel(x, y, arcada.display->color565(red, green, blue));
166+
#ifdef ADAFRUIT_MONSTER_M4SK_EXPRESS
167+
arcada.display2->drawPixel(x, y, arcada.display->color565(red, green, blue));
168+
#endif
169+
}
170+
171+
void drawLineCallback(int16_t x, int16_t y, uint8_t *buf, int16_t w, uint16_t *palette, int16_t skip) {
172+
uint16_t maxline = arcada.display->width();
173+
bool splitdisplay = false;
174+
175+
uint8_t pixel;
176+
//uint32_t t = millis();
177+
x += gif_offset_x;
178+
y += gif_offset_y;
179+
if (y >= arcada.display->height() || x >= maxline ) {
180+
return;
181+
}
182+
183+
#ifdef ADAFRUIT_MONSTER_M4SK_EXPRESS
184+
// two possibilities
185+
if ((x + w) > 2*maxline) {
186+
w = 2*maxline - x;
187+
}
188+
if ((x + w) > maxline) {
189+
splitdisplay = true; // split the gif over both displays
190+
}
191+
#else
192+
if (x + w > maxline) {
193+
w = maxline - x;
194+
}
195+
#endif
196+
if (w <= 0) return;
197+
198+
//Serial.printf("Line (%d, %d) %d pixels skipping %d\n", x, y, w, skip);
199+
200+
uint16_t buf565[2][w];
201+
bool first = true; // First write op on this line?
202+
uint8_t bufidx = 0;
203+
uint16_t *ptr;
204+
205+
for (int i = 0; i < w; ) {
206+
int n = 0, startColumn = i;
207+
ptr = &buf565[bufidx][0];
208+
// Handle opaque span of pixels (stop at end of line or first transparent pixel)
209+
if (skip == -1) {// no transparent pixels
210+
while(i < w) {
211+
ptr[n++] = palette[buf[i++]];
212+
}
213+
}
214+
else {
215+
while((i < w) && ((pixel = buf[i++]) != skip)) {
216+
ptr[n++] = palette[pixel];
217+
}
218+
}
219+
if (n) {
220+
arcada.display->dmaWait(); // Wait for prior DMA transfer to complete
221+
#ifdef ADAFRUIT_MONSTER_M4SK_EXPRESS
222+
arcada.display2->dmaWait(); // Wait for prior DMA transfer to complete
223+
#endif
224+
if (first) {
225+
arcada.display->endWrite(); // End transaction from prior callback
226+
arcada.display->startWrite(); // Start new display transaction
227+
#ifdef ADAFRUIT_MONSTER_M4SK_EXPRESS
228+
arcada.display2->endWrite(); // End transaction from prior callback
229+
arcada.display2->startWrite(); // Start new display transaction
230+
#endif
231+
first = false;
232+
}
233+
arcada.display->setAddrWindow(x + startColumn, y, min(maxline, n), 1);
234+
arcada.display->writePixels(ptr, min(maxline, n), false, true);
235+
#ifdef ADAFRUIT_MONSTER_M4SK_EXPRESS
236+
if (! splitdisplay) { // same image on both!
237+
arcada.display2->setAddrWindow(x + startColumn, y, min(maxline, n), 1);
238+
arcada.display2->writePixels(ptr, min(maxline, n), false, true);
239+
} else {
240+
arcada.display2->setAddrWindow(x + startColumn, y, n-maxline, 1);
241+
arcada.display2->writePixels(ptr+maxline, n-maxline, false, true);
242+
}
243+
#endif
244+
bufidx = 1 - bufidx;
245+
}
246+
}
247+
// arcada.display->dmaWait(); // Wait for last DMA transfer to complete
248+
#ifdef ADAFRUIT_MONSTER_M4SK_EXPRESS
249+
arcada.display2->dmaWait(); // Wait for last DMA transfer to complete
250+
#endif
251+
}
252+
253+
254+
bool fileSeekCallback(unsigned long position) {
255+
return file.seek(position);
256+
}
257+
258+
unsigned long filePositionCallback(void) {
259+
return file.position();
260+
}
261+
262+
int fileReadCallback(void) {
263+
return file.read();
264+
}
265+
266+
int fileReadBlockCallback(void * buffer, int numberOfBytes) {
267+
return file.read((uint8_t*)buffer, numberOfBytes); //.kbv
268+
}
269+
270+
271+
/*
272+
Animated GIFs Display Code for SmartMatrix and 32x32 RGB LED Panels
273+
274+
Uses SmartMatrix Library for Teensy 3.1 written by Louis Beaudoin at pixelmatix.com
275+
276+
Written by: Craig A. Lindley
277+
278+
Copyright (c) 2014 Craig A. Lindley
279+
Refactoring by Louis Beaudoin (Pixelmatix)
280+
281+
Permission is hereby granted, free of charge, to any person obtaining a copy of
282+
this software and associated documentation files (the "Software"), to deal in
283+
the Software without restriction, including without limitation the rights to
284+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
285+
the Software, and to permit persons to whom the Software is furnished to do so,
286+
subject to the following conditions:
287+
288+
The above copyright notice and this permission notice shall be included in all
289+
copies or substantial portions of the Software.
290+
291+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
292+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
293+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
294+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
295+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
296+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
297+
*/
298+
299+
/*
300+
This example displays 32x32 GIF animations loaded from a SD Card connected to the Teensy 3.1
301+
The GIFs can be up to 32 pixels in width and height.
302+
This code has been tested with 32x32 pixel and 16x16 pixel GIFs, but is optimized for 32x32 pixel GIFs.
303+
304+
Wiring is on the default Teensy 3.1 SPI pins, and chip select can be on any GPIO,
305+
set by defining SD_CS in the code below
306+
Function | Pin
307+
DOUT | 11
308+
DIN | 12
309+
CLK | 13
310+
CS (default) | 15
311+
312+
This code first looks for .gif files in the /gifs/ directory
313+
(customize below with the GIF_DIRECTORY definition) then plays random GIFs in the directory,
314+
looping each GIF for displayTimeSeconds
315+
316+
This example is meant to give you an idea of how to add GIF playback to your own sketch.
317+
For a project that adds GIF playback with other features, take a look at
318+
Light Appliance and Aurora:
319+
https://github.com/CraigLindley/LightAppliance
320+
https://github.com/pixelmatix/aurora
321+
322+
If you find any GIFs that won't play properly, please attach them to a new
323+
Issue post in the GitHub repo here:
324+
https://github.com/pixelmatix/AnimatedGIFs/issues
325+
*/
326+
327+
/*
328+
CONFIGURATION:
329+
- If you're using SmartLED Shield V4 (or above), uncomment the line that includes <SmartMatrixShieldV4.h>
330+
- update the "SmartMatrix configuration and memory allocation" section to match the width and height and other configuration of your display
331+
- Note for 128x32 and 64x64 displays with Teensy 3.2 - need to reduce RAM:
332+
set kRefreshDepth=24 and kDmaBufferRows=2 or set USB Type: "None" in Arduino,
333+
decrease refreshRate in setup() to 90 or lower to get good an accurate GIF frame rate
334+
- Set the chip select pin for your board. On Teensy 3.5/3.6, the onboard microSD CS pin is "BUILTIN_SDCARD"
335+
*/

0 commit comments

Comments
 (0)