171 lines
6.2 KiB
C
Executable file
171 lines
6.2 KiB
C
Executable file
|
|
/*
|
|
* This program source code file is part of SystemK, a library in the KTag project.
|
|
*
|
|
* 🛡️ <https://ktag.clubk.club> 🃞
|
|
*
|
|
* Copyright © 2016-2025 Joseph P. Kearney and the KTag developers.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License as published by the Free
|
|
* Software Foundation, either version 3 of the License, or (at your option) any
|
|
* later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
* details.
|
|
*
|
|
* There should be a copy of the GNU Affero General Public License in the LICENSE
|
|
* file in the root of this repository. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
|
|
#include "SystemK.h"
|
|
|
|
#define NEC_BLUE_TEAM 0x01
|
|
#define NEC_RED_TEAM 0x02
|
|
|
|
#define NEC_HEADER_MARK_DURATION_IN_us 9000
|
|
#define NEC_HEADER_SPACE_DURATION_IN_us 4500
|
|
#define NEC_ZERO_MARK_DURATION_IN_us 562
|
|
#define NEC_ZERO_SPACE_DURATION_IN_us 562
|
|
#define NEC_ONE_MARK_DURATION_IN_us 562
|
|
#define NEC_ONE_SPACE_DURATION_IN_us 1688
|
|
#define NEC_TOLERANCE_IN_us 200
|
|
|
|
#define MIN_ZERO_MARK_IN_us (NEC_ZERO_MARK_DURATION_IN_us - NEC_TOLERANCE_IN_us)
|
|
#define MAX_ZERO_MARK_IN_us (NEC_ZERO_MARK_DURATION_IN_us + NEC_TOLERANCE_IN_us)
|
|
#define MIN_ZERO_SPACE_IN_us (NEC_ZERO_SPACE_DURATION_IN_us - NEC_TOLERANCE_IN_us)
|
|
#define MAX_ZERO_SPACE_IN_us (NEC_ZERO_SPACE_DURATION_IN_us + NEC_TOLERANCE_IN_us)
|
|
#define MIN_ONE_MARK_IN_us (NEC_ONE_MARK_DURATION_IN_us - NEC_TOLERANCE_IN_us)
|
|
#define MAX_ONE_MARK_IN_us (NEC_ONE_MARK_DURATION_IN_us + NEC_TOLERANCE_IN_us)
|
|
#define MIN_ONE_SPACE_IN_us (NEC_ONE_SPACE_DURATION_IN_us - NEC_TOLERANCE_IN_us)
|
|
#define MAX_ONE_SPACE_IN_us (NEC_ONE_SPACE_DURATION_IN_us + NEC_TOLERANCE_IN_us)
|
|
|
|
static const TimedBit_T NEC_HEADER_MARK = {.symbol = MARK, .duration = NEC_HEADER_MARK_DURATION_IN_us};
|
|
static const TimedBit_T NEC_HEADER_SPACE = {.symbol = MARK, .duration = NEC_HEADER_SPACE_DURATION_IN_us};
|
|
static const TimedBit_T NEC_ZERO_SPACE = {.symbol = SPACE, .duration = NEC_ZERO_SPACE_DURATION_IN_us};
|
|
static const TimedBit_T NEC_ONE_SPACE = {.symbol = SPACE, .duration = NEC_ONE_SPACE_DURATION_IN_us};
|
|
static const TimedBit_T NEC_ZERO_MARK = {.symbol = MARK, .duration = NEC_ZERO_MARK_DURATION_IN_us};
|
|
static const TimedBit_T NEC_ONE_MARK = {.symbol = MARK, .duration = NEC_ONE_MARK_DURATION_IN_us};
|
|
static const TimedBit_T NEC_END = {.symbol = SPACE, .duration = LAST_PULSE};
|
|
|
|
static TimedPulseTrain_T Command_Send_Buffer;
|
|
static DecodedPacket_T NEC_Command_Buffers[3] =
|
|
{
|
|
{.Command.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
|
{.Command.type = DECODED_PACKET_TYPE_BUFFER_FREE},
|
|
{.Command.type = DECODED_PACKET_TYPE_BUFFER_FREE}};
|
|
|
|
static inline DecodedPacket_T *Get_Command_Packet_Buffer(void)
|
|
{
|
|
if (NEC_Command_Buffers[0].Command.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
|
{
|
|
return &NEC_Command_Buffers[0];
|
|
}
|
|
else if (NEC_Command_Buffers[1].Command.type == DECODED_PACKET_TYPE_BUFFER_FREE)
|
|
{
|
|
return &NEC_Command_Buffers[1];
|
|
}
|
|
else
|
|
{
|
|
// Just use it.
|
|
return &NEC_Command_Buffers[2];
|
|
}
|
|
}
|
|
|
|
static inline void PackPulseTrain(TimedPulseTrain_T *pulsetrain, uint32_t data)
|
|
{
|
|
pulsetrain->bitstream[0] = NEC_HEADER_MARK;
|
|
pulsetrain->bitstream[1] = NEC_HEADER_SPACE;
|
|
|
|
for (uint8_t n = 0; n < 16; n++)
|
|
{
|
|
if ((data & 0x80000000) == 0x80000000)
|
|
{
|
|
pulsetrain->bitstream[1 + (n * 2)] = NEC_ONE_SPACE;
|
|
}
|
|
else
|
|
{
|
|
pulsetrain->bitstream[1 + (n * 2)] = NEC_ZERO_SPACE;
|
|
}
|
|
data = data << 1;
|
|
if ((data & 0x80000000) == 0x80000000)
|
|
{
|
|
pulsetrain->bitstream[2 + (n * 2)] = NEC_ONE_MARK;
|
|
}
|
|
else
|
|
{
|
|
pulsetrain->bitstream[2 + (n * 2)] = NEC_ZERO_MARK;
|
|
}
|
|
data = data << 1;
|
|
}
|
|
|
|
pulsetrain->bitstream[(2 * 20) + 1] = NEC_END;
|
|
}
|
|
|
|
TimedPulseTrain_T *NEC_EncodePacket(CommandPacket_T *packet)
|
|
{
|
|
uint32_t packed_data = 0x00000000;
|
|
|
|
PackPulseTrain(&Command_Send_Buffer, packed_data);
|
|
|
|
return &Command_Send_Buffer;
|
|
}
|
|
|
|
DecodedPacket_T *NEC_MaybeDecodePacket(TimedPulseTrain_T *packet)
|
|
{
|
|
// Sometimes the Roku remote will repeat keys, so only check for a minimum here.
|
|
if (packet->count < 67)
|
|
{
|
|
// Fail fast!
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
// Convert pulse durations to bits.
|
|
// packet->bitstream[0] is the header mark--ignore it.
|
|
// packet->bitstream[1] is the header space--ignore it, too.
|
|
|
|
uint32_t data = 0;
|
|
for (uint8_t n = 0; n < 32; n++)
|
|
{
|
|
uint8_t mark_index = (n * 2) + 2;
|
|
uint8_t space_index = (n * 2) + 3;
|
|
|
|
if ((packet->bitstream[mark_index].duration > MIN_ONE_MARK_IN_us) && (packet->bitstream[mark_index].duration < MAX_ONE_MARK_IN_us) &&
|
|
(packet->bitstream[space_index].duration > MIN_ONE_SPACE_IN_us) && (packet->bitstream[space_index].duration < MAX_ONE_SPACE_IN_us))
|
|
{
|
|
// One
|
|
data |= ((uint32_t)1 << n);
|
|
}
|
|
else if ((packet->bitstream[mark_index].duration > MIN_ZERO_MARK_IN_us) && (packet->bitstream[mark_index].duration < MAX_ZERO_MARK_IN_us) &&
|
|
(packet->bitstream[space_index].duration > MIN_ZERO_SPACE_IN_us) && (packet->bitstream[space_index].duration < MAX_ZERO_SPACE_IN_us))
|
|
{
|
|
// Zero--nothing to do.
|
|
}
|
|
else
|
|
{
|
|
// This mark is neither a zero or a one; abort.
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// PulseDurations[66] is the end-of-message mark. It can also be ignored.
|
|
DecodedPacket_T *Command_Rx_Buffer = Get_Command_Packet_Buffer();
|
|
Command_Rx_Buffer->Command.type = DECODED_PACKET_TYPE_COMMAND_RECEIVED;
|
|
Command_Rx_Buffer->Command.protocol = NEC_PROTOCOL;
|
|
Command_Rx_Buffer->Command.data = data;
|
|
return Command_Rx_Buffer;
|
|
}
|
|
}
|
|
|
|
color_t NEC_GetTeamColor(uint8_t team_ID)
|
|
{
|
|
(void)team_ID;
|
|
|
|
return COLOR_BLACK;
|
|
}
|