Playing sound with ATTINY85 and W25Q80BV?

Hehey!
I am still very green, so bare with me. I am trying to play sound with a ATTINY85 connected to a W25Q80BV that stores the actual sound. I followed the guide at Overview | Trinket Audio Player | Adafruit Learning System and I have the chip set up and it gives me the encouraging "hello" at least.

For now It's all on a breadboard with a power supply that gives me 3.3v on one rail and 5v on the other. The ATTINY85 gets the 5v and the memory chip gets the 3.3v. I am planning on using a TL431 to regulate the power later on when it's all working, but so far it's not giving me any sound. Or anything really.

(I am using the TrinketPlayer.ino file from the Adafruit_TinyFlash library, will that work on a stand-alone ATTINY85?)

Can you post your schematic. Are you using a level shifter for the circuit.

I don't understand, first you say it works, you get the hello, then you say it dossn't which is it?

The chip is correctly connected, and gives the "hello" message, and it can be "formatted" with the audio. It's when I am trying to connect them both together I do something wrong. (I take it for granted that it's something on my part :O) ).

Not using anything yet but the ATTINY85, W25Q80BV, a 10uf, 2 x 0.1uf, a 10k pot and ... well, I didn't have a 68ohm resistor, so I chained a 47 and a 22 one. My setup is like this, but replace the Adafruit Trinket with an ATTINY85.

The picture did not come through. Unless you say what you are doing it is almost impossible to help.

Does this work then?

...And maybe a LM3940 instead of the planned TL431.

I've connected as the image shows, but with an ATTINY85 instead.

Wooohooo! It works! :OD

I was in my bed, trying to sleep. Bitter over the fact that I couldn't make it work, and because I spilled a huge cafe latte all over over my keyboard, rendering it useless, even after drying and cleaning it carefully. Suddenly it dawned on me. I remembered that when I programmed the Tiny, I set it to 16 MHz, but to work in just 3.3v, it should be set to 8 MHz instead! So after some fiddling and reprogramming, it finally works! (I am typing this with the helpful, but time consuming on-screen keyboard. Haha!)

So... a little follow up question, how would I do for putting in more than just one sound in the chip, and trigger them by buttons?

how would I do for putting in more than just one sound in the chip, and trigger them by buttons?

Should be possible.
The code ( which you have not posted ) will read this memory from a specific address, I assume that this will be zero. All you need to do is to start reading it from the address of your required sample to pick a different sound.

Hey, now I have a keyboard again, which makes it a looot easier. Ok, here goes...

I convert the sound I want to use into an uncompressed wav-file.

The library and code can be found here: https://github.com/adafruit/Adafruit_TinyFlash/archive/master.zip

I upload the following code to the Arduino, UNO in my case, which apparently somehow works together with the Processing code (below this code) to load the wav-file onto the memory chip:

// Winbond serial flash loader.  Runs on normal Arduino (e.g. Uno,
// NOT Trinket), works with 'AudioXfer' Processing code on host PC.

#include <Adafruit_TinyFlash.h>
#include <SPI.h>

#define LED A0

// Blink LED (not pin 13) to indicate flash error
void error(uint32_t d) {
  for(;;) {
    digitalWrite(LED, HIGH);
    delay(d);
    digitalWrite(LED, LOW);
    delay(d);
  }
}

