Page 185

PWM colours breadboard layout

Page 186

struct Lookup { byte code; char result; }; struct Lookup bSearch[36]; struct Node { byte left; byte right; char result; }; struct Node bTree[40]; void createCodeArray() { for(int i = 0; i < 36; i++) {     bSearch[i].code = mCode[i];     bSearch[i].result = (i < 26) ? (char)(i + 65) : (char)(i + 22); } } // a quick sort for the bSearch array Lookup structs // makes use of pointer arithmatic (just for fun) void bSort(Lookup array[], int left, int right) { if(left > right) {return;} swapB(array + left, array + ((left + right) / 2)); int last = left; for(int i = left + 1; i <= right; i++) {     if(compB(array + i, array + left) < 0) {      swapB(array + (++last), array + i);     } } swapB(array + left, array + last); bSort(array, left, last - 1); bSort(array, last + 1, right); }

Page 187

int compB(Lookup* a, Lookup* b) { if(a->code == b->code) {return 0;} return (a->code > b->code) ? 1 : -1; } void swapB(Lookup* a, Lookup* b) { Lookup temp = *a; *a = *b; *b = temp; } void createBinaryTree() { for(int i = 0; i < 40; i++) {     bTree[i].result = ' '; } int bIndex = 0; for(int i = 0; i < 36; i++) {     bIndex = 0; // start at the beginning     byte code = bSearch[i].code;     int bc = code & ((1 << 3)-1);     for(int bi = 0; bi < bc; bi++) {      if((code & HIBIT_MASK) == 0){         // dot         if(bTree[bIndex].left == 0) {          bTree[bIndex].left = nextFree();         }         bIndex = bTree[bIndex].left;      } else {         if(bTree[bIndex].right == 0) {          bTree[bIndex].right = nextFree();         }         bIndex = bTree[bIndex].right;      }      code = code << 1;     }     bTree[bIndex].result = bSearch[i].result; } } byte nextFree() { static byte nFree = 0; return ++nFree; }

Page 188

char treeSearch(byte findThis) { int treeIndex = 0; int bc = findThis & ((1 << 3)-1); for(int bi = 0; bi < bc; bi++) {     if((findThis & HIBIT_MASK) == 0){      treeIndex = bTree[treeIndex].left;     } else {      treeIndex = bTree[treeIndex].right;     }     findThis = findThis << 1; } return bTree[treeIndex].result; } void displayTreeCode() { Serial.print("const Node bTree[] = {"); for(int i = 0; i < 40; i++) {     Serial.print((i > 0) ? ", {" : "{");     Serial.print(bTree[i].left);     Serial.print(", ");     Serial.print(bTree[i].right);     Serial.print(", '");     Serial.print(bTree[i].result);     Serial.print("'}"); } Serial.println("};"); } void testTree() { Serial.println("Testing binary tree"); for(int i = 0; i < 36; i++) {     Serial.print((i < 26) ? (char)(i + 65) : (char)( i + 22));     Serial.print(" : ");     char getC = treeSearch(mCode[i]);     Serial.println(getC); } }

Page 189

void setup() { Serial.begin(115200); createCodeArray(); // load codes into bSearch bSort(bSearch, 0, 35); // sort the array createBinaryTree(); displayTreeCode(); testTree(); } const Node bTree[] = {{1, 20, ' '}, {2, 13, 'E'}, {3, 9, 'I'},                      {4, 7, 'S'}, {5, 6, 'H'}, {0, 0, '5'},                      {0, 0, '4'}, {0, 8, 'V'}, {0, 0, '3'},                      {10, 11, 'U'}, {0, 0, 'F'}, {0, 12, ' '},                      {0, 0, '2'}, {14, 16, 'A'}, {15, 0, 'R'},                      {0, 0, 'L'}, {17, 18, 'W'}, {0, 0, 'P'},                      {0, 19, 'J'}, {0, 0, '1'}, {21, 29, 'T'},                      {22, 26, 'N'}, {23, 25, 'D'}, {24, 0, 'B'},                      {0, 0, '6'}, {0, 0, 'X'}, {27, 28, 'K'},                      {0, 0, 'C'}, {0, 0, 'Y'}, {30, 34, 'M'},                      {31, 33, 'G'}, {32, 0, 'Z'}, {0, 0, '7'},                      {0, 0, 'Q'}, {35, 37, 'O'}, {36, 0, ' '},                      {0, 0, '8'}, {38, 39, ' '}, {0, 0, '9'},                      {0, 0, '0'}}; void showCode() { for(int i = 0, j = sizeof(readCodes); i < j; i++) {     if(readCodes[i] == B00000000) {      if(i > 0 and readCodes[i-1] == B00000000) {         break;      }      Serial.println(" ");     } else {      Serial.print(treeSearch(readCodes[i]));     } } }

Page 190

#define CODE_LIMIT 30 byte readCodes[CODE_LIMIT]; int nextCode = 0; void rollCodes() { nextCode--; for(int i = 0; i < nextCode; i++) {     readCodes[i] = readCodes[i+1]; } } void showCode(bool all) { for(int i = 0; i < nextCode; i++) {     if(readCodes[i] == B00000000) {      Serial.println(" ");     } else {      Serial.print(treeSearch(readCodes[i]));     }     if(!all) {return;} } nextCode = 0; }

Page 191

void readBytes(bool justOne) { bool codeAdded = false; int bCount = 0; byte wrk = B00000000; while(signalQ->count) {     SignalInt* item = readFirst();     long t = item->intTime;     if(item->isCode) {      wrk = wrk << 1;      if(t >= codeTimes[dash].minMillis &&          t <= codeTimes[dash].maxMillis) {         wrk ^= 1;      }      bCount++;     } else {      if(t >= codeTimes[letterSpace].minMillis) {         wrk = wrk << (8 - bCount);         wrk ^= bCount;         bCount = 0;         readCodes[nextCode] = wrk;         codeAdded = true;         nextCode++;         wrk = B00000000;         if(t >= codeTimes[wordSpace].minMillis &&          t <= codeTimes[wordSpace].maxMillis){          readCodes[nextCode] = wrk;          nextCode++;         }      }     }     deQueue();     if((justOne && codeAdded) || nextCode > CODE_LIMIT - 1) {break;} } }

Page 192

void loop() { switch(sStatus) {     case 2:      detachInterrupt(digitalPinToInterrupt(MORSE_PIN));      sStatus = 0;      while(signalQ->count > 0 || nextCode > 0) {         readBytes(false);         while(nextCode > 0) {          showCode(true); // display all characters in array         }      }      attachInterrupt(digitalPinToInterrupt(MORSE_PIN),morseInter,CHANGE);      Serial.println("Ready for more");      break;     case 1:      if(signalQ->count >=11 && nextCode < CODE_LIMIT - 2) {         readBytes(true); // read a character from signal stream      }      if(nextCode > 0) {         showCode(false); // display top character         rollCodes(); // roll it off the array      }      break; } }

Page 193

} else { int t = (unsigned long)(currentMicros -     lastMicros) / 1000; if(wasCode || t >= codeTimes[letterSpace].minMillis) {     if(queueAdd(wasCode, t)) {         TCNT1 = 3036; // reset timer     } else {      sStatus = 2; // error adding to Q so stop     } }

Further thoughts

By the time we got to the end of the development of the Morse reading program we could have refined the SignalInt structure.
The time value no longer needed to be a long and could have been signed and the sign bit used in place of the boolean isCode.
Alternately the idea from Charles Petzold could also have been applied to load incoming signals into a sequence of bytes.
As the final program demonstrated that Morse could be translated into text more or less in real time the readCodes[] array could be smaller although the rollCodes() function never has to work very hard.