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 » Optimization and Outgrowth

June 06, 2011
by delinthe
delinthe's Avatar

Hey guys first time posing on the forums. I've been working on setting up an environmental controller for a friends lizard enclosure using my nerd kid. My code is getting a bit lengthy and I just ran into an error which I've determined means I'm out of memory on my Atmega168. While i'll probably need to go to a 328 before I'm done with this project anyway, I bet that my code is highly inefficient, as I'm not used to having to deal with memory constrained environments and don't pay attention to things like memory/cpu expense of my programming.

For reference the error i'm getting is "avrdude: ERROR: address 0x4010 out of range at line 1025 of tempsensor.hex". So I'm just going to paste my gigantic unruly, unfinished chunk of code here and if anyone would like to critique my coding style/substance it would be greatly appreciated.

#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"
//---------------------------------------------------------------------------------------------------------------------------------------
const int mainmenu = 1; //different menu levels
const int night = 2;
const int day = 3;
const int bask = 4;
const int flouro = 5;
const int ceramic = 6;
const int humidifier = 7;
const int settime = 8;
const int settemp = 9;
const int sethumidity = 10;

const int start = 1;
const int stop = 2;

const int hour = 1;
const int minute = 2;
const int ampm = 3;
const int temp = 1;
const int temp_fract = 2;
const int humidity = 1;
const int humidity_fract = 2;

const int rollover_hour = 1;
const int rollover_minute = 2;
const int rollover_ampm = 3;
const int rollover_temp = 4;
const int rollover_temp_fract = 5;
const int rollover_humidity = 6;
const int rollover_humidity_fract = 7;

const int pass = 0;
const int true = 1;
const int false = 0;

uint8_t control_variable[8][4][4]; //[device][start/stop][section]
// first dimension = control device (day, night, bask, etc); second dimension = start or stop value; third dimension = variable segment (hour,minute,am_pm,degrees, fractions of degrees)
//---------------------------------------------------------------------------------------------------------------------------------------
const int cursor_right = 1; //holds the type of cursor that should be displayed right point arrow, up pointing arrow, or double up pointing arrow for double variables like hours or minutes that occupy two character spaces on the lcd
const int cursor_up = 2;
const int cursor_double_up =3;
//---------------------------------------------------------------------------------------------------------------------------------------
const int on = 0;
const int off = 1;
const int id_up = 0;
const int id_down = 1;
const int id_enter = 2;
//---------------------------------------------------------------------------------------------------------------------------------------
uint8_t edit_mode = 1; //sets whether or not up and down will change menu items or increment/decrement variables such as time or temperature
//---------------------------------------------------------------------------------------------------------------------------------------
uint8_t button_up; //holds button up state
uint8_t button_down; //holds button down state
uint8_t button_enter; //holds enter button state
uint8_t switch_debug; //holds debug switch state
//---------------------------------------------------------------------------------------------------------------------------------------
uint8_t clock_sec; //holds clock seconds
uint8_t clock_min; //holds clock minutes
uint8_t clock_hour; //holds clock hours
uint8_t clock_ampm; //holds clock am/pm state
uint8_t clock_ampm_check; //used in rolling over am and pm state

volatile int16_t click; //holds interupt counter used to keep accurate time based on crystal frequency
//---------------------------------------------------------------------------------------------------------------------------------------
uint8_t current_menu; //holds the value of which menu the user is presently in
uint8_t last_item; //holds the item number of the previous item so update cursor knows how many blocks 1 or 2 to overwrite with blank characters to clear the previous cursor position
//---------------------------------------------------------------------------------------------------------------------------------------
//uint8_t night_start_hour; //hold the different values of the variables used to control the execution of components
//uint8_t night_start_minute;
//uint8_t night_start_ampm;
//uint8_t day_start_hour;
//uint8_t day_start_minute;
//uint8_t day_start_ampm;
//uint8_t bask_start_hour;
//uint8_t bask_start_minute;
//uint8_t bask_start_ampm;
//uint8_t flouro_start_hour;
//uint8_t flouro_start_minute;
//uint8_t flouro_start_ampm;
//
//uint8_t night_stop_hour;
//uint8_t night_stop_minute;
//uint8_t night_stop_ampm;
//uint8_t day_stop_hour;
//uint8_t day_stop_minute;
//uint8_t day_stop_ampm;
//uint8_t bask_stop_hour;
//uint8_t bask_stop_minute;
//uint8_t bask_stop_ampm;
//uint8_t flouro_stop_hour;
//uint8_t flouro_stop_minute;
//uint8_t flouro_stop_ampm;
//
//uint8_t ceramic_start_temp;
//uint8_t ceramic_start_temp_fract;
//uint8_t ceramic_stop_temp;
//uint8_t ceramic_stop_temp_fract;
//uint8_t humidifier_start_fract;
//uint8_t humidifier_start;
//uint8_t humidifier_stop_fract;
//uint8_t humidifier_stop;
//---------------------------------------------------------------------------------------------------------------------------------------
int last_button_press[3]; //used for debouncing and delaying button presses
int last_button_press_time[3];
int last_button_press_reset[3];
int button_return[3];
//---------------------------------------------------------------------------------------------------------------------------------------
void main_menu_init();
void update_curson();
void menu_execute();
void update_cursor(int cursor_row,int cursor_column,int cursor_orientation,int previous);
void sub_menu_init(int sub_level);
void set_variable_menu_init(int sub_level, int variable_number);
void cursor_init();
void set_time_init();
void set_temp_init();
void set_humidity_init();
void set_humidifier_init();
void night_menu_init();
void day_menu_init();
void bask_menu_init();
void flouro_menu_init();
void ceramic_menu_init();
void humidifier_menu_init();
void set_variable(int presenting_menu_level,int presenting_variable_number,int variable_section,int direction,int first_pass);
int debounce_and_delay(int button_press,int button_id);
//---------------------------------------------------------------------------------------------------------------------------------------
struct cursor{
    int x_position;
    int y_position;
    int orientation;
};

struct interface{
    int item;
    int total_item;
    struct cursor arrow[8];
};

struct interface menu[11];
//---------------------------------------------------------------------------------------------------------------------------------------
FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
//---------------------------------------------------------------------------------------------------------------------------------------
void adc_init(){

    ADMUX = 0;
    ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
    ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
    while(ADCSRA & (1<<ADSC)){}

    uint16_t result = ADCL;
    uint16_t temp = ADCH;
    result = result + (temp<<8);

    ADCSRA |= (1<<ADSC);

    return result;
}

double sampleToFahrenheit(uint16_t sample) {

    return sample * (5000.0/1024.0/10.0);
}

void timer_init(){
    TCCR0A |= (1<<WGM01);
    TCCR0B |= (1<<CS02) | (1<<CS00);
    OCR0A = 143;
    TIMSK0 |= (1<<OCIE0A);
}

int main() {
lcd_init();

    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
    lcd_home();

    adc_init();

    uart_init();

    FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
    stdin = stdout = &uart_stream;

    DDRC &= (1<<PC5);
    DDRC &= (1<<PC4);
    DDRC &= (1<<PC3);
    DDRC &= (1<<PC2);

    PORTC |= (1<<PC5);
    PORTC |= (1<<PC4);
    PORTC |= (1<<PC3);
    PORTC |= (1<<PC2);

    timer_init();

    sei();

    uint16_t last_sample = 0;

    double this_temp;
    double temp_avg;

    uint8_t i;

    clock_sec = 0;
    clock_min = 0;
    clock_hour = 12;
    clock_ampm = 1;

    cursor_init();

    current_menu = mainmenu;
    main_menu_init();

    while(1) {

        //lcd_home();
        //fprintf_P(&lcd_stream, PSTR("%.2d-u %.2d-d %.4d-click"),button_up,button_down,click);

        temp_avg = 0.0;
        for(i=0; i<100; i++) {
            last_sample = adc_read();
            this_temp = sampleToFahrenheit(last_sample);
            temp_avg = temp_avg + this_temp/100;
        }
        //lcd_home();
        //fprintf_P(&lcd_stream, PSTR("%.2d:%.2d.%.2d"),button_up,button_down,button_enter);

    } 
}
SIGNAL(SIG_OUTPUT_COMPARE0A){
    click++;    
    if (click == 6001){
        click = 0;
    }
    clock_sec = (click / 100);
    //---------------------------------------------------------------------------------------------------------------------------------------
    button_up = (PINC & (1<<PC5)) >> PC5; //dip 1
    button_enter = (PINC & (1<<PC3)) >> PC3; //dip 3
    button_down = (PINC & (1<<PC4)) >> PC4; //dip 4
    switch_debug = (PINC & (1<<PC2)) >> PC2;

    if ((debounce_and_delay(button_down,id_down)) == 1){
            if ((menu[current_menu].item < menu[current_menu].total_item)&&(edit_mode == off)){
                last_item = menu[current_menu].item;
                menu[current_menu].item++;
                update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);

            }
            else if (edit_mode == on){
                set_variable(pass,pass,pass,id_down,false);
            }
        }

    if ((debounce_and_delay(button_up,id_up)) == 1){
        if ((menu[current_menu].item > 1)&&(edit_mode == off)){
            last_item = menu[current_menu].item;
            menu[current_menu].item--;
            update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);

        }
        else if (edit_mode == on){
                set_variable(pass,pass,pass,id_up,false);
        }
    }

    if (debounce_and_delay(button_enter,id_enter) == 1){
        if (edit_mode == off){
            menu_execute(current_menu,menu[current_menu].item);
        }
        else if (edit_mode == on){

        }
    }
}
void menu_execute(current_menu_level, itemnum){
    uint8_t first_pass = on;
    static uint8_t passing_menu;
    static uint8_t passing_item;

    if (current_menu_level == mainmenu){
        if (itemnum == 1){
            current_menu = night;
            sub_menu_init(night);
        }

        if (itemnum == 2){
            current_menu = day;
            sub_menu_init(day);
        }

        if (itemnum == 3){
            current_menu = bask;
            sub_menu_init(bask);

        }

        if (itemnum == 4){
            current_menu = flouro;
            sub_menu_init(flouro);

        }
        if (itemnum == 5){
            current_menu = ceramic;
            sub_menu_init(ceramic);

        }
        if (itemnum == 6){
            current_menu = humidifier;
            sub_menu_init(humidifier);
        }
        first_pass = off;

    }

    if ((current_menu_level == night)&&(first_pass == on)){
        passing_menu = current_menu_level;
        if (itemnum < 3){
            passing_item = itemnum;
        }
        if (itemnum == 1){
            set_variable_menu_init(night,1);
            current_menu = settime; 
        }
        if (itemnum == 2){
            set_variable_menu_init(night,2);
            current_menu = settime;
        }
        if (itemnum == 3){
            //clear temp variable
            current_menu = mainmenu;
            main_menu_init();
        }
        if (itemnum == 4){
            //save temp variable
            current_menu = mainmenu;
            main_menu_init();
        }
        first_pass = off;   
    }
    if ((current_menu_level == day)&&(first_pass == on)){
        passing_menu = current_menu_level;
        if (itemnum < 3){
            passing_item = itemnum;
        }
        if (itemnum == 1){
            set_variable_menu_init(day,1);
            current_menu = settime;
        }
        if (itemnum == 2){
            set_variable_menu_init(day,2);
            current_menu = settime;
        }
        if (itemnum == 3){
            current_menu = mainmenu;
            main_menu_init();
        }
        if (itemnum == 4){
            //save temp variable
            current_menu = mainmenu;
            main_menu_init();
        }
        first_pass = off;
    }
    if ((current_menu_level == bask)&&(first_pass == on)){
        passing_menu = current_menu_level;
        if (itemnum < 3){
            passing_item = itemnum;
        }
        if (itemnum == 1){
            set_variable_menu_init(bask,1);
            current_menu = settime;
        }
        if (itemnum == 2){
            set_variable_menu_init(bask,2);
            current_menu = settime;
        }
        if (itemnum == 3){
            current_menu = mainmenu;
            main_menu_init();
        }
        if (itemnum == 4){
            //save temp variable
            current_menu = mainmenu;
            main_menu_init();
        }
        first_pass = off;
    }
    if ((current_menu_level == flouro)&&(first_pass == on)){
        passing_menu = current_menu_level;
        if (itemnum < 3){
            passing_item = itemnum;
        }
        if (itemnum == 1){
            set_variable_menu_init(flouro,1);
            current_menu = settime;
        }
        if (itemnum == 2){
            set_variable_menu_init(flouro,2);
            current_menu = settime;
        }
        if (itemnum == 3){
            current_menu = mainmenu;
            main_menu_init();
        }
        if (itemnum == 4){
            //save temp variable
            current_menu = mainmenu;
            main_menu_init();
        }
        first_pass = off;
    }
    if ((current_menu_level == ceramic)&&(first_pass == on)){
        passing_menu = current_menu_level;
        if (itemnum < 3){
            passing_item = itemnum;
        }
        if (itemnum == 1){
            set_variable_menu_init(ceramic,1);
            current_menu = settemp;
        }
        if (itemnum == 2){
            set_variable_menu_init(ceramic,2);
            current_menu = settemp;
        }
        if (itemnum == 3){
            current_menu = mainmenu;
            main_menu_init();
        }
        if (itemnum == 4){
            //save temp variable
            current_menu = mainmenu;
            main_menu_init();
        }
        first_pass = off;
    }
    if ((current_menu_level == humidifier)&&(first_pass == on)){
        passing_menu = current_menu_level;
        if (itemnum < 3){
            passing_item = itemnum;
        }
        if (itemnum == 1){
            set_variable_menu_init(humidifier,1);
            current_menu = sethumidity;
        }
        if (itemnum == 2){
            set_variable_menu_init(humidifier,2);
            current_menu = sethumidity;
        }
        if (itemnum == 3){
            current_menu = mainmenu;
            main_menu_init();
        }
        if (itemnum == 4){
            //save temp variable
            current_menu = mainmenu;
            main_menu_init();
        }
        first_pass = off;
    }
    if ((current_menu_level == settime)&&(first_pass == on)){
        if (itemnum == 1){
            edit_mode = on;
            set_variable(passing_menu, passing_item, itemnum,id_enter,true);
        }
        if (itemnum == 2){
            edit_mode = on;
            set_variable(passing_menu, passing_item, itemnum, id_enter,true);
        }
        if (itemnum == 3){
            edit_mode = on;
            set_variable(passing_menu, passing_item, itemnum, id_enter,true);
        }
        if (itemnum == 4){
            //save variable code
        }
        if (itemnum == 5){
            current_menu = passing_menu;
            menu[current_menu].item = passing_item;
        }
    }
    if ((current_menu_level == settemp)&&(first_pass == on)){
        if (itemnum == 1){
            edit_mode = on;
            set_variable(passing_menu, passing_item, itemnum,id_enter,true);    
        }
        if (itemnum == 2){
            edit_mode = on;
            set_variable(passing_menu, passing_item, itemnum,id_enter,true);
        }
        if (itemnum == 3){
            //save variable code
        }
        if (itemnum == 4){
            current_menu = passing_menu;
            menu[current_menu].item = passing_item;
        }
    }
    if ((current_menu_level == sethumidity)&&(first_pass == on)){
        if (itemnum == 1){
            edit_mode = on;
            set_variable(passing_menu, passing_item, itemnum,id_enter,true);
        }
        if (itemnum == 2){
            edit_mode = on;
            set_variable(passing_menu, passing_item, itemnum,id_enter,true);
        }
        if (itemnum == 3){
            //save variable code
        }
        if (itemnum == 4){
            current_menu = passing_menu;
            menu[current_menu].item = passing_item;
        }
    }
}

void set_variable_menu_init(int sub_level, int variable_number){
    lcd_clear_and_home();
    lcd_goto_position(3,1);
    lcd_write_string(PSTR("Save"));
    lcd_goto_position(3,14);
    lcd_write_string(PSTR("Cancel"));
    update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, 0);

    if (sub_level == night){
        if (variable_number == start){
            lcd_goto_position(0,4);
            lcd_write_string(PSTR("-Night Start-"));    
            lcd_goto_position(1,6);
            fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[night][start][hour], control_variable[night][start][minute], control_variable[night][start][ampm]);
        }
        if (variable_number == stop){
            lcd_goto_position(0,4);
            lcd_write_string(PSTR("-Night Stop-"));
            lcd_goto_position(1,6);
            fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[night][stop][hour], control_variable[night][stop][minute], control_variable[night][stop][ampm]);
        }
        update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
    }
    if (sub_level == day){
        if (variable_number == start){
            lcd_goto_position(0,4);
            lcd_write_string(PSTR("-Day Start-"));
            lcd_goto_position(1,6);
            fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[day][start][hour], control_variable[day][start][minute], control_variable[day][start][ampm]);
        }
        if (variable_number == stop){
            lcd_goto_position(0,5);
            lcd_write_string(PSTR("-Day Stop-"));
            lcd_goto_position(1,6);
            fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[day][stop][hour], control_variable[day][stop][minute], control_variable[day][stop][ampm]);
        }
        update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
    }
    if (sub_level == bask){
        if (variable_number == start){
            lcd_goto_position(0,4);
            lcd_write_string(PSTR("-Bask Start-"));
            lcd_goto_position(1,6);
            fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[bask][start][hour], control_variable[bask][start][minute], control_variable[bask][start][ampm]);
        }
        if (variable_number == stop){
            lcd_goto_position(0,4);
            lcd_write_string(PSTR("-Bask Stop-"));
            lcd_goto_position(1,6);
            fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[bask][stop][hour], control_variable[bask][stop][minute], control_variable[bask][stop][ampm]);
        }
        update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
    }
    if (sub_level == flouro){
        if (variable_number == start){
            lcd_goto_position(0,3);
            lcd_write_string(PSTR("-Flouro Start-"));
            lcd_goto_position(1,6);
            fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[flouro][start][hour], control_variable[flouro][start][minute], control_variable[flouro][start][ampm]);
        }
        if (variable_number == stop){
            lcd_goto_position(0,3);
            lcd_write_string(PSTR("-Flouro Stop-"));
            lcd_goto_position(1,6);
            fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[flouro][stop][hour], control_variable[flouro][stop][minute], control_variable[flouro][stop][ampm]);
        }
        update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
    }
    if (sub_level == ceramic){
        if (variable_number == start){
            lcd_goto_position(0,3);
            lcd_write_string(PSTR("-Ceramic Start-"));
            lcd_goto_position(1,6);
            fprintf_P(&lcd_stream, PSTR("%.2d.%.2d"),control_variable[ceramic][start][temp], control_variable[ceramic][start][temp_fract]);
            lcd_write_data(0xdf);
        }
        if (variable_number == stop){
            lcd_goto_position(0,3);
            lcd_write_string(PSTR("-Ceramic Stop-"));
            lcd_goto_position(1,6);
            fprintf_P(&lcd_stream, PSTR("%.2d.%.2d"),control_variable[ceramic][stop][temp], control_variable[ceramic][stop][temp_fract]);
            lcd_write_data(0xdf);
        }
        update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
    }
    if (sub_level == humidifier){
        if (variable_number == start){
            lcd_goto_position(0,3);
            lcd_write_string(PSTR("-Humid Start-"));
            lcd_goto_position(1,6);
            fprintf_P(&lcd_stream, PSTR("%.2d.%.2d% RH"),control_variable[humidifier][start][humidity], control_variable[humidifier][start][humidity_fract]);
            lcd_write_data(0xdf);
        }
        if (variable_number == stop){
            lcd_goto_position(0,4);
            lcd_write_string(PSTR("-Humid Stop-"));
            lcd_goto_position(1,6);
            fprintf_P(&lcd_stream, PSTR("%.2d:%.2d% RH"),control_variable[humidifier][stop][humidity], control_variable[humidifier][stop][humidity_fract]);
            lcd_write_data(0xdf);
        }
        update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
    }
}

void main_menu_init(){  
    lcd_clear_and_home();
    lcd_goto_position(0,1);
    lcd_write_string(PSTR("Night"));
    lcd_goto_position(1,1);
    lcd_write_string(PSTR("Day"));
    lcd_goto_position(2,1);
    lcd_write_string(PSTR("Bask"));
    lcd_goto_position(3,1);
    lcd_write_string(PSTR("Flouro"));
    lcd_goto_position(0,10);
    lcd_write_string(PSTR("Ceramic"));
    lcd_goto_position(1,10);    
    lcd_write_string(PSTR("Humidifier"));
    lcd_goto_position(2,10);
    lcd_write_string(PSTR("Time"));
    update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, 0);
}

void sub_menu_init(sub_level){
    lcd_clear_and_home();
    lcd_goto_position(1,1);
    lcd_write_string(PSTR("Start"));
    lcd_goto_position(2,1);
    lcd_write_string(PSTR("Stop"));
    lcd_goto_position(3,1);
    lcd_write_string(PSTR("Save"));
    lcd_goto_position(3,14);
    lcd_write_string(PSTR("Cancel"));
    update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);              
    if (sub_level == night){
        //night_menu_init();
        lcd_goto_position(0,6);
        lcd_write_string(PSTR("-Night-"));
        lcd_goto_position(1,7);
        printf_P(PSTR("%.2d:%.2d%s"),control_variable[night][start][hour],control_variable[night][start][minute],control_variable[night][start][ampm]);
        lcd_goto_position(2,7);
        printf_P(PSTR("%.2d:%.2d%s"),control_variable[night][start][hour],control_variable[night][start][minute],control_variable[night][start][ampm]);
        update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
    }
    if (sub_level == day){
        //day_menu_init();
        lcd_goto_position(0,7);
        lcd_write_string(PSTR("-Day-"));
        lcd_goto_position(1,7);
        fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[day][start][hour],control_variable[day][start][minute],control_variable[day][start][ampm]);
        lcd_goto_position(2,7);
        fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[day][stop][hour],control_variable[day][stop][minute],control_variable[day][stop][ampm]);
        update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
    }
    if (sub_level == bask){
        //bask_menu_init();
        lcd_line_one();
        lcd_goto_position(0,7);
        lcd_write_string(PSTR("-Bask-"));
        lcd_goto_position(1,7);
        fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[bask][start][hour],control_variable[bask][start][minute],control_variable[bask][start][ampm]);
        lcd_goto_position(2,7);
        fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[bask][stop][hour],control_variable[bask][stop][minute],control_variable[bask][stop][ampm]);
        update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
    }
    if (sub_level == flouro){
        //flouro_menu_init();
        lcd_goto_position(0,6);
        lcd_write_string(PSTR("-Flouro-"));
        lcd_goto_position(1,7);
        fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[flouro][start][hour],control_variable[flouro][start][minute],control_variable[flouro][start][ampm]);
        lcd_goto_position(2,7);
        fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[flouro][stop][hour],control_variable[flouro][stop][minute],control_variable[flouro][stop][ampm]);
        update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
    }
    if (sub_level == ceramic){
        //ceramic_menu_init();
        lcd_goto_position(0,5);
        lcd_write_string(PSTR("-Ceramic-"));
        lcd_goto_position(1,7);
        fprintf_P(&lcd_stream, PSTR("%2.1fF"),control_variable[ceramic][start][temp],control_variable[ceramic][start][temp_fract]);
        lcd_goto_position(2,7);
        fprintf_P(&lcd_stream, PSTR("%2.1fF"),control_variable[ceramic][stop][temp],control_variable[ceramic][stop][temp_fract]);
        update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
    }
    if (sub_level == humidifier){
        //humidifier_menu_init();
        lcd_goto_position(0,4);
        lcd_write_string(PSTR("-Humidifier"));
        lcd_goto_position(1,6);
        fprintf_P(&lcd_stream,PSTR("%2.2f%-RHumid."),control_variable[humidifier][start][humidity],control_variable[humidifier][start][humidity_fract]);
        lcd_goto_position(2,6);
        fprintf_P(&lcd_stream,PSTR("%2.2f%-RHumid."),control_variable[humidifier][stop][humidity],control_variable[humidifier][stop][humidity_fract]);
        update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
    }
}

void night_menu_init(){
    lcd_goto_position(0,6);
    lcd_write_string(PSTR("-Night-"));
    lcd_goto_position(1,7);
    printf_P(PSTR("%.2d:%.2d%s"),control_variable[night][start][hour],control_variable[night][start][minute],control_variable[night][start][ampm]);
    lcd_goto_position(2,7);
    printf_P(PSTR("%.2d:%.2d%s"),control_variable[night][stop][hour],control_variable[night][stop][minute],control_variable[night][stop][ampm]);
    update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
}

void day_menu_init(){

    lcd_goto_position(0,7);
    lcd_write_string(PSTR("-Day-"));
    lcd_goto_position(1,7);
    fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[day][start][hour],control_variable[day][start][minute],control_variable[day][start][ampm]);
    lcd_goto_position(2,7);
    fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[day][stop][hour],control_variable[day][stop][minute],control_variable[day][stop][ampm]);
    update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
}

void bask_menu_init(){

    lcd_line_one();
    lcd_goto_position(0,7);
    lcd_write_string(PSTR("-Bask-"));
    lcd_goto_position(1,7);
    fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[bask][start][hour],control_variable[bask][start][minute],control_variable[bask][start][ampm]);
    lcd_goto_position(2,7);
    fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[bask][stop][hour],control_variable[bask][stop][minute],control_variable[bask][stop][ampm]);
    update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
}

void flouro_menu_init(){

    lcd_goto_position(0,6);
    lcd_write_string(PSTR("-Flouro-"));
    lcd_goto_position(1,7);
    fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[flouro][start][hour],control_variable[flouro][start][minute],control_variable[flouro][start][ampm]);
    lcd_goto_position(2,7);
    fprintf_P(&lcd_stream, PSTR("%.2d:%.2d%s"),control_variable[flouro][stop][hour],control_variable[flouro][stop][minute],control_variable[flouro][stop][ampm]);
    update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
}

void ceramic_menu_init(){

    lcd_goto_position(0,5);
    lcd_write_string(PSTR("-Ceramic-"));
    lcd_goto_position(1,7);
    fprintf_P(&lcd_stream, PSTR("%2.1fF"),control_variable[ceramic][start][temp],control_variable[ceramic][start][temp_fract]);
    lcd_goto_position(2,7);
    fprintf_P(&lcd_stream, PSTR("%2.1fF"),control_variable[ceramic][stop][temp],control_variable[ceramic][stop][temp_fract]);
    update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
}

void humidifier_menu_init(){
    lcd_goto_position(0,4);
    lcd_write_string(PSTR("-Humidifier-"));
    lcd_goto_position(1,7);
    fprintf_P(&lcd_stream, PSTR("%2.1fF"),control_variable[humidifier][start][humidity],control_variable[humidifier][start][humidity_fract]);
    lcd_goto_position(2,7);
    fprintf_P(&lcd_stream, PSTR("%2.1fF"),control_variable[humidifier][stop][humidity],control_variable[humidifier][stop][humidity_fract]);
    update_cursor(menu[current_menu].arrow[menu[current_menu].item].x_position , menu[current_menu].arrow[menu[current_menu].item].y_position , menu[current_menu].arrow[menu[current_menu].item].orientation, last_item);
}

void rollover(rolling_variable,rolling_variable_type,rolling_variable_direction){

    if (rolling_variable_direction == id_down){
        rolling_variable--;
    }
    else if (rolling_variable_direction == id_up){
        rolling_variable++;
    }

    if (rolling_variable_type == rollover_hour){
        if (rolling_variable == 13){
            rolling_variable = 1;
        }
    }
    if (rolling_variable_type == rollover_minute){
        if (rolling_variable == 60){
            rolling_variable = 0;
        }
    }
    if (rolling_variable_type == rollover_ampm){
        if (rolling_variable == 2){
            rolling_variable = 0;
        }

    }
    if (rolling_variable_type == rollover_temp){
        if (rolling_variable == 100){
            rolling_variable = 0;
        }
    }
    if (rolling_variable_type == rollover_temp_fract){
        if (rolling_variable == 100){
            rolling_variable = 0;
        }
    }
    if (rolling_variable_type == rollover_humidity){
        if (rolling_variable == 101){
            rolling_variable = 0;
        }
    }
    if (rolling_variable_type == rollover_humidity_fract){
        if (rolling_variable == 100){
            rolling_variable = 0;
        }
    }

    if (clock_sec == 60){
        clock_min++;
    }

    if (clock_min == 60){
        clock_min = 0;
        clock_hour++;
    }

    if (clock_hour == 12){
        if (clock_ampm_check == 0){
            clock_ampm++;
            clock_ampm_check = 1;
        }
    }

    if (clock_ampm == 3){
        clock_ampm =1;
    }

    if (clock_hour == 13){
        clock_hour = 1;
        clock_ampm_check = 0;
    }
}

