update PHY62x2BTHome.html

This commit is contained in:
pvvx 2024-01-27 21:26:47 +03:00
parent 00013d3da7
commit ceffb41989
6 changed files with 572 additions and 1008 deletions

View file

@ -1,7 +1,308 @@
<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 v0.1</title>
<style type="text/css">
/* 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;
}
</style>
<title>PHY62x2-BTHome v0.2</title>
</head>
<body>
<script>
@ -15,7 +316,7 @@ var devcfg = {};
var devcfs = {};
var startTime = 0,
connected = false;
isConnected = false;
var ota = {
fwArray: null,
@ -28,7 +329,7 @@ var ota = {
};
//Connection values
var connectTrys = 0;
var connectRetries = 0;
var $ = function(id) { return document.getElementById(id);}
@ -47,14 +348,15 @@ function resetVariables() {
function handleError(text) {
showError(text);
resetVariables();
if (connectTrys < 5) {
connectTrys++;
addLog("Переподключение " + connectTrys + " из " + 5);
if (connectRetries < 5) {
connectRetries++;
addLog("Переподключение " + connectRetries + " из " + 5);
doConnect();
} else {
addLog("Подключиться не удалось!");
connectTrys = 0;
disconnect();
connectRetries = 0;
// disconnect();
$('btnConnect').disabled = false;
}
}
@ -78,19 +380,25 @@ function connect() {
resetVariables();
$('btnReconnect').disabled = true;
showState("Поиск устройств");
connectTrys = 0;
navigator.bluetooth.requestDevice(deviceOptions).then(device => {
bluetoothDevice = device;
bluetoothDevice.addEventListener('gattserverdisconnected', onDisconnected);
if(typeof(navigator.bluetooth) != "undefined") {
$('btnConnect').disabled = true;
showState("Поиск устройств");
connectRetries = 0;
//console.log()
navigator.bluetooth.requestDevice(deviceOptions).then(device => {
bluetoothDevice = device;
bluetoothDevice.addEventListener('gattserverdisconnected', onDisconnected);
//addLog("Connecting to: " + bluetoothDevice.name);
doConnect();
}).catch(handleError);
doConnect();
}).catch(handleError);
} else {
showError("Browser doesn't support BLE API");
}
}
function disconnect() {
addLog("Отключение");
connected = false;
isConnected = false;
if (bluetoothDevice != null)
bluetoothDevice.gatt.disconnect();
}
@ -98,10 +406,10 @@ function disconnect() {
function reconnect() {
addLog("Переподключение");
if (bluetoothDevice != null) {
connected = false;
bluetoothDevice.gatt.disconnect();
isConnected = false;
}
connectTrys = 0;
connectRetries = 0;
doConnect();
}
@ -196,7 +504,8 @@ function phyConnect(info_flg) {
return linkOta();
}).then(_ => {
showState("Устройство подключено.");
connected = true;
isConnected = true;
// $('btnConnect').disabled = true;
$('btnDisconnect').disabled = false;
$('btnReconnect').disabled = false;
$('btnSendCommand').disabled = false;
@ -204,11 +513,13 @@ function phyConnect(info_flg) {
$('btnSendData').disabled = false;
if (otaCharacteristic != null && ota.fwArray != null)
$('btnStartDFU').disabled = false;
selectConfigTab();
$('tabConfig').style.display = "block";
}).catch(handleError);
}
function doConnect() {
connected = false;
isConnected = false;
showState("Ожидание соединения с " + bluetoothDevice.name)
return bluetoothDevice.gatt.connect().then(server => {
console.log("Найден GATT сервер");
@ -225,18 +536,20 @@ function doConnect() {
}
if(phy)
return phyConnect(true);
addLog("Выбрано не то устройтво!");
addLog("Выбрано неверное устройтво!");
bluetoothDevice.gatt.disconnect();
connectTrys = 10;
connectRetries = 10;
return null;
}).catch(handleError);
}).catch(handleError);
}
function onDisconnected() {
connected = false;
isConnected = false;
resetVariables();
showState('Устройство отключено.');
$('btnConnect').disabled = false;
$('tabConfig').style.display = "none";
}
function startDFU() {
@ -253,13 +566,13 @@ function addLog(logTXT) {
function showState(text) {
let s = "Состояние: " + text;
document.getElementById("lblStatus").className = "shadowbox";
$("lblStatus").className = "shadowbox";
$("lblStatus").innerHTML = s;
addLog(text);
}
function showProgress(text) {
document.getElementById("lblStatus").className = "shadowprogress";
$("lblStatus").className = "shadowprogress";
$("lblStatus").innerHTML = text;
}
@ -270,7 +583,7 @@ function clearLog() {
function showError(text) {
// console.log("Status: " + status);
let s = "Ошибка: " + text;
document.getElementById("lblStatus").className = "shadowerror";
$("lblStatus").className = "shadowerror";
$("lblStatus").innerHTML = s;
addLog(text);
}
@ -446,7 +759,7 @@ function getFwArray(data, filename) {
ota.blockCount = ota.fwArray.length / 32;
addLog("Счетчик: " + ota.blockCount + " блоков");
ota.fwname = filename;
if(connected)
if(isConnected)
$('btnStartDFU').disabled = false;
}
@ -672,28 +985,70 @@ function customBlkParse(value) {
devsrs.dev_spec_data = value.getUint16(6, true);
devsrs.services = value.getUint32(8, true);
addLog("Dev info # hw: "+hex(devsrs.hw_version,4)+", sw: "+hex(devsrs.sw_version,4)+", services: "+hex(devsrs.services,8)+", sd: "+hex(devsrs.dev_spec_data, 4));
} else if(blkId == 0x35){
if(len >= 12) {
let cnt = value.getUint16(1, true);
let tc = value.getUint32(3, true);
let tm = value.getInt16(7, true) / 100.0;
let hm = value.getUint16(9, true) / 100.0;
let vb = value.getUint16(11, true);
let dt = new Date(tc*1000);
console.log(((dt.toISOString().slice(0, -1)).replace('T',' ')).replace('.000','')+' # Батарея: '+vb+' мВ , Температура: '+tm+'°C, Влажность: '+hm+'%, Счетчик: '+cnt);
} else if(len == 3) {
let flg = value.getUint16(1, true);
console.log('Последний блок: '+flg);
flg_memo_act = false;
} else if(len == 2) {
addLog('Нет сервиса записи истории!');
} else
console.log('blk: ' + dump8(value, value.byteLength));
} else if((blkId == 0x25 || blkId == 0x26) && (len > 12)) {
// CMD_ID_CFS Get/Set sensor config
devcfs.temp_k = value.getUint32(1, true);
devcfs.humi_k = value.getUint32(5, true);
devcfs.temp_z = value.getInt16(9, true);
devcfs.humi_z = value.getInt16(11, true);
$('inputTempK').value = devcfs.temp_k;
$('inputHumK').value = devcfs.humi_k;
$('inputTempZ').value = devcfs.temp_z;
$('inputHumZ').value = devcfs.humi_z;
addLog("Dev sensor # tk: "+devcfs.temp_k+", hk: "+devcfs.humi_k+", tz: "+devcfs.temp_z+", hz: "+devcfs.humi_z);
if (len > 17) {
devcfs.mid = value.getUint16(13, false);
devcfs.vid = value.getUint16(15, false);
devcfs.i2c_addr = value.getUint8(17);
s = "???";
if(devcfs.mid == 0x5959)
s = "CHT"+hex(devcfs.vid,4);
else if(devcfs.mid == 0 && devcfs.vid == 0xaaaa)
s = "AHT20";
$('txtSensor').innerHTML = s+', I2C адрес: 0x'+hex(devcfs.i2c_addr,2);
addLog("Dev sensor # id: "+hex(devcfs.mid,4)+"-"+hex(devcfs.vid,4)+", i2c_addr: 0x"+hex(devcfs.i2c_addr,2));
}
} else if((blkId == 0x55 || blkId == 0x56) && (len > 7)) {
} else if((blkId == 0x55 || blkId == 0x56) && (len > 12)) {
// CMD_ID_CFG Get/Set device config
devcfg.flg = value.getUint32(1, true);
devcfg.rf_tx_power = value.getUint8(2);
devcfg.advertising_interval = value.getUint8(3);
devcfg.connect_latency = value.getUint8(4);
devcfg.measure_interval = value.getUint8(5);
devcfg.batt_interval = value.getUint8(6);
devcfg.averaging_measurements = value.getUint8(7);
devcfg.rf_tx_power = value.getUint8(5);
devcfg.advertising_interval = value.getUint8(6);
devcfg.connect_latency = value.getUint8(7);
devcfg.reserved1 = value.getUint8(8);
devcfg.measure_interval = value.getUint8(9);
devcfg.batt_interval = value.getUint8(10);
devcfg.averaging_measurements = value.getUint8(11);
devcfg.reserved2 = value.getUint8(12);
$('inputFlag').value = devcfg.flg; // пока нет
$('inputTxPwr').value = devcfg.rf_tx_power; // 0..0x3f -> -20..+5 dBm нелинейное 0x1f = +0 дБм
$('inputLat').value = (devcfg.connect_latency + 1)*30; // = (connect_latency + 1)*30 ms
$('inputAdvInt').value = devcfg.advertising_interval*62.5; // *62.5 = интервала реклам в ms
$('inputMeasInt').value = devcfg.measure_interval; // *devcfg.advertising_interval*62.5 = опрос датчика в ms, value минимум = 2 (интервала рекламы)
$('inputAverInt').value = devcfg.averaging_measurements; // запись истории: при 0 - отключена, 1...255 * шаг опроса датчика = интерал записи истории
$('inputBatInt').value = devcfg.batt_interval; // в секундах, минимум 2 секунды, но кратно интервалу рекламы
addLog("Dev config # flg: "+hex(devcfg.flg,8)+", tx: "+devcfg.rf_tx_power+", adi: "+devcfg.advertising_interval+", cly: "+devcfg.connect_latency+", msi: "+devcfg.measure_interval+", bti: "+devcfg.batt_interval+", avi: "+devcfg.averaging_measurements);
} else if((blkId == 0xdb || blkId == 0xda) && (len > 4)) {
// CMD_ID_MEM_RW Read/Write memory
@ -716,6 +1071,87 @@ function customBlkParse(value) {
}
}
function GetDevCfg() {
if(cmdCharacteristic != null) {
//addLog("GetDevCfg...");
cmdCharacteristic.writeValue(new Uint8Array([0x55])).catch(error => { console.log(error); addLog("GetDevCfg error!") });
}
}
function GetSensCfg() {
if(cmdCharacteristic != null) {
//addLog("GetSensCfg...");
cmdCharacteristic.writeValue(new Uint8Array([0x25])).catch(error => { console.log(error); addLog("GetSensCfg error!"); });
}
}
function SetDevCfg() {
if(cmdCharacteristic != null) {
// addLog("SetDevCfg...");
devcfg.flg = parseInt($('inputFlag').value)&0xffffffff; // пока нет
devcfg.rf_tx_power = parseInt($('inputTxPwr').value)&0x3f; // 0..0x3f -> -20..+5 dBm нелинейное 0x1f = +0 дБм
let connect_latency = parseInt($('inputLat').value); // = (connect_latency + 1)*30 ms
if (connect_latency < 0) {
connect_latency = 0;
} else if (connect_latency <= 7680) {
connect_latency = (connect_latency/30) - 1;
} else
connect_latency = 255;
devcfg.connect_latency = connect_latency & 0xff;
let advertising_interval = parseInt($('inputAdvInt').value);
if(advertising_interval <= 62.5)
advertising_interval = 1;
else if(advertising_interval >= 15937.5)
advertising_interval = 255;
else
advertising_interval = advertising_interval/62.5;
devcfg.advertising_interval = advertising_interval & 0xff;
devcfg.measure_interval = parseInt($('inputMeasInt').value);
if(devcfg.measure_interval < 2) // опрос датчика в интервалах рекламы, value минимум = 2 (интервала рекламы)
devcfg.measure_interval = 2;
else if(devcfg.measure_interval > 255)
devcfg.measure_interval = 255
devcfg.averaging_measurements = parseInt($('inputAverInt').value);
if(devcfg.averaging_measurements < 0) // запись истории: при 0 - отключена, 1...255 * шаг опроса датчика = интерал записи истории
devcfg.averaging_measurements = 0;
if(devcfg.averaging_measurements > 255)
devcfg.averaging_measurements = 255;
devcfg.batt_interval = parseInt($('inputBatInt').value);
if(devcfg.batt_interval < 2) // в секундах, минимум 2 секунды, но кратно интервалу рекламы
devcfg.batt_interval = 2;
if(devcfg.batt_interval > 255)
devcfg.batt_interval = 255;
blk = new Uint8Array([0x55,
devcfg.flg & 0xff, (devcfg.flg>>8) & 0xff, (devcfg.flg>>16) & 0xff, (devcfg.flg>>24) & 0xff,
devcfg.rf_tx_power,
devcfg.advertising_interval,
devcfg.connect_latency,
devcfg.reserved1,
devcfg.measure_interval,
devcfg.batt_interval,
devcfg.averaging_measurements,
devcfg.reserved2
]);
cmdCharacteristic.writeValue(blk).catch(error => { console.log(error); addLog("SetDevCfg error!");});
}
}
function SetSensCfg() {
if(cmdCharacteristic != null) {
addLog("SetSensCfg...");
devcfs.temp_k = parseInt($('inputTempK').value);
devcfs.humi_k = parseInt($('inputHumK').value);
devcfs.temp_z = parseInt($('inputTempZ').value);
devcfs.humi_z = parseInt($('inputHumZ').value);
blk = new Uint8Array([0x55,
devcfs.temp_k & 0xff, (devcfs.temp_k >> 8) & 0xff, (devcfs.temp_k >> 16) & 0xff, (devcfs.temp_k >> 24) & 0xff,
devcfs.humi_k & 0xff, (devcfs.humi_k >> 8) & 0xff, (devcfs.humi_k >> 16) & 0xff, (devcfs.humi_k >> 24) & 0xff,
devcfs.temp_z & 0xff, (devcfs.temp_z >> 8) & 0xff,
devcfs.humi_z & 0xff, (devcfs.humi_z >> 8) & 0xff
]);
cmdCharacteristic.writeValue(new Uint8Array([0x25])).catch(error => { console.log(error); addLog("SetSensCfg error!"); });
}
}
var url;
function download(data, filename, type) {
var file = new Blob([data], {type: type});
@ -747,8 +1183,8 @@ const selectFile = function() {
this.check = () => {
if (this.choose && this.selected != null) {
let choose = document.getElementById(this.choose),
selected = document.getElementById(this.selected)
let choose = $(this.choose),
selected = $(this.selected)
choose.addEventListener('change',() => {
if (choose.value != '') {
selected.innerHTML = choose.value.match(regex);
@ -767,7 +1203,7 @@ const selectFile = function() {
selectFile.prototype.simulate = () => {
if (this.choose != null) {
let choose = document.getElementById(this.choose)
let choose = $(this.choose)
if (typeof choose != 'undefined') {
choose.click()
this.check()
@ -814,18 +1250,23 @@ function openTab(evt, tabName) {
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);
$(tabName).style.display = "block";
if (!isConnected) $('tabConfig').style.display = "none";
if (evt != 0) {
evt.currentTarget.className += " active";
// console.log(evt.currentTarget.className);
}
}
function selectConfigTab() {
openTab(0, "tabConfig");
tablinks = document.getElementsByClassName("tablinks");
tablinks[0].className += " active";
}
window.onload = (event) => {
showState("Нет подключения" )
openTab(event, 'tabOTA');
// Selected Tab header style
tablinks = document.getElementsByClassName("tablinks");
tablinks[0].className += " active";
showState("Нет подключения" );
selectConfigTab();
};
</script>
@ -839,12 +1280,96 @@ window.onload = (event) => {
<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, 'tabConfig')">Config</button>
<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="tabConfig" class="tabcontent">
Параметры связи<br>
<!--
Флаг: <input size="8" type="text" id="inputFlag" maxlength="8">
Tx Power: <input size="8" type="text" id="inputTxPwr" maxlength="8">
Conn. Latency: <input size="8" type="text" id="inputLat" maxlength="8">
<br>
Интервал рекламы: <input size="8" type="text" id="inputAdvInt" maxlength="8">
Интервал измерений: <input size="8" type="text" id="inputMeasInt" maxlength="8">
Интервал опроса батареи: <input size="8" type="text" id="inputMeasBat" maxlength="8">
Интервал усреднения: <input size="8" type="text" id="inputAverInt" maxlength="8">
<br> -->
<table>
<tr>
<th>Флаг</th>
<th>Tx Power</th>
<th>Conn. Latency</th>
</tr>
<tr>
<td><input size="8" type="text" id="inputFlag" maxlength="8"></td>
<td><input size="4" type="text" id="inputTxPwr" maxlength="4"></td>
<td><input size="4" type="text" id="inputLat" maxlength="4"></td>
</tr>
</table>
Интервалы
<table>
<tr>
<th>Реклама</th>
<th>Измерения</th>
<th>Усреднение</th>
<th>Уровень батареи</th>
</tr>
<tr>
<td><input size="4" type="text" id="inputAdvInt" maxlength="4"></td>
<td><input size="4" type="text" id="inputMeasInt" maxlength="4"></td>
<td><input size="4" type="text" id="inputAverInt" maxlength="4"></td>
<td><input size="4" type="text" id="inputBatInt" maxlength="4"></td>
</tr>
</table>
<button type="button"id="btnGetDev" onclick="GetDevCfg()" >Прочитать</button>
<button type="button"id="btnSetDev" onclick="SetDevCfg()" >Записать</button>
<hr>
<table>
<tr>
<th>Параметры сенсора:</th>
<th>Температура</th>
<th>Влажность</th>
</tr>
<tr>
<th><div id="txtSensor">?</div></th>
</tr>
<tr>
<td>Поправочные коэффициенты</td>
<td><input size="8" type="text" id="inputTempK" maxlength="8"></td>
<td><input size="8" type="text" id="inputHumK" maxlength="8"></td>
</tr>
<tr>
<td>Коррекция смещения</td>
<td><input size="8" type="text" id="inputTempZ" maxlength="8"></td>
<td><input size="8" type="text" id="inputHumZ" maxlength="8"></td>
</tr>
</table>
<br>
<!--
Поправочные коэффициенты:
Температуры: <input size="8" type="text" id="inputTempK" maxlength="8">
Влажности:
<br>
Коррекция смещения:
Температуры: <input size="8" type="text" id="inputTempZ" maxlength="8">
Влажности: <input size="8" type="text" id="inputHumZ" maxlength="8">
<br>
Параметры I2C<br>
Vendor: <input size="4" type="text" id="inputI2CVen" maxlength="4">
Model: <input size="4" type="text" id="inputI2CMod" maxlength="4">
Address: <input size="2" type="text" id="inputI2CAddr" maxlength="2">
<br> -->
<button type="button"id="btnGetSens" onclick="GetSensCfg()" >Прочитать</button>
<button type="button"id="btnSetSens" onclick="SetSensCfg()" >Записать</button>
</div>
<div id="tabOTA" class="tabcontent">
<p>Файл прошивки:
<input type=file hidden id=inpFile>

View file

@ -1,621 +0,0 @@
<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

@ -1,40 +0,0 @@
01ff 50485936 593601000000200100111001000020
01ff 01000000 50485936 593601000000200100111001000020
01ff 20010011 50485936 0100
01ff 20010011 50485936 0010
01ff 20010011 50485936 1200
ffffffff010000002000011110010000
0000504859360100000020000111100100004388
Ñòàðò DFU
OTA send: 00ff
OTA read: ver: 01, err: 0, dbg: 0, start: 0, offs: 0x11010020, idx: 0x0010, total: 0x0011, crc: 0xC012FD5E
OTA send: 01ff20000111504859361200
OTA read: ver: 01, err: 0, dbg: 0, start: 1, offs: 0x11010020, idx: 0xFFFF, total: 0x0012, crc: 0xC012FD5E
OTA send: 0000504859360100000020000111100100004388
OTA read: ver: 01, err: 0, dbg: 0, start: 1, offs: 0x11010020, idx: 0x0000, total: 0x0012, crc: 0x557AB31D
OTA send: 010020010000f00000002000011100000000440b
OTA read: ver: 01, err: 0, dbg: 0, start: 1, offs: 0x11010020, idx: 0x0001, total: 0x0012, crc: 0xD52EEB28
OTA send: 020000000000000000000000000000000000d05c
OTA read: ver: 01, err: 0, dbg: 0, start: 1, offs: 0x11010020, idx: 0x0002, total: 0x0012, crc: 0x04517BB9
OTA send: 03000000000000000000000000000000000081a0
OTA read: ver: 01, err: 0, dbg: 0, start: 1, offs: 0x11010020, idx: 0x0003, total: 0x0012, crc: 0xD28B9AE6
OTA send: 04000000000000000000000000000000000030d5
OTA read: ver: 01, err: 0, dbg: 0, start: 1, offs: 0x11010020, idx: 0x0004, total: 0x0012, crc: 0x5E306857
OTA send: 0500000000000000000000000000000000006129
OTA read: ver: 01, err: 0, dbg: 0, start: 1, offs: 0x11010020, idx: 0x0005, total: 0x0012, crc: 0xE0ADC600
OTA send: 060000000000000000000000000000000000916d
OTA read: ver: 01, err: 0, dbg: 0, start: 1, offs: 0x11010020, idx: 0x0006, total: 0x0012, crc: 0xF1F2471C
29:29: Ïîèñê óñòðîéñòâ
29:31: Connecting to: BTH01-31BDBC
29:31: OTA: FF010100200001111100120050485936E5394CC0
29:31: DevCfg: 000000001F501D000206B400
29:31: Óñòðîéñòâî ïîäêëþ÷åíî.
29:33: Ôàéë:
29:33: PHY6 OTA ID: 36594850, Ñåãìåíòîâ: 1, Ñòàðò: 0x11010000, Ðàçìåð: 20304 áàéò
29:33: Ðàçìåð ôàéëà: 20308 áàéò
29:33: Ñ÷åò÷èê: 1270
29:46: Ñòàðò ïðîãðàììèðîâàíèÿ...
31:56: Ïðîãðàììèðîâàíèå çàâåðøåíî çà 129.404 ñåêóíä

View file

@ -1,300 +0,0 @@
/* 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;
}

Binary file not shown.

Binary file not shown.