sync prod -> git
4662
config.json
|
|
@ -7,14 +7,59 @@ body {
|
|||
height: 100vh;
|
||||
}
|
||||
|
||||
body:has([v-cloak]):after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: calc(50% - 50px);
|
||||
left: calc(50% - 50px);
|
||||
width: 50px;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 50%;
|
||||
border: 8px solid #f0f0f0;
|
||||
animation:
|
||||
l20-1 0.8s infinite linear alternate,
|
||||
l20-2 1.6s infinite linear;
|
||||
}
|
||||
@keyframes l20-1{
|
||||
0% {clip-path: polygon(50% 50%,0 0, 50% 0%, 50% 0%, 50% 0%, 50% 0%, 50% 0% )}
|
||||
12.5% {clip-path: polygon(50% 50%,0 0, 50% 0%, 100% 0%, 100% 0%, 100% 0%, 100% 0% )}
|
||||
25% {clip-path: polygon(50% 50%,0 0, 50% 0%, 100% 0%, 100% 100%, 100% 100%, 100% 100% )}
|
||||
50% {clip-path: polygon(50% 50%,0 0, 50% 0%, 100% 0%, 100% 100%, 50% 100%, 0% 100% )}
|
||||
62.5% {clip-path: polygon(50% 50%,100% 0, 100% 0%, 100% 0%, 100% 100%, 50% 100%, 0% 100% )}
|
||||
75% {clip-path: polygon(50% 50%,100% 100%, 100% 100%, 100% 100%, 100% 100%, 50% 100%, 0% 100% )}
|
||||
100% {clip-path: polygon(50% 50%,50% 100%, 50% 100%, 50% 100%, 50% 100%, 50% 100%, 0% 100% )}
|
||||
}
|
||||
@keyframes l20-2{
|
||||
0% {transform:scaleY(1) rotate(0deg)}
|
||||
49.99%{transform:scaleY(1) rotate(135deg)}
|
||||
50% {transform:scaleY(-1) rotate(0deg)}
|
||||
100% {transform:scaleY(-1) rotate(-135deg)}
|
||||
}
|
||||
|
||||
#app {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#app h5.logo-top img {
|
||||
height: 16px;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
#app h5.logo-top span {
|
||||
font-style: italic; font-weight: bold; line-height: 0.9
|
||||
}
|
||||
|
||||
#app select {
|
||||
cursor: pointer
|
||||
}
|
||||
#app .tooltip:has(>.device) {
|
||||
background: #f0f0f055;
|
||||
width: auto;
|
||||
}
|
||||
#app img.device {
|
||||
width: 300px;
|
||||
width: 400px;
|
||||
max-width: 400px;
|
||||
max-height: 400px;
|
||||
}
|
||||
#app div.autoscroller {
|
||||
overflow: auto;
|
||||
|
|
@ -62,4 +107,23 @@ body {
|
|||
}
|
||||
#app .version div.field {
|
||||
min-width: 140px;
|
||||
align-self: flex-start;
|
||||
}
|
||||
#app .version .pre {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
#app img.icon {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.flash-container {
|
||||
padding-bottom: 150px;
|
||||
}
|
||||
|
||||
.strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.text-wrap {
|
||||
text-wrap: auto;
|
||||
}
|
||||
270
flasher.js
|
|
@ -41,6 +41,10 @@ const commandReference = {
|
|||
'get lon': 'Get the advertisement map longitude',
|
||||
};
|
||||
|
||||
async function delay(milis) {
|
||||
return await new Promise((resolve) => setTimeout(resolve, milis));
|
||||
}
|
||||
|
||||
function getGithubReleases(roleType, files) {
|
||||
const versions = {};
|
||||
for(const [fileType, matchRE] of Object.entries(files)) {
|
||||
|
|
@ -78,25 +82,62 @@ function addGithubFiles() {
|
|||
}
|
||||
}
|
||||
|
||||
config.device = config.device.filter(device => device.firmware.some(firmware => Object.keys(firmware.version).length > 0 ));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
async function digestMessage(message) {
|
||||
const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array
|
||||
const hashBuffer = await window.crypto.subtle.digest("SHA-256", msgUint8); // hash the message
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
|
||||
|
||||
const hashHex = hashArray
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join(""); // convert bytes to hex string
|
||||
|
||||
return hashHex;
|
||||
}
|
||||
|
||||
async function blobToBinaryString(blob) {
|
||||
const bytes = new Uint8Array(await blob.arrayBuffer())
|
||||
let binString = '';
|
||||
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
binString += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
|
||||
return binString;
|
||||
}
|
||||
|
||||
console.log(addGithubFiles());
|
||||
|
||||
function setup() {
|
||||
const consoleEditBox = ref();
|
||||
const consoleWindow = ref();
|
||||
|
||||
const deviceFilterText = ref('');
|
||||
|
||||
const snackbar = reactive({
|
||||
text: '',
|
||||
class: '',
|
||||
icon: '',
|
||||
});
|
||||
|
||||
const selected = reactive({
|
||||
device: null,
|
||||
firmware: null,
|
||||
version: null,
|
||||
wipe: false,
|
||||
nrfEraserFlashingPercent: 0,
|
||||
nrfEraserFlashing: false,
|
||||
port: null,
|
||||
});
|
||||
|
||||
const getRoleFwValue = (firmware, key) => {
|
||||
return firmware[key] || config.role[firmware.role][key] || '';
|
||||
const role = config.role[firmware.role] ?? {};
|
||||
|
||||
return firmware[key] ?? role[key] ?? '';
|
||||
}
|
||||
|
||||
const getSelFwValue = (key) => {
|
||||
|
|
@ -108,8 +149,8 @@ function setup() {
|
|||
const flashing = reactive({
|
||||
supported: 'Serial' in window,
|
||||
instance: null,
|
||||
active: false,
|
||||
percentage: 0,
|
||||
locked: false,
|
||||
percent: 0,
|
||||
log: '',
|
||||
error: '',
|
||||
dfuComplete: false,
|
||||
|
|
@ -130,8 +171,20 @@ function setup() {
|
|||
writeLine(data) { flashing.log += data + '\n' }
|
||||
};
|
||||
|
||||
const refresh = () => {
|
||||
location.reload();
|
||||
const retry = async() => {
|
||||
flashing.active = false;
|
||||
flashing.log = '';
|
||||
flashing.error = '';
|
||||
flashing.dfuComplete = false;
|
||||
flashing.percent = 0;
|
||||
if(flashing.instance instanceof ESPLoader) {
|
||||
await flashing.instance?.hr.reset();
|
||||
await flashing.instance?.transport?.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
location.reload()
|
||||
}
|
||||
|
||||
const getFirmwarePath = (file) => {
|
||||
|
|
@ -172,15 +225,25 @@ function setup() {
|
|||
flashing.log = '';
|
||||
flashing.error = '';
|
||||
flashing.dfuComplete = false;
|
||||
flashing.percentage = 0;
|
||||
flashing.percent = 0;
|
||||
selected.firmware = null;
|
||||
selected.version = null;
|
||||
selected.wipe = false;
|
||||
selected.device = null;
|
||||
selected.nrfEraserFlashingPercent = 0;
|
||||
selected.nrfEraserFlashing = false;
|
||||
if(flashing.instance instanceof ESPLoader) {
|
||||
await flashing.instance?.hr.reset();
|
||||
await flashing.instance?.transport?.disconnect();
|
||||
}
|
||||
else if(flashing.instance instanceof Dfu) {
|
||||
try {
|
||||
flashing.instance.port.close()
|
||||
}
|
||||
catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
flashing.instance = null;
|
||||
}
|
||||
|
||||
|
|
@ -191,12 +254,12 @@ function setup() {
|
|||
const openSerialCon = async() => {
|
||||
const port = selected.port = await navigator.serial.requestPort();
|
||||
const serialConsole = serialCon.instance = new SerialConsole(port);
|
||||
serialCon.content = 'Welcome to MeshCore serial console.\n'
|
||||
serialCon.content += '-------------------------------------------------------------------------\n';
|
||||
serialCon.content += 'WARNING: This console only works with *Repeater* and *Room server* roles.\n';
|
||||
serialCon.content += '-------------------------------------------------------------------------\n';
|
||||
serialCon.content += 'If you came here right after flashing, please restart your device.\n';
|
||||
serialCon.content += 'Click on the cursor to get all supported commands.\n\n';
|
||||
|
||||
serialCon.content = '-------------------------------------------------------------------------\n';
|
||||
serialCon.content += 'Welcome to MeshCore serial console.\n'
|
||||
serialCon.content += 'Click on the cursor to get all supported commands.\n';
|
||||
serialCon.content += '-------------------------------------------------------------------------\n\n';
|
||||
|
||||
serialConsole.onOutput = (text) => {
|
||||
serialCon.content += text;
|
||||
};
|
||||
|
|
@ -227,10 +290,19 @@ function setup() {
|
|||
const customFirmwareLoad = async(ev) => {
|
||||
const firmwareFile = ev.target.files[0];
|
||||
const type = firmwareFile.name.endsWith('.bin') ? 'esp32' : 'nrf52';
|
||||
selected.device = {
|
||||
selected.device = {
|
||||
name: 'Custom device',
|
||||
type,
|
||||
};
|
||||
if(firmwareFile.name.endsWith('-merged.bin')) {
|
||||
alert(
|
||||
'You selected custom file that ends with "merged.bin".'+
|
||||
'This will erase your flash! Proceed with caution.'+
|
||||
'If you want just to update your firmware, please use non-merged bin.'
|
||||
);
|
||||
|
||||
selected.wipe = true;
|
||||
}
|
||||
|
||||
selected.firmware = {
|
||||
icon: 'unknown_document',
|
||||
|
|
@ -244,55 +316,100 @@ function setup() {
|
|||
}
|
||||
}
|
||||
|
||||
const espReset = async(t) => {
|
||||
await t.setRTS(true);
|
||||
await delay(100)
|
||||
await t.setRTS(false);
|
||||
}
|
||||
|
||||
const nrfErase = async() => {
|
||||
if(!(selected.device.type === 'nrf52' && selected.device.erase)) {
|
||||
console.error('nRF erase called for non-nrf device or device.erase is not defined')
|
||||
return;
|
||||
}
|
||||
|
||||
const url = `${config.staticPath}/${selected.device.erase}`;
|
||||
|
||||
console.log('downloading: ' + url);
|
||||
const resp = await fetch(url);
|
||||
if(resp.status !== 200) {
|
||||
alert(`Could not download the firmware file from the server, reported: HTTP ${resp.status}.\nPlease try again.`)
|
||||
return;
|
||||
}
|
||||
const flashData = await resp.blob();
|
||||
|
||||
const port = selected.port = await navigator.serial.requestPort({});
|
||||
const dfu = new Dfu(port);
|
||||
|
||||
try {
|
||||
selected.nrfEraserFlashing = true;
|
||||
await dfu.dfuUpdate(flashData, async (progress) => {
|
||||
selected.nrfEraserFlashingPercent = progress;
|
||||
if(progress === 100 && selected.nrfEraserFlashing) {
|
||||
selected.nrfEraserFlashing = false;
|
||||
selected.dfuComplete = false;
|
||||
setTimeout(() => {
|
||||
alert('Device erase firmware has been flashed and flash has been erased.\nYou can flash MeshCore now.');
|
||||
}, 200);
|
||||
}
|
||||
}, 60000);
|
||||
|
||||
}
|
||||
catch(e) {
|
||||
alert(`nRF flashing erase firmware failed: ${e}.\nDid you put the device into DFU mode before attempting erasing?`);
|
||||
selected.nrfEraserFlashing = false;
|
||||
selected.nrfEraserFlashingPercent = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const canFlash = (device) => {
|
||||
return device.type !== 'noflash'
|
||||
}
|
||||
|
||||
const flashDevice = async() => {
|
||||
const device = selected.device;
|
||||
const firmware = selected.firmware.version[selected.version];
|
||||
let flashFile;
|
||||
|
||||
flashFile = firmware.files.find(f => f.type === 'flash');
|
||||
if(!flashFile) {
|
||||
const flashFiles = firmware.files.filter(f => f.type.startsWith('flash'));
|
||||
if(!flashFiles[0]) {
|
||||
alert('Cannot find configuration for flash file! please report this to Discord.')
|
||||
flasherCleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log({flashFile, instanceFile: flashFile instanceof File});
|
||||
console.log({flashFiles});
|
||||
|
||||
if(flashFile.file) {
|
||||
flashFile = flashFile.file;
|
||||
let flashData;
|
||||
if(flashFiles[0].file) {
|
||||
flashData = flashFiles[0].file;
|
||||
} else {
|
||||
let flashFile;
|
||||
if(device.type === 'esp32') {
|
||||
flashFile = flashFiles.find(f => f.type === (selected.wipe ? 'flash-wipe' : 'flash-update'));
|
||||
}
|
||||
else {
|
||||
flashFile = flashFiles[0];
|
||||
}
|
||||
console.log({flashFiles, flashFile});
|
||||
|
||||
const url = getFirmwarePath(flashFile);
|
||||
console.log('downloading: ' + url);
|
||||
const resp = await fetch(url);
|
||||
flashFile = await resp.blob();
|
||||
if(resp.status !== 200) {
|
||||
alert(`Could not download the firmware file from the server, reported: HTTP ${resp.status}.\nPlease try again.`)
|
||||
return;
|
||||
}
|
||||
|
||||
flashData = await resp.blob();
|
||||
}
|
||||
|
||||
const port = selected.port = await navigator.serial.requestPort({});
|
||||
|
||||
if(device.type === 'esp32') {
|
||||
let esploader;
|
||||
let fileData;
|
||||
let transport;
|
||||
|
||||
try {
|
||||
const reader = new FileReader();
|
||||
fileData = await new Promise((resolve, reject) => {
|
||||
reader.addEventListener('error', () => {
|
||||
reader.abort();
|
||||
reject(new DOMException('Problem parsing input file.'));
|
||||
});
|
||||
|
||||
reader.addEventListener('load', () => resolve(reader.result));
|
||||
|
||||
reader.readAsBinaryString(flashFile);
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
console.error(e);
|
||||
flashing.error = `Cannot read flash file: ${e}`;
|
||||
return;
|
||||
}
|
||||
|
||||
const flashOptions = {
|
||||
terminal: log,
|
||||
compress: true,
|
||||
|
|
@ -304,18 +421,18 @@ function setup() {
|
|||
romBaudrate: 115200,
|
||||
enableTracing: false,
|
||||
fileArray: [{
|
||||
data: fileData,
|
||||
address: 0
|
||||
data: await blobToBinaryString(flashData),
|
||||
address: selected.wipe ? 0x00000 : 0x10000
|
||||
}],
|
||||
reportProgress: async (_, written, total) => {
|
||||
flashing.percentage = (written / total) * 100;
|
||||
flashing.percent = (written / total) * 100;
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
flashing.active = true;
|
||||
transport = new Transport(port, true);
|
||||
flashOptions.transport = transport
|
||||
flashOptions.transport = transport;
|
||||
flashing.instance = esploader = new ESPLoader(flashOptions);
|
||||
esploader.hr = new HardReset(transport);
|
||||
await esploader.main();
|
||||
|
|
@ -330,44 +447,87 @@ function setup() {
|
|||
|
||||
try {
|
||||
await esploader.writeFlash(flashOptions);
|
||||
await esploader.after();
|
||||
await delay(100);
|
||||
await esploader.after('hard_reset');
|
||||
await delay(100);
|
||||
await espReset(transport);
|
||||
await transport.disconnect();
|
||||
}
|
||||
catch(e) {
|
||||
console.error(e);
|
||||
flashing.error = `ESP32 flashing failed: ${e}`;
|
||||
await esploader.hardReset();
|
||||
await espReset(transport);
|
||||
await transport.disconnect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(device.type === 'nrf52') {
|
||||
const dfu = flashing.instance = new Dfu(port, selected.wipe);
|
||||
const dfu = flashing.instance = new Dfu(port);
|
||||
|
||||
flashing.active = true;
|
||||
|
||||
try {
|
||||
await dfu.dfuUpdate(flashFile, async (progress) => {
|
||||
flashing.percentage = progress;
|
||||
});
|
||||
await dfu.dfuUpdate(flashData, async (progress) => {
|
||||
flashing.percent = progress;
|
||||
}, 60000);
|
||||
|
||||
}
|
||||
catch(e) {
|
||||
console.error(e);
|
||||
flashing.error = `nRF flashing failed: ${e}`;
|
||||
flashing.error = `nRF flashing failed: ${e}. Please reset the device and try again.`;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const devices = computed(() => {
|
||||
const classSortPrefix = (d) => d.class === 'ripple' ? '1' : '2';
|
||||
const deviceGroups = {};
|
||||
|
||||
for(const cls of ['ripple', 'community']) {
|
||||
const devices = config.device.toSorted(
|
||||
(a, b) => (classSortPrefix(a) + a.maker + a.name).localeCompare(classSortPrefix(b) + b.maker + b.name)
|
||||
).filter(
|
||||
d => d.class === cls && (deviceFilterText.value == '' || d.name.toLowerCase().includes(deviceFilterText.value?.toLowerCase()))
|
||||
)
|
||||
if(devices.length > 0) deviceGroups[cls] = devices;
|
||||
}
|
||||
|
||||
return deviceGroups;
|
||||
});
|
||||
|
||||
const showMessage = (text, icon, displayMs) => {
|
||||
snackbar.class = 'active';
|
||||
snackbar.text = text;
|
||||
snackbar.icon = icon || '';
|
||||
|
||||
setTimeout(() => {
|
||||
snackbar.icon = '';
|
||||
snackbar.text = '';
|
||||
snackbar.class = '';
|
||||
}, displayMs || 2000);
|
||||
}
|
||||
|
||||
const consoleMouseUp = (ev) => {
|
||||
if(window.getSelection().toString().length) {
|
||||
navigator.clipboard.writeText(window.getSelection().toString())
|
||||
showMessage('text copied to clipboard');
|
||||
}
|
||||
consoleEditBox.value.focus();
|
||||
}
|
||||
|
||||
return {
|
||||
consoleEditBox, consoleWindow,
|
||||
config, selected, flashing,
|
||||
snackbar,
|
||||
consoleEditBox, consoleWindow, consoleMouseUp,
|
||||
config, devices, selected, flashing, deviceFilterText,
|
||||
flashDevice, flasherCleanup, dfuMode,
|
||||
serialCon, closeSerialCon, openSerialCon,
|
||||
sendCommand, openSerialGUI,
|
||||
refresh, commandReference,
|
||||
retry, close, commandReference,
|
||||
stepBack,
|
||||
customFirmwareLoad, getFirmwarePath, getSelFwValue, getRoleFwValue,
|
||||
firmwareHasData
|
||||
firmwareHasData,
|
||||
canFlash, nrfErase
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
1
img/esp32.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300" width="300" height="300"><style>.a{fill:#e7352c}</style><path class="a" d="m99.4 223.2c0 3.1-0.6 6.2-2 9-1.3 2.8-3.2 5.3-5.6 7.3-2.5 2-5.3 3.4-8.3 4.1-3 0.8-6.1 0.9-9.2 0.3-2-0.4-4-1.1-5.8-2.1-1.8-1-3.5-2.2-5-3.7-1.4-1.4-2.7-3.1-3.6-4.9-1-1.9-1.7-3.8-2.1-5.9-0.9-4.4-0.3-9 1.6-13.1 1.9-4 5.1-7.4 9.1-9.5 4-2.1 8.5-2.9 13-2.3 4.5 0.7 8.6 2.8 11.8 6q1.4 1.4 2.6 3.1 1.1 1.8 1.9 3.7 0.8 1.9 1.2 3.9 0.4 2.1 0.4 4.1zm183.5-21.9c-3.3-23.1-10.3-45.6-20.8-66.5-10.5-21-24.3-40.1-40.8-56.6-16.5-16.6-35.6-30.3-56.6-40.8-20.9-10.5-43.3-17.5-66.5-20.9q-4.2 2.2-8.2 4.7-4 2.5-7.8 5.3-3.9 2.8-7.5 5.8-3.6 3-7.1 6.3v20.3c22.7 0 45.2 4.5 66.2 13.2 20.9 8.7 40 21.4 56 37.5 16.1 16 28.8 35.1 37.5 56 8.7 21 13.2 43.5 13.3 66.2h20.3q3.2-3.5 6.2-7.1 3-3.6 5.8-7.4 2.8-3.9 5.3-7.8 2.5-4 4.7-8.2z"/><path class="a" d="m299.4 137.5c0-13.5-2-26.9-6-39.8-3.9-12.9-9.7-25.2-17.2-36.4-7.5-11.2-16.7-21.2-27.1-29.8-10.5-8.5-22.1-15.4-34.6-20.5q-6.2-2.6-12.7-4.5-6.4-1.9-13.1-3.2-6.6-1.3-13.3-1.9-6.7-0.7-13.4-0.6c-4.9 0-9.6 0-14.2 0.7l-3.2 9.1c16.6 5.8 32.5 13.4 47.4 22.8 14.9 9.3 28.6 20.3 41.1 32.8 12.4 12.4 23.4 26.2 32.8 41 9.3 14.9 17 30.8 22.8 47.4l9.1-3.3c0-4.5 0.9-9.1 0.9-14.2m-135.2 162.2c-21.2 0-42.3-4.2-62-12.3-19.7-8.2-37.6-20.1-52.7-35.1-15-15-27-32.9-35.2-52.5-8.2-19.7-12.5-40.7-12.6-62 0-10.7 1-21.3 3-31.8 2.1-10.4 5.2-20.7 9.3-30.5 4-9.9 9.1-19.3 15-28.1 5.9-8.9 12.7-17.1 20.3-24.6l9.2 8.6c-14 14-25.2 30.6-32.8 48.9-7.7 18.3-11.6 37.9-11.6 57.8 0 19.8 3.9 39.4 11.6 57.7 7.6 18.3 18.8 34.9 32.8 48.9 14 14.1 30.6 25.2 48.9 32.9 18.3 7.6 37.9 11.5 57.7 11.5 19.8 0 39.4-3.9 57.7-11.5 18.3-7.7 34.9-18.8 48.9-32.9l8.6 8.6c-7.6 7.6-16 14.3-24.9 20.1-9 5.9-18.5 10.9-28.4 14.8-10 4-20.3 7-30.8 8.9-10.6 1.9-21.3 2.8-32 2.6z"/><path class="a" d="m161.7 241.1c0.7-7.9 0.5-15.9-0.8-23.8-1.3-7.9-3.6-15.6-6.8-23-3.3-7.3-7.4-14.2-12.4-20.4-5-6.3-10.7-11.9-17.1-16.8q-5.1-4-10.7-7.3-5.7-3.3-11.7-5.7-6-2.5-12.4-4-6.3-1.6-12.8-2.3-1.6-0.2-3-1-1.5-0.8-2.5-2.1-1.1-1.2-1.6-2.8-0.5-1.6-0.4-3.2 0.1-1.3 0.6-2.4 0.4-1.2 1.2-2.2 0.8-1 1.9-1.7 1-0.7 2.2-1.1 0.4-0.1 0.8-0.2 0.4 0 0.9-0.1 0.4 0 0.8 0 0.4 0 0.8 0c14.6 1.5 28.6 5.9 41.5 12.8 12.8 7 24.2 16.4 33.4 27.7 9.2 11.3 16.2 24.3 20.4 38.3 4.2 14 5.6 28.7 4.2 43.2q-0.2 2.4-0.6 4.9-0.3 2.4-0.8 4.8-0.4 2.4-1 4.8-0.6 2.3-1.3 4.7l24.7 7q2.8-0.8 5.5-1.7 2.7-0.9 5.3-2 2.7-1 5.3-2.1 2.7-1.1 5.2-2.3 0.7-3.6 1.2-7.3 0.6-3.6 0.9-7.3 0.3-3.6 0.5-7.3 0.2-3.6 0.1-7.3c0-18.5-3.3-36.9-9.7-54.2-6.5-17.4-15.9-33.5-28-47.5-12.1-14.1-26.5-25.9-42.7-34.8-16.2-9-33.8-15.1-52.1-17.9q-3.1-0.5-6.2-0.8-3.1-0.3-6.3-0.3-3.1 0-6.2 0.3-3.1 0.3-6.2 0.8-3.9 1.1-7.6 2.6-3.7 1.6-7.1 3.7-3.4 2.2-6.4 4.7-3.1 2.6-5.8 5.7c-5.7 6.6-9.7 14.5-11.6 23.1-1.9 8.5-1.7 17.4 0.7 25.9 2.3 8.4 6.7 16.1 12.8 22.5 6 6.3 13.5 11 21.9 13.8q1.6 0.3 3.3 0.7 1.6 0.3 3.3 0.6 1.7 0.3 3.4 0.6 1.6 0.3 3.3 0.5c5.8 1 11.3 2.9 16.4 5.8 5.1 2.9 9.6 6.7 13.4 11.2 3.7 4.4 6.7 9.5 8.7 15 2 5.5 3 11.3 3 17.2q0 3.5-0.5 7-0.5 3.6-1.5 7-1 3.4-2.5 6.6-1.5 3.2-3.4 6.2l17 11q3.1 0.8 6.3 1.5 3.2 0.7 6.4 1.3 3.2 0.5 6.4 0.9 3.3 0.3 6.5 0.5 1.9-3.8 3.4-7.8 1.6-4 2.8-8.1 1.2-4.1 2-8.3 0.8-4.2 1.2-8.5"/></svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
31
img/esp_now.svg
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 26" width="100%" height="100%">
|
||||
<style>
|
||||
.a {
|
||||
fill: #fff
|
||||
}
|
||||
|
||||
.b {
|
||||
fill: #c00000
|
||||
}
|
||||
</style>
|
||||
<path
|
||||
d="m5 25.3c-2.1-0.4-3.9-2.3-4.1-4.3-0.1-0.3-0.1-4-0.1-8.2 0-8.3 0-8.2 0.5-9.2 0.3-0.6 0.4-0.9 1-1.4 0.6-0.6 0.7-0.6 1.4-1 0.7-0.3 0.8-0.4 1.5-0.5 0.2 0 20.5 0 45.1 0 45.8 0 45.1 0 45.5 0.3q0.1 0 0.1 0c0.1 0 1 0.5 1.1 0.5 0.1 0.1 0.2 0.2 0.4 0.3 0.2 0.2 0.6 0.6 0.8 0.9 0.2 0.3 0.5 0.7 0.4 0.7q0 0 0.1 0.1c0 0.1 0.1 0.2 0.1 0.2 0 0.1 0.1 0.3 0.1 0.5 0.1 0.2 0.2 0.6 0.2 0.9 0.1 0.7 0.1 15.8 0 16.3 0 0.1-0.1 0.3-0.1 0.4q-0.1 0.1-0.1 0.1c0.1 0 0 0.3-0.2 0.6-0.7 1.4-1.9 2.3-3.5 2.7-0.3 0.1-89.7 0.1-90.2 0.1zm38.6-2.3c0-0.1-0.4-0.9-0.9-1.9l-0.8-1.7v-6.7c0-5.8 0-6.8-0.2-7.2-0.2-0.8-0.7-1.6-1.6-2.1-0.8-0.5 0.3-0.5-17.5-0.5-17.9 0-16.7 0-17.6 0.5-0.8 0.5-1.5 1.4-1.6 2.3-0.1 0.3-0.1 3.1-0.1 7.4 0 7.6 0 7.4 0.5 8.2 0.2 0.5 0.9 1.1 1.3 1.3 0.1 0.1 0.4 0.3 0.6 0.3 0.4 0.2 1.3 0.2 19.2 0.2 14.9 0 18.7 0 18.7-0.1zm27.5-3.2q3.1-0.8 4.2-3.9c0.9-2.4 0.7-5.6-0.5-7.7-0.4-0.6-1.4-1.6-2.1-1.9-2.1-1.2-5.1-1.2-7.3-0.1-0.3 0.2-0.8 0.6-1.2 1-0.6 0.6-0.7 0.8-1.1 1.5-0.6 1.4-0.9 2.8-0.8 4.5 0.2 3.7 2 6.1 5.2 6.7 0.9 0.2 2.8 0.1 3.6-0.1zm-22.4-4.6c0-2.5-0.1-5-0.1-5.5v-1h0.3c0.2 0 0-0.4 2.8 4.5 0.5 0.8 1.5 2.6 2.3 4.1l1.5 2.5 1.8 0.1h1.8v-7.2-7.1h-1.2-1.2l0.1 4.8c0 2.7 0 5.2 0 5.6l0.1 0.6h-0.3-0.3l-3.2-5.5-3.2-5.5h-1.7-1.8v7.1 7.2h1.2 1.2zm36 2.2c1.2-4.9 1.6-6.4 1.8-7.5l0.2-1.2h0.2c0.3 0 0.3 0 0.4 0.6 0.2 1.1 0.7 3.2 1.6 6.9l0.9 3.6h1.6 1.6l1.7-7c1-3.9 1.8-7.1 1.9-7.2 0-0.1-0.3-0.1-1.2-0.1h-1.3l-0.1 0.4c-0.7 2.8-2.2 9.5-2.4 10.8l-0.1 0.6h-0.3c-0.2 0-0.2 0-0.3-0.4-0.1-1.1-0.6-3.1-1.6-7.1l-1.1-4.3h-1.3-1.2l-0.7 2.7c-1.5 5.7-1.8 6.8-1.9 7.9l-0.2 1.1h-0.3c-0.2 0-0.2 0-0.3-0.2 0-0.1-0.1-0.8-0.3-1.7-0.1-0.8-0.7-3.3-1.2-5.5-0.5-2.1-0.9-4-1-4.1 0-0.2 0-0.2-1.3-0.2-1.1 0-1.2 0-1.2 0.1 0.1 0.2 0.7 2.5 2.3 8.8 0.7 2.9 1.3 5.3 1.3 5.3 0.1 0.1 0.4 0.1 1.6 0.1h1.5z" />
|
||||
<path
|
||||
d="m67.7 17.8c-1.3-0.4-2.1-1.3-2.4-2.9-0.2-0.9-0.2-3.5 0-4.4 0.2-1.2 0.9-2.2 1.7-2.6 1.1-0.6 3-0.6 4.1 0 0.8 0.4 1.5 1.4 1.8 2.6 0.1 0.5 0.1 1 0.1 2.2 0 1.8-0.1 2.5-0.6 3.4-0.2 0.5-0.8 1.2-1.3 1.4-0.8 0.4-2.4 0.5-3.4 0.3z" />
|
||||
<path class="a"
|
||||
d="m19.5 20.5c-0.8-0.1-1.5-0.3-2.1-0.5l-0.4-0.2v-1.7-1.7h0.2c0.1 0 0.7 0.2 1.1 0.4 1.4 0.5 2.3 0.8 3.2 0.8 0.7 0 0.9-0.1 1.2-0.2 0.6-0.3 0.9-0.9 0.7-1.4-0.2-0.5-0.7-0.9-2-1.5-2.4-1.2-3-1.7-3.6-2.6-0.5-0.7-0.6-1.3-0.6-2.5-0.1-1.1 0.1-1.6 0.5-2.3q1.2-2.1 4.7-2.1c1.5 0 2.8 0.3 4.2 0.9 0.3 0.2 0.4 0.2 0.4 0.4 0 0.2-0.3 0.8-0.5 1.5l-0.5 1.2-0.4-0.1c-0.2 0-0.7-0.2-1.1-0.3-0.8-0.3-2.1-0.6-2.4-0.5-0.5 0.1-0.7 0.2-1 0.5q-0.8 0.7 0 1.5c0.3 0.3 0.8 0.6 1.8 1.1 2.6 1.3 3.5 2 3.9 3.3 0.2 0.6 0.3 2.3 0.1 3-0.4 1.5-1.5 2.5-3.2 2.9-0.8 0.2-3.3 0.3-4.2 0.1z" />
|
||||
<path class="a"
|
||||
d="m5.7 12.8v-7.6h4.4c2.4 0 4.4 0 4.5 0 0.1 0.1 0.1 0.5 0.1 1.6l-0.1 1.5h-2.6-2.7v1.4 1.3h2.5 2.5v1.6 1.5h-2.5-2.5v1.6 1.7h2.7 2.6v1.5 1.6h-4.4-4.5z" />
|
||||
<path class="a"
|
||||
d="m29.9 12.8v-7.6h2.4c3.8 0 5 0.1 6.1 0.7 1 0.5 1.8 1.5 2 2.6 0.1 0.3 0.1 0.9 0.1 1.6 0 1.8-0.3 2.7-1.2 3.6q-0.9 0.9-2.2 1.3c-0.5 0.1-1 0.1-2.1 0.2h-1.5v2.7 2.6h-1.8-1.8zm5.8-1c0.7-0.3 1-0.7 1.2-1.3 0.1-0.5 0-1-0.4-1.5-0.3-0.5-1.2-0.8-2.5-0.8h-0.5v2 1.9h0.9c0.7 0 0.9-0.1 1.3-0.3z" />
|
||||
<path class="a"
|
||||
d="m67.3 20.1c-2.1-0.3-3.7-1.6-4.5-3.3-0.5-1.1-0.7-2.1-0.7-3.6-0.1-2 0.1-3.3 0.8-4.7 0.4-0.7 0.5-0.9 1.1-1.5 1.4-1.4 3-2 5.5-1.9 1.4 0.1 2.4 0.3 3.5 0.9 0.6 0.4 1.6 1.4 2 2 0.8 1.4 1.2 3.2 1.1 5.3q-0.2 3.4-2 5.2c-0.8 0.8-1.6 1.2-2.8 1.5-0.8 0.3-3.1 0.3-4 0.1zm2.7-2.5c0.7-0.2 1.1-0.4 1.6-0.9 0.5-0.5 0.8-1.1 1-2 0.2-0.9 0.2-3 0-3.9-0.3-1.3-0.9-2.3-1.8-2.7-0.4-0.2-1.2-0.4-1.7-0.4-0.6 0-1.4 0.2-1.8 0.4-0.9 0.4-1.5 1.4-1.8 2.6-0.2 0.9-0.2 3.1 0 4 0.3 1.2 0.8 2.1 1.6 2.5 0.8 0.4 1.9 0.6 2.9 0.4z" />
|
||||
<path class="a"
|
||||
d="m46.2 12.7v-7.4l2 0.1h2l3.1 5.5c1.8 3 3.3 5.5 3.3 5.5 0 0 0-0.2 0-0.4 0-0.2-0.1-2.7-0.1-5.5v-5.2h1.4 1.4v7.4 7.4h-2-2l-1.5-2.6c-0.8-1.4-1.9-3.3-2.3-4.1-2.8-4.8-2.6-4.5-2.7-4.2 0 0.1 0.1 0.7 0.1 1.3 0 0.6 0.1 3 0.1 5.4v4.2h-1.4-1.4z" />
|
||||
<path class="a"
|
||||
d="m80.7 20c0 0-0.6-2.4-1.3-5.3-0.8-2.9-1.6-6.1-1.9-7.2-0.2-1.1-0.5-2-0.5-2.1 0 0 0.5-0.1 1.6-0.1 1.5 0 1.5 0 1.5 0.2 0 0.1 0.5 2 1 4.2 0.5 2.2 1 4.7 1.2 5.5 0.2 0.8 0.3 1.6 0.3 1.7 0.1 0.1 0.1-0.2 0.3-0.9 0.2-1.1 0.4-2.3 1.9-8l0.7-2.7h1.5 1.5l1.1 4.3c1.2 4.5 1.2 4.9 1.5 6.5l0.2 1.1 0.1-0.6c0.2-1.3 0.5-2.7 1.4-6.5 0.5-2.2 1-4.2 1-4.4l0.1-0.4h1.5c0.9 0 1.6 0.1 1.6 0.1 0 0-0.9 3.4-1.9 7.4l-1.9 7.3h-1.7-1.8l-1-3.9c-1-3.7-1.4-5.3-1.6-6.6l-0.1-0.6-0.2 1.2c-0.2 1.1-0.6 2.6-1.9 7.5l-0.6 2.4h-1.7c-1.4 0-1.8 0-1.9-0.1z" />
|
||||
<path class="b"
|
||||
d="m5.5 23.2c-0.2-0.1-0.5-0.2-0.6-0.3-0.5-0.2-1.1-0.9-1.4-1.3-0.5-0.9-0.5-0.7-0.5-8.5 0-4.5 0.1-7.3 0.1-7.6 0.2-0.9 0.8-1.8 1.7-2.3 0.9-0.5-0.3-0.5 17.8-0.5 18.1 0 16.9 0 17.8 0.5 0.8 0.5 1.4 1.2 1.6 2.1 0.1 0.4 0.1 1.4 0.1 7.2v6.6l1 2.1c0.5 1.1 1 2.1 1 2.1 0 0-8.6 0-19.1 0-18.2 0-19.1 0-19.5-0.1zm18-3c1.7-0.5 2.8-1.4 3.2-2.9 0.2-0.7 0.1-2-0.1-2.6-0.5-1.2-1.3-1.9-3.9-3.2-1.5-0.8-1.9-1.1-2.1-1.5-0.2-0.3-0.2-1 0-1.2 0.1-0.3 0.4-0.7 0.7-0.8 0.7-0.3 2.1-0.1 3.4 0.4 0.5 0.1 0.9 0.3 0.9 0.3 0.1 0 0.3-0.3 0.6-1.2 0.3-0.6 0.5-1.2 0.5-1.2-0.1-0.1-1.2-0.5-1.8-0.7-1-0.2-1.5-0.3-2.6-0.3-2.1 0-3.6 0.7-4.4 2-0.4 0.7-0.5 1.2-0.5 2.1 0 1.3 0.4 2.1 1.3 3.1 0.5 0.4 1.1 0.8 2.9 1.7 1.8 0.9 2.2 1.4 2.1 2.3q-0.1 0.8-0.8 1.1c-0.3 0.2-0.5 0.2-1.4 0.2-1.3 0-2-0.2-4.2-1.1-0.1 0-0.1 0.3-0.1 1.4v1.5l0.5 0.2c1.2 0.5 2.2 0.6 3.9 0.6 1 0 1.4-0.1 1.9-0.2zm-9.1-1.3v-1.3h-2.7-2.6v-1.8-1.9h2.5 2.5v-1.3-1.3h-2.5-2.5v-1.6-1.7h2.6 2.7v-1.2c0-0.7 0-1.3 0-1.3 0 0-2 0-4.3 0h-4.2v7.4 7.4l4.3-0.1h4.2zm18.9-1.3v-2.6l1.5-0.1c1.6 0 2.3-0.2 3.2-0.6 0.7-0.4 1.5-1.2 1.8-1.8 0.5-0.9 0.7-2.6 0.4-3.8-0.3-1.1-1-2-2-2.5-1.2-0.6-1.8-0.7-5.2-0.7l-2.8-0.1v7.4 7.5h1.5 1.6z" />
|
||||
<path class="b"
|
||||
d="m33.3 10.2v-2.2h0.7c2 0 2.8 0.4 3.1 1.6 0.1 0.4 0.1 1.2-0.1 1.6-0.3 0.5-0.9 0.9-1.6 1.1-0.2 0-0.8 0.1-1.2 0.1h-0.9z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.8 KiB |
1
img/faketec.svg
Normal file
|
After Width: | Height: | Size: 182 KiB |
1
img/heltec_mesh_solar.svg
Normal file
|
After Width: | Height: | Size: 242 KiB |
1
img/heltec_meshpocket.svg
Normal file
|
After Width: | Height: | Size: 17 KiB |
1
img/heltec_paper.svg
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 36 KiB |
1
img/heltec_t114.svg
Normal file
|
After Width: | Height: | Size: 104 KiB |
1
img/heltec_v2.svg
Normal file
|
After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 38 KiB |
1
img/heltec_v3.svg
Normal file
|
After Width: | Height: | Size: 77 KiB |
1
img/heltec_v4.svg
Normal file
|
After Width: | Height: | Size: 123 KiB |
1
img/heltec_wp.svg
Normal file
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 158 KiB |
1
img/heltec_wsl3.svg
Normal file
|
After Width: | Height: | Size: 124 KiB |
1
img/heltec_wt3.svg
Normal file
|
After Width: | Height: | Size: 157 KiB |
1
img/ikoka_nano.svg
Normal file
|
After Width: | Height: | Size: 231 KiB |
1
img/ikoka_stick.svg
Normal file
|
After Width: | Height: | Size: 192 KiB |
1
img/keepteen_lt1.svg
Normal file
|
After Width: | Height: | Size: 23 KiB |
1
img/lilygo_pager.svg
Normal file
|
After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 47 KiB |
1
img/lilygo_t3s3.svg
Normal file
|
After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 47 KiB |
1
img/lilygo_t5_pro.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg width="752pt" height="1408pt" viewBox="0 0 752 1408" xmlns="http://www.w3.org/2000/svg"><path fill="#888b94" d="M107.77 0h528.71c17.3 1.49 34.61 5.38 50.12 13.37 17.57 8.72 32.62 22.13 44.12 37.95 10.87 15 17.82 32.63 21.28 50.78v1204.22c-5.62 31.38-23.45 60.63-49.71 78.92-19.12 14.09-42.69 21.1-66.2 22.76H106.6c-18.76-2.28-37.44-7.76-53.22-18.4-15.3-9.66-28.12-23.02-37.77-38.29-7.76-12.35-12.48-26.33-15.61-40.5V96.78C2.81 84.89 6.53 73.11 12.48 62.4 26.44 36.77 50.02 16.6 77.44 6.66 87.2 3.1 97.48 1.18 107.77 0m.71 17.65c-14.96 1.63-29.39 6.86-42.34 14.46-30.82 18.54-51.06 53.76-50.61 89.84-.16 53.35-.02 106.7-.07 160.05.02 334.31-.01 668.63.01 1002.94-.25 15.23 3.03 30.43 9.27 44.31 5.58 13.35 14.91 24.78 25.28 34.72 13.19 12.28 29.89 20.4 47.32 24.62 11.02 2.67 22.44 2.71 33.71 2.73 164-.08 328-.06 492-.06 12.32.13 24.79-.83 36.59-4.6 24.31-7.14 45.99-23.09 59.44-44.62 11.24-17.8 16.97-39.01 16.22-60.05-.11-381.66 0-763.33-.06-1144.99-.09-9.1.43-18.25-.77-27.3-3.4-31.62-22.54-60.95-49.83-77.18-17-10.27-36.79-15.57-56.62-15.71-166.67-.09-333.34.03-500.01-.02-6.52-.1-13.05.18-19.53.86z"/><path fill="#cad3d4" d="M108.48 17.65c6.48-.68 13.01-.96 19.53-.86 166.67.05 333.34-.07 500.01.02 19.83.14 39.62 5.44 56.62 15.71 27.29 16.23 46.43 45.56 49.83 77.18 1.2 9.05.68 18.2.77 27.3.06 381.66-.05 763.33.06 1144.99.75 21.04-4.98 42.25-16.22 60.05-13.45 21.53-35.13 37.48-59.44 44.62-11.8 3.77-24.27 4.73-36.59 4.6-164 0-328-.02-492 .06-11.27-.02-22.69-.06-33.71-2.73-17.43-4.22-34.13-12.34-47.32-24.62-10.37-9.94-19.7-21.37-25.28-34.72-6.24-13.88-9.52-29.08-9.27-44.31-.02-334.31.01-668.63-.01-1002.94.05-53.35-.09-106.7.07-160.05-.45-36.08 19.79-71.3 50.61-89.84 12.95-7.6 27.38-12.83 42.34-14.46m-47.99 108.3c-.05 371.96-.07 743.94.01 1115.9 44.14-.37 88.29-.03 132.44-.15 165.88-.04 331.76.15 497.63-.1-.21-49.19-.05-98.38-.08-147.56-.03-322.66 0-645.32-.01-967.98-90.81.04-181.63-.11-272.43-.03-119.19-.17-238.37.17-357.56-.08M368.3 1263.34c-20.49 2.64-37.11 21.1-38.3 41.6-1.48 17.69 8.72 35.64 24.62 43.53 15.67 8.3 36.04 6.17 49.75-5.07 10.84-8.23 16.75-21.96 16.81-35.41.15-14.48-7.28-28.68-19.09-37.01-9.68-6.82-22.14-9.42-33.79-7.64z"/><path fill="#4e4e54" d="M60.49 125.95c119.19.25 238.37-.09 357.56.08 90.8-.08 181.62.07 272.43.03.01 322.66-.02 645.32.01 967.98.03 49.18-.13 98.37.08 147.56-165.87.25-331.75.06-497.63.1-44.15.12-88.3-.22-132.44.15-.08-371.96-.06-743.94-.01-1115.9m7.93 8.21c.31 12.63.09 25.25.04 37.88.02 199.66-.03 399.32.03 598.98-.08 154.25.1 308.51-.1 462.77 151.55.16 303.11.04 454.66.02 53.17-.17 106.35.26 159.52-.13.13-268.56.03-537.13.07-805.7-.08-97.86.13-195.73-.12-293.59-1.15-1.06-3.05-.35-4.47-.59-89.36.71-178.7-.06-268.03.26-109.02 0-218.04.13-327.05-.08-4.85-.16-9.72-.34-14.55.18z"/><path fill="#747474" d="M68.42 134.16c4.83-.52 9.7-.34 14.55-.18 109.01.21 218.03.08 327.05.08 89.33-.32 178.67.45 268.03-.26 1.42.24 3.32-.47 4.47.59.25 97.86.04 195.73.12 293.59-.04 268.57.06 537.14-.07 805.7-53.17.39-106.35-.04-159.52.13-151.55.02-303.11.14-454.66-.02.2-154.26.02-308.52.1-462.77-.06-199.66-.01-399.32-.03-598.98.05-12.63.27-25.25-.04-37.88z"/><path fill="#b1b9bb" d="M368.3 1263.34c11.65-1.78 24.11.82 33.79 7.64 11.81 8.33 19.24 22.53 19.09 37.01-.06 13.45-5.97 27.18-16.81 35.41-13.71 11.24-34.08 13.37-49.75 5.07-15.9-7.89-26.1-25.84-24.62-43.53 1.19-20.5 17.81-38.96 38.3-41.6z"/></svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 55 KiB |
1
img/lilygo_tbeam.svg
Normal file
|
After Width: | Height: | Size: 419 KiB |
|
Before Width: | Height: | Size: 39 KiB |
1
img/lilygo_tbeam_supreme.svg
Normal file
|
After Width: | Height: | Size: 247 KiB |
|
Before Width: | Height: | Size: 73 KiB |
1
img/lilygo_tdeck.svg
Normal file
|
After Width: | Height: | Size: 81 KiB |
1
img/lilygo_tdeck_pro.svg
Normal file
|
After Width: | Height: | Size: 70 KiB |
1
img/lilygo_tdisplay.svg
Normal file
|
After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 57 KiB |
1
img/lilygo_techo.svg
Normal file
|
After Width: | Height: | Size: 22 KiB |
1
img/lilygo_techo_lite.svg
Normal file
|
After Width: | Height: | Size: 13 KiB |
1
img/lilygo_tlora_1.6.svg
Normal file
|
After Width: | Height: | Size: 58 KiB |
1
img/lilygo_tlora_c6.svg
Normal file
|
After Width: | Height: | Size: 64 KiB |
28
img/lora.svg
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 405" width="100%" height="100%">
|
||||
<style>
|
||||
.a { fill: #fff }
|
||||
</style>
|
||||
<path class="a"
|
||||
d="m206.6 22.5c8.7 0 17.4 0.6 26 2 8.5 1.3 17 3.2 25.3 5.8 8.3 2.5 16.4 5.7 24.2 9.5 7.9 3.8 15.4 8.1 22.6 13l13.8-18.6c-8.3-5.4-16.9-10.2-25.9-14.4-8.9-4.3-18.2-7.8-27.6-10.7-9.5-2.9-19.1-5.1-28.9-6.6-9.7-1.6-19.6-2.4-29.5-2.5-9.9 0.1-19.7 0.9-29.5 2.5-9.8 1.5-19.4 3.7-28.9 6.6-9.4 2.9-18.6 6.5-27.6 10.7-8.9 4.2-17.6 9-25.8 14.4l13.7 18.6c7.2-4.9 14.8-9.2 22.6-13 7.8-3.8 15.9-7 24.2-9.5 8.3-2.6 16.8-4.5 25.4-5.8 8.5-1.4 17.2-2 25.9-2z" />
|
||||
<path class="a"
|
||||
d="m260 116.6l8.2-11.1q-6.8-4.3-14.1-7.7-7.4-3.4-15.1-5.7-7.7-2.4-15.7-3.7-7.9-1.3-16-1.5h-1.2-0.1q-8.1 0.2-16.1 1.5-7.9 1.3-15.7 3.7-7.7 2.3-15 5.7-7.4 3.4-14.2 7.7l8.3 11.1q6-3.8 12.4-6.7 6.4-2.9 13.2-4.9 6.7-2 13.7-3.1 7-1.2 14-1.4 7.1 0.2 14 1.4 7 1.1 13.7 3.1 6.8 2 13.2 4.9 6.5 2.9 12.5 6.7z" />
|
||||
<path class="a"
|
||||
d="m206.6 43.9q-11.4 0.2-22.7 1.9-11.3 1.8-22.3 5.2-10.9 3.3-21.3 8.2-10.3 4.8-19.9 11l10.9 15c11.1-7.3 23.2-13 36-16.9 12.7-3.8 26-5.8 39.3-5.8 13.3 0 26.6 2 39.3 5.8 12.8 3.9 24.9 9.6 36 16.9l10.9-15q-9.6-6.2-19.9-11-10.4-4.9-21.3-8.2-11-3.4-22.3-5.2-11.2-1.7-22.7-1.9z" />
|
||||
<path class="a"
|
||||
d="m206.6 379.9c-8.7 0-17.4-0.6-25.9-1.9-8.6-1.3-17.1-3.3-25.4-5.9-8.3-2.5-16.4-5.7-24.2-9.5-7.8-3.8-15.4-8.1-22.6-13l-13.7 18.6c8.2 5.4 16.9 10.3 25.8 14.5 9 4.2 18.2 7.7 27.6 10.6 9.5 2.9 19.1 5.1 28.9 6.7 9.8 1.5 19.6 2.3 29.5 2.4 9.9-0.1 19.8-0.9 29.5-2.4 9.8-1.6 19.4-3.8 28.9-6.7 9.4-2.9 18.7-6.4 27.6-10.6 9-4.2 17.6-9.1 25.9-14.5l-13.8-18.6c-7.2 4.9-14.7 9.2-22.6 13-7.8 3.8-15.9 7-24.2 9.5-8.3 2.6-16.8 4.6-25.3 5.9-8.6 1.3-17.3 1.9-26 1.9z" />
|
||||
<path class="a"
|
||||
d="m153.3 285.9l-8.3 11q6.8 4.4 14.2 7.7 7.3 3.4 15 5.7 7.7 2.4 15.7 3.7 8 1.3 16 1.5h0.1 1.2q8.1-0.2 16.1-1.5 8-1.3 15.7-3.7 7.7-2.3 15.1-5.7 7.3-3.3 14.1-7.7l-8.2-11q-6 3.7-12.5 6.6-6.4 2.9-13.1 4.9-6.8 2-13.8 3.2-6.9 1.1-14 1.3-7-0.2-14-1.3-7-1.2-13.7-3.2-6.8-2-13.2-4.9-6.4-2.9-12.4-6.6z" />
|
||||
<path class="a"
|
||||
d="m120.4 332.2q9.6 6.3 19.9 11.1 10.4 4.8 21.3 8.2 11 3.3 22.3 5.1 11.3 1.8 22.7 2 11.5-0.2 22.7-2 11.3-1.8 22.3-5.1 10.9-3.4 21.3-8.2 10.3-4.8 19.9-11.1l-10.9-15c-11.1 7.4-23.2 13.1-36 16.9-12.7 3.9-26 5.8-39.3 5.8-13.3 0-26.6-1.9-39.3-5.8-12.8-3.8-24.9-9.5-36-16.9z" />
|
||||
<path fill-rule="evenodd" class="a" d="m118.8 231.6h-72.2v-146.5h-46.6v185.3h118.8v-38.8z" />
|
||||
<path class="a"
|
||||
d="m445.2 188c3.5-2.4 6.7-5.2 9.5-8.4 2.8-3.3 5.1-6.8 7-10.7 1.9-3.8 3.3-7.8 4.1-12 0.8-4.2 1.1-8.5 0.9-12.7 0.3-4.2 0.1-8.4-0.5-12.6-0.7-4.1-1.8-8.2-3.4-12-1.6-3.9-3.6-7.6-6-11.1-2.4-3.4-5.2-6.5-8.3-9.3q-5-3.7-10.5-6.5-5.6-2.8-11.5-4.6-6-1.8-12.2-2.5-6.1-0.7-12.3-0.4h-94.5v185h46v-66.5h29.5l36.3 66.5h51.7l-42.3-74.2q2.2-0.6 4.3-1.4 2.2-0.8 4.3-1.8 2-1.1 4-2.2 2-1.2 3.9-2.6zm-47.1-18.3h-44.6v-50.5h42c3.4-0.4 6.9 0 10.1 1.1 3.3 1.1 6.2 3 8.6 5.4 2.4 2.4 4.3 5.4 5.4 8.6 1.1 3.2 1.5 6.7 1.1 10.1 0.4 3.2 0.1 6.4-0.8 9.5-1 3.1-2.6 5.9-4.7 8.3-2.2 2.4-4.8 4.3-7.7 5.6-3 1.3-6.2 1.9-9.4 1.9z" />
|
||||
<path class="a"
|
||||
d="m206.7 274c29 0 73.5-13.8 73.5-74.1 0-60.2-48.8-71.5-73.5-71.5-29.7 0-73.7 14.8-73.7 72.8 0 58 44.7 72.8 73.7 72.8zm-0.4-114.7c14.6 0 33.3 7.9 33.3 41.3 0 33.3-16.4 42.6-32.5 42.6-16.2 0-33.4-10.2-33.4-42 0-31.7 17.1-41.9 32.6-41.9z" />
|
||||
<path class="a"
|
||||
d="m610.3 170c0-20.4-7.7-41.8-58.9-41.8-51.3 0-59.6 24.1-59.6 43.4h39c0.1-2.7 0.7-5.3 1.8-7.7 1.2-2.5 2.8-4.6 4.8-6.4 2-1.7 4.4-3 7-3.8 2.5-0.7 5.2-1 7.9-0.6 8.2 0 18.6 2.9 18.6 13 0 10-0.2 13.1-0.2 13.1-78.9 9.7-85.5 30-85.5 55.7 0 25.7 23.7 39.2 45.6 39.2 4 0.2 8 0 12-0.8 3.9-0.7 7.7-1.8 11.4-3.4 3.7-1.6 7.1-3.7 10.3-6.1 3.2-2.4 6.1-5.2 8.7-8.2q0.2 1.8 0.4 3.7 0.3 1.8 0.7 3.6 0.4 1.9 0.8 3.7 0.5 1.8 1.1 3.5h37.4q-0.9-2.5-1.6-5.1-0.6-2.6-1-5.2-0.4-2.6-0.6-5.3-0.2-2.7-0.1-5.3zm-40 46.8c0.1 3.8-0.6 7.4-2 10.9-1.4 3.4-3.5 6.5-6.2 9.1-2.6 2.6-5.8 4.6-9.3 6-3.4 1.3-7.1 1.8-10.9 1.7-2.3 0.3-4.6 0.1-6.8-0.6-2.2-0.7-4.3-1.9-6-3.5-1.7-1.5-3-3.5-3.8-5.7-0.9-2.1-1.2-4.5-1-6.8 0-3.8 3.1-13.2 21.5-17.8q3.1-0.7 6.2-1.5 3.1-0.8 6.1-1.7 3.1-0.9 6.1-1.9 3.1-1 6-2.1z" />
|
||||
<path class="a"
|
||||
d="m629.7 97.8v-0.1q0.6-0.1 1-0.4 0.5-0.2 0.9-0.6 0.3-0.5 0.5-0.9 0.2-0.5 0.3-1.1 0-0.4-0.1-0.8 0-0.4-0.2-0.7-0.2-0.4-0.4-0.7-0.3-0.3-0.6-0.6-0.5-0.3-1-0.5-0.5-0.2-1.1-0.3-0.6-0.1-1.2-0.2-0.5 0-1.1 0.1-0.6-0.1-1.2 0-0.7 0-1.3 0-0.6 0.1-1.2 0.2-0.6 0-1.2 0.1v12.8h3v-5.1h1.4c1.6 0 2.4 0.6 2.6 2q0.1 0.4 0.2 0.8 0.1 0.4 0.2 0.8 0.1 0.4 0.3 0.8 0.1 0.4 0.3 0.7h3.2q-0.2-0.3-0.3-0.7-0.2-0.4-0.3-0.8-0.1-0.4-0.2-0.8-0.1-0.4-0.1-0.9-0.1-0.5-0.3-1-0.2-0.4-0.5-0.8-0.3-0.5-0.7-0.8-0.4-0.3-0.9-0.5zm-3.5-0.9h-1.4v-3.7q0.2 0 0.4 0 0.2-0.1 0.4-0.1 0.2 0 0.5 0 0.2 0 0.4 0c1.8 0 2.7 0.8 2.7 1.9 0 1.4-1.3 1.9-3 1.9z" />
|
||||
<path class="a"
|
||||
d="m626.8 84.7c-5.2 0-9.8 3.3-11.8 8.1-1.9 4.8-0.7 10.3 3 13.9 3.7 3.7 9.3 4.7 14 2.7 4.8-2.1 7.9-6.8 7.9-11.9-0.1-1.7-0.4-3.4-1.1-5-0.6-1.6-1.6-3-2.8-4.2-1.2-1.2-2.7-2.1-4.3-2.7-1.5-0.7-3.2-1-4.9-0.9zm0 23c-4.1 0.2-8-2.2-9.8-6-1.7-3.8-0.9-8.3 2-11.3 2.9-3.1 7.3-4 11.2-2.4 3.9 1.5 6.4 5.3 6.4 9.5 0 1.3-0.2 2.6-0.7 3.9-0.4 1.2-1.1 2.3-2 3.3-1 0.9-2 1.7-3.3 2.2-1.2 0.5-2.5 0.8-3.8 0.8z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5 KiB |
|
Before Width: | Height: | Size: 12 KiB |
12
img/meshcore.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 134 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<path d="M3.277,0.053C2.829,0.053 2.401,0.41 2.321,0.851L0.013,13.623C-0.067,14.064 0.232,14.421 0.681,14.421L3.13,14.421C3.578,14.421 4.006,14.064 4.086,13.623L5.004,8.54L6.684,13.957C6.766,14.239 7.02,14.421 7.337,14.421L10.58,14.421C10.897,14.421 11.217,14.239 11.401,13.957L15.043,8.513L14.119,13.623C14.038,14.064 14.338,14.421 14.787,14.421L17.236,14.421C17.684,14.421 18.112,14.064 18.192,13.623L20.5,0.851C20.582,0.41 20.283,0.053 19.834,0.053L16.69,0.053C16.373,0.053 16.053,0.235 15.87,0.517L9.897,9.473C9.803,9.616 9.578,9.578 9.528,9.41L7.074,0.517C6.992,0.235 6.738,0.053 6.421,0.053L3.277,0.053Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M21.146,14.421C21.146,14.421 33.257,14.421 33.257,14.421C33.526,14.421 33.784,14.205 33.831,13.942L34.337,11.128C34.385,10.863 34.206,10.649 33.936,10.649L25.519,10.649C25.429,10.649 25.37,10.576 25.385,10.488L25.635,9.105C25.65,9.017 25.736,8.944 25.826,8.944L32.596,8.944C32.865,8.944 33.123,8.728 33.171,8.465L33.621,5.974C33.669,5.709 33.49,5.495 33.221,5.495L26.45,5.495C26.361,5.495 26.301,5.423 26.317,5.335L26.584,3.852C26.599,3.764 26.685,3.691 26.775,3.691L35.192,3.691C35.462,3.691 35.719,3.476 35.767,3.21L36.258,0.498C36.306,0.235 36.126,0.019 35.857,0.019L23.746,0.019C23.297,0.019 22.867,0.378 22.788,0.819L20.474,13.621C20.396,14.062 20.695,14.421 21.146,14.421Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M45.926,14.419L45.926,14.421L46.346,14.421C48.453,14.421 50.465,12.742 50.839,10.67L51.081,9.327C51.456,7.256 50.05,5.576 47.943,5.576L41.455,5.576C41.186,5.576 41.007,5.363 41.054,5.097L41.218,4.192C41.266,3.927 41.524,3.713 41.793,3.713L50.569,3.713C51.018,3.713 51.446,3.356 51.526,2.915L51.9,0.85C51.98,0.407 51.68,0.05 51.232,0.05L41.638,0.05C39.531,0.05 37.519,1.73 37.145,3.801L36.88,5.267C36.505,7.339 37.91,9.018 40.018,9.018L46.506,9.018C46.775,9.018 46.954,9.231 46.907,9.497L46.785,10.176C46.737,10.441 46.479,10.655 46.21,10.655L37.189,10.655C36.741,10.655 36.313,11.012 36.233,11.453L35.841,13.621C35.761,14.062 36.061,14.419 36.51,14.419L45.926,14.419Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M68.008,0.046C68.008,0.046 65.296,0.046 65.296,0.046C64.847,0.046 64.42,0.403 64.34,0.844L63.532,5.31C63.517,5.398 63.431,5.469 63.341,5.469L58.085,5.469C57.995,5.469 57.936,5.398 57.951,5.31L58.758,0.844C58.837,0.403 58.539,0.046 58.09,0.046L55.378,0.046C54.93,0.046 54.502,0.403 54.422,0.844L52.112,13.623C52.032,14.064 52.331,14.421 52.78,14.421L55.492,14.421C55.941,14.421 56.369,14.064 56.449,13.623L57.272,9.074C57.287,8.986 57.373,8.914 57.462,8.914L62.719,8.914C62.809,8.914 62.868,8.985 62.853,9.074L62.032,13.623C61.952,14.064 62.252,14.421 62.7,14.421L65.413,14.421C65.861,14.421 66.289,14.064 66.369,13.623L68.678,0.844C68.755,0.403 68.457,0.046 68.008,0.046Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M72.099,14.421C72.099,14.421 80.066,14.421 80.066,14.421C80.515,14.421 80.943,14.064 81.022,13.623L81.414,11.453C81.494,11.012 81.194,10.655 80.746,10.655L73.828,10.655C73.559,10.655 73.38,10.441 73.427,10.176L74.51,4.215C74.558,3.951 74.815,3.736 75.082,3.736L82,3.736C82.448,3.736 82.876,3.379 82.956,2.938L83.34,0.817C83.42,0.376 83.12,0.019 82.672,0.019L74.724,0.019C72.622,0.019 70.614,1.691 70.236,3.757L68.965,10.665C68.587,12.738 69.99,14.421 72.099,14.421Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M97.176,-0C97.176,0 88.882,0 88.882,0C86.775,0 84.763,1.68 84.389,3.751L83.139,10.67C82.765,12.741 84.169,14.421 86.277,14.421L94.571,14.421C96.678,14.421 98.69,12.741 99.064,10.67L100.314,3.751C100.689,1.68 99.284,-0 97.176,-0ZM94.798,10.178C94.75,10.443 94.492,10.657 94.223,10.657L87.978,10.657C87.709,10.657 87.529,10.443 87.577,10.178L88.659,4.192C88.707,3.927 88.964,3.713 89.234,3.713L95.477,3.713C95.747,3.713 95.926,3.927 95.878,4.192L94.798,10.178Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M101.284,14.421L103.995,14.421C104.443,14.421 104.871,14.065 104.951,13.624L105.43,10.97C105.446,10.882 105.531,10.81 105.621,10.81L108.902,10.806C109.064,10.806 109.2,10.886 109.267,11.018L110.813,14.035C110.992,14.392 111.319,14.434 112.303,14.419C112.88,14.426 113.756,14.382 115.169,14.382C115.623,14.382 115.902,13.907 115.678,13.51L113.989,10.569C113.945,10.491 113.993,10.386 114.086,10.34C115.39,9.707 116.423,8.477 116.681,7.055L117.27,3.785C117.646,1.713 116.242,0.033 114.134,0.033L103.884,0.033C103.436,0.033 103.008,0.39 102.928,0.831L100.616,13.623C100.536,14.064 100.836,14.421 101.284,14.421L101.284,14.421ZM106.73,3.791C106.745,3.703 106.831,3.631 106.921,3.631L112.225,3.631C112.626,3.631 112.891,3.949 112.821,4.343L112.431,6.494C112.359,6.885 111.979,7.204 111.58,7.204L106.276,7.204C106.186,7.204 106.127,7.133 106.142,7.043L106.73,3.791Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M118.277,14.421C118.277,14.421 130.388,14.421 130.388,14.421C130.657,14.421 130.915,14.205 130.963,13.942L131.468,11.128C131.516,10.863 131.337,10.649 131.068,10.649L122.65,10.649C122.56,10.649 122.501,10.576 122.516,10.488L122.766,9.105C122.781,9.017 122.867,8.944 122.957,8.944L129.728,8.944C129.997,8.944 130.254,8.728 130.302,8.465L130.753,5.974C130.801,5.709 130.621,5.495 130.352,5.495L123.581,5.495C123.492,5.495 123.432,5.423 123.448,5.335L123.715,3.852C123.73,3.764 123.816,3.691 123.906,3.691L132.324,3.691C132.593,3.691 132.851,3.476 132.898,3.21L133.389,0.498C133.437,0.235 133.257,0.019 132.988,0.019L120.877,0.019C120.428,0.019 119.999,0.378 119.919,0.819L117.605,13.621C117.527,14.062 117.827,14.421 118.277,14.421Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
1
img/nano_g2.svg
Normal file
|
After Width: | Height: | Size: 22 KiB |
1
img/nrf52.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 342 300" width="342" height="300"><style>.a{fill:none}.b{opacity:.6;fill:#00a9ce}.c{opacity:.8;fill:#00a9ce}.d{opacity:.5;fill:#00a9ce}.e{fill:#00a9ce}</style><path class="a" d=""/><path class="b" d="m243.6 1.9c0 0-0.5 0 0 0-1.4 0.9-1.9 1.4-2.8 1.4l-72.6 41.7 84.3 48.8v-93.8c-2.8 0-6.1 0.9-8.9 1.9z"/><path class="c" d="m95.1 295.8l76.8-44.5-84.3-48.8v95.2c2.4 0.4 4.7-0.5 7.5-1.9z"/><path class="d" d="m338.7 246.1l-172.9-99.9-7.9-4.6-156-90.5c-1 2.8-1.9 5.6-1.9 8.9 0 0.5 0 1.4 0 1.9v179c0 5.7 3.3 10.8 8 13.6l71.2 41.8c2.3 1.4 5.1 1.8 7.9 1.8v-91.4-3.7l161.7 93.3c0.4 0.4 0.9 0.4 1.4 0.4h0.4c4.7 1.9 8.5 0.5 12.7-2.3l27.2-15.5 43.1-25.8c2.3-1.4 4.2-4.2 5.1-7z"/><path class="e" d="m339.7 128v-68c0-8.9-4.7-16.9-12.2-21.1l-62.8-36.1c-3.7-1.9-8-2.8-12.2-2.8v94.2l-141-82-15.9-8.9c-7.1-3.8-13.1-4.2-20.2-1 0 0-0.4 0-0.4 0.5-1 0.5-1.4 1-2.4 1.4l-24.8 14.5-35.6 20.7c-4.7 2.8-8.5 7-10.8 12.2l156 90 8 4.6 172.9 99.9c0.9-3.3 0.9-8.9 0.9-8.9v-109.2z"/></svg>
|
||||
|
After Width: | Height: | Size: 1,022 B |
|
Before Width: | Height: | Size: 88 KiB |
1
img/rak_11300.svg
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
img/rak_4631.png
|
Before Width: | Height: | Size: 66 KiB |
1
img/rak_4631.svg
Normal file
|
After Width: | Height: | Size: 134 KiB |
1
img/rak_wismesh_tag.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
1
img/rpi.svg
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
1
img/rpi_picow.svg
Normal file
|
After Width: | Height: | Size: 310 KiB |
|
Before Width: | Height: | Size: 65 KiB |
1
img/sensecap_solar.svg
Normal file
|
After Width: | Height: | Size: 10 KiB |
1
img/sensecap_t1000e.svg
Normal file
|
After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 52 KiB |
1
img/station_g2.svg
Normal file
|
After Width: | Height: | Size: 58 KiB |
1
img/thinknode_m1.svg
Normal file
|
After Width: | Height: | Size: 41 KiB |
1
img/thinknode_m2.svg
Normal file
|
After Width: | Height: | Size: 17 KiB |
1
img/thinknode_m3.svg
Normal file
|
After Width: | Height: | Size: 13 KiB |
1
img/thinknode_m5.svg
Normal file
|
After Width: | Height: | Size: 42 KiB |
1
img/thinknode_m6.svg
Normal file
|
After Width: | Height: | Size: 22 KiB |
1
img/wio_tracker_l1.svg
Normal file
|
After Width: | Height: | Size: 28 KiB |
1
img/wio_tracker_l1_eink.svg
Normal file
|
After Width: | Height: | Size: 46 KiB |
1
img/xiao_esp32c3.svg
Normal file
|
After Width: | Height: | Size: 62 KiB |
1
img/xiao_esp32c6.svg
Normal file
|
After Width: | Height: | Size: 78 KiB |
1
img/xiao_esp32s3.svg
Normal file
|
After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 96 KiB |
1
img/xiao_nrf52.svg
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
img/xiao_s3.png
|
Before Width: | Height: | Size: 73 KiB |
196
index.html
|
|
@ -9,9 +9,14 @@
|
|||
<link href="./css/flasher.css" rel="stylesheet">
|
||||
<script type="module" src="./flasher.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="dark">
|
||||
<div id="app" v-cloak>
|
||||
<div class="flash-container">
|
||||
<div class="snackbar" :class="snackbar.class" popover>
|
||||
<i v-if="snackbar.icon">{{ snackbar.icon }}</i>
|
||||
<span v-html="snackbar.text"></span>
|
||||
</div>
|
||||
<div class="flash-container" data-iframe-height>
|
||||
<div v-if="flashing.active">
|
||||
<header>
|
||||
<nav>
|
||||
|
|
@ -19,7 +24,7 @@
|
|||
<span class="small">{{ selected.device.name }}</span>
|
||||
<i>chevron_right</i>
|
||||
<i>{{ selected.firmware.icon || config.role[selected.firmware.role].icon }}</i>
|
||||
<span class="small">{{ selected.firmware.title || config.role[selected.firmware.role].title }}</span>
|
||||
<span class="small strong">{{ selected.firmware.title || config.role[selected.firmware.role].title }}</span>
|
||||
</nav>
|
||||
</header>
|
||||
<article v-if="flashing.error">
|
||||
|
|
@ -27,31 +32,49 @@
|
|||
<div class="max">
|
||||
<h6>Flashing failed!</h6>
|
||||
<p><span>{{ flashing.error }}</span></p>
|
||||
<p><button @click="refresh()">Retry</button></p>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="small-margin">
|
||||
<button @click="retry">
|
||||
<i>bolt</i>
|
||||
<span>Retry</span>
|
||||
</button>
|
||||
<button @click="close">
|
||||
<i>arrow_back</i>
|
||||
<span>Close</span>
|
||||
</button>
|
||||
</nav>
|
||||
</article>
|
||||
<article v-else>
|
||||
<div class="row">
|
||||
<div class="max" v-if="flashing.percentage < 100">
|
||||
<div class="max" v-if="flashing.percent < 100">
|
||||
<h6><progress class="circle small"></progress> Flashing...</h6>
|
||||
<p>Please do not disconnect the device</p>
|
||||
</div>
|
||||
<div class="max" v-else>
|
||||
<h6>Flashing complete!</h6>
|
||||
<p v-if="selected.firmware.role === 'roomServer' || selected.firmware.role === 'repeater'">
|
||||
Before you use <b><a target="_blank" href="https://config.meshcore.dev/">Repeater/Room server setup</a></b>, please restart your device.
|
||||
</p>
|
||||
<p>
|
||||
<button @click="flasherCleanup()">Close</button>
|
||||
</p>
|
||||
<nav class="small-margin">
|
||||
<button @click="openSerialGUI()" v-if="selected.firmware.role === 'roomServer' || selected.firmware.role === 'repeater'">
|
||||
<i>manufacturing</i>
|
||||
<span>Configure via USB</span>
|
||||
<div class="tooltip left max">Open GUI to configure Repeaters and Room servers via USB serial</div>
|
||||
</button>
|
||||
<button @click="close">
|
||||
<i>arrow_back</i>
|
||||
<span>Close</span>
|
||||
</button>
|
||||
<button @click="retry">
|
||||
<i>bolt</i>
|
||||
<span>Flash Again</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="autoscroller">
|
||||
<pre class="term" v-if="flashing.log">{{ flashing.log }}</pre>
|
||||
</div>
|
||||
<nav>
|
||||
<progress :value="flashing.percentage" max="100"></progress>
|
||||
<progress :value="flashing.percent" max="100"></progress>
|
||||
</nav>
|
||||
</article>
|
||||
</div>
|
||||
|
|
@ -62,9 +85,18 @@
|
|||
<i>developer_board</i>
|
||||
<a class="small" href="javascript:;" @click="stepBack">{{ selected.device.name }}</a>
|
||||
<i>chevron_right</i>
|
||||
<i>{{ getRoleFwValue(selected.firmware, 'icon') }}</i>
|
||||
<span class="small">{{ getRoleFwValue(selected.firmware, 'title') }}</span>
|
||||
</nav>
|
||||
<article class="no-margin">
|
||||
<ul class="list">
|
||||
<li>
|
||||
<i>{{ getRoleFwValue(selected.firmware, 'icon') }}</i>
|
||||
<div class="max">
|
||||
<h6 class="small strong">{{ getRoleFwValue(selected.firmware, 'title') }} {{ getRoleFwValue(selected.firmware, 'subTitle') }}</h6>
|
||||
<span class="text-wrap">{{ getRoleFwValue(selected.firmware, 'tooltip') }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
<nav class="version">
|
||||
<div class="field label suffix border small">
|
||||
<select v-model="selected.version">
|
||||
|
|
@ -75,8 +107,8 @@
|
|||
<label>Version</label>
|
||||
<i>arrow_drop_down</i>
|
||||
</div>
|
||||
<div class="max small-padding pre" v-if="getSelFwValue('notes')">
|
||||
{{ getSelFwValue('notes') }}
|
||||
<div class="max pre" v-if="getSelFwValue('notes')">
|
||||
<b>Changelog:</b><br>{{ getSelFwValue('notes').replace('Change log:\r\n', '') }}
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
|
@ -94,34 +126,54 @@
|
|||
</ul>
|
||||
<template v-if="selected.device.type === 'nrf52'">
|
||||
<div class="small-space"></div>
|
||||
<button @click="dfuMode" :disabled="flashing.dfuComplete || !flashing.supported">
|
||||
<i>{{ flashing.dfuComplete ? 'check' : 'code' }}</i>
|
||||
<span>{{ flashing.dfuComplete ? 'DFU mode active' : 'Enter DFU mode' }}</span>
|
||||
<div class="tooltip right max" v-if="flashing.supported">
|
||||
Enter DFU mode - this mode enables you to flash your firmware.
|
||||
If you did not trigger the DFU mode manually, please click this button.
|
||||
</div>
|
||||
<div class="tooltip right max" v-else>
|
||||
Your browser doesn't support Web Serial API. Please use Chrome or Edge on Desktop
|
||||
</div>
|
||||
</button>
|
||||
<button @click="dfuMode" :disabled="selected.nrfEraserFlashing || !flashing.supported">
|
||||
<i>{{ flashing.dfuComplete ? 'check' : 'code' }}</i>
|
||||
<span>{{ flashing.dfuComplete ? 'DFU mode active' : 'Enter DFU mode' }}</span>
|
||||
<div class="tooltip right max" v-if="flashing.supported">
|
||||
Enter DFU mode - this mode enables you to flash your firmware.
|
||||
If you did not trigger the DFU mode manually, please click this button.
|
||||
</div>
|
||||
<div class="tooltip right max" v-else>
|
||||
Your browser doesn't support Web Serial API. Please use Chrome or Edge on Desktop
|
||||
</div>
|
||||
</button>
|
||||
<button @click="nrfErase" :disabled="!flashing.supported || selected.nrfEraserFlashing || selected.nrfEraserFlashingPercent > 0">
|
||||
<i v-if="selected.nrfEraserFlashingPercent === 100">check</i>
|
||||
<progress v-else-if="selected.nrfEraserFlashing" class="circle small"></progress>
|
||||
<i v-else>delete_forever</i>
|
||||
|
||||
<span v-if="selected.nrfEraserFlashingPercent === 100">Flash Erased!</span>
|
||||
<span v-if="selected.nrfEraserFlashingPercent > 0">Flashing erase firmware: {{selected.nrfEraserFlashingPercent}}%</span>
|
||||
<span v-else-if="selected.nrfEraserFlashing">Connecting...</span>
|
||||
<span v-else>Erase Flash</span>
|
||||
|
||||
<div class="tooltip right max" v-if="flashing.supported">
|
||||
Flash special firmware that will format your flash and erase all user data.<br>
|
||||
Click <b>Enter DFU mode</b> before erasing, if you did not trigger the DFU mode manually
|
||||
</div>
|
||||
<div class="tooltip right max" v-else>
|
||||
Your browser doesn't support Web Serial API. Please use Chrome or Edge on Desktop
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
<div class="medium-space"></div>
|
||||
<nav class="small-margin">
|
||||
<button @click="flashDevice" :disabled="!flashing.supported">
|
||||
<i>bolt</i>
|
||||
<span>Flash!</span>
|
||||
<div class="tooltip right max" v-if="flashing.supported">
|
||||
Upload the firmware into your device. Existing firwmare will get overwritten.
|
||||
<span v-if="selected.device.type === 'nrf52'">
|
||||
If you did not trigger DFU mode manually, use the <b>Enter DFU mode</b> before flashing
|
||||
</span>
|
||||
</div>
|
||||
<div class="tooltip right max" v-else>
|
||||
Your browser doesn't support Web Serial API. Please use Chrome or Edge on Desktop
|
||||
</div>
|
||||
</button>
|
||||
<div class="max"></div>
|
||||
<template v-if="canFlash(selected.device)">
|
||||
<button @click="flashDevice" :disabled="selected.nrfEraserFlashing || !flashing.supported">
|
||||
<i>bolt</i>
|
||||
<span>Flash!</span>
|
||||
<div class="tooltip right max" v-if="flashing.supported">
|
||||
Upload the firmware into your device. Existing firwmare will get overwritten.
|
||||
<span v-if="selected.device.type === 'nrf52'">
|
||||
If you did not trigger DFU mode manually, use the <b>Enter DFU mode</b> before flashing
|
||||
</span>
|
||||
</div>
|
||||
<div class="tooltip right max" v-else>
|
||||
Your browser doesn't support Web Serial API. Please use Chrome or Edge on Desktop
|
||||
</div>
|
||||
</button>
|
||||
<div class="max"></div>
|
||||
</template>
|
||||
<button data-ui="#down" class="active" v-if="!getSelFwValue('customFile')">
|
||||
<i>download</i>
|
||||
<span>Download</span><i>arrow_drop_down</i>
|
||||
|
|
@ -129,6 +181,9 @@
|
|||
<li v-for="file in getSelFwValue('files')">
|
||||
<a data-ui="menu-selector" :href="getFirmwarePath(file)" download>{{ file.title }}</a>
|
||||
</li>
|
||||
<li v-if="selected.device.type === 'nrf52' && selected.device.erase">
|
||||
<a data-ui="menu-selector" :href="`${config.staticPath}/${selected.device.erase.replace('.zip', '.uf2')}`" download>{{ selected.device.erase.replace('.zip', '.uf2') }}</a>
|
||||
</li>
|
||||
</menu>
|
||||
<div class="tooltip left max">Download a copy of the firmware files for use with other flashers</div>
|
||||
</button>
|
||||
|
|
@ -139,7 +194,7 @@
|
|||
<nav>
|
||||
<button class="circle transparent" @click="stepBack"><i>arrow_back</i></button>
|
||||
<i>developer_board</i>
|
||||
<span>{{ selected.device.name }}</span>
|
||||
<span class="small strong">{{ selected.device.name }}</span>
|
||||
</nav>
|
||||
<nav class="no-margin">
|
||||
<h6 class="small max">Choose role</h6>
|
||||
|
|
@ -161,7 +216,9 @@
|
|||
<header>
|
||||
<nav>
|
||||
<i>bolt</i>
|
||||
<h5 class="small max">MeshCore flasher</h5>
|
||||
<h5 class="small max logo-top">
|
||||
<img src="img/meshcore.svg"> <span>flasher</span>
|
||||
</h5>
|
||||
<button class="transparent hide-mobile" @click="openSerialGUI()">
|
||||
<i>manufacturing</i>
|
||||
<span>Repeater Setup</span>
|
||||
|
|
@ -177,24 +234,54 @@
|
|||
<h6 class="small max">Choose device</h6>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="field label prefix suffix border w-100 no-margin">
|
||||
<i>search</i>
|
||||
<input type="text" placeholder=" " v-model="deviceFilterText" autofocus>
|
||||
<label>Filter</label>
|
||||
</div>
|
||||
<ul class="list border">
|
||||
<li v-for="device in config.device">
|
||||
<button class="transparent" @click="selected.device = device">
|
||||
<i>developer_board</i>
|
||||
<span>{{ device.name }}</span>
|
||||
<div class="tooltip right max" v-if="device.tooltip" v-html="device.tooltip"></div>
|
||||
</button>
|
||||
<li v-for="(deviceGroup, type) in devices">
|
||||
<details open>
|
||||
<summary style="background-color: var(--surface-container)">
|
||||
<div>
|
||||
<strong>{{ type === 'community' ? 'Community Firmware' : 'Ripple Firmware' }}</strong>
|
||||
<div class="tooltip right max">
|
||||
<template v-if="type === 'ripple'">
|
||||
Freemium firmware provided by <a class="inverse-link" target="_blank" href="https://buymeacoffee.com/ripplebiz">Ripple Radios</a>
|
||||
</template>
|
||||
<template v-else>
|
||||
Open Source <a class="inverse-link" target="_blank" href="https://github.com/meshcore-dev/MeshCore">Community firmware</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</summary>
|
||||
<ul class="list border">
|
||||
<li v-for="device in deviceGroup">
|
||||
<template v-if="device.firmware.length > 0">
|
||||
<button class="device-select transparent" @click="selected.device = device">
|
||||
<img class="icon" :title="device.type" :src="`/img/${device.type}.svg`" v-if="device.type !== 'noflash'">
|
||||
<i v-else>developer_board</i>
|
||||
<span>{{ device.name }}</span>
|
||||
<div class="tooltip right max" v-if="device.tooltip" v-html="device.tooltip"></div>
|
||||
<div class="max"></div>
|
||||
</button>
|
||||
<div class="max"></div>
|
||||
<img class="icon" :src="device.icon" v-if="device.icon" >
|
||||
</template>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
<li>
|
||||
<button class="transparent">
|
||||
<i>unknown_document</i>
|
||||
<span>Custom Firmware</span>
|
||||
<label>
|
||||
<i>unknown_document</i>
|
||||
<strong style="font-size: small;">Custom Firmware</strong>
|
||||
<div class="tooltip right max">
|
||||
Flash custom firmware file from your computer.
|
||||
Flasher supports <b>.bin</b> files for esp32 devices and OTA <b>.zip</b> files for nRF52 devices.
|
||||
</div>
|
||||
<input type="file" accept=".zip,.bin" @change="customFirmwareLoad">
|
||||
</button>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -209,7 +296,7 @@
|
|||
<h6 class="small max">Serial Console</h6>
|
||||
</nav>
|
||||
</header>
|
||||
<pre class="console" @click="consoleEditBox.focus()" ref="consoleWindow">
|
||||
<pre class="console" ref="consoleWindow" tabindex="0" @mouseup="consoleMouseUp">
|
||||
<code>{{ serialCon.content }}</code>
|
||||
<div class="holder">
|
||||
<span>></span>
|
||||
|
|
@ -217,8 +304,7 @@
|
|||
</div>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script src="./lib/iframeResizer.contentWindow.min.js"></script>
|
||||
</body>
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@open-iframe-resizer/core@latest/dist/index.js"></script>
|
||||
</html>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
function delay(msecs) {
|
||||
return new Promise((resolve) => setTimeout(resolve, msecs));
|
||||
}
|
||||
|
||||
class LineBreakTransformer {
|
||||
chunks = '';
|
||||
port = null;
|
||||
|
|
|
|||
63
lib/dfu.js
|
|
@ -80,7 +80,30 @@ function calcCrc16(data, crc = 0xFFFF) {
|
|||
}
|
||||
|
||||
function sleep(milliseconds) {
|
||||
return new Promise((resolve) => setTimeout(resolve, milliseconds))
|
||||
return new Promise((resolve) => {
|
||||
const startTime = performance.now();
|
||||
const targetTime = startTime + milliseconds;
|
||||
|
||||
function checkTime() {
|
||||
if (performance.now() >= targetTime) {
|
||||
resolve();
|
||||
} else {
|
||||
requestAnimationFrame(checkTime);
|
||||
}
|
||||
}
|
||||
|
||||
const initialDelay = Math.max(0, milliseconds - 16); // Wake up ~1 frame early to start polling
|
||||
|
||||
if (initialDelay > 0) {
|
||||
setTimeout(() => {
|
||||
// Now start the more precise polling
|
||||
checkTime();
|
||||
}, initialDelay);
|
||||
} else {
|
||||
// If the desired sleep is very short, go straight to polling
|
||||
checkTime();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- HciPacket Class (adapted from dfu/dfu_transport_serial.py) ---
|
||||
|
|
@ -128,6 +151,29 @@ export class Dfu {
|
|||
this.eraseBeforeUpdate = eraseBeforeUpdate; // Store the erase flag
|
||||
}
|
||||
|
||||
getReader() {
|
||||
const reader = this.port.readable.getReader();
|
||||
|
||||
return {
|
||||
read() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeoutHandle = setTimeout(() => {
|
||||
reader.releaseLock();
|
||||
reject(new Error("Read timeout"));
|
||||
}, DEFAULT_SERIAL_PORT_TIMEOUT * 1000 * 5)
|
||||
|
||||
reader.read().then(result => {
|
||||
clearTimeout(timeoutHandle);
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
},
|
||||
releaseLock() {
|
||||
return reader.releaseLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async sendPacket(pkt) {
|
||||
if (!this.port || !this.port.writable) {
|
||||
throw new Error("Serial port not open or not writable.");
|
||||
|
|
@ -149,7 +195,7 @@ export class Dfu {
|
|||
throw new Error("Serial port not open or not readable.");
|
||||
}
|
||||
|
||||
const reader = this.port.readable.getReader();
|
||||
const reader = this.getReader();
|
||||
let buffer = [];
|
||||
let c0Count = 0;
|
||||
|
||||
|
|
@ -169,13 +215,12 @@ export class Dfu {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Date.now() - startTime > DEFAULT_SERIAL_PORT_TIMEOUT * 1000 * 5) { // Increased timeout for safety
|
||||
HciPacket.sequenceNumber = 0; // Reset sequence number on timeout.
|
||||
throw new Error("Timeout waiting for ACK.");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
catch(e) {
|
||||
HciPacket.sequenceNumber = 0;
|
||||
}
|
||||
finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
// Decode SLIP
|
||||
|
|
@ -216,7 +261,7 @@ export class Dfu {
|
|||
} else if (data[i] === 0xC0) {
|
||||
// Ignore 0xC0 (start/end of packet)
|
||||
}
|
||||
else {
|
||||
else {
|
||||
result.push(data[i]);
|
||||
}
|
||||
i++;
|
||||
|
|
|
|||