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.

Microcontroller Programming » Tempsensor w/scrolling message - kind of works

December 30, 2010
by SpaceGhost
SpaceGhost's Avatar

Hello, total n00b (I'm not apologizing for it though!!) here when it comes to this C programming stuff, but been playing with electronics for years.

I just got my Nerdkit Dec.27, & today's been the day I've really got to play with it beyond downloading the initialload program (that I did first day I got it), and writing the program for tempsensor_edu. Both have worked out fine, after some initial confusion of course.

I modified the tempsensor_edu program to display a third line on the LCD to read "Have a Nice Day!!" pretty easily. Today I've been playing around with trying to get my "Have a Nice Day!!" message to scroll, with some limited success:

The ADC line, temperature line, and my scrolling message all display on the LCD... And my message scrolls very nicely. I acheived this by putting some code for scrolling text (posted by mrobbins) that I found in another thread into my tempsensor_edu program. After some initial trial and error it all seemed to work - very cool!

But then I noticed that the number in the ADC line wasn't flickering, nor was the .00 part of the temperature reading fluctuating anymore... Touching the sensor didn't cause the reading(s) to go up either, as it had prior to my adding the scrolling text...

HOWEVER, the temperature reading displayed was pretty close to what it had been reading before. I pulled the + battery wire, then put it back in - the temperature reading had changed slightly!

Then, I pulled the power wire again, put my finger on the sensor, then plugged the wire back in - the temperature displayed now had jumped by a few degrees!!

This kind of suggests to me that I may be on the right track... It works, but the MCU is longer updates sensor information... Like there's no "refresh" rate going on, or something (?).

Here is the code:

// tempsensor.c
// for NerdKits with ATmega168
// mrobbins@mit.edu

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
//
// PC0 -- temperature sensor analog input

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  return result;
}

double sampleToFahrenheit(uint16_t sample) {
  // conversion ratio in DEGREES/STEP:
  // (5000 mV / 1024 steps) * (1 degree / 10mV)
  //    ^^^^^^^^^^^      ^^^^^^^^^^
  //     from ADC         from LM34
  return sample * (5000.0 / 1024.0 / 10.0);  
}

int main() {
  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // start up the Analog to Digital Converter
  adc_init();

  // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // holder variables for temperature data
  uint16_t last_sample = 0;
  double this_temp;
  double temp_avg;
  uint8_t i;

  while(1) {
    // take 100 samples and average them!
    temp_avg = 0.0;
    for(i=0; i<100; i++) {
      last_sample = adc_read();
      this_temp = sampleToFahrenheit(last_sample);

      // add this contribution to the average
      temp_avg = temp_avg + this_temp/100.0;
    }

    // write message to LCD
    lcd_home();
    lcd_write_string(PSTR("ADC: "));
    lcd_write_int16(last_sample);
    lcd_write_string(PSTR(" of 1024   "));
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("Temperature: %.2f"), temp_avg);
    lcd_write_data(0xdf);
    lcd_write_string(PSTR("F      "));
    lcd_line_three();
      // Variable to hold our current scroll offset.
int8_t scrollPos=0;

// Variable to iterate through the 20-wide LCD screen.
uint8_t i;

// This is our actual string, longer than the LCD width,
// stored in program memory (flash).
const char *linethree = PSTR(" Have a Nice Day!!       ");

// This calculates its width once.
// (For the string above, linetwolen = 37)
uint8_t linethreelen = strlen_P(linethree);

// Main loop.
while(1) {
  // Move to the 2nd line of the LCD.
  lcd_line_three();

  for(i=0; i<20; i++) {
    // For each of the 20 columns of the LCD display,
    // we want to write a particular character from the
    // string to be scrolled.
    // Specifically, for column i (0..19), we want to write
    // the (scrollPos+i) character of the string as we shift it around.
    // But we take (scrollPos+i) MOD linetwolen to allow us to
    // "wrap around".
    // Calculate the right character number ((scrollPos+i)%linetwolen)
    // and add this to the linetwo variable, essentially moving forward
    // in memory by that many bytes.
    // Then read it from program memory (pgm_read_byte).
    // Finally, write it out to the LCD (lcd_write_data).
    lcd_write_data(pgm_read_byte(linethree + ((scrollPos+i)%linethreelen)));
  }

  // Shift to the left by one position.  When we get to having
  // shifted by the entire length fo the string, then reset back to zero.
  scrollPos = (scrollPos + 1) % linethreelen;

  // Delay about 1/5th of a second before re-displaying.
  delay_ms(200);
}
    // write message to serial port
    printf_P(PSTR("%.2f degrees F\r\n"), temp_avg);
  }

  return 0;
}

