Compare commits
16 Commits
v0.0.1
...
8cd866d16d
| Author | SHA1 | Date | |
|---|---|---|---|
| 8cd866d16d | |||
| 38d91e6c30 | |||
| 88caab56a2 | |||
| d3325b71cd | |||
| 3d315be27f | |||
| 71dbf1f4ca | |||
| 7f7e02a8b7 | |||
| 746d0fad6f | |||
| 4460224202 | |||
| a7db18d8ac | |||
| 52d03a1ac9 | |||
| 07f46ecfe7 | |||
| 90e101c47e | |||
| e488cf86d4 | |||
| 9f9dfbfff2 | |||
| 89dd174543 |
25
.gitea/workflows/build.yaml
Normal file
25
.gitea/workflows/build.yaml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: PlatformIO CI
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: catthehacker/ubuntu:act-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/pip
|
||||||
|
~/.platformio/.cache
|
||||||
|
key: ${{ runner.os }}-pio
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.12'
|
||||||
|
- name: Install PlatformIO Core
|
||||||
|
run: pip install --upgrade platformio
|
||||||
|
|
||||||
|
- name: Build PlatformIO Project
|
||||||
|
run: pio test --environment native
|
||||||
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
|
// for the documentation about the extensions.json format
|
||||||
|
"recommendations": [
|
||||||
|
"platformio.platformio-ide"
|
||||||
|
],
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
"ms-vscode.cpptools-extension-pack"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
# RFPowerView
|
# RFPowerView
|
||||||
|
|
||||||
|
A library for receiving and sending PowerView packets via an nRF24L01 module
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef BUFFERFILLER_H
|
#ifndef BUFFERFILLER_H
|
||||||
#define BUFFERFILLER_H
|
#define BUFFERFILLER_H
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <stdint.h>
|
||||||
#include "PacketCRC.h"
|
#include "PacketCRC.h"
|
||||||
#include "PacketTypes.h"
|
#include "PacketTypes.h"
|
||||||
|
|
||||||
@@ -19,11 +19,11 @@ private:
|
|||||||
|
|
||||||
void setPacketSize(uint8_t *buffer, uint8_t);
|
void setPacketSize(uint8_t *buffer, uint8_t);
|
||||||
void setConstants(uint8_t *buffer);
|
void setConstants(uint8_t *buffer);
|
||||||
void setSourceAddress(uint8_t *buffer, uint16_t);
|
void setSourceAddress(uint8_t *buffer, uint8_t offset, uint16_t source);
|
||||||
void setDestinationAddress(uint8_t *buffer, uint16_t);
|
void setDestinationAddress(uint8_t *buffer, uint8_t offset, uint16_t destination);
|
||||||
void setRollingCodes(uint8_t *buffer, uint8_t rollingCode1, uint8_t rollingCode2);
|
void setRollingCodes(uint8_t *buffer, uint8_t rollingCode1, uint8_t rollingCode2);
|
||||||
void setProtocolVersion(uint8_t *buffer, uint8_t);
|
void setProtocolVersion(uint8_t *buffer, uint8_t);
|
||||||
void setFieldsData(uint8_t *buffer, const FieldsParameters& parameters);
|
void setFieldsData(uint8_t *buffer, uint8_t offset, const FieldsParameters& parameters);
|
||||||
void calculateCRC(uint8_t *buffer);
|
void calculateCRC(uint8_t *buffer);
|
||||||
void incrementRollingCodes();
|
void incrementRollingCodes();
|
||||||
uint8_t calculateTotalFieldSize(const FieldsParameters& parameters);
|
uint8_t calculateTotalFieldSize(const FieldsParameters& parameters);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef PACKET_PARSER_H
|
#ifndef PACKET_PARSER_H
|
||||||
#define PACKET_PARSER_H
|
#define PACKET_PARSER_H
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <vector>
|
||||||
#include "PacketTypes.h"
|
#include "PacketTypes.h"
|
||||||
|
|
||||||
class PacketParser {
|
class PacketParser {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef PACKET_TYPES_H
|
#ifndef PACKET_TYPES_H
|
||||||
#define PACKET_TYPES_H
|
#define PACKET_TYPES_H
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <stdint.h>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
// Define packet types
|
// Define packet types
|
||||||
@@ -9,6 +9,9 @@ enum class PacketType {
|
|||||||
OPEN,
|
OPEN,
|
||||||
CLOSE,
|
CLOSE,
|
||||||
STOP,
|
STOP,
|
||||||
|
OPEN_SLOW,
|
||||||
|
CLOSE_SLOW,
|
||||||
|
MOVE_TO_SAVED_POSITION,
|
||||||
FIELDS,
|
FIELDS,
|
||||||
FIELD_COMMAND,
|
FIELD_COMMAND,
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
@@ -34,11 +37,25 @@ struct FieldsParameters {
|
|||||||
|
|
||||||
using PacketParameters = std::variant<std::monostate, FieldsParameters>;
|
using PacketParameters = std::variant<std::monostate, FieldsParameters>;
|
||||||
|
|
||||||
|
struct BroadcastHeader {
|
||||||
|
uint16_t source;
|
||||||
|
};
|
||||||
|
|
||||||
// Define Message structure
|
struct UnicastHeader {
|
||||||
struct Packet {
|
|
||||||
uint16_t source;
|
uint16_t source;
|
||||||
uint16_t destination;
|
uint16_t destination;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GroupsHeader {
|
||||||
|
uint16_t source;
|
||||||
|
std::vector<uint8_t> groups;
|
||||||
|
};
|
||||||
|
|
||||||
|
using PacketHeader = std::variant<BroadcastHeader, UnicastHeader, GroupsHeader>;
|
||||||
|
|
||||||
|
// Define Packet structure
|
||||||
|
struct Packet {
|
||||||
|
PacketHeader header;
|
||||||
PacketType type;
|
PacketType type;
|
||||||
PacketParameters parameters;
|
PacketParameters parameters;
|
||||||
uint8_t rollingCode1;
|
uint8_t rollingCode1;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef RFPOWERVIEW_H
|
#ifndef RFPOWERVIEW_H
|
||||||
#define RFPOWERVIEW_H
|
#define RFPOWERVIEW_H
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <stdint.h>
|
||||||
#include <RF24.h>
|
#include <RF24.h>
|
||||||
#include "PacketReceiver.h"
|
#include "PacketReceiver.h"
|
||||||
#include "PacketParser.h"
|
#include "PacketParser.h"
|
||||||
@@ -23,7 +23,10 @@ public:
|
|||||||
bool begin();
|
bool begin();
|
||||||
void loop();
|
void loop();
|
||||||
|
|
||||||
void setPacketCallback(std::function<void(const Packet*)> callback);
|
void setPacketReceivedCallback(std::function<void(const Packet*)> callback);
|
||||||
|
void setValidBufferReceivedCallback(std::function<void(const uint8_t*)> callback);
|
||||||
|
void setInvalidBufferReceivedCallback(std::function<void(const uint8_t*)> callback);
|
||||||
|
|
||||||
bool sendPacket(const Packet* packet);
|
bool sendPacket(const Packet* packet);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -34,7 +37,9 @@ private:
|
|||||||
uint8_t irqPin;
|
uint8_t irqPin;
|
||||||
uint8_t rfID[2];
|
uint8_t rfID[2];
|
||||||
|
|
||||||
std::function<void(const Packet*)> packetCallback;
|
std::function<void(const Packet*)> packetReceivedCallback;
|
||||||
|
std::function<void(const uint8_t*)> validBufferReceivedCallback;
|
||||||
|
std::function<void(const uint8_t*)> invalidBufferReceivedCallback;
|
||||||
|
|
||||||
uint8_t sendBuffer[32];
|
uint8_t sendBuffer[32];
|
||||||
|
|
||||||
|
|||||||
19
library.json
19
library.json
@@ -1,14 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "RFPowerView",
|
"name": "RFPowerView",
|
||||||
"version": "0.0.1",
|
"version": "0.0.3",
|
||||||
"description": "A library for receiving and sending PowerView packets via an nRF24L01 module",
|
"description": "A library for receiving and sending PowerView packets via an nRF24L01 module",
|
||||||
"keywords": "powerview, hunterdouglas, luxaflex, rf, radio",
|
"keywords": "powerview, hunterdouglas, luxaflex, rf, radio",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.mattway.com.au/matt/RFPowerView.git"
|
"url": "https://github.com/mattyway/RFPowerView.git"
|
||||||
},
|
},
|
||||||
"authors":
|
"authors": [
|
||||||
[
|
|
||||||
{
|
{
|
||||||
"name": "Matt Way",
|
"name": "Matt Way",
|
||||||
"email": "mattyway@gmail.com"
|
"email": "mattyway@gmail.com"
|
||||||
@@ -16,12 +15,10 @@
|
|||||||
],
|
],
|
||||||
"license": "GPL-2.0-only",
|
"license": "GPL-2.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"robtillaart/CRC": "^1.0.2",
|
"robtillaart/CRC": "1.0.2",
|
||||||
"nrf24/RF24": "^1.4.8",
|
"nrf24/RF24": "1.4.8",
|
||||||
"rlogiacco/CircularBuffer": "^1.3.3"
|
"rlogiacco/CircularBuffer": "1.3.3"
|
||||||
},
|
},
|
||||||
"frameworks": "arduino",
|
"frameworks": "arduino",
|
||||||
"platforms": [
|
"platforms": ["espressif8266"]
|
||||||
"espressif8266"
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|||||||
33
platformio.ini
Normal file
33
platformio.ini
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:nodemcuv2]
|
||||||
|
platform = espressif8266
|
||||||
|
board = nodemcuv2
|
||||||
|
framework = arduino
|
||||||
|
|
||||||
|
[env]
|
||||||
|
monitor_speed = 115200
|
||||||
|
test_build_src = true
|
||||||
|
test_framework = unity
|
||||||
|
lib_deps =
|
||||||
|
robtillaart/CRC @ 1.0.2
|
||||||
|
nrf24/RF24 @ 1.4.8
|
||||||
|
rlogiacco/CircularBuffer @ 1.3.3
|
||||||
|
|
||||||
|
[env:native]
|
||||||
|
platform = native
|
||||||
|
lib_deps =
|
||||||
|
${env.lib_deps}
|
||||||
|
ArduinoFake
|
||||||
|
build_src_filter =
|
||||||
|
+<**/*.cpp>
|
||||||
|
-<**/RFPowerView.cpp>
|
||||||
|
-<**/PacketReceiver.cpp>
|
||||||
@@ -11,45 +11,86 @@ BufferFiller::~BufferFiller()
|
|||||||
|
|
||||||
bool BufferFiller::fill(uint8_t *buffer, const Packet* packet) {
|
bool BufferFiller::fill(uint8_t *buffer, const Packet* packet) {
|
||||||
setConstants(buffer);
|
setConstants(buffer);
|
||||||
setProtocolVersion(buffer, protocolVersion);
|
|
||||||
setSourceAddress(buffer, packet->source);
|
int dataOffset = -1;
|
||||||
setDestinationAddress(buffer, packet->destination);
|
|
||||||
|
if (std::holds_alternative<BroadcastHeader>(packet->header)) {
|
||||||
|
setProtocolVersion(buffer, 0x04);
|
||||||
|
auto header = std::get<BroadcastHeader>(packet->header);
|
||||||
|
setSourceAddress(buffer, 12, header.source);
|
||||||
|
dataOffset = 14;
|
||||||
|
} else if (std::holds_alternative<UnicastHeader>(packet->header)) {
|
||||||
|
setProtocolVersion(buffer, 0x05);
|
||||||
|
auto header = std::get<UnicastHeader>(packet->header);
|
||||||
|
setDestinationAddress(buffer, 12, header.destination);
|
||||||
|
setSourceAddress(buffer, 14, header.source);
|
||||||
|
dataOffset = 16;
|
||||||
|
} else if (std::holds_alternative<GroupsHeader>(packet->header)) {
|
||||||
|
setProtocolVersion(buffer, 0x06);
|
||||||
|
auto header = std::get<GroupsHeader>(packet->header);
|
||||||
|
int groupsOffset = 12;
|
||||||
|
for (size_t i = 0; i < header.groups.size(); i++) {
|
||||||
|
buffer[groupsOffset] = header.groups[i];
|
||||||
|
groupsOffset++;
|
||||||
|
}
|
||||||
|
buffer[groupsOffset] = 0x00;
|
||||||
|
setSourceAddress(buffer, groupsOffset + 1, header.source);
|
||||||
|
dataOffset = groupsOffset + 3;
|
||||||
|
}
|
||||||
|
|
||||||
switch(packet->type) {
|
switch(packet->type) {
|
||||||
case PacketType::STOP:
|
case PacketType::STOP:
|
||||||
setPacketSize(buffer, 0x11);
|
setPacketSize(buffer, 0x11);
|
||||||
buffer[16] = 0x52;
|
buffer[dataOffset + 0] = 0x52;
|
||||||
buffer[17] = 0x53;
|
buffer[dataOffset + 1] = 0x53;
|
||||||
buffer[18] = 0x00;
|
buffer[dataOffset + 2] = 0x00;
|
||||||
break;
|
break;
|
||||||
case PacketType::CLOSE:
|
case PacketType::CLOSE:
|
||||||
setPacketSize(buffer, 0x11);
|
setPacketSize(buffer, 0x11);
|
||||||
buffer[16] = 0x52;
|
buffer[dataOffset + 0] = 0x52;
|
||||||
buffer[17] = 0x44;
|
buffer[dataOffset + 1] = 0x44;
|
||||||
buffer[18] = 0x00;
|
buffer[dataOffset + 2] = 0x00;
|
||||||
break;
|
break;
|
||||||
case PacketType::OPEN:
|
case PacketType::OPEN:
|
||||||
setPacketSize(buffer, 0x11);
|
setPacketSize(buffer, 0x11);
|
||||||
buffer[16] = 0x52;
|
buffer[dataOffset + 0] = 0x52;
|
||||||
buffer[17] = 0x55;
|
buffer[dataOffset + 1] = 0x55;
|
||||||
buffer[18] = 0x00;
|
buffer[dataOffset + 2] = 0x00;
|
||||||
|
break;
|
||||||
|
case PacketType::CLOSE_SLOW:
|
||||||
|
setPacketSize(buffer, 0x11);
|
||||||
|
buffer[dataOffset + 0] = 0x52;
|
||||||
|
buffer[dataOffset + 1] = 0x4C;
|
||||||
|
buffer[dataOffset + 2] = 0x00;
|
||||||
|
break;
|
||||||
|
case PacketType::OPEN_SLOW:
|
||||||
|
setPacketSize(buffer, 0x11);
|
||||||
|
buffer[dataOffset + 0] = 0x52;
|
||||||
|
buffer[dataOffset + 1] = 0x52;
|
||||||
|
buffer[dataOffset + 2] = 0x00;
|
||||||
|
break;
|
||||||
|
case PacketType::MOVE_TO_SAVED_POSITION:
|
||||||
|
setPacketSize(buffer, 0x11);
|
||||||
|
buffer[dataOffset + 0] = 0x52;
|
||||||
|
buffer[dataOffset + 1] = 0x48;
|
||||||
|
buffer[dataOffset + 2] = 0x00;
|
||||||
break;
|
break;
|
||||||
case PacketType::FIELDS: {
|
case PacketType::FIELDS: {
|
||||||
FieldsParameters parameters = std::get<FieldsParameters>(packet->parameters);
|
FieldsParameters parameters = std::get<FieldsParameters>(packet->parameters);
|
||||||
// 0x10 is the number of bytes without any fields
|
// 0x10 is the number of bytes without any fields
|
||||||
setPacketSize(buffer, 0x10 + calculateTotalFieldSize(parameters));
|
setPacketSize(buffer, 0x10 + calculateTotalFieldSize(parameters));
|
||||||
buffer[16] = 0x21;
|
buffer[dataOffset + 0] = 0x21;
|
||||||
buffer[17] = 0x5A;
|
buffer[dataOffset + 1] = 0x5A;
|
||||||
setFieldsData(buffer, parameters);
|
setFieldsData(buffer, dataOffset + 2, parameters);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PacketType::FIELD_COMMAND: {
|
case PacketType::FIELD_COMMAND: {
|
||||||
FieldsParameters parameters = std::get<FieldsParameters>(packet->parameters);
|
FieldsParameters parameters = std::get<FieldsParameters>(packet->parameters);
|
||||||
// 0x10 is the number of bytes without any fields
|
// 0x10 is the number of bytes without any fields
|
||||||
setPacketSize(buffer, 0x10 + calculateTotalFieldSize(parameters));
|
setPacketSize(buffer, 0x10 + calculateTotalFieldSize(parameters));
|
||||||
buffer[16] = 0x3F;
|
buffer[dataOffset + 0] = 0x3F;
|
||||||
buffer[17] = 0x5A;
|
buffer[dataOffset + 1] = 0x5A;
|
||||||
setFieldsData(buffer, std::get<FieldsParameters>(packet->parameters));
|
setFieldsData(buffer, dataOffset + 2, parameters);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -62,9 +103,7 @@ bool BufferFiller::fill(uint8_t *buffer, const Packet* packet) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferFiller::setFieldsData(uint8_t *buffer, const FieldsParameters& parameters) {
|
void BufferFiller::setFieldsData(uint8_t *buffer, uint8_t offset, const FieldsParameters& parameters) {
|
||||||
uint8_t offset = 18;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < parameters.fields.size(); i++) {
|
for (size_t i = 0; i < parameters.fields.size(); i++) {
|
||||||
Field field = parameters.fields[i];
|
Field field = parameters.fields[i];
|
||||||
uint8_t fieldSize = calculateFieldSize(field);
|
uint8_t fieldSize = calculateFieldSize(field);
|
||||||
@@ -113,20 +152,20 @@ void BufferFiller::setConstants(uint8_t *buffer) {
|
|||||||
buffer[9] = 0x86; // Constant?
|
buffer[9] = 0x86; // Constant?
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferFiller::setSourceAddress(uint8_t *buffer, uint16_t sourceID) {
|
void BufferFiller::setSourceAddress(uint8_t *buffer, uint8_t offset, uint16_t sourceID) {
|
||||||
// Physical source address (could be the address of a repeater when receiving a packet)
|
// Physical source address (could be the address of a repeater when receiving a packet)
|
||||||
buffer[7] = (uint8_t)((sourceID & 0xFF00) >> 8);
|
buffer[7] = (uint8_t)((sourceID & 0xFF00) >> 8);
|
||||||
buffer[8] = (uint8_t)(sourceID & 0x00FF);
|
buffer[8] = (uint8_t)(sourceID & 0x00FF);
|
||||||
|
|
||||||
// Logical source address (usually the same as the physical source address)
|
// Logical source address (usually the same as the physical source address)
|
||||||
buffer[14] = (uint8_t)((sourceID & 0xFF00) >> 8);
|
buffer[offset + 0] = (uint8_t)((sourceID & 0xFF00) >> 8);
|
||||||
buffer[15] = (uint8_t)(sourceID & 0x00FF);
|
buffer[offset + 1] = (uint8_t)(sourceID & 0x00FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferFiller::setDestinationAddress(uint8_t *buffer, uint16_t targetID) {
|
void BufferFiller::setDestinationAddress(uint8_t *buffer, uint8_t offset, uint16_t targetID) {
|
||||||
// Logical target address
|
// Logical target address
|
||||||
buffer[12] = (uint8_t)((targetID & 0xFF00) >> 8);
|
buffer[offset + 0] = (uint8_t)((targetID & 0xFF00) >> 8);
|
||||||
buffer[13] = (uint8_t)(targetID & 0x00FF);
|
buffer[offset + 1] = (uint8_t)(targetID & 0x00FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferFiller::setRollingCodes(uint8_t *buffer, uint8_t rollingCode1, uint8_t rollingCode2) {
|
void BufferFiller::setRollingCodes(uint8_t *buffer, uint8_t rollingCode1, uint8_t rollingCode2) {
|
||||||
|
|||||||
@@ -10,27 +10,65 @@ PacketParser::~PacketParser()
|
|||||||
|
|
||||||
bool PacketParser::parsePacket(const uint8_t *buffer, Packet& packet)
|
bool PacketParser::parsePacket(const uint8_t *buffer, Packet& packet)
|
||||||
{
|
{
|
||||||
packet.source = (uint16_t)(buffer[14] << 8 | buffer[15]);
|
int dataOffset = -1;
|
||||||
packet.destination = (uint16_t)(buffer[12] << 8 | buffer[13]);
|
if (buffer[10] == 0x04) {
|
||||||
|
BroadcastHeader header{};
|
||||||
|
header.source = (uint16_t)(buffer[12] << 8 | buffer[13]);
|
||||||
|
packet.header = header;
|
||||||
|
dataOffset = 14;
|
||||||
|
} else if(buffer[10] == 0x05) {
|
||||||
|
UnicastHeader header{};
|
||||||
|
header.destination = (uint16_t)(buffer[12] << 8 | buffer[13]);
|
||||||
|
header.source = (uint16_t)(buffer[14] << 8 | buffer[15]);
|
||||||
|
packet.header = header;
|
||||||
|
dataOffset = 16;
|
||||||
|
} else if (buffer[10] == 0x06) {
|
||||||
|
GroupsHeader header{};
|
||||||
|
size_t i = 12;
|
||||||
|
uint8_t length = buffer[1] + 2;
|
||||||
|
while (i < length) {
|
||||||
|
if (buffer[i] == 0x00) {
|
||||||
|
dataOffset = i + 3;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
header.groups.push_back(buffer[i]);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i == length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
header.source = (uint16_t)(buffer[i + 1] << 8 | buffer[i + 2]);
|
||||||
|
packet.header = header;
|
||||||
|
}
|
||||||
|
|
||||||
packet.rollingCode1 = buffer[4];
|
packet.rollingCode1 = buffer[4];
|
||||||
packet.rollingCode2 = buffer[11];
|
packet.rollingCode2 = buffer[11];
|
||||||
|
|
||||||
if (buffer[16] == 0x52 && buffer[17] == 0x53) {
|
if (buffer[dataOffset + 0] == 0x52 && buffer[dataOffset + 1] == 0x53) {
|
||||||
packet.type = PacketType::STOP;
|
packet.type = PacketType::STOP;
|
||||||
packet.parameters = std::monostate{};
|
packet.parameters = std::monostate{};
|
||||||
} else if (buffer[16] == 0x52 && buffer[17] == 0x44) {
|
} else if (buffer[dataOffset + 0] == 0x52 && buffer[dataOffset + 1] == 0x44) {
|
||||||
packet.type = PacketType::CLOSE;
|
packet.type = PacketType::CLOSE;
|
||||||
packet.parameters = std::monostate{};
|
packet.parameters = std::monostate{};
|
||||||
} else if (buffer[16] == 0x52 && buffer[17] == 0x55) {
|
} else if (buffer[dataOffset + 0] == 0x52 && buffer[dataOffset + 1] == 0x55) {
|
||||||
packet.type = PacketType::OPEN;
|
packet.type = PacketType::OPEN;
|
||||||
packet.parameters = std::monostate{};
|
packet.parameters = std::monostate{};
|
||||||
} else if (buffer[16] == 0x21 && buffer[17] == 0x5A) {
|
} else if (buffer[dataOffset + 0] == 0x52 && buffer[dataOffset + 1] == 0x4C) {
|
||||||
|
packet.type = PacketType::CLOSE_SLOW;
|
||||||
|
packet.parameters = std::monostate{};
|
||||||
|
} else if (buffer[dataOffset + 0] == 0x52 && buffer[dataOffset + 1] == 0x52) {
|
||||||
|
packet.type = PacketType::OPEN_SLOW;
|
||||||
|
packet.parameters = std::monostate{};
|
||||||
|
} else if (buffer[dataOffset + 0] == 0x52 && buffer[dataOffset + 1] == 0x48) {
|
||||||
|
packet.type = PacketType::MOVE_TO_SAVED_POSITION;
|
||||||
|
packet.parameters = std::monostate{};
|
||||||
|
} else if (buffer[dataOffset + 0] == 0x21 && buffer[dataOffset + 1] == 0x5A) {
|
||||||
packet.type = PacketType::FIELDS;
|
packet.type = PacketType::FIELDS;
|
||||||
std::vector<Field> fields;
|
std::vector<Field> fields;
|
||||||
parseFields(buffer, fields);
|
parseFields(buffer, fields);
|
||||||
packet.parameters = FieldsParameters{fields};
|
packet.parameters = FieldsParameters{fields};
|
||||||
} else if (buffer[16] == 0x3F && buffer[17] == 0x5A) {
|
} else if (buffer[dataOffset + 0] == 0x3F && buffer[dataOffset + 1] == 0x5A) {
|
||||||
packet.type = PacketType::FIELD_COMMAND;
|
packet.type = PacketType::FIELD_COMMAND;
|
||||||
std::vector<Field> fields;
|
std::vector<Field> fields;
|
||||||
parseFields(buffer, fields);
|
parseFields(buffer, fields);
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ RFPowerView::RFPowerView(uint8_t cePin, uint8_t csPin, uint8_t irqPin, uint16_t
|
|||||||
bufferFiller(0x05),
|
bufferFiller(0x05),
|
||||||
irqPin(irqPin),
|
irqPin(irqPin),
|
||||||
rfID{static_cast<uint8_t>(rfID & 0xFF), static_cast<uint8_t>(rfID >> 8)},
|
rfID{static_cast<uint8_t>(rfID & 0xFF), static_cast<uint8_t>(rfID >> 8)},
|
||||||
packetCallback(nullptr) {}
|
packetReceivedCallback(nullptr),
|
||||||
|
validBufferReceivedCallback(nullptr),
|
||||||
|
invalidBufferReceivedCallback(nullptr) {}
|
||||||
|
|
||||||
bool RFPowerView::begin() {
|
bool RFPowerView::begin() {
|
||||||
if (!radio.begin()) {
|
if (!radio.begin()) {
|
||||||
@@ -60,21 +62,35 @@ void RFPowerView::interruptHandler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RFPowerView::processBuffer(const uint8_t *buffer) {
|
void RFPowerView::processBuffer(const uint8_t *buffer) {
|
||||||
|
if (validBufferReceivedCallback != nullptr) {
|
||||||
|
validBufferReceivedCallback(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
Packet packet;
|
Packet packet;
|
||||||
bool result = packetParser.parsePacket(buffer, packet);
|
bool result = packetParser.parsePacket(buffer, packet);
|
||||||
if (result) {
|
if (result) {
|
||||||
if (packetCallback != nullptr) {
|
if (packetReceivedCallback != nullptr) {
|
||||||
packetCallback(&packet);
|
packetReceivedCallback(&packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RFPowerView::processInvalidBuffer(const uint8_t *buffer) {
|
void RFPowerView::processInvalidBuffer(const uint8_t *buffer) {
|
||||||
|
if (invalidBufferReceivedCallback != nullptr) {
|
||||||
|
invalidBufferReceivedCallback(buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RFPowerView::setPacketCallback(std::function<void(const Packet*)> callback) {
|
void RFPowerView::setPacketReceivedCallback(std::function<void(const Packet*)> callback) {
|
||||||
packetCallback = callback;
|
packetReceivedCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RFPowerView::setValidBufferReceivedCallback(std::function<void(const uint8_t*)> callback) {
|
||||||
|
validBufferReceivedCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RFPowerView::setInvalidBufferReceivedCallback(std::function<void(const uint8_t*)> callback) {
|
||||||
|
invalidBufferReceivedCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RFPowerView::sendPacket(const Packet* packet) {
|
bool RFPowerView::sendPacket(const Packet* packet) {
|
||||||
|
|||||||
98
test/test_crc/test_crc.cpp
Normal file
98
test/test_crc/test_crc.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#include <unity.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "PacketCRC.h"
|
||||||
|
|
||||||
|
void setUp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_calculate_test(const uint8_t *packet_data, const uint16_t expected_checksum)
|
||||||
|
{
|
||||||
|
PacketCRC packetCRC;
|
||||||
|
|
||||||
|
uint16_t actual_checksum = packetCRC.calculate(packet_data);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_UINT16(expected_checksum, actual_checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_calculate_withDataLengthOf15()
|
||||||
|
{
|
||||||
|
const uint8_t packet_data[] = {0xC0, 0x0F, 0x00, 0x05, 0xA1, 0xFF, 0xFF, 0x00, 0x00, 0x86, 0x04, 0xFF, 0x00, 0x00, 0x53, 0x47, 0x1B};
|
||||||
|
const uint16_t expected_checksum = 0x446B;
|
||||||
|
|
||||||
|
run_calculate_test(packet_data, expected_checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_calculate_withDataLengthOf17()
|
||||||
|
{
|
||||||
|
const uint8_t packet_data[] = {0xC0, 0x11, 0x00, 0x05, 0xD7, 0xFF, 0xFF, 0x36, 0x9E, 0x86, 0x06, 0xB3, 0x04, 0x00, 0x36, 0x9E, 0x52, 0x53, 0x00};
|
||||||
|
const uint16_t expected_checksum = 0xE04C;
|
||||||
|
|
||||||
|
run_calculate_test(packet_data, expected_checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_calculate_withDataLengthOf19()
|
||||||
|
{
|
||||||
|
const uint8_t packet_data[] = {0xC0, 0x13, 0x00, 0x05, 0xDD, 0xFF, 0xFF, 0x00, 0x00, 0x86, 0x05, 0x36, 0x4E, 0xF1, 0x00, 0x00, 0x3F, 0x5A, 0x02, 0x3F, 0x42};
|
||||||
|
const uint16_t expected_checksum = 0x9BD3;
|
||||||
|
|
||||||
|
run_calculate_test(packet_data, expected_checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_calculate_withDataLengthOf20()
|
||||||
|
{
|
||||||
|
const uint8_t packet_data[] = {0xC0, 0x14, 0x10, 0x05, 0x59, 0xFF, 0xFF, 0x4E, 0xF1, 0x86, 0x05, 0xC2, 0x00, 0x00, 0x4E, 0xF1, 0x21, 0x5A, 0x03, 0x21, 0x42, 0x9C};
|
||||||
|
const uint16_t expected_checksum = 0x4A8B;
|
||||||
|
|
||||||
|
run_calculate_test(packet_data, expected_checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_calculate_withDataLengthOf21()
|
||||||
|
{
|
||||||
|
const uint8_t packet_data[] = {0xC0, 0x15, 0x10, 0x05, 0xEA, 0xFF, 0xFF, 0x4E, 0xF1, 0x86, 0x05, 0x24, 0x00, 0x00, 0x4E, 0xF1, 0x21, 0x46, 0x4E, 0x98, 0x07, 0x08, 0x01};
|
||||||
|
const uint16_t expected_checksum = 0x9887;
|
||||||
|
|
||||||
|
run_calculate_test(packet_data, expected_checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_calculate_withDataLengthOf25()
|
||||||
|
{
|
||||||
|
const uint8_t packet_data[] = {0xC0, 0x19, 0x00, 0x05, 0x47, 0xFF, 0xFF, 0x00, 0x00, 0x86, 0x05, 0x3D, 0x4E, 0xF1, 0x00, 0x00, 0x3F, 0x5A, 0x02, 0x3F, 0x50, 0x02, 0x3F, 0x4D, 0x02, 0x3F, 0x54};
|
||||||
|
const uint16_t expected_checksum = 0x8318;
|
||||||
|
|
||||||
|
run_calculate_test(packet_data, expected_checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
int runUnityTests(void)
|
||||||
|
{
|
||||||
|
UNITY_BEGIN();
|
||||||
|
RUN_TEST(test_calculate_withDataLengthOf15);
|
||||||
|
RUN_TEST(test_calculate_withDataLengthOf17);
|
||||||
|
RUN_TEST(test_calculate_withDataLengthOf19);
|
||||||
|
RUN_TEST(test_calculate_withDataLengthOf20);
|
||||||
|
RUN_TEST(test_calculate_withDataLengthOf21);
|
||||||
|
RUN_TEST(test_calculate_withDataLengthOf25);
|
||||||
|
return UNITY_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For Arduino framework
|
||||||
|
*/
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
// Wait ~2 seconds before the Unity test runner
|
||||||
|
// establishes connection with a board Serial interface
|
||||||
|
delay(2000);
|
||||||
|
|
||||||
|
runUnityTests();
|
||||||
|
}
|
||||||
|
void loop() {}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
return runUnityTests();
|
||||||
|
}
|
||||||
81
test/test_packet_parser/test_packet_parser.cpp
Normal file
81
test/test_packet_parser/test_packet_parser.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include <unity.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "PacketParser.h"
|
||||||
|
|
||||||
|
void setUp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_parse_test(const uint8_t *packet_data, Packet &packet)
|
||||||
|
{
|
||||||
|
PacketParser packetParser;
|
||||||
|
|
||||||
|
bool result = packetParser.parsePacket(packet_data, packet);
|
||||||
|
|
||||||
|
TEST_ASSERT_TRUE(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_parse_stop()
|
||||||
|
{
|
||||||
|
const uint8_t packet_data[] = {0xC0, 0x11, 0x00, 0x05, 0x6C, 0xFF, 0xFF, 0x36, 0x9E, 0x86, 0x06, 0x3C, 0x04, 0x00, 0x36, 0x9E, 0x52, 0x53, 0x00};
|
||||||
|
|
||||||
|
Packet packet;
|
||||||
|
|
||||||
|
run_parse_test(packet_data, packet);
|
||||||
|
|
||||||
|
TEST_ASSERT_TRUE(packet.type == PacketType::STOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_parse_close()
|
||||||
|
{
|
||||||
|
const uint8_t packet_data[] = {0xC0, 0x11, 0x00, 0x05, 0x6C, 0xFF, 0xFF, 0x36, 0x9E, 0x86, 0x06, 0x3C, 0x04, 0x00, 0x36, 0x9E, 0x52, 0x44, 0x00};
|
||||||
|
|
||||||
|
Packet packet;
|
||||||
|
|
||||||
|
run_parse_test(packet_data, packet);
|
||||||
|
|
||||||
|
TEST_ASSERT_TRUE(packet.type == PacketType::CLOSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_parse_open()
|
||||||
|
{
|
||||||
|
const uint8_t packet_data[] = {0xC0, 0x11, 0x00, 0x05, 0x6C, 0xFF, 0xFF, 0x36, 0x9E, 0x86, 0x06, 0x3C, 0x04, 0x00, 0x36, 0x9E, 0x52, 0x55, 0x00};
|
||||||
|
|
||||||
|
Packet packet;
|
||||||
|
|
||||||
|
run_parse_test(packet_data, packet);
|
||||||
|
|
||||||
|
TEST_ASSERT_TRUE(packet.type == PacketType::OPEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
int runUnityTests(void)
|
||||||
|
{
|
||||||
|
UNITY_BEGIN();
|
||||||
|
RUN_TEST(test_parse_stop);
|
||||||
|
RUN_TEST(test_parse_close);
|
||||||
|
RUN_TEST(test_parse_open);
|
||||||
|
return UNITY_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For Arduino framework
|
||||||
|
*/
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
// Wait ~2 seconds before the Unity test runner
|
||||||
|
// establishes connection with a board Serial interface
|
||||||
|
delay(2000);
|
||||||
|
|
||||||
|
runUnityTests();
|
||||||
|
}
|
||||||
|
void loop() {}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
return runUnityTests();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user