Skip to content

Commit 6f3f4fb

Browse files
hhvrcCopilot
andauthored
Better RMT and RFTX logic (#368)
* Initial proposal * Less enthusiastic changes * More tweaking * Update src/radio/rmt/Sequence.cpp Co-authored-by: Copilot <[email protected]> * Update src/radio/RFTransmitter.cpp Co-authored-by: Copilot <[email protected]> * Update src/radio/RFTransmitter.cpp Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent 21f3b5e commit 6f3f4fb

File tree

6 files changed

+202
-146
lines changed

6 files changed

+202
-146
lines changed

include/radio/RFTransmitter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ namespace OpenShock {
3232
void destroy();
3333
void TransmitTask();
3434

35+
struct Command;
36+
3537
gpio_num_t m_txPin;
3638
rmt_obj_t* m_rmtHandle;
3739
QueueHandle_t m_queueHandle;

include/radio/rmt/MainEncoder.h renamed to include/radio/rmt/Sequence.h

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,62 +9,77 @@
99
#include <cstdint>
1010

1111
namespace OpenShock::Rmt {
12-
class MainEncoder {
13-
DISABLE_COPY(MainEncoder);
12+
class Sequence {
13+
DISABLE_COPY(Sequence);
1414

1515
public:
16-
MainEncoder()
16+
Sequence()
1717
: m_data(nullptr)
1818
, m_size(0)
19-
, m_shockerModel()
19+
, m_transmitEnd(0)
2020
, m_shockerId(0)
21+
, m_shockerModel()
2122
{
2223
}
23-
MainEncoder(ShockerModelType shockerModel, uint16_t shockerId);
24-
MainEncoder(MainEncoder&& other) noexcept
24+
Sequence(ShockerModelType shockerModel, uint16_t shockerId, int64_t transmitEnd);
25+
Sequence(Sequence&& other) noexcept
2526
: m_data(other.m_data)
2627
, m_size(other.m_size)
27-
, m_shockerModel(other.m_shockerModel)
28+
, m_transmitEnd(other.m_transmitEnd)
2829
, m_shockerId(other.m_shockerId)
30+
, m_shockerModel(other.m_shockerModel)
2931
{
30-
other.m_data = nullptr;
31-
other.m_size = 0;
32-
other.m_shockerId = 0;
32+
other.reset();
3333
}
34-
~MainEncoder() { free(m_data); }
34+
~Sequence() { free(m_data); }
3535

3636
inline bool is_valid() const noexcept { return m_data != nullptr && m_size > 0; }
3737

3838
inline ShockerModelType shockerModel() const noexcept { return m_shockerModel; }
3939
inline uint16_t shockerId() const noexcept { return m_shockerId; }
4040

41+
inline int64_t transmitEnd() const noexcept { return m_transmitEnd; }
42+
inline void setTransmitEnd(int64_t transmitEnd) noexcept { m_transmitEnd = transmitEnd; }
43+
4144
inline rmt_data_t* payload() noexcept { return m_data; }
4245
inline const rmt_data_t* payload() const noexcept { return m_data; }
4346
inline rmt_data_t* terminator() noexcept { return m_data + m_size; }
4447
inline const rmt_data_t* terminator() const noexcept { return m_data + m_size; }
4548
inline size_t size() const noexcept { return m_size; }
4649

47-
bool fillSequence(ShockerCommandType commandType, uint8_t intensity);
50+
bool fill(ShockerCommandType commandType, uint8_t intensity);
4851

49-
MainEncoder& operator=(MainEncoder&& other)
52+
Sequence& operator=(Sequence&& other)
5053
{
5154
if (this == &other) return *this;
5255

5356
free(m_data);
5457

55-
m_data = other.m_data;
56-
m_size = other.m_size;
58+
m_data = other.m_data;
59+
m_size = other.m_size;
60+
m_transmitEnd = other.m_transmitEnd;
61+
m_shockerId = other.m_shockerId;
62+
m_shockerModel = other.m_shockerModel;
5763

58-
other.m_data = nullptr;
59-
other.m_size = 0;
64+
other.reset();
6065

6166
return *this;
6267
}
6368

6469
private:
70+
void reset()
71+
{
72+
m_data = nullptr;
73+
m_size = 0;
74+
m_transmitEnd = 0;
75+
m_shockerId = 0;
76+
m_shockerModel = (ShockerModelType)0;
77+
}
78+
6579
rmt_data_t* m_data;
6680
size_t m_size;
67-
ShockerModelType m_shockerModel;
81+
int64_t m_transmitEnd;
6882
uint16_t m_shockerId;
83+
ShockerModelType m_shockerModel;
6984
};
7085
} // namespace OpenShock::Rmt

src/CommandHandler.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -297,18 +297,6 @@ bool CommandHandler::HandleCommand(ShockerModelType model, uint16_t shockerId, S
297297
return false;
298298
}
299299

300-
// Stop logic
301-
if (type == ShockerCommandType::Stop) {
302-
OS_LOGV(TAG, "Stop command received, clearing pending commands");
303-
304-
type = ShockerCommandType::Vibrate;
305-
intensity = 0;
306-
durationMs = 300;
307-
308-
s_rfTransmitter->ClearPendingCommands();
309-
} else {
310-
OS_LOGD(TAG, "Command received: %u %u %u %u", model, shockerId, type, intensity);
311-
}
312300

313301
bool ok = s_rfTransmitter->SendCommand(model, shockerId, type, intensity, durationMs);
314302

src/radio/RFTransmitter.cpp

Lines changed: 99 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,30 @@ const char* const TAG = "RFTransmitter";
77
#include "Core.h"
88
#include "estop/EStopManager.h"
99
#include "Logging.h"
10-
#include "radio/rmt/MainEncoder.h"
10+
#include "radio/rmt/Sequence.h"
1111
#include "util/FnProxy.h"
1212
#include "util/TaskUtils.h"
1313

1414
#include <freertos/queue.h>
1515

16-
const UBaseType_t RFTRANSMITTER_QUEUE_SIZE = 64;
17-
const BaseType_t RFTRANSMITTER_TASK_PRIORITY = 1;
18-
const uint32_t RFTRANSMITTER_TASK_STACK_SIZE = 4096; // PROFILED: 1.4KB stack usage
19-
const float RFTRANSMITTER_TICKRATE_NS = 1000;
20-
const int64_t TRANSMIT_END_DURATION = 300;
21-
const int64_t SEQUENCE_TIME_TO_LIVE = 1000;
16+
const UBaseType_t kQueueSize = 64;
17+
const BaseType_t kTaskPriority = 1;
18+
const uint32_t kTaskStackSize = 4096; // PROFILED: 1.4KB stack usage
19+
const float kTickrateNs = 1000;
20+
const int64_t kTerminatorDurationMs = 300;
21+
const uint8_t kFlagOverwrite = 1 << 0;
22+
const uint8_t kFlagDeleteTask = 1 << 1;
23+
const TickType_t kTaskIdleDelay = pdMS_TO_TICKS(5);
2224

2325
using namespace OpenShock;
2426

25-
struct command_t {
26-
int64_t until;
27-
ShockerModelType model;
27+
struct RFTransmitter::Command {
28+
int64_t transmitEnd;
29+
ShockerModelType modelType;
2830
ShockerCommandType type;
2931
uint16_t shockerId;
3032
uint8_t intensity;
31-
bool overwrite : 1;
32-
bool destroy : 1;
33-
};
34-
struct sequence_t {
35-
int64_t until;
36-
Rmt::MainEncoder encoder;
33+
uint8_t flags;
3734
};
3835

3936
RFTransmitter::RFTransmitter(gpio_num_t gpioPin)
@@ -51,10 +48,10 @@ RFTransmitter::RFTransmitter(gpio_num_t gpioPin)
5148
return;
5249
}
5350

54-
float realTick = rmtSetTick(m_rmtHandle, RFTRANSMITTER_TICKRATE_NS);
51+
float realTick = rmtSetTick(m_rmtHandle, kTickrateNs);
5552
OS_LOGD(TAG, "[pin-%hhi] real tick set to: %fns", m_txPin, realTick);
5653

57-
m_queueHandle = xQueueCreate(RFTRANSMITTER_QUEUE_SIZE, sizeof(command_t));
54+
m_queueHandle = xQueueCreate(kQueueSize, sizeof(Command));
5855
if (m_queueHandle == nullptr) {
5956
OS_LOGE(TAG, "[pin-%hhi] Failed to create queue", m_txPin);
6057
destroy();
@@ -64,7 +61,7 @@ RFTransmitter::RFTransmitter(gpio_num_t gpioPin)
6461
char name[32];
6562
snprintf(name, sizeof(name), "RFTransmitter-%u", m_txPin);
6663

67-
if (TaskUtils::TaskCreateExpensive(&Util::FnProxy<&RFTransmitter::TransmitTask>, name, RFTRANSMITTER_TASK_STACK_SIZE, this, RFTRANSMITTER_TASK_PRIORITY, &m_taskHandle) != pdPASS) {
64+
if (TaskUtils::TaskCreateExpensive(&Util::FnProxy<&RFTransmitter::TransmitTask>, name, kTaskStackSize, this, kTaskPriority, &m_taskHandle) != pdPASS) {
6865
OS_LOGE(TAG, "[pin-%hhi] Failed to create task", m_txPin);
6966
destroy();
7067
return;
@@ -83,7 +80,19 @@ bool RFTransmitter::SendCommand(ShockerModelType model, uint16_t shockerId, Shoc
8380
return false;
8481
}
8582

86-
command_t cmd = command_t {.until = OpenShock::millis() + durationMs, .model = model, .type = type, .shockerId = shockerId, .intensity = intensity, .overwrite = overwriteExisting, .destroy = false};
83+
// Stop logic
84+
if (type == ShockerCommandType::Stop) {
85+
OS_LOGV(TAG, "Stop command received");
86+
87+
type = ShockerCommandType::Vibrate;
88+
intensity = 0;
89+
durationMs = 300;
90+
overwriteExisting = true;
91+
} else {
92+
OS_LOGD(TAG, "Command received: %u %u %u %u", model, shockerId, type, intensity);
93+
}
94+
95+
Command cmd = Command {.transmitEnd = OpenShock::millis() + durationMs, .modelType = model, .type = type, .shockerId = shockerId, .intensity = intensity, .flags = overwriteExisting ? kFlagOverwrite : (uint8_t)0};
8796

8897
// Add the command to the queue, wait max 10 ms (Adjust this)
8998
if (xQueueSend(m_queueHandle, &cmd, pdMS_TO_TICKS(10)) != pdTRUE) {
@@ -102,7 +111,7 @@ void RFTransmitter::ClearPendingCommands()
102111

103112
OS_LOGI(TAG, "[pin-%hhi] Clearing pending commands", m_txPin);
104113

105-
command_t command;
114+
Command command;
106115
while (xQueueReceive(m_queueHandle, &command, 0) == pdPASS) {
107116
}
108117
}
@@ -113,7 +122,7 @@ void RFTransmitter::destroy()
113122
OS_LOGD(TAG, "[pin-%hhi] Stopping task", m_txPin);
114123

115124
// Wait for the task to stop
116-
command_t cmd {.destroy = true};
125+
Command cmd {.flags = kFlagDeleteTask};
117126
while (eTaskGetState(m_taskHandle) != eDeleted) {
118127
vTaskDelay(pdMS_TO_TICKS(10));
119128

@@ -138,73 +147,98 @@ void RFTransmitter::destroy()
138147
}
139148
}
140149

150+
static bool addSequence(std::vector<Rmt::Sequence>& sequences, ShockerModelType modelType, uint16_t shockerId, ShockerCommandType commandType, uint8_t intensity, int64_t transmitEnd)
151+
{
152+
Rmt::Sequence sequence(modelType, shockerId, transmitEnd);
153+
if (!sequence.is_valid()) return false;
154+
155+
if (!sequence.fill(commandType, intensity)) return false;
156+
157+
sequences.push_back(std::move(sequence));
158+
159+
return true;
160+
}
161+
162+
static bool modifySequence(std::vector<Rmt::Sequence>& sequences, ShockerModelType modelType, uint16_t shockerId, ShockerCommandType commandType, uint8_t intensity, int64_t transmitEnd)
163+
{
164+
for (auto& seq : sequences) {
165+
if (seq.shockerModel() == modelType && seq.shockerId() == shockerId) {
166+
bool ok = seq.fill(commandType, intensity);
167+
seq.setTransmitEnd(ok ? transmitEnd : 0); // Remove this immediately if fill didnt succeed
168+
return ok; // Returns whether modification succeeded; caller should generate a new sequence if this fails
169+
}
170+
}
171+
172+
return false;
173+
}
174+
175+
static void writeSequences(rmt_obj_t* rmt_handle, std::vector<Rmt::Sequence>& sequences)
176+
{
177+
// Send queued commands
178+
for (auto seq = sequences.begin(); seq != sequences.end();) {
179+
int64_t timeToLive = seq->transmitEnd() - OpenShock::millis();
180+
181+
if (timeToLive > 0) {
182+
// Send the command
183+
rmtWriteBlocking(rmt_handle, seq->payload(), seq->size());
184+
} else {
185+
// Remove command if it has sent out its termination sequence for long enough
186+
if (timeToLive + kTerminatorDurationMs <= 0) {
187+
seq = sequences.erase(seq);
188+
continue;
189+
}
190+
191+
// Send the termination sequence to stop the shocker
192+
rmtWriteBlocking(rmt_handle, seq->terminator(), seq->size());
193+
}
194+
195+
// Move to the next command
196+
++seq;
197+
}
198+
}
199+
141200
void RFTransmitter::TransmitTask()
142201
{
143202
OS_LOGD(TAG, "[pin-%hhi] RMT loop running on core %d", m_txPin, xPortGetCoreID());
144203

145-
std::vector<sequence_t> sequences;
204+
bool wasEstopped = false;
205+
std::vector<Rmt::Sequence> sequences;
146206
while (true) {
147207
// Receive commands
148-
command_t cmd;
208+
Command cmd;
149209
while (xQueueReceive(m_queueHandle, &cmd, sequences.empty() ? portMAX_DELAY : 0) == pdTRUE) {
150210
// Destroy task if we receive destroy command
151-
if (cmd.destroy) {
211+
if ((cmd.flags & kFlagDeleteTask) != 0) {
152212
sequences.clear();
153213
vTaskDelete(nullptr);
154214
return;
155215
}
156216

157-
if (cmd.overwrite) {
217+
if ((cmd.flags & kFlagOverwrite) != 0) {
158218
// Replace the sequence if it already exists
159-
auto it = std::find_if(sequences.begin(), sequences.end(), [&cmd](const sequence_t& seq) { return seq.encoder.shockerId() == cmd.shockerId; });
160-
if (it != sequences.end()) {
161-
it->encoder.fillSequence(cmd.type, cmd.intensity);
162-
it->until = cmd.until;
219+
if (modifySequence(sequences, cmd.modelType, cmd.shockerId, cmd.type, cmd.intensity, cmd.transmitEnd)) {
163220
continue;
164221
}
165222
}
166223

167-
Rmt::MainEncoder encoder(cmd.model, cmd.shockerId);
168-
if (encoder.is_valid()) {
169-
encoder.fillSequence(cmd.type, cmd.intensity);
170-
171-
// If the command was not replaced, add it to the queue
172-
sequences.push_back(sequence_t {.until = cmd.until, .encoder = std::move(encoder)});
224+
if (!addSequence(sequences, cmd.modelType, cmd.shockerId, cmd.type, cmd.intensity, cmd.transmitEnd)) {
225+
OS_LOGD(TAG, "[pin-%hhi] Failed to add sequence");
173226
}
174227
}
175228

176-
if (OpenShock::EStopManager::IsEStopped()) {
177-
int64_t whenEStoppedTime = EStopManager::LastEStopped();
229+
bool isEstopped = OpenShock::EStopManager::IsEStopped();
230+
if (isEstopped != wasEstopped) {
231+
wasEstopped = isEstopped;
178232

179-
for (auto seq = sequences.begin(); seq != sequences.end(); ++seq) {
180-
seq->until = whenEStoppedTime;
181-
}
182-
}
183-
184-
// Send queued commands
185-
for (auto seq = sequences.begin(); seq != sequences.end();) {
186-
bool expired = seq->until < OpenShock::millis();
187-
188-
// Remove expired commands, else send the command.
189-
// After sending/receiving a command, move to the next one.
190-
if (expired) {
191-
// Send the zero sequence to stop the shocker
192-
rmtWriteBlocking(m_rmtHandle, seq->encoder.terminator(), seq->encoder.size());
193-
194-
if (seq->until + TRANSMIT_END_DURATION < OpenShock::millis()) {
195-
// Remove the command and move to the next one
196-
seq = sequences.erase(seq);
197-
} else {
198-
// Move to the next command
199-
++seq;
233+
if (isEstopped) {
234+
// Set all sequences to transmit their terminators
235+
int64_t now = OpenShock::millis();
236+
for (auto seq = sequences.begin(); seq != sequences.end(); ++seq) {
237+
seq->setTransmitEnd(now);
200238
}
201-
} else {
202-
// Send the command
203-
rmtWriteBlocking(m_rmtHandle, seq->encoder.payload(), seq->encoder.size());
204-
205-
// Move to the next command
206-
++seq;
207239
}
208240
}
241+
242+
writeSequences(m_rmtHandle, sequences);
209243
}
210244
}

0 commit comments

Comments
 (0)