El Wire Color Changing

Years ago I experimented with shifting the color of El Wire by changing the AC frequency.

The color effect is subtle, but can be seen by most people with good color vision.

Not long after I published this video, I discovered “dcroy” built this same thing a few months before I did, and using only a 555 timer! Looks like it’s not as unique as I thought!

This is the schematic for the circuit I built.  Most of the parts are small surface mount and they’re hidden under the transformer.

Here’s the code from the Arduino IDE. Pretty simple stuff. But beware, if you try this, it’s essential to drive each transistor for the same length of time, and not more than about 1ms. If you drive one more than the other, or leave either on too long, the transform sees a DC or low frequency signal. That could result in far too much current, probably destroying the transistors if the battery is fresh.

const int pin1 = 12;
const int pin2 = 19;

void setup() {
  Serial.end();  // USB off, to save power
  for (byte i=0; i<25; i++) {
    pinMode(i, OUTPUT);    // all unused pins low
    digitalWrite(i, LOW);  // to avoid wasting power
  }
  pinMode(pin1, OUTPUT);
  pinMode(pin2, OUTPUT);
}

unsigned int us = 250 * 64;
const int usMax = 500 * 32;
const int usMin = 120 * 32;
byte dir = 0;

void loop() {
  digitalWrite(pin1, HIGH);
  delayMicroseconds(us / 32);
  digitalWrite(pin1, LOW);
  if (dir) {
    us = us + 1 + us / 2048;
    if (us >= usMax) dir = 0;
  } else {
    us = us - 1 - us / 2048;
    if (us <= usMin) dir = 1; 
  }
  digitalWrite(pin2, HIGH);
  delayMicroseconds(us / 32);
  digitalWrite(pin2, LOW);
}

This article was originally published on the DorkbotPDX website, on July 12, 2011.  In late 2018, DorkbotPDX removed its blog section.  An archive of the original article is still available on the Internet Archive.  I am republishing this article here, in case anyone might wish to try El Wire color changing.  Admittedly, El Wire became less popular around 2013 when inexpensive LED strips appeared on the market, but it still offers interesting effects which are hard to accomplish with LEDs.

 

Big 7-Segment Countdown Timer

Years ago, several “short” talks at a hackerspace that went far beyond their allotted time inspired me to make this nice countdown timer.

I found these 4 inch 7 segment displays cheap on Ebay, made a little constant-current driver board, and put it all together into a simple project.

Here’s what the back side looks like, with buttons to control the countdown.

The green buttons starts & stops the countdown.  The 2 blue buttons add or subtract 1 minute.  It’s a very simple and minimal user interface!

 

Here’s a rough schematic for the whole project.  Well, except I left off the 7805 regulator and maybe some other mundane stuff, but this is pretty close.

The main challenge was driving the LEDs with a constant current, because they need about 10.5 volts across the several series LEDs.  I wanted to run from 12 volts, so there wasn’t much voltage left over for the normal current limiting resistors.  Instead, I used this opamp circuit.

(edit: opps, I wrote LM358A on the schematic, but it’s actually a LM324A opamp.  Really, they’re the same, just 2 vs 4 per package… so you could use either if you try to build this on your own board, but if you use the PCB files below, get a 14 pin LM324A opamps)

The project runs from a Teensy 2.0.  The code is very simple, using the SPI and Bounce libraries for the hardware interfacing.

#include <SPI.h>
#include <Bounce.h>

// pins
//  0 - Latch
//  1 - Clock
//  2 - Data
//  4 - Enable (low=on)
//  9 - Dots (high=on)

Bounce button1 = Bounce(10, 12);
Bounce button2 = Bounce(23, 12);
Bounce button3 = Bounce(22, 12);

uint8_t min=5;
uint8_t sec=0;
uint8_t unused_pins[] = {3,5,6,7,8,11,12,13,14,15,16,17,18,19,20,21,24};

void setup()
{
	for (uint8_t i=0; i < sizeof(unused_pins); i++) {
		pinMode(i, OUTPUT);
		digitalWrite(i, LOW);
	}
	pinMode(10, INPUT_PULLUP);
	PORTC |= 0x80;
	pinMode(22, INPUT_PULLUP);
	pinMode(23, INPUT_PULLUP);
	digitalWrite(4, HIGH);
	pinMode(0, OUTPUT);
	pinMode(1, OUTPUT);
	pinMode(2, OUTPUT);
	pinMode(4, OUTPUT);
	pinMode(9, OUTPUT);
	digitalWrite(0, LOW);
	digitalWrite(1, LOW);
	digitalWrite(2, LOW);
	digitalWrite(4, HIGH);
	digitalWrite(9, LOW);
	SPI.begin();
	update();
}

uint8_t sevenseg[10] = {
	// gfedcba
	0b00111111, //  aaa
	0b00000110, // f   b
	0b01011011, // f   b
	0b01001111, //  ggg
	0b01100110, // e   c
	0b01101101, // e   c
	0b01111101, //  ddd
	0b00000111,  
	0b01111111,  
	0b01101111  
};

