Announcing The Particle Pi Day Contest!


#1

Happy holidays, everyone!!

From St. Patrick’s Day, to March Maddness, to even (in some circles) the Ides of March, Mid-March is chock full of holiday cheer. However, for the admittedly nerdy team here at Particle HQ, no other late-winter celebration gets our spirits up quite like Pi Day!! :pie:

We’d like to celebrate with a quick contest here in our fantastic community.

Here’s all you need to know:

RULES

  • Create a #ParticlePowered project related to Pi, Pie, or any other Pi-pun you can come up with!
  • Post your project to Hackster, complete with all appropriate instructions, documentation, code, etc.
  • Post the link to your Hackster post + a short project description in this thread.
  • The project must include Particle hardware.
  • The project must be submitted by the end of the contest deadline: Monday, March 18th @ 9:00am Pacific

Prizes

  • Everyone who submits a completed project will earn a fabulous new forum badge, designed by the one and only @iamgabesanchez.

  • One winner, selected at random by yours truly, will win a Particle Prize Pack, consisting of the finest :particle: branded swag this Earth has ever seen + a few select items from our third generation of IoT hardware, powered by Particle Mesh.

Good luck & have fun! Please feel free to tag me (@joe) with any questions, comments, compliments, ideas, etc. that you might have.

:sunglasses:


Pi Day Celebration
#2

Who has ideas to get the creative juices flowin’? I’ll share some of what the team sitting around me here at the office came up with:

  • A Photon-powered heat sensor that tells you when the oven is just warm enough to bake a delicious pie.
  • A motorized device that can draw a perfect circle.
  • An alarm clock that plays hair metal each and every morning.
  • A device that turns the page of your book, perfect for reading The Life of Pi.
  • A robotic arm that flings whipped cream pies across the cafeteria if a food fight breaks out, or into the face of a :clown_face: if you need a quick laugh.
  • A memory game that sees how many digits of Pi you can remember.

…and that’s just to start! I can’t wait to see what creative projects y’all come up with.

@joe


#3

Just as an alternative to the alarm clock, how about one that plays a more mellow song that you can go to sleep to? :smile:


#4

@dougal anything from Piebald’s discography would also suffice :wink:


#5

Well… First!.. I hope.

Here’s my entry. It’s so simple. Flash the first 100 decimal places of Pi in Morse code using the on-board D7 LED. It actually flashes 102 digits when you include the decimal point and the number 3 to the left of it. This is also an example of using a finite-state machine to track which state and processing step is being processed. I used the Morse code example from another hackster project called “Morse Code With Arduino+LED”.


#6

Well, darn. My weekend went to heck, and I didn’t get to touch any Particle stuff. But I thought I’d at least share the project idea that I wanted to do…

There are algorithms to compute the Nth digit of Pi. I was going to set up a Mesh network where each node would compute a requested digit, then publish their results.

A web page would let the user decide how many digits they wanted (up to a reasonable limit, probably something like 10 - 100 digits), and kick off the computations.

The server would choose digit indexes in random order, and dole out the requests to the Mesh nodes. “Node1, compute digit 7”, “Node5, compute digit 83”, “Node 2, compute digit 24”, etc. On the web page, each node would have an associated color, and as their Pi digits were received, the digit would be populated into a grid, color coded to the node that computed it. This would continue until all of the requested digits had been computed.


#7

I’m afraid I won’t be able to finish an “expansion” to my entry today. I tried to create the converse of my Morse code encoder… the Morse code decoder. Using the Photosensor that came with the Xenon kits from the preorder, I created a Xenon that watches for the Morse code pulses and decodes those pulses back into the Pi digits and published the Pi buffer to the cloud every 5 seconds. Unfortunately, I keep getting a panic hard fault after it listens for a while and don’t have enough time to fix the issue. It does receive the pulses and can decode at least the first digit received. I tried to make the decoder accommodate changing pulse lengths by using a circular buffer of sorts. The idea was so that the decoder didn’t need to know the set pulse widths for short and long pulses. Rather, it would look at the buffer history and figure out what was a “dot” or a “dash”. Alas, I’ll leave this here for posterity and I may just get the decoder working at some point.

#include <math.h>

#define MorseBufferSize 128 //The max number of pulses (high and low stored separately) to buffer.

