Sim kumandası yapalım ...

Sim kumandası yapalım ...

Ben şu an "yarı cahil" konumundayım ki bu en tehlikelisi. :) Yavaş yavaş bazı ayrıntıları öğreniyorum. Güzel de oluyor.
Öncelikle analog okumanın makine kodu düzeyindeki karşılığını araştırmalıyım. Sonuçta bir analogRead() dediğimizde Arduino IDE bunu kimbilir kaç komuta çeviriyor. Tam okuma aşamasında interrupt geldiğinde ne olacağını merak ediyorum. Bildiğim kadarıyla interrupt geldiğinde işlemci o anki komutu tamaladıktan sonra ISR'ye dallanıyor. Eğer analog okuma işlemi işlemciyi bloke ediyorsa interrupt ciddi derecede gecikebilir ve bu da her şeyi alt üst eder. Pek ihtimal vermiyorum ama biliyorsun ummadık taş baş yarıyor her zaman. :)
 
Sim kumandası yapalım ...

Günü doküman ve datasheet okumakla geçirdim. Ulaştığım kanaat şu: ADC sırasında interrupt gelmesinin herhangi bir sakıncası yok. Ohh çok şükür! Zafer'in kodu bu şekilde kafamda daha da iyi oturdu. Ellerine sağlık tekrar.
 
Sim kumandası yapalım ...

Sümer Yamaner' Alıntı:
Günü doküman ve datasheet okumakla geçirdim. Ulaştığım kanaat şu: ADC sırasında interrupt gelmesinin herhangi bir sakıncası yok. Ohh çok şükür! Zafer'in kodu bu şekilde kafamda daha da iyi oturdu. Ellerine sağlık tekrar.
Abi gercekten Harikasin :thumbup:
 
Sim kumandası yapalım ...

Konunun ilk fazının nihayete bağlanmadığını düşünenler :laugh: için kısa bir güncelleme geçeyim dedim...

Devre şemamız burada;


Çalışan kod ve bu kod ile hazırlanmış prototip de bu mesajda var;


Kendisine sim kumandası hazırlamak isteyen birisinin yapması gerekenler şunlar;
- Arduino'ya 2. mesajdaki kod yüklenecek, yüklemeler hakkındaki detaylı bilgiler için aşağıdaki konuya bakılabilir

- İlk mesajdaki şemaya uygun olarak, kumandaya kablo lehimlenip bunlar arduino'nun pinlerine takılacak.

2. faz konusuna gelince, BLUETOOTH Beacon'un kablosuz ve kesintisiz veri iletme konusunda çok da uygun olmaması, arduino'nun BLE BEACON verilerini okuyamaması gibi bir kaç sıkıntım var. Bunun için çeşitli alternatifler düşünüyorum ve dolayısı ile kablosuz kumanda konusu şimdilik beklemede ama kesinlikle durmuş ya da hedeften sapmış değil :) ...
 
Sim kumandası yapalım ...

Bak bak bak... Çaktırmadan laf da çakarmışızzz! [emoji38]

SM-N910C cihazımdan Tapatalk kullanılarak gönderildi
 
Sim kumandası yapalım ...

Ben ilk kodu direk pro mini'ye kodum [emoji1] eski sim kumandasini da soktum dagittim, ama toplayamadim, bahanem de soyle, lehim telim bitmis [emoji54] Vallahi billahi. O lehim yillarca bitmeyecek gibiydi, Rasim ve kendim icin 6mm rcproplus yaptim bilmem kac set, namussuz oyle boyle lehim yemiyormus [emoji16] Neyse, araya bayram girdi, tatil girdi, 1 agustosa kadar kapattim tukkani. Donuste toplar calistirir bir ucak ucururum [emoji57]
 
Sim kumandası yapalım ...

Kod yazmaktan falan anlamadığım için bu konuya çok girip bakmıyordum. Ara sıra bakmak lazimmis. Kumanda ihtiyacı olmuş görmedim hiç. Bende wingdragon ile gelen 4 ch kumanda var. İçinde pil takılı kaldığı için anakart oksitlenmis. Kumandaya pil takıyorum yeşil ışık yanıyor. Ama gerçekte ve sim de kolları oynamama rağmen hiçbir hareketlilik yok. Bir şeyler yaparım derseniz gönderebilirim. Belki projenizi tamamlayıp geri verirsiniz bende sim çalışırım [emoji1] [emoji1] şaka bir yana lazımsa göndereyim

Edit: bugün izmir e geldim kumanda afyon da
 
Sim kumandası yapalım ...

Yavaş yavaş, el hareketleri ile(gimbalsiz çubuksuz olarak) kontrol edilen simulatör kumandası olayına giriş yapayım dedim. İşe çift elle sarılmam bir süre mümkün olamayacağından :laugh: halihazırda çalışıyor olan simulatör kumandası kodunda biraz geliştirme yaptım.

- Trim ve epa değerlerini kaydetmek için, eeprom kodu eklendi, ama henüz trim ve epa aktif değil
- Anlaşılabilirliği arttıracak şekide kod düzenlendi

MPU6050'den açıları(elin hareketlerini) okuyabiliyor olsam da, henüz tam olarak hangi senaryolar çerçevesinde bu değerleri servo sinyaline çevireceğime karar veremediğimden(bir de henüz mpu6050'ye tam hakim olamadığımdan), bu değerlerin bir geçerliliği yok, analog ve dijital inputlar geçerli... Şimdilik aşağıdaki gibi yapmayı düşünüyorum:

- Eli dik(vertical) olarak indirip kaldırmak, throttle yerine geçecek. Fakat bu durumda, aşağıdaki hareketler esnasında gaza yanlışlıkla müdahale sözkonusu olabilir... Üzerinde çalışmak lazım.
- Eli sağa ve sola yatırmak, tahmin edileceği üzere aileron/roll yerine geçecek.
- Eli öne ve arkaya yatırmak, tahmin edileceği üzere elevator/pitch yerine geçecek.
- Eli yatay eksen üzerinde sağa sola bakacak şekilde çevirmek rudder/yaw yerine geçecek.

Bir sıkıntı da, kodun interrupt kullanmadan, delaymicrosecons'un 3 mikrosaniyelik çözünürlük problemine rağmen daha kolay gerçeklenecek izlenimi vermesi... Neyse, ilerledikçe göreceğim :D.

Kod:
// Arduino Eldiven Simulator Kumandası
// V0.5 Alfa - Zafer ŞAHİN

#include "I2Cdev.h"

#include "MPU6050_6Axis_MotionApps20.h"

#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

#include <EEPROM.h>

// TEMEL AYARLAR.
#define CH_ANL 4          // kanal sayısı - ANALOG ( potansiyometre, v.b. )
#define CH_DGT 4          // kanal sayısı - DIGITAL ( anahtar v.b. )
#define CENTER_VAL 1500   // varsayılan servo merkez sinyali
#define PWM_H_LEN 500     // varsayılan pwm sinyal genişliğinin yarısı, yani maksimum sinyal = CENTER_VAL + PWM_H_LEN, minimum sinyal = CENTER_VAL - PWM_H_LEN
#define PPM_FRLEN 22500   // mikrosaniye cinsinden PPM çerçeve uzunluğu (1ms = 1000µs)
#define PPM_PULSELEN 300  // PPM başlangıç darbesinin uzunluğu
#define ONSTATE 0         // darbelerin kutuplaması, 0 negatif, 1 pozitif
#define SIGPIN 10         // PPM sinyali için arduino'nun çıkış pini
#define T_LIMIT 255       // EPA ve TRIM için limit değer
#define TRIM_ENABLE 0     // TRIM ayarlamasının açık olup olmadığını belirtir. 0 kapalı,  >0 açık.
#define EPA_ENABLE 0      // EPA ayarlamasının açık olup olmadığını belirtir. 0 kapalı,  >0 açık.

const int CHANNELS = CH_ANL + CH_DGT;                  // toplam kanal sayısı - ANALOG + DIJITAL
int ppm_val[CHANNELS];                                 // kanallarda tutulacak sinyal değerleri
int input_pins[CHANNELS] = {18,19,20,21,14,15,16,17};  // A4, A5, A6, A7, A0, A1, A2, A3
int trim_pins[2*CH_ANL] = {2,3,4,5,6,7,8,9};           // D2, D3, D4, D5, D6, D7, D8, D9
int trim_addr[CH_ANL] = {11,13,15,17};                 // trim değerlerini depolayacak EEPROM adresleri, integer depolayabilmek için ardışık 2 adres gerekiyor
int trim_val[CH_ANL] = {0,0,0,0};                      // trim için başlangıç değerleri, TRIM_ENABLE 0 için gerekli
int epa_addr[2*CH_ANL] = {21,23,25,27,29,31,33,35};    // epa değerlerini depolayacak EEPROM adresleri, integer depolayabilmek için ardışık 2 adres gerekiyor
int epa_val[2*CH_ANL] = {0,0,0,0};                     // epa için başlangıç değerleri, EPA_ENABLE 0 için gerekli
int pwm_max = CENTER_VAL + PWM_H_LEN;                  // PWM servo sinyali için üst limit
int pwm_min = CENTER_VAL - PWM_H_LEN;                  // PWM servo sinyali için alt limit

MPU6050 mpu;
bool dmpReady = false;  
uint8_t mpuIntStatus;   
uint8_t devStatus;      
uint16_t packetSize;    
uint16_t fifoCount;     
uint8_t fifoBuffer[64]; 

Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
float throttle,roll,pitch,yaw;

int dummy;

void setup(){
/* ######## ######## MPU6050 KODU ######## ########
    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
        Wire.begin();
        TWBR = 24; 
    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
        Fastwire::setup(400, true);
    #endif

    mpu.initialize();
    devStatus = mpu.dmpInitialize();

// offset değerleri
    mpu.setXGyroOffset(220);
    mpu.setYGyroOffset(76);
    mpu.setZGyroOffset(-85);
    mpu.setZAccelOffset(178:coolxf:; 

    if (devStatus == 0) {
        mpu.setDMPEnabled(true);
        packetSize = mpu.dmpGetFIFOPacketSize();
    } else {
        dummy = 0;
    }
######## ######## MPU6050 KODU ######## ######## */
    for (int i = 0;  i < CHANNELS; ++i) {
        if ( i < CH_ANL){
            pinMode(input_pins[i], INPUT);
            ppm_val[i] = CENTER_VAL;
            #if TRIM_ENABLE > 0
                // trim değerleri EEPROM'dan okunur ve T_LIMIT değeri ile kıyaslanarak, gerekli ise T_LIMIT değerine çekilir.
                trim_val[i] = (EEPROM_rd_int(trim_addr[i]) > T_LIMIT ) ? T_LIMIT : EEPROM_rd_int(trim_addr[i]);
                trim_val[i] = (trim_val[i] < -T_LIMIT ) ? -T_LIMIT : trim_val[i];
            #endif
            
            #if EPA_ENABLE > 0
                // epa değerleri EEPROM'dan okunur ve T_LIMIT değeri ile kıyaslanarak, gerekli ise T_LIMIT değerine çekilir.
                // epa_val[(2*i)+0] -> epa alt değer
                epa_val[(2*i)+0] = (EEPROM_rd_int(epa_addr[(2*i)+0]) > T_LIMIT ) ? T_LIMIT : EEPROM_rd_int(epa_addr[(2*i)+0]);
                epa_val[(2*i)+0] = (epa_val[(2*i)+0] < -T_LIMIT ) ? -T_LIMIT : epa_val[(2*i)+0];

                // epa_val[(2*i)+1] -> epa tepe değer
                epa_val[(2*i)+1] = (EEPROM_rd_int(epa_addr[(2*i)+1]) > T_LIMIT ) ? T_LIMIT : EEPROM_rd_int(epa_addr[(2*i)+1]);
                epa_val[(2*i)+1] = (epa_val[(2*i)+1] < -T_LIMIT ) ? -T_LIMIT : epa_val[(2*i)+1];
            #endif

        } else {
            pinMode(input_pins[i], INPUT_PULLUP);
            ppm_val[i] = pwm_min;
        }
    }
    pinMode(SIGPIN, OUTPUT);
    digitalWrite(SIGPIN, !ONSTATE);
    
    cli();
    TCCR1A = 0;               // tüm TCCR1 yazmaçları sıfırlanıyor
    TCCR1B = 0;
    
    OCR1A = 100;             // compare match register,
    TCCR1B |= (1 << WGM12);  // CTC modu açılır, böylece "timer compare interrupt" bayrağının da ayarlanması ile beraber, 
                             // "OCR1A" değeri her zamanaşımına uğradığında "ISR(TIMER1_COMPA_vect)" fonsiyonunun çağrılması sağlanmış olur.
    TCCR1B |= (1 << CS11);   // 8 prescaler: 16Mhz'de 0,5 mikrosaniye
    TIMSK1 |= (1 << OCIE1A); // timer compare interrupt
    sei();
}

void loop(){
/* ######## ######## MPU6050 KODU ######## ########
    mpuIntStatus = mpu.getIntStatus();
    fifoCount = mpu.getFIFOCount();

    if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
        mpu.resetFIFO();
    } else if (mpuIntStatus & 0x02) {
        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
        mpu.getFIFOBytes(fifoBuffer, packetSize);

        fifoCount -= packetSize;
        
        mpu.dmpGetQuaternion(&q, fifoBuffer);
        mpu.dmpGetGravity(&gravity, &q);
        mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
        yaw = ypr[0] * 180/M_PI;
        pitch = ypr[1] * 180/M_PI;
        roll = ypr[2] * 180/M_PI;
        throttle = dummy;
    }
######## ######## MPU6050 KODU ######## ######## */
    for (int i = 0;  i < CHANNELS; ++i) {
        if ( i < CH_ANL){
            analogRead(input_pins[i]);              // normal şartlarda analogRead'ın bu kadar sık okunması nedeni ile bu ekstra analogRead'a
                                                     // gerek olmadığını düşünüyorum fakat Sümer abi nedeni ile koyuldu :D ... 
            ppm_val[i] = trim_val[i] + map(analogRead(input_pins[i]), 0, 1023, pwm_min+epa_val[(2*i)+0], pwm_max+epa_val[(2*i)+1]);  // analogRead ile okunan 0 ile 1023 arasındaki değerler, 1000 - 2000 mikrosaniye arasındaki sürelere dönüştürülür.
        } else {
            ppm_val[i] = map(digitalRead(input_pins[i]), 0, 1, pwm_min, pwm_max);
        }
    }
}

// EEPROM'un sadece byte değerleri kabul etmesi sebebi ile, ardışık olarak 2 byte boyundaki adresler kullanarak, integer kaydeden rutin...
void EEPROM_wr_int(int address, int value){
      byte b_one = ((value >> 0) & 0xFF);
      byte b_two = ((value >> :coolxf: & 0xFF);
      EEPROM.write(address + 0, b_one);
      EEPROM.write(address + 1, b_two);
}

// EEPROM'un sadece byte değerleri kabul etmesi sebebi ile, ardışık olarak 2 byte boyundaki adresler kullanarak, kaydedilmiş integer değerini okuyan rutin...
int EEPROM_rd_int(int address){
    int i_one = EEPROM.read(address + 0);
    int i_two = EEPROM.read(address + 1);
    return ((i_one << 0) & 0xFF) + ((i_two << :coolxf: & 0xFF);
}

ISR(TIMER1_COMPA_vect){
    static boolean bState = true;               // PPM zincirinde, her bir kanalın başlangıcındaki 300 mikrosaniyelik darbeyi oluşturmak için kullanılacak mantıksal değişken.
    TCNT1 = 0;                                  // İşe başlamadan önce Timer sıfırlanır,
    if(bState) {
        digitalWrite(SIGPIN, ONSTATE);          // 300 mikrosniyelik PPM darbesi başlangıcı için sinyal seviyesi (ONSTATE) hazırlanır.
        OCR1A = PPM_PULSELEN * 2;               // 0,5 mikrosaniyeye karşılık gelen tık süresi nedeni ile 300 mikrosaniyeyi elde etmek için OCR1A, 600 olarak belirlenir.
        bState = false;                         // ISR(TIMER1_COMPA_vect) fonksiyonunun sonraki çağrılışında, PPM darbesinin kalanının üretebilmek amacı ile 300 milisaniyelik kısmın bittiği belirtilir
    } else {
        static byte bCurChan = 0;               // kaç tane kanal varsa, bu kanalları hatırlamak için kullanacağımız değişken.
        static unsigned int iPPMRest = 0;       // PPM darbesinin geri kalan süresini oluşturmak için kullanılacak değişken.  

        digitalWrite(SIGPIN, !ONSTATE);         // PPM darbesinin başlangıcı bitirilip, kalan süre için ters kutuplu darbe sinyali başlatılır.
        bState = true;                          // ISR(TIMER1_COMPA_vect) fonksiyonunun sonraki çağrılışında, 300 mikrosaniyelik darbe başlangıcı üretebilmek için mantıksal değişken ayarlanır.

        if(bCurChan < CHANNELS){                // 0'dan başlayarak her kanal sıra ile işlenir.
            OCR1A = (ppm_val[bCurChan] - PPM_PULSELEN) * 2;   // loop içerisinde oluşturulmuş olan, mevcut kanala ait ppm_val değerinden, PPM çerçevesinin başlangıcı olan 300 mikrosaniyelik kısım çıkarılır.
                                                              // 0,5 mikrosaniyeye karşılık gelen tık süresi nedeni ile 2 ile çarpılarak darbenin kalanına ait OCR1A değeri oluşturulur.
            iPPMRest = iPPMRest + ppm_val[bCurChan];          // PPM senkronizasyon darbesinin oluşturulması için kullanılacak, iPPMRest değişkeni hazırlanır.
                                                              // Sıra ile toplanarak kanlların oluşturulması sırasındaki geçen zaman tutulur
            bCurChan++;
        } else {                               // tüm kanallar sıra ile işlenip, oluşturulduktan sonra en son olarak senkronizasyon darbesinin oluşturulması gerekir.
            bCurChan = 0;                                     // kanalların tekrar 0'dan işlenebilmesi için bCurChan sıfırlanır
            iPPMRest = iPPMRest + PPM_PULSELEN;               // iPPMRest değişkeni, önceki kanalların oluşturulması esnasında geçen zamanı tutmaktadır, 
                                                              // darbe başlangıcını oluşturan 300 mikrosaniye de eklenerek, PPM çerçevesine ait olan geçen toplam zaman bulunmuş olur.
            OCR1A = (PPM_FRLEN - iPPMRest) * 2;               // senkronizasyon darbesinin kalanını üretebilmek amacı ile, iPPMRest ile tanımlanmış,
                                                              // şimdiye kadar geçen zaman toplam PPM çerçeve süresinden çıkarılarak, kalan süre hesaplanır ve tabii ki OCR1A değerini oluşturmak için 2 ile çarpılır. 
            iPPMRest = 0;                                     // geçen süreyi hesaplamak için kullanılan iPPMRest değişkeni sıfırlanır.
        }     
    }
}
 
Sim kumandası yapalım ...

Lövye sistemi deneyimime göre interrupt ile elde edilen PPM sinyalinin kodu en basiti gibi görünüyor. Zaten verdiğin örnek kodda da interrupt ile üretiliyor sinyal (gerçi interrupt kodunu sevmedim, benim kodum daha şık! :p :D :p :D ). Onu değiştirip delayMicroseconds'a geçmenin sebebi nedir?
Bir de bir öneri...
El kontrolünü aşama aşama yapabilirsin. Yani örneğin simülatör kumandasından aileron kanalını el kontrole aktarıp kumandanın geri kalan işlevlerini yerinde bırakırsın. Simde de olsa uçağı birisi uçururken sen sadece bir eksenin test ve ince ayarlarını yaparsın.
 
Sim kumandası yapalım ...

Zafer SAHIN' Alıntı:
Yavaş yavaş, el hareketleri ile(gimbalsiz çubuksuz olarak) kontrol edilen simulatör kumandası olayına giriş yapayım dedim. İşe çift elle sarılmam bir süre mümkün olamayacağından :laugh: halihazırda çalışıyor olan simulatör kumandası kodunda biraz geliştirme yaptım.
Senin tek elin bile yeter Zafer Hocam :D
 
Sim kumandası yapalım ...

Düz simulatör kumandası kodum aşağıdaki 74 satırlık kod :D ...

Kod:
#define CH_ANL 4
#define CH_DGT 4
#define CENTER_VAL 1500
#define PWM_H_LEN 500
#define PPM_FRLEN 22500
#define PPM_PULSELEN 300
#define ONSTATE 0
#define SIGPIN 10

const int CHANNELS = CH_ANL + CH_DGT;
int ppm_val[CHANNELS];
int input_pins[CHANNELS] = {18,19,20,21,14,15,16,17};
int pwm_max = CENTER_VAL + PWM_H_LEN;
int pwm_min = CENTER_VAL - PWM_H_LEN;
void setup(){
    for (int i = 0;  i < CHANNELS; ++i) {
        if ( i < CH_ANL){
            pinMode(input_pins[i], INPUT);
            ppm_val[i] = CENTER_VAL;
        } else {
            pinMode(input_pins[i], INPUT_PULLUP);
            ppm_val[i] = pwm_min;
        }
    }
    pinMode(SIGPIN, OUTPUT);
    digitalWrite(SIGPIN, !ONSTATE);

    cli();
    TCCR1A = 0;
    TCCR1B = 0;
    OCR1A = 600;
    TCCR1B |= (1 << WGM12);
    TCCR1B |= (1 << CS11);
    TIMSK1 |= (1 << OCIE1A);
    sei();
}

void loop(){
    for (int i = 0;  i < CHANNELS; ++i) {
        if ( i < CH_ANL){
            analogRead(input_pins[i]);
            ppm_val[i] = trim_val[i] + map(analogRead(input_pins[i]), 0, 1023, pwm_min, pwm_max);
        } else {
            ppm_val[i] = map(digitalRead(input_pins[i]), 0, 1, pwm_min, pwm_max);
        }
    }
}

ISR(TIMER1_COMPA_vect){
    static boolean bState = true;
    TCNT1 = 0;
    if(bState) {
        digitalWrite(SIGPIN, ONSTATE);
        OCR1A = PPM_PULSELEN * 2;
        bState = false;
    } else {
        static byte bCurChan = 0;
        static unsigned int iPPMRest = 0;

        digitalWrite(SIGPIN, !ONSTATE);
        bState = true;

        if(bCurChan < CHANNELS){
            OCR1A = (ppm_val[bCurChan] - PPM_PULSELEN) * 2;
            iPPMRest = iPPMRest + ppm_val[bCurChan];
            bCurChan++;
        } else {
            bCurChan = 0;
            iPPMRest = iPPMRest + PPM_PULSELEN;
            OCR1A = (PPM_FRLEN - iPPMRest) * 2;
            iPPMRest = 0;
        }
    }
}
Ama yukarıdaki kodda hiç bir harici kütüphane yok. Önceki mesajımda da bu koddan devşirilmiş interrupt rutinleri kullanıyor olsam da MPU6050'yi işleyebilmek için ciddi miktarda harici kütüphane kullanmak gerekiyor. Dolayısı ile interrupt'ları koda dahil etmek için çok dikkatli davranmak gerekiyor. Ama delaymicroseconds kullandığımda bu risk yok...
El kontrolünü aşama aşama devreye alma önerini dikkate alacağım Sümer abi. Aslında önceki mesajımda belirttiğm kodlarda, MPU6050 kodlarının yanında gimbal ve anahtarlardan okuma işlevlerinin duruyor olma sebebi de kısmen buydu. MPU6050'den ve gimballerden okunan değerleri karşılaştırararak ilerlemek istediğim için kodda beraber bulunuyorlar.
 
Sim kumandası yapalım ...

Bu arada tamamen arduino için yapılmış bir RC kumandası projesi varmış, zaten olmaması beklenemezdi :D :D :D ...


Features:
Programmable with Linux or Windows via USB
up to 9 proportional channels, 6 channels by default
Channels controlled by a potentiometer or by a switch
9 model memories
Model selection switch
2 programmable mixers
Dual rate/Exponential switch
Throttle cut switch
End point adjustment
Subtrims
Potentiometers and servos calibration
Throttle security check at startup
Transmitter battery low voltage alarm
Arduino board with ATmega328 microcontroller (Nano v3.0 recommended)
 
Sim kumandası yapalım ...

Vayy helal olsun adama. En çok tek analog input ile üç switch okuma olayını tuttum. Hemen inceleyip kapmalıyım. :)
 
Sim kumandası yapalım ...

Sümer Yamaner' Alıntı:
Vayy helal olsun adama. En çok tek analog input ile üç switch okuma olayını tuttum. Hemen inceleyip kapmalıyım. :)
Sümer abi, dakikalardır bakıyorum şemaya ama söylediğin şeyi göremedim bir türlü...

Bu RESMİ görmek için izniniz yok. Giriş yap veya üye ol


Ama zaten bu dediğin şu şekilde değil mi?

[attachimg=1]
 

Ekli dosyalar

  • ardu-sim-cc.png
    ardu-sim-cc.png
    2.7 KB · Görülme: 53
Sim kumandası yapalım ...

Şurada ayrıntılı anlatmış:



Peki bu kumandada trim yok. Ne iş???

Bu arada bir hatırlatma yapayım. Bu uygulamada ben olsam A4 ve A5'i kullanmazdım. Çünkü onlar I2C iletişimi için belirlenmiş pinler. Bendekine benzer bir OLED ekran sürülecek olursa o pinleri kullanmak gerekiyor.