Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix: xlc rmt encoder postamble #148

Merged
merged 3 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions include/radio/rmt/internal/Shared.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <esp32-hal-rmt.h>

#include <cstdint>
#include <limits>
#include <type_traits>
#include <vector>

namespace OpenShock::Rmt::Internal {
template<std::size_t N, typename T>
inline void EncodeBits(std::vector<rmt_data_t>& pulses, T data, const rmt_data_t& rmtOne, const rmt_data_t& rmtZero) {
static_assert(std::is_unsigned<T>::value, "T must be an unsigned integer");
static_assert(N > 0, "N must be greater than 0");
static_assert(N < std::numeric_limits<T>::digits, "N must be less or equal to the number of bits in T");

for (std::size_t bit_pos = N - 1; bit_pos >= 0; --bit_pos) {
pulses.push_back((data >> bit_pos) & 1 ? rmtOne : rmtZero);
}
}
}
36 changes: 29 additions & 7 deletions src/radio/rmt/CaiXianlinEncoder.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "radio/rmt/CaiXianlinEncoder.h"

#include "radio/rmt/internal/Shared.h"

#include "Checksum.h"

// This is the encoder for the CaiXianlin shocker.
Expand All @@ -17,19 +19,39 @@ std::vector<rmt_data_t> Rmt::CaiXianlinEncoder::GetSequence(std::uint16_t transm
// Intensity must be between 0 and 99
intensity = std::min(intensity, (std::uint8_t)99);

std::uint64_t data = (std::uint64_t(transmitterId) << 24) | (std::uint64_t(channelId & 0xF) << 20) | (std::uint64_t((std::uint8_t)type & 0xF) << 16) | (std::uint64_t(intensity & 0xFF) << 8);
std::uint8_t typeVal = 0;
switch (type) {
case ShockerCommandType::Shock:
typeVal = 0x01;
break;
case ShockerCommandType::Vibrate:
typeVal = 0x02;
break;
case ShockerCommandType::Sound:
typeVal = 0x03;
break;
default:
return {}; // Invalid type
}

// Payload layout: [transmitterId:16][channelId:4][type:4][intensity:8]
std::uint32_t payload = (std::uint32_t(transmitterId & 0xFFFF) << 16) | (std::uint32_t(channelId & 0xF) << 12) | (std::uint32_t(typeVal) << 8) | std::uint32_t(intensity & 0xFF);

data |= Checksum::CRC8(data) & 0xFF;
// Calculate the checksum of the payload
std::uint8_t checksum = Checksum::CRC8(payload);

data <<= 2; // The 2 last bits are always 0. this is the postamble of the packet.
// Add the checksum to the payload
std::uint64_t data = (std::uint64_t(payload) << 8) | std::uint64_t(checksum);

// Shift the data left by 3 bits to add the postamble (3 bits of 0)
data <<= 3;

std::vector<rmt_data_t> pulses;
pulses.reserve(43);
pulses.reserve(44);

// Generate the sequence
pulses.push_back(kRmtPreamble);
for (int bit_pos = 41; bit_pos >= 0; --bit_pos) {
pulses.push_back((data >> bit_pos) & 1 ? kRmtOne : kRmtZero);
}
Internal::EncodeBits<43>(pulses, data, kRmtOne, kRmtZero);

return pulses;
}
32 changes: 26 additions & 6 deletions src/radio/rmt/PetrainerEncoder.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "radio/rmt/PetrainerEncoder.h"

#include "radio/rmt/internal/Shared.h"

const rmt_data_t kRmtPreamble = {750, 1, 750, 0};
const rmt_data_t kRmtOne = {200, 1, 1500, 0};
const rmt_data_t kRmtZero = {200, 1, 750, 0};
Expand All @@ -11,18 +13,36 @@ std::vector<rmt_data_t> Rmt::PetrainerEncoder::GetSequence(std::uint16_t shocker
// Intensity must be between 0 and 100
intensity = std::min(intensity, (std::uint8_t)100);

std::uint8_t methodBit = (0x80 | (1 << ((std::uint8_t)type - 1))) & 0xFF;
std::uint8_t methodChecksum = 0xFF ^ ((1 << (8 - (std::uint8_t)type)) | 1);
std::uint8_t nShift = 0;
switch (type) {
case ShockerCommandType::Shock:
nShift = 0;
break;
case ShockerCommandType::Vibrate:
nShift = 1;
break;
case ShockerCommandType::Sound:
nShift = 2;
break;
default:
return {}; // Invalid type
}

// Type is 0x80 | (0x01 << nShift)
std::uint8_t typeVal = (0x80 | (0x01 << nShift)) & 0xFF;

std::uint64_t data = (std::uint64_t(methodBit) << 32) | (std::uint64_t(shockerId) << 16) | (std::uint64_t(intensity) << 8) | (std::uint64_t(methodChecksum) << 0);
// TypeSum is NOT(0x01 | (0x80 >> nShift))
std::uint8_t typeSum = (~(0x01 | (0x80 >> nShift))) & 0xFF;

// Payload layout: [methodBit:8][shockerId:16][intensity:8][methodChecksum:8]
std::uint64_t data = (std::uint64_t(typeVal) << 32) | (std::uint64_t(shockerId) << 16) | (std::uint64_t(intensity) << 8) | std::uint64_t(typeSum);

std::vector<rmt_data_t> pulses;
pulses.reserve(42);

// Generate the sequence
pulses.push_back(kRmtPreamble);
for (int bit_pos = 39; bit_pos >= 0; --bit_pos) {
pulses.push_back((data >> bit_pos) & 1 ? kRmtOne : kRmtZero);
}
Internal::EncodeBits<40>(pulses, data, kRmtOne, kRmtZero);
pulses.push_back(kRmtPostamble);

return pulses;
Expand Down