A Place to share code for arduino/Pi

Selfbondage software and other kinky developments

Moderators: Riddle, Shannon SteelSlave

User avatar
GeneralError
**
Posts: 141
Joined: 16 Sep 2019, 15:30
Location: Germany

Re: A Place to share code for arduino/Pi

Post by GeneralError »

mtlsub wrote: I hope someone can help me with this code ( i know it is messy but as I have said this is my first attempt at coding)
I have the times suck working and it knows if i sucked fast or slow. Currently when i suck slow it will print "Transmit" but it does not send a signal to the collar.
the code does send the signal before I attempted to merge the codes. I can't figure out what I did.
Debugging in Arduino is a real pain since there is no source level debugger with stepping and variable inspection and so on.
Just go for the brutal approach and place Serial.println("....") at every interesting point in your programm and watch the serial monitor to see what is going on.
Another aspect I saw in your code, is that you run your whole "testing round" in one loop. This makes one loop taking a quite long time with all the delays in it. It is a best practice to separate the state changes in the program from the main processing loop. I think it should be no problem in this specific program since you are not working with interrupts or so. But if Serial.println() does not help to find the bug it might be a good idea to restructure the program.
OneAndOnlyOdin
**
Posts: 109
Joined: 31 Mar 2019, 16:00

Removed

Post by OneAndOnlyOdin »

Removed
Last edited by OneAndOnlyOdin on 12 Jan 2022, 13:11, edited 1 time in total.
RIP Odin - Odin was killed to make place for other things. This forum will forever be a part of Odin's soul.
User avatar
Gregovic
****
Posts: 1118
Joined: 26 Mar 2016, 21:31
Location: Netherlands

Re: A Place to share code for arduino/Pi

Post by Gregovic »

OneAndOnlyOdin wrote:
Debugging in Arduino is a real pain since there is no source level debugger with stepping and variable inspection and so on.
Actually there is. The Arduino is using an AVR microcontroller for which AVR offers an IDE (based on Microsoft's Visual Studio) at least for Windows. However the downside is you'll have to get down to C and loose all the sugar the Arduino language (which is based on C++) has to offer. Depending on how good you are with C and how much code you want to stuff into the Arduino it can actually be a good idea.


Also the Arduino might not be the best idea to start out learning programming because its lack of proper debugging tools.

While the lack of debugging tools in the arduino IDE is a problem, the sheer amount of tutorials, instructions and example code on the internet is a VERY valuable resource. I havr yet to encounter anything even close for using the AVR programming studio on the "barebones" chip or any other IDE for a different microcontroller like the Teensy.
How may I serve you? *Curtsey*
User avatar
Riddle
****
Posts: 1135
Joined: 24 Sep 2008, 08:37
Location: Oregon, USA
Contact:

Re: A Place to share code for arduino/Pi

Post by Riddle »

OneAndOnlyOdin wrote:
Debugging in Arduino is a real pain since there is no source level debugger with stepping and variable inspection and so on.
Actually there is. The Arduino is using an AVR microcontroller for which AVR offers an IDE (based on Microsoft's Visual Studio) at least for Windows. However the downside is you'll have to get down to C and loose all the sugar the Arduino language (which is based on C++) has to offer. Depending on how good you are with C and how much code you want to stuff into the Arduino it can actually be a good idea.


Also the Arduino might not be the best idea to start out learning programming because its lack of proper debugging tools.
There are ways to debug with Arduino. Just have to print everything out to the serial port. I am confident we will get mtlsub’s program working soon enough.
Resident timer maker. :hi:
Let’s make timers together!
User avatar
Riddle
****
Posts: 1135
Joined: 24 Sep 2008, 08:37
Location: Oregon, USA
Contact:

Re: A Place to share code for arduino/Pi

Post by Riddle »

mtlsub wrote:I thought that might be the case however even if I fail the first suck it does not transmit. Regardless I will work on getting rid of some of the delays and seeing what that does. Thanks for taking a look. I'll take all the help I can get :D
It looks like you may be using pin 12 twice. Try using pin 14 for the transmit pin.

Can you post the working suck code before you added the collar code?

What are you using on pins 12, 8, and 7?

Try using the example code StateChangeDetection under 02. Digital for counting sucks.
Resident timer maker. :hi:
Let’s make timers together!
User avatar
Riddle
****
Posts: 1135
Joined: 24 Sep 2008, 08:37
Location: Oregon, USA
Contact:

Re: A Place to share code for arduino/Pi

Post by Riddle »

Here is a starting point for rewriting your code.

Code: Select all

/*
Originally developed by Smouldery and Mikey who very kindly allowed us the use of their code. 

Link to Smoulderys original code: https://github.com/smouldery/shock-collar-control/blob/master/Arduino%20Modules/transmitter_vars.ino

adapted by Deviant Designs
*/


const byte buttonPin = 9;

//=================================================== START OF COLLAR SETUP CODE ======================================================================

//const int shock_min = 0; // Minimum of power a command will be executed at
const int shock_delay = 10; // Maximum rate at which the shock function can be used at
//const int cmd_max = 1000; // Maximum of milliseconds which a command can be executed at

// Constant variables
const int pin_led = LED_BUILTIN; // Pin for indication LED
const int pin_rtx =  14; // Pin to transmit over
const String key = "00101100101001010"; // Key of the transmitter, dont touch if you dont know how it works

// Variables which do change
int collar_chan = 0; // Can be channel 0 or 1
int collar_duration = 500; // Duration of the command in milliseconds
int collar_power = 10; // Strength of the command, can be 0-100, but will be limited by shock_min and shock_max

// Define values for easier recognition
#define COLLAR_LED 1
#define COLLAR_BEEP 2
#define COLLAR_VIB 3
#define COLLAR_ZAP 4

// Strings used for building up the command sequence
String sequence, power, channelnorm, channelinv, modenorm, modeinv;

// Store the last time anything was transmitted to the collar
unsigned long transmit_last = 0;
unsigned long shock_last = 0;


void collar_keepalive()
{
  if (millis() - transmit_last >= 120000) // Send command to the collar at least every 2 minutes to make it stay on
  {
    Serial.println("Keep-alive:\tCollar");
    transmit_command(collar_chan, COLLAR_LED, 50);
  }
}

//=================================================== END OF COLLAR SETUP CODE ======================================================================

//================================== Suck Setup =================
// this constant won't change:
#define btnSTART 4 // Change these pins if you want to use different ones
#define btnHIT 2
#define ledTrigger 13
const int  buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 13;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
byte start = 0;              // variable to record program start

unsigned long startMillis;
unsigned long currentMillis;
int times = 50; // number of sucks
long maxTime = (120000); // Backup timer release
//================================== End Suck Setup =================


void setup()
{
//=================================================== START OF COLLAR SETUP CODE ======================================================================
  pinMode(pin_rtx, OUTPUT); // Set transmitter pin as output
  pinMode(pin_led, OUTPUT); // Set LED pin as output
  Serial.begin(115200);
  //=================================================== END OF COLLAR SETUP CODE ======================================================================
  pinMode(buttonPin, INPUT_PULLUP);
  //================================== Suck Setup =================
  pinMode(btnSTART, INPUT_PULLUP);
  pinMode(btnHIT, INPUT);
  pinMode(ledTrigger, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(7, OUTPUT);
//================================== End Suck Setup =================
}


void loop()
{
  collar_keepalive(); // This keeps the collar alive
  button_read(); // This reads the button presses.
  check_suck();  // This checks the suck time
  output_change();  // This changes the outputs
  collar_transmit(); // This transmits commands to the collar as needed
}



void button_read() { // This reads the button presses.
if (digitalRead(buttonPin) == LOW) {
    transmit_command(collar_chan, COLLAR_VIB, 100);
  }
if (digitalRead(btnSTART) == LOW) {
    start = 1;                // Start the program
    startMillis = millis();   // Record the start time
    buttonPushCounter = 0;    // Reset the buttonPushCounter
  }
 buttonState = digitalRead(btnHIT);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter++;
      Serial.println("on");
      Serial.print("number of button pushes: ");
      Serial.println(buttonPushCounter);
    } else {
      // if the current state is LOW then the button went from on to off:
      Serial.println("off");
    }
  }
}


void check_suck() {

}


void output_change() {
  
}


void collar_transmit() {
  transmit_command(collar_chan, COLLAR_VIB, 100);
}


void transmit_command(int c, int m, int p = 0)
{
  transmit_last = millis();
  switch (c) // Check the channel
  {
    case 1: // Channel 1
      channelnorm = "111";
      channelinv = "000";
      break;
    default: // Channel 0
      channelnorm = "000";
      channelinv = "111";
      break;
  }

  switch (m) // Check the mode
  {
    case 1: // Light
      modenorm = "1000";
      modeinv = "1110";
      break;
    case 2: // Beep
      modenorm = "0100";
      modeinv = "1101";
      break;
    case 4: // Shock
      modenorm = "0001";
      modeinv = "0111";
      shock_last = millis();
      break;
    default: // Vibrate
      modenorm = "0010";
      modeinv = "1011";
//      p = 10; // Set strengh to 10 for the command to be executed properly
      break;
  }

  // Convert power to binary
  int zeros = String(p, BIN).length();

  String power;
  for (int i = 0; i < 7 - zeros; i++)
  {
    power = power + "0";
  }
  power = power + String(p, BIN);

  String sequence = "1" + channelnorm + modenorm + key + power + modeinv + channelinv + "00";

  digitalWrite(pin_led, LOW);
//  d = constrain(d, 50, cmd_max); // Clamp duration of the command
  unsigned long cmd_start = millis();
//  while (millis() - cmd_start < d)
//  {
    // start bit
    digitalWrite(pin_rtx, HIGH);
    delayMicroseconds(1500); // wait 1500 uS
    digitalWrite(pin_rtx, LOW);
    delayMicroseconds(741);// wait 741 uS

    for (int n = 0; n < 41 ; n++)
    {
      if (sequence.charAt(n) == '1') // Transmit a one
      {
        digitalWrite(pin_rtx, HIGH);
        delayMicroseconds(741);
        digitalWrite(pin_rtx, LOW);
        delayMicroseconds(247);
      }
      else // Transmit a zero
      {
        digitalWrite(pin_rtx, HIGH);
        delayMicroseconds(247);
        digitalWrite(pin_rtx, LOW);
        delayMicroseconds(741);
      }
    }
    delayMicroseconds(4500);
//  }
  digitalWrite(pin_led, HIGH);
}
Resident timer maker. :hi:
Let’s make timers together!
mtlsub
**
Posts: 170
Joined: 12 Oct 2010, 05:23
Location: Great kinky North

Re: A Place to share code for arduino/Pi

Post by mtlsub »

Thanks! You guys are awesome. I'm not home for a few days but I will try out the code and I'll post the results.
mtlsub
**
Posts: 170
Joined: 12 Oct 2010, 05:23
Location: Great kinky North

Re: A Place to share code for arduino/Pi

Post by mtlsub »

Here is the code that was working using a relay (pin 7) to trigger the remote in stead of an RF transmitter. Pin 8 is the magnet. BtnHIT is now the photoresistor module. (Was a button in a previous prototype) 12 is the vib.

Code: Select all

#define btnSTART 4 // Change these pins if you want to use different ones
#define btnHIT 2
#define ledTrigger 13
unsigned long startMillis;
unsigned long currentMillis;
int times = 50; // number of sucks
long maxTime = (120000); // Backup timer release

void setup() {
  // setup the switches and LED
  pinMode(btnSTART, INPUT_PULLUP);
  pinMode(btnHIT, INPUT);
  pinMode(ledTrigger, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(7, OUTPUT);

  // Turn off the LED
  digitalWrite(ledTrigger, HIGH);
  digitalWrite(12, HIGH);
  digitalWrite(8, HIGH);
  digitalWrite(7, HIGH);

  // Start the Serial port
  Serial.begin(9600);
  Serial.println("Arduino Suck Light");
  Serial.println();
  // Randomize the random number generator
  randomSeed(analogRead(0));
  

}
void period1() {
  currentMillis = millis();  //get the current "time" (actually the number of milliseconds since the program started)
  if (currentMillis - startMillis >= maxTime)  //test whether the period has elapsed{
    digitalWrite (8, HIGH); // Electro magnet release
    
  delay (100);
}




void loop() {
  long timeReaction;
  long timeTotal = 0;



  Serial.println("Press START button when ready");

  // Wait for the START button
  while (digitalRead(btnSTART)) {}
  // Then wait for it to be released
  delay(10);
  while (!digitalRead(btnSTART)) {}
  digitalWrite(8, LOW);
  delay(1000);
  startMillis = millis();


  // Start the testing round
  for (int i = 0; i < times; i++) {

    delay(random(300, 900));
    timeReaction = millis();
    digitalWrite(ledTrigger, LOW);
    Serial.println ("SUCK");

    // Wait for btnHit
    while (!digitalRead(btnHIT)) {
      period1();
    }

    timeReaction = millis() - timeReaction;
    timeTotal += timeReaction;
    delay(10);
    while (digitalRead(btnHIT)) {}
    digitalWrite(ledTrigger, HIGH);
    Serial.println(i + 1);
    //Serial.print(": "); used for debuging
    //Serial.print(timeReaction); used for debuging
    //Serial.println(" msec"); used for debuging
    delay(500);

    if (timeReaction <= 2000) {
      digitalWrite (12, LOW);
      Serial.println ("GOOD");
      delay (800);
      digitalWrite (12, HIGH);
    }
    if (timeReaction >= 2001) {
      digitalWrite (7, LOW);
      Serial.println ("FASTER");
      delay (500);
      digitalWrite (7, HIGH);


    }
  }
  // The set of 5 is done, show the results
  Serial.println();
  Serial.print("Avg = ");
  Serial.print(timeTotal / times);
  Serial.println(" msec");
  Serial.println();
  

  if (timeTotal / times >= 2001) {
    digitalWrite (7, LOW);
    delay (500);
    digitalWrite (7, HIGH);
    delay (500);
    digitalWrite (7, LOW);
    delay (500);
    digitalWrite (7, HIGH);
    digitalWrite (8, HIGH);
  }
  if (timeTotal / times <= 2000) {
    digitalWrite (12, LOW);
    delay (4000);
    digitalWrite (12, HIGH);
    digitalWrite (8, HIGH);
  }

}
mtlsub
**
Posts: 170
Joined: 12 Oct 2010, 05:23
Location: Great kinky North

Re: A Place to share code for arduino/Pi

Post by mtlsub »

I tested the code you guys gave me and it doesn't work as is i get the error Transmit Command not declared. I have not tried to join the codes yet.
I can sort of debug stuff that is working by trying to fix it and when it stops working hitting undo but I am not proficient enough to simply read code and know what is wrong.
User avatar
Riddle
****
Posts: 1135
Joined: 24 Sep 2008, 08:37
Location: Oregon, USA
Contact:

Re: A Place to share code for arduino/Pi

Post by Riddle »

This will compile.

Code: Select all

/*
Originally developed by Smouldery and Mikey who very kindly allowed us the use of their code. 

Link to Smoulderys original code: https://github.com/smouldery/shock-collar-control/blob/master/Arduino%20Modules/transmitter_vars.ino

adapted by Deviant Designs
*/


const byte buttonPin = 9;

//=================================================== START OF COLLAR SETUP CODE ======================================================================

//const int shock_min = 0; // Minimum of power a command will be executed at
const int shock_delay = 10; // Maximum rate at which the shock function can be used at
//const int cmd_max = 1000; // Maximum of milliseconds which a command can be executed at

// Constant variables
const int pin_led = LED_BUILTIN; // Pin for indication LED
const int pin_rtx =  14; // Pin to transmit over
const String key = "00101100101001010"; // Key of the transmitter, dont touch if you dont know how it works

// Variables which do change
int collar_chan = 0; // Can be channel 0 or 1
int collar_duration = 500; // Duration of the command in milliseconds
int collar_power = 10; // Strength of the command, can be 0-100, but will be limited by shock_min and shock_max

// Define values for easier recognition
const int COLLAR_LED = 1;
const int COLLAR_BEEP = 2;
const int COLLAR_VIB = 3;
const int COLLAR_ZAP = 4;

// Strings used for building up the command sequence
String sequence, power, channelnorm, channelinv, modenorm, modeinv;

// Store the last time anything was transmitted to the collar
unsigned long transmit_last = 0;
unsigned long shock_last = 0;




//=================================================== END OF COLLAR SETUP CODE ======================================================================

//================================== Suck Setup =================
// this constant won't change:
const byte btnSTART = 4; // Change these pins if you want to use different ones
const byte btnHIT = 2;
const byte ledTrigger = 13;
const byte electroMag = 8;
const byte vib = 12;
//const byte buttonPin = 3;    // the pin that the pushbutton is attached to
const byte ledPin = 13;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
byte start = 0;              // variable to record program start

unsigned long startMillis;    // Variable to track the start time
unsigned long currentMillis;  // variable to track the current time
int times = 50; // number of sucks
long maxTime = (120000); // Backup timer release

long timeReaction;
long timeTotal = 0;
//================================== End Suck Setup =================
//=============================== Transmit Command Below =============
//=============================== Do NOT Modify Code =================

void transmit_command(int c, int m, int p = 0)
{
  transmit_last = millis();
  switch (c) // Check the channel
  {
    case 1: // Channel 1
      channelnorm = "111";
      channelinv = "000";
      break;
    default: // Channel 0
      channelnorm = "000";
      channelinv = "111";
      break;
  }

  switch (m) // Check the mode
  {
    case 1: // Light
      modenorm = "1000";
      modeinv = "1110";
      break;
    case 2: // Beep
      modenorm = "0100";
      modeinv = "1101";
      break;
    case 4: // Shock
      modenorm = "0001";
      modeinv = "0111";
      shock_last = millis();
      break;
    default: // Vibrate
      modenorm = "0010";
      modeinv = "1011";
//      p = 10; // Set strengh to 10 for the command to be executed properly
      break;
  }

  // Convert power to binary
  int zeros = String(p, BIN).length();

  String power;
  for (int i = 0; i < 7 - zeros; i++)
  {
    power = power + "0";
  }
  power = power + String(p, BIN);

  String sequence = "1" + channelnorm + modenorm + key + power + modeinv + channelinv + "00";

  digitalWrite(pin_led, LOW);
//  d = constrain(d, 50, cmd_max); // Clamp duration of the command
  unsigned long cmd_start = millis();
//  while (millis() - cmd_start < d)
//  {
    // start bit
    digitalWrite(pin_rtx, HIGH);
    delayMicroseconds(1500); // wait 1500 uS
    digitalWrite(pin_rtx, LOW);
    delayMicroseconds(741);// wait 741 uS

    for (int n = 0; n < 41 ; n++)
    {
      if (sequence.charAt(n) == '1') // Transmit a one
      {
        digitalWrite(pin_rtx, HIGH);
        delayMicroseconds(741);
        digitalWrite(pin_rtx, LOW);
        delayMicroseconds(247);
      }
      else // Transmit a zero
      {
        digitalWrite(pin_rtx, HIGH);
        delayMicroseconds(247);
        digitalWrite(pin_rtx, LOW);
        delayMicroseconds(741);
      }
    }
    delayMicroseconds(4500);
  digitalWrite(pin_led, HIGH);
}

void setup()
{
//=================================================== START OF COLLAR SETUP CODE ======================================================================
  pinMode(pin_rtx, OUTPUT); // Set transmitter pin as output
  pinMode(pin_led, OUTPUT); // Set LED pin as output
  Serial.begin(115200);
  //=================================================== END OF COLLAR SETUP CODE ======================================================================
  pinMode(buttonPin, INPUT_PULLUP);
  //================================== Suck Setup =================
  pinMode(btnSTART, INPUT_PULLUP);
  pinMode(btnHIT, INPUT);
  pinMode(ledTrigger, OUTPUT);
  pinMode(vib, OUTPUT);
  pinMode(electroMag, OUTPUT);
//================================== End Suck Setup =================
}


void loop()
{
  collar_keepalive(); // This keeps the collar alive
  button_read(); // This reads the button presses.
  check_suck();  // This checks the suck time
  output_change();  // This changes the outputs
  collar_transmit(); // This transmits commands to the collar as needed
  cerial();          // This sends Serial data for debug
}



void button_read() { // This reads the button presses.
if (digitalRead(buttonPin) == LOW) {
    
  }
if (digitalRead(btnSTART) == LOW) {
    start = 1;                // Start the program
    startMillis = millis();   // Record the start time
    buttonPushCounter = 0;    // Reset the buttonPushCounter
    digitalWrite(electroMag, LOW); // Turn on the electromagnet
  }
 buttonState = digitalRead(btnHIT);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter++;
      Serial.println("on");
      Serial.print("number of button pushes: ");
      Serial.println(buttonPushCounter);
    } else {
      // if the current state is LOW then the button went from on to off:
      Serial.println("off");
    }
  }
}


