Electret Microphone with LM358 gives weird analog reads combined with WS2812

Hi All,

I hooked up an electret microphone with an opamp circuit (the one used on the Esplora board) with an AtTiny85 and a WS2812 led string (http://www.adafruit.com/products/1461). See the circuit below:
http://tinyurl.com/olgqwxb
AtTiny85 + Microphone OpAmp + WS2812 strip.

What happens is that when plugging in the arduino, everything works fine, I get nice clean reads from the sensor by itself, ranging from 0-~700. When hooking up about 20 leds to the circuit, everything still works perfect. When the sensor is triggered to its max once though, it get stuck giving a high signal (around 650) and never gets out of there until the power is taken off or the signal pin and ground are shorted for about 5 seconds.

The coding part is simply an analogRead, combined with an example code of the WS2812 strip, mapping the input into a brightness level for all pixels. So when the volume signal goes high, brightness is full on. And the problem is that it never goes down anymore after that. It’s not a code thing since it does work when I split up the supply to the strip (USB) and the sensor + AtTiny85(external supply 5V/1A DC)
When powering the same circuit with an Uno I still have the same problem.
The strange part is that everything is adding up to ~390mA, which is all well within the limits of both the USB port and all the power supplies I tried out with.

I tried a pull down resistor between signal and ground, ~100nF caps between powerlines, etc, nothing seems to work… The only thing I could think of now is that the distance between the sensor power lines and strip power lines is too short (about 5cm). Right now the strip is closest to the supply, then a few cm of wire run from there to the AtTiny and sensor.

Any suggestions?

Yeah, forget about getting any help on this until you post a photo of your circuit, a schematic that shows anything that is not in the schematic you linked of the op amp circuit and your code. At this point it is anyone's guess as to whether or not it is hardware or software. We need the EXACT sketch that your running when this occurs, and a schematic of the entire setup when this occurs. FYI. - Try isolating the problem by separating the led power from the ATtiny power. Also separate the code . Run just the op amp code with everything else commented out. Take voltage measurements to see if you can find anything strange.

You need to learn more about Op Amps and how to use them.

http://www.ti.com/lit/an/slod006b/slod006b.pdf

forget about getting any help on this until you post a photo of your circuit, a schematic that shows anything that is not in the schematic you linked of the op amp circuit and your code.

Thanks for the replies, here’s some more info

Yeah, forget about getting any help on this until you post a photo of your circuit, a schematic that shows anything that is not in the schematic you linked of the op amp circuit and your code.

There’s nothing else in the schematic, this is it. Here’s a picture and the code;

#include <Adafruit_NeoPixel.h>
#define analogIn 1
#define PIN 4
#define NUM_LEDS 19


uint8_t input=0;
uint8_t oldInput=0;
uint8_t decrease=2;
float increaseFact=1.2;

uint8_t colors[3];
uint8_t brightness;


uint8_t lastRead=90;

uint16_t lowCol=20;
uint16_t highCol=70;
uint16_t mainCol=45;

uint16_t maxInput=500;


// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);


void setup() {
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'



}



void loop() {

  wavey(lowCol,highCol,0.011,1); // color effect, with speed and color limits

}


void readSens(){
  if (millis()>lastRead+50){
    input = analogRead(analogIn); 

    if (input>maxInput){
      maxInput=input;
    }

    input=map(input,0,maxInput,10,255);
    // Serial.println(input);
  }
}

void updateBrightness(){  // call the sensor function + translate sens value into a brightness level
  readSens();

  if (input>oldInput && oldInput<(254/increaseFact)){
    oldInput=oldInput*increaseFact+1;
    if (oldInput>254){
      oldInput=254;
    }
  }
  else{
    oldInput=oldInput*.98;
  }
  brightness=oldInput;
  //Serial.println(oldInput);

}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {

    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}



void wavey(int low,int high,float rt,uint8_t wait) {
  float in,out;
  int diff=high-low;
  int step = diff/strip.numPixels();
  for (in = 0; in < 6.283; in = in + rt) {
    for(int i=0; i< strip.numPixels(); i++) {
      out=sin(in+i*(6.283/strip.numPixels())) * diff + low;
      HSVtoRGB(out,255,255,colors);
      strip.setPixelColor(i,(colors[0]*brightness)/255,(colors[1]*brightness)/255,(colors[2]*brightness)/255);
    }
    updateBrightness();
    strip.show();
    delay(wait);

  }
}

void HSVtoRGB(int hue, int sat, int val, uint8_t * colors) {
  int r, g, b, base;
  if (sat == 0) { // Achromatic color (gray).
    colors[0] = val;
    colors[1] = val;
    colors[2] = val;
  } 
  else {
    base = ((255 - sat) * val) >> 8;
    switch (hue / 60) {
    case 0:
      colors[0] = val;
      colors[1] = (((val - base) * hue) / 60) + base;
      colors[2] = base;
      break;
    case 1:
      colors[0] = (((val - base) * (60 - (hue % 60))) / 60) + base;
      colors[1] = val;
      colors[2] = base;
      break;
    case 2:
      colors[0] = base;
      colors[1] = val;
      colors[2] = (((val - base) * (hue % 60)) / 60) + base;
      break;
    case 3:
      colors[0] = base;
      colors[1] = (((val - base) * (60 - (hue % 60))) / 60) + base;
      colors[2] = val;
      break;
    case 4:
      colors[0] = (((val - base) * (hue % 60)) / 60) + base;
      colors[1] = base;
      colors[2] = val;
      break;
    case 5:
      colors[0] = val;
      colors[1] = base;
      colors[2] = (((val - base) * (60 - (hue % 60))) / 60) + base;
      break;
    }

  }
}

FYI. - Try isolating the problem by separating the led power from the ATtiny power. Also separate the code . Run just the op amp

Like I said, isolating the opamp-sensor + ATtiny part from the LEDS (also running them on an Uno) works perfect, no problem at all, but I would them to work together and that doesnt work on any of them.

Take voltage measurements to see if you can find anything strange.

Voltage readings while measuring are showing the same as the analog reads; low signal (<.5V) when there is no volume, ~3V at loud sounds, and then the signal voltage stays there, even without sound input. Input voltage stays stable at 5.0V.

You need to learn more about Op Amps and how to use them.

http://www.ti.com/lit/an/slod006b/slod006b.pdf

I’ve tried to research as much as I could into what I need, but I got stuck at what I have right now… some more indications on what part to look at would be helpful. I’ve had my basic introduction… its been a few years though.

The OpAmp circuit is an exact copy of the Arduino Esplora volume sensor part, the only thing I changed of the esplora design (http://arduino.cc/en/uploads/Main/arduino-esplora-schematic.pdf top right corner part), the value of R1 100k resistor to a 2mOhm resistor in my setup…

I don't see anything to indicate that it is hardware related. The op amp amplifier circuit has an RC low pass filter on the output but that doesn't explain the latching behavior you describe. My guess is it is software conflict somewhere. Have you considered the possibility that you are using too much ram in the ATtiny85 ? It only has 512 bytes of internal SRAM. You might be expecting too much. Did you write the code ? Do you understand it enough to modify it so it only does a simple on/off function for the leds instead of a fancy pattern ? Can you comment out a bunch of stuff and just stick in a simple statement that turns them on so that we can verify that it is not the leds per sei , but rather the side-effect of running the led code (like filling up ram). The other possibility is that it is NOT what the code is doing , but the speed it is doing it , ergo TIMING issue. Put delays in it for debug purposes. Ignore the light pattern and just concentrate on getting it to work by slowing it down with delays. If it doesn't lock up with the delays then I think you have found your cause. Forget about what you want the system to do and focus seeing what you can do to stop it from locking up. The first place to put a delay is right after the analogRead. I know you're using millis because you don't want any delays but that isn't working for you is it ? You need to think about what is the connection between the timing and the millis() statements and the analogRead(). You may be filling up the SRAM with your use of millis(). I don't know. I'm just throwing stuff out there. The person who could tell you , if anyone could is PaulS. He's a real Guru but like all gurus, he's very particular (I don't think "picky" quite describes it) If you ask for his help , you better be prepared to follow his instructions to the letter if you don't want to get dropped like a hot potato.

That sounds like a road that could help me get a bit further, thanks! I added the sensor part to the code, the light pattern is (part) from another ATtiny & WS2812 led strip example program (https://github.com/danasf/attiny_pixel_switch/blob/master/attiny_pixel_switch.ino). Its easy enough to understand what is happening so that's not a problem. I'll try to work it out with just one color, and if that's the trick I'll simplify the general code a bit.

I'll update as soon as I've had a chance to try it out. Thanks!

Try a GO/NO GO test where you keep the led code in but do the absolute minimum with it . Record your results. Then start with adding the dreaded delays that aren't supposed to be there , starting small and working up , increasing the delay little by little. Try picking different places in the code and testing it by commenting out all but one location and add locations one by one after incrementing the delay in each location little by little.

Hi DeBende

Couple of points on the following:

uint8_t input=0;
uint8_t lastRead=90;
...
void readSens(){
  if (millis()>lastRead+50){
    input = analogRead(analogIn); 

    if (input>maxInput){
      maxInput=input;
    }

    input=map(input,0,maxInput,10,255);
    // Serial.println(input);
  }
}

analogRead() returns a value from 0 to 1023, so not sure what will happen when you assign it to input declared as uint8_t. EDIT: it will calculate the remainder of the analogRead() value when divided by 256. I.e. as per the modulus (%) operator.

lastRead is also declared as uint8_t, whereas millis() returns uint32_t. Also I can't see where lastRead is updated, so the comparison with millis() is (almost) always going to be true, I think.

Regards

Ray

Did you mean 2M Ohm? 2m Ohm is pretty small...

So you've boosted the gain by a factor of 20. To about 1000. That's a bit extreme in one stage.

Are you using an LMV358, or LM358? Not all Op Amps are that well behaved when running the inputs down to and below the negative rail.

It is also possible that it is going into oscillation after the input kicks the voltage a bit higher. Try putting a 100pF capacitor across R5 and see what happens.

ok so here’s what I’ve got now;

Simplified the code so it only puts on the LEDS in one color, with the audio input mapped to the brightness. I removed all the millis() functions and LED animations (other than brightness).

#include <Adafruit_NeoPixel.h>
#define analogIn 1
#define PIN 4
#define NUM_LEDS 19


uint16_t input=0;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);


void setup() {
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}


void loop() {
plainColor();
}

void readSens(){

    input = analogRead(analogIn); 

 //   if (input>maxInput){
 //     maxInput=input;
 //   }

    input=map(input,0,1023,10,255);
    // Serial.println(input);
    delay(50);
  
}
void plainColor(){
  readSens();
  for (int i=0;i<NUM_LEDS;i++){
  strip.setPixelColor(i,0, input, 0); // input at 'green' channel, ranging from 10-255
  }
  strip.show();
  delay(10);
}

This also gives the the glitchy signal, basically the same as before, but when I change NUM_LEDS in the for-loop into 5 or less, it works perfect. At 8 active LED’s it kind of works, but it give’s a few pulses after I make a loud noise.

@Hackscribble
Thanks! those we’re some sloppy things in my code, thanks for pointing at them!

@polymorph
Its an LM358, indeed another difference. It sounds like a logic explanation that it would go into oscillation… but then why would it only do so at higher amount of active LED’s in the circuit? The 100pF cap only made things worse… But I did lower the value of R5 to 470k, which does make it work! It has a drastic effect on the range though, but I guess I cant have it all. It’s still complete rubbish when I try to use more than just the green LED’s.

------ UPDATE -----
Also tried my original code - with changing some of the uint8 errors and limits - with a slider instead of the volume sensor, and it works perfect… thats why I am more tempted to say its a hardware thing in the opamp circuit. Is there a way to split up the two power lines (to the strip and sensor/uC) even more, by adding another component for example?.

What power source? Are you overwhelming the power source with more LEDs?

Do you have a DMM so you can monitor the 5V line?

Hi DeBende

Your photo does not show the connection from the board to the LED strip. So you may already have tried the following ...

Adafruit recommend this method of connecting to a Neopixel strip:

Before connecting NeoPixels to any power source, add a large capacitor (1000 µF, 6.3V or higher) across the + and – terminals. Place a 300 to 500 Ohm resistor between the Arduino data output pin and the input to the first NeoPixel. This resistor must be at the NeoPixel end of the wire to be effective! Some products already incorporate this resistor…if you’re not sure, add one…there’s no harm in doubling up! Try to minimize the distance between the Arduino and first pixel.

https://learn.adafruit.com/adafruit-neopixel-uberguide/best-practices

Regards

Ray

add a large capacitor (1000 µF, 6.3V or higher) across the + and – terminals.

That seems to help a lot! I did find the info about the resistor, so I have that in front of my Din signal, but I did not have any large resistors like that around. Found one now, and that, in combination with a lower amplification on the opamp works! Thanks a lot Ray!

The thing with sound, is that it is a wave, and the value you get depends on when you read it.

Just a guess, but I reckon you should be reading the microphone as an ac signal, biased to half Voltage, then when you read it the values will be probably equal either side of half your reading resolution

for example if your resolution is 1024, then the half way point would be 512, so you would get ac/sinwave values above and below this number.

to ensure you would get a good ac coupled signal, use a 1uf cap (to block the DC path) then bias the output of the cap with two equal value resistors, one going to +ve, the other to 0v