#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#include "HX711.h"
#include "Adafruit_Si7021.h"
#include "Adafruit_FRAM_SPI.h"

// Defines
#define TIMEOUTMS 60000
#define SENDDIFFTHRESHOLD 999
#define DONEPIN  A5
#define VBATPIN  A7
#define MAX_VALUES_TO_SEND 4
#define MAX_SHORT 32767
#define JOIN_AFTER_N_STARTUPS 100

// Enumerations
enum which {
 NWKSKEY,
 APPSKEY,
 DEVADDR,
 APPEUI,
 DEVEUI,
 APPKEY
};

// Constants
const uint8_t FRAM_CS = 10;

#if defined(ARDUINO_SAMD_ZERO) && defined(SERIAL_PORT_USBVIRTUAL)
// Required for Serial on Zero based boards
#define Serial SERIAL_PORT_USBVIRTUAL
#endif

typedef struct {
  band_t      bands[MAX_BANDS];
  u4_t        channelFreq[MAX_CHANNELS];
  u2_t        channelDrMap[MAX_CHANNELS];
  u2_t        channelMap;
  u4_t        seqnoUp;   
  u4_t        seqnoDn;     
  u1_t        datarate;
  u1_t        dn2Dr;
  u4_t        dn2Freq;
  s1_t        adrTxPow;
  u1_t        adrEnabled;
} SESSION_info;

// Data Types
typedef struct {
  // for ABP
  u1_t nwkskey[16];
  u1_t appskey[16];
  u4_t devaddr;

  // for OTAA
  u1_t appeui[8];   // Swisscom: F0:3D:29:AC:71:00:00:01
  u1_t deveui[8];
  u1_t appkey[16];  

  long cal_w1_0;
  long cal_w2_0;
  float cal_w1_factor;
  float cal_w2_factor;
  byte startup_counter[MAX_VALUES_TO_SEND];
  short weight[MAX_VALUES_TO_SEND];
  short temperature[MAX_VALUES_TO_SEND];
  byte my_position;
  byte logging; // 0: no, otherwise: yes
  byte current_startup_counter;

  // sesion info
  SESSION_info session_info;

} FRAM_data;

typedef struct {
  uint8_t version;                             // Versionierung des Paketformats
  uint8_t vbat;                                // Batteriespannung (1 Einheit => 20 mV)
  uint8_t startup_counter[MAX_VALUES_TO_SEND]; // wann die Messung gemacht wurde
  int16_t weight[MAX_VALUES_TO_SEND];          // Gewicht in 10-Gramm
  int16_t temperature[MAX_VALUES_TO_SEND];     // Temperatur in 1/10 Grad Celsius
} LORA_data;

// Global Variables
bool lora_data_sent = false;
bool usb_cable_attached = false;
FRAM_data fram_data;
LORA_data lora_data;
Adafruit_FRAM_SPI fram = Adafruit_FRAM_SPI(FRAM_CS);  // use hardware SPI
//static uint8_t mydata[] = "Hello, world!";
static osjob_t sendjob;
char mystring[100];

// Temp./Humidity Sensor
Adafruit_Si7021 temp_sensor;

// Scales
HX711 scale1;
HX711 scale2;

// These callbacks are only used in over-the-air activation, so they are
// left empty here (we cannot leave them out completely unless
// DISABLE_JOIN is set in config.h, otherwise the linker will complain).
void os_getArtEui (u1_t* buf) { memcpy_P(buf, fram_data.appeui, 8); }
void os_getDevEui (u1_t* buf) { memcpy_P(buf, fram_data.deveui, 8); }
void os_getDevKey (u1_t* buf) { memcpy_P(buf, fram_data.appkey, 16); }

// Pin mapping
const lmic_pinmap lmic_pins = {
  .nss = 8,
  .rxtx = LMIC_UNUSED_PIN,
  .rst = 4,
  .dio = {3, 6, LMIC_UNUSED_PIN},
};

void logit(char* logstring) {
  if (fram_data.logging) {
    Serial.print(millis());
    Serial.print(": ");
    Serial.println(logstring);
  }
}

void print_byte_array(u1_t arr[], int n) {
  int i;
  for (i = 0; i < n; i++)
  {
    if (arr[i] < 16) Serial.write('0');
    Serial.print(arr[i], HEX);
  }
}

void ShowLORAData() {
  Serial.println('{');
  Serial.print("  \"version\": \"");
  Serial.print(lora_data.version);
  Serial.println("\",");
  Serial.print("  \"vbat\": \"");
  Serial.print(lora_data.vbat);
  Serial.println("\",");
  Serial.print("  \"startup_counter\": [");
  for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
    Serial.print(lora_data.startup_counter[i]);
    if (i < (MAX_VALUES_TO_SEND - 1)) {
      Serial.print(",");
    }
  }
  Serial.println("],");
  Serial.print("  \"weight\": [");
  for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
    Serial.print(lora_data.weight[i]);
    if (i < (MAX_VALUES_TO_SEND - 1)) {
      Serial.print(",");
    }
  }
  Serial.println("],");
  Serial.print("  \"temperature\": [");
  for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
    Serial.print(lora_data.temperature[i]);
    if (i < (MAX_VALUES_TO_SEND - 1)) {
      Serial.print(",");
    }
  }
  Serial.println("],");
  Serial.println("}");
}


void ShowFRAMData() {
  Serial.println('{');
  Serial.print("  \"nwkskey\": \"");
  print_byte_array(fram_data.nwkskey, 16);
  Serial.println("\", ");
  Serial.print("  \"appskey\": \"");
  print_byte_array(fram_data.appskey, 16);
  Serial.println("\", ");
  Serial.print("  \"devaddr\": \"");
  Serial.print(fram_data.devaddr, HEX);
  Serial.println("\",");

  Serial.print("  \"appeui\": \"");
  print_byte_array(fram_data.appeui, 8);
  Serial.println("\", ");
  Serial.print("  \"deveui\": \"");
  print_byte_array(fram_data.deveui, 8);
  Serial.println("\", ");
  Serial.print("  \"appkey\": \"");
  print_byte_array(fram_data.appkey, 16);
  Serial.println("\", ");

  
  Serial.print("  \"framecounterup\": ");
  Serial.print(fram_data.session_info.seqnoUp);
  Serial.println(",");
  Serial.print("  \"framecounterdown\": ");
  Serial.print(fram_data.session_info.seqnoDn);
  Serial.println(",");
  Serial.print("  \"cal_w1_0\": ");
  Serial.print(fram_data.cal_w1_0);
  Serial.println(",");
  Serial.print("  \"cal_w2_0\": ");
  Serial.print(fram_data.cal_w2_0);
  Serial.println(",");
  Serial.print("  \"cal_w1_factor\": ");
  Serial.print(fram_data.cal_w1_factor);
  Serial.println(",");
  Serial.print("  \"cal_w2_factor\": ");
  Serial.print(fram_data.cal_w2_factor);
  Serial.println(",");
  Serial.print("  \"startup_counter\": [");
  for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
    Serial.print(fram_data.startup_counter[i]);
    if (i < (MAX_VALUES_TO_SEND - 1)) {
      Serial.print(",");
    }
  }
  Serial.println("],");
  Serial.print("  \"weight\": [");
  for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
    Serial.print(fram_data.weight[i]);
    if (i < (MAX_VALUES_TO_SEND - 1)) {
      Serial.print(",");
    }
  }
  Serial.println("],");
  Serial.print("  \"temperature\": [");
  for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
    Serial.print(fram_data.temperature[i]);
    if (i < (MAX_VALUES_TO_SEND - 1)) {
      Serial.print(",");
    }
  }
  Serial.println("],");
  Serial.print("  \"my_position\": ");
  Serial.print(fram_data.my_position);
  Serial.println(",");
  Serial.print("  \"current_startup_counter\": ");
  Serial.println(fram_data.current_startup_counter);
  Serial.println("}");
}

void onEvent (ev_t ev) {
  logit("Received LoraWAN Event");
  switch (ev) {
    case EV_SCAN_TIMEOUT:
      logit("EV_SCAN_TIMEOUT");
      break;
    case EV_BEACON_FOUND:
      logit("EV_BEACON_FOUND");
      break;
    case EV_BEACON_MISSED:
      logit("EV_BEACON_MISSED");
      break;
    case EV_BEACON_TRACKED:
      logit("EV_BEACON_TRACKED");
      break;
    case EV_JOINING:
      logit("EV_JOINING");
      break;
    case EV_JOINED:
      logit("EV_JOINED");
      memcpy(fram_data.nwkskey,LMIC.nwkKey,16);
      memcpy(fram_data.appskey,LMIC.artKey,16);
      fram_data.devaddr = LMIC.devaddr;
      break;
    case EV_RFU1:
      logit("EV_RFU1");
      break;
    case EV_JOIN_FAILED:
      logit("EV_JOIN_FAILED");
      break;
    case EV_REJOIN_FAILED:
      logit("EV_REJOIN_FAILED");
      break;
    case EV_TXCOMPLETE:
      logit("EV_TXCOMPLETE (includes waiting for RX windows)");
      if (LMIC.txrxFlags & TXRX_ACK)
        logit("Received ack");
      lora_data_sent = true;
      if (LMIC.dataLen) {
        sprintf(mystring,"Received %d bytes of payload", LMIC.dataLen);
      }
      break;
    case EV_LOST_TSYNC:
      logit("EV_LOST_TSYNC");
      break;
    case EV_RESET:
      logit("EV_RESET");
      break;
    case EV_RXCOMPLETE:
      // data received in ping slot
      logit("EV_RXCOMPLETE");
      break;
    case EV_LINK_DEAD:
      logit("EV_LINK_DEAD");
      break;
    case EV_LINK_ALIVE:
      logit("EV_LINK_ALIVE");
      break;
    default:
      logit("Unknown event");
      break;
  }
}

void InitFeather() {
  // DONEPIN must be low...
  pinMode(DONEPIN, OUTPUT);
  digitalWrite(DONEPIN, LOW);
}

float GetTemp() {
  return temp_sensor.readTemperature();
}

int GetBat() {
  float vb = analogRead(VBATPIN);
  vb *= 2;    // we divided by 2, so multiply back
  vb *= 3.3;  // Multiply by 3.3V, our reference voltage
  return vb;
}

bool isUSBCableAttached() {
  // Reading battery; Value is in Millivolts
  int vbat = GetBat();
  // Wir nehmen an, dass das USB-Kabel gesteckt ist, wenn die Batteriespannung
  // groesser als 4.4V ist (Schalter muss auf 0 sein)
  usb_cable_attached = (vbat >= 4400);

  // fuer Debugzwecke
  pinMode(LED_BUILTIN, OUTPUT);
  if (usb_cable_attached) {
    digitalWrite(LED_BUILTIN, HIGH);
  } else {
    digitalWrite(LED_BUILTIN, LOW);
  }

  return usb_cable_attached;
}

void InitSerial() {
  Serial.begin(115200);
  Serial.setTimeout(6000);
  //while (!Serial);
  delay(1000);
  logit("Serial Port initialized");
}

bool ShouldDataBeSent() {
  // Data should be sent if difference is too big or if count is reached
  bool res = false;

  // we send it when all measurement slots are taken or if startup_counter expires
  if ((fram_data.my_position >= MAX_VALUES_TO_SEND) || ((fram_data.current_startup_counter >= JOIN_AFTER_N_STARTUPS))) {
    res = true;
  }
  if (fram_data.my_position > 1) {
    if ((fram_data.weight[(fram_data.my_position - 1)] - fram_data.weight[(fram_data.my_position - 2)]) >= SENDDIFFTHRESHOLD) {
      res = true;
    }
  }
  return res;
}

void RestoreLoraSessionInfo() {
  memcpy(&LMIC.bands,&fram_data.session_info.bands,sizeof(LMIC.bands));
  memcpy(&LMIC.channelFreq,&fram_data.session_info.channelFreq,sizeof(LMIC.channelFreq));
  memcpy(&LMIC.channelDrMap,&fram_data.session_info.channelDrMap,sizeof(LMIC.channelDrMap));
  LMIC.channelMap = fram_data.session_info.channelMap;
  LMIC.seqnoUp = fram_data.session_info.seqnoUp;
  LMIC.seqnoDn = fram_data.session_info.seqnoDn;
  LMIC.datarate = fram_data.session_info.datarate;
  LMIC.dn2Dr = fram_data.session_info.dn2Dr;
  LMIC.dn2Freq = fram_data.session_info.dn2Freq;
  LMIC.adrTxPow = fram_data.session_info.adrTxPow;
  LMIC.adrEnabled = fram_data.session_info.adrEnabled;
}

void SaveLoraSessionInfo() {
  memcpy(&fram_data.session_info.bands,&LMIC.bands,sizeof(LMIC.bands));
  memcpy(&fram_data.session_info.channelFreq,&LMIC.channelFreq,sizeof(LMIC.channelFreq));
  memcpy(&fram_data.session_info.channelDrMap,&LMIC.channelDrMap,sizeof(LMIC.channelDrMap));
  fram_data.session_info.channelMap = LMIC.channelMap;
  fram_data.session_info.seqnoUp = LMIC.seqnoUp;
  fram_data.session_info.seqnoDn = LMIC.seqnoDn;
  fram_data.session_info.datarate = LMIC.datarate;
  fram_data.session_info.dn2Dr = LMIC.dn2Dr;
  fram_data.session_info.dn2Freq = LMIC.dn2Freq;
  fram_data.session_info.adrTxPow = LMIC.adrTxPow;
  fram_data.session_info.adrEnabled = LMIC.adrEnabled;
}

void InitLORA() {
  // LMIC init
  os_init();
  // Reset the MAC state. Session and pending data transfers will be discarded.
  LMIC_reset();
  // see https://startiot.telenor.com/learning/getting-started-with-adafruit-feather-m0-lora/
  LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100);

  // We normally use ABP, but make a join for the first startup and after JOIN_AFTER_N_STARTUPS startups
  if (fram_data.current_startup_counter == 0) {
    // do nothing
    logit("we do nothing => OTAA");
    // We use maximum SF for OTAA Joins...
    LMIC_setDrTxpow(DR_SF12, 14);
  }
  else if (fram_data.current_startup_counter >= JOIN_AFTER_N_STARTUPS) {
    logit("we do a OTAA Join again, as the startup counter is high enough...");
    fram_data.current_startup_counter = 0;
  } else {
    LMIC_setSession (0x1, fram_data.devaddr, fram_data.nwkskey, fram_data.appskey);
    RestoreLoraSessionInfo();
  }
  // Disable link check validation
  //LMIC_setLinkCheckMode(0);
  // TTN uses SF9 for its RX2 window.
  //LMIC.dn2Dr = DR_SF9;
  // Swisscom uses SF12 for its RX2 window.
  //LMIC.dn2Dr = DR_SF12;

  // Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library)
  //LMIC_setDrTxpow(DR_SF7, 14);
//LMIC_setDrTxpow(DR_SF12, 14);
}

void do_send(osjob_t* j) {
  // Check if there is not a current TX/RX job running
  if (LMIC.opmode & OP_TXRXPEND) {
    logit("OP_TXRXPEND, not sending");
  } else {
    // Prepare upstream data transmission at the next possible time.
    //LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
    LMIC_setTxData2(1, (uint8_t*) &lora_data, sizeof(LORA_data), 0);
    logit("Packet queued");
  }
}

void InitAndReaFRAM() {
  // we set #8 to High (CS of Lora Module), to be able to use FRAM
  pinMode(8, INPUT_PULLUP);
  digitalWrite(8, HIGH);
  if (fram.begin()) {
    logit("Found SPI FRAM");
  } else {
    logit("No SPI FRAM found ... check your connections\r\n");
  }
  // wir lesen die FRAM-Werte
  logit("Reading FRAM...");
  fram.read(0x0, (uint8_t*) &fram_data, sizeof(FRAM_data));
  fram_data.logging = 1;
  logit("FRAM was read...");
}

float GetWeight() {
  long raw_weight1 = scale1.read_average(3);
  long raw_weight2 = scale2.read_average(3);

  return (((raw_weight1 - fram_data.cal_w1_0) / fram_data.cal_w1_factor) + ((raw_weight2 - fram_data.cal_w2_0) / fram_data.cal_w2_factor));
}

void ReadSensors() {
  temp_sensor = Adafruit_Si7021();

  // we initialize the scales
  logit("Initializing the scale");
  scale1.begin(A3, A2);
  scale2.begin(A1, A0);

  // Now we initialize the Si7021
  if (!temp_sensor.begin()) {
    logit("Did not find Si7021 sensor!");
  }


  lora_data.vbat = (byte)(GetBat() / 20);

  // wir verwerten die Messung nur, falls der Unterschied zur Letzten registrierten Messung gross genug ist
  short weight = (int)GetWeight() / 10;
  short temperature = (int)GetTemp() * 10;
  short last_weight = 0;
  short last_temperature = 0;
  if (fram_data.my_position > 0) {
    last_weight = fram_data.weight[fram_data.my_position - 1];
    last_temperature = fram_data.temperature[fram_data.my_position -1];
  }

  Serial.println("AAA");
  Serial.println(fram_data.my_position);
  Serial.println(temperature);
  Serial.println(last_temperature);
  Serial.println(weight);
  Serial.println(last_weight);
  Serial.println("ZZZ");
  
  if ((fram_data.my_position == 0) || (abs(temperature - last_temperature) > 10) || (abs(weight - last_weight) > 50) || (fram_data.current_startup_counter >= JOIN_AFTER_N_STARTUPS)) {
    fram_data.startup_counter[fram_data.my_position] = fram_data.current_startup_counter;
    fram_data.weight[fram_data.my_position] = weight;
    fram_data.temperature[fram_data.my_position] = temperature;

    fram_data.my_position++;
  }
}

void SendLoraData() {
  logit("Now sending packet...");
  lora_data.version = 1;
  for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
    lora_data.startup_counter[i] = fram_data.startup_counter[i];
    lora_data.weight[i] = fram_data.weight[i];
    lora_data.temperature[i] = fram_data.temperature[i];
  }

  // Start job
  
  //lora_data.version=11;
  //lora_data.vbat=22;
  //for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
  //  lora_data.weight[i] = 200+i;
  //  lora_data.temperature[i] = 300+i;
  //}
  ShowLORAData();

  do_send(&sendjob);
  unsigned long starttime;
  starttime = millis();
  bool logged = false;
  while (!lora_data_sent && ((millis() - starttime) < TIMEOUTMS)) {
    os_runloop_once();
    if ((millis() % 1000) == 0) {
      if (!logged) {
        logit("Loop until sent...");
        logged = true;
      }
    } else {
      logged = false;
    }
  }
  if (lora_data_sent) {
    logit("Lora Data was sent!");
  } else {
    logit("Timeout elapsed...");
  }

  // We clear the data...
  for (int i = 0; i < MAX_VALUES_TO_SEND; i++) {
    fram_data.startup_counter[i] = 0;
    fram_data.weight[i] = MAX_SHORT;
    fram_data.temperature[i] = MAX_SHORT;
  }
  fram_data.my_position = 0;

  SaveLoraSessionInfo();
}

void SaveFRAM() {
  logit("we save the fram_data");
  fram.writeEnable(true);
  fram.write(0x0, (uint8_t *) &fram_data, sizeof(FRAM_data) );
  fram.writeEnable(false);
  logit("fram_data saved...");
}

boolean isValidHexKey(String hk, int length) {
  if (hk.length() != length) {
    return false;
  }
  char mychar;
  for (int i = 0; i < hk.length(); i++) {
    mychar = hk.charAt(i);
    if (not(isHexadecimalDigit(mychar))) {
      return false;
    }
  }
  return true;
}

byte dehex(char c) { // Get nibble value 0...15 from character c
  // Treat digit if c<'A', else letter
  return c < 'A' ? c & 0xF : 9 + (c & 0xF);
  // Above assumes that c is a 'hex digit' in 0...9, A or a ... F or f.
  // It would make more sense to just use 16 consecutive characters,
  // like eg 0123456789:;<=>? or @ABCDEFGHIJKLMNO so the above
  // could just say `return c & 0xF;`
}

void WriteKey (String input, which w) {
  const char *hin = input.c_str();  // Get character array
  int clen = input.length() / 2;
  // Next line invalid in C++, ok in C99.  Probably need to
  // instead declare a fixed-length array, cmd[MAXCMDLEN], etc
  char cmd[clen + 1]; // Leave a byte for null terminator
  for (int i = 0; i < 2 * clen; i += 2) {
    cmd[i / 2] = dehex(hin[i]) << 4 | dehex(hin[i + 1]);
  }
  cmd[clen] = 0;                     // Null-byte terminator
  if (w == APPSKEY) {
    memcpy(&fram_data.appskey, &cmd, 16);
  } else if (w == NWKSKEY) {
    memcpy(&fram_data.nwkskey, &cmd, 16);
  } else if (w == DEVADDR) {
    logit("write device address");
    fram_data.devaddr = strtol(hin, 0, 16);
  } else if (w == APPEUI) {
    memcpy(&fram_data.appeui, &cmd, 8);
  } else if (w == DEVEUI) {
    memcpy(&fram_data.deveui, &cmd, 8);
  } else if (w == APPKEY) {
    memcpy(&fram_data.appkey, &cmd, 16);
  } else {
    logit("Invalid which");
  }
  SaveFRAM();
}

void Setup() {
  String s = "";
  while (s != "exit") {
    s = Serial.readStringUntil('\n');
    s.trim();
 
    if (s == "") {
      //Serial.println("Leerzeile wird ignoriert...");
    }
    else if (s == "setup") {
        Serial.println("{ \"msg\": \"Entering setup mode\" }");
    }
    else if (s.startsWith("setnwkskey")) {
      String key;
      key = s.substring(11);
      if (isValidHexKey(key, 32)) {
        WriteKey(key, NWKSKEY);
        Serial.println("{ \"msg\": \"setnwkskey was successful\" }");
      }
      else {
        Serial.println("{ \"msg\": \"Ist kein gueltiger Hex Key mit 32 Zeichen\" }");
      }
    }
    else if (s.startsWith("setappskey")) {
      String key;
      key = s.substring(11);
      if (isValidHexKey(key, 32)) {
        WriteKey(key, APPSKEY);
        Serial.println("{ \"msg\": \"setappskey was successful\" }");
      }
      else {
        Serial.println("{ \"msg\": \"Ist kein gueltiger Hex Key mit 32 Zeichen\" }");
      }
    }
    else if (s.startsWith("setdevaddr")) {
      String key;
      key = s.substring(11);
      if (isValidHexKey(key, 8)) {
        WriteKey(key, DEVADDR);
        Serial.println("{ \"msg\": \"setdevaddr was successful\" }");
      }
      else {
        Serial.println("{ \"msg\": \"Ist kein gueltiger Hex Key mit 32 Zeichen\" }");
      }
    }

    else if (s.startsWith("setappeui")) {
      String key;
      key = s.substring(10);
      if (isValidHexKey(key, 16)) {
        WriteKey(key, APPEUI);
        Serial.println("{ \"msg\": \"setappeui was successful\" }");
      }
      else {
        Serial.println("{ \"msg\": \"Ist kein gueltiger Hex Key mit 16 Zeichen\" }");
      }
    }
    else if (s.startsWith("setdeveui")) {
      String key;
      key = s.substring(10);
      if (isValidHexKey(key, 16)) {
        WriteKey(key, DEVEUI);
        Serial.println("{ \"msg\": \"setdeveui was successful\" }");
      }
      else {
        Serial.println("{ \"msg\": \"Ist kein gueltiger Hex Key mit 16 Zeichen\" }");
      }
    }
    else if (s.startsWith("setappkey")) {
      String key;
      key = s.substring(10);
      if (isValidHexKey(key, 32)) {
        WriteKey(key, APPKEY);
        Serial.println("{ \"msg\": \"setappkey was successful\" }");
      }
      else {
        Serial.println("{ \"msg\": \"Ist kein gueltiger Hex Key mit 32 Zeichen\" }");
      }
    }
    
    else if (s == "getvalues") {
      Serial.println('{');
      Serial.print("  \"weight\": ");
      Serial.print(GetWeight());
      Serial.println(", ");
      Serial.print("  \"temperature\": ");
      Serial.print(GetTemp());
      Serial.println(", ");
      Serial.print("  \"batt\": ");
      Serial.println(GetBat());
      Serial.println("}");
    }
    else if (s == "getrawvalues") {
      long raw_weight1 = scale1.read_average(3);
      long raw_weight2 = scale2.read_average(3);
      Serial.println('{');
      Serial.print("  \"w1_raw\": ");
      Serial.print(raw_weight1);
      Serial.println(", ");
      Serial.print("  \"w2_raw\": ");
      Serial.print(raw_weight2);
      Serial.println("");
      Serial.println("}");
    }
    else if (s == "getframdata") {
      ShowFRAMData();
    }
    else if (s == "calibrate_zero_1") {
      long raw_weight1 = scale1.read_average(3);
      fram_data.cal_w1_0 = (int)raw_weight1;
      Serial.println("{ \"msg\": \"calibrate_zero_1 was successful\" }");
    }
    else if (s == "calibrate_zero_2") {
      long raw_weight2 = scale2.read_average(3);
      fram_data.cal_w2_0 = (int)raw_weight2;
      Serial.println("{ \"msg\": \"calibrate_zero_2 was successful\" }");
    }
    else if (s.startsWith("calibrate_1")) {
      String w1_gramm;
      w1_gramm = s.substring(12);
      long raw_weight1 = scale1.read_average(3);
      //fram_data.cal_w1_factor = (w1_gramm.toFloat() / (float)raw_weight1);
      fram_data.cal_w1_factor = (((float)raw_weight1 - fram_data.cal_w1_0)/ w1_gramm.toFloat());
      Serial.println("{ \"msg\": \"calibrate_1 was successful\" }");
    }
    else if (s.startsWith("calibrate_2")) {
      String w2_gramm;
      w2_gramm = s.substring(12);
      long raw_weight2 = scale2.read_average(3);
      fram_data.cal_w2_factor = (((float)raw_weight2 - fram_data.cal_w2_0)/ w2_gramm.toFloat());
      Serial.println("{ \"msg\": \"calibrate_2 was successful\" }");
    } 
    else if (s == "exit") {
      // we exit the loop
    }
    else {
      Serial.println("{ \"msg\": \"You sent me an unknown command (valid commands: exit,calibrate_zero_1,calibrate_zero_2,calibrate_1,calibrate_2,getvalues,getrawvalues,setnwkskey,setappskey,setdevaddr,setappeui,setdeveui,setappkey,getframdata)\" }");
    }
  }
  Serial.println("We exited the setup loop...");
}

void TurnOff() {
  logit("We turn off...");
  logit("Jetzt senden wir das DONE Signal...");
  delay(500);
  while (1) {
    digitalWrite(DONEPIN, HIGH);
    delay(5);
    digitalWrite(DONEPIN, LOW);
    delay(5);
  }
}

void setup() {
  fram_data.logging = 1;
  //fram_data.current_startup_counter = 0;
  InitFeather();
  if (isUSBCableAttached()) {
    InitSerial();
  }
  InitAndReaFRAM();
  //fram_data.current_startup_counter = 0;
  ReadSensors();
  if (isUSBCableAttached() && Serial.available()) {
    Setup();
  }
  if (ShouldDataBeSent()) {
    InitLORA();
    SendLoraData();
  }
  fram_data.current_startup_counter++;

  ShowFRAMData();

  SaveFRAM();
  TurnOff();
}

void loop() {
}
