Petrainer PET998-Series E-Collar
Brand: Petrainer
FCC ID: 2AATP-PET998DRU
Modulation
The collars use pulse-position modulation (PPM) at 433MHz to encode command messages. A regular button press on the remote will repeat the message 3 times, though you can get it to send only one if you press the button quickly enough. The reciever seems to have diffuculty picking up the command if it isn’t repeated at least 3 times. Holding down the button will continuously repeat the message.
Messages begin with an 780µs pulse followed by a 780µs gap as a starting signal, followed by a series of 216µs pulses. The binary data is encoded in the gaps between pulses: a 780µs gap for a 0
or a 1580µs gap for a 1
.
We can use the wonderful rtl_433
program to record the signal and generate some nice visualizations:
rtl_433 -A -R 0 -S all
💡 Command options used (click to expand)
[-A] Pulse Analyzer. Enable pulse analysis and decode attempt. Disable all decoders with -R 0 if you want analyzer output only. [-S none | all | unknown | known] Signal auto save. Creates one file per signal. Note: Saves raw I/Q samples (uint8 pcm, 2 channel). Preferred mode for generating test files.
Here’s the result:
Using this we can easily decode our signal to binary (ignoring the extra 0
pulseview shows at the start)
- Binary:
11110010 00101011 00111010 00101101 10110000
- Hex:
F2 2B 3A 2D B0
Binary Encoding
The first 4 bits encode the channel (The remote supports switching between 2 channels).
#- -- -- -- --
F2 2B 3A 2D B0
Binary | Hex | Decimal | Channel |
---|---|---|---|
1000 | 0x8 | 8 | Channel 1 |
1111 | 0xF | 15 | Channel 2 |
The second 4 bits encode the command mode.
-# -- -- -- --
F2 2B 3A 2D B0
Binary | Hex | Decimal | Mode |
---|---|---|---|
0001 | 0x1 | 1 | Shock |
0010 | 0x2 | 2 | Vibrate |
0100 | 0x4 | 4 | Beep |
1000 | 0x8 | 8 | Flash |
The second and third bytes encode the remote’s unique id.
-- ## ## -- --
F2 2B 3A 2D B0
Binary | Hex |
---|---|
00101011 00111010 | 2B 3A |
The fourth byte encodes the shock/vibrate intensity (0-100).
*In my testing, the PET998DBB would not go higher than 100, but I’ve seen people using generic older versions of the collar that say they will.
-- -- -- ## --
F2 2B 3A 2D B0
Binary | Hex | Decimal |
---|---|---|
00101101 | 0x2D | 45 |
The fifth byte is an inverted, flipped copy of the first byte (probably used as a weak error check).
-- -- -- -- ##
F2 2B 3A 2D B0
Binary | Hex | Decimal | |
---|---|---|---|
First Byte | 11110010 | 0xF2 | 242 |
Last Byte | 10110000 | 0xB0 | 176 |
Here’s an example rtl_433
command that can decode the signal and break it into it’s components:
rtl_433 -F json -R 0 -X "name=petrainer,modulation=OOK_PPM,short=784,long=1580,gap=3000,reset=16000,get=channel:@1:{4}:[8:channel_1 15:channel_2],get=mode:@5:{4}:[1:shock 2:vibration 4:beep 8:flash],get=UID:@9:{16}:%X,get=intensity:@25:{8}"
{
"time": "2023-01-13 21:41:05",
"model": "petrainer",
"count": 4,
"num_rows": 4,
"rows": [
{
"len": 41,
"data": "79159d16d80",
"channel": "channel_2",
"mode": "vibration",
"UID": 11066,
"intensity": 45
},
{
"len": 41,
"data": "79159d16d80",
"channel": "channel_2",
"mode": "vibration",
"UID": 11066,
"intensity": 45
},
{
"len": 41,
"data": "79159d16d80",
"channel": "channel_2",
"mode": "vibration",
"UID": 11066,
"intensity": 45
},
{
"len": 0,
"data": "",
"channel": 0,
"mode": 0,
"UID": 0,
"intensity": 0
}
],
"codes": [
"{41}79159d16d80",
"{41}79159d16d80",
"{41}79159d16d80",
{0}
]
}
*this output isn’t fully valid JSON - there seems to be a bug in rtl_433
when it writes the codes
array.
Full Model List
Model | Notes | Links |
PET998D1 (IS-PET998D-1) |
Seems to be the original? Not rechargeable | |
PET998D2 (IS-PET998D-2) |
Two-collar bundle for PET998D | |
PET998DR1 (IS-PET998DR-1) |
Rechargeable (5v barrel) | |
PET998DR2 (IS-PET998DR-2) |
Two-collar bundle for PET998DR | |
PET998DRB1 (IS-PET998DRB-1) |
Rechargeable (5v barrel) | |
PET998DRB2 (IS-PET998DRB-2) |
Two-collar bundle for PET998DRB | |
PET998DRU1 (IS-PET998DRU-1) |
Rechargeable (micro-usb) | |
PET998DRU2 (IS-PET998DRU-2) |
Two-collar bundle for PET998DRU | |
PET998DB1 (IS-PET998DB 1 for 1) |
Thinner design. Rechargeable (5v barrel) | |
PET998DB2 (IS-PET998DB 1 for 2) |
Two-collar bundle for PET998DB | |
PET998DBB1 | Thinner design. Rechargeable (5v barrel). Links to the same manual as the PET998DB. | |
PET998DBB2 | Two-collar bundle for PET998DBB | |
PET998DBU (IS-PET998DBU 1 for 1) |
Thinner design. Rechargeable (micro-usb). The manual also shows a 2-collar bundle but I haven't found any listings for it. |
PET998DBB Teardown
Other Resources
- https://reverseengineering.stackexchange.com/questions/19671/need-help-to-reverse-engineer-a-dog-collar-transmitter
- http://brettleaver.com/collar/
- https://www.rtl-sdr.com/hak5-at-shmoocon-2017-shock-collar-radio-roulette-gnu-radio-sniffing-ir-terrahertz-signals-and-more/
- https://gist.github.com/tkuester/67f2d8f5c03aee22c6d7
- https://github.com/spth/cockshock2
- https://github.com/smouldery/shock-collar-control*
- https://github.com/CrashOverride85/collar/blob/master/extra/Type2.MD
- https://github.com/remoshock/remoshock/blob/master/src/remoshock/receiver/petrainer.py
- https://buttplug-spec.docs.buttplug.io/docs/stpihkal/protocols/petrainer/
*there seems to be some conflicting information in this repo about the protocol from older models. Contact me if you find a PET988 model collar that uses a different protocol.