/* Copyright 2025 by COG9 Enterprises LLC
 * All rights reserved.
 * Title: sercmd.ino
 * Author: Jed Marti KI7NNP
 * Description: Serial command line stuff. Gets called when there's a character.
 * Created: Mon Oct  6 12:19:03 2025
 * Revision History:
 */

#include <stdint.h>

#include "ina219.h"
#include "ds1340.h"
#include "powerard.h"
#include "record.h"
#include "eepromconst.h"

// See record.ino
extern uint16_t sampleMS;
extern uint16_t sampleSize;

// See eeprom.ino
extern eePROMValues E;

// The input buffer.
char    inBuffer[SERIALBUFFER];
int8_t  inIndex = 0;
volatile uint8_t serialReady = 0;

// The three amperage sensors.
extern INA219 MA;
extern INA219 MB;
extern INA219 MC;

static void help(void)
/*     Question mark in column 1 means get help.
 * Parameters: none
 * External References: none really.
 * Returned values: none
 * Errors detected: none
 */
{
  Serial.print(F("amps        Show current readings\n"));
  Serial.print(F("bat         Calibrate battery reading\n"));
  Serial.print(F("eeprom ...  Dump, set EEPROM fields\n"));
  Serial.print(F("date day-name month day year hh:mm:ss\n"));
  Serial.print(F("low ff.ff   Low voltage battery cutoff indication\n"));
  Serial.print(F("off ff.ff   Battery voltage to shutoff\n"));
  Serial.print(F("over ff.ff  Voltage cutoff high voltage\n"));
  Serial.print(F("owner call  Owner's call sign\n"));
  Serial.print(F("rate n      Sample rate per second (1-25)\n"));
  Serial.print(F("reset       Reset eeprom\n"));
  Serial.print(F("samples n   Samples/record (1-100)\n"));
  Serial.print(F("serve       HTML of status\n"));
  Serial.print(F("svg xxx     SVG of one of the graphs\n"));
  Serial.print(F("start amps/volts/temps/all  Start recording\n"));
  Serial.print(F("stop        Stop recording\n"));
}



void accumulateCharacter(void)
/*     Read a chacter when one is present and store it in the buffer. Do editing and so on. When
 *   done signal that it's time to do something.
 * Parameters: none
 * External References:
 *   SerialReady: indicates a full line received.
 *   inBuffer[inIndex]: buffer and charactger.
 * Returned values: none
 * Errors detected: buffer overflow, characters ignored.
 */
{
  char c;
  c = Serial.read();

  // If we haven't serviced the last character, punt.
  if (serialReady) return;

  // Newline means do something.
  if (c == 10 || c == 13)
  {
    inBuffer[inIndex] = 0;
    serialReady = 1;
  }

  // Backspace.
  else if (c == 8 || c == 127)
  {
    if (inIndex > 0)
    {
      Serial.print((char) 8);
      Serial.print(' ');
      Serial.print((char) 8);
      --inIndex;
    }
  }

  // Help anywhere. Note, must be in column 1.
  //  else if (c == '?')
  //  {
  //    Serial.println();
  //    help();
  //    inIndex = 0;
  //  }

  else if (inIndex < SERIALBUFFER - 1)
  {
    Serial.print(c);
    inBuffer[inIndex++] = c;
  }
}



void setVoltage(uint8_t which)
/*     Set one of the battery voltage limits. Low (time to quit), over (too much) bad (done). Commands
 *  are:
 *     low volts
 *     off volts
 *     over volts
 * Parameters:
 *   which: which voltage is being set (from one of the 3 commands.
 * External References:
 *   E: EEProm locations for the 3 values.
 * Returned values: none
 * Errors detected: missing voltage value.
 */
{
  char *tok;
  float f;
  tok = strtok(NULL, " \n");
  if (tok == NULL)
  {
    Serial.println("Missing battery voltage");
    return;
  }

  switch (which) {
  case 0: E.eeBatLow = f; break;
  case 1: E.eeBatOver = f; break;
  case 2: E.eeBatBad = f; break;
  }
  updateEEPROM();
}


void setSampleRate(void)
/*     Set the number of samples taken per second. This can be 1 - 25. Format:
 *          rate nnn
 */
{
  char *tok;
  int16_t sr;

  tok = strtok(NULL, " \n");
  sr = atoi(tok);
  if (sr < 1 || sr > 25)
  {
    Serial.print(F("\n Rate "));
    Serial.print(sr);
    Serial.println(F(" illegal"));
    sr = 5;
  }
  Serial.print(F("\n Rate is now "));
  Serial.print(sr);
  Serial.println(F(" samples/second"));

  // But the recorder wants the number of milliseconds between readings.
  sampleMS = 1000 / sr;
  E.eeSampleMS = sampleMS;
  updateEEPROM();
}



