23 Mayıs 2021 Pazar

QCX-Mini için ek devre

 QCX-Mini serencamımı yazmıştım. Avuç içine kolaylıkla sığan ve oldukça hafif olan bu devrenin öncelikli amacı taşınabilir bir QRP CW alıcı-vericisidir. Taşınabilir olmanın gerçekleşebilmesi için besleme devresinin de taşınabilir olması gereklidir. Şu hâlde QCX-Mini bir akü tarafından beslenmelidir diye düşündüm. Bunun için 3 adet li-ion pil kullanmaya karar verdim. 3 adet li-ion pil tam doluyken 4.2 x 3) 12.6 v., en at sınırda ise (3 x 3) 9v gerilim sağlar.

Bunun için 3 adet kaliteli li-ion pil bularak işe başladım. Çin'den gelen ve uçuk kapasite değeri olan pillerden uzak durarak, LG markalı 3 pil satın aldım. Bunları 500F.'lık bir süper kondansatörle kendim puntalayarak, üzerine de bir 3S pil koruma devresi monte ettim. Böylece 12v.luk pil bloğum hazır oldu.


Bu pilleri sıradan bir kutuya koymak işime gelmedi. Altınkaya firmasının PR-224 kodlu kutusunu kullanmaya karar verdim.
QCX-Mini, normalde kulaklıkla kullanılmak üzere tasarlandığından, ses çıkışını güçlendirerek bir hoparlörden dinlemek üzere devreye bir de ses frekansı güç kuvvetlendiricisi eklemeye karar verdim.
Ayrıca, li-ion pillerimim gerilimini de öğrenebilmeliydim.
Yine bir amatör telsizci, bu cihazı alarak bir yerlere gidip oradan QSO yapmak isterse, konum bilgisinin ve UTC olarak saat bilgisinin de gerekli olduğu açık idi. Şu hâlde bir de GPS gerekiyordu.
Böylece araştırmaya başladım. 
ON4IY çağrı işaretli Belçikalı amatör telsizcinin "Roverbox" projesinin çeşitli ekleme ve iyileştirmelerden sonra ON4CDU tarafından hazırlanan Arduino projesini incelemeye başladım.
Madem devrede bir Arduino kullanılacak o zaman ek özellikler de olsun diye düşünerek, bir CW çözücü özelliği de eklemek istedim. Yine araştırmalarım sonucunda K4ICY çağrı işaretli Amerikalı bir amatör telsizcinin CW çözücü projesine ulaştım. 
GPS bilgilerini, CW kod çözmeyi ve pil gerilimini ölçmeyi tek bir Arduino yazılımından toplamak üzere ON4CDU'nun ve K4ICY'nin yazılımlarını birleştirdim, gereksiz bâzı özellikleri çıkardım ve yazılıma bir de gerilim ölçme rutini ekleyerek Arduino'ya yükledim.
Küçük bir plastik kutuya Çin malı bir GPS yerleştirip, bu GPS'i 9'lu bir konnektör vasıtasıyla kutunun üstüne takılıp çıkarılır hâle getirdim.

                                  
Kutulanmış GPS









29 Mayıs 2021

Bugün 29 Mayıs. İstanbul'un fethinin 568. yıldönümü kutlu olsun.

CW çözme işi zor bir iş. Alıcının işaretlerinin mikroişlemcinin anlayacağı hâle getirilmesi başlı başına bir problem. Esasen QCX-mini, CW çözme işini kendisi mükemmel olarak yapıyor. O yüzden CW çözme işinden vazgeçtim. Tek ekranda tarih, UTC saat, enlem ve boylam, râkım ve pil gerilimini aynı anda göstermenin daha uygun olacağını düşündüm ve yazılımı ona göre değiştirdim.

İşte yeni ekran:





14 Ağustos 2021
Her ne kadar pil dengeleme devreleri Li-ion pillerin aşırı boşalmasına karşı koruma sağlıyor denilse de, ben yine de bir tedbir olarak devreye bir doldurma ikazı ekledim. Pillerin gerilimi belli bir değerin altına düşünce ekranda DOLDUR yazısı çıkıyor. Ben bu değeri 10v olarak belirledim. Ancak bu değeri kendi isteğinize göre değiştirebilirsiniz. (9v'tan daha düşük olmamak kaydıyla!)
Devrenin şeması ile yeni kodlar aşağıdadır.

15 Ağustos 2021
Arduino kodları ile ilgili bâzı açıklamalar:
1) Devrede, R1 ve R2'den oluşan bir gerilim bölücü kullanılmıştır. Tam dolu iken her bir li-ion pilin gerilimi 4,2v'a kadar yükselir. Tam dolu hâlde seri bağlı 3 pilin gerilimi 4,2 x 3 = 12,6v, en düşük olarak da 3 x 3 = 9V olacağından, bu gerilim bölücü ZORUNLUDUR. R1 için 10k'lık, R2 için 3,9k'lık bir direnç kullandım. Kullandığınız dirençlerin değerini ölçerek Ohm birimi ile 
/ / Voltmetre değişkenleri kısmına yazın.
Benim devremde bu dirençlerin değeri şöyle idi:
float           r1=9960.0; // R1'in tam değerini ölçerek yazın. (ohm)
float           r2=3895.0; // R2'nin tam değerini ölçerek yazın. (ohm)
2) Devrede kullanılan LCD ekran, 4 satır 20 karakterlik olup, bir I2C sürücüsü ile kullanılmıştır. Bu sürücünün adresi genellikle 0x27'dir. Eğer sizin kullandığınız sürücünün adresi farklı ise
LiquidCrystal_I2C lcd(0x27,20,4); 
satırına doğru değeri yazın. (0x27 yerine)
3) Kullandığınız GPS modülünün haberleşme hızını
const int       GPSbitrate = 9600
satırına yazın. Benim kullandığım modülünü hızı 9600 baud idi.
4) Li - ion pilleri için doldurma ikaz değerini, void volc() yordamında
 if(input_voltage<10)
satırına yazınız. Benim belirlediğim değer 10 v idi.
5) Aşağıdaki kodlar, Arduino Leonardo'ya göre hazırlanmıştır. Eğer Arduino Uno vena Nano kullanacaksanız Serial1 ibârelerini Serial olarak değiştirin. Bu ibârelerin değiştirileceği yerler kodlarda belirtilmiştir.
6) Ne yaptığınızı tam bilmeden kodlarda değişiklik yapmayın!!



ARDUINO KODLARI

/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*  Devrenin GPS kodları ON4CDU çağrı işaretli amatöre aittir.
 *   Arduino Micro 
 *    2    LCD SDA
 *    3    LCD SCL
 *    RX    GPS TX
 *    A0    ADC V input
 *    Bu yazılım, Tarihi, UTC olarak saati, yüksekliği, Uydu sayısını, enlem ve boylamı, grid locator'u, pil gerilimini
 *    ve gerilimin 10 v.'un altına düşmesi hâlinde şarj ihtiyacını gösterir.
 */

/// Included Libraries ///////////////////////

#include <Wire.h>               
#include <LiquidCrystal_I2C.h> 
LiquidCrystal_I2C lcd(0x27,20,4); // kendi devrenizin i2c adresini yazın.

//GPS için define'lar
#define DEG_TO_RAD 0.01745329
#define PI 3.141592654
#define TWOPI 6.28318531
// NMEA fields
#define RMC_TIME 1
#define RMC_STATUS 2
#define RMC_LAT 3
#define RMC_NS 4
#define RMC_LONG 5
#define RMC_EW 6
#define RMC_DATE 9
#define GGA_FIX 6
#define GGA_SATS 7
#define GGA_HDOP 8
#define GGA_ALTITUDE 9
#define HEADER_RMC "$GPRMC"
#define HEADER_GGA "$GPGGA"


/// SYSTEM VARIABLES ////////////////////

//*** GPS için değişkenler
const int       GPSbitrate = 9600;        //GPS cihazının haberleşme hızı
const int       sentenceSize = 82;
const int       max_nr_words = 20;
char            sentence[sentenceSize], sz[20],regel[20],diger[10];
char*           words [max_nr_words];
char            sts, latdir, longdir;
int             alternate;
int             hours, minutes, seconds, year, yeard, month, day;
int             gps_fixval, gps_sats, alti, hdopi;
int             latdeg, latmin, latsec;
int             londeg, lonmin, lonsec;
float           time, date, latf, lonf, hdopf;
boolean         status, gps_fix, hdop;
char            char_string [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char            num_string[]= "0123456789";

//some variables for sun position calculation
float           Lon, Lat;
float           T,JD_frac,L0,M,e,C,L_true,f,R,GrHrAngle,Obl,RA,Decl,HrAngle;
long            JD_whole,JDx;

// Voltmetre değişkenleri
float           temp=0.0;
float           r1=9960.0; // R1'in tam değerini ölçerek yazın.
float           r2=3895.0; // R2'nin tam değerini ölçerek yazın.
float           input_voltage = 0.0;
// buraya kadar

/// System Setup /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
Serial1.begin(GPSbitrate);      // Uno veya nano kullanacaksanız serial1'i serial yapın.
lcd.init();                     // Initialize the LCD display
lcd.backlight(); 
lcd.clear();
lcd.begin(20,4); 

// Splash Screen: Credit / Version / Site
lcd.setCursor(0,0);   lcd.print(" QCX MiNi YARDIMCI  ");
lcd.setCursor(0,1);   lcd.print(" GPS  - ON4CDU      ");
lcd.setCursor(0,2);   lcd.print(" Mod. - TA2EI       ");
lcd.setCursor(0,3);   lcd.print(" RECEP AYDIN GULEC  ");
delay(4000);
//lcd.blink();
lcd.clear();
while (!Serial1.available())
{
  yok();
}
}
/////////////////////////////////// Operation //////////////////////////////////////////////////////////////////////////////////////

void loop() 
{
  gps();
 
//volc();
}  // End Main Loop /////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void gps()
{
   
  static int i = 0;
  if (Serial1.available())       // Uno veya nano kullanacaksanız serial1'i serial yapın.
  {
    char ch = Serial1.read();   // Uno veya nano kullanacaksanız serial1'i serial yapın.
    if (ch != '\n' && i < sentenceSize)
    {
      sentence[i] = ch;
      i++;
    }
    else
    {
      sentence[i] = '\0';
      i = 0;
      displayGPS();
    }  
  }
 
  }
/////////////////////////////////////////////////////////////////////////////////////////
void displayGPS()
{
  
  getField ();
  if (strcmp(words [0], HEADER_RMC) == 0)
  {
    processRMCstring();
    printfirstrow ();
    locator();
  }
  else if (strcmp(words[0], HEADER_GGA) == 0)
  {
    processGGAstring ();
  }
  printGGAinfo ();
}
///////////////////////////////////////////////////////////////////////////////////////////
void getField () // split string was probably a better name
{
  char* p_input = &sentence[0];
  //words [0] = strsep (&p_input, ",");
  for (int i=0; i<max_nr_words; ++i)
  {
    words [i] = strsep (&p_input, ",");
  }
}
//////////////////////////////////////////////////////////////////////////////////////////////
void processRMCstring ()
{
    time = atof(words [RMC_TIME]);
    hours = (int)(time * 0.0001);
    minutes = int((time * 0.01) - (hours * 100));
    seconds = int(time - (hours * 10000) - (minutes * 100));
    sts = words[RMC_STATUS][0]; // status
    status = (sts == 'A');
    latf = atof(words [RMC_LAT]); // latitude
    latdeg = latf/100;
    latmin = (latf - (int(latf/100))*100);
    latsec = (((latf -int(latf))*100)*0.6);
    latf = int (latf * 0.01) + (((latf * 0.01) - int(latf * 0.01)) / 0.6);
    latdir = words[RMC_NS][0]; // N/S
    lonf= atof(words [RMC_LONG]);// longitude 
    londeg = lonf/100;
    lonmin = (lonf - (int(lonf/100))*100);
    lonsec = (((lonf -int(lonf))*100)*0.6);
    lonf = int (lonf * 0.01) + ((lonf * 0.01 - int(lonf * 0.01)) / 0.6);
    longdir = words[RMC_EW][0]; // E/W      
    date = atof(words [RMC_DATE]); // date
    day = int(date * 0.0001);
    month = int ((date * 0.01) - (day * 100));
    year = int (date - (day * 10000) - (month * 100));
    year = year + 2000;    
}
///////////////////////////////////////////////////////////////////////////////////////
void processGGAstring ()
{
    gps_fixval = words[GGA_FIX][0] - 48;
    gps_fix = (gps_fixval > 0);
    gps_sats = atoi(words[GGA_SATS]);    
    //hdopf = atof (words[GGA_HDOP]);
    alti = atoi(words[GGA_ALTITUDE]); 
}
////////////////////////////////////////////////////////////////////////////////////////
void printfirstrow ()
{
    sprintf(sz, "%02d.%02d.%04d %02d:%02d UTC",
            day, month , year, hours , minutes );
    lcd.setCursor(0, 0);
    lcd.print(sz); // Saat ve tarih LCD'de gösterilecek  
}
//////////////////////////////////////////////////////////////////////////////////////
void printGGAinfo ()
{
  if (seconds == 30)
    sunpos();
  if (status && gps_fix)
  {
        sprintf (sz, " ");
        sprintf(regel, "Lt:%2d %2d %2d %1c", latdeg, latmin , latsec ,latdir);
        lcd.setCursor(0, 2); lcd.print(regel); // Enlem LCD'de gösterilecek
        sprintf(regel, "Ln:%2d %2d %2d %1c", londeg, lonmin , lonsec ,longdir);
        lcd.setCursor(0, 3); lcd.print(regel); // Boylam LCD'de gösterilecek
        lcd.setCursor (7,1);
        sprintf(diger, "R:%4d m", alti);         
        lcd.print(diger);
        volc();
        lcd.setCursor(16,1);
        lcd.print("U:");
        lcd.print(gps_sats);  
  } 
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void locator()                                       // locator hesabı ve LCD'de gösterilmesi
{
  float loclong = lonf * 1000000;
  float loclat = latf * 1000000;
  float scrap;
  if (longdir == 'E') 
  {
    loclong = (loclong) + 180000000;
  }
  if (longdir == 'W') 
  {
    loclong = 180000000 - (loclong);
  }
  if (latdir == 'N') 
  {
    loclat = loclat + 90000000;
  }
  if (latdir == 'S') 
  {
    loclat = 90000000 - loclat;
  }
  lcd.setCursor(0, 1); // printing QTH locator
  lcd.print(char_string[int(loclong / 20000000)]);        // First Character - longitude based (every 20° = 1 gridsq)  
  lcd.print(char_string[int(loclat / 10000000)]);         // Second Character - latitude based (every 10° = 1 gridsq) 
  scrap = loclong - (20000000 * int(loclong / 20000000)); // Third Character - longitude based (every 2° = 1 gridsq)
  lcd.print(num_string[int(scrap * 10 / 20 / 1000000)]);
  scrap = loclat - (10000000 * int(loclat / 10000000));   // Fourth Character - latitude based (every 1° = 1 gridsq)
  lcd.print(num_string[int(scrap / 1000000)]); 
  scrap = (loclong / 2000000) - (int(loclong / 2000000)); // Fifth Character - longitude based (every 5' = 1 gridsq)
  lcd.print(char_string[int(scrap * 24)]);
  scrap = (loclat / 1000000) - (int(loclat / 1000000));   // Sixth Character - longitude based (every 2.5' = 1 gridsq)
  lcd.print(char_string[int(scrap * 24)]); 
 }
  
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void sunpos()
{
int A, B;  
    Lat = latf*DEG_TO_RAD;
    if (latdir == 'S')
    {
      Lat = -Lat;
    } 
    Lon = lonf*DEG_TO_RAD;
    if (longdir == 'W')
    {
      Lon = -Lon;
    }
    if(month <= 2) 
    { 
      year--;
      month+=12;
    }  
    A = year/100; 
    B = 2 - A + A/4;
    JD_whole = (long)(365.25*(year + 4716)) + (int)(30.6001 * (month+1)) + day + B - 1524;
    JD_frac=(hours + minutes/60. + seconds/3600.)/24. - .5;
    T = JD_whole - 2451545; 
    T = (T + JD_frac)/36525.;
    L0 = DEG_TO_RAD * fmod(280.46645 + 36000.76983 * T,360);
    M = DEG_TO_RAD * fmod(357.5291 + 35999.0503 * T,360);
    e = 0.016708617 - 0.000042037 * T;
    C=DEG_TO_RAD*((1.9146 - 0.004847 * T) * sin(M) + (0.019993 - 0.000101 * T) * sin(2 * M) + 0.00029 * sin(3 * M));
    f = M + C;
    Obl = DEG_TO_RAD * (23 + 26/60. + 21.448/3600. - 46.815/3600 * T);     
    JDx = JD_whole - 2451545;  
    GrHrAngle = 280.46061837 + (360 * JDx)%360 + .98564736629 * JDx + 360.98564736629 * JD_frac;
    GrHrAngle = fmod(GrHrAngle,360.);    
    L_true = fmod(C + L0,TWOPI);
    R=1.000001018 * (1 - e * e)/(1 + e * cos(f));
    RA = atan2(sin(L_true) * cos(Obl),cos(L_true));
    Decl = asin(sin(Obl) * sin(L_true));
    HrAngle = DEG_TO_RAD * GrHrAngle + Lon - RA;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////

void volc()
{
   int analog_value = analogRead(A0);
    temp = (analog_value * 5.0) / 1024.0; 
    input_voltage = temp / (r2/(r1+r2)); 
    lcd.setCursor(14, 3);
    if(input_voltage<10)
    {
    lcd.print("DOLDUR");
    }
 if(input_voltage>10)
    {
    lcd.print(input_voltage);
    lcd.print("v");
    }
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
void bekle()
{
   lcd.setCursor(0,1);
    lcd.print("     GPS SiNYALi    ");
    lcd.setCursor(0,2);
    lcd.print("     BEKLENiYOR     ");
    delay(1000);
    //lcd.clear();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void yok()

{
    lcd.setCursor(0,1);
    lcd.print("     GPS CiHAZI     ");
    lcd.setCursor(0,2);
    lcd.print("   TAKILI DEGiL!    ");
    delay(1000);
    lcd.clear();
  } 




14 Mayıs 2021 Cuma

İpuçları

 Bâzı şeyleri satın alamazsınız, tecrübe de bunlardan birisi. Meşhur hikâyedir, adam attan düşmüş, yatakta ilk sözü "bana attan düşen birisini bulun" olmuş. Zaman zaman bir zorluğun pençesine düşüp kıvranmış, nîce debelenmeden sonra bulduğumuz hâl çâresi kulağımıza küpe olmuştur. İşte böyle debelenmeler netîcesinde edindiğim tecrübeleri paylaşmak istiyorum.

* Yukarıdaki resimde görüldüğü gibi bir Arduino Pro Micro satın alırsanız, Arduino IDE'de "leonardo"yu seçin.

Arduino Leonardo'da iki adet seri UART olduğu için eğer RX ve TX yazılı pinleri kullanmak istiyorsanız, serial yerine serial1'i seçmelisiniz.

* Arduino nano'da 13. pin 1k'lık bir direnç ile devredeki LED'e bağlıdır. Eğer 13. portu uygulamanızda kullanmak istiyorsanız, bu pinin LED ile olan bağlantısını kesmelisiniz.

* Piyasada "Li ion şarj devresi" ibaresi de kullanılarak satılan pil koruma ve dengeleme devreleri ŞARJ için ÜRETİLMEMİŞTİR. Bu devrenin görevi, seri bağlanan li ion pillerin aşırı akım ve gerilime ve aşırı boşalmaya karşı korunması ve piller arasında dengeleme sağlamaktır.

* USB'den seriye dönüştürücüler ne kadar masum?

Son zamanlarda ESP32 CAM kartları pek revaçta. Üzerine takılan minicik bir kamerayla kolayca IP kamera, yüz tanıma sistemi, hareket hâlinde resim çekme uygulamaları gerçekleştirilebiliyor bu kartla. Hadi oynayayım diye aldım bu kartlardan. Arduino IDE'si üzerinden programlamak üzere USB'den seriye dönüştürücü bir arabirim gerekiyordu. Elimde, FTDI232 çipiyle çalışan bir dönüştürücü vardı, bunu kullandım.

Gelin görün ki ESP32 kartını bir türlü programlayamıyordum. Sürekli "A fatal error occurred: Failed to write compressed data to flash after seq 0 (result was C100)" hata mesajı alıyordum. Seri iletişim hızını düşürdüm başka şeyler denedim. I ıh. Olmuyordu. Sonunda bir yerlerde "bu FTDI çiplerinin sahteleri pek randımanlı çalışmıyor" gibi bir şeyler okuyunca, Silicon Labs'ın CP210X tümdevresini kullanan bir başka USB seri dönüştürücüyü denedim ve ESP32 şıppadanak programlandı.
FTDI232 çipini kullanırken dikkat!

Plâstik kutulara düzgün delik açmak

Bir devre yaptınız, şık bir de plastik kutu satın aldınız. Kutuda soğutma delikleri, LED delikleri vs. açmak istiyorunuz. İki problemle karşılaşırsınız:

1) Delikleri eşit mesafede açmak

2) Deliğin etrafının düzgün olması.

1. problemi bertaraf etmek için, tutunun üzerine eşit aralıklarla çizgiler çizip, birleşme noktalarını delerek eşit mesafeli delikler elde edebilirsiniz. Ancak, çizgileri çizdiğiniz kalemin izlerini plâstik üzerinden çıkarmak zor olabileceği gibi, asetat kalemi gibi bir kalem kullanırsanız, plâstiği boyama riskiniz de ortaya çıkar. Bu mahzurlar olmadan delik delmenin en kolay yolu şöyledir:

Yukarıda görüldüğü gibi bir delikli pertinaksı kutunuzun deliklerin açılacağı yüzüne tercihan kağıt bantla yapıştırın. Deleceğiniz delikleri bir kalemle işaretleyin ve 0.8 mm'lik bir matkap ucuyla PERTİNAKSI KALDIRMADAN delikleri delin. 


Sonra pertinaksı kaldırın. Deldiğiniz delikleri istediğiniz kalınlıkta bir matkap ucuyla genişletin.
2. problemi bertaraf etmek için, delikleri görünen yüzden değil ters taraftan başlayarak delin. Plâstik, delinirken matkap ucunun dönmesiyle ısınarak bir miktar erir. Bu erişim plâstik delmeye başladığınız yüzde kalır.

Yukarıda 1. problemin çözümünde ilk olarak 0,8mm'lik bir matkap ucuyla delikleri delmiştik, genişletme deliklerini MUTLAKA görünen yüzün tersinden başlayarak açın.


Sonuç böyle olacaktır.

Âcil duruma hazırlık

Güncelleme : 5  Geçenlerde basında şöyle bir haber yer aldı: 'İngiltere Başbakan Yardımcısı Oliver Dowden, bir felaket sonucu internet v...