#include <Keypad.h>
#include <stdio.h>
// Define the size of the keypad (4 rows and 4 columns)
const byte ROWS = 4; // four rows
const byte COLS = 3; // four columns
#define TOGGLE_SWITCH_PIN PH4
#define HIGH_THRESHOLD 600
#define LOW_THRESHOLD 540
// Define the keys on your keypad
char keys[ROWS][COLS] = {
{'1', '2', '3'},
{'4', '5', '6'},
{'7', '8', '9'},
{'*', '0', '#'},
};
// Connect the keypad's row and column pins to your Arduino
byte rowPins[ROWS] = {A0, A1, A2, A3}; // Row pins connected to A0, A1, A2, A3
byte colPins[COLS] = {A4, A5, A6}; // Column pins connected to A4, A5, A6, A7
// Create the Keypad object
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// Function prototypes for USART communication
void USART_init(unsigned int ubrr);
unsigned char USART_receive(void);
void USART_transmit(unsigned char data);
void printString(const char* str);
void setup() {
cli(); // Disable all interrupts
// Set pin 7 as an input with internal pull-up enabled (PD7)
DDRH &= ~(1 << TOGGLE_SWITCH_PIN); // Clear the PD7 bit in DDRD to make it an input
PORTH |= (1 << TOGGLE_SWITCH_PIN); // Set the PD7 bit in PORTD to enable the internal pull-up resistor
// Set up the ADC
ADMUX = (1 << REFS0); // Set reference voltage to AVcc
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); // Enable ADC, set prescaler to 64
// *** Timer/Counter 3 Configuration (for 3 servos on pins PE3, PE4, and PE5) ***
// Set pins PE3 (Pin 5), PE4 (Pin 2), as output pins for OC3A, OC3B,
DDRE |= (1 << DDE3) | (1 << DDE4) ;
// Configure Timer/Counter 3 for Fast PWM mode 14
// Set Output Compare Mode 2 (Clear OC3A, OC3B, and OC3C on compare match)
TCCR3A = (1 << COM3A1) | (1 << COM3B1) | (1 << COM3C1) | (1 << WGM31);
// Set Fast PWM mode 14, set ICR3 as TOP, and set prescaler to clk/256
TCCR3B = (1 << WGM33) | (1 << WGM32) | (1 << CS32);
// Set the TOP value for 20 ms period (for all 3 servos controlled by Timer 3)
ICR3 = 1250;
// Set initial duty cycles (neutral midpoint for each servo)
OCR3A = 93; // Servo 1 (Pin 5)
OCR3B = 93; // Servo 2 (Pin 2)
// Initialize Timer/Counter 3 to start from 0
TCNT3 = 0;
// *** Timer/Counter 1 Configuration (for 2 servos on pins PB1 and PB2) ***
// Set pins PB1 (Pin 11) and PB2 (Pin 12) as output pins for OC1A and OC1B
DDRB |= (1 << DDB5) | (1 << DDB6);
// Configure Timer/Counter 1 for Fast PWM mode 14
// Set Output Compare Mode 2 (Clear OC1A and OC1B on compare match)
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
// Set Fast PWM mode 14, set ICR1 as TOP, and set prescaler to clk/256
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12);
// Set the TOP value for 20 ms period (for the 2 servos controlled by Timer 1)
ICR1 = 1250;
// Set initial duty cycles (neutral midpoint for each servo)
OCR1A = 93; // Servo 3 (Pin 11)
OCR1B = 93; // Servo 4 (Pin 12)
// Initialize Timer/Counter 1 to start from 0
TCNT1 = 0;
// *** Timer/Counter 4 Configuration (for 2 servos on pins PB1 and PB2) ***
// Set pins PH3 (Pin 6) as output pins for OC1A and OC1B
DDRH |= (1 << DDH3) ;
// Configure Timer/Counter 4 for Fast PWM mode 14
// Set Output Compare Mode 2 (Clear OC1A and OC1B on compare match)
TCCR4A = (1 << COM4A1) | (1 << COM4B1) | (1 << WGM41);
// Set Fast PWM mode 14, set ICR1 as TOP, and set prescaler to clk/256
TCCR4B = (1 << WGM43) | (1 << WGM42) | (1 << CS42);
// Set the TOP value for 20 ms period (for the servo controlled by Timer 4)
ICR4 = 1250;
// Set initial duty cycles (neutral midpoint for servo)
OCR4A = 93; // Servo 5 (Pin 6)
// Initialize Timer/Counter 4 to start from 0
TCNT4 = 0;
sei(); // Enable interrupts globally
USART_init(103); // Initialize USART with baud rate 9600
setNeutralExpression(); // Start with neutral expression
}
// Function to set neutral expression
void setNeutralExpression() {
// Move all servos to 0 degrees
OCR3A = 92; // Servo 1
OCR3B = 92; // Servo 2
OCR4A = 92; // Servo 3
OCR1A = 92; // Servo 4
OCR1B = 92; // Servo 5
_delay_ms(1000); // Wait for 1 second
}
// Function to set happy expression
void setHappyExpression() {
// Move all servos to 0 degrees
OCR3A = 92; // Servo 1
OCR3B = 92; // Servo 2
OCR4A = 63; // Servo 3
OCR1A = 125; // Servo 4
OCR1B = 125; // Servo 5
_delay_ms(1000); // Wait for 1 second
}
// Function to set sad expression
void setSadExpression() {
// Move all servos to 0 degrees
OCR3A = 63; // Servo 1
OCR3B = 125; // Servo 2
OCR4A = 63; // Servo 3
OCR1A = 125; // Servo 4
OCR1B = 63; // Servo 5
_delay_ms(1000); // Wait for 1 second
// Move all servos to 90 degrees
}
// Function to set angry expression
void setAngryExpression() {
// Move all servos to 0 degrees
OCR3A = 125; // Servo 1
OCR3B = 63; // Servo 2
OCR4A = 63; // Servo 3
OCR1A = 63; // Servo 4
OCR1B = 125; // Servo 5
_delay_ms(1000); // Wait for 1 second
}
// Function to set surprised expression
void setSurprisedExpression() {
// Move all servos to 0 degrees
OCR3A = 63; // Servo 1
OCR3B = 92; // Servo 2
OCR4A = 63; // Servo 3
OCR1A = 92; // Servo 4
OCR1B = 63; // Servo 5
_delay_ms(1000); // Wait for 1 second
}
// Read microphone input from ADC7 (A7 pin)
int readMicrophone() {
ADMUX &= 0xF0; // Clear the lower 4 bits to reset the ADC channel selection
ADMUX |= 0x07; // Set the MUX[3:0] bits to 0111 to select ADC7 (A7 pin)
ADCSRA |= (1 << ADSC); // Start the conversion
while (ADCSRA & (1 << ADSC)); // Wait for the conversion to finish
return ADC; // Return the ADC result
}
// USART Initialization
void USART_init(unsigned int ubrr) {
// Set baud rate
UBRR0H = (unsigned char)(ubrr >> 8);
UBRR0L = (unsigned char)ubrr;
// Enable receiver and transmitter
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
// Set frame format: 8 data bits, 1 stop bit
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}
// Function to receive data via USART
unsigned char USART_receive(void) {
// Wait for data to be received
while (!(UCSR0A & (1 << RXC0)));
// Get and return received data from buffer
return UDR0;
}
// Function to transmit data via USART
void USART_transmit(unsigned char data) {
// Wait for empty transmit buffer
while (!(UCSR0A & (1 << UDRE0)));
// Put data into buffer, sends the data
UDR0 = data;
}
// Print a string over USART
void printString(const char* str) {
while (*str) {
USART_transmit(*str);
str++;
}
}
// Function to print a number over USART (convert to ASCII)
void printNumber(int num) {
char buffer[10];
itoa(num, buffer, 10); // Convert the number to a string (decimal format)
printString(buffer); // Print the string over USART
}
// Main loop
void loop() {
// Read the state of the toggle switch
bool switchState = !(PINH & (1 << TOGGLE_SWITCH_PIN)); // Reads pin 7 (inverts logic for pull-up)
// Check the state of the toggle switch
if (switchState) { // Mode 1: Switch is ON (connected to ground)
printString("Mode 1: Switch is ON\r\n");
char key = keypad.getKey(); // Read the key pressed
if (key) { // If a key is pressed
USART_transmit(key); // Send the key over USART
// Perform actions based on the key pressed
switch (key) {
case '1':
setNeutralExpression(); // Show neutral expression
break;
case '2':
setHappyExpression(); // Show happy expression
break;
case '3':
setSadExpression(); // Show sad expression
break;
case '4':
setAngryExpression(); // Show angry expression
break;
case '5':
setSurprisedExpression(); // Show surprised expression
break;
default:
break; // No action for other keys
}
}
}
else { // Mode 2: Switch is OFF (not connected to ground, pulled HIGH)
// Handle microphone input in Mode 2
static int noSoundCounter = 0; // Counter for no sound detected
static bool isEmotionActive = false;
// Read microphone input
int micValue = readMicrophone(); // Read the value from the microphone
printString("Microphone ADC Value: ");
printNumber(micValue); // Send the ADC value to the serial monitor
printString("\r\n");
if (micValue < LOW_THRESHOLD) { // Low-volume sound detected
noSoundCounter++;
// If no sound detected for specific intervals, change expressions
if (noSoundCounter == 3) { // After 5 seconds
setNeutralExpression();
isEmotionActive = false;
}
else if (noSoundCounter == 5) { // After 8 seconds
setSadExpression();
isEmotionActive = false;
}
else if (noSoundCounter == 6) { // After 14 seconds
setAngryExpression();
isEmotionActive = false;
}
if (micValue > LOW_THRESHOLD) { // Low-volume sound detected
if (!isEmotionActive) {
setHappyExpression();
isEmotionActive = true; // Set flag to indicate an emotion is active
noSoundCounter = 0; // Reset no sound counter
}
}
else if (micValue > HIGH_THRESHOLD) { // High-volume sound detected
if (!isEmotionActive) {
setSurprisedExpression();
isEmotionActive = true; // Set flag to indicate an emotion is active
noSoundCounter = 0; // Reset no sound counter
}
}
// Small delay to prevent constant polling
_delay_ms(100);
}
// Add a small delay to avoid spamming the serial monitor in both modes
_delay_ms(500);
}
}