Author Archives: Allen Huffman

About Allen Huffman

Co-founder of Sub-Etha Software.

C escape codes

Now maybe someone here can tell me if this makes any sense:

#define SOME_NAME "SomeName\0"

I ran across something like this in my day job and wondered what the purpose of adding a “\0” zero byte was to the end of the string. C already does that, doesn’t it?

C escape codes

I learned about using backslash to embed certain codes in strings when I was first learning C on my Radio Shack Color Computer. I was using OS-9/6809 and a pre-ANSI K&R C compiler.

I learned about “\n” at the end of a line, and that may be the only one I knew about back then. (I expect even K&R has “\l” and maybe “\t” too, but I never used them in any of my code back then.)

The wikipedia has a handy reference:

Escape sequences in C – Wikipedia

It lists many I was completely unaware of – like “vertical tab.” I’d have to look up what a vertical tab is, as well ;-)

It was during my “modern” career that I learned you could embed any value in a printf by escaping it with “\x” and a hex value:

int main()
{
    const char bytes[] = "\x01\x02\x03\x04\x05";
    
    printf ("sizeof(bytes) = %zu\n", sizeof(bytes));

    for (int idx=0; idx<sizeof(bytes); idx++)
    {
        printf ("%02x ", bytes[idx]);
    }
    
    printf ("\n");

    return EXIT_SUCCESS;
}

This code makes a character array containing the bytes 0x01, 0x02, 0x03, 0x04 and 0x05. A zero follows, added by C to terminate the quoted string. The output looks like:

sizeof(bytes) = 6
01 02 03 04 05 00

I do not know how I learned it, but it was just two jobs ago when I used this to embed a bunch of data in a C program. I believe I was tokenizing some strings to reduce code size, and I had some kind of lookup table of strings, and then the “token” strings of bytes that referred back to the full string. Something like this, except less stupid:

#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
#include <stdint.h>

const char *words[] =
{
    "I",
    "know",
    "you"
};

const uint8_t sentence[] = "\x01\x02\x03\x02\x01\x02";
int main()
{
    printf ("sizeof(sentence) = %zu\n", sizeof(sentence));

    for (int idx=0; idx<sizeof(sentence)-1; idx++)
    {
        printf ("%s ", words[sentence[idx]-1]);
    }
    
    printf ("\n");

    return EXIT_SUCCESS;
}

In this silly example, I have an array of strings, and then an encoded sentence with bytes representing each word. The encoded bytes will have a 0 at the end, so I use 1 for the first word, and so on, with 0 marking the end of the sequence. But, this example doesn’t actually look for the 0. It just uses the number of bytes in the sentence (minus one, to skip the 0 at the end) via sizeof().

It really should use the 0, so this could be a function. You could pass it the dictionary of words, and the sentence bytes, and let it decode them in a more flexible/modular way:

#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
#include <stdint.h>

// Dictionary of words
const char *words[] =
{
    "I",
    "know",
    "you"
};

// Encoded sentence
const uint8_t sentence[] = "\x01\x02\x03\x02\x01\x02";

// Decoder
void showSentence(const char *words[], const uint8_t sentence[])
{
    int idx = 0;
    
    while (sentence[idx] != 0)
    {
        printf ("%s ", words[sentence[idx]-1]);
        
        idx++;
    }
    
    printf ("\n");
}

// Test
int main()
{
    printf ("sizeof(sentence) = %zu\n", sizeof(sentence));

    showSentence (words, sentence);

    return EXIT_SUCCESS;
}

But I digress. My point is — I’m still learning things in C, even after knowing it since the late 1980s.

So back to the original question: What is adding a “\0” to a string doing? This is one advantage of using sizeof() versus strlen(). strlen() will stop at the 0, but sizeof() will tell you everything that is there.

#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
#include <string.h> // for strlen()

int main()
{
    const char string[] = "This is a test.\0And so is this.\0And this is also.";

    printf ("strlen(string) = %zu\n", strlen(string));

    printf ("sizeof(string) = %zu\n", sizeof(string));
    
    return EXIT_SUCCESS;
}

The output:

strlen(string) = 15
sizeof(string) = 50

If you try to printf() that string, it will print only up to the first \0. But, there is more “hidden” data after the zero. If you have the sizeof(), that size could be used in a routine to print everything. But why? We can already do string arrays or just embed carriage returns in a string if we wanted to print multiple lines.

But it’s still neat.

Have you ever done something creating with C escape codes? Leave a comment…

Until then…

C strings and pointers and arrays, revisited…

Previously, I posted more of my “stream of consciousness” ramblings ending this bit of code:

#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
#include <string.h> // for strlen()

int main()
{
    const char *stringPtr = "hello";
    
    printf ("sizeof(stringPtr) = %ld\n", sizeof(stringPtr));
    printf ("strlen(stringPtr) = %ld\n", strlen(stringPtr));

    printf ("\n");

    const char string[] = "hello";

    printf ("sizeof(string) = %ld\n", sizeof(string));
    printf ("strlen(string) = %ld\n", strlen(string));

    return EXIT_SUCCESS;
}

Sean Patrick Conner commented:

I would expect the following:

sizeof(stringPtr) = 8; /* or 4 or 2, depending upon the pointer size */
strlen(stringPtr) = 5;

sizeof(string) = 6; /* because of the NUL byte at the end */
strlen(string) = 5;

– Sean Patrick Conner

Sean sees things much more clearly than I. When I tried it, I was initially puzzled by the output and had to get my old brain to see the obvious. His comments explain it clearly.

These musings led me to learning about “%zu” for printing a size_t, and a few other things, which I have now posted here in other articles.

I learn so much from folks who take time to post a comment.

More to come…

My 360 photo/video (VR) experiments…

I often forget to cross-post things between my project sites, so let’s do that right now.

I bought my first digital camera (an Epson PhotoPC) in 1996. I have had many others since then. In addition to photo cameras, I also had various camcorders including my first digital camcorder in 1999. It recorded digitally to 8mm video tapes (Digital8 was the format, I believe). I have also experimented in 3-D, with a NuView camcorder attachment (what a beast that was) and some other gadgets, and some 360 photography.

For 360 photos, you could originally just use a normal camera and take photos in all directions then “stitch” them together using special software. You can find examples of that in some 2002 photos I took at an Illinois Renaissance festival.

There was an early attempt to do “one shot” 360 photos by using a half mirror ball on a rod, and attaching that to the lens of a camera. You would shoot with the camera pointed up, which captured the mirror ball and all things going on around it. Those images could be processed back to panoramas with special software. I had a gadget called SurroundPhoto I experimented with back around 2005.

In the mid-2010s we started seeing consumer 360 cameras made by companies like Giroptic, RICHO and even Kodak. I have had a variety of those in recent years and am currently using an Insta360 X4.

Sharing 360 photos and videos is not easy. Facebook supports them, and YouTube supports video, so I created some Facebook Groups for sharing photos (I made them groups so others could share theirs’s as well) and new YouTube channels for sharing videos.

If you have ended up on my site for Insta360 topics, maybe you will want to pop by these groups/channels…

VR videos on YouTube:

VR photos on Facebook (post your own there, too):

Until next time…

Yes, Virginia. You CAN printf a size_t! And pointers.

I always learn from comments. Sadly, I don’t mean the comments inside the million lines of code I maintain for my day job — they usually don’t exist ;-)

I have had two previous posts dealing with sizeof() being used on a string constant like this:

#define VERSION "1.0.42-beta"
printf ("sizeof(VERSION) = %d\n", sizeof(VERSION));

Several comments were left to make this more better.

Use %z to print a size_t

The first pointed out that sizeof() is not returning a %d integer:

sizeof does not result in an int, so using %d is not correct.

– F W

Indeed, this code should generate a compiler warning on a good compiler. I would normally cast the sizeof() return value to an int like this:

printf ("sizeof(VERSION) = %d\n", (int)sizeof(VERSION));

BUT, I knew that really wasn’t a solution since that code is not portable. An int might be 16-bits, 32-bits or 64-bits (or more?) depending on the system architecture. I often write test code on a PC using Code::Blocks which uses the GNU-C compiler. On that system, I would need to use “%ld” for a long int. When that code is used on an embedded compiler (such as the CCS compiler for PIC42 chips), I need to make that “%d”.

I just figured printf() pre-dates stuff like that and thus you couldn’t do anything about it.

But now I know there is a proper solution — if you have a compiler that supports it. In the comments again…