void setup() {
  Adafruit_TinyFlash flash;
  uint8_t            buffer[256];
  int                c, index      = 0;
  uint32_t           capacity,
                     address       = 0,
                     bytesExpected = 0x7FFFFFFF,
                     bytesReceived = 0;

  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);

  Serial.begin(57600);
  Serial.println("HELLO");      // ACK to host

  capacity = flash.begin();
  Serial.println(capacity);     // Chip size to host
  if(!capacity) error(250);     // Fast blink

  while(!Serial.find("ERASE")); // Wait for ERASE command from host
  if(!flash.eraseChip()) {
    Serial.println("ERROR");
    error(500);                 // Slow blink
  }
  Serial.println("READY");      // ACK to host

  for(;;) {
    // Buffer data until a full page is ready or last packet arrives.
    if((c = Serial.read()) >= 0) {
      buffer[index++] = c;

      // Bytes 3-6 indicate number of samples to follow
      if(++bytesReceived == 6) {
        bytesExpected = (((uint32_t)buffer[2] << 24) |
                         ((uint32_t)buffer[3] << 16) |
                         ((uint32_t)buffer[4] <<  8) |
                                    buffer[5]      ) + 6;
      }

      if((index >= sizeof(buffer)) || (bytesReceived >= bytesExpected)) {
        if(flash.writePage(address, buffer)) {
          digitalWrite(LED, HIGH);
          Serial.print('.');
        } else {
          Serial.print('X');
        }
        address += sizeof(buffer);
        digitalWrite(LED, LOW);
        if((address >= capacity) || (bytesReceived >= bytesExpected)) break;
        index = 0;
      }
    }
  }
}

void loop() { }

I hookup the memory chip like this:

Then I use Processing and the following code to upload the sound-file to the chip: (This code is NOT for the Arduino IDE, but for the Processing IDE, which sort of looks like a black and white version of Arduino IDE)

// WAV file converter for Trinket audio project.  Works with
// 'AudioLoader' sketch on Arduino w/Winbond flash chip.
// For Processing 2.0; will not work in 1.5!

import processing.serial.*;

Serial  port = null;
int     capacity;
boolean done = false;

// Wait for line from serial port, with timeout
String readLine() {
  String s;
  int    start = millis();
  do {
    s = port.readStringUntil('\n');
  } while((s == null) && ((millis() - start) < 3000));
  return s;
}

// Extract unsigned multibyte value from byte array
int uvalue(byte b[], int offset, int len) {
  int    i, x, result = 0;
  byte[] bytes = java.util.Arrays.copyOfRange(b, offset, offset + len);
  for(i=0; i<len; i++) {
    x = bytes[i];
    if(x < 0) x += 256;
    result += x << (i * 8);
  }
  return result;
}

void setup() {
  String s;

  size(200, 200); // Serial freaks out without a window :/

  // Locate Arduino running AudioLoader sketch.
  // Try each serial port, checking for ACK after opening.
  println("Scanning serial ports...");
  for(String portname : Serial.list()) {
    try {
      // portname = "/dev/tty.usbmodem1a1331"; // bypass scan
      port = new Serial(this, portname, 57600);
    } catch (Exception e) { // Port in use, etc.
      continue;
    }
    print("Trying port " + portname + "...");
    if(((s = readLine()) != null) && s.contains("HELLO")) {
      println("OK");
      break;
    } else {
      println();
      port.stop();
      port = null;
    }
  }

  if(port != null) { // Find one?
    if(((s        = readLine())                != null)
    && ((capacity = Integer.parseInt(s.trim())) > 0)) {
      println("Found Arduino w/" + capacity + " byte flash chip.");
      selectInput("Select a file to process:", "fileSelected");
    } else {
      println("Arduino failed to initialize flash memory.");
      done = true;
    }
  } else {
    println("Could not find connected Arduino running AudioLoader sketch.");
    done = true;
  }
}

