Friday, 28 February 2014

Echo display

I have been playing with the 128 x 64 bit mapped display LCD. This can be driven over an SPI bus and can use the very flexible u8glib library. My set up is like this. The display shows both the distance away from the sensor in cm, and has a graphical bar that move left and right to show this distance.

2014 02 28 18 35 39

Some of the methods for the u8glib library (Google it to find/download) are:

lcd.begin(); // reset display and put in default state
lcd.enableCursor(); // cursor visible
lcd,disableCursor(); // cursor not visible (default state)
lcd.setCursorPos(x, y); // cursor to x, y
lcd.setColorIndex(col); // BW on = 1, off = 0
lcd.drawBitMap(x, y, cnt, h, *bitmap); // draw bit map of cnt bytes
                                       // width is cnt * 8
                                       // at x, y (top left) height h
lcd.drawBox(x, y, w, h); // filled box at x, y. width w, height h
                         // fill set by color index
lcd.drawCircle(x, y, rad, opt); // x, y centre, radius rad, 
                                // opt is option to draw part circle
                                // U8G_DRAW_UPPER_RIGHT, ...LEFT,
                                //  ... LOWER_LEFT, ... RIGHT, ...ALL
lcd.drawDisk(x, y, rad, opt); // as above but filled circle
lcd.drawFrame(x, y, w, h); // frame at x, y width w height h
lcd.drawRFrame(x, y, w, h, r); // rounded frame, edges radius r
lcd.drawHLine(x, y, w); // draw horizontal line from x,y width w
lcd.drawVLine(x, y, h); // draw vertical line from x, y height h
lcd.drawLine(x1, y1, x2, y2,); // draw line from x1, y1 to x2, y2
lcd.drawTriangle(x1, y1, x2, y2, x3, y3); // filled triangle
lcd.drawPixel(x, y); // pixel at x, y
lcd.setFont(*font); // sets font - see table below
lcd.getStrWidth(*s); // returns string width in pixels in current font
lcd.drawStr(x, y, *s); // string at x, lower left of font characters 
lcd.firstPage(); // starts a “picture loop”
lcd.nextPage(); // marks end of “picture loop”, returns 0 if finished


x = col, y = row down from the top left 0, 0 of the LCD. Images (like boxes) draw w = right, h = down from the index x, y. f

For an 128 x 64 display using the ST7920 controller chip an object can be created very simply with

#include “u8glib.h”
U8GLIB_ST7920_128X64_1X lcd(SCK, MOSI, CS); // lcd object


where SCK, MOSI & CS are the standard Arduino Uno SPI bus signals - see code below for connections. Note that display PSB must be GND for serial SPI bus operation.

Code

// QC12864B display using ST7920, with built in pot "VR" for adjusting VO
// LCD connections VSS - GND, VDD - 5V, RS - 10, RW - 11, E - 13, PSB - GND, A - 120R - 5V, K - GND
// sonar Trig 8, Echo 9

#include "U8glib.h"
#include "NewPing.h"

// SPI SCK, MOSI & SS
#define EN 13
#define RW 11
#define CS 10

// sonar
#define TRIG 8
#define ECHO 9
#define MAXDIST 200

// display set up, bar, line position length, caption position
#define BARWDTH 5
#define BARHT 10
#define LINEX1 2
#define LINEY1 50
#define LINELEN 120
#define CAPY1 10

// lcd object
U8GLIB_ST7920_128X64_1X lcd(EN, RW, CS); // SCK = EN D13, MOSI = RW D11, SS = CS D10

// sonar object
NewPing sonar(TRIG, ECHO, MAXDIST);

void setup(void)
{
  lcd.begin(); // init display
  lcd.setRot180(); // flip screen
}

void loop(void) 
{
  unsigned int barht; // bar height = number of echos (0-50)
  unsigned int barecho; // echo distance (0 to 120-BARWDTH) = 50
  unsigned cm; // echo return
  char cmbuf[4]; // echo return in ASCII
  

  cm = sonar.ping_cm(); // get distance echo
  sprintf(cmbuf, "%d", cm); // convert to ASCII
  barecho = map(cm, 0, MAXDIST, LINEX1, LINEX1 + LINELEN); // scale cm to line len
  barht = BARHT; // set bar height
   
  // picture loop
  lcd.firstPage();  
  do 
  {
    lcd.setFont(u8g_font_helvR08); // font height "A" of 8 pixels, 2 rows "g" descenders
    
    lcd.drawStr(LINEX1, CAPY1, "Echo distance measurement"); // col 2 row 8, text bottom left is on row
    lcd.drawStr(40, CAPY1 + 11, cmbuf);
    lcd.drawStr(60, CAPY1 + 11, "cm");
    
    lcd.drawLine(LINEX1, LINEY1, LINEX1 + LINELEN, LINEY1); // base line
    
    lcd.drawStr(LINEX1, LINEY1 + 10, "0"); // scale numbers
    lcd.drawStr(LINEX1 + LINELEN - 16, LINEY1 + 10, "200"); 
    
    lcd.drawBox(LINEX1 + barecho, LINEY1 - barht, BARWDTH, barht);
    

  } while(lcd.nextPage());
  
  // rebuild the picture after some delay
  delay(500);
}

Tuesday, 18 February 2014

Starting thoughts for an Arduino based SDR

Humming about in my mind for some time has been an Arduino based SDR. A few things have come together to at least create a schematic of how it would look.

2014 03 06 11 02 26

On the left is a balanced mixer. This circuit is very interesting as it works both ways. An input from the antenna goes to the SDR Tayloe detector, on the other hand a signal from the Tayloe circuit (for transmitting!) will go out to the antenna. The JFETs used can provide 0.2-0.5W output.

The Tayloe detector uses the FET switch FST3253, which is very common in SDR designs. The I & Q (in-phase and quadrature) signals for the detector switching are generated by the Arduino Uno - more about that below.

The outputs 1B... of the FST3253 go to the summing capacitors of 470nF and the op-amps which provide the audio output. The balanced mixer runs from the Ardunio 3V3 supply, as do the bias to the op-amps (on the +ve inputs). The FST3253 and op-amps run on the Arduino 5V supply.

Arduino Side

The Arduino has to do two things, generate the I & Q signals in quadrature, and drive the DDS AD9850 VFO. A previous post showed how to connect and drive the VFO. So here is the other bit for the I & Q signals.

The chip on my Arduino Uno is the ATmega328. This has three Timers (0, 1 & 2) but no output circuitry to generate quadrature signals (a sad omission on the part of the chip designers...). But in this configuration different signals could be used, generated either by an external counter or directly from the Arduino... which works is to found out when I build it!

So what I do is to set up one of the timers (Timer0) to generate two square waves. One with twice the frequency of the other. Like this:

Screen Shot 2014 02 18 at 12 33 36

To do this you need to understand the complexities of the timers. Timer0 is an 8-bit register which counts up at the clock frequency of 16MHz. Every time is resets it toggles the output signal OC0A which comes out of pin D6 of the Arduino Uno board. The count at which is resets can be set by a value in the OCR0A register. So putting a value of 32 in OCR0A will make it count up to 32 then reset. Thus generating a signal on pin 6.

There is a second separate compare register called OCR0B which can be set up in a mode such that it gets set on the counter reset, and cleared when the count in the OCR0B register is reached. This OC0B signal is then output on pin 5 of the Arduino Uno board. If the OCR0B register is loaded with the value 16 (32/2), then the OC0B signal will be twice the frequency of the OC0A signal as you can see in the diagram.

Other values can be tried for OCR0A & B registers. This has yet to be tried and checked.

The FT3253 can be driven either from a counter, or maybe directly from the two signals from the Arduino...

The snippet of Arduino code to set up Timer0 and generate these signals is:

// Using Timer0, Fast PWM mode
// WGM = 111, mode 7
// COM0A = 01, toggle on match, with WGM02 = 1
// COM0B = 10, clear output OC0B on match, set at BOTTOM
// CS0 = 001, no pre-scale /1
// OCR0A = 32, OCR0B = 16, 50% duty

// includes and object definitions go here

void setup()
{
  pinMode(5, OUTPUT); // OC0B
  pinMode(6, OUTPUT); // OC0A
  // could use DDRD = DDRD | B01100000; i.e. bits 5 & 6 of PORTD OUTPUTS

  // any other setup code goes here

  TCCR0A = 0; // init registers, set all bits to 0
  TCCR0B = 0;

  // set TCCR0A
  // 7        6        5        4        3        2        1        0
  // COMP0A1  COMPA0   COMPB1   COMPB0   -        -        WGM01    WGM00 
  //    0        1       1        0      0        0          1       1    
  // set OC0A on match: COM0A1 = 1, COM0A0 = 1
  // set Fast PWM: WGM01 = 1, WGM00 = 1

  TCCR0A = (1 << COM0A0) | (1 << COM0B1) | (1 << WGM01) | (1 << WGM00);



  // set TCCR0B
  // 7        6        5        4        3        2        1        0
  // FOC0A    FOC0B    -        -        WGM02    CS02     CS01     CS00 
  //   0        0      0        0          1        0        0        1
  //prescale CS02 = 0, CS01 = 0, CS00 = 1, and Fast PWM WGN02 = 1

  TCCR0B = (1 << WGM02) | (1 << CS00);

  OCR0A = 32;
  OCR0B = 16;
}

void loop()
{
 // code for AD9850 goes here
}


The Transmitter

Instead of pins 1B... of the FST3253 going out to the op-amps, a pair of op-amps are used to input 180 deg phase shifted I & Q audio to these pins, this will then be encoded, mixed in the mixer, and transmitted to the antenna. Voila.

Use an SDR program

The I & Q audio signals can be decoded with an SDR progam, for example DSP Radio for the Mac, this progran can also generate the I & Q signals from the Mac microphone input on transmit.

The only part of the SDR "loop" not yet joined up is the control of the AD9850 VFO by the SDR prorgam. Almost all SDRs use a micro chip with sofware pre-loaded to repond to commends from the SDR software over a USB connection. But so far I have not found out how this works...

Thursday, 6 February 2014

GPS with added Start record and distance travelled

And another update to my playing around with the GPS module. This code adds a more complex display with

Start Position - recorded when you press a button

Current Position, or "No Fix" if no valid satellite data received

Time and Date

Distance travelled from Start to Current in nm

2014 02 06 15 32 56

Here's the code:

// GPS with report of No Fix 
// and record/display of start position 
// and distance travelled

#include "SoftwareSerial.h"
#include "U8glib.h"

// GPS 11 RX, 10 TX
SoftwareSerial gps(10, 11);

// LCD 6 SCK(E), 5 MOSI(RW), 4 SS(RS)
U8GLIB_ST7920_128X64_1X lcd(6, 5, 4);

char gpsbuf[200]; // GPS output line
char temp[20] = "No Start posn";
char startlatdeg[20] = "--", startlatmin[20] = "", startlondeg[20] = "--", startlonmin[20] = "";
char startns[2] = "", startew[2] = "";
char curlatdeg[20] = "--", curlatmin[20] = "--", curlondeg[20] = "--", curlonmin[20] = "--";
char curns[2] = "-", curew[2] = "-";
char time[20] = "__", date[20] = "--";
char fix[5];
int startflag = false;

  
void setup()
{
    gps.begin(9600); // start GPS serial
    Serial.begin(9600);
    pinMode(2, INPUT_PULLUP); // LOW = read start position
}

void loop()
{
  float lat;
  float lon;
  float dist;
  
  do // get GPS output, look for $GPRMC
  {  
    getline(gpsbuf);
  } while(strncmp(gpsbuf, "$GPRMC", 6) != 0);
  Serial.println(gpsbuf);
  
  // button press records start position
  if(digitalRead(2) == LOW)
  {
    delay(5); // button bounce
    extfmt(3, "**", gpsbuf, startlatdeg); // read deg lat
    extfmt(3, "  *****", gpsbuf, startlatmin); // read min lat
    extfmt(4, "*", gpsbuf, startns); // NS
    extfmt(5, "***", gpsbuf, startlondeg); // read deg lon
    extfmt(5, "   *****", gpsbuf, startlonmin); // read min lon
    extfmt(6, "*", gpsbuf, startew); // EW
    startflag = true;   
  }
  
  
 // extract current position to cur*** arrays
  extfmt(2, "*", gpsbuf, fix);
  Serial.println(fix);
  
  if(fix[0] == 'A')
  {
  extfmt(3, "**", gpsbuf, curlatdeg); // read deg lat
  extfmt(3, "  *****", gpsbuf, curlatmin); // read min lat
  extfmt(4, "*", gpsbuf, curns); // NS
  extfmt(5, "***", gpsbuf, curlondeg); // read deg lon
  extfmt(5, "   *****", gpsbuf, curlonmin); // read min lon
  extfmt(6, "*", gpsbuf, curew); // EW
  extfmt(1, "**:**", gpsbuf, time);
  extfmt(9, "**-**-**", gpsbuf, date);
  }
  else strcpy(curlatdeg, "No Fix");
  
  
  if(startflag) // if there is a cstart position
  {
    
    lat = atof(startlatdeg) * 60 + atof(startlatmin);
    lat -= atof(curlatdeg) * 60 + atof(curlatmin); // lat in minutes
    lon = atof(startlondeg) * 60 + atof(startlonmin);
    lon -= atof(curlondeg) * 60 + atof(curlonmin); // lon in minutes
    dist = sqrt(lat * lat + lon * lon);
    sprintf(temp, "%d", dist); // print int into temp buffer
  }
   
   lcd.firstPage(); // prepare display
   
   do
   {
     lcd.setFont(u8g_font_helvR08);
     
     // 1st line
     lcd.drawStr(2, 8, "Start");
     
     lcd.drawStr(2, 17, startlatdeg);
     lcd.drawStr(20, 17, startlatmin);
     lcd.drawStr(48, 17, startns);
     
     lcd.drawStr(65, 17, startlondeg);
     lcd.drawStr(87, 17, startlonmin);
     lcd.drawStr(118, 17, startew);
     
     lcd.drawStr(2, 26, "Current");
     
     lcd.drawStr(2, 35, curlatdeg);
     lcd.drawStr(20, 35, curlatmin);
     lcd.drawStr(48, 35, curns);
     
     lcd.drawStr(65, 35, curlondeg);
     lcd.drawStr(88, 35, curlonmin);
     lcd.drawStr(118, 35, curew);
     
     lcd.drawStr(2, 44, "Time");
     lcd.drawStr(65, 44, "Date");
     lcd.drawStr(2, 53, time);
     lcd.drawStr(65, 53, date);
     
     lcd.drawStr(2, 62, "Dist nm");
     lcd.drawStr(44, 62, temp);
     
   } while(lcd.nextPage());   
}

// get a line from the GPS
void getline(char *buf)
{
  int bp = 0;
  char c;
  
  do
  {
    c = gps.read(); // -1 if no char available
    if(c == -1) continue;
    buf[bp++] = c;
  } while(c != '\n');
  buf[bp] = '\0';
}

// find field, extract in fmt from inbuf to outbuf
// fmt * = copy inbuf to outbuf, else copy fmt to outbuf
void extfmt(int field, char *fmt, char *inbuf, char *outbuf)
{
  int f = 0; // field count, common & input pointers
  int fp = 0;
  int op = 0;
  int ip = 0;
   
  // find field and ip start
  while(f != field)
  {
    while(inbuf[ip++] != ',');
    f++;
  }
  
 // format from ip
    while(fmt[fp] !='\0')
    {      
      if(fmt[fp] == ' ') ip++; // skip along
      else if(fmt[fp] == '*')
      {
        outbuf[op++] = inbuf[ip++]; // copy in to out
      }
      else
      {
        outbuf[op++] = fmt[fp]; // insert format
      }
      fp++;
    }
  outbuf[op] = '\0'; // add end of string
}
  

Monday, 3 February 2014

HELP GY273 compass on I2C bus

GY273 compass on I2C bus

Here’s an example using the GY273 compass and the Wire.h library. The GY273 detects three axes for magnetic field. It uses the Honeywell HMC5883L chip.

Screen Shot 2014 02 03 at 13 10 09

Top view of chip, arrow shows +ve output for the axis direction. The module has an I2C address of 0x1E.

Screen Shot 2014 02 03 at 13 10 35


// compass module GY273 I2C
/* connections
GY273    Arduino
VCC       3.3V
GND       GND
SCL       A5
SDA       A4
DRDY      not used
*/

#include "Wire.h"

// module I2C address
#define GY273ADDR 0x1E

// register addresses for Axes X, Y and Z data, see table above
#define X 0x03
#define Z 0x05
#define Y 0x07


void setup() 
{
  Serial.begin(9600);
  
  Wire.begin(); // create wire object
 
  initGY273(); // initialise module
}

void loop() 
{
  /* Read each sensor axis data and output to the serial port */
  Serial.print(GY273read(X));
  Serial.print(" ");
  Serial.print(GY273read(Y));
  Serial.print(" ");
  Serial.println(GY273read(Z));
  
  // Wait a little before reading again
  delay(500);
}


// initialise module
void initGY273(void)
{
  // Set the module to 8x averaging, 15Hx rate
  Wire.beginTransmission(GY273ADDR);
  Wire.write(0x00); // config reg A
  Wire.write(0x70); // 0 11 100 00
          
  // Set a gain of 1090 (default), range +/-1.3Ga, gain 1090
  Wire.write(0x01); // config reg B
  Wire.write(0x20); // 001 00000
  Wire.endTransmission();
}


// read from X, Y or Z data registers 
// return 16 bit signed int
int GY273read(byte Axis)
{
  int r;
  
  // single measurement mode
  Wire.beginTransmission(GY273ADDR);
  Wire.write(0x02); // mode register
  Wire.write(0x01); // 000000 01
  Wire.endTransmission();
  delay(50); // was 6
  
  // move register pointer 
  Wire.beginTransmission(GY273ADDR);
  Wire.write(Axis); // to one of the axis data registers 0x03, 0x05 or 0x07
  Wire.endTransmission();
   
  // Read the 2 bytes of data, concatenate and return 
  Wire.requestFrom(GY273ADDR, 2);  
  r = Wire.read() << 8; // shift L for 8 MSB, reads register 0x03...
  r |= Wire.read(); // add LSB to make int, reads register 0x04...
  return r;
}

HELP GPS Module

GPS NEO-6M-0-001 - HCMODU0023 module with active antenna

Screen Shot 2014 02 03 at 13 06 27 Pin connections

Module - Arduino
GND    -     GND
RX       -  Pin 11
TX     -  Pin 10
VCC    -    3.3V
Output is via UART, 9600 baud 8/none/1. Format is NMEA
	$ ADDR ,VALUES *CHECKSUM CRLF
ADDR is GPxxx xxx = msg format
,VALUES are ‘,’ separated values
*CHECKSUM (no prior ‘,’) is Hex exclusive OR $ to *
Latitude and longitude

Latitude or Longitude min.decmin

e.g. Lat 5204.82093 = 52deg 04.82093min

e.g. Long 00120.77221 = 1deg 20.77221min

Typical module output and message formats

NMEA sentences

$Address ,[value] ... *[checksum]
$GPRMC fields are

Field
1 UTC hhmmss.ss
2 A|V status
3 Lat ddmm.mmmm
4 N|S
5 Lon dddmm.mmmm
6 E|W
7 SOG knots
8 COG deg T
9 Date ddmmyy
+ some others

$GPVTG fields are

Field
1 True Track
2 T
3 Magnetic track
4 M
5 Ground Speed, knots
6 N
7 GroundSpeed, Km/h
8 K

$GPGGA fields are

Field
1 Time UTC
2 Latitude
3 NS
4 Longitude
5 EW
6 Fix quality 0 - invalid, 1 = GPS fix, 2-9 other
7 No of satellits tracked
8 Horix dilution
9 Altitude metres ASL
10 Height geoid abbove WSG84 metres
+ others

$GPGSV fields are satellite positions and data

$GPGLL fields are
1 Latitude
2 NS
3 Longitude
4 EW
5 Time
6 AV active or void
Code for 128 x 64 LCD display using U8glib LCD library

#include "SoftwareSerial.h"
#include "U8glib.h"

// GPS 11 RX, 10 TX
SoftwareSerial gps(10, 11);

// LCD 6 SCK(E), 5 MOSI(RW), 4 SS(RS)
U8GLIB_ST7920_128X64_1X lcd(6, 5, 4);

void setup()
{
    gps.begin(9600); // start GPS serial
}
void loop()
{
  char gpsbuf[200]; // GPS output line
  char pbuf[20]; // extract and printed buffer
  
  do
  {  
    getline(gpsbuf);
  } while(strncmp(gpsbuf, "$GPRMC", 6) != 0);
      
   lcd.firstPage();
   do
   {
     lcd.setFont(u8g_font_helvR08);
     
     // 1st line
     lcd.drawStr(2, 8, "Latitude");            
     lcd.drawStr(66, 8, "Longitude");
     // Lat
     extfmt(3, "** ****", gpsbuf, pbuf);
     lcd.drawStr(2, 19, pbuf);
     // NS 
     extfmt(4, "*", gpsbuf, pbuf);
     lcd.drawStr(40, 19, pbuf);
     // Lon
     extfmt(5, "*** ****", gpsbuf, pbuf);
     lcd.drawStr(66, 19, pbuf);
     // EW 
     extfmt(6, "*", gpsbuf, pbuf);
     lcd.drawStr(110, 19, pbuf);
     
     // 2nd line
     lcd.drawStr(2, 40, "Time");
     lcd.drawStr(66, 40, "Date");
     // Time
     extfmt(1, "**:**", gpsbuf, pbuf);
     lcd.drawStr(2, 51, pbuf);
     // Date 
     extfmt(9, "**-**-**", gpsbuf, pbuf);
     lcd.drawStr(66, 51, pbuf);        
   } while(lcd.nextPage());   
}

// get a line from the GPS
void getline(char *buf)
{
  int bp = 0;
  char c;
  
  do
  {
    c = gps.read(); // -1 if no char available
    if(c == -1) continue;
    buf[bp++] = c;
  } while(c != '\n');
  buf[bp] = '\0';
}

// find field, extract in fmt from inbuf to outbuf
// fmt * = copy inbuf to outbuf, else copy fmt to outbuf
void extfmt(int field, char *fmt, char *inbuf, char *outbuf)
{
  int f = 0; // field count and char pointers
  int fp = 0;
  int ip = 0;
  int op = 0;
   
  // find field and ip start
  while(f != field)
  {
    while(inbuf[ip++] != ',');
    f++;
  }
  
  // format from ip
    while(fmt[fp] !='\0')
    {      
      if(fmt[fp] == '*') outbuf[op] = inbuf[ip++]; // outbuf = inbuf
      else outbuf[op] = fmt[fp]; // outbuf = fmt
      fp++;
      op++;
    }
  outbuf[op] = '\0'; // add end of string
}
  

HELP Bluetooth module

BLUETOOTH

Bluetooth JY-MCU module (Hobby Components) HC-06 module

Screen Shot 2014 02 03 at 12 54 10  Connection as above (4 pins)

BT Module HC-06
BT Name: see below
Pwd: see below
Baud rate default 9600
Can be “slave” only as based on HC-06 module.

TXD is the data received from remote

RXD is the data to send to remote

BT module configuration

Do not pair before running this sketch, in order to send AT commands BT red LED must be flashing.

Set name to GanymedeBT (or whatever you want), PIN to 0000, baud rate to 9600 using AT commands below:
/* BT module configure
BT flashing
send AT+NAMEGanymedeBT
send AT+PIN0000
send AT+BAUD4 (9600)
connections
Arduino       BT
5V            PIN 2 (VCC)
GND           PIN 3 (GND)
10            PIN 4 (TXD)
11            PIN 5 (RXD)
*/

#include "SoftwareSerial.h"

#define RXPIN 10 
#define TXPIN 11

SoftwareSerial BT(RXPIN, TXPIN); // create class BT

void setup()  
{
  Serial.begin(9600); // start USB
  BT.begin(9600); // start BT
}

void loop()
{
  if (BT.available())
  {
    Serial.write(BT.read()); // echo from BT
  }
 
  if (Serial.available())
  {
    BT.write(Serial.read()); // write AT command to BT
  }
}


Then on a Mac OSX computer System > Preferences > Bluetooth. “GanymedeBT” will be detected and can be [Pair]ed.

Serial Test BT

Use CoolTerm to transmit/receive BT module, and Serial Monitor to receive/transmit back.

Echo test BT. Data sent on Serial Monitor to BT and back to CoolTerm, and CoolTerm back to Serial Monitor

Connections

Arduino       BT
5V            PIN 2 (VCC)
GND           PIN 3 (GND)
10 (RXPIN)    PIN 4 (TXD) - data transmitted from remote
11 (TXPIN)    PIN 5 (RXD) - remote to receive this data


Operation:

- load sketch - System > Preferences > Bluetooth "GanymedeBT" Pair - CoolTerm > Options, select GanymedeBT serial port - open IDE serial monitor

#include "SoftwareSerial.h"

#define RXPIN 10
#define TXPIN 11

SoftwareSerial BT(RXPIN, TXPIN); // create BT class object

void setup()  
{
  Serial.begin(9600); // init serial
  BT.begin(9600); // init BT
}

void loop()
{
  if (BT.available()) // if BT OK
  {
    Serial.write(BT.read()); // read from BT, write to USB
  }
 
  if (Serial.available()) // if USB OK
  {
    BT.write(Serial.read()); // read USB, write to BT
  }
}


Another Bluetooth project

This project uses the Morse generator
morseEnDecoder.h
to output morse to an LED. The messgae to send are received by Bluetooth from the iMac.
1 System > Preferences > Bluetooh
2 Pair the GanymedeBT module
3 Open the terminal app and enter: screen /dev/tty.GanymedebT-DevB
4 The terminal app will clear the screen and now act as a terminal, morse messages can be sent by typing them on the KB
5 Alternatively the CoolTerm app can be used. Options > Serial Port > GanymedeBT-DevB and Options > Terminal > Key Emulation > CR. Disconnect after use.


Code

// morse sender - flash LED on 7, could be a relay
/*
connections
Arduino       BT
5V            PIN 2 (VCC)
GND           PIN 3 (GND)
10 (RXPIN)    PIN 4 (TXD) - data transmitted from remote
11 (TXPIN)    PIN 5 (RXD) - remote to receive this data
- load sketch
- System > Preferences > Bluetooth "GanymedeBT" Pair
- CoolTerm > Options, select GanymedeBT serial port
*/

#include "MorseEnDecoder.h"
#include "SoftwareSerial.h"

#define WPM 5

#define LEDPIN 7
#define RXPIN 10
#define TXPIN 11

morseEncoder morseOut(LEDPIN);

SoftwareSerial GanymedeBT(RXPIN, TXPIN); // GanymedeBT class object in Arduino

long lastTrans;
long current;
boolean ended = true;

void setup() {
  
  pinMode(LEDPIN, OUTPUT);
  
  GanymedeBT.begin(9600);
  
  morseOut.setspeed(WPM);
  
  lastTrans = millis();
}

void loop() {
  
  current = millis();
  
  morseOut.encode();
  
  if(GanymedeBT.available() && morseOut.available()) {
    char text = GanymedeBT.read();
    GanymedeBT.write(text); // echo
    if(text == '\r') GanymedeBT.write('\n'); // when CR received addcq cq cq m6kwh NL
    morseOut.write(text);
  }
  
  if(!morseOut.available()) {
    lastTrans = current;
    ended = false;
  }
}

HELP 2 Relay module

I bought on of these as I wanted to play with it. It is rated at 240V/10A on both circuits with a single pole change over switch on each. The replays are isolated by opt-o-couplers.

2014 02 03 12 35 21

It took me over an hour to find the circuit diagram on the web, by calling up Google images for a 2 relay module, and looking at every sit4e showing one like mine! I finally found this one:

Screen Shot 2014 02 03 at 12 33 27

The IN1 & IN2 are active LOW inputs which drive the Opt-o-couplers. They in turn drive the transistors Q1 & Q2 and the relays. The power supply for the relays can be taken from the VCC supply to the board (i.e. from the Arduino) or from a separate supply by removing the jumper from JD-VCC and VCC. Whatever you do DO NOT connect the jumper from VCC to GND as a place to park it, this will short circuit the Arduino +5V supply. If you want to use a separate supply for the relays then just remove the jumper and discard it.

Sunday, 2 February 2014

Updated AD9850 VFO

I recently blogged about the AD9850 frequency synthesiser (DDS). This can output any frequency up to 40MHz under program control from an Arduino.

I have just received a new display shield and here is the AD9850 controlled by the Arduino and a display of amateur band and frequency. As the shield has only 5 buttons for user input (the sixth is a Reset button). I have chosen to use them for:

1 Select band - step through the amateur bands, on each band the band edges are known and when selected a mid band frequency is programmed

2 Left/right +/-10KHz and up/down +/-10Hz buttons. this make tuning a bit arduous as you have to push the buttons many times to tune a band, but you can tune to the nearest 10Hz.

The display shield has a series of resistors that mean the buttons output various voltages, these are measured by the analog input as digital 0-1023 data.

2014 02 02 10 17 17

Code

#include "LiquidCrystal.h"

// DDS-9850 module programmer, with LCD display and button inputs


#define RESET 0     // Pin  RST
#define DATA 1      // Pin  DATA
#define FQ_UD 2     // Pin  FQ
#define W_CLK 3     // Pin  CLK
#define D4 4        // LCD data pins
#define D5 5
#define D6 6
#define D7 7
#define RS 8        // LCD RS * EN pins
#define EN 9
#define BUTTONS A0   // button sensor

// short high pulse
#define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }

// names of bands
char bandName[][5] = {"160", "80", "60", "40", "30", "20", "17", "15", "12", "10"};

// array lookup of lower, current and upper band limits
#define LOWER 0
#define CURRENT 1
#define UPPER 2

// band limits
double freqRange[10][3] = {
    {1810000, 1900000, 2000000},
    {3500000, 3600000, 3800000},
    {5258000, 5100000,5406500},
    {7000000, 7100000, 7200000},
    {10100000, 10300000, 10150000},
    {14000000, 14200000, 14350000},
    {18068000, 18050000, 18168000},
    {21000000, 21250000, 21450000},
    {24890000, 24700000, 24990000},
    {28000000, 28500000, 29700000}
};

// LCD object
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);

// band number
int band;

// actual frequency
float frequency; 

void setup()
{
  pinMode(FQ_UD, OUTPUT);
  pinMode(W_CLK, OUTPUT);
  pinMode(DATA, OUTPUT);
  pinMode(RESET, OUTPUT);
   
  pulseHigh(RESET);
  pulseHigh(W_CLK);
  pulseHigh(FQ_UD);  // this pulse enables serial mode
  
  lcd.begin(16, 2);
  
  band = 0; // 160m band 0
  frequency = freqRange[band][CURRENT]; // init frequency mid-band
}

void loop() 
{
  int x;
  
  x = analogRead(BUTTONS);
  if (x < 100)
  {
    while(analogRead(BUTTONS) < 100);
    if(frequency < freqRange[band][UPPER]) frequency += 10000;
  }
  else if (x < 200) 
  {
    while(analogRead(BUTTONS) < 200);
    if(frequency < freqRange[band][UPPER]) frequency += 10;
  }
  else if (x < 400)
  {
    while(analogRead(BUTTONS) < 400);
    if(frequency > freqRange[band][LOWER]) frequency -= 10;
  }
  else if (x < 600)
  {
    while(analogRead(BUTTONS) < 600);
    if(frequency > freqRange[band][LOWER]) frequency -= 10000;
  }
  else if (x < 800)
  {
    while(analogRead(BUTTONS) < 800);
    freqRange[band][CURRENT] = frequency; // save rpvious band freq
    if(band < 9) band++;
    else band = 0;
    frequency = freqRange[band][CURRENT]; // load current band last freq
  }
  
  lcdDisplay(bandName[band], frequency);
  sendFrequency(frequency);  // freq Hz
}

// display LCD band & freq
void lcdDisplay(char* b, double f)
{

  lcd.setCursor(0,0); // write band & freq
  lcd.print("Band = ");
  lcd.setCursor(0, 1);
  lcd.print("Freq = ");
  
  delay(100); // stop flicker
  lcd.setCursor(7,0); // clear band data
  lcd.print("   ");
  lcd.setCursor(7,0); // write band data
  lcd.print(b);

  lcd.setCursor(7, 1); // clear freq data
  lcd.print("        ");
  lcd.setCursor(7, 1); // write freq data
  lcd.print(f/1000);

}

// frequency calc =  * /2^32
void sendFrequency(double frequency) 
{
  int32_t freq = frequency * 4294967295/125000000;  // note 125 MHz clock on 9850
  
  for (int b=0; b<4; b++, freq>>=8) 
  {
    tfr_byte(freq & 0xFF);
  }
  tfr_byte(0x000);   // Final control byte, all 0
  pulseHigh(FQ_UD);
}

// transfers a byte, a bit at a time, LSB first via serial DATA line
void tfr_byte(byte data)
{
  for (int i=0; i<8; i++, data>>=1) 
  {
    digitalWrite(DATA, data & 0x01);
    pulseHigh(W_CLK);   //after each bit sent, CLK is pulsed high
  }
}


HELP FTDI USB module

I have just bought an FTDI USB module. And searched the web for information about wiring it and coding for it. This module is a UART converting USB serial data to "RS232" serial interface data. It is not strictly RS232 as the signals are not all there and the interface is 5V versus the RS232 spec of 12V signals. But is does provide a valid USB interface with vendor & product IDs and unique serial number.

And what confusion there is on blogs about the operation and use!

So I decided to look into the chip used and the signals to/from the module. And here are my findings:

Screen Shot 2014 02 02 at 09 36 13

As you can see the module has a mini-USB plug and six pin connections. These are

Arduino		FTDI

Reset?          DTR output, LOW when FT232 ready (Data Terminal Ready)
TX              RXI Arduino data out
RX              TXO Arduino data in
GND             CTS input, LOW when FT232 can send data (TXO)
GND	        GND


The power to the module is provided BY THE USB INPUT (not the 3V3 pin!). The 3V3 pin is an OUTPUT from the FTDI chip able to provide 50mA at 3V3 from the USB 5V supply.

The DTR signal is "Data Terminal Ready" and is an output LOW active signal showing when the device is ready to accept signals. It has been suggested that this could be connected to the Arduino RESET line - certainly it must not be connected directly or the Arduino will be held in reset permanently - but it could be pulled positive by a 10K resistor and connected via a 10nF . This would have the effect of sending a reset pulse to the Arduino when DTR goes LOW. I can't see the value in this but... probably best to leave DTR unconnected.

The CTS signal is an active LOW input. it tells the FTDI interface when the Arduino is ready to accept data from the TXO pin. For all intents and purposes this can be connected to GND as you can assume the Arduino is always ready to accept data. At least as much as will fill its incoming buffer... but you could connect this an Arduino pin and pull it LOW when you are ready to accept data.

The RXI signal goes to the Arduino TX pin (see below for pins and software code), and the TXO goes to the Arduino RX pin.

Each FTDi module has a unique serial number. Mine is reported by my Mac computer as:

FT232R USB UART
Product ID 0x6001
Vendor ID 0x0403 (FTDI Ltd)
Serial # A702LESX


The Mac will create a device for I/O called
/dev/tty.usbserial-A702LESX
when the USB is connected. This is the port to use for data I/O, for example using a terminal program such as CoolTerm.

Screen Shot 2014 02 02 at 10 08 03

There is a documentation error on the schematic from the supplier, the default operation is 5V and there is a tiny connection between the supply pad and the 5V pad on the back of the module. However the schematic says 3V3 operation... which is incorrect.

Code

TX and RX should NOT be pins 0 & 1 of the Arduino as these are tied to the onboard USB interface. Use the library
#include "SoftwareSerial"
and create a serial object to talk to the FTDI module. For example:

#define RX 10
#define TX 11

SoftwareSerial usb(RX, TX);

void setup()
{

  usb.begin(9600); // sets FTDI interface to 9600 baud

}


OK that's it, now it should work just fine. Try it.