Microprop Variomodul 4 Favorit > FrSky DHT

Single to Multi propo
Post Reply
User avatar
F2B
Posts: 200
Joined: 16 Feb 2018, 11:23
Location: 20 m NE of Amsterdam

Microprop Variomodul 4 Favorit > FrSky DHT

Post by F2B »

It must have been years ago when I mailed Phil with a question regarding timing between an original Microprop 4 and a FrSky DHT module that, for the dear life of me, I couldn’t get it going as well as I’d hoped for.
I was converting this set for a friend and therefore it should be working 110% reliably.
Having used DHTs before (with D4R-II receivers), and those always worked fine, I estimated this a week's work. Wrong.... :cry:
I got the combination going, but not reliably, as the D4R-II turned out being sensitive to frame length.
When moving all sticks in the corner, all channels hopped one position forward. By adjusting what I figured had to be the frame rate pot, I could counter this behaviour, but then I found the servo travel linearity suffered too much. I couldn’t find the specific timing component for the reset pulse, so tried drawing up a schematic diagram from the PC board, but this taking so much time, I started looking for other solutions.

With the original 40MHz already removed:
Originele print.jpg
As I couldn’t properly permanently adapt the Microprop's original board, I went on to plan B., the PhilG Arduino encoder.
I found Phil on the World Wide Web, initially had no clue who he was, but as he was so friendly and helpful, I decided giving his design a try.

With my latest (1982) programming experience being machine language on an 8080, Arduino was a whole new world, but gradually I got a handle. At first I only modified examples by others (mostly by Phil & Frank), but gradually getting bolder (and failing accordingly, of course :lol: ) I managed to coax different ideas into one design.

The Microprop ended up working just as hoped for. Because of the set previously being used in a trainer with servos and linkage set up already, I tried figuring out a way for adding sub trim and travel controls.
When I understood how analog voltages from 0-5 V could be used for generating numeric values from 0-1023, I went that way. In a 4 channel set these extra controls would require 8 more analog inputs. Problem is, however, that the Nano and Pro Mini only have 8 analog inputs so nothing would be left for stick inputs and ‘special effects’.

Somewhere on the web I found an example of using an 8 into 1 analog multiplexer with an Arduino. After modifying this to my requirements I could implement this piece of circuitry + software into Phil’s original that I already reworked to a 4 effective channel sketch. As I couldn’t come up with a ‘simple’ binary 3 bit counter, I wrote it all down, line after line and that worked at first try. Obvious to all you software wizards, but not to this old school, soldering hack. Never mind, I now could trim neutral and adapt servo travel real time.

Now I had best of both worlds: A perfect driver for the DHT module, coupled with trim facilities, enabling me making the servo’s believe they were still controlled by the old Microprop. Even better, the pilot didn’t find it any different, apart from this funny, little TX antenna. In the end we went adventurous and decided for a little (fixed) expo but that’s all.
Since the set was to be used on IC models, I added a separate throttle reverse (inside the TX case!) too.

Wanting to mimic the old Microprop’s battery meter (a flashing green LED similar to the ones found in the Simprop SSM series), I modified the infamous Blink sketch with an analog input.. My limited insight in the consequences of my programming attempts did not allow me working out something that would work reliably within the encoder program. Because I didn’t want to mess up the timing of said encoder, I decided for a separate AtTiny battery check circuit and that combination finally worked.
Only difference, at minimum battery voltage the green LED turns red.....

The strip board lay out:
Print PPM Encoder ProMini.jpg
The encoder + battery meter + DHT:
Het eindproduct.jpg
And the Microprop lived happily ever after.
Klaar!.jpg
The encoder sketch:

// Simple four propo channel PPM encoder. All channels with separate subtrim and rate settings.
// Please do not use Arduino bootloader for programming, its startup delay messes things up and can strip servo gears with some modules (eg Corona)
// Connections:
// Pots are wired between ground and regulated 5v from Arduino. Wipers connected as follows:
// A0=aileron, A1=elevator, A2=rudder, A3=throttle. Always use this wiring regardless of PPM channel order.
// If fewer analogue channels are used, tie unused ones to a used one, do not leave floating. Eg on a 2ch set, tie A2 to aileron wiper, A4 to elevator wiper
// Switch 1 = Throttle reverse, switch 6 (on D11) = stick calibration.
// Features:
// Stick calibration - hold switch 6, switch on power, move sticks and trims to extreme corners. Centralise trims, switch 6 = off.
// Servo reversing by holding sticks over on power up (saved to eeprom).
// Optionally variable expo & rates on JR ch2, 3 & 4.
// Throttle reverse can only be changed on power-up (avoids accidentally switching during flight).
// Throttle reversing, power off, change switch 1, power up.
// Auto throttle-lock on power up, throttle stays closed until throttle stick & trim have been fully in closed position once.
// Follow the story on the single-channel forum http://www.mode-zero.uk
// and on the RCM&E forum http://www.modelflying.co.uk/forums/pos ... p?th=97589 diagram and documentation on http://www.singlechannel.co.uk
// If you use this software in whole or in part please acknowledge its source - Phil_G on most forums, philg@talk21.com http://www.singlechannel.co.uk http://www.mode-zero.uk
// Thanks to Paul Luby for his expo maths, to Ronstv for a couple of feature suggestions and an additional throttle safety feature (10/05/15)
// Thanks also to testers Ronstv, 2.4g Shaun, Doug C, Pat T, Frank S and _Al_
// Latest mods: F2B (Bruno).

static int Futaba = 0; // set to 1 for Futaba channel order AETR. Set to 0 for JR/Spektrum order TAER
//static int mix_50 = 7; // mixer 50:50
//static int mix_75 = 8; // mixer 75:25
//static int sw_cut = 9; // throttle-cut toggle
static int sw_rev = 10; // throttle reverse switch
static int sw_cal = 11; // calibration switch
//static int sw_gea = 12; // toggle channel 5 (gear)
static int ppm = 13, ppmPulse = 300, neutralPulse = 1200;
int raw, ch, calibrated=1, startup=1, stickcalHi[]={0,0,0,0,0,0}, stickcalLo[]={1023,1023,1023,1023,1023,1023}, pot1, pot2, pot3, pot4, pot5, pot6, pot7, pot8;
float ch0val, chtemp, neutral = {1200}, channel[]={1200,1200,1200,1200,1200,1200,1200,1200}; // last is sync
float lastch7, cchannel4, rate0 = 0, rate1 = 0, rate2 = 0, rate3 = 0, subt0 = 0, subt1 = 0, subt2 = 0, subt3 = 0, expo = 0;
byte reverse[]={0,0,0,0,0,0};
byte tlock=1, mix_option=0, fiftyhz=0;

#include <EEPROM.h>

void setup() {
noInterrupts();
pinMode(ppm, OUTPUT);
pinMode(sw_rev, INPUT_PULLUP);
pinMode(sw_cal, INPUT_PULLUP);

// 4051 digital control pins, input 4051 inh=6 s0=11, s1=10, s2=9 , controlling 4051 analoge multiplexer, brings 8 potvalues to Analog In 4.
pinMode(2, OUTPUT); // inh
pinMode(3, OUTPUT); // s0 = 1
pinMode(4, OUTPUT); // s1 = 2
pinMode(5, OUTPUT); // s2 = 4
}

void loop() {
while (startup==1 && (digitalRead(sw_cal) == 0 )) {
// calibrate sticks
calibrated=0;
for (int stick=0; stick <4; ++stick) {
raw=analogRead(stick);
if (raw > stickcalHi[stick]) stickcalHi[stick] = raw;
if (raw < stickcalLo[stick]) stickcalLo[stick] = raw;
}
}
if (startup==1 && calibrated==0) {
for (ch=0; ch<4; ch++) {EEPROMWriteInt(ch*4,stickcalLo[ch]); EEPROMWriteInt(ch*4+2,stickcalHi[ch]);}
calibrated=1;
}
if (startup==1) {
for (ch=0; ch<4; ch++) {stickcalLo[ch]=EEPROMReadInt(ch*4); stickcalHi[ch]=EEPROMReadInt(ch*4+2); reverse[ch]=EEPROM.read(ch+24) & 1; }
reverse[3]=0; reverse[4]=0; reverse[5]=0; // no throttle reverse for safety, no aux reverse

}


for (ch=0; ch<4; ch++) {channel[ch] = map(analogRead(ch),stickcalLo[ch],stickcalHi[ch],-500,500); }


// throttle cut/hold-off
//if (channel[3] < -490) tlock=0;
//if (tlock) channel[3]=-500;


// check for reversing, stick over on power-up, not throttle though.
if (startup==1) {
for (ch=0; ch<3; ch++) {
if (channel[ch] > 450 || channel[ch] < -450) { reverse[ch] ^=B00000001; EEPROM.write(24+ch,reverse[ch]);}
}


// check for reversing, reverse switch on power-up, just throttle.
if (startup==1 && digitalRead(sw_rev)==0) {
{ reverse[3] ^=B00000001; EEPROM.write(24+3,reverse[3]);}
}
}

// end of once-only startup routines
startup=0;


//Read Value of 4051 analog-ins 0 by setting the values of inh, s0, s1 and s2 (D2, D3 & D4), write to potx.
{

digitalWrite(2, LOW);
digitalWrite(3, LOW);
digitalWrite(4, LOW);
digitalWrite(5, LOW);

int pot1 = analogRead(4); // read the input pin 1 (4051) to Arduino pin A4

digitalWrite(2, LOW);
digitalWrite(3, HIGH);
digitalWrite(4, LOW);
digitalWrite(5, LOW);

int pot2 = analogRead(4); // read the input pin 1 (4051) to Arduino pin A4

digitalWrite(2, LOW);
digitalWrite(3, LOW);
digitalWrite(4, HIGH);
digitalWrite(5, LOW);

int pot3 = analogRead(4); // read the input pin 1 (4051) to Arduino pin A4

digitalWrite(2, LOW);
digitalWrite(3, HIGH);
digitalWrite(4, HIGH);
digitalWrite(5, LOW);

int pot4 = analogRead(4); // read the input pin 1 (4051) to Arduino pin A4

digitalWrite(2, LOW);
digitalWrite(3, LOW);
digitalWrite(4, LOW);
digitalWrite(5, HIGH);

int pot5 = analogRead(4); // read the input pin 1 (4051) to Arduino pin A4

digitalWrite(2, LOW);
digitalWrite(3, HIGH);
digitalWrite(4, LOW);
digitalWrite(5, HIGH);

int pot6 = analogRead(4); // read the input pin 1 (4051) to Arduino pin A4

digitalWrite(2, LOW);
digitalWrite(3, LOW);
digitalWrite(4, HIGH);
digitalWrite(5, HIGH);

int pot7 = analogRead(4); // read the input pin 1 (4051) to Arduino pin A4

digitalWrite(2, LOW);
digitalWrite(3, HIGH);
digitalWrite(4, HIGH);
digitalWrite(5, HIGH);

int pot8 = analogRead(4); // read the input pin 1 (4051) to Arduino pin A4

expo = 125; expo/=100; // expo fixed at 1,25
// read rates pots
rate0 = map(pot6,0,1023,50,115);
rate0/=100;
rate1 = map(pot7,0,1023,50,115);
rate1/=100;
rate2 = map(pot8,0,1023,50,115);
rate2/=100;
rate3 = map(pot5,0,1023,50,115);
rate3/=100;
for (ch=0; ch<3; ch++) {
if (channel[ch] <0) {channel[ch]=abs(channel[ch])/500; channel[ch]=pow(channel[ch],expo)*-500; }
else
channel[ch]=(pow(channel[ch]/500,expo)*500);
channel[0]*=rate0;
channel[1]*=rate1;
channel[2]*=rate2;
channel[3]*=rate3;
}

{
// read/map subtrim pots
subt0 = map(pot2,0,1023,-30,30);
subt1 = map(pot3,0,1023,-30,30);
subt2 = map(pot4,0,1023,-30,30);
subt3 = map(pot1,0,1023,-30,30 );
}
}
// format the frame
channel[7]=0; // 8th element is sync
for (ch=0; ch<7; ch++) {
channel[ch]+=neutralPulse;
channel[ch]=constrain(channel[ch], 600, 1800);
if (reverse[ch] ==1) channel[ch] = 2400-channel[ch];
channel[7]+=channel[ch];
// Add subtrim
channel[0]=channel[0] + subt0;
channel[1]=channel[1] + subt1;
channel[2]=channel[2] + subt2;
channel[3]=channel[3] + subt3;
}
channel[7] = 13500-channel[7];

// this puts the channels in Futaba or Spektrum/JR channel order as required // JR = AERT12G > TAERXXX
if(Futaba==1) {chtemp=channel[3]; channel[3]=channel[2]; channel[2]=chtemp; chtemp=channel[6]; channel[6]=channel[5]; channel[5]=channel[4]; channel[4]=chtemp; }
else {chtemp=channel[3]; channel[3]=channel[2]; channel[2]=channel[1]; channel[1]=channel[0]; channel[0]=chtemp; channel[4]=neutral; channel[5]=neutral; channel[6]=neutral; }

//send ppm frame, last channel holds sync value
for (int ch=0; ch<8; ch++) {
digitalWrite(ppm, HIGH);
delayMicroseconds(ppmPulse);
digitalWrite(ppm, LOW);
delayMicroseconds(channel[ch]);
}
}

// This function will write a 2 byte integer to the eeprom at the specified address and address + 1
void EEPROMWriteInt(int p_address, int p_value)
{
byte lowByte = p_value%256;
byte highByte = p_value/256;
EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}

//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
unsigned int EEPROMReadInt(int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);
return lowByte + highByte*256;
}

The battery meter sketch (in Dutch ;) ):

int inputBat = 1; // selecteert de input pin voor de verschaalde (max. 5 volt) accuspanning
int ledPin1 = 0; // selecteert de pin voor de 'accu toestand' LED
int ledPin2 = 1; // selecteert de pin voor de signaal 'accu leeg > nu landen!' LED
int inputwaarde = 0; // variabele die de gemeten accuspanning voorstelt
int outputwaarde = 0; // variabele waar de berekende outputwaarde wordt opgeslagen
int LED = 0; // variabele die de tijdsduur van de groene LED 'aan' bepaalt.
byte low = 0; // 'Battery low' flag

void setup() {

pinMode(ledPin1, OUTPUT); // stelt de ledPin1 in als OUTPUT
pinMode(ledPin2, OUTPUT); // stelt de ledPin2 in als OUTPUT
}

void loop()
{inputwaarde = analogRead(inputBat); // lees de verschaalde (max. 5 volt) accuspanning

outputwaarde = map(inputwaarde, 600, 880 , 1000, 13); // verschaal de inputwaarde naar het bereik van de outputwaarde

if (outputwaarde > 877) low = 1; // zet 'Battery low' flag aan (Vbatt ~6,6V).

if (low == 1) // als flag 'aan', dan:
{
digitalWrite(ledPin2, HIGH); // schakel de ledPin2 (rood) aan
digitalWrite(ledPin1, LOW); // schakel de ledPin1 (groen) uit
}
else // staat flag nog uit, dan:
{
LED = map(outputwaarde, 1000, 15, 1700, 1000); // verschaal de outputwaarde naar het bereik van de LEDwaarde

digitalWrite(ledPin1, HIGH); // schakel ledPin1 (groen) aan
delay(60); // houdt ledPin1 (groen gedurende <60> milliseconden aan
digitalWrite(ledPin1, LOW); // schakel ledPin1 (groen) uit
delay(LED); // houdt ledPin1 (groen) gedurende <LED> milliseconden uit
}
}
F2B or not to be....
Post Reply