void fileSelected(File f) {
  if(f == null) {
    println("Cancel selected or window closed.");
  } else {
    println("Selected file: " + f.getAbsolutePath());
    byte input[] = loadBytes(f.getAbsolutePath());

    // Check for a few 'magic words' in the file
    if(java.util.Arrays.equals(java.util.Arrays.copyOfRange(input, 0, 4 ),     "RIFF".getBytes())
    && java.util.Arrays.equals(java.util.Arrays.copyOfRange(input, 8, 16), "WAVEfmt ".getBytes())
    && (uvalue(input, 20, 2) == 1) ) {
      int chunksize  = uvalue(input, 16, 4),
          channels   = uvalue(input, 22, 2),
          rate       = uvalue(input, 24, 4),
          bitsPer    = uvalue(input, 34, 2),
          bytesPer   = uvalue(input, 32, 2),
          bytesTotal = uvalue(input, 24+chunksize, 4),
          samples    = bytesTotal / bytesPer;

      port.write("ERASE"); // Issue erase command now, process audio while it works

      println("Processing sound file...\n"
      + "  " + channels + " channel(s)\n"
      + "  " + rate     + " Hz\n" +
        "  " + bitsPer  + " bits");

      if(samples > (capacity - 6)) samples = capacity - 6;
      byte[] output = new byte[samples + 6];
      output[0] = (byte)(rate >> 8);     // Sampling rate (Hz)
      output[1] = (byte)(rate);
      output[2] = (byte)(samples >> 24); // Number of samples
      output[3] = (byte)(samples >> 16);
      output[4] = (byte)(samples >> 8);
      output[5] = (byte)(samples);

      int index_in = chunksize + 28, index_out = 6, end = samples + 6;
      int c, lo, hi, sum, div;

      // Merge channels, convert to 8-bit
      if(bitsPer == 16) div = (channels * 256);
      else              div =  channels;
      while(index_out < end) {
        sum = 0;
        for(c=0; c<channels; c++) {
          if(bitsPer == 8) {
            lo = input[index_in++];
            if(lo < 0) lo += 256;
            sum += lo;
          } else if(bitsPer == 16) {
            lo = input[index_in++];
            if(lo < 0) lo += 256;
            hi = input[index_in++];
            sum += (hi + 128) * 256 + lo;
          }
        }
        output[index_out++] = (byte)(sum / div);
      }
      print("Erasing flash..."); // Really just waiting for the erase ACK
      String s;
      while(((s = readLine()) == null) || (s.contains("READY") == false));

      print("OK\nWriting...");
      // Instead of just write(output), it's done per-byte so we
      // can echo progress dots from the Arduino to the console.
      // Eventually may want to make it re-write error sectors.
      for(int i=0; i<output.length; i++) {
        port.write(output[i]);
        if((c = port.read()) >= 0) print((char)c);
      }
      println("done!");
    } else {
      println("Invalid WAV file.");
    }
  }
  done = true;
}

void draw() {
  if(done) exit();
}

When you run it, it will open up a dialog box asking for your input file, the uncompressed wav-file, that is.

Then finally, I hookup my ATTiny85 and memory chip like this:


Replace the Trinket with an ATTiny85 and the ports on the trinket are the equivalent pins on the ATTiny85 as the image below shows:

This works just fine and loops the sound over and over again! joy

The original guide is by Adafruit, at Overview | Trinket Audio Player | Adafruit Learning System

So, what I would like to do here, is to somehow manage to upload more than one sound to the memory chip, and then be able to trigger them by buttons. This configuration gives me a spare pin to play with so I can hook up a bunch of buttons there. And, with a 0.5 watt 8 ohm speaker connected, how would I go for connecting all this to a battery pack with 4 x AA batteries? How would you setup the voltage regulation?

Any thoughts, pointers, tips and help is greatly appreciated. And please bare in mind that I am very green. Cheers! :O)

I am using the same sketch mentioned by Pepeu, using Cypress S25FL132K chip. Arduino IDE works perfect, but Processing gives me the below error:

Scanning serial ports...
Trying port COM4...
Trying port COM7...
Trying port COM8...
Could not find connected Arduino running AudioLoader sketch.

Pleas help me out with this.

Hey guys,

I would like to same project with this small controller and flash memory

  • ATtiny85V-10SHR (1,8V) and
  • flash memory N25Q032A11EF640E (1,8V)

Does somebody has experience with similar project based on 1,8V?

Thank for your reply