NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Project Help and Ideas » GPS Code Help.

March 29, 2012
by haaser
haaser's Avatar

I am new to this and I do not understand most of this code. I am hoping that someone can help me figure this out. First off thank you to E.Soderberg for coding this and getting me this far.

I am trying to take a NMEA RMC string from a FM 750(Agriculture GPS monitor). The strings that are coming from the monitor look like this.

$GPRMC,211501,A,5112.671694,N,10225.010369,W,000.03,284.2,290312,11.3,E,R1E $GPRMC,211502,A,5112.671692,N,10225.010362,W,000.05,112.4,290312,11.3,E,R1C $GPRMC,211503,A,5112.671692,N,10225.010364,W,000.04,112.4,290312,11.3,E,R1A $GPRMC,211504,A,5112.671691,N,10225.010353,W,000.07,93.1,290312,11.3,E,R24 $GPRMC,211505,A,5112.671691,N,10225.010350,W,000.06,93.1,290312,11.3,E,R27 $GPRMC,211506,A,5112.671693,N,10225.010357,W,000.06,270.9,290312,11.3,E,R16 $GPRMC,211507,A,5112.671692,N,10225.010358,W,000.02,270.9,290312,11.3,E,R1D $GPRMC,211508,A,5112.671692,N,10225.010358,W,000.07,270.9,290312,11.3,E,R17 $GPRMC,211509,A,5112.671692,N,10225.010362,W,000.04,270.9,290312,11.3,E,R1C $GPRMC,211510,A,5112.671694,N,10225.010355,W,000.10,270.9,290312,11.3,E,R13

The code that E.Soderberg will convert NMEA RMS strings and output them on the screen in a very nice format. My problem is that converts RMS and I need it to convert RMC strings. I do not understand the ISR event and I am hoping that someone can help me with this.

The project goal is to get the long. and lat. so that I can do a math calculation to calculate distance travelled. When it reaches 7 meters travelled I need it to trigger an event and reset itself to count another 7 meters and trigger the same event again.

Existing Code:

// gps.c
// for ATmega328p nano
// by E.Soderberg

#include "../libnerdkits/io_328p.h"
#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

volatile char received_from_uart, time[10], v[10], lat[12], lon[12];
volatile uint16_t nmea_state = 1;

void uartgps_init(uint8_t br) {
 // set baud rate
  UBRR0H = 0;
  UBRR0L = br;  //103 9.6kbps and 16Mhz see page 203 in datasheet//25 for 38.4kbs//207 4.8//16 57.6//8        115.2kbps

 // enable uart RX and interrupt on RX
 UCSR0B = (1<<RXEN0) | (1<<RXCIE0) | (1<<TXEN0);

  // set 8N1 frame format
  UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
}

ISR(USART_RX_vect){  // will be triggered on every character received //  gps configured for RMS only
received_from_uart = UDR0;
UDR0=received_from_uart;

nmea_state++;

if (received_from_uart == '$')nmea_state=0;

if ((nmea_state>5) & (nmea_state<18)) time[nmea_state-6]=received_from_uart;
    if ((nmea_state>19) & (nmea_state<32)) lat[nmea_state-20]=received_from_uart;
        if ((nmea_state>31) & (nmea_state<45)) lon[nmea_state-32]=received_from_uart;
            if ((nmea_state>44) & (nmea_state<50)) v[nmea_state-45]=received_from_uart;

}

int main(void) {
// init uart
uartgps_init(16);

//fire up lcd
lcd_init();
FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
lcd_home();

 // turn on interrupt handler
 sei();

  // main loop
  while(1) {

  lcd_line_one();
  fprintf_P(&lcd_stream, PSTR("GPS  Speed: %c%c%c%c kts"),v[0],v[1],v[2],v[3], v[4]);
  lcd_line_two();
  fprintf_P(&lcd_stream, PSTR("GMT: %c%c%c%c%c%c%c%c%c%"), time[1],time[2],time[3],time[4],time[5],time[6],time[7],time[8], time[9]);
  lcd_line_three();
  fprintf_P(&lcd_stream, PSTR("lat:   %c%c%c%c%c%c%c%c%c%c%c"), lat[0],lat[1],lat[2],lat[3],lat[4],lat[5],lat[6],lat[7],lat[8],lat[9], lat[10]);
  lcd_line_four();
  fprintf_P(&lcd_stream, PSTR("long: %c%c%c%c%c%c%c%c%c%c%c%c"), lon[0],lon[1],lon[2],lon[3],lon[4],lon[5],lon[6],lon[7],lon[8],lon[9],lon[10],lon[11]);
  }

 return 0;

    }
March 29, 2012
by pcbolt
pcbolt's Avatar

hi haaser -

Looks like you only need to change a few things in your ISR to read the RMC strings. All you'd really need to do is take this:

if ((nmea_state>5) & (nmea_state<18)) time[nmea_state-6]=received_from_uart;
if ((nmea_state>19) & (nmea_state<32)) lat[nmea_state-20]=received_from_uart;
if ((nmea_state>31) & (nmea_state<45)) lon[nmea_state-32]=received_from_uart;
if ((nmea_state>44) & (nmea_state<50)) v[nmea_state-45]=received_from_uart;

And modify it to read:

if ((nmea_state>6) & (nmea_state<13)) time[nmea_state-7]=received_from_uart;
if ((nmea_state>15) & (nmea_state<27)) lat[nmea_state-16]=received_from_uart;
if ((nmea_state>29) & (nmea_state<42)) lon[nmea_state-29]=received_from_uart;

Try this first and see if it displays Lat and Long. The next step is to convert the ascii text of lat/long into actual numbers (I think there is a library that does this, I'll check later on). Once you do that, you have to figure out the formula to convert the difference in lat/long co-ordinates into distance. This can be easy or hard depending on your accuracy standards. Try the code above first and see if it works. I've done the conversion to distance before so it should not be a roadblock you can't overcome.

March 29, 2012
by esoderberg
esoderberg's Avatar

Haaser,

Glad you got some use out of the code, as you can see it does work, but beware it is far from elegant (I still need to get more familiar with manipulating strings). It looks like PCBolt has you on the right path to modify it to meet your needs. The ASCII table below might help you out in converting the characters to numbers for your calculations.

ascii

March 29, 2012
by pcbolt
pcbolt's Avatar

@haaser -

If you include "stdlib.h", there is a function you can use called "atof()" which will convert an ascii string into a double floating-point number. You'll have to be careful though, the NMEA GPS format for Lat/Long is usually DDDMM.mmmmmm, which is Degrees Minutes and decimal Minutes. So your ultimate goal would be to try and convert the ascii string into decimal degrees by first splitting the string up into degrees and minutes, converting the strings to numbers using "atoi()" and "atof()" then combine them to decimal degrees by dividing the minutes by 60 and adding the whole number of degrees. Hope that made sense.

March 29, 2012
by haaser
haaser's Avatar

Thanks for all the help so far. I had a few minutes tonight to modify the code, but I feel that I am still doing something seriously wrong. I assumed that I did not need a physical serial port plugged into the bread board. I just put the TX and RX line from the serial cable to pin 2 and pin 3 of the MCU. Do not assume that I know what I am doing.

alt image text

March 29, 2012
by pcbolt
pcbolt's Avatar

@haaser

Check the baud rate of your GPS unit. It should be set to 57600 according to your code. But then again, the code seems to be setup for a 16 MHz clock. The NK clock is 14.7 Mhz. If you want to use a baud rate of 57,600 and the NK clock, line 47 in your code should read:

uartgps_init(15);

I'm not sure if this interferes with the baud rate the programmer uses which is 115,200. To be safe try using this speed (if your GPS unit can output this rate). You can set this by changing line 47 to:

uartgps_init(7);

All of this info is on pages 198-199 in the ATmega168.pdf. If you need a lower speed, the table on page 198 (4th to last column) will tell you what number to pass to the "uartgps_init()" function.

April 01, 2012
by haaser
haaser's Avatar

Ok so everything was going well. pcbolt fixed my problem with the code, but I have a new problem now. I am using the atof() function but it is not converting the text to the same as the actual char string. The Long and lon should be the same number. I tried using it as a float or double variable both give the same result. I assume that I am not doing something correct again, but I can not figure it out. I was using this http://www.cplusplus.com/reference/clibrary/cstdlib/atof/ for help

volatile float test1;
volatile char received_from_uart, time[10], v[10], lat[12], lon[12];

// main loop
  while(1){
  test1 = atof(lon);
  lcd_line_one();
  fprintf_P(&lcd_stream, PSTR("lon: %f"), test1);

alt image text

April 01, 2012
by pcbolt
pcbolt's Avatar

Hi haaser -

That is strange. I did a little test code and it seemed to work fine. Except for one thing, using AVR programming, it looks like "double" and "float" are the same size (32-bits). This is going to be a problem since a 32-bit floating point number can only contain about 7 significant decimal digits. Your lat/long values are 12 decimal digits, which would need a 64-bit "double" float which is not supported. I would suggest splitting up the lat/long strings into "deg_lat", "min_lat" and "dec_lat" (same for long). The degrees and minutes variables can be integers, and the decimal lat/long can be a float. You might need something like:

char c_deg_lon[4],c_min_lon[3],c_dec_lon[7];
uint8_t deg_lon, min_lon, i;
float dec_lon;

for (i = 0; i < 3; i++)
  c_deg_lon[i] = lon[i];
for (i = 3; i < 5; i++)
  c_min_lon[i] = lon[i];
for (i = 6; i < 12; i++)
  c_dec_lon[i] = lon[i];

// Zero-terminate strings just in case
c_deg_lon[3] = 0;
c_deg_lon[2] = 0;
c_deg_lon[7] = 0;

deg_lon = atoi(c_deg_lon);
min_lon = atoi(c_min_lon);
dec_lon = atof(c_dec_lon);

I still don't know why you got the strange error you did though...very odd. Oh and you may need to tweak the previous code I posted to get the latitude to display correctly.

April 02, 2012
by haaser
haaser's Avatar

I think that I am still having a problem with the ATOF function. Same issue as before. it will not display the correct lon. Assume that I am doing something wrong. lol. Just for clarification. I am using the USB nerdkit cable to connect to the MCU and computer. I think use putty and connect to it. I then paste in this string to the putty window. $GPRMC,211501,A,5112.671694,N,10225.010369,W,000.03,284.2,290312,11.3,E,R*1E Once that goes into the number show up on the display. Is this the correct way to test it rather then having to hook it up to the monitor everytime? Picture:

alt image text

Full code that I currently have.

// gps.c
// for ATmega328p nano
// by E.Soderberg

#include "../libnerdkits/io_328p.h"
#include <string.h>
#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"
#include <math.h>

volatile char received_from_uart, time[10], v[10], lat[12], lon[12];
volatile uint16_t nmea_state = 0;
char c_deg_lon[4],c_min_lon[3],c_dec_lon[7];
uint8_t deg_lon, min_lon, i;
float dec_lon;

void uartgps_init(uint8_t br) {
 // set baud  UBRR0L = br;  //103 9.6kbps and 16Mhz see page 203 in datasheet//25 for 38.4kbs//207 4.8//16 57.6//8        115.2kbps

 // enable uart RX and interrupt on RX
 UCSR0B = (1<<RXEN0) | (1<<RXCIE0) | (1<<TXEN0);

  // set 8N1 frame format
  UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
}

ISR(USART_RX_vect){  // will be triggered on every character received //  gps configured for RMS only
received_from_uart = UDR0; // Fetch the received byte value into the variable "ByteReceived"
//UDR0=received_from_uart; // Echo back the received byte back to the computer

nmea_state++;

if (received_from_uart == '$')nmea_state=0;
if ((nmea_state>6) & (nmea_state<13)) time[nmea_state-7]=received_from_uart;
if ((nmea_state>15) & (nmea_state<27)) lat[nmea_state-16]=received_from_uart;
if ((nmea_state>29) & (nmea_state<42)) lon[nmea_state-30]=received_from_uart;
}

//double CalculateGreatCircleDistance(double lat1, double long1, double lat2, double long2, double radius)
//{
//    return radius * Math.Acos(Math.Sin(lat1) * Math.Sin(lat2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(long2 - long1));
//}

int main(void) {
// init uart
uartgps_init(7);

//fire up lcd
lcd_init();
FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
lcd_home();

 // turn on interrupt handler
 sei();

  // main loop
  while(1){   
      for (i = 0; i < 3; i++)c_deg_lon[i] = lon[i];
      for (i = 3; i < 5; i++)c_min_lon[i] = lon[i];
      for (i = 6; i < 12; i++)c_dec_lon[i] = lon[i];

      deg_lon = atoi(c_deg_lon);
      min_lon = atoi(c_min_lon);
      dec_lon = atof(c_dec_lon);

      lcd_line_one();
      fprintf_P(&lcd_stream, PSTR("lon: %f"), dec_lon);
      lcd_line_two();
      fprintf_P(&lcd_stream, PSTR(" GMT: %c%c:%c%c:%c%c"), time[0],time[1],time[2],time[3],time[4],time[5]);
      lcd_line_three();
      fprintf_P(&lcd_stream, PSTR(" lat: %c%c%c%c%c%c%c%c%c%c%c"), lat[0],lat[1],lat[2],lat[3],lat[4],lat[5],lat[6],lat[7],lat[8],lat[9],lat[10]);
      lcd_line_four();
      fprintf_P(&lcd_stream, PSTR("long: %c%c%c%c%c%c%c%c%c%c%c%c"), lon[0],lon[1],lon[2],lon[3],lon[4],lon[5],lon[6],lon[7],lon[8],lon[9],lon[10],lon[11]);
  }
 return 0;
}
April 02, 2012
by pcbolt
pcbolt's Avatar

I think I would try to change line 70 from:

dec_lon = atof(c_dec_lon);

To:

dec_lon = atoi(c_dec_lon);

Then change the fprintf_P output (line 73) from:

fprintf_P(&lcd_stream, PSTR("lon: %f"), dec_lon);

To:

fprintf_P(&lcd_stream, PSTR("lon: %d"), dec_lon);

This should change it to an integer variable that you can manipulate later. I saw you were going to use the "Great Circle" equation above to calculate distance. You may not get the resolution you need with the 32-bit float variables. There is an easier way...kind of a hack but it should work for you.

April 02, 2012
by haaser
haaser's Avatar

The "Great Circle" equation is just what I found when I googled it. Do you have a better or easier way to calculate distance via GPS cordinates? I am very open to suggestions on this. Just so that you know we are using a RTK Glonass system which will give us sub inch accuracy levels. I know that we might not be able to get that level of accuracy, but I am not sure how else to do it.

Did a couple of trial runs with the code you sent me. So far it is showing some numbers,but I need to do some tweaking I think. I had to make one other change which I am not sure was correct?

float dec_lon; ---> uint16_t dec_lon;

April 02, 2012
by pcbolt
pcbolt's Avatar

Actually you might need uint32_t (I didn't think about that part...good catch).

Wow...you're using an RTK system? That certainly changes things a bit :-)

The "right" way to do that would be to convert lat/long into UTM x,y. That is one heck of an equation though (and you definitely need 64-bit floating variables for that).

The nautical mile was actually defined as "one minute arc of longitude at the equator" or "one minute arc of latitude". So for the distance between two points of latitude on the same meridian of longitude is just the difference in latitude times one nautical mile or;

north_south_distance = delta_lat * 1,852;   // meters

Longitude is a bit harder since the same delta_lon is 1,852 meters/minute at the equator and 0 at the north and south poles. In other words, the longitude conversion factor is dependent on your latitude...the cosine of your latitude to be precise. So the formula for east/west distance is;

east_west_distance = delta_lon * 1,852 * cos(average_latitude);

I don't think the average_latitude need to be too precise since you are taking the cosine value (remember to convert to radian measure).

Of course the final distance is;

final_distance = sqrt(east_west_distance^2 + north_south_distance^2);

I think you can just use the dec_lat/dec_lon to get deltas, but you need to put some code in for the "rollover" situations.

April 03, 2012
by haaser
haaser's Avatar

Can not figure this out. I am still struggling with converting a string to a number. I can not get the same value to show up with the conversion and I am not sure what I am doing wrong. I am not sure what else to do. Do you have any suggestions?

April 03, 2012
by haaser
haaser's Avatar

Never posted my makefile before so here it is. I am really not sure what is going on and I am hoping that someone can shed some light on this for me.

GCCFLAGS=-g -Os -Wall -mmcu=atmega168 
LINKFLAGS=-Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm
AVRDUDEFLAGS=-c avr109 -p m168 -b 115200 -P COM3
LINKOBJECTS=../libnerdkits/delay.o ../libnerdkits/lcd.o ../libnerdkits/uart.o

all:    gps-upload

gps.hex:    gps.c
    make -C ../libnerdkits
    avr-gcc ${GCCFLAGS} ${LINKFLAGS} -o gps.o gps.c ${LINKOBJECTS}
    avr-objcopy -j .text -O ihex gps.o gps.hex

gps.ass:    gps.hex
    avr-objdump -S -d gps.o > gps.ass

gps-upload: gps.hex
    avrdude ${AVRDUDEFLAGS} -U flash:w:gps.hex:a
April 03, 2012
by pcbolt
pcbolt's Avatar

Try adding this line to the "include" list:

#include <stdlib.h>

The makefile looks fine.

April 03, 2012
by haaser
haaser's Avatar

That seemed to help as the number are actually showing the correct value now. I had to increase uint8_t deg_lon, min_lon, i; --> uint16_t deg_lon, min_lon, i; as well.

I just noticed another problem with the code that I am going to try to figure out. This code will output the folowing ( lon: 102 0 25). It is not showing the correct min_lon or dec_lon values. Again just noticed the problem so I am going to see if I can figure it out. Any suggestions?

      for (i = 0; i < 3; i++)c_deg_lon[i] = lon[i];
  for (i = 3; i < 5; i++)c_min_lon[i] = lon[i];
  for (i = 6; i < 12; i++)c_dec_lon[i] = lon[i];

  deg_lon = atoi(c_deg_lon);
  min_lon = atoi(c_min_lon);
  dec_lon = atof(c_dec_lon);

  lcd_line_one();
  fprintf_P(&lcd_stream, PSTR("lon: %d %d %d"), deg_lon, min_lon, dec_lon);
April 03, 2012
by pcbolt
pcbolt's Avatar

Ach --- I goofed. Line 2 (right above this post) reads;

for (i = 3; i < 5; i++)c_min_lon[i] = lon[i];

This won't work. Use;

for (i = 3; i < 5; i++)c_min_lon[i - 3] = lon[i];

And while you're at it, change line 3 from;

for (i = 6; i < 12; i++)c_dec_lon[i] = lon[i];

To;

for (i = 6; i < 12; i++)c_dec_lon[i - 6] = lon[i];

Sorry about that.

The way it's setup now "dec_lon" won't have the decimal point in it, so you can use "atoi()". Now you can treat "dec_lon" as "micro-minutes" and not have to worry about floating math til the end of the calcs.

April 03, 2012
by haaser
haaser's Avatar

I had an idea and I think I got it to work. Instead of doing the for loops in the main program I just went to the interupt code and added the following. I then just modifed the existing code to reflect the changes. Everything seems to be working so far.

if ((nmea_state>15) & (nmea_state<18)) c_deg_lat[nmea_state-16]=received_from_uart;
if ((nmea_state>17) & (nmea_state<20)) c_min_lat[nmea_state-18]=received_from_uart;
if ((nmea_state>20) & (nmea_state<27)) c_dec_lat[nmea_state-21]=received_from_uart;

if ((nmea_state>29) & (nmea_state<33)) c_deg_lon[nmea_state-30]=received_from_uart;
if ((nmea_state>32) & (nmea_state<35)) c_min_lon[nmea_state-33]=received_from_uart;
if ((nmea_state>35) & (nmea_state<42)) c_dec_lon[nmea_state-36]=received_from_uart;

Now I just need to figure out the math for the distance calculation. I am trying to figure out what you said in a previous post but I am somewhat lost. I am not sure but how many digits can I use with uint32_t? I was thinking that I just need to use the c_min_lon + c_dec_lon Which would give me 8 digits but I am not sure if that is going to be too large?

April 03, 2012
by pcbolt
pcbolt's Avatar

Good work.

Basically you need three values to compute distance; delta_lat, delta_lon and average_lat. You can compute the deltas just by subtracting 2 dec_lon values (after the atoi() conversion). In most cases this would be exactly the same as if you used min_lon + dec_lon. The only time it doesn't work is when dec_lon "rollsover" from 999999 to 000000. You would have the same problem when min_lon rollsover from 59 to 0.

One way to fix this is to check the delta_lon to see if it is larger than say, 900000 and if it is just subtract it from 1000000. One thing to be aware of is that delta_lon can be a positive or negative value. Here's some code to try;

int32_t delta_lon;      // use a signed integer

delta_lon = start_lon - test_lon;
if (delta_lon < 0)
    delta_lon *= -1;    // use absolute value
if (delta_lon > 900000)
    delta_lon = 1000000 - delta_lon;    // takes care of rollover

You can do the same for delta_lat. Keep in mind we're working in units of "micro_minutes" so the meters/minute conversion value of 1,852 needs to be 0.001852.

For "average_lat" you can just use the latitude of the starting co-ordinates (degree + minutes/60). Now you're getting into using floating point numbers. For the longitude scale factor something like this will work;

double lon_scale, ns_dist, ew_dist, final_dist;
double lat_scale = 0.001852;

lon_scale = (cos((deg_lat + ((double)min_lat)/60.0)) * 0.0174533)) * lat_scale;

ns_dist = delta_lat * lat_scale;
ew_dist = delta_lon * lon_scale; 
final_dist = sqrt((ew_dist * ew_dist) + (ns_dist * ns_dist));

A quick note - the value 0.0174533 is used to convert degrees to radians.

April 04, 2012
by Ralphxyz
Ralphxyz's Avatar

Wow RTK is cool!

Now eventually I am going to have a roaming robot that might cover a 1 sq mile area. How involved do I have to make the math to figure out distances. I'll be using a old GPS device so I am limited in "accuracy" to start.

I do not specifically care about straight line distance though that would be nice but what I want specifically to do is to record way points with the distance between them and cumulative distance. I probable could get the cumulative distance from wheel rotation but I might be riding on an air curtain instead of wheels.

So what would the math look like? Would I have to worry about the Great Circle for Longitude at such relative short distances?

Ralph

April 04, 2012
by haaser
haaser's Avatar

Yeah the RTK is pretty neat. We are using a VRS RTK network with GLONASS Satellites. Which will provide us with sub inch horizontal accuracy and 1 - 2 inch vertical accuracy. Once it is finished and I get a chance I will take some video of the project for you all to see if you want.

If you are going to be purchasing a GPS receiver make sure that you find one that will read the WAAS correction signal. The WAAS signal is a free signal as well. You should be able to get 6-8 inch accuracy.

I am just doing some testing with the math that pcbolt provided. Might have some more questions but hopefully I can get this figured out today. Math and I do not get along. Once I get it working I will post all the code I have.

April 04, 2012
by pcbolt
pcbolt's Avatar

Ralph -

If you look at the NMEA data string from haaser's first post, after the longitude string there is a 'W' indicating West. The next two fields are speed (in knots) and heading (measured in degrees from north - clockwise). Since the first numeric field is time, you will be able to multiply elapsed time with speed to get distance. Keep in mind when you are sitting still the speed field still indicates small values (these get bigger using WAAS), so you might have to filter them out. Also, if your robot drives around in circles, you will accumulate large distance values but the robot is still in the same place.

April 06, 2012
by haaser
haaser's Avatar

Ok so I am going to need some help again. Like I said before me and math do not get along. I have put it back to where I started and I am hoping that you can help me with this calculation. I am not sure how to calculate lon_scale? I am also assuming that start_lon and start_lat should be my start lon and lat cordinates but only 8 digits longs? Also assuming that test_lon and test_lat will be always changing as the vehicle moves and the cordinates are changing which will calculate our distance?

    while(1){
      deg_lon = atoi(c_deg_lon);
      min_lon = atoi(c_min_lon);
      dec_lon = atof(c_dec_lon);

      deg_lat = atoi(c_deg_lat);
      min_lat = atoi(c_min_lat);
      dec_lat = atof(c_dec_lat);

      // Conversion
      delta_lon = start_lon - test_lon;
      if (delta_lon < 0)
        delta_lon *= -1;    // use absolute value
      if (delta_lon > 900000)
        delta_lon = 1000000 - delta_lon;    // takes care of rollover

      delta_lat = start_lat - test_lat;
      if (delta_lat < 0)
        delta_lat *= -1;    // use absolute value
      if (delta_lat > 900000)
        delta_lat = 1000000 - delta_lat;    // takes care of rollover

      double lon_scale, ns_dist, ew_dist, final_dist;
      double lat_scale = 0.001852;

      lon_scale = (cos((deg_lat + ((double)min_lat)/60.0) * 0.0174533) * lat_scale);
      lat_scale = (cos((deg_lon + ((double)min_lon)/60.0) * 0.0174533) * lon_scale);

      ns_dist = delta_lat * lat_scale;
      ew_dist = delta_lon * lon_scale; 
      final_dist = sqrt((ew_dist * ew_dist) + (ns_dist * ns_dist));

      lcd_line_one();
      fprintf_P(&lcd_stream, PSTR("Final Dist: %g"), final_dist);
      lcd_line_two();
      fprintf_P(&lcd_stream, PSTR(" GMT: %c%c:%c%c:%c%c"), time[0],time[1],time[2],time[3],time[4],time[5]);
      lcd_line_three();
      fprintf_P(&lcd_stream, PSTR(" lat: %c%c%c%c.%c%c%c%c%c%c"), c_deg_lat[0],c_deg_lat[1],c_min_lat[0],c_min_lat[1],c_dec_lat[0],c_dec_lat[1],c_dec_lat[2],c_dec_lat[3],c_dec_lat[4],c_dec_lat[5],c_dec_lat[6]);
      lcd_line_four();
      fprintf_P(&lcd_stream, PSTR("long: %c%c%c%c%c.%c%c%c%c%c%c"), c_deg_lon[0],c_deg_lon[1],c_deg_lon[2],c_min_lon[0],c_min_lon[1],c_dec_lon[0],c_dec_lon[1],c_dec_lon[2],c_dec_lon[3],c_dec_lon[4],c_dec_lon[5],c_dec_lon[6]);
  }
April 06, 2012
by pcbolt
pcbolt's Avatar

haaser -

It think it would work best if you used integers for "deg_lon", "min_lon" AND "dec_lon". So lines 4 and 8 should use "atoi()" to convert to integers. Integers are more precise since they extend to 10 digits. The problem with them is when you use division and higher math functions, so we can do all work with integers and only when needed convert to floating point numbers.

The variables of "start_lon" and "start_lat" are the position you are computing a distance from. These numbers would be better labelled as "start_dec_lon" and "start_dec_lat".

You don't need to calculate a "lat_scale"...that is a constant. A 1-minute change in latitude is 1852 meters no matter where you are. A 1-minute change in longitude is 1852 meters ONLY at the equator. As you start moving toward the poles, 1-minute change in longitude gets smaller and smaller....that's why it need to be computed for your particular location. Let me work on your code just a little...because you're getting real close to having it work.

April 06, 2012
by pcbolt
pcbolt's Avatar

haaser -

This might come close to the final code:

// uint32_t start_dec_lon = 10500, start_dec_lat = 671500;   // TEST = 3.9m

uint32_t start_dec_lon = 0, start_dec_lat = 0;   // ADDED
uint32_t delta_lon, delta_lat;                   // ADDED
double lon_scale, ns_dist, ew_dist, final_dist;  // MOVED
double lat_scale = 0.001852;                     // MOVED

while(1){
    deg_lon = atoi(c_deg_lon);
    min_lon = atoi(c_min_lon);
    dec_lon = atoi(c_dec_lon);           // EDITED

    deg_lat = atoi(c_deg_lat);
    min_lat = atoi(c_min_lat);
    dec_lat = atoi(c_dec_lat);           // EDITED

    if (start_dec_lat == 0){             // ADDED
        start_dec_lat = dec_lat);        // ADDED
        start_dec_lon = dec_lon);        // ADDED
    }                                    // ADDED

    // Conversion
    delta_lon = start_dec_lon - dec_lon;  // EDITED
    if (delta_lon < 0)
      delta_lon *= -1;                    // use absolute value
    if (delta_lon > 900000)
      delta_lon = 1000000 - delta_lon;    // takes care of rollover

    delta_lat = start_dec_lat - dec_lat;  // EDITED
    if (delta_lat < 0)
      delta_lat *= -1;    // use absolute value
    if (delta_lat > 900000)
      delta_lat = 1000000 - delta_lat;    // takes care of rollover

// LINE MOVED 
// LINE MOVED

    lon_scale = (cos((deg_lat + ((double)min_lat)/60.0) * 0.0174533) * lat_scale);
// LINE REMOVED

    ns_dist = delta_lat * lat_scale;
    ew_dist = delta_lon * lon_scale;
    final_dist = sqrt((ew_dist * ew_dist) + (ns_dist * ns_dist));

    if (final_dist > 7.0){              // ADDED - Trigger event here
       start_dec_lat = dec_lat);        // ADDED
       start_dec_lon = dec_lon);        // ADDED
    }                                   // ADDED

    lcd_line_one();
    fprintf_P(&lcd_stream, PSTR("Final Dist: %.2f"), final_dist);    // EDITED
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR(" GMT: %c%c:%c%c:%c%c"), time[0],time[1],time[2],time[3],time[4],time[5]);
    lcd_line_three();
    fprintf_P(&lcd_stream, PSTR(" lat: %c%c%c%c.%c%c%c%c%c%c"), c_deg_lat[0],c_deg_lat[1],c_min_lat[0],c_min_lat[1],c_dec_lat[0],c_dec_lat[1],c_dec_lat[2],c_dec_lat[3],c_dec_lat[4],c_dec_lat[5],c_dec_lat[6]);
    lcd_line_four();
    fprintf_P(&lcd_stream, PSTR("long: %c%c%c%c%c.%c%c%c%c%c%c"), c_deg_lon[0],c_deg_lon[1],c_deg_lon[2],c_min_lon[0],c_min_lon[1],c_dec_lon[0],c_dec_lon[1],c_dec_lon[2],c_dec_lon[3],c_dec_lon[4],c_dec_lon[5],c_dec_lon[6]);
}

You "un-comment" line 1 and "comment" line 3 for testing purposes so you don't have to hook up the GPS unit.

The final code should have line 3 as is.

April 06, 2012
by pcbolt
pcbolt's Avatar

One correction ...

Line 39 should read;

lon_scale = lat_scale * cos((deg_lat + (((double) min_lat)/60.0)) * 0.0174533);
April 07, 2012
by haaser
haaser's Avatar

Ok so I having been trying to get it to work but I not sure if it is working correctly. I had to modify the atoi() stuff again. What I did was just shink the c_dec_lon and c_dec_lat to only read 4 digits from the decimal point. It seems that it can not convert anything greater then 4 digits. If it is greater then four digits it returns a number that is not the same. Also when I put in your test values it gives me 00.00.77.

// gps.c
// for ATmega328p nano
// by E.Soderberg

#define F_CPU 14745600

#include "../libnerdkits/io_328p.h"
#include <avr/io.h>
#include <stdlib.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"
#include <math.h>

volatile char received_from_uart, time[10], v[10], lat[12], lon[12];
volatile uint16_t nmea_state = 0;
char c_deg_lon[4],c_min_lon[3],c_dec_lon[7], c_deg_lat[4],c_min_lat[3],c_dec_lat[7];

void uartgps_init(uint8_t br) {
 // set baud  UBRR0L = br;  //103 9.6kbps and 16Mhz see page 203 in datasheet//25 for 38.4kbs//207 4.8//16 57.6//8        115.2kbps

 // enable uart RX and interrupt on RX
 UCSR0B = (1<<RXEN0) | (1<<RXCIE0) | (1<<TXEN0);

  // set 8N1 frame format
  UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
}

ISR(USART_RX_vect){  // will be triggered on every character received //  gps configured for RMS only
received_from_uart = UDR0; // Fetch the received byte value into the variable "ByteReceived"
UDR0=received_from_uart; // Echo back the received byte back to the computer

nmea_state++;

if (received_from_uart == '$')nmea_state=0;
if ((nmea_state>6) & (nmea_state<13)) time[nmea_state-7]=received_from_uart;

if ((nmea_state>15) & (nmea_state<18)) c_deg_lat[nmea_state-16]=received_from_uart;
if ((nmea_state>17) & (nmea_state<20)) c_min_lat[nmea_state-18]=received_from_uart;
if ((nmea_state>20) & (nmea_state<25)) c_dec_lat[nmea_state-21]=received_from_uart;

if ((nmea_state>29) & (nmea_state<33)) c_deg_lon[nmea_state-30]=received_from_uart;
if ((nmea_state>32) & (nmea_state<35)) c_min_lon[nmea_state-33]=received_from_uart;
if ((nmea_state>35) & (nmea_state<40)) c_dec_lon[nmea_state-36]=received_from_uart;
}

int main(void) {
uint32_t i, deg_lon, min_lon, dec_lon, deg_lat, min_lat, dec_lat;
uint32_t start_dec_lon = 10500, start_dec_lat = 671500;   // TEST = 3.9m
//uint32_t start_dec_lon = 0, start_dec_lat = 0;
uint32_t delta_lon, delta_lat;
double lon_scale, ns_dist, ew_dist, final_dist;
double lat_scale = 0.001852;

// init uart
uartgps_init(7);

//fire up lcd
lcd_init();
FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
lcd_home();

 // turn on interrupt handler
 sei();

  // initally Trip the seeding bowl

  // main loop
  while(1){
    deg_lon = atoi(c_deg_lon);
    min_lon = atoi(c_min_lon);
    dec_lon = atoi(c_dec_lon);

    deg_lat = atoi(c_deg_lat);
    min_lat = atoi(c_min_lat);
    dec_lat = atoi(c_dec_lat);

    if (start_dec_lat == 0){
        start_dec_lat = dec_lat;
        start_dec_lon = dec_lon;
    }

    // Conversion
    delta_lon = start_dec_lon - dec_lon;
    if (delta_lon < 0)
      delta_lon *= -1;
    if (delta_lon > 900000)
      delta_lon = 1000000 - delta_lon;

    delta_lat = start_dec_lat - dec_lat;
    if (delta_lat < 0)
      delta_lat *= -1;
    if (delta_lat > 900000)
      delta_lat = 1000000 - delta_lat;

    lon_scale = lat_scale * cos((deg_lat + (((double) min_lat)/60.0)) * 0.0174533);

    ns_dist = delta_lat * lat_scale;
    ew_dist = delta_lon * lon_scale;
    final_dist = sqrt((ew_dist * ew_dist) + (ns_dist * ns_dist));

    if (final_dist > 7.0){     // Trigger Function
       start_dec_lat = dec_lat;
       start_dec_lon = dec_lon;
    }

    lcd_line_one();
    fprintf_P(&lcd_stream, PSTR("Dist: %.2f"), final_dist);
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR(" GMT: %c%c:%c%c:%c%c"), time[0],time[1],time[2],time[3],time[4],time[5]);
    lcd_line_three();
    fprintf_P(&lcd_stream, PSTR(" lat: %c%c%c%c.%c%c%c%c%c%c"), c_deg_lat[0],c_deg_lat[1],c_min_lat[0],c_min_lat[1],c_dec_lat[0],c_dec_lat[1],c_dec_lat[2],c_dec_lat[3],c_dec_lat[4],c_dec_lat[5],c_dec_lat[6]);
    lcd_line_four();
    fprintf_P(&lcd_stream, PSTR("long: %c%c%c%c%c.%c%c%c%c%c%c"), c_deg_lon[0],c_deg_lon[1],c_deg_lon[2],c_min_lon[0],c_min_lon[1],c_dec_lon[0],c_dec_lon[1],c_dec_lon[2],c_dec_lon[3],c_dec_lon[4],c_dec_lon[5],c_dec_lon[6]);
  }
 return 0;
}
April 07, 2012
by pcbolt
pcbolt's Avatar

haaser -

I think I found part of the solution. Try using "atol()" instead of "atoi()". (That's the letter 'l' not number one). If you are testing the value you get use something like this:

 fprintf_P(&lcd_stream, PSTR("Dec Lon: %ld"), dec_lon);

Remember to change it back to read in 6 digits and not 4. It interesting to see how there are minor differences when you use the AVR compiler as opposed to the one used for PC's.

April 07, 2012
by haaser
haaser's Avatar

I made some changes to the code tonight and set the uint32_t to long. It seemed to fix the problem but created a different problem. I took a video of it for you so you can see it in action. The problem is that the distance is calculating very fast. It seemed we moved 7 feet and it counted 7 meters. It is currently calculating with 8 decimal points.

Video Link

long i, deg_lon, min_lon, dec_lon, deg_lat, min_lat, dec_lat; //uint32_t start_dec_lon = 10500, start_dec_lat = 671500; // TEST = 3.9m long start_dec_lon = 0, start_dec_lat = 0; long delta_lon, delta_lat; double lon_scale = 0.0, ns_dist = 0.0, ew_dist = 0.0, final_dist = 0.0; double lat_scale = 0.001852;

April 07, 2012
by pcbolt
pcbolt's Avatar

Looks pretty good. Assigning to "long" I think is the same as int32_t (signed number) so that could be the solution. I got good results using "atol()" (ascii to long) and wrong results using "atoi()" (ascii to int).

Curious if the test values came up with 3.9m

April 07, 2012
by haaser
haaser's Avatar

I changed the values to long and atol(). I had to disable setting final_dist to 0 as the test values came up with 1243.77

April 07, 2012
by pcbolt
pcbolt's Avatar

And still using a test string of 5112.671694 and 10225.010369?

April 07, 2012
by pcbolt
pcbolt's Avatar

Did you change lines 43 and 47 back to read in 6 digits? If not, that distance result is in the right ballpark.

April 08, 2012
by haaser
haaser's Avatar

I am still using

long start_dec_lon = 10500, start_dec_lat = 671500; // TEST = 3.9m

and yes I adjusted lines 42 and 47 to read in more digits.

I also assumed that the final_dist was going to be in meters? Do we need to convert the number to from something to meters? Maybe I am just not understanding.

April 08, 2012
by pcbolt
pcbolt's Avatar

haaser -

The answer should be in meters.

Hmmm...a little puzzled here. Looks like you'll have to go into debug mode. I would declare a variable on maybe line 21 (off by itself);

volatile long debug;

Then on line 94 (after the 2 "if" statements) add;

debug = delta_lon;

Make a copy of this line and paste it right after;

fprintf_P(&lcd_stream, PSTR(" GMT: %c%c:%c%c:%c%c"), time[0],time[1],time[2],time[3],time[4],time[5]);

Alter the second line to read;

fprintf_P(&lcd_stream, PSTR("Delta Lon: %ld"), debug);

If it looks right...start moving down through the code to assign "debug" something else (delta_lat, ns_dist, etc). Just be careful when assigning debug that you change it to "double" on line 21 if you want to assign it to a floating number (also change the print statement to use %.2f)

April 08, 2012
by haaser
haaser's Avatar

Ok a couple of things.

  1. I found a nmea simulator so that you can send nmea strings to the nerd kit. Link but it outputs GGA instead of RMC.

    if ((nmea_state>=7) & (nmea_state<=12)) time[nmea_state-7]=received_from_uart;

    if ((nmea_state>=17) & (nmea_state<=18)) c_deg_lat[nmea_state-17]=received_from_uart; if ((nmea_state>=19) & (nmea_state<=20)) c_min_lat[nmea_state-19]=received_from_uart; if ((nmea_state>=22) & (nmea_state<=27)) c_dec_lat[nmea_state-22]=received_from_uart;

    if ((nmea_state>=34) & (nmea_state<=35)) c_deg_lon[nmea_state-34]=received_from_uart; if ((nmea_state>=36) & (nmea_state<=37)) c_min_lon[nmea_state-36]=received_from_uart; if ((nmea_state>=39) & (nmea_state<=44)) c_dec_lon[nmea_state-39]=received_from_uart;

  2. I used your debug code and check all the variables. two of them stands out and it was the variable lon_scale and lat_scale. lat_scale is coming up as a negative number -166891 and lon_scale will not change from 0;

April 09, 2012
by pcbolt
pcbolt's Avatar

haaser -

I had an old Comm program I modified to print 1 RMC string so I was able to test your code. I changed all the atoi() to atol() (lower case "L"). And also changed line 55 to:

int32_t delta_lon, delta_lat;

This is critical because the deltas can't be unsigned. I found out too the test values on line 53 will give an answer of 0.39 meters.

Everything else went fine, but I want to update my Comm program to increment co-ordinates at 1 second intervals. I'll let you know how it goes.

Oh and for some reason the "if" statements (lines 38 to 48) use a single '&' when it should be '&&'. It still worked I just don;t know why it did :-)

April 10, 2012
by haaser
haaser's Avatar

I made the changes you suggested and from my inital tests it looks like it is working perfectly! We need to do some wiring on the seeding tool with relays and such but I believe it is going to work. I will keep you updated and let you know how it goes. I will keep you updated when we get to test it in the field.

April 10, 2012
by pcbolt
pcbolt's Avatar

Great!

I added a little bulletproofing to the programming so it would only update after a full message was read in and then tested an input speed of 10 Hz (10 RMC strings/sec)...worked like a champ. If your system starts to act up at these speeds, let me know. The extra code is only about 6 lines.

Looking forward to the results of the field test. Also curious how accurate it is.

April 10, 2012
by haaser
haaser's Avatar

I actually tried it at 10hz and it lagged the screen about ever 4 seconds. Let me know what code you wrote and I can test it again to see if it helps.

April 10, 2012
by pcbolt
pcbolt's Avatar

OK. I'm know the code you have now has changed, but just to make it easy, I'll reference line numbers on your last "complete" code listing (about 14 posts ago). First just declare a global flag variable somewhere between lines 17 and 21;

volatile uint8_t ok_to_update;

Then assign it to 1 at the end of the interrupt code (right after line 48 but before the closing brace);

if (nmea_state==44) ok_to_update = 1;

When the program starts the flag should get set to 0 (I used line 58);

ok_to_update = 0;

Then the first line in the while loop (line 74) should test it and if it is 0, continue to the start of the while loop without executing anything else. If it is 1, it will update the LCD;

if (ok_to_update == 0) continue;      // If 0 re-start while loop

Once the LCD does get updated, you need to reset the flag back to 0 (last line in while loop after line 119);

ok_to_update = 0;
April 13, 2012
by haaser
haaser's Avatar

Ok so I have another problem. The GPS code itself is working perfectly. Thank you for all your help with that. Our next hurdle is to control a 5 volt relay. I am using this to turn on pin 4 PORTC |= (1<<PC4); and this to turn off pin 4 PORTC &= ~(1<<PC4);. Now my problem is that I only get 1.2 volts out of the output pin on the mcu which is not enough to drive the relay. So I was trying to hook up a 2n 7000 n channel to try to hookup a external 5 volt source and have the MCU control the gate pin on the 2n7000 to turn the voltage on and off but I can not get it to work. I do not know if I am doing this correctly but I am open to suggestions on how to make this work. All I need to do is turn a 5 volt relay on and off with the MCU now and I will be one step closer to being finished.

April 13, 2012
by pcbolt
pcbolt's Avatar

Is PC4 enabled as an output pin (i.e. DDRC |= (1<<PC4);)?

April 13, 2012
by haaser
haaser's Avatar

Yes

//Make pc4 an output DDRC |= (1<<PC4);

April 14, 2012
by pcbolt
pcbolt's Avatar

Hmmm -

I guess it's back to the standard old steps...

  • Check power supply
  • Check wiring
  • Check shorts
  • Is Voltage regulator getting too hot
  • Do the other pins do the same thing

Man...just when you're getting so close too :-)

April 14, 2012
by haaser
haaser's Avatar

Ok so I found out that I destroyed the mosfet as I installed another one and it is working as expected now, but... lol there is always a but when I need proper direction on how to hook up a 9 volt source via the mosfet so that it will still work and I do not destroy any the mosfet. I am trying to go by this but I just do not understand it. I wish you could have a picture of the breadboard beside the drawing in CircuitLab as some of us are still learning and it would be nice to compare the two side by side. https://www.circuitlab.com/circuit/vt9kbm/2n7000-driving-an-led/

What I tried to do was hook up a 9 volt battery in series to give it the required power to run energize the relays but I am concerned that was what wrecked the mosfet's. I had 4 mosfet's and I destroyed 2 of them so far.

picture

April 14, 2012
by Rick_S
Rick_S's Avatar

Are you placing flyback diodes across the coil terminals of the relay? If not, the reverse surge you get when turning it off can damage your drivers. See the Motors tutorial to see how to connect them. The tutorial talks of small electric motors but the same principle applies to relays.

Also, if you have proper power to your micro, and a pin is set for output, assuming the micro isn't damaged, it should source VCC when turned on. If you are running it at 5V it should put out 5V. If not you have a problem somewhere and need to diagnose like pcbolt said.

Rick

April 14, 2012
by Rick_S
Rick_S's Avatar

One other thing, if you want more power for your project, hook the batteries up in parallel not series. Parallel connections increases your current (amperage). Series connections increase your voltage. In series, the current will remain that of the weaker battery.

April 14, 2012
by haaser
haaser's Avatar

Considering that I do not have any diodes currently other then LED lights I will assume that I can use a LED as a fly back diode? Can I put a resistor in line with the LED so that I do not blow the led up with 5 volts or do I need to order a diode?

I will do some checking here with a volt meter but assume that I can not get 5 volts out of PC4 will I be able to get 5 volts out of a different output pin or is the entire chip wrecked?

April 14, 2012
by haaser
haaser's Avatar

Ok I checked PC4 and PC0 and they are both putting out the same voltage 5.06 volts. So I am going to assume that the MCU is not damaged.

I am using the USB cable for my 5 volt source.

April 14, 2012
by haaser
haaser's Avatar

Ok so I removed the mosfet, led, and with the tested voltage at 5.06 volts it should trigger the relay but it does not. I hooked the relay up to the mcu PC4 and directly to negative but all I get is 2.15 volts across the pins on the relay. Does this mean that the MCU is not working?

April 14, 2012
by haaser
haaser's Avatar

I know this is probably wrong and going to make everyone that reads this shake their head, but anyway lol.

I turned on pc0, pc1 and wired it to the relay and it triggered the relay like it is suppose to. Could I just not be getting enough power out of one pin to trigger this relay?

April 14, 2012
by haaser
haaser's Avatar

Also forgot to mention that I am only getting 3.15 volts across the relay pins with two output pins connected.

April 14, 2012
by esoderberg
esoderberg's Avatar

Haaser,

It sounds like you might be trying to pull more current than the MCU pin can put out. Just because the MCU can put out sufficient voltage for your relay, it may not be able to maintain that voltage with the current draw you're demanding to switch the relay. It sounds like that might be the case because with one pin you were getting just over 2v and when added the second pin you increase that to just over 3v. I recommend you go back to controlling the relay via a 2n7000. If you post some info on the relay we might be able to help with that circuit.

Eric

April 14, 2012
by haaser
haaser's Avatar

I am open to using a 5 volt relay or a 12 volt relay. The only reason I was using a 5 volt relay as I assumed it would work with the 5 volt output but apprently I was wrong. If we are going to use the 2n7000 mosfet work we could use a 12 volt relay or 5 volt relay. I would have to run to back to work to get some 12 volt relays but I have a 5 volt relay with me at home.

Tell me If I am wrong here. From what I have been reading and watching on youtube. With the 2n7000 I should be able to hookup a 12volt or 5 volt source to the source and drain on the mosfet. When the gate is trigger via the MCU with 5 volts is should engage the source and drain in turn switching my relay?

Relay http://octopart.com/g8p-1c2t-f-dc12-omron-27129

April 14, 2012
by esoderberg
esoderberg's Avatar

Haaser,

You should be able to use the 2n7000 to control the relay shown in the link: hookup the positive input of the relay to your 12v source, send the output to the drain of the MOSFET and connect the MOSFET source to ground. This should put 77mA through the MOSFET which is within the 2n7000's rated capacity. Control the 2n7000 gate direct from the mcu.

April 14, 2012
by haaser
haaser's Avatar

Where should I be putting the fly back diode? I went to the source and picked some up today. I am going to assume that it should go between the relay points but how do I know which way to put it? Also could I use a LED here instead of a diode?

April 14, 2012
by esoderberg
esoderberg's Avatar

The flyback should go from the negative side of the coil back to the 12v supply voltage; it will block current from the 12V but allow for any induced voltage(which will build up when you first shut off your MOSFET) above 12v to go back to the supply.

April 15, 2012
by haaser
haaser's Avatar

Does this relay have a fly back diode in it? This relay is a Tyco v23234-a0001-x042

Relay

April 15, 2012
by haaser
haaser's Avatar

Ok so I am having some problems again. I can make it work with the 5 volt relay but I can not make it work with the 12 volt relay. I was hoping that we could make it work with the 12 volt relay as the tractor has a 12 volt system. How can I tell if the 12 volt relay is going to work with the 2n7000? The relay I am using is the same relay as in the picture above.

April 15, 2012
by esoderberg
esoderberg's Avatar

The coil resistance is 75 Ohm so it should pull about 160 mA with 12v on the coil, which is within the tolerance of the 2n7000.
2n7000datasheet

Try putting 12v across the coil(terminals 85/86) manually and check if the relay switches to make sure it is working as expected. Once this is checked doing the same with the 2n7000 should be straight forward.

April 15, 2012
by haaser
haaser's Avatar

I cooked my last 2n7000 trying to make it work. I purchased a box of random mosfets from the source but I am not sure what I am looking for or what do I need to do to see what will support a 12 volt system? Is there a math equation or something how I can figure this out.

April 17, 2012
by haaser
haaser's Avatar

Ok so I found a mosfet that works and that problem is fixed. Had a weird problem today with it today. I have been doing all my testing via laptop power and having the 5 volts from the laptop plugged directly into the power rail. I took the breadboard and wiring to the tractor and hooked it up via the voltage regulator but it would not trigger the relays. I brought my laptop back out and powered it via the power rails and the relays triggered. Does the voltage regulator not put out enough power to trigger two 5 volt relays? Put an amp meter on it and they are only drawing .1 amps.

Post a Reply

Please log in to post a reply.

Did you know that microcontrollers have two different kinds of memory, program space (flash) and SRAM? Learn more...