void update(void)
{
	if (min > 99) min == 99;
	if (sec > 59) sec = 59;
	SPI.transfer(min >= 10 ? sevenseg[min/10] : 0);
	SPI.transfer(min > 0 ? sevenseg[min%10] : 0);
	SPI.transfer(sevenseg[sec/10]);
	SPI.transfer(sevenseg[sec%10]);
	delayMicroseconds(2);
	digitalWrite(0, LOW);
	delayMicroseconds(2);
	digitalWrite(0, HIGH);
	digitalWrite(9, HIGH);
	digitalWrite(4, LOW);
	delayMicroseconds(5);
	digitalWrite(0, LOW);
}

elapsedMillis count = 0;
uint8_t running = 0;

void loop()
{
	button1.update();
	button2.update();
	button3.update();
	if (button1.fallingEdge()) {
		Serial.println("button1 - Start/Stop");
		if (running) {
			running = 0;
		} else {
			running = 1;
			count = 750;
		}
	}
	if (button2.fallingEdge()) {
		Serial.println("button2 - Add 1 minute");
		if (min < 99) {
			min++;
			update();
		}
	}
	if (button3.fallingEdge()) {
		Serial.println("button3 - Subtract 1 minute");
		if (min > 0) {
			min--;
		} else {
			sec = 0;
			running = 0;
		}
		update();
	}

	if (running && count >= 1000) {
		count -= 1000;
		if (sec > 0) {
			sec--;
		} else {
			if (min > 0) {
				min--;
				sec = 59;
			} else {
				running = 0;
			}
		}
		update();
	}
}

The 7 segment displays were something I’d purchased from an E-bay merchant about a year ago.  I can’t find then anymore (at least not for low prices), which is sad because they were really cheap at the time.

I actually created this PCB only days after the last Dorkbot open mic meetup (where talks went way beyond allotted time).  Here’s a photo of the board.

There’s actually quite a few parts on the board.  Here are the placement diagrams:

The circuit board can be ordered from OSH Park, or you can download the original gerber files if you’d like to have them made elsewhere.

If you find any of these awesome 4 inch displays, hopefully this little board will come in handy for driving them efficiently with only 12 volts.

 

This article was originally published on the DorkbotPDX website, on March 22, 2013.  In late 2018, DorkbotPDX removed its blog section.  An archive of the original article is still available on the Internet Archive.  I am republishing this article here, in the hope it may continue to be found and used by anyone interested in these 7 segment displays.

Embrace Heart Sequenced Incandescent Light Dimming

I worked on a tiny piece of the Embrace sculpture at Burning Man 2014.

Inside were 2 hearts, one made here in Portland by Lostmachine Andy & others at Flat Rat Studios.  I made electronics to gradually fade 4 incandescent light bulbs in heart beating patterns.

These wonderful photos where taken by Sarah Taylor.

Inside the enormous sculpture were two hearts.  The blue on was built by a group in Vancouver, B.C., Canada, and of course this one was built here in Portland, Oregon, USA.

Andy wanted this heart to have a very warm, gentle atmosphere, with warn incandescent bulbs slowly fading to create the heart beat.  These effect turned out quite well.  Andy really knows his stuff!

Here’s a great time-lapse video where you can see the slow, gradual incandescent light fading as a rapid heart beat.  Skip forward to about 0:36 in this video.

 

The light fading was done using a Teensy-based 4-channel AC dimmer board, on this 4 by 3.5 inch circuit board.

Here’s a quick video, from the first test of the light controller.

Four BT139X Triacs that actually switch the AC voltage are mounted on the bottom side to a heatsink that’s meant to dissipate any heat to the metal case.  Originally Andy believed the lights might be 500 watts each, so I was concerned about heat.  In the end, four 60 watt bulbs were used and the Triacs did not get noticeably warm.

Here is a parts placement diagram for building the circuit board.  Two boards were built, the one that ran the project and a spare… just in case!

The PCB cad files are attached below, if anyone wants to make more of these boards.

The AC switching circuitry was basically Fairchild Semiconductor’s recommended circuit for the MOC3023 optical isolator, which allows a Teensy 2.0 board to safely control the AC voltage.  Four copies of this circuit were built on the board.

This circuit requires the Teensy 2.0 to know the AC voltage timing, so it can trigger the Triac at the right moment.  Triggering early in the AC waveform causes the Triac to conduct near the full AC voltage for maximum brightness.  Triggering later reduces the brightness.

To get the AC timing, I built this special power supply onto the board.

The Teensy 2.0 receives pulses on pins 5 and 6 as the AC waveform cycles positive and negative.

One caveat is this approach depends on the AC voltage being a sine wave.  The AC voltage was one of the first questions I asked Andy, and he was told Burning Man would supply a true sine wave AC voltage.  When he got out there, it turned out the power was actually a “modified sine wave”, which really isn’t anything like a sine wave.  This circuit didn’t work well.  Fortunately, they were able to run the lighting from a small generator that produced a true sine wave.