void update_cursor(cursor_row,cursor_column,cursor_orientation,previous){

    if (previous != 0){
        lcd_goto_position(menu[current_menu].arrow[last_item].x_position , menu[current_menu].arrow[last_item].y_position); 
        if ((cursor_orientation == 1) | (cursor_orientation == 2)){
            lcd_write_string(PSTR(" "));
        }
        if (cursor_orientation == 3){
            lcd_write_string(PSTR("  "));
        }
    }
    lcd_goto_position(cursor_row,cursor_column);

    if (cursor_orientation == 1){
        lcd_write_data(0x7e);
    }
    else if (cursor_orientation == 2){
        lcd_write_data(0x5e);
    }
    else if (cursor_orientation == 3){
        lcd_write_data(0x5e);
        lcd_write_data(0x5e);
    }
}

void cursor_init(){
    //--------------------------------
    //--------------------------------
    menu[mainmenu].item = 1;
    menu[mainmenu].total_item = 7;
    menu[mainmenu].arrow[1].x_position = 0;
    menu[mainmenu].arrow[1].y_position = 0;
    menu[mainmenu].arrow[1].orientation = cursor_right;

    menu[mainmenu].arrow[2].x_position = 1;
    menu[mainmenu].arrow[2].y_position = 0;
    menu[mainmenu].arrow[2].orientation = cursor_right;

    menu[mainmenu].arrow[3].x_position = 2;
    menu[mainmenu].arrow[3].y_position = 0;
    menu[mainmenu].arrow[3].orientation = cursor_right;

    menu[mainmenu].arrow[4].x_position = 3;
    menu[mainmenu].arrow[4].y_position = 0;
    menu[mainmenu].arrow[4].orientation = cursor_right;

    menu[mainmenu].arrow[5].x_position = 0;
    menu[mainmenu].arrow[5].y_position = 9;
    menu[mainmenu].arrow[5].orientation = cursor_right;

    menu[mainmenu].arrow[6].x_position = 1;
    menu[mainmenu].arrow[6].y_position = 9;
    menu[mainmenu].arrow[6].orientation = cursor_right;

    menu[mainmenu].arrow[7].x_position = 2;
    menu[mainmenu].arrow[7].y_position = 9;
    menu[mainmenu].arrow[7].orientation = cursor_right;
    //--------------------------------
    //--------------------------------
    menu[night].item = 1;
    menu[night].total_item = 4;
    menu[night].arrow[1].x_position = 1;
    menu[night].arrow[1].y_position = 0;
    menu[night].arrow[1].orientation = cursor_right;

    menu[night].arrow[2].x_position = 2;
    menu[night].arrow[2].y_position = 0;
    menu[night].arrow[2].orientation = cursor_right;

    menu[night].arrow[3].x_position = 3;
    menu[night].arrow[3].y_position = 0;
    menu[night].arrow[3].orientation = cursor_right;

    menu[night].arrow[4].x_position = 3;
    menu[night].arrow[4].y_position = 13;
    menu[night].arrow[4].orientation = cursor_right;
    //--------------------------------
    //--------------------------------
    menu[day].item = 1;
    menu[day].total_item = 4;
    menu[day].arrow[1].x_position = 1;
    menu[day].arrow[1].y_position = 0;
    menu[day].arrow[1].orientation = cursor_right;

    menu[day].arrow[2].x_position = 2;
    menu[day].arrow[2].y_position = 0;
    menu[day].arrow[2].orientation = cursor_right;

    menu[day].arrow[3].x_position = 3;
    menu[day].arrow[3].y_position = 0;
    menu[day].arrow[3].orientation = cursor_right;

    menu[day].arrow[4].x_position = 3;
    menu[day].arrow[4].y_position = 13;
    menu[day].arrow[4].orientation = cursor_right;
    //--------------------------------
    //--------------------------------
    menu[bask].item = 1;
    menu[bask].total_item = 4;
    menu[bask].arrow[1].x_position = 1;
    menu[bask].arrow[1].y_position = 0;
    menu[bask].arrow[1].orientation = cursor_right;

    menu[bask].arrow[2].x_position = 2;
    menu[bask].arrow[2].y_position = 0;
    menu[bask].arrow[2].orientation = cursor_right;

    menu[bask].arrow[3].x_position = 3;
    menu[bask].arrow[3].y_position = 0;
    menu[bask].arrow[3].orientation = cursor_right;

    menu[bask].arrow[4].x_position = 3;
    menu[bask].arrow[4].y_position = 13;
    menu[bask].arrow[4].orientation = cursor_right;
    //--------------------------------
    //--------------------------------
    menu[flouro].item = 1;
    menu[flouro].total_item = 4;
    menu[flouro].arrow[1].x_position = 1;
    menu[flouro].arrow[1].y_position = 0;
    menu[flouro].arrow[1].orientation = cursor_right;

    menu[flouro].arrow[2].x_position = 2;
    menu[flouro].arrow[2].y_position = 0;
    menu[flouro].arrow[2].orientation = cursor_right;

    menu[flouro].arrow[3].x_position = 3;
    menu[flouro].arrow[3].y_position = 0;
    menu[flouro].arrow[3].orientation = cursor_right;

    menu[flouro].arrow[4].x_position = 3;
    menu[flouro].arrow[4].y_position = 13;
    menu[flouro].arrow[4].orientation = cursor_right;
    //--------------------------------
    //--------------------------------
    menu[ceramic].item = 1;
    menu[ceramic].total_item = 4;
    menu[ceramic].arrow[1].x_position = 1;
    menu[ceramic].arrow[1].y_position = 0;
    menu[ceramic].arrow[1].orientation = cursor_right;

    menu[ceramic].arrow[2].x_position = 2;
    menu[ceramic].arrow[2].y_position = 0;
    menu[ceramic].arrow[2].orientation = cursor_right;

    menu[ceramic].arrow[3].x_position = 3;
    menu[ceramic].arrow[3].y_position = 0;
    menu[ceramic].arrow[3].orientation = cursor_right;

    menu[ceramic].arrow[4].x_position = 3;
    menu[ceramic].arrow[4].y_position = 13;
    menu[ceramic].arrow[4].orientation = cursor_right;
    //--------------------------------
    //--------------------------------
    menu[humidifier].item = 1;
    menu[humidifier].total_item = 4;
    menu[humidifier].arrow[1].x_position = 1;
    menu[humidifier].arrow[1].y_position = 0;
    menu[humidifier].arrow[1].orientation = cursor_right;

    menu[humidifier].arrow[2].x_position = 2;
    menu[humidifier].arrow[2].y_position = 0;
    menu[humidifier].arrow[2].orientation = cursor_right;

    menu[humidifier].arrow[3].x_position = 3;
    menu[humidifier].arrow[3].y_position = 0;
    menu[humidifier].arrow[3].orientation = cursor_right;

    menu[humidifier].arrow[4].x_position = 3;
    menu[humidifier].arrow[4].y_position = 13;
    menu[humidifier].arrow[4].orientation = cursor_right;
    //--------------------------------
    //--------------------------------
    menu[settime].item = 1;
    menu[settime].total_item = 5;
    menu[settime].arrow[1].x_position = 2;
    menu[settime].arrow[1].y_position = 5;
    menu[settime].arrow[1].orientation = cursor_double_up;

    //menu[settime].arrow[2].x_position = 2;
    //menu[settime].arrow[2].y_position = 6;
    //menu[settime].arrow[2].orientation = cursor_up;

    menu[settime].arrow[2].x_position = 2;
    menu[settime].arrow[2].y_position = 8;
    menu[settime].arrow[2].orientation = cursor_double_up;

    //menu[settime].arrow[4].x_position = 2;
    //menu[settime].arrow[4].y_position = 9;
    //menu[settime].arrow[4].orientation = cursor_up;

    menu[settime].arrow[3].x_position = 2;
    menu[settime].arrow[3].y_position = 10;
    menu[settime].arrow[3].orientation = cursor_double_up;

    menu[settime].arrow[4].x_position = 3;
    menu[settime].arrow[4].y_position = 0;
    menu[settime].arrow[4].orientation = cursor_right;

    menu[settime].arrow[5].x_position = 3;
    menu[settime].arrow[5].y_position = 13;
    menu[settime].arrow[5].orientation = cursor_right;
    //--------------------------------
    //--------------------------------
    menu[settemp].item = 1;
    menu[settemp].total_item = 5;
    menu[settemp].arrow[1].x_position = 2;
    menu[settemp].arrow[1].y_position = 7;
    menu[settemp].arrow[1].orientation = cursor_up;

    menu[settemp].arrow[2].x_position = 2;
    menu[settemp].arrow[2].y_position = 8;
    menu[settemp].arrow[2].orientation = cursor_up;

    menu[settemp].arrow[3].x_position = 2;
    menu[settemp].arrow[3].y_position = 10;
    menu[settemp].arrow[3].orientation = cursor_up;

    menu[settemp].arrow[4].x_position = 3;
    menu[settemp].arrow[4].y_position = 0;
    menu[settemp].arrow[4].orientation = cursor_right;

    menu[settemp].arrow[5].x_position = 3;
    menu[settemp].arrow[5].y_position = 13;
    menu[settemp].arrow[5].orientation = cursor_right;
    //--------------------------------
    //--------------------------------
    menu[sethumidity].item = 1;
    menu[sethumidity].total_item = 6;
    menu[sethumidity].arrow[1].x_position = 2;
    menu[sethumidity].arrow[1].y_position = 6;
    menu[sethumidity].arrow[1].orientation = cursor_up;

    menu[sethumidity].arrow[2].x_position = 2;
    menu[sethumidity].arrow[2].y_position = 7;
    menu[sethumidity].arrow[2].orientation = cursor_up;

    menu[sethumidity].arrow[3].x_position = 2;
    menu[sethumidity].arrow[3].y_position = 9;
    menu[sethumidity].arrow[3].orientation = cursor_up;

    menu[sethumidity].arrow[4].x_position = 2;
    menu[sethumidity].arrow[4].y_position = 10;
    menu[sethumidity].arrow[4].orientation = cursor_up;

    menu[sethumidity].arrow[5].x_position = 3;
    menu[sethumidity].arrow[5].y_position = 0;
    menu[sethumidity].arrow[5].orientation = cursor_right;

    menu[sethumidity].arrow[6].x_position = 3;
    menu[sethumidity].arrow[6].y_position = 13;
    menu[sethumidity].arrow[6].orientation = cursor_right;
    //--------------------------------
    //--------------------------------
}
int debounce_and_delay(button_press,button_id){

    if (button_press == on){
        if (button_press != last_button_press[button_id]){
            last_button_press_time[button_id] = click;
        }
    }

    if (button_press == on){
        if (button_press != last_button_press[button_id]){
            button_return[button_id] = 1;

        }
        if (button_press == last_button_press[button_id]){
            if (click == (last_button_press_time[button_id] + 50)){
                last_button_press_reset[button_id] = 1;
                button_return[button_id] = 1;

            }
        }
        if (button_press == last_button_press[button_id]){
            if (click < (last_button_press_time[button_id] + 50)){
                button_return[button_id] = 0;

            }
        }

    }
    else if (button_press == off){
        button_return[button_id] = 0;

    }

    if (last_button_press_reset[button_id] == 1){
        last_button_press_time[button_id] = click;
        last_button_press_reset[button_id] = 0;
    }

    last_button_press[button_id] = button_press;

    if (button_return[button_id] == 1){

        return 1;
    }

    return 0;
}