void check_suck() {

}


void output_change() {  // This is to change the outputs
  check_release();      // This checks the release time
  vib_status();         // This turns the vib on and off
}


void collar_transmit() {
  transmit_command(collar_chan, COLLAR_VIB, 100);
}

void check_release() {  // This code checks to see if the release time has occured
  currentMillis = millis();  //get the current "time" (actually the number of milliseconds since the program started)
  if (currentMillis - startMillis >= maxTime)  //test whether the period has elapsed{
    digitalWrite (electroMag, HIGH); // Electro magnet release
    start = 0;    // Stops the program
    startMillis = 0; // Resets the startMillis
    Serial.println("Time has expired");
}


void vib_status() { // This code starts and stops the vib
  
}


void cerial() {
  if(start==0) {
    Serial.println("Press START button when ready");
  }
}


void collar_keepalive()
{
  if (millis() - transmit_last >= 120000) // Send command to the collar at least every 2 minutes to make it stay on
  {
    Serial.println("Keep-alive:\tCollar");
    transmit_command(collar_chan, COLLAR_LED, 50);
  }
}

Resident timer maker. :hi:
Let’s make timers together!
User avatar
Riddle
****
Posts: 1135
Joined: 24 Sep 2008, 08:37
Location: Oregon, USA
Contact:

Re: A Place to share code for arduino/Pi

Post by Riddle »

I changed the loop to be as follows:

Code: Select all

void loop()
{
  collar_keepalive(); // This keeps the collar alive
  button_read(); // This reads the button presses.
  check_suck();  // This checks the suck time
  output_change();  // This changes the outputs
  collar_transmit(); // This transmits commands to the collar as needed
  cerial();          // This sends Serial data for debug
}
We need to work together to write the following functions:
check_suck(); // This checks the suck time
output_change(); // This changes the outputs
collar_transmit(); // This transmits commands to the collar as needed

We need to write these functions so that they do not take long and the rest of the program may run. The collar needs a command every 2 minutes or it will shut down.
Resident timer maker. :hi:
Let’s make timers together!
Ballgag-lover
*
Posts: 7
Joined: 27 Mar 2013, 00:48

Re: A Place to share code for arduino/Pi

Post by Ballgag-lover »

someone has a version of pain.exe that runs on win10?
User avatar
kinbaku
*****
Posts: 5047
Joined: 10 Jan 2020, 20:26
Location: Belgium

Re: A Place to share code for arduino/Pi

Post by kinbaku »

I once bought the following set of advanced sensors for the Arduino (http://linksprite.com/wiki/index.php?ti ... or_Arduino).
It has many options for bondage testing. Certainly if you have just started with Arduino programs.
User avatar
GeneralError
**
Posts: 141
Joined: 16 Sep 2019, 15:30
Location: Germany

Re: A Place to share code for arduino/Pi

Post by GeneralError »

kinbaku wrote:I once bought the following set of advanced sensors for the Arduino (http://linksprite.com/wiki/index.php?ti ... or_Arduino).
It has many options for bondage testing. Certainly if you have just started with Arduino programs.
Oh yes, there is a huge kinkify potential ;-)
A tilt switch for example could be fixed to someones back or head to enforce a devot posture
User avatar
IcarusBurned
*
Posts: 32
Joined: 07 Mar 2020, 22:37

Re: A Place to share code for arduino/Pi

Post by IcarusBurned »

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
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 :D

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! :rofl:

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
     }    
}
Post Reply