With the AC timing arriving on pins 5 and 6, and 4 pins able to trigger Triacs, and 3 pins connected to analog voltages for changing speed, brightness and pattern, the only other major piece of this technology puzzle is the software.

In this code, loop() tracks the changes in the waveform on pins 5 & 6, and it fires the Triacs at their programmed times.  120 times per second (each AC half cycle), the recompute_levels() function runs, which reads the analog controls and changes the Triac time targets, which loop() uses to actually control the voltage outputs.

Here’s all the code:

void setup()
{
	pinMode(0, INPUT_PULLUP);	// unused
	pinMode(1, INPUT_PULLUP);	// unused
	pinMode(2, INPUT_PULLUP);	// unused
	pinMode(3, INPUT_PULLUP);	// unused
	pinMode(4, INPUT_PULLUP);	// unused
	pinMode(5, INPUT);		// Phase A
	pinMode(6, INPUT);		// Phase B
	pinMode(7, INPUT_PULLUP);	// unused
	pinMode(8, INPUT_PULLUP);	// unused
	pinMode(9, INPUT_PULLUP);	// unused
	pinMode(10, INPUT_PULLUP);	// unused
	digitalWrite(11, LOW);
	pinMode(11, OUTPUT);		// LED
	digitalWrite(12, HIGH);
	pinMode(12, OUTPUT);		// trigger4, low=trigger
	digitalWrite(13, HIGH);
	pinMode(13, OUTPUT);		// trigger3, low=trigger
	digitalWrite(14, HIGH);
	pinMode(14, OUTPUT);		// trigger2, low=trigger
	digitalWrite(15, HIGH);
	pinMode(15, OUTPUT);		// trigger1, low=trigger
	pinMode(16, INPUT_PULLUP);	// unused
	pinMode(17, INPUT_PULLUP);	// unused
	pinMode(18, INPUT_PULLUP);	// unused
	analogRead(19);			// pot #3
	analogRead(20);			// pot #2
	analogRead(21);			// pot #1
	pinMode(22, INPUT_PULLUP);	// unused
	pinMode(23, INPUT_PULLUP);	// unused
	pinMode(24, INPUT_PULLUP);	// unused
}


uint8_t pot1=0, pot2=0, pot3=0;
uint8_t level1=100, level2=128, level3=0, level4=250;


uint8_t phase_to_level(uint16_t phase)
{
	uint16_t amplitude;

	// 10923 = 32768 / 3
	//     0 to 10922 = increasing: 0 -> 32767
	// 10923 to 21845 = decreasing: 32767 -> 0
	// 21846 to 32768 = increasing: 0 -> 32767
	// 32769 to 43691 = decreasing: 32767 -> 0
	// 43692 to 65535 = resting: 0

	if (phase < 10923) {
		amplitude = phase * 3;
	} else if (phase < 21845) {
		phase = phase - 10923;
		phase = 10922 - phase;
		amplitude = phase * 3;
	} else if (phase < 32768) {
		phase = phase - 21846;
		amplitude = phase * 3;
	} else if (phase < 43691) {
		phase = phase - 32769;
		phase = 10922 - phase;
		amplitude = phase * 3;
	} else {
		amplitude = 0;
	}
	//amplitude = (phase < 32768) ? phase : 65535 - phase;
	amplitude >>= 6;  // range 0 to 511
	amplitude *= (pot2 + 84) / 6;  //
	amplitude += 6000 + pot2 * 8; // minimum brightness
	return (amplitude < 32768) ? amplitude >> 7 : 255;
}

void recompute_levels()
{
	static uint16_t phase=0;
	static uint8_t n=0;

	analog_update();
	//Serial.print("pot: ");
	//Serial.print(pot1);
	//Serial.print(", ");
	//Serial.print(pot2);
	//Serial.print(", ");
	//Serial.print(pot3);
	phase += (((uint16_t)pot1 * 83) >> 5) + 170;
	//Serial.print(", phase: ");
	//Serial.print(phase);
	if (pot3 < 128) {
		level1 = phase_to_level(phase);
		level2 = level1;
		level3 = phase_to_level(phase + pot3 * 52);
		level4 = level3;
	} else {
		uint16_t n = (pot3 - 127) * 26;
		level1 = phase_to_level(phase);
		level2 = phase_to_level(phase + 6604 - n);
		level3 = phase_to_level(phase + 6604);
		level4 = phase_to_level(phase + 6604 + n);
	}
	//Serial.print(", levels: ");
	//Serial.print(level1);
	//Serial.print(", ");
	//Serial.print(level2);
	//Serial.print(", ");
	//Serial.print(level3);
	//Serial.print(", ");
	//Serial.print(level4);
	//Serial.println();
}