void set_variable(presenting_menu_level,presenting_variable_number,variable_section,direction,first_pass){
    static int set_variable_menu_level;
    static int set_variable_variable_number;
    static int set_variable_variable_section;

    if (first_pass == true){
        set_variable_menu_level = presenting_menu_level;
        set_variable_variable_number = presenting_variable_number; 
        set_variable_variable_section = variable_section;
    }

    if ((presenting_menu_level == night) | (presenting_menu_level == day) | (presenting_menu_level == bask) | (presenting_menu_level == flouro)){
        if (set_variable_variable_number == hour){
            if (direction == id_up){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_hour, id_up);
            }
            if (direction == id_down){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_hour, id_down);
            }
        }
        if (set_variable_variable_number == minute){
            if (direction == id_up){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_minute, id_up);
            }
            if (direction == id_down){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_minute, id_down);
            }
        }
        if (set_variable_variable_number == ampm){ 
            if (direction == id_up){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_ampm, id_up);
            }
            if (direction == id_down){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_ampm, id_down);
            }
        }
    }

    if (presenting_menu_level == ceramic){
        if (set_variable_variable_number == temp){
            if (direction == id_up){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_temp, id_up);
            }
            if (direction == id_down){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_temp, id_down);
            }
        }
        if (set_variable_variable_number == temp_fract){
            if (direction == id_up){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_temp_fract, id_up);
            }
            if (direction == id_down){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_temp_fract, id_down);
            }
        }
    }

    if (presenting_menu_level == humidifier){
        if (set_variable_variable_number == humidity){
            if (direction == id_up){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_humidity, id_up);
            }
            if (direction == id_down){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_humidity, id_down);
            }
        }
        if (set_variable_variable_number == humidity_fract){
            if (direction == id_up){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_humidity_fract, id_up);
            }
            if (direction == id_down){
                rollover(control_variable[set_variable_menu_level][set_variable_variable_number][set_variable_variable_section], rollover_humidity_fract, id_down);
            }
        }
    }
}
June 06, 2011
by Ralphxyz
Ralphxyz's Avatar

Well I am impressed, that is a lot of work. Afraid I can not really help with the program but I do see that you are including the UART library and initiating it but never using it. If you are not sending any output to a pc you probable coud save some space by removing the #include "../libnerdkits/uart.h".

Ralph

June 06, 2011
by delinthe
delinthe's Avatar

I use the uart library for serial debugging and will eventually use it for optional serial control, I want to whip up a quick and dirty ios and android control app that will communicate with a small local server on the host computer for control and datalogging.

I believe I stripped all of the serial debugging code out a few builds ago just for make it more manageable to maintain as it nearly doubled the size of the code base.

June 06, 2011
by Noter
Noter's Avatar

You can look at the generated assembly (*.ass) file to see the actual size of your image. Go to the bottom and see what the last address is then add the bytes in that last instruction and that will be the exact size.

June 06, 2011
by Ralphxyz
Ralphxyz's Avatar

Of course Paul that opens a whole new discussion (thread).

Those of us that continue to use the default Nerdkit Makefile haven't the slightest idea how to generate an assembly *.ass file.

Care to elucidate exactly how one might do this, also Avrfreaks references a .elf file often.

Ralph

June 06, 2011
by Noter
Noter's Avatar

Ok, it is already in the initialload makefile and probably all the others too.

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 COM2
LINKOBJECTS=../libnerdkits/delay.o ../libnerdkits/lcd.o ../libnerdkits/uart.o

all:    initialload-upload

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

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

initialload-upload: initialload.hex
    avrdude ${AVRDUDEFLAGS} -U flash:w:initialload.hex:a

Just delete lines 12 and 13 and the .ass will become part of the initialload.hex recipe and thus generated whenever the program is compiled. You may need to delete the .hex or save a change to the .c to be sure it is compiled again after changing the makefile.

June 06, 2011
by Ralphxyz
Ralphxyz's Avatar

Oh that makes sense, just "delete" lines 12 and 13 to make the assemble file.

I love it!!

Of course the nice fellows over at Avrfreaks have "suggested" (in rather strong terms) that I use MFile to make my Makefiles.

I have actually started playing around with it once I learned how to run a .tcl script on my Mac

(use Wish).

I have all sorts of options using MFile. MFile is part of the AVR-gcc install on Windows.

Ralph

June 06, 2011
by Noter
Noter's Avatar

Nothing beats learning how to write makefiles. Typical AVR makefiles are so simple that if you know how makefiles work you don't really need anything other than a text editor like programmers notepad.

Your time is better spent studying and learning the makefile syntax and rules. Otherwise as soon as MFile doesn't do 100% of what you want or need, you're stuck again.

June 07, 2011
by delinthe
delinthe's Avatar

I generated the .ass file and i see the last instruction as

00004ac2 <__stop_program>:
    4ac2:   ff cf           rjmp    .-2         ; 0x4ac2 <__stop_program>
June 07, 2011
by Noter
Noter's Avatar

If you select the scientific view in the windows calculator it will do hex arithmetic and conversion. 0x4ac2 + 0x2 = 0x4ac4 = 19,140 bytes. Definately time for an Atmega328.

June 07, 2011
by delinthe
delinthe's Avatar

I'm afraid that this program is going to wind up exceeding the capacity of the 328 in short order. I'm already at 19k and I still have to shoe horn actual device control code, and serial control/output into this thing. Luckily I don't think my program is very CPU intensive, just large.

For now i'll grab a 328 to continue on my way, but if i should find myself exceeding the memory capacity of the 328 what is a good option to go further? Is their another mega family AVR MCU I can change up to without having to entirely rework my project, or would it be easier to just go to a Master/Slave i2c bus scheme?

Any opinions would be appreciated, and thanks for the help determining my program size guys.

June 07, 2011
by bretm
bretm's Avatar

You can use the avr-size program to display the size of code and data sections in the hex file (which is actually an ELF file). I put avr-size in the makefile to display the size at the end of the build.

June 07, 2011
by Ralphxyz
Ralphxyz's Avatar

bretm, what is the syntax of using avr-size.

I tried just avr-size and got a missing a.out file so I added that but still do not get any output with size.

Ralph

June 07, 2011
by bretm
bretm's Avatar

I don't have access to my PC from here, but I would try

avr-size -A led_blink.hex
June 08, 2011
by Ralphxyz
Ralphxyz's Avatar

Thanks bretm, that did it.

Well it leads to more questions about what exactly "it" is.

avrdude: 7850 bytes of flash written
avrdude: verifying flash memory against stepper.hex:
avrdude: load data flash data from input file stepper.hex:
avrdude: input file stepper.hex auto detected as Intel Hex
avrdude: input file stepper.hex contains 7850 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.99s

avrdude: verifying ...
avrdude: 7850 bytes of flash verified

avrdude done.  Thank you.

avr-size -A stepper.hex
stepper.hex  :
section   size   addr
.sec1   7850      0
Total   7850

Avrdude is reporting the amount of flash written in the default Nerdkit Makefile.

The flash written and the size of the .hex are the same so have we had the amount of memory being used all along

or is the .hex size and flash written not the memory usage?

I've always wondered about "flash written".

I see there are more options for avr-size.

Ralph

June 08, 2011
by bretm
bretm's Avatar

Avrsize will also show the bytes allocated to sram and eeprom variables.

June 08, 2011
by bretm
bretm's Avatar

Avrsize will also show the bytes allocated to sram and eeprom variables.

June 08, 2011
by bretm
bretm's Avatar

Ok, here's the line from my makefile:

avr-size -C --mcu=m328p detonator.hex

Which results in

Program:    6328 bytes
(.text + .data + .bootloader)

Data:        273 bytes
(.data + .bss + .noinit)
June 08, 2011
by Ralphxyz
Ralphxyz's Avatar

Thanks again bretm,

I guess I need to update AVRDUDE or something my atmega328p device is unknown!!

avrdude done.  Thank you.

avr-size -C --mcu=m328p multi-tempsensor.hex
AVR Memory Usage
----------------
Device: Unknown

Program:       0 bytes
(.text + .data + .bootloader)

Data:          0 bytes
(.data + .bss + .noinit)

Another thing to note for others who might follow this thread and like me not know their way around a Makefile beyond using the default Nerdkit modified InitialLoad Makefile.

It is:

[TAB]avr-size -C --mcu=m328p $(ProjectName).hex

If you do not use a TAB you get the "Missing Separator" error.


avr-size -A $(ProjectName).hex reports the same as the default Nerdkit Makefile make output:

avrdude: verifying ...
avrdude: 11102 bytes of flash verified

Ralph

June 08, 2011
by esoderberg
esoderberg's Avatar

I see lots of questions and work put into Makefiles. Is there some advantage to using Makefiles vs just using AVR Studio where no separate Makefile is needed?

Eric

June 09, 2011
by Ralphxyz
Ralphxyz's Avatar

First thing Eric I do not believe many here use AVR Studio.

Once you know what you are doing you use custom Makefiles and have a distain for the Nerdkit's Initialload make file (which I have used for the past year).

It seems like a rite of passage sort of like when I was growing up growing hair on your chest which of course is no longer fashionable and again held in distain by some.

I have generated some of the Makfile discussions because I want to know the advantage.

I like bretm's ($ProjectName) usage and I had to move the -lm flag in order to use the Math.h library so I do use a custom Makefile as I have progressed in my learning and exposure. So I guess it is an evolutionary issue.

Try using the Math.h library in a AVR Studio project see if it compiles?

There is a lot of whining on Avrfreaks about Atmel misplacing the -lm flag.

Ralph

June 09, 2011
by bretm
bretm's Avatar

Honestly I think makefiles are overkill for Atmega-sized projects. Anything small enough to fit in 16k or 32k is going to compile instantly.

Makefiles are a big advantage with large projects with many dependencies between a large number of code files and header files. For those projects, it greatly reduces the time required to rebuild when just part of the project changes.

But for 8-bit microcontroller projects, I don't see the advantage to defining file dependencies, etc. The programs will always be tiny, by definition. A makefile is overkill. It becomes just a glorified batch file to string together the compiler, the uploader, and any other gadgets like avr-size that you might want. Just rebuild everything every time. A simple batch file will do just as well.

About the only advantage I see is that MAKE is supported on all the different platforms. Batch files aren't. But makefiles are not completely portable across platforms either because the avrdude parameters aren't portable (e.g. Windows has COMx, etc.)

Bottom line for me is use whatever works best for you. AVR Studio, make, shell scripts, whatever. For tiny projects like this it doesn't matter.

June 09, 2011
by Ralphxyz
Ralphxyz's Avatar

Thanks bretm, that is the perspective I have been looking for.

I knew the "potential" of using a full blown Makfile was way out of whack for these AVR projects I just had never seen anyone admit it.

Ralph

June 09, 2011
by Noter
Noter's Avatar

The make utility is just another of many tools employed to reach the end result. Just like any tool, if you know how to use it you'll realize greater benifit in it's application. I wouldn't want to type commands into a dos window or use batch files even for these little AVR projects when a utility like make is available.

June 10, 2011
by bretm
bretm's Avatar

What's the difference between typing the name of a batch file or typing "make"? I could even call my batch file "make.bat".

June 10, 2011
by Noter
Noter's Avatar

Well, I don't know how you do it but I don't type anything to invoke the make utility. From within Programmers Notepad tools menu I select make all, make clean, or program depending on which I want to do.

So, if you want to use a command file and you don't already know the command language, you have to learn it to go much beyond a simple list of commands. I'm no expert on the command language but I don't think it would be so easy to reproduce the functionality of the make utility and a person would probably be better off learning the makefile as it will carry forward to other platforms as well as more complex projects.

Take for example my lib make file. I never have to edit it to insert a new module into the library because it searches all the .c sources, compiles, and then inserts them into the library. I suppose you could do that with a more complex command file but why would you when you can use make?

It still comes down to one thing. That is to use any of the tools efficiently and effectively you have to learn them. I don't think there is any way around it.

June 10, 2011
by Ralphxyz
Ralphxyz's Avatar

Is Programmers NotePad making your Makefile or are you specifying an external Makefile.

Using AVR Studio it will automagically make the Makefile or you can specify an external Makefile.

Seems like using a batch file would force one to understand the Makefile deeper than using an auto builder.

Ralph

June 10, 2011
by bretm
bretm's Avatar

Huh? If you're using an editor that auto-generates makefiles or executes make automatically, then you wouldn't be using batch files. And if you're using batch files, you wouldn't need to understand anything about makefiles. Or am I missing something?

June 10, 2011
by Noter
Noter's Avatar

I create/edit makefiles as needed and the editor just invokes them for me so I don't have to type anything at the command line or even have a command window open. To open a source file in the editor I just double click it in a folder window and then I do everything to build and program the chip from within the editor.

Programmers Notepad was installed with win-avr and I've been using it since I started with the nerdkit. It's a pretty good editor with syntax highlighting so I keep using it. As far as I know it isn't able to automatically create a makefile like AVR Studio or MS Visual Studio, it's just an editor. I thought everyone used it but maybe it's only on the windows platform, I don't know for sure.

June 10, 2011
by Ralphxyz
Ralphxyz's Avatar

Bretm, I meant you would have to understand the syntax of compiling a project, the various linking etc.

I was think of the batch file essentially being a step by step replacement for the Makefile which is not literally true.

Of course I probable have not seen a batch file (make) in 15 -20 years.

I really understand Noter's perspective and I think I appreciate the power of the Makefile.

The first time I compiled a Linux build there was not yet a complete Makefile so most of the linking and building had to be done by hand. I think it took something like three hours to get a new complete build.

Of course I didn't have any idea what I was dong then, as now.

Ralph

June 11, 2011
by BobaMosfet
BobaMosfet's Avatar

First, don't trust everything on avrfreaks- I've seen a significant amount of misinformation perpetuated as fact. Enough that I haven't logged in there in over 1.5 years. Secondly, everything uses a makefile, including AVR Studio- AVR Studio just happens to be a nice IDE wrapper, to obfuscate that fact. Thirdly, I use AVR Studio all the time- why muck with a makefile, when the IDE is much nicer to work with? I only touch a makefile, if the IDE isn't generating one that work sufficiently.

Lastly, The OP's original code would be about 25% of it's size if they would use a few nested loops to handle all the repetitive stuff, and a few very small multi-dimension arrays.

As for memory usage- look at the bottom of the compile attempt; it will always tell you how big your data segment is- that's how it knows if it's too big or not. In most cases, unless you are accessing the declaration by address or pointer, don't uses 'const'. It wastes space. Use '#define'. This may be part of why you are running out of space.

BM

June 11, 2011
by BobaMosfet
BobaMosfet's Avatar

Your structures are not properly defined.

When you type 'struct myStruct', that is a forward declaration- not a type definition (definition of a new type). It has only one purpose- to tell the compiler the name of the struct being built, so that that struct can reference ITSELF as one of its own fields, before the variable type is fully defined (for example, creating a structure for a linked list would use this). This is quite possibly the most misused syntax in history.

Instead, you tell the compiler to create an entirely new type (just like int or char or byte- just an aggregate). If you don't do this correctly, you must keep scattering the keyword 'struct' everywhere because the compiler has no formal type declared!

Instead, do this:

typedef struct
    {
    int x_position;
    int y_position;
    int orientation;
    }cursor;

typedef struct
    {
    int item;
    int total_item;
    cursor arrow[8];
    }interface;

interface menu[11];  /* <-- Since type is real, no 'struct' keyword is required */

Another reason this is important is so that you can then take the type further. Let's say we wanted a pointer unique to our 'interface' struct. Here's the cool way to do it:

typedef struct
    {
    int item;
    int total_item;
    cursor arrow[8];
    }interface, *ifPtr;

Then if you need a variable of this type of pointer, instead of the more frequently seen method (interface *myIFPtr), you could just say this:

ifPtr  myIFPtr;

The other reason this is important, is because it helps the compiler do a better job at syntax checking.

BM

June 15, 2011
by delinthe
delinthe's Avatar

Boba, thank you for the coding critiques I'll go ahead and rework those structure problems you pointed out, and take a look at what i can do with some nested loops to cut down the programs length.

Post a Reply

Please log in to post a reply.

Did you know that NerdKits has been featured in the MIT Undergraduate Research Journal? Learn more...