//All the digit mappings for decoding the characters.
static const struct {const char letter, *code;} MorseMap[] =
{
	{ 'A', ".-" },
	{ 'B', "-..." },
	{ 'C', "-.-." },
	{ 'D', "-.." },
	{ 'E', "." },
	{ 'F', "..-." },
	{ 'G', "--." },
	{ 'H', "...." },
	{ 'I', ".." },
	{ 'J', ".---" },
	{ 'K', ".-.-" },
	{ 'L', ".-.." },
	{ 'M', "--" },
	{ 'N', "-." },
	{ 'O', "---" },
	{ 'P', ".--." },
	{ 'Q', "--.-" },
	{ 'R', ".-." },
	{ 'S', "..." },
	{ 'T', "-" },
	{ 'U', "..-" },
	{ 'V', "...-" },
	{ 'W', ".--" },
	{ 'X', "-..-" },
	{ 'Y', "-.--" },
	{ 'Z', "--.." },
	{ ' ', "     " }, //Gap between word, seven units 
		
	{ '1', ".----" },
	{ '2', "..---" },
	{ '3', "...--" },
	{ '4', "....-" },
	{ '5', "....." },
	{ '6', "-...." },
	{ '7', "--..." },
	{ '8', "---.." },
	{ '9', "----." },
	{ '0', "-----" },
		
	{ '.', "·–·–·–" },
	{ ',', "--..--" },
	{ '?', "..--.." },
	{ '!', "-.-.--" },
	{ ':', "---..." },
	{ ';', "-.-.-." },
	{ '(', "-.--." },
	{ ')', "-.--.-" },
	{ '"', ".-..-." },
	{ '@', ".--.-." },
	{ '&', ".-..." },
};

//**************************************************
//Variables
//**************************************************
//Set the photosensor input
int SenseIn = D2;

bool morseIn = false;

unsigned long durationTimer = 0;        //Record the duration for both the high and low portions of a pulse.
unsigned long lastCharTimer = 0;        //Record the time since we last received a character.
unsigned long resetTimeout = 5000;      //If a pulse isn't received during this period, reset the buffer.

unsigned long morseBuffer[MorseBufferSize]; //Holds the pulse duration times in millis.
uint8_t morseBufferType[MorseBufferSize];   //Holds the type of puls (LOW or HIGH).

uint8_t indexBufferIn = 0;  //Holds the current buffer position where we are storing values.
uint8_t indexBufferOut = 0; //Holds the current buffer position where we are reading out values for processing.

char charBuffer[255];       //Holds the decoded character string.

unsigned long dotDuration = 0;  //Holds the shortest HIGH pulse time (which should be a Morse code "dot").
unsigned long dashDuration = 0; //Holde the longest HIGH pulse time (which should be a Morse code "dash").

//Publish Variables
unsigned long pubInterval = 5000;   //Interval between publishes (default 5000 ms = 5 seconds).
unsigned long lastPub = 0;          //Time of the last publish in millis.

//**********SETUP**********
void setup() {
    pinMode(SenseIn, INPUT);
    pinMode(D7, OUTPUT);        //Use D7 for diagnostics to visualize the incoming sensor pulses.
    
    //Use interrupts to sense the high/low changes on the sense pin.
    attachInterrupt(SenseIn, SenseChange, CHANGE);
}

//**********LOOP**********
void loop() {
    
    //There should be a long pause between iterations of seding Pi.
    //Reset if the pause is detected.
    if ( (millis() - durationTimer > resetTimeout) && morseIn) {
        ResetBuffer();
        strcpy(charBuffer,"");
        morseIn = false;
    }
    
    //Process the morse buffer if pulses have been received.
    if (morseIn) {
        ProcessMorse();
    }
    
    //Publish the received characters at the publish interval (default 5 seconds)
    if (millis() - lastPub > pubInterval) {
        Particle.publish("PiDay",charBuffer,PRIVATE);
        lastPub = millis();
    }
}

//This is the interrupt handler.
void SenseChange() {
    //Detect if this was a low>high or high<low change and log the type of change.
    if (digitalRead(SenseIn) == HIGH) {
        morseBufferType[indexBufferIn] = LOW;
        digitalWrite(D7,HIGH);
    } else {
        morseBufferType[indexBufferIn] = HIGH;
        digitalWrite(D7,LOW);
    }
    
    //Record the duration of the high or low time.
    morseBuffer[indexBufferIn++] = millis() - durationTimer;
    durationTimer = millis();   //Reset the timer
    ValidateIndex(&indexBufferIn);  //Make sure the buffer index is within the bounds of the array size.
    morseIn = true; //Set the flag so we know pulses have been received.
}

//Reset the indexes to 0 and delete the duration data.
void ResetBuffer() {
    indexBufferIn = 0;
    indexBufferOut = 0;
    for(int i = 0; i<MorseBufferSize; i++) {
        morseBufferType[i] = -1;
        morseBuffer[i] = 0;
    }
}

