@@ -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
2325using 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
3936RFTransmitter::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+
141200void 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