blob: 44f90449082b01e7f6a3e299ef7abd42f113587f [file] [log] [blame]
/*
* 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);
}
}
}