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?)
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.
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.
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() { }
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
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)