Oopsy & Daisy Seed : hardware mapping

hey! i’ve been puzzling after this for a while now, and while the daisy examples are well-documented enough for someone who has experience with C++, they’re not entirely legible to me. maybe i’m missing something major, but i can’t find anywhere in any of the github repositories how to use the code.

can you tell me where to find a reference to understand your JSON file? specifically what the

AdcChannelConfig cfg[3]

bit does? i’m digging through the Doxygen PDF for libdaisy and it’s not helping at all.

sorry to be asking simple questions; this is my first foray into any programming beyond Max/MSP/gen~ and it’s a bit of a learning curve.

@falejczyk We’re definitely still working on improving the documentation for everything in the Daisy Ecosystem, and the library code, etc. will be improving as we continue developing everything.

The existing page for the AdcChannelConfig is lacking a bit, but it does at least go over the more-advanced multiplexing support.
https://electro-smith.github.io/libDaisy/structdaisy_1_1_adc_channel_config.html

In the oopsy custom hardware lines of code can be inserted into the program. For example, in the example linked above:

"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);"
    },
}

The AdcChannelConfig cfg[3]; creates a group of 3 ADC Input Channels

The following three cfg[x].InitSingle(oopsy::daisy.hardware.seed.GetPin(y)); set the given input channel to the hardware pin on the Daisy Seed. For example, cfg[0] is connected to GPIO21 on the Daisy Seed (physical pin 28).

Finally, the oopsy::daisy.hardware.seed.adc.Init(cfg, 3); is telling the DaisySeed’s ADC to initialize that group of three pins.

This can be edited/customized to fit the needs of your own hardware.

We have plans of adding a wiki page that goes of the libdaisy side of working with custom seed-based hardware, and what it would look like to use that with native C++, as well as oopsy and the other integrations. An initial version of that should be up on the wiki in a few weeks.

Hope that helps to address your question :slight_smile:

I should add – that JSON config format for Oopsy is likely to change over the coming months, but only to make it a little easier for custom target configurations.

1 Like

thanks so much, this clears up everything i was wondering about!

1 Like

Hello, I’m new to Max but I’ve flashed examples to my Pod and like many of you would like to do the same with my seed on a breadboard with different knobs, etc. I have a json file ready to go.

Per the wiki page…The path to this JSON file can be configured in the [oopsy] patcher using either the “browse” button, by sending the "target " message to it, or including it in the [oopsy] patcher arguments.

Do I start with a blank patch and add an Oopsy object to it OR start with one of the existing templates?
How do I add the json file to my max patch?

Can someone reply with more detailed steps please?

Thanks!

  • Create a new Max patcher and add a gen~ object for your algorithm.
  • Save the patcher somewhere.
  • Put the JSON file for your custom target in the same folder as your max patcher. (Not strictly necessary, but it makes some things a bit easier). Let’s say your file is called “seedy.json”.
  • Add a new object to the Max patcher with the text “bpatcher oopsy @args seedy.json” (or whatever your JSON is called)
  • The bpatcher interface should now have your custom JSON listed and selected in the target drop-down.

Now hit the button (or save the patcher) and it should be working.

If you turn off the “quiet” mode, the Max console should show something like:

oopsy-verbose: script start /path/to/your/seedy.json 48kHz block48 /path/to/your/exported.cpp

Thanks!!!

I was able to flash my seed with the code in the gen~ object, but using the json file from the wiki page as reference, the bpatcher interface does not have my custom JSON listed, it shows the 4 std choices (patch, field, petal, pod) and the max console outputs:
oopsy-verbose:
script start patch 48/Users/jdoe/Downloads/MaxSeedNg/seed_20noise_20gate_20patch.cpp

Thanks again for the assistance!

Ah – the screenshot helps!

It looks like you are using the “main” branch of oopsy. The support for custom seed boards isn’t part of that branch yet, but you can get it by working from the “dev” branch.

To grab that, clone the oopsy repo into your Documents/Max 8/Packages (delete the old oopsy folder first) and then use git command line or a git app to switch to the “dev” branch.