… when you want to print a size_t value, using %zu.

– Sean Patrick Conner

Thank you, Sean Patrick Conner! You have now given me new information I will use from now on. I was unaware of %z. I generally use the website www.cplusplus.com to look up C things, and sure enough, on the printf entry it mentions %z — just in a separate box below the one I always look at. I guess I’d never scrolled down.

cplusplus.com/reference/cstdio/printf/

This old dog just learned some new tricks!

int var = 123;

printf ("sizeof(var) = %zu\n", sizeof(var));

Thank you very much for pointing this out to me. Unfortunately, the embedded compiler I use for my day job does not support any of the new stuff, and only has a sub-set of printf, but the Windows compiler I use for testing does.

Bonus: printing pointers for fun and profit

I’d previously ran in to this when trying to print out a pointer:

int main()
{
    char *ptr = 0x12345678;
    
    printf ("ptr = 0x%x\n", ptr);

    return EXIT_SUCCESS;
}

A compiler should complain about that, like this:

warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘char *’ [-Wformat=]

…so I’d just do a bit of casting, to cast the pointer to what %x expects:

printf ("ptr = 0x%x\n", (unsigned int)ptr);

BUT, that assumes an “int” is a certain size. This casting might work find on a 16-bit Arduino, then need to be changed for a 32-bit or 64-bit PC program.

And, the same needs to be done when trying to assign a number (int) to a char pointer. This corrects both issues, but does so the incorrect way:

int main()
{
    char *ptr = (char*)0x12345678;

    printf ("ptr = 0x%lx\n", (unsigned long)ptr);

    return EXIT_SUCCESS;
}

First, I had to cast the number to be a character pointer, else it would not assign to “char *ptr” without a warning.

Second, since %x expects an “unsigned int”, and pointers on this sytem are long, I had to change the printf to use “%lx” for a long version of %x, and cast the “ptr” itself to be an “unsigned long”.

Had I written this initially on a system that uses 16-bit ints (like Arduino, PIC24, etc.), I would have had to do it differently, casting things to “int” instead of “long.”

This always drove me nuts, and one day I wondered if modern C had a way to deal with this. And, indeed, it does: %p

This was something that my old compilers either didn’t have, or I just never learned. I only discovered this within the past five years at my current job. It solves the problems by handling a “pointer” in whatever size it is for the system the code is compiled on. AND it even includes the “0x” prefix in the output:

int main()
{
    char *ptr = (char*)0x12345678;

    printf ("ptr = %p\n", ptr);

    return EXIT_SUCCESS;
}

I suppose when I found there was a “real” way to print pointers I should have expected there was also a real way to print size_t … but it took you folks to teach me that.

And I thank you.

Until next time…

C strings and pointers and arrays…

In a previous post about using sizeof() on string literals, there was an interesting comment by S. Enevoldsen:

To better remember this realize that arrays are not pointers, and string literals are arrays (that can decay to pointers).

const char arrayVersion[] = “1.0.42-beta”;
const char* pointerString = “1.0.42-beta”;
printf (“sizeof(arrayVersion) = %d\n”, sizeof(arrayVersion));
printf (“sizeof(pointerString) = %d\n”, sizeof(pointerString));

Outputs

sizeof(arrayVersion) = 12
sizeof(pointerString) = 4

– S. Enevoldsen

If I knew this, I have long forgotten it. Over the years at my “day jobs” I have gotten used to making string pointers like this:

const char *versionStringPtr = "1.0.42-beta";

I generally add the “Ptr” at the end to remind me (or other programmers) that it is a pointer to a string. In my mind, I knew I could have done “char *string” or “char string[]” and gotten the same use from normal code, but I do not recall if I knew they were treated differently.

What do you expect the output of this to be?

#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
#include <string.h> // for strlen()

int main()
{
    const char *stringPtr = "hello";
    
    printf ("sizeof(stringPtr) = %ld\n", sizeof(stringPtr));
    printf ("strlen(stringPtr) = %ld\n", strlen(stringPtr));

    printf ("\n");

    const char string[] = "hello";

    printf ("sizeof(string) = %ld\n", sizeof(string));
    printf ("strlen(string) = %ld\n", strlen(string));

    return EXIT_SUCCESS;
}

Output would show … what?

sizeof(stringPtr) = ???
strlen(stringPtr) = ???

