Hello all,
I recently bought a Petrainer 900B training collar from China. It's very cheap and is pretty solidly made, the collar has a (I think) unique feature - that the transmitter can send a command that includes a beep, zap and vibrate command all at the same time.
The collars are waterproof - but the transmitter is a bit clumsy to operate. The power of the shock goes very high and the vibe and beep are also quite strong.
They are available for less than $45 with a transmitter and 3 collars, mine took about 3 weeks to arrive, you can find them here:
https://www.aliexpress.com/item/1959757426.html There is a demo video on the page.
They work on 433mhz so should be fairly easy to hack. I decided to do just that.
First, I soldered a 433mhz receiver to a USB sound card and looked for the signal using audacity, the signal looks like this:
https://ibb.co/L6ydRRX
- Image of the signal from Audacity
I found that playing the audio back through a 433mhz transmitter from Audacity was pretty unreliable, so I connected the 433mhz receiver to an Arduino and hoped that I could use the rc-switch library to decode the signal - (but found that I couldn't
).
Eventually I found this utility which allowed me to download the raw data from the signal:
https://github.com/sui77/SimpleRcScanne ... canner.ino
Using the above sketch, I downloaded the signal with a wide variety of settings on the remote and pasted them all into an excel spreadsheet to see if I could decode the way the signal works.
It turns out I could
The signal is encoded as follows:
A long high and low preamble pulse then the 16 bit transmitter ID, then 2 bit collar ID, 7 bit shock level, 7 bit vibe level, 7 bit beep level, 7 bit checksum and a zero.
With a little bit of head scratching, I worked out that the checksum is the result of using and XOR operation on the collar ID and each of the 7 bit level values in turn.
I wrote some code to test it and it works!
I have tided the code up a bit and tested it on a Arduino Uno and a DOIT ESP32 Dev module - I offer it here to anyone who is interested.
Note that you need to pair your collar to the 433mhz module and Arduino you are using (not to the transmitter that comes with it) to make this work.
IB
Code: Select all
// This code is designed to operate Petrainer 900B model collars.
// This collar is able to beep, zap and vibe all at the same time
// Use the code to drive the data pin on a 433mhz 3 pin transmitter module
// Some of these modules work better than others - the least fancy ones seem to work best
//
// Written by IcarusBurned 2020 - Use it at your own risk!!
// Variables which you can change during the loop
int ZapPower = 05; // Strength of the zap (0-99)
int VibePower = 50; // Strength of vibrate (0-99)
int BeepPower = 50; // Strength of the beep (0-99)
int TxDuration = 250; // Pulse duration in milliseconds
int CollarID = 1; // Collar ID Can be 1, 2 or 3
// You can change these (but not during the loop)
const String TxID = "0010101011000011"; // ID of the transmitter
const int LEDpin = 2; // Pin for indication LED (pin 2 is the onboard LED on Node ESP32 Dev board)
const int TxPin = 19; // GPIO Pin connected to transmitter
const int StayAwakeInt = 30000; // Length of inactivity before a signal is sent (milliSeconds)
// You shouldn't need to change these values
const int IntervalLow = 720; // Pause between sequences (Microseconds)
const int StartHigh = 1400; // Initial high pulse (Microseconds)
const int StartLow = 440; // Initial low pulse (Microseconds)
const int OneHigh = 662; // Length of "1" high pulse (Microseconds)
const int OneLow = 440; // Length of "1" low pulse (Microseconds)
const int ZeroHigh = 316; // Length of "0" high pulse (Microseconds)
const int ZeroLow = 380; // Length of "0" low pulse (Microseconds)
unsigned long LastActive = 0; // Timestamp of last activation time (for StayAwake function)
// Here is the main function that builds the code and then transmits it
void TxCmdSequence(int Chan, int Zpwr, int Vpwr, int Bpwr, int TxLength)
// Syntax is:
//
// TxCmdSequence( ChannelID, Zap_strength, Vibe_strength, Beep_Strength, Length_of_transmission );
//
// Channel can be 1, 2 or 3
// Zap, Vibe and Beep strength all need to be values between 0-99
// Length of transmission is in milliseconds and should be above 100mS
// All the values are set when you call the function from the loop
//
// You shouldn't need to change any of the code in this function
{
String collarNo = "00"; // Declare the string values
String CheckSum = "0000000";
String TxSequence; // Holds the full TX sequence
digitalWrite(LEDpin, HIGH); // Switch on the TX Indicator LED
switch (Chan) // Check the channel
{
case 1: // For channel 1
collarNo = "10"; // Set the string value for the Tx code
CheckSum = "1000000"; // And the 7 bit checksum value
break;
case 2: // Same if it's channel 2
collarNo = "11";
CheckSum = "1100000";
break;
case 3: // Etc.
collarNo = "01";
CheckSum = "0100000";
break;
}
// Now generate the binary strings for each variable
String ZpwrBin = "0000000"; // Each string must contain exactly 7 characters
for (int i = 0; i <= 6; i++) // Loop to read the binary value bit by bit
{
ZpwrBin[i] = bitRead(Zpwr, i) + 48; // Write the binary to the "ZpwrBin" string
CheckSum[i] = (CheckSum[i] ^ ZpwrBin[i]) + 48; // Use XOR to compare bits and update the checksum
}
String VpwrBin = "0000000"; // Same process for Vibe
for (int i = 0; i <= 6; i++)
{
VpwrBin[i] = bitRead(Vpwr, i) + 48;
CheckSum[i] = (CheckSum[i] ^ VpwrBin[i]) + 48;
}
String BpwrBin = "0000000"; // And same again for Beep
for (int i = 0; i <= 6; i++)
{
BpwrBin[i] = bitRead(Bpwr, i) + 48;
CheckSum[i] = (CheckSum[i] ^ BpwrBin[i]) + 48;
}
// Build the transmit sequence with the values and add a zero at the end
TxSequence = ( TxID + collarNo + ZpwrBin + VpwrBin + BpwrBin + CheckSum + "0");
// And send a debugging message
Serial.println("New pulse settings:");
Serial.print("C:");
Serial.print(Chan);
Serial.print(" Z:");
Serial.print(Zpwr);
Serial.print(" V:");
Serial.print(Vpwr);
Serial.print(" B:");
Serial.print(Bpwr);
Serial.print(" ");
Serial.print(collarNo);
Serial.print("-");
Serial.print(ZpwrBin);
Serial.print("-");
Serial.print(VpwrBin);
Serial.print("-");
Serial.print(BpwrBin);
Serial.print("-");
Serial.print(CheckSum);
Serial.println("-0");
// Now transmit the code
unsigned long TxStartTime = millis(); // Timestamp the start of the transmision
LastActive = millis(); // Reset stay awake counter
while (millis() - TxStartTime < TxLength) // Transmit until the duration is reached
{
// Transmit preamble bits
Serial.print("TX: ");
Serial.println(TxSequence);
digitalWrite(TxPin, LOW);
delayMicroseconds(IntervalLow);
digitalWrite(TxPin, HIGH);
delayMicroseconds(StartHigh);
digitalWrite(TxPin, LOW);
delayMicroseconds(StartLow);
// Then transmit the rest of the sequence
for (int n = 0; n < 47 ; n++) // read the 48 bit sequence one character at a time
{
if (TxSequence.charAt(n) == '1') // If it's a "1"
{
digitalWrite(TxPin, HIGH); // Transmit a one
delayMicroseconds(OneHigh);
digitalWrite(TxPin, LOW);
delayMicroseconds(OneLow);
//Serial.print("1");
}
else // Otherwise transmit a zero
{
digitalWrite(TxPin, HIGH);
delayMicroseconds(ZeroHigh);
digitalWrite(TxPin, LOW);
delayMicroseconds(ZeroLow);
//Serial.print("0");
}
}
}
digitalWrite(LEDpin, LOW); // Switch off the TX Indicator LED
}
void setup()
{
pinMode (LEDpin, OUTPUT); // Set the pin modes for the outputs
pinMode (TxPin, OUTPUT);
Serial.begin(115200); // Start the serial
delay (250); // Send debug message
Serial.println("Starting levels are:");
Serial.print("Zap: ");
Serial.print(ZapPower);
Serial.print("% Vibe: ");
Serial.print(VibePower);
Serial.print("% Beep: ");
Serial.print(BeepPower);
Serial.print("% Pulse Duration: ");
Serial.print(TxDuration);
Serial.println("mS");
}
void loop()
{
if (millis() - LastActive >= StayAwakeInt) // Put whatever trigger code you want here
{
TxCmdSequence(CollarID, ZapPower, VibePower, BeepPower, TxDuration); // Send the command to the collar
}
}