Project Home

H1 Prototype

Mechanicals

Electronics

Software

Libraries

Heliostat Software

Software

Arduino uses a special extension of the Processing language, which is based in C and has a few special functions useful for embedded microprocessor programming. This avoids requiring assembly level knowledge to access digital i/o, pwm, and other MCU functions. One button from the Arduino UI invokes the compilation process, which actually uses the Gnu C compiler in the background. The many layers of Open Source software involved are truly amazing. The Arduino community is an invaluable resource for those interested in learning how to use the Arduino platform for ATMEL ATMega family programming.

For those new to the Arduino platform, they like to call its programs Sketches, as if they were short and quick. If you've written enough to code to fill a 168 or 328 you've written a lot more than a sketch, and filling more than half of a ATMEGA 1280 takes thousands of lines of code. My code mantra is modularity and lots of testing. Debugging a huge main program using untested libraries makes for huge head aches given the primitive state of Arduino debugging. There is no provision for setting break points and checking and altering variables during run time. So called instrumenting the code with Serial.print statements is the most effective way of deducing the cause of strange run-time behavior, besides and always in conjunction with careful study of the code base.


Sketches

HelioEEPROM

HelioEEPROM was created for managing the EEPROM configuration. It was instrumented with print statements to output the text for a suitable header file for use by the main program. The actual header file is created by running the program with the Serial monitor window open and then using Copy and Paste to create the actual file using your favorite text editor.

The HelioCom program expects to find certain configuration information in EEPROM in order to work properly, thus HelioEEPROM is essential for creating the default EEPROM data.
In order to save program and stack memory, most strings are stored in EEPROM. This is accomplished by use of a lookup table in EEPROM which translates defined constants into EEPROM pointers to access the strings when needed during run time.


MotorTestCal

The MotorTestCal program was created for just focusing on motor testing and calibration routines. It of course needs access to the configuration structure created in EEPROM by that program.

    Menu Items
  • ALT - selects the Altitude motor for subsequent operation
  • AZI - selects the Azimuth motor for subsequent operation
  • MANU - allows joystick style manual control of motors
  • PPI - invokes for calibration procedure for feedback pulse per inch
  • LIM - invokes calibration procedure for actuator maximum extension limit
  • ABC - accepts input of geometric constants A, B, and C. These three values define the basic triangle upon which angle calculations are based.
    • A - minumum length of actuator
    • B - length of control radius
    • C - distance from control radius center to actuator mount point
  • AAA - calibration of (0, 90), or (135,180,225) degree points. This also invokes the findError() procedure to automatically calculate the necessary error constants (Ec, Ef1, Ef2)
  • M - For the altitude axis, M represents the distance of the actuator body axis from the mounting point which the actuator slightly revolves about.
  • EF - allows input of specific error correction constants for angle calculations: Ec, Ef1, and Ef2.
  • GOTO - invokes the motor.goTo(pos) function to move to a desired location.
  • GANG - go to a specified Angle.
  • SAVE - save motor configuration data to EEPROM
  • PRY - invokes Pitch, Roll, and Yaw calibration procedure.
  • ZERO - fully retracts motor and resets pulse count to zero.


HelioCom

HelioCom is the main heliostat program. It incorporates a main loop that checks for Serial input as well as user inputs, allowing manual and serial control, and also checks for scheduled events and invokes the proper control routines during tracking operation.


Libraries

Clock

The clock is currently based on the DS1307 chip, and the clock library is based on code originally written by Maurice Ribble.

DB

The DB library provides a very simple database model for data stored in EEPROM. This is very useful for such things as heliostat target, and scheduled event records. It requires use of the EEPROM library.

Here's the library header code:

struct DB_Header
{
  byte n_recs;
  byte rec_size;
  byte head; // points to first record in sort order
  byte tail; // last record in sort, but sort is not implemented in the library
             // because you need to know the key and that gets complicated!
};

#define DB_HEAD_SIZE 4

// DB_error values
#define DB_OK 0
#define DB_RECNO_OUT_OF_RANGE 1

#define DB_REC (byte*)(void*)&&

typedef byte* DB_Rec;

class DB {
  public:
    void    create(int head_ptr, byte recsize);
    void    open(int head_ptr);
    boolean write(byte recno, const DB_Rec rec);
    boolean read(byte recno, DB_Rec rec);
    boolean deleteRec(byte recno);	                // delete is a reserved word
    boolean insert(byte recno, const DB_Rec rec);
    byte    append(DB_Rec rec);
    byte    nRecs();
    byte    head();
    void    head(byte h);
    void    tail(byte t);
    byte    tail();
    DB_Header DB_head;
    byte DB_error;
  private:
    int writeHead();
    int readHead();
    int EEPROM_dbWrite(int ee, const byte* p);
    int EEPROM_dbRead(int ee, byte* p);
    int DB_head_ptr;
    int DB_tbl_ptr;
};

extern DB db;

...and here is a sample Arduino sketch demonstrating usage:
// DB library test/example sketch

#include <EEPROM.h>
#include <DB.h>

#define TEST_TBL_PTR 3072

DB db;
struct Test_Rec
{
  short id;
  int x,y;
  char product[8];
} test_rec;

void listRecords()
{
  for (int i=0;i<db.DB_head.n_recs;i++)
  {
    db.read(i+1, DB_REC test_rec);
    Serial.print("id: ");
    Serial.println(test_rec.id,DEC);
    Serial.print("x,y: ");
    Serial.print(test_rec.x,DEC);
    Serial.print(',');
    Serial.println(test_rec.y,DEC);
    Serial.print("product = ");
    Serial.println(test_rec.product);
    Serial.println();
  }
}

void setup()
{
  char buf[8];
  int i;

  Serial.begin(9600);  
  db.create(TEST_TBL_PTR, sizeof(test_rec));
  // no need to call db.open after a create
  
  for (i=0;i<5;i++)
  {
    test_rec.id = i;
    test_rec.x = i*2;
    test_rec.y = i*3;
    itoa(test_rec.x*test_rec.y,test_rec.product,10);
    db.append(DB_REC test_rec);    
  }
  listRecords();  

  Serial.print("NRecs = ");
  Serial.println(db.DB_head.n_recs,DEC);
  Serial.println("---------------");
  
  db.deleteRec(2, DB_REC test_rec);
  Serial.println("Deleted rec 2");
  Serial.print("NRecs = ");
  Serial.println(db.DB_head.n_recs,DEC);
  Serial.println("---------------");
  listRecords();
  
  Serial.println("---------------");
  test_rec.id=99;
  test_rec.x=99;
  test_rec.y=99;
  db.insert(2,DB_REC test_rec);
  Serial.println("Inserted new #2=99");
  Serial.print("NRecs = ");
  Serial.println(db.DB_head.n_recs,DEC);
  Serial.println("---------------");
  listRecords();
  
}

void loop()
{

}

FLInt

FLInt stands for Float, Long, and Integer types. This is basically a set of routines for formating numbers to strings.

int itoaRJ(int n, char *s, byte w=6, char jchar=' ', byte sign_flag=USE_PLUS)
int myitoa(int n, char *s, byte sign_flag)
int myltoa(long n, char *s, byte sign_flag)
char* myftoa( float val, char* outbuf, byte dlod, byte precision)

LCDShield

This is a useful library for human I/O using the popular LCD shield for Arduinos.

int get_key(unsigned int input)// Convert ADC value to key number
boolean getKey(int &&key)
boolean confirmYesNo()
void displayFloatVal(float val, byte dlod, byte drod, byte row, byte col)
void displayIntVal(int val, byte w, byte row, byte col)
void displayByteVal(int val, byte w, byte row, byte col)
boolean setByteParm(byte && p, byte ll, byte ul, byte row, byte col)
boolean setIntParm(int && p, int ll, int ul, byte row, byte col)
boolean setFloatParm(float && f, byte diglod, byte digrod, int fmin, int fmax, byte row, byte col)
void editString(char* s, const byte width, const byte row, const byte col)
void writeLCD(char* line1, char* line2)

Motor

This has a few kludges in the code to account for differences in the Azimuth and Altitude angle calculations, but the rest is pretty generic and all intended to work with most H-bridge style motor drivers.

class Motor {
public: 
  Motor(byte L0, byte L1);
  void init(Motor_Cfg &c);
  void reset();
  void backward();
  void forward();
  void go();
  void goToAngle(float ang);
  void goTo(int p);
  void run(int duration);
  void speed(byte s);
  void stop();
  void finePosition();
  void checkStatus();
  float position(byte unit);
  byte flags;      // refer to above definitions for bit flag definitions
  int count;        // pulse count for reed sensor
  int address;      // pulse address count for reed sensor
  unsigned long last_pulse;  // timestamp of last pulse in micros
  unsigned int period;       // milliseconds between last two pulses
  byte L0_pin;    // logic pin, direction control HIGH=Clockwise used for PWM when no en_pin
  byte L1_pin;    // logic pin, direction control HIGH=CCW
  unsigned int pulse_per_unit;// pulses per positional unit (cm, inch, deg, etc)
  unsigned int max_count;    // # of pulses limit to limit switch
  unsigned int offset;       // motor 1 pulse origin offset
  float angle;

SolPos

The SolPos algorithm was developed by Martin Rymes with the NREL. I hacked it up fairly severely to save memory space on the Arduino, but it retains most of the original's functionality.

Triad

Here's some bona fide rocket science. The so called Triad algorithm was developed by NASA scientists as a means of determining attitude based on two measurements of another frame of reference. Applied to the issue of heliostat mount alignment and the Triad algorithm allows deduction of the Euler pitch and roll angles for the azimuth axis of rotation which allows the MCU to compensate for these alignment errors while tracking.

The Triad algorithm requires two vectors in the body (i.e. rocket/heliostat) frame of reference, and two corresponding vectors expressed in the inertial (earth) frame of reference. The cross product is used to create a third and orthogonal vector for each input vector pair. Things are normalized and the resulting pair of vector triads represents a frames of reference for the body and inertial systems. By some magic of matrix math, the attitude matrix relating these two systems is found by summing the dot products of each vector with its corresponding transposed vector pair, but I bet you already knew that.
Chapter 4 of a book by Chris Hall, March 18, 2003, entitled Attitude Determination, should be refered to for a more complete explanation of this subject and the mathematics involved.

The Triad algorithm is part of a general vector library.

/* example usage

  Matrix3x3 Rbi;
  Vector v1b,v2b,v1i,v2i;
  v1b = ...
  v2b = ...
  v1i = ...
  v2i = ...

  useTriad(v1b,v2b,v1i,v2i,Rbi);
  Roll = atan2(-Rbi.b2,Rbi.b1);
  Pitch = asin(Rbi.b0);
  Yaw = atan2(-Rbi.c0,Rbi.a0);

*/