diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 5ac99db090..4b32990682 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -92,9 +92,7 @@ uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secr if (data[0] == 0) { // blank password, just check if sender is in ACL client = acl.getClient(sender.pub_key, PUB_KEY_SIZE); if (client == NULL) { - #if MESH_DEBUG MESH_DEBUG_PRINTLN("Login, sender not in ACL"); - #endif } } if (client == NULL) { @@ -104,9 +102,7 @@ uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secr } else if (strcmp((char *)data, _prefs.guest_password) == 0) { // check guest password perms = PERM_ACL_GUEST; } else { -#if MESH_DEBUG MESH_DEBUG_PRINTLN("Invalid password: %s", data); -#endif return 0; } diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 5451505a29..69fa780121 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -294,9 +294,7 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m if (data[8] == 0) { // blank password, just check if sender is in ACL client = acl.getClient(sender.pub_key, PUB_KEY_SIZE); if (client == NULL) { - #if MESH_DEBUG MESH_DEBUG_PRINTLN("Login, sender not in ACL"); - #endif } } if (client == NULL) { diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 68fea474ee..5bd5de37c8 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -332,16 +332,12 @@ uint8_t SensorMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* if (data[0] == 0) { // blank password, just check if sender is in ACL client = acl.getClient(sender.pub_key, PUB_KEY_SIZE); if (client == NULL) { - #if MESH_DEBUG MESH_DEBUG_PRINTLN("Login, sender not in ACL"); - #endif return 0; } } else { if (strcmp((char *) data, _prefs.password) != 0) { // check for valid admin password - #if MESH_DEBUG MESH_DEBUG_PRINTLN("Invalid password: %s", &data[4]); - #endif return 0; } @@ -615,10 +611,8 @@ void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_i bool SensorMesh::handleIncomingMsg(ClientInfo& from, uint32_t timestamp, uint8_t* data, uint8_t flags, size_t len) { MESH_DEBUG_PRINT("handleIncomingMsg: unhandled msg from "); - #ifdef MESH_DEBUG - mesh::Utils::printHex(Serial, from.id.pub_key, PUB_KEY_SIZE); - Serial.printf(": %s\n", data); - #endif + MESH_DEBUG_PRINT_HEX(from.id.pub_key, PUB_KEY_SIZE); + MESH_DEBUG_PRINT_RAW(": %s\n", data); return false; } diff --git a/src/EndianTypes.h b/src/EndianTypes.h new file mode 100644 index 0000000000..708f20c9c0 --- /dev/null +++ b/src/EndianTypes.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +/** + * @brief Endianness-safe integer types for network/protocol communication + * + * These types automatically handle byte order conversion between wire format + * and host format, preventing endianness bugs in protocol implementations. + */ + +// Big-endian 32-bit unsigned integer +struct be_uint32_t { + uint32_t wireBytes; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + operator uint32_t() const { return __builtin_bswap32(wireBytes); } +#else + operator uint32_t() const { return wireBytes; } +#endif +} __attribute__((packed)); + +// Big-endian 16-bit unsigned integer +struct be_uint16_t { + uint16_t wireBytes; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + operator uint16_t() const { return __builtin_bswap16(wireBytes); } +#else + operator uint16_t() const { return wireBytes; } +#endif +} __attribute__((packed)); + +// Little-endian 16-bit unsigned integer +struct le_uint16_t { + uint16_t wireBytes; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + operator uint16_t() const { return wireBytes; } +#else + operator uint16_t() const { return __builtin_bswap16(wireBytes); } +#endif +} __attribute__((packed)); + +// Little-endian 32-bit unsigned integer +struct le_uint32_t { + uint32_t wireBytes; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + operator uint32_t() const { return wireBytes; } +#else + operator uint32_t() const { return __builtin_bswap32(wireBytes); } +#endif +} __attribute__((packed)); \ No newline at end of file diff --git a/src/MeshCore.h b/src/MeshCore.h index 70cd0f0672..5a2bbbae9d 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -24,9 +24,13 @@ #include #define MESH_DEBUG_PRINT(F, ...) Serial.printf("DEBUG: " F, ##__VA_ARGS__) #define MESH_DEBUG_PRINTLN(F, ...) Serial.printf("DEBUG: " F "\n", ##__VA_ARGS__) + #define MESH_DEBUG_PRINT_RAW(F, ...) Serial.printf(F, ##__VA_ARGS__) + #define MESH_DEBUG_PRINT_HEX(src,len) mesh::Utils::printHex(Serial, src, len) #else #define MESH_DEBUG_PRINT(...) {} #define MESH_DEBUG_PRINTLN(...) {} + #define MESH_DEBUG_PRINT_RAW(F, ...) {} + #define MESH_DEBUG_PRINT_HEX(src,len) {} #endif #if BRIDGE_DEBUG && ARDUINO diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 89a174c228..b10247e0a4 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -16,6 +16,7 @@ class SensorManager { SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0; } virtual bool begin() { return false; } + virtual bool hasPendingWork() { return false; } virtual bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { return false; } virtual void loop() { } virtual int getNumSettings() const { return 0; } diff --git a/src/helpers/sensors/DFROBOT_SEN0658.cpp b/src/helpers/sensors/DFROBOT_SEN0658.cpp new file mode 100644 index 0000000000..20d9746fc6 --- /dev/null +++ b/src/helpers/sensors/DFROBOT_SEN0658.cpp @@ -0,0 +1,391 @@ +#include +#include "target.h" +#include "DFROBOT_SEN0658.h" +#include "EndianTypes.h" + +#if ENV_INCLUDE_SEN0658 + +struct PacketHeader { + uint8_t address; + uint8_t function; + uint8_t validBytes; +} __attribute__((packed)); + +void DFROBOT_SEN0658::flushSerial() { + while (WITH_SEN0658_SERIAL.available() > 0) { + WITH_SEN0658_SERIAL.read(); + } +} + +bool DFROBOT_SEN0658::readBytes(uint8_t *buffer, int len) { + const int16_t timeout = 500; + uint8_t *start_buf = buffer; + int remaining = len; + long start = millis(); + while (remaining > 0) { + if (WITH_SEN0658_SERIAL.available()) { + *buffer = WITH_SEN0658_SERIAL.read(); + buffer++; + remaining--; + } + if (millis() - start > timeout) { + MESH_DEBUG_PRINTLN("Timed out reading bytes SEN0658. Read %i of %i bytes.", (int)(len - remaining), (int)len); + return false; + } + } + MESH_DEBUG_PRINT("SEN0658 RX [%i]:", len); + MESH_DEBUG_PRINT_HEX(start_buf, len); + MESH_DEBUG_PRINT_RAW("\n"); + return true; +} + +bool DFROBOT_SEN0658::begin() { + WITH_SEN0658_SERIAL.setPins(WITH_SEN0658_RX, WITH_SEN0658_TX); + + powerOn(); + + bool result; + int i; + for(i = 0; i < 15; i++) { + + WITH_SEN0658_SERIAL.begin(4800); + sendWriteCommand(0x07D1, 0x0006); // set baud rate to 115200 + WITH_SEN0658_SERIAL.end(); + + WITH_SEN0658_SERIAL.begin(115200); + delay(100); + flushSerial(); + + uint16_t offset; + if (result = readWindDirectionOffset(offset)) { + break; + } + MESH_DEBUG_PRINTLN("SEN0658 not detected."); + delay(100); + } + + if (result) { + MESH_DEBUG_PRINTLN("SEN0658 started after %i attempts.", i+1); + } else { + MESH_DEBUG_PRINTLN("SEN0658 failed to start after %i attempts.", i+1); + } + + return result; +} + +bool DFROBOT_SEN0658::hasPendingWork() { +#if defined(WITH_SEN0658_EN) + // if power is on, don't go into powersaving mode until we turn it off. + return _powerOnTime != 0; +#else + return false; +#endif +} + +template +bool DFROBOT_SEN0658::readRegisters(uint16_t registerStart, PacketType& packet) { + const uint16_t dataBytes = sizeof(PacketType) - sizeof(PacketHeader) - sizeof(le_uint16_t); + const uint16_t registerLength = dataBytes / 2; + flushSerial(); + sendCommand(0x03, registerStart, registerLength); + if (!readBytes((uint8_t *)&packet, sizeof(PacketType))) { + MESH_DEBUG_PRINTLN("Could not read packet from SEN0658."); + return false; + } + if (packet.header.address != 1 || packet.header.function != 3 || packet.header.validBytes != dataBytes) { + MESH_DEBUG_PRINTLN( + "Packet header mismatch reading packet from SEN0658. Address: %02X Function: %02X ValidBytes: %02X", + (int)packet.header.address, (int)packet.header.function, (int)packet.header.validBytes); + return false; + } + uint16_t crc = DFROBOT_SEN0658::CRC16_2((uint8_t *)&packet, sizeof(PacketType) - sizeof(le_uint16_t)); + if (crc != (uint16_t)packet.crc) { + MESH_DEBUG_PRINTLN("CRC mismatch %04X != %04X reading packet from SEN0658.", (int)crc, (int)packet.crc); + return false; + } + return true; +} + +bool DFROBOT_SEN0658::readWind(DFROBOT_SEN0658_Sample &sample) { + struct WindPacket { + PacketHeader header; + be_uint16_t windSpeed; + be_uint16_t Reserved; + be_uint16_t ignoredWindDirection; + be_uint16_t windAngle; + le_uint16_t crc; + } __attribute__((packed)) packet; + + if (!readRegisters(0x01F4, packet)) { + MESH_DEBUG_PRINTLN("Could not read wind data from SEN0658."); + return false; + } + // Wind is sometimes really all zeros + // if (packet.windSpeed == 0 && packet.windAngle == 0) { + // MESH_DEBUG_PRINTLN("WindPacket is all zeros."); + // return false; + // } + sample.windSpeed = packet.windSpeed / 100.0; + sample.windAngle = packet.windAngle; + return true; +} + +bool DFROBOT_SEN0658::readLight(DFROBOT_SEN0658_Sample &sample) { + struct LightPacket { + PacketHeader header; + be_uint32_t light; + le_uint16_t crc; + } __attribute__((packed)) packet; + + if (!readRegisters(0x01FE, packet)) { + MESH_DEBUG_PRINTLN("Could not read light data from SEN0658."); + return false; + } + + // lux is 0 at night + // if (packet.light == 0) { + // MESH_DEBUG_PRINTLN("LightPacket is all zeros."); + // return false; + // } + + sample.luminosity = packet.light; + return true; +} + +bool DFROBOT_SEN0658::readTemperature(DFROBOT_SEN0658_Sample &sample) { + struct TemperaturePacket { + PacketHeader header; + be_uint16_t humidity; + be_uint16_t temperature; + be_uint16_t noise; + le_uint16_t crc; + } __attribute__((packed)) packet; + + if (!readRegisters(0x01F8, packet)) { + MESH_DEBUG_PRINTLN("Could not read temperature data from SEN0658."); + return false; + } + if (packet.humidity == 0 || packet.temperature == 0 || packet.noise == 0) { + MESH_DEBUG_PRINTLN("TemperaturePacket is all zeros."); + return false; + } + sample.humidity = packet.humidity / 10.0; + sample.temperature = packet.temperature / 10.0; + sample.noiseDb = packet.noise / 10.0; + return true; +} + +bool DFROBOT_SEN0658::readAir(DFROBOT_SEN0658_Sample &sample) { + struct AirPacket { + PacketHeader header; + be_uint16_t pm2_5; + be_uint16_t pm10; + be_uint16_t airPressure; + le_uint16_t crc; + } __attribute__((packed)) packet; + + if (!readRegisters(0x01FB, packet)) { + MESH_DEBUG_PRINTLN("Could not read air data from SEN0658."); + return false; + } + if (packet.pm2_5 == 0 || packet.pm10 == 0 || packet.airPressure == 0) { + MESH_DEBUG_PRINTLN("AirPacket is all zeros."); + return false; + } + sample.pm2_5 = packet.pm2_5; + sample.pm10 = packet.pm10; + sample.airPressure = packet.airPressure; // register is 0.1 kPa = 1 hPa + return true; +} + +bool DFROBOT_SEN0658::readWindDirectionOffset(uint16_t &offset) { + struct OffsetPacket { + PacketHeader header; + be_uint16_t windOffset; + le_uint16_t crc; + } __attribute__((packed)) packet; + + if (!readRegisters(0x6000, packet)) { + MESH_DEBUG_PRINTLN("Could not read wind direction offset from SEN0658."); + return false; + } + offset = (uint16_t)packet.windOffset; + return true; +} + +bool DFROBOT_SEN0658::writeRegister(uint16_t registerIndex, uint16_t value) { + powerOn(); + sendWriteCommand(registerIndex, value); + + struct SingleRegisterPacket { + uint8_t address; + uint8_t function; + be_uint16_t regiterIndex; + be_uint16_t registerValue; + le_uint16_t crc; + } __attribute__((packed)) packet; + + if (!readBytes((uint8_t *)&packet, sizeof(SingleRegisterPacket))) { + MESH_DEBUG_PRINTLN("Could not read packet from SEN0658."); + return false; + } + if (packet.address != 1 || packet.function != 6 || packet.regiterIndex != registerIndex) { + MESH_DEBUG_PRINTLN( + "Packet header mismatch reading packet from SEN0658. Address: %02X Function: %02X RegisterIndex: %02X", + (int)packet.address, (int)packet.function, (int)packet.regiterIndex); + return false; + } + uint16_t crc = DFROBOT_SEN0658::CRC16_2((uint8_t *)&packet, sizeof(SingleRegisterPacket) - sizeof(le_uint16_t)); + if (crc != (uint16_t)packet.crc) { + MESH_DEBUG_PRINTLN("CRC mismatch %04X != %04X reading packet from SEN0658.", + (int)crc, (int)packet.crc); + return false; + } + + if (packet.registerValue != value) { + MESH_DEBUG_PRINTLN("Verification mismatch after writing to SEN0658. Wrote %i but read back %i.", + (int)value, (int)packet.registerValue); + return false; + } + + return true; +} + +bool DFROBOT_SEN0658::writeWindDirectionOffset(uint16_t offset) { + if (offset > 1) { + MESH_DEBUG_PRINTLN("Invalid wind direction offset value %i. Must be 0 or 1.", (int)offset); + return false; + } + return writeRegister(0x6000, offset); +} + +bool DFROBOT_SEN0658::zeroWindSpeed() { + return writeRegister(0x6001, 0x00AA); +} + +bool DFROBOT_SEN0658::zeroRainfall() { + return writeRegister(0x6002, 0x005A); +} + +void DFROBOT_SEN0658::sendCommand(uint8_t function, uint16_t registerStart, uint16_t registerLengthOrValue) { + uint8_t command[6]; + command[0] = 1; + command[1] = function; + command[2] = (registerStart >> 8) & 0xFF; + command[3] = registerStart & 0xFF; + command[4] = (registerLengthOrValue >> 8) & 0xFF; + command[5] = registerLengthOrValue & 0xFF; + + _lastActivityTime = millis(); + + WITH_SEN0658_SERIAL.write(command, 6); + uint16_t crc = CRC16_2(&command[0], 6); + WITH_SEN0658_SERIAL.write(crc & 0xFF); + WITH_SEN0658_SERIAL.write((crc >> 8) & 0xFF); +} + + +void DFROBOT_SEN0658::sendWriteCommand(uint16_t registerIndex, uint16_t value) { + sendCommand(0x06, registerIndex, value); +} + +void DFROBOT_SEN0658::powerOn() { + if (_powerOnTime == 0) { + MESH_DEBUG_PRINTLN("SEN0658 power on and ready to read sensor values in %i seconds.", (int)SEN0658_WARMUP_SECONDS); +#if defined(WITH_SEN0658_EN) + digitalWrite(WITH_SEN0658_EN, HIGH); + delay(100); // wait for power rail to stabilize +#endif + _powerOnTime = millis(); + } + _lastActivityTime = millis(); +} + +bool DFROBOT_SEN0658::updateSample(DFROBOT_SEN0658_Sample &sample) { + powerOn(); + // If still warming up, return cached data if available + if (!isSensorReady()) { + uint32_t timeSincePowerOn = millis() - _powerOnTime; + uint32_t time_remaining = (uint32_t)SEN0658_WARMUP_SECONDS * 1000 - timeSincePowerOn; + + // limit debug spew + if (_lastWarmupLog == 0 || millis() - _lastWarmupLog >= 1000) { + MESH_DEBUG_PRINTLN("SEN0658 is warming up. %lu ms until ready.", time_remaining); + _lastWarmupLog = millis(); + } + + return false; + } else if (readTemperature(sample) && readAir(sample) && readLight(sample) && readWind(sample)) { + sample.timestamp = rtc_clock.getCurrentTime(); + _cachedSample = sample; + _lastCacheTime = millis(); + MESH_DEBUG_PRINTLN("SEN0658 sample updated and cached at timestamp %lu.", sample.timestamp); + return true; + } else { + MESH_DEBUG_PRINTLN("SEN0658 sample update failed."); + return false; + } +} + +bool DFROBOT_SEN0658::readSample(DFROBOT_SEN0658_Sample &sample) { + bool result; + if (result = updateSample(sample)) { + MESH_DEBUG_PRINTLN("SEN0658 poll OK."); + } else if (!hasCachedSample()) { + MESH_DEBUG_PRINTLN("SEN0658 poll failed; no cached sample available."); + } else if (isCachedSampleValid()) { + sample = _cachedSample; + MESH_DEBUG_PRINTLN("SEN0658 poll failed; returning cached sample."); + result = true; + } else { + MESH_DEBUG_PRINTLN("SEN0658 poll failed; cached sample expired."); + } + + return result; +} + +void DFROBOT_SEN0658::loop() { + DFROBOT_SEN0658_Sample sample; + + // Check if it is time to poll + if (isPollDue()) { + if (updateSample(sample)) { + _lastPollTime = millis(); + MESH_DEBUG_PRINTLN("SEN0658 poll successful in loop."); + } + } + + // Idle timeout: power off if no activity +#if defined(WITH_SEN0658_EN) + if (isPoweredOn() && isSensorReady() && isIdle()) { + // opportunistically read a sample before shutting down + if(updateSample(sample)) { + _lastPollTime = millis(); + MESH_DEBUG_PRINTLN("SEN0658 poll successful before idle shutdown."); + } else { + MESH_DEBUG_PRINTLN("SEN0658 poll failed before idle shutdown."); + } + digitalWrite(WITH_SEN0658_EN, LOW); + _powerOnTime = 0; + _lastActivityTime = 0; + } +#endif +} + +uint16_t DFROBOT_SEN0658::CRC16_2(const uint8_t *buf, int len) { + uint16_t crc = 0xFFFF; + for (int pos = 0; pos < len; pos++) { + crc ^= (uint16_t)buf[pos]; + for (int i = 8; i != 0; i--) { + if ((crc & 0x0001) != 0) { + crc >>= 1; + crc ^= 0xA001; + } else { + crc >>= 1; + } + } + } + return crc; +} + +#endif diff --git a/src/helpers/sensors/DFROBOT_SEN0658.h b/src/helpers/sensors/DFROBOT_SEN0658.h new file mode 100644 index 0000000000..1649121ea8 --- /dev/null +++ b/src/helpers/sensors/DFROBOT_SEN0658.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include + +#if defined(ENV_INCLUDE_GPS) && \ + (PIN_GPS_TX == WITH_SEN0658_TX || PIN_GPS_TX == WITH_SEN0658_RX || \ + PIN_GPS_RX == WITH_SEN0658_TX || PIN_GPS_RX == WITH_SEN0658_RX) + #error "GPS pins conflict with SEN0658 pins. Please change the pin definitions to avoid conflicts" +#endif + + +#ifndef SEN0658_POLL_PERIOD_SECONDS +#define SEN0658_POLL_PERIOD_SECONDS (10 * 60) +#endif + +#ifndef SEN0658_CACHE_MAX_AGE_SECONDS +#define SEN0658_CACHE_MAX_AGE_SECONDS (SEN0658_POLL_PERIOD_SECONDS * 5 / 2) +#endif + +#ifndef SEN0658_WARMUP_SECONDS +#define SEN0658_WARMUP_SECONDS 30 +#endif + +#ifndef SEN0658_IDLE_TIMEOUT_SECONDS +#define SEN0658_IDLE_TIMEOUT_SECONDS 30 +#endif + +struct DFROBOT_SEN0658_Sample { + float temperature; + float humidity; + float airPressure; + float pm2_5; + float pm10; + float windSpeed; + float windAngle; + float noiseDb; + float luminosity; + uint32_t timestamp; +}; + +class DFROBOT_SEN0658 +{ + public: + bool begin(); + bool readSample(DFROBOT_SEN0658_Sample &sample); + bool readWindDirectionOffset(uint16_t &offset); + bool writeWindDirectionOffset(uint16_t offset); + bool zeroWindSpeed(); + bool zeroRainfall(); + void loop(); + bool hasPendingWork(); + private: + DFROBOT_SEN0658_Sample _cachedSample = {0}; + uint32_t _lastCacheTime = 0; + uint32_t _lastPollTime = 0; + uint32_t _powerOnTime = 0; + uint32_t _lastActivityTime = 0; + uint32_t _lastWarmupLog = 0; + void powerOn(); + static uint16_t CRC16_2(const uint8_t *buf, int len); + void sendCommand(uint8_t function, uint16_t registerStart, uint16_t registerLengthOrValue); + void sendWriteCommand(uint16_t registerIndex, uint16_t value); + bool readBytes(uint8_t *buffer, int len); + void flushSerial(); + bool updateSample(DFROBOT_SEN0658_Sample &sample); + template bool readRegisters(uint16_t registerStart, PacketType& packet); + bool writeRegister(uint16_t registerIndex, uint16_t value); + bool readWind(DFROBOT_SEN0658_Sample &sample); + bool readTemperature(DFROBOT_SEN0658_Sample &sample); + bool readAir(DFROBOT_SEN0658_Sample &sample); + bool readLight(DFROBOT_SEN0658_Sample &sample); + // helpers + bool isPollDue() { return millis() - _lastPollTime >= (uint32_t)SEN0658_POLL_PERIOD_SECONDS * 1000; } + bool isPoweredOn() { return _powerOnTime != 0; } + bool isSensorReady() { return isPoweredOn() && millis() - _powerOnTime >= (uint32_t)SEN0658_WARMUP_SECONDS * 1000; } + bool hasCachedSample() { return _lastCacheTime != 0; } + bool isCachedSampleValid() { return hasCachedSample() && millis() - _lastCacheTime < (uint32_t)SEN0658_CACHE_MAX_AGE_SECONDS * 1000; } + bool isIdle() { return isPoweredOn() && millis() - _lastActivityTime >= (uint32_t)SEN0658_IDLE_TIMEOUT_SECONDS * 1000; } +}; diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 07807011db..ab5487de5e 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -45,6 +45,11 @@ static Adafruit_BME280 BME280; static Adafruit_BMP280 BMP280(TELEM_WIRE); #endif +#if ENV_INCLUDE_SEN0658 +#include "DFROBOT_SEN0658.h" +static DFROBOT_SEN0658 SEN0658; +#endif + #if ENV_INCLUDE_SHTC3 #include static Adafruit_SHTC3 SHTC3; @@ -331,6 +336,16 @@ bool EnvironmentSensorManager::begin() { } #endif + #if ENV_INCLUDE_SEN0658 + if (SEN0658.begin()) { + MESH_DEBUG_PRINTLN("Found sensor SEN0658"); + SEN0658_initialized = true; + } else { + SEN0658_initialized = false; + MESH_DEBUG_PRINTLN("SEN0658 was not found"); + } + #endif + return true; } @@ -483,6 +498,26 @@ bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, Cayen } #endif + #if ENV_INCLUDE_SEN0658 + if (SEN0658_initialized) { + DFROBOT_SEN0658_Sample sample = {0}; + if (SEN0658.readSample(sample)) { + // this is an external sensor, so don't start with SELF channel + telemetry.addTemperature(next_available_channel, sample.temperature); + telemetry.addRelativeHumidity(next_available_channel, sample.humidity); + telemetry.addBarometricPressure(next_available_channel, sample.airPressure); + telemetry.addConcentration(next_available_channel, sample.pm2_5); + telemetry.addAnalogInput(next_available_channel, sample.pm10); + telemetry.addDistance(next_available_channel, sample.windSpeed); + telemetry.addDirection(next_available_channel, sample.windAngle); + telemetry.addLuminosity(next_available_channel, sample.luminosity); + telemetry.addGenericSensor(next_available_channel, sample.noiseDb); + telemetry.addUnixTime(next_available_channel, sample.timestamp); + next_available_channel++; + } + } + #endif + } return true; @@ -494,6 +529,9 @@ int EnvironmentSensorManager::getNumSettings() const { #if ENV_INCLUDE_GPS if (gps_detected) settings++; // only show GPS setting if GPS is detected #endif + #if ENV_INCLUDE_SEN0658 + if (SEN0658_initialized) settings += 3; + #endif return settings; } @@ -504,8 +542,17 @@ const char* EnvironmentSensorManager::getSettingName(int i) const { return "gps"; } #endif - // convenient way to add params (needed for some tests) -// if (i == settings++) return "param.2"; + #if ENV_INCLUDE_SEN0658 + if (SEN0658_initialized && i == settings++) { + return "wind_dir_offset"; + } + if (SEN0658_initialized && i == settings++) { + return "wind_speed_zero"; + } + if (SEN0658_initialized && i == settings++) { + return "rainfall_zero"; + } + #endif return NULL; } @@ -516,8 +563,28 @@ const char* EnvironmentSensorManager::getSettingValue(int i) const { return gps_active ? "1" : "0"; } #endif - // convenient way to add params ... -// if (i == settings++) return "2"; + #if ENV_INCLUDE_SEN0658 + if (SEN0658_initialized && i == settings++) { + uint16_t offset; + if (SEN0658.readWindDirectionOffset(offset)) { + if (offset == 0) { + return "0"; + } else if (offset == 1) { + return "1"; + } else { + return "[unknown]"; + } + } else { + return "[error]"; + } + } + if (SEN0658_initialized && i == settings++) { + return "[only writeable]"; + } + if (SEN0658_initialized && i == settings++) { + return "[only writeable]"; + } + #endif return NULL; } @@ -541,6 +608,15 @@ bool EnvironmentSensorManager::setSettingValue(const char* name, const char* val return true; } #endif + #if ENV_INCLUDE_SEN0658 + if (strcmp(name, "wind_dir_offset") == 0) { + return SEN0658.writeWindDirectionOffset(atoi(value)); + } else if (strcmp(name, "wind_speed_zero") == 0) { + return SEN0658.zeroWindSpeed(); + } else if (strcmp(name, "rainfall_zero") == 0) { + return SEN0658.zeroRainfall(); + } + #endif return false; // not supported } @@ -702,6 +778,19 @@ void EnvironmentSensorManager::stop_gps() { MESH_DEBUG_PRINTLN("Stop GPS is N/A on this board. Actual GPS state unchanged"); #endif } +#endif + +bool EnvironmentSensorManager::hasPendingWork() { + #if ENV_INCLUDE_SEN0658 + if (SEN0658_initialized) { + if(SEN0658.hasPendingWork()) { + return true; + } + } + #endif + + return false; +} void EnvironmentSensorManager::loop() { static long next_gps_update = 0; @@ -734,5 +823,10 @@ void EnvironmentSensorManager::loop() { next_gps_update = millis() + (gps_update_interval_sec * 1000); } #endif -} -#endif + + #if ENV_INCLUDE_SEN0658 + if (SEN0658_initialized) { + SEN0658.loop(); + } + #endif +} \ No newline at end of file diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index f176a33f5f..1fe5b22f25 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -22,6 +22,7 @@ class EnvironmentSensorManager : public SensorManager { bool SHT4X_initialized = false; bool BME680_initialized = false; bool BMP085_initialized = false; + bool SEN0658_initialized = false; bool gps_detected = false; bool gps_active = false; @@ -47,10 +48,9 @@ class EnvironmentSensorManager : public SensorManager { EnvironmentSensorManager(){}; #endif bool begin() override; + bool hasPendingWork() override; bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; - #if ENV_INCLUDE_GPS void loop() override; - #endif int getNumSettings() const override; const char* getSettingName(int i) const override; const char* getSettingValue(int i) const override; diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 737ef5652f..c44275ba9e 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -46,6 +46,28 @@ build_src_filter = ${rak4631.build_src_filter} + +<../examples/simple_repeater> +[env:RAK_4631_repeater_SEN0658_serial2] +extends = rak4631 +build_flags = + ${rak4631.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"RAK4631 Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 + -D ENV_INCLUDE_SEN0658=1 + -D WITH_SEN0658_SERIAL=Serial2 + -D WITH_SEN0658_RX=PIN_SERIAL2_RX + -D WITH_SEN0658_TX=PIN_SERIAL2_TX + -D WITH_SEN0658_EN=WB_IO3 + -UENV_INCLUDE_GPS +; -D MESH_PACKET_LOGGING=1 + -D MESH_DEBUG=1 +build_src_filter = ${rak4631.build_src_filter} + + + +<../examples/simple_repeater> + [env:RAK_4631_repeater_bridge_rs232_serial1] extends = rak4631 build_flags = diff --git a/variants/rak4631/variant.cpp b/variants/rak4631/variant.cpp index bd85e97133..f5655c0cb4 100644 --- a/variants/rak4631/variant.cpp +++ b/variants/rak4631/variant.cpp @@ -45,5 +45,10 @@ void initVariant() pinMode(PIN_LED2, OUTPUT); ledOff(PIN_LED2);; + +#if defined(WITH_SEN0658_EN) + pinMode(WITH_SEN0658_EN, OUTPUT); + digitalWrite(WITH_SEN0658_EN, LOW); +#endif }