void setSamples(void)
/*     Set the number of samples to average. After the average, one record is written to
 *   the serial EEPROM. The value can range from 1 to 100.
 */
{
  char *tok;
  tok = strtok(NULL, " \n");
  sampleSize = atoi(tok);
  if (sampleSize < 1 || sampleSize > 100)
  {
    Serial.print(F("\n Sample Size "));
    Serial.print(sampleSize);
    Serial.println(F(" illegal"));
    sampleSize = 5;
  }
  Serial.print(F("\nSample size now "));
  Serial.println(sampleSize);
  E.eeSampleSize = sampleSize;
  updateEEPROM();
}



static void setOwner(void)
{
  char *tok;

  tok = strtok(NULL, " \n");
  
  if (tok == NULL)
  {
    Serial.println(F("\n  owner missing call sign"));
    return;
  }
  Serial.print(F("\n  Owner set to "));
  Serial.print(tok);
  Serial.print(F("\n\n"));
  strncpy(E.eeOwner, tok, 8);
  updateEEPROM();
}


static void readReg(uint8_t addr, uint8_t reg, uint8_t *pBuf, uint16_t len)
{
  Wire.begin();
  Wire.beginTransmission(addr);
  Wire.write(reg);
  if(Wire.endTransmission() != 0) return;
  Wire.requestFrom(addr, (uint8_t) len);
  for(uint16_t i = 0; i < len; i ++)
  {
       pBuf[i] = Wire.read();
  }
  Wire.endTransmission();
}

uint16_t readInaReg(uint8_t addr, uint8_t reg)
{
    uint8_t buf[2] = {0};
    
    readReg(addr, reg, buf, sizeof(buf));
    return (buf[0] << 8) | buf[1];
}

void writeReg(uint8_t addr, uint8_t reg, uint8_t *pBuf, uint16_t len)
{
    Wire.begin();
    Wire.beginTransmission(addr);
    Wire.write(reg);
    for(uint16_t i = 0; i < len; i ++)Wire.write(pBuf[i]);
    Wire.endTransmission();
}

void writeInaReg(uint8_t addr, uint8_t reg, uint16_t value)
{
    uint8_t buffer[2] = {(value >> 8), value & 0xff};
    writeReg(addr, reg, buffer, 2);
}


static void all3read(uint8_t reg)
{
  Serial.print(F("A:0x"));
  Serial.print(readInaReg(MONITORA, reg), HEX);
  Serial.print(F(" B:0x"));
  Serial.print(readInaReg(MONITORB, reg), HEX);
  Serial.print(F(" C:0x"));
  Serial.print(readInaReg(MONITORC, reg), HEX);
  terpri();
}


static void regDump(void)
{
  Serial.print(F("\n  Config  ")); all3read(0);
  Serial.print(F("  Shunt   ")); all3read(1);
  Serial.print(F("  Bus     ")); all3read(2);
  Serial.print(F("  Power   ")); all3read(3);
  Serial.print(F("  Current ")); all3read(4);
  Serial.print(F("  Calib   ")); all3read(5);
}
  

static void cdraw(void)
/*     Show the values from each available INA219. Called when the amps command given.
 * Parameters: none
 * External References:
 *   MA, MB, MC: control blocks for the sensor.
 * Returned values: none
 * Errors detected: none
 */
{
  if (isOK(MA))
  {
    Serial.print(F("\nA: shunt "));
    Serial.print(ina219GetShuntVoltage(&MA), 2);
    Serial.print(F(" mv, bus "));
    Serial.print(ina219GetVoltage(&MA));
    Serial.print(F(" V, draw "));
    Serial.print(ina219GetCurrent(&MA), 1);
    Serial.print(F(" ma\n"));
  }
  if (isOK(MB))
  {
    Serial.print(F("B: shunt "));
    Serial.print(ina219GetShuntVoltage(&MB), 2);
    Serial.print(F(" mv, bus "));
    Serial.print(ina219GetVoltage(&MB));
    Serial.print(F(" V, draw "));
    Serial.print(ina219GetCurrent(&MB));
    Serial.print(F(" ma\n"));
  }
  if (isOK(MC))
  {
    Serial.print(F("C: shunt "));
    Serial.print(ina219GetShuntVoltage(&MC), 2);
    Serial.print(F(" mv, bus "));
    Serial.print(ina219GetVoltage(&MC));
    Serial.print(F(" V, draw "));
    Serial.print(ina219GetCurrent(&MC));
    Serial.println(F(" ma\n"));
  }
}



