ELECTRONIC EXPERIMENTS
Continuous Rotation Servo Motor
Blinking Chest Lights Rev 1.0
Blinking Chest Lights Rev 3.0
Sound Display VU Meter Using A Microphone
Printed Circuit Board for Chest Lights
Return to Skips Messy Workbench Homepage
CONTINUOUS ROTATION SERVO MOTOR
As it's name describes, a continuous rotation servo is a servo motor that rotates 360°. Regular servo motors can only rotate 180°. Other than rotation angle, following are some of the differences:
Beware that the two types of servo motors look exactly the same except for the label. Be sure to read the label on the servo motor. The following photo shows a continuous rotation servo motor on the left and a standard servo motor on the right. These motors are also wired the same, see the schematic of the circuit I used while learning about the continuous rotation servo motor.
How a continuous rotation motor works
This explanation will be assuming the motor will be wired to an Arduino micro processor board. It can be any of the various Arduino boards provided they have at least one output pin that is a Pulse Width Modulation (PWM) pin. On an Arduino Uno these pins are marked with a Tilde (~). For other board check the data sheet for that board.
The motor speed and direction are controlled by sending a PWM pulse to the motor between 0.50mS to 2.50mS. The pulse width is controlled by writing a value of 0 to 180 to the PWM pin.
Figures 1 through 3 show the pulse width that is sent to a continuous rotation servo motor to make it run. A short video is included of the motor and an oscilloscope screen showing the pulse change versus motor action.
Figure 1 shows a pulse width of 0.50mS. The width from 1.5mS to 0.50mS is 1.0mS and is created by values in the range of 0 to 89 being written to the PWM pin that the motor is attached to. When the pulse varies from 1.50mS to 0.50mS (value of 90 to 0), the motor will be turning clock-wise (CW). At a value of 89 the motor will be turning CW and running very slow. As the value being written is decrease toward zero (0), the motor will continue turning CW and will increase in speed until achieving full speed in the CW direction at a value of zero (0).
Figure 2 shows a pulse width of 1.50mS. When the pulse width is 1.50mS the value written to the PWM pin that the motor is attached to is 90 and the motor is stopped.
Figure 3 shows a pulse width of 2.50mS. The width from 1.50mS to 2.50mS is 1.0mS and is created by values in the range of 91 to 180 being written to the PWM pin that the motor is attached to. When the pulse varies from 1.50mS to 2.50mS (value of 91 to 180), the motor will be turning counter-clock-wise (CCW). At the value of 91 the motor will be turning CCW and running very slow. As the value being written is increased toward 180, the motor will continue turning CCW and will increase in speed until achieving full speed in the CCW direction at a value of 180.
Click to enlarge image
Schematic Diagram and Bill of Material (BOM)
Schematic Diagram
Here is the schematic diagram that is using an Arduino UNO. Most any Arduino board can be used as long as there is at least one PWM output pin available. The schematic was created using Easy EDA. Click the schematic image to enlarge.
Bill of Material (BOM)
Prices are as of March 11, 2024
Quantity | Description | Part Number | Vendor | ASIN Number | Price Per | Extended Price |
---|---|---|---|---|---|---|
1 | Arduino Uno or similar | Uno R3 [A000066] | Amazon | B008GRTSV6 | $27.60 | $27.60 |
1 | Continuous rotation servo | FS90R | Adafruit.com | N/A | $7.50 | $7.50 |
3 |
Male-to-Male wire jumpers Set of 130pcs 12, 16, 20, 25cm Only 3 wires required for this project |
41383 | Amazon | B016KI622U | $0.06 per wire | $0.18 |
Program Code Used to Test Motor with Explanation
The code in the following window can be selected, copied and
pasted directly into your IDE. It should work well on a
Windows or Mac. Simply place you mouse cursor where you
want to start copying and then click and drag to
select the text. Then copy the selection and paste it into
your Integrated Development Environment (IDE).
Program code I wrote to learn how to use the continuous rotation Servo
// Testing Continuous Servo Test Code
// John G. (Skip) Todora
// Date: February 29, 2024
/*
Servo.h Methods:
attach()
write()
writeMicroseconds()
read()
attached()
detach()
*/
/*
Motor Number: FS90R - From Adafruit.com
To control with an Arduino, Adafruit suggest connecting the orange control wire
to pin 9 or 10 and using the Servo library included with the Arduino
IDE.
Write a value of "90" (1.5ms pulse) to the PWM pin to stop the motor.
Write a value of 180, (2.50ms pulse) is full speed CCW.
Write a value of zero '0' (0.50ms pulse) is full speed CW.
The motor may require some simple calibration. Simply tell the servo
to 'stop' and then gently adjust the potentiometer in the recessed hole
on the bottom of the servo using a small screwdriver until the servo stops moving.
***
During testing this is what I discovered:
CW direction count down from 89 to 0, pulse width = 0.50mS (1.0mS less than full stop)
CCW direction count up from 91 to 180, pulse width = 2.5mS (1.0mS greater than full stop)
Motor stopped count = 90, pulse width = 1.5mS
Adjustment screw on bottom of motor to tweak the motor stop position
***
MOTOR SPECIFICATIONS
No load speed: 110RPM (4.8v) / 130RPM (6v)
Running Current (at no load): 100mA (4.8v) / 120mA (6v)
Peak Stall Torque (4.8v): 1.3 kg/cm / 18.09 oz/in
Peak Stall Torque (6v): 1.5 kg/cm / 20.86 oz/in
Stall Current: 550mA (4.8v) / 650mA (6v)
Dimensions: 32mm x 30mm x 12mm / 1.3" x 1.2" x 0.5"
Wire Length: 240mm / 9.4"
Weight: 10g
Spline Count: 21
*/
#include<servo.h
> // Include the servo library.
#define servoMtr 10 // Assign the servo motor to pin 10
Servo myServo; // Create a servo object
int i = 90; // Motor stopped
int rotDir = 0; // 0 = CCW, 1 = CW. Not really for rotation. Flag used to change operation
void setup() {
Serial.begin(9600); // Set up the Serial port
myServo.attach(servoMtr); // Attach the servo object to the defined pin
myServo.write(90); // Make sure the motor is stopped
}
void loop() {
// Count up from 90 to 180. CCW stop to full speed
if (rotDir == 0) {
Serial.print("i = "); Serial.print(i); Serial.print(" "); // Can be removed
Serial.print("rotDir = "); Serial.println(rotDir); // Can be removed
i++;
myServo.write(i);
delay(300); // Can be removed
if (i == 180) {
rotDir = 1;
Serial.print("i = "); Serial.print(i); Serial.print(" "); // Can be removed
Serial.print("rotDir = "); Serial.println(rotDir); // Can be removed
}
}
// Count down from 180 to 90. CCW full speed to stop
if (rotDir == 1) {
Serial.print("i = "); Serial.print(i); Serial.print(" "); // Can be removed
Serial.print("rotDir = "); Serial.println(rotDir); // Can be removed
i--;
myServo.write(i);
delay(300); // Can be removed
if (i == 90) {
rotDir = 2;
Serial.print("i = "); Serial.print(i); Serial.print(" "); // Can be removed
Serial.print("rotDir = "); Serial.println(rotDir); // Can be removed
}
}
// Count down from 90 to 0. Stop to CW full speed
if (rotDir == 2) {
Serial.print("i = "); Serial.print(i); Serial.print(" "); // Can be removed
Serial.print("rotDir = "); Serial.println(rotDir); // Can be removed
i--;
myServo.write(i);
delay(300); // Can be removed
if (i == 0) {
rotDir = 3;
Serial.print("i = "); Serial.print(i); Serial.print(" "); // Can be removed
Serial.print("rotDir = "); Serial.println(rotDir); // Can be removed
}
}
// Count up from 0 to 90. Full speed CW to stop
if (rotDir == 3) {
Serial.print("i = "); Serial.print(i); Serial.print(" "); // Can be removed
Serial.print("rotDir = "); Serial.println(rotDir); // Can be removed
i++;
myServo.write(i);
delay(300); // Can be removed
if (i == 90) {
rotDir = 0;
Serial.print("i = "); Serial.print(i); Serial.print(" "); // Can be removed
Serial.print("rotDir = "); Serial.println(rotDir); // Can be removed
}
}
}
Explanation of the code
This video is best viewed in full screen mode.
BLINKING CHEST LIGHTS Rev 1.0, PROGRAM Rev 2.0
As it's name describes, this section will demonstrate my first attempt at blinking the robots chest lights, the Function Indicators, in a completely random pattern.
This experiment uses an Arduino Mega 2560 board. This board will not be used in the final robot animation because the Mega board is too large to be self contained within the robot body.
Random Blinking of Function Lights Testing
Schematic Diagram and Bill of Material (BOM)
Schematic Diagram Rev 1.0
Here is the schematic diagram that is using an Arduino Mega 2560. Most any Arduino board can be used. The schematic was created using Easy EDA. Click the schematic image to enlarge.
Bill of Material (BOM)
Prices are as of March 11, 2024
Quantity | Description | Part Number | Vendor | ASIN Number | Price Per | Extended Price |
---|---|---|---|---|---|---|
1 | Arduino Mega 2560 or similar | Mega 2560 Rev3 [A000067] | Amazon | B0046AMGW0 | $48.90 | $48.90 |
22 |
LED, 5mm Defused Pack - 5 LEDs each in 5 colors 25-pack |
4203 | Adafruit.com | N/A | $4.95 | $4.95 |
22 |
YOKIVE 100 pcs Carbon Film Resistors, 1/4W, 300Ω 5% |
Amazon | B0BNHTXS1S | $0.0499 | $1.098 | |
24 |
Male-to-Male wire jumpers Set of 130pcs 12, 16, 20, 25cm Only 3 wires required for this project |
41383 | Amazon | B016KI622U | $0.06 per wire | $0.18 |
Program Code Used to Test Random Blinking of Function Indicators Rev 2.0
The code in the following window can be selected, copied and
pasted directly into your IDE. It should work well on a
Windows or Mac. Simply place you mouse cursor where you
want to start copying and then click and drag to
select the text. Then copy the selection and paste it into
your Integrated Development Environment (IDE).
Program code I wrote to produce random blinking of LED's
// Blink without using delay and using a random // number for the duty cycle // Programmed for an Arduino Mega or Arduino Mega 2560 // By: John G. (Skip) Todora // Date: March 03, 2024 // Revision: 02 // B9 Robot chest lights: Function Indicators and Programming Buttons // Two rows of six lights per row; small round lamps // Bottom row of Function Indicators #define botRow00 22 #define botRow01 24 #define botRow02 26 #define botRow03 28 #define botRow04 30 #define botRow05 32 // Top row of Function Indicators #define topRow00 23 #define topRow01 25 #define topRow02 27 #define topRow03 29 #define topRow04 31 #define topRow05 33 // Bottom rows of five lights per row; Programming Buttons #define botRowSq00 44 #define botRowSq01 46 #define botRowSq02 48 #define botRowSq03 50 #define botRowSq04 52 // Top rows or five lights per row: Programming Buttons #define topRowSq00 45 #define topRowSq01 47 #define topRowSq02 49 #define topRowSq03 51 #define topRowSq04 53 // The variable: range and min form the data for creating a duty cycle for blinking // These values will most likely need to be adjusted depending upon what processor // board is being used. unsigned long range = 80000; // Max duty cycle for blink in mS unsigned long min = 700; // Min duty cycle for blink in mS int runOnce = 1; // Used for lamp test on power up // Variable to store the current time // unsigned long curTime = 0; // Bottom row of the six; Function Indicators // Assign Current Time and Previous time values unsigned long botRow00CurTime = 0; unsigned long botRow01CurTime = 0; unsigned long botRow02CurTime = 0; unsigned long botRow03CurTime = 0; unsigned long botRow04CurTime = 0; unsigned long botRow05CurTime = 0; unsigned long botRow00PrevTime = 0; unsigned long botRow01PrevTime = 0; unsigned long botRow02PrevTime = 0; unsigned long botRow03PrevTime = 0; unsigned long botRow04PrevTime = 0; unsigned long botRow05PrevTime = 0; // Top row of the six; Function Indicators // Assign Current Time and Previous time values unsigned long topRow00CurTime = 0; unsigned long topRow01CurTime = 0; unsigned long topRow02CurTime = 0; unsigned long topRow03CurTime = 0; unsigned long topRow04CurTime = 0; unsigned long topRow05CurTime = 0; unsigned long topRow00PrevTime = 0; unsigned long topRow01PrevTime = 0; unsigned long topRow02PrevTime = 0; unsigned long topRow03PrevTime = 0; unsigned long topRow04PrevTime = 0; unsigned long topRow05PrevTime = 0; // Bottom row of five square: Programming Buttons // Assign Current Time values. Previous time not needed because // these lights do not blink unsigned long botRowSq00CurTime = 0; unsigned long botRowSq01CurTime = 0; unsigned long botRowSq02CurTime = 0; unsigned long botRowSq03CurTime = 0; unsigned long botRowSq04CurTime = 0; // Top row of five square: Programming Buttons // Assign Current Time values. Previous time not needed because // these lights do not blink unsigned long topRowSq00CurTime = 0; unsigned long topRowSq01CurTime = 0; unsigned long topRowSq02CurTime = 0; unsigned long topRowSq03CurTime = 0; unsigned long topRowSq04CurTime = 0; // Initialize state of bottom row of small round lights: // Function Indicators int botRow00State = LOW; int botRow01State = LOW; int botRow02State = LOW; int botRow03State = LOW; int botRow04State = LOW; int botRow05State = LOW; // Intitialize state of top row of small round lights: // Function Indicators int topRow00State = LOW; int topRow01State = LOW; int topRow02State = LOW; int topRow03State = LOW; int topRow04State = LOW; int topRow05State = LOW; // Initialize state of bottom row of square lights: // Programming Push Buttons int botRowSq00State = LOW; int botRowSq01State = LOW; int botRowSq02State = LOW; int botRowSq03State = LOW; int botRowSq04State = LOW; // Initialize state of top row of square lights // Programming Push Buttons int topRowSq00State = LOW; int topRowSq01State = LOW; int topRowSq02State = LOW; int topRowSq03State = LOW; int topRowsq04State = LOW; void setup() { Serial.begin(9600); // Declare all lamp pins as data outputs pinMode(botRow00, OUTPUT); pinMode(botRow01, OUTPUT); pinMode(botRow02, OUTPUT); pinMode(botRow03, OUTPUT); pinMode(botRow04, OUTPUT); pinMode(botRow05, OUTPUT); pinMode(topRow00, OUTPUT); pinMode(topRow01, OUTPUT); pinMode(topRow02, OUTPUT); pinMode(topRow03, OUTPUT); pinMode(topRow04, OUTPUT); pinMode(topRow05, OUTPUT); pinMode(botRowSq00, OUTPUT); pinMode(botRowSq01, OUTPUT); pinMode(botRowSq02, OUTPUT); pinMode(botRowSq03, OUTPUT); pinMode(botRowSq04, OUTPUT); pinMode(topRowSq00, OUTPUT); pinMode(topRowSq01, OUTPUT); pinMode(topRowSq02, OUTPUT); pinMode(topRowSq03, OUTPUT); pinMode(topRowSq04, OUTPUT); } void loop() { // Run once - Lamp Test if (runOnce == 1) { lampTest(); // Function that controls lamp test } // Assign millis() to each light // curTime = millis(); botRow00CurTime = millis(); botRow01CurTime = millis(); botRow02CurTime = millis(); botRow03CurTime = millis(); botRow04CurTime = millis(); botRow05CurTime = millis(); topRow00CurTime = millis(); topRow01CurTime = millis(); topRow02CurTime = millis(); topRow03CurTime = millis(); topRow04CurTime = millis(); topRow05CurTime = millis(); // Control the lights // Bottom Row LED 00 unsigned long botRow00DutyCycle = rand() % range + min; // rand() C stdlib.h (0 to 32767) if (botRow00CurTime - botRow00PrevTime > botRow00DutyCycle / 2) { // if (curTime - botRow00PrevTime > botRow00DutyCycle / 2) { botRow00Blink(); botRow00PrevTime = botRow00CurTime; } // Bottom Row LED 01 unsigned long botRow01DutyCycle = rand() % range + min; if (botRow01CurTime - botRow01PrevTime > botRow01DutyCycle / 2) { // if (curTime - botRow01PrevTime > botRow01DutyCycle / 2) { botRow01Blink(); botRow01PrevTime = botRow01CurTime; } // Bottom Row LED 02 unsigned long botRow02DutyCycle = rand() % range + min; if (botRow02CurTime - botRow02PrevTime > botRow02DutyCycle / 2) { // if (curTime - botRow02PrevTime > botRow02DutyCycle / 2) { botRow02Blink(); botRow02PrevTime = botRow02CurTime; } // Bottom Row LED 03 unsigned long botRow03DutyCycle = rand() % range + min; if (botRow03CurTime - botRow03PrevTime > botRow03DutyCycle / 2) { // if (curTime - botRow03PrevTime > botRow03DutyCycle / 2) { botRow03Blink(); botRow03PrevTime = botRow03CurTime; } // Bottom Row LED 04 unsigned long botRow04DutyCycle = rand() % range + min; if (botRow04CurTime - botRow04PrevTime > botRow04DutyCycle / 2) { // if (curTime - botRow04PrevTime > botRow04DutyCycle / 2) { botRow04Blink(); botRow04PrevTime = botRow04CurTime; } // Bottom Row LED 05 unsigned long botRow05DutyCycle = rand() % range + min; if (botRow05CurTime - botRow05PrevTime > botRow05DutyCycle / 2) { // if (curTime - botRow05PrevTime > botRow05DutyCycle / 2) { botRow05Blink(); botRow05PrevTime = botRow05CurTime; } // Top Row LED 00 unsigned long topRow00DutyCycle = rand() % range + min; if (topRow00CurTime - topRow00PrevTime > topRow00DutyCycle / 2) { // if (curTime - topRow00PrevTime > topRow00DutyCycle / 2) { topRow00Blink(); topRow00PrevTime = topRow00CurTime; } // Top Row LED 01 unsigned long topRow01DutyCycle = rand() % range + min; if (topRow01CurTime - topRow01PrevTime > topRow01DutyCycle / 2) { // if (curTime - topRow01PrevTime > topRow01DutyCycle / 2) { topRow01Blink(); topRow01PrevTime = topRow01CurTime; } // Top Row LED 02 unsigned long topRow02DutyCycle = rand() % range + min; if (topRow02CurTime - topRow02PrevTime > topRow02DutyCycle / 2) { // if (curTime - topRow02PrevTime > topRow02DutyCycle / 2) { topRow02Blink(); topRow02PrevTime = botRow02CurTime; } // Top Row LED 03 unsigned long topRow03DutyCycle = rand() % range + min; if (topRow03CurTime - topRow03PrevTime > topRow03DutyCycle / 2) { // if (curTime - topRow03PrevTime > topRow03DutyCycle / 2) { topRow03Blink(); topRow03PrevTime = topRow03CurTime; } // Top Row LED 04 unsigned long topRow04DutyCycle = rand() % range + min; if (topRow04CurTime - topRow04PrevTime > topRow04DutyCycle / 2) { // if (curTime - topRow04PrevTime > topRow04DutyCycle / 2) { topRow04Blink(); topRow04PrevTime = topRow04CurTime; } // Top Row LED 05 unsigned long topRow05DutyCycle = rand() % range + min; if (topRow05CurTime - topRow05PrevTime > topRow05DutyCycle / 2) { // if (curTime - topRow05PrevTime > topRow05DutyCycle / 2) { topRow05Blink(); topRow05PrevTime = topRow05CurTime; } } void lampTest() { digitalWrite(botRow00, HIGH); delay(500); digitalWrite(botRow01, HIGH); delay(500); digitalWrite(botRow02, HIGH); delay(500); digitalWrite(botRow03, HIGH); delay(500); digitalWrite(botRow04, HIGH); delay(500); digitalWrite(botRow05, HIGH); delay(500); digitalWrite(topRow00, HIGH); delay(500); digitalWrite(topRow01, HIGH); delay(500); digitalWrite(topRow02, HIGH); delay(500); digitalWrite(topRow03, HIGH); delay(500); digitalWrite(topRow04, HIGH); delay(500); digitalWrite(topRow05, HIGH); delay(500); digitalWrite(botRowSq00, HIGH); delay(500); digitalWrite(botRowSq01, HIGH); delay(500); digitalWrite(botRowSq02, HIGH); delay(500); digitalWrite(botRowSq03, HIGH); delay(500); digitalWrite(botRowSq04, HIGH); delay(500); digitalWrite(topRowSq00, HIGH); delay(500); digitalWrite(topRowSq01, HIGH); delay(500); digitalWrite(topRowSq02, HIGH); delay(500); digitalWrite(topRowSq03, HIGH); delay(500); digitalWrite(topRowSq04, HIGH); delay(2000); // Adjust to user liking runOnce = 0; digitalWrite(botRow00, LOW); digitalWrite(botRow01, LOW); digitalWrite(botRow02, LOW); digitalWrite(botRow03, LOW); digitalWrite(botRow04, LOW); digitalWrite(botRow05, LOW); digitalWrite(topRow00, LOW); digitalWrite(topRow00, LOW); digitalWrite(topRow00, LOW); digitalWrite(topRow00, LOW); digitalWrite(topRow00, LOW); digitalWrite(topRow00, LOW); /* digitalWrite(botRowSq00, LOW); digitalWrite(botRowSq01, LOW); digitalWrite(botRowSq02, LOW); digitalWrite(botRowSq03, LOW); digitalWrite(botRowSq04, LOW); digitalWrite(topRowSq00, LOW); digitalWrite(topRowSq01, LOW); digitalWrite(topRowSq02, LOW); digitalWrite(topRowSq03, LOW); digitalWrite(topRowSq04, LOW); */ } // Bottom row Function Indicators Blink void botRow00Blink() { botRow00State = (botRow00State == HIGH) ? LOW : HIGH; digitalWrite(botRow00, botRow00State); } void botRow01Blink() { botRow01State = (botRow01State == HIGH) ? LOW : HIGH; digitalWrite(botRow01, botRow01State); } void botRow02Blink() { botRow02State = (botRow02State == HIGH) ? LOW : HIGH; digitalWrite(botRow02, botRow02State); } void botRow03Blink() { botRow03State = (botRow03State == HIGH) ? LOW : HIGH; digitalWrite(botRow03, botRow03State); } void botRow04Blink() { botRow04State = (botRow04State == HIGH) ? LOW : HIGH; digitalWrite(botRow04, botRow04State); } void botRow05Blink() { botRow05State = (botRow05State == HIGH) ? LOW : HIGH; digitalWrite(botRow05, botRow05State); } // Top row Function Indicators Blink void topRow00Blink() { topRow00State = (topRow00State == HIGH) ? LOW : HIGH; digitalWrite(topRow00, topRow00State); } void topRow01Blink() { topRow01State = (topRow01State == HIGH) ? LOW : HIGH; digitalWrite(topRow01, topRow01State); } void topRow02Blink() { topRow02State = (topRow02State == HIGH) ? LOW : HIGH; digitalWrite(topRow02, topRow02State); } void topRow03Blink() { topRow03State = (topRow03State == HIGH) ? LOW : HIGH; digitalWrite(topRow03, topRow03State); } void topRow04Blink() { topRow04State = (topRow04State == HIGH) ? LOW : HIGH; digitalWrite(topRow04, topRow04State); } void topRow05Blink() { topRow05State = (topRow05State == HIGH) ? LOW : HIGH; digitalWrite(topRow05, topRow05State); }
Explanation of the code
This video is best viewed in full screen mode.
BLINKING CHEST LIGHTS Rev 1.1, PROGRAM Rev 3.0
As it's name describes, this section will demonstrate my second, actually third attempt at blinking the robots chest lights; the Function Indicators, in a completely random pattern, and the Sensor Indicators either back-and-forth at a random rate or at a fixed equal time interval. In this experiment I'm using an Arduino Micro and I paralleled three of the Function Indicator LED's, in a random arrangement, off of a single Arduino output pin (See the schematic diagram). I did this to save output pins but to also check if the lights still look as if they are blinking in a random pattern. Following is a short video of this design:
This video is best viewed in full screen mode.
Schematic Diagram and Bill of Material (BOM)
Schematic Diagram Rev 3.0
Here is the schematic diagram that is using an Arduino Mega 2560. Most any Arduino board can be used. The schematic was created using Easy EDA. Click the schematic image to enlarge.
Bill of Material (BOM)
Prices are as of March 11, 2024
Quantity | Description | Part Number | Vendor | ASIN Number | Price Per | Extended Price |
---|---|---|---|---|---|---|
1 | Arduino Mega 2560 or similar | Mega 2560 Rev3 [A000067] | Amazon | B0046AMGW0 | $48.90 | $48.90 |
24 |
LED, 5mm Defused Pack - 5 LEDs each in 5 colors 25-pack |
4203 | Adafruit.com | N/A | $4.95 | $4.95 |
24 |
YOKIVE 100 pcs Carbon Film Resistors, 1/4W, 300Ω 5% |
Amazon | B0BNHTXS1S | $0.0499 | $1.098 | |
24 |
Male-to-Male wire jumpers Set of 130pcs 12, 16, 20, 25cm Only 3 wires required for this project |
41383 | Amazon | B016KI622U | $0.06 per wire | $0.18 |
Program Code Used to Test Blinking Chest Lights Using Groups Rev. 3.0
The code in the following window can be selected, copied and
pasted directly into your IDE. It should work well on a
Windows or Mac. Simply place you mouse cursor where you
want to start copying and then click and drag to
select the text. Then copy the selection and paste it into
your Integrated Development Environment (IDE).
Program code I wrote to produce random blinking of LED's
// Blink without using delay and using a random // number for the duty cycle // Programmed for an Arduino Mega or Arduino Mega 2560 // By: John G. (Skip) Todora // Date: March 16, 2024 // Revision: 3.0 // Filename: Light_Test_with_Micro_using_4-Outputs.ino // B9 Robot chest lights: Function Indicators and Sensor Indicators // Two rows of six lights per row; Function Indicators // The 12-lights are 4-groups of 3-lights/group // Pin assignment for Function Indicators and for Sensor Indicators #define group01 2 // D1, D5, D9 - See schematic diagram #define group02 3 // D2, D8, D10 - See schematic diagram #define group03 4 // D3, D6, D11 - See schematic diagram #define group04 5 // D4, D7, D12 - See schematic diagram #define sensorInd01 6 // Sensor indicator #define sensorInd02 7 // Sensor indicator // The variable: range and min form the data for creating a duty cycle for blinking // These values will most likely need to be adjusted depending upon what processor // board is being used. unsigned long range = 80000; // Max duty cycle for blink in mS unsigned long min = 700; // Min duty cycle for blink in mS int runOnce = 1; // Used for lamp test on power up // Assign Current Time and Previous time values unsigned long group01CurTime = 0; unsigned long group02CurTime = 0; unsigned long group03CurTime = 0; unsigned long group04CurTime = 0; unsigned long sensorInd01CurTime = 0; unsigned long sensorInd02CurTime = 0; unsigned long group01PrevTime = 0; unsigned long group02PrevTime = 0; unsigned long group03PrevTime = 0; unsigned long group04PrevTime = 0; unsigned long sensorInd01PrevTime = 0; unsigned long sensorInd02PrevTime = 0; // Initialize state of bottom row of small round lights: // Function Indicators int group01State = LOW; int group02State = LOW; int group03State = LOW; int group04State = LOW; int sensorInd01State = LOW; int sensorInd02State = LOW; void setup() { Serial.begin(9600); // Declare all lamp pins as data outputs pinMode(group01, OUTPUT); pinMode(group02, OUTPUT); pinMode(group03, OUTPUT); pinMode(group04, OUTPUT); pinMode(sensorInd01, OUTPUT); pinMode(sensorInd02, OUTPUT); } void loop() { // Run once - Lamp Test if (runOnce == 1) { lampTest(); // Function that controls lamp test } // Assign millis() to each light // curTime = millis(); group01CurTime = millis(); group02CurTime = millis(); group03CurTime = millis(); group04CurTime = millis(); sensorInd01CurTime = millis(); sensorInd02CurTime = millis(); // Control the lights // Group 01 - D1, D5, D9 unsigned long group01DutyCycle = rand() % range + min; // rand() C stdlib.h (0 to 32767) if (group01CurTime - group01PrevTime > group01DutyCycle / 2) { group01Blink(); group01PrevTime = group01CurTime; } // Group 02 - D3, D8, D10 unsigned long group02DutyCycle = rand() % range + min; if (group02CurTime - group02PrevTime > group02DutyCycle / 2) { group02Blink(); group02PrevTime = group02CurTime; } // Group 03 - D3, D6, D11 unsigned long group03DutyCycle = rand() % range + min; if (group03CurTime - group03PrevTime > group03DutyCycle / 2) { group03Blink(); group03PrevTime = group03CurTime; } // Group 04 - D4, D7, D12 unsigned long group04DutyCycle = rand() % range + min; if (group04CurTime - group04PrevTime > group04DutyCycle / 2) { group04Blink(); group04PrevTime = group04CurTime; } // Sensor Indicator 01 unsigned long sensorInd01DutyCycle = rand() % range + min; if (sensorInd01CurTime - sensorInd01PrevTime > sensorInd01DutyCycle / 2) { sensorInd01Blink(); sensorInd01PrevTime = sensorInd01CurTime; } // Sensor Indicator 02 unsigned long sensorInd02DutyCycle = rand() % range + min; if (sensorInd02CurTime - sensorInd02PrevTime > sensorInd02DutyCycle / 2) { sensorInd02Blink(); sensorInd02PrevTime = sensorInd02CurTime; } } void lampTest() { digitalWrite(group01, HIGH); delay(500); digitalWrite(group02, HIGH); delay(500); digitalWrite(group03, HIGH); delay(500); digitalWrite(group04, HIGH); delay(500); digitalWrite(sensorInd01, HIGH); delay(500); digitalWrite(sensorInd02, HIGH); delay(2000); runOnce = 0; digitalWrite(group01, LOW); digitalWrite(group02, LOW); digitalWrite(group03, LOW); digitalWrite(group04, LOW); digitalWrite(sensorInd01, LOW); digitalWrite(sensorInd02, LOW); delay(500); } // Change the state of Group 01 Function Indicators void group01Blink() { group01State = (group01State == HIGH) ? LOW : HIGH; digitalWrite(group01, group01State); } // Change the state of Group 02 Function Indicators void group02Blink() { group02State = (group02State == HIGH) ? LOW : HIGH; digitalWrite(group02, group02State); } // Change the state of Group 03 Function Indicators void group03Blink() { group03State = (group03State == HIGH) ? LOW : HIGH; digitalWrite(group03, group03State); } // Change the state of Group 04 Function Indicators void group04Blink() { group04State = (group04State == HIGH) ? LOW : HIGH; digitalWrite(group04, group04State); } // Change the state of Sensor Indicator 01 void sensorInd01Blink() { sensorInd01State = (sensorInd01State == HIGH) ? LOW : HIGH; digitalWrite(sensorInd01, sensorInd01State); } // Change the state of Sensor Indicator 02 void sensorInd02Blink() { sensorInd02State = (sensorInd02State == HIGH) ? LOW : HIGH; digitalWrite(sensorInd02, sensorInd02State); }
Explanation of the code
This video is best viewed in full screen mode.
SOUND INDICATOR - MAKING THE ROBOT MOUTH LIGHT WITH HIS VOICE PATTERN
I cannot take credit for the original design or program code that I show with the Yellow LED's. The idea came from: Science Buddies - How to Use a Microphone with Arduino (Lesson #12)
However, I made modification to the circuit and the program code replace the individual LED's with an Adafruit Neo-Pixels 7-LED Jewel. I "think" the Neo-Pixels Jewel will work better behind the robot mouth.
Here is the schematic diagrams for the two versions. I used an Arduino Uno for the circuit using the individual LED's an an Arduino Micro for the circuit using the Adafruit Neo-Pixels Jewel. These circuits should work on any Arduino board. Just make sure the Neo-Pixels Jewel uses a Pulse Width Modulated (PWM) output pin. The schematic was created using EasyEDA.
Prices are as of March 11, 2024
Quantity | Description | Part Number | Vendor | ASIN Number | Price Per | Extended Price |
---|---|---|---|---|---|---|
1 | Arduino Uno REV3 (or similar) | [A000066] | Amazon | B008GRTSV6 | $24.50 | $24.50 |
5 |
LED, 5mm Defused Pack - 5 LEDs each in 5 colors 25-pack |
4203 | Adafruit.com | N/A | $0.198 | $4.95 |
5 |
E-Project #A-0004-B06 100 pcs Carbon Film Resistors, 1/4W, 330Ω 5% |
Amazon | B0185F1D32 | $0.07 | $6.69 | |
10 |
Male-to-Male wire jumpers Set of 130pcs 12, 16, 20, 25cm Only 3 wires required for this project |
41383 | Amazon | B016KI622U | $0.06 per wire | $0.18 |
Prices are as of June 11, 2024
Quantity | Description | Part Number | Vendor | ASIN Number | Price Per | Extended Price |
---|---|---|---|---|---|---|
1 | Arduino Micro with headers (or similar) |
[A000053] | Amazon | B00AFY2556 | $27.48 | $27.48 |
1 |
Electret Microphone Amplifier Module MAX4466 Adjustable Gain Blue Breakout Board for Arduino Package of Six(6) |
MAX4466 | Amazon.com | B08LD9C7WX | $1.67 | $9.99 |
1 |
NeoPixels Jewel 7 x 5050 RGB LED with integrated drivers |
2226 | Adafruit.com | N/A | $5.95 | $5.95 |
Program Code Used to Test Visual Voice Indication
The code in the following window can be selected, copied and
pasted directly into your IDE. It should work well on a
Windows or Mac. Simply place your mouse cursor where you
want to start copying and then click and drag to
select the text. Then copy the selection and paste it into
your Integrated Development Environment (IDE).
Program code I modified from Science Buddies to produce a visual audio indicator using individual LEDs to be used for the B9 robot mouth.
// Microphone test for audio level blinking // February 15, 2024 // Modified By: John G. Todora, Jr. // Science Buddies // Reference: https://www.youtube.com/watch?v=bMs5J4bJOD0 #define mic A0 #define led01 3 #define led02 4 #define led03 5 #define led04 6 #define led05 7 int delta = 0; // Will be used to set threshold steps int maxAmplitude = 300; // Guess as to the maximum amplitude the mic will see int amplitude = 0; // Variable to store the actual amplitude of the mice /* The microphone is best powered with the 3.3V supply. This is according to the microphone specification sheet. The microphone output will be connected to an analog input pin of an Arduino. The analog pin(s) on the Arduino produce a count of 1023 based on a 5.0V signal, since I'll be using 3.3V the count will be: 3.3V * 1023 / 5 = 675 counts. The microphone has a DC offset of approximately 1.55V. That means that zero (0) volts is not silence and 3.3V is not maximum volume, but that 1.55V is silence and the audio will be positive and negative, therefore; the positive half will have a count of 337 counts and the negative half will have a count of 337 counts. This means that to get the amplitude, I'll have to take the absolute value (ABS). Based on this the baseline, or silence, will be 337 counts and therefore a variable named: baseline is declared and assigned the value of 337. */ int baseline = 337; /* The program will be using five LED's to display sound level, acting like a VU meter in a recording studion. Get set a level or threshold for when each LED lights a variable is created named: numOfLEDs to set the number of LEDs that will be used to display the sound level. */ int numOfLEDs = 5; // Five variable will be needed to store the different threshold values. int threshHold01 = 0; int threshHold02 = 0; int threshHold03 = 0; int threshHold04 = 0; int threshHold05 = 0; void setup() { Serial.begin(9600); pinMode(mic, INPUT); pinMode(led01, OUTPUT); pinMode(led02, OUTPUT); pinMode(led03, OUTPUT); pinMode(led04, OUTPUT); pinMode(led05, OUTPUT); } void loop() { // The delta or step size of the threshold is calculated delta = maxAmplitude / (numOfLEDs); // Each threshold value is set threshHold01 = delta; threshHold02 = threshHold01 + delta; threshHold03 = threshHold02 + delta; threshHold04 = threshHold03 + delta; threshHold05 = threshHold04 + delta; // Calculate the amplitude of the audio signal amplitude = abs(analogRead(mic) - baseline); Serial.print("mic value = "); Serial.println(amplitude); // Display the amplitude on the LED's if (amplitude < threshHold01) { digitalWrite(led01, LOW); digitalWrite(led02, LOW); digitalWrite(led03, LOW); digitalWrite(led04, LOW); digitalWrite(led05, LOW); } if (amplitude > threshHold01) { digitalWrite(led01, HIGH); digitalWrite(led02, LOW); digitalWrite(led03, LOW); digitalWrite(led04, LOW); digitalWrite(led05, LOW); } if (amplitude > threshHold02) { digitalWrite(led01, HIGH); digitalWrite(led02, HIGH); digitalWrite(led03, LOW); digitalWrite(led04, LOW); digitalWrite(led05, LOW); } if (amplitude > threshHold03) { digitalWrite(led01, HIGH); digitalWrite(led02, HIGH); digitalWrite(led03, HIGH); digitalWrite(led04, LOW); digitalWrite(led05, LOW); } if (amplitude > threshHold04) { digitalWrite(led01, HIGH); digitalWrite(led02, HIGH); digitalWrite(led03, HIGH); digitalWrite(led04, HIGH); digitalWrite(led05, LOW); } if (amplitude > threshHold05) { digitalWrite(led01, HIGH); digitalWrite(led02, HIGH); digitalWrite(led03, HIGH); digitalWrite(led04, HIGH); digitalWrite(led05, HIGH); } }
This video is best viewed in full screen mode.
This video is best viewed in full screen mode.
The program code for using a Neo-Pixels Jewel is similar to the code used for individual LEDs; however, the Adafruit Neo-Pixels Library is needed and the method used to control the individual LEDs is different.
// Experimenting with NeoPixel as sound VU // Filename: experimenting_pixel_vu.ino // Code Modified by: John G. (Skip) Todora, Jr. // Date: June 5, 2024 // Science Buddies - Source of original concept and code // Reference: https://www.youtube.com/watch?v=bMs5J4bJOD0 // Science Buddies used individual LED's for the VU meter // I modified the program to use a NeoPixels 7-LED Pixels #include// Include the library for the Adafruit Pixels #define pixelLedCount 7 // Define the number of LED's on the Pixels #define pixelLedPin 10 // Define the output pin of the Pixels #define mic A0 // Define the analog input of the microphone // Create a NewPixel object Adafruit_NeoPixel soundMonitor(pixelLedCount, pixelLedPin, NEO_GRB + NEO_KHZ800); // Define the LED color of the Pixels #define COLOR_RED soundMonitor.Color(255,0,0) /* According to the microphone specification sheet, when wiring the microphone to an Arduino, it is best powered with from the Arduino 3.3V supply. This will aid in reducing the power supply noise level that could be present. The microphone output will be connected to an analog input pin of an Arduino. The analog pin(s) on the Arduino produce a count of 0 to 1023 based on a 5.0V signal, since I'll be using 3.3V, the count will be: 3.3V * 1023 / 5 = 675 counts, therefore a count will be produced in the range of 0 to 675 counts. The microphone has a DC offset of ~1.6V, half of the 3.3V. That means zero (0) volts will not represent silence, but silence will be ~1.6V which will produce a count value of ~337. That said, a count value of ~337 represents silence. The audio will be positive and negative above and below the offset, therefore; the positive half will have a count of 337 + 337 = 674 counts and the negative half will have a count of 337 - 337 = 0 counts. Therefore, the audio will vary 337 counts above and below the baseline of 337 counts. Because math will be used to calculate the amplitude of the audio, I'll have to take the absolute value (ABS) so there will be no negative numbers. Based on this the baseline, or silence, will be 337 counts and therefore a variable named: baseline is declared and assigned the value of 337. */ int baseLine = 337; // Counts that represent silence int delta = 0; // Variable to store the different audio levels int maxAmplitude = 334; // Counts that represent maximum audio level int amplitude = 0; // Variable to store the calculated amplitude // To make the LEDs blink at different audio levels, we need to set up levels // for when each LED of the Pixels will turn on or off // The LED's of the Pixels are numbered starting with zero (0) therefore; // seven (7) LED's are numbered 0 through 6 int level00 = 0; int level01 = 0; int level02 = 0; int level03 = 0; int level04 = 0; int level05 = 0; int level06 = 0; int level07 = 0; // Setup function runs once at startup void setup() { Serial.begin(9600); // Initialize the Serial port if needed pinMode(mic, INPUT); // Set the mode of the analog pin delta = maxAmplitude / (pixelLedCount + 1); // Calculate the level for each LED of the Pixels // Calculate the levels for each LED of the Pixels to turn on/off level00 = delta; level01 = level00 + delta; level02 = level01 + delta; level03 = level02 + delta; level04 = level03 + delta; level05 = level04 + delta; level06 = level05 + delta; level07 = level06 + delta; // Call function to initialize the NeoPixels Pixels initPixels(); } void loop() { // Calculate the amplitude of the microphone signal amplitude = abs(analogRead(mic) - baseLine); // Use the amplitude and test against the levels calculated to turn on/off // the appropriate LED's of the Pixels. if (amplitude < level00) { soundMonitor.clear(); soundMonitor.show(); } if (amplitude > level01) { soundMonitor.clear(); soundMonitor.setPixelColor(0, COLOR_RED); // soundMonitor.setPixelColor(1, COLOR_RED); soundMonitor.show(); } if (amplitude > level02) { soundMonitor.clear(); soundMonitor.setPixelColor(0, COLOR_RED); soundMonitor.setPixelColor(1, COLOR_RED); // soundMonitor.setPixelColor(2, COLOR_RED); soundMonitor.show(); } if (amplitude > level03) { soundMonitor.clear(); soundMonitor.setPixelColor(0, COLOR_RED); soundMonitor.setPixelColor(1, COLOR_RED); soundMonitor.setPixelColor(2, COLOR_RED); // soundMonitor.setPixelColor(3, COLOR_RED); soundMonitor.show(); } if (amplitude > level04) { soundMonitor.clear(); soundMonitor.setPixelColor(0, COLOR_RED); soundMonitor.setPixelColor(1, COLOR_RED); soundMonitor.setPixelColor(2, COLOR_RED); soundMonitor.setPixelColor(3, COLOR_RED); // soundMonitor.setPixelColor(4, COLOR_RED); soundMonitor.show(); } if (amplitude > level05) { soundMonitor.clear(); soundMonitor.setPixelColor(0, COLOR_RED); soundMonitor.setPixelColor(1, COLOR_RED); soundMonitor.setPixelColor(2, COLOR_RED); soundMonitor.setPixelColor(3, COLOR_RED); soundMonitor.setPixelColor(4, COLOR_RED); // soundMonitor.setPixelColor(5, COLOR_RED); soundMonitor.show(); } if (amplitude > level06) { soundMonitor.clear(); soundMonitor.setPixelColor(0, COLOR_RED); soundMonitor.setPixelColor(1, COLOR_RED); soundMonitor.setPixelColor(2, COLOR_RED); soundMonitor.setPixelColor(3, COLOR_RED); soundMonitor.setPixelColor(4, COLOR_RED); soundMonitor.setPixelColor(5, COLOR_RED); // soundMonitor.setPixelColor(6, COLOR_RED); soundMonitor.show(); } if (amplitude > level07) { soundMonitor.clear(); soundMonitor.setPixelColor(0, COLOR_RED); soundMonitor.setPixelColor(1, COLOR_RED); soundMonitor.setPixelColor(2, COLOR_RED); soundMonitor.setPixelColor(3, COLOR_RED); soundMonitor.setPixelColor(4, COLOR_RED); soundMonitor.setPixelColor(5, COLOR_RED); soundMonitor.setPixelColor(6, COLOR_RED); soundMonitor.show(); } } // Function to initialize the NeoPixel Pixels void initPixels(void) { soundMonitor.begin(); soundMonitor.setBrightness(100); soundMonitor.clear(); soundMonitor.show(); }
This video is best viewed in full screen mode.
This video is best viewed in full screen mode.
PRINTED CIRCUIT BOARD DESIGN FOR THE CHEST LIGHTS
The chest lights are divided into three sections as shown in the photo - Chest Light Identification. (Click the image to enlarge the image).
The Function Indicator holes are perfect to accept a 3mm LED. What I have to do is figure out a way to mount and wire them to use as little space as possible and to make it removable for future repairs. Yes...I'm from a generation of engineers who actually considers a design for not only functionality, but also for serviceability.
The Programming Switches are backlit. I've been looking for small push button switches; however, as of this writing (05/09/2025) have not found a suitable candidate. Therefore, I plant to use the clear lenses that come in the kit and back light them.
The Sensor Indicator holes are perfect to accept a 5mm LED. The colors of these two indicators varied depending on what season the B9 robot appears in.
The kit comes with a clear insert for the chest to fill the programming switch and the function indicator openings, as shown in the photo - Clear Plastic Chest Insert. (Click the image to enlarge the image).
I will not need the section of this clear insert that fills the function indicators because the 5mm LED's will protrude through those holes; however, I will be using the clear insert for the programming switches. Therefore, the insert will be cut where where shown in the photo. These rectangular areas will be back lit with 5mm LED's using a diffuser and a light guide tube to help with focus on each rectangle, but not to over power the clear rectangle.
The photo - Parts Used For Assembling Chest Indicators, shows the parts that will be used in my first attempt to back light the programming switches. (click the image to enlarge the image).
Notice that in this photo the clear chest insert has been cut as I showed in the previous photo. The plastic cup that has been cut will be used as a diffuser. The rectangular cut piece of the cup will be glued to the back of the clear programming switches insert. I than used Evergreen #226 (3/16" - 0.188" - 4.77mm) tube, cut to the length of the LED, to focus the LED light forward to to the clear insert. The length of the light guides is 6mm. I might need to increase the length of these tubes to more disperse the light so that it does not create a "hot spot" on the clear insert.
I used Elmer's Disappearing Purple Washable School Glue to glue the diffuser to the clear plastic Programming Switch insert.
I used Elmer's Disappearing Purple Washable School Glue to glue the diffuser to the clear plastic Programming Switch insert. I often use this glue in certain areas of my model builds in places where there is little to know stress and also need the glue to dry transparent.
Spoiler alert!...After letting the glue dry for 24-hours, which is much longer than required, I was happy that there wasn't a trace of glue showing. I started the assembly process of placing the LED's into the guides and aligning everything into the chest. This is when I found out the glue is not strong enough for this application because parts started falling off. Oh well...lesson learned.
I disassembled the assembly and cleaned all the glue residue from all surfaces. I then experimented with using Elmer's white glue.