I'm pretty excited so far by getting some of this stuff to work... I have a looong way to go though, to understanding it completely! (again- NO apologies!)

I appreciate any input that anyone might have.

Dave

December 30, 2010
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Dave,

Great job so far! You are definitely starting to put some good stuff together. I think the issue you are having is happening because you have two while(1) loops in your program. A while(1) loop is an infinite loop meant to run forever. You will most often find these loops around the main loop of a program which you would expect to just keep running. Your program starts off fine, and enters the first while loop on line 86 reads the temperature fine, but then it enters while loop that starts on line 122 and gets stuck on that one looping your text forever.

Since you have two different things are trying to accomplish 1) reading and displaying the temperature, and 2) scrolling text, you need to think a little about how these two ideas interact with each other. The easiest solution is to just remove your inner while loop, and have the text move over one spot every time the large while loop goes through. This means that as your main while loop goes through you do one temperature read, one update of the temperature line, and move the scrolling text one spot. The other solution is to read and update the temperature variable using an interrupt. Interrupts are able to interrupt your main loop and jump to another piece of code, and then jump back when that piece of code is over. Interrupts are often used in situations like the one you have where you want to update a display in your main thread, and read some sensors on the side. You can read more about interrupts in our PS/2 Keyboard tutorial.

Keep up the great work!

Humberto

December 31, 2010
by SpaceGhost
SpaceGhost's Avatar

Okay, here is what I've done so far:

I tried editing out the while(1) on line 86, and left the { there. The program ran as it did before, message scrolling, temperature and ADC values displayed but not updating.

I restored the code to the way it was on line 86, then edited out the while(1) on line 122 and left the { on that line. This time, the ADC and Temperature lines were updating themselves on the display, my "Have a Nice Day!!" message was present - but not scrolling.

I tried some other variations with lines 86 and 122, with results ranging from error messages when trying to load the code, to the program working either way described above.

The interrupt suggestion sounds interesting, I will take a look at the PS/2 keyboard tutorial.

I'm still pretty new to this - whatever it is that I'm doing wrong with lines 122 and/or 86 may be obvious to some folks, but I'm pretty much just stabbing around in the dark. Another pointer or two would be appreciated, if anyone has any they'd be willing to offer.

Dave

December 31, 2010
by SpaceGhost
SpaceGhost's Avatar

Well, I'm totally stumped now. The tutorial on the PS/2 keyboard just kind of confounded me even more. Perhaps I should try that project, I'm pretty sure I have an old PS/2 keyboard somewhere around the house, to try to get a better understanding of the interrupts. The interrupts seem kind of advanced for where I am now though.

I've been tinkering around with the original code I presented in the first post - I have it trimmed down now (maybe too much?). The ADC & Temperature are displayed and updated, but the text doesn't scroll:

// tempsensor.c
// for NerdKits with ATmega168
// mrobbins@mit.edu

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
//
// PC0 -- temperature sensor analog input

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  return result;
}

double sampleToFahrenheit(uint16_t sample) {
  // conversion ratio in DEGREES/STEP:
  // (5000 mV / 1024 steps) * (1 degree / 10mV)
  //    ^^^^^^^^^^^      ^^^^^^^^^^
  //     from ADC         from LM34
  return sample * (5000.0 / 1024.0 / 10.0); 
}

int main() {
  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // start up the Analog to Digital Converter
  adc_init();

  // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // holder variables for temperature data
  uint16_t last_sample = 0;
  double this_temp;
  double temp_avg;
  uint8_t i;

  while(1) {
    // take 100 samples and average them!
    temp_avg = 0.0;
    for(i=0; i<100; i++) {
      last_sample = adc_read();
      this_temp = sampleToFahrenheit(last_sample);

      // add this contribution to the average
      temp_avg = temp_avg + this_temp/100.0;
    }

    // write message to LCD
    lcd_home();
    lcd_write_string(PSTR("ADC: "));
    lcd_write_int16(last_sample);
    lcd_write_string(PSTR(" of 1024   "));
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("Temperature: %.2f"), temp_avg);
    lcd_write_data(0xdf);
    lcd_write_string(PSTR("F      "));
    lcd_line_three();
      // Variable to hold our current scroll offset.
int8_t scrollPos=0;

// Variable to iterate through the 20-wide LCD screen.
uint8_t i;

// This is our actual string, longer than the LCD width,
// stored in program memory (flash).
const char *linethree = PSTR(" Have a Nice Day!!       ");

// This calculates its width once.
// (For the string above, linetwolen = 37)
uint8_t linethreelen = strlen_P(linethree);

  { for(i=0; i<20; i++) {
    // For each of the 20 columns of the LCD display,
    // we want to write a particular character from the
    // string to be scrolled.
    // Specifically, for column i (0..19), we want to write
    // the (scrollPos+i) character of the string as we shift it around.
    // But we take (scrollPos+i) MOD linetwolen to allow us to
    // "wrap around".
    // Calculate the right character number ((scrollPos+i)%linetwolen)
    // and add this to the linetwo variable, essentially moving forward
    // in memory by that many bytes.
    // Then read it from program memory (pgm_read_byte).
    // Finally, write it out to the LCD (lcd_write_data).
    lcd_write_data(pgm_read_byte(linethree + ((scrollPos+i)%linethreelen)));
  }

  // Shift to the left by one position.  When we get to having
  // shifted by the entire length fo the string, then reset back to zero.
  scrollPos = (scrollPos + 1) % linethreelen;

  // Delay about 1/5th of a second before re-displaying.
  delay_ms(200);
}
    // write message to serial port
    printf_P(PSTR("%.2f degrees F\r\n"), temp_avg);
  }

  return 0;
}

So what am I missing?

I think that I could come up with a hardware solution to the problem and run the code that scrolls the text and displays stationary values for ADC and temperature (from my first post, with some slight modifications), and update the values with that every few minutes (I'm thinking a 555 timer circuit). But I really want to do it with the code and not just add other components.

I definitely plan on getting a book on C programming... Anyone have any suggestions for one that focuses on MCU programming for raw beginners?

Dave

December 31, 2010
by nanaeem
nanaeem's Avatar

Hi Dave,

I see the main() in your latest code only had one while(1) loop. Good. However, lets go through what the code in the while(1) does. First, the average temperature reading is computed (87-95). Second, some LCD output is generated (97-106). Now comes the code which I think you copied from another scrolling example. The mistake appears to be in LINE 108 which creates a new variable scrollPoss and sets it to 0.

Code 121 to 134 prints the 20 characters.

Line 139 increments scrollPoss by 1. The intent was that the next time the while(1) loop (the one you took out though leaving the braces{}) iterates, the scrollPass variable has been incremented by 1 and this will give the scroll effect. However, now think of whats happening in the current while(1) loop. WITHIN the loop line 108 sets scrollPos to 0, then we write the characters (121-134) then we increment by 1 (line 139). In the next iteration this happens again. NOTICE: scrollPos is set to 0 again... therefore NO SCROLLING.

I am guessing by this time you already have an idea how to fix it: I would suggest moving the code at 107-120 out of the while(1) loop. Although in this example the declaration of scrollPos within the while(1) loop is a logical error, in general it is always good to have only that code within a loop that needs to be recomputed, even if its not necessary. If you look through the code segment 107-120, nothing in the code segment needs to be repeated. Therefore place it above and outside the while(1) (sames computer cycles :) ).

