/*
 *
 *   Arduinowindensteuerung Aws 
 *
 */

#include <Arduino.h>
#include <EEPROM.h>
#include <Wire.h>

#include "Aws.h"
#include "AwsPins.h"
#include "AwsHandle.h"
#include "AwsLCD.h"
#include "AwsRamp.h"
#include "AwsRotaryAdapter.h"
#include "AwsEEPROM.h"

// Global state Variables

/*
 * The selected program
 */
int8_t program = 0;

/*
 * Data of program from EEPROM (modifiable by EDITNAME, EDITSPEED, EDITWEIGHT)
 */
planedata selectedPlane;
/*
 * Lock status of selected program. Lock required before using the Joystick
 */
boolean locked = false;

/*
 * The selected wind speed
 */
int8_t wind = 0;
/*
 * The state of the Joystick
 * values are STATE_RESET, STATE_MANUAL, STATE_START and STATE_ILLEGAL
 */
uint8_t state = STATE_RESET;
/*
 * editMode describes the action that can be performed with the rotary encoders
 * values are NOEDIT, EDITNAME, EDITSPEED, EDITWEIGHT, EDITSAVE
 */
uint8_t editMode = NOEDIT;

// Controller for rotary sensors
AwsRotaryAdapter rotary(&selectedPlane);
// Ramp Controller
AwsRamp ramp;
// LCD Controller
AwsLCD awsLcd;
// Start-Stop Controler
AwsHandle handle;


/** 
 * Initialize hardware
 * Executed once after boot
*/

static int startC=START_CONSTANTS;
static int planecnt=0;

void sc(int16_t value){
  EEPROM.put(startC,value);
  startC+=sizeof(value);
}

void sp(char* name, int16_t speed, int16_t weight){
  planedata plane;
  plane.speed=speed;
  plane.weight=weight;
  memcpy(plane.name,name,DISPLAY_WIDTH);
  EEPROM.put(START_PLANES+planecnt*sizeof(planedata),plane);
  planecnt++;
}

void spc(){
  EEPROM.put(0,(uint8_t)planecnt);
}

void setup() {
  // Serial

  DEBUG(Serial.begin(9600); Serial.println(F("Start"));)
  
  sp("20 Gruene Post      ", 1477   ,  1694);
  sp("21 Baby             ", 1527   ,  1794);
  sp("30 Ka8 K6 Zugvogel  ", 1572   ,  2146);
  sp("40 LS1 Libelle      ", 1857   ,  2200);
  sp("41 Astir            ", 1897   ,  2200);
  sp("50 LS4              ", 1934   ,  2200);
  sp("51 Discus 1         ", 1984   ,  2314);
  sp("52 Discus 2         ", 2034   ,  2294);
  sp("53 ASW28            ", 2044   ,  2314);
  sp("60 Discus Wasser    ", 2044   ,  2798);
  sp("70 ASK13 einsitzig  ", 1668   ,  2286);
  sp("80 ASK13 zweisitzig ", 1772   ,  2449);
  sp("90 ASK21            ", 1927   ,  2894);
  sp("99 Duo Discus       ", 2142   ,  2940);
  spc();
  sc(10);sc(14);sc(16);sc(25);
  sc(27);sc(-15);sc(40);sc(12);
  sc(24);sc(37);sc(49);sc(61);
  sc(73);sc(86);sc(98);sc(110);
  sc(122);sc(135);sc(147);sc(159);
  sc(172);sc(184);sc(196);sc(208);
  sc(220);sc(233);sc(245);sc(257);
  sc(269);sc(282);sc(294);sc(306);

  //LOCK, Buzzer and LED
  pinMode(LOCKPIN, INPUT_PULLUP); 
  pinMode(LED_PIN, OUTPUT);
  pinMode(Buzzer_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);
  digitalWrite(Buzzer_PIN, LOW);
  awsLcd.lcdInit();
  rotary.init();
  handle.init();
  DEBUG(Serial.println(F("Setup finished"));)
}

/**
 * aliveMessage for DEBUG only
 */
void aliveMessage() {
  DEBUG(long seconds = millis() / 15000;
        ONCHANGE(seconds, Serial.println(F("alive"));))
}
/**
 * show or hide Voltages incl wind and ramp reduction on demand
 */
void updateVoltagesDisplay() {
  boolean displayVolts = rotary.displayVoltage();
#if DISPLAY_WIDTH == 16
  static char *voltageHeader = (char *)"Ges      La";
#else
  static char *voltageHeader = (char *)"Gesch      Last";
#endif
  ONCHANGE(
      displayVolts,
      if (displayVolts) { awsLcd.showMessage(voltageHeader, 1, 0, 1); } else {
        awsLcd.updateLCDSpeed(0);
        awsLcd.updateLCDWeight(0);
        awsLcd.showMessage((char *)"                ", 1, 0, 1);
      })

  if (displayVolts) {
    int16_t effectiveSpeed =
        selectedPlane.speed - (editMode == NOEDIT ? WIND_FACTOR * wind : 0);
    awsLcd.updateLCDSpeed(effectiveSpeed > 0 ? (uint16_t)effectiveSpeed : 0);
    int16_t reducedWeight = selectedPlane.weight -
                            (editMode == NOEDIT ? ramp.getRampReduction() : 0);
    awsLcd.updateLCDWeight(reducedWeight > 0 ? (uint16_t)reducedWeight : 0);
  }
}
/**
 * updateLockDisplay aktualisiert die Anzeige der
 * LED und des Lock Symbols im LCD Display
 */
void updateLockDisplay() {
  // Set locked marker in lcd display and LED
  ONCHANGE(
      locked,
      if (locked) {
        awsLcd.showMessage((char *)"L", 0, DISPLAY_WIDTH - 1, 0);
        digitalWrite(LED_PIN, LOW);
        ramp.init(wind, selectedPlane);
      } else {
        awsLcd.showMessage((char *)" ", 0, DISPLAY_WIDTH - 1, 0);
        digitalWrite(LED_PIN, HIGH);
        ramp.stop();
      })
}
/**
 * updateRamp aktualisiert den Zustand der Rampe
 * und startet oder stoppt die Rampe
 * je nach Zustand
 */
void updateRamp() {
  // things to be done once on change
  ONCHANGE(
      state,
      DEBUG(Serial.print(F("Handle state changed to "));
            Serial.println(state);) if (state == STATE_RESET) {
        rotary.enable();
        if (ramp.isRunning()) awsLcd.unToggleProgressIndicator();
        ramp.stop();
        digitalWrite(Buzzer_PIN, LOW);
        if (locked) {
          ramp.init(wind, selectedPlane);
        }
      } else {
        rotary.disable();
        if (locked && !ramp.isRunning()) {
          ramp.prepareStart();
        }
        if ((!locked) || state == STATE_ILLEGAL) {
          digitalWrite(Buzzer_PIN, HIGH);
          DEBUG(char buf[16]; snprintf(buf, sizeof(buf), "%s %d",
                                       locked ? "locked" : "unlocked", state);
                Serial.println(buf);)
        }
      })  // END ONCHANGE STATE

  if (ramp.isRunning()) {  // if ramp not stopped State must be MANUAL and not
                           // RESET keep on truning progress indicator, ramp
                           // may be resumed
    awsLcd.toggleProgressIndicator();
  }
  if ((state == STATE_START) && locked) {
    ramp.run();
  }
}

/**
 *
 * mainMenu
 *
 * Main Menu to Control UI using rotary sensors
 *
 */
void mainMenu() {
  aliveMessage();
  ONCHANGE(
      editMode, DEBUG(Serial.print("editMode Changed");
                      Serial.println(editMode);) switch (editMode) {
        case NOEDIT:
          switch (rotary.terminateEditMode()) {
            case EDITINSERT:
              insertPlaneIntoEEPROM(program, selectedPlane);
              rotary.init(); //numPlanes changed
              break;
            case EDITSAVE:
              storePlaneInEEPROM(program, selectedPlane);
              break;
            case EDITDELETE:
              deletePlaneFromEEPROM(program);
              rotary.init(); //numPlanes changed
            default:
              break;
          };
          awsLcd.showMessage((char *)" ", 0, DISPLAY_WIDTH - 1, 0);
          break;
        case EDITNAME:
          awsLcd.showMessage((char *)"1", 0, DISPLAY_WIDTH - 1, 0);
          updateVoltagesDisplay();  // No Wind component required
          break;
        case EDITSPEED:
          awsLcd.showMessage((char *)"2", 0, DISPLAY_WIDTH - 1, 0);
          awsLcd.blink(1, 0);
          break;
        case EDITWEIGHT:
          awsLcd.showMessage((char *)"3", 0, DISPLAY_WIDTH - 1, 0);
          awsLcd.blink(1, 8);
          break;
        case EDITSAVE:
          awsLcd.noblink();
          awsLcd.showMessage((char *)"S", 0, DISPLAY_WIDTH - 1, 0);
          awsLcd.showMessage((char *)"SAVE?                    ", 0, 0, 1);
          break;
        case EDITINSERT:
          awsLcd.showMessage((char *)"INSERT?                  ", 0, 0, 1);
          break;
        case EDITDELETE:
          awsLcd.showMessage((char *)"DELETE?                  ", 0, 0, 1);
          break;
        case EDITCANCEL:
          awsLcd.showMessage((char *)"CANCEL?                  ", 0, 0, 1);
          break;
      })
  switch (editMode) {
    case NOEDIT: {
      ONCHANGE(program, getPlaneFromEEPROM(program, selectedPlane);
               char line[DISPLAY_WIDTH + 1]; line[DISPLAY_WIDTH] = 0x00;
               strncpy(line, selectedPlane.name, DISPLAY_WIDTH);
               awsLcd.showMessage(line, 0, 0, 1);)
      // LCD Update only if Changes happened
      updateVoltagesDisplay();
      awsLcd.updateLCDWind(wind);
      updateLockDisplay();
      updateRamp();
    } break;
    case EDITNAME: {
      int8_t editPos = rotary.getEditPos();
      int16_t hash = 0;
      for (uint8_t i = 0; i < sizeof(selectedPlane.name); i++)
        hash += selectedPlane.name[i];
      ONCHANGE(hash, char line[DISPLAY_WIDTH + 1]; line[DISPLAY_WIDTH] = 0x00;
               strncpy(line, selectedPlane.name, DISPLAY_WIDTH);
               awsLcd.showMessage(line, 0, 0, 1);
               awsLcd.blink(0, editPos);)  // END ONCHANGE hash

      ONCHANGE(editPos, awsLcd.blink(0, editPos);)
    } break;
    case EDITSPEED:
      awsLcd.blink(1, 0);
      updateVoltagesDisplay();
      break;
    case EDITWEIGHT:
      awsLcd.blink(1, 9);
      updateVoltagesDisplay();
      break;
    case EDITSAVE:
      break;
  }
}

void computeNewState() {
  rotary.update();  // Poll rotary sensors and store status
  program = rotary.getProgram();
  locked = rotary.isLocked();
  wind = rotary.getWind();
  state = handle.getState();
  editMode = rotary.editMode();
}

void loop() {
  computeNewState();
  mainMenu();
}
$abcdeabcde151015202530fghijfghij
mcp4822Breakout
Edit Lock
Reset
Start
inverterBreakout
Buzzer Alarm
Programm Sperre Rot: Kein Start möglich, Programmwahl möglich Grün: Start möglich, Programmwahl und Windkomponente gesperrt.
Programmanzeige
Windanzeige
Drehregler Programmwahl
Drehregler Windkomponente
Bedienungsanleitung 1) Mit Drehregler Programmwahl den Flugzeugtyp einstellen. 2) Mit Drehregler Windkomponente diese einstellen. 3) Sperren der Einstellungen durch Druck auf Drehregler Programmwahl. L (Lock) deutet die Sperre an. Grüne LED leuchtet. 4) Schieberegler Reset nach rechts. 5) Schieberegler Start rechts. 6) Aktivitätsanzeiger in der Programmanzeige läuft. Die Spannungsvorgaben werden in der Ausgabe angezeigt. 7) Schieberegler Start auf Ausgangsposition (links). 8) Schieberegler Reset auf Ausgangsposition (links). 9) Sperre durch Druck auf Programmwahlregler aufheben. L verschwindet und rote LED leuchtet.