// Bad Apple RESPECT!
// include necessary standard library
#include <string>
#include <vector>
#include <iostream>
using namespace std;
// include SD card library
#include "SD.h"
// include libraries for threads
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
// Define PIN
#define PIN_11_C 15
#define PIN_21_C 2
#define PIN_31_C 4
#define PIN_41_C 21
#define PIN_51_C 22
#define PIN_61_C 16 // RX2
#define PIN_11_A 13
#define PIN_12_A 12
#define PIN_13_A 14
#define PIN_14_A 27
#define PIN_15_A 26
#define PIN_16_A 25
#define PIN_17_A 33
#define PIN_18_A 32
// Define SD card connection
#define SD_MOSI_PIN 20
#define SD_MISO_PIN 19
#define SD_SCLK_PIN 18
#define SD_CS_PIN 5
// Define BUZZER connection
#define BUZZER_PIN 17 // TX2
// Define map data
#define WIDTH 8
#define HEIGHT 6
#define FPS 24
#define DELAY_MS 1
#define REVISION_MS 4.02
#define PIC_NUM 4391
// Define music data
#define BEAT_FREQUENCY 50
#define QUARTER_NOTE_DURATION 433.86 // ms // (bad apple BPM = 138)??
#define SHUT_DURATION 180
#define SHEET_LENGTH 917
string tone_hz_arr[86][2] = {
// {"(簡譜碼)(升降符)"}
{"00", "0.00000"},{"10", "261.626"},{"20", "293.665"},{"30", "329.628"},{"40", "349.228"},{"50", "391.995"},{"60", "440.000"},{"70", "493.883"},{"12", "277.183"},{"21", "277.183"},{"22", "311.127"},{"31", "311.127"},{"42", "369.994"},{"51", "369.994"},{"52", "415.305"},{"61", "415.305"},{"62", "466.164"},{"71", "466.164"},
{".10", "130.813"},{".20", "146.832"},{".30", "164.814"},{".40", "174.614"},{".50", "195.998"},{".60", "220.000"},{".70", "246.942"},{".12", "138.591"},{".21", "138.591"},{".22", "155.563"},{".31", "155.563"},{".42", "184.997"},{".51", "184.997"},{".52", "184.997"},{".61", "184.997"},{".62", "233.082"},{".71", "233.082"},
{"..10", "65.4064"},{"..20", "73.4162"},{"..30", "82.4069"},{"..40", "87.3071"},{"..50", "97.9989"},{"..60", "110.000"},{"..70", "123.471"},{"..12", "69.2957"},{"..21", "69.2957"},{"..22", "77.7817"},{"..31", "77.7817"},{"..42", "92.4986"},{"..51", "92.4986"},{"..52", "103.826"},{"..61", "103.826"},{"..62", "116.541"},{"..71", "116.541"},
{"1.0", "523.251"},{"2.0", "587.330"},{"3.0", "659.255"},{"4.0", "698.456"},{"5.0", "783.991"},{"6.0", "880.000"},{"7.0", "987.767"},{"1.2", "554.365"},{"2.1", "554.365"},{"2.2", "622.254"},{"3.1", "622.254"},{"4.2", "739.989"},{"5.1", "739.989"},{"5.2", "830.609"},{"6.1", "830.609"},{"6.2", "932.328"},{"7.1", "932.328"},
{"1..0", "1046.50"},{"2..0", "1174.66"},{"3..0", "1318.51"},{"4..0", "1396.91"},{"5..0", "1567.98"},{"6..0", "1760.00"},{"7..0", "1975.53"},{"1..2", "1108.73"},{"2..1", "1108.73"},{"2..2", "1244.51"},{"3..1", "1244.51"},{"4..2", "1479.98"},{"5..1", "1479.98"},{"5..2", "1661.22"},{"6..1", "1661.22"},{"6..2", "1864.66"},{"7..1", "1864.66"}
};
// pin array
const int allPin[] = {
PIN_11_C, PIN_21_C, PIN_31_C, PIN_41_C, PIN_51_C, PIN_61_C, PIN_11_A, PIN_12_A, PIN_13_A, PIN_14_A, PIN_15_A, PIN_16_A, PIN_17_A, PIN_18_A,
BUZZER_PIN, SD_MOSI_PIN, SD_MISO_PIN, SD_SCLK_PIN, SD_CS_PIN
};
const int pinLineHeadArr[] = {
PIN_11_C,
PIN_21_C,
PIN_31_C,
PIN_41_C,
PIN_51_C,
PIN_61_C,
};
const int pinColTopArr[] = {
PIN_11_A,
PIN_12_A,
PIN_13_A,
PIN_14_A,
PIN_15_A,
PIN_16_A,
PIN_17_A,
PIN_18_A,
};
void set_pin_mode(){
// set pin mode
for (int p:allPin){
pinMode(p, OUTPUT);
}
}
void set_buzz_PWM(){
// set LED controller for PWM buzzer
ledcSetup(0, 5000, 8); // use LED controller 0, freq is 5000 Hz, 分辨率為8位
// set buzzer pin
ledcAttachPin(BUZZER_PIN, 0);
}
void load_SD_card(){
// load SD card
if (!SD.begin(SD_CS_PIN)) {
Serial.println("Card initialization failed!");
while (true);
}
}
// audio functions
void playTone(double frequency, double duration) {
// set PWM frequency
ledcWriteTone(0, frequency);
delay(duration);
ledcWrite(0, 0);
}
string getLine(File textFile){
if (!textFile.available()){
Serial.println("no more lines in file!");
while (true);
}
string result;
char c_temp = textFile.read();
while (c_temp != '\n'){
result += c_temp;
c_temp = textFile.read(); // output an unknown char if read a '\n'
}
result.erase(result.size() - 1); // remove an unknown char at last
return result;
}
const vector<string> split(const string& str, const string& pattern) {
vector<string> result;
string::size_type begin, end;
end = str.find(pattern);
begin = 0;
while (end != string::npos) {
if (end - begin != 0) {
result.push_back(str.substr(begin, end-begin));
}
begin = end + pattern.size();
end = str.find(pattern, begin);
}
if (begin != str.length()) {
result.push_back(str.substr(begin));
}
return result;
}
string find_freq(string s){
if (s[0] == 'x'){
return to_string(BEAT_FREQUENCY);
}
for (string* s_arr:tone_hz_arr){
if (s_arr[0] == s){
return s_arr[1];
}
}
return "error";
}
void play_music(void *audioFilePointer){
// cout << "start" << endl;
File audioFile = *((File *)audioFilePointer); // 取得傳入的參數
for (int i = 0; i < SHEET_LENGTH; i++){
string line = getLine(audioFile);
vector<string> v = split(line, ",");
float freq = stof(find_freq(v[0] + v[3]));
if (stoi(v[2])){
float duration = stof(v[1]) * QUARTER_NOTE_DURATION;
playTone(freq, duration);
}else{
float duration = stof(v[1]) * (QUARTER_NOTE_DURATION - SHUT_DURATION);
float shut_duration = stof(v[1]) * (SHUT_DURATION);
playTone(freq, duration);
delay(shut_duration);
}
}
// cout << "finished" << endl;
vTaskDelete(NULL); // 任務執行完畢後刪除自身
}
void setup() {
Serial.begin(115200);
set_pin_mode();
set_buzz_PWM();
load_SD_card();
// reading file from the card:
File screenFile = SD.open("/map.txt", FILE_READ);
File audioFile = SD.open("/sheet.txt", FILE_READ);
// check file is open
if (!screenFile) {
Serial.println("screenFile file was not opened");
return ;
}
if (!audioFile) {
Serial.println("file was not opened");
return ;
}
// use thread to play LED and music at the same time
// play LED
xTaskCreate(play_LED, "Play LED", 5000, &screenFile, 1, NULL);
// play music
play_music(&audioFile);
}
void loop() {
// null
}
// led functions
void play_LED(void *screenFilePointer){
File screenFile = *((File *)screenFilePointer); // 取得傳入的參數
for (int i = 0; i < PIC_NUM; i++){
char* matrix[HEIGHT];
for (int j = 0; j < HEIGHT; j++){
char* line_temp = new char[WIDTH];
for (int k = 0; k < WIDTH; k++){
line_temp[k] = screenFile.read();
}
screenFile.read(); // skip '\n'
screenFile.read(); // skip unknown char
matrix[j] = line_temp;
}
draw(matrix, FPS);
for (int j = 0; j < HEIGHT; j++){
delete[] matrix[j];
}
delay(REVISION_MS);
}
vTaskDelete(NULL); // 任務執行完畢後刪除自身
}
void closeAllLight(){
for (int p: pinLineHeadArr){
digitalWrite(p, HIGH);
}
for (int p: pinColTopArr){
digitalWrite(p, LOW);
}
}
void draw(char* matrix[HEIGHT], int fps){
float time_start = millis(); // get start time (ms)
float wait_ms = 1000.0 / fps;
while (millis() - time_start < wait_ms){
for (int i = 0; i < HEIGHT; i++){
drawLine(i, matrix[i]);
delay(DELAY_MS);
closeAllLight();
}
}
}
void drawLine(int lineNum, char* pos){
for (int i = 0; i < HEIGHT; i ++){
if (i != lineNum){
digitalWrite(pinLineHeadArr[i], HIGH); // high to close the line
}else{
digitalWrite(pinLineHeadArr[i], LOW);
}
}
for (int i = 0; i < WIDTH; i ++){
byte num = pos[i] - '0';
digitalWrite(pinColTopArr[i], num);
}
}