Register | Sign in

Home > Touch Control System (TCS) > TCS Tutorial: Arduino Libraries/Shields & RGB LEDs


TCS Tutorial: Arduino Libraries/Shields & RGB LEDs

Double, double toil and trouble: how-to make a spooky touchscreen operated cauldron that's sure to spice up your Halloween




Arduino Example

This TCS & Arduino example outlines the creation of a pretty impressive cauldron for the upcoming Halloween celebrations including a moving spoon, lighting effects, and some spooky audio playback. This example is a little more on the intermediate side as far as the hardware and Arduino programming is concerned. If you are new to the Arduino IDE software, check out the previous TCS & Arduino posts to get a little more familiar with how Arduino systems work and the syntax for coding.

 

Hardware

The main components used include:

Arduino Mega

Arduino Micro

Adafruit Music Maker shield

28YBJ-48 DC 5V 4 Phase 5 Wire Stepper Motor With ULN2003 Driver Board

Battery connector (Voltage should be between 7v and 12v)

Several LEDs – RGB Diffused Common Anode

Resistors

Home made cauldron

 

Schematic

We found that the Mega had a bit of trouble handling the motor and LEDs while playing an audio file from the Music Maker shield. In an effort to keep a smooth running project, we enlisted the help of the Micro. The motor is controlled by the Micro while the Mega will handle playing audio, adjusting the LEDs, and triggering the Micro.

Check out Adafruit’s guide for preparing and wiring the Music Maker Shield. If you are using a Leonardo or Mega, make sure to solder the SPI jumpers and although it is not clearly stated in their guide, make sure to connect the shield to the controller’s 5v source and ground.

This schematic depicts the use of only 3 RGB LEDs and lists their respective resistor values. However, you can add more to each group. The more LEDs you have the brighter your cauldron will be. This LED calculator is a great resource to get wiring diagrams, resistor values, and even the resistor color codes.

The motor shield needs a source voltage between 5v and 12v. Although it is possible to use an Arduino board to power the motor shield, it is always recommended to use an external power source for these types of devices.

The Arduino Micro can be powered through it’s VIN pin. Ardunio recommends using a 7v-12v power source. Since the motor shield can handle anything between 5v and 12v, a 9v battery will work for both devices.


Arduino Micro Code

The Micro will only be handling the motor to spin the spoon around inside of the cauldron. If you are using a 28YBJ-48 motor, you will find that Arduino’s built in stepper motor functions don’t work. Instead, you will need to either type out the code to turn the motor or use a custom library. There are many examples for this motor, but the one we are using can be found here. This guide does not have links to download the library files, but I have included them in the downloads section at the bottom of this post.

If you are new to the concept of libraries, check out Arduino’s Libraries page as well as their guide for installing new libraries.

//Library, wiring diagram, and basic source code from http://engyfun.blogspot.com.au/2015/02/here-is-source-code-for-our-28byj48.html
#include <StepperMotor.h>
StepperMotor motor(8,9,10,11);
int analogPin =A5;
void setup(){
  Serial.begin(9600);
  motor.setStepDuration(1);
 
}
void loop(){
 int analogValue = analogRead(analogPin);
 
 
 if (analogValue > 200){
   
   motor.step(12400);
  delay(2000);
 
 
 }
   
}
                        

The first thing to do is to include the StepperMotor library. The library handles the communication between the Micro and the motor board. This helps keep our Arduino project nice and short. Next we tell the system which digital pins are being used for the motor and initiate the analog pin that we connected to the Mega. The void loop function checks the status of our analog pin and if it is receiving voltage, executes the motor.step function that is included in the StepperMotor Library. For this example, I used 12400 steps which turns the motor approximately 3 revolutions and set a 2 second delay.

You can expand on this by adding different scenarios based off of the analog value. For example, you could have the motor turn in reverse or change the amount of time the motor turns.


Arduino Mega Code

We are using the Arduino Mega to talk with TCS, trigger the motor, and handle the LED values. The majority of the code is wrapped up in the Music Maker’s player_interrupts example in the Adafruit VS1053 Library. Before trying to compile and upload the configuration, make sure to download and import the necessary library.

 

// include SPI, MP3 and SD libraries
#include <SPI.h>
#include <Adafruit_VS1053.h>
#include <SD.h>
#define SHIELD_RESET  -1      // VS1053 reset pin (unused!)
#define SHIELD_CS     7      // VS1053 chip select pin (output)
#define SHIELD_DCS    6      // VS1053 Data/command select pin (output)
#define CARDCS 4     // Card chip select pin
#define DREQ 3       // VS1053 Data request, ideally an Interrupt pin
Adafruit_VS1053_FilePlayer musicPlayer =
  Adafruit_VS1053_FilePlayer(SHIELD_RESET, SHIELD_CS, SHIELD_DCS, DREQ, CARDCS);
 
  
int ledR1 = 8;
int ledG1 = 9;
int ledB1 = 10;
int ledR2 = 11;
int ledG2 = 12;
int ledB2 = 13;
int ledR3 = 44;
int ledG3 = 45;
int ledB3 = 46;
int motorPin = 2;
void setup() {
  Serial.begin(9600);
  pinMode(motorPin, OUTPUT);
    
  analogWrite(ledG1, 255);
  analogWrite(ledG2, 255);
  analogWrite(ledG3, 255);
Serial.println("Adafruit VS1053 Library Test");
  // initialise the music player
  if (! musicPlayer.begin()) { // initialise the music player
     Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
     while (1);
  }
  Serial.println(F("VS1053 found"));
 
  if (!SD.begin(CARDCS)) {
    Serial.println(F("SD failed, or not present"));
    while (1);  // don't do anything more
  }
  Serial.println("SD OK!");
  
 
  // Set volume for left, right channels. lower numbers == louder volume!
  musicPlayer.setVolume(20,20);
  if (! musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT))
    Serial.println(F("DREQ pin is not an interrupt pin"));
    
}
void loop() {  
   
 
 
  if (Serial.available()) {
    char ser = Serial.read();
    
   
     
if(ser == 'a'){
        
  analogWrite(motorPin, 200 );  // analogRead values go from 0 to 1023, analogWrite values from 0 to 255
     
     delay(10);
     analogWrite(motorPin, 0 );
     
    
  analogWrite(ledG1, 25);
  analogWrite(ledG2, 25);
  analogWrite(ledG3, 25);
  analogWrite(ledR1, 255);
  analogWrite(ledR2, 255);
  analogWrite(ledR3, 255);
if (! musicPlayer.startPlayingFile("Track1.wav")) {
    Serial.println("Could not open file");
    while (1);
  }
  Serial.println(F("Started playing Scene 1"));
  while (musicPlayer.playingMusic) {
    // file is now playing in the 'background' so now's a good time
    // to do something else like handling LEDs or buttons
    
    }
    
    Serial.print(".");
    delay(1000);
    
   
  Serial.println("Done playing music");
      delay(13820);
    analogWrite(ledR1, 0);
    analogWrite(ledR2, 0);
    analogWrite(ledR3, 0);
    analogWrite(ledG1, 255);
    analogWrite(ledG2, 255);
    analogWrite(ledG3, 255);
    
 
  }
if(ser == 'b'){
        
       analogWrite(motorPin, 200 );  // analogRead values go from 0 to 1023, analogWrite values from 0 to 255
     
     delay(10);
     analogWrite(motorPin, 0 );
     
    
    analogWrite(ledR1, 255);
    analogWrite(ledR2, 255);
    analogWrite(ledR3, 255);
    analogWrite(ledG1, 0);
    analogWrite(ledG2, 0);
    analogWrite(ledG3, 0);
if (! musicPlayer.startPlayingFile("Track2.wav")) {
    Serial.println("Could not open fil2");
    while (1);
  }
  Serial.println(F("Started playing Scene 2"));
  while (musicPlayer.playingMusic) {
    // file is now playing in the 'background' so now's a good time
    // to do something else like handling LEDs or buttons
    
    }
    
    Serial.print(".");
    delay(1000);
    
   
  Serial.println("Done playing music");
      delay(13820);
      digitalWrite(ledR1, 0);
    digitalWrite(ledR2, 0);
     digitalWrite(ledR3, 0);
   
 
  }
if(ser == 'c'){
        
       analogWrite(motorPin, 200 );  // analogRead values go from 0 to 1023, analogWrite values from 0 to 255
     
     delay(10);
     analogWrite(motorPin, 0 );
     
    
    analogWrite(ledB1, 255);
    analogWrite(ledB2, 255);
    analogWrite(ledB3, 255);
    analogWrite(ledG1, 0);
    analogWrite(ledG2, 0);
    analogWrite(ledG3, 0);
if (! musicPlayer.startPlayingFile("Track3.wav")) {
    Serial.println("Could not open file");
    while (1);
  }
  Serial.println(F("Started playing Scene 3"));
  while (musicPlayer.playingMusic) {
    // file is now playing in the 'background' so now's a good time
    // to do something else like handling LEDs or buttons
    
    }
    
    Serial.print(".");
    delay(1000);
    
   
  Serial.println("Done playing music");
      delay(13820);
    analogWrite(ledB1, 0);
    analogWrite(ledB2, 0);
    analogWrite(ledB3, 0);
    analogWrite(ledG1, 255);
    analogWrite(ledG2, 255);
    analogWrite(ledG3, 255);
   
 
  }
  }
}
                        

