// MODULE <vehicleid> <units> <notifications>
BOOL net_sms_handle_module(char *caller, char *command, char *arguments)
{
    BOOL moduleq_result;

    par_set(PARAM_VEHICLEID, arguments);
    arguments = net_sms_nextarg(arguments);
    if (arguments != NULL)
    {
        par_set(PARAM_MILESKM, arguments);
        arguments = net_sms_nextarg(arguments);
        if (arguments != NULL)
        {
            par_set(PARAM_NOTIFIES, arguments);
            arguments = net_sms_nextarg(arguments);
            if (arguments != NULL)
            {
                par_set(PARAM_VEHICLETYPE, arguments);
            }
        }
    }
    moduleq_result = net_sms_handle_moduleq(caller, command, arguments);
    vehicle_initialise();

    if (net_state != NET_STATE_DIAGMODE)
    {
        net_state_vint = NET_GPRS_RETRIES;
        net_state_enter(NET_STATE_NETINITP);
    }

    return moduleq_result;
}
// VEHICLE <vehicletype>
BOOL net_sms_handle_vehicle(char *caller, char *command, char *arguments)
  {
  BOOL vehicleq_result;

  if (arguments[0]=='-') arguments[0]=0;
  par_set(PARAM_VEHICLETYPE, arguments);

  vehicleq_result = net_sms_handle_vehicleq(caller, command, arguments);
  vehicle_initialise();

  if (net_state != NET_STATE_DIAGMODE)
    net_state_enter(NET_STATE_DONETINIT);

  return vehicleq_result;
  }
BOOL net_sms_handle_feature(char *caller, char *command, char *arguments)
{
    unsigned char f;

    if (arguments == NULL) return FALSE;

    f = atoi(arguments);
    arguments = net_sms_nextarg(arguments);
    if ((f>=0)&&(f<FEATURES_MAX)&&(arguments != NULL))
    {
        sys_features[f] = atoi(arguments);
        if (f>=FEATURES_MAP_PARAM) // Top N features are persistent
            par_set(PARAM_FEATURE_S+(f-FEATURES_MAP_PARAM), arguments);
        if (f == FEATURE_CANWRITE) vehicle_initialise();
        net_send_sms_start(caller);
        net_puts_rom(NET_MSG_FEATURE);
        return TRUE;
    }
    return FALSE;
}
BOOL net_sms_handle_params(char *caller, char *command, char *arguments)
{
    unsigned char d = PARAM_MILESKM;

    while ((d < PARAM_MAX)&&(arguments != NULL))
    {
        if ((arguments[0]=='-')&&(arguments[1]==0))
            par_set(d++, arguments+1);
        else
            par_set(d++, arguments);
        arguments = net_sms_nextarg(arguments);
    }

    net_send_sms_start(caller);
    net_puts_rom(NET_MSG_PARAMS);
    net_send_sms_finish();

    vehicle_initialise();

    if (net_state != NET_STATE_DIAGMODE)
        net_state_enter(NET_STATE_DONETINIT);

    return FALSE;
}
void main(void)
{
  unsigned char x, y;

  // DEBUG / QA stats: get last reset reason:
  x = (~RCON) & 0x1f;
  if (STKPTRbits.STKFUL) x += 32;
  if (STKPTRbits.STKUNF) x += 64;

  // ...clear RCON:
  RCONbits.NOT_BOR = 1; // b0 = 1  = Brown Out Reset
  RCONbits.NOT_POR = 1; // b1 = 2  = Power On Reset
  //RCONbits.NOT_PD = 1;    // b2 = 4  = Power Down detection
  //RCONbits.NOT_TO = 1;    // b3 = 8  = watchdog TimeOut occured
  RCONbits.NOT_RI = 1; // b4 = 16 = Reset Instruction

  if (x == 3) // 3 = normal Power On
  {
    debug_crashreason = 0;
    debug_crashcnt = 0;
#ifdef OVMS_LOGGINGMODULE
    logging_initialise();
#endif
  }
  else
  {
    debug_crashreason = x | 0x80; // 0x80 = keep checkpoint until sent to server
    debug_crashcnt++;
  }

  CHECKPOINT(0x20)

  for (x = 0; x < FEATURES_MAP_PARAM; x++)
    sys_features[x] = 0; // Turn off the features

  // The top N features are persistent
  for (x = FEATURES_MAP_PARAM; x < FEATURES_MAX; x++)
  {
    sys_features[x] = atoi(par_get(PARAM_FEATURE_S + (x - FEATURES_MAP_PARAM)));
  }

  // Make sure cooldown is off
  car_coolingdown = -1;

  // Port configuration
  inputs_initialise();
  TRISB = 0xFE;

  // Timer 0 enabled, Fosc/4, 16 bit mode, prescaler 1:256
  // This gives us one tick every 51.2uS before prescale (13.1ms after)
  T0CON = 0b10000111; // @ 5Mhz => 51.2uS

  // Initialisation...
  led_initialise();
  par_initialise();
  vehicle_initialise();
  net_initialise();

  CHECKPOINT(0x21)

  // Startup sequence...
  // Holding the RED led on, pulse out the firmware version on the GREEN led
  delay100(10); // Delay 1 second
  led_set(OVMS_LED_RED, OVMS_LED_ON);
  led_set(OVMS_LED_GRN, OVMS_LED_OFF);
  led_start();
  delay100(10); // Delay 1.0 seconds
  led_set(OVMS_LED_GRN, ovms_firmware[0]);
  led_start();
  delay100(35); // Delay 3.5 seconds
  ClrWdt(); // Clear Watchdog Timer
  led_set(OVMS_LED_GRN, ovms_firmware[1]);
  led_start();
  delay100(35); // Delay 3.5 seconds
  ClrWdt(); // Clear Watchdog Timer
  led_set(OVMS_LED_GRN, ovms_firmware[2]);
  led_start();
  delay100(35); // Delay 3.5 seconds
  ClrWdt(); // Clear Watchdog Timer
  led_set(OVMS_LED_GRN, OVMS_LED_OFF);
  led_set(OVMS_LED_RED, OVMS_LED_OFF);
  led_start();
  delay100(10); // Delay 1 second
  ClrWdt(); // Clear Watchdog Timer

  // Setup ready for the main loop
  led_set(OVMS_LED_GRN, OVMS_LED_OFF);
  led_start();

#ifdef OVMS_HW_V2
  car_12vline = inputs_voltage()*10;
  car_12vline_ref = 0;
#endif

#ifdef OVMS_ACCMODULE
  acc_initialise();
#endif

  // Proceed to main loop
  y = 0; // Last TMR0H
  while (1) // Main Loop
  {
    CHECKPOINT(0x22)
    if ((vUARTIntStatus.UARTIntRxError) ||
            (vUARTIntStatus.UARTIntRxOverFlow))
      net_reset_async();

    while (!vUARTIntStatus.UARTIntRxBufferEmpty)
    {
      CHECKPOINT(0x23)
      net_poll();
    }

    CHECKPOINT(0x24)
    vehicle_idlepoll();

    ClrWdt(); // Clear Watchdog Timer

    x = TMR0L;
    if (TMR0H >= 0x4c) // Timout ~1sec (actually 996ms)
    {
      TMR0H = 0;
      TMR0L = 0; // Reset timer
      CHECKPOINT(0x25)
      net_ticker();
      CHECKPOINT(0x26)
      vehicle_ticker();
#ifdef OVMS_LOGGINGMODULE
      CHECKPOINT(0x27)
      logging_ticker();
#endif
#ifdef OVMS_ACCMODULE
      CHECKPOINT(0x28)
      acc_ticker();
#endif
    }
    else if (TMR0H != y)
    {
      if ((TMR0H % 0x04) == 0)
      {
        CHECKPOINT(0x29)
        net_ticker10th();
        CHECKPOINT(0x2A)
        vehicle_ticker10th();
        CHECKPOINT(0x2B)
      }
      y = TMR0H;
    }
  }
BOOL net_msg_cmd_exec(void)
  {
  int k;
  char *p, *s;

  delay100(2);

  CHECKPOINT(0x43)

  switch (net_msg_cmd_code)
    {
    case 1: // Request feature list (params unused)
      for (k=0;k<FEATURES_MAX;k++)
        {
          s = stp_i(net_scratchpad, "MP-0 c1,0,", k);
          s = stp_i(s, ",", FEATURES_MAX);
          s = stp_i(s, ",", sys_features[k]);
          net_msg_encode_puts();
        }
      break;

    case 2: // Set feature (params: feature number, value)
      for (p=net_msg_cmd_msg;(*p != 0)&&(*p != ',');p++) ;
      // check if a value exists and is separated by a comma
      if (*p == ',')
        {
        *p++ = 0;
        // At this point, <net_msg_cmd_msg> points to the command, and <p> to the param value
        k = atoi(net_msg_cmd_msg);
        if ((k>=0)&&(k<FEATURES_MAX))
          {
          sys_features[k] = atoi(p);
          if (k>=FEATURES_MAP_PARAM) // Top N features are persistent
            par_set(PARAM_FEATURE_S+(k-FEATURES_MAP_PARAM), p);
          if (k == FEATURE_CANWRITE) vehicle_initialise();
          STP_OK(net_scratchpad, net_msg_cmd_code);
          }
        else
          {
          STP_INVALIDRANGE(net_scratchpad, net_msg_cmd_code);
          }
        }
      else
        {
        STP_INVALIDSYNTAX(net_scratchpad, net_msg_cmd_code);
        }
      net_msg_encode_puts();
      break;

    case 3: // Request parameter list (params unused)
      for (k=0;k<PARAM_MAX;k++)
        {
          p = par_get(k);
          if (k==PARAM_SERVERPASS) *p=0; // Don't show netpass1
          s = stp_i(net_scratchpad, "MP-0 c3,0,", k);
          s = stp_i(s, ",", PARAM_MAX);
          s = stp_s(s, ",", p);
          net_msg_encode_puts();
        }
      break;

    case 4: // Set parameter (params: param number, value)
      for (p=net_msg_cmd_msg;(*p != 0)&&(*p != ',');p++) ;
      // check if a value exists and is separated by a comma
      if (*p == ',')
        {
        *p++ = 0;
        // At this point, <net_msg_cmd_msg> points to the command, and <p> to the param value
        k = atoi(net_msg_cmd_msg);
        if ((k>=0)&&(k<PARAM_FEATURE_S))
          {
          par_set(k, p);
          STP_OK(net_scratchpad, net_msg_cmd_code);
          if ((k==PARAM_MILESKM) || (k==PARAM_VEHICLETYPE)) vehicle_initialise();
          }
        else
          {
          STP_INVALIDRANGE(net_scratchpad, net_msg_cmd_code);
          }
        }
      else
        {
        STP_INVALIDSYNTAX(net_scratchpad, net_msg_cmd_code);
        }
      net_msg_encode_puts();
      break;

    case 5: // Reboot (params unused)
      STP_OK(net_scratchpad, net_msg_cmd_code);
      net_msg_encode_puts();
      net_state_enter(NET_STATE_HARDSTOP);
      break;

    case 6: // CHARGE ALERT (params unused)
      net_msg_alert();
      net_msg_encode_puts();
      break;

    case 40: // Send SMS (params: phone number, SMS message)
      for (p=net_msg_cmd_msg;(*p != 0)&&(*p != ',');p++) ;
      // check if a value exists and is separated by a comma
      if (*p == ',')
        {
        *p++ = 0;
        // At this point, <net_msg_cmd_msg> points to the phone number, and <p> to the SMS message
        net_send_sms_start(net_msg_cmd_msg);
        net_puts_ram(p);
        net_puts_rom("\x1a");
        delay100(5);
        net_msg_start();
        STP_OK(net_scratchpad, net_msg_cmd_code);
        }
      else
        {
        net_msg_start();
        STP_INVALIDSYNTAX(net_scratchpad, net_msg_cmd_code);
        }
      net_msg_encode_puts();
      delay100(2);
      break;

    case 41: // Send MMI/USSD Codes (param: USSD_CODE)
      net_puts_rom("AT+CUSD=1,\"");
      net_puts_ram(net_msg_cmd_msg);
      net_puts_rom("\",15\r");
      // cmd reply #1 to acknowledge command:
      delay100(5);
      net_msg_start();
      STP_OK(net_scratchpad, net_msg_cmd_code);
      net_msg_encode_puts();
      delay100(2);
      // cmd reply #2 sent on USSD response, see net_msg_reply_ussd()
      break;
      
    case 49: // Send raw AT command (param: raw AT command)
      net_puts_ram(net_msg_cmd_msg);
      net_puts_rom("\r");
      delay100(5);
      net_msg_start();
      STP_OK(net_scratchpad, net_msg_cmd_code);
      net_msg_encode_puts();
      delay100(2);
      break;
    default:
      return FALSE;
    }

  return TRUE;
  }
BOOL net_msg_cmd_exec(void)
  {
  int k;
  char *p, *s;

  delay100(2);

  CHECKPOINT(0x43)

  switch (net_msg_cmd_code)
    {
    case 1: // Request feature list (params unused)
      for (k=0;k<FEATURES_MAX;k++)
        {
          s = stp_i(net_scratchpad, "MP-0 c1,0,", k);
          s = stp_i(s, ",", FEATURES_MAX);
          s = stp_i(s, ",", sys_features[k]);
          net_msg_encode_puts();
        }
      break;

    case 2: // Set feature (params: feature number, value)
      for (p=net_msg_cmd_msg;(*p != 0)&&(*p != ',');p++) ;
      // check if a value exists and is separated by a comma
      if (*p == ',')
        {
        *p++ = 0;
        // At this point, <net_msg_cmd_msg> points to the command, and <p> to the param value
        k = atoi(net_msg_cmd_msg);
        if ((k>=0)&&(k<FEATURES_MAX))
          {
          sys_features[k] = atoi(p);
          if (k>=FEATURES_MAP_PARAM) // Top N features are persistent
            par_set(PARAM_FEATURE_S+(k-FEATURES_MAP_PARAM), p);
          if (k == FEATURE_CANWRITE) vehicle_initialise();
          STP_OK(net_scratchpad, net_msg_cmd_code);
          }
        else
          {
          STP_INVALIDRANGE(net_scratchpad, net_msg_cmd_code);
          }
        }
      else
        {
        STP_INVALIDSYNTAX(net_scratchpad, net_msg_cmd_code);
        }
      net_msg_encode_puts();
      break;

    case 3: // Request parameter list (params unused)
      for (k=0;k<PARAM_MAX;k++)
        {
          p = par_get(k);
          if (k==PARAM_SERVERPASS) *p=0; // Don't show netpass1
          s = stp_i(net_scratchpad, "MP-0 c3,0,", k);
          s = stp_i(s, ",", PARAM_MAX);
          s = stp_s(s, ",", p);
          net_msg_encode_puts();
        }
      break;

    case 4: // Set parameter (params: param number, value)
      for (p=net_msg_cmd_msg;(*p != 0)&&(*p != ',');p++) ;
      // check if a value exists and is separated by a comma
      if (*p == ',')
        {
        *p++ = 0;
        // At this point, <net_msg_cmd_msg> points to the command, and <p> to the param value
        k = atoi(net_msg_cmd_msg);
        if ((k>=0)&&(k<PARAM_FEATURE_S))
          {
          par_set(k, p);
          STP_OK(net_scratchpad, net_msg_cmd_code);
          if ((k==PARAM_MILESKM) || (k==PARAM_VEHICLETYPE)) vehicle_initialise();
#ifdef OVMS_ACCMODULE
          // Reset the ACC state it an ACC parameter is changed
          if ((k>=PARAM_ACC_S)&&(k<(PARAM_ACC_S+PARAM_ACC_COUNT)))
            acc_state_enter(ACC_STATE_FIRSTRUN);
#endif
          }
        else
          {
          STP_INVALIDRANGE(net_scratchpad, net_msg_cmd_code);
          }
        }
      else
        {
        STP_INVALIDSYNTAX(net_scratchpad, net_msg_cmd_code);
        }
      net_msg_encode_puts();
      break;

    case 5: // Reboot (params unused)
      STP_OK(net_scratchpad, net_msg_cmd_code);
      net_msg_encode_puts();
      net_state_enter(NET_STATE_HARDSTOP);
      break;

    case 6: // CHARGE ALERT (params unused)
      net_msg_alert();
      net_msg_encode_puts();
      break;

    case 7: // SMS command wrapper

      // process command:
      net_msg_bufpos = net_msg_scratchpad;
      k = net_sms_in(par_get(PARAM_REGPHONE), net_msg_cmd_msg);
      net_msg_bufpos = NULL;
      // output is now in net_msg_scratchpad

      // create return string:
      s = stp_i(net_scratchpad, NET_MSG_CMDRESP, net_msg_cmd_code);
      s = stp_i(s, ",", 1-k); // 0=ok 1=error
      if (k)
        {
        *s++ = ',';
        for (p = net_msg_scratchpad; *p; p++)
          {
            if (*p == '\n')
              *s++ = '\r'; // translate LF to CR
            else if (*p == ',')
              *s++ = ';'; // translate , to ;
            else
              *s++ = *p;
          }
        *s = 0;
        }

      // send return string:
      net_msg_encode_puts();
      break;

    case 40: // Send SMS (params: phone number, SMS message)
      for (p=net_msg_cmd_msg;(*p != 0)&&(*p != ',');p++) ;
      // check if a value exists and is separated by a comma
      if (*p == ',')
        {
        *p++ = 0;
        // At this point, <net_msg_cmd_msg> points to the phone number, and <p> to the SMS message
        net_send_sms_start(net_msg_cmd_msg);
        net_puts_ram(p);
        net_puts_rom("\x1a");
        delay100(5);
        net_msg_start();
        STP_OK(net_scratchpad, net_msg_cmd_code);
        }
      else
        {
        net_msg_start();
        STP_INVALIDSYNTAX(net_scratchpad, net_msg_cmd_code);
        }
      net_msg_encode_puts();
      delay100(2);
      break;

    case 41: // Send MMI/USSD Codes (param: USSD_CODE)
      net_puts_rom("AT+CUSD=1,\"");
      net_puts_ram(net_msg_cmd_msg);
      net_puts_rom("\",15\r");
      // cmd reply #1 to acknowledge command:
      delay100(5);
      net_msg_start();
      STP_OK(net_scratchpad, net_msg_cmd_code);
      net_msg_encode_puts();
      delay100(2);
      // cmd reply #2 sent on USSD response, see net_msg_reply_ussd()
      break;
      
    case 49: // Send raw AT command (param: raw AT command)
      net_puts_ram(net_msg_cmd_msg);
      net_puts_rom("\r");
      delay100(5);
      net_msg_start();
      STP_OK(net_scratchpad, net_msg_cmd_code);
      net_msg_encode_puts();
      delay100(2);
      break;

  default:
      return FALSE;
    }

  return TRUE;
  }