Hagiwo $9 Raspberry pi VCO with Seeed XIAO RP2040 Eurorack Modular Synthesizer

I just soldered together the Hagiwo $9 Rasberry pi VCO with Seeed XIAO RP2040 Eurorack Modular Synthesizer.
What can I say: it didn’t work. So first I looked at the sketch. Potentiometer and switch values were output to the serial monitor. Everything ok! Then I saw that the function

void loop1() {//modulation

in line 129 is not called at all. I then inserted the

function void loop()

and lo and behold, now the VCO works.

However, I have a slight jitter in the audio and my filter does not suppress the high frequency of the PWM.
Maybe hagiwo can say something about this or has another member already built this VCO?

Greetings tangram


I’m sure @HAGIWO would change the code.


Thanks for the report. It is helpful.
I have tried it in my environment and it seems to work fine with the current code.

I am not sure why, but it may be a difference in the board configuration. I am attaching an image of my configuration for your reference.
The void loop1 means that the processing is done on core 2 of the RP2040.

As noted in my blog, the audio output is PWM and therefore generates harmonic noise.
Try tuning the low-pass filter resistor.

Thank you!



There is indeed a problem compiling the code as published on Hagiwo’s web site in the Arduino IDE. Nothing calls the loop1() function and it is removed by the optimiser - the resulting code size is identical to that with the entire loop1() function deleted from the source code. From my limited reading so far (I’m very new to the RP2040) there should be a function call somewhere to set loop1() running on core1.

I’ve just tried adding
#include “pico/multicore.h”
at the head of the source code together with
in the setup() function (before the interrupts are set up). This gives a larger executable than before, so I hope that this will indeed run loop1() on core1 of the RP2040. Unfortunately I haven’t yet breadboarded the hardware so don’t know if these precise changes work…

Many thanks to Hagiwo for publishing such an amazing resource of synth modules!

Update: my simple modifications unfortunately didn’t fix the issue - not surprising given my limited experience with the RP2040! I’ll continue investigating how to get a multithreaded program running via the Arduino IDE.

Update 2: The contents of the loop1() function also need to be in an explicit while (1) {…} loop, otherwise they only get executed once. With these changes there is sound output and responses to input from the switches and potentiometers. Still not sure if it’s working correctly, but definitely significant progress!


A final update from me. This issue depends on which RP2040 core is in use in the Arduino IDE. My guess is that Hagiwo uses the earlephilhower/arduino-pico core; this recognises the loop1() function as special and will automatically run the contained code in a loop on core1. In contrast, I use the Seeed XIAO RP2040 and/or Arduino Mbed OS RP2040 cores. These don’t recognise loop1() as special and require the function to be explicitly associated with core1, as well as needing an explicit loop within the function itself.

So Hagiwo’s original code should work for anyone using the earlephilhower/arduino-pico core. Otherwise the changes detailed in my previous post should allow successful compilation and upload of working code to the RP2040 using the Arduino IDE.

For clarity, these changes are -

  1. Add
    #include <pico/multicore.h>
    at the head of the source code
  2. Add
    just after the pinMode() statements in setup()
  3. Enclose the body of the loop1() function in a while loop -
    void loop1() {
    while (1) {
    // existing contents of function

With these changes the code works as expected for me - I hope this will help other builders too.

A couple of other notes:
I increased the value of the two filter resistors to 1k, which should bring the cutoff frequency down to about 15kHz and removes all the high frequency artefacts.
The analogue inputs on the RP2040 are high impedance and very susceptible to induced noise. If you’re testing the VCO on a breadboard using only the frequency and modulation potentiometers without the input circuitry for V/OCT and MOD CV (as I did), be sure to tie pins 26 and 28 to ground. Otherwise you will get spurious effects due to noise pick-up.

Hope this helps - and thanks again to Hagiwo for the VCO design - it’s brilliant!


I still have one question: Where is the folder /pico and which files are in this folder?
That was 2 questions. Sorry!
Greeting tangram

1 Like


If you’ve installed the relevant boards through the Boards Manager in the Arduino IDE, it will have been created somewhere in the compiler’s INCLUDE path. The IDE searches this list of directories for header files at compile time. You shouldn’t need to worry about where it is, just put the #include line at the top of source file and the IDE will find it.

Under linux, for me the path is /home/<user_name>/.arduino15/packages/Seeeduino/hardware/rp2040/2.7.2/cores/arduino/mbed/targets/TARGET_RASPBERRYPI/TARGET_RP2040/pico-sdk/rp2_common/pico_multicore/include/pico, in Windows it’s C:\Users\<user_name>\AppData\Local\Arduino15.… (the rest is the same but with \ rather than /). This folder only contains multicore.h, but there are many more branches higher up in the directory structure. But you shouldn’t really need to look in here at all.

Hope this helps!


it helped and how it helped. No more jitter and also the exchange of the resistors has helped.
I also had a bug in the software:
I had entered #include “pico/multicore.h”.
But of course it should be #include <pico/multicore.h>.
Embarrassing, Embarrassing
Thanks again for the help
Greetings tangram



Good to hear you’ve got it working!

The #include issue is interesting. I agree that it should really be <pico/multicore.h>, but I had accidentally entered “pico/multicore.h” and it worked fine for me. Anyway, I’ve edited the main post to the correct form in case anyone else needs to apply this fix.


Thanks for the detailed explanation!
I will verify my source code this weekend.


Corrected source code.
The solution is simple: core1 processing is now performed by core0.

Thanks to the RP2040’s powerful processor.
Audio output, waveform modulation, and IN/OUT interface can be performed in one core.


Hi there, just breadboarded the oscillator and I was wondering if you can post the updated code, I’ve tried the mods and I’m stuck on the loop1 implentation.


Three questions

Can you modulate the frequency with an LFO for instance, where would be the best place to inject an LFO signal, at the frequency input or at the CV input?

Will it support glide if the CV is fed through a portamento circuit or is the input quantized?

What about pitch bend? again can that be apppied through the frequency (probably too narrow) or the CV input.


There isn’t an input jack labeled frequency, just a frequency pot and a CV jack. The latter is where an LFO or pitch bend would go in.

I’m not familiar with the module but I’d be astonished if it quantized the input.

Yes, I’m aware there is no jack on the frequency control, however it might be possible to use that analogue input as a voltage mixing point for Freq, modulation and pitchbend. But it might be easier to the CV input and put a voltage mixer on that. I can test the Quantize out quite easily by just pitchbending a note.

The Electric Druid VCDO1 chips Quantize on their CV inputs. But they have a seperate coarse and fine frequency input for modulation etc.

1 Like

The relevant code is

  adc = analogRead(26) * calb;//Correct resistance errors
  adc = constrain(adc , 0, 1225) ;//Covers 6(=1220) octaves. If it is 1230 or more, the operation becomes unstable.
  freq_pot = map(analogRead(27), 0, 1023, 0, 127);
  osc_freq = freq_table[adc + freq_pot]; // V/oct apply
  osc_freq = 256 * osc_freq / 122070 * (1 + oct_sw);

where freq_table is a 1230 element array, so it’s certainly not quantizing to semitones. (Technically, it is quantizing, but to 1230 possible values over 6 octaves.) It’s summing what it gets from pins 26 and 27, the CV input and frequency pot. However pin 26 has a calibration constant for accurate 1 V/oct response while pin 27 does not, and pin 26 is not scaled but constrained to range 0 to 1225 while pin 27 is scaled to range 0 to 127. You could attach a jack and summing circuit to pin 27 for input of an LFO or pitch bend but the total range would be limited to about 0.6 octaves. Or you could mix CVs externally, or add a jack and a summing circuit to pin 27, to get a larger response range.

Incidentally, a bug report for @HAGIWO — in the code adc can be 0 to 1225 and freq_pot can be 0 to 127, so their sum can be 1402, in which case the array index is out of bounds.


If you want to mix multiple frequency CV inputs it would be easy to add an inverting mixer for these followed by a simple inverting buffer using a couple more op-amps, then feed the combined output into a single analogue input on the rp2040. I’ll probably do this when I build my final version as it will allow both positive and negative pitch modulation by CV (for all except the lowest notes) in addition to the main 1V/Oct CV input.

BTW, there isn’t a bug associated with values of (adc + freq_pot) > 1225. The freq_table array is of size 2048 with all elements >= 1230 set to a value of 6. However, given the scaling of freq_pot to the range 0-127 quite a lot of this extra space is wasted.

You’re right, I was confusing the freq_table list with voctpow, which is dimensioned to 1230 but is used only to initialize freq_table. Which uses more data space than it needs to, but isn’t a bug.

A rather belated update. I’ve now completed an RP2040zero VCO based on Hagiwo’s design, but with a few significant changes:

  • A TL074 input buffer mixes input voltages from the Pitch potentiometer, a V/OCT input and a pitch modulation CV input before passing the result to a single analogue input on the RP2040. This allows negative modulation voltages to be mixed in if required. The configuration is a simple inverting mixer followed by an inverting buffer with a 10-turn trimmer pot in the feedback loop, allowing the gain to be set so 5V on the V/OCT input is reduced to 3.3V on the analogue input to the RP2040. This allows fine adjustment of the oscillator’s tuning without having to iteratively reprogram the ‘calb’ variable in the code.

  • The other two op-amps in the TL074 are wired similarly to mix the Modulation potentiometer and a Modulation CV input. The BAT43 diode voltage clamps are retained on both this input to the RP2040 and the Pitch input to protect against out-of-range combined input voltages.

  • The above changes mean that only two analogue inputs are required, so an original Pi zero could be used. Note that there is only one ADC in the RP2040, with the inputs connected via a multiplexer. This means that there can be crosstalk between the inputs and I found that changing the Modulation pot would also slightly affect the pitch. This is easy to fix in the main loop() function by doing a dummy analogRead() of the pitch input (without assigning the result to a variable) immediately after reading the modulation parameter. This allows the multiplexer to settle during the rest of the loop so the pitch input is accurate. Slight errors in the modulation input are not noticeable.

  • I prefer to have DC coupled output from VCOs, so I used a TL072 as the output stage with the high impedance buffer as in Hagiwo’s original design, followed by an inverting buffer mixing an adjustable negative voltage with the audio signal to shift the output to be symmetrical about 0V. The signals are inverted of course, but sound the same. I removed the clamp diodes from the output as well.

  • The on-board neopixel LED indicates which of the 8 waveforms or FM modulations is selected (red, orange, yellow, green, cyan, blue, magenta & white respectively). A small piece of transparent ABS filament works as a light pipe to display this on the front panel.

  • The waveform select switch is debounced in software

  • Wavefolding a square wave only changes its amplitude (not particularly useful), so this waveform now has pulse width modulation instead

I hope these ideas may prove useful to other builders. I’m delighted with the result, so thanks once again to Hagiwo for the brilliant original design. If Hagiwo agrees, I’m happy to publish full details of my modified circuit and the corresponding code.


So a question, if the CV is 0V and you apply modulation, how does the bipolar modulation affect the frequency and also if you applied pitchbend into the signal and it was downwards, how would this work.

I’m asking as I also have used a couple of these DCOs in a project and now I’m working on the modulation and pitchbend inputs. My plan was actually to repurpose the MOD CV input as an FM/PB input and center it at 2.5V so bipolar FM can be applied and also pos/neg pitchbend even at a CV of 0 volts or 5 volts.