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.