This commit is contained in:
pvvx 2024-01-21 06:46:39 +03:00
parent be89ccec2c
commit 91be666ec3
8 changed files with 7466 additions and 5599 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -85,7 +85,7 @@ CFLAGS += -Wl,--gc-sections
#CFLAGS += -MM $(CFLAGS) $(INCFLAGS) $< -MT $@ -MF $(OBJ_DIR)/$(patsubst %.o,%.d,$@)
#LDSCRIPT ?= $(SDK_PATH)/misc/phy6222.ld
LDSCRIPT ?= $(SDK_PATH)/misc/ota_phy62x2.ld
LDSCRIPT ?= $(SDK_PATH)/misc/boot_ota_phy62x2.ld
LDFLAGS := $(ARCH_FLAGS)
LDFLAGS += --static -nostartfiles -nostdlib
LDFLAGS += -Wl,--gc-sections

View file

@ -21,7 +21,7 @@
typedef struct _app_info_t {
uint32_t flag; // id = START_UP_FLAG
uint32_t seg_count; // кол-во сегментов
uint32_t start_addr; // стартовый/run адрес
uint32_t start_addr; // стартовый/run адрес (if = -1 -> берестя из первого значения != -1 у сегмента)
uint32_t app_size; // размер OTA без 4-х байт CRC32
} app_info_t;
@ -29,7 +29,7 @@ typedef struct _app_info_t {
typedef struct _app_info_seg_t {
uint32_t faddr; // адрес записи в Flash
uint32_t size; // размер сегмента
uint32_t saddr; // рабочий адрес
uint32_t waddr; // рабочий адрес
uint32_t chk; // не используется
} app_info_seg_t;
@ -126,7 +126,7 @@ int ota_parser(unsigned char *pout, unsigned char *pmsg, unsigned int msg_size)
| (pmsg[4] << 16)
| (pmsg[5] << 24));
} else
ota.erase_addr = FADDR_APP_SEC-1;
ota.erase_addr = FADDR_START_ADDR;
ota.err_flag = OTA_SUCCESS;
ota.start_flag = 0;
ota.debug_flag = 0;
@ -163,7 +163,7 @@ int ota_parser(unsigned char *pout, unsigned char *pmsg, unsigned int msg_size)
} else if (ota_adr == CMD_OTA_END) {
//go to reboot or start app
GAPRole_TerminateConnection();
if(msg_size == 2 + 4) {
if(msg_size >= 2 + 4) {
tmp = ((pmsg[2])
| (pmsg[3] << 8)
| (pmsg[4] << 16)
@ -177,7 +177,7 @@ int ota_parser(unsigned char *pout, unsigned char *pmsg, unsigned int msg_size)
// stop - old error
} else if(ota.start_flag) {
if (ota_adr == (uint16_t)(ota.pkt_index + 1)) { // correct OTA data index
if(msg_size >= 6) {
if(msg_size >= 20) {
crc = (pmsg[19] << 8) | pmsg[18];
if (crc == crc16(pmsg, 18)) {
if (ota_adr == 0) {
@ -187,11 +187,17 @@ int ota_parser(unsigned char *pout, unsigned char *pmsg, unsigned int msg_size)
| (pmsg[5] << 24));
if (tmp != ota.fw_value) // id != ?
err_flg = OTA_FW_CHECK_ERR;
else if(ota.pkt_total == 0) {
tmp = (pmsg[14]
| (pmsg[15] << 8)
| (pmsg[16] << 16));
ota.pkt_total = (tmp >> 4) + 1;
else {
pmsg[2] = 0xff;
pmsg[3] = 0xff;
pmsg[4] = 0xff;
pmsg[5] = 0xff;
if(ota.pkt_total == 0) {
tmp = (pmsg[14]
| (pmsg[15] << 8)
| (pmsg[16] << 16));
ota.pkt_total = (tmp >> 4) + 1;
}
}
} else if (ota_adr == (uint16_t)(ota.pkt_total - 1)) {
tmp = ((pmsg[2])
@ -200,18 +206,19 @@ int ota_parser(unsigned char *pout, unsigned char *pmsg, unsigned int msg_size)
| (pmsg[5] << 24));
ota.crc32 = ota_test_crc();
if(ota.crc32 == tmp) {
// write ID
hal_flash_write(ota.program_offset,
(uint8_t*) &ota.fw_value, 4);
hal_flash_read(ota.program_offset,
(uint8_t*) &tmp, 4);
if (ota.fw_value == tmp) { // OK
if (ota.fw_value == tmp) // Write OK
err_flg = OTA_END;
} else
else
err_flg = OTA_WRITE_FLASH_ERR; // flash write err
} else
err_flg = OTA_FW_CRC32_ERR;
}
if (ota_adr < ota.pkt_total) {
if (err_flg == OTA_SUCCESS && ota_adr < ota.pkt_total) {
tmp = (ota.program_offset + (ota_adr << 4))
& (~(FLASH_SECTOR_SIZE-1));
if (tmp > ota.erase_addr) {
@ -219,12 +226,6 @@ int ota_parser(unsigned char *pout, unsigned char *pmsg, unsigned int msg_size)
hal_flash_erase_sector(tmp);
ota.debug_flag++;
}
if (ota_adr == 0) {
pmsg[2] = 0xff;
pmsg[3] = 0xff;
pmsg[4] = 0xff;
pmsg[5] = 0xff;
}
hal_flash_write(ota.program_offset + (ota_adr << 4), pmsg + 2, 16);
ota.pkt_index = ota_adr;
} else
@ -266,12 +267,13 @@ static uint32_t start_app(void) {
info_seg_faddr += sizeof(info_app);
spif_read(info_seg_faddr, (uint8_t*)&info_seg, sizeof(info_seg));
if(info_app.start_addr == 0xffffffff) // если не назначен
info_app.start_addr = info_seg.saddr; // берется первый сегмент
// берется значение из первого сегмента отличного от -1
info_app.start_addr = info_seg.waddr;
info_seg.faddr += FADDR_START_ADDR;
info_seg.size &= 0x000fffff;
if (info_seg.saddr != info_seg.faddr // не XIP
if (info_seg.waddr != info_seg.faddr // не XIP
&& info_seg.size < (128*1024)) { // < 128k
osal_memcpy((void *)info_seg.saddr, (void *)info_seg.faddr, info_seg.size);
osal_memcpy((void *)info_seg.waddr, (void *)info_seg.faddr, info_seg.size);
}
info_app.seg_count--;
}
@ -285,13 +287,13 @@ static uint32_t start_app(void) {
}
#if defined ( __CC_ARM )
#define __APP_RUN_ADDR__ (0x1FFF1838)
//#define __APP_RUN_ADDR__ (0x1FFF1838)
__asm void __attribute__((section("ota_app_loader_area"))) jump2app(uint32_t entry)
{
LDR R0, = __APP_RUN_ADDR__
LDR R1, [R0, #4]
BX R1
ALIGN
LDR R0, = %0
LDR R1, [R0, #4]
BX R1
ALIGN
}
#elif defined ( __GNUC__ )
__ATTR_SECTION_XIP__

View file

@ -1,59 +1,66 @@
<html class="telFlasherClass"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<html class="phy6222Class"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="styles.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PHY62x2-BTHome тест версия</title>
<title>PHY62x2-BTHome v0.1</title>
</head>
<body>
<script>
//BLE values
const FLASH_SIZE = 0x80000;
var bluetoothDevice, gattServer, otaCharacteristic, myCharacteristic;
//Firmware values
var firmwareArray = null,
startTime = 0,
flgRdFF = false,
fwmaxsize = 196608,
fwname = "",
blockCount = 0;
var bluetoothDevice, gattServer, otaCharacteristic, cmdCharacteristic, infoService;
var devinfo = {};
var startTime = 0,
connected = false;
var ota = {
fwArray: null,
fwname: "",
fwsize: 0,
fwmaxsize: 196608,
ext_flg: false,
blockCount: 0,
program_offset: 0x11010000
};
//Connection values
var connectTrys = 0;
var $ = function(id) { return document.getElementById(id);}
function resetVariables() {
busy = false;
gattServer = null;
Theservice = null;
mainService = null;
otaCharacteristic = null;
myCharacteristic = null;
$('butReadAddr').disabled = true;
$('butStartDFU').disabled = true;
$('butWriteData').disabled = true;
$('butCmdData').disabled = true;
cmdCharacteristic = null;
$('btnDisconnect').disabled = true;
$('btnReadAddr').disabled = true;
$('btnStartDFU').disabled = true;
$('btnSendData').disabled = true;
$('btnSendCommand').disabled = true;
}
function handleError(error) {
addLog(error);
function handleError(text) {
showError(text);
resetVariables();
if (connectTrys < 5) {
connectTrys++;
addLog("Переподключение " + connectTrys + " из " + 5);
doConnect();
} else {
addLog("Подключится не удалось!");
addLog("Подключиться не удалось!");
connectTrys = 0;
disconnect();
}
}
function onDisconnected() {
addLog('Disconnected.');
}
function connect() {
var deviceOptions = {
optionalServices: [0xfff0],
services: [0x180a, 0x181c, 0x181e, 0xfff0],
optionalServices: [0x1800, 0x180a, 0x180f, 0x181a, 0xfff0, 0x2a26],
services: [0x1800, 0x180a, 0x180f, 0x181a, 0xfff0, 0x2a26],
acceptAllDevices: true };
const namePrefix = $('namePrefix').value;
const namePrefix = $('inpNamePrefix').value;
if (namePrefix) {
deviceOptions.acceptAllDevices = false;
deviceOptions.filters = namePrefix.split(",")
@ -66,62 +73,166 @@ function connect() {
}
if (bluetoothDevice != null) bluetoothDevice.gatt.disconnect();
resetVariables();
addLog("Поиск устройств");
$('btnReconnect').disabled = true;
showState("Поиск устройств");
connectTrys = 0;
navigator.bluetooth.requestDevice(deviceOptions).then(device => {
bluetoothDevice = device;
bluetoothDevice.addEventListener('gattserverdisconnected', onDisconnected);
addLog("Connecting to: " + bluetoothDevice.name);
//addLog("Connecting to: " + bluetoothDevice.name);
doConnect();
}).catch(handleError);
}
function disconnect() {
addLog("Отключение");
connected = false;
if (bluetoothDevice != null)
bluetoothDevice.gatt.disconnect();
}
function doConnect() {
bluetoothDevice.gatt.connect().then(server => {
addClog("Найден GATT сервер");
gattServer = server;
return gattServer.getPrimaryService(0xfff0);
}).then(service => {
addClog("Найден Main сервис");
Theservice = service;
return service.getCharacteristic(0xfff3);
}).then(characteristic => {
addClog("Найдена OTA характеристика");
function reconnect() {
addLog("Переподключение");
if (bluetoothDevice != null) {
connected = false;
bluetoothDevice.gatt.disconnect();
}
connectTrys = 0;
doConnect();
}
function getStrCharacteristic(infosrv, suuid) {
return new Promise((resolve, reject) => {
infosrv.getCharacteristic(suuid).then(characteristic => {
characteristic.readValue().then(value => {
return resolve(new TextDecoder("utf-8").decode(value));
}).catch(error => {console.log(error); return resolve(null); });
}).catch(error => {console.log(error); return resolve(null); });
})};
function getDevInfo(devInfEnabled) {
return new Promise((resolve, reject) => {
devinfo.nrstr = null;
devinfo.srstr = null;
devinfo.frstr = null;
devinfo.hrstr = null;
devinfo.vrstr = null;
if (devInfEnabled == true) {
gattServer.getPrimaryService(0x180a).then(service => {
console.log("Найден Device Information Service");
infoService = service;
return getStrCharacteristic(infoService, 0x2a24).then(value => {
devinfo.nrstr = value;
return getStrCharacteristic(infoService, 0x2a25).then(value => {
devinfo.srstr = value;
return getStrCharacteristic(infoService, 0x2a26).then(value => {
devinfo.frstr = value;
return getStrCharacteristic(infoService, 0x2a27).then(value => {
devinfo.hrstr = value;
return getStrCharacteristic(infoService, 0x2a28).then(value => {
devinfo.vrstr = value;
return resolve(devinfo.flg);
}).catch(error => { console.log(error); return resolve(null);});
}).catch(error => { console.log(error); return resolve(null);});
}).catch(error => { console.log(error); return resolve(null);});
}).catch(error => { console.log(error); return resolve(null);});
}).catch(error => { console.log(error); return resolve(null);});
}).catch(error => { console.log(error); return resolve(null);});
} else return resolve(null);
}).catch(error => { console.log(error); return resolve(null);});
}
function linkOta() {
return new Promise((resolve, reject) => {
mainService.getCharacteristic(0xfff3).catch(error => { console.log(error); return resolve(null);})
.then(characteristic => {
console.log("Найдена OTA Characteristic");
otaCharacteristic = characteristic;
return otaCharacteristic.addEventListener('characteristicvaluechanged', event => OtaBlkParse(event.target.value));
}).then(_ => {
return otaCharacteristic.readValue();
}).then(value => {
if(value.byteLength >= 20)
addLog("OTA: "+dump8(value, value.byteLength));
return Theservice.getCharacteristic(0xfff4);
addLog("OTA ver: "+ hex(value.getUint8(1),2));
return resolve(null);});
}).catch(error => { console.log(error); return resolve(null);});
}
function phyConnect(info_flg) {
return getDevInfo(info_flg).then(_ => {
if(devinfo.nrstr != null)
addLog("Model: "+devinfo.nrstr);
if(devinfo.srstr != null)
addLog("Serial: "+devinfo.srstr);
if(devinfo.frstr != null)
addLog("Firmware: "+devinfo.frstr);
if(devinfo.hrstr != null)
addLog("Hardware: "+devinfo.hrstr);
if(devinfo.vrstr != null)
addLog("Software: "+devinfo.vrstr);
return gattServer.getPrimaryService(0xfff0);
}).then(service => {
console.log("Найден Main Service");
mainService = service;
return mainService.getCharacteristic(0xfff4);
}).then(characteristic => {
addClog("Найдена CMD характеристика");
myCharacteristic = characteristic;
return myCharacteristic.readValue();
console.log("Найдена CMD Characteristic");
cmdCharacteristic = characteristic;
return cmdCharacteristic.addEventListener('characteristicvaluechanged', event => customBlkParse(event.target.value));
}).then(_ => {
return cmdCharacteristic.startNotifications();
}).then(_ => {
return cmdCharacteristic.readValue();
}).then(value => {
if(value.byteLength >= 10)
addLog("DevCfg: "+dump8(value, value.byteLength));
return myCharacteristic.addEventListener('characteristicvaluechanged', event => CustomBlkParse(event.target.value));
}).then(_ => {
myCharacteristic.startNotifications().then(_ => {
let s = "Устройство подключено.";
addAlog(s);
$('butCmdData').disabled = false;
$('butReadAddr').disabled = false;
$('butWriteData').disabled = false;
if (firmwareArray != null) $('butStartDFU').disabled = false;
})
addLog("DevCfg: 55"+dump8(value, value.byteLength));
otaCharacteristic = null;
return linkOta();
}).then(_ => {
showState("Устройство подключено.");
connected = true;
$('btnDisconnect').disabled = false;
$('btnReconnect').disabled = false;
$('btnSendCommand').disabled = false;
$('btnReadAddr').disabled = false;
$('btnSendData').disabled = false;
if (otaCharacteristic != null && ota.fwArray != null)
$('btnStartDFU').disabled = false;
}).catch(handleError);
}
function reConnect() {
if (bluetoothDevice != null) bluetoothDevice.gatt.disconnect();
function doConnect() {
connected = false;
showState("Ожидание соединения с " + bluetoothDevice.name)
return bluetoothDevice.gatt.connect().then(server => {
console.log("Найден GATT сервер");
gattServer = server;
gattServer.getPrimaryServices().then(services => {
let phy = false;
let info = false;
for (var i = 0; i < services.length; i++) {
console.log("Services: " + services[i].uuid);
if (services[i].uuid == "0000180a-0000-1000-8000-00805f9b34fb")
info = true;
else if (services[i].uuid == "0000fff0-0000-1000-8000-00805f9b34fb")
phy = true;
}
if(phy)
return phyConnect(true);
addLog("Выбрано не то устройтво!");
bluetoothDevice.gatt.disconnect();
connectTrys = 10;
return null;
}).catch(handleError);
}).catch(handleError);
}
function onDisconnected() {
connected = false;
resetVariables();
addLog("Reconnect");
connectTrys = 0;
doConnect();
showState('Устройство отключено.');
}
function startDFU() {
@ -136,27 +247,33 @@ function addLog(logTXT) {
$("log").innerHTML += logString + "<br>";
}
function addClog(logTXT) {
console.log(logTXT);
function showState(text) {
let s = "Состояние: " + text;
document.getElementById("lblStatus").className = "shadowbox";
$("lblStatus").innerHTML = s;
addLog(text);
}
function addAlog(logTXT) {
addLog(logTXT);
setStatus(logTXT);
function showProgress(text) {
document.getElementById("lblStatus").className = "shadowprogress";
$("lblStatus").innerHTML = text;
}
function clearLog() {
$("log").innerHTML = "";
}
function setStatus(status) {
// addClog("Status: " + status);
$("percent").innerHTML = "Статус: " + status;
function showError(text) {
// console.log("Status: " + status);
let s = "Ошибка: " + text;
document.getElementById("lblStatus").className = "shadowerror";
$("lblStatus").innerHTML = s;
addLog(text);
}
function updateFail(err) {
let s = "OTA error: " + err;
addAlog(s);
let s = "OTA: " + err;
showError(s);
}
function decimalToHex(d, padding) {
@ -179,27 +296,6 @@ function bytesToHex(data) {
}, "");
}
function crc16_modbus(buffer) {
var crc = 0xFFFF;
var odd;
for (var i = 0; i < buffer.length; i++) {
crc = crc ^ buffer[i];
for (var j = 0; j < 8; j++) {
odd = crc & 0x0001;
crc = crc >> 1;
if (odd) {
crc = crc ^ 0xA001;
}
}
}
return crc;
};
function getHexCRC(data) {
var tempCRC = decimalToHex(crc16_modbus(hexToBytes(data)));
return tempCRC.substring(2, 4) + tempCRC.substring(0, 2);
}
function makeRandomID(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
@ -247,6 +343,27 @@ function hex2ascii(hexx) {
return str;
}
function crc16_modbus(buffer) {
var crc = 0xFFFF;
var odd;
for (var i = 0; i < buffer.length; i++) {
crc = crc ^ buffer[i];
for (var j = 0; j < 8; j++) {
odd = crc & 0x0001;
crc = crc >> 1;
if (odd) {
crc = crc ^ 0xA001;
}
}
}
return crc;
};
function getHexCRC(data) {
var tempCRC = decimalToHex(crc16_modbus(hexToBytes(data)));
return tempCRC.substring(2, 4) + tempCRC.substring(0, 2);
}
var crc32 = (function() {
let table = new Uint32Array(256);
for(var i=256; i--;) {
@ -268,69 +385,67 @@ var crc32 = (function() {
function testOTAFirmware(data) {
let fsize = data.byteLength;
addClog("File size = 0x"+ fsize.toString(16));
if (fsize < 272)
console.log("File size = 0x"+ fsize.toString(16));
if (fsize < 20)
return "Неправильный размер двоичной прошивки PHY6 OTA!";
if(fsize > 192*1024) // 208kB for BLE
return "Размер прошивки более 192 кбайт!";
let head = new DataView(data, 0, 272);
let h = {};
h.id = head.getUint32(0, true);
h.segs = head.getUint32(4, true);
h.start = head.getUint32(8, true);
h.size = head.getUint32(12, true);
addAlog("PHY6 OTA ID: "+hex(h.id, 4)+ ", Сегментов: " + h.segs + ", Старт: 0x"+hex(h.start, 8)+ ", Размер: " + h.size + " байт");
if(h.id != 0x36594850) // "PHY6"
return "Неверное id в заголовке PHY6 OTA!";
if(h.segs > 16)
return "Неверное количество сегментов в заголовке PHY6 OTA!";
if(h.size != fsize-4)
return "Неверный размер в заголовке PHY6 OTA!";
let fcrc = new Uint32Array(2);
fcrc[0] = crc32(new Uint8Array(data.slice(0, h.size)));
let x = new DataView(data, h.size, 4);
fcrc[1] = x.getUint32(0, true);
addClog("Файл CRC = 0x" + fcrc[1].toString(16) + ", Расчет CRC = 0x" + fcrc[0].toString(16));
if(fcrc[0] != fcrc[1]) {
if(fsize > ota.fwmaxsize)
return "Размер прошивки более " + (fwmaxsize/1024).toFixed(1) + " кбайт!";
if(fsize != (fsize & 0x1ffff0) + 4)
return "Неверный формат размера файла для прошивки PHY6 OTA!";
ota.fwsize = fsize;
let head = new DataView(data, 0, 16);
ota.h = {};
ota.h.id = head.getUint32(0, true);
ota.h.segs = head.getUint32(4, true);
ota.h.start = head.getUint32(8, true);
ota.h.size = head.getUint32(12, true);
if(ota.h.id != 0x36594850) { // "PHY6"
ota.ext_flg = true;
ota.h.size = fsize - 4;
ota.h.start = ota.program_offset;
addLog("Файл id:"+hex(ota.h.id, 4)+" , специальная прошивка.");
// return "Неверное id в заголовке PHY6 OTA!";
} else {
ota.ext_flg = false;
addLog("Файл id:PHY6, Сегментов: " + ota.h.segs + ", Старт: 0x"+hex(ota.h.start, 8)+ ", Размер: " + ota.h.size + " байт");
if(ota.h.segs > 16)
return "Неверное количество сегментов в заголовке PHY6 OTA!";
if(ota.h.size + 4 != ota.fwsize)
return "Неверный размер в заголовке PHY6 OTA!";
}
let crc = crc32(new Uint8Array(data.slice(0, ota.h.size)));
let x = new DataView(data, ota.h.size, 4);
ota.fcrc = x.getUint32(0, true);
console.log("Файл CRC = 0x" + ota.fcrc.toString(16) + ", Расчет CRC = 0x" + crc.toString(16));
if(ota.fcrc != crc) {
return "Неправильный CRC в файле OTA!";
}
return "ok";
}
function getFirmwareArray(data, filename) {
addAlog("Файл: " + filename);
addClog("Файл: " + filename);
function getFwArray(data, filename) {
addLog("Файл: " + filename);
let s = testOTAFirmware(data);
if(s != "ok") {
addAlog(s);
blockCount = 0;
firmwareArray = null;
$('btnStartDFU').disabled = true;
addLog(s);
ota.blockCount = 0;
ota.fwArray = null;
fwname = "";
//$("ldfrmw").innerHTML=s;
alert(s);
return;
}
firmwareArray = bytesToHex(data);
addAlog("Размер файла: " + (firmwareArray.length/2).toString(10) + " байт");
if (firmwareArray.length % 32 !== 0) { // pad last block to 16bytes
ota.fwArray = bytesToHex(data);
addLog("Размер файла: " + (ota.fwArray.length/2).toString(10) + " байт");
if (ota.fwArray.length % 32 !== 0) { // pad last block to 16bytes
var padHex = "ffffffffffffffffffffffffffffffff";
firmwareArray += padHex.substr(0, 32 - firmwareArray.length % 32);
ota.fwArray += padHex.substr(0, 32 - ota.fwArray.length % 32);
}
blockCount = firmwareArray.length / 32;
addAlog("Счетчик: " + blockCount + " блоков");
fwname = filename;
$('butStartDFU').disabled = false;
}
window.onload = function() {
document.querySelector("#file").addEventListener("change", function() {
let reader = new FileReader();
reader.fname = "";
reader.onload = function() {getFirmwareArray(this.result, this.fname);};
if (this.files[0] != null)
reader.readAsArrayBuffer(this.files[0]);
else
addLog("Файл не выбран"); }, false);
ota.blockCount = ota.fwArray.length / 32;
addLog("Счетчик: " + ota.blockCount + " блоков");
ota.fwname = filename;
if(connected)
$('btnStartDFU').disabled = false;
}
var ota_errors = [
@ -359,30 +474,40 @@ function get_msg_ota_err(err) {
function OtaBlkParse(value) {
if(value.byteLength < 20) return;
var ota = {};
ota.err_flag = value.getUint8(0);
ota.version = value.getUint8(1);
ota.start_flag = value.getUint8(2);
ota.debug_flag = value.getUint8(3);
ota.program_offset = value.getUint32(4,true);
ota.pkt_index = value.getUint16(8,true);
ota.pkt_total = value.getUint16(10,true);
ota.fw_value = value.getUint32(12,true);
ota.crc32 = value.getUint32(16,true);
//addClog('otablk: '+dump8(value, value.byteLength));
addClog('OTA read: ver: '+hex(ota.version,2)+', err: '+ota.err_flag+' - '+get_msg_ota_err(ota.err_flag)+', dbg: '+ota.debug_flag+', start: '+ota.start_flag+', offs: 0x'+hex(ota.program_offset,8)+', idx: 0x'+hex(ota.pkt_index,4)+', total: 0x'+hex(ota.pkt_total,4)+', crc: 0x'+hex(ota.crc32,8));
ota.ind = {};
ota.ind.err_flag = value.getUint8(0);
ota.ind.version = value.getUint8(1);
ota.ind.start_flag = value.getUint8(2);
ota.ind.debug_flag = value.getUint8(3);
ota.ind.prg_offset = value.getUint32(4,true);
ota.ind.pkt_index = value.getUint16(8,true);
ota.ind.pkt_total = value.getUint16(10,true);
ota.ind.fw_value = value.getUint32(12,true);
ota.ind.crc32 = value.getUint32(16,true);
//console.log('otablk: '+dump8(value, value.byteLength));
console.log('OTA read: ver: '+hex(ota.ind.version,2)+', err: '+ota.ind.err_flag+' - '+get_msg_ota_err(ota.ind.err_flag)+', dbg: '+ota.ind.debug_flag+', start: '+ota.ind.start_flag+', offs: 0x'+hex(ota.ind.prg_offset,8)+', idx: 0x'+hex(ota.ind.pkt_index,4)+', total: 0x'+hex(ota.ind.pkt_total,4)+', crc: 0x'+hex(ota.ind.crc32,8));
}
function updateBegin() {
if (blockCount <= 0) {
addLog("Не выбран файл!");
if (ota.blockCount <= 0) {
showError("Не выбран файл!");
return;
}
setTimeout(function() {
otaCharSend("00ff")
.then(_ => { otaCharacteristic.readValue().then(value => {
// otaCharSend("01ff"+firmwareArray.substring(16,24)+firmwareArray.substring(0,8)+hex((blockCount >> 8) | ((blockCount & 0xff) << 8),4))
otaCharSend("01ff")
otaCharSend("00ff")
.then(_ => { otaCharacteristic.readValue().then(value => {
let cmd = "01ff";
if(ota.ext_flg) {
let blk = new DataView(10);
blk.setUint32(0, ota.program_offset, true);
blk.setUint32(4, ota.h.id, true);
blk.setUint16(8, ota.blockCount, true);
cmd += bytesToHex(blk);
// cmd += ota.fwArray.substring(16,24); // program_offset
// cmd += ota.fwArray.substring(0,8); // fw_id
// cmd += hex((ota.blockCount >> 8) | ((ota.blockCount & 0xff) << 8),4); // pkt_total
}
otaCharSend(cmd)
.then(_ => { otaCharacteristic.readValue().then(value => {
if(value.byteLength >= 2 && value.getUint8(0) == 0) {
setTimeout(function() {
@ -390,7 +515,7 @@ function updateBegin() {
sendOTAblock(0);
}, 100);
} else
addAlog("Ошибка N"+value.getUint8(0)+" OTA!");
showError("Ошибка N"+value.getUint8(0)+" OTA!");
}).catch(function(err) {updateFail(err); });
}).catch(function(err) {updateFail(err); });
}).catch(function(err) {updateFail(err); });
@ -400,31 +525,33 @@ function updateBegin() {
function sendLastOTA() {
otaCharacteristic.readValue().then(value => {
if(value.byteLength >= 1 && value.getUint8(0) == 0xff)
addAlog("Программирование завершено за " + (new Date().getTime() - startTime) / 1000 + " секунды");
else
addAlog("Ошибка ("+value.getUint8(0)+") OTA: " + get_msg_ota_err(value.getUint8(0)));
if(value.byteLength >= 1 && value.getUint8(0) == 0xff) {
let s = "Программирование завершено за " + (new Date().getTime() - startTime) / 1000 + " секунды";
showProgress(s);
addLog(s);
} else
showError("Ошибка ("+value.getUint8(0)+") OTA: " + get_msg_ota_err(value.getUint8(0)));
}).catch(function(err) { updateFail(err); });
/* Сброс - отключен для теста
var data = "02ff";
otaCharSend(data).then(_ => {
addAlog("Программирование завершено за " + (new Date().getTime() - startTime) / 1000 + " секунды");
addLog("Программирование завершено за " + (new Date().getTime() - startTime) / 1000 + " секунды");
}).catch(function(err) {
updateFail(err);
}); */
}
function sendOTAblock(blockNr) {
if (blockNr >= blockCount) {
if (blockNr >= ota.blockCount) {
sendLastOTA();
return;
}
setStatus("Передан блок N: " + blockNr + " из " + blockCount + ", " + Math.floor(blockNr / (blockCount * 1.0) * 100) + "% успеха, время от старта " + (new Date().getTime() - startTime) / 1000.0 + " сек");
showProgress("Передан блок N: " + blockNr + " из " + ota.blockCount + ", " + Math.floor(blockNr / (ota.blockCount * 1.0) * 100) + "% успеха, время от старта " + (new Date().getTime() - startTime) / 1000.0 + " сек");
var blockNrString = getHexBLockCount(blockNr);
var blockString = blockNrString + firmwareArray.substring(blockNr * 32, blockNr * 32 + 32);
var blockString = blockNrString + ota.fwArray.substring(blockNr * 32, blockNr * 32 + 32);
var blockCRC = getHexCRC(blockString);
otaCharSend(blockString + blockCRC).then(_ => {
if (blockNr >= blockCount - 1) {
if (blockNr >= ota.blockCount - 1) {
sendLastOTA();
return;
}
@ -436,7 +563,7 @@ function sendOTAblock(blockNr) {
else {
let s = get_msg_ota_err(value.getUint8(0));
if(s != "ok")
addAlog("Ошибка ("+value.getUint8(0)+") на передаче блока "+blockNr+" OTA: "+s);
showError("Ошибка ("+value.getUint8(0)+") на передаче блока "+blockNr+" OTA: "+s);
}
}).catch(function(err) { updateFail(err); });
} else
@ -455,7 +582,7 @@ function getHexBLockCount(count) {
var otaCharSend = function(data) {
return new Promise(function(resolve, reject) {
//addClog("OTA send: " + data);
//console.log("OTA send: " + data);
otaCharacteristic.writeValue(hexToBytes(data)).then(function(character) {
resolve("ok");
}).catch(function(err) {
@ -466,7 +593,7 @@ var otaCharSend = function(data) {
var mainCharSend = function(data, characteristic) {
return new Promise(function(resolve, reject) {
addClog("Send: " + data);
console.log("Send: " + data);
characteristic.writeValue(hexToBytes(data)).then(function(character) {
resolve("ok");
}).catch(function(err) {
@ -475,90 +602,85 @@ var mainCharSend = function(data, characteristic) {
});
}
function ReadAddr(addr) {
if(myCharacteristic) {
function readAddr(addr) {
if (cmdCharacteristic) {
let blk = new Uint8Array([0xdb, addr&0xff, (addr>>8)&0xff, (addr>>16)&0xff, (addr>>24)&0xff]);
myCharacteristic.writeValue(blk).then(_ => {
cmdCharacteristic.writeValue(blk).then(_ => {
startTime = new Date().getTime();
addAlog("Чтение 16 байт из 0x"+hex(addr,8)+"...");
showState("Прочитано 16 байт из адреса 0x" + hex(addr,8));
});
}
}
function WriteAddr(addr, data) {
if(myCharacteristic) {
function writeAddr(addr, data) {
if (cmdCharacteristic) {
len = data.length;
if(len != 0 && len <= 16) {
let blk = new Uint8Array(len + 5);
blk.set([0xdb, addr&0xff, (addr>>8)&0xff, (addr>>16)&0xff, (addr>>24)&0xff]);
blk.set(data, 5);
console.log(blk);
addAlog("Запись "+len+" байт в 0x"+hex(addr,6)+"...");
myCharacteristic.writeValue(blk);
cmdCharacteristic.writeValue(blk);
showState("Записано " + len + " байт по адресу 0x" + hex(addr,8));
} else {
console.log(data);
addClog('Должно быть от 1 до 16 байт!');
showState('Ошибка, длина блока от 1 до 16 байт!');
}
}
}
function InfoReadAddr() {
if(myCharacteristic) {
function infoReadAddr() {
if(cmdCharacteristic) {
let faddr = parseInt($('inputAddr').value, 16);
ReadAddr(faddr);
readAddr(faddr);
}
}
function WriteCmd(data) {
if(myCharacteristic) {
function writeCmd(data) {
if(cmdCharacteristic) {
len = data.length;
if(len != 0 && len <= 20) {
let blk = new Uint8Array(data);
console.log(blk);
myCharacteristic.writeValue(blk);
} else {
console.log(data);
addClog('Должно быть от 1 до 20 байт!');
}
cmdCharacteristic.writeValue(blk);
} else
showState('Ошибка, длина команды от 1 до 20 байт!');
}
}
function WriteData() {
function sendData() {
let addr = parseInt($('inputAddr').value, 16);
let data = hexToBytes($('inputData').value);
if(data.length != 0 && data.length <= 16)
WriteAddr(addr, data);
writeAddr(addr, data);
else
addClog('Должно быть от 1 до 16 hex байт!');
console.log('Ошибка, длина от 1 до 16 байт!');
}
function CmdData() {
let data = hexToBytes($('inputCmdData').value);
function sendCommand() {
let data = hexToBytes($('inpCmdData').value);
if(data.length != 0 && data.length <= 20)
WriteCmd(data);
writeCmd(data);
else
addClog('Должно быть от 1 до 20 hex байт!');
console.log('Должно быть от 1 до 20 hex байт!');
}
function CustomBlkParse(value) {
function customBlkParse(value) {
let len = value.byteLength;
if(len == 0) return;
len--; // size from cmd
let blkid = value.getUint8(0);
s = 'Ответ на команду id: '+hex(blkid,2)+' data: '+bytesToHex(value.buffer.slice(1));
let blkId = value.getUint8(0);
s = 'Ответ на команду Id: ' + hex(blkId,2) + ' data: ' + bytesToHex(value.buffer.slice(1));
addLog(s);
if(blkid == 0xdb && value.byteLength > 4) {
if((blkId == 0xdb) && (value.byteLength > 4)) {
len -= 4;
let addr = value.getUint32(1,true);
let s = bytesToHex(value.buffer.slice(5), len);
$('inputData').value = s;
addLog(hex(addr,8)+':'+s);
setStatus("Считано "+len+" байт из 0x" + hex(addr,8));
addLog(hex(addr,8) + ':' + s);
showProgress("Считано: " + len + " байт из 0x" + hex(addr,8));
} else
addClog('blk: '+dump8(value, value.byteLength));
console.log('blk: ' + dump8(value, value.byteLength));
}
var url;
@ -578,24 +700,146 @@ function download(data, filename, type) {
}
}
const selectFile = function() {
let regex = /[^\\]+$/
this.choose,
this.selected
this.msg = str => {
let prefix = '[selectFile]\n\nError: '
return alert(prefix+str)
}
this.check = () => {
if (this.choose && this.selected != null) {
let choose = document.getElementById(this.choose),
selected = document.getElementById(this.selected)
choose.addEventListener('change',() => {
if (choose.value != '') {
selected.innerHTML = choose.value.match(regex);
readFile(choose.files[0]);
}
})
} else {
this.msg('Targets not set.')
}
}
selectFile.prototype.targets = (trigger, filetext) => {
this.choose = trigger
this.selected = filetext
}
selectFile.prototype.simulate = () => {
if (this.choose != null) {
let choose = document.getElementById(this.choose)
if (typeof choose != 'undefined') {
choose.click()
this.check()
} else {
this.msg('Could not find element '+this.choose)
}
} else {
this.msg('Targets not set.')
}
}
};
function readFile(file) {
var reader = new FileReader();
if (file != null) {
reader.fname = file.name;
reader.readAsArrayBuffer(file);
} else {
showError("Файл не загружен");
}
reader.onload = function() {
getFwArray(this.result, this.fname);
}
reader.onerror = function() {
console.log(this.error);
};
}
var getFile = new selectFile;
getFile.targets('inpFile','lblFile');
function openTab(evt, tabName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(tabName).style.display = "block";
evt.currentTarget.className += " active";
console.log(evt.currentTarget.className);
}
window.onload = (event) => {
showState("Нет подключения" )
openTab(event, 'tabOTA');
// Selected Tab header style
tablinks = document.getElementsByClassName("tablinks");
tablinks[0].className += " active";
};
</script>
<big><big>PHY62x2-BTHome</big></big> <a href="https://github.com/pvvx/THB2">&#9432;</a><hr>
<button type="button" onclick="connect();">Соединение</button>
<button type="button" onclick="reConnect();">Переподключение</button><br><br>
<label for="namePrefix">Префикс названия устройств(а)</label>
<input type="text" id="namePrefix" value="" placeholder="THB, BT"><br><hr>
Выбор файла прошивки: <input type="file" accept=".bin" id="file"/><br>
<div id="percent">Состояние: Ожидание соединения с устройством</div>
<button type="button"id="butStartDFU" disabled="true" onclick="startDFU();">Старт программирования</button>
<h2>PHY62x2-BTHome <a href="https://github.com/pvvx/THB2"><small>&#9432;</small></a></h2>
<label for="inpNamePrefix">Префикс названия устройств(а)</label><br>
<input type="text" id="inpNamePrefix" value="" placeholder="THB, BT">
<hr>
<label id="lblStatus">Состояние: загрузка страницы</label>
<br><br><hr>
<button type="button" id="btnConnect" onclick="connect()">Соединение</button>
<button type="button" id="btnDisconnect" onclick="disconnect()" disabled="true">Отключение</button>
<button type="button" id="btnReconnect" onclick="reconnect()" disabled="true">Переподключение</button>
<br><hr>
Чтение и запись памяти:<br>
Адрес (hex): <input size="8" type="text" id="inputAddr" value="11000000" maxlength="8">
<input type="button" id="butReadAddr" onclick="InfoReadAddr()" disabled="true" value="Читать">
Данные (hex): <input size="40" type="text" id="inputData" value="?" maxlength="32">
<input type="button" id="butWriteData" onclick="WriteData()" disabled="true" value="Записать"><hr>
<input type="button" id="butCmdData" onclick="CmdData()" disabled="true" value="Команда">
<input size="40" type="text" id="inputCmdData" value="55" maxlength="40"><hr>
<button type="button" onclick="clearLog();">Очистить лог</button><br>
<div class="tab">
<button class="tablinks" onclick="openTab(event, 'tabOTA')">OTA</button>
<button class="tablinks" onclick="openTab(event, 'tabFlash')">Flash</button>
<button class="tablinks" onclick="openTab(event, 'tabService')">Service</button>
</div>
<div id="tabOTA" class="tabcontent">
<p>Файл прошивки:
<input type=file hidden id=inpFile>
<label id=lblFile>не выбран</label>
<button type=button onClick=getFile.simulate()>Выбрать</button>
<p>
<hr>
<button type="button"id="btnStartDFU" onclick="startDFU()" disabled="true" >Старт программирования</button>
</div>
<div id="tabFlash" class="tabcontent">
<p>
<button type="button" id="btnReadFF" onclick="startReadFF()" disabled="true">Прочитать</button>
<button type="button" id="btnSave" disabled="true">Сохранить в файл</button>
<p>
</div>
<div id="tabService" class="tabcontent">
Чтение и запись памяти:<br>
Адрес (hex): <input size="8" type="text" id="inputAddr" value="11000000" maxlength="8">
<button type="button" id="btnReadAddr" onclick="infoReadAddr()" disabled="true">Прочитать</button>
Данные (hex): <input size="32" type="text" id="inputData" value="?" maxlength="32">
<button type="button" id="btnSendData" onclick="sendData()" disabled="true">Записать</button><hr>
<button type="button" id="btnSendCommand" onclick="sendCommand()" disabled="true">Команда</button>
<input type="text" id="inpCmdData" value="55" size="40" maxlength="40"><hr>
</div>
<hr>
<button type="button" onclick="clearLog()">Очистить лог</button><br>
<div id="log"></div>
</body></html>
</body>
</html>

View file

@ -0,0 +1,601 @@
<html class="telFlasherClass"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PHY62x2-BTHome тест версия</title>
</head>
<body>
<script>
//BLE values
const FLASH_SIZE = 0x80000;
var bluetoothDevice, gattServer, otaCharacteristic, myCharacteristic;
//Firmware values
var firmwareArray = null,
startTime = 0,
flgRdFF = false,
fwmaxsize = 196608,
fwname = "",
blockCount = 0;
//Connection values
var connectTrys = 0;
var $ = function(id) { return document.getElementById(id);}
function resetVariables() {
busy = false;
gattServer = null;
Theservice = null;
otaCharacteristic = null;
myCharacteristic = null;
$('butReadAddr').disabled = true;
$('butStartDFU').disabled = true;
$('butWriteData').disabled = true;
$('butCmdData').disabled = true;
}
function handleError(error) {
addLog(error);
resetVariables();
if (connectTrys < 5) {
connectTrys++;
addLog("Переподключение " + connectTrys + " из " + 5);
doConnect();
} else {
addLog("Подключится не удалось!");
connectTrys = 0;
}
}
function onDisconnected() {
addLog('Disconnected.');
}
function connect() {
var deviceOptions = {
optionalServices: [0xfff0],
services: [0x180a, 0x181c, 0x181e, 0xfff0],
acceptAllDevices: true };
const namePrefix = $('namePrefix').value;
if (namePrefix) {
deviceOptions.acceptAllDevices = false;
deviceOptions.filters = namePrefix.split(",")
.map((x) => ({ namePrefix: x }));
} else {
deviceOptions.acceptAllDevices = false;
deviceOptions.filters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz_#@!*0123456789';,.<>{}[]"
.split("")
.map((x) => ({ namePrefix: x }));
}
if (bluetoothDevice != null) bluetoothDevice.gatt.disconnect();
resetVariables();
addLog("Поиск устройств");
connectTrys = 0;
navigator.bluetooth.requestDevice(deviceOptions).then(device => {
bluetoothDevice = device;
bluetoothDevice.addEventListener('gattserverdisconnected', onDisconnected);
addLog("Connecting to: " + bluetoothDevice.name);
doConnect();
}).catch(handleError);
}
function doConnect() {
bluetoothDevice.gatt.connect().then(server => {
addClog("Найден GATT сервер");
gattServer = server;
return gattServer.getPrimaryService(0xfff0);
}).then(service => {
addClog("Найден Main сервис");
Theservice = service;
return service.getCharacteristic(0xfff3);
}).then(characteristic => {
addClog("Найдена OTA характеристика");
otaCharacteristic = characteristic;
return otaCharacteristic.addEventListener('characteristicvaluechanged', event => OtaBlkParse(event.target.value));
}).then(_ => {
return otaCharacteristic.readValue();
}).then(value => {
if(value.byteLength >= 20)
addLog("OTA: "+dump8(value, value.byteLength));
return Theservice.getCharacteristic(0xfff4);
}).then(characteristic => {
addClog("Найдена CMD характеристика");
myCharacteristic = characteristic;
return myCharacteristic.readValue();
}).then(value => {
if(value.byteLength >= 10)
addLog("DevCfg: "+dump8(value, value.byteLength));
return myCharacteristic.addEventListener('characteristicvaluechanged', event => CustomBlkParse(event.target.value));
}).then(_ => {
myCharacteristic.startNotifications().then(_ => {
let s = "Устройство подключено.";
addAlog(s);
$('butCmdData').disabled = false;
$('butReadAddr').disabled = false;
$('butWriteData').disabled = false;
if (firmwareArray != null) $('butStartDFU').disabled = false;
})
}).catch(handleError);
}
function reConnect() {
if (bluetoothDevice != null) bluetoothDevice.gatt.disconnect();
resetVariables();
addLog("Reconnect");
connectTrys = 0;
doConnect();
}
function startDFU() {
addLog("Старт программирования...");
updateBegin();
}
function addLog(logTXT) {
console.log(logTXT)
var time = new Date().toLocaleTimeString();
var logString = time + ": " + logTXT;
$("log").innerHTML += logString + "<br>";
}
function addClog(logTXT) {
console.log(logTXT);
}
function addAlog(logTXT) {
addLog(logTXT);
setStatus(logTXT);
}
function clearLog() {
$("log").innerHTML = "";
}
function setStatus(status) {
// addClog("Status: " + status);
$("percent").innerHTML = "Статус: " + status;
}
function updateFail(err) {
let s = "OTA error: " + err;
addAlog(s);
}
function decimalToHex(d, padding) {
var hex = Number(d).toString(16);
while (hex.length < 4) {
hex = "0" + hex;
}
return hex;
}
function hexToBytes(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return new Uint8Array(bytes);
}
function bytesToHex(data) {
return new Uint8Array(data).reduce(function(memo, i) {
return memo + ("0" + i.toString(16)).slice(-2);
}, "");
}
function crc16_modbus(buffer) {
var crc = 0xFFFF;
var odd;
for (var i = 0; i < buffer.length; i++) {
crc = crc ^ buffer[i];
for (var j = 0; j < 8; j++) {
odd = crc & 0x0001;
crc = crc >> 1;
if (odd) {
crc = crc ^ 0xA001;
}
}
}
return crc;
};
function getHexCRC(data) {
var tempCRC = decimalToHex(crc16_modbus(hexToBytes(data)));
return tempCRC.substring(2, 4) + tempCRC.substring(0, 2);
}
function makeRandomID(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return bytesToHex(new TextEncoder("utf-8").encode(result));
}
function hex(number, len) {
var str = (number.toString(16)).toUpperCase();
while (str.length < len) str = '0' + str;
return str;
}
function hexToBytes(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return new Uint8Array(bytes);
}
function dump(ar, len) {
let s = '';
for(let i=0; i < len; i++) {
s += hex(ar[i],2);
}
return s;
}
function dump8(ar, len) {
let s = '';
for(let i=0; i < len; i++) {
s += hex(ar.getUint8(i),2);
}
return s;
}
function hex2ascii(hexx) {
var hex = hexx.toString();
var str = '';
for (var i = 0;
(i < hex.length && hex.substr(i, 2) !== '00'); i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}
var crc32 = (function() {
let table = new Uint32Array(256);
for(var i=256; i--;) {
let tmp = i;
for(let k=8; k--;) {
tmp = tmp & 1 ? 0xEDB88320 ^ tmp >>> 1 : tmp >>> 1;
}
table[i] = tmp;
}
return function( data ) {
let crc = -1;
let l = data.length;
for(let i=0; i<l; i++) {
crc = crc >>> 8 ^ table[ crc & 255 ^ data[i] ];
}
return (crc >>> 0);
};
})();
function testOTAFirmware(data) {
let fsize = data.byteLength;
addClog("File size = 0x"+ fsize.toString(16));
if (fsize < 272)
return "Неправильный размер двоичной прошивки PHY6 OTA!";
if(fsize > 192*1024) // 208kB for BLE
return "Размер прошивки более 192 кбайт!";
let head = new DataView(data, 0, 272);
let h = {};
h.id = head.getUint32(0, true);
h.segs = head.getUint32(4, true);
h.start = head.getUint32(8, true);
h.size = head.getUint32(12, true);
addAlog("PHY6 OTA ID: "+hex(h.id, 4)+ ", Сегментов: " + h.segs + ", Старт: 0x"+hex(h.start, 8)+ ", Размер: " + h.size + " байт");
if(h.id != 0x36594850) // "PHY6"
return "Неверное id в заголовке PHY6 OTA!";
if(h.segs > 16)
return "Неверное количество сегментов в заголовке PHY6 OTA!";
if(h.size != fsize-4)
return "Неверный размер в заголовке PHY6 OTA!";
let fcrc = new Uint32Array(2);
fcrc[0] = crc32(new Uint8Array(data.slice(0, h.size)));
let x = new DataView(data, h.size, 4);
fcrc[1] = x.getUint32(0, true);
addClog("Файл CRC = 0x" + fcrc[1].toString(16) + ", Расчет CRC = 0x" + fcrc[0].toString(16));
if(fcrc[0] != fcrc[1]) {
return "Неправильный CRC в файле OTA!";
}
return "ok";
}
function getFirmwareArray(data, filename) {
addAlog("Файл: " + filename);
addClog("Файл: " + filename);
let s = testOTAFirmware(data);
if(s != "ok") {
addAlog(s);
blockCount = 0;
firmwareArray = null;
fwname = "";
//$("ldfrmw").innerHTML=s;
alert(s);
return;
}
firmwareArray = bytesToHex(data);
addAlog("Размер файла: " + (firmwareArray.length/2).toString(10) + " байт");
if (firmwareArray.length % 32 !== 0) { // pad last block to 16bytes
var padHex = "ffffffffffffffffffffffffffffffff";
firmwareArray += padHex.substr(0, 32 - firmwareArray.length % 32);
}
blockCount = firmwareArray.length / 32;
addAlog("Счетчик: " + blockCount + " блоков");
fwname = filename;
$('butStartDFU').disabled = false;
}
window.onload = function() {
document.querySelector("#file").addEventListener("change", function() {
let reader = new FileReader();
reader.fname = "";
reader.onload = function() {getFirmwareArray(this.result, this.fname);};
if (this.files[0] != null)
reader.readAsArrayBuffer(this.files[0]);
else
addLog("Файл не выбран"); }, false);
}
var ota_errors = [
'ok',
'Неверная команда',
'Не задан старт',
'Не заданы параметры',
'Неверные параметры',
'Неправильный размер пакета',
'Ошибка CRC16 пакета',
'Потеря пакетов',
'Ошибка записи в Flash',
'Ошибка в номере пакета',
'Ошибка идентификатора в файле программы',
'Ошибка CRC32 переданной программы'];
function get_msg_ota_err(err) {
if(err == 0)
return "ok";
if(err == 255)
return "OTA end";
if(err <= 11)
return ota_errors[err];
return "Неизвестная ошибка";
}
function OtaBlkParse(value) {
if(value.byteLength < 20) return;
var ota = {};
ota.err_flag = value.getUint8(0);
ota.version = value.getUint8(1);
ota.start_flag = value.getUint8(2);
ota.debug_flag = value.getUint8(3);
ota.program_offset = value.getUint32(4,true);
ota.pkt_index = value.getUint16(8,true);
ota.pkt_total = value.getUint16(10,true);
ota.fw_value = value.getUint32(12,true);
ota.crc32 = value.getUint32(16,true);
//addClog('otablk: '+dump8(value, value.byteLength));
addClog('OTA read: ver: '+hex(ota.version,2)+', err: '+ota.err_flag+' - '+get_msg_ota_err(ota.err_flag)+', dbg: '+ota.debug_flag+', start: '+ota.start_flag+', offs: 0x'+hex(ota.program_offset,8)+', idx: 0x'+hex(ota.pkt_index,4)+', total: 0x'+hex(ota.pkt_total,4)+', crc: 0x'+hex(ota.crc32,8));
}
function updateBegin() {
if (blockCount <= 0) {
addLog("Не выбран файл!");
return;
}
setTimeout(function() {
otaCharSend("00ff")
.then(_ => { otaCharacteristic.readValue().then(value => {
// otaCharSend("01ff"+firmwareArray.substring(16,24)+firmwareArray.substring(0,8)+hex((blockCount >> 8) | ((blockCount & 0xff) << 8),4))
otaCharSend("01ff")
.then(_ => { otaCharacteristic.readValue().then(value => {
if(value.byteLength >= 2 && value.getUint8(0) == 0) {
setTimeout(function() {
startTime = new Date().getTime();
sendOTAblock(0);
}, 100);
} else
addAlog("Ошибка N"+value.getUint8(0)+" OTA!");
}).catch(function(err) {updateFail(err); });
}).catch(function(err) {updateFail(err); });
}).catch(function(err) {updateFail(err); });
}).catch(function(err) {updateFail(err); });
}, 100);
}
function sendLastOTA() {
otaCharacteristic.readValue().then(value => {
if(value.byteLength >= 1 && value.getUint8(0) == 0xff)
addAlog("Программирование завершено за " + (new Date().getTime() - startTime) / 1000 + " секунды");
else
addAlog("Ошибка ("+value.getUint8(0)+") OTA: " + get_msg_ota_err(value.getUint8(0)));
}).catch(function(err) { updateFail(err); });
/* Сброс - отключен для теста
var data = "02ff";
otaCharSend(data).then(_ => {
addAlog("Программирование завершено за " + (new Date().getTime() - startTime) / 1000 + " секунды");
}).catch(function(err) {
updateFail(err);
}); */
}
function sendOTAblock(blockNr) {
if (blockNr >= blockCount) {
sendLastOTA();
return;
}
setStatus("Передан блок N: " + blockNr + " из " + blockCount + ", " + Math.floor(blockNr / (blockCount * 1.0) * 100) + "% успеха, время от старта " + (new Date().getTime() - startTime) / 1000.0 + " сек");
var blockNrString = getHexBLockCount(blockNr);
var blockString = blockNrString + firmwareArray.substring(blockNr * 32, blockNr * 32 + 32);
var blockCRC = getHexCRC(blockString);
otaCharSend(blockString + blockCRC).then(_ => {
if (blockNr >= blockCount - 1) {
sendLastOTA();
return;
}
setTimeout(function() {
if ((blockNr + 1) % 8 == 0) {
otaCharacteristic.readValue().then(value => {
if(value.byteLength >= 1 && value.getUint8(0) == 0)
sendOTAblock(blockNr + 1);
else {
let s = get_msg_ota_err(value.getUint8(0));
if(s != "ok")
addAlog("Ошибка ("+value.getUint8(0)+") на передаче блока "+blockNr+" OTA: "+s);
}
}).catch(function(err) { updateFail(err); });
} else
sendOTAblock(blockNr + 1);
}, 0);
}).catch(function(err) {
updateFail(err);
});
}
function getHexBLockCount(count) {
var tempHEX = decimalToHex(count);
return tempHEX.substring(2, 4) + tempHEX.substring(0, 2);
}
var otaCharSend = function(data) {
return new Promise(function(resolve, reject) {
//addClog("OTA send: " + data);
otaCharacteristic.writeValue(hexToBytes(data)).then(function(character) {
resolve("ok");
}).catch(function(err) {
reject("Ошибка при отправке данных");
});
});
}
var mainCharSend = function(data, characteristic) {
return new Promise(function(resolve, reject) {
addClog("Send: " + data);
characteristic.writeValue(hexToBytes(data)).then(function(character) {
resolve("ok");
}).catch(function(err) {
reject("Ошибка при отправке данных");
});
});
}
function ReadAddr(addr) {
if(myCharacteristic) {
let blk = new Uint8Array([0xdb, addr&0xff, (addr>>8)&0xff, (addr>>16)&0xff, (addr>>24)&0xff]);
myCharacteristic.writeValue(blk).then(_ => {
startTime = new Date().getTime();
addAlog("Чтение 16 байт из 0x"+hex(addr,8)+"...");
});
}
}
function WriteAddr(addr, data) {
if(myCharacteristic) {
len = data.length;
if(len != 0 && len <= 16) {
let blk = new Uint8Array(len + 5);
blk.set([0xdb, addr&0xff, (addr>>8)&0xff, (addr>>16)&0xff, (addr>>24)&0xff]);
blk.set(data, 5);
console.log(blk);
addAlog("Запись "+len+" байт в 0x"+hex(addr,6)+"...");
myCharacteristic.writeValue(blk);
} else {
console.log(data);
addClog('Должно быть от 1 до 16 байт!');
}
}
}
function InfoReadAddr() {
if(myCharacteristic) {
let faddr = parseInt($('inputAddr').value, 16);
ReadAddr(faddr);
}
}
function WriteCmd(data) {
if(myCharacteristic) {
len = data.length;
if(len != 0 && len <= 20) {
let blk = new Uint8Array(data);
console.log(blk);
myCharacteristic.writeValue(blk);
} else {
console.log(data);
addClog('Должно быть от 1 до 20 байт!');
}
}
}
function WriteData() {
let addr = parseInt($('inputAddr').value, 16);
let data = hexToBytes($('inputData').value);
if(data.length != 0 && data.length <= 16)
WriteAddr(addr, data);
else
addClog('Должно быть от 1 до 16 hex байт!');
}
function CmdData() {
let data = hexToBytes($('inputCmdData').value);
if(data.length != 0 && data.length <= 20)
WriteCmd(data);
else
addClog('Должно быть от 1 до 20 hex байт!');
}
function CustomBlkParse(value) {
let len = value.byteLength;
if(len == 0) return;
len--; // size from cmd
let blkid = value.getUint8(0);
s = 'Ответ на команду id: '+hex(blkid,2)+' data: '+bytesToHex(value.buffer.slice(1));
addLog(s);
if(blkid == 0xdb && value.byteLength > 4) {
len -= 4;
let addr = value.getUint32(1,true);
let s = bytesToHex(value.buffer.slice(5), len);
$('inputData').value = s;
addLog(hex(addr,8)+':'+s);
setStatus("Считано "+len+" байт из 0x" + hex(addr,8));
} else
addClog('blk: '+dump8(value, value.byteLength));
}
var url;
function download(data, filename, type) {
var file = new Blob([data], {type: type});
if (window.navigator.msSaveOrOpenBlob) { // ie10+
window.navigator.msSaveOrOpenBlob(file, filename);
} else { // ff, chrome
url = URL.createObjectURL(file);
let a = document.createElement("a");
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function(){document.body.removeChild(a);window.URL.revokeObjectURL(url);},0);
URL.revokeObjectURL(url);
}
}
</script>
<big><big>PHY62x2-BTHome</big></big> <a href="https://github.com/pvvx/THB2">&#9432;</a><hr>
<button type="button" onclick="connect();">Соединение</button>
<button type="button" onclick="reConnect();">Переподключение</button><br><br>
<label for="namePrefix">Префикс названия устройств(а)</label>
<input type="text" id="namePrefix" value="" placeholder="THB, BT"><br><hr>
Выбор файла прошивки: <input type="file" accept=".bin" id="file"/><br>
<div id="percent">Состояние: Ожидание соединения с устройством</div>
<button type="button"id="butStartDFU" disabled="true" onclick="startDFU();">Старт программирования</button>
<br><hr>
Чтение и запись памяти:<br>
Адрес (hex): <input size="8" type="text" id="inputAddr" value="11000000" maxlength="8">
<input type="button" id="butReadAddr" onclick="InfoReadAddr()" disabled="true" value="Читать">
Данные (hex): <input size="40" type="text" id="inputData" value="?" maxlength="32">
<input type="button" id="butWriteData" onclick="WriteData()" disabled="true" value="Записать"><hr>
<input type="button" id="butCmdData" onclick="CmdData()" disabled="true" value="Команда">
<input size="40" type="text" id="inputCmdData" value="55" maxlength="40"><hr>
<button type="button" onclick="clearLog();">Очистить лог</button><br>
<div id="log"></div>
</body></html>

View file

@ -0,0 +1,621 @@
<html class="phy6222Class"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="styles.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PHY62x2-BTHome тестовая версия</title>
</head>
<body>
<script>
//BLE values
const FLASH_SIZE = 0x80000;
var bluetoothDevice, gattServer, otaCharacteristic, myCharacteristic;
//Firmware values
var firmwareArray = "",
startTime = 0,
flgRdFF = false,
blockCount = 0;
//Connection values
var connectTrys = 0;
var $ = function(id) { return document.getElementById(id);}
function resetVariables() {
busy = false;
gattServer = null;
Theservice = null;
otaCharacteristic = null;
myCharacteristic = null;
$('btnDisconnect').disabled = true;
$('btnReadFF').disabled = true;
$('btnReadAddr').disabled = true;
$('btnSave').disabled = true;
$('btnStartDFU').disabled = true;
$('btnSendData').disabled = true;
$('btnSendCommand').disabled = true;
// showState("Ожидание соединения с устройством")
}
function handleError(text) {
showError(text);
resetVariables();
if (connectTrys < 5) {
connectTrys++;
addLog("Переподключение " + connectTrys + " из " + 5);
doConnect();
} else {
addLog("Подключиться не удалось!");
connectTrys = 0;
}
}
function doConnect() {
showState("Ожидание соединения с " + bluetoothDevice.name)
bluetoothDevice.gatt.connect().then(server => {
console.log("Найден GATT сервер");
gattServer = server;
return gattServer.getPrimaryService(0xfff0);
}).then(service => {
console.log("Найден Main сервис");
Theservice = service;
return service.getCharacteristic(0xfff3);
}).then(characteristic => {
console.log("Найдена OTA характеристика");
otaCharacteristic = characteristic;
return Theservice.getCharacteristic(0xfff4);
}).then(characteristic => {
console.log("Найдена CMD характеристика");
myCharacteristic = characteristic;
myCharacteristic.readValue().then(value => {
if(value.byteLength >= 10)
addLog("DevCfg: " + dump8(value, value.byteLength));
//flash_buf = new Uint8Array(FLASH_SIZE);
myCharacteristic.addEventListener('characteristicvaluechanged', event => customBlkParse(event.target.value));
myCharacteristic.startNotifications().then(_ => {
showState("Устройство подключено.");
$('btnDisconnect').disabled = false;
$('btnReconnect').disabled = false;
$('btnSendCommand').disabled = false;
$('btnReadFF').disabled = false;
$('btnReadAddr').disabled = false;
$('btnSendData').disabled = false;
$('btnStartDFU').disabled = false; })
})
}).catch(handleError);
}
function onDisconnected() {
resetVariables();
showState('Устройство отключено.');
}
function connect() {
var deviceOptions = {
optionalServices: [0xfff0],
services: [0x180a, 0x181c, 0x181e, 0xfff0],
acceptAllDevices: true };
const namePrefix = $('inpNamePrefix').value;
if (namePrefix) {
deviceOptions.acceptAllDevices = false;
deviceOptions.filters = namePrefix.split(",")
.map((x) => ({ namePrefix: x }));
} else {
deviceOptions.acceptAllDevices = false;
deviceOptions.filters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz_#@!*0123456789';,.<>{}[]"
.split("")
.map((x) => ({ namePrefix: x }));
}
if (bluetoothDevice != null) bluetoothDevice.gatt.disconnect();
resetVariables();
$('btnReconnect').disabled = true;
showState("Поиск устройств");
connectTrys = 0;
navigator.bluetooth.requestDevice(deviceOptions).then(device => {
bluetoothDevice = device;
bluetoothDevice.addEventListener('gattserverdisconnected', onDisconnected);
// addLog("Connecting to: " + bluetoothDevice.name);
doConnect();
}).catch(handleError);
}
function disconnect() {
addLog("Отключение");
if (bluetoothDevice != null) bluetoothDevice.gatt.disconnect();
}
function reconnect() {
addLog("Переподключение");
if (bluetoothDevice != null) bluetoothDevice.gatt.disconnect();
connectTrys = 0;
doConnect();
}
function startDFU() {
addLog("Start DFU");
updateBegin();
}
function addLog(text) {
var stime = new Date().toLocaleTimeString();
var string = stime + ": " + text;
console.log(string)
$("log").innerHTML += string + "<br>";
}
function clearLog() {
$("log").innerHTML = "";
}
function showState(text) {
// console.log("Status: " + status);
let s = "Состояние: " + text;
document.getElementById("lblStatus").className = "shadowbox";
$("lblStatus").innerHTML = s;
addLog(text);
}
function showError(text) {
// console.log("Status: " + status);
let s = "Ошибка: " + text;
document.getElementById("lblStatus").className = "shadowerror";
$("lblStatus").innerHTML = s;
addLog(text);
}
function showProgress(text) {
document.getElementById("lblStatus").className = "shadowprogress";
$("lblStatus").innerHTML = text;
}
function updateFail(err) {
let s = err + ". Update failed";
showError(s);
}
function decimalToHex(d, padding) {
var hex = Number(d).toString(16);
while (hex.length < 4) {
hex = "0" + hex;
}
return hex;
}
function hexToBytes(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return new Uint8Array(bytes);
}
function bytesToHex(data) {
return new Uint8Array(data).reduce(function(memo, i) {
return memo + ("0" + i.toString(16)).slice(-2);
}, "");
}
function crc16_modbus(buffer) {
var crc = 0xFFFF;
var odd;
for (var i = 0; i < buffer.length; i++) {
crc = crc ^ buffer[i];
for (var j = 0; j < 8; j++) {
odd = crc & 0x0001;
crc = crc >> 1;
if (odd) {
crc = crc ^ 0xA001;
}
}
}
return crc;
};
function getHexCRC(data) {
var tempCRC = decimalToHex(crc16_modbus(hexToBytes(data)));
return tempCRC.substring(2, 4) + tempCRC.substring(0, 2);
}
function makeRandomID(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return bytesToHex(new TextEncoder("utf-8").encode(result));
}
function hex(number, len) {
var str = (number.toString(16)).toUpperCase();
while (str.length < len) str = '0' + str;
return str;
}
function hexToBytes(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return new Uint8Array(bytes);
}
function dump(ar, len) {
let s = '';
for(let i=0; i < len; i++) {
s += hex(ar[i],2);
}
return s;
}
function dump8(ar, len) {
let s = '';
for(let i=0; i < len; i++) {
s += hex(ar.getUint8(i),2);
}
return s;
}
function hex2ascii(hexx) {
var hex = hexx.toString();
var str = '';
for (var i = 0;
(i < hex.length && hex.substr(i, 2) !== '00'); i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}
function updateBegin() {
if (blockCount <= 0) {
showError("Не выбран файл!");
return;
}
setTimeout(function() {
otaCharSend("00ff").then(function(character) {
otaCharSend("01ff").then(function(character) {
setTimeout(function() {
startTime = new Date().getTime();
sendOTAblock(0);
}, 300);
}).catch(function(err) {
updateFail(err);
});
}).catch(function(err) {
updateFail(err);
});
}, 500);
}
function sendOTAblock(blockNr) {
if (blockNr >= blockCount) {
sendLastOTA();
return;
}
showProgress("Передан блок N: " + blockNr + " из " + blockCount + ", " + Math.floor(blockNr / (blockCount * 1.0) * 100) + "% успеха, время " + (new Date().getTime() - startTime) / 1000.0 + "сек");
var blockNrString = getHexBLockCount(blockNr);
var blockString = blockNrString + firmwareArray.substring(blockNr * 32, blockNr * 32 + 32);
var blockCRC = getHexCRC(blockString);
otaCharSend(blockString + blockCRC).then(function(character) {
setTimeout(function() {
if ((blockNr + 1) % 8 == 0) {
otaCharacteristic.readValue().then(function(result) {
console.log('Чтение OTA');
sendOTAblock(blockNr + 1);
}).catch(function(err) {
updateFail(err);
});
} else {
sendOTAblock(blockNr + 1);
}
}, 0);
}).catch(function(err) {
updateFail(err);
});
}
function getHexBLockCount(count) {
var tempHEX = decimalToHex(count);
return tempHEX.substring(2, 4) + tempHEX.substring(0, 2);
}
function sendLastOTA() {
var data = "02ff" + getHexBLockCount(blockCount - 1) + getHexBLockCount(~(blockCount - 1) & 0xffff);
otaCharSend(data).then(function(character) {
showProgress("Прошивка выполнена за " + (new Date().getTime() - startTime) / 1000 + " сек");
}).catch(function(err) {
updateFail(err);
});
}
var otaCharSend = function(data) {
return new Promise(function(resolve, reject) {
// console.log("OTA: " + data);
otaCharacteristic.writeValue(hexToBytes(data)).then(function(character) {
resolve("ok");
}).catch(function(err) {
reject("some error while sending char data");
});
});
}
var mainCharSend = function(data, characteristic) {
return new Promise(function(resolve, reject) {
console.log("Send: " + data);
characteristic.writeValue(hexToBytes(data)).then(function(character) {
resolve("OK");
}).catch(function(err) {
reject("Some error while sending char data");
});
});
}
function readAddr(addr) {
if (myCharacteristic) {
let blk = new Uint8Array([0xdb, addr&0xff, (addr>>8)&0xff, (addr>>16)&0xff, (addr>>24)&0xff]);
myCharacteristic.writeValue(blk).then(_ => {
startTime = new Date().getTime();
showState("Прочитано 16 байт из адреса 0x" + hex(addr,8));
});
}
}
function writeAddr(addr, data) {
if (myCharacteristic) {
len = data.length;
if(len != 0 && len <= 16) {
let blk = new Uint8Array(len + 5);
blk.set([0xdb, addr&0xff, (addr>>8)&0xff, (addr>>16)&0xff, (addr>>24)&0xff]);
blk.set(data, 5);
console.log(blk);
myCharacteristic.writeValue(blk);
showState("Записано " + len + " байт по адресу 0x" + hex(addr,8));
} else {
console.log(data);
showState('Ошибка, длина блока от 1 до 16 байт!');
}
}
}
function infoReadAddr() {
if(myCharacteristic) {
let faddr = parseInt($('inputAddr').value, 16);
readAddr(faddr);
}
}
function writeCmd(data) {
if(myCharacteristic) {
len = data.length;
if(len != 0 && len <= 20) {
let blk = new Uint8Array(data);
console.log(blk);
myCharacteristic.writeValue(blk);
} else {
console.log(data);
showState('Ошибка, длина команды от 1 до 20 байт!');
}
}
}
function sendData() {
let addr = parseInt($('inputAddr').value, 16);
let data = hexToBytes($('inputData').value);
if(data.length != 0 && data.length <= 16)
writeAddr(addr, data);
else
console.log('Ошибка, длина от 1 до 16 байт!');
}
function sendCommand() {
let data = hexToBytes($('inpCmdData').value);
if(data.length != 0 && data.length <= 20)
WriteCmd(data);
else
console.log('Должно быть от 1 до 20 hex байт!');
}
function customBlkParse(value) {
let len = value.byteLength;
if(len == 0) return;
len--; // size from cmd
let blkId = value.getUint8(0);
s = 'Ответ на команду Id: ' + hex(blkId,2) + ' data: ' + bytesToHex(value.buffer.slice(1));
addLog(s);
if((blkId == 0xdb) && (value.byteLength > 4)) {
len -= 4;
let addr = value.getUint32(1,true);
let s = bytesToHex(value.buffer.slice(5), len);
$('inputData').value = s;
addLog(hex(addr,8) + ':' + s);
showProgress("Считано: " + len + " байт из 0x" + hex(addr,8));
} else
console.log('blk: ' + dump8(value, value.byteLength));
}
var url;
function download(data, filename, type) {
var file = new Blob([data], {type: type});
if (window.navigator.msSaveOrOpenBlob) { // ie10+
window.navigator.msSaveOrOpenBlob(file, filename);
} else { // ff, chrome
url = URL.createObjectURL(file);
let a = document.createElement("a");
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function(){document.body.removeChild(a);window.URL.revokeObjectURL(url);},0);
URL.revokeObjectURL(url);
}
}
function readFile(file) {
var reader = new FileReader();
if (file != null) {
reader.readAsArrayBuffer(file);
} else {
showError("Файл не загружен");
}
reader.onload = function() {
// console.log(this.result);
firmwareArray = bytesToHex(this.result);
if(firmwareArray.substring(0,4) != "1234"){
blockCount = 0;
firmwareArray = "";
$("lblFile").innerHTML = "не загружен";
alert("Выбранный файл не для PHY62x2 OTA!");
showError("Выбранный файл не для PHY62x2 OTA!");
return;
}
showState("Файл загружен, размер: " + firmwareArray.length / 2 + " байт");
if (firmwareArray.length % 32 !== 0) { // pad last block to 16bytes
var padHex = "ffffffffffffffffffffffffffffffff";
firmwareArray += padHex.substr(0, 32 - firmwareArray.length % 32);
}
blockCount = firmwareArray.length / 32;
addLog("Блоков: " + blockCount);
}
reader.onerror = function() {
console.log(this.error);
};
}
const selectFile = function() {
let regex = /[^\\]+$/
this.choose,
this.selected
this.msg = str => {
let prefix = '[selectFile]\n\nError: '
return alert(prefix+str)
}
this.check = () => {
if (this.choose && this.selected != null) {
let choose = document.getElementById(this.choose),
selected = document.getElementById(this.selected)
choose.addEventListener('change',() => {
if (choose.value != '') {
selected.innerHTML = choose.value.match(regex);
readFile(choose.files[0]);
}
})
} else {
this.msg('Targets not set.')
}
}
selectFile.prototype.targets = (trigger, filetext) => {
this.choose = trigger
this.selected = filetext
}
selectFile.prototype.simulate = () => {
if (this.choose != null) {
let choose = document.getElementById(this.choose)
if (typeof choose != 'undefined') {
choose.click()
this.check()
} else {
this.msg('Could not find element '+this.choose)
}
} else {
this.msg('Targets not set.')
}
}
};
var getFile = new selectFile;
getFile.targets('inpFile','lblFile');
function openTab(evt, tabName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(tabName).style.display = "block";
evt.currentTarget.className += " active";
console.log(evt.currentTarget.className);
}
window.onload = function() {
$("btnSave").onclick = function() { download(flash_buf, 'ff.bin', 'application/octet-stream;charset=utf-8'); };
}
window.onload = (event) => {
// console.log("page is fully loaded");
showState("Нет подключения" )
openTab(event, 'tabOTA');
// Selected Tab header style
tablinks = document.getElementsByClassName("tablinks");
tablinks[0].className += " active";
};
</script>
<h2>PHY62x2-BTHome <a href="https://github.com/pvvx/THB2"><small>&#9432;</small></a></h2>
<label for="inpNamePrefix">Префикс названия устройств(а)</label><br>
<input type="text" id="inpNamePrefix" value="" placeholder="THB, BT">
<hr>
<label id="lblStatus">Состояние: загрузка страницы</label>
<br><br><hr>
<button type="button" id="btnConnect" onclick="connect()">Соединение</button>
<button type="button" id="btnDisconnect" onclick="disconnect()" disabled="true">Отключение</button>
<button type="button" id="btnReconnect" onclick="reconnect()" disabled="true">Переподключение</button>
<br><hr>
<div class="tab">
<button class="tablinks" onclick="openTab(event, 'tabOTA')">OTA</button>
<button class="tablinks" onclick="openTab(event, 'tabFlash')">Flash</button>
<button class="tablinks" onclick="openTab(event, 'tabService')">Service</button>
</div>
<div id="tabOTA" class="tabcontent">
<p>Файл прошивки:
<input type=file hidden id=inpFile>
<label id=lblFile>не выбран</label>
<button type=button onClick=getFile.simulate()>Выбрать</button>
<p>
<hr>
<button type="button"id="btnStartDFU" onclick="startDFU()" disabled="true" >Старт программирования</button>
</div>
<div id="tabFlash" class="tabcontent">
<p>
<button type="button" id="btnReadFF" onclick="startReadFF()" disabled="true">Прочитать</button>
<button type="button" id="btnSave" disabled="true">Сохранить в файл</button>
<p>
</div>
<div id="tabService" class="tabcontent">
Чтение и запись памяти:<br>
Адрес (hex): <input size="8" type="text" id="inputAddr" value="11000000" maxlength="8">
<button type="button" id="btnReadAddr" onclick="infoReadAddr()" disabled="true">Прочитать</button>
Данные (hex): <input size="32" type="text" id="inputData" value="?" maxlength="32">
<button type="button" id="btnSendData" onclick="sendData()" disabled="true">Записать</button><hr>
<button type="button" id="btnSendCommand" onclick="sendCommand()" disabled="true">Команда</button>
<input type="text" id="inpCmdData" value="55" size="40" maxlength="40"><hr>
</div>
<hr>
<button type="button" onclick="clearLog()">Очистить лог</button><br>
<div id="log"></div>
</body></html>

View file

@ -0,0 +1,300 @@
/* basic sytles */
body {
font-family: Arial, 'Open Sans', sans-serif;
color: #204056;
}
h1 {
font-size: 28px;
font-weight: 400;
text-align: center;
margin-top: 12px;
margin-bottom: 18px;
}
hr {
height: 10px;
border: 0;
box-shadow: 0 10px 10px -10px #8c8b8b inset;
}
span#info {
font-style: italic;
}
.button, [type='button'] {
background-color: #1a73e8;
border: none;
border-radius: 4px;
color: white;
padding: 8px 24px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 6px 6px;
cursor: pointer;
box-shadow: 0 3px 1px -2px #0003, 0 2px 2px #00000024, 0 1px 5px #0000001f;
}
.button, [type='button'].ok {
background-color: #4CAF50; /* Green */
color: white;
border-color: #4CAF50;
}
.button, [type='button'].danger {
background-color: #f44336; /* Red */
color: white;
border-color: #f44336;
}
.button, [type='button']:disabled {
cursor: not-allowed;
opacity: 0.6;
}
input[type="checkbox"] + label {
margin-right: 8px;
}
div#div_v {
height:400px;
margin-top: 16px;
margin-bottom: 16px;
}
div#labdiv {
margin-top: 16px;
margin-bottom: 16px;
}
div#log {
padding: 12px;
font-style: italic;
font-size: 16px;
}
div#MAC {
font-style: smaller;
margin: 8px;
}
div#txtStatus {
font-style: italic;
font-size: 16px;
text-align: center;
background-color: #eef6fc;
padding-top: 5px;
padding-bottom: 5px;
margin-top: 8px;
}
div#tempHumiData{
text-align: center;
background-color: #eef6fc;
padding-top: 5px;
padding-bottom: 5px;
}
input {
padding: 4px;
margin: 4px;
}
select {
padding: 4px;
}
/* menu */
.navbar {
width: 95%;
/* box-shadow: 0 1px 4px rgb(146 161 176 / 15%);*/
position: absolute;
top: 0;
}
.nav-container {
display: flex;
justify-content: space-between;
align-items: center;
height: 62px;
}
.navbar .menu-items {
display: flex;
}
.navbar .nav-container li {
list-style: none;
}
.navbar .nav-container a {
font-size: 1.0rem;
font-weight: 400;
}
.navbar .nav-container a:hover{
font-weight: bolder;
}
.nav-container {
display: block;
position: relative;
height: 60px;
}
.nav-container .checkbox {
position: absolute;
display: block;
height: 32px;
width: 32px;
top: 20px;
left: 20px;
z-index: 5;
opacity: 0;
cursor: pointer;
}
.nav-container .hamburger-lines {
display: block;
height: 26px;
width: 32px;
position: absolute;
top: 17px;
left: 20px;
z-index: 2;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.nav-container .hamburger-lines .line {
display: block;
height: 4px;
width: 100%;
border-radius: 10px;
background: #0e2431;
}
.nav-container .hamburger-lines .line1 {
transform-origin: 0% 0%;
transition: transform 0.4s ease-in-out;
}
.nav-container .hamburger-lines .line2 {
transition: transform 0.2s ease-in-out;
}
.nav-container .hamburger-lines .line3 {
transform-origin: 0% 100%;
transition: transform 0.4s ease-in-out;
}
.navbar .menu-items {
position: relative;
padding-top: 55px;
box-shadow: 5px 3px 13px 0px rgb(204 204 204 / 80%);
min-height: 100vh;
width: 60%;
transform: translate(-150%);
display: flex;
flex-direction: column;
transition: transform 0.5s ease-in-out;
text-align: center;
z-index: 1;
background: white;
}
.navbar .menu-items li {
margin-bottom: 12px;
font-size: 1.2rem;
font-weight: 800;
}
.nav-container input[type="checkbox"]:checked ~ .menu-items {
transform: translateX(0);
}
.nav-container input[type="checkbox"]:checked ~ .hamburger-lines .line1 {
transform: rotate(45deg);
}
.nav-container input[type="checkbox"]:checked ~ .hamburger-lines .line2 {
transform: scaleY(0);
}
.nav-container input[type="checkbox"]:checked ~ .hamburger-lines .line3 {
transform: rotate(-45deg);
}
.nav-container input[type="checkbox"]:checked ~ .logo{
display: none;
}
.shadowbox {
width: 15em;
border: 1px solid #333;
box-shadow: 8px 8px 5px #444;
padding: 8px 12px;
background-image: linear-gradient(180deg, #fff, #ddd 40%, #ccc);
}
.shadowprogress {
width: 15em;
border: 1px solid #333;
/* box-shadow: 8px 8px 5px #444; */
padding: 8px 12px;
/* background-image: linear-gradient(180deg, #fff, #ddd 40%, #ccc); */
}
.shadowerror {
color: red;
width: 15em;
border: 1px solid #333;
box-shadow: 8px 8px 5px #444;
padding: 8px 12px;
background-image: linear-gradient(180deg, #fff, #ddd 40%, #ccc);
}
/* Style the tab */
.tab {
overflow: hidden;
border: 1px solid #ccc;
background-color: #f1f1f1;
}
/* Style the buttons inside the tab */
.tab button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
font-size: 17px;
}
/* Change background color of buttons on hover */
.tab button:hover {
background-color: #ddd;
}
/* Create an active/current tablink class */
.tab button.active {
background-color: #ccc;
}
/* Style the tab content */
.tabcontent {
display: none;
padding: 6px 12px;
border: 1px solid #ccc;
border-top: none;
}