With this code, we setup 3 different “Scenes” based off of the serial command TCS sends to the Mega. The different scenes all activate the motor, assigns new values for the LEDs, and plays an audio track. When a scene is not being played out, all of the RGB LEDs turn green as their default state. The value set for the motor trigger is arbitrary. You can change this function to equal a certain number, or modify the function to perform different tasks based on the analog value being sent.



The Screen

The TCS screen contains 3 cauldron objects representing the different scenes and a few lights. Name the objects according to their prospective scene. control_cauldron1, control_cauldron2, control_cauldron3.



The Models

Three models have been included in the below downloads section with their diffuse color map. Each model contains the same cauldron, but each model has their scene number above in their corresponding colors. Play around with the models and make this TCS screen your own. When exporting, name the models cauldron1.fbx, cauldron2.fbx, and cauldron3.fbx.




Start up Script

The start up script only runs when the TCS mod is loaded. First, we remove the labels from the models.

setup.DrawLabels = false

Set the ambient light.

setup:SetAmbientLight(.8, .8, .8)

Setup the global variables.

tcs.CustomProperties["started"] = 0
tcs.CustomProperties["act"] = 0

Assign the dummy objects in the cauldron_screen.fbx file to the models.

setup:SetControlModel("control_cauldron1","cauldron1")
setup:SetControlModel("control_cauldron2","cauldron2")
setup:SetControlModel("control_cauldron3","cauldron3")

Use the scale of the models rather than the dummy objects.

setup:SetControlUseParentScale("control_cauldron1", false)
setup:SetControlUseParentScale("control_cauldron2", false)
setup:SetControlUseParentScale("control_cauldron3", false)

 

Update Script

The update script will continuously run over and over again. We want to connect to the Arduino and set global variables to reference each model. However, we don’t want TCS to keep trying to open the same connection and we don’t need TCS to keep declaring the same values to the model variables . We will use the global variable ‘started’ (declared in the startup script) with an ‘if’ statement so that this function is only ran the first time the update script is processed.

if (tcs.CustomProperties["started"] == 0) then
    tcs.CustomProperties["started"] = 1
    tcs:SerialCreate("mega", "COM3", 9600, 2, 8, 1, 2000, 2000)
    tcs:SerialOpen("mega")
    scene1 = tcs:GetControl("control_cauldron1")
    scene2 = tcs:GetControl("control_cauldron2")
    scene3 = tcs:GetControl("control_cauldron3")
end

This project is completely dependent upon user input, so there is nothing else we need in the update script.


Touch Down Script

The touch down scripts are ran when an object in the TCS screen is activated. Each cauldron object will need it’s own touch down script. Similar to the update script, we will just use a single ‘if’ statement.

if (tcs.CustomProperties["act"] == 0) then
    tcs.CustomProperties["act"] = 1
    model.CustomProperties["Glow_Amount"] = .4
    tcs:SerialWrite("mega", "a")
    tcs:AddScriptTimer("script1", "scene",13820)
end

We started by using the ‘act’ variable declared in the startup script. We use this variable to determine if a scene is already in process. When a cauldron is pressed and the ‘act’ variable is 0, we change the ‘act’ variable to 1, the glow amount of the model is adjusted, a command is sent to the Arduino board, and a timed script is ran. By setting the ‘act’ variable to 1, we stop any other cauldron from being activated. This is to make sure that your scenes play out completely and that they accurately depict what your cauldron is doing.

Make sure to change the serial write command for each touch down script. The Arduino Mega is programmed to activate scene 1 with the command “a”, scene 2 with the command “b”, and scene 3 with the command “c”. Name the script files:

control_cauldron1_touch_down.lua

control_cauldron2_touch_down.lua

control_cauldron3_touch_down.lua

 

AddScriptTimer API Command

The add script timer command first calls out a script name within TCS. You can have many different scripts and use their name to run the script later on or stop the script using the following:

tcs:StartScriptTimer(“script1″)

tcs:StopScriptTimer(“script1″)

The second part of the add script line calls the actual .lua script that you want to run. For this project we are only using one script called scene.lua. Do not add the file extension .lua to the API command.

Finally, the add script line defines how long to wait before running the script in milliseconds. In the Arduino code for the Mega, there is a delay of 13820 milliseconds. This is approximately the length of time needed for the scenes to play out. If you adjust the length of a scene in the Arduino code (by changing the rotations of the motor.step function, changing the length of the audio file, etc.) you will want to change the time at the end of your add script line in the appropriate touch down script.

 

Timed Script

In the touch down scripts, we set the following scene.lua script to be ran at the end of our scenes.

scene1.CustomProperties["Glow_Amount"] = 0
scene2.CustomProperties["Glow_Amount"] = 0
scene3.CustomProperties["Glow_Amount"] = 0
tcs.CustomProperties["act"] = 0
tcs:StopScriptTimer("script1")

 

When this script is ran, all of the cauldron models have their glow amounts set to o, our ‘act’ variable is set back to 0 (so that another scene can be activated), and we tell TCS to stop running the script.


The Mod Editor

Check out TCS:Screens for the creation of the mod and make sure that you have all of the necessary models, glow maps, and scripts needed.


Build the mod and enjoy spooking out your neighbors!

 

 

Downloads

TCSandArduinoCauldron.zip

This includes:

scene.lua

control_cauldron1_touch_down.lua

control_cauldron2_touch_down.lua

control_cauldron3_touch_down.lua

cauldron_startup.lua

cauldron_update.lua

CauldronScreen.tcmProj

cauldron_screen.fbx

cauldron1.fbx

cauldron2.fbx

cauldron3.fbx

cauldron1Map.png

cauldron2Map.png

cauldron3Map.png

 

TCSCauldronArduino.zip

This Includes:

Micro ino

Mega ino

StepperMotor Library


Conclusion

This project covers several different aspects of what TCS can do with a couple Arduino boards. Modify and add on to the project to make it automated and custom to your preferences. Try modifying the direction and turning length of the spoon, play around with the different RGB LED groups, or even add a camera display to the screen so that you can watch the reactions. The possibilities are endless.



< TCS Home