Skip to content

Instantly share code, notes, and snippets.

@jamesbulpin
Created November 20, 2016 13:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jamesbulpin/f3b20833ab0bf035ae8fd3a69405b222 to your computer and use it in GitHub Desktop.
Save jamesbulpin/f3b20833ab0bf035ae8fd3a69405b222 to your computer and use it in GitHub Desktop.
Arduino sketch to control the motors in a Big Mouth Billy Bass novelty animatronic fish.
const long DEBOUNCE_DELAY = 50;
const long TRIGGER_PULSE = 50;
const int PIN_MOTOR_MOUTH = 10;
const int PIN_MOTOR_HEAD = 9;
const int PIN_MOTOR_TAIL = 11;
const int PIN_CMD_MOUTH = 1;
const int PIN_CMD_HEAD = 2;
const int PIN_CMD_TAIL = 0;
const int PIN_AUDIO_IN = 3;
const int PIN_BUTTON = 2;
#define BUTTON_STATE_COUNT 1
long buttonDebounceTime[BUTTON_STATE_COUNT];
boolean buttonState[BUTTON_STATE_COUNT];
boolean passThrough = 0;
int lastCmdMouth = 0;
int lastCmdHead = 0;
int lastCmdTail = 0;
int debug = 0;
void setup() {
int i;
for (i = 0; i < BUTTON_STATE_COUNT; i++) {
buttonDebounceTime[i] = -50;
buttonState[i] = 255;
}
pinMode(PIN_MOTOR_MOUTH, OUTPUT);
pinMode(PIN_MOTOR_HEAD, OUTPUT);
pinMode(PIN_MOTOR_TAIL, OUTPUT);
analogWrite(PIN_MOTOR_MOUTH, 0);
analogWrite(PIN_MOTOR_HEAD, 0);
analogWrite(PIN_MOTOR_TAIL, 0);
pinMode(PIN_BUTTON, INPUT_PULLUP);
Serial.begin(9600);
}
// Have motor running timeouts to avoid burnout
long timeoutMouth = 0;
long timeoutHead = 0;
long timeoutTail = 0;
void mouth_open()
{
analogWrite(PIN_MOTOR_MOUTH, 255);
timeoutMouth = millis() + 5000;
}
void mouth_close()
{
analogWrite(PIN_MOTOR_MOUTH, 0);
}
void head_move()
{
analogWrite(PIN_MOTOR_TAIL, 0);
analogWrite(PIN_MOTOR_HEAD, 255);
timeoutHead = millis() + 20000;
}
void head_release()
{
analogWrite(PIN_MOTOR_HEAD, 0);
}
void tail_move()
{
analogWrite(PIN_MOTOR_HEAD, 0);
analogWrite(PIN_MOTOR_TAIL, 255);
timeoutTail = millis() + 20000;
}
void tail_release()
{
analogWrite(PIN_MOTOR_TAIL, 0);
}
void motor_timeouts()
{
long now = millis();
if (now > timeoutMouth) {
mouth_close();
}
if (now > timeoutHead) {
head_release();
}
if (now > timeoutTail) {
tail_release();
}
}
void handle_message(char *ptr)
{
char *p, *i;
int j;
p = strtok_r(ptr, " ", &i);
if (strcmp(p, "MOUTH") == 0) {
p = strtok_r(NULL, " ", &i);
if (strcmp(p, "OPEN") == 0) {
mouth_open();
}
else if (strcmp(p, "CLOSE") == 0) {
mouth_close();
}
}
else if (strcmp(p, "HEAD") == 0) {
p = strtok_r(NULL, " ", &i);
if (strcmp(p, "MOVE") == 0) {
head_move();
}
else if (strcmp(p, "RELEASE") == 0) {
head_release();
}
}
else if (strcmp(p, "TAIL") == 0) {
p = strtok_r(NULL, " ", &i);
if (strcmp(p, "MOVE") == 0) {
tail_move();
}
else if (strcmp(p, "RELEASE") == 0) {
tail_release();
}
}
else if (strcmp(p, "DEBUG") == 0) {
p = strtok_r(NULL, " ", &i);
if (strcmp(p, "ON") == 0) {
debug = 1;
}
else if (strcmp(p, "OFF") == 0) {
debug = 0;
}
else if (strcmp(p, "MOUTH") == 0) {
p = strtok_r(NULL, " ", &i);
if (p) {
if (debug) {
Serial.print("DEBUG Sending mouth motor ");
Serial.println(atoi(p));
}
analogWrite(PIN_MOTOR_MOUTH, atoi(p));
}
}
else if (strcmp(p, "HEAD") == 0) {
p = strtok_r(NULL, " ", &i);
if (p) {
if (debug) {
Serial.print("DEBUG Sending mouth motor ");
Serial.println(atoi(p));
}
analogWrite(PIN_MOTOR_TAIL, 0);
analogWrite(PIN_MOTOR_HEAD, atoi(p));
}
}
else if (strcmp(p, "TAIL") == 0) {
p = strtok_r(NULL, " ", &i);
if (p) {
if (debug) {
Serial.print("DEBUG Sending mouth motor ");
Serial.println(atoi(p));
}
analogWrite(PIN_MOTOR_HEAD, 0);
analogWrite(PIN_MOTOR_TAIL, atoi(p));
}
}
}
else if (strcmp(p, "PASSTHROUGH") == 0) {
p = strtok_r(NULL, " ", &i);
if (strcmp(p, "ON") == 0) {
passThrough = 1;
}
else if (strcmp(p, "OFF") == 0) {
passThrough = 0;
}
}
}
const int RX_BUFFER_SIZE = 64;
char rxBuffer[RX_BUFFER_SIZE];
int rxBufferPtr = 0;
void handle_char(int data)
{
if ((data == '\n') || (data == '\0') || (data == '!')) {
if (rxBufferPtr < RX_BUFFER_SIZE) {
rxBuffer[rxBufferPtr] = '\0';
handle_message(rxBuffer);
}
rxBufferPtr = 0;
}
else {
if (rxBufferPtr < RX_BUFFER_SIZE) {
rxBuffer[rxBufferPtr] = (char)data;
rxBufferPtr++;
}
}
}
void do_button(int pin, int idx, char *fn)
{
byte v;
long now;
v = digitalRead(pin);
if (v != buttonState[idx]) {
now = millis();
if ((now - buttonDebounceTime[idx]) < DEBOUNCE_DELAY) {
// In the debounce window, ignore
}
else {
buttonState[idx] = v;
buttonDebounceTime[idx] = now;
if (buttonState[idx] == LOW) {
Serial.println(fn);
}
}
}
}
void do_sense(int inPin, int outPin, int *last, char *dbgName)
{
int v;
v = analogRead(inPin);
if (v != *last) {
if (debug) {
Serial.print("Command ");
Serial.print(dbgName);
Serial.print("=");
Serial.println(v);
}
if (outPin == PIN_MOTOR_HEAD) analogWrite(PIN_MOTOR_TAIL, 0);
if (outPin == PIN_MOTOR_TAIL) analogWrite(PIN_MOTOR_HEAD, 0);
analogWrite(outPin, (v < 512)?0:255);
*last = v;
}
}
int minAudio = 1023;
int maxAudio = 0;
int lastAudio = 0;
int inPeak = 0;
int outPeak = 0;
void do_audio()
{
int v;
v = analogRead(PIN_AUDIO_IN);
if (v < minAudio) minAudio = v;
if (v > maxAudio) maxAudio = v;
if (v != lastAudio) {
//Serial.println(v);
}
lastAudio = v;
// Find the beginning and end of peaks
v = abs(v-512);
if (v > 10) {
if (inPeak < 100) {
inPeak++;
}
if (inPeak == 100) {
inPeak++; // Saturate the counter
if (debug) {
Serial.println("Start peak");
}
outPeak = 0;
mouth_open();
}
}
if (v < 5) {
if (outPeak < 100) {
outPeak++;
}
if (outPeak == 100) {
outPeak++; // Saturate the counter
if (debug) {
Serial.println("End peak");
}
inPeak = 0;
mouth_close();
}
}
}
long nextReport = 0;
void loop()
{
long now = millis();
if (Serial.available()) {
handle_char(Serial.read());
}
if (debug) {
if (now > nextReport) {
Serial.print("Audio ");
Serial.print(minAudio);
Serial.print(" (");
Serial.print(lastAudio);
Serial.print(") ");
Serial.println(maxAudio);
nextReport = now + 1000;
}
}
motor_timeouts();
do_button(PIN_BUTTON, 0, "BUTTON");
if (passThrough) {
do_sense(PIN_CMD_MOUTH, PIN_MOTOR_MOUTH, &lastCmdMouth, "mouth");
do_sense(PIN_CMD_HEAD, PIN_MOTOR_HEAD, &lastCmdHead, "head");
do_sense(PIN_CMD_TAIL, PIN_MOTOR_TAIL, &lastCmdTail, "tail");
}
do_audio();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment