Teensy 3.2 + Propshield lightsaberbuild

hello there!
im currently try to build my lightsaber but in the guide it says you need a sd card but the Teensy 3.2 has no slot for one and the prop shield too. so im a bloody beginner with this all and my question may be stupid but I found no answer to this. I tought the code is playing and storing the sounds in the teensy or the prop shield?

and I forgot one thing I use a WS2812B Led strip is this supported? and where do I need to make changes in case it is supported

You can store your sound font on the prop shield flash, but there isn’t a lot of space there, so it’s very limited. Also, nobody has done that in a while, so it’s possible that it won’t work “out of the box”, but if so I can help with fixing that.

It’s possible to use an external SD card reader, like I did:
https://fredrik.hubbe.net/lightsaber/electronics.html

It’s also possible to use a Teensy 3.5 which has an SD card reader on it.

And yes, WS2812B strips are absolutely supported.

1 Like

thanks for the fast reply!
ok good so I will try to get it work. I don’t need much sound files but how do I change them when not using a sd card? has the prop shield be connected while uploading the code to the teensy?
and im not finding a link to the style generator
ah ok good so I just have too change the led name to WS2812B?
and thanks for your help!

If you select MTP in Arduino->Tools->USB type, then ProffieOS will add an mtp server that lets you upload/download files to the flash memory on the prop shield. The prop shield doesn’t have to be connected to upload code, but obviously the code won’t work well if it expects a prop shield that isn’t there. You don’t need to change the LED name. (Where would you even do that?)

ah ok so I need to change that and re upload the code.
good so this was stupid :smiley: I thought I need to change this in WS2812B
BladeConfig blades[] = {
{ 0, WS2811BladePtr<144, WS2811_ACTUALLY_800kHz | WS2811_GRB>(), CONFIGARRAY(presets) },
};

1 Like

also where do I change the sounds that will be used like humming turn off and on swings etc.?

That’s the font that you would upload to the prop shield flash through MTP.

that would be this file I guess right?

#ifndef MTP_MTP_STORAGE_SD_H
#define MTP_MTP_STORAGE_SD_H