More info here: How do I get the dev branch working? (never mind, got it)

1 Like

Just did the change to the dev branch and it works, thanks again for the help, I really appreciate it!

Hi!

I am sure its a dumb mistake on my part but I am having difficulty getting custom seed hardware mapping working. I am able to successfully manipulate and upload .json files that interface correctly with my Gen patches using daisy.field.json, daisy.petal.json…etc etc. (which is amazing and I am incredibly grateful for all the work that has gone into making this possible!) But now I want to make a custom .json file with a target SEED and daisy_seed.h file for my own custom built device. I try to compile and build using the most basic .json and a Gen patch that is empty but it wont compile. I would live some help if anyone can spot something I am missing.
I have included a screen shot:


and the errors I get in the Max Window:
oopsy-verbose: oopsy generated code
oopsy-verbose: oopsy compiling…
oopsy-verbose: compiling…
oopsy-verbose: generated code
oopsy-verbose: oopsy compiler error
oopsy-verbose: Error: Command failed: export PATH=$PATH:/usr/local/bin && make clean && make
oopsy-verbose: In file included from SIMPLE_BasicSeed.cpp:20:
oopsy-verbose: …/genlib_daisy.h:33:4: error: conflicting declaration ‘typedef struct Daisy Daisy’
oopsy-verbose: 33 | } Daisy;
oopsy-verbose: | ^~~~~
oopsy-verbose: SIMPLE_BasicSeed.cpp:19:26: note: previous declaration as ‘typedef class daisy::DaisySeed Daisy’
oopsy-verbose: 19 | typedef daisy::DaisySeed Daisy;
oopsy-verbose: | ^~~~~
oopsy-verbose: In file included from SIMPLE_BasicSeed.cpp:20:
oopsy-verbose: …/genlib_daisy.h: In member function ‘void oopsy::GenDaisy::reset(A&)’:
oopsy-verbose: …/genlib_daisy.h:393:13: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 393 | hardware.seed.ChangeAudioCallback(nullAudioCallback);
oopsy-verbose: | ^~~~
oopsy-verbose: …/genlib_daisy.h:406:13: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 406 | hardware.seed.ChangeAudioCallback(newapp.staticAudioCallback);
oopsy-verbose: | ^~~~
oopsy-verbose: …/genlib_daisy.h:408:42: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 408 | log(“SR %dkHz / %dHz”, (int)(hardware.seed.AudioSampleRate()/1000), (int)hardware.seed.AudioCallbackRate());
oopsy-verbose: | ^~~~
oopsy-verbose: …/genlib_daisy.h:408:86: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 408 | log(“SR %dkHz / %dHz”, (int)(hardware.seed.AudioSampleRate()/1000), (int)hardware.seed.AudioCallbackRate());
oopsy-verbose: | ^~~~
oopsy-verbose: …/genlib_daisy.h: In member function ‘int oopsy::GenDaisy::run(oopsy::AppDef*, int)’:
oopsy-verbose: …/genlib_daisy.h:475:13: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 475 | hardware.seed.adc.Start();
oopsy-verbose: | ^~~~
oopsy-verbose: …/genlib_daisy.h:476:13: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 476 | hardware.seed.StartAudio(nullAudioCallback);
oopsy-verbose: | ^~~~
oopsy-verbose: …/genlib_daisy.h:516:14: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 516 | hardware.seed.SetLed((t % 1000)/10 <= uint32_t(audioCpuUsage));
oopsy-verbose: | ^~~~
oopsy-verbose: In file included from SIMPLE_BasicSeed.cpp:20:
oopsy-verbose: …/genlib_daisy.h: In static member function ‘static void oopsy::App::staticAudioCallback(float**, float**, size_t)’:
oopsy-verbose: …/genlib_daisy.h:1089:76: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 1089 | float percent = (daisy::System::GetUs() - start)0.0001fdaisy.hardware.seed.AudioCallbackRate();
oopsy-verbose: | ^~~~
oopsy-verbose: SIMPLE_BasicSeed.cpp: In member function ‘void App_SIMPLE::init(oopsy::GenDaisy&)’:
oopsy-verbose: SIMPLE_BasicSeed.cpp:35:45: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 35 | daisy.gen = SIMPLE::create(daisy.hardware.seed.AudioSampleRate(), daisy.hardware.seed.AudioBlockSize());
oopsy-verbose: | ^~~~
oopsy-verbose: SIMPLE_BasicSeed.cpp:35:84: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 35 | daisy.gen = SIMPLE::create(daisy.hardware.seed.AudioSampleRate(), daisy.hardware.seed.AudioBlockSize());
oopsy-verbose: | ^~~~
oopsy-verbose: SIMPLE_BasicSeed.cpp: In function ‘int main()’:
oopsy-verbose: SIMPLE_BasicSeed.cpp:112:24: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 112 | oopsy::daisy.hardware.seed.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_48KHZ);
oopsy-verbose: | ^~~~
oopsy-verbose: SIMPLE_BasicSeed.cpp:113:24: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 113 | oopsy::daisy.hardware.seed.SetAudioBlockSize(OOPSY_BLOCK_SIZE);
oopsy-verbose: | ^~~~
oopsy-verbose: make: *** [build/SIMPLE_BasicSeed.o] Error 1
oopsy-verbose:
oopsy-verbose: at ChildProcess.exithandler (child_process.js:295:12)
oopsy-verbose: at ChildProcess.emit (events.js:223:5)
oopsy-verbose: at maybeClose (internal/child_process.js:1021:16)
oopsy-verbose: at Socket. (internal/child_process.js:430:11)
oopsy-verbose: at Socket.emit (events.js:223:5)
oopsy-verbose: at Pipe. (net.js:664:12) {
oopsy-verbose: killed: false,
oopsy-verbose: code: 2,
oopsy-verbose: signal: null,
oopsy-verbose: cmd: ‘export PATH=$PATH:/usr/local/bin && make clean && make’
oopsy-verbose: }
oopsy-verbose: In file included from SIMPLE_BasicSeed.cpp:20:
oopsy-verbose: …/genlib_daisy.h:33:4: error: conflicting declaration ‘typedef struct Daisy Daisy’
oopsy-verbose: 33 | } Daisy;
oopsy-verbose: | ^~~~~
oopsy-verbose: SIMPLE_BasicSeed.cpp:19:26: note: previous declaration as ‘typedef class daisy::DaisySeed Daisy’
oopsy-verbose: 19 | typedef daisy::DaisySeed Daisy;
oopsy-verbose: | ^~~~~
oopsy-verbose: In file included from SIMPLE_BasicSeed.cpp:20:
oopsy-verbose: …/genlib_daisy.h: In member function ‘void oopsy::GenDaisy::reset(A&)’:
oopsy-verbose: …/genlib_daisy.h:393:13: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 393 | hardware.seed.ChangeAudioCallback(nullAudioCallback);
oopsy-verbose: | ^~~~
oopsy-verbose: …/genlib_daisy.h:406:13: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 406 | hardware.seed.ChangeAudioCallback(newapp.staticAudioCallback);
oopsy-verbose: | ^~~~
oopsy-verbose: …/genlib_daisy.h:408:42: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 408 | log(“SR %dkHz / %dHz”, (int)(hardware.seed.AudioSampleRate()/1000), (int)hardware.seed.AudioCallbackRate());
oopsy-verbose: | ^~~~
oopsy-verbose: …/genlib_daisy.h:408:86: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 408 | log(“SR %dkHz / %dHz”, (int)(hardware.seed.AudioSampleRate()/1000), (int)hardware.seed.AudioCallbackRate());
oopsy-verbose: | ^~~~
oopsy-verbose: …/genlib_daisy.h: In member function ‘int oopsy::GenDaisy::run(oopsy::AppDef*, int)’:
oopsy-verbose: …/genlib_daisy.h:475:13: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 475 | hardware.seed.adc.Start();
oopsy-verbose: | ^~~~
oopsy-verbose: …/genlib_daisy.h:476:13: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 476 | hardware.seed.StartAudio(nullAudioCallback);
oopsy-verbose: | ^~~~
oopsy-verbose: …/genlib_daisy.h:516:14: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 516 | hardware.seed.SetLed((t % 1000)/10 <= uint32_t(audioCpuUsage));
oopsy-verbose: | ^~~~
oopsy-verbose: In file included from SIMPLE_BasicSeed.cpp:20:
oopsy-verbose: …/genlib_daisy.h: In static member function ‘static void oopsy::App::staticAudioCallback(float**, float**, size_t)’:
oopsy-verbose: …/genlib_daisy.h:1089:76: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 1089 | float percent = (daisy::System::GetUs() - start)0.0001fdaisy.hardware.seed.AudioCallbackRate();
oopsy-verbose: | ^~~~
oopsy-verbose: SIMPLE_BasicSeed.cpp: In member function ‘void App_SIMPLE::init(oopsy::GenDaisy&)’:
oopsy-verbose: SIMPLE_BasicSeed.cpp:35:45: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 35 | daisy.gen = SIMPLE::create(daisy.hardware.seed.AudioSampleRate(), daisy.hardware.seed.AudioBlockSize());
oopsy-verbose: | ^~~~
oopsy-verbose: SIMPLE_BasicSeed.cpp:35:84: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 35 | daisy.gen = SIMPLE::create(daisy.hardware.seed.AudioSampleRate(), daisy.hardware.seed.AudioBlockSize());
oopsy-verbose: | ^~~~
oopsy-verbose: SIMPLE_BasicSeed.cpp: In function ‘int main()’:
oopsy-verbose: SIMPLE_BasicSeed.cpp:112:24: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 112 | oopsy::daisy.hardware.seed.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_48KHZ);
oopsy-verbose: | ^~~~
oopsy-verbose: SIMPLE_BasicSeed.cpp:113:24: error: ‘Daisy’ {aka ‘class daisy::DaisySeed’} has no member named ‘seed’
oopsy-verbose: 113 | oopsy::daisy.hardware.seed.SetAudioBlockSize(OOPSY_BLOCK_SIZE);
oopsy-verbose: | ^~~~
oopsy-verbose: make: *** [build/SIMPLE_BasicSeed.o] Error 1
oopsy-verbose:
oopsy-verbose: compiler error

Hey, I have installed dev branch of oopsy and MIDI program change doesn’t work for me. I don’t see any info on the Daisy Patch (shows 0.000) nor apps are being changed. Is it supposed to be connected to each app and oopsy.midi.parse object as well? or should just work by default ie by just sending Program Change msgs?
Would really appreciate if you can point me in the right direction, keen to try it out.

If you have more than one app on the firmware, the MIDI program change for app changing should “just work”, doesn’t need any oopsy.midi.parse object or anything.

Try the “multi_app” example from the Oopsy examples folder.

Note that currently USB midi isn’t supported, only the TRS midi ports on the Patch.

1 Like

Thank you I will test it again. That was the one i was checking and have new dev one. At least the patch is different then the previous version.
Is there a way to send MIDI program change from the oopsy itself to itself. Sort of self patching it internally so i can use different input to change the apps? Really appreciate your tips.

Ah I see. The thing is, currently, knob/gate/etc. mappings are per-app, rather than global. I’ll have to think about whether or how we could make an app-change mapping global. I hadn’t really considered this but it does make sense to be able to switch apps by button presses etc., but this might be something for a custom seed target.

2 Likes

I’d be very interested in a custom mapping for knobs to Daisy. One of the thing Daisy patch lacks is control knobs. You can make a beautiful and powerful synthesizer, but not be able to control it effectively.

But a great workaround is the midi in! 128 knobs!

Although the scope for custom seed uses for oopsy is large, one thing I think most could benefit from is just sending midi out.

Then you can just buy another Daisy and use it as the base for an up to 12 potentiometer midi controller. I think more if you use encoders.

Although midi modulations aren’t so smooth on 7 bit midi synths, it also opens things up to a midi controller that can not only send cc via knobs. You can also map it to send modulations. Give a synth that extra lfo you need etc. send program changes to many synths or sequence song sections. Or go wild with 14 bit midi synths.

You can even map it for presets for midi devices that can’t save presets. Or you can use it to send 100 random CC values to a synth and give it a “random patch” function.

If oopsy could just give seed a midi out functionality based on knobs and then a midi jack out, I think it would be a very useful thing. It’s a pretty general use that gives people a lot to work with. And programming in a great editor.

There are diy midi kits, but imo Daisy would make a far superior midi controller brain than virtually all of them. And, again, it not only can send controls, but movements. With 16 knobs it could easily be a sequencer more customizable and personal than anything on the market since, Oopsy/max.

i’m back after a while, and having a little trouble. i’ve built a little hardware to host a seed, and now that i’m ready to test it, i cant get oopsy to handle custom .JSON files written in the new format.

i get an error, specifically

“node.script: Unexpected string in JSON at position 223”

when i try my new config file. here’s the context it happens in:

and here’s my config file:

{
	"name":"bread",
	"components": {
		"LW2": {
			"component": "AnalogControl",
			"pin": 15
		},
		"LW1": {
			"component": "AnalogControl",
			"pin": 16
		},
		"LC2": {
			"component": "AnalogControl",
			"pin": 17
		}
		"LC1": {
			"component": "AnalogControl",
			"pin": 18
		}
		"RC1": {
			"component": "AnalogControl",
			"pin": 19
		}
		"HEAD": {
			"component": "AnalogControl",
			"pin": 20
		}
		"RC2": {
			"component": "AnalogControl",
			"pin": 21
		}
		"RW1": {
			"component": "AnalogControl",
			"pin": 22
		}
		"RW2": {
			"component": "AnalogControl",
			"pin": 23
		}
	}
}

i can’t figure out anything wrong, except that i used to be on the dev branch of the github to be able to do the custom targets, and now that seems to be gone (?), but i assume that was just folded into main.

what could the issue be? i can’t find anything on line 223 of anything that looks out of place, so i assume i’m looking in the wrong places.

edit: LMAO it’s just missing commas, look at the code i pasted in! thanks everyone for building this wonderful hardware and software!

Hello,

I am trying implement multi-app on Daisy Seed using a custom json.

I have conected the encoder following this schematic:

The Json has this code:

{
“name”:“versio”,
“defines”: {},
“components”: {

	"encoder": {
		"component": "Encoder",
		"pin": {"a":11, "b":12, "click":0 }
	},
	"led1": {
		"component": "RgbLed",
		"pin": {"r":20, "g":19, "b":18 }
	},
	"led2": {
		"component": "RgbLed",
		"pin": {"r":17, "g":24, "b":23 }
	}
}

}

And I have created a Max patch with Oopsy object:

And two gen~ patches playing sinewaves with differente frequency:

Captura de Pantalla 2022-12-28 a las 18.00.57
(upload://547OAtqNOVBTbVVFGod6WhkjtZG.png)

Captura de Pantalla 2022-12-28 a las 17.55.55

I select my Json from bowser and It flashes fine, but I can only listen one of the frequencies and I cannot change using the encoder.

Any clue about how to approach this.

Best

1 Like

One month and no answer… I really appreciate any clue. Thanks

Hey xoanxil!

I’m sorry for the delay in response.

As of right now, Multi-App feature is designed for Patch, Field, and Petal. You can read more about it in oopsy’s README file.

It could be worth your time hooking up your OLED and encoder to the same pins that Daisy Patch uses for those components. Hardware/ES_Daisy_Patch_Rev4.pdf at master · electro-smith/Hardware · GitHub
And then flash your patcher with the Patch json file.

I unfortunately don’t have an encoder nor OLED at the moment, so I can’t test with you to guarantee that it’ll work.
I hope it does though!

2 Likes

Thanks for your answer Takumi. I gonna try with OLED and encoder as in Patch schematics.

Best,

x.x

1 Like