sizeof(string) = ???
strlen(string) = ???

To be continued…

In C, you can sizeof() a string constant?

Updates:

  • 2024-08-27 – Adding a note about strlen()/sizeof() that was mentioned by Dave in the comments.

I am used to using sizeof() to know the size of a structure, or size of a variable…

typedef struct {
   char a;
   short b;
   int c;
   long d;
} MyStruct;

printf ("sizeof(MyStruct) is %d\n", sizeof(MyStruct));

MyStruct foo;
printf ("sizeof(foo) is %d\n", sizeof(foo));

…but every time I re-learn you can use it on strings, I am surprised:

#include <stdio.h>

#define VERSION_STRING __DATE__" "__TIME__

int main()
{
    printf ("Build: %s\n", VERSION_STRING);

    printf ("sizeof(): %ld\n", sizeof(VERSION_STRING));

    return 0;
}

Normally, I see strlen() used, and that works for a string that is in a buffer, or a constant string:

#define VERSION_STRING "1.0.42-beta"
const char versionString[] = "1.0.42-beta";

printf ("strlen(VERSION_STRING) = %d\n", strlen(VERSION_STRING));

printf ("strlen(versionString) = %d\n", strlen(versionString));

…but if you know it is a #define string constant, you can use sizeof() and that will be changed in to the hard-coded value that matches the length of that hard-coded string. This will be smaller code, and faster, since strlen() has to scan through the string memory looking for the ‘0’ at the end, counting along the way.

I wonder how many times I have posted about this over the years.

Additional Notes:

In the comments, Dave added:

sizeof a string literal includes the terminating nul character, so it will be strlen +1.

– Dave

Ah, yes – a very good thing to note. C strings have a 0 byte added to the end of them, so “hello” is really “hello\0”. The standard C string functions like strcpy(), strlen(), etc. look for that 0 to know when to stop.

#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
#include <string.h> // for strlen()

#define STRING "hello"

int main()
{
    printf ("sizeof(STRING) = %ld\n", sizeof(STRING));
    
    printf ("strlen(STRING) = %ld\n", strlen(STRING));

    return EXIT_SUCCESS;
}

Output would show:

sizeof(STRING) = 6
strlen(STRING) = 5

So if using sizeof() to memcpy() bytes somewhere without the overhead of a strlen() counting first, you’d really want something like…

memcpy (buffer, STRING, sizeof(STRING)-1);

Until next time…

Why do 5.7K and 8K Insta360 X3/X4 photos/videos look so bad?

For the search engines…

I see this question come up over and over (and over and over) again on discussion groups (Facebook, REDDIT, etc.). Folks see “8K camera” or “5.7K camera” and expect that will be better than an HD camera or 4K camera.

But not with a 360 camera.

With a normal camera, you have a lens recording a square/rectangular image. An HD camera will record an image that is 1920×1080 pixels. Those pixels are used for the entire square/rectangular image.

But, a 360 camera with two lenses takes its resolution and divides that by two — one for each lens. An 8K Insta360 X4 camera is therefore shooting a 4K image out the front lens, and a 4K image out the back.

BUT, instead of shooting straight ahead, it is a wide angle fisheye style image that is actually capturing everything in front, above, below, to the left and right of that lens. The back lens is doing the same.

When you think of it that way, the number of pixels that would be for the “forward” view is a fraction of the pixels you would get with a normal non-fisheye single lens camera.

Here is my quick doodle:

Now, reality is actually much more complex than this simple drawing, but the end result is you an “reframe” 360 footage to be a view in any direction. If you only use those six main directions (forward, backwards, left, right, up and down), you are dividing the pixels of that 8K image in to 6 smaller images. If 8K video is 7680 × 4320, then each view is closer to 1280×720 — which you can see is below “full HD” of 1920×1080.

So even with an 8K 360 camera, what you get in any specific direction is still not going to be as good as a simple HD camera that only records in one direction.

(And yes, I know the reality is much more complex, but this is just greatly simplified to help new users visualize how it works.)

Until next time…

Insta360 X4 firmware 1.2.20

Updates:

  • 2024-08-01 – Fernando T. in the comments noted that there are still missing features: “I can see no control of bracketing steps and number of shots for composing HDR Photo yet…” Let’s hope that, eventually, Insta360 can make the X4 do as much as its predecessor could do.

