diff options
Diffstat (limited to 'library/ADK2/examples/clock/clock.ino')
-rw-r--r-- | library/ADK2/examples/clock/clock.ino | 1660 |
1 files changed, 1660 insertions, 0 deletions
diff --git a/library/ADK2/examples/clock/clock.ino b/library/ADK2/examples/clock/clock.ino new file mode 100644 index 0000000..44f9044 --- /dev/null +++ b/library/ADK2/examples/clock/clock.ino @@ -0,0 +1,1660 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ADK.h> +#include <HCI.h> + +ADK L; + +//android app needs to match this +#define BT_ADK_UUID 0x1d, 0xd3, 0x50, 0x50, 0xa4, 0x37, 0x11, 0xe1, 0xb3, 0xdd, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 + + +void adkPutchar(char c){Serial.write(c);} +extern "C" void dbgPrintf(const char *, ... ); + +enum AdkStates{ + AdkClock, + AdkAlarm, + AdkBrightness, + AdkColor, + AdkVolume, + AdkDisplay, + AdkPresets, + + //always last + AdkInvalid +}; + +enum AdkDisplayMode{ + + AdkShowAnimation, + AdkShowAccel, + AdkShowMag, + AdkShowTemp, + AdkShowHygro, + AdkShowBaro, + AdkShowProx, + AdkShowColor, + + AdkShowLast //always last +}; + +enum AdkAlarmStates { + AdkAlarmIdle, + AdkAlarmIdleWait, + AdkAlarmAlarm, + AdkAlarmSnooze +}; +/* + + button numbering (all in hex) + + __ __ __ __ __ __ + |1A| |18| |16| |14| |12| |11| 03 04 05 06 + -- -- -- -- -- -- 0A 09 08 07 + |1B| |19| |17| |15| |13| |10| + -- -- -- -- -- -- + +*/ + +#define BTN_SLIDER_0 0x00 +#define BTN_SLIDER_1 0x01 +#define BTN_SLIDER_2 0x02 +#define BTN_0_UP 0x1A +#define BTN_1_UP 0x18 +#define BTN_2_UP 0x16 +#define BTN_3_UP 0x14 +#define BTN_4_UP 0x12 +#define BTN_5_UP 0x11 +#define BTN_0_DN 0x1B +#define BTN_1_DN 0x19 +#define BTN_2_DN 0x17 +#define BTN_3_DN 0x15 +#define BTN_4_DN 0x13 +#define BTN_5_DN 0x10 +#define BTN_CLOCK 0x03 +#define BTN_ALARM 0x04 +#define BTN_BRIGHTNESS 0x05 +#define BTN_COLOR 0x06 +#define BTN_LOCK 0x07 +#define BTN_PRESETS 0x08 +#define BTN_DISPLAY 0x09 +#define BTN_VOLUME 0x0A + + + +#define BTN_MASK_PRESS 0x8000 +#define BTN_MASK_HOLD 0x4000 +#define BTN_MASK_RELEASE 0x2000 +#define BTN_MASK_ID 0x003F + +#define BTN_INITIAL_DELAY 10 //10ms before a click registers +#define BTN_HOLD_DELAY 400 //400mas before a click becomes a hold +#define BTN_AUTOREPEAT 100 //auto-repeat every 100ms + + + +#define SETTINGS_NAME "/AdkSettings.bin" +#define SETTINGS_MAGIX 0xAF +typedef struct AdkSettings{ + + uint8_t magix; + uint8_t ver; + +//v1 settings: + + uint8_t R, G, B, bri, vol, almH, almM, almOn; + char btName[249]; //null terminated + char btPIN[17]; //null terminated + + uint16_t almSnooze; + char almTune[256]; // null terminated + + uint8_t speed, displayMode; + +//later settings + +}AdkSettings; + + + +AdkSettings settings; +const char* btPIN = 0; + + +volatile static int32_t hTemp, hHum, bPress, bTemp; +volatile static uint16_t prox[7]; //prox, clear, IR ,R, G, B, temp pProx, pClear, pR, pG, pB, pIR, pTemp; +volatile static uint16_t proxNormalized[3]; +volatile static int16_t accel[3], mag[3]; +volatile static uint32_t btSSP = ADK_BT_SSP_DONE_VAL; +volatile static char locked = 0; + + +void btStart(); + +static uint16_t btnProcess(){ + + static uint64_t lastActionTime[32] = {0, }; + static uint32_t clickSent = 0; + static uint32_t holdSent = 0; + static uint32_t lastStates = 0; + uint32_t curState, t, i, mask; + + curState = (((uint32_t)L.capSenseButtons()) << 16) | L.capSenseIcons(); + t = lastStates ^ curState; + lastStates = curState; + + //update states for all buttons + for(mask = 1, i = 0; i < 32; i++, mask <<= 1) if(t & mask){ + + lastActionTime[i] = L.getUptime(); + } + + //generate events + for(mask = 1, i = 0; i < 32; i++, mask <<= 1){ + if(curState & mask){ + + uint64_t time = L.getUptime(); + uint64_t lapsed = time - lastActionTime[i]; + + if(holdSent & mask){ //maybe resend hold + + if(lapsed > BTN_AUTOREPEAT){ + + lastActionTime[i] = time; + return i | BTN_MASK_HOLD; + } + } + else if(clickSent & mask){ //maybe time for first hold + + if(lapsed > BTN_HOLD_DELAY){ + + holdSent |= mask; + lastActionTime[i] = time; + return i | BTN_MASK_HOLD; + } + } + else{ //maybe time to click + + if(lapsed > BTN_INITIAL_DELAY){ + + clickSent |= mask; + lastActionTime[i] = time; + return i | BTN_MASK_PRESS; + } + } + } + else if(clickSent & mask){ //release + + clickSent &=~ mask; + holdSent &=~ mask; + return i | BTN_MASK_RELEASE; + } + } + return 0; +} + +void readSettings(){ + + uint32_t read; + FatFileP f; + char r; + AdkSettings ts; + + //apply defaults + strcpy(settings.btName, "ADK 2012"); + strcpy(settings.btPIN, "1337"); + settings.magix = SETTINGS_MAGIX; + settings.ver = 1; + settings.R = 0; + settings.G = 0; + settings.B = 255; + settings.bri = 255; + settings.vol = 255; + settings.almH = 6; + settings.almM = 0; + settings.almOn = 0; + settings.speed = 1; + settings.displayMode = AdkShowAnimation; + settings.almSnooze = 10 * 60; //10-minute alarm + strcpy(settings.almTune, "/Tunes/Alarm_Rooster_02.ogg"); + + r = L.fatfsOpen(&f, SETTINGS_NAME, FATFS_READ); + if(!r){ + + r = L.fatfsRead(f, &ts, sizeof(AdkSettings), &read); + + if(r || read != sizeof(AdkSettings) || settings.magix != SETTINGS_MAGIX || settings.ver != 1){ + + //in future, check for other versions and read as needed here... + dbgPrintf("ADK: settings: file read failed: %d, %u/%u, 0x%x\n", r, read, sizeof(AdkSettings), settings.magix); + } + else settings = ts; + L.fatfsClose(f); + } + else dbgPrintf("ADK: settings: file open failed: %d\n", r); +} + +void writeSettings(){ + + FatFileP f; + char r; + + L.fatfsUnlink(SETTINGS_NAME); + r = L.fatfsOpen(&f, SETTINGS_NAME, FATFS_WRITE | FATFS_CREATE | FATFS_TRUNCATE); + if(!r){ + uint32_t written = 0; + + r = L.fatfsWrite(f, &settings, sizeof(AdkSettings), &written); + + if(r || written != sizeof(AdkSettings)) dbgPrintf("ADK: settings: file write failed: %d, %u/%u\n", r, written, sizeof(AdkSettings)); + L.fatfsClose(f); + } + else dbgPrintf("ADK: settings: file open failed: %d\n", r); +} + +void setup(void) +{ + + Serial.begin(115200); + + L.adkSetPutchar(adkPutchar); + L.adkInit(); + btStart(); + + if(L.fatfsMount()) dbgPrintf("ADK: failed to mount SD card\n"); + L.usbStart(); +} + +static uint8_t rnd(void){ + + static uint64_t seed = 7454131806685196871ULL; + + seed *= 2340027325706224672ULL; + seed += 7310016643071172983ULL; + seed %= 9876543210987654321ULL; + seed ^= seed << 3; + seed += L.getUptime(); + + return seed >> 53; +} + +void gethue(uint8_t h, uint8_t* Rp, uint8_t* Gp, uint8_t* Bp){ + uint8_t R, G, B, seg, prog; + + if(h == 255) h = 254; + seg = (unsigned)h / 85; + prog = (((uint32_t)((unsigned)h % 85)) << 8) / 85; + + switch(seg){ + case 0: R = prog; G = 0; B = 255 - prog; break; + case 1: R = 255 - prog; G = prog; B = 0; break; + case 2: R = 0; G = 255 - prog; B = prog; break; + } + + *Rp = R; + *Gp = G; + *Bp = B; +} + +static void screenClear(void){ + uint32_t i; + + for(i = 0; i < NUM_LEDS; i++) L.ledWrite(i, 0, 0, 0); //clear screen if leaving display mode +} + +void adkBtSspF(const uint8_t* mac, uint32_t val){ + + btSSP = val; + dbgPrintf("ssp with val %u\n", val); +} + +void loop(void) +{ + uint8_t r, g, b, dimR, dimG, dimB; + uint8_t colorSlider, volSlider, briSlider, speedSlider; + static const uint8_t dimIconFactor = 6; + AdkStates state = AdkInvalid; + AdkStates newState = AdkClock; + AdkAlarmStates alarmState = AdkAlarmIdle; + uint32_t i, j, k; + uint8_t loopCounter = 0; + uint64_t nextTime = 0; //for animation + unsigned int snoozeStart; + char alarmEnded = 1; + char alarmStop, alarmFake = 0; + int32_t alarmDirPos = -1; + FatDirP dir = 0; + char wasBt = 0; + static const uint8_t btSeq[] = {6,5,8,12,11}; + + readSettings(); + + if(0){ //very helpful to some of us - set alarm song to the first song we find :) + FatDirP dir; + if(!L.fatfsOpenDir(&dir, "/Tunes")){ + + FatFileInfo fi; + fi.longName = settings.almTune + 7; + fi.nameSz = 100; + + strcpy(settings.almTune, "/Tunes/"); + + if(!L.fatfsReadDir(dir, &fi)){ + + dbgPrintf("tune: '%s'\n", settings.almTune); + } else dbgPrintf("file find fail\n"); + } else dbgPrintf("dir open fail\n"); + } + + + btPIN = settings.btPIN; + L.setVolume(settings.vol); + dbgPrintf("ADK: setting BT name '%s' and pin '%s'\n", settings.btName, settings.btPIN); + if(!L.btSetDeviceClass(DEVICE_CLASS_SERVICE_AUDIO | DEVICE_CLASS_SERVICE_RENDERING | + DEVICE_CLASS_SERVICE_INFORMATION | (DEVICE_CLASS_MAJOR_AV << DEVICE_CLASS_MAJOR_SHIFT) | + (DEVICE_CLASS_MINOR_AV_PORTBL_AUDIO << DEVICE_CLASS_MINOR_AV_SHIFT))) dbgPrintf("ADK: Failed to set device class\n"); + if(!L.btSetLocalName(settings.btName)) dbgPrintf("ADK: failed to set BT name\n"); + if(!L.btDiscoverable(1)) dbgPrintf("ADK: Failed to set discoverable\n"); + if(!L.btConnectable(1)) dbgPrintf("ADK: Failed to set connectable\n"); + L.btSetSspCallback(adkBtSspF); + + while(1){ + + loopCounter++; + + uint16_t button = btnProcess(); + + if(locked == 2){ + if(button == (BTN_MASK_HOLD | BTN_LOCK)) locked = 0; + else button = 0; + } + else if(button == (BTN_MASK_PRESS | BTN_LOCK) && locked == 0) locked = 1; + else if(button == (BTN_MASK_RELEASE | BTN_LOCK) && locked == 1) locked = 2; + + L.adkEventProcess(); //let the adk framework do its thing + + r = (((uint32_t)settings.R) * (settings.bri + 1)) >> 8; + g = (((uint32_t)settings.G) * (settings.bri + 1)) >> 8; + b = (((uint32_t)settings.B) * (settings.bri + 1)) >> 8; + + dimR = (r + dimIconFactor - 1) / dimIconFactor; + dimG = (g + dimIconFactor - 1) / dimIconFactor; + dimB = (b + dimIconFactor - 1) / dimIconFactor; + + if(newState != AdkInvalid){ + state = newState; + newState = AdkInvalid; + for(i = 0; i < 8; i++) L.ledDrawIcon(i, dimR, dimG, dimB); + L.ledDrawIcon(state, r, g, b); + } + L.ledDrawIcon(7, locked ? r : dimR, locked ? g : dimG, locked ? b : dimB); + + if(!(loopCounter & 127)) if(!L.hygroRead((int32_t *)&hTemp, (int32_t *)&hHum)) hTemp = hHum = 0; + if(!(loopCounter & 7)){ + uint16_t proxMax; + + L.baroRead(3, (long *)&bPress, (long *)&bTemp); + L.alsRead((uint16_t *)prox + 0, (uint16_t *)prox + 1, (uint16_t *)prox + 3, (uint16_t *)prox + 4, (uint16_t *)prox + 5, (uint16_t *)prox + 2, (uint16_t *)prox + 6); + L.accelRead((int16_t *)accel + 0, (int16_t *)accel + 1, (int16_t *)accel + 2); + L.magRead((int16_t *)mag + 0, (int16_t *)mag + 1, (int16_t *)mag + 2); + for(i = 0; i < 3; i++) mag[i] <<= 4; //convert to 16-bit value + + //copy + for(i = 0; i < 3; i++) proxNormalized[i] = prox[i + 3]; + proxNormalized[2] *= 3; //blue needs more sensitivity + + //find max + proxMax = 0; + for(i = 0; i < 3; i++) if(proxMax < proxNormalized[i]) proxMax = proxNormalized[i]; + proxMax++; + + //normalize to 8-bits + for(i = 0; i < 3; i++) proxNormalized[i] = (proxNormalized[i] << 8) / proxMax; + + //exponentize (as per human eyes) + static const uint16_t exp[] = + { + 0,19,39,59,79,100,121,143,165,187,209,232,255,279,303,327,352,377,402,428,454,481,508,536,564,592,621,650,680,710,741,772,804, 836,869,902, + 936,970,1005,1040,1076,1113,1150,1187,1226,1264,1304,1344,1385,1426,1468,1511,1554,1598,1643,1688,1734,1781,1829,1877,1926,1976,2026,2078, + 2130,2183,2237,2292,2348,2404,2461,2520,2579,2639,2700,2762,2825,2889,2954,3020,3088,3156,3225,3295,3367,3439,3513,3588,3664,3741,3819,3899, + 3980,4062,4146,4231,4317,4404,4493,4583,4675,4768,4863,4959,5057,5156,5257,5359,5463,5568,5676,5785,5895,6008,6122,6238,6355,6475,6597, + 6720,6845,6973,7102,7233,7367,7502,7640,7780,7922,8066,8213,8362,8513,8666,8822,8981,9142,9305,9471,9640,9811,9986,10162,10342,10524,10710, + 10898,11089,11283,11480,11681,11884,12091,12301,12514,12731,12951,13174,13401,13632,13866,14104,14345,14591,14840,15093,15351,15612,15877,16147, + 16421,16699,16981,17268,17560,17856,18156,18462,18772,19087,19407,19733,20063,20398,20739,21085,21437,21794,22157,22525,22899,23279,23666, + 24058,24456,24861,25272,25689,26113,26544,26982,27426,27878,28336,28802,29275,29756,30244,30740,31243,31755,32274,32802,33338,33883,34436, + 34998,35568,36148,36737,37335,37942,38559,39186,39823,40469,41126,41793,42471,43159,43859,44569,45290,46023,46767,47523,48291,49071,49863, + 50668,51486,52316,53159,54016,54886,55770,56668,57580,58506,59447,60403,61373,62359,63361,64378,65412 + }; + + for(i = 0; i < 3; i++) proxNormalized[i] = exp[proxNormalized[i]]; + } + + if(wasBt && btSSP == ADK_BT_SSP_DONE_VAL){ //clear leftover segments + + L.ledWrite(btSeq[wasBt - 1], 0, 0, 0); + wasBt = 0; + } + else if(btSSP != ADK_BT_SSP_DONE_VAL){ + + k = btSSP; + for(i = 0, j = 100000; i < 6; i++, j /= 10){ + + L.ledDrawLetter(i, k / j + '0', 0, 0, settings.bri); + k %= j; + } + + L.ledWrite(9, 0, 0, 0); + L.ledWrite(2, 0, 0, 0); + + if(wasBt) L.ledWrite(btSeq[wasBt - 1], 0, 0, 0); + k = (L.getUptime() >> 7) % sizeof(btSeq); + wasBt = k + 1; + L.ledWrite(btSeq[k], 0, 0, settings.bri); + + button = 0; + } + else switch(state){ + + case AdkClock:{ + + uint16_t year; + uint8_t month, day, h, m, s, timechange = 1; + + //get and draw time + L.rtcGet(&year, &month, &day, &h, &m, &s); + L.ledDrawLetter(0, h / 10 + '0', r, g, b); + L.ledDrawLetter(1, h % 10 + '0', r, g, b); + L.ledDrawLetter(2, m / 10 + '0', r, g, b); + L.ledDrawLetter(3, m % 10 + '0', r, g, b); + L.ledDrawLetter(4, s / 10 + '0', r, g, b); + L.ledDrawLetter(5, s % 10 + '0', r, g, b); + L.ledWrite(9, r, g, b); + L.ledWrite(2, r, g, b); + + //handle buttons + if(button & (BTN_MASK_PRESS | BTN_MASK_HOLD)) switch(button & BTN_MASK_ID){ + + case BTN_5_DN: + if(s) s--; + break; + case BTN_5_UP: + if(s < 59) s++; + break; + case BTN_4_UP: + if(s < 50) s += 10; + break; + case BTN_4_DN: + if(s > 9) s -= 10; + break; + case BTN_3_UP: + if(m < 59) m++; + break; + case BTN_3_DN: + if(m) m--; + break; + case BTN_2_UP: + if(m < 50) m += 10; + break; + case BTN_2_DN: + if(m > 9) m -= 10; + break; + case BTN_1_UP: + if(h < 23) h++; + break; + case BTN_1_DN: + if(h) h--; + break; + case BTN_0_UP: + if(h < 14) h += 10; + break; + case BTN_0_DN : + if(h > 9) h -=10; + break; + default: timechange = 0; + } else timechange = 0; + + if(timechange){ + L.rtcSet(year, month, day, h, m, s); + } + break; + } + case AdkAlarm:{ + + if(!alarmFake || alarmEnded){ + L.ledDrawLetter(0, settings.almH / 10 + '0', r, g, b); + L.ledDrawLetter(1, settings.almH % 10 + '0', r, g, b); + L.ledDrawLetter(2, settings.almM / 10 + '0', r, g, b); + L.ledDrawLetter(3, settings.almM % 10 + '0', r, g, b); + L.ledDrawLetter(4, 'O', r, g, b); + L.ledDrawLetter(5, (settings.almOn ? 'N' : 'F'), r, g, b); + L.ledWrite(9, r, g, b); + L.ledWrite(2, 0, 0, 0); + + if(button & (BTN_MASK_PRESS | BTN_MASK_HOLD)) switch(button & BTN_MASK_ID){ + + case BTN_5_DN: + case BTN_5_UP: + case BTN_4_UP: + case BTN_4_DN: + settings.almOn ^= 1; + break; + case BTN_3_UP: + if(settings.almM < 59) settings.almM++; + break; + case BTN_3_DN: + if(settings.almM) settings.almM--; + break; + case BTN_2_UP: + if(settings.almM < 50) settings.almM += 10; + break; + case BTN_2_DN: + if(settings.almM > 9) settings.almM -= 10; + break; + case BTN_1_UP: + if(settings.almH < 23) settings.almH++; + break; + case BTN_1_DN: + if(settings.almH) settings.almH--; + break; + case BTN_0_UP: + if(settings.almH < 14) settings.almH += 10; + break; + case BTN_0_DN : + if(settings.almH > 9) settings.almH -=10; + break; + } + } + else{ + L.ledWrite(9, 0, 0, 0); + L.ledWrite(2, 0, 0, 0); + } + if(button == (BTN_MASK_HOLD | BTN_ALARM) && alarmEnded && (alarmState == AdkAlarmIdle || alarmState == AdkAlarmIdleWait)){ //handle sound file selection + + char* name = settings.almTune; + alarmFake = 1; + alarmEnded = 0; + alarmStop = 0; + dbgPrintf("Playing song '%s'\n", settings.almTune); + if(name[0] == '/' && (name[1] == 'T' || name[1] == 't') && + (name[2] == 'u' || name[2] == 'U') && (name[3] == 'n' || name[3] == 'N') && + (name[4] == 'e' || name[4] == 'E') && (name[5] == 's' || name[5] == 'S') && + name[6] == '/') name += 7; + for(i = 0; i < 6; i++) L.ledDrawLetter(i, name[i], r, g, b); + L.playOggBackground(settings.almTune, &alarmEnded, &alarmStop); + } + if(button == (BTN_MASK_PRESS | BTN_SLIDER_0) || button == (BTN_MASK_PRESS | BTN_SLIDER_1) || button == (BTN_MASK_PRESS | BTN_SLIDER_2)){ + + char name[256] = "/Tunes/", found = 0; + + dbgPrintf("next file\n"); + + for(j = 0; j < 2 && !found; j++) if(!L.fatfsOpenDir(&dir, "/Tunes")){ + + alarmDirPos++; + for(i = 0; i <= alarmDirPos; i++){ + + FatFileInfo fi; + fi.longName = name + 7; + fi.nameSz = sizeof(name) - 7; + + if(!L.fatfsReadDir(dir, &fi)){ + + if(i == alarmDirPos){ + dbgPrintf("file: '%s'\n", name); + found = 1; + } + } + else{ + + alarmDirPos = -1; + dbgPrintf("no more files\n"); + break; + } + } + L.fatfsCloseDir(dir); + } + if(found) strcpy(settings.almTune, name); + } + break; + } + case AdkVolume:{ + + uint8_t dispVol = ((uint32_t)L.getVolume() * 100) >> 8; + + L.ledDrawLetter(0, 'V', r, g, b); + L.ledDrawLetter(1, 'O', r, g, b); + L.ledDrawLetter(2, 'L', r, g, b); + L.ledDrawLetter(3, ' ', r, g, b); + L.ledDrawLetter(4, dispVol / 10 + '0', r, g, b); + L.ledDrawLetter(5, dispVol % 10 + '0', r, g, b); + + dispVol = L.capSenseSlider(); + if(dispVol != volSlider){ + L.setVolume(settings.vol = dispVol); + volSlider = dispVol; + } + + break; + } + case AdkColor:{ + + uint8_t slider, seg, prog; + static const char hexch[] = "0123456789ABCDEF"; + + L.ledDrawLetter(0, hexch[settings.R >> 4], r, g, b); + L.ledDrawLetter(1, hexch[settings.R & 15], r, g, b); + L.ledDrawLetter(2, hexch[settings.G >> 4], r, g, b); + L.ledDrawLetter(3, hexch[settings.G & 15], r, g, b); + L.ledDrawLetter(4, hexch[settings.B >> 4], r, g, b); + L.ledDrawLetter(5, hexch[settings.B & 15], r, g, b); + L.ledWrite(9, 0, 0, 0); + L.ledWrite(2, 0, 0, 0); + + if(button & (BTN_MASK_PRESS | BTN_MASK_HOLD)) switch(button & BTN_MASK_ID){ + + case BTN_5_DN: + if(settings.B) settings.B--; + break; + case BTN_5_UP: + if(settings.B < 0xFF) settings.B++; + break; + case BTN_4_UP: + if(settings.B < 0xF0) settings.B += 0x10; + break; + case BTN_4_DN: + if(settings.B > 0x0F) settings.B -= 0x10; + break; + case BTN_3_UP: + if(settings.G < 0xFF) settings.G++; + break; + case BTN_3_DN: + if(settings.G) settings.G--; + break; + case BTN_2_UP: + if(settings.G < 0xF0) settings.G += 0x10; + break; + case BTN_2_DN: + if(settings.G > 0x0F) settings.G -= 0x10; + break; + case BTN_1_UP: + if(settings.R < 0xFF) settings.R++; + break; + case BTN_1_DN: + if(settings.R) settings.R--; + break; + case BTN_0_UP: + if(settings.R < 0xF0) settings.R += 0x10; + break; + case BTN_0_DN : + if(settings.R > 0x0F) settings.R -= 0x10; + break; + } + + slider = L.capSenseSlider(); + if(slider != colorSlider){ + colorSlider = slider; + + gethue(slider, &settings.R, &settings.G, &settings.B); + } + break; + } + case AdkBrightness:{ + + uint8_t slider; + uint8_t dispBri = ((uint32_t)settings.bri * 100) >> 8; + + L.ledDrawLetter(0, 'B', r, g, b); + L.ledDrawLetter(1, 'R', r, g, b); + L.ledDrawLetter(2, 'I', r, g, b); + L.ledDrawLetter(3, ' ', r, g, b); + L.ledDrawLetter(4, dispBri / 10 + '0', r, g, b); + L.ledDrawLetter(5, dispBri % 10 + '0', r, g, b); + L.ledWrite(9, 0, 0, 0); + L.ledWrite(2, 0, 0, 0); + + slider = L.capSenseSlider(); + if(slider != briSlider){ + slider = ((((uint32_t)slider) * 191) >> 8) + 64; + briSlider = slider; + settings.bri = slider; + } + break; + } + case AdkDisplay:{ + + static uint8_t vals[NUM_LEDS][3] = {{0,},}; + static const uint8_t doNotTouch[] = {49, 33, 17, 1, 48, 32, 16, 0}; + uint8_t slider = L.capSenseSlider(); + + if(slider != speedSlider){ + speedSlider = slider; + settings.speed = (255 - slider) >> 3; + } + + if(L.getUptime() > nextTime){ + + if(!nextTime){ + + const char* name = NULL; + + switch(settings.displayMode){ + + default: settings.displayMode = AdkShowAnimation; //fallthrough + case AdkShowAnimation: name = "PRETTY"; break; + case AdkShowAccel: name = " ACCEL"; break; + case AdkShowMag: name = "MAGNET"; break; + case AdkShowTemp: name = " TEMP "; break; + case AdkShowHygro: name = " HYGRO"; break; + case AdkShowBaro: name = " BARO "; break; + case AdkShowProx: name = " PROX "; break; + case AdkShowColor: name = " COLOR"; break; + } + for(i = 0; i < 6; i++) L.ledDrawLetter(i, name[i], r, g, b); + L.ledWrite(9, 0, 0, 0); + L.ledWrite(2, 0, 0, 0); + nextTime = L.getUptime() + 1000; + } + else{ + nextTime = L.getUptime() + settings.speed; + volatile int16_t* arrPtr = NULL; + static const long valUnused = 0x7FFFFFFF; + int val = valUnused; + char isSigned = 1, cR = r, cG = g, cB = b; + + switch(settings.displayMode){ + + case AdkShowAnimation: + + if(rnd() < 30){ //spawn + + i = rnd() % NUM_LEDS; + gethue(rnd(), vals[i] + 0, vals[i] + 1, vals[i] + 2); + } + for(i = 0; i < NUM_LEDS; i++){ + for(j = 0; j < 3; j++) vals[i][j] = ((uint32_t)vals[i][j] * 127) >> 7; + for(j = 0; j < sizeof(doNotTouch) && i != doNotTouch[j]; j++); + if(j == sizeof(doNotTouch)) L.ledWrite(i, vals[i][0], vals[i][1], vals[i][2]); + } + break; + + case AdkShowAccel: + + arrPtr = accel; + //fallthrough + + case AdkShowMag: + + if(!arrPtr) arrPtr = mag; + //fallthrough + + case AdkShowProx: + + if(!arrPtr){ + + arrPtr = (int16_t*)prox; + isSigned = 0; + } + //fallthrough + + case AdkShowColor: + + if(!arrPtr){ + + arrPtr = (int16_t*)proxNormalized; + isSigned = 0; + cR = proxNormalized[0] >> 8; + cG = proxNormalized[1] >> 8; + cB = proxNormalized[2] >> 8; + } + + for(i = 0; i < 6; i += 2){ + + val = arrPtr[i >> 1]; + if(isSigned){ + if(val < 0){ + val = -val; + j = 0; + } + else j = 1; + } + else val = (uint16_t)val; + + val *= 100; + val >>= isSigned ? 15 : 16; + L.ledDrawLetter(i + 0, + val / 10 + '0', isSigned ? (j ? settings.bri : 0) : cR, isSigned ? 0 : cG, isSigned ? (j ? 0 : settings.bri) : cB); + L.ledDrawLetter(i + 1, + val % 10 + '0', isSigned ? (j ? settings.bri : 0) : cR, isSigned ? 0 : cG, isSigned ? (j ? 0 : settings.bri) : cB); + } + break; + + case AdkShowTemp: + + val = bTemp; + //fall through + + case AdkShowHygro: + + if(val == valUnused) val = hHum; + //fall through + + case AdkShowBaro: + + if(val == valUnused) val = (bPress + 50) / 100; + if(val < 0){ + L.ledDrawLetter(0, '-', r, g, b); + val = -val; + } + for(j = 1000, i = 1, k = 0; i < 6; i++){ + + if(i == 4) L.ledDrawLetter(i, '.', r, g, b); + else{ + + if(val >= j || i >= 4 || k){ + k = 1; + L.ledDrawLetter(i, val / j + '0', r, g, b); + } + else L.ledDrawLetter(i, ' ', r, g, b); + val %= j; + j /= 10; + } + } + break; + } + } + } + break; + } + case AdkPresets:{ + + L.ledWrite(9, 0, 0, 0); + L.ledWrite(2, 0, 0, 0); + + L.ledDrawLetter(0, ' ', r, g, b); + L.ledDrawLetter(1, 'n', r, g, b); + L.ledDrawLetter(2, 'o', r, g, b); + L.ledDrawLetter(3, 'n', r, g, b); + L.ledDrawLetter(4, 'e', r, g, b); + L.ledDrawLetter(5, ' ', r, g, b); + + break; + } + } + + if(button == (BTN_MASK_RELEASE | BTN_ALARM) && alarmFake && !alarmEnded){ + + dbgPrintf("Stopping song\n"); + alarmStop = 1; + alarmFake = 0; + } + + if(button & BTN_MASK_PRESS) switch(button & BTN_MASK_ID){ + + case BTN_CLOCK: newState = AdkClock; break; + case BTN_ALARM: + // eat the transition to alarm edit mode if we're in an alarm + if (alarmState == AdkAlarmIdle || alarmState == AdkAlarmIdleWait) + newState = AdkAlarm; + break; + case BTN_BRIGHTNESS: newState = AdkBrightness; briSlider = L.capSenseSlider(); break; + case BTN_COLOR: newState = AdkColor; colorSlider = L.capSenseSlider(); break; + case BTN_PRESETS: newState = AdkPresets; break; + case BTN_DISPLAY: newState = AdkDisplay; nextTime = 0; speedSlider = L.capSenseSlider(); break; + case BTN_VOLUME: newState = AdkVolume; volSlider = L.capSenseSlider(); break; + } + if(newState != AdkInvalid){ + writeSettings(); + if(state == AdkDisplay){ + screenClear(); + + //switch to next display mode + if(newState == AdkDisplay && ++settings.displayMode == AdkShowLast) settings.displayMode = 0; + } + } + + // alarm + if (settings.almOn) { + uint8_t month, day, h, m, sec; + + L.rtcGet(0, 0, 0, &h, &m, 0); + + switch (alarmState) { + case AdkAlarmIdle: // see if we need to trigger an alarm + if (settings.almH == h && settings.almM == m) { + // start alarm + Serial.write("ALARM\n"); + alarmState = AdkAlarmAlarm; + } + break; + case AdkAlarmIdleWait: // we had triggered, need to wait at least a minute before waiting for next alarm + if (settings.almH != h || settings.almM != m) { + alarmState = AdkAlarmIdle; + } + break; + case AdkAlarmAlarm: { + // check for alarm button to cancel playing alarm + if (((button & BTN_MASK_ID) == BTN_ALARM) && + (button & (BTN_MASK_PRESS | BTN_MASK_HOLD))) { + alarmState = AdkAlarmIdleWait; + alarmStop = 1; + L.ledDrawIcon(1, (settings.R + dimIconFactor - 1) / dimIconFactor, (settings.G + dimIconFactor - 1) / dimIconFactor, (settings.B + dimIconFactor - 1) / dimIconFactor); + break; + } + // check for snooze button + if (((button & BTN_MASK_ID) <= 2) && + (button & (BTN_MASK_PRESS | BTN_MASK_HOLD))) { + Serial.write("ALARM SNOOZE\n"); + alarmState = AdkAlarmSnooze; + snoozeStart = millis() / 1000; + alarmStop = 1; + break; + } + + // make sure ogg is playing + if (alarmEnded) { + alarmEnded = 0; + alarmStop = 0; + alarmFake = 0; + L.playOggBackground(settings.almTune, &alarmEnded, &alarmStop); + } + + // blink the alarm button red + if ((millis() / 500) & 1) + L.ledDrawIcon(1, 0xff, 0, 0); + else + L.ledDrawIcon(1, (settings.R + dimIconFactor - 1) / dimIconFactor, (settings.G + dimIconFactor - 1) / dimIconFactor, (settings.B + dimIconFactor - 1) / dimIconFactor); + break; + } + case AdkAlarmSnooze: + // check for alarm button to cancel playing alarm + if (alarmState != AdkAlarmIdle && + ((button & BTN_MASK_ID) == 4) && + (button & (BTN_MASK_PRESS | BTN_MASK_HOLD))) { + alarmState = AdkAlarmIdleWait; + alarmStop = 1; + L.ledDrawIcon(1, (settings.R + dimIconFactor - 1) / dimIconFactor, (settings.G + dimIconFactor - 1) / dimIconFactor, (settings.B + dimIconFactor - 1) / dimIconFactor); + break; + } + // see if we need to transition back to alarm state + if ((millis() / 1000) - snoozeStart > settings.almSnooze) { + Serial.write("ALARM FROM SNOOZE\n"); + alarmState = AdkAlarmAlarm; + break; + } + + // blink the alarm button blue + if ((millis() / 500) & 1) + L.ledDrawIcon(1, 0, 0, 0xff); + else + L.ledDrawIcon(1, (settings.R + dimIconFactor - 1) / dimIconFactor, (settings.G + dimIconFactor - 1) / dimIconFactor, (settings.B + dimIconFactor - 1) / dimIconFactor); + break; + } + } else { + // make sure there are no dangling alarms + if(!alarmFake) alarmStop = 1; + } + + // usb accessory processing + processUSBAccessory(); + } +} + + + + +//////////// bt interface (fun) + +//commands +#define MAX_PACKET_SZ 260 //256b payload + header + +// command header +// u8 cmd opcode +// u8 sequence +// u16 size + +// data formats: +// timespec = (year,month,day,hour,min,sec) (u16,u8,u8,u8,u8,u8) + +#define CMD_MASK_REPLY 0x80 +#define BT_CMD_GET_PROTO_VERSION 1 // () -> (u8 protocolVersion) +#define BT_CMD_GET_SENSORS 2 // () -> (sensors: i32,i32,i32,i32,u16,u16,u16,u16,u16,u16,u16,i16,i16,i16,i16,i16,i16) +#define BT_CMD_FILE_LIST 3 // FIRST: (char name[]) -> (fileinfo or single zero byte) OR NONLATER: () -> (fileinfo or empty or single zero byte) +#define BT_CMD_FILE_DELETE 4 // (char name[0-255)) -> (char success) +#define BT_CMD_FILE_OPEN 5 // (char name[0-255]) -> (char success) +#define BT_CMD_FILE_WRITE 6 // (u8 data[]) -> (char success) +#define BT_CMD_FILE_CLOSE 7 // () -> (char success) +#define BT_CMD_GET_UNIQ_ID 8 // () -> (u8 uniq[16]) +#define BT_CMD_BT_NAME 9 // (char name[]) -> () OR () -> (char name[]) +#define BT_CMD_BT_PIN 10 // (char PIN[]) -> () OR () -> (char PIN[]) +#define BT_CMD_TIME 11 // (timespec) -> (char success)) OR () > (timespec) +#define BT_CMD_SETTINGS 12 // () -> (alarm:u8,u8,u8,brightness:u8,color:u8,u8,u8:volume:u8) or (alarm:u8,u8,u8,brightness:u8,color:u8,u8,u8:volume:u8) > (char success) +#define BT_CMD_ALARM_FILE 13 // () -> (char file[0-255]) OR (char file[0-255]) > (char success) +#define BT_CMD_GET_LICENSE 14 // () -> (u8 licensechunk[]) OR () if last sent +#define BT_CMD_DISPLAY_MODE 15 // () -> (u8) OR (u8) -> () +#define BT_CMD_LOCK 16 // () -> (u8) OR (u8) -> () + +#define BT_PROTO_VERSION_1 1 //this line marks the end of v1.0 API, all things after this are the next version + + +//constants +#define BT_PROTO_VERSION_CURRENT BT_PROTO_VERSION_1 + +static const uint8_t gzippedLicences[] = { + 0x1F, 0x8B, 0x08, 0x00, 0x36, 0xB6, 0xDF, 0x4F, 0x02, 0x03, 0xCD, 0x58, 0x5D, 0x73, 0xDA, 0x38, 0x14, 0x7D, 0xD7, 0xAF, 0xB8, 0x93, 0x97, 0x26, 0x19, 0x07, 0xB2, 0x79, 0xDA, 0x49, 0x9F, 0x0C, + 0x18, 0xD0, 0x2C, 0xB1, 0x59, 0xDB, 0x24, 0xE5, 0xAD, 0xC2, 0x16, 0xE0, 0x1D, 0x63, 0x79, 0x2D, 0x93, 0x34, 0xFF, 0x7E, 0xEF, 0x95, 0x6D, 0x3E, 0x12, 0x92, 0xA6, 0x09, 0xDD, 0x96, 0xE9, 0x34, + 0x06, 0x49, 0x57, 0xE7, 0x9E, 0xFB, 0x71, 0x2C, 0x31, 0x38, 0xC6, 0x87, 0xFD, 0x02, 0x2B, 0x2C, 0x5C, 0x26, 0x1A, 0xF2, 0x42, 0xC5, 0xEB, 0xA8, 0x84, 0x24, 0x8B, 0xD2, 0x75, 0x2C, 0x35, 0x68, + 0x35, 0x2F, 0x1F, 0x44, 0x21, 0x61, 0x5E, 0xA8, 0x15, 0x74, 0x97, 0xC2, 0xBD, 0x66, 0xE7, 0x17, 0xC7, 0xFB, 0xB4, 0x59, 0x1B, 0xA0, 0x2F, 0xCA, 0xBE, 0x86, 0x0B, 0xE8, 0xDB, 0x21, 0xCC, 0x93, + 0x54, 0x82, 0x7E, 0xD4, 0xA5, 0x5C, 0xC1, 0x0A, 0xC1, 0xE0, 0xB7, 0x1A, 0x4C, 0x35, 0x04, 0xFE, 0x65, 0xEB, 0xF2, 0xCF, 0x19, 0x41, 0x3E, 0xED, 0x9E, 0x11, 0x20, 0x0B, 0xAE, 0x2E, 0xFF, 0xF8, + 0x83, 0xB5, 0x2F, 0x8E, 0x0C, 0xAB, 0x42, 0xD5, 0x60, 0xD0, 0x20, 0x60, 0x21, 0x33, 0x59, 0x24, 0xD1, 0x4B, 0x38, 0xE7, 0xAA, 0x00, 0xBD, 0x12, 0x69, 0x0A, 0x72, 0x35, 0x93, 0x71, 0x2C, 0xE3, + 0x7A, 0x82, 0x6E, 0xA1, 0x39, 0xC3, 0xAF, 0x31, 0x33, 0x2F, 0xA4, 0xDC, 0x12, 0x5B, 0x2E, 0x45, 0x09, 0x2A, 0x47, 0xD3, 0xB1, 0xB1, 0x20, 0x31, 0x00, 0xA2, 0x4C, 0x54, 0x66, 0x41, 0x21, 0xB5, + 0x14, 0x45, 0xB4, 0x04, 0x91, 0xC5, 0x10, 0xA9, 0xD5, 0x4A, 0x16, 0x51, 0x22, 0x52, 0x34, 0x16, 0xCB, 0x7B, 0x99, 0xAA, 0x7C, 0x25, 0xB3, 0x52, 0xC3, 0x3A, 0x8B, 0x65, 0x01, 0x69, 0x12, 0xC9, + 0x4C, 0x4B, 0xC8, 0x15, 0x3E, 0x3D, 0x82, 0x9A, 0xA3, 0xB5, 0x34, 0x55, 0x0F, 0x49, 0xB6, 0x80, 0xB2, 0xA8, 0x40, 0x10, 0xD9, 0x5D, 0x95, 0x3F, 0x16, 0xC9, 0x62, 0x59, 0x12, 0x7F, 0x86, 0x3A, + 0x0B, 0x2A, 0x1A, 0x09, 0x79, 0x35, 0x42, 0x1B, 0x17, 0xF7, 0x32, 0xAE, 0x96, 0x9C, 0x23, 0x76, 0x79, 0x80, 0x8E, 0x7D, 0x3F, 0x08, 0x63, 0xB9, 0x94, 0x85, 0x19, 0x74, 0x3D, 0xB8, 0xB3, 0x7D, + 0xDF, 0x76, 0xC3, 0x69, 0xCB, 0x58, 0x70, 0x15, 0x19, 0x2D, 0x91, 0x3D, 0x72, 0x0D, 0xF0, 0xDF, 0x5A, 0xCB, 0x16, 0x4C, 0xD5, 0x1A, 0x22, 0x61, 0xBE, 0x58, 0x64, 0x3B, 0x99, 0x3F, 0x1A, 0x43, + 0x85, 0x8C, 0x13, 0x9A, 0x3D, 0x5B, 0x97, 0x68, 0xAF, 0x24, 0x62, 0x08, 0x3B, 0xE4, 0xB2, 0xD0, 0x2A, 0x13, 0xA9, 0x05, 0x99, 0xCA, 0x2E, 0x30, 0x5B, 0xE7, 0x38, 0x88, 0xA4, 0x6D, 0xC9, 0xD9, + 0xA4, 0xF0, 0xC4, 0xED, 0x39, 0x3E, 0x4C, 0xBD, 0x89, 0x0F, 0xBE, 0x13, 0x8C, 0x3D, 0x37, 0xE0, 0x1D, 0x3E, 0xE2, 0x0D, 0x20, 0x7F, 0xBB, 0x03, 0x02, 0xD2, 0xC4, 0x97, 0x56, 0xEB, 0x22, 0x92, + 0x68, 0x0B, 0x73, 0x6D, 0xB5, 0xD6, 0x44, 0x43, 0x29, 0x92, 0x8C, 0xBC, 0x02, 0x31, 0x53, 0xF7, 0x34, 0xD4, 0x70, 0x97, 0xA9, 0x12, 0xF9, 0x36, 0xFC, 0x1C, 0x33, 0xEF, 0xCE, 0xDB, 0x8C, 0xBD, + 0xA9, 0x12, 0x7B, 0xAB, 0xA4, 0x2C, 0x1E, 0x61, 0x50, 0x24, 0xD9, 0x4C, 0x16, 0x8B, 0x6B, 0xD6, 0x3E, 0x67, 0x3B, 0x91, 0x8D, 0x9A, 0xC8, 0x3E, 0x99, 0x07, 0xA7, 0x02, 0x4D, 0xAF, 0x67, 0x69, + 0xA2, 0x97, 0x75, 0xBE, 0xF5, 0x06, 0x5E, 0x40, 0xF1, 0x58, 0x96, 0x65, 0x7E, 0xDD, 0x6E, 0xC7, 0x0B, 0xA5, 0x67, 0xA9, 0x5A, 0xB4, 0xE8, 0x3F, 0x9D, 0xAB, 0xB2, 0x85, 0xDC, 0x9E, 0x31, 0xBB, + 0xC9, 0x0D, 0xBD, 0x93, 0x1C, 0x6C, 0x9F, 0x44, 0x13, 0x39, 0x8C, 0x24, 0x62, 0x6E, 0xB8, 0xA4, 0x5F, 0x66, 0x49, 0x26, 0x10, 0x02, 0xEE, 0xB5, 0xD2, 0x16, 0x3C, 0x24, 0xE5, 0x92, 0x22, 0x46, + 0x7F, 0xD5, 0xBA, 0x64, 0x26, 0xE8, 0x49, 0x93, 0xF1, 0xE4, 0x1F, 0xC6, 0x18, 0x41, 0x97, 0x08, 0x0F, 0x39, 0xB8, 0x4F, 0xA8, 0x8C, 0x4C, 0x8D, 0x50, 0x18, 0xB6, 0x29, 0x1D, 0xA9, 0x2C, 0x4E, + 0xCC, 0xAE, 0x48, 0xD6, 0x4A, 0x96, 0xD7, 0xC0, 0xD8, 0x91, 0xA2, 0x6A, 0xE1, 0x58, 0xA2, 0xA9, 0x7D, 0x22, 0x4D, 0x25, 0xD9, 0xD8, 0x6C, 0xA6, 0x9B, 0x34, 0xDF, 0x41, 0x82, 0x3B, 0x46, 0xA9, + 0x48, 0x30, 0xFF, 0x5A, 0x88, 0x21, 0x1C, 0xF2, 0x00, 0x02, 0xAF, 0x1F, 0x62, 0xFE, 0x3B, 0x80, 0xCF, 0x63, 0xDF, 0xBB, 0xE5, 0x3D, 0xA7, 0x07, 0x9D, 0x29, 0x84, 0x43, 0x07, 0xBA, 0xDE, 0x78, + 0xEA, 0xF3, 0xC1, 0x30, 0x84, 0xA1, 0x37, 0xC2, 0x14, 0x0D, 0xC0, 0x76, 0x7B, 0xF8, 0xAB, 0x1B, 0xFA, 0xBC, 0x33, 0x09, 0x3D, 0xFC, 0xE1, 0xC4, 0x0E, 0x70, 0xE5, 0x09, 0x0D, 0x30, 0xDB, 0x9D, + 0x82, 0xF3, 0x65, 0x8C, 0x29, 0x1C, 0x80, 0xE7, 0x03, 0xBF, 0x19, 0x8F, 0x38, 0x1A, 0xAB, 0xAB, 0x8B, 0x3B, 0x81, 0x05, 0xDC, 0xED, 0x8E, 0x26, 0x3D, 0xEE, 0x0E, 0x2C, 0x40, 0x03, 0x58, 0x7B, + 0x21, 0x8C, 0xF8, 0x0D, 0x0F, 0x71, 0x5A, 0xE8, 0x59, 0x66, 0xD3, 0x7A, 0x19, 0xDB, 0x2E, 0x03, 0xAF, 0x0F, 0x37, 0x8E, 0xDF, 0x1D, 0xE2, 0x57, 0xBB, 0x2A, 0x0C, 0x03, 0xA4, 0xCF, 0x43, 0x97, + 0xF6, 0xEA, 0xE3, 0x66, 0x36, 0x8C, 0x6D, 0x3F, 0xE4, 0xDD, 0xC9, 0xC8, 0xF6, 0x61, 0x3C, 0xF1, 0xC7, 0x5E, 0xE0, 0x00, 0xBA, 0xC5, 0x7A, 0x3C, 0xE8, 0x8E, 0x6C, 0x7E, 0xE3, 0xF4, 0x5A, 0xB8, + 0x3B, 0x55, 0xBB, 0x73, 0xEB, 0xB8, 0x21, 0x04, 0x43, 0x7B, 0x34, 0x7A, 0xE2, 0xA5, 0x77, 0xE7, 0x62, 0x1D, 0xA2, 0xB5, 0x3D, 0x17, 0x3B, 0x0E, 0x62, 0xB4, 0x3B, 0x23, 0x87, 0x36, 0x32, 0x4E, + 0xF6, 0xB8, 0xEF, 0x74, 0x43, 0xF2, 0x66, 0xFB, 0xD4, 0x45, 0xE2, 0x10, 0xDE, 0xC8, 0x82, 0x60, 0xEC, 0x74, 0x39, 0x3D, 0x38, 0x5F, 0x1C, 0xF4, 0xC5, 0xF6, 0xA7, 0x56, 0x6D, 0x33, 0x70, 0xFE, + 0x9E, 0xE0, 0x24, 0x1C, 0x84, 0x9E, 0x7D, 0x63, 0x0F, 0x9C, 0x80, 0x9D, 0x7E, 0x87, 0x11, 0x0C, 0x49, 0x77, 0xE2, 0x3B, 0x37, 0x04, 0x19, 0x69, 0x08, 0x26, 0x9D, 0x20, 0xE4, 0xE1, 0x24, 0x74, + 0x60, 0xE0, 0x79, 0x3D, 0xC3, 0x73, 0xE0, 0xF8, 0xB7, 0xBC, 0xEB, 0x04, 0x9F, 0xD9, 0xC8, 0x0B, 0x0C, 0x59, 0x93, 0xC0, 0xC1, 0x72, 0xB2, 0x43, 0xDB, 0x6C, 0x8C, 0x26, 0x90, 0xA9, 0xE0, 0x33, + 0x3D, 0x77, 0x26, 0x01, 0x37, 0x9C, 0x71, 0x37, 0x74, 0x7C, 0x7F, 0x32, 0x0E, 0xB9, 0xE7, 0x9E, 0x61, 0x78, 0xEF, 0x90, 0x15, 0xC4, 0x68, 0xE3, 0xD2, 0x9E, 0x09, 0xA6, 0xE7, 0x02, 0xB9, 0x8A, + 0x04, 0x79, 0xFE, 0x94, 0x8C, 0x12, 0x07, 0x86, 0x7B, 0x0B, 0xEE, 0x86, 0x0E, 0xFE, 0xEE, 0x13, 0x9F, 0x86, 0x29, 0x9B, 0x28, 0x08, 0x90, 0xB1, 0x6E, 0xB8, 0x3B, 0x0D, 0xF7, 0x43, 0x02, 0xC3, + 0x1D, 0x1F, 0xC1, 0x75, 0x06, 0x23, 0x3E, 0x70, 0xDC, 0xAE, 0x43, 0xA3, 0x1E, 0x59, 0xB9, 0xE3, 0x81, 0x73, 0x86, 0xA1, 0xE2, 0x01, 0x4D, 0xE0, 0xD5, 0xB6, 0x77, 0x36, 0xEE, 0x39, 0x31, 0x2E, + 0x53, 0x88, 0x10, 0x55, 0xF5, 0xC8, 0x03, 0xD6, 0x24, 0xAC, 0x65, 0x02, 0x09, 0xBC, 0x0F, 0x76, 0xEF, 0x96, 0x13, 0xEC, 0x7A, 0x32, 0x86, 0xBE, 0xE9, 0x9F, 0x15, 0x65, 0xDD, 0x61, 0x4D, 0x77, + 0x8B, 0xBD, 0xB9, 0x69, 0xD9, 0xE5, 0x4A, 0xA6, 0x28, 0x40, 0x45, 0xAE, 0x0A, 0x53, 0xF0, 0xD4, 0xB6, 0xE0, 0x50, 0xDB, 0x7A, 0x36, 0xF3, 0xAD, 0xAF, 0x31, 0x88, 0x05, 0x4D, 0x1E, 0xF5, 0x53, + 0x99, 0x3C, 0xD4, 0x00, 0x3F, 0x68, 0xF2, 0x27, 0xA0, 0xFC, 0x78, 0x43, 0x7E, 0x66, 0xF2, 0x78, 0x0D, 0xFA, 0x67, 0x3A, 0x7E, 0x71, 0xAC, 0xD6, 0xBF, 0x35, 0x49, 0x22, 0xF0, 0x9A, 0x02, 0x6C, + 0xFB, 0x3E, 0xCC, 0xF0, 0x65, 0xEC, 0xA1, 0xF5, 0x6B, 0xF2, 0x92, 0x2A, 0xE5, 0x93, 0x86, 0x4C, 0xAC, 0xD0, 0x49, 0xF1, 0x48, 0x8E, 0x20, 0x1E, 0x0A, 0x3B, 0xC2, 0x54, 0x20, 0xB3, 0x58, 0x15, + 0x98, 0x02, 0x18, 0x61, 0x8C, 0xD6, 0x4A, 0xE1, 0xFB, 0x54, 0x5D, 0xA5, 0x1A, 0xDF, 0x22, 0x8B, 0xE4, 0x9E, 0x5E, 0x03, 0xA8, 0x38, 0x9F, 0x38, 0xBE, 0xA9, 0xDC, 0x26, 0x2F, 0x74, 0x2E, 0x23, + 0x4A, 0x04, 0x5C, 0x9E, 0x50, 0xBA, 0x14, 0x94, 0x02, 0x59, 0x95, 0x0C, 0x5A, 0x23, 0x33, 0xAD, 0xFF, 0xD7, 0xF1, 0x8D, 0x06, 0xF9, 0x98, 0x59, 0xAF, 0xAA, 0xAE, 0x1D, 0xDE, 0x38, 0xA3, 0x5D, + 0x51, 0x85, 0x27, 0xA2, 0xDA, 0x98, 0xFC, 0x98, 0xB8, 0xC2, 0xBE, 0xB8, 0x56, 0x26, 0x9F, 0x48, 0xAC, 0xF5, 0x06, 0x7D, 0x45, 0x7C, 0xAE, 0xE7, 0x5E, 0x70, 0xB7, 0xEF, 0xE3, 0xB6, 0x95, 0x4A, + 0x91, 0x57, 0xCF, 0x1C, 0x3F, 0x28, 0xBE, 0x95, 0xB3, 0x7B, 0xF2, 0x0A, 0x87, 0xE5, 0x75, 0x87, 0xCB, 0xF7, 0x2A, 0x2D, 0x1C, 0x52, 0xDA, 0xCA, 0xE4, 0x7B, 0xF5, 0x16, 0x0E, 0xE8, 0x6D, 0x6D, + 0xF2, 0xBD, 0xB2, 0x0B, 0xCF, 0x64, 0x77, 0xE3, 0xF8, 0xBB, 0xF5, 0x77, 0xC7, 0xF3, 0x27, 0x79, 0xF9, 0x71, 0x35, 0x86, 0xAD, 0x1A, 0x57, 0x26, 0x7F, 0x5C, 0x93, 0x5F, 0xAF, 0x9E, 0x97, 0xD4, + 0x3A, 0xC4, 0x18, 0x21, 0xE6, 0x7D, 0xD1, 0xA6, 0x56, 0xF7, 0x25, 0xC9, 0x97, 0x2D, 0x85, 0xC7, 0x87, 0xBE, 0xC2, 0xC3, 0x66, 0x2D, 0xDD, 0x4F, 0x75, 0xFB, 0xF2, 0xCA, 0x3A, 0x34, 0xF1, 0xF7, + 0x38, 0x25, 0x68, 0x46, 0x8B, 0x48, 0x86, 0x18, 0xFB, 0xA8, 0x56, 0xB0, 0xDD, 0x63, 0xC2, 0x0F, 0x9F, 0x11, 0x0E, 0xED, 0x8F, 0xFB, 0xEC, 0xF8, 0xDF, 0xEC, 0x5F, 0xC5, 0x47, 0x1E, 0x1D, 0x02, + 0x54, 0x6E, 0xB1, 0x58, 0x45, 0x6B, 0xBA, 0x41, 0x10, 0x4D, 0x58, 0xDA, 0xC8, 0xB8, 0xA2, 0x33, 0x3C, 0x0A, 0x49, 0x89, 0xDA, 0x20, 0x52, 0xBD, 0x65, 0xD7, 0x84, 0xA4, 0xD6, 0xBD, 0x0D, 0x74, + 0xE3, 0x8D, 0x2B, 0x13, 0xB3, 0x88, 0x06, 0x8D, 0x0C, 0x21, 0x94, 0x17, 0xB2, 0x06, 0xD5, 0x69, 0x3B, 0xCF, 0xF0, 0x9E, 0x94, 0x9A, 0x21, 0xEE, 0xCA, 0x22, 0x2A, 0x95, 0xD1, 0xB0, 0x77, 0xE8, + 0x17, 0x7B, 0xAF, 0x6A, 0x1D, 0xE5, 0xC8, 0xC6, 0xBE, 0x7E, 0x35, 0xF2, 0xF2, 0xE9, 0xD3, 0x21, 0x7D, 0x79, 0x9B, 0xAE, 0xB0, 0xB7, 0xE9, 0xCA, 0x77, 0x0E, 0x6D, 0xEC, 0xA5, 0x43, 0xDB, 0x9E, + 0x6E, 0xBC, 0x70, 0x6A, 0xEB, 0x7B, 0x13, 0x17, 0x5B, 0x2E, 0xF6, 0x51, 0xF6, 0xEA, 0x81, 0x0D, 0xBE, 0x7B, 0x60, 0x63, 0x1F, 0x95, 0x11, 0x76, 0x14, 0x01, 0x61, 0x1F, 0x3B, 0xB0, 0xD5, 0xCA, + 0xC1, 0x7E, 0x9B, 0x03, 0x1B, 0x7B, 0x2E, 0x11, 0xEF, 0x38, 0xB0, 0xBD, 0xED, 0xB4, 0x46, 0x65, 0x6A, 0x67, 0x71, 0xA1, 0x92, 0x18, 0xBC, 0x5C, 0x66, 0x17, 0x41, 0xD5, 0x22, 0xC7, 0x85, 0xFA, + 0x47, 0x46, 0xA5, 0xB9, 0x72, 0x82, 0xF3, 0xE7, 0xF7, 0x89, 0x57, 0xE6, 0xBA, 0x70, 0x77, 0x25, 0xEC, 0xAF, 0xC4, 0x55, 0xB4, 0x70, 0x54, 0x5D, 0x58, 0xC6, 0xF5, 0xFD, 0xA5, 0xD9, 0x2E, 0x17, + 0x11, 0xFE, 0xA9, 0x47, 0x2C, 0xB8, 0x95, 0x05, 0xD5, 0x28, 0x5C, 0xB5, 0x2E, 0xE1, 0x94, 0x26, 0x9C, 0xD4, 0x43, 0x27, 0x67, 0x9F, 0xC9, 0xC4, 0xA3, 0x5A, 0x6F, 0xDE, 0x79, 0x49, 0x52, 0x4C, + 0x0F, 0x30, 0x37, 0xB3, 0xF2, 0x5B, 0x24, 0x73, 0x72, 0x8D, 0xAE, 0x06, 0xF3, 0x34, 0x11, 0x59, 0x24, 0xB7, 0x2D, 0xAC, 0xB6, 0xD2, 0x22, 0x1B, 0xD3, 0xDA, 0x86, 0x9A, 0x99, 0x7E, 0x2F, 0x4C, + 0x93, 0x6D, 0x5A, 0x58, 0x3D, 0x11, 0x44, 0x03, 0xDA, 0x7C, 0xEA, 0xAB, 0xB2, 0x87, 0x87, 0x87, 0x96, 0x30, 0x88, 0xA9, 0xCB, 0xB5, 0xEB, 0x0B, 0x58, 0xDD, 0x1E, 0x61, 0x1E, 0x62, 0x96, 0x5F, + 0x20, 0xEA, 0x7A, 0xD5, 0x24, 0x4B, 0xA5, 0xA6, 0x83, 0xE3, 0xBF, 0xEB, 0xA4, 0x40, 0x8F, 0x67, 0x8F, 0x20, 0x72, 0x44, 0x15, 0x89, 0x19, 0x62, 0x4D, 0xC5, 0x03, 0xB5, 0x37, 0xB1, 0x28, 0x64, + 0xD5, 0xF3, 0x10, 0x06, 0x35, 0x2A, 0xEC, 0xDA, 0xD6, 0x26, 0x26, 0x64, 0x66, 0x7B, 0x27, 0xBA, 0x4B, 0x5A, 0x83, 0x11, 0x5D, 0xDF, 0x9D, 0x60, 0x5A, 0xFA, 0xE6, 0xAD, 0xB7, 0x63, 0x07, 0x3C, + 0xB0, 0xC8, 0xC8, 0x1D, 0x0F, 0x87, 0x94, 0x54, 0xBB, 0x5D, 0xC5, 0xD4, 0x65, 0x8F, 0x53, 0x19, 0x98, 0xD2, 0xA1, 0xE4, 0xFB, 0x0B, 0xEB, 0xDA, 0x82, 0xBA, 0xB5, 0xCB, 0x6F, 0x79, 0x41, 0x1E, + 0x20, 0xCC, 0x84, 0xE8, 0xA4, 0xDB, 0x3F, 0xB4, 0x15, 0x48, 0xB9, 0x07, 0x61, 0x5E, 0x77, 0xF7, 0x4D, 0xD7, 0x4D, 0x45, 0xB6, 0x58, 0x8B, 0x85, 0x84, 0x05, 0xCA, 0x57, 0x91, 0x91, 0x0E, 0x6D, + 0x5B, 0xAF, 0x51, 0x29, 0x32, 0x93, 0x26, 0x28, 0xE5, 0xA2, 0x12, 0xAE, 0x67, 0x7E, 0xD1, 0x46, 0xF8, 0xCA, 0xF2, 0x1F, 0xF8, 0xB9, 0x5A, 0xB2, 0x5A, 0x19, 0x00, 0x00 +}; + + +static void putLE32(uint8_t* buf, uint16_t* idx, uint32_t val){ + + buf[(*idx)++] = val; + buf[(*idx)++] = val >> 8; + buf[(*idx)++] = val >> 16; + buf[(*idx)++] = val >> 24; +} + +static void putLE16(uint8_t* buf, uint16_t* idx, uint32_t val){ + + buf[(*idx)++] = val; + buf[(*idx)++] = val >> 8; +} + +static uint16_t adkProcessCommand(uint8_t cmd, const uint8_t* dataIn, uint16_t sz, char fromBT, uint8_t* reply, uint16_t maxReplySz){ //returns num bytes to reply with (or 0 for no reply) + + uint16_t sendSz = 0; + static FatFileP btFile = 0, usbFile = 0; + static FatDirP btDir = 0, usbDir = 0; + static uint16_t btLicPos = 0, usbLicPos = 0; + FatFileP* filP = fromBT ? &btFile : &usbFile; + FatDirP* dirP = fromBT ? &btDir : &usbDir; + uint16_t* licPos = fromBT ? &btLicPos : &usbLicPos; + + dbgPrintf("ADK: BT: have cmd 0x%x with %db of data\n", cmd, sz); + + //NOTE: this code was written in a hurry and features little error checking. yes, I know it's bad. -DG + + //process packet + switch(cmd){ + + case BT_CMD_GET_PROTO_VERSION: + { + reply[sendSz++] = BT_PROTO_VERSION_CURRENT; + } + break; + + case BT_CMD_GET_SENSORS: + { + putLE32(reply, &sendSz, hTemp); + putLE32(reply, &sendSz, hHum); + putLE32(reply, &sendSz, bPress); + putLE32(reply, &sendSz, bTemp); + putLE16(reply, &sendSz, prox[0]); + putLE16(reply, &sendSz, prox[1]); + putLE16(reply, &sendSz, prox[3]); + putLE16(reply, &sendSz, prox[4]); + putLE16(reply, &sendSz, prox[5]); + putLE16(reply, &sendSz, prox[2]); + putLE16(reply, &sendSz, prox[6]); + putLE16(reply, &sendSz, accel[0]); + putLE16(reply, &sendSz, accel[1]); + putLE16(reply, &sendSz, accel[2]); + putLE16(reply, &sendSz, mag[0]); + putLE16(reply, &sendSz, mag[1]); + putLE16(reply, &sendSz, mag[2]); + } + break; + + case BT_CMD_FILE_LIST: + { + + if(sz){ //reset + + if(*dirP) L.fatfsCloseDir(*dirP); + if(L.fatfsOpenDir(dirP, (char*)dataIn)) *dirP = 0; + } + if(*dirP){ + + FatFileInfo fi; + fi.longName = (char*)reply + 5; + fi.nameSz = maxReplySz - 5; + + if(!L.fatfsReadDir(*dirP, &fi)){ + + fi.longName[fi.nameSz - 1] = 0; + reply[sendSz++] = fi.fsize; + reply[sendSz++] = fi.fsize >> 8; + reply[sendSz++] = fi.fsize >> 16; + reply[sendSz++] = fi.fsize >> 24; + reply[sendSz++] = fi.attrib; + sendSz += strlen(fi.longName) + 1; + } + else reply[sendSz++] = 0; + } + else reply[sendSz++] = 0; + } + break; + + case BT_CMD_FILE_DELETE: + { + reply[sendSz++] = !L.fatfsUnlink((const char*)dataIn); + } + break; + + case BT_CMD_FILE_OPEN: + { + if(*filP) L.fatfsClose(*filP); + reply[sendSz++] = !L.fatfsOpen(filP, (const char*)dataIn, FATFS_WRITE | FATFS_CREATE | FATFS_TRUNCATE); + if(!reply[sendSz - 1]) *filP = 0; + } + break; + + case BT_CMD_FILE_WRITE: + { + uint32_t written; + + reply[sendSz++] = !L.fatfsWrite(*filP, (void*)dataIn, sz, &written) && written == sz; + } + break; + + case BT_CMD_FILE_CLOSE: + { + if(*filP){ + reply[sendSz++] = 1; + L.fatfsClose(*filP); + *filP = 0; + } + else reply[sendSz++] = 0; + } + break; + + case BT_CMD_GET_UNIQ_ID: + { + + uint32_t id[4]; + + L.getUniqueId(id); + putLE32(reply, &sendSz, id[0]); + putLE32(reply, &sendSz, id[1]); + putLE32(reply, &sendSz, id[2]); + putLE32(reply, &sendSz, id[3]); + } + break; + + case BT_CMD_BT_NAME: + { + if(sz){ //set + strcpy(settings.btName, (char*)dataIn); + reply[sendSz++] = 1; + writeSettings(); + } + else{ + strcpy((char*)reply, settings.btName); + sendSz = strlen((char*)reply) + 1; + } + } + break; + + case BT_CMD_BT_PIN: + { + if(sz){ //set + strcpy(settings.btPIN, (char*)dataIn); + reply[sendSz++] = 1; + writeSettings(); + } + else{ + strcpy((char*)reply, settings.btPIN); + sendSz = strlen((char*)reply) + 1; + } + } + break; + case BT_CMD_TIME: + { + if (sz >= 7) { //set + L.rtcSet(dataIn[1] << 8 | dataIn[0], dataIn[2], dataIn[3], dataIn[4], dataIn[5], dataIn[6]); + reply[sendSz++] = 1; + } else if (sz == 0) { + L.rtcGet((uint16_t *)&reply[0], &reply[2], &reply[3], &reply[4], &reply[5], &reply[6]); + sendSz += 7; + } + } + break; + case BT_CMD_SETTINGS: + { + if (sz >= 8) { //set + settings.almH = dataIn[0]; + settings.almM = dataIn[1]; + settings.almOn = dataIn[2]; + settings.bri = dataIn[3]; + settings.R = dataIn[4]; + settings.G = dataIn[5]; + settings.B = dataIn[6]; + settings.vol = dataIn[7]; + L.setVolume(settings.vol); + writeSettings(); + reply[sendSz++] = 1; + } else { + reply[sendSz++] = settings.almH; + reply[sendSz++] = settings.almM; + reply[sendSz++] = settings.almOn; + reply[sendSz++] = settings.bri; + reply[sendSz++] = settings.R; + reply[sendSz++] = settings.G; + reply[sendSz++] = settings.B; + reply[sendSz++] = settings.vol; + } + } + break; + case BT_CMD_ALARM_FILE: + { + if(sz){ //set + strcpy(settings.almTune, (char*)dataIn); + reply[sendSz++] = 1; + writeSettings(); + } else{ + strcpy((char*)reply, settings.almTune); + sendSz = strlen((char*)reply) + 1; + } + } + break; + case BT_CMD_GET_LICENSE: + { + static const uint32_t maxPacket = MAX_PACKET_SZ - 10; //seems reasonable + + if(*licPos >= sizeof(gzippedLicences)){ //send terminator + reply[sendSz++] = 0; + *licPos = 0; + } + else{ + + uint32_t left = sizeof(gzippedLicences) - *licPos; + if(left > maxPacket) left = maxPacket; + reply[sendSz++] = 1; + while(left--) reply[sendSz++] = gzippedLicences[(*licPos)++]; + } + } + break; + case BT_CMD_DISPLAY_MODE: + { + if (sz) { //set + settings.displayMode = dataIn[0]; + reply[sendSz++] = 1; + } else if (sz == 0) { + reply[sendSz++] = settings.displayMode; + } + } + break; + case BT_CMD_LOCK: + { + if (sz) { //set + locked = dataIn[0] ? 2 : 0; + reply[sendSz++] = 1; + } else if (sz == 0) { + reply[sendSz++] = locked; + } + } + break; + } + return sendSz; +} + +static uint8_t cmdBuf[MAX_PACKET_SZ]; +static uint32_t bufPos = 0; + + +static void btAdkPortOpen(void* port, uint8_t dlci){ + + bufPos = 0; +} + +static void btAdkPortClose(void* port, uint8_t dlci){ + + //nothing here [yet?] +} + + +static void btAdkPortRx(void* port, uint8_t dlci, const uint8_t* data, uint16_t sz){ + + uint8_t reply[MAX_PACKET_SZ]; + uint32_t i; + uint8_t seq, cmd; + uint16_t cmdSz; + uint8_t* ptr; + + while(sz || bufPos){ + + uint16_t sendSz = 0; + + //copy to buffer as much as we can + while(bufPos < MAX_PACKET_SZ && sz){ + cmdBuf[bufPos++] = *data++; + sz--; + } + + //see if a packet exists + if(bufPos < 4) return; // too small to be a packet -> discard + cmd = cmdBuf[0]; + seq = cmdBuf[1]; + cmdSz = cmdBuf[3]; + cmdSz <<= 8; + cmdSz += cmdBuf[2]; + + if(bufPos - 4 < cmdSz) return; //not entire command received yet + + sendSz = adkProcessCommand(cmd, cmdBuf + 4, cmdSz, 1, reply + 4, MAX_PACKET_SZ - 4); + if(sendSz){ + + reply[0] = cmd | CMD_MASK_REPLY; + reply[1] = seq; + reply[2] = sendSz; + reply[3] = sendSz >> 8; + sendSz += 4; + + L.btRfcommPortTx(port, dlci, reply, sendSz); + } + + //adjust buffer as needed + for(i = 0; i < bufPos - cmdSz - 4; i++){ + cmdBuf[i] = cmdBuf[i + cmdSz + 4]; + } + bufPos = i; + } +} + + + + + + +//////////// bt support (boring) + + +static const uint8_t maxPairedDevices = 4; +static uint8_t numPairedDevices = 0; +static uint8_t savedMac[maxPairedDevices][BLUETOOTH_MAC_SIZE]; +static uint8_t savedKey[maxPairedDevices][BLUETOOTH_LINK_KEY_SIZE]; + +static char adkBtConnectionRequest(const uint8_t* mac, uint32_t devClass, uint8_t linkType){ //return 1 to accept + + Serial.print("Accepting connection from "); + Serial.print(mac[5], HEX); + Serial.print(":"); + Serial.print(mac[4], HEX); + Serial.print(":"); + Serial.print(mac[3], HEX); + Serial.print(":"); + Serial.print(mac[2], HEX); + Serial.print(":"); + Serial.print(mac[1], HEX); + Serial.print(":"); + Serial.println(mac[0], HEX); + return 1; +} + +static char adkBtLinkKeyRequest(const uint8_t* mac, uint8_t* buf){ //link key create + + uint8_t i, j; + + Serial.print("Key request from "); + Serial.print(mac[5], HEX); + Serial.print(":"); + Serial.print(mac[4], HEX); + Serial.print(":"); + Serial.print(mac[3], HEX); + Serial.print(":"); + Serial.print(mac[2], HEX); + Serial.print(":"); + Serial.print(mac[1], HEX); + Serial.print(":"); + Serial.print(mac[0], HEX); + Serial.print(" -> "); + + for(i = 0; i < numPairedDevices; i++){ + + for(j = 0; j < BLUETOOTH_MAC_SIZE && savedMac[i][j] == mac[j]; j++); + if(j == BLUETOOTH_MAC_SIZE){ //match + + Serial.print("{"); + for(j = 0; j < BLUETOOTH_LINK_KEY_SIZE; j++){ + + Serial.print(" "); + Serial.print(savedKey[i][j], HEX); + buf[j] = savedKey[i][j]; + } + Serial.println(" }"); + return 1; + } + } + Serial.println("FAIL"); + return 0; +} + +static void adkBtLinkKeyCreated(const uint8_t* mac, const uint8_t* buf){ //link key was just created, save it if you want it later + + uint8_t j; + + Serial.print("Key created for "); + Serial.print(mac[5], HEX); + Serial.print(":"); + Serial.print(mac[4], HEX); + Serial.print(":"); + Serial.print(mac[3], HEX); + Serial.print(":"); + Serial.print(mac[2], HEX); + Serial.print(":"); + Serial.print(mac[1], HEX); + Serial.print(":"); + Serial.print(mac[0], HEX); + Serial.print(" <- "); + + Serial.print("{"); + for(j = 0; j < BLUETOOTH_LINK_KEY_SIZE; j++){ + + Serial.print(" "); + Serial.print(buf[j], HEX); + } + Serial.print(" }"); + + if(numPairedDevices < maxPairedDevices){ + + for(j = 0; j < BLUETOOTH_LINK_KEY_SIZE; j++) savedKey[numPairedDevices][j] = buf[j]; + for(j = 0; j < BLUETOOTH_MAC_SIZE; j++) savedMac[numPairedDevices][j] = mac[j]; + numPairedDevices++; + Serial.print("saved to slot "); + Serial.print(numPairedDevices); + Serial.print("/"); + Serial.println(maxPairedDevices); + } + else{ + Serial.println("out of slots...discaring\n"); + } +} + +static char adkBtPinRequest(const uint8_t* mac, uint8_t* buf){ //fill buff with PIN code, return num bytes used (16 max) return 0 to decline + + uint8_t v, i = 0; + + Serial.print("PIN request from "); + Serial.print(mac[5], HEX); + Serial.print(":"); + Serial.print(mac[4], HEX); + Serial.print(":"); + Serial.print(mac[3], HEX); + Serial.print(":"); + Serial.print(mac[2], HEX); + Serial.print(":"); + Serial.print(mac[1], HEX); + Serial.print(":"); + Serial.print(mac[0], HEX); + + if(btPIN){ + Serial.print(" -> using pin '"); + Serial.print((char*)btPIN); + Serial.println("'"); + for(i = 0; btPIN[i]; i++) buf[i] = btPIN[i]; + return i; + } + else Serial.println(" no PIN set. rejecting"); + return 0; +} + +#define MAGIX 0xFA + +static uint8_t sdpDescrADK[] = +{ + //service class ID list + SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), 0x00, 0x01, SDP_ITEM_DESC(SDP_TYPE_ARRAY, SDP_SZ_u8), 17, + SDP_ITEM_DESC(SDP_TYPE_UUID, SDP_SZ_16), BT_ADK_UUID, + //ServiceId + SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), 0x00, 0x03, SDP_ITEM_DESC(SDP_TYPE_UUID, SDP_SZ_2), 0x11, 0x01, + //ProtocolDescriptorList + SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), 0x00, 0x04, SDP_ITEM_DESC(SDP_TYPE_ARRAY, SDP_SZ_u8), 15, + SDP_ITEM_DESC(SDP_TYPE_ARRAY, SDP_SZ_u8), 6, + SDP_ITEM_DESC(SDP_TYPE_UUID, SDP_SZ_2), 0x01, 0x00, // L2CAP + SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), L2CAP_PSM_RFCOMM >> 8, L2CAP_PSM_RFCOMM & 0xFF, // L2CAP PSM + SDP_ITEM_DESC(SDP_TYPE_ARRAY, SDP_SZ_u8), 5, + SDP_ITEM_DESC(SDP_TYPE_UUID, SDP_SZ_2), 0x00, 0x03, // RFCOMM + SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_1), MAGIX, // port ### + //browse group list + SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), 0x00, 0x05, SDP_ITEM_DESC(SDP_TYPE_ARRAY, SDP_SZ_u8), 3, + SDP_ITEM_DESC(SDP_TYPE_UUID, SDP_SZ_2), 0x10, 0x02, // Public Browse Group + //name + SDP_ITEM_DESC(SDP_TYPE_UINT, SDP_SZ_2), 0x01, 0x00, SDP_ITEM_DESC(SDP_TYPE_TEXT, SDP_SZ_u8), 12, 'A', 'D', 'K', ' ', 'B', 'T', ' ', 'C', 'O', 'M', 'M', 'S' +}; + +void btStart(){ + uint8_t i, dlci; + int f; + + L.btEnable(adkBtConnectionRequest, adkBtLinkKeyRequest, adkBtLinkKeyCreated, adkBtPinRequest, NULL); + + dlci = L.btRfcommReserveDlci(RFCOMM_DLCI_NEED_EVEN); + + if(!dlci) dbgPrintf("BTADK: failed to allocate DLCI\n"); + else{ + + //change descriptor to be valid... + for(i = 0, f = -1; i < sizeof(sdpDescrADK); i++){ + + if(sdpDescrADK[i] == MAGIX){ + if(f == -1) f = i; + else break; + } + } + + if(i != sizeof(sdpDescrADK) || f == -1){ + + dbgPrintf("BTADK: failed to find a single marker in descriptor\n"); + L.btRfcommReleaseDlci(dlci); + return; + } + + sdpDescrADK[f] = dlci >> 1; + + dbgPrintf("BTADK has DLCI %u\n", dlci); + + L.btRfcommRegisterPort(dlci, btAdkPortOpen, btAdkPortClose, btAdkPortRx); + L.btSdpServiceDescriptorAdd(sdpDescrADK, sizeof(sdpDescrADK)); + } +} + +// USB accessory +static void processUSBAccessory() +{ + if (!L.accessoryConnected()) + return; + + uint8_t receiveBuf[MAX_PACKET_SZ]; + uint8_t reply[MAX_PACKET_SZ]; + + int res = L.accessoryReceive(receiveBuf, sizeof(receiveBuf)); + if (res >= 4) { + uint8_t cmd = receiveBuf[0]; + uint8_t seq = receiveBuf[1]; + uint16_t size = receiveBuf[2] | receiveBuf[3] << 8; + + if (size + 4 > res) { + // short packet + return; + } + + uint16_t replylen = adkProcessCommand(cmd, receiveBuf + 4, size, 0, reply + 4, MAX_PACKET_SZ - 4); + if (replylen > 0) { + reply[0] = cmd | CMD_MASK_REPLY; + reply[1] = seq; + reply[2] = replylen; + reply[3] = replylen >> 8; + replylen += 4; + + dbgPrintf("ADK: USB: sending %d bytes\n", replylen); + L.accessorySend(reply, replylen); + } + } +} + |