Using Servos with Vixen and Arduino

Joe Paul

Supporting Member
Hi Folks,

So I have been getting unexpected behavior using code from here:

https://www.instructables.com/Talking-Pumpkins/

But I am using it to animate Peanuts figures like I've done here:


I the motion above is random; I want to time it to the music.

So my question is about using an older version of Vixen. I am using Win 11 with the latest release of Vixen.
What would be the best version to downgrade into. I suspect that the code on that page is over 10 years old. I am very familiar with using servos, understand their power requirements, etc. I suspect it has something to do with the way the serial is being sent to the Arduino. The code works intermittently it seems. Seems the serial communication is being corrupted or garbled.

Any help much appreciated!

Thanks and wising everyone a very Blessed and Merry Christmas!!!!!!

Take care, Joe.
 
The serial code has not changed in Vixen for many years, so I doubt downgrading would change anything. I would look more at the serial data coming into the Arduino and see what that looks like and validate there is something wrong there first. We have other users that have been using Arduino based controllers to run their lights so the general serial port functionality works.
 
The code that is posted in the instructable is not great code. It can lose its place in the incoming job stream and put the wrong channels on the servo.

Here is an example "start code"

In the definition section
// Generic Serial controller config - must be present, must match controller setup
const int CONTROLLER_HEADER[3] = {33, 34, 35}; // This is character !"# (hard to replicate in sequencer)
const int SIZE_OF_HEADER = sizeof(CONTROLLER_HEADER) / sizeof(int); // no need to change

in loop add this before reading the serial data bytes
waitForControllerHeader(CONTROLLER_HEADER);

// this this will read the incoming bytes of data looking for the three character start bytes
void waitForControllerHeader(int header[])
{
for (int i = 0; i < SIZE_OF_HEADER; i++) {
// wait for serial available
while (!Serial.available()) {}
// Check the byte until it matches the CONTROLLER_HEADER byte
int inByte = Serial.read();
if (inByte != CONTROLLER_HEADER) {
i = -1; // wrong data set to "zero"
}
}
// found the header!
}

In Vixen, put !"# in the start characters in the controller setup.

Try that and report success or failure.
 
The serial code has not changed in Vixen for many years, so I doubt downgrading would change anything. I would look more at the serial data coming into the Arduino and see what that looks like and validate there is something wrong there first. We have other users that have been using Arduino based controllers to run their lights so the general serial port functionality works.

Many thanks for the reply!!! I've spent the better part of 3 days on this so far. I have been futzing with this code
https://github.com/jackStalnaker/singingpumpkins/blob/master/singingpumpkins.ino
But I can only get 3 servos working no matter how I try to expand or remove what I don't need. If I add in up to 8 servos, I get randon jittering. I think the code could be simplified but although I can get it to compile, I get those unexplained jitters. I've worked with serial with the ESP32 and Bluetooth, but this has me stumped. But I'm not giving up!

Take care, Joe.
 
The code that is posted in the instructable is not great code. It can lose its place in the incoming job stream and put the wrong channels on the servo.

Here is an example "start code"

In the definition section
// Generic Serial controller config - must be present, must match controller setup
const int CONTROLLER_HEADER[3] = {33, 34, 35}; // This is character !"# (hard to replicate in sequencer)
const int SIZE_OF_HEADER = sizeof(CONTROLLER_HEADER) / sizeof(int); // no need to change

in loop add this before reading the serial data bytes
waitForControllerHeader(CONTROLLER_HEADER);

// this this will read the incoming bytes of data looking for the three character start bytes
void waitForControllerHeader(int header[])
{
for (int i = 0; i < SIZE_OF_HEADER; i++) {
// wait for serial available
while (!Serial.available()) {}
// Check the byte until it matches the CONTROLLER_HEADER byte
int inByte = Serial.read();
if (inByte != CONTROLLER_HEADER) {
i = -1; // wrong data set to "zero"
}
}
// found the header!
}

In Vixen, put !"# in the start characters in the controller setup.

