Convert Hotdog sketch to PlatformIO
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1 +1,5 @@
|
||||
arduino_secrets.h
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
|
||||
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,191 +0,0 @@
|
||||
#include "PacketCRC.h"
|
||||
|
||||
#define LENGTH_17 (0x11)
|
||||
#define LENGTH_21 (0x15)
|
||||
#define LENGTH_25 (0x19)
|
||||
|
||||
#define VERSION_5 (0x05)
|
||||
#define VERSION_6 (0x06)
|
||||
|
||||
#define REMOTE_ID (0x369E)
|
||||
#define HUB_ID (0x0000)
|
||||
|
||||
uint8_t remoteRollingCode1 = 0xAC;
|
||||
uint8_t remoteRollingCode2 = 0x82;
|
||||
uint8_t hubRollingCode1 = 0x00;
|
||||
uint8_t hubRollingCode2 = 0x00;
|
||||
|
||||
uint8_t buf[32];
|
||||
|
||||
PacketCRC packetCRC;
|
||||
|
||||
void setPacketLength(uint8_t);
|
||||
void setConstants();
|
||||
void setSourceAddress(uint16_t);
|
||||
void setTargetAddress(uint16_t);
|
||||
void setRollingCodes();
|
||||
void setProtocolVersion(uint8_t);
|
||||
void calculateCRC();
|
||||
|
||||
|
||||
void constructUpPacket(uint16_t shadeID)
|
||||
{
|
||||
setPacketLength(LENGTH_17);
|
||||
setConstants();
|
||||
setSourceAddress(REMOTE_ID);
|
||||
setTargetAddress(shadeID);
|
||||
setRollingCodes();
|
||||
setProtocolVersion(VERSION_6);
|
||||
|
||||
buf[16] = 0x52; // ?
|
||||
buf[17] = 0x55; // Command
|
||||
buf[18] = 0x00; // Empty?
|
||||
|
||||
calculateCRC();
|
||||
}
|
||||
|
||||
void constructDownPacket(uint16_t shadeID)
|
||||
{
|
||||
setPacketLength(LENGTH_17);
|
||||
setConstants();
|
||||
setSourceAddress(REMOTE_ID);
|
||||
setTargetAddress(shadeID);
|
||||
setRollingCodes();
|
||||
setProtocolVersion(VERSION_6);
|
||||
|
||||
buf[16] = 0x52; // ?
|
||||
buf[17] = 0x44; // Command
|
||||
buf[18] = 0x00; // Empty?
|
||||
|
||||
calculateCRC();
|
||||
}
|
||||
|
||||
void constructStopPacket(uint16_t shadeID)
|
||||
{
|
||||
setPacketLength(LENGTH_17);
|
||||
setConstants();
|
||||
setSourceAddress(REMOTE_ID);
|
||||
setTargetAddress(shadeID);
|
||||
setRollingCodes();
|
||||
setProtocolVersion(VERSION_6);
|
||||
|
||||
buf[16] = 0x52; // ?
|
||||
buf[17] = 0x53; // Command
|
||||
buf[18] = 0x00; // Empty?
|
||||
|
||||
calculateCRC();
|
||||
}
|
||||
|
||||
void constructPositionPacket(uint16_t shadeID, float percentage)
|
||||
{
|
||||
setPacketLength(LENGTH_21);
|
||||
setConstants();
|
||||
setSourceAddress(HUB_ID);
|
||||
setTargetAddress(shadeID);
|
||||
setRollingCodes();
|
||||
setProtocolVersion(VERSION_5);
|
||||
|
||||
buf[16] = 0x3F; // Command
|
||||
buf[17] = 0x5A; // Empty?
|
||||
|
||||
buf[18] = 0x04; // Constant in this size packet?
|
||||
buf[19] = 0x40; // Constant in this size packet?
|
||||
buf[20] = 0x50; // Constant in this size packet?
|
||||
|
||||
uint16_t position = (uint16_t)(0xFFFF * percentage);
|
||||
|
||||
buf[21] = (uint8_t)(position & 0x00FF);
|
||||
buf[22] = (uint8_t)((position & 0xFF00) >> 8);
|
||||
|
||||
calculateCRC();
|
||||
}
|
||||
|
||||
void setPacketLength(uint8_t length) {
|
||||
buf[1] = length; // Packet size
|
||||
}
|
||||
|
||||
void setConstants() {
|
||||
buf[0] = 0xC0; // Header byte
|
||||
|
||||
buf[2] = 0x00; // Constant when sending, can be 0x10 when receiving a packet
|
||||
buf[3] = 0x05; // Constant
|
||||
|
||||
buf[5] = 0xFF; // Constant
|
||||
buf[6] = 0xFF; // Constant
|
||||
|
||||
buf[9] = 0x86; // Constant?
|
||||
}
|
||||
|
||||
void setSourceAddress(uint16_t sourceID) {
|
||||
// Physical source address (could be the address of a repeater when receiving a packet)
|
||||
buf[7] = (uint8_t)((sourceID & 0xFF00) >> 8);
|
||||
buf[8] = (uint8_t)(sourceID & 0x00FF);
|
||||
|
||||
// Logical source address (usually the same as the physical source address)
|
||||
buf[14] = (uint8_t)((sourceID & 0xFF00) >> 8);
|
||||
buf[15] = (uint8_t)(sourceID & 0x00FF);
|
||||
}
|
||||
|
||||
void setTargetAddress(uint16_t targetID) {
|
||||
// Logical target address
|
||||
buf[12] = (uint8_t)((targetID & 0xFF00) >> 8);
|
||||
buf[13] = (uint8_t)(targetID & 0x00FF);
|
||||
}
|
||||
|
||||
void setRollingCodes() {
|
||||
uint16_t physicalSourceID = (buf[7] << 8) | buf[8];
|
||||
|
||||
uint8_t rollingCode1 = 0x00;
|
||||
if (physicalSourceID == REMOTE_ID) {
|
||||
rollingCode1 = remoteRollingCode1;
|
||||
} else if (physicalSourceID == HUB_ID) {
|
||||
rollingCode1 = hubRollingCode1;
|
||||
}
|
||||
|
||||
uint16_t logicalSourceID = (buf[14] << 8) | buf[15];
|
||||
|
||||
uint8_t rollingCode2 = 0x00;
|
||||
if (physicalSourceID == REMOTE_ID) {
|
||||
rollingCode2 = remoteRollingCode2;
|
||||
} else if (physicalSourceID == HUB_ID) {
|
||||
rollingCode2 = hubRollingCode2;
|
||||
}
|
||||
|
||||
buf[4] = rollingCode1; // Rolling code 1
|
||||
buf[11] = rollingCode2; // Rolling code 2
|
||||
}
|
||||
|
||||
void incrementRollingCodes() {
|
||||
uint16_t physicalSourceID = (buf[7] << 8) | buf[8];
|
||||
|
||||
uint8_t rollingCode1 = 0x00;
|
||||
if (physicalSourceID == REMOTE_ID) {
|
||||
remoteRollingCode1++;
|
||||
} else if (physicalSourceID == HUB_ID) {
|
||||
hubRollingCode1++;
|
||||
}
|
||||
|
||||
uint16_t logicalSourceID = (buf[14] << 8) | buf[15];
|
||||
|
||||
uint8_t rollingCode2 = 0x00;
|
||||
if (physicalSourceID == REMOTE_ID) {
|
||||
remoteRollingCode2++;
|
||||
} else if (physicalSourceID == HUB_ID) {
|
||||
hubRollingCode2++;
|
||||
}
|
||||
}
|
||||
|
||||
void setProtocolVersion(uint8_t version) {
|
||||
buf[10] = version; // Protocol version?
|
||||
}
|
||||
|
||||
void calculateCRC() { // must be called after the buffer has been filled
|
||||
uint8_t length = buf[1];
|
||||
uint16_t result = packetCRC.calculate(buf);
|
||||
|
||||
uint8_t checksum1 = (uint8_t)((result & 0xFF00) >> 8);
|
||||
uint8_t checksum2 = (uint8_t)(result & 0x00FF);
|
||||
|
||||
buf[length + 2] = checksum1; // Checksum
|
||||
buf[length + 3] = checksum2; // Checksum
|
||||
}
|
||||
134
Hotdog.ino
134
Hotdog.ino
@@ -1,134 +0,0 @@
|
||||
#include "arduino_secrets.h"
|
||||
#include "HDPacketBuilder.h"
|
||||
#include <EspMQTTClient.h>
|
||||
#include <RF24.h> // library by TMRh20
|
||||
|
||||
#define SER_BAUDRATE (115200)
|
||||
|
||||
// pins for: esp8266 NodeMCU v3 dev board
|
||||
#define RF_CE_PIN (5) // pins for: esp
|
||||
#define RF_CS_PIN (15) // pins for: esp
|
||||
#define RF_IRQ_PIN (4) // run a jumper from IRQ pin to pin 2
|
||||
|
||||
RF24 radio(RF_CE_PIN, RF_CS_PIN);
|
||||
|
||||
// Copied from Powerview Hub userdata API (eg: http://POWERVIEW_HUB_IP_ADDRESS/api/userdata/ and find the field labeled "rfID")
|
||||
static const uint16_t rfID = 0x2EC8;
|
||||
|
||||
EspMQTTClient client(
|
||||
SECRET_WIFI_SSID, // Wifi SSID
|
||||
SECRET_WIFI_PASSWORD, // Wifi Password
|
||||
SECRET_MQTT_SERVER_IP, // MQTT Broker server IP
|
||||
SECRET_MQTT_USERNAME, // MQTT Username
|
||||
SECRET_MQTT_PASSWORD, // MQTT Password
|
||||
"hotdog", // MQTT Client ID
|
||||
1883
|
||||
);
|
||||
|
||||
void setup() {
|
||||
for (int i = 0; i < 32; i++) {
|
||||
buf[i] = 0x00;
|
||||
}
|
||||
|
||||
Serial.begin(SER_BAUDRATE);
|
||||
|
||||
client.setKeepAlive(10);
|
||||
client.enableLastWillMessage("hotdog/availability", "offline", true);
|
||||
client.enableDebuggingMessages();
|
||||
|
||||
radio.begin();
|
||||
radioTransmitSetup();
|
||||
|
||||
Serial.println("Ready");
|
||||
}
|
||||
|
||||
void onConnectionEstablished() {
|
||||
|
||||
client.subscribe("hotdog/test_mqtt_blind/set", [] (const String &payload) {
|
||||
uint16_t shadeID = 0x0400;
|
||||
if (payload == "OPEN") {
|
||||
constructUpPacket(shadeID);
|
||||
printPacket(buf);
|
||||
Serial.println();
|
||||
sendCommand(buf);
|
||||
} else if (payload == "CLOSE") {
|
||||
constructDownPacket(shadeID);
|
||||
printPacket(buf);
|
||||
Serial.println();
|
||||
sendCommand(buf);
|
||||
} else if (payload == "STOP") {
|
||||
constructStopPacket(shadeID);
|
||||
printPacket(buf);
|
||||
Serial.println();
|
||||
sendCommand(buf);
|
||||
}
|
||||
});
|
||||
|
||||
client.subscribe("hotdog/test_mqtt_blind/set_position", [] (const String &payload) {
|
||||
|
||||
});
|
||||
|
||||
client.publish("hotdog/availability", "online", true);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
client.loop();
|
||||
}
|
||||
|
||||
void radioTransmitSetup() {
|
||||
radio.stopListening();
|
||||
radio.powerDown();
|
||||
radio.begin();
|
||||
delay(100);
|
||||
Serial.println();
|
||||
radio.setChannel(0x07); //shooting for 2.407 GHz
|
||||
radio.setDataRate(RF24_1MBPS); //can go at 250kbs, 1Mbs, 2Mbs
|
||||
radio.setPALevel(RF24_PA_HIGH); //transmit power
|
||||
radio.disableCRC();
|
||||
radio.setAutoAck(false);
|
||||
radio.setAddressWidth(3);
|
||||
radio.setRetries(0, 0);
|
||||
|
||||
radio.openWritingPipe(rfID);
|
||||
radio.powerUp();
|
||||
//radio.printDetails();
|
||||
//radio.printPrettyDetails();
|
||||
Serial.println("Ready to Transmit");
|
||||
}
|
||||
|
||||
void sendCommand(byte * byteArray) //transmit a command
|
||||
{
|
||||
uint8_t bytecount = byteArray[1] + 4;
|
||||
for (int j = 1; j < 5; j++) {
|
||||
radio.setAddressWidth(3);
|
||||
radio.disableCRC();
|
||||
radio.setAutoAck(false);
|
||||
radio.openWritingPipe(rfID);
|
||||
for (int i = 1; i < 200; i++) {
|
||||
radio.writeFast(byteArray, bytecount);
|
||||
}
|
||||
delay(500);
|
||||
radio.flush_tx();
|
||||
radio.txStandBy();
|
||||
}
|
||||
|
||||
incrementRollingCodes();
|
||||
}
|
||||
|
||||
void printHex(const uint8_t num) { //print a byte as two hex chars
|
||||
if (num < 0x10) {
|
||||
Serial.print("0");
|
||||
}
|
||||
Serial.print(num, HEX);
|
||||
}
|
||||
|
||||
void printByteArray(const uint8_t * byteArray, const int arraySize) { //print a byte array as hex chars
|
||||
for (int i = 0; i < arraySize; i++) {
|
||||
printHex(byteArray[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void printPacket(const uint8_t *buffer) {
|
||||
uint8_t length = buffer[1] + 4;
|
||||
printByteArray(buffer, length);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
#include "PacketCRC.h"
|
||||
|
||||
PacketCRC::PacketCRC() : crc(CRC16(0x755B, 0x0, 0xBA68, false, false)) {
|
||||
// Constructor implementation (if needed)
|
||||
}
|
||||
|
||||
PacketCRC::~PacketCRC() {
|
||||
// Destructor implementation (if needed)
|
||||
}
|
||||
|
||||
bool PacketCRC::check(const uint8_t* packet) {
|
||||
uint8_t length = packet[1];
|
||||
switch(length) {
|
||||
case 0x11:
|
||||
case 0x15:
|
||||
case 0x19:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t result = calculate(packet);
|
||||
uint8_t checksum1 = (uint8_t)((result & 0xFF00) >> 8);
|
||||
uint8_t checksum2 = (uint8_t)(result & 0x00FF);
|
||||
|
||||
if (packet[length + 2] == checksum1 && packet[length + 3] == checksum2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t PacketCRC::calculate(const uint8_t* packet) {
|
||||
crc.restart();
|
||||
uint8_t length = packet[1];
|
||||
switch(length) {
|
||||
case 0x11:
|
||||
crc.setXorOut(0x539E);
|
||||
break;
|
||||
case 0x15:
|
||||
crc.setXorOut(0x3B0E);
|
||||
break;
|
||||
case 0x19:
|
||||
crc.setXorOut(0x5C02);
|
||||
break;
|
||||
default:
|
||||
return 0x0000;
|
||||
}
|
||||
crc.add(packet, length + 2);
|
||||
return crc.calc();
|
||||
}
|
||||
18
PacketCRC.h
18
PacketCRC.h
@@ -1,18 +0,0 @@
|
||||
#ifndef PACKET_CRC_H
|
||||
#define PACKET_CRC_H
|
||||
|
||||
#include <CRC16.h>
|
||||
|
||||
class PacketCRC {
|
||||
public:
|
||||
PacketCRC();
|
||||
~PacketCRC();
|
||||
|
||||
// Method to calculate CRC for a packet
|
||||
bool check(const uint8_t* packet);
|
||||
uint16_t calculate(const uint8_t* packet);
|
||||
private:
|
||||
CRC16 crc;
|
||||
};
|
||||
|
||||
#endif // PACKET_CRC_H
|
||||
39
include/README
Normal file
39
include/README
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||
34
lib/PacketBuilder/include/PacketBuilder.h
Normal file
34
lib/PacketBuilder/include/PacketBuilder.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#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);
|
||||
~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 buildPositionPacket(uint8_t *buffer, uint16_t shadeID, float percentage);
|
||||
|
||||
private:
|
||||
uint16_t address;
|
||||
uint8_t rollingCode1;
|
||||
uint8_t rollingCode2;
|
||||
|
||||
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
|
||||
18
lib/PacketBuilder/library.json
Normal file
18
lib/PacketBuilder/library.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
149
lib/PacketBuilder/src/PacketBuilder.cpp
Normal file
149
lib/PacketBuilder/src/PacketBuilder.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#include "PacketBuilder.h"
|
||||
|
||||
PacketBuilder::PacketBuilder(uint16_t address, uint8_t rollingCode1, uint8_t rollingCode2) :
|
||||
address(address),
|
||||
rollingCode1(rollingCode1),
|
||||
rollingCode2(rollingCode2)
|
||||
{
|
||||
}
|
||||
|
||||
PacketBuilder::~PacketBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
void PacketBuilder::buildUpPacket(uint8_t *buffer, uint16_t shadeID)
|
||||
{
|
||||
setPacketSize(buffer, 0x11);
|
||||
setConstants(buffer);
|
||||
setProtocolVersion(buffer, 0x06);
|
||||
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, 0x06);
|
||||
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, 0x06);
|
||||
setSourceAddress(buffer, address);
|
||||
setDestinationAddress(buffer, shadeID);
|
||||
setRollingCodes(buffer);
|
||||
|
||||
buffer[16] = 0x52; // ?
|
||||
buffer[17] = 0x53; // Command
|
||||
buffer[18] = 0x00; // Empty?
|
||||
|
||||
calculateCRC(buffer);
|
||||
|
||||
incrementRollingCodes();
|
||||
}
|
||||
|
||||
void PacketBuilder::buildPositionPacket(uint8_t *buffer, uint16_t shadeID, float percentage)
|
||||
{
|
||||
setPacketSize(buffer, 0x15);
|
||||
setConstants(buffer);
|
||||
setProtocolVersion(buffer, 0x05);
|
||||
setSourceAddress(buffer, address);
|
||||
setDestinationAddress(buffer, shadeID);
|
||||
setRollingCodes(buffer);
|
||||
|
||||
buffer[16] = 0x3F; // Command
|
||||
buffer[17] = 0x5A; // Empty?
|
||||
|
||||
buffer[18] = 0x04; // Constant in this size packet?
|
||||
buffer[19] = 0x40; // Constant in this size packet?
|
||||
buffer[20] = 0x50; // Constant in this size packet?
|
||||
|
||||
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::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++;
|
||||
}
|
||||
46
lib/README
Normal file
46
lib/README
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||
31
platformio.ini
Normal file
31
platformio.ini
Normal file
@@ -0,0 +1,31 @@
|
||||
; 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
|
||||
build_flags =
|
||||
-D RF_CE_PIN=5
|
||||
-D RF_CS_PIN=15
|
||||
-D RF_IRQ_PIN=4
|
||||
|
||||
[env]
|
||||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
nrf24/RF24@^1.4.8
|
||||
|
||||
plapointe6/EspMQTTClient@^1.13.3
|
||||
|
||||
PacketReceiver=symlink://..\PowerViewSniffer\lib\PacketReceiver
|
||||
|
||||
PacketCRC=symlink://..\PowerViewSniffer\lib\PacketCRC
|
||||
|
||||
PacketBuilder
|
||||
1
src/.gitignore
vendored
Normal file
1
src/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
secrets.h
|
||||
158
src/main.cpp
Normal file
158
src/main.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
// sketch to sniff Hunter Douglas packets from a pebble remote or hub
|
||||
|
||||
#define CIRCULAR_BUFFER_INT_SAFE
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <RF24.h> // library by TMRh20
|
||||
#include <EspMQTTClient.h>
|
||||
#include <PacketReceiver.h>
|
||||
#include <PacketBuilder.h>
|
||||
#include "secrets.h"
|
||||
|
||||
#define SER_BAUDRATE (115200)
|
||||
|
||||
#define DEFAULT_RF_CHANNEL (7) //this is the channel HD shades use
|
||||
#define DEFAULT_RF_DATARATE (RF24_1MBPS) //this is the speed HD shades use
|
||||
|
||||
RF24 radio(RF_CE_PIN, RF_CS_PIN);
|
||||
|
||||
// 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);
|
||||
|
||||
EspMQTTClient client(
|
||||
SECRET_WIFI_SSID, // Wifi SSID
|
||||
SECRET_WIFI_PASSWORD, // Wifi Password
|
||||
SECRET_MQTT_SERVER_IP, // MQTT Broker server IP
|
||||
SECRET_MQTT_USERNAME, // MQTT Username
|
||||
SECRET_MQTT_PASSWORD, // MQTT Password
|
||||
"hotdog", // MQTT Client ID
|
||||
1883
|
||||
);
|
||||
|
||||
uint8_t buffer[32];
|
||||
|
||||
// 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() {
|
||||
Serial.begin(SER_BAUDRATE);
|
||||
|
||||
Serial.println("Starting up");
|
||||
|
||||
packetReceiver.setReceivedCallback(processPacket);
|
||||
|
||||
if (!radio.begin()) {
|
||||
Serial.println("Failed to communicate with radio");
|
||||
}
|
||||
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.enableLastWillMessage("hotdog/availability", "offline", true);
|
||||
client.enableDebuggingMessages();
|
||||
|
||||
delay(100);
|
||||
Serial.println("Ready");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
packetReceiver.loop();
|
||||
client.loop();
|
||||
}
|
||||
|
||||
// put function definitions here:
|
||||
static void handleNrfIrq() {
|
||||
packetReceiver.read();
|
||||
}
|
||||
|
||||
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) {
|
||||
Serial.println("Got packet");
|
||||
}
|
||||
|
||||
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(500);
|
||||
radio.flush_tx();
|
||||
radio.txStandBy();
|
||||
}
|
||||
radioListenSetup();
|
||||
}
|
||||
|
||||
void onConnectionEstablished() {
|
||||
Serial.println("Connection established");
|
||||
|
||||
client.subscribe("hotdog/test_mqtt_blind/set", [] (const String &payload) {
|
||||
uint16_t shadeID = 0x0400;
|
||||
if (payload == "OPEN") {
|
||||
remotePacketBuilder.buildUpPacket(buffer, shadeID);
|
||||
sendCommand(buffer);
|
||||
} else if (payload == "CLOSE") {
|
||||
remotePacketBuilder.buildDownPacket(buffer, shadeID);
|
||||
sendCommand(buffer);
|
||||
} else if (payload == "STOP") {
|
||||
remotePacketBuilder.buildStopPacket(buffer, shadeID);
|
||||
sendCommand(buffer);
|
||||
}
|
||||
});
|
||||
|
||||
client.subscribe("hotdog/test_mqtt_blind/set_position", [] (const String &payload) {
|
||||
|
||||
});
|
||||
|
||||
client.publish("hotdog/availability", "online", true);
|
||||
}
|
||||
11
test/README
Normal file
11
test/README
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PlatformIO Test Runner and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
||||
Reference in New Issue
Block a user