/*
P4Groan.
Simple web server with connected 16x2 LCD
A simple web server that shows and changes the value of the Groan Index in a browser an well as on connected LCD
Circuit:
* LCD on pins 2,3,4,5,6,7
* Ethernet shield attached to pins 10, 11, 12, 13
* Servo on PWM pin 9
Author: Simon West
Created: 2012-08-24. Perforce Workshop initial addition: 2014-08-01
*/
#define DEBUG 1
// Various defines...
// The IP address will be dependent on your local network
// Made a defined to allow for future use of DHCP
#define useLCD 1
// #define staticIP 169,254,10,75
// Use servo?
#define useServo 1
// Limits - must be a multiple of 180 is using servo to make mapping work cleanly
#define MAXGROAN 179
// Includes needed for Ethernet shield
#include <SPI.h>
#include <Ethernet.h>
#if defined useServo
// Servo control library
#include <Servo.h>
Servo pointerArm;
#endif
// Optionally include LCD code
#if defined useLCD
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7,6,5,4,3,2);
boolean activityIndicator = true;
#endif
// Enter a MAC address and IP address for your controller below.
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
#if defined staticIP
IPAddress ip(staticIP);
#endif
// Holds the GrownLevel
int groanLevel = 0;
// Initialize the Ethernet server library
EthernetServer server(80);
void setup()
{
Serial.begin(9600);
Serial.println("Starting");
#if defined useLCD
lcd.begin(16,2);
lcd.print("starting");
#endif
// start the Ethernet connection and the server:
// start the Ethernet connection:
#if defined staticIP
Ethernet.begin(mac,ip);
#else
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
#if defined useLCD
lcd.clear();
lcd.print("DHCP not available");
lcd.setCursor(0,1);
lcd.print("**Game Over**");
#endif
// no point in carrying on, so do nothing forevermore:
for(;;)
;
}
#endif
// Output the IP address on LCD and debug
#if defined useLCD
lcd.clear();
lcd.print("IP:");
lcd.setCursor(0,1);
#endif
// print your local IP address:
Serial.print("My IP address: ");
for (byte thisByte = 0; thisByte < 4; thisByte++) {
// print the value of each byte of the IP address:
Serial.print(Ethernet.localIP()[thisByte], DEC);
Serial.print(".");
#if defined useLCD
lcd.print(Ethernet.localIP()[thisByte], DEC);
lcd.print(".");
#endif
}
Serial.println();
#if defined useServo
//Connect to the servo
#if defined DEBUG
Serial.println("Init pointer");
#endif
pointerArm.attach(9);
setArmPosition(groanLevel);
#endif
// Start the ethernet server
server.begin();
#if defined useLCD
// Wait for 2 secs to for mesage to be read on LCD
delay(2000); // wait 2 secs
// initial display and we're ready to go...
lcd.clear();
lcd.print("Perforce Groans");
lcd.setCursor(0,1);
lcd.print("Groan Index:");
lcd.print(groanLevel);
#endif
}
void loop()
{
readStream();
#if defined useLCD
blinkScreen();
#endif
}
// =========== read from network and write back to browser ==============
int readStream()
{
char pageRequested;
boolean validLine = false;
// listen for incoming clients
EthernetClient client = server.available();
if (client) {
// an http request ends with a blank line
boolean currentLineIsBlank = true;
String ln = "";
while (client.connected()) {
if (client.available()) {
char c = client.read();
#if defined DEBUG
Serial.print(c);
#endif
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
sendPage(client, pageRequested);
#if defined useLCD
updateLCD();
#endif
#if defined useServo
if (pageRequested == 'i'|| pageRequested == 'j'|| pageRequested =='r') { // do this after sending page to stop browser having to wait for arm to position itself...
setArmPosition(groanLevel);
}
#endif
break;
} // end if get line
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
ln = "";
} // end if starting new line
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
ln += c;
// catches the fact that some browsers request /faveico or /applemobileicon as a 2nd request. we ignore these
if (!validLine) {
validLine = (ln == "GET /i " || ln == "GET / " || ln == "GET /r " || ln == "GET /j ");
if (validLine) {
pageRequested = ln.charAt(5);
}
} // if !validLine
} // end else if
} // end if (client.available())
} // end while
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
}
}
void sendPage(EthernetClient client, char pageRequested)
{
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println();
// output the page
client.print("<html><head>");
client.print("<link rel='stylesheet' href='//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css'>");
client.print("<link rel='stylesheet' href='//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css'></head>");
client.print("<body><div class='container theme-showcase' role='main'><div class='jumbotron'><h1>Perforce Groan-o-meter</h1>");
if (pageRequested == 'i') { // Increment
++groanLevel;
}
if (pageRequested == 'j') { // jump the increment by 10
groanLevel = groanLevel + 10;
}
if (pageRequested == 'r') { // reset
groanLevel = 0;
}
// Always display the help and value
client.print("<h2>Groan level is now ");
client.print(groanLevel);
client.print("</h2>");
client.print("<br /><p><a href=/ class='btn btn-primary btn-lg' role='button'> <span class='glyphicon glyphicon-refresh'></span> </a> ");
client.print("<a href=/i class='btn btn-success btn-lg' role='button'>+1</a> <a href=/j class='btn btn-warning btn-lg' role='button'>+10</a></p><br />");
client.print("<a href=/r class='btn btn-lg btn-link'>Reset</a>");
client.print("</div></div></body></html>");
}
// ========== LCD Code ==================
#if defined useLCD
void updateLCD()
{
lcd.setCursor(12,1);
lcd.print(" ");
lcd.setCursor(12,1);
lcd.print(groanLevel);
}
// not a very accurate way if you want timing, but sufficient to show that things are still working...
void blinkScreen()
{
if (millis()%1000 == 0) {
lcd.setCursor(15,0);
if (activityIndicator) {
lcd.print('.');
}
else {
lcd.print(' ');
}
}
activityIndicator = !activityIndicator;
}
#endif
// ============ servo code ===========
#if defined useServo
void setArmPosition(int armPos)
{
#if defined DEBUG
Serial.println("");
Serial.print("Setting arm to:");
Serial.println(armPos);
Serial.println("");
#endif
int currentPos = pointerArm.read();
// As the pointer and groanindex run on different scales we need to remap the value to between 0 and 180. The index can continue to increase but the arm sweep will stop at 180.
armPos = map(armPos,0,MAXGROAN,179,0);
pointerArm.write(armPos);
} // setArmPosition
#endif