Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@Grathio
Created October 23, 2012 04:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Grathio/3936612 to your computer and use it in GitHub Desktop.
Save Grathio/3936612 to your computer and use it in GitHub Desktop.
National Novel Writing Month Progress Meter
/* Simple Arduino project to turn a servo to the value sent via the Serial connection.
Also periodically sends a ping back so the host knows that the Arduino is connected.
Connect a servo to the Arduino:
- Black or brown wire to GND
- Red wire to 5v
- White or Orange wire to Digital pin 9.
Send a byte to the Arduino with the value of 0 to 180 and it will turn the servo to that heading.
The host can detect if the Arduino is alive and listening if it sends "ok".
By Steve Hoefer
Grathio Labs
http://grathio.com
Provided under the MIT license. http://opensource.org/licenses/mit-license.php
Copyright (c) 2012 Steve Hoefer
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <Servo.h>
int servoPin = 9; // Servo data connected to pin 9. (Usually the white or orange wire on the servo.)
Servo servo; // (To test your servo run the "Sweep" example under File -> Examples -> Servo -> Sweep.)
int servoAngle = 0;
unsigned long pingTime = 2000; // Seconds between sending "I'm alive" back to host.
unsigned long lastPingTime = 0;
void setup(){
servo.attach(servoPin);
Serial.begin(9600);
}
void loop(){
// wait for info
if (Serial.available() > 0) { // Read a number when its sent.
servoAngle = Serial.read();
servo.write(servoAngle); // Set the servo to that position.
}
if (millis() > lastPingTime + pingTime){ // Time to send another "I'm alive" ping.
Serial.print("ok");
Serial.flush();
lastPingTime = millis(); // But don't do it again for a while.
}
}
/*
Operates a physical gauge to track your progress for National Novel Writing Month.
Uses the NaNoWriMo wordcount API available at http://www.nanowrimo.org/en/wordcount_api
By Steve Hoefer of Grathio Labs
http://grathio.com
This code is provided AS IS. NO SUPPORT IS PROVIDED. See TROUBLESHOOTING below if you have
difficulty.
It's also under the very permissive MIT License. Feel free to copy, modify, share, etc.
The full license is below.
-------------------------------------------------------------------------------------------------------
Setup:
For more through instructions see http://grathio.com/2012/10/national-novel-writing-month-progress-monitor
Processing:
0) REQUIRES Processing 2.0 or better! ( http://processing.org/download/ )
1) Change the value of [userName] (below, around line 100) to the NaNoWriMo user name you want to track.
2) If you're doing more (or less) than 50,000 words, change the value of [wordTarget] as well.
3) You might need to change the [serialPortNumber] as well. See TROUBLESHOOTING below.
Arduino:
1) Download the code for the Arduino
from: http://grathio.com/2012/10/national-novel-writing-month-progress-monitor
2) Connect a small hobby servo to the Arduino:
- Brown or black to Ground (GND)
- Red to +5V
- White or orange to Digital Pin 9
3) Attach a gauge, dial or some other indicator to the servo and secure it in some kind of
enclosure. Put it somewhere visible, near your computer.
4) Connect the Arduino to your computer.
5) Launch the Arduino application. (Requires version 1.0.1 or newer) http://arduino.cc/download/
6) Open the "nanowrimo_Arduino.ino" file in the Arduino application and upload it to your Arduino
(Be sure you have the correct serial port and Arduino model selected.)
The yellow "TX" light should blink every second or two.
7) Run this Processing sketch.
- If you're on a Mac or Linux you may need to follow some instructions in the message window
below to get Serial communication to work.
8) If everything is working correctly a small window will open with your NaNoWriMo status widget and a
status message underneath. If not... keep reading.
TROUBLESHOOTING:
Problem: The Processing sketch shows errors when I try to run it.
Solutions: 1) Make sure any changes you made didn't break anything. (Watch quotes, semicolons, etc.)
2) Update to the latest version of Processing http://processing.org/download/ (Tested with 2.0 Beta 3)
Problem: Message "Arduino Not Connected" or "Arduino Connecting..." won't go away.
Solutions: 1) Double check that your Arduino is connected to your computer with a USB cable and that the
green power light is on.
2) Be sure the "nanowrimo_Arduino.ino" is uploaded to the Arduino.
3) Check the message window below to see if you're using the correct serial port for your Arduino.
If not, set the value of portNumber (below) to the number in [brackets] that matches your Arduino.
Problem: Message "Error loading data"
Solutions: 1) Make sure your computer is connected to the internet and can connect to http://www.nanowrimo.org/
2) Make sure the value of "username" is set to the user account name you want to track.
Problem: Servo doesn't do anything.
Solutions: 1) Double check your wiring (See above under "Setup")
2) Check that the servo works at all by opening the Arduino development application and opening
Examples -> Servo -> Sweep. Upload this to your Arduino and if your servo is working is should
sweep back and forth. (Be sure to re-upload the nanowrimo code when you're through.)
Problem: I get a "Problem uploading to board" message when trying to program the Arduino
Solution: The Arduino application and Processing are trying to use the same serial port at the same time.
Close the Processing window and try again. If that doesn't work exit Processing completely. Sometimes
the computer might get confused and a reboot is required.
Suggested Improvements:
- Better error handling all around.
- A GUI so users don't have to edit code to enter user names and select serial ports.
-------------------------------------------------------------------------------------------------------
Copyright (c) 2012 Steve Hoefer
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
import processing.serial.*;
//////////////////////////////////////////////////////////////////////////////
// Configuration Stuff. Change the values below to match your configuration //
// //
String userName = "ChangeMe"; // The NaNoWriMo account name to track. You'll need to convert spaces to dashes.
int wordTarget = 50000; // The total number of words that is your goal.
int serialPortNumber = 0; // The number of the Arduino's serial port shown in the info window.
// //
// End of easily configurable stuff //
//////////////////////////////////////////////////////////////////////////////
Serial arduinoPort; // Where we talk to the Arduino.
String[] serialPortList; // List of all the serial ports on the computer.
int updateTime = 3600; // How often to check for updates. (In seconds. Set to 1 hour because NaNoWriMo's servers only update once an hour. Checking more often won't do anything but stress their servers.)
long lastUpdateTime; // Time of the last time that we checked for an update.
String apiURL = "http://www.nanowrimo.org/wordcount_api/wc/"; // Location of the XML data. For more information see http://www.nanowrimo.org/en/wordcount_api
String widgetString = "http://www.nanowrimo.org/widget/MyMonth/"; // Where we get the widget image. (Can be any of the widget images from here: http://www.nanowrimo.org/en/widgets )
String arduinoStatusMessage = "";
String dataStatusMessage = "";
String nanowrimoStatusMessage = "";
PImage widgetImage = new PImage();
XML apiReturnData; // The data that NaNoWriMo returns about the author and book.
boolean statusChange = true; // Becomes [true] when the data on the server changes. This is so we only load the image when it changes. (And the first time run.)
int servoAngle = -1; // -1 means we don't know what it should be yet.
int lastWordCount = -1;
int currentWordCount = -1;
int arduinoTimeoutCheck = 5; // How many seconds before we get no successful pings before we say the Arduino has disappeared.
long lastArduinoPingTime = 0; // Time of last successful ping.
void setup() {
size(150, 200); // size of our window. Big enough for the month calendar image and a couple status messages below.
serialPortList = Serial.list(); // Get a list of all the active serial ports.
// Tell the user what port we're using and what the options are.
// (This would be a good place for a graphical interface, but that would take much more code)
println("\n\n----------------------------------------------------------------------");
println("The currently selected serial port is shown below. If this is not the port your");
println("Arduino is connected to you need to change the number of [serialPortNumber]");
println("to match the correct port.\n");
for (int i = 0; i < serialPortList.length; i++){ // go through all the available ports.
if (i == serialPortNumber){ // highlight the current port
println("===> [" + i + "] " + serialPortList[i] + " <===");
} else {
println(" [" + i + "] " + serialPortList[i]);
}
}
println("----------------------------------------------------------------------");
arduinoPort = new Serial(this, serialPortList[serialPortNumber], 9600); // connect to the Arduino. We hope.
lastUpdateTime = 0; // Set this is far in the past so we will update on first load.
}
void draw() {
background(255);
// Check to see if the Arduino is actually connected and listening.
if (arduinoPort.available()>0){ // There are some characters waiting for us to look at them.
String s = arduinoPort.readString();
if (s.equals("ok")){
arduinoStatusMessage = "Arduino Connected";
lastArduinoPingTime = new Date().getTime();
}
}
// Has it been too long since last Arduino communication?
long now = new Date().getTime();
if (lastArduinoPingTime + (arduinoTimeoutCheck * 1000) < now ){
if (lastArduinoPingTime == 0) { // This is zero if it's the first time we're running it.
arduinoStatusMessage = "Arduino Connecting...";
} else {
arduinoStatusMessage = "Arduino Not Connected";
}
}
// show the status of the Arudino.
fill(128);
textAlign(CENTER);
text(arduinoStatusMessage, 5, 180, 130, 20);
// Put up any message about the data from nanowrimo. (Usually only errors.)
text(dataStatusMessage, 5, 160, 130, 20);
// Handle the widget image
if (statusChange == true){ // if the image is out of date (or it's the first time here) load a new widget image.
updateWidgetImage();
statusChange = false;
}
if (widgetImage.width == -1) { // images with errors (or that aren't found) have a width of -1.
nanowrimoStatusMessage = "Problem loading image";
fill(128);
textAlign(CENTER);
text(nanowrimoStatusMessage, 10, 25, 130, 50);
} else if (widgetImage.width == 0) { // zero width images have simply not loaded yet.
nanowrimoStatusMessage = "Loading...";
fill(128);
textAlign(CENTER);
text(nanowrimoStatusMessage, 10, 25, 130, 50);
} else {
// All is well. Draw the widget image on the screen, horizontally centered.
image(widgetImage, 75-(widgetImage.width/2), 3);
}
// Handle the NaNoWriMo data
// Check to see if it's time to look for new data.
if (now > lastUpdateTime + (updateTime*1000)){ // Multiply by 1000 because date getTime() is in milliseconds.
updateWordcountData();
lastUpdateTime = now; // don't do it again for a bit.
}
}
// Gets the graphic for the widget image.
// See http://www.nanowrimo.org/en/widgets for information on customizing widgets.
void updateWidgetImage(){
widgetImage = requestImage(widgetString+userName+",goal=" + wordTarget + ".png");
}
// load and processes the XML data regarding word count.
void updateWordcountData(){
dataStatusMessage = "";
try{
apiReturnData = loadXML(apiURL+userName);
for (int i =0; i< apiReturnData.getChildCount();i++){ // Go through each line of the XML data.
XML item = apiReturnData.getChild(i);
if (item.getName() == "error"){ // If it's an error, show it on the screen.
dataStatusMessage = item.getContent();
} else if (item.getName() == "user_wordcount"){ // if we have good word count data, use it.
currentWordCount = Integer.parseInt(item.getContent());
if (currentWordCount != lastWordCount){ // if word count has changed, update things.
statusChange = true; // This will trigger a refresh of the widget image on the next frame. (Small bug: this will load the widget image twice in a row when first run.)
updateArduino(); // New data means we need to move the gauge.
dataStatusMessage = "";
lastWordCount = currentWordCount;
}
}
}
} catch (Exception e){
dataStatusMessage = "Error loading data";
}
}
// Send new servo position to Arduino
void updateArduino(){
if (currentWordCount > wordTarget){ // Keep everything within limits.
wordTarget = currentWordCount;
}
servoAngle = (int) map(currentWordCount, 0, wordTarget, 180, 0); // Calculate the position of the servo.
arduinoPort.write(servoAngle); // And send it on to the Arduino.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment