CaiXianlin
Compatible
This product is compatible with OpenShock.
Cheap and easily acquirable.
Buying
Shockers
Best effort list of current AliExpress sellers. Feel free to add more sources to this list!
- International: AliExpress
- Netherlands: AliExpress
Cables
The charging port for this model is a standard DC 3.5 x 1.35mm. A USB to DC 3.5 x 1.35mm cable is used to charge the shocker. You might even have one laying around as they are common.
- International: AliExpress
- International: AliBaba
Media
Thank you @dasbrin
on Discord for the images.
Technical Specification
Official documents
Community Reversed Engineered documents
Shocker & Remote Documents on GitHub by @Nat-the-Kat
RF Specification
Name | Value |
---|---|
Carrier Frequency | 433.95 MHz |
Modulation Type | ASK / OOK |
Bit encoding
Type | High duration | Low duration |
---|---|---|
Sync | 1400µs | 750µs |
1 | 750µs | 250µs |
0 | 250µs | 750µs |
Packet fields
Name | Value | Length | Remarks |
---|---|---|---|
Transmitter ID | 0 - 65535 | 16 bits | The collar will be Paired to this |
Channel Number | 0 - 2 | 4 bits | The collar will be Paired to this |
Action Command | 1 - 3 | 4 bits | 1 = Shock, 2 = Vibrate, 3 = Beep |
Command Intensity | 0 - 99 | 8 bits | Should always be 0 for beep |
Message checksum | 0 - 255 | 8 bits | 8-bit sum of all other fields as a int32 |
Layout
[PREFIX ] = SYNC
[TRANSMITTER ID] = XXXXXXXXXXXXXXXX
[CHANNEL ] = XXXX
[MODE ] = XXXX
[STRENGTH ] = XXXXXXXX
[CHECKSUM ] = XXXXXXXX
[END ] = 00
Working C++ code
Example untested RFCat code
# Import the necessary libraries and functions
from rflib import *
import time
# Set up the RfCat device
d = RfCat()
d.setPktPQT(0)
d.setMdmNumPreamble(0)
d.setEnableMdmManchester(False)
d.setFreq(433950000)
d.setMdmModulation(MOD_ASK_OOK)
d.setMdmDRate(3950)
d.makePktFLEN(22)
d.setMdmSyncWord(0)
"""
Returns the string representation of the action (Shock, Vibrate, or Beep)
"""
def get_action_string(action):
if action == 1:
return 'Shock'
elif action == 2:
return 'Vibrate'
elif action == 3:
return 'Beep'
else:
return 'Unknown'
# Since the receivers only support 3 channels, we can change the transmitter ID to extend the number of channels
for transmitter_id in range(46231, 46233):
# Loop through the channels
for channel in range(3):
# Loop through the actions
for action in range(1, 4):
# Loop through the intensities, but not if the action is 3 (beep)
for intensity in range(0, 100, 10) if action != 3 else [0]:
# Intensity has max of 99
if intensity == 100:
intensity = 99
# Assemble the payload
payload = (transmitter_id << 24) | (channel << 20) | (action << 16) | (intensity << 8)
# Calculate the checksum (sum(bytes) % 256)
checksum = 0
for i in range(8):
checksum += (payload >> (i * 8)) & 0xFF
checksum %= 256
# Add the checksum to the payload
payload |= checksum
# Assemble the message
message = bytes.fromhex('fc{0:040b}88'.format(payload).replace('1', 'e').replace('0', '8'))
print('Sending {0} on channel {1} with intensity {2} and checksum {3}'.format(get_action_string(action), channel, intensity, checksum))
# Transmit the message 5 times
for i in range(5):
d.RFxmit(message)