Hothouse DSP Pedal Kit

Hi folks. Even though it feels like a shameful plug, Electrosmith recommended I post here :sunglasses: :pray:

I just wanted to mention the availability of the Hothouse Digital Signal Processing Pedal Kit. It’s essentially an accelerator for taking your Daisy Seed projects from the breadboard to your effects pedalboard without the need to design and source parts for your own PCBs and enclosure. There are no through-hole resistors, capacitors, diodes, etc. to mess with; it’s a matter of assembling the pre-populated PCBs and hardware bits with solder and ribbon cables. Everything except the Daisy Seed is provided, and there’s a growing companion Github repo.

The key features can be found at the Cleveland Music Co. link above. Also note that the Electrosmith folks were kind enough to link to the Hothouse from the Daisy Seed product page (thank you, Andrew!)

Cheers for reading this far!

P.S. Worry not audiophiles: yes, it’s a surface mount design, but all the audio path capacitors are C0G/NP0 with Class 1 dielectric material.


2 Likes

Is the schematic available?

Sure. Dead simple stuff for a dead simple use case.

I’m not planning to post the CAD files, gerbers, drill plots, etc. because that’s not the target audience, in my mind.

1 Like

Worth a try - ordered!

1 Like

I love the idea of this kit - providing the hardware to users to remove the challenge of mechanical construction that many electronic hobbyists (and professionals) find challenging. It can let us get on with coding without worrying how that gets into the audio chain and at our feet. I do have some observations about the electronic circuit design:

How does the NE5532 handle a small, single rail supply? Its spec says its minimum supply is +/-5V yet you are using it with 0-5V which seems half the specified minimum. Did you consider the MCP6022 which has pretty good audio spec (good enough for guitar!) and supports rail-to-rail output. It has a maximum supply of 5.5V but seems an ideal candidate for this use case.

The input impedance looks a bit low, with three 1MΩ resistors effectively in parallel which may result in 0.3MΩ. Have you checked how this sounds with a single coil pickup?

The virtual earth isn’t particularly well isolated from the input which could drag it around quite a lot. You could have used smaller resistors for the potential divider and summed it into the non-inverting input with a high value resistor (e.g. 1MΩ).

I think you missed a trick, not presenting both audio channels of the microcontroller, especially the output which could be used to implement stereo / widening effects. It would be cool to have two jacks on each side to facilitate this or at least two on the output - or even a single stereo socket on the output. (I appreciate many users will use a stereo socket on the input to make the 9V battery connection.)

Good luck with the product. It is definitely welcome.

1 Like

Cheers very much for the feedback, @riban. In testing, the circuit above performs well enough and is based on other tried and true designs. But I am always open to learning and improving, so I’ll certainly consider your thoughtful suggestions.

I agree about the stereo design. In fact, the next version will be stereo.

2 Likes

Kit arrived today, I assembled it this evening. Loaded the ShimmerVerb, seems to work.

1 Like

That’s great. Cheers again!

I’ll be posting a few more straightforward effects in the coming days. Nothing crazy, but some useful examples to show how to access all the hardware while sounding decent at the same time.

I wanted to point out there is a mistake in this schematic: IC1 is noted as an NE5532 op-amp, but that is incorrect. While doing the PCB layout, this component was selected simply for its SOP-8 footprint. But in the fabrication BOM, an MCP6022 is used.

Apologies for any confusion.

2 Likes

That’s super useful feedback. Cheers for that. I’ll add to the FAQ and comment the code accordingly.

At the moment, the examples use FallingEdge().

For example:

  // Toggle effect bypass LED when footswitch is pressed
  if (hw.switches[Hothouse::FOOTSWITCH_2].FallingEdge()) {
    bypass = !bypass;
    led_bypass.Set(bypass ? 0.0f : 1.0f);
  }

I accidentally deleted a post above, which was riddled with errors. OOPS!

