// BezDratak na dalkove ovladani - OVLADAC (KLIENT) // muze prehravat MP3 a slouzit jako bluetooth reproduktor // // Ovladac rizeny kontroleram Ardiono UNO // osazeno LCD shieldem a LT8920 (vysilac/prijimac 2.4 GHz) #define noDEBUG #define pinCS A1 //5 #define pinRST A2 //6 #define pinPKT 2 // A3 4 #define pinLCD 10 #define maxlen 64 #define btnSelect 0x01 //tlacitka #define btnLeft 0x02 #define btnRight 0x03 #define btnUp 0x04 #define btnDown 0x05 #define btnSelect2 0x81 //tlacitka s dlouhym stiskem #define btnLeft2 0x82 #define btnRight2 0x83 #define btnUp2 0x84 #define btnDown2 0x85 #define dispOff 80 #define maxErrors 8 #define noMode -1 #define btMode 1 #define usbMode 2 #define sdMode 3 #define divisor 1500 //2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484 #define wifiCanal 2483 - 2402 // nastaveny kanal #include #include #include LT8920 lt(pinCS, pinPKT, pinRST); LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // inicializace LCD displeje bool dispOn, waitResponse; uint8_t seq, errors; uint16_t mp3Order, mp3Count; int8_t state = -1; int8_t mode; int dispTime; int responseCount; unsigned long mp3Time, mp3Elapsed, pressTime; unsigned long secs = 0; char mp3File[17]; uint8_t response[maxlen + 1]; uint8_t currentCmd[maxlen]; // ----------------------------------------------------------------------------------------------------- void clearTimes() { mp3Time = 0; mp3Elapsed = 0; } void clearValues(bool all) { seq = 0; errors = 0; mp3File[0] = '\0'; mp3Order = 0; clearTimes(); if (all) { mp3Count = 0; mode = noMode; } } // -- LCD shield 16x2 ---------------------------------------------------------------------------------- void initLCD() { dispTime = dispOff; dispOn = true; lcd.begin(16, 2); pinMode(pinLCD, OUTPUT); digitalWrite(pinLCD, HIGH); lcd.print(F("BezDratak")); lcd.setCursor(0, 1); lcd.print(F("MP3 a Bluetooth")); } void lcdLine(uint8_t r, char info[]) { lcd.setCursor(0, r); lcd.print(info); for (byte i = strlen(info); i < 16; i++) lcd.print(" "); } void lcdInfo() { if (!dispOn) return; if (mode == btMode) { lcd.clear(); lcd.print(F(" BLUETOOTH ")); lcd.setCursor(0, 1); lcd.print(F(" reproduktor ")); return; } char e[] = " \0"; lcdLine(0, e); lcd.setCursor(0, 0); if (mp3Order > 0) lcd.print(mp3Order); else lcd.print("---"); lcd.print("/"); if (mp3Count > 0) lcd.print(mp3Count); else lcd.print("---"); lcd.setCursor(9, 0); lcd.print("|"); unsigned long r = mp3Time; if (r == 0) { if (mode == usbMode) lcd.print(F(" [USB]")); if (mode == sdMode) lcd.print(F(" [SD]")); } else { if (mp3Elapsed < mp3Time) r = mp3Time - mp3Elapsed; int m = r / 60; int s = r % 60; if (m < 100) lcd.print(" "); if (m < 10) lcd.print("0"); lcd.print(m); lcd.print(":"); if (s < 10) lcd.print("0"); lcd.print(s); } lcdLine(1, mp3File); } void lcdOn() { dispTime = dispOff; dispOn = true; digitalWrite(pinLCD, HIGH); lcdInfo(); } uint8_t readKey() { uint8_t result = 0xFF; pressTime = 0; int n = analogRead(0); if (n < 95) result = btnRight; //Vpravo(RIGHT) if ((n > 95) && (n < 150)) result = btnUp; //Nahoru(UP) if ((n > 150) && (n < 350)) result = btnDown; //Dolu(DOWN) if ((n > 350) && (n < 500)) result = btnLeft; //Vlevo(LEFT) if ((n > 500) && (n < 750)) result = btnSelect; //Vyber(SELECT) if (result < 255) { // test dlouhy stisk unsigned long t = millis(); bool w = true; while (analogRead(0) < 750) { pressTime = millis() - t; if (w && (pressTime > 1200)) { w = false; lcd.setCursor(14, 0); lcd.print(" *"); } } if (pressTime > 1200) result = result | 0x80; //byl dlouhy stisk if (pressTime < 100) result = 0xFF; //prilis kratky stisk #ifdef DEBUG Serial.println("readKey ----------------------------------------------"); Serial.print(result, HEX); Serial.print("; analog: "); Serial.println(n); #endif } return result; } uint8_t selectMode() { lcd.clear(); lcd.print(" Vyber zdroje "); delay(1000); lcd.clear(); lcd.print("up/down: bltooth"); lcd.setCursor(0, 1); lcd.print("left:sd righ:usb"); unsigned long t = millis(); while ((millis() - t) < 8000) { uint8_t k = readKey(); if (k < 255) return k; } return btnSelect; } // -- BezdratovĂ˝ vysilac+priimac 2,4GHz XY-WB s IO LT8920 ---------------------------------------------- void initLT() { SPI.begin(); SPI.beginTransaction(SPISettings(12000000, MSBFIRST, SPI_MODE1)); //12000000 SPI.setClockDivider(SPI_CLOCK_DIV16); //SPI_CLOCK_DIV16 lt.begin(); lt.writeRegister(35, 0x0F00); //Setting the number of repeated dispatches to 255 (F) lt.writeRegister(9, 0x8400); //setCurrentControl(8, 4); //vykona lt.setDataRate(LT8920::LT8920_1MBPS); //LT8920_62KBPS 1MBPS, pri nizsich rychlostech je neustale chyba CRC lt.writeRegister(45, 0x0080); //doporucene nastaveni pro 1MB/sec lt.setChannel(wifiCanal); //2402 + n lt.setSyncWord(0x123A123B123C123D); //volitelne ID, modul komunikuje s jinym modulem pouze pokud ma stejne ID } void ltSend(char* cmd) { //odesle data na wifi uint8_t n = strlen(cmd); currentCmd[0] = 0; if (n == 0) return; if (n < maxlen - 2) { for (byte i = 0; i < maxlen; i++) currentCmd[i] = 0; memcpy(currentCmd, cmd, n); waitResponse = lt.sendPacket(currentCmd, n); if (waitResponse) { lt.startListening(); interrupts(); #ifdef DEBUG Serial.print("Sent: "); Serial.print(cmd); Serial.print("; seq"); Serial.println(seq); #endif } } } // -- cteni nazvu souboru MP3 ----------------------------------------------------------------------------- int lastDot(char p[]) { int result = strlen(p) - 1; for (int i = result; i >= 0; i--) { if (p[i] == '.') return i; } return result; } void getName(char respStr[]) { //prevezme jmeno souboru, poslednich 16 znaku bez pripony for (byte i = 0; i < 16; i++) mp3File[i] = '\0'; char* p = respStr; p += 3; int tecka = lastDot(p); #ifdef DEBUG Serial.print("lastDot: "); Serial.println(tecka); #endif p[tecka] = '\0'; int8_t n = strlen(p); int8_t ix = 0; int8_t s = n - 16; //ignorovat priponu .mp3 if (s < 0) s = 0; for (byte i = s; i < n; i++) { char c = p[i]; if (byte(c) == 13) return; if (byte(c) > 32) { mp3File[ix] = c; ix++; if (ix >= 16) return; } } } // -- dekodovani prijate pakety -------------------------------------------------------------------------- uint16_t atValue(char* pattern, char* buf, uint16_t defa, bool* found) { *found = false; char* p = strstr(buf, pattern); if (p == NULL) return defa; *found = true; p += 3; uint16_t result = strtol(p, NULL, 16); #ifdef DEBUG Serial.print("atValue: "); Serial.print(pattern); Serial.print(" = "); Serial.println(result); #endif return result; } bool testMode(char* respStr) { bool found; char p[] = "Qm+\0"; uint8_t n = atValue(p, respStr, mode, &found); if (found) { if (mode != n) clearValues(true); //pri zmene modu nacist vse znovu mode = n; } return found; } bool testState(char* respStr) { bool found; char p[] = "Mp+\0"; state = atValue(p, respStr, state, &found); return found; } bool testOrder(char* respStr) { bool found; char p[] = "M1+\0"; uint16_t n = atValue(p, respStr, mp3Order, &found); if (found && (mp3Order != n)) { if (mp3Order > 0) clearValues(false); //jiny soubor mp3Order = n; lcdOn(); } return found; } bool testCount(char* respStr) { bool found; char p[] = "M2+\0"; int n = atValue(p, respStr, mp3Count, &found); if (found) mp3Count = n; return found; } bool testTime(char* respStr) { bool found; char p[] = "Mt+\0"; mp3Time = atValue(p, respStr, mp3Time, &found); return found; } bool testElapsed(char* respStr) { bool found; char p[] = "Mk+\0"; mp3Elapsed = atValue(p, respStr, mp3Elapsed, &found); return found; } bool testFilename(char* respStr) { char* p = strstr(respStr, "Mf+"); if (p) getName(respStr); return (p != NULL); } // --------------------------------------------------------------------------------------------------- void parseResponse() { //zpracuje prijata data z wifi char respStr[maxlen]; #ifdef DEBUG for (byte i = 0; i < responseCount; i++) { Serial.print(response[i], HEX); Serial.print(" "); } Serial.println((char*)response); #endif byte t = 0; for (byte i = 0; i < responseCount; i++) { char c = response[i]; if (c == 10) break; respStr[t] = c; t++; } respStr[t] = '\0'; #ifdef DEBUG Serial.print("Response: "); Serial.println(respStr); #endif if (testMode(respStr)) return; if (testState(respStr)) return; if (testOrder(respStr)) return; if (testCount(respStr)) return; if (testTime(respStr)) return; if (testElapsed(respStr)) return; if (testFilename(respStr)) return; } //-------------------------------------------------------------------------------------------------------- void goFolder(bool fwd) { //o slozku vpred/zpet char cmd[16]; if (mp3Count > 0) { if (fwd) strcpy(cmd, "AT+AA06\0"); else strcpy(cmd, "AT+AA07\0"); ltSend(cmd); } clearValues(false); } byte pressCnt() { if (pressTime > 5000) return 50; else return (pressTime / 100); } bool musicMode(char* p) { uint8_t m = selectMode(); switch (m) { case btnLeft: strcpy(p, "AT+CM03\0"); return true; case btnRight: strcpy(p, "AT+CM02\0"); return true; case btnDown: ; case btnUp: strcpy(p, "AT+CM01\0"); return true; default: return false; } } void svcKey(uint8_t k) { //cteni klaves ovladace if (!dispOn) { //pouze rozsviti displej lcdOn(); delay(200); return; } seq = 1; dispTime = dispOff; char cmd[16]; switch (k) { case btnSelect: // play/pause strcpy(cmd, "AT+CB\0"); ltSend(cmd); return; case btnLeft: // o jeden zpet clearValues(false); strcpy(cmd, "AT+CD\0"); ltSend(cmd); return; case btnRight: // o jeden dal clearValues(false); strcpy(cmd, "AT+CC\0"); ltSend(cmd); return; case btnUp: // zesilit strcpy(cmd, "AT+CE\0"); ltSend(cmd); return; case btnDown: // zeslabit strcpy(cmd, "AT+CF\0"); ltSend(cmd); return; case (0x80 | btnSelect): // vyber zdroje Bluetooth, USB, SD if (musicMode(cmd)) { clearValues(true); ltSend(cmd); return; } return; case (0x80 | btnLeft): // o slozku zpet goFolder(false); return; case (0x80 | btnRight): // o slozku dal goFolder(true); return; case (0x80 | btnDown): // posun zpet strcpy(cmd, "AT+AA05\0"); for (byte i = 0; i < pressCnt(); i++) { ltSend(cmd); delay(200); } clearTimes(); return; case (0x80 | btnUp): // posun vpred strcpy(cmd, "AT+AA04\0"); for (byte i = 0; i < pressCnt(); i++) { ltSend(cmd); delay(200); } clearTimes(); return; } } void svcBT201() { //prubezna komunikace s BT201 #ifdef DEBUG Serial.print("svcBT201 mode: "); Serial.print(mode); Serial.print("; seq: "); Serial.print(seq); Serial.print("; sec: "); Serial.println(secs); #endif char p[16]; for (byte i = 0; i < 16; i++) p[i] = 0; switch (seq) { case 0: //zjisteni modu, nebo stavu if (dispOn) { strcpy(p, "AT+QM\0"); ltSend(p); } else seq++; break; case 1: //nazev aktivniho souboru if ((mode > btMode) && (strlen(mp3File) == 0)) { strcpy(p, "AT+MF\0"); ltSend(p); } else seq++; break; case 2: //celkovy pocet souboru na pametove karte, nebo poradi aktivniho souboru if (mode > btMode) { if (mp3Count == 0) strcpy(p, "AT+M2\0"); else strcpy(p, "AT+M1\0"); ltSend(p); } else seq++; break; case 3: //celkovy cas aktivniho souboru if (dispOn && (mode > btMode)) { if (mp3Order == 0) strcpy(p, "AT+M1\0"); else if (mp3Time == 0) strcpy(p, "AT+MT\0"); else strcpy(p, "AT+MK\0"); ltSend(p); } else seq = 0; break; } } // ---------------------------------------------------------------------------------------------------- void ltEvent() { if (waitResponse && lt.available()) { noInterrupts(); responseCount = lt.read(response, maxlen - 1); #ifdef DEBUG Serial.print("Interrupt: "); Serial.println(responseCount); Serial.println((char*)currentCmd); #endif } } void setup() { #ifdef DEBUG Serial.begin(115200); Serial.println("LT9820"); #endif clearValues(true); initLCD(); initLT(); currentCmd[0] = '\0'; responseCount = 0; response[0] = 0; waitResponse = false; attachInterrupt(digitalPinToInterrupt(pinPKT), ltEvent, HIGH); } void loop() { unsigned long pp = millis() / divisor; uint8_t k = readKey(); if (k < 0xFF) { svcKey(k); secs = millis() / divisor; return; } if (secs != pp) { secs = pp; if (dispOn && (dispTime == 0)) { lcd.clear(); digitalWrite(pinLCD, LOW); dispOn = false; return; } else { if (dispTime > 0) dispTime--; } if (waitResponse) { #ifdef DEBUG Serial.print("Odpover neprisla: "); Serial.println(responseCount); Serial.println((char*)currentCmd); #endif lcd.setCursor(9, 0); lcd.print("!"); waitResponse = false; secs = millis() / divisor; return; } svcBT201(); } if (waitResponse && (responseCount > 0)) { response[responseCount] = 0; parseResponse(); errors = 0; seq++; if (seq > 3) seq = 0; responseCount = 0; response[0] = 0; currentCmd[0] = 0; waitResponse = false; lcdInfo(); } }