Created
April 19, 2015 00:09
-
-
Save mhungerford/1fdf667b37238525c939 to your computer and use it in GitHub Desktop.
SparkFM Pebble (spark core code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A fun program for the Spark Core that works without the use of the tone() function | |
// Originally written by Brett Hagman (http://roguerobotics.com), | |
// Reworked by Technobly (http://technobly.com) for the Spark Core. | |
// | |
// This plays RTTTL (RingTone Text Transfer Language) songs | |
// by bit-banging a selected digital output. | |
// | |
// To play the output on a small speaker (i.e. 8 Ohms or higher), simply use | |
// a 220 ohm to 1k ohm resistor from the output pin (D6) to the speaker, | |
// and connect the other side of the speaker to ground. | |
// | |
// You can get more RTTTL songs from | |
// http://code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation | |
/* Includes ------------------------------------------------------------------*/ | |
#include <application.h> | |
//#include <string.h> | |
//#include <stdint.h> | |
#define DO_DEBUG 0 | |
#define OCTAVE_OFFSET 0 | |
#include "spark_wiring_wifi.h" | |
//SYSTEM_MODE(AUTOMATIC); | |
//Example spark function call <coreName> setSong "1" | |
//prototypes | |
void tone(int pin, int16_t note, int16_t duration); | |
int rtttlChunk(String args); | |
int16_t tonePin = D6; | |
bool startSong = false; | |
char songBuffer[512] = ""; | |
// Notes defined in microseconds (Period/2) | |
// from note C to B, Octaves 3 through 7 | |
int notes[] = | |
{0, | |
3817,3597,3401,3205,3030,2857,2703,2551,2404,2273,2146,2024, | |
1908,1805,1701,1608,1515,1433,1351,1276,1205,1136,1073,1012, | |
956,903,852,804,759,716,676,638,602,568,536,506, | |
478,451,426,402,379,358,338,319,301,284,268,253, | |
239,226,213,201,190,179,169,159,151,142,134,127}; | |
byte default_dur = 4; | |
byte default_oct = 6; | |
byte lowest_oct = 3; | |
int bpm = 63; | |
int num; | |
long wholenote; | |
long duration; | |
byte note; | |
byte scale; | |
bool songDone = false; | |
char *songPtr; | |
void begin_rtttl(char *p) | |
{ | |
// Absolutely no error checking in here | |
// format: d=N,o=N,b=NNN: | |
// find the start (skip name, etc) | |
// get default duration | |
if(*p == 'd') | |
{ | |
p++; p++; // skip "d=" | |
num = 0; | |
while(isdigit(*p)) | |
{ | |
num = (num * 10) + (*p++ - '0'); | |
} | |
if(num > 0) default_dur = num; | |
p++; // skip comma | |
} | |
if(DO_DEBUG) { Serial.print("ddur: "); Serial.println(default_dur, 10); } | |
// get default octave | |
if(*p == 'o') | |
{ | |
p++; p++; // skip "o=" | |
num = *p++ - '0'; | |
if(num >= 3 && num <=7) default_oct = num; | |
p++; // skip comma | |
} | |
if(DO_DEBUG) { Serial.print("doct: "); Serial.println(default_oct, 10); } | |
// get BPM | |
if(*p == 'b') | |
{ | |
p++; p++; // skip "b=" | |
num = 0; | |
while(isdigit(*p)) | |
{ | |
num = (num * 10) + (*p++ - '0'); | |
} | |
bpm = num; | |
p++; // skip colon | |
} | |
if(DO_DEBUG) { Serial.print("bpm: "); Serial.println(bpm, 10); } | |
// BPM usually expresses the number of quarter notes per minute | |
wholenote = (60 * 1000L / bpm) * 2; // this is the time for whole note (in milliseconds) | |
if(DO_DEBUG) { Serial.print("wn: "); Serial.println(wholenote, 10); } | |
// Save current song pointer... | |
songPtr = p; | |
} | |
bool next_rtttl() { | |
char *p = songPtr; | |
// if notes remain, play next note | |
if(*p) | |
{ | |
// first, get note duration, if available | |
num = 0; | |
while(isdigit(*p)) | |
{ | |
num = (num * 10) + (*p++ - '0'); | |
} | |
if(num) duration = wholenote / num; | |
else duration = wholenote / default_dur; // we will need to check if we are a dotted note after | |
// now get the note | |
note = 0; | |
switch(*p) | |
{ | |
case 'c': | |
note = 1; | |
break; | |
case 'd': | |
note = 3; | |
break; | |
case 'e': | |
note = 5; | |
break; | |
case 'f': | |
note = 6; | |
break; | |
case 'g': | |
note = 8; | |
break; | |
case 'a': | |
note = 10; | |
break; | |
case 'b': | |
note = 12; | |
break; | |
case 'p': | |
default: | |
note = 0; | |
} | |
p++; | |
// now, get optional '#' sharp | |
if(*p == '_') | |
{ | |
note++; | |
p++; | |
} | |
// now, get optional '.' dotted note | |
if(*p == '.') | |
{ | |
duration += duration/2; | |
p++; | |
} | |
// now, get scale | |
if(isdigit(*p)) | |
{ | |
scale = *p - '0'; | |
p++; | |
} | |
else | |
{ | |
scale = default_oct; | |
} | |
scale += OCTAVE_OFFSET; | |
if(*p == ',') | |
p++; // skip comma for next note (or we may be at the end) | |
// Save current song pointer... | |
songPtr = p; | |
// now play the note | |
if(note) | |
{ | |
if(DO_DEBUG) { | |
Serial.print("Playing: "); | |
Serial.print(scale, 10); Serial.print(' '); | |
Serial.print(note, 10); Serial.print(" ("); | |
Serial.print(notes[(scale - lowest_oct) * 12 + note], 10); | |
Serial.print(") "); | |
Serial.println(duration, 10); | |
} | |
tone(tonePin, notes[(scale - lowest_oct) * 12 + note], duration); | |
//noTone(tonePin); | |
} | |
else | |
{ | |
if(DO_DEBUG) { | |
Serial.print("Pausing: "); | |
Serial.println(duration, 10); | |
} | |
delay(duration); | |
} | |
return 1; // note played successfully. | |
} | |
else { | |
return 0; // all done | |
} | |
} | |
void tone(int pin, int16_t note, int16_t duration) { | |
for(int16_t x=0;x<(duration*1000/note);x++) { | |
PIN_MAP[pin].gpio_peripheral->BSRR = PIN_MAP[pin].gpio_pin; // HIGH | |
delayMicroseconds(note); | |
PIN_MAP[pin].gpio_peripheral->BRR = PIN_MAP[pin].gpio_pin; // LOW | |
delayMicroseconds(note); | |
} | |
} | |
void print_mac_addr(void) { | |
byte mac[6]; // the MAC address of your Wifi shield | |
WiFiClass *wifi = new WiFiClass(); | |
wifi->macAddress(mac); | |
Serial.print("MAC: "); | |
Serial.print(mac[5],HEX); | |
Serial.print(":"); | |
Serial.print(mac[4],HEX); | |
Serial.print(":"); | |
Serial.print(mac[3],HEX); | |
Serial.print(":"); | |
Serial.print(mac[2],HEX); | |
Serial.print(":"); | |
Serial.print(mac[1],HEX); | |
Serial.print(":"); | |
Serial.println(mac[0],HEX); | |
} | |
//------------------- | |
// MAIN PROGRAM | |
//------------------- | |
void setup(void) | |
{ | |
Serial.begin(115200); | |
pinMode(tonePin,OUTPUT); | |
pinMode(D7,OUTPUT); | |
Spark.function("rtttlChunk",rtttlChunk); | |
delay(2000); | |
Serial.println("Start!"); | |
//print_mac_addr(); | |
} | |
void loop(void) | |
{ | |
// The main loop() processes one note of the song at a time | |
// to avoid blocking the background tasks for too long or else | |
// the Spark Core would disconnect from the Cloud. | |
if(startSong) { | |
if(!songDone) { // Start song | |
digitalWrite(D7,HIGH); // Light the onboard Blue LED while the song plays | |
songDone = true; | |
begin_rtttl(songBuffer); | |
} | |
if(!next_rtttl()) { // Play next note | |
digitalWrite(D7,LOW); // Turn off the onboard Blue LED. | |
songDone = false; | |
startSong = false; | |
songBuffer[0] = '\0'; | |
if(DO_DEBUG) Serial.println("Done!"); | |
delay(1000); | |
} | |
} | |
} | |
// Args are limited to 64 bytes, so accept RTTL as chunks | |
int rtttlChunk(String args) { | |
if (args.length() > 0) { | |
strncat(songBuffer, args.c_str(), args.length()); | |
char msg[32]; | |
sprintf(msg, "chunk_len:%d", args.length()); | |
Serial.println(msg); | |
} else { //we use a 0 length chunk to mark the end | |
startSong = true; | |
Serial.println("0 chunk: Play!"); | |
} | |
return 200; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment