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