mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-09-14 23:26:38 +00:00
+2
-1
@@ -193,8 +193,9 @@ public:
|
||||
bool millisHasNowPassed(unsigned long timestamp) const;
|
||||
unsigned long futureMillis(int millis_from_now) const;
|
||||
|
||||
private:
|
||||
bool tryParsePacket(Packet* pkt, const uint8_t* raw, int len);
|
||||
|
||||
private:
|
||||
void checkRecv();
|
||||
void checkSend();
|
||||
};
|
||||
|
||||
+8
-11
@@ -361,13 +361,10 @@ DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) {
|
||||
|
||||
void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) {
|
||||
if (!packet->isMarkedDoNotRetransmit()) {
|
||||
uint32_t crc;
|
||||
memcpy(&crc, packet->payload, 4);
|
||||
|
||||
uint8_t extra = getExtraAckTransmitCount();
|
||||
while (extra > 0) {
|
||||
delay_millis += getDirectRetransmitDelay(packet) + 300;
|
||||
auto a1 = createMultiAck(crc, extra);
|
||||
auto a1 = createMultiAck(packet->payload, packet->payload_len, extra);
|
||||
if (a1) {
|
||||
a1->path_len = Packet::copyPath(a1->path, packet->path, packet->path_len);
|
||||
a1->header &= ~PH_ROUTE_MASK;
|
||||
@@ -377,7 +374,7 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) {
|
||||
extra--;
|
||||
}
|
||||
|
||||
auto a2 = createAck(crc);
|
||||
auto a2 = createAck(packet->payload, packet->payload_len);
|
||||
if (a2) {
|
||||
a2->path_len = Packet::copyPath(a2->path, packet->path, packet->path_len);
|
||||
a2->header &= ~PH_ROUTE_MASK;
|
||||
@@ -543,7 +540,7 @@ Packet* Mesh::createGroupDatagram(uint8_t type, const GroupChannel& channel, con
|
||||
return packet;
|
||||
}
|
||||
|
||||
Packet* Mesh::createAck(uint32_t ack_crc) {
|
||||
Packet* Mesh::createAck(const uint8_t* ack, uint8_t len) {
|
||||
Packet* packet = obtainNewPacket();
|
||||
if (packet == NULL) {
|
||||
MESH_DEBUG_PRINTLN("%s Mesh::createAck(): error, packet pool empty", getLogDateTime());
|
||||
@@ -551,13 +548,13 @@ Packet* Mesh::createAck(uint32_t ack_crc) {
|
||||
}
|
||||
packet->header = (PAYLOAD_TYPE_ACK << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later
|
||||
|
||||
memcpy(packet->payload, &ack_crc, 4);
|
||||
packet->payload_len = 4;
|
||||
memcpy(packet->payload, ack, len);
|
||||
packet->payload_len = len;
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
Packet* Mesh::createMultiAck(uint32_t ack_crc, uint8_t remaining) {
|
||||
Packet* Mesh::createMultiAck(const uint8_t* ack, uint8_t len, uint8_t remaining) {
|
||||
Packet* packet = obtainNewPacket();
|
||||
if (packet == NULL) {
|
||||
MESH_DEBUG_PRINTLN("%s Mesh::createMultiAck(): error, packet pool empty", getLogDateTime());
|
||||
@@ -566,8 +563,8 @@ Packet* Mesh::createMultiAck(uint32_t ack_crc, uint8_t remaining) {
|
||||
packet->header = (PAYLOAD_TYPE_MULTIPART << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later
|
||||
|
||||
packet->payload[0] = (remaining << 4) | PAYLOAD_TYPE_ACK;
|
||||
memcpy(&packet->payload[1], &ack_crc, 4);
|
||||
packet->payload_len = 5;
|
||||
memcpy(&packet->payload[1], ack, len);
|
||||
packet->payload_len = 1 + len;
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
+4
-2
@@ -185,8 +185,10 @@ public:
|
||||
Packet* createDatagram(uint8_t type, const Identity& dest, const uint8_t* secret, const uint8_t* data, size_t len);
|
||||
Packet* createAnonDatagram(uint8_t type, const LocalIdentity& sender, const Identity& dest, const uint8_t* secret, const uint8_t* data, size_t data_len);
|
||||
Packet* createGroupDatagram(uint8_t type, const GroupChannel& channel, const uint8_t* data, size_t data_len);
|
||||
Packet* createAck(uint32_t ack_crc);
|
||||
Packet* createMultiAck(uint32_t ack_crc, uint8_t remaining);
|
||||
Packet* createAck(const uint8_t* ack, uint8_t len);
|
||||
Packet* createAck(uint32_t ack_crc) { return createAck((uint8_t *) &ack_crc, 4); }
|
||||
Packet* createMultiAck(const uint8_t* ack, uint8_t len, uint8_t remaining);
|
||||
Packet* createMultiAck(uint32_t ack_crc, uint8_t remaining) { return createMultiAck((uint8_t *)&ack_crc, 4, remaining); }
|
||||
Packet* createPathReturn(const uint8_t* dest_hash, const uint8_t* secret, const uint8_t* path, uint8_t path_len, uint8_t extra_type, const uint8_t*extra, size_t extra_len);
|
||||
Packet* createPathReturn(const Identity& dest, const uint8_t* secret, const uint8_t* path, uint8_t path_len, uint8_t extra_type, const uint8_t*extra, size_t extra_len);
|
||||
Packet* createRawData(const uint8_t* data, size_t len);
|
||||
|
||||
@@ -52,6 +52,10 @@ public:
|
||||
virtual void onAfterTransmit() { }
|
||||
virtual void reboot() = 0;
|
||||
virtual void powerOff() { /* no op */ }
|
||||
// Called by example setup() functions to signal that boot is complete.
|
||||
// Boards may override to stop a boot-indicator LED sequence or similar.
|
||||
// Default no-op: boards that don't care need not implement anything.
|
||||
virtual void onBootComplete() { /* no op */ }
|
||||
virtual void sleep(uint32_t secs) { /* no op */ }
|
||||
virtual uint32_t getGpio() { return 0; }
|
||||
virtual void setGpio(uint32_t values) {}
|
||||
|
||||
@@ -38,19 +38,19 @@ mesh::Packet* BaseChatMesh::createSelfAdvert(const char* name, double lat, doubl
|
||||
return createAdvert(self_id, app_data, app_data_len);
|
||||
}
|
||||
|
||||
void BaseChatMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) {
|
||||
void BaseChatMesh::sendAckTo(const ContactInfo& dest, const uint8_t* ack_hash, uint8_t ack_len) {
|
||||
if (dest.out_path_len == OUT_PATH_UNKNOWN) {
|
||||
mesh::Packet* ack = createAck(ack_hash);
|
||||
mesh::Packet* ack = createAck(ack_hash, ack_len);
|
||||
if (ack) sendFloodScoped(dest, ack, TXT_ACK_DELAY);
|
||||
} else {
|
||||
uint32_t d = TXT_ACK_DELAY;
|
||||
if (getExtraAckTransmitCount() > 0) {
|
||||
mesh::Packet* a1 = createMultiAck(ack_hash, 1);
|
||||
mesh::Packet* a1 = createMultiAck(ack_hash, ack_len, 1);
|
||||
if (a1) sendDirect(a1, dest.out_path, dest.out_path_len, d);
|
||||
d += 300;
|
||||
}
|
||||
|
||||
mesh::Packet* a2 = createAck(ack_hash);
|
||||
mesh::Packet* a2 = createAck(ack_hash, ack_len);
|
||||
if (a2) sendDirect(a2, dest.out_path, dest.out_path_len, d);
|
||||
}
|
||||
}
|
||||
@@ -218,16 +218,20 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender
|
||||
from.lastmod = getRTCClock()->getCurrentTime(); // update last heard time
|
||||
onMessageRecv(from, packet, timestamp, (const char *) &data[5]); // let UI know
|
||||
|
||||
uint32_t ack_hash; // calc truncated hash of the message timestamp + text + sender pub_key, to prove to sender that we got it
|
||||
mesh::Utils::sha256((uint8_t *) &ack_hash, 4, data, 5 + strlen((char *)&data[5]), from.id.pub_key, PUB_KEY_SIZE);
|
||||
int text_len = strlen((char *)&data[5]);
|
||||
uint8_t ack_hash[6]; // calc truncated hash of the message timestamp + text + sender pub_key, to prove to sender that we got it
|
||||
mesh::Utils::sha256(ack_hash, 4, data, 5 + text_len, from.id.pub_key, PUB_KEY_SIZE);
|
||||
// NEW: append (potential) extended attempt byte (to make packethash unique)
|
||||
ack_hash[4] = data[5 + text_len + 1];
|
||||
getRNG()->random(&ack_hash[5], 1); // make 6th byte random
|
||||
|
||||
if (packet->isRouteFlood()) {
|
||||
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the ACK
|
||||
mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len,
|
||||
PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4);
|
||||
PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 6);
|
||||
if (path) sendFloodScoped(from, path, TXT_ACK_DELAY);
|
||||
} else {
|
||||
sendAckTo(from, ack_hash);
|
||||
sendAckTo(from, ack_hash, 6);
|
||||
}
|
||||
} else if (flags == TXT_TYPE_CLI_DATA) {
|
||||
onCommandDataRecv(from, packet, timestamp, (const char *) &data[5]); // let UI know
|
||||
@@ -254,7 +258,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender
|
||||
PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4);
|
||||
if (path) sendFloodScoped(from, path, TXT_ACK_DELAY);
|
||||
} else {
|
||||
sendAckTo(from, ack_hash);
|
||||
sendAckTo(from, (uint8_t *) &ack_hash);
|
||||
}
|
||||
} else {
|
||||
MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported message type: %u", (uint32_t) flags);
|
||||
|
||||
@@ -72,7 +72,7 @@ class BaseChatMesh : public mesh::Mesh {
|
||||
ConnectionInfo connections[MAX_CONNECTIONS];
|
||||
|
||||
mesh::Packet* composeMsgPacket(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char *text, uint32_t& expected_ack);
|
||||
void sendAckTo(const ContactInfo& dest, uint32_t ack_hash);
|
||||
void sendAckTo(const ContactInfo& dest, const uint8_t* ack_hash, uint8_t ack_len=4);
|
||||
|
||||
protected:
|
||||
BaseChatMesh(mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::PacketManager& mgr, mesh::MeshTables& tables)
|
||||
|
||||
@@ -547,7 +547,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
_prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0;
|
||||
savePrefs();
|
||||
strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON");
|
||||
#if defined(USE_SX1262) || defined(USE_SX1268)
|
||||
#if defined(USE_SX1262) || defined(USE_SX1268) || defined(USE_LR1110)
|
||||
} else if (memcmp(config, "radio.rxgain ", 13) == 0) {
|
||||
_prefs->rx_boosted_gain = memcmp(&config[13], "on", 2) == 0;
|
||||
strcpy(reply, "OK");
|
||||
@@ -582,21 +582,21 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
strcpy(reply, "OK");
|
||||
} else if (memcmp(config, "rxdelay ", 8) == 0) {
|
||||
float db = atof(&config[8]);
|
||||
if (db >= 0) {
|
||||
if (db >= 0 && db <= 20.0f) {
|
||||
_prefs->rx_delay_base = db;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "Error, cannot be negative");
|
||||
strcpy(reply, "Error, must be 0-20");
|
||||
}
|
||||
} else if (memcmp(config, "txdelay ", 8) == 0) {
|
||||
float f = atof(&config[8]);
|
||||
if (f >= 0) {
|
||||
if (f >= 0 && f <= 2.0f) {
|
||||
_prefs->tx_delay_factor = f;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "Error, cannot be negative");
|
||||
strcpy(reply, "Error, must be 0-2");
|
||||
}
|
||||
} else if (memcmp(config, "flood.max ", 10) == 0) {
|
||||
uint8_t m = atoi(&config[10]);
|
||||
@@ -609,12 +609,12 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
}
|
||||
} else if (memcmp(config, "direct.txdelay ", 15) == 0) {
|
||||
float f = atof(&config[15]);
|
||||
if (f >= 0) {
|
||||
if (f >= 0 && f <= 2.0f) {
|
||||
_prefs->direct_tx_delay_factor = f;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "Error, cannot be negative");
|
||||
strcpy(reply, "Error, must be 0-2");
|
||||
}
|
||||
} else if (memcmp(config, "owner.info ", 11) == 0) {
|
||||
config += 11;
|
||||
@@ -769,7 +769,7 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lat));
|
||||
} else if (memcmp(config, "lon", 3) == 0) {
|
||||
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lon));
|
||||
#if defined(USE_SX1262) || defined(USE_SX1268)
|
||||
#if defined(USE_SX1262) || defined(USE_SX1268) || defined(USE_LR1110)
|
||||
} else if (memcmp(config, "radio.rxgain", 12) == 0) {
|
||||
sprintf(reply, "> %s", _prefs->rx_boosted_gain ? "on" : "off");
|
||||
#endif
|
||||
@@ -895,9 +895,76 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
}
|
||||
}
|
||||
|
||||
static char* skipSpaces(char* s) {
|
||||
while (*s == ' ') s++;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void rtrimSpaces(char* s) {
|
||||
char* e = s + strlen(s);
|
||||
while (e > s && e[-1] == ' ') *--e = '\0';
|
||||
}
|
||||
|
||||
static char* takeToken(char** cursor) {
|
||||
char* p = skipSpaces(*cursor);
|
||||
if (*p == '\0') { *cursor = p; return nullptr; }
|
||||
char* tok = p;
|
||||
while (*p && *p != ' ') p++;
|
||||
if (*p) *p++ = '\0';
|
||||
*cursor = p;
|
||||
return tok;
|
||||
}
|
||||
|
||||
static char* splitNameJump(char* tok) {
|
||||
for (char* q = tok; *q; q++) {
|
||||
if (*q == '|' || *q == ',') {
|
||||
*q = '\0';
|
||||
char* jump = skipSpaces(q + 1);
|
||||
rtrimSpaces(jump);
|
||||
return jump;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool processRegionDefSegment(RegionMap* map, char* tok, RegionEntry** cursor, char* reply) {
|
||||
char* jump = splitNameJump(tok);
|
||||
char* name = skipSpaces(tok);
|
||||
if (*name == '\0') { snprintf(reply, 160, "Err - empty name"); return false; }
|
||||
if (jump && *jump == '\0') { snprintf(reply, 160, "Err - empty jump"); return false; }
|
||||
|
||||
RegionEntry* r = map->putRegion(name, (*cursor)->id);
|
||||
if (r == NULL) { snprintf(reply, 160, "Err - put failed: %s", name); return false; }
|
||||
r->flags = 0;
|
||||
|
||||
if (jump) {
|
||||
RegionEntry* j = map->findByNamePrefix(jump);
|
||||
if (j == NULL) { snprintf(reply, 160, "Err - unknown jump: %s", jump); return false; }
|
||||
*cursor = j;
|
||||
} else {
|
||||
*cursor = r;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CommonCLI::handleRegionCmd(char* command, char* reply) {
|
||||
reply[0] = 0;
|
||||
|
||||
// `region def`: must run before parseTextParts mutates the buffer
|
||||
char* cmd = skipSpaces(command);
|
||||
if (strncmp(cmd, "region def", 10) == 0 && (cmd[10] == ' ' || cmd[10] == '\0')) {
|
||||
char* payload = skipSpaces(cmd + 10);
|
||||
rtrimSpaces(payload);
|
||||
if (*payload == '\0') { snprintf(reply, 160, "Err - empty def"); return; }
|
||||
|
||||
RegionEntry* cursor = &_region_map->getWildcard();
|
||||
for (char* tok; (tok = takeToken(&payload)) != nullptr; ) {
|
||||
if (!processRegionDefSegment(_region_map, tok, &cursor, reply)) return;
|
||||
}
|
||||
_region_map->exportTo(reply, 160);
|
||||
return;
|
||||
}
|
||||
|
||||
const char* parts[4];
|
||||
int n = mesh::Utils::parseTextParts(command, parts, 4, ' ');
|
||||
if (n == 1) {
|
||||
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
/*
|
||||
* The NRF52 has an internal DC/DC regulator that allows increased efficiency
|
||||
* compared to the LDO regulator. For being able to use it, the module/board
|
||||
* needs to have the required inductors and and capacitors populated. If the
|
||||
* needs to have the required inductors and capacitors populated. If the
|
||||
* hardware requirements are met, this subclass can be used to enable the DC/DC
|
||||
* regulator.
|
||||
*/
|
||||
|
||||
@@ -6,22 +6,17 @@
|
||||
#include <FS.h>
|
||||
#endif
|
||||
|
||||
#define MAX_PACKET_HASHES 128
|
||||
#define MAX_PACKET_ACKS 64
|
||||
#define MAX_PACKET_HASHES (128+32)
|
||||
|
||||
class SimpleMeshTables : public mesh::MeshTables {
|
||||
uint8_t _hashes[MAX_PACKET_HASHES*MAX_HASH_SIZE];
|
||||
int _next_idx;
|
||||
uint32_t _acks[MAX_PACKET_ACKS];
|
||||
int _next_ack_idx;
|
||||
uint32_t _direct_dups, _flood_dups;
|
||||
|
||||
public:
|
||||
SimpleMeshTables() {
|
||||
memset(_hashes, 0, sizeof(_hashes));
|
||||
_next_idx = 0;
|
||||
memset(_acks, 0, sizeof(_acks));
|
||||
_next_ack_idx = 0;
|
||||
_direct_dups = _flood_dups = 0;
|
||||
}
|
||||
|
||||
@@ -29,37 +24,14 @@ public:
|
||||
void restoreFrom(File f) {
|
||||
f.read(_hashes, sizeof(_hashes));
|
||||
f.read((uint8_t *) &_next_idx, sizeof(_next_idx));
|
||||
f.read((uint8_t *) &_acks[0], sizeof(_acks));
|
||||
f.read((uint8_t *) &_next_ack_idx, sizeof(_next_ack_idx));
|
||||
}
|
||||
void saveTo(File f) {
|
||||
f.write(_hashes, sizeof(_hashes));
|
||||
f.write((const uint8_t *) &_next_idx, sizeof(_next_idx));
|
||||
f.write((const uint8_t *) &_acks[0], sizeof(_acks));
|
||||
f.write((const uint8_t *) &_next_ack_idx, sizeof(_next_ack_idx));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool hasSeen(const mesh::Packet* packet) override {
|
||||
if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) {
|
||||
uint32_t ack;
|
||||
memcpy(&ack, packet->payload, 4);
|
||||
for (int i = 0; i < MAX_PACKET_ACKS; i++) {
|
||||
if (ack == _acks[i]) {
|
||||
if (packet->isRouteDirect()) {
|
||||
_direct_dups++; // keep some stats
|
||||
} else {
|
||||
_flood_dups++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_acks[_next_ack_idx] = ack;
|
||||
_next_ack_idx = (_next_ack_idx + 1) % MAX_PACKET_ACKS; // cyclic table
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t hash[MAX_HASH_SIZE];
|
||||
packet->calculatePacketHash(hash);
|
||||
|
||||
@@ -81,25 +53,14 @@ public:
|
||||
}
|
||||
|
||||
void clear(const mesh::Packet* packet) override {
|
||||
if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) {
|
||||
uint32_t ack;
|
||||
memcpy(&ack, packet->payload, 4);
|
||||
for (int i = 0; i < MAX_PACKET_ACKS; i++) {
|
||||
if (ack == _acks[i]) {
|
||||
_acks[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint8_t hash[MAX_HASH_SIZE];
|
||||
packet->calculatePacketHash(hash);
|
||||
uint8_t hash[MAX_HASH_SIZE];
|
||||
packet->calculatePacketHash(hash);
|
||||
|
||||
uint8_t* sp = _hashes;
|
||||
for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) {
|
||||
if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) {
|
||||
memset(sp, 0, MAX_HASH_SIZE);
|
||||
break;
|
||||
}
|
||||
uint8_t* sp = _hashes;
|
||||
for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) {
|
||||
if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) {
|
||||
memset(sp, 0, MAX_HASH_SIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ static Adafruit_BMP085 BMP085;
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_AHTX0
|
||||
#ifndef TELEM_AHTX_ADDRESS
|
||||
#define TELEM_AHTX_ADDRESS 0x38 // AHT10, AHT20 temperature and humidity sensor I2C address
|
||||
#endif
|
||||
#include <Adafruit_AHTX0.h>
|
||||
static Adafruit_AHTX0 AHTX0;
|
||||
#endif
|
||||
@@ -57,7 +59,9 @@ static Adafruit_SHTC3 SHTC3;
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_SHT4X
|
||||
#ifndef TELEM_SHT4X_ADDRESS
|
||||
#define TELEM_SHT4X_ADDRESS 0x44
|
||||
#endif
|
||||
#include <SensirionI2cSht4x.h>
|
||||
static SensirionI2cSht4x SHT4X;
|
||||
#endif
|
||||
@@ -82,19 +86,25 @@ static Adafruit_INA3221 INA3221;
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA219
|
||||
#ifndef TELEM_INA219_ADDRESS
|
||||
#define TELEM_INA219_ADDRESS 0x40 // INA219 single channel current sensor I2C address
|
||||
#endif
|
||||
#include <Adafruit_INA219.h>
|
||||
static Adafruit_INA219 INA219(TELEM_INA219_ADDRESS);
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA260
|
||||
#ifndef TELEM_INA260_ADDRESS
|
||||
#define TELEM_INA260_ADDRESS 0x41 // INA260 single channel current sensor I2C address
|
||||
#endif
|
||||
#include <Adafruit_INA260.h>
|
||||
static Adafruit_INA260 INA260;
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA226
|
||||
#ifndef TELEM_INA226_ADDRESS
|
||||
#define TELEM_INA226_ADDRESS 0x44
|
||||
#endif
|
||||
#define TELEM_INA226_SHUNT_VALUE 0.100
|
||||
#define TELEM_INA226_MAX_AMP 0.8
|
||||
#include <INA226.h>
|
||||
@@ -102,19 +112,25 @@ static INA226 INA226(TELEM_INA226_ADDRESS, TELEM_WIRE);
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_MLX90614
|
||||
#ifndef TELEM_MLX90614_ADDRESS
|
||||
#define TELEM_MLX90614_ADDRESS 0x5A // MLX90614 IR temperature sensor I2C address
|
||||
#endif
|
||||
#include <Adafruit_MLX90614.h>
|
||||
static Adafruit_MLX90614 MLX90614;
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_VL53L0X
|
||||
#ifndef TELEM_VL53L0X_ADDRESS
|
||||
#define TELEM_VL53L0X_ADDRESS 0x29 // VL53L0X time-of-flight distance sensor I2C address
|
||||
#endif
|
||||
#include <Adafruit_VL53L0X.h>
|
||||
static Adafruit_VL53L0X VL53L0X;
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_RAK12035
|
||||
#ifndef TELEM_RAK12035_ADDRESS
|
||||
#define TELEM_RAK12035_ADDRESS 0x20 // RAK12035 Soil Moisture sensor I2C address
|
||||
#endif
|
||||
#include "RAK12035_SoilMoisture.h"
|
||||
static RAK12035_SoilMoisture RAK12035;
|
||||
#endif
|
||||
@@ -127,7 +143,9 @@ static RAK12035_SoilMoisture RAK12035;
|
||||
static uint32_t gpsResetPin = 0;
|
||||
static bool i2cGPSFlag = false;
|
||||
static bool serialGPSFlag = false;
|
||||
#ifndef TELEM_RAK12500_ADDRESS
|
||||
#define TELEM_RAK12500_ADDRESS 0x42 //RAK12500 Ublox GPS via i2c
|
||||
#endif
|
||||
#include <SparkFun_u-blox_GNSS_Arduino_Library.h>
|
||||
static SFE_UBLOX_GNSS ublox_GNSS;
|
||||
|
||||
|
||||
@@ -67,10 +67,10 @@ void RAK12035_SoilMoisture::setup(TwoWire &i2c)
|
||||
// setup() and that the bus has been initialized externally (Wire.begin()).
|
||||
// It uses the passed in I2C Address (default 0x20)
|
||||
//
|
||||
// *** This code does not supprt three sensors ***
|
||||
// *** This code does not support three sensors ***
|
||||
// The RAK12023 has three connectors, but each of the sensors attached must
|
||||
// all have a different I2C addresses.
|
||||
// This code has a function to set the I2C adress of a sensor
|
||||
// This code has a function to set the I2C address of a sensor
|
||||
// and currently only supports one address 0x20 (the default).
|
||||
// To support three sensors, EnvironmentSensorManager would need to be modified
|
||||
// to support multiple instances of the RAK12035_SoilMoisture class,
|
||||
@@ -78,7 +78,7 @@ void RAK12035_SoilMoisture::setup(TwoWire &i2c)
|
||||
// The begin() function would need to be modified to loop through the three addresses
|
||||
//
|
||||
// DEBUG STATEMENTS: Can be enabled by uncommenting or adding:
|
||||
// File: varients/rak4631 platformio.ini
|
||||
// File: variants/rak4631 platformio.ini
|
||||
// Section example: [env:RAK_4631_companion_radio_ble]
|
||||
// Enable Debug statements: -D MESH_DEBUG=1
|
||||
//
|
||||
@@ -107,7 +107,7 @@ bool RAK12035_SoilMoisture::begin(uint8_t addr)
|
||||
* Change the value to 1 in the RAK12035_SoilMoisture.h file
|
||||
*
|
||||
* Calibration Procedure:
|
||||
* 1) Flash the the Calibration version of the firmware.
|
||||
* 1) Flash the Calibration version of the firmware.
|
||||
* 2) Leave the sensor dry, power up the device.
|
||||
* 3) After detecting the RAK12035 this firmware will display calibration data on Channel 3
|
||||
*
|
||||
|
||||
@@ -846,7 +846,7 @@ void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) {
|
||||
if (this->logBuffer[i] == 10) {
|
||||
length++;
|
||||
// Draw string on line `line` from lastPos to length
|
||||
// Passing 0 as the lenght because we are in TEXT_ALIGN_LEFT
|
||||
// Passing 0 as the length because we are in TEXT_ALIGN_LEFT
|
||||
drawStringInternal(xMove, yMove + (line++) * lineHeight, &this->logBuffer[lastPos], length, 0, false);
|
||||
// Remember last pos
|
||||
lastPos = i;
|
||||
|
||||
@@ -60,7 +60,7 @@ private:
|
||||
};
|
||||
|
||||
#else
|
||||
#error "Unkown operating system"
|
||||
#error "Unknown operating system"
|
||||
#endif
|
||||
|
||||
#include "OLEDDisplayFonts.h"
|
||||
@@ -160,7 +160,7 @@ class OLEDDisplay : public Print {
|
||||
#elif __MBED__
|
||||
class OLEDDisplay : public Stream {
|
||||
#else
|
||||
#error "Unkown operating system"
|
||||
#error "Unknown operating system"
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include "DisplayDriver.h"
|
||||
#include <U8g2lib.h>
|
||||
#include <Wire.h>
|
||||
|
||||
#ifndef DISPLAY_ADDRESS
|
||||
#define DISPLAY_ADDRESS 0x3C
|
||||
#endif
|
||||
|
||||
#ifndef OLED_WIDTH
|
||||
#define OLED_WIDTH 72
|
||||
#endif
|
||||
|
||||
#ifndef OLED_HEIGHT
|
||||
#define OLED_HEIGHT 40
|
||||
#endif
|
||||
|
||||
class U8g2Display : public DisplayDriver {
|
||||
// U8g2 constructor for SSD1306/SSD1315 72×40 panel — handles all
|
||||
// GDDRAM column/page offsets, SETMULTIPLEX, SETDISPLAYOFFSET internally
|
||||
U8G2_SSD1306_72X40_ER_F_HW_I2C _u8g2;
|
||||
bool _isOn;
|
||||
uint8_t _drawColor;
|
||||
|
||||
// Font metrics for current font (cached on setTextSize)
|
||||
uint8_t _fontAscent;
|
||||
uint8_t _fontHeight;
|
||||
|
||||
void applyFont(int sz) {
|
||||
if (sz >= 2) {
|
||||
_u8g2.setFont(u8g2_font_6x10_mr); // slightly larger font for better readability. TODO: more font sizes?
|
||||
} else {
|
||||
_u8g2.setFont(u8g2_font_5x7_mr);
|
||||
}
|
||||
_fontAscent = _u8g2.getAscent();
|
||||
_fontHeight = _u8g2.getAscent() - _u8g2.getDescent();
|
||||
}
|
||||
|
||||
public:
|
||||
U8g2Display() : DisplayDriver(OLED_WIDTH, OLED_HEIGHT),
|
||||
_u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE),
|
||||
_isOn(false), _drawColor(1), _fontAscent(5), _fontHeight(6) {}
|
||||
|
||||
bool begin() {
|
||||
// Wire must already be initialised by board.begin() before this is called
|
||||
bool ok = _u8g2.begin();
|
||||
if (ok) {
|
||||
_u8g2.setI2CAddress(DISPLAY_ADDRESS * 2); // U8g2 uses 8-bit address
|
||||
_u8g2.setFontPosTop(); // y coordinate = top of text, not baseline
|
||||
_u8g2.setFontMode(1); // transparent background
|
||||
applyFont(1); // default to compact font
|
||||
_isOn = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool isOn() override { return _isOn; }
|
||||
|
||||
void turnOn() override {
|
||||
_u8g2.setPowerSave(0);
|
||||
_isOn = true;
|
||||
}
|
||||
|
||||
void turnOff() override {
|
||||
_u8g2.setPowerSave(1);
|
||||
_isOn = false;
|
||||
}
|
||||
|
||||
void clear() override {
|
||||
_u8g2.clearBuffer();
|
||||
_u8g2.sendBuffer();
|
||||
}
|
||||
|
||||
void startFrame(Color bkg = DARK) override {
|
||||
_u8g2.clearBuffer();
|
||||
_drawColor = 1;
|
||||
_u8g2.setDrawColor(1);
|
||||
applyFont(1);
|
||||
}
|
||||
|
||||
void setTextSize(int sz) override {
|
||||
applyFont(sz);
|
||||
}
|
||||
|
||||
void setColor(Color c) override {
|
||||
_drawColor = (c != DARK) ? 1 : 0;
|
||||
_u8g2.setDrawColor(_drawColor);
|
||||
}
|
||||
|
||||
void setCursor(int x, int y) override {
|
||||
_cursorX = x;
|
||||
_cursorY = y;
|
||||
}
|
||||
|
||||
void print(const char* str) override {
|
||||
_u8g2.setDrawColor(_drawColor);
|
||||
_u8g2.drawStr(_cursorX, _cursorY, str);
|
||||
}
|
||||
|
||||
void fillRect(int x, int y, int w, int h) override {
|
||||
_u8g2.setDrawColor(_drawColor);
|
||||
_u8g2.drawBox(x, y, w, h);
|
||||
}
|
||||
|
||||
void drawRect(int x, int y, int w, int h) override {
|
||||
_u8g2.setDrawColor(_drawColor);
|
||||
_u8g2.drawFrame(x, y, w, h);
|
||||
}
|
||||
|
||||
void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override {
|
||||
_u8g2.setDrawColor(1);
|
||||
_u8g2.drawXBM(x, y, w, h, bits);
|
||||
}
|
||||
|
||||
uint16_t getTextWidth(const char* str) override {
|
||||
return _u8g2.getStrWidth(str);
|
||||
}
|
||||
|
||||
void endFrame() override {
|
||||
_u8g2.sendBuffer();
|
||||
}
|
||||
|
||||
private:
|
||||
int _cursorX = 0;
|
||||
int _cursorY = 0;
|
||||
};
|
||||
Reference in New Issue
Block a user