First, a couple of pictures:
I followed the schematic very closely, but I forgot that the speaker connections have TWO wires each, they all have a ground in addition to in/out. Also, the above has the ground from the PT2257 going to the wrong place. So this is a better picture:
This one has connections for the ground wires. |
I added the code to run this to the code for the speaker controller, so it's all in one place. Here are the parts that I added:
In the "html head" section, I added a little bit of javascript:
server.println("<script>");
server.println("function doit(){");
server.println("document.getElementById(\"vF\").submit();}");
server.println("</script>
Then in the main body of the html I added a slider to set the volume:
server.print("</div><div class='container'><h3>Master Volume</h3><form id='vF' action='/' role='form'>");
server.print("<input type='range' name='volume' min='0' max='79' value='");
server.print(volume);
server.print("' oninput='doit(value)'>");
server.print("</form>");
server.print("<form id='mF' action='/' role='form'>");
server.print("<input type='submit' name='mute' value='Mute'/></form>
To handle the form data, I added these lines to the "handleRequest" method:
if (request.indexOf("volume=") > -1) {
int start = request.indexOf("volume=") + 7; // 7 is length of "volume="
int end = request.indexOf(" ", start);
volume = request.substring(start, end).toInt();
} else {
if (request.indexOf("mute") > -1) {
volume = 0;
}
}
In the "html head" section, I added a little bit of javascript:
server.println("<script>");
server.println("function doit(){");
server.println("document.getElementById(\"vF\").submit();}");
server.println("</script>
Then in the main body of the html I added a slider to set the volume:
server.print("</div><div class='container'><h3>Master Volume</h3><form id='vF' action='/' role='form'>");
server.print("<input type='range' name='volume' min='0' max='79' value='");
server.print(volume);
server.print("' oninput='doit(value)'>");
server.print("</form>");
server.print("<form id='mF' action='/' role='form'>");
server.print("<input type='submit' name='mute' value='Mute'/></form>
To handle the form data, I added these lines to the "handleRequest" method:
if (request.indexOf("volume=") > -1) {
int start = request.indexOf("volume=") + 7; // 7 is length of "volume="
int end = request.indexOf(" ", start);
volume = request.substring(start, end).toInt();
} else {
if (request.indexOf("mute") > -1) {
volume = 0;
}
}
There are several libraries around for running the PT2257, but it's pretty simple and doesn't really need a whole library for what I want to do, so I only needed to add these few methods:
void setVolume(int level) {
byte bbbaaaa = calculateVolume(level);
byte aaaa = bbbaaaa & 0b00001111;
byte bbb = (bbbaaaa >> 4) & 0b00001111;
Wire.beginTransmission(EVC_ADDR);
Wire.write(STEP_10 | bbb);
Wire.write(STEP_1 | aaaa);
Wire.endTransmission();
}
byte calculateVolume(int level) {
if (level > 79)
level = 79;
if (level < 0)
level = 0;
level = 79 - level; // invert the volume, so 0 is low and 79 is high
uint8_t b = level / 10; // get the most significant digit (eg. 79 gets 7)
uint8_t a = level % 10; // get the least significant digit (eg. 79 gets 9)
b = b & 0b0000111; // limit the most significant digit to 3 bit (7)
return (b << 4) | a; // return both numbers in one byte (0BBBAAAA)
}
void unmute() {
byte value = MUTE_OFF;
Wire.beginTransmission(EVC_ADDR);
Wire.write(value);
byte error = Wire.endTransmission();
}
Here is the complete code. I had to remove a few thing and optimize a bit as this barely fits in the memory of the Arduino.
/*
Simple web server to serve a form to turn on or off digital pins. In this case,
the pins are connected to relays to turn speakers on or off. This uses pins
2 through 9 in pairs, so 2 and 3 control the hot tub speakers, 4 and 5 control
the dining room speakers, and 6 and 7 control the portico speakers. 8 and 9 are
available in case I ever find a need to connect one more pair of speakers.
*/
#include <Ethernet.h>
#include <SPI.h>
#include <Wire.h>
#include <string.h>
// MAC address can be anything that is unique within the local network.
byte mac[] = {0x00, 0x1E, 0x2A, 0x76, 0x24, 0x08};
// Some unused IP address on the local network.
byte ip[] = {192, 168, 2, 251};
// web server, nothing fancy, just port 80 for http
EthernetServer server(80);
#define RELAY_ON 0
#define RELAY_OFF 1
// settings for the volume controller
#define EVC_ADDR 0x44 // volume control chip address
#define STEP_1 0b11010000 // 2-Channel, -1dB/step
#define STEP_10 0b11100000 // 2-Channel, -10dB/step
#define MUTE_OFF 0b01111000 // 2-Channel MUTE off
// true, just show speaker status or false, actually change the speaker states
boolean showStatus = false;
// which zones are on or off, initially, all are off
boolean zone1 = false;
boolean zone2 = false;
boolean zone3 = false;
boolean zone4 = false;
// default volume is 25 to start
int volume = 0;
// pin definition, one pin per speaker,
// so the left speaker in zone 1 is pin 2, etc
static const int zone1L = 2;
static const int zone1R = 3;
static const int zone2L = 4;
static const int zone2R = 5;
static const int zone3L = 6;
static const int zone3R = 7;
static const int zone4L = 8;
static const int zone4R = 9;
// set up pins, initially all speakers in all zones are off
void setupPins() {
digitalWrite(zone1L, RELAY_OFF);
digitalWrite(zone1R, RELAY_OFF);
digitalWrite(zone2L, RELAY_OFF);
digitalWrite(zone2R, RELAY_OFF);
digitalWrite(zone3L, RELAY_OFF);
digitalWrite(zone3R, RELAY_OFF);
digitalWrite(zone4L, RELAY_OFF);
digitalWrite(zone4R, RELAY_OFF);
pinMode(zone1L, OUTPUT);
pinMode(zone1R, OUTPUT);
pinMode(zone2L, OUTPUT);
pinMode(zone2R, OUTPUT);
pinMode(zone3L, OUTPUT);
pinMode(zone3R, OUTPUT);
pinMode(zone4L, OUTPUT);
pinMode(zone4R, OUTPUT);
delay(2000);
}
void setup() {
Ethernet.begin(mac, ip);
server.begin();
Wire.begin(); // join i2c bus
setupPins();
//Serial.begin(115200);
}
// set up buffer for reading web requests
static const int bufferMax = 128;
int bufferSize;
char buffer[bufferMax];
void loop() {
EthernetClient client = server.available();
if (client) {
waitForRequest(client);
handleRequest();
client.stop();
}
}
void waitForRequest(EthernetClient client) {
bufferSize = 0;
while (client.connected()) {
if (client.available()) {
char c = client.read();
if (c == '\n') {
break;
} else {
if (bufferSize < bufferMax) {
buffer[bufferSize++] = c;
} else {
break;
}
}
}
}
}
void handleRequest() {
// Received buffer contains a standard HTTP GET line, something like
// "GET /?X=X&Y=Y HTTP/1.1".
// Could have up to 4 parameters, 1=on&2=on&3=on&4=on,
// one for each set of speakers. All that is necessary here is to
// extract the query string and check for each of the zone numbers.
String request = String(buffer);
//int firstSpace = request.indexOf(" "); // right after GET
//int lastSpace = request.indexOf(" ", firstSpace + 1); // just after the query string
//request = request.substring(firstSpace, lastSpace);
//Serial.println(request);
showStatus = request.indexOf("?") == -1;
if (request.indexOf("1=on") > -1) {
zone1 = true;
} else if (request.indexOf("1=off") > -1) {
zone1 = false;
}
if (request.indexOf("2=on") > -1) {
zone2 = true;
} else if (request.indexOf("2=off") > -1) {
zone2 = false;
}
if (request.indexOf("3=on") > -1) {
zone3 = true;
} else if (request.indexOf("3=off") > -1) {
zone3 = false;
}
if (request.indexOf("4=on") > -1) {
zone4 = true;
} else if (request.indexOf("4=off") > -1) {
zone4 = false;
}
if (request.indexOf("volume=") > -1) {
int start = request.indexOf("volume=") + 7; // 7 is length of "volume="
int end = request.indexOf(" ", start);
volume = request.substring(start, end).toInt();
} else {
if (request.indexOf("mute") > -1) {
volume = 0;
}
}
//Serial.println("Volume = " + String(volume));
if (!showStatus) {
setSpeakerState();
}
sendPage();
}
void setSpeakerState() {
digitalWrite(zone1L, zone1 ? RELAY_ON : RELAY_OFF);
digitalWrite(zone1R, zone1 ? RELAY_ON : RELAY_OFF);
digitalWrite(zone2L, zone2 ? RELAY_ON : RELAY_OFF);
digitalWrite(zone2R, zone2 ? RELAY_ON : RELAY_OFF);
digitalWrite(zone3L, zone3 ? RELAY_ON : RELAY_OFF);
digitalWrite(zone3R, zone3 ? RELAY_ON : RELAY_OFF);
digitalWrite(zone4L, zone4 ? RELAY_ON : RELAY_OFF);
digitalWrite(zone4R, zone4 ? RELAY_ON : RELAY_OFF);
unmute();
setVolume(volume);
}
void sendPage() {
// http response header
/*
int length = 1442;
if (digitalRead(zone4L) == RELAY_ON)
length += 18;
if (digitalRead(zone3L) == RELAY_ON)
length += 18;
if (digitalRead(zone2L) == RELAY_ON)
length += 18;
if (digitalRead(zone1L) == RELAY_ON)
length += 18;
*/
server.println("HTTP/1.1 200 OK");
server.println("Content-Type: text/html");
// server.print("Content-Length: ");
// server.println(length);
server.println();
// html head
server.println("<html><head><meta charset=\"UTF-8\">");
server.println("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
server.println("<link rel=\"stylesheet\" href=\"http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css\">");
server.println("<title>Speaker Control</title>");
server.println("<script>");
server.println("function doit(){");
server.println("document.getElementById(\"vF\").submit();}");
server.println("</script></head><body>");
// html body
server.println("<div class='container'><h2>Speaker Control</h2><form action='/' role='form'>");
int pinState = digitalRead(zone4L);
printCheckBox(4, pinState, "Hot tub");
pinState = digitalRead(zone3L);
printCheckBox(3, pinState, "Dining Room");
pinState = digitalRead(zone2L);
printCheckBox(2, pinState, "Portico");
/* not used
pinState = digitalRead(zone1L);
printCheckBox(1, pinState, "(Empty)");
*/
server.print("<br/><button type='submit'>Save</button></form></div>");
server.print("</div><div class='container'><h3>Master Volume</h3><form id='vF' action='/' role='form'>");
server.print("<input type='range' name='volume' min='0' max='79' value='");
server.print(volume);
server.print("' oninput='doit(value)'>");
server.print("</form>");
server.print("<form id='mF' action='/' role='form'>");
server.print("<input type='submit' name='mute' value='Mute'/></form></div></body></html>");
}
void printCheckBox(int zone, int pinState, String label) {
server.print("<br/><input type='hidden' name='");
server.print(zone);
server.print("' value='off'/><div class='checkbox'><label><input type='checkbox' name='");
server.print(zone);
server.print("' value='on'");
server.print(pinState == RELAY_ON ? " checked='checked'" : "");
server.print(">");
server.print(label);
server.print("</label></div>");
}
void setVolume(int level) {
byte bbbaaaa = calculateVolume(level);
byte aaaa = bbbaaaa & 0b00001111;
byte bbb = (bbbaaaa >> 4) & 0b00001111;
Wire.beginTransmission(EVC_ADDR);
Wire.write(STEP_10 | bbb);
Wire.write(STEP_1 | aaaa);
Wire.endTransmission();
//Serial.println("set volume to " + (79 - level));
}
byte calculateVolume(int level) {
if (level > 79)
level = 79;
if (level < 0)
level = 0;
level = 79 - level; // invert the volume, so 0 is low and 79 is high
uint8_t b = level / 10; // get the most significant digit (eg. 79 gets 7)
uint8_t a = level % 10; // get the least significant digit (eg. 79 gets 9)
b = b & 0b0000111; // limit the most significant digit to 3 bit (7)
return (b << 4) | a; // return both numbers in one byte (0BBBAAAA)
}
void unmute() {
byte value = MUTE_OFF;
Wire.beginTransmission(EVC_ADDR);
Wire.write(value);
byte error = Wire.endTransmission();
//if (error)
// Serial.println("mute, error code = " + String(error));
}
No comments:
Post a Comment