This should fix the problem. Sorry if this post seems rather long but my intent was to explain rather than simply tell you whats wrong.

Enjoy.

January 01, 2011
by SpaceGhost
SpaceGhost's Avatar

Nope, I'm still not getting it. I moved lines 107 - 120 (from the previous code that I posted) out of the while(1) loop, and placed it all after

// holder variables for temperature data
  uint16_t last_sample = 0;
  double this_temp;
  double temp_avg;
  uint8_t i;

to modify the code. This put it all just before the while(1) loop. The code would not upload to the MCU. Here's the modified code:

// tempsensor.c
// for NerdKits with ATmega168
// mrobbins@mit.edu

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
//
// PC0 -- temperature sensor analog input

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  return result;
}

double sampleToFahrenheit(uint16_t sample) {
  // conversion ratio in DEGREES/STEP:
  // (5000 mV / 1024 steps) * (1 degree / 10mV)
  //    ^^^^^^^^^^^      ^^^^^^^^^^
  //     from ADC         from LM34
  return sample * (5000.0 / 1024.0 / 10.0);
}

int main() {
  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // start up the Analog to Digital Converter
  adc_init();

  // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // holder variables for temperature data
  uint16_t last_sample = 0;
  double this_temp;
  double temp_avg;
  uint8_t i;
// Variable to hold our current scroll offset.
int8_t scrollPos=0;

// Variable to iterate through the 20-wide LCD screen.
uint8_t i;

// This is our actual string, longer than the LCD width,
// stored in program memory (flash).
const char *linethree = PSTR(" Have a Nice Day!!       ");

// This calculates its width once.
// (For the string above, linetwolen = 37)
uint8_t linethreelen = strlen_P(linethree);

  while(1) {
    // take 100 samples and average them!
    temp_avg = 0.0;
    for(i=0; i<100; i++) {
      last_sample = adc_read();
      this_temp = sampleToFahrenheit(last_sample);

      // add this contribution to the average
      temp_avg = temp_avg + this_temp/100.0;
    }

    // write message to LCD
    lcd_home();
    lcd_write_string(PSTR("ADC: "));
    lcd_write_int16(last_sample);
    lcd_write_string(PSTR(" of 1024   "));
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("Temperature: %.2f"), temp_avg);
    lcd_write_data(0xdf);
    lcd_write_string(PSTR("F      "));
    lcd_line_three();

  { for(i=0; i<20; i++) {
    // For each of the 20 columns of the LCD display,
    // we want to write a particular character from the
    // string to be scrolled.
    // Specifically, for column i (0..19), we want to write
    // the (scrollPos+i) character of the string as we shift it around.
    // But we take (scrollPos+i) MOD linetwolen to allow us to
    // "wrap around".
    // Calculate the right character number ((scrollPos+i)%linetwolen)
    // and add this to the linetwo variable, essentially moving forward
    // in memory by that many bytes.
    // Then read it from program memory (pgm_read_byte).
    // Finally, write it out to the LCD (lcd_write_data).
    lcd_write_data(pgm_read_byte(linethree + ((scrollPos+i)%linethreelen)));
  }

  // Shift to the left by one position.  When we get to having
  // shifted by the entire length fo the string, then reset back to zero.
  scrollPos = (scrollPos + 1) % linethreelen;

  // Delay about 1/5th of a second before re-displaying.
  delay_ms(200);
}
    // write message to serial port
    printf_P(PSTR("%.2f degrees F\r\n"), temp_avg);
  }

  return 0;
}

When trying to run the makefile from the Command Prompt, I get the following errors listed after "tempsensor.c: In function 'main':"

tempsensor.c:89: error: redeclaration of 'i' with no linkage

tempsensor.c:89: error: previous declaration of 'i' was here

make: *** [tempsensor.hex] Error 1

I tried moving around, deleting, etc. various parts of the code with no luck. I'm pretty sure that it has to do with the way that I cut and pasted the code before the while(1) loop, and after the "holder variables for temperature data" -

uint16_t last_sample = 0;
double this_temp;
double temp_avg;
uint8_t i;

Something still missing, isn't it? How do I redeclare 'i' with the "linkage" that it needs?

I truly appreciate the help that's been offered so far.

Dave

January 01, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Dave,

What error are you getting. I think you just might have a complier error because of extra { } floating around your code.

Humberto

January 01, 2011
by SpaceGhost
SpaceGhost's Avatar

I'm getting these errors in the Command Prompt when I try to load the code onto the MCU:

tempsensor.c:89: error: redeclaration of 'i' with no linkage

tempsensor.c:89: error: previous declaration of 'i' was here

make: *** [tempsensor.hex] Error 1

January 01, 2011
by SpaceGhost
SpaceGhost's Avatar

Which {}'s should I get rid of, and why?

January 01, 2011
by SpaceGhost
SpaceGhost's Avatar

My errors (in the Command Prompt) actually read:

tempsensor.c:89: error: redeclaration of 'i' with no linkage

tempsensor.c:84: error: previous declaration of 'i' was here

make: *** [tempsensor.hex] Error 1

sorry about that, my bad.

Dave

January 01, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Dave,

There is an extra { on line 121 that you close again on line 135. Try removing those. I think they are at the root of this issue.

Humberto

January 01, 2011
by SpaceGhost
SpaceGhost's Avatar

Nope. Same thing happens, the Command Prompt lists the same errors. Revised code:

// tempsensor.c
// for NerdKits with ATmega168
// mrobbins@mit.edu

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
//
// PC0 -- temperature sensor analog input

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  return result;
}

double sampleToFahrenheit(uint16_t sample) {
  // conversion ratio in DEGREES/STEP:
  // (5000 mV / 1024 steps) * (1 degree / 10mV)
  //    ^^^^^^^^^^^      ^^^^^^^^^^
  //     from ADC         from LM34
  return sample * (5000.0 / 1024.0 / 10.0);
}

int main() {
  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // start up the Analog to Digital Converter
  adc_init();

  // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // holder variables for temperature data
  uint16_t last_sample = 0;
  double this_temp;
  double temp_avg;
  uint8_t i;
// Variable to hold our current scroll offset.
int8_t scrollPos=0;

// Variable to iterate through the 20-wide LCD screen.
uint8_t i;

// This is our actual string, longer than the LCD width,
// stored in program memory (flash).
const char *linethree = PSTR(" Have a Nice Day!!       ");

// This calculates its width once.
// (For the string above, linetwolen = 37)
uint8_t linethreelen = strlen_P(linethree);

  while(1) {
    // take 100 samples and average them!
    temp_avg = 0.0;
    for(i=0; i<100; i++) {
      last_sample = adc_read();
      this_temp = sampleToFahrenheit(last_sample);

      // add this contribution to the average
      temp_avg = temp_avg + this_temp/100.0;
    }

    // write message to LCD
    lcd_home();
    lcd_write_string(PSTR("ADC: "));
    lcd_write_int16(last_sample);
    lcd_write_string(PSTR(" of 1024   "));
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("Temperature: %.2f"), temp_avg);
    lcd_write_data(0xdf);
    lcd_write_string(PSTR("F      "));
    lcd_line_three();

    for(i=0; i<20; i++) {
    // For each of the 20 columns of the LCD display,
    // we want to write a particular character from the
    // string to be scrolled.
    // Specifically, for column i (0..19), we want to write
    // the (scrollPos+i) character of the string as we shift it around.
    // But we take (scrollPos+i) MOD linetwolen to allow us to
    // "wrap around".
    // Calculate the right character number ((scrollPos+i)%linetwolen)
    // and add this to the linetwo variable, essentially moving forward
    // in memory by that many bytes.
    // Then read it from program memory (pgm_read_byte).
    // Finally, write it out to the LCD (lcd_write_data).
    lcd_write_data(pgm_read_byte(linethree + ((scrollPos+i)%linethreelen)));

  // Shift to the left by one position.  When we get to having
  // shifted by the entire length fo the string, then reset back to zero.
  scrollPos = (scrollPos + 1) % linethreelen;

  // Delay about 1/5th of a second before re-displaying.
  delay_ms(200);
}
    // write message to serial port
    printf_P(PSTR("%.2f degrees F\r\n"), temp_avg);
  }

  return 0;
}

