1
0
forked from ctmesh/web

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
This commit is contained in:
2026-06-08 20:31:45 -04:00
parent ed56ce1098
commit e69537c34c
3 changed files with 340 additions and 22 deletions
+313
View File
@@ -0,0 +1,313 @@
<!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>Configure MeshCore MQTT Upload | CT Mesh</title>
<meta name="description" content="How to configure a MeshCore repeater to upload traffic to the CT Mesh MQTT system.">
<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>How to Configure Your Repeater to Upload to the CT Mesh MQTT System</h1>
<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 also requires custom observer firmware on the radio. See <a href="https://analyzer.letsmesh.net/observer/onboard" target="_blank">custom repeater firmware setup</a> before configuring the broker.</p>
<h2>Configuration</h2>
<p>Configure meshcoretomqtt with a <a href="https://github.com/Cisien/meshcoretomqtt" target="_blank">Toml file</a> at <code>/etc/mctomqtt/config.d/00-user.toml</code>. In your existing <code>[general]</code> section, set the region to <code>iata = "BDL"</code>. Then add the custom broker block below at the end of the config file. Fill in your own broker credentials if you were issued dedicated credentials.</p>
<div class="code-block">
<button class="copy-btn" type="button" data-copy-target="meshcore-mqtt">Copy all</button>
<pre id="meshcore-mqtt">[[broker]]
name = "ctmesh"
enabled = true
server = "mqtt.ctmesh.org"
port = 1883
transport = "tcp"
keepalive = 60
qos = 0
retain = true
[broker.auth]
method = "password"
username = "meshdev"
password = "large4cats"</pre>
</div>
<p class="note"><strong>Note:</strong> The <code>meshdev</code>/<code>large4cats</code> credentials are intentionally shared secrets for this broker. We recognize the security implications. Specific credentials to this broker can be requested.</p>
<h2>Testing</h2>
<p>After the service is running, check the <a href="https://meshcore-map.ctmesh.org/" target="_blank">MeshCore Map</a>, <a href="https://bdl.meshmapper.net/" target="_blank">MeshMapper</a>, and <a href="https://analyzer.letsmesh.net/packets?region=BDL" target="_blank">MeshCore Analyzer</a> to confirm that traffic from the repeater is being reported.</p>
<p>MQTT messages sent to channels such as <code>#test</code> are also mirrored into the <a href="https://discord.com/channels/1359317424301801512/1484659122988126339" target="_blank">Discord #test channel</a> by a bot. This gives you a quick way to test whether the main areas of the mesh can hear your new repeater, and whether companion nodes behind the repeater are making it through.</p>
<h2>Related Guides</h2>
<p><a href="repeater-setup.html">CT Mesh repeater setup guide</a></p>
<p><a href="meshcore-resources.html">MeshCore resources</a></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>