Yes, first thing I noticed was that example code FallingEdge() went active on RELEASE of the footswitch.

Similarly, the schematic doesn’t show the actual connection of the LEDs.

Here’s a little example for Hothouse.
@newkular - add it to your examples if you like
trem_verb.cpp:


// TremVerb for Hothouse DIY DSP Platform
// Copyright (C) 2024 Cleveland Music Co. <code@clevelandmusicco.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

// KNOB_1 : trem RATE
// KNOB_2 : trem DEPTH
// KNOB_3 : Reverb Amount
// TOGGLESWITCH_1 : SAW, SIN, SQUARE
// FS_1 : TREM enable
// FS_2 : VERB enable

#include "daisysp.h"
#include "hothouse.h"

using clevelandmusicco::Hothouse;
using daisy::AudioHandle;
using daisy::Led;
using daisy::SaiHandle;
using daisy::Parameter;
using daisysp::Tremolo; 
using daisysp::Oscillator;
using daisysp::ReverbSc;

Hothouse hw;

ReverbSc verb;
Tremolo trem;
Parameter p_rate, p_depth, p_vamt;

// Bypass vars
Led led_trem, led_verb;
bool bypass_trem = true;
bool bypass_verb = true;

int get_waveform(void)
{
  switch (hw.GetToggleswitchPosition(Hothouse::TOGGLESWITCH_1)) {
    case Hothouse::TOGGLESWITCH_UP:
		return Oscillator::WAVE_SAW;
      break;
    case Hothouse::TOGGLESWITCH_MIDDLE:
		return Oscillator::WAVE_SIN;
      break;
    case Hothouse::TOGGLESWITCH_DOWN:
    default:
	return Oscillator::WAVE_SQUARE;
      break;
  } 
}

void AudioCallback(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out,
                   size_t size) {
   static float trem_val;
   float vamt;
   hw.ProcessAllControls();

  if (hw.switches[Hothouse::FOOTSWITCH_1].RisingEdge()) {
    bypass_trem = !bypass_trem;
  }
  // reduce number of LED Updates for pulsing trem LED
  {
	static int count = 0;
	// set led 100 times/sec
	if (++count == hw.AudioCallbackRate() / 100) {
		count = 0;
		led_trem.Set(bypass_trem ? 0.f : trem_val);
	}
  }
  led_trem.Update();
  if (hw.switches[Hothouse::FOOTSWITCH_2].RisingEdge()) {
    bypass_verb = !bypass_verb;
	led_verb.Set(bypass_verb ? 0.f : 1.f);
  }
  led_verb.Update();

  trem.SetFreq(p_rate.Process());
  trem.SetDepth(p_depth.Process());
  trem.SetWaveform(get_waveform());

  for (size_t i = 0; i < size; ++i) {
	float s, out_l, out_r;
	s = in[0][i];
	if (!bypass_trem) {
		// trem_val gets used above for pulsing LED
		trem_val = trem.Process(1.f);
		s = s * trem_val;
	}
	if (!bypass_verb) {
		verb.Process(s, s, &out_l, & out_r);
		vamt = p_vamt.Process();
		s = (s * (1.f - vamt) + vamt * ((out_l+out_r)/2.f));
	}

    out[0][i] = s ;
  }
}

int main() {
  hw.Init();
  hw.SetAudioBlockSize(4);  // Number of samples handled per callback
  hw.SetAudioSampleRate(SaiHandle::Config::SampleRate::SAI_48KHZ);

  // Initialize LEDs
  led_trem.Init(hw.seed.GetPin(Hothouse::LED_1), false);
  led_verb.Init(hw.seed.GetPin(Hothouse::LED_2), false);

  p_rate.Init(hw.knobs[Hothouse::KNOB_1], 0.2f, 20.0f, Parameter::LINEAR);
  p_depth.Init(hw.knobs[Hothouse::KNOB_2], 0.0f, 1.0f, Parameter::LINEAR);
  p_vamt.Init (hw.knobs[Hothouse::KNOB_3], 0.0f, 1.0f, Parameter::LINEAR);

  trem.Init(hw.AudioSampleRate());
  trem.SetWaveform(Oscillator::WAVE_SIN);

  verb.Init(hw.AudioSampleRate());
  verb.SetFeedback(0.87);
  verb.SetLpFreq(10000.0f);

  hw.StartAdc();
  hw.StartAudio(AudioCallback);

  while(1) {
	;
  }

  return 0;
}
2 Likes

