RSeries astromech firmware
HoloDisplay.h
Go to the documentation of this file.
1 #ifndef HoloDisplay_h
2 #define HoloDisplay_h
3 
4 #include "ReelTwo.h"
5 #include "HoloLights.h"
6 #include <Adafruit_GFX.h>
7 #include <Adafruit_SSD1331.h>
8 #include <SD.h>
9 #include <SPI.h>
10 
25 #if USE_HOLO_TEMPLATE
26 template<uint8_t DATA_PIN = 45, uint32_t RGB_ORDER = GRB, uint16_t NUM_LEDS = 12>
27 class HoloDisplay :
28  public HoloLights<DATA_PIN, RGB_ORDER, NUM_LEDS>
29 #else
30 class HoloDisplay :
31  public HoloLights
32 #endif
33 {
34 public:
35 #if USE_HOLO_TEMPLATE
36 
39  HoloDisplay(const int id = 0) :
40  HoloLights<DATA_PIN, RGB_ORDER, NUM_LEDS>(id),
41 #else
42  HoloDisplay(PixelType type = kRGBW, const int id = 0, const byte pin = 45, const byte numPixels = 12) :
43  HoloLights(pin, type, id, numPixels),
44 #endif
45  fDisplay(cs, dc, rst)
46  {
47  }
48 
52  virtual void setup() override
53  {
55 
56  /* Set SD chip select to OUTPUT */
57  pinMode(SD_CS, OUTPUT);
58  digitalWrite(SD_CS, HIGH);
59 
60  fDisplay.begin();
61  #ifdef HOLO_DEBUG
62  testPattern();
63  DEBUG_PRINTLN(F("Initializing SD card..."));
64  #else
65  clearDisplay();
66  #endif
67 
68  if (!SD.begin(SD_CS))
69  {
70  #ifdef HOLO_DEBUG
71  DEBUG_PRINTLN(F("failed!"));
72  #endif
73  }
74  }
75 
79  void setDownloadStream(Stream* stream)
80  {
81  fDownloadStream = stream;
82  }
83 
87  virtual void selectSequence(int sequence, int durationSec) override
88  {
89  switch (sequence)
90  {
91  case 1:
92  play("LEIA.BD2");
93  break;
94  case 2:
95  play("R2.BD2");
96  break;
97  case 3:
98  play("PLANS.BD2");
99  break;
100  default:
101  HoloLights::selectSequence(sequence, durationSec);
102  break;
103  }
104  }
105 
109  virtual void handleCommand(const char* cmd) override
110  {
111  if (cmd[0] != 'H' || cmd[1] != 'O')
112  {
114  return;
115  }
116  cmd += 2;
117 
118  char filename[12];
119  char* fp = filename;
120  int commandLength = strlen(cmd);
121  const char* ch = (commandLength >= 3) ? &cmd[2] : "";
122  while (*ch != '\0')
123  {
124  unsigned char c = (unsigned char)*ch++;
125  *fp++ = (char)((c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c);
126  }
127  *fp = '\0';
128  if (filename != fp)
129  {
130  *fp++ = '.'; *fp++ = 'B'; *fp++ = 'D'; *fp++ = '2'; *fp = '\0';
131  #ifdef OLED_DOWNLOAD
132  if (cmd[1] == 'D' && fDownloadStream != NULL)
133  {
134  uint32_t len = 0;
135  fDownload = true;
136  fDownloadStream->println("ACK");
137  if (fDownloadStream->find("LEN"))
138  {
139  len = fDownloadStream->readStringUntil('\n').toInt();
140  }
141  uint32_t bytesReceived = uploadFile(filename, len);
142  fDownload = false;
143  if (bytesReceived > 0)
144  {
145  #ifdef HOLO_DEBUG
146  DEBUG_PRINT(F("Received: "));
147  DEBUG_PRINT(filename);
148  DEBUG_PRINT(F(" "));
149  DEBUG_PRINT(bytesReceived);
150  DEBUG_PRINTLN(F(" bytes"));
151  #endif
152  }
153  } else
154  #endif
155  if (cmd[1] == 'X')
156  {
157  SD.remove(filename);
158  }
159  else if (cmd[1] == 'P')
160  {
161  play(filename);
162  }
163  }
164  }
165 
166 #ifdef OLED_DOWNLOAD
167 #endif
168 
169 #ifdef HOLO_DEBUG
170  void testPattern(void)
171  {
172  unsigned y;
173 
174  for(y = 0; y < 64; y++)
175  {
176  fDisplay.setAddrWindow(0, y, 96, 1);
177  uint16_t color = BLACK;
178  if(y > 55) color = WHITE;
179  else if(y > 47) color = BLUE;
180  else if(y > 39) color = GREEN;
181  else if(y > 31) color = CYAN;
182  else if(y > 23) color = RED;
183  else if(y > 15) color = MAGENTA;
184  else if(y > 7) color = YELLOW;
185  fDisplay.writeColor(color, 96);
186  fDisplay.endWrite();
187  }
188  }
189 #endif
190 
195  {
196  fDisplay.fillScreen(BLACK);
197  }
198 
202  void stop()
203  {
204  f.close();
205  fFrameCount = 0;
206  fFrameCountInitial = 0;
207  fTimestamp = 0;
208  fTimestart = 0;
209  clearDisplay();
210 // restoreFrontHoloBrigthness();
211  }
212 
216  void play(const char* filename)
217  {
218  stop();
219  #ifdef HOLO_DEBUG
220  DEBUG_PRINT(F("OLED play: "));
221  DEBUG_PRINTLN(filename);
222  #endif
223  f = SD.open(filename);
224  if (!f.available())
225  {
226  #ifdef HOLO_DEBUG
227  DEBUG_PRINT(F("File not found: "));
228  DEBUG_PRINTLN(filename);
229  #endif
230  return;
231  }
232 
233  #ifdef HOLO_DEBUG
234  DEBUG_PRINT(F("Playing movie: "));
235  DEBUG_PRINTLN(filename);
236  #endif
237  // Set front holo to half-bright during movie play
238  //dimFrontHoloBrigthness();
239  fFrameCount = read32();
240  fFrameCountInitial = fFrameCount;
241  fFramesPerSec = read32();
242  fTimestamp = 0;
243  fTimestart = millis();
244  }
245 
249  virtual void animate()
250  {
252  if (fFrameCount > 0 && millis() - fTimestamp >= 100)
253  {
254  fTimestamp = millis();
255  drawFrame();
256  fFrameCount--;
257  if (!fFrameCount)
258  {
259  #ifdef HOLO_DEBUG
260  DEBUG_PRINT(F("Time elapsed: "));
261  DEBUG_PRINT(fTimestamp - fTimestart);
262  DEBUG_PRINT(F("ms "));
263  DEBUG_PRINT((float)fFrameCountInitial / ((fTimestamp - fTimestart) / 1000.0));
264  DEBUG_PRINTLN(F(" fps"));
265  #endif
266  stop();
267  }
268  }
269  }
270 
271 #ifdef OLED_DOWNLOAD
272 
275  long uploadFile(const char* filename, uint32_t fileLength)
276  {
277  uint32_t receivedFileSize = 0;
278  const uint32_t bufferSize = 4098;
279  unsigned char* buffer = (unsigned char*)malloc(bufferSize);
280  if (buffer == NULL || fDownloadStream == NULL)
281  return -1;
282  if (SD.exists(filename))
283  SD.remove(filename);
284  File file = SD.open(filename, FILE_WRITE);
285  while (receivedFileSize < fileLength)
286  {
287  uint32_t sum = 0;
288  if (!fDownloadStream->find("ACK"))
289  {
290  #ifdef HOLO_DEBUG
291  DEBUG_PRINT(F("NAK received="));
292  DEBUG_PRINTLN(receivedFileSize);
293  #endif
294  return -1;
295  }
296  sum = fDownloadStream->readStringUntil('\n').toInt();
297  unsigned char* bufin = buffer;
298  unsigned char* bufout = buffer;
299  int nprbytes = fDownloadStream->readBytesUntil('\n', buffer, bufferSize);
300  uint32_t sum2 = 0;
301  for (int i = 0; i < nprbytes; i++)
302  {
303  sum2 += buffer[i];
304  }
305  if (sum == sum2)
306  {
307  static char sPr2Six[256] PROGMEM =
308  {
309  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
310  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
311  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
312  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
313  64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
314  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
315  64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
316  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
317  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
318  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
319  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
320  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
321  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
322  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
323  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
324  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
325  };
326 
327  receivedFileSize += nprbytes;
328  showDownloadProgress(receivedFileSize, fileLength);
329  while (nprbytes > 4)
330  {
331  *bufout++ = (unsigned char)((unsigned int) (pgm_read_byte(&sPr2Six[bufin[0]]) << 2) | (unsigned int)pgm_read_byte(&sPr2Six[bufin[1]]) >> 4);
332  *bufout++ = (unsigned char)((unsigned int) (pgm_read_byte(&sPr2Six[bufin[1]]) << 4) | (unsigned int)pgm_read_byte(&sPr2Six[bufin[2]]) >> 2);
333  *bufout++ = (unsigned char)((unsigned int) (pgm_read_byte(&sPr2Six[bufin[2]]) << 6) | (unsigned int)pgm_read_byte(&sPr2Six[bufin[3]])]);
334  bufin += 4;
335  nprbytes -= 4;
336  }
337  if (nprbytes > 1)
338  {
339  *bufout++ = (unsigned char) (pgm_read_byte(&sPr2Six[bufin[0]]) << 2 | pgm_read_byte(&sPr2Six[bufin[1]]) >> 4);
340  }
341  if (nprbytes > 2)
342  {
343  *bufout++ = (unsigned char) (pgm_read_byte(&sPr2Six[bufin[1]]) << 4 | pgm_read_byte(&sPr2Six[bufin[2]]) >> 2);
344  }
345  if (nprbytes > 3)
346  {
347  *bufout++ = (unsigned char) (pgm_read_byte(&sPr2Six[bufin[2]]) << 6 | pgm_read_byte(&sPr2Six[bufin[3]]));
348  }
349  file.write(buffer, bufout - buffer);
350  #ifdef HOLO_DEBUG
351  fDownloadStream->println("ACK");
352  #endif
353  }
354  else
355  {
356  #ifdef HOLO_DEBUG
357  DEBUG_PRINT(F("NAK: badchecksum: "));
358  DEBUG_PRINT(sum2);
359  DEBUG_PRINT(F("expected: "));
360  DEBUG_PRINTLN(sum);
361  #endif
362  }
363  }
364  showDownloadProgress(0, 0);
365  file.flush();
366  file.close();
367  free((char*)buffer);
368  if (receivedFileSize == 0 || receivedFileSize != fileLength)
369  {
370  SD.remove(filename);
371  return -1;
372  }
373  return receivedFileSize;
374  }
375 #endif
376 private:
377  void drawFrame()
378  {
379  uint16_t sdbuffer[96];
380  int frameType = (char)f.read();
381  if (frameType == -1)
382  {
383  /* Keyframe */
384  for (int y = 0; y < 64; y++)
385  {
386  int num = f.read((uint8_t*)sdbuffer, sizeof(sdbuffer));
387  if (num != sizeof(sdbuffer))
388  {
389  #ifdef HOLO_DEBUG
390  DEBUG_PRINTLN(F("Error reading key frame"));
391  DEBUG_PRINT(F("Incomplete scanline: "));
392  DEBUG_PRINTLN(y);
393  #endif
394  stop();
395  break;
396  }
397  fDisplay.setAddrWindow(0, y, 96, 1);
398  fDisplay.writePixels(sdbuffer, 96);
399  fDisplay.endWrite();
400  }
401  }
402  else if (frameType == 1)
403  {
404  /* Incremental */
405  int x = f.read();
406  int y = f.read();
407  int w = f.read();
408  int h = f.read();
409  while (h-- > 0)
410  {
411  int num2 = f.read((uint8_t*)sdbuffer, w*sizeof(uint16_t));
412  if (num2 != int(w*sizeof(uint16_t)))
413  {
414  #ifdef HOLO_DEBUG
415  DEBUG_PRINTLN(F("Error reading frame"));
416  DEBUG_PRINT(F("Incomplete scanline: "));
417  DEBUG_PRINTLN(y);
418  #endif
419  stop();
420  break;
421  }
422  fDisplay.setAddrWindow(x,y,w,1);
423  fDisplay.writePixels(sdbuffer, w);
424  fDisplay.endWrite();
425  y++;
426  }
427  }
428  }
429 
430 #ifdef OLED_DOWNLOAD
431  void showDownloadProgress(uint32_t receivedFileSize, uint32_t fileLength)
432  {
433  int hp = 0;
434  uint32_t siz = 0;
435  int val = (fileLength > 0) ? (int)(255 * numLEDs * (double)receivedFileSize / (double)fileLength) : 0;
436  for (unsigned i = 0; i < numLEDs; i++)
437  {
438  if (val >= 255)
439  {
440  setPixelColor(i, 0, 0, 255);
441  val -= 255;
442  }
443  else if (val > 0)
444  {
445  setPixelColor(i, 0, 0, val);
446  val = 0;
447  }
448  else
449  {
450  setPixelColor(i, kOff);
451  }
452  }
453  show();
454  }
455 #endif
456 
457  uint32_t read32()
458  {
459  uint32_t result;
460  ((uint8_t *)&result)[0] = f.read(); // LSB
461  ((uint8_t *)&result)[1] = f.read();
462  ((uint8_t *)&result)[2] = f.read();
463  ((uint8_t *)&result)[3] = f.read(); // MSB
464  return result;
465  }
466 
467 
468  Adafruit_SSD1331 fDisplay;
469 
470  File f;
471  boolean fDownload = false;
472  uint32_t fFrameCount;
473  uint32_t fTimestamp;
474  uint32_t fTimestart;
475  uint32_t fFrameCountInitial;
476  uint32_t fFramesPerSec;
477  Stream* fDownloadStream = NULL;
478 
479  enum
480  {
481  BLACK = 0x0000,
482  BLUE = 0x001F,
483  RED = 0xF800,
484  GREEN = 0x07E0,
485  CYAN = 0x07FF,
486  MAGENTA = 0xF81F,
487  YELLOW = 0xFFE0,
488  WHITE = 0xFFFF
489  };
490 
491 #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
492 
493  // GND (G) - Gnd (Black Wire)
494  // VCC (+) - 5v (Red Wire)
495  // SDCS (SC) - Digital #53 (Gray Wire)
496  // OCS (OC) - Digital #47 (Orange Wire)
497  // RST (R) - Digital #49 (Green Wire)
498  // D/C (DC) - Digital #48 (Brown Wire)
499  // SCK (CK) - Digital #52 (White Wire)
500  // MOSI (SI) - Digital #51 (Blue Wire)
501  // MISO (SO) - Digital #50 (Purple Wire)
502  // CD (CD) - skip
503 
504  enum {
505  miso = 50,
506  mosi = 51,
507  sclk = 52,
508  cs = 47,
509  dc = 48,
510  rst = 49,
511  SD_CS = 53
512  };
513 #elif defined(__AVR__)
514  /* It is possible to run the OLED and everything else on the Mini Pro but you need to strip all the libraries */
515 
516  // GND (G) - Gnd (Black Wire)
517  // VCC (+) - 5v (Red Wire)
518  // SDCS (SC) - Digital #4.(Gray Wire)
519  // OCS (OC) - Digital #10 (Orange Wire)
520  // RST (R) - Digital #9 (Green Wire)
521  // D/C (DC) - Digital #8 (Brown Wire)
522  // SCK (CK) - Digital #13 (White Wire)
523  // MOSI (SI) - Digital #11 (Blue Wire)
524  // MISO (SO) - Digital #12 (Purple Wire)
525  // CD (CD) - skip
526 
527  enum {
528  sclk = 13,
529  mosi = 11,
530  cs = 10,
531  rst = 9,
532  dc = 8,
533  SD_CS = 4
534  };
535 #elif defined(ESP32)
536  enum {
537  miso = 19,
538  mosi = 23,
539  sclk = 18,
540  cs = 5,
541  dc = 16,
542  rst = 17,
543  SD_CS = 4
544  };
545 #else
546  #error Holo OLED - Target MCU not supported
547 #endif
548 };
549 
550 #endif
551 
HoloDisplay::animate
virtual void animate()
Runs through one frame of animation for this holoprojector display instance.
Definition: HoloDisplay.h:249
DEBUG_PRINT
#define DEBUG_PRINT(s)
Definition: ReelTwo.h:189
HoloDisplay::setup
virtual void setup() override
Initalizes the OLED display and SD card.
Definition: HoloDisplay.h:52
ReelTwo.h
HoloDisplay::handleCommand
virtual void handleCommand(const char *cmd) override
See HoloLights::handleCommand()
Definition: HoloDisplay.h:109
DEBUG_PRINTLN
#define DEBUG_PRINTLN(s)
Definition: ReelTwo.h:188
HoloLights
Controls the movement and display functions of a single Holoprojector.
Definition: HoloLights.h:57
HoloLights.h
HoloDisplay
Holo Projector with Adafruit SSD1131 OLED display.
Definition: HoloDisplay.h:27
HoloDisplay::clearDisplay
void clearDisplay()
Clear OLED screen to black.
Definition: HoloDisplay.h:194
HoloLights< 45, GRB, 12 >::kOff
@ kOff
Off.
Definition: HoloLights.h:111
HoloLights< 45, GRB, 12 >::setPixelColor
void setPixelColor(uint16_t n, uint32_t c)
Definition: HoloLights.h:202
HoloDisplay::stop
void stop()
Stop playing the current movie.
Definition: HoloDisplay.h:202
HoloDisplay::HoloDisplay
HoloDisplay(const int id=0)
Constructor.
Definition: HoloDisplay.h:39
HoloDisplay::play
void play(const char *filename)
Play the specified movie file on the OLD display.
Definition: HoloDisplay.h:216
HoloLights< 45, GRB, 12 >::numPixels
uint16_t numPixels()
Definition: HoloLights.h:197
HoloLights::setup
virtual void setup() override
Configures the NeoPixel ring and centers the holoprojector if servos have been assigned.
Definition: HoloLights.h:653
HoloLights::selectSequence
virtual void selectSequence(int sequence, int durationSec)
Specify the sequence to animate.
Definition: HoloLights.h:583
HoloDisplay::selectSequence
virtual void selectSequence(int sequence, int durationSec) override
Specify the sequence to animate.
Definition: HoloDisplay.h:87
HoloLights< 45, GRB, 12 >::show
void show()
Definition: HoloLights.h:182
HoloDisplay::setDownloadStream
void setDownloadStream(Stream *stream)
Enables support for downloading additional media onto the SD card through the specified Serial instan...
Definition: HoloDisplay.h:79
HoloLights::handleCommand
virtual void handleCommand(const char *cmd) override
Command Prefix: HP.
Definition: HoloLights.h:304
HoloLights::animate
virtual void animate() override
Runs through one frame of animation for this holoprojector instance.
Definition: HoloLights.h:417