]> git.defcon.no Git - rctxduino/blobdiff - source/RCTXDuino/RCTXDuino.pde
Changes to debugging, battery alarm, ppm timing:
[rctxduino] / source / RCTXDuino / RCTXDuino.pde
index cf711bb4700d7789d8cb07855862cb55e8baa59b..ebe62ad503afde00aea28315d61861a35b48b52e 100644 (file)
@@ -1,23 +1,74 @@
 #include <LiquidCrystal.h>
 #include <TimerOne.h>
+#include <EEPROM.h>
+
+// Undefine this whenever a "release" or "flight-test" build is made.
+// Defining DEBUG sets some crazy values for things like battery warning,
+// and includes a whole bunch of debugging-related code ...
+#define DEBUG 1
+
+#define MAX_INPUTS 8
+
+// Update this _every_ time a change in datastructures that
+// can/will ber written to EEPROM is done. EEPROM data is
+// read/written torectly into/from the data structures using
+// pointers, so every time a data-set change occurs, the EEPROM
+// format changes as well..
+#define EEPROM_VERSION 7
+
+// Some data is stored in fixed locations, e.g.:
+//  * The EEPROM version number for the stored data (loc 0)
+//  * The selected model configuration number (loc 1)
+//  * (add any other fixed-loc's here for doc-purpose)
+// This means that any pointer-math-operations need a BASE
+// adress to start calc'ing from. This is defined as:
+#define EE_BASE_ADDR 10 
+
+// Having to repeat tedious base-address-calculations for the
+// start of model data should be unnessecary. Plus, updating
+// what data is stored before the models will mean that each
+// of those calculations must be updated. A better approach is
+// to define the calculation in a define!
+// NOTE: If new data is added in front of the model data,
+// this define must be updated!
+#define EE_MDL_BASE_ADDR (EE_BASE_ADDR+(sizeof(input_cal_t)+ 10))
+
+// Just as a safety-precaution, update/change this if a chip with
+// a different internal EEPROM size is used. Atmega328p has 1024 bytes.
+#define INT_EEPROM_SIZE 1024
+
+#define MAX_MODELS 4 // Nice and random number..
+
 
 // --------------- ADC related stuffs.... --------------------
 
-int maxval[8];   // Stores maximum values read during calibration, setup() sets 1024
-int minval[8];   // Stores minimum values read during calibration, setup() sets 0
-int rev[8];
-int dr[8];        // The Dual-rate array uses magic numbers :P
-/*     dr[0] = Input channel #1 of 2 for D/R switch #1. 0 means off, 1-4 valid values.
-       dr[1] = Input channel #2 of 2 for D/R switch #1. 0 means off, 1-4 valid values.
-       dr[2] = Input channel #1 of 2 for D/R switch #2. 0 means off, 1-4 valid values.
-       dr[3] = Input channel #2 of 2 for D/R switch #2. 0 means off, 1-4 valid values.
-       dr[4] = D/R value for switch # 1 LOW(off). Value -100 to 100 in steps of 5.
-       dr[5] = D/R value for switch # 1 HIGH(on). Value -100 to 100 in steps of 5.
-       dr[6] = D/R value for switch # 1 LOW(off). Value -100 to 100 in steps of 5.
-       dr[7] = D/R value for switch # 1 HIGH(on). Value -100 to 100 in steps of 5. 
-*/
-
-volatile float channel[8];
+struct input_cal_t // Struct type for input calibration values
+{
+       int min[MAX_INPUTS];
+       int max[MAX_INPUTS];
+       int center[MAX_INPUTS];
+} ;
+input_cal_t input_cal;
+
+struct model_t
+{
+       int channels; // How many channels should PPM generate for this model ...
+       float stick[8]; // The (potentially recalc'ed) value of stick/input channel.
+       int raw[8];
+       boolean rev[8];
+       int dr[8];        // The Dual-rate array uses magic numbers :P
+       /*      dr[0] = Input channel #1 of 2 for D/R switch #1. 0 means off, 1-4 valid values.
+               dr[1] = Input channel #2 of 2 for D/R switch #1. 0 means off, 1-4 valid values.
+               dr[2] = Input channel #1 of 2 for D/R switch #2. 0 means off, 1-4 valid values.
+               dr[3] = Input channel #2 of 2 for D/R switch #2. 0 means off, 1-4 valid values.
+               dr[4] = D/R value for switch # 1 LOW(off). Value -100 to 100 in steps of 5.
+               dr[5] = D/R value for switch # 1 HIGH(on). Value -100 to 100 in steps of 5.
+               dr[6] = D/R value for switch # 1 LOW(off). Value -100 to 100 in steps of 5.
+               dr[7] = D/R value for switch # 1 HIGH(on). Value -100 to 100 in steps of 5. 
+       */
+};
+volatile model_t model;
+unsigned char current_model; // Using uchar to spend a single byte of mem..
 
 // ----------------- Display related stuffs --------------------
 LiquidCrystal lcd( 12, 11, 10,     6,  7,  8,  9);
@@ -26,20 +77,22 @@ LiquidCrystal lcd( 12, 11, 10,     6,  7,  8,  9);
 // ----------------- PPM related stuffs ------------------------
 // The PPM generation is handled by Timer0 interrupts, and needs
 // all modifiable variables to be global and volatile...
-int max_channels = 6;                   // How many channels should PPM generate ...
+
 volatile long sum = 0;                 // Frame-time spent so far
 volatile int cchannel = 0;             // Current channnel
 volatile bool do_channel = true;        // Is next operation a channel or a separator
 
+
 // All time values in usecs
 // TODO:
 // The timing here (and/or in the ISR) needs to be tweaked to provide valid
 // RC PPM signals accepted by standard RC RX'es and the Microcopter...
-long framelength = 21500;              // Max length of frame
-long seplength = 400;                  // Lenght of a channel separator
-long chmax = 1700;                     // Max lenght of channel pulse
-long chmin = 600;                      // Min length of channel
-long chwidht = (chmax - chmin);                // Useable time of channel pulse (1000)
+
+#define framelength  21000             // Max length of frame
+#define seplength      300             // Lenght of a channel separator
+#define chmax         1600             // Max lenght of channel pulse
+#define chmin          495             // Min length of channel
+#define chwidht  (chmax - chmin)// Useable time of channel pulse
 
 // ----------------- Menu/IU related stuffs --------------------
 
@@ -57,12 +110,21 @@ long chwidht = (chmax - chmin);            // Useable time of channel pulse (1000)
 
 // Voltage sense pin is connected to a 1/3'd voltage divider.
 #define BATTERY_CONV (10 * 3 * (5.0f/1024.0f))
+
+#ifdef DEBUG
+// The following values are for DEBUGGING ONLY!!
 #define BATTERY_LOW 92
+#define BATTERY_CRITICAL 0
+#else
+#define BATTERY_LOW 92
+#define BATTERY_CRITICAL 92
+#endif
 
 enum {
   VALUES,
   BATTERY,
   TIMER,
+  CURMODEL,
   MENU
 } 
 displaystate;
@@ -72,7 +134,7 @@ enum {
   INVERTS,
   DUALRATES,
   EXPOS, // Some radios have "drawn curves", i.e. loopup tables stored in external EEPROM ...
-  DEBUG,
+  DEBUG_DUMP,
   SAVE
 } 
 menu_mainstate;
@@ -88,16 +150,20 @@ int battery_val;
 #define UI_INTERVAL 250
 unsigned long last = 0;
 
-unsigned long timer_start = 0;
-unsigned long timer_init  = 0;
-unsigned long timer_value = 0;
-boolean timer_running = false;
+struct clock_timer_t 
+{
+       unsigned long start;
+       unsigned long init;
+       unsigned long value;
+       boolean running;
+} clock_timer;
 
+#ifdef DEBUG
 // -----------------  DEBUG-STUFF --------------------
 unsigned long prev_loop_time;
 unsigned long avg_loop_time;
 unsigned long t;
-
+#endif
 
 // ---------- CODE! -----------------------------------
 
@@ -116,16 +182,9 @@ void setup(){
   Serial.begin(9600);
   Serial.println("Starting....");
   delay(500);
+  
+  model_defaults();
   read_settings();
-  scan_keys();
-  if ( keys[KEY_UP])
-    calibrate();
-
-  pinMode(A5, OUTPUT);  // PPM output pin  
-  do_channel = false;
-  set_timer( seplength );
-  Timer1.initialize(framelength);
-  Timer1.attachInterrupt(ISR_timer);  
 
   displaystate = VALUES;
   
@@ -133,27 +192,55 @@ void setup(){
   // In reality they are tri-purpose; ADC, Digital, Digital Interrupts
   // Unfortunately the interrupt mode is unusable in this scenario, but digital I/O works :P
   pinMode(A2, INPUT); 
+  digitalWrite(A2, HIGH);
+  scan_keys();
+  if ( !keys[KEY_UP])
+    calibrate();
   
+#ifdef DEBUG
   // Debugging: how long does the main loop take on avg...  
   t = micros();
   avg_loop_time = t;
   prev_loop_time = t;    
+#endif
   
-  dr[0] = dr[1] = dr[2] = dr[3] = 0;
-  dr[4] = dr[5] = dr[6] = dr[7] = 100;
+  // Initializing the stopwatch timer/clock values...
+  clock_timer = (clock_timer_t){0, 0, 0, false};
 
+  pinMode(A5, OUTPUT);  // PPM output pin  
+  do_channel = false;
+  set_timer( seplength );
+  Timer1.initialize(framelength);
+  Timer1.attachInterrupt(ISR_timer);  
+  
 }
 
-// ---------- Arduino main loop -----------------------
-void loop () {
+void model_defaults( void )
+{
+  // This function provides default values for model data
+  // that is not a result of stick input, or in other words:
+  // provides defautls for all user-configurable model options.
   
-  // Determine if the UI needs to run...
-  boolean disp;
-  if ( millis() - last > UI_INTERVAL ) { 
-    last = millis(); 
-    disp = true; 
-  }
-  else disp = false;
+  // Remember to update this when a new option/element is added
+  // to the model_t struct (preferably before implementing the
+  // menu code that sets those options ...)
+
+  // This is used when a user wants a new, blank model, a reset
+  // of a configured model, and (most important) when EEPROM
+  // data format changes.
+  // NOTE: This means that stored model conficuration is reset
+  // to defaults when the EEPROM version/format changes.
+  model.channels = 8;
+  model.rev[0] = model.rev[1] = model.rev[2] = model.rev[3] = 
+  model.rev[4] = model.rev[5] = model.rev[6] = model.rev[7] = false;
+  model.dr[0] = model.dr[1] = model.dr[2] = model.dr[3] = 0;
+  model.dr[4] = model.dr[5] = model.dr[6] = model.dr[7] = 100;
+
+}
+
+// ---------- Arduino main loop -----------------------
+void loop ()
+{  
 
   process_inputs();
 
@@ -161,13 +248,18 @@ void loop () {
   battery_val = analogRead(1) * BATTERY_CONV;
   if ( battery_val < BATTERY_LOW ) {
     digitalWrite(13, 1); // Simulate alarm :P
+  }
+  if ( battery_val < BATTERY_CRITICAL ) {
     displaystate = BATTERY;
   }
 
-  if ( disp )
-  {
+  if ( millis() - last > UI_INTERVAL ) 
+  { 
+    last = millis(); 
     ui_handler(); 
   }
+  
+#ifdef DEBUG
   if ( displaystate != MENU )
   {
     // Debugging: how long does the main loop take on avg,
@@ -176,6 +268,7 @@ void loop () {
     avg_loop_time = ( t - prev_loop_time + avg_loop_time ) / 2;
     prev_loop_time = t;    
   }
+#endif
 
   // Whoa! Slow down partner! Let everything settle down before proceeding.
   delay(5);
@@ -196,7 +289,7 @@ void set_timer(long time)
 
 boolean check_key( int key)
 {
-  return ( keys[key] && !prev_keys[key] );
+  return ( !keys[key] && prev_keys[key] );
 }
 
 void mplx_select(int pin)
@@ -219,8 +312,7 @@ void mplx_select(int pin)
 
 void calibrate()
 {
-  int i, r0, r1, r2, adc_in;
-  int calcount = 0;
+  int i, adc_in;
   int num_calibrations = 200;
 
   lcd.clear();
@@ -229,62 +321,249 @@ void calibrate()
   lcd.print("their extremes..");
   Serial.print("Calibration. Move all controls to their extremes.");
 
-  for (i=0; i<=7; i++) {
-    minval[i] = 1024;
-    maxval[i] = 0;
+  for (i=0; i<MAX_INPUTS; i++) {
+    input_cal.min[i] = 1024;
+       input_cal.center[i] = 512;
+    input_cal.max[i] = 0;
   }
-  while ( calcount <= num_calibrations )
+
+  while ( num_calibrations-- )
   {
-    for (i=0; i<=7; i++) {
+    for (i=0; i<MAX_INPUTS; i++) {
       mplx_select(i);
       adc_in = analogRead(0);
 
       // Naive min/max calibration
-      if ( adc_in < minval[i] ) {
-        minval[i] = adc_in;
+      if ( adc_in < input_cal.min[i] ) {
+        input_cal.min[i] = adc_in;
       }
-      if ( adc_in > maxval[i] ) {
-        maxval[i] = adc_in;
+      if ( adc_in > input_cal.max[i] ) {
+        input_cal.max[i] = adc_in;
       }
       delay(10);
     }
-
-    calcount++;
   }
 
   // TODO: WILL need to do center-point calibration after min-max...
 
   lcd.clear();
+  lcd.print("Saving to EEPROM");
+  write_calibration();
+  lcd.setCursor(0 , 1);
   lcd.print("Done calibrating");
-
+  
   Serial.print("Done calibrating");
   delay(2000);  
 }
 
+void write_calibration(void)
+{
+  int i;
+  unsigned char v;
+  const byte *p;
+  
+  // Set p to be a pointer to the start of the input calibration struct.
+  p = (const byte*)(const void*)&input_cal;
+  
+  // Iterate through the bytes of the struct...
+  for (i = 0; i < sizeof(input_cal_t); i++)
+  {
+    // Get a byte of data from the struct...
+       v = (unsigned char) *p;
+       // write it to EEPROM
+       EEPROM.write( EE_BASE_ADDR + i, v);
+       // and move the pointer to the next byte in the struct.
+       *p++;
+  }
+}
+
 void read_settings(void)
 {
-  // Dummy. Will be modified to read model settings from EEPROM
-  for (int i=0; i<=7; i++) {
-    minval[i] = 0;
-    maxval[i] = 1024;
+  int i;
+  unsigned char v;
+  byte *p;
+
+  v = EEPROM.read(0);
+  if ( v != EEPROM_VERSION )
+  {
+       // All models have been reset. Set the current model to 0
+       current_model = 0;
+       EEPROM.write(1, current_model);
+
+       calibrate();
+       model_defaults();
+       // The following does not yet work...
+       for ( i = 0; i < MAX_MODELS; i++)
+               write_model_settings(i);
+
+       
+       // After saving calibration data and model defaults,
+       // update the saved version-identifier to the current ver.
+       EEPROM.write(0, EEPROM_VERSION);
+  }
+
+  // Read calibration values from EEPROM.
+  // This uses simple pointer-arithmetic and byte-by-byte
+  // to put bytes read from EEPROM to the data-struct.
+  p = (byte*)(void*)&input_cal;
+  for (i = 0; i < sizeof(input_cal_t); i++)
+               *p++ = EEPROM.read( EE_BASE_ADDR + i);
+
+  // Get the previously selected model from EEPROM.
+  current_model = EEPROM.read(1);
+  read_model_settings( current_model );
+}
+
+void read_model_settings(unsigned char mod_no)
+{
+  int model_address;
+  int i;
+  unsigned char v;
+  byte *p;
+
+  // Calculate the EEPROM start adress for the given model (mod_no)
+  model_address = EE_MDL_BASE_ADDR + (mod_no * sizeof(model_t));
+
+  // Do not try to write the model to EEPROM if it won't fit.
+  if ( INT_EEPROM_SIZE < (model_address + sizeof(model_t)) )
+  {
+       lcd.clear();
+       lcd.print("Aborting READ");
+    lcd.setCursor(0 , 1);
+    lcd.print("Invalid location");
+    delay(2000);  
+       return;
   }
+  
+  lcd.clear();
+  lcd.print("Reading model ");
+  lcd.print( (int)mod_no );
+  
+  // Pointer to the start of the model_t data struct,
+  // used for byte-by-byte reading of data...
+  p = (byte*)(void*)&model;
+  for (i = 0; i < sizeof(model_t); i++)
+               *p++ = EEPROM.read( model_address++ );
+
+#ifdef DEBUG
+  serial_dump_model();
+#endif
+
+  lcd.setCursor(0 , 1);
+  lcd.print("... Loaded.");
+  delay(1000);    
 }
 
-void write_settings(void)
+void write_model_settings(unsigned char mod_no)
 {
-  // Dummy. Not used anywhere. Will be fleshed out to save settings to EEPROM.
+  int model_address;
+  int i;
+  unsigned char v;
+  byte *p;
+
+  // Calculate the EEPROM start adress for the given model (mod_no)
+  model_address = EE_MDL_BASE_ADDR + (mod_no * sizeof(model_t));
+  
+  // Do not try to write the model to EEPROM if it won't fit.
+  if ( INT_EEPROM_SIZE < (model_address + sizeof(model_t)) )
+  {
+       lcd.clear();
+       lcd.print("Aborting SAVE");
+    lcd.setCursor(0 , 1);
+    lcd.print("No room for data");
+    delay(2000);  
+       return;
+  }
+  
+  lcd.clear();
+  lcd.print("Saving model ");
+  lcd.print( (int)mod_no);
+  
+  // Pointer to the start of the model_t data struct,
+  // used for byte-by-byte reading of data...
+  p = (byte*)(void*)&model;
+  
+  // Write/serialize the model data struct to EEPROM...
+  for (i = 0; i < sizeof(model_t); i++)
+               EEPROM.write( model_address++, *p++);
+               
+  lcd.setCursor(0 , 1);
+  lcd.print(".. done saving.");
+  delay(200);    
+}
+
+#ifdef DEBUG
+void serial_dump_model ( void )
+{
+  int i;
+  int model_address;
+  // Calculate the EEPROM start adress for the given model (mod_no)
+  model_address = EE_MDL_BASE_ADDR + (current_model * sizeof(model_t));
+  Serial.print("Current model:    ");
+  Serial.println( (int)current_model );
+  Serial.print("Models base addr: ");
+  Serial.println( EE_MDL_BASE_ADDR );
+  Serial.print("Model no:         ");
+  Serial.println( current_model, 10 );
+  Serial.print("Size of struct:   ");
+  Serial.println( sizeof( model_t) );
+  Serial.print("Model address:    ");
+  Serial.println( model_address );
+  Serial.print("End of model:     ");
+  Serial.println( model_address + sizeof(model_t) );
+
+  Serial.println();
+  
+  Serial.print("Channel reversions: ");
+  for ( i = 0; i<8; i++)
+  {
+       Serial.print(i);
+       Serial.print("=");
+       Serial.print(model.rev[i], 10);
+       Serial.print(" ");
+  }
+  Serial.println();
+  
+  Serial.print("DR1 inp 0: ");
+  Serial.println(model.dr[0]);
+  Serial.print("DR1 inp 1: ");
+  Serial.println(model.dr[1]);
+  Serial.print("DR1 LO val: ");
+  Serial.println(model.dr[4]);
+  Serial.print("DR1 HI val: ");
+  Serial.println(model.dr[5]);
+  Serial.print("DR2 inp 0: ");
+  Serial.println(model.dr[2]);
+  Serial.print("DR2 inp 1: ");
+  Serial.println(model.dr[3]);
+  Serial.print("DR2 LO val: ");
+  Serial.println(model.dr[6]);
+  Serial.print("DR2 HI val: ");
+  Serial.println(model.dr[7]);
+  
+  for (i=0; i<MAX_INPUTS; i++) {
+    Serial.print("Input #");
+    Serial.print(i);
+    Serial.print(" pct: ");
+    Serial.print(model.stick[i]);
+    Serial.print(" min: ");
+    Serial.print(input_cal.min[i]);
+    Serial.print(" max: ");
+    Serial.print(input_cal.max[i]);
+    Serial.println();
+  }  
 }
+#endif
 
 void scan_keys ( void )
 {
-  int i, r0, r1, r2;
   boolean key_in;
 
   // To get more inputs, another 4051 analog multiplexer is used,
   // but this time it is used for digital inputs. 8 digital inputs
   // on one input line, as long as proper debouncing and filtering
   // is done in hardware :P
-  for (i=0; i<=7; i++) {
+  for (int i=0; i<=7; i++) {
     // To be able to detect that a key has changed state, preserve the previous..
     prev_keys[i] = keys[i];
 
@@ -298,14 +577,69 @@ void scan_keys ( void )
 
 void process_inputs(void )
 {
-  int current_input, r0, r1, r2, adc_in;
-  for (current_input=0; current_input<=7; current_input++) {
+  int current_input, adc_in, fact;
+  float min, max;
+
+  for (current_input=0; current_input<MAX_INPUTS; current_input++) {
 
     mplx_select(current_input);
     adc_in = analogRead(0);
 
-    channel[current_input] = ((float)adc_in - (float)minval[current_input]) / (float)(maxval[current_input]-minval[current_input]);    
-    if ( rev[current_input] ) channel[current_input] = 1.0f - channel[current_input];  
+       model.raw[current_input] = adc_in;
+       // New format on stick values
+       // The calculations happen around the center point, the values
+       // need to arrive at 0...100 of the range "center-to-edge",
+       // and must end up as negative on the ... negative side of center.
+       
+    if ( adc_in < input_cal.center[current_input] )
+    {
+                       // The stick is on the negative side, so the range is
+                       // from the lowest possible value to center, and we must
+                       // make this a negative percentage value.
+            max = input_cal.min[current_input];
+            min = input_cal.center[current_input];
+                       fact = -100;
+    } 
+    else 
+    {
+                       // The stick is at center, or on the positive side.
+                       // Thus, the range is from center to max, and
+                       // we need positive percentages.
+            min = input_cal.center[current_input];
+            max = input_cal.max[current_input];
+                       fact = 100;
+    }
+       // Calculate the percentage that the current stick position is at
+       // in the given range, referenced to or from center, depending :P
+    model.stick[current_input] =  fact * ((float)adc_in - min ) / (max - min);
+       
+       // If this input is configured to be reversed, simply do a sign-flip :D
+    if ( model.rev[current_input] ) model.stick[current_input] *= -1;
+
+    // Dual-rate calculation :D
+    // This is very repetitive code. It should be fast, but it may waste code-space.
+    float dr_val;
+    // Test to see if dualrate-switch #1 applies to channel...
+    if ( ( current_input == ( model.dr[0]-1) ) || ( current_input == ( model.dr[1]-1) ) )
+    {
+            if ( !keys[KEY_DR1] )
+                    dr_val = ((float)model.dr[4])/100.0; 
+            else
+                    dr_val = ((float)model.dr[5])/100.0;
+
+            model.stick[current_input] *= dr_val; 
+    }
+    else
+    // Test to see if dualrate-switch #1 applies to channel...
+    if ( ( current_input == ( model.dr[2]-1) ) || ( current_input == ( model.dr[3]-1) ) )
+    {
+            if ( !keys[KEY_DR1] )
+                    dr_val = ((float)model.dr[6])/100.0; 
+            else
+                    dr_val = ((float)model.dr[7])/100.0;
+
+            model.stick[current_input] *= dr_val; 
+    }
   }
 }
 
@@ -323,7 +657,7 @@ void ISR_timer(void)
     return;
   }
 
-  if ( cchannel >= max_channels )
+  if ( cchannel >= model.channels )
   {
     set_ppm_output( HIGH );
     long framesep = framelength - sum;
@@ -338,9 +672,19 @@ void ISR_timer(void)
   if ( do_channel )
   {
     set_ppm_output( HIGH );
-    long next_timer = (( chwidht * channel[cchannel] ) + chmin);
-    // Do sanity-check of next_timer compared to chmax ...
+
+       // New format on stick values
+    // model.stick contains percentages, -100% to 100% in float. To make the timer-handling
+    // here as simple as possible. We want to calc the channel value as a  "ratio-value",
+    // a float in the range 0..1.0. So, by moving the lower bound to 0, then cutting the
+    // range in half, and finally dividing by 100, we should get the ratio value.
+    // Some loss of presicion occurs, perhaps the algo' should be reconsidered :P
+    long next_timer = (( chwidht * ((model.stick[cchannel]+100)/200) ) + chmin);
+    // Do sanity-check of next_timer compared to chmax and chmin...
     while ( chmax < next_timer ) next_timer--;
+    while ( next_timer < chmin ) next_timer++;
+       
+       // Update the sum of elapsed time
     sum += next_timer;
 
     // Done with channel separator and value,
@@ -352,22 +696,22 @@ void ISR_timer(void)
   }
 }
 
+#ifdef DEBUG
 void serial_debug()
 {
   int current_input;
-  for (current_input=0; current_input<=7; current_input++) {
-    int v = (int)(channel[current_input] * 100);
+  for (current_input=0; current_input<MAX_INPUTS; current_input++) {
 
     Serial.print("Input #");
     Serial.print(current_input);
-    Serial.print(" value: ");
-    Serial.print(channel[current_input]);
     Serial.print(" pct: ");
-    Serial.print(v);
+    Serial.print(model.stick[current_input]);
+    Serial.print(" raw value: ");
+    Serial.print(model.raw[current_input]);
     Serial.print(" min: ");
-    Serial.print(minval[current_input]);
+    Serial.print(input_cal.min[current_input]);
     Serial.print(" max: ");
-    Serial.print(maxval[current_input]);
+    Serial.print(input_cal.max[current_input]);
     Serial.println();
   }
   Serial.print("Battery level is: ");
@@ -378,10 +722,12 @@ void serial_debug()
 
   Serial.println();
 }
+#endif
+
 void dr_inputselect( int no, int in )
 {
-       if ( dr[menu_substate] < 0 ) dr[menu_substate] = 4;
-       if ( dr[menu_substate] > 4 ) dr[menu_substate] = 0;
+       if ( model.dr[menu_substate] < 0 ) model.dr[menu_substate] = 4;
+       if ( model.dr[menu_substate] > 4 ) model.dr[menu_substate] = 0;
 
        lcd.setCursor(0 , 0);
        lcd.print("D/R switch ");
@@ -392,15 +738,15 @@ void dr_inputselect( int no, int in )
        lcd.print(in+1);
        
        lcd.print(":  ");
-       if ( ! dr[menu_substate] ) lcd.print("Off");
-       else lcd.print(dr[menu_substate]);
+       if ( ! model.dr[menu_substate] ) lcd.print("Off");
+       else lcd.print(model.dr[menu_substate]);
        
        if ( check_key(KEY_INC) ) {
-               dr[menu_substate]++;
+               model.dr[menu_substate]++;
                return;
        }
        else if ( check_key(KEY_DEC) ) {
-               dr[menu_substate]--;
+               model.dr[menu_substate]--;
                return;
        }
        // Wrap around.
@@ -427,14 +773,14 @@ void dr_value()
        lcd.print( state ? "HI" : "LO" );
        lcd.print(" Value :");
        
-       lcd.print( dr[pos] );
+       lcd.print( model.dr[pos] );
        
-       if ( keys[KEY_INC] ) {
-               if ( dr[pos] < 100) dr[pos] += 5;
+       if ( !keys[KEY_INC] ) {
+               if ( model.dr[pos] < 100) model.dr[pos] += 5;
                return;
        }
-       else if ( keys[KEY_DEC] ) {
-               if ( dr[pos] > -100) dr[pos] -= 5;
+       else if ( !keys[KEY_DEC] ) {
+               if ( model.dr[pos] > -100) model.dr[pos] -= 5;
                return;
        }
        
@@ -458,6 +804,10 @@ void ui_handler()
       return; 
     }
     else if ( check_key(KEY_UP) && displaystate == TIMER ) { 
+      displaystate = CURMODEL; 
+      return; 
+    }
+    else if ( check_key(KEY_UP) && displaystate == CURMODEL ) { 
       displaystate = VALUES; 
       return; 
     }
@@ -474,7 +824,7 @@ void ui_handler()
   {
     case VALUES:
       int current_input;
-      for (current_input=0; current_input<=7; current_input++) {
+      for (current_input=0; current_input<MAX_INPUTS; current_input++) {
         // In channel value display, do a simple calc
         // of the LCD row & column location. With 8 channels
         // we can fit eight channels as percentage values on
@@ -496,8 +846,8 @@ void ui_handler()
         lcd.print("    ");
         lcd.setCursor(col, row);
         // Display uses percents, while PPM uses ratio....
-        int v = (int)(channel[current_input] * 100);
-        lcd.print(v);
+               // New format on stick values
+               lcd.print( (int)model.stick[current_input] );
       }
       break;
 
@@ -522,16 +872,16 @@ void ui_handler()
                
                lcd.clear();
                lcd.print("Timer: ");
-               lcd.print( timer_running ? "Running" : "Stopped" );
+               lcd.print( clock_timer.running ? "Running" : "Stopped" );
                lcd.setCursor(5 , 1);
-               if ( timer_running )
+               if ( clock_timer.running )
                {
-                       timer_value = millis() - (timer_start + timer_init);
+                       clock_timer.value = millis() - (clock_timer.start + clock_timer.init);
                }
-               hours = ( timer_value / 1000 ) / 3600;
-               timer_value = timer_value % 3600000;
-               minutes = ( timer_value / 1000 ) / 60;
-               seconds = ( timer_value / 1000 ) % 60;
+               hours = ( clock_timer.value / 1000 ) / 3600;
+               clock_timer.value = clock_timer.value % 3600000;
+               minutes = ( clock_timer.value / 1000 ) / 60;
+               seconds = ( clock_timer.value / 1000 ) % 60;
                if ( hours ) {
                        lcd.print(hours);
                        lcd.print(":");
@@ -543,28 +893,40 @@ void ui_handler()
                lcd.print( seconds );
                
         if ( check_key(KEY_INC) ) { 
-                       if ( !timer_running && !timer_start )
+                       if ( !clock_timer.running && !clock_timer.start )
                        {
-                               timer_start = millis();
-                               timer_value = 0;
-                               timer_running = true;
-                       } else if ( !timer_running && timer_start ) {
-                               timer_start = millis() - timer_value;
-                               timer_running = true;
-                       } else if ( timer_running ) {
-                               timer_running = false;
+                               clock_timer.start = millis();
+                               clock_timer.value = 0;
+                               clock_timer.running = true;
+                       } else if ( !clock_timer.running && clock_timer.start ) {
+                               clock_timer.start = millis() - clock_timer.value;
+                               clock_timer.running = true;
+                       } else if ( clock_timer.running ) {
+                               clock_timer.running = false;
                        }
                        return; 
         } else if ( check_key(KEY_DEC) ) { 
-                       if ( !timer_running && timer_start ) {
-                               timer_value = 0;
-                               timer_start = 0;
-                               timer_init = 0;
+                       if ( !clock_timer.running && clock_timer.start ) {
+                               clock_timer.value = 0;
+                               clock_timer.start = 0;
+                               clock_timer.init = 0;
                        }
                        return; 
         }
                break;
 
+
+               
+       case CURMODEL:    
+      lcd.clear();
+      lcd.print("Model #: ");
+         lcd.print( (int)current_model );
+      lcd.setCursor(0 , 1);
+      lcd.print("NAME (not impl)");
+      break;
+
+
+         
     case MENU:
       lcd.clear();
       switch ( menu_mainstate )
@@ -586,13 +948,13 @@ void ui_handler()
     
     
         case INVERTS:
-          if ( menu_substate >= max_channels ) menu_substate = 0;
-          if ( menu_substate < 0) menu_substate = (max_channels - 1);
+          if ( menu_substate >= model.channels ) menu_substate = 0;
+          if ( menu_substate < 0) menu_substate = (model.channels - 1);
           lcd.print("Channel invert");
           lcd.setCursor(0 , 1);
           lcd.print("Ch ");
           lcd.print(menu_substate+1);
-          lcd.print( (rev[menu_substate] ?  ": Invert" : ": Normal"));
+          lcd.print( (model.rev[menu_substate] ?  ": Invert" : ": Normal"));
 
           if ( check_key(KEY_UP) ) { 
             menu_mainstate = TOP; 
@@ -612,7 +974,7 @@ void ui_handler()
             return; 
           }
           else if ( check_key(KEY_INC) || check_key(KEY_DEC) ) { 
-            rev[menu_substate] ^= 1; 
+            model.rev[menu_substate] ^= 1; 
             return; 
           }
           break;
@@ -671,17 +1033,30 @@ void ui_handler()
           // Run in wolfram to see result, adjust the 1.0 factor to inc/red effect.
           // Problem: -100 to 100 is terribly bad presicion, esp. considering that
           // the values started as 0...1024, and we have 1000usec to "spend" on channels.
+                 
+                 // NEW IDEA provided my ivarf @ hig: use bezier curves og hermite curves!
+                 // Looks like a promising idea, but the implementation is still a bitt off
+                 // on the time-horizon :P
           if ( check_key(KEY_UP ) ) { 
             menu_mainstate = DUALRATES; 
             return; 
           }          
+#ifdef DEBUG          
           if ( check_key(KEY_DOWN ) ) {
-            menu_mainstate = DEBUG;
+            menu_mainstate = DEBUG_DUMP;
             return;
           }
+#else
+          if ( check_key(KEY_DOWN ) ) {
+            menu_mainstate = TOP;
+            return;
+          }
+
+#endif
           break;
-          
-        case DEBUG:
+
+#ifdef DEBUG          
+        case DEBUG_DUMP:
           lcd.setCursor(0 , 0);
           lcd.print("Dumping debug to");
           lcd.setCursor(0 , 1);
@@ -696,7 +1071,7 @@ void ui_handler()
             return;
           }
           break;
-    
+#endif    
         default:
           lcd.print("Not implemented");
           lcd.setCursor(0 , 1);
@@ -717,4 +1092,4 @@ void ui_handler()
 
 
 
-\r
+