123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667 |
- /*---------------------------------------------------------------*
- * Automotive GPS logger (C)ChaN, 2014 *
- * Modified k4be, 2022 *
- *---------------------------------------------------------------*/
- #include <avr/io.h>
- #include <avr/interrupt.h>
- #include <avr/sleep.h>
- #include <util/delay.h>
- #include <avr/pgmspace.h>
- #include <avr/wdt.h>
- #include <string.h>
- #include <stdlib.h>
- #include "main.h"
- #include "ff.h"
- #include "diskio.h"
- #include "uart0.h"
- #include "uart1.h"
- #include "xprintf.h"
- #include "stime.h"
- #include "ds18b20.h"
- #include "I2C.h"
- #include "expander.h"
- #include "HD44780-I2C.h"
- #include "gpx.h"
- #include "display.h"
- /*FUSES = {0xFF, 0x11, 0xFE};*/ /* ATmega644PA fuses: Low, High, Extended.
- This is the fuse settings for this project. The fuse bits will be included
- in the output hex file with program code. However some old flash programmers
- cannot load the fuse bits from hex file. If it is the case, remove this line
- and use these values to program the fuse bits. */
- volatile struct system_s System;
- FATFS Fatfs; /* File system object for each logical drive */
- FIL gps_log; /* File object */
- FIL system_log; /* System log file */
- char Line[100]; /* Line buffer */
- time_t utc; /* current time */
- struct location_s location;
- void start_bootloader(void) {
- typedef void (*do_reboot_t)(void);
- const do_reboot_t do_reboot = (do_reboot_t)((FLASHEND - 1023) >> 1);
- cli();
- // close_files(0); // FIXME not working
- LCD_Clear(); /* Do not call display.c here! */
- LCD_GoTo(0,0);
- LCD_WriteTextP(PSTR("Aktualizacja"));
- LCD_GoTo(8,1);
- LCD_WriteTextP(PSTR("softu..."));
- TCCR0A = TCCR1A = TCCR2A = 0; // make sure interrupts are off and timers are reset.
- do_reboot();
- wdt_enable(WDTO_15MS);
- while(1);
- }
- /*---------------------------------------------------------*/
- /* 100Hz timer interrupt generated by OC1A */
- /*---------------------------------------------------------*/
- ISR(TIMER1_COMPA_vect)
- {
- static WORD ivt_sync;
- // static BYTE led;
- static unsigned int power_sw;
- unsigned int *volatile ctimer;
- unsigned char i;
-
- for(i=0; i<sizeof(System.timers)/sizeof(unsigned int); i++){ // decrement every variable from timers struct unless it's already zero
- ctimer = ((unsigned int *)&System.timers) + i;
- if(*ctimer)
- (*ctimer)--;
- }
- /* Sync interval */
- if (IVT_SYNC && ++ivt_sync >= IVT_SYNC * 100) {
- ivt_sync = 0;
- FLAGS |= F_SYNC;
- }
- /* Green LED drive */
- /* if (FLAGS & F_POW) {
- if ((FLAGS & F_GPSOK) || (++led & 0x20)) {
- LEDG_ON();
- } else {
- LEDG_OFF();
- }
- } else {
- LEDG_OFF();
- }*/
- /* MMC/SD timer procedure */
- disk_timerproc();
-
- /* Switch off */
- if (POWER_SW_PRESSED()) {
- if (++power_sw == POWER_SW_TIME){
- FLAGS |= F_POWEROFF;
- power_sw++;
- }
- } else {
- power_sw = 0;
- }
-
- if (uart1_test() && uart1_get() == '0' && uart1_get() == ' '){
- LEDB_ON();
- start_bootloader();
- LEDR_ON();
- }
- }
- /* Power supply monitor */
- ISR(ADC_vect)
- {
- float bat_volt;
- static unsigned int adc_buf;
- static unsigned char adc_cnt;
- static BYTE lvt;
-
- adc_buf += ADC;
- if(++adc_cnt < 15)
- return;
- bat_volt = (float)adc_buf/(float)(adc_cnt)/VI_MULT;
- adc_buf = 0;
- adc_cnt = 0;
-
- System.bat_volt = bat_volt;
- if (FLAGS & F_LVD) {
- if (bat_volt > VI_LVH) {
- FLAGS &= ~F_LVD;
- lvt = 0;
- }
- } else {
- if (bat_volt < VI_LVL) {
- if (++lvt >= 3)
- FLAGS |= F_LVD;
- } else {
- lvt = 0;
- }
- }
- }
- /*---------------------------------------------------------*/
- /* User Provided Timer Function for FatFs module */
- /*---------------------------------------------------------*/
- DWORD get_fattime (void)
- {
- struct tm *stm;
- time_t localtime = utc + LOCALDIFF;
- stm = gmtime(&localtime);
- return (DWORD)(stm->tm_year - 80) << 25 |
- (DWORD)(stm->tm_mon + 1) << 21 |
- (DWORD)stm->tm_mday << 16 |
- (DWORD)stm->tm_hour << 11 |
- (DWORD)stm->tm_min << 5 |
- (DWORD)stm->tm_sec >> 1;
- }
- static void sleep(void) {
- set_sleep_mode(SLEEP_MODE_IDLE);
- sleep_enable();
- sleep_cpu();
- sleep_disable();
- }
- /*----------------------------------------------------*/
- /* Get a line received from GPS module */
- /*----------------------------------------------------*/
- UINT get_line ( /* 0:Brownout or timeout, >0: Number of bytes received. */
- char *buff,
- UINT sz_buf
- )
- {
- char c;
- UINT i = 0;
- set_timer(recv_timeout, 1000);
- for (;;) {
- wdt_reset();
- if (FLAGS & (F_LVD | F_POWEROFF))
- return 0; /* A brownout is detected */
- if (timer_expired(recv_timeout))
- return 0; /* timeout; continue the main loop */
- if (!uart0_test()) {
- sleep();
- continue;
- }
- c = (char)uart0_get();
- uart1_put(c);
- if (i == 0 && c != '$')
- continue; /* Find start of line */
- buff[i++] = c;
- if (c == '\n')
- break; /* EOL */
- if (i >= sz_buf)
- i = 0; /* Buffer overflow (abort this line) */
- }
- return i;
- }
- /*--------------------------------------------------------------------------*/
- /* Controls */
- void beep (UINT len, BYTE cnt)
- {
- while (cnt--) {
- BEEP_ON();
- set_timer(beep, len);
- while(!timer_expired(beep)) {};
- BEEP_OFF();
- set_timer(beep, len);
- while(!timer_expired(beep)) {};
- }
- }
- /* Make ISO time string */
- const char *get_iso_time(time_t time) {
- static char output[32];
- struct tm *ct = gmtime(&time);
- xsprintf(output, PSTR("%4u-%02u-%02uT%02u:%02u:%02u.000Z"), ct->tm_year+1900, ct->tm_mon+1, ct->tm_mday, ct->tm_hour, ct->tm_min, ct->tm_sec);
- return output;
- }
- /* Compare sentence header string */
- BYTE gp_comp (const char *str1, __flash const char *str2)
- {
- char c;
- do {
- c = pgm_read_byte(str2++);
- } while (c && c == *str1++);
- return c;
- }
- #define FIELD_BUF_LEN 32
- /* Get a column item */
- static
- const char* gp_col ( /* Returns pointer to the item (returns a NULL when not found) */
- const char* buf, /* Pointer to the sentence */
- BYTE col /* Column number (0 is the 1st item) */
- ) {
- BYTE c;
- static char field_buf[FIELD_BUF_LEN];
- unsigned char length = 0;
- while (col) {
- do {
- c = *buf++;
- if (c <= ' ') return NULL;
- } while (c != ',');
- col--;
- }
- while (*buf && *buf != ',' && length < FIELD_BUF_LEN-1) {
- field_buf[length++] = *buf++;
- }
- field_buf[length] = '\0';
- return field_buf;
- }
- static
- BYTE gp_val2 (
- const char *db
- )
- {
- BYTE n, m;
- n = db[0] - '0';
- if (n >= 10)
- return 0;
- m = db[1] - '0';
- if (m >= 10)
- return 0;
- return n * 10 + m;
- }
- static
- UINT gp_val3 (
- const char *db
- )
- {
- BYTE n, m, l;
- n = db[0] - '0';
- if (n >= 10)
- return 0;
- m = db[1] - '0';
- if (m >= 10)
- return 0;
- l = db[2] - '0';
- if (l >= 10)
- return 0;
- return n * 100 + m * 10 + l;
- }
- static time_t gp_rmc_parse(const char *str) {
- const char *p;
- struct tm tmc;
- p = gp_col(str, 1); /* Get h:m:s */
- if (!p)
- return 0;
- tmc.tm_hour = gp_val2(p);
- tmc.tm_min = gp_val2(p+2);
- tmc.tm_sec = gp_val2(p+4);
- p = gp_col(str, 9); /* Get y:m:d */
- if (!p)
- return 0;
- tmc.tm_mday = gp_val2(p);
- tmc.tm_mon = gp_val2(p+2) - 1;
- tmc.tm_year = gp_val2(p+4) + 100;
- utc = mktime(&tmc); /* Check time validity */
- if (utc == -1)
- return 0;
- p = gp_col(str, 2); /* Get status */
- if (!p || *p != 'A') {
- FLAGS &= ~F_GPSOK;
- return 0; /* Return 0 even is time is valid (comes from module's internal RTC) */
- }
- FLAGS |= F_GPSOK;
-
- return utc;
- }
- static void gp_gga_parse(const char *str) {
- const char *p;
- double tmp;
- /* check validity */
- p = gp_col(str, 6);
- if (*p == '0')
- return;
-
- System.location_valid = 1;
- /* parse location */
- p = gp_col(str, 2); /* latitude */
- location.lat = gp_val2(p); /* degrees */
- p += 2;
- xatof(&p, &tmp); /* minutes */
- tmp /= 60; /* convert minutes to degrees */
- location.lat += tmp;
- p = gp_col(str, 3); /* N/S */
- if (*p != 'N')
- location.lat = -location.lat;
-
- p = gp_col(str, 4); /* longitude */
- location.lon = gp_val3(p); /* degrees */
- p += 3;
- xatof(&p, &tmp); /* minutes */
- tmp /= 60; /* convert minutes to degrees */
- location.lon += tmp;
- p = gp_col(str, 5); /* E/W */
- if (*p != 'E')
- location.lon = -location.lon;
- p = gp_col(str, 7); /* satellites used */
- System.satellites_used = atoi(p);
-
- p = gp_col(str, 9); /* MSL altitude */
- xatof(&p, &tmp);
- location.alt = tmp;
- location.time = utc; /* parsed from RMC */
- }
- static time_t gps_parse(const char *str) { /* Get all required data from NMEA sentences */
- if (!gp_comp(str, PSTR("$GPRMC"))) {
- return gp_rmc_parse(str);
- }
- if (!gp_comp(str, PSTR("$GPGGA"))) {
- gp_gga_parse(str);
- return 0;
- }
- return 0;
- }
- #define LOG_SIZE 300
- struct {
- char buf[LOG_SIZE+1];
- unsigned int len;
- } logbuf;
- void log_put(char c){
- UINT bw;
- uart1_put(c);
- logbuf.buf[logbuf.len++] = c;
- if (logbuf.len >= LOG_SIZE && (FLAGS & F_FILEOPEN)) {
- if(!f_write(&system_log, logbuf.buf, logbuf.len, &bw))
- logbuf.len = 0;
- }
- if (logbuf.len > LOG_SIZE) {
- logbuf.len--;
- }
- }
- static
- void ioinit (void)
- {
- wdt_enable(WDTO_4S);
- MCUSR = 0;
- POWER_ON_DDR |= POWER_ON;
- PORTA |= POWER_ON;
-
- BUZZER_DDR |= BUZZER;
- GPS_DIS_DDR |= GPS_DIS;
- LEDR_DDR |= LEDR;
- OCR1A = F_CPU/8/100-1; /* Timer1: 100Hz interval (OC1A) */
- TCCR1B = _BV(WGM12) | _BV(CS11);
- TIMSK1 = _BV(OCIE1A); /* Enable TC1.oca interrupt */
- /* ADC */
- // ADMUX = 0;
- ADMUX = 1; // FIXME only testing battery voltage
- ADCSRA = _BV(ADEN)|_BV(ADATE)|_BV(ADIE)|_BV(ADPS2)|_BV(ADPS1)|_BV(ADPS0);
-
- /* uart1 (debug) */
- uart1_init();
-
- I2C_init();
- expander_init(0, 0x00, 0x00); /* all as outputs */
- LCD_Initialize();
- LCD_Clear();
- sei();
-
- ADCSRA |= _BV(ADSC);
-
- /* unused pins */
- DDRA |= _BV(PA2) | _BV(PA4) | _BV(PA5);
- PRR0 |= _BV(PRTIM2) | _BV(PRTIM0);
- ACSR = _BV(ACD); /* Disable analog comp */
- }
- void close_files(unsigned char flush_logs) {
- UINT bw;
- if (FLAGS & F_FILEOPEN) {
- if (f_close(&gps_log))
- System.status = STATUS_FILE_CLOSE_ERROR;
- if (gpx_close(&gpx_file))
- System.status = STATUS_FILE_CLOSE_ERROR;
- if (flush_logs && logbuf.len && !f_write(&system_log, logbuf.buf, logbuf.len, &bw)) {
- logbuf.len = 0;
- }
- if (f_close(&system_log))
- System.status = STATUS_FILE_CLOSE_ERROR;
- xputs_P(PSTR("File closed\r\n"));
- display_state(DISPLAY_STATE_FILE_CLOSED);
- }
- FLAGS &= ~F_FILEOPEN;
- disk_ioctl(0, CTRL_POWER, 0);
- }
- __flash const char __open_msg[] = "Open %s\r\n";
- /*-----------------------------------------------------------------------*/
- /* Main */
- /*-----------------------------------------------------------------------*/
- int main (void)
- {
- UINT bw, len;
- static struct tm ct;
- time_t tmp_utc, localtime;
- FRESULT res;
- unsigned char prev_status;
- ioinit();
- xdev_out(log_put);
- xputs_P(PSTR("STARTUP\r\n"));
- display_state(DISPLAY_STATE_STARTUP);
-
- for (;;) {
- wdt_reset();
- if (FLAGS & (F_POWEROFF | F_LVD)) {
- xputs_P(PSTR("POWEROFF\r\n"));
- display_state(DISPLAY_STATE_POWEROFF);
- if (FLAGS & F_LVD) {
- display_state(DISPLAY_STATE_POWEROFF_LOWBAT);
- _delay_ms(500);
- }
- POWEROFF();
- while (POWER_SW_PRESSED());
- _delay_ms(2000); /* wait for switch off */
- FLAGS &= ~F_POWEROFF; /* restart if power is not lost */
- POWERON();
- xputs_P(PSTR("RESTART\r\n"));
- }
- display_state(DISPLAY_STATE_START_MESSAGE);
- xprintf(PSTR("LOOP err=%u\r\n"), (unsigned int)System.status);
- utc = 0;
- localtime = 0;
- prev_status = System.status;
- System.status = STATUS_NO_POWER;
- beep(250, prev_status); /* Error beep */
- /* Wait for supply voltage stabled */
- _delay_ms(500);
- if (FLAGS & F_LVD)
- continue;
- /* report error here ( prev_status) */
- if (disk_status(0) & STA_NODISK) {
- System.status = STATUS_NO_DISK;
- continue;
- }
- res = f_mount(&Fatfs, "", 1);
- if (res != FR_OK) {
- xprintf(PSTR("FS error %u\r\n"), res);
- System.status = STATUS_DISK_ERROR;
- continue;
- }
- System.status = STATUS_NO_GPS;
- xputs(PSTR("FS Ok\r\n"));
- display_state(DISPLAY_STATE_CARD_OK);
- beep(50, 1); /* 1 beep */
- /* Initialize GPS receiver */
- GPS_ON(); /* GPS power on */
- FLAGS |= F_POW;
- _delay_ms(300); /* Delay */
- uart0_init(); /* Enable UART */
- // xfprintf(uart0_put, PSTR("$PSRF106,21*0F\r\n")); /* Send initialization command (depends on the receiver) */
- for (;;) { /* main loop */
- wdt_reset();
- display_refresh(DISPLAY_STATE_NO_CHANGE);
- gettemp();
- if (!(FLAGS & F_GPSOK))
- xputs_P(PSTR("Waiting for GPS\r\n"));
- len = get_line(Line, sizeof Line); /* Receive a line from GPS receiver */
- if (!len){
- if (FLAGS & (F_LVD | F_POWEROFF))
- break; /* brownout */
- continue;
- }
- tmp_utc = gps_parse(Line);
- if (tmp_utc) {
- localtime = tmp_utc + LOCALDIFF * 3600L; /* Local time */
- ct = *gmtime(&localtime);
- if (timer_expired(system_log)) {
- set_timer(system_log, 5000);
- xprintf(PSTR("Time: %u.%02u.%04u %u:%02u:%02u\r\n"), ct.tm_mday, ct.tm_mon + 1, ct.tm_year+1900, ct.tm_hour, ct.tm_min, ct.tm_sec);
- xprintf(PSTR("Bat volt: %.3f\r\n"), System.bat_volt);
- if (System.temperature_ok)
- xprintf(PSTR("Temp: %.2f\r\n"), System.temperature);
- else
- xputs_P(PSTR("Temperature unknown\r\n"));
- }
- LEDG_ON();
- _delay_ms(2);
- LEDG_OFF();
- }
- if (FLAGS & F_FILEOPEN) {
- f_write(&gps_log, Line, len, &bw);
- if (bw != len) {
- System.status = STATUS_FILE_WRITE_ERROR;
- break;
- }
- if (System.location_valid) /* a new point */
- gpx_process_point(&location, &gpx_file);
- wdt_reset();
- if (FLAGS & F_SYNC) {
- if (f_sync(&gps_log)) {
- System.status = STATUS_FILE_SYNC_ERROR;
- break;
- }
- FLAGS &= ~F_SYNC;
- }
- }
- System.location_valid = 0;
- if (localtime && !(FLAGS & F_FILEOPEN)) {
- xsprintf(Line, PSTR("%04u-%02u-%02u_%02u-%02u-%02u.LOG"), ct.tm_year+1900, ct.tm_mon + 1, ct.tm_mday, ct.tm_hour, ct.tm_min, ct.tm_sec);
- xprintf(__open_msg, Line);
- if (f_open(&gps_log, Line, FA_WRITE | FA_OPEN_ALWAYS) /* Open log file */
- || f_lseek(&gps_log, f_size(&gps_log)) /* Append mode */
- || f_write(&gps_log, "\r\n", 2, &bw)) /* Put a blank line as start marker */
- {
- System.status = STATUS_FILE_OPEN_ERROR;
- break; /* Failed to start logging */
- }
- wdt_reset();
- xsprintf(Line, PSTR("%04u-%02u-%02u_%02u-%02u-%02u.GPX"), ct.tm_year+1900, ct.tm_mon + 1, ct.tm_mday, ct.tm_hour, ct.tm_min, ct.tm_sec);
- xprintf(__open_msg, Line);
- if (f_open(&gpx_file, Line, FA_WRITE | FA_OPEN_ALWAYS) /* Open log file */
- || f_lseek(&gpx_file, f_size(&gpx_file)) /* Append mode */
- || gpx_init(&gpx_file)) /* Put a blank line as start marker */
- {
- f_close(&gpx_file);
- System.status = STATUS_FILE_OPEN_ERROR;
- break; /* Failed to start logging */
- }
- wdt_reset();
- xsprintf(Line, PSTR("%04u-%02u-%02u_%02u-%02u-%02u-SYSTEM.LOG"), ct.tm_year+1900, ct.tm_mon + 1, ct.tm_mday, ct.tm_hour, ct.tm_min, ct.tm_sec);
- xprintf(__open_msg, Line);
- if (f_open(&system_log, Line, FA_WRITE | FA_OPEN_ALWAYS) /* Open log file */
- || f_lseek(&system_log, f_size(&system_log)) /* Append mode */
- || f_write(&system_log, "\r\n", 2, &bw)) /* Put a blank line as start marker */
- {
- f_close(&gpx_file);
- f_close(&gps_log);
- System.status = STATUS_FILE_OPEN_ERROR;
- break; /* Failed to start logging */
- }
- wdt_reset();
- FLAGS |= F_FILEOPEN;
- System.status = STATUS_OK;
- beep(50, 2); /* Two beeps. Start logging. */
- display_state(DISPLAY_STATE_FILE_OPEN);
- continue;
- }
- }
- /* Stop GPS receiver */
- uart0_deinit();
- GPS_OFF();
- FLAGS &= ~F_POW;
- /* Close file */
- close_files(1);
- if (System.status != STATUS_FILE_CLOSE_ERROR)
- beep(500, 1); /* Long beep on file close succeeded */
- }
- }
|