|
|
|
@@ -73,13 +73,15 @@ void MyMesh::pushPostToClient(ClientInfo *client, PostInfo &post) {
|
|
|
|
|
|
|
|
|
|
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, client->shared_secret, reply_data, len);
|
|
|
|
|
if (reply) {
|
|
|
|
|
if (client->out_path_len < 0) {
|
|
|
|
|
sendFlood(reply);
|
|
|
|
|
if (client->out_path_len == OUT_PATH_UNKNOWN) {
|
|
|
|
|
unsigned long delay_millis = 0;
|
|
|
|
|
sendFloodScoped(default_scope, reply, delay_millis, _prefs.path_hash_mode + 1); // REVISIT
|
|
|
|
|
client->extra.room.ack_timeout = futureMillis(PUSH_ACK_TIMEOUT_FLOOD);
|
|
|
|
|
} else {
|
|
|
|
|
sendDirect(reply, client->out_path, client->out_path_len);
|
|
|
|
|
client->extra.room.ack_timeout =
|
|
|
|
|
futureMillis(PUSH_TIMEOUT_BASE + PUSH_ACK_TIMEOUT_FACTOR * (client->out_path_len + 1));
|
|
|
|
|
|
|
|
|
|
uint8_t path_hash_count = client->out_path_len & 63;
|
|
|
|
|
client->extra.room.ack_timeout = futureMillis(PUSH_TIMEOUT_BASE + PUSH_ACK_TIMEOUT_FACTOR * (path_hash_count + 1));
|
|
|
|
|
}
|
|
|
|
|
_num_post_pushes++; // stats
|
|
|
|
|
} else {
|
|
|
|
@@ -138,7 +140,7 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t
|
|
|
|
|
if (payload[0] == REQ_TYPE_GET_STATUS) {
|
|
|
|
|
ServerStats stats;
|
|
|
|
|
stats.batt_milli_volts = board.getBattMilliVolts();
|
|
|
|
|
stats.curr_tx_queue_len = _mgr->getOutboundCount(0xFFFFFFFF);
|
|
|
|
|
stats.curr_tx_queue_len = _mgr->getOutboundTotal();
|
|
|
|
|
stats.noise_floor = (int16_t)_radio->getNoiseFloor();
|
|
|
|
|
stats.last_rssi = (int16_t)radio_driver.getLastRSSI();
|
|
|
|
|
stats.n_packets_recv = radio_driver.getPacketsRecv();
|
|
|
|
@@ -170,6 +172,12 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t
|
|
|
|
|
}
|
|
|
|
|
sensors.querySensors(perm_mask, telemetry);
|
|
|
|
|
|
|
|
|
|
// This default temperature will be overridden by external sensors (if any)
|
|
|
|
|
float temperature = board.getMCUTemperature();
|
|
|
|
|
if(!isnan(temperature)) { // Supported boards with built-in temperature sensor. ESP32-C3 may return NAN
|
|
|
|
|
telemetry.addTemperature(TELEM_CHANNEL_SELF, temperature); // Built-in MCU Temperature
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t tlen = telemetry.getSize();
|
|
|
|
|
memcpy(&reply_data[4], telemetry.getBuffer(), tlen);
|
|
|
|
|
return 4 + tlen; // reply_len
|
|
|
|
@@ -264,20 +272,37 @@ const char *MyMesh::getLogDateTime() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t MyMesh::getRetransmitDelay(const mesh::Packet *packet) {
|
|
|
|
|
uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.tx_delay_factor);
|
|
|
|
|
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.tx_delay_factor);
|
|
|
|
|
return getRNG()->nextInt(0, 5*t + 1);
|
|
|
|
|
}
|
|
|
|
|
uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {
|
|
|
|
|
uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.direct_tx_delay_factor);
|
|
|
|
|
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.direct_tx_delay_factor);
|
|
|
|
|
return getRNG()->nextInt(0, 5*t + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
|
|
|
|
|
if (_prefs.disable_fwd) return false;
|
|
|
|
|
if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false;
|
|
|
|
|
if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) {
|
|
|
|
|
// just try to determine region for packet (apply later in allowPacketForward())
|
|
|
|
|
if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
|
|
|
|
recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD);
|
|
|
|
|
} else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) {
|
|
|
|
|
if (region_map.getWildcard().flags & REGION_DENY_FLOOD) {
|
|
|
|
|
recv_pkt_region = NULL;
|
|
|
|
|
} else {
|
|
|
|
|
recv_pkt_region = ®ion_map.getWildcard();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
recv_pkt_region = NULL;
|
|
|
|
|
}
|
|
|
|
|
// do normal processing
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender,
|
|
|
|
|
uint8_t *data, size_t len) {
|
|
|
|
|
if (packet->getPayloadType() == PAYLOAD_TYPE_ANON_REQ) { // received an initial request by a possible admin
|
|
|
|
@@ -333,7 +358,7 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (packet->isRouteFlood()) {
|
|
|
|
|
client->out_path_len = -1; // need to rediscover out_path
|
|
|
|
|
client->out_path_len = OUT_PATH_UNKNOWN; // need to rediscover out_path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t now = getRTCClock()->getCurrentTimeUnique();
|
|
|
|
@@ -353,14 +378,14 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m
|
|
|
|
|
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response
|
|
|
|
|
mesh::Packet *path = createPathReturn(sender, client->shared_secret, packet->path, packet->path_len,
|
|
|
|
|
PAYLOAD_TYPE_RESPONSE, reply_data, 13);
|
|
|
|
|
if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
|
|
|
|
|
if (path) sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
|
|
|
|
} else {
|
|
|
|
|
mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->shared_secret, reply_data, 13);
|
|
|
|
|
if (reply) {
|
|
|
|
|
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
|
|
|
|
|
if (client->out_path_len != OUT_PATH_UNKNOWN) { // we have an out_path, so send DIRECT
|
|
|
|
|
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY);
|
|
|
|
|
} else {
|
|
|
|
|
sendFlood(reply, SERVER_RESPONSE_DELAY);
|
|
|
|
|
sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -448,9 +473,9 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
|
|
|
|
|
|
|
|
|
|
uint32_t delay_millis;
|
|
|
|
|
if (send_ack) {
|
|
|
|
|
if (client->out_path_len < 0) {
|
|
|
|
|
if (client->out_path_len == OUT_PATH_UNKNOWN) {
|
|
|
|
|
mesh::Packet *ack = createAck(ack_hash);
|
|
|
|
|
if (ack) sendFlood(ack, TXT_ACK_DELAY);
|
|
|
|
|
if (ack) sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize());
|
|
|
|
|
delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS;
|
|
|
|
|
} else {
|
|
|
|
|
uint32_t d = TXT_ACK_DELAY;
|
|
|
|
@@ -482,8 +507,8 @@ 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 < 0) {
|
|
|
|
|
sendFlood(reply, delay_millis + SERVER_RESPONSE_DELAY);
|
|
|
|
|
if (client->out_path_len == OUT_PATH_UNKNOWN) {
|
|
|
|
|
sendFloodReply(reply, delay_millis + SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
|
|
|
|
} else {
|
|
|
|
|
sendDirect(reply, client->out_path, client->out_path_len, delay_millis + SERVER_RESPONSE_DELAY);
|
|
|
|
|
}
|
|
|
|
@@ -521,7 +546,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
|
|
|
|
|
// if client sends too quickly, evict()
|
|
|
|
|
|
|
|
|
|
// RULE: only send keep_alive response DIRECT!
|
|
|
|
|
if (client->out_path_len >= 0) {
|
|
|
|
|
if (client->out_path_len != OUT_PATH_UNKNOWN) {
|
|
|
|
|
uint32_t ack_hash; // calc ACK to prove to sender that we got request
|
|
|
|
|
mesh::Utils::sha256((uint8_t *)&ack_hash, 4, data, 9, client->id.pub_key, PUB_KEY_SIZE);
|
|
|
|
|
|
|
|
|
@@ -538,14 +563,14 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
|
|
|
|
|
// 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,
|
|
|
|
|
PAYLOAD_TYPE_RESPONSE, reply_data, reply_len);
|
|
|
|
|
if (path) sendFlood(path, SERVER_RESPONSE_DELAY);
|
|
|
|
|
if (path) sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
|
|
|
|
} else {
|
|
|
|
|
mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len);
|
|
|
|
|
if (reply) {
|
|
|
|
|
if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT
|
|
|
|
|
if (client->out_path_len != OUT_PATH_UNKNOWN) { // we have an out_path, so send DIRECT
|
|
|
|
|
sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY);
|
|
|
|
|
} else {
|
|
|
|
|
sendFlood(reply, SERVER_RESPONSE_DELAY);
|
|
|
|
|
sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -563,7 +588,7 @@ bool MyMesh::onPeerPathRecv(mesh::Packet *packet, int sender_idx, const uint8_t
|
|
|
|
|
if (i >= 0 && i < acl.getNumClients()) { // get from our known_clients table (sender SHOULD already be known in this context)
|
|
|
|
|
MESH_DEBUG_PRINTLN("PATH to client, path_len=%d", (uint32_t)path_len);
|
|
|
|
|
auto client = acl.getClientByIdx(i);
|
|
|
|
|
memcpy(client->out_path, path, client->out_path_len = path_len); // store a copy of path, for sendDirect()
|
|
|
|
|
client->out_path_len = mesh::Packet::copyPath(client->out_path, path, path_len); // store a copy of path, for sendDirect()
|
|
|
|
|
client->last_activity = getRTCClock()->getCurrentTime();
|
|
|
|
|
} else {
|
|
|
|
|
MESH_DEBUG_PRINTLN("onPeerPathRecv: invalid peer idx: %d", i);
|
|
|
|
@@ -587,17 +612,21 @@ void MyMesh::onAckRecv(mesh::Packet *packet, uint32_t ack_crc) {
|
|
|
|
|
MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng,
|
|
|
|
|
mesh::RTCClock &rtc, mesh::MeshTables &tables)
|
|
|
|
|
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables),
|
|
|
|
|
_cli(board, rtc, sensors, acl, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) {
|
|
|
|
|
region_map(key_store), temp_map(key_store),
|
|
|
|
|
_cli(board, rtc, sensors, region_map, acl, &_prefs, this),
|
|
|
|
|
telemetry(MAX_PACKET_PAYLOAD - 4)
|
|
|
|
|
{
|
|
|
|
|
last_millis = 0;
|
|
|
|
|
uptime_millis = 0;
|
|
|
|
|
next_local_advert = next_flood_advert = 0;
|
|
|
|
|
dirty_contacts_expiry = 0;
|
|
|
|
|
_logging = false;
|
|
|
|
|
region_load_active = false;
|
|
|
|
|
set_radio_at = revert_radio_at = 0;
|
|
|
|
|
|
|
|
|
|
// defaults
|
|
|
|
|
memset(&_prefs, 0, sizeof(_prefs));
|
|
|
|
|
_prefs.airtime_factor = 1.0; // one half
|
|
|
|
|
_prefs.airtime_factor = 1.0;
|
|
|
|
|
_prefs.rx_delay_base = 0.0f; // off by default, was 10.0
|
|
|
|
|
_prefs.tx_delay_factor = 0.5f; // was 0.25f;
|
|
|
|
|
_prefs.direct_tx_delay_factor = 0.2f; // was zero
|
|
|
|
@@ -629,6 +658,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
|
|
|
|
next_push = 0;
|
|
|
|
|
memset(posts, 0, sizeof(posts));
|
|
|
|
|
_num_posted = _num_post_pushes = 0;
|
|
|
|
|
|
|
|
|
|
memset(default_scope.key, 0, sizeof(default_scope.key));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MyMesh::begin(FILESYSTEM *fs) {
|
|
|
|
@@ -638,6 +669,27 @@ void MyMesh::begin(FILESYSTEM *fs) {
|
|
|
|
|
_cli.loadPrefs(_fs);
|
|
|
|
|
|
|
|
|
|
acl.load(_fs, self_id);
|
|
|
|
|
region_map.load(_fs);
|
|
|
|
|
|
|
|
|
|
// establish default-scope
|
|
|
|
|
{
|
|
|
|
|
RegionEntry* r = region_map.getDefaultRegion();
|
|
|
|
|
if (r) {
|
|
|
|
|
region_map.getTransportKeysFor(*r, &default_scope, 1);
|
|
|
|
|
} else {
|
|
|
|
|
#ifdef DEFAULT_FLOOD_SCOPE_NAME
|
|
|
|
|
r = region_map.findByName(DEFAULT_FLOOD_SCOPE_NAME);
|
|
|
|
|
if (r == NULL) {
|
|
|
|
|
r = region_map.putRegion(DEFAULT_FLOOD_SCOPE_NAME, 0); // auto-create the default scope region
|
|
|
|
|
if (r) { r->flags = 0; } // Allow-flood
|
|
|
|
|
}
|
|
|
|
|
if (r) {
|
|
|
|
|
region_map.setDefaultRegion(r);
|
|
|
|
|
region_map.getTransportKeysFor(*r, &default_scope, 1);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
|
|
|
|
|
radio_set_tx_power(_prefs.tx_power_dbm);
|
|
|
|
@@ -652,6 +704,30 @@ void MyMesh::begin(FILESYSTEM *fs) {
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint32_t delay_millis, uint8_t path_hash_size) {
|
|
|
|
|
if (scope.isNull()) {
|
|
|
|
|
sendFlood(pkt, delay_millis, path_hash_size);
|
|
|
|
|
} else {
|
|
|
|
|
uint16_t codes[2];
|
|
|
|
|
codes[0] = scope.calcTransportCode(pkt);
|
|
|
|
|
codes[1] = 0; // REVISIT: set to 'home' Region, for sender/return region?
|
|
|
|
|
sendFlood(pkt, codes, delay_millis, path_hash_size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size) {
|
|
|
|
|
if (recv_pkt_region && !recv_pkt_region->isWildcard()) { // if _request_ packet scope is known, send reply with same scope
|
|
|
|
|
TransportKey scope;
|
|
|
|
|
if (region_map.getTransportKeysFor(*recv_pkt_region, &scope, 1) > 0) {
|
|
|
|
|
sendFloodScoped(scope, packet, delay_millis, path_hash_size);
|
|
|
|
|
} else {
|
|
|
|
|
sendFlood(packet, delay_millis, path_hash_size); // send un-scoped
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
sendFlood(packet, delay_millis, path_hash_size); // send un-scoped
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) {
|
|
|
|
|
set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params
|
|
|
|
|
pending_freq = freq;
|
|
|
|
@@ -679,7 +755,7 @@ void MyMesh::sendSelfAdvertisement(int delay_millis, bool flood) {
|
|
|
|
|
mesh::Packet *pkt = createSelfAdvert();
|
|
|
|
|
if (pkt) {
|
|
|
|
|
if (flood) {
|
|
|
|
|
sendFlood(pkt, delay_millis);
|
|
|
|
|
sendFloodScoped(default_scope, pkt, delay_millis, _prefs.path_hash_mode + 1);
|
|
|
|
|
} else {
|
|
|
|
|
sendZeroHop(pkt, delay_millis);
|
|
|
|
|
}
|
|
|
|
@@ -736,6 +812,25 @@ void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) {
|
|
|
|
|
store.save("_main", new_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MyMesh::startRegionsLoad() {
|
|
|
|
|
temp_map.resetFrom(region_map); // rebuild regions in a temp instance
|
|
|
|
|
memset(load_stack, 0, sizeof(load_stack));
|
|
|
|
|
load_stack[0] = &temp_map.getWildcard();
|
|
|
|
|
region_load_active = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MyMesh::saveRegions() {
|
|
|
|
|
return region_map.save(_fs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MyMesh::onDefaultRegionChanged(const RegionEntry* r) {
|
|
|
|
|
if (r) {
|
|
|
|
|
region_map.getTransportKeysFor(*r, &default_scope, 1);
|
|
|
|
|
} else {
|
|
|
|
|
memset(default_scope.key, 0, sizeof(default_scope.key));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MyMesh::clearStats() {
|
|
|
|
|
radio_driver.resetStats();
|
|
|
|
|
resetStats();
|
|
|
|
@@ -756,6 +851,40 @@ void MyMesh::formatPacketStatsReply(char *reply) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) {
|
|
|
|
|
if (region_load_active) {
|
|
|
|
|
if (StrHelper::isBlank(command)) { // empty/blank line, signal to terminate 'load' operation
|
|
|
|
|
region_map = temp_map; // copy over the temp instance as new current map
|
|
|
|
|
region_load_active = false;
|
|
|
|
|
|
|
|
|
|
sprintf(reply, "OK - loaded %d regions", region_map.getCount());
|
|
|
|
|
} else {
|
|
|
|
|
char *np = command;
|
|
|
|
|
while (*np == ' ') np++; // skip indent
|
|
|
|
|
int indent = np - command;
|
|
|
|
|
|
|
|
|
|
char *ep = np;
|
|
|
|
|
while (RegionMap::is_name_char(*ep)) ep++;
|
|
|
|
|
if (*ep) { *ep++ = 0; } // set null terminator for end of name
|
|
|
|
|
|
|
|
|
|
while (*ep && *ep != 'F') ep++; // look for (optional) flags
|
|
|
|
|
|
|
|
|
|
if (indent > 0 && indent < 8 && strlen(np) > 0) {
|
|
|
|
|
auto parent = load_stack[indent - 1];
|
|
|
|
|
if (parent) {
|
|
|
|
|
auto old = region_map.findByName(np);
|
|
|
|
|
auto nw = temp_map.putRegion(np, parent->id, old ? old->id : 0); // carry-over the current ID (if name already exists)
|
|
|
|
|
if (nw) {
|
|
|
|
|
nw->flags = old ? old->flags : (*ep == 'F' ? 0 : REGION_DENY_FLOOD); // carry-over flags from curr
|
|
|
|
|
|
|
|
|
|
load_stack[indent] = nw; // keep pointers to parent regions, to resolve parent_id's
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
reply[0] = 0;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (*command == ' ')
|
|
|
|
|
command++; // skip leading spaces
|
|
|
|
|
|
|
|
|
@@ -856,7 +985,8 @@ void MyMesh::loop() {
|
|
|
|
|
|
|
|
|
|
if (next_flood_advert && millisHasNowPassed(next_flood_advert)) {
|
|
|
|
|
mesh::Packet *pkt = createSelfAdvert();
|
|
|
|
|
if (pkt) sendFlood(pkt);
|
|
|
|
|
uint32_t delay_millis = 0;
|
|
|
|
|
if (pkt) sendFloodScoped(default_scope, pkt, delay_millis, _prefs.path_hash_mode + 1);
|
|
|
|
|
|
|
|
|
|
updateFloodAdvertTimer(); // schedule next flood advert
|
|
|
|
|
updateAdvertTimer(); // also schedule local advert (so they don't overlap)
|
|
|
|
|