// Storage implementation for SD. SD needs to be already initialized.
class MTPStorage_SD : public MTPStorageInterface {
public:
explicit MTPStorage_SD(MTPD* mtpd) { mtpd->AddStorage(this); }
private:
File index_;

uint8_t mode_ = 0;
uint32_t open_file_ = 0xFFFFFFFEUL;
File f_;
uint32_t index_entries_ = 0;

struct Record {
uint32_t parent;
uint32_t child; // size stored here for files
uint32_t sibling;
uint8_t isdir;
uint8_t scanned;
char name[14];
};

bool readonly() override { return false; }
bool has_directories() override { return true; }
uint64_t size() override { return 1 << 30; }
uint64_t free() override { return 1 << 29; }
uint32_t free_objects() override { return 0xFFFFFFFEUL; }

const char* GetName() override {
return “SD Card”;
}

void OpenIndex() {
if (index_) return;
mtp_lock_storage(true);
index_ = SD.open(“mtpindex.dat”, FILE_WRITE);
mtp_lock_storage(false);
}

void WriteIndexRecord(uint32_t i, const Record& r) {
OpenIndex();
mtp_lock_storage(true);
index_.seek(sizeof(r) * i);
index_.write((char*)&r, sizeof(r));
mtp_lock_storage(false);
}

uint32_t AppendIndexRecord(const Record& r) {
uint32_t new_record = index_entries_++;
WriteIndexRecord(new_record, r);
return new_record;
}

// TODO(hubbe): Cache a few records for speed.
Record ReadIndexRecord(uint32_t i) {
Record ret;
if (i > index_entries_) {
memset(&ret, 0, sizeof(ret));
return ret;
}
OpenIndex();
mtp_lock_storage(true);
index_.seek(sizeof(ret) * i);
index_.read(&ret, sizeof(ret));
mtp_lock_storage(false);
return ret;
}

void ConstructFilename(int i, char* out) {
if (i == 0) {
strcpy(out, “/”);
} else {
Record tmp = ReadIndexRecord(i);
ConstructFilename(tmp.parent, out);
if (out[strlen(out)-1] != ‘/’)
strcat(out, “/”);
strcat(out, tmp.name);
}
}

void OpenFileByIndex(uint32_t i, uint8_t mode = O_RDONLY) {
if (open_file_ == i && mode_ == mode)
return;
char filename[256];
ConstructFilename(i, filename);
mtp_lock_storage(true);
f_.close();
f_ = SD.open(filename, mode);
open_file_ = i;
mode_ = mode;
mtp_lock_storage(false);
}

// MTP object handles should not change or be re-used during a session.
// This would be easy if we could just have a list of all files in memory.
// Since our RAM is limited, we’ll keep the index in a file instead.
bool index_generated = false;
void GenerateIndex() {
if (index_generated) return;
index_generated = true;

mtp_lock_storage(true);
SD.remove("mtpindex.dat");
mtp_lock_storage(false);
index_entries_ = 0;

Record r;
r.parent = 0;
r.sibling = 0;
r.child = 0;
r.isdir = true;
r.scanned = false;
strcpy(r.name, "/");
AppendIndexRecord(r);

}

void ScanDir(uint32_t i) {
Record record = ReadIndexRecord(i);
if (record.isdir && !record.scanned) {
OpenFileByIndex(i);
if (!f_) return;
int sibling = 0;
while (true) {
mtp_lock_storage(true);
File child = f_.openNextFile();
mtp_lock_storage(false);
if (!child) break;
Record r;
r.parent = i;
r.sibling = sibling;
r.isdir = child.isDirectory();
r.child = r.isdir ? 0 : child.size();
r.scanned = false;
strcpy(r.name, child.name());
sibling = AppendIndexRecord(r);
child.close();
}
record.scanned = true;
record.child = sibling;
WriteIndexRecord(i, record);
}
}

bool all_scanned_ = false;
void ScanAll() {
if (all_scanned_) return;
all_scanned_ = true;

GenerateIndex();
for (uint32_t i = 0; i < index_entries_; i++) {
  ScanDir(i);
}

}

uint32_t next_;
bool follow_sibling_;
void StartGetObjectHandles(uint32_t parent) override {
GenerateIndex();
if (parent) {
if (parent == 0xFFFFFFFF) parent = 0;
ScanDir(parent);
follow_sibling_ = true;
// Root folder?
next_ = ReadIndexRecord(parent).child;
} else {
ScanAll();
follow_sibling_ = false;
next_ = 1;
}
}

uint32_t GetNextObjectHandle() override {
while (true) {
if (next_ == 0)
return 0;
int ret = next_;
Record r = ReadIndexRecord(ret);
if (follow_sibling_) {
next_ = r.sibling;
} else {
next_++;
if (next_ >= index_entries_)
next_ = 0;
}
if (r.name[0]) return ret;
}
}

void GetObjectInfo(uint32_t handle,
char* name,
uint32_t* size,
uint32_t* parent) override {
Record r = ReadIndexRecord(handle);
strcpy(name, r.name);
*parent = r.parent;
*size = r.isdir ? 0xFFFFFFFFUL : r.child;
}

uint32_t GetSize(uint32_t handle) {
return ReadIndexRecord(handle).child;
}

void read(uint32_t handle,
uint32_t pos,
char* out,
uint32_t bytes) override {
OpenFileByIndex(handle);
mtp_lock_storage(true);
f_.seek(pos);
f_.read(out, bytes);
mtp_lock_storage(false);
}

bool DeleteObject(uint32_t object) override {
char filename[256];
Record r;
while (true) {
r = ReadIndexRecord(object == 0xFFFFFFFFUL ? 0 : object);
if (!r.isdir) break;
if (!r.child) break;
if (!DeleteObject(r.child))
return false;
}

// We can't actually delete the root folder,
// but if we deleted everything else, return true.
if (object == 0xFFFFFFFFUL) return true;

ConstructFilename(object, filename);
bool success;
mtp_lock_storage(true);
if (r.isdir) {
  success = SD.rmdir(filename);
} else {
  success = SD.remove(filename);
}
mtp_lock_storage(false);
if (!success) return false;
r.name[0] = 0;
int p = r.parent;
WriteIndexRecord(object, r);
Record tmp = ReadIndexRecord(p);
if (tmp.child == object) {
  tmp.child = r.sibling;
  WriteIndexRecord(p, tmp);
} else {
  int c = tmp.child;
  while (c) {
    tmp = ReadIndexRecord(c);
    if (tmp.sibling == object) {
      tmp.sibling = r.sibling;
      WriteIndexRecord(c, tmp);
      break;
    } else {
      c = tmp.sibling;
    }
  }
}
return true;

}

uint32_t Create(uint32_t parent,
bool folder,
const char* filename) override {
uint32_t ret;
if (parent == 0xFFFFFFFFUL) parent = 0;
Record p = ReadIndexRecord(parent);
Record r;
if (strlen(filename) > 12) return 0;
strcpy(r.name, filename);
r.parent = parent;
r.child = 0;
r.sibling = p.child;
r.isdir = folder;
// New folder is empty, scanned = true.
r.scanned = 1;
ret = p.child = AppendIndexRecord(r);
WriteIndexRecord(parent, p);
if (folder) {
char filename[256];
ConstructFilename(ret, filename);
mtp_lock_storage(true);
SD.mkdir(filename);
mtp_lock_storage(false);
} else {
OpenFileByIndex(ret, FILE_WRITE);
}
return ret;
}

void write(const char* data, uint32_t bytes) override {
mtp_lock_storage(true);
f_.write(data, bytes);
mtp_lock_storage(false);
}

void close() override {
mtp_lock_storage(true);
uint32_t size = f_.size();
f_.close();
mtp_lock_storage(false);
Record r = ReadIndexRecord(open_file_);
r.child = size;
WriteIndexRecord(open_file_, r);
open_file_ = 0xFFFFFFFEUL;
}
};