void loop()
{
	uint8_t a, b, prev_a=0, prev_b=0, state=255, triggered=0;
	uint32_t usec, abegin, bbegin, alen, blen;
	uint16_t atrig1, atrig2, atrig3, atrig4;
	uint16_t btrig1, btrig2, btrig3, btrig4;
	bool any;

	while (1) {
		// read the phase voltage and keep track of AC waveform timing
		a = digitalRead(5);
		b = digitalRead(6);
		if (a && !prev_a) {
			// begin phase A
			usec = micros();
			if (state == 0) {
				state = 1;
				abegin = usec;
				triggered = 0;
				Serial.print("A");
				Serial.println(usec);
			} else if (state == 255) {
				state = 11;
				abegin = usec;
			} else {
				state = 255;
			}
		}
		if (!a && prev_a) {
			// end phase A
			usec = micros();
			if (state == 1) {
				state = 2;
				alen = usec - abegin;
				Serial.print("a");
				Serial.print(usec);
				Serial.print(",");
				Serial.println(alen);
				if (alen < 12000) {
					// compute trigger offsets for next A phase
					recompute_levels();
					atrig1 = level1 ? ((256 - level1) * alen) >> 8 : 30000;
					atrig2 = level2 ? ((256 - level2) * alen) >> 8 : 30000;
					atrig3 = level3 ? ((256 - level3) * alen) >> 8 : 30000;
					atrig4 = level4 ? ((256 - level4) * alen) >> 8 : 30000;
				} else {
					state = 255;
				}
			} else if (state == 11) {
				state = 12;
				alen = usec - abegin;
			} else {
				state = 255;
			}
		}
		if (b && !prev_b) {
			// begin phase B
			usec = micros();
			if (state == 2) {
				state = 3;
				bbegin = usec;
				triggered = 0;
				Serial.print("B");
				Serial.println(usec);
			} else if (state == 12) {
				state = 13;
				bbegin = usec;
			} else {
				state = 255;
			}
		}
		if (!b && prev_b) {
			// end phase B
			usec = micros();
			if (state == 3) {
				state = 0;
				blen = usec - bbegin;
				Serial.print("b");
				Serial.print(usec);
				Serial.print(",");
				Serial.println(blen);
				if (blen < 12000) {
					// compute trigger offsets for next B phase
					recompute_levels();
					btrig1 = level1 ? ((256 - level1) * blen) >> 8 : 30000;
					btrig2 = level2 ? ((256 - level2) * blen) >> 8 : 30000;
					btrig3 = level3 ? ((256 - level3) * blen) >> 8 : 30000;
					btrig4 = level4 ? ((256 - level4) * blen) >> 8 : 30000;
				} else {
					state = 255;
				}
			} else if (state == 13) {
				state = 0;
				blen = usec - bbegin;
			} else {
				state = 255;
			}
		}
		prev_a = a;
		prev_b = b;

		// trigger triacs at the right moments
		if (state == 1) {
			usec = micros();
			any = false;
			if (!(triggered & 1) && usec - abegin >= atrig1) {
				digitalWrite(15, LOW);
				triggered |= 1;
				any = true;
				//Serial.println("trig1(a)");
			}
			if (!(triggered & 2) && usec - abegin >= atrig2) {
				digitalWrite(14, LOW);
				triggered |= 2;
				any = true;
				//Serial.println("trig2(a)");
			}
			if (!(triggered & 4) && usec - abegin >= atrig3) {
				digitalWrite(13, LOW);
				triggered |= 4;
				any = true;
				//Serial.println("trig3(a)");
			}
			if (!(triggered & 8) && usec - abegin >= atrig4) {
				digitalWrite(12, LOW);
				triggered |= 8;
				any = true;
				//Serial.println("trig4(a)");
			}
			if (any) {
				delayMicroseconds(25);
				digitalWrite(15, HIGH);
				digitalWrite(14, HIGH);
				digitalWrite(13, HIGH);
				digitalWrite(12, HIGH);
			}
		} else if (state == 3) {
			usec = micros();
			any = false;
			if (!(triggered & 1) && usec - bbegin >= btrig1) {
				digitalWrite(15, LOW);
				triggered |= 1;
				any = true;
				//Serial.println("trig1(b)");
			}
			if (!(triggered & 2) && usec - bbegin >= btrig2) {
				digitalWrite(14, LOW);
				triggered |= 2;
				any = true;
				//Serial.println("trig2(b)");
			}
			if (!(triggered & 4) && usec - bbegin >= btrig3) {
				digitalWrite(13, LOW);
				triggered |= 4;
				any = true;
				//Serial.println("trig3(b)");
			}
			if (!(triggered & 8) && usec - bbegin >= btrig4) {
				digitalWrite(12, LOW);
				triggered |= 8;
				any = true;
				//Serial.println("trig4(b)");
			}
			if (any) {
				delayMicroseconds(25);
				digitalWrite(15, HIGH);
				digitalWrite(14, HIGH);
				digitalWrite(13, HIGH);
				digitalWrite(12, HIGH);
			}
		}
	}
}



