Logomatic

While not strictly a GPS topic, this seems to be the only currently available place for this.

I am trying to couple a Serial SD Logomatic to an EM-406 GPS and an analog output from a magnetometer.

Unfortunately, the Logomatic will record ADC or ASCII but not both.

I guess the firmware needs to be modified to switch between the two, preferably by means of the LOGCON.TXT file. I guess this should be doable, but would need the assistance of someone who can rejig the C Source Code. Any volunteers?

Secondly, I have noticed a small abberation in the operation. When setting the Frequency (of samples in ADC mode), if using numbers 1-9, they must be in the format 01-09 or there is an error of about 75 times (ie if set to 3, the Logamatic will record 3 readings every 75 seconds, not every second).

Ray

I’m trying to do the same thing as you Ray with some different analog sensors.

I’ve got a Logomatic and I just ordered the EM-406 GPS module. I’m kinda a hack at C, but I’m going to give it a go anyway. I’ll post back anything I learn, but if someone has more expertise please drop hints!

Hi Colin,

Are you making any headway with deciphering the code?

I spoke to SFE about them upgrading the code, thinking it would be a worthwhile enhancement and they could sell lots more Logomatics, but they instead offered to do it for me at US$75 per hour.

I don’t think so!

Some further information on the Logomatic which is probably obvious to most people but wasn’t to me:

1. In ADC mode, minor hiccup when setting FREQUENCY as mentioned above in earlier post. Reported to Sparkfun but not yet fixed.

2. Cannot record ADC and UART, or even alternate between them. It is one or the other.

3. Both UART logging modes will record a massive amount of data from a GPS. It records everything thrown at it. This can amount to about 1 kB/second or about 2 weeks to fill a 1 GB SD card. You cannot use the FREQUENCY command to only log periodically. Your only hope for longer logging is to turn off some of the GPS sentences before hooking up to the Logomatic.

4. MODE 1 UART logging logs a set number of characters. In the GPS example, using $ as the trigger, as the GPS sentences are generally of varying length, you will either not record the entire sentence or the sentences will run together. Ideally, MODE 1 should be triggered by a specified character and terminated by CRLF, or the trigger character could trigger each new frame irrespective of frame size.

Hopefully the software guys at Sparkfun will take some of these comments on board and further improve an already impressive product.

Ray

I have the same concerns and I have been going over the C code. I’m hoping to find a way to make the unit more configurable. In addition to your items 1 - 4, there ought to be a way to put it sleep during intervals that it’s not recording data.

I have just received my unit and I haven’t had a chance to work on it. I will keep you posted.

David

If anyone from SFE reads this, can they put in their ten cents worth and let us know the state of play with fixing/enhancing the Logomatic firmware.

Thanks,

Ray

It’s been nearly half a year now since I brought up the issue of limitations and a bug in the LOGOMATIC firmware, and apart from the initial acknowledgement, there seems to have been nothing done.

Is the LOGOMATIC a dead product? If so, it seems a shame as it could so easily be the solution to many an engineers prayers if it only had the ability to log UART (for GPS) and analog alternately.

In many of my applications, I need to log analog data, but also need a periodic position and time update from a GPS. Generally, most data logging needs to have a time reference to be useful.

I believe that the LOGOMATIC is capable of this as it is, with nothing more than a firmware update.

Ray

Hello,

Is there any update on the Logomatic firmware being able to record serial + ADC inputs? I would be happy to spend time developing with someone …!

Please get in touch

Yep I am in the same boat with the Logomatic V2. In someways I wish I would have seen this post before I bought it. But thats neither nor there.

I did have one idea to potentially solve the problem and that is analyzing the package tracker main file and the Logomatic main file and then a kind of a merge-diff.

THe package tracker is basically a Logomatic with the upgraded software that alot of folks have been asking for.

Really seems weird Spark Fun hasn’t officially responded. I used to manage the forums at a startup for a while, and we knew for company image we had to have an official answer for questions like these. Clearly the product isn’t dead as they have released v2.

Anyways, if I do figure something out I will let folks know. :wink:

I had the same need for my Logomatic, where I have a GPS and six axes of inertial measurements, and need to record both.

Here is my heavily modified main.c. It expects the GPS to be in 8N1 SIRF binary, attached to UART1 (not 0), baud rate specified as before in LOGCON.TXT, and it records everything in SIRF binary, including the ADCs. I made up two packets to describe the ADCs and one to describe the Logomatic processor load and timing.

ADC Columns

0xA0A2 - packet start

0x00XX - packet length, 1byte+(2 bytes*number of ADCs active)

0x2A - packet type

For each ADC

  • 1 byte pin number as labeled on the Logomatic board ( 1-8 )

  • 1 byte pin number as seen by the LPC2148 (1.3 etc) ADC ‘side’ 1.X is in high 4 bits, ADC ‘channel’ X.3 is in the low 4 bits, so 1.3 is 0x13

0xXXXX - checksum, same algorithm as SIRF

0xB0B3 - packet end

ADC readout

0xA0A2 - packet start

0x00XX - packet length, 9bytes+(2 bytes*number of ADCs active)

0x2C - packet type

4 byte timestamp before reading ADCs (60MHz counter, resets to 0 each second, not synced with GPS)

For each ADC

  • 2 byte ADC reading, 0-1023

4 byte timestamp after reading ADCs

0xXXXX - checksum, same algorithm as SIRF

0xB0B3 - packet end

Logomatic time and load - 1 per second

0xA0A2 - packet start

0x00XX - packet length, 16 bytes

0x15 - Packet type

2 byte year

1 byte month

1 byte day of month

1 byte hour

1 byte minute

1 byte second (above calendar set to 0000/00/00 00:00:00 at reset)

4 byte time under load in last second (divide by 60M for percentage)

0xXXXX - checksum

0xB0B3 - message end

Software can send SIRF binary commands to GPS, see ‘message’ variable. The software puts on the header, calculates the checksum, and puts on the footer, all automatically. Just encode the payload. It also records all commands sent to the GPS in the log, again as a packet, which is ok since command and received packets’ message IDs don’t overlap. You may even be able to send NMEA commands with this, since if the receiver is in NMEA mode, the SIRF header and footer will just be interpreted as line noise. However, this program expects the data received from the GPS to be in SIRF mode, and probably won’t handle NMEA properly.

This program works with UART1, not 0! It was more convenient on my board to use, since I had a row of headers already soldered to it.

This saves all the data on the card, as we have come to expect. However, since it is all in SIRF binary, a special program is needed in order to interpret it. I have one written in Java, which this margin is too narrow to contain.

To compile this, get the Logomatic V2 firmware packet from Sparkfun, make sure you can compile it according to the instructions, then replace the given main/main.c with the code below and recompile Follow the remaining instructions to install the firmware. I do it by USB bootloader.

The Logomatic hardware is pretty flexible, as the Sparkfun guys say. It’s a full blown computer, not just a logger. If this code doesn’t do what you need, hopefully you can modify it. After all, we all love to tinker, right?

I have successfully run this with my GPS at 57600baud and ADC readout of around ~100Hz.

This is posted with no guarantees that it will do anything, helpful or harmful. It will probably work (it does for me) but if it makes your GPS explode and burns down your house, it’s not my fault.

The code was originally the Logomatic V2 firmware, but I think I have changed it enough to call my own. Anyone may use and/or modify it for any purpose, including commercial purposes.

/*********************************************************************************
 * Logomatic Version Kwan Firmware
 * Sparkfun Electronics 2008
 * Kwan Systems 2009
 * ******************************************************************************/

/*******************************************************
 *          Header Files
 ******************************************************/
#include <stdio.h>
#include <string.h>
#include "LPC21xx.h"

#include "serial.h"

//Needed for main function calls
#include "main_msc.h"
#include "fat16.h"
#include "armVIC.h"
#include "itoa.h"
#include "rootdir.h"
#include "sd_raw.h"


/*******************************************************
 *          Global Variables
 ******************************************************/

#define ON  1
#define OFF  0

#define stringSize 512
char RX_array1[stringSize];
char RX_array2[stringSize];
char log_array1 = 0;
char log_array2 = 0;
short RX_in = 0;
char get_frame = 0;

struct fat16_file_struct* handle;
struct fat16_file_struct * fd;
char stringBuf[256];

int nChannels = 0;

// Default Settings
static int baudcode = 4;
static int freq = 100;
char channelActive[8];

static char fn[]="LOK9_XXX.bin";

/*******************************************************
 *      Function Declarations
 ******************************************************/

void setupPins(void);

void setup_uart0(int newbaud, char want_ints);
void setup_uart1(int newbaud, char want_ints);

void startRecordUART(void);
void startRecordADC(void);
void setup(void);
void loop(void);
void blinklock(void);

void readLogCon(void);
void stat(int statnum, int onoff);

//void feed(void);

static void UART0ISR(void); //__attribute__ ((interrupt("IRQ")));
static void UART1ISR(void); //__attribute__ ((interrupt("IRQ")));
static void timerISR(void); //__attribute__ ((interrupt("IRQ")));

void FIQ_Routine(void) __attribute__ ((interrupt("FIQ")));
void SWI_Routine(void) __attribute__ ((interrupt("SWI")));
void UNDEF_Routine(void) __attribute__ ((interrupt("UNDEF")));

void fat_initialize(void);

void delay_ms(int count);


/*******************************************************
 *            MAIN
 * Arduino-style structure here
 ******************************************************/

int main (void) {
  setup();
  for(;;) loop();
}

void setup() {
  int i;
  int count = 0;
  
  //Turn on timer 1, 60MHz by default  
  T1TCR=0x01;

  //Turn on the clock, using the 32kHz clock crystal
  CCR=0x13;
  YEAR=0;
  MONTH=0;
  DOM=0;
  DOW=0;
  DOY=0;
  HOUR=0;
  MIN=0;
  SEC=0;
  CCR=0x11;
  
  enableFIQ();
  
  setupPins();
  
  fat_initialize();    

  // Flash Status Lights
  for(i = 0; i < 5; i++) {
    stat(0,ON);
    delay_ms(50);
    stat(0,OFF);
    stat(1,ON);
    delay_ms(50);
    stat(1,OFF);
  }
  
  count=0;
  do {
    if(count >=99) {
      blinklock();
    }
    fn[5]=count/100+'0';
    fn[6]=(count%100)/10+'0';
    fn[7]=(count%10)+'0';
    count++;
  } while(root_file_exists(fn));
  
  handle = root_open_new(fn);

  sd_raw_sync();  
    
  readLogCon();

  startRecordUART();
  //Only set up the ADC if some channels are to be recorded  
  if(nChannels>0) startRecordADC(); 
}

/*******************************************************
 *          Initialize
 ******************************************************/

#define PLOCK 0x400

void setupPins(void) {
  //  C    F    3    5    1    5    0    5
  // 1100 1111 0011 0101 0001 0101 0000 0101
  //  F E  D C  B A  9 8  7 6  5 4  3 2  1 0 
  // Pin 0.00 - 01 - TxD0
  // Pin 0.01 - 01 - RxD0
  // Pin 0.02 - 00 - GPIO 0.02
  // Pin 0.03 - 00 - GPIO 0.03
  // Pin 0.04 - 01 - SCK0
  // Pin 0.05 - 01 - MISO0
  // Pin 0.06 - 01 - MOSI0
  // Pin 0.07 - 00 - GPIO 0.07
  // Pin 0.08 - 01 - TxD1
  // Pin 0.09 - 01 - RxD1
  // Pin 0.10 - 11 - AD1.2
  // Pin 0.11 - 00 - GPIO 0.11
  // Pin 0.12 - 11 - AD1.3
  // Pin 0.13 - 11 - AD1.4
  // Pin 0.14 - 00 - GPIO 0.14
  // Pin 0.15 - 11 - AD1.5
  PINSEL0 = 0xCF351505;

  //  1    5    4    4    1    8    0    1
  // 0001 0101 0100 0100 0001 1000 0000 0001
  //  F E  D C  B A  9 8  7 6  5 4  3 2  1 0 
  // Pin 0.16 0 - 01 - EINT0
  // Pin 0.17 1 - 00 - GPIO 0.17
  // Pin 0.18 2 - 00 - GPIO 0.18
  // Pin 0.19 3 - 00 - GPIO 0.19
  // Pin 0.20 4 - 00 - GPIO 0.20
  // Pin 0.21 5 - 10 - AD1.6
  // Pin 0.22 6 - 01 - AD1.7
  // Pin 0.23 7 - 00 - GPIO 0.23
  // Pin 0.24 8 - 00 - Reserved
  // Pin 0.25 9 - 01 - GPIO 0.25
  // Pin 0.26 A - 00 - Reserved
  // Pin 0.27 B - 01 - Reserved
  // Pin 0.28 C - 01 - AD0.1
  // Pin 0.29 D - 01 - AD0.2
  // Pin 0.30 E - 01 - AD0.3
  // Pin 0.31 F - 00 - GPO only
  PINSEL1 = 0x15441801;
  // Pin 2,7,11 set to out
  IODIR0 |= 0x00000884;
  IOSET0 = 0x00000080;

  S0SPCR = 0x08;  // SPI clk to be pclk/8
  S0SPCR = 0x30;  // master, msb, first clk edge, active high, no ints
}

void writeRX(char in) {
  if(RX_in < 512) {
  RX_array1[RX_in] = in;
  RX_in++;
  if(RX_in == 512) log_array1 = 1;
  } else if(RX_in >= 512) {
    RX_array2[RX_in-512] = in;
    RX_in++;
    if(RX_in == 1024) {
      log_array2 = 1;
      RX_in = 0;
    }
  }
}

void writeRXHex(char in) {
  char bit1=(in>>4) & 0x0F;
  if(bit1<10) {
    bit1+=48;
  } else {
    bit1+=55;
  }
  writeRX(bit1);
  bit1=(in & 0x0F);
  if(bit1<10) {
    bit1+=48;
  } else {
    bit1+=55;
  }
  writeRX(bit1);
}

static char sirfIn[256];
static int sirfInPtr=0;
static int GPSLight=0;
static int lockOn=0;
static int loops=0;

static void parseSirf() {
  if(sirfIn[4]==0x29) {
    //Check if we have a position
    lockOn=((sirfIn[6]==0) & (sirfIn[5]==0));
	  if(lockOn) GPSLight=1;
  } else if(!lockOn && sirfIn[4]==4) {
    //Check if we have at least one sat
    if(sirfIn[12]>0) {
	    GPSLight=1-GPSLight;
  	} else {
	    GPSLight=0;
	  }
  }
  stat(0,GPSLight);
}

static void addSirf(char temp) {
  sirfIn[sirfInPtr]=temp;
  sirfInPtr++;
  if((sirfInPtr>0 && sirfIn[sirfInPtr-1]==0xB3 && sirfIn[sirfInPtr-2]==0xB0) || sirfInPtr>=256) {
    for(int i=0;i<sirfInPtr;i++) writeRX(sirfIn[i]);
	  parseSirf();
    sirfInPtr=0;
  }
}

static int sendRX(char out, int checksum) {
  writeRX(out);
  checksum+=out;
  checksum &= 0x7FFF;
  return checksum;
}

static int sendcmd(char out, int checksum) {
  putc_serial1(out);
  return sendRX(out,checksum);
}

static void UART0ISR(void) {
  char temp=U0RBR;
  addSirf(temp);
  temp = U0IIR; // Have to read this to clear the interrupt 
  VICVectAddr = 0;
}

static void UART1ISR(void) {
  char temp=U1RBR;
  addSirf(temp);
  temp = U1IIR; // Have to read this to clear the interrupt 
  VICVectAddr = 0;
}

static char adcSide[]={0,0,0,0,1,1,1,1};
static char adcChan[]={3,2,1,4,7,6,2,3};
static volatile char timeForADC=0;

static void timerISR(void) {
  T0IR = 1; // reset TMR0 interrupt
  timeForADC=1;
  VICVectAddr= 0;
}

void readADC() {
  int temp = 0, adcValue = 0, ind = 0;
  int j;
  
  timeForADC=0;
  char q[50];
  unsigned int tsc0=T1TC;
  tsc0 %= 60000000;

  
  for(j = 0; j < 50; j++) {
    q[j] = 0;
  }

  for(j=0;j<8;j++) {
    temp=0x0020FF00 | (1 << adcChan[j]);
    if(channelActive[j]) {
      if(adcSide[j]==0) {
        AD0CR=temp; //Set up ADC and select channel
        AD0CR|= 0x01000000; // start conversion
        do {
          temp = AD0DR;
        } while((temp & 0x80000000) == 0);
        AD0CR = 0x00000000;
      } else {
        AD1CR=temp; //Set up ADC and select channel
        temp=0;
        AD1CR|= 0x01000000; // start conversion
        do {
          temp = AD1DR;
        } while((temp & 0x80000000) == 0);
        AD1CR = 0x00000000;
      }
      adcValue = (temp & 0xFFC0) >> 6;
      q[ind]=adcValue>>8;
  	  ind++;
      q[ind]=adcValue & 0xFF;
      ind++;
    }
  }

  unsigned int tsc1=T1TC;
  tsc1 %= 60000000;

  writeRX(0xA0);
  writeRX(0xA2);
  writeRX(0x00);
  writeRX(ind+9); //length
  int checksum=sendRX(0x2C,0);//Steal an unused output message slot;
  checksum=sendRX((tsc0 >>24) & 0xFF,checksum);
  checksum=sendRX((tsc0 >>16) & 0xFF,checksum);
  checksum=sendRX((tsc0 >> 8) & 0xFF,checksum);
  checksum=sendRX((tsc0 >> 0) & 0xFF,checksum);
  for(j = 0; j < ind; j++) checksum=sendRX(q[j],checksum);
  checksum=sendRX((tsc1 >>24) & 0xFF,checksum);
  checksum=sendRX((tsc1 >>16) & 0xFF,checksum);
  checksum=sendRX((tsc1 >> 8) & 0xFF,checksum);
  checksum=sendRX((tsc1 >> 0) & 0xFF,checksum);
  writeRX(checksum>>8);
  writeRX(checksum & 0xFF);
  writeRX(0xB0);
  writeRX(0xB3);
}

void FIQ_Routine(void) {
  char a;
  int j;

  stat(0,ON);
  for(j = 0; j < 5000000; j++);
  stat(0,OFF);
  a = U0RBR;

  a = U0IIR;  // have to read this to clear the interrupt
}

void SWI_Routine(void) {
  while(1);
}

void UNDEF_Routine(void) {
  stat(0,ON);
}
                    //0         1      2      3      4      5      6      7       8
                    //X      1200   2400   4800   9600  19200  38400  57600  115200
static int baudDL[]={0xFFFF,0x0C00,0x0600,0x0300,0x0180,0x00C0,0x0060,0x0040,0x0020};

void setup_uart0(int newbaudcode, char want_ints) {
  baudcode = newbaudcode;
  U0LCR = 0x83;   // 8 bits, no parity, 1 stop bit, DLAB = 1
  //DLAB - Divisor Latch Access bit. When set, a certain memory address
  //       maps to the divisor latches, which control the baud rate. When
  //       cleared, those same addresses correspond to the processor end 
  //       of the FIFOs. In other words, set the DLAB to change the baud
  //       rate, and clear it to use the FIFOs.
  U0DLM=baudDL[baudcode] >> 8;
  U0DLL=baudDL[baudcode] & 0xFF;
  
  U0FCR = 0x01; //Enable both FIFOs
  U0LCR = 0x03; //Turn of DLAB - FIFOs accessable
//  U0TER = 0x80; //Enable transmitter

  if(want_ints == 1) {
    enableIRQ();
    VICIntSelect &= ~0x00000040; //Not an FIQ interrupt
    VICIntEnable |= 0x00000040;  //Enabled
    VICVectCntl1 = 0x26;         //VIC slot 1 enabled, IRQ6 (UART0)
    VICVectAddr1 = (unsigned int)UART0ISR;
    U0IER = 0x01; //Enable UART0 interrupts on Rx data available
  } else {
    VICIntEnClr = 0x00000040;
    U0IER = 0x00;
  }
}

void setup_uart1(int newbaudcode, char want_ints) {
  baudcode = newbaudcode;
  U1LCR = 0x83;   // 8 bits, no parity, 1 stop bit, DLAB = 1
  //DLAB - Divisor Latch Access bit. When set, a certain memory address
  //       maps to the divisor latches, which control the baud rate. When
  //       cleared, those same addresses correspond to the processor end 
  //       of the FIFOs. In other words, set the DLAB to change the baud
  //       rate, and clear it to use the FIFOs.
  
  U1DLM=baudDL[baudcode] >> 8;
  U1DLL=baudDL[baudcode] & 0xFF;

  U1FCR = 0x01; //Enable both FIFOs
  U1LCR = 0x03; //Turn of DLAB - FIFOs accessable
//  U1TER = 0x80; //Enable transmitter

  if(want_ints == 1) {
    enableIRQ();
    VICIntSelect &= ~0x00000080;  //Make this a normal interrupt instead of FIQ
    VICIntEnable |= 0x00000080;   //Enable the interrupt
    VICVectCntl1 = 0x27;          //VIC slot 1 enabled, IRQ6 (UART0)
    VICVectAddr1 = (unsigned int)UART1ISR;
    U1IER = 0x01;  //UART throws interrupts
  } else {
    VICIntEnClr = 0x00000080;
    U1IER = 0x00;
  }
}

void stat(int statnum, int onoff) {
  if(statnum) {
    // Stat 1
    if(onoff){ IOCLR0 = 0x00000800; } // On
    else { IOSET0 = 0x00000800; } // Off
  } else {
    // Stat 0 
    if(onoff){ IOCLR0 = 0x00000004; } // On
    else { IOSET0 = 0x00000004; } // Off
  }
}

void readLogCon(void) {
  int x, mark = 0, ind = 0, s;
  char temp;

  if(root_file_exists("LOGCON.txt")) {
    fd = root_open("LOGCON.txt");
    s = fat16_read_file(fd, (unsigned char *)stringBuf, 512);
    stringBuf[s] = '\0';
    fat16_close_file(fd);
  } else {
    fd = root_open_new("LOGCON.txt");
    if(fd == NULL) {
      blinklock();
    }

    strcpy(stringBuf, ";MODE = 0\r\n"
                      ";ASCII = N\r\n"
                      "Baud = 7\r\n"
                      "Frequency = 100\r\n"
                      ";Trigger Character = $\r\n"
                      ";Text Frame = 100\r\n"
                      "AD1 = N\r\n"
                      "AD2 = N\r\n"
                      "AD3 = N\r\n"
                      "AD4 = N\r\n"
                      "AD5 = N\r\n"
                      "AD6 = N\r\n"
                      "AD7 = N\r\n"
                      "AD8 = N\r\n"
                      ";Saftey On = Y\r\n");
    s = strlen(stringBuf);
    fat16_write_file(fd, (unsigned char*)stringBuf, s);
    sd_raw_sync();
  }

  for(x = 0; x < stringSize; x++) {
    temp = stringBuf[x];
    if(temp == 10) {
      mark = x;
      ind++;
      if(ind == 3) {
        baudcode=stringBuf[mark-2]-'0';
      } else if(ind == 4) {
        freq = (stringBuf[mark-2]-48) + (stringBuf[mark-3]-48) * 10;
        if((stringBuf[mark-4] >= 48) && (stringBuf[mark-4] < 58)) {
          freq+= (stringBuf[mark-4]-48) * 100;
          if((stringBuf[mark-5] >= 48) && (stringBuf[mark-5] < 58)){ freq += (stringBuf[mark-5]-48)*1000; }
        }
      } else if(ind == 7) {
        channelActive[0] = (stringBuf[mark-2]=='Y');
        if(channelActive[0]){ nChannels++; }
      } else if(ind == 8) {
        channelActive[1] = (stringBuf[mark-2]=='Y');
        if(channelActive[1]){ nChannels++; }
      } else if(ind == 9) {
        channelActive[2] = (stringBuf[mark-2]=='Y');
        if(channelActive[2]){ nChannels++; }
      } else if(ind == 10) {
        channelActive[3] = (stringBuf[mark-2]=='Y');
        if(channelActive[3]){ nChannels++; }
      } else if(ind == 11) {
        channelActive[4] = (stringBuf[mark-2]=='Y');
        if(channelActive[4]){ nChannels++; }
      } else if(ind == 12) {
        channelActive[5] = (stringBuf[mark-2]=='Y');
        if(channelActive[5]){ nChannels++; }
      } else if(ind == 13) {
        channelActive[6] = (stringBuf[mark-2]=='Y');
        if(channelActive[6]){ nChannels++; }
      } else if(ind == 14) {
        channelActive[7] = (stringBuf[mark-2]=='Y');
        if(channelActive[7]){ nChannels++; }
      }
    }
  }
  
  writeRX(0xA0);
  writeRX(0xA2);
  writeRX(0x00);
  writeRX(nChannels*2+1); //length
  int checksum=sendRX(0x2A,0); //Steal an unused output message slot;
  for(x = 0; x < nChannels; x++) {
    if(channelActive[x]) {
      checksum=sendRX(x+1,checksum);
  	  checksum=sendRX(adcSide[x]<<4 | adcChan[x],checksum);
    }
  }
  writeRX(checksum>>8);
  writeRX(checksum & 0xFF);
  writeRX(0xB0);
  writeRX(0xB3);
}

void startRecordUART(void) {
  //  setup_uart0(baud,1);
  setup_uart1(baudcode,1);
}

void startRecordADC(void) {
  enableIRQ();
  // Timer0  interrupt is an IRQ interrupt
  VICIntSelect &= ~0x00000010;
  // Enable Timer0 interrupt
  VICIntEnable |= 0x00000010;
  // Use slot 3 for Timer0 interrupt
  VICVectCntl3 = 0x24;
  // Set the address of ISR for slot 3
  VICVectAddr3 = (unsigned int)timerISR;

  T0TCR = 0x00000002;  // Reset counter and prescaler
  T0MCR = 0x00000003;  // On match reset the counter and generate interrupt
  T0MR0 = 58982400 / freq;

  T0PR = 0x00000000;

  T0TCR = 0x00000001; // enable timer

}

void blinklock() {
  for(;;) {
    stat(0,ON);
    delay_ms(50);
    stat(0,OFF);
    stat(1,ON);
    delay_ms(50);
    stat(1,OFF);
  }
}

static int limit[]={1,2};
static int fired[]={0,0};
static char messages[]={25,        //payload length
                        0x80,      //Init Data Source
                        0x00,0x00,0x00,0x00,  //ECF X (ignored)
                        0x00,0x00,0x00,0x00,  //ECF Y (ignored)
                        0x00,0x00,0x00,0x00,  //ECF Z (ignored)
                        0x00,0x00,0x00,0x00,  //Clock drift (ignored)
                        0x00,0x00,0x00,0x00,  //GPSTOW (ignored)
                        0x00,0x00,            //GPS Week (ignored)
                        0x0C,                 //Channel count (ignored)
                        0x10,      //Hot init, Firehose on! Raw track, nav lib, 50bps, RTCM, clock, DR
                        2,         //payload length
                        0x84,      //Poll Software Version
                        0x00       //Not used
							         };

void writeMsgs() {
  for(int j=0;j<2;j++) {
    if((!fired[j]) && (loops>=limit[j])) {
	    fired[j]=1;
      char* ptr=messages;
      char len=*ptr;
      ptr++;
      for(int k=0;k<j;k++) {
        ptr+=len;
        len=*ptr;
        ptr++;
      }
      sendcmd(0xA0,0);
      sendcmd(0xA2,0);
      sendcmd(0x00,0);
      sendcmd(len,0);
	    int checksum=0;
      for(int k=0;k<len;k++) {
        checksum=sendcmd(*ptr,checksum);
		    ptr++;
	    }
      sendcmd(checksum >> 8,0);
      sendcmd(checksum & 0xFF,0);
      sendcmd(0xB0,0);
      sendcmd(0xB3,0);
    }
  }
}							 

void writeTimeUsed(int lasttoc, int thistoc, int time) {
  writeRX(0xA0);
  writeRX(0xA2);
  writeRX(0x00);
  writeRX(12); //length
  int checksum=sendRX(0x15,0); //Steal an unused output message slot;
  checksum=sendRX((YEAR >> 8) & 0xFF,checksum);
  checksum=sendRX((YEAR >> 0) & 0xFF,checksum);
  checksum=sendRX((MONTH >> 0) & 0xFF,checksum);
  checksum=sendRX((DOM >> 0) & 0xFF,checksum);
  checksum=sendRX((HOUR >> 0) & 0xFF,checksum);
  checksum=sendRX((MIN >> 0) & 0xFF,checksum);
  checksum=sendRX((SEC >> 0) & 0xFF,checksum);
  checksum=sendRX((time >>24) & 0xFF,checksum);
  checksum=sendRX((time >>16) & 0xFF,checksum);
  checksum=sendRX((time >> 8) & 0xFF,checksum);
  checksum=sendRX((time >> 0) & 0xFF,checksum);
  writeRX(checksum>>8);
  writeRX(checksum & 0xFF);
  writeRX(0xB0);
  writeRX(0xB3);
}

static int lasttoc=-1;
static int timeused=0;
static int redlight=0;							 
void loop(void) {
  int idle=1;
  if(timeForADC) {
    stat(1,ON);
    readADC();
    idle=0;
  }
  if(log_array1 == 1) {
    stat(1,ON);
    if(fat16_write_file(handle,(unsigned char *)RX_array1, stringSize) < 0) blinklock();
    sd_raw_sync();
    log_array1 = 0;
   	loops++;
	  writeMsgs();
    idle=0;
  }
  
  if(log_array2 == 1) {
    stat(1,ON);
    if(fat16_write_file(handle,(unsigned char *)RX_array2, stringSize) < 0) blinklock();
    sd_raw_sync();
    log_array2 = 0;
    idle=0;
  }
  
  // if button pushed, log file & quit
  if((IOPIN0 & 0x00000008) == 0) {
    VICIntEnClr = 0xFFFFFFFF; //Turn off interrupts
    //Write out the last bit of buffer
    if(RX_in < 512) {
      fat16_write_file(handle, (unsigned char *)RX_array1, RX_in);
      sd_raw_sync();
    } else if(RX_in >= 512) {
      fat16_write_file(handle, (unsigned char *)RX_array2, RX_in - 512);
      sd_raw_sync();
    }
    //quit
    blinklock();
  }
  
  int thistoc=T1TC % 60000000;
  //Handle the last little bit of each timer cycle
//  if(T1TC>4260000000) {
//    T1TC-=426000000;
//  }
  if(lasttoc>0) {
    if(thistoc<lasttoc) {
      //Get the last little bit of time around the corner
      if(!idle) timeused+=60000000-lasttoc;
      writeTimeUsed(lasttoc,thistoc,timeused);
      //This resets the counter
      if(!idle) {
        timeused=thistoc;
      } else {
        timeused=0;
      }
    } else if(!idle) {
      timeused+=(thistoc-lasttoc);
    }
  }
  lasttoc=thistoc;
  stat(1,OFF);
}

void fat_initialize(void) {
  if(!sd_raw_init()) {
    blinklock();
  }

  if(openroot()) { 
    blinklock();
  }
}

void delay_ms(int count) {
  int i;
  count *= 10000;
  for(i = 0; i < count; i++) asm volatile ("nop");
}

//Turn on timer 1, 60MHz by default

T1TCR=0x01;

//Turn on the clock, using the 32kHz clock crystal

CCR=0x13;

so I could use say 1 MHZ timer, thus its 60 times slower, uses less energy and I can do 1 logging every second?

Want to use it for long time logging, in a house, a logging every 5 min would be enough?

Will run on batterys, hence energy saving.

I suppose you could, but in my program Timer1 is only read passively to determine processor load, not used to activate any interrupts or anything (timer0 is used for the ADC timing, like the original firmware). Other than incrementing a number only 1M times a second instead of 60M I don’t know that it would save a lot of power. Also, I just turn the timer on, I don’t know if it was already on and if so what was using it, so if you mess with the timer frequency, it might cause something else totally unrelated to break. Turning it off entirely might save more power

The real time clock is just used to keep track of total uptime. Turning it off might save some power at the expense of not knowing in an absolute sense when a log point was taken (except from GPS). Also if you turn off the time and load packet, you can save some space in the log.

For big power savings (maybe) look into putting the GPS to sleep. On the EM406 I use, (and I think all SiRF GPS) there is a mode where it runs only X00ms out of each second (X can be as low as 2) but still keeps its lock, and therefore draws only 20% of its nominal 70mA. Use the message mechanism to send binary commands to the GPS.

Keep the ideas coming. I am doing a major rewrite of the Logomatic and I welcome suggestions, but don’t promise to implement them. Among the features:

*Reorganizing all of the code so that it should be easier to read/modify for others

*Logging from UART1 (done) AND/OR UART0 (would anyone use this?)

*(done) Reading SiRF binary packets and writing them interleaved with fake SiRF packets for the ADC

*(done) Did you know that AD0.4 is hard-wired on the Logomatic board to monitor the battery voltage? The stock firmware makes no use of this hardware, and I was forced to use one of the 8 ADC pins for this instead. The new firmware makes this hardware available as AD0 (done). New capabilities from old hardware rocks!

*(done) Always refer to the ADC pins by the number ( 1-8 ) printed on the board rather than their internal designations (1.2 etc) and write the ADC out in the order they come on the board

*Writing the data from the UART in hex form (each byte converted into 2 bytes 0-9A-F) and the ADC data in decimal or hex text as well

*Writing the data from the UART in text form in frames (for NMEA) with an end marker as well as a start marker and the ADC data

*(done) Reworking the parser so it actually looks at the left side of the = instead of counting lines, and making it easier to extend

*Putting in an NMEA and SiRF parser so it could actually set variables in the code so that the Logomatic actively knows where it is (to be used by others extending the Logomatic further)

*Maybe writing all data received from UART1 on UART0, so it could be monitored as well as recorded. Maybe ADC data as well, heck maybe everything which goes into the log. Of course if you want that, go get an Arduino.

*Maybe switches to turn on and off various peripherals, such as the timer, an unused UART, the USB system (while in Logomatic mode, not readout)

*Put messages to be written out to the UART in a text file (or binary file) rather than in the code. Make the messages able to be either SiRF, raw binary (probably in hex in the file) or text (for NMEA commands)

*Maybe a package tracker mode where it logs for x seconds, then sleeps for y seconds (I think the above poster wanted this)

I myself run the thing in SiRF binary with ADC in fake SiRF. I can easily imagine running in a text mode where most of the lines are ADC and some of the rest are NMEA:

0 123 123 145 176

0 111 192 983 112

$GNMEA,stuff,stuff,stuff*0A

0 123 123 145 176

0 111 192 983 112

$GNMEA,stuff,stuff,stuff*0A

etc…

Don’t know what to do about the column labels and time and load packets in this case, maybe

C 1(0.2) 2(0.3) 3(1.4) 4(1.7) 5(1.6)

A 0 123 123 145 176

A 0 111 192 983 112

N $GNMEA,stuff,stuff,stuff*0A

A 0 123 123 145 176

A 0 111 192 983 112

N $GNMEA,stuff,stuff,stuff*0A

L 0000/00/00 00:00:00 24.72%

etc…

where the letter at the beginning says what kind of line it is (C=columns, A=ADC, N=NMEA, L=time and (L)oad)

Code will be available when I feel like it and figure out a good place to post it, or perhaps never (my publishing house is ‘St. Kwan’s Home for the Terminally ADD’ :smiley: ). When I do so, anyone can use it for any purpose, even commercially (I’m looking at you, Sparkfun) as long as my name remains in the code comments. With any luck, this will be the firmware everyone has been asking for here in this forum for years.

so its christmas wishlist time?

What about a time stamp option, so it puts time when logged in front od a line?

Some sort of modules in the program? I would delete most of it, only want to do do ADC. So if shorter code, less to do for the CPU?

Possibility to do logs every 5 or so min, I just wound it down to 1min?

Some sort of stack buffer, have trouble with my temperature readings, they differ up to 5 digits in 1s, quite impossible (using LM35). So, buffer would read say 10 values and store the average, drops oldest value reads new, average again? Ok, that involves some processor time, so suppose only for 1Hz or so operations.

That all in neat blocks, so you just comment out what you don’t need to have a nice small .SFE file, just as much as needed.

Your code can runs with batterys. I am wondering if it can runs with external power like wall charger through USB