Try that and report success or failure.


Thanks for the reply!!!!
I will try it.
The header and servo code works fine from here
https://github.com/jackStalnaker/singingpumpkins/blob/master/singingpumpkins.ino
but only for 3 servos no matter how I expand/edit what's there.

Still working on it!! Take care, Joe.
 
You could try this code I knocked up- it is UNTESTED but shows the basic concepts. I've almost certainly messed something up.

Assumptions - you have 8 channels of data in your controller outputs (corresponding to 8 vixen 'nodes' / timeline items). You can increase this but the code will need to be modified In Vixen set the header to just >>, and the baud rate to 115200. Compared with the example above and others oniine, this code does not read the number of channels from header data. Rather it is fixed at 8, and relies on there being 8 output channel on the controller therefore.

Code:
code removed to save confusion! See a later version which will work! This doesn't include the channel count as data in the 'header' but it would be feasible to add that in to make the code more 'dynamic'. You're likely to want to assign servo pins one by one though so it's not hugely important for this use case.

See post #11
 
Last edited:
I think that there needs to be another 'while (!Serial.available());' before the second Serial.read(). This is because (afaik) serial.read() is non-blocking.
 
Hi Folks,

So I couldn't get the code to compile -- getting an error message about the "continue" not being in a loop.
So I am about to try this:
Code:
void loop() { 
    while(!Serial.available()); 

    //set Vixen header to '>>'. This code is expecting that!
    for( ; ; )    
    {if(Serial.read() != '>') {continue;}}
    
     while(!Serial.available()); 
   
    for( ; ; )    
    {if(Serial.read() != '>') {continue;}}

Trying it now. Will report back, Thanks, Joe.
 
Hi Richie,

So here's the deal. First, many, many thanks!!!! Your code works without the header in my very quick testing. I haven't yet done a lot of effects, or tested it after a restart. I couldn't get the header to work.

Here's what's happening--
Servo.......Pin
1...........10
2...........3
3...........4
4...........5
5...........6
6...........9
7...........7
8...........8

Pins are out of order, but if behavior is consistent, this can be worked out. Don't know if this will change as I use it and experiment.

I think we are very close to solving the header issue. Interesting that some of the pins are not PWM pins yet drive the servos.

Your code without the header code:

Code:
#include <Servo.h>
#define numChannels 8

Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;
Servo servo5;
Servo servo6;
Servo servo7;
Servo servo8;


void setup() {
   Serial.begin(115200);

   //attach servo objects to output pin numbers
   servo1.attach(3);
   servo2.attach(4);
   servo3.attach(5);
   servo4.attach(6);
   servo5.attach(7);
   servo6.attach(8);
   servo7.attach(9);
   servo8.attach(10);
}


void loop() {
   



     //define an array of the size of the number of channels, to store the channel values
     byte channelValues[numChannels];

     for(byte iChn = 0; iChn < numChannels; iChn++){
       while(!Serial.available());
       channelValues[iChn] = Serial.read(); //read channel value from serial data
     }

     //servo library needs values in range  0->180, whereas vixen data is in range 0->255 (1 byte)

     servo1.write(map(channelValues[0], 0,255, 0, 180));
     servo2.write(map(channelValues[1], 0,255, 0, 180));
     servo3.write(map(channelValues[2], 0,255, 0, 180));
     servo4.write(map(channelValues[3], 0,255, 0, 180));
     servo5.write(map(channelValues[4], 0,255, 0, 180));
     servo6.write(map(channelValues[5], 0,255, 0, 180));
     servo7.write(map(channelValues[6], 0,255, 0, 180));
     servo8.write(map(channelValues[7], 0,255, 0, 180));
}

If anyone has an explanation, info is most welcome!!!!

Thanks again, Everyone!!!!!!!

Merry Christmas, Everyone!!!!!!!

Take care, Joe.
 
Last edited:
Hi Folks,

So the channels change if I restart Vixen, but not if I close the sequence.
I'll try some suggestions for the header issue, and report back.

Take care, Joe.
 
Okay this IS tested in the Arduino IDE (without actual servos) and should work. In Vixen, set the header to >>, make sure you have 8 outputs on your controller.

Re your previous point, from what I have read I think the servo library works with any digital out pins, you don't need to use the PWM pins specifically.

Code:
#include <Servo.h>
#define numChannels 8

byte channelValues[numChannels];
Servo servo[numChannels];

void setup() {
  //start serial port
  Serial.begin(115200);
  
  //init servo objects
  servo[0].attach(10);
  servo[1].attach(3);
  servo[2].attach(4);
  servo[3].attach(5);
  servo[4].attach(6);
  servo[5].attach(9);
  servo[6].attach(7);
  servo[7].attach(8);
}

void loop() {  
  for(;;){
    //check for 2 consecutive '>' chars
    while(!Serial.available());
    if(Serial.read() != 0x3E) {continue;}
    
    while(!Serial.available());
    if(Serial.read() != 0x3E) {continue;}
        
    //now read the next bytes (qty = numChannels) into the channelValues array
    for(byte i=0; i<numChannels; i++){
      while(!Serial.available());
      channelValues[i] = Serial.read();
    }

    updateServoPositions();//this function updates the servos
  }
}

void updateServoPositions(){
  for(byte i=0; i<numChannels; i++){
    servo[i].write(map(channelValues[i], 0, 255, 0, 180));
  
    /*uncomment this to print test values to IDE serial monitor
    Serial.print("controller output ");
    Serial.print(i);
    Serial.print(" value = ");
    Serial.print(channelValues[i]);        
    Serial.print("/255 or ");
    Serial.print(map(channelValues[i], 0, 255, 0, 180));
    Serial.println("/180 degrees");
    */
  }
}

You can test this in the IDE serial monitor, e.g. type in >>12345678 and hit the 'send' button. To see the conversions in the serial monitor, uncomment the last block. As mentioned before you can't use the IDE at the same time as Vixen as the COM port gets locked. This can be adapted for more / less servos, but make sure the number of outputs in your Vixen 'controller' matches. The array is (as is normal) zero indexed so they are known as 0-7 in the code.

in ASCII character 1 = 49, 2 = 50, 3=51 etc....

The ACII code for > is 62, so in your sequence if you have two adjacent channels which are outputting 62 this will temporarily mess up the header. But this is unlikely to happen.
 
Last edited:
You could try this code I knocked up- it is UNTESTED but shows the basic concepts. I've almost certainly messed something up........

Hi Richie and Everyone,

So you were great with your code. Sven on the MERG forum suggested to use "break" instead of "continue."

Also I discovered that we needed "==" instead of "!=" in the line.

This has worked after several restarts; no mis mapping:

Code:
     while(!Serial.available());

     //set Vixen header to '~!'. Ths code is expcting that!
     for( ; ; )
     {if(Serial.read() == '~') {break;}}

      while(!Serial.available());
     for( ; ; )
      {if(Serial.read() == '!') {break;}}

      while(!Serial.available());


So everything seems great now!!!!!

Many thanks, Everyone for all the suggestions and moral support!!!!!!!!

I have been wanting to do this for years!!!! What a Christmas present!!!!!!!

Merry Christmas, Everyone!!!!!

Take care, Joe.
 
Last edited:
Cool - yes that's a different way of doing the same thing. If something is NOT true then stay in the loop. or if something IS true then exit the loop. The most recent version I posted above should work, there were a few probs with my V1.

Header is crucial for correct framing, or the code doesn't know for sure which byte is for which channel.
 
Cool - yes that's a different way of doing the same thing. If something is NOT true then stay in the loop. or if something IS true then exit the loop. The most recent version I posted above should work, there were a few probs with my V1.

Header is crucial for correct framing, or the code doesn't know for sure which byte is for which channel.

I didn't see your last code update. Wish I had. All kinds of things were acting up and the Arduino IDE wasn't showing my changes, then I accidentally disconnected wires, and Vixen unmatched my outputs --
lost hours!

Thanks again!!
 
Have a play with the above in the Serial Monitor in the IDE (uncomment the serial.println lines in the code)- that will give you a good feel for how it works. It will only read and process the 8 bytes immediately after receiving >>. You can try submitting different strings. Remember that any character is just a representation of 0->255 with ref to the ASCII values. Vixen baically repeats the frame of data over and over again with the header and the current channel values. When a sequence isn't playing, it will still be outputting zeros as the channel values.

I'm curious - if you type in >>AAAAAAAA in the IDE that should set all your 8 servos to 45 degrees. Char A is 65(/255) which is equivalent to 45(/180). You'll want to comment those lines when running the code proper though since it will run faster without the unnecessary serial.println(.....) commands.

Forgive me if I'm explaining basics, it's hard to know what level to pitch things at!

MERG in the UK is the Model Eletconics Railway Group - or is this something else? Are you UK based?
 
Last edited:
MERG in the UK is the Model Electronics Railway Group - or is this something else? Are you UK based?

I am in the U.S., Pennsylvania. I joined MERG a few years ago for electronics help with my eccentric Lionel train projects. The group is very polite but they don't quite understand why I do what I do. My quirky projects here:
https://www.youtube.com/user/christmasgarden
Personal web site, much outdated: https://josephrampolla.com/

If I may make a suggestion? I think your code and instructions would be good on GetHub.

Thanks, again, for the help!!!!!!!

Take care, Joe.
 
Last edited:
Glad the code worked for you, was a useful exercise for me writing it, i learned a few things! As a programmer one always ends up revisiting previous written code and improving it.

I looked at MERG a while back, was interested in some of the loco electonics they offer, though I don't have space for trains currently. I'll be curious to see the video of your completed Vixen project! I'be never played with servos (well not true, I fly RC planes) but not from Arduino/Vixen. I do use Arduinos in my Vixen-driven christmas show, as a pixel controller. The current installation uses 5 x nanos with ethernet shields - videos at https://youtube.com/richienorthcott
 
Glad the code worked for you, was a useful exercise for me writing it, i learned a few things! As a programmer one always ends up revisiting previous written code and improving it.

I looked at MERG a while back, was interested in some of the loco electonics they offer, though I don't have space for trains currently. I'll be curious to see the video of your completed Vixen project! I'be never played with servos (well not true, I fly RC planes) but not from Arduino/Vixen. I do use Arduinos in my Vixen-driven christmas show, as a pixel controller. The current installation uses 5 x nanos with ethernet shields - videos at https://youtube.com/richienorthcott

I am always running behind in my projects. Yes, I will make a video!

I have gotten the Arduino with Vixen to work with the HC-06 Bluetooth transceiver. It's just a matter of connecting TX to RX, etc., pairing, and you don't have to have a physical connection to a computer. The baud rates have to match. The trickiest part is changing the default baud rate from 9600 to 115200 with the FTDI board.

There are ways to have a very small set-up with the trains and there is a very affordable system called DCC++ that uses Arduino and free software on your computer. Some of the DCC decoders are only about $22.

One idea for a small set-up is something like a Murphy bed that folds against the wall. HO scale flextrack can be bent to a 24" diameter. Some equipment can navigate the curve.

I learned programming back in 1976 in college, in BASIC Plus. Love the Sinclair 1000 and PicAxe chips. I'm old!
 
The serial code has not changed in Vixen for many years, so I doubt downgrading would change anything. I would look more at the serial data coming into the Arduino and see what that looks like and validate there is something wrong there first. We have other users that have been using Arduino based controllers to run their lights so the general serial port functionality works.

Thanks for the reply! I missed it, somehow. I tried to downgrade, but I futzed up. Things working well now wit Vixen 3. Some of the examples I was using were done about 10 years ago with Vixen 2.

Take care, Joe.
 
Back
Top