Saturday, August 31, 2019, 14:44
Posted by Administrator
Posted by Administrator
This year I have been experimenting quite a bit with the Arduino platform. I love how Arduino clones are much cheaper than Raspberry Pi’s and only cost me around 2 dollars each from China. Arduinos also consume very little power and run for many days on my rechargeable external battery packs.
Long ago I created a blinking morse code light on a Raspberry Pi which slowly flashed the Tao Te Ching. It was a relatively straightforward process as there is far more than enough storage available on virtually any Micro Sd card to hold even the largest of books.
My first true Arduino project was to remake my morse code Tao Te Ching light. Getting the Arduino to flash morse code was straightforward and lots of people have worked up examples that got me started. I made many modifications to some code I found online, and eventually got the Litany Against Fear from Dune to flash out on a small white LED:
// Message
byte m[] = "I MUST NOT FEAR. FEAR IS THE MIND-KILLER. FEAR IS THE LITTLE-DEATH THAT BRINGS TOTAL OBLITERATION. I WILL FACE MY FEAR. I WILL PERMIT IT TO PASS OVER ME AND THROUGH ME. AND WHEN IT HAS GONE PAST I WILL TURN THE INNER EYE TO SEE ITS PATH. WHERE THE FEAR HAS GONE THERE WILL BE NOTHING. ONLY I WILL REMAIN.";
// Pin to flash
int pin = 13;
// Tempo
// 240 ~5wpm
// 120 ~10wpm
// 80 ~15wpm
int t = 240;
// Setup the output pin
void setup () {
pinMode(pin, OUTPUT);
}
// Perform the dits
void d() {
digitalWrite(pin, HIGH);
delay(1 * t);
digitalWrite(pin, LOW);
delay(1 * t);
}
// Perform the das
void da() {
digitalWrite(pin, HIGH);
delay(3 * t);
digitalWrite(pin, LOW);
delay(1 * t);
}
// Define the pattern of dits and das for each letter
void morse(byte l) {
if (l == 'A' or l == 'a') {d(); da();}
else if (l == 'B' or l == 'b') {da(); d(); d(); d();}
else if (l == 'C' or l == 'c') {da(); d(); da(); d();}
else if (l == 'D' or l == 'd') {da(); d(); d();}
else if (l == 'E' or l == 'e') {d();}
else if (l == 'F' or l == 'f') {d(); d(); da(); d();}
else if (l == 'G' or l == 'g') {da(); da(); d();}
else if (l == 'H' or l == 'h') {d(); d(); d(); d();}
else if (l == 'I' or l == 'i') {d(); d();}
else if (l == 'J' or l == 'j') {d(); da(); da(); da();}
else if (l == 'K' or l == 'k') {da(); d(); da();}
else if (l == 'L' or l == 'l') {d(); da(); d(); d();}
else if (l == 'M' or l == 'm') {da(); da();}
else if (l == 'N' or l == 'n') {da(); d();}
else if (l == 'O' or l == 'o') {da(); da(); da();}
else if (l == 'P' or l == 'p') {d(); da(); da(); d();}
else if (l == 'Q' or l == 'q') {da(); da(); d(); da();}
else if (l == 'R' or l == 'r') {d(); da(); d();}
else if (l == 'S' or l == 's') {d(); d(); d();}
else if (l == 'T' or l == 't') {da();}
else if (l == 'U' or l == 'u') {d(); d(); da();}
else if (l == 'V' or l == 'v') {d(); d(); d(); da();}
else if (l == 'W' or l == 'w') {d(); da(); da();}
else if (l == 'X' or l == 'x') {da(); d(); d(); da();}
else if (l == 'Y' or l == 'y') {da(); d(); da(); da();}
else if (l == 'Z' or l == 'z') {da(); da(); d(); d();}
else if (l == '1') {d(); da(); da(); da(); da();}
else if (l == '2') {d(); d(); da(); da(); da();}
else if (l == '3') {d(); d(); d(); da(); da();}
else if (l == '4') {d(); d(); d(); d(); da();}
else if (l == '5') {d(); d(); d(); d(); d();}
else if (l == '6') {da(); d(); d(); d(); d();}
else if (l == '7') {da(); da(); d(); d(); d();}
else if (l == '8') {da(); da(); da(); d(); d();}
else if (l == '9') {da(); da(); da(); da(); d();}
else if (l == '0') {da(); da(); da(); da(); da();}
else if (l == '.') {d(); da(); d(); da(); d(); da();}
else if (l == ',') {da(); da(); d(); d(); da(); da();}
else if (l == '?') {d(); d(); da(); da(); d(); d();}
else if (l == '\'') {d(); da(); da(); da(); da(); d();}
else if (l == '!') {da(); d(); da(); d(); da(); da();}
else if (l == '/') {da(); d(); d(); da(); d();}
else if (l == '(') {da(); d(); da(); da(); d();}
else if (l == ')') {da(); d(); da(); da(); d(); da();}
else if (l == '&') {d(); da(); d(); d(); d();}
else if (l == ':') {da(); da(); da(); d(); d();d();}
else if (l == ';') {da(); d(); da(); d(); da(); d();}
else if (l == '=') {da(); d(); d(); d(); da();}
else if (l == '+') {d(); da(); d(); da(); d();}
else if (l == '-') {da(); d(); d(); d(); d(); da();}
else if (l == '_') {d(); d(); da(); da(); d(); da();}
else if (l == '"') {d(); da(); d(); d(); da(); d();}
else if (l == '$') {d(); d(); d(); da(); d(); d(); da();}
else if (l == '@') {d(); da(); da(); d(); da(); d();}
// Plus the two below equals a delay of 7 tempos for a space
if (l == ' ') {delay(5 * t);}
// Delay of 3 tempos, 2, plus the delay after a character
delay(2 * t);
}
// Loop through the message
void loop () {
// For each character in the message
for (int g = 0; g < sizeof(m); g++) {
// Flash the morse equivalent for this letter
morse(m[g]);
}
}
The real challenge was in getting an entire book to fit on an Arduino. Most standard Arduino’s such as the Arduino Nano clones that I’m using have roughly 32K of storage space for holding programs. You get even less than this in reality since a typical bootloader for the Arduino takes .5K – 2K of the space before you even load your code on there. Even though the morse code flashing instructions I put together are relatively small, there was still not nearly enough space left to hold my favorite copy of the Tao Te Ching translated by Stephen Mitchell.
His translation is around 37.32K and my original hope was that if I removed all the line breaks, and most of of the punctuation that I could get it to fit somehow. However, after adding in the additional code for flashing the LED and the bootloader, it became apparent that there was just no way this was going to work. I then pondered over it all for a couple of days.
Compression is the natural solution to this problem. I was able to find a wonderfully up to date text compression utility called Shox96. It is a hybrid entropy and dictionary encoder and the author has put together a great white paper on the design which is well worth reading. It also has a version which works great at compressing short strings into the Arduino program memory.
After compressing Stephen Mitchell’s translation of the Tao Te Ching with Shox96 and then decompressing one chunk at a time and feeding that into my morse code flashing program it all worked perfectly. I had the entire book flashing before my eyes.
There was even plenty of free space left over. So I decided to make things a lot harder and use this public domain translation of the Tao Te Ching by John H. McDonald which is ~45.70K in size when not compressed. After compressing it… it did not fit on the Arduino anymore.
I continued to poke at it and eventually realized that Shox96 encoding is more efficient when the lines of text are long. I then went through the full text manually and increased the line sizes to mostly be full sentences. Compressing this version got it all plenty small enough and I finally considered the project to be finished.
I left the light blinking for many weeks running off of the USB port on the side of my office monitor. At the slow speed I set it at it takes longer than a day to run through the entire book, at this point it loops and starts from the beginning. I left serial debugging on, so if you monitor the serial output of the device you can watch it print each character to the screen as it flashes. This looked especially nice when paired with outputting to a CRT television. I ran it this way for many days as well.
This project helped me get quite a bit more familiar with Arduino’s and making use of additional libraries when working on sketches. The final flashing art piece also got me much better at sight-reading morse code which was a nice bonus.
The full code for this project is available here. Let me know if you can think of any other great books that would benefit from this sort of harassment.
add comment
( 3146 views )
| permalink
| ( 3 / 1317 )