CLAUDE.md 5.1 KB

Project

GPStracker is a portable device used for acquiring GNSS data via a wireless module and saving tracks onto a memory card.

Main directory structure

optiboot - An "arduino"-type bootloader, modified slightly to handle device's power circuits. Likely does not need any modifications. PCB - Altium Designer and mechanical files for project PCBs. soft - Main application code. gps-test-tool - PC program to process NMEA data just like the embedded device would do, using actual main project code. Used for manual reprocessing of already recorded data and for code testing.

Main Project source files

Optimally, all program parts should have a .c and a corresponding .h file. 1wire: Utility for communicating with a Dallas 1-Wire bus. diskio.h: Header file for memory card states. display: Main display routines. ds18b20: Utility for reading temperature sensor data using 1-Wire. expander: Utility for accessing I2C GPIO expander. ff: Utility for accessing FAT filesystem. ffconf.h: FAT configuration ffunicode.c: FAT helper functions for unicode support gpx: Handles GPX file generation and writes, also processes and filters location data. HD44780-I2C: Utility for writing data to an alphanumerical display. I2C: Utility for communicating with devices on the I2C bus. integer.h: Type definition header. main: Main loop, global data storage, device initialization and system utilities. menu: Utility for drawing nested menus with various item types. mmc: Utility for accessing a memory card. nmea: Utility for parsing and generating NMEA messages. settings: Device configuration and settings menu. stime: Manages UNIX time conversions. timec: Calculates local time and generates time and date strings. uart*: Initializes and handles serial port communication. UC1601S-I2C':Utility for writing data to a graphic display. working_modes: Provides main display (default_menu), handles device state. xprintf`: Utility providing printf-like API.

Build

Two display types are supported. Always provide display type as make argument. Use make clean when changing display types. At the moment, both types display identical data.

cd soft make clean make LCD=alnum make clean make LCD=graphic

Notes

User facing (display) messages - always in Polish. Logging, code constructs and code comments always in English. Polish character support not available at the time.

Main Code guidelines

This is a very restricted embedded environment. Minimize RAM and Flash usage, maximize code efficiency. Some of these guidelines may be deemed antipatterns in other environments, but are essential here.

  1. All const variables should be declared as __flash const, example __flash const char _logging_active[] = "Zapis aktywny";. Do not use PROGMEM.
  2. Const array of strings example:

    __flash const char gnss_gps_glonass_galileo[] = "GPS+GL.NS+GAL.EO";
    __flash const char gnss_gps[] = "GPS";
    __flash const char gnss_gps_galileo[] = "GPS+GALILEO";
    __flash const char gnss_galileo[] = "GALILEO";
    __flash const char gnss_gps_beidou[] = "GPS+BEIDOU";
    __flash const char gnss_beidou[] = "BEIDOU";
    
    __flash const char *gnss_names[] = {
    	gnss_gps_glonass_galileo,
    	gnss_gps,
    	gnss_gps_galileo,
    	gnss_galileo,
    	gnss_gps_beidou,
    	gnss_beidou,
    };
    
  3. When a constant is to be used as a function argument, also declare the parameter as __flash const to allow direct accesses without pgm_read_XXX(). If the function (or a struct field) is to support both RAM variables and Flash constants, declare it as const and add another function argument / structure member to determine the memory type. Then, RAM data can be accessed directly, and Flash consts with pgm_read_XXX(addr) (DON'T USE direct accesses if a passed const pointer without __flash is for Flash - it won't work!). It is recommended to create separate functions for const and variable parameters (these can, in turn, call a single helper).

  4. Avoid placing auto variables on stack. Don't declare auto (local) variables larger than 4 bytes (beware of structs). If such storage is needed, consider using static type instead - even if persistence is not needed. It will consume predictable amount of RAM and generate more efficient code. Only variables allowed on stack are arguments for variadic functions.

  5. Consider using global buffer variables (for example text buffers), to be later used locally by a function, if possible. Be sure other functions won't overwrite it when being in use.

  6. Use struct system_s structure, with its only extern volatile struct system_s System instance, to keep global state - both for "system" info and for output data from application logic. Ideally, store only "fully cooked" data here - no partial states and unprocessed data, unless needed globally in the application.

  7. For busy loops, use _delay_ms() and _delay_us() - watch out for watchdog timeouts.

  8. For non-precision delays, use set_timer(timer_name, time_in_ms). Declare new timer names inside struct timers (always unsigned int!). To check whether time has passed, query timer_expired(timer_name).

  9. Don't add static and global variables to store data which can be cheaply recomputed.