From 330d5aaa621280e6fb760879539a5f231184e2cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?PontualTech=20/=20Karl=C3=A3o?= Date: Wed, 29 Apr 2026 08:21:41 -0300 Subject: [PATCH] =?UTF-8?q?feat(ble):=20wake-up=20Xiaoxiang=20BMS=20?= =?UTF-8?q?=E2=80=94=20read=20inicial=20+=205A=20x4=20v1.10.11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Confirmado: BMS responde ao app oficial Xiaoxiang. Problema é técnica de inicialização não implementada na nossa abordagem. Adicionado wake-up sequence ANTES do probe de protocolos: 1. ble.read na notify char (acorda stack BLE) 2. delay 300ms 3. write 0x5A x4 (handshake hello observado em alguns Xiaoxiang) 4. delay 1500ms (BMS processa wake) 5. probe normal de protocolos Cada step protegido por try/catch + timeout 2s — não trava loop. Logs detalhados pra ver onde falha se acontecer. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/diario-bordo.html | 23 ++++++++++++++++++++--- mobile/android/app/build.gradle | 4 ++-- mobile/package.json | 2 +- server/public/index.html | 23 ++++++++++++++++++++--- server/src/index.js | 2 +- 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/app/diario-bordo.html b/app/diario-bordo.html index d9c64a8..b1fd736 100644 --- a/app/diario-bordo.html +++ b/app/diario-bordo.html @@ -6122,8 +6122,25 @@ async function bmsProbeAndAttach(deviceId,deviceName){ else if(first===0xA5)bmsHandleDaly(deviceId,dv,deviceName); // Daly }); await ble.startNotifications({deviceId,service:foundService,characteristic:notifyChar}); - setBleDiag('Notify ativo · aguardando 800ms...','ok'); - await new Promise(r=>setTimeout(r,800)); + setBleDiag('Notify ativo · iniciando wake sequence...','ok'); + await new Promise(r=>setTimeout(r,500)); + // Wake-up sequence (técnica Xiaoxiang): read inicial + wake bytes + try{ + await ble.read({deviceId,service:foundService,characteristic:notifyChar}); + setBleDiag('Read inicial OK (acorda stack)','info'); + }catch(e){setBleDiag('Read inicial skip: '+(e.message||'?'),'info')} + await new Promise(r=>setTimeout(r,300)); + // Wake bytes 5A x4 (algumas firmwares Xiaoxiang) + try{ + const useWnr=writeOnlyWnr; + const fn=useWnr?'writeWithoutResponse':'write'; + const wakePromise=ble[fn]({deviceId,service:foundService,characteristic:writeChar,value:bytesToBase64([0x5A,0x5A,0x5A,0x5A])}); + const wakeTimeout=new Promise((_,rej)=>setTimeout(()=>rej(new Error('wake timeout')),2000)); + await Promise.race([wakePromise,wakeTimeout]); + setBleDiag('Wake 5A x4 enviado','info'); + }catch(e){setBleDiag('Wake skip: '+(e.message||'?'),'info')} + await new Promise(r=>setTimeout(r,1500)); + setBleDiag('Iniciando probe de protocolos...','ok'); // Salva config no device pra reuso const dev=state.btDevices?.find(d=>d.id===deviceId); if(dev){ @@ -6352,7 +6369,7 @@ async function removeBluetoothDevice(id){ renderBluetoothCard(); } -const APP_VERSION='1.10.10'; +const APP_VERSION='1.10.11'; function renderBluetoothCard(){ const el=document.getElementById('bt-list'); const supportEl=document.getElementById('bt-support'); diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle index ccd0f6e..77a733d 100644 --- a/mobile/android/app/build.gradle +++ b/mobile/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "br.com.pontualtech.shivao" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 26 - versionName "1.10.10" + versionCode 27 + versionName "1.10.11" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/mobile/package.json b/mobile/package.json index 5ebc487..ce258b4 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -1,6 +1,6 @@ { "name": "shivao-mobile", - "version": "1.10.10", + "version": "1.10.11", "description": "Shivao app nativo (Capacitor wrapper Android/iOS)", "main": "index.js", "type": "module", diff --git a/server/public/index.html b/server/public/index.html index d9c64a8..b1fd736 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -6122,8 +6122,25 @@ async function bmsProbeAndAttach(deviceId,deviceName){ else if(first===0xA5)bmsHandleDaly(deviceId,dv,deviceName); // Daly }); await ble.startNotifications({deviceId,service:foundService,characteristic:notifyChar}); - setBleDiag('Notify ativo · aguardando 800ms...','ok'); - await new Promise(r=>setTimeout(r,800)); + setBleDiag('Notify ativo · iniciando wake sequence...','ok'); + await new Promise(r=>setTimeout(r,500)); + // Wake-up sequence (técnica Xiaoxiang): read inicial + wake bytes + try{ + await ble.read({deviceId,service:foundService,characteristic:notifyChar}); + setBleDiag('Read inicial OK (acorda stack)','info'); + }catch(e){setBleDiag('Read inicial skip: '+(e.message||'?'),'info')} + await new Promise(r=>setTimeout(r,300)); + // Wake bytes 5A x4 (algumas firmwares Xiaoxiang) + try{ + const useWnr=writeOnlyWnr; + const fn=useWnr?'writeWithoutResponse':'write'; + const wakePromise=ble[fn]({deviceId,service:foundService,characteristic:writeChar,value:bytesToBase64([0x5A,0x5A,0x5A,0x5A])}); + const wakeTimeout=new Promise((_,rej)=>setTimeout(()=>rej(new Error('wake timeout')),2000)); + await Promise.race([wakePromise,wakeTimeout]); + setBleDiag('Wake 5A x4 enviado','info'); + }catch(e){setBleDiag('Wake skip: '+(e.message||'?'),'info')} + await new Promise(r=>setTimeout(r,1500)); + setBleDiag('Iniciando probe de protocolos...','ok'); // Salva config no device pra reuso const dev=state.btDevices?.find(d=>d.id===deviceId); if(dev){ @@ -6352,7 +6369,7 @@ async function removeBluetoothDevice(id){ renderBluetoothCard(); } -const APP_VERSION='1.10.10'; +const APP_VERSION='1.10.11'; function renderBluetoothCard(){ const el=document.getElementById('bt-list'); const supportEl=document.getElementById('bt-support'); diff --git a/server/src/index.js b/server/src/index.js index c12f6b0..5c58e75 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -413,7 +413,7 @@ app.get('/.well-known/assetlinks.json', (req, res) => { }); // Atalho: /apk redireciona pra última APK release no Forgejo -const LATEST_APK_URL = 'https://git.pontualtech.work/karlao/shivao-projeto/releases/download/v1.10.10/Shivao-v1.10.10.apk'; +const LATEST_APK_URL = 'https://git.pontualtech.work/karlao/shivao-projeto/releases/download/v1.10.11/Shivao-v1.10.11.apk'; app.get('/apk', (req, res) => res.redirect(302, LATEST_APK_URL)); // Página A4 imprimível com QR Code + instruções (cola no barco/marina)