// Included libraries
#include <WiFi.h>
#include <Wire.h>
#include <PubSubClient.h>
#include <LiquidCrystal_I2C.h>
// WiFi
const char* ssid = "Wokwi-GUEST";
WiFiClient wifiClient;
// MQTT
#define USERNAME "baworthi"
#define START_TOPIC "uark/csce5013/" USERNAME "/song"
#define NOTES_TOPIC "uark/csce5013/" USERNAME "/notes"
#define INFO_TOPIC "uark/csce5013/" USERNAME "/info"
String notesPayload = "";
String infoPayload = "";
#define SERVER_PORT 1883
const char* mqtt_server = "broker.hivemq.com";
PubSubClient client(wifiClient);
// Pins
#define BUZZER_PIN 12
#define BUTTON_PIN 13
// Notes and Pitches
typedef enum{
A_NOTE,B_NOTE,C_NOTE,D_NOTE,E_NOTE,F_NOTE,G_NOTE
} PITCH;
typedef struct{
PITCH pitch;
int length;
} NOTE;
// Tones
const int tones[7] = {440, 494, 523, 587, 659, 698, 784};
// Test notesTest
// String notesTest[50] = {""};
NOTE notes[50];
const int noteLength = 2;
int songLength = 0;
// Flags
int lcdFlag = 0;
int songFlag = 0;
int buttonFlag = 0;
// int notesFlag = 0;
#define SONG_TASK_TIME 250
// Button debouncing
int buttonState;
int lastButtonState = LOW;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
// Create LCD
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2);
// Setup WiFi
void setupWifi() {
// WiFi setup
Serial.begin(9600);
Serial.print("Connecting to ");
Serial.print(ssid);
WiFi.begin("Wokwi-GUEST", "", 6);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
Serial.print(".");
}
Serial.println(" Connected!");
}
void lcdTask() {
if (lcdFlag == 1) {
lcdFlag = 0;
lcd.clear();
if (infoPayload.indexOf('\n') >= 0) {
String firstLine = infoPayload.substring(0, infoPayload.indexOf('\n'));
String secondLine = infoPayload.substring(infoPayload.indexOf('\n') + 1);
lcd.print(firstLine);
lcd.setCursor(0, 1);
lcd.print(secondLine);
} else if (infoPayload.length() > 16) {
lcd.print(infoPayload);
delay(1000);
for (int i = 0; i < infoPayload.length() - 16; i++) {
lcd.scrollDisplayLeft();
delay(500); // Adjust this delay time to control the scrolling speed
}
} else {
lcd.print(infoPayload);
}
}
}
void parseNotes() {
memset(notes, 0, sizeof(notes));
songLength = 0;
String notesSubStr;
for (int i = 0; i < notesPayload.length(); i += noteLength) {
notesSubStr = notesPayload.substring(i, i + noteLength);
PITCH pitch = PITCH(toupper(notesSubStr[0])-65);
int length = notesSubStr[1]-48;
notes[i].pitch = pitch;
notes[i].length = length;
songLength++;
// Serial.println(songLength);
}
Serial.println(songLength);
}
void playSong() {
static int currentNote = 0;
static int lastRun = 0;
static int isSongPlaying = 0;
static int isFirstNote = 0;
if (songFlag == 1) {
songFlag = 0;
parseNotes();
isSongPlaying = 1;
currentNote = 0;
isFirstNote = 1;
}
long currentTime = millis();
if (isSongPlaying == 1) {
if ((currentTime - lastRun) > SONG_TASK_TIME) {
lastRun = currentTime;
if (isFirstNote == 1) {
isFirstNote = 0;
tone(BUZZER_PIN, tones[notes[currentNote].pitch],250*notes[currentNote].length);
}
if (notes[currentNote].length > 0) {
notes[currentNote].length -= 1;
} else {
tone(BUZZER_PIN, tones[notes[currentNote].pitch],250*notes[currentNote].length);
currentNote++;
if (currentNote == songLength) {
noTone(BUZZER_PIN);
isSongPlaying = 0;
}
}
}
}
}
void newSong() {
int buttonReading = digitalRead(BUTTON_PIN);
if (buttonReading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (buttonReading != buttonState) {
buttonState = buttonReading;
if (buttonState == HIGH) {
Serial.println("Sending Highway to Hell");
client.publish(INFO_TOPIC, "Highway to Hell");
client.publish(NOTES_TOPIC, "A3D2F1G1D2F1G1D2F1G1D1F1A2");
} else {
Serial.println("Sending Randomness");
client.publish(INFO_TOPIC, "My Random Creation");
client.publish(NOTES_TOPIC, "A1B4A1B4C2C2D1E1A1B4");
}
}
}
lastButtonState = buttonReading;
}
void setup() {
// Setup wifi connection
setupWifi();
// Setup MQTT
client.setServer(mqtt_server, SERVER_PORT);
client.setCallback(callback);
// Set pin modes
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT);
// LCD
lcd.init();
lcd.print("No Music");
}
void callback(char* topic, byte* payload, unsigned int length) {
// Convert the payload to a String
String message = "";
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
if (String(topic) == INFO_TOPIC) {
infoPayload = message;
Serial.println("Trying to print info payload");
Serial.println(infoPayload);
lcdFlag = 1;
} else if (String(topic) == NOTES_TOPIC) {
notesPayload = message;
Serial.println("Trying to print notes payload");
Serial.println(notesPayload);
// notesFlag = 1;
songFlag = 1;
}
// lcdFlag = 1;
}
// Reconnect MQTT
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect(USERNAME)) {
Serial.println(" Connected!");
// Start command for MQTT server
client.publish(START_TOPIC, "START");
// Once connected, subscribe to input channel
client.subscribe(INFO_TOPIC);
client.subscribe(NOTES_TOPIC);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void loop() {
// Program loop
if (!client.connected()) {
reconnect();
}
client.loop();
lcdTask();
playSong();
// Serial.println("Did we get here");
newSong();
// Serial.println("After new song");
delay(100);
// Serial.println("testing");
}