Lecture slides, class recording, and project specs for this module are available below.
You can find additional resources for this module (and others) in the Course Wiki.
Since we didn't cover the project fully in class, be sure to watch this project overview video before getting started!
Under construction. Apologies for the delay; for now please refer to the overview video above and check back here later!
Under construction. Apologies for the delay; for now please refer to the overview video above and check back here later!
Under construction. Apologies for the delay; for now please refer to the overview video above and check back here later!
You can also download the pseudocode in an .ino file here (just make sure you place the ino file into a folder for project 4 inside you Arduino projects directory).
#include <Servo.h>
#define potPin A#
#define servoPin #
#define button0 #
#define LED0 #
// Add support for 2 more buttons / LEDs?
// Global variables:
// For button debouncing
bool lastButtonState[3] = {0, 0, 0};
int debounceDelay = 50;
unsigned long lastButtonRead = 0;
// For toggling LEDs
bool LEDState[3] = {0, 0, 0};
// For reading the potentiometer
int LastPotValue = 0;
int potReadInterval = 100;
unsigned long lastPotRead = 0;
// Make a servo object
Servo servo;
// Function declaration(s)
void sendMessage(char messageType, int value);
void readMessage();
void setup() {
// Set pinmodes
// Attach servo object to servo pin
Serial.begin(9600);
}
void loop() {
unsigned long currentMillis = millis();
// Read the potentiometer only on certain intervals:
// (to prevent spamming the serial connection when turning)
if (currentMillis - lastPotRead > potReadInterval) {
lastPotRead = currentMillis;
if (LastPotValue != analogRead(potPin)) {
LastPotValue = analogRead(potPin);
sendMessage('P', LastPotValue);
}
}
//Read the button(s) using software debounce:
if (currentMillis - lastButtonRead > debounceDelay) {
lastButtonRead = currentMillis; // update the last button read time
// Implement the remaining debounce code here
}
//Process messages:
if (Serial.available()) {
readMessage();
}
/* Delay a bit
This is important because we need to give time for the UART input buffer to fill if we get
a message. Otherwise we might only read part of a message in the next loop iteration
*/
delay (20);
/* Bonus question:
What math did I do to get 20ms?
Hint: 20ms adds a good safety margin, even for a full 10 digit message at 9600 baud
(type char + 10 digits + newline), not forgetting that we also have start and stop
bits for each byte to account for
*/
}
/* readMessage()
Reads a message from serial and sets outputs accordingly
Message format: "[type][value]\n"
Example: T1\n
Example: A128\n
Example: D3000\n
Example: P1023\n
*/
void readMessage() {
// Read the message type
char messageType = Serial.read();
// Check if the type character is valid
if (messageType != 'T' && messageType != 'P' && messageType != 'A' && messageType != 'D') {
// If it's not, clear the UART buffer because it probably has junk data in it
while (Serial.available()) {
Serial.read(); // Just need to read the next byte to clear it; not doing anything with it
}
return;
}
// Read the value
int value = 0; // Start off with a value of zero
while (Serial.available()) { // While there are still bytes in the UART
char c = Serial.read(); // read a character from the UART
// Check if the character is an ASCII digit
if (c >= '0' && c <= '9') {
// If it is, shift the current value left by one (multiply by 10) and add the new digit
// The (c - '0') part converts the ASCII character to an integer we can add to value
// (you'll learn about this in ECE 109)
value = value * 10 + (c - '0');
} else {
// If it's not a digit, we've reached the end of the number
// (or received junk data) so we can break out of the loop
break;
}
}
// Set outputs according to the message type
if (messageType == 'T') {
// Toggle the LED state for the given value
// Write the new state to that LED
// ALSO: handle the case where value is > the number of
// LEDs on your communicator (hint: toggle all your LED states)
} else if (messageType == 'P') {
// Constraind and map the given value to a servo angle
// Write that to the servo
} else if (messageType == 'A') {
// Use this section if you want to implement support for
// the optional analog message type (dimmable LED maybe?)
} else if (messageType == 'D') {
// Use this section if you want to implement support for
// the optional delay message type (blink LED(s) at
// a certain speed maybe?)
}
}
/* sendMessage('[message type char]', value)
Sends a message to serial with a message type and a value followed by a terminator
messageType(char): 'P' for potentiometer, 'T' for button, 'A' for analog, 'D' for delay
value (int): the value to send
Example: sendMessage('P', 1023) sends "P1023\n" to serial
*/
void sendMessage(char messageType, int value) {
// Temporary array to store value digits
// Note: This means our value can't be greater than 10 digits!
char buffer[10];
int i = 0;
// Extract value digits and store in buffer
if (value < 1) { // Still need to store a 0 in the buffer if value is 0
buffer[i] = 0;
i++;
} else {
// Extract digits and store in buffer using modulo
// This works in reverse, so if we have 937, we get {7,3,9} in buffer
while (value > 0) {
buffer[i] = value % 10;
value /= 10;
i++;
// Fail gracefully by chopping off the rest of the number (break loop)
// instead of overflowing the buffer
if (i >= 10) break;
}
}
// Send the message type
Serial.write(messageType);
// Send digits in correct order (iterate through the buffer in reverse)
while (i > 0) {
// Decrement i first to get the correct index
// (because current i is 1 greater than the last index)
i--;
// Note: the + '0' below converst our value to an ASCII value:
Serial.write(buffer[i]+'0');
}
// Send the message terminator (newline character)
Serial.write('\n');
}