1
0
forked from ctmesh/web
Files
web/meshcore-resources.html
T
jared e69537c34c Refine MeshCore repeater setup guidance
* Add a dedicated CT Mesh MQTT upload configuration page
* Link the resources page to the MQTT upload guide and MeshCore CLI command reference
* Add common repeater commands, MeshMapper, neighbor discovery, and Discord #test validation guidance
* Update the recommended flood advert interval to 47 hours
2026-06-08 20:31:45 -04:00

303 lines
9.6 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>CT Mesh MeshCore Resources</title>
<link rel="shortcut icon" type="image/x-icon" href="favicon.png?3">
<link rel="icon" type="image/png" href="favicon.png?3">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
<style>
html {
margin: 0;
padding: 0;
width: 100%;
min-height: 100%;
background-color: #272727;
}
body {
margin: 0;
padding: 0;
width: 100%;
min-height: 100%;
font-family: "Roboto", sans-serif;
background-color: transparent;
color: #2C2D3C;
display: flex;
align-items: flex-start;
justify-content: center;
padding: 2em 0;
}
body::before {
content: "";
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('background.svg') no-repeat center center;
background-size: cover;
filter: invert(1) brightness(0.85);
z-index: 0;
}
a,
a:visited {
color: #000000;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(80, 80, 80, 0.35);
pointer-events: none;
z-index: 0;
}
.content-box {
background-color: #EBEBEB;
border-radius: 30px;
padding: 2.5em 3em;
margin: 2.5em;
max-width: 820px;
width: 90%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
position: relative;
z-index: 1;
}
h1 {
margin-top: 0;
font-size: 28px;
}
h2 {
margin-top: 1.5em;
margin-bottom: 0.5em;
font-size: 20px;
}
p {
margin: 0.5em 0;
line-height: 1.4;
}
ul {
margin: 0.5em 0 0.5em 1.25em;
padding: 0;
line-height: 1.5;
}
.community-tools li {
margin-bottom: 0.5em;
}
.note {
background: #F5F5F5;
border-left: 4px solid #2B3A4E;
border-radius: 8px;
padding: 0.75em 1em;
margin: 0.75em 0 0.5em;
font-size: 0.95em;
}
.code-block {
background: #F5F5F5;
border-radius: 12px;
padding: 1em;
border: 1px solid #D0D0D0;
margin: 0.75em 0;
position: relative;
}
.code-block pre {
margin: 0;
white-space: pre-wrap;
font-family: monospace;
font-size: 0.95em;
}
.copy-btn {
position: absolute;
top: 0.75em;
right: 0.75em;
background-color: #2B3A4E;
color: #ffffff;
border: none;
border-radius: 10px;
padding: 0.4em 0.75em;
font-size: 0.75em;
font-weight: bold;
cursor: pointer;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
background-color: #2B3A4E;
color: #ffffff;
border: none;
padding: 0.75em 1.5em;
border-radius: 999px;
cursor: pointer;
font-weight: bold;
font-size: 15px;
text-decoration: none;
}
.page-header-bg {
background-color: #272727;
border-radius: 20px 20px 0 0;
margin: -2.5em -3em 1.5em -3em;
padding: 2em 3em;
display: flex;
justify-content: center;
}
.page-header {
display: flex;
align-items: center;
gap: 0.5em;
text-decoration: none;
}
.page-header img {
width: 300px;
height: auto;
}
.page-header span {
font-weight: bold;
font-size: 19px;
color: #e0e0e0;
}
.back-link,
.back-link:visited {
margin-top: 1.5em;
color: #ffffff;
display: inline-flex;
align-items: center;
gap: 6px;
}
.back-link::before {
content: "";
width: 16px;
height: 16px;
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2.5L3.5 6L8 9.5'/%3E%3C/svg%3E") no-repeat center;
background-size: contain;
flex-shrink: 0;
opacity: 0.4;
filter: invert(1);
}
a[target="_blank"]::after {
content: "";
display: inline-block;
width: 12px;
height: 12px;
vertical-align: middle;
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5L8.5 3.5M5 3.5h3.5V7'/%3E%3C/svg%3E") no-repeat center;
background-size: contain;
opacity: 0.4;
text-decoration: underline;
}
.btn[target="_blank"]::after,
.copy-btn::after,
.site-footer a::after {
display: none;
}
.site-footer {
margin-top: 2em;
padding-top: 2em;
text-align: center;
font-size: 0.875rem;
color: #2C2D3C;
border-top: 1px solid #ccc;
}
.site-footer a {
text-decoration: none;
}
.site-footer a:hover {
text-decoration: underline;
}
@media (max-width: 800px) {
body {
padding-top: calc(env(safe-area-inset-top, 1em));
}
.content-box {
padding: 2em;
}
.page-header-bg {
margin: -2em -2em 1.5em -2em;
padding: 2em;
}
}
@media (max-width: 480px) {
.content-box {
padding: 1.5em;
}
.page-header-bg {
margin: -1.5em -1.5em 1.5em -1.5em;
padding: 1.5em;
}
h1 {
font-size: 22px;
}
h2 {
font-size: 18px;
}
}
</style>
</head>
<body>
<div class="overlay"></div>
<div class="content-box">
<div class="page-header-bg">
<a href="index.html" class="page-header">
<img src="logo_sm.png" alt="CT Mesh" />
</a>
</div>
<h1>MeshCore Resources</h1>
<h2>Web Tools</h2>
<div class="community-tools">
<p><em>These tools use data reported to MQTT using <a href="https://analyzer.letsmesh.net/observer/onboard" target="_blank">Observer Firmware</a> by nodes across the state.</em></p>
<ul>
<li><a href="https://meshcore-map.ctmesh.org/" target="_blank">MeshCore Map</a> <strong>NEW!</strong> &ndash; live map showing MeshCore nodes</li>
<li><a href="https://bdl.meshmapper.net/" target="_blank">MeshMapper</a> <strong>NEW!</strong> &ndash; coverage maps for MeshCore. Contribute via <a href="https://apps.apple.com/us/app/meshmapper/id6758073991" target="_blank">iOS</a> and <a href="https://play.google.com/store/apps/details?id=net.meshmapper.app" target="_blank">Android</a> mobile apps</li>
<li><a href="https://analyzer.letsmesh.net/packets?region=BDL" target="_blank">MeshCore Analyzer</a> &ndash; real-time MeshCore network analytics, node tracking, and packet analysis</li>
</ul>
</div>
<h2>Official Links</h2>
<p><a href="https://meshcore.io/" target="_blank">meshcore.io</a></p>
<p><a href="https://flasher.meshcore.io/" target="_blank">flasher.meshcore.io</a></p>
<p><a href="https://docs.meshcore.io/cli_commands/" target="_blank">MeshCore CLI commands reference</a></p>
<p><a href="repeater-setup.html">CT Mesh repeater setup guide</a></p>
<div class="note"><strong>Note:</strong> the flasher requires WebSerial or Web Bluetooth support in your browser</div>
<h2>MQTT</h2>
<p>Our MQTT broker is <em>uplink-only</em> and designed for fixed nodes across the state to serve as gateway nodes. Its purpose is not to bridge gaps or extend mesh coverage, but to report local traffic to our own web-based tools for analytical data and metrics to assess the mesh's performance. <strong>This role is best suited for stable, well-placed nodes with reliable coverage.</strong></p>
<p>For setup steps, see <a href="meshcore-mqtt-upload.html">How to configure your repeater to upload to the CT Mesh MQTT system</a>.</p>
<p class="note"><strong>Testing note:</strong> MQTT messages sent to channels such as <code>#test</code> are mirrored into the <a href="https://discord.com/channels/1359317424301801512/1484659122988126339" target="_blank">Discord #test channel</a> by a bot. This is useful for checking whether the main mesh can hear a new repeater, or whether companion nodes behind that repeater are making it back to the wider mesh.</p>
<a class="btn back-link" href="index.html">Back to CT Mesh</a>
<footer class="site-footer">
<p><a href="https://ctmesh.org/">CT Mesh</a> is a volunteer-run user group for mesh technology enthusiasts in Connecticut.</p>
<p><a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank" rel="noopener">Content licensed CC BY-SA 4.0</a></p>
</footer>
</div>
<script>
const copyButtons = document.querySelectorAll("[data-copy-target]");
copyButtons.forEach((button) => {
button.addEventListener("click", async () => {
const targetId = button.getAttribute("data-copy-target");
const target = document.getElementById(targetId);
if (!target) return;
try {
await navigator.clipboard.writeText(target.textContent);
button.textContent = "Copied";
setTimeout(() => {
button.textContent = "Copy all";
}, 1500);
} catch (err) {
button.textContent = "Copy failed";
setTimeout(() => {
button.textContent = "Copy all";
}, 1500);
}
});
});
</script>
</body>
</html>