diff --git a/tasmota/xdrv_52_3_berry_MI32.ino b/tasmota/xdrv_52_3_berry_MI32.ino index 027d4892a..032e2e2ed 100644 --- a/tasmota/xdrv_52_3_berry_MI32.ino +++ b/tasmota/xdrv_52_3_berry_MI32.ino @@ -118,7 +118,7 @@ extern "C" { ********************************************************************/ extern void MI32setBerryAdvCB(void* function, uint8_t *buffer); extern void MI32setBerryConnCB(void* function, uint8_t *buffer); - extern bool MI32runBerryConnection(uint8_t operation); + extern bool MI32runBerryConnection(uint8_t operation, bool response); extern bool MI32setBerryCtxSvc(const char *Svc); extern bool MI32setBerryCtxChr(const char *Chr); extern bool MI32setBerryCtxMAC(uint8_t *MAC, uint8_t type); @@ -193,8 +193,12 @@ extern "C" { int be_BLE_run(bvm *vm); int be_BLE_run(bvm *vm){ int32_t argc = be_top(vm); // Get the number of arguments - if ((argc == 2) && be_isint(vm, 2)) { - if (MI32runBerryConnection(be_toint(vm, 2))) be_return(vm); + if ((argc > 1) && be_isint(vm, 2)) { + bool response = false; + if(argc == 3 && be_isint(vm, 3)){ + response = be_toint(vm,3)>0; + } + if (MI32runBerryConnection(be_toint(vm, 2),response)) be_return(vm); } be_raise(vm, kTypeError, nullptr); } diff --git a/tasmota/xsns_62_esp32_mi.h b/tasmota/xsns_62_esp32_mi.h index 190155160..f536c6c9c 100644 --- a/tasmota/xsns_62_esp32_mi.h +++ b/tasmota/xsns_62_esp32_mi.h @@ -154,6 +154,7 @@ struct MI32connectionContextBerry_t{ uint8_t addrType; int error; bool oneOp; + bool response; }; struct { @@ -180,6 +181,8 @@ struct { uint32_t didStartHAP:1; uint32_t triggerBerryAdvCB:1; uint32_t triggerBerryConnCB:1; + uint32_t triggerNextConnJob:1; + uint32_t readyForNextConnJob:1; }; uint32_t all = 0; } mode; @@ -382,7 +385,7 @@ const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType1,kMI32DeviceType2,kMI3 kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12, kMI32DeviceType13,kMI32DeviceType14,kMI32DeviceType15,kMI32DeviceType16}; -const char kMI32_ConnErrorMsg[] PROGMEM = "no Error|could not connect|got no service|got no characteristic|can not read|can not notify|can not write|did not write|notify time out"; +const char kMI32_ConnErrorMsg[] PROGMEM = "no Error|could not connect|did disconnect|got no service|got no characteristic|can not read|can not notify|can not write|did not write|notify time out"; const char kMI32_BLEInfoMsg[] PROGMEM = "Scan ended|Got Notification|Did connect|Did disconnect|Still connected|Start scanning"; @@ -407,6 +410,7 @@ enum MI32_TASK { enum MI32_ConnErrorMsg { MI32_CONN_NO_ERROR = 0, MI32_CONN_NO_CONNECT, + MI32_CONN_DID_DISCCONNECT, MI32_CONN_NO_SERVICE, MI32_CONN_NO_CHARACTERISTIC, MI32_CONN_CAN_NOT_READ, diff --git a/tasmota/xsns_62_esp32_mi.ino b/tasmota/xsns_62_esp32_mi.ino index b1485e148..f8814f0f9 100644 --- a/tasmota/xsns_62_esp32_mi.ino +++ b/tasmota/xsns_62_esp32_mi.ino @@ -22,6 +22,9 @@ -------------------------------------------------------------------------------------------- Version yyyymmdd Action Description -------------------------------------------------------------------------------------------- + 0.9.5.5 20220326 changed - refactored connection task for asynchronous op, add response option, + fixed MI32Key command + ------- 0.9.5.4 20220325 changed - add Berry adv_watch and adv_block to BLE class ------- 0.9.5.3 20220315 changed - reworked Berry part, active scanning and holding active connections possible, new format of advertisement buffer @@ -92,7 +95,9 @@ class MI32SensorCallback : public NimBLEClientCallbacks { } void onDisconnect(NimBLEClient* pclient) { MI32.mode.connected = 0; - MI32.infoMsg = MI32_DID_DISCONNECT; + MI32.infoMsg = MI32_DID_DISCONNECT; + MI32.conCtx->error = MI32_CONN_DID_DISCCONNECT; + MI32.mode.triggerBerryConnCB = 1; //mainly for unexpected or requested disconnects //AddLog(LOG_LEVEL_DEBUG,PSTR("disconnected")); } bool onConnParamsUpdateRequest(NimBLEClient* MI32Client, const ble_gap_upd_params* params) { @@ -169,12 +174,15 @@ void MI32scanEndedCB(NimBLEScanResults results){ void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ if(MI32.mode.triggerBerryConnCB) return; //discard data, if we did not pass the old to Berry yet - MI32.infoMsg = MI32_GOT_NOTIFICATION; - MI32.conCtx->buffer[0] = (uint8_t)length; - memcpy(MI32.conCtx->buffer + 1, pData, length); - MI32.conCtx->returnCharUUID = pRemoteCharacteristic->getUUID().getNative()->u16.value; - MI32.mode.triggerBerryConnCB = 1; - MI32.mode.readingDone = 1; + if(isNotify){ + MI32.infoMsg = MI32_GOT_NOTIFICATION; + MI32.conCtx->buffer[0] = (uint8_t)length; + memcpy(MI32.conCtx->buffer + 1, pData, length); + MI32.conCtx->returnCharUUID = pRemoteCharacteristic->getUUID().getNative()->u16.value; + MI32.conCtx->operation = 103; + MI32.mode.triggerBerryConnCB = 1; + MI32.mode.readingDone = 1; + } } /*********************************************************************************************\ * Helper functions @@ -238,15 +246,13 @@ void MI32_ReverseMAC(uint8_t _mac[]){ void MI32AddKey(mi_bindKey_t keyMAC){ bool unknownMAC = true; - for(auto _sensor : MIBLEsensors){ + for(auto &_sensor : MIBLEsensors){ if(memcmp(keyMAC.MAC,_sensor.MAC,sizeof(keyMAC.MAC))==0){ - AddLog(LOG_LEVEL_DEBUG,PSTR("new key")); - uint8_t* _key = (uint8_t*) malloc(16); - memcpy(_key,keyMAC.key,16); - _sensor.key = _key; + _sensor.key = new uint8_t[16]; + memcpy(_sensor.key,keyMAC.key,16); unknownMAC=false; _sensor.status.hasWrongKey = 0; - AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*) _sensor.key, 16); + AddLog(LOG_LEVEL_INFO,PSTR("add key to %s"),kMI32DeviceType[_sensor.type-1]); } } if(unknownMAC){ @@ -638,6 +644,7 @@ void MI32Init(void) { NimBLEDevice::init(""); AddLog(LOG_LEVEL_INFO,PSTR("M32: Init BLE device")); MI32.mode.init = 1; + MI32.mode.readyForNextConnJob = 1; MI32StartTask(MI32_TASK_SCAN); // Let's get started !! } #ifdef USE_MI_EXT_GUI @@ -653,12 +660,27 @@ void MI32Init(void) { \*********************************************************************************************/ extern "C" { - bool MI32runBerryConnection(uint8_t operation){ + bool MI32runBerryConnection(uint8_t operation, bool response){ if(MI32.conCtx != nullptr){ MI32.conCtx->oneOp = (operation > 9); MI32.conCtx->operation = operation%10; - AddLog(LOG_LEVEL_INFO,PSTR("M32: Berry connection op: %d, addrType: %d, oneOp: %u"),MI32.conCtx->operation, MI32.conCtx->addrType, MI32.conCtx->oneOp); - MI32StartConnectionTask(); + MI32.conCtx->response = response; + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Berry connection op: %d, addrType: %d, oneOp: %u, response: %u"),MI32.conCtx->operation, MI32.conCtx->addrType, MI32.conCtx->oneOp, MI32.conCtx->response); + if(MI32.conCtx->oneOp || MI32.conCtx->operation == 5){ //...or disconnect is requested + MI32StartConnectionTask(); + } + else{ + if(MI32.mode.connected){ + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: continue connection job")); + MI32.mode.triggerNextConnJob = 1; + if(!MI32.mode.readyForNextConnJob){ + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: old connection job not finished yet!!")); + } + } + else{ + MI32StartConnectionTask(); //first job of many or unexpected disconnect + } + } return true; } return false; @@ -676,7 +698,7 @@ extern "C" { bool MI32setBerryCtxSvc(const char *Svc){ if(MI32.conCtx != nullptr){ MI32.conCtx->serviceUUID = NimBLEUUID(Svc); - AddLog(LOG_LEVEL_INFO,PSTR("M32: SVC: %s"),MI32.conCtx->serviceUUID.toString().c_str()); + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: SVC: %s"),MI32.conCtx->serviceUUID.toString().c_str()); return true; } return false; @@ -685,10 +707,10 @@ extern "C" { bool MI32setBerryCtxChr(const char *Chr){ if(MI32.conCtx != nullptr){ MI32.conCtx->charUUID = NimBLEUUID(Chr); - AddLog(LOG_LEVEL_INFO,PSTR("M32: CHR: %s"),MI32.conCtx->charUUID.toString().c_str()); + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: CHR: %s"),MI32.conCtx->charUUID.toString().c_str()); uint16_t _uuid = MI32.conCtx->charUUID.getNative()->u16.value; //if not "notify op" -> present requested characteristic as return UUID MI32.conCtx->returnCharUUID = _uuid; - AddLog(LOG_LEVEL_INFO,PSTR("M32: return UUID: %04x"),MI32.conCtx->returnCharUUID); + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: return UUID: %04x"),MI32.conCtx->returnCharUUID); return true; } return false; @@ -732,7 +754,7 @@ extern "C" { return false; } } - AddLog(LOG_LEVEL_INFO,PSTR("M32: add %s to watchlist of size: %u"),_newAddress.toString().c_str(),NimBLEDevice::getWhiteListCount()); + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: add %s to watchlist of size: %u"),_newAddress.toString().c_str(),NimBLEDevice::getWhiteListCount()); return true; } @@ -1120,7 +1142,6 @@ bool MI32StartConnectionTask(){ void MI32ConnectionTask(void *pvParameters){ NimBLEDevice::setOwnAddrType(BLE_OWN_ADDR_RANDOM,false); //seems to be important for i.e. xbox controller, hopefully not breaking other things NimBLEDevice::setSecurityAuth(true, true, true); - MI32.conCtx->error = MI32_CONN_NO_ERROR; if (MI32ConnectActiveSensor()){ MI32.mode.readingDone = 0; @@ -1139,83 +1160,110 @@ void MI32ConnectionTask(void *pvParameters){ timer++; vTaskDelay(10/ portTICK_PERIOD_MS); } + MI32Client->discoverAttributes(); // solves connection problems on i.e. yeelight dimmer NimBLERemoteService* pSvc = nullptr; NimBLERemoteCharacteristic* pChr = nullptr; - pSvc = MI32Client->getService(MI32.conCtx->serviceUUID); - if(pSvc) { - pChr = pSvc->getCharacteristic(MI32.conCtx->charUUID); - } - else{ - MI32.conCtx->error = MI32_CONN_NO_SERVICE; - } - if (pChr){ - switch(MI32.conCtx->operation){ - case 1: - if(pChr->canRead()) { - std::string _val = pChr->readValue(); - MI32.conCtx->buffer[0] = (uint8_t)_val.size(); - const char *_c_val = _val.c_str(); - memcpy( MI32.conCtx->buffer + 1,_c_val,MI32.conCtx->buffer[0]); - } - else{ - MI32.conCtx->error = MI32_CONN_CAN_NOT_READ; - } - break; - case 2: - if(pChr->canWrite()) { - uint8_t len = MI32.conCtx->buffer[0]; - if(pChr->writeValue(MI32.conCtx->buffer + 1,len,true)) { // true is important ! - // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: write op done")); + // AddLog(LOG_LEVEL_INFO,PSTR("M32: start connection loop")); + bool keepConnectionAlive = true; + MI32.mode.triggerNextConnJob = 1; + while(keepConnectionAlive){ + while(MI32.mode.triggerNextConnJob == 0){ + vTaskDelay(50/ portTICK_PERIOD_MS); + if(MI32.mode.connected == 0){ + MI32StartTask(MI32_TASK_SCAN); + vTaskDelete( NULL ); + } + // AddLog(LOG_LEVEL_INFO,PSTR("M32: wait ...")); + } + // AddLog(LOG_LEVEL_INFO,PSTR("M32: really start connection job now ...")); + MI32.mode.triggerNextConnJob = 0; + MI32.mode.readyForNextConnJob = 0; + pSvc = MI32Client->getService(MI32.conCtx->serviceUUID); + if(pSvc) { + pChr = pSvc->getCharacteristic(MI32.conCtx->charUUID); + } + else{ + MI32.conCtx->error = MI32_CONN_NO_SERVICE; + } + if (pChr){ + switch(MI32.conCtx->operation){ + case 1: + if(pChr->canRead()) { + NimBLEAttValue _val = pChr->readValue(); + MI32.conCtx->buffer[0] = _val.size(); + memcpy( MI32.conCtx->buffer + 1,_val.data(),MI32.conCtx->buffer[0]); } else{ - MI32.conCtx->error = MI32_CONN_DID_NOT_WRITE; + MI32.conCtx->error = MI32_CONN_CAN_NOT_READ; } - } - else{ - MI32.conCtx->error = MI32_CONN_CAN_NOT_WRITE; - } - MI32.mode.readingDone = 1; - break; - case 3: - if(pChr->canNotify()) { - if(pChr->subscribe(true,MI32notifyCB,false)) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: subscribe")); - } - else{ - MI32.conCtx->error = MI32_CONN_CAN_NOT_NOTIFY; - } - break; - default: - break; + break; + case 2: + if(pChr->canWrite()) { + uint8_t len = MI32.conCtx->buffer[0]; + if(pChr->writeValue(MI32.conCtx->buffer + 1,len,MI32.conCtx->response)) { // true is important ! + // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: write op done")); + } + else{ + MI32.conCtx->error = MI32_CONN_DID_NOT_WRITE; + } + } + else{ + MI32.conCtx->error = MI32_CONN_CAN_NOT_WRITE; + } + MI32.mode.readingDone = 1; + break; + case 3: + if(pChr->canNotify()) { + if(pChr->subscribe(true,MI32notifyCB, MI32.conCtx->response)){ + // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: subscribe")); + } + } + else{ + MI32.conCtx->error = MI32_CONN_CAN_NOT_NOTIFY; + } + break; + default: + break; + } } - } - else{ - MI32.conCtx->error = MI32_CONN_NO_CHARACTERISTIC; - } - timer = 0; + else{ + MI32.conCtx->error = MI32_CONN_NO_CHARACTERISTIC; + } + timer = 0; - while (timer<150){ - if (MI32.mode.readingDone){ - break; + while (timer<150){ + if (MI32.mode.readingDone || !MI32.conCtx->oneOp){ + break; + } + else{ + if (MI32.conCtx->operation==3 && MI32.conCtx->oneOp) { + MI32.conCtx->error = MI32_CONN_NOTIFY_TIMEOUT; //did not read on notify - timeout only for one-shot op + } + } + timer++; + vTaskDelay(100/ portTICK_PERIOD_MS); + } + MI32.mode.readingDone = 0; + if(MI32.conCtx->oneOp){ + MI32Client->disconnect(); + MI32.mode.connected = 0; + keepConnectionAlive = false; + MI32StartTask(MI32_TASK_SCAN); } else{ - if (MI32.conCtx->operation==3 && MI32.conCtx->oneOp) { - MI32.conCtx->error = MI32_CONN_NOTIFY_TIMEOUT; //did not read on notify - timeout only for one-shot op - } + MI32.mode.readyForNextConnJob = 1; + MI32.mode.triggerBerryConnCB = 1; } - timer++; - vTaskDelay(100/ portTICK_PERIOD_MS); - } - MI32.mode.readingDone = 0; - if(MI32.conCtx->oneOp){ - MI32Client->disconnect(); - MI32.mode.connected = 0; - DEBUG_SENSOR_LOG(PSTR("M32: requested disconnect")); - MI32StartTask(MI32_TASK_SCAN); } } else{ - MI32.conCtx->error = MI32_CONN_NO_CONNECT; // not connected + if(MI32.conCtx->operation==5){ + MI32.conCtx->error = MI32_CONN_DID_DISCCONNECT; // did succesfully disconnect + } + else{ + MI32.conCtx->error = MI32_CONN_NO_CONNECT; // could not connect + } MI32.mode.connected = 0; MI32StartTask(MI32_TASK_SCAN); }