ATTiny85 // 74HC595 // pretty flashy lights… mostly working

hey,

the following code is like 99% working, but just not quite. following my arduous journey of the attiny85 and 74hc595 i finally have some software pwm, and usable control over some led bars.

It was originally written by Slugsie, and I've hacked it to death to get my wanted outcome.

what i have at the moment, is a 1k potentiometer attached to PB3 on the tiny, and if i turn the potentiometer, the led bar lights up, and fades out off the top end (as expected)

when i replace the potentiometer with an electret microphone (spark fun one with adc and boost built in) the flashing becomes unstable (it rotates around random patterns)

I've plugged the electret into a arduino nano and looked at the serial, and its outputting 0-1023 as expected.

on this page, they have most of what I'm doing with the mic and at tiny, but nothing to do with the 74hc595, and the code is beyond my arduino knowledge

https://simonetti.media.mit.edu/~jieqi/2012/01/electret-microphone-example-code/

bars with potentiometer, the flicking isn't really intentional, but i quite like it as an off product, it looks "soundy", if this is part of the issue, i can always code it back in later!

microphone attached and tapping the mic

nothing attached (flailing wire, but no change if i remove the wire completely

it looks to me like I'm getting interference somewhere and i wonder if i need to set a ground / reference point, for the analog input maybe? but i don't know how to do this? or something to do with address/port with the microphone / 74hc595 / tiny / code?

/* ***********************************
  Pseudo PWM of 8 LEDs through a
  74HC595 Bit Shift register
  By Slugsie - 19th Nov 2009
* ***********************************/

// Setup pin definitions for bit shift register
int   dataPin = 0;
int   clockPin = 3;
int   latchPin = 2;

int   micPin = A2;
int   micInput = 0;
int   micSmoothing = 0;
int   micTotal = 0;
int   micBase = 0;

// Setup array to hold brightnesses for each LED
// Start each LED off with a different brightness
int   ledBrightness[] = {4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, 0, 0, 0, 0, 0};
int   offset = 8;

// Delay time between each cycle - smaller the better to reduce flicker
int   delayTime = 0;

void setup() {
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(micPin, INPUT);

  // mic smoothing
  for (int i = 0; i < 100; i++) {
    micSmoothing += analogRead(micPin);
  }
  micBase = (micSmoothing / 100) + 3;
}

void loop() {
  //    micInput = map(analogRead(micPin), 1023, 0, 6, -15);
  micInput = map(analogRead(micPin), 1023, micBase, 6, -15);

  // Cycle through the brightness pattern BIT by BIT 256 times
  //  for (int iLoop = 0; iLoop <= 256; iLoop++) {
  for (int iLoop = 0; iLoop <= 16; iLoop++) {

    // Stores the BIT pattern for the run
    byte bitPattern = 0;
    // Cycle through each LED
    for (int loopLED = 0; loopLED <= 7; loopLED++) {
      bitPattern = bitPattern | getBit(loopLED);
      //    bitPattern = getBit(loopLED);

    }
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, bitPattern);
    digitalWrite(latchPin, HIGH);
    incrementPWMPosition();
    delay(delayTime);
  }
}

// Increment the position within the brightnessPattern[] from 0 thru 7
int pwmPosition = 0;

void incrementPWMPosition() {
  pwmPosition++;
  if (pwmPosition > 7) {
    pwmPosition = 0;
  }
}

// Pattern of BITs that we use to determine if an LED gets power this cycle or not
// The more 1 BITs in the pattern, the brighter the LED will be overall.
// The 1's are spread as much as possible in order to try and reduce flicker.
byte brightnessPattern[] = {B00000000, B00000001, B00010001, B01010101, B11111111};


// BIT mask used to look at an individual BIT in the brightnessPattern[],
// also to set the individual BIT for the LED
byte bitMask[] = {B00000001, B00000010, B00000100, B00001000,
                  B00010000, B00100000, B01000000, B10000000
                 };

