Migrate Hotdog to RFPowerView
This commit is contained in:
@@ -1,36 +0,0 @@
|
|||||||
#ifndef PACKETBUILDER_H
|
|
||||||
#define PACKETBUILDER_H
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <PacketCRC.h>
|
|
||||||
|
|
||||||
class PacketBuilder {
|
|
||||||
public:
|
|
||||||
PacketBuilder(uint16_t address, uint8_t rollingCode1, uint8_t rollingCode2, uint8_t protocolVersion);
|
|
||||||
~PacketBuilder();
|
|
||||||
|
|
||||||
void buildUpPacket(uint8_t *buffer, uint16_t shadeID);
|
|
||||||
void buildDownPacket(uint8_t *buffer, uint16_t shadeID);
|
|
||||||
void buildStopPacket(uint8_t *buffer, uint16_t shadeID);
|
|
||||||
void buildSetPositionPacket(uint8_t *buffer, uint16_t shadeID, float percentage);
|
|
||||||
void buildFetchPositionPacket(uint8_t *buffer, uint16_t shadeID);
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint16_t address;
|
|
||||||
uint8_t rollingCode1;
|
|
||||||
uint8_t rollingCode2;
|
|
||||||
uint8_t protocolVersion;
|
|
||||||
|
|
||||||
PacketCRC packetCRC;
|
|
||||||
|
|
||||||
void setPacketSize(uint8_t *buffer, uint8_t);
|
|
||||||
void setConstants(uint8_t *buffer);
|
|
||||||
void setSourceAddress(uint8_t *buffer, uint16_t);
|
|
||||||
void setDestinationAddress(uint8_t *buffer, uint16_t);
|
|
||||||
void setRollingCodes(uint8_t *buffer);
|
|
||||||
void setProtocolVersion(uint8_t *buffer, uint8_t);
|
|
||||||
void calculateCRC(uint8_t *buffer);
|
|
||||||
void incrementRollingCodes();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // PACKETBUILDER_H
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "PacketBuilder",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "A tool for building PowerView packets",
|
|
||||||
"keywords": "powerview, hunterdouglas",
|
|
||||||
"authors":
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "Matt Way",
|
|
||||||
"email": "mattyway@gmail.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"PacketCRC": "*"
|
|
||||||
},
|
|
||||||
"frameworks": "arduino"
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
#include "PacketBuilder.h"
|
|
||||||
|
|
||||||
PacketBuilder::PacketBuilder(uint16_t address, uint8_t rollingCode1, uint8_t rollingCode2, uint8_t protocolVersion) :
|
|
||||||
address(address),
|
|
||||||
rollingCode1(rollingCode1),
|
|
||||||
rollingCode2(rollingCode2),
|
|
||||||
protocolVersion(protocolVersion)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
PacketBuilder::~PacketBuilder()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::buildUpPacket(uint8_t *buffer, uint16_t shadeID)
|
|
||||||
{
|
|
||||||
setPacketSize(buffer, 0x11);
|
|
||||||
setConstants(buffer);
|
|
||||||
setProtocolVersion(buffer, protocolVersion);
|
|
||||||
setSourceAddress(buffer, address);
|
|
||||||
setDestinationAddress(buffer, shadeID);
|
|
||||||
setRollingCodes(buffer);
|
|
||||||
|
|
||||||
buffer[16] = 0x52; // ?
|
|
||||||
buffer[17] = 0x55; // Command
|
|
||||||
buffer[18] = 0x00; // Empty?
|
|
||||||
|
|
||||||
calculateCRC(buffer);
|
|
||||||
|
|
||||||
incrementRollingCodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::buildDownPacket(uint8_t *buffer, uint16_t shadeID)
|
|
||||||
{
|
|
||||||
setPacketSize(buffer, 0x11);
|
|
||||||
setConstants(buffer);
|
|
||||||
setProtocolVersion(buffer, protocolVersion);
|
|
||||||
setSourceAddress(buffer, address);
|
|
||||||
setDestinationAddress(buffer, shadeID);
|
|
||||||
setRollingCodes(buffer);
|
|
||||||
|
|
||||||
buffer[16] = 0x52; // ?
|
|
||||||
buffer[17] = 0x44; // Command
|
|
||||||
buffer[18] = 0x00; // Empty?
|
|
||||||
|
|
||||||
calculateCRC(buffer);
|
|
||||||
|
|
||||||
incrementRollingCodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::buildStopPacket(uint8_t *buffer, uint16_t shadeID)
|
|
||||||
{
|
|
||||||
setPacketSize(buffer, 0x11);
|
|
||||||
setConstants(buffer);
|
|
||||||
setProtocolVersion(buffer, protocolVersion);
|
|
||||||
setSourceAddress(buffer, address);
|
|
||||||
setDestinationAddress(buffer, shadeID);
|
|
||||||
setRollingCodes(buffer);
|
|
||||||
|
|
||||||
buffer[16] = 0x52; // ?
|
|
||||||
buffer[17] = 0x53; // Command
|
|
||||||
buffer[18] = 0x00; // Empty?
|
|
||||||
|
|
||||||
calculateCRC(buffer);
|
|
||||||
|
|
||||||
incrementRollingCodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::buildSetPositionPacket(uint8_t *buffer, uint16_t shadeID, float percentage)
|
|
||||||
{
|
|
||||||
setPacketSize(buffer, 0x15);
|
|
||||||
setConstants(buffer);
|
|
||||||
setProtocolVersion(buffer, protocolVersion);
|
|
||||||
setSourceAddress(buffer, address);
|
|
||||||
setDestinationAddress(buffer, shadeID);
|
|
||||||
setRollingCodes(buffer);
|
|
||||||
|
|
||||||
buffer[16] = 0x3F; // Field command
|
|
||||||
buffer[17] = 0x5A; // Constant
|
|
||||||
|
|
||||||
buffer[18] = 0x04; // Field of 4 bytes
|
|
||||||
buffer[19] = 0x40; // Field type (0x40 is set)
|
|
||||||
buffer[20] = 0x50; // ID of position field
|
|
||||||
|
|
||||||
uint16_t position = (uint16_t)(0xFFFF * percentage);
|
|
||||||
|
|
||||||
buffer[21] = (uint8_t)(position & 0x00FF);
|
|
||||||
buffer[22] = (uint8_t)((position & 0xFF00) >> 8);
|
|
||||||
|
|
||||||
calculateCRC(buffer);
|
|
||||||
|
|
||||||
incrementRollingCodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::buildFetchPositionPacket(uint8_t *buffer, uint16_t shadeID) {
|
|
||||||
setPacketSize(buffer, 0x13);
|
|
||||||
setConstants(buffer);
|
|
||||||
setProtocolVersion(buffer, protocolVersion);
|
|
||||||
setSourceAddress(buffer, address);
|
|
||||||
setDestinationAddress(buffer, shadeID);
|
|
||||||
setRollingCodes(buffer);
|
|
||||||
|
|
||||||
buffer[16] = 0x3F; // Field command
|
|
||||||
buffer[17] = 0x5A; // Constant
|
|
||||||
|
|
||||||
buffer[18] = 0x02; // Field of 2 bytes
|
|
||||||
buffer[19] = 0x3F; // Field type (0x3F is fetch)
|
|
||||||
buffer[20] = 0x50; // ID of position field
|
|
||||||
|
|
||||||
calculateCRC(buffer);
|
|
||||||
|
|
||||||
incrementRollingCodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::setPacketSize(uint8_t *buffer, uint8_t length) {
|
|
||||||
buffer[1] = length; // Packet size
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::setConstants(uint8_t *buffer) {
|
|
||||||
buffer[0] = 0xC0; // Header byte
|
|
||||||
|
|
||||||
buffer[2] = 0x00; // Constant when sending, can be 0x10 when receiving a packet
|
|
||||||
buffer[3] = 0x05; // Constant
|
|
||||||
|
|
||||||
buffer[5] = 0xFF; // Constant
|
|
||||||
buffer[6] = 0xFF; // Constant
|
|
||||||
|
|
||||||
buffer[9] = 0x86; // Constant?
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::setSourceAddress(uint8_t *buffer, uint16_t sourceID) {
|
|
||||||
// Physical source address (could be the address of a repeater when receiving a packet)
|
|
||||||
buffer[7] = (uint8_t)((sourceID & 0xFF00) >> 8);
|
|
||||||
buffer[8] = (uint8_t)(sourceID & 0x00FF);
|
|
||||||
|
|
||||||
// Logical source address (usually the same as the physical source address)
|
|
||||||
buffer[14] = (uint8_t)((sourceID & 0xFF00) >> 8);
|
|
||||||
buffer[15] = (uint8_t)(sourceID & 0x00FF);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::setDestinationAddress(uint8_t *buffer, uint16_t targetID) {
|
|
||||||
// Logical target address
|
|
||||||
buffer[12] = (uint8_t)((targetID & 0xFF00) >> 8);
|
|
||||||
buffer[13] = (uint8_t)(targetID & 0x00FF);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::setRollingCodes(uint8_t *buffer) {
|
|
||||||
buffer[4] = rollingCode1; // Rolling code 1
|
|
||||||
buffer[11] = rollingCode2; // Rolling code 2
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::setProtocolVersion(uint8_t *buffer, uint8_t version) {
|
|
||||||
buffer[10] = version; // Protocol version?
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::calculateCRC(uint8_t *buffer) { // must be called after the buffer has been filled
|
|
||||||
uint8_t length = buffer[1];
|
|
||||||
uint16_t result = packetCRC.calculate(buffer);
|
|
||||||
|
|
||||||
uint8_t checksum1 = (uint8_t)((result & 0xFF00) >> 8);
|
|
||||||
uint8_t checksum2 = (uint8_t)(result & 0x00FF);
|
|
||||||
|
|
||||||
buffer[length + 2] = checksum1; // Checksum
|
|
||||||
buffer[length + 3] = checksum2; // Checksum
|
|
||||||
}
|
|
||||||
|
|
||||||
void PacketBuilder::incrementRollingCodes() {
|
|
||||||
rollingCode1++;
|
|
||||||
rollingCode2++;
|
|
||||||
}
|
|
||||||
@@ -24,10 +24,4 @@ lib_deps =
|
|||||||
|
|
||||||
plapointe6/EspMQTTClient@^1.13.3
|
plapointe6/EspMQTTClient@^1.13.3
|
||||||
|
|
||||||
PacketReceiver=symlink://..\PowerViewSniffer\lib\PacketReceiver
|
RFPowerView=https://git.mattway.com.au/matt/RFPowerView.git
|
||||||
|
|
||||||
PacketCRC=symlink://..\PowerViewSniffer\lib\PacketCRC
|
|
||||||
|
|
||||||
PacketBuilder
|
|
||||||
|
|
||||||
PacketParser=symlink://..\PowerViewSniffer\lib\PacketParser
|
|
||||||
|
|||||||
133
src/main.cpp
133
src/main.cpp
@@ -1,29 +1,17 @@
|
|||||||
// sketch to sniff Hunter Douglas packets from a pebble remote or hub
|
// sketch to sniff Hunter Douglas packets from a pebble remote or hub
|
||||||
|
|
||||||
#define CIRCULAR_BUFFER_INT_SAFE
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <RF24.h> // library by TMRh20
|
|
||||||
#include <EspMQTTClient.h>
|
#include <EspMQTTClient.h>
|
||||||
#include <PacketReceiver.h>
|
#include <RFPowerView.h>
|
||||||
#include <PacketBuilder.h>
|
|
||||||
#include <PacketParser.h>
|
|
||||||
#include "secrets.h"
|
#include "secrets.h"
|
||||||
|
|
||||||
#define SER_BAUDRATE (115200)
|
#define SER_BAUDRATE (115200)
|
||||||
|
|
||||||
#define DEFAULT_RF_CHANNEL (7) //this is the channel HD shades use
|
// Copied from Powerview Hub userdata API
|
||||||
#define DEFAULT_RF_DATARATE (RF24_1MBPS) //this is the speed HD shades use
|
// eg: http://POWERVIEW_HUB_IP_ADDRESS/api/userdata/ and find the field labeled "rfID"
|
||||||
|
#define RF_ID (0x2EC8)
|
||||||
|
|
||||||
RF24 radio(RF_CE_PIN, RF_CS_PIN);
|
RFPowerView powerView(RF_CE_PIN, RF_CS_PIN, RF_IRQ_PIN, RF_ID);
|
||||||
|
|
||||||
// Copied from Powerview Hub userdata API (eg: http://POWERVIEW_HUB_IP_ADDRESS/api/userdata/ and find the field labeled "rfID")
|
|
||||||
static const uint8_t rfID[2] = { 0xC8, 0x2E }; // 0x2EC8
|
|
||||||
|
|
||||||
PacketReceiver packetReceiver(&radio);
|
|
||||||
PacketBuilder remotePacketBuilder(0x369E, 0xAC, 0x82, 0x06);
|
|
||||||
PacketBuilder hubPacketBuilder(0x0000, 0x3D, 0x96, 0x05);
|
|
||||||
PacketParser packetParser;
|
|
||||||
|
|
||||||
EspMQTTClient client(
|
EspMQTTClient client(
|
||||||
SECRET_WIFI_SSID, // Wifi SSID
|
SECRET_WIFI_SSID, // Wifi SSID
|
||||||
@@ -35,48 +23,18 @@ EspMQTTClient client(
|
|||||||
1883
|
1883
|
||||||
);
|
);
|
||||||
|
|
||||||
uint8_t buffer[32];
|
void processPacket(const Packet*);
|
||||||
|
|
||||||
// put function declarations here:
|
|
||||||
|
|
||||||
static void
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
|
||||||
IRAM_ATTR
|
|
||||||
#endif
|
|
||||||
handleNrfIrq();
|
|
||||||
|
|
||||||
void radioListenSetup();
|
|
||||||
void radioTransmitSetup();
|
|
||||||
void processPacket(const uint8_t*);
|
|
||||||
void sendCommand(const uint8_t*);
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(SER_BAUDRATE);
|
Serial.begin(SER_BAUDRATE);
|
||||||
|
|
||||||
Serial.println("Starting up");
|
Serial.println("Starting up");
|
||||||
|
|
||||||
packetReceiver.setPacketCallback(processPacket);
|
powerView.setPacketCallback(processPacket);
|
||||||
|
if (!powerView.begin()) {
|
||||||
if (!radio.begin()) {
|
Serial.println("Failed to start RFPowerView");
|
||||||
Serial.println("Failed to communicate with radio");
|
return;
|
||||||
}
|
}
|
||||||
Serial.println("Connected to radio");
|
|
||||||
|
|
||||||
radio.setChannel(DEFAULT_RF_CHANNEL);
|
|
||||||
radio.setDataRate(DEFAULT_RF_DATARATE);
|
|
||||||
|
|
||||||
radio.setAutoAck(false);
|
|
||||||
radio.disableCRC();
|
|
||||||
radio.setRetries(0, 0);
|
|
||||||
radio.setPayloadSize(32);
|
|
||||||
radio.setAddressWidth(2);
|
|
||||||
radio.powerUp();
|
|
||||||
|
|
||||||
pinMode(RF_IRQ_PIN, INPUT);
|
|
||||||
|
|
||||||
attachInterrupt(digitalPinToInterrupt(RF_IRQ_PIN), handleNrfIrq, FALLING);
|
|
||||||
|
|
||||||
radioListenSetup();
|
|
||||||
|
|
||||||
client.setKeepAlive(10);
|
client.setKeepAlive(10);
|
||||||
client.enableLastWillMessage("hotdog/availability", "offline", true);
|
client.enableLastWillMessage("hotdog/availability", "offline", true);
|
||||||
@@ -87,45 +45,17 @@ void setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
packetReceiver.loop();
|
powerView.loop();
|
||||||
client.loop();
|
client.loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// put function definitions here:
|
void processPacket(const Packet *packet) {
|
||||||
static void handleNrfIrq() {
|
if (packet->type == PacketType::FIELDS) {
|
||||||
packetReceiver.read();
|
FieldsParameters parameters = std::get<FieldsParameters>(packet->parameters);
|
||||||
}
|
|
||||||
|
|
||||||
void radioListenSetup() {
|
|
||||||
radio.openReadingPipe(0, rfID);
|
|
||||||
|
|
||||||
radio.startListening();
|
|
||||||
Serial.println("Listening");
|
|
||||||
|
|
||||||
interrupts();
|
|
||||||
}
|
|
||||||
|
|
||||||
void radioTransmitSetup() {
|
|
||||||
radio.stopListening();
|
|
||||||
|
|
||||||
radio.setPALevel(RF24_PA_HIGH, true);
|
|
||||||
|
|
||||||
radio.openWritingPipe(rfID);
|
|
||||||
radio.powerUp();
|
|
||||||
|
|
||||||
Serial.println("Ready to Transmit");
|
|
||||||
}
|
|
||||||
|
|
||||||
void processPacket(const uint8_t *buffer) {
|
|
||||||
Packet packet;
|
|
||||||
bool result = packetParser.parsePacket(buffer, packet);
|
|
||||||
if (result) {
|
|
||||||
if (packet.type == PacketType::FIELDS) {
|
|
||||||
FieldsParameters parameters = std::get<FieldsParameters>(packet.parameters);
|
|
||||||
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];
|
||||||
if (field.identifier == 0x50) {
|
if (field.identifier == 0x50) {
|
||||||
if (packet.source == 0x4EF1) {
|
if (packet->source == 0x4EF1) {
|
||||||
uint16_t value = std::get<uint16_t>(field.value);
|
uint16_t value = std::get<uint16_t>(field.value);
|
||||||
uint8_t position = (uint8_t)std::round(((float)value / 0xFFFF) * 100);
|
uint8_t position = (uint8_t)std::round(((float)value / 0xFFFF) * 100);
|
||||||
String payload = String(position);
|
String payload = String(position);
|
||||||
@@ -134,23 +64,6 @@ void processPacket(const uint8_t *buffer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendCommand(uint8_t *buffer) //transmit a command
|
|
||||||
{
|
|
||||||
radioTransmitSetup();
|
|
||||||
uint8_t bytecount = buffer[1] + 4;
|
|
||||||
for (int j = 1; j < 5; j++) {
|
|
||||||
radio.openWritingPipe(rfID);
|
|
||||||
for (int i = 1; i < 200; i++) {
|
|
||||||
radio.writeFast(buffer, bytecount);
|
|
||||||
}
|
|
||||||
delay(100);
|
|
||||||
radio.flush_tx();
|
|
||||||
radio.txStandBy();
|
|
||||||
}
|
|
||||||
radioListenSetup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onConnectionEstablished() {
|
void onConnectionEstablished() {
|
||||||
@@ -159,24 +72,20 @@ void onConnectionEstablished() {
|
|||||||
client.subscribe("hotdog/test_mqtt_blind/set", [] (const String &payload) {
|
client.subscribe("hotdog/test_mqtt_blind/set", [] (const String &payload) {
|
||||||
uint16_t shadeID = 0x4EF1;
|
uint16_t shadeID = 0x4EF1;
|
||||||
if (payload == "OPEN") {
|
if (payload == "OPEN") {
|
||||||
hubPacketBuilder.buildUpPacket(buffer, shadeID);
|
// TODO: Build open Packet and send
|
||||||
sendCommand(buffer);
|
|
||||||
} else if (payload == "CLOSE") {
|
} else if (payload == "CLOSE") {
|
||||||
hubPacketBuilder.buildDownPacket(buffer, shadeID);
|
// TODO: Build close Packet and send
|
||||||
sendCommand(buffer);
|
|
||||||
} else if (payload == "STOP") {
|
} else if (payload == "STOP") {
|
||||||
hubPacketBuilder.buildStopPacket(buffer, shadeID);
|
// TODO: Build stop Packet and send
|
||||||
sendCommand(buffer);
|
// TODO: Schedule fetching position of blind
|
||||||
hubPacketBuilder.buildFetchPositionPacket(buffer, shadeID);
|
|
||||||
sendCommand(buffer);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
client.subscribe("hotdog/test_mqtt_blind/set_position", [] (const String &payload) {
|
client.subscribe("hotdog/test_mqtt_blind/set_position", [] (const String &payload) {
|
||||||
uint16_t shadeID = 0x4EF1;
|
uint16_t shadeID = 0x4EF1;
|
||||||
float percentage = payload.toInt() / 100.0f;
|
float percentage = payload.toInt() / 100.0f;
|
||||||
hubPacketBuilder.buildSetPositionPacket(buffer, shadeID, percentage);
|
// TODO: Build set position Packet and send
|
||||||
sendCommand(buffer);
|
// TODO: Schedule fetching position of blind
|
||||||
});
|
});
|
||||||
|
|
||||||
client.publish("hotdog/availability", "online", true);
|
client.publish("hotdog/availability", "online", true);
|
||||||
|
|||||||
Reference in New Issue
Block a user