Awesome :metal:

Posted at: clevelandmusicco/HothouseExamples Show And Tell · Discussions · GitHub. I’ll work it in to the repo after some thought about organisation.

Cracking idea for independent control of the verb and trem (like that pedal named after a town in Michigan).

Also note that pull requests are totally welcome!

1 Like

We sorted things out in a DM thread, but I wanted to follow up here for anyone else.

I was away from a dev machine yesterday and couldn’t confirm or dispel the observation, so this morning I threw together some quick and dirty test code:

#include "daisysp.h"
#include "hothouse.h"

using clevelandmusicco::Hothouse;
using daisy::AudioHandle;
using daisy::Led;
using daisy::SaiHandle;

Hothouse hw;

Led led_one, led_bypass;
bool bypass = true;

void AudioCallback(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t size) {
  hw.ProcessAllControls();

  // Light LED_1 *while* FOOTSWITCH_1 is pressed
  if (hw.switches[Hothouse::FOOTSWITCH_1].Pressed()) {
    led_one.Set(1.0f);
  } else {
    led_one.Set(0.0f);
  }

  // Toggle effect bypass LED when switch is pressed
  // FallingEdge() also works as expected (LED lights when switch is released)
  if (hw.switches[Hothouse::FOOTSWITCH_2].RisingEdge()) {
    bypass = !bypass;
    // LED off when bypassed, on otherwise
    led_bypass.Set(bypass ? 0.0f : 1.0f);
  }

  // Update the LEDs
  led_one.Update();
  led_bypass.Update();

  for (size_t i = 0; i < size; ++i) {
    if (bypass) {
      out[0][i] = in[0][i];
    } else {
      out[0][i] = 0.0f;  // TODO: replace silence with something awesome
    }
  }
}

int main() {
  hw.Init();
  hw.SetAudioBlockSize(4);  // Number of samples handled per callback
  hw.SetAudioSampleRate(SaiHandle::Config::SampleRate::SAI_48KHZ);

  // Initialize LEDs
  led_one.Init(hw.seed.GetPin(Hothouse::LED_1), false);
  led_bypass.Init(hw.seed.GetPin(Hothouse::LED_2), false);

  hw.StartAdc();
  hw.StartAudio(AudioCallback);

  while (true) {
    // Do nothing forever
  }
  return 0;
}

In a nutshell: everything works as I believe it should. And for completeness, here’s where Init() on daisy::Switch gets called in hothouse.cpp:

void Hothouse::InitSwitches() {
  constexpr Pin pin_numbers[SWITCH_LAST] = {
      PIN_SW_1_UP, PIN_SW_1_DOWN, PIN_SW_2_UP, PIN_SW_2_DOWN,
      PIN_SW_3_UP, PIN_SW_3_DOWN, PIN_FSW_1,   PIN_FSW_2,
  };

  for (size_t i = 0; i < SWITCH_LAST; i++) {
    switches[i].Init(pin_numbers[i]);
  }
}

I’ll probably include some little code snippets (or gists) in the doc with clear examples of how to access and use the various hardware controls.

And you’re right: I need to post a correct and complete schematic (including the daughterboard for the LEDs and footswitches) in the doc today. Cheers for that feedback.

1 Like

A quick update: for those that have asked, this kit now ships with a cutout for the USB.

1 Like