#define ADMUX_POT1  0x60
#define ADMUX_POT2  0x61
#define ADMUX_POT3  0x64
void analog_update()
{
	static uint8_t count=0;

	switch (count) {
	  case 0: // start conversion on pot #1
		ADMUX = ADMUX_POT1;
		ADCSRA |= (1<<ADSC);
		count = 1;
		return;
	  case 1: // read conversion on pot #1
		if (ADCSRA & (1<<ADSC)) return;
		pot1 = ADCH;
		ADMUX = ADMUX_POT2;
		count = 2;
		return;

	  case 2: // start conversion on pot #2
		ADMUX = ADMUX_POT2;
		ADCSRA |= (1<<ADSC);
		count = 3;
		return;
	  case 3: // read conversion on pot #2
		if (ADCSRA & (1<<ADSC)) return;
		pot2 = ADCH;
		ADMUX = ADMUX_POT3;
		count = 4;
		return;

	  case 4: // start conversion on pot #3
		ADMUX = ADMUX_POT3;
		ADCSRA |= (1<<ADSC);
		count = 5;
		return;
	  case 5: // read conversion on pot #3
		if (ADCSRA & (1<<ADSC)) return;
		pot3 = ADCH;
		ADMUX = ADMUX_POT1;
		count = 0;
		return;
	  default:
		count = 0;
	}
}

 

This article was originally published on the DorkbotPDX website, on September 3, 2014.  In late 2018, DorkbotPDX removed its blog section.  An archive of the original article is still available on the Internet Archive.  I am republishing this article here, in the hope it may continue to be found and used by anyone interested in the Embrace art installation or any other project needing sequenced AC light dimming effects.

 

These comments where written on the old site:

 

From Brandon:

Once we determined that the AC source was modified sine, I knew there wasn’t anything I could do to help the situation easily in the middle of the desert 🙂
As someone who looked over the board and what not, nice job! Worked really well and looked amazing in operation (on the right power source).

Rev two, perhaps rectified DC drive of the incandescent lights to avoid the modified sine issue? So many folks use those types of inverters to cut costs on big artwork solar installations.

Cheers and thanks for contributing!

 

From Anonymous:

The embrace structure was prettty cool, I got a chance to explore it at Burninman this year. Wish I got to see it burn down, the videos looked amazing. I assume you guys removed the heart materials before that happened.

 

Better SPI Bus Design in 3 Steps

Most Arduino SPI tutorials show this simple but poor SPI bus design:

Better SPI bus design can prevent conflicts.  3 simple improvements are needed:

  1. Use pullup resistors on all chip select signals.

  2. Verify tri-state behavior on MISO: use a tri-state buffer chip if necessary.

  3. Protect bus access with SPI.beginTransaction(settings) and SPI.endTransaction().

Step 1: Pullup Resistors for Chip Select & Reset Signals

When multiple SPI devices are used, and especially when each is supported by its own library, pullup resistors are needed on the chip select pins.

Without a pullup resistor, the second device can “hear” and respond to the communication taking place on the first device, if that second device’s chip select pin is not pulled up.  This is easy to understand in hindsight, but it can be temendously confusing and frustrating to novice Arduino users who purchase shields or breakout boards without pullup resistors.  Each SPI device works when used alone, but they sometimes mysteriously fail when used together, only because both devices are hearing communication meant to initialize only the first device!

A simpe workaround for devices without pullup resistor involves adding code at the beginning of setup.

    void setup() {
      pinMode(4, OUTPUT);
      digitalWrite(4, HIGH);
      pinMode(10, OUTPUT);
      digitalWrite(10, HIGH);
      delay(1);
      // now it's safe to use SD.begin(4) and Ethernet.begin()
    }

Step 2: Proper MISO Tri-State Behavior

Most SPI chips will tri-state (effectively disconnect) their MISO pin when their chip select signal is high (inactive).

However, some chips do not have proper MISO tri-state behavior.  Fortunately, checking MISO tri-state is easy, especially when prototyping on a breadboard.  Just connect two 10K resistors to the MISO line, like this:

When all SPI chips are disabled, the MISO signal should “float” to approximately half the Vcc voltage.  If any device is still driving the MISO line, you’ll see a logic high (usually close to 3.3V or 5.0V) or logic low (close to zero volts).  This test is so easy, it should always be performed by designers of Arduino compatible products.

Arduino shields and breakout boards with poorly-behaved chips should always include a tri-state buffer.  Adafruit’s CC3000 breakout board is a good example:

Step 3: USB SPI Transactions in Software

Newer versions of Arduino’s SPI library support transactions.  Transactions give you 2 benefits:

  • Your SPI settings are used, even if other devices use different settings
  • Your device gains exclusive use of the SPI bus.  Others will not disturb you.

These improvements solve software conflicts, allowing multiple SPI devices to properly share the SPI bus.

A typical use of transactions looks like this:

    SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE0));
    digitalWrite(chipSelectPin, LOW);
    SPI.transfer(mybyte1);
    SPI.transfer(mybyte2);
    digitalWrite(chipSelectPin, HIGH);
    SPI.endTransaction();

SPI.beginTransaction() takes a special SPISettings variable, which give the maximum clock speed, the data order, and clock polarity mode.  The speed is give as an ordinary number, expressing the maximum clock speed that device can use.  The SPI library will automatically select the fastest clock available which is equal or less than your number.  This allows your code to always use the best speed, even on board with different clock speeds.

If your code will ever call SPI library functions from within an interrupt (eg, from attachInterrupt), you must call SPI.usingInterrupt().  For example:

    SPI.begin();
    SPI.usingInterrupt(digitalPinToInterrupt(mypin));
    attachInterrupt(digitalPinToInterrupt(mypin), myFunction, LOW);

If you are developing a library that must be compatible with older versions of Arduino, which lack these SPI transaction functions, you can use SPI_HAS_TRANSACTION to check for the new version.  For example:

    #ifdef SPI_HAS_TRANSACTION
    SPI.beginTransaction(SPISettings(2000000, LSBFIRST, SPI_MODE1));
    #endif

Please Share and Use This Information

Many SPI-based products for Arduino do not work well together.  My hope is this information can help all makers of Arduino compatible devices to achieve much better compatibility.

Long-term, sharing of knowledge is needed.  Please share this information and ask makers of SPI devices and libraries to consider these suggestions.

This article may be shared and copied under the terms of the Creative Commons Attribution 4.0 International License.  Please, copy & share!  🙂

 

This article was originally published on the DorkbotPDX website, on November 24, 2014.  In late 2018, DorkbotPDX removed its blog section.  An archive of the original article is still available on the Internet Archive.  I am republishing this article here, in the hope it may continue to be found and used by everyone using SPI chips, and especially companies making SPI-based products for the Arduino community.

 

 

 

Control Voltage to 1.2V Analog Input Pin

Often the question is asked, what is the simplest way to get modular synth control voltage (CV) into an analog input pin?

This simple circuit using only 3 resistors and 1 capacitor converts the -5V to +5V CV signal range to the 0 to 1.2V ADC input range.

For Teensy 3.2, 3.5 and 3.6, you would use analogReference(INTERNAL) to configure for the 0 to 1.2V range.  Then you can read the CV signal at any time with analogRead().

While this circuit is the simplest and easiest way, it is not necessarily the best way.  The input impedance, or load placed on the CV signal, it 31K ohms, rather than the standard 100K impedance normally used for modular synthesis systems.  This forum discussion goes into the circuit’s limitations and suggests more advanced ways using opamps.

 

Originally this article was published on the DorkbotPDX website.  Since that time, the DorkbotPDX blog section has vanished.  I’m reposting it here, to preserve this info.  A copy of the original can also be found at the internet archive.

Light Table For Web White Background Photos

Years ago, in my slow quest for better photography of electronic projects, I built a light table to eliminate shadows.  Most of the white background photos you see on the PJRC site are shot with this light table.

This is how it turned out.

The build used boards from OSH Park (then “Laen’s PCB group order”), materials from TAP Plastics, 15 white LEDs and parts I mostly had laying around.

This view is inside a cheap 2 foot sized light tent I purchased from some ebay vendor, and a couple bright lights outside the tent on both side.

The LEDs are Cree CLM3C-WKW-CWBYA453, which are supposedly the same 5500K color temperature as the CFL lights outside the tent. Maybe that matters, maybe not, but it seemed like a good idea.

The Cree LED is a surface mount part, but fortunately Lean’s PCB group order made it very easy to convert to something I can solder wires onto. All the PCBs mount with double sticky mounting tape.

As you can see in this LED photo, there’s a bit of shadow. It’s a soft shadow due to the light tent casting light from many directions, but it’s still very present.  This is the type of shadow I’m hoping just a little bit of under side lighting will eliminate.

This little board is a constant current regulator. It takes a 0 to 5 volt input and regulates a 0 to 20 mA output current to a string of 5 LEDs. I wanted to make sure the current was perfectly constant since the camera might choose a quick shutter time.

Here’s the schematic for that circuit. At the time, it seemed like a good idea to sense the current using a resistor between the NPN transistor’s collector and the LEDs. The idea was any small change in ground potential between the board 0-5V control signal wouldn’t matter, if I ran separate signal and power ground lines.

But I didn’t consider the current draw though those resistors around the upper opamp. As you can see in the schematic, I change the values to about as high as I reasonably could. It still have a tiny bit of the lowest part of the range where the LEDs won’t completely turn off.

The 0 to 5 volt signal just comes from this potentiometer on the front panel.  Because it’s driving only the inputs to opamps, it doesn’t have any substantial load.  I still used a 1K pot anyways, though a higher value would have consumed a little less current.  I guess I didn’t care about an extra 5 mA.

The power for everything comes from this simple little power supply, which is just (approx) 24 volts unregulated, and a 5 volt regulated output from the pot, which is from a 7805 regulator.  Simple.

Of course, the opamp circuit isn’t perfect. After putting this together, I decided to try a different approach, sensing on the emitter side, and no current sensing path to add to the LED current! I also included 4.7K resistors on the feedback looks, and the positive inputs see about a 1K impedance. Any errors from the opamp’s input (PNP) bias currents should be small, and should be more on the negative than positive, so hopefully any tiny error will tend to reduce the LED current, not increase.

Then again, the original boards might work out ok, but Laen’s PCB group order makes it so very easy to just quickly draw up a (small) board.  Because the cost is so low, it’s easy to just send it off without all the worry the goes into a normal board order.