static void setReg(void)
/*     Manually set one of the INA219 internal registers. Mostly for debugging, not actual use
 *   as the values aren't saved anywhere.
 * Syntax:
 *      set dev-name register-number value
 *   dev-name           A, B, or C.
 *   register-number    0-5, internal register number.
 *   value              0-255 register value to set.
 * External References:
 *   MONITOR?: I2C address of the 3 sensors.
 * Returned values: none
 * Errors detected: no arguments, give help, bad device name, illegal register.
 */
{
  char     *tok;
  INA219   *dev;
  char     devl;
  uint8_t  reg;
  uint16_t val;

  tok = strtok(NULL, " \n");
  if (tok == NULL)
  {
    Serial.print(F("set <name> <reg> <value>\n"));
    Serial.print(F("  <name>     A, B, C - INA219 device.\n"));
    Serial.print(F("  <reg>      0-5 - INA219 register number.\n"));
    Serial.print(F("  <value>    0-255, value to set.\n"));
    return;
  }

  // Convert device name to I2C address.
  if (strcasecmp(tok, "a") == 0) dev = &MA;
  else if (strcasecmp(tok, "b") == 0) dev = &MB;
  else if (strcasecmp(tok, "c") == 0) dev = &MC;
  else
  {
    Serial.print(tok);
    Serial.print(F(" not a known device (A or B or C)\n"));
    return;
  }
  devl = tok[0];

  // Register.
  tok = strtok(NULL, " \n");
  reg = atoi(tok);
  if (reg > INA219_Calibration)
  {
    Serial.print(F("Register "));
    Serial.print(reg);
    Serial.print(F(" illegal (0-5 only)\n"));
    return;
  }

  // Value.
  tok = strtok(NULL, " \n");
  val = strtol(tok, NULL, 0);
  Serial.print(F("\nSetting MONITOR"));
  Serial.print(devl);
  Serial.print(F(" register "));
  Serial.print(reg);
  Serial.print(F(" to 0x"));
  Serial.print(val, HEX);
  terpri();
  writeInaReg(dev, reg, val);
}



void serialCommand(void)
/*    Gets called during the service loop when an end of line is detected. Commands are case
 *   insensitive.
 * Parameters: none
 * External References:
 *   inBuffer[]: input buffer - limited size.
 *   inIndex: index into the buffer.
 *   serialReady: Indicates a complete line is present.
 * Returned values: none
 * Errors detected: empty line, unknown command.
 */
{
  char *tok;

  // ? for help is special.
  if (inBuffer[0] == '?')
  {
    help();
    inIndex = serialReady = 0;
    Serial.printf(F("?"));
    return;
  }

  tok = strtok(inBuffer, " \n");
  if (tok == NULL)
  {
    inIndex = serialReady = 0;
    Serial.printf(F("?"));
    return;
  }

  if (strcasecmp(tok, "ascii") == 0) asciiDump();
  else if (strcasecmp(tok, "date") == 0) setDateTime();
  else if (strcasecmp(tok, "eeprom") == 0) setEEPROM();
  else if (strcasecmp(tok, "low") == 0) setVoltage(0);
  else if (strcasecmp(tok, "off") == 0) setVoltage(1);
  else if (strcasecmp(tok, "owner") == 0) setOwner();
  else if (strcasecmp(tok, "over") == 0) setVoltage(2);
  else if (strcasecmp(tok, "rate") == 0) setSampleRate();
  else if (strcasecmp(tok, "samples") == 0) setSamples();
  else if (strcasecmp(tok, "start") == 0) startRecording();
  else if (strcasecmp(tok, "stop") == 0) stopRecording();
  else if (strcasecmp(tok, "cterm") == 0) ctermDump();
  else if (strcasecmp(tok, "csv") == 0) csvDump();
  else if (strcasecmp(tok, "batcalib") == 0) calibrateBatMon();
  else if (strcasecmp(tok, "amps") == 0) cdraw();
  else if (strcasecmp(tok, "reset") == 0) resetEEPROM();
  else if (strcasecmp(tok, "regs") == 0) regDump();
  else if (strcasecmp(tok, "serve") == 0) netpage();
  else if (strcasecmp(tok, "set") == 0) setReg();
  else if (strcasecmp(tok, "svg") == 0) graphSVG();

  else Serial.print(F("\nEh?\n"));


  // Reset for next input line.
  inIndex = serialReady = 0;
  Serial.print(F("?"));
}



// EOF
