1
0
forked from ctmesh/web

46 Commits

Author SHA1 Message Date
ed56ce1098 Adds information about setting up a MeshCore repeater 2026-05-10 12:04:48 -04:00
2a58872520 resolve issue with one external link 2026-05-05 14:26:03 -04:00
e7dd2f9bfa additional meshcore.io links 2026-04-23 08:53:33 -04:00
664a45eebc update meshcore.io links 2026-04-23 08:52:23 -04:00
120b8cfdbb new contact email go-live 2026-04-06 22:34:16 -04:00
197e449518 Map subdomain changes, add additional redirects 2026-03-24 10:17:17 -04:00
af040a4096 update redirects 2026-03-18 09:50:10 -04:00
38af7a1e27 fix name 2026-03-09 11:15:16 -04:00
94a0410957 add a rounded icon 2026-03-09 11:13:33 -04:00
afa73644d4 fix cache on logos 2026-03-08 20:27:10 -04:00
7d8e4cd5cc Merge pull request 'New site design & logos' (#7) from staging into master
Reviewed-on: ctmesh/web#7
2026-03-09 00:22:21 +00:00
b4ce029dd6 fix contrast issue with learn button 2026-03-08 20:20:18 -04:00
55a84a03ea redesign header section, background changes, other styling fixes/changes 2026-03-08 18:46:41 -04:00
4a25f00f1b implement new logo design 2026-03-08 17:46:20 -04:00
ba49ce7cf0 Merge pull request 'Updated MQTT configuration information' (#5) from jared/web:feature/mqtt-upload-updates into master
Reviewed-on: ctmesh/web#5
2026-03-08 18:16:19 +00:00
be76e4919e Updated MQTT configuration information 2026-03-07 11:14:38 -05:00
032edad7ac add MeshCore Analyzer to MeshCore resources; verbiage changes 2026-03-06 11:07:09 -05:00
0be6bb21ea Merge pull request 'MeshCore prominence, merge Jared's outreach info, remove old apps' (#4) from staging into master
Reviewed-on: ctmesh/web#4
2026-03-04 02:21:16 +00:00
335c7e7334 mobile display fix for back button not in button group, button color consistency 2026-03-03 09:42:10 -05:00
91664bfb81 Merge pull request 'Improve outreach information' (#3) from jared/web:feature/why-run-node into staging
Reviewed-on: ctmesh/web#3
2026-03-03 14:22:33 +00:00
04a1af17cd Improve outreach information 2026-03-02 22:33:17 -05:00
b23bff5d67 remove legacy wardrive app instead 2026-03-02 13:57:54 -05:00
8b500c4aea old wardrive app now legacy, reorder home for MeshCore prominence 2026-03-02 10:05:52 -05:00
ff5f94d9fd Fix dashes 2026-02-17 21:01:29 -05:00
183fad5ca4 Stage changes to MeshCore resources, mark old wardrive app as legacy, and change verbiage. Will hotlink to mobile apps once published on MeshMapper's website. 2026-02-17 20:57:05 -05:00
9f910b2c1e fix deploy workflow for main branch 2026-02-10 19:10:54 -05:00
d5c5e0e9fa Merge pull request 'staging' (#2) from staging into master
Reviewed-on: ctmesh/web#2
2026-02-11 00:06:38 +00:00
a7fdf8fea5 update wardrive apps, add Discord link centralized redirect, cosmetic fixes to external links 2026-02-10 19:02:34 -05:00
0fe6f5f757 fixes 2026-02-10 15:48:08 -05:00
627c970374 fixes 2026-02-10 15:46:37 -05:00
027b481290 mobile padding, external link arrows on resource pages 2026-02-10 15:35:33 -05:00
a386b37258 alignment of discord button contents 2026-02-10 15:21:56 -05:00
0a48f589e0 minor cosmetic 2026-02-10 14:20:17 -05:00
a6c3b70282 fix code block styling and copy button 2026-02-10 14:17:52 -05:00
89cdaf0791 add a small header on resources pages, forward/back chevron to resources/back buttons 2026-02-10 14:13:15 -05:00
81ea6a8005 slight margin increase between sections 2026-02-10 14:01:00 -05:00
a7dabace24 change footer text, add footer to resources pages 2026-02-10 13:52:24 -05:00
adf5e83991 add gitignore 2026-02-10 13:46:06 -05:00
e4527ce9b1 cleanup 2026-02-10 13:45:41 -05:00
6cc2f95fd4 maybe adjusted workflow to push to staging automatically 2026-02-10 13:40:00 -05:00
dad112a5f8 fix deploy workflow to not push our staging site to prod :D 2026-02-10 13:36:07 -05:00
bc23226733 changes & fixes. fewer buttons, fix scrolling issue with page top cut off, rejigger resources pages with resources on top, other minor cosmetics. 2026-02-10 13:33:37 -05:00
6acb31e2a9 Merge pull request 'Several improvements to the webpage with updated resources' (#1) from jared/web:feature/update-website into staging
Reviewed-on: ctmesh/web#1
2026-02-10 18:28:50 +00:00
c93f87dd8b fixed links 2026-02-07 18:28:50 -05:00
0db84fcbee Updates 2026-02-07 12:24:39 -05:00
542f6d8e3f Several improvements to the webpage with updated resources 2026-02-07 10:37:32 -05:00
18 changed files with 1896 additions and 102 deletions

View File

@ -12,9 +12,16 @@ jobs:
- uses: actions/checkout@v4
- run: mkdir /tmp/deploy
- run: rsync -ua --exclude=".*" . /tmp/deploy
- name: Set deploy branch
run: |
if [ "${{ github.ref_name }}" = "master" ]; then
echo "DEPLOY_BRANCH=main" >> "$GITHUB_ENV"
else
echo "DEPLOY_BRANCH=${{ github.ref_name }}" >> "$GITHUB_ENV"
fi
- name: Build & Deploy Worker
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
command: pages deploy /tmp/deploy --project-name=ctmesh-org
command: pages deploy /tmp/deploy --project-name=ctmesh-org --branch=${{ env.DEPLOY_BRANCH }}

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.DS_Store

7
_redirects Normal file
View File

@ -0,0 +1,7 @@
/discord https://discord.gg/m4F328as3K 302
/merch https://ctmesh.redbubble.com 302
/store https://ctmesh.redbubble.com 302
/map https://map.ctmesh.org 302
/resources https://ctmesh.org/meshcore-resources 302
/coverage https://bdl.meshmapper.net 302
/analyzer https://analyzer.letsmesh.net/packets?region=BDL 302

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 34 KiB

BIN
favicon_sq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -6,74 +6,96 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<!-- Site Description & Keywords -->
<meta name="description" content="A Connecticut Meshtastic User Group. Join the local community to connect, share, and explore with fellow enthusiasts.">
<meta name="keywords" content="CT Mesh, Meshtastic, Connecticut, CT, community, user group, Discord, map">
<meta name="description" content="A Connecticut mesh technologies user group. Join the local community to connect, share, and explore with fellow enthusiasts.">
<meta name="keywords" content="CT Mesh, Mesh, MeshCore, Meshtastic, Connecticut, CT, community, networking, user group, Discord, map, radio">
<meta name="author" content="CT Mesh">
<link rel="shortcut icon" type="image/x-icon" href="favicon.png?3">
<link rel="shortcut icon" type="image/x-icon" href="favicon.png?2026-1">
<!-- Open Graph / Facebook -->
<meta property="og:title" content="CT Mesh">
<meta property="og:description" content="A Connecticut Meshtastic User Group. Join the local community to connect, share, and explore with fellow enthusiasts.">
<meta property="og:image" content="favicon.png?3">
<meta property="og:description" content="A Connecticut mesh technologies user group. Join the local community to connect, share, and explore with fellow enthusiasts.">
<meta property="og:image" content="favicon.png?2026-1">
<meta property="og:url" content="https://ctmesh.org">
<meta property="og:type" content="website">
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="CT Mesh">
<meta name="twitter:description" content="A Connecticut Meshtastic User Group. Join the local community to connect, share, and explore with fellow enthusiasts.">
<meta name="twitter:image" content="favicon.png?3">
<meta name="twitter:description" content="A Connecticut mesh technologies user group. Join the local community to connect, share, and explore with fellow enthusiasts.">
<meta name="twitter:image" content="favicon.png?2026-1">
<!-- Fonts and Icons -->
<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">
<link rel="icon" type="image/png" href="favicon.png?3">
<link rel="apple-touch-icon" sizes="57x57" href="favicon.png?3">
<link rel="apple-touch-icon" sizes="72x72" href="favicon.png?3">
<link rel="apple-touch-icon" sizes="114x114" href="favicon.png?3">
<link rel="apple-touch-icon" sizes="120x120" href="favicon.png?3">
<link rel="apple-touch-icon" sizes="144x144" href="favicon.png?3">
<link rel="apple-touch-icon" sizes="152x152" href="favicon.png?3">
<link rel="apple-touch-icon" sizes="180x180" href="favicon.png?3">
<meta name="msapplication-TileColor" content="#67EA94">
<meta name="msapplication-TileImage" content="favicon.png?3">
<meta name="theme-color" content="#67EA94">
<link rel="icon" type="image/png" href="favicon.png?2026-14">
<link rel="apple-touch-icon" sizes="57x57" href="favicon.png?2026-1">
<link rel="apple-touch-icon" sizes="72x72" href="favicon.png?2026-1">
<link rel="apple-touch-icon" sizes="114x114" href="favicon.png?2026-1">
<link rel="apple-touch-icon" sizes="120x120" href="favicon.png?2026-1">
<link rel="apple-touch-icon" sizes="144x144" href="favicon.png?2026-1">
<link rel="apple-touch-icon" sizes="152x152" href="favicon.png?2026-1">
<link rel="apple-touch-icon" sizes="180x180" href="favicon.png?2026-1">
<meta name="msapplication-TileColor" content="#6EBEE1">
<meta name="msapplication-TileImage" content="favicon.png?2026-1">
<meta name="theme-color" content="#6EBEE1">
<title>CT Mesh</title>
<style>
html, body {
html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
min-height: 100%;
background-color: #272727;
}
body {
margin: 0;
padding: 0;
width: 100%;
min-height: 100%;
font-family: "Roboto", sans-serif;
background: url('background.svg') no-repeat center center fixed;
background-size: cover;
background-color: #EBEBEB;
background-color: transparent;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
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(87, 87, 87, 0.4);
background: rgba(80, 80, 80, 0.35);
pointer-events: none;
z-index: 0;
}
.splash-box {
background-color: #EBEBEB;
border-radius: 30px;
padding: 3em 3em 2em 3em;
margin: 1em;
margin: 2.5em;
width: 90%;
max-width: 700px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
color: #000000;
position: relative;
@ -84,23 +106,42 @@
}
.top-section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1em;
gap: 0.5em;
width: 100%;
padding-bottom: 15px;
background-color: #272727;
border-radius: 20px 20px 0 0;
margin: -3em -3em 0 -3em;
padding: 2.5em 3em 50px 3em;
position: relative;
}
.top-section a img {
.email-link img {
width: 35px;
filter: invert(15%) sepia(17%) saturate(767%) hue-rotate(198deg) brightness(94%) contrast(91%);
filter: brightness(0) invert(0.7);
}
.wave-divider {
position: absolute;
bottom: -1px;
left: 0;
right: 0;
line-height: 0;
}
.wave-divider svg {
width: 100%;
display: block;
}
.logo {
width: 200px;
width: 540px;
height: auto;
padding-right: 30px;
}
.text-content {
text-align: left;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25em;
}
.text-content h1 {
margin: 0;
@ -111,28 +152,33 @@
}
.sub-header {
font-weight: bold;
padding-bottom: 10px;
padding-bottom: 4px;
margin: 0;
color: #e0e0e0;
}
.btn {
display: flex;
align-items: center;
gap: 8px;
gap: 12px;
background-color: #67EA94;
color: #000000;
border: none;
padding: 1em 1.75em;
border-radius: 15px;
border-radius: 999px;
cursor: pointer;
font-weight: bold;
margin-top: 0.5em;
font-size: 17px;
text-align: left;
text-decoration: none;
box-sizing: border-box;
flex: 1 1 240px;
max-width: 260px;
}
.btn-text {
display: flex;
flex-direction: column;
line-height: 0.7;
line-height: 1.1;
}
.btn-text small {
font-size: 12px;
@ -142,16 +188,82 @@
height: 30px;
filter: brightness(0);
}
.btn-discord,
.btn-discord:visited {
background-color: #5865F2;
color: #ffffff;
justify-content: center;
}
.btn-discord img {
filter: brightness(0) invert(1);
}
.btn-meshcore,
.btn-meshcore:visited {
background-color: #2B3A4E;
color: #ffffff;
}
.btn-learn,
.btn-learn:visited {
background-color: #6EBEE1;
color: #000;
}
.btn-learn img {
filter: brightness(1);
}
.btn-meshcore img {
filter: brightness(0) invert(1);
}
a.btn[target="_blank"]::after {
content: "";
width: 16px;
height: 16px;
margin-left: auto;
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;
flex-shrink: 0;
opacity: 0.4;
}
a.btn:not([target="_blank"])::after {
content: "";
width: 16px;
height: 16px;
margin-left: auto;
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='M4 2.5L8.5 6L4 9.5'/%3E%3C/svg%3E") no-repeat center;
background-size: contain;
flex-shrink: 0;
opacity: 0.4;
}
.btn-discord::after {
display: none;
}
.btn-meshcore::after {
filter: invert(1) !important;
}
.btn-group {
display: flex;
gap: 1em;
flex-wrap: nowrap;
flex-wrap: wrap;
justify-content: center;
width: 100%;
}
.btn-groups {
display: flex;
flex-direction: column;
gap: 1em;
gap: 1.5em;
}
.section-title {
margin: 0.5em 0 0;
padding-top: 1em;
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #2C2D3C;
text-align: center;
}
.section {
display: flex;
flex-direction: column;
gap: 0.75em;
}
.btn-small {
display: inline-block;
@ -172,7 +284,6 @@
border-top: 1px solid #ccc;
}
.site-footer a {
color: #757684;
text-decoration: none;
}
.site-footer a:hover {
@ -186,32 +297,33 @@
padding-top: calc(env(safe-area-inset-top, 1em));
}
.splash-box {
margin-top: 1em;
margin-bottom: 1em;
padding: 2em;
min-width: 350px;
margin: 1em 0;
width: calc(100% - 5em);
}
.top-section {
flex-direction: column;
padding-bottom: 20px;
margin: -2em -2em 0 -2em;
padding: 2em 2em 50px 2em;
}
.logo {
width: 150px;
width: 335px;
padding-right: 0;
margin-bottom: 1em;
}
.text-content {
text-align: center;
}
.btn-group {
flex-direction: column;
align-items: center;
}
.section-title {
text-align: center;
}
.btn {
width: 100%;
justify-content: center;
width: 340px;
max-width: 100%;
justify-content: flex-start;
padding: 0.8em 1em;
font-size: 16px;
text-align: left;
flex: none;
}
}
@ -219,12 +331,13 @@
@media (max-width: 480px) {
.splash-box {
padding: 1.5em;
margin: 1em;
width: 90%;
min-width: 250px;
}
.top-section {
margin: -1.5em -1.5em 0 -1.5em;
padding: 1.5em 1.5em 50px 1.5em;
}
.logo {
width: 120px;
width: 300px;
}
.text-content h1 {
font-size: 20px;
@ -243,56 +356,97 @@
<div class="overlay"></div>
<div class="splash-box">
<div class="top-section">
<img src="logo_sm.png" alt="CT Mesh Logo" class="logo" />
<img src="logo_sm.png?2026" alt="CT Mesh Logo" class="logo" />
<div class="text-content">
<h1>CT Mesh</h1>
<p class="sub-header">Connecticut Meshtastic User Group</p>
<a href="mailto:noah@ctmesh.org" rel="nofollow noindex"><img src="email.svg" alt="Email" /></a>
<p class="sub-header">A Connecticut Mesh Technologies User Group</p>
<a href="mailto:contact@ctmesh.org" class="email-link" rel="nofollow noindex"><img src="email.svg" alt="Email" /></a>
</div>
<div class="btn-group" style="margin-bottom: 1em;">
<a href="https://ctmesh.org/discord" target="_blank" class="btn btn-discord">
<img src="discord.svg" alt="Discord" />
<div class="btn-text">
Join our Discord
<small>Chat with the community</small>
</div>
</a>
<a href="why-run-a-node.html" class="btn btn-learn">
<img src="info.svg" alt="Learn about mesh networking" />
<div class="btn-text">
Why Mesh?
<small>Learn about the mesh!</small>
</div>
</a>
</div>
<div class="wave-divider">
<svg viewBox="0 0 1440 60" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0,30 C240,60 480,0 720,30 C960,60 1200,0 1440,30 L1440,60 L0,60 Z" fill="#EBEBEB"/>
</svg>
</div>
</div>
<div class="btn-groups">
<div class="section">
<p class="section-title">MeshCore</p>
<div class="btn-group">
<a href="https://malla.ctmesh.org/" target="_blank" class="btn">
<img src="info.svg" alt="Info Icon" />
<a href="https://meshcore.io/" target="_blank" class="btn btn-meshcore">
<img src="meshcore.svg" alt="MeshCore" />
<div class="btn-text">
Web Dashboard
<small>Network metrics & analytics</small>
MeshCore
<small>About the project</small>
</div>
</a>
<a href="https://map.ctmesh.org/?lat=41.734429390721&lng=287.3501586914063&zoom=10" target="_blank" class="btn">
<img src="map.svg" alt="Map Icon" />
<a href="https://meshcore-map.ctmesh.org/" target="_blank" class="btn btn-meshcore">
<img src="map.svg" alt="Map" />
<div class="btn-text">
CT Map
<small>Nodes seen by CT Mesh</small>
Node Map
<small>Live MeshCore nodes</small>
</div>
</a>
<a href="https://meshtastic.liamcottle.net/?lat=26.58852714730864&lng=285.11718750000006&zoom=2" target="_blank" class="btn">
<img src="globe.svg" alt="Globe Icon" />
<a href="meshcore-resources.html" class="btn btn-meshcore">
<img src="info.svg" alt="Guides & Tools" />
<div class="btn-text">
Global Map
<small>Nodes around the world</small>
Guides & Tools
<small>Wardrive, MQTT, setup</small>
</div>
</a>
<a href="repeater-setup.html" class="btn btn-meshcore">
<img src="info.svg" alt="Repeater Setup" />
<div class="btn-text">
Repeater Setup
<small>Build and program a node</small>
</div>
</a>
</div>
</div>
</div>
<div class="section">
<p class="section-title">Meshtastic</p>
<div class="btn-group">
<a href="https://discord.gg/m4F328as3K" target="_blank" class="btn">
<img src="discord.svg" alt="Discord Icon" />
<div class="btn-text">
Discord
<small>Join the community</small>
</div>
</a>
<a href="https://meshtastic.org/" target="_blank" class="btn">
<img src="meshtastic.svg" alt="Meshtastic Icon" />
<img src="meshtastic.svg" alt="Meshtastic" />
<div class="btn-text">
Meshtastic
<small>Learn more!</small>
<small>About the project</small>
</div>
</a>
<a href="https://meshtastic-map.ctmesh.org/?lat=41.734429390721&lng=287.3501586914063&zoom=10" target="_blank" class="btn">
<img src="map.svg" alt="Map" />
<div class="btn-text">
Node Map
<small>CT Mesh nodes</small>
</div>
</a>
<a href="meshtastic-resources.html" class="btn">
<img src="info.svg" alt="Guides & Tools" />
<div class="btn-text">
Guides & Tools
<small>Channels, MQTT, setup</small>
</div>
</a>
</div>
</div>
<footer class="site-footer">
<p><a href="https://ctmesh.org/">CT Mesh</a> is a volunteer-run user group for <a href="https://meshtastic.org" target="_blank">Meshtastic</a> enthusiasts in Connecticut.</p>
<p><a href="https://ctmesh.org/">CT Mesh</a> is a volunteer-run user group for mesh technology enthusiasts in Connecticut.</p>
<p>Site edits can be submitted via git at <a href="https://git.ctmesh.org/ctmesh/web" target="_blank" rel="noopener">git.ctmesh.org/ctmesh/web</a>.</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>

BIN
logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 KiB

After

Width:  |  Height:  |  Size: 458 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 77 KiB

319
meshcore-resources.html Normal file
View File

@ -0,0 +1,319 @@
<!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="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>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. Configure it with a <a href="https://github.com/Cisien/meshcoretomqtt" target="_blank">Toml file</a> at <code>/etc/mctomqtt/config.d/00-user.toml</code>, with region set in your existing <code>[general]</code> section as <code>iata = "BDL"</code>, and a custom broker named <code>ctmesh</code>. The block below should be added at the end of the config file. Fill in your own broker credentials. <strong>This also requires 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">[[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>
<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>

68
meshcore.svg Normal file
View File

@ -0,0 +1,68 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="822.000000pt" height="391.000000pt" viewBox="0 0 822.000000 391.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.16, written by Peter Selinger 2001-2019
</metadata>
<g transform="translate(0.000000,391.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M265 3878 c-2 -13 -11 -70 -20 -128 -47 -327 -178 -1211 -210 -1420
-13 -91 -25 -171 -25 -177 0 -10 60 -13 270 -13 l269 0 6 33 c3 17 30 205 60
417 31 212 57 389 58 394 2 5 57 -140 123 -322 l119 -332 221 0 222 0 144 223
c79 122 173 268 208 325 35 56 64 101 65 100 3 -3 -101 -766 -109 -810 l-5
-28 264 0 265 0 5 23 c2 12 30 198 60 412 72 499 183 1251 191 1293 l6 32
-318 0 -319 -1 -151 -247 c-255 -415 -424 -686 -439 -701 -11 -12 -37 58 -171
468 l-158 481 -313 0 -313 0 -5 -22z"/>
<path d="M2706 3883 c-3 -10 -15 -83 -26 -163 -37 -253 -110 -740 -130 -870
-11 -69 -38 -246 -60 -395 -22 -148 -43 -280 -45 -292 l-5 -23 780 0 c495 0
780 4 780 10 0 10 46 325 55 373 l5 27 -500 0 c-470 0 -500 1 -500 18 0 15 19
145 35 240 l6 32 439 0 c286 0 440 3 440 10 0 5 11 87 25 182 14 95 25 176 25
180 0 4 -197 9 -438 10 l-438 3 20 143 19 142 502 0 502 0 26 178 c15 97 27
185 27 195 0 16 -44 17 -769 17 -724 0 -770 -1 -775 -17z"/>
<path d="M4753 3889 c-170 -28 -289 -128 -342 -285 -18 -53 -61 -313 -78 -466
-13 -113 39 -208 139 -257 85 -41 141 -48 518 -61 179 -6 341 -16 361 -21 55
-14 74 -42 73 -103 -1 -29 -9 -67 -18 -85 -32 -61 -29 -61 -626 -61 -297 0
-540 -3 -540 -7 0 -5 -7 -48 -15 -98 -14 -92 -45 -296 -45 -302 0 -2 321 -2
713 -1 l712 3 67 26 c117 46 210 135 249 239 32 85 73 357 73 490 1 100 -2
120 -21 155 -40 75 -110 122 -213 144 -30 7 -205 16 -390 21 -385 11 -436 15
-469 36 -22 15 -23 20 -18 84 7 78 26 122 64 147 25 17 71 18 558 21 l531 3
27 187 c15 103 27 190 27 195 0 10 -1273 7 -1337 -4z"/>
<path d="M6383 3823 c-7 -43 -26 -170 -42 -283 -40 -268 -109 -723 -166 -1087
-25 -160 -45 -297 -45 -302 0 -8 89 -11 278 -11 l277 0 49 343 c26 188 50 345
53 350 2 4 159 7 349 7 307 0 344 -2 344 -16 0 -8 -16 -118 -35 -242 -34 -227
-65 -433 -65 -439 0 -2 128 -3 284 -3 l284 0 6 33 c3 17 46 304 97 637 50 333
108 713 128 845 l37 240 -280 3 c-181 1 -282 -1 -286 -8 -3 -5 -28 -159 -55
-340 l-49 -330 -348 0 -349 0 6 33 c3 17 23 158 45 312 22 154 42 292 45 308
l5 27 -278 0 -278 0 -11 -77z"/>
<path d="M553 1745 c-133 -29 -252 -122 -313 -243 -40 -80 -54 -150 -131 -677
-46 -319 -60 -434 -55 -479 12 -121 83 -219 199 -278 111 -55 139 -58 753 -58
l562 0 6 33 c3 17 15 95 26 172 11 77 22 152 25 168 l5 27 -443 0 c-271 0
-456 4 -479 10 -45 13 -88 61 -88 101 0 50 100 743 111 770 16 39 59 76 104
88 26 7 199 11 497 11 l456 0 6 28 c6 32 46 317 46 332 0 17 -1209 12 -1287
-5z"/>
<path d="M2385 1754 c-181 -40 -294 -122 -355 -259 -35 -79 -39 -98 -116 -615
-73 -494 -76 -554 -33 -645 48 -103 159 -179 305 -210 101 -21 1067 -21 1169
0 165 35 303 138 368 277 41 86 39 74 123 653 72 494 73 530 28 622 -32 66
-94 118 -182 152 l-67 26 -610 2 c-335 0 -619 -1 -630 -3z m892 -390 c61 -46
61 -38 2 -463 -60 -428 -60 -429 -139 -469 -43 -21 -54 -22 -316 -22 -329 0
-361 7 -383 88 -13 45 87 760 113 810 19 36 52 61 99 73 17 4 159 7 314 6 268
-2 283 -3 310 -23z"/>
<path d="M4168 1638 c-10 -68 -37 -260 -62 -428 -25 -168 -61 -415 -80 -550
-48 -332 -85 -578 -91 -617 l-6 -33 269 0 270 0 17 113 c10 61 27 182 39 267
l22 155 143 0 144 0 138 -259 c77 -143 139 -263 139 -268 0 -4 149 -8 330 -8
l331 0 -12 23 c-6 12 -72 130 -145 263 -74 132 -134 246 -134 251 0 6 17 13
38 17 134 22 284 163 326 306 20 67 86 513 86 578 0 149 -98 259 -261 296 -58
14 -172 16 -776 16 l-709 0 -16 -122z m1158 -301 c29 -33 30 -64 9 -195 -18
-109 -27 -126 -81 -152 -37 -19 -62 -20 -342 -20 l-302 0 4 23 c3 12 15 87 26
167 11 80 23 160 26 178 l5 33 315 -3 316 -3 24 -28z"/>
<path d="M6190 1748 c0 -7 -12 -87 -25 -178 -14 -91 -50 -329 -80 -530 -30
-201 -66 -441 -80 -535 -14 -93 -35 -233 -46 -310 -11 -77 -22 -150 -24 -162
l-5 -23 785 0 785 0 5 23 c9 40 55 337 55 357 0 20 -10 20 -510 20 -417 0
-510 2 -510 13 0 13 25 192 35 255 l6 32 453 0 453 0 22 143 c12 78 26 166 30
195 l8 52 -449 0 c-352 0 -448 3 -448 13 1 6 9 68 18 135 l18 124 514 1 514 2
27 180 c16 99 28 186 29 193 0 9 -164 12 -790 12 -625 0 -790 -3 -790 -12z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

345
meshtastic-resources.html Normal file
View File

@ -0,0 +1,345 @@
<!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 Meshtastic 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;
}
h3 {
margin-top: 1em;
margin-bottom: 0.25em;
font-size: 16px;
}
p {
margin: 0.5em 0;
line-height: 1.4;
}
ul {
margin: 0.5em 0 0.5em 1.25em;
padding: 0;
line-height: 1.5;
}
.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 {
background-color: #67EA94;
color: #000000;
border: none;
border-radius: 10px;
padding: 0.25em 0.6em;
font-size: 0.75em;
font-weight: bold;
cursor: pointer;
margin-left: 0.5em;
vertical-align: middle;
}
.code-block .copy-btn {
position: absolute;
top: 0.75em;
right: 0.75em;
padding: 0.4em 0.75em;
margin-left: 0;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
background-color: #67EA94;
color: #000000;
border: none;
padding: 0.75em 1.5em;
border-radius: 999px;
cursor: pointer;
font-weight: bold;
font-size: 15px;
text-decoration: none;
}
.community-tools {
margin-top: 0.75em;
padding-top: 0.5em;
}
.community-tools ul {
margin: 0.5em 0 0.5em 1.25em;
}
.community-tools li {
margin-bottom: 0.5em;
}
.note {
background: #F5F5F5;
border-left: 4px solid #67EA94;
border-radius: 8px;
padding: 0.75em 1em;
margin: 0.75em 0 0.5em;
font-size: 0.95em;
}
.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 {
margin-top: 1.5em;
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;
}
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>Meshtastic 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://meshtastic-map.ctmesh.org/?lat=41.6558113360196&lng=287.3377990722657&zoom=10" target="_blank">Meshtastic Map</a> - live map of Meshtastic nodes CT Mesh has heard</li>
<li><a href="https://meshtastic.liamcottle.net/?lat=26.58852714730864&lng=285.11718750000006&zoom=2" target="_blank">Global Map</a> - Meshtastic nodes around the world</li>
<li><a href="https://malla.ctmesh.org/" target="_blank">Malla</a> - deeper packet-level metrics and mesh performance insights fed from MQTT</li>
<li><a href="https://potato.ctmesh.org/" target="_blank">PotatoMesh</a> <strong>NEW!</strong> - packet, chat, node list, and map fed from RF on Box Mountain</li>
<li><a href="https://meshinfo.ctmesh.org/" target="_blank">MeshInfo</a> <em>legacy, partially broken</em> - MQTT gateway setup info, secondary map, graphs, message history, etc.</li>
</ul>
</div>
<h2>Mesh Channels</h2>
<h3>Default (Primary) Channel</h3>
<p><em>Most CT chatter occurs on the default LongFast channel.</em></p>
<p>Name: <code id="channel-primary-name">LongFast</code> or (blank) <button class="copy-btn" type="button" data-copy-target="channel-primary-name">Copy</button></p>
<p>Key: <code id="channel-primary-key">AQ==</code> <button class="copy-btn" type="button" data-copy-target="channel-primary-key">Copy</button></p>
<p>Key size: Default or <code>1 byte</code></p>
<h3>CT's established community secondary channel</h3>
<p>Name: <code id="channel-secondary-name">ConnNet</code> <button class="copy-btn" type="button" data-copy-target="channel-secondary-name">Copy</button></p>
<p>Key: <code id="channel-secondary-key">pJgk5XqJY8Ar+jSqC5W81g==</code> <button class="copy-btn" type="button" data-copy-target="channel-secondary-key">Copy</button></p>
<p>Key size: 128 bit</p>
<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 <a href="https://meshtastic-map.ctmesh.org" target="_blank">map</a> and other 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> If that describes your setup, follow the steps at on <a href="https://meshinfo.ctmesh.org/" target="_blank">this page</a> to get set up.</p>
<h3>Key Settings</h3>
<div class="code-block">
<pre id="meshtastic-mqtt">MQTT > Enabled > ON
MQTT > Encryption Enabled > ON
MQTT > Map Report > OFF
MQTT > Root Topic > msh/US/CT
MQTT > Address > mqtt.ctmesh.org
MQTT > Username > meshdev
MQTT > Password > large4cats
MQTT > TLS Enabled > OFF
Channels > Primary Channel > Positions Enabled > ON
Channels > Primary Channel > Approximate Location > (set as desired)
Channels > Primary Channel > MQTT Uplink > ON
Channels > Primary Channel > MQTT Downlink > OFF
Settings > Modules > Neighbor Info > ON
LoRa > Ok to MQTT > ON</pre>
</div>
<div class="note"><strong>Note:</strong> CT Mesh's MQTT server is bridged and uplinks to the MQTT servers for both <a href="https://meshtastic.liamcottle.net" target="_blank">meshtastic.liamcottle.net</a> and <a href="https://meshmap.net" target="_blank">meshmap.net</a></div>
<h2>Infrastructure Nodes</h2>
<p><strong>Important:</strong> Infrastructure roles like <code>ROUTER</code>, <code>REPEATER</code>, <code>ROUTER_CLIENT</code>, or <code>ROUTER_LATE</code> are rarely appropriate. Unless your node is sitting on top of one of the tallest buildings in Hartford with clear line-of-sight across the region, using these roles will hurt more than help. For almost everyone, the correct choice is <code>CLIENT</code>. If you think you've got a node that truly warrants an infrastructure role, reach out first - we'd rather make sure it benefits the mesh than inadvertently weakens it.</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) => {
const defaultLabel = button.textContent;
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 = defaultLabel;
}, 1500);
} catch (err) {
button.textContent = "Copy failed";
setTimeout(() => {
button.textContent = defaultLabel;
}, 1500);
}
});
});
</script>
</body>
</html>