Finally! The GPS Remote and Apple Watch may be used to control the X4. Also support for streaming. We are now getting close to the standard features we were used to with the X3.

Hacking Home Depot Lethal Lily animatronic – part 4

See Also: intro, part 1, part 2, part 3 and part 4. (Github project.)

The story this far…

In 2023, Home Depot sold an animated Halloween prop called Lethal Lily Swamp Witch. This prop was unique in that it has a rather nicely animate head that could move its eyes, blink, and tilt the neck in various directions. This was done by having the prop controlled by servos. Lethal Lily is much more advanced that most props that just use simple motors and gears to repeat motions.

The animated head is controlled via a four-wire connector which provides power, ground, a serial data line, and some kind of line that pulses to control the servos. Somehow.

While I do not yet understand how the pulse line works, I have been able to capture the pulse timing from the Control Box with the goal of having the Arduino generate the same pulses directly to the head — hopefully animating it without using the Control Box.

Using my LethalLilyReader program from the previous part, I was able to capture the serial and pulse data and display it as numbers. This allowed me to copy the pulse timing data so I could put it in a new program that would pulse the blue wire going to the head on and off with appropriate timing delays between each change.

I will skip all the trial and error I went through to get this far, and just share the program.

One thing I should mention — since the Saleae was showing times like 145.6 ms and such, I wanted to capture the time as accurately as possible. I used micros() on the Arduino to capture microseconds. When I went to play this back, I saw that my delayMicroseconds() were not working. I found this note:

TCurrently, the largest value that will produce an accurate delay is 16383; larger values can produce an extremely short delay. This could change in future Arduino releases. For delays longer than a few thousand microseconds, you should use delay() instead.

delayMicroseconds() – Arduino Reference

Because of this, my choice was to use delay() and milliseconds (which might throw timing off, if those partial milliseconds matter — probably they don’t), or to use a combination of delay() for the large amount, then delayMicroseconds() for the remaining amount. Even then, this wouldn’t be 100% accurate since there is still the overhead of doing two library calls instead of just one.

ChatGPT provided me with this snippet:

void customDelayMicroseconds(unsigned long delayTimeMicros) {
    unsigned long delayTimeMillis = delayTimeMicros / 1000;
    unsigned long remainingMicros = delayTimeMicros % 1000;

    delay(delayTimeMillis); // This will not delay if delayTimeMillis is 0
    delayMicroseconds(remainingMicros); // This will handle the remaining microseconds
}

You will see this routine used in my program, below. Note that, since I am PRINTING OUT each pulse time (on, off, and time), it’s slowing it down anyway, so even with my “more accurate” delay, the timing will still be off from the Control Box. Hopefully this will not matter…

Here we go…

// LethalLilyWriter.ino
/*-----------------------------------------------------------------------------
 
 Lethal Lily Swamp Witch animatronic Control Box writer.
 By Allen C. Huffman (alsplace@pobox.com)
 www.subethasoftware.com
 
 This program writes the data to the Lethal Lily head over a
 serial and digital input.
 
 Hardware:
 
 https://sviservice.com/collections/2023/products/sv2323794
 
 Documentation:
 
 CONFIGURATION:
 1. Define the pins on the Arduino that will be used for TX in
 .  the Software Serial library (connected to the green wire), and the pulse
 .  sending pin (connected to the blue wire).
 
 VERSION HISTORY:
 2024-06-13 0.00 allenh - Created based on LethalLilyReader.ino
 2024-06-14 0.01 allenh - Changed to PIN 13 so onboard LED will blink.
 
 TODO:
 * TODO...
 
 TOFIX:
 * TODO...

 ---------------------------------------------------------------------------*/ 

/*--------------------------------------------------------------------------*/
// Configuration
/*--------------------------------------------------------------------------*/
#define BAUD_RATE       550
#define GREEN_WIRE_PIN  12  // Serial TX to HEAD
#define BLUE_WIRE_PIN   13   // Pulses OUT to HEAD

/*--------------------------------------------------------------------------*/
// Includes
/*--------------------------------------------------------------------------*/
#include <SoftwareSerial.h>

/*--------------------------------------------------------------------------*/
// RX pin connected to GREEN wire.
// TX pin not used.
/*--------------------------------------------------------------------------*/
SoftwareSerial mySerial(3, GREEN_WIRE_PIN); // RX, TX