//Make sure the index is within the bounds of the buffer array.
void ValidateIndex(uint8_t * indexBuffer) {
    if (*indexBuffer >= MorseBufferSize) {
        uint8_t i = floor( float(*indexBuffer) / MorseBufferSize );
        *indexBuffer = *indexBuffer - (MorseBufferSize * i);
    }
}

//Process the pulses sitting in the buffer.
void ProcessMorse() {
    
    //Only process if at least 10 high>low or low>high transistions have been recorded.
    if (GetToProcessLength() < 10) {
        return;
    }
    
    //look at every HIGH pulse in the buffer and look for the longest and shortest pulses.
    for(int i = indexBufferOut; i != indexBufferIn; i++) {
        if (morseBufferType[i] == HIGH) {
            unsigned long tempDur = morseBuffer[i];
            if ( tempDur > 0 && tempDur < 2000) {
                if (dotDuration == 0) {
                    dotDuration = tempDur;
                } else {
                    if (dotDuration > tempDur) {
                        dotDuration = tempDur;
                    }
                }
                
                if (dashDuration == 0) {
                    dashDuration = tempDur;
                } else {
                    if (dashDuration < tempDur) {
                        dashDuration = tempDur;
                    }
                }
            }
        }
    }
    
    //Temp buffer to hold the pulses converted to dots or dashes.
    char tempBuffer[128];
    
    //Calculate a +-10% variance for the pulse lengths.
    unsigned long dotShort = floor(float(dotDuration) * 0.9);
    unsigned long dotLong = ceil(float(dotDuration) * 1.1);
    
    unsigned long dashShort = floor(float(dashDuration) * 0.9);
    unsigned long dashLong = ceil(float(dashDuration) * 1.1);
    
    //For each high or low pulse, determine if it is a dot, dash, short space or long space.
    for(uint8_t i = indexBufferOut; i != indexBufferIn; i++) {
        if (morseBufferType[i] == HIGH) {
            if ( morseBuffer[i] >= dotShort && morseBuffer[i] <= dotLong ) {
                strcat(tempBuffer, ".");
            } else if ( morseBuffer[i] >= dashShort && morseBuffer[i] <= dashLong ) {
                strcat(tempBuffer, "-");
            }
            ValidateIndex(&i);
        } else {
            if ( morseBuffer[i] >= (dotShort*2) && morseBuffer[i] <= (dotLong*2) ) {
                strcat(tempBuffer, " ");  //Shortspace (space between letters)
            } else if ( morseBuffer[i] >= (dotShort*3) && morseBuffer[i] <= (dotLong*3) ) {
                strcat(tempBuffer, "w");  //Wordspace (space between words)
            }
            ValidateIndex(&i);
        }
    }
    
    //Break the temp buffer into tokens on the space.
    char * pch;
    pch = strtok (tempBuffer," ");
    while (pch != NULL)
    {
        //char morseChar[1] = ;
        strcat(charBuffer, GetMorseChar(pch));
        indexBufferOut = indexBufferOut + strlen(pch) + 1;
        ValidateIndex(&indexBufferOut);
        pch = strtok (NULL, " ");
    }
}

//Get the difference from the out index to the start index... so we know how many tranistions there are to process.
uint8_t GetToProcessLength() {
    if (indexBufferIn != indexBufferOut) {
        if (indexBufferIn < indexBufferOut) {
            return ( (indexBufferIn + MorseBufferSize) - indexBufferOut );
        } else if (indexBufferIn > indexBufferOut) {
            return ( indexBufferIn - indexBufferOut );
        }
    }
}

//Parse through the MorseMap and find the correct character.
char* GetMorseChar(char * morseDotDash) {
    char* result = new char[1];
    for (uint8_t i = 0; i < 48; i++) {
        if (strcmp(morseDotDash, MorseMap[i].code) == 0) {
            strcpy(result, &(MorseMap[i].letter));
            return result;
        }
    }
    strcpy(result,"_");
    return result;
}

And a side note… the 220 ohm resistor that came with the photosensor is too small. I could not get the LED pulses to register until I changed the resistor to a 4.7k ohm. The LED and the photosensor have to be within a centimeter apart (in a well lit room) as well.


#8

Is that code for K right? I think it’s supposed to be -.-?


#9

Perhaps. I copied and pasted from a github example. I was only concern with the numerical digits for this project. But I didn’t validate any of the encoding or decoding.


#10

Thanks for participating, everyone! I’ll contact winners in the DMs.

:particle: :pie: