From 460307e608ae9bb28095de90ef40586fd557a52e Mon Sep 17 00:00:00 2001 From: Matt Way Date: Sun, 10 Dec 2023 19:35:12 +1100 Subject: [PATCH] Simple program to sniff PowerView packets using RFPowerView --- .gitignore | 6 ++ .vscode/extensions.json | 10 ++ lib/PrintHelpers/PrintHelpers.h | 39 ++++++++ platformio.ini | 23 +++++ src/main.cpp | 159 ++++++++++++++++++++++++++++++++ 5 files changed, 237 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 lib/PrintHelpers/PrintHelpers.h create mode 100644 platformio.ini create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..be99e35 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.pio + +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/.vscode/extensions.json @@ -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" + ] +} diff --git a/lib/PrintHelpers/PrintHelpers.h b/lib/PrintHelpers/PrintHelpers.h new file mode 100644 index 0000000..08f1ead --- /dev/null +++ b/lib/PrintHelpers/PrintHelpers.h @@ -0,0 +1,39 @@ +#ifndef PRINTHELPERS_H +#define PRINTHELPERS_H + +#include + +void printHex(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 uint8_t arraySize) { //print a byte array as hex chars + for (int i = 0; i < arraySize; i++) { + printHex(byteArray[i]); + } +} + +void printBuffer(const uint8_t *buffer) { + uint8_t length = buffer[1] + 4; + printByteArray(buffer, length); +} + +void printUint8(uint8_t value, bool prefix = true) { + if (prefix) { + Serial.print("0x"); + } + printHex(value); +} + +void printUint16(uint16_t value, bool prefix = true) { + if (prefix) { + Serial.print("0x"); + } + printUint8(uint8_t(value & 0xFF00 >> 8), false); + printUint8(uint8_t(value & 0x00FF), false); +} + +#endif // PRINTHELPERS_H \ No newline at end of file diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..56cfabe --- /dev/null +++ b/platformio.ini @@ -0,0 +1,23 @@ +; 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 = + RFPowerView=https://github.com/mattyway/RFPowerView#v0.0.3 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..7de6f24 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,159 @@ +// sketch to sniff PowerView packets from a pebble remote or hub + +#include +#include +#include + +#define SER_BAUDRATE (115200) + +// Copied from Powerview Hub userdata API +// eg: http://POWERVIEW_HUB_IP_ADDRESS/api/userdata/ and find the field labeled "rfID" +#define RF_ID (0x2EC8) + +RFPowerView powerView(RF_CE_PIN, RF_CS_PIN, RF_IRQ_PIN, RF_ID); + +void processBuffer(const uint8_t*); +void processPacket(const Packet*); +void printField(Field& field); + + +void setup() { + Serial.begin(SER_BAUDRATE); + + Serial.println("Starting up"); + + powerView.setPacketReceivedCallback(processPacket); + powerView.setValidBufferReceivedCallback(processBuffer); + if (!powerView.begin()) { + Serial.println("Failed to start RFPowerView"); + return; + } + + Serial.println("Ready"); +} + +void loop() { + powerView.loop(); +} + +void processBuffer(const uint8_t *buffer) { + printBuffer(buffer); + Serial.println(); +} + +void processPacket(const Packet *packet) { + if (std::holds_alternative(packet->header)) { + auto header = std::get(packet->header); + Serial.print("Source: "); + Serial.print(header.source, HEX); + } else if (std::holds_alternative(packet->header)) { + auto header = std::get(packet->header); + Serial.print("Source: "); + Serial.print(header.source, HEX); + Serial.print(" Destination: "); + Serial.print(header.destination, HEX); + } else if (std::holds_alternative(packet->header)) { + auto header = std::get(packet->header); + Serial.print("Source: "); + Serial.print(header.source, HEX); + Serial.print(" Groups: "); + for (size_t i = 0; i < header.groups.size(); i++) { + Serial.print(header.groups[i]); + if (i != header.groups.size() - 1) { + Serial.print(" "); + } + } + } + Serial.print(" "); + Serial.print("Rolling codes: "); + Serial.print(packet->rollingCode1, HEX); + Serial.print(" "); + Serial.print(packet->rollingCode2, HEX); + Serial.print(" "); + switch(packet->type) { + case PacketType::STOP: + Serial.print("Stop"); + break; + case PacketType::OPEN: + Serial.print("Open"); + break; + case PacketType::CLOSE: + Serial.print("Close"); + break; + case PacketType::OPEN_SLOW: + Serial.print("Open Slow"); + break; + case PacketType::CLOSE_SLOW: + Serial.print("Close Slow"); + break; + case PacketType::MOVE_TO_SAVED_POSITION: + Serial.print("Move To Saved Position"); + break; + case PacketType::FIELDS: { + FieldsParameters parameters = std::get(packet->parameters); + for (size_t i = 0; i < parameters.fields.size(); i++) { + Field field = parameters.fields[i]; + printField(field); + } + break; + } + case PacketType::FIELD_COMMAND: { + FieldsParameters parameters = std::get(packet->parameters); + for (size_t i = 0; i < parameters.fields.size(); i++) { + Field field = parameters.fields[i]; + printField(field); + } + break; + } + case PacketType::UNKNOWN: + Serial.print("???"); + break; + } + Serial.println(); +} + +void printField(Field& field) { + if (field.type == FieldType::SET) { + Serial.print("Set "); + } + if (field.type == FieldType::FETCH) { + Serial.print("Fetch "); + } + switch (field.identifier) { + case 0x50: { + // position + Serial.print("Position"); + if (field.hasValue) { + uint16_t value = std::get(field.value); + uint8_t position = (uint8_t)std::round(((float)value / 0xFFFF) * 100); + Serial.print("="); + Serial.print(position); + } + break; + } + case 0x42: { + // battery + Serial.print("Battery"); + if (field.hasValue) { + uint8_t value = std::get(field.value); + uint8_t battery = uint8_t(((float)value / 200) * 100); + Serial.print("="); + Serial.print(battery); + } + break; + } + default: { + printUint8(field.identifier); + if (field.hasValue) { + if (std::holds_alternative(field.value)) { + Serial.print("="); + printUint8(std::get(field.value)); + } else if (std::holds_alternative(field.value)) { + Serial.print("="); + printUint16(std::get(field.value)); + } + } + } + } + Serial.print(" "); +}