/*--------------------------------------------------------------------------*/
// Globals
/*--------------------------------------------------------------------------*/
volatile unsigned int g_pulseCount = 0;

// Pattern one ON and OFF pulse time in micros.
const unsigned long g_pattern1[] = {
    108336,
    185892,
    161408,
    328592,
    200140,
    156160,
    191696,
    161860,
    222340,
    507668,
    63712,
    236864,
    481116,
    97364,
    77224,
    230036,
    189148,
    128228,
    219716,
    153316,
    144596,
    55636,
    66692,
    92192,
    99984,
    73600,
    158388,
    205228,
    244916,
    141988,
    97352,
    114224,
    194472,
    103252,
    239248,
    575908,
    459728,
    16744,
    64608,
    59992,
    64688,
    59916,
    65168,
    59624,
    64788,
    59820,
    67140,
    287636,
    400560,
    325912,
    225148,
    145256,
    80220,
    109232,
    278032,
    175556,
    253068,
    156324,
    174984,
    381868,
    333784,
    158776,
    108328,
    164388,
    108324,
    242136,
    297388,
    195164,
    155604,
    100400,
    269976,
    153988,
    211304,
    136536,
    175524,
    133576,
    147248,
    83664,
    91740,
    279872,
    381224,
    105904,
    83312,
    69700,
    119660,
    108528,
    216900,
    319000,
    139236,
    153120,
    69424,
    55812,
    114036,
    91828,
    127924,
    184272,
    216976,
    105800,
    133452,
    181064,
    139080,
    128108,
    69424,
    67880,
    122192,
    58644,
    74852,
    81140,
    77672,
    142156,
    60888,
    119856,
    61256,
    156028,
    77752,
    122588,
    63792,
    103344,
    102604,
    92016,
    91632,
    83948,
    94256,
    125492,
    75016,
    97204,
    111228,
    80860,
    155744
};


//Prototypes
uint8_t convertValue (unsigned int value);

/*--------------------------------------------------------------------------*/
// Setup
/*--------------------------------------------------------------------------*/
void setup() {
    // Arduino console port.
    Serial.begin(9600);
    while (!Serial);

    // Control Box serial baud rate.
    mySerial.begin(BAUD_RATE);

    pinMode(BLUE_WIRE_PIN, OUTPUT);

    for (int idx=0; idx<10; idx++) Serial.println();
    Serial.println("LethalLillyWriter - "__DATE__" "__TIME__);
}

/*--------------------------------------------------------------------------*/
// Main loop
/*--------------------------------------------------------------------------*/
void loop()
{
    Serial.println ("Sending pattern 1...");

    mySerial.write (convertValue (8)); // End any sequence.

    delay (1000);

    mySerial.write (convertValue (1)); // Sequence 1
    
    delay (745);

    for (int idx=0; idx < sizeof(g_pattern1)/sizeof(g_pattern1[0]); idx++)
    {
        if (idx & 1)
        {
            digitalWrite(BLUE_WIRE_PIN, HIGH);
            Serial.print ("On for ");
        }
        else
        {
            digitalWrite(BLUE_WIRE_PIN, LOW);
            Serial.print ("Off for ");
        }
        Serial.println (g_pattern1[idx]);
        
        // Large values do not work for delayMicroseconds(), so a custom
        // function is used to delay using ms, then leftover time in us.
        customDelayMicroseconds (g_pattern1[idx]);
    }

    Serial.println ("Done.");

    while (1);
} // end of loop()


/*--------------------------------------------------------------------------*/
// Convert number (1-8) to Lethal Lilly byte format (0011 1100).
/*--------------------------------------------------------------------------*/
uint8_t convertValue (unsigned int value)
{
    uint8_t returnByte = 0;

    if ((value < 1) || (value > 8))
    {
        Serial.println ("convertValue must be 1-7 (pattern) or 8 (end).");
        returnByte = 0;
    }
    else
    {
        returnByte = value | (~value << 4);
    }

    return returnByte;
}

// Created by ChatGPT.
void customDelayMicroseconds(unsigned long delayTimeMicros) {
    unsigned long delayTimeMillis = delayTimeMicros / 1000;
    unsigned long remainingMicros = delayTimeMicros % 1000;

    delay(delayTimeMillis); // This will not delay if delayTimeMillis is 0
    delayMicroseconds(remainingMicros); // This will handle the remaining microseconds
}

