Skip to content

Instantly share code, notes, and snippets.

@mhungerford
Created April 19, 2015 00:09
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 mhungerford/1fdf667b37238525c939 to your computer and use it in GitHub Desktop.
Save mhungerford/1fdf667b37238525c939 to your computer and use it in GitHub Desktop.
SparkFM Pebble (spark core code)
// 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