My errors in the Command Prompt still read:

tempsensor.c:89: error: redeclaration of 'i' with no linkage

tempsensor.c:84: error: previous declaration of 'i' was here

make: *** [tempsensor.hex] Error 1

Dave

January 01, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Dave,

I see the problem. You are declaring i twice. Once on line 84, and once on line 89. Simply remove one of them and the code should compile. I thought your extra {} were causing scoping problems that were causing the error, and I failed to look for the more likely error. Sorry I didn't catch that earlier.

Humberto

January 01, 2011
by SpaceGhost
SpaceGhost's Avatar

Ha Ha, wow! It's getting closer -

The ADC and Temperature values are being displayed and updated. The text is, um scrolling... But it's a garbled version of my "Have a Nice Day!!" message - and the garbled stuff seems to be scrolling in sections... Hmmm!

Okay, don't give it away to me, not just yet anyway. I can see that we're on to something here. Neat thing for me is that I'm finally getting a different result than what I've had over the last few days... Cool!

I think I might know where to look in the code to figure this out. May take me a day or so - my wife's getting kinda irritated that I've been glued to this thing, but she is glad that I'm having fun with my Christmas present!

Myself, I want to solve this problem and get a better understanding of how the whole thing works. I tried the code fix both ways - with only line 84 removed, and then tried it with only line 89 removed. The results appear to be identical. Is there a preferred line that should be removed (even if just by someone's opinion)? If so, which one and why? If it really doesn't matter, could someone explain that??

That's all I'm asking for now. I'll keep you guys updated on this project!!

Thanks for all the great help,

Dave

January 01, 2011
by SpaceGhost
SpaceGhost's Avatar

Quick update: I think I'm seeing a pattern to the nonsense being displayed on the 3rd line of my LCD... Hmmmm!

January 02, 2011
by SpaceGhost
SpaceGhost's Avatar

OK.. Been tweeking the code and getting some interesting results - here's what I've came up with so far worth mentioning:

On line 138 when I change

scrollPos = (scrollPos + 1) % linethreelen;

To

scrollPos = (scrollPos + 18) % linethreelen;

I get

H a v e a N i c e D a y ! !

Scrolling on line 3 of the LCD, instead of

Have a Nice Day!!

The message seems to "overlap" itself during some parts of the sequence also. Close, but no cigar.

When I edit line 93 of the code by DELETING a space between the (" Have a Nice Day!! ") parentheses (changing lcd line 3's length to 36, instead of 37) I get no text, scrolling or otherwise on the lcd's line 3.

However, if I ADD a space there (changing line 3's length from 37 to 38),

I get -

!!!!!!!!!! scrolling by moving from right to left, then again (because I have two exclamation points, of course), followed by yyyyyyyyyy, aaaaaaaaaa, DDDDDDDDDD, long space, eeeeeeeeee, cccccccccc, iiiiiiiiii, NNNNNNNNNN, long space, aaaaaaaaaa, long space, etc... It's the obvious that follows; the rest of my message with its severe stuttering problem! Funeeeeeeeeee!

So, I'm figuring that there must be some sort of ratio, or equation that must be met relative to the amount of space between the parentheses with the message in it on line 93 and the "scroll position" entered in line 138? Or am I even barking up the right tree??

I'll keep messing around with it. I am however open again to any hints or suggestions that anyone might like to offer...

January 02, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Dave,

Im afraid this little bug is my fault. Back when I suggested you drop some {} I suggested you drop the wrong one accidentally. You want to put tback the } on 135 and remove the one on 143. When you removed the wrong one you basically extended the for loop that scrolls the characters to include the increment of scroll pos. So you were writing every new character out to a different scrolled position. What you want it to do is write all 20 characters with a single value of scrolpos, then increment scroll los once so the next time the message moves over by one. I hope I just haven't confused you more.

Sorry about the mix up, the holidys are hectic around here!

January 02, 2011
by SpaceGhost
SpaceGhost's Avatar

You mean remove the } on line 142, right?

January 02, 2011
by SpaceGhost
SpaceGhost's Avatar

Yahoo, it works!!! Here's the completed code with amended comments-

// tempsensor.c
// for NerdKits with ATmega168
// tempsensor modified with scrolling text message.

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
//
// PC0 -- temperature sensor analog input

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  return result;
}