byte getBit(int led) {
  if (brightnessPattern[ledBrightness[led + offset + micInput]] & bitMask[pwmPosition]) {
    return bitMask[led];
  } else {
    return 0;
  }
}

While I haven't looked too closely at your source, I can see potential for some issues...

The pot delivers a relatively 'constant' value to the input, but the microphone delivers almost arbitrary samples that vary in realtime at several kHz - unless the audio environment is silent except for some monotone source.

Also, with delay() in your code, you will be sampling that varying sound field at virtually random points- with the expected random results. You would need some method of synchronising your sampling phase to the source - which requires some additional effort.

lastchancename:
The pot delivers a relatively 'constant' value to the input, but the microphone delivers almost arbitrary samples that vary in realtime at several kHz - unless the audio environment is silent except for some monotone source.

yeah, thats the flicking i see. I've been jiggling the cables and i think i had something loose, as its stabilising out a little more now… dammit i hate breadboard.

I've bypassed the attiny85 for the moment, and put a nano in there, and using the same circuitry, i have taken a reading from the micInput with the potentiometer connected, and the mic connected;

this mic data is far too erratic.

POTENTIOMETER

micInput: 623
micInput: 621
micInput: 623
micInput: 623
micInput: 620
micInput: 622
micInput: 623
micInput: 623
micInput: 623
micInput: 623
micInput: 623
micInput: 622
micInput: 620
micInput: 621
micInput: 624
micInput: 625
MIC

micInput: 558
micInput: 425
micInput: 916
micInput: 784
micInput: 675
micInput: 807
micInput: 1023
micInput: 16
micInput: 14
micInput: 606
micInput: 595
micInput: 1023
micInput: 55
micInput: 7
micInput: 730
micInput: 634
micInput: 901
micInput: 505
micInput: 796
micInput: 976
micInput: 941
micInput: 26
micInput: 34
micInput: 3
MIC with capacitor (vcc to signal)

micInput: 423
micInput: 624
micInput: 858
micInput: 849
micInput: 538
micInput: 575
micInput: 275
micInput: 314
micInput: 320
micInput: 715
micInput: 435
micInput: 1002
micInput: 578
micInput: 533
micInput: 285
micInput: 305
micInput: 834
micInput: 426
micInput: 710
micInput: 703
micInput: 571
micInput: 494
micInput: 712
micInput: 713
micInput: 802
micInput: 645
micInput: 364
micInput: 377
micInput: 804
micInput: 998
micInput: 651
micInput: 737
micInput: 759
micInput: 691

I seriously doubt it's the breadboard.
Consider a sine wave - the simplest sound you're ever going to sense.

Your Arduino program is doing to sample that waveform randomly at different points in the curve - unless your capture code is phase locked to the signal source.

The measurements you see will range between 0 and full scale. with no obvious rhyme or reason.
The capacitor is heading in the right direction, as it creates a very simple filter.

Audio in the real world is a lot more complex than a sine wave, so your source/filtering requirements just became a lot more complex.

Ok, so I've stripped the program down, and using a nano purely to read the serial output of the mic, I can see when I plug the attiny85 and the mic I'm getting sensible readings. I add the 2 X 74hc595s, and no issues, but as soon as I plug the LEDs in the erratic data starts.

I'm driving 16 LEDs from the attiny on pwm now. I wonder if it's a simple power issue. I'll try running from an external power supply.

Let's put aside the idea of serial from the mic... it's an interesting analogy though!

it may be worth putting some decoupling caps close to the 595 chips, to minimise bounce on the power rails as the LEDs flicker.
Digital noise may also be getting into your mic input pin, so a separate ultra-clean analog supply, and aggressive filtering / shielding might help.

Hey. So I dropped the pretty fade outs from the pwm, and the mic sensor is working fine.

Now I have what I was looking for with no interesting flourish. :slight_smile:

But at least when you have something working, you can make it better! :slight_smile:

Good work.
BTW, do you have a scope?
You may find some ideas by using a spare output pin as a scope trigger source (fired just before the analogread()), and watching the analog input pin - to see what is actually being sampled...!?