Created
November 20, 2016 13:38
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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