# Overview The Furious Interconnected Shouting Helper (FISH) is an interactive connected personal assistant. It is also a fish. The FISH performs two main classes of tasks—it sets and manage alarms, and performs Google Assistant tasks. The FISH consists of a battery powered ESP32 microcontroller connected to an amplified speaker, microphone, PIR motion sensor, and a stepper motor to control mouth movements. It sits on a wall and, when it detects movement, listens for a loud start signal to record a command. After recording a verbal command from the user, it sends this audio data to our proxy server for processing. We leverage the Google Speech API to perform a speech to text conversion and parse for keywords to decide how to delegate tasks. Alarm management is handled on the ESP32, and Google Assistant tasks are handled by an instance of a Google Assistant living on our server. The server will return a response to the FISH, which will in turn relay it to the user. <center> <iframe width="560" height="315" src="https://www.youtube.com/embed/bPRJAFwixCo" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe> </center> ### System Functionality and State Machine The FISH provides the full functionality of a Google Assistant and an (obnoxious singing) alarm clock in an entertaining way. The PIR sensor detects motion and alerts the user that it is listening for a loud noise to start the request sequence. Once a loud noise is detected, the FISH records the user's voice command and converts it to text using a direct query to the Google Text-to-Speech API. If the user's request has to do with setting or editing an alarm, this is handled on the microcontroller. Otherwise, the user's request is sent to our proxy server, which processes the request and serves it to an instance of a Google Assistant, returning a text transcript of the Assistant's response. This text string is passed to the Emic 2, a text-to-speech module that synthesizes speech and plays it through an amplified speaker. Throughout the time of the speech, the stepper motor syncs the mouth's movement to fit the words being spoken. <!-- YORAI IF YOU WANT TO EMBED VIDEOS PUT THEM IM MEDIA FOLDER AND REFERENCE HERE. (Awesome got it. Will do.) Locally hosted video embedded example: <center> <video width="500" height="400" controls> <source src="./media/6s08_EX02_final_product.mp4" type="video/mp4" > Your browser does not support the video tag. </video> </center> --> # System Design Our project consists of two primary components: the physical FISH which is controlled by the ESP32 microcontroller and the proxy server that interfaces with the Google Assistant. ### Hardware <center> <img src="./media/system_schematic.png" width="600"/> </center> ##Decision Making and Challenges ###Interfacing With Hardware The very first step, after deciding how to create the FISH, was to <b> physically </b> build it. Initially, we decided to use a small servo motor to control the mouth movements. Even though integrating it into the CAD model was challenging, it was worth it since interfacing with it was very simple. We could simply input an angle in our code, and the motor would turn until it reached it. We couldn’t ask for much more than that. To sync the servo the the fish’s speech, we sampled audio from a computer using an analog pin on the ESP32 before it was amplified by the speaker. We mapped the reading to fall between 0-60 degrees (work range for the mouth), and sent the resulting angle to the servo motor. Since the motor was relatively slow, the jaw movement seemed smooth despite some unwanted spikes in the audio readings. Everything seemed to work well. Although like many things that work a little too well, there was a catch.<br> Not long after the integration of the servo and the simple sync capabilities, we decided that the motor was too loud and could not be used anymore. It was hard to understand the words that were played from the speaker. The overall user experience could be improved.<br> <center> <iframe width="560" height="315" src="https://www.youtube.com/embed/W4Y7hL4nNws" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe> </center> With the guidance of our instructor, we decided to change our motor choice to a stepper motor. Despite having to almost completely redesign the FISH, we looked forward to use the stepper. This kind of motor is a lot quieter, but also a lot harder to work with.<br> In contrast to the servo motor, that is able to move to a specific angle on command, the stepper only knows how to move one step in a particular direction. And since its increments are very small and quick, the motor was also very sensitive to jitters in the signal. To solve those challenges, we wrote a dedicated class that can handle all aspects of the motor control. This class is discussed in detail in the code description section. As much as switching to a stepper motor was challenging, it was worth it. The FISH's speech ended up being far more comprehensible with the reduced motor noise.<br> <br> The other main hardware challenge we had to overcome was figuring out how to distribute power throughot the system. Even though the FISH started out with 2 LiPo batteries and a 9V power supply, it is powered by only one 3.7V LiPo in its final form. Our approach to energy management and distribution is discussed in more detail in that section.<br> <br> Adittionally, interfacing with the other hardware components was pretty simple. This next section will explain why we chose the specific componenets we used and how we interfaced with them. (exact connection pins and voltages are all included in the schematic above)<br> <b>PIR sensor</b><br> <b>Purpose: </b> This sensor is used to detect motion in front of the FISH, which will initiate the user communication states in the state machine.<br> <b>Why this one?</b> This PIR sensor was the most natural choice for us for two reasons. Firstly, it is very easy to interface with; When movement is detected, it outputs a digital HIGH signal. And LOW when there is no movement. (Sensitivity is determined using potentiometers on the board.) Secondly, the sensor's ability to detect <b> motion</b>, and not simply the distance of an object was appealing to us since that is exactly what we were looking for.<br> <b>Emic 2 Text To Speech (TTS) Board</b><br> <b>Purpose:</b> Since one of the main capabilities of the FISH is its ability to talk, and data is mostly kept in text form, we tried to find a way to synthesize speech from text on board the FISH itself. After reaching a dead end while trying to synthesize speech on board the ESP32 using various libraries, using APIs, and even trying to send audio files directly from the server, we concluded that outsourcing the TTS requirement to an independent board would be the best solution.<br> <b>Why this one?</b> Being very impressed by the capabilities of the Emic 2 board (it can sing! Well, one song but we still used this function - see alarms), and the fact that there are not many TTS boards out there, we decided to give that board a shot, and were not disappointed. Adding to that, the way the Emic 2 receives information is by serial communication. Owing to that, the ESP32 is able to communicate with it through its additional serial port (see <a href="https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/HardwareSerial.cpp"> hardwareSerial </a>) in a pretty conveient way. Strings are being sent (<a href="https://www.parallax.com/sites/default/files/downloads/30016-Emic-2-Text-To-Speech-Documentation-v1.2.pdf">along with a few special characters</a>) from the ESP32 to the Emic 2, and are being spoken out.<br> These are the main components we had to actively interface with. The <b>Microphone, Amplifier, and Speaker</b> are more straightforward. The <b>Boost boards</b> used to distribute power to the system will be, as written earlier, discussed further in that section.<br> We orginized all the component on a soldered perfboard, to make our system more robust and permanent than it was on a breadboard.<br> <center> <img src="./media/Breadboard.jpg" width="300"/> <img src="./media/Board.jpg" width="300"/> <img src="./media/boardBack.jpg" width="300"/> </center> ##Code Description ### ESP32 code ####State Machine Architecture <center> <img src="./media/FishStateMachine.jpg" width="600"/> </center> ####State Machine Description IDLE:<br> ● The IDLE state is the base sensing state of the FISH. This state reads the PIR sensor data and current time to begin the two branches of funcctionality in the FISH: the alarm and do task branches.<br><br> Double Check:<br> ● In the Double Check state, the state machine simply makes sure that a human object is still in view of the FISH within 2 seconds of the first PIR high reading. If the PIR is still reading high after 2 seconds but before 2.5 seconds, the machine will transition to the Start state.<br><br> Start:<br> ● The Start state takes in raw audio data from the microphone lookingfor a distinct signal from the user. If a loud enough noise is heard, the FISH will exclaim, “OKAY, I’M LISTENING!” and continues to the Listen state. If the signal is not heard within 10 seconds, the machine returns to the IDLE state.<br><br> Listen:<br> ● When in the Listen state, the FISH listens to the user’s voice for 5 seconds, then sends the audio data to Google Speech API. The FISH then transitions to the Do Task state with the audio data converted into a String.<br><br> Do Task:<br> ● The Do Task state sends the command given to the FISH to the 6.08 server. The server will then parse this string to interact with the online Google Assistant API.<br><br> Alarm:<br> ● The Alarm state is activated when the current time on the ESP matches one of the alarms read from the 6.08 server. This state then enters the Make Noise state.<br><br> Make Noise:<br> ● The Make Noise simply has the FISH sing a song to wake up the user then moves into the Yell Receptor state.<br><br> Yell Receptor:<br> ● The Yell Receptor state listens for audio data from the microphone to determine the consciousness of the user. If the user yells at the FISH, then the FISH will stop singing to the user. Otherwise, the FISH will continue to sing indefinitely.<br><br> ####Interfacing With Stepper And Syncing to Speech <!-- this is still work in progress --> The ```fishStepper``` class, which is in charge of controlling the stepper motor and syncing it to the FISH’s speech, is able to initialize motors, track their location (angle) over time, and most importantly sync the movement of the jaw to audio speech. More Specifically, it houses the functions ```syncMouth()```, ```initialize()```,```update()```,and ```hardZero()```. And the variables ```pos``` and ```target```, which hold information about the current position of the motor and the target position it needs to get to. If they are equal, then the motor is already at the target.<br> On startup (in ```setup()```), the function ```initialize()``` is called. It assignes pinModes to all the necessary pins and equates the position variables to 0. In the loop, all the other functions are used together.<br> The most fundamental, and most important, of them all is ```update```. This function is in charge of getting the stepper from its current ```pos``` to the needed ```target```. Every time ```update``` is called, it moves the motor one step to the needed direction, while making sure not to step too fast which could cause a stall or overshoot.<br> <!-- ``` void update() { //makes sure we don't step too fast if (millis() - lastStep >= 13) { //determine the direction we want to move in if (target == pos) { //Serial.println("we here"); //lastStep = millis(); } else if (target > pos) { digitalWrite(dirPin, opening); digitalWrite(stepPin, HIGH); digitalWrite(stepPin, LOW); pos += 1; lastStep = millis(); //Serial.print("open "); } else if (target < pos) { digitalWrite(dirPin, closing); digitalWrite(stepPin, HIGH); digitalWrite(stepPin, LOW); pos -= 1; lastStep = millis(); //Serial.print("close "); } } ``` --> For our purposes, we didn't only want the FISH's jaw to move to a certain position, but to also be synced to its speech. Therefore, we developed the second function ```syncMouth```. This function utilizes the ```update``` funtion, and works in a fairly straightforward way.<br> For starters, the function reads the analog signal that is being sent to the speaker by the Emic 2. That signal is the amplitude of the speech the FISH needs to sync to. In general, the greater the magnitude is, the bigger the angle of the jaw should be.<br> Since the audio signal was not very clean, we had to smooth it out using a running average filter, which is the second part of of the function. To get readings that are somewhat different from each other, the audio signal is samples every 5 milliseconds. That signal is then mapped to fall into the range of the jaw, and added as the tenth and final entry of the filter. The average of the last ten readings is then assigned to ```target``` variable. Finally, ```update()``` is called, and the stepper is moving to the desired location.<br> One side effect of the un-smoothness of the audio signal, was that the stepper tended to drift open over time. To make sure the mouth is closed all the way, we added two small "tricks". The first, in ```syncMouth()```, we are adding an extra step in the closing direction when ```target``` is very small. The other is the ```hardZero()``` function itself. ``` void syncMouth(int sensorPin) { int initReading = analogRead(sensorPin); if (initReading < minRead) { initReading = minRead; } if (initReading > maxRead) { initReading = maxRead; } if (millis() - lastReading >= 5) { reading = initReading; int angle = map(reading, minRead, maxRead, 0, maxSteps); //shift things in filter for (int i = 0; i < 9; i++) { filter[i] = filter[i + 1]; } filter[9] = angle; avg = 0; for (int i = 0; i < 10; i++) { avg += filter[i]; } avg /= 10; // done dealing with filter lastReading = millis(); //for really small angles, throw in another step in the closing direction - so the mouth will be closed all the way if (avg < 2 && avg > 0) { digitalWrite(dirPin, closing); digitalWrite(stepPin, HIGH); digitalWrite(stepPin, LOW); } //write to stepper - target = avg; } update(); } ``` <br> The ```hardZero(inputSteps)``` function is the simplest of them all. All it does is turn the stepper motor inputSteps steps in the closing direction. This is a "blind" function, in the sense that it does'nt know if it runs into the upper teeth of the FISH or not. We are using it when we want to make sure that the mouth is absolutely closed. Since the stepper motor is not strong enough to damage the FISH, we allow the function to run into the top teeth from time to time. This function also assigns value 0 to ```pos```. <br> ####Alarms The way this function works is thoroughly explained in the Energy Management And Distribution section. Scroll down to get there! ### Server Architecture: Integration with Google Assistant Once our server receives a GET request from the ESP32 with a text query passed in with the parameter ```instructions```; it passes this request to the Google Assistant. We leveraged the Google Assistant SDK to get our Assistant running on our server and leveraged some of the SDK's sample code to initialize our Google Assistant and process the ```AssistResponse``` objects returned by an ```AssistRequest```. We created an RPC client stub that feeds requests to an instance of a Google Assistant device. Our server interfaces with the Google Assistant with a gRPC connection call to the ```Assist``` method of the ```EmbeddedAssistant``` object. We first created a device model that was appropriate for our use case; this can be done easily using the SDK's registration tool (```googlesamples-assistant-devicetool```). We then registered a device instance under this existing device model with this same registration tool. ```textinput.py``` defines a ```SampleTextAssistant``` class that supports text based conversations. Its ```assist``` method sends a text query to the Assistant and parses through the ```supplemental_display_text``` field of the ```DialogStateOut``` associated with the ```AssistResponse``` messages streamed back to our server. ```assistant_helpers.py``` has helper functions that log ```AssistRequest``` and ```AssistResponse``` fields without audio data. ```run.py``` is the script that our ESP32 sends requests to; its ```request_handler``` function ensures that a valid request was received and calls the ```query``` function, passing in ```instructions```. ```query``` loads ```credentials.json```, a file that contains the OAuth 2.0 credentials of a specified user. This file can be generated with Google's ```google-auth-oauthlib``` tool. The ```query``` function then creates a ```SampleTextAssistant``` object and passes the user's request into its ```assist``` funtion. It then ensures that a proper response is received from the Assistant and returns it—this is the response that is sent back to the ESP32. ##Energy Management And Distribution As promised earlier, in this section we will discuss our approach to distributing and conserving energy in our system.<br> <br> ####Distribution The distribution of power in the system evolved quite a bit throughout the time we worked on the project. From being powered by a 1-cell LiPo with another 2-cell LiPo, to later being powered by two 1-cell LiPo Batteries in conjunction with a 9V power supply, the FISH was finally driven by only one 1-cell LiPo battery. The challenges we encountered arose because we had to work with three voltage levels simultaneously. 9 volts for the stepper motor, 5 volts for the ESP32, Amplifier, and PIR sensor, and 3.3 volts for the microphone.<br> To achieve that, we chose boost the voltage of a 1-cell LiPo using two boosters, connected in parallel. That setup allowed us to boost the battery to 9 volts and 5 volts, while we could use the 3.3 volts regulator onboard the ESP32 to power the microphone. Using only one power source was not only energetically efficient, since we didn't lose any enery to heat due to resistors, but was also a great improvement in our quality of life. Not having to charge two battries or having to disconnect three power sourced before uploading new code was a change we welcomed with a smile :) . <br> <br> ####Energy Efficiency In The Code<br> One of the main goals we initially had was to use wifi communications as little as possible, since those are consuming more energy than other offline operation. Because we knew that when the FISH was interacting with a user it had to make queries to Google Speech API and Google Assistant, we decided to try and make other functionalities of the FISH work offline. The most prominent of those is the Alarms functionality.<br> Along the process of creating that featue, we experimented with two approaches. The first one, was to design an alarms database on the class server and check whether there is an alarm every few minutes. That approach heavily relied on HTTP queries and as much as it was elegant, it was energetically inefficient. Therefore, we diverged to another approach that handles all the Alarms functions on board the ESP32.<br> To achieve this goal, we had to do two things: establish an alarms time-list on the ESP32, and sync the ESP32 to real time - so it would know when is the time to wake up the user with a great song:<br> <center> <iframe width="560" height="315" src="https://www.youtube.com/embed/ci9wgm_AHDw" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe> </center> Starting with the alarm time list, we coded it quite quickly in the form of two C++ arrays. One for the hour and the other for the minutes. The other requirement was a little more tricky. In order to turn wifi on as little as possible, we decided to sync the ESP32 to real-time only once, on startup, and then update it using the ```millis()``` function. After getting the startup real time from an Time Zone Database API, using our ```syncTime``` function in the wifi client class, ```updateTime``` function is called once every loop.<br> In short, ```updateTime``` does its job by calculating the difference between the initial ```syncTime``` milliseconds value to the current ```millis())```, and appends it to the initial real time it got on startup:<br> ``` void updateTime() { int deltaMillis = millis() - iMillis; int deltaMinutes = deltaMillis / 60000; //this is how many minutes passed since the initial sync int deltaHours = 0; while (deltaMinutes >= 60) { deltaHours ++; deltaMinutes -= 60; } nowHour = iHour + deltaHours; nowMinute = iMinute + deltaMinutes; } ``` With this solution, in comparison to our other trials, the accuracy of the alarm times is much better than periodic database queries, and is much more energy efficient since it only requires one HTTP GET request per startup. <br> <br> ####Current Draw and Lifetime<br> In order to calculate the lifetime of the FISH, we started by measuring the currents it drew in various times during its operation. We noticed two distinct magnitudes of current throughout our tests: <br><br> <b>Mouth Sync Current= 400 mA</b><br> The time when the stepper motor in working to sync the FISH’s mouth to its speech.<br> <b>IDLE Current = 320-330 mA</b><br> The time when the ESP32 is waiting for sensor signal or retrieving information from the server. <br><br> We decided to divide the energy consumption into two "modes" because there was no significant difference in the current-draw between the time when the ESP32 was making HTTP WiFi requests, and when it was waiting for sensor readings. Whereas the stepper’s operation was significantly more energy consuming. Therefore, we decided to divide current magnitudes to <b>Mouth Sync</b>, and <b>IDLE</b>, which is essentially any time when there is no motor movement.<br> To make use of the current measurements, we calculated the times in which every mode is being used:<br><br> <center> <img src="./media/powerTimeline.jpeg" width="800"/> </center> <br> In the diagram above, the numbers in the circles stand for seconds. Since the response from the server varies in length, the last time is not constant. From our tests, we found that there are, broadly speaking, three kinds of response lengths:<br><br> <b>Short Responses = 4 seconds</b>. In return to "What is the date today?", "How tall is Barack Obama?" <br> <b>Medium Responses = 8 seconds</b>. In return to "What is the weather today?"<br> <b>Long Responses = 30-35 seconds</b>. In return to "What is Canada?"<br> <br> During our time with the FISH, we could also tell that the distribution of the response lengths is roughly:<br> <center> <img src="./media/distribution.png" width="400"/> </center> <br> Therefore, in our following calculations, we will use the average ~8 seconds as the response speech time.<br> In that case, a cycle of use takes 29 seconds. At this moment, the FISH is powered by a 1200mAH LiPo battery, so this capacity value will be used in the calculations. #####Constantly In Use In the case when a user just can't get enough of the FISH, we can calculate he will be able to use it until the battery runs out. In that case, the average current draw is:<br> <img src="http://latex.codecogs.com/svg.latex?I_{avg,const}=\frac{14[seconds]*400[mA]+15[seconds]*325[mA]}{29[seconds]} = 361 $$\approx$$ 360 mA" border="0"/> To find Lifetime:<br> <img src="http://latex.codecogs.com/svg.latex?T=\frac{capacity[mAH]}{I_{avg,const}[mA]} = \frac{1200}{360} = 3.333 Hours" border="0"/> #####Normal Use / Idle In the case where the user is (unfortunately for him) not obsessed with the FISH, and is only interacting with it once every 15 minutes, the small LiPo battery will last for a longer time. More specifically, after every 29 seconds use-cycle, we will add 871 seconds of IDLE inactivity.<br> <img src="http://latex.codecogs.com/svg.latex?I_{avg,norm}=\frac{14[seconds]*400[mA]+15[seconds]*325[mA]+871[seconds]*325[mA]}{900[seconds]}=326$$\approx$$325 mA" border="0"/> As we can see, the current draw of normal use is nearly identical to the current draw in IDLE mode. To calculate Lifetime:<br> <img src="http://latex.codecogs.com/svg.latex?T=\frac{capacity[mAH]}{I_{avg,const}[mA]} = \frac{1200}{325} = 3.69 Hours" border="0"/> <br> Even though the lifetime of the FISH is not bad, we would like it to be longer. Mainly because if a user would want to use the Alarms functionality to wake him up, it requires the FISH to be working throughout the night. Even though it has not been done yet, we thought about two main ways in which we could solve that problem. The first and easier one, would simply be to get a bigger battery. The FISH has enough internal space to fit a (at least) 12,000 mAH LiPo battery. Such change would immidiately allow the FISH to operate for 33 hours.<br> The other option, which is more preferable and frankly more interesting to explore, is to make the whole system more efficient. We think that the current situation originates in the fact that stepper motors don't really have an "off" mode. Even when they are not getting a command to move, current is still flowing through their coils to secure the motor shaft in place. Therefore, in a future revision we could use relays or transistors to completely disconnect the power to the steppers when they do not need to work for long periods of time (making sure to close the FISH's mouth when the motors gets power again). ###Conclusions And Looking Forward Buiding the FISH was a fantastic learning experience, and a great way to reinforce the topics learned in the 6.08 introduction to electrical engineering and computer science class. Luckily, the scope of this project was large enough to allow our team to revisit many of the Internet of Things portions of the class, such as working with proxy servers, APIs, and both secured and unsecured WiFi connections. Furthermore, our team gained extensive experience with the hardware side of EECS as well. We really tried to push the boundary of what we could do with our project on the hardware side while still maintaining a power conserving balance. The project truly was a great introduction to team based projects and EECS principles.<br> <br> The future of the FISH is bright! Down the road, our team would like to add some more functionality to the FISH both with regard to hardware as well as software. For example, adding moving fins to the FISH's exterior would add a great realism factor to our mounted helper. Additionally, we believe that we can make our system a lot more energetically efficient. To lengthen the lifetime of the FISH, we might implement a prescheduled night sleep cycle. Our team would also like to work more with Python language processing on the server to add more spoken functionality to the FISH's actions. For example, the ability to change existing alarms without the Google Assistant App would be a fun and interesting challenge to undertake in the future.<br> ### Parts List <p> <b>Adafruit PIR Motion Sensor</b><br> URL: https://www.adafruit.com/product/189<br> Unit Price: $9.95<br> Quantity: 1<br> Description: Recognizes whether a user is nearby<br> Use Case: After recognition, the system will alert the user and go into listening mode.<br> <br> <b>Adafruit STEREO 3.7W CLASS D AUDIO AMPLIFIER - MAX98306</b><br> URL: https://www.adafruit.com/product/987<br> Unit Price: $8.95<br> Quantity: 1<br> Description: Amplifies the square wave provided by the Emic 2 before feeding it to the speaker<br> Use Case: Necessary to operate the speaker<br> <br> <b>Adafruit SPEAKER - 3" DIAMETER - 4 OHM 3 WATT</b><br> URL: https://www.adafruit.com/product/1314<br> Unit Price: $1.95<br> Quantity: 1<br> Description: Transforms square wave signal to audible sound<br> Use Case: Will be used to interact with the user. Tell weather, next meeting, wake up alarm, etc.<br> <br> <b>Battery: 3.7 Volts, 1 cell LiPo</b><br> Description: Portable power bank, usually used to charge smartphones.<br> Use Case: Will be used to power the system (ESP32, amplifier, stepper).<br> <br> <b>XL6009 Voltage Boost Module DC-DC </b><br> URL: https://www.amazon.com/HiLetgo-Adjustable-DC3-0-30V-DC5-35V-Converter/dp/B00LP2LZ4M<br> Unit Price: $4.99<br> Quantity: 1<br> Description: Boosts the battery voltage fromt the initial 3.7-4.2V to 9<br> Use Case: Necessary to operate the speaker<br> <br> <b>5V Voltage Regulator and Booster - Adafruit PowerBoost 1000 </b><br> URL: https://www.adafruit.com/product/2465<br> Unit Price: $19.95<br> Quantity: 1<br> Description: Boosts the battery voltage fromt the initial 3.7-4.2V to 5V. Serves as a chrger for the LiPo Battery<br> Use Case: Regulated 5V necessary to operate the PIR sensor and ESP 32<br> <br> <b>Stepper Motor - Nema 11</b><br> URL: https://www.ebay.com/itm/New-Nema11-Hybrid-Stepper-Motor-Bipolar-2-phase-12V-0-67A28mm-DIY-Robot-CNC-/112493410378<br> Unit Price: $12<br> Quantity: 1<br> Description: Motor the can move in increments of one step (1/200 of one revolution) to both directions by digital command.<br> Use Case: Allows the FISH's mouth to sync to its speech<br> <br> <b>Stepper Motor Controller - A4988</b><br> URL: https://www.amazon.com/BIQU-Compatible-Stepper-StepStick-Controller/dp/B01FFGAKK8/ref=sr_1_3?s=industrial&ie=UTF8&qid=1526046015&sr=1-3&keywords=a4988<br> Unit Price: ~$2<br> Quantity: 1<br> Description: Board that translates the microcontroller's digital commands to motor movement<br> Use Case: Necessary to operate the motor<br> <br> <b>Emic 2 Text To Speech Board</b><br> URL: https://www.adafruit.com/product/924<br> Unit Price: ~$60<br> Quantity: 1<br> Description: Capable of outputting audio speech from text input<br> Use Case: This board allows the fish to say any text message. It allows the code to work with text strings and not audio files.<br> <br> <br> <br> </p> <center> Created by Dylan Doblar, Liam Ackerman, and Yorai Shaoul. </center> <!-- EMBED YOUTUBE VIDEOS <center> <iframe width="560" height="315" src="https://www.youtube.com/embed/UtrimZxRXBM" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe> </center> --> <!-- Here's some code: ``` //Draws the grid on the display void drawGrid(void) { uint16_t color = TFT_WHITE; for (int16_t x = 1; x < GRIDX - 1; x++) { for (int16_t y = 1; y < GRIDY - 1; y++) { if ((grid[x][y]) != (newgrid[x][y])) { if (newgrid[x][y] == 1) color = 0xFFFF; //random(0xFFFF); else color = 0; tft.fillRect(CELLXY * x, CELLXY * y, CELLXY, CELLXY, color); } } } } ``` -->