No1sonuk Posted May 27, 2021 Posted May 27, 2021 Not sure if here is the right place for this, but here goes: I thought I'd share a device I've made for quickly working out the servo times for gauge positions. I don't have photos of mine at the moment because I made it on breadboard and dismantled it when I was done. Here's the schematic: Here's the Arduino Code: /* Servo calibration program * Designed to be used to determine the times of servo pulses for setting up * flight simulator gauges. * * Pin assignments are for Arduino Nano. Other arduinos my work without modifying * the code, but you may need to adjust pin numbers dependent on function. * * The display used is a 20x4 character LCD with I2C backpack. * The two ceramic 100nF capacitors kill noise from the rotary switch. * * Potentiometer code is included, but disabled as the rotary encoder * is more reliable. * * Written by Tim Jacobs * Publicised May 2021 */ #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 20 chars and 4 line display #include <Servo.h> Servo myservo; byte vertline0[8] = {16,16,16,16,16,16,16,}; // Custom character bitmaps for bars byte vertline1[8] = {8,8,8,8,8,8,8,}; byte vertline2[8] = {4,4,4,4,4,4,4,}; byte vertline3[8] = {2,2,2,2,2,2,2,}; byte vertline4[8] = {1,1,1,1,1,1,1,}; unsigned int ServoTimeOutput; unsigned int ServoTimeOutputLast; unsigned int ServoTimeMin; unsigned int ServoTimeMax; unsigned int ServoTimeMid; unsigned int manualpos; int ServoTimeIncrement; int bar_charNo; int bar_number; int bar_character; #define ServoPin 6 // Servo output pin (PWM pin) #define PotPin A3 // NOT USED #define encoder0PinA 2 // Encoder input A (Interrupt pin) #define encoder0PinB 3 // Encoder input B (Interrupt pin) #define encoder0Btn 7 // Encoder button input (Any input pin) // A4 and A5 are automatically defined for I2C use by the WIRE library int encoder0PinALast = LOW; int n = LOW; int ButtonState; int ButtonStateLast; int incrementButtonClicks; void setup() { pinMode(encoder0PinA, INPUT_PULLUP); pinMode(encoder0PinB, INPUT_PULLUP); pinMode(encoder0Btn, INPUT_PULLUP); attachInterrupt(0, encoderRead, CHANGE); attachInterrupt(1, encoderRead, CHANGE); lcd.init(); // initialize the lcd lcd.createChar(0, vertline0); // Custom character definition lcd.createChar(1, vertline1); lcd.createChar(2, vertline2); lcd.createChar(3, vertline3); lcd.createChar(4, vertline4); lcd.backlight(); lcd.setCursor(0,0); // column, row ServoTimeMin = 500; // safe defaults ServoTimeMax = 2300; ServoTimeMid = ((ServoTimeMax-ServoTimeMin)/2)+ServoTimeMin; ServoTimeOutput = ServoTimeMid; // set servo to mid-point incrementButtonClicks = 1; // Set default increment digit 0 = 1, 3 = 1000 myservo.attach(ServoPin); myservo.writeMicroseconds(ServoTimeOutput); // } void loop() { // PotInput(); incrementButtonRead(); TimeBarDisplay(); delay(10); } void incrementButtonRead(){ ButtonState = digitalRead(encoder0Btn); if (ButtonState != ButtonStateLast){ if (ButtonState == LOW){ incrementButtonClicks = incrementButtonClicks + 1; if (incrementButtonClicks >=4){incrementButtonClicks = 0;} } ButtonStateLast = ButtonState; switch (incrementButtonClicks) { case 0: ServoTimeIncrement = 1; break; case 1: ServoTimeIncrement = 10; break; case 2: ServoTimeIncrement = 100; break; case 3: ServoTimeIncrement = 1000; break; } manualCursorBlink(); // Sets manual mode cursor position to blink } } // Potentiometer input not used void PotInput(){ manualpos = analogRead(PotPin); ServoTimeOutput = map(manualpos, 0, 1023, ServoTimeMin, ServoTimeMax); } void TimeBarDisplay(){ if (ServoTimeOutput >= ServoTimeMax){ ServoTimeOutput = ServoTimeMax; } if (ServoTimeOutput <= ServoTimeMin){ ServoTimeOutput = ServoTimeMin; } if (ServoTimeOutput != ServoTimeOutputLast){ // lcd.clear(); // May take too much time. lcd.setCursor(0,1); lcd.print("Servo output: "); // Clear the line if (ServoTimeOutput >= 1000){ lcd.setCursor(14,1); } else{ lcd.setCursor(15,1); } lcd.print(ServoTimeOutput); lcd.print("us "); lcd.setCursor(0,2); lcd.print(ServoTimeMin); ServoTimeMid = ((ServoTimeMax-ServoTimeMin)/2)+ServoTimeMin; lcd.setCursor(8,2); lcd.print(ServoTimeMid); lcd.setCursor(16,2); lcd.print(ServoTimeMax); lcd.setCursor(0,3); lcd.print(" "); // Clear the line bar_number = map(ServoTimeOutput, ServoTimeMin, ServoTimeMax, 0, 99); bar_charNo = bar_number/5; bar_character = (bar_number) - (bar_charNo * 5); lcd.setCursor(bar_charNo,3); lcd.write (bar_character); myservo.writeMicroseconds(ServoTimeOutput); ServoTimeOutputLast = ServoTimeOutput; manualCursorBlink(); // Sets manual mode cursor position to blink } } void manualCursorBlink() { // Sets manual mode cursor position to blink lcd.cursor(); // Straight underline cursor // lcd.blink(); // Full block flashing cursor switch (ServoTimeIncrement) { case 1: lcd.setCursor(17,1); break; case 10: lcd.setCursor(16,1); break; case 100: lcd.setCursor(15,1); break; case 1000: lcd.setCursor(14,1); break; } } void encoderRead() { n = digitalRead(encoder0PinA); if ((encoder0PinALast == LOW) && (n == HIGH)) { if (digitalRead(encoder0PinB) == HIGH) { ServoTimeOutput = ServoTimeOutput - ServoTimeIncrement; } else { ServoTimeOutput = ServoTimeOutput + ServoTimeIncrement; } } encoder0PinALast = n; } Usage: Connect the device to the servo that drives a gauge. When the device first starts, it has some "safe" min and max servo times set. These can be changed if you want, but I just went with the numbers given. To adjust the time, turn the rotary encoder and the selected digit will change, and also the servo output. Pressing the rotary encoder button changes the selected digit. You can then adjust the time until the gauge needle reaches the required end positions and note the times to put into the DCS servo lines. Note that if your servo draws too much current, it could overload the arduino's regulator and "brown-out" the arduino. In this case, you might have to drive the circuit (or just the servo) off an external 5V supply. I hope that helps someone. I may one day get around to building a permanent version 1
crash test pilot Posted May 28, 2021 Posted May 28, 2021 Wonderful! Thanks for a really handy tool! A note: even the small 9g servo draws up to 700 mA, which is far too much for a nano (max 200mA), so an external power source is a must.
No1sonuk Posted May 29, 2021 Author Posted May 29, 2021 On 5/28/2021 at 9:02 PM, crash test pilot said: Wonderful! Thanks for a really handy tool! A note: even the small 9g servo draws up to 700 mA, which is far too much for a nano (max 200mA), so an external power source is a must. Agreed. Though I've not had much trouble driving one or two 9g servos from a Nano - they don't tend to be pulling hard when used in gauges. I may have just been lucky.
DH14300 Posted June 2, 2021 Posted June 2, 2021 A few years ago I invested in about $2000 worth of Flight Illusion gauges and use them with P3D and X-Plane. Does anyone know how or where I might find someone who might be able to create a software interface to drive them?
No1sonuk Posted June 3, 2021 Author Posted June 3, 2021 On 6/2/2021 at 2:43 PM, DH14300 said: A few years ago I invested in about $2000 worth of Flight Illusion gauges and use them with P3D and X-Plane. Does anyone know how or where I might find someone who might be able to create a software interface to drive them? You should probably create your own new topic so more people could see your question.
DH14300 Posted June 4, 2021 Posted June 4, 2021 This question was intentionally directed to gauge builders.
No1sonuk Posted June 5, 2021 Author Posted June 5, 2021 On 6/4/2021 at 1:06 PM, DH14300 said: This question was intentionally directed to gauge builders. This part of the forum is pretty much ALL gauge builders, and you'll get more of a response in a separate thread because people skip threads they're not interested in. It's entirely possible someone out there knows the answer to your question, but isn't reading this topic. It would also make it easier for others to find the info later. Unless you're one of those annoying people who delete their posts after getting the info they want...
DH14300 Posted June 7, 2021 Posted June 7, 2021 I appreciate your guidance here. To make sure I understand you correctly, start a new thread under the 'Home Cockpits' category or 'Inputs and Outputs'? Thank you.
No1sonuk Posted June 7, 2021 Author Posted June 7, 2021 That's a tricky one, but I'd suggest trying Inputs and outputs first.
No1sonuk Posted September 14, 2021 Author Posted September 14, 2021 On 5/28/2021 at 12:29 AM, No1sonuk said: I may one day get around to building a permanent version "One day" has arrived... This is behind the face: A close-up of the Arduino and encoder: The screw in the corner of the Arduino is securing it in the holder. In order to make the wiring a bit easier, I switched around some signals. This includes using D11 for the servo as that meant I could wire that into the ICSP header holes. New schematic: And the new code: /* Servo calibration program * Designed to be used to determine the times of servo pulses for setting up * flight simulator gauges. * * Pin assignments are for Arduino Nano. Other arduinos my work without modifying * the code, but you may need to adjust pin numbers dependent on function. * * The display used is a 20x4 character LCD with I2C backpack. * The two ceramic 100nF capacitors kill noise from the rotary switch. * * Potentiometer code is included, but disabled as the rotary encoder * is more reliable. * * Written by Tim Jacobs * Publicised September 2021 */ #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 20 chars and 4 line display #include <Servo.h> Servo myservo; byte vertline0[8] = {16,16,16,16,16,16,16,}; // Custom character bitmaps for bars byte vertline1[8] = {8,8,8,8,8,8,8,}; byte vertline2[8] = {4,4,4,4,4,4,4,}; byte vertline3[8] = {2,2,2,2,2,2,2,}; byte vertline4[8] = {1,1,1,1,1,1,1,}; unsigned int ServoTimeOutput; unsigned int ServoTimeOutputLast; unsigned int ServoTimeMin; unsigned int ServoTimeMax; unsigned int ServoTimeMid; unsigned int manualpos; int ServoTimeIncrement; int bar_charNo; int bar_number; int bar_character; #define ServoPin 11 // Servo output pin (PWM pin)(Also available on the ICSP header) #define PotPin A3 // NOT USED #define encoder0PinA 3 // Encoder input A (Interrupt pin) #define encoder0PinB 2 // Encoder input B (Interrupt pin) #define encoder0Btn 4 // Encoder button input (Any input pin) // A4 and A5 are automatically defined for I2C use by the WIRE library int encoder0PinALast = LOW; int n = LOW; int ButtonState; int ButtonStateLast; int incrementButtonClicks; void setup() { pinMode(encoder0PinA, INPUT_PULLUP); pinMode(encoder0PinB, INPUT_PULLUP); pinMode(encoder0Btn, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(encoder0PinA), encoderRead, CHANGE); attachInterrupt(digitalPinToInterrupt(encoder0PinB), encoderRead, CHANGE); lcd.init(); // initialize the lcd lcd.createChar(0, vertline0); // Custom character definition lcd.createChar(1, vertline1); lcd.createChar(2, vertline2); lcd.createChar(3, vertline3); lcd.createChar(4, vertline4); lcd.backlight(); lcd.setCursor(0,0); // column, row ServoTimeMin = 400; // safe defaults ServoTimeMax = 2500; ServoTimeMid = ((ServoTimeMax-ServoTimeMin)/2)+ServoTimeMin; ServoTimeOutput = ServoTimeMid; // set servo to mid-point incrementButtonClicks = 1; // Set default increment digit 0 = 1, 3 = 1000 myservo.attach(ServoPin); myservo.writeMicroseconds(ServoTimeOutput); // } void loop() { // PotInput(); incrementButtonRead(); TimeBarDisplay(); delay(10); } void incrementButtonRead(){ ButtonState = digitalRead(encoder0Btn); if (ButtonState != ButtonStateLast){ if (ButtonState == LOW){ incrementButtonClicks = incrementButtonClicks + 1; if (incrementButtonClicks >=4){incrementButtonClicks = 0;} } ButtonStateLast = ButtonState; switch (incrementButtonClicks) { case 0: ServoTimeIncrement = 1; break; case 1: ServoTimeIncrement = 10; break; case 2: ServoTimeIncrement = 100; break; case 3: ServoTimeIncrement = 1000; break; } manualCursorBlink(); // Sets manual mode cursor position to blink } } // Potentiometer input not used void PotInput(){ manualpos = analogRead(PotPin); ServoTimeOutput = map(manualpos, 0, 1023, ServoTimeMin, ServoTimeMax); } void TimeBarDisplay(){ if (ServoTimeOutput >= ServoTimeMax){ ServoTimeOutput = ServoTimeMax; } if (ServoTimeOutput <= ServoTimeMin){ ServoTimeOutput = ServoTimeMin; } if (ServoTimeOutput != ServoTimeOutputLast){ // lcd.clear(); // May take too much time. lcd.setCursor(0,1); lcd.print("Servo output: "); // Clear the line if (ServoTimeOutput >= 1000){ lcd.setCursor(14,1); } else{ lcd.setCursor(15,1); } lcd.print(ServoTimeOutput); lcd.print("us "); lcd.setCursor(0,2); lcd.print(ServoTimeMin); ServoTimeMid = ((ServoTimeMax-ServoTimeMin)/2)+ServoTimeMin; lcd.setCursor(8,2); lcd.print(ServoTimeMid); lcd.setCursor(16,2); lcd.print(ServoTimeMax); lcd.setCursor(0,3); lcd.print(" "); // Clear the line bar_number = map(ServoTimeOutput, ServoTimeMin, ServoTimeMax, 0, 99); bar_charNo = bar_number/5; bar_character = (bar_number) - (bar_charNo * 5); lcd.setCursor(bar_charNo,3); lcd.write (bar_character); myservo.writeMicroseconds(ServoTimeOutput); ServoTimeOutputLast = ServoTimeOutput; manualCursorBlink(); // Sets manual mode cursor position to blink } } void manualCursorBlink() { // Sets manual mode cursor position to blink lcd.cursor(); // Straight underline cursor // lcd.blink(); // Full block flashing cursor switch (ServoTimeIncrement) { case 1: lcd.setCursor(17,1); break; case 10: lcd.setCursor(16,1); break; case 100: lcd.setCursor(15,1); break; case 1000: lcd.setCursor(14,1); break; } } void encoderRead() { n = digitalRead(encoder0PinA); if ((encoder0PinALast == LOW) && (n == HIGH)) { if (digitalRead(encoder0PinB) == HIGH) { ServoTimeOutput = ServoTimeOutput - ServoTimeIncrement; } else { ServoTimeOutput = ServoTimeOutput + ServoTimeIncrement; } } encoder0PinALast = n; } The hardware is fixed now, but I may still add functions to the code. Model and image files here: https://www.thingiverse.com/thing:4965237 1
Vinc_Vega Posted September 15, 2021 Posted September 15, 2021 Thank you Tim, that seems to be a handy tool The other black box in the first picture is a power bank, am I right? (battery power for an USB port) Regards, Vinc Regards, Vinc real life: Royal Bavarian Airforce online: VJS-GermanKnights.de [sIGPIC][/sIGPIC]
No1sonuk Posted September 15, 2021 Author Posted September 15, 2021 2 hours ago, Vinc_Vega said: Thank you Tim, that seems to be a handy tool The other black box in the first picture is a power bank, am I right? (battery power for an USB port) Regards, Vinc Yes. It's an old 4-LED Tecknet IEP1500 dual output power bank. It should run OK on a 1A or more phone charger with miniUSB lead.
rifraf105 Posted September 29, 2022 Posted September 29, 2022 Hi, great job on the tool! Just a quick one for you, I am really struggling to understand the wiring on the rotary. Any chance of a close up pic so i can see where the wires and the resistors go. Thanks very much PC Specs: Asus ROG Strix Z370-E Motherboard 4x 8Gb corsair vengance 3000Mhz RAM ROG STRIX 1080Ti Graphics Processor Intel® Core i7-8700K CPU @ 3.70GHz, 3701 Mhz, 6 Core(s), 12 Logical Processor(s) DCS runs on its own 1Tb 970 EVO M.2 SSD Oculas Rift S Thrustmaster Hotas Warthog setup.
No1sonuk Posted September 29, 2022 Author Posted September 29, 2022 Thanks, Does this help? BTW, they're capacitors, not resistors. 1
rifraf105 Posted September 30, 2022 Posted September 30, 2022 Awesome, Thankyou very much PC Specs: Asus ROG Strix Z370-E Motherboard 4x 8Gb corsair vengance 3000Mhz RAM ROG STRIX 1080Ti Graphics Processor Intel® Core i7-8700K CPU @ 3.70GHz, 3701 Mhz, 6 Core(s), 12 Logical Processor(s) DCS runs on its own 1Tb 970 EVO M.2 SSD Oculas Rift S Thrustmaster Hotas Warthog setup.
rifraf105 Posted October 3, 2022 Posted October 3, 2022 Hi Tim, unit all built now and the screen comes on with nothing on the display! What will I expect to see when powering up? Or do I need to do something else to start the code? PC Specs: Asus ROG Strix Z370-E Motherboard 4x 8Gb corsair vengance 3000Mhz RAM ROG STRIX 1080Ti Graphics Processor Intel® Core i7-8700K CPU @ 3.70GHz, 3701 Mhz, 6 Core(s), 12 Logical Processor(s) DCS runs on its own 1Tb 970 EVO M.2 SSD Oculas Rift S Thrustmaster Hotas Warthog setup.
No1sonuk Posted October 3, 2022 Author Posted October 3, 2022 It should look like the photo above with the powerpack and servo. Have you done a simple I2C test with the display to make sure it's OK?
rifraf105 Posted October 4, 2022 Posted October 4, 2022 Thanks, I found the problem, Just a loose female connector! story of my life. Working now thanks 1 PC Specs: Asus ROG Strix Z370-E Motherboard 4x 8Gb corsair vengance 3000Mhz RAM ROG STRIX 1080Ti Graphics Processor Intel® Core i7-8700K CPU @ 3.70GHz, 3701 Mhz, 6 Core(s), 12 Logical Processor(s) DCS runs on its own 1Tb 970 EVO M.2 SSD Oculas Rift S Thrustmaster Hotas Warthog setup.
No1sonuk Posted October 4, 2022 Author Posted October 4, 2022 LOL Let me know how it works out for you.
rifraf105 Posted October 5, 2022 Posted October 5, 2022 So, I was having issues with the burning out of a servo in an instrument I made and now your device has given me the ability to accurately enter the extremes of the instrument in the code! The instrument is now perfectly aligned and works flawlessly. Thanks so much, Ill post a make on thingiverse 1 1 PC Specs: Asus ROG Strix Z370-E Motherboard 4x 8Gb corsair vengance 3000Mhz RAM ROG STRIX 1080Ti Graphics Processor Intel® Core i7-8700K CPU @ 3.70GHz, 3701 Mhz, 6 Core(s), 12 Logical Processor(s) DCS runs on its own 1Tb 970 EVO M.2 SSD Oculas Rift S Thrustmaster Hotas Warthog setup.
Recommended Posts