I still haven’t actually put this thing to use… the top plastic cover turned out to be just a bit too small, so I need to go shave it down to size on my table saw (which currently has a bunch of other project stuff piled on top of it). But soon, with a little luck, I’ll be able to take pictures of electronic stuff and adjust the light table to null out or at least soften away most of the little shadows that I still get, even with the light tent.

 

Originally this article was published on the DorkbotPDX website.  Since that time, the DorkbotPDX blog section has vanished.  I’m reposting it here, to preserve this info.  A copy of the original can also be found at the internet archive.

Measuring Microamps & Milliamps at 3 MHz Bandwidth

Some time ago, I needed to actually “see” a current waveform in the 100 uA to 5 mA range with at least a couple MHz bandwidth.  This extremely expensive probe would have been perfect, but instead I built something similar for about $30 using the amazing Analog Devices AD8428 amplifier.

The first step was cutting the power trace and adding a resistor.  I used two 1 ohm resistors in parallel.

At 5 mA, this makes only 2.5 mV.  My scope’s supposed resolution is 1 mV, but the truth is there’s plenty of noise down in the 1 mV range.  That’s pretty common for most scopes, even pretty spendy ones.  So it’s just not feasible to measure this signal directly (not to mention using 2 probes and subtracting them in the scope).

That incredibly expensive Agilent probe probably has a couple really nice amplifiers inside…. so I went searching for an amplifier.  After a bit of searching, I found the AD8428.  It has a fixed gain of 2000 and a bandwidth of 3.5 MHz.  That’s a gain-bandwidth product of 7 GHz !!!  It’s also an extremely well matched instrumentation amp with an amazing CMRR of 140 dB.  So it gets rid of the power supply voltage and outputs the amplified signal referenced to ground.

The AD8428 is perfect.  It’s so very easy!  Of course, such amazing performance costs money: about $20.  Here’s that expensive little amplifier, and a 5V to +/- 15V power supply (about $10) to power it.

The one trick with measuring such tiny voltages is twisting the 2 sense wires together.  Honestly, I didn’t try it running them separately, but since this thing is getting voltages in only the microvoltage range for the lower measurements, I didn’t want to risk picking up noise.  I also put a 100 ohm resistor on the output, just in case I accidentally short the output or do something clumsy that might blow that little $20 part.

Here’s a scope screenshot using this little amplifier to “see” the current (the blue waveform).

In this test, the microcontroller is running in its slowest mode at only 10 kHz, drawing about 120 uA.  Then when the chip’s internal oscillator is started, the current jumps to about 600 uA.  Later, the CPU switches to actually clocking from that oscillator.  There’s an on-chip clock divider which is switched in and increased gradually.

The bottom trace (red) is the voltage on the chip’s 1.8V linear regulator.  It turns out that sudden jumps in current cause pretty substantial downward spikes on the regulated voltage.  This more gradual startup approach really helps.  This sort of thing is impossible to see with a slow multimeter, but with a reasonably good bandwidth measurement of the current, it’s easy to see what various code actually does to the current.

I tried connecting my multimeter to the amp output.  Sometimes it’s just a lot more convenient to look at a single number on a meter than fiddle with the scope.  I had been using the current mode on the meter before building this.  One thing I was surprised to find it my little meter updates its screen much faster while measuring about 125 mA than it does when measuring 125 uA.

Another interesting thing I’ve been noticing is patterns within the blue current waveform.  This Agilent scope has a “digital phosphor” rendering of the huge amount of data it collected.  This static screenshot can’t really capture the interactive experience of adjusting the waveform intensity, where various regions within the data change brightness differently, indicating there’s something interesting/different going on.  Even so, you can see several areas in the screenshot where interesting things are happening once the CPU is up and running.  It’s interesting how the current waveform changes as different code executes.

I know this isn’t anything terribly impressive… basically just buy a high-end amplifier and use it with a series resistor.  Maybe it even reads like an Analog Devices ad?  I’m not affiliated with Analog Devices… I just bought this part at normal qty 1 pricing from Digikey.

Still, this is the first time I’ve ever really looked at such low microcontroller currents with a few MHz bandwidth, and I’m guessing not many people have ever bothered to really measure such currents, so I thought I’d share.

 

Years ago I originally posted this article to the DorkbotPDX site.  Hackday published an article about it at the time.  Since then, the DorkbotPDX blog section has vanished.  I’m reposting it here, to preserve the info… for the next time I or others might need to do this sort of current measurement on a budget.

 

 

 

 

 

 

Monolith Synth

Ben Davis, Darcy Neal and Ross Fish collaborated with Paul in early 2017 this Monolith Synth.  It was shown at Tested and the San Mateo Maker Faire.

The Monolith Synth is interactive sound sculpture, with 40 “arcade” buttons on the front as a percussive step sequencer and 2 touch-sensitive side panels for direct performance.

Typical usage scene at Maker Faire, with kids and adults playing with it non-stop all weekend long.

