Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 47 additions & 2 deletions examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
#define TXT_ACK_DELAY 200
#endif

#ifndef TELEM_REPLY_ZEROHOP_DEFAULT
#define TELEM_REPLY_ZEROHOP_DEFAULT 0
#endif

#define FIRMWARE_VER_LEVEL 2

#define REQ_TYPE_GET_STATUS 0x01 // same as _GET_STATS
Expand All @@ -60,6 +64,10 @@

#define LAZY_CONTACTS_WRITE_DELAY 5000

static void setRepeaterTxPower(int8_t power_dbm) {
radio_set_tx_power(power_dbm);
}

void MyMesh::putNeighbour(const mesh::Identity &id, uint32_t timestamp, float snr) {
#if MAX_NEIGHBOURS // check if neighbours enabled
// find existing neighbour, else use least recently updated
Expand Down Expand Up @@ -426,6 +434,14 @@ void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, ui
}
}

void MyMesh::applyTelemReplyPower(mesh::Packet* packet) {
#ifdef TELEM_REPLY_PER_PACKET_POWER
if (packet && _prefs.telem_reply_power_dbm != TELEM_REPLY_POWER_UNSET) {
packet->_tx_power = _prefs.telem_reply_power_dbm;
}
#endif
}

bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
if (_prefs.disable_fwd) return false;
if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false;
Expand Down Expand Up @@ -587,6 +603,15 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m

if (reply_len == 0) return; // invalid request

if (_prefs.telem_reply_zerohop) {
mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, secret, reply_data, reply_len);
if (reply) {
applyTelemReplyPower(reply);
sendZeroHop(reply, SERVER_RESPONSE_DELAY);
}
return;
}

if (packet->isRouteFlood()) {
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
mesh::Packet* path = createPathReturn(sender, secret, packet->path, packet->path_len,
Expand Down Expand Up @@ -663,6 +688,16 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
client->last_timestamp = timestamp;
client->last_activity = getRTCClock()->getCurrentTime();

if (_prefs.telem_reply_zerohop) {
mesh::Packet *reply =
createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len);
if (reply) {
applyTelemReplyPower(reply);
sendZeroHop(reply, SERVER_RESPONSE_DELAY);
}
return;
}

if (packet->isRouteFlood()) {
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
mesh::Packet *path = createPathReturn(client->id, secret, packet->path, packet->path_len,
Expand Down Expand Up @@ -705,7 +740,10 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,

mesh::Packet *ack = createAck(ack_hash);
if (ack) {
if (client->out_path_len == OUT_PATH_UNKNOWN) {
if (_prefs.telem_reply_zerohop) {
applyTelemReplyPower(ack);
sendZeroHop(ack, TXT_ACK_DELAY);
} else if (client->out_path_len == OUT_PATH_UNKNOWN) {
sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize());
} else {
sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY);
Expand Down Expand Up @@ -733,7 +771,10 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,

auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len);
if (reply) {
if (client->out_path_len == OUT_PATH_UNKNOWN) {
if (_prefs.telem_reply_zerohop) {
applyTelemReplyPower(reply);
sendZeroHop(reply, CLI_REPLY_DELAY_MILLIS);
} else if (client->out_path_len == OUT_PATH_UNKNOWN) {
sendFloodReply(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize());
} else {
sendDirect(reply, client->out_path, client->out_path_len, CLI_REPLY_DELAY_MILLIS);
Expand Down Expand Up @@ -883,6 +924,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.bw = LORA_BW;
_prefs.cr = LORA_CR;
_prefs.tx_power_dbm = LORA_TX_POWER;
_prefs.telem_reply_zerohop = TELEM_REPLY_ZEROHOP_DEFAULT;
_prefs.telem_reply_power_dbm = TELEM_REPLY_POWER_UNSET;
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
_prefs.flood_advert_interval = 12; // 12 hours
_prefs.flood_max = 64;
Expand Down Expand Up @@ -955,6 +998,7 @@ void MyMesh::begin(FILESYSTEM *fs) {

radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_set_tx_power(_prefs.tx_power_dbm);
setTxPowerControl(setRepeaterTxPower, _prefs.tx_power_dbm);

radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
Expand Down Expand Up @@ -1051,6 +1095,7 @@ void MyMesh::dumpLogFile() {

void MyMesh::setTxPower(int8_t power_dbm) {
radio_set_tx_power(power_dbm);
setNodeTxPower(power_dbm);
}

#if defined(USE_SX1262) || defined(USE_SX1268)
Expand Down
1 change: 1 addition & 0 deletions examples/simple_repeater/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {

File openAppend(const char* fname);
bool isLooped(const mesh::Packet* packet, const uint8_t max_counters[]);
void applyTelemReplyPower(mesh::Packet* packet);

protected:
float getAirtimeBudgetFactor() const override {
Expand Down
17 changes: 16 additions & 1 deletion src/Dispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ uint32_t Dispatcher::getCADFailMaxDuration() const {
return 4000; // 4 seconds
}

void Dispatcher::restoreTxPower() {
if (_tx_power_overridden && _set_tx_power_fn) {
_set_tx_power_fn(_node_tx_power);
}
_tx_power_overridden = false;
}

void Dispatcher::loop() {
if (millisHasNowPassed(next_floor_calib_time)) {
_radio->triggerNoiseFloorCalibrate(getInterferenceThreshold());
Expand Down Expand Up @@ -111,6 +118,7 @@ void Dispatcher::loop() {
} else {
n_sent_direct++;
}
restoreTxPower();
releasePacket(outbound); // return to pool
outbound = NULL;
} else if (millisHasNowPassed(outbound_expiry)) {
Expand All @@ -119,6 +127,7 @@ void Dispatcher::loop() {
_radio->onSendFinished();
logTxFail(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len);

restoreTxPower();
releasePacket(outbound); // return to pool
outbound = NULL;
} else {
Expand Down Expand Up @@ -323,6 +332,10 @@ void Dispatcher::checkSend() {
} else {
memcpy(&raw[len], outbound->payload, outbound->payload_len); len += outbound->payload_len;

if (_set_tx_power_fn && outbound->_tx_power != PACKET_TX_POWER_DEFAULT) {
_set_tx_power_fn(outbound->_tx_power);
_tx_power_overridden = true;
}
uint32_t max_airtime = _radio->getEstAirtimeFor(len)*3/2;
outbound_start = _ms->getMillis();
bool success = _radio->startSendRaw(raw, len);
Expand All @@ -331,6 +344,7 @@ void Dispatcher::checkSend() {

logTxFail(outbound, outbound->getRawLength());

restoreTxPower();
releasePacket(outbound); // return to pool
outbound = NULL;
return;
Expand Down Expand Up @@ -359,6 +373,7 @@ Packet* Dispatcher::obtainNewPacket() {
} else {
pkt->payload_len = pkt->path_len = 0;
pkt->_snr = 0;
pkt->_tx_power = PACKET_TX_POWER_DEFAULT;
}
return pkt;
}
Expand Down Expand Up @@ -386,4 +401,4 @@ unsigned long Dispatcher::futureMillis(int millis_from_now) const {
return _ms->getMillis() + millis_from_now;
}

}
}
12 changes: 12 additions & 0 deletions src/Dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,13 @@ class Dispatcher {
unsigned long tx_budget_ms;
unsigned long last_budget_update;
unsigned long duty_cycle_window_ms;
void (*_set_tx_power_fn)(int8_t);
int8_t _node_tx_power;
bool _tx_power_overridden;

void processRecvPacket(Packet* pkt);
void updateTxBudget();
void restoreTxPower();

protected:
PacketManager* _mgr;
Expand All @@ -150,6 +154,9 @@ class Dispatcher {
tx_budget_ms = 0;
last_budget_update = 0;
duty_cycle_window_ms = 3600000;
_set_tx_power_fn = NULL;
_node_tx_power = 0;
_tx_power_overridden = false;
}

virtual DispatcherAction onRecvPacket(Packet* pkt) = 0;
Expand All @@ -176,6 +183,11 @@ class Dispatcher {
Packet* obtainNewPacket();
void releasePacket(Packet* packet);
void sendPacket(Packet* packet, uint8_t priority, uint32_t delay_millis=0);
void setTxPowerControl(void (*fn)(int8_t), int8_t node_dbm) {
_set_tx_power_fn = fn;
_node_tx_power = node_dbm;
}
void setNodeTxPower(int8_t node_dbm) { _node_tx_power = node_dbm; }

unsigned long getTotalAirTime() const { return total_air_time; }
unsigned long getReceiveAirTime() const {return rx_air_time; }
Expand Down
3 changes: 2 additions & 1 deletion src/Packet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Packet::Packet() {
header = 0;
path_len = 0;
payload_len = 0;
_tx_power = PACKET_TX_POWER_DEFAULT;
}

bool Packet::isValidPathLen(uint8_t path_len) {
Expand Down Expand Up @@ -84,4 +85,4 @@ bool Packet::readFrom(const uint8_t src[], uint8_t len) {
return true; // success
}

}
}
3 changes: 3 additions & 0 deletions src/Packet.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ namespace mesh {
#define ROUTE_TYPE_DIRECT 0x02 // direct route, 'path' is supplied
#define ROUTE_TYPE_TRANSPORT_DIRECT 0x03 // direct route + transport codes

#define PACKET_TX_POWER_DEFAULT ((int8_t)0x7F)

#define PAYLOAD_TYPE_REQ 0x00 // request (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
#define PAYLOAD_TYPE_RESPONSE 0x01 // response to REQ or ANON_REQ (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
#define PAYLOAD_TYPE_TXT_MSG 0x02 // a plain text message (prefixed with dest/src hashes, MAC) (enc data: timestamp, text)
Expand Down Expand Up @@ -49,6 +51,7 @@ class Packet {
uint8_t path[MAX_PATH_SIZE];
uint8_t payload[MAX_PACKET_PAYLOAD];
int8_t _snr;
int8_t _tx_power;

/**
* \brief calculate the hash of payload + type
Expand Down
34 changes: 30 additions & 4 deletions src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
// next: 291
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
file.read((uint8_t *)&_prefs->telem_reply_zerohop, sizeof(_prefs->telem_reply_zerohop)); // 291
file.read((uint8_t *)&_prefs->telem_reply_power_dbm, sizeof(_prefs->telem_reply_power_dbm)); // 292
// next: 293

// sanitise bad pref values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
Expand Down Expand Up @@ -118,6 +120,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {

// sanitise settings
_prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
_prefs->telem_reply_zerohop = constrain(_prefs->telem_reply_zerohop, 0, 1); // boolean

file.close();
}
Expand Down Expand Up @@ -178,8 +181,10 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
// next: 291
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
file.write((uint8_t *)&_prefs->telem_reply_zerohop, sizeof(_prefs->telem_reply_zerohop)); // 291
file.write((uint8_t *)&_prefs->telem_reply_power_dbm, sizeof(_prefs->telem_reply_power_dbm)); // 292
// next: 293

file.close();
}
Expand Down Expand Up @@ -492,6 +497,19 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->multi_acks = atoi(&config[11]);
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "telem.reply.zerohop ", 20) == 0) {
_prefs->telem_reply_zerohop = (memcmp(&config[20], "on", 2) == 0) ? 1 : 0;
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "telem.reply.power ", 18) == 0) {
const char* v = &config[18];
if (memcmp(v, "off", 3) == 0) {
_prefs->telem_reply_power_dbm = TELEM_REPLY_POWER_UNSET;
} else {
_prefs->telem_reply_power_dbm = (int8_t)atoi(v);
}
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "allow.read.only ", 16) == 0) {
_prefs->allow_read_only = memcmp(&config[16], "on", 2) == 0;
savePrefs();
Expand Down Expand Up @@ -745,6 +763,14 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4);
} else if (memcmp(config, "multi.acks", 10) == 0) {
sprintf(reply, "> %d", (uint32_t) _prefs->multi_acks);
} else if (memcmp(config, "telem.reply.zerohop", 19) == 0) {
sprintf(reply, "> %s", _prefs->telem_reply_zerohop ? "on" : "off");
} else if (memcmp(config, "telem.reply.power", 17) == 0) {
if (_prefs->telem_reply_power_dbm == TELEM_REPLY_POWER_UNSET) {
strcpy(reply, "> off");
} else {
sprintf(reply, "> %d", (int32_t)_prefs->telem_reply_power_dbm);
}
} else if (memcmp(config, "allow.read.only", 15) == 0) {
sprintf(reply, "> %s", _prefs->allow_read_only ? "on" : "off");
} else if (memcmp(config, "flood.advert.interval", 21) == 0) {
Expand Down
4 changes: 4 additions & 0 deletions src/helpers/CommonCLI.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#define LOOP_DETECT_MODERATE 2
#define LOOP_DETECT_STRICT 3

#define TELEM_REPLY_POWER_UNSET ((int8_t)-128)

struct NodePrefs { // persisted to file
float airtime_factor;
char node_name[32];
Expand Down Expand Up @@ -61,6 +63,8 @@ struct NodePrefs { // persisted to file
uint8_t rx_boosted_gain; // power settings
uint8_t path_hash_mode; // which path mode to use when sending
uint8_t loop_detect;
uint8_t telem_reply_zerohop;
int8_t telem_reply_power_dbm;
};

class CommonCLICallbacks {
Expand Down
6 changes: 5 additions & 1 deletion src/helpers/StaticPoolPacketManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ StaticPoolPacketManager::StaticPoolPacketManager(int pool_size): unused(pool_siz
}

mesh::Packet* StaticPoolPacketManager::allocNew() {
return unused.removeByIdx(0); // just get first one (returns NULL if empty)
mesh::Packet* packet = unused.removeByIdx(0); // just get first one (returns NULL if empty)
if (packet) {
packet->_tx_power = PACKET_TX_POWER_DEFAULT;
}
return packet;
}

void StaticPoolPacketManager::free(mesh::Packet* packet) {
Expand Down
1 change: 1 addition & 0 deletions variants/rak3401/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ build_flags =
-D ADVERT_LON=0.0
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
-D TELEM_REPLY_PER_PACKET_POWER
;-D MESH_PACKET_LOGGING=1
;-D MESH_DEBUG=1
build_src_filter = ${rak3401.build_src_filter}
Expand Down