Initial commit: Security Mesh Node
This commit is contained in:
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.pio/
|
||||||
|
.pioenvs/
|
||||||
|
.piolibdeps/
|
||||||
|
__pycache__/
|
||||||
|
*.o
|
||||||
|
*.d
|
||||||
|
*.elf
|
||||||
|
*.bin
|
||||||
|
*.hex
|
||||||
|
*.zip
|
||||||
236
INSTRUCTION.md
Normal file
236
INSTRUCTION.md
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
# Security Mesh Node — Architecture & Development Guide
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
security-new/
|
||||||
|
├── platformio.ini # PlatformIO config (targets, libs, flags)
|
||||||
|
├── src/
|
||||||
|
│ ├── main.cpp # Main firmware — SecurityMesh class + loop
|
||||||
|
│ ├── target.h # Pin mappings, radio params, platform defs
|
||||||
|
│ └── variants/
|
||||||
|
│ └── promicro/ # ProMicro-specific files
|
||||||
|
├── boards/ # Custom linker scripts
|
||||||
|
├── scripts/ # Build helper scripts
|
||||||
|
├── .pio/ # Build output (ignored)
|
||||||
|
```
|
||||||
|
|
||||||
|
All MeshCore library lives at `../MeshCore/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How Radio Mesh Works
|
||||||
|
|
||||||
|
### Low-level Layer (RadioLib)
|
||||||
|
- `radio_module` (SX1262) — raw LoRa send/receive.
|
||||||
|
- `radio_driver` (CustomSX1262Wrapper) — RadioLib wrapper. Provides `getCurrentRSSI()`, `setParams()`, `setTxPower()`.
|
||||||
|
|
||||||
|
### Mesh Layer (MeshCore)
|
||||||
|
- `mesh::Mesh` — main class that manages flooding, routing, encryption, channel storage.
|
||||||
|
- `mesh::Dispatcher` — base of Mesh (packet scheduling, radio control).
|
||||||
|
- `mesh::GroupChannel` — channel with `hash[1]` and `secret[32]`.
|
||||||
|
- `mesh::Packet` — a mesh packet. Obtained from `PacketManager` (pool of 16 slots).
|
||||||
|
|
||||||
|
### Lifecycle
|
||||||
|
|
||||||
|
```
|
||||||
|
1. radio_module.begin() — init SX1262
|
||||||
|
2. radio_driver.setParams(...) — freq, BW, SF, CR
|
||||||
|
3. Mesh.begin() — starts listening
|
||||||
|
4. Mesh.loop() loop() — pumps radio ISR, processes incoming
|
||||||
|
5. onGroupDataRecv() callback — called when encrypted group msg arrives
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SecurityMesh Class
|
||||||
|
|
||||||
|
### Channel Derivation (`setup_channel()`)
|
||||||
|
|
||||||
|
```
|
||||||
|
SHA256("1234") → 32 bytes key
|
||||||
|
mesh_channel.secret = key[0..15] (first 16 bytes)
|
||||||
|
mesh_channel.hash = SHA256(secret)[0..0] (first 1 byte, PATH_HASH_SIZE=1)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Critical Virtual Overrides
|
||||||
|
|
||||||
|
#### `searchChannelsByHash()`
|
||||||
|
**WHY IT'S NEEDED:** `Mesh::loop()` calls `searchChannelsByHash()` to match incoming packet hashes against stored channels. The base implementation returns 0 (no matches). Without this override, **every incoming group message is silently dropped** and `onGroupDataRecv` is never called.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int searchChannelsByHash(const uint8_t* hash, mesh::GroupChannel channels[], int max_matches) override {
|
||||||
|
if (max_matches > 0 && channel_ready
|
||||||
|
&& memcmp(hash, mesh_channel.hash, sizeof(mesh_channel.hash)) == 0) {
|
||||||
|
channels[0] = mesh_channel;
|
||||||
|
return 1; // found 1 match
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`sizeof(mesh_channel.hash)` = `PATH_HASH_SIZE` = 1 byte.
|
||||||
|
|
||||||
|
#### `onGroupDataRecv()`
|
||||||
|
Called for each decrypted group message that matched a channel. Format: `[4 bytes ts][1 byte type][text]`. Type byte must be 0 for text.
|
||||||
|
|
||||||
|
### Sending (`send_text()`)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void send_text(const char* text) {
|
||||||
|
uint8_t buf[256];
|
||||||
|
buf[0..3] = timestamp (4 bytes)
|
||||||
|
buf[4] = 0 (type: text)
|
||||||
|
buf[5..] = text
|
||||||
|
Packet* pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, mesh_channel, buf, 5 + text_len);
|
||||||
|
sendFlood(pkt, 0, 1); // 1 hop
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`sendFlood` with `path_hash_size=1` means 0-hop (immediate neighbors only). Increase to flood across the mesh.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Command Processing
|
||||||
|
|
||||||
|
All commands are processed in `onGroupDataRecv()`:
|
||||||
|
|
||||||
|
### Preprocessing
|
||||||
|
1. **Trim trailing spaces** — `"status "` → `"status"`
|
||||||
|
2. **Lowercase** — `"Status"` → `"status"`
|
||||||
|
3. **Strip prefix** — `"Security: status"` → `"status"` (anything before `": "`)
|
||||||
|
4. **Trim leading spaces**
|
||||||
|
|
||||||
|
### Command Table
|
||||||
|
|
||||||
|
| Command | Action | Response |
|
||||||
|
|---------|--------|----------|
|
||||||
|
| `help` | list commands | `help:...` |
|
||||||
|
| `ohrana on` | arm, relay1 ON | `ohrana on ok` |
|
||||||
|
| `ohrana off` | disarm, relay1 OFF | `ohrana off ok` |
|
||||||
|
| `relay1 on` | relay1 ON (direct) | `relay1 on ok` |
|
||||||
|
| `relay1 off` | relay1 OFF (direct) | `relay1 off ok` |
|
||||||
|
| `relay2 on` | relay2 ON | `relay2 on ok` |
|
||||||
|
| `relay2 off` | relay2 OFF | `relay2 off ok` |
|
||||||
|
| `status` | report state | `status:armed/off temp:±X.XC hall:trig/ok motion:trig/ok` |
|
||||||
|
| `ver` | build date | `build:Mon DD YYYY HH:MM:SS` |
|
||||||
|
| `scan` | noise floor | `noise:-XXXdBm` |
|
||||||
|
| `security_conf <name>` | change channel | `channel:<name>` |
|
||||||
|
|
||||||
|
### Auto-Sent Messages
|
||||||
|
- **Startup:** `poweron Mon DD YYYY HH:MM:SS` — sent 10s after boot.
|
||||||
|
- **Hall alert:** `alert:hall` — when armed and reed opens.
|
||||||
|
- **Motion alert:** `alert:motion` — when armed and PIR triggers.
|
||||||
|
|
||||||
|
Cooldown: 10 minutes between alerts (ALERT_COOLDOWN_MS).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
| Pin (nRF52) | Pin (ProMicro) | Function |
|
||||||
|
|-------------|----------------|----------|
|
||||||
|
| 30 | 13 | NSS (LoRa SPI CS) |
|
||||||
|
| 29 | 12 | SCK |
|
||||||
|
| 31 | 14 | MOSI |
|
||||||
|
| 2 | 15 | MISO |
|
||||||
|
| 7 | 11 | DIO1 (LoRa IRQ) |
|
||||||
|
| 6 | 10 | RST |
|
||||||
|
| 10 | 16 | BUSY |
|
||||||
|
| — | 21 | POWER_EN (ProMicro only) |
|
||||||
|
| 12 | 0 | REED (hall sensor) |
|
||||||
|
| 11 | 1 | PIR (motion) |
|
||||||
|
| 3 | 2 | DS18B20 (temp) |
|
||||||
|
| 4 | 3 | RELAY_1 |
|
||||||
|
| 5 | 4 | RELAY_2 |
|
||||||
|
| 13 | 22 | LED_STATUS |
|
||||||
|
|
||||||
|
**Reference:** `target.h` lines 25–114.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Radio Config
|
||||||
|
|
||||||
|
| Param | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Frequency | 868.731018 MHz |
|
||||||
|
| Bandwidth | 62.5 kHz |
|
||||||
|
| Spreading Factor | 7 |
|
||||||
|
| Coding Rate | 7 (4/7) |
|
||||||
|
| TX Power | 22 dBm |
|
||||||
|
| TCXO voltage | 1.8V |
|
||||||
|
| Current limit | 140 mA |
|
||||||
|
| RX Boosted Gain | on |
|
||||||
|
| DIO2 as RF switch | on |
|
||||||
|
|
||||||
|
`PATH_HASH_MODE=0` → `PATH_HASH_SIZE=1` (minimal hash, maximum throughput).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build & Upload
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build
|
||||||
|
pio run -e promicro_security
|
||||||
|
|
||||||
|
# Upload (via nrfutil)
|
||||||
|
pio run -e promicro_security -t upload
|
||||||
|
|
||||||
|
# Monitor
|
||||||
|
pio device monitor -b 115200
|
||||||
|
```
|
||||||
|
|
||||||
|
Current resource usage: RAM 8%, Flash 36.5%.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Pitfalls & Debugging
|
||||||
|
|
||||||
|
### `onGroupDataRecv` not called despite LED blink
|
||||||
|
The LED blink (3 fast flashes) is the first thing in `onGroupDataRecv`. If LED doesn't blink:
|
||||||
|
- Check `searchChannelsByHash()` override — it's required.
|
||||||
|
- Check hash match: both sides must derive the same `secret` and `hash` from passphrase.
|
||||||
|
- Check radio config matches (freq, BW, SF, CR).
|
||||||
|
|
||||||
|
### No response despite LED blinking
|
||||||
|
- Command was received (LED blinked) but processing or send failed.
|
||||||
|
- **Test with simple response** — if `send_text("ok")` fails but `send_text("relay2 on ok")` works, check response length/SNPRINTF.
|
||||||
|
- `sendFlood` with `path_hash_size=1` sends only to neighbors. If response doesn't arrive at the remote app, check app's mesh routing or increase path_hash_size.
|
||||||
|
|
||||||
|
### `send_text()` doesn't actually send
|
||||||
|
- `createGroupDatagram` may return NULL if packet pool is full (16 slots).
|
||||||
|
- `rtc_clock.getCurrentTime()` returning bogus data (VolatileRTCClock starts from 0, fine).
|
||||||
|
|
||||||
|
### Adding a new command
|
||||||
|
1. Add `else if` block in `onGroupDataRecv()` after line 168.
|
||||||
|
2. Use `strcmp(cmd, "command") == 0` for exact match.
|
||||||
|
3. Call `send_text("response")` to reply.
|
||||||
|
4. Update the `help` string.
|
||||||
|
|
||||||
|
### Config persistent storage
|
||||||
|
- `InternalFS` (LittleFS) on nRF52 internal flash.
|
||||||
|
- `SecurityConfig`: armed + relay2 state saved as binary struct.
|
||||||
|
- File: `/security.cfg`, magic `0x53454355`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Files in MeshCore Library
|
||||||
|
|
||||||
|
| File | What it provides |
|
||||||
|
|------|-----------------|
|
||||||
|
| `MeshCore/src/Mesh.h` | `Mesh`, `GroupChannel`, virtual methods |
|
||||||
|
| `MeshCore/src/MeshCore.h` | Constants (`PUB_KEY_SIZE`, `PATH_HASH_SIZE`, etc.) |
|
||||||
|
| `MeshCore/src/Dispatcher.h` | `Dispatcher`, packet scheduling |
|
||||||
|
| `MeshCore/src/helpers/SimpleMeshTables.h` | Duplicate packet detection |
|
||||||
|
| `MeshCore/src/helpers/StaticPoolPacketManager.h` | Packet memory pool |
|
||||||
|
| `MeshCore/src/helpers/ChannelDetails.h` | Channel helper structs |
|
||||||
|
| `MeshCore/src/helpers/NRF52Board.h` | nRF52 board abstraction |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Things That Won't Work
|
||||||
|
|
||||||
|
- **BLE** — not included. No `BLESerialInterface`, no `BaseChatMesh`.
|
||||||
|
- **`BaseChatMesh`** — not used. SecurityMesh inherits directly from `mesh::Mesh`.
|
||||||
|
- **Serial commands** — not processed. Serial is for debug output only.
|
||||||
|
- **`onPeerDataRecv`** — not used. Group messaging only, no direct peer messages.
|
||||||
121
USER_MANUAL.md
Normal file
121
USER_MANUAL.md
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# Security Mesh Node — Инструкция пользователя
|
||||||
|
|
||||||
|
## Обзор
|
||||||
|
|
||||||
|
Security Node — LoRa mesh-устройство, которое следит за датчиками (геркон, PIR движение, температура) и управляет 2 реле. Работает по радиоканалу через MeshCore — без интернета, без Bluetooth, без проводов.
|
||||||
|
|
||||||
|
Команды и оповещения передаются как текстовые сообщения по mesh-сети.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Быстрый старт
|
||||||
|
|
||||||
|
1. Подайте питание (USB или батарея).
|
||||||
|
2. Подождите ~10 секунд — устройство отправит `poweron`.
|
||||||
|
3. Отправляйте команды с любого другого узла mesh.
|
||||||
|
|
||||||
|
Все команды **не чувствительны к регистру**, лишние пробелы в конце игнорируются.
|
||||||
|
**Префикс не обязателен** — работают и `"status"`, и `"Security: status"`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Команды
|
||||||
|
|
||||||
|
| Команда | Что делает | Пример ответа |
|
||||||
|
|---------|-----------|---------------|
|
||||||
|
| `help` | Список всех команд | `help:ohrana on,ohrana off,...` |
|
||||||
|
| `ohrana on` | **Включить охрану** — датчики активны, реле 1 замкнуто | `ohrana on ok` |
|
||||||
|
| `ohrana off` | **Выключить охрану** — датчики не активны, реле 1 разомкнуто | `ohrana off ok` |
|
||||||
|
| `relay1 on` | Включить реле 1 (независимо от охраны) | `relay1 on ok` |
|
||||||
|
| `relay1 off` | Выключить реле 1 | `relay1 off ok` |
|
||||||
|
| `relay2 on` | Включить реле 2 | `relay2 on ok` |
|
||||||
|
| `relay2 off` | Выключить реле 2 | `relay2 off ok` |
|
||||||
|
| `status` | Показать состояние устройства | `status:armed temp:+23.5C hall:ok motion:trig` |
|
||||||
|
| `ver` | Версия прошивки (дата сборки) | `build:Jul 21 2026 15:30:00` |
|
||||||
|
| `scan` | Измерить уровень шума в эфире | `noise:-112dBm` |
|
||||||
|
| `security_conf <имя>` | Сменить канал (пароль из `<имя>`) | `channel:mysecret` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Поля статуса
|
||||||
|
|
||||||
|
Ответ: `status:armed/off temp:+XX.XC/N/A hall:trig/ok motion:trig/ok`
|
||||||
|
|
||||||
|
| Поле | Значение |
|
||||||
|
|------|----------|
|
||||||
|
| `status:armed` | Охрана включена (датчики активны) |
|
||||||
|
| `status:off` | Охрана выключена |
|
||||||
|
| `temp:+23.5C` | Температура с датчика DS18B20 |
|
||||||
|
| `temp:N/A` | Датчик температуры не подключён |
|
||||||
|
| `hall:ok` | Геркон замкнут (норма) |
|
||||||
|
| `hall:trig` | Геркон разомкнулся (дверь/окно открыли) |
|
||||||
|
| `motion:ok` | Движения нет |
|
||||||
|
| `motion:trig` | Движение обнаружено |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Оповещения
|
||||||
|
|
||||||
|
Когда охрана **включена** (`ohrana on`), устройство автоматически отправляет:
|
||||||
|
|
||||||
|
| Оповещение | Что значит |
|
||||||
|
|------------|-----------|
|
||||||
|
| `alert:hall` | Геркон разомкнулся — дверь/окно открыли |
|
||||||
|
| `alert:motion` | PIR датчик сработал — движение |
|
||||||
|
|
||||||
|
Одно и то же оповещение не повторится раньше чем через **10 минут** (защита от дребезга).
|
||||||
|
Оповещение сбрасывается автоматически, когда датчик возвращается в нормальное состояние.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Сообщение при запуске
|
||||||
|
|
||||||
|
Через ~10 секунд после подачи питания устройство отправляет:
|
||||||
|
`poweron Jul 21 2026 15:30:00`
|
||||||
|
|
||||||
|
Это подтверждает, что устройство включено и работает на текущем канале.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Светодиод (LED на плате)
|
||||||
|
|
||||||
|
| Режим | Что значит |
|
||||||
|
|-------|-----------|
|
||||||
|
| Не горит | Охрана выключена, нет активности |
|
||||||
|
| Медленно мигает (1 сек) | **Охрана включена** |
|
||||||
|
| 3 быстрых вспышки | Получена команда (любая) |
|
||||||
|
| Часто мигает (100 мс) | **Ошибка радио** — аппаратная проблема |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Реле
|
||||||
|
|
||||||
|
- **Реле 1** — управляется `ohrana on/off` и `relay1 on/off`.
|
||||||
|
Охрана включена (`ohrana on`): **замкнуто** (NC разомкнуты, NO замкнуты).
|
||||||
|
Охрана выключена (`ohrana off`): **разомкнуто**.
|
||||||
|
|
||||||
|
- **Реле 2** — независимое, только `relay2 on/off`.
|
||||||
|
Состояние сохраняется при перезагрузке.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Смена канала (`security_conf`)
|
||||||
|
|
||||||
|
По умолчанию канал формируется из пароля `"1234"`.
|
||||||
|
Чтобы сменить:
|
||||||
|
|
||||||
|
```
|
||||||
|
security_conf мойпароль
|
||||||
|
```
|
||||||
|
|
||||||
|
Ответ: `channel:мойпароль`
|
||||||
|
|
||||||
|
**Важно:** Все устройства должны быть на одном канале. После смены канала связь будет только с теми узлами, у которых такой же пароль.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Советы
|
||||||
|
|
||||||
|
- Если команда не получила ответа: проверьте канал, питание, дальность связи. LED должен мигнуть 3 раза при получении команды — если не мигнул, сообщение не дошло.
|
||||||
|
- Все ответы тоже передаются по mesh — они появятся в ленте сообщений вашего приложения.
|
||||||
|
- `scan` выполняется около 1 секунды (20 замеров RSSI).
|
||||||
61
boards/heltec_t114.json
Normal file
61
boards/heltec_t114.json
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "nrf52840_s140_v6.ld"
|
||||||
|
},
|
||||||
|
"core": "nRF5",
|
||||||
|
"cpu": "cortex-m4",
|
||||||
|
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
|
||||||
|
"f_cpu": "64000000L",
|
||||||
|
"hwids": [
|
||||||
|
["0x239A","0x8029"],
|
||||||
|
["0x239A","0x0029"],
|
||||||
|
["0x239A","0x002A"],
|
||||||
|
["0x239A","0x802A"]
|
||||||
|
],
|
||||||
|
"usb_product": "HT-n5262",
|
||||||
|
"mcu": "nrf52840",
|
||||||
|
"variant": "Heltec_T114_Board",
|
||||||
|
"bsp": {
|
||||||
|
"name": "adafruit"
|
||||||
|
},
|
||||||
|
"softdevice": {
|
||||||
|
"sd_flags": "-DS140",
|
||||||
|
"sd_name": "s140",
|
||||||
|
"sd_version": "6.1.1",
|
||||||
|
"sd_fwid": "0x00B6"
|
||||||
|
},
|
||||||
|
"bootloader": {
|
||||||
|
"settings_addr": "0xFF000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectivity": [
|
||||||
|
"bluetooth"
|
||||||
|
],
|
||||||
|
"debug": {
|
||||||
|
"jlink_device": "nRF52840_xxAA",
|
||||||
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52.cfg"
|
||||||
|
},
|
||||||
|
"frameworks": [
|
||||||
|
"arduino"
|
||||||
|
],
|
||||||
|
"name": "Heltec T114 Board",
|
||||||
|
"upload": {
|
||||||
|
"maximum_ram_size": 235520,
|
||||||
|
"maximum_size": 815104,
|
||||||
|
"speed": 115200,
|
||||||
|
"protocol": "nrfutil",
|
||||||
|
"protocols": [
|
||||||
|
"jlink",
|
||||||
|
"nrfjprog",
|
||||||
|
"nrfutil",
|
||||||
|
"stlink"
|
||||||
|
],
|
||||||
|
"use_1200bps_touch": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"wait_for_upload_port": true
|
||||||
|
},
|
||||||
|
"url": "https://heltec.org/",
|
||||||
|
"vendor": "Heltec"
|
||||||
|
}
|
||||||
28
boards/nrf52840_s140_v6_extrafs.ld
Normal file
28
boards/nrf52840_s140_v6_extrafs.ld
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
SEARCH_DIR(.)
|
||||||
|
GROUP(-lgcc -lc -lnosys)
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xD4000 - 0x26000
|
||||||
|
RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 0x20040000 - 0x20006000
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
.svc_data :
|
||||||
|
{
|
||||||
|
PROVIDE(__start_svc_data = .);
|
||||||
|
KEEP(*(.svc_data))
|
||||||
|
PROVIDE(__stop_svc_data = .);
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
.fs_data :
|
||||||
|
{
|
||||||
|
PROVIDE(__start_fs_data = .);
|
||||||
|
KEEP(*(.fs_data))
|
||||||
|
PROVIDE(__stop_fs_data = .);
|
||||||
|
} > RAM
|
||||||
|
} INSERT AFTER .data;
|
||||||
|
|
||||||
|
INCLUDE "nrf52_common.ld"
|
||||||
79
boards/promicro_nrf52840.json
Normal file
79
boards/promicro_nrf52840.json
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino":{
|
||||||
|
"ldscript": "nrf52840_s140_v6.ld"
|
||||||
|
},
|
||||||
|
"core": "nRF5",
|
||||||
|
"cpu": "cortex-m4",
|
||||||
|
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
|
||||||
|
"f_cpu": "64000000L",
|
||||||
|
"hwids": [
|
||||||
|
[
|
||||||
|
"0x239A",
|
||||||
|
"0x00B3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"0x239A",
|
||||||
|
"0x8029"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"0x239A",
|
||||||
|
"0x0029"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"0x239A",
|
||||||
|
"0x002A"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"0x239A",
|
||||||
|
"0x802A"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"usb_product": "ProMicro NRF52840",
|
||||||
|
"mcu": "nrf52840",
|
||||||
|
"variant": "promicro_nrf52840",
|
||||||
|
"variants_dir": "variants",
|
||||||
|
"bsp": {
|
||||||
|
"name": "adafruit"
|
||||||
|
},
|
||||||
|
"softdevice": {
|
||||||
|
"sd_flags": "-DS140",
|
||||||
|
"sd_name": "s140",
|
||||||
|
"sd_version": "6.1.1",
|
||||||
|
"sd_fwid": "0x00B6"
|
||||||
|
},
|
||||||
|
"bootloader": {
|
||||||
|
"settings_addr": "0xFF000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectivity": [
|
||||||
|
"bluetooth"
|
||||||
|
],
|
||||||
|
"debug": {
|
||||||
|
"jlink_device": "nRF52840_xxAA",
|
||||||
|
"svd_path": "nrf52840.svd",
|
||||||
|
"openocd_target": "nrf52.cfg"
|
||||||
|
},
|
||||||
|
"frameworks": [
|
||||||
|
"arduino",
|
||||||
|
"zephyr"
|
||||||
|
],
|
||||||
|
"name": "ProMicro NRF52840",
|
||||||
|
"upload": {
|
||||||
|
"maximum_ram_size": 235520,
|
||||||
|
"maximum_size": 815104,
|
||||||
|
"speed": 115200,
|
||||||
|
"protocol": "nrfutil",
|
||||||
|
"protocols": [
|
||||||
|
"jlink",
|
||||||
|
"nrfjprog",
|
||||||
|
"nrfutil",
|
||||||
|
"stlink"
|
||||||
|
],
|
||||||
|
"use_1200bps_touch": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"wait_for_upload_port": true
|
||||||
|
},
|
||||||
|
"url": "https://www.nologo.tech/en/product/otherboard/NRF52840.html",
|
||||||
|
"vendor": "Nologo"
|
||||||
|
}
|
||||||
93
platformio.ini
Normal file
93
platformio.ini
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
[platformio]
|
||||||
|
default_envs = promicro_security
|
||||||
|
|
||||||
|
[arduino_base]
|
||||||
|
framework = arduino
|
||||||
|
monitor_speed = 115200
|
||||||
|
lib_deps =
|
||||||
|
SPI
|
||||||
|
Wire
|
||||||
|
jgromes/RadioLib @ ^7.6.0
|
||||||
|
rweather/Crypto @ ^0.4.0
|
||||||
|
adafruit/RTClib @ ^2.1.3
|
||||||
|
electroniccats/CayenneLPP @ 1.6.1
|
||||||
|
paulstoffregen/OneWire @ ^2.3.8
|
||||||
|
milesburton/DallasTemperature @ ^3.11.0
|
||||||
|
build_flags =
|
||||||
|
-w -DNDEBUG
|
||||||
|
-DRADIOLIB_STATIC_ONLY=1
|
||||||
|
-DRADIOLIB_GODMODE=1
|
||||||
|
-D ENABLE_ADVERT_ON_BOOT=1
|
||||||
|
-D ENABLE_PRIVATE_KEY_IMPORT=1
|
||||||
|
-D ENABLE_PRIVATE_KEY_EXPORT=1
|
||||||
|
-D RADIOLIB_EXCLUDE_CC1101=1
|
||||||
|
-D RADIOLIB_EXCLUDE_RF69=1
|
||||||
|
-D RADIOLIB_EXCLUDE_SX1231=1
|
||||||
|
-D RADIOLIB_EXCLUDE_SI443X=1
|
||||||
|
-D RADIOLIB_EXCLUDE_RFM2X=1
|
||||||
|
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||||
|
-D RADIOLIB_EXCLUDE_AFSK=1
|
||||||
|
-D RADIOLIB_EXCLUDE_AX25=1
|
||||||
|
-D RADIOLIB_EXCLUDE_HELLSCHREIBER=1
|
||||||
|
-D RADIOLIB_EXCLUDE_MORSE=1
|
||||||
|
-D RADIOLIB_EXCLUDE_APRS=1
|
||||||
|
-D RADIOLIB_EXCLUDE_BELL=1
|
||||||
|
-D RADIOLIB_EXCLUDE_RTTY=1
|
||||||
|
-D RADIOLIB_EXCLUDE_SSTV=1
|
||||||
|
|
||||||
|
[nrf52_base]
|
||||||
|
extends = arduino_base
|
||||||
|
platform = nordicnrf52
|
||||||
|
platform_packages =
|
||||||
|
framework-arduinoadafruitnrf52 @ https://github.com/meshcore-dev/Adafruit_nRF52_Arduino#d541301
|
||||||
|
extra_scripts = scripts/create_uf2.py
|
||||||
|
build_flags = ${arduino_base.build_flags}
|
||||||
|
-D NRF52_PLATFORM
|
||||||
|
-D LFS_NO_ASSERT=1
|
||||||
|
-D EXTRAFS=1
|
||||||
|
lib_deps =
|
||||||
|
${arduino_base.lib_deps}
|
||||||
|
https://github.com/oltaco/CustomLFS#0.2.2
|
||||||
|
|
||||||
|
[security_base]
|
||||||
|
extends = nrf52_base
|
||||||
|
build_flags =
|
||||||
|
${nrf52_base.build_flags}
|
||||||
|
-I src
|
||||||
|
-I ../MeshCore/src
|
||||||
|
-I ../MeshCore/lib/ed25519
|
||||||
|
-D USE_SX1262
|
||||||
|
-D RADIO_CLASS=CustomSX1262
|
||||||
|
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||||
|
-D MAX_CONTACTS=50
|
||||||
|
-D MAX_GROUP_CHANNELS=8
|
||||||
|
-D OFFLINE_QUEUE_SIZE=32
|
||||||
|
-D PATH_HASH_MODE=0
|
||||||
|
-D NRF52_PLATFORM
|
||||||
|
lib_deps =
|
||||||
|
${nrf52_base.lib_deps}
|
||||||
|
file://../MeshCore
|
||||||
|
densaugeo/base64 @ ~1.4.0
|
||||||
|
Bluefruit52Lib
|
||||||
|
|
||||||
|
[env:promicro_security]
|
||||||
|
extends = security_base
|
||||||
|
board = promicro_nrf52840
|
||||||
|
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
|
||||||
|
board_upload.maximum_size = 712704
|
||||||
|
build_flags =
|
||||||
|
${security_base.build_flags}
|
||||||
|
-D PROMICRO
|
||||||
|
-I src/variants/promicro
|
||||||
|
-D LORA_FREQ=868.731018
|
||||||
|
-D LORA_BW=62.5
|
||||||
|
-D LORA_SF=7
|
||||||
|
-D LORA_CR=7
|
||||||
|
-I $PROJECT_PACKAGES_DIR/framework-arduinoadafruitnrf52/libraries/Bluefruit52Lib/src
|
||||||
|
-I $PROJECT_PACKAGES_DIR/framework-arduinoadafruitnrf52/libraries/Adafruit_nRFCrypto/src
|
||||||
|
build_src_filter =
|
||||||
|
+<*.cpp>
|
||||||
|
+<variants/promicro/*.cpp>
|
||||||
|
+<../../MeshCore/lib/ed25519/*.c>
|
||||||
|
upload_protocol = nrfutil
|
||||||
|
debug_tool = jlink
|
||||||
33
scripts/add_external_sources.py
Normal file
33
scripts/add_external_sources.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
from os.path import join, normpath
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
project_dir = env.subst("$PROJECT_DIR")
|
||||||
|
meshcore_dir = normpath(join(project_dir, "..", "MeshCore"))
|
||||||
|
|
||||||
|
# Add MeshCore source files via build_src_filter override
|
||||||
|
# We need to add them before the build starts
|
||||||
|
meshcore_glob_patterns = [
|
||||||
|
"src/*.cpp",
|
||||||
|
"src/helpers/*.cpp",
|
||||||
|
"src/helpers/radiolib/*.cpp",
|
||||||
|
"src/helpers/bridges/BridgeBase.cpp",
|
||||||
|
"src/helpers/nrf52/SerialBLEInterface.cpp",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Add source files to the build environment
|
||||||
|
for pattern in meshcore_glob_patterns:
|
||||||
|
full_pattern = normpath(join(meshcore_dir, pattern))
|
||||||
|
files = glob.glob(full_pattern)
|
||||||
|
for f in sorted(files):
|
||||||
|
env.Append(SOURCES=[f])
|
||||||
|
|
||||||
|
# Also add T114 variant files if they exist
|
||||||
|
t114_pattern = normpath(join(meshcore_dir, "variants/heltec_t114/*.cpp"))
|
||||||
|
t114_files = glob.glob(t114_pattern)
|
||||||
|
for f in sorted(t114_files):
|
||||||
|
env.Append(SOURCES=[f])
|
||||||
31
scripts/create_uf2.py
Normal file
31
scripts/create_uf2.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
firmware_hex = "${BUILD_DIR}/${PROGNAME}.hex"
|
||||||
|
uf2_file = os.environ.get("UF2_FILE_PATH", "${BUILD_DIR}/${PROGNAME}.uf2")
|
||||||
|
meshcore_dir = os.path.normpath(os.path.join(env.subst("$PROJECT_DIR"), "..", "MeshCore"))
|
||||||
|
uf2conv = os.path.join(meshcore_dir, "bin", "uf2conv", "uf2conv.py")
|
||||||
|
|
||||||
|
def create_uf2_action(source, target, env):
|
||||||
|
uf2_cmd = " ".join(
|
||||||
|
[
|
||||||
|
'"$PYTHONEXE"',
|
||||||
|
'"' + uf2conv + '"',
|
||||||
|
'-f', '0xADA52840',
|
||||||
|
'-c', firmware_hex,
|
||||||
|
'-o', uf2_file,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
env.Execute(uf2_cmd)
|
||||||
|
|
||||||
|
env.AddCustomTarget(
|
||||||
|
name="create_uf2",
|
||||||
|
dependencies=firmware_hex,
|
||||||
|
actions=create_uf2_action,
|
||||||
|
title="Create UF2 file",
|
||||||
|
description="Use uf2conv to convert hex binary into uf2",
|
||||||
|
always_build=True,
|
||||||
|
)
|
||||||
251
src/main.cpp
Normal file
251
src/main.cpp
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <Mesh.h>
|
||||||
|
#include <InternalFileSystem.h>
|
||||||
|
#include <OneWire.h>
|
||||||
|
#include <DallasTemperature.h>
|
||||||
|
#include <helpers/ArduinoHelpers.h>
|
||||||
|
#include <helpers/NRF52Board.h>
|
||||||
|
#include <helpers/SimpleMeshTables.h>
|
||||||
|
#include <helpers/StaticPoolPacketManager.h>
|
||||||
|
#include <helpers/ChannelDetails.h>
|
||||||
|
#include <helpers/ArduinoSerialInterface.h>
|
||||||
|
#include <Crypto.h>
|
||||||
|
#include <SHA256.h>
|
||||||
|
#include "target.h"
|
||||||
|
|
||||||
|
using namespace Adafruit_LittleFS_Namespace;
|
||||||
|
|
||||||
|
#ifdef PROMICRO
|
||||||
|
PromicroBoard board;
|
||||||
|
Module radio_module_obj(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
|
||||||
|
RADIO_CLASS radio_module(&radio_module_obj);
|
||||||
|
WRAPPER_CLASS radio_driver(radio_module, board);
|
||||||
|
VolatileRTCClock fallback_clock;
|
||||||
|
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||||
|
#else
|
||||||
|
NRF52Board board("Security");
|
||||||
|
Module radio_module_obj(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
|
||||||
|
RADIO_CLASS radio_module(&radio_module_obj);
|
||||||
|
WRAPPER_CLASS radio_driver(radio_module, board);
|
||||||
|
AutoDiscoverRTCClock rtc_clock;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
StdRNG fast_rng;
|
||||||
|
SimpleMeshTables tables;
|
||||||
|
|
||||||
|
OneWire oneWire(PIN_DS18B20);
|
||||||
|
DallasTemperature ds18b20(&oneWire);
|
||||||
|
|
||||||
|
static bool g_armed = false;
|
||||||
|
static bool g_hall_alert = false;
|
||||||
|
static bool g_motion_alert = false;
|
||||||
|
static bool g_relay2_state = false;
|
||||||
|
static bool g_temp_requested = false;
|
||||||
|
static unsigned long g_temp_last_request = 0;
|
||||||
|
static float g_last_temperature = -127.0f;
|
||||||
|
static unsigned long g_last_hall_alert = 0;
|
||||||
|
static unsigned long g_last_motion_alert = 0;
|
||||||
|
static const unsigned long ALERT_COOLDOWN_MS = 600000;
|
||||||
|
static unsigned long g_boot_time = 0;
|
||||||
|
static bool g_startup_msg_sent = false;
|
||||||
|
|
||||||
|
struct SecurityConfig { uint32_t magic; uint8_t armed; uint8_t relay2; };
|
||||||
|
static const uint32_t CFG_MAGIC = 0x53454355;
|
||||||
|
|
||||||
|
bool saveSecurityConfig() {
|
||||||
|
SecurityConfig cfg = { CFG_MAGIC, g_armed ? 1 : 0, g_relay2_state ? 1 : 0 };
|
||||||
|
File f = InternalFS.open(CONFIG_FILE, FILE_O_WRITE);
|
||||||
|
if (!f) return false;
|
||||||
|
bool ok = f.write((const uint8_t*)&cfg, sizeof(cfg)) == sizeof(cfg);
|
||||||
|
f.close(); return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loadSecurityConfig() {
|
||||||
|
File f = InternalFS.open(CONFIG_FILE, FILE_O_READ);
|
||||||
|
if (!f) return false;
|
||||||
|
SecurityConfig cfg;
|
||||||
|
if (f.read(&cfg, sizeof(cfg)) != sizeof(cfg)) { f.close(); return false; }
|
||||||
|
f.close();
|
||||||
|
if (cfg.magic != CFG_MAGIC) return false;
|
||||||
|
g_armed = cfg.armed; g_relay2_state = cfg.relay2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SecurityMesh : public mesh::Mesh {
|
||||||
|
public:
|
||||||
|
mesh::GroupChannel mesh_channel;
|
||||||
|
bool channel_ready = false;
|
||||||
|
|
||||||
|
SecurityMesh(mesh::Radio& r, mesh::RNG& rng, mesh::RTCClock& clock, SimpleMeshTables& t)
|
||||||
|
: Mesh(r, *new ArduinoMillis(), rng, clock, *new StaticPoolPacketManager(16), t) {}
|
||||||
|
|
||||||
|
void setup_channel() {
|
||||||
|
uint8_t key[32];
|
||||||
|
SHA256 sha; sha.reset(); sha.update((const uint8_t*)"1234", 4); sha.finalize(key, 32);
|
||||||
|
memset(mesh_channel.secret, 0, 32);
|
||||||
|
memcpy(mesh_channel.secret, key, 16);
|
||||||
|
mesh::Utils::sha256(mesh_channel.hash, sizeof(mesh_channel.hash), mesh_channel.secret, 16);
|
||||||
|
channel_ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int searchChannelsByHash(const uint8_t* hash, mesh::GroupChannel channels[], int max_matches) override {
|
||||||
|
if (max_matches > 0 && channel_ready && memcmp(hash, mesh_channel.hash, sizeof(mesh_channel.hash)) == 0) {
|
||||||
|
channels[0] = mesh_channel;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_text(const char* text) {
|
||||||
|
if (!channel_ready) return;
|
||||||
|
uint32_t ts = rtc_clock.getCurrentTime();
|
||||||
|
uint8_t buf[256];
|
||||||
|
memcpy(buf, &ts, 4);
|
||||||
|
buf[4] = 0;
|
||||||
|
int tlen = strlen(text);
|
||||||
|
if (tlen > 200) tlen = 200;
|
||||||
|
memcpy(buf + 5, text, tlen);
|
||||||
|
auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, mesh_channel, buf, 5 + tlen);
|
||||||
|
if (pkt) sendFlood(pkt, (uint32_t)0, (uint8_t)1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onGroupDataRecv(mesh::Packet* pkt, uint8_t type, const mesh::GroupChannel& channel, uint8_t* data, size_t len) override {
|
||||||
|
if (type != PAYLOAD_TYPE_GRP_TXT) return;
|
||||||
|
if (len < 5 || data[4] != 0) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) { digitalWrite(PIN_LED_STATUS, HIGH); delay(50); digitalWrite(PIN_LED_STATUS, LOW); delay(50); }
|
||||||
|
|
||||||
|
int tlen = len - 5;
|
||||||
|
if (tlen > 250) tlen = 250;
|
||||||
|
char text[256];
|
||||||
|
memcpy(text, data + 5, tlen);
|
||||||
|
text[tlen] = 0;
|
||||||
|
|
||||||
|
int i = tlen; while (i > 0 && text[i-1] == ' ') { text[i-1] = 0; i--; }
|
||||||
|
for (int i = 0; text[i]; i++) { if (text[i] >= 'A' && text[i] <= 'Z') text[i] += 32; }
|
||||||
|
|
||||||
|
const char* cmd = text;
|
||||||
|
const char* colon = strchr(text, ':');
|
||||||
|
if (colon && colon[1] == ' ') cmd = colon + 2;
|
||||||
|
while (*cmd == ' ') cmd++;
|
||||||
|
|
||||||
|
if (strcmp(cmd, "help") == 0) {
|
||||||
|
send_text("help:ohrana on,ohrana off,relay1 on,relay1 off,relay2 on,relay2 off,status,ver,scan,security_conf");
|
||||||
|
} else if (strcmp(cmd, "ohrana on") == 0) {
|
||||||
|
g_armed = true; digitalWrite(PIN_RELAY_1, LOW); saveSecurityConfig(); send_text("ohrana on ok");
|
||||||
|
} else if (strcmp(cmd, "ohrana off") == 0) {
|
||||||
|
g_armed = false; digitalWrite(PIN_RELAY_1, HIGH); saveSecurityConfig(); send_text("ohrana off ok");
|
||||||
|
} else if (strcmp(cmd, "relay1 on") == 0) {
|
||||||
|
digitalWrite(PIN_RELAY_1, LOW); send_text("relay1 on ok");
|
||||||
|
} else if (strcmp(cmd, "relay1 off") == 0) {
|
||||||
|
digitalWrite(PIN_RELAY_1, HIGH); send_text("relay1 off ok");
|
||||||
|
} else if (strcmp(cmd, "relay2 on") == 0) {
|
||||||
|
g_relay2_state = true; digitalWrite(PIN_RELAY_2, LOW); send_text("relay2 on ok");
|
||||||
|
} else if (strcmp(cmd, "relay2 off") == 0) {
|
||||||
|
g_relay2_state = false; digitalWrite(PIN_RELAY_2, HIGH); send_text("relay2 off ok");
|
||||||
|
} else if (strcmp(cmd, "status") == 0) {
|
||||||
|
char r[128]; char tb[16]; float t = g_last_temperature;
|
||||||
|
if (t == -127.0f) strcpy(tb, "N/A"); else snprintf(tb, sizeof(tb), "%+.1fC", t);
|
||||||
|
snprintf(r, sizeof(r), "status:%s temp:%s hall:%s motion:%s",
|
||||||
|
g_armed ? "armed" : "off", tb, g_hall_alert ? "trig" : "ok", g_motion_alert ? "trig" : "ok");
|
||||||
|
send_text(r);
|
||||||
|
} else if (strcmp(cmd, "ver") == 0) {
|
||||||
|
char r[64]; snprintf(r, sizeof(r), "build:%s %s", __DATE__, __TIME__); send_text(r);
|
||||||
|
} else if (strcmp(cmd, "scan") == 0) {
|
||||||
|
float sum = 0;
|
||||||
|
for (int j = 0; j < 20; j++) { sum += radio_driver.getCurrentRSSI(); delay(50); }
|
||||||
|
char r[32]; snprintf(r, sizeof(r), "noise:%.0fdBm", sum / 20); send_text(r);
|
||||||
|
} else if (strncmp(cmd, "security_conf", 13) == 0) {
|
||||||
|
const char* nc = cmd + 13; while (*nc == ' ') nc++;
|
||||||
|
if (strlen(nc) > 0 && strlen(nc) < 32) {
|
||||||
|
uint8_t key[32];
|
||||||
|
SHA256 sha; sha.reset(); sha.update((const uint8_t*)nc, strlen(nc)); sha.finalize(key, 32);
|
||||||
|
memset(mesh_channel.secret, 0, 32);
|
||||||
|
memcpy(mesh_channel.secret, key, 16);
|
||||||
|
mesh::Utils::sha256(mesh_channel.hash, sizeof(mesh_channel.hash), mesh_channel.secret, 16);
|
||||||
|
char r[64]; snprintf(r, sizeof(r), "channel:%s", nc); send_text(r);
|
||||||
|
} else send_text("bad name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SecurityMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables);
|
||||||
|
|
||||||
|
bool radio_init() {
|
||||||
|
#ifdef PROMICRO
|
||||||
|
board.begin();
|
||||||
|
#endif
|
||||||
|
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
|
||||||
|
SPI.begin();
|
||||||
|
if (radio_module.begin() != RADIOLIB_ERR_NONE) return false;
|
||||||
|
radio_module.setDio2AsRfSwitch(true);
|
||||||
|
radio_module.setTCXO(SX126X_DIO3_TCXO_VOLTAGE);
|
||||||
|
radio_module.setCurrentLimit(SX126X_CURRENT_LIMIT);
|
||||||
|
radio_driver.setParams(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR);
|
||||||
|
radio_driver.setTxPower(LORA_TX_POWER);
|
||||||
|
radio_driver.setRxBoostedGainMode(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
g_boot_time = millis();
|
||||||
|
|
||||||
|
NRF_WDT->CONFIG = (WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos) | (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos);
|
||||||
|
NRF_WDT->CRV = (WDT_TIMEOUT_MS * 32768) / 1000;
|
||||||
|
NRF_WDT->RREN = WDT_RREN_RR0_Enabled << WDT_RREN_RR0_Pos;
|
||||||
|
NRF_WDT->TASKS_START = 1;
|
||||||
|
|
||||||
|
pinMode(PIN_REED, INPUT_PULLUP); pinMode(PIN_PIR, INPUT); ds18b20.begin();
|
||||||
|
pinMode(PIN_RELAY_1, OUTPUT); pinMode(PIN_RELAY_2, OUTPUT); pinMode(PIN_LED_STATUS, OUTPUT);
|
||||||
|
digitalWrite(PIN_RELAY_1, HIGH); digitalWrite(PIN_RELAY_2, HIGH); digitalWrite(PIN_LED_STATUS, LOW);
|
||||||
|
|
||||||
|
InternalFS.begin();
|
||||||
|
loadSecurityConfig();
|
||||||
|
if (g_armed) digitalWrite(PIN_RELAY_1, LOW); else digitalWrite(PIN_RELAY_1, HIGH);
|
||||||
|
digitalWrite(PIN_RELAY_2, g_relay2_state ? LOW : HIGH);
|
||||||
|
|
||||||
|
if (!radio_init()) {
|
||||||
|
Serial.println("RADIO FAIL");
|
||||||
|
while (1) { digitalWrite(PIN_LED_STATUS, HIGH); delay(100); digitalWrite(PIN_LED_STATUS, LOW); delay(100); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fast_rng.begin(radio_driver.getRngSeed());
|
||||||
|
|
||||||
|
the_mesh.setup_channel();
|
||||||
|
the_mesh.begin();
|
||||||
|
|
||||||
|
Serial.println("OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
NRF_WDT->RR[0] = WDT_RR_RR_Reload;
|
||||||
|
the_mesh.loop();
|
||||||
|
rtc_clock.tick();
|
||||||
|
|
||||||
|
if (!g_startup_msg_sent && millis() - g_boot_time >= 10000) {
|
||||||
|
g_startup_msg_sent = true;
|
||||||
|
char m[80]; snprintf(m, sizeof(m), "poweron %s %s", __DATE__, __TIME__);
|
||||||
|
the_mesh.send_text(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long now = millis();
|
||||||
|
int reed = digitalRead(PIN_REED);
|
||||||
|
if (g_armed && reed == HIGH && !g_hall_alert && now - g_last_hall_alert > ALERT_COOLDOWN_MS) {
|
||||||
|
g_hall_alert = true; g_last_hall_alert = now; the_mesh.send_text("alert:hall");
|
||||||
|
}
|
||||||
|
int pir = digitalRead(PIN_PIR);
|
||||||
|
if (g_armed && pir == HIGH && !g_motion_alert && now - g_last_motion_alert > ALERT_COOLDOWN_MS) {
|
||||||
|
g_motion_alert = true; g_last_motion_alert = now; the_mesh.send_text("alert:motion");
|
||||||
|
}
|
||||||
|
if (g_hall_alert && reed == LOW) g_hall_alert = false;
|
||||||
|
if (g_motion_alert && pir == LOW) g_motion_alert = false;
|
||||||
|
|
||||||
|
if (!g_temp_requested) { ds18b20.requestTemperatures(); g_temp_requested = true; g_temp_last_request = now; }
|
||||||
|
else if (now - g_temp_last_request > 750) { g_last_temperature = ds18b20.getTempCByIndex(0); g_temp_requested = false; }
|
||||||
|
|
||||||
|
if (g_armed) {
|
||||||
|
static unsigned long lt = 0; static bool ls = false;
|
||||||
|
if (now - lt > 1000) { lt = now; ls = !ls; digitalWrite(PIN_LED_STATUS, ls); }
|
||||||
|
} else digitalWrite(PIN_LED_STATUS, LOW);
|
||||||
|
}
|
||||||
146
src/target.h
Normal file
146
src/target.h
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define RADIOLIB_STATIC_ONLY 1
|
||||||
|
|
||||||
|
#define USE_SX1262
|
||||||
|
#define RADIO_CLASS CustomSX1262
|
||||||
|
#define WRAPPER_CLASS CustomSX1262Wrapper
|
||||||
|
|
||||||
|
#define SX126X_DIO2_AS_RF_SWITCH true
|
||||||
|
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
||||||
|
#define SX126X_CURRENT_LIMIT 140
|
||||||
|
#define SX126X_RX_BOOSTED_GAIN 1
|
||||||
|
|
||||||
|
#define LORA_FREQ 868.731018
|
||||||
|
#define LORA_BW 62.5
|
||||||
|
#define LORA_SF 7
|
||||||
|
#define LORA_CR 7
|
||||||
|
#ifndef LORA_TX_POWER
|
||||||
|
#define LORA_TX_POWER 22
|
||||||
|
#endif
|
||||||
|
#define MAX_LORA_TX_POWER 22
|
||||||
|
|
||||||
|
#define PATH_HASH_MODE 0
|
||||||
|
|
||||||
|
#ifdef PROMICRO
|
||||||
|
#ifndef P_LORA_NSS
|
||||||
|
#define P_LORA_NSS 13
|
||||||
|
#endif
|
||||||
|
#ifndef P_LORA_SCLK
|
||||||
|
#define P_LORA_SCLK 12
|
||||||
|
#endif
|
||||||
|
#ifndef P_LORA_MOSI
|
||||||
|
#define P_LORA_MOSI 14
|
||||||
|
#endif
|
||||||
|
#ifndef P_LORA_MISO
|
||||||
|
#define P_LORA_MISO 15
|
||||||
|
#endif
|
||||||
|
#ifndef P_LORA_DIO_1
|
||||||
|
#define P_LORA_DIO_1 11
|
||||||
|
#endif
|
||||||
|
#ifndef P_LORA_RESET
|
||||||
|
#define P_LORA_RESET 10
|
||||||
|
#endif
|
||||||
|
#ifndef P_LORA_BUSY
|
||||||
|
#define P_LORA_BUSY 16
|
||||||
|
#endif
|
||||||
|
#define SX126X_POWER_EN 21
|
||||||
|
#else
|
||||||
|
#ifndef P_LORA_NSS
|
||||||
|
#define P_LORA_NSS 30
|
||||||
|
#endif
|
||||||
|
#ifndef P_LORA_SCLK
|
||||||
|
#define P_LORA_SCLK 29
|
||||||
|
#endif
|
||||||
|
#ifndef P_LORA_MOSI
|
||||||
|
#define P_LORA_MOSI 31
|
||||||
|
#endif
|
||||||
|
#ifndef P_LORA_MISO
|
||||||
|
#define P_LORA_MISO 2
|
||||||
|
#endif
|
||||||
|
#ifndef P_LORA_DIO_1
|
||||||
|
#define P_LORA_DIO_1 7
|
||||||
|
#endif
|
||||||
|
#ifndef P_LORA_RESET
|
||||||
|
#define P_LORA_RESET 6
|
||||||
|
#endif
|
||||||
|
#ifndef P_LORA_BUSY
|
||||||
|
#define P_LORA_BUSY 10
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PROMICRO
|
||||||
|
#ifndef PIN_REED
|
||||||
|
#define PIN_REED 0
|
||||||
|
#endif
|
||||||
|
#ifndef PIN_PIR
|
||||||
|
#define PIN_PIR 1
|
||||||
|
#endif
|
||||||
|
#ifndef PIN_DS18B20
|
||||||
|
#define PIN_DS18B20 2
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifndef PIN_REED
|
||||||
|
#define PIN_REED 12
|
||||||
|
#endif
|
||||||
|
#ifndef PIN_PIR
|
||||||
|
#define PIN_PIR 11
|
||||||
|
#endif
|
||||||
|
#ifndef PIN_DS18B20
|
||||||
|
#define PIN_DS18B20 3
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PROMICRO
|
||||||
|
#ifndef PIN_RELAY_1
|
||||||
|
#define PIN_RELAY_1 3
|
||||||
|
#endif
|
||||||
|
#ifndef PIN_RELAY_2
|
||||||
|
#define PIN_RELAY_2 4
|
||||||
|
#endif
|
||||||
|
#ifndef PIN_LED_STATUS
|
||||||
|
#define PIN_LED_STATUS 22
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifndef PIN_RELAY_1
|
||||||
|
#define PIN_RELAY_1 4
|
||||||
|
#endif
|
||||||
|
#ifndef PIN_RELAY_2
|
||||||
|
#define PIN_RELAY_2 5
|
||||||
|
#endif
|
||||||
|
#ifndef PIN_LED_STATUS
|
||||||
|
#define PIN_LED_STATUS 13
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CONFIG_FILE "/security.cfg"
|
||||||
|
#define CHANNEL_FILE "/channel.cfg"
|
||||||
|
#define WDT_TIMEOUT_MS 10000
|
||||||
|
|
||||||
|
#include <RadioLib.h>
|
||||||
|
#include <helpers/radiolib/CustomSX1262Wrapper.h>
|
||||||
|
#include <helpers/AutoDiscoverRTCClock.h>
|
||||||
|
|
||||||
|
#ifdef PROMICRO
|
||||||
|
#include <PromicroBoard.h>
|
||||||
|
#elif defined(HELTEC_T114)
|
||||||
|
#include <T114Board.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PROMICRO
|
||||||
|
extern PromicroBoard board;
|
||||||
|
extern RADIO_CLASS radio_module;
|
||||||
|
extern WRAPPER_CLASS radio_driver;
|
||||||
|
#elif defined(HELTEC_T114)
|
||||||
|
extern T114Board board;
|
||||||
|
extern RADIO_CLASS radio_module;
|
||||||
|
extern WRAPPER_CLASS radio_driver;
|
||||||
|
#else
|
||||||
|
extern RADIO_CLASS radio_module;
|
||||||
|
extern WRAPPER_CLASS radio_driver;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern AutoDiscoverRTCClock rtc_clock;
|
||||||
|
|
||||||
|
bool radio_init();
|
||||||
|
mesh::LocalIdentity radio_new_identity();
|
||||||
59
src/variants/T114Board.cpp
Normal file
59
src/variants/T114Board.cpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#include "T114Board.h"
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
#ifdef NRF52_POWER_MANAGEMENT
|
||||||
|
// Static configuration for power management
|
||||||
|
// Values come from variant.h defines
|
||||||
|
const PowerMgtConfig power_config = {
|
||||||
|
.lpcomp_ain_channel = PWRMGT_LPCOMP_AIN,
|
||||||
|
.lpcomp_refsel = PWRMGT_LPCOMP_REFSEL,
|
||||||
|
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK
|
||||||
|
};
|
||||||
|
|
||||||
|
void T114Board::initiateShutdown(uint8_t reason) {
|
||||||
|
#if ENV_INCLUDE_GPS == 1
|
||||||
|
pinMode(GPS_EN, OUTPUT);
|
||||||
|
digitalWrite(GPS_EN, LOW);
|
||||||
|
#endif
|
||||||
|
digitalWrite(SX126X_POWER_EN, LOW);
|
||||||
|
|
||||||
|
bool enable_lpcomp = (reason == SHUTDOWN_REASON_LOW_VOLTAGE ||
|
||||||
|
reason == SHUTDOWN_REASON_BOOT_PROTECT);
|
||||||
|
pinMode(PIN_BAT_CTL, OUTPUT);
|
||||||
|
digitalWrite(PIN_BAT_CTL, enable_lpcomp ? HIGH : LOW);
|
||||||
|
|
||||||
|
if (enable_lpcomp) {
|
||||||
|
configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel);
|
||||||
|
}
|
||||||
|
|
||||||
|
enterSystemOff(reason);
|
||||||
|
}
|
||||||
|
#endif // NRF52_POWER_MANAGEMENT
|
||||||
|
|
||||||
|
void T114Board::begin() {
|
||||||
|
NRF52Board::begin();
|
||||||
|
|
||||||
|
pinMode(PIN_VBAT_READ, INPUT);
|
||||||
|
|
||||||
|
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
|
||||||
|
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Wire.begin();
|
||||||
|
|
||||||
|
#ifdef P_LORA_TX_LED
|
||||||
|
pinMode(P_LORA_TX_LED, OUTPUT);
|
||||||
|
digitalWrite(P_LORA_TX_LED, HIGH);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pinMode(SX126X_POWER_EN, OUTPUT);
|
||||||
|
#ifdef NRF52_POWER_MANAGEMENT
|
||||||
|
// Boot voltage protection check (may not return if voltage too low)
|
||||||
|
// We need to call this after we configure SX126X_POWER_EN as output but before we pull high
|
||||||
|
checkBootVoltage(&power_config);
|
||||||
|
#endif
|
||||||
|
digitalWrite(SX126X_POWER_EN, HIGH);
|
||||||
|
delay(10); // give sx1262 some time to power up
|
||||||
|
}
|
||||||
59
src/variants/T114Board.h
Normal file
59
src/variants/T114Board.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <MeshCore.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <helpers/NRF52Board.h>
|
||||||
|
|
||||||
|
// built-ins
|
||||||
|
#define PIN_VBAT_READ 4
|
||||||
|
#define PIN_BAT_CTL 6
|
||||||
|
#define MV_LSB (3000.0F / 4096.0F) // 12-bit ADC with 3.0V input range
|
||||||
|
|
||||||
|
class T114Board : public NRF52BoardDCDC {
|
||||||
|
protected:
|
||||||
|
#ifdef NRF52_POWER_MANAGEMENT
|
||||||
|
void initiateShutdown(uint8_t reason) override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
T114Board() : NRF52Board("T114_OTA") {}
|
||||||
|
void begin();
|
||||||
|
|
||||||
|
#if defined(P_LORA_TX_LED)
|
||||||
|
void onBeforeTransmit() override {
|
||||||
|
digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED on
|
||||||
|
}
|
||||||
|
void onAfterTransmit() override {
|
||||||
|
digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED off
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint16_t getBattMilliVolts() override {
|
||||||
|
int adcvalue = 0;
|
||||||
|
analogReadResolution(12);
|
||||||
|
analogReference(AR_INTERNAL_3_0);
|
||||||
|
pinMode(PIN_BAT_CTL, OUTPUT); // battery adc can be read only ctrl pin 6 set to high
|
||||||
|
digitalWrite(PIN_BAT_CTL, 1);
|
||||||
|
|
||||||
|
delay(10);
|
||||||
|
adcvalue = analogRead(PIN_VBAT_READ);
|
||||||
|
digitalWrite(6, 0);
|
||||||
|
|
||||||
|
return (uint16_t)((float)adcvalue * MV_LSB * 4.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* getManufacturerName() const override {
|
||||||
|
return "Heltec T114";
|
||||||
|
}
|
||||||
|
|
||||||
|
void powerOff() override {
|
||||||
|
#ifdef LED_PIN
|
||||||
|
digitalWrite(LED_PIN, HIGH);
|
||||||
|
#endif
|
||||||
|
#if ENV_INCLUDE_GPS == 1
|
||||||
|
pinMode(GPS_EN, OUTPUT);
|
||||||
|
digitalWrite(GPS_EN, LOW);
|
||||||
|
#endif
|
||||||
|
sd_power_system_off();
|
||||||
|
}
|
||||||
|
};
|
||||||
25
src/variants/promicro/PromicroBoard.cpp
Normal file
25
src/variants/promicro/PromicroBoard.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
#include "PromicroBoard.h"
|
||||||
|
|
||||||
|
void PromicroBoard::begin() {
|
||||||
|
NRF52Board::begin();
|
||||||
|
btn_prev_state = HIGH;
|
||||||
|
|
||||||
|
pinMode(PIN_VBAT_READ, INPUT);
|
||||||
|
|
||||||
|
#ifdef BUTTON_PIN
|
||||||
|
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
|
||||||
|
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Wire.begin();
|
||||||
|
|
||||||
|
pinMode(SX126X_POWER_EN, OUTPUT);
|
||||||
|
digitalWrite(SX126X_POWER_EN, HIGH);
|
||||||
|
delay(10); // give sx1262 some time to power up
|
||||||
|
}
|
||||||
79
src/variants/promicro/PromicroBoard.h
Normal file
79
src/variants/promicro/PromicroBoard.h
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <MeshCore.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <helpers/NRF52Board.h>
|
||||||
|
|
||||||
|
#define P_LORA_NSS 13 //P1.13 45
|
||||||
|
#define P_LORA_DIO_1 11 //P0.10 10
|
||||||
|
#define P_LORA_RESET 10 //P0.09 9
|
||||||
|
#define P_LORA_BUSY 16 //P0.29 29
|
||||||
|
#define P_LORA_MISO 15 //P0.02 2
|
||||||
|
#define P_LORA_SCLK 12 //P1.11 43
|
||||||
|
#define P_LORA_MOSI 14 //P1.15 47
|
||||||
|
#define SX126X_POWER_EN 21 //P0.13 13
|
||||||
|
#define SX126X_RXEN 2 //P0.17
|
||||||
|
#define SX126X_TXEN RADIOLIB_NC
|
||||||
|
#define SX126X_DIO2_AS_RF_SWITCH true
|
||||||
|
#define SX126X_DIO3_TCXO_VOLTAGE (1.8f)
|
||||||
|
|
||||||
|
#define PIN_VBAT_READ 17
|
||||||
|
#define ADC_MULTIPLIER (1.815f) // dependent on voltage divider resistors. TODO: more accurate battery tracking
|
||||||
|
|
||||||
|
class PromicroBoard : public NRF52BoardDCDC {
|
||||||
|
protected:
|
||||||
|
uint8_t btn_prev_state;
|
||||||
|
float adc_mult = ADC_MULTIPLIER;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PromicroBoard() : NRF52Board("ProMicro_OTA") {}
|
||||||
|
void begin();
|
||||||
|
|
||||||
|
#define BATTERY_SAMPLES 8
|
||||||
|
|
||||||
|
uint16_t getBattMilliVolts() override {
|
||||||
|
analogReadResolution(12);
|
||||||
|
|
||||||
|
uint32_t raw = 0;
|
||||||
|
for (int i = 0; i < BATTERY_SAMPLES; i++) {
|
||||||
|
raw += analogRead(PIN_VBAT_READ);
|
||||||
|
}
|
||||||
|
raw = raw / BATTERY_SAMPLES;
|
||||||
|
return (adc_mult * raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setAdcMultiplier(float multiplier) override {
|
||||||
|
if (multiplier == 0.0f) {
|
||||||
|
adc_mult = ADC_MULTIPLIER;}
|
||||||
|
else {
|
||||||
|
adc_mult = multiplier;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
float getAdcMultiplier() const override {
|
||||||
|
if (adc_mult == 0.0f) {
|
||||||
|
return ADC_MULTIPLIER;
|
||||||
|
} else {
|
||||||
|
return adc_mult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* getManufacturerName() const override {
|
||||||
|
return "ProMicro DIY";
|
||||||
|
}
|
||||||
|
|
||||||
|
int buttonStateChanged() {
|
||||||
|
#ifdef BUTTON_PIN
|
||||||
|
uint8_t v = digitalRead(BUTTON_PIN);
|
||||||
|
if (v != btn_prev_state) {
|
||||||
|
btn_prev_state = v;
|
||||||
|
return (v == LOW) ? 1 : -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void powerOff() override {
|
||||||
|
sd_power_system_off();
|
||||||
|
}
|
||||||
|
};
|
||||||
15
src/variants/promicro/variant.cpp
Normal file
15
src/variants/promicro/variant.cpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#include "variant.h"
|
||||||
|
#include "wiring_constants.h"
|
||||||
|
#include "wiring_digital.h"
|
||||||
|
|
||||||
|
const uint32_t g_ADigitalPinMap[] = {
|
||||||
|
8, 6, 17, 20, 22, 24, 32, 11, 36, 38,
|
||||||
|
9, 10, 43, 45, 47, 2, 29, 31,
|
||||||
|
33, 34, 37,
|
||||||
|
13, 15
|
||||||
|
};
|
||||||
|
|
||||||
|
void initVariant()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
82
src/variants/promicro/variant.h
Normal file
82
src/variants/promicro/variant.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* variant.h
|
||||||
|
* Copyright (C) 2023 Seeed K.K.
|
||||||
|
* MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "WVariant.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Low frequency clock source
|
||||||
|
|
||||||
|
#define VARIANT_MCK (64000000ul)
|
||||||
|
|
||||||
|
//#define USE_LFXO // 32.768 kHz crystal oscillator
|
||||||
|
#define USE_LFRC // 32.768 kHz RC oscillator
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Power
|
||||||
|
|
||||||
|
#define PIN_EXT_VCC (21)
|
||||||
|
#define EXT_VCC (PIN_EXT_VCC)
|
||||||
|
|
||||||
|
#define BATTERY_PIN (17)
|
||||||
|
#define ADC_RESOLUTION 12
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Number of pins
|
||||||
|
|
||||||
|
#define PINS_COUNT (23)
|
||||||
|
#define NUM_DIGITAL_PINS (23)
|
||||||
|
#define NUM_ANALOG_INPUTS (3)
|
||||||
|
#define NUM_ANALOG_OUTPUTS (0)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UART pin definition
|
||||||
|
|
||||||
|
#define PIN_SERIAL1_TX (1)
|
||||||
|
#define PIN_SERIAL1_RX (0)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// I2C pin definition
|
||||||
|
|
||||||
|
#define WIRE_INTERFACES_COUNT 2
|
||||||
|
|
||||||
|
#define PIN_WIRE_SDA (6)
|
||||||
|
#define PIN_WIRE_SCL (7)
|
||||||
|
#define PIN_WIRE1_SDA (13)
|
||||||
|
#define PIN_WIRE1_SCL (14)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// SPI pin definition
|
||||||
|
|
||||||
|
#define SPI_INTERFACES_COUNT 2
|
||||||
|
|
||||||
|
#define PIN_SPI_SCK (2)
|
||||||
|
#define PIN_SPI_MISO (3)
|
||||||
|
#define PIN_SPI_MOSI (4)
|
||||||
|
|
||||||
|
#define PIN_SPI_NSS (5)
|
||||||
|
|
||||||
|
#define PIN_SPI1_SCK (18)
|
||||||
|
#define PIN_SPI1_MISO (19)
|
||||||
|
#define PIN_SPI1_MOSI (20)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Builtin LEDs
|
||||||
|
|
||||||
|
#define PIN_LED (22)
|
||||||
|
#define LED_PIN PIN_LED
|
||||||
|
#define LED_BLUE PIN_LED
|
||||||
|
#define LED_BUILTIN PIN_LED
|
||||||
|
#define LED_STATE_ON 1
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Builtin buttons
|
||||||
|
|
||||||
|
#define PIN_BUTTON1 (6)
|
||||||
|
#define BUTTON_PIN PIN_BUTTON1
|
||||||
|
|
||||||
|
|
||||||
15
src/variants/variant.cpp
Normal file
15
src/variants/variant.cpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#include "variant.h"
|
||||||
|
#include "wiring_constants.h"
|
||||||
|
#include "wiring_digital.h"
|
||||||
|
|
||||||
|
const uint32_t g_ADigitalPinMap[] = {
|
||||||
|
0xff, 0xff, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
||||||
|
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
||||||
|
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
|
||||||
|
40, 41, 42, 43, 44, 45, 46, 47
|
||||||
|
};
|
||||||
|
|
||||||
|
void initVariant()
|
||||||
|
{
|
||||||
|
pinMode(PIN_USER_BTN, INPUT);
|
||||||
|
}
|
||||||
142
src/variants/variant.h
Normal file
142
src/variants/variant.h
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* variant.h
|
||||||
|
* Copyright (C) 2023 Seeed K.K.
|
||||||
|
* MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "WVariant.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Low frequency clock source
|
||||||
|
|
||||||
|
#define USE_LFXO // 32.768 kHz crystal oscillator
|
||||||
|
#define VARIANT_MCK (64000000ul)
|
||||||
|
|
||||||
|
#define WIRE_INTERFACES_COUNT (2)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Power
|
||||||
|
|
||||||
|
#define NRF_APM
|
||||||
|
#define PIN_3V3_EN (38)
|
||||||
|
|
||||||
|
#define BATTERY_PIN (4)
|
||||||
|
#define ADC_MULTIPLIER (4.90F)
|
||||||
|
|
||||||
|
#define ADC_RESOLUTION (14)
|
||||||
|
#define BATTERY_SENSE_RES (12)
|
||||||
|
|
||||||
|
#define AREF_VOLTAGE (3.0)
|
||||||
|
|
||||||
|
// Power management boot protection threshold (millivolts)
|
||||||
|
// Set to 0 to disable boot protection
|
||||||
|
#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV)
|
||||||
|
// LPCOMP wake configuration (voltage recovery from SYSTEMOFF)
|
||||||
|
// AIN2 = P0.04 = BATTERY_PIN / PIN_VBAT_READ
|
||||||
|
#define PWRMGT_LPCOMP_AIN 2
|
||||||
|
#define PWRMGT_LPCOMP_REFSEL 1 // 2/8 VDD (~3.68-4.04V)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Number of pins
|
||||||
|
|
||||||
|
#define PINS_COUNT (48)
|
||||||
|
#define NUM_DIGITAL_PINS (48)
|
||||||
|
#define NUM_ANALOG_INPUTS (1)
|
||||||
|
#define NUM_ANALOG_OUTPUTS (0)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UART pin definition
|
||||||
|
|
||||||
|
#define PIN_SERIAL1_RX (37)
|
||||||
|
#define PIN_SERIAL1_TX (39)
|
||||||
|
|
||||||
|
#define PIN_SERIAL2_RX (9)
|
||||||
|
#define PIN_SERIAL2_TX (10)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// I2C pin definition
|
||||||
|
|
||||||
|
#define PIN_WIRE_SDA (26) // P0.26
|
||||||
|
#define PIN_WIRE_SCL (27) // P0.27
|
||||||
|
|
||||||
|
#define PIN_WIRE1_SDA (7) // P0.8
|
||||||
|
#define PIN_WIRE1_SCL (8) // P0.7
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// SPI pin definition
|
||||||
|
|
||||||
|
#define SPI_INTERFACES_COUNT (2)
|
||||||
|
|
||||||
|
#define PIN_SPI_MISO (23)
|
||||||
|
#define PIN_SPI_MOSI (22)
|
||||||
|
#define PIN_SPI_SCK (19)
|
||||||
|
#define PIN_SPI_NSS (24)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Builtin LEDs
|
||||||
|
|
||||||
|
#define LED_BUILTIN (35)
|
||||||
|
#define PIN_LED LED_BUILTIN
|
||||||
|
#define LED_RED LED_BUILTIN
|
||||||
|
#define LED_BLUE (-1) // No blue led, prevents Bluefruit flashing the green LED during advertising
|
||||||
|
#define LED_PIN LED_BUILTIN
|
||||||
|
|
||||||
|
#define LED_STATE_ON LOW
|
||||||
|
|
||||||
|
#define PIN_NEOPIXEL (14)
|
||||||
|
#define NEOPIXEL_NUM (2)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Builtin buttons
|
||||||
|
|
||||||
|
#define PIN_BUTTON1 (42)
|
||||||
|
#define BUTTON_PIN PIN_BUTTON1
|
||||||
|
|
||||||
|
// #define PIN_BUTTON2 (11)
|
||||||
|
// #define BUTTON_PIN2 PIN_BUTTON2
|
||||||
|
|
||||||
|
#define PIN_USER_BTN BUTTON_PIN
|
||||||
|
|
||||||
|
#define EXTERNAL_FLASH_DEVICES MX25R1635F
|
||||||
|
#define EXTERNAL_FLASH_USE_QSPI
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Lora
|
||||||
|
|
||||||
|
#define USE_SX1262
|
||||||
|
#define LORA_CS (24)
|
||||||
|
#define SX126X_DIO1 (20)
|
||||||
|
#define SX126X_BUSY (17)
|
||||||
|
#define SX126X_RESET (25)
|
||||||
|
#define SX126X_DIO2_AS_RF_SWITCH
|
||||||
|
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
||||||
|
|
||||||
|
#define PIN_SPI1_MISO (43)
|
||||||
|
#define PIN_SPI1_MOSI (41)
|
||||||
|
#define PIN_SPI1_SCK (40)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Buzzer
|
||||||
|
|
||||||
|
// #define PIN_BUZZER (46)
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// GPS
|
||||||
|
|
||||||
|
#define GPS_EN (21)
|
||||||
|
#define GPS_RESET (38)
|
||||||
|
#define PIN_GPS_RX (39) // This is for bits going TOWARDS the GPS
|
||||||
|
#define PIN_GPS_TX (37) // This is for bits going TOWARDS the CPU
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TFT
|
||||||
|
#define PIN_TFT_SCL (40)
|
||||||
|
#define PIN_TFT_SDA (41)
|
||||||
|
#define PIN_TFT_RST (2)
|
||||||
|
#define PIN_TFT_VDD_CTL (3)
|
||||||
|
#define PIN_TFT_LEDA_CTL (15)
|
||||||
|
#define PIN_TFT_CS (11)
|
||||||
|
#define PIN_TFT_DC (12)
|
||||||
Reference in New Issue
Block a user