What’s going on?
I’m not that fond of vibrating transmitter’s, I’m perfectly fine without it. Of course that does not apply to all and since I like to fiddle my brain especially with things that can be invented I took on myself to start thinking “how to make a Jeti transmitter vibrate”.
NOTE: Updated 09.04.2016 to v1.2 (Code and downloadable sketch for arduino, more info here)
Rules of engagement
Of course there are some rules I thought would be good to follow:
- No permanent modifications to transmitter
- No visible modifications to transmitter
- Must be controllable only to specific alarms or other triggering
- Cheap
- Possible to make as DIY by anyone
- Should be usable in DC/DS-14/16/24
How to get the signal to “Jeti Vibrator”?
Since I did not want to make any permanent modifications like soldering etc on the transmitter my eyes quickly stopped on the internal PPM-output. That is basically only point of interest inside transmitter that is free to use and can be programmed.
Ok. So now we know that we are using PPM. This is where there are limitations. Users use the channels differently, we are therefor limited to channels 1-16 (1-14 in DC/DS-14) and this of course means there are users who cannot use this since all channels are used. If user can move a channel, good. SO, this is not for absolutely everyone, unfortunately. But, hey, read on, it’s pretty cool :)
The internal PPM-output is pretty versatile. It has a unregulated output with 3.2-4.2V and maximum 1A current. The 1A is by far good to what we have planned.
Signal processing
So, we have now decided to use PPM and only one channel. Also we have decided to use the unregulated power-output in PPM-connector. Time to hunt for some parts.
Solution is surprisingly easy, with processing power measured it’s total overkill but hey, we like that don’t we? :)
- Arduino Pro Mini (5V 8Mhz or 16Mhz)
- DC-DC Step-Up to 5VDC board (1A max is enough) <- Not necessarily needed!
- Vibrator motor suitable to 5VDC operation
- 2N7000 N-FET for motor-control
- Resistors (100Ohm & 10kOhm)
- One diode (For example 1N4001)
- One capacitor 10uF 10V
- PPM-connector (Almost impossible to find, buy one JMU-KTU for two connectors)
Connections are pretty simple in the end. Power from PPM-output to DC-DC Step-Up board, from step-up’s output to motor and feed to FET. We need a fet, the vibrating motor I bought from internet draws 70mA and Arduino output is limited to 40mA, so a FET is suitable to this.
The diode is to protect FET from back-current from motor. (Or motors, I’m might have two for the permanent installation)
The 10kOhm resistor between output 3 and GND is a pull-down, good for persistent signaling. Also the 100Ohm resistor for FET’s gate is to make sure there’s no irregularities.
Cut lead 1 from the JMU-KTU PPM-cable, we only need positive, negatove and PPM out.
The PPM-port has a 1A limit (from Jeti manual) so we are clearly within the limit’s, Arduino takes ~30mA, vibrating motor’s take ~90mA a piece (depends on model) so when I’m running prototype the current consumption was being around ~200mA which is 1/5th of allowed maximum. To prevent the motorload affecting PPM-output too much I added a 10uF capacitor to input on step-up board.
Schematic
Quite easy to install, not that much to solder:
I moved the output from 3 to 4 later since I soldered the resistor straight to board with FET. I then drew the VCC and GND from step-up board to the arduino’s VCC and GND on the lower-row for easy programming. This is all up to you, both works.
The end-result is small enough to put inside the transmitter.
This is still a prototype so the wires are way too long.
When choosing the vibrator motor I was a afraid that it would not be noticeable due the weight of Jeti transmitter, and I was right. Finally I found it, Xbox controllers motor, works as a charm! Also there’s two motors in a controller, choose one to your liking! Difference is in vibration force.
When installing inside transmitter make sure no cables are touching gimbals, make sure everything (especially the motor) is secured in position in good fashion.
It’s also possible to make the board without step-up. Advantage is a bit smaller footprint, downside is that vibration “feeling” is not consistent when battery voltage in transmitter goes down during use. Choice is yours. (Difference is very small)
Whole installation could look like this with some 3D-printed parts to make a good installation:
Here’s a picture of my not so good soldering-skills, it does make small footprint, very easy to install neatly inside transmitter.
Next is time for some nerd-stuff!
Arduino programming
This was fun. My first arduino-application, at least first one where the whole code was not just copu paste. It took a while to find a suitable PPM-reading example until I found the Read_any_PPM_V05 made by Hasi some time ago. This was the first that I found reliable. I did however need to adjust timings due the fact that Jeti has a wider signal width on the servos than others.
As a “real programmer” I even commented the code just so i would not have to write everything here. Laziness is the mother of all good deeds :)
Code and sketch is available on Github: https://github.com/RC-Thoughts/Jeti_Vibe
Function is simple, when designated channel is at 100% vibrator is activated in pulse-mode 0.7 seconds ON, 0.15 seconds OFF until channel-value is lowered. Also there’s a faster vibration pattern in start of transmitter. (Note that PPM-port is powered already when Jeti ask’s you the “really wanna start me”-question. It’s easy code, just change these to Your liking!
/*
Jeti Transmitter Vibrator v1.2
For use with DC/DS-16/24 internal PPM-output
- Vibrates three times on radio power-up
- Vibration off when PPM-channel 14/16 (Selectable) is -100%
- 10 different vibration patterns when channel 14/16 is used between -100% and +100%
- Motor Off at 0% or if no signal
- Internal Led (Red) follows motor for debugging purpose when no computer is connected
by Tero Salminen 2016 www.RC-Thoughts.com
Based on Read_any_PPM_V05 by Hasi http://www.rcgroups.com/forums/showthread.php?t=1808432
*/
// Define functions and constants
#define Motor_Pin 4 // Motor and Led drive (N-FET Gate, Source to GND, Drain to motor negative)
#define PPM_Pin 2 // This must be 2 or 3
#define Led_Pin 13 // Internal led
#define multiplier (F_CPU/8000000) // Do not touch - needed for PPM timing also
int ppm[16]; // Array for storing Jeti PPM16 output
// Only one of these should be uncommentented!
#define chan (ppm[13]) // Uncomment for DC/DC-14 (PPM-channel 14)
//#define chan (ppm[15]) // Uncomment for DC/DS-16/24 (PPM-channel 16)
void setup() // Do not touch unless you know what you are doing
{
Serial.begin(115200);
Serial.println("ready");
pinMode(PPM_Pin, INPUT);
pinMode(Motor_Pin, OUTPUT);
pinMode(Led_Pin, OUTPUT);
attachInterrupt(PPM_Pin - 2, read_ppm, CHANGE);
TCCR1A = 0;
TCCR1B = 0;
TCCR1B |= (1 << CS11);
for (int i = 0; i <= 2; i++) { // Run motor in start three times
digitalWrite(Motor_Pin, HIGH);
digitalWrite(Led_Pin, HIGH);
delay(200); // ON period for motor in ms
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW);
delay(100); // OFF period for motor in ms
}
}
void loop()
{
Serial.print("CH value: "); // Print channelvalue to serial port for debug
Serial.print(chan);
Serial.println("");
delay(50);
if (chan < 10) { // Motor off if no signal
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW);
Serial.print("No signal");
}
else if (chan > 1480 && chan < 1511) { // Zero position -0% Motor off
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW);
Serial.print("0%");
}
else if (chan > 10 && chan < 1091) { // Position 1 <-80%
digitalWrite(Motor_Pin, HIGH);
digitalWrite(Led_Pin, HIGH);
Serial.print("Pos 1");
}
else if (chan > 1090 && chan < 1191) { // Position 2 -70%
digitalWrite(Motor_Pin, HIGH);
digitalWrite(Led_Pin, HIGH);
delay(900); // ON period for motor in ms
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW);
delay(300); // OFF period for motor in ms
Serial.print("Pos 2");
}
else if (chan > 1190 && chan < 1291) { // Position 3 -50%
digitalWrite(Motor_Pin, HIGH);
digitalWrite(Led_Pin, HIGH);
delay(900); // ON period for motor in ms
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW);
delay(200); // OFF period for motor in ms
Serial.print("Pos 3");
}
else if (chan > 1290 && chan < 1391) { // Position 4 -30%
digitalWrite(Motor_Pin, HIGH);
digitalWrite(Led_Pin, HIGH);
delay(700); // ON period for motor in ms
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW);
delay(200); // OFF period for motor in ms
Serial.print("Pos 4");
}
else if (chan > 1390 && chan < 1481) { // Position 5 -10%
digitalWrite(Motor_Pin, HIGH);
digitalWrite(Led_Pin, HIGH);
delay(600); // ON period for motor in ms
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW);
delay(100); // OFF period for motor in ms
Serial.print("Pos 5");
}
else if (chan > 1510 && chan < 1591) { // Position 6 10%
digitalWrite(Motor_Pin, HIGH);
digitalWrite(Led_Pin, HIGH);
delay(500); // ON period for motor in ms
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW);
delay(100); // OFF period for motor in ms
Serial.print("Pos 6");
}
else if (chan > 1590 && chan < 1691) { // Position 7 30%
digitalWrite(Motor_Pin, HIGH);
digitalWrite(Led_Pin, HIGH);
delay(400); // ON period for motor in ms
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW);
delay(100); // OFF period for motor in ms
Serial.print("Pos 7");
}
else if (chan > 1690 && chan < 1791) { // Position 8 50%
digitalWrite(Motor_Pin, HIGH);
digitalWrite(Led_Pin, HIGH);
delay(300); // ON period for motor in ms
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW);
delay(75); // OFF period for motor in ms
Serial.print("Pos 8");
}
else if (chan > 1790 && chan < 1891) { // Position 9 70%
digitalWrite(Motor_Pin, HIGH);
digitalWrite(Led_Pin, HIGH);
delay(200); // ON period for motor in ms
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW); delay(50); // OFF period for motor in ms
Serial.print("Pos 9");
} else if (chan > 1890) { // Position 10 90%
digitalWrite(Motor_Pin, HIGH);
digitalWrite(Led_Pin, HIGH);
delay(150); // ON period for motor in ms
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW);
delay(25); // OFF period for motor in ms
Serial.print("Pos 10");
}
else {
digitalWrite(Motor_Pin, LOW);
digitalWrite(Led_Pin, LOW); // If nothing matches then motor off
Serial.print("No match");
}
}
// Do not touch anything below this
void read_ppm() {
static unsigned int pulse;
static unsigned long counter;
static byte channel;
static unsigned long last_micros;
counter = TCNT1;
TCNT1 = 0;
if (counter < 510 * multiplier) { // Must be a pulse if less than 510us
pulse = counter;
}
else if (counter > 1910 * multiplier) { // Sync pulses over 1910us
channel = 0;
}
else { // Servo values between 510us and 2420us will end up here
ppm[channel] = (counter + pulse) / multiplier;
channel++;
}
}
That should be pretty simple, go from there, modify to your liking (timings for example), that’s what open-source is all about :)
Channel values and functions (v1.2):
- Channel -90% = Motor ON
- Channel -80% = Pattern 1 (Slow)
- Channel -70% = Pattern 2
- Channel -50% = Pattern 3
- Channel -30% = Pattern 4
- Channel -10% = Pattern 5
- Channel 10% = Pattern 6
- Channel 30% = Pattern 7
- Channel 50% = Pattern 8
- Channel 70% = Pattern 9
- Channel 90% = Pattern 10 (Fast)
Making Jeti vibrate, finally!
Now that we have hardware fixed let’s fix the settings to Jeti in order.
Make sure You are using Jeti PPM16 Positive in your system-settings.
Next step is to create a new function, choose any available slot, in my case first free was 8, name it like “Vibe” or similar way:
Here you can see that I’m activating the channel with telemetry control. (This is not a good idea for permanent solution, you can use only one telemetry-alarm this way.)
Now it’s time to assign the function to a channel, I’m using the last available channel in my loaner DS-14 so it’s channel 14:
Idea is simple, Jeti vibrates when channel 14 or 16 (Depending on your Arduino-setup) is on 100%.
One way to accomplish this is to use a telemetry control to control the channel:
I would suggest to use logical switches or whatever to launch the vibrator, a bit more versatile. “If battery voltage is too low or timer is up then vibrate”-fashion, you know.
Here’s the vibrator in action:
Final words
Yes, I know this is not the perfect solution. It’s not as versatile as a “real” vibration would be. BUT. It works, it’s inside specs, it’s not permanent, it’s removable without a trace.
And, it’s cheap, total cost around 10€.
So, start doing some DIY or go out flying :)
I got a question: "Why do you have the Step-Up board? Ardu runs fine without it?"
That is true. But the vibrating characteristics of the motor changes a lot between the voltage-range the PPM-output provides. No, it's not a necessity, yes, it is recommended for a constant user-experience.
Hi RC-T.
Only to be correct, in the guide you are speaking about slot 7 but you are using slot 8
''Next step is to create a new function, choose any available slot, in my case forst free was 7, name it like “Vibe” or similar way:
Screen019'''
You're so right, corrected. Thanks!
Impressive! I really like your website Tero!
Grat content buddy, keep updating your site
Hi Tero,
Awesome mod! Even for DS-14/16Gen 2 radios. Can you publish the stl files for the 3D printed parts?
Thank you!
I searched all very old backups I have and unfortunately stl’s were not there, sorry :/