BIN
repeater-hash-mode-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

BIN
repeater-hash-mode-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

552
repeater-setup.html Normal file
View File

@ -0,0 +1,552 @@
<!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>MeshCore Repeater Setup | CT Mesh</title>
<meta name="description" content="CT Mesh guide for building, flashing, and programming a MeshCore repeater node.">
<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: 860px;
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;
}
h3 {
margin-top: 1.25em;
margin-bottom: 0.4em;
font-size: 17px;
}
p {
margin: 0.5em 0;
line-height: 1.45;
}
ol,
ul {
margin: 0.5em 0 0.5em 1.25em;
padding: 0;
line-height: 1.55;
}
li {
margin-bottom: 0.4em;
}
.toc {
list-style: none;
margin-left: 0;
padding-left: 0;
}
.toc-sub {
padding-left: 1.25em;
font-size: 0.96em;
}
.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;
}
.notes-list {
margin-top: 0.75em;
}
.inline-shot,
.inline-shot:visited {
margin-left: 0.35em;
white-space: nowrap;
}
.image-modal {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.72);
display: none;
align-items: center;
justify-content: center;
padding: 1.5em;
z-index: 10;
}
.image-modal.is-open {
display: flex;
}
.image-modal-content {
position: relative;
max-width: min(96vw, 920px);
max-height: 92vh;
}
.image-modal-content img {
display: block;
max-width: 100%;
max-height: 92vh;
border-radius: 18px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}
.image-modal-close {
position: absolute;
top: 0.75em;
right: 0.75em;
border: none;
border-radius: 999px;
background: rgba(39, 39, 39, 0.88);
color: #ffffff;
width: 36px;
height: 36px;
font-size: 22px;
cursor: pointer;
}
.button-row {
display: flex;
flex-wrap: wrap;
gap: 0.75em;
margin-top: 1em;
}
.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;
}
.btn,
.btn:visited {
color: #ffffff;
}
.btn-accent,
.btn-accent:visited {
background-color: #67EA94;
color: #000000;
}
.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;
}
.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,
.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;
}
.btn {
width: 100%;
}
}
</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 Repeater Setup</h1>
<p>This guide walks through the full repeater workflow: choose or build a MeshCore-compatible repeater node, flash and configure it over USB, then finish programming it from your phone using a separate companion node.</p>
<p>A repeater is the fixed radio that stays installed to relay traffic for the mesh. A companion is a second node you use to reach and manage that repeater from the MeshCore mobile app. The flow here is: build if needed, flash and configure over USB, test companion-based setup, then deploy the node.</p>
<div class="button-row">
<a class="btn btn-accent" href="https://flasher.meshcore.io/" target="_blank">Open MeshCore Flasher</a>
<a class="btn btn-accent" href="https://www.amazon.com/hz/wishlist/ls/2CMP31ZQIM9JD?ref_=wl_share" target="_blank">Repeater Parts List</a>
</div>
<h2>Table of Contents</h2>
<ul class="toc">
<li><a href="#before-you-start">Before You Start</a></li>
<li class="toc-sub"><a href="#guide-organization">Guide Organization</a></li>
<li class="toc-sub"><a href="#overall-needs">What You Will Need Overall</a></li>
<li><a href="#build-path">Build the Repeater Node</a></li>
<li class="toc-sub"><a href="#build-needs">Build Requirements</a></li>
<li class="toc-sub"><a href="#prepare-enclosure">Prepare the Enclosure</a></li>
<li class="toc-sub"><a href="#mount-solar">Mount the Solar Hardware</a></li>
<li class="toc-sub"><a href="#install-radio">Install the Radio Hardware</a></li>
<li class="toc-sub"><a href="#finish-assembly">Finish the Physical Assembly</a></li>
<li><a href="#flash-path">Flash the Repeater Node</a></li>
<li class="toc-sub"><a href="#flash-needs">Flash Requirements</a></li>
<li class="toc-sub"><a href="#flash-web">Option 1: MeshCore Web Flasher</a></li>
<li class="toc-sub"><a href="#flash-esptool">Option 2: Flash ESP32 Devices with esptool.py</a></li>
<li class="toc-sub"><a href="#flash-rak">Option 3: Flash RAK Devices with UF2</a></li>
<li class="toc-sub"><a href="#usb-config">Configure the Node Over USB</a></li>
<li><a href="#program-path">Programming with a Companion</a></li>
<li class="toc-sub"><a href="#program-needs">Programming Requirements</a></li>
<li class="toc-sub"><a href="#program-repeater">Program the Repeater</a></li>
<li><a href="#deploy-path">Deploy the Node</a></li>
<li class="toc-sub"><a href="#choose-site">Choose a Good Site</a></li>
<li class="toc-sub"><a href="#tree-mounting">Tree Mounting</a></li>
<li class="toc-sub"><a href="#structure-mounting">Structure or Pole Mounting</a></li>
<li class="toc-sub"><a href="#other-mounting">Other Mounting Options</a></li>
<li><a href="#power-addendum">Power Saving Addendum</a></li>
</ul>
<h2 id="before-you-start">Before You Start</h2>
<h3 id="guide-organization">How This Guide Is Organized</h3>
<p>Start with the build section if you are assembling a repeater from parts. If you already have a finished repeater or an off-the-shelf device, begin at the flash section instead.</p>
<h3 id="overall-needs">What You Will Need Overall</h3>
<ul>
<li>A MeshCore-compatible repeater device. Common choices include a <strong>Heltec LoRa 32 V4</strong>, <strong>RAK4631-based build</strong>, or another off-the-shelf or already-built MeshCore-capable node.</li>
<li>A <strong>second companion node</strong> for programming and day-to-day access to the repeater. Common companion nodes include a <strong>LILYGO T-Echo</strong>, <strong>Heltec LoRa 32 V4</strong>, or <strong>RAK4631 portable build</strong>.</li>
<li>Your phone with the MeshCore mobile app installed:
<a href="https://play.google.com/store/apps/details?id=com.liamcottle.meshcore.android" target="_blank">Android</a> /
<a href="https://apps.apple.com/us/app/meshcore/id6742354151" target="_blank">iPhone</a>
</li>
<li>A laptop or desktop computer for the flashing and USB configuration steps</li>
<li>A USB programming cable for your node, with <strong>USB-C</strong> being a common connector on many devices</li>
<li>A drill with a step bit, a bit sized for the solar wire, wire strippers, and outdoor waterproof sealant</li>
</ul>
<div class="note"><strong>Note:</strong> Look for the MeshCore app by <strong>Liam Cottle</strong> to make sure you have the correct app. The phone apps do not require internet to use once installed. You just need the app on the phone and a working companion node to reach the repeater.</div>
<h2 id="build-path">Build the Repeater Node</h2>
<p>If you already have a repeater build or a ready-made device, skip this section and go straight to <a href="#flash-path">Flash the Repeater Node</a>.</p>
<h3 id="build-needs">What You Will Need</h3>
<ul>
<li>A MeshCore-compatible repeater device or a parts build based on the linked repeater parts list</li>
<li>A drill with a step bit, a bit sized for the solar wire, wire strippers, zip ties, and outdoor waterproof sealant</li>
<li>Your enclosure, antenna, battery, solar panel, JST pigtail, and mounting hardware</li>
</ul>
<h3 id="prepare-enclosure">Prepare the Enclosure</h3>
<ol>
<li>Drill the top of the enclosure for the N-type bulkhead connector. Test-fit as you go so the hole stays snug.</li>
<li>Drill the bottom for the solar panel cable input. This opening can also act as a pressure vent.</li>
</ol>
<h3 id="mount-solar">Mount the Solar Hardware</h3>
<ol start="3">
<li>Mount the solar panel where it gets reliable sun. If you are mounting it to the case, leave enough wire slack for the door to open without pulling on the cable.</li>
<li>Splice the panel lead to the included JST pigtail from the Heltec box.</li>
</ol>
<h3 id="install-radio">Install the Radio Hardware</h3>
<ol start="5">
<li>Install the N-type bulkhead, attach the antenna to the bulkhead, and connect the radio to the IPEX lead.</li>
<li>If you are using a screenless V4, attach the included Bluetooth/Wi-Fi antenna.</li>
</ol>
<h3 id="finish-assembly">Finish the Physical Assembly</h3>
<ol start="7">
<li>After programming is complete, connect the battery and solar lead, then secure the board and battery with zip ties.</li>
<li>Seal the bulkhead opening and any other penetrations with outdoor waterproof sealant before deployment.</li>
</ol>
<h2 id="flash-path">Flash the Repeater Node</h2>
<p>This is the point where everyone should start if the repeater hardware already exists.</p>
<h3 id="flash-needs">What You Will Need</h3>
<ul>
<li>A laptop or desktop computer</li>
<li>A USB data cable for the repeater device</li>
<li>A Chromium-based browser with WebSerial support such as <strong>Google Chrome</strong>, if using the web flasher</li>
<li>Or a local Python environment with <code>esptool.py</code>, if flashing manually</li>
</ul>
<p>An optional power-saving mode is covered in the addendum at the bottom of this guide.</p>
<h3 id="flash-web">Option 1: MeshCore Web Flasher</h3>
<ol>
<li>Connect the radio to your computer and open <a href="https://flasher.meshcore.io/" target="_blank">flasher.meshcore.io</a>.</li>
<li>Use a Chromium-based browser such as Chrome so the site can access the radio over USB.</li>
<li>Load the correct firmware for your hardware and complete the flash.</li>
</ol>
<h3 id="flash-esptool">Option 2: Flash ESP32 Devices with <code>esptool.py</code></h3>
<ol>
<li>Download the correct firmware file for your repeater hardware from <a href="https://github.com/meshcore-dev/MeshCore/releases" target="_blank">the MeshCore GitHub releases page</a>.</li>
<li>Install <code>esptool.py</code> if needed.</li>
<li>Put the repeater device into bootloader mode if your hardware requires it.</li>
<li>Identify the serial port for the device, then flash the firmware with a command like:</li>
</ol>
<div class="note"><strong>Example:</strong> <code>esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x0 firmware.bin</code></div>
<ol start="5">
<li>Adjust the serial port, chip target, and firmware filename to match your hardware.</li>
</ol>
<h3 id="flash-rak">Option 3: Flash RAK Devices with <code>.uf2</code></h3>
<ol>
<li>Download the correct <code>.uf2</code> firmware file for your RAK device from <a href="https://github.com/meshcore-dev/MeshCore/releases" target="_blank">the MeshCore GitHub releases page</a>.</li>
<li>Put the device into the appropriate USB programming mode. Depending on the model, this may mean holding reset while plugging in USB, or holding the boot button while connecting the USB cable.</li>
<li>If a USB flash drive appears, copy the <code>.uf2</code> firmware file onto that drive and allow the device to reboot when the copy finishes.</li>
<li>On some devices you may need to temporarily disconnect the battery during programming.</li>
<li>Check the instructions for your exact device and make sure you selected the correct firmware image for that model.</li>
</ol>
<h3 id="usb-config">Configure the Node Over USB</h3>
<div class="note"><strong>Post-flash note:</strong> After flashing by any supported method, the node screen may show <strong>Please Wait...</strong> for up to about a minute during initial setup. If it stays there for more than <strong>5 minutes</strong>, retry the flash.</div>
<p>After flashing by any supported method, finish the initial repeater configuration over USB before trying to complete setup through the companion.</p>
<ol>
<li>Go to <a href="https://meshcore.io/" target="_blank">meshcore.io</a> and click <strong>Repeater Setup</strong> using the gear icon next to it. This opens the <strong>Repeater / Room server USB setup</strong> page at <code>config.meshcore.io</code>.</li>
<li>In the upper-right corner of that page, click <strong>Connect</strong>.</li>
<li>When the browser asks which serial device to use, select your repeater. This is often the top device in the list.</li>
<li>Set the repeater name.</li>
<li>Set <strong>Owner info</strong> only if you want public contact details attached to the repeater. This can include a name, phone number, email address, and/or website. This field is publicly shared and has a <strong>119 byte</strong> limit.</li>
<li>Set the repeater location by clicking the map button, or by copying and pasting latitude and longitude from your web browser for the intended repeater site.</li>
<li>Under <strong>Access</strong>, set a new admin password. This is recommended because it lets you further configure the repeater later from the mobile app for more advanced settings.</li>
<li>Leave <strong>Guest password</strong> empty unless you have a specific reason to enable it.</li>
<li>Under <strong>Radio settings</strong>, set the preset to <strong>USA/Canada (Recommended)</strong>.</li>
<li>That preset defaults to:
<strong>Frequency 910.525</strong>,
<strong>Bandwidth 62.5</strong>,
<strong>Spreading Factor (SF) 7</strong>,
and <strong>Coding Rate (CR) 5</strong>.
</li>
<li>If you want additional error correction, you can increase <strong>CR</strong> as high as <strong>8</strong>. This increases radio airtime substantially, nearly doubling it, and can reduce battery life.</li>
<li>Your device instructions will often specify the correct <strong>TX Power</strong> setting. Never set TX power above <strong>30 dBm</strong>, which is the FCC limit in the United States.</li>
<li>Under <strong>Advertising</strong>, adjust the values as needed. The UI shows fields for <strong>Advert interval (minutes)</strong>, <strong>Flood advert interval (hours)</strong>, and <strong>Flood max</strong>.</li>
<li>Click <strong>Save settings</strong> at the bottom, then reboot the device.</li>
</ol>
<div class="note notes-list">
<strong>Notes:</strong>
<ul>
<li>You can leave location blank for privacy reasons.</li>
<li>If you change the frequency, bandwidth, or spreading factor, the repeater will no longer communicate with nodes on the public mesh, which is usually not what you want.</li>
<li>For the public mesh, set <strong>Flood advert interval (hours)</strong> to <strong>48</strong>. The UI may default lower, such as <strong>12</strong>, so change it before saving.</li>
</ul>
</div>
<h2 id="program-path">Programming with a Companion</h2>
<h3 id="program-needs">What You Will Need</h3>
<ul>
<li>The flashed repeater node</li>
<li>A separate companion node that can hear and manage the repeater</li>
<li>Your phone with the MeshCore Android or iPhone app installed</li>
</ul>
<h3 id="program-repeater">Program the Repeater</h3>
<ol>
<li>Power on both devices: the repeater and your separate companion node. Make sure the repeater is within range.</li>
<li>Open the MeshCore app on your phone. In the upper right, tap the Bluetooth icon where it shows <strong>Connect</strong>.</li>
<li>Choose your companion node from the devices shown and provide the Bluetooth code if needed.</li>
<li>Once connected in the app, open the three-dot menu, tap <strong>Tools</strong>, then tap <strong>Discover Nearby Nodes</strong>, and then <strong>Discover repeaters</strong>.</li>
<li>The repeater should be discovered quickly if it is powered on and in range. Add it as a contact. You can also favorite it if you want.</li>
<li>Open the repeater contact, tap <strong>Manage</strong>, and enter the admin password.</li>
<li>This opens the repeater admin status page. Tap <strong>Request status</strong> if you want to refresh and view the repeater status.</li>
<li>At the bottom of that screen, open <strong>Command Line</strong> and run <code>set path.hash.mode 1</code> to set <strong>2 byte mode</strong>. <a href="repeater-hash-mode-entry-iphone.png" class="inline-shot" data-modal-image="repeater-hash-mode-entry-iphone.png">View screenshot</a> <a href="repeater-hash-mode-confirm-iphone.png" class="inline-shot" data-modal-image="repeater-hash-mode-confirm-iphone.png">View result</a></li>
<li>Tap the <strong>Settings</strong> icon in the bottom right next to <strong>Command Line</strong> to change other node settings such as advert intervals, owner info, position, syncing the device clock, and other access control or telemetry settings.</li>
<li>Set <strong>Flood advert interval (hours)</strong> to <strong>48</strong> for best public mesh performance if you have not already set it during USB configuration.</li>
</ol>
<div class="note"><strong>Notes:</strong>
<ul>
<li>If the app has trouble reconnecting to the companion over Bluetooth, you may need to go into your phone's Bluetooth settings and forget the device before trying again.</li>
<li>Syncing the clock on the device is recommended.</li>
<li><strong>1 byte mode</strong> is not recommended because it can cause hash collisions.</li>
<li>You can use <code>set path.hash.mode 2</code> for <strong>3 byte mode</strong>, but that reduces the hop limit from <strong>32</strong> hops in 2 byte mode to <strong>21</strong> hops in 3 byte mode.</li>
</ul>
</div>
<h2 id="deploy-path">Deploy the Node</h2>
<h3 id="choose-site">Choose a Good Site</h3>
<ul>
<li>Choose a stable location with useful height, clear exposure, and enough sun to keep the battery healthy.</li>
<li>If your radio site and panel site differ, remote panel placement is fine if it improves sun exposure.</li>
</ul>
<h3 id="tree-mounting">Tree Mounting</h3>
<p>For tree installs, a common approach is to use a slingshot to place a lightweight mason line over a suitable branch, then use that line to pull up a stronger rope for hoisting and securing the node. Choose a healthy branch, avoid power lines, and make sure the enclosure and antenna can hang clear without rubbing through on bark or hardware.</p>
<h3 id="structure-mounting">Structure or Pole Mounting</h3>
<p>Nodes can also be attached to the side of a structure, a mast, a fence post, or a utility-style pole using appropriate brackets, hose clamps, or other weather-resistant mounting hardware. Keep the enclosure secure, keep cable runs tidy, and position the antenna with as much clear exposure as practical.</p>
<h3 id="other-mounting">Other Mounting Options</h3>
<p>Other logical mounting options include sheds, garages, balcony rails, old TV antenna mounts, rooftop masts, and similar elevated locations with stable support and useful line-of-sight.</p>
<div class="note"><strong>Spacing Note:</strong> If you have more than one LoRa device at a site, keep the LoRa antennas at least <strong>3 feet apart</strong> for optimal performance.</div>
<h2>Need More Context?</h2>
<p>If you want help validating a parts list, firmware choice, or deployment location, ask in Discord before you seal everything up and mount it.</p>
<div class="button-row">
<a class="btn" href="meshcore-resources.html">More MeshCore Guides</a>
<a class="btn" href="https://ctmesh.org/discord" target="_blank">Join CT Mesh Discord</a>
</div>
<h2 id="power-addendum">Power Saving Addendum</h2>
<p>If you are running a solar-powered repeater or otherwise want to reduce current draw, power saving is available as an optional post-setup change.</p>
<p>After logging into the repeater, open the command line and run <code>powersaving on</code>.</p>
<p>This can be useful for deployments where power budget matters, especially small solar installations.</p>
<p>If you want a lower-power firmware variant, you can also use the flasher's custom firmware option with releases from <a href="https://github.com/IoTThinks/EasySkyMesh/releases" target="_blank">EasySkyMesh</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>
<div class="image-modal" id="image-modal" aria-hidden="true">
<div class="image-modal-content">
<button class="image-modal-close" id="image-modal-close" type="button" aria-label="Close image">&times;</button>
<img id="image-modal-img" src="" alt="Guide screenshot">
</div>
</div>
<script>
const imageModal = document.getElementById("image-modal");
const imageModalImg = document.getElementById("image-modal-img");
const imageModalClose = document.getElementById("image-modal-close");
const imageLinks = document.querySelectorAll("[data-modal-image]");
const closeImageModal = () => {
imageModal.classList.remove("is-open");
imageModal.setAttribute("aria-hidden", "true");
imageModalImg.setAttribute("src", "");
};
imageLinks.forEach((link) => {
link.addEventListener("click", (event) => {
event.preventDefault();
const imageSrc = link.getAttribute("data-modal-image");
imageModalImg.setAttribute("src", imageSrc);
imageModal.classList.add("is-open");
imageModal.setAttribute("aria-hidden", "false");
});
});
imageModalClose.addEventListener("click", closeImageModal);
imageModal.addEventListener("click", (event) => {
if (event.target === imageModal) {
closeImageModal();
}
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape" && imageModal.classList.contains("is-open")) {
closeImageModal();
}
});
</script>
</body>
</html>

2
wheel.svg Normal file
View File

@ -0,0 +1,2 @@
<svg id="Capa_1" enable-background="new 0 0 512 512" height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><g><circle cx="256" cy="271" r="15"/><path d="m256 0c-140.958 0-256 115.049-256 256 0 140.961 115.049 256 256 256 140.96 0 256-115.049 256-256 0-140.958-115.049-256-256-256zm0 61c97.323 0 178.219 71.668 192.692 165h-52.019c-32.982 0-64.318-13.235-85.973-36.311-29.6-31.546-79.797-31.556-109.4-.001-21.655 23.077-52.991 36.312-85.974 36.312h-52.018c14.473-93.332 95.369-165 192.692-165zm0 255c-24.812 0-45-20.186-45-45s20.188-45 45-45c24.814 0 45 20.186 45 45s-20.186 45-45 45zm-181.58 11.097c17.17-6.736 29.568-11.097 46.58-11.097 54.688 0 97.219 48.734 88.981 103.49h-.035c-1.224 8.381-3.632 16.502-7.105 24.145-58.759-16.67-106.31-60.262-128.421-116.538zm234.751 116.534c-3.568-7.845-5.924-15.965-7.117-24.141h-.035c-9.599-63.805 49.301-116.452 110.354-100.936 6.436 1.636 14.49 4.34 25.206 8.544-22.109 56.271-69.655 99.861-128.408 116.533z"/></g></svg>

After

Width:  |  Height:  |  Size: 993 B

339
why-run-a-node.html Normal file
View File

@ -0,0 +1,339 @@
<!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>Why Run a Mesh Node? | CT Mesh</title>
<meta name="description" content="Learn how running a local MeshCore node helps your community with off-grid communication, emergency resilience, and private encrypted messaging.">
<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: 2em;
margin-bottom: 0.5em;
font-size: 28px;
}
.page-title {
margin-top: 0;
}
h2 {
margin-top: 1.5em;
margin-bottom: 0.5em;
font-size: 22px;
font-weight: 700;
}
h3 {
margin-top: 1.25em;
margin-bottom: 0.4em;
font-size: 17px;
font-weight: 600;
}
.subtitle {
margin-top: -0.25em;
font-size: 1.05em;
font-weight: 500;
color: #1F2230;
}
p {
margin: 0.5em 0;
line-height: 1.45;
}
ul {
margin: 0.5em 0 0.5em 1.25em;
padding: 0;
line-height: 1.5;
}
.note {
background: #F5F5F5;
border-left: 4px solid #67EA94;
border-radius: 8px;
padding: 0.75em 1em;
margin: 0.75em 0 0.5em;
font-size: 0.95em;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
background-color: #67EA94;
color: #000000;
border: none;
padding: 0.75em 1.5em;
border-radius: 999px;
cursor: pointer;
font-weight: bold;
font-size: 15px;
text-decoration: none;
margin-top: 0.75em;
}
.btn-secondary,
.btn-secondary:visited {
background-color: #2B3A4E;
color: #ffffff;
}
.btn-discord,
.btn-discord:visited {
background-color: #5865F2;
color: #ffffff;
justify-content: center;
}
.btn-discord img {
filter: brightness(0) invert(1);
}
.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;
}
.button-row {
display: flex;
flex-wrap: wrap;
gap: 0.75em;
margin-top: 0.5em;
}
.back-link {
margin-top: 1.5em;
display: inline-flex;
align-items: center;
gap: 6px;
color: #ffffff;
}
.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,
.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;
}
h3 {
font-size: 16px;
}
.btn {
width: 100%;
}
}
</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 class="page-title">Why Run a Mesh Node?</h1>
<p class="subtitle">Learn about the mesh!</p>
<p>A local node is more than a gadget. It strengthens community communication by adding coverage, redundancy, and local routing for everyone nearby.</p>
<h2>How the Public Benefits</h2>
<ul>
<li><strong>Resilient network without internet or cellular dependency:</strong> Nodes route locally by radio and create alternate paths, so communication can continue even when phone or ISP backbones are unavailable.</li>
<li><strong>Emergency communication:</strong> During outages or disasters, local radio links can keep basic coordination available.</li>
<li><strong>Flexible encrypted communications:</strong> Mesh platforms support public channels for open community traffic, private encrypted channels for groups, and private encrypted direct messages between individuals.</li>
<li><strong>Accessible mobile apps:</strong> Both iPhone and Android users can manage nodes and messaging from their phones, using Bluetooth to communicate with the radios.</li>
<li><strong>Affordable hardware:</strong> Many compatible devices are commonly available for under $100.</li>
<li><strong>No operator license required (USA):</strong> On 915 MHz ISM, operation under 1W does not require an amateur radio license, and this is typically plenty of power for local mesh use.</li>
<li><strong>Easy onboarding:</strong> Pairing a node, loading channels, and joining local community resources is straightforward for new users.</li>
</ul>
<h2>Solar and Off-Grid Operation</h2>
<p>Many fixed mesh repeater nodes can run from small solar systems and batteries, with no grid connection required. That means:</p>
<ul>
<li><strong>Improved resilience</strong> because communication infrastructure can remain active during grid disruptions</li>
<li><strong>Install options beyond existing infrastructure</strong>, including remote locations with clear line-of-sight and reliable sun exposure</li>
<li><strong>No additional household electric cost</strong> for grid power at the node site</li>
</ul>
<h2>Why Ham Radio Operators Often Love Mesh</h2>
<ul>
<li>It combines RF experimentation, antenna work, and practical local service.</li>
<li>It rewards good station design: Elevation, feedline quality, power planning, and interference control.</li>
<li>It offers an always-on local network that can complement other emergency and community communication practices.</li>
<li>These communities represent some of the largest mesh networks in the world: Over 21,000 MeshCore nodes globally (including about 14,000 repeaters) and over 32,000 Meshtastic nodes worldwide, with adoption levels comparable to APRS and major amateur IP mesh networks in Europe.</li>
</ul>
<h1>Getting Involved</h1>
<p>Most users only need a companion/client device. If you want to expand mesh coverage in your area, build a repeater/router node. In MeshCore, a <strong>repeater</strong> is a fixed station that stays in place to relay traffic for the mesh, while a <strong>companion</strong> is a personal device that travels with you for day-to-day messaging. Meshtastic follows a similar pattern with <strong>routers</strong> (fixed relay-focused nodes) and <strong>clients</strong> (user-focused mobile/personal nodes). Contact us on Discord if you want help building or operating a repeater.</p>
<h2>Recommended Hardware</h2>
<ul>
<li><strong>Heltec LoRa 32 V4</strong> - popular low-cost option and a common first node. Typical price: <strong>$25-$35</strong>.</li>
<li><strong>LILYGO T-Echo</strong> - portable handheld form factor with an integrated display and battery support. Typical price: <strong>$40-$80</strong> depending on configuration.</li>
<li><strong>RAK4631 Development Board</strong> - a common choice for reliable fixed installations with low-power operation and flexible deployment options. Typical combined core + base price: <strong>$30-$45</strong>.</li>
</ul>
<h2>Typical Installation Locations</h2>
<p>Fixed nodes in strong locations often improve neighborhood coverage for many nearby users, not just the node owner, and even modest elevation can make a big difference. For example, an upstairs attic-window repeater can significantly improve in-home and neighborhood reception, and a few extra feet of height can meaningfully improve overall mesh performance.</p>
<ul>
<li><strong>Indoor locations:</strong> Attic or top-floor window positions with clear line-of-sight.</li>
<li><strong>Outdoor home locations:</strong> Rooftop mast or chimney mount, balcony rail, garage or shed roof, fences, trees, and old over-the-air TV antenna roof mounts.</li>
<li><strong>Hosted or remote sites:</strong> Hilltop, tower, or shared hosted locations with sun exposure for solar operation, including off-grid sites with no utility infrastructure.</li>
<li><strong>Mobile deployments:</strong> Vehicle-based nodes for mapping and temporary event coverage.</li>
</ul>
<div class="note"><strong>Safety Note:</strong> Use a spotter when working on a ladder or on a roof.</div>
<h2>Meshtastic vs MeshCore in CT Mesh</h2>
<p>Both platforms are still useful, and many members experiment with both. In the CT Mesh fixed-node community, Meshtastic has gradually been replaced in many deployments by MeshCore as operators have seen stronger day-to-day reliability and broader platform functionality from current MeshCore firmware.</p>
<p>If you are starting fresh and your goal is reliable fixed-node service, MeshCore is often the first recommendation from local operators.</p>
<h1>Learn More</h1>
<p>Start with our local guides, then review official project docs and community channels to choose the right platform for your goals.</p>
<div class="button-row">
<a class="btn btn-secondary" href="meshcore-resources.html">MeshCore Guides & Tools</a>
<a class="btn btn-secondary" href="https://meshcore.io/" target="_blank">Official MeshCore Site</a>
</div>
<div class="button-row">
<a class="btn" href="meshtastic-resources.html">Meshtastic Guides & Tools</a>
<a class="btn" href="https://meshtastic.org/" target="_blank">Official Meshtastic Site</a>
</div>
<div class="button-row">
<a class="btn btn-discord" href="https://ctmesh.org/discord" target="_blank">Join CT Mesh Discord</a>
</div>
<div class="button-row">
<a class="btn btn-secondary back-link" href="index.html">Back to CT Mesh</a>
</div>
<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>
</body>
</html>