This crazy adventure started with Kickstarter reached out to me, only 6 weeks before Maker Faire, looking to showcase 4 successful projects in their booth. They wanted to show “creative tools” and how people used them. So I reached out to a few synthesizer folks I’ve met and who’ve used Teensy. They also suggested bringing it to Tested to make a video. So it began…

From the beginning I had a step sequencer using illuminated arcade buttons in mind. So I quickly designed this little I/O expander board and sent it off to OSH Park’s Super-Swift service.

The whole project came together over just 4 weeks. Our first meetup was just to discuss what to build, followed a week later by our first build night. By then the I/O expander boards had arrived. We made not the final Monolith, but 3 breadboard prototypes, so the software development side could begin!

Another meetup focused only on software. Almost all the software was developed on these prototype panels.

In this picture you can also see the panel layout sketches on the notepad on the right side, and a blue tape model underneath on the table, which we made to get an idea of the overall size.

Ross and Darcy had synthesis plans that needed a signal-controlled PWM waveform and improvements to the envelope feature, so I worked on improvements to the Teensy Audio Library while they wrote the Arduino sketch code.

The day before our next meetup, I started turning those sketches into a design for the laser cutting. I made this 1/4 scale model of the front and side pieces. At this point, none of the back side or interior ribs (for strength) had been designed, and you can see the model lacks the many holes for screws & brackets which joined everything.

Only 2 weeks before Maker Faire we had an epic 13-hour build day where all the final parts were laser cut and assembled. Here’s a photo of Darcy & Ben putting the panels together on my kitchen counter!

All the clear acrylic plastic parts were completely drawn, with all mounting holes, and made that day.

Here’s the complete layout of all parts (mk2017_design):

Here’s a large high-res copy of this image, and a big ZIP file with all the original Corel Draw files for anyone who wishes to try making their own.

While the laser did most fabrication work, other steps like countersinking for the potentiometers were needed. It was indeed an epic 13 hour day of making.

A couple days later, I spent a whole day completing the wiring we couldn’t get done in those 13 hours. Erin Murphy (the “Soldering Goddess” at PJRC) put in a few hours on aesthetic improvements to the messy tangle of wires from so many buttons.

Just a few days later we had our last “build” session, to get the 3 separately written Arduino sketches merged and working together as one integrated project. Even though everything has been designed to go together, this session went very late. Ben did much of the heavy lifting to merge the 3 programs.

This is the final audio DSP system settled upon that late night.

Here’s a large high-res copy of this image.

This was the first actual usage of the Monolith, well past 1am when we finally had it all up and running.

The next day I took it all apart and packed all the pieces and spare parts into these 2 big boxes, weighing in at 55 and 40 pounds!

This is the first time I’ve ever shipped a project to Maker Faire, rather than driving a truck or hauling cases of checked baggage on a plane. So much easier, and it allowed time to work on a nice handout card. After some back and forth with the others and last-minute proof reading by Robin, who caught what would have been embarrassing typos and grammatical errors, we sent this card off to be fast-turn printed.

Here is a printable PDF file for the front side.

Here is a printable PDF file for the back side.

Darcy and I flew to San Francisco early and spent the day with Tested, putting it back together while they shot that awesome video. Sometime I hope to have even 1/10th that sort of video production skill.

Since it was already put together, we had little to do setup-wise. Friday morning Ben, Ross and Darcy did some adjustments of the sound levels which really made it come to life in the space. For anyone who wishes to dig deeper into the technical details, the complete source code is available on Github.

All weekend long people really enjoyed playing with it. There were many really awesome moments, like this young & determined girl playing with the front panel (Facebook link).

The look on this kid's face just made my day! Maker Faire is this girl's jam. Kickstarter + #teensy

Posted by Clarissa Redwine on Saturday, May 20, 2017

Here is Kickstarter’s coverage of the event. Scroll down a bit to the part about Teensy. 🙂

During the 3 days of Maker Faire, things went very well. We did experience a couple minor issues. Massive electrical noise from so many other projects played havoc with the capacitive touch sensing. Saturday evening I rewrote the code to look for changes from an average rather than just an increase from a threshold, which allowed it to usually work well enough. The other tech issue was a bass. When turned up louder, the bass notes would shake all the plastic panels, rattling screws and even some of the connectors loose at time. Easy to fix.

Towards the end of Sunday, the Maker Faire folks came around and gave up an award. At first I shrugged it off, since they’ve done the same for other stuff I’ve brought in prior years. But those were the blue ribbons. Apparently the only hand out one of these red one each in “zone”. They said it’s a big deal…

Really, the best thing about this year was working with a great team. Ross, Darcy and Ben really stepped up and did a great job on so many parts.

 

Shortly after Maker Faire 2017, this article was posted to the DorkbotPDX website.  Since that time, the DorkbotPDX blog section has vanished.  We’re reposting it here, to preserve this project’s history.  A copy of the original can also be found at the internet archive.

On 2018, the OctoWS2811 LED board was added to the Monolith Synth project and shown at the Teardown 2018 conference.

The Monolith Synth will make its next public appearance at Portland’s Winter Lights Festival in February 2019.