// End of LethalLilyWriter.ino

This program will send serial data out at 550 baud on Arduino pin 12 (which will hook to the green wire going in to the head), and pulse data on pin 13 (which will go to the blue wire in to the head). I chose pin 13 for the pulse line because it also has an LED connected to it on the UNO, and this will blink in time with the pulses being sent to the head.

The Arduino I/O pins are only 5V, while the Control Box pulses at 5.4V. I expect this may cause some differences in what the Arduino sends versus what the Control Box sends. Let’s find out!

Arduino, meet Lethal Lily’s head

I used the Control Box to provide power to the head via the red and black wires. I also connected the black ground wire to the Arduino, giving everything a common ground (bad things happen if you don’t do this, as I found out years ago when dealing with programmable LED strips using external power supplies).

My wiring looked like this:

Control Box  Head         Arduino UNO
----------- ----------- -----------
Green Green--------PIN 12
Red----------Red
Black--------Black--------GND
Blue Blue---------PIN 13 (build in LED)

My hope was that the Control Box could act as a power supply, providing 5.9V DC to the head, then the Arduino could act as the Control Box sending serial data on pin 12 to the head’s green wire, and pulsing data on pin 13 to the head’s blue wire.

My program sends the byte code for pattern 1, pauses (using the delay I could see from the Saleae Logic Analyer), then starts sending ON and OFF pulses using the time values I captured in the LethalLilyReader program. When the last pulse is sent, I do a short delay and then send out the “end” byte.

When I went to test it, it actually did something! The head does indeed animate, but the mouth did not move at all. Perhaps the low 5V power of the Arduino UNO’s I/O pin is not enough to match what the head expects. The Control Box sends pulses at 5.4V and perhaps at a much higher amperage than the Arduino is capable of.

This was an exciting first step.

Some questions…

  • Why does the mouth not work? Is this due to the Arduino only able to pulse at 5V instead of 5.4V like the Control Box?
  • What do the pulses actually mean? How can we control the servos in the head to create custom animations?

Some answers…

I tried writing a test program to send pulses at different rates, but nothing happened. It does look like the blue wire expects something beyond a digital I/O signal. I decided to take a chance and see what happens if I route power (from the Control Box red wire) in to the blue wire. I tapped a wire between the two, quickly, and saw … the mouth opened!

It looks like the blue wire has nothing to do with the servos at all! It just controls the mouth. The pulses I was capturing are only the mouth movements, to align with the audio being played by the Control Box! All the motion in the head (eyes, eyelids, neck) was just being done by the head itself when it receives the start code byte!

To test this, I made a simple program that would send the start byte code… and the head began animating. The pulses had nothing to do with the head animation (other than the mouth).

I guess I should have started there!

The bad news…

The bad news is that it looks like we cannot control the servos in the head, as-is. If we were hoping to make the animatronic look in specific directions or blink at specific times … that is not possible. While it may be possible to hack the head and get access to all the wires running to the servo, that was not I was hoping for. Also, since the blue wire needs a signal with more power than what the Arduino I/O pin can provide, there will be some extra hardware (like a relay) needed to control the mouth.

The good news…

The good news is — now we don’t have to learn how to control all those servos! The head will just “do its thing” and we can control the mouth to match custom audio. This is probably good enough for most things — an animated prop that delivers a custom spiel.

To test this, I took the power out from the Control Box (red/black wires) and ran them throuh an Arduino Relay Shield that then connected to the head’s blue/black wires. This would allow me to pulse the 5.9V coming out of the Control Box power supply to the head’s blue wire.

And it worked!

Here is the first simple test I did, where I played some audio and manually toggled the relay (that is the clicking sound you will hear) to make the mouth open/close — like a puppet :)

Lethal Lily Swamp With as … the Ghost Host.

At this point, I believe I understand what I need to do to. By using a cheap triggered MP3 player (either an Arduino Shield, or some external board), and a relay, it should be quite easy to make Lily “speak” any audio I want her to.

My electronics (MP3 players, etc.) are in storage currently, so I am not sure how soon I can get to it, but I hope that this series will be…

To be continued…