double sampleToFahrenheit(uint16_t sample) {
  // conversion ratio in DEGREES/STEP:
  // (5000 mV / 1024 steps) * (1 degree / 10mV)
  //    ^^^^^^^^^^^      ^^^^^^^^^^
  //     from ADC         from LM34
  return sample * (5000.0 / 1024.0 / 10.0);
}

int main() {
  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // start up the Analog to Digital Converter
  adc_init();

  // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  // holder variables for temperature data
  uint16_t last_sample = 0;
  double this_temp;
  double temp_avg;
  uint8_t i;
// Variable to hold our current scroll offset.
int8_t scrollPos=0;

// This is our actual string, longer than the LCD width,
// stored in program memory (flash).
const char *linethree = PSTR(" Have a Nice Day!!       ");

// This calculates its width once.
// (For the string above, linethreelen = 37)
uint8_t linethreelen = strlen_P(linethree);

  while(1) {
    // take 100 samples and average them!
    temp_avg = 0.0;
    for(i=0; i<100; i++) {
      last_sample = adc_read();
      this_temp = sampleToFahrenheit(last_sample);

      // add this contribution to the average
      temp_avg = temp_avg + this_temp/100.0;
    }

    // write message to LCD
    lcd_home();
    lcd_write_string(PSTR("ADC: "));
    lcd_write_int16(last_sample);
    lcd_write_string(PSTR(" of 1024   "));
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("Temperature: %.2f"), temp_avg);
    lcd_write_data(0xdf);
    lcd_write_string(PSTR("F      "));
    lcd_line_three();

    for(i=0; i<20; i++) {
    // For each of the 20 columns of the LCD display,
    // we want to write a particular character from the
    // string to be scrolled.
    // Specifically, for column i (0..19), we want to write
    // the (scrollPos+i) character of the string as we shift it around.
    // But we take (scrollPos+i) MOD linethreelen to allow us to
    // "wrap around".
    // Calculate the right character number ((scrollPos+i)%linethreelen)
    // and add this to the linethree variable, essentially moving forward
    // in memory by that many bytes.
    // Then read it from program memory (pgm_read_byte).
    // Finally, write it out to the LCD (lcd_write_data).
    lcd_write_data(pgm_read_byte(linethree + ((scrollPos+i)%linethreelen)));
} 
  // Shift to the left by one position.  When we get to having
  // shifted by the entire length fo the string, then reset back to zero.
  scrollPos = (scrollPos + 1) % linethreelen;

  // Delay about 1/5th of a second before re-displaying.
  delay_ms(200);

    // write message to serial port
    printf_P(PSTR("%.2f degrees F\r\n"), temp_avg);
  }

  return 0;
}

This is really pretty cool! I can see that I have a lot to learn though...

I can foresee future possibilities for this project when I decide to revisit it (after some more education and practice of course). A temperature display with scrolling messages that correspond to specific temperature ranges comes to mind...

Ah, but for now I feel that I must move on - I have LEDs to blink and keyboards to destroy. I have some specific applications in mind to use with this great chip, other than putting together a fancy thermometer (which I had a blast working on).

Thanks again Humberto & nanaeem for your interest and great help!

Dave

Post a Reply

Please log in to post a reply.

Did you know that interrupts can be used to trigger pieces of code when events happen? Learn more...