1
0
forked from ctmesh/web
Files
web/meshcore-resources.html

279 lines
8.8 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, body {
margin: 0;
padding: 0;
width: 100%;
min-height: 100%;
}
body {
font-family: "Roboto", sans-serif;
background: url('background.svg') no-repeat center center fixed;
background-size: cover;
background-color: #EBEBEB;
color: #2C2D3C;
display: flex;
align-items: flex-start;
justify-content: center;
padding: 2em 0;
}
a,
a:visited {
color: #000000;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(87, 87, 87, 0.4);
pointer-events: none;
}
.content-box {
background-color: #EBEBEB;
border-radius: 30px;
padding: 2.5em 3em;
margin: 1em;
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 {
display: flex;
align-items: center;
gap: 0.5em;
text-decoration: none;
margin-bottom: 1.5em;
}
.page-header img {
width: 44px;
height: auto;
}
.page-header span {
font-weight: bold;
font-size: 19px;
color: #2C2D3C;
}
.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;
}
}
@media (max-width: 480px) {
.content-box {
padding: 1.5em;
}
h1 {
font-size: 22px;
}
h2 {
font-size: 18px;
}
}
</style>
</head>
<body>
<div class="overlay"></div>
<div class="content-box">
<a href="index.html" class="page-header">
<img src="logo_sm.png" alt="CT Mesh" />
<span>CT Mesh</span>
</a>
<h1>MeshCore Resources</h1>
<h2>Community-run Web Tools</h2>
<div class="community-tools">
<p><em>These tools use data reported to our MQTT server by nodes across the state.</em></p>
<ul>
<li><a href="https://meshcore-map.ctmesh.org/" target="_blank">MeshCore Map</a> <strong>NEW!</strong> - live map showing MeshCore nodes</li>
<li><a href="https://bdl.meshmapper.net/" target="_blank">MeshMapper</a> <strong>NEW!</strong> — coverage maps for MeshCore. Contribute to the map via the MeshMapper mobile apps!</li>
<li><a href="https://meshcore-wardrive.ctmesh.org/" target="_blank">Wardrive Map</a> <i>Legacy</i> - coverage maps for MeshCore. <em>Note: MeshMapper has replaced this for most people.</em></li>
</ul>
</div>
<h2>Official Links</h2>
<p><a href="https://meshcore.co.uk/" target="_blank">meshcore.co.uk</a></p>
<p><a href="https://flasher.meshcore.co.uk/" target="_blank">flasher.meshcore.co.uk</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>MeshCore MQTT uplink uses <a href="https://github.com/Cisien/meshcoretomqtt" target="_blank">meshcoretomqtt</a> on a Raspberry Pi or similar Linux computer with the service installed. This configuration stanza goes in your <code>.env.local</code> file after the first two MQTT servers that upload to analyzer.letsmesh.net. <strong>This also requires a custom firmware.</strong> See <a href="https://analyzer.letsmesh.net/observer/onboard" target="_blank">custom repeater firmware setup</a>.</p>
<div class="code-block">
<button class="copy-btn" type="button" data-copy-target="meshcore-mqtt">Copy all</button>
<pre id="meshcore-mqtt"># MQTT Broker 3 - CT Mesh
MCTOMQTT_MQTT3_ENABLED=true
MCTOMQTT_MQTT3_SERVER=mqtt.ctmesh.org
MCTOMQTT_MQTT3_PORT=1883
MCTOMQTT_MQTT3_USE_TLS=false
MCTOMQTT_MQTT3_USERNAME=meshdev
MCTOMQTT_MQTT3_PASSWORD=large4cats</pre>
</div>
<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>