Hey everyone! I’ve got an issue that I can’t work out since it occurs randomly and without any interaction with the device.
I have a set of 8 Photon devices, each with an SD card reader (with SD card containing WAV files), button (with LED), strip of 20 neopixels, and speakers connected via PAM8610 amplifiers. Everything is running off a large 12V battery, providing 12V for the amplifiers, and running into a GB60-1205 DC/DC converter to provide 5V for the Photons. The Photons connect to a “pocket wifi” style mobile internet device.
Occasionally, one or more of the Photons will freeze and the LED will be stuck on cyan; an “offline” event appears in the console when this happens. This appears to be random and doesn’t require any interaction to have occurred first. I can’t think of what could be causing this, especially since it’s happening at random and not on all of the devices (so far at least).
Any ideas? (edit: I am isolating one of the culprits to see if it still crashes on a USB power supply)
Code is below. Apologies for the mess!
// This #include statement was automatically added by the Particle IDE.
#include <DeviceNameHelperRK.h>
// This #include statement was automatically added by the Particle IDE.
#include <FastLED.h>
#include <speaker.h>
#include <SdFat.h>
FASTLED_USING_NAMESPACE;
#define NUM_LEDS 20
#define LED_PIN 0
#define STRIP_PIN 6
#define BUT_PIN 1
#define UPDATES_PER_SECOND 60
String file_name [17]{ "1.wav","2.wav", "3.wav", "4.wav", "5.wav", "6.wav", "7.wav", "8.wav","9.wav",
"10.wav", "11.wav", "12.wav", "13.wav","14.wav","15.wav","16.wav","17.wav"};
String device_name = "none";
SerialLogHandler logHandler(LOG_LEVEL_NONE, { // Logging level for non-app messages
{ "app", LOG_LEVEL_INFO } // Logging level for application messages
});
// Define the array of leds
CRGB leds[NUM_LEDS];
typedef struct wav_header {
// RIFF Header
char riff_header[4]; // Contains "RIFF"
uint32_t wav_size; // Size of the wav portion of the file, which follows the first 8 bytes. File size - 8
char wave_header[4]; // Contains "WAVE"
// Format Header
char fmt_header[4]; // Contains "fmt " (includes trailing space)
uint32_t fmt_chunk_size; // Should be 16 for PCM
uint16_t audio_format; // Should be 1 for PCM. 3 for IEEE Float
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate; // Number of bytes per second. sample_rate * num_channels * Bytes Per Sample
uint16_t sample_alignment; // num_channels * Bytes Per Sample
uint16_t bit_depth; // Number of bits per sample
// Data
char data_header[4]; // Contains "data"
uint32_t data_bytes; // Number of bytes in data. Number of samples * num_channels * sample byte size
// uint8_t bytes[]; // Remainder of wave file is bytes
} WavHeader_t;
const int SD_SS = A2;
const size_t BUFFERSIZE = 1024;
uint16_t data[2][BUFFERSIZE];
SdFat sd;
File wavFile;
WavHeader_t wh;
int buttonCount = 0;
int buttonState;
int lastButtonState = LOW;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
static uint8_t hue;
Speaker speaker(data[0], data[1], BUFFERSIZE);
// handler id
int h = 0;
bool buttondown = false;
void setup() {
delay( 3000 );
Serial.begin(57600);
Serial.println("resetting");
FastLED.addLeds<NEOPIXEL, STRIP_PIN>(leds, NUM_LEDS); // GRB ordering is assumed
//FastLED.setMaxPowerInVoltsAndMilliamps(5,10000); // limit my draw to 10A at 5v of power draw
LEDS.setBrightness(100);
pinMode(BUT_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
Particle.function("playWav", selectFile);
Particle.function("lights", ledChange);
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CHSV(hue,255,255);
if (sd.begin(SD_SS)) Log.info("SD initialised");
else Log.warn("failed to open card");
Particle.subscribe("particle/device/name", subscriptionHandler);
}
void subscriptionHandler(const char *topic, const char *data){
device_name = data;
}
//_______________________________________________________________________________
void loop() {
if(device_name == "none"){
Particle.publish("particle/device/name");
}
int reading = digitalRead(BUT_PIN);
if (reading != lastButtonState) lastDebounceTime = millis();
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != buttonState) {
buttonState = reading;
if (buttonState == HIGH) {
buttonCount++;
//String newSTR = buttonCount + ".wav";
selectFile(file_name[buttonCount]); // REMOVE THIS TO DISABLE SOUND PLAYBACK ON BUTTON PUSH
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CHSV(hue++,255,255);
Particle.publish("buttonpushed", device_name);
buttondown = true;
}
}
}
lastButtonState = reading;
if (buttondown && reading == LOW){
buttondown = false;
Particle.publish("buttonreleased", device_name);
}
if (buttonCount > 16) buttonCount = 0;
FastLED.show();
FastLED.delay(1000 / UPDATES_PER_SECOND);
if (speaker.ready()) readChunk();
}
//_______________________________________________________________________________
int selectFile(const char* filename) {
int retVal = 0;
if (!strcmp("ls", filename) || !strcmp("dir", filename)) {
sd.ls("/", LS_R);
return 0;
}
if (wavFile.isOpen()) wavFile.close();
wavFile = sd.open(filename);
if (wavFile) {
memset((uint8_t*)data, 0x80, sizeof(data)); // reset buffer to bias value 0x8080 (quicker via memset() than writing 0x8000 in a loop)
if (sizeof(wh) == wavFile.read((uint8_t*)&wh, sizeof(wh))) {
Log.printf("%s\n\r\t%.4s\r\n\tsize: %lu\r\n\t%.4s\r\n\t%.4s\r\n\tchunk size (16?): %lu\r\n\taudio format (1?): %u\r\n\tchannels: %u"
, filename
, wh.riff_header
, wh.wav_size
, wh.wave_header
, wh.fmt_header
, wh.fmt_chunk_size
, wh.audio_format
, wh.num_channels
);
// two chunks since Log only supports limited length output
Log.printf("\r\n\tsample rate: %lu\r\n\tbyte rate: %lu\r\n\tsample alignment: %u\r\n\tbit depth: %u\r\n\t%.4s\r\n\tdata bytes: %u"
, wh.sample_rate
, wh.byte_rate
, wh.sample_alignment
, wh.bit_depth
, wh.data_header
, wh.data_bytes
);
retVal = wh.data_bytes;
readChunk();
speaker.begin(wh.sample_rate);
}
}
else {
Log.error("%s not found", filename);
}
return retVal;
}
int readChunk() {
int retVal = 0;
if (wavFile.isOpen()) {
uint16_t* wav = speaker.getBuffer();
uint8_t buf[BUFFERSIZE * 2 * wh.num_channels];
int n = wavFile.read(buf, sizeof(buf));
if (n < sizeof(buf)) {
wavFile.close(); // when all data is read close the file
}
memset((uint8_t*)wav, 0x80, BUFFERSIZE); // reset buffer to bias value 0x8080
for(int b = 0; b < n; b += wh.sample_alignment) {
wav[retVal++] = *(uint16_t*)&buf[b] + 32768; // convert int16_t to uin16_t with bias 0x8000
}
} else {
speaker.end();
}
return retVal;
}
//_______________________________________________________________________________
void fadeall() { for(int i = 0; i < NUM_LEDS; i++) { leds[i].nscale8(250); } }
//_______________________________________________________________________________
int ledChange(String preset){
if(preset == "blue"){
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CRGB(0,0,255);
} else if(preset =="red"){
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CRGB(255,0,0);
} else if(preset =="green"){
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CRGB(0,255,0);
} else if(preset =="yellow"){
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CRGB(255,255,0);
} else if(preset =="pink"){
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CRGB(255,0,255);
} else if(preset =="purple"){
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CRGB(127,0,255);
} else if(preset =="cyan"){
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CRGB(0,255,255);
} else if(preset =="off"){
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CRGB(0,0,0);
} else {
for(int i = 0; i < NUM_LEDS; i++) leds[i] = CHSV(hue++,255,255);
}
return 1;
}