#endif

No, that file is for using MTP with SD cards.
This is the file that implement support for MTP on serialflash:

Note that the serialflash doesn’t actually support directories, so you probably can only have one font. Not that it matters, since you will probably not have room for more than one font anyways.

You don’t really need to know which file implements what though. All you should need to worry about is what defines to put (and not put) in your config file. For instance, you should NOT have this in your config file:

#define ENABLE_SD

Instead, you should have:

#define ENABLE_SERIALFLASH

If you have ENABLE_SERIALFLASH and select MTP in arduino->tools->usb type, the ProffieOS should automatically use the file I linked above to make the serialflash storage available through MTP. Note that if you’re on a mac, you might need some android tools installed to get MTP support.

2 Likes

ok good then I will change this to enable serialfalsh! and the Arduino I already changed to mtp storage so I got no notification that there was a problem.
so I need to select the fond I want and put it in the serial flash code?

thank you for the help Im a total beginner in this code stuff

You’ll need to upload the font files to the serialflash. Since there are no directories, all the files will just be in the top level directory. In the config file, you’ll make a preset that uses “” for the font path.
If everything work, proffieos will find the files and play them from there.

ok could you show me how it should look or can look? does it know wich sound to play?

Yes, it knows which sounds to play.
This page explains most about how the file structure works for fonts:

Sound Font Configuration · profezzorn/ProffieOS Wiki · GitHub

On a serialflash, there wouldn’t be a directory though, just files.
Most fonts you can find/buy/download already have the files named correctly, so you would just upload them to the serialflash and call it a day. (Assuming they fit, which they probably won’t…)

ok i will try it but can’t I just use the sound files from the teensy saber site?

Yes you can, but you will need to pick one font, and even then you probably will need to remove some of the files to make it fit.

good I will do that!
but I can use only the sounds from one or can I copy one from another and mix it?

You can mix sounds from different fonts.
I would recommend making it work before you start doing that.

1 Like

good you helped me out a lot thanks again. I will try all this and if im running in a problem I will post here again!