Oopsy & Daisy Seed : hardware mapping

Hi everyone!
Received my Daisy Seeds a couple of days ago and am blown away by how great they are and how good the software integration is with Oopsy. Fantastic work.

I’m not yet very clear on how to setup custom JSON files inside Oopsy to map hardware elements for a barebones Seed : I’ve been able to compile and run things using the “patch” target, but the knob mappings get weird past the first two ADCs :slight_smile:

Is there any guidance or tutorial material on how to create a custom mapping target with Oopsy / its NodeJS scripts?

Thanks!

1 Like

Hi,

Custom JSON files are still a work-in-progress (Oopsy is still in beta…) so there’s no documentation on that yet. There’s probably a few assumptions in the genlib_daisy.h file and oopsy.js to work around yet. We’d need to add a “custom” target option in the Max interface, ensure genlib_daisy.h can work with the daisy_seed.h directly, and fill in the gaps of what a custom JSON can contain. Part of my challenge is not knowing what kinds of code would be needed to be added for a custom seed platform; it’s a less familiar area for me. But I’d be happy to work through ideas if you can help. I added an issue for this on the github (issue 34) – would link it here directly but the forum settings won’t let me

Graham

Hi Graham, thanks for the info and for opening the issue.
I’m haven’t really dived into the sources yet, but I’d be happy to help wherever I can.
Feel free to get in touch!
James

Looking over the scripty files involved that “compensate” for the different hardware implementations it doesn’t look too difficult to recreate one based on another.
I, for one, will be trying to kludge MIDI IN onto the Petal and a Terrarium pedal PCB.

For the folks asking about custom Seed-based projects (i.e. not Patch/Field/Pod/Petal/Versio etc.), initial support for this is now in the dev branch of the github repo. Progress is being tracked in the issue tracker (https://github.com/electro-smith/oopsy/issues/34) and wiki (https://github.com/electro-smith/oopsy/wiki/Custom-Seed-Targets).
Fair warnings: I don’t have a custom seed project to really test this on so it would have to be a community effort to work out if anything is missing and what would need to be documented. Moreover: this capacity is extremely alpha, as the JSON configuration format itself is likely to change to accommodate needs as they are found.

5 Likes

Wow, thanks! I just had a quick look into it. I’m starting to understand the mechanics of the code generation and where the JSON elements go (your wiki helps a lot), but I’m still not quite there.

If I understand correctly, I should be able to define some hardware elements (ADC configuration, etc) using the JSON “inserts” and define knob and other behaviour relying only on “daisy_seed.h”, similarly to what is done for the Seed DaisyExamples (such as Osc: https://github.com/electro-smith/DaisyExamples/blob/master/seed/Osc/Osc.cpp), without having to create a custom C equivalent to, for instance, daisy_petal.h/.cpp ?

This is what I tried to do (code below - simply setting up 3 knobs on the first 3 ADC pins), but something is wrong: the code compiles OK and flashes onto the Daisy but the knobs have no effect. Am I missing something really obvious here?

{
“defines”: {
“OOPSY_TARGET_SEED”: 1
},
“labels”: {
“params”: {
“knob1”: “kn1”,
“knob2”: “kn2”,
“knob3”: “kn3”
},
“outs”: {},
“datas”: {}
},
“inputs”: {
“kn1”: {
“automap”: true,
“code”: “hardware.seed.adc.GetFloat(0);”
},
“kn2”: {
“automap”: true,
“code”: “hardware.seed.adc.GetFloat(1);”
},
“kn3”: {
“automap”: true,
“code”: “hardware.seed.adc.GetFloat(2);”
}

},
“outputs”: {},
“datahandlers”: {},
“inserts”: [
{ “where”: “init”, “code”: “AdcChannelConfig cfg[3];” },
{ “where”: “init”, “code”: “cfg[0].InitSingle(oopsy::daisy.hardware.seed.GetPin(21));” },
{ “where”: “init”, “code”: “cfg[1].InitSingle(oopsy::daisy.hardware.seed.GetPin(22));” },
{ “where”: “init”, “code”: “cfg[2].InitSingle(oopsy::daisy.hardware.seed.GetPin(23));” },
{ “where”: “init”, “code”: “oopsy::daisy.hardware.seed.adc.Init(cfg, 3);” },
{ “where”: “header”, “code”: “#include “daisy_seed.h””},
{ “where”: “header”, “code”: “using namespace daisy;”}
],
“max_apps”: 1
}

Yes that’s the general idea.

I tried your JSON here, and (other than weird formatting issues from the forum post) it generates good looking code AFAICT. I also compared it to the basic DaisySeed Knob example (https://github.com/electro-smith/DaisyExamples/blob/master/seed/Knob/Knob.cpp) and it looks quite comparable. I’m not sure why this wouldn’t be working, it all looks good from here.

Here’s the JSON formatted for anyone following along:

{
	"defines": {
	  "OOPSY_TARGET_SEED": 1
	},
	"labels": {
	  "params": {
		"knob1": "kn1",
		"knob2": "kn2",
		"knob3": "kn3"
	  },
	  "outs": {},
	  "datas": {}
	},
	"inputs": {
	  "kn1": {
		"automap": true,
		"code": "hardware.seed.adc.GetFloat(0);"
	  },
	  "kn2": {
		"automap": true,
		"code": "hardware.seed.adc.GetFloat(1);"
	  },
	  "kn3": {
		"automap": true,
		"code": "hardware.seed.adc.GetFloat(2);"
	  }
	},
	"outputs": {},
	"datahandlers": {},
	"inserts": [
	  {
		"where": "init",
		"code": "AdcChannelConfig cfg[3];"
	  },
	  {
		"where": "init",
		"code": "cfg[0].InitSingle(oopsy::daisy.hardware.seed.GetPin(21));"
	  },
	  {
		"where": "init",
		"code": "cfg[1].InitSingle(oopsy::daisy.hardware.seed.GetPin(22));"
	  },
	  {
		"where": "init",
		"code": "cfg[2].InitSingle(oopsy::daisy.hardware.seed.GetPin(23));"
	  },
	  {
		"where": "init",
		"code": "oopsy::daisy.hardware.seed.adc.Init(cfg, 3);"
	  },
	  {
		"where": "header",
		"code": "#include \"daisy_seed.h\""
	  },
	  {
		"where": "header",
		"code": "using namespace daisy;"
	  }
	],
	"max_apps": 1
}

The generated code looks like this:


#define OOPSY_TARGET_SEED (1)
#define OOPSY_IO_COUNT (2)
#include "daisy_seed.h"
using namespace daisy;
#include "../genlib_daisy.h"
#include "../genlib_daisy.cpp"
#include "../../graham/custom_2dtest.cpp"

struct App_custom_2dtest : public oopsy::App<App_custom_2dtest> {
	float gen_param_knob1;	
	void init(oopsy::GenDaisy& daisy) {
		daisy.gen = custom_2dtest::create(daisy.hardware.seed.AudioSampleRate(), daisy.hardware.seed.AudioBlockSize());
		custom_2dtest::State& gen = *(custom_2dtest::State *)daisy.gen;
		daisy.param_count = 1;
		gen_param_knob1 = 0.f;
	}

	void mainloopCallback(oopsy::GenDaisy& daisy, uint32_t t, uint32_t dt) {
		Daisy& hardware = daisy.hardware;
		custom_2dtest::State& gen = *(custom_2dtest::State *)daisy.gen;
	}

	void displayCallback(oopsy::GenDaisy& daisy, uint32_t t, uint32_t dt) {
		Daisy& hardware = daisy.hardware;
		custom_2dtest::State& gen = *(custom_2dtest::State *)daisy.gen;
	}

	void audioCallback(oopsy::GenDaisy& daisy, float **hardware_ins, float **hardware_outs, size_t size) {
		Daisy& hardware = daisy.hardware;
		custom_2dtest::State& gen = *(custom_2dtest::State *)daisy.gen;
		float kn1 = hardware.seed.adc.GetFloat(0);
		gen_param_knob1 = (float)(kn1*1.f + 0.f);
		gen.set_knob1(gen_param_knob1);
		float * dsy_in1 = hardware_ins[0];
		float * dsy_in2 = hardware_ins[1];
		float * dsy_out1 = hardware_outs[0];
		float * dsy_out2 = hardware_outs[1];
		// in1:
		float * inputs[] = { dsy_in1 }; 
		// out1:
		float * outputs[] = { dsy_out1 };
		gen.perform(inputs, outputs, size);
		memcpy(dsy_out2, dsy_out1, sizeof(float)*size);
	}	
};

// store apps in a union to re-use memory, since only one app is active at once:
union {
	App_custom_2dtest app_custom_2dtest;
} apps;

oopsy::AppDef appdefs[] = {
	{"custom_2dtest", []()->void { oopsy::daisy.reset(apps.app_custom_2dtest); } },
};

int main(void) {
	oopsy::daisy.hardware.Init(); 
	oopsy::daisy.hardware.seed.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_48KHZ);
	AdcChannelConfig cfg[3];
	cfg[0].InitSingle(oopsy::daisy.hardware.seed.GetPin(21));
	cfg[1].InitSingle(oopsy::daisy.hardware.seed.GetPin(22));
	cfg[2].InitSingle(oopsy::daisy.hardware.seed.GetPin(23));
	oopsy::daisy.hardware.seed.adc.Init(cfg, 3);
	// insert custom hardware initialization here
	return oopsy::daisy.run(appdefs, 1);
}
2 Likes

Just to rule out hardware, it’s worth mentioning that one common issue when doing things with the Seed outside of a breakout board is to forget to connect the AGND pin to the DGND pin. This would result in the ADC pins not reading correctly (or at all).

Hi, thanks for your answers and sorry for the late reply (the notifications got filtered by my inbox).
My hardware configuration seemed OK, as I can run my code using the “patch” target and the first two ADC pots respond correctly, so I went back to try the “Osc” seed example again and found out my (stupid) mistake: my JSON was using the pin numbers corresponding to the actual hardware pins and not the “libDaisy” pins ! So the first ADCs would be 15 and 16 and not 22 and 23 (I actually got that wrong by 1 in my first JSON map too).
All is good now, some pot behaviour seems reversed compared to the “patch” target but I can easily switch that around on the breadboard. Only issue now is getting a clean decoupled 5v to the analog parts of my circuit, I’m waiting on an isolated DC/DC converter to try and reduce digital noise (saw this in another post in the forum, and on the “petal” schematics).
Thanks for your help, I’ll be more than happy to beta test more aspects of the custom seed Oopsy target as it progresses !

I’m getting to work on a Terrarium JSON this week.
Apparently PedalPCB has plans for another two or three Daisy boards “coming soon”
SO MANY THINGS!!!

/edit: found the Terrarium pin out out Github

1 Like