diff --git a/include/radio/rmt/Sequence.h b/include/radio/rmt/Sequence.h index ba043258..ea8ae17d 100644 --- a/include/radio/rmt/Sequence.h +++ b/include/radio/rmt/Sequence.h @@ -19,6 +19,8 @@ namespace OpenShock::Rmt { , m_transmitEnd(0) , m_shockerId(0) , m_shockerModel() + , m_commandType() + , m_intensity(0) { } Sequence(ShockerModelType shockerModel, uint16_t shockerId, int64_t transmitEnd); @@ -28,6 +30,8 @@ namespace OpenShock::Rmt { , m_transmitEnd(other.m_transmitEnd) , m_shockerId(other.m_shockerId) , m_shockerModel(other.m_shockerModel) + , m_commandType(other.m_commandType) + , m_intensity(other.m_intensity) { other.reset(); } @@ -48,6 +52,7 @@ namespace OpenShock::Rmt { inline size_t size() const noexcept { return m_size; } bool fill(ShockerCommandType commandType, uint8_t intensity); + bool refill(); Sequence& operator=(Sequence&& other) { @@ -60,6 +65,8 @@ namespace OpenShock::Rmt { m_transmitEnd = other.m_transmitEnd; m_shockerId = other.m_shockerId; m_shockerModel = other.m_shockerModel; + m_commandType = other.m_commandType; + m_intensity = other.m_intensity; other.reset(); @@ -74,6 +81,8 @@ namespace OpenShock::Rmt { m_transmitEnd = 0; m_shockerId = 0; m_shockerModel = static_cast(0); + m_commandType = static_cast(0); + m_intensity = 0; } rmt_data_t* m_data; @@ -81,5 +90,7 @@ namespace OpenShock::Rmt { int64_t m_transmitEnd; uint16_t m_shockerId; ShockerModelType m_shockerModel; + ShockerCommandType m_commandType; + uint8_t m_intensity; }; } // namespace OpenShock::Rmt diff --git a/src/radio/RFTransmitter.cpp b/src/radio/RFTransmitter.cpp index 9f5de283..50de338f 100644 --- a/src/radio/RFTransmitter.cpp +++ b/src/radio/RFTransmitter.cpp @@ -180,7 +180,7 @@ static void writeSequences(rmt_obj_t* rmt_handle, std::vector& se int64_t timeToLive = seq->transmitEnd() - OpenShock::millis(); if (timeToLive > 0) { - // Send the command + seq->refill(); rmtWriteBlocking(rmt_handle, seq->payload(), seq->size()); } else { // Remove command if it has sent out its termination sequence for long enough diff --git a/src/radio/rmt/Sequence.cpp b/src/radio/rmt/Sequence.cpp index 9d952cc8..0d820574 100644 --- a/src/radio/rmt/Sequence.cpp +++ b/src/radio/rmt/Sequence.cpp @@ -73,5 +73,22 @@ Rmt::Sequence::Sequence(ShockerModelType shockerModel, uint16_t shockerId, int64 bool Rmt::Sequence::fill(ShockerCommandType commandType, uint8_t intensity) { + m_commandType = commandType; + m_intensity = intensity; return fillSequenceImpl(payload(), m_shockerModel, m_shockerId, commandType, intensity); } + +static bool refillSequenceImpl(rmt_data_t* data, ShockerModelType modelType, uint16_t shockerId, ShockerCommandType commandType, uint8_t intensity) +{ + switch (modelType) { + case ShockerModelType::WellturnT330: + return Rmt::WellturnT330Encoder::FillBuffer(data, shockerId, commandType, intensity); + default: + return true; + } +} + +bool Rmt::Sequence::refill() +{ + return refillSequenceImpl(payload(), m_shockerModel, m_shockerId, m_commandType, m_intensity); +} diff --git a/src/radio/rmt/T330Encoder.cpp b/src/radio/rmt/T330Encoder.cpp index 59896daa..6e7e08c2 100644 --- a/src/radio/rmt/T330Encoder.cpp +++ b/src/radio/rmt/T330Encoder.cpp @@ -1,8 +1,10 @@ #include "radio/rmt/T330Encoder.h" +#include "Core.h" #include "radio/rmt/internal/Shared.h" #include +#include const rmt_data_t kRmtPreamble = {960, 1, 790, 0}; const rmt_data_t kRmtOne = {220, 1, 980, 0}; @@ -11,6 +13,14 @@ const rmt_data_t kRmtPostamble = {220, 1, 135, 0}; using namespace OpenShock; +struct ShockRollingState { + int64_t lastFillTime = 0; + int64_t transmitStart = 0; + uint8_t baseCounter = 0; +}; + +static std::unordered_map s_shockState; + size_t Rmt::WellturnT330Encoder::GetBufferSize() { return 43; @@ -37,10 +47,36 @@ bool Rmt::WellturnT330Encoder::FillBuffer(rmt_data_t* sequence, uint16_t shocker return false; // Invalid type } + // Shock intensity byte: [toggle:1][counter:3][level:4] + // Toggle flips every ~1s, counter increments every ~2s (each toggle cycle). + // Computed from wall-clock time so the buffer can be re-filled each send. + uint8_t intensityByte = intensity; + if (type == ShockerCommandType::Shock) { + auto& state = s_shockState[shockerId]; + uint8_t level = (intensity * 15) / 100; + + int64_t now = OpenShock::millis(); + if (state.lastFillTime == 0 || (now - state.lastFillTime) > 200) { + if (state.lastFillTime != 0) { + state.baseCounter = (state.baseCounter + 1) & 0x7; + } + state.transmitStart = now; + } + state.lastFillTime = now; + + int64_t elapsed = now - state.transmitStart; + int64_t periods = elapsed / 1000; + + bool toggle = (periods % 2) != 0; + uint8_t counter = (state.baseCounter + static_cast(periods / 2)) & 0x7; + + intensityByte = (static_cast(toggle) << 7) | ((counter & 0x7) << 4) | (level & 0xF); + } + uint8_t channelId = 0; // CH1 is 0b0000 and CH2 is 0b1110 on my remote but other values probably work. // Payload layout: [channelId:4][typeU:4][transmitterId:16][intensity:8][typeL:4][channelId:4] - uint64_t data = (static_cast(channelId & 0xF) << 36) | (static_cast(typeVal & 0xF0) << 28) | (static_cast(shockerId) << 16) | (static_cast(intensity) << 8) | (static_cast(typeVal & 0xF) << 4) + uint64_t data = (static_cast(channelId & 0xF) << 36) | (static_cast(typeVal & 0xF0) << 28) | (static_cast(shockerId) << 16) | (static_cast(intensityByte) << 8) | (static_cast(typeVal & 0xF) << 4) | static_cast(channelId & 